mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-08 12:37:01 +03:00
update CSpot + clear audio buffer when changing track
This commit is contained in:
@@ -17,6 +17,59 @@ private:
|
|||||||
public:
|
public:
|
||||||
BiquadFilter(){};
|
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)
|
void generateHighShelfCoEffs(float f, float gain, float q)
|
||||||
{
|
{
|
||||||
if (q <= 0.0001)
|
if (q <= 0.0001)
|
||||||
@@ -106,6 +159,119 @@ public:
|
|||||||
coeffs[4] = a2 / a0;
|
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)
|
void processSamples(float *input, int numSamples)
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(processMutex);
|
std::scoped_lock lock(processMutex);
|
||||||
|
|||||||
@@ -67,6 +67,6 @@ long long bell::BinaryReader::readLong() {
|
|||||||
long low = readInt();
|
long low = readInt();
|
||||||
|
|
||||||
return static_cast<long long>(
|
return static_cast<long long>(
|
||||||
(high << 32) | low );
|
((long long) high << 32) | low );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ void CryptoOpenSSL::aesECBdecrypt(const std::vector<uint8_t>& key, std::vector<u
|
|||||||
int len = 0;
|
int len = 0;
|
||||||
|
|
||||||
EVP_DecryptInit_ex(ctx, EVP_aes_192_ecb(), NULL, key.data(), NULL);
|
EVP_DecryptInit_ex(ctx, EVP_aes_192_ecb(), NULL, key.data(), NULL);
|
||||||
|
EVP_CIPHER_CTX_set_padding(ctx, 0); // disable padding
|
||||||
EVP_DecryptUpdate(ctx, data.data(), &len, data.data(), data.size());
|
EVP_DecryptUpdate(ctx, data.data(), &len, data.data(), data.size());
|
||||||
EVP_DecryptFinal_ex(ctx, data.data() + len, &len);
|
EVP_DecryptFinal_ex(ctx, data.data() + len, &len);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "HTTPServer.h"
|
#include "HTTPServer.h"
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
bell::HTTPServer::HTTPServer(int serverPort) { this->serverPort = serverPort; }
|
bell::HTTPServer::HTTPServer(int serverPort) { this->serverPort = serverPort; }
|
||||||
|
|
||||||
@@ -65,8 +66,7 @@ void bell::HTTPServer::registerHandler(RequestType requestType,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void bell::HTTPServer::listen() {
|
void bell::HTTPServer::listen() {
|
||||||
BELL_LOG(info, "http", "Starting server at port %d",
|
BELL_LOG(info, "http", "Starting server at port %d", this->serverPort);
|
||||||
this->serverPort);
|
|
||||||
|
|
||||||
// setup address
|
// setup address
|
||||||
struct addrinfo hints, *server;
|
struct addrinfo hints, *server;
|
||||||
@@ -82,9 +82,20 @@ void bell::HTTPServer::listen() {
|
|||||||
socklen_t incomingSockSize;
|
socklen_t incomingSockSize;
|
||||||
int i;
|
int i;
|
||||||
int yes = true;
|
int yes = true;
|
||||||
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
|
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) {
|
||||||
bind(sockfd, server->ai_addr, server->ai_addrlen);
|
throw std::runtime_error("setsockopt failed: " +
|
||||||
::listen(sockfd, 10);
|
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_ZERO(&activeFdSet);
|
||||||
FD_SET(sockfd, &activeFdSet);
|
FD_SET(sockfd, &activeFdSet);
|
||||||
@@ -172,7 +183,8 @@ void bell::HTTPServer::readFromClient(int clientFd) {
|
|||||||
std::stoi(line.substr(16, line.size() - 1));
|
std::stoi(line.substr(16, line.size() - 1));
|
||||||
}
|
}
|
||||||
// detect hostname for captive portal
|
// 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;
|
conn.isCaptivePortal = true;
|
||||||
BELL_LOG(info, "http", "Captive portal request detected");
|
BELL_LOG(info, "http", "Captive portal request detected");
|
||||||
}
|
}
|
||||||
@@ -182,11 +194,11 @@ void bell::HTTPServer::readFromClient(int clientFd) {
|
|||||||
goto READBODY;
|
goto READBODY;
|
||||||
} else {
|
} else {
|
||||||
if (!conn.isCaptivePortal) {
|
if (!conn.isCaptivePortal) {
|
||||||
findAndHandleRoute(conn.httpMethod, conn.currentLine, clientFd);
|
findAndHandleRoute(conn.httpMethod,
|
||||||
|
conn.currentLine, clientFd);
|
||||||
} else {
|
} else {
|
||||||
this->redirectCaptivePortal(clientFd);
|
this->redirectCaptivePortal(clientFd);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -299,7 +311,7 @@ void bell::HTTPServer::redirectCaptivePortal(int connectionFd) {
|
|||||||
this->closeConnection(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::lock_guard lock(this->responseMutex);
|
||||||
std::stringstream stream;
|
std::stringstream stream;
|
||||||
stream << "HTTP/1.1 301 Moved Permanently\r\n";
|
stream << "HTTP/1.1 301 Moved Permanently\r\n";
|
||||||
|
|||||||
@@ -85,7 +85,11 @@ bool SPDIFAudioSink::setParams(uint32_t sampleRate, uint8_t channelCount, uint8_
|
|||||||
|
|
||||||
i2s_config_t i2s_config = {
|
i2s_config_t i2s_config = {
|
||||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
|
.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),
|
.bits_per_sample = (i2s_bits_per_sample_t)(bitDepth * 2),
|
||||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
|
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
|
||||||
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
|
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ public:
|
|||||||
|
|
||||||
uint16_t volume;
|
uint16_t volume;
|
||||||
std::string deviceName;
|
std::string deviceName;
|
||||||
|
std::string apOverride;
|
||||||
AudioFormat format;
|
AudioFormat format;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
extern char deviceId[];
|
extern char deviceId[];
|
||||||
|
|
||||||
// Hardcoded information sent to spotify servers
|
// Hardcoded information sent to spotify servers
|
||||||
const char * const informationString = "cspot";
|
const char * const informationString = "cspot-player";
|
||||||
const char * const brandName = "corn";
|
const char * const brandName = "cspot";
|
||||||
const char * const versionString = "cspot-1.0";
|
const char * const versionString = "cspot-1.1";
|
||||||
const char * const protocolVersion = "2.7.1";
|
const char * const protocolVersion = "2.7.1";
|
||||||
const char * const defaultDeviceName = "CSpot";
|
const char * const defaultDeviceName = "CSpot";
|
||||||
const char * const swVersion = "1.0.0";
|
const char * const swVersion = "1.0.0";
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ private:
|
|||||||
std::unique_ptr<WrappedSemaphore> queueSemaphore;
|
std::unique_ptr<WrappedSemaphore> queueSemaphore;
|
||||||
unsigned long long lastRequestTimestamp = -1;
|
unsigned long long lastRequestTimestamp = -1;
|
||||||
unsigned long long lastPingTimestamp = -1;
|
unsigned long long lastPingTimestamp = -1;
|
||||||
std::atomic<bool> isRunning = false;
|
|
||||||
uint64_t sequenceId;
|
uint64_t sequenceId;
|
||||||
uint32_t audioKeySequence;
|
uint32_t audioKeySequence;
|
||||||
audioKeyCallback keyCallback;
|
audioKeyCallback keyCallback;
|
||||||
@@ -78,10 +77,11 @@ private:
|
|||||||
public:
|
public:
|
||||||
MercuryManager(std::unique_ptr<Session> session);
|
MercuryManager(std::unique_ptr<Session> session);
|
||||||
~MercuryManager();
|
~MercuryManager();
|
||||||
|
std::atomic<bool> isRunning = false;
|
||||||
voidCallback reconnectedCallback;
|
voidCallback reconnectedCallback;
|
||||||
uint16_t audioChunkSequence;
|
uint16_t audioChunkSequence;
|
||||||
std::shared_ptr<TimeProvider> timeProvider;
|
std::shared_ptr<TimeProvider> timeProvider;
|
||||||
std::string countryCode;
|
char countryCode[2];
|
||||||
|
|
||||||
bool timeoutHandler();
|
bool timeoutHandler();
|
||||||
uint64_t execute(MercuryType method, std::string uri, mercuryCallback &callback, mercuryCallback &subscription, mercuryParts &payload);
|
uint64_t execute(MercuryType method, std::string uri, mercuryCallback &callback, mercuryCallback &subscription, mercuryParts &payload);
|
||||||
|
|||||||
@@ -14,17 +14,17 @@
|
|||||||
#include "SpotifyTrack.h"
|
#include "SpotifyTrack.h"
|
||||||
#include "AudioSink.h"
|
#include "AudioSink.h"
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include "Queue.h"
|
|
||||||
#include "Task.h"
|
#include "Task.h"
|
||||||
|
|
||||||
class Player : public bell::Task {
|
class Player : public bell::Task {
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<MercuryManager> manager;
|
std::shared_ptr<MercuryManager> manager;
|
||||||
std::shared_ptr<SpotifyTrack> currentTrack = nullptr;
|
SpotifyTrack *currentTrack = nullptr;
|
||||||
|
SpotifyTrack *nextTrack = nullptr;
|
||||||
std::shared_ptr<AudioSink> audioSink;
|
std::shared_ptr<AudioSink> audioSink;
|
||||||
std::mutex loadTrackMutex;
|
std::mutex loadTrackMutex;
|
||||||
// @TODO: Use some actual structure here
|
WrappedMutex nextTrackMutex;
|
||||||
bell::Queue<std::shared_ptr<SpotifyTrack>> trackQueue;
|
WrappedMutex currentTrackMutex;
|
||||||
void runTask();
|
void runTask();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ enum class CSpotEventType {
|
|||||||
PREV,
|
PREV,
|
||||||
SEEK,
|
SEEK,
|
||||||
LOAD,
|
LOAD,
|
||||||
|
PLAYBACK_START
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CSpotEvent {
|
struct CSpotEvent {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include "NanoPBHelper.h"
|
#include "NanoPBHelper.h"
|
||||||
#include "protobuf/metadata.pb.h"
|
#include "protobuf/metadata.pb.h"
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
struct TrackInfo {
|
struct TrackInfo {
|
||||||
std::string name;
|
std::string name;
|
||||||
@@ -33,8 +34,8 @@ private:
|
|||||||
void trackInformationCallback(std::unique_ptr<MercuryResponse> response, uint32_t position_ms, bool isPaused);
|
void trackInformationCallback(std::unique_ptr<MercuryResponse> response, uint32_t position_ms, bool isPaused);
|
||||||
void episodeInformationCallback(std::unique_ptr<MercuryResponse> response, uint32_t position_ms, bool isPaused);
|
void episodeInformationCallback(std::unique_ptr<MercuryResponse> response, uint32_t position_ms, bool isPaused);
|
||||||
void requestAudioKey(std::vector<uint8_t> fileId, std::vector<uint8_t> trackId, int32_t trackDuration, uint32_t position_ms, bool isPaused);
|
void requestAudioKey(std::vector<uint8_t> fileId, std::vector<uint8_t> trackId, int32_t trackDuration, uint32_t position_ms, bool isPaused);
|
||||||
bool countryListContains(std::string countryList, std::string country);
|
bool countryListContains(char *countryList, char *country);
|
||||||
bool canPlayTrack();
|
bool canPlayTrack(int altIndex);
|
||||||
Track trackInfo;
|
Track trackInfo;
|
||||||
Episode episodeInfo;
|
Episode episodeInfo;
|
||||||
|
|
||||||
@@ -45,6 +46,7 @@ public:
|
|||||||
SpotifyTrack(std::shared_ptr<MercuryManager> manager, std::shared_ptr<TrackReference> trackRef, uint32_t position_ms, bool isPaused);
|
SpotifyTrack(std::shared_ptr<MercuryManager> manager, std::shared_ptr<TrackReference> trackRef, uint32_t position_ms, bool isPaused);
|
||||||
~SpotifyTrack();
|
~SpotifyTrack();
|
||||||
uint64_t reqSeqNum = -1;
|
uint64_t reqSeqNum = -1;
|
||||||
|
std::atomic<bool> loaded = false;
|
||||||
std::function<void()> loadedTrackCallback;
|
std::function<void()> loadedTrackCallback;
|
||||||
std::unique_ptr<ChunkedAudioStream> audioStream;
|
std::unique_ptr<ChunkedAudioStream> audioStream;
|
||||||
trackChangedCallback trackInfoReceived;
|
trackChangedCallback trackInfoReceived;
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ public:
|
|||||||
TrackReference(TrackRef *ref);
|
TrackReference(TrackRef *ref);
|
||||||
~TrackReference();
|
~TrackReference();
|
||||||
|
|
||||||
TrackRef* ref;
|
|
||||||
|
|
||||||
std::vector<uint8_t> gid;
|
std::vector<uint8_t> gid;
|
||||||
|
|
||||||
bool isEpisode = false;
|
bool isEpisode = false;
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
Header.uri max_size:64, fixed_length:false
|
Header.uri max_size:256, fixed_length:false
|
||||||
Header.method max_size:32, fixed_length:false
|
Header.method max_size:64, fixed_length:false
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include <cJSON.h>
|
#include <cJSON.h>
|
||||||
|
#include <ConfigJSON.h>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
ApResolve::ApResolve() {}
|
ApResolve::ApResolve() {}
|
||||||
|
|
||||||
@@ -83,12 +85,18 @@ std::string ApResolve::getApList()
|
|||||||
|
|
||||||
std::string ApResolve::fetchFirstApAddress()
|
std::string ApResolve::fetchFirstApAddress()
|
||||||
{
|
{
|
||||||
|
if (configMan->apOverride != "")
|
||||||
|
{
|
||||||
|
return configMan->apOverride;
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch json body
|
// Fetch json body
|
||||||
auto jsonData = getApList();
|
auto jsonData = getApList();
|
||||||
|
|
||||||
// Use cJSON to get first ap address
|
// Use cJSON to get first ap address
|
||||||
auto root = cJSON_Parse(jsonData.c_str());
|
auto root = cJSON_Parse(jsonData.c_str());
|
||||||
auto apList = cJSON_GetObjectItemCaseSensitive(root, "ap_list");
|
auto apList = cJSON_GetObjectItemCaseSensitive(root, "ap_list");
|
||||||
|
|
||||||
auto firstAp = cJSON_GetArrayItem(apList, 0);
|
auto firstAp = cJSON_GetArrayItem(apList, 0);
|
||||||
auto data = std::string(firstAp->valuestring);
|
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);
|
this->session->shanConn->sendPacket(static_cast<uint8_t>(MercuryType::AUDIO_CHUNK_REQUEST_COMMAND), buffer);
|
||||||
|
|
||||||
// Used for broken connection detection
|
// Used for broken connection detection
|
||||||
|
CSPOT_LOG(info, "requesting Chunk %hu", this->audioChunkSequence - 1);
|
||||||
this->lastRequestTimestamp = this->timeProvider->getSyncedTimestamp();
|
this->lastRequestTimestamp = this->timeProvider->getSyncedTimestamp();
|
||||||
return this->audioChunkManager->registerNewChunk(this->audioChunkSequence - 1, audioKey, startPos, endPos);
|
return this->audioChunkManager->registerNewChunk(this->audioChunkSequence - 1, audioKey, startPos, endPos);
|
||||||
}
|
}
|
||||||
@@ -208,16 +209,19 @@ void MercuryManager::updateQueue() {
|
|||||||
if (queueSemaphore->twait() == 0) {
|
if (queueSemaphore->twait() == 0) {
|
||||||
if (this->queue.size() > 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());
|
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());
|
CSPOT_LOG(debug, "Received packet with code %d of length %d", packet->command, packet->data.size());
|
||||||
switch (static_cast<MercuryType>(packet->command))
|
switch (static_cast<MercuryType>(packet->command))
|
||||||
{
|
{
|
||||||
case MercuryType::COUNTRY_CODE_RESPONSE:
|
case MercuryType::COUNTRY_CODE_RESPONSE:
|
||||||
{
|
{
|
||||||
|
|
||||||
countryCode = std::string(packet->data.begin(), packet->data.end());
|
memcpy(countryCode, packet->data.data(), 2);
|
||||||
CSPOT_LOG(debug, "Received country code: %s", countryCode.c_str());
|
CSPOT_LOG(debug, "Received country code: %.2s", countryCode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MercuryType::AUDIO_KEY_FAILURE_RESPONSE:
|
case MercuryType::AUDIO_KEY_FAILURE_RESPONSE:
|
||||||
|
|||||||
@@ -12,12 +12,24 @@ Player::Player(std::shared_ptr<MercuryManager> manager, std::shared_ptr<AudioSin
|
|||||||
|
|
||||||
void Player::pause()
|
void Player::pause()
|
||||||
{
|
{
|
||||||
|
if (currentTrack != nullptr)
|
||||||
|
{
|
||||||
|
if (currentTrack->audioStream != nullptr)
|
||||||
|
{
|
||||||
this->currentTrack->audioStream->isPaused = true;
|
this->currentTrack->audioStream->isPaused = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::play()
|
void Player::play()
|
||||||
{
|
{
|
||||||
|
if (currentTrack != nullptr)
|
||||||
|
{
|
||||||
|
if (currentTrack->audioStream != nullptr)
|
||||||
|
{
|
||||||
this->currentTrack->audioStream->isPaused = false;
|
this->currentTrack->audioStream->isPaused = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::setVolume(uint32_t volume)
|
void Player::setVolume(uint32_t volume)
|
||||||
@@ -39,7 +51,13 @@ void Player::setVolume(uint32_t volume)
|
|||||||
|
|
||||||
void Player::seekMs(size_t positionMs)
|
void Player::seekMs(size_t positionMs)
|
||||||
{
|
{
|
||||||
|
if (currentTrack != nullptr)
|
||||||
|
{
|
||||||
|
if (currentTrack->audioStream != nullptr)
|
||||||
|
{
|
||||||
this->currentTrack->audioStream->seekMs(positionMs);
|
this->currentTrack->audioStream->seekMs(positionMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
// VALGRIND_DO_LEAK_CHECK;
|
// VALGRIND_DO_LEAK_CHECK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,11 +68,11 @@ void Player::feedPCM(uint8_t *data, size_t len)
|
|||||||
if (this->audioSink->softwareVolumeControl)
|
if (this->audioSink->softwareVolumeControl)
|
||||||
{
|
{
|
||||||
int16_t* psample;
|
int16_t* psample;
|
||||||
uint32_t pmax;
|
|
||||||
psample = (int16_t*)(data);
|
|
||||||
for (int32_t i = 0; i < (len / 2); i++)
|
|
||||||
{
|
|
||||||
int32_t temp;
|
int32_t temp;
|
||||||
|
psample = (int16_t*)(data);
|
||||||
|
size_t half_len = len / 2;
|
||||||
|
for (uint32_t i = 0; i < half_len; i++)
|
||||||
|
{
|
||||||
// Offset data for unsigned sinks
|
// Offset data for unsigned sinks
|
||||||
if (this->audioSink->usign)
|
if (this->audioSink->usign)
|
||||||
{
|
{
|
||||||
@@ -78,27 +96,44 @@ void Player::runTask()
|
|||||||
this->isRunning = true;
|
this->isRunning = true;
|
||||||
while (isRunning)
|
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->audioStream->startPlaybackLoop(pcmOut, 4096 / 4);
|
||||||
currentTrack->loadedTrackCallback = nullptr;
|
currentTrack->loadedTrackCallback = nullptr;
|
||||||
currentTrack->audioStream->streamFinishedCallback = nullptr;
|
currentTrack->audioStream->streamFinishedCallback = nullptr;
|
||||||
currentTrack->audioStream->audioSink = nullptr;
|
currentTrack->audioStream->audioSink = nullptr;
|
||||||
currentTrack->audioStream->pcmCallback = nullptr;
|
currentTrack->audioStream->pcmCallback = nullptr;
|
||||||
} else {
|
|
||||||
usleep(100);
|
delete currentTrack;
|
||||||
|
currentTrack = nullptr;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
usleep(10000);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
free(pcmOut);
|
free(pcmOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::stop() {
|
void Player::stop() {
|
||||||
|
CSPOT_LOG(info, "Trying to stop");
|
||||||
this->isRunning = false;
|
this->isRunning = false;
|
||||||
CSPOT_LOG(info, "Stopping player");
|
|
||||||
this->trackQueue.clear();
|
|
||||||
cancelCurrentTrack();
|
cancelCurrentTrack();
|
||||||
CSPOT_LOG(info, "Track cancelled");
|
|
||||||
std::scoped_lock lock(this->runningMutex);
|
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()
|
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)
|
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);
|
std::lock_guard<std::mutex> guard(loadTrackMutex);
|
||||||
cancelCurrentTrack();
|
|
||||||
|
|
||||||
pcmDataCallback framesCallback = [=](uint8_t *frames, size_t len) {
|
pcmDataCallback framesCallback = [=](uint8_t *frames, size_t len) {
|
||||||
this->feedPCM(frames, 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);
|
this->nextTrack = new SpotifyTrack(this->manager, trackReference, position_ms, isPaused);
|
||||||
track->trackInfoReceived = this->trackChanged;
|
|
||||||
track->loadedTrackCallback = [this, track, framesCallback, loadedLambda]() {
|
this->nextTrack->trackInfoReceived = this->trackChanged;
|
||||||
loadedLambda();
|
this->nextTrack->loadedTrackCallback = [this, framesCallback, trackLoadedCallback]() {
|
||||||
track->audioStream->streamFinishedCallback = this->endOfFileCallback;
|
trackLoadedCallback();
|
||||||
track->audioStream->audioSink = this->audioSink;
|
|
||||||
track->audioStream->pcmCallback = framesCallback;
|
this->nextTrackMutex.lock();
|
||||||
this->trackQueue.push(track);
|
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,16 +136,22 @@ void PlayerState::updatePositionMs(uint32_t position)
|
|||||||
innerFrame.state.position_measured_at = timeProvider->getSyncedTimestamp();
|
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()
|
void PlayerState::updateTracks()
|
||||||
{
|
{
|
||||||
CSPOT_LOG(info, "---- Track count %d", remoteFrame.state.track_count);
|
CSPOT_LOG(info, "---- Track count %d", remoteFrame.state.track_count);
|
||||||
|
CSPOT_LOG(info, "---- Inner track count %d", innerFrame.state.track_count);
|
||||||
|
|
||||||
// free unused tracks
|
// free unused tracks
|
||||||
if(innerFrame.state.track_count > remoteFrame.state.track_count)
|
if(innerFrame.state.track_count > remoteFrame.state.track_count)
|
||||||
{
|
{
|
||||||
for(uint16_t i = remoteFrame.state.track_count; i < innerFrame.state.track_count; ++i)
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,21 +160,27 @@ void PlayerState::updateTracks()
|
|||||||
|
|
||||||
for(uint16_t i = 0; i < remoteFrame.state.track_count; ++i)
|
for(uint16_t i = 0; i < remoteFrame.state.track_count; ++i)
|
||||||
{
|
{
|
||||||
uint16_t gid_size = remoteFrame.state.track[i].gid->size;
|
if(i >= innerFrame.state.track_count) {
|
||||||
// allocate if need more tracks
|
innerFrame.state.track[i].gid = NULL;
|
||||||
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].uri = NULL;
|
||||||
innerFrame.state.track[i].context = 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;
|
||||||
|
}
|
||||||
|
innerFrame.state.track[i].has_queued = remoteFrame.state.track[i].has_queued;
|
||||||
|
innerFrame.state.track[i].queued = remoteFrame.state.track[i].queued;
|
||||||
|
|
||||||
|
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,
|
innerFrame.state.context_uri = (char *) realloc(innerFrame.state.context_uri,
|
||||||
strlen(remoteFrame.state.context_uri) + 1);
|
strlen(remoteFrame.state.context_uri) + 1);
|
||||||
strcpy(innerFrame.state.context_uri, remoteFrame.state.context_uri);
|
strcpy(innerFrame.state.context_uri, remoteFrame.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.gc);
|
||||||
|
|
||||||
clientHello.login_crypto_hello.diffie_hellman.server_keys_known = 1;
|
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.platform = Platform2_PLATFORM_LINUX_X86;
|
||||||
clientHello.build_info.version = SPOTIFY_VERSION;
|
clientHello.build_info.version = SPOTIFY_VERSION;
|
||||||
clientHello.feature_set.autoupdate2 = true;
|
clientHello.feature_set.autoupdate2 = true;
|
||||||
|
|||||||
@@ -166,6 +166,8 @@ void SpircController::handleFrame(std::vector<uint8_t> &data) {
|
|||||||
}
|
}
|
||||||
case MessageType_kMessageTypeReplace: {
|
case MessageType_kMessageTypeReplace: {
|
||||||
CSPOT_LOG(debug, "Got replace frame!");
|
CSPOT_LOG(debug, "Got replace frame!");
|
||||||
|
state->updateTracks();
|
||||||
|
this->notify();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MessageType_kMessageTypeShuffle: {
|
case MessageType_kMessageTypeShuffle: {
|
||||||
@@ -191,6 +193,7 @@ void SpircController::loadTrack(uint32_t position_ms, bool isPaused) {
|
|||||||
std::function<void()> loadedLambda = [=]() {
|
std::function<void()> loadedLambda = [=]() {
|
||||||
// Loading finished, notify that playback started
|
// Loading finished, notify that playback started
|
||||||
setPause(isPaused, false);
|
setPause(isPaused, false);
|
||||||
|
sendEvent(CSpotEventType::PLAYBACK_START);
|
||||||
};
|
};
|
||||||
|
|
||||||
player->handleLoad(state->getCurrentTrack(), loadedLambda, position_ms,
|
player->handleLoad(state->getCurrentTrack(), loadedLambda, position_ms,
|
||||||
|
|||||||
@@ -39,11 +39,12 @@ SpotifyTrack::~SpotifyTrack()
|
|||||||
pb_release(Episode_fields, &this->episodeInfo);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -51,21 +52,38 @@ bool SpotifyTrack::countryListContains(std::string countryList, std::string coun
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SpotifyTrack::canPlayTrack()
|
bool SpotifyTrack::canPlayTrack(int altIndex)
|
||||||
{
|
{
|
||||||
|
if(altIndex < 0)
|
||||||
|
{
|
||||||
for (int x = 0; x < trackInfo.restriction_count; x++)
|
for (int x = 0; x < trackInfo.restriction_count; x++)
|
||||||
{
|
{
|
||||||
if (trackInfo.restriction[x].countries_allowed != nullptr)
|
if (trackInfo.restriction[x].countries_allowed != nullptr)
|
||||||
{
|
{
|
||||||
return countryListContains(std::string(trackInfo.restriction[x].countries_allowed), manager->countryCode);
|
return countryListContains(trackInfo.restriction[x].countries_allowed, manager->countryCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackInfo.restriction[x].countries_forbidden != nullptr)
|
if (trackInfo.restriction[x].countries_forbidden != nullptr)
|
||||||
{
|
{
|
||||||
return !countryListContains(std::string(trackInfo.restriction[x].countries_forbidden), manager->countryCode);
|
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;
|
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 name: %s", trackInfo.name);
|
||||||
CSPOT_LOG(info, "Track duration: %d", trackInfo.duration);
|
CSPOT_LOG(info, "Track duration: %d", trackInfo.duration);
|
||||||
CSPOT_LOG(debug, "trackInfo.restriction.size() = %d", trackInfo.restriction_count);
|
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++;
|
altIndex++;
|
||||||
|
CSPOT_LOG(info, "Trying alternative %d", altIndex);
|
||||||
|
|
||||||
if(altIndex > trackInfo.alternative_count) {
|
if(altIndex > trackInfo.alternative_count) {
|
||||||
// no alternatives for song
|
// no alternatives for song
|
||||||
@@ -98,9 +112,12 @@ void SpotifyTrack::trackInformationCallback(std::unique_ptr<MercuryResponse> res
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto trackId = pbArrayToVector(trackInfo.gid);
|
std::vector<uint8_t> trackId;
|
||||||
this->fileId = std::vector<uint8_t>();
|
this->fileId = std::vector<uint8_t>();
|
||||||
|
|
||||||
|
if(altIndex < 0)
|
||||||
|
{
|
||||||
|
trackId = pbArrayToVector(trackInfo.gid);
|
||||||
for (int x = 0; x < trackInfo.file_count; x++)
|
for (int x = 0; x < trackInfo.file_count; x++)
|
||||||
{
|
{
|
||||||
if (trackInfo.file[x].format == configMan->format)
|
if (trackInfo.file[x].format == configMan->format)
|
||||||
@@ -109,6 +126,19 @@ void SpotifyTrack::trackInformationCallback(std::unique_ptr<MercuryResponse> res
|
|||||||
break; // If file found stop searching
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (trackInfoReceived != nullptr)
|
if (trackInfoReceived != nullptr)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include "ConfigJSON.h"
|
#include "ConfigJSON.h"
|
||||||
|
|
||||||
// provide weak deviceId (see ConstantParameters.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) {
|
ZeroconfAuthenticator::ZeroconfAuthenticator(authCallback callback, std::shared_ptr<bell::BaseHTTPServer> httpServer) {
|
||||||
this->gotBlobCallback = callback;
|
this->gotBlobCallback = callback;
|
||||||
|
|||||||
@@ -364,6 +364,8 @@ static bool cspot_cmd_handler(cspot_event_t cmd, va_list args)
|
|||||||
LOG_INFO("CSpot disconnected");
|
LOG_INFO("CSpot disconnected");
|
||||||
break;
|
break;
|
||||||
case CSPOT_TRACK:
|
case CSPOT_TRACK:
|
||||||
|
_buf_flush(outputbuf);
|
||||||
|
abort_sink = true;
|
||||||
LOG_INFO("CSpot sink new track rate %d", output.next_sample_rate);
|
LOG_INFO("CSpot sink new track rate %d", output.next_sample_rate);
|
||||||
break;
|
break;
|
||||||
case CSPOT_PLAY:
|
case CSPOT_PLAY:
|
||||||
|
|||||||
Reference in New Issue
Block a user