diff --git a/components/_override/CMakeLists.txt b/components/_override/CMakeLists.txt index 7de1484f..af83bb65 100644 --- a/components/_override/CMakeLists.txt +++ b/components/_override/CMakeLists.txt @@ -4,4 +4,4 @@ idf_component_register( SRCS ${srcs} ) # CMake is just a pile of crap -message("overriding ${srcs} !! THIS MUST BE REQUIRED BY ONE COMPONENT BUT NO MAIN !!") \ No newline at end of file +message("overriding ${srcs} !! THIS MUST BE REQUIRED BY ONE COMPONENT BUT NOT MAIN !!") \ No newline at end of file diff --git a/components/spotify/Shim.cpp b/components/spotify/Shim.cpp index cb270287..87020b8a 100644 --- a/components/spotify/Shim.cpp +++ b/components/spotify/Shim.cpp @@ -246,8 +246,8 @@ void ShimAudioSink::volumeChanged(uint16_t volume) { cspot.cHandler(CSPOT_VOLUME, volume); } -void ShimAudioSink::feedPCMFrames(std::vector &data) { - cspot.dHandler(&data[0], data.size()); +void ShimAudioSink::feedPCMFrames(const uint8_t *data, size_t bytes) { + cspot.dHandler(data, bytes); } /**************************************************************************************** diff --git a/components/spotify/Shim.h b/components/spotify/Shim.h index 9c0a8cce..4f758b0b 100644 --- a/components/spotify/Shim.h +++ b/components/spotify/Shim.h @@ -23,7 +23,7 @@ class ShimAudioSink : public AudioSink { public: ShimAudioSink(void) { softwareVolumeControl = false; } - void feedPCMFrames(std::vector &data); + void feedPCMFrames(const uint8_t *data, size_t bytes); virtual void volumeChanged(uint16_t volume); }; diff --git a/components/spotify/cspot/CMakeLists.txt b/components/spotify/cspot/CMakeLists.txt index 01be3ff7..e91c8460 100644 --- a/components/spotify/cspot/CMakeLists.txt +++ b/components/spotify/cspot/CMakeLists.txt @@ -44,5 +44,6 @@ endif() add_library(cspot STATIC ${SOURCES} ${PROTO_SRCS}) # PUBLIC to propagate includes from bell to cspot dependents +target_compile_definitions(bell PUBLIC PB_ENABLE_MALLOC) target_link_libraries(cspot PUBLIC ${EXTRA_LIBS}) target_include_directories(cspot PUBLIC "include" ${GENERATED_INCLUDES} ${NANOPB_INCLUDE_DIRS}) diff --git a/components/spotify/cspot/bell/include/AudioSink.h b/components/spotify/cspot/bell/include/AudioSink.h new file mode 100644 index 00000000..2f0be3de --- /dev/null +++ b/components/spotify/cspot/bell/include/AudioSink.h @@ -0,0 +1,21 @@ +#ifndef AUDIOSINK_H +#define AUDIOSINK_H + +#include +#include +#include + +class AudioSink +{ + public: + AudioSink() {} + virtual ~AudioSink() {} + virtual void feedPCMFrames(const uint8_t *buffer, size_t bytes) = 0; + virtual void volumeChanged(uint16_t volume) {} + // return true if the sink supports rate changing + virtual bool setRate(uint16_t sampleRate) { return false; } + bool softwareVolumeControl = true; + bool usign = false; +}; + +#endif diff --git a/components/spotify/cspot/bell/include/BellLogger.h b/components/spotify/cspot/bell/include/BellLogger.h index 341a1895..78bb3c0d 100644 --- a/components/spotify/cspot/bell/include/BellLogger.h +++ b/components/spotify/cspot/bell/include/BellLogger.h @@ -83,8 +83,6 @@ namespace bell { std::string basenameStr(filename.substr(filename.rfind("/") + 1)); unsigned long hash = 5381; - int c; - for (char const &c : basenameStr) { hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ diff --git a/components/spotify/cspot/bell/include/BellUtils.h b/components/spotify/cspot/bell/include/BellUtils.h index 060e4f7b..ac88eb7b 100644 --- a/components/spotify/cspot/bell/include/BellUtils.h +++ b/components/spotify/cspot/bell/include/BellUtils.h @@ -15,13 +15,13 @@ std::string generateRandomUUID(); #include #define BELL_SLEEP_MS(ms) vTaskDelay(ms / portTICK_PERIOD_MS) -#define BELL_YIELD() vTaskYield() +#define BELL_YIELD() taskYIELD() #else #include #define BELL_SLEEP_MS(ms) usleep(ms * 1000) -#define BELL_YIELD() () +#define BELL_YIELD() ; #endif -#endif \ No newline at end of file +#endif diff --git a/components/spotify/cspot/bell/include/ByteStream.h b/components/spotify/cspot/bell/include/ByteStream.h index 9e242a98..cdaf41b2 100644 --- a/components/spotify/cspot/bell/include/ByteStream.h +++ b/components/spotify/cspot/bell/include/ByteStream.h @@ -1,7 +1,8 @@ #ifndef BELL_BYTE_READER_H #define BELL_BYTE_READER_H -#include "stdlib.h" +#include +#include /** * A class for reading bytes from a stream. Further implemented in HTTPStream.h diff --git a/components/spotify/cspot/bell/include/HTTPClient.h b/components/spotify/cspot/bell/include/HTTPClient.h index eef7a615..6461f6b8 100644 --- a/components/spotify/cspot/bell/include/HTTPClient.h +++ b/components/spotify/cspot/bell/include/HTTPClient.h @@ -2,6 +2,7 @@ #define BELL_HTTP_CLIENT #include "BellSocket.h" +#include "ByteStream.h" #include "TCPSocket.h" #include "platform/TLSSocket.h" #include @@ -21,13 +22,15 @@ class HTTPClient { struct HTTPRequest { HTTPMethod method = HTTPMethod::GET; std::string url; - std::string body; + const char *body = nullptr; + const char *contentType = nullptr; std::map headers; - std::string contentType; int maxRedirects = -1; + std::ostream *dumpFs = nullptr; + std::ostream *dumpRawFs = nullptr; }; - struct HTTPResponse { + struct HTTPResponse : public ByteStream { std::shared_ptr socket; std::map headers; @@ -41,18 +44,32 @@ class HTTPClient { bool isComplete = false; bool isRedirect = false; size_t redirectCount = 0; + std::ostream *dumpFs = nullptr; + std::ostream *dumpRawFs = nullptr; - void close() { - socket->close(); - free(buf); - buf = nullptr; - bufPtr = nullptr; - } + ~HTTPResponse(); + void close() override; void readHeaders(); - size_t read(char *dst, size_t len); + size_t read(char *dst, size_t len, bool wait = false); std::string readToString(); + inline size_t skip(size_t len) override { + return read((char *)nullptr, len); + } + inline size_t read(uint8_t *dst, size_t len) override { + return read((char *)dst, len); + } + inline size_t read(uint8_t *dst, size_t len, bool wait) { + return read((char *)dst, len, wait); + } + inline size_t size() override { + return contentLength; + } + inline size_t position() override { + return bodyRead; + } + private: char *buf = nullptr; // allocated buffer char *bufPtr = nullptr; // reading pointer within buf @@ -61,16 +78,19 @@ class HTTPClient { size_t chunkRemaining = 0; bool isStreaming = false; size_t readRaw(char *dst); - bool skip(size_t len, bool dontRead = false); + bool skipRaw(size_t len, bool dontRead = false); }; + typedef std::unique_ptr HTTPResponse_t; + private: - static void executeImpl(const struct HTTPRequest &request, const char *url, struct HTTPResponse *&response); + static HTTPResponse_t executeImpl(const struct HTTPRequest &request, HTTPResponse_t response); static bool readHeader(const char *&header, const char *name); public: - static struct HTTPResponse *execute(const struct HTTPRequest &request); + static HTTPResponse_t execute(const struct HTTPRequest &request); }; +typedef std::unique_ptr HTTPResponse_t; } // namespace bell #endif diff --git a/components/spotify/cspot/bell/include/NanoPBHelper.h b/components/spotify/cspot/bell/include/NanoPBHelper.h index cb7ba3cc..d370b199 100644 --- a/components/spotify/cspot/bell/include/NanoPBHelper.h +++ b/components/spotify/cspot/bell/include/NanoPBHelper.h @@ -4,6 +4,7 @@ #include #include "pb_encode.h" #include "pb_decode.h" +#include "HTTPClient.h" #include std::vector pbEncode(const pb_msgdesc_t *fields, const void *src_struct); @@ -41,6 +42,7 @@ void pbDecode(T &result, const pb_msgdesc_t *fields, std::vector &data) } } -void pbFree(const pb_msgdesc_t *fields, void *src_struct); +const char* pb_encode_to_string(const pb_msgdesc_t *fields, const void *data); +pb_istream_t pb_istream_from_http(bell::HTTPClient::HTTPResponse *response, size_t length = 0); -#endif \ No newline at end of file +#endif diff --git a/components/spotify/cspot/bell/include/Queue.h b/components/spotify/cspot/bell/include/Queue.h index 545a74fe..ae938ea2 100644 --- a/components/spotify/cspot/bell/include/Queue.h +++ b/components/spotify/cspot/bell/include/Queue.h @@ -15,7 +15,7 @@ namespace bell /// Queue std::queue m_queue; /// Mutex to controll multiple access - std::mutex m_mutex; + mutable std::mutex m_mutex; /// Conditional variable used to fire event std::condition_variable m_cv; /// Atomic variable used to terminate immediately wpop and wtpop functions diff --git a/components/spotify/cspot/bell/include/TCPSocket.h b/components/spotify/cspot/bell/include/TCPSocket.h index 6e25aa6c..3128b508 100644 --- a/components/spotify/cspot/bell/include/TCPSocket.h +++ b/components/spotify/cspot/bell/include/TCPSocket.h @@ -46,7 +46,7 @@ namespace bell hints.ai_protocol = IPPROTO_IP; // no enum : possible value can be read in /etc/protocols hints.ai_flags = AI_CANONNAME | AI_ALL | AI_ADDRCONFIG; - BELL_LOG(info, "http", "%s %d", host.c_str(), port); + // BELL_LOG(info, "http", "%s %d", host.c_str(), port); char portStr[6]; sprintf(portStr, "%u", port); diff --git a/components/spotify/cspot/bell/include/Task.h b/components/spotify/cspot/bell/include/Task.h index cebf2eb7..72f9412b 100644 --- a/components/spotify/cspot/bell/include/Task.h +++ b/components/spotify/cspot/bell/include/Task.h @@ -10,6 +10,7 @@ #endif #include +#include namespace bell { @@ -51,6 +52,7 @@ namespace bell } else { + printf("task on internal %s", this->taskName.c_str()); esp_pthread_cfg_t cfg = esp_pthread_get_default_config(); cfg.stack_size = stackSize; cfg.inherit_cfg = true; diff --git a/components/spotify/cspot/bell/include/sinks/esp/AC101AudioSink.h b/components/spotify/cspot/bell/include/sinks/esp/AC101AudioSink.h new file mode 100644 index 00000000..98c1c0ff --- /dev/null +++ b/components/spotify/cspot/bell/include/sinks/esp/AC101AudioSink.h @@ -0,0 +1,26 @@ +#ifndef AC101AUDIOSINK_H +#define AC101AUDIOSINK_H + +#include +#include +#include "BufferedAudioSink.h" +#include +#include +#include +#include +#include "esp_err.h" +#include "esp_log.h" +#include "ac101.h" +#include "adac.h" + +class AC101AudioSink : public BufferedAudioSink +{ +public: + AC101AudioSink(); + ~AC101AudioSink(); + void volumeChanged(uint16_t volume); +private: + adac_s *dac; +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/sinks/esp/BufferedAudioSink.h b/components/spotify/cspot/bell/include/sinks/esp/BufferedAudioSink.h new file mode 100644 index 00000000..f090f582 --- /dev/null +++ b/components/spotify/cspot/bell/include/sinks/esp/BufferedAudioSink.h @@ -0,0 +1,25 @@ +#ifndef BUFFEREDAUDIOSINK_H +#define BUFFEREDAUDIOSINK_H + +#include +#include +#include "AudioSink.h" +#include +#include +#include +#include +#include "esp_err.h" +#include "esp_log.h" + +class BufferedAudioSink : public AudioSink +{ +public: + void feedPCMFrames(const uint8_t *buffer, size_t bytes); + bool setRate(uint16_t sampleRate) override; +protected: + void startI2sFeed(size_t buf_size = 4096 * 8); + void feedPCMFramesInternal(const void *pvItem, size_t xItemSize); +private: +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/sinks/esp/ES8388AudioSink.h b/components/spotify/cspot/bell/include/sinks/esp/ES8388AudioSink.h new file mode 100644 index 00000000..77c917c8 --- /dev/null +++ b/components/spotify/cspot/bell/include/sinks/esp/ES8388AudioSink.h @@ -0,0 +1,105 @@ +#ifndef ES8388AUDIOSINK_H +#define ES8388AUDIOSINK_H + +#include "driver/i2s.h" +#include +#include +#include +#include "BufferedAudioSink.h" +#include +#include +#include +#include +#include +#include "esp_err.h" +#include "esp_log.h" + + +#define ES8388_ADDR 0x20 + +#define ACK_CHECK_EN 0x1 + +/* ES8388 register */ +#define ES8388_CONTROL1 0x00 +#define ES8388_CONTROL2 0x01 +#define ES8388_CHIPPOWER 0x02 +#define ES8388_ADCPOWER 0x03 +#define ES8388_DACPOWER 0x04 +#define ES8388_CHIPLOPOW1 0x05 +#define ES8388_CHIPLOPOW2 0x06 +#define ES8388_ANAVOLMANAG 0x07 +#define ES8388_MASTERMODE 0x08 + +/* ADC */ +#define ES8388_ADCCONTROL1 0x09 +#define ES8388_ADCCONTROL2 0x0a +#define ES8388_ADCCONTROL3 0x0b +#define ES8388_ADCCONTROL4 0x0c +#define ES8388_ADCCONTROL5 0x0d +#define ES8388_ADCCONTROL6 0x0e +#define ES8388_ADCCONTROL7 0x0f +#define ES8388_ADCCONTROL8 0x10 +#define ES8388_ADCCONTROL9 0x11 +#define ES8388_ADCCONTROL10 0x12 +#define ES8388_ADCCONTROL11 0x13 +#define ES8388_ADCCONTROL12 0x14 +#define ES8388_ADCCONTROL13 0x15 +#define ES8388_ADCCONTROL14 0x16 + +/* DAC */ +#define ES8388_DACCONTROL1 0x17 +#define ES8388_DACCONTROL2 0x18 +#define ES8388_DACCONTROL3 0x19 +#define ES8388_DACCONTROL4 0x1a +#define ES8388_DACCONTROL5 0x1b +#define ES8388_DACCONTROL6 0x1c +#define ES8388_DACCONTROL7 0x1d +#define ES8388_DACCONTROL8 0x1e +#define ES8388_DACCONTROL9 0x1f +#define ES8388_DACCONTROL10 0x20 +#define ES8388_DACCONTROL11 0x21 +#define ES8388_DACCONTROL12 0x22 +#define ES8388_DACCONTROL13 0x23 +#define ES8388_DACCONTROL14 0x24 +#define ES8388_DACCONTROL15 0x25 +#define ES8388_DACCONTROL16 0x26 +#define ES8388_DACCONTROL17 0x27 +#define ES8388_DACCONTROL18 0x28 +#define ES8388_DACCONTROL19 0x29 +#define ES8388_DACCONTROL20 0x2a +#define ES8388_DACCONTROL21 0x2b +#define ES8388_DACCONTROL22 0x2c +#define ES8388_DACCONTROL23 0x2d +#define ES8388_DACCONTROL24 0x2e +#define ES8388_DACCONTROL25 0x2f +#define ES8388_DACCONTROL26 0x30 +#define ES8388_DACCONTROL27 0x31 +#define ES8388_DACCONTROL28 0x32 +#define ES8388_DACCONTROL29 0x33 +#define ES8388_DACCONTROL30 0x34 + +class ES8388AudioSink : public BufferedAudioSink +{ +public: + ES8388AudioSink(); + ~ES8388AudioSink(); + + bool begin(int sda = -1, int scl = -1, uint32_t frequency = 400000U); + + enum ES8388_OUT + { + ES_MAIN, // this is the DAC output volume (both outputs) + ES_OUT1, // this is the additional gain for OUT1 + ES_OUT2 // this is the additional gain for OUT2 + }; + + void mute(const ES8388_OUT out, const bool muted); + void volume(const ES8388_OUT out, const uint8_t vol); + + void writeReg(uint8_t reg_add, uint8_t data); +private: + i2c_config_t i2c_config; + i2c_port_t i2c_port = 0; +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/sinks/esp/ES9018AudioSink.h b/components/spotify/cspot/bell/include/sinks/esp/ES9018AudioSink.h new file mode 100644 index 00000000..986b53fa --- /dev/null +++ b/components/spotify/cspot/bell/include/sinks/esp/ES9018AudioSink.h @@ -0,0 +1,22 @@ +#ifndef ES9018AUDIOSINK_H +#define ES9018AUDIOSINK_H + +#include +#include +#include "BufferedAudioSink.h" +#include +#include +#include +#include +#include "esp_err.h" +#include "esp_log.h" + +class ES9018AudioSink : public BufferedAudioSink +{ +public: + ES9018AudioSink(); + ~ES9018AudioSink(); +private: +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/sinks/esp/InternalAudioSink.h b/components/spotify/cspot/bell/include/sinks/esp/InternalAudioSink.h new file mode 100644 index 00000000..9c98523c --- /dev/null +++ b/components/spotify/cspot/bell/include/sinks/esp/InternalAudioSink.h @@ -0,0 +1,22 @@ +#ifndef INTERNALAUDIOSINK_H +#define INTERNALAUDIOSINK_H + +#include +#include +#include "BufferedAudioSink.h" +#include +#include +#include +#include +#include "esp_err.h" +#include "esp_log.h" + +class InternalAudioSink : public BufferedAudioSink +{ +public: + InternalAudioSink(); + ~InternalAudioSink(); +private: +}; + +#endif diff --git a/components/spotify/cspot/bell/include/sinks/esp/PCM5102AudioSink.h b/components/spotify/cspot/bell/include/sinks/esp/PCM5102AudioSink.h new file mode 100644 index 00000000..77096a23 --- /dev/null +++ b/components/spotify/cspot/bell/include/sinks/esp/PCM5102AudioSink.h @@ -0,0 +1,22 @@ +#ifndef PCM5102AUDIOSINK_H +#define PCM5102AUDIOSINK_H + +#include +#include +#include "BufferedAudioSink.h" +#include +#include +#include +#include +#include "esp_err.h" +#include "esp_log.h" + +class PCM5102AudioSink : public BufferedAudioSink +{ +public: + PCM5102AudioSink(); + ~PCM5102AudioSink(); +private: +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/sinks/esp/SPDIFAudioSink.h b/components/spotify/cspot/bell/include/sinks/esp/SPDIFAudioSink.h new file mode 100644 index 00000000..6f3da9d1 --- /dev/null +++ b/components/spotify/cspot/bell/include/sinks/esp/SPDIFAudioSink.h @@ -0,0 +1,27 @@ +#ifndef SPDIFAUDIOSINK_H +#define SPDIFAUDIOSINK_H + +#include +#include +#include "BufferedAudioSink.h" +#include +#include +#include +#include +#include "esp_err.h" +#include "esp_log.h" + +class SPDIFAudioSink : public BufferedAudioSink +{ +private: + uint8_t spdifPin; +public: + explicit SPDIFAudioSink(uint8_t spdifPin); + ~SPDIFAudioSink() override; + void feedPCMFrames(const uint8_t *buffer, size_t bytes) override; + void initialize(uint16_t sampleRate); + bool setRate(uint16_t sampleRate) override; +private: +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/sinks/esp/TAS5711AudioSink.h b/components/spotify/cspot/bell/include/sinks/esp/TAS5711AudioSink.h new file mode 100644 index 00000000..23a705d8 --- /dev/null +++ b/components/spotify/cspot/bell/include/sinks/esp/TAS5711AudioSink.h @@ -0,0 +1,30 @@ +#ifndef TAS5711AUDIOSINK_H +#define TAS5711AUDIOSINK_H + + +#include "driver/i2s.h" +#include +#include +#include +#include "BufferedAudioSink.h" +#include +#include +#include +#include +#include "esp_err.h" +#include "esp_log.h" + +class TAS5711AudioSink : public BufferedAudioSink +{ +public: + TAS5711AudioSink(); + ~TAS5711AudioSink(); + + + void writeReg(uint8_t reg, uint8_t value); +private: + i2c_config_t i2c_config; + i2c_port_t i2c_port = 0; +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/sinks/esp/ac101.h b/components/spotify/cspot/bell/include/sinks/esp/ac101.h new file mode 100644 index 00000000..39c17379 --- /dev/null +++ b/components/spotify/cspot/bell/include/sinks/esp/ac101.h @@ -0,0 +1,176 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2018 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __AC101_H__ +#define __AC101_H__ + +#include "esp_types.h" + +#define AC101_ADDR 0x1a /*!< Device address*/ + +#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */ +#define READ_BIT I2C_MASTER_READ /*!< I2C master read */ +#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/ +#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */ +#define ACK_VAL 0x0 /*!< I2C ack value */ +#define NACK_VAL 0x1 /*!< I2C nack value */ + +#define CHIP_AUDIO_RS 0x00 +#define PLL_CTRL1 0x01 +#define PLL_CTRL2 0x02 +#define SYSCLK_CTRL 0x03 +#define MOD_CLK_ENA 0x04 +#define MOD_RST_CTRL 0x05 +#define I2S_SR_CTRL 0x06 +#define I2S1LCK_CTRL 0x10 +#define I2S1_SDOUT_CTRL 0x11 +#define I2S1_SDIN_CTRL 0x12 +#define I2S1_MXR_SRC 0x13 +#define I2S1_VOL_CTRL1 0x14 +#define I2S1_VOL_CTRL2 0x15 +#define I2S1_VOL_CTRL3 0x16 +#define I2S1_VOL_CTRL4 0x17 +#define I2S1_MXR_GAIN 0x18 +#define ADC_DIG_CTRL 0x40 +#define ADC_VOL_CTRL 0x41 +#define HMIC_CTRL1 0x44 +#define HMIC_CTRL2 0x45 +#define HMIC_STATUS 0x46 +#define DAC_DIG_CTRL 0x48 +#define DAC_VOL_CTRL 0x49 +#define DAC_MXR_SRC 0x4c +#define DAC_MXR_GAIN 0x4d +#define ADC_ANA_CTRL 0x50 +#define ADC_SRC 0x51 +#define ADC_SRCBST_CTRL 0x52 +#define OMIXER_DACA_CTRL 0x53 +#define OMIXER_SR 0x54 +#define OMIXER_BST1_CTRL 0x55 +#define HPOUT_CTRL 0x56 +#define SPKOUT_CTRL 0x58 +#define AC_DAC_DAPCTRL 0xa0 +#define AC_DAC_DAPHHPFC 0xa1 +#define AC_DAC_DAPLHPFC 0xa2 +#define AC_DAC_DAPLHAVC 0xa3 +#define AC_DAC_DAPLLAVC 0xa4 +#define AC_DAC_DAPRHAVC 0xa5 +#define AC_DAC_DAPRLAVC 0xa6 +#define AC_DAC_DAPHGDEC 0xa7 +#define AC_DAC_DAPLGDEC 0xa8 +#define AC_DAC_DAPHGATC 0xa9 +#define AC_DAC_DAPLGATC 0xaa +#define AC_DAC_DAPHETHD 0xab +#define AC_DAC_DAPLETHD 0xac +#define AC_DAC_DAPHGKPA 0xad +#define AC_DAC_DAPLGKPA 0xae +#define AC_DAC_DAPHGOPA 0xaf +#define AC_DAC_DAPLGOPA 0xb0 +#define AC_DAC_DAPOPT 0xb1 +#define DAC_DAP_ENA 0xb5 + +typedef enum{ + SAMPLE_RATE_8000 = 0x0000, + SAMPLE_RATE_11052 = 0x1000, + SAMPLE_RATE_12000 = 0x2000, + SAMPLE_RATE_16000 = 0x3000, + SAMPLE_RATE_22050 = 0x4000, + SAMPLE_RATE_24000 = 0x5000, + SAMPLE_RATE_32000 = 0x6000, + SAMPLE_RATE_44100 = 0x7000, + SAMPLE_RATE_48000 = 0x8000, + SAMPLE_RATE_96000 = 0x9000, + SAMPLE_RATE_192000 = 0xa000, +} ac_adda_fs_i2s1_t; + +typedef enum{ + BCLK_DIV_1 = 0x0, + BCLK_DIV_2 = 0x1, + BCLK_DIV_4 = 0x2, + BCLK_DIV_6 = 0x3, + BCLK_DIV_8 = 0x4, + BCLK_DIV_12 = 0x5, + BCLK_DIV_16 = 0x6, + BCLK_DIV_24 = 0x7, + BCLK_DIV_32 = 0x8, + BCLK_DIV_48 = 0x9, + BCLK_DIV_64 = 0xa, + BCLK_DIV_96 = 0xb, + BCLK_DIV_128 = 0xc, + BCLK_DIV_192 = 0xd, +} ac_i2s1_bclk_div_t; + +typedef enum{ + LRCK_DIV_16 =0x0, + LRCK_DIV_32 =0x1, + LRCK_DIV_64 =0x2, + LRCK_DIV_128 =0x3, + LRCK_DIV_256 =0x4, +} ac_i2s1_lrck_div_t; + +typedef enum { + BIT_LENGTH_8_BITS = 0x00, + BIT_LENGTH_16_BITS = 0x01, + BIT_LENGTH_20_BITS = 0x02, + BIT_LENGTH_24_BITS = 0x03, +} ac_bits_length_t; + +typedef enum { + AC_MODE_MIN = -1, + AC_MODE_SLAVE = 0x00, + AC_MODE_MASTER = 0x01, + AC_MODE_MAX, +} ac_mode_sm_t; + +typedef enum { + AC_MODULE_MIN = -1, + AC_MODULE_ADC = 0x01, + AC_MODULE_DAC = 0x02, + AC_MODULE_ADC_DAC = 0x03, + AC_MODULE_LINE = 0x04, + AC_MODULE_MAX +} ac_module_t; + +typedef enum{ + SRC_MIC1 = 1, + SRC_MIC2 = 2, + SRC_LINEIN = 3, +}ac_output_mixer_source_t; + +typedef enum { + GAIN_N45DB = 0, + GAIN_N30DB = 1, + GAIN_N15DB = 2, + GAIN_0DB = 3, + GAIN_15DB = 4, + GAIN_30DB = 5, + GAIN_45DB = 6, + GAIN_60DB = 7, +} ac_output_mixer_gain_t; + +typedef struct { + ac_i2s1_bclk_div_t bclk_div; /*!< bits clock divide */ + ac_i2s1_lrck_div_t lclk_div; /*!< WS clock divide */ +} ac_i2s_clock_t; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/sinks/esp/adac.h b/components/spotify/cspot/bell/include/sinks/esp/adac.h new file mode 100644 index 00000000..1b1a2668 --- /dev/null +++ b/components/spotify/cspot/bell/include/sinks/esp/adac.h @@ -0,0 +1,28 @@ +/* + * Squeezelite for esp32 + * + * (c) Sebastien 2019 + * Philippe G. 2019, philippe_44@outlook.com + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + * + */ + +#include "freertos/FreeRTOS.h" +#include "driver/i2s.h" + +typedef enum { ADAC_ON = 0, ADAC_STANDBY, ADAC_OFF } adac_power_e; + +struct adac_s { + bool (*init)(int i2c_port_num, int i2s_num, i2s_config_t *config); + void (*deinit)(void); + void (*power)(adac_power_e mode); + void (*speaker)(bool active); + void (*headset)(bool active); + void (*volume)(unsigned left, unsigned right); +}; + +extern struct adac_s dac_tas57xx; +extern struct adac_s dac_a1s; +extern struct adac_s dac_external; \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/sinks/unix/ALSAAudioSink.h b/components/spotify/cspot/bell/include/sinks/unix/ALSAAudioSink.h new file mode 100644 index 00000000..03f42985 --- /dev/null +++ b/components/spotify/cspot/bell/include/sinks/unix/ALSAAudioSink.h @@ -0,0 +1,124 @@ +#pragma once + +#include +#include +#include "AudioSink.h" +#include +#include +#include +#include +#include +#include + +#define PCM_DEVICE "default" + +template +class RingbufferPointer +{ + typedef std::unique_ptr TPointer; + +public: + explicit RingbufferPointer() + { + // create objects + for (int i = 0; i < SIZE; i++) + { + buf_[i] = std::make_unique(); + } + } + + bool push(TPointer &item) + { + std::lock_guard lock(mutex_); + if (full()) + return false; + + std::swap(buf_[head_], item); + + if (full_) + tail_ = (tail_ + 1) % max_size_; + + head_ = (head_ + 1) % max_size_; + full_ = head_ == tail_; + + return true; + } + + bool pop(TPointer &item) + { + std::lock_guard lock(mutex_); + if (empty()) + return false; + + std::swap(buf_[tail_], item); + + full_ = false; + tail_ = (tail_ + 1) % max_size_; + + return true; + } + + void reset() + { + std::lock_guard lock(mutex_); + head_ = tail_; + full_ = false; + } + + bool empty() const + { + return (!full_ && (head_ == tail_)); + } + + bool full() const + { + return full_; + } + + int capacity() const + { + return max_size_; + } + + int size() const + { + int size = max_size_; + + if (!full_) + { + if (head_ >= tail_) + size = head_ - tail_; + else + size = max_size_ + head_ - tail_; + } + + return size; + } + +private: + TPointer buf_[SIZE]; + + std::mutex mutex_; + int head_ = 0; + int tail_ = 0; + const int max_size_ = SIZE; + bool full_ = 0; +}; + +class ALSAAudioSink : public AudioSink, public bell::Task +{ +public: + ALSAAudioSink(); + ~ALSAAudioSink(); + void feedPCMFrames(const uint8_t *buffer, size_t bytes); + void runTask(); + +private: + RingbufferPointer, 3> ringbuffer; + unsigned int pcm; + snd_pcm_t *pcm_handle; + snd_pcm_hw_params_t *params; + snd_pcm_uframes_t frames; + int buff_size; + std::vector buff; +}; diff --git a/components/spotify/cspot/bell/include/sinks/unix/NamedPipeAudioSink.h b/components/spotify/cspot/bell/include/sinks/unix/NamedPipeAudioSink.h new file mode 100644 index 00000000..cbce8884 --- /dev/null +++ b/components/spotify/cspot/bell/include/sinks/unix/NamedPipeAudioSink.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include "AudioSink.h" + +class NamedPipeAudioSink : public AudioSink +{ +public: + NamedPipeAudioSink(); + ~NamedPipeAudioSink(); + void feedPCMFrames(const uint8_t *buffer, size_t bytes); + +private: + std::ofstream namedPipeFile; +}; diff --git a/components/spotify/cspot/bell/include/sinks/unix/PortAudioSink.h b/components/spotify/cspot/bell/include/sinks/unix/PortAudioSink.h new file mode 100644 index 00000000..3a5ea2bd --- /dev/null +++ b/components/spotify/cspot/bell/include/sinks/unix/PortAudioSink.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include "portaudio.h" +#include +#include +#include "AudioSink.h" + +class PortAudioSink : public AudioSink +{ +public: + PortAudioSink(); + ~PortAudioSink(); + void feedPCMFrames(const uint8_t *buffer, size_t bytes); + void initialize(uint16_t sampleRate); + bool setRate(uint16_t sampleRate) override; + +private: + PaStream *stream; +}; diff --git a/components/spotify/cspot/bell/src/HTTPClient.cpp b/components/spotify/cspot/bell/src/HTTPClient.cpp index e3d2b730..33de1ac7 100644 --- a/components/spotify/cspot/bell/src/HTTPClient.cpp +++ b/components/spotify/cspot/bell/src/HTTPClient.cpp @@ -5,14 +5,31 @@ using namespace bell; -struct HTTPClient::HTTPResponse *HTTPClient::execute(const struct HTTPRequest &request) { - auto *response = new HTTPResponse(); - auto *url = request.url.c_str(); - HTTPClient::executeImpl(request, url, response); - return response; +void HTTPClient::HTTPResponse::close() { + socket = nullptr; + if (buf) + free(buf); + buf = nullptr; + bufPtr = nullptr; +} +HTTPClient::HTTPResponse::~HTTPResponse() { + socket = nullptr; + if (buf) + free(buf); } -void HTTPClient::executeImpl(const struct HTTPRequest &request, const char *url, struct HTTPResponse *&response) { +HTTPResponse_t HTTPClient::execute(const struct HTTPRequest &request) { + auto response = std::make_unique(); + response->dumpFs = request.dumpFs; + response->dumpRawFs = request.dumpRawFs; + return HTTPClient::executeImpl(request, std::move(response)); +} + +HTTPResponse_t HTTPClient::executeImpl(const struct HTTPRequest &request, HTTPResponse_t response) { + const char *url = request.url.c_str(); + if (response->isRedirect) { + url = response->location.c_str(); + } bool https = url[4] == 's'; uint16_t port = https ? 443 : 80; auto *hostname = url + (https ? 8 : 7); @@ -45,9 +62,9 @@ void HTTPClient::executeImpl(const struct HTTPRequest &request, const char *url, stream << path << " HTTP/1.1" << endl; stream << "Host: " << hostnameStr << ":" << port << endl; stream << "Accept: */*" << endl; - if (!request.body.empty()) { + if (request.body != nullptr) { stream << "Content-Type: " << request.contentType << endl; - stream << "Content-Length: " << request.body.size() << endl; + stream << "Content-Length: " << strlen(request.body) << endl; } for (const auto &header : request.headers) { stream << header.first << ": " << header.second << endl; @@ -60,9 +77,7 @@ void HTTPClient::executeImpl(const struct HTTPRequest &request, const char *url, if (len != data.size()) { response->close(); BELL_LOG(error, "http", "Writing failed: wrote %d of %d bytes", len, data.size()); - free(response); - response = nullptr; - return; + return nullptr; } response->readHeaders(); @@ -70,8 +85,9 @@ void HTTPClient::executeImpl(const struct HTTPRequest &request, const char *url, if (response->isRedirect && (request.maxRedirects < 0 || response->redirectCount < request.maxRedirects)) { response->redirectCount++; response->close(); // close the previous socket - HTTPClient::executeImpl(request, response->location.c_str(), response); + return HTTPClient::executeImpl(request, std::move(response)); } + return response; } bool HTTPClient::readHeader(const char *&header, const char *name) { @@ -87,8 +103,9 @@ bool HTTPClient::readHeader(const char *&header, const char *name) { size_t HTTPClient::HTTPResponse::readRaw(char *dst) { size_t len = this->socket->read((uint8_t *)dst, BUF_SIZE); + if (dumpRawFs) + dumpRawFs->write(dst, (long)len); // BELL_LOG(debug, "http", "Read %d bytes", len); - this->bodyRead += len; // after reading headers this gets overwritten dst[len] = '\0'; return len; } @@ -128,7 +145,6 @@ void HTTPClient::HTTPResponse::readHeaders() { if (lineEnd + 2 < this->buf + len) { this->bufPtr = lineEnd + 2; this->bufRemaining = len - (this->bufPtr - this->buf); - this->bodyRead = this->bufRemaining; this->isStreaming = !this->isComplete && !this->contentLength && (len < BUF_SIZE || this->socket->poll() == 0); } @@ -151,7 +167,7 @@ void HTTPClient::HTTPResponse::readHeaders() { this->isRedirect = true; this->location = std::string(header); } else { - char *colonPtr = (char*) strchr(header, ':'); + auto *colonPtr = strchr((char *)header, ':'); if (colonPtr) { auto *valuePtr = colonPtr + 1; while (*valuePtr == ' ') @@ -166,10 +182,10 @@ void HTTPClient::HTTPResponse::readHeaders() { lineBuf.clear(); line = lineEnd + 2; // skip \r\n } while (true); - } while (!complete); + } while (!complete && len); // if len == 0, the connection is closed } -bool HTTPClient::HTTPResponse::skip(size_t len, bool dontRead) { +bool HTTPClient::HTTPResponse::skipRaw(size_t len, bool dontRead) { size_t skip = 0; if (len > bufRemaining) { skip = len - bufRemaining; @@ -184,7 +200,7 @@ bool HTTPClient::HTTPResponse::skip(size_t len, bool dontRead) { } bufRemaining = this->readRaw(this->buf); if (!bufRemaining) - return false; // no more data - shouldn't happen for valid responses + return false; // if len == 0, the connection is closed bufPtr = this->buf + skip; bufRemaining -= skip; if (!contentLength && bufRemaining < BUF_SIZE) { @@ -195,16 +211,15 @@ bool HTTPClient::HTTPResponse::skip(size_t len, bool dontRead) { return true; } -size_t HTTPClient::HTTPResponse::read(char *dst, size_t toRead) { +size_t HTTPClient::HTTPResponse::read(char *dst, size_t toRead, bool wait) { if (isComplete) { // end of chunked stream was found OR complete body was read - dst[0] = '\0'; return 0; } - auto *dstStart = dst; + auto *dstStart = dst ? dst : nullptr; size_t read = 0; while (toRead) { // this loop ends after original toRead - skip(0); // ensure the buffer contains data, wait if necessary + skipRaw(0); // ensure the buffer contains data, wait if necessary if (isChunked && !chunkRemaining) { if (*bufPtr == '0') { // all chunks were read *and emitted* isComplete = true; @@ -213,7 +228,7 @@ size_t HTTPClient::HTTPResponse::read(char *dst, size_t toRead) { auto *endPtr = bufPtr; if (strchr(bufPtr, '\r') == nullptr) { // buf doesn't contain complete chunk size auto size = std::string(bufPtr, bufPtr + bufRemaining); // take the rest of the buffer - if (!skip(bufRemaining)) // skip the rest, read another buf + if (!skipRaw(bufRemaining)) // skip the rest, read another buf break; // -> no more data endPtr = strchr(bufPtr, '\r'); // find the end of the actual number if (endPtr == nullptr) // something's wrong @@ -223,41 +238,51 @@ size_t HTTPClient::HTTPResponse::read(char *dst, size_t toRead) { } else { chunkRemaining = strtol(bufPtr, &endPtr, 16); // read the hex size } - if (!skip(endPtr - bufPtr + 2)) // skip the size and \r\n - break; // -> no more data, break out of main loop + if (!skipRaw(endPtr - bufPtr + 2)) // skip the size and \r\n + break; // -> no more data, break out of main loop } else if (contentLength && !chunkRemaining) { chunkRemaining = contentLength; } while (chunkRemaining && toRead) { size_t count = std::min(toRead, std::min(bufRemaining, chunkRemaining)); - strncpy(dst, bufPtr, count); - dst += count; // move the dst pointer + if (dst) { + memcpy(dst, bufPtr, count); + dst += count; // move the dst pointer + } read += count; // increment read counter + bodyRead += count; // increment total response size chunkRemaining -= count; // decrease chunk remaining size toRead -= count; // decrease local remaining size - if (!skip(count)) { // eat some buffer + if (!skipRaw(count)) { // eat some buffer toRead = 0; // -> no more data, break out of main loop break; } - if (isChunked && !chunkRemaining && !skip(2, isStreaming)) // skip the \r\n for chunked encoding - toRead = 0; // -> no more data, break out of main loop + if (isChunked && !chunkRemaining) { // bufPtr is on the end of chunk + if (!skipRaw(2, isStreaming)) // skip the \r\n for chunked encoding + toRead = 0; // -> no more data, break out of main loop + if (bufRemaining > 1 && bufPtr[0] == '0' && bufPtr[1] == '\r') // this is the last chunk + isComplete = true; + } } - if (isStreaming && !bufRemaining) { // stream with no buffer available, just yield the current chunk + if (isStreaming && !bufRemaining && !wait) { // stream with no buffer available, just yield the current chunk break; } } if (!isChunked && contentLength && !chunkRemaining) - isComplete = true; + isComplete = true; // entire response was read + if (dumpFs && dstStart) + dumpFs->write(dstStart, (long)read); // BELL_LOG(debug, "http", "Read %d of %d bytes", bodyRead, contentLength); - dstStart[read] = '\0'; return read; } std::string HTTPClient::HTTPResponse::readToString() { if (this->contentLength) { std::string result(this->contentLength, '\0'); - this->read(result.data(), this->contentLength); + auto *data = result.data(); + auto len = this->read(data, this->contentLength); + data[len] = '\0'; this->close(); return result; } @@ -266,6 +291,7 @@ std::string HTTPClient::HTTPResponse::readToString() { size_t len; do { len = this->read(buffer, BUF_SIZE); + buffer[len] = '\0'; result += std::string(buffer); } while (len); this->close(); diff --git a/components/spotify/cspot/bell/src/HTTPStream.cpp b/components/spotify/cspot/bell/src/HTTPStream.cpp index 5a6fe223..ce3f4894 100644 --- a/components/spotify/cspot/bell/src/HTTPStream.cpp +++ b/components/spotify/cspot/bell/src/HTTPStream.cpp @@ -155,6 +155,7 @@ size_t bell::HTTPStream::read(uint8_t *buf, size_t nbytes) if (status != StreamStatus::READING_DATA) { BELL_LOG(error, "http", "Not ready to read data"); + exit(0); return 0; } diff --git a/components/spotify/cspot/bell/src/NanoPBHelper.cpp b/components/spotify/cspot/bell/src/NanoPBHelper.cpp index 063c0211..c19e031d 100644 --- a/components/spotify/cspot/bell/src/NanoPBHelper.cpp +++ b/components/spotify/cspot/bell/src/NanoPBHelper.cpp @@ -37,10 +37,6 @@ void packString(char *&dst, std::string stringToPack) strcpy(dst, stringToPack.c_str()); } -void pbFree(const pb_msgdesc_t *fields, void *src_struct) { - pb_release(fields, src_struct); -} - pb_bytes_array_t* vectorToPbArray(const std::vector& vectorToPack) { auto size = static_cast(vectorToPack.size()); @@ -53,4 +49,34 @@ pb_bytes_array_t* vectorToPbArray(const std::vector& vectorToPack) std::vector pbArrayToVector(pb_bytes_array_t* pbArray) { return std::vector(pbArray->bytes, pbArray->bytes + pbArray->size); -} \ No newline at end of file +} + +const char *pb_encode_to_string(const pb_msgdesc_t *fields, const void *data) { + size_t len; + pb_get_encoded_size(&len, fields, data); + auto *buf = static_cast(malloc(len + 1)); + auto ostream = pb_ostream_from_buffer(buf, len); + pb_encode(&ostream, fields, data); + buf[len] = '\0'; + return reinterpret_cast(buf); +} + +static bool pb_read_from_http(pb_istream_t *stream, pb_byte_t *buf, size_t count) { + auto *response = (bell::HTTPClient::HTTPResponse *)stream->state; + size_t len = response->read(buf, count, /* wait */ true); + if (response->isComplete) + stream->bytes_left = count; // count is subtracted after the callback + return len == count; +} + +pb_istream_t pb_istream_from_http(bell::HTTPClient::HTTPResponse *response, size_t length) { + if (!length) + length = response->contentLength; + if (!length) + length = SIZE_MAX; + return { + .callback = &pb_read_from_http, + .state = response, + .bytes_left = length, + }; +} diff --git a/components/spotify/cspot/bell/src/sinks/esp/AC101AudioSink.cpp b/components/spotify/cspot/bell/src/sinks/esp/AC101AudioSink.cpp new file mode 100644 index 00000000..5e83f10d --- /dev/null +++ b/components/spotify/cspot/bell/src/sinks/esp/AC101AudioSink.cpp @@ -0,0 +1,46 @@ +#include "AC101AudioSink.h" + +#include "driver/i2s.h" + +AC101AudioSink::AC101AudioSink() +{ + // Disable software volume control, all handled by ::volumeChanged + softwareVolumeControl = false; + + i2s_config_t i2s_config = { + + .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX + .sample_rate = 44100, + .bits_per_sample = (i2s_bits_per_sample_t)16, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels + .communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_I2S, + .intr_alloc_flags = 0, //Default interrupt priority + .dma_buf_count = 8, + .dma_buf_len = 512, + .use_apll = true, + .tx_desc_auto_clear = true //Auto clear tx descriptor on underflow + }; + + i2s_pin_config_t pin_config = { + .bck_io_num = 27, + .ws_io_num = 26, + .data_out_num = 25, + .data_in_num = -1 //Not used + }; + + dac = &dac_a1s; + + dac->init(0, 0, &i2s_config); + dac->speaker(false); + dac->power(ADAC_ON); + + startI2sFeed(); +} + +AC101AudioSink::~AC101AudioSink() +{ +} + +void AC101AudioSink::volumeChanged(uint16_t volume) { + dac->volume(volume, volume); +} diff --git a/components/spotify/cspot/bell/src/sinks/esp/BufferedAudioSink.cpp b/components/spotify/cspot/bell/src/sinks/esp/BufferedAudioSink.cpp new file mode 100644 index 00000000..5f7138bd --- /dev/null +++ b/components/spotify/cspot/bell/src/sinks/esp/BufferedAudioSink.cpp @@ -0,0 +1,46 @@ +#include "BufferedAudioSink.h" + +#include "driver/i2s.h" +#include "freertos/task.h" +#include "freertos/ringbuf.h" + +RingbufHandle_t dataBuffer; + +static void i2sFeed(void *pvParameters) +{ + while (true) + { + size_t itemSize; + char *item = (char *)xRingbufferReceiveUpTo(dataBuffer, &itemSize, portMAX_DELAY, 512); + if (item != NULL) + { + size_t written = 0; + while (written < itemSize) + { + i2s_write((i2s_port_t)0, item, itemSize, &written, portMAX_DELAY); + } + vRingbufferReturnItem(dataBuffer, (void *)item); + } + } +} + +void BufferedAudioSink::startI2sFeed(size_t buf_size) +{ + dataBuffer = xRingbufferCreate(buf_size, RINGBUF_TYPE_BYTEBUF); + xTaskCreatePinnedToCore(&i2sFeed, "i2sFeed", 4096, NULL, 10, NULL, tskNO_AFFINITY); +} + +void BufferedAudioSink::feedPCMFrames(const uint8_t *buffer, size_t bytes) +{ + feedPCMFramesInternal(buffer, bytes); +} + +void BufferedAudioSink::feedPCMFramesInternal(const void *pvItem, size_t xItemSize) +{ + xRingbufferSend(dataBuffer, pvItem, xItemSize, portMAX_DELAY); +} + +bool BufferedAudioSink::setRate(uint16_t sampleRate) { + i2s_set_sample_rates((i2s_port_t)0, sampleRate); + return true; +} \ No newline at end of file diff --git a/components/spotify/cspot/bell/src/sinks/esp/ES8388AudioSink.cpp b/components/spotify/cspot/bell/src/sinks/esp/ES8388AudioSink.cpp new file mode 100644 index 00000000..a7f7baa9 --- /dev/null +++ b/components/spotify/cspot/bell/src/sinks/esp/ES8388AudioSink.cpp @@ -0,0 +1,155 @@ +#include "ES8388AudioSink.h" + +struct es8388_cmd_s { + uint8_t reg; + uint8_t value; +}; + +ES8388AudioSink::ES8388AudioSink() +{ + // configure i2c + i2c_config = { + .mode = I2C_MODE_MASTER, + .sda_io_num = 33, + .scl_io_num = 32, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + }; + + i2c_config.master.clk_speed = 100000; + + i2s_config_t i2s_config = { + .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX + .sample_rate = 44100, + .bits_per_sample = (i2s_bits_per_sample_t)16, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels + .communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_MSB, + .intr_alloc_flags = 0, //Default interrupt priority + .dma_buf_count = 8, + .dma_buf_len = 512, + .use_apll = true, + .tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow + .fixed_mclk = 256 * 44100 + }; + + i2s_pin_config_t pin_config = { + .bck_io_num = 27, + .ws_io_num = 25, + .data_out_num = 26, + .data_in_num = -1 //Not used + }; + + int err; + + err = i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL); + if (err != ESP_OK) { + ESP_LOGE("OI", "i2s driver installation error: %d", err); + } + + err = i2s_set_pin((i2s_port_t)0, &pin_config); + if (err != ESP_OK) { + ESP_LOGE("OI", "i2s set pin error: %d", err); + } + + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); + REG_SET_FIELD(PIN_CTRL, CLK_OUT1, 0); + ESP_LOGI("OI", "MCLK output on CLK_OUT1"); + + + err = i2c_param_config(0, &i2c_config); + if (err != ESP_OK) { + ESP_LOGE("OI", "i2c param config error: %d", err); + } + + err = i2c_driver_install(0, I2C_MODE_MASTER, 0, 0, 0); + if (err != ESP_OK) { + ESP_LOGE("OI", "i2c driver installation error: %d", err); + } + + i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create(); + + err = i2c_master_start(i2c_cmd); + if (err != ESP_OK) { + ESP_LOGE("OI", "i2c master start error: %d", err); + } + + /* mute DAC during setup, power up all systems, slave mode */ + writeReg(ES8388_DACCONTROL3, 0x04); + writeReg(ES8388_CONTROL2, 0x50); + writeReg(ES8388_CHIPPOWER, 0x00); + writeReg(ES8388_MASTERMODE, 0x00); + + /* power up DAC and enable LOUT1+2 / ROUT1+2, ADC sample rate = DAC sample rate */ + writeReg(ES8388_DACPOWER, 0x3e); + writeReg(ES8388_CONTROL1, 0x12); + + /* DAC I2S setup: 16 bit word length, I2S format; MCLK / Fs = 256*/ + writeReg(ES8388_DACCONTROL1, 0x18); + writeReg(ES8388_DACCONTROL2, 0x02); + + /* DAC to output route mixer configuration: ADC MIX TO OUTPUT */ + writeReg(ES8388_DACCONTROL16, 0x1B); + writeReg(ES8388_DACCONTROL17, 0x90); + writeReg(ES8388_DACCONTROL20, 0x90); + + /* DAC and ADC use same LRCK, enable MCLK input; output resistance setup */ + writeReg(ES8388_DACCONTROL21, 0x80); + writeReg(ES8388_DACCONTROL23, 0x00); + + /* DAC volume control: 0dB (maximum, unattented) */ + writeReg(ES8388_DACCONTROL5, 0x00); + writeReg(ES8388_DACCONTROL4, 0x00); + + /* power down ADC while configuring; volume: +9dB for both channels */ + writeReg(ES8388_ADCPOWER, 0xff); + writeReg(ES8388_ADCCONTROL1, 0x88); // +24db + + /* select LINPUT2 / RINPUT2 as ADC input; stereo; 16 bit word length, format right-justified, MCLK / Fs = 256 */ + writeReg(ES8388_ADCCONTROL2, 0xf0); // 50 + writeReg(ES8388_ADCCONTROL3, 0x80); // 00 + writeReg(ES8388_ADCCONTROL4, 0x0e); + writeReg(ES8388_ADCCONTROL5, 0x02); + + /* set ADC volume */ + writeReg(ES8388_ADCCONTROL8, 0x20); + writeReg(ES8388_ADCCONTROL9, 0x20); + + /* set LOUT1 / ROUT1 volume: 0dB (unattenuated) */ + writeReg(ES8388_DACCONTROL24, 0x1e); + writeReg(ES8388_DACCONTROL25, 0x1e); + + /* set LOUT2 / ROUT2 volume: 0dB (unattenuated) */ + writeReg(ES8388_DACCONTROL26, 0x1e); + writeReg(ES8388_DACCONTROL27, 0x1e); + + /* power up and enable DAC; power up ADC (no MIC bias) */ + writeReg(ES8388_DACPOWER, 0x3c); + writeReg(ES8388_DACCONTROL3, 0x00); + writeReg(ES8388_ADCPOWER, 0x00); + + startI2sFeed(); +} + +void ES8388AudioSink::writeReg(uint8_t reg_add, uint8_t data) +{ + + int res = 0; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + res |= i2c_master_start(cmd); + res |= i2c_master_write_byte(cmd, ES8388_ADDR, ACK_CHECK_EN); + res |= i2c_master_write_byte(cmd, reg_add, ACK_CHECK_EN); + res |= i2c_master_write_byte(cmd, data, ACK_CHECK_EN); + res |= i2c_master_stop(cmd); + res |= i2c_master_cmd_begin(0, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + + if (res != ESP_OK) { + ESP_LOGE("RR", "Unable to write to ES8388: %d", res); + }else{ + ESP_LOGE("RR", "register successfull written."); + } +} + +ES8388AudioSink::~ES8388AudioSink() +{ +} diff --git a/components/spotify/cspot/bell/src/sinks/esp/ES9018AudioSink.cpp b/components/spotify/cspot/bell/src/sinks/esp/ES9018AudioSink.cpp new file mode 100644 index 00000000..50acba92 --- /dev/null +++ b/components/spotify/cspot/bell/src/sinks/esp/ES9018AudioSink.cpp @@ -0,0 +1,40 @@ +#include "ES9018AudioSink.h" + +#include "driver/i2s.h" + +ES9018AudioSink::ES9018AudioSink() +{ + i2s_config_t i2s_config = { + + .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX + .sample_rate = 44100, + .bits_per_sample = (i2s_bits_per_sample_t)16, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels + .communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_MSB, + .intr_alloc_flags = 0, //Default interrupt priority + .dma_buf_count = 8, + .dma_buf_len = 512, + .use_apll = true, + .tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow + .fixed_mclk = 384 * 44100 + }; + + i2s_pin_config_t pin_config = { + .bck_io_num = 27, + .ws_io_num = 32, + .data_out_num = 25, + .data_in_num = -1 //Not used + }; + i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL); + i2s_set_pin((i2s_port_t)0, &pin_config); + + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); + REG_SET_FIELD(PIN_CTRL, CLK_OUT1, 0); + ESP_LOGI("OI", "MCLK output on CLK_OUT1"); + + startI2sFeed(); +} + +ES9018AudioSink::~ES9018AudioSink() +{ +} diff --git a/components/spotify/cspot/bell/src/sinks/esp/InternalAudioSink.cpp b/components/spotify/cspot/bell/src/sinks/esp/InternalAudioSink.cpp new file mode 100644 index 00000000..abd1e49c --- /dev/null +++ b/components/spotify/cspot/bell/src/sinks/esp/InternalAudioSink.cpp @@ -0,0 +1,33 @@ +#include "InternalAudioSink.h" +#include "driver/i2s.h" + +InternalAudioSink::InternalAudioSink() +{ + softwareVolumeControl = true; + usign = true; + + i2s_config_t i2s_config = { + .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN), // Only TX + .sample_rate = (i2s_bits_per_sample_t)44100, + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_I2S, + .intr_alloc_flags = 0,//ESP_INTR_FLAG_LEVEL1 + .dma_buf_count = 6, + .dma_buf_len = 512, + .use_apll = true, + .tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow + .fixed_mclk=-1 + }; + + //install and start i2s driver + i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL); + //init DAC + i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN); + + startI2sFeed(); +} + +InternalAudioSink::~InternalAudioSink() +{ +} diff --git a/components/spotify/cspot/bell/src/sinks/esp/PCM5102AudioSink.cpp b/components/spotify/cspot/bell/src/sinks/esp/PCM5102AudioSink.cpp new file mode 100644 index 00000000..5cd6ecb6 --- /dev/null +++ b/components/spotify/cspot/bell/src/sinks/esp/PCM5102AudioSink.cpp @@ -0,0 +1,36 @@ +#include "PCM5102AudioSink.h" + +#include "driver/i2s.h" + +PCM5102AudioSink::PCM5102AudioSink() +{ + i2s_config_t i2s_config = { + + .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX + .sample_rate = 44100, + .bits_per_sample = (i2s_bits_per_sample_t)16, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels + .communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_I2S, + .intr_alloc_flags = 0, //Default interrupt priority + .dma_buf_count = 8, + .dma_buf_len = 512, + .use_apll = true, + .tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow + .fixed_mclk = 384 * 44100 + }; + + i2s_pin_config_t pin_config = { + .bck_io_num = 27, + .ws_io_num = 32, + .data_out_num = 25, + .data_in_num = -1 //Not used + }; + i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL); + i2s_set_pin((i2s_port_t)0, &pin_config); + + startI2sFeed(); +} + +PCM5102AudioSink::~PCM5102AudioSink() +{ +} diff --git a/components/spotify/cspot/bell/src/sinks/esp/SPDIFAudioSink.cpp b/components/spotify/cspot/bell/src/sinks/esp/SPDIFAudioSink.cpp new file mode 100644 index 00000000..1ac5e097 --- /dev/null +++ b/components/spotify/cspot/bell/src/sinks/esp/SPDIFAudioSink.cpp @@ -0,0 +1,184 @@ +#include "SPDIFAudioSink.h" + +#include "driver/i2s.h" + +// See http://www.hardwarebook.info/S/PDIF for more info on this protocol +// Conversion table to biphase code mark (LSB first, ending in 1) +static const uint16_t bmc_convert[256] = { + 0x3333, 0xb333, 0xd333, 0x5333, 0xcb33, 0x4b33, 0x2b33, 0xab33, + 0xcd33, 0x4d33, 0x2d33, 0xad33, 0x3533, 0xb533, 0xd533, 0x5533, + 0xccb3, 0x4cb3, 0x2cb3, 0xacb3, 0x34b3, 0xb4b3, 0xd4b3, 0x54b3, + 0x32b3, 0xb2b3, 0xd2b3, 0x52b3, 0xcab3, 0x4ab3, 0x2ab3, 0xaab3, + 0xccd3, 0x4cd3, 0x2cd3, 0xacd3, 0x34d3, 0xb4d3, 0xd4d3, 0x54d3, + 0x32d3, 0xb2d3, 0xd2d3, 0x52d3, 0xcad3, 0x4ad3, 0x2ad3, 0xaad3, + 0x3353, 0xb353, 0xd353, 0x5353, 0xcb53, 0x4b53, 0x2b53, 0xab53, + 0xcd53, 0x4d53, 0x2d53, 0xad53, 0x3553, 0xb553, 0xd553, 0x5553, + 0xcccb, 0x4ccb, 0x2ccb, 0xaccb, 0x34cb, 0xb4cb, 0xd4cb, 0x54cb, + 0x32cb, 0xb2cb, 0xd2cb, 0x52cb, 0xcacb, 0x4acb, 0x2acb, 0xaacb, + 0x334b, 0xb34b, 0xd34b, 0x534b, 0xcb4b, 0x4b4b, 0x2b4b, 0xab4b, + 0xcd4b, 0x4d4b, 0x2d4b, 0xad4b, 0x354b, 0xb54b, 0xd54b, 0x554b, + 0x332b, 0xb32b, 0xd32b, 0x532b, 0xcb2b, 0x4b2b, 0x2b2b, 0xab2b, + 0xcd2b, 0x4d2b, 0x2d2b, 0xad2b, 0x352b, 0xb52b, 0xd52b, 0x552b, + 0xccab, 0x4cab, 0x2cab, 0xacab, 0x34ab, 0xb4ab, 0xd4ab, 0x54ab, + 0x32ab, 0xb2ab, 0xd2ab, 0x52ab, 0xcaab, 0x4aab, 0x2aab, 0xaaab, + 0xcccd, 0x4ccd, 0x2ccd, 0xaccd, 0x34cd, 0xb4cd, 0xd4cd, 0x54cd, + 0x32cd, 0xb2cd, 0xd2cd, 0x52cd, 0xcacd, 0x4acd, 0x2acd, 0xaacd, + 0x334d, 0xb34d, 0xd34d, 0x534d, 0xcb4d, 0x4b4d, 0x2b4d, 0xab4d, + 0xcd4d, 0x4d4d, 0x2d4d, 0xad4d, 0x354d, 0xb54d, 0xd54d, 0x554d, + 0x332d, 0xb32d, 0xd32d, 0x532d, 0xcb2d, 0x4b2d, 0x2b2d, 0xab2d, + 0xcd2d, 0x4d2d, 0x2d2d, 0xad2d, 0x352d, 0xb52d, 0xd52d, 0x552d, + 0xccad, 0x4cad, 0x2cad, 0xacad, 0x34ad, 0xb4ad, 0xd4ad, 0x54ad, + 0x32ad, 0xb2ad, 0xd2ad, 0x52ad, 0xcaad, 0x4aad, 0x2aad, 0xaaad, + 0x3335, 0xb335, 0xd335, 0x5335, 0xcb35, 0x4b35, 0x2b35, 0xab35, + 0xcd35, 0x4d35, 0x2d35, 0xad35, 0x3535, 0xb535, 0xd535, 0x5535, + 0xccb5, 0x4cb5, 0x2cb5, 0xacb5, 0x34b5, 0xb4b5, 0xd4b5, 0x54b5, + 0x32b5, 0xb2b5, 0xd2b5, 0x52b5, 0xcab5, 0x4ab5, 0x2ab5, 0xaab5, + 0xccd5, 0x4cd5, 0x2cd5, 0xacd5, 0x34d5, 0xb4d5, 0xd4d5, 0x54d5, + 0x32d5, 0xb2d5, 0xd2d5, 0x52d5, 0xcad5, 0x4ad5, 0x2ad5, 0xaad5, + 0x3355, 0xb355, 0xd355, 0x5355, 0xcb55, 0x4b55, 0x2b55, 0xab55, + 0xcd55, 0x4d55, 0x2d55, 0xad55, 0x3555, 0xb555, 0xd555, 0x5555, +}; + +#define I2S_BUG_MAGIC (26 * 1000 * 1000) // magic number for avoiding I2S bug +#define BITS_PER_SUBFRAME 64 +#define FRAMES_PER_BLOCK 192 +#define SPDIF_BUF_SIZE (BITS_PER_SUBFRAME/8 * 2 * FRAMES_PER_BLOCK) +#define SPDIF_BUF_ARRAY_SIZE (SPDIF_BUF_SIZE / sizeof(uint32_t)) + +#define BMC_B 0x33173333 // block start +#define BMC_M 0x331d3333 // left ch +#define BMC_W 0x331b3333 // right ch +#define BMC_MW_DIF (BMC_M ^ BMC_W) + +static uint32_t spdif_buf[SPDIF_BUF_ARRAY_SIZE]; +static uint32_t *spdif_ptr; + +static void spdif_buf_init(void) +{ + // first bllock has W preamble + spdif_buf[0] = BMC_B; + + // all other blocks are alternating M, then W preamble + uint32_t bmc_mw = BMC_M; + for (int i = 2; i < SPDIF_BUF_ARRAY_SIZE; i += 2) + { + spdif_buf[i] = bmc_mw ^= BMC_MW_DIF; + } +} + +SPDIFAudioSink::SPDIFAudioSink(uint8_t spdifPin) +{ + // initialize S/PDIF buffer + spdif_buf_init(); + spdif_ptr = spdif_buf; + this->spdifPin = spdifPin; + this->initialize(44100); + startI2sFeed(SPDIF_BUF_SIZE * 16); +} + +void SPDIFAudioSink::initialize(uint16_t sampleRate) { + int sample_rate = sampleRate * 2; + int bclk = sample_rate * 64 * 2; + int mclk = (I2S_BUG_MAGIC / bclk) * bclk; + + i2s_config_t i2s_config = { + .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), + .sample_rate = (uint32_t) sample_rate, + .bits_per_sample = (i2s_bits_per_sample_t)32, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = I2S_COMM_FORMAT_STAND_I2S, + .intr_alloc_flags = 0, + .dma_buf_count = 8, + .dma_buf_len = 512, + .use_apll = true, + .tx_desc_auto_clear = true, + .fixed_mclk = mclk, // avoiding I2S bug + }; + i2s_pin_config_t pin_config = { + .bck_io_num = -1, + .ws_io_num = -1, + .data_out_num = spdifPin, + .data_in_num = -1, + }; + i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL); + i2s_set_pin((i2s_port_t)0, &pin_config); +} + +SPDIFAudioSink::~SPDIFAudioSink() +{ +} + +bool SPDIFAudioSink::setRate(uint16_t sampleRate) { + i2s_driver_uninstall((i2s_port_t)0); + this->initialize(sampleRate); + return true; +} + +int num_frames = 0; + +void SPDIFAudioSink::feedPCMFrames(const uint8_t *buffer, size_t bytes) +{ + for (int i = 0; i < bytes; i += 2) + { + /** + * What is this, and why does it work? + * + * Rather than assemble all S/PDIF frames from scratch we want to do the + * minimum amount of work possible. To that extent, we fix the final four + * bits (VUCP) to be all-zero prior to BMC encoding (= valid, no subcode + * or channel-status bits set, even parity), and zero the lowest 8 sample + * bits (prior to BMC encoding). This is all done in spdif_buf_init(), + * aligning at word boundaries and setting alternating preambles as well + * as encoding 8 bits of zeros as 0x33, leaving the final bit high. + * + * We must therefore BMC encode our 16 bit PCM data in such a way that: + * - the firstĀ (least significant) bit is 0 (to fit with 0x33 zeros) + * - the final bit is 1 (so as to fit with the following 0x33 VUCP bits) + * - the result has even parity + * + * As biphase mark code retains parity (0 encodes as two 1s or two 0s), + * this is evidently not possible without loss of data, as the input PCM + * data isn't already even parity. We can use the first (least significant) + * bit as parity bit to achieve our desired encoding. + * + * The bmc_convert table converts the lower and upper 8 bit of our PCM + * frames into 16 bit biphase mark code patterns with the first two bits + * encoding the LSB and the final bit always high. We combine both 16bit + * patterns into a 32 bit encoding of our original input data by shifting + * the first (lower) 16 bit into position, then sign-extending the second + * (higher) 16bit pattern. If that pattern started with a 1, the resulting + * 32 bit pattern will now contain 1s in the first 16 bits. + * + * Keep in mind that the shifted value in the first (lower) 16 bits always + * ends in a 1 bit, so the entire pattern must be flipped in case the + * second (higher) 16 bit pattern starts with a 1 bit. XORing the sign- + * extended component to the first one achieves exactly that. + * + * Finally, we zero out the very first bit of the resulting value. This + * may change the lowest bit of our encoded value, but ensures that our + * newly encoded bits form a valid BMC pattern with the already zeroed out + * lower 8 bits in the pattern set up in spdif_buf_init(). + * + * Further, this also happens to ensure even parity: + * All entries in the BMC table end in a 1, so an all-zero pattern would + * end (after encoding an even number of bits) in two 0 bits. Setting any + * bit will cause the BMC-encoded pattern to flip its first (lowest) bit, + * meaning we can use that bit to infer parity. Setting it to zero flips + * the first (lowest) bit such that we always have even parity. + * + * I did not come up with this, all credit goes to + * github.com/amedes/esp_a2dp_sink_spdif + */ + uint32_t lo = ((uint32_t)(bmc_convert[buffer[i]]) << 16); + uint32_t hi = (uint32_t)((int16_t)bmc_convert[buffer[i+1]]); + + *(spdif_ptr + 1) = ((lo ^ hi) << 1) >> 1; + + spdif_ptr += 2; // advance to next audio data + + if (spdif_ptr >= &spdif_buf[SPDIF_BUF_ARRAY_SIZE]) { + feedPCMFramesInternal(spdif_buf, sizeof(spdif_buf)); + spdif_ptr = spdif_buf; + } + } +} \ No newline at end of file diff --git a/components/spotify/cspot/bell/src/sinks/esp/TAS5711AudioSink.cpp b/components/spotify/cspot/bell/src/sinks/esp/TAS5711AudioSink.cpp new file mode 100644 index 00000000..0d2d847e --- /dev/null +++ b/components/spotify/cspot/bell/src/sinks/esp/TAS5711AudioSink.cpp @@ -0,0 +1,121 @@ +#include "TAS5711AudioSink.h" + + +struct tas5711_cmd_s { + uint8_t reg; + uint8_t value; +}; + +static const struct tas5711_cmd_s tas5711_init_sequence[] = { + { 0x00, 0x6c }, // 0x6c - 256 x mclk + { 0x04, 0x03 }, // 0x03 - 16 bit i2s + { 0x05, 0x00 }, // system control 0x00 is audio playback + { 0x06, 0x00 }, // disable mute + { 0x07, 0x50 }, // volume register + { 0xff, 0xff } + +}; +i2c_ack_type_t ACK_CHECK_EN = (i2c_ack_type_t)0x1; + +TAS5711AudioSink::TAS5711AudioSink() +{ + i2s_config_t i2s_config = { + + .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX + .sample_rate = 44100, + .bits_per_sample = (i2s_bits_per_sample_t)16, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels + .communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_MSB, + .intr_alloc_flags = 0, //Default interrupt priority + .dma_buf_count = 8, + .dma_buf_len = 512, + .use_apll = true, + .tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow + .fixed_mclk = 256 * 44100 + }; + + + i2s_pin_config_t pin_config = { + .bck_io_num = 5, + .ws_io_num = 25, + .data_out_num = 26, + .data_in_num = -1 //Not used + }; + i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL); + i2s_set_pin((i2s_port_t)0, &pin_config); + + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); + REG_SET_FIELD(PIN_CTRL, CLK_OUT1, 0); + ESP_LOGI("OI", "MCLK output on CLK_OUT1"); + + // configure i2c + i2c_config = { + .mode = I2C_MODE_MASTER, + .sda_io_num = 21, + .scl_io_num = 23, + .sda_pullup_en = GPIO_PULLUP_DISABLE, + .scl_pullup_en = GPIO_PULLUP_DISABLE, + }; + + i2c_config.master.clk_speed = 250000; + + i2c_param_config(i2c_port, &i2c_config); + i2c_driver_install(i2c_port, I2C_MODE_MASTER, false, false, false); + i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create(); + + uint8_t data, addr = (0x1b); + + i2c_master_start(i2c_cmd); + i2c_master_write_byte(i2c_cmd, (addr << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN); + i2c_master_write_byte(i2c_cmd, 00, ACK_CHECK_EN); + + i2c_master_start(i2c_cmd); + i2c_master_write_byte(i2c_cmd, (addr << 1) | I2C_MASTER_READ, ACK_CHECK_EN); + i2c_master_read_byte(i2c_cmd, &data, ACK_CHECK_EN); + + i2c_master_stop(i2c_cmd); + int ret = i2c_master_cmd_begin(i2c_port, i2c_cmd, 50 / portTICK_RATE_MS); + i2c_cmd_link_delete(i2c_cmd); + + if (ret == ESP_OK) { + ESP_LOGI("RR", "Detected TAS"); + } + else { + ESP_LOGI("RR", "Unable to detect dac"); + } + + writeReg(0x1b, 0x00); + vTaskDelay(100 / portTICK_PERIOD_MS); + + + for (int i = 0; tas5711_init_sequence[i].reg != 0xff; i++) { + writeReg(tas5711_init_sequence[i].reg, tas5711_init_sequence[i].value); + vTaskDelay(1 / portTICK_PERIOD_MS); + } + + startI2sFeed(); +} + +void TAS5711AudioSink::writeReg(uint8_t reg, uint8_t value) +{ + i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create(); + + i2c_master_start(i2c_cmd); + i2c_master_write_byte(i2c_cmd, (0x1b << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN); + i2c_master_write_byte(i2c_cmd, reg, ACK_CHECK_EN); + i2c_master_write_byte(i2c_cmd, value, ACK_CHECK_EN); + + + i2c_master_stop(i2c_cmd); + esp_err_t res = i2c_master_cmd_begin(i2c_port, i2c_cmd, 500 / portTICK_RATE_MS); + + if (res != ESP_OK) { + ESP_LOGE("RR", "Unable to write to TAS5711"); + } + i2c_cmd_link_delete(i2c_cmd); + +} + +TAS5711AudioSink::~TAS5711AudioSink() +{ +} diff --git a/components/spotify/cspot/bell/src/sinks/esp/ac101.c b/components/spotify/cspot/bell/src/sinks/esp/ac101.c new file mode 100644 index 00000000..4e39782f --- /dev/null +++ b/components/spotify/cspot/bell/src/sinks/esp/ac101.c @@ -0,0 +1,426 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2018 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "adac.h" +#include "ac101.h" + +const static char TAG[] = "AC101"; + +#define SPKOUT_EN ((1 << 9) | (1 << 11) | (1 << 7) | (1 << 5)) +#define EAROUT_EN ((1 << 11) | (1 << 12) | (1 << 13)) +#define BIN(a, b, c, d) 0b##a##b##c##d + +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +#define AC_ASSERT(a, format, b, ...) \ + if ((a) != 0) \ + { \ + ESP_LOGE(TAG, format, ##__VA_ARGS__); \ + return b; \ + } + +static bool init(int i2c_port_num, int i2s_num, i2s_config_t *config); +static void deinit(void); +static void speaker(bool active); +static void headset(bool active); +static void volume(unsigned left, unsigned right); +static void power(adac_power_e mode); + +struct adac_s dac_a1s = {init, deinit, power, speaker, headset, volume}; + +static esp_err_t i2c_write_reg(uint8_t reg, uint16_t val); +static uint16_t i2c_read_reg(uint8_t reg); +static void ac101_start(ac_module_t mode); +static void ac101_stop(void); +static void ac101_set_earph_volume(uint8_t volume); +static void ac101_set_spk_volume(uint8_t volume); +static int ac101_get_spk_volume(void); + +static int i2c_port; + +/**************************************************************************************** + * init + */ +static bool init(int i2c_port_num, int i2s_num, i2s_config_t *i2s_config) +{ + esp_err_t res = ESP_OK; + + i2c_port = i2c_port_num; + + // configure i2c + i2c_config_t i2c_config = { + .mode = I2C_MODE_MASTER, + .sda_io_num = 33, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_io_num = 32, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + .master.clk_speed = 250000, + }; + + i2c_param_config(i2c_port, &i2c_config); + i2c_driver_install(i2c_port, I2C_MODE_MASTER, false, false, false); + + res = i2c_read_reg(CHIP_AUDIO_RS); + + if (!res) + { + ESP_LOGW(TAG, "No AC101 detected"); + i2c_driver_delete(i2c_port); + return 0; + } + + ESP_LOGI(TAG, "AC101 DAC using I2C sda:%u, scl:%u", i2c_config.sda_io_num, i2c_config.scl_io_num); + + res = i2c_write_reg(CHIP_AUDIO_RS, 0x123); + // huh? + vTaskDelay(100 / portTICK_PERIOD_MS); + + // enable the PLL from BCLK source + i2c_write_reg(PLL_CTRL1, BIN(0000, 0001, 0100, 1111)); // F=1,M=1,PLL,INT=31 (medium) + i2c_write_reg(PLL_CTRL2, BIN(1000, 0110, 0000, 0000)); // PLL, F=96,N_i=1024-96,F=0,N_f=0*0.2; + // i2c_write_reg(PLL_CTRL2, BIN(1000,0011,1100,0000)); + + // clocking system + i2c_write_reg(SYSCLK_CTRL, BIN(1010, 1010, 0000, 1000)); // PLLCLK, BCLK1, IS1CLK, PLL, SYSCLK + i2c_write_reg(MOD_CLK_ENA, BIN(1000, 0000, 0000, 1100)); // IS21, ADC, DAC + i2c_write_reg(MOD_RST_CTRL, BIN(1000, 0000, 0000, 1100)); // IS21, ADC, DAC + i2c_write_reg(I2S_SR_CTRL, BIN(0111, 0000, 0000, 0000)); // 44.1kHz + + // analogue config + i2c_write_reg(I2S1LCK_CTRL, BIN(1000, 1000, 0101, 0000)); // Slave, BCLK=I2S/8,LRCK=32,16bits,I2Smode, Stereo + i2c_write_reg(I2S1_SDOUT_CTRL, BIN(1100, 0000, 0000, 0000)); // I2S1ADC (R&L) + i2c_write_reg(I2S1_SDIN_CTRL, BIN(1100, 0000, 0000, 0000)); // IS21DAC (R&L) + i2c_write_reg(I2S1_MXR_SRC, BIN(0010, 0010, 0000, 0000)); // ADCL, ADCR + i2c_write_reg(ADC_SRCBST_CTRL, BIN(0100, 0100, 0100, 0000)); // disable all boost (default) +#if ENABLE_ADC + i2c_write_reg(ADC_SRC, BIN(0000, 0100, 0000, 1000)); // source=linein(R/L) + i2c_write_reg(ADC_DIG_CTRL, BIN(1000, 0000, 0000, 0000)); // enable digital ADC + i2c_write_reg(ADC_ANA_CTRL, BIN(1011, 1011, 0000, 0000)); // enable analogue R/L, 0dB +#else + i2c_write_reg(ADC_SRC, BIN(0000, 0000, 0000, 0000)); // source=none + i2c_write_reg(ADC_DIG_CTRL, BIN(0000, 0000, 0000, 0000)); // disable digital ADC + i2c_write_reg(ADC_ANA_CTRL, BIN(0011, 0011, 0000, 0000)); // disable analogue R/L, 0dB +#endif + + //Path Configuration + i2c_write_reg(DAC_MXR_SRC, BIN(1000, 1000, 0000, 0000)); // DAC from I2S + i2c_write_reg(DAC_DIG_CTRL, BIN(1000, 0000, 0000, 0000)); // enable DAC + i2c_write_reg(OMIXER_DACA_CTRL, BIN(1111, 0000, 0000, 0000)); // enable DAC/Analogue (see note on offset removal and PA) + i2c_write_reg(OMIXER_DACA_CTRL, BIN(1111, 1111, 0000, 0000)); // this toggle is needed for headphone PA offset +#if ENABLE_ADC + i2c_write_reg(OMIXER_SR, BIN(0000, 0001, 0000, 0010)); // source=DAC(R/L) (are DACR and DACL really inverted in bitmap?) +#else + i2c_write_reg(OMIXER_SR, BIN(0000, 0101, 0000, 1010)); // source=DAC(R/L) and LINEIN(R/L) +#endif + + // configure I2S pins & install driver + i2s_pin_config_t i2s_pin_config = (i2s_pin_config_t){.bck_io_num = 27, .ws_io_num = 26, .data_out_num = 25, .data_in_num = -1}; + res |= i2s_driver_install(i2s_num, i2s_config, 0, NULL); + res |= i2s_set_pin(i2s_num, &i2s_pin_config); + + // enable earphone & speaker + i2c_write_reg(SPKOUT_CTRL, 0x0220); + i2c_write_reg(HPOUT_CTRL, 0xf801); + + // set gain for speaker and earphone + ac101_set_spk_volume(70); + ac101_set_earph_volume(70); + + ESP_LOGI(TAG, "DAC using I2S bck:%d, ws:%d, do:%d", i2s_pin_config.bck_io_num, i2s_pin_config.ws_io_num, i2s_pin_config.data_out_num); + + return (res == ESP_OK); +} + +/**************************************************************************************** + * init + */ +static void deinit(void) +{ + i2c_driver_delete(i2c_port); +} + +/**************************************************************************************** + * change volume + */ +static void volume(unsigned left, unsigned right) +{ + ac101_set_earph_volume(left); + // nothing at that point, volume is handled by backend +} + +/**************************************************************************************** + * power + */ +static void power(adac_power_e mode) +{ + switch (mode) + { + case ADAC_STANDBY: + case ADAC_OFF: + ac101_stop(); + break; + case ADAC_ON: + ac101_start(AC_MODULE_DAC); + break; + default: + ESP_LOGW(TAG, "unknown power command"); + break; + } +} + +/**************************************************************************************** + * speaker + */ +static void speaker(bool active) +{ + uint16_t value = i2c_read_reg(SPKOUT_CTRL); + if (active) + i2c_write_reg(SPKOUT_CTRL, value | SPKOUT_EN); + else + i2c_write_reg(SPKOUT_CTRL, value & ~SPKOUT_EN); +} + +/**************************************************************************************** + * headset + */ +static void headset(bool active) +{ + // there might be aneed to toggle OMIXER_DACA_CTRL 11:8, not sure + uint16_t value = i2c_read_reg(HPOUT_CTRL); + if (active) + i2c_write_reg(HPOUT_CTRL, value | EAROUT_EN); + else + i2c_write_reg(HPOUT_CTRL, value & ~EAROUT_EN); +} + +/**************************************************************************************** + * + */ +static esp_err_t i2c_write_reg(uint8_t reg, uint16_t val) +{ + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + esp_err_t ret = 0; + uint8_t send_buff[4]; + send_buff[0] = (AC101_ADDR << 1); + send_buff[1] = reg; + send_buff[2] = (val >> 8) & 0xff; + send_buff[3] = val & 0xff; + ret |= i2c_master_start(cmd); + ret |= i2c_master_write(cmd, send_buff, 4, ACK_CHECK_EN); + ret |= i2c_master_stop(cmd); + ret |= i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + return ret; +} + +/**************************************************************************************** + * + */ +static uint16_t i2c_read_reg(uint8_t reg) +{ + uint8_t data[2] = {0}; + + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (AC101_ADDR << 1) | WRITE_BIT, ACK_CHECK_EN); + i2c_master_write_byte(cmd, reg, ACK_CHECK_EN); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (AC101_ADDR << 1) | READ_BIT, ACK_CHECK_EN); //check or not + i2c_master_read(cmd, data, 2, ACK_VAL); + i2c_master_stop(cmd); + i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + + return (data[0] << 8) + data[1]; + ; +} + +/**************************************************************************************** + * + */ +void set_sample_rate(int rate) +{ + if (rate == 8000) + rate = SAMPLE_RATE_8000; + else if (rate == 11025) + rate = SAMPLE_RATE_11052; + else if (rate == 12000) + rate = SAMPLE_RATE_12000; + else if (rate == 16000) + rate = SAMPLE_RATE_16000; + else if (rate == 22050) + rate = SAMPLE_RATE_22050; + else if (rate == 24000) + rate = SAMPLE_RATE_24000; + else if (rate == 32000) + rate = SAMPLE_RATE_32000; + else if (rate == 44100) + rate = SAMPLE_RATE_44100; + else if (rate == 48000) + rate = SAMPLE_RATE_48000; + else if (rate == 96000) + rate = SAMPLE_RATE_96000; + else if (rate == 192000) + rate = SAMPLE_RATE_192000; + else + { + ESP_LOGW(TAG, "Unknown sample rate %hu", rate); + rate = SAMPLE_RATE_44100; + } + i2c_write_reg(I2S_SR_CTRL, rate); +} + +/**************************************************************************************** + * Get normalized (0..100) speaker volume + */ +static int ac101_get_spk_volume(void) +{ + return ((i2c_read_reg(SPKOUT_CTRL) & 0x1f) * 100) / 0x1f; +} + +/**************************************************************************************** + * Set normalized (0..100) volume + */ +static void ac101_set_spk_volume(uint8_t volume) +{ + uint16_t value = min(volume, 100); + value = ((int)value * 0x1f) / 100; + value |= i2c_read_reg(SPKOUT_CTRL) & ~0x1f; + i2c_write_reg(SPKOUT_CTRL, value); +} + +/**************************************************************************************** + * Get normalized (0..100) earphone volume + */ +static int ac101_get_earph_volume(void) +{ + return (((i2c_read_reg(HPOUT_CTRL) >> 4) & 0x3f) * 100) / 0x3f; +} + +/**************************************************************************************** + * Set normalized (0..100) earphone volume + */ +static void ac101_set_earph_volume(uint8_t volume) +{ + uint16_t value = min(volume, 255); + value = (((int)value * 0x3f) / 255) << 4; + value |= i2c_read_reg(HPOUT_CTRL) & ~(0x3f << 4); + i2c_write_reg(HPOUT_CTRL, value); +} + +/**************************************************************************************** + * + */ +static void ac101_set_output_mixer_gain(ac_output_mixer_gain_t gain, ac_output_mixer_source_t source) +{ + uint16_t regval, temp, clrbit; + regval = i2c_read_reg(OMIXER_BST1_CTRL); + switch (source) + { + case SRC_MIC1: + temp = (gain & 0x7) << 6; + clrbit = ~(0x7 << 6); + break; + case SRC_MIC2: + temp = (gain & 0x7) << 3; + clrbit = ~(0x7 << 3); + break; + case SRC_LINEIN: + temp = (gain & 0x7); + clrbit = ~0x7; + break; + default: + return; + } + regval &= clrbit; + regval |= temp; + i2c_write_reg(OMIXER_BST1_CTRL, regval); +} + +/**************************************************************************************** + * + */ +static void ac101_start(ac_module_t mode) +{ + if (mode == AC_MODULE_LINE) + { + i2c_write_reg(0x51, 0x0408); + i2c_write_reg(0x40, 0x8000); + i2c_write_reg(0x50, 0x3bc0); + } + if (mode == AC_MODULE_ADC || mode == AC_MODULE_ADC_DAC || mode == AC_MODULE_LINE) + { + // I2S1_SDOUT_CTRL + // i2c_write_reg(PLL_CTRL2, 0x8120); + i2c_write_reg(0x04, 0x800c); + i2c_write_reg(0x05, 0x800c); + // res |= i2c_write_reg(0x06, 0x3000); + } + if (mode == AC_MODULE_DAC || mode == AC_MODULE_ADC_DAC || mode == AC_MODULE_LINE) + { + uint16_t value = i2c_read_reg(PLL_CTRL2); + value |= 0x8000; + i2c_write_reg(PLL_CTRL2, value); + } +} + +/**************************************************************************************** + * + */ +static void ac101_stop(void) +{ + uint16_t value = i2c_read_reg(PLL_CTRL2); + value &= ~0x8000; + i2c_write_reg(PLL_CTRL2, value); +} + +/**************************************************************************************** + * + */ +static void ac101_deinit(void) +{ + i2c_write_reg(CHIP_AUDIO_RS, 0x123); //soft reset +} + +/**************************************************************************************** + * Don't know when this one is supposed to be called + */ +static void ac101_i2s_config_clock(ac_i2s_clock_t *cfg) +{ + uint16_t regval = 0; + regval = i2c_read_reg(I2S1LCK_CTRL); + regval &= 0xe03f; + regval |= (cfg->bclk_div << 9); + regval |= (cfg->lclk_div << 6); + i2c_write_reg(I2S1LCK_CTRL, regval); +} \ No newline at end of file diff --git a/components/spotify/cspot/bell/src/sinks/unix/ALSAAudioSink.cpp b/components/spotify/cspot/bell/src/sinks/unix/ALSAAudioSink.cpp new file mode 100644 index 00000000..0219fbbe --- /dev/null +++ b/components/spotify/cspot/bell/src/sinks/unix/ALSAAudioSink.cpp @@ -0,0 +1,101 @@ +#include "ALSAAudioSink.h" + +ALSAAudioSink::ALSAAudioSink() : Task("", 0, 0, 0) +{ + /* Open the PCM device in playback mode */ + if (pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE, + SND_PCM_STREAM_PLAYBACK, 0) < 0) + { + printf("ERROR: Can't open \"%s\" PCM device. %s\n", + PCM_DEVICE, snd_strerror(pcm)); + } + + /* Allocate parameters object and fill it with default values*/ + snd_pcm_hw_params_alloca(¶ms); + + snd_pcm_hw_params_any(pcm_handle, params); + + /* Set parameters */ + if (pcm = snd_pcm_hw_params_set_access(pcm_handle, params, + SND_PCM_ACCESS_RW_INTERLEAVED) < 0) + printf("ERROR: Can't set interleaved mode. %s\n", snd_strerror(pcm)); + + if (pcm = snd_pcm_hw_params_set_format(pcm_handle, params, + SND_PCM_FORMAT_S16_LE) < 0) + printf("ERROR: Can't set format. %s\n", snd_strerror(pcm)); + + if (pcm = snd_pcm_hw_params_set_channels(pcm_handle, params, 2) < 0) + printf("ERROR: Can't set channels number. %s\n", snd_strerror(pcm)); + unsigned int rate = 44100; + if (pcm = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0) < 0) + printf("ERROR: Can't set rate. %s\n", snd_strerror(pcm)); + unsigned int periodTime = 800; + int dir = -1; + snd_pcm_hw_params_set_period_time_near(pcm_handle, params, &periodTime, &dir); + /* Write parameters */ + if (pcm = snd_pcm_hw_params(pcm_handle, params) < 0) + printf("ERROR: Can't set harware parameters. %s\n", snd_strerror(pcm)); + + /* Resume information */ + printf("PCM name: '%s'\n", snd_pcm_name(pcm_handle)); + + printf("PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle))); + unsigned int tmp; + snd_pcm_hw_params_get_channels(params, &tmp); + printf("channels: %i ", tmp); + if (tmp == 1) + printf("(mono)\n"); + else if (tmp == 2) + printf("(stereo)\n"); + + snd_pcm_hw_params_get_period_time(params, &tmp, NULL); + printf("period_time = %d\n", tmp); + snd_pcm_hw_params_get_period_size(params, &frames, 0); + + this->buff_size = frames * 2 * 2 /* 2 -> sample size */; + printf("required buff_size: %d\n", buff_size); + this->startTask(); +} + +ALSAAudioSink::~ALSAAudioSink() +{ + snd_pcm_drain(pcm_handle); + snd_pcm_close(pcm_handle); +} + +void ALSAAudioSink::runTask() +{ + std::unique_ptr> dataPtr; + while (true) + { + if (!this->ringbuffer.pop(dataPtr)) + { + usleep(100); + continue; + } + if (pcm = snd_pcm_writei(pcm_handle, dataPtr->data(), this->frames) == -EPIPE) + { + + snd_pcm_prepare(pcm_handle); + } + else if (pcm < 0) + { + printf("ERROR. Can't write to PCM device. %s\n", snd_strerror(pcm)); + } + } +} + +void ALSAAudioSink::feedPCMFrames(const uint8_t *buffer, size_t bytes) +{ + + buff.insert(buff.end(), buffer, buffer + bytes); + while (buff.size() > this->buff_size) + { + auto ptr = std::make_unique>(this->buff.begin(), this->buff.begin() + this->buff_size); + this->buff = std::vector(this->buff.begin() + this->buff_size, this->buff.end()); + while (!this->ringbuffer.push(ptr)) + { + usleep(100); + }; + } +} diff --git a/components/spotify/cspot/bell/src/sinks/unix/NamedPipeAudioSink.cpp b/components/spotify/cspot/bell/src/sinks/unix/NamedPipeAudioSink.cpp new file mode 100644 index 00000000..2c310786 --- /dev/null +++ b/components/spotify/cspot/bell/src/sinks/unix/NamedPipeAudioSink.cpp @@ -0,0 +1,21 @@ +#include "NamedPipeAudioSink.h" + +NamedPipeAudioSink::NamedPipeAudioSink() +{ + printf("Start\n"); + this->namedPipeFile = std::ofstream("outputFifo", std::ios::binary); + printf("stop\n"); + +} + +NamedPipeAudioSink::~NamedPipeAudioSink() +{ + this->namedPipeFile.close(); +} + +void NamedPipeAudioSink::feedPCMFrames(const uint8_t *buffer, size_t bytes) +{ + // Write the actual data + this->namedPipeFile.write((char*)buffer, (long)bytes); + this->namedPipeFile.flush(); +} diff --git a/components/spotify/cspot/bell/src/sinks/unix/PortAudioSink.cpp b/components/spotify/cspot/bell/src/sinks/unix/PortAudioSink.cpp new file mode 100644 index 00000000..48bdb1d0 --- /dev/null +++ b/components/spotify/cspot/bell/src/sinks/unix/PortAudioSink.cpp @@ -0,0 +1,53 @@ +#include "PortAudioSink.h" + +PortAudioSink::PortAudioSink() +{ + Pa_Initialize(); + this->initialize(44100); +} + +void PortAudioSink::initialize(uint16_t sampleRate) { + PaStreamParameters outputParameters; + outputParameters.device = Pa_GetDefaultOutputDevice(); + if (outputParameters.device == paNoDevice) { + printf("PortAudio: Default audio device not found!\n"); + // exit(0); + } + printf("PortAudio: Default audio device not found!\n"); + + outputParameters.channelCount = 2; /* stereo output */ + outputParameters.sampleFormat = paInt16; /* 32 bit floating point output */ + outputParameters.suggestedLatency = 0.050; + outputParameters.hostApiSpecificStreamInfo = NULL; + + PaError err = Pa_OpenStream( + &stream, + NULL, + &outputParameters, + sampleRate, + 4096 / 4, + paClipOff, + NULL, // blocking api + NULL + ); + Pa_StartStream(stream); +} + +PortAudioSink::~PortAudioSink() +{ + Pa_StopStream(stream); + Pa_Terminate(); +} + +bool PortAudioSink::setRate(uint16_t sampleRate) { + if (Pa_GetStreamInfo(stream)->sampleRate != sampleRate) { + Pa_StopStream(stream); + this->initialize(sampleRate); + } + return true; +} + +void PortAudioSink::feedPCMFrames(const uint8_t *buffer, size_t bytes) +{ + Pa_WriteStream(stream, buffer, bytes / 4); +} diff --git a/components/spotify/cspot/include/AudioChunk.h b/components/spotify/cspot/include/AudioChunk.h index b633bd10..3aa46878 100644 --- a/components/spotify/cspot/include/AudioChunk.h +++ b/components/spotify/cspot/include/AudioChunk.h @@ -62,7 +62,7 @@ public: * * @param data encrypted binary audio data. */ - void appendData(std::vector &data); + void appendData(const std::vector &data); /** * @brief Performs AES CTR decryption of received data. diff --git a/components/spotify/cspot/include/AudioChunkManager.h b/components/spotify/cspot/include/AudioChunkManager.h index d8956fd6..f120a129 100644 --- a/components/spotify/cspot/include/AudioChunkManager.h +++ b/components/spotify/cspot/include/AudioChunkManager.h @@ -21,6 +21,7 @@ public: AudioChunkManager(); std::atomic isRunning = false; std::mutex runningMutex; + std::mutex chunkMutex; /** * @brief Registers a new audio chunk request. * @@ -54,4 +55,4 @@ public: void close(); }; -#endif \ No newline at end of file +#endif diff --git a/components/spotify/cspot/include/AudioSink.h b/components/spotify/cspot/include/AudioSink.h deleted file mode 100644 index a2f2a0a7..00000000 --- a/components/spotify/cspot/include/AudioSink.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef AUDIOSINK_H -#define AUDIOSINK_H - -#include -#include - -class AudioSink -{ -public: - AudioSink() {} - virtual ~AudioSink() {} - virtual void feedPCMFrames(std::vector &data) = 0; - virtual void volumeChanged(uint16_t volume) {} - bool softwareVolumeControl = true; - bool usign = false; -}; - -#endif diff --git a/components/spotify/cspot/include/ChunkedAudioStream.h b/components/spotify/cspot/include/ChunkedAudioStream.h index e0b8f5ad..5aa5bdd5 100644 --- a/components/spotify/cspot/include/ChunkedAudioStream.h +++ b/components/spotify/cspot/include/ChunkedAudioStream.h @@ -12,6 +12,7 @@ #include "AudioSink.h" #include "AudioChunk.h" #include "platform/WrappedMutex.h" +#include "ChunkedByteStream.h" #define SPOTIFY_HEADER_SIZE 167 #define BUFFER_SIZE 0x20000 * 1.5 @@ -32,10 +33,6 @@ private: ov_callbacks vorbisCallbacks; int currentSection; - // Audio chunking - std::vector audioKey; - std::vector> chunks; - // Audio data uint32_t duration; @@ -46,19 +43,12 @@ private: std::vector fileId; uint32_t startPositionMs; - std::shared_ptr requestChunk(size_t chunkIndex); - void fetchTraillingPacket(); - std::shared_ptr findChunkForPosition(size_t position); - public: ChunkedAudioStream(std::vector fileId, std::vector audioKey, uint32_t duration, std::shared_ptr manager, uint32_t startPositionMs, bool isPaused); ~ChunkedAudioStream(); - int requestedChunkIndex = 0; + std::shared_ptr byteStream; + std::function streamFinishedCallback; - size_t pos = SPOTIFY_HEADER_SIZE; // size of some spotify header - uint32_t fileSize; - uint32_t readBeforeSeek = 0; - bool loadingMeta = true; std::atomic isPaused = false; std::atomic isRunning = false; std::atomic finished = false; diff --git a/components/spotify/cspot/src/AudioChunk.cpp b/components/spotify/cspot/src/AudioChunk.cpp index 3df06e67..17ff25b7 100644 --- a/components/spotify/cspot/src/AudioChunk.cpp +++ b/components/spotify/cspot/src/AudioChunk.cpp @@ -10,16 +10,17 @@ AudioChunk::AudioChunk(uint16_t seqId, std::vector &audioKey, uint32_t this->startPosition = startPosition; this->endPosition = predictedEndPosition; this->decryptedData = std::vector(); - this->isHeaderFileSizeLoadedSemaphore = std::make_unique(2); - this->isLoadedSemaphore = std::make_unique(2); + this->isHeaderFileSizeLoadedSemaphore = std::make_unique(5); + this->isLoadedSemaphore = std::make_unique(5); } AudioChunk::~AudioChunk() { } -void AudioChunk::appendData(std::vector &data) +void AudioChunk::appendData(const std::vector &data) { + //if (this == nullptr) return; this->decryptedData.insert(this->decryptedData.end(), data.begin(), data.end()); } diff --git a/components/spotify/cspot/src/AudioChunkManager.cpp b/components/spotify/cspot/src/AudioChunkManager.cpp index 5b263c02..98426814 100644 --- a/components/spotify/cspot/src/AudioChunkManager.cpp +++ b/components/spotify/cspot/src/AudioChunkManager.cpp @@ -3,7 +3,7 @@ #include "Logger.h" AudioChunkManager::AudioChunkManager() - : bell::Task("AudioChunkManager", 4 * 1024, +1, 0) { + : bell::Task("AudioChunkManager", 4 * 1024, 2, 0) { this->chunks = std::vector>(); startTask(); } @@ -12,6 +12,7 @@ std::shared_ptr AudioChunkManager::registerNewChunk(uint16_t seqId, std::vector &audioKey, uint32_t startPos, uint32_t endPos) { + std::scoped_lock lock(chunkMutex); auto chunk = std::make_shared(seqId, audioKey, startPos * 4, endPos * 4); this->chunks.push_back(chunk); @@ -26,6 +27,7 @@ void AudioChunkManager::handleChunkData(std::vector &data, } void AudioChunkManager::failAllChunks() { + std::scoped_lock lock(chunkMutex); // Enumerate all the chunks and mark em all failed for (auto const &chunk : this->chunks) { if (!chunk->isLoaded) { @@ -47,9 +49,10 @@ void AudioChunkManager::close() { void AudioChunkManager::runTask() { std::scoped_lock lock(this->runningMutex); this->isRunning = true; + std::pair, bool> audioPair; while (isRunning) { - std::pair, bool> audioPair; if (this->audioChunkDataQueue.wtpop(audioPair, 100)) { + std::scoped_lock lock(this->chunkMutex); auto data = audioPair.first; auto failed = audioPair.second; uint16_t seqId = ntohs(extract(data, 0)); @@ -57,7 +60,7 @@ void AudioChunkManager::runTask() { // Erase all chunks that are not referenced elsewhere anymore chunks.erase( std::remove_if(chunks.begin(), chunks.end(), - [](const std::shared_ptr &chunk) { + [](std::shared_ptr& chunk) { return chunk.use_count() == 1; }), chunks.end()); @@ -67,7 +70,7 @@ void AudioChunkManager::runTask() { // Found the right chunk if (chunk != nullptr && chunk->seqId == seqId) { if (failed) { - // chunk->isFailed = true; + chunk->isFailed = true; chunk->startPosition = 0; chunk->endPosition = 0; chunk->isHeaderFileSizeLoadedSemaphore->give(); @@ -96,9 +99,6 @@ void AudioChunkManager::runTask() { break; default: - if (chunk.get() == nullptr) { - return; - } auto actualData = std::vector( data.begin() + 2, data.end()); chunk->appendData(actualData); @@ -109,8 +109,6 @@ void AudioChunkManager::runTask() { } catch (...) { } - } else { - usleep(100); } } diff --git a/components/spotify/cspot/src/ChunkedAudioStream.cpp b/components/spotify/cspot/src/ChunkedAudioStream.cpp index 4f2c2a8e..62fe7a0d 100644 --- a/components/spotify/cspot/src/ChunkedAudioStream.cpp +++ b/components/spotify/cspot/src/ChunkedAudioStream.cpp @@ -4,9 +4,11 @@ static size_t vorbisReadCb(void *ptr, size_t size, size_t nmemb, ChunkedAudioStream *self) { - auto data = self->read(nmemb); - std::copy(data.begin(), data.end(), (char *)ptr); - return data.size(); + size_t readSize = 0; + while (readSize < nmemb * size && self->byteStream->position() < self->byteStream->size()) { + readSize += self->byteStream->read((uint8_t *) ptr + readSize, (size * nmemb) - readSize); + } + return readSize; } static int vorbisCloseCb(ChunkedAudioStream *self) { @@ -29,7 +31,7 @@ static int vorbisSeekCb(ChunkedAudioStream *self, int64_t offset, int whence) static long vorbisTellCb(ChunkedAudioStream *self) { - return static_cast(self->pos); + return static_cast(self->byteStream->position()); } ChunkedAudioStream::~ChunkedAudioStream() @@ -38,22 +40,22 @@ ChunkedAudioStream::~ChunkedAudioStream() ChunkedAudioStream::ChunkedAudioStream(std::vector fileId, std::vector audioKey, uint32_t duration, std::shared_ptr manager, uint32_t startPositionMs, bool isPaused) { - this->audioKey = audioKey; this->duration = duration; - this->manager = manager; - this->fileId = fileId; this->startPositionMs = startPositionMs; this->isPaused = isPaused; - auto beginChunk = manager->fetchAudioChunk(fileId, audioKey, 0, 0x4000); - beginChunk->keepInMemory = true; - while(beginChunk->isHeaderFileSizeLoadedSemaphore->twait() != 0); - this->fileSize = beginChunk->headerFileSize; - chunks.push_back(beginChunk); - - // File size is required for this packet to be downloaded - this->fetchTraillingPacket(); +// auto beginChunk = manager->fetchAudioChunk(fileId, audioKey, 0, 0x4000); +// beginChunk->keepInMemory = true; +// while(beginChunk->isHeaderFileSizeLoadedSemaphore->twait() != 0); +// this->fileSize = beginChunk->headerFileSize; +// chunks.push_back(beginChunk); +// +// // File size is required for this packet to be downloaded +// this->fetchTraillingPacket(); + this->byteStream = std::make_shared(manager); + this->byteStream->setFileInfo(fileId, audioKey); + this->byteStream->fetchFileInformation(); vorbisFile = { }; vorbisCallbacks = { @@ -66,12 +68,11 @@ ChunkedAudioStream::ChunkedAudioStream(std::vector fileId, std::vector< void ChunkedAudioStream::seekMs(uint32_t positionMs) { - + byteStream->setEnableLoadAhead(false); this->seekMutex.lock(); - loadingMeta = true; ov_time_seek(&vorbisFile, positionMs); - loadingMeta = false; this->seekMutex.unlock(); + byteStream->setEnableLoadAhead(true); CSPOT_LOG(debug, "--- Finished seeking!"); } @@ -79,33 +80,31 @@ void ChunkedAudioStream::seekMs(uint32_t positionMs) void ChunkedAudioStream::startPlaybackLoop() { - loadingMeta = true; isRunning = true; + byteStream->setEnableLoadAhead(false); int32_t r = ov_open_callbacks(this, &vorbisFile, NULL, 0, vorbisCallbacks); CSPOT_LOG(debug, "--- Loaded file"); if (this->startPositionMs != 0) { - ov_time_seek(&vorbisFile, startPositionMs); - } - else - { - this->requestChunk(0); + ov_time_seek(&vorbisFile, startPositionMs); } - loadingMeta = false; bool eof = false; + std::vector pcmOut(4096 / 4); + byteStream->setEnableLoadAhead(true); + while (!eof && isRunning) { if (!isPaused) { - std::vector pcmOut(4096 / 4); this->seekMutex.lock(); long ret = ov_read(&vorbisFile, (char *)&pcmOut[0], 4096 / 4, ¤tSection); this->seekMutex.unlock(); if (ret == 0) { + CSPOT_LOG(info, "EOL"); // and done :) eof = true; } @@ -139,193 +138,37 @@ void ChunkedAudioStream::startPlaybackLoop() this->streamFinishedCallback(); } } - -void ChunkedAudioStream::fetchTraillingPacket() -{ - auto startPosition = (this->fileSize / 4) - 0x1000; - - // AES block size is 16, so the index must be divisible by it - while ((startPosition * 4) % 16 != 0) - startPosition++; // ik, ugly lol - - auto endChunk = manager->fetchAudioChunk(fileId, audioKey, startPosition, fileSize / 4); - endChunk->keepInMemory = true; - - chunks.push_back(endChunk); - while (endChunk->isLoadedSemaphore->twait() != 0); -} - -std::vector ChunkedAudioStream::read(size_t bytes) -{ - auto toRead = bytes; - auto res = std::vector(); -READ: - while (res.size() < bytes) - { - auto position = pos; - auto isLoadingMeta = loadingMeta; - - // Erase all chunks not close to current position - chunks.erase(std::remove_if( - chunks.begin(), chunks.end(), - [position, &isLoadingMeta](const std::shared_ptr &chunk) { - if (isLoadingMeta) { - return false; - } - - if (chunk->keepInMemory) - { - return false; - } - - if (chunk->isFailed) - { - return true; - } - - if (chunk->endPosition < position || chunk->startPosition > position + BUFFER_SIZE) - { - return true; - } - - return false; - }), - chunks.end()); - - int16_t chunkIndex = this->pos / AUDIO_CHUNK_SIZE; - int32_t offset = this->pos % AUDIO_CHUNK_SIZE; - - if (pos >= fileSize) - { - - CSPOT_LOG(debug, "EOL!"); - return res; - } - - auto chunk = findChunkForPosition(pos); - - if (chunk != nullptr) - { - auto offset = pos - chunk->startPosition; - if (chunk->isLoaded) - { - if (chunk->decryptedData.size() - offset >= toRead) - { - if((chunk->decryptedData.begin() + offset) < chunk->decryptedData.end()) { - res.insert(res.end(), chunk->decryptedData.begin() + offset, - chunk->decryptedData.begin() + offset + toRead); - this->pos += toRead; - } else { - chunk->decrypt(); - } - } - else - { - res.insert(res.end(), chunk->decryptedData.begin() + offset, chunk->decryptedData.end()); - this->pos += chunk->decryptedData.size() - offset; - toRead -= chunk->decryptedData.size() - offset; - } - } - else - { - CSPOT_LOG(debug, "Waiting for chunk to load"); - while (chunk->isLoadedSemaphore->twait() != 0); - if (chunk->isFailed) - { - auto requestChunk = this->requestChunk(chunkIndex); - while (requestChunk->isLoadedSemaphore->twait() != 0); - goto READ; - } - } - } - else - { - CSPOT_LOG(debug, "Actual request %d", chunkIndex); - this->requestChunk(chunkIndex); - } - } - - if (!loadingMeta) - { - - auto requestedOffset = 0; - - while (requestedOffset < BUFFER_SIZE) - { - auto chunk = findChunkForPosition(pos + requestedOffset); - - if (chunk != nullptr) - { - requestedOffset = chunk->endPosition - pos; - - // Don not buffer over EOL - unnecessary "failed chunks" - if ((pos + requestedOffset) >= fileSize) - { - break; - } - } - - else - { - auto chunkReq = manager->fetchAudioChunk(fileId, audioKey, (pos + requestedOffset) / 4, (pos + requestedOffset + AUDIO_CHUNK_SIZE) / 4); - CSPOT_LOG(debug, "Chunk req end pos %d", chunkReq->endPosition); - this->chunks.push_back(chunkReq); - } - } - } - return res; -} - -std::shared_ptr ChunkedAudioStream::findChunkForPosition(size_t position) -{ - for (int i = 0; i < this->chunks.size(); i++) - { - auto chunk = this->chunks[i]; - if (chunk->startPosition <= position && chunk->endPosition > position) - { - return chunk; - } - } - - return nullptr; -} +// +//void ChunkedAudioStream::fetchTraillingPacket() +//{ +// auto startPosition = (this->fileSize / 4) - 0x1000; +// +// // AES block size is 16, so the index must be divisible by it +// while ((startPosition * 4) % 16 != 0) +// startPosition++; // ik, ugly lol +// +// auto endChunk = manager->fetchAudioChunk(fileId, audioKey, startPosition, fileSize / 4); +// endChunk->keepInMemory = true; +// +// chunks.push_back(endChunk); +// while (endChunk->isLoadedSemaphore->twait() != 0); +//} void ChunkedAudioStream::seek(size_t dpos, Whence whence) { + BELL_LOG(info, "cspot", "%d", dpos); + auto seekPos = 0; switch (whence) { case Whence::START: - this->pos = dpos; + seekPos = dpos; break; case Whence::CURRENT: - this->pos += dpos; + seekPos = byteStream->position() + dpos; break; case Whence::END: - this->pos = fileSize + dpos; + seekPos = byteStream->size() + dpos; break; } - - auto currentChunk = this->pos / AUDIO_CHUNK_SIZE; - - if (findChunkForPosition(this->pos) == nullptr) - { - // Seeking might look back - therefore we preload some past data - auto startPosition = (this->pos / 4) - (AUDIO_CHUNK_SIZE / 4); - - // AES block size is 16, so the index must be divisible by it - while ((startPosition * 4) % 16 != 0) - startPosition++; // ik, ugly lol - - this->chunks.push_back(manager->fetchAudioChunk(fileId, audioKey, startPosition, startPosition + (AUDIO_CHUNK_SIZE / 4))); - } - CSPOT_LOG(debug, "Change in current chunk %d", currentChunk); -} - -std::shared_ptr ChunkedAudioStream::requestChunk(size_t chunkIndex) -{ - - CSPOT_LOG(debug, "Chunk Req %d", chunkIndex); - auto chunk = manager->fetchAudioChunk(fileId, audioKey, chunkIndex); - this->chunks.push_back(chunk); - return chunk; -} + byteStream->seek(seekPos); +} \ No newline at end of file diff --git a/components/spotify/cspot/src/MercuryManager.cpp b/components/spotify/cspot/src/MercuryManager.cpp index 0564ded2..62c842f3 100644 --- a/components/spotify/cspot/src/MercuryManager.cpp +++ b/components/spotify/cspot/src/MercuryManager.cpp @@ -9,7 +9,7 @@ std::map MercuryTypeMap({ {MercuryType::UNSUB, "UNSUB"}, }); -MercuryManager::MercuryManager(std::unique_ptr session): bell::Task("mercuryManager", 6 * 1024, +1, 1) +MercuryManager::MercuryManager(std::unique_ptr session): bell::Task("mercuryManager", 6 * 1024, 2, 1) { tempMercuryHeader = Header_init_default; this->timeProvider = std::make_shared(); @@ -30,7 +30,7 @@ MercuryManager::MercuryManager(std::unique_ptr session): bell::Task("me MercuryManager::~MercuryManager() { - pb_release(Header_fields, tempMercuryHeader); + //pb_release(Header_fields, &tempMercuryHeader); } bool MercuryManager::timeoutHandler() @@ -177,7 +177,6 @@ void MercuryManager::runTask() } if (static_cast(packet->command) == MercuryType::PING) // @TODO: Handle time synchronization through ping { - CSPOT_LOG(debug, "Got ping, syncing timestamp"); this->timeProvider->syncWithPingPacket(packet->data); this->lastPingTimestamp = this->timeProvider->getSyncedTimestamp(); diff --git a/components/spotify/cspot/src/MercuryResponse.cpp b/components/spotify/cspot/src/MercuryResponse.cpp index e8e68e6d..7026e98f 100644 --- a/components/spotify/cspot/src/MercuryResponse.cpp +++ b/components/spotify/cspot/src/MercuryResponse.cpp @@ -9,7 +9,7 @@ MercuryResponse::MercuryResponse(std::vector &data) } MercuryResponse::~MercuryResponse() { - pb_release(Header_fields, mercuryHeader); + pb_release(Header_fields, &mercuryHeader); } void MercuryResponse::parseResponse(std::vector &data) @@ -34,6 +34,6 @@ void MercuryResponse::parseResponse(std::vector &data) pos += 2 + partSize; } - pb_release(Header_fields, this->mercuryHeader); + pb_release(Header_fields, &this->mercuryHeader); pbDecode(this->mercuryHeader, Header_fields, headerBytes); } \ No newline at end of file diff --git a/components/spotify/cspot/src/PlainConnection.cpp b/components/spotify/cspot/src/PlainConnection.cpp index 6a07cccf..d1f1200e 100644 --- a/components/spotify/cspot/src/PlainConnection.cpp +++ b/components/spotify/cspot/src/PlainConnection.cpp @@ -104,6 +104,7 @@ std::vector PlainConnection::readBlock(size_t size) std::vector buf(size); unsigned int idx = 0; ssize_t n; + int retries = 0; // printf("START READ\n"); while (idx < size) @@ -124,7 +125,8 @@ std::vector PlainConnection::readBlock(size_t size) case EINTR: break; default: - throw std::runtime_error("Corn"); + if (retries++ > 4) throw std::runtime_error("Error in read"); + } } idx += n; @@ -138,6 +140,7 @@ size_t PlainConnection::writeBlock(const std::vector &data) unsigned int idx = 0; ssize_t n; // printf("START WRITE\n"); + int retries = 0; while (idx < data.size()) { @@ -156,7 +159,7 @@ size_t PlainConnection::writeBlock(const std::vector &data) case EINTR: break; default: - throw std::runtime_error("Corn"); + if (retries++ > 4) throw std::runtime_error("Error in write"); } } idx += n; diff --git a/components/spotify/cspot/src/Player.cpp b/components/spotify/cspot/src/Player.cpp index ebfdc1a2..0fb32b6d 100644 --- a/components/spotify/cspot/src/Player.cpp +++ b/components/spotify/cspot/src/Player.cpp @@ -68,7 +68,7 @@ void Player::feedPCM(std::vector& data) } } - this->audioSink->feedPCMFrames(data); + this->audioSink->feedPCMFrames(data.data(), data.size()); } void Player::runTask() diff --git a/components/spotify/cspot/src/PlayerState.cpp b/components/spotify/cspot/src/PlayerState.cpp index f503b5a0..70e87430 100644 --- a/components/spotify/cspot/src/PlayerState.cpp +++ b/components/spotify/cspot/src/PlayerState.cpp @@ -53,8 +53,9 @@ PlayerState::PlayerState(std::shared_ptr timeProvider) } PlayerState::~PlayerState() { - pb_release(Frame_fields, innerFrame); - pb_release(Frame_fields, remoteFrame); + pb_release(Frame_fields, &remoteFrame); + // do not destruct inner frame as it is never allocated +// pb_release(Frame_fields, &innerFrame); } void PlayerState::setPlaybackState(const PlaybackState state) @@ -136,7 +137,7 @@ void PlayerState::updatePositionMs(uint32_t position) void PlayerState::updateTracks() { CSPOT_LOG(info, "---- Track count %d", remoteFrame.state.track_count); - innerFrame.state.context_uri = remoteFrame.state.context_uri == nullptr ? nullptr : strdup(remoteFrame.state.context_uri); + //innerFrame.state.context_uri = remoteFrame.state.context_uri == nullptr ? nullptr : strdup(remoteFrame.state.context_uri); std::copy(std::begin(remoteFrame.state.track), std::end(remoteFrame.state.track), std::begin(innerFrame.state.track)); innerFrame.state.track_count = remoteFrame.state.track_count; innerFrame.state.has_playing_track_index = true; diff --git a/components/spotify/cspot/src/Session.cpp b/components/spotify/cspot/src/Session.cpp index ee4975fc..4a51c4d9 100644 --- a/components/spotify/cspot/src/Session.cpp +++ b/components/spotify/cspot/src/Session.cpp @@ -6,10 +6,10 @@ using random_bytes_engine = std::independent_bits_engineclientHello = ClientHello_init_default; - this->apResponse = APResponseMessage_init_default; - this->authRequest = ClientResponseEncrypted_init_default; - this->clientResPlaintext = ClientResponsePlaintext_init_default; + this->clientHello = {}; + this->apResponse = {}; + this->authRequest = {}; + this->clientResPlaintext = {}; // Generates the public and priv key this->crypto = std::make_unique(); @@ -18,10 +18,9 @@ Session::Session() Session::~Session() { - pb_release(ClientHello_fields, clientHello); - pb_release(APResponseMessage_fields, apResponse); - pb_release(ClientResponseEncrypted_fields, authRequest); - pb_release(ClientResponsePlaintext_fields, clientResPlaintext); + pb_release(ClientHello_fields, &clientHello); + pb_release(APResponseMessage_fields, &apResponse); + pb_release(ClientResponsePlaintext_fields, &clientResPlaintext); } void Session::connect(std::unique_ptr connection) @@ -60,6 +59,7 @@ std::vector Session::authenticate(std::shared_ptr blob) authRequest.version_string = (char *)versionString; auto data = pbEncode(ClientResponseEncrypted_fields, &authRequest); + free(authRequest.login_credentials.auth_data); // Send login request this->shanConn->sendPacket(LOGIN_REQUEST_COMMAND, data); @@ -72,7 +72,7 @@ std::vector Session::authenticate(std::shared_ptr blob) CSPOT_LOG(debug, "Authorization successful"); // @TODO store the reusable credentials - // PBWrapper welcomePacket(packet->data) + // PBWrapper welcomePacket(packet->data) return std::vector({0x1}); // TODO: return actual reusable credentaials to be stored somewhere break; } @@ -96,7 +96,7 @@ void Session::processAPHelloResponse(std::vector &helloPacket) // Decode the response auto skipSize = std::vector(data.begin() + 4, data.end()); - pb_release(APResponseMessage_fields, apResponse); + pb_release(APResponseMessage_fields, &apResponse); pbDecode(apResponse, APResponseMessage_fields, skipSize); auto diffieKey = std::vector(apResponse.challenge.login_crypto_challenge.diffie_hellman.gs, apResponse.challenge.login_crypto_challenge.diffie_hellman.gs + 96); diff --git a/components/spotify/cspot/src/SpircController.cpp b/components/spotify/cspot/src/SpircController.cpp index d2fd92fe..da9586aa 100644 --- a/components/spotify/cspot/src/SpircController.cpp +++ b/components/spotify/cspot/src/SpircController.cpp @@ -103,7 +103,7 @@ void SpircController::prevSong() { } void SpircController::handleFrame(std::vector &data) { - pb_release(Frame_fields, state->remoteFrame); + //pb_release(Frame_fields, &state->remoteFrame); pbDecode(state->remoteFrame, Frame_fields, data); switch (state->remoteFrame.typ) { diff --git a/components/spotify/cspot/src/SpotifyTrack.cpp b/components/spotify/cspot/src/SpotifyTrack.cpp index 023c0470..6db0416c 100644 --- a/components/spotify/cspot/src/SpotifyTrack.cpp +++ b/components/spotify/cspot/src/SpotifyTrack.cpp @@ -35,8 +35,8 @@ SpotifyTrack::~SpotifyTrack() { this->manager->unregisterMercuryCallback(this->reqSeqNum); this->manager->freeAudioKeyCallback(); - pb_release(Track_fields, this->trackInfo); - pb_release(Episode_fields, this->episodeInfo); + pb_release(Track_fields, &this->trackInfo); + pb_release(Episode_fields, &this->episodeInfo); } bool SpotifyTrack::countryListContains(std::string countryList, std::string country) @@ -75,7 +75,7 @@ void SpotifyTrack::trackInformationCallback(std::unique_ptr res return; CSPOT_ASSERT(response->parts.size() > 0, "response->parts.size() must be greater than 0"); - pb_release(Track_fields, trackInfo); + pb_release(Track_fields, &trackInfo); pbDecode(trackInfo, Track_fields, response->parts[0]); CSPOT_LOG(info, "Track name: %s", trackInfo.name); @@ -127,7 +127,7 @@ void SpotifyTrack::episodeInformationCallback(std::unique_ptr r return; CSPOT_LOG(debug, "Got to episode"); CSPOT_ASSERT(response->parts.size() > 0, "response->parts.size() must be greater than 0"); - pb_release(Episode_fields, episodeInfo); + pb_release(Episode_fields, &episodeInfo); pbDecode(episodeInfo, Episode_fields, response->parts[0]); CSPOT_LOG(info, "--- Episode name: %s", episodeInfo.name); diff --git a/components/spotify/cspot/src/TrackReference.cpp b/components/spotify/cspot/src/TrackReference.cpp index a98fc969..e51afb35 100644 --- a/components/spotify/cspot/src/TrackReference.cpp +++ b/components/spotify/cspot/src/TrackReference.cpp @@ -19,8 +19,9 @@ TrackReference::TrackReference(TrackRef *ref) TrackReference::~TrackReference() { - pb_release(TrackRef_fields, ref); - } + //pb_release(TrackRef_fields, &ref); + //pbFree(TrackRef_fields, &ref); +} std::vector TrackReference::base62Decode(std::string uri) {