mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-09 13:07:03 +03:00
update CSpot + clear audio buffer when changing track
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
#include <fstream>
|
||||
#include "Logger.h"
|
||||
#include <cJSON.h>
|
||||
#include <ConfigJSON.h>
|
||||
#include <random>
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ std::shared_ptr<AudioChunk> MercuryManager::fetchAudioChunk(std::vector<uint8_t>
|
||||
this->session->shanConn->sendPacket(static_cast<uint8_t>(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> 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<MercuryType>(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:
|
||||
|
||||
@@ -12,12 +12,24 @@ Player::Player(std::shared_ptr<MercuryManager> manager, std::shared_ptr<AudioSin
|
||||
|
||||
void Player::pause()
|
||||
{
|
||||
this->currentTrack->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> trackReference, std::function<void()>& trackLoadedCallback, uint32_t position_ms, bool isPaused)
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<SpotifyTrack>(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();
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -168,7 +168,7 @@ std::vector<uint8_t> 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;
|
||||
|
||||
@@ -166,6 +166,8 @@ void SpircController::handleFrame(std::vector<uint8_t> &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<void()> loadedLambda = [=]() {
|
||||
// Loading finished, notify that playback started
|
||||
setPause(isPaused, false);
|
||||
sendEvent(CSpotEventType::PLAYBACK_START);
|
||||
};
|
||||
|
||||
player->handleLoad(state->getCurrentTrack(), loadedLambda, position_ms,
|
||||
|
||||
@@ -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<MercuryResponse> 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<MercuryResponse> res
|
||||
}
|
||||
}
|
||||
|
||||
auto trackId = pbArrayToVector(trackInfo.gid);
|
||||
std::vector<uint8_t> trackId;
|
||||
this->fileId = std::vector<uint8_t>();
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<bell::BaseHTTPServer> httpServer) {
|
||||
this->gotBlobCallback = callback;
|
||||
|
||||
Reference in New Issue
Block a user