浏览代码

catching up (trying to) wiht CSpot

Philippe G 3 年之前
父节点
当前提交
9af4cd5b23
共有 57 个文件被更改,包括 2166 次插入344 次删除
  1. 1 1
      components/_override/CMakeLists.txt
  2. 2 2
      components/spotify/Shim.cpp
  3. 1 1
      components/spotify/Shim.h
  4. 1 0
      components/spotify/cspot/CMakeLists.txt
  5. 21 0
      components/spotify/cspot/bell/include/AudioSink.h
  6. 0 2
      components/spotify/cspot/bell/include/BellLogger.h
  7. 3 3
      components/spotify/cspot/bell/include/BellUtils.h
  8. 2 1
      components/spotify/cspot/bell/include/ByteStream.h
  9. 33 13
      components/spotify/cspot/bell/include/HTTPClient.h
  10. 4 2
      components/spotify/cspot/bell/include/NanoPBHelper.h
  11. 1 1
      components/spotify/cspot/bell/include/Queue.h
  12. 1 1
      components/spotify/cspot/bell/include/TCPSocket.h
  13. 2 0
      components/spotify/cspot/bell/include/Task.h
  14. 26 0
      components/spotify/cspot/bell/include/sinks/esp/AC101AudioSink.h
  15. 25 0
      components/spotify/cspot/bell/include/sinks/esp/BufferedAudioSink.h
  16. 105 0
      components/spotify/cspot/bell/include/sinks/esp/ES8388AudioSink.h
  17. 22 0
      components/spotify/cspot/bell/include/sinks/esp/ES9018AudioSink.h
  18. 22 0
      components/spotify/cspot/bell/include/sinks/esp/InternalAudioSink.h
  19. 22 0
      components/spotify/cspot/bell/include/sinks/esp/PCM5102AudioSink.h
  20. 27 0
      components/spotify/cspot/bell/include/sinks/esp/SPDIFAudioSink.h
  21. 30 0
      components/spotify/cspot/bell/include/sinks/esp/TAS5711AudioSink.h
  22. 176 0
      components/spotify/cspot/bell/include/sinks/esp/ac101.h
  23. 28 0
      components/spotify/cspot/bell/include/sinks/esp/adac.h
  24. 124 0
      components/spotify/cspot/bell/include/sinks/unix/ALSAAudioSink.h
  25. 16 0
      components/spotify/cspot/bell/include/sinks/unix/NamedPipeAudioSink.h
  26. 20 0
      components/spotify/cspot/bell/include/sinks/unix/PortAudioSink.h
  27. 60 34
      components/spotify/cspot/bell/src/HTTPClient.cpp
  28. 1 0
      components/spotify/cspot/bell/src/HTTPStream.cpp
  29. 31 5
      components/spotify/cspot/bell/src/NanoPBHelper.cpp
  30. 46 0
      components/spotify/cspot/bell/src/sinks/esp/AC101AudioSink.cpp
  31. 46 0
      components/spotify/cspot/bell/src/sinks/esp/BufferedAudioSink.cpp
  32. 155 0
      components/spotify/cspot/bell/src/sinks/esp/ES8388AudioSink.cpp
  33. 40 0
      components/spotify/cspot/bell/src/sinks/esp/ES9018AudioSink.cpp
  34. 33 0
      components/spotify/cspot/bell/src/sinks/esp/InternalAudioSink.cpp
  35. 36 0
      components/spotify/cspot/bell/src/sinks/esp/PCM5102AudioSink.cpp
  36. 184 0
      components/spotify/cspot/bell/src/sinks/esp/SPDIFAudioSink.cpp
  37. 121 0
      components/spotify/cspot/bell/src/sinks/esp/TAS5711AudioSink.cpp
  38. 426 0
      components/spotify/cspot/bell/src/sinks/esp/ac101.c
  39. 101 0
      components/spotify/cspot/bell/src/sinks/unix/ALSAAudioSink.cpp
  40. 21 0
      components/spotify/cspot/bell/src/sinks/unix/NamedPipeAudioSink.cpp
  41. 53 0
      components/spotify/cspot/bell/src/sinks/unix/PortAudioSink.cpp
  42. 1 1
      components/spotify/cspot/include/AudioChunk.h
  43. 2 1
      components/spotify/cspot/include/AudioChunkManager.h
  44. 0 18
      components/spotify/cspot/include/AudioSink.h
  45. 3 13
      components/spotify/cspot/include/ChunkedAudioStream.h
  46. 4 3
      components/spotify/cspot/src/AudioChunk.cpp
  47. 7 9
      components/spotify/cspot/src/AudioChunkManager.cpp
  48. 48 205
      components/spotify/cspot/src/ChunkedAudioStream.cpp
  49. 2 3
      components/spotify/cspot/src/MercuryManager.cpp
  50. 2 2
      components/spotify/cspot/src/MercuryResponse.cpp
  51. 5 2
      components/spotify/cspot/src/PlainConnection.cpp
  52. 1 1
      components/spotify/cspot/src/Player.cpp
  53. 4 3
      components/spotify/cspot/src/PlayerState.cpp
  54. 10 10
      components/spotify/cspot/src/Session.cpp
  55. 1 1
      components/spotify/cspot/src/SpircController.cpp
  56. 4 4
      components/spotify/cspot/src/SpotifyTrack.cpp
  57. 3 2
      components/spotify/cspot/src/TrackReference.cpp

