From 57b77766ffa83e9d0b4bc0c39ee92bfd254082a4 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Fri, 4 Mar 2022 20:06:19 -0800 Subject: [PATCH] update CSpot --- .../spotify/cspot/bell/include/AudioSink.h | 8 +++- .../cspot/bell/include/BaseHTTPServer.h | 1 + .../spotify/cspot/bell/include/BellUtils.h | 1 + .../spotify/cspot/bell/include/BiquadFilter.h | 2 +- .../spotify/cspot/bell/include/HTTPClient.h | 26 ++++++------ .../spotify/cspot/bell/include/HTTPServer.h | 2 +- .../spotify/cspot/bell/include/JSONObject.h | 3 ++ .../spotify/cspot/bell/include/TCPSocket.h | 11 ++--- .../include/sinks/esp/BufferedAudioSink.h | 4 +- .../bell/include/sinks/esp/SPDIFAudioSink.h | 3 +- .../bell/include/sinks/unix/PortAudioSink.h | 11 +++-- .../spotify/cspot/bell/src/BellUtils.cpp | 5 +++ .../spotify/cspot/bell/src/BinaryReader.cpp | 11 +++-- .../spotify/cspot/bell/src/HTTPClient.cpp | 42 ++++++++++++------- .../spotify/cspot/bell/src/HTTPServer.cpp | 35 ++++++++++++++-- .../spotify/cspot/bell/src/JSONObject.cpp | 9 +++- .../bell/src/sinks/esp/BufferedAudioSink.cpp | 5 ++- .../bell/src/sinks/esp/SPDIFAudioSink.cpp | 30 +++++++------ .../bell/src/sinks/unix/PortAudioSink.cpp | 38 +++++++++++------ .../cspot/include/ChunkedAudioStream.h | 4 +- components/spotify/cspot/include/Player.h | 2 +- .../spotify/cspot/include/SpircController.h | 1 + .../spotify/cspot/src/ChunkedAudioStream.cpp | 10 ++--- .../spotify/cspot/src/ChunkedByteStream.cpp | 1 + components/spotify/cspot/src/Player.cpp | 20 +++++---- components/spotify/cspot/src/PlayerState.cpp | 41 ++++++++++++++++-- .../spotify/cspot/src/SpircController.cpp | 3 ++ components/spotify/cspot/src/SpotifyTrack.cpp | 7 +++- 28 files changed, 226 insertions(+), 110 deletions(-) diff --git a/components/spotify/cspot/bell/include/AudioSink.h b/components/spotify/cspot/bell/include/AudioSink.h index 2f0be3de..313193eb 100644 --- a/components/spotify/cspot/bell/include/AudioSink.h +++ b/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; }; diff --git a/components/spotify/cspot/bell/include/BaseHTTPServer.h b/components/spotify/cspot/bell/include/BaseHTTPServer.h index 6526be79..8881ef80 100644 --- a/components/spotify/cspot/bell/include/BaseHTTPServer.h +++ b/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 { diff --git a/components/spotify/cspot/bell/include/BellUtils.h b/components/spotify/cspot/bell/include/BellUtils.h index ac88eb7b..175af44e 100644 --- a/components/spotify/cspot/bell/include/BellUtils.h +++ b/components/spotify/cspot/bell/include/BellUtils.h @@ -8,6 +8,7 @@ namespace bell { std::string generateRandomUUID(); +void freeAndNull(void *&ptr); } // namespace bell diff --git a/components/spotify/cspot/bell/include/BiquadFilter.h b/components/spotify/cspot/bell/include/BiquadFilter.h index bbf041d2..7053c1f1 100644 --- a/components/spotify/cspot/bell/include/BiquadFilter.h +++ b/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(){}; diff --git a/components/spotify/cspot/bell/include/HTTPClient.h b/components/spotify/cspot/bell/include/HTTPClient.h index 6461f6b8..5c7709d4 100644 --- a/components/spotify/cspot/bell/include/HTTPClient.h +++ b/components/spotify/cspot/bell/include/HTTPClient.h @@ -36,14 +36,14 @@ class HTTPClient { std::map 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 HTTPResponse_t; diff --git a/components/spotify/cspot/bell/include/HTTPServer.h b/components/spotify/cspot/bell/include/HTTPServer.h index 28072f52..3394df75 100644 --- a/components/spotify/cspot/bell/include/HTTPServer.h +++ b/components/spotify/cspot/bell/include/HTTPServer.h @@ -51,7 +51,7 @@ namespace bell std::vector splitUrl(const std::string &url, char delimiter); std::mutex responseMutex; std::vector responseBuffer = std::vector(128); - + void redirectCaptivePortal(int connectionFd); void readFromClient(int clientFd); std::map parseQueryString(const std::string &queryString); unsigned char h2int(char c); diff --git a/components/spotify/cspot/bell/include/JSONObject.h b/components/spotify/cspot/bell/include/JSONObject.h index 8cecacd9..53926f47 100644 --- a/components/spotify/cspot/bell/include/JSONObject.h +++ b/components/spotify/cspot/bell/include/JSONObject.h @@ -2,6 +2,8 @@ #define JSONOBJECT_H #include #include +#include +#include namespace bell { class JSONValue @@ -24,6 +26,7 @@ namespace bell { ~JSONObject(); JSONValue operator[](std::string index); std::string toString(); + std::vector toVector(); private: cJSON* body; diff --git a/components/spotify/cspot/bell/include/TCPSocket.h b/components/spotify/cspot/bell/include/TCPSocket.h index 3128b508..9f26a693 100644 --- a/components/spotify/cspot/bell/include/TCPSocket.h +++ b/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) { diff --git a/components/spotify/cspot/bell/include/sinks/esp/BufferedAudioSink.h b/components/spotify/cspot/bell/include/sinks/esp/BufferedAudioSink.h index f090f582..eb63caf9 100644 --- a/components/spotify/cspot/bell/include/sinks/esp/BufferedAudioSink.h +++ b/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); diff --git a/components/spotify/cspot/bell/include/sinks/esp/SPDIFAudioSink.h b/components/spotify/cspot/bell/include/sinks/esp/SPDIFAudioSink.h index 6f3da9d1..91ca110b 100644 --- a/components/spotify/cspot/bell/include/sinks/esp/SPDIFAudioSink.h +++ b/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: }; diff --git a/components/spotify/cspot/bell/include/sinks/unix/PortAudioSink.h b/components/spotify/cspot/bell/include/sinks/unix/PortAudioSink.h index 3a5ea2bd..d3d435cf 100644 --- a/components/spotify/cspot/bell/include/sinks/unix/PortAudioSink.h +++ b/components/spotify/cspot/bell/include/sinks/unix/PortAudioSink.h @@ -2,7 +2,7 @@ #include #include "portaudio.h" -#include +#include #include #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; }; diff --git a/components/spotify/cspot/bell/src/BellUtils.cpp b/components/spotify/cspot/bell/src/BellUtils.cpp index f3be492c..3afe318d 100644 --- a/components/spotify/cspot/bell/src/BellUtils.cpp +++ b/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; +} diff --git a/components/spotify/cspot/bell/src/BinaryReader.cpp b/components/spotify/cspot/bell/src/BinaryReader.cpp index cf5e8760..f67d8b4f 100644 --- a/components/spotify/cspot/bell/src/BinaryReader.cpp +++ b/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( (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( (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]; } diff --git a/components/spotify/cspot/bell/src/HTTPClient.cpp b/components/spotify/cspot/bell/src/HTTPClient.cpp index c500f804..bb4837e8 100644 --- a/components/spotify/cspot/bell/src/HTTPClient.cpp +++ b/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'; diff --git a/components/spotify/cspot/bell/src/HTTPServer.cpp b/components/spotify/cspot/bell/src/HTTPServer.cpp index 0c168dd1..2d3e2518 100644 --- a/components/spotify/cspot/bell/src/HTTPServer.cpp +++ b/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) { diff --git a/components/spotify/cspot/bell/src/JSONObject.cpp b/components/spotify/cspot/bell/src/JSONObject.cpp index 8e303a28..310e34ae 100644 --- a/components/spotify/cspot/bell/src/JSONObject.cpp +++ b/components/spotify/cspot/bell/src/JSONObject.cpp @@ -42,4 +42,11 @@ std::string bell::JSONObject::toString() free(body); return retVal; -} \ No newline at end of file +} + +std::vector bell::JSONObject::toVector() { + char *body = cJSON_Print(this->body); + std::vector res(body, body + strlen(body)); + free(body); + return res; +} diff --git a/components/spotify/cspot/bell/src/sinks/esp/BufferedAudioSink.cpp b/components/spotify/cspot/bell/src/sinks/esp/BufferedAudioSink.cpp index 5f7138bd..4d849fc3 100644 --- a/components/spotify/cspot/bell/src/sinks/esp/BufferedAudioSink.cpp +++ b/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; } \ No newline at end of file diff --git a/components/spotify/cspot/bell/src/sinks/esp/SPDIFAudioSink.cpp b/components/spotify/cspot/bell/src/sinks/esp/SPDIFAudioSink.cpp index 1ac5e097..893985cb 100644 --- a/components/spotify/cspot/bell/src/sinks/esp/SPDIFAudioSink.cpp +++ b/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_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 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() { + i2s_driver_uninstall((i2s_port_t)0); } int num_frames = 0; diff --git a/components/spotify/cspot/bell/src/sinks/unix/PortAudioSink.cpp b/components/spotify/cspot/bell/src/sinks/unix/PortAudioSink.cpp index 48bdb1d0..51ff7ed3 100644 --- a/components/spotify/cspot/bell/src/sinks/unix/PortAudioSink.cpp +++ b/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); diff --git a/components/spotify/cspot/include/ChunkedAudioStream.h b/components/spotify/cspot/include/ChunkedAudioStream.h index 5aa5bdd5..3ca5b024 100644 --- a/components/spotify/cspot/include/ChunkedAudioStream.h +++ b/components/spotify/cspot/include/ChunkedAudioStream.h @@ -16,7 +16,7 @@ #define SPOTIFY_HEADER_SIZE 167 #define BUFFER_SIZE 0x20000 * 1.5 -typedef std::function&)> pcmDataCallback; +typedef std::function pcmDataCallback; enum class Whence { @@ -59,7 +59,7 @@ public: std::vector 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 diff --git a/components/spotify/cspot/include/Player.h b/components/spotify/cspot/include/Player.h index 3154f93d..b92b1cf7 100644 --- a/components/spotify/cspot/include/Player.h +++ b/components/spotify/cspot/include/Player.h @@ -41,7 +41,7 @@ public: void pause(); void cancelCurrentTrack(); void seekMs(size_t positionMs); - void feedPCM(std::vector &data); + void feedPCM(uint8_t *data, size_t len); void play(); void stop(); diff --git a/components/spotify/cspot/include/SpircController.h b/components/spotify/cspot/include/SpircController.h index a681031b..89c34d86 100644 --- a/components/spotify/cspot/include/SpircController.h +++ b/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 manager, std::string username, std::shared_ptr audioSink); + ~SpircController(); void subscribe(); /** diff --git a/components/spotify/cspot/src/ChunkedAudioStream.cpp b/components/spotify/cspot/src/ChunkedAudioStream.cpp index 432eb103..6185153f 100644 --- a/components/spotify/cspot/src/ChunkedAudioStream.cpp +++ b/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 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, ¤tSection); + long ret = ov_read(&vorbisFile, (char *)&pcmOut[0], pcmOut_len, ¤tSection); this->seekMutex.unlock(); if (ret == 0) { @@ -117,8 +116,7 @@ void ChunkedAudioStream::startPlaybackLoop() else { // Write the actual data - auto data = std::vector(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); -} \ No newline at end of file +} diff --git a/components/spotify/cspot/src/ChunkedByteStream.cpp b/components/spotify/cspot/src/ChunkedByteStream.cpp index 0c92aa84..e44467e9 100644 --- a/components/spotify/cspot/src/ChunkedByteStream.cpp +++ b/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(); } diff --git a/components/spotify/cspot/src/Player.cpp b/components/spotify/cspot/src/Player.cpp index 3bccc0b0..b7c59396 100644 --- a/components/spotify/cspot/src/Player.cpp +++ b/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& 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& 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& 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, std::fun std::lock_guard guard(loadTrackMutex); cancelCurrentTrack(); - pcmDataCallback framesCallback = [=](std::vector& frames) { - this->feedPCM(frames); - }; + pcmDataCallback framesCallback = [=](uint8_t *frames, size_t len) { + this->feedPCM(frames, len); + }; auto loadedLambda = trackLoadedCallback; diff --git a/components/spotify/cspot/src/PlayerState.cpp b/components/spotify/cspot/src/PlayerState.cpp index 8c3ef7b4..60d4b85f 100644 --- a/components/spotify/cspot/src/PlayerState.cpp +++ b/components/spotify/cspot/src/PlayerState.cpp @@ -37,6 +37,8 @@ PlayerState::PlayerState(std::shared_ptr 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::vectorinnerFrame.device_state.capabilities[capabilityIndex].stringValue_count = stringValue.size(); this->capabilityIndex += 1; -} \ No newline at end of file +} diff --git a/components/spotify/cspot/src/SpircController.cpp b/components/spotify/cspot/src/SpircController.cpp index 7de1e737..de4eb538 100644 --- a/components/spotify/cspot/src/SpircController.cpp +++ b/components/spotify/cspot/src/SpircController.cpp @@ -22,6 +22,9 @@ SpircController::SpircController(std::shared_ptr manager, subscribe(); } +SpircController::~SpircController() { +} + void SpircController::subscribe() { mercuryCallback responseLambda = [=](std::unique_ptr res) { // this->trackInformationCallback(std::move(res)); diff --git a/components/spotify/cspot/src/SpotifyTrack.cpp b/components/spotify/cspot/src/SpotifyTrack.cpp index ad39f122..6003100a 100644 --- a/components/spotify/cspot/src/SpotifyTrack.cpp +++ b/components/spotify/cspot/src/SpotifyTrack.cpp @@ -89,8 +89,13 @@ void SpotifyTrack::trackInformationCallback(std::unique_ptr 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);