diff --git a/components/spotify/cspot/bell/include/BiquadFilter.h b/components/spotify/cspot/bell/include/BiquadFilter.h index 7053c1f1..0f83e97e 100644 --- a/components/spotify/cspot/bell/include/BiquadFilter.h +++ b/components/spotify/cspot/bell/include/BiquadFilter.h @@ -17,6 +17,59 @@ private: public: BiquadFilter(){}; + // Generates coefficients for a high pass biquad filter + void generateHighPassCoEffs(float f, float q){ + if (q <= 0.0001) { + q = 0.0001; + } + float Fs = 1; + + float w0 = 2 * M_PI * f / Fs; + float c = cosf(w0); + float s = sinf(w0); + float alpha = s / (2 * q); + + float b0 = (1 + c) / 2; + float b1 = -(1 + c); + float b2 = b0; + float a0 = 1 + alpha; + float a1 = -2 * c; + float a2 = 1 - alpha; + + coeffs[0] = b0 / a0; + coeffs[1] = b1 / a0; + coeffs[2] = b2 / a0; + coeffs[3] = a1 / a0; + coeffs[4] = a2 / a0; + } + + // Generates coefficients for a low pass biquad filter + void generateLowPassCoEffs(float f, float q){ + if (q <= 0.0001) { + q = 0.0001; + } + float Fs = 1; + + float w0 = 2 * M_PI * f / Fs; + float c = cosf(w0); + float s = sinf(w0); + float alpha = s / (2 * q); + + float b0 = (1 - c) / 2; + float b1 = 1 - c; + float b2 = b0; + float a0 = 1 + alpha; + float a1 = -2 * c; + float a2 = 1 - alpha; + + coeffs[0] = b0 / a0; + coeffs[1] = b1 / a0; + coeffs[2] = b2 / a0; + coeffs[3] = a1 / a0; + coeffs[4] = a2 / a0; + } + + // Generates coefficients for a high shelf biquad filter void generateHighShelfCoEffs(float f, float gain, float q) { if (q <= 0.0001) @@ -106,6 +159,119 @@ public: coeffs[4] = a2 / a0; } + // Generates coefficients for a peaking biquad filter + void generatePeakCoEffs(float f, float gain, float q) + { + if (q <= 0.0001) { + q = 0.0001; + } + float Fs = 1; + + float w0 = 2 * M_PI * f / Fs; + float c = cosf(w0); + float s = sinf(w0); + float alpha = s / (2 * q); + + float b0 = alpha; + float b1 = 0; + float b2 = -alpha; + float a0 = 1 + alpha; + float a1 = -2 * c; + float a2 = 1 - alpha; + + coeffs[0] = b0 / a0; + coeffs[1] = b1 / a0; + coeffs[2] = b2 / a0; + coeffs[3] = a1 / a0; + coeffs[4] = a2 / a0; + } + + + // Generates coefficients for a peaking EQ biquad filter + void generatePeakingEqCoEffs(float f, float gain, float q) + { + // formular taken from: https://webaudio.github.io/Audio-EQ-Cookbook/audio-eq-cookbook.html + if (q <= 0.0001) { + q = 0.0001; + } + float Fs = 1; + + float w0 = 2 * M_PI * f / Fs; + float c = cosf(w0); + float s = sinf(w0); + float alpha = s / (2 * q); + float A = sqrtf(pow(10, (double)gain / 20.0)); + + float b0 = 1 + (alpha*A); + float b1 = (-2*c); + float b2 = 1 - (alpha*A); + float a0 = 1 + (alpha/A); + float a1 = b1; + float a2 = 1 - (alpha/A); + + coeffs[0] = b0 / a0; + coeffs[1] = b1 / a0; + coeffs[2] = b2 / a0; + coeffs[3] = a1 / a0; + coeffs[4] = a2 / a0; + } + + + + // Generates coefficients for an all pass 180° biquad filter + void generateAllPass180CoEffs(float f, float q) + { + if (q <= 0.0001) { + q = 0.0001; + } + float Fs = 1; + + float w0 = 2 * M_PI * f / Fs; + float c = cosf(w0); + float s = sinf(w0); + float alpha = s / (2 * q); + + float b0 = 1 - alpha; + float b1 = -2 * c; + float b2 = 1 + alpha; + float a0 = 1 + alpha; + float a1 = -2 * c; + float a2 = 1 - alpha; + + coeffs[0] = b0 / a0; + coeffs[1] = b1 / a0; + coeffs[2] = b2 / a0; + coeffs[3] = a1 / a0; + coeffs[4] = a2 / a0; + } + + // Generates coefficients for an all pass 360° biquad filter + void generateAllPass360CoEffs(float f, float q) + { + if (q <= 0.0001) { + q = 0.0001; + } + float Fs = 1; + + float w0 = 2 * M_PI * f / Fs; + float c = cosf(w0); + float s = sinf(w0); + float alpha = s / (2 * q); + + float b0 = 1 - alpha; + float b1 = -2 * c; + float b2 = 1 + alpha; + float a0 = 1 + alpha; + float a1 = -2 * c; + float a2 = 1 - alpha; + + coeffs[0] = b0 / a0; + coeffs[1] = b1 / a0; + coeffs[2] = b2 / a0; + coeffs[3] = a1 / a0; + coeffs[4] = a2 / a0; + } + void processSamples(float *input, int numSamples) { std::scoped_lock lock(processMutex); @@ -125,4 +291,4 @@ public: } }; -#endif \ No newline at end of file +#endif diff --git a/components/spotify/cspot/bell/src/BinaryReader.cpp b/components/spotify/cspot/bell/src/BinaryReader.cpp index f67d8b4f..95db59de 100644 --- a/components/spotify/cspot/bell/src/BinaryReader.cpp +++ b/components/spotify/cspot/bell/src/BinaryReader.cpp @@ -67,6 +67,6 @@ long long bell::BinaryReader::readLong() { long low = readInt(); return static_cast( - (high << 32) | low ); + ((long long) high << 32) | low ); } diff --git a/components/spotify/cspot/bell/src/CryptoOpenSSL.cpp b/components/spotify/cspot/bell/src/CryptoOpenSSL.cpp index 5b5ac171..662e2d84 100644 --- a/components/spotify/cspot/bell/src/CryptoOpenSSL.cpp +++ b/components/spotify/cspot/bell/src/CryptoOpenSSL.cpp @@ -127,6 +127,7 @@ void CryptoOpenSSL::aesECBdecrypt(const std::vector& key, std::vector bell::HTTPServer::HTTPServer(int serverPort) { this->serverPort = serverPort; } @@ -65,8 +66,7 @@ void bell::HTTPServer::registerHandler(RequestType requestType, } void bell::HTTPServer::listen() { - BELL_LOG(info, "http", "Starting server at port %d", - this->serverPort); + BELL_LOG(info, "http", "Starting server at port %d", this->serverPort); // setup address struct addrinfo hints, *server; @@ -82,9 +82,20 @@ void bell::HTTPServer::listen() { socklen_t incomingSockSize; int i; int yes = true; - setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); - bind(sockfd, server->ai_addr, server->ai_addrlen); - ::listen(sockfd, 10); + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) { + throw std::runtime_error("setsockopt failed: " + + std::string(strerror(errno))); + } + if (bind(sockfd, server->ai_addr, server->ai_addrlen) < 0) { + throw std::runtime_error("bind failed on port " + + std::to_string(this->serverPort) + ": " + + std::string(strerror(errno))); + } + if (::listen(sockfd, 5) < 0) { + throw std::runtime_error("listen failed on port " + + std::to_string(this->serverPort) + ": " + + std::string(strerror(errno))); + } FD_ZERO(&activeFdSet); FD_SET(sockfd, &activeFdSet); @@ -172,7 +183,8 @@ void bell::HTTPServer::readFromClient(int clientFd) { std::stoi(line.substr(16, line.size() - 1)); } // detect hostname for captive portal - if (line.find("Host: connectivitycheck.gstatic.com") != std::string::npos) { + if (line.find("Host: connectivitycheck.gstatic.com") != + std::string::npos) { conn.isCaptivePortal = true; BELL_LOG(info, "http", "Captive portal request detected"); } @@ -182,11 +194,11 @@ void bell::HTTPServer::readFromClient(int clientFd) { goto READBODY; } else { if (!conn.isCaptivePortal) { - findAndHandleRoute(conn.httpMethod, conn.currentLine, clientFd); + findAndHandleRoute(conn.httpMethod, + conn.currentLine, clientFd); } else { this->redirectCaptivePortal(clientFd); } - } } } @@ -299,7 +311,7 @@ void bell::HTTPServer::redirectCaptivePortal(int connectionFd) { this->closeConnection(connectionFd); } -void bell::HTTPServer::redirectTo(const std::string & url, int connectionFd) { +void bell::HTTPServer::redirectTo(const std::string &url, int connectionFd) { std::lock_guard lock(this->responseMutex); std::stringstream stream; stream << "HTTP/1.1 301 Moved Permanently\r\n"; diff --git a/components/spotify/cspot/bell/src/sinks/esp/SPDIFAudioSink.cpp b/components/spotify/cspot/bell/src/sinks/esp/SPDIFAudioSink.cpp index 893985cb..d6ce712b 100644 --- a/components/spotify/cspot/bell/src/sinks/esp/SPDIFAudioSink.cpp +++ b/components/spotify/cspot/bell/src/sinks/esp/SPDIFAudioSink.cpp @@ -85,7 +85,11 @@ bool SPDIFAudioSink::setParams(uint32_t sampleRate, uint8_t channelCount, uint8_ i2s_config_t i2s_config = { .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), - .sample_rate = (uint32_t)sample_rate, +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0) + .sample_rate = (uint32_t) sample_rate, +#else + .sample_rate = (int) sample_rate, +#endif .bits_per_sample = (i2s_bits_per_sample_t)(bitDepth * 2), .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, .communication_format = I2S_COMM_FORMAT_STAND_I2S, diff --git a/components/spotify/cspot/include/ConfigJSON.h b/components/spotify/cspot/include/ConfigJSON.h index 7fce1811..cb16ad7c 100644 --- a/components/spotify/cspot/include/ConfigJSON.h +++ b/components/spotify/cspot/include/ConfigJSON.h @@ -18,6 +18,7 @@ public: uint16_t volume; std::string deviceName; + std::string apOverride; AudioFormat format; }; diff --git a/components/spotify/cspot/include/ConstantParameters.h b/components/spotify/cspot/include/ConstantParameters.h index 13a7a8fb..2a8c155e 100644 --- a/components/spotify/cspot/include/ConstantParameters.h +++ b/components/spotify/cspot/include/ConstantParameters.h @@ -7,9 +7,9 @@ extern char deviceId[]; // Hardcoded information sent to spotify servers -const char * const informationString = "cspot"; -const char * const brandName = "corn"; -const char * const versionString = "cspot-1.0"; +const char * const informationString = "cspot-player"; +const char * const brandName = "cspot"; +const char * const versionString = "cspot-1.1"; const char * const protocolVersion = "2.7.1"; const char * const defaultDeviceName = "CSpot"; const char * const swVersion = "1.0.0"; diff --git a/components/spotify/cspot/include/MercuryManager.h b/components/spotify/cspot/include/MercuryManager.h index e02c210e..38e9029d 100644 --- a/components/spotify/cspot/include/MercuryManager.h +++ b/components/spotify/cspot/include/MercuryManager.h @@ -69,7 +69,6 @@ private: std::unique_ptr queueSemaphore; unsigned long long lastRequestTimestamp = -1; unsigned long long lastPingTimestamp = -1; - std::atomic isRunning = false; uint64_t sequenceId; uint32_t audioKeySequence; audioKeyCallback keyCallback; @@ -78,10 +77,11 @@ private: public: MercuryManager(std::unique_ptr session); ~MercuryManager(); + std::atomic isRunning = false; voidCallback reconnectedCallback; uint16_t audioChunkSequence; std::shared_ptr timeProvider; - std::string countryCode; + char countryCode[2]; bool timeoutHandler(); uint64_t execute(MercuryType method, std::string uri, mercuryCallback &callback, mercuryCallback &subscription, mercuryParts &payload); diff --git a/components/spotify/cspot/include/Player.h b/components/spotify/cspot/include/Player.h index b92b1cf7..2ad8a439 100644 --- a/components/spotify/cspot/include/Player.h +++ b/components/spotify/cspot/include/Player.h @@ -14,17 +14,17 @@ #include "SpotifyTrack.h" #include "AudioSink.h" #include -#include "Queue.h" #include "Task.h" class Player : public bell::Task { private: std::shared_ptr manager; - std::shared_ptr currentTrack = nullptr; + SpotifyTrack *currentTrack = nullptr; + SpotifyTrack *nextTrack = nullptr; std::shared_ptr audioSink; std::mutex loadTrackMutex; - // @TODO: Use some actual structure here - bell::Queue> trackQueue; + WrappedMutex nextTrackMutex; + WrappedMutex currentTrackMutex; void runTask(); public: diff --git a/components/spotify/cspot/include/SpircController.h b/components/spotify/cspot/include/SpircController.h index 89c34d86..ab453f26 100644 --- a/components/spotify/cspot/include/SpircController.h +++ b/components/spotify/cspot/include/SpircController.h @@ -24,6 +24,7 @@ enum class CSpotEventType { PREV, SEEK, LOAD, + PLAYBACK_START }; struct CSpotEvent { diff --git a/components/spotify/cspot/include/SpotifyTrack.h b/components/spotify/cspot/include/SpotifyTrack.h index 603195d2..ff09ba7a 100644 --- a/components/spotify/cspot/include/SpotifyTrack.h +++ b/components/spotify/cspot/include/SpotifyTrack.h @@ -15,6 +15,7 @@ #include "NanoPBHelper.h" #include "protobuf/metadata.pb.h" #include +#include struct TrackInfo { std::string name; @@ -33,8 +34,8 @@ private: void trackInformationCallback(std::unique_ptr response, uint32_t position_ms, bool isPaused); void episodeInformationCallback(std::unique_ptr response, uint32_t position_ms, bool isPaused); void requestAudioKey(std::vector fileId, std::vector trackId, int32_t trackDuration, uint32_t position_ms, bool isPaused); - bool countryListContains(std::string countryList, std::string country); - bool canPlayTrack(); + bool countryListContains(char *countryList, char *country); + bool canPlayTrack(int altIndex); Track trackInfo; Episode episodeInfo; @@ -45,6 +46,7 @@ public: SpotifyTrack(std::shared_ptr manager, std::shared_ptr trackRef, uint32_t position_ms, bool isPaused); ~SpotifyTrack(); uint64_t reqSeqNum = -1; + std::atomic loaded = false; std::function loadedTrackCallback; std::unique_ptr audioStream; trackChangedCallback trackInfoReceived; diff --git a/components/spotify/cspot/include/TrackReference.h b/components/spotify/cspot/include/TrackReference.h index 0c7e8813..a5feb9a3 100644 --- a/components/spotify/cspot/include/TrackReference.h +++ b/components/spotify/cspot/include/TrackReference.h @@ -18,8 +18,6 @@ public: TrackReference(TrackRef *ref); ~TrackReference(); - TrackRef* ref; - std::vector gid; bool isEpisode = false; @@ -32,4 +30,4 @@ public: std::string getMercuryRequestUri(); }; -#endif \ No newline at end of file +#endif diff --git a/components/spotify/cspot/protobuf/mercury.options b/components/spotify/cspot/protobuf/mercury.options index 29da3dcb..e7c7718b 100644 --- a/components/spotify/cspot/protobuf/mercury.options +++ b/components/spotify/cspot/protobuf/mercury.options @@ -1,2 +1,2 @@ -Header.uri max_size:64, fixed_length:false -Header.method max_size:32, fixed_length:false +Header.uri max_size:256, fixed_length:false +Header.method max_size:64, fixed_length:false diff --git a/components/spotify/cspot/src/ApResolve.cpp b/components/spotify/cspot/src/ApResolve.cpp index 3282faa8..c28a4012 100644 --- a/components/spotify/cspot/src/ApResolve.cpp +++ b/components/spotify/cspot/src/ApResolve.cpp @@ -15,6 +15,8 @@ #include #include "Logger.h" #include +#include +#include ApResolve::ApResolve() {} @@ -76,19 +78,25 @@ std::string ApResolve::getApList() jsonData += cur; } - close(sockFd); + close(sockFd); return jsonData; } std::string ApResolve::fetchFirstApAddress() { + if (configMan->apOverride != "") + { + return configMan->apOverride; + } + // Fetch json body auto jsonData = getApList(); // Use cJSON to get first ap address auto root = cJSON_Parse(jsonData.c_str()); auto apList = cJSON_GetObjectItemCaseSensitive(root, "ap_list"); + auto firstAp = cJSON_GetArrayItem(apList, 0); auto data = std::string(firstAp->valuestring); diff --git a/components/spotify/cspot/src/MercuryManager.cpp b/components/spotify/cspot/src/MercuryManager.cpp index 87fd2ff9..c7daf05c 100644 --- a/components/spotify/cspot/src/MercuryManager.cpp +++ b/components/spotify/cspot/src/MercuryManager.cpp @@ -115,6 +115,7 @@ std::shared_ptr MercuryManager::fetchAudioChunk(std::vector this->session->shanConn->sendPacket(static_cast(MercuryType::AUDIO_CHUNK_REQUEST_COMMAND), buffer); // Used for broken connection detection +CSPOT_LOG(info, "requesting Chunk %hu", this->audioChunkSequence - 1); this->lastRequestTimestamp = this->timeProvider->getSyncedTimestamp(); return this->audioChunkManager->registerNewChunk(this->audioChunkSequence - 1, audioKey, startPos, endPos); } @@ -208,16 +209,19 @@ void MercuryManager::updateQueue() { if (queueSemaphore->twait() == 0) { if (this->queue.size() > 0) { - auto packet = std::move(this->queue[0]); + std::unique_ptr packet = std::move(this->queue[0]); this->queue.erase(this->queue.begin()); + if(packet == nullptr){ + return; + } CSPOT_LOG(debug, "Received packet with code %d of length %d", packet->command, packet->data.size()); switch (static_cast(packet->command)) { case MercuryType::COUNTRY_CODE_RESPONSE: { - countryCode = std::string(packet->data.begin(), packet->data.end()); - CSPOT_LOG(debug, "Received country code: %s", countryCode.c_str()); + memcpy(countryCode, packet->data.data(), 2); + CSPOT_LOG(debug, "Received country code: %.2s", countryCode); break; } case MercuryType::AUDIO_KEY_FAILURE_RESPONSE: diff --git a/components/spotify/cspot/src/Player.cpp b/components/spotify/cspot/src/Player.cpp index b7c59396..a863e78e 100644 --- a/components/spotify/cspot/src/Player.cpp +++ b/components/spotify/cspot/src/Player.cpp @@ -12,12 +12,24 @@ Player::Player(std::shared_ptr manager, std::shared_ptrcurrentTrack->audioStream->isPaused = true; + if (currentTrack != nullptr) + { + if (currentTrack->audioStream != nullptr) + { + this->currentTrack->audioStream->isPaused = true; + } + } } void Player::play() { - this->currentTrack->audioStream->isPaused = false; + if (currentTrack != nullptr) + { + if (currentTrack->audioStream != nullptr) + { + this->currentTrack->audioStream->isPaused = false; + } + } } void Player::setVolume(uint32_t volume) @@ -39,7 +51,13 @@ void Player::setVolume(uint32_t volume) void Player::seekMs(size_t positionMs) { - this->currentTrack->audioStream->seekMs(positionMs); + if (currentTrack != nullptr) + { + if (currentTrack->audioStream != nullptr) + { + this->currentTrack->audioStream->seekMs(positionMs); + } + } // VALGRIND_DO_LEAK_CHECK; } @@ -50,11 +68,11 @@ void Player::feedPCM(uint8_t *data, size_t len) if (this->audioSink->softwareVolumeControl) { int16_t* psample; - uint32_t pmax; + int32_t temp; psample = (int16_t*)(data); - for (int32_t i = 0; i < (len / 2); i++) + size_t half_len = len / 2; + for (uint32_t i = 0; i < half_len; i++) { - int32_t temp; // Offset data for unsigned sinks if (this->audioSink->usign) { @@ -78,27 +96,44 @@ void Player::runTask() this->isRunning = true; while (isRunning) { - if (this->trackQueue.wpop(currentTrack)) { + if(nextTrack != nullptr && nextTrack->loaded) + { + this->nextTrackMutex.lock(); + currentTrack = this->nextTrack; + this->nextTrack = nullptr; + this->nextTrackMutex.unlock(); + currentTrack->audioStream->startPlaybackLoop(pcmOut, 4096 / 4); currentTrack->loadedTrackCallback = nullptr; currentTrack->audioStream->streamFinishedCallback = nullptr; currentTrack->audioStream->audioSink = nullptr; currentTrack->audioStream->pcmCallback = nullptr; - } else { - usleep(100); + + delete currentTrack; + currentTrack = nullptr; } + else + { + usleep(10000); + } + } free(pcmOut); } void Player::stop() { + CSPOT_LOG(info, "Trying to stop"); this->isRunning = false; - CSPOT_LOG(info, "Stopping player"); - this->trackQueue.clear(); cancelCurrentTrack(); - CSPOT_LOG(info, "Track cancelled"); std::scoped_lock lock(this->runningMutex); - CSPOT_LOG(info, "Done"); + if(this->nextTrack != nullptr) + { + delete this->nextTrack; + } + this->isRunning = false; + CSPOT_LOG(info, "Track cancelled"); + cancelCurrentTrack(); + CSPOT_LOG(info, "Stopping player"); } void Player::cancelCurrentTrack() @@ -115,21 +150,32 @@ void Player::cancelCurrentTrack() void Player::handleLoad(std::shared_ptr trackReference, std::function& trackLoadedCallback, uint32_t position_ms, bool isPaused) { std::lock_guard guard(loadTrackMutex); - cancelCurrentTrack(); pcmDataCallback framesCallback = [=](uint8_t *frames, size_t len) { this->feedPCM(frames, len); }; - auto loadedLambda = trackLoadedCallback; + this->nextTrackMutex.lock(); + if(this->nextTrack != nullptr) + { + delete this->nextTrack; + this->nextTrack = nullptr; + } - auto track = std::make_shared(this->manager, trackReference, position_ms, isPaused); - track->trackInfoReceived = this->trackChanged; - track->loadedTrackCallback = [this, track, framesCallback, loadedLambda]() { - loadedLambda(); - track->audioStream->streamFinishedCallback = this->endOfFileCallback; - track->audioStream->audioSink = this->audioSink; - track->audioStream->pcmCallback = framesCallback; - this->trackQueue.push(track); + this->nextTrack = new SpotifyTrack(this->manager, trackReference, position_ms, isPaused); + + this->nextTrack->trackInfoReceived = this->trackChanged; + this->nextTrack->loadedTrackCallback = [this, framesCallback, trackLoadedCallback]() { + trackLoadedCallback(); + + this->nextTrackMutex.lock(); + this->nextTrack->audioStream->streamFinishedCallback = this->endOfFileCallback; + this->nextTrack->audioStream->audioSink = this->audioSink; + this->nextTrack->audioStream->pcmCallback = framesCallback; + this->nextTrack->loaded = true; + this->nextTrackMutex.unlock(); + + cancelCurrentTrack(); }; + this->nextTrackMutex.unlock(); } diff --git a/components/spotify/cspot/src/PlayerState.cpp b/components/spotify/cspot/src/PlayerState.cpp index 60d4b85f..2fc62fca 100644 --- a/components/spotify/cspot/src/PlayerState.cpp +++ b/components/spotify/cspot/src/PlayerState.cpp @@ -136,37 +136,49 @@ void PlayerState::updatePositionMs(uint32_t position) innerFrame.state.position_measured_at = timeProvider->getSyncedTimestamp(); } +#define FREE(ptr) { free(ptr); ptr = NULL; } +#define STRDUP(dst, src) if(src != NULL) { dst = strdup(src); } else { FREE(dst); } // strdup null pointer safe + void PlayerState::updateTracks() { CSPOT_LOG(info, "---- Track count %d", remoteFrame.state.track_count); + CSPOT_LOG(info, "---- Inner track count %d", innerFrame.state.track_count); // 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); + FREE(innerFrame.state.track[i].gid); + FREE(innerFrame.state.track[i].uri); + FREE(innerFrame.state.track[i].context); } } - + // 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)); + if(i >= innerFrame.state.track_count) { + innerFrame.state.track[i].gid = NULL; + innerFrame.state.track[i].uri = NULL; + innerFrame.state.track[i].context = NULL; + } + + if(remoteFrame.state.track[i].gid != NULL) + { + uint16_t gid_size = remoteFrame.state.track[i].gid->size; + innerFrame.state.track[i].gid = (pb_bytes_array_t *) realloc(innerFrame.state.track[i].gid, 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; } - 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; + + STRDUP(innerFrame.state.track[i].uri, remoteFrame.state.track[i].uri); + STRDUP(innerFrame.state.track[i].context, remoteFrame.state.track[i].context); } innerFrame.state.context_uri = (char *) realloc(innerFrame.state.context_uri, diff --git a/components/spotify/cspot/src/Session.cpp b/components/spotify/cspot/src/Session.cpp index 638ef7f3..e7ddb467 100644 --- a/components/spotify/cspot/src/Session.cpp +++ b/components/spotify/cspot/src/Session.cpp @@ -168,7 +168,7 @@ std::vector Session::sendClientHelloRequest() clientHello.login_crypto_hello.diffie_hellman.gc); clientHello.login_crypto_hello.diffie_hellman.server_keys_known = 1; - clientHello.build_info.product = Product_PRODUCT_PARTNER; + clientHello.build_info.product = Product_PRODUCT_CLIENT; clientHello.build_info.platform = Platform2_PLATFORM_LINUX_X86; clientHello.build_info.version = SPOTIFY_VERSION; clientHello.feature_set.autoupdate2 = true; diff --git a/components/spotify/cspot/src/SpircController.cpp b/components/spotify/cspot/src/SpircController.cpp index de4eb538..f490048d 100644 --- a/components/spotify/cspot/src/SpircController.cpp +++ b/components/spotify/cspot/src/SpircController.cpp @@ -166,6 +166,8 @@ void SpircController::handleFrame(std::vector &data) { } case MessageType_kMessageTypeReplace: { CSPOT_LOG(debug, "Got replace frame!"); + state->updateTracks(); + this->notify(); break; } case MessageType_kMessageTypeShuffle: { @@ -191,6 +193,7 @@ void SpircController::loadTrack(uint32_t position_ms, bool isPaused) { std::function loadedLambda = [=]() { // Loading finished, notify that playback started setPause(isPaused, false); + sendEvent(CSpotEventType::PLAYBACK_START); }; player->handleLoad(state->getCurrentTrack(), loadedLambda, position_ms, diff --git a/components/spotify/cspot/src/SpotifyTrack.cpp b/components/spotify/cspot/src/SpotifyTrack.cpp index 6003100a..477a09ba 100644 --- a/components/spotify/cspot/src/SpotifyTrack.cpp +++ b/components/spotify/cspot/src/SpotifyTrack.cpp @@ -39,11 +39,12 @@ SpotifyTrack::~SpotifyTrack() pb_release(Episode_fields, &this->episodeInfo); } -bool SpotifyTrack::countryListContains(std::string countryList, std::string country) +bool SpotifyTrack::countryListContains(char *countryList, char *country) { - for (int x = 0; x < countryList.size(); x += 2) + uint16_t countryList_length = strlen(countryList); + for (int x = 0; x < countryList_length; x += 2) { - if (countryList.substr(x, 2) == country) + if (countryList[x] == country[0] && countryList[x + 1] == country[1]) { return true; } @@ -51,21 +52,38 @@ bool SpotifyTrack::countryListContains(std::string countryList, std::string coun return false; } -bool SpotifyTrack::canPlayTrack() +bool SpotifyTrack::canPlayTrack(int altIndex) { - for (int x = 0; x < trackInfo.restriction_count; x++) + if(altIndex < 0) { - if (trackInfo.restriction[x].countries_allowed != nullptr) + for (int x = 0; x < trackInfo.restriction_count; x++) { - return countryListContains(std::string(trackInfo.restriction[x].countries_allowed), manager->countryCode); - } + if (trackInfo.restriction[x].countries_allowed != nullptr) + { + return countryListContains(trackInfo.restriction[x].countries_allowed, manager->countryCode); + } - if (trackInfo.restriction[x].countries_forbidden != nullptr) - { - return !countryListContains(std::string(trackInfo.restriction[x].countries_forbidden), manager->countryCode); + if (trackInfo.restriction[x].countries_forbidden != nullptr) + { + return !countryListContains(trackInfo.restriction[x].countries_forbidden, manager->countryCode); + } } } + else + { + for (int x = 0; x < trackInfo.alternative[altIndex].restriction_count; x++) + { + if (trackInfo.alternative[altIndex].restriction[x].countries_allowed != nullptr) + { + return countryListContains(trackInfo.alternative[altIndex].restriction[x].countries_allowed, manager->countryCode); + } + if (trackInfo.alternative[altIndex].restriction[x].countries_forbidden != nullptr) + { + return !countryListContains(trackInfo.alternative[altIndex].restriction[x].countries_forbidden, manager->countryCode); + } + } + } return true; } @@ -81,16 +99,12 @@ void SpotifyTrack::trackInformationCallback(std::unique_ptr res CSPOT_LOG(info, "Track name: %s", trackInfo.name); CSPOT_LOG(info, "Track duration: %d", trackInfo.duration); CSPOT_LOG(debug, "trackInfo.restriction.size() = %d", trackInfo.restriction_count); - int altIndex = 0; - while (!canPlayTrack()) + + int altIndex = -1; + while (!canPlayTrack(altIndex)) { - std::swap(trackInfo.restriction, trackInfo.alternative[altIndex].restriction); - std::swap(trackInfo.restriction_count, trackInfo.alternative[altIndex].restriction_count); - 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++; + CSPOT_LOG(info, "Trying alternative %d", altIndex); if(altIndex > trackInfo.alternative_count) { // no alternatives for song @@ -98,15 +112,31 @@ void SpotifyTrack::trackInformationCallback(std::unique_ptr res } } - auto trackId = pbArrayToVector(trackInfo.gid); + std::vector trackId; this->fileId = std::vector(); - for (int x = 0; x < trackInfo.file_count; x++) + if(altIndex < 0) { - if (trackInfo.file[x].format == configMan->format) + trackId = pbArrayToVector(trackInfo.gid); + for (int x = 0; x < trackInfo.file_count; x++) { - this->fileId = pbArrayToVector(trackInfo.file[x].file_id); - break; // If file found stop searching + if (trackInfo.file[x].format == configMan->format) + { + this->fileId = pbArrayToVector(trackInfo.file[x].file_id); + break; // If file found stop searching + } + } + } + else + { + trackId = pbArrayToVector(trackInfo.alternative[altIndex].gid); + for (int x = 0; x < trackInfo.alternative[altIndex].file_count; x++) + { + if (trackInfo.alternative[altIndex].file[x].format == configMan->format) + { + this->fileId = pbArrayToVector(trackInfo.alternative[altIndex].file[x].file_id); + break; // If file found stop searching + } } } diff --git a/components/spotify/cspot/src/ZeroconfAuthenticator.cpp b/components/spotify/cspot/src/ZeroconfAuthenticator.cpp index 78fdc9a3..e4500df4 100644 --- a/components/spotify/cspot/src/ZeroconfAuthenticator.cpp +++ b/components/spotify/cspot/src/ZeroconfAuthenticator.cpp @@ -8,7 +8,7 @@ #include "ConfigJSON.h" // provide weak deviceId (see ConstantParameters.h) -char deviceId[] __attribute__((weak)) = "162137fd329622137a14901634264e6f332e2422"; +char deviceId[] __attribute__((weak)) = "142137fd329622137a14901634264e6f332e2411"; ZeroconfAuthenticator::ZeroconfAuthenticator(authCallback callback, std::shared_ptr httpServer) { this->gotBlobCallback = callback; diff --git a/components/squeezelite/decode_external.c b/components/squeezelite/decode_external.c index a9924959..6ef2c7a1 100644 --- a/components/squeezelite/decode_external.c +++ b/components/squeezelite/decode_external.c @@ -364,6 +364,8 @@ static bool cspot_cmd_handler(cspot_event_t cmd, va_list args) LOG_INFO("CSpot disconnected"); break; case CSPOT_TRACK: + _buf_flush(outputbuf); + abort_sink = true; LOG_INFO("CSpot sink new track rate %d", output.next_sample_rate); break; case CSPOT_PLAY: