Skip to content

Commit

Permalink
update CSpot + clear audio buffer when changing track
Browse files Browse the repository at this point in the history
  • Loading branch information
philippe44 committed Aug 24, 2022
1 parent 36f18fc commit 0222dbd
Show file tree
Hide file tree
Showing 22 changed files with 382 additions and 92 deletions.
168 changes: 167 additions & 1 deletion components/spotify/cspot/bell/include/BiquadFilter.h
Expand Up @@ -17,6 +17,59 @@ class BiquadFilter
public:
BiquadFilter(){};

// Generates coefficients for a high pass biquad filter
void generateHighPassCoEffs(float f, float q){
if (q <= 0.0001) {
q = 0.0001;
}
float Fs = 1;

float w0 = 2 * M_PI * f / Fs;
float c = cosf(w0);
float s = sinf(w0);
float alpha = s / (2 * q);

float b0 = (1 + c) / 2;
float b1 = -(1 + c);
float b2 = b0;
float a0 = 1 + alpha;
float a1 = -2 * c;
float a2 = 1 - alpha;

coeffs[0] = b0 / a0;
coeffs[1] = b1 / a0;
coeffs[2] = b2 / a0;
coeffs[3] = a1 / a0;
coeffs[4] = a2 / a0;
}

// Generates coefficients for a low pass biquad filter
void generateLowPassCoEffs(float f, float q){
if (q <= 0.0001) {
q = 0.0001;
}
float Fs = 1;

float w0 = 2 * M_PI * f / Fs;
float c = cosf(w0);
float s = sinf(w0);
float alpha = s / (2 * q);

float b0 = (1 - c) / 2;
float b1 = 1 - c;
float b2 = b0;
float a0 = 1 + alpha;
float a1 = -2 * c;
float a2 = 1 - alpha;

coeffs[0] = b0 / a0;
coeffs[1] = b1 / a0;
coeffs[2] = b2 / a0;
coeffs[3] = a1 / a0;
coeffs[4] = a2 / a0;
}

// Generates coefficients for a high shelf biquad filter
void generateHighShelfCoEffs(float f, float gain, float q)
{
if (q <= 0.0001)
Expand Down Expand Up @@ -106,6 +159,119 @@ class BiquadFilter
coeffs[4] = a2 / a0;
}

// Generates coefficients for a peaking biquad filter
void generatePeakCoEffs(float f, float gain, float q)
{
if (q <= 0.0001) {
q = 0.0001;
}
float Fs = 1;

float w0 = 2 * M_PI * f / Fs;
float c = cosf(w0);
float s = sinf(w0);
float alpha = s / (2 * q);

float b0 = alpha;
float b1 = 0;
float b2 = -alpha;
float a0 = 1 + alpha;
float a1 = -2 * c;
float a2 = 1 - alpha;

coeffs[0] = b0 / a0;
coeffs[1] = b1 / a0;
coeffs[2] = b2 / a0;
coeffs[3] = a1 / a0;
coeffs[4] = a2 / a0;
}


// Generates coefficients for a peaking EQ biquad filter
void generatePeakingEqCoEffs(float f, float gain, float q)
{
// formular taken from: https://webaudio.github.io/Audio-EQ-Cookbook/audio-eq-cookbook.html
if (q <= 0.0001) {
q = 0.0001;
}
float Fs = 1;

float w0 = 2 * M_PI * f / Fs;
float c = cosf(w0);
float s = sinf(w0);
float alpha = s / (2 * q);
float A = sqrtf(pow(10, (double)gain / 20.0));

float b0 = 1 + (alpha*A);
float b1 = (-2*c);
float b2 = 1 - (alpha*A);
float a0 = 1 + (alpha/A);
float a1 = b1;
float a2 = 1 - (alpha/A);

coeffs[0] = b0 / a0;
coeffs[1] = b1 / a0;
coeffs[2] = b2 / a0;
coeffs[3] = a1 / a0;
coeffs[4] = a2 / a0;
}



// Generates coefficients for an all pass 180° biquad filter
void generateAllPass180CoEffs(float f, float q)
{
if (q <= 0.0001) {
q = 0.0001;
}
float Fs = 1;

float w0 = 2 * M_PI * f / Fs;
float c = cosf(w0);
float s = sinf(w0);
float alpha = s / (2 * q);

float b0 = 1 - alpha;
float b1 = -2 * c;
float b2 = 1 + alpha;
float a0 = 1 + alpha;
float a1 = -2 * c;
float a2 = 1 - alpha;

coeffs[0] = b0 / a0;
coeffs[1] = b1 / a0;
coeffs[2] = b2 / a0;
coeffs[3] = a1 / a0;
coeffs[4] = a2 / a0;
}

