mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-08 04:27:12 +03:00
catching up (trying to) wiht CSpot
This commit is contained in:
@@ -4,4 +4,4 @@ idf_component_register( SRCS ${srcs}
|
||||
)
|
||||
|
||||
# CMake is just a pile of crap
|
||||
message("overriding ${srcs} !! THIS MUST BE REQUIRED BY ONE COMPONENT BUT NO MAIN !!")
|
||||
message("overriding ${srcs} !! THIS MUST BE REQUIRED BY ONE COMPONENT BUT NOT MAIN !!")
|
||||
@@ -246,8 +246,8 @@ void ShimAudioSink::volumeChanged(uint16_t volume) {
|
||||
cspot.cHandler(CSPOT_VOLUME, volume);
|
||||
}
|
||||
|
||||
void ShimAudioSink::feedPCMFrames(std::vector<uint8_t> &data) {
|
||||
cspot.dHandler(&data[0], data.size());
|
||||
void ShimAudioSink::feedPCMFrames(const uint8_t *data, size_t bytes) {
|
||||
cspot.dHandler(data, bytes);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
class ShimAudioSink : public AudioSink {
|
||||
public:
|
||||
ShimAudioSink(void) { softwareVolumeControl = false; }
|
||||
void feedPCMFrames(std::vector<uint8_t> &data);
|
||||
void feedPCMFrames(const uint8_t *data, size_t bytes);
|
||||
virtual void volumeChanged(uint16_t volume);
|
||||
};
|
||||
|
||||
|
||||
@@ -44,5 +44,6 @@ endif()
|
||||
|
||||
add_library(cspot STATIC ${SOURCES} ${PROTO_SRCS})
|
||||
# PUBLIC to propagate includes from bell to cspot dependents
|
||||
target_compile_definitions(bell PUBLIC PB_ENABLE_MALLOC)
|
||||
target_link_libraries(cspot PUBLIC ${EXTRA_LIBS})
|
||||
target_include_directories(cspot PUBLIC "include" ${GENERATED_INCLUDES} ${NANOPB_INCLUDE_DIRS})
|
||||
|
||||
21
components/spotify/cspot/bell/include/AudioSink.h
Normal file
21
components/spotify/cspot/bell/include/AudioSink.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef AUDIOSINK_H
|
||||
#define AUDIOSINK_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
|
||||
class AudioSink
|
||||
{
|
||||
public:
|
||||
AudioSink() {}
|
||||
virtual ~AudioSink() {}
|
||||
virtual void feedPCMFrames(const uint8_t *buffer, size_t bytes) = 0;
|
||||
virtual void volumeChanged(uint16_t volume) {}
|
||||
// return true if the sink supports rate changing
|
||||
virtual bool setRate(uint16_t sampleRate) { return false; }
|
||||
bool softwareVolumeControl = true;
|
||||
bool usign = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -83,8 +83,6 @@ namespace bell
|
||||
{
|
||||
std::string basenameStr(filename.substr(filename.rfind("/") + 1));
|
||||
unsigned long hash = 5381;
|
||||
int c;
|
||||
|
||||
for (char const &c : basenameStr)
|
||||
{
|
||||
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
|
||||
|
||||
@@ -15,13 +15,13 @@ std::string generateRandomUUID();
|
||||
#include <freertos/FreeRTOS.h>
|
||||
|
||||
#define BELL_SLEEP_MS(ms) vTaskDelay(ms / portTICK_PERIOD_MS)
|
||||
#define BELL_YIELD() vTaskYield()
|
||||
#define BELL_YIELD() taskYIELD()
|
||||
|
||||
#else
|
||||
#include <unistd.h>
|
||||
|
||||
#define BELL_SLEEP_MS(ms) usleep(ms * 1000)
|
||||
#define BELL_YIELD() ()
|
||||
#define BELL_YIELD() ;
|
||||
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#ifndef BELL_BYTE_READER_H
|
||||
#define BELL_BYTE_READER_H
|
||||
|
||||
#include "stdlib.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* A class for reading bytes from a stream. Further implemented in HTTPStream.h
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define BELL_HTTP_CLIENT
|
||||
|
||||
#include "BellSocket.h"
|
||||
#include "ByteStream.h"
|
||||
#include "TCPSocket.h"
|
||||
#include "platform/TLSSocket.h"
|
||||
#include <map>
|
||||
@@ -21,13 +22,15 @@ class HTTPClient {
|
||||
struct HTTPRequest {
|
||||
HTTPMethod method = HTTPMethod::GET;
|
||||
std::string url;
|
||||
std::string body;
|
||||
const char *body = nullptr;
|
||||
const char *contentType = nullptr;
|
||||
std::map<std::string, std::string> headers;
|
||||
std::string contentType;
|
||||
int maxRedirects = -1;
|
||||
std::ostream *dumpFs = nullptr;
|
||||
std::ostream *dumpRawFs = nullptr;
|
||||
};
|
||||
|
||||
struct HTTPResponse {
|
||||
struct HTTPResponse : public ByteStream {
|
||||
std::shared_ptr<bell::Socket> socket;
|
||||
|
||||
std::map<std::string, std::string> headers;
|
||||
@@ -41,18 +44,32 @@ class HTTPClient {
|
||||
bool isComplete = false;
|
||||
bool isRedirect = false;
|
||||
size_t redirectCount = 0;
|
||||
std::ostream *dumpFs = nullptr;
|
||||
std::ostream *dumpRawFs = nullptr;
|
||||
|
||||
void close() {
|
||||
socket->close();
|
||||
free(buf);
|
||||
buf = nullptr;
|
||||
bufPtr = nullptr;
|
||||
}
|
||||
~HTTPResponse();
|
||||
void close() override;
|
||||
|
||||
void readHeaders();
|
||||
size_t read(char *dst, size_t len);
|
||||
size_t read(char *dst, size_t len, bool wait = false);
|
||||
std::string readToString();
|
||||
|
||||
inline size_t skip(size_t len) override {
|
||||
return read((char *)nullptr, len);
|
||||
}
|
||||
inline size_t read(uint8_t *dst, size_t len) override {
|
||||
return read((char *)dst, len);
|
||||
}
|
||||
inline size_t read(uint8_t *dst, size_t len, bool wait) {
|
||||
return read((char *)dst, len, wait);
|
||||
}
|
||||
inline size_t size() override {
|
||||
return contentLength;
|
||||
}
|
||||
inline size_t position() override {
|
||||
return bodyRead;
|
||||
}
|
||||
|
||||
private:
|
||||
char *buf = nullptr; // allocated buffer
|
||||
char *bufPtr = nullptr; // reading pointer within buf
|
||||
@@ -61,16 +78,19 @@ class HTTPClient {
|
||||
size_t chunkRemaining = 0;
|
||||
bool isStreaming = false;
|
||||
size_t readRaw(char *dst);
|
||||
bool skip(size_t len, bool dontRead = false);
|
||||
bool skipRaw(size_t len, bool dontRead = false);
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<struct HTTPClient::HTTPResponse> HTTPResponse_t;
|
||||
|
||||
private:
|
||||
static void executeImpl(const struct HTTPRequest &request, const char *url, struct HTTPResponse *&response);
|
||||
static HTTPResponse_t executeImpl(const struct HTTPRequest &request, HTTPResponse_t response);
|
||||
static bool readHeader(const char *&header, const char *name);
|
||||
|
||||
public:
|
||||
static struct HTTPResponse *execute(const struct HTTPRequest &request);
|
||||
static HTTPResponse_t execute(const struct HTTPRequest &request);
|
||||
};
|
||||
typedef std::unique_ptr<struct HTTPClient::HTTPResponse> HTTPResponse_t;
|
||||
} // namespace bell
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <vector>
|
||||
#include "pb_encode.h"
|
||||
#include "pb_decode.h"
|
||||
#include "HTTPClient.h"
|
||||
#include <string>
|
||||
|
||||
std::vector<uint8_t> pbEncode(const pb_msgdesc_t *fields, const void *src_struct);
|
||||
@@ -41,6 +42,7 @@ void pbDecode(T &result, const pb_msgdesc_t *fields, std::vector<uint8_t> &data)
|
||||
}
|
||||
}
|
||||
|
||||
void pbFree(const pb_msgdesc_t *fields, void *src_struct);
|
||||
const char* pb_encode_to_string(const pb_msgdesc_t *fields, const void *data);
|
||||
pb_istream_t pb_istream_from_http(bell::HTTPClient::HTTPResponse *response, size_t length = 0);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace bell
|
||||
/// Queue
|
||||
std::queue<dataType> m_queue;
|
||||
/// Mutex to controll multiple access
|
||||
std::mutex m_mutex;
|
||||
mutable std::mutex m_mutex;
|
||||
/// Conditional variable used to fire event
|
||||
std::condition_variable m_cv;
|
||||
/// Atomic variable used to terminate immediately wpop and wtpop functions
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace bell
|
||||
hints.ai_protocol = IPPROTO_IP; // no enum : possible value can be read in /etc/protocols
|
||||
hints.ai_flags = AI_CANONNAME | AI_ALL | AI_ADDRCONFIG;
|
||||
|
||||
BELL_LOG(info, "http", "%s %d", host.c_str(), port);
|
||||
// BELL_LOG(info, "http", "%s %d", host.c_str(), port);
|
||||
|
||||
char portStr[6];
|
||||
sprintf(portStr, "%u", port);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
#include <string>
|
||||
|
||||
namespace bell
|
||||
{
|
||||
@@ -51,6 +52,7 @@ namespace bell
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("task on internal %s", this->taskName.c_str());
|
||||
esp_pthread_cfg_t cfg = esp_pthread_get_default_config();
|
||||
cfg.stack_size = stackSize;
|
||||
cfg.inherit_cfg = true;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef AC101AUDIOSINK_H
|
||||
#define AC101AUDIOSINK_H
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include "BufferedAudioSink.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "ac101.h"
|
||||
#include "adac.h"
|
||||
|
||||
class AC101AudioSink : public BufferedAudioSink
|
||||
{
|
||||
public:
|
||||
AC101AudioSink();
|
||||
~AC101AudioSink();
|
||||
void volumeChanged(uint16_t volume);
|
||||
private:
|
||||
adac_s *dac;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef BUFFEREDAUDIOSINK_H
|
||||
#define BUFFEREDAUDIOSINK_H
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include "AudioSink.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
class BufferedAudioSink : public AudioSink
|
||||
{
|
||||
public:
|
||||
void feedPCMFrames(const uint8_t *buffer, size_t bytes);
|
||||
bool setRate(uint16_t sampleRate) override;
|
||||
protected:
|
||||
void startI2sFeed(size_t buf_size = 4096 * 8);
|
||||
void feedPCMFramesInternal(const void *pvItem, size_t xItemSize);
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,105 @@
|
||||
#ifndef ES8388AUDIOSINK_H
|
||||
#define ES8388AUDIOSINK_H
|
||||
|
||||
#include "driver/i2s.h"
|
||||
#include <driver/i2c.h>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include "BufferedAudioSink.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
|
||||
#define ES8388_ADDR 0x20
|
||||
|
||||
#define ACK_CHECK_EN 0x1
|
||||
|
||||
/* ES8388 register */
|
||||
#define ES8388_CONTROL1 0x00
|
||||
#define ES8388_CONTROL2 0x01
|
||||
#define ES8388_CHIPPOWER 0x02
|
||||
#define ES8388_ADCPOWER 0x03
|
||||
#define ES8388_DACPOWER 0x04
|
||||
#define ES8388_CHIPLOPOW1 0x05
|
||||
#define ES8388_CHIPLOPOW2 0x06
|
||||
#define ES8388_ANAVOLMANAG 0x07
|
||||
#define ES8388_MASTERMODE 0x08
|
||||
|
||||
/* ADC */
|
||||
#define ES8388_ADCCONTROL1 0x09
|
||||
#define ES8388_ADCCONTROL2 0x0a
|
||||
#define ES8388_ADCCONTROL3 0x0b
|
||||
#define ES8388_ADCCONTROL4 0x0c
|
||||
#define ES8388_ADCCONTROL5 0x0d
|
||||
#define ES8388_ADCCONTROL6 0x0e
|
||||
#define ES8388_ADCCONTROL7 0x0f
|
||||
#define ES8388_ADCCONTROL8 0x10
|
||||
#define ES8388_ADCCONTROL9 0x11
|
||||
#define ES8388_ADCCONTROL10 0x12
|
||||
#define ES8388_ADCCONTROL11 0x13
|
||||
#define ES8388_ADCCONTROL12 0x14
|
||||
#define ES8388_ADCCONTROL13 0x15
|
||||
#define ES8388_ADCCONTROL14 0x16
|
||||
|
||||
/* DAC */
|
||||
#define ES8388_DACCONTROL1 0x17
|
||||
#define ES8388_DACCONTROL2 0x18
|
||||
#define ES8388_DACCONTROL3 0x19
|
||||
#define ES8388_DACCONTROL4 0x1a
|
||||
#define ES8388_DACCONTROL5 0x1b
|
||||
#define ES8388_DACCONTROL6 0x1c
|
||||
#define ES8388_DACCONTROL7 0x1d
|
||||
#define ES8388_DACCONTROL8 0x1e
|
||||
#define ES8388_DACCONTROL9 0x1f
|
||||
#define ES8388_DACCONTROL10 0x20
|
||||
#define ES8388_DACCONTROL11 0x21
|
||||
#define ES8388_DACCONTROL12 0x22
|
||||
#define ES8388_DACCONTROL13 0x23
|
||||
#define ES8388_DACCONTROL14 0x24
|
||||
#define ES8388_DACCONTROL15 0x25
|
||||
#define ES8388_DACCONTROL16 0x26
|
||||
#define ES8388_DACCONTROL17 0x27
|
||||
#define ES8388_DACCONTROL18 0x28
|
||||
#define ES8388_DACCONTROL19 0x29
|
||||
#define ES8388_DACCONTROL20 0x2a
|
||||
#define ES8388_DACCONTROL21 0x2b
|
||||
#define ES8388_DACCONTROL22 0x2c
|
||||
#define ES8388_DACCONTROL23 0x2d
|
||||
#define ES8388_DACCONTROL24 0x2e
|
||||
#define ES8388_DACCONTROL25 0x2f
|
||||
#define ES8388_DACCONTROL26 0x30
|
||||
#define ES8388_DACCONTROL27 0x31
|
||||
#define ES8388_DACCONTROL28 0x32
|
||||
#define ES8388_DACCONTROL29 0x33
|
||||
#define ES8388_DACCONTROL30 0x34
|
||||
|
||||
class ES8388AudioSink : public BufferedAudioSink
|
||||
{
|
||||
public:
|
||||
ES8388AudioSink();
|
||||
~ES8388AudioSink();
|
||||
|
||||
bool begin(int sda = -1, int scl = -1, uint32_t frequency = 400000U);
|
||||
|
||||
enum ES8388_OUT
|
||||
{
|
||||
ES_MAIN, // this is the DAC output volume (both outputs)
|
||||
ES_OUT1, // this is the additional gain for OUT1
|
||||
ES_OUT2 // this is the additional gain for OUT2
|
||||
};
|
||||
|
||||
void mute(const ES8388_OUT out, const bool muted);
|
||||
void volume(const ES8388_OUT out, const uint8_t vol);
|
||||
|
||||
void writeReg(uint8_t reg_add, uint8_t data);
|
||||
private:
|
||||
i2c_config_t i2c_config;
|
||||
i2c_port_t i2c_port = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef ES9018AUDIOSINK_H
|
||||
#define ES9018AUDIOSINK_H
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include "BufferedAudioSink.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
class ES9018AudioSink : public BufferedAudioSink
|
||||
{
|
||||
public:
|
||||
ES9018AudioSink();
|
||||
~ES9018AudioSink();
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef INTERNALAUDIOSINK_H
|
||||
#define INTERNALAUDIOSINK_H
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include "BufferedAudioSink.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
class InternalAudioSink : public BufferedAudioSink
|
||||
{
|
||||
public:
|
||||
InternalAudioSink();
|
||||
~InternalAudioSink();
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef PCM5102AUDIOSINK_H
|
||||
#define PCM5102AUDIOSINK_H
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include "BufferedAudioSink.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
class PCM5102AudioSink : public BufferedAudioSink
|
||||
{
|
||||
public:
|
||||
PCM5102AudioSink();
|
||||
~PCM5102AudioSink();
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef SPDIFAUDIOSINK_H
|
||||
#define SPDIFAUDIOSINK_H
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include "BufferedAudioSink.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
class SPDIFAudioSink : public BufferedAudioSink
|
||||
{
|
||||
private:
|
||||
uint8_t spdifPin;
|
||||
public:
|
||||
explicit SPDIFAudioSink(uint8_t spdifPin);
|
||||
~SPDIFAudioSink() override;
|
||||
void feedPCMFrames(const uint8_t *buffer, size_t bytes) override;
|
||||
void initialize(uint16_t sampleRate);
|
||||
bool setRate(uint16_t sampleRate) override;
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef TAS5711AUDIOSINK_H
|
||||
#define TAS5711AUDIOSINK_H
|
||||
|
||||
|
||||
#include "driver/i2s.h"
|
||||
#include <driver/i2c.h>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include "BufferedAudioSink.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
class TAS5711AudioSink : public BufferedAudioSink
|
||||
{
|
||||
public:
|
||||
TAS5711AudioSink();
|
||||
~TAS5711AudioSink();
|
||||
|
||||
|
||||
void writeReg(uint8_t reg, uint8_t value);
|
||||
private:
|
||||
i2c_config_t i2c_config;
|
||||
i2c_port_t i2c_port = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
176
components/spotify/cspot/bell/include/sinks/esp/ac101.h
Normal file
176
components/spotify/cspot/bell/include/sinks/esp/ac101.h
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __AC101_H__
|
||||
#define __AC101_H__
|
||||
|
||||
#include "esp_types.h"
|
||||
|
||||
#define AC101_ADDR 0x1a /*!< Device address*/
|
||||
|
||||
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
|
||||
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
|
||||
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
|
||||
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
|
||||
#define ACK_VAL 0x0 /*!< I2C ack value */
|
||||
#define NACK_VAL 0x1 /*!< I2C nack value */
|
||||
|
||||
#define CHIP_AUDIO_RS 0x00
|
||||
#define PLL_CTRL1 0x01
|
||||
#define PLL_CTRL2 0x02
|
||||
#define SYSCLK_CTRL 0x03
|
||||
#define MOD_CLK_ENA 0x04
|
||||
#define MOD_RST_CTRL 0x05
|
||||
#define I2S_SR_CTRL 0x06
|
||||
#define I2S1LCK_CTRL 0x10
|
||||
#define I2S1_SDOUT_CTRL 0x11
|
||||
#define I2S1_SDIN_CTRL 0x12
|
||||
#define I2S1_MXR_SRC 0x13
|
||||
#define I2S1_VOL_CTRL1 0x14
|
||||
#define I2S1_VOL_CTRL2 0x15
|
||||
#define I2S1_VOL_CTRL3 0x16
|
||||
#define I2S1_VOL_CTRL4 0x17
|
||||
#define I2S1_MXR_GAIN 0x18
|
||||
#define ADC_DIG_CTRL 0x40
|
||||
#define ADC_VOL_CTRL 0x41
|
||||
#define HMIC_CTRL1 0x44
|
||||
#define HMIC_CTRL2 0x45
|
||||
#define HMIC_STATUS 0x46
|
||||
#define DAC_DIG_CTRL 0x48
|
||||
#define DAC_VOL_CTRL 0x49
|
||||
#define DAC_MXR_SRC 0x4c
|
||||
#define DAC_MXR_GAIN 0x4d
|
||||
#define ADC_ANA_CTRL 0x50
|
||||
#define ADC_SRC 0x51
|
||||
#define ADC_SRCBST_CTRL 0x52
|
||||
#define OMIXER_DACA_CTRL 0x53
|
||||
#define OMIXER_SR 0x54
|
||||
#define OMIXER_BST1_CTRL 0x55
|
||||
#define HPOUT_CTRL 0x56
|
||||
#define SPKOUT_CTRL 0x58
|
||||
#define AC_DAC_DAPCTRL 0xa0
|
||||
#define AC_DAC_DAPHHPFC 0xa1
|
||||
#define AC_DAC_DAPLHPFC 0xa2
|
||||
#define AC_DAC_DAPLHAVC 0xa3
|
||||
#define AC_DAC_DAPLLAVC 0xa4
|
||||
#define AC_DAC_DAPRHAVC 0xa5
|
||||
#define AC_DAC_DAPRLAVC 0xa6
|
||||
#define AC_DAC_DAPHGDEC 0xa7
|
||||
#define AC_DAC_DAPLGDEC 0xa8
|
||||
#define AC_DAC_DAPHGATC 0xa9
|
||||
#define AC_DAC_DAPLGATC 0xaa
|
||||
#define AC_DAC_DAPHETHD 0xab
|
||||
#define AC_DAC_DAPLETHD 0xac
|
||||
#define AC_DAC_DAPHGKPA 0xad
|
||||
#define AC_DAC_DAPLGKPA 0xae
|
||||
#define AC_DAC_DAPHGOPA 0xaf
|
||||
#define AC_DAC_DAPLGOPA 0xb0
|
||||
#define AC_DAC_DAPOPT 0xb1
|
||||
#define DAC_DAP_ENA 0xb5
|
||||
|
||||
typedef enum{
|
||||
SAMPLE_RATE_8000 = 0x0000,
|
||||
SAMPLE_RATE_11052 = 0x1000,
|
||||
SAMPLE_RATE_12000 = 0x2000,
|
||||
SAMPLE_RATE_16000 = 0x3000,
|
||||
SAMPLE_RATE_22050 = 0x4000,
|
||||
SAMPLE_RATE_24000 = 0x5000,
|
||||
SAMPLE_RATE_32000 = 0x6000,
|
||||
SAMPLE_RATE_44100 = 0x7000,
|
||||
SAMPLE_RATE_48000 = 0x8000,
|
||||
SAMPLE_RATE_96000 = 0x9000,
|
||||
SAMPLE_RATE_192000 = 0xa000,
|
||||
} ac_adda_fs_i2s1_t;
|
||||
|
||||
typedef enum{
|
||||
BCLK_DIV_1 = 0x0,
|
||||
BCLK_DIV_2 = 0x1,
|
||||
BCLK_DIV_4 = 0x2,
|
||||
BCLK_DIV_6 = 0x3,
|
||||
BCLK_DIV_8 = 0x4,
|
||||
BCLK_DIV_12 = 0x5,
|
||||
BCLK_DIV_16 = 0x6,
|
||||
BCLK_DIV_24 = 0x7,
|
||||
BCLK_DIV_32 = 0x8,
|
||||
BCLK_DIV_48 = 0x9,
|
||||
BCLK_DIV_64 = 0xa,
|
||||
BCLK_DIV_96 = 0xb,
|
||||
BCLK_DIV_128 = 0xc,
|
||||
BCLK_DIV_192 = 0xd,
|
||||
} ac_i2s1_bclk_div_t;
|
||||
|
||||
typedef enum{
|
||||
LRCK_DIV_16 =0x0,
|
||||
LRCK_DIV_32 =0x1,
|
||||
LRCK_DIV_64 =0x2,
|
||||
LRCK_DIV_128 =0x3,
|
||||
LRCK_DIV_256 =0x4,
|
||||
} ac_i2s1_lrck_div_t;
|
||||
|
||||
typedef enum {
|
||||
BIT_LENGTH_8_BITS = 0x00,
|
||||
BIT_LENGTH_16_BITS = 0x01,
|
||||
BIT_LENGTH_20_BITS = 0x02,
|
||||
BIT_LENGTH_24_BITS = 0x03,
|
||||
} ac_bits_length_t;
|
||||
|
||||
typedef enum {
|
||||
AC_MODE_MIN = -1,
|
||||
AC_MODE_SLAVE = 0x00,
|
||||
AC_MODE_MASTER = 0x01,
|
||||
AC_MODE_MAX,
|
||||
} ac_mode_sm_t;
|
||||
|
||||
typedef enum {
|
||||
AC_MODULE_MIN = -1,
|
||||
AC_MODULE_ADC = 0x01,
|
||||
AC_MODULE_DAC = 0x02,
|
||||
AC_MODULE_ADC_DAC = 0x03,
|
||||
AC_MODULE_LINE = 0x04,
|
||||
AC_MODULE_MAX
|
||||
} ac_module_t;
|
||||
|
||||
typedef enum{
|
||||
SRC_MIC1 = 1,
|
||||
SRC_MIC2 = 2,
|
||||
SRC_LINEIN = 3,
|
||||
}ac_output_mixer_source_t;
|
||||
|
||||
typedef enum {
|
||||
GAIN_N45DB = 0,
|
||||
GAIN_N30DB = 1,
|
||||
GAIN_N15DB = 2,
|
||||
GAIN_0DB = 3,
|
||||
GAIN_15DB = 4,
|
||||
GAIN_30DB = 5,
|
||||
GAIN_45DB = 6,
|
||||
GAIN_60DB = 7,
|
||||
} ac_output_mixer_gain_t;
|
||||
|
||||
typedef struct {
|
||||
ac_i2s1_bclk_div_t bclk_div; /*!< bits clock divide */
|
||||
ac_i2s1_lrck_div_t lclk_div; /*!< WS clock divide */
|
||||
} ac_i2s_clock_t;
|
||||
|
||||
#endif
|
||||
28
components/spotify/cspot/bell/include/sinks/esp/adac.h
Normal file
28
components/spotify/cspot/bell/include/sinks/esp/adac.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Squeezelite for esp32
|
||||
*
|
||||
* (c) Sebastien 2019
|
||||
* Philippe G. 2019, philippe_44@outlook.com
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "driver/i2s.h"
|
||||
|
||||
typedef enum { ADAC_ON = 0, ADAC_STANDBY, ADAC_OFF } adac_power_e;
|
||||
|
||||
struct adac_s {
|
||||
bool (*init)(int i2c_port_num, int i2s_num, i2s_config_t *config);
|
||||
void (*deinit)(void);
|
||||
void (*power)(adac_power_e mode);
|
||||
void (*speaker)(bool active);
|
||||
void (*headset)(bool active);
|
||||
void (*volume)(unsigned left, unsigned right);
|
||||
};
|
||||
|
||||
extern struct adac_s dac_tas57xx;
|
||||
extern struct adac_s dac_a1s;
|
||||
extern struct adac_s dac_external;
|
||||
124
components/spotify/cspot/bell/include/sinks/unix/ALSAAudioSink.h
Normal file
124
components/spotify/cspot/bell/include/sinks/unix/ALSAAudioSink.h
Normal file
@@ -0,0 +1,124 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include "AudioSink.h"
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <stdio.h>
|
||||
#include <Task.h>
|
||||
#include <unistd.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#define PCM_DEVICE "default"
|
||||
|
||||
template <typename T, int SIZE>
|
||||
class RingbufferPointer
|
||||
{
|
||||
typedef std::unique_ptr<T> TPointer;
|
||||
|
||||
public:
|
||||
explicit RingbufferPointer()
|
||||
{
|
||||
// create objects
|
||||
for (int i = 0; i < SIZE; i++)
|
||||
{
|
||||
buf_[i] = std::make_unique<T>();
|
||||
}
|
||||
}
|
||||
|
||||
bool push(TPointer &item)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (full())
|
||||
return false;
|
||||
|
||||
std::swap(buf_[head_], item);
|
||||
|
||||
if (full_)
|
||||
tail_ = (tail_ + 1) % max_size_;
|
||||
|
||||
head_ = (head_ + 1) % max_size_;
|
||||
full_ = head_ == tail_;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pop(TPointer &item)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (empty())
|
||||
return false;
|
||||
|
||||
std::swap(buf_[tail_], item);
|
||||
|
||||
full_ = false;
|
||||
tail_ = (tail_ + 1) % max_size_;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
head_ = tail_;
|
||||
full_ = false;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return (!full_ && (head_ == tail_));
|
||||
}
|
||||
|
||||
bool full() const
|
||||
{
|
||||
return full_;
|
||||
}
|
||||
|
||||
int capacity() const
|
||||
{
|
||||
return max_size_;
|
||||
}
|
||||
|
||||
int size() const
|
||||
{
|
||||
int size = max_size_;
|
||||
|
||||
if (!full_)
|
||||
{
|
||||
if (head_ >= tail_)
|
||||
size = head_ - tail_;
|
||||
else
|
||||
size = max_size_ + head_ - tail_;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
private:
|
||||
TPointer buf_[SIZE];
|
||||
|
||||
std::mutex mutex_;
|
||||
int head_ = 0;
|
||||
int tail_ = 0;
|
||||
const int max_size_ = SIZE;
|
||||
bool full_ = 0;
|
||||
};
|
||||
|
||||
class ALSAAudioSink : public AudioSink, public bell::Task
|
||||
{
|
||||
public:
|
||||
ALSAAudioSink();
|
||||
~ALSAAudioSink();
|
||||
void feedPCMFrames(const uint8_t *buffer, size_t bytes);
|
||||
void runTask();
|
||||
|
||||
private:
|
||||
RingbufferPointer<std::vector<uint8_t>, 3> ringbuffer;
|
||||
unsigned int pcm;
|
||||
snd_pcm_t *pcm_handle;
|
||||
snd_pcm_hw_params_t *params;
|
||||
snd_pcm_uframes_t frames;
|
||||
int buff_size;
|
||||
std::vector<uint8_t> buff;
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include "AudioSink.h"
|
||||
|
||||
class NamedPipeAudioSink : public AudioSink
|
||||
{
|
||||
public:
|
||||
NamedPipeAudioSink();
|
||||
~NamedPipeAudioSink();
|
||||
void feedPCMFrames(const uint8_t *buffer, size_t bytes);
|
||||
|
||||
private:
|
||||
std::ofstream namedPipeFile;
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "portaudio.h"
|
||||
#include <stdint.h>
|
||||
#include <iostream>
|
||||
#include "AudioSink.h"
|
||||
|
||||
class PortAudioSink : public AudioSink
|
||||
{
|
||||
public:
|
||||
PortAudioSink();
|
||||
~PortAudioSink();
|
||||
void feedPCMFrames(const uint8_t *buffer, size_t bytes);
|
||||
void initialize(uint16_t sampleRate);
|
||||
bool setRate(uint16_t sampleRate) override;
|
||||
|
||||
private:
|
||||
PaStream *stream;
|
||||
};
|
||||
@@ -5,14 +5,31 @@
|
||||
|
||||
using namespace bell;
|
||||
|
||||
struct HTTPClient::HTTPResponse *HTTPClient::execute(const struct HTTPRequest &request) {
|
||||
auto *response = new HTTPResponse();
|
||||
auto *url = request.url.c_str();
|
||||
HTTPClient::executeImpl(request, url, response);
|
||||
return response;
|
||||
void HTTPClient::HTTPResponse::close() {
|
||||
socket = nullptr;
|
||||
if (buf)
|
||||
free(buf);
|
||||
buf = nullptr;
|
||||
bufPtr = nullptr;
|
||||
}
|
||||
HTTPClient::HTTPResponse::~HTTPResponse() {
|
||||
socket = nullptr;
|
||||
if (buf)
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void HTTPClient::executeImpl(const struct HTTPRequest &request, const char *url, struct HTTPResponse *&response) {
|
||||
HTTPResponse_t HTTPClient::execute(const struct HTTPRequest &request) {
|
||||
auto response = std::make_unique<HTTPResponse>();
|
||||
response->dumpFs = request.dumpFs;
|
||||
response->dumpRawFs = request.dumpRawFs;
|
||||
return HTTPClient::executeImpl(request, std::move(response));
|
||||
}
|
||||
|
||||
HTTPResponse_t HTTPClient::executeImpl(const struct HTTPRequest &request, HTTPResponse_t response) {
|
||||
const char *url = request.url.c_str();
|
||||
if (response->isRedirect) {
|
||||
url = response->location.c_str();
|
||||
}
|
||||
bool https = url[4] == 's';
|
||||
uint16_t port = https ? 443 : 80;
|
||||
auto *hostname = url + (https ? 8 : 7);
|
||||
@@ -45,9 +62,9 @@ void HTTPClient::executeImpl(const struct HTTPRequest &request, const char *url,
|
||||
stream << path << " HTTP/1.1" << endl;
|
||||
stream << "Host: " << hostnameStr << ":" << port << endl;
|
||||
stream << "Accept: */*" << endl;
|
||||
if (!request.body.empty()) {
|
||||
if (request.body != nullptr) {
|
||||
stream << "Content-Type: " << request.contentType << endl;
|
||||
stream << "Content-Length: " << request.body.size() << endl;
|
||||
stream << "Content-Length: " << strlen(request.body) << endl;
|
||||
}
|
||||
for (const auto &header : request.headers) {
|
||||
stream << header.first << ": " << header.second << endl;
|
||||
@@ -60,9 +77,7 @@ void HTTPClient::executeImpl(const struct HTTPRequest &request, const char *url,
|
||||
if (len != data.size()) {
|
||||
response->close();
|
||||
BELL_LOG(error, "http", "Writing failed: wrote %d of %d bytes", len, data.size());
|
||||
free(response);
|
||||
response = nullptr;
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
response->readHeaders();
|
||||
@@ -70,8 +85,9 @@ void HTTPClient::executeImpl(const struct HTTPRequest &request, const char *url,
|
||||
if (response->isRedirect && (request.maxRedirects < 0 || response->redirectCount < request.maxRedirects)) {
|
||||
response->redirectCount++;
|
||||
response->close(); // close the previous socket
|
||||
HTTPClient::executeImpl(request, response->location.c_str(), response);
|
||||
return HTTPClient::executeImpl(request, std::move(response));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
bool HTTPClient::readHeader(const char *&header, const char *name) {
|
||||
@@ -87,8 +103,9 @@ bool HTTPClient::readHeader(const char *&header, const char *name) {
|
||||
|
||||
size_t HTTPClient::HTTPResponse::readRaw(char *dst) {
|
||||
size_t len = this->socket->read((uint8_t *)dst, BUF_SIZE);
|
||||
if (dumpRawFs)
|
||||
dumpRawFs->write(dst, (long)len);
|
||||
// BELL_LOG(debug, "http", "Read %d bytes", len);
|
||||
this->bodyRead += len; // after reading headers this gets overwritten
|
||||
dst[len] = '\0';
|
||||
return len;
|
||||
}
|
||||
@@ -128,7 +145,6 @@ void HTTPClient::HTTPResponse::readHeaders() {
|
||||
if (lineEnd + 2 < this->buf + len) {
|
||||
this->bufPtr = lineEnd + 2;
|
||||
this->bufRemaining = len - (this->bufPtr - this->buf);
|
||||
this->bodyRead = this->bufRemaining;
|
||||
this->isStreaming =
|
||||
!this->isComplete && !this->contentLength && (len < BUF_SIZE || this->socket->poll() == 0);
|
||||
}
|
||||
@@ -151,7 +167,7 @@ void HTTPClient::HTTPResponse::readHeaders() {
|
||||
this->isRedirect = true;
|
||||
this->location = std::string(header);
|
||||
} else {
|
||||
char *colonPtr = (char*) strchr(header, ':');
|
||||
auto *colonPtr = strchr((char *)header, ':');
|
||||
if (colonPtr) {
|
||||
auto *valuePtr = colonPtr + 1;
|
||||
while (*valuePtr == ' ')
|
||||
@@ -166,10 +182,10 @@ void HTTPClient::HTTPResponse::readHeaders() {
|
||||
lineBuf.clear();
|
||||
line = lineEnd + 2; // skip \r\n
|
||||
} while (true);
|
||||
} while (!complete);
|
||||
} while (!complete && len); // if len == 0, the connection is closed
|
||||
}
|
||||
|
||||
bool HTTPClient::HTTPResponse::skip(size_t len, bool dontRead) {
|
||||
bool HTTPClient::HTTPResponse::skipRaw(size_t len, bool dontRead) {
|
||||
size_t skip = 0;
|
||||
if (len > bufRemaining) {
|
||||
skip = len - bufRemaining;
|
||||
@@ -184,7 +200,7 @@ bool HTTPClient::HTTPResponse::skip(size_t len, bool dontRead) {
|
||||
}
|
||||
bufRemaining = this->readRaw(this->buf);
|
||||
if (!bufRemaining)
|
||||
return false; // no more data - shouldn't happen for valid responses
|
||||
return false; // if len == 0, the connection is closed
|
||||
bufPtr = this->buf + skip;
|
||||
bufRemaining -= skip;
|
||||
if (!contentLength && bufRemaining < BUF_SIZE) {
|
||||
@@ -195,16 +211,15 @@ bool HTTPClient::HTTPResponse::skip(size_t len, bool dontRead) {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t HTTPClient::HTTPResponse::read(char *dst, size_t toRead) {
|
||||
size_t HTTPClient::HTTPResponse::read(char *dst, size_t toRead, bool wait) {
|
||||
if (isComplete) {
|
||||
// end of chunked stream was found OR complete body was read
|
||||
dst[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
auto *dstStart = dst;
|
||||
auto *dstStart = dst ? dst : nullptr;
|
||||
size_t read = 0;
|
||||
while (toRead) { // this loop ends after original toRead
|
||||
skip(0); // ensure the buffer contains data, wait if necessary
|
||||
skipRaw(0); // ensure the buffer contains data, wait if necessary
|
||||
if (isChunked && !chunkRemaining) {
|
||||
if (*bufPtr == '0') { // all chunks were read *and emitted*
|
||||
isComplete = true;
|
||||
@@ -213,7 +228,7 @@ size_t HTTPClient::HTTPResponse::read(char *dst, size_t toRead) {
|
||||
auto *endPtr = bufPtr;
|
||||
if (strchr(bufPtr, '\r') == nullptr) { // buf doesn't contain complete chunk size
|
||||
auto size = std::string(bufPtr, bufPtr + bufRemaining); // take the rest of the buffer
|
||||
if (!skip(bufRemaining)) // skip the rest, read another buf
|
||||
if (!skipRaw(bufRemaining)) // skip the rest, read another buf
|
||||
break; // -> no more data
|
||||
endPtr = strchr(bufPtr, '\r'); // find the end of the actual number
|
||||
if (endPtr == nullptr) // something's wrong
|
||||
@@ -223,41 +238,51 @@ size_t HTTPClient::HTTPResponse::read(char *dst, size_t toRead) {
|
||||
} else {
|
||||
chunkRemaining = strtol(bufPtr, &endPtr, 16); // read the hex size
|
||||
}
|
||||
if (!skip(endPtr - bufPtr + 2)) // skip the size and \r\n
|
||||
break; // -> no more data, break out of main loop
|
||||
if (!skipRaw(endPtr - bufPtr + 2)) // skip the size and \r\n
|
||||
break; // -> no more data, break out of main loop
|
||||
} else if (contentLength && !chunkRemaining) {
|
||||
chunkRemaining = contentLength;
|
||||
}
|
||||
|
||||
while (chunkRemaining && toRead) {
|
||||
size_t count = std::min(toRead, std::min(bufRemaining, chunkRemaining));
|
||||
strncpy(dst, bufPtr, count);
|
||||
dst += count; // move the dst pointer
|
||||
if (dst) {
|
||||
memcpy(dst, bufPtr, count);
|
||||
dst += count; // move the dst pointer
|
||||
}
|
||||
read += count; // increment read counter
|
||||
bodyRead += count; // increment total response size
|
||||
chunkRemaining -= count; // decrease chunk remaining size
|
||||
toRead -= count; // decrease local remaining size
|
||||
if (!skip(count)) { // eat some buffer
|
||||
if (!skipRaw(count)) { // eat some buffer
|
||||
toRead = 0; // -> no more data, break out of main loop
|
||||
break;
|
||||
}
|
||||
if (isChunked && !chunkRemaining && !skip(2, isStreaming)) // skip the \r\n for chunked encoding
|
||||
toRead = 0; // -> no more data, break out of main loop
|
||||
if (isChunked && !chunkRemaining) { // bufPtr is on the end of chunk
|
||||
if (!skipRaw(2, isStreaming)) // skip the \r\n for chunked encoding
|
||||
toRead = 0; // -> no more data, break out of main loop
|
||||
if (bufRemaining > 1 && bufPtr[0] == '0' && bufPtr[1] == '\r') // this is the last chunk
|
||||
isComplete = true;
|
||||
}
|
||||
}
|
||||
if (isStreaming && !bufRemaining) { // stream with no buffer available, just yield the current chunk
|
||||
if (isStreaming && !bufRemaining && !wait) { // stream with no buffer available, just yield the current chunk
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isChunked && contentLength && !chunkRemaining)
|
||||
isComplete = true;
|
||||
isComplete = true; // entire response was read
|
||||
if (dumpFs && dstStart)
|
||||
dumpFs->write(dstStart, (long)read);
|
||||
// BELL_LOG(debug, "http", "Read %d of %d bytes", bodyRead, contentLength);
|
||||
dstStart[read] = '\0';
|
||||
return read;
|
||||
}
|
||||
|
||||
std::string HTTPClient::HTTPResponse::readToString() {
|
||||
if (this->contentLength) {
|
||||
std::string result(this->contentLength, '\0');
|
||||
this->read(result.data(), this->contentLength);
|
||||
auto *data = result.data();
|
||||
auto len = this->read(data, this->contentLength);
|
||||
data[len] = '\0';
|
||||
this->close();
|
||||
return result;
|
||||
}
|
||||
@@ -266,6 +291,7 @@ std::string HTTPClient::HTTPResponse::readToString() {
|
||||
size_t len;
|
||||
do {
|
||||
len = this->read(buffer, BUF_SIZE);
|
||||
buffer[len] = '\0';
|
||||
result += std::string(buffer);
|
||||
} while (len);
|
||||
this->close();
|
||||
|
||||
@@ -155,6 +155,7 @@ size_t bell::HTTPStream::read(uint8_t *buf, size_t nbytes)
|
||||
if (status != StreamStatus::READING_DATA)
|
||||
{
|
||||
BELL_LOG(error, "http", "Not ready to read data");
|
||||
exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,10 +37,6 @@ void packString(char *&dst, std::string stringToPack)
|
||||
strcpy(dst, stringToPack.c_str());
|
||||
}
|
||||
|
||||
void pbFree(const pb_msgdesc_t *fields, void *src_struct) {
|
||||
pb_release(fields, src_struct);
|
||||
}
|
||||
|
||||
pb_bytes_array_t* vectorToPbArray(const std::vector<uint8_t>& vectorToPack)
|
||||
{
|
||||
auto size = static_cast<pb_size_t>(vectorToPack.size());
|
||||
@@ -53,4 +49,34 @@ pb_bytes_array_t* vectorToPbArray(const std::vector<uint8_t>& vectorToPack)
|
||||
|
||||
std::vector<uint8_t> pbArrayToVector(pb_bytes_array_t* pbArray) {
|
||||
return std::vector<uint8_t>(pbArray->bytes, pbArray->bytes + pbArray->size);
|
||||
}
|
||||
}
|
||||
|
||||
const char *pb_encode_to_string(const pb_msgdesc_t *fields, const void *data) {
|
||||
size_t len;
|
||||
pb_get_encoded_size(&len, fields, data);
|
||||
auto *buf = static_cast<uint8_t *>(malloc(len + 1));
|
||||
auto ostream = pb_ostream_from_buffer(buf, len);
|
||||
pb_encode(&ostream, fields, data);
|
||||
buf[len] = '\0';
|
||||
return reinterpret_cast<const char *>(buf);
|
||||
}
|
||||
|
||||
static bool pb_read_from_http(pb_istream_t *stream, pb_byte_t *buf, size_t count) {
|
||||
auto *response = (bell::HTTPClient::HTTPResponse *)stream->state;
|
||||
size_t len = response->read(buf, count, /* wait */ true);
|
||||
if (response->isComplete)
|
||||
stream->bytes_left = count; // count is subtracted after the callback
|
||||
return len == count;
|
||||
}
|
||||
|
||||
pb_istream_t pb_istream_from_http(bell::HTTPClient::HTTPResponse *response, size_t length) {
|
||||
if (!length)
|
||||
length = response->contentLength;
|
||||
if (!length)
|
||||
length = SIZE_MAX;
|
||||
return {
|
||||
.callback = &pb_read_from_http,
|
||||
.state = response,
|
||||
.bytes_left = length,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
#include "AC101AudioSink.h"
|
||||
|
||||
#include "driver/i2s.h"
|
||||
|
||||
AC101AudioSink::AC101AudioSink()
|
||||
{
|
||||
// Disable software volume control, all handled by ::volumeChanged
|
||||
softwareVolumeControl = false;
|
||||
|
||||
i2s_config_t i2s_config = {
|
||||
|
||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX
|
||||
.sample_rate = 44100,
|
||||
.bits_per_sample = (i2s_bits_per_sample_t)16,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels
|
||||
.communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_I2S,
|
||||
.intr_alloc_flags = 0, //Default interrupt priority
|
||||
.dma_buf_count = 8,
|
||||
.dma_buf_len = 512,
|
||||
.use_apll = true,
|
||||
.tx_desc_auto_clear = true //Auto clear tx descriptor on underflow
|
||||
};
|
||||
|
||||
i2s_pin_config_t pin_config = {
|
||||
.bck_io_num = 27,
|
||||
.ws_io_num = 26,
|
||||
.data_out_num = 25,
|
||||
.data_in_num = -1 //Not used
|
||||
};
|
||||
|
||||
dac = &dac_a1s;
|
||||
|
||||
dac->init(0, 0, &i2s_config);
|
||||
dac->speaker(false);
|
||||
dac->power(ADAC_ON);
|
||||
|
||||
startI2sFeed();
|
||||
}
|
||||
|
||||
AC101AudioSink::~AC101AudioSink()
|
||||
{
|
||||
}
|
||||
|
||||
void AC101AudioSink::volumeChanged(uint16_t volume) {
|
||||
dac->volume(volume, volume);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#include "BufferedAudioSink.h"
|
||||
|
||||
#include "driver/i2s.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/ringbuf.h"
|
||||
|
||||
RingbufHandle_t dataBuffer;
|
||||
|
||||
static void i2sFeed(void *pvParameters)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
size_t itemSize;
|
||||
char *item = (char *)xRingbufferReceiveUpTo(dataBuffer, &itemSize, portMAX_DELAY, 512);
|
||||
if (item != NULL)
|
||||
{
|
||||
size_t written = 0;
|
||||
while (written < itemSize)
|
||||
{
|
||||
i2s_write((i2s_port_t)0, item, itemSize, &written, portMAX_DELAY);
|
||||
}
|
||||
vRingbufferReturnItem(dataBuffer, (void *)item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BufferedAudioSink::startI2sFeed(size_t buf_size)
|
||||
{
|
||||
dataBuffer = xRingbufferCreate(buf_size, RINGBUF_TYPE_BYTEBUF);
|
||||
xTaskCreatePinnedToCore(&i2sFeed, "i2sFeed", 4096, NULL, 10, NULL, tskNO_AFFINITY);
|
||||
}
|
||||
|
||||
void BufferedAudioSink::feedPCMFrames(const uint8_t *buffer, size_t bytes)
|
||||
{
|
||||
feedPCMFramesInternal(buffer, bytes);
|
||||
}
|
||||
|
||||
void BufferedAudioSink::feedPCMFramesInternal(const void *pvItem, size_t xItemSize)
|
||||
{
|
||||
xRingbufferSend(dataBuffer, pvItem, xItemSize, portMAX_DELAY);
|
||||
}
|
||||
|
||||
bool BufferedAudioSink::setRate(uint16_t sampleRate) {
|
||||
i2s_set_sample_rates((i2s_port_t)0, sampleRate);
|
||||
return true;
|
||||
}
|
||||
155
components/spotify/cspot/bell/src/sinks/esp/ES8388AudioSink.cpp
Normal file
155
components/spotify/cspot/bell/src/sinks/esp/ES8388AudioSink.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
#include "ES8388AudioSink.h"
|
||||
|
||||
struct es8388_cmd_s {
|
||||
uint8_t reg;
|
||||
uint8_t value;
|
||||
};
|
||||
|
||||
ES8388AudioSink::ES8388AudioSink()
|
||||
{
|
||||
// configure i2c
|
||||
i2c_config = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = 33,
|
||||
.scl_io_num = 32,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
};
|
||||
|
||||
i2c_config.master.clk_speed = 100000;
|
||||
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX
|
||||
.sample_rate = 44100,
|
||||
.bits_per_sample = (i2s_bits_per_sample_t)16,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels
|
||||
.communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_MSB,
|
||||
.intr_alloc_flags = 0, //Default interrupt priority
|
||||
.dma_buf_count = 8,
|
||||
.dma_buf_len = 512,
|
||||
.use_apll = true,
|
||||
.tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow
|
||||
.fixed_mclk = 256 * 44100
|
||||
};
|
||||
|
||||
i2s_pin_config_t pin_config = {
|
||||
.bck_io_num = 27,
|
||||
.ws_io_num = 25,
|
||||
.data_out_num = 26,
|
||||
.data_in_num = -1 //Not used
|
||||
};
|
||||
|
||||
int err;
|
||||
|
||||
err = i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE("OI", "i2s driver installation error: %d", err);
|
||||
}
|
||||
|
||||
err = i2s_set_pin((i2s_port_t)0, &pin_config);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE("OI", "i2s set pin error: %d", err);
|
||||
}
|
||||
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
|
||||
REG_SET_FIELD(PIN_CTRL, CLK_OUT1, 0);
|
||||
ESP_LOGI("OI", "MCLK output on CLK_OUT1");
|
||||
|
||||
|
||||
err = i2c_param_config(0, &i2c_config);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE("OI", "i2c param config error: %d", err);
|
||||
}
|
||||
|
||||
err = i2c_driver_install(0, I2C_MODE_MASTER, 0, 0, 0);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE("OI", "i2c driver installation error: %d", err);
|
||||
}
|
||||
|
||||
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
|
||||
|
||||
err = i2c_master_start(i2c_cmd);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE("OI", "i2c master start error: %d", err);
|
||||
}
|
||||
|
||||
/* mute DAC during setup, power up all systems, slave mode */
|
||||
writeReg(ES8388_DACCONTROL3, 0x04);
|
||||
writeReg(ES8388_CONTROL2, 0x50);
|
||||
writeReg(ES8388_CHIPPOWER, 0x00);
|
||||
writeReg(ES8388_MASTERMODE, 0x00);
|
||||
|
||||
/* power up DAC and enable LOUT1+2 / ROUT1+2, ADC sample rate = DAC sample rate */
|
||||
writeReg(ES8388_DACPOWER, 0x3e);
|
||||
writeReg(ES8388_CONTROL1, 0x12);
|
||||
|
||||
/* DAC I2S setup: 16 bit word length, I2S format; MCLK / Fs = 256*/
|
||||
writeReg(ES8388_DACCONTROL1, 0x18);
|
||||
writeReg(ES8388_DACCONTROL2, 0x02);
|
||||
|
||||
/* DAC to output route mixer configuration: ADC MIX TO OUTPUT */
|
||||
writeReg(ES8388_DACCONTROL16, 0x1B);
|
||||
writeReg(ES8388_DACCONTROL17, 0x90);
|
||||
writeReg(ES8388_DACCONTROL20, 0x90);
|
||||
|
||||
/* DAC and ADC use same LRCK, enable MCLK input; output resistance setup */
|
||||
writeReg(ES8388_DACCONTROL21, 0x80);
|
||||
writeReg(ES8388_DACCONTROL23, 0x00);
|
||||
|
||||
/* DAC volume control: 0dB (maximum, unattented) */
|
||||
writeReg(ES8388_DACCONTROL5, 0x00);
|
||||
writeReg(ES8388_DACCONTROL4, 0x00);
|
||||
|
||||
/* power down ADC while configuring; volume: +9dB for both channels */
|
||||
writeReg(ES8388_ADCPOWER, 0xff);
|
||||
writeReg(ES8388_ADCCONTROL1, 0x88); // +24db
|
||||
|
||||
/* select LINPUT2 / RINPUT2 as ADC input; stereo; 16 bit word length, format right-justified, MCLK / Fs = 256 */
|
||||
writeReg(ES8388_ADCCONTROL2, 0xf0); // 50
|
||||
writeReg(ES8388_ADCCONTROL3, 0x80); // 00
|
||||
writeReg(ES8388_ADCCONTROL4, 0x0e);
|
||||
writeReg(ES8388_ADCCONTROL5, 0x02);
|
||||
|
||||
/* set ADC volume */
|
||||
writeReg(ES8388_ADCCONTROL8, 0x20);
|
||||
writeReg(ES8388_ADCCONTROL9, 0x20);
|
||||
|
||||
/* set LOUT1 / ROUT1 volume: 0dB (unattenuated) */
|
||||
writeReg(ES8388_DACCONTROL24, 0x1e);
|
||||
writeReg(ES8388_DACCONTROL25, 0x1e);
|
||||
|
||||
/* set LOUT2 / ROUT2 volume: 0dB (unattenuated) */
|
||||
writeReg(ES8388_DACCONTROL26, 0x1e);
|
||||
writeReg(ES8388_DACCONTROL27, 0x1e);
|
||||
|
||||
/* power up and enable DAC; power up ADC (no MIC bias) */
|
||||
writeReg(ES8388_DACPOWER, 0x3c);
|
||||
writeReg(ES8388_DACCONTROL3, 0x00);
|
||||
writeReg(ES8388_ADCPOWER, 0x00);
|
||||
|
||||
startI2sFeed();
|
||||
}
|
||||
|
||||
void ES8388AudioSink::writeReg(uint8_t reg_add, uint8_t data)
|
||||
{
|
||||
|
||||
int res = 0;
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
res |= i2c_master_start(cmd);
|
||||
res |= i2c_master_write_byte(cmd, ES8388_ADDR, ACK_CHECK_EN);
|
||||
res |= i2c_master_write_byte(cmd, reg_add, ACK_CHECK_EN);
|
||||
res |= i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
|
||||
res |= i2c_master_stop(cmd);
|
||||
res |= i2c_master_cmd_begin(0, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
|
||||
if (res != ESP_OK) {
|
||||
ESP_LOGE("RR", "Unable to write to ES8388: %d", res);
|
||||
}else{
|
||||
ESP_LOGE("RR", "register successfull written.");
|
||||
}
|
||||
}
|
||||
|
||||
ES8388AudioSink::~ES8388AudioSink()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "ES9018AudioSink.h"
|
||||
|
||||
#include "driver/i2s.h"
|
||||
|
||||
ES9018AudioSink::ES9018AudioSink()
|
||||
{
|
||||
i2s_config_t i2s_config = {
|
||||
|
||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX
|
||||
.sample_rate = 44100,
|
||||
.bits_per_sample = (i2s_bits_per_sample_t)16,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels
|
||||
.communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_MSB,
|
||||
.intr_alloc_flags = 0, //Default interrupt priority
|
||||
.dma_buf_count = 8,
|
||||
.dma_buf_len = 512,
|
||||
.use_apll = true,
|
||||
.tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow
|
||||
.fixed_mclk = 384 * 44100
|
||||
};
|
||||
|
||||
i2s_pin_config_t pin_config = {
|
||||
.bck_io_num = 27,
|
||||
.ws_io_num = 32,
|
||||
.data_out_num = 25,
|
||||
.data_in_num = -1 //Not used
|
||||
};
|
||||
i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
|
||||
i2s_set_pin((i2s_port_t)0, &pin_config);
|
||||
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
|
||||
REG_SET_FIELD(PIN_CTRL, CLK_OUT1, 0);
|
||||
ESP_LOGI("OI", "MCLK output on CLK_OUT1");
|
||||
|
||||
startI2sFeed();
|
||||
}
|
||||
|
||||
ES9018AudioSink::~ES9018AudioSink()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "InternalAudioSink.h"
|
||||
#include "driver/i2s.h"
|
||||
|
||||
InternalAudioSink::InternalAudioSink()
|
||||
{
|
||||
softwareVolumeControl = true;
|
||||
usign = true;
|
||||
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN), // Only TX
|
||||
.sample_rate = (i2s_bits_per_sample_t)44100,
|
||||
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
|
||||
.communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_I2S,
|
||||
.intr_alloc_flags = 0,//ESP_INTR_FLAG_LEVEL1
|
||||
.dma_buf_count = 6,
|
||||
.dma_buf_len = 512,
|
||||
.use_apll = true,
|
||||
.tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow
|
||||
.fixed_mclk=-1
|
||||
};
|
||||
|
||||
//install and start i2s driver
|
||||
i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
|
||||
//init DAC
|
||||
i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
|
||||
|
||||
startI2sFeed();
|
||||
}
|
||||
|
||||
InternalAudioSink::~InternalAudioSink()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "PCM5102AudioSink.h"
|
||||
|
||||
#include "driver/i2s.h"
|
||||
|
||||
PCM5102AudioSink::PCM5102AudioSink()
|
||||
{
|
||||
i2s_config_t i2s_config = {
|
||||
|
||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX
|
||||
.sample_rate = 44100,
|
||||
.bits_per_sample = (i2s_bits_per_sample_t)16,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels
|
||||
.communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_I2S,
|
||||
.intr_alloc_flags = 0, //Default interrupt priority
|
||||
.dma_buf_count = 8,
|
||||
.dma_buf_len = 512,
|
||||
.use_apll = true,
|
||||
.tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow
|
||||
.fixed_mclk = 384 * 44100
|
||||
};
|
||||
|
||||
i2s_pin_config_t pin_config = {
|
||||
.bck_io_num = 27,
|
||||
.ws_io_num = 32,
|
||||
.data_out_num = 25,
|
||||
.data_in_num = -1 //Not used
|
||||
};
|
||||
i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
|
||||
i2s_set_pin((i2s_port_t)0, &pin_config);
|
||||
|
||||
startI2sFeed();
|
||||
}
|
||||
|
||||
PCM5102AudioSink::~PCM5102AudioSink()
|
||||
{
|
||||
}
|
||||
184
components/spotify/cspot/bell/src/sinks/esp/SPDIFAudioSink.cpp
Normal file
184
components/spotify/cspot/bell/src/sinks/esp/SPDIFAudioSink.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
#include "SPDIFAudioSink.h"
|
||||
|
||||
#include "driver/i2s.h"
|
||||
|
||||
// See http://www.hardwarebook.info/S/PDIF for more info on this protocol
|
||||
// Conversion table to biphase code mark (LSB first, ending in 1)
|
||||
static const uint16_t bmc_convert[256] = {
|
||||
0x3333, 0xb333, 0xd333, 0x5333, 0xcb33, 0x4b33, 0x2b33, 0xab33,
|
||||
0xcd33, 0x4d33, 0x2d33, 0xad33, 0x3533, 0xb533, 0xd533, 0x5533,
|
||||
0xccb3, 0x4cb3, 0x2cb3, 0xacb3, 0x34b3, 0xb4b3, 0xd4b3, 0x54b3,
|
||||
0x32b3, 0xb2b3, 0xd2b3, 0x52b3, 0xcab3, 0x4ab3, 0x2ab3, 0xaab3,
|
||||
0xccd3, 0x4cd3, 0x2cd3, 0xacd3, 0x34d3, 0xb4d3, 0xd4d3, 0x54d3,
|
||||
0x32d3, 0xb2d3, 0xd2d3, 0x52d3, 0xcad3, 0x4ad3, 0x2ad3, 0xaad3,
|
||||
0x3353, 0xb353, 0xd353, 0x5353, 0xcb53, 0x4b53, 0x2b53, 0xab53,
|
||||
0xcd53, 0x4d53, 0x2d53, 0xad53, 0x3553, 0xb553, 0xd553, 0x5553,
|
||||
0xcccb, 0x4ccb, 0x2ccb, 0xaccb, 0x34cb, 0xb4cb, 0xd4cb, 0x54cb,
|
||||
0x32cb, 0xb2cb, 0xd2cb, 0x52cb, 0xcacb, 0x4acb, 0x2acb, 0xaacb,
|
||||
0x334b, 0xb34b, 0xd34b, 0x534b, 0xcb4b, 0x4b4b, 0x2b4b, 0xab4b,
|
||||
0xcd4b, 0x4d4b, 0x2d4b, 0xad4b, 0x354b, 0xb54b, 0xd54b, 0x554b,
|
||||
0x332b, 0xb32b, 0xd32b, 0x532b, 0xcb2b, 0x4b2b, 0x2b2b, 0xab2b,
|
||||
0xcd2b, 0x4d2b, 0x2d2b, 0xad2b, 0x352b, 0xb52b, 0xd52b, 0x552b,
|
||||
0xccab, 0x4cab, 0x2cab, 0xacab, 0x34ab, 0xb4ab, 0xd4ab, 0x54ab,
|
||||
0x32ab, 0xb2ab, 0xd2ab, 0x52ab, 0xcaab, 0x4aab, 0x2aab, 0xaaab,
|
||||
0xcccd, 0x4ccd, 0x2ccd, 0xaccd, 0x34cd, 0xb4cd, 0xd4cd, 0x54cd,
|
||||
0x32cd, 0xb2cd, 0xd2cd, 0x52cd, 0xcacd, 0x4acd, 0x2acd, 0xaacd,
|
||||
0x334d, 0xb34d, 0xd34d, 0x534d, 0xcb4d, 0x4b4d, 0x2b4d, 0xab4d,
|
||||
0xcd4d, 0x4d4d, 0x2d4d, 0xad4d, 0x354d, 0xb54d, 0xd54d, 0x554d,
|
||||
0x332d, 0xb32d, 0xd32d, 0x532d, 0xcb2d, 0x4b2d, 0x2b2d, 0xab2d,
|
||||
0xcd2d, 0x4d2d, 0x2d2d, 0xad2d, 0x352d, 0xb52d, 0xd52d, 0x552d,
|
||||
0xccad, 0x4cad, 0x2cad, 0xacad, 0x34ad, 0xb4ad, 0xd4ad, 0x54ad,
|
||||
0x32ad, 0xb2ad, 0xd2ad, 0x52ad, 0xcaad, 0x4aad, 0x2aad, 0xaaad,
|
||||
0x3335, 0xb335, 0xd335, 0x5335, 0xcb35, 0x4b35, 0x2b35, 0xab35,
|
||||
0xcd35, 0x4d35, 0x2d35, 0xad35, 0x3535, 0xb535, 0xd535, 0x5535,
|
||||
0xccb5, 0x4cb5, 0x2cb5, 0xacb5, 0x34b5, 0xb4b5, 0xd4b5, 0x54b5,
|
||||
0x32b5, 0xb2b5, 0xd2b5, 0x52b5, 0xcab5, 0x4ab5, 0x2ab5, 0xaab5,
|
||||
0xccd5, 0x4cd5, 0x2cd5, 0xacd5, 0x34d5, 0xb4d5, 0xd4d5, 0x54d5,
|
||||
0x32d5, 0xb2d5, 0xd2d5, 0x52d5, 0xcad5, 0x4ad5, 0x2ad5, 0xaad5,
|
||||
0x3355, 0xb355, 0xd355, 0x5355, 0xcb55, 0x4b55, 0x2b55, 0xab55,
|
||||
0xcd55, 0x4d55, 0x2d55, 0xad55, 0x3555, 0xb555, 0xd555, 0x5555,
|
||||
};
|
||||
|
||||
#define I2S_BUG_MAGIC (26 * 1000 * 1000) // magic number for avoiding I2S bug
|
||||
#define BITS_PER_SUBFRAME 64
|
||||
#define FRAMES_PER_BLOCK 192
|
||||
#define SPDIF_BUF_SIZE (BITS_PER_SUBFRAME/8 * 2 * FRAMES_PER_BLOCK)
|
||||
#define SPDIF_BUF_ARRAY_SIZE (SPDIF_BUF_SIZE / sizeof(uint32_t))
|
||||
|
||||
#define BMC_B 0x33173333 // block start
|
||||
#define BMC_M 0x331d3333 // left ch
|
||||
#define BMC_W 0x331b3333 // right ch
|
||||
#define BMC_MW_DIF (BMC_M ^ BMC_W)
|
||||
|
||||
static uint32_t spdif_buf[SPDIF_BUF_ARRAY_SIZE];
|
||||
static uint32_t *spdif_ptr;
|
||||
|
||||
static void spdif_buf_init(void)
|
||||
{
|
||||
// first bllock has W preamble
|
||||
spdif_buf[0] = BMC_B;
|
||||
|
||||
// all other blocks are alternating M, then W preamble
|
||||
uint32_t bmc_mw = BMC_M;
|
||||
for (int i = 2; i < SPDIF_BUF_ARRAY_SIZE; i += 2)
|
||||
{
|
||||
spdif_buf[i] = bmc_mw ^= BMC_MW_DIF;
|
||||
}
|
||||
}
|
||||
|
||||
SPDIFAudioSink::SPDIFAudioSink(uint8_t spdifPin)
|
||||
{
|
||||
// initialize S/PDIF buffer
|
||||
spdif_buf_init();
|
||||
spdif_ptr = spdif_buf;
|
||||
this->spdifPin = spdifPin;
|
||||
this->initialize(44100);
|
||||
startI2sFeed(SPDIF_BUF_SIZE * 16);
|
||||
}
|
||||
|
||||
void SPDIFAudioSink::initialize(uint16_t sampleRate) {
|
||||
int sample_rate = sampleRate * 2;
|
||||
int bclk = sample_rate * 64 * 2;
|
||||
int mclk = (I2S_BUG_MAGIC / bclk) * bclk;
|
||||
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
|
||||
.sample_rate = (uint32_t) sample_rate,
|
||||
.bits_per_sample = (i2s_bits_per_sample_t)32,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
|
||||
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
|
||||
.intr_alloc_flags = 0,
|
||||
.dma_buf_count = 8,
|
||||
.dma_buf_len = 512,
|
||||
.use_apll = true,
|
||||
.tx_desc_auto_clear = true,
|
||||
.fixed_mclk = mclk, // avoiding I2S bug
|
||||
};
|
||||
i2s_pin_config_t pin_config = {
|
||||
.bck_io_num = -1,
|
||||
.ws_io_num = -1,
|
||||
.data_out_num = spdifPin,
|
||||
.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);
|
||||
this->initialize(sampleRate);
|
||||
return true;
|
||||
}
|
||||
|
||||
int num_frames = 0;
|
||||
|
||||
void SPDIFAudioSink::feedPCMFrames(const uint8_t *buffer, size_t bytes)
|
||||
{
|
||||
for (int i = 0; i < bytes; i += 2)
|
||||
{
|
||||
/**
|
||||
* What is this, and why does it work?
|
||||
*
|
||||
* Rather than assemble all S/PDIF frames from scratch we want to do the
|
||||
* minimum amount of work possible. To that extent, we fix the final four
|
||||
* bits (VUCP) to be all-zero prior to BMC encoding (= valid, no subcode
|
||||
* or channel-status bits set, even parity), and zero the lowest 8 sample
|
||||
* bits (prior to BMC encoding). This is all done in spdif_buf_init(),
|
||||
* aligning at word boundaries and setting alternating preambles as well
|
||||
* as encoding 8 bits of zeros as 0x33, leaving the final bit high.
|
||||
*
|
||||
* We must therefore BMC encode our 16 bit PCM data in such a way that:
|
||||
* - the first (least significant) bit is 0 (to fit with 0x33 zeros)
|
||||
* - the final bit is 1 (so as to fit with the following 0x33 VUCP bits)
|
||||
* - the result has even parity
|
||||
*
|
||||
* As biphase mark code retains parity (0 encodes as two 1s or two 0s),
|
||||
* this is evidently not possible without loss of data, as the input PCM
|
||||
* data isn't already even parity. We can use the first (least significant)
|
||||
* bit as parity bit to achieve our desired encoding.
|
||||
*
|
||||
* The bmc_convert table converts the lower and upper 8 bit of our PCM
|
||||
* frames into 16 bit biphase mark code patterns with the first two bits
|
||||
* encoding the LSB and the final bit always high. We combine both 16bit
|
||||
* patterns into a 32 bit encoding of our original input data by shifting
|
||||
* the first (lower) 16 bit into position, then sign-extending the second
|
||||
* (higher) 16bit pattern. If that pattern started with a 1, the resulting
|
||||
* 32 bit pattern will now contain 1s in the first 16 bits.
|
||||
*
|
||||
* Keep in mind that the shifted value in the first (lower) 16 bits always
|
||||
* ends in a 1 bit, so the entire pattern must be flipped in case the
|
||||
* second (higher) 16 bit pattern starts with a 1 bit. XORing the sign-
|
||||
* extended component to the first one achieves exactly that.
|
||||
*
|
||||
* Finally, we zero out the very first bit of the resulting value. This
|
||||
* may change the lowest bit of our encoded value, but ensures that our
|
||||
* newly encoded bits form a valid BMC pattern with the already zeroed out
|
||||
* lower 8 bits in the pattern set up in spdif_buf_init().
|
||||
*
|
||||
* Further, this also happens to ensure even parity:
|
||||
* All entries in the BMC table end in a 1, so an all-zero pattern would
|
||||
* end (after encoding an even number of bits) in two 0 bits. Setting any
|
||||
* bit will cause the BMC-encoded pattern to flip its first (lowest) bit,
|
||||
* meaning we can use that bit to infer parity. Setting it to zero flips
|
||||
* the first (lowest) bit such that we always have even parity.
|
||||
*
|
||||
* I did not come up with this, all credit goes to
|
||||
* github.com/amedes/esp_a2dp_sink_spdif
|
||||
*/
|
||||
uint32_t lo = ((uint32_t)(bmc_convert[buffer[i]]) << 16);
|
||||
uint32_t hi = (uint32_t)((int16_t)bmc_convert[buffer[i+1]]);
|
||||
|
||||
*(spdif_ptr + 1) = ((lo ^ hi) << 1) >> 1;
|
||||
|
||||
spdif_ptr += 2; // advance to next audio data
|
||||
|
||||
if (spdif_ptr >= &spdif_buf[SPDIF_BUF_ARRAY_SIZE]) {
|
||||
feedPCMFramesInternal(spdif_buf, sizeof(spdif_buf));
|
||||
spdif_ptr = spdif_buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
121
components/spotify/cspot/bell/src/sinks/esp/TAS5711AudioSink.cpp
Normal file
121
components/spotify/cspot/bell/src/sinks/esp/TAS5711AudioSink.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
#include "TAS5711AudioSink.h"
|
||||
|
||||
|
||||
struct tas5711_cmd_s {
|
||||
uint8_t reg;
|
||||
uint8_t value;
|
||||
};
|
||||
|
||||
static const struct tas5711_cmd_s tas5711_init_sequence[] = {
|
||||
{ 0x00, 0x6c }, // 0x6c - 256 x mclk
|
||||
{ 0x04, 0x03 }, // 0x03 - 16 bit i2s
|
||||
{ 0x05, 0x00 }, // system control 0x00 is audio playback
|
||||
{ 0x06, 0x00 }, // disable mute
|
||||
{ 0x07, 0x50 }, // volume register
|
||||
{ 0xff, 0xff }
|
||||
|
||||
};
|
||||
i2c_ack_type_t ACK_CHECK_EN = (i2c_ack_type_t)0x1;
|
||||
|
||||
TAS5711AudioSink::TAS5711AudioSink()
|
||||
{
|
||||
i2s_config_t i2s_config = {
|
||||
|
||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX
|
||||
.sample_rate = 44100,
|
||||
.bits_per_sample = (i2s_bits_per_sample_t)16,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels
|
||||
.communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_MSB,
|
||||
.intr_alloc_flags = 0, //Default interrupt priority
|
||||
.dma_buf_count = 8,
|
||||
.dma_buf_len = 512,
|
||||
.use_apll = true,
|
||||
.tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow
|
||||
.fixed_mclk = 256 * 44100
|
||||
};
|
||||
|
||||
|
||||
i2s_pin_config_t pin_config = {
|
||||
.bck_io_num = 5,
|
||||
.ws_io_num = 25,
|
||||
.data_out_num = 26,
|
||||
.data_in_num = -1 //Not used
|
||||
};
|
||||
i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
|
||||
i2s_set_pin((i2s_port_t)0, &pin_config);
|
||||
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
|
||||
REG_SET_FIELD(PIN_CTRL, CLK_OUT1, 0);
|
||||
ESP_LOGI("OI", "MCLK output on CLK_OUT1");
|
||||
|
||||
// configure i2c
|
||||
i2c_config = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = 21,
|
||||
.scl_io_num = 23,
|
||||
.sda_pullup_en = GPIO_PULLUP_DISABLE,
|
||||
.scl_pullup_en = GPIO_PULLUP_DISABLE,
|
||||
};
|
||||
|
||||
i2c_config.master.clk_speed = 250000;
|
||||
|
||||
i2c_param_config(i2c_port, &i2c_config);
|
||||
i2c_driver_install(i2c_port, I2C_MODE_MASTER, false, false, false);
|
||||
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
|
||||
|
||||
uint8_t data, addr = (0x1b);
|
||||
|
||||
i2c_master_start(i2c_cmd);
|
||||
i2c_master_write_byte(i2c_cmd, (addr << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN);
|
||||
i2c_master_write_byte(i2c_cmd, 00, ACK_CHECK_EN);
|
||||
|
||||
i2c_master_start(i2c_cmd);
|
||||
i2c_master_write_byte(i2c_cmd, (addr << 1) | I2C_MASTER_READ, ACK_CHECK_EN);
|
||||
i2c_master_read_byte(i2c_cmd, &data, ACK_CHECK_EN);
|
||||
|
||||
i2c_master_stop(i2c_cmd);
|
||||
int ret = i2c_master_cmd_begin(i2c_port, i2c_cmd, 50 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(i2c_cmd);
|
||||
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI("RR", "Detected TAS");
|
||||
}
|
||||
else {
|
||||
ESP_LOGI("RR", "Unable to detect dac");
|
||||
}
|
||||
|
||||
writeReg(0x1b, 0x00);
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
|
||||
|
||||
for (int i = 0; tas5711_init_sequence[i].reg != 0xff; i++) {
|
||||
writeReg(tas5711_init_sequence[i].reg, tas5711_init_sequence[i].value);
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
startI2sFeed();
|
||||
}
|
||||
|
||||
void TAS5711AudioSink::writeReg(uint8_t reg, uint8_t value)
|
||||
{
|
||||
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
|
||||
|
||||
i2c_master_start(i2c_cmd);
|
||||
i2c_master_write_byte(i2c_cmd, (0x1b << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN);
|
||||
i2c_master_write_byte(i2c_cmd, reg, ACK_CHECK_EN);
|
||||
i2c_master_write_byte(i2c_cmd, value, ACK_CHECK_EN);
|
||||
|
||||
|
||||
i2c_master_stop(i2c_cmd);
|
||||
esp_err_t res = i2c_master_cmd_begin(i2c_port, i2c_cmd, 500 / portTICK_RATE_MS);
|
||||
|
||||
if (res != ESP_OK) {
|
||||
ESP_LOGE("RR", "Unable to write to TAS5711");
|
||||
}
|
||||
i2c_cmd_link_delete(i2c_cmd);
|
||||
|
||||
}
|
||||
|
||||
TAS5711AudioSink::~TAS5711AudioSink()
|
||||
{
|
||||
}
|
||||
426
components/spotify/cspot/bell/src/sinks/esp/ac101.c
Normal file
426
components/spotify/cspot/bell/src/sinks/esp/ac101.c
Normal file
@@ -0,0 +1,426 @@
|
||||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_types.h>
|
||||
#include <esp_system.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <driver/i2c.h>
|
||||
#include <driver/i2s.h>
|
||||
#include "adac.h"
|
||||
#include "ac101.h"
|
||||
|
||||
const static char TAG[] = "AC101";
|
||||
|
||||
#define SPKOUT_EN ((1 << 9) | (1 << 11) | (1 << 7) | (1 << 5))
|
||||
#define EAROUT_EN ((1 << 11) | (1 << 12) | (1 << 13))
|
||||
#define BIN(a, b, c, d) 0b##a##b##c##d
|
||||
|
||||
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#define max(a, b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
#define AC_ASSERT(a, format, b, ...) \
|
||||
if ((a) != 0) \
|
||||
{ \
|
||||
ESP_LOGE(TAG, format, ##__VA_ARGS__); \
|
||||
return b; \
|
||||
}
|
||||
|
||||
static bool init(int i2c_port_num, int i2s_num, i2s_config_t *config);
|
||||
static void deinit(void);
|
||||
static void speaker(bool active);
|
||||
static void headset(bool active);
|
||||
static void volume(unsigned left, unsigned right);
|
||||
static void power(adac_power_e mode);
|
||||
|
||||
struct adac_s dac_a1s = {init, deinit, power, speaker, headset, volume};
|
||||
|
||||
static esp_err_t i2c_write_reg(uint8_t reg, uint16_t val);
|
||||
static uint16_t i2c_read_reg(uint8_t reg);
|
||||
static void ac101_start(ac_module_t mode);
|
||||
static void ac101_stop(void);
|
||||
static void ac101_set_earph_volume(uint8_t volume);
|
||||
static void ac101_set_spk_volume(uint8_t volume);
|
||||
static int ac101_get_spk_volume(void);
|
||||
|
||||
static int i2c_port;
|
||||
|
||||
/****************************************************************************************
|
||||
* init
|
||||
*/
|
||||
static bool init(int i2c_port_num, int i2s_num, i2s_config_t *i2s_config)
|
||||
{
|
||||
esp_err_t res = ESP_OK;
|
||||
|
||||
i2c_port = i2c_port_num;
|
||||
|
||||
// configure i2c
|
||||
i2c_config_t i2c_config = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = 33,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = 32,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = 250000,
|
||||
};
|
||||
|
||||
i2c_param_config(i2c_port, &i2c_config);
|
||||
i2c_driver_install(i2c_port, I2C_MODE_MASTER, false, false, false);
|
||||
|
||||
res = i2c_read_reg(CHIP_AUDIO_RS);
|
||||
|
||||
if (!res)
|
||||
{
|
||||
ESP_LOGW(TAG, "No AC101 detected");
|
||||
i2c_driver_delete(i2c_port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "AC101 DAC using I2C sda:%u, scl:%u", i2c_config.sda_io_num, i2c_config.scl_io_num);
|
||||
|
||||
res = i2c_write_reg(CHIP_AUDIO_RS, 0x123);
|
||||
// huh?
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
|
||||
// enable the PLL from BCLK source
|
||||
i2c_write_reg(PLL_CTRL1, BIN(0000, 0001, 0100, 1111)); // F=1,M=1,PLL,INT=31 (medium)
|
||||
i2c_write_reg(PLL_CTRL2, BIN(1000, 0110, 0000, 0000)); // PLL, F=96,N_i=1024-96,F=0,N_f=0*0.2;
|
||||
// i2c_write_reg(PLL_CTRL2, BIN(1000,0011,1100,0000));
|
||||
|
||||
// clocking system
|
||||
i2c_write_reg(SYSCLK_CTRL, BIN(1010, 1010, 0000, 1000)); // PLLCLK, BCLK1, IS1CLK, PLL, SYSCLK
|
||||
i2c_write_reg(MOD_CLK_ENA, BIN(1000, 0000, 0000, 1100)); // IS21, ADC, DAC
|
||||
i2c_write_reg(MOD_RST_CTRL, BIN(1000, 0000, 0000, 1100)); // IS21, ADC, DAC
|
||||
i2c_write_reg(I2S_SR_CTRL, BIN(0111, 0000, 0000, 0000)); // 44.1kHz
|
||||
|
||||
// analogue config
|
||||
i2c_write_reg(I2S1LCK_CTRL, BIN(1000, 1000, 0101, 0000)); // Slave, BCLK=I2S/8,LRCK=32,16bits,I2Smode, Stereo
|
||||
i2c_write_reg(I2S1_SDOUT_CTRL, BIN(1100, 0000, 0000, 0000)); // I2S1ADC (R&L)
|
||||
i2c_write_reg(I2S1_SDIN_CTRL, BIN(1100, 0000, 0000, 0000)); // IS21DAC (R&L)
|
||||
i2c_write_reg(I2S1_MXR_SRC, BIN(0010, 0010, 0000, 0000)); // ADCL, ADCR
|
||||
i2c_write_reg(ADC_SRCBST_CTRL, BIN(0100, 0100, 0100, 0000)); // disable all boost (default)
|
||||
#if ENABLE_ADC
|
||||
i2c_write_reg(ADC_SRC, BIN(0000, 0100, 0000, 1000)); // source=linein(R/L)
|
||||
i2c_write_reg(ADC_DIG_CTRL, BIN(1000, 0000, 0000, 0000)); // enable digital ADC
|
||||
i2c_write_reg(ADC_ANA_CTRL, BIN(1011, 1011, 0000, 0000)); // enable analogue R/L, 0dB
|
||||
#else
|
||||
i2c_write_reg(ADC_SRC, BIN(0000, 0000, 0000, 0000)); // source=none
|
||||
i2c_write_reg(ADC_DIG_CTRL, BIN(0000, 0000, 0000, 0000)); // disable digital ADC
|
||||
i2c_write_reg(ADC_ANA_CTRL, BIN(0011, 0011, 0000, 0000)); // disable analogue R/L, 0dB
|
||||
#endif
|
||||
|
||||
//Path Configuration
|
||||
i2c_write_reg(DAC_MXR_SRC, BIN(1000, 1000, 0000, 0000)); // DAC from I2S
|
||||
i2c_write_reg(DAC_DIG_CTRL, BIN(1000, 0000, 0000, 0000)); // enable DAC
|
||||
i2c_write_reg(OMIXER_DACA_CTRL, BIN(1111, 0000, 0000, 0000)); // enable DAC/Analogue (see note on offset removal and PA)
|
||||
i2c_write_reg(OMIXER_DACA_CTRL, BIN(1111, 1111, 0000, 0000)); // this toggle is needed for headphone PA offset
|
||||
#if ENABLE_ADC
|
||||
i2c_write_reg(OMIXER_SR, BIN(0000, 0001, 0000, 0010)); // source=DAC(R/L) (are DACR and DACL really inverted in bitmap?)
|
||||
#else
|
||||
i2c_write_reg(OMIXER_SR, BIN(0000, 0101, 0000, 1010)); // source=DAC(R/L) and LINEIN(R/L)
|
||||
#endif
|
||||
|
||||
// configure I2S pins & install driver
|
||||
i2s_pin_config_t i2s_pin_config = (i2s_pin_config_t){.bck_io_num = 27, .ws_io_num = 26, .data_out_num = 25, .data_in_num = -1};
|
||||
res |= i2s_driver_install(i2s_num, i2s_config, 0, NULL);
|
||||
res |= i2s_set_pin(i2s_num, &i2s_pin_config);
|
||||
|
||||
// enable earphone & speaker
|
||||
i2c_write_reg(SPKOUT_CTRL, 0x0220);
|
||||
i2c_write_reg(HPOUT_CTRL, 0xf801);
|
||||
|
||||
// set gain for speaker and earphone
|
||||
ac101_set_spk_volume(70);
|
||||
ac101_set_earph_volume(70);
|
||||
|
||||
ESP_LOGI(TAG, "DAC using I2S bck:%d, ws:%d, do:%d", i2s_pin_config.bck_io_num, i2s_pin_config.ws_io_num, i2s_pin_config.data_out_num);
|
||||
|
||||
return (res == ESP_OK);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* init
|
||||
*/
|
||||
static void deinit(void)
|
||||
{
|
||||
i2c_driver_delete(i2c_port);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* change volume
|
||||
*/
|
||||
static void volume(unsigned left, unsigned right)
|
||||
{
|
||||
ac101_set_earph_volume(left);
|
||||
// nothing at that point, volume is handled by backend
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* power
|
||||
*/
|
||||
static void power(adac_power_e mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case ADAC_STANDBY:
|
||||
case ADAC_OFF:
|
||||
ac101_stop();
|
||||
break;
|
||||
case ADAC_ON:
|
||||
ac101_start(AC_MODULE_DAC);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "unknown power command");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* speaker
|
||||
*/
|
||||
static void speaker(bool active)
|
||||
{
|
||||
uint16_t value = i2c_read_reg(SPKOUT_CTRL);
|
||||
if (active)
|
||||
i2c_write_reg(SPKOUT_CTRL, value | SPKOUT_EN);
|
||||
else
|
||||
i2c_write_reg(SPKOUT_CTRL, value & ~SPKOUT_EN);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* headset
|
||||
*/
|
||||
static void headset(bool active)
|
||||
{
|
||||
// there might be aneed to toggle OMIXER_DACA_CTRL 11:8, not sure
|
||||
uint16_t value = i2c_read_reg(HPOUT_CTRL);
|
||||
if (active)
|
||||
i2c_write_reg(HPOUT_CTRL, value | EAROUT_EN);
|
||||
else
|
||||
i2c_write_reg(HPOUT_CTRL, value & ~EAROUT_EN);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
static esp_err_t i2c_write_reg(uint8_t reg, uint16_t val)
|
||||
{
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
esp_err_t ret = 0;
|
||||
uint8_t send_buff[4];
|
||||
send_buff[0] = (AC101_ADDR << 1);
|
||||
send_buff[1] = reg;
|
||||
send_buff[2] = (val >> 8) & 0xff;
|
||||
send_buff[3] = val & 0xff;
|
||||
ret |= i2c_master_start(cmd);
|
||||
ret |= i2c_master_write(cmd, send_buff, 4, ACK_CHECK_EN);
|
||||
ret |= i2c_master_stop(cmd);
|
||||
ret |= i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
static uint16_t i2c_read_reg(uint8_t reg)
|
||||
{
|
||||
uint8_t data[2] = {0};
|
||||
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (AC101_ADDR << 1) | WRITE_BIT, ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (AC101_ADDR << 1) | READ_BIT, ACK_CHECK_EN); //check or not
|
||||
i2c_master_read(cmd, data, 2, ACK_VAL);
|
||||
i2c_master_stop(cmd);
|
||||
i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
|
||||
return (data[0] << 8) + data[1];
|
||||
;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
void set_sample_rate(int rate)
|
||||
{
|
||||
if (rate == 8000)
|
||||
rate = SAMPLE_RATE_8000;
|
||||
else if (rate == 11025)
|
||||
rate = SAMPLE_RATE_11052;
|
||||
else if (rate == 12000)
|
||||
rate = SAMPLE_RATE_12000;
|
||||
else if (rate == 16000)
|
||||
rate = SAMPLE_RATE_16000;
|
||||
else if (rate == 22050)
|
||||
rate = SAMPLE_RATE_22050;
|
||||
else if (rate == 24000)
|
||||
rate = SAMPLE_RATE_24000;
|
||||
else if (rate == 32000)
|
||||
rate = SAMPLE_RATE_32000;
|
||||
else if (rate == 44100)
|
||||
rate = SAMPLE_RATE_44100;
|
||||
else if (rate == 48000)
|
||||
rate = SAMPLE_RATE_48000;
|
||||
else if (rate == 96000)
|
||||
rate = SAMPLE_RATE_96000;
|
||||
else if (rate == 192000)
|
||||
rate = SAMPLE_RATE_192000;
|
||||
else
|
||||
{
|
||||
ESP_LOGW(TAG, "Unknown sample rate %hu", rate);
|
||||
rate = SAMPLE_RATE_44100;
|
||||
}
|
||||
i2c_write_reg(I2S_SR_CTRL, rate);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Get normalized (0..100) speaker volume
|
||||
*/
|
||||
static int ac101_get_spk_volume(void)
|
||||
{
|
||||
return ((i2c_read_reg(SPKOUT_CTRL) & 0x1f) * 100) / 0x1f;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Set normalized (0..100) volume
|
||||
*/
|
||||
static void ac101_set_spk_volume(uint8_t volume)
|
||||
{
|
||||
uint16_t value = min(volume, 100);
|
||||
value = ((int)value * 0x1f) / 100;
|
||||
value |= i2c_read_reg(SPKOUT_CTRL) & ~0x1f;
|
||||
i2c_write_reg(SPKOUT_CTRL, value);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Get normalized (0..100) earphone volume
|
||||
*/
|
||||
static int ac101_get_earph_volume(void)
|
||||
{
|
||||
return (((i2c_read_reg(HPOUT_CTRL) >> 4) & 0x3f) * 100) / 0x3f;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Set normalized (0..100) earphone volume
|
||||
*/
|
||||
static void ac101_set_earph_volume(uint8_t volume)
|
||||
{
|
||||
uint16_t value = min(volume, 255);
|
||||
value = (((int)value * 0x3f) / 255) << 4;
|
||||
value |= i2c_read_reg(HPOUT_CTRL) & ~(0x3f << 4);
|
||||
i2c_write_reg(HPOUT_CTRL, value);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
static void ac101_set_output_mixer_gain(ac_output_mixer_gain_t gain, ac_output_mixer_source_t source)
|
||||
{
|
||||
uint16_t regval, temp, clrbit;
|
||||
regval = i2c_read_reg(OMIXER_BST1_CTRL);
|
||||
switch (source)
|
||||
{
|
||||
case SRC_MIC1:
|
||||
temp = (gain & 0x7) << 6;
|
||||
clrbit = ~(0x7 << 6);
|
||||
break;
|
||||
case SRC_MIC2:
|
||||
temp = (gain & 0x7) << 3;
|
||||
clrbit = ~(0x7 << 3);
|
||||
break;
|
||||
case SRC_LINEIN:
|
||||
temp = (gain & 0x7);
|
||||
clrbit = ~0x7;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
regval &= clrbit;
|
||||
regval |= temp;
|
||||
i2c_write_reg(OMIXER_BST1_CTRL, regval);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
static void ac101_start(ac_module_t mode)
|
||||
{
|
||||
if (mode == AC_MODULE_LINE)
|
||||
{
|
||||
i2c_write_reg(0x51, 0x0408);
|
||||
i2c_write_reg(0x40, 0x8000);
|
||||
i2c_write_reg(0x50, 0x3bc0);
|
||||
}
|
||||
if (mode == AC_MODULE_ADC || mode == AC_MODULE_ADC_DAC || mode == AC_MODULE_LINE)
|
||||
{
|
||||
// I2S1_SDOUT_CTRL
|
||||
// i2c_write_reg(PLL_CTRL2, 0x8120);
|
||||
i2c_write_reg(0x04, 0x800c);
|
||||
i2c_write_reg(0x05, 0x800c);
|
||||
// res |= i2c_write_reg(0x06, 0x3000);
|
||||
}
|
||||
if (mode == AC_MODULE_DAC || mode == AC_MODULE_ADC_DAC || mode == AC_MODULE_LINE)
|
||||
{
|
||||
uint16_t value = i2c_read_reg(PLL_CTRL2);
|
||||
value |= 0x8000;
|
||||
i2c_write_reg(PLL_CTRL2, value);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
static void ac101_stop(void)
|
||||
{
|
||||
uint16_t value = i2c_read_reg(PLL_CTRL2);
|
||||
value &= ~0x8000;
|
||||
i2c_write_reg(PLL_CTRL2, value);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
static void ac101_deinit(void)
|
||||
{
|
||||
i2c_write_reg(CHIP_AUDIO_RS, 0x123); //soft reset
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Don't know when this one is supposed to be called
|
||||
*/
|
||||
static void ac101_i2s_config_clock(ac_i2s_clock_t *cfg)
|
||||
{
|
||||
uint16_t regval = 0;
|
||||
regval = i2c_read_reg(I2S1LCK_CTRL);
|
||||
regval &= 0xe03f;
|
||||
regval |= (cfg->bclk_div << 9);
|
||||
regval |= (cfg->lclk_div << 6);
|
||||
i2c_write_reg(I2S1LCK_CTRL, regval);
|
||||
}
|
||||
101
components/spotify/cspot/bell/src/sinks/unix/ALSAAudioSink.cpp
Normal file
101
components/spotify/cspot/bell/src/sinks/unix/ALSAAudioSink.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "ALSAAudioSink.h"
|
||||
|
||||
ALSAAudioSink::ALSAAudioSink() : Task("", 0, 0, 0)
|
||||
{
|
||||
/* Open the PCM device in playback mode */
|
||||
if (pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE,
|
||||
SND_PCM_STREAM_PLAYBACK, 0) < 0)
|
||||
{
|
||||
printf("ERROR: Can't open \"%s\" PCM device. %s\n",
|
||||
PCM_DEVICE, snd_strerror(pcm));
|
||||
}
|
||||
|
||||
/* Allocate parameters object and fill it with default values*/
|
||||
snd_pcm_hw_params_alloca(¶ms);
|
||||
|
||||
snd_pcm_hw_params_any(pcm_handle, params);
|
||||
|
||||
/* Set parameters */
|
||||
if (pcm = snd_pcm_hw_params_set_access(pcm_handle, params,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
|
||||
printf("ERROR: Can't set interleaved mode. %s\n", snd_strerror(pcm));
|
||||
|
||||
if (pcm = snd_pcm_hw_params_set_format(pcm_handle, params,
|
||||
SND_PCM_FORMAT_S16_LE) < 0)
|
||||
printf("ERROR: Can't set format. %s\n", snd_strerror(pcm));
|
||||
|
||||
if (pcm = snd_pcm_hw_params_set_channels(pcm_handle, params, 2) < 0)
|
||||
printf("ERROR: Can't set channels number. %s\n", snd_strerror(pcm));
|
||||
unsigned int rate = 44100;
|
||||
if (pcm = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0) < 0)
|
||||
printf("ERROR: Can't set rate. %s\n", snd_strerror(pcm));
|
||||
unsigned int periodTime = 800;
|
||||
int dir = -1;
|
||||
snd_pcm_hw_params_set_period_time_near(pcm_handle, params, &periodTime, &dir);
|
||||
/* Write parameters */
|
||||
if (pcm = snd_pcm_hw_params(pcm_handle, params) < 0)
|
||||
printf("ERROR: Can't set harware parameters. %s\n", snd_strerror(pcm));
|
||||
|
||||
/* Resume information */
|
||||
printf("PCM name: '%s'\n", snd_pcm_name(pcm_handle));
|
||||
|
||||
printf("PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle)));
|
||||
unsigned int tmp;
|
||||
snd_pcm_hw_params_get_channels(params, &tmp);
|
||||
printf("channels: %i ", tmp);
|
||||
if (tmp == 1)
|
||||
printf("(mono)\n");
|
||||
else if (tmp == 2)
|
||||
printf("(stereo)\n");
|
||||
|
||||
snd_pcm_hw_params_get_period_time(params, &tmp, NULL);
|
||||
printf("period_time = %d\n", tmp);
|
||||
snd_pcm_hw_params_get_period_size(params, &frames, 0);
|
||||
|
||||
this->buff_size = frames * 2 * 2 /* 2 -> sample size */;
|
||||
printf("required buff_size: %d\n", buff_size);
|
||||
this->startTask();
|
||||
}
|
||||
|
||||
ALSAAudioSink::~ALSAAudioSink()
|
||||
{
|
||||
snd_pcm_drain(pcm_handle);
|
||||
snd_pcm_close(pcm_handle);
|
||||
}
|
||||
|
||||
void ALSAAudioSink::runTask()
|
||||
{
|
||||
std::unique_ptr<std::vector<uint8_t>> dataPtr;
|
||||
while (true)
|
||||
{
|
||||
if (!this->ringbuffer.pop(dataPtr))
|
||||
{
|
||||
usleep(100);
|
||||
continue;
|
||||
}
|
||||
if (pcm = snd_pcm_writei(pcm_handle, dataPtr->data(), this->frames) == -EPIPE)
|
||||
{
|
||||
|
||||
snd_pcm_prepare(pcm_handle);
|
||||
}
|
||||
else if (pcm < 0)
|
||||
{
|
||||
printf("ERROR. Can't write to PCM device. %s\n", snd_strerror(pcm));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ALSAAudioSink::feedPCMFrames(const uint8_t *buffer, size_t bytes)
|
||||
{
|
||||
|
||||
buff.insert(buff.end(), buffer, buffer + bytes);
|
||||
while (buff.size() > this->buff_size)
|
||||
{
|
||||
auto ptr = std::make_unique<std::vector<uint8_t>>(this->buff.begin(), this->buff.begin() + this->buff_size);
|
||||
this->buff = std::vector<uint8_t>(this->buff.begin() + this->buff_size, this->buff.end());
|
||||
while (!this->ringbuffer.push(ptr))
|
||||
{
|
||||
usleep(100);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "NamedPipeAudioSink.h"
|
||||
|
||||
NamedPipeAudioSink::NamedPipeAudioSink()
|
||||
{
|
||||
printf("Start\n");
|
||||
this->namedPipeFile = std::ofstream("outputFifo", std::ios::binary);
|
||||
printf("stop\n");
|
||||
|
||||
}
|
||||
|
||||
NamedPipeAudioSink::~NamedPipeAudioSink()
|
||||
{
|
||||
this->namedPipeFile.close();
|
||||
}
|
||||
|
||||
void NamedPipeAudioSink::feedPCMFrames(const uint8_t *buffer, size_t bytes)
|
||||
{
|
||||
// Write the actual data
|
||||
this->namedPipeFile.write((char*)buffer, (long)bytes);
|
||||
this->namedPipeFile.flush();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#include "PortAudioSink.h"
|
||||
|
||||
PortAudioSink::PortAudioSink()
|
||||
{
|
||||
Pa_Initialize();
|
||||
this->initialize(44100);
|
||||
}
|
||||
|
||||
void PortAudioSink::initialize(uint16_t sampleRate) {
|
||||
PaStreamParameters outputParameters;
|
||||
outputParameters.device = Pa_GetDefaultOutputDevice();
|
||||
if (outputParameters.device == paNoDevice) {
|
||||
printf("PortAudio: Default audio device not found!\n");
|
||||
// exit(0);
|
||||
}
|
||||
printf("PortAudio: Default audio device not found!\n");
|
||||
|
||||
outputParameters.channelCount = 2; /* stereo output */
|
||||
outputParameters.sampleFormat = paInt16; /* 32 bit floating point output */
|
||||
outputParameters.suggestedLatency = 0.050;
|
||||
outputParameters.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
PaError err = Pa_OpenStream(
|
||||
&stream,
|
||||
NULL,
|
||||
&outputParameters,
|
||||
sampleRate,
|
||||
4096 / 4,
|
||||
paClipOff,
|
||||
NULL, // blocking api
|
||||
NULL
|
||||
);
|
||||
Pa_StartStream(stream);
|
||||
}
|
||||
|
||||
PortAudioSink::~PortAudioSink()
|
||||
{
|
||||
Pa_StopStream(stream);
|
||||
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)
|
||||
{
|
||||
Pa_WriteStream(stream, buffer, bytes / 4);
|
||||
}
|
||||
@@ -62,7 +62,7 @@ public:
|
||||
*
|
||||
* @param data encrypted binary audio data.
|
||||
*/
|
||||
void appendData(std::vector<uint8_t> &data);
|
||||
void appendData(const std::vector<uint8_t> &data);
|
||||
|
||||
/**
|
||||
* @brief Performs AES CTR decryption of received data.
|
||||
|
||||
@@ -21,6 +21,7 @@ public:
|
||||
AudioChunkManager();
|
||||
std::atomic<bool> isRunning = false;
|
||||
std::mutex runningMutex;
|
||||
std::mutex chunkMutex;
|
||||
/**
|
||||
* @brief Registers a new audio chunk request.
|
||||
*
|
||||
@@ -54,4 +55,4 @@ public:
|
||||
void close();
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
#ifndef AUDIOSINK_H
|
||||
#define AUDIOSINK_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
class AudioSink
|
||||
{
|
||||
public:
|
||||
AudioSink() {}
|
||||
virtual ~AudioSink() {}
|
||||
virtual void feedPCMFrames(std::vector<uint8_t> &data) = 0;
|
||||
virtual void volumeChanged(uint16_t volume) {}
|
||||
bool softwareVolumeControl = true;
|
||||
bool usign = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "AudioSink.h"
|
||||
#include "AudioChunk.h"
|
||||
#include "platform/WrappedMutex.h"
|
||||
#include "ChunkedByteStream.h"
|
||||
|
||||
#define SPOTIFY_HEADER_SIZE 167
|
||||
#define BUFFER_SIZE 0x20000 * 1.5
|
||||
@@ -32,10 +33,6 @@ private:
|
||||
ov_callbacks vorbisCallbacks;
|
||||
int currentSection;
|
||||
|
||||
// Audio chunking
|
||||
std::vector<uint8_t> audioKey;
|
||||
std::vector<std::shared_ptr<AudioChunk>> chunks;
|
||||
|
||||
// Audio data
|
||||
uint32_t duration;
|
||||
|
||||
@@ -46,19 +43,12 @@ private:
|
||||
std::vector<uint8_t> fileId;
|
||||
uint32_t startPositionMs;
|
||||
|
||||
std::shared_ptr<AudioChunk> requestChunk(size_t chunkIndex);
|
||||
void fetchTraillingPacket();
|
||||
std::shared_ptr<AudioChunk> findChunkForPosition(size_t position);
|
||||
|
||||
public:
|
||||
ChunkedAudioStream(std::vector<uint8_t> fileId, std::vector<uint8_t> audioKey, uint32_t duration, std::shared_ptr<MercuryManager> manager, uint32_t startPositionMs, bool isPaused);
|
||||
~ChunkedAudioStream();
|
||||
int requestedChunkIndex = 0;
|
||||
std::shared_ptr<ChunkedByteStream> byteStream;
|
||||
|
||||
std::function<void()> streamFinishedCallback;
|
||||
size_t pos = SPOTIFY_HEADER_SIZE; // size of some spotify header
|
||||
uint32_t fileSize;
|
||||
uint32_t readBeforeSeek = 0;
|
||||
bool loadingMeta = true;
|
||||
std::atomic<bool> isPaused = false;
|
||||
std::atomic<bool> isRunning = false;
|
||||
std::atomic<bool> finished = false;
|
||||
|
||||
@@ -10,16 +10,17 @@ AudioChunk::AudioChunk(uint16_t seqId, std::vector<uint8_t> &audioKey, uint32_t
|
||||
this->startPosition = startPosition;
|
||||
this->endPosition = predictedEndPosition;
|
||||
this->decryptedData = std::vector<uint8_t>();
|
||||
this->isHeaderFileSizeLoadedSemaphore = std::make_unique<WrappedSemaphore>(2);
|
||||
this->isLoadedSemaphore = std::make_unique<WrappedSemaphore>(2);
|
||||
this->isHeaderFileSizeLoadedSemaphore = std::make_unique<WrappedSemaphore>(5);
|
||||
this->isLoadedSemaphore = std::make_unique<WrappedSemaphore>(5);
|
||||
}
|
||||
|
||||
AudioChunk::~AudioChunk()
|
||||
{
|
||||
}
|
||||
|
||||
void AudioChunk::appendData(std::vector<uint8_t> &data)
|
||||
void AudioChunk::appendData(const std::vector<uint8_t> &data)
|
||||
{
|
||||
//if (this == nullptr) return;
|
||||
this->decryptedData.insert(this->decryptedData.end(), data.begin(), data.end());
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "Logger.h"
|
||||
|
||||
AudioChunkManager::AudioChunkManager()
|
||||
: bell::Task("AudioChunkManager", 4 * 1024, +1, 0) {
|
||||
: bell::Task("AudioChunkManager", 4 * 1024, 2, 0) {
|
||||
this->chunks = std::vector<std::shared_ptr<AudioChunk>>();
|
||||
startTask();
|
||||
}
|
||||
@@ -12,6 +12,7 @@ std::shared_ptr<AudioChunk>
|
||||
AudioChunkManager::registerNewChunk(uint16_t seqId,
|
||||
std::vector<uint8_t> &audioKey,
|
||||
uint32_t startPos, uint32_t endPos) {
|
||||
std::scoped_lock lock(chunkMutex);
|
||||
auto chunk =
|
||||
std::make_shared<AudioChunk>(seqId, audioKey, startPos * 4, endPos * 4);
|
||||
this->chunks.push_back(chunk);
|
||||
@@ -26,6 +27,7 @@ void AudioChunkManager::handleChunkData(std::vector<uint8_t> &data,
|
||||
}
|
||||
|
||||
void AudioChunkManager::failAllChunks() {
|
||||
std::scoped_lock lock(chunkMutex);
|
||||
// Enumerate all the chunks and mark em all failed
|
||||
for (auto const &chunk : this->chunks) {
|
||||
if (!chunk->isLoaded) {
|
||||
@@ -47,9 +49,10 @@ void AudioChunkManager::close() {
|
||||
void AudioChunkManager::runTask() {
|
||||
std::scoped_lock lock(this->runningMutex);
|
||||
this->isRunning = true;
|
||||
std::pair<std::vector<uint8_t>, bool> audioPair;
|
||||
while (isRunning) {
|
||||
std::pair<std::vector<uint8_t>, bool> audioPair;
|
||||
if (this->audioChunkDataQueue.wtpop(audioPair, 100)) {
|
||||
std::scoped_lock lock(this->chunkMutex);
|
||||
auto data = audioPair.first;
|
||||
auto failed = audioPair.second;
|
||||
uint16_t seqId = ntohs(extract<uint16_t>(data, 0));
|
||||
@@ -57,7 +60,7 @@ void AudioChunkManager::runTask() {
|
||||
// Erase all chunks that are not referenced elsewhere anymore
|
||||
chunks.erase(
|
||||
std::remove_if(chunks.begin(), chunks.end(),
|
||||
[](const std::shared_ptr<AudioChunk> &chunk) {
|
||||
[](std::shared_ptr<AudioChunk>& chunk) {
|
||||
return chunk.use_count() == 1;
|
||||
}),
|
||||
chunks.end());
|
||||
@@ -67,7 +70,7 @@ void AudioChunkManager::runTask() {
|
||||
// Found the right chunk
|
||||
if (chunk != nullptr && chunk->seqId == seqId) {
|
||||
if (failed) {
|
||||
// chunk->isFailed = true;
|
||||
chunk->isFailed = true;
|
||||
chunk->startPosition = 0;
|
||||
chunk->endPosition = 0;
|
||||
chunk->isHeaderFileSizeLoadedSemaphore->give();
|
||||
@@ -96,9 +99,6 @@ void AudioChunkManager::runTask() {
|
||||
break;
|
||||
|
||||
default:
|
||||
if (chunk.get() == nullptr) {
|
||||
return;
|
||||
}
|
||||
auto actualData = std::vector<uint8_t>(
|
||||
data.begin() + 2, data.end());
|
||||
chunk->appendData(actualData);
|
||||
@@ -109,8 +109,6 @@ void AudioChunkManager::runTask() {
|
||||
|
||||
} catch (...) {
|
||||
}
|
||||
} else {
|
||||
usleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
|
||||
static size_t vorbisReadCb(void *ptr, size_t size, size_t nmemb, ChunkedAudioStream *self)
|
||||
{
|
||||
auto data = self->read(nmemb);
|
||||
std::copy(data.begin(), data.end(), (char *)ptr);
|
||||
return data.size();
|
||||
size_t readSize = 0;
|
||||
while (readSize < nmemb * size && self->byteStream->position() < self->byteStream->size()) {
|
||||
readSize += self->byteStream->read((uint8_t *) ptr + readSize, (size * nmemb) - readSize);
|
||||
}
|
||||
return readSize;
|
||||
}
|
||||
static int vorbisCloseCb(ChunkedAudioStream *self)
|
||||
{
|
||||
@@ -29,7 +31,7 @@ static int vorbisSeekCb(ChunkedAudioStream *self, int64_t offset, int whence)
|
||||
|
||||
static long vorbisTellCb(ChunkedAudioStream *self)
|
||||
{
|
||||
return static_cast<long>(self->pos);
|
||||
return static_cast<long>(self->byteStream->position());
|
||||
}
|
||||
|
||||
ChunkedAudioStream::~ChunkedAudioStream()
|
||||
@@ -38,22 +40,22 @@ ChunkedAudioStream::~ChunkedAudioStream()
|
||||
|
||||
ChunkedAudioStream::ChunkedAudioStream(std::vector<uint8_t> fileId, std::vector<uint8_t> audioKey, uint32_t duration, std::shared_ptr<MercuryManager> manager, uint32_t startPositionMs, bool isPaused)
|
||||
{
|
||||
this->audioKey = audioKey;
|
||||
this->duration = duration;
|
||||
this->manager = manager;
|
||||
this->fileId = fileId;
|
||||
this->startPositionMs = startPositionMs;
|
||||
this->isPaused = isPaused;
|
||||
|
||||
auto beginChunk = manager->fetchAudioChunk(fileId, audioKey, 0, 0x4000);
|
||||
beginChunk->keepInMemory = true;
|
||||
while(beginChunk->isHeaderFileSizeLoadedSemaphore->twait() != 0);
|
||||
this->fileSize = beginChunk->headerFileSize;
|
||||
chunks.push_back(beginChunk);
|
||||
|
||||
// File size is required for this packet to be downloaded
|
||||
this->fetchTraillingPacket();
|
||||
// auto beginChunk = manager->fetchAudioChunk(fileId, audioKey, 0, 0x4000);
|
||||
// beginChunk->keepInMemory = true;
|
||||
// while(beginChunk->isHeaderFileSizeLoadedSemaphore->twait() != 0);
|
||||
// this->fileSize = beginChunk->headerFileSize;
|
||||
// chunks.push_back(beginChunk);
|
||||
//
|
||||
// // File size is required for this packet to be downloaded
|
||||
// this->fetchTraillingPacket();
|
||||
|
||||
this->byteStream = std::make_shared<ChunkedByteStream>(manager);
|
||||
this->byteStream->setFileInfo(fileId, audioKey);
|
||||
this->byteStream->fetchFileInformation();
|
||||
vorbisFile = { };
|
||||
vorbisCallbacks =
|
||||
{
|
||||
@@ -66,12 +68,11 @@ ChunkedAudioStream::ChunkedAudioStream(std::vector<uint8_t> fileId, std::vector<
|
||||
|
||||
void ChunkedAudioStream::seekMs(uint32_t positionMs)
|
||||
{
|
||||
|
||||
byteStream->setEnableLoadAhead(false);
|
||||
this->seekMutex.lock();
|
||||
loadingMeta = true;
|
||||
ov_time_seek(&vorbisFile, positionMs);
|
||||
loadingMeta = false;
|
||||
this->seekMutex.unlock();
|
||||
byteStream->setEnableLoadAhead(true);
|
||||
|
||||
CSPOT_LOG(debug, "--- Finished seeking!");
|
||||
}
|
||||
@@ -79,33 +80,31 @@ void ChunkedAudioStream::seekMs(uint32_t positionMs)
|
||||
void ChunkedAudioStream::startPlaybackLoop()
|
||||
{
|
||||
|
||||
loadingMeta = true;
|
||||
isRunning = true;
|
||||
|
||||
byteStream->setEnableLoadAhead(false);
|
||||
int32_t r = ov_open_callbacks(this, &vorbisFile, NULL, 0, vorbisCallbacks);
|
||||
CSPOT_LOG(debug, "--- Loaded file");
|
||||
if (this->startPositionMs != 0)
|
||||
{
|
||||
ov_time_seek(&vorbisFile, startPositionMs);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->requestChunk(0);
|
||||
ov_time_seek(&vorbisFile, startPositionMs);
|
||||
}
|
||||
|
||||
loadingMeta = false;
|
||||
bool eof = false;
|
||||
std::vector<uint8_t> pcmOut(4096 / 4);
|
||||
byteStream->setEnableLoadAhead(true);
|
||||
|
||||
while (!eof && isRunning)
|
||||
{
|
||||
if (!isPaused)
|
||||
{
|
||||
std::vector<uint8_t> pcmOut(4096 / 4);
|
||||
|
||||
this->seekMutex.lock();
|
||||
long ret = ov_read(&vorbisFile, (char *)&pcmOut[0], 4096 / 4, ¤tSection);
|
||||
this->seekMutex.unlock();
|
||||
if (ret == 0)
|
||||
{
|
||||
CSPOT_LOG(info, "EOL");
|
||||
// and done :)
|
||||
eof = true;
|
||||
}
|
||||
@@ -139,193 +138,37 @@ void ChunkedAudioStream::startPlaybackLoop()
|
||||
this->streamFinishedCallback();
|
||||
}
|
||||
}
|
||||
|
||||
void ChunkedAudioStream::fetchTraillingPacket()
|
||||
{
|
||||
auto startPosition = (this->fileSize / 4) - 0x1000;
|
||||
|
||||
// AES block size is 16, so the index must be divisible by it
|
||||
while ((startPosition * 4) % 16 != 0)
|
||||
startPosition++; // ik, ugly lol
|
||||
|
||||
auto endChunk = manager->fetchAudioChunk(fileId, audioKey, startPosition, fileSize / 4);
|
||||
endChunk->keepInMemory = true;
|
||||
|
||||
chunks.push_back(endChunk);
|
||||
while (endChunk->isLoadedSemaphore->twait() != 0);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ChunkedAudioStream::read(size_t bytes)
|
||||
{
|
||||
auto toRead = bytes;
|
||||
auto res = std::vector<uint8_t>();
|
||||
READ:
|
||||
while (res.size() < bytes)
|
||||
{
|
||||
auto position = pos;
|
||||
auto isLoadingMeta = loadingMeta;
|
||||
|
||||
// Erase all chunks not close to current position
|
||||
chunks.erase(std::remove_if(
|
||||
chunks.begin(), chunks.end(),
|
||||
[position, &isLoadingMeta](const std::shared_ptr<AudioChunk> &chunk) {
|
||||
if (isLoadingMeta) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (chunk->keepInMemory)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (chunk->isFailed)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (chunk->endPosition < position || chunk->startPosition > position + BUFFER_SIZE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}),
|
||||
chunks.end());
|
||||
|
||||
int16_t chunkIndex = this->pos / AUDIO_CHUNK_SIZE;
|
||||
int32_t offset = this->pos % AUDIO_CHUNK_SIZE;
|
||||
|
||||
if (pos >= fileSize)
|
||||
{
|
||||
|
||||
CSPOT_LOG(debug, "EOL!");
|
||||
return res;
|
||||
}
|
||||
|
||||
auto chunk = findChunkForPosition(pos);
|
||||
|
||||
if (chunk != nullptr)
|
||||
{
|
||||
auto offset = pos - chunk->startPosition;
|
||||
if (chunk->isLoaded)
|
||||
{
|
||||
if (chunk->decryptedData.size() - offset >= toRead)
|
||||
{
|
||||
if((chunk->decryptedData.begin() + offset) < chunk->decryptedData.end()) {
|
||||
res.insert(res.end(), chunk->decryptedData.begin() + offset,
|
||||
chunk->decryptedData.begin() + offset + toRead);
|
||||
this->pos += toRead;
|
||||
} else {
|
||||
chunk->decrypt();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
res.insert(res.end(), chunk->decryptedData.begin() + offset, chunk->decryptedData.end());
|
||||
this->pos += chunk->decryptedData.size() - offset;
|
||||
toRead -= chunk->decryptedData.size() - offset;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CSPOT_LOG(debug, "Waiting for chunk to load");
|
||||
while (chunk->isLoadedSemaphore->twait() != 0);
|
||||
if (chunk->isFailed)
|
||||
{
|
||||
auto requestChunk = this->requestChunk(chunkIndex);
|
||||
while (requestChunk->isLoadedSemaphore->twait() != 0);
|
||||
goto READ;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CSPOT_LOG(debug, "Actual request %d", chunkIndex);
|
||||
this->requestChunk(chunkIndex);
|
||||
}
|
||||
}
|
||||
|
||||
if (!loadingMeta)
|
||||
{
|
||||
|
||||
auto requestedOffset = 0;
|
||||
|
||||
while (requestedOffset < BUFFER_SIZE)
|
||||
{
|
||||
auto chunk = findChunkForPosition(pos + requestedOffset);
|
||||
|
||||
if (chunk != nullptr)
|
||||
{
|
||||
requestedOffset = chunk->endPosition - pos;
|
||||
|
||||
// Don not buffer over EOL - unnecessary "failed chunks"
|
||||
if ((pos + requestedOffset) >= fileSize)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
auto chunkReq = manager->fetchAudioChunk(fileId, audioKey, (pos + requestedOffset) / 4, (pos + requestedOffset + AUDIO_CHUNK_SIZE) / 4);
|
||||
CSPOT_LOG(debug, "Chunk req end pos %d", chunkReq->endPosition);
|
||||
this->chunks.push_back(chunkReq);
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::shared_ptr<AudioChunk> ChunkedAudioStream::findChunkForPosition(size_t position)
|
||||
{
|
||||
for (int i = 0; i < this->chunks.size(); i++)
|
||||
{
|
||||
auto chunk = this->chunks[i];
|
||||
if (chunk->startPosition <= position && chunk->endPosition > position)
|
||||
{
|
||||
return chunk;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
//
|
||||
//void ChunkedAudioStream::fetchTraillingPacket()
|
||||
//{
|
||||
// auto startPosition = (this->fileSize / 4) - 0x1000;
|
||||
//
|
||||
// // AES block size is 16, so the index must be divisible by it
|
||||
// while ((startPosition * 4) % 16 != 0)
|
||||
// startPosition++; // ik, ugly lol
|
||||
//
|
||||
// auto endChunk = manager->fetchAudioChunk(fileId, audioKey, startPosition, fileSize / 4);
|
||||
// endChunk->keepInMemory = true;
|
||||
//
|
||||
// chunks.push_back(endChunk);
|
||||
// while (endChunk->isLoadedSemaphore->twait() != 0);
|
||||
//}
|
||||
|
||||
void ChunkedAudioStream::seek(size_t dpos, Whence whence)
|
||||
{
|
||||
BELL_LOG(info, "cspot", "%d", dpos);
|
||||
auto seekPos = 0;
|
||||
switch (whence)
|
||||
{
|
||||
case Whence::START:
|
||||
this->pos = dpos;
|
||||
seekPos = dpos;
|
||||
break;
|
||||
case Whence::CURRENT:
|
||||
this->pos += dpos;
|
||||
seekPos = byteStream->position() + dpos;
|
||||
break;
|
||||
case Whence::END:
|
||||
this->pos = fileSize + dpos;
|
||||
seekPos = byteStream->size() + dpos;
|
||||
break;
|
||||
}
|
||||
|
||||
auto currentChunk = this->pos / AUDIO_CHUNK_SIZE;
|
||||
|
||||
if (findChunkForPosition(this->pos) == nullptr)
|
||||
{
|
||||
// Seeking might look back - therefore we preload some past data
|
||||
auto startPosition = (this->pos / 4) - (AUDIO_CHUNK_SIZE / 4);
|
||||
|
||||
// AES block size is 16, so the index must be divisible by it
|
||||
while ((startPosition * 4) % 16 != 0)
|
||||
startPosition++; // ik, ugly lol
|
||||
|
||||
this->chunks.push_back(manager->fetchAudioChunk(fileId, audioKey, startPosition, startPosition + (AUDIO_CHUNK_SIZE / 4)));
|
||||
}
|
||||
CSPOT_LOG(debug, "Change in current chunk %d", currentChunk);
|
||||
}
|
||||
|
||||
std::shared_ptr<AudioChunk> ChunkedAudioStream::requestChunk(size_t chunkIndex)
|
||||
{
|
||||
|
||||
CSPOT_LOG(debug, "Chunk Req %d", chunkIndex);
|
||||
auto chunk = manager->fetchAudioChunk(fileId, audioKey, chunkIndex);
|
||||
this->chunks.push_back(chunk);
|
||||
return chunk;
|
||||
}
|
||||
byteStream->seek(seekPos);
|
||||
}
|
||||
@@ -9,7 +9,7 @@ std::map<MercuryType, std::string> MercuryTypeMap({
|
||||
{MercuryType::UNSUB, "UNSUB"},
|
||||
});
|
||||
|
||||
MercuryManager::MercuryManager(std::unique_ptr<Session> session): bell::Task("mercuryManager", 6 * 1024, +1, 1)
|
||||
MercuryManager::MercuryManager(std::unique_ptr<Session> session): bell::Task("mercuryManager", 6 * 1024, 2, 1)
|
||||
{
|
||||
tempMercuryHeader = Header_init_default;
|
||||
this->timeProvider = std::make_shared<TimeProvider>();
|
||||
@@ -30,7 +30,7 @@ MercuryManager::MercuryManager(std::unique_ptr<Session> session): bell::Task("me
|
||||
|
||||
MercuryManager::~MercuryManager()
|
||||
{
|
||||
pb_release(Header_fields, tempMercuryHeader);
|
||||
//pb_release(Header_fields, &tempMercuryHeader);
|
||||
}
|
||||
|
||||
bool MercuryManager::timeoutHandler()
|
||||
@@ -177,7 +177,6 @@ void MercuryManager::runTask()
|
||||
}
|
||||
if (static_cast<MercuryType>(packet->command) == MercuryType::PING) // @TODO: Handle time synchronization through ping
|
||||
{
|
||||
CSPOT_LOG(debug, "Got ping, syncing timestamp");
|
||||
this->timeProvider->syncWithPingPacket(packet->data);
|
||||
|
||||
this->lastPingTimestamp = this->timeProvider->getSyncedTimestamp();
|
||||
|
||||
@@ -9,7 +9,7 @@ MercuryResponse::MercuryResponse(std::vector<uint8_t> &data)
|
||||
}
|
||||
|
||||
MercuryResponse::~MercuryResponse() {
|
||||
pb_release(Header_fields, mercuryHeader);
|
||||
pb_release(Header_fields, &mercuryHeader);
|
||||
}
|
||||
|
||||
void MercuryResponse::parseResponse(std::vector<uint8_t> &data)
|
||||
@@ -34,6 +34,6 @@ void MercuryResponse::parseResponse(std::vector<uint8_t> &data)
|
||||
pos += 2 + partSize;
|
||||
}
|
||||
|
||||
pb_release(Header_fields, this->mercuryHeader);
|
||||
pb_release(Header_fields, &this->mercuryHeader);
|
||||
pbDecode(this->mercuryHeader, Header_fields, headerBytes);
|
||||
}
|
||||
@@ -104,6 +104,7 @@ std::vector<uint8_t> PlainConnection::readBlock(size_t size)
|
||||
std::vector<uint8_t> buf(size);
|
||||
unsigned int idx = 0;
|
||||
ssize_t n;
|
||||
int retries = 0;
|
||||
// printf("START READ\n");
|
||||
|
||||
while (idx < size)
|
||||
@@ -124,7 +125,8 @@ std::vector<uint8_t> PlainConnection::readBlock(size_t size)
|
||||
case EINTR:
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Corn");
|
||||
if (retries++ > 4) throw std::runtime_error("Error in read");
|
||||
|
||||
}
|
||||
}
|
||||
idx += n;
|
||||
@@ -138,6 +140,7 @@ size_t PlainConnection::writeBlock(const std::vector<uint8_t> &data)
|
||||
unsigned int idx = 0;
|
||||
ssize_t n;
|
||||
// printf("START WRITE\n");
|
||||
int retries = 0;
|
||||
|
||||
while (idx < data.size())
|
||||
{
|
||||
@@ -156,7 +159,7 @@ size_t PlainConnection::writeBlock(const std::vector<uint8_t> &data)
|
||||
case EINTR:
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Corn");
|
||||
if (retries++ > 4) throw std::runtime_error("Error in write");
|
||||
}
|
||||
}
|
||||
idx += n;
|
||||
|
||||
@@ -68,7 +68,7 @@ void Player::feedPCM(std::vector<uint8_t>& data)
|
||||
}
|
||||
}
|
||||
|
||||
this->audioSink->feedPCMFrames(data);
|
||||
this->audioSink->feedPCMFrames(data.data(), data.size());
|
||||
}
|
||||
|
||||
void Player::runTask()
|
||||
|
||||
@@ -53,8 +53,9 @@ PlayerState::PlayerState(std::shared_ptr<TimeProvider> timeProvider)
|
||||
}
|
||||
|
||||
PlayerState::~PlayerState() {
|
||||
pb_release(Frame_fields, innerFrame);
|
||||
pb_release(Frame_fields, remoteFrame);
|
||||
pb_release(Frame_fields, &remoteFrame);
|
||||
// do not destruct inner frame as it is never allocated
|
||||
// pb_release(Frame_fields, &innerFrame);
|
||||
}
|
||||
|
||||
void PlayerState::setPlaybackState(const PlaybackState state)
|
||||
@@ -136,7 +137,7 @@ void PlayerState::updatePositionMs(uint32_t position)
|
||||
void PlayerState::updateTracks()
|
||||
{
|
||||
CSPOT_LOG(info, "---- Track count %d", remoteFrame.state.track_count);
|
||||
innerFrame.state.context_uri = remoteFrame.state.context_uri == nullptr ? nullptr : strdup(remoteFrame.state.context_uri);
|
||||
//innerFrame.state.context_uri = remoteFrame.state.context_uri == nullptr ? nullptr : strdup(remoteFrame.state.context_uri);
|
||||
std::copy(std::begin(remoteFrame.state.track), std::end(remoteFrame.state.track), std::begin(innerFrame.state.track));
|
||||
innerFrame.state.track_count = remoteFrame.state.track_count;
|
||||
innerFrame.state.has_playing_track_index = true;
|
||||
|
||||
@@ -6,10 +6,10 @@ using random_bytes_engine = std::independent_bits_engine<std::default_random_eng
|
||||
|
||||
Session::Session()
|
||||
{
|
||||
this->clientHello = ClientHello_init_default;
|
||||
this->apResponse = APResponseMessage_init_default;
|
||||
this->authRequest = ClientResponseEncrypted_init_default;
|
||||
this->clientResPlaintext = ClientResponsePlaintext_init_default;
|
||||
this->clientHello = {};
|
||||
this->apResponse = {};
|
||||
this->authRequest = {};
|
||||
this->clientResPlaintext = {};
|
||||
|
||||
// Generates the public and priv key
|
||||
this->crypto = std::make_unique<Crypto>();
|
||||
@@ -18,10 +18,9 @@ Session::Session()
|
||||
|
||||
Session::~Session()
|
||||
{
|
||||
pb_release(ClientHello_fields, clientHello);
|
||||
pb_release(APResponseMessage_fields, apResponse);
|
||||
pb_release(ClientResponseEncrypted_fields, authRequest);
|
||||
pb_release(ClientResponsePlaintext_fields, clientResPlaintext);
|
||||
pb_release(ClientHello_fields, &clientHello);
|
||||
pb_release(APResponseMessage_fields, &apResponse);
|
||||
pb_release(ClientResponsePlaintext_fields, &clientResPlaintext);
|
||||
}
|
||||
|
||||
void Session::connect(std::unique_ptr<PlainConnection> connection)
|
||||
@@ -60,6 +59,7 @@ std::vector<uint8_t> Session::authenticate(std::shared_ptr<LoginBlob> blob)
|
||||
authRequest.version_string = (char *)versionString;
|
||||
|
||||
auto data = pbEncode(ClientResponseEncrypted_fields, &authRequest);
|
||||
free(authRequest.login_credentials.auth_data);
|
||||
|
||||
// Send login request
|
||||
this->shanConn->sendPacket(LOGIN_REQUEST_COMMAND, data);
|
||||
@@ -72,7 +72,7 @@ std::vector<uint8_t> Session::authenticate(std::shared_ptr<LoginBlob> blob)
|
||||
CSPOT_LOG(debug, "Authorization successful");
|
||||
|
||||
// @TODO store the reusable credentials
|
||||
// PBWrapper<APWelcome> welcomePacket(packet->data)
|
||||
// PBWrapper<APWelcome> welcomePacket(packet->data)
|
||||
return std::vector<uint8_t>({0x1}); // TODO: return actual reusable credentaials to be stored somewhere
|
||||
break;
|
||||
}
|
||||
@@ -96,7 +96,7 @@ void Session::processAPHelloResponse(std::vector<uint8_t> &helloPacket)
|
||||
// Decode the response
|
||||
auto skipSize = std::vector<uint8_t>(data.begin() + 4, data.end());
|
||||
|
||||
pb_release(APResponseMessage_fields, apResponse);
|
||||
pb_release(APResponseMessage_fields, &apResponse);
|
||||
pbDecode(apResponse, APResponseMessage_fields, skipSize);
|
||||
|
||||
auto diffieKey = std::vector<uint8_t>(apResponse.challenge.login_crypto_challenge.diffie_hellman.gs, apResponse.challenge.login_crypto_challenge.diffie_hellman.gs + 96);
|
||||
|
||||
@@ -103,7 +103,7 @@ void SpircController::prevSong() {
|
||||
}
|
||||
|
||||
void SpircController::handleFrame(std::vector<uint8_t> &data) {
|
||||
pb_release(Frame_fields, state->remoteFrame);
|
||||
//pb_release(Frame_fields, &state->remoteFrame);
|
||||
pbDecode(state->remoteFrame, Frame_fields, data);
|
||||
|
||||
switch (state->remoteFrame.typ) {
|
||||
|
||||
@@ -35,8 +35,8 @@ SpotifyTrack::~SpotifyTrack()
|
||||
{
|
||||
this->manager->unregisterMercuryCallback(this->reqSeqNum);
|
||||
this->manager->freeAudioKeyCallback();
|
||||
pb_release(Track_fields, this->trackInfo);
|
||||
pb_release(Episode_fields, this->episodeInfo);
|
||||
pb_release(Track_fields, &this->trackInfo);
|
||||
pb_release(Episode_fields, &this->episodeInfo);
|
||||
}
|
||||
|
||||
bool SpotifyTrack::countryListContains(std::string countryList, std::string country)
|
||||
@@ -75,7 +75,7 @@ void SpotifyTrack::trackInformationCallback(std::unique_ptr<MercuryResponse> res
|
||||
return;
|
||||
CSPOT_ASSERT(response->parts.size() > 0, "response->parts.size() must be greater than 0");
|
||||
|
||||
pb_release(Track_fields, trackInfo);
|
||||
pb_release(Track_fields, &trackInfo);
|
||||
pbDecode(trackInfo, Track_fields, response->parts[0]);
|
||||
|
||||
CSPOT_LOG(info, "Track name: %s", trackInfo.name);
|
||||
@@ -127,7 +127,7 @@ void SpotifyTrack::episodeInformationCallback(std::unique_ptr<MercuryResponse> r
|
||||
return;
|
||||
CSPOT_LOG(debug, "Got to episode");
|
||||
CSPOT_ASSERT(response->parts.size() > 0, "response->parts.size() must be greater than 0");
|
||||
pb_release(Episode_fields, episodeInfo);
|
||||
pb_release(Episode_fields, &episodeInfo);
|
||||
pbDecode(episodeInfo, Episode_fields, response->parts[0]);
|
||||
|
||||
CSPOT_LOG(info, "--- Episode name: %s", episodeInfo.name);
|
||||
|
||||
@@ -19,8 +19,9 @@ TrackReference::TrackReference(TrackRef *ref)
|
||||
|
||||
TrackReference::~TrackReference()
|
||||
{
|
||||
pb_release(TrackRef_fields, ref);
|
||||
}
|
||||
//pb_release(TrackRef_fields, &ref);
|
||||
//pbFree(TrackRef_fields, &ref);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> TrackReference::base62Decode(std::string uri)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user