瀏覽代碼

Merge remote-tracking branch 'origin/master-v4.3' into master-v4.3

Sebastien L 3 年之前
父節點
當前提交
12f9b1eae8
共有 30 個文件被更改,包括 225 次插入110 次删除
  1. 1 2
      components/display/ST77xx.c
  2. 1 1
      components/display/display.c
  3. 6 2
      components/spotify/cspot/bell/include/AudioSink.h
  4. 1 0
      components/spotify/cspot/bell/include/BaseHTTPServer.h
  5. 1 0
      components/spotify/cspot/bell/include/BellUtils.h
  6. 1 1
      components/spotify/cspot/bell/include/BiquadFilter.h
  7. 13 13
      components/spotify/cspot/bell/include/HTTPClient.h
  8. 1 1
      components/spotify/cspot/bell/include/HTTPServer.h
  9. 3 0
      components/spotify/cspot/bell/include/JSONObject.h
  10. 6 5
      components/spotify/cspot/bell/include/TCPSocket.h
  11. 2 2
      components/spotify/cspot/bell/include/sinks/esp/BufferedAudioSink.h
  12. 1 2
      components/spotify/cspot/bell/include/sinks/esp/SPDIFAudioSink.h
  13. 5 6
      components/spotify/cspot/bell/include/sinks/unix/PortAudioSink.h
  14. 5 0
      components/spotify/cspot/bell/src/BellUtils.cpp
  15. 7 4
      components/spotify/cspot/bell/src/BinaryReader.cpp
  16. 26 16
      components/spotify/cspot/bell/src/HTTPClient.cpp
  17. 31 4
      components/spotify/cspot/bell/src/HTTPServer.cpp
  18. 8 1
      components/spotify/cspot/bell/src/JSONObject.cpp
  19. 3 2
      components/spotify/cspot/bell/src/sinks/esp/BufferedAudioSink.cpp
  20. 11 13
      components/spotify/cspot/bell/src/sinks/esp/SPDIFAudioSink.cpp
  21. 25 13
      components/spotify/cspot/bell/src/sinks/unix/PortAudioSink.cpp
  22. 2 2
      components/spotify/cspot/include/ChunkedAudioStream.h
  23. 1 1
      components/spotify/cspot/include/Player.h
  24. 1 0
      components/spotify/cspot/include/SpircController.h
  25. 4 6
      components/spotify/cspot/src/ChunkedAudioStream.cpp
  26. 1 0
      components/spotify/cspot/src/ChunkedByteStream.cpp
  27. 11 9
      components/spotify/cspot/src/Player.cpp
  28. 38 3
      components/spotify/cspot/src/PlayerState.cpp
  29. 3 0
      components/spotify/cspot/src/SpircController.cpp
  30. 6 1
      components/spotify/cspot/src/SpotifyTrack.cpp

+ 1 - 2
components/display/ST77xx.c

@@ -195,15 +195,14 @@ static void SetLayout( struct GDS_Device* Device, struct GDS_Layout *Layout ) {
 		if (Layout->Rotate) Private->Offset.Width += Layout->HFlip ? 320 - Device->Width : 0;
 		else Private->Offset.Height += Layout->HFlip ? 320 - Device->Height : 0;
 		Device->WriteCommand( Device, Layout->Invert ? 0x20 : 0x21 );			
-		Private->MADCtl = Layout->ColorSwap ? (Private->MADCtl & ~(1 << 3)) : (Private->MADCtl | (1 << 3));
 	} else {
 		Device->WriteCommand( Device, Layout->Invert ? 0x21 : 0x20 );	
-		Private->MADCtl = Layout->ColorSwap ? (Private->MADCtl | (1 << 3)) : (Private->MADCtl & ~(1 << 3));		
 	}		
 
 	Private->MADCtl = Layout->HFlip ? (Private->MADCtl | (1 << 7)) : (Private->MADCtl & ~(1 << 7));
 	Private->MADCtl = Layout->VFlip ? (Private->MADCtl | (1 << 6)) : (Private->MADCtl & ~(1 << 6));
 	Private->MADCtl = Layout->Rotate ? (Private->MADCtl | (1 << 5)) : (Private->MADCtl & ~(1 << 5));
+	Private->MADCtl = Layout->ColorSwap ? (Private->MADCtl & ~(1 << 3)) : (Private->MADCtl | (1 << 3));	
 
 	Device->WriteCommand( Device, 0x36 );
 	WriteByte( Device, Private->MADCtl );

+ 1 - 1
components/display/display.c

@@ -148,7 +148,7 @@ void display_init(char *welcome) {
 			.Invert = strcasestr(config, "invert"),
 			.ColorSwap = strcasestr(config, "cswap"),
 		};	
