mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-07 03:57:07 +03:00
Merge remote-tracking branch 'origin/master-v4.3' into master-v4.3
This commit is contained in:
@@ -281,6 +281,19 @@ CONFIG_LED_GREEN_GPIO=-1
|
||||
CONFIG_LED_RED_GPIO=-1
|
||||
# end of LED configuration
|
||||
|
||||
|
||||
#
|
||||
# Audio controls
|
||||
#
|
||||
CONFIG_AUDIO_CONTROLS=""
|
||||
# end of Audio Contorls configuration
|
||||
|
||||
#
|
||||
# AMP configuration
|
||||
#
|
||||
CONFIG_AMP_GPIO=-1
|
||||
# end of AMP configuration
|
||||
|
||||
#
|
||||
# Audio JACK
|
||||
#
|
||||
@@ -296,7 +309,7 @@ CONFIG_SPKFAULT_GPIO=-1
|
||||
#
|
||||
# Battery measure
|
||||
#
|
||||
CONFIG_BAT_CHANNEL=-1
|
||||
CONFIG_BAT_CONFIG=""
|
||||
# end of Battery measure
|
||||
|
||||
CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -C 30 -W"
|
||||
@@ -533,7 +546,8 @@ CONFIG_ESP_TLS_USING_MBEDTLS=y
|
||||
# CONFIG_ESP_TLS_USE_SECURE_ELEMENT is not set
|
||||
# CONFIG_ESP_TLS_SERVER is not set
|
||||
# CONFIG_ESP_TLS_PSK_VERIFICATION is not set
|
||||
# CONFIG_ESP_TLS_INSECURE is not set
|
||||
CONFIG_ESP_TLS_INSECURE=y
|
||||
CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y
|
||||
# end of ESP-TLS
|
||||
|
||||
#
|
||||
@@ -727,7 +741,7 @@ CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y
|
||||
#
|
||||
# ESP HTTP client
|
||||
#
|
||||
# CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS is not set
|
||||
CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y
|
||||
# CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH is not set
|
||||
# end of ESP HTTP client
|
||||
|
||||
|
||||
@@ -269,11 +269,22 @@ CONFIG_JACK_GPIO=34
|
||||
CONFIG_JACK_GPIO_LEVEL=0
|
||||
CONFIG_SPKFAULT_GPIO=2
|
||||
CONFIG_SPKFAULT_GPIO_LEVEL=0
|
||||
CONFIG_BAT_CHANNEL=7
|
||||
CONFIG_BAT_SCALE="20.24"
|
||||
CONFIG_BAT_CONFIG="channel=7,scale=20.24,atten=0"
|
||||
CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -C 30 -W"
|
||||
# end of Squeezelite-ESP32
|
||||
|
||||
#
|
||||
# Audio controls
|
||||
#
|
||||
CONFIG_AUDIO_CONTROLS=""
|
||||
# end of Audio Contorls configuration
|
||||
|
||||
#
|
||||
# AMP configuration
|
||||
#
|
||||
CONFIG_AMP_GPIO=-1
|
||||
# end of AMP configuration
|
||||
|
||||
#
|
||||
# Compiler options
|
||||
#
|
||||
@@ -505,7 +516,8 @@ CONFIG_ESP_TLS_USING_MBEDTLS=y
|
||||
# CONFIG_ESP_TLS_USE_SECURE_ELEMENT is not set
|
||||
# CONFIG_ESP_TLS_SERVER is not set
|
||||
# CONFIG_ESP_TLS_PSK_VERIFICATION is not set
|
||||
# CONFIG_ESP_TLS_INSECURE is not set
|
||||
CONFIG_ESP_TLS_INSECURE=y
|
||||
CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y
|
||||
# end of ESP-TLS
|
||||
|
||||
#
|
||||
@@ -699,7 +711,7 @@ CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y
|
||||
#
|
||||
# ESP HTTP client
|
||||
#
|
||||
# CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS is not set
|
||||
CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y
|
||||
# CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH is not set
|
||||
# end of ESP HTTP client
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ idf_component_register(SRC_DIRS . core core/ifaces fonts
|
||||
INCLUDE_DIRS . fonts core
|
||||
REQUIRES platform_config tools esp_common
|
||||
PRIV_REQUIRES services freertos driver
|
||||
EMBED_FILES note.jpg
|
||||
)
|
||||
|
||||
set_source_files_properties(display.c
|
||||
|
||||
@@ -86,7 +86,7 @@ static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
|
||||
Device->WriteCommand( Device, Contrast );
|
||||
}
|
||||
|
||||
static void SPIParams(int Speed, uint8_t *mode, uint8_t *CS_pre, uint8_t *CS_post) {
|
||||
static void SPIParams(int Speed, uint8_t *mode, uint16_t *CS_pre, uint8_t *CS_post) {
|
||||
*CS_post = Speed / (8*1000*1000);
|
||||
}
|
||||
|
||||
|
||||
@@ -235,12 +235,13 @@ void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
|
||||
ledc_update_duty( LEDC_HIGH_SPEED_MODE, Device->Backlight.Channel );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GDS_SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) { if (Device->SetLayout) Device->SetLayout( Device, HFlip, VFlip, Rotate ); }
|
||||
void GDS_SetDirty( struct GDS_Device* Device ) { Device->Dirty = true; }
|
||||
int GDS_GetWidth( struct GDS_Device* Device ) { return Device ? Device->Width : 0; }
|
||||
int GDS_GetHeight( struct GDS_Device* Device ) { return Device ? Device->Height : 0; }
|
||||
int GDS_GetDepth( struct GDS_Device* Device ) { return Device ? Device->Depth : 0; }
|
||||
int GDS_GetMode( struct GDS_Device* Device ) { return Device ? Device->Mode : 0; }
|
||||
int GDS_GetWidth( struct GDS_Device* Device ) { return Device ? Device->Width : 0; }
|
||||
void GDS_SetTextWidth( struct GDS_Device* Device, int TextWidth ) { Device->TextWidth = Device && TextWidth && TextWidth < Device->Width ? TextWidth : Device->Width; }
|
||||
int GDS_GetHeight( struct GDS_Device* Device ) { return Device ? Device->Height : 0; }
|
||||
int GDS_GetDepth( struct GDS_Device* Device ) { return Device ? Device->Depth : 0; }
|
||||
int GDS_GetMode( struct GDS_Device* Device ) { return Device ? Device->Mode : 0; }
|
||||
void GDS_DisplayOn( struct GDS_Device* Device ) { if (Device->DisplayOn) Device->DisplayOn( Device ); }
|
||||
void GDS_DisplayOff( struct GDS_Device* Device ) { if (Device->DisplayOff) Device->DisplayOff( Device ); }
|
||||
@@ -38,6 +38,7 @@ void GDS_Update( struct GDS_Device* Device );
|
||||
void GDS_SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate );
|
||||
void GDS_SetDirty( struct GDS_Device* Device );
|
||||
int GDS_GetWidth( struct GDS_Device* Device );
|
||||
void GDS_SetTextWidth( struct GDS_Device* Device, int TextWidth );
|
||||
int GDS_GetHeight( struct GDS_Device* Device );
|
||||
int GDS_GetDepth( struct GDS_Device* Device );
|
||||
int GDS_GetMode( struct GDS_Device* Device );
|
||||
|
||||
@@ -73,13 +73,13 @@ void GDS_FontDrawChar( struct GDS_Device* Device, char Character, int x, int y,
|
||||
CharStartY+= OffsetY;
|
||||
|
||||
/* Do not attempt to draw if this character is entirely offscreen */
|
||||
if ( CharEndX < 0 || CharStartX >= Device->Width || CharEndY < 0 || CharStartY >= Device->Height ) {
|
||||
if ( CharEndX < 0 || CharStartX >= Device->TextWidth || CharEndY < 0 || CharStartY >= Device->Height ) {
|
||||
ClipDebug( x, y );
|
||||
return;
|
||||
}
|
||||
|
||||
/* Do not attempt to draw past the end of the screen */
|
||||
CharEndX = ( CharEndX >= Device->Width ) ? Device->Width - 1 : CharEndX;
|
||||
CharEndX = ( CharEndX >= Device->TextWidth ) ? Device->TextWidth - 1 : CharEndX;
|
||||
CharEndY = ( CharEndY >= Device->Height ) ? Device->Height - 1 : CharEndY;
|
||||
Device->Dirty = true;
|
||||
|
||||
@@ -146,7 +146,7 @@ int GDS_FontGetCharWidth( struct GDS_Device* Display, char Character ) {
|
||||
}
|
||||
|
||||
int GDS_FontGetMaxCharsPerRow( struct GDS_Device* Display ) {
|
||||
return Display->Width / Display->Font->Width;
|
||||
return Display->TextWidth / Display->Font->Width;
|
||||
}
|
||||
|
||||
int GDS_FontGetMaxCharsPerColumn( struct GDS_Device* Display ) {
|
||||
@@ -210,7 +210,7 @@ void GDS_FontGetAnchoredStringCoords( struct GDS_Device* Display, int* OutX, int
|
||||
switch ( Anchor ) {
|
||||
case TextAnchor_East: {
|
||||
*OutY = ( Display->Height / 2 ) - ( StringHeight / 2 );
|
||||
*OutX = ( Display->Width - StringWidth );
|
||||
*OutX = ( Display->TextWidth - StringWidth );
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -221,19 +221,19 @@ void GDS_FontGetAnchoredStringCoords( struct GDS_Device* Display, int* OutX, int
|
||||
break;
|
||||
}
|
||||
case TextAnchor_North: {
|
||||
*OutX = ( Display->Width / 2 ) - ( StringWidth / 2 );
|
||||
*OutX = ( Display->TextWidth / 2 ) - ( StringWidth / 2 );
|
||||
*OutY = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
case TextAnchor_South: {
|
||||
*OutX = ( Display->Width / 2 ) - ( StringWidth / 2 );
|
||||
*OutX = ( Display->TextWidth / 2 ) - ( StringWidth / 2 );
|
||||
*OutY = ( Display->Height - StringHeight );
|
||||
|
||||
break;
|
||||
}
|
||||
case TextAnchor_NorthEast: {
|
||||
*OutX = ( Display->Width - StringWidth );
|
||||
*OutX = ( Display->TextWidth - StringWidth );
|
||||
*OutY = 0;
|
||||
|
||||
break;
|
||||
@@ -246,7 +246,7 @@ void GDS_FontGetAnchoredStringCoords( struct GDS_Device* Display, int* OutX, int
|
||||
}
|
||||
case TextAnchor_SouthEast: {
|
||||
*OutY = ( Display->Height - StringHeight );
|
||||
*OutX = ( Display->Width - StringWidth );
|
||||
*OutX = ( Display->TextWidth - StringWidth );
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -258,7 +258,7 @@ void GDS_FontGetAnchoredStringCoords( struct GDS_Device* Display, int* OutX, int
|
||||
}
|
||||
case TextAnchor_Center: {
|
||||
*OutY = ( Display->Height / 2 ) - ( StringHeight / 2 );
|
||||
*OutX = ( Display->Width / 2 ) - ( StringWidth / 2 );
|
||||
*OutX = ( Display->TextWidth / 2 ) - ( StringWidth / 2 );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ static unsigned OutHandlerDirect(JDEC *Decoder, void *Bitmap, JRECT *Frame) {
|
||||
JpegCtx *Context = (JpegCtx*) Decoder->device;
|
||||
uint8_t *Pixels = (uint8_t*) Bitmap;
|
||||
int Shift = 8 - Context->Depth;
|
||||
|
||||
|
||||
// decoded image is RGB888, shift only make sense for grayscale
|
||||
if (Context->Mode == GDS_RGB888) {
|
||||
OUTHANDLERDIRECT(Scaler888, 0);
|
||||
@@ -167,7 +167,7 @@ static unsigned OutHandlerDirect(JDEC *Decoder, void *Bitmap, JRECT *Frame) {
|
||||
static void* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scale, bool SizeOnly, int RGB_Mode) {
|
||||
JDEC Decoder;
|
||||
JpegCtx Context;
|
||||
char *Scratch = calloc(SCRATCH_SIZE, 1);
|
||||
char *Scratch = malloc(SCRATCH_SIZE);
|
||||
|
||||
if (!Scratch) {
|
||||
ESP_LOGE(TAG, "Cannot allocate workspace");
|
||||
@@ -372,7 +372,7 @@ bool GDS_DrawJPEG(struct GDS_Device* Device, uint8_t *Source, int x, int y, int
|
||||
JDEC Decoder;
|
||||
JpegCtx Context;
|
||||
bool Ret = false;
|
||||
char *Scratch = calloc(SCRATCH_SIZE, 1);
|
||||
char *Scratch = malloc(SCRATCH_SIZE);
|
||||
|
||||
if (!Scratch) {
|
||||
ESP_LOGE(TAG, "Cannot allocate workspace");
|
||||
|
||||
@@ -95,7 +95,7 @@ struct GDS_Device {
|
||||
const struct GDS_FontDef* Font;
|
||||
} Lines[MAX_LINES];
|
||||
|
||||
uint16_t Width;
|
||||
uint16_t Width, TextWidth;
|
||||
uint16_t Height;
|
||||
uint8_t Depth, Mode;
|
||||
|
||||
@@ -125,7 +125,7 @@ struct GDS_Device {
|
||||
void (*DrawRGB)( struct GDS_Device* Device, uint8_t *Image,int x, int y, int Width, int Height, int RGB_Mode );
|
||||
void (*ClearWindow)( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color );
|
||||
// may provide for tweaking
|
||||
void (*SPIParams)(int Speed, uint8_t *mode, uint8_t *CS_pre, uint8_t *CS_post);
|
||||
void (*SPIParams)(int Speed, uint8_t *mode, uint16_t *CS_pre, uint8_t *CS_post);
|
||||
|
||||
// interface-specific methods
|
||||
WriteCommandProc WriteCommand;
|
||||
|
||||
@@ -100,13 +100,13 @@ bool GDS_TextLine(struct GDS_Device* Device, int N, int Pos, int Attr, char *Tex
|
||||
Width = GDS_FontMeasureString( Device, Text );
|
||||
|
||||
// adjusting position, erase only EoL for rigth-justified
|
||||
if (Pos == GDS_TEXT_RIGHT) X = Device->Width - Width - 1;
|
||||
else if (Pos == GDS_TEXT_CENTER) X = (Device->Width - Width) / 2;
|
||||
if (Pos == GDS_TEXT_RIGHT) X = Device->TextWidth - Width - 1;
|
||||
else if (Pos == GDS_TEXT_CENTER) X = (Device->TextWidth - Width) / 2;
|
||||
|
||||
// erase if requested
|
||||
if (Attr & GDS_TEXT_CLEAR) {
|
||||
int Y_min = max(0, Device->Lines[N].Y), Y_max = max(0, Device->Lines[N].Y + Device->Lines[N].Font->Height);
|
||||
for (int c = (Attr & GDS_TEXT_CLEAR_EOL) ? X : 0; c < Device->Width; c++)
|
||||
for (int c = (Attr & GDS_TEXT_CLEAR_EOL) ? X : 0; c < Device->TextWidth; c++)
|
||||
for (int y = Y_min; y < Y_max; y++)
|
||||
DrawPixelFast( Device, c, y, GDS_COLOR_BLACK );
|
||||
}
|
||||
@@ -119,7 +119,7 @@ bool GDS_TextLine(struct GDS_Device* Device, int N, int Pos, int Attr, char *Tex
|
||||
Device->Dirty = true;
|
||||
if (Attr & GDS_TEXT_UPDATE) GDS_Update( Device );
|
||||
|
||||
return Width + X < Device->Width;
|
||||
return Width + X < Device->TextWidth;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
@@ -146,7 +146,7 @@ int GDS_TextStretch(struct GDS_Device* Device, int N, char *String, int Max) {
|
||||
|
||||
// we might already fit
|
||||
GDS_SetFont( Device, Device->Lines[N].Font );
|
||||
if (GDS_FontMeasureString( Device, String ) <= Device->Width) return 0;
|
||||
if (GDS_FontMeasureString( Device, String ) <= Device->TextWidth) return 0;
|
||||
|
||||
// add some space for better visual
|
||||
strncat(String, Space, Max-Len);
|
||||
@@ -157,7 +157,7 @@ int GDS_TextStretch(struct GDS_Device* Device, int N, char *String, int Max) {
|
||||
Boundary = GDS_FontMeasureString( Device, String );
|
||||
|
||||
// add a full display width
|
||||
while (Len < Max && GDS_FontMeasureString( Device, String ) - Boundary < Device->Width) {
|
||||
while (Len < Max && GDS_FontMeasureString( Device, String ) - Boundary < Device->TextWidth) {
|
||||
String[Len++] = String[Extra++];
|
||||
String[Len] = '\0';
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int
|
||||
Device->RSTPin = RSTPin;
|
||||
Device->Backlight.Pin = BacklightPin;
|
||||
Device->IF = GDS_IF_I2C;
|
||||
Device->Width = Width;
|
||||
Device->Width = Device->TextWidth = Width;
|
||||
Device->Height = Height;
|
||||
|
||||
if ( RSTPin >= 0 ) {
|
||||
|
||||
@@ -35,7 +35,7 @@ bool GDS_SPIInit( int SPI, int DC ) {
|
||||
}
|
||||
|
||||
bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int CSPin, int RSTPin, int BackLightPin, int Speed ) {
|
||||
spi_device_interface_config_t SPIDeviceConfig;
|
||||
spi_device_interface_config_t SPIDeviceConfig = { };
|
||||
spi_device_handle_t SPIDevice;
|
||||
|
||||
NullCheck( Device, return false );
|
||||
@@ -45,8 +45,6 @@ bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( CSPin, 0 ), return false );
|
||||
}
|
||||
|
||||
memset( &SPIDeviceConfig, 0, sizeof( spi_device_interface_config_t ) );
|
||||
|
||||
SPIDeviceConfig.clock_speed_hz = Speed > 0 ? Speed : SPI_MASTER_FREQ_8M;
|
||||
SPIDeviceConfig.spics_io_num = CSPin;
|
||||
SPIDeviceConfig.queue_size = 1;
|
||||
@@ -63,7 +61,7 @@ bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int
|
||||
Device->CSPin = CSPin;
|
||||
Device->Backlight.Pin = BackLightPin;
|
||||
Device->IF = GDS_IF_SPI;
|
||||
Device->Width = Width;
|
||||
Device->Width = Device->TextWidth = Width;
|
||||
Device->Height = Height;
|
||||
|
||||
if ( RSTPin >= 0 ) {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "gds_draw.h"
|
||||
#include "gds_text.h"
|
||||
#include "gds_font.h"
|
||||
#include "gds_image.h"
|
||||
|
||||
static const char *TAG = "display";
|
||||
|
||||
@@ -30,6 +31,9 @@ static const char *TAG = "display";
|
||||
#define SCROLLABLE_SIZE 384
|
||||
#define HEADER_SIZE 64
|
||||
#define DEFAULT_SLEEP 3600
|
||||
#define ARTWORK_BORDER 1
|
||||
|
||||
extern const uint8_t default_artwork[] asm("_binary_note_jpg_start");
|
||||
|
||||
static EXT_RAM_ATTR struct {
|
||||
TaskHandle_t task;
|
||||
@@ -47,6 +51,13 @@ static EXT_RAM_ATTR struct {
|
||||
char string[8]; // H:MM:SS
|
||||
bool visible;
|
||||
} duration;
|
||||
struct {
|
||||
bool enable, active;
|
||||
bool fit;
|
||||
bool updated;
|
||||
int tick;
|
||||
int offset;
|
||||
} artwork;
|
||||
TickType_t tick;
|
||||
} displayer;
|
||||
|
||||
@@ -147,6 +158,14 @@ void display_init(char *welcome) {
|
||||
GDS_TextSetFontAuto(display, 2, GDS_FONT_LINE_2, -3);
|
||||
|
||||
displayer.metadata_config = config_alloc_get(NVS_TYPE_STR, "metadata_config");
|
||||
|
||||
// leave room for artwork is display is horizontal-style
|
||||
if (strcasestr(displayer.metadata_config, "artwork")) {
|
||||
displayer.artwork.enable = true;
|
||||
displayer.artwork.fit = true;
|
||||
if (height <= 64 && width > height * 2) displayer.artwork.offset = width - height - ARTWORK_BORDER;
|
||||
PARSE_PARAM(displayer.metadata_config, "artwork", ':', displayer.artwork.fit);
|
||||
}
|
||||
}
|
||||
|
||||
free(config);
|
||||
@@ -225,7 +244,12 @@ static void displayer_task(void *args) {
|
||||
// just re-write the whole line it's easier
|
||||
GDS_TextLine(display, 1, GDS_TEXT_LEFT, GDS_TEXT_CLEAR, displayer.header);
|
||||
GDS_TextLine(display, 1, GDS_TEXT_RIGHT, GDS_TEXT_UPDATE, _line);
|
||||
|
||||
|
||||
// if we have not received artwork after 5s, display a default icon
|
||||
if (displayer.artwork.active && !displayer.artwork.updated && tick - displayer.artwork.tick > pdMS_TO_TICKS(5000)) {
|
||||
ESP_LOGI(TAG, "no artwork received, setting default");
|
||||
displayer_artwork((uint8_t*) default_artwork);
|
||||
}
|
||||
timer_sleep = 1000;
|
||||
} else timer_sleep = max(1000 - elapsed, 0);
|
||||
} else timer_sleep = DEFAULT_SLEEP;
|
||||
@@ -238,6 +262,32 @@ static void displayer_task(void *args) {
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
void displayer_artwork(uint8_t *data) {
|
||||
if (!displayer.artwork.active) return;
|
||||
|
||||
int x = displayer.artwork.offset ? displayer.artwork.offset + ARTWORK_BORDER : 0;
|
||||
int y = x ? 0 : 32;
|
||||
GDS_ClearWindow(display, x, y, -1, -1, GDS_COLOR_BLACK);
|
||||
if (data) {
|
||||
displayer.artwork.updated = true;
|
||||
GDS_DrawJPEG(display, data, x, y, GDS_IMAGE_CENTER | (displayer.artwork.fit ? GDS_IMAGE_FIT : 0));
|
||||
} else {
|
||||
displayer.artwork.updated = false;
|
||||
displayer.artwork.tick = xTaskGetTickCount();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
bool displayer_can_artwork(void) {
|
||||
return displayer.artwork.active;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
@@ -378,6 +428,7 @@ void displayer_control(enum displayer_cmd_e cmd, ...) {
|
||||
switch(cmd) {
|
||||
case DISPLAYER_ACTIVATE: {
|
||||
char *header = va_arg(args, char*);
|
||||
displayer.artwork.active = displayer.artwork.enable && va_arg(args, int);
|
||||
strncpy(displayer.header, header, HEADER_SIZE);
|
||||
displayer.header[HEADER_SIZE] = '\0';
|
||||
displayer.state = DISPLAYER_ACTIVE;
|
||||
@@ -388,16 +439,20 @@ void displayer_control(enum displayer_cmd_e cmd, ...) {
|
||||
displayer.duration.visible = false;
|
||||
displayer.offset = displayer.boundary = 0;
|
||||
display_bus(&displayer, DISPLAY_BUS_TAKE);
|
||||
if (displayer.artwork.active) GDS_SetTextWidth(display, displayer.artwork.offset);
|
||||
vTaskResume(displayer.task);
|
||||
break;
|
||||
}
|
||||
case DISPLAYER_SUSPEND:
|
||||
// task will display the line 2 from beginning and suspend
|
||||
displayer.state = DISPLAYER_IDLE;
|
||||
displayer_artwork(NULL);
|
||||
display_bus(&displayer, DISPLAY_BUS_GIVE);
|
||||
break;
|
||||
case DISPLAYER_SHUTDOWN:
|
||||
// let the task self-suspend (we might be doing i2c_write)
|
||||
GDS_SetTextWidth(display, 0);
|
||||
displayer_artwork(NULL);
|
||||
displayer.state = DISPLAYER_DOWN;
|
||||
display_bus(&displayer, DISPLAY_BUS_GIVE);
|
||||
break;
|
||||
|
||||
@@ -38,5 +38,7 @@ bool display_is_valid_driver(const char * driver);
|
||||
void displayer_scroll(char *string, int speed, int pause);
|
||||
void displayer_control(enum displayer_cmd_e cmd, ...);
|
||||
void displayer_metadata(char *artist, char *album, char *title);
|
||||
void displayer_artwork(uint8_t *data);
|
||||
void displayer_timer(enum displayer_time_e mode, int elapsed, int duration);
|
||||
bool displayer_can_artwork(void);
|
||||
char * display_get_supported_drivers(void);
|
||||
|
||||
BIN
components/display/note.jpg
Normal file
BIN
components/display/note.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -172,7 +172,7 @@ static bool cmd_handler(bt_sink_cmd_t cmd, ...) {
|
||||
// now handle events for display
|
||||
switch(cmd) {
|
||||
case BT_SINK_AUDIO_STARTED:
|
||||
displayer_control(DISPLAYER_ACTIVATE, "BLUETOOTH");
|
||||
displayer_control(DISPLAYER_ACTIVATE, "BLUETOOTH", false);
|
||||
break;
|
||||
case BT_SINK_AUDIO_STOPPED:
|
||||
displayer_control(DISPLAYER_SUSPEND);
|
||||
|
||||
@@ -14,6 +14,11 @@ extern "C" {
|
||||
if ((__p = strcasestr(S, P)) && (__p = strchr(__p, C))) V = atoi(__p+1); \
|
||||
} while (0)
|
||||
|
||||
#define PARSE_PARAM_FLOAT(S,P,C,V) do { \
|
||||
char *__p; \
|
||||
if ((__p = strcasestr(S, P)) && (__p = strchr(__p, C))) V = atof(__p+1); \
|
||||
} while (0)
|
||||
|
||||
#define PARSE_PARAM_STR(S,P,C,V,I) do { \
|
||||
char *__p; \
|
||||
if ((__p = strstr(S, P)) && (__p = strchr(__p, C))) { \
|
||||
|
||||
@@ -11,3 +11,9 @@ idf_component_register( SRCS
|
||||
PRIV_REQUIRES console app_update tools services spi_flash platform_config vfs pthread wifi-manager platform_config newlib telnet display squeezelite tools)
|
||||
target_link_libraries(${COMPONENT_LIB} "-Wl,--undefined=GDS_DrawPixelFast")
|
||||
target_link_libraries(${COMPONENT_LIB} ${build_dir}/esp-idf/$<TARGET_PROPERTY:RECOVERY_PREFIX>/lib$<TARGET_PROPERTY:RECOVERY_PREFIX>.a )
|
||||
|
||||
set_source_files_properties(cmd_config.c
|
||||
PROPERTIES COMPILE_FLAGS
|
||||
-Wno-unused-function
|
||||
)
|
||||
|
||||
|
||||
@@ -283,17 +283,17 @@ static int stdin_dummy(const char * path, int flags, int mode) { return 0; }
|
||||
|
||||
void initialize_console() {
|
||||
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed (unused if we redirect stdin) */
|
||||
esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
|
||||
esp_vfs_dev_uart_port_set_rx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CR);
|
||||
/* Move the caret to the beginning of the next line on '\n' */
|
||||
esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
|
||||
esp_vfs_dev_uart_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF);
|
||||
|
||||
/* Configure UART. Note that REF_TICK is used so that the baud rate remains
|
||||
* correct while APB frequency is changing in light sleep mode.
|
||||
*/
|
||||
const uart_config_t uart_config = { .baud_rate =
|
||||
CONFIG_ESP_CONSOLE_UART_BAUDRATE, .data_bits = UART_DATA_8_BITS,
|
||||
const uart_config_t uart_config = { .baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1,
|
||||
.use_ref_tick = true };
|
||||
};
|
||||
ESP_ERROR_CHECK(uart_param_config(CONFIG_ESP_CONSOLE_UART_NUM, &uart_config));
|
||||
|
||||
/* Install UART driver for interrupt-driven reads and writes */
|
||||
|
||||
@@ -628,6 +628,9 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
|
||||
LOG_INFO("[%p]: received metadata", ctx);
|
||||
settings.ctx = &metadata;
|
||||
memset(&metadata, 0, sizeof(struct metadata_s));
|
||||
if (!dmap_parse(&settings, body, len)) {
|
||||
LOG_INFO("[%p]: received metadata\n\tartist: %s\n\talbum: %s\n\ttitle: %s",
|
||||
ctx, metadata.artist, metadata.album, metadata.title);
|
||||
success = ctx->cmd_cb(RAOP_METADATA, metadata.artist, metadata.album, metadata.title);
|
||||
free_metadata(&metadata);
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ static bool cmd_handler(raop_event_t event, ...) {
|
||||
switch(event) {
|
||||
case RAOP_SETUP:
|
||||
actrls_set(controls, false, NULL, actrls_ir_action);
|
||||
displayer_control(DISPLAYER_ACTIVATE, "AIRPLAY");
|
||||
displayer_control(DISPLAYER_ACTIVATE, "AIRPLAY", true);
|
||||
break;
|
||||
case RAOP_PLAY:
|
||||
displayer_control(DISPLAYER_TIMER_RUN);
|
||||
@@ -127,8 +127,14 @@ static bool cmd_handler(raop_event_t event, ...) {
|
||||
case RAOP_METADATA: {
|
||||
char *artist = va_arg(args, char*), *album = va_arg(args, char*), *title = va_arg(args, char*);
|
||||
displayer_metadata(artist, album, title);
|
||||
displayer_artwork(NULL);
|
||||
break;
|
||||
}
|
||||
case RAOP_ARTWORK: {
|
||||
uint8_t *data = va_arg(args, uint8_t*);
|
||||
displayer_artwork(data);
|
||||
break;
|
||||
}
|
||||
case RAOP_PROGRESS: {
|
||||
int elapsed = va_arg(args, int), duration = va_arg(args, int);
|
||||
displayer_timer(DISPLAYER_ELAPSED, elapsed, duration);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#define RAOP_SAMPLE_RATE 44100
|
||||
|
||||
typedef enum { RAOP_SETUP, RAOP_STREAM, RAOP_PLAY, RAOP_FLUSH, RAOP_METADATA, RAOP_PROGRESS, RAOP_PAUSE, RAOP_STOP,
|
||||
typedef enum { RAOP_SETUP, RAOP_STREAM, RAOP_PLAY, RAOP_FLUSH, RAOP_METADATA, RAOP_ARTWORK, RAOP_PROGRESS, RAOP_PAUSE, RAOP_STOP,
|
||||
RAOP_VOLUME, RAOP_TIMING, RAOP_PREV, RAOP_NEXT, RAOP_REW, RAOP_FWD,
|
||||
RAOP_VOLUME_UP, RAOP_VOLUME_DOWN, RAOP_RESUME, RAOP_TOGGLE } raop_event_t ;
|
||||
|
||||
|
||||
@@ -1109,7 +1109,6 @@ cJSON * get_gpio_list(bool refresh) {
|
||||
}
|
||||
gpio_list= cJSON_CreateArray();
|
||||
|
||||
#ifndef CONFIG_BAT_LOCKED
|
||||
char *bat_config = config_alloc_get_default(NVS_TYPE_STR, "bat_config", NULL, 0);
|
||||
if (bat_config) {
|
||||
int channel = -1;
|
||||
@@ -1121,11 +1120,6 @@ cJSON * get_gpio_list(bool refresh) {
|
||||
}
|
||||
free(bat_config);
|
||||
}
|
||||
#else
|
||||
if(adc1_pad_get_io_num(CONFIG_BAT_CHANNEL,&gpio_num )==ESP_OK){
|
||||
cJSON_AddItemToArray(gpio_list,get_gpio_entry("bat","other",gpio_num,true));
|
||||
}
|
||||
#endif
|
||||
gpio_list=get_GPIO_nvs_list(gpio_list);
|
||||
gpio_list=get_SPDIF_GPIO(gpio_list,is_spdif_config_locked());
|
||||
gpio_list=get_Rotary_GPIO(gpio_list);
|
||||
|
||||
@@ -35,12 +35,13 @@ static struct {
|
||||
int count;
|
||||
int cells, attenuation;
|
||||
TimerHandle_t timer;
|
||||
} battery = {
|
||||
.channel = CONFIG_BAT_CHANNEL,
|
||||
} battery = {
|
||||
.channel = -1,
|
||||
.cells = 2,
|
||||
.attenuation = ADC_ATTEN_DB_0,
|
||||
};
|
||||
|
||||
void (*battery_handler_svc)(float value);
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
@@ -65,6 +66,7 @@ static void battery_callback(TimerHandle_t xTimer) {
|
||||
if (++battery.count == 30) {
|
||||
battery.avg = battery.sum / battery.count;
|
||||
battery.sum = battery.count = 0;
|
||||
if (battery_handler_svc) (battery_handler_svc)(battery.avg);
|
||||
ESP_LOGI(TAG, "Voltage %.2fV", battery.avg);
|
||||
}
|
||||
}
|
||||
@@ -73,17 +75,18 @@ static void battery_callback(TimerHandle_t xTimer) {
|
||||
*
|
||||
*/
|
||||
void battery_svc_init(void) {
|
||||
#ifdef CONFIG_BAT_SCALE
|
||||
battery.scale = atof(CONFIG_BAT_SCALE);
|
||||
#endif
|
||||
|
||||
char *nvs_item = config_alloc_get_default(NVS_TYPE_STR, "bat_config", "n", 0);
|
||||
if (nvs_item) {
|
||||
#ifndef CONFIG_BAT_LOCKED
|
||||
PARSE_PARAM(nvs_item, "channel", '=', battery.channel);
|
||||
PARSE_PARAM(nvs_item, "scale", '=', battery.scale);
|
||||
PARSE_PARAM(nvs_item, "atten", '=', battery.attenuation);
|
||||
char *nvs_item = config_alloc_get_default(NVS_TYPE_STR, "bat_config", "", 0);
|
||||
|
||||
#ifdef CONFIG_BAT_LOCKED
|
||||
char *p = nvs_item;
|
||||
asprintf(&nvs_item, CONFIG_BAT_CONFIG ",%s", p);
|
||||
free(p);
|
||||
#endif
|
||||
|
||||
if (nvs_item) {
|
||||
PARSE_PARAM(nvs_item, "channel", '=', battery.channel);
|
||||
PARSE_PARAM_FLOAT(nvs_item, "scale", '=', battery.scale);
|
||||
PARSE_PARAM(nvs_item, "atten", '=', battery.attenuation);
|
||||
PARSE_PARAM(nvs_item, "cells", '=', battery.cells);
|
||||
free(nvs_item);
|
||||
}
|
||||
@@ -96,7 +99,7 @@ void battery_svc_init(void) {
|
||||
battery.timer = xTimerCreate("battery", BATTERY_TIMER / portTICK_RATE_MS, pdTRUE, NULL, battery_callback);
|
||||
xTimerStart(battery.timer, portMAX_DELAY);
|
||||
|
||||
ESP_LOGI(TAG, "Battery measure channel: %u, scale %f, cells %u, avg %.2fV", battery.channel, battery.scale, battery.cells, battery.avg);
|
||||
ESP_LOGI(TAG, "Battery measure channel: %u, scale %f, atten %d, cells %u, avg %.2fV", battery.channel, battery.scale, battery.attenuation, battery.cells, battery.avg);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "No battery");
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ extern bool jack_inserted_svc(void);
|
||||
extern void (*spkfault_handler_svc)(bool inserted);
|
||||
extern bool spkfault_svc(void);
|
||||
|
||||
extern void (*battery_handler_svc)(float value);
|
||||
extern float battery_value_svc(void);
|
||||
extern uint16_t battery_level_svc(void);
|
||||
|
||||
|
||||
@@ -119,8 +119,8 @@ static void cspotTask(void *pvParameters) {
|
||||
switch (event.eventType) {
|
||||
case CSpotEventType::TRACK_INFO: {
|
||||
TrackInfo track = std::get<TrackInfo>(event.data);
|
||||
// duration is in chunks of 0.5 ms
|
||||
cspot.cHandler(CSPOT_TRACK, 44100, track.duration / 2, track.artist.c_str(), track.album.c_str(), track.name.c_str());
|
||||
cspot.cHandler(CSPOT_TRACK, 44100, track.duration, track.artist.c_str(),
|
||||
track.album.c_str(), track.name.c_str(), track.imageUrl.c_str());
|
||||
break;
|
||||
}
|
||||
case CSpotEventType::PLAY_PAUSE: {
|
||||
@@ -192,7 +192,7 @@ struct cspot_s* cspot_create(const char *name, cspot_cmd_cb_t cmd_cb, cspot_data
|
||||
cspot.cHandler = cmd_cb;
|
||||
cspot.dHandler = data_cb;
|
||||
strncpy(cspot.name, name, sizeof(cspot.name) - 1);
|
||||
cspot.TaskHandle = xTaskCreateStatic(&cspotTask, "cspot", CSPOT_STACK_SIZE, NULL, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT, xStack, &xTaskBuffer);
|
||||
cspot.TaskHandle = xTaskCreateStatic(&cspotTask, "cspot", CSPOT_STACK_SIZE, NULL, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT - 2, xStack, &xTaskBuffer);
|
||||
|
||||
return &cspot;
|
||||
}
|
||||
|
||||
@@ -45,5 +45,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_compile_definitions(bell PUBLIC PB_FIELD_32BIT)
|
||||
target_link_libraries(cspot PUBLIC ${EXTRA_LIBS})
|
||||
target_include_directories(cspot PUBLIC "include" ${GENERATED_INCLUDES} ${NANOPB_INCLUDE_DIRS})
|
||||
|
||||
@@ -7,8 +7,6 @@ option(BELL_DISABLE_CODECS "Disable libhelix AAC and MP3 codecs" OFF)
|
||||
#set(BELL_EXTERNAL_CJSON "" CACHE STRING "External cJSON library target name, optional")
|
||||
#set(BELL_EXTERNAL_TREMOR "" CACHE STRING "External tremor library target name, optional")
|
||||
|
||||
add_definitions(-DPB_ENABLE_MALLOC)
|
||||
|
||||
# Include nanoPB library
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/nanopb/extra)
|
||||
find_package(Nanopb REQUIRED)
|
||||
@@ -98,3 +96,4 @@ message(${NANOPB_INCLUDE_DIRS})
|
||||
# PUBLIC to propagate esp-idf includes to bell dependents
|
||||
target_link_libraries(bell PUBLIC ${EXTRA_LIBS})
|
||||
target_include_directories(bell PUBLIC "include" ${EXTRA_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
target_compile_definitions(bell PUBLIC PB_ENABLE_MALLOC)
|
||||
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
std::vector<uint8_t> sha1HMAC(const std::vector<uint8_t>& inputKey, const std::vector<uint8_t>& message);
|
||||
|
||||
// AES CTR
|
||||
void aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& iv, std::vector<uint8_t>& data);
|
||||
void aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& iv, uint8_t* data, size_t nbytes);
|
||||
|
||||
// AES ECB
|
||||
void aesECBdecrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& data);
|
||||
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
std::vector<uint8_t> sha1HMAC(const std::vector<uint8_t>& inputKey, const std::vector<uint8_t>& message);
|
||||
|
||||
// AES CTR
|
||||
void aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& iv, std::vector<uint8_t>& data);
|
||||
void aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& iv, uint8_t* buffer, size_t nbytes);
|
||||
|
||||
// AES ECB
|
||||
void aesECBdecrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& data);
|
||||
|
||||
@@ -42,6 +42,10 @@ void pbDecode(T &result, const pb_msgdesc_t *fields, std::vector<uint8_t> &data)
|
||||
}
|
||||
}
|
||||
|
||||
void pbPutString(const std::string &stringToPack, char* dst);
|
||||
void pbPutCharArray(const char * stringToPack, char* dst);
|
||||
void pbPutBytes(const std::vector<uint8_t> &data, pb_bytes_array_t &dst);
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace bell
|
||||
#ifdef ESP_PLATFORM
|
||||
this->xStack = NULL;
|
||||
this->priority = CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + priority;
|
||||
if (this->priority < 0) this->priority = ESP_TASK_PRIO_MIN;
|
||||
if (this->priority <= ESP_TASK_PRIO_MIN) this->priority = ESP_TASK_PRIO_MIN + 1;
|
||||
if (runOnPSRAM) {
|
||||
this->xStack = (StackType_t*) heap_caps_malloc(this->stackSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ std::vector<uint8_t> CryptoMbedTLS::sha1HMAC(const std::vector<uint8_t> &inputKe
|
||||
}
|
||||
|
||||
// AES CTR
|
||||
void CryptoMbedTLS::aesCTRXcrypt(const std::vector<uint8_t> &key, std::vector<uint8_t> &iv, std::vector<uint8_t> &data)
|
||||
void CryptoMbedTLS::aesCTRXcrypt(const std::vector<uint8_t> &key, std::vector<uint8_t> &iv, uint8_t* buffer, size_t nbytes)
|
||||
{
|
||||
// needed for internal cache
|
||||
size_t off = 0;
|
||||
@@ -99,12 +99,12 @@ void CryptoMbedTLS::aesCTRXcrypt(const std::vector<uint8_t> &key, std::vector<ui
|
||||
|
||||
// Perform decrypt
|
||||
mbedtls_aes_crypt_ctr(&aesCtx,
|
||||
data.size(),
|
||||
nbytes,
|
||||
&off,
|
||||
iv.data(),
|
||||
streamBlock,
|
||||
data.data(),
|
||||
data.data());
|
||||
buffer,
|
||||
buffer);
|
||||
}
|
||||
|
||||
void CryptoMbedTLS::aesECBdecrypt(const std::vector<uint8_t> &key, std::vector<uint8_t> &data)
|
||||
@@ -115,7 +115,7 @@ void CryptoMbedTLS::aesECBdecrypt(const std::vector<uint8_t> &key, std::vector<u
|
||||
// Mbedtls's decrypt only works on 16 byte blocks
|
||||
for (unsigned int x = 0; x < data.size() / 16; x++)
|
||||
{
|
||||
// Perform decrypt
|
||||
// Perform finalize
|
||||
mbedtls_aes_crypt_ecb(&aesCtx,
|
||||
MBEDTLS_AES_DECRYPT,
|
||||
data.data() + (x * 16),
|
||||
|
||||
@@ -99,7 +99,7 @@ std::vector<uint8_t> CryptoOpenSSL::sha1HMAC(const std::vector<uint8_t>& inputKe
|
||||
}
|
||||
|
||||
// AES CTR
|
||||
void CryptoOpenSSL::aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& iv, std::vector<uint8_t> &data)
|
||||
void CryptoOpenSSL::aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& iv, uint8_t* buffer, size_t nbytes)
|
||||
{
|
||||
// Prepare AES_KEY
|
||||
auto cryptoKey = AES_KEY();
|
||||
@@ -110,9 +110,9 @@ void CryptoOpenSSL::aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<ui
|
||||
unsigned int offsetInBlock = 0;
|
||||
|
||||
CRYPTO_ctr128_encrypt(
|
||||
data.data(),
|
||||
data.data(),
|
||||
data.size(),
|
||||
buffer,
|
||||
buffer,
|
||||
nbytes,
|
||||
&cryptoKey,
|
||||
iv.data(),
|
||||
ecountBuf,
|
||||
|
||||
@@ -287,7 +287,7 @@ std::string HTTPClient::HTTPResponse::readToString() {
|
||||
return result;
|
||||
}
|
||||
std::string result;
|
||||
char buffer[BUF_SIZE];
|
||||
char buffer[BUF_SIZE+1]; // make space for null-terminator
|
||||
size_t len;
|
||||
do {
|
||||
len = this->read(buffer, BUF_SIZE);
|
||||
|
||||
@@ -47,6 +47,22 @@ pb_bytes_array_t* vectorToPbArray(const std::vector<uint8_t>& vectorToPack)
|
||||
return result;
|
||||
}
|
||||
|
||||
void pbPutString(const std::string &stringToPack, char* dst) {
|
||||
stringToPack.copy(dst, stringToPack.size());
|
||||
dst[stringToPack.size()] = '\0';
|
||||
}
|
||||
|
||||
void pbPutCharArray(const char * stringToPack, char* dst) {
|
||||
// copy stringToPack into dst
|
||||
strcpy(dst, stringToPack);
|
||||
//dst[sizeof(stringToPack)-1] = '\0';
|
||||
}
|
||||
|
||||
void pbPutBytes(const std::vector<uint8_t> &data, pb_bytes_array_t &dst) {
|
||||
dst.size = data.size();
|
||||
std::copy(data.begin(), data.end(), dst.bytes);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> pbArrayToVector(pb_bytes_array_t* pbArray) {
|
||||
return std::vector<uint8_t>(pbArray->bytes, pbArray->bytes + pbArray->size);
|
||||
}
|
||||
|
||||
@@ -14,12 +14,15 @@ class AudioChunk {
|
||||
private:
|
||||
/**
|
||||
* @brief Calculates a correct IV by performing bignum addition.
|
||||
*
|
||||
*
|
||||
* @param num Number to add to IV.
|
||||
* @return std::vector<uint8_t>
|
||||
* @return std::vector<uint8_t>
|
||||
*/
|
||||
std::vector<uint8_t> getIVSum(uint32_t num);
|
||||
|
||||
size_t decryptedCount = 0;
|
||||
size_t oldStartPos;
|
||||
|
||||
public:
|
||||
std::unique_ptr<Crypto> crypto;
|
||||
std::vector<uint8_t> decryptedData;
|
||||
@@ -41,14 +44,21 @@ public:
|
||||
std::unique_ptr<WrappedSemaphore> isLoadedSemaphore;
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @brief
|
||||
*/
|
||||
std::unique_ptr<WrappedSemaphore> isHeaderFileSizeLoadedSemaphore;
|
||||
|
||||
/**
|
||||
* Decrypts data and writes it to the target buffer
|
||||
* @param target data buffer to write to
|
||||
* @param offset data offset
|
||||
* @param nbytes number of bytes to read
|
||||
*/
|
||||
void readData(uint8_t *target, size_t offset, size_t nbytes);
|
||||
|
||||
/**
|
||||
* @brief AudioChunk handles all audiochunk related operations.
|
||||
*
|
||||
*
|
||||
* @param seqId Sequence id of requested chunk
|
||||
* @param audioKey Audio key used for decryption of audio data
|
||||
* @param startPosition Start position of current chunk in audio file
|
||||
@@ -59,16 +69,16 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Appends incoming chunked data to local cache.
|
||||
*
|
||||
*
|
||||
* @param data encrypted binary audio data.
|
||||
*/
|
||||
void appendData(const std::vector<uint8_t> &data);
|
||||
|
||||
/**
|
||||
* @brief Performs AES CTR decryption of received data.
|
||||
*
|
||||
* @brief Sets loaded status on the chunk
|
||||
*
|
||||
*/
|
||||
void decrypt();
|
||||
void finalize();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,7 +9,7 @@ class Packet
|
||||
private:
|
||||
|
||||
public:
|
||||
Packet(uint8_t command, std::vector<uint8_t> &data);
|
||||
Packet(uint8_t command, const std::vector<uint8_t> &data);
|
||||
uint8_t command;
|
||||
std::vector<uint8_t> data;
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ public:
|
||||
void stream(std::vector<uint8_t> &buf); /* stream cipher */
|
||||
void maconly(std::vector<uint8_t> &buf); /* accumulate MAC */
|
||||
void encrypt(std::vector<uint8_t> &buf); /* encrypt + MAC */
|
||||
void decrypt(std::vector<uint8_t> &buf); /* decrypt + MAC */
|
||||
void decrypt(std::vector<uint8_t> &buf); /* finalize + MAC */
|
||||
void finish(std::vector<uint8_t> &buf); /* finalise MAC */
|
||||
|
||||
private:
|
||||
|
||||
@@ -37,6 +37,7 @@ class SpircController {
|
||||
private:
|
||||
std::shared_ptr<MercuryManager> manager;
|
||||
std::string username;
|
||||
bool firstFrame = true;
|
||||
std::unique_ptr<Player> player;
|
||||
std::unique_ptr<PlayerState> state;
|
||||
std::shared_ptr<AudioSink> audioSink;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
LoginCredentials.username type:FT_POINTER
|
||||
LoginCredentials.auth_data type:FT_POINTER
|
||||
LoginCredentials.auth_data type:FT_POINTER
|
||||
SystemInfo.system_information_string type:FT_POINTER
|
||||
SystemInfo.device_id type:FT_POINTER
|
||||
ClientResponseEncrypted.version_string type:FT_POINTER
|
||||
LoginCredentials.username max_size:30, fixed_length:false
|
||||
LoginCredentials.auth_data max_size:512, fixed_length:false
|
||||
SystemInfo.system_information_string max_size:16, fixed_length:false
|
||||
SystemInfo.device_id max_size:50, fixed_length:false
|
||||
ClientResponseEncrypted.version_string max_size:32, fixed_length:false
|
||||
@@ -9,7 +9,7 @@
|
||||
PB_BIND(SystemInfo, SystemInfo, 2)
|
||||
|
||||
|
||||
PB_BIND(LoginCredentials, LoginCredentials, AUTO)
|
||||
PB_BIND(LoginCredentials, LoginCredentials, 2)
|
||||
|
||||
|
||||
PB_BIND(ClientResponseEncrypted, ClientResponseEncrypted, 2)
|
||||
|
||||
@@ -58,23 +58,25 @@ typedef enum _AuthenticationType {
|
||||
} AuthenticationType;
|
||||
|
||||
/* Struct definitions */
|
||||
typedef PB_BYTES_ARRAY_T(512) LoginCredentials_auth_data_t;
|
||||
typedef struct _LoginCredentials {
|
||||
char *username;
|
||||
char username[30];
|
||||
AuthenticationType typ;
|
||||
pb_bytes_array_t *auth_data;
|
||||
LoginCredentials_auth_data_t auth_data;
|
||||
} LoginCredentials;
|
||||
|
||||
typedef struct _SystemInfo {
|
||||
CpuFamily cpu_family;
|
||||
Os os;
|
||||
char *system_information_string;
|
||||
char *device_id;
|
||||
char system_information_string[16];
|
||||
char device_id[50];
|
||||
} SystemInfo;
|
||||
|
||||
typedef struct _ClientResponseEncrypted {
|
||||
LoginCredentials login_credentials;
|
||||
SystemInfo system_info;
|
||||
char *version_string;
|
||||
bool has_version_string;
|
||||
char version_string[32];
|
||||
} ClientResponseEncrypted;
|
||||
|
||||
|
||||
@@ -97,12 +99,12 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define SystemInfo_init_default {_CpuFamily_MIN, _Os_MIN, NULL, NULL}
|
||||
#define LoginCredentials_init_default {NULL, _AuthenticationType_MIN, NULL}
|
||||
#define ClientResponseEncrypted_init_default {LoginCredentials_init_default, SystemInfo_init_default, NULL}
|
||||
#define SystemInfo_init_zero {_CpuFamily_MIN, _Os_MIN, NULL, NULL}
|
||||
#define LoginCredentials_init_zero {NULL, _AuthenticationType_MIN, NULL}
|
||||
#define ClientResponseEncrypted_init_zero {LoginCredentials_init_zero, SystemInfo_init_zero, NULL}
|
||||
#define SystemInfo_init_default {_CpuFamily_MIN, _Os_MIN, "", ""}
|
||||
#define LoginCredentials_init_default {"", _AuthenticationType_MIN, {0, {0}}}
|
||||
#define ClientResponseEncrypted_init_default {LoginCredentials_init_default, SystemInfo_init_default, false, ""}
|
||||
#define SystemInfo_init_zero {_CpuFamily_MIN, _Os_MIN, "", ""}
|
||||
#define LoginCredentials_init_zero {"", _AuthenticationType_MIN, {0, {0}}}
|
||||
#define ClientResponseEncrypted_init_zero {LoginCredentials_init_zero, SystemInfo_init_zero, false, ""}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define LoginCredentials_username_tag 10
|
||||
@@ -120,22 +122,22 @@ extern "C" {
|
||||
#define SystemInfo_FIELDLIST(X, a) \
|
||||
X(a, STATIC, REQUIRED, UENUM, cpu_family, 10) \
|
||||
X(a, STATIC, REQUIRED, UENUM, os, 60) \
|
||||
X(a, POINTER, OPTIONAL, STRING, system_information_string, 90) \
|
||||
X(a, POINTER, OPTIONAL, STRING, device_id, 100)
|
||||
X(a, STATIC, REQUIRED, STRING, system_information_string, 90) \
|
||||
X(a, STATIC, REQUIRED, STRING, device_id, 100)
|
||||
#define SystemInfo_CALLBACK NULL
|
||||
#define SystemInfo_DEFAULT NULL
|
||||
|
||||
#define LoginCredentials_FIELDLIST(X, a) \
|
||||
X(a, POINTER, OPTIONAL, STRING, username, 10) \
|
||||
X(a, STATIC, REQUIRED, STRING, username, 10) \
|
||||
X(a, STATIC, REQUIRED, UENUM, typ, 20) \
|
||||
X(a, POINTER, OPTIONAL, BYTES, auth_data, 30)
|
||||
X(a, STATIC, REQUIRED, BYTES, auth_data, 30)
|
||||
#define LoginCredentials_CALLBACK NULL
|
||||
#define LoginCredentials_DEFAULT NULL
|
||||
|
||||
#define ClientResponseEncrypted_FIELDLIST(X, a) \
|
||||
X(a, STATIC, REQUIRED, MESSAGE, login_credentials, 10) \
|
||||
X(a, STATIC, REQUIRED, MESSAGE, system_info, 50) \
|
||||
X(a, POINTER, OPTIONAL, STRING, version_string, 70)
|
||||
X(a, STATIC, OPTIONAL, STRING, version_string, 70)
|
||||
#define ClientResponseEncrypted_CALLBACK NULL
|
||||
#define ClientResponseEncrypted_DEFAULT NULL
|
||||
#define ClientResponseEncrypted_login_credentials_MSGTYPE LoginCredentials
|
||||
@@ -151,9 +153,9 @@ extern const pb_msgdesc_t ClientResponseEncrypted_msg;
|
||||
#define ClientResponseEncrypted_fields &ClientResponseEncrypted_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
/* SystemInfo_size depends on runtime parameters */
|
||||
/* LoginCredentials_size depends on runtime parameters */
|
||||
/* ClientResponseEncrypted_size depends on runtime parameters */
|
||||
#define ClientResponseEncrypted_size 665
|
||||
#define LoginCredentials_size 550
|
||||
#define SystemInfo_size 75
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -48,14 +48,14 @@ enum AuthenticationType {
|
||||
message SystemInfo {
|
||||
required CpuFamily cpu_family = 0xa;
|
||||
required Os os = 0x3c;
|
||||
optional string system_information_string = 0x5a;
|
||||
optional string device_id = 0x64;
|
||||
required string system_information_string = 0x5a;
|
||||
required string device_id = 0x64;
|
||||
}
|
||||
|
||||
message LoginCredentials {
|
||||
optional string username = 0xa;
|
||||
required string username = 0xa;
|
||||
required AuthenticationType typ = 0x14;
|
||||
optional bytes auth_data = 0x1e;
|
||||
required bytes auth_data = 0x1e;
|
||||
}
|
||||
|
||||
message ClientResponseEncrypted {
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
Header.uri type:FT_POINTER
|
||||
Header.method type:FT_POINTER
|
||||
Header.uri max_size:64, fixed_length:false
|
||||
Header.method max_size:32, fixed_length:false
|
||||
|
||||
@@ -11,8 +11,10 @@
|
||||
|
||||
/* Struct definitions */
|
||||
typedef struct _Header {
|
||||
char *uri;
|
||||
char *method;
|
||||
bool has_uri;
|
||||
char uri[64];
|
||||
bool has_method;
|
||||
char method[32];
|
||||
} Header;
|
||||
|
||||
|
||||
@@ -21,8 +23,8 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define Header_init_default {NULL, NULL}
|
||||
#define Header_init_zero {NULL, NULL}
|
||||
#define Header_init_default {false, "", false, ""}
|
||||
#define Header_init_zero {false, "", false, ""}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define Header_uri_tag 1
|
||||
@@ -30,8 +32,8 @@ extern "C" {
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define Header_FIELDLIST(X, a) \
|
||||
X(a, POINTER, OPTIONAL, STRING, uri, 1) \
|
||||
X(a, POINTER, OPTIONAL, STRING, method, 3)
|
||||
X(a, STATIC, OPTIONAL, STRING, uri, 1) \
|
||||
X(a, STATIC, OPTIONAL, STRING, method, 3)
|
||||
#define Header_CALLBACK NULL
|
||||
#define Header_DEFAULT NULL
|
||||
|
||||
@@ -41,7 +43,7 @@ extern const pb_msgdesc_t Header_msg;
|
||||
#define Header_fields &Header_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
/* Header_size depends on runtime parameters */
|
||||
#define Header_size 98
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
Track.name type:FT_POINTER
|
||||
Track.gid type:FT_POINTER
|
||||
Track.restriction type:FT_POINTER
|
||||
Track.alternative type:FT_POINTER
|
||||
Track.file type:FT_POINTER
|
||||
Track.artist type:FT_POINTER
|
||||
AudioFile.file_id type:FT_POINTER
|
||||
Image.file_id type:FT_POINTER
|
||||
Artist.gid type:FT_POINTER
|
||||
Artist.name type:FT_POINTER
|
||||
Album.name type:FT_POINTER
|
||||
Episode.gid type:FT_POINTER
|
||||
Episode.name type:FT_POINTER
|
||||
ImageGroup.image type:FT_POINTER
|
||||
Episode.audio type:FT_POINTER
|
||||
Episode.covers type:FT_POINTER
|
||||
Restriction.countries_allowed type:FT_POINTER
|
||||
Restriction.countries_forbidden type:FT_POINTER
|
||||
Track.name type: FT_POINTER
|
||||
Track.gid type: FT_POINTER
|
||||
Track.file type: FT_POINTER
|
||||
Track.restriction type: FT_POINTER
|
||||
Track.alternative type: FT_POINTER
|
||||
Track.artist type: FT_POINTER
|
||||
AudioFile.file_id type: FT_POINTER
|
||||
Image.file_id type: FT_POINTER
|
||||
Artist.gid type: FT_POINTER
|
||||
Artist.name type: FT_POINTER
|
||||
Album.name type: FT_POINTER
|
||||
Episode.gid type: FT_POINTER
|
||||
Episode.name type: FT_POINTER
|
||||
ImageGroup.image type: FT_POINTER
|
||||
Episode.audio type: FT_POINTER
|
||||
Episode.covers type: FT_POINTER
|
||||
Restriction.countries_allowed type: FT_POINTER
|
||||
Restriction.countries_forbidden type: FT_POINTER
|
||||
@@ -7,7 +7,7 @@ DeviceState.sw_version type:FT_POINTER
|
||||
DeviceState.name type:FT_POINTER
|
||||
DeviceState.capabilities max_count:17, fixed_count:false
|
||||
State.context_uri type:FT_POINTER
|
||||
State.track max_count:100, fixed_count:false
|
||||
State.track type:FT_POINTER
|
||||
TrackRef.gid type:FT_POINTER
|
||||
TrackRef.uri type:FT_POINTER
|
||||
TrackRef.context type:FT_POINTER
|
||||
@@ -9,7 +9,7 @@
|
||||
PB_BIND(TrackRef, TrackRef, AUTO)
|
||||
|
||||
|
||||
PB_BIND(State, State, 4)
|
||||
PB_BIND(State, State, AUTO)
|
||||
|
||||
|
||||
PB_BIND(Capability, Capability, 2)
|
||||
|
||||
@@ -62,6 +62,27 @@ typedef struct _Capability {
|
||||
char stringValue[50][50];
|
||||
} Capability;
|
||||
|
||||
typedef struct _State {
|
||||
char *context_uri;
|
||||
bool has_index;
|
||||
uint32_t index;
|
||||
bool has_position_ms;
|
||||
uint32_t position_ms;
|
||||
bool has_status;
|
||||
PlayStatus status;
|
||||
bool has_position_measured_at;
|
||||
uint64_t position_measured_at;
|
||||
pb_callback_t context_description;
|
||||
bool has_shuffle;
|
||||
bool shuffle;
|
||||
bool has_repeat;
|
||||
bool repeat;
|
||||
bool has_playing_track_index;
|
||||
uint32_t playing_track_index;
|
||||
pb_size_t track_count;
|
||||
struct _TrackRef *track;
|
||||
} State;
|
||||
|
||||
typedef struct _TrackRef {
|
||||
pb_bytes_array_t *gid;
|
||||
char *uri;
|
||||
@@ -89,27 +110,6 @@ typedef struct _DeviceState {
|
||||
pb_callback_t local_uris;
|
||||
} DeviceState;
|
||||
|
||||
typedef struct _State {
|
||||
char *context_uri;
|
||||
bool has_index;
|
||||
uint32_t index;
|
||||
bool has_position_ms;
|
||||
uint32_t position_ms;
|
||||
bool has_status;
|
||||
PlayStatus status;
|
||||
bool has_position_measured_at;
|
||||
uint64_t position_measured_at;
|
||||
pb_callback_t context_description;
|
||||
bool has_shuffle;
|
||||
bool shuffle;
|
||||
bool has_repeat;
|
||||
bool repeat;
|
||||
bool has_playing_track_index;
|
||||
uint32_t playing_track_index;
|
||||
pb_size_t track_count;
|
||||
TrackRef track[100];
|
||||
} State;
|
||||
|
||||
typedef struct _Frame {
|
||||
bool has_version;
|
||||
uint32_t version;
|
||||
@@ -154,12 +154,12 @@ extern "C" {
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define TrackRef_init_default {NULL, NULL, false, 0, NULL}
|
||||
#define State_init_default {NULL, false, 0, false, 0, false, _PlayStatus_MIN, false, 0, {{NULL}, NULL}, false, 0, false, 0, false, 0, 0, {TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default, TrackRef_init_default}}
|
||||
#define State_init_default {NULL, false, 0, false, 0, false, _PlayStatus_MIN, false, 0, {{NULL}, NULL}, false, 0, false, 0, false, 0, 0, NULL}
|
||||
#define Capability_init_default {false, _CapabilityType_MIN, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}}
|
||||
#define DeviceState_init_default {NULL, false, 0, false, 0, false, 0, NULL, false, 0, false, 0, {{NULL}, NULL}, 0, {Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default, Capability_init_default}, {{NULL}, NULL}}
|
||||
#define Frame_init_default {false, 0, NULL, NULL, false, 0, false, _MessageType_MIN, false, DeviceState_init_default, false, State_init_default, false, 0, false, 0, false, 0, 0, NULL}
|
||||
#define TrackRef_init_zero {NULL, NULL, false, 0, NULL}
|
||||
#define State_init_zero {NULL, false, 0, false, 0, false, _PlayStatus_MIN, false, 0, {{NULL}, NULL}, false, 0, false, 0, false, 0, 0, {TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero, TrackRef_init_zero}}
|
||||
#define State_init_zero {NULL, false, 0, false, 0, false, _PlayStatus_MIN, false, 0, {{NULL}, NULL}, false, 0, false, 0, false, 0, 0, NULL}
|
||||
#define Capability_init_zero {false, _CapabilityType_MIN, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}}
|
||||
#define DeviceState_init_zero {NULL, false, 0, false, 0, false, 0, NULL, false, 0, false, 0, {{NULL}, NULL}, 0, {Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero, Capability_init_zero}, {{NULL}, NULL}}
|
||||
#define Frame_init_zero {false, 0, NULL, NULL, false, 0, false, _MessageType_MIN, false, DeviceState_init_zero, false, State_init_zero, false, 0, false, 0, false, 0, 0, NULL}
|
||||
@@ -168,6 +168,16 @@ extern "C" {
|
||||
#define Capability_typ_tag 1
|
||||
#define Capability_intValue_tag 2
|
||||
#define Capability_stringValue_tag 3
|
||||
#define State_context_uri_tag 2
|
||||
#define State_index_tag 3
|
||||
#define State_position_ms_tag 4
|
||||
#define State_status_tag 5
|
||||
#define State_position_measured_at_tag 7
|
||||
#define State_context_description_tag 8
|
||||
#define State_shuffle_tag 13
|
||||
#define State_repeat_tag 14
|
||||
#define State_playing_track_index_tag 26
|
||||
#define State_track_tag 27
|
||||
#define TrackRef_gid_tag 1
|
||||
#define TrackRef_uri_tag 2
|
||||
#define TrackRef_queued_tag 3
|
||||
@@ -182,16 +192,6 @@ extern "C" {
|
||||
#define DeviceState_error_message_tag 16
|
||||
#define DeviceState_capabilities_tag 17
|
||||
#define DeviceState_local_uris_tag 18
|
||||
#define State_context_uri_tag 2
|
||||
#define State_index_tag 3
|
||||
#define State_position_ms_tag 4
|
||||
#define State_status_tag 5
|
||||
#define State_position_measured_at_tag 7
|
||||
#define State_context_description_tag 8
|
||||
#define State_shuffle_tag 13
|
||||
#define State_repeat_tag 14
|
||||
#define State_playing_track_index_tag 26
|
||||
#define State_track_tag 27
|
||||
#define Frame_version_tag 1
|
||||
#define Frame_ident_tag 2
|
||||
#define Frame_protocol_version_tag 3
|
||||
@@ -223,7 +223,7 @@ X(a, CALLBACK, OPTIONAL, STRING, context_description, 8) \
|
||||
X(a, STATIC, OPTIONAL, BOOL, shuffle, 13) \
|
||||
X(a, STATIC, OPTIONAL, BOOL, repeat, 14) \
|
||||
X(a, STATIC, OPTIONAL, UINT32, playing_track_index, 26) \
|
||||
X(a, STATIC, REPEATED, MESSAGE, track, 27)
|
||||
X(a, POINTER, REPEATED, MESSAGE, track, 27)
|
||||
#define State_CALLBACK pb_default_field_callback
|
||||
#define State_DEFAULT NULL
|
||||
#define State_track_MSGTYPE TrackRef
|
||||
|
||||
@@ -24,13 +24,28 @@ void AudioChunk::appendData(const std::vector<uint8_t> &data)
|
||||
this->decryptedData.insert(this->decryptedData.end(), data.begin(), data.end());
|
||||
}
|
||||
|
||||
void AudioChunk::decrypt()
|
||||
void AudioChunk::readData(uint8_t *target, size_t offset, size_t nbytes) {
|
||||
auto readPos = offset + nbytes;
|
||||
auto modulo = (readPos % 16);
|
||||
auto ivReadPos = readPos;
|
||||
if (modulo != 0) {
|
||||
ivReadPos += (16 - modulo);
|
||||
}
|
||||
if (ivReadPos > decryptedCount) {
|
||||
// calculate the IV for right position
|
||||
auto calculatedIV = this->getIVSum((oldStartPos + decryptedCount) / 16);
|
||||
|
||||
crypto->aesCTRXcrypt(this->audioKey, calculatedIV, decryptedData.data() + decryptedCount, ivReadPos - decryptedCount);
|
||||
|
||||
decryptedCount = ivReadPos;
|
||||
}
|
||||
memcpy(target, this->decryptedData.data() + offset, nbytes);
|
||||
|
||||
}
|
||||
|
||||
void AudioChunk::finalize()
|
||||
{
|
||||
// calculate the IV for right position
|
||||
auto calculatedIV = this->getIVSum(startPosition / 16);
|
||||
|
||||
crypto->aesCTRXcrypt(this->audioKey, calculatedIV, decryptedData);
|
||||
|
||||
this->oldStartPos = this->startPosition;
|
||||
this->startPosition = this->endPosition - this->decryptedData.size();
|
||||
this->isLoaded = true;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "Logger.h"
|
||||
|
||||
AudioChunkManager::AudioChunkManager()
|
||||
: bell::Task("AudioChunkManager", 4 * 1024, 2, 0) {
|
||||
: bell::Task("AudioChunkManager", 4 * 1024, -1, 0) {
|
||||
this->chunks = std::vector<std::shared_ptr<AudioChunk>>();
|
||||
startTask();
|
||||
}
|
||||
@@ -80,7 +80,7 @@ void AudioChunkManager::runTask() {
|
||||
|
||||
switch (data.size()) {
|
||||
case DATA_SIZE_HEADER: {
|
||||
CSPOT_LOG(debug, "ID: %d: header decrypt!", seqId);
|
||||
CSPOT_LOG(debug, "ID: %d: header finalize!", seqId);
|
||||
auto headerSize = ntohs(extract<uint16_t>(data, 2));
|
||||
// Got file size!
|
||||
chunk->headerFileSize =
|
||||
@@ -92,9 +92,9 @@ void AudioChunkManager::runTask() {
|
||||
if (chunk->endPosition > chunk->headerFileSize) {
|
||||
chunk->endPosition = chunk->headerFileSize;
|
||||
}
|
||||
CSPOT_LOG(debug, "ID: %d: Starting decrypt!",
|
||||
CSPOT_LOG(debug, "ID: %d: finalize chunk!",
|
||||
seqId);
|
||||
chunk->decrypt();
|
||||
chunk->finalize();
|
||||
chunk->isLoadedSemaphore->give();
|
||||
break;
|
||||
|
||||
|
||||
@@ -156,7 +156,6 @@ void ChunkedAudioStream::startPlaybackLoop()
|
||||
|
||||
void ChunkedAudioStream::seek(size_t dpos, Whence whence)
|
||||
{
|
||||
BELL_LOG(info, "cspot", "%d", dpos);
|
||||
auto seekPos = 0;
|
||||
switch (whence)
|
||||
{
|
||||
|
||||
@@ -31,6 +31,7 @@ void ChunkedByteStream::fetchFileInformation() {
|
||||
endChunk->keepInMemory = true;
|
||||
|
||||
chunks.push_back(endChunk);
|
||||
requestChunk(0);
|
||||
}
|
||||
|
||||
std::shared_ptr<AudioChunk> ChunkedByteStream::getChunkForPosition(size_t position) {
|
||||
@@ -57,11 +58,14 @@ size_t ChunkedByteStream::read(uint8_t *buf, size_t nbytes) {
|
||||
std::scoped_lock lock(this->readMutex);
|
||||
auto chunk = getChunkForPosition(pos);
|
||||
uint16_t chunkIndex = this->pos / AUDIO_CHUNK_SIZE;
|
||||
for (auto it = chunks.begin(); it != chunks.end();) {
|
||||
if (((*it)->endPosition<pos || (*it)->startPosition>(pos + 2 * AUDIO_CHUNK_SIZE)) && !(*it)->keepInMemory) {
|
||||
it = chunks.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
|
||||
if (loadAheadEnabled) {
|
||||
for (auto it = chunks.begin(); it != chunks.end();) {
|
||||
if (((*it)->endPosition<pos || (*it)->startPosition>(pos + 2 * AUDIO_CHUNK_SIZE)) && !(*it)->keepInMemory) {
|
||||
it = chunks.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +89,7 @@ size_t ChunkedByteStream::read(uint8_t *buf, size_t nbytes) {
|
||||
pos += read;
|
||||
|
||||
auto nextChunkPos = ((chunkIndex + 1) * AUDIO_CHUNK_SIZE) + 1;
|
||||
if (loadAheadEnabled && nextChunkPos + AUDIO_CHUNK_SIZE < fileSize) {
|
||||
if (loadAheadEnabled && nextChunkPos < fileSize) {
|
||||
auto nextChunk = getChunkForPosition(nextChunkPos);
|
||||
|
||||
if (nextChunk == nullptr) {
|
||||
@@ -109,15 +113,12 @@ size_t ChunkedByteStream::attemptRead(uint8_t *buffer, size_t bytes, std::shared
|
||||
toRead = chunk->decryptedData.size() - offset;
|
||||
}
|
||||
|
||||
// Copy data
|
||||
memcpy(buffer, chunk->decryptedData.data() + offset, toRead);
|
||||
|
||||
chunk->readData(buffer, offset, toRead);
|
||||
return toRead;
|
||||
}
|
||||
|
||||
void ChunkedByteStream::seek(size_t nbytes) {
|
||||
std::scoped_lock lock(this->readMutex);
|
||||
BELL_LOG(info, "cspot", "seeking to %d", nbytes);
|
||||
pos = nbytes;
|
||||
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ std::vector<uint8_t> LoginBlob::decodeBlob(const std::vector<uint8_t> &blob, con
|
||||
}
|
||||
|
||||
encryptionKey = std::vector<uint8_t>(encryptionKey.begin(), encryptionKey.begin() + 16);
|
||||
crypto->aesCTRXcrypt(encryptionKey, iv, encrypted);
|
||||
crypto->aesCTRXcrypt(encryptionKey, iv, encrypted.data(), encrypted.size());
|
||||
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ std::map<MercuryType, std::string> MercuryTypeMap({
|
||||
{MercuryType::UNSUB, "UNSUB"},
|
||||
});
|
||||
|
||||
MercuryManager::MercuryManager(std::unique_ptr<Session> session): bell::Task("mercuryManager", 6 * 1024, 2, 1)
|
||||
MercuryManager::MercuryManager(std::unique_ptr<Session> session): bell::Task("mercuryManager", 6 * 1024, 0, 1)
|
||||
{
|
||||
tempMercuryHeader = Header_init_default;
|
||||
tempMercuryHeader = {};
|
||||
this->timeProvider = std::make_shared<TimeProvider>();
|
||||
this->callbacks = std::map<uint64_t, mercuryCallback>();
|
||||
this->subscriptions = std::map<std::string, mercuryCallback>();
|
||||
@@ -294,8 +294,11 @@ uint64_t MercuryManager::execute(MercuryType method, std::string uri, mercuryCal
|
||||
// Construct mercury header
|
||||
|
||||
CSPOT_LOG(debug, "executing MercuryType %s", MercuryTypeMap[method].c_str());
|
||||
tempMercuryHeader.uri = (char *)(uri.c_str());
|
||||
tempMercuryHeader.method = (char *)(MercuryTypeMap[method].c_str());
|
||||
pbPutString(uri, tempMercuryHeader.uri);
|
||||
pbPutString(MercuryTypeMap[method], tempMercuryHeader.method);
|
||||
|
||||
tempMercuryHeader.has_method = true;
|
||||
tempMercuryHeader.has_uri = true;
|
||||
|
||||
// GET and SEND are actually the same. Therefore the override
|
||||
// The difference between them is only in header's method
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
MercuryResponse::MercuryResponse(std::vector<uint8_t> &data)
|
||||
{
|
||||
// this->mercuryHeader = std::make_unique<Header>();
|
||||
this->mercuryHeader = Header_init_default;
|
||||
this->mercuryHeader = {};
|
||||
this->parts = mercuryParts(0);
|
||||
this->parseResponse(data);
|
||||
}
|
||||
|
||||
MercuryResponse::~MercuryResponse() {
|
||||
pb_release(Header_fields, &mercuryHeader);
|
||||
}
|
||||
|
||||
void MercuryResponse::parseResponse(std::vector<uint8_t> &data)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "Packet.h"
|
||||
|
||||
Packet::Packet(uint8_t command, std::vector<uint8_t> &data) {
|
||||
Packet::Packet(uint8_t command, const std::vector<uint8_t> &data) {
|
||||
this->command = command;
|
||||
this->data = data;
|
||||
};
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
// #include <valgrind/memcheck.h>
|
||||
|
||||
Player::Player(std::shared_ptr<MercuryManager> manager, std::shared_ptr<AudioSink> audioSink): bell::Task("player", 10 * 1024, +0, 1)
|
||||
Player::Player(std::shared_ptr<MercuryManager> manager, std::shared_ptr<AudioSink> audioSink): bell::Task("player", 10 * 1024, -2, 1)
|
||||
{
|
||||
this->audioSink = audioSink;
|
||||
this->manager = manager;
|
||||
|
||||
@@ -24,18 +24,18 @@ PlayerState::PlayerState(std::shared_ptr<TimeProvider> timeProvider)
|
||||
innerFrame.state.repeat = false;
|
||||
innerFrame.state.has_repeat = true;
|
||||
|
||||
innerFrame.device_state.sw_version = (char*) swVersion;
|
||||
innerFrame.device_state.sw_version = strdup(swVersion);
|
||||
|
||||
innerFrame.device_state.is_active = false;
|
||||
innerFrame.device_state.has_is_active = true;
|
||||
|
||||
|
||||
innerFrame.device_state.can_play = true;
|
||||
innerFrame.device_state.has_can_play = true;
|
||||
|
||||
innerFrame.device_state.volume = configMan->volume;
|
||||
innerFrame.device_state.has_volume = true;
|
||||
|
||||
innerFrame.device_state.name = (char*) configMan->deviceName.c_str();
|
||||
innerFrame.device_state.name = strdup(configMan->deviceName.c_str());
|
||||
|
||||
// Prepare player's capabilities
|
||||
addCapability(CapabilityType_kCanBePlayer, 1);
|
||||
@@ -53,33 +53,32 @@ PlayerState::PlayerState(std::shared_ptr<TimeProvider> timeProvider)
|
||||
}
|
||||
|
||||
PlayerState::~PlayerState() {
|
||||
pb_release(Frame_fields, &innerFrame);
|
||||
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)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case PlaybackState::Loading:
|
||||
// Prepare the playback at position 0
|
||||
innerFrame.state.status = PlayStatus_kPlayStatusPause;
|
||||
innerFrame.state.position_ms = 0;
|
||||
innerFrame.state.position_measured_at = timeProvider->getSyncedTimestamp();
|
||||
break;
|
||||
case PlaybackState::Playing:
|
||||
innerFrame.state.status = PlayStatus_kPlayStatusPlay;
|
||||
innerFrame.state.position_measured_at = timeProvider->getSyncedTimestamp();
|
||||
break;
|
||||
case PlaybackState::Stopped:
|
||||
break;
|
||||
case PlaybackState::Paused:
|
||||
// Update state and recalculate current song position
|
||||
innerFrame.state.status = PlayStatus_kPlayStatusPause;
|
||||
uint32_t diff = timeProvider->getSyncedTimestamp() - innerFrame.state.position_measured_at;
|
||||
this->updatePositionMs(innerFrame.state.position_ms + diff);
|
||||
break;
|
||||
case PlaybackState::Loading:
|
||||
// Prepare the playback at position 0
|
||||
innerFrame.state.status = PlayStatus_kPlayStatusPause;
|
||||
innerFrame.state.position_ms = 0;
|
||||
innerFrame.state.position_measured_at = timeProvider->getSyncedTimestamp();
|
||||
break;
|
||||
case PlaybackState::Playing:
|
||||
innerFrame.state.status = PlayStatus_kPlayStatusPlay;
|
||||
innerFrame.state.position_measured_at = timeProvider->getSyncedTimestamp();
|
||||
break;
|
||||
case PlaybackState::Stopped:
|
||||
break;
|
||||
case PlaybackState::Paused:
|
||||
// Update state and recalculate current song position
|
||||
innerFrame.state.status = PlayStatus_kPlayStatusPause;
|
||||
uint32_t diff = timeProvider->getSyncedTimestamp() - innerFrame.state.position_measured_at;
|
||||
this->updatePositionMs(innerFrame.state.position_ms + diff);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,8 +136,8 @@ 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);
|
||||
std::copy(std::begin(remoteFrame.state.track), std::end(remoteFrame.state.track), std::begin(innerFrame.state.track));
|
||||
std::swap(innerFrame.state.context_uri, remoteFrame.state.context_uri);
|
||||
std::swap(innerFrame.state.track, remoteFrame.state.track);
|
||||
innerFrame.state.track_count = remoteFrame.state.track_count;
|
||||
innerFrame.state.has_playing_track_index = true;
|
||||
innerFrame.state.playing_track_index = remoteFrame.state.playing_track_index;
|
||||
@@ -167,17 +166,13 @@ void PlayerState::setShuffle(bool shuffle)
|
||||
if (shuffle)
|
||||
{
|
||||
// Put current song at the begining
|
||||
auto tmp = innerFrame.state.track[0];
|
||||
innerFrame.state.track[0] = innerFrame.state.track[innerFrame.state.playing_track_index];
|
||||
innerFrame.state.track[innerFrame.state.playing_track_index] = tmp;
|
||||
std::swap(innerFrame.state.track[0], innerFrame.state.track[innerFrame.state.playing_track_index]);
|
||||
|
||||
// Shuffle current tracks
|
||||
for (int x = 1; x < innerFrame.state.track_count - 1; x++)
|
||||
{
|
||||
auto j = x + (std::rand() % (innerFrame.state.track_count - x));
|
||||
tmp = innerFrame.state.track[j];
|
||||
innerFrame.state.track[j] = innerFrame.state.track[x];
|
||||
innerFrame.state.track[x] = tmp;
|
||||
std::swap(innerFrame.state.track[j], innerFrame.state.track[x]);
|
||||
}
|
||||
innerFrame.state.playing_track_index = 0;
|
||||
}
|
||||
@@ -196,11 +191,14 @@ std::shared_ptr<TrackReference> PlayerState::getCurrentTrack()
|
||||
|
||||
std::vector<uint8_t> PlayerState::encodeCurrentFrame(MessageType typ)
|
||||
{
|
||||
free(innerFrame.ident);
|
||||
free(innerFrame.protocol_version);
|
||||
|
||||
// Prepare current frame info
|
||||
innerFrame.version = 1;
|
||||
innerFrame.ident = (char *) deviceId;
|
||||
innerFrame.ident = strdup(deviceId);
|
||||
innerFrame.seq_nr = this->seqNum;
|
||||
innerFrame.protocol_version = (char*) protocolVersion;
|
||||
innerFrame.protocol_version = strdup(protocolVersion);
|
||||
innerFrame.typ = typ;
|
||||
innerFrame.state_update_id = timeProvider->getSyncedTimestamp();
|
||||
innerFrame.has_version = true;
|
||||
@@ -233,10 +231,9 @@ void PlayerState::addCapability(CapabilityType typ, int intValue, std::vector<st
|
||||
|
||||
for (int x = 0; x < stringValue.size(); x++)
|
||||
{
|
||||
stringValue[x].copy(this->innerFrame.device_state.capabilities[capabilityIndex].stringValue[x], stringValue[x].size());
|
||||
this->innerFrame.device_state.capabilities[capabilityIndex].stringValue[x][stringValue[x].size()] = '\0';
|
||||
pbPutString(stringValue[x], this->innerFrame.device_state.capabilities[capabilityIndex].stringValue[x]);
|
||||
}
|
||||
|
||||
this->innerFrame.device_state.capabilities[capabilityIndex].stringValue_count = stringValue.size();
|
||||
this->capabilityIndex += 1;
|
||||
}
|
||||
}
|
||||
@@ -49,17 +49,26 @@ std::vector<uint8_t> Session::authenticate(std::shared_ptr<LoginBlob> blob)
|
||||
authBlob = blob;
|
||||
|
||||
// prepare authentication request proto
|
||||
authRequest.login_credentials.username = (char *)(blob->username.c_str());
|
||||
authRequest.login_credentials.auth_data = vectorToPbArray(blob->authData);
|
||||
pbPutString(blob->username, authRequest.login_credentials.username);
|
||||
|
||||
std::copy(blob->authData.begin(), blob->authData.end(), authRequest.login_credentials.auth_data.bytes);
|
||||
authRequest.login_credentials.auth_data.size = blob->authData.size();
|
||||
|
||||
authRequest.login_credentials.typ = (AuthenticationType) blob->authType;
|
||||
authRequest.system_info.cpu_family = CpuFamily_CPU_UNKNOWN;
|
||||
authRequest.system_info.os = Os_OS_UNKNOWN;
|
||||
authRequest.system_info.system_information_string = (char *)informationString;
|
||||
authRequest.system_info.device_id = (char *)deviceId;
|
||||
authRequest.version_string = (char *)versionString;
|
||||
|
||||
auto infoStr = std::string(informationString);
|
||||
pbPutString(infoStr, authRequest.system_info.system_information_string);
|
||||
|
||||
auto deviceIdStr = std::string(deviceId);
|
||||
pbPutString(deviceId, authRequest.system_info.device_id);
|
||||
|
||||
auto versionStr = std::string(versionString);
|
||||
pbPutString(versionStr, authRequest.version_string);
|
||||
authRequest.has_version_string = true;
|
||||
|
||||
auto data = pbEncode(ClientResponseEncrypted_fields, &authRequest);
|
||||
free(authRequest.login_credentials.auth_data);
|
||||
|
||||
// Send login request
|
||||
this->shanConn->sendPacket(LOGIN_REQUEST_COMMAND, data);
|
||||
|
||||
@@ -56,7 +56,7 @@ void SpircController::disconnect(void) {
|
||||
state->setActive(false);
|
||||
notify();
|
||||
// Send the event at the end at it might be a last gasp
|
||||
sendEvent(CSpotEventType::DISC);
|
||||
sendEvent(CSpotEventType::DISC);
|
||||
}
|
||||
|
||||
void SpircController::playToggle() {
|
||||
@@ -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) {
|
||||
|
||||
@@ -10,8 +10,8 @@ SpotifyTrack::SpotifyTrack(std::shared_ptr<MercuryManager> manager, std::shared_
|
||||
{
|
||||
this->manager = manager;
|
||||
this->fileId = std::vector<uint8_t>();
|
||||
episodeInfo = Episode_init_default;
|
||||
trackInfo = Track_init_default;
|
||||
episodeInfo = {};
|
||||
trackInfo = {};
|
||||
|
||||
mercuryCallback trackResponseLambda = [=](std::unique_ptr<MercuryResponse> res) {
|
||||
this->trackInformationCallback(std::move(res), position_ms, isPaused);
|
||||
@@ -84,13 +84,15 @@ void SpotifyTrack::trackInformationCallback(std::unique_ptr<MercuryResponse> res
|
||||
int altIndex = 0;
|
||||
while (!canPlayTrack())
|
||||
{
|
||||
trackInfo.restriction = trackInfo.alternative[altIndex].restriction;
|
||||
trackInfo.restriction_count = trackInfo.alternative[altIndex].restriction_count;
|
||||
trackInfo.gid = trackInfo.alternative[altIndex].gid;
|
||||
trackInfo.file = trackInfo.alternative[altIndex].file;
|
||||
altIndex++;
|
||||
std::swap(trackInfo.restriction, trackInfo.alternative[altIndex].restriction);
|
||||
std::swap(trackInfo.restriction_count, trackInfo.alternative[altIndex].restriction_count);
|
||||
std::swap(trackInfo.file, trackInfo.alternative[altIndex].file);
|
||||
std::swap(trackInfo.file_count, trackInfo.alternative[altIndex].file_count);
|
||||
std::swap(trackInfo.gid, trackInfo.alternative[altIndex].gid);
|
||||
|
||||
CSPOT_LOG(info, "Trying alternative %d", altIndex);
|
||||
}
|
||||
|
||||
auto trackId = pbArrayToVector(trackInfo.gid);
|
||||
this->fileId = std::vector<uint8_t>();
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "display.h"
|
||||
#include "accessors.h"
|
||||
#include "network_services.h"
|
||||
#include "tools.h"
|
||||
#include "cspot_private.h"
|
||||
#include "cspot_sink.h"
|
||||
|
||||
@@ -87,6 +88,19 @@ const static actrls_t controls = {
|
||||
cspot_volume_down, cspot_volume_up, cspot_toggle// knob left, knob_right, knob push
|
||||
};
|
||||
|
||||
/****************************************************************************************
|
||||
* Download callback
|
||||
*/
|
||||
void got_artwork(uint8_t* data, size_t len, void *context) {
|
||||
if (data) {
|
||||
ESP_LOGI(TAG, "got artwork of %zu bytes", len);
|
||||
displayer_artwork(data);
|
||||
free(data);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "artwork error or too large %zu", len);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Command handler
|
||||
*/
|
||||
@@ -106,7 +120,7 @@ static bool cmd_handler(cspot_event_t event, ...) {
|
||||
switch(event) {
|
||||
case CSPOT_SETUP:
|
||||
actrls_set(controls, false, NULL, actrls_ir_action);
|
||||
displayer_control(DISPLAYER_ACTIVATE, "SPOTIFY");
|
||||
displayer_control(DISPLAYER_ACTIVATE, "SPOTIFY", true);
|
||||
break;
|
||||
case CSPOT_PLAY:
|
||||
displayer_control(DISPLAYER_TIMER_RUN);
|
||||
@@ -129,6 +143,11 @@ static bool cmd_handler(cspot_event_t event, ...) {
|
||||
uint32_t sample_rate = va_arg(args, uint32_t);
|
||||
int duration = va_arg(args, int);
|
||||
char *artist = va_arg(args, char*), *album = va_arg(args, char*), *title = va_arg(args, char*);
|
||||
char *artwork = va_arg(args, char*);
|
||||
if (artwork && displayer_can_artwork()) {
|
||||
ESP_LOGI(TAG, "requesting artwork %s", artwork);
|
||||
http_download(artwork, 128*1024, got_artwork, NULL);
|
||||
}
|
||||
displayer_metadata(artist, album, title);
|
||||
displayer_timer(DISPLAYER_ELAPSED, loaded ? -1 : 0, duration);
|
||||
loaded = false;
|
||||
|
||||
@@ -21,6 +21,11 @@ set_source_files_properties(mad.c pcm.c flac.c alac.c helix-aac.c vorbis.c opus.
|
||||
-Wno-maybe-uninitialized
|
||||
)
|
||||
|
||||
set_source_files_properties(wm8978/wm8978.c
|
||||
PROPERTIES COMPILE_FLAGS
|
||||
-Wno-unused-function
|
||||
)
|
||||
|
||||
add_definitions(-DLINKALL -DLOOPBACK -DNO_FAAD -DEMBEDDED -DTREMOR_ONLY -DCUSTOM_VERSION=${BUILD_NUMBER})
|
||||
|
||||
if (${DEPTH} EQUAL "32")
|
||||
|
||||
@@ -89,16 +89,14 @@ void equalizer_close(void) {
|
||||
*/
|
||||
void equalizer_update(s8_t *gain) {
|
||||
char config[EQ_BANDS * 4 + 1] = { };
|
||||
char *p = config;
|
||||
int n = 0;
|
||||
|
||||
for (int i = 0; i < EQ_BANDS; i++) {
|
||||
equalizer.gain[i] = gain[i];
|
||||
if (gain[i] < 0) *p++ = '-';
|
||||
*p++ = (gain[i] / 10) + 0x30;
|
||||
*p++ = (gain[i] % 10) + 0x30;
|
||||
if (i < EQ_BANDS - 1) *p++ = ',';
|
||||
n += sprintf(config + n, "%d,", gain[i]);
|
||||
}
|
||||
|
||||
config[n-1] = '\0';
|
||||
config_set_value(NVS_TYPE_STR, "equalizer", config);
|
||||
equalizer.update = true;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,10 @@ sure that using rate_delay would fix that
|
||||
#define STATS_PERIOD_MS 5000
|
||||
#define STAT_STACK_SIZE (3*1024)
|
||||
|
||||
#ifndef CONFIG_AMP_GPIO_LEVEL
|
||||
#define CONFIG_AMP_GPIO_LEVEL 1
|
||||
#endif
|
||||
|
||||
extern struct outputstate output;
|
||||
extern struct buffer *streambuf;
|
||||
extern struct buffer *outputbuf;
|
||||
@@ -101,7 +105,7 @@ static TaskHandle_t stats_task, output_i2s_task;
|
||||
static bool stats;
|
||||
static struct {
|
||||
int gpio, active;
|
||||
} amp_control = { -1, 1 },
|
||||
} amp_control = { CONFIG_AMP_GPIO, CONFIG_AMP_GPIO_LEVEL },
|
||||
mute_control = { CONFIG_MUTE_GPIO, CONFIG_MUTE_GPIO_LEVEL };
|
||||
|
||||
DECLARE_ALL_MIN_MAX;
|
||||
@@ -171,20 +175,16 @@ static void jack_handler(bool inserted) {
|
||||
/****************************************************************************************
|
||||
* amp GPIO
|
||||
*/
|
||||
#ifndef AMP_GPIO_LOCKED
|
||||
static void set_amp_gpio(int gpio, char *value) {
|
||||
char *p;
|
||||
|
||||
if (strcasestr(value, "amp")) {
|
||||
amp_control.gpio = gpio;
|
||||
if ((p = strchr(value, ':')) != NULL) amp_control.active = atoi(p + 1);
|
||||
|
||||
gpio_pad_select_gpio_x(amp_control.gpio);
|
||||
gpio_set_direction_x(amp_control.gpio, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level_x(amp_control.gpio, !amp_control.active);
|
||||
|
||||
LOG_INFO("setting amplifier GPIO %d (active:%d)", amp_control.gpio, amp_control.active);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************************
|
||||
* Set pin from config string
|
||||
@@ -247,7 +247,7 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
|
||||
// common I2S initialization
|
||||
i2s_config.mode = I2S_MODE_MASTER | I2S_MODE_TX;
|
||||
i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
|
||||
i2s_config.communication_format = I2S_COMM_FORMAT_I2S| I2S_COMM_FORMAT_I2S_MSB;
|
||||
i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S;
|
||||
// in case of overflow, do not replay old buffer
|
||||
i2s_config.tx_desc_auto_clear = true;
|
||||
i2s_config.use_apll = true;
|
||||
@@ -347,13 +347,21 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
|
||||
jack_handler_chain = jack_handler_svc;
|
||||
jack_handler_svc = jack_handler;
|
||||
|
||||
#ifndef AMP_GPIO_LOCKED
|
||||
parse_set_GPIO(set_amp_gpio);
|
||||
#endif
|
||||
|
||||
if (amp_control.gpio != -1) {
|
||||
gpio_pad_select_gpio_x(amp_control.gpio);
|
||||
gpio_set_direction_x(amp_control.gpio, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level_x(amp_control.gpio, !amp_control.active);
|
||||
LOG_INFO("setting amplifier GPIO %d (active:%d)", amp_control.gpio, amp_control.active);
|
||||
}
|
||||
|
||||
if (jack_mutes_amp && jack_inserted_svc()) adac->speaker(false);
|
||||
else adac->speaker(true);
|
||||
|
||||
adac->headset(jack_inserted_svc());
|
||||
|
||||
adac->headset(jack_inserted_svc());
|
||||
|
||||
// create task as a FreeRTOS task but uses stack in internal RAM
|
||||
{
|
||||
@@ -672,7 +680,7 @@ static const u16_t spdif_bmclookup[256] = { //biphase mark encoded values (least
|
||||
audio is transmitted first (not the MSB) and that ESP32 libray sends R then L,
|
||||
contrary to what seems to be usually done, so (dst) order had to be changed
|
||||
*/
|
||||
void spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst, size_t *count) {
|
||||
static void IRAM_ATTR spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst, size_t *count) {
|
||||
register u16_t hi, lo, aux;
|
||||
size_t cnt = *count;
|
||||
|
||||
|
||||
13
components/targets/CMakeLists.txt
Normal file
13
components/targets/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
# This should be made a pure CMake component but as CMake is even
|
||||
# more shitty under Windows, backslash in path screws it all
|
||||
|
||||
if(CONFIG_MUSE)
|
||||
message("Compiling for MUSE")
|
||||
set(src_dirs "muse")
|
||||
else()
|
||||
set(src_dirs ".")
|
||||
endif()
|
||||
|
||||
idf_component_register( SRC_DIRS ${src_dirs}
|
||||
PRIV_REQUIRES services
|
||||
)
|
||||
3
components/targets/init.c
Normal file
3
components/targets/init.c
Normal file
@@ -0,0 +1,3 @@
|
||||
// weak should do the job but it does not...
|
||||
__attribute__((weak)) void target_init(void) {
|
||||
}
|
||||
138
components/targets/muse/muse.c
Normal file
138
components/targets/muse/muse.c
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
YOUR LICENSE
|
||||
*/
|
||||
#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/adc.h>
|
||||
#include "driver/rmt.h"
|
||||
#include "monitor.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
//*********************** NeoPixels ***************************
|
||||
////////////////////////////////////////////////////////////////
|
||||
#define NUM_LEDS 1
|
||||
#define LED_RMT_TX_CHANNEL 0
|
||||
#define LED_RMT_TX_GPIO 22
|
||||
|
||||
#define BITS_PER_LED_CMD 24
|
||||
#define LED_BUFFER_ITEMS ((NUM_LEDS * BITS_PER_LED_CMD))
|
||||
|
||||
// These values are determined by measuring pulse timing with logic analyzer and adjusting to match datasheet.
|
||||
#define T0H 14 // 0 bit high time
|
||||
#define T1H 52 // 1 bit high time
|
||||
#define TL 52 // low time for either bit
|
||||
|
||||
#define GREEN 0xFF0000
|
||||
#define RED 0x00FF00
|
||||
#define BLUE 0x0000FF
|
||||
#define WHITE 0xFFFFFF
|
||||
#define YELLOW 0xE0F060
|
||||
struct led_state {
|
||||
uint32_t leds[NUM_LEDS];
|
||||
};
|
||||
|
||||
void ws2812_control_init(void);
|
||||
void ws2812_write_leds(struct led_state new_state);
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
static const char TAG[] = "muse";
|
||||
|
||||
static void (*battery_handler_chain)(float value);
|
||||
static void battery_svc(float value);
|
||||
|
||||
void target_init(void) {
|
||||
battery_handler_chain = battery_handler_svc;
|
||||
battery_handler_svc = battery_svc;
|
||||
ESP_LOGI(TAG, "Initializing for Muse");
|
||||
}
|
||||
|
||||
static void battery_svc(float value) {
|
||||
ESP_LOGI(TAG, "Called for battery service with %f", value);
|
||||
// put here your code for LED according to value
|
||||
if (battery_handler_chain) battery_handler_chain(value);
|
||||
}
|
||||
|
||||
// Battery monitoring
|
||||
/*
|
||||
static void battery(void *data)
|
||||
{
|
||||
#define VGREEN 2300
|
||||
#define VRED 2000
|
||||
#define NM 10
|
||||
static int val;
|
||||
static int V[NM];
|
||||
static int I=0;
|
||||
int S;
|
||||
for(int i=0;i<NM;i++)V[i]=VGREEN;
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
struct led_state new_state;
|
||||
ws2812_control_init();
|
||||
// init ADC interface for battery survey
|
||||
adc1_config_width(ADC_WIDTH_BIT_12);
|
||||
adc1_config_channel_atten(ADC1_GPIO33_CHANNEL, ADC_ATTEN_DB_11);
|
||||
while(true)
|
||||
{
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
V[I++] = adc1_get_raw(ADC1_GPIO33_CHANNEL);
|
||||
if(I >= NM)I = 0;
|
||||
S = 0;
|
||||
for(int i=0;i<NM;i++)S = S + V[i];
|
||||
val = S / NM;
|
||||
new_state.leds[0] = YELLOW;
|
||||
if(val > VGREEN) new_state.leds[0] = GREEN;
|
||||
if(val < VRED) new_state.leds[0] = RED;
|
||||
printf("====> %d %6x\n", val, new_state.leds[0]);
|
||||
ws2812_write_leds(new_state);
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// This is the buffer which the hw peripheral will access while pulsing the output pin
|
||||
rmt_item32_t led_data_buffer[LED_BUFFER_ITEMS];
|
||||
|
||||
void setup_rmt_data_buffer(struct led_state new_state);
|
||||
|
||||
void ws2812_control_init(void)
|
||||
{
|
||||
rmt_config_t config;
|
||||
config.rmt_mode = RMT_MODE_TX;
|
||||
config.channel = LED_RMT_TX_CHANNEL;
|
||||
config.gpio_num = LED_RMT_TX_GPIO;
|
||||
config.mem_block_num = 3;
|
||||
config.tx_config.loop_en = false;
|
||||
config.tx_config.carrier_en = false;
|
||||
config.tx_config.idle_output_en = true;
|
||||
config.tx_config.idle_level = 0;
|
||||
config.clk_div = 2;
|
||||
|
||||
ESP_ERROR_CHECK(rmt_config(&config));
|
||||
ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));
|
||||
}
|
||||
|
||||
void ws2812_write_leds(struct led_state new_state) {
|
||||
setup_rmt_data_buffer(new_state);
|
||||
ESP_ERROR_CHECK(rmt_write_items(LED_RMT_TX_CHANNEL, led_data_buffer, LED_BUFFER_ITEMS, false));
|
||||
ESP_ERROR_CHECK(rmt_wait_tx_done(LED_RMT_TX_CHANNEL, portMAX_DELAY));
|
||||
}
|
||||
|
||||
void setup_rmt_data_buffer(struct led_state new_state)
|
||||
{
|
||||
for (uint32_t led = 0; led < NUM_LEDS; led++) {
|
||||
uint32_t bits_to_send = new_state.leds[led];
|
||||
uint32_t mask = 1 << (BITS_PER_LED_CMD - 1);
|
||||
for (uint32_t bit = 0; bit < BITS_PER_LED_CMD; bit++) {
|
||||
uint32_t bit_is_set = bits_to_send & mask;
|
||||
led_data_buffer[led * BITS_PER_LED_CMD + bit] = bit_is_set ?
|
||||
(rmt_item32_t){{{T1H, 1, TL, 0}}} :
|
||||
(rmt_item32_t){{{T0H, 1, TL, 0}}};
|
||||
mask >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
idf_component_register( SRCS operator.cpp tools.c trace.c
|
||||
REQUIRES _override esp_common pthread
|
||||
PRIV_REQUIRES esp_http_client esp-tls
|
||||
INCLUDE_DIRS .
|
||||
)
|
||||
|
||||
|
||||
@@ -11,9 +11,14 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "tools.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_task.h"
|
||||
#include "esp_tls.h"
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_log.h"
|
||||
#include "tools.h"
|
||||
|
||||
const static char TAG[] = "tools";
|
||||
|
||||
@@ -171,3 +176,100 @@ char * strdup_psram(const char * source){
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* URL download
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
void *user_context;
|
||||
http_download_cb_t callback;
|
||||
size_t max, bytes;
|
||||
bool abort;
|
||||
uint8_t *data;
|
||||
esp_http_client_handle_t client;
|
||||
} http_context_t;
|
||||
|
||||
static void http_downloader(void *arg);
|
||||
static esp_err_t http_event_handler(esp_http_client_event_t *evt);
|
||||
|
||||
void http_download(char *url, size_t max, http_download_cb_t callback, void *context) {
|
||||
http_context_t *http_context = (http_context_t*) heap_caps_calloc(sizeof(http_context_t), 1, MALLOC_CAP_SPIRAM);
|
||||
|
||||
esp_http_client_config_t config = {
|
||||
.url = url,
|
||||
.event_handler = http_event_handler,
|
||||
.user_data = http_context,
|
||||
};
|
||||
|
||||
http_context->callback = callback;
|
||||
http_context->user_context = context;
|
||||
http_context->max = max;
|
||||
http_context->client = esp_http_client_init(&config);
|
||||
|
||||
xTaskCreate(http_downloader, "downloader", 4*1024, http_context, ESP_TASK_PRIO_MIN + 1, NULL);
|
||||
}
|
||||
|
||||
static void http_downloader(void *arg) {
|
||||
http_context_t *http_context = (http_context_t*) arg;
|
||||
|
||||
esp_http_client_perform(http_context->client);
|
||||
esp_http_client_cleanup(http_context->client);
|
||||
|
||||
free(http_context);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static esp_err_t http_event_handler(esp_http_client_event_t *evt) {
|
||||
http_context_t *http_context = (http_context_t*) evt->user_data;
|
||||
|
||||
if (http_context->abort) return ESP_FAIL;
|
||||
|
||||
switch(evt->event_id) {
|
||||
case HTTP_EVENT_ERROR:
|
||||
http_context->callback(NULL, 0, http_context->user_context);
|
||||
http_context->abort = true;
|
||||
break;
|
||||
case HTTP_EVENT_ON_HEADER:
|
||||
if (!strcasecmp(evt->header_key, "Content-Length")) {
|
||||
size_t len = atoi(evt->header_value);
|
||||
if (!len || len > http_context->max) {
|
||||
ESP_LOGI(TAG, "content-length null or too large %zu / %zu", len, http_context->max);
|
||||
http_context->abort = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HTTP_EVENT_ON_DATA: {
|
||||
size_t len = esp_http_client_get_content_length(evt->client);
|
||||
if (!http_context->data) {
|
||||
if ((http_context->data = (uint8_t*) malloc(len)) == NULL) {
|
||||
http_context->abort = true;
|
||||
ESP_LOGE(TAG, "gailed to allocate memory for output buffer %zu", len);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
memcpy(http_context->data + http_context->bytes, evt->data, evt->data_len);
|
||||
http_context->bytes += evt->data_len;
|
||||
break;
|
||||
}
|
||||
case HTTP_EVENT_ON_FINISH:
|
||||
http_context->callback(http_context->data, http_context->bytes, http_context->user_context);
|
||||
break;
|
||||
case HTTP_EVENT_DISCONNECTED: {
|
||||
int mbedtls_err = 0;
|
||||
esp_err_t err = esp_tls_get_and_clear_last_error(evt->data, &mbedtls_err, NULL);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "HTTP download disconnect %d", err);
|
||||
if (http_context->data) free(http_context->data);
|
||||
http_context->callback(NULL, 0, http_context->user_context);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,9 @@ char* strdup_psram(const char * source);
|
||||
const char* str_or_unknown(const char * str);
|
||||
const char* str_or_null(const char * str);
|
||||
|
||||
typedef void (*http_download_cb_t)(uint8_t* data, size_t len, void *context);
|
||||
void http_download(char *url, size_t max, http_download_cb_t callback, void *context);
|
||||
|
||||
extern const char unknown_string_placeholder[];
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -440,6 +440,7 @@
|
||||
GPL
|
||||
License.</li>
|
||||
<li>tarablessd1306, © 2017-2018, Tara Keeling. Licensed under the MIT license.</li>
|
||||
<li>CSpot, © 2020 feelfreelinux & alufers. Licensed under the GPL License</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
idf_component_register(SRC_DIRS .
|
||||
PRIV_REQUIRES esp_common wifi-manager pthread squeezelite-ota platform_console telnet display
|
||||
PRIV_REQUIRES esp_common wifi-manager pthread squeezelite-ota platform_console telnet display targets
|
||||
EMBED_FILES ../server_certs/github.pem
|
||||
LDFRAGMENTS "linker.lf"
|
||||
)
|
||||
|
||||
@@ -26,6 +26,8 @@ menu "Squeezelite-ESP32"
|
||||
help
|
||||
Set logging level info|debug|sdebug
|
||||
endmenu
|
||||
config AMP_LOCKED
|
||||
bool
|
||||
config JACK_LOCKED
|
||||
bool
|
||||
config BAT_LOCKED
|
||||
@@ -41,6 +43,9 @@ menu "Squeezelite-ESP32"
|
||||
config MUTE_GPIO_LEVEL
|
||||
int
|
||||
default 0
|
||||
config WELL_KNOWN
|
||||
bool
|
||||
default n
|
||||
menu "Target"
|
||||
choice OUTPUT_TYPE
|
||||
prompt "Main system"
|
||||
@@ -54,12 +59,21 @@ menu "Squeezelite-ESP32"
|
||||
select I2C_LOCKED
|
||||
select LED_LOCKED
|
||||
select SPKFAULT_LOCKED
|
||||
config BASIC_I2C_BT
|
||||
bool "Generic I2S & Bluetooth"
|
||||
config TWATCH2020
|
||||
bool "T-WATCH2020 by LilyGo"
|
||||
select WELL_KNOWN
|
||||
config MUSE
|
||||
bool "Muse"
|
||||
select JACK_LOCKED
|
||||
select BAT_LOCKED
|
||||
select I2C_LOCKED
|
||||
endchoice
|
||||
select AMP_LOCKED
|
||||
select WELL_KNOWN
|
||||
config BASIC_I2C_BT
|
||||
bool "Generic I2S & Bluetooth"
|
||||
config TWATCH2020
|
||||
bool "T-WATCH2020 by LilyGo"
|
||||
select I2C_LOCKED
|
||||
select WELL_KNOWN
|
||||
endchoice
|
||||
config RELEASE_API
|
||||
string "Software update URL"
|
||||
default "https://api.github.com/repos/sle118/squeezelite-esp32/releases"
|
||||
@@ -76,11 +90,13 @@ menu "Squeezelite-ESP32"
|
||||
string
|
||||
default "SqueezeAMP" if SQUEEZEAMP
|
||||
default "Squeezelite-TWATCH" if TWATCH2020
|
||||
default "Muse" if MUSE
|
||||
default "Squeezelite-ESP32"
|
||||
config FW_PLATFORM_NAME
|
||||
string
|
||||
default "SqueezeAmp" if SQUEEZEAMP
|
||||
default "TWATCH" if TWATCH2020
|
||||
default "Muse" if MUSE
|
||||
default "ESP32"
|
||||
# AGGREGATES - begin
|
||||
# these parameters are "aggregates" that take precedence. They must have a default value
|
||||
@@ -88,6 +104,7 @@ menu "Squeezelite-ESP32"
|
||||
string
|
||||
default "model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14:0" if SQUEEZEAMP
|
||||
default "model=I2S,bck=26,ws=25,do=33,i2c=53,sda=21,scl=22" if TWATCH2020
|
||||
default "model=I2S,bck=5,ws=25,do=26,di=35,i2c=16,sda=18,scl=23,mck" if MUSE
|
||||
default ""
|
||||
config SPDIF_CONFIG
|
||||
string
|
||||
@@ -98,7 +115,8 @@ menu "Squeezelite-ESP32"
|
||||
default ""
|
||||
config SPI_CONFIG
|
||||
string
|
||||
default "dc=27,data=19,clk=18" if TWATCH2020
|
||||
default "dc=27,data=19,clk=18" if TWATCH2020
|
||||
default "mosi=15,miso=2,clk=14" if MUSE
|
||||
default ""
|
||||
config DISPLAY_CONFIG
|
||||
string
|
||||
@@ -107,17 +125,28 @@ menu "Squeezelite-ESP32"
|
||||
config ETH_CONFIG
|
||||
string
|
||||
default ""
|
||||
# AGGREGATES - end
|
||||
config DAC_CONTROLSET
|
||||
string
|
||||
default '{ "init": [ {"reg":41, "val":128}, {"reg":18, "val":255} ], "poweron": [ {"reg":18, "val":64, "mode":"or"} ], "poweroff": [ {"reg":18, "val":191, "mode":"and" } ] }' if TWATCH2020
|
||||
default "{ \"init\": [ {\"reg\":41, \"val\":128}, {\"reg\":18, \"val\":255} ], \"poweron\": [ {\"reg\":18, \"val\":64, \"mode\":\"or\"} ], \"poweroff\": [ {\"reg\":18, \"val\":191, \"mode\":\"and\"} ] }" if TWATCH2020
|
||||
default "{\"init\":[ {\"reg\":0,\"val\":128}, {\"reg\":0,\"val\":0}, {\"reg\":25,\"val\":4}, {\"reg\":1,\"val\":80}, {\"reg\":2,\"val\":0}, {\"reg\":8,\"val\":0}, {\"reg\":4,\"val\":192}, {\"reg\":0,\"val\":18}, {\"reg\":1,\"val\":0}, {\"reg\":23,\"val\":24}, {\"reg\":24,\"val\":2}, {\"reg\":38,\"val\":9}, {\"reg\":39,\"val\":144}, {\"reg\":42,\"val\":144}, {\"reg\":43,\"val\":128}, {\"reg\":45,\"val\":128}, {\"reg\":27,\"val\":0}, {\"reg\":26,\"val\":0}, {\"reg\":2,\"val\":240}, {\"reg\":2,\"val\":0}, {\"reg\":29,\"val\":28}, {\"reg\":4,\"val\":48}, {\"reg\":25,\"val\":0} ]}" if MUSE
|
||||
default ""
|
||||
config AUDIO_CONTROLS
|
||||
string
|
||||
default "[{\"gpio\":32, \"pull\":true, \"debounce\":10, \"normal\":{\"pressed\":\"ACTRLS_VOLDOWN\"}}, {\"gpio\":19, \"pull\":true, \"debounce\":40, \"normal\":{\"pressed\":\"ACTRLS_VOLUP\"}}, {\"gpio\":12, \"pull\":true, \"debounce\":40, \"longpress\":1000, \"normal\":{\"pressed\":\"ACTRLS_TOGGLE\"},\"longpress\":{\"pressed\":\"ACTRLS_POWER\"}}]" if MUSE
|
||||
default ""
|
||||
# AGGREGATES - end
|
||||
|
||||
# VARs that must be reset when changign target
|
||||
config BAT_CONFIG
|
||||
default "channel=7,scale=20.24,atten=0" if SQUEEZEAMP
|
||||
default "channel=5,scale=1,atten=3,cells=1" if MUSE
|
||||
default ""
|
||||
config AMP_GPIO
|
||||
int
|
||||
default 21 if MUSE
|
||||
default -1
|
||||
config JACK_GPIO
|
||||
int
|
||||
default 34 if SQUEEZEAMP
|
||||
default -1
|
||||
default 34 if SQUEEZEAMP || MUSE
|
||||
default -1
|
||||
config SPKFAULT_GPIO
|
||||
int
|
||||
default 2 if SQUEEZEAMP
|
||||
@@ -129,6 +158,7 @@ menu "Squeezelite-ESP32"
|
||||
config LED_GREEN_GPIO
|
||||
int
|
||||
default 12 if SQUEEZEAMP
|
||||
default 22 if MUSE
|
||||
default -1
|
||||
config LED_RED_GPIO
|
||||
int
|
||||
@@ -274,6 +304,14 @@ menu "Squeezelite-ESP32"
|
||||
help
|
||||
Enable Spotify connect using CSpot
|
||||
endmenu
|
||||
|
||||
menu "Controls"
|
||||
depends on !MUSE
|
||||
config AUDIO_CONTROLS
|
||||
string "Audio buttons set (JSON)"
|
||||
help
|
||||
Configuration of buttons (see README for syntax)
|
||||
endmenu
|
||||
|
||||
menu "Display Screen"
|
||||
depends on !TWATCH2020
|
||||
@@ -320,8 +358,9 @@ menu "Squeezelite-ESP32"
|
||||
Set parameters of GPIO extender
|
||||
model=<model>[,addr=<addr>][,base=<100..N>][,count=<0..32>][,intr=<gpio>][,port=dac|system]
|
||||
endmenu
|
||||
|
||||
menu "LED configuration"
|
||||
visible if !SQUEEZEAMP && !TWATCH2020
|
||||
visible if !SQUEEZEAMP && !TWATCH2020 && !MUSE
|
||||
config LED_GREEN_GPIO
|
||||
int "Green led GPIO"
|
||||
help
|
||||
@@ -329,7 +368,7 @@ menu "Squeezelite-ESP32"
|
||||
config LED_GREEN_GPIO_LEVEL
|
||||
int "Green led ON level"
|
||||
depends on LED_GREEN_GPIO != -1
|
||||
config LED_RED_GPIO
|
||||
config LED_RED_GPIO
|
||||
int "Red led GPIO"
|
||||
help
|
||||
Set to -1 for no LED
|
||||
@@ -339,9 +378,10 @@ menu "Squeezelite-ESP32"
|
||||
default 0 if SQUEEZEAMP
|
||||
default 1
|
||||
endmenu
|
||||
menu "Audio JACK"
|
||||
visible if !SQUEEZEAMP && !TWATCH2020
|
||||
config JACK_GPIO
|
||||
|
||||
menu "Audio JACK"
|
||||
visible if !WELL_KNOWN
|
||||
config JACK_GPIO
|
||||
int "Jack insertion GPIO"
|
||||
help
|
||||
GPIO to detect speaker jack insertion. Set to -1 for no detection.
|
||||
@@ -349,10 +389,23 @@ menu "Squeezelite-ESP32"
|
||||
depends on JACK_GPIO != -1
|
||||
int "Level when inserted (0/1)"
|
||||
default 0
|
||||
endmenu
|
||||
menu "Speaker Fault"
|
||||
visible if !SQUEEZEAMP && !TWATCH2020
|
||||
config SPKFAULT_GPIO
|
||||
endmenu
|
||||
|
||||
menu "Amplifier"
|
||||
visible if !WELL_KNOWN
|
||||
config AMP_GPIO
|
||||
int "Amplifier GPIO"
|
||||
help
|
||||
GPIO to switch on/off amplifier. Set to -1 for no amplifier.
|
||||
config AMP_GPIO_LEVEL
|
||||
depends on AMP_GPIO != -1
|
||||
int "Active level(0/1)"
|
||||
default 1
|
||||
endmenu
|
||||
|
||||
menu "Speaker Fault"
|
||||
visible if !WELL_KNOWN
|
||||
config SPKFAULT_GPIO
|
||||
int "Speaker fault GPIO"
|
||||
help
|
||||
GPIO to detect speaker fault condition. Set to -1 for no detection.
|
||||
@@ -361,20 +414,16 @@ menu "Squeezelite-ESP32"
|
||||
int "Level when fault (0/1)"
|
||||
default 0
|
||||
endmenu
|
||||
|
||||
menu "Battery measure"
|
||||
visible if !SQUEEZEAMP && !TWATCH2020
|
||||
config BAT_CHANNEL
|
||||
int "Set channel (0..7)"
|
||||
visible if !WELL_KNOWN
|
||||
config BAT_CONFIG
|
||||
string "Battery acquisition configuration"
|
||||
help
|
||||
Read a value every 10s on ADC1 on set Channel
|
||||
config BAT_SCALE
|
||||
string "Set scaling factor"
|
||||
depends on BAT_CHANNEL != -1
|
||||
default "20.24" if SQUEEZEAMP
|
||||
default ""
|
||||
help
|
||||
Set the scaling factor for this 12 bits ADC
|
||||
Sets parameters for battery voltage measure
|
||||
channel=<0..7>,scale=<ratio_to_4096>,atten=<adc_atten>,cells=<1..3>
|
||||
endmenu
|
||||
|
||||
config DEFAULT_COMMAND_LINE
|
||||
string "Default command line to execute"
|
||||
default "squeezelite -o I2S -b 500:2000 -d all=info -C 30"
|
||||
|
||||
@@ -71,6 +71,7 @@ extern const uint8_t server_cert_pem_end[] asm("_binary_github_pem_end");
|
||||
// as an exception _init function don't need include
|
||||
extern void services_init(void);
|
||||
extern void display_init(char *welcome);
|
||||
extern void target_init(void);
|
||||
const char * str_or_unknown(const char * str) { return (str?str:unknown_string_placeholder); }
|
||||
const char * str_or_null(const char * str) { return (str?str:null_string_placeholder); }
|
||||
bool is_recovery_running;
|
||||
@@ -446,6 +447,7 @@ void app_main()
|
||||
ESP_LOGI(TAG,"Initializing display");
|
||||
display_init("SqueezeESP32");
|
||||
MEMTRACE_PRINT_DELTA();
|
||||
target_init();
|
||||
if(is_recovery_running && display){
|
||||
GDS_ClearExt(display, true);
|
||||
GDS_SetFont(display, &Font_line_2 );
|
||||
|
||||
Reference in New Issue
Block a user