+ 1 - 1
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 !!")
+message("overriding ${srcs} !! THIS MUST BE REQUIRED BY ONE COMPONENT BUT NOT MAIN !!")

+ 2 - 2
components/spotify/Shim.cpp

@@ -246,8 +246,8 @@ void ShimAudioSink::volumeChanged(uint16_t volume) {
 	cspot.cHandler(CSPOT_VOLUME, volume);
 }
 
-void ShimAudioSink::feedPCMFrames(std::vector<uint8_t> &data) {	
-	cspot.dHandler(&data[0], data.size());
+void ShimAudioSink::feedPCMFrames(const uint8_t *data, size_t bytes) {	
+	cspot.dHandler(data, bytes);
 }
 
 /****************************************************************************************

+ 1 - 1
components/spotify/Shim.h

@@ -23,7 +23,7 @@
 class ShimAudioSink : public AudioSink {
 public:
 	ShimAudioSink(void) { softwareVolumeControl = false; }
-    void feedPCMFrames(std::vector<uint8_t> &data);
+    void feedPCMFrames(const uint8_t *data, size_t bytes);
 	virtual void volumeChanged(uint16_t volume);
 };
 

+ 1 - 0
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})

+ 21 - 0
components/spotify/cspot/bell/include/AudioSink.h

@@ -0,0 +1,21 @@
+#ifndef AUDIOSINK_H
+#define AUDIOSINK_H
+
+#include <cstdint>
+#include <cstdlib>
+#include <vector>
+
+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

+ 0 - 2
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 */

+ 3 - 3
components/spotify/cspot/bell/include/BellUtils.h

@@ -15,13 +15,13 @@ std::string generateRandomUUID();
 #include <freertos/FreeRTOS.h>
 
 #define BELL_SLEEP_MS(ms) vTaskDelay(ms / portTICK_PERIOD_MS)
-#define BELL_YIELD() vTaskYield()
+#define BELL_YIELD() taskYIELD()
 
 #else
 #include <unistd.h>
 
 #define BELL_SLEEP_MS(ms) usleep(ms * 1000)
-#define BELL_YIELD() ()
+#define BELL_YIELD() ;
 
 #endif
-#endif
+#endif

+ 2 - 1
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 <stdlib.h>
+#include <stdint.h>
 
 /**
  * A class for reading bytes from a stream. Further implemented in HTTPStream.h

+ 33 - 13
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 <map>
@@ -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<std::string, std::string> headers;
-		std::string contentType;
 		int maxRedirects = -1;
+		std::ostream *dumpFs = nullptr;
+		std::ostream *dumpRawFs = nullptr;
 	};
 
-	struct HTTPResponse {
+	struct HTTPResponse : public ByteStream {
 		std::shared_ptr<bell::Socket> socket;
 
 		std::map<std::string, std::string> 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<struct HTTPClient::HTTPResponse> 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<struct HTTPClient::HTTPResponse> HTTPResponse_t;
 } // namespace bell
 
 #endif

+ 4 - 2
components/spotify/cspot/bell/include/NanoPBHelper.h

@@ -4,6 +4,7 @@
 #include <vector>
 #include "pb_encode.h"
 #include "pb_decode.h"
+#include "HTTPClient.h"
 #include <string>
 
 std::vector<uint8_t> 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<uint8_t> &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
+#endif

+ 1 - 1
components/spotify/cspot/bell/include/Queue.h

@@ -15,7 +15,7 @@ namespace bell
         /// Queue
         std::queue<dataType> 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

+ 1 - 1
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);

+ 2 - 0
components/spotify/cspot/bell/include/Task.h

@@ -10,6 +10,7 @@
 #endif
 
 #include <pthread.h>
+#include <string>
 
 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;

+ 26 - 0
components/spotify/cspot/bell/include/sinks/esp/AC101AudioSink.h

@@ -0,0 +1,26 @@
+#ifndef AC101AUDIOSINK_H
+#define AC101AUDIOSINK_H
+
+#include <vector>
+#include <iostream>
+#include "BufferedAudioSink.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/unistd.h>
+#include <sys/stat.h>
+#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

+ 25 - 0
components/spotify/cspot/bell/include/sinks/esp/BufferedAudioSink.h

@@ -0,0 +1,25 @@
+#ifndef BUFFEREDAUDIOSINK_H
+#define BUFFEREDAUDIOSINK_H
+
+#include <vector>
+#include <iostream>
+#include "AudioSink.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/unistd.h>
+#include <sys/stat.h>
+#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

+ 105 - 0
components/spotify/cspot/bell/include/sinks/esp/ES8388AudioSink.h

@@ -0,0 +1,105 @@
+#ifndef ES8388AUDIOSINK_H
+#define ES8388AUDIOSINK_H
+
+#include "driver/i2s.h"
+#include <driver/i2c.h>
+#include <vector>
+#include <iostream>
+#include "BufferedAudioSink.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/unistd.h>
+#include <sys/stat.h>
+#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

+ 22 - 0
components/spotify/cspot/bell/include/sinks/esp/ES9018AudioSink.h

@@ -0,0 +1,22 @@
+#ifndef ES9018AUDIOSINK_H
+#define ES9018AUDIOSINK_H
+
+#include <vector>
+#include <iostream>
+#include "BufferedAudioSink.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/unistd.h>
+#include <sys/stat.h>
+#include "esp_err.h"
+#include "esp_log.h"
+
+class ES9018AudioSink : public BufferedAudioSink
+{
+public:
+    ES9018AudioSink();
+    ~ES9018AudioSink();
+private:
+};
+
+#endif

+ 22 - 0
components/spotify/cspot/bell/include/sinks/esp/InternalAudioSink.h

@@ -0,0 +1,22 @@
+#ifndef INTERNALAUDIOSINK_H
+#define INTERNALAUDIOSINK_H
+
+#include <vector>
+#include <iostream>
+#include "BufferedAudioSink.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/unistd.h>
+#include <sys/stat.h>
+#include "esp_err.h"
+#include "esp_log.h"
+
+class InternalAudioSink : public BufferedAudioSink
+{
+public:
+    InternalAudioSink();
+    ~InternalAudioSink();
+private:
+};
+
+#endif

+ 22 - 0
components/spotify/cspot/bell/include/sinks/esp/PCM5102AudioSink.h

@@ -0,0 +1,22 @@
+#ifndef PCM5102AUDIOSINK_H
+#define PCM5102AUDIOSINK_H
+
+#include <vector>
+#include <iostream>
+#include "BufferedAudioSink.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/unistd.h>
+#include <sys/stat.h>
+#include "esp_err.h"
+#include "esp_log.h"
+
+class PCM5102AudioSink : public BufferedAudioSink
+{
+public:
+    PCM5102AudioSink();
+    ~PCM5102AudioSink();
+private:
+};
+
+#endif

+ 27 - 0
components/spotify/cspot/bell/include/sinks/esp/SPDIFAudioSink.h

@@ -0,0 +1,27 @@
+#ifndef SPDIFAUDIOSINK_H
+#define SPDIFAUDIOSINK_H
+
+#include <vector>
+#include <iostream>
+#include "BufferedAudioSink.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/unistd.h>
+#include <sys/stat.h>
+#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

+ 30 - 0
components/spotify/cspot/bell/include/sinks/esp/TAS5711AudioSink.h

@@ -0,0 +1,30 @@
+#ifndef TAS5711AUDIOSINK_H
+#define TAS5711AUDIOSINK_H
+
+
+#include "driver/i2s.h"
+#include <driver/i2c.h>
+#include <vector>
+#include <iostream>
+#include "BufferedAudioSink.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/unistd.h>
+#include <sys/stat.h>
+#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

+ 176 - 0
components/spotify/cspot/bell/include/sinks/esp/ac101.h

@@ -0,0 +1,176 @@
+/*
+ * ESPRESSIF MIT License
+ *
+ * Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
+ *
+ * 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

+ 28 - 0
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;

+ 124 - 0
components/spotify/cspot/bell/include/sinks/unix/ALSAAudioSink.h

@@ -0,0 +1,124 @@
+#pragma once
+
+#include <vector>
+#include <fstream>
+#include "AudioSink.h"
+#include <alsa/asoundlib.h>
+#include <stdio.h>
+#include <Task.h>
+#include <unistd.h>
+#include <memory>
+#include <mutex>
+
+#define PCM_DEVICE "default"
+
+template <typename T, int SIZE>
+class RingbufferPointer
+{
+    typedef std::unique_ptr<T> TPointer;
+
+public:
+    explicit RingbufferPointer()
+    {
+        // create objects
+        for (int i = 0; i < SIZE; i++)
+        {
+            buf_[i] = std::make_unique<T>();
+        }
+    }
+
+    bool push(TPointer &item)
+    {
+        std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::vector<uint8_t>, 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<uint8_t> buff;
+};

+ 16 - 0
components/spotify/cspot/bell/include/sinks/unix/NamedPipeAudioSink.h

@@ -0,0 +1,16 @@
+#pragma once
+
+#include <vector>
+#include <fstream>
+#include "AudioSink.h"
+
+class NamedPipeAudioSink : public AudioSink
+{
+public:
+    NamedPipeAudioSink();
+    ~NamedPipeAudioSink();
+    void feedPCMFrames(const uint8_t *buffer, size_t bytes);
+    
+private:
+    std::ofstream namedPipeFile;
+};

+ 20 - 0
components/spotify/cspot/bell/include/sinks/unix/PortAudioSink.h

@@ -0,0 +1,20 @@
+#pragma once
+
+#include <vector>
+#include "portaudio.h"
+#include <stdint.h>
+#include <iostream>
+#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;
+};

+ 60 - 34
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);
+}
+
+HTTPResponse_t HTTPClient::execute(const struct HTTPRequest &request) {
+	auto response = std::make_unique<HTTPResponse>();
+	response->dumpFs = request.dumpFs;
+	response->dumpRawFs = request.dumpRawFs;
+	return HTTPClient::executeImpl(request, std::move(response));
 }
 
-void HTTPClient::executeImpl(const struct HTTPRequest &request, const char *url, struct HTTPResponse *&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();

+ 1 - 0
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;
     }
 

+ 31 - 5
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<uint8_t>& vectorToPack)
 {
     auto size = static_cast<pb_size_t>(vectorToPack.size());
@@ -53,4 +49,34 @@ pb_bytes_array_t* vectorToPbArray(const std::vector<uint8_t>& vectorToPack)
 
 std::vector<uint8_t> pbArrayToVector(pb_bytes_array_t* pbArray) {
     return std::vector<uint8_t>(pbArray->bytes, pbArray->bytes + pbArray->size);
-}
+}
+
+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<uint8_t *>(malloc(len + 1));
+	auto ostream = pb_ostream_from_buffer(buf, len);
+	pb_encode(&ostream, fields, data);
+	buf[len] = '\0';
+	return reinterpret_cast<const char *>(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,
+	};
+}

+ 46 - 0
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);
+}

+ 46 - 0
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;
+}

+ 155 - 0
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()
+{
+}

+ 40 - 0
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()
+{
+}

+ 33 - 0
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()
+{
+}

+ 36 - 0
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()
+{
+}

+ 184 - 0
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;
+        }
+    }
+}

+ 121 - 0
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()
+{
+}

+ 426 - 0
components/spotify/cspot/bell/src/sinks/esp/ac101.c

@@ -0,0 +1,426 @@
+/*
+ * ESPRESSIF MIT License
+ *
+ * Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
+ *
+ * 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 <string.h>
+#include <esp_log.h>
+#include <esp_types.h>
+#include <esp_system.h>
+#include <freertos/FreeRTOS.h>
+#include <freertos/task.h>
+#include <driver/i2c.h>
+#include <driver/i2s.h>
+#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);
+}

+ 101 - 0
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(&params);
+
+    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<std::vector<uint8_t>> 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<std::vector<uint8_t>>(this->buff.begin(), this->buff.begin() + this->buff_size);
+        this->buff = std::vector<uint8_t>(this->buff.begin() + this->buff_size, this->buff.end());
+        while (!this->ringbuffer.push(ptr))
+        {
+            usleep(100);
+        };
+    }
+}

+ 21 - 0
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();
+}

+ 53 - 0
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);
+}

+ 1 - 1
components/spotify/cspot/include/AudioChunk.h

@@ -62,7 +62,7 @@ public:
      * 
      * @param data encrypted binary audio data.
      */
-    void appendData(std::vector<uint8_t> &data);
+    void appendData(const std::vector<uint8_t> &data);
 
     /**
      * @brief Performs AES CTR decryption of received data.

+ 2 - 1
components/spotify/cspot/include/AudioChunkManager.h

@@ -21,6 +21,7 @@ public:
     AudioChunkManager();
     std::atomic<bool> isRunning = false;
     std::mutex runningMutex;
+    std::mutex chunkMutex;
     /**
      * @brief Registers a new audio chunk request.
      * 
@@ -54,4 +55,4 @@ public:
     void close();
 };
 
-#endif
+#endif

+ 0 - 18
components/spotify/cspot/include/AudioSink.h

@@ -1,18 +0,0 @@
-#ifndef AUDIOSINK_H
-#define AUDIOSINK_H
-
-#include <stdint.h>
-#include <vector>
-
-class AudioSink
-{
-public:
-    AudioSink() {}
-    virtual ~AudioSink() {}
-    virtual void feedPCMFrames(std::vector<uint8_t> &data) = 0;
-    virtual void volumeChanged(uint16_t volume) {}
-    bool softwareVolumeControl = true;
-    bool usign = false;
-};
-
-#endif

+ 3 - 13
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<uint8_t> audioKey;
-    std::vector<std::shared_ptr<AudioChunk>> chunks;
-
     // Audio data
     uint32_t duration;
 
@@ -46,19 +43,12 @@ private:
     std::vector<uint8_t> fileId;
     uint32_t startPositionMs;
 
-    std::shared_ptr<AudioChunk> requestChunk(size_t chunkIndex);
-    void fetchTraillingPacket();
-    std::shared_ptr<AudioChunk> findChunkForPosition(size_t position);
-
 public:
     ChunkedAudioStream(std::vector<uint8_t> fileId, std::vector<uint8_t> audioKey, uint32_t duration, std::shared_ptr<MercuryManager> manager, uint32_t startPositionMs, bool isPaused);
     ~ChunkedAudioStream();
-    int requestedChunkIndex = 0;
+    std::shared_ptr<ChunkedByteStream> byteStream;
+
     std::function<void()> streamFinishedCallback;
-    size_t pos = SPOTIFY_HEADER_SIZE; // size of some spotify header
-    uint32_t fileSize;
-    uint32_t readBeforeSeek = 0;
-    bool loadingMeta = true;
     std::atomic<bool> isPaused = false;
     std::atomic<bool> isRunning = false;
     std::atomic<bool> finished = false;

+ 4 - 3
components/spotify/cspot/src/AudioChunk.cpp

@@ -10,16 +10,17 @@ AudioChunk::AudioChunk(uint16_t seqId, std::vector<uint8_t> &audioKey, uint32_t
     this->startPosition = startPosition;
     this->endPosition = predictedEndPosition;
     this->decryptedData = std::vector<uint8_t>();
-    this->isHeaderFileSizeLoadedSemaphore = std::make_unique<WrappedSemaphore>(2);
-    this->isLoadedSemaphore = std::make_unique<WrappedSemaphore>(2);
+    this->isHeaderFileSizeLoadedSemaphore = std::make_unique<WrappedSemaphore>(5);
+    this->isLoadedSemaphore = std::make_unique<WrappedSemaphore>(5);
 }
 
 AudioChunk::~AudioChunk()
 {
 }
 
-void AudioChunk::appendData(std::vector<uint8_t> &data)
+void AudioChunk::appendData(const std::vector<uint8_t> &data)
 {
+    //if (this == nullptr) return;
     this->decryptedData.insert(this->decryptedData.end(), data.begin(), data.end());
 }
 

+ 7 - 9
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<std::shared_ptr<AudioChunk>>();
     startTask();
 }
@@ -12,6 +12,7 @@ std::shared_ptr<AudioChunk>
 AudioChunkManager::registerNewChunk(uint16_t seqId,
                                     std::vector<uint8_t> &audioKey,
                                     uint32_t startPos, uint32_t endPos) {
+    std::scoped_lock lock(chunkMutex);
     auto chunk =
         std::make_shared<AudioChunk>(seqId, audioKey, startPos * 4, endPos * 4);
     this->chunks.push_back(chunk);
@@ -26,6 +27,7 @@ void AudioChunkManager::handleChunkData(std::vector<uint8_t> &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<std::vector<uint8_t>, bool> audioPair;
     while (isRunning) {
-        std::pair<std::vector<uint8_t>, 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<uint16_t>(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<AudioChunk> &chunk) {
+                               [](std::shared_ptr<AudioChunk>& 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<uint8_t>(
                                 data.begin() + 2, data.end());
                             chunk->appendData(actualData);
@@ -109,8 +109,6 @@ void AudioChunkManager::runTask() {
 
             } catch (...) {
             }
-        } else {
-            usleep(100);
         }
     }
 

+ 48 - 205
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<long>(self->pos);
+    return static_cast<long>(self->byteStream->position());
 }
 
 ChunkedAudioStream::~ChunkedAudioStream()
@@ -38,22 +40,22 @@ ChunkedAudioStream::~ChunkedAudioStream()
 
 ChunkedAudioStream::ChunkedAudioStream(std::vector<uint8_t> fileId, std::vector<uint8_t> audioKey, uint32_t duration, std::shared_ptr<MercuryManager> 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<ChunkedByteStream>(manager);
+    this->byteStream->setFileInfo(fileId, audioKey);
+    this->byteStream->fetchFileInformation();
     vorbisFile = { };
     vorbisCallbacks =
         {
@@ -66,12 +68,11 @@ ChunkedAudioStream::ChunkedAudioStream(std::vector<uint8_t> 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<uint8_t> pcmOut(4096 / 4);
+    byteStream->setEnableLoadAhead(true);
+
     while (!eof && isRunning)
     {
         if (!isPaused)
         {
-            std::vector<uint8_t> pcmOut(4096 / 4);
 
             this->seekMutex.lock();
             long ret = ov_read(&vorbisFile, (char *)&pcmOut[0], 4096 / 4, &currentSection);
             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<uint8_t> ChunkedAudioStream::read(size_t bytes)
-{
-    auto toRead = bytes;
-    auto res = std::vector<uint8_t>();
-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<AudioChunk> &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<AudioChunk> 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<AudioChunk> 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);
+}

+ 2 - 3
components/spotify/cspot/src/MercuryManager.cpp

@@ -9,7 +9,7 @@ std::map<MercuryType, std::string> MercuryTypeMap({
     {MercuryType::UNSUB, "UNSUB"},
     });
 
-MercuryManager::MercuryManager(std::unique_ptr<Session> session): bell::Task("mercuryManager", 6 * 1024, +1, 1)
+MercuryManager::MercuryManager(std::unique_ptr<Session> session): bell::Task("mercuryManager", 6 * 1024, 2, 1)
 {
     tempMercuryHeader = Header_init_default;
     this->timeProvider = std::make_shared<TimeProvider>();
@@ -30,7 +30,7 @@ MercuryManager::MercuryManager(std::unique_ptr<Session> 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<MercuryType>(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();

+ 2 - 2
components/spotify/cspot/src/MercuryResponse.cpp

@@ -9,7 +9,7 @@ MercuryResponse::MercuryResponse(std::vector<uint8_t> &data)
 }
 
 MercuryResponse::~MercuryResponse() {
-    pb_release(Header_fields, mercuryHeader);
+    pb_release(Header_fields, &mercuryHeader);
 }
 
 void MercuryResponse::parseResponse(std::vector<uint8_t> &data)
@@ -34,6 +34,6 @@ void MercuryResponse::parseResponse(std::vector<uint8_t> &data)
         pos += 2 + partSize;
     }
 
-    pb_release(Header_fields, this->mercuryHeader);
+    pb_release(Header_fields, &this->mercuryHeader);
     pbDecode(this->mercuryHeader, Header_fields, headerBytes);
 }

+ 5 - 2
components/spotify/cspot/src/PlainConnection.cpp

@@ -104,6 +104,7 @@ std::vector<uint8_t> PlainConnection::readBlock(size_t size)
     std::vector<uint8_t> 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<uint8_t> 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<uint8_t> &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<uint8_t> &data)
             case EINTR:
                 break;
             default:
-                throw std::runtime_error("Corn");
+                if (retries++ > 4) throw std::runtime_error("Error in write");
             }
         }
         idx += n;

+ 1 - 1
components/spotify/cspot/src/Player.cpp

@@ -68,7 +68,7 @@ void Player::feedPCM(std::vector<uint8_t>& data)
         }
     }
 
-    this->audioSink->feedPCMFrames(data);
+    this->audioSink->feedPCMFrames(data.data(), data.size());
 }
 
 void Player::runTask()

+ 4 - 3
components/spotify/cspot/src/PlayerState.cpp

@@ -53,8 +53,9 @@ PlayerState::PlayerState(std::shared_ptr<TimeProvider> 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;

+ 10 - 10
components/spotify/cspot/src/Session.cpp

@@ -6,10 +6,10 @@ using random_bytes_engine = std::independent_bits_engine<std::default_random_eng
 
 Session::Session()
 {
-    this->clientHello = 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<Crypto>();
@@ -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<PlainConnection> connection)
@@ -60,6 +59,7 @@ std::vector<uint8_t> Session::authenticate(std::shared_ptr<LoginBlob> 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<uint8_t> Session::authenticate(std::shared_ptr<LoginBlob> blob)
         CSPOT_LOG(debug, "Authorization successful");
 
         // @TODO store the reusable credentials
-        // PBWrapper<APWelcome> welcomePacket(packet->data)
+        // PBWrapper<APWelcome>  welcomePacket(packet->data)
         return std::vector<uint8_t>({0x1}); // TODO: return actual reusable credentaials to be stored somewhere
         break;
     }
@@ -96,7 +96,7 @@ void Session::processAPHelloResponse(std::vector<uint8_t> &helloPacket)
     // Decode the response
     auto skipSize = std::vector<uint8_t>(data.begin() + 4, data.end());
 
-    pb_release(APResponseMessage_fields, apResponse);
+    pb_release(APResponseMessage_fields, &apResponse);
     pbDecode(apResponse, APResponseMessage_fields, skipSize);
 
     auto diffieKey = std::vector<uint8_t>(apResponse.challenge.login_crypto_challenge.diffie_hellman.gs, apResponse.challenge.login_crypto_challenge.diffie_hellman.gs + 96);

+ 1 - 1
components/spotify/cspot/src/SpircController.cpp

@@ -103,7 +103,7 @@ void SpircController::prevSong() {
 }
 
 void SpircController::handleFrame(std::vector<uint8_t> &data) {
-    pb_release(Frame_fields, state->remoteFrame);
+    //pb_release(Frame_fields, &state->remoteFrame);
     pbDecode(state->remoteFrame, Frame_fields, data);
 
     switch (state->remoteFrame.typ) {

+ 4 - 4
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<MercuryResponse> 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<MercuryResponse> 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);

+ 3 - 2
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<uint8_t> TrackReference::base62Decode(std::string uri)
 {