// Generates coefficients for an all pass 360° biquad filter
void generateAllPass360CoEffs(float f, float q)
{
if (q <= 0.0001) {
q = 0.0001;
}
float Fs = 1;

float w0 = 2 * M_PI * f / Fs;
float c = cosf(w0);
float s = sinf(w0);
float alpha = s / (2 * q);

float b0 = 1 - alpha;
float b1 = -2 * c;
float b2 = 1 + alpha;
float a0 = 1 + alpha;
float a1 = -2 * c;
float a2 = 1 - alpha;

coeffs[0] = b0 / a0;
coeffs[1] = b1 / a0;
coeffs[2] = b2 / a0;
coeffs[3] = a1 / a0;
coeffs[4] = a2 / a0;
}

void processSamples(float *input, int numSamples)
{
std::scoped_lock lock(processMutex);
Expand All @@ -125,4 +291,4 @@ class BiquadFilter
}
};

#endif
#endif
2 changes: 1 addition & 1 deletion components/spotify/cspot/bell/src/BinaryReader.cpp
Expand Up @@ -67,6 +67,6 @@ long long bell::BinaryReader::readLong() {
long low = readInt();

return static_cast<long long>(
(high << 32) | low );
((long long) high << 32) | low );
}

1 change: 1 addition & 0 deletions components/spotify/cspot/bell/src/CryptoOpenSSL.cpp
Expand Up @@ -127,6 +127,7 @@ void CryptoOpenSSL::aesECBdecrypt(const std::vector<uint8_t>& key, std::vector<u
int len = 0;

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_DecryptFinal_ex(ctx, data.data() + len, &len);

Expand Down
30 changes: 21 additions & 9 deletions components/spotify/cspot/bell/src/HTTPServer.cpp
@@ -1,4 +1,5 @@
#include "HTTPServer.h"
#include <cstring>

bell::HTTPServer::HTTPServer(int serverPort) { this->serverPort = serverPort; }

Expand Down Expand Up @@ -65,8 +66,7 @@ void bell::HTTPServer::registerHandler(RequestType requestType,
}

void bell::HTTPServer::listen() {
BELL_LOG(info, "http", "Starting server at port %d",
this->serverPort);
BELL_LOG(info, "http", "Starting server at port %d", this->serverPort);

// setup address
struct addrinfo hints, *server;
Expand All @@ -82,9 +82,20 @@ void bell::HTTPServer::listen() {
socklen_t incomingSockSize;
int i;
int yes = true;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
bind(sockfd, server->ai_addr, server->ai_addrlen);
::listen(sockfd, 10);
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) {
throw std::runtime_error("setsockopt failed: " +
std::string(strerror(errno)));
}
if (bind(sockfd, server->ai_addr, server->ai_addrlen) < 0) {
throw std::runtime_error("bind failed on port " +
std::to_string(this->serverPort) + ": " +
std::string(strerror(errno)));
}
if (::listen(sockfd, 5) < 0) {
throw std::runtime_error("listen failed on port " +
std::to_string(this->serverPort) + ": " +
std::string(strerror(errno)));
}

FD_ZERO(&activeFdSet);
FD_SET(sockfd, &activeFdSet);
Expand Down Expand Up @@ -172,7 +183,8 @@ void bell::HTTPServer::readFromClient(int clientFd) {
std::stoi(line.substr(16, line.size() - 1));
}
// detect hostname for captive portal
if (line.find("Host: connectivitycheck.gstatic.com") != std::string::npos) {
if (line.find("Host: connectivitycheck.gstatic.com") !=
std::string::npos) {
conn.isCaptivePortal = true;
BELL_LOG(info, "http", "Captive portal request detected");
}
Expand All @@ -182,11 +194,11 @@ void bell::HTTPServer::readFromClient(int clientFd) {
goto READBODY;
} else {
if (!conn.isCaptivePortal) {
findAndHandleRoute(conn.httpMethod, conn.currentLine, clientFd);
findAndHandleRoute(conn.httpMethod,
conn.currentLine, clientFd);
} else {
this->redirectCaptivePortal(clientFd);
}

}
}
}
Expand Down Expand Up @@ -299,7 +311,7 @@ void bell::HTTPServer::redirectCaptivePortal(int connectionFd) {
this->closeConnection(connectionFd);
}

void bell::HTTPServer::redirectTo(const std::string & url, int connectionFd) {
void bell::HTTPServer::redirectTo(const std::string &url, int connectionFd) {
std::lock_guard lock(this->responseMutex);
std::stringstream stream;
stream << "HTTP/1.1 301 Moved Permanently\r\n";
Expand Down
Expand Up @@ -85,7 +85,11 @@ bool SPDIFAudioSink::setParams(uint32_t sampleRate, uint8_t channelCount, uint8_

i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
.sample_rate = (uint32_t)sample_rate,
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
.sample_rate = (uint32_t) sample_rate,
#else
.sample_rate = (int) sample_rate,
#endif
.bits_per_sample = (i2s_bits_per_sample_t)(bitDepth * 2),
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
Expand Down
1 change: 1 addition & 0 deletions components/spotify/cspot/include/ConfigJSON.h
Expand Up @@ -18,6 +18,7 @@ class ConfigJSON

uint16_t volume;
std::string deviceName;
std::string apOverride;
AudioFormat format;
};

Expand Down
6 changes: 3 additions & 3 deletions components/spotify/cspot/include/ConstantParameters.h
Expand Up @@ -7,9 +7,9 @@
extern char deviceId[];

// Hardcoded information sent to spotify servers
const char * const informationString = "cspot";
const char * const brandName = "corn";
const char * const versionString = "cspot-1.0";
const char * const informationString = "cspot-player";
const char * const brandName = "cspot";
const char * const versionString = "cspot-1.1";
const char * const protocolVersion = "2.7.1";
const char * const defaultDeviceName = "CSpot";
const char * const swVersion = "1.0.0";
Expand Down
4 changes: 2 additions & 2 deletions components/spotify/cspot/include/MercuryManager.h
Expand Up @@ -69,7 +69,6 @@ class MercuryManager : public bell::Task
std::unique_ptr<WrappedSemaphore> queueSemaphore;
unsigned long long lastRequestTimestamp = -1;
unsigned long long lastPingTimestamp = -1;
std::atomic<bool> isRunning = false;
uint64_t sequenceId;
uint32_t audioKeySequence;
audioKeyCallback keyCallback;
Expand All @@ -78,10 +77,11 @@ class MercuryManager : public bell::Task
public:
MercuryManager(std::unique_ptr<Session> session);
~MercuryManager();
std::atomic<bool> isRunning = false;
voidCallback reconnectedCallback;
uint16_t audioChunkSequence;
std::shared_ptr<TimeProvider> timeProvider;
std::string countryCode;
char countryCode[2];

bool timeoutHandler();
uint64_t execute(MercuryType method, std::string uri, mercuryCallback &callback, mercuryCallback &subscription, mercuryParts &payload);
Expand Down
8 changes: 4 additions & 4 deletions components/spotify/cspot/include/Player.h
Expand Up @@ -14,17 +14,17 @@
#include "SpotifyTrack.h"
#include "AudioSink.h"
#include <mutex>
#include "Queue.h"
#include "Task.h"

class Player : public bell::Task {
private:
std::shared_ptr<MercuryManager> manager;
std::shared_ptr<SpotifyTrack> currentTrack = nullptr;
SpotifyTrack *currentTrack = nullptr;
SpotifyTrack *nextTrack = nullptr;
std::shared_ptr<AudioSink> audioSink;
std::mutex loadTrackMutex;
// @TODO: Use some actual structure here
bell::Queue<std::shared_ptr<SpotifyTrack>> trackQueue;
WrappedMutex nextTrackMutex;
WrappedMutex currentTrackMutex;
void runTask();

public:
Expand Down
1 change: 1 addition & 0 deletions components/spotify/cspot/include/SpircController.h
Expand Up @@ -24,6 +24,7 @@ enum class CSpotEventType {
PREV,
SEEK,
LOAD,
PLAYBACK_START
};

struct CSpotEvent {
Expand Down
6 changes: 4 additions & 2 deletions components/spotify/cspot/include/SpotifyTrack.h
Expand Up @@ -15,6 +15,7 @@
#include "NanoPBHelper.h"
#include "protobuf/metadata.pb.h"
#include <cassert>
#include <atomic>

struct TrackInfo {
std::string name;
Expand All @@ -33,8 +34,8 @@ class SpotifyTrack
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 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 canPlayTrack();
bool countryListContains(char *countryList, char *country);
bool canPlayTrack(int altIndex);
Track trackInfo;
Episode episodeInfo;

Expand All @@ -45,6 +46,7 @@ class SpotifyTrack
SpotifyTrack(std::shared_ptr<MercuryManager> manager, std::shared_ptr<TrackReference> trackRef, uint32_t position_ms, bool isPaused);
~SpotifyTrack();
uint64_t reqSeqNum = -1;
std::atomic<bool> loaded = false;
std::function<void()> loadedTrackCallback;
std::unique_ptr<ChunkedAudioStream> audioStream;
trackChangedCallback trackInfoReceived;
Expand Down

0 comments on commit 0222dbd

Please sign in to comment.