-					
+
 		GDS_SetLayout(display, &Layout);
 		GDS_SetFont(display, &Font_line_2);
 		GDS_TextPos(display, GDS_FONT_DEFAULT, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, welcome);

+ 6 - 2
components/spotify/cspot/bell/include/AudioSink.h

@@ -12,8 +12,12 @@ class 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; }
+	// Return false if the sink doesn't support reconfiguration.
+	virtual bool setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) { return false; }
+	// Deprecated. Implement/use setParams() instead.
+	virtual inline bool setRate(uint16_t sampleRate) {
+		return setParams(sampleRate, 2, 16);
+	}
 	bool softwareVolumeControl = true;
 	bool usign = false;
 };

+ 1 - 0
components/spotify/cspot/bell/include/BaseHTTPServer.h

@@ -78,6 +78,7 @@ struct HTTPConnection {
     std::string httpMethod;
     bool toBeClosed = false;
     bool isEventConnection = false;
+    bool isCaptivePortal = false;
 };
 
 class BaseHTTPServer {

+ 1 - 0
components/spotify/cspot/bell/include/BellUtils.h

@@ -8,6 +8,7 @@
 namespace bell {
 
 std::string generateRandomUUID();
+void freeAndNull(void *&ptr);
 
 } // namespace bell
 

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

@@ -12,7 +12,7 @@ class BiquadFilter
 private:
     std::mutex processMutex;
     float coeffs[5];
-    float w[2];
+    float w[2] = {0, 0};
 
 public:
     BiquadFilter(){};

+ 13 - 13
components/spotify/cspot/bell/include/HTTPClient.h

@@ -36,14 +36,14 @@ class HTTPClient {
 		std::map<std::string, std::string> headers;
 
 		uint16_t statusCode;
-		size_t contentLength;
+		uint32_t contentLength;
 		std::string contentType;
 		std::string location;
 		bool isChunked = false;
 		bool isGzip = false;
 		bool isComplete = false;
 		bool isRedirect = false;
-		size_t redirectCount = 0;
+		uint8_t redirectCount = 0;
 		std::ostream *dumpFs = nullptr;
 		std::ostream *dumpRawFs = nullptr;
 
@@ -51,34 +51,34 @@ class HTTPClient {
 		void close() override;
 
 		void readHeaders();
-		size_t read(char *dst, size_t len, bool wait = false);
+		uint32_t read(char *dst, uint32_t len, bool wait = false);
 		std::string readToString();
 
 		inline size_t skip(size_t len) override {
-			return read((char *)nullptr, len);
+			return (size_t)read((char *)nullptr, len);
 		}
 		inline size_t read(uint8_t *dst, size_t len) override {
-			return read((char *)dst, len);
+			return (size_t)read((char *)dst, len, false);
 		}
-		inline size_t read(uint8_t *dst, size_t len, bool wait) {
+		inline uint32_t read(uint8_t *dst, uint32_t len, bool wait) {
 			return read((char *)dst, len, wait);
 		}
 		inline size_t size() override {
-			return contentLength;
+			return (size_t)contentLength;
 		}
 		inline size_t position() override {
-			return bodyRead;
+			return (size_t)bodyRead;
 		}
 
 	  private:
 		char *buf = nullptr;	// allocated buffer
 		char *bufPtr = nullptr; // reading pointer within buf
-		size_t bodyRead = 0;
-		size_t bufRemaining = 0;
-		size_t chunkRemaining = 0;
+		uint32_t bodyRead = 0;
+		uint32_t bufRemaining = 0;
+		uint32_t chunkRemaining = 0;
 		bool isStreaming = false;
-		size_t readRaw(char *dst);
-		bool skipRaw(size_t len, bool dontRead = false);
+		uint32_t readRaw(char *dst);
+		bool skipRaw(uint32_t len, bool dontRead = false);
 	};
 
 	typedef std::unique_ptr<struct HTTPClient::HTTPResponse> HTTPResponse_t;

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

@@ -51,7 +51,7 @@ namespace bell
         std::vector<std::string> splitUrl(const std::string &url, char delimiter);
         std::mutex responseMutex;
         std::vector<char> responseBuffer = std::vector<char>(128);
-
+        void redirectCaptivePortal(int connectionFd);
         void readFromClient(int clientFd);
         std::map<std::string, std::string> parseQueryString(const std::string &queryString);
         unsigned char h2int(char c);

+ 3 - 0
components/spotify/cspot/bell/include/JSONObject.h

@@ -2,6 +2,8 @@
 #define JSONOBJECT_H
 #include <cJSON.h>
 #include <string>
+#include <cstring>
+#include <vector>
 
 namespace bell {
     class JSONValue
@@ -24,6 +26,7 @@ namespace bell {
         ~JSONObject();
         JSONValue operator[](std::string index);
         std::string toString();
+        std::vector<uint8_t> toVector();
 
     private:
         cJSON* body;

+ 6 - 5
components/spotify/cspot/bell/include/TCPSocket.h

@@ -73,6 +73,7 @@ namespace bell
                        sizeof(int));  /* length of option value */
 
             freeaddrinfo(addr);
+            std::cout << "Socket opened" << std::endl;
         }
 
         size_t read(uint8_t *buf, size_t len) {
@@ -83,11 +84,11 @@ namespace bell
             return send(sockFd, buf, len, 0);
         }
 
-		size_t poll() {
-			int value;
-			ioctl(sockFd, FIONREAD, &value);
-			return value;
-		}
+        size_t poll() {
+            int value;
+            ioctl(sockFd, FIONREAD, &value);
+            return value;
+        }
 
         void close() {
             if (!isClosed) {

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

@@ -14,8 +14,8 @@
 class BufferedAudioSink : public AudioSink
 {
 public:
-    void feedPCMFrames(const uint8_t *buffer, size_t bytes);
-	bool setRate(uint16_t sampleRate) override;
+    void feedPCMFrames(const uint8_t *buffer, size_t bytes) override;
+	bool setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) override;
 protected:
     void startI2sFeed(size_t buf_size = 4096 * 8);
     void feedPCMFramesInternal(const void *pvItem, size_t xItemSize);

+ 1 - 2
components/spotify/cspot/bell/include/sinks/esp/SPDIFAudioSink.h

@@ -19,8 +19,7 @@ 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;
+	bool setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) override;
 private:
 };
 

+ 5 - 6
components/spotify/cspot/bell/include/sinks/unix/PortAudioSink.h

@@ -2,7 +2,7 @@
 
 #include <vector>
 #include "portaudio.h"
-#include <stdint.h>
+#include <cstdint>
 #include <iostream>
 #include "AudioSink.h"
 
@@ -10,11 +10,10 @@ 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;
+    ~PortAudioSink() override;
+    void feedPCMFrames(const uint8_t *buffer, size_t bytes) override;
+	bool setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) override;
     
 private:
-    PaStream *stream;
+    PaStream *stream = nullptr;
 };

+ 5 - 0
components/spotify/cspot/bell/src/BellUtils.cpp

@@ -18,3 +18,8 @@ std::string bell::generateRandomUUID() {
     }
     return res;
 }
