diff --git a/build-scripts/I2S-4MFlash-sdkconfig.defaults b/build-scripts/I2S-4MFlash-sdkconfig.defaults index aaa52f80..d3914d3f 100644 --- a/build-scripts/I2S-4MFlash-sdkconfig.defaults +++ b/build-scripts/I2S-4MFlash-sdkconfig.defaults @@ -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 diff --git a/build-scripts/SqueezeAmp-sdkconfig.defaults b/build-scripts/SqueezeAmp-sdkconfig.defaults index 57abadb8..326bbbd7 100644 --- a/build-scripts/SqueezeAmp-sdkconfig.defaults +++ b/build-scripts/SqueezeAmp-sdkconfig.defaults @@ -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 diff --git a/components/display/CMakeLists.txt b/components/display/CMakeLists.txt index 01886005..8a1944b9 100644 --- a/components/display/CMakeLists.txt +++ b/components/display/CMakeLists.txt @@ -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 diff --git a/components/display/SH1106.c b/components/display/SH1106.c index 7bd28182..11e69707 100644 --- a/components/display/SH1106.c +++ b/components/display/SH1106.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); } diff --git a/components/display/core/gds.c b/components/display/core/gds.c index 63e32861..fc1d8d3d 100644 --- a/components/display/core/gds.c +++ b/components/display/core/gds.c @@ -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 ); } \ No newline at end of file diff --git a/components/display/core/gds.h b/components/display/core/gds.h index d79ade04..fa782329 100644 --- a/components/display/core/gds.h +++ b/components/display/core/gds.h @@ -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 ); diff --git a/components/display/core/gds_font.c b/components/display/core/gds_font.c index 2ea38872..65c1a08e 100644 --- a/components/display/core/gds_font.c +++ b/components/display/core/gds_font.c @@ -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; } diff --git a/components/display/core/gds_image.c b/components/display/core/gds_image.c index cbb392e5..f47e3383 100644 --- a/components/display/core/gds_image.c +++ b/components/display/core/gds_image.c @@ -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"); diff --git a/components/display/core/gds_private.h b/components/display/core/gds_private.h index 6b098ebe..17203083 100644 --- a/components/display/core/gds_private.h +++ b/components/display/core/gds_private.h @@ -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; diff --git a/components/display/core/gds_text.c b/components/display/core/gds_text.c index 468414ba..b2ef8903 100644 --- a/components/display/core/gds_text.c +++ b/components/display/core/gds_text.c @@ -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'; } diff --git a/components/display/core/ifaces/default_if_i2c.c b/components/display/core/ifaces/default_if_i2c.c index 28fd2347..22f5934e 100644 --- a/components/display/core/ifaces/default_if_i2c.c +++ b/components/display/core/ifaces/default_if_i2c.c @@ -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 ) { diff --git a/components/display/core/ifaces/default_if_spi.c b/components/display/core/ifaces/default_if_spi.c index 968ce51c..1f047584 100644 --- a/components/display/core/ifaces/default_if_spi.c +++ b/components/display/core/ifaces/default_if_spi.c @@ -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 ) { diff --git a/components/display/display.c b/components/display/display.c index 0d965a44..94414dac 100644 --- a/components/display/display.c +++ b/components/display/display.c @@ -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; diff --git a/components/display/display.h b/components/display/display.h index 9c945555..278bc077 100644 --- a/components/display/display.h +++ b/components/display/display.h @@ -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); diff --git a/components/display/note.jpg b/components/display/note.jpg new file mode 100644 index 00000000..6beb3ef7 Binary files /dev/null and b/components/display/note.jpg differ diff --git a/components/driver_bt/bt_app_sink.c b/components/driver_bt/bt_app_sink.c index e741f108..7a3a866b 100644 --- a/components/driver_bt/bt_app_sink.c +++ b/components/driver_bt/bt_app_sink.c @@ -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); diff --git a/components/platform_config/platform_config.h b/components/platform_config/platform_config.h index e8782ca6..be2a982b 100644 --- a/components/platform_config/platform_config.h +++ b/components/platform_config/platform_config.h @@ -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))) { \ diff --git a/components/platform_console/CMakeLists.txt b/components/platform_console/CMakeLists.txt index 6e3ef4cb..5a315a3e 100644 --- a/components/platform_console/CMakeLists.txt +++ b/components/platform_console/CMakeLists.txt @@ -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/$/lib$.a ) + +set_source_files_properties(cmd_config.c + PROPERTIES COMPILE_FLAGS + -Wno-unused-function +) + diff --git a/components/platform_console/platform_console.c b/components/platform_console/platform_console.c index a0d1f506..999d63b7 100644 --- a/components/platform_console/platform_console.c +++ b/components/platform_console/platform_console.c @@ -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 */ diff --git a/components/raop/raop.c b/components/raop/raop.c index 53679a78..82f5e8b4 100644 --- a/components/raop/raop.c +++ b/components/raop/raop.c @@ -628,6 +628,9 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) success = ctx->cmd_cb(RAOP_METADATA, metadata.artist, metadata.album, metadata.title); free_metadata(&metadata); } + } else if (body && ((p = kd_lookup(headers, "Content-Type")) != NULL) && strcasestr(p, "image/jpeg")) { + LOG_INFO("[%p]: received JPEG image of %d bytes", ctx, len); + ctx->cmd_cb(RAOP_ARTWORK, body, len); } else { char *dump = kd_dump(headers); LOG_INFO("Unhandled SET PARAMETER\n%s", dump); diff --git a/components/raop/raop_sink.c b/components/raop/raop_sink.c index fe66b365..ea3f02f0 100644 --- a/components/raop/raop_sink.c +++ b/components/raop/raop_sink.c @@ -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); diff --git a/components/raop/raop_sink.h b/components/raop/raop_sink.h index a3cb87d4..4f6c5dea 100644 --- a/components/raop/raop_sink.h +++ b/components/raop/raop_sink.h @@ -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 ; diff --git a/components/services/accessors.c b/components/services/accessors.c index 312e0e75..683870d3 100644 --- a/components/services/accessors.c +++ b/components/services/accessors.c @@ -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); diff --git a/components/services/battery.c b/components/services/battery.c index a0134c8c..09efe79e 100644 --- a/components/services/battery.c +++ b/components/services/battery.c @@ -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"); } diff --git a/components/services/monitor.h b/components/services/monitor.h index 3f441ff9..b0710153 100644 --- a/components/services/monitor.h +++ b/components/services/monitor.h @@ -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); diff --git a/components/spotify/Shim.cpp b/components/spotify/Shim.cpp index 87020b8a..577143e3 100644 --- a/components/spotify/Shim.cpp +++ b/components/spotify/Shim.cpp @@ -119,8 +119,8 @@ static void cspotTask(void *pvParameters) { switch (event.eventType) { case CSpotEventType::TRACK_INFO: { TrackInfo track = std::get(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; } diff --git a/components/spotify/cspot/CMakeLists.txt b/components/spotify/cspot/CMakeLists.txt index e91c8460..3f5f44ce 100644 --- a/components/spotify/cspot/CMakeLists.txt +++ b/components/spotify/cspot/CMakeLists.txt @@ -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}) diff --git a/components/spotify/cspot/bell/CMakeLists.txt b/components/spotify/cspot/bell/CMakeLists.txt index c900048f..2b81a0d4 100644 --- a/components/spotify/cspot/bell/CMakeLists.txt +++ b/components/spotify/cspot/bell/CMakeLists.txt @@ -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) diff --git a/components/spotify/cspot/bell/include/CryptoMbedTLS.h b/components/spotify/cspot/bell/include/CryptoMbedTLS.h index 089a3bad..27a4ff42 100644 --- a/components/spotify/cspot/bell/include/CryptoMbedTLS.h +++ b/components/spotify/cspot/bell/include/CryptoMbedTLS.h @@ -56,7 +56,7 @@ public: std::vector sha1HMAC(const std::vector& inputKey, const std::vector& message); // AES CTR - void aesCTRXcrypt(const std::vector& key, std::vector& iv, std::vector& data); + void aesCTRXcrypt(const std::vector& key, std::vector& iv, uint8_t* data, size_t nbytes); // AES ECB void aesECBdecrypt(const std::vector& key, std::vector& data); diff --git a/components/spotify/cspot/bell/include/CryptoOpenSSL.h b/components/spotify/cspot/bell/include/CryptoOpenSSL.h index abc8d01c..56310fd3 100644 --- a/components/spotify/cspot/bell/include/CryptoOpenSSL.h +++ b/components/spotify/cspot/bell/include/CryptoOpenSSL.h @@ -63,7 +63,7 @@ public: std::vector sha1HMAC(const std::vector& inputKey, const std::vector& message); // AES CTR - void aesCTRXcrypt(const std::vector& key, std::vector& iv, std::vector& data); + void aesCTRXcrypt(const std::vector& key, std::vector& iv, uint8_t* buffer, size_t nbytes); // AES ECB void aesECBdecrypt(const std::vector& key, std::vector& data); diff --git a/components/spotify/cspot/bell/include/NanoPBHelper.h b/components/spotify/cspot/bell/include/NanoPBHelper.h index d370b199..de6a26b1 100644 --- a/components/spotify/cspot/bell/include/NanoPBHelper.h +++ b/components/spotify/cspot/bell/include/NanoPBHelper.h @@ -42,6 +42,10 @@ void pbDecode(T &result, const pb_msgdesc_t *fields, std::vector &data) } } +void pbPutString(const std::string &stringToPack, char* dst); +void pbPutCharArray(const char * stringToPack, char* dst); +void pbPutBytes(const std::vector &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); diff --git a/components/spotify/cspot/bell/include/Task.h b/components/spotify/cspot/bell/include/Task.h index 72f9412b..425b2072 100644 --- a/components/spotify/cspot/bell/include/Task.h +++ b/components/spotify/cspot/bell/include/Task.h @@ -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); } diff --git a/components/spotify/cspot/bell/src/CryptoMBedTLS.cpp b/components/spotify/cspot/bell/src/CryptoMBedTLS.cpp index 7fbe5c72..fc596762 100644 --- a/components/spotify/cspot/bell/src/CryptoMBedTLS.cpp +++ b/components/spotify/cspot/bell/src/CryptoMBedTLS.cpp @@ -88,7 +88,7 @@ std::vector CryptoMbedTLS::sha1HMAC(const std::vector &inputKe } // AES CTR -void CryptoMbedTLS::aesCTRXcrypt(const std::vector &key, std::vector &iv, std::vector &data) +void CryptoMbedTLS::aesCTRXcrypt(const std::vector &key, std::vector &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 &key, std::vector &key, std::vector &data) @@ -115,7 +115,7 @@ void CryptoMbedTLS::aesECBdecrypt(const std::vector &key, std::vector CryptoOpenSSL::sha1HMAC(const std::vector& inputKe } // AES CTR -void CryptoOpenSSL::aesCTRXcrypt(const std::vector& key, std::vector& iv, std::vector &data) +void CryptoOpenSSL::aesCTRXcrypt(const std::vector& key, std::vector& iv, uint8_t* buffer, size_t nbytes) { // Prepare AES_KEY auto cryptoKey = AES_KEY(); @@ -110,9 +110,9 @@ void CryptoOpenSSL::aesCTRXcrypt(const std::vector& key, std::vectorread(buffer, BUF_SIZE); diff --git a/components/spotify/cspot/bell/src/NanoPBHelper.cpp b/components/spotify/cspot/bell/src/NanoPBHelper.cpp index c19e031d..0bd4c27d 100644 --- a/components/spotify/cspot/bell/src/NanoPBHelper.cpp +++ b/components/spotify/cspot/bell/src/NanoPBHelper.cpp @@ -47,6 +47,22 @@ pb_bytes_array_t* vectorToPbArray(const std::vector& 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 &data, pb_bytes_array_t &dst) { + dst.size = data.size(); + std::copy(data.begin(), data.end(), dst.bytes); +} + std::vector pbArrayToVector(pb_bytes_array_t* pbArray) { return std::vector(pbArray->bytes, pbArray->bytes + pbArray->size); } diff --git a/components/spotify/cspot/include/AudioChunk.h b/components/spotify/cspot/include/AudioChunk.h index 3aa46878..3b75e789 100644 --- a/components/spotify/cspot/include/AudioChunk.h +++ b/components/spotify/cspot/include/AudioChunk.h @@ -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 + * @return std::vector */ std::vector getIVSum(uint32_t num); + size_t decryptedCount = 0; + size_t oldStartPos; + public: std::unique_ptr crypto; std::vector decryptedData; @@ -41,14 +44,21 @@ public: std::unique_ptr isLoadedSemaphore; /** - * @brief + * @brief */ std::unique_ptr 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 &data); /** - * @brief Performs AES CTR decryption of received data. - * + * @brief Sets loaded status on the chunk + * */ - void decrypt(); + void finalize(); }; #endif diff --git a/components/spotify/cspot/include/Packet.h b/components/spotify/cspot/include/Packet.h index 7635db87..d7b828f1 100644 --- a/components/spotify/cspot/include/Packet.h +++ b/components/spotify/cspot/include/Packet.h @@ -9,7 +9,7 @@ class Packet private: public: - Packet(uint8_t command, std::vector &data); + Packet(uint8_t command, const std::vector &data); uint8_t command; std::vector data; }; diff --git a/components/spotify/cspot/include/Shannon.h b/components/spotify/cspot/include/Shannon.h index df9a5eb2..34107bf2 100644 --- a/components/spotify/cspot/include/Shannon.h +++ b/components/spotify/cspot/include/Shannon.h @@ -13,7 +13,7 @@ public: void stream(std::vector &buf); /* stream cipher */ void maconly(std::vector &buf); /* accumulate MAC */ void encrypt(std::vector &buf); /* encrypt + MAC */ - void decrypt(std::vector &buf); /* decrypt + MAC */ + void decrypt(std::vector &buf); /* finalize + MAC */ void finish(std::vector &buf); /* finalise MAC */ private: diff --git a/components/spotify/cspot/include/SpircController.h b/components/spotify/cspot/include/SpircController.h index 32cbd182..a681031b 100644 --- a/components/spotify/cspot/include/SpircController.h +++ b/components/spotify/cspot/include/SpircController.h @@ -37,6 +37,7 @@ class SpircController { private: std::shared_ptr manager; std::string username; + bool firstFrame = true; std::unique_ptr player; std::unique_ptr state; std::shared_ptr audioSink; diff --git a/components/spotify/cspot/protobuf/authentication.options b/components/spotify/cspot/protobuf/authentication.options index f7c08e40..33aad19f 100644 --- a/components/spotify/cspot/protobuf/authentication.options +++ b/components/spotify/cspot/protobuf/authentication.options @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/components/spotify/cspot/protobuf/authentication.pb.c b/components/spotify/cspot/protobuf/authentication.pb.c index b45976af..3e9fd652 100644 --- a/components/spotify/cspot/protobuf/authentication.pb.c +++ b/components/spotify/cspot/protobuf/authentication.pb.c @@ -9,7 +9,7 @@ PB_BIND(SystemInfo, SystemInfo, 2) -PB_BIND(LoginCredentials, LoginCredentials, AUTO) +PB_BIND(LoginCredentials, LoginCredentials, 2) PB_BIND(ClientResponseEncrypted, ClientResponseEncrypted, 2) diff --git a/components/spotify/cspot/protobuf/authentication.pb.h b/components/spotify/cspot/protobuf/authentication.pb.h index a331adfb..e3eb8e91 100644 --- a/components/spotify/cspot/protobuf/authentication.pb.h +++ b/components/spotify/cspot/protobuf/authentication.pb.h @@ -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" */ diff --git a/components/spotify/cspot/protobuf/authentication.proto b/components/spotify/cspot/protobuf/authentication.proto index 84d1b2ee..d3896147 100644 --- a/components/spotify/cspot/protobuf/authentication.proto +++ b/components/spotify/cspot/protobuf/authentication.proto @@ -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 { diff --git a/components/spotify/cspot/protobuf/mercury.options b/components/spotify/cspot/protobuf/mercury.options index f5a10767..29da3dcb 100644 --- a/components/spotify/cspot/protobuf/mercury.options +++ b/components/spotify/cspot/protobuf/mercury.options @@ -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 diff --git a/components/spotify/cspot/protobuf/mercury.pb.h b/components/spotify/cspot/protobuf/mercury.pb.h index dabc1980..32e61d09 100644 --- a/components/spotify/cspot/protobuf/mercury.pb.h +++ b/components/spotify/cspot/protobuf/mercury.pb.h @@ -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" */ diff --git a/components/spotify/cspot/protobuf/metadata.options b/components/spotify/cspot/protobuf/metadata.options index 7504a906..3758c5f6 100644 --- a/components/spotify/cspot/protobuf/metadata.options +++ b/components/spotify/cspot/protobuf/metadata.options @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/components/spotify/cspot/protobuf/spirc.options b/components/spotify/cspot/protobuf/spirc.options index 5f853084..3ed13c47 100644 --- a/components/spotify/cspot/protobuf/spirc.options +++ b/components/spotify/cspot/protobuf/spirc.options @@ -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 \ No newline at end of file diff --git a/components/spotify/cspot/protobuf/spirc.pb.c b/components/spotify/cspot/protobuf/spirc.pb.c index f6dfc4b1..fd9bffb5 100644 --- a/components/spotify/cspot/protobuf/spirc.pb.c +++ b/components/spotify/cspot/protobuf/spirc.pb.c @@ -9,7 +9,7 @@ PB_BIND(TrackRef, TrackRef, AUTO) -PB_BIND(State, State, 4) +PB_BIND(State, State, AUTO) PB_BIND(Capability, Capability, 2) diff --git a/components/spotify/cspot/protobuf/spirc.pb.h b/components/spotify/cspot/protobuf/spirc.pb.h index 9ebc82b6..9b79fb63 100644 --- a/components/spotify/cspot/protobuf/spirc.pb.h +++ b/components/spotify/cspot/protobuf/spirc.pb.h @@ -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 diff --git a/components/spotify/cspot/src/AudioChunk.cpp b/components/spotify/cspot/src/AudioChunk.cpp index 17ff25b7..407fc425 100644 --- a/components/spotify/cspot/src/AudioChunk.cpp +++ b/components/spotify/cspot/src/AudioChunk.cpp @@ -24,13 +24,28 @@ void AudioChunk::appendData(const std::vector &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; } diff --git a/components/spotify/cspot/src/AudioChunkManager.cpp b/components/spotify/cspot/src/AudioChunkManager.cpp index 98426814..827d2a32 100644 --- a/components/spotify/cspot/src/AudioChunkManager.cpp +++ b/components/spotify/cspot/src/AudioChunkManager.cpp @@ -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>(); 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(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; diff --git a/components/spotify/cspot/src/ChunkedAudioStream.cpp b/components/spotify/cspot/src/ChunkedAudioStream.cpp index 62fe7a0d..432eb103 100644 --- a/components/spotify/cspot/src/ChunkedAudioStream.cpp +++ b/components/spotify/cspot/src/ChunkedAudioStream.cpp @@ -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) { diff --git a/components/spotify/cspot/src/ChunkedByteStream.cpp b/components/spotify/cspot/src/ChunkedByteStream.cpp index 643544d3..0c92aa84 100644 --- a/components/spotify/cspot/src/ChunkedByteStream.cpp +++ b/components/spotify/cspot/src/ChunkedByteStream.cpp @@ -31,6 +31,7 @@ void ChunkedByteStream::fetchFileInformation() { endChunk->keepInMemory = true; chunks.push_back(endChunk); + requestChunk(0); } std::shared_ptr 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)->endPositionstartPosition>(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)->endPositionstartPosition>(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; diff --git a/components/spotify/cspot/src/LoginBlob.cpp b/components/spotify/cspot/src/LoginBlob.cpp index 36143e9f..477b9b94 100644 --- a/components/spotify/cspot/src/LoginBlob.cpp +++ b/components/spotify/cspot/src/LoginBlob.cpp @@ -35,7 +35,7 @@ std::vector LoginBlob::decodeBlob(const std::vector &blob, con } encryptionKey = std::vector(encryptionKey.begin(), encryptionKey.begin() + 16); - crypto->aesCTRXcrypt(encryptionKey, iv, encrypted); + crypto->aesCTRXcrypt(encryptionKey, iv, encrypted.data(), encrypted.size()); return encrypted; } diff --git a/components/spotify/cspot/src/MercuryManager.cpp b/components/spotify/cspot/src/MercuryManager.cpp index 62c842f3..87fd2ff9 100644 --- a/components/spotify/cspot/src/MercuryManager.cpp +++ b/components/spotify/cspot/src/MercuryManager.cpp @@ -9,9 +9,9 @@ std::map MercuryTypeMap({ {MercuryType::UNSUB, "UNSUB"}, }); -MercuryManager::MercuryManager(std::unique_ptr session): bell::Task("mercuryManager", 6 * 1024, 2, 1) +MercuryManager::MercuryManager(std::unique_ptr session): bell::Task("mercuryManager", 6 * 1024, 0, 1) { - tempMercuryHeader = Header_init_default; + tempMercuryHeader = {}; this->timeProvider = std::make_shared(); this->callbacks = std::map(); this->subscriptions = std::map(); @@ -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 diff --git a/components/spotify/cspot/src/MercuryResponse.cpp b/components/spotify/cspot/src/MercuryResponse.cpp index 7026e98f..46dd8a48 100644 --- a/components/spotify/cspot/src/MercuryResponse.cpp +++ b/components/spotify/cspot/src/MercuryResponse.cpp @@ -3,13 +3,12 @@ MercuryResponse::MercuryResponse(std::vector &data) { // this->mercuryHeader = std::make_unique
(); - 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 &data) diff --git a/components/spotify/cspot/src/Packet.cpp b/components/spotify/cspot/src/Packet.cpp index 534e7cba..f7e0a503 100644 --- a/components/spotify/cspot/src/Packet.cpp +++ b/components/spotify/cspot/src/Packet.cpp @@ -1,6 +1,6 @@ #include "Packet.h" -Packet::Packet(uint8_t command, std::vector &data) { +Packet::Packet(uint8_t command, const std::vector &data) { this->command = command; this->data = data; }; \ No newline at end of file diff --git a/components/spotify/cspot/src/Player.cpp b/components/spotify/cspot/src/Player.cpp index 0fb32b6d..3bccc0b0 100644 --- a/components/spotify/cspot/src/Player.cpp +++ b/components/spotify/cspot/src/Player.cpp @@ -3,7 +3,7 @@ // #include -Player::Player(std::shared_ptr manager, std::shared_ptr audioSink): bell::Task("player", 10 * 1024, +0, 1) +Player::Player(std::shared_ptr manager, std::shared_ptr audioSink): bell::Task("player", 10 * 1024, -2, 1) { this->audioSink = audioSink; this->manager = manager; diff --git a/components/spotify/cspot/src/PlayerState.cpp b/components/spotify/cspot/src/PlayerState.cpp index 70e87430..8c3ef7b4 100644 --- a/components/spotify/cspot/src/PlayerState.cpp +++ b/components/spotify/cspot/src/PlayerState.cpp @@ -24,18 +24,18 @@ PlayerState::PlayerState(std::shared_ptr 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) } 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 PlayerState::getCurrentTrack() std::vector 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::vectorinnerFrame.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; -} +} \ No newline at end of file diff --git a/components/spotify/cspot/src/Session.cpp b/components/spotify/cspot/src/Session.cpp index 4a51c4d9..638ef7f3 100644 --- a/components/spotify/cspot/src/Session.cpp +++ b/components/spotify/cspot/src/Session.cpp @@ -49,17 +49,26 @@ std::vector Session::authenticate(std::shared_ptr 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); diff --git a/components/spotify/cspot/src/SpircController.cpp b/components/spotify/cspot/src/SpircController.cpp index da9586aa..7de1e737 100644 --- a/components/spotify/cspot/src/SpircController.cpp +++ b/components/spotify/cspot/src/SpircController.cpp @@ -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 &data) { - //pb_release(Frame_fields, &state->remoteFrame); + pb_release(Frame_fields, &state->remoteFrame); pbDecode(state->remoteFrame, Frame_fields, data); switch (state->remoteFrame.typ) { diff --git a/components/spotify/cspot/src/SpotifyTrack.cpp b/components/spotify/cspot/src/SpotifyTrack.cpp index 6db0416c..ad39f122 100644 --- a/components/spotify/cspot/src/SpotifyTrack.cpp +++ b/components/spotify/cspot/src/SpotifyTrack.cpp @@ -10,8 +10,8 @@ SpotifyTrack::SpotifyTrack(std::shared_ptr manager, std::shared_ { this->manager = manager; this->fileId = std::vector(); - episodeInfo = Episode_init_default; - trackInfo = Track_init_default; + episodeInfo = {}; + trackInfo = {}; mercuryCallback trackResponseLambda = [=](std::unique_ptr res) { this->trackInformationCallback(std::move(res), position_ms, isPaused); @@ -84,13 +84,15 @@ void SpotifyTrack::trackInformationCallback(std::unique_ptr 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(); diff --git a/components/spotify/cspot_sink.c b/components/spotify/cspot_sink.c index 0df72605..c98634cd 100644 --- a/components/spotify/cspot_sink.c +++ b/components/spotify/cspot_sink.c @@ -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; diff --git a/components/squeezelite/CMakeLists.txt b/components/squeezelite/CMakeLists.txt index d7422b48..5c1f0375 100644 --- a/components/squeezelite/CMakeLists.txt +++ b/components/squeezelite/CMakeLists.txt @@ -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") diff --git a/components/squeezelite/equalizer.c b/components/squeezelite/equalizer.c index ca74b92e..9b083560 100644 --- a/components/squeezelite/equalizer.c +++ b/components/squeezelite/equalizer.c @@ -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; } diff --git a/components/squeezelite/output_i2s.c b/components/squeezelite/output_i2s.c index 2da4d583..5f3a4a2c 100644 --- a/components/squeezelite/output_i2s.c +++ b/components/squeezelite/output_i2s.c @@ -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; diff --git a/components/targets/CMakeLists.txt b/components/targets/CMakeLists.txt new file mode 100644 index 00000000..f8a5b188 --- /dev/null +++ b/components/targets/CMakeLists.txt @@ -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 +) diff --git a/components/targets/init.c b/components/targets/init.c new file mode 100644 index 00000000..f6e42cb8 --- /dev/null +++ b/components/targets/init.c @@ -0,0 +1,3 @@ +// weak should do the job but it does not... + __attribute__((weak)) void target_init(void) { +} diff --git a/components/targets/muse/muse.c b/components/targets/muse/muse.c new file mode 100644 index 00000000..1601d64c --- /dev/null +++ b/components/targets/muse/muse.c @@ -0,0 +1,138 @@ +/* + YOUR LICENSE + */ +#include +#include +#include +#include +#include +#include +//#include +#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 = 0; + S = 0; + for(int i=0;i 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; + } + } + } + diff --git a/components/tools/CMakeLists.txt b/components/tools/CMakeLists.txt index 6a36b20f..c9644f5e 100644 --- a/components/tools/CMakeLists.txt +++ b/components/tools/CMakeLists.txt @@ -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 . ) diff --git a/components/tools/tools.c b/components/tools/tools.c index 4f582b81..9a6af79a 100644 --- a/components/tools/tools.c +++ b/components/tools/tools.c @@ -11,9 +11,14 @@ #include #include #include -#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; +} + diff --git a/components/tools/tools.h b/components/tools/tools.h index 785d22e0..3b29a512 100644 --- a/components/tools/tools.h +++ b/components/tools/tools.h @@ -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 diff --git a/components/wifi-manager/webapp/src/index.ejs b/components/wifi-manager/webapp/src/index.ejs index e6669370..670e332e 100644 --- a/components/wifi-manager/webapp/src/index.ejs +++ b/components/wifi-manager/webapp/src/index.ejs @@ -440,6 +440,7 @@ GPL License.
  • tarablessd1306, © 2017-2018, Tara Keeling. Licensed under the MIT license.
  • +
  • CSpot, © 2020 feelfreelinux & alufers. Licensed under the GPL License
  • diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 11d1029f..e0509541 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -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" ) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index ed29407b..153ceb51 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -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=[,addr=][,base=<100..N>][,count=<0..32>][,intr=][,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=,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" diff --git a/main/esp_app_main.c b/main/esp_app_main.c index b2e9178f..b70c6099 100644 --- a/main/esp_app_main.c +++ b/main/esp_app_main.c @@ -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 );