mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-08 04:27:12 +03:00
update CSpot
This commit is contained in:
@@ -12,8 +12,12 @@ class AudioSink
|
|||||||
virtual ~AudioSink() {}
|
virtual ~AudioSink() {}
|
||||||
virtual void feedPCMFrames(const uint8_t *buffer, size_t bytes) = 0;
|
virtual void feedPCMFrames(const uint8_t *buffer, size_t bytes) = 0;
|
||||||
virtual void volumeChanged(uint16_t volume) {}
|
virtual void volumeChanged(uint16_t volume) {}
|
||||||
// return true if the sink supports rate changing
|
// Return false if the sink doesn't support reconfiguration.
|
||||||
virtual bool setRate(uint16_t sampleRate) { return false; }
|
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 softwareVolumeControl = true;
|
||||||
bool usign = false;
|
bool usign = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ struct HTTPConnection {
|
|||||||
std::string httpMethod;
|
std::string httpMethod;
|
||||||
bool toBeClosed = false;
|
bool toBeClosed = false;
|
||||||
bool isEventConnection = false;
|
bool isEventConnection = false;
|
||||||
|
bool isCaptivePortal = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BaseHTTPServer {
|
class BaseHTTPServer {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
namespace bell {
|
namespace bell {
|
||||||
|
|
||||||
std::string generateRandomUUID();
|
std::string generateRandomUUID();
|
||||||
|
void freeAndNull(void *&ptr);
|
||||||
|
|
||||||
} // namespace bell
|
} // namespace bell
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class BiquadFilter
|
|||||||
private:
|
private:
|
||||||
std::mutex processMutex;
|
std::mutex processMutex;
|
||||||
float coeffs[5];
|
float coeffs[5];
|
||||||
float w[2];
|
float w[2] = {0, 0};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BiquadFilter(){};
|
BiquadFilter(){};
|
||||||
|
|||||||
@@ -36,14 +36,14 @@ class HTTPClient {
|
|||||||
std::map<std::string, std::string> headers;
|
std::map<std::string, std::string> headers;
|
||||||
|
|
||||||
uint16_t statusCode;
|
uint16_t statusCode;
|
||||||
size_t contentLength;
|
uint32_t contentLength;
|
||||||
std::string contentType;
|
std::string contentType;
|
||||||
std::string location;
|
std::string location;
|
||||||
bool isChunked = false;
|
bool isChunked = false;
|
||||||
bool isGzip = false;
|
bool isGzip = false;
|
||||||
bool isComplete = false;
|
bool isComplete = false;
|
||||||
bool isRedirect = false;
|
bool isRedirect = false;
|
||||||
size_t redirectCount = 0;
|
uint8_t redirectCount = 0;
|
||||||
std::ostream *dumpFs = nullptr;
|
std::ostream *dumpFs = nullptr;
|
||||||
std::ostream *dumpRawFs = nullptr;
|
std::ostream *dumpRawFs = nullptr;
|
||||||
|
|
||||||
@@ -51,34 +51,34 @@ class HTTPClient {
|
|||||||
void close() override;
|
void close() override;
|
||||||
|
|
||||||
void readHeaders();
|
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();
|
std::string readToString();
|
||||||
|
|
||||||
inline size_t skip(size_t len) override {
|
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 {
|
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);
|
return read((char *)dst, len, wait);
|
||||||
}
|
}
|
||||||
inline size_t size() override {
|
inline size_t size() override {
|
||||||
return contentLength;
|
return (size_t)contentLength;
|
||||||
}
|
}
|
||||||
inline size_t position() override {
|
inline size_t position() override {
|
||||||
return bodyRead;
|
return (size_t)bodyRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char *buf = nullptr; // allocated buffer
|
char *buf = nullptr; // allocated buffer
|
||||||
char *bufPtr = nullptr; // reading pointer within buf
|
char *bufPtr = nullptr; // reading pointer within buf
|
||||||
size_t bodyRead = 0;
|
uint32_t bodyRead = 0;
|
||||||
size_t bufRemaining = 0;
|
uint32_t bufRemaining = 0;
|
||||||
size_t chunkRemaining = 0;
|
uint32_t chunkRemaining = 0;
|
||||||
bool isStreaming = false;
|
bool isStreaming = false;
|
||||||
size_t readRaw(char *dst);
|
uint32_t readRaw(char *dst);
|
||||||
bool skipRaw(size_t len, bool dontRead = false);
|
bool skipRaw(uint32_t len, bool dontRead = false);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::unique_ptr<struct HTTPClient::HTTPResponse> HTTPResponse_t;
|
typedef std::unique_ptr<struct HTTPClient::HTTPResponse> HTTPResponse_t;
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ namespace bell
|
|||||||
std::vector<std::string> splitUrl(const std::string &url, char delimiter);
|
std::vector<std::string> splitUrl(const std::string &url, char delimiter);
|
||||||
std::mutex responseMutex;
|
std::mutex responseMutex;
|
||||||
std::vector<char> responseBuffer = std::vector<char>(128);
|
std::vector<char> responseBuffer = std::vector<char>(128);
|
||||||
|
void redirectCaptivePortal(int connectionFd);
|
||||||
void readFromClient(int clientFd);
|
void readFromClient(int clientFd);
|
||||||
std::map<std::string, std::string> parseQueryString(const std::string &queryString);
|
std::map<std::string, std::string> parseQueryString(const std::string &queryString);
|
||||||
unsigned char h2int(char c);
|
unsigned char h2int(char c);
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
#define JSONOBJECT_H
|
#define JSONOBJECT_H
|
||||||
#include <cJSON.h>
|
#include <cJSON.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace bell {
|
namespace bell {
|
||||||
class JSONValue
|
class JSONValue
|
||||||
@@ -24,6 +26,7 @@ namespace bell {
|
|||||||
~JSONObject();
|
~JSONObject();
|
||||||
JSONValue operator[](std::string index);
|
JSONValue operator[](std::string index);
|
||||||
std::string toString();
|
std::string toString();
|
||||||
|
std::vector<uint8_t> toVector();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
cJSON* body;
|
cJSON* body;
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ namespace bell
|
|||||||
sizeof(int)); /* length of option value */
|
sizeof(int)); /* length of option value */
|
||||||
|
|
||||||
freeaddrinfo(addr);
|
freeaddrinfo(addr);
|
||||||
|
std::cout << "Socket opened" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t read(uint8_t *buf, size_t len) {
|
size_t read(uint8_t *buf, size_t len) {
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
class BufferedAudioSink : public AudioSink
|
class BufferedAudioSink : public AudioSink
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void feedPCMFrames(const uint8_t *buffer, size_t bytes);
|
void feedPCMFrames(const uint8_t *buffer, size_t bytes) override;
|
||||||
bool setRate(uint16_t sampleRate) override;
|
bool setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) override;
|
||||||
protected:
|
protected:
|
||||||
void startI2sFeed(size_t buf_size = 4096 * 8);
|
void startI2sFeed(size_t buf_size = 4096 * 8);
|
||||||
void feedPCMFramesInternal(const void *pvItem, size_t xItemSize);
|
void feedPCMFramesInternal(const void *pvItem, size_t xItemSize);
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ public:
|
|||||||
explicit SPDIFAudioSink(uint8_t spdifPin);
|
explicit SPDIFAudioSink(uint8_t spdifPin);
|
||||||
~SPDIFAudioSink() override;
|
~SPDIFAudioSink() override;
|
||||||
void feedPCMFrames(const uint8_t *buffer, size_t bytes) override;
|
void feedPCMFrames(const uint8_t *buffer, size_t bytes) override;
|
||||||
void initialize(uint16_t sampleRate);
|
bool setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) override;
|
||||||
bool setRate(uint16_t sampleRate) override;
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "portaudio.h"
|
#include "portaudio.h"
|
||||||
#include <stdint.h>
|
#include <cstdint>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "AudioSink.h"
|
#include "AudioSink.h"
|
||||||
|
|
||||||
@@ -10,11 +10,10 @@ class PortAudioSink : public AudioSink
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PortAudioSink();
|
PortAudioSink();
|
||||||
~PortAudioSink();
|
~PortAudioSink() override;
|
||||||
void feedPCMFrames(const uint8_t *buffer, size_t bytes);
|
void feedPCMFrames(const uint8_t *buffer, size_t bytes) override;
|
||||||
void initialize(uint16_t sampleRate);
|
bool setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) override;
|
||||||
bool setRate(uint16_t sampleRate) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PaStream *stream;
|
PaStream *stream = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,3 +18,8 @@ std::string bell::generateRandomUUID() {
|
|||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bell::freeAndNull(void *&ptr) {
|
||||||
|
free(ptr);
|
||||||
|
ptr = nullptr;
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ void bell::BinaryReader::skip(size_t pos) {
|
|||||||
|
|
||||||
int32_t bell::BinaryReader::readInt() {
|
int32_t bell::BinaryReader::readInt() {
|
||||||
uint8_t b[4];
|
uint8_t b[4];
|
||||||
stream->read((uint8_t *) b,4);
|
if (stream->read((uint8_t *) b,4) != 4)
|
||||||
|
return 0;
|
||||||
|
|
||||||
return static_cast<int32_t>(
|
return static_cast<int32_t>(
|
||||||
(b[3]) |
|
(b[3]) |
|
||||||
@@ -35,11 +36,12 @@ int32_t bell::BinaryReader::readInt() {
|
|||||||
|
|
||||||
int16_t bell::BinaryReader::readShort() {
|
int16_t bell::BinaryReader::readShort() {
|
||||||
uint8_t b[2];
|
uint8_t b[2];
|
||||||
stream->read((uint8_t *) b,2);
|
if (stream->read((uint8_t *) b,2) != 2)
|
||||||
|
return 0;
|
||||||
|
|
||||||
return static_cast<int16_t>(
|
return static_cast<int16_t>(
|
||||||
(b[1]) |
|
(b[1]) |
|
||||||
(b[1] << 8));
|
(b[0] << 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -49,7 +51,8 @@ uint32_t bell::BinaryReader::readUInt() {
|
|||||||
|
|
||||||
uint8_t bell::BinaryReader::readByte() {
|
uint8_t bell::BinaryReader::readByte() {
|
||||||
uint8_t b[1];
|
uint8_t b[1];
|
||||||
stream->read((uint8_t *) b,1);
|
if (stream->read((uint8_t *) b,1) != 1)
|
||||||
|
return 0;
|
||||||
return b[0];
|
return b[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,9 +13,7 @@ void HTTPClient::HTTPResponse::close() {
|
|||||||
bufPtr = nullptr;
|
bufPtr = nullptr;
|
||||||
}
|
}
|
||||||
HTTPClient::HTTPResponse::~HTTPResponse() {
|
HTTPClient::HTTPResponse::~HTTPResponse() {
|
||||||
socket = nullptr;
|
this->close();
|
||||||
if (buf)
|
|
||||||
free(buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTPResponse_t HTTPClient::execute(const struct HTTPRequest &request) {
|
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 << header.first << ": " << header.second << endl;
|
||||||
}
|
}
|
||||||
stream << endl;
|
stream << endl;
|
||||||
|
if (request.body != nullptr) {
|
||||||
stream << request.body;
|
stream << request.body;
|
||||||
|
}
|
||||||
std::string data = stream.str();
|
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()) {
|
if (len != data.size()) {
|
||||||
response->close();
|
response->close();
|
||||||
BELL_LOG(error, "http", "Writing failed: wrote %d of %d bytes", len, data.size());
|
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) {
|
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) {
|
if (strncasecmp(header, name, len) == 0) {
|
||||||
header += len;
|
header += len;
|
||||||
while (*header == ' ')
|
while (*header == ' ')
|
||||||
@@ -101,8 +101,13 @@ bool HTTPClient::readHeader(const char *&header, const char *name) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t HTTPClient::HTTPResponse::readRaw(char *dst) {
|
uint32_t HTTPClient::HTTPResponse::readRaw(char *dst) {
|
||||||
size_t len = this->socket->read((uint8_t *)dst, BUF_SIZE);
|
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)
|
if (dumpRawFs)
|
||||||
dumpRawFs->write(dst, (long)len);
|
dumpRawFs->write(dst, (long)len);
|
||||||
// BELL_LOG(debug, "http", "Read %d bytes", len);
|
// BELL_LOG(debug, "http", "Read %d bytes", len);
|
||||||
@@ -111,7 +116,7 @@ size_t HTTPClient::HTTPResponse::readRaw(char *dst) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HTTPClient::HTTPResponse::readHeaders() {
|
void HTTPClient::HTTPResponse::readHeaders() {
|
||||||
size_t len;
|
uint32_t len;
|
||||||
char *line, *lineEnd;
|
char *line, *lineEnd;
|
||||||
bool complete = false;
|
bool complete = false;
|
||||||
std::string lineBuf;
|
std::string lineBuf;
|
||||||
@@ -185,8 +190,8 @@ void HTTPClient::HTTPResponse::readHeaders() {
|
|||||||
} while (!complete && len); // if len == 0, the connection is closed
|
} while (!complete && len); // if len == 0, the connection is closed
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HTTPClient::HTTPResponse::skipRaw(size_t len, bool dontRead) {
|
bool HTTPClient::HTTPResponse::skipRaw(uint32_t len, bool dontRead) {
|
||||||
size_t skip = 0;
|
uint32_t skip = 0;
|
||||||
if (len > bufRemaining) {
|
if (len > bufRemaining) {
|
||||||
skip = len - bufRemaining;
|
skip = len - bufRemaining;
|
||||||
len = bufRemaining;
|
len = bufRemaining;
|
||||||
@@ -211,16 +216,17 @@ bool HTTPClient::HTTPResponse::skipRaw(size_t len, bool dontRead) {
|
|||||||
return true;
|
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) {
|
if (isComplete) {
|
||||||
// end of chunked stream was found OR complete body was read
|
// end of chunked stream was found OR complete body was read
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
auto *dstStart = dst ? dst : nullptr;
|
auto *dstStart = dst ? dst : nullptr;
|
||||||
size_t read = 0;
|
uint32_t read = 0;
|
||||||
while (toRead) { // this loop ends after original toRead
|
while (toRead) { // this loop ends after original toRead
|
||||||
skipRaw(0); // ensure the buffer contains data, wait if necessary
|
skipRaw(0); // ensure the buffer contains data, wait if necessary
|
||||||
if (isChunked && !chunkRemaining) {
|
if (isChunked && !chunkRemaining) {
|
||||||
|
// chunked responses (either streaming or not)
|
||||||
if (*bufPtr == '0') { // all chunks were read *and emitted*
|
if (*bufPtr == '0') { // all chunks were read *and emitted*
|
||||||
isComplete = true;
|
isComplete = true;
|
||||||
break;
|
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
|
if (!skipRaw(endPtr - bufPtr + 2)) // skip the size and \r\n
|
||||||
break; // -> no more data, break out of main loop
|
break; // -> no more data, break out of main loop
|
||||||
} else if (contentLength && !chunkRemaining) {
|
} else if (contentLength && !chunkRemaining) {
|
||||||
|
// normal responses (having content-length)
|
||||||
chunkRemaining = contentLength;
|
chunkRemaining = contentLength;
|
||||||
|
} else if (!chunkRemaining) {
|
||||||
|
// fallback for non-chunked streams (without content-length)
|
||||||
|
chunkRemaining = toRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (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) {
|
if (dst) {
|
||||||
memcpy(dst, bufPtr, count);
|
memcpy(dst, bufPtr, count);
|
||||||
dst += count; // move the dst pointer
|
dst += count; // move the dst pointer
|
||||||
@@ -288,7 +298,7 @@ std::string HTTPClient::HTTPResponse::readToString() {
|
|||||||
}
|
}
|
||||||
std::string result;
|
std::string result;
|
||||||
char buffer[BUF_SIZE + 1]; // make space for null-terminator
|
char buffer[BUF_SIZE + 1]; // make space for null-terminator
|
||||||
size_t len;
|
uint32_t len;
|
||||||
do {
|
do {
|
||||||
len = this->read(buffer, BUF_SIZE);
|
len = this->read(buffer, BUF_SIZE);
|
||||||
buffer[len] = '\0';
|
buffer[len] = '\0';
|
||||||
|
|||||||
@@ -170,16 +170,23 @@ void bell::HTTPServer::readFromClient(int clientFd) {
|
|||||||
if (line.find("Content-Length: ") != std::string::npos) {
|
if (line.find("Content-Length: ") != std::string::npos) {
|
||||||
conn.contentLength =
|
conn.contentLength =
|
||||||
std::stoi(line.substr(16, line.size() - 1));
|
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 (line.size() == 0) {
|
||||||
if (conn.contentLength != 0) {
|
if (conn.contentLength != 0) {
|
||||||
conn.isReadingBody = true;
|
conn.isReadingBody = true;
|
||||||
goto READBODY;
|
goto READBODY;
|
||||||
} else {
|
} else {
|
||||||
findAndHandleRoute(conn.httpMethod, conn.currentLine,
|
if (!conn.isCaptivePortal) {
|
||||||
clientFd);
|
findAndHandleRoute(conn.httpMethod, conn.currentLine, clientFd);
|
||||||
|
} else {
|
||||||
|
this->redirectCaptivePortal(clientFd);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -277,6 +284,21 @@ void bell::HTTPServer::respond(const HTTPResponse &response) {
|
|||||||
writeResponse(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) {
|
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;
|
||||||
@@ -407,6 +429,11 @@ void bell::HTTPServer::findAndHandleRoute(std::string &url, std::string &body,
|
|||||||
if (routeSplit.back().find('*') != std::string::npos &&
|
if (routeSplit.back().find('*') != std::string::npos &&
|
||||||
urlSplit[1] == routeSplit[1]) {
|
urlSplit[1] == routeSplit[1]) {
|
||||||
matches = true;
|
matches = true;
|
||||||
|
for (int x = 1; x <= routeSplit.size() - 2; x++) {
|
||||||
|
if (urlSplit[x] != routeSplit[x]) {
|
||||||
|
matches = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matches) {
|
if (matches) {
|
||||||
|
|||||||
@@ -43,3 +43,10 @@ std::string bell::JSONObject::toString()
|
|||||||
return retVal;
|
return retVal;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> bell::JSONObject::toVector() {
|
||||||
|
char *body = cJSON_Print(this->body);
|
||||||
|
std::vector<uint8_t> res(body, body + strlen(body));
|
||||||
|
free(body);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ void BufferedAudioSink::feedPCMFramesInternal(const void *pvItem, size_t xItemSi
|
|||||||
xRingbufferSend(dataBuffer, pvItem, xItemSize, portMAX_DELAY);
|
xRingbufferSend(dataBuffer, pvItem, xItemSize, portMAX_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BufferedAudioSink::setRate(uint16_t sampleRate) {
|
bool BufferedAudioSink::setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) {
|
||||||
i2s_set_sample_rates((i2s_port_t)0, sampleRate);
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -72,19 +72,21 @@ SPDIFAudioSink::SPDIFAudioSink(uint8_t spdifPin)
|
|||||||
spdif_buf_init();
|
spdif_buf_init();
|
||||||
spdif_ptr = spdif_buf;
|
spdif_ptr = spdif_buf;
|
||||||
this->spdifPin = spdifPin;
|
this->spdifPin = spdifPin;
|
||||||
this->initialize(44100);
|
this->setParams(44100, 16, 2);
|
||||||
startI2sFeed(SPDIF_BUF_SIZE * 16);
|
startI2sFeed(SPDIF_BUF_SIZE * 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPDIFAudioSink::initialize(uint16_t sampleRate) {
|
bool SPDIFAudioSink::setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) {
|
||||||
int sample_rate = sampleRate * 2;
|
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 bclk = sample_rate * 64 * 2;
|
||||||
int mclk = (I2S_BUG_MAGIC / bclk) * bclk;
|
int mclk = (I2S_BUG_MAGIC / bclk) * bclk;
|
||||||
|
|
||||||
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,
|
.sample_rate = (uint32_t)sample_rate,
|
||||||
.bits_per_sample = (i2s_bits_per_sample_t)32,
|
.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,
|
||||||
.intr_alloc_flags = 0,
|
.intr_alloc_flags = 0,
|
||||||
@@ -100,18 +102,14 @@ void SPDIFAudioSink::initialize(uint16_t sampleRate) {
|
|||||||
.data_out_num = spdifPin,
|
.data_out_num = spdifPin,
|
||||||
.data_in_num = -1,
|
.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);
|
i2s_driver_uninstall((i2s_port_t)0);
|
||||||
this->initialize(sampleRate);
|
int err = i2s_driver_install((i2s_port_t)0, &i2s_config, 0, nullptr);
|
||||||
return true;
|
i2s_set_pin((i2s_port_t)0, &pin_config);
|
||||||
|
return !err;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDIFAudioSink::~SPDIFAudioSink() {
|
||||||
|
i2s_driver_uninstall((i2s_port_t)0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int num_frames = 0;
|
int num_frames = 0;
|
||||||
|
|||||||
@@ -3,10 +3,13 @@
|
|||||||
PortAudioSink::PortAudioSink()
|
PortAudioSink::PortAudioSink()
|
||||||
{
|
{
|
||||||
Pa_Initialize();
|
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;
|
PaStreamParameters outputParameters;
|
||||||
outputParameters.device = Pa_GetDefaultOutputDevice();
|
outputParameters.device = Pa_GetDefaultOutputDevice();
|
||||||
if (outputParameters.device == paNoDevice) {
|
if (outputParameters.device == paNoDevice) {
|
||||||
@@ -15,8 +18,24 @@ void PortAudioSink::initialize(uint16_t sampleRate) {
|
|||||||
}
|
}
|
||||||
printf("PortAudio: Default audio device not found!\n");
|
printf("PortAudio: Default audio device not found!\n");
|
||||||
|
|
||||||
outputParameters.channelCount = 2; /* stereo output */
|
outputParameters.channelCount = channelCount;
|
||||||
outputParameters.sampleFormat = paInt16; /* 32 bit floating point output */
|
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.suggestedLatency = 0.050;
|
||||||
outputParameters.hostApiSpecificStreamInfo = NULL;
|
outputParameters.hostApiSpecificStreamInfo = NULL;
|
||||||
|
|
||||||
@@ -25,12 +44,13 @@ void PortAudioSink::initialize(uint16_t sampleRate) {
|
|||||||
NULL,
|
NULL,
|
||||||
&outputParameters,
|
&outputParameters,
|
||||||
sampleRate,
|
sampleRate,
|
||||||
4096 / 4,
|
4096 / (channelCount * bitDepth / 8),
|
||||||
paClipOff,
|
paClipOff,
|
||||||
NULL, // blocking api
|
NULL, // blocking api
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
Pa_StartStream(stream);
|
Pa_StartStream(stream);
|
||||||
|
return !err;
|
||||||
}
|
}
|
||||||
|
|
||||||
PortAudioSink::~PortAudioSink()
|
PortAudioSink::~PortAudioSink()
|
||||||
@@ -39,14 +59,6 @@ PortAudioSink::~PortAudioSink()
|
|||||||
Pa_Terminate();
|
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)
|
void PortAudioSink::feedPCMFrames(const uint8_t *buffer, size_t bytes)
|
||||||
{
|
{
|
||||||
Pa_WriteStream(stream, buffer, bytes / 4);
|
Pa_WriteStream(stream, buffer, bytes / 4);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
#define SPOTIFY_HEADER_SIZE 167
|
#define SPOTIFY_HEADER_SIZE 167
|
||||||
#define BUFFER_SIZE 0x20000 * 1.5
|
#define BUFFER_SIZE 0x20000 * 1.5
|
||||||
typedef std::function<void(std::vector<uint8_t>&)> pcmDataCallback;
|
typedef std::function<void(uint8_t *, size_t)> pcmDataCallback;
|
||||||
|
|
||||||
enum class Whence
|
enum class Whence
|
||||||
{
|
{
|
||||||
@@ -59,7 +59,7 @@ public:
|
|||||||
std::vector<uint8_t> read(size_t bytes);
|
std::vector<uint8_t> read(size_t bytes);
|
||||||
void seekMs(uint32_t positionMs);
|
void seekMs(uint32_t positionMs);
|
||||||
void seek(size_t pos, Whence whence);
|
void seek(size_t pos, Whence whence);
|
||||||
void startPlaybackLoop();
|
void startPlaybackLoop(uint8_t *pcmOut, size_t pcmOut_len);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public:
|
|||||||
void pause();
|
void pause();
|
||||||
void cancelCurrentTrack();
|
void cancelCurrentTrack();
|
||||||
void seekMs(size_t positionMs);
|
void seekMs(size_t positionMs);
|
||||||
void feedPCM(std::vector<uint8_t> &data);
|
void feedPCM(uint8_t *data, size_t len);
|
||||||
void play();
|
void play();
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ private:
|
|||||||
void loadTrack(uint32_t position_ms = 0, bool isPaused = 0);
|
void loadTrack(uint32_t position_ms = 0, bool isPaused = 0);
|
||||||
public:
|
public:
|
||||||
SpircController(std::shared_ptr<MercuryManager> manager, std::string username, std::shared_ptr<AudioSink> audioSink);
|
SpircController(std::shared_ptr<MercuryManager> manager, std::string username, std::shared_ptr<AudioSink> audioSink);
|
||||||
|
~SpircController();
|
||||||
void subscribe();
|
void subscribe();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ void ChunkedAudioStream::seekMs(uint32_t positionMs)
|
|||||||
CSPOT_LOG(debug, "--- Finished seeking!");
|
CSPOT_LOG(debug, "--- Finished seeking!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChunkedAudioStream::startPlaybackLoop()
|
void ChunkedAudioStream::startPlaybackLoop(uint8_t *pcmOut, size_t pcmOut_len)
|
||||||
{
|
{
|
||||||
|
|
||||||
isRunning = true;
|
isRunning = true;
|
||||||
@@ -91,7 +91,6 @@ void ChunkedAudioStream::startPlaybackLoop()
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool eof = false;
|
bool eof = false;
|
||||||
std::vector<uint8_t> pcmOut(4096 / 4);
|
|
||||||
byteStream->setEnableLoadAhead(true);
|
byteStream->setEnableLoadAhead(true);
|
||||||
|
|
||||||
while (!eof && isRunning)
|
while (!eof && isRunning)
|
||||||
@@ -100,7 +99,7 @@ void ChunkedAudioStream::startPlaybackLoop()
|
|||||||
{
|
{
|
||||||
|
|
||||||
this->seekMutex.lock();
|
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();
|
this->seekMutex.unlock();
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
{
|
{
|
||||||
@@ -117,8 +116,7 @@ void ChunkedAudioStream::startPlaybackLoop()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Write the actual data
|
// Write the actual data
|
||||||
auto data = std::vector<uint8_t>(pcmOut.begin(), pcmOut.begin() + ret);
|
pcmCallback(pcmOut, ret);
|
||||||
pcmCallback(data);
|
|
||||||
// audioSink->feedPCMFrames(data);
|
// audioSink->feedPCMFrames(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ size_t ChunkedByteStream::read(uint8_t *buf, size_t nbytes) {
|
|||||||
if (chunk != nullptr) {
|
if (chunk != nullptr) {
|
||||||
// Wait for chunk if not loaded yet
|
// Wait for chunk if not loaded yet
|
||||||
if (!chunk->isLoaded && !chunk->isFailed) {
|
if (!chunk->isLoaded && !chunk->isFailed) {
|
||||||
|
BELL_LOG(info, "cspot", "Chunk not loaded, waiting for %d", chunkIndex);
|
||||||
chunk->isLoadedSemaphore->wait();
|
chunk->isLoadedSemaphore->wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ void Player::seekMs(size_t positionMs)
|
|||||||
// VALGRIND_DO_LEAK_CHECK;
|
// VALGRIND_DO_LEAK_CHECK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::feedPCM(std::vector<uint8_t>& data)
|
void Player::feedPCM(uint8_t *data, size_t len)
|
||||||
{
|
{
|
||||||
// Simple digital volume control alg
|
// Simple digital volume control alg
|
||||||
// @TODO actually extract it somewhere
|
// @TODO actually extract it somewhere
|
||||||
@@ -51,8 +51,8 @@ void Player::feedPCM(std::vector<uint8_t>& data)
|
|||||||
{
|
{
|
||||||
int16_t* psample;
|
int16_t* psample;
|
||||||
uint32_t pmax;
|
uint32_t pmax;
|
||||||
psample = (int16_t*)(data.data());
|
psample = (int16_t*)(data);
|
||||||
for (int32_t i = 0; i < (data.size() / 2); i++)
|
for (int32_t i = 0; i < (len / 2); i++)
|
||||||
{
|
{
|
||||||
int32_t temp;
|
int32_t temp;
|
||||||
// Offset data for unsigned sinks
|
// Offset data for unsigned sinks
|
||||||
@@ -68,25 +68,27 @@ void Player::feedPCM(std::vector<uint8_t>& data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->audioSink->feedPCMFrames(data.data(), data.size());
|
this->audioSink->feedPCMFrames(data, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::runTask()
|
void Player::runTask()
|
||||||
{
|
{
|
||||||
|
uint8_t *pcmOut = (uint8_t *) malloc(4096 / 4);
|
||||||
std::scoped_lock lock(this->runningMutex);
|
std::scoped_lock lock(this->runningMutex);
|
||||||
this->isRunning = true;
|
this->isRunning = true;
|
||||||
while (isRunning)
|
while (isRunning)
|
||||||
{
|
{
|
||||||
if (this->trackQueue.wpop(currentTrack)) {
|
if (this->trackQueue.wpop(currentTrack)) {
|
||||||
currentTrack->audioStream->startPlaybackLoop();
|
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 {
|
} else {
|
||||||
//usleep(100000);
|
usleep(100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
free(pcmOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::stop() {
|
void Player::stop() {
|
||||||
@@ -115,8 +117,8 @@ void Player::handleLoad(std::shared_ptr<TrackReference> trackReference, std::fun
|
|||||||
std::lock_guard<std::mutex> guard(loadTrackMutex);
|
std::lock_guard<std::mutex> guard(loadTrackMutex);
|
||||||
cancelCurrentTrack();
|
cancelCurrentTrack();
|
||||||
|
|
||||||
pcmDataCallback framesCallback = [=](std::vector<uint8_t>& frames) {
|
pcmDataCallback framesCallback = [=](uint8_t *frames, size_t len) {
|
||||||
this->feedPCM(frames);
|
this->feedPCM(frames, len);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto loadedLambda = trackLoadedCallback;
|
auto loadedLambda = trackLoadedCallback;
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ PlayerState::PlayerState(std::shared_ptr<TimeProvider> timeProvider)
|
|||||||
|
|
||||||
innerFrame.device_state.name = strdup(configMan->deviceName.c_str());
|
innerFrame.device_state.name = strdup(configMan->deviceName.c_str());
|
||||||
|
|
||||||
|
innerFrame.state.track_count = 0;
|
||||||
|
|
||||||
// Prepare player's capabilities
|
// Prepare player's capabilities
|
||||||
addCapability(CapabilityType_kCanBePlayer, 1);
|
addCapability(CapabilityType_kCanBePlayer, 1);
|
||||||
addCapability(CapabilityType_kDeviceType, 4);
|
addCapability(CapabilityType_kDeviceType, 4);
|
||||||
@@ -133,11 +135,44 @@ void PlayerState::updatePositionMs(uint32_t position)
|
|||||||
innerFrame.state.position_ms = position;
|
innerFrame.state.position_ms = position;
|
||||||
innerFrame.state.position_measured_at = timeProvider->getSyncedTimestamp();
|
innerFrame.state.position_measured_at = timeProvider->getSyncedTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
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.track_count = remoteFrame.state.track_count;
|
||||||
innerFrame.state.has_playing_track_index = true;
|
innerFrame.state.has_playing_track_index = true;
|
||||||
innerFrame.state.playing_track_index = remoteFrame.state.playing_track_index;
|
innerFrame.state.playing_track_index = remoteFrame.state.playing_track_index;
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ SpircController::SpircController(std::shared_ptr<MercuryManager> manager,
|
|||||||
subscribe();
|
subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SpircController::~SpircController() {
|
||||||
|
}
|
||||||
|
|
||||||
void SpircController::subscribe() {
|
void SpircController::subscribe() {
|
||||||
mercuryCallback responseLambda = [=](std::unique_ptr<MercuryResponse> res) {
|
mercuryCallback responseLambda = [=](std::unique_ptr<MercuryResponse> res) {
|
||||||
// this->trackInformationCallback(std::move(res));
|
// this->trackInformationCallback(std::move(res));
|
||||||
|
|||||||
@@ -89,8 +89,13 @@ void SpotifyTrack::trackInformationCallback(std::unique_ptr<MercuryResponse> res
|
|||||||
std::swap(trackInfo.file, trackInfo.alternative[altIndex].file);
|
std::swap(trackInfo.file, trackInfo.alternative[altIndex].file);
|
||||||
std::swap(trackInfo.file_count, trackInfo.alternative[altIndex].file_count);
|
std::swap(trackInfo.file_count, trackInfo.alternative[altIndex].file_count);
|
||||||
std::swap(trackInfo.gid, trackInfo.alternative[altIndex].gid);
|
std::swap(trackInfo.gid, trackInfo.alternative[altIndex].gid);
|
||||||
|
|
||||||
CSPOT_LOG(info, "Trying alternative %d", altIndex);
|
CSPOT_LOG(info, "Trying alternative %d", altIndex);
|
||||||
|
altIndex++;
|
||||||
|
|
||||||
|
if(altIndex > trackInfo.alternative_count) {
|
||||||
|
// no alternatives for song
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto trackId = pbArrayToVector(trackInfo.gid);
|
auto trackId = pbArrayToVector(trackInfo.gid);
|
||||||
|
|||||||
Reference in New Issue
Block a user