+
+void bell::freeAndNull(void *&ptr) {
+	free(ptr);
+	ptr = nullptr;
+}

+ 7 - 4
components/spotify/cspot/bell/src/BinaryReader.cpp

@@ -24,7 +24,8 @@ void bell::BinaryReader::skip(size_t pos) {
 
 int32_t bell::BinaryReader::readInt() {
     uint8_t b[4];
-    stream->read((uint8_t *) b,4);
+    if (stream->read((uint8_t *) b,4) != 4)
+		return 0;
     
     return static_cast<int32_t>(
         (b[3])      |
@@ -35,11 +36,12 @@ int32_t bell::BinaryReader::readInt() {
 
 int16_t bell::BinaryReader::readShort() {
     uint8_t b[2];
-    stream->read((uint8_t *) b,2);
+    if (stream->read((uint8_t *) b,2) != 2)
+		return 0;
     
     return static_cast<int16_t>(
         (b[1])      |
-        (b[1] << 8));
+        (b[0] << 8));
 }
 
 
@@ -49,7 +51,8 @@ uint32_t bell::BinaryReader::readUInt() {
 
 uint8_t bell::BinaryReader::readByte() {
     uint8_t b[1];
-    stream->read((uint8_t *) b,1);
+    if (stream->read((uint8_t *) b,1) != 1)
+		return 0;
     return b[0];
 }
 

+ 26 - 16
components/spotify/cspot/bell/src/HTTPClient.cpp

@@ -13,9 +13,7 @@ void HTTPClient::HTTPResponse::close() {
 	bufPtr = nullptr;
 }
 HTTPClient::HTTPResponse::~HTTPResponse() {
-	socket = nullptr;
-	if (buf)
-		free(buf);
+	this->close();
 }
 
 HTTPResponse_t HTTPClient::execute(const struct HTTPRequest &request) {
@@ -70,10 +68,12 @@ HTTPResponse_t HTTPClient::executeImpl(const struct HTTPRequest &request, HTTPRe
 		stream << header.first << ": " << header.second << endl;
 	}
 	stream << endl;
-	stream << request.body;
+        if (request.body != nullptr) {
+            stream << request.body;
+        }
 	std::string data = stream.str();
 
-	size_t len = response->socket->write((uint8_t *)data.c_str(), data.size());
+	uint32_t len = response->socket->write((uint8_t *)data.c_str(), data.size());
 	if (len != data.size()) {
 		response->close();
 		BELL_LOG(error, "http", "Writing failed: wrote %d of %d bytes", len, data.size());
@@ -91,7 +91,7 @@ HTTPResponse_t HTTPClient::executeImpl(const struct HTTPRequest &request, HTTPRe
 }
 
 bool HTTPClient::readHeader(const char *&header, const char *name) {
-	size_t len = strlen(name);
+	uint32_t len = strlen(name);
 	if (strncasecmp(header, name, len) == 0) {
 		header += len;
 		while (*header == ' ')
@@ -101,8 +101,13 @@ bool HTTPClient::readHeader(const char *&header, const char *name) {
 	return false;
 }
 
-size_t HTTPClient::HTTPResponse::readRaw(char *dst) {
-	size_t len = this->socket->read((uint8_t *)dst, BUF_SIZE);
+uint32_t HTTPClient::HTTPResponse::readRaw(char *dst) {
+	if (!this->socket)
+		return 0; // socket is already closed, I guess
+	uint32_t len = this->socket->read((uint8_t *)dst, BUF_SIZE);
+	if (len == 0 || len == -1) {
+		isComplete = true;
+	}
 	if (dumpRawFs)
 		dumpRawFs->write(dst, (long)len);
 	//	BELL_LOG(debug, "http", "Read %d bytes", len);
@@ -111,7 +116,7 @@ size_t HTTPClient::HTTPResponse::readRaw(char *dst) {
 }
 
 void HTTPClient::HTTPResponse::readHeaders() {
-	size_t len;
+	uint32_t len;
 	char *line, *lineEnd;
 	bool complete = false;
 	std::string lineBuf;
@@ -185,8 +190,8 @@ void HTTPClient::HTTPResponse::readHeaders() {
 	} while (!complete && len); // if len == 0, the connection is closed
 }
 
-bool HTTPClient::HTTPResponse::skipRaw(size_t len, bool dontRead) {
-	size_t skip = 0;
+bool HTTPClient::HTTPResponse::skipRaw(uint32_t len, bool dontRead) {
+	uint32_t skip = 0;
 	if (len > bufRemaining) {
 		skip = len - bufRemaining;
 		len = bufRemaining;
@@ -211,16 +216,17 @@ bool HTTPClient::HTTPResponse::skipRaw(size_t len, bool dontRead) {
 	return true;
 }
 
-size_t HTTPClient::HTTPResponse::read(char *dst, size_t toRead, bool wait) {
+uint32_t HTTPClient::HTTPResponse::read(char *dst, uint32_t toRead, bool wait) {
 	if (isComplete) {
 		// end of chunked stream was found OR complete body was read
 		return 0;
 	}
 	auto *dstStart = dst ? dst : nullptr;
-	size_t read = 0;
+	uint32_t read = 0;
 	while (toRead) { // this loop ends after original toRead
 		skipRaw(0);	 // ensure the buffer contains data, wait if necessary
 		if (isChunked && !chunkRemaining) {
+			// chunked responses (either streaming or not)
 			if (*bufPtr == '0') { // all chunks were read *and emitted*
 				isComplete = true;
 				break;
@@ -241,11 +247,15 @@ size_t HTTPClient::HTTPResponse::read(char *dst, size_t toRead, bool wait) {
 			if (!skipRaw(endPtr - bufPtr + 2)) // skip the size and \r\n
 				break;						   // -> no more data, break out of main loop
 		} else if (contentLength && !chunkRemaining) {
+			// normal responses (having content-length)
 			chunkRemaining = contentLength;
+		} else if (!chunkRemaining) {
+			// fallback for non-chunked streams (without content-length)
+			chunkRemaining = toRead;
 		}
 
 		while (chunkRemaining && toRead) {
-			size_t count = std::min(toRead, std::min(bufRemaining, chunkRemaining));
+			uint32_t count = std::min(toRead, std::min(bufRemaining, chunkRemaining));
 			if (dst) {
 				memcpy(dst, bufPtr, count);
 				dst += count; // move the dst pointer
@@ -287,8 +297,8 @@ std::string HTTPClient::HTTPResponse::readToString() {
 		return result;
 	}
 	std::string result;
-        char buffer[BUF_SIZE+1]; // make space for null-terminator
-	size_t len;
+	char buffer[BUF_SIZE + 1]; // make space for null-terminator
+	uint32_t len;
 	do {
 		len = this->read(buffer, BUF_SIZE);
 		buffer[len] = '\0';

+ 31 - 4
components/spotify/cspot/bell/src/HTTPServer.cpp

@@ -170,16 +170,23 @@ void bell::HTTPServer::readFromClient(int clientFd) {
                 if (line.find("Content-Length: ") != std::string::npos) {
                     conn.contentLength =
                         std::stoi(line.substr(16, line.size() - 1));
-                    //BELL_LOG(info, "http", "Content-Length: %d",
-                    //         conn.contentLength);
+                }
+                // detect hostname for captive portal
+                if (line.find("Host: connectivitycheck.gstatic.com") != std::string::npos) {
+                    conn.isCaptivePortal = true;
+                    BELL_LOG(info, "http", "Captive portal request detected");
                 }
                 if (line.size() == 0) {
                     if (conn.contentLength != 0) {
                         conn.isReadingBody = true;
                         goto READBODY;
                     } else {
-                        findAndHandleRoute(conn.httpMethod, conn.currentLine,
-                                           clientFd);
+                        if (!conn.isCaptivePortal) {
+                            findAndHandleRoute(conn.httpMethod, conn.currentLine, clientFd);                        
+                        } else {
+                            this->redirectCaptivePortal(clientFd);
+                        }
+
                     }
                 }
             }
@@ -277,6 +284,21 @@ void bell::HTTPServer::respond(const HTTPResponse &response) {
     writeResponse(response);
 }
 
+void bell::HTTPServer::redirectCaptivePortal(int connectionFd) {
+    std::lock_guard lock(this->responseMutex);
+    std::stringstream stream;
+    stream << "HTTP/1.1 302 Found\r\n";
+    stream << "Server: bell-http\r\n";
+    stream << "Connection: close\r\n";
+    stream << "Location: http://euphonium.audio\r\n\r\n";
+    stream << "Content-Length: 9\r\n";
+    stream << "302 Found";
+    auto responseStr = stream.str();
+
+    write(connectionFd, responseStr.c_str(), responseStr.size());
+    this->closeConnection(connectionFd);
+}
+
 void bell::HTTPServer::redirectTo(const std::string & url, int connectionFd) {
     std::lock_guard lock(this->responseMutex);
     std::stringstream stream;
@@ -407,6 +429,11 @@ void bell::HTTPServer::findAndHandleRoute(std::string &url, std::string &body,
             if (routeSplit.back().find('*') != std::string::npos &&
                 urlSplit[1] == routeSplit[1]) {
                 matches = true;
+                for (int x = 1; x <= routeSplit.size() - 2; x++) {
+                    if (urlSplit[x] != routeSplit[x]) {
+                        matches = false;
+                    }
+                }
             }
 
             if (matches) {

+ 8 - 1
components/spotify/cspot/bell/src/JSONObject.cpp

@@ -42,4 +42,11 @@ std::string bell::JSONObject::toString()
     free(body);
     return retVal;
     
-}
+}
+
+std::vector<uint8_t> bell::JSONObject::toVector() {
+    char *body = cJSON_Print(this->body);
+    std::vector<uint8_t> res(body, body + strlen(body));
+    free(body);
+    return res;
+}

+ 3 - 2
components/spotify/cspot/bell/src/sinks/esp/BufferedAudioSink.cpp

@@ -40,7 +40,8 @@ void BufferedAudioSink::feedPCMFramesInternal(const void *pvItem, size_t xItemSi
     xRingbufferSend(dataBuffer, pvItem, xItemSize, portMAX_DELAY);
 }
 
-bool BufferedAudioSink::setRate(uint16_t sampleRate) {
-	i2s_set_sample_rates((i2s_port_t)0, sampleRate);
+bool BufferedAudioSink::setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth)  {
+	// TODO override this for sinks with custom mclk
+	i2s_set_clk((i2s_port_t)0, sampleRate, (i2s_bits_per_sample_t)bitDepth, (i2s_channel_t)channelCount);
 	return true;
 }

+ 11 - 13
components/spotify/cspot/bell/src/sinks/esp/SPDIFAudioSink.cpp

@@ -72,19 +72,21 @@ SPDIFAudioSink::SPDIFAudioSink(uint8_t spdifPin)
     spdif_buf_init();
     spdif_ptr = spdif_buf;
 	this->spdifPin = spdifPin;
-	this->initialize(44100);
+	this->setParams(44100, 16, 2);
 	startI2sFeed(SPDIF_BUF_SIZE * 16);
 }
 
-void SPDIFAudioSink::initialize(uint16_t sampleRate) {
-    int sample_rate = sampleRate * 2;
+bool SPDIFAudioSink::setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) {
+	if (bitDepth != 16 || channelCount != 2) // TODO support mono playback and different bit widths
+		return false;
+    int sample_rate = (int)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,
+    	.sample_rate = (uint32_t)sample_rate,
+        .bits_per_sample = (i2s_bits_per_sample_t)(bitDepth * 2),
         .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
         .communication_format = I2S_COMM_FORMAT_STAND_I2S,
         .intr_alloc_flags = 0,
@@ -100,18 +102,14 @@ void SPDIFAudioSink::initialize(uint16_t sampleRate) {
         .data_out_num = spdifPin,
         .data_in_num = -1,
     };
-    i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
+	i2s_driver_uninstall((i2s_port_t)0);
+    int err = i2s_driver_install((i2s_port_t)0, &i2s_config, 0, nullptr);
     i2s_set_pin((i2s_port_t)0, &pin_config);
+	return !err;
 }
 
-SPDIFAudioSink::~SPDIFAudioSink()
-{
-}
-
-bool SPDIFAudioSink::setRate(uint16_t sampleRate) {
+SPDIFAudioSink::~SPDIFAudioSink() {
 	i2s_driver_uninstall((i2s_port_t)0);
-	this->initialize(sampleRate);
-	return true;
 }
 
 int num_frames = 0;

+ 25 - 13
components/spotify/cspot/bell/src/sinks/unix/PortAudioSink.cpp

@@ -3,10 +3,13 @@
 PortAudioSink::PortAudioSink()
 {
     Pa_Initialize();
-	this->initialize(44100);
+	this->setParams(44100, 2, 16);
 }
 
-void PortAudioSink::initialize(uint16_t sampleRate) {
+bool PortAudioSink::setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) {
+	if (stream) {
+		Pa_StopStream(stream);
+	}
     PaStreamParameters outputParameters;
     outputParameters.device = Pa_GetDefaultOutputDevice();
     if (outputParameters.device == paNoDevice) {
@@ -15,8 +18,24 @@ void PortAudioSink::initialize(uint16_t sampleRate) {
     }
         printf("PortAudio: Default audio device not found!\n");
 
-    outputParameters.channelCount = 2;       /* stereo output */
-    outputParameters.sampleFormat = paInt16; /* 32 bit floating point output */
+    outputParameters.channelCount = channelCount;
+	switch (bitDepth) {
+		case 32:
+			outputParameters.sampleFormat = paInt32;
+			break;
+		case 24:
+			outputParameters.sampleFormat = paInt24;
+			break;
+		case 16:
+			outputParameters.sampleFormat = paInt16;
+			break;
+		case 8:
+			outputParameters.sampleFormat = paInt8;
+			break;
+		default:
+			outputParameters.sampleFormat = paInt16;
+			break;
+	}
     outputParameters.suggestedLatency = 0.050;
     outputParameters.hostApiSpecificStreamInfo = NULL;
 
@@ -25,12 +44,13 @@ void PortAudioSink::initialize(uint16_t sampleRate) {
         NULL,
         &outputParameters,
 		sampleRate,
-        4096 / 4,
+        4096 / (channelCount * bitDepth / 8),
         paClipOff,
         NULL, // blocking api
         NULL
     );
     Pa_StartStream(stream);
+	return !err;
 }
 
 PortAudioSink::~PortAudioSink()
@@ -39,14 +59,6 @@ PortAudioSink::~PortAudioSink()
     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);

+ 2 - 2
components/spotify/cspot/include/ChunkedAudioStream.h

@@ -16,7 +16,7 @@
 
 #define SPOTIFY_HEADER_SIZE 167
 #define BUFFER_SIZE 0x20000 * 1.5
-typedef std::function<void(std::vector<uint8_t>&)> pcmDataCallback;
+typedef std::function<void(uint8_t *, size_t)> pcmDataCallback;
 
 enum class Whence
 {
@@ -59,7 +59,7 @@ public:
     std::vector<uint8_t> read(size_t bytes);
     void seekMs(uint32_t positionMs);
     void seek(size_t pos, Whence whence);
-    void startPlaybackLoop();
+    void startPlaybackLoop(uint8_t *pcmOut, size_t pcmOut_len);
 };
 
 #endif

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

@@ -41,7 +41,7 @@ public:
     void pause();
     void cancelCurrentTrack();
     void seekMs(size_t positionMs);
-    void feedPCM(std::vector<uint8_t> &data);
+    void feedPCM(uint8_t *data, size_t len);
     void play();
     void stop();
 

+ 1 - 0
components/spotify/cspot/include/SpircController.h

@@ -51,6 +51,7 @@ private:
     void loadTrack(uint32_t position_ms = 0, bool isPaused = 0);
 public:
     SpircController(std::shared_ptr<MercuryManager> manager, std::string username, std::shared_ptr<AudioSink> audioSink);
+    ~SpircController();
     void subscribe();
 
     /** 

+ 4 - 6
components/spotify/cspot/src/ChunkedAudioStream.cpp

@@ -77,7 +77,7 @@ void ChunkedAudioStream::seekMs(uint32_t positionMs)
     CSPOT_LOG(debug, "--- Finished seeking!");
 }
 
-void ChunkedAudioStream::startPlaybackLoop()
+void ChunkedAudioStream::startPlaybackLoop(uint8_t *pcmOut, size_t pcmOut_len)
 {
 
     isRunning = true;
@@ -91,7 +91,6 @@ void ChunkedAudioStream::startPlaybackLoop()
     }
 
     bool eof = false;
-    std::vector<uint8_t> pcmOut(4096 / 4);
     byteStream->setEnableLoadAhead(true);
 
     while (!eof && isRunning)
@@ -100,7 +99,7 @@ void ChunkedAudioStream::startPlaybackLoop()
         {
 
             this->seekMutex.lock();
-            long ret = ov_read(&vorbisFile, (char *)&pcmOut[0], 4096 / 4, &currentSection);
+            long ret = ov_read(&vorbisFile, (char *)&pcmOut[0], pcmOut_len, &currentSection);
             this->seekMutex.unlock();
             if (ret == 0)
             {
@@ -117,8 +116,7 @@ void ChunkedAudioStream::startPlaybackLoop()
             else
             {
                 // Write the actual data
-                auto data = std::vector<uint8_t>(pcmOut.begin(), pcmOut.begin() + ret);
-                pcmCallback(data);
+                pcmCallback(pcmOut, ret);
                 // audioSink->feedPCMFrames(data);
             }
         }
@@ -170,4 +168,4 @@ void ChunkedAudioStream::seek(size_t dpos, Whence whence)
         break;
     }
     byteStream->seek(seekPos);
-}
+}

+ 1 - 0
components/spotify/cspot/src/ChunkedByteStream.cpp

@@ -79,6 +79,7 @@ size_t ChunkedByteStream::read(uint8_t *buf, size_t nbytes) {
     if (chunk != nullptr) {
         // Wait for chunk if not loaded yet
         if (!chunk->isLoaded && !chunk->isFailed) {
+            BELL_LOG(info, "cspot", "Chunk not loaded, waiting for %d", chunkIndex);
             chunk->isLoadedSemaphore->wait();
         }
 

+ 11 - 9
components/spotify/cspot/src/Player.cpp

@@ -43,7 +43,7 @@ void Player::seekMs(size_t positionMs)
     // VALGRIND_DO_LEAK_CHECK;
 }
 
-void Player::feedPCM(std::vector<uint8_t>& data)
+void Player::feedPCM(uint8_t *data, size_t len)
 {
     // Simple digital volume control alg
     // @TODO actually extract it somewhere
@@ -51,8 +51,8 @@ void Player::feedPCM(std::vector<uint8_t>& data)
     {
         int16_t* psample;
         uint32_t pmax;
-        psample = (int16_t*)(data.data());
-        for (int32_t i = 0; i < (data.size() / 2); i++)
+        psample = (int16_t*)(data);
+        for (int32_t i = 0; i < (len / 2); i++)
         {
             int32_t temp;
             // Offset data for unsigned sinks
@@ -68,25 +68,27 @@ void Player::feedPCM(std::vector<uint8_t>& data)
         }
     }
 
-    this->audioSink->feedPCMFrames(data.data(), data.size());
+    this->audioSink->feedPCMFrames(data, len);
 }
 
 void Player::runTask()
 {
+    uint8_t *pcmOut = (uint8_t *) malloc(4096 / 4);
     std::scoped_lock lock(this->runningMutex);
     this->isRunning = true;
     while (isRunning)
     {
         if (this->trackQueue.wpop(currentTrack)) {
-            currentTrack->audioStream->startPlaybackLoop();
+            currentTrack->audioStream->startPlaybackLoop(pcmOut, 4096 / 4);
             currentTrack->loadedTrackCallback = nullptr;
             currentTrack->audioStream->streamFinishedCallback = nullptr;
             currentTrack->audioStream->audioSink = nullptr;
             currentTrack->audioStream->pcmCallback = nullptr;
         } else {
-            //usleep(100000);
+            usleep(100);
         }
     }
+    free(pcmOut);
 }
 
 void Player::stop() {
@@ -115,9 +117,9 @@ void Player::handleLoad(std::shared_ptr<TrackReference> trackReference, std::fun
     std::lock_guard<std::mutex> guard(loadTrackMutex);
     cancelCurrentTrack();
 
-    pcmDataCallback framesCallback = [=](std::vector<uint8_t>& frames) {
-        this->feedPCM(frames);
-    };
+    pcmDataCallback framesCallback = [=](uint8_t *frames, size_t len) {
+        this->feedPCM(frames, len);
+     };
 
     auto loadedLambda = trackLoadedCallback;
 

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

@@ -37,6 +37,8 @@ PlayerState::PlayerState(std::shared_ptr<TimeProvider> timeProvider)
 
     innerFrame.device_state.name = strdup(configMan->deviceName.c_str());
 
+    innerFrame.state.track_count = 0;
+
     // Prepare player's capabilities
     addCapability(CapabilityType_kCanBePlayer, 1);
     addCapability(CapabilityType_kDeviceType, 4);
@@ -133,11 +135,44 @@ void PlayerState::updatePositionMs(uint32_t position)
     innerFrame.state.position_ms = position;
     innerFrame.state.position_measured_at = timeProvider->getSyncedTimestamp();
 }
+
 void PlayerState::updateTracks()
 {
     CSPOT_LOG(info, "---- Track count %d", remoteFrame.state.track_count);
-    std::swap(innerFrame.state.context_uri, remoteFrame.state.context_uri);
-    std::swap(innerFrame.state.track, remoteFrame.state.track);
+
+    // free unused tracks
+    if(innerFrame.state.track_count > remoteFrame.state.track_count)
+    {
+        for(uint16_t i = remoteFrame.state.track_count; i < innerFrame.state.track_count; ++i)
+        {
+            free(innerFrame.state.track[i].gid);
+        }
+    }
+
+    // reallocate memory for new tracks
+    innerFrame.state.track = (TrackRef *) realloc(innerFrame.state.track, sizeof(TrackRef) * remoteFrame.state.track_count);
+
+    for(uint16_t i = 0; i < remoteFrame.state.track_count; ++i)
+    {
+        uint16_t gid_size = remoteFrame.state.track[i].gid->size;
+        // allocate if need more tracks
+        if(i >= innerFrame.state.track_count)
+        {
+            innerFrame.state.track[i].gid = (pb_bytes_array_t *) malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(gid_size));
+        }
+        memcpy(innerFrame.state.track[i].gid->bytes, remoteFrame.state.track[i].gid->bytes, gid_size);
+        innerFrame.state.track[i].gid->size = gid_size;
+        innerFrame.state.track[i].has_queued = remoteFrame.state.track[i].has_queued;
+        innerFrame.state.track[i].queued = remoteFrame.state.track[i].queued;
+        // not used?
+        innerFrame.state.track[i].uri = NULL;
+        innerFrame.state.track[i].context = NULL;
+    }
+
+    innerFrame.state.context_uri = (char *) realloc(innerFrame.state.context_uri,
+                                            strlen(remoteFrame.state.context_uri) + 1);
+    strcpy(innerFrame.state.context_uri, remoteFrame.state.context_uri);
+
     innerFrame.state.track_count = remoteFrame.state.track_count;
     innerFrame.state.has_playing_track_index = true;
     innerFrame.state.playing_track_index = remoteFrame.state.playing_track_index;
@@ -236,4 +271,4 @@ void PlayerState::addCapability(CapabilityType typ, int intValue, std::vector<st
 
     this->innerFrame.device_state.capabilities[capabilityIndex].stringValue_count = stringValue.size();
     this->capabilityIndex += 1;
-}
+}

+ 3 - 0
components/spotify/cspot/src/SpircController.cpp

@@ -22,6 +22,9 @@ SpircController::SpircController(std::shared_ptr<MercuryManager> manager,
     subscribe();
 }
 
+SpircController::~SpircController() {
+}
+
 void SpircController::subscribe() {
     mercuryCallback responseLambda = [=](std::unique_ptr<MercuryResponse> res) {
         // this->trackInformationCallback(std::move(res));

+ 6 - 1
components/spotify/cspot/src/SpotifyTrack.cpp

@@ -89,8 +89,13 @@ void SpotifyTrack::trackInformationCallback(std::unique_ptr<MercuryResponse> res
         std::swap(trackInfo.file, trackInfo.alternative[altIndex].file);
         std::swap(trackInfo.file_count, trackInfo.alternative[altIndex].file_count);
         std::swap(trackInfo.gid, trackInfo.alternative[altIndex].gid);
-
         CSPOT_LOG(info, "Trying alternative %d", altIndex);
+        altIndex++;
+    
+        if(altIndex > trackInfo.alternative_count) {
+            // no alternatives for song
+            return;
+        }
     }
 
     auto trackId = pbArrayToVector(trackInfo.gid);