diff --git a/components/codecs/CMakeLists.txt b/components/codecs/CMakeLists.txt index c38196f2..85438ca9 100644 --- a/components/codecs/CMakeLists.txt +++ b/components/codecs/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register( - INCLUDE_DIRS . ./inc inc/alac inc/FLAC inc/helix-aac inc/mad inc/ogg inc/opus inc/opusfile inc/resample16 inc/soxr inc/vorbis + INCLUDE_DIRS . ./inc inc/alac inc/FLAC inc/helix-aac inc/mad inc/ogg inc/opus inc/opusfile inc/resample16 inc/soxr inc/vorbis ) if (DEFINED AAC_DISABLE_SBR) diff --git a/components/display/SSD1675.c b/components/display/SSD1675.c index 5b421ea0..deaa4145 100644 --- a/components/display/SSD1675.c +++ b/components/display/SSD1675.c @@ -243,7 +243,7 @@ struct GDS_Device* SSD1675_Detect(char *Driver, struct GDS_Device* Device) { char *p; struct PrivateSpace* Private = (struct PrivateSpace*) Device->Private; Private->ReadyPin = -1; - if ((p = strcasestr(Driver, "ready")) != NULL) Private->ReadyPin = atoi(strchr(p, '=') + 1); + if ((p = strcasestr(Driver, "ready")) && (p = strchr(p, '='))) Private->ReadyPin = atoi(p + 1); ESP_LOGI(TAG, "SSD1675 driver with ready GPIO %d", Private->ReadyPin); diff --git a/components/display/ST77xx.c b/components/display/ST77xx.c index 78d59d87..8661feb4 100644 --- a/components/display/ST77xx.c +++ b/components/display/ST77xx.c @@ -199,8 +199,8 @@ static void SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool R WriteByte( Device, Private->MADCtl ); if (Private->Model == ST7789) { - if (Rotate) Private->Offset.Width = HFlip ? 320 - Device->Width : 0; - else Private->Offset.Height = HFlip ? 320 - Device->Height : 0; + if (Rotate) Private->Offset.Width += HFlip ? 320 - Device->Width : 0; + else Private->Offset.Height += HFlip ? 320 - Device->Height : 0; } #ifdef SHADOW_BUFFER @@ -235,7 +235,7 @@ static bool Init( struct GDS_Device* Device ) { Private->iRAM = heap_caps_malloc( (Private->PageSize + 1) * Device->Width * Depth, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); #endif - ESP_LOGI(TAG, "ST77xx with bit depth %u, page %u, iRAM %p", Device->Depth, Private->PageSize, Private->iRAM); + ESP_LOGI(TAG, "ST77xx with bit depth %u, offsets %hu:%hu, page %u, iRAM %p", Device->Depth, Private->Offset.Height, Private->Offset.Width, Private->PageSize, Private->iRAM); // Sleepout + Booster Device->WriteCommand( Device, 0x11 ); @@ -283,8 +283,14 @@ struct GDS_Device* ST77xx_Detect(char *Driver, struct GDS_Device* Device) { *Device = ST77xx; sscanf(Driver, "%*[^:]:%u", &Depth); + struct PrivateSpace* Private = (struct PrivateSpace*) Device->Private; Private->Model = Model; + + if (Model == ST7735) { + sscanf(Driver, "%*[^:]%*[^x]%*[^=]=%hu", &Private->Offset.Height); + sscanf(Driver, "%*[^:]%*[^y]%*[^=]=%hu", &Private->Offset.Width); + } if (Depth == 18) { Device->Mode = GDS_RGB666; diff --git a/components/display/core/gds_font.c b/components/display/core/gds_font.c index e22ebe28..2ea38872 100644 --- a/components/display/core/gds_font.c +++ b/components/display/core/gds_font.c @@ -98,12 +98,14 @@ void GDS_FontDrawChar( struct GDS_Device* Device, char Character, int x, int y, } } -bool GDS_SetFont( struct GDS_Device* Display, const struct GDS_FontDef* Font ) { +const struct GDS_FontDef* GDS_SetFont( struct GDS_Device* Display, const struct GDS_FontDef* Font ) { + const struct GDS_FontDef* OldFont = Display->Font; + Display->FontForceProportional = false; Display->FontForceMonospace = false; Display->Font = Font; - return true; + return OldFont; } void GDS_FontForceProportional( struct GDS_Device* Display, bool Force ) { diff --git a/components/display/core/gds_font.h b/components/display/core/gds_font.h index f80c9825..566f8546 100644 --- a/components/display/core/gds_font.h +++ b/components/display/core/gds_font.h @@ -46,7 +46,7 @@ typedef enum { TextAnchor_Center } TextAnchor; -bool GDS_SetFont( struct GDS_Device* Display, const struct GDS_FontDef* Font ); +const struct GDS_FontDef* GDS_SetFont( struct GDS_Device* Display, const struct GDS_FontDef* Font ); void GDS_FontForceProportional( struct GDS_Device* Display, bool Force ); void GDS_FontForceMonospace( struct GDS_Device* Display, bool Force ); @@ -59,7 +59,8 @@ int GDS_FontGetMaxCharsPerColumn( struct GDS_Device* Display ); int GDS_FontGetCharWidth( struct GDS_Device* Display, char Character ); int GDS_FontGetCharHeight( struct GDS_Device* Display ); -int GDS_FontMeasureString( struct GDS_Device* Display, const char* Text );\ +int GDS_FontMeasureString( struct GDS_Device* Display, const char* Text ); +int GDS_FontMeasureStringLine( struct GDS_Device* Display, int Line, const char* Text ); void GDS_FontDrawChar( struct GDS_Device* Display, char Character, int x, int y, int Color ); void GDS_FontDrawString( struct GDS_Device* Display, int x, int y, const char* Text, int Color ); diff --git a/components/display/core/gds_image.c b/components/display/core/gds_image.c index 3d1fb7ca..cbb392e5 100644 --- a/components/display/core/gds_image.c +++ b/components/display/core/gds_image.c @@ -15,7 +15,7 @@ #include "gds_private.h" #include "gds_image.h" -const char TAG[] = "ImageDec"; +const static char TAG[] = "ImageDec"; #define SCRATCH_SIZE 3100 diff --git a/components/display/core/gds_text.c b/components/display/core/gds_text.c index eab28883..e77999ca 100644 --- a/components/display/core/gds_text.c +++ b/components/display/core/gds_text.c @@ -121,6 +121,19 @@ bool GDS_TextLine(struct GDS_Device* Device, int N, int Pos, int Attr, char *Tex return Width + X < Device->Width; } +/**************************************************************************************** + * + */ +int GDS_GetTextWidth(struct GDS_Device* Device, int N, int Attr, char *Text) { + struct GDS_FontDef *Font = GDS_SetFont( Device, Device->Lines[N-1].Font ); + + if (Attr & GDS_TEXT_MONOSPACE) GDS_FontForceMonospace( Device, true ); + int Width = GDS_FontMeasureString( Device, Text ); + GDS_SetFont( Device, Font ); + + return Width; +} + /**************************************************************************************** * Try to align string for better scrolling visual. there is probably much better to do */ diff --git a/components/display/core/gds_text.h b/components/display/core/gds_text.h index 3b888157..80d1a995 100644 --- a/components/display/core/gds_text.h +++ b/components/display/core/gds_text.h @@ -31,5 +31,6 @@ struct GDS_Device; bool GDS_TextSetFontAuto(struct GDS_Device* Device, int N, int FontType, int Space); bool GDS_TextSetFont(struct GDS_Device* Device, int N, const struct GDS_FontDef *Font, int Space); bool GDS_TextLine(struct GDS_Device* Device, int N, int Pos, int Attr, char *Text); +int GDS_GetTextWidth(struct GDS_Device* Device, int N, int Attr, char *Text); int GDS_TextStretch(struct GDS_Device* Device, int N, char *String, int Max); void GDS_TextPos(struct GDS_Device* Device, int FontType, int Where, int Attr, char *Text, ...); \ No newline at end of file diff --git a/components/display/display.c b/components/display/display.c index 6e0fb338..192c834a 100644 --- a/components/display/display.c +++ b/components/display/display.c @@ -41,7 +41,12 @@ static EXT_RAM_ATTR struct { int offset, boundary; char *metadata_config; bool timer, refresh; - uint32_t elapsed, duration; + uint32_t elapsed; + struct { + uint32_t value; + char string[8]; // H:MM:SS + bool visible; + } duration; TickType_t tick; } displayer; @@ -71,11 +76,11 @@ void display_init(char *welcome) { char *config = config_alloc_get_str("display_config", CONFIG_DISPLAY_CONFIG, "N/A"); int width = -1, height = -1, backlight_pin = -1; - char *p, *drivername = strstr(config, "driver"); + char *drivername = strstr(config, "driver"); - if ((p = strcasestr(config, "width")) != NULL) width = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(config, "height")) != NULL) height = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(config, "back")) != NULL) backlight_pin = atoi(strchr(p, '=') + 1); + PARSE_PARAM(config, "width", '=', width); + PARSE_PARAM(config, "height", '=', height); + PARSE_PARAM(config, "back", '=', backlight_pin); // query drivers to see if we have a match ESP_LOGI(TAG, "Trying to configure display with %s", config); @@ -89,24 +94,24 @@ void display_init(char *welcome) { // so far so good if (display && width > 0 && height > 0) { int RST_pin = -1; - if ((p = strcasestr(config, "reset")) != NULL) RST_pin = atoi(strchr(p, '=') + 1); + PARSE_PARAM(config, "reset", '=', RST_pin); // Detect driver interface - if (strstr(config, "I2C") && i2c_system_port != -1) { + if (strcasestr(config, "I2C") && i2c_system_port != -1) { int address = 0x3C; - if ((p = strcasestr(config, "address")) != NULL) address = atoi(strchr(p, '=') + 1); + PARSE_PARAM(config, "address", '=', address); init = true; GDS_I2CInit( i2c_system_port, -1, -1, i2c_system_speed ) ; GDS_I2CAttachDevice( display, width, height, address, RST_pin, backlight_pin ); ESP_LOGI(TAG, "Display is I2C on port %u", address); - } else if (strstr(config, "SPI") && spi_system_host != -1) { + } else if (strcasestr(config, "SPI") && spi_system_host != -1) { int CS_pin = -1, speed = 0; - if ((p = strcasestr(config, "cs")) != NULL) CS_pin = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(config, "speed")) != NULL) speed = atoi(strchr(p, '=') + 1); + PARSE_PARAM(config, "cs", '=', CS_pin); + PARSE_PARAM(config, "speed", '=', speed); init = true; GDS_SPIInit( spi_system_host, spi_system_dc_gpio ); @@ -126,7 +131,7 @@ void display_init(char *welcome) { static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4))); static EXT_RAM_ATTR StackType_t xStack[DISPLAYER_STACK_SIZE] __attribute__ ((aligned (4))); - GDS_SetLayout( display, strcasestr(config, "HFlip"), strcasestr(config, "VFlip"), strcasestr(config, "rotate")); + GDS_SetLayout(display, strcasestr(config, "HFlip"), strcasestr(config, "VFlip"), strcasestr(config, "rotate")); GDS_SetFont(display, &Font_droid_sans_fallback_15x17 ); GDS_TextPos(display, GDS_FONT_MEDIUM, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, welcome); @@ -193,18 +198,34 @@ static void displayer_task(void *args) { // handler elapsed track time if (displayer.timer && displayer.state == DISPLAYER_ACTIVE) { - char counter[16]; + char line[19] = "-", *_line = line + 1; // [-]H:MM:SS / H:MM:SS TickType_t tick = xTaskGetTickCount(); uint32_t elapsed = (tick - displayer.tick) * portTICK_PERIOD_MS; - + if (elapsed >= 1000) { xSemaphoreTake(displayer.mutex, portMAX_DELAY); displayer.tick = tick; - displayer.elapsed += elapsed / 1000; - xSemaphoreGive(displayer.mutex); - if (displayer.elapsed < 3600) snprintf(counter, 16, "%5u:%02u", displayer.elapsed / 60, displayer.elapsed % 60); - else snprintf(counter, 16, "%2u:%02u:%02u", displayer.elapsed / 3600, (displayer.elapsed % 3600) / 60, displayer.elapsed % 60); - GDS_TextLine(display, 1, GDS_TEXT_RIGHT, (GDS_TEXT_CLEAR | GDS_TEXT_CLEAR_EOL) | GDS_TEXT_UPDATE, counter); + elapsed = displayer.elapsed += elapsed / 1000; + xSemaphoreGive(displayer.mutex); + + // when we have duration but no space, display remaining time + if (displayer.duration.value && !displayer.duration.visible) elapsed = displayer.duration.value - elapsed; + + if (elapsed < 3600) sprintf(_line, "%u:%02u", elapsed / 60, elapsed % 60); + else sprintf(_line, "%u:%02u:%02u", (elapsed / 3600) % 100, (elapsed % 3600) / 60, elapsed % 60); + + // concatenate if we have room for elapsed / duration + if (displayer.duration.visible) { + strcat(_line, "/"); + strcat(_line, displayer.duration.string); + } else if (displayer.duration.value) { + _line--; + } + + // 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); + timer_sleep = 1000; } else timer_sleep = max(1000 - elapsed, 0); } else timer_sleep = DEFAULT_SLEEP; @@ -279,8 +300,8 @@ void displayer_metadata(char *artist, char *album, char *title) { } // get optional scroll speed & pause - if ((p = strcasestr(displayer.metadata_config, "speed")) != NULL) sscanf(p, "%*[^=]=%d", &displayer.speed); - if ((p = strcasestr(displayer.metadata_config, "pause")) != NULL) sscanf(p, "%*[^=]=%d", &displayer.pause); + PARSE_PARAM(displayer.metadata_config, "speed", '=', displayer.speed); + PARSE_PARAM(displayer.metadata_config, "pause", '=', displayer.pause); displayer.offset = 0; utf8_decode(displayer.string); @@ -318,9 +339,27 @@ void displayer_timer(enum displayer_time_e mode, int elapsed, int duration) { xSemaphoreTake(displayer.mutex, portMAX_DELAY); - if (elapsed >= 0) displayer.elapsed = elapsed / 1000; - if (duration >= 0) displayer.duration = duration / 1000; if (displayer.timer) displayer.tick = xTaskGetTickCount(); + if (elapsed >= 0) displayer.elapsed = elapsed / 1000; + if (duration > 0) { + displayer.duration.visible = true; + displayer.duration.value = duration / 1000; + + if (displayer.duration.value > 3600) sprintf(displayer.duration.string, "%u:%02u:%02u", (displayer.duration.value / 3600) % 10, + (displayer.duration.value % 3600) / 60, displayer.duration.value % 60); + else sprintf(displayer.duration.string, "%u:%02u", displayer.duration.value / 60, displayer.duration.value % 60); + + char *buf; + asprintf(&buf, "%s %s/%s", displayer.header, displayer.duration.string, displayer.duration.string); + if (GDS_GetTextWidth(display, 1, 0, buf) > GDS_GetWidth(display)) { + ESP_LOGW(TAG, "Can't fit duration %s (%d) on screen using elapsed only", buf, GDS_GetTextWidth(display, 1, 0, buf)); + displayer.duration.visible = false; + } + free(buf); + } else if (!duration) { + displayer.duration.visible = false; + displayer.duration.value = 0; + } xSemaphoreGive(displayer.mutex); } @@ -345,7 +384,8 @@ void displayer_control(enum displayer_cmd_e cmd, ...) { displayer.timer = false; displayer.refresh = true; displayer.string[0] = '\0'; - displayer.elapsed = displayer.duration = 0; + displayer.elapsed = displayer.duration.value = 0; + displayer.duration.visible = false; displayer.offset = displayer.boundary = 0; display_bus(&displayer, DISPLAY_BUS_TAKE); vTaskResume(displayer.task); diff --git a/components/display/fonts/font_line_1.c b/components/display/fonts/font_line_1.c index 1047bcdf..7f967418 100644 --- a/components/display/fonts/font_line_1.c +++ b/components/display/fonts/font_line_1.c @@ -26,7 +26,7 @@ static const uint8_t Square721_BT11x14[] = { 0x02, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char , 0x04, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char - 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char . - 0x04, 0x00, 0x0C, 0x80, 0x03, 0x70, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char / + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x80, 0x03, 0x70, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char / 0x08, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x03, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0 0x08, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x10, 0x04, 0x08, 0x04, 0xF8, 0x07, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 1 0x08, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0x08, 0x05, 0x88, 0x04, 0x88, 0x04, 0x88, 0x04, 0x70, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 2 diff --git a/components/driver_bt/bt_app_core.c b/components/driver_bt/bt_app_core.c index e35a8337..2a297ea3 100644 --- a/components/driver_bt/bt_app_core.c +++ b/components/driver_bt/bt_app_core.c @@ -17,7 +17,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/queue.h" #include "freertos/task.h" -#include "globdefs.h" +#include "tools.h" static const char * TAG = "btappcore"; diff --git a/components/driver_bt/bt_app_sink.c b/components/driver_bt/bt_app_sink.c index c175ccf2..ed88636b 100644 --- a/components/driver_bt/bt_app_sink.c +++ b/components/driver_bt/bt_app_sink.c @@ -25,7 +25,7 @@ #include "platform_config.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "trace.h" +#include "tools.h" #include "audio_controls.h" #include "sys/lock.h" #include "display.h" diff --git a/components/driver_bt/bt_app_source.c b/components/driver_bt/bt_app_source.c index 246085fd..ed357335 100644 --- a/components/driver_bt/bt_app_source.c +++ b/components/driver_bt/bt_app_source.c @@ -17,10 +17,9 @@ #include "freertos/timers.h" #include "argtable3/argtable3.h" #include "platform_config.h" -#include "trace.h" #include "messaging.h" #include "cJSON.h" -#include "globdefs.h" +#include "tools.h" static const char * TAG = "bt_app_source"; static const char * BT_RC_CT_TAG="RCCT"; diff --git a/components/esp_http_server/CMakeLists.txt b/components/esp_http_server/CMakeLists.txt new file mode 100644 index 00000000..501b00b9 --- /dev/null +++ b/components/esp_http_server/CMakeLists.txt @@ -0,0 +1,10 @@ +idf_build_get_property(prefix IDF_PATH) +string(CONCAT prefix "${prefix}" "/components/esp_http_server") + +idf_component_register( + SRC_DIRS "${prefix}/src" "${prefix}/src/util" + INCLUDE_DIRS "${prefix}/include" + PRIV_INCLUDE_DIRS "." "${prefix}/src/port/esp32" "${prefix}/src/util" + REQUIRES nghttp # for http_parser.h + PRIV_REQUIRES lwip mbedtls esp_timer +) diff --git a/components/esp_http_server/Kconfig b/components/esp_http_server/Kconfig new file mode 100644 index 00000000..0724bf6c --- /dev/null +++ b/components/esp_http_server/Kconfig @@ -0,0 +1 @@ +source "$IDF_PATH/components/esp_http_server/Kconfig" diff --git a/components/esp_http_server/osal.h b/components/esp_http_server/osal.h new file mode 100644 index 00000000..81281a9c --- /dev/null +++ b/components/esp_http_server/osal.h @@ -0,0 +1,68 @@ +// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _OSAL_H_ +#define _OSAL_H_ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define OS_SUCCESS ESP_OK +#define OS_FAIL ESP_FAIL + +typedef TaskHandle_t othread_t; + +static inline int httpd_os_thread_create(othread_t *thread, + const char *name, uint16_t stacksize, int prio, + void (*thread_routine)(void *arg), void *arg, + BaseType_t core_id) +{ + StaticTask_t *xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)); + StackType_t *xStack = heap_caps_malloc(stacksize,(MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT)); + + *thread = xTaskCreateStaticPinnedToCore(thread_routine, name, stacksize, arg, prio, xStack,xTaskBuffer,core_id); + if (*thread) { + return OS_SUCCESS; + } + return OS_FAIL; +} + +/* Only self delete is supported */ +static inline void httpd_os_thread_delete(void) +{ + vTaskDelete(xTaskGetCurrentTaskHandle()); +} + +static inline void httpd_os_thread_sleep(int msecs) +{ + vTaskDelay(msecs / portTICK_RATE_MS); +} + +static inline othread_t httpd_os_thread_handle(void) +{ + return xTaskGetCurrentTaskHandle(); +} + +#ifdef __cplusplus +} +#endif + +#endif /* ! _OSAL_H_ */ diff --git a/components/heap/CMakeLists.txt b/components/heap/CMakeLists.txt new file mode 100644 index 00000000..0a77108f --- /dev/null +++ b/components/heap/CMakeLists.txt @@ -0,0 +1,49 @@ +set(srcs + "heap_caps.c" + "heap_caps_init.c" + "multi_heap.c" + "heap_tlsf.c") + +if(NOT CONFIG_HEAP_POISONING_DISABLED) + list(APPEND srcs "multi_heap_poisoning.c") +endif() + +if(CONFIG_HEAP_TASK_TRACKING) + list(APPEND srcs "heap_task_info.c") +endif() + +if(CONFIG_HEAP_TRACING_STANDALONE) + list(APPEND srcs "heap_trace_standalone.c") + set_source_files_properties(heap_trace_standalone.c + PROPERTIES COMPILE_FLAGS + -Wno-frame-address) +endif() + +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS include + LDFRAGMENTS linker.lf + PRIV_REQUIRES soc) + +if(CONFIG_HEAP_TRACING) + set(WRAP_FUNCTIONS + calloc + malloc + free + realloc + heap_caps_malloc + heap_caps_free + heap_caps_realloc + heap_caps_malloc_default + heap_caps_realloc_default) + + foreach(wrap ${WRAP_FUNCTIONS}) + target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=${wrap}") + endforeach() +endif() + +if(NOT CMAKE_BUILD_EARLY_EXPANSION) + idf_build_get_property(build_components BUILD_COMPONENTS) + if(freertos IN_LIST build_components) + target_compile_options(${COMPONENT_TARGET} PRIVATE "-DMULTI_HEAP_FREERTOS") + endif() +endif() diff --git a/components/heap/Kconfig b/components/heap/Kconfig new file mode 100644 index 00000000..5b9dc561 --- /dev/null +++ b/components/heap/Kconfig @@ -0,0 +1,74 @@ +menu "Heap memory debugging" + + choice HEAP_CORRUPTION_DETECTION + prompt "Heap corruption detection" + default HEAP_POISONING_DISABLED + help + Enable heap poisoning features to detect heap corruption caused by out-of-bounds access to heap memory. + + See the "Heap Memory Debugging" page of the IDF documentation + for a description of each level of heap corruption detection. + + config HEAP_POISONING_DISABLED + bool "Basic (no poisoning)" + config HEAP_POISONING_LIGHT + bool "Light impact" + config HEAP_POISONING_COMPREHENSIVE + bool "Comprehensive" + endchoice + + choice HEAP_TRACING_DEST + bool "Heap tracing" + default HEAP_TRACING_OFF + help + Enables the heap tracing API defined in esp_heap_trace.h. + + This function causes a moderate increase in IRAM code side and a minor increase in heap function + (malloc/free/realloc) CPU overhead, even when the tracing feature is not used. + So it's best to keep it disabled unless tracing is being used. + + config HEAP_TRACING_OFF + bool "Disabled" + config HEAP_TRACING_STANDALONE + bool "Standalone" + select HEAP_TRACING + config HEAP_TRACING_TOHOST + bool "Host-based" + select HEAP_TRACING + endchoice + + config HEAP_TRACING + bool + default F + help + Enables/disables heap tracing API. + + config HEAP_TRACING_STACK_DEPTH + int "Heap tracing stack depth" + range 0 0 if IDF_TARGET_ARCH_RISCV # Disabled for RISC-V due to `__builtin_return_address` limitation + default 0 if IDF_TARGET_ARCH_RISCV + range 0 10 + default 2 + depends on HEAP_TRACING + help + Number of stack frames to save when tracing heap operation callers. + + More stack frames uses more memory in the heap trace buffer (and slows down allocation), but + can provide useful information. + + config HEAP_TASK_TRACKING + bool "Enable heap task tracking" + depends on !HEAP_POISONING_DISABLED + help + Enables tracking the task responsible for each heap allocation. + + This function depends on heap poisoning being enabled and adds four more bytes of overhead for each block + allocated. + + config HEAP_ABORT_WHEN_ALLOCATION_FAILS + bool "Abort if memory allocation fails" + default n + help + When enabled, if a memory allocation operation fails it will cause a system abort. + +endmenu diff --git a/components/heap/component.mk b/components/heap/component.mk new file mode 100644 index 00000000..5c8e7bcf --- /dev/null +++ b/components/heap/component.mk @@ -0,0 +1,32 @@ +# +# Component Makefile +# + +COMPONENT_OBJS := heap_caps_init.o heap_caps.o multi_heap.o heap_tlsf.o + +ifndef CONFIG_HEAP_POISONING_DISABLED +COMPONENT_OBJS += multi_heap_poisoning.o + +ifdef CONFIG_HEAP_TASK_TRACKING +COMPONENT_OBJS += heap_task_info.o +endif +endif + +ifdef CONFIG_HEAP_TRACING_STANDALONE + +COMPONENT_OBJS += heap_trace_standalone.o + +endif + +ifdef CONFIG_HEAP_TRACING + +WRAP_FUNCTIONS = calloc malloc free realloc heap_caps_malloc heap_caps_free heap_caps_realloc heap_caps_malloc_default heap_caps_realloc_default +WRAP_ARGUMENT := -Wl,--wrap= + +COMPONENT_ADD_LDFLAGS = -l$(COMPONENT_NAME) $(addprefix $(WRAP_ARGUMENT),$(WRAP_FUNCTIONS)) + +endif + +COMPONENT_ADD_LDFRAGMENTS += linker.lf + +CFLAGS += -DMULTI_HEAP_FREERTOS diff --git a/components/heap/heap_caps.c b/components/heap/heap_caps.c new file mode 100644 index 00000000..07cc258e --- /dev/null +++ b/components/heap/heap_caps.c @@ -0,0 +1,609 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include +#include +#include +#include "esp_attr.h" +#include "esp_heap_caps.h" +#include "multi_heap.h" +#include "esp_log.h" +#include "heap_private.h" +#include "esp_system.h" + +/* +This file, combined with a region allocator that supports multiple heaps, solves the problem that the ESP32 has RAM +that's slightly heterogeneous. Some RAM can be byte-accessed, some allows only 32-bit accesses, some can execute memory, +some can be remapped by the MMU to only be accessed by a certain PID etc. In order to allow the most flexible memory +allocation possible, this code makes it possible to request memory that has certain capabilities. The code will then use +its knowledge of how the memory is configured along with a priority scheme to allocate that memory in the most sane way +possible. This should optimize the amount of RAM accessible to the code without hardwiring addresses. +*/ + +static esp_alloc_failed_hook_t alloc_failed_callback; + +/* + This takes a memory chunk in a region that can be addressed as both DRAM as well as IRAM. It will convert it to + IRAM in such a way that it can be later freed. It assumes both the address as well as the length to be word-aligned. + It returns a region that's 1 word smaller than the region given because it stores the original Dram address there. +*/ +IRAM_ATTR static void *dram_alloc_to_iram_addr(void *addr, size_t len) +{ + uintptr_t dstart = (uintptr_t)addr; //First word + uintptr_t dend = dstart + len - 4; //Last word + assert(esp_ptr_in_diram_dram((void *)dstart)); + assert(esp_ptr_in_diram_dram((void *)dend)); + assert((dstart & 3) == 0); + assert((dend & 3) == 0); +#if SOC_DIRAM_INVERTED // We want the word before the result to hold the DRAM address + uint32_t *iptr = esp_ptr_diram_dram_to_iram((void *)dend); +#else + uint32_t *iptr = esp_ptr_diram_dram_to_iram((void *)dstart); +#endif + *iptr = dstart; + return iptr + 1; +} + + +static void heap_caps_alloc_failed(size_t requested_size, uint32_t caps, const char *function_name) +{ + if (alloc_failed_callback) { + alloc_failed_callback(requested_size, caps, function_name); + } + + #ifdef CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS + esp_system_abort("Memory allocation failed"); + #endif +} + +esp_err_t heap_caps_register_failed_alloc_callback(esp_alloc_failed_hook_t callback) +{ + if (callback == NULL) { + return ESP_ERR_INVALID_ARG; + } + + alloc_failed_callback = callback; + + return ESP_OK; +} + +bool heap_caps_match(const heap_t *heap, uint32_t caps) +{ + return heap->heap != NULL && ((get_all_caps(heap) & caps) == caps); +} + +/* +Routine to allocate a bit of memory with certain capabilities. caps is a bitfield of MALLOC_CAP_* bits. +*/ +IRAM_ATTR void *heap_caps_malloc( size_t size, uint32_t caps ) +{ + void *ret = NULL; + + if (size > HEAP_SIZE_MAX) { + // Avoids int overflow when adding small numbers to size, or + // calculating 'end' from start+size, by limiting 'size' to the possible range + heap_caps_alloc_failed(size, caps, __func__); + + return NULL; + } + + if (caps & MALLOC_CAP_EXEC) { + //MALLOC_CAP_EXEC forces an alloc from IRAM. There is a region which has both this as well as the following + //caps, but the following caps are not possible for IRAM. Thus, the combination is impossible and we return + //NULL directly, even although our heap capabilities (based on soc_memory_tags & soc_memory_regions) would + //indicate there is a tag for this. + if ((caps & MALLOC_CAP_8BIT) || (caps & MALLOC_CAP_DMA)) { + heap_caps_alloc_failed(size, caps, __func__); + + return NULL; + } + caps |= MALLOC_CAP_32BIT; // IRAM is 32-bit accessible RAM + } + + if (caps & MALLOC_CAP_32BIT) { + /* 32-bit accessible RAM should allocated in 4 byte aligned sizes + * (Future versions of ESP-IDF should possibly fail if an invalid size is requested) + */ + size = (size + 3) & (~3); // int overflow checked above + } + + for (int prio = 0; prio < SOC_MEMORY_TYPE_NO_PRIOS; prio++) { + //Iterate over heaps and check capabilities at this priority + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap->heap == NULL) { + continue; + } + if ((heap->caps[prio] & caps) != 0) { + //Heap has at least one of the caps requested. If caps has other bits set that this prio + //doesn't cover, see if they're available in other prios. + if ((get_all_caps(heap) & caps) == caps) { + //This heap can satisfy all the requested capabilities. See if we can grab some memory using it. + if ((caps & MALLOC_CAP_EXEC) && esp_ptr_in_diram_dram((void *)heap->start)) { + //This is special, insofar that what we're going to get back is a DRAM address. If so, + //we need to 'invert' it (lowest address in DRAM == highest address in IRAM and vice-versa) and + //add a pointer to the DRAM equivalent before the address we're going to return. + ret = multi_heap_malloc(heap->heap, size + 4); // int overflow checked above + + if (ret != NULL) { + return dram_alloc_to_iram_addr(ret, size + 4); // int overflow checked above + } + } else { + //Just try to alloc, nothing special. + ret = multi_heap_malloc(heap->heap, size); + if (ret != NULL) { + return ret; + } + } + } + } + } + } + + heap_caps_alloc_failed(size, caps, __func__); + + //Nothing usable found. + return NULL; +} + + +#define MALLOC_DISABLE_EXTERNAL_ALLOCS -1 +//Dual-use: -1 (=MALLOC_DISABLE_EXTERNAL_ALLOCS) disables allocations in external memory, >=0 sets the limit for allocations preferring internal memory. +static int malloc_alwaysinternal_limit=MALLOC_DISABLE_EXTERNAL_ALLOCS; + +void heap_caps_malloc_extmem_enable(size_t limit) +{ + malloc_alwaysinternal_limit=limit; +} + +/* + Default memory allocation implementation. Should return standard 8-bit memory. malloc() essentially resolves to this function. +*/ +IRAM_ATTR void *heap_caps_malloc_default( size_t size ) +{ + if (malloc_alwaysinternal_limit==MALLOC_DISABLE_EXTERNAL_ALLOCS) { + return heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL); + } else { + void *r; + if (size <= (size_t)malloc_alwaysinternal_limit) { + r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL ); + } else { + r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM ); + } + if (r==NULL) { + //try again while being less picky + r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT ); + } + return r; + } +} + +/* + Same for realloc() + Note: keep the logic in here the same as in heap_caps_malloc_default (or merge the two as soon as this gets more complex...) + */ +IRAM_ATTR void *heap_caps_realloc_default( void *ptr, size_t size ) +{ + if (malloc_alwaysinternal_limit==MALLOC_DISABLE_EXTERNAL_ALLOCS) { + return heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL ); + } else { + void *r; + if (size <= (size_t)malloc_alwaysinternal_limit) { + r=heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL ); + } else { + r=heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM ); + } + if (r==NULL && size>0) { + //We needed to allocate memory, but we didn't. Try again while being less picky. + r=heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT ); + } + return r; + } +} + +/* + Memory allocation as preference in decreasing order. + */ +IRAM_ATTR void *heap_caps_malloc_prefer( size_t size, size_t num, ... ) +{ + va_list argp; + va_start( argp, num ); + void *r = NULL; + while (num--) { + uint32_t caps = va_arg( argp, uint32_t ); + r = heap_caps_malloc( size, caps ); + if (r != NULL) { + break; + } + } + va_end( argp ); + return r; +} + +/* + Memory reallocation as preference in decreasing order. + */ +IRAM_ATTR void *heap_caps_realloc_prefer( void *ptr, size_t size, size_t num, ... ) +{ + va_list argp; + va_start( argp, num ); + void *r = NULL; + while (num--) { + uint32_t caps = va_arg( argp, uint32_t ); + r = heap_caps_realloc( ptr, size, caps ); + if (r != NULL || size == 0) { + break; + } + } + va_end( argp ); + return r; +} + +/* + Memory callocation as preference in decreasing order. + */ +IRAM_ATTR void *heap_caps_calloc_prefer( size_t n, size_t size, size_t num, ... ) +{ + va_list argp; + va_start( argp, num ); + void *r = NULL; + while (num--) { + uint32_t caps = va_arg( argp, uint32_t ); + r = heap_caps_calloc( n, size, caps ); + if (r != NULL) break; + } + va_end( argp ); + return r; +} + +/* Find the heap which belongs to ptr, or return NULL if it's + not in any heap. + + (This confirms if ptr is inside the heap's region, doesn't confirm if 'ptr' + is an allocated block or is some other random address inside the heap.) +*/ +IRAM_ATTR static heap_t *find_containing_heap(void *ptr ) +{ + intptr_t p = (intptr_t)ptr; + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap->heap != NULL && p >= heap->start && p < heap->end) { + return heap; + } + } + return NULL; +} + +IRAM_ATTR void heap_caps_free( void *ptr) +{ + if (ptr == NULL) { + return; + } + + if (esp_ptr_in_diram_iram(ptr)) { + //Memory allocated here is actually allocated in the DRAM alias region and + //cannot be de-allocated as usual. dram_alloc_to_iram_addr stores a pointer to + //the equivalent DRAM address, though; free that. + uint32_t *dramAddrPtr = (uint32_t *)ptr; + ptr = (void *)dramAddrPtr[-1]; + } + + heap_t *heap = find_containing_heap(ptr); + assert(heap != NULL && "free() target pointer is outside heap areas"); + multi_heap_free(heap->heap, ptr); +} + +IRAM_ATTR void *heap_caps_realloc( void *ptr, size_t size, uint32_t caps) +{ + bool ptr_in_diram_case = false; + heap_t *heap = NULL; + void *dram_ptr = NULL; + + if (ptr == NULL) { + return heap_caps_malloc(size, caps); + } + + if (size == 0) { + heap_caps_free(ptr); + return NULL; + } + + if (size > HEAP_SIZE_MAX) { + heap_caps_alloc_failed(size, caps, __func__); + + return NULL; + } + + //The pointer to memory may be aliased, we need to + //recover the corresponding address before to manage a new allocation: + if(esp_ptr_in_diram_iram((void *)ptr)) { + uint32_t *dram_addr = (uint32_t *)ptr; + dram_ptr = (void *)dram_addr[-1]; + + heap = find_containing_heap(dram_ptr); + assert(heap != NULL && "realloc() pointer is outside heap areas"); + + //with pointers that reside on diram space, we avoid using + //the realloc implementation due to address translation issues, + //instead force a malloc/copy/free + ptr_in_diram_case = true; + + } else { + heap = find_containing_heap(ptr); + assert(heap != NULL && "realloc() pointer is outside heap areas"); + } + + // are the existing heap's capabilities compatible with the + // requested ones? + bool compatible_caps = (caps & get_all_caps(heap)) == caps; + + if (compatible_caps && !ptr_in_diram_case) { + // try to reallocate this memory within the same heap + // (which will resize the block if it can) + void *r = multi_heap_realloc(heap->heap, ptr, size); + if (r != NULL) { + return r; + } + } + + // if we couldn't do that, try to see if we can reallocate + // in a different heap with requested capabilities. + void *new_p = heap_caps_malloc(size, caps); + if (new_p != NULL) { + size_t old_size = 0; + + //If we're dealing with aliased ptr, information regarding its containing + //heap can only be obtained with translated address. + if(ptr_in_diram_case) { + old_size = multi_heap_get_allocated_size(heap->heap, dram_ptr); + } else { + old_size = multi_heap_get_allocated_size(heap->heap, ptr); + } + + assert(old_size > 0); + memcpy(new_p, ptr, MIN(size, old_size)); + heap_caps_free(ptr); + return new_p; + } + + heap_caps_alloc_failed(size, caps, __func__); + + return NULL; +} + +IRAM_ATTR void *heap_caps_calloc( size_t n, size_t size, uint32_t caps) +{ + void *result; + size_t size_bytes; + + if (__builtin_mul_overflow(n, size, &size_bytes)) { + return NULL; + } + + result = heap_caps_malloc(size_bytes, caps); + if (result != NULL) { + bzero(result, size_bytes); + } + return result; +} + +size_t heap_caps_get_total_size(uint32_t caps) +{ + size_t total_size = 0; + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap_caps_match(heap, caps)) { + total_size += (heap->end - heap->start); + } + } + return total_size; +} + +size_t heap_caps_get_free_size( uint32_t caps ) +{ + size_t ret = 0; + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap_caps_match(heap, caps)) { + ret += multi_heap_free_size(heap->heap); + } + } + return ret; +} + +size_t heap_caps_get_minimum_free_size( uint32_t caps ) +{ + size_t ret = 0; + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap_caps_match(heap, caps)) { + ret += multi_heap_minimum_free_size(heap->heap); + } + } + return ret; +} + +size_t heap_caps_get_largest_free_block( uint32_t caps ) +{ + multi_heap_info_t info; + heap_caps_get_info(&info, caps); + return info.largest_free_block; +} + +void heap_caps_get_info( multi_heap_info_t *info, uint32_t caps ) +{ + bzero(info, sizeof(multi_heap_info_t)); + + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap_caps_match(heap, caps)) { + multi_heap_info_t hinfo; + multi_heap_get_info(heap->heap, &hinfo); + + info->total_free_bytes += hinfo.total_free_bytes; + info->total_allocated_bytes += hinfo.total_allocated_bytes; + info->largest_free_block = MAX(info->largest_free_block, + hinfo.largest_free_block); + info->minimum_free_bytes += hinfo.minimum_free_bytes; + info->allocated_blocks += hinfo.allocated_blocks; + info->free_blocks += hinfo.free_blocks; + info->total_blocks += hinfo.total_blocks; + } + } +} + +void heap_caps_print_heap_info( uint32_t caps ) +{ + multi_heap_info_t info; + printf("Heap summary for capabilities 0x%08X:\n", caps); + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap_caps_match(heap, caps)) { + multi_heap_get_info(heap->heap, &info); + + printf(" At 0x%08x len %d free %d allocated %d min_free %d\n", + heap->start, heap->end - heap->start, info.total_free_bytes, info.total_allocated_bytes, info.minimum_free_bytes); + printf(" largest_free_block %d alloc_blocks %d free_blocks %d total_blocks %d\n", + info.largest_free_block, info.allocated_blocks, + info.free_blocks, info.total_blocks); + } + } + printf(" Totals:\n"); + heap_caps_get_info(&info, caps); + + printf(" free %d allocated %d min_free %d largest_free_block %d\n", info.total_free_bytes, info.total_allocated_bytes, info.minimum_free_bytes, info.largest_free_block); +} + +bool heap_caps_check_integrity(uint32_t caps, bool print_errors) +{ + bool all_heaps = caps & MALLOC_CAP_INVALID; + bool valid = true; + + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap->heap != NULL + && (all_heaps || (get_all_caps(heap) & caps) == caps)) { + valid = multi_heap_check(heap->heap, print_errors) && valid; + } + } + + return valid; +} + +bool heap_caps_check_integrity_all(bool print_errors) +{ + return heap_caps_check_integrity(MALLOC_CAP_INVALID, print_errors); +} + +bool heap_caps_check_integrity_addr(intptr_t addr, bool print_errors) +{ + heap_t *heap = find_containing_heap((void *)addr); + if (heap == NULL) { + return false; + } + return multi_heap_check(heap->heap, print_errors); +} + +void heap_caps_dump(uint32_t caps) +{ + bool all_heaps = caps & MALLOC_CAP_INVALID; + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap->heap != NULL + && (all_heaps || (get_all_caps(heap) & caps) == caps)) { + multi_heap_dump(heap->heap); + } + } +} + +void heap_caps_dump_all(void) +{ + heap_caps_dump(MALLOC_CAP_INVALID); +} + +size_t heap_caps_get_allocated_size( void *ptr ) +{ + heap_t *heap = find_containing_heap(ptr); + size_t size = multi_heap_get_allocated_size(heap->heap, ptr); + return size; +} + +IRAM_ATTR void *heap_caps_aligned_alloc(size_t alignment, size_t size, uint32_t caps) +{ + void *ret = NULL; + + if(!alignment) { + return NULL; + } + + //Alignment must be a power of two: + if((alignment & (alignment - 1)) != 0) { + return NULL; + } + + if (size > HEAP_SIZE_MAX) { + // Avoids int overflow when adding small numbers to size, or + // calculating 'end' from start+size, by limiting 'size' to the possible range + heap_caps_alloc_failed(size, caps, __func__); + + return NULL; + } + + for (int prio = 0; prio < SOC_MEMORY_TYPE_NO_PRIOS; prio++) { + //Iterate over heaps and check capabilities at this priority + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap->heap == NULL) { + continue; + } + if ((heap->caps[prio] & caps) != 0) { + //Heap has at least one of the caps requested. If caps has other bits set that this prio + //doesn't cover, see if they're available in other prios. + if ((get_all_caps(heap) & caps) == caps) { + //Just try to alloc, nothing special. + ret = multi_heap_aligned_alloc(heap->heap, size, alignment); + if (ret != NULL) { + return ret; + } + } + } + } + } + + heap_caps_alloc_failed(size, caps, __func__); + + //Nothing usable found. + return NULL; +} + +IRAM_ATTR void heap_caps_aligned_free(void *ptr) +{ + heap_caps_free(ptr); +} + +void *heap_caps_aligned_calloc(size_t alignment, size_t n, size_t size, uint32_t caps) +{ + size_t size_bytes; + if (__builtin_mul_overflow(n, size, &size_bytes)) { + return NULL; + } + + void *ptr = heap_caps_aligned_alloc(alignment,size_bytes, caps); + if(ptr != NULL) { + memset(ptr, 0, size_bytes); + } + + return ptr; +} diff --git a/components/heap/heap_caps_init.c b/components/heap/heap_caps_init.c new file mode 100644 index 00000000..26c819de --- /dev/null +++ b/components/heap/heap_caps_init.c @@ -0,0 +1,241 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "heap_private.h" +#include +#include +#include + +#include "esp_log.h" +#include "multi_heap.h" +#include "multi_heap_platform.h" +#include "esp_heap_caps_init.h" +#include "soc/soc_memory_layout.h" + +static const char *TAG = "heap_init"; + +/* Linked-list of registered heaps */ +struct registered_heap_ll registered_heaps; + +static void register_heap(heap_t *region) +{ + size_t heap_size = region->end - region->start; + assert(heap_size <= HEAP_SIZE_MAX); + region->heap = multi_heap_register((void *)region->start, heap_size); + if (region->heap != NULL) { + ESP_EARLY_LOGD(TAG, "New heap initialised at %p", region->heap); + } +} + +void heap_caps_enable_nonos_stack_heaps(void) +{ + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + // Assume any not-yet-registered heap is + // a nonos-stack heap + if (heap->heap == NULL) { + register_heap(heap); + if (heap->heap != NULL) { + multi_heap_set_lock(heap->heap, &heap->heap_mux); + } + } + } +} + +/* Initialize the heap allocator to use all of the memory not + used by static data or reserved for other purposes + */ +void heap_caps_init(void) +{ + /* Get the array of regions that we can use for heaps + (with reserved memory removed already.) + */ + size_t num_regions = soc_get_available_memory_region_max_count(); + soc_memory_region_t regions[num_regions]; + num_regions = soc_get_available_memory_regions(regions); + + //The heap allocator will treat every region given to it as separate. In order to get bigger ranges of contiguous memory, + //it's useful to coalesce adjacent regions that have the same type. + for (size_t i = 1; i < num_regions; i++) { + soc_memory_region_t *a = ®ions[i - 1]; + soc_memory_region_t *b = ®ions[i]; + if (b->start == (intptr_t)(a->start + a->size) && b->type == a->type ) { + a->type = -1; + b->start = a->start; + b->size += a->size; + } + } + + /* Count the heaps left after merging */ + size_t num_heaps = 0; + for (size_t i = 0; i < num_regions; i++) { + if (regions[i].type != -1) { + num_heaps++; + } + } + + /* Start by allocating the registered heap data on the stack. + + Once we have a heap to copy it to, we will copy it to a heap buffer. + */ + heap_t temp_heaps[num_heaps]; + size_t heap_idx = 0; + + ESP_EARLY_LOGI(TAG, "Initializing. RAM available for dynamic allocation:"); + for (size_t i = 0; i < num_regions; i++) { + soc_memory_region_t *region = ®ions[i]; + const soc_memory_type_desc_t *type = &soc_memory_types[region->type]; + heap_t *heap = &temp_heaps[heap_idx]; + if (region->type == -1) { + continue; + } + heap_idx++; + assert(heap_idx <= num_heaps); + + memcpy(heap->caps, type->caps, sizeof(heap->caps)); + heap->start = region->start; + heap->end = region->start + region->size; + MULTI_HEAP_LOCK_INIT(&heap->heap_mux); + if (type->startup_stack) { + /* Will be registered when OS scheduler starts */ + heap->heap = NULL; + } else { + register_heap(heap); + } + SLIST_NEXT(heap, next) = NULL; + + ESP_EARLY_LOGI(TAG, "At %08X len %08X (%d KiB): %s", + region->start, region->size, region->size / 1024, type->name); + } + + assert(heap_idx == num_heaps); + + /* Allocate the permanent heap data that we'll use as a linked list at runtime. + + Allocate this part of data contiguously, even though it's a linked list... */ + assert(SLIST_EMPTY(®istered_heaps)); + + heap_t *heaps_array = NULL; + for (size_t i = 0; i < num_heaps; i++) { + if (heap_caps_match(&temp_heaps[i], MALLOC_CAP_8BIT|MALLOC_CAP_INTERNAL)) { + /* use the first DRAM heap which can fit the data */ + heaps_array = multi_heap_malloc(temp_heaps[i].heap, sizeof(heap_t) * num_heaps); + if (heaps_array != NULL) { + break; + } + } + } + assert(heaps_array != NULL); /* if NULL, there's not enough free startup heap space */ + + memcpy(heaps_array, temp_heaps, sizeof(heap_t)*num_heaps); + + /* Iterate the heaps and set their locks, also add them to the linked list. */ + for (size_t i = 0; i < num_heaps; i++) { + if (heaps_array[i].heap != NULL) { + multi_heap_set_lock(heaps_array[i].heap, &heaps_array[i].heap_mux); + } + if (i == 0) { + SLIST_INSERT_HEAD(®istered_heaps, &heaps_array[0], next); + } else { + SLIST_INSERT_AFTER(&heaps_array[i-1], &heaps_array[i], next); + } + } +} + +esp_err_t heap_caps_add_region(intptr_t start, intptr_t end) +{ + if (start == 0) { + return ESP_ERR_INVALID_ARG; + } + + for (size_t i = 0; i < soc_memory_region_count; i++) { + const soc_memory_region_t *region = &soc_memory_regions[i]; + // Test requested start only as 'end' may be in a different region entry, assume 'end' has same caps + if (region->start <= start && (intptr_t)(region->start + region->size) > start) { + const uint32_t *caps = soc_memory_types[region->type].caps; + return heap_caps_add_region_with_caps(caps, start, end); + } + } + + return ESP_ERR_NOT_FOUND; +} + +esp_err_t heap_caps_add_region_with_caps(const uint32_t caps[], intptr_t start, intptr_t end) +{ + esp_err_t err = ESP_FAIL; + if (caps == NULL || start == 0 || end == 0 || end <= start) { + return ESP_ERR_INVALID_ARG; + } + + //Check if region overlaps the start and/or end of an existing region. If so, the + //region is invalid (or maybe added twice) + /* + * assume that in on region, start must be less than end (cannot equal to) !! + * Specially, the 4th scenario can be allowed. For example, allocate memory from heap, + * then change the capability and call this function to create a new region for special + * application. + * In the following chart, 'start = start' and 'end = end' is contained in 3rd scenario. + * This all equal scenario is incorrect because the same region cannot be add twice. For example, + * add the .bss memory to region twice, if not do the check, it will cause exception. + * + * the existing heap region s(tart) e(nd) + * |----------------------| + * 1.add region [Correct] (s1start && end > heap->start) + || (start < heap->end && end > heap->end)) { + return ESP_FAIL; + } + } + + heap_t *p_new = heap_caps_malloc(sizeof(heap_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT); + if (p_new == NULL) { + err = ESP_ERR_NO_MEM; + goto done; + } + memcpy(p_new->caps, caps, sizeof(p_new->caps)); + p_new->start = start; + p_new->end = end; + MULTI_HEAP_LOCK_INIT(&p_new->heap_mux); + p_new->heap = multi_heap_register((void *)start, end - start); + SLIST_NEXT(p_new, next) = NULL; + if (p_new->heap == NULL) { + err = ESP_ERR_INVALID_SIZE; + goto done; + } + multi_heap_set_lock(p_new->heap, &p_new->heap_mux); + + /* (This insertion is atomic to registered_heaps, so + we don't need to worry about thread safety for readers, + only for writers. */ + static multi_heap_lock_t registered_heaps_write_lock = MULTI_HEAP_LOCK_STATIC_INITIALIZER; + MULTI_HEAP_LOCK(®istered_heaps_write_lock); + SLIST_INSERT_HEAD(®istered_heaps, p_new, next); + MULTI_HEAP_UNLOCK(®istered_heaps_write_lock); + + err = ESP_OK; + + done: + if (err != ESP_OK) { + free(p_new); + } + return err; +} diff --git a/components/heap/heap_private.h b/components/heap/heap_private.h new file mode 100644 index 00000000..5e172c03 --- /dev/null +++ b/components/heap/heap_private.h @@ -0,0 +1,77 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include +#include +#include +#include "multi_heap.h" +#include "multi_heap_platform.h" +#include "sys/queue.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Some common heap registration data structures used + for heap_caps_init.c to share heap information with heap_caps.c +*/ + +#define HEAP_SIZE_MAX (SOC_MAX_CONTIGUOUS_RAM_SIZE) + +/* Type for describing each registered heap */ +typedef struct heap_t_ { + uint32_t caps[SOC_MEMORY_TYPE_NO_PRIOS]; ///< Capabilities for the type of memory in this heap (as a prioritised set). Copied from soc_memory_types so it's in RAM not flash. + intptr_t start; + intptr_t end; + multi_heap_lock_t heap_mux; + multi_heap_handle_t heap; + SLIST_ENTRY(heap_t_) next; +} heap_t; + +/* All registered heaps. + + Forms a single linked list, even though most entries are contiguous. + This means at the expense of 4 bytes per heap, new heaps can be + added at runtime in a fast & thread-safe way. +*/ +extern SLIST_HEAD(registered_heap_ll, heap_t_) registered_heaps; + +bool heap_caps_match(const heap_t *heap, uint32_t caps); + +/* return all possible capabilities (across all priorities) for a given heap */ +inline static IRAM_ATTR uint32_t get_all_caps(const heap_t *heap) +{ + if (heap->heap == NULL) { + return 0; + } + uint32_t all_caps = 0; + for (int prio = 0; prio < SOC_MEMORY_TYPE_NO_PRIOS; prio++) { + all_caps |= heap->caps[prio]; + } + return all_caps; +} + +/* + Because we don't want to add _another_ known allocation method to the stack of functions to trace wrt memory tracing, + these are declared private. The newlib malloc()/realloc() implementation also calls these, so they are declared + separately in newlib/syscalls.c. +*/ +void *heap_caps_realloc_default(void *p, size_t size); +void *heap_caps_malloc_default(size_t size); + + +#ifdef __cplusplus +} +#endif diff --git a/components/heap/heap_task_info.c b/components/heap/heap_task_info.c new file mode 100644 index 00000000..5914e664 --- /dev/null +++ b/components/heap/heap_task_info.c @@ -0,0 +1,129 @@ +// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include "multi_heap_internal.h" +#include "heap_private.h" +#include "esp_heap_task_info.h" + +#ifdef CONFIG_HEAP_TASK_TRACKING + +/* + * Return per-task heap allocation totals and lists of blocks. + * + * For each task that has allocated memory from the heap, return totals for + * allocations within regions matching one or more sets of capabilities. + * + * Optionally also return an array of structs providing details about each + * block allocated by one or more requested tasks, or by all tasks. + * + * Returns the number of block detail structs returned. + */ +size_t heap_caps_get_per_task_info(heap_task_info_params_t *params) +{ + heap_t *reg; + heap_task_block_t *blocks = params->blocks; + size_t count = *params->num_totals; + size_t remaining = params->max_blocks; + + // Clear out totals for any prepopulated tasks. + if (params->totals) { + for (size_t i = 0; i < count; ++i) { + for (size_t type = 0; type < NUM_HEAP_TASK_CAPS; ++type) { + params->totals[i].size[type] = 0; + params->totals[i].count[type] = 0; + } + } + } + + SLIST_FOREACH(reg, ®istered_heaps, next) { + multi_heap_handle_t heap = reg->heap; + if (heap == NULL) { + continue; + } + + // Find if the capabilities of this heap region match on of the desired + // sets of capabilities. + uint32_t caps = get_all_caps(reg); + uint32_t type; + for (type = 0; type < NUM_HEAP_TASK_CAPS; ++type) { + if ((caps & params->mask[type]) == params->caps[type]) { + break; + } + } + if (type == NUM_HEAP_TASK_CAPS) { + continue; + } + + multi_heap_block_handle_t b = multi_heap_get_first_block(heap); + multi_heap_internal_lock(heap); + for ( ; b ; b = multi_heap_get_next_block(heap, b)) { + if (multi_heap_is_free(b)) { + continue; + } + void *p = multi_heap_get_block_address(b); // Safe, only arithmetic + size_t bsize = multi_heap_get_allocated_size(heap, p); // Validates + TaskHandle_t btask = (TaskHandle_t)multi_heap_get_block_owner(b); + + // Accumulate per-task allocation totals. + if (params->totals) { + size_t i; + for (i = 0; i < count; ++i) { + if (params->totals[i].task == btask) { + break; + } + } + if (i < count) { + params->totals[i].size[type] += bsize; + params->totals[i].count[type] += 1; + } + else { + if (count < params->max_totals) { + params->totals[count].task = btask; + params->totals[count].size[type] = bsize; + params->totals[i].count[type] = 1; + ++count; + } + } + } + + // Return details about allocated blocks for selected tasks. + if (blocks && remaining > 0) { + if (params->tasks) { + size_t i; + for (i = 0; i < params->num_tasks; ++i) { + if (btask == params->tasks[i]) { + break; + } + } + if (i == params->num_tasks) { + continue; + } + } + blocks->task = btask; + blocks->address = p; + blocks->size = bsize; + ++blocks; + --remaining; + } + } + multi_heap_internal_unlock(heap); + } + *params->num_totals = count; + return params->max_blocks - remaining; +} + +#endif // CONFIG_HEAP_TASK_TRACKING diff --git a/components/heap/heap_tlsf.c b/components/heap/heap_tlsf.c new file mode 100644 index 00000000..42a1ad2e --- /dev/null +++ b/components/heap/heap_tlsf.c @@ -0,0 +1,1015 @@ +/* +** Two Level Segregated Fit memory allocator, version 3.1. +** Written by Matthew Conte +** http://tlsf.baisoku.org +** +** Based on the original documentation by Miguel Masmano: +** http://www.gii.upv.es/tlsf/main/docs +** +** This implementation was written to the specification +** of the document, therefore no GPL restrictions apply. +** +** Copyright (c) 2006-2016, Matthew Conte +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the copyright holder nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL MATTHEW CONTE BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include "multi_heap_config.h" +#include "multi_heap.h" +#include "multi_heap_internal.h" +#include "heap_tlsf_config.h" +#include "heap_tlsf.h" + +#include "esp_log.h" +/* +** Architecture-specific bit manipulation routines. +** +** TLSF achieves O(1) cost for malloc and free operations by limiting +** the search for a free block to a free list of guaranteed size +** adequate to fulfill the request, combined with efficient free list +** queries using bitmasks and architecture-specific bit-manipulation +** routines. +** +** Most modern processors provide instructions to count leading zeroes +** in a word, find the lowest and highest set bit, etc. These +** specific implementations will be used when available, falling back +** to a reasonably efficient generic implementation. +** +** NOTE: TLSF spec relies on ffs/fls returning value 0..31. +** ffs/fls return 1-32 by default, returning 0 for error. +*/ + +/* The TLSF control structure. */ +typedef struct control_t +{ + /* Empty lists point at this block to indicate they are free. */ + block_header_t block_null; + + /* Local parameter for the pool */ + unsigned int fl_index_count; + unsigned int fl_index_shift; + unsigned int fl_index_max; + unsigned int sl_index_count; + unsigned int sl_index_count_log2; + unsigned int small_block_size; + size_t size; + + /* Bitmaps for free lists. */ + unsigned int fl_bitmap; + unsigned int *sl_bitmap; + + /* Head of free lists. */ + block_header_t** blocks; +} control_t; + +static inline __attribute__((__always_inline__)) int tlsf_ffs(unsigned int word) +{ + const unsigned int reverse = word & (~word + 1); + const int bit = 32 - __builtin_clz(reverse); + return bit - 1; +} + +static inline __attribute__((__always_inline__)) int tlsf_fls(unsigned int word) +{ + const int bit = word ? 32 - __builtin_clz(word) : 0; + return bit - 1; +} + +/* +** Set assert macro, if it has not been provided by the user. +*/ +#if !defined (tlsf_assert) +#define tlsf_assert assert +#endif + +/* +** Static assertion mechanism. +*/ +#define _tlsf_glue2(x, y) x ## y +#define _tlsf_glue(x, y) _tlsf_glue2(x, y) +#define tlsf_static_assert(exp) \ + typedef char _tlsf_glue(static_assert, __LINE__) [(exp) ? 1 : -1] + +/* This code has been tested on 32- and 64-bit (LP/LLP) architectures. */ +tlsf_static_assert(sizeof(int) * CHAR_BIT == 32); +tlsf_static_assert(sizeof(size_t) * CHAR_BIT >= 32); +tlsf_static_assert(sizeof(size_t) * CHAR_BIT <= 64); + +static inline __attribute__((__always_inline__)) size_t align_up(size_t x, size_t align) +{ + tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two"); + return (x + (align - 1)) & ~(align - 1); +} + +static inline __attribute__((__always_inline__)) size_t align_down(size_t x, size_t align) +{ + tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two"); + return x - (x & (align - 1)); +} + +static inline __attribute__((__always_inline__)) void* align_ptr(const void* ptr, size_t align) +{ + const tlsfptr_t aligned = + (tlsf_cast(tlsfptr_t, ptr) + (align - 1)) & ~(align - 1); + tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two"); + return tlsf_cast(void*, aligned); +} + +/* +** Adjust an allocation size to be aligned to word size, and no smaller +** than internal minimum. +*/ +static inline __attribute__((__always_inline__)) size_t adjust_request_size(tlsf_t tlsf, size_t size, size_t align) +{ + size_t adjust = 0; + if (size) + { + const size_t aligned = align_up(size, align); + + /* aligned sized must not exceed block_size_max or we'll go out of bounds on sl_bitmap */ + if (aligned < tlsf_block_size_max(tlsf)) + { + adjust = tlsf_max(aligned, block_size_min); + } + } + return adjust; +} + +/* +** TLSF utility functions. In most cases, these are direct translations of +** the documentation found in the white paper. +*/ + +static inline __attribute__((__always_inline__)) void mapping_insert(control_t *control, size_t size, int* fli, int* sli) +{ + int fl, sl; + if (size < control->small_block_size) + { + /* Store small blocks in first list. */ + fl = 0; + sl = tlsf_cast(int, size) >> 2; + } + else + { + fl = tlsf_fls(size); + sl = tlsf_cast(int, size >> (fl - control->sl_index_count_log2)) ^ (1 << control->sl_index_count_log2); + fl -= (control->fl_index_shift - 1); + } + *fli = fl; + *sli = sl; +} + +/* This version rounds up to the next block size (for allocations) */ +static inline __attribute__((__always_inline__)) void mapping_search(control_t *control, size_t size, int* fli, int* sli) +{ + if (size >= control->small_block_size) + { + const size_t round = (1 << (tlsf_fls(size) - control->sl_index_count_log2)) - 1; + size += round; + } + mapping_insert(control, size, fli, sli); +} + +static inline __attribute__((__always_inline__)) block_header_t* search_suitable_block(control_t* control, int* fli, int* sli) +{ + int fl = *fli; + int sl = *sli; + + /* + ** First, search for a block in the list associated with the given + ** fl/sl index. + */ + unsigned int sl_map = control->sl_bitmap[fl] & (~0U << sl); + if (!sl_map) + { + /* No block exists. Search in the next largest first-level list. */ + const unsigned int fl_map = control->fl_bitmap & (~0U << (fl + 1)); + if (!fl_map) + { + /* No free blocks available, memory has been exhausted. */ + return 0; + } + + fl = tlsf_ffs(fl_map); + *fli = fl; + sl_map = control->sl_bitmap[fl]; + } + tlsf_assert(sl_map && "internal error - second level bitmap is null"); + sl = tlsf_ffs(sl_map); + *sli = sl; + + /* Return the first block in the free list. */ + return control->blocks[fl*control->sl_index_count + sl]; +} + +/* Remove a free block from the free list.*/ +static inline __attribute__((__always_inline__)) void remove_free_block(control_t* control, block_header_t* block, int fl, int sl) +{ + block_header_t* prev = block->prev_free; + block_header_t* next = block->next_free; + tlsf_assert(prev && "prev_free field can not be null"); + tlsf_assert(next && "next_free field can not be null"); + next->prev_free = prev; + prev->next_free = next; + + /* If this block is the head of the free list, set new head. */ + if (control->blocks[fl*control->sl_index_count + sl] == block) + { + control->blocks[fl*control->sl_index_count + sl] = next; + + /* If the new head is null, clear the bitmap. */ + if (next == &control->block_null) + { + control->sl_bitmap[fl] &= ~(1 << sl); + + /* If the second bitmap is now empty, clear the fl bitmap. */ + if (!control->sl_bitmap[fl]) + { + control->fl_bitmap &= ~(1 << fl); + } + } + } +} + +/* Insert a free block into the free block list. */ +static inline __attribute__((__always_inline__)) void insert_free_block(control_t* control, block_header_t* block, int fl, int sl) +{ + block_header_t* current = control->blocks[fl*control->sl_index_count + sl]; + tlsf_assert(current && "free list cannot have a null entry"); + tlsf_assert(block && "cannot insert a null entry into the free list"); + block->next_free = current; + block->prev_free = &control->block_null; + current->prev_free = block; + + tlsf_assert(block_to_ptr(block) == align_ptr(block_to_ptr(block), ALIGN_SIZE) + && "block not aligned properly"); + /* + ** Insert the new block at the head of the list, and mark the first- + ** and second-level bitmaps appropriately. + */ + control->blocks[fl*control->sl_index_count + sl] = block; + control->fl_bitmap |= (1 << fl); + control->sl_bitmap[fl] |= (1 << sl); +} + +/* Remove a given block from the free list. */ +static inline __attribute__((__always_inline__)) void block_remove(control_t* control, block_header_t* block) +{ + int fl, sl; + mapping_insert(control, block_size(block), &fl, &sl); + remove_free_block(control, block, fl, sl); +} + +/* Insert a given block into the free list. */ +static inline __attribute__((__always_inline__)) void block_insert(control_t* control, block_header_t* block) +{ + int fl, sl; + mapping_insert(control, block_size(block), &fl, &sl); + insert_free_block(control, block, fl, sl); +} + +static inline __attribute__((__always_inline__)) int block_can_split(block_header_t* block, size_t size) +{ + return block_size(block) >= sizeof(block_header_t) + size; +} + +/* Split a block into two, the second of which is free. */ +static inline __attribute__((__always_inline__)) block_header_t* block_split(block_header_t* block, size_t size) +{ + /* Calculate the amount of space left in the remaining block. + * REMINDER: remaining pointer's first field is `prev_phys_block` but this field is part of the + * previous physical block. */ + block_header_t* remaining = + offset_to_block(block_to_ptr(block), size - block_header_overhead); + + /* `size` passed as an argument is the first block's new size, thus, the remaining block's size + * is `block_size(block) - size`. However, the block's data must be precedeed by the data size. + * This field is NOT part of the size, so it has to be substracted from the calculation. */ + const size_t remain_size = block_size(block) - (size + block_header_overhead); + + tlsf_assert(block_to_ptr(remaining) == align_ptr(block_to_ptr(remaining), ALIGN_SIZE) + && "remaining block not aligned properly"); + + tlsf_assert(block_size(block) == remain_size + size + block_header_overhead); + block_set_size(remaining, remain_size); + tlsf_assert(block_size(remaining) >= block_size_min && "block split with invalid size"); + + block_set_size(block, size); + block_mark_as_free(remaining); + + /** + * Here is the final outcome of this function: + * + * block remaining (block_ptr + size - BHO) + * + + + * | | + * v v + * +----------------------------------------------------------------------+ + * |0000| |xxxxxxxxxxxxxxxxxxxxxx|xxxx| |###########################| + * |0000| |xxxxxxxxxxxxxxxxxxxxxx|xxxx| |###########################| + * |0000| |xxxxxxxxxxxxxxxxxxxxxx|xxxx| |###########################| + * |0000| |xxxxxxxxxxxxxxxxxxxxxx|xxxx| |###########################| + * +----------------------------------------------------------------------+ + * | | | | + * + +<------------------------->+ +<-------------------------> + * BHO `size` (argument) bytes BHO `remain_size` bytes + * + * Where BHO = block_header_overhead, + * 0: part of the memory owned by a `block`'s previous neighbour, + * x: part of the memory owned by `block`. + * #: part of the memory owned by `remaining`. + */ + + return remaining; +} + +/* Absorb a free block's storage into an adjacent previous free block. */ +static inline __attribute__((__always_inline__)) block_header_t* block_absorb(block_header_t* prev, block_header_t* block) +{ + tlsf_assert(!block_is_last(prev) && "previous block can't be last"); + /* Note: Leaves flags untouched. */ + prev->size += block_size(block) + block_header_overhead; + block_link_next(prev); + +#ifdef MULTI_HEAP_POISONING_SLOW + /* next_block header needs to be replaced with a fill pattern */ + multi_heap_internal_poison_fill_region(block, sizeof(block_header_t), true /* free */); +#endif + + return prev; +} + +/* Merge a just-freed block with an adjacent previous free block. */ +static inline __attribute__((__always_inline__)) block_header_t* block_merge_prev(control_t* control, block_header_t* block) +{ + if (block_is_prev_free(block)) + { + block_header_t* prev = block_prev(block); + tlsf_assert(prev && "prev physical block can't be null"); + tlsf_assert(block_is_free(prev) && "prev block is not free though marked as such"); + block_remove(control, prev); + block = block_absorb(prev, block); + } + + return block; +} + +/* Merge a just-freed block with an adjacent free block. */ +static inline __attribute__((__always_inline__)) block_header_t* block_merge_next(control_t* control, block_header_t* block) +{ + block_header_t* next = block_next(block); + tlsf_assert(next && "next physical block can't be null"); + + if (block_is_free(next)) + { + tlsf_assert(!block_is_last(block) && "previous block can't be last"); + block_remove(control, next); + block = block_absorb(block, next); + } + + return block; +} + +/* Trim any trailing block space off the end of a block, return to pool. */ +static inline __attribute__((__always_inline__)) void block_trim_free(control_t* control, block_header_t* block, size_t size) +{ + tlsf_assert(block_is_free(block) && "block must be free"); + if (block_can_split(block, size)) + { + block_header_t* remaining_block = block_split(block, size); + block_link_next(block); + block_set_prev_free(remaining_block); + block_insert(control, remaining_block); + } +} + +/* Trim any trailing block space off the end of a used block, return to pool. */ +static inline __attribute__((__always_inline__)) void block_trim_used(control_t* control, block_header_t* block, size_t size) +{ + tlsf_assert(!block_is_free(block) && "block must be used"); + if (block_can_split(block, size)) + { + /* If the next block is free, we must coalesce. */ + block_header_t* remaining_block = block_split(block, size); + block_set_prev_used(remaining_block); + + remaining_block = block_merge_next(control, remaining_block); + block_insert(control, remaining_block); + } +} + +static inline __attribute__((__always_inline__)) block_header_t* block_trim_free_leading(control_t* control, block_header_t* block, size_t size) +{ + block_header_t* remaining_block = block; + if (block_can_split(block, size)) + { + /* We want to split `block` in two: the first block will be freed and the + * second block will be returned. */ + remaining_block = block_split(block, size - block_header_overhead); + + /* `remaining_block` is the second block, mark its predecessor (first + * block) as free. */ + block_set_prev_free(remaining_block); + + block_link_next(block); + + /* Put back the first block into the free memory list. */ + block_insert(control, block); + } + + return remaining_block; +} + +static inline __attribute__((__always_inline__)) block_header_t* block_locate_free(control_t* control, size_t size) +{ + int fl = 0, sl = 0; + block_header_t* block = 0; + + if (size) + { + mapping_search(control, size, &fl, &sl); + + /* + ** mapping_search can futz with the size, so for excessively large sizes it can sometimes wind up + ** with indices that are off the end of the block array. + ** So, we protect against that here, since this is the only callsite of mapping_search. + ** Note that we don't need to check sl, since it comes from a modulo operation that guarantees it's always in range. + */ + if (fl < control->fl_index_count) + { + block = search_suitable_block(control, &fl, &sl); + } + } + + if (block) + { + tlsf_assert(block_size(block) >= size); + remove_free_block(control, block, fl, sl); + } + + return block; +} + +static inline __attribute__((__always_inline__)) void* block_prepare_used(control_t* control, block_header_t* block, size_t size) +{ + void* p = 0; + if (block) + { + tlsf_assert(size && "size must be non-zero"); + block_trim_free(control, block, size); + block_mark_as_used(block); + p = block_to_ptr(block); + } + return p; +} + +/* Clear structure and point all empty lists at the null block. */ +static void control_construct(control_t* control, size_t bytes) +{ + int i, j; + + control->block_null.next_free = &control->block_null; + control->block_null.prev_free = &control->block_null; + + /* find the closest ^2 for first layer */ + i = (bytes - 1) / (16 * 1024); + control->fl_index_max = FL_INDEX_MAX_MIN + sizeof(i) * 8 - __builtin_clz(i); + + /* adapt second layer to the pool */ + if (bytes <= 16 * 1024) control->sl_index_count_log2 = 3; + else if (bytes <= 256 * 1024) control->sl_index_count_log2 = 4; + else control->sl_index_count_log2 = 5; + + control->fl_index_shift = (control->sl_index_count_log2 + ALIGN_SIZE_LOG2); + control->sl_index_count = 1 << control->sl_index_count_log2; + control->fl_index_count = control->fl_index_max - control->fl_index_shift + 1; + control->small_block_size = 1 << control->fl_index_shift; + control->fl_bitmap = 0; + + control->sl_bitmap = align_ptr(control + 1, sizeof(*control->sl_bitmap)); + control->blocks = align_ptr(control->sl_bitmap + control->fl_index_count, sizeof(*control->blocks)); + control->size = (void*) (control->blocks + control->sl_index_count * control->fl_index_count) - (void*) control; + + ESP_EARLY_LOGW( "REMOVE", "NEW POOL of %d bytes, ctrl_size: %d sli_c:%d fli_c:%d small_b %d max_b:%d", + bytes, + control->size, control->sl_index_count, control->fl_index_count, + control->small_block_size, 1 << control->fl_index_max ); + + /* SL_INDEX_COUNT must be <= number of bits in sl_bitmap's storage type. */ + tlsf_assert(sizeof(unsigned int) * CHAR_BIT >= control->sl_index_count && "CHAR_BIT less than sl_index_count"); + + /* Ensure we've properly tuned our sizes. */ + tlsf_assert(ALIGN_SIZE == control->small_block_size / control->sl_index_count && "ALIGN_SIZE does not match"); + + for (i = 0; i < control->fl_index_count; ++i) + { + control->sl_bitmap[i] = 0; + for (j = 0; j < control->sl_index_count; ++j) + { + control->blocks[i*control->sl_index_count + j] = &control->block_null; + } + } +} + +/* +** Debugging utilities. +*/ + +typedef struct integrity_t +{ + int prev_status; + int status; +} integrity_t; + +#define tlsf_insist(x) { tlsf_assert(x); if (!(x)) { status--; } } + +static void integrity_walker(void* ptr, size_t size, int used, void* user) +{ + block_header_t* block = block_from_ptr(ptr); + integrity_t* integ = tlsf_cast(integrity_t*, user); + const int this_prev_status = block_is_prev_free(block) ? 1 : 0; + const int this_status = block_is_free(block) ? 1 : 0; + const size_t this_block_size = block_size(block); + + int status = 0; + (void)used; + tlsf_insist(integ->prev_status == this_prev_status && "prev status incorrect"); + tlsf_insist(size == this_block_size && "block size incorrect"); + + integ->prev_status = this_status; + integ->status += status; +} + +int tlsf_check(tlsf_t tlsf) +{ + int i, j; + + control_t* control = tlsf_cast(control_t*, tlsf); + int status = 0; + + /* Check that the free lists and bitmaps are accurate. */ + for (i = 0; i < control->fl_index_count; ++i) + { + for (j = 0; j < control->sl_index_count; ++j) + { + const int fl_map = control->fl_bitmap & (1 << i); + const int sl_list = control->sl_bitmap[i]; + const int sl_map = sl_list & (1 << j); + const block_header_t* block = control->blocks[i*control->sl_index_count + j]; + + /* Check that first- and second-level lists agree. */ + if (!fl_map) + { + tlsf_insist(!sl_map && "second-level map must be null"); + } + + if (!sl_map) + { + tlsf_insist(block == &control->block_null && "block list must be null"); + continue; + } + + /* Check that there is at least one free block. */ + tlsf_insist(sl_list && "no free blocks in second-level map"); + tlsf_insist(block != &control->block_null && "block should not be null"); + + while (block != &control->block_null) + { + int fli, sli; + tlsf_insist(block_is_free(block) && "block should be free"); + tlsf_insist(!block_is_prev_free(block) && "blocks should have coalesced"); + tlsf_insist(!block_is_free(block_next(block)) && "blocks should have coalesced"); + tlsf_insist(block_is_prev_free(block_next(block)) && "block should be free"); + tlsf_insist(block_size(block) >= block_size_min && "block not minimum size"); + + mapping_insert(control, block_size(block), &fli, &sli); + tlsf_insist(fli == i && sli == j && "block size indexed in wrong list"); + block = block->next_free; + } + } + } + + return status; +} + +#undef tlsf_insist + +static void default_walker(void* ptr, size_t size, int used, void* user) +{ + (void)user; + printf("\t%p %s size: %x (%p)\n", ptr, used ? "used" : "free", (unsigned int)size, block_from_ptr(ptr)); +} + +void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user) +{ + tlsf_walker pool_walker = walker ? walker : default_walker; + block_header_t* block = + offset_to_block(pool, -(int)block_header_overhead); + + while (block && !block_is_last(block)) + { + pool_walker( + block_to_ptr(block), + block_size(block), + !block_is_free(block), + user); + block = block_next(block); + } +} + +size_t tlsf_block_size(void* ptr) +{ + size_t size = 0; + if (ptr) + { + const block_header_t* block = block_from_ptr(ptr); + size = block_size(block); + } + return size; +} + +int tlsf_check_pool(pool_t pool) +{ + /* Check that the blocks are physically correct. */ + integrity_t integ = { 0, 0 }; + tlsf_walk_pool(pool, integrity_walker, &integ); + + return integ.status; +} + +size_t tlsf_fit_size(tlsf_t tlsf, size_t size) +{ + /* because it's GoodFit, allocable size is one range lower */ + if (size) + { + control_t* control = tlsf_cast(control_t*, tlsf); + size_t sl_interval = (1 << ((sizeof(size_t) * 8 - 1) - __builtin_clz(size))) / control->sl_index_count; + return size & ~(sl_interval - 1); + } + + return 0; +} + + +/* +** Size of the TLSF structures in a given memory block passed to +** tlsf_create, equal to the size of a control_t +*/ +size_t tlsf_size(tlsf_t tlsf) +{ + if (tlsf) + { + control_t* control = tlsf_cast(control_t*, tlsf); + return control->size; + } + + /* no tlsf, we'll just return a min size */ + return sizeof(control_t) + + sizeof(int) * SL_INDEX_COUNT_MIN + + sizeof(block_header_t*) * SL_INDEX_COUNT_MIN * FL_INDEX_COUNT_MIN; +} + +size_t tlsf_align_size(void) +{ + return ALIGN_SIZE; +} + +size_t tlsf_block_size_min(void) +{ + return block_size_min; +} + +size_t tlsf_block_size_max(tlsf_t tlsf) +{ + control_t* control = tlsf_cast(control_t*, tlsf); + return tlsf_cast(size_t, 1) << control->fl_index_max; +} + +/* +** Overhead of the TLSF structures in a given memory block passed to +** tlsf_add_pool, equal to the overhead of a free block and the +** sentinel block. +*/ +size_t tlsf_pool_overhead(void) +{ + return 2 * block_header_overhead; +} + +size_t tlsf_alloc_overhead(void) +{ + return block_header_overhead; +} + +pool_t tlsf_add_pool(tlsf_t tlsf, void* mem, size_t bytes) +{ + block_header_t* block; + block_header_t* next; + + const size_t pool_overhead = tlsf_pool_overhead(); + const size_t pool_bytes = align_down(bytes - pool_overhead, ALIGN_SIZE); + + if (((ptrdiff_t)mem % ALIGN_SIZE) != 0) + { + printf("tlsf_add_pool: Memory must be aligned by %u bytes.\n", + (unsigned int)ALIGN_SIZE); + return 0; + } + + if (pool_bytes < block_size_min || pool_bytes > tlsf_block_size_max(tlsf)) + { +#if defined (TLSF_64BIT) + printf("tlsf_add_pool: Memory size must be between 0x%x and 0x%x00 bytes.\n", + (unsigned int)(pool_overhead + block_size_min), + (unsigned int)((pool_overhead + tlsf_block_size_max(tlsf)) / 256)); +#else + printf("tlsf_add_pool: Memory size must be between %u and %u bytes.\n", + (unsigned int)(pool_overhead + block_size_min), + (unsigned int)(pool_overhead + tlsf_block_size_max(tlsf))); +#endif + return 0; + } + + /* + ** Create the main free block. Offset the start of the block slightly + ** so that the prev_phys_block field falls outside of the pool - + ** it will never be used. + */ + block = offset_to_block(mem, -(tlsfptr_t)block_header_overhead); + block_set_size(block, pool_bytes); + block_set_free(block); + block_set_prev_used(block); + block_insert(tlsf_cast(control_t*, tlsf), block); + + /* Split the block to create a zero-size sentinel block. */ + next = block_link_next(block); + block_set_size(next, 0); + block_set_used(next); + block_set_prev_free(next); + + return mem; +} + +void tlsf_remove_pool(tlsf_t tlsf, pool_t pool) +{ + control_t* control = tlsf_cast(control_t*, tlsf); + block_header_t* block = offset_to_block(pool, -(int)block_header_overhead); + + int fl = 0, sl = 0; + + tlsf_assert(block_is_free(block) && "block should be free"); + tlsf_assert(!block_is_free(block_next(block)) && "next block should not be free"); + tlsf_assert(block_size(block_next(block)) == 0 && "next block size should be zero"); + + mapping_insert(control, block_size(block), &fl, &sl); + remove_free_block(control, block, fl, sl); +} + +/* +** TLSF main interface. +*/ + + +tlsf_t tlsf_create(void* mem, size_t max_bytes) +{ +#if _DEBUG + if (test_ffs_fls()) + { + return 0; + } +#endif + + if (((tlsfptr_t)mem % ALIGN_SIZE) != 0) + { + printf("tlsf_create: Memory must be aligned to %u bytes.\n", + (unsigned int)ALIGN_SIZE); + return 0; + } + + control_construct(tlsf_cast(control_t*, mem), max_bytes); + + return tlsf_cast(tlsf_t, mem); +} + +pool_t tlsf_get_pool(tlsf_t tlsf) +{ + return tlsf_cast(pool_t, (char*)tlsf + tlsf_size(tlsf)); +} + +tlsf_t tlsf_create_with_pool(void* mem, size_t pool_bytes, size_t max_bytes) +{ + tlsf_t tlsf = tlsf_create(mem, max_bytes ? max_bytes : pool_bytes); + tlsf_add_pool(tlsf, (char*)mem + tlsf_size(tlsf), pool_bytes - tlsf_size(tlsf)); + return tlsf; +} + +void* tlsf_malloc(tlsf_t tlsf, size_t size) +{ + control_t* control = tlsf_cast(control_t*, tlsf); + size_t adjust = adjust_request_size(tlsf, size, ALIGN_SIZE); + block_header_t* block = block_locate_free(control, adjust); + return block_prepare_used(control, block, adjust); +} + +/** + * @brief Allocate memory of at least `size` bytes where byte at `data_offset` will be aligned to `alignment`. + * + * This function will allocate memory pointed by `ptr`. However, the byte at `data_offset` of + * this piece of memory (i.e., byte at `ptr` + `data_offset`) will be aligned to `alignment`. + * This function is useful for allocating memory that will internally have a header, and the + * usable memory following the header (i.e. `ptr` + `data_offset`) must be aligned. + * + * For example, a call to `multi_heap_aligned_alloc_impl_offs(heap, 64, 256, 20)` will return a + * pointer `ptr` to free memory of minimum 64 bytes, where `ptr + 20` is aligned on `256`. + * So `(ptr + 20) % 256` equals 0. + * + * @param tlsf TLSF structure to allocate memory from. + * @param align Alignment for the returned pointer's offset. + * @param size Minimum size, in bytes, of the memory to allocate INCLUDING + * `data_offset` bytes. + * @param data_offset Offset to be aligned on `alignment`. This can be 0, in + * this case, the returned pointer will be aligned on + * `alignment`. If it is not a multiple of CPU word size, + * it will be aligned up to the closest multiple of it. + * + * @return pointer to free memory. + */ +void* tlsf_memalign_offs(tlsf_t tlsf, size_t align, size_t size, size_t data_offset) +{ + control_t* control = tlsf_cast(control_t*, tlsf); + const size_t adjust = adjust_request_size(tlsf, size, ALIGN_SIZE); + const size_t off_adjust = align_up(data_offset, ALIGN_SIZE); + + /* + ** We must allocate an additional minimum block size bytes so that if + ** our free block will leave an alignment gap which is smaller, we can + ** trim a leading free block and release it back to the pool. We must + ** do this because the previous physical block is in use, therefore + ** the prev_phys_block field is not valid, and we can't simply adjust + ** the size of that block. + */ + const size_t gap_minimum = sizeof(block_header_t) + off_adjust; + /* The offset is included in both `adjust` and `gap_minimum`, so we + ** need to subtract it once. + */ + const size_t size_with_gap = adjust_request_size(tlsf, adjust + align + gap_minimum - off_adjust, align); + + /* + ** If alignment is less than or equals base alignment, we're done. + ** If we requested 0 bytes, return null, as tlsf_malloc(0) does. + */ + const size_t aligned_size = (adjust && align > ALIGN_SIZE) ? size_with_gap : adjust; + + block_header_t* block = block_locate_free(control, aligned_size); + + /* This can't be a static assert. */ + tlsf_assert(sizeof(block_header_t) == block_size_min + block_header_overhead); + + if (block) + { + void* ptr = block_to_ptr(block); + void* aligned = align_ptr(ptr, align); + size_t gap = tlsf_cast(size_t, + tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr)); + + /* + ** If gap size is too small or if there is not gap but we need one, + ** offset to next aligned boundary. + */ + if ((gap && gap < gap_minimum) || (!gap && off_adjust)) + { + const size_t gap_remain = gap_minimum - gap; + const size_t offset = tlsf_max(gap_remain, align); + const void* next_aligned = tlsf_cast(void*, + tlsf_cast(tlsfptr_t, aligned) + offset); + + aligned = align_ptr(next_aligned, align); + gap = tlsf_cast(size_t, + tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr)); + } + + if (gap) + { + tlsf_assert(gap >= gap_minimum && "gap size too small"); + block = block_trim_free_leading(control, block, gap - off_adjust); + } + } + + /* Preparing the block will also the trailing free memory. */ + return block_prepare_used(control, block, adjust); +} + +/** + * @brief Same as `tlsf_memalign_offs` function but with a 0 offset. + * The pointer returned is aligned on `align`. + */ +void* tlsf_memalign(tlsf_t tlsf, size_t align, size_t size) +{ + return tlsf_memalign_offs(tlsf, align, size, 0); +} + + +void tlsf_free(tlsf_t tlsf, void* ptr) +{ + /* Don't attempt to free a NULL pointer. */ + if (ptr) + { + control_t* control = tlsf_cast(control_t*, tlsf); + block_header_t* block = block_from_ptr(ptr); + tlsf_assert(!block_is_free(block) && "block already marked as free"); + block_mark_as_free(block); + block = block_merge_prev(control, block); + block = block_merge_next(control, block); + block_insert(control, block); + } +} + +/* +** The TLSF block information provides us with enough information to +** provide a reasonably intelligent implementation of realloc, growing or +** shrinking the currently allocated block as required. +** +** This routine handles the somewhat esoteric edge cases of realloc: +** - a non-zero size with a null pointer will behave like malloc +** - a zero size with a non-null pointer will behave like free +** - a request that cannot be satisfied will leave the original buffer +** untouched +** - an extended buffer size will leave the newly-allocated area with +** contents undefined +*/ +void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size) +{ + control_t* control = tlsf_cast(control_t*, tlsf); + void* p = 0; + + /* Zero-size requests are treated as free. */ + if (ptr && size == 0) + { + tlsf_free(tlsf, ptr); + } + /* Requests with NULL pointers are treated as malloc. */ + else if (!ptr) + { + p = tlsf_malloc(tlsf, size); + } + else + { + block_header_t* block = block_from_ptr(ptr); + block_header_t* next = block_next(block); + + const size_t cursize = block_size(block); + const size_t combined = cursize + block_size(next) + block_header_overhead; + const size_t adjust = adjust_request_size(tlsf, size, ALIGN_SIZE); + + tlsf_assert(!block_is_free(block) && "block already marked as free"); + + /* + ** If the next block is used, or when combined with the current + ** block, does not offer enough space, we must reallocate and copy. + */ + if (adjust > cursize && (!block_is_free(next) || adjust > combined)) + { + p = tlsf_malloc(tlsf, size); + if (p) + { + const size_t minsize = tlsf_min(cursize, size); + memcpy(p, ptr, minsize); + tlsf_free(tlsf, ptr); + } + } + else + { + /* Do we need to expand to the next block? */ + if (adjust > cursize) + { + block_merge_next(control, block); + block_mark_as_used(block); + } + + /* Trim the resulting block and return the original pointer. */ + block_trim_used(control, block, adjust); + p = ptr; + } + } + + return p; +} diff --git a/components/heap/heap_tlsf.h b/components/heap/heap_tlsf.h new file mode 100644 index 00000000..b2f94a62 --- /dev/null +++ b/components/heap/heap_tlsf.h @@ -0,0 +1,119 @@ +/* +** Two Level Segregated Fit memory allocator, version 3.1. +** Written by Matthew Conte +** http://tlsf.baisoku.org +** +** Based on the original documentation by Miguel Masmano: +** http://www.gii.upv.es/tlsf/main/docs +** +** This implementation was written to the specification +** of the document, therefore no GPL restrictions apply. +** +** Copyright (c) 2006-2016, Matthew Conte +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the copyright holder nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL MATTHEW CONTE BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include "heap_tlsf_config.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* +** Cast and min/max macros. +*/ +#define tlsf_cast(t, exp) ((t) (exp)) +#define tlsf_min(a, b) ((a) < (b) ? (a) : (b)) +#define tlsf_max(a, b) ((a) > (b) ? (a) : (b)) + +/* A type used for casting when doing pointer arithmetic. */ +typedef ptrdiff_t tlsfptr_t; + +typedef struct block_header_t +{ + /* Points to the previous physical block. */ + struct block_header_t* prev_phys_block; + + /* The size of this block, excluding the block header. */ + size_t size; + + /* Next and previous free blocks. */ + struct block_header_t* next_free; + struct block_header_t* prev_free; +} block_header_t; + +#include "heap_tlsf_block_functions.h" + +/* tlsf_t: a TLSF structure. Can contain 1 to N pools. */ +/* pool_t: a block of memory that TLSF can manage. */ +typedef void* tlsf_t; +typedef void* pool_t; + +/* Create/destroy a memory pool. */ +tlsf_t tlsf_create(void* mem, size_t max_bytes); +tlsf_t tlsf_create_with_pool(void* mem, size_t pool_bytes, size_t max_bytes); +pool_t tlsf_get_pool(tlsf_t tlsf); + +/* Add/remove memory pools. */ +pool_t tlsf_add_pool(tlsf_t tlsf, void* mem, size_t bytes); +void tlsf_remove_pool(tlsf_t tlsf, pool_t pool); + +/* malloc/memalign/realloc/free replacements. */ +void* tlsf_malloc(tlsf_t tlsf, size_t size); +void* tlsf_memalign(tlsf_t tlsf, size_t align, size_t size); +void* tlsf_memalign_offs(tlsf_t tlsf, size_t align, size_t size, size_t offset); +void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size); +void tlsf_free(tlsf_t tlsf, void* ptr); + +/* Returns internal block size, not original request size */ +size_t tlsf_block_size(void* ptr); + +/* Overheads/limits of internal structures. */ +size_t tlsf_size(tlsf_t tlsf); +size_t tlsf_align_size(void); +size_t tlsf_block_size_min(void); +size_t tlsf_block_size_max(tlsf_t tlsf); +size_t tlsf_pool_overhead(void); +size_t tlsf_alloc_overhead(void); +size_t tlsf_fit_size(tlsf_t tlsf, size_t size); + +/* Debugging. */ +typedef void (*tlsf_walker)(void* ptr, size_t size, int used, void* user); +void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user); +/* Returns nonzero if any internal consistency check fails. */ +int tlsf_check(tlsf_t tlsf); +int tlsf_check_pool(pool_t pool); + +#if defined(__cplusplus) +}; +#endif diff --git a/components/heap/heap_tlsf_block_functions.h b/components/heap/heap_tlsf_block_functions.h new file mode 100644 index 00000000..efc92c9c --- /dev/null +++ b/components/heap/heap_tlsf_block_functions.h @@ -0,0 +1,174 @@ +/* +** Two Level Segregated Fit memory allocator, version 3.1. +** Written by Matthew Conte +** http://tlsf.baisoku.org +** +** Based on the original documentation by Miguel Masmano: +** http://www.gii.upv.es/tlsf/main/docs +** +** This implementation was written to the specification +** of the document, therefore no GPL restrictions apply. +** +** Copyright (c) 2006-2016, Matthew Conte +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the copyright holder nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL MATTHEW CONTE BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +/* +** Data structures and associated constants. +*/ + +/* +** Since block sizes are always at least a multiple of 4, the two least +** significant bits of the size field are used to store the block status: +** - bit 0: whether block is busy or free +** - bit 1: whether previous block is busy or free +*/ +#define block_header_free_bit (1 << 0) +#define block_header_prev_free_bit (1 << 1) + +/* +** The size of the block header exposed to used blocks is the size field. +** The prev_phys_block field is stored *inside* the previous free block. +*/ +#define block_header_overhead (sizeof(size_t)) + +/* User data starts directly after the size field in a used block. */ +#define block_start_offset (offsetof(block_header_t, size) + sizeof(size_t)) + +/* +** A free block must be large enough to store its header minus the size of +** the prev_phys_block field, and no larger than the number of addressable +** bits for FL_INDEX. +** The block_size_max macro returns the maximum block for the minimum pool +** use tlsf_block_size_max for a value specific to the pool +*/ +#define block_size_min (sizeof(block_header_t) - sizeof(block_header_t*)) +#define block_size_max (tlsf_cast(size_t, 1) << FL_INDEX_MAX_MIN) + +/* +** block_header_t member functions. +*/ +static inline __attribute__((__always_inline__)) size_t block_size(const block_header_t* block) +{ + return block->size & ~(block_header_free_bit | block_header_prev_free_bit); +} + +static inline __attribute__((__always_inline__)) void block_set_size(block_header_t* block, size_t size) +{ + const size_t oldsize = block->size; + block->size = size | (oldsize & (block_header_free_bit | block_header_prev_free_bit)); +} + +static inline __attribute__((__always_inline__)) int block_is_last(const block_header_t* block) +{ + return block_size(block) == 0; +} + +static inline __attribute__((__always_inline__)) int block_is_free(const block_header_t* block) +{ + return tlsf_cast(int, block->size & block_header_free_bit); +} + +static inline __attribute__((__always_inline__)) void block_set_free(block_header_t* block) +{ + block->size |= block_header_free_bit; +} + +static inline __attribute__((__always_inline__)) void block_set_used(block_header_t* block) +{ + block->size &= ~block_header_free_bit; +} + +static inline __attribute__((__always_inline__)) int block_is_prev_free(const block_header_t* block) +{ + return tlsf_cast(int, block->size & block_header_prev_free_bit); +} + +static inline __attribute__((__always_inline__)) void block_set_prev_free(block_header_t* block) +{ + block->size |= block_header_prev_free_bit; +} + +static inline __attribute__((__always_inline__)) void block_set_prev_used(block_header_t* block) +{ + block->size &= ~block_header_prev_free_bit; +} + +static inline __attribute__((__always_inline__)) block_header_t* block_from_ptr(const void* ptr) +{ + return tlsf_cast(block_header_t*, + tlsf_cast(unsigned char*, ptr) - block_start_offset); +} + +static inline __attribute__((__always_inline__)) void* block_to_ptr(const block_header_t* block) +{ + return tlsf_cast(void*, + tlsf_cast(unsigned char*, block) + block_start_offset); +} + +/* Return location of next block after block of given size. */ +static inline __attribute__((__always_inline__)) block_header_t* offset_to_block(const void* ptr, size_t size) +{ + return tlsf_cast(block_header_t*, tlsf_cast(tlsfptr_t, ptr) + size); +} + +/* Return location of previous block. */ +static inline __attribute__((__always_inline__)) block_header_t* block_prev(const block_header_t* block) +{ + return block->prev_phys_block; +} + +/* Return location of next existing block. */ +static inline __attribute__((__always_inline__)) block_header_t* block_next(const block_header_t* block) +{ + block_header_t* next = offset_to_block(block_to_ptr(block), + block_size(block) - block_header_overhead); + return next; +} + +/* Link a new block with its physical neighbor, return the neighbor. */ +static inline __attribute__((__always_inline__)) block_header_t* block_link_next(block_header_t* block) +{ + block_header_t* next = block_next(block); + next->prev_phys_block = block; + return next; +} + +static inline __attribute__((__always_inline__)) void block_mark_as_free(block_header_t* block) +{ + /* Link the block to the next block, first. */ + block_header_t* next = block_link_next(block); + block_set_prev_free(next); + block_set_free(block); +} + +static inline __attribute__((__always_inline__)) void block_mark_as_used(block_header_t* block) +{ + block_header_t* next = block_next(block); + block_set_prev_used(next); + block_set_used(block); +} diff --git a/components/heap/heap_tlsf_config.h b/components/heap/heap_tlsf_config.h new file mode 100644 index 00000000..a3899e7e --- /dev/null +++ b/components/heap/heap_tlsf_config.h @@ -0,0 +1,66 @@ +/* +** Two Level Segregated Fit memory allocator, version 3.1. +** Written by Matthew Conte +** http://tlsf.baisoku.org +** +** Based on the original documentation by Miguel Masmano: +** http://www.gii.upv.es/tlsf/main/docs +** +** This implementation was written to the specification +** of the document, therefore no GPL restrictions apply. +** +** Copyright (c) 2006-2016, Matthew Conte +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the copyright holder nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL MATTHEW CONTE BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +enum tlsf_config +{ + /* log2 of number of linear subdivisions of block sizes. Larger + ** values require more memory in the control structure. Values of + ** 4 or 5 are typical, 3 is for very small pools. + */ + SL_INDEX_COUNT_LOG2_MIN = 3, + + /* All allocation sizes and addresses are aligned to 4 bytes. */ + ALIGN_SIZE_LOG2 = 2, + ALIGN_SIZE = (1 << ALIGN_SIZE_LOG2), + + /* + ** We support allocations of sizes up to (1 << FL_INDEX_MAX) bits. + ** However, because we linearly subdivide the second-level lists, and + ** our minimum size granularity is 4 bytes, it doesn't make sense to + ** create first-level lists for sizes smaller than SL_INDEX_COUNT * 4, + ** or (1 << (SL_INDEX_COUNT_LOG2 + 2)) bytes, as there we will be + ** trying to split size ranges into more slots than we have available. + ** Instead, we calculate the minimum threshold size, and place all + ** blocks below that size into the 0th first-level list. + ** Values below are the absolute minimum to accept a pool addition + */ + FL_INDEX_MAX_MIN = 14, // For a less than 16kB pool + SL_INDEX_COUNT_MIN = (1 << SL_INDEX_COUNT_LOG2_MIN), + FL_INDEX_COUNT_MIN = (FL_INDEX_MAX_MIN - (SL_INDEX_COUNT_LOG2_MIN + ALIGN_SIZE_LOG2) + 1), +}; diff --git a/components/heap/heap_trace_standalone.c b/components/heap/heap_trace_standalone.c new file mode 100644 index 00000000..138a8cd3 --- /dev/null +++ b/components/heap/heap_trace_standalone.c @@ -0,0 +1,255 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include + +#define HEAP_TRACE_SRCFILE /* don't warn on inclusion here */ +#include "esp_heap_trace.h" +#undef HEAP_TRACE_SRCFILE + +#include "esp_attr.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + + +#define STACK_DEPTH CONFIG_HEAP_TRACING_STACK_DEPTH + +#if CONFIG_HEAP_TRACING_STANDALONE + +static portMUX_TYPE trace_mux = portMUX_INITIALIZER_UNLOCKED; +static bool tracing; +static heap_trace_mode_t mode; + +/* Buffer used for records, starting at offset 0 +*/ +static heap_trace_record_t *buffer; +static size_t total_records; + +/* Count of entries logged in the buffer. + + Maximum total_records +*/ +static size_t count; + +/* Actual number of allocations logged */ +static size_t total_allocations; + +/* Actual number of frees logged */ +static size_t total_frees; + +/* Has the buffer overflowed and lost trace entries? */ +static bool has_overflowed = false; + +esp_err_t heap_trace_init_standalone(heap_trace_record_t *record_buffer, size_t num_records) +{ + if (tracing) { + return ESP_ERR_INVALID_STATE; + } + buffer = record_buffer; + total_records = num_records; + memset(buffer, 0, num_records * sizeof(heap_trace_record_t)); + return ESP_OK; +} + +esp_err_t heap_trace_start(heap_trace_mode_t mode_param) +{ + if (buffer == NULL || total_records == 0) { + return ESP_ERR_INVALID_STATE; + } + + portENTER_CRITICAL(&trace_mux); + + tracing = false; + mode = mode_param; + count = 0; + total_allocations = 0; + total_frees = 0; + has_overflowed = false; + heap_trace_resume(); + + portEXIT_CRITICAL(&trace_mux); + return ESP_OK; +} + +static esp_err_t set_tracing(bool enable) +{ + if (tracing == enable) { + return ESP_ERR_INVALID_STATE; + } + tracing = enable; + return ESP_OK; +} + +esp_err_t heap_trace_stop(void) +{ + return set_tracing(false); +} + +esp_err_t heap_trace_resume(void) +{ + return set_tracing(true); +} + +size_t heap_trace_get_count(void) +{ + return count; +} + +esp_err_t heap_trace_get(size_t index, heap_trace_record_t *record) +{ + if (record == NULL) { + return ESP_ERR_INVALID_STATE; + } + esp_err_t result = ESP_OK; + + portENTER_CRITICAL(&trace_mux); + if (index >= count) { + result = ESP_ERR_INVALID_ARG; /* out of range for 'count' */ + } else { + memcpy(record, &buffer[index], sizeof(heap_trace_record_t)); + } + portEXIT_CRITICAL(&trace_mux); + return result; +} + + +void heap_trace_dump(void) +{ + size_t delta_size = 0; + size_t delta_allocs = 0; + printf("%u allocations trace (%u entry buffer)\n", + count, total_records); + size_t start_count = count; + for (int i = 0; i < count; i++) { + heap_trace_record_t *rec = &buffer[i]; + + if (rec->address != NULL) { + printf("%d bytes (@ %p) allocated CPU %d ccount 0x%08x caller ", + rec->size, rec->address, rec->ccount & 1, rec->ccount & ~3); + for (int j = 0; j < STACK_DEPTH && rec->alloced_by[j] != 0; j++) { + printf("%p%s", rec->alloced_by[j], + (j < STACK_DEPTH - 1) ? ":" : ""); + } + + if (mode != HEAP_TRACE_ALL || STACK_DEPTH == 0 || rec->freed_by[0] == NULL) { + delta_size += rec->size; + delta_allocs++; + printf("\n"); + } else { + printf("\nfreed by "); + for (int j = 0; j < STACK_DEPTH; j++) { + printf("%p%s", rec->freed_by[j], + (j < STACK_DEPTH - 1) ? ":" : "\n"); + } + } + } + } + if (mode == HEAP_TRACE_ALL) { + printf("%u bytes alive in trace (%u/%u allocations)\n", + delta_size, delta_allocs, heap_trace_get_count()); + } else { + printf("%u bytes 'leaked' in trace (%u allocations)\n", delta_size, delta_allocs); + } + printf("total allocations %u total frees %u\n", total_allocations, total_frees); + if (start_count != count) { // only a problem if trace isn't stopped before dumping + printf("(NB: New entries were traced while dumping, so trace dump may have duplicate entries.)\n"); + } + if (has_overflowed) { + printf("(NB: Buffer has overflowed, so trace data is incomplete.)\n"); + } +} + +/* Add a new allocation to the heap trace records */ +static IRAM_ATTR void record_allocation(const heap_trace_record_t *record) +{ + if (!tracing || record->address == NULL) { + return; + } + + portENTER_CRITICAL(&trace_mux); + if (tracing) { + if (count == total_records) { + has_overflowed = true; + /* Move the whole buffer back one slot. + + This is a bit slow, compared to treating this buffer as a ringbuffer and rotating a head pointer. + + However, ringbuffer code gets tricky when we remove elements in mid-buffer (for leak trace mode) while + trying to keep track of an item count that may overflow. + */ + memmove(&buffer[0], &buffer[1], sizeof(heap_trace_record_t) * (total_records -1)); + count--; + } + // Copy new record into place + memcpy(&buffer[count], record, sizeof(heap_trace_record_t)); + count++; + total_allocations++; + } + portEXIT_CRITICAL(&trace_mux); +} + +// remove a record, used when freeing +static void remove_record(int index); + +/* record a free event in the heap trace log + + For HEAP_TRACE_ALL, this means filling in the freed_by pointer. + For HEAP_TRACE_LEAKS, this means removing the record from the log. +*/ +static IRAM_ATTR void record_free(void *p, void **callers) +{ + if (!tracing || p == NULL) { + return; + } + + portENTER_CRITICAL(&trace_mux); + if (tracing && count > 0) { + total_frees++; + /* search backwards for the allocation record matching this free */ + int i; + for (i = count - 1; i >= 0; i--) { + if (buffer[i].address == p) { + break; + } + } + + if (i >= 0) { + if (mode == HEAP_TRACE_ALL) { + memcpy(buffer[i].freed_by, callers, sizeof(void *) * STACK_DEPTH); + } else { // HEAP_TRACE_LEAKS + // Leak trace mode, once an allocation is freed we remove it from the list + remove_record(i); + } + } + } + portEXIT_CRITICAL(&trace_mux); +} + +/* remove the entry at 'index' from the ringbuffer of saved records */ +static IRAM_ATTR void remove_record(int index) +{ + if (index < count - 1) { + // Remove the buffer entry from the list + memmove(&buffer[index], &buffer[index+1], + sizeof(heap_trace_record_t) * (total_records - index - 1)); + } else { + // For last element, just zero it out to avoid ambiguity + memset(&buffer[index], 0, sizeof(heap_trace_record_t)); + } + count--; +} + +#include "heap_trace.inc" + +#endif /*CONFIG_HEAP_TRACING_STANDALONE*/ diff --git a/components/heap/include/esp_heap_caps.h b/components/heap/include/esp_heap_caps.h new file mode 100644 index 00000000..bed3d710 --- /dev/null +++ b/components/heap/include/esp_heap_caps.h @@ -0,0 +1,402 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include +#include +#include "multi_heap.h" +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Flags to indicate the capabilities of the various memory systems + */ +#define MALLOC_CAP_EXEC (1<<0) ///< Memory must be able to run executable code +#define MALLOC_CAP_32BIT (1<<1) ///< Memory must allow for aligned 32-bit data accesses +#define MALLOC_CAP_8BIT (1<<2) ///< Memory must allow for 8/16/...-bit data accesses +#define MALLOC_CAP_DMA (1<<3) ///< Memory must be able to accessed by DMA +#define MALLOC_CAP_PID2 (1<<4) ///< Memory must be mapped to PID2 memory space (PIDs are not currently used) +#define MALLOC_CAP_PID3 (1<<5) ///< Memory must be mapped to PID3 memory space (PIDs are not currently used) +#define MALLOC_CAP_PID4 (1<<6) ///< Memory must be mapped to PID4 memory space (PIDs are not currently used) +#define MALLOC_CAP_PID5 (1<<7) ///< Memory must be mapped to PID5 memory space (PIDs are not currently used) +#define MALLOC_CAP_PID6 (1<<8) ///< Memory must be mapped to PID6 memory space (PIDs are not currently used) +#define MALLOC_CAP_PID7 (1<<9) ///< Memory must be mapped to PID7 memory space (PIDs are not currently used) +#define MALLOC_CAP_SPIRAM (1<<10) ///< Memory must be in SPI RAM +#define MALLOC_CAP_INTERNAL (1<<11) ///< Memory must be internal; specifically it should not disappear when flash/spiram cache is switched off +#define MALLOC_CAP_DEFAULT (1<<12) ///< Memory can be returned in a non-capability-specific memory allocation (e.g. malloc(), calloc()) call +#define MALLOC_CAP_IRAM_8BIT (1<<13) ///< Memory must be in IRAM and allow unaligned access +#define MALLOC_CAP_RETENTION (1<<14) + +#define MALLOC_CAP_INVALID (1<<31) ///< Memory can't be used / list end marker + +/** + * @brief callback called when a allocation operation fails, if registered + * @param size in bytes of failed allocation + * @param caps capabillites requested of failed allocation + * @param function_name function which generated the failure + */ +typedef void (*esp_alloc_failed_hook_t) (size_t size, uint32_t caps, const char * function_name); + +/** + * @brief registers a callback function to be invoked if a memory allocation operation fails + * @param callback caller defined callback to be invoked + * @return ESP_OK if callback was registered. + */ +esp_err_t heap_caps_register_failed_alloc_callback(esp_alloc_failed_hook_t callback); + +/** + * @brief Allocate a chunk of memory which has the given capabilities + * + * Equivalent semantics to libc malloc(), for capability-aware memory. + * + * In IDF, ``malloc(p)`` is equivalent to ``heap_caps_malloc(p, MALLOC_CAP_8BIT)``. + * + * @param size Size, in bytes, of the amount of memory to allocate + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory to be returned + * + * @return A pointer to the memory allocated on success, NULL on failure + */ +void *heap_caps_malloc(size_t size, uint32_t caps); + + +/** + * @brief Free memory previously allocated via heap_caps_malloc() or heap_caps_realloc(). + * + * Equivalent semantics to libc free(), for capability-aware memory. + * + * In IDF, ``free(p)`` is equivalent to ``heap_caps_free(p)``. + * + * @param ptr Pointer to memory previously returned from heap_caps_malloc() or heap_caps_realloc(). Can be NULL. + */ +void heap_caps_free( void *ptr); + +/** + * @brief Reallocate memory previously allocated via heap_caps_malloc() or heap_caps_realloc(). + * + * Equivalent semantics to libc realloc(), for capability-aware memory. + * + * In IDF, ``realloc(p, s)`` is equivalent to ``heap_caps_realloc(p, s, MALLOC_CAP_8BIT)``. + * + * 'caps' parameter can be different to the capabilities that any original 'ptr' was allocated with. In this way, + * realloc can be used to "move" a buffer if necessary to ensure it meets a new set of capabilities. + * + * @param ptr Pointer to previously allocated memory, or NULL for a new allocation. + * @param size Size of the new buffer requested, or 0 to free the buffer. + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory desired for the new allocation. + * + * @return Pointer to a new buffer of size 'size' with capabilities 'caps', or NULL if allocation failed. + */ +void *heap_caps_realloc( void *ptr, size_t size, uint32_t caps); + +/** + * @brief Allocate a aligned chunk of memory which has the given capabilities + * + * Equivalent semantics to libc aligned_alloc(), for capability-aware memory. + * @param alignment How the pointer received needs to be aligned + * must be a power of two + * @param size Size, in bytes, of the amount of memory to allocate + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory to be returned + * + * @return A pointer to the memory allocated on success, NULL on failure + * + * + */ +void *heap_caps_aligned_alloc(size_t alignment, size_t size, uint32_t caps); + +/** + * @brief Used to deallocate memory previously allocated with heap_caps_aligned_alloc + * + * @param ptr Pointer to the memory allocated + * @note This function is deprecated, plase consider using heap_caps_free() instead + */ +void __attribute__((deprecated)) heap_caps_aligned_free(void *ptr); + +/** + * @brief Allocate a aligned chunk of memory which has the given capabilities. The initialized value in the memory is set to zero. + * + * @param alignment How the pointer received needs to be aligned + * must be a power of two + * @param n Number of continuing chunks of memory to allocate + * @param size Size, in bytes, of a chunk of memory to allocate + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory to be returned + * + * @return A pointer to the memory allocated on success, NULL on failure + * + */ +void *heap_caps_aligned_calloc(size_t alignment, size_t n, size_t size, uint32_t caps); + + +/** + * @brief Allocate a chunk of memory which has the given capabilities. The initialized value in the memory is set to zero. + * + * Equivalent semantics to libc calloc(), for capability-aware memory. + * + * In IDF, ``calloc(p)`` is equivalent to ``heap_caps_calloc(p, MALLOC_CAP_8BIT)``. + * + * @param n Number of continuing chunks of memory to allocate + * @param size Size, in bytes, of a chunk of memory to allocate + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory to be returned + * + * @return A pointer to the memory allocated on success, NULL on failure + */ +void *heap_caps_calloc(size_t n, size_t size, uint32_t caps); + +/** + * @brief Get the total size of all the regions that have the given capabilities + * + * This function takes all regions capable of having the given capabilities allocated in them + * and adds up the total space they have. + * + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory + * + * @return total size in bytes + */ + +size_t heap_caps_get_total_size(uint32_t caps); + +/** + * @brief Get the total free size of all the regions that have the given capabilities + * + * This function takes all regions capable of having the given capabilities allocated in them + * and adds up the free space they have. + * + * Note that because of heap fragmentation it is probably not possible to allocate a single block of memory + * of this size. Use heap_caps_get_largest_free_block() for this purpose. + + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory + * + * @return Amount of free bytes in the regions + */ +size_t heap_caps_get_free_size( uint32_t caps ); + + +/** + * @brief Get the total minimum free memory of all regions with the given capabilities + * + * This adds all the low water marks of the regions capable of delivering the memory + * with the given capabilities. + * + * Note the result may be less than the global all-time minimum available heap of this kind, as "low water marks" are + * tracked per-region. Individual regions' heaps may have reached their "low water marks" at different points in time. However + * this result still gives a "worst case" indication for all-time minimum free heap. + * + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory + * + * @return Amount of free bytes in the regions + */ +size_t heap_caps_get_minimum_free_size( uint32_t caps ); + +/** + * @brief Get the largest free block of memory able to be allocated with the given capabilities. + * + * Returns the largest value of ``s`` for which ``heap_caps_malloc(s, caps)`` will succeed. + * + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory + * + * @return Size of largest free block in bytes. + */ +size_t heap_caps_get_largest_free_block( uint32_t caps ); + + +/** + * @brief Get heap info for all regions with the given capabilities. + * + * Calls multi_heap_info() on all heaps which share the given capabilities. The information returned is an aggregate + * across all matching heaps. The meanings of fields are the same as defined for multi_heap_info_t, except that + * ``minimum_free_bytes`` has the same caveats described in heap_caps_get_minimum_free_size(). + * + * @param info Pointer to a structure which will be filled with relevant + * heap metadata. + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory + * + */ +void heap_caps_get_info( multi_heap_info_t *info, uint32_t caps ); + + +/** + * @brief Print a summary of all memory with the given capabilities. + * + * Calls multi_heap_info on all heaps which share the given capabilities, and + * prints a two-line summary for each, then a total summary. + * + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory + * + */ +void heap_caps_print_heap_info( uint32_t caps ); + +/** + * @brief Check integrity of all heap memory in the system. + * + * Calls multi_heap_check on all heaps. Optionally print errors if heaps are corrupt. + * + * Calling this function is equivalent to calling heap_caps_check_integrity + * with the caps argument set to MALLOC_CAP_INVALID. + * + * @param print_errors Print specific errors if heap corruption is found. + * + * @return True if all heaps are valid, False if at least one heap is corrupt. + */ +bool heap_caps_check_integrity_all(bool print_errors); + +/** + * @brief Check integrity of all heaps with the given capabilities. + * + * Calls multi_heap_check on all heaps which share the given capabilities. Optionally + * print errors if the heaps are corrupt. + * + * See also heap_caps_check_integrity_all to check all heap memory + * in the system and heap_caps_check_integrity_addr to check memory + * around a single address. + * + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory + * @param print_errors Print specific errors if heap corruption is found. + * + * @return True if all heaps are valid, False if at least one heap is corrupt. + */ +bool heap_caps_check_integrity(uint32_t caps, bool print_errors); + +/** + * @brief Check integrity of heap memory around a given address. + * + * This function can be used to check the integrity of a single region of heap memory, + * which contains the given address. + * + * This can be useful if debugging heap integrity for corruption at a known address, + * as it has a lower overhead than checking all heap regions. Note that if the corrupt + * address moves around between runs (due to timing or other factors) then this approach + * won't work and you should call heap_caps_check_integrity or + * heap_caps_check_integrity_all instead. + * + * @note The entire heap region around the address is checked, not only the adjacent + * heap blocks. + * + * @param addr Address in memory. Check for corruption in region containing this address. + * @param print_errors Print specific errors if heap corruption is found. + * + * @return True if the heap containing the specified address is valid, + * False if at least one heap is corrupt or the address doesn't belong to a heap region. + */ +bool heap_caps_check_integrity_addr(intptr_t addr, bool print_errors); + +/** + * @brief Enable malloc() in external memory and set limit below which + * malloc() attempts are placed in internal memory. + * + * When external memory is in use, the allocation strategy is to initially try to + * satisfy smaller allocation requests with internal memory and larger requests + * with external memory. This sets the limit between the two, as well as generally + * enabling allocation in external memory. + * + * @param limit Limit, in bytes. + */ +void heap_caps_malloc_extmem_enable(size_t limit); + +/** + * @brief Allocate a chunk of memory as preference in decreasing order. + * + * @attention The variable parameters are bitwise OR of MALLOC_CAP_* flags indicating the type of memory. + * This API prefers to allocate memory with the first parameter. If failed, allocate memory with + * the next parameter. It will try in this order until allocating a chunk of memory successfully + * or fail to allocate memories with any of the parameters. + * + * @param size Size, in bytes, of the amount of memory to allocate + * @param num Number of variable paramters + * + * @return A pointer to the memory allocated on success, NULL on failure + */ +void *heap_caps_malloc_prefer( size_t size, size_t num, ... ); + +/** + * @brief Allocate a chunk of memory as preference in decreasing order. + * + * @param ptr Pointer to previously allocated memory, or NULL for a new allocation. + * @param size Size of the new buffer requested, or 0 to free the buffer. + * @param num Number of variable paramters + * + * @return Pointer to a new buffer of size 'size', or NULL if allocation failed. + */ +void *heap_caps_realloc_prefer( void *ptr, size_t size, size_t num, ... ); + +/** + * @brief Allocate a chunk of memory as preference in decreasing order. + * + * @param n Number of continuing chunks of memory to allocate + * @param size Size, in bytes, of a chunk of memory to allocate + * @param num Number of variable paramters + * + * @return A pointer to the memory allocated on success, NULL on failure + */ +void *heap_caps_calloc_prefer( size_t n, size_t size, size_t num, ... ); + +/** + * @brief Dump the full structure of all heaps with matching capabilities. + * + * Prints a large amount of output to serial (because of locking limitations, + * the output bypasses stdout/stderr). For each (variable sized) block + * in each matching heap, the following output is printed on a single line: + * + * - Block address (the data buffer returned by malloc is 4 bytes after this + * if heap debugging is set to Basic, or 8 bytes otherwise). + * - Data size (the data size may be larger than the size requested by malloc, + * either due to heap fragmentation or because of heap debugging level). + * - Address of next block in the heap. + * - If the block is free, the address of the next free block is also printed. + * + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory + */ +void heap_caps_dump(uint32_t caps); + +/** + * @brief Dump the full structure of all heaps. + * + * Covers all registered heaps. Prints a large amount of output to serial. + * + * Output is the same as for heap_caps_dump. + * + */ +void heap_caps_dump_all(void); + +/** + * @brief Return the size that a particular pointer was allocated with. + * + * @param ptr Pointer to currently allocated heap memory. Must be a pointer value previously + * returned by heap_caps_malloc,malloc,calloc, etc. and not yet freed. + * + * @note The app will crash with an assertion failure if the pointer is not valid. + * + * @return Size of the memory allocated at this block. + * + */ +size_t heap_caps_get_allocated_size( void *ptr ); + +#ifdef __cplusplus +} +#endif diff --git a/components/heap/include/esp_heap_caps_init.h b/components/heap/include/esp_heap_caps_init.h new file mode 100644 index 00000000..74e8cb90 --- /dev/null +++ b/components/heap/include/esp_heap_caps_init.h @@ -0,0 +1,92 @@ +// Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include "esp_err.h" +#include "esp_heap_caps.h" +#include "soc/soc_memory_layout.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize the capability-aware heap allocator. + * + * This is called once in the IDF startup code. Do not call it + * at other times. + */ +void heap_caps_init(void); + +/** + * @brief Enable heap(s) in memory regions where the startup stacks are located. + * + * On startup, the pro/app CPUs have a certain memory region they use as stack, so we + * cannot do allocations in the regions these stack frames are. When FreeRTOS is + * completely started, they do not use that memory anymore and heap(s) there can + * be enabled. + */ +void heap_caps_enable_nonos_stack_heaps(void); + +/** + * @brief Add a region of memory to the collection of heaps at runtime. + * + * Most memory regions are defined in soc_memory_layout.c for the SoC, + * and are registered via heap_caps_init(). Some regions can't be used + * immediately and are later enabled via heap_caps_enable_nonos_stack_heaps(). + * + * Call this function to add a region of memory to the heap at some later time. + * + * This function does not consider any of the "reserved" regions or other data in soc_memory_layout, caller needs to + * consider this themselves. + * + * All memory within the region specified by start & end parameters must be otherwise unused. + * + * The capabilities of the newly registered memory will be determined by the start address, as looked up in the regions + * specified in soc_memory_layout.c. + * + * Use heap_caps_add_region_with_caps() to register a region with custom capabilities. + * + * @param start Start address of new region. + * @param end End address of new region. + * + * @return ESP_OK on success, ESP_ERR_INVALID_ARG if a parameter is invalid, ESP_ERR_NOT_FOUND if the + * specified start address doesn't reside in a known region, or any error returned by heap_caps_add_region_with_caps(). + */ +esp_err_t heap_caps_add_region(intptr_t start, intptr_t end); + + +/** + * @brief Add a region of memory to the collection of heaps at runtime, with custom capabilities. + * + * Similar to heap_caps_add_region(), only custom memory capabilities are specified by the caller. + * + * @param caps Ordered array of capability masks for the new region, in order of priority. Must have length + * SOC_MEMORY_TYPE_NO_PRIOS. Does not need to remain valid after the call returns. + * @param start Start address of new region. + * @param end End address of new region. + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if a parameter is invalid + * - ESP_ERR_NO_MEM if no memory to register new heap. + * - ESP_ERR_INVALID_SIZE if the memory region is too small to fit a heap + * - ESP_FAIL if region overlaps the start and/or end of an existing region + */ +esp_err_t heap_caps_add_region_with_caps(const uint32_t caps[], intptr_t start, intptr_t end); + + +#ifdef __cplusplus +} +#endif diff --git a/components/heap/include/esp_heap_task_info.h b/components/heap/include/esp_heap_task_info.h new file mode 100644 index 00000000..fca9a43b --- /dev/null +++ b/components/heap/include/esp_heap_task_info.h @@ -0,0 +1,98 @@ +// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#ifdef CONFIG_HEAP_TASK_TRACKING + +#ifdef __cplusplus +extern "C" { +#endif + +// This macro controls how much space is provided for partitioning the per-task +// heap allocation info according to one or more sets of heap capabilities. +#define NUM_HEAP_TASK_CAPS 4 + +/** @brief Structure to collect per-task heap allocation totals partitioned by selected caps */ +typedef struct { + TaskHandle_t task; ///< Task to which these totals belong + size_t size[NUM_HEAP_TASK_CAPS]; ///< Total allocations partitioned by selected caps + size_t count[NUM_HEAP_TASK_CAPS]; ///< Number of blocks partitioned by selected caps +} heap_task_totals_t; + +/** @brief Structure providing details about a block allocated by a task */ +typedef struct { + TaskHandle_t task; ///< Task that allocated the block + void *address; ///< User address of allocated block + uint32_t size; ///< Size of the allocated block +} heap_task_block_t; + +/** @brief Structure to provide parameters to heap_caps_get_per_task_info + * + * The 'caps' and 'mask' arrays allow partitioning the per-task heap allocation + * totals by selected sets of heap region capabilities so that totals for + * multiple regions can be accumulated in one scan. The capabilities flags for + * each region ANDed with mask[i] are compared to caps[i] in order; the + * allocations in that region are added to totals->size[i] and totals->count[i] + * for the first i that matches. To collect the totals without any + * partitioning, set mask[0] and caps[0] both to zero. The allocation totals + * are returned in the 'totals' array of heap_task_totals_t structs. To allow + * easily comparing the totals array between consecutive calls, that array can + * be left populated from one call to the next so the order of tasks is the + * same even if some tasks have freed their blocks or have been deleted. The + * number of blocks prepopulated is given by num_totals, which is updated upon + * return. If there are more tasks with allocations than the capacity of the + * totals array (given by max_totals), information for the excess tasks will be + * not be collected. The totals array pointer can be NULL if the totals are + * not desired. + * + * The 'tasks' array holds a list of handles for tasks whose block details are + * to be returned in the 'blocks' array of heap_task_block_t structs. If the + * tasks array pointer is NULL, block details for all tasks will be returned up + * to the capacity of the buffer array, given by max_blocks. The function + * return value tells the number of blocks filled into the array. The blocks + * array pointer can be NULL if block details are not desired, or max_blocks + * can be set to zero. + */ +typedef struct { + int32_t caps[NUM_HEAP_TASK_CAPS]; ///< Array of caps for partitioning task totals + int32_t mask[NUM_HEAP_TASK_CAPS]; ///< Array of masks under which caps must match + TaskHandle_t *tasks; ///< Array of tasks whose block info is returned + size_t num_tasks; ///< Length of tasks array + heap_task_totals_t *totals; ///< Array of structs to collect task totals + size_t *num_totals; ///< Number of task structs currently in array + size_t max_totals; ///< Capacity of array of task totals structs + heap_task_block_t *blocks; ///< Array of task block details structs + size_t max_blocks; ///< Capacity of array of task block info structs +} heap_task_info_params_t; + +/** + * @brief Return per-task heap allocation totals and lists of blocks. + * + * For each task that has allocated memory from the heap, return totals for + * allocations within regions matching one or more sets of capabilities. + * + * Optionally also return an array of structs providing details about each + * block allocated by one or more requested tasks, or by all tasks. + * + * @param params Structure to hold all the parameters for the function + * (@see heap_task_info_params_t). + * @return Number of block detail structs returned (@see heap_task_block_t). + */ +extern size_t heap_caps_get_per_task_info(heap_task_info_params_t *params); + +#ifdef __cplusplus +} +#endif + +#endif // CONFIG_HEAP_TASK_TRACKING diff --git a/components/heap/include/esp_heap_trace.h b/components/heap/include/esp_heap_trace.h new file mode 100644 index 00000000..a71f9636 --- /dev/null +++ b/components/heap/include/esp_heap_trace.h @@ -0,0 +1,154 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include "sdkconfig.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(CONFIG_HEAP_TRACING) && !defined(HEAP_TRACE_SRCFILE) +#warning "esp_heap_trace.h is included but heap tracing is disabled in menuconfig, functions are no-ops" +#endif + +#ifndef CONFIG_HEAP_TRACING_STACK_DEPTH +#define CONFIG_HEAP_TRACING_STACK_DEPTH 0 +#endif + +typedef enum { + HEAP_TRACE_ALL, + HEAP_TRACE_LEAKS, +} heap_trace_mode_t; + +/** + * @brief Trace record data type. Stores information about an allocated region of memory. + */ +typedef struct { + uint32_t ccount; ///< CCOUNT of the CPU when the allocation was made. LSB (bit value 1) is the CPU number (0 or 1). + void *address; ///< Address which was allocated + size_t size; ///< Size of the allocation + void *alloced_by[CONFIG_HEAP_TRACING_STACK_DEPTH]; ///< Call stack of the caller which allocated the memory. + void *freed_by[CONFIG_HEAP_TRACING_STACK_DEPTH]; ///< Call stack of the caller which freed the memory (all zero if not freed.) +} heap_trace_record_t; + +/** + * @brief Initialise heap tracing in standalone mode. + * + * This function must be called before any other heap tracing functions. + * + * To disable heap tracing and allow the buffer to be freed, stop tracing and then call heap_trace_init_standalone(NULL, 0); + * + * @param record_buffer Provide a buffer to use for heap trace data. Must remain valid any time heap tracing is enabled, meaning + * it must be allocated from internal memory not in PSRAM. + * @param num_records Size of the heap trace buffer, as number of record structures. + * @return + * - ESP_ERR_NOT_SUPPORTED Project was compiled without heap tracing enabled in menuconfig. + * - ESP_ERR_INVALID_STATE Heap tracing is currently in progress. + * - ESP_OK Heap tracing initialised successfully. + */ +esp_err_t heap_trace_init_standalone(heap_trace_record_t *record_buffer, size_t num_records); + +/** + * @brief Initialise heap tracing in host-based mode. + * + * This function must be called before any other heap tracing functions. + * + * @return + * - ESP_ERR_INVALID_STATE Heap tracing is currently in progress. + * - ESP_OK Heap tracing initialised successfully. + */ +esp_err_t heap_trace_init_tohost(void); + +/** + * @brief Start heap tracing. All heap allocations & frees will be traced, until heap_trace_stop() is called. + * + * @note heap_trace_init_standalone() must be called to provide a valid buffer, before this function is called. + * + * @note Calling this function while heap tracing is running will reset the heap trace state and continue tracing. + * + * @param mode Mode for tracing. + * - HEAP_TRACE_ALL means all heap allocations and frees are traced. + * - HEAP_TRACE_LEAKS means only suspected memory leaks are traced. (When memory is freed, the record is removed from the trace buffer.) + * @return + * - ESP_ERR_NOT_SUPPORTED Project was compiled without heap tracing enabled in menuconfig. + * - ESP_ERR_INVALID_STATE A non-zero-length buffer has not been set via heap_trace_init_standalone(). + * - ESP_OK Tracing is started. + */ +esp_err_t heap_trace_start(heap_trace_mode_t mode); + +/** + * @brief Stop heap tracing. + * + * @return + * - ESP_ERR_NOT_SUPPORTED Project was compiled without heap tracing enabled in menuconfig. + * - ESP_ERR_INVALID_STATE Heap tracing was not in progress. + * - ESP_OK Heap tracing stopped.. + */ +esp_err_t heap_trace_stop(void); + +/** + * @brief Resume heap tracing which was previously stopped. + * + * Unlike heap_trace_start(), this function does not clear the + * buffer of any pre-existing trace records. + * + * The heap trace mode is the same as when heap_trace_start() was + * last called (or HEAP_TRACE_ALL if heap_trace_start() was never called). + * + * @return + * - ESP_ERR_NOT_SUPPORTED Project was compiled without heap tracing enabled in menuconfig. + * - ESP_ERR_INVALID_STATE Heap tracing was already started. + * - ESP_OK Heap tracing resumed. + */ +esp_err_t heap_trace_resume(void); + +/** + * @brief Return number of records in the heap trace buffer + * + * It is safe to call this function while heap tracing is running. + */ +size_t heap_trace_get_count(void); + +/** + * @brief Return a raw record from the heap trace buffer + * + * @note It is safe to call this function while heap tracing is running, however in HEAP_TRACE_LEAK mode record indexing may + * skip entries unless heap tracing is stopped first. + * + * @param index Index (zero-based) of the record to return. + * @param[out] record Record where the heap trace record will be copied. + * @return + * - ESP_ERR_NOT_SUPPORTED Project was compiled without heap tracing enabled in menuconfig. + * - ESP_ERR_INVALID_STATE Heap tracing was not initialised. + * - ESP_ERR_INVALID_ARG Index is out of bounds for current heap trace record count. + * - ESP_OK Record returned successfully. + */ +esp_err_t heap_trace_get(size_t index, heap_trace_record_t *record); + +/** + * @brief Dump heap trace record data to stdout + * + * @note It is safe to call this function while heap tracing is running, however in HEAP_TRACE_LEAK mode the dump may skip + * entries unless heap tracing is stopped first. + * + * + */ +void heap_trace_dump(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/heap/include/heap_trace.inc b/components/heap/include/heap_trace.inc new file mode 100644 index 00000000..6f789285 --- /dev/null +++ b/components/heap/include/heap_trace.inc @@ -0,0 +1,200 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include "soc/soc_memory_layout.h" +#include "esp_attr.h" + +/* Encode the CPU ID in the LSB of the ccount value */ +inline static uint32_t get_ccount(void) +{ + uint32_t ccount = cpu_hal_get_cycle_count() & ~3; +#ifndef CONFIG_FREERTOS_UNICORE + ccount |= xPortGetCoreID(); +#endif + return ccount; +} + +/* Architecture-specific return value of __builtin_return_address which + * should be interpreted as an invalid address. + */ +#ifdef __XTENSA__ +#define HEAP_ARCH_INVALID_PC 0x40000000 +#else +#define HEAP_ARCH_INVALID_PC 0x00000000 +#endif + +// Caller is 2 stack frames deeper than we care about +#define STACK_OFFSET 2 + +#define TEST_STACK(N) do { \ + if (STACK_DEPTH == N) { \ + return; \ + } \ + callers[N] = __builtin_return_address(N+STACK_OFFSET); \ + if (!esp_ptr_executable(callers[N]) \ + || callers[N] == (void*) HEAP_ARCH_INVALID_PC) { \ + callers[N] = 0; \ + return; \ + } \ + } while(0) + +/* Static function to read the call stack for a traced heap call. + + Calls to __builtin_return_address are "unrolled" via TEST_STACK macro as gcc requires the + argument to be a compile-time constant. +*/ +static IRAM_ATTR __attribute__((noinline)) void get_call_stack(void **callers) +{ + bzero(callers, sizeof(void *) * STACK_DEPTH); + TEST_STACK(0); + TEST_STACK(1); + TEST_STACK(2); + TEST_STACK(3); + TEST_STACK(4); + TEST_STACK(5); + TEST_STACK(6); + TEST_STACK(7); + TEST_STACK(8); + TEST_STACK(9); +} + +_Static_assert(STACK_DEPTH >= 0 && STACK_DEPTH <= 10, "CONFIG_HEAP_TRACING_STACK_DEPTH must be in range 0-10"); + + +typedef enum { + TRACE_MALLOC_CAPS, + TRACE_MALLOC_DEFAULT +} trace_malloc_mode_t; + + +void *__real_heap_caps_malloc(size_t size, uint32_t caps); +void *__real_heap_caps_malloc_default( size_t size ); +void *__real_heap_caps_realloc_default( void *ptr, size_t size ); + +/* trace any 'malloc' event */ +static IRAM_ATTR __attribute__((noinline)) void *trace_malloc(size_t size, uint32_t caps, trace_malloc_mode_t mode) +{ + uint32_t ccount = get_ccount(); + void *p; + + if ( mode == TRACE_MALLOC_CAPS ) { + p = __real_heap_caps_malloc(size, caps); + } else { //TRACE_MALLOC_DEFAULT + p = __real_heap_caps_malloc_default(size); + } + + heap_trace_record_t rec = { + .address = p, + .ccount = ccount, + .size = size, + }; + get_call_stack(rec.alloced_by); + record_allocation(&rec); + return p; +} + +void __real_heap_caps_free(void *p); + +/* trace any 'free' event */ +static IRAM_ATTR __attribute__((noinline)) void trace_free(void *p) +{ + void *callers[STACK_DEPTH]; + get_call_stack(callers); + record_free(p, callers); + + __real_heap_caps_free(p); +} + +void * __real_heap_caps_realloc(void *p, size_t size, uint32_t caps); + +/* trace any 'realloc' event */ +static IRAM_ATTR __attribute__((noinline)) void *trace_realloc(void *p, size_t size, uint32_t caps, trace_malloc_mode_t mode) +{ + void *callers[STACK_DEPTH]; + uint32_t ccount = get_ccount(); + void *r; + + /* trace realloc as free-then-alloc */ + get_call_stack(callers); + record_free(p, callers); + + if (mode == TRACE_MALLOC_CAPS ) { + r = __real_heap_caps_realloc(p, size, caps); + } else { //TRACE_MALLOC_DEFAULT + r = __real_heap_caps_realloc_default(p, size); + } + /* realloc with zero size is a free */ + if (size != 0) { + heap_trace_record_t rec = { + .address = r, + .ccount = ccount, + .size = size, + }; + memcpy(rec.alloced_by, callers, sizeof(void *) * STACK_DEPTH); + record_allocation(&rec); + } + return r; +} + +/* Note: this changes the behaviour of libc malloc/realloc/free a bit, + as they no longer go via the libc functions in ROM. But more or less + the same in the end. */ + +IRAM_ATTR void *__wrap_malloc(size_t size) +{ + return trace_malloc(size, 0, TRACE_MALLOC_DEFAULT); +} + +IRAM_ATTR void __wrap_free(void *p) +{ + trace_free(p); +} + +IRAM_ATTR void *__wrap_realloc(void *p, size_t size) +{ + return trace_realloc(p, size, 0, TRACE_MALLOC_DEFAULT); +} + +IRAM_ATTR void *__wrap_calloc(size_t nmemb, size_t size) +{ + size = size * nmemb; + void *result = trace_malloc(size, 0, TRACE_MALLOC_DEFAULT); + if (result != NULL) { + memset(result, 0, size); + } + return result; +} + +IRAM_ATTR void *__wrap_heap_caps_malloc(size_t size, uint32_t caps) +{ + return trace_malloc(size, caps, TRACE_MALLOC_CAPS); +} + +void __wrap_heap_caps_free(void *p) __attribute__((alias("__wrap_free"))); + +IRAM_ATTR void *__wrap_heap_caps_realloc(void *p, size_t size, uint32_t caps) +{ + return trace_realloc(p, size, caps, TRACE_MALLOC_CAPS); +} + +IRAM_ATTR void *__wrap_heap_caps_malloc_default( size_t size ) +{ + return trace_malloc(size, 0, TRACE_MALLOC_DEFAULT); +} + +IRAM_ATTR void *__wrap_heap_caps_realloc_default( void *ptr, size_t size ) +{ + return trace_realloc(ptr, size, 0, TRACE_MALLOC_DEFAULT); +} diff --git a/components/heap/include/multi_heap.h b/components/heap/include/multi_heap.h new file mode 100644 index 00000000..622191fe --- /dev/null +++ b/components/heap/include/multi_heap.h @@ -0,0 +1,190 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once +#include +#include +#include + +/* multi_heap is a heap implementation for handling multiple + heterogenous heaps in a single program. + + Any contiguous block of memory can be registered as a heap. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Opaque handle to a registered heap */ +typedef struct multi_heap_info *multi_heap_handle_t; + +/** + * @brief allocate a chunk of memory with specific alignment + * + * @param heap Handle to a registered heap. + * @param size size in bytes of memory chunk + * @param alignment how the memory must be aligned + * + * @return pointer to the memory allocated, NULL on failure + */ +void *multi_heap_aligned_alloc(multi_heap_handle_t heap, size_t size, size_t alignment); + +/** @brief malloc() a buffer in a given heap + * + * Semantics are the same as standard malloc(), only the returned buffer will be allocated in the specified heap. + * + * @param heap Handle to a registered heap. + * @param size Size of desired buffer. + * + * @return Pointer to new memory, or NULL if allocation fails. + */ +void *multi_heap_malloc(multi_heap_handle_t heap, size_t size); + +/** @brief free() a buffer aligned in a given heap. + * + * @param heap Handle to a registered heap. + * @param p NULL, or a pointer previously returned from multi_heap_aligned_alloc() for the same heap. + * @note This function is deprecated, consider using multi_heap_free() instead + */ +void __attribute__((deprecated)) multi_heap_aligned_free(multi_heap_handle_t heap, void *p); + +/** @brief free() a buffer in a given heap. + * + * Semantics are the same as standard free(), only the argument 'p' must be NULL or have been allocated in the specified heap. + * + * @param heap Handle to a registered heap. + * @param p NULL, or a pointer previously returned from multi_heap_malloc() or multi_heap_realloc() for the same heap. + */ +void multi_heap_free(multi_heap_handle_t heap, void *p); + +/** @brief realloc() a buffer in a given heap. + * + * Semantics are the same as standard realloc(), only the argument 'p' must be NULL or have been allocated in the specified heap. + * + * @param heap Handle to a registered heap. + * @param p NULL, or a pointer previously returned from multi_heap_malloc() or multi_heap_realloc() for the same heap. + * @param size Desired new size for buffer. + * + * @return New buffer of 'size' containing contents of 'p', or NULL if reallocation failed. + */ +void *multi_heap_realloc(multi_heap_handle_t heap, void *p, size_t size); + + +/** @brief Return the size that a particular pointer was allocated with. + * + * @param heap Handle to a registered heap. + * @param p Pointer, must have been previously returned from multi_heap_malloc() or multi_heap_realloc() for the same heap. + * + * @return Size of the memory allocated at this block. May be more than the original size argument, due + * to padding and minimum block sizes. + */ +size_t multi_heap_get_allocated_size(multi_heap_handle_t heap, void *p); + + +/** @brief Register a new heap for use + * + * This function initialises a heap at the specified address, and returns a handle for future heap operations. + * + * There is no equivalent function for deregistering a heap - if all blocks in the heap are free, you can immediately start using the memory for other purposes. + * + * @param start Start address of the memory to use for a new heap. + * @param size Size (in bytes) of the new heap. + * + * @return Handle of a new heap ready for use, or NULL if the heap region was too small to be initialised. + */ +multi_heap_handle_t multi_heap_register(void *start, size_t size); + + +/** @brief Associate a private lock pointer with a heap + * + * The lock argument is supplied to the MULTI_HEAP_LOCK() and MULTI_HEAP_UNLOCK() macros, defined in multi_heap_platform.h. + * + * The lock in question must be recursive. + * + * When the heap is first registered, the associated lock is NULL. + * + * @param heap Handle to a registered heap. + * @param lock Optional pointer to a locking structure to associate with this heap. + */ +void multi_heap_set_lock(multi_heap_handle_t heap, void* lock); + +/** @brief Dump heap information to stdout + * + * For debugging purposes, this function dumps information about every block in the heap to stdout. + * + * @param heap Handle to a registered heap. + */ +void multi_heap_dump(multi_heap_handle_t heap); + +/** @brief Check heap integrity + * + * Walks the heap and checks all heap data structures are valid. If any errors are detected, an error-specific message + * can be optionally printed to stderr. Print behaviour can be overriden at compile time by defining + * MULTI_CHECK_FAIL_PRINTF in multi_heap_platform.h. + * + * @param heap Handle to a registered heap. + * @param print_errors If true, errors will be printed to stderr. + * @return true if heap is valid, false otherwise. + */ +bool multi_heap_check(multi_heap_handle_t heap, bool print_errors); + +/** @brief Return free heap size + * + * Returns the number of bytes available in the heap. + * + * Equivalent to the total_free_bytes member returned by multi_heap_get_heap_info(). + * + * Note that the heap may be fragmented, so the actual maximum size for a single malloc() may be lower. To know this + * size, see the largest_free_block member returned by multi_heap_get_heap_info(). + * + * @param heap Handle to a registered heap. + * @return Number of free bytes. + */ +size_t multi_heap_free_size(multi_heap_handle_t heap); + +/** @brief Return the lifetime minimum free heap size + * + * Equivalent to the minimum_free_bytes member returned by multi_heap_get_info(). + * + * Returns the lifetime "low water mark" of possible values returned from multi_free_heap_size(), for the specified + * heap. + * + * @param heap Handle to a registered heap. + * @return Number of free bytes. + */ +size_t multi_heap_minimum_free_size(multi_heap_handle_t heap); + +/** @brief Structure to access heap metadata via multi_heap_get_info */ +typedef struct { + size_t total_free_bytes; ///< Total free bytes in the heap. Equivalent to multi_free_heap_size(). + size_t total_allocated_bytes; ///< Total bytes allocated to data in the heap. + size_t largest_free_block; ///< Size of largest free block in the heap. This is the largest malloc-able size. + size_t minimum_free_bytes; ///< Lifetime minimum free heap size. Equivalent to multi_minimum_free_heap_size(). + size_t allocated_blocks; ///< Number of (variable size) blocks allocated in the heap. + size_t free_blocks; ///< Number of (variable size) free blocks in the heap. + size_t total_blocks; ///< Total number of (variable size) blocks in the heap. +} multi_heap_info_t; + +/** @brief Return metadata about a given heap + * + * Fills a multi_heap_info_t structure with information about the specified heap. + * + * @param heap Handle to a registered heap. + * @param info Pointer to a structure to fill with heap metadata. + */ +void multi_heap_get_info(multi_heap_handle_t heap, multi_heap_info_t *info); + +#ifdef __cplusplus +} +#endif diff --git a/components/heap/linker.lf b/components/heap/linker.lf new file mode 100644 index 00000000..a2be01e1 --- /dev/null +++ b/components/heap/linker.lf @@ -0,0 +1,7 @@ +[mapping:heap] +archive: libheap.a +entries: + heap_tlsf (noflash) + multi_heap (noflash) + if HEAP_POISONING_DISABLED = n: + multi_heap_poisoning (noflash) diff --git a/components/heap/multi_heap.c b/components/heap/multi_heap.c new file mode 100644 index 00000000..3ba3d3c0 --- /dev/null +++ b/components/heap/multi_heap.c @@ -0,0 +1,376 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include +#include +#include +#include +#include +#include +#include "heap_tlsf.h" +#include +#include "multi_heap_internal.h" + +/* Note: Keep platform-specific parts in this header, this source + file should depend on libc only */ +#include "multi_heap_platform.h" + +/* Defines compile-time configuration macros */ +#include "multi_heap_config.h" + +#ifndef MULTI_HEAP_POISONING +/* if no heap poisoning, public API aliases directly to these implementations */ +void *multi_heap_malloc(multi_heap_handle_t heap, size_t size) + __attribute__((alias("multi_heap_malloc_impl"))); + +void *multi_heap_aligned_alloc(multi_heap_handle_t heap, size_t size, size_t alignment) + __attribute__((alias("multi_heap_aligned_alloc_impl"))); + +void multi_heap_aligned_free(multi_heap_handle_t heap, void *p) + __attribute__((alias("multi_heap_free_impl"))); + +void multi_heap_free(multi_heap_handle_t heap, void *p) + __attribute__((alias("multi_heap_free_impl"))); + +void *multi_heap_realloc(multi_heap_handle_t heap, void *p, size_t size) + __attribute__((alias("multi_heap_realloc_impl"))); + +size_t multi_heap_get_allocated_size(multi_heap_handle_t heap, void *p) + __attribute__((alias("multi_heap_get_allocated_size_impl"))); + +multi_heap_handle_t multi_heap_register(void *start, size_t size) + __attribute__((alias("multi_heap_register_impl"))); + +void multi_heap_get_info(multi_heap_handle_t heap, multi_heap_info_t *info) + __attribute__((alias("multi_heap_get_info_impl"))); + +size_t multi_heap_free_size(multi_heap_handle_t heap) + __attribute__((alias("multi_heap_free_size_impl"))); + +size_t multi_heap_minimum_free_size(multi_heap_handle_t heap) + __attribute__((alias("multi_heap_minimum_free_size_impl"))); + +void *multi_heap_get_block_address(multi_heap_block_handle_t block) + __attribute__((alias("multi_heap_get_block_address_impl"))); + +void *multi_heap_get_block_owner(multi_heap_block_handle_t block) +{ + return NULL; +} + +#endif + +#define ALIGN(X) ((X) & ~(sizeof(void *)-1)) +#define ALIGN_UP(X) ALIGN((X)+sizeof(void *)-1) +#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) + + +typedef struct multi_heap_info { + void *lock; + size_t free_bytes; + size_t minimum_free_bytes; + size_t pool_size; + tlsf_t heap_data; +} heap_t; + +/* Return true if this block is free. */ +static inline bool is_free(const block_header_t *block) +{ + return ((block->size & 0x01) != 0); +} + +/* Data size of the block (excludes this block's header) */ +static inline size_t block_data_size(const block_header_t *block) +{ + return (block->size & ~0x03); +} + +/* Check a block is valid for this heap. Used to verify parameters. */ +static void assert_valid_block(const heap_t *heap, const block_header_t *block) +{ + pool_t pool = tlsf_get_pool(heap->heap_data); + void *ptr = block_to_ptr(block); + + MULTI_HEAP_ASSERT((ptr >= pool) && + (ptr < pool + heap->pool_size), + (uintptr_t)ptr); +} + +void *multi_heap_get_block_address_impl(multi_heap_block_handle_t block) +{ + void *ptr = block_to_ptr(block); + return (ptr); +} + +size_t multi_heap_get_allocated_size_impl(multi_heap_handle_t heap, void *p) +{ + return tlsf_block_size(p); +} + +multi_heap_handle_t multi_heap_register_impl(void *start_ptr, size_t size) +{ + assert(start_ptr); + if(size < (tlsf_size(NULL) + tlsf_block_size_min() + sizeof(heap_t))) { + //Region too small to be a heap. + return NULL; + } + + heap_t *result = (heap_t *)start_ptr; + size -= sizeof(heap_t); + + result->heap_data = tlsf_create_with_pool(start_ptr + sizeof(heap_t), size, 0); + if(!result->heap_data) { + return NULL; + } + + result->lock = NULL; + result->free_bytes = size - tlsf_size(result->heap_data); + result->pool_size = size; + result->minimum_free_bytes = result->free_bytes; + return result; +} + +void multi_heap_set_lock(multi_heap_handle_t heap, void *lock) +{ + heap->lock = lock; +} + +void inline multi_heap_internal_lock(multi_heap_handle_t heap) +{ + MULTI_HEAP_LOCK(heap->lock); +} + +void inline multi_heap_internal_unlock(multi_heap_handle_t heap) +{ + MULTI_HEAP_UNLOCK(heap->lock); +} + +multi_heap_block_handle_t multi_heap_get_first_block(multi_heap_handle_t heap) +{ + assert(heap != NULL); + pool_t pool = tlsf_get_pool(heap->heap_data); + block_header_t* block = offset_to_block(pool, -(int)block_header_overhead); + + return (multi_heap_block_handle_t)block; +} + +multi_heap_block_handle_t multi_heap_get_next_block(multi_heap_handle_t heap, multi_heap_block_handle_t block) +{ + assert(heap != NULL); + assert_valid_block(heap, block); + block_header_t* next = block_next(block); + + if(block_data_size(next) == 0) { + //Last block: + return NULL; + } else { + return (multi_heap_block_handle_t)next; + } + +} + +bool multi_heap_is_free(multi_heap_block_handle_t block) +{ + return is_free(block); +} + +void *multi_heap_malloc_impl(multi_heap_handle_t heap, size_t size) +{ + if (size == 0 || heap == NULL) { + return NULL; + } + + + multi_heap_internal_lock(heap); + void *result = tlsf_malloc(heap->heap_data, size); + if(result) { + heap->free_bytes -= tlsf_block_size(result); + if (heap->free_bytes < heap->minimum_free_bytes) { + heap->minimum_free_bytes = heap->free_bytes; + } + } + multi_heap_internal_unlock(heap); + + return result; +} + +void multi_heap_free_impl(multi_heap_handle_t heap, void *p) +{ + + if (heap == NULL || p == NULL) { + return; + } + + assert_valid_block(heap, p); + + multi_heap_internal_lock(heap); + heap->free_bytes += tlsf_block_size(p); + tlsf_free(heap->heap_data, p); + multi_heap_internal_unlock(heap); +} + +void *multi_heap_realloc_impl(multi_heap_handle_t heap, void *p, size_t size) +{ + assert(heap != NULL); + + if (p == NULL) { + return multi_heap_malloc_impl(heap, size); + } + + assert_valid_block(heap, p); + + if (heap == NULL) { + return NULL; + } + + multi_heap_internal_lock(heap); + size_t previous_block_size = tlsf_block_size(p); + void *result = tlsf_realloc(heap->heap_data, p, size); + if(result) { + heap->free_bytes += previous_block_size; + heap->free_bytes -= tlsf_block_size(result); + if (heap->free_bytes < heap->minimum_free_bytes) { + heap->minimum_free_bytes = heap->free_bytes; + } + } + + multi_heap_internal_unlock(heap); + + return result; +} + +void *multi_heap_aligned_alloc_impl_offs(multi_heap_handle_t heap, size_t size, size_t alignment, size_t offset) +{ + if(heap == NULL) { + return NULL; + } + + if(!size) { + return NULL; + } + + //Alignment must be a power of two: + if(((alignment & (alignment - 1)) != 0) ||(!alignment)) { + return NULL; + } + + multi_heap_internal_lock(heap); + void *result = tlsf_memalign_offs(heap->heap_data, alignment, size, offset); + if(result) { + heap->free_bytes -= tlsf_block_size(result); + if(heap->free_bytes < heap->minimum_free_bytes) { + heap->minimum_free_bytes = heap->free_bytes; + } + } + multi_heap_internal_unlock(heap); + + return result; +} + + +void *multi_heap_aligned_alloc_impl(multi_heap_handle_t heap, size_t size, size_t alignment) +{ + return multi_heap_aligned_alloc_impl_offs(heap, size, alignment, 0); +} + +bool multi_heap_check(multi_heap_handle_t heap, bool print_errors) +{ + (void)print_errors; + bool valid = true; + assert(heap != NULL); + + multi_heap_internal_lock(heap); + if(tlsf_check(heap->heap_data)) { + valid = false; + } + + if(tlsf_check_pool(tlsf_get_pool(heap->heap_data))) { + valid = false; + } + + multi_heap_internal_unlock(heap); + return valid; +} + +static void multi_heap_dump_tlsf(void* ptr, size_t size, int used, void* user) +{ + (void)user; + MULTI_HEAP_STDERR_PRINTF("Block %p data, size: %d bytes, Free: %s \n", + (void *)ptr, + size, + used ? "No" : "Yes"); +} + +void multi_heap_dump(multi_heap_handle_t heap) +{ + assert(heap != NULL); + + multi_heap_internal_lock(heap); + MULTI_HEAP_STDERR_PRINTF("Showing data for heap: %p \n", (void *)heap); + tlsf_walk_pool(tlsf_get_pool(heap->heap_data), multi_heap_dump_tlsf, NULL); + multi_heap_internal_unlock(heap); +} + +size_t multi_heap_free_size_impl(multi_heap_handle_t heap) +{ + if (heap == NULL) { + return 0; + } + + return heap->free_bytes; +} + +size_t multi_heap_minimum_free_size_impl(multi_heap_handle_t heap) +{ + if (heap == NULL) { + return 0; + } + + return heap->minimum_free_bytes; +} + +static void multi_heap_get_info_tlsf(void* ptr, size_t size, int used, void* user) +{ + multi_heap_info_t *info = user; + + if(used) { + info->allocated_blocks++; + } else { + info->free_blocks++; + + if(size > info->largest_free_block ) { + info->largest_free_block = size; + } + } + + info->total_blocks++; +} + +void multi_heap_get_info_impl(multi_heap_handle_t heap, multi_heap_info_t *info) +{ + memset(info, 0, sizeof(multi_heap_info_t)); + + if (heap == NULL) { + return; + } + + multi_heap_internal_lock(heap); + tlsf_walk_pool(tlsf_get_pool(heap->heap_data), multi_heap_get_info_tlsf, info); + info->total_allocated_bytes = (heap->pool_size - tlsf_size(heap->heap_data)) - heap->free_bytes; + info->minimum_free_bytes = heap->minimum_free_bytes; + info->total_free_bytes = heap->free_bytes; + info->largest_free_block = tlsf_fit_size(heap->heap_data, info->largest_free_block); + multi_heap_internal_unlock(heap); +} diff --git a/components/heap/multi_heap_config.h b/components/heap/multi_heap_config.h new file mode 100644 index 00000000..13d30cfd --- /dev/null +++ b/components/heap/multi_heap_config.h @@ -0,0 +1,31 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#ifdef ESP_PLATFORM +#include "sdkconfig.h" +#include "soc/soc.h" +#include "soc/soc_caps.h" +#endif + +/* Configuration macros for multi-heap */ + +#ifdef CONFIG_HEAP_POISONING_LIGHT +#define MULTI_HEAP_POISONING +#endif + +#ifdef CONFIG_HEAP_POISONING_COMPREHENSIVE +#define MULTI_HEAP_POISONING +#define MULTI_HEAP_POISONING_SLOW +#endif diff --git a/components/heap/multi_heap_internal.h b/components/heap/multi_heap_internal.h new file mode 100644 index 00000000..5be4dee6 --- /dev/null +++ b/components/heap/multi_heap_internal.h @@ -0,0 +1,76 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +/* Opaque handle to a heap block */ +typedef const struct block_header_t *multi_heap_block_handle_t; + +/* Internal definitions for the "implementation" of the multi_heap API, + as defined in multi_heap.c. + + If heap poisioning is disabled, these are aliased directly to the public API. + + If heap poisoning is enabled, wrapper functions call each of these. +*/ + +void *multi_heap_malloc_impl(multi_heap_handle_t heap, size_t size); + +/* Allocate a memory region of minimum `size` bytes, aligned on `alignment`. */ +void *multi_heap_aligned_alloc_impl(multi_heap_handle_t heap, size_t size, size_t alignment); + +/* Allocate a memory region of minimum `size` bytes, where memory's `offset` is aligned on `alignment`. */ +void *multi_heap_aligned_alloc_impl_offs(multi_heap_handle_t heap, size_t size, size_t alignment, size_t offset); + +void multi_heap_free_impl(multi_heap_handle_t heap, void *p); +void *multi_heap_realloc_impl(multi_heap_handle_t heap, void *p, size_t size); +multi_heap_handle_t multi_heap_register_impl(void *start, size_t size); +void multi_heap_get_info_impl(multi_heap_handle_t heap, multi_heap_info_t *info); +size_t multi_heap_free_size_impl(multi_heap_handle_t heap); +size_t multi_heap_minimum_free_size_impl(multi_heap_handle_t heap); +size_t multi_heap_get_allocated_size_impl(multi_heap_handle_t heap, void *p); +void *multi_heap_get_block_address_impl(multi_heap_block_handle_t block); + +/* Some internal functions for heap poisoning use */ + +/* Check an allocated block's poison bytes are correct. Called by multi_heap_check(). */ +bool multi_heap_internal_check_block_poisoning(void *start, size_t size, bool is_free, bool print_errors); + +/* Fill a region of memory with the free or malloced pattern. + Called when merging blocks, to overwrite the old block header. +*/ +void multi_heap_internal_poison_fill_region(void *start, size_t size, bool is_free); + +/* Allow heap poisoning to lock/unlock the heap to avoid race conditions + if multi_heap_check() is running concurrently. +*/ +void multi_heap_internal_lock(multi_heap_handle_t heap); + +void multi_heap_internal_unlock(multi_heap_handle_t heap); + +/* Some internal functions for heap debugging code to use */ + +/* Get the handle to the first (fixed free) block in a heap */ +multi_heap_block_handle_t multi_heap_get_first_block(multi_heap_handle_t heap); + +/* Get the handle to the next block in a heap, with validation */ +multi_heap_block_handle_t multi_heap_get_next_block(multi_heap_handle_t heap, multi_heap_block_handle_t block); + +/* Test if a heap block is free */ +bool multi_heap_is_free(const multi_heap_block_handle_t block); + +/* Get the data address of a heap block */ +void *multi_heap_get_block_address(multi_heap_block_handle_t block); + +/* Get the owner identification for a heap block */ +void *multi_heap_get_block_owner(multi_heap_block_handle_t block); diff --git a/components/heap/multi_heap_platform.h b/components/heap/multi_heap_platform.h new file mode 100644 index 00000000..e6114a5e --- /dev/null +++ b/components/heap/multi_heap_platform.h @@ -0,0 +1,108 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#ifdef MULTI_HEAP_FREERTOS + +#include "freertos/FreeRTOS.h" + +#include "sdkconfig.h" +#include "esp_rom_sys.h" +#if CONFIG_IDF_TARGET_ESP32 +#include "esp32/rom/ets_sys.h" // will be removed in idf v5.0 +#elif CONFIG_IDF_TARGET_ESP32S2 +#include "esp32s2/rom/ets_sys.h" +#endif +#include + +typedef portMUX_TYPE multi_heap_lock_t; + +/* Because malloc/free can happen inside an ISR context, + we need to use portmux spinlocks here not RTOS mutexes */ +#define MULTI_HEAP_LOCK(PLOCK) do { \ + if((PLOCK) != NULL) { \ + portENTER_CRITICAL((PLOCK)); \ + } \ + } while(0) + + +#define MULTI_HEAP_UNLOCK(PLOCK) do { \ + if ((PLOCK) != NULL) { \ + portEXIT_CRITICAL((PLOCK)); \ + } \ + } while(0) + +#define MULTI_HEAP_LOCK_INIT(PLOCK) do { \ + vPortCPUInitializeMutex((PLOCK)); \ + } while(0) + +#define MULTI_HEAP_LOCK_STATIC_INITIALIZER portMUX_INITIALIZER_UNLOCKED + +/* Not safe to use std i/o while in a portmux critical section, + can deadlock, so we use the ROM equivalent functions. */ + +#define MULTI_HEAP_PRINTF esp_rom_printf +#define MULTI_HEAP_STDERR_PRINTF(MSG, ...) esp_rom_printf(MSG, __VA_ARGS__) + +inline static void multi_heap_assert(bool condition, const char *format, int line, intptr_t address) +{ + /* Can't use libc assert() here as it calls printf() which can cause another malloc() for a newlib lock. + + Also, it's useful to be able to print the memory address where corruption was detected. + */ +#ifndef NDEBUG + if(!condition) { +#ifndef CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT + esp_rom_printf(format, line, address); +#endif // CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT + abort(); + } +#else // NDEBUG + (void) condition; +#endif // NDEBUG +} + +#define MULTI_HEAP_ASSERT(CONDITION, ADDRESS) \ + multi_heap_assert((CONDITION), "CORRUPT HEAP: multi_heap.c:%d detected at 0x%08x\n", \ + __LINE__, (intptr_t)(ADDRESS)) + +#ifdef CONFIG_HEAP_TASK_TRACKING +#include +#define MULTI_HEAP_BLOCK_OWNER TaskHandle_t task; +#define MULTI_HEAP_SET_BLOCK_OWNER(HEAD) (HEAD)->task = xTaskGetCurrentTaskHandle() +#define MULTI_HEAP_GET_BLOCK_OWNER(HEAD) ((HEAD)->task) +#else +#define MULTI_HEAP_BLOCK_OWNER +#define MULTI_HEAP_SET_BLOCK_OWNER(HEAD) +#define MULTI_HEAP_GET_BLOCK_OWNER(HEAD) (NULL) +#endif + +#else // MULTI_HEAP_FREERTOS + +#include + +#define MULTI_HEAP_PRINTF printf +#define MULTI_HEAP_STDERR_PRINTF(MSG, ...) fprintf(stderr, MSG, __VA_ARGS__) +#define MULTI_HEAP_LOCK(PLOCK) (void) (PLOCK) +#define MULTI_HEAP_UNLOCK(PLOCK) (void) (PLOCK) +#define MULTI_HEAP_LOCK_INIT(PLOCK) (void) (PLOCK) +#define MULTI_HEAP_LOCK_STATIC_INITIALIZER 0 + +#define MULTI_HEAP_ASSERT(CONDITION, ADDRESS) assert((CONDITION) && "Heap corrupt") + +#define MULTI_HEAP_BLOCK_OWNER +#define MULTI_HEAP_SET_BLOCK_OWNER(HEAD) +#define MULTI_HEAP_GET_BLOCK_OWNER(HEAD) (NULL) + +#endif // MULTI_HEAP_FREERTOS diff --git a/components/heap/multi_heap_poisoning.c b/components/heap/multi_heap_poisoning.c new file mode 100644 index 00000000..ca27f3f2 --- /dev/null +++ b/components/heap/multi_heap_poisoning.c @@ -0,0 +1,426 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "multi_heap_internal.h" + +/* Note: Keep platform-specific parts in this header, this source + file should depend on libc only */ +#include "multi_heap_platform.h" + +/* Defines compile-time configuration macros */ +#include "multi_heap_config.h" + +#ifdef MULTI_HEAP_POISONING + +/* Alias MULTI_HEAP_POISONING_SLOW to SLOW for better readabilty */ +#ifdef SLOW +#error "external header has defined SLOW" +#endif +#ifdef MULTI_HEAP_POISONING_SLOW +#define SLOW 1 +#endif + +#define MALLOC_FILL_PATTERN 0xce +#define FREE_FILL_PATTERN 0xfe + +#define HEAD_CANARY_PATTERN 0xABBA1234 +#define TAIL_CANARY_PATTERN 0xBAAD5678 + + +#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) + +typedef struct { + uint32_t head_canary; + MULTI_HEAP_BLOCK_OWNER + size_t alloc_size; +} poison_head_t; + +typedef struct { + uint32_t tail_canary; +} poison_tail_t; + +#define POISON_OVERHEAD (sizeof(poison_head_t) + sizeof(poison_tail_t)) + +/* Given a "poisoned" region with pre-data header 'head', and actual data size 'alloc_size', fill in the head and tail + region checks. + + Returns the pointer to the actual usable data buffer (ie after 'head') +*/ +static uint8_t *poison_allocated_region(poison_head_t *head, size_t alloc_size) +{ + uint8_t *data = (uint8_t *)(&head[1]); /* start of data ie 'real' allocated buffer */ + poison_tail_t *tail = (poison_tail_t *)(data + alloc_size); + head->alloc_size = alloc_size; + head->head_canary = HEAD_CANARY_PATTERN; + MULTI_HEAP_SET_BLOCK_OWNER(head); + + uint32_t tail_canary = TAIL_CANARY_PATTERN; + if ((intptr_t)tail % sizeof(void *) == 0) { + tail->tail_canary = tail_canary; + } else { + /* unaligned tail_canary */ + memcpy(&tail->tail_canary, &tail_canary, sizeof(uint32_t)); + } + + return data; +} + +/* Given a pointer to some allocated data, check the head & tail poison structures (before & after it) that were + previously injected by poison_allocated_region(). + + Returns a pointer to the poison header structure, or NULL if the poison structures are corrupt. +*/ +static poison_head_t *verify_allocated_region(void *data, bool print_errors) +{ + poison_head_t *head = (poison_head_t *)((intptr_t)data - sizeof(poison_head_t)); + poison_tail_t *tail = (poison_tail_t *)((intptr_t)data + head->alloc_size); + + /* check if the beginning of the data was overwritten */ + if (head->head_canary != HEAD_CANARY_PATTERN) { + if (print_errors) { + MULTI_HEAP_STDERR_PRINTF("CORRUPT HEAP: Bad head at %p. Expected 0x%08x got 0x%08x\n", &head->head_canary, + HEAD_CANARY_PATTERN, head->head_canary); + } + return NULL; + } + + /* check if the end of the data was overrun */ + uint32_t canary; + if ((intptr_t)tail % sizeof(void *) == 0) { + canary = tail->tail_canary; + } else { + /* tail is unaligned */ + memcpy(&canary, &tail->tail_canary, sizeof(canary)); + } + if (canary != TAIL_CANARY_PATTERN) { + if (print_errors) { + MULTI_HEAP_STDERR_PRINTF("CORRUPT HEAP: Bad tail at %p. Expected 0x%08x got 0x%08x\n", &tail->tail_canary, + TAIL_CANARY_PATTERN, canary); + } + return NULL; + } + + return head; +} + +#ifdef SLOW +/* Go through a region that should have the specified fill byte 'pattern', + verify it. + + if expect_free is true, expect FREE_FILL_PATTERN otherwise MALLOC_FILL_PATTERN. + + if swap_pattern is true, swap patterns in the buffer (ie replace MALLOC_FILL_PATTERN with FREE_FILL_PATTERN, and vice versa.) + + Returns true if verification checks out. +*/ +static bool verify_fill_pattern(void *data, size_t size, bool print_errors, bool expect_free, bool swap_pattern) +{ + const uint32_t FREE_FILL_WORD = (FREE_FILL_PATTERN << 24) | (FREE_FILL_PATTERN << 16) | (FREE_FILL_PATTERN << 8) | FREE_FILL_PATTERN; + const uint32_t MALLOC_FILL_WORD = (MALLOC_FILL_PATTERN << 24) | (MALLOC_FILL_PATTERN << 16) | (MALLOC_FILL_PATTERN << 8) | MALLOC_FILL_PATTERN; + + const uint32_t EXPECT_WORD = expect_free ? FREE_FILL_WORD : MALLOC_FILL_WORD; + const uint32_t REPLACE_WORD = expect_free ? MALLOC_FILL_WORD : FREE_FILL_WORD; + bool valid = true; + + /* Use 4-byte operations as much as possible */ + if ((intptr_t)data % 4 == 0) { + uint32_t *p = data; + while (size >= 4) { + if (*p != EXPECT_WORD) { + if (print_errors) { + MULTI_HEAP_STDERR_PRINTF("CORRUPT HEAP: Invalid data at %p. Expected 0x%08x got 0x%08x\n", p, EXPECT_WORD, *p); + } + valid = false; +#ifndef NDEBUG + /* If an assertion is going to fail as soon as we're done verifying the pattern, leave the rest of the + buffer contents as-is for better post-mortem analysis + */ + swap_pattern = false; +#endif + } + if (swap_pattern) { + *p = REPLACE_WORD; + } + p++; + size -= 4; + } + data = p; + } + + uint8_t *p = data; + for (size_t i = 0; i < size; i++) { + if (p[i] != (uint8_t)EXPECT_WORD) { + if (print_errors) { + MULTI_HEAP_STDERR_PRINTF("CORRUPT HEAP: Invalid data at %p. Expected 0x%02x got 0x%02x\n", p, (uint8_t)EXPECT_WORD, *p); + } + valid = false; +#ifndef NDEBUG + swap_pattern = false; // same as above +#endif + } + if (swap_pattern) { + p[i] = (uint8_t)REPLACE_WORD; + } + } + return valid; +} +#endif + +void *multi_heap_aligned_alloc(multi_heap_handle_t heap, size_t size, size_t alignment) +{ + if (!size) { + return NULL; + } + + if (size > SIZE_MAX - POISON_OVERHEAD) { + return NULL; + } + + multi_heap_internal_lock(heap); + poison_head_t *head = multi_heap_aligned_alloc_impl_offs(heap, size + POISON_OVERHEAD, + alignment, sizeof(poison_head_t)); + uint8_t *data = NULL; + if (head != NULL) { + data = poison_allocated_region(head, size); +#ifdef SLOW + /* check everything we got back is FREE_FILL_PATTERN & swap for MALLOC_FILL_PATTERN */ + bool ret = verify_fill_pattern(data, size, true, true, true); + assert( ret ); +#endif + } else { + multi_heap_internal_unlock(heap); + return NULL; + } + + multi_heap_internal_unlock(heap); + + return data; +} + +void *multi_heap_malloc(multi_heap_handle_t heap, size_t size) +{ + if (!size) { + return NULL; + } + + if(size > SIZE_MAX - POISON_OVERHEAD) { + return NULL; + } + + multi_heap_internal_lock(heap); + poison_head_t *head = multi_heap_malloc_impl(heap, size + POISON_OVERHEAD); + uint8_t *data = NULL; + if (head != NULL) { + data = poison_allocated_region(head, size); +#ifdef SLOW + /* check everything we got back is FREE_FILL_PATTERN & swap for MALLOC_FILL_PATTERN */ + bool ret = verify_fill_pattern(data, size, true, true, true); + assert( ret ); +#endif + } + + multi_heap_internal_unlock(heap); + return data; +} + +void multi_heap_free(multi_heap_handle_t heap, void *p) +{ + if (p == NULL) { + return; + } + multi_heap_internal_lock(heap); + + poison_head_t *head = verify_allocated_region(p, true); + assert(head != NULL); + + #ifdef SLOW + /* replace everything with FREE_FILL_PATTERN, including the poison head/tail */ + memset(head, FREE_FILL_PATTERN, + head->alloc_size + POISON_OVERHEAD); + #endif + multi_heap_free_impl(heap, head); + + multi_heap_internal_unlock(heap); +} + +void multi_heap_aligned_free(multi_heap_handle_t heap, void *p) +{ + multi_heap_free(heap, p); +} + +void *multi_heap_realloc(multi_heap_handle_t heap, void *p, size_t size) +{ + poison_head_t *head = NULL; + poison_head_t *new_head; + void *result = NULL; + + if(size > SIZE_MAX - POISON_OVERHEAD) { + return NULL; + } + if (p == NULL) { + return multi_heap_malloc(heap, size); + } + if (size == 0) { + multi_heap_free(heap, p); + return NULL; + } + + /* p != NULL, size != 0 */ + head = verify_allocated_region(p, true); + assert(head != NULL); + + multi_heap_internal_lock(heap); + +#ifndef SLOW + new_head = multi_heap_realloc_impl(heap, head, size + POISON_OVERHEAD); + if (new_head != NULL) { + /* For "fast" poisoning, we only overwrite the head/tail of the new block so it's safe + to poison, so no problem doing this even if realloc resized in place. + */ + result = poison_allocated_region(new_head, size); + } +#else // SLOW + /* When slow poisoning is enabled, it becomes very fiddly to try and correctly fill memory when resizing in place + (where the buffer may be moved (including to an overlapping address with the old buffer), grown, or shrunk in + place.) + + For now we just malloc a new buffer, copy, and free. :| + + Note: If this ever changes, multi_heap defrag realloc test should be enabled. + */ + size_t orig_alloc_size = head->alloc_size; + + new_head = multi_heap_malloc_impl(heap, size + POISON_OVERHEAD); + if (new_head != NULL) { + result = poison_allocated_region(new_head, size); + memcpy(result, p, MIN(size, orig_alloc_size)); + multi_heap_free(heap, p); + } +#endif + + multi_heap_internal_unlock(heap); + + return result; +} + +void *multi_heap_get_block_address(multi_heap_block_handle_t block) +{ + char *head = multi_heap_get_block_address_impl(block); + return head + sizeof(poison_head_t); +} + +void *multi_heap_get_block_owner(multi_heap_block_handle_t block) +{ + return MULTI_HEAP_GET_BLOCK_OWNER((poison_head_t*)multi_heap_get_block_address_impl(block)); +} + +multi_heap_handle_t multi_heap_register(void *start, size_t size) +{ +#ifdef SLOW + if (start != NULL) { + memset(start, FREE_FILL_PATTERN, size); + } +#endif + return multi_heap_register_impl(start, size); +} + +static inline void subtract_poison_overhead(size_t *arg) { + if (*arg > POISON_OVERHEAD) { + *arg -= POISON_OVERHEAD; + } else { + *arg = 0; + } +} + +size_t multi_heap_get_allocated_size(multi_heap_handle_t heap, void *p) +{ + poison_head_t *head = verify_allocated_region(p, true); + assert(head != NULL); + size_t result = multi_heap_get_allocated_size_impl(heap, head); + return result; +} + +void multi_heap_get_info(multi_heap_handle_t heap, multi_heap_info_t *info) +{ + multi_heap_get_info_impl(heap, info); + /* don't count the heap poison head & tail overhead in the allocated bytes size */ + info->total_allocated_bytes -= info->allocated_blocks * POISON_OVERHEAD; + /* trim largest_free_block to account for poison overhead */ + subtract_poison_overhead(&info->largest_free_block); + /* similarly, trim total_free_bytes so there's no suggestion that + a block this big may be available. */ + subtract_poison_overhead(&info->total_free_bytes); + subtract_poison_overhead(&info->minimum_free_bytes); +} + +size_t multi_heap_free_size(multi_heap_handle_t heap) +{ + size_t r = multi_heap_free_size_impl(heap); + subtract_poison_overhead(&r); + return r; +} + +size_t multi_heap_minimum_free_size(multi_heap_handle_t heap) +{ + size_t r = multi_heap_minimum_free_size_impl(heap); + subtract_poison_overhead(&r); + return r; +} + +/* Internal hooks used by multi_heap to manage poisoning, while keeping some modularity */ + +bool multi_heap_internal_check_block_poisoning(void *start, size_t size, bool is_free, bool print_errors) +{ + if (is_free) { +#ifdef SLOW + return verify_fill_pattern(start, size, print_errors, true, false); +#else + return true; /* can only verify empty blocks in SLOW mode */ +#endif + } else { + void *data = (void *)((intptr_t)start + sizeof(poison_head_t)); + poison_head_t *head = verify_allocated_region(data, print_errors); + if (head != NULL && head->alloc_size > size - POISON_OVERHEAD) { + /* block can be bigger than alloc_size, for reasons of alignment & fragmentation, + but block can never be smaller than head->alloc_size... */ + if (print_errors) { + MULTI_HEAP_STDERR_PRINTF("CORRUPT HEAP: Size at %p expected <=0x%08x got 0x%08x\n", &head->alloc_size, + size - POISON_OVERHEAD, head->alloc_size); + } + return false; + } + return head != NULL; + } +} + +void multi_heap_internal_poison_fill_region(void *start, size_t size, bool is_free) +{ + memset(start, is_free ? FREE_FILL_PATTERN : MALLOC_FILL_PATTERN, size); +} + +#else // !MULTI_HEAP_POISONING + +#ifdef MULTI_HEAP_POISONING_SLOW +#error "MULTI_HEAP_POISONING_SLOW requires MULTI_HEAP_POISONING" +#endif + +#endif // MULTI_HEAP_POISONING diff --git a/components/heap/test/CMakeLists.txt b/components/heap/test/CMakeLists.txt new file mode 100644 index 00000000..6da69a0c --- /dev/null +++ b/components/heap/test/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRC_DIRS "." + PRIV_INCLUDE_DIRS "." + PRIV_REQUIRES cmock test_utils heap) diff --git a/components/heap/test/component.mk b/components/heap/test/component.mk new file mode 100644 index 00000000..5dd172bd --- /dev/null +++ b/components/heap/test/component.mk @@ -0,0 +1,5 @@ +# +#Component Makefile +# + +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/heap/test/test_aligned_alloc_caps.c b/components/heap/test/test_aligned_alloc_caps.c new file mode 100644 index 00000000..e6648815 --- /dev/null +++ b/components/heap/test/test_aligned_alloc_caps.c @@ -0,0 +1,147 @@ +/* + Tests for the capabilities-based memory allocator. +*/ + +#include +#include +#include "unity.h" +#include "esp_attr.h" +#include "esp_heap_caps.h" +#include "esp_spi_flash.h" +#include +#include +#include +#include + +TEST_CASE("Capabilities aligned allocator test", "[heap]") +{ + uint32_t alignments = 0; + + printf("[ALIGNED_ALLOC] Allocating from default CAP: \n"); + + for(;alignments <= 1024; alignments++) { + uint8_t *buf = (uint8_t *)memalign(alignments, (alignments + 137)); + if(((alignments & (alignments - 1)) != 0) || (!alignments)) { + TEST_ASSERT( buf == NULL ); + //printf("[ALIGNED_ALLOC] alignment: %u is not a power of two, don't allow allocation \n", aligments); + } else { + TEST_ASSERT( buf != NULL ); + printf("[ALIGNED_ALLOC] alignment required: %u \n", alignments); + printf("[ALIGNED_ALLOC] address of allocated memory: %p \n\n", (void *)buf); + //Address of obtained block must be aligned with selected value + TEST_ASSERT(((intptr_t)buf & (alignments - 1)) == 0); + + //Write some data, if it corrupts memory probably the heap + //canary verification will fail: + memset(buf, 0xA5, (alignments + 137)); + + free(buf); + } + } + + //Alloc from a non permitted area: + uint32_t *not_permitted_buf = (uint32_t *)heap_caps_aligned_alloc(alignments, (alignments + 137), MALLOC_CAP_EXEC | MALLOC_CAP_32BIT); + TEST_ASSERT( not_permitted_buf == NULL ); + +#if CONFIG_ESP32_SPIRAM_SUPPORT || CONFIG_ESP32S2_SPIRAM_SUPPORT + alignments = 0; + printf("[ALIGNED_ALLOC] Allocating from external memory: \n"); + + for(;alignments <= 1024 * 1024; alignments++) { + //Now try to take aligned memory from IRAM: + uint8_t *buf = (uint8_t *)heap_caps_aligned_alloc(alignments, 10*1024, MALLOC_CAP_SPIRAM); + if(((alignments & (alignments - 1)) != 0) || (!alignments)) { + TEST_ASSERT( buf == NULL ); + //printf("[ALIGNED_ALLOC] alignment: %u is not a power of two, don't allow allocation \n", aligments); + } else { + TEST_ASSERT( buf != NULL ); + printf("[ALIGNED_ALLOC] alignment required: %u \n", alignments); + printf("[ALIGNED_ALLOC] address of allocated memory: %p \n\n", (void *)buf); + //Address of obtained block must be aligned with selected value + TEST_ASSERT(((intptr_t)buf & (alignments - 1)) == 0); + + //Write some data, if it corrupts memory probably the heap + //canary verification will fail: + memset(buf, 0xA5, (10*1024)); + heap_caps_free(buf); + } + } +#endif + +} + +TEST_CASE("Capabilities aligned calloc test", "[heap]") +{ + uint32_t alignments = 0; + + printf("[ALIGNED_ALLOC] Allocating from default CAP: \n"); + + for(;alignments <= 1024; alignments++) { + uint8_t *buf = (uint8_t *)heap_caps_aligned_calloc(alignments, 1, (alignments + 137), MALLOC_CAP_DEFAULT); + if(((alignments & (alignments - 1)) != 0) || (!alignments)) { + TEST_ASSERT( buf == NULL ); + //printf("[ALIGNED_ALLOC] alignment: %u is not a power of two, don't allow allocation \n", aligments); + } else { + TEST_ASSERT( buf != NULL ); + printf("[ALIGNED_ALLOC] alignment required: %u \n", alignments); + printf("[ALIGNED_ALLOC] address of allocated memory: %p \n\n", (void *)buf); + //Address of obtained block must be aligned with selected value + TEST_ASSERT(((intptr_t)buf & (alignments - 1)) == 0); + + //Write some data, if it corrupts memory probably the heap + //canary verification will fail: + memset(buf, 0xA5, (alignments + 137)); + + heap_caps_free(buf); + } + } + + //Check if memory is initialized with zero: + uint8_t byte_array[1024]; + memset(&byte_array, 0, sizeof(byte_array)); + uint8_t *buf = (uint8_t *)heap_caps_aligned_calloc(1024, 1, 1024, MALLOC_CAP_DEFAULT); + TEST_ASSERT(memcmp(byte_array, buf, sizeof(byte_array)) == 0); + heap_caps_free(buf); + + //Same size, but different chunk: + buf = (uint8_t *)heap_caps_aligned_calloc(1024, 1024, 1, MALLOC_CAP_DEFAULT); + TEST_ASSERT(memcmp(byte_array, buf, sizeof(byte_array)) == 0); + heap_caps_free(buf); + + //Alloc from a non permitted area: + uint32_t *not_permitted_buf = (uint32_t *)heap_caps_aligned_calloc(alignments, 1, (alignments + 137), MALLOC_CAP_32BIT); + TEST_ASSERT( not_permitted_buf == NULL ); + +#if CONFIG_ESP32_SPIRAM_SUPPORT || CONFIG_ESP32S2_SPIRAM_SUPPORT + alignments = 0; + printf("[ALIGNED_ALLOC] Allocating from external memory: \n"); + + for(;alignments <= 1024 * 1024; alignments++) { + //Now try to take aligned memory from IRAM: + uint8_t *buf = (uint8_t *)(uint8_t *)heap_caps_aligned_calloc(alignments, 1, 10*1024, MALLOC_CAP_SPIRAM); + if(((alignments & (alignments - 1)) != 0) || (!alignments)) { + TEST_ASSERT( buf == NULL ); + //printf("[ALIGNED_ALLOC] alignment: %u is not a power of two, don't allow allocation \n", aligments); + } else { + TEST_ASSERT( buf != NULL ); + printf("[ALIGNED_ALLOC] alignment required: %u \n", alignments); + printf("[ALIGNED_ALLOC] address of allocated memory: %p \n\n", (void *)buf); + //Address of obtained block must be aligned with selected value + TEST_ASSERT(((intptr_t)buf & (alignments - 1)) == 0); + + //Write some data, if it corrupts memory probably the heap + //canary verification will fail: + memset(buf, 0xA5, (10*1024)); + heap_caps_free(buf); + } + } +#endif + +} + +TEST_CASE("aligned_alloc(0) should return a NULL pointer", "[heap]") +{ + void *p; + p = heap_caps_aligned_alloc(32, 0, MALLOC_CAP_DEFAULT); + TEST_ASSERT(p == NULL); +} diff --git a/components/heap/test/test_allocator_timings.c b/components/heap/test/test_allocator_timings.c new file mode 100644 index 00000000..0e130c95 --- /dev/null +++ b/components/heap/test/test_allocator_timings.c @@ -0,0 +1,108 @@ +#include "freertos/FreeRTOS.h" +#include +#include +#include "unity.h" +#include "esp_attr.h" +#include "esp_heap_caps.h" +#include +#include +#include +#include + +//This test only makes sense with poisoning disabled (light or comprehensive) +#if !defined(CONFIG_HEAP_POISONING_COMPREHENSIVE) && !defined(CONFIG_HEAP_POISONING_LIGHT) + +#define NUM_POINTERS 128 +#define ITERATIONS 10000 + +TEST_CASE("Heap many random allocations timings", "[heap]") +{ + void *p[NUM_POINTERS] = { 0 }; + size_t s[NUM_POINTERS] = { 0 }; + + uint32_t cycles_before; + uint64_t alloc_time_average = 0; + uint64_t free_time_average = 0; + uint64_t realloc_time_average = 0; + + for (int i = 0; i < ITERATIONS; i++) { + uint8_t n = esp_random() % NUM_POINTERS; + + if (esp_random() % 4 == 0) { + /* 1 in 4 iterations, try to realloc the buffer instead + of using malloc/free + */ + size_t new_size = esp_random() % 1024; + + cycles_before = portGET_RUN_TIME_COUNTER_VALUE(); + void *new_p = heap_caps_realloc(p[n], new_size, MALLOC_CAP_DEFAULT); + realloc_time_average = portGET_RUN_TIME_COUNTER_VALUE() - cycles_before; + + printf("realloc %p -> %p (%zu -> %zu) time spent cycles: %lld \n", p[n], new_p, s[n], new_size, realloc_time_average); + heap_caps_check_integrity(MALLOC_CAP_DEFAULT, true); + if (new_size == 0 || new_p != NULL) { + p[n] = new_p; + s[n] = new_size; + if (new_size > 0) { + memset(p[n], n, new_size); + } + } + continue; + } + + if (p[n] != NULL) { + if (s[n] > 0) { + /* Verify pre-existing contents of p[n] */ + uint8_t compare[s[n]]; + memset(compare, n, s[n]); + TEST_ASSERT(( memcmp(compare, p[n], s[n]) == 0 )); + } + TEST_ASSERT(heap_caps_check_integrity(MALLOC_CAP_DEFAULT, true)); + + cycles_before = portGET_RUN_TIME_COUNTER_VALUE(); + heap_caps_free(p[n]); + free_time_average = portGET_RUN_TIME_COUNTER_VALUE() - cycles_before; + + printf("freed %p (%zu) time spent cycles: %lld\n", p[n], s[n], free_time_average); + + if (!heap_caps_check_integrity(MALLOC_CAP_DEFAULT, true)) { + printf("FAILED iteration %d after freeing %p\n", i, p[n]); + heap_caps_dump(MALLOC_CAP_DEFAULT); + TEST_ASSERT(0); + } + } + + s[n] = rand() % 1024; + heap_caps_check_integrity(MALLOC_CAP_DEFAULT, true); + cycles_before = portGET_RUN_TIME_COUNTER_VALUE(); + p[n] = heap_caps_malloc(s[n], MALLOC_CAP_DEFAULT); + alloc_time_average = portGET_RUN_TIME_COUNTER_VALUE() - cycles_before; + + printf("malloc %p (%zu) time spent cycles: %lld \n", p[n], s[n], alloc_time_average); + + if (!heap_caps_check_integrity(MALLOC_CAP_DEFAULT, true)) { + printf("FAILED iteration %d after mallocing %p (%zu bytes)\n", i, p[n], s[n]); + heap_caps_dump(MALLOC_CAP_DEFAULT); + TEST_ASSERT(0); + } + + if (p[n] != NULL) { + memset(p[n], n, s[n]); + } + } + + for (int i = 0; i < NUM_POINTERS; i++) { + cycles_before = portGET_RUN_TIME_COUNTER_VALUE(); + heap_caps_free( p[i]); + free_time_average = portGET_RUN_TIME_COUNTER_VALUE() - cycles_before; + + if (!heap_caps_check_integrity(MALLOC_CAP_DEFAULT, true)) { + printf("FAILED during cleanup after freeing %p\n", p[i]); + heap_caps_dump(MALLOC_CAP_DEFAULT); + TEST_ASSERT(0); + } + } + + TEST_ASSERT(heap_caps_check_integrity(MALLOC_CAP_DEFAULT, true)); +} +#endif diff --git a/components/heap/test/test_diram.c b/components/heap/test/test_diram.c new file mode 100644 index 00000000..17966352 --- /dev/null +++ b/components/heap/test/test_diram.c @@ -0,0 +1,74 @@ +/* + Tests for D/IRAM support in heap capability allocator +*/ + +#include +#include +#include "unity.h" +#include "esp_heap_caps.h" +#include "soc/soc_memory_layout.h" + +#define ALLOC_SZ 1024 + +static void *malloc_block_diram(uint32_t caps) +{ + void *attempts[256] = { 0 }; // Allocate up to 256 ALLOC_SZ blocks to exhaust all non-D/IRAM memory temporarily + int count = 0; + void *result; + + while(count < sizeof(attempts)/sizeof(void *)) { + result = heap_caps_malloc(ALLOC_SZ, caps); + TEST_ASSERT_NOT_NULL_MESSAGE(result, "not enough free heap to perform test"); + + if (esp_ptr_in_diram_dram(result) || esp_ptr_in_diram_iram(result)) { + break; + } + + attempts[count] = result; + result = NULL; + count++; + } + + for (int i = 0; i < count; i++) { + free(attempts[i]); + } + + TEST_ASSERT_NOT_NULL_MESSAGE(result, "not enough D/IRAM memory is free"); + return result; +} + +TEST_CASE("Allocate D/IRAM as DRAM", "[heap]") +{ + uint32_t *dram = malloc_block_diram(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + + for (int i = 0; i < ALLOC_SZ / sizeof(uint32_t); i++) { + uint32_t v = i + 0xAAAA; + dram[i] = v; + volatile uint32_t *iram = esp_ptr_diram_dram_to_iram(dram + i); + TEST_ASSERT_EQUAL(v, dram[i]); + TEST_ASSERT_EQUAL(v, *iram); + *iram = UINT32_MAX; + TEST_ASSERT_EQUAL(UINT32_MAX, *iram); + TEST_ASSERT_EQUAL(UINT32_MAX, dram[i]); + } + + free(dram); +} + +TEST_CASE("Allocate D/IRAM as IRAM", "[heap]") +{ + uint32_t *iram = malloc_block_diram(MALLOC_CAP_EXEC); + + for (int i = 0; i < ALLOC_SZ / sizeof(uint32_t); i++) { + uint32_t v = i + 0xEEE; + iram[i] = v; + volatile uint32_t *dram = esp_ptr_diram_iram_to_dram(iram + i); + TEST_ASSERT_EQUAL_HEX32(v, iram[i]); + TEST_ASSERT_EQUAL_HEX32(v, *dram); + *dram = UINT32_MAX; + TEST_ASSERT_EQUAL_HEX32(UINT32_MAX, *dram); + TEST_ASSERT_EQUAL_HEX32(UINT32_MAX, iram[i]); + } + + free(iram); +} diff --git a/components/heap/test/test_heap_trace.c b/components/heap/test/test_heap_trace.c new file mode 100644 index 00000000..909aa31b --- /dev/null +++ b/components/heap/test/test_heap_trace.c @@ -0,0 +1,164 @@ +/* + Generic test for heap tracing support + + Only compiled in if CONFIG_HEAP_TRACING is set +*/ + +#include +#include +#include +#include +#include "sdkconfig.h" +#include "unity.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#ifdef CONFIG_HEAP_TRACING +// only compile in heap tracing tests if tracing is enabled + +#include "esp_heap_trace.h" + +TEST_CASE("heap trace leak check", "[heap]") +{ + heap_trace_record_t recs[8]; + heap_trace_init_standalone(recs, 8); + + printf("Leak check test\n"); // Print something before trace starts, or stdout allocations skew total counts + fflush(stdout); + + heap_trace_start(HEAP_TRACE_LEAKS); + + void *a = malloc(64); + memset(a, '3', 64); + + void *b = malloc(96); + memset(b, '4', 11); + + printf("a.address %p vs %p b.address %p vs %p\n", a, recs[0].address, b, recs[1].address); + + heap_trace_dump(); + TEST_ASSERT_EQUAL(2, heap_trace_get_count()); + + heap_trace_record_t trace_a, trace_b; + heap_trace_get(0, &trace_a); + heap_trace_get(1, &trace_b); + + printf("trace_a.address %p trace_bb.address %p\n", trace_a.address, trace_b.address); + + TEST_ASSERT_EQUAL_PTR(a, trace_a.address); + TEST_ASSERT_EQUAL_PTR(b, trace_b.address); + + TEST_ASSERT_EQUAL_PTR(recs[0].address, trace_a.address); + TEST_ASSERT_EQUAL_PTR(recs[1].address, trace_b.address); + + free(a); + + TEST_ASSERT_EQUAL(1, heap_trace_get_count()); + + heap_trace_get(0, &trace_b); + TEST_ASSERT_EQUAL_PTR(b, trace_b.address); + + /* buffer deletes trace_a when freed, + so trace_b at head of buffer */ + TEST_ASSERT_EQUAL_PTR(recs[0].address, trace_b.address); + + heap_trace_stop(); +} + +TEST_CASE("heap trace wrapped buffer check", "[heap]") +{ + const size_t N = 8; + heap_trace_record_t recs[N]; + heap_trace_init_standalone(recs, N); + + heap_trace_start(HEAP_TRACE_LEAKS); + + void *ptrs[N+1]; + for (int i = 0; i < N+1; i++) { + ptrs[i] = malloc(i*3); + } + + // becuase other mallocs happen as part of this control flow, + // we can't guarantee N entries of ptrs[] are in the heap check buffer. + // but we should guarantee at least the last one is + bool saw_last_ptr = false; + for (int i = 0; i < N; i++) { + heap_trace_record_t rec; + heap_trace_get(i, &rec); + if (rec.address == ptrs[N-1]) { + saw_last_ptr = true; + } + } + TEST_ASSERT(saw_last_ptr); + + void *other = malloc(6); + + heap_trace_dump(); + + for (int i = 0; i < N+1; i++) { + free(ptrs[i]); + } + + heap_trace_dump(); + + bool saw_other = false; + + for (int i = 0; i < heap_trace_get_count(); i++) { + heap_trace_record_t rec; + heap_trace_get(i, &rec); + + // none of ptr[]s should be in the heap trace any more + for (int j = 0; j < N+1; j++) { + TEST_ASSERT_NOT_EQUAL(ptrs[j], rec.address); + } + if (rec.address == other) { + saw_other = true; + } + } + + // 'other' pointer should be somewhere in the leak dump + TEST_ASSERT(saw_other); + + heap_trace_stop(); +} + +static void print_floats_task(void *ignore) +{ + heap_trace_start(HEAP_TRACE_ALL); + char buf[16] = { }; + volatile float f = 12.3456; + sprintf(buf, "%.4f", f); + TEST_ASSERT_EQUAL_STRING("12.3456", buf); + heap_trace_stop(); + + vTaskDelete(NULL); +} + +TEST_CASE("can trace allocations made by newlib", "[heap]") +{ + const size_t N = 8; + heap_trace_record_t recs[N]; + heap_trace_init_standalone(recs, N); + + /* Verifying that newlib code performs an allocation is very fiddly: + + - Printing a float allocates data associated with the task, but only the + first time a task prints a float of this length. So we do it in a one-shot task + to avoid possibility it already happened. + + - If newlib is updated this test may start failing if the printf() implementation + changes. (This version passes for both nano & regular formatting in newlib 2.2.0) + + - We also do the tracing in the task so we only capture things directly related to it. + */ + + xTaskCreate(print_floats_task, "print_float", 4096, NULL, 5, NULL); + vTaskDelay(10); + + /* has to be at least a few as newlib allocates via multiple different function calls */ + TEST_ASSERT(heap_trace_get_count() > 3); +} + + +#endif diff --git a/components/heap/test/test_leak.c b/components/heap/test/test_leak.c new file mode 100644 index 00000000..0144cd93 --- /dev/null +++ b/components/heap/test/test_leak.c @@ -0,0 +1,60 @@ +/* + Tests for a leak tag +*/ + +#include +#include "unity.h" +#include "esp_heap_caps_init.h" +#include "esp_system.h" +#include + + +static char* check_calloc(int size) +{ + char *arr = calloc(size, sizeof(char)); + TEST_ASSERT_NOT_NULL(arr); + return arr; +} + +TEST_CASE("Check for leaks (no leak)", "[heap]") +{ + char *arr = check_calloc(1000); + free(arr); +} + +TEST_CASE("Check for leaks (leak)", "[heap][ignore]") +{ + check_calloc(1000); +} + +TEST_CASE("Not check for leaks", "[heap][leaks]") +{ + check_calloc(1000); +} + +TEST_CASE("Set a leak level = 7016", "[heap][leaks=7016]") +{ + check_calloc(7000); +} + +static void test_fn(void) +{ + check_calloc(1000); +} + +TEST_CASE_MULTIPLE_STAGES("Not check for leaks in MULTIPLE_STAGES mode", "[heap][leaks]", test_fn, test_fn, test_fn); + +TEST_CASE_MULTIPLE_STAGES("Check for leaks in MULTIPLE_STAGES mode (leak)", "[heap][ignore]", test_fn, test_fn, test_fn); + +static void test_fn2(void) +{ + check_calloc(1000); + esp_restart(); +} + +static void test_fn3(void) +{ + check_calloc(1000); +} + +TEST_CASE_MULTIPLE_STAGES("Check for leaks in MULTIPLE_STAGES mode (manual reset)", "[heap][leaks][reset=SW_CPU_RESET, SW_CPU_RESET]", test_fn2, test_fn2, test_fn3); diff --git a/components/heap/test/test_malloc.c b/components/heap/test/test_malloc.c new file mode 100644 index 00000000..6f4bbd87 --- /dev/null +++ b/components/heap/test/test_malloc.c @@ -0,0 +1,134 @@ +/* + Generic test for malloc/free +*/ + +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "unity.h" +#include "esp_heap_caps.h" + +#include "sdkconfig.h" + + +static int **allocatedMem; +static int noAllocated; + + +static int tryAllocMem(void) { + int i, j; + const int allocateMaxK=1024*5; //try to allocate a max of 5MiB + + allocatedMem=malloc(sizeof(int *)*allocateMaxK); + if (!allocatedMem) return 0; + + for (i=0; i 1024) +TEST_CASE("Check if reserved DMA pool still can allocate even when malloc()'ed memory is exhausted", "[heap]") +{ + char** dmaMem=malloc(sizeof(char*)*512); + assert(dmaMem); + int m=tryAllocMem(); + int i=0; + for (i=0; i<512; i++) { + dmaMem[i]=heap_caps_malloc(1024, MALLOC_CAP_DMA); + if (dmaMem[i]==NULL) break; + } + for (int j=0; j +#include +#include "unity.h" +#include "esp_attr.h" +#include "esp_heap_caps.h" +#include "esp_spi_flash.h" +#include +#include + +#ifndef CONFIG_ESP_SYSTEM_MEMPROT_FEATURE +TEST_CASE("Capabilities allocator test", "[heap]") +{ + char *m1, *m2[10]; + int x; + size_t free8start, free32start, free8, free32; + + /* It's important we printf() something before we take the empty heap sizes, + as the first printf() in a task allocates heap resources... */ + printf("Testing capabilities allocator...\n"); + + free8start = heap_caps_get_free_size(MALLOC_CAP_8BIT); + free32start = heap_caps_get_free_size(MALLOC_CAP_32BIT); + printf("Free 8bit-capable memory (start): %dK, 32-bit capable memory %dK\n", free8start, free32start); + TEST_ASSERT(free32start >= free8start); + + printf("Allocating 10K of 8-bit capable RAM\n"); + m1= heap_caps_malloc(10*1024, MALLOC_CAP_8BIT); + printf("--> %p\n", m1); + free8 = heap_caps_get_free_size(MALLOC_CAP_8BIT); + free32 = heap_caps_get_free_size(MALLOC_CAP_32BIT); + printf("Free 8bit-capable memory (both reduced): %dK, 32-bit capable memory %dK\n", free8, free32); + //Both should have gone down by 10K; 8bit capable ram is also 32-bit capable + TEST_ASSERT(free8<=(free8start-10*1024)); + TEST_ASSERT(free32<=(free32start-10*1024)); + //Assume we got DRAM back + TEST_ASSERT((((int)m1)&0xFF000000)==0x3F000000); + free(m1); + + //The goal here is to allocate from IRAM. Since there is no external IRAM (yet) + //the following gives size of IRAM-only (not D/IRAM) memory. + size_t free_iram = heap_caps_get_free_size(MALLOC_CAP_INTERNAL) - + heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + size_t alloc32 = MIN(free_iram / 2, 10*1024) & (~3); + if(free_iram) { + printf("Freeing; allocating %u bytes of 32K-capable RAM\n", alloc32); + m1 = heap_caps_malloc(alloc32, MALLOC_CAP_32BIT); + printf("--> %p\n", m1); + //Check that we got IRAM back + TEST_ASSERT((((int)m1)&0xFF000000)==0x40000000); + free8 = heap_caps_get_free_size(MALLOC_CAP_8BIT); + free32 = heap_caps_get_free_size(MALLOC_CAP_32BIT); + printf("Free 8bit-capable memory (after 32-bit): %dK, 32-bit capable memory %dK\n", free8, free32); + //Only 32-bit should have gone down by alloc32: 32-bit isn't necessarily 8bit capable + TEST_ASSERT(free32<=(free32start-alloc32)); + TEST_ASSERT(free8==free8start); + free(m1); + } else { + printf("This platform has no 32-bit only capable RAM, jumping to next test \n"); + } + + printf("Allocating impossible caps\n"); + m1= heap_caps_malloc(10*1024, MALLOC_CAP_8BIT|MALLOC_CAP_EXEC); + printf("--> %p\n", m1); + TEST_ASSERT(m1==NULL); + + if(free_iram) { + printf("Testing changeover iram -> dram"); + // priorities will exhaust IRAM first, then start allocating from DRAM + for (x=0; x<10; x++) { + m2[x]= heap_caps_malloc(alloc32, MALLOC_CAP_32BIT); + printf("--> %p\n", m2[x]); + } + TEST_ASSERT((((int)m2[0])&0xFF000000)==0x40000000); + TEST_ASSERT((((int)m2[9])&0xFF000000)==0x3F000000); + + } else { + printf("This platform has no IRAM-only so changeover will never occur, jumping to next test\n"); + } + + printf("Test if allocating executable code still gives IRAM, even with dedicated IRAM region depleted\n"); + if(free_iram) { + // (the allocation should come from D/IRAM) + free_iram = heap_caps_get_free_size(MALLOC_CAP_EXEC); + m1= heap_caps_malloc(MIN(free_iram / 2, 10*1024), MALLOC_CAP_EXEC); + printf("--> %p\n", m1); + TEST_ASSERT((((int)m1)&0xFF000000)==0x40000000); + for (x=0; x<10; x++) free(m2[x]); + + } else { + // (the allocation should come from D/IRAM) + free_iram = heap_caps_get_free_size(MALLOC_CAP_EXEC); + m1= heap_caps_malloc(MIN(free_iram / 2, 10*1024), MALLOC_CAP_EXEC); + printf("--> %p\n", m1); + TEST_ASSERT((((int)m1)&0xFF000000)==0x40000000); + } + + free(m1); + printf("Done.\n"); +} +#endif + +#ifdef CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY +TEST_CASE("IRAM_8BIT capability test", "[heap]") +{ + uint8_t *ptr; + size_t free_size, free_size32, largest_free_size; + + /* need to print something as first printf allocates some heap */ + printf("IRAM_8BIT capability test\n"); + + free_size = heap_caps_get_free_size(MALLOC_CAP_IRAM_8BIT); + free_size32 = heap_caps_get_free_size(MALLOC_CAP_32BIT); + + largest_free_size = heap_caps_get_largest_free_block(MALLOC_CAP_IRAM_8BIT); + + ptr = heap_caps_malloc(largest_free_size, MALLOC_CAP_IRAM_8BIT); + + TEST_ASSERT((((int)ptr)&0xFF000000)==0x40000000); + + TEST_ASSERT(heap_caps_get_free_size(MALLOC_CAP_IRAM_8BIT) == (free_size - heap_caps_get_allocated_size(ptr))); + TEST_ASSERT(heap_caps_get_free_size(MALLOC_CAP_32BIT) == (free_size32 - heap_caps_get_allocated_size(ptr))); + + free(ptr); +} +#endif + +TEST_CASE("heap_caps metadata test", "[heap]") +{ + /* need to print something as first printf allocates some heap */ + printf("heap_caps metadata test\n"); + heap_caps_print_heap_info(MALLOC_CAP_8BIT); + + multi_heap_info_t original; + heap_caps_get_info(&original, MALLOC_CAP_8BIT); + + void *b = heap_caps_malloc(original.largest_free_block, MALLOC_CAP_8BIT); + TEST_ASSERT_NOT_NULL(b); + + printf("After allocating %d bytes:\n", original.largest_free_block); + heap_caps_print_heap_info(MALLOC_CAP_8BIT); + + multi_heap_info_t after; + heap_caps_get_info(&after, MALLOC_CAP_8BIT); + TEST_ASSERT(after.largest_free_block <= original.largest_free_block); + TEST_ASSERT(after.total_free_bytes <= original.total_free_bytes); + + free(b); + heap_caps_get_info(&after, MALLOC_CAP_8BIT); + + printf("\n\n After test, heap status:\n"); + heap_caps_print_heap_info(MALLOC_CAP_8BIT); + + /* Allow some leeway here, because LWIP sometimes allocates up to 144 bytes in the background + as part of timer management. + */ + TEST_ASSERT_INT32_WITHIN(200, after.total_free_bytes, original.total_free_bytes); + TEST_ASSERT_INT32_WITHIN(200, after.largest_free_block, original.largest_free_block); + TEST_ASSERT(after.minimum_free_bytes < original.total_free_bytes); +} + +/* Small function runs from IRAM to check that malloc/free/realloc + all work OK when cache is disabled... +*/ +static IRAM_ATTR __attribute__((noinline)) bool iram_malloc_test(void) +{ + spi_flash_guard_get()->start(); // Disables flash cache + + bool result = true; + void *x = heap_caps_malloc(64, MALLOC_CAP_EXEC); + result = result && (x != NULL); + void *y = heap_caps_realloc(x, 32, MALLOC_CAP_EXEC); + result = result && (y != NULL); + heap_caps_free(y); + + spi_flash_guard_get()->end(); // Re-enables flash cache + + return result; +} + + +TEST_CASE("heap_caps_xxx functions work with flash cache disabled", "[heap]") +{ + TEST_ASSERT( iram_malloc_test() ); +} + +#ifdef CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS +TEST_CASE("When enabled, allocation operation failure generates an abort", "[heap][reset=abort,SW_CPU_RESET]") +{ + const size_t stupid_allocation_size = (128 * 1024 * 1024); + void *ptr = heap_caps_malloc(stupid_allocation_size, MALLOC_CAP_DEFAULT); + (void)ptr; + TEST_FAIL_MESSAGE("should not be reached"); +} +#endif + +static bool called_user_failed_hook = false; + +void heap_caps_alloc_failed_hook(size_t requested_size, uint32_t caps, const char *function_name) +{ + printf("%s was called but failed to allocate %d bytes with 0x%X capabilities. \n",function_name, requested_size, caps); + called_user_failed_hook = true; +} + +TEST_CASE("user provided alloc failed hook must be called when allocation fails", "[heap]") +{ + TEST_ASSERT(heap_caps_register_failed_alloc_callback(heap_caps_alloc_failed_hook) == ESP_OK); + + const size_t stupid_allocation_size = (128 * 1024 * 1024); + void *ptr = heap_caps_malloc(stupid_allocation_size, MALLOC_CAP_DEFAULT); + TEST_ASSERT(called_user_failed_hook != false); + + called_user_failed_hook = false; + ptr = heap_caps_realloc(ptr, stupid_allocation_size, MALLOC_CAP_DEFAULT); + TEST_ASSERT(called_user_failed_hook != false); + + called_user_failed_hook = false; + ptr = heap_caps_aligned_alloc(0x200, stupid_allocation_size, MALLOC_CAP_DEFAULT); + TEST_ASSERT(called_user_failed_hook != false); + + (void)ptr; +} + +TEST_CASE("allocation with invalid capability should also trigger the alloc failed hook", "[heap]") +{ + const size_t allocation_size = 64; + const uint32_t invalid_cap = MALLOC_CAP_INVALID; + + TEST_ASSERT(heap_caps_register_failed_alloc_callback(heap_caps_alloc_failed_hook) == ESP_OK); + + called_user_failed_hook = false; + void *ptr = heap_caps_malloc(allocation_size, invalid_cap); + TEST_ASSERT(called_user_failed_hook != false); + + called_user_failed_hook = false; + ptr = heap_caps_realloc(ptr, allocation_size, invalid_cap); + TEST_ASSERT(called_user_failed_hook != false); + + called_user_failed_hook = false; + ptr = heap_caps_aligned_alloc(0x200, allocation_size, invalid_cap); + TEST_ASSERT(called_user_failed_hook != false); + + (void)ptr; +} diff --git a/components/heap/test/test_realloc.c b/components/heap/test/test_realloc.c new file mode 100644 index 00000000..60a7749f --- /dev/null +++ b/components/heap/test/test_realloc.c @@ -0,0 +1,67 @@ +/* + Generic test for realloc +*/ + +#include +#include +#include "unity.h" +#include "sdkconfig.h" +#include "esp_heap_caps.h" +#include "soc/soc_memory_layout.h" + + +#ifndef CONFIG_HEAP_POISONING_COMPREHENSIVE +/* (can't realloc in place if comprehensive is enabled) */ + +TEST_CASE("realloc shrink buffer in place", "[heap]") +{ + void *x = malloc(64); + TEST_ASSERT(x); + void *y = realloc(x, 48); + TEST_ASSERT_EQUAL_PTR(x, y); +} + +#endif + +#ifndef CONFIG_ESP_SYSTEM_MEMPROT_FEATURE +TEST_CASE("realloc shrink buffer with EXEC CAPS", "[heap]") +{ + const size_t buffer_size = 64; + + void *x = heap_caps_malloc(buffer_size, MALLOC_CAP_EXEC); + TEST_ASSERT(x); + void *y = heap_caps_realloc(x, buffer_size - 16, MALLOC_CAP_EXEC); + TEST_ASSERT(y); + + //y needs to fall in a compatible memory area of IRAM: + TEST_ASSERT(esp_ptr_executable(y)|| esp_ptr_in_iram(y) || esp_ptr_in_diram_iram(y)); + + free(y); +} + +TEST_CASE("realloc move data to a new heap type", "[heap]") +{ + const char *test = "I am some test content to put in the heap"; + char buf[64]; + memset(buf, 0xEE, 64); + strlcpy(buf, test, 64); + + char *a = malloc(64); + memcpy(a, buf, 64); + // move data from 'a' to IRAM + char *b = heap_caps_realloc(a, 64, MALLOC_CAP_EXEC); + TEST_ASSERT_NOT_NULL(b); + TEST_ASSERT_NOT_EQUAL(a, b); + TEST_ASSERT(heap_caps_check_integrity(MALLOC_CAP_INVALID, true)); + TEST_ASSERT_EQUAL_HEX32_ARRAY(buf, b, 64 / sizeof(uint32_t)); + + // Move data back to DRAM + char *c = heap_caps_realloc(b, 48, MALLOC_CAP_8BIT); + TEST_ASSERT_NOT_NULL(c); + TEST_ASSERT_NOT_EQUAL(b, c); + TEST_ASSERT(heap_caps_check_integrity(MALLOC_CAP_INVALID, true)); + TEST_ASSERT_EQUAL_HEX8_ARRAY(buf, c, 48); + + free(c); +} +#endif diff --git a/components/heap/test/test_runtime_heap_reg.c b/components/heap/test/test_runtime_heap_reg.c new file mode 100644 index 00000000..92e81d8f --- /dev/null +++ b/components/heap/test/test_runtime_heap_reg.c @@ -0,0 +1,72 @@ +/* + Tests for registering new heap memory at runtime +*/ + +#include +#include "unity.h" +#include "esp_heap_caps_init.h" +#include "esp_system.h" +#include + + +/* NOTE: This is not a well-formed unit test, it leaks memory */ +TEST_CASE("Allocate new heap at runtime", "[heap][ignore]") +{ + const size_t BUF_SZ = 1000; + const size_t HEAP_OVERHEAD_MAX = 200; + void *buffer = malloc(BUF_SZ); + TEST_ASSERT_NOT_NULL(buffer); + uint32_t before_free = esp_get_free_heap_size(); + TEST_ESP_OK( heap_caps_add_region((intptr_t)buffer, (intptr_t)buffer + BUF_SZ) ); + uint32_t after_free = esp_get_free_heap_size(); + printf("Before %u after %u\n", before_free, after_free); + /* allow for some 'heap overhead' from accounting structures */ + TEST_ASSERT(after_free >= before_free + BUF_SZ - HEAP_OVERHEAD_MAX); +} + +/* NOTE: This is not a well-formed unit test, it leaks memory and + may fail if run twice in a row without a reset. +*/ +TEST_CASE("Allocate new heap with new capability", "[heap][ignore]") +{ + const size_t BUF_SZ = 100; +#ifdef CONFIG_ESP_SYSTEM_MEMPROT_FEATURE + const size_t ALLOC_SZ = 32; +#else + const size_t ALLOC_SZ = 64; // More than half of BUF_SZ +#endif + const uint32_t MALLOC_CAP_INVENTED = (1 << 30); /* this must be unused in esp_heap_caps.h */ + + /* no memory exists to provide this capability */ + TEST_ASSERT_NULL( heap_caps_malloc(ALLOC_SZ, MALLOC_CAP_INVENTED) ); + + void *buffer = malloc(BUF_SZ); + TEST_ASSERT_NOT_NULL(buffer); + uint32_t caps[SOC_MEMORY_TYPE_NO_PRIOS] = { MALLOC_CAP_INVENTED }; + TEST_ESP_OK( heap_caps_add_region_with_caps(caps, (intptr_t)buffer, (intptr_t)buffer + BUF_SZ) ); + + /* ta-da, it's now possible! */ + TEST_ASSERT_NOT_NULL( heap_caps_malloc(ALLOC_SZ, MALLOC_CAP_INVENTED) ); +} + +/* NOTE: This is not a well-formed unit test. + * If run twice without a reset, it will failed. + */ + +TEST_CASE("Add .bss memory to heap region runtime", "[heap][ignore]") +{ +#define BUF_SZ 1000 +#define HEAP_OVERHEAD_MAX 200 + static uint8_t s_buffer[BUF_SZ]; + + printf("s_buffer start %08x end %08x\n", (intptr_t)s_buffer, (intptr_t)s_buffer + BUF_SZ); + uint32_t before_free = esp_get_free_heap_size(); + TEST_ESP_OK( heap_caps_add_region((intptr_t)s_buffer, (intptr_t)s_buffer + BUF_SZ) ); + uint32_t after_free = esp_get_free_heap_size(); + printf("Before %u after %u\n", before_free, after_free); + /* allow for some 'heap overhead' from accounting structures */ + TEST_ASSERT(after_free >= before_free + BUF_SZ - HEAP_OVERHEAD_MAX); + + /* Twice add must be failed */ + TEST_ASSERT( (heap_caps_add_region((intptr_t)s_buffer, (intptr_t)s_buffer + BUF_SZ) != ESP_OK) ); +} diff --git a/components/heap/test_multi_heap_host/Makefile b/components/heap/test_multi_heap_host/Makefile new file mode 100644 index 00000000..0827e346 --- /dev/null +++ b/components/heap/test_multi_heap_host/Makefile @@ -0,0 +1,54 @@ +TEST_PROGRAM=test_multi_heap +all: $(TEST_PROGRAM) + +ifneq ($(filter clean,$(MAKECMDGOALS)),) +.NOTPARALLEL: # prevent make clean racing the other targets +endif + +SOURCE_FILES = $(abspath \ + ../multi_heap.c \ + ../heap_tlsf.c \ + ../multi_heap_poisoning.c \ + test_multi_heap.cpp \ + main.cpp \ + ) + +INCLUDE_FLAGS = -I../include -I../../../tools/catch + +GCOV ?= gcov + +CPPFLAGS += $(INCLUDE_FLAGS) -D CONFIG_LOG_DEFAULT_LEVEL -g -fstack-protector-all -m32 -DCONFIG_HEAP_POISONING_COMPREHENSIVE +CFLAGS += -Wall -Werror -fprofile-arcs -ftest-coverage +CXXFLAGS += -std=c++11 -Wall -Werror -fprofile-arcs -ftest-coverage +LDFLAGS += -lstdc++ -fprofile-arcs -ftest-coverage -m32 + +OBJ_FILES = $(filter %.o, $(SOURCE_FILES:.cpp=.o) $(SOURCE_FILES:.c=.o)) + +COVERAGE_FILES = $(OBJ_FILES:.o=.gc*) + +$(TEST_PROGRAM): $(OBJ_FILES) + g++ $(LDFLAGS) -o $(TEST_PROGRAM) $(OBJ_FILES) + +$(OUTPUT_DIR): + mkdir -p $(OUTPUT_DIR) + +test: $(TEST_PROGRAM) + ./$(TEST_PROGRAM) + +$(COVERAGE_FILES): $(TEST_PROGRAM) test + +coverage.info: $(COVERAGE_FILES) + find ../ -name "*.gcno" -exec $(GCOV) -r -pb {} + + lcov --capture --directory $(abspath ../) --no-external --output-file coverage.info --gcov-tool $(GCOV) + +coverage_report: coverage.info + genhtml coverage.info --output-directory coverage_report + @echo "Coverage report is in coverage_report/index.html" + +clean: + rm -f $(OBJ_FILES) $(TEST_PROGRAM) + rm -f $(COVERAGE_FILES) *.gcov + rm -rf coverage_report/ + rm -f coverage.info + +.PHONY: clean all test diff --git a/components/heap/test_multi_heap_host/main.cpp b/components/heap/test_multi_heap_host/main.cpp new file mode 100644 index 00000000..0c7c351f --- /dev/null +++ b/components/heap/test_multi_heap_host/main.cpp @@ -0,0 +1,2 @@ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" diff --git a/components/heap/test_multi_heap_host/test_all_configs.sh b/components/heap/test_multi_heap_host/test_all_configs.sh new file mode 100644 index 00000000..ce144d0d --- /dev/null +++ b/components/heap/test_multi_heap_host/test_all_configs.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# +# Run the test suite with all configurations enabled +# + +FAIL=0 + +for FLAGS in "CONFIG_HEAP_POISONING_NONE" "CONFIG_HEAP_POISONING_LIGHT" "CONFIG_HEAP_POISONING_COMPREHENSIVE" ; do + echo "==== Testing with config: ${FLAGS} ====" + CPPFLAGS="-D${FLAGS}" make clean test || FAIL=1 +done + +make clean + +if [ $FAIL == 0 ]; then + echo "All configurations passed" +else + echo "Some configurations failed, see log." + exit 1 +fi diff --git a/components/heap/test_multi_heap_host/test_multi_heap.cpp b/components/heap/test_multi_heap_host/test_multi_heap.cpp new file mode 100644 index 00000000..837d915b --- /dev/null +++ b/components/heap/test_multi_heap_host/test_multi_heap.cpp @@ -0,0 +1,508 @@ +#include "catch.hpp" +#include "multi_heap.h" + +#include "../multi_heap_config.h" + +#include +#include + +static void *__malloc__(size_t bytes) +{ + return malloc(bytes); +} + +static void __free__(void *ptr) +{ + free(ptr); +} + +/* Insurance against accidentally using libc heap functions in tests */ +#undef free +#define free #error +#undef malloc +#define malloc #error +#undef calloc +#define calloc #error +#undef realloc +#define realloc #error + +TEST_CASE("multi_heap simple allocations", "[multi_heap]") +{ + uint8_t small_heap[4 * 1024]; + + multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap)); + + size_t test_alloc_size = (multi_heap_free_size(heap) + 4) / 2; + + printf("New heap:\n"); + multi_heap_dump(heap); + printf("*********************\n"); + + uint8_t *buf = (uint8_t *)multi_heap_malloc(heap, test_alloc_size); + + printf("small_heap %p buf %p\n", small_heap, buf); + REQUIRE( buf != NULL ); + REQUIRE((intptr_t)buf >= (intptr_t)small_heap); + REQUIRE( (intptr_t)buf < (intptr_t)(small_heap + sizeof(small_heap))); + + REQUIRE( multi_heap_get_allocated_size(heap, buf) >= test_alloc_size ); + REQUIRE( multi_heap_get_allocated_size(heap, buf) < test_alloc_size + 16); + + memset(buf, 0xEE, test_alloc_size); + + REQUIRE( multi_heap_malloc(heap, test_alloc_size) == NULL ); + + multi_heap_free(heap, buf); + + printf("Empty?\n"); + multi_heap_dump(heap); + printf("*********************\n"); + + /* Now there should be space for another allocation */ + buf = (uint8_t *)multi_heap_malloc(heap, test_alloc_size); + REQUIRE( buf != NULL ); + multi_heap_free(heap, buf); + + REQUIRE( multi_heap_free_size(heap) > multi_heap_minimum_free_size(heap) ); +} + + +TEST_CASE("multi_heap fragmentation", "[multi_heap]") +{ + uint8_t small_heap[4 * 1024]; + multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap)); + + const size_t alloc_size = 128; + + void *p[4]; + for (int i = 0; i < 4; i++) { + multi_heap_dump(heap); + REQUIRE( multi_heap_check(heap, true) ); + p[i] = multi_heap_malloc(heap, alloc_size); + printf("%d = %p ****->\n", i, p[i]); + multi_heap_dump(heap); + REQUIRE( p[i] != NULL ); + } + + printf("allocated %p %p %p %p\n", p[0], p[1], p[2], p[3]); + + REQUIRE( multi_heap_malloc(heap, alloc_size * 5) == NULL ); /* no room to allocate 5*alloc_size now */ + + printf("4 allocations:\n"); + multi_heap_dump(heap); + printf("****************\n"); + + multi_heap_free(heap, p[0]); + multi_heap_free(heap, p[1]); + multi_heap_free(heap, p[3]); + + printf("1 allocations:\n"); + multi_heap_dump(heap); + printf("****************\n"); + + void *big = multi_heap_malloc(heap, alloc_size * 3); + //Blocks in TLSF are organized in different form, so this makes no sense + multi_heap_free(heap, big); + + multi_heap_free(heap, p[2]); + + printf("0 allocations:\n"); + multi_heap_dump(heap); + printf("****************\n"); + + big = multi_heap_malloc(heap, alloc_size * 2); + //Blocks in TLSF are organized in different form, so this makes no sense + multi_heap_free(heap, big); +} + +/* Test that malloc/free does not leave free space fragmented */ +TEST_CASE("multi_heap defrag", "[multi_heap]") +{ + void *p[4]; + uint8_t small_heap[4 * 1024]; + multi_heap_info_t info, info2; + multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap)); + + printf("0 ---\n"); + multi_heap_dump(heap); + REQUIRE( multi_heap_check(heap, true) ); + multi_heap_get_info(heap, &info); + REQUIRE( 0 == info.allocated_blocks ); + REQUIRE( 1 == info.free_blocks ); + + printf("1 ---\n"); + p[0] = multi_heap_malloc(heap, 128); + p[1] = multi_heap_malloc(heap, 32); + multi_heap_dump(heap); + REQUIRE( multi_heap_check(heap, true) ); + + printf("2 ---\n"); + multi_heap_free(heap, p[0]); + p[2] = multi_heap_malloc(heap, 64); + multi_heap_dump(heap); + REQUIRE( p[2] == p[0] ); + REQUIRE( multi_heap_check(heap, true) ); + + printf("3 ---\n"); + multi_heap_free(heap, p[2]); + p[3] = multi_heap_malloc(heap, 32); + multi_heap_dump(heap); + REQUIRE( p[3] == p[0] ); + REQUIRE( multi_heap_check(heap, true) ); + + multi_heap_get_info(heap, &info2); + REQUIRE( 2 == info2.allocated_blocks ); + REQUIRE( 2 == info2.free_blocks ); + + multi_heap_free(heap, p[0]); + multi_heap_free(heap, p[1]); + multi_heap_get_info(heap, &info2); + REQUIRE( 0 == info2.allocated_blocks ); + REQUIRE( 1 == info2.free_blocks ); + REQUIRE( info.total_free_bytes == info2.total_free_bytes ); +} + +/* Test that malloc/free does not leave free space fragmented + Note: With fancy poisoning, realloc is implemented as malloc-copy-free and this test does not apply. + */ +#ifndef MULTI_HEAP_POISONING_SLOW +TEST_CASE("multi_heap defrag realloc", "[multi_heap]") +{ + void *p[4]; + uint8_t small_heap[4 * 1024]; + multi_heap_info_t info, info2; + multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap)); + + printf("0 ---\n"); + multi_heap_dump(heap); + REQUIRE( multi_heap_check(heap, true) ); + multi_heap_get_info(heap, &info); + REQUIRE( 0 == info.allocated_blocks ); + REQUIRE( 1 == info.free_blocks ); + + printf("1 ---\n"); + p[0] = multi_heap_malloc(heap, 128); + p[1] = multi_heap_malloc(heap, 32); + multi_heap_dump(heap); + REQUIRE( multi_heap_check(heap, true) ); + + printf("2 ---\n"); + p[2] = multi_heap_realloc(heap, p[0], 64); + multi_heap_dump(heap); + REQUIRE( p[2] == p[0] ); + REQUIRE( multi_heap_check(heap, true) ); + + printf("3 ---\n"); + p[3] = multi_heap_realloc(heap, p[2], 32); + multi_heap_dump(heap); + REQUIRE( p[3] == p[0] ); + REQUIRE( multi_heap_check(heap, true) ); + + multi_heap_get_info(heap, &info2); + REQUIRE( 2 == info2.allocated_blocks ); + REQUIRE( 2 == info2.free_blocks ); + + multi_heap_free(heap, p[0]); + multi_heap_free(heap, p[1]); + multi_heap_get_info(heap, &info2); + REQUIRE( 0 == info2.allocated_blocks ); + REQUIRE( 1 == info2.free_blocks ); + REQUIRE( info.total_free_bytes == info2.total_free_bytes ); +} +#endif + + +void multi_heap_allocation_impl(int heap_size) +{ + uint8_t *big_heap = (uint8_t *) __malloc__(2*heap_size); + const int NUM_POINTERS = 64; + + printf("Running multi-allocation test with heap_size %d...\n", heap_size); + + REQUIRE( big_heap ); + multi_heap_handle_t heap = multi_heap_register(big_heap, heap_size); + + void *p[NUM_POINTERS] = { 0 }; + size_t s[NUM_POINTERS] = { 0 }; + + const size_t initial_free = multi_heap_free_size(heap); + + const int ITERATIONS = 10000; + + for (int i = 0; i < ITERATIONS; i++) { + /* check all pointers allocated so far are valid inside big_heap */ + for (int j = 0; j < NUM_POINTERS; j++) { + if (p[j] != NULL) { + } + } + + uint8_t n = rand() % NUM_POINTERS; + + if (rand() % 4 == 0) { + /* 1 in 4 iterations, try to realloc the buffer instead + of using malloc/free + */ + size_t new_size = rand() % 1024; + void *new_p = multi_heap_realloc(heap, p[n], new_size); + printf("realloc %p -> %p (%zu -> %zu)\n", p[n], new_p, s[n], new_size); + multi_heap_check(heap, true); + if (new_size == 0 || new_p != NULL) { + p[n] = new_p; + s[n] = new_size; + if (new_size > 0) { + REQUIRE( p[n] >= big_heap ); + REQUIRE( p[n] < big_heap + heap_size ); + memset(p[n], n, new_size); + } + } + continue; + } + if (p[n] != NULL) { + if (s[n] > 0) { + /* Verify pre-existing contents of p[n] */ + uint8_t compare[s[n]]; + memset(compare, n, s[n]); + /*REQUIRE*/assert( memcmp(compare, p[n], s[n]) == 0 ); + } + REQUIRE( multi_heap_check(heap, true) ); + multi_heap_free(heap, p[n]); + printf("freed %p (%zu)\n", p[n], s[n]); + if (!multi_heap_check(heap, true)) { + printf("FAILED iteration %d after freeing %p\n", i, p[n]); + multi_heap_dump(heap); + REQUIRE(0); + } + } + + s[n] = rand() % 1024; + REQUIRE( multi_heap_check(heap, true) ); + p[n] = multi_heap_malloc(heap, s[n]); + printf("malloc %p (%zu)\n", p[n], s[n]); + if (p[n] != NULL) { + REQUIRE( p[n] >= big_heap ); + REQUIRE( p[n] < big_heap + heap_size ); + } + if (!multi_heap_check(heap, true)) { + printf("FAILED iteration %d after mallocing %p (%zu bytes)\n", i, p[n], s[n]); + multi_heap_dump(heap); + REQUIRE(0); + } + if (p[n] != NULL) { + memset(p[n], n, s[n]); + } + } + + for (int i = 0; i < NUM_POINTERS; i++) { + multi_heap_free(heap, p[i]); + if (!multi_heap_check(heap, true)) { + printf("FAILED during cleanup after freeing %p\n", p[i]); + multi_heap_dump(heap); + REQUIRE(0); + } + } + + REQUIRE( initial_free == multi_heap_free_size(heap) ); + __free__(big_heap); +} + +TEST_CASE("multi_heap many random allocations", "[multi_heap]") +{ + size_t poolsize[] = { 15, 255, 4095, 8191 }; + for (size_t i = 0; i < sizeof(poolsize)/sizeof(size_t); i++) { + multi_heap_allocation_impl(poolsize[i] * 1024); + } +} + +TEST_CASE("multi_heap_get_info() function", "[multi_heap]") +{ + uint8_t heapdata[4 * 1024]; + multi_heap_handle_t heap = multi_heap_register(heapdata, sizeof(heapdata)); + multi_heap_info_t before, after, freed; + + multi_heap_get_info(heap, &before); + printf("before: total_free_bytes %zu\ntotal_allocated_bytes %zu\nlargest_free_block %zu\nminimum_free_bytes %zu\nallocated_blocks %zu\nfree_blocks %zu\ntotal_blocks %zu\n", + before.total_free_bytes, + before.total_allocated_bytes, + before.largest_free_block, + before.minimum_free_bytes, + before.allocated_blocks, + before.free_blocks, + before.total_blocks); + + REQUIRE( 0 == before.allocated_blocks ); + REQUIRE( 0 == before.total_allocated_bytes ); + REQUIRE( before.total_free_bytes == before.minimum_free_bytes ); + + void *x = multi_heap_malloc(heap, 32); + multi_heap_get_info(heap, &after); + printf("after: total_free_bytes %zu\ntotal_allocated_bytes %zu\nlargest_free_block %zu\nminimum_free_bytes %zu\nallocated_blocks %zu\nfree_blocks %zu\ntotal_blocks %zu\n", + after.total_free_bytes, + after.total_allocated_bytes, + after.largest_free_block, + after.minimum_free_bytes, + after.allocated_blocks, + after.free_blocks, + after.total_blocks); + + REQUIRE( 1 == after.allocated_blocks ); + REQUIRE( 32 == after.total_allocated_bytes ); + REQUIRE( after.minimum_free_bytes < before.minimum_free_bytes); + REQUIRE( after.minimum_free_bytes > 0 ); + + multi_heap_free(heap, x); + multi_heap_get_info(heap, &freed); + printf("freed: total_free_bytes %zu\ntotal_allocated_bytes %zu\nlargest_free_block %zu\nminimum_free_bytes %zu\nallocated_blocks %zu\nfree_blocks %zu\ntotal_blocks %zu\n", + freed.total_free_bytes, + freed.total_allocated_bytes, + freed.largest_free_block, + freed.minimum_free_bytes, + freed.allocated_blocks, + freed.free_blocks, + freed.total_blocks); + + REQUIRE( 0 == freed.allocated_blocks ); + REQUIRE( 0 == freed.total_allocated_bytes ); + REQUIRE( before.total_free_bytes == freed.total_free_bytes ); + REQUIRE( after.minimum_free_bytes == freed.minimum_free_bytes ); +} + +TEST_CASE("multi_heap minimum-size allocations", "[multi_heap]") +{ + uint8_t heapdata[4096]; + void *p[sizeof(heapdata) / sizeof(void *)] = {NULL}; + const size_t NUM_P = sizeof(p) / sizeof(void *); + size_t allocated_size = 0; + multi_heap_handle_t heap = multi_heap_register(heapdata, sizeof(heapdata)); + size_t before_free = multi_heap_free_size(heap); + + size_t i; + for (i = 0; i < NUM_P; i++) { + //TLSF minimum block size is 4 bytes + p[i] = multi_heap_malloc(heap, 1); + if (p[i] == NULL) { + break; + } + } + + REQUIRE( i < NUM_P); // Should have run out of heap before we ran out of pointers + printf("Allocated %zu minimum size chunks\n", i); + + REQUIRE(multi_heap_free_size(heap) < before_free); + multi_heap_check(heap, true); + + /* Free in random order */ + bool has_allocations = true; + while (has_allocations) { + i = rand() % NUM_P; + multi_heap_free(heap, p[i]); + p[i] = NULL; + multi_heap_check(heap, true); + + has_allocations = false; + for (i = 0; i < NUM_P && !has_allocations; i++) { + has_allocations = (p[i] != NULL); + } + } + + /* all freed! */ + REQUIRE( before_free == multi_heap_free_size(heap) ); +} + +TEST_CASE("multi_heap_realloc()", "[multi_heap]") +{ + const uint32_t PATTERN = 0xABABDADA; + uint8_t small_heap[4 * 1024]; + multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap)); + + uint32_t *a = (uint32_t *)multi_heap_malloc(heap, 64); + uint32_t *b = (uint32_t *)multi_heap_malloc(heap, 32); + REQUIRE( a != NULL ); + REQUIRE( b != NULL ); + REQUIRE( b > a); /* 'b' takes the block after 'a' */ + + *a = PATTERN; + + uint32_t *c = (uint32_t *)multi_heap_realloc(heap, a, 72); + REQUIRE( multi_heap_check(heap, true)); + REQUIRE( c != NULL ); + REQUIRE( c > b ); /* 'a' moves, 'c' takes the block after 'b' */ + REQUIRE( *c == PATTERN ); + +#ifndef MULTI_HEAP_POISONING_SLOW + // "Slow" poisoning implementation doesn't reallocate in place, so these + // test will fail... + + uint32_t *d = (uint32_t *)multi_heap_realloc(heap, c, 36); + REQUIRE( multi_heap_check(heap, true) ); + REQUIRE( c == d ); /* 'c' block should be shrunk in-place */ + REQUIRE( *d == PATTERN); + + uint32_t *e = (uint32_t *)multi_heap_malloc(heap, 64); + REQUIRE( multi_heap_check(heap, true)); + REQUIRE( a == e ); /* 'e' takes the block formerly occupied by 'a' */ + + multi_heap_free(heap, d); + uint32_t *f = (uint32_t *)multi_heap_realloc(heap, b, 64); + REQUIRE( multi_heap_check(heap, true) ); + REQUIRE( f == b ); /* 'b' should be extended in-place, over space formerly occupied by 'd' */ + +#ifdef MULTI_HEAP_POISONING +#define TOO_MUCH 7420 + 1 +#else +#define TOO_MUCH 7420 + 1 +#endif + /* not enough contiguous space left in the heap */ + uint32_t *g = (uint32_t *)multi_heap_realloc(heap, e, TOO_MUCH); + REQUIRE( g == NULL ); + + multi_heap_free(heap, f); + /* try again */ + g = (uint32_t *)multi_heap_realloc(heap, e, 128); + REQUIRE( multi_heap_check(heap, true) ); + REQUIRE( e == g ); /* 'g' extends 'e' in place, into the space formerly held by 'f' */ +#endif +} + +// TLSF only accepts heaps aligned to 4-byte boundary so +// only aligned allocation tests make sense. +TEST_CASE("multi_heap aligned allocations", "[multi_heap]") +{ + uint8_t test_heap[4 * 1024]; + multi_heap_handle_t heap = multi_heap_register(test_heap, sizeof(test_heap)); + uint32_t aligments = 0; // starts from alignment by 4-byte boundary + size_t old_size = multi_heap_free_size(heap); + size_t leakage = 1024; + printf("[ALIGNED_ALLOC] heap_size before: %d \n", old_size); + + printf("New heap:\n"); + multi_heap_dump(heap); + printf("*********************\n"); + + for(;aligments <= 256; aligments++) { + + //Use some stupid size value to test correct alignment even in strange + //memory layout objects: + uint8_t *buf = (uint8_t *)multi_heap_aligned_alloc(heap, (aligments + 137), aligments ); + if(((aligments & (aligments - 1)) != 0) || (!aligments)) { + REQUIRE( buf == NULL ); + } else { + REQUIRE( buf != NULL ); + REQUIRE((intptr_t)buf >= (intptr_t)test_heap); + REQUIRE((intptr_t)buf < (intptr_t)(test_heap + sizeof(test_heap))); + + printf("[ALIGNED_ALLOC] alignment required: %u \n", aligments); + printf("[ALIGNED_ALLOC] address of allocated memory: %p \n\n", (void *)buf); + //Address of obtained block must be aligned with selected value + REQUIRE(((intptr_t)buf & (aligments - 1)) == 0); + + //Write some data, if it corrupts memory probably the heap + //canary verification will fail: + memset(buf, 0xA5, (aligments + 137)); + + multi_heap_free(heap, buf); + } + } + + printf("[ALIGNED_ALLOC] heap_size after: %d \n", multi_heap_free_size(heap)); + REQUIRE((old_size - multi_heap_free_size(heap)) <= leakage); +} diff --git a/components/platform_config/CMakeLists.txt b/components/platform_config/CMakeLists.txt index 4090bbc2..808e01d6 100644 --- a/components/platform_config/CMakeLists.txt +++ b/components/platform_config/CMakeLists.txt @@ -1,6 +1,6 @@ idf_component_register( SRC_DIRS . INCLUDE_DIRS . - PRIV_REQUIRES tools newlib console esp_common freertos services + PRIV_REQUIRES tools newlib console esp_common freertos tools REQUIRES nvs_flash json ) diff --git a/components/platform_config/nvs_utilities.c b/components/platform_config/nvs_utilities.c index e3ce3c64..e7ebbc31 100644 --- a/components/platform_config/nvs_utilities.c +++ b/components/platform_config/nvs_utilities.c @@ -14,7 +14,7 @@ #include "nvs_flash.h" #include "nvs_utilities.h" #include "platform_config.h" -#include "globdefs.h" +#include "tools.h" const char current_namespace[] = "config"; const char settings_partition[] = "settings"; diff --git a/components/platform_config/platform_config.c b/components/platform_config/platform_config.c index b5dc863d..81ee8d5b 100644 --- a/components/platform_config/platform_config.c +++ b/components/platform_config/platform_config.c @@ -21,7 +21,6 @@ #include "platform_config.h" #include "nvs_utilities.h" #include "platform_esp32.h" -#include "trace.h" #include #include #include "esp_system.h" @@ -38,7 +37,7 @@ #include "cJSON.h" #include "freertos/timers.h" #include "freertos/event_groups.h" -#include "globdefs.h" +#include "tools.h" #define CONFIG_COMMIT_DELAY 1000 #define LOCK_MAX_WAIT 20*CONFIG_COMMIT_DELAY diff --git a/components/platform_config/platform_config.h b/components/platform_config/platform_config.h index 6215e6b2..cda90644 100644 --- a/components/platform_config/platform_config.h +++ b/components/platform_config/platform_config.h @@ -8,9 +8,20 @@ #ifdef __cplusplus extern "C" { #endif -#ifdef __cplusplus -} -#endif + +#define PARSE_PARAM(S,P,C,V) do { \ + char *__p; \ + if ((__p = strcasestr(S, P)) && (__p = strchr(__p, C))) V = atoi(__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))) { \ + while (*++__p == ' '); \ + sscanf(__p,"%" #I "[^,]", V); \ + } \ +} while (0) + #define DECLARE_SET_DEFAULT(t) void config_set_default_## t (const char *key, t value); #define DECLARE_GET_NUM(t) esp_err_t config_get_## t (const char *key, t * value); #ifndef FREE_RESET @@ -45,3 +56,7 @@ esp_err_t config_set_value(nvs_type_t nvs_type, const char *key, const void * va nvs_type_t config_get_item_type(cJSON * entry); void * config_safe_alloc_get_entry_value(nvs_type_t nvs_type, cJSON * entry); +#ifdef __cplusplus +} +#endif + diff --git a/components/platform_console/CMakeLists.txt b/components/platform_console/CMakeLists.txt index ad32232c..2c41c179 100644 --- a/components/platform_console/CMakeLists.txt +++ b/components/platform_console/CMakeLists.txt @@ -8,7 +8,7 @@ idf_component_register( SRCS cmd_config.c INCLUDE_DIRS . REQUIRES nvs_flash - PRIV_REQUIRES console app_update tools services spi_flash platform_config vfs pthread wifi-manager platform_config newlib telnet display squeezelite services) + 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 ) target_add_binary_data( __idf_platform_console presets.json BINARY) diff --git a/components/platform_console/app_squeezelite/CMakeLists.txt b/components/platform_console/app_squeezelite/CMakeLists.txt index 10899949..f7f98b9f 100644 --- a/components/platform_console/app_squeezelite/CMakeLists.txt +++ b/components/platform_console/app_squeezelite/CMakeLists.txt @@ -1,7 +1,7 @@ idf_build_get_property(idf_path IDF_PATH) idf_component_register( SRCS cmd_squeezelite.c INCLUDE_DIRS . - PRIV_REQUIRES spi_flash bootloader_support partition_table bootloader_support console codecs squeezelite newlib pthread tools platform_config display services) + PRIV_REQUIRES spi_flash bootloader_support partition_table bootloader_support console codecs squeezelite newlib pthread tools platform_config display tools) target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=feof") diff --git a/components/platform_console/app_squeezelite/cmd_squeezelite.c b/components/platform_console/app_squeezelite/cmd_squeezelite.c index 036ec691..1326bf55 100644 --- a/components/platform_console/app_squeezelite/cmd_squeezelite.c +++ b/components/platform_console/app_squeezelite/cmd_squeezelite.c @@ -12,7 +12,7 @@ #include "platform_esp32.h" #include "platform_config.h" #include "esp_app_format.h" -#include "globdefs.h" +#include "tools.h" extern esp_err_t process_recovery_ota(const char * bin_url, char * bin_buffer, uint32_t length); static const char * TAG = "squeezelite_cmd"; diff --git a/components/platform_console/cmd_config.c b/components/platform_console/cmd_config.c index fa5486f5..d263fbbb 100644 --- a/components/platform_console/cmd_config.c +++ b/components/platform_console/cmd_config.c @@ -14,11 +14,10 @@ #include "string.h" #include "stdio.h" #include "platform_config.h" -#include "trace.h" #include "messaging.h" #include "accessors.h" #include "adac.h" -#include "globdefs.h" +#include "tools.h" #include "cJSON.h" #include "cmd_i2ctools.h" diff --git a/components/platform_console/cmd_i2ctools.c b/components/platform_console/cmd_i2ctools.c index f5073fac..a559b271 100644 --- a/components/platform_console/cmd_i2ctools.c +++ b/components/platform_console/cmd_i2ctools.c @@ -20,7 +20,7 @@ #include "messaging.h" #include "display.h" #include "config.h" -#include "globdefs.h" +#include "tools.h" #include "adac.h" #define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */ diff --git a/components/platform_console/cmd_nvs.c b/components/platform_console/cmd_nvs.c index 2690e4af..d4897d66 100644 --- a/components/platform_console/cmd_nvs.c +++ b/components/platform_console/cmd_nvs.c @@ -26,8 +26,7 @@ extern "C" { #include "nvs_utilities.h" #include "platform_console.h" #include "messaging.h" -#include "globdefs.h" -#include "trace.h" +#include "tools.h" extern esp_err_t network_wifi_erase_legacy(); extern esp_err_t network_wifi_erase_known_ap(); diff --git a/components/platform_console/cmd_system.c b/components/platform_console/cmd_system.c index f07af71e..4dac6206 100644 --- a/components/platform_console/cmd_system.c +++ b/components/platform_console/cmd_system.c @@ -31,8 +31,7 @@ #include "driver/uart.h" // for the uart driver access #include "messaging.h" #include "platform_console.h" -#include "trace.h" -#include "globdefs.h" +#include "tools.h" #ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS #pragma message("Runtime stats enabled") diff --git a/components/platform_console/platform_console.c b/components/platform_console/platform_console.c index 9afbe2b6..eb553fc5 100644 --- a/components/platform_console/platform_console.c +++ b/components/platform_console/platform_console.c @@ -26,17 +26,26 @@ #include "trace.h" #include "platform_config.h" #include "telnet.h" +#include "tools.h" #include "messaging.h" #include "config.h" -pthread_t thread_console; +static pthread_t thread_console; static void * console_thread(); void console_start(); static const char * TAG = "console"; extern bool bypass_network_manager; extern void register_squeezelite(); +static EXT_RAM_ATTR QueueHandle_t uart_queue; +static EXT_RAM_ATTR struct { + uint8_t _buf[128]; + StaticRingbuffer_t _ringbuf; + RingbufHandle_t handle; + QueueSetHandle_t queue_set; +} stdin_redir; + /* Prompt to be printed before each line. * This can be customized, made dynamic, etc. */ @@ -50,7 +59,7 @@ const char* recovery_prompt = LOG_COLOR_E "recovery-squeezelite-esp32> " LOG_RES #define MOUNT_PATH "/data" #define HISTORY_PATH MOUNT_PATH "/history.txt" -esp_err_t run_command(char * line); +static esp_err_t run_command(char * line); #define ADD_TO_JSON(o,t,n) if (t->n) cJSON_AddStringToObject(o,QUOTE(n),t->n); #define ADD_PARMS_TO_CMD(o,t,n) { cJSON * parms = ParmsToJSON(&t.n->hdr); if(parms) cJSON_AddItemToObject(o,QUOTE(n),parms); } cJSON * cmdList; @@ -230,17 +239,43 @@ void process_autoexec(){ } } +static ssize_t stdin_read(int fd, void* data, size_t size) { + size_t bytes = -1; + + while (1) { + QueueSetMemberHandle_t activated = xQueueSelectFromSet(stdin_redir.queue_set, portMAX_DELAY); + + if (activated == uart_queue) { + uart_event_t event; + + xQueueReceive(uart_queue, &event, 0); + + if (event.type == UART_DATA) { + bytes = uart_read_bytes(CONFIG_ESP_CONSOLE_UART_NUM, data, size < event.size ? size : event.size, 0); + // we have to do our own line ending translation here + for (int i = 0; i < bytes; i++) if (((char*)data)[i] == '\r') ((char*)data)[i] = '\n'; + break; + } + } else if (xRingbufferCanRead(stdin_redir.handle, activated)) { + char *p = xRingbufferReceiveUpTo(stdin_redir.handle, &bytes, 0, size); + // we might receive strings, replace null by \n + for (int i = 0; i < bytes; i++) if (p[i] == '\0' || p[i] == '\r') p[i] = '\n'; + memcpy(data, p, bytes); + vRingbufferReturnItem(stdin_redir.handle, p); + break; + } + } + + return bytes; +} + +static int stdin_dummy(const char * path, int flags, int mode) { return 0; } void initialize_console() { - - /* Disable buffering on stdin */ - setvbuf(stdin, NULL, _IONBF, 0); - -/* Minicom, screen, idf_monitor send CR when ENTER key is pressed */ - 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_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF); - + /* 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); + /* Move the caret to the beginning of the next line on '\n' */ + esp_vfs_dev_uart_set_tx_line_endings(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. @@ -252,10 +287,28 @@ void initialize_console() { ESP_ERROR_CHECK(uart_param_config(CONFIG_ESP_CONSOLE_UART_NUM, &uart_config)); /* Install UART driver for interrupt-driven reads and writes */ - ESP_ERROR_CHECK( uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM, 256, 0, 0, NULL, 0)); - + ESP_ERROR_CHECK( uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM, 256, 0, 3, &uart_queue, 0)); + /* Tell VFS to use UART driver */ esp_vfs_dev_uart_use_driver(CONFIG_ESP_CONSOLE_UART_NUM); + + /* re-direct stdin to our own driver so we can gather data from various sources */ + stdin_redir.queue_set = xQueueCreateSet(2); + stdin_redir.handle = xRingbufferCreateStatic(sizeof(stdin_redir._buf), RINGBUF_TYPE_BYTEBUF, stdin_redir._buf, &stdin_redir._ringbuf); + xRingbufferAddToQueueSetRead(stdin_redir.handle, stdin_redir.queue_set); + xQueueAddToSet(uart_queue, stdin_redir.queue_set); + + const esp_vfs_t vfs = { + .flags = ESP_VFS_FLAG_DEFAULT, + .open = stdin_dummy, + .read = stdin_read, + }; + + ESP_ERROR_CHECK(esp_vfs_register("/dev/console", &vfs, NULL)); + freopen("/dev/console", "r", stdin); + + /* Disable buffering on stdin */ + setvbuf(stdin, NULL, _IONBF, 0); /* Initialize the console */ esp_console_config_t console_config = { .max_cmdline_args = 28, @@ -283,20 +336,14 @@ void initialize_console() { //linenoiseHistoryLoad(HISTORY_PATH); } +bool console_push(const char *data, size_t size) { + return xRingbufferSend(stdin_redir.handle, data, size, pdMS_TO_TICKS(100)) == pdPASS; +} + void console_start() { - if(!is_serial_suppressed()){ - initialize_console(); - } - else { - /* Initialize the console */ - esp_console_config_t console_config = { .max_cmdline_args = 28, - .max_cmdline_length = 600, - #if CONFIG_LOG_COLORS - .hint_color = atoi(LOG_COLOR_CYAN) - #endif - }; - ESP_ERROR_CHECK(esp_console_init(&console_config)); - } + /* we always run console b/c telnet sends commands to stdin */ + initialize_console(); + /* Register commands */ MEMTRACE_PRINT_DELTA_MESSAGE("Registering help command"); esp_console_register_help_command(); @@ -320,67 +367,58 @@ void console_start() { MEMTRACE_PRINT_DELTA_MESSAGE("Registering i2c commands"); register_i2ctools(); - if(!is_serial_suppressed()){ - printf("\n"); - if(is_recovery_running){ - printf("****************************************************************\n" - "RECOVERY APPLICATION\n" - "This mode is used to flash Squeezelite into the OTA partition\n" - "****\n\n"); - } - printf("Type 'help' to get the list of commands.\n" - "Use UP/DOWN arrows to navigate through command history.\n" - "Press TAB when typing command name to auto-complete.\n" - "\n"); - if(!is_recovery_running){ - printf("To automatically execute lines at startup:\n" - "\tSet NVS variable autoexec (U8) = 1 to enable, 0 to disable automatic execution.\n" - "\tSet NVS variable autoexec[1~9] (string)to a command that should be executed automatically\n"); - } - printf("\n\n"); - - /* Figure out if the terminal supports escape sequences */ - int probe_status = linenoiseProbe(); - if (probe_status) { /* zero indicates success */ - printf("\n****************************\n" - "Your terminal application does not support escape sequences.\n" - "Line editing and history features are disabled.\n" - "On Windows, try using Putty instead.\n" - "****************************\n"); - linenoiseSetDumbMode(1); - #if CONFIG_LOG_COLORS - /* Since the terminal doesn't support escape sequences, - * don't use color codes in the prompt. - */ - if(is_recovery_running){ - recovery_prompt= "recovery-squeezelite-esp32>"; - } - prompt = "squeezelite-esp32> "; - - #endif //CONFIG_LOG_COLORS - } - esp_pthread_cfg_t cfg = esp_pthread_get_default_config(); - cfg.thread_name= "console"; - cfg.inherit_cfg = true; - if(is_recovery_running){ - prompt = recovery_prompt; - cfg.stack_size = 4096 ; - } - MEMTRACE_PRINT_DELTA_MESSAGE("Creating console thread with stack size of 4096 bytes"); - esp_pthread_set_cfg(&cfg); - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_create(&thread_console, &attr, console_thread, NULL); - pthread_attr_destroy(&attr); - MEMTRACE_PRINT_DELTA_MESSAGE("Console thread created"); - } - else if(!is_recovery_running){ - MEMTRACE_PRINT_DELTA_MESSAGE("Running autoexec"); - process_autoexec(); + printf("\n"); + if(is_recovery_running){ + printf("****************************************************************\n" + "RECOVERY APPLICATION\n" + "This mode is used to flash Squeezelite into the OTA partition\n" + "****\n\n"); } + printf("Type 'help' to get the list of commands.\n" + "Use UP/DOWN arrows to navigate through command history.\n" + "Press TAB when typing command name to auto-complete.\n" + "\n"); + if(!is_recovery_running){ + printf("To automatically execute lines at startup:\n" + "\tSet NVS variable autoexec (U8) = 1 to enable, 0 to disable automatic execution.\n" + "\tSet NVS variable autoexec[1~9] (string)to a command that should be executed automatically\n"); + } + printf("\n\n"); + + /* Figure out if the terminal supports escape sequences */ + int probe_status = linenoiseProbe(); + if (probe_status) { /* zero indicates success */ + printf("\n****************************\n" + "Your terminal application does not support escape sequences.\n" + "Line editing and history features are disabled.\n" + "On Windows, try using Putty instead.\n" + "****************************\n"); + linenoiseSetDumbMode(1); +#if CONFIG_LOG_COLORS + /* Since the terminal doesn't support escape sequences, + * don't use color codes in the prompt. + */ + if(is_recovery_running){ + recovery_prompt= "recovery-squeezelite-esp32>"; + } + prompt = "squeezelite-esp32> "; +#endif //CONFIG_LOG_COLORS + } + esp_pthread_cfg_t cfg = esp_pthread_get_default_config(); + cfg.thread_name= "console"; + cfg.inherit_cfg = true; + if(is_recovery_running){ + prompt = recovery_prompt; + cfg.stack_size = 4096 ; + } + MEMTRACE_PRINT_DELTA_MESSAGE("Creating console thread with stack size of 4096 bytes"); + esp_pthread_set_cfg(&cfg); + pthread_create(&thread_console, NULL, console_thread, NULL); + MEMTRACE_PRINT_DELTA_MESSAGE("Console thread created"); } -esp_err_t run_command(char * line){ + +static esp_err_t run_command(char * line){ /* Try to run the command */ int ret; esp_err_t err = esp_console_run(line, &ret); @@ -400,6 +438,7 @@ esp_err_t run_command(char * line){ } return err; } + static void * console_thread() { if(!is_recovery_running){ MEMTRACE_PRINT_DELTA_MESSAGE("Running autoexec"); diff --git a/components/raop/raop.c b/components/raop/raop.c index 33f4f694..e419736d 100644 --- a/components/raop/raop.c +++ b/components/raop/raop.c @@ -255,7 +255,7 @@ void raop_delete(struct raop_ctx_s *ctx) { vTaskDelay(100 / portTICK_PERIOD_MS); ulTaskNotifyTake(pdFALSE, portMAX_DELAY); vTaskDelete(ctx->thread); - heap_caps_free(ctx->xTaskBuffer); + SAFE_PTR_FREE(ctx->xTaskBuffer); // cleanup all session-created items cleanup_rtsp(ctx, true); @@ -525,15 +525,17 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) char *p; rtp_resp_t rtp = { 0 }; short unsigned tport = 0, cport = 0; + uint8_t *buffer = NULL; + size_t size = 0; - // we are about to stream, do something if needed - success = ctx->cmd_cb(RAOP_SETUP); + // we are about to stream, do something if needed and optionally give buffers to play with + success = ctx->cmd_cb(RAOP_SETUP, &buffer, &size); if ((p = strcasestr(buf, "timing_port")) != NULL) sscanf(p, "%*[^=]=%hu", &tport); if ((p = strcasestr(buf, "control_port")) != NULL) sscanf(p, "%*[^=]=%hu", &cport); rtp = rtp_init(ctx->peer, ctx->latency, ctx->rtsp.aeskey, ctx->rtsp.aesiv, - ctx->rtsp.fmtp, cport, tport, ctx->cmd_cb, ctx->data_cb); + ctx->rtsp.fmtp, cport, tport, buffer, size, ctx->cmd_cb, ctx->data_cb); ctx->rtp = rtp.ctx; @@ -605,7 +607,6 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) sscanf(p, "%*[^:]:%u/%u/%u", &start, ¤t, &stop); current = ((current - start) / 44100) * 1000; if (stop) stop = ((stop - start) / 44100) * 1000; - else stop = -1; LOG_INFO("[%p]: SET PARAMETER progress %d/%u %s", ctx, current, stop, p); success = ctx->cmd_cb(RAOP_PROGRESS, max(current, 0), stop); } else if (body && ((p = kd_lookup(headers, "Content-Type")) != NULL) && !strcasecmp(p, "application/x-dmap-tagged")) { @@ -672,9 +673,8 @@ void cleanup_rtsp(raop_ctx_t *ctx, bool abort) { ctx->active_remote.running = false; xSemaphoreTake(ctx->active_remote.destroy_mutex, portMAX_DELAY); vTaskDelete(ctx->active_remote.thread); + SAFE_PTR_FREE(ctx->active_remote.xTaskBuffer); vSemaphoreDelete(ctx->active_remote.thread); - - heap_caps_free(ctx->active_remote.xTaskBuffer); #endif memset(&ctx->active_remote, 0, sizeof(ctx->active_remote)); LOG_INFO("[%p]: Remote search thread aborted", ctx); diff --git a/components/raop/raop.h b/components/raop/raop.h index 86879f71..15a8ba4b 100644 --- a/components/raop/raop.h +++ b/components/raop/raop.h @@ -1,15 +1,12 @@ /* - * AirCast: Chromecast to AirPlay - * - * (c) Philippe 2016-2017, philippe_44@outlook.com + * (c) Philippe 2020, philippe_44@outlook.com * * This software is released under the MIT License. * https://opensource.org/licenses/MIT * */ -#ifndef __RAOP_H -#define __RAOP_H +#pragma once #include "platform.h" #include "raop_sink.h" @@ -20,4 +17,3 @@ void raop_delete(struct raop_ctx_s *ctx); void raop_abort(struct raop_ctx_s *ctx); bool raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param); -#endif diff --git a/components/raop/raop_sink.c b/components/raop/raop_sink.c index 52f1cd0f..4f09211e 100644 --- a/components/raop/raop_sink.c +++ b/components/raop/raop_sink.c @@ -16,9 +16,7 @@ #include "audio_controls.h" #include "display.h" #include "accessors.h" - #include "log_util.h" -#include "trace.h" #ifndef CONFIG_AIRPLAY_NAME #define CONFIG_AIRPLAY_NAME "ESP32-AirPlay" @@ -184,7 +182,7 @@ static bool raop_sink_start(raop_cmd_vcb_t cmd_cb, raop_data_cb_t data_cb) { ESP_ERROR_CHECK( mdns_hostname_set(hostname) ); char * sink_name_buffer= (char *)config_alloc_get(NVS_TYPE_STR,"airplay_name"); - if(sink_name_buffer != NULL){ + if (sink_name_buffer != NULL){ memset(sink_name, 0x00, sizeof(sink_name)); strncpy(sink_name,sink_name_buffer,sizeof(sink_name)-1 ); free(sink_name_buffer); @@ -219,7 +217,7 @@ void raop_sink_init(raop_cmd_vcb_t cmd_cb, raop_data_cb_t data_cb) { raop_cbs.data = data_cb; TimerHandle_t timer = xTimerCreate("raopStart", 5000 / portTICK_RATE_MS, pdTRUE, NULL, raop_start_handler); xTimerStart(timer, portMAX_DELAY); - LOG_INFO( "delaying AirPlay start"); + LOG_INFO( "Delaying AirPlay start"); } } diff --git a/components/raop/rtp.c b/components/raop/rtp.c index e12cf95f..11218483 100644 --- a/components/raop/rtp.c +++ b/components/raop/rtp.c @@ -65,8 +65,8 @@ #define MS2TS(ms, rate) ((((u64_t) (ms)) * (rate)) / 1000) #define TS2MS(ts, rate) NTP2MS(TS2NTP(ts,rate)) - extern log_level raop_loglevel; - static log_level *loglevel = &raop_loglevel; +extern log_level raop_loglevel; +static log_level *loglevel = &raop_loglevel; //#define __RTP_STORE @@ -93,6 +93,7 @@ typedef struct audio_buffer_entry { // decoded audio packets u32_t rtptime, last_resend; s16_t *data; int len; + bool allocated; } abuf_t; typedef struct rtp_s { @@ -152,7 +153,7 @@ typedef struct rtp_s { #define BUFIDX(seqno) ((seq_t)(seqno) % BUFFER_FRAMES) -static void buffer_alloc(abuf_t *audio_buffer, int size); +static void buffer_alloc(abuf_t *audio_buffer, int size, uint8_t *buf, size_t buf_size); static void buffer_release(abuf_t *audio_buffer); static void buffer_reset(abuf_t *audio_buffer); static void buffer_push_packet(rtp_t *ctx); @@ -208,6 +209,7 @@ static struct alac_codec_s* alac_init(int fmtp[32]) { /*---------------------------------------------------------------------------*/ rtp_resp_t rtp_init(struct in_addr host, int latency, char *aeskey, char *aesiv, char *fmtpstr, short unsigned pCtrlPort, short unsigned pTimingPort, + uint8_t *buffer, size_t size, raop_cmd_cb_t cmd_cb, raop_data_cb_t data_cb) { int i = 0; @@ -260,7 +262,7 @@ rtp_resp_t rtp_init(struct in_addr host, int latency, char *aeskey, char *aesiv, ctx->alac_codec = alac_init(fmtp); rc &= ctx->alac_codec != NULL; - buffer_alloc(ctx->audio_buffer, ctx->frame_size*4); + buffer_alloc(ctx->audio_buffer, ctx->frame_size*4, buffer, size); // create rtp ports for (i = 0; i < 3; i++) { @@ -311,7 +313,7 @@ void rtp_end(rtp_t *ctx) #else ulTaskNotifyTake(pdFALSE, portMAX_DELAY); vTaskDelete(ctx->thread); - heap_caps_free(ctx->xTaskBuffer); + SAFE_PTR_FREE(ctx->xTaskBuffer); #endif } @@ -369,10 +371,18 @@ void rtp_record(rtp_t *ctx, unsigned short seqno, unsigned rtptime) { } /*---------------------------------------------------------------------------*/ -static void buffer_alloc(abuf_t *audio_buffer, int size) { +static void buffer_alloc(abuf_t *audio_buffer, int size, uint8_t *buf, size_t buf_size) { int i; for (i = 0; i < BUFFER_FRAMES; i++) { - audio_buffer[i].data = malloc(size); + if (buf && buf_size >= size) { + audio_buffer[i].data = (s16_t*) buf; + audio_buffer[i].allocated = false; + buf += size; + buf_size -= size; + } else { + audio_buffer[i].allocated = true; + audio_buffer[i].data = malloc(size); + } audio_buffer[i].ready = 0; } } @@ -381,7 +391,7 @@ static void buffer_alloc(abuf_t *audio_buffer, int size) { static void buffer_release(abuf_t *audio_buffer) { int i; for (i = 0; i < BUFFER_FRAMES; i++) { - free(audio_buffer[i].data); + if (audio_buffer[i].allocated) free(audio_buffer[i].data); } } diff --git a/components/raop/rtp.h b/components/raop/rtp.h index ebf734f5..a28e1d8a 100644 --- a/components/raop/rtp.h +++ b/components/raop/rtp.h @@ -12,6 +12,7 @@ typedef struct { rtp_resp_t rtp_init(struct in_addr host, int latency, char *aeskey, char *aesiv, char *fmtpstr, short unsigned pCtrlPort, short unsigned pTimingPort, + uint8_t *buffer, size_t size, raop_cmd_cb_t cmd_cb, raop_data_cb_t data_cb); void rtp_end(struct rtp_s *ctx); bool rtp_flush(struct rtp_s *ctx, unsigned short seqno, unsigned rtptime, bool exit_locked); diff --git a/components/raop/util.h b/components/raop/util.h index d788f4b2..94001895 100644 --- a/components/raop/util.h +++ b/components/raop/util.h @@ -18,6 +18,11 @@ #include "platform.h" #include "pthread.h" +#ifndef WIN32 +#include "freertos/FreeRTOS.h" +#include "freertos/timers.h" +#endif + #define NFREE(p) if (p) { free(p); p = NULL; } typedef struct metadata_s { @@ -46,9 +51,21 @@ char *strndup(const char *s, size_t n); int asprintf(char **strp, const char *fmt, ...); void winsock_init(void); void winsock_close(void); - #else +#define SAFE_PTR_FREE(P) free(P) #else char *strlwr(char *str); + +// reason is that TCB might be cleanup in idle task +#define SAFE_PTR_FREE(P) \ + do { \ + TimerHandle_t timer = xTimerCreate("cleanup", pdMS_TO_TICKS(5000), pdFALSE, P, _delayed_free); \ + xTimerStart(timer, portMAX_DELAY); \ + } while (0) +static void inline _delayed_free(TimerHandle_t xTimer) { + free(pvTimerGetTimerID(xTimer)); + xTimerDelete(xTimer, portMAX_DELAY); +} #endif + char* strextract(char *s1, char *beg, char *end); in_addr_t get_localhost(char **name); void get_mac(u8_t mac[]); @@ -67,5 +84,6 @@ char* kd_dump(key_data_t *kd); void kd_free(key_data_t *kd); int _fprintf(FILE *file, ...); - #endif + +#endif diff --git a/components/services/accessors.c b/components/services/accessors.c index 5940271d..af421041 100644 --- a/components/services/accessors.c +++ b/components/services/accessors.c @@ -29,12 +29,11 @@ #include "driver/spi_common_internal.h" #include "esp32/rom/efuse.h" #include "adac.h" -#include "trace.h" +#include "tools.h" #include "monitor.h" #include "messaging.h" #include "network_ethernet.h" - static const char *TAG = "services"; const char *i2c_name_type="I2C"; const char *spi_name_type="SPI"; @@ -63,7 +62,6 @@ static char * config_spdif_get_string(){ ",ws=" STR(CONFIG_SPDIF_WS_IO) ",do=" STR(CONFIG_SPDIF_DO_IO)); } - /**************************************************************************************** * */ @@ -110,9 +108,9 @@ bool is_spdif_config_locked(){ static void set_i2s_pin(char *config, i2s_pin_config_t *pin_config) { char *p; pin_config->bck_io_num = pin_config->ws_io_num = pin_config->data_out_num = pin_config->data_in_num = -1; - if ((p = strcasestr(config, "bck")) != NULL) pin_config->bck_io_num = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(config, "ws")) != NULL) pin_config->ws_io_num = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(config, "do")) != NULL) pin_config->data_out_num = atoi(strchr(p, '=') + 1); + if ((p = strcasestr(config, "bck"))) sscanf(p, "bck%*[^=]=%d", &pin_config->bck_io_num); + if ((p = strcasestr(config, "ws"))) sscanf(p, "ws%*[^=]=%d", &pin_config->ws_io_num); + if ((p = strcasestr(config, "do"))) sscanf(p, "do%*[^=]=%d", &pin_config->data_out_num); } /**************************************************************************************** @@ -120,19 +118,20 @@ static void set_i2s_pin(char *config, i2s_pin_config_t *pin_config) { */ const i2s_platform_config_t * config_get_i2s_from_str(char * dac_config ){ static EXT_RAM_ATTR i2s_platform_config_t i2s_dac_pin; - memset(&i2s_dac_pin, 0xFF, sizeof(i2s_dac_pin)); + memset(&i2s_dac_pin, 0xff, sizeof(i2s_dac_pin)); set_i2s_pin(dac_config, &i2s_dac_pin.pin); strcpy(i2s_dac_pin.model, "i2s"); char * p=NULL; - if ((p = strcasestr(dac_config, "i2c")) != NULL) i2s_dac_pin.i2c_addr = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(dac_config, "sda")) != NULL) i2s_dac_pin.sda = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(dac_config, "scl")) != NULL) i2s_dac_pin.scl = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(dac_config, "model")) != NULL) sscanf(p, "%*[^=]=%31[^,]", i2s_dac_pin.model); - if ((p = strcasestr(dac_config, "mute")) != NULL) { + + PARSE_PARAM(dac_config, "i2c", '=', i2s_dac_pin.i2c_addr); + PARSE_PARAM(dac_config, "sda", '=', i2s_dac_pin.sda); + PARSE_PARAM(dac_config, "scl", '=', i2s_dac_pin.scl); + PARSE_PARAM_STR(dac_config, "model", '=', i2s_dac_pin.model, 31); + if ((p = strcasestr(dac_config, "mute"))) { char mute[8] = ""; sscanf(p, "%*[^=]=%7[^,]", mute); i2s_dac_pin.mute_gpio = atoi(mute); - if ((p = strchr(mute, ':')) != NULL) i2s_dac_pin.mute_level = atoi(p + 1); + PARSE_PARAM(p, "mute", ':', i2s_dac_pin.mute_level); } return &i2s_dac_pin; } @@ -140,52 +139,56 @@ const i2s_platform_config_t * config_get_i2s_from_str(char * dac_config ){ /**************************************************************************************** * Get eth config structure from config string */ -const eth_config_t * config_get_eth_from_str(char * eth_config ){ - char * p=NULL; - static EXT_RAM_ATTR eth_config_t eth_pin; - memset(ð_pin, 0xFF, sizeof(eth_pin)); - memset(ð_pin.model, 0x00, sizeof(eth_pin.model)); - eth_pin.valid = true; +const eth_config_t * config_get_eth_from_str(char* config ){ + static EXT_RAM_ATTR eth_config_t eth_config; + memset(ð_config, 0xff, sizeof(eth_config)); + memset(ð_config.model, 0x00, sizeof(eth_config.model)); + eth_config.valid = true; - if ((p = strcasestr(eth_config, "model")) != NULL) sscanf(p, "%*[^=]=%15[^,]", eth_pin.model); - if ((p = strcasestr(eth_config, "mdc")) != NULL) eth_pin.mdc = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(eth_config, "mdio")) != NULL) eth_pin.mdio = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(eth_config, "rst")) != NULL) eth_pin.rst = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(eth_config, "mosi")) != NULL) eth_pin.mosi = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(eth_config, "miso")) != NULL) eth_pin.miso = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(eth_config, "intr")) != NULL) eth_pin.intr = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(eth_config, "cs")) != NULL) eth_pin.cs = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(eth_config, "speed")) != NULL) eth_pin.speed = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(eth_config, "clk")) != NULL) eth_pin.clk = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(eth_config, "host")) != NULL) eth_pin.host = atoi(strchr(p, '=') + 1); + PARSE_PARAM_STR(config, "model", '=', eth_config.model, 15); + PARSE_PARAM(config, "mdc", '=', eth_config.mdc); + PARSE_PARAM(config, "mdio", '=', eth_config.mdio); + PARSE_PARAM(config, "rst", '=', eth_config.rst); + PARSE_PARAM(config, "mosi", '=', eth_config.mosi); + PARSE_PARAM(config, "miso", '=', eth_config.miso); + PARSE_PARAM(config, "intr", '=', eth_config.intr); + PARSE_PARAM(config, "cs", '=', eth_config.cs); + PARSE_PARAM(config, "speed", '=', eth_config.speed); + PARSE_PARAM(config, "clk", '=', eth_config.clk); - if(!eth_pin.model || strlen(eth_pin.model)==0){ - eth_pin.valid = false; - return ð_pin; + // only system host is available + eth_config.host = spi_system_host; + + if(!eth_config.model || strlen(eth_config.model)==0){ + eth_config.valid = false; + return ð_config; } - network_ethernet_driver_t* network_driver = network_ethernet_driver_autodetect(eth_pin.model); + + network_ethernet_driver_t* network_driver = network_ethernet_driver_autodetect(eth_config.model); + if(!network_driver || !network_driver->valid){ - messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Ethernet config invalid: model %s %s",eth_pin.model,network_driver?"was not compiled in":"was not found"); - eth_pin.valid = false; + messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Ethernet config invalid: model %s %s",eth_config.model,network_driver?"was not compiled in":"was not found"); + eth_config.valid = false; } + if(network_driver){ - eth_pin.rmii = network_driver->rmii; - eth_pin.spi = network_driver->spi; + eth_config.rmii = network_driver->rmii; + eth_config.spi = network_driver->spi; if(network_driver->rmii){ - if(!GPIO_IS_VALID_GPIO(eth_pin.mdio) || !GPIO_IS_VALID_GPIO(eth_pin.mdc)){ - messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Ethernet config invalid: %s %s",!GPIO_IS_VALID_GPIO(eth_pin.mdc)?"Invalid MDC":"",!GPIO_IS_VALID_GPIO(eth_pin.mdio)?"Invalid mdio":""); - eth_pin.valid = false; + if(!GPIO_IS_VALID_GPIO(eth_config.mdio) || !GPIO_IS_VALID_GPIO(eth_config.mdc)){ + messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Ethernet config invalid: %s %s",!GPIO_IS_VALID_GPIO(eth_config.mdc)?"Invalid MDC":"",!GPIO_IS_VALID_GPIO(eth_config.mdio)?"Invalid mdio":""); + eth_config.valid = false; } } else if(network_driver->spi){ - if(!GPIO_IS_VALID_GPIO(eth_pin.cs)){ + if(!GPIO_IS_VALID_GPIO(eth_config.cs)){ messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Ethernet config invalid: invalid CS pin"); return false; } } } - return ð_pin; + return ð_config; } /**************************************************************************************** @@ -468,16 +471,18 @@ const display_config_t * config_display_get(){ sscanf(p, "%*[^:]:%u", &dstruct.depth); dstruct.drivername = display_conf_get_driver_name(strchr(p, '=') + 1); } - - if ((p = strcasestr(config, "width")) != NULL) dstruct.width = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(config, "height")) != NULL) dstruct.height = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(config, "reset")) != NULL) dstruct.RST_pin = atoi(strchr(p, '=') + 1); + + PARSE_PARAM(config, "width", '=', dstruct.width); + PARSE_PARAM(config, "height", '=', dstruct.height); + PARSE_PARAM(config, "reset", '=', dstruct.RST_pin); + PARSE_PARAM(config, "address", '=', dstruct.address); + PARSE_PARAM(config, "cs", '=', dstruct.CS_pin); + PARSE_PARAM(config, "speed", '=', dstruct.speed); + PARSE_PARAM(config, "back", '=', dstruct.back); + if (strstr(config, "I2C") ) dstruct.type=i2c_name_type; - if ((p = strcasestr(config, "address")) != NULL) dstruct.address = atoi(strchr(p, '=') + 1); if (strstr(config, "SPI") ) dstruct.type=spi_name_type; - if ((p = strcasestr(config, "cs")) != NULL) dstruct.CS_pin = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(config, "speed")) != NULL) dstruct.speed = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(config, "back")) != NULL) dstruct.back = atoi(strchr(p, '=') + 1); + dstruct.hflip= strcasestr(config, "HFlip") ? true : false; dstruct.vflip= strcasestr(config, "VFlip") ? true : false; dstruct.rotate= strcasestr(config, "rotate") ? true : false; @@ -488,7 +493,7 @@ const display_config_t * config_display_get(){ * */ const i2c_config_t * config_i2c_get(int * i2c_port) { - char *nvs_item, *p; + char *nvs_item; static i2c_config_t i2c = { .mode = I2C_MODE_MASTER, .sda_io_num = -1, @@ -502,10 +507,10 @@ const i2c_config_t * config_i2c_get(int * i2c_port) { nvs_item = config_alloc_get(NVS_TYPE_STR, "i2c_config"); if (nvs_item) { - if ((p = strcasestr(nvs_item, "scl")) != NULL) i2c.scl_io_num = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(nvs_item, "sda")) != NULL) i2c.sda_io_num = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(nvs_item, "speed")) != NULL) i2c.master.clk_speed = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(nvs_item, "port")) != NULL) i2c_system_port = atoi(strchr(p, '=') + 1); + PARSE_PARAM(nvs_item, "scl", '=', i2c.scl_io_num); + PARSE_PARAM(nvs_item, "sda", '=', i2c.sda_io_num); + PARSE_PARAM(nvs_item, "speed", '=', i2c.master.clk_speed); + PARSE_PARAM(nvs_item, "port", '=', i2c_system_port); free(nvs_item); } if(i2c_port) { @@ -518,6 +523,46 @@ const i2c_config_t * config_i2c_get(int * i2c_port) { return &i2c; } +/**************************************************************************************** + * Get IO expander config structure from config string + */ +const gpio_exp_config_t* config_gpio_exp_get(int index) { + char *nvs_item, *item, *p; + static gpio_exp_config_t config; + + // re-initialize config every time + memset(&config, 0, sizeof(config)); + config.intr = -1; config.count = 16; config.base = GPIO_NUM_MAX; config.phy.port = i2c_system_port; config.phy.host = spi_system_host; + + nvs_item = config_alloc_get(NVS_TYPE_STR, "gpio_exp_config"); + if (!nvs_item || !*nvs_item) return NULL; + + // search index items + for (item = strtok(nvs_item, ";"); index && item; index--) { + if ((item = strtok(NULL, ";")) == NULL) { + free(nvs_item); + return NULL; + } + } + + PARSE_PARAM(item, "addr", '=', config.phy.addr); + PARSE_PARAM(item, "cs", '=', config.phy.cs_pin); + PARSE_PARAM(item, "speed", '=', config.phy.speed); + PARSE_PARAM(item, "intr", '=', config.intr); + PARSE_PARAM(item, "base", '=', config.base); + PARSE_PARAM(item, "count", '=', config.count); + PARSE_PARAM_STR(item, "model", '=', config.model, 31); + + if ((p = strcasestr(item, "port")) != NULL) { + char port[8] = ""; + sscanf(p, "%*[^=]=%7[^,]", port); + if (strcasestr(port, "dac")) config.phy.port = 0; + } + + free(nvs_item); + return &config; +} + /**************************************************************************************** * */ @@ -596,18 +641,19 @@ const set_GPIO_struct_t * get_gpio_struct(){ * */ const spi_bus_config_t * config_spi_get(spi_host_device_t * spi_host) { - char *nvs_item, *p; + char *nvs_item; static EXT_RAM_ATTR spi_bus_config_t spi; - memset(&spi, 0xFF, sizeof(spi)); + memset(&spi, 0xff, sizeof(spi)); nvs_item = config_alloc_get_str("spi_config", CONFIG_SPI_CONFIG, NULL); if (nvs_item) { - if ((p = strcasestr(nvs_item, "data")) != NULL) spi.mosi_io_num = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(nvs_item, "mosi")) != NULL) spi.mosi_io_num = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(nvs_item, "miso")) != NULL) spi.miso_io_num = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(nvs_item, "clk")) != NULL) spi.sclk_io_num = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(nvs_item, "dc")) != NULL) spi_system_dc_gpio = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(nvs_item, "host")) != NULL) spi_system_host = atoi(strchr(p, '=') + 1); + PARSE_PARAM(nvs_item, "data", '=', spi.mosi_io_num); + PARSE_PARAM(nvs_item, "mosi", '=', spi.mosi_io_num); + PARSE_PARAM(nvs_item, "miso", '=', spi.miso_io_num); + PARSE_PARAM(nvs_item, "clk", '=', spi.sclk_io_num); + PARSE_PARAM(nvs_item, "dc", '=', spi_system_dc_gpio); + // only VSPI (1) can be used as Flash and PSRAM run at 80MHz + // if ((p = strcasestr(nvs_item, "host")) != NULL) spi_system_host = atoi(strchr(p, '=') + 1); free(nvs_item); } if(spi_host) *spi_host = spi_system_host; @@ -642,11 +688,11 @@ const rotary_struct_t * config_rotary_get() { char *config = config_alloc_get_default(NVS_TYPE_STR, "rotary_config", NULL, 0); if (config && *config) { char *p; - + // parse config - if ((p = strcasestr(config, "A")) != NULL) rotary.A = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(config, "B")) != NULL) rotary.B = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(config, "SW")) != NULL) rotary.SW = atoi(strchr(p, '=') + 1); + PARSE_PARAM(config, "A", '=', rotary.A); + PARSE_PARAM(config, "B", '=', rotary.B); + PARSE_PARAM(config, "SW", '=', rotary.SW); if ((p = strcasestr(config, "knobonly")) != NULL) { p = strchr(p, '='); rotary.knobonly = true; @@ -691,13 +737,10 @@ cJSON * add_gpio_for_value(cJSON * list,const char * name,int gpio, const char * */ cJSON * add_gpio_for_name(cJSON * list,const char * nvs_entry,const char * name, const char * prefix, bool fixed){ cJSON * llist = list?list:cJSON_CreateArray(); - char *p; int gpioNum=0; - if ((p = strcasestr(nvs_entry, name)) != NULL) { - gpioNum = atoi(strchr(p, '=') + 1); - if(gpioNum>=0){ - cJSON_AddItemToArray(llist,get_gpio_entry(name,prefix,gpioNum,fixed)); - } + PARSE_PARAM(nvs_entry, name, '=', gpioNum); + if(gpioNum>=0){ + cJSON_AddItemToArray(llist,get_gpio_entry(name,prefix,gpioNum,fixed)); } return llist; } @@ -1059,14 +1102,11 @@ cJSON * get_gpio_list(bool refresh) { #ifndef CONFIG_BAT_LOCKED char *bat_config = config_alloc_get_default(NVS_TYPE_STR, "bat_config", NULL, 0); if (bat_config) { - char *p; - int channel; - if ((p = strcasestr(bat_config, "channel") ) != NULL) { - channel = atoi(strchr(p, '=') + 1); - if(channel != -1){ - if(adc1_pad_get_io_num(channel,&gpio_num )==ESP_OK){ - cJSON_AddItemToArray(gpio_list,get_gpio_entry("bat","other",gpio_num,false)); - } + int channel = -1; + PARSE_PARAM(bat_config, "channel", '=', channel); + if(channel != -1){ + if(adc1_pad_get_io_num(channel,&gpio_num )==ESP_OK){ + cJSON_AddItemToArray(gpio_list,get_gpio_entry("bat","other",gpio_num,false)); } } free(bat_config); diff --git a/components/services/accessors.h b/components/services/accessors.h index 2c03c996..854054e9 100644 --- a/components/services/accessors.h +++ b/components/services/accessors.h @@ -12,10 +12,11 @@ #include "driver/i2c.h" #include "driver/i2s.h" #include "driver/spi_master.h" -#include "freertos/queue.h" +#include "gpio_exp.h" extern const char *i2c_name_type; extern const char *spi_name_type; + typedef struct { int width; int height; @@ -97,6 +98,7 @@ esp_err_t config_i2s_set(const i2s_platform_config_t * config, const char * esp_err_t config_spi_set(const spi_bus_config_t * config, int host, int dc); const i2c_config_t * config_i2c_get(int * i2c_port); const spi_bus_config_t * config_spi_get(spi_host_device_t * spi_host); +const gpio_exp_config_t * config_gpio_exp_get(int index); void parse_set_GPIO(void (*cb)(int gpio, char *value)); const i2s_platform_config_t * config_dac_get(); const i2s_platform_config_t * config_spdif_get( ); diff --git a/components/services/audio_controls.c b/components/services/audio_controls.c index 00a0fdb0..397a698c 100644 --- a/components/services/audio_controls.c +++ b/components/services/audio_controls.c @@ -67,12 +67,12 @@ static const char * actrls_action_s[ ] = { EP(ACTRLS_POWER),EP(ACTRLS_VOLUP),EP( static const char * TAG = "audio controls"; static actrls_config_t *json_config; cJSON * control_profiles = NULL; -static actrls_t default_controls, current_controls; +static EXT_RAM_ATTR actrls_t default_controls, current_controls; static actrls_hook_t *default_hook, *current_hook; static bool default_raw_controls, current_raw_controls; static actrls_ir_handler_t *default_ir_handler, *current_ir_handler; -static struct { +static EXT_RAM_ATTR struct { bool long_state; bool volume_lock; TimerHandle_t timer; @@ -137,10 +137,10 @@ esp_err_t actrls_init(const char *profile_name) { int A = -1, B = -1, SW = -1, longpress = 0; // parse config - if ((p = strcasestr(config, "A")) != NULL) A = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(config, "B")) != NULL) B = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(config, "SW")) != NULL) SW = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(config, "knobonly")) != NULL) { + PARSE_PARAM(config, "A", '=', A); + PARSE_PARAM(config, "B", '=', B); + PARSE_PARAM(config, "SW", '=', SW); + if ((p = strcasestr(config, "knobonly"))) { p = strchr(p, '='); int double_press = p ? atoi(p + 1) : 350; rotary.timer = xTimerCreate("knobTimer", double_press / portTICK_RATE_MS, pdFALSE, NULL, rotary_timer); diff --git a/components/services/battery.c b/components/services/battery.c index 23f3573e..a0134c8c 100644 --- a/components/services/battery.c +++ b/components/services/battery.c @@ -79,13 +79,12 @@ void battery_svc_init(void) { char *nvs_item = config_alloc_get_default(NVS_TYPE_STR, "bat_config", "n", 0); if (nvs_item) { - char *p; #ifndef CONFIG_BAT_LOCKED - if ((p = strcasestr(nvs_item, "channel")) != NULL) battery.channel = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(nvs_item, "scale")) != NULL) battery.scale = atof(strchr(p, '=') + 1); - if ((p = strcasestr(nvs_item, "atten")) != NULL) battery.attenuation = atoi(strchr(p, '=') + 1); + PARSE_PARAM(nvs_item, "channel", '=', battery.channel); + PARSE_PARAM(nvs_item, "scale", '=', battery.scale); + PARSE_PARAM(nvs_item, "atten", '=', battery.attenuation); #endif - if ((p = strcasestr(nvs_item, "cells")) != NULL) battery.cells = atof(strchr(p, '=') + 1); + PARSE_PARAM(nvs_item, "cells", '=', battery.cells); free(nvs_item); } diff --git a/components/services/buttons.c b/components/services/buttons.c index 7b23788b..fafb8668 100644 --- a/components/services/buttons.c +++ b/components/services/buttons.c @@ -21,16 +21,17 @@ #include "esp_task.h" #include "driver/gpio.h" #include "driver/rmt.h" +#include "gpio_exp.h" #include "buttons.h" #include "rotary_encoder.h" #include "globdefs.h" static const char * TAG = "buttons"; -static int n_buttons = 0; +static EXT_RAM_ATTR int n_buttons; #define BUTTON_STACK_SIZE 4096 -#define MAX_BUTTONS 16 +#define MAX_BUTTONS 32 #define DEBOUNCE 50 #define BUTTON_QUEUE_LEN 10 @@ -47,6 +48,7 @@ static EXT_RAM_ATTR struct button_s { TimerHandle_t timer; } buttons[MAX_BUTTONS]; +// can't use EXT_RAM_ATTR for initialized structure static struct { int gpio, level; struct button_s *button; @@ -67,10 +69,11 @@ static EXT_RAM_ATTR struct { infrared_handler handler; } infrared; -static xQueueHandle button_evt_queue; -static QueueSetHandle_t common_queue_set; +static EXT_RAM_ATTR QueueHandle_t button_queue; +static EXT_RAM_ATTR QueueSetHandle_t common_queue_set; static void buttons_task(void* arg); +static void buttons_handler(struct button_s *button, int level); /**************************************************************************************** * Start task needed by button,s rotaty and infrared @@ -86,40 +89,33 @@ static void common_task_init(void) { } /**************************************************************************************** - * GPIO low-level handler + * GPIO low-level ISR handler */ static void IRAM_ATTR gpio_isr_handler(void* arg) { struct button_s *button = (struct button_s*) arg; BaseType_t woken = pdFALSE; - if (xTimerGetPeriod(button->timer) > button->debounce / portTICK_RATE_MS) xTimerChangePeriodFromISR(button->timer, button->debounce / portTICK_RATE_MS, &woken); // does that restart the timer? - else xTimerResetFromISR(button->timer, &woken); + if (xTimerGetPeriod(button->timer) > pdMS_TO_TICKS(button->debounce)) { + if (button->gpio < GPIO_NUM_MAX) xTimerChangePeriodFromISR(button->timer, pdMS_TO_TICKS(button->debounce), &woken); + else xTimerChangePeriod(button->timer, pdMS_TO_TICKS(button->debounce), pdMS_TO_TICKS(10)); + } else { + if (button->gpio < GPIO_NUM_MAX) xTimerResetFromISR(button->timer, &woken); + else xTimerReset(button->timer, portMAX_DELAY); + } + if (woken) portYIELD_FROM_ISR(); + ESP_EARLY_LOGD(TAG, "INT gpio %u level %u", button->gpio, button->level); } /**************************************************************************************** * Buttons debounce/longpress timer */ -static void buttons_timer( TimerHandle_t xTimer ) { +static void buttons_timer_handler( TimerHandle_t xTimer ) { struct button_s *button = (struct button_s*) pvTimerGetTimerID (xTimer); - - button->level = gpio_get_level(button->gpio); - if (button->shifter && button->shifter->type == button->shifter->level) button->shifter->shifting = true; - - if (button->long_press && !button->long_timer && button->level == button->type) { - // detect a long press, so hold event generation - ESP_LOGD(TAG, "setting long timer gpio:%u level:%u", button->gpio, button->level); - xTimerChangePeriod(xTimer, button->long_press / portTICK_RATE_MS, 0); - button->long_timer = true; - } else { - // send a button pressed/released event (content is copied in queue) - ESP_LOGD(TAG, "sending event for gpio:%u level:%u", button->gpio, button->level); - // queue will have a copy of button's context - xQueueSend(button_evt_queue, button, 0); - button->long_timer = false; - } + // if this is an expanded GPIO, must give cache a chance + buttons_handler(button, gpio_exp_get_level(button->gpio, (button->debounce * 3) / 2, NULL)); } /**************************************************************************************** @@ -133,11 +129,33 @@ static void buttons_polling( TimerHandle_t xTimer ) { if (level != polled_gpio[i].level) { polled_gpio[i].level = level; - buttons_timer(polled_gpio[i].button->timer); + buttons_handler(polled_gpio[i].button, level); } } } +/**************************************************************************************** + * Buttons timer handler for press/longpress + */ +static void buttons_handler(struct button_s *button, int level) { + button->level = level; + + if (button->shifter && button->shifter->type == button->shifter->level) button->shifter->shifting = true; + + if (button->long_press && !button->long_timer && button->level == button->type) { + // detect a long press, so hold event generation + ESP_LOGD(TAG, "setting long timer gpio:%u level:%u", button->gpio, button->level); + xTimerChangePeriod(button->timer, button->long_press / portTICK_RATE_MS, 0); + button->long_timer = true; + } else { + // send a button pressed/released event (content is copied in queue) + ESP_LOGD(TAG, "sending event for gpio:%u level:%u", button->gpio, button->level); + // queue will have a copy of button's context + xQueueSend(button_queue, button, 0); + button->long_timer = false; + } +} + /**************************************************************************************** * Tasks that calls the appropriate functions when buttons are pressed */ @@ -150,13 +168,13 @@ static void buttons_task(void* arg) { // wait on button, rotary and infrared queues if ((xActivatedMember = xQueueSelectFromSet( common_queue_set, portMAX_DELAY )) == NULL) continue; - if (xActivatedMember == button_evt_queue) { + if (xActivatedMember == button_queue) { struct button_s button; button_event_e event; button_press_e press; // received a button event - xQueueReceive(button_evt_queue, &button, 0); + xQueueReceive(button_queue, &button, 0); event = (button.level == button.type) ? BUTTON_PRESSED : BUTTON_RELEASED; @@ -175,18 +193,18 @@ static void buttons_task(void* arg) { if (event == BUTTON_RELEASED) { // early release of a long-press button, send press/release if (!button.shifting) { - (*button.handler)(button.client, BUTTON_PRESSED, press, false); - (*button.handler)(button.client, BUTTON_RELEASED, press, false); + button.handler(button.client, BUTTON_PRESSED, press, false); + button.handler(button.client, BUTTON_RELEASED, press, false); } // button is a copy, so need to go to real context button.self->shifting = false; } else if (!button.shifting) { // normal long press and not shifting so don't discard - (*button.handler)(button.client, BUTTON_PRESSED, press, true); + button.handler(button.client, BUTTON_PRESSED, press, true); } } else { // normal press/release of a button or release of a long-press button - if (!button.shifting) (*button.handler)(button.client, event, press, button.long_press); + if (!button.shifting) button.handler(button.client, event, press, button.long_press); // button is a copy, so need to go to real context button.self->shifting = false; } @@ -195,12 +213,12 @@ static void buttons_task(void* arg) { // received a rotary event xQueueReceive(rotary.queue, &event, 0); - + ESP_LOGD(TAG, "Event: position %d, direction %s", event.state.position, - event.state.direction ? (event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW" : "CCW") : "NOT_SET"); + event.state.direction ? (event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW" : "CCW") : "NOT_SET"); rotary.handler(rotary.client, event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? - ROTARY_RIGHT : ROTARY_LEFT, false); + ROTARY_RIGHT : ROTARY_LEFT, false); } else { // this is IR infrared_receive(infrared.rb, infrared.handler); @@ -224,9 +242,9 @@ void button_create(void *client, int gpio, int type, bool pull, int debounce, bu ESP_LOGI(TAG, "Creating button using GPIO %u, type %u, pull-up/down %u, long press %u shifter %d", gpio, type, pull, long_press, shifter_gpio); if (!n_buttons) { - button_evt_queue = xQueueCreate(BUTTON_QUEUE_LEN, sizeof(struct button_s)); + button_queue = xQueueCreate(BUTTON_QUEUE_LEN, sizeof(struct button_s)); common_task_init(); - xQueueAddToSet( button_evt_queue, common_queue_set ); + xQueueAddToSet( button_queue, common_queue_set ); } // just in case this structure is allocated in a future release @@ -240,7 +258,7 @@ void button_create(void *client, int gpio, int type, bool pull, int debounce, bu buttons[n_buttons].long_press = long_press; buttons[n_buttons].shifter_gpio = shifter_gpio; buttons[n_buttons].type = type; - buttons[n_buttons].timer = xTimerCreate("buttonTimer", buttons[n_buttons].debounce / portTICK_RATE_MS, pdFALSE, (void *) &buttons[n_buttons], buttons_timer); + buttons[n_buttons].timer = xTimerCreate("buttonTimer", buttons[n_buttons].debounce / portTICK_RATE_MS, pdFALSE, (void *) &buttons[n_buttons], buttons_timer_handler); buttons[n_buttons].self = buttons + n_buttons; for (int i = 0; i < n_buttons; i++) { @@ -257,24 +275,21 @@ void button_create(void *client, int gpio, int type, bool pull, int debounce, bu } } - gpio_pad_select_gpio(gpio); - gpio_set_direction(gpio, GPIO_MODE_INPUT); - - // we need any edge detection - gpio_set_intr_type(gpio, GPIO_INTR_ANYEDGE); + gpio_pad_select_gpio_x(gpio); + gpio_set_direction_x(gpio, GPIO_MODE_INPUT); // do we need pullup or pulldown if (pull) { - if (GPIO_IS_VALID_OUTPUT_GPIO(gpio)) { - if (type == BUTTON_LOW) gpio_set_pull_mode(gpio, GPIO_PULLUP_ONLY); - else gpio_set_pull_mode(gpio, GPIO_PULLDOWN_ONLY); + if (GPIO_IS_VALID_OUTPUT_GPIO(gpio) || gpio >= GPIO_NUM_MAX) { + if (type == BUTTON_LOW) gpio_set_pull_mode_x(gpio, GPIO_PULLUP_ONLY); + else gpio_set_pull_mode_x(gpio, GPIO_PULLDOWN_ONLY); } else { ESP_LOGW(TAG, "cannot set pull up/down for gpio %u", gpio); } } // and initialize level ... - buttons[n_buttons].level = gpio_get_level(gpio); + buttons[n_buttons].level = gpio_get_level_x(gpio); // nasty ESP32 bug: fire-up constantly INT on GPIO 36/39 if ADC1, AMP, Hall used which WiFi does when PS is activated for (int i = 0; polled_gpio[i].gpio != -1; i++) if (polled_gpio[i].gpio == gpio) { @@ -282,19 +297,21 @@ void button_create(void *client, int gpio, int type, bool pull, int debounce, bu polled_timer = xTimerCreate("buttonsPolling", 100 / portTICK_RATE_MS, pdTRUE, polled_gpio, buttons_polling); xTimerStart(polled_timer, portMAX_DELAY); } - + polled_gpio[i].button = buttons + n_buttons; polled_gpio[i].level = gpio_get_level(gpio); ESP_LOGW(TAG, "creating polled gpio %u, level %u", gpio, polled_gpio[i].level); - + gpio = -1; break; } - // only create timers and ISR is this is not a polled gpio + // only create ISR if this is not a polled gpio if (gpio != -1) { - gpio_isr_handler_add(gpio, gpio_isr_handler, (void*) &buttons[n_buttons]); - gpio_intr_enable(gpio); + // we need any edge detection + gpio_set_intr_type_x(gpio, GPIO_INTR_ANYEDGE); + gpio_isr_handler_add_x(gpio, gpio_isr_handler, buttons + n_buttons); + gpio_intr_enable_x(gpio); } n_buttons++; @@ -362,7 +379,7 @@ void *button_remap(void *client, int gpio, button_handler handler, int long_pres } /**************************************************************************************** - * Create rotary encoder + * Rotary encoder handler */ static void rotary_button_handler(void *id, button_event_e event, button_press_e mode, bool long_press) { ESP_LOGI(TAG, "Rotary push-button %d", event); diff --git a/components/services/globdefs.h b/components/services/globdefs.h index 79be0033..db66a875 100644 --- a/components/services/globdefs.h +++ b/components/services/globdefs.h @@ -21,13 +21,3 @@ typedef struct { int timer, base_channel, max; } pwm_system_t; extern pwm_system_t pwm_system; -#ifdef CONFIG_SQUEEZEAMP -#define ADAC dac_tas57xx -#elif defined(CONFIG_A1S) -#define ADAC dac_a1s -#else -#define ADAC dac_external -#endif -void * malloc_init_external(size_t sz); -void * clone_obj_psram(void * source, size_t source_sz); -char * strdup_psram(const char * source); \ No newline at end of file diff --git a/components/services/gpio_exp.c b/components/services/gpio_exp.c new file mode 100644 index 00000000..cb264f8b --- /dev/null +++ b/components/services/gpio_exp.c @@ -0,0 +1,738 @@ +/* GDS Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/timers.h" +#include "freertos/queue.h" +#include "esp_task.h" +#include "esp_log.h" +#include "driver/gpio.h" +#include "driver/i2c.h" +#include "driver/spi_master.h" +#include "gpio_exp.h" + +#define GPIO_EXP_INTR 0x100 +#define GPIO_EXP_WRITE 0x200 + +/* + shadow register is both output and input, so we assume that reading to the + ports also reads the value set on output +*/ + +typedef struct gpio_exp_s { + uint32_t first, last; + int intr; + bool intr_pending; + struct { + struct gpio_exp_phy_s phy; + spi_device_handle_t spi_handle; + }; + uint32_t shadow, pending; + TickType_t age; + SemaphoreHandle_t mutex; + uint32_t r_mask, w_mask; + uint32_t pullup, pulldown; + struct gpio_exp_isr_s { + gpio_isr_t handler; + void *arg; + TimerHandle_t timer; + } isr[32]; + struct gpio_exp_model_s const *model; +} gpio_exp_t; + +typedef struct { + enum { ASYNC_WRITE } type; + int gpio; + int level; + gpio_exp_t *expander; +} queue_request_t; + +static const char TAG[] = "gpio expander"; + +static void IRAM_ATTR intr_isr_handler(void* arg); +static gpio_exp_t* find_expander(gpio_exp_t *expander, int *gpio); + +static void pca9535_set_direction(gpio_exp_t* self); +static uint32_t pca9535_read(gpio_exp_t* self); +static void pca9535_write(gpio_exp_t* self); + +static uint32_t pca85xx_read(gpio_exp_t* self); +static void pca85xx_write(gpio_exp_t* self); + +static esp_err_t mcp23017_init(gpio_exp_t* self); +static void mcp23017_set_pull_mode(gpio_exp_t* self); +static void mcp23017_set_direction(gpio_exp_t* self); +static uint32_t mcp23017_read(gpio_exp_t* self); +static void mcp23017_write(gpio_exp_t* self); + +static esp_err_t mcp23s17_init(gpio_exp_t* self); +static void mcp23s17_set_pull_mode(gpio_exp_t* self); +static void mcp23s17_set_direction(gpio_exp_t* self); +static uint32_t mcp23s17_read(gpio_exp_t* self); +static void mcp23s17_write(gpio_exp_t* self); + +static void service_handler(void *arg); +static void debounce_handler( TimerHandle_t xTimer ); + +static esp_err_t i2c_write(uint8_t port, uint8_t addr, uint8_t reg, uint32_t data, int len); +static uint32_t i2c_read(uint8_t port, uint8_t addr, uint8_t reg, int len); + +static spi_device_handle_t spi_config(struct gpio_exp_phy_s *phy); +static esp_err_t spi_write(spi_device_handle_t handle, uint8_t addr, uint8_t reg, uint32_t data, int len); +static uint32_t spi_read(spi_device_handle_t handle, uint8_t addr, uint8_t reg, int len); + +static const struct gpio_exp_model_s { + char *model; + gpio_int_type_t trigger; + esp_err_t (*init)(gpio_exp_t* self); + uint32_t (*read)(gpio_exp_t* self); + void (*write)(gpio_exp_t* self); + void (*set_direction)(gpio_exp_t* self); + void (*set_pull_mode)(gpio_exp_t* self); +} registered[] = { + { .model = "pca9535", + .trigger = GPIO_INTR_NEGEDGE, + .set_direction = pca9535_set_direction, + .read = pca9535_read, + .write = pca9535_write, }, + { .model = "pca85xx", + .trigger = GPIO_INTR_NEGEDGE, + .read = pca85xx_read, + .write = pca85xx_write, }, + { .model = "mcp23017", + .trigger = GPIO_INTR_NEGEDGE, + .init = mcp23017_init, + .set_direction = mcp23017_set_direction, + .set_pull_mode = mcp23017_set_pull_mode, + .read = mcp23017_read, + .write = mcp23017_write, }, + { .model = "mcp23s17", + .trigger = GPIO_INTR_NEGEDGE, + .init = mcp23s17_init, + .set_direction = mcp23s17_set_direction, + .set_pull_mode = mcp23s17_set_pull_mode, + .read = mcp23s17_read, + .write = mcp23s17_write, }, +}; + +static EXT_RAM_ATTR uint8_t n_expanders; +static EXT_RAM_ATTR QueueHandle_t message_queue; +static EXT_RAM_ATTR gpio_exp_t expanders[4]; +static EXT_RAM_ATTR TaskHandle_t service_task; + +/****************************************************************************** + * Retrieve base from an expander reference + */ +uint32_t gpio_exp_get_base(gpio_exp_t *expander) { + return expander->first; +} + +/****************************************************************************** + * Retrieve reference from a GPIO + */ +gpio_exp_t *gpio_exp_get_expander(int gpio) { + int _gpio = gpio; + return find_expander(NULL, &_gpio); +} + +/****************************************************************************** + * Create an I2C expander + */ +gpio_exp_t* gpio_exp_create(const gpio_exp_config_t *config) { + gpio_exp_t *expander = expanders + n_expanders; + + if (config->base < GPIO_NUM_MAX || n_expanders == sizeof(expanders)/sizeof(gpio_exp_t)) { + ESP_LOGE(TAG, "Base %d GPIO must be at least %d for %s or too many expanders %d", config->base, GPIO_NUM_MAX, config->model, n_expanders); + return NULL; + } + + // See if we know that model (expanders is zero-initialized) + for (int i = 0; !expander->model && i < sizeof(registered)/sizeof(struct gpio_exp_model_s); i++) { + if (strcasestr(config->model, registered[i].model)) expander->model = registered + i; + } + + // well... try again + if (!expander->model) { + ESP_LOGE(TAG, "Unknown GPIO expansion chip %s", config->model); + return NULL; + } + + memcpy(&expander->phy, &config->phy, sizeof(struct gpio_exp_phy_s)); + + // try to initialize the expander if required + if (expander->model->init && expander->model->init(expander) != ESP_OK) { + ESP_LOGE(TAG, "Cannot create GPIO expander %s, check i2c/spi configuration", config->model); + return NULL; + } + + n_expanders++; + expander->first = config->base; + expander->last = config->base + config->count - 1; + expander->intr = config->intr; + expander->mutex = xSemaphoreCreateMutex(); + + // create a task to handle asynchronous requests (only write at this time) + if (!message_queue) { + // we allocate TCB but stack is static to avoid SPIRAM fragmentation + StaticTask_t* xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + static EXT_RAM_ATTR StackType_t xStack[4*1024] __attribute__ ((aligned (4))); + + message_queue = xQueueCreate(4, sizeof(queue_request_t)); + service_task = xTaskCreateStatic(service_handler, "gpio_expander", sizeof(xStack), NULL, ESP_TASK_PRIO_MIN + 1, xStack, xTaskBuffer); + } + + // set interrupt if possible + if (config->intr >= 0) { + gpio_pad_select_gpio(config->intr); + gpio_set_direction(config->intr, GPIO_MODE_INPUT); + + switch (expander->model->trigger) { + case GPIO_INTR_NEGEDGE: + case GPIO_INTR_LOW_LEVEL: + gpio_set_pull_mode(config->intr, GPIO_PULLUP_ONLY); + break; + case GPIO_INTR_POSEDGE: + case GPIO_INTR_HIGH_LEVEL: + gpio_set_pull_mode(config->intr, GPIO_PULLDOWN_ONLY); + break; + default: + gpio_set_pull_mode(config->intr, GPIO_PULLUP_PULLDOWN); + break; + } + + gpio_set_intr_type(config->intr, expander->model->trigger); + gpio_isr_handler_add(config->intr, intr_isr_handler, expander); + gpio_intr_enable(config->intr); + } + + ESP_LOGI(TAG, "Create GPIO expander %s at base %u with INT %d at @%x on port/host %d/%d", config->model, config->base, config->intr, config->phy.addr, config->phy.port, config->phy.host); + return expander; +} + +/****************************************************************************** + * Add ISR handler for a GPIO + */ +esp_err_t gpio_exp_isr_handler_add(int gpio, gpio_isr_t isr_handler, uint32_t debounce, void *arg, struct gpio_exp_s *expander) { + if (gpio < GPIO_NUM_MAX && !expander) return gpio_isr_handler_add(gpio, isr_handler, arg); + if ((expander = find_expander(expander, &gpio)) == NULL) return ESP_ERR_INVALID_ARG; + + expander->isr[gpio].handler = isr_handler; + expander->isr[gpio].arg = arg; + if (debounce) expander->isr[gpio].timer = xTimerCreate("gpioExpDebounce", pdMS_TO_TICKS(debounce), + pdFALSE, expander->isr + gpio, debounce_handler ); + + return ESP_OK; +} + +/****************************************************************************** + * Remove ISR handler for a GPIO + */ +esp_err_t gpio_exp_isr_handler_remove(int gpio, struct gpio_exp_s *expander) { + if (gpio < GPIO_NUM_MAX && !expander) return gpio_isr_handler_remove(gpio); + if ((expander = find_expander(expander, &gpio)) == NULL) return ESP_ERR_INVALID_ARG; + + if (expander->isr[gpio].timer) xTimerDelete(expander->isr[gpio].timer, portMAX_DELAY); + memset(expander->isr + gpio, 0, sizeof(struct gpio_exp_isr_s)); + + return ESP_OK; +} + +/****************************************************************************** + * Set GPIO direction + */ +esp_err_t gpio_exp_set_direction(int gpio, gpio_mode_t mode, gpio_exp_t *expander) { + if (gpio < GPIO_NUM_MAX && !expander) return gpio_set_direction(gpio, mode); + if ((expander = find_expander(expander, &gpio)) == NULL) return ESP_ERR_INVALID_ARG; + + xSemaphoreTake(expander->mutex, pdMS_TO_TICKS(portMAX_DELAY)); + + if (mode == GPIO_MODE_INPUT) { + expander->r_mask |= 1 << gpio; + expander->shadow = expander->model->read(expander); + expander->age = ~xTaskGetTickCount(); + } else { + expander->w_mask |= 1 << gpio; + } + + if (expander->r_mask & expander->w_mask) { + xSemaphoreGive(expander->mutex); + ESP_LOGE(TAG, "GPIO %d on expander base %u can't be r/w", gpio, expander->first); + return ESP_ERR_INVALID_ARG; + } + + // most expanders want unconfigured GPIO to be set to output + if (expander->model->set_direction) expander->model->set_direction(expander); + + xSemaphoreGive(expander->mutex); + + return ESP_OK; +} + +/****************************************************************************** + * Get GPIO level with cache + */ +int gpio_exp_get_level(int gpio, int age, gpio_exp_t *expander) { + if (gpio < GPIO_NUM_MAX && !expander) return gpio_get_level(gpio); + if ((expander = find_expander(expander, &gpio)) == NULL) return -1; + uint32_t now = xTaskGetTickCount(); + + // return last thing we had if we can't get the mutex + if (xSemaphoreTake(expander->mutex, pdMS_TO_TICKS(50)) == pdFALSE) { + ESP_LOGW(TAG, "Can't get mutex for GPIO %d", expander->first + gpio); + return (expander->shadow >> gpio) & 0x01; + } + + // re-read the expander if data is too old + if (age >= 0 && now - expander->age >= pdMS_TO_TICKS(age)) { + uint32_t value = expander->model->read(expander); + expander->pending |= (expander->shadow ^ value) & expander->r_mask; + expander->shadow = value; + expander->age = now; + } + + // clear pending bit + expander->pending &= ~(1 << gpio); + + xSemaphoreGive(expander->mutex); + + ESP_LOGD(TAG, "Get level for GPIO %u => read %x", expander->first + gpio, expander->shadow); + return (expander->shadow >> gpio) & 0x01; +} + +/****************************************************************************** + * Set GPIO level with cache + */ +esp_err_t gpio_exp_set_level(int gpio, int level, bool direct, gpio_exp_t *expander) { + if (gpio < GPIO_NUM_MAX && !expander) return gpio_set_level(gpio, level); + if ((expander = find_expander(expander, &gpio)) == NULL) return ESP_ERR_INVALID_ARG; + uint32_t mask = 1 << gpio; + + // very limited risk with lack of semaphore here + if ((expander->w_mask & mask) == 0) { + ESP_LOGW(TAG, "GPIO %d is not set for output", expander->first + gpio); + return ESP_ERR_INVALID_ARG; + } + + if (direct) { + xSemaphoreTake(expander->mutex, pdMS_TO_TICKS(portMAX_DELAY)); + + level = level ? mask : 0; + mask &= expander->shadow; + + // only write if shadow not up to date + if ((mask ^ level) && expander->model->write) { + expander->shadow = (expander->shadow & ~(mask | level)) | level; + expander->model->write(expander); + } + + xSemaphoreGive(expander->mutex); + ESP_LOGD(TAG, "Set level %x for GPIO %u => wrote %x", level, expander->first + gpio, expander->shadow); + } else { + queue_request_t request = { .gpio = gpio, .level = level, .type = ASYNC_WRITE, .expander = expander }; + if (xQueueSend(message_queue, &request, 0) == pdFALSE) return ESP_ERR_INVALID_RESPONSE; + + // notify service task that will write it when it can + xTaskNotify(service_task, GPIO_EXP_WRITE, eSetValueWithoutOverwrite); + } + + return ESP_OK; +} + +/****************************************************************************** + * Set GPIO pullmode + */ +esp_err_t gpio_exp_set_pull_mode(int gpio, gpio_pull_mode_t mode, gpio_exp_t *expander) { + if (gpio < GPIO_NUM_MAX && !expander) return gpio_set_pull_mode(gpio, mode); + if ((expander = find_expander(expander, &gpio)) != NULL && expander->model->set_pull_mode) { + + expander->pullup &= ~(1 << gpio); + expander->pulldown &= ~(1 << gpio); + + if (mode == GPIO_PULLUP_ONLY || mode == GPIO_PULLUP_PULLDOWN) expander->pullup |= 1 << gpio; + if (mode == GPIO_PULLDOWN_ONLY || mode == GPIO_PULLUP_PULLDOWN) expander->pulldown |= 1 << gpio; + + expander->model->set_pull_mode(expander); + return ESP_OK; + } + return ESP_ERR_INVALID_ARG; +} + +/****************************************************************************** + * Wrapper function + */ +esp_err_t gpio_set_pull_mode_x(int gpio, gpio_pull_mode_t mode) { + if (gpio < GPIO_NUM_MAX) return gpio_set_pull_mode(gpio, mode); + return gpio_exp_set_pull_mode(gpio, mode, NULL); +} + +esp_err_t gpio_set_direction_x(int gpio, gpio_mode_t mode) { + if (gpio < GPIO_NUM_MAX) return gpio_set_direction(gpio, mode); + return gpio_exp_set_direction(gpio, mode, NULL); +} + +int gpio_get_level_x(int gpio) { + if (gpio < GPIO_NUM_MAX) return gpio_get_level(gpio); + return gpio_exp_get_level(gpio, 10, NULL); +} + +esp_err_t gpio_set_level_x(int gpio, int level) { + if (gpio < GPIO_NUM_MAX) return gpio_set_level(gpio, level); + return gpio_exp_set_level(gpio, level, false, NULL); +} + +esp_err_t gpio_isr_handler_add_x(int gpio, gpio_isr_t isr_handler, void* args) { + if (gpio < GPIO_NUM_MAX) return gpio_isr_handler_add(gpio, isr_handler, args); + return gpio_exp_isr_handler_add(gpio, isr_handler, 0, args, NULL); +} + +esp_err_t gpio_isr_handler_remove_x(int gpio) { + if (gpio < GPIO_NUM_MAX) return gpio_isr_handler_remove(gpio); + return gpio_exp_isr_handler_remove(gpio, NULL); +} + + +/**************************************************************************************** + * INTR low-level handler + */ +static void IRAM_ATTR intr_isr_handler(void* arg) { + gpio_exp_t *self = (gpio_exp_t*) arg; + BaseType_t woken = pdFALSE; + + // activate all, including ourselves + for (int i = 0; i < n_expanders; i++) if (expanders[i].intr == self->intr) expanders[i].intr_pending = true; + + xTaskNotifyFromISR(service_task, GPIO_EXP_INTR, eSetValueWithOverwrite, &woken); + if (woken) portYIELD_FROM_ISR(); + + ESP_EARLY_LOGD(TAG, "INTR for expander base %d", gpio_exp_get_base(self)); +} + +/**************************************************************************************** + * INTR debounce handler + */ +static void debounce_handler( TimerHandle_t xTimer ) { + struct gpio_exp_isr_s *isr = (struct gpio_exp_isr_s*) pvTimerGetTimerID (xTimer); + isr->handler(isr->arg); +} + +/**************************************************************************************** + * Service task + */ +void service_handler(void *arg) { + while (1) { + queue_request_t request; + uint32_t notif = ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + + // we have been notified of an interrupt + if (notif == GPIO_EXP_INTR) { + /* If we want a smarter bitmap of expanders with a pending interrupt + we'll have to disable interrupts while clearing that bitmap. For + now, a loop will do */ + for (int i = 0; i < n_expanders; i++) { + gpio_exp_t *expander = expanders + i; + + // no interrupt for that gpio + if (expander->intr < 0) continue; + + // only check expander with pending interrupts + gpio_intr_disable(expander->intr); + if (!expander->intr_pending) { + gpio_intr_enable(expander->intr); + continue; + } + expander->intr_pending = false; + gpio_intr_enable(expander->intr); + + xSemaphoreTake(expander->mutex, pdMS_TO_TICKS(50)); + + // read GPIOs and clear all pending status + uint32_t value = expander->model->read(expander); + uint32_t pending = expander->pending | ((expander->shadow ^ value) & expander->r_mask); + expander->shadow = value; + expander->pending = 0; + expander->age = xTaskGetTickCount(); + + xSemaphoreGive(expander->mutex); + ESP_LOGD(TAG, "Handling GPIO %d reads 0x%04x and has 0x%04x pending", expander->first, expander->shadow, pending); + + for (int gpio = 31, clz; pending; pending <<= (clz + 1)) { + clz = __builtin_clz(pending); + gpio -= clz; + if (expander->isr[gpio].timer) xTimerReset(expander->isr[gpio].timer, 1); // todo 0 + else if (expander->isr[gpio].handler) expander->isr[gpio].handler(expander->isr[gpio].arg); + } + } + } + + // check if we have some other pending requests + while (xQueueReceive(message_queue, &request, 0) == pdTRUE) { + esp_err_t err = gpio_exp_set_level(request.gpio, request.level, true, request.expander); + if (err != ESP_OK) ESP_LOGW(TAG, "Can't execute async GPIO %d write request (%d)", request.gpio, err); + } + } +} + +/**************************************************************************************** + * Find the expander related to base + */ +static gpio_exp_t* find_expander(gpio_exp_t *expander, int *gpio) { + // a mutex would be better, but risk is so small... + for (int i = 0; !expander && i < n_expanders; i++) { + if (*gpio >= expanders[i].first && *gpio <= expanders[i].last) expander = expanders + i; + } + + // normalize GPIO number + if (expander && *gpio >= expander->first) *gpio -= expander->first; + + return expander; +} + +/**************************************************************************************** + DRIVERS +****************************************************************************************/ + +/**************************************************************************************** + * PCA9535 family : direction, read and write + */ +static void pca9535_set_direction(gpio_exp_t* self) { + i2c_write(self->phy.port, self->phy.addr, 0x06, self->r_mask, 2); +} + +static uint32_t pca9535_read(gpio_exp_t* self) { + return i2c_read(self->phy.port, self->phy.addr, 0x00, 2); +} + +static void pca9535_write(gpio_exp_t* self) { + i2c_write(self->phy.port, self->phy.addr, 0x02, self->shadow, 2); +} + +/**************************************************************************************** + * PCA85xx family : read and write + */ +static uint32_t pca85xx_read(gpio_exp_t* self) { + // must return the full set of pins, not just inputs + uint32_t data = i2c_read(self->phy.port, self->phy.addr, 0xff, 2); + return (data & self->r_mask) | (self->shadow & ~self->r_mask); +} + +static void pca85xx_write(gpio_exp_t* self) { + /* + There is no good option with this chip: normally, unused pin should be set to input + to avoid any conflict but then they float and create tons of suprious. So option 1 is + to le tthem float and option 2 is to set them as output to 0. + In addition, setting an output pin to 1 equals is making it an input and if this is + use to short a led (e.g.) instead of being the sink, the it generates a spurious + */ + // option 1 + // i2c_write(self->phy.port, self->phy.addr, 0xff, (self->shadow & self->w_mask) | ~self->w_mask, 2); + // option 2 + i2c_write(self->phy.port, self->phy.addr, 0xff, (self->shadow & self->w_mask) | self->r_mask, 2); +} + +/**************************************************************************************** + * MCP23017 family : init, direction, read and write + */ +static esp_err_t mcp23017_init(gpio_exp_t* self) { + /* + 0111 x10x = same bank, mirrot single int, no sequentµial, open drain, active low + not sure about this funny change of mapping of the control register itself, really? + */ + esp_err_t err = i2c_write(self->phy.port, self->phy.addr, 0x05, 0x74, 1); + err |= i2c_write(self->phy.port, self->phy.addr, 0x0a, 0x74, 1); + + // no interrupt on comparison or on change + err |= i2c_write(self->phy.port, self->phy.addr, 0x04, 0x00, 2); + err |= i2c_write(self->phy.port, self->phy.addr, 0x08, 0x00, 2); + + return err; +} + +static void mcp23017_set_direction(gpio_exp_t* self) { + // default to input and set real input to generate interrupt + i2c_write(self->phy.port, self->phy.addr, 0x00, ~self->w_mask, 2); + i2c_write(self->phy.port, self->phy.addr, 0x04, self->r_mask, 2); +} + +static void mcp23017_set_pull_mode(gpio_exp_t* self) { + i2c_write(self->phy.port, self->phy.addr, 0x0c, self->pullup, 2); +} + +static uint32_t mcp23017_read(gpio_exp_t* self) { + // read the pins value, not the stored one @interrupt + return i2c_read(self->phy.port, self->phy.addr, 0x12, 2); +} + +static void mcp23017_write(gpio_exp_t* self) { + i2c_write(self->phy.port, self->phy.addr, 0x12, self->shadow, 2); +} + +/**************************************************************************************** + * MCP23s17 family : init, direction, read and write + */ +static esp_err_t mcp23s17_init(gpio_exp_t* self) { + if ((self->spi_handle = spi_config(&self->phy)) == NULL) return ESP_ERR_INVALID_ARG; + + /* + 0111 x10x = same bank, mirrot single int, no sequentµial, open drain, active low + not sure about this funny change of mapping of the control register itself, really? + */ + esp_err_t err = spi_write(self->spi_handle, self->phy.addr, 0x05, 0x74, 1); + err |= spi_write(self->spi_handle, self->phy.addr, 0x0a, 0x74, 1); + + // no interrupt on comparison or on change + err |= spi_write(self->spi_handle, self->phy.addr, 0x04, 0x00, 2); + err |= spi_write(self->spi_handle, self->phy.addr, 0x08, 0x00, 2); + + return err; +} + +static void mcp23s17_set_direction(gpio_exp_t* self) { + // default to input and set real input to generate interrupt + spi_write(self->spi_handle, self->phy.addr, 0x00, ~self->w_mask, 2); + spi_write(self->spi_handle, self->phy.addr, 0x04, self->r_mask, 2); +} + +static void mcp23s17_set_pull_mode(gpio_exp_t* self) { + spi_write(self->spi_handle, self->phy.addr, 0x0c, self->pullup, 2); +} + +static uint32_t mcp23s17_read(gpio_exp_t* self) { + // read the pins value, not the stored one @interrupt + return spi_read(self->spi_handle, self->phy.addr, 0x12, 2); +} + +static void mcp23s17_write(gpio_exp_t* self) { + spi_write(self->spi_handle, self->phy.addr, 0x12, self->shadow, 2); +} + +/*************************************************************************************** + I2C low level +***************************************************************************************/ + +/**************************************************************************************** + * I2C write up to 32 bits + */ +static esp_err_t i2c_write(uint8_t port, uint8_t addr, uint8_t reg, uint32_t data, int len) { + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + + i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK); + if (reg != 0xff) i2c_master_write_byte(cmd, reg, I2C_MASTER_NACK); + + // works with our endianness + if (len > 1) i2c_master_write(cmd, (uint8_t*) &data, len, I2C_MASTER_NACK); + else i2c_master_write_byte(cmd, data, I2C_MASTER_NACK); + + i2c_master_stop(cmd); + esp_err_t ret = i2c_master_cmd_begin(port, cmd, 100 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + + if (ret != ESP_OK) { + ESP_LOGW(TAG, "I2C write failed"); + } + + return ret; +} + +/**************************************************************************************** + * I2C read up to 32 bits + */ +static uint32_t i2c_read(uint8_t port, uint8_t addr, uint8_t reg, int len) { + uint32_t data = 0; + + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + + i2c_master_start(cmd); + + // when using a register, write it's value then the device address again + if (reg != 0xff) { + i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK); + i2c_master_write_byte(cmd, reg, I2C_MASTER_NACK); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_READ, I2C_MASTER_NACK); + } else { + i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_READ, I2C_MASTER_NACK); + } + + // works with our endianness + if (len > 1) i2c_master_read(cmd, (uint8_t*) &data, len, I2C_MASTER_LAST_NACK); + else i2c_master_read_byte(cmd, (uint8_t*) &data, I2C_MASTER_NACK); + + i2c_master_stop(cmd); + esp_err_t ret = i2c_master_cmd_begin(port, cmd, 100 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + + if (ret != ESP_OK) { + ESP_LOGW(TAG, "I2C read failed"); + } + + return data; +} + +/*************************************************************************************** + SPI low level +***************************************************************************************/ + +/**************************************************************************************** + * SPI device addition + */ +static spi_device_handle_t spi_config(struct gpio_exp_phy_s *phy) { + spi_device_interface_config_t config = { }; + spi_device_handle_t handle = NULL; + + config.command_bits = config.address_bits = 8; + config.clock_speed_hz = phy->speed ? phy->speed : SPI_MASTER_FREQ_8M; + config.spics_io_num = phy->cs_pin; + config.queue_size = 1; + config.flags = SPI_DEVICE_NO_DUMMY; + + spi_bus_add_device( phy->host, &config, &handle ); + ESP_LOGI(TAG, "SPI expander initialized on host:%d with cs:%d and speed:%dHz", phy->host, phy->cs_pin, config.clock_speed_hz); + + return handle; +} + +/**************************************************************************************** + * SPI write up to 32 bits + */ +static esp_err_t spi_write(spi_device_handle_t handle, uint8_t addr, uint8_t reg, uint32_t data, int len) { + spi_transaction_t transaction = { }; + + // rx_buffer is NULL, nothing to receive + transaction.flags = SPI_TRANS_USE_TXDATA; + transaction.cmd = addr << 1; + transaction.addr = reg; + transaction.tx_data[0] = data; transaction.tx_data[1] = data >> 8; + transaction.length = len * 8; + + // only do polling as we don't have contention on SPI (otherwise DMA for transfers > 16 bytes) + return spi_device_polling_transmit(handle, &transaction); +} + +/**************************************************************************************** + * SPI read up to 32 bits + */ +static uint32_t spi_read(spi_device_handle_t handle, uint8_t addr, uint8_t reg, int len) { + spi_transaction_t *transaction = heap_caps_calloc(1, sizeof(spi_transaction_t), MALLOC_CAP_DMA); + + // tx_buffer is NULL, nothing to transmit except cmd/addr + transaction->flags = SPI_TRANS_USE_RXDATA; + transaction->cmd = (addr << 1) | 0x01; + transaction->addr = reg; + transaction->length = len * 8; + + // only do polling as we don't have contention on SPI (otherwise DMA for transfers > 16 bytes) + spi_device_polling_transmit(handle, transaction); + uint32_t data = *(uint32_t*) transaction->rx_data; + free(transaction); + + return data; +} \ No newline at end of file diff --git a/components/services/gpio_exp.h b/components/services/gpio_exp.h new file mode 100644 index 00000000..91339880 --- /dev/null +++ b/components/services/gpio_exp.h @@ -0,0 +1,61 @@ +/* GDS Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#pragma once + +#include +#include "freertos/FreeRTOS.h" +#include "driver/gpio.h" + +struct gpio_exp_s; + +typedef struct { + char model[32]; + int intr; + uint8_t count; + uint32_t base; + struct gpio_exp_phy_s { + uint8_t addr; + struct { // for I2C + uint8_t port; + }; + struct { // for SPI + uint32_t speed; + uint8_t host; + uint8_t cs_pin; + }; + } phy; +} gpio_exp_config_t; + +// set to -1 and to NULL if there is no interrupt +struct gpio_exp_s* gpio_exp_create(const gpio_exp_config_t *config); +uint32_t gpio_exp_get_base(struct gpio_exp_s *expander); +struct gpio_exp_s* gpio_exp_get_expander(int gpio); +#define gpio_is_expanded(gpio) (gpio < GPIO_NUM_MAX) + +/* + For all functions below when is provided, GPIO's can be numbered from 0. If + is NULL, then GPIO must start from base OR be on-chip +*/ +esp_err_t gpio_exp_set_direction(int gpio, gpio_mode_t mode, struct gpio_exp_s *expander); +esp_err_t gpio_exp_set_pull_mode(int gpio, gpio_pull_mode_t mode, struct gpio_exp_s *expander); +int gpio_exp_get_level(int gpio, int age, struct gpio_exp_s *expander); +esp_err_t gpio_exp_set_level(int gpio, int level, bool direct, struct gpio_exp_s *expander); +esp_err_t gpio_exp_isr_handler_add(int gpio, gpio_isr_t isr, uint32_t debounce, void *arg, struct gpio_exp_s *expander); +esp_err_t gpio_exp_isr_handler_remove(int gpio, struct gpio_exp_s *expander); + +// unified function to use either built-in or expanded GPIO +esp_err_t gpio_set_direction_x(int gpio, gpio_mode_t mode); +esp_err_t gpio_set_pull_mode_x(int gpio, gpio_pull_mode_t mode); +int gpio_get_level_x(int gpio); +esp_err_t gpio_set_level_x(int gpio, int level); +esp_err_t gpio_isr_handler_add_x(int gpio, gpio_isr_t isr_handler, void* args); +esp_err_t gpio_isr_handler_remove_x(int gpio); +#define gpio_set_intr_type_x(gpio, type) do { if (gpio < GPIO_NUM_MAX) gpio_set_intr_type(gpio, type); } while (0) +#define gpio_intr_enable_x(gpio) do { if (gpio < GPIO_NUM_MAX) gpio_intr_enable(gpio); } while (0) +#define gpio_pad_select_gpio_x(gpio) do { if (gpio < GPIO_NUM_MAX) gpio_pad_select_gpio(gpio); } while (0) diff --git a/components/services/led.c b/components/services/led.c index 1f2fedbe..fe48d7fd 100644 --- a/components/services/led.c +++ b/components/services/led.c @@ -19,6 +19,7 @@ #include "driver/gpio.h" #include "driver/ledc.h" #include "platform_config.h" +#include "gpio_exp.h" #include "led.h" #include "globdefs.h" #include "accessors.h" @@ -40,7 +41,8 @@ static EXT_RAM_ATTR struct led_s { TimerHandle_t timer; } leds[MAX_LED]; -static EXT_RAM_ATTR struct { +// can't use EXT_RAM_ATTR for initialized structure +static struct { int gpio; int active; int pwm; @@ -53,7 +55,7 @@ static int led_max = 2; * */ static void set_level(struct led_s *led, bool on) { - if (led->pwm < 0) gpio_set_level(led->gpio, on ? led->onstate : !led->onstate); + if (led->pwm < 0 || led->gpio >= GPIO_NUM_MAX) gpio_set_level_x(led->gpio, on ? led->onstate : !led->onstate); else { ledc_set_duty(LEDC_HIGH_SPEED_MODE, led->channel, on ? led->pwm : (led->onstate ? 0 : pwm_system.max)); ledc_update_duty(LEDC_HIGH_SPEED_MODE, led->channel); @@ -179,9 +181,9 @@ bool led_config(int idx, gpio_num_t gpio, int onstate, int pwm) { leds[idx].onstate = onstate; leds[idx].pwm = -1; - if (pwm < 0) { - gpio_pad_select_gpio(gpio); - gpio_set_direction(gpio, GPIO_MODE_OUTPUT); + if (pwm < 0 || gpio >= GPIO_NUM_MAX) { + gpio_pad_select_gpio_x(gpio); + gpio_set_direction_x(gpio, GPIO_MODE_OUTPUT); } else { leds[idx].channel = pwm_system.base_channel++; leds[idx].pwm = pwm_system.max * powf(pwm / 100.0, 3); @@ -232,10 +234,10 @@ void led_svc_init(void) { parse_set_GPIO(set_led_gpio); #endif - char *nvs_item = config_alloc_get(NVS_TYPE_STR, "led_brightness"), *p; + char *nvs_item = config_alloc_get(NVS_TYPE_STR, "led_brightness"); if (nvs_item) { - if ((p = strcasestr(nvs_item, "green")) != NULL) green.pwm = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(nvs_item, "red")) != NULL) red.pwm = atoi(strchr(p, '=') + 1); + PARSE_PARAM(nvs_item, "green", '=', green.pwm); + PARSE_PARAM(nvs_item, "red", '=', red.pwm); free(nvs_item); } diff --git a/components/services/messaging.c b/components/services/messaging.c index bcdc7077..f72ad6f2 100644 --- a/components/services/messaging.c +++ b/components/services/messaging.c @@ -14,8 +14,7 @@ #include "nvs_utilities.h" #include "platform_esp32.h" #include "messaging.h" -#include "trace.h" -#include "globdefs.h" +#include "tools.h" /************************************ * Globals */ diff --git a/components/services/monitor.c b/components/services/monitor.c index 3aeabfa9..64dc1068 100644 --- a/components/services/monitor.c +++ b/components/services/monitor.c @@ -23,7 +23,7 @@ #include "accessors.h" #include "messaging.h" #include "cJSON.h" -#include "trace.h" +#include "tools.h" #define MONITOR_TIMER (10*1000) #define SCRATCH_SIZE 256 @@ -147,7 +147,7 @@ static void monitor_callback(TimerHandle_t xTimer) { * */ static void jack_handler_default(void *id, button_event_e event, button_press_e mode, bool long_press) { - ESP_LOGD(TAG, "Jack %s", event == BUTTON_PRESSED ? "inserted" : "removed"); + ESP_LOGI(TAG, "Jack %s", event == BUTTON_PRESSED ? "inserted" : "removed"); if (jack_handler_svc) (*jack_handler_svc)(event == BUTTON_PRESSED); } diff --git a/components/services/rotary_encoder.c b/components/services/rotary_encoder.c index d9aa854d..650c0206 100644 --- a/components/services/rotary_encoder.c +++ b/components/services/rotary_encoder.c @@ -91,6 +91,7 @@ #include "esp_log.h" #include "driver/gpio.h" +#include "gpio_exp.h" #define TAG "rotary_encoder" @@ -148,7 +149,7 @@ static uint8_t _process(rotary_encoder_info_t * info) if (info != NULL) { // Get state of input pins. - uint8_t pin_state = (gpio_get_level(info->pin_b) << 1) | gpio_get_level(info->pin_a); + uint8_t pin_state = (gpio_get_level_x(info->pin_b) << 1) | gpio_get_level_x(info->pin_a); // Determine new state from the pins and state table. #ifdef ROTARY_ENCODER_DEBUG @@ -198,12 +199,18 @@ static void _isr_rotenc(void * args) .direction = info->state.direction, }, }; - BaseType_t task_woken = pdFALSE; - xQueueOverwriteFromISR(info->queue, &queue_event, &task_woken); - if (task_woken) - { - portYIELD_FROM_ISR(); - } + if (info->pin_a < GPIO_NUM_MAX) { + BaseType_t task_woken = pdFALSE; + xQueueOverwriteFromISR(info->queue, &queue_event, &task_woken); + if (task_woken) + { + portYIELD_FROM_ISR(); + } + } + else + { + xQueueOverwrite(info->queue, &queue_event); + } } } @@ -220,19 +227,19 @@ esp_err_t rotary_encoder_init(rotary_encoder_info_t * info, gpio_num_t pin_a, gp info->state.direction = ROTARY_ENCODER_DIRECTION_NOT_SET; // configure GPIOs - gpio_pad_select_gpio(info->pin_a); - gpio_set_pull_mode(info->pin_a, GPIO_PULLUP_ONLY); - gpio_set_direction(info->pin_a, GPIO_MODE_INPUT); - gpio_set_intr_type(info->pin_a, GPIO_INTR_ANYEDGE); + gpio_pad_select_gpio_x(info->pin_a); + gpio_set_pull_mode_x(info->pin_a, GPIO_PULLUP_ONLY); + gpio_set_direction_x(info->pin_a, GPIO_MODE_INPUT); + gpio_set_intr_type_x(info->pin_a, GPIO_INTR_ANYEDGE); - gpio_pad_select_gpio(info->pin_b); - gpio_set_pull_mode(info->pin_b, GPIO_PULLUP_ONLY); - gpio_set_direction(info->pin_b, GPIO_MODE_INPUT); - gpio_set_intr_type(info->pin_b, GPIO_INTR_ANYEDGE); + gpio_pad_select_gpio_x(info->pin_b); + gpio_set_pull_mode_x(info->pin_b, GPIO_PULLUP_ONLY); + gpio_set_direction_x(info->pin_b, GPIO_MODE_INPUT); + gpio_set_intr_type_x(info->pin_b, GPIO_INTR_ANYEDGE); // install interrupt handlers - gpio_isr_handler_add(info->pin_a, _isr_rotenc, info); - gpio_isr_handler_add(info->pin_b, _isr_rotenc, info); + gpio_isr_handler_add_x(info->pin_a, _isr_rotenc, info); + gpio_isr_handler_add_x(info->pin_b, _isr_rotenc, info); } else { @@ -280,8 +287,8 @@ esp_err_t rotary_encoder_uninit(rotary_encoder_info_t * info) esp_err_t err = ESP_OK; if (info) { - gpio_isr_handler_remove(info->pin_a); - gpio_isr_handler_remove(info->pin_b); + gpio_isr_handler_remove_x(info->pin_a); + gpio_isr_handler_remove_x(info->pin_b); } else { diff --git a/components/services/services.c b/components/services/services.c index 3633f4ba..810b94c7 100644 --- a/components/services/services.c +++ b/components/services/services.c @@ -12,15 +12,13 @@ #include "driver/ledc.h" #include "driver/i2c.h" #include "platform_config.h" +#include "gpio_exp.h" #include "battery.h" #include "led.h" #include "monitor.h" #include "globdefs.h" #include "accessors.h" #include "messaging.h" -#include "esp_heap_caps.h" -#include "esp_log.h" - extern void battery_svc_init(void); extern void monitor_svc_init(void); @@ -38,48 +36,14 @@ pwm_system_t pwm_system = { static const char *TAG = "services"; - -void * malloc_init_external(size_t sz){ - void * ptr=NULL; - ptr = heap_caps_malloc(sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - if(ptr==NULL){ - ESP_LOGE(TAG,"malloc_init_external: unable to allocate %d bytes of PSRAM!",sz); - } - else { - memset(ptr,0x00,sz); - } - return ptr; -} -void * clone_obj_psram(void * source, size_t source_sz){ - void * ptr=NULL; - ptr = heap_caps_malloc(source_sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - if(ptr==NULL){ - ESP_LOGE(TAG,"clone_obj_psram: unable to allocate %d bytes of PSRAM!",source_sz); - } - else { - memcpy(ptr,source,source_sz); - } - return ptr; -} -char * strdup_psram(const char * source){ - void * ptr=NULL; - size_t source_sz = strlen(source)+1; - ptr = heap_caps_malloc(source_sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - if(ptr==NULL){ - ESP_LOGE(TAG,"strdup_psram: unable to allocate %d bytes of PSRAM! Cannot clone string %s",source_sz,source); - } - else { - memset(ptr,0x00,source_sz); - strcpy(ptr,source); - } - return ptr; -} - /**************************************************************************************** * */ -void set_power_gpio(int gpio, char *value) { +void set_chip_power_gpio(int gpio, char *value) { bool parsed = true; + + // we only parse on-chip GPIOs + if (gpio >= GPIO_NUM_MAX) return; if (!strcasecmp(value, "vcc") ) { gpio_pad_select_gpio(gpio); @@ -89,9 +53,26 @@ void set_power_gpio(int gpio, char *value) { gpio_pad_select_gpio(gpio); gpio_set_direction(gpio, GPIO_MODE_OUTPUT); gpio_set_level(gpio, 0); - } else parsed = false ; + } else parsed = false; if (parsed) ESP_LOGI(TAG, "set GPIO %u to %s", gpio, value); +} + +void set_exp_power_gpio(int gpio, char *value) { + bool parsed = true; + + // we only parse on-chip GPIOs + if (gpio < GPIO_NUM_MAX) return; + + if (!strcasecmp(value, "vcc") ) { + gpio_exp_set_direction(gpio, GPIO_MODE_OUTPUT, NULL); + gpio_exp_set_level(gpio, 1, true, NULL); + } else if (!strcasecmp(value, "gnd")) { + gpio_exp_set_direction(gpio, GPIO_MODE_OUTPUT, NULL); + gpio_exp_set_level(gpio, 0, true, NULL); + } else parsed = false; + + if (parsed) ESP_LOGI(TAG, "set expanded GPIO %u to %s", gpio, value); } @@ -109,8 +90,8 @@ void services_init(void) { } #endif - // set potential power GPIO - parse_set_GPIO(set_power_gpio); + // set potential power GPIO on chip first in case expanders are power using these + parse_set_GPIO(set_chip_power_gpio); // shared I2C bus const i2c_config_t * i2c_config = config_i2c_get(&i2c_system_port); @@ -140,6 +121,13 @@ void services_init(void) { ESP_LOGW(TAG, "no SPI configured"); } + // create GPIO expanders + const gpio_exp_config_t* gpio_exp_config; + for (int count = 0; (gpio_exp_config = config_gpio_exp_get(count)); count++) gpio_exp_create(gpio_exp_config); + + // now set potential power GPIO on expander + parse_set_GPIO(set_exp_power_gpio); + // system-wide PWM timer configuration ledc_timer_config_t pwm_timer = { .duty_resolution = LEDC_TIMER_13_BIT, diff --git a/components/spotify/CMakeLists.txt b/components/spotify/CMakeLists.txt new file mode 100644 index 00000000..87364a88 --- /dev/null +++ b/components/spotify/CMakeLists.txt @@ -0,0 +1,28 @@ +# this must be set *before* idf_component_register +set(CMAKE_CXX_STANDARD 17) + +idf_component_register( + SRC_DIRS . + INCLUDE_DIRS . "cspot/include" "cspot/bell/include" "cspot/protos" + PRIV_REQUIRES mbedtls mdns nvs_flash platform_config services esp_http_server tools + LDFRAGMENTS "linker.lf" +) + +include_directories("../codecs/inc") +add_definitions(-DBELL_USE_MBEDTLS) +add_definitions(-Wno-unused-variable -Wno-unused-const-variable -Wchar-subscripts -Wunused-label -Wmaybe-uninitialized -Wmisleading-indentation) + +set(BELL_DISABLE_CODECS 1) +set(BELL_TREMOR_EXTERNAL "idf::codecs") +set(BELL_CJSON_EXTERNAL "idf::json") + +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/cspot ${CMAKE_CURRENT_BINARY_DIR}/cspot) +target_link_libraries(${COMPONENT_LIB} PRIVATE cspot ${EXTRA_REQ_LIBS}) + +#if (!WIN32) +# message(${CMAKE_CURRENT_SOURCE_DIR}/cmake/generate_protos.sh) +# execute_process(COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/cmake/generate_protos.sh" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") +#endif () + + + diff --git a/components/spotify/Shim.cpp b/components/spotify/Shim.cpp new file mode 100644 index 00000000..64dcd3a2 --- /dev/null +++ b/components/spotify/Shim.cpp @@ -0,0 +1,381 @@ +/* + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + * + */ + +#include +#include +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_http_server.h" + +#include +#include +#include +#include +#include +#include +#include +#include "ConfigJSON.h" +#include "Logger.h" + +#include "platform_config.h" +#include "tools.h" +#include "cspot_private.h" +#include "cspot_sink.h" +#include "Shim.h" + +extern "C" { + httpd_handle_t get_http_server(int *port); + static esp_err_t handlerWrapper(httpd_req_t *req); +}; + +#define CSPOT_STACK_SIZE (8*1024) + +static const char *TAG = "cspot"; + +// using a global is pretty ugly, but it's easier with all Lambda below +static EXT_RAM_ATTR struct cspot_s { + char name[32]; + cspot_cmd_cb_t cHandler; + cspot_data_cb_t dHandler; + TaskHandle_t TaskHandle; + std::shared_ptr blob; +} cspot; + +std::shared_ptr configMan; +std::shared_ptr file; +std::shared_ptr mercuryManager; +std::shared_ptr spircController; + +/**************************************************************************************** + * Main task (could it be deleted after spirc has started?) + */ +static void cspotTask(void *pvParameters) { + char configName[] = "cspot_config"; + std::string jsonConfig; + + // Config file + file = std::make_shared(); + configMan = std::make_shared(configName, file); + + // We might have no config at all + if (!file->readFile(configName, jsonConfig) || !jsonConfig.length()) { + ESP_LOGW(TAG, "Cannot load config, using default"); + + configMan->deviceName = cspot.name; + configMan->format = AudioFormat::OGG_VORBIS_160; + configMan->volume = 32767; + + configMan->save(); + } + + // safely load config now + configMan->load(); + if (!configMan->deviceName.length()) configMan->deviceName = cspot.name; + ESP_LOGI(TAG, "Started CSpot with %s (bitrate %d)", configMan->deviceName.c_str(), configMan->format == AudioFormat::OGG_VORBIS_320 ? 320 : (configMan->format == AudioFormat::OGG_VORBIS_160 ? 160 : 96)); + + // All we do here is notify the task to start the mercury loop + auto createPlayerCallback = [](std::shared_ptr blob) { + // TODO: handle/refuse that another user takes ownership + cspot.blob = blob; + xTaskNotifyGive(cspot.TaskHandle); + }; + + int port; + httpd_handle_t server = get_http_server(&port); + auto httpServer = std::make_shared(server, port); + + auto authenticator = std::make_shared(createPlayerCallback, httpServer); + authenticator->registerHandlers(); + + // wait to be notified and have a mercury loop + while (1) { + ulTaskNotifyTake(pdFALSE, portMAX_DELAY); + + auto session = std::make_unique(); + session->connectWithRandomAp(); + auto token = session->authenticate(cspot.blob); + + ESP_LOGI(TAG, "Creating Spotify(CSpot) player"); + + // Auth successful + if (token.size() > 0) { + // tell sink that we are taking over + cspot.cHandler(CSPOT_SETUP, 44100); + + auto audioSink = std::make_shared(); + + // @TODO Actually store this token somewhere + mercuryManager = std::make_shared(std::move(session)); + mercuryManager->startTask(); + + spircController = std::make_shared(mercuryManager, cspot.blob->username, audioSink); + + spircController->setEventHandler([](CSpotEvent &event) { + switch (event.eventType) { + case CSpotEventType::TRACK_INFO: { + TrackInfo track = std::get(event.data); + cspot.cHandler(CSPOT_TRACK, 44100, track.artist.c_str(), track.album.c_str(), track.name.c_str()); + break; + } + case CSpotEventType::PLAY_PAUSE: { + bool isPaused = std::get(event.data); + if (isPaused) cspot.cHandler(CSPOT_PAUSE); + else cspot.cHandler(CSPOT_PLAY); + break; + } + case CSpotEventType::SEEK: + cspot.cHandler(CSPOT_SEEK, std::get(event.data)); + break; + case CSpotEventType::DISC: + cspot.cHandler(CSPOT_DISC); + spircController->stopPlayer(); + mercuryManager->stop(); + break; + case CSpotEventType::PREV: + case CSpotEventType::NEXT: + cspot.cHandler(CSPOT_FLUSH); + break; + /* + // we use volume from sink which is a 16 bits value + case CSpotEventType::VOLUME: { + int volume = std::get(event.data); + cspot.cHandler(CSPOT_VOLUME, volume); + ESP_LOGW(TAG, "cspot volume : %d", volume); + break; + } + */ + default: + break; + } + }); + + mercuryManager->reconnectedCallback = []() { + return spircController->subscribe(); + }; + + mercuryManager->handleQueue(); + } + + // release all ownership + mercuryManager.reset(); + spircController.reset(); + cspot.blob.reset(); + + // flush files + file->flush(); +ESP_LOGW(TAG, "THIS SESSION IS FINISHED %ld %ld %ld", mercuryManager.use_count(), spircController.use_count(), cspot.blob.use_count()); + } + + // we should not be here + vTaskDelete(NULL); +} + +/**************************************************************************************** + * API to create and start a cspot instance + */ +struct cspot_s* cspot_create(const char *name, cspot_cmd_cb_t cmd_cb, cspot_data_cb_t data_cb) { + static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4))); + static EXT_RAM_ATTR StackType_t xStack[CSPOT_STACK_SIZE] __attribute__ ((aligned (4))); + + bell::setDefaultLogger(); + + 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, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer); + + return &cspot; +} + +/**************************************************************************************** + * Commands sent by local buttons/actions + */ +bool cspot_cmd(struct cspot_s* ctx, cspot_event_t event, void *param) { + switch(event) { + case CSPOT_PREV: + spircController->prevSong(); + break; + case CSPOT_NEXT: + spircController->nextSong(); + break; + case CSPOT_TOGGLE: + spircController->playToggle(); + break; + case CSPOT_PAUSE: + spircController->setPause(true); + break; + case CSPOT_PLAY: + spircController->setPause(false); + break; + case CSPOT_DISC: + spircController->stopPlayer(); + mercuryManager->stop(); + break; + case CSPOT_STOP: + spircController->stopPlayer(); + break; + case CSPOT_VOLUME_UP: + spircController->adjustVolume(MAX_VOLUME / 100 + 1); + break; + case CSPOT_VOLUME_DOWN: + spircController->adjustVolume(-(MAX_VOLUME / 100 + 1)); + break; + default: + break; + } + + return true; +} + +/**************************************************************************************** + * AudioSink class to push data to squeezelite backend (decode_external) + */ +void ShimAudioSink::volumeChanged(uint16_t volume) { + cspot.cHandler(CSPOT_VOLUME, volume); +} + +void ShimAudioSink::feedPCMFrames(std::vector &data) { + cspot.dHandler(&data[0], data.size()); +} + +/**************************************************************************************** + * NVSFile class to store config + */ + bool NVSFile::readFile(std::string filename, std::string &fileContent) { + auto search = files.find(filename); + + // cache + if (search == files.end()) { + char *content = (char*) config_alloc_get(NVS_TYPE_STR, filename.c_str()); + if (!content) return false; + fileContent = content; + free(content); + } else { + fileContent = search->second; + } + + return true; +} + +bool NVSFile::writeFile(std::string filename, std::string fileContent) { + auto search = files.find(filename); + + files[filename] = fileContent; + if (search == files.end()) return (ESP_OK == config_set_value(NVS_TYPE_STR, filename.c_str(), fileContent.c_str())); + return true; +} + +bool NVSFile::flush() { + esp_err_t err = ESP_OK; + + for (auto it = files.begin(); it != files.end(); ++it) { + err |= config_set_value(NVS_TYPE_STR, it->first.c_str(), it->second.c_str()); + } + return (err == ESP_OK); +} + +/**************************************************************************************** + * Shim HTTP server for spirc + */ +static esp_err_t handlerWrapper(httpd_req_t *req) { + bell::HTTPRequest request = { }; + char *query = NULL, *body = NULL; + bell::httpHandler *handler = (bell::httpHandler*) req->user_ctx; + size_t query_len = httpd_req_get_url_query_len(req); + + request.connection = httpd_req_to_sockfd(req); + + // get body if any (add '\0' at the end if used as string) + if (req->content_len) { + body = (char*) calloc(1, req->content_len + 1); + int size = httpd_req_recv(req, body, req->content_len); + request.body = body; + ESP_LOGD(TAG,"wrapper received body %d/%d", size, req->content_len); + } + + // parse query if any (can be in body as well for url-encoded) + if (query_len) { + query = (char*) malloc(query_len + 1); + httpd_req_get_url_query_str(req, query, query_len + 1); + } else if (body && strchr(body, '&')) { + query = body; + body = NULL; + } + + // I know this is very crude and unsafe... + url_decode(query); + char *key = strtok(query, "&"); + + while (key) { + char *value = strchr(key, '='); + *value++ = '\0'; + request.queryParams[key] = value; + ESP_LOGD(TAG,"wrapper received key:%s value:%s", key, value); + key = strtok(NULL, "&"); + }; + + if (query) free(query); + if (body) free(body); + + /* + This is a strange construct as the C++ handler will call the ShimHTTPSer::respond + and then we'll return. So we can't obtain the response to be sent, as esp_http_server + normally expects, instead respond() will use raw socket and close connection + */ + (*handler)(request); + + return ESP_OK; +} + +void ShimHTTPServer::registerHandler(bell::RequestType requestType, const std::string &routeUrl, bell::httpHandler handler) { + httpd_uri_t request = { + .uri = routeUrl.c_str(), + .method = (requestType == bell::RequestType::GET ? HTTP_GET : HTTP_POST), + .handler = handlerWrapper, + .user_ctx = NULL, + }; + + // find athe first free spot and register handler + for (int i = 0; i < sizeof(uriHandlers)/sizeof(bell::httpHandler); i++) { + if (!uriHandlers[i]) { + uriHandlers[i] = handler; + request.user_ctx = uriHandlers + i; + httpd_register_uri_handler(serverHandle, &request); + break; + } + } + + if (!request.user_ctx) ESP_LOGW(TAG, "Cannot add handler for %s", routeUrl.c_str()); +} + +void ShimHTTPServer::respond(const bell::HTTPResponse &response) { + char *buf; + size_t len = asprintf(&buf, "HTTP/1.1 %d OK\r\n" + "Server: SQUEEZEESP32\r\n" + "Connection: close\r\n" + "Content-type: %s\r\n" + "Content-length: %d\r\n" + "Access-Control-Allow-Origin: *\r\n" + "Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS\r\n" + "Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token\r\n" + "\r\n%s", + response.status, response.contentType.c_str(), + response.body.size(), response.body.c_str() + ); + + // use raw socket send and close connection + httpd_socket_send(serverHandle, response.connectionFd, buf, len, 0); + free(buf); + + // we want to close the socket due to the strange construct + httpd_sess_trigger_close(serverHandle, response.connectionFd); +} diff --git a/components/spotify/Shim.h b/components/spotify/Shim.h new file mode 100644 index 00000000..9c0a8cce --- /dev/null +++ b/components/spotify/Shim.h @@ -0,0 +1,50 @@ +/* + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + * + */ + +#pragma once + +#include +#include +#include +#include "AudioSink.h" +#include "FileHelper.h" +#include "BaseHTTPServer.h" +#include +#include +#include +#include +#include "esp_err.h" +#include "esp_http_server.h" +#include "esp_log.h" + +class ShimAudioSink : public AudioSink { +public: + ShimAudioSink(void) { softwareVolumeControl = false; } + void feedPCMFrames(std::vector &data); + virtual void volumeChanged(uint16_t volume); +}; + +class NVSFile : public FileHelper { +private: + std::map files; + +public: + bool readFile(std::string filename, std::string &fileContent); + bool writeFile(std::string filename, std::string fileContent); + bool flush(); +}; + +class ShimHTTPServer : public bell::BaseHTTPServer { +private: + httpd_handle_t serverHandle; + bell::httpHandler uriHandlers[4]; + +public: + ShimHTTPServer(httpd_handle_t server, int port) { serverHandle = server; serverPort = port; } + + void registerHandler(bell::RequestType requestType, const std::string &, bell::httpHandler); + void respond(const bell::HTTPResponse &); +}; diff --git a/components/spotify/cspot/CMakeLists.txt b/components/spotify/cspot/CMakeLists.txt new file mode 100644 index 00000000..6972827a --- /dev/null +++ b/components/spotify/cspot/CMakeLists.txt @@ -0,0 +1,66 @@ +project(cspot) + +cmake_minimum_required(VERSION 2.8.9) +set (CMAKE_CXX_STANDARD 17) + +file(GLOB SOURCES "src/*.cpp" "src/*.c") + +if (NOT DEFINED ${USE_EXTERNAL_BELL}) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/bell ${CMAKE_CURRENT_BINARY_DIR}/bell) +endif() +# Add platform specific sources +# if(${ESP_PLATFORM}) +# file(GLOB ESP_PLATFORM_SOURCES "src/platform/esp/*.cpp" "src/platform/esp/*.c") +# set(SOURCES ${SOURCES} ${ESP_PLATFORM_SOURCES} ) +# endif() + +# if(UNIX) +# file(GLOB UNIX_PLATFORM_SOURCES "src/platform/unix/*.cpp" "src/platform/unix/*.c") +# set(SOURCES ${SOURCES} ${UNIX_PLATFORM_SOURCES} ) +# endif() + +# if(APPLE) +# file(GLOB APPLE_PLATFORM_SOURCES "src/platform/apple/*.cpp" "src/platform/apple/*.c") +# set(SOURCES ${SOURCES} ${APPLE_PLATFORM_SOURCES} ) +# endif() + +# if(UNIX AND NOT APPLE) +# # file(GLOB LINUX_PLATFORM_SOURCES "src/platform/linux/*.cpp" "src/platform/linux/*.c") +# # set(SOURCES ${SOURCES} ${LINUX_PLATFORM_SOURCES} ) +# # endif() + + +# if(${ESP_PLATFORM}) +# list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/CryptoOpenSSL.cpp) # use MBedTLS +# idf_build_set_property(COMPILE_DEFINITIONS "-DCSPOT_USE_MBEDTLS" APPEND) +# set(EXTRA_REQ_LIBS idf::mbedtls idf::pthread idf::mdns) +# add_definitions(-Wunused-const-variable -Wchar-subscripts -Wunused-label -Wmaybe-uninitialized -Wmisleading-indentation) +# else() +# list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/CryptoMbedTLS.cpp) # use OpenSSL +# find_package(OpenSSL REQUIRED) +# if(OPENSSL_FOUND) +# set(OPENSSL_USE_STATIC_LIBS TRUE) +# endif() +# set(EXTRA_REQ_LIBS OpenSSL::Crypto Threads::Threads) +# set(THREADS_PREFER_PTHREAD_FLAG ON) +# find_package(Threads REQUIRED) +# endif() + +if(UNIX AND NOT APPLE) + set(EXTRA_REQ_LIBS ${EXTRA_REQ_LIBS} dns_sd) # add apple bonjur compatibility library for linux + # TODO: migrate from this to native linux mDNS +endif() + +include_directories("include") +include_directories("${CMAKE_CURRENT_BINARY_DIR}") +include_directories("protos") + +message(${CMAKE_CURRENT_SOURCE_DIR}/cmake/generate_protos.sh) +execute_process(COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/cmake/generate_protos.sh" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + +set(SOURCES ${SOURCES} "protos/AnyRefImpl.cpp" "protos/ReflectTypeInfo.cpp") + +add_library(cspot STATIC ${SOURCES}) +target_link_libraries(cspot PRIVATE bell ${EXTRA_REQ_LIBS}) + +target_include_directories(cspot PUBLIC "include" "protos" bell ${EXTRA_REQ_LIBS} ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/components/spotify/cspot/bell/.DS_Store b/components/spotify/cspot/bell/.DS_Store new file mode 100644 index 00000000..1572ed4a Binary files /dev/null and b/components/spotify/cspot/bell/.DS_Store differ diff --git a/components/spotify/cspot/bell/.gitmodules b/components/spotify/cspot/bell/.gitmodules new file mode 100644 index 00000000..1a7ec48c --- /dev/null +++ b/components/spotify/cspot/bell/.gitmodules @@ -0,0 +1,7 @@ +[submodule "tremor"] + path = tremor + url = https://gitlab.xiph.org/xiph/tremor.git + branch = lowmem +[submodule "cJSON"] + path = cJSON + url = https://github.com/DaveGamble/cJSON diff --git a/components/spotify/cspot/bell/CMakeLists.txt b/components/spotify/cspot/bell/CMakeLists.txt new file mode 100644 index 00000000..08acc6e2 --- /dev/null +++ b/components/spotify/cspot/bell/CMakeLists.txt @@ -0,0 +1,80 @@ +project(bell) + +cmake_minimum_required(VERSION 2.8.9) +set (CMAKE_CXX_STANDARD 17) + +file(GLOB SOURCES "src/*.cpp" "src/*.c") + +# Add platform specific sources + +if(${ESP_PLATFORM}) + file(GLOB ESP_PLATFORM_SOURCES "src/platform/esp/*.cpp" "src/platform/esp/*.c" "src/asm/biquad_f32_ae32.S") + set(SOURCES ${SOURCES} ${ESP_PLATFORM_SOURCES} ) +endif() + +if(UNIX) + file(GLOB UNIX_PLATFORM_SOURCES "src/platform/unix/*.cpp" "src/platform/linux/TLSSocket.cpp" "src/platform/unix/*.c") + set(SOURCES ${SOURCES} ${UNIX_PLATFORM_SOURCES} ) +endif() + +if(APPLE) + file(GLOB APPLE_PLATFORM_SOURCES "src/platform/apple/*.cpp" "src/platform/linux/TLSSocket.cpp" "src/platform/apple/*.c") + set(SOURCES ${SOURCES} ${APPLE_PLATFORM_SOURCES} ) +endif() + +if(UNIX AND NOT APPLE) + file(GLOB LINUX_PLATFORM_SOURCES "src/platform/linux/*.cpp" "src/platform/linux/*.c") + set(SOURCES ${SOURCES} ${LINUX_PLATFORM_SOURCES} ) +endif() + +if(${ESP_PLATFORM}) + list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/CryptoOpenSSL.cpp) # use MBedTLS + idf_build_set_property(COMPILE_DEFINITIONS "-DBELL_USE_MBEDTLS" APPEND) + set(EXTRA_REQ_LIBS idf::mbedtls idf::pthread idf::mdns) + add_definitions(-Wunused-const-variable -Wchar-subscripts -Wunused-label -Wmaybe-uninitialized -Wmisleading-indentation) +else() + list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/CryptoMbedTLS.cpp) # use OpenSSL + find_package(OpenSSL REQUIRED) + if(OPENSSL_FOUND) + set(OPENSSL_USE_STATIC_LIBS TRUE) + endif() + set(EXTRA_REQ_LIBS OpenSSL::Crypto OpenSSL::SSL Threads::Threads) + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) +endif() + +if (BELL_DISABLE_CODECS) + list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/DecoderGlobals.cpp) # use OpenSSL + add_definitions(-DBELL_DISABLE_CODECS) +else() + set(EXTRA_INC ${EXTRA_INC} "libhelix-aac" "libhelix-mp3") + set(SOURCES ${SOURCES} "libhelix-aac/aacdec.c" "libhelix-aac/aactabs.c" "libhelix-aac/bitstream.c" "libhelix-aac/buffers.c" "libhelix-aac/dct4.c" "libhelix-aac/decelmnt.c" "libhelix-aac/dequant.c" "libhelix-aac/fft.c" "libhelix-aac/filefmt.c" "libhelix-aac/huffman.c" "libhelix-aac/hufftabs.c" "libhelix-aac/imdct.c" "libhelix-aac/noiseless.c" "libhelix-aac/pns.c" "libhelix-aac/sbr.c" "libhelix-aac/sbrfft.c" "libhelix-aac/sbrfreq.c" "libhelix-aac/sbrhfadj.c" "libhelix-aac/sbrhfgen.c" "libhelix-aac/sbrhuff.c" "libhelix-aac/sbrimdct.c" "libhelix-aac/sbrmath.c" "libhelix-aac/sbrqmf.c" "libhelix-aac/sbrside.c" "libhelix-aac/sbrtabs.c" "libhelix-aac/stproc.c" "libhelix-aac/tns.c" "libhelix-aac/trigtabs.c") + set(SOURCES ${SOURCES} "libhelix-mp3/bitstream.c" "libhelix-mp3/buffers.c" "libhelix-mp3/dct32.c" "libhelix-mp3/dequant.c" "libhelix-mp3/dqchan.c" "libhelix-mp3/huffman.c" "libhelix-mp3/hufftabs.c" "libhelix-mp3/imdct.c" "libhelix-mp3/mp3dec.c" "libhelix-mp3/mp3tabs.c" "libhelix-mp3/polyphase.c" "libhelix-mp3/scalfact.c" "libhelix-mp3/stproc.c" "libhelix-mp3/subband.c" "libhelix-mp3/trigtabs.c") +endif() + +if(UNIX AND NOT APPLE) + set(EXTRA_REQ_LIBS ${EXTRA_REQ_LIBS} dns_sd) # add apple bonjur compatibility library for linux + # TODO: migrate from this to native linux mDNS +endif() +add_definitions( -DUSE_DEFAULT_STDLIB=1) + +if (BELL_CJSON_EXTERNAL) + message("Using external cJSON") + set(EXTRA_REQ_LIBS ${EXTRA_REQ_LIBS} ${BELL_CJSON_EXTERNAL}) +else() + set(EXTRA_INC ${EXTRA_INC} "cJSON") + set(SOURCES ${SOURCES} "cJSON/cJSON.c") +endif() + +if (BELL_TREMOR_EXTERNAL) + message("Using external TREMOR") + set(EXTRA_REQ_LIBS ${EXTRA_REQ_LIBS} ${BELL_TREMOR_EXTERNAL}) +else() + set(EXTRA_INC ${EXTRA_INC} "tremor") + set(SOURCES ${SOURCES} "tremor/mdct.c" "tremor/dsp.c" "tremor/info.c" "tremor/misc.c" "tremor/floor1.c" "tremor/floor0.c" "tremor/vorbisfile.c" "tremor/res012.c" "tremor/mapping0.c" "tremor/codebook.c" "tremor/framing.c" "tremor/bitwise.c" "tremor/floor_lookup.c") +endif() + +add_library(bell STATIC ${SOURCES}) +target_link_libraries(bell PRIVATE ${EXTRA_REQ_LIBS}) +target_include_directories(bell PUBLIC "include" "protos" ${EXTRA_INC} ${EXTRA_REQ_LIBS} ${CMAKE_CURRENT_BINARY_DIR}) + diff --git a/components/spotify/cspot/bell/README.md b/components/spotify/cspot/bell/README.md new file mode 100644 index 00000000..598b8b81 --- /dev/null +++ b/components/spotify/cspot/bell/README.md @@ -0,0 +1,9 @@ +# bell + +Core audio utils library used in cspot and euphonium projects. + +Implemented utilities: + +- HTTPServer +- Crypto (openssl and mbedtls backed) +- Semaphore implementations \ No newline at end of file diff --git a/components/spotify/cspot/bell/cJSON/.editorconfig b/components/spotify/cspot/bell/cJSON/.editorconfig new file mode 100644 index 00000000..befb2c20 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/.editorconfig @@ -0,0 +1,23 @@ +root = true + + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[Makefile] +indent_style = tab +indent_size = unset + +# ignore external repositories and test inputs +[tests/{unity,json-patch-tests,inputs}/*] +indent_style = unset +indent_size = unset +end_of_line = unset +charset = unset +trim_trailing_whitespace = unset +insert_final_newline = unset diff --git a/components/spotify/cspot/bell/cJSON/.gitattributes b/components/spotify/cspot/bell/cJSON/.gitattributes new file mode 100644 index 00000000..883895fb --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/.gitattributes @@ -0,0 +1,11 @@ +* text=auto +/tests/inputs/* text eol=lf + +.gitattributes export-ignore +.gitignore export-ignore +.github export-ignore +.editorconfig export-ignore +.travis.yml export-ignore + +# Linguist incorrectly identified the headers as C++, manually override this. +*.h linguist-language=C diff --git a/components/spotify/cspot/bell/cJSON/.github/CONTRIBUTING.md b/components/spotify/cspot/bell/cJSON/.github/CONTRIBUTING.md new file mode 100644 index 00000000..83d2e599 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/.github/CONTRIBUTING.md @@ -0,0 +1,54 @@ +Contribution Guidelines +======================= + +Contributions to cJSON are welcome. If you find a bug or want to improve cJSON in another way, pull requests are appreciated. + +For bigger changes, in order to avoid wasted effort, please open an issue to discuss the technical details before creating a pull request. + +The further sections explain the process in more detail and provides some guidelines on how contributions should look like. + +Branches +-------- +There are two branches to be aware of, the `master` and the `develop` branch. The `master` branch is reserved for the latest release, so only make pull requests to the `master` branch for small bug- or security fixes (these are usually just a few lines). In all other cases, please make a pull request to the `develop` branch. + +Coding Style +------------ +The coding style has been discussed in [#24](https://github.com/DaveGamble/cJSON/issues/24). The basics are: + +* Use 4 spaces for indentation +* No oneliners (conditions, loops, variable declarations ...) +* Always use parenthesis for control structures +* Don't implicitly rely on operator precedence, use round brackets in expressions. e.g. `(a > b) && (c < d)` instead of `a>b && cbuffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} +``` + +Unit Tests +---------- +cJSON uses the [Unity](https://github.com/ThrowTheSwitch/Unity) library for unit tests. The tests are located in the `tests` directory. In order to add a new test, either add it to one of the existing files (if it fits) or add a new C file for the test. That new file has to be added to the list of tests in `tests/CMakeLists.txt`. + +All new features have to be covered by unit tests. + +Other Notes +----------- +* Internal functions are to be declared static. +* Wrap the return type of external function in the `CJSON_PUBLIC` macro. diff --git a/components/spotify/cspot/bell/cJSON/.github/workflows/CI.yml b/components/spotify/cspot/bell/cJSON/.github/workflows/CI.yml new file mode 100644 index 00000000..dc9d17c6 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/.github/workflows/CI.yml @@ -0,0 +1,102 @@ +name: CI + +on: + push: + branches: [ master ] + paths-ignore: + - '**.md' + - 'LICENSE' + pull_request: + types: [opened, synchronize] + paths-ignore: + - '**.md' + - 'LICENSE' + +jobs: + linux: + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, 'ci skip')" + strategy: + fail-fast: false + matrix: + mem_check: + - ENABLE_VALGRIND + - ENABLE_SANITIZERS + - NONE_MEM_CHECK + compiler: + - GCC + - CLANG + steps: + - uses: actions/checkout@v2 + - name: install build dependencies + run: | + sudo apt-get update + sudo apt-get install clang-8 valgrind + - name: build and test + shell: bash + run: | + if [ "${{ matrix.mem_check }}" == "ENABLE_VALGRIND" ]; then + EVENT_CMAKE_OPTIONS="-DENABLE_CJSON_UTILS=ON -DENABLE_VALGRIND=ON -DENABLE_SAFE_STACK=ON -DENABLE_SANITIZERS=OFF" + elif [ "${{ matrix.mem_check }}" == "ENABLE_SANITIZERS" ]; then + EVENT_CMAKE_OPTIONS="-DENABLE_CJSON_UTILS=ON -DENABLE_VALGRIND=OFF -DENABLE_SAFE_STACK=OFF -DENABLE_SANITIZERS=ON" + else + EVENT_CMAKE_OPTIONS="-DENABLE_CJSON_UTILS=ON -DENABLE_VALGRIND=OFF -DENABLE_SAFE_STACK=OFF -DENABLE_SANITIZERS=OFF" + fi + if [ "${{ matrix.compiler }}" == "GCC" ]; then + export CC=gcc + else + export CC=clang + fi + #run build and test + JOBS=20 + export CTEST_PARALLEL_LEVEL=$JOBS + export CTEST_OUTPUT_ON_FAILURE=1 + mkdir -p build + cd build + echo [cmake]: cmake .. $EVENT_CMAKE_OPTIONS + cmake .. $EVENT_CMAKE_OPTIONS || (rm -rf * && cmake .. $EVENT_CMAKE_OPTIONS) + cmake --build . + make + make test + + macos: + runs-on: macos-latest + if: "!contains(github.event.head_commit.message, 'ci skip')" + strategy: + fail-fast: false + matrix: + mem_check: + - ENABLE_VALGRIND + - ENABLE_SANITIZERS + - NONE_MEM_CHECK + compiler: + - GCC + - CLANG + steps: + - uses: actions/checkout@v2 + - name: build and test + shell: bash + run: | + if [ "${{ matrix.mem_check }}" == "ENABLE_VALGRIND" ]; then + EVENT_CMAKE_OPTIONS="-DENABLE_CJSON_UTILS=ON -DENABLE_VALGRIND=ON -DENABLE_SAFE_STACK=ON -DENABLE_SANITIZERS=OFF" + elif [ "${{ matrix.mem_check }}" == "ENABLE_SANITIZERS" ]; then + EVENT_CMAKE_OPTIONS="-DENABLE_CJSON_UTILS=ON -DENABLE_VALGRIND=OFF -DENABLE_SAFE_STACK=OFF -DENABLE_SANITIZERS=ON" + else + EVENT_CMAKE_OPTIONS="-DENABLE_CJSON_UTILS=ON -DENABLE_VALGRIND=OFF -DENABLE_SAFE_STACK=OFF -DENABLE_SANITIZERS=OFF" + fi + if [ "${{ matrix.compiler }}" == "GCC" ]; then + export CC=gcc + else + export CC=clang + fi + #run build and test + JOBS=20 + export CTEST_PARALLEL_LEVEL=$JOBS + export CTEST_OUTPUT_ON_FAILURE=1 + mkdir -p build + cd build + echo [cmake]: cmake .. $EVENT_CMAKE_OPTIONS + cmake .. $EVENT_CMAKE_OPTIONS || (rm -rf * && cmake .. $EVENT_CMAKE_OPTIONS) + cmake --build . + make + make test diff --git a/components/spotify/cspot/bell/cJSON/.gitignore b/components/spotify/cspot/bell/cJSON/.gitignore new file mode 100644 index 00000000..b3c8d6b4 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/.gitignore @@ -0,0 +1,20 @@ +.svn +test +*.o +*.a +*.so +*.swp +*.patch +tags +*.dylib +build/ +cJSON_test +cJSON_test_utils +libcjson.so.* +libcjson_utils.so.* +*.orig +.vscode +.idea +cmake-build-debug +*.lst +*.lss diff --git a/components/spotify/cspot/bell/cJSON/.travis.yml b/components/spotify/cspot/bell/cJSON/.travis.yml new file mode 100644 index 00000000..e7ff744e --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/.travis.yml @@ -0,0 +1,28 @@ +dist: trusty +sudo: false +language: c +env: + matrix: + - VALGRIND=On SANITIZERS=Off + - VALGRIND=Off SANITIZERS=Off + - VALGRIND=Off SANITIZERS=On +compiler: + - gcc + - clang +addons: + apt: + packages: + - valgrind + - libasan0 + - lib32asan0 + # currently not supported on travis: + # - libasan1 + # - libasan2 + # - libubsan0 + - llvm +script: + - mkdir build + - cd build + - cmake .. -DENABLE_CJSON_UTILS=On -DENABLE_VALGRIND="${VALGRIND}" -DENABLE_SAFE_STACK="${VALGRIND}" -DENABLE_SANITIZERS="${SANITIZERS}" + - make + - make test CTEST_OUTPUT_ON_FAILURE=On diff --git a/components/spotify/cspot/bell/cJSON/CHANGELOG.md b/components/spotify/cspot/bell/cJSON/CHANGELOG.md new file mode 100644 index 00000000..ef5325de --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/CHANGELOG.md @@ -0,0 +1,428 @@ +1.7.15 (Aug 25, 2021) +====== +Fixes: +------ +* Fix potential core dumped for strrchr, see [#546](https://github.com/DaveGamble/cJSON/pull/546) +* Fix null pointer crash in cJSON_CreateXxArray, see [#538](https://github.com/DaveGamble/cJSON/pull/538) +* Fix several null pointer problems on allocation failure, see [#526](https://github.com/DaveGamble/cJSON/pull/526) +* Fix a possible dereference of null pointer, see [#519](https://github.com/DaveGamble/cJSON/pull/519) +* Fix windows build failure about defining nan, see [#518](https://github.com/DaveGamble/cJSON/pull/518) + +1.7.14 (Sep 3, 2020) +====== +Fixes: +------ +* optimize the way to find tail node, see [#503](https://github.com/DaveGamble/cJSON/pull/503) +* Fix WError error on macosx because NAN is a float. Thanks @sappo, see [#484](https://github.com/DaveGamble/cJSON/pull/484) +* Fix some bugs in detach and replace. Thanks @miaoerduo, see [#456](https://github.com/DaveGamble/cJSON/pull/456) + +1.7.13 (Apr 2, 2020) +====== +Features: +--------- +* add new API of cJSON_ParseWithLength without breaking changes. Thanks @caglarivriz, see [#358](https://github.com/DaveGamble/cJSON/pull/358) +* add new API of cJSON_GetNumberValue. Thanks @Intuition, see[#385](https://github.com/DaveGamble/cJSON/pull/385) +* add uninstall target function for CMake. See [#402](https://github.com/DaveGamble/cJSON/pull/402) +* Improve performance of adding item to array. Thanks @xiaomianhehe, see [#430](https://github.com/DaveGamble/cJSON/pull/430), [#448](https://github.com/DaveGamble/cJSON/pull/448) +* add new API of cJSON_SetValuestring, for changing the valuestring safely. See [#451](https://github.com/DaveGamble/cJSON/pull/451) +* add return value for cJSON_AddItemTo... and cJSON_ReplaceItem... (check if the operation successful). See [#453](https://github.com/DaveGamble/cJSON/pull/453) + +Fixes: +------ +* Fix clang -Wfloat-equal warning. Thanks @paulmalovanyi, see [#368](https://github.com/DaveGamble/cJSON/pull/368) +* Fix make failed in mac os. See [#405](https://github.com/DaveGamble/cJSON/pull/405) +* Fix memory leak in cJSONUtils_FindPointerFromObjectTo. Thanks @andywolk for reporting, see [#414](https://github.com/DaveGamble/cJSON/issues/414) +* Fix bug in encode_string_as_pointer. Thanks @AIChangJiang for reporting, see [#439](https://github.com/DaveGamble/cJSON/issues/439) + +1.7.12 (May 17, 2019) +====== +Fixes: +------ +* Fix infinite loop in `cJSON_Minify` (potential Denial of Service). Thanks @Alanscut for reporting, see [#354](https://github.com/DaveGamble/cJSON/issues/354) +* Fix link error for Visual Studio. Thanks @tan-wei, see [#352](https://github.com/DaveGamble/cJSON/pull/352). +* Undefine `true` and `false` for `cJSON_Utils` before redefining them. Thanks @raiden00pl, see [#347](https://github.com/DaveGamble/cJSON/pull/347). + +1.7.11 (Apr 15, 2019) +====== +Fixes: +------ +* Fix a bug where cJSON_Minify could overflow it's buffer, both reading and writing. This is a security issue, see [#338](https://github.com/DaveGamble/cJSON/issues/338). Big thanks @bigric3 for reporting. +* Unset `true` and `false` macros before setting them if they exist. See [#339](https://github.com/DaveGamble/cJSON/issues/339), thanks @raiden00pl for reporting + +1.7.10 (Dec 21, 2018) +====== +Fixes: +------ +* Fix package config file for `libcjson`. Thanks @shiluotang for reporting [#321](https://github.com/DaveGamble/cJSON/issues/321) +* Correctly split lists in `cJSON_Utils`'s merge sort. Thanks @andysCaplin for the fix [#322](https://github.com/DaveGamble/cJSON/issues/322) + +1.7.9 (Dec 16, 2018) +===== +Fixes: +------ +* Fix a bug where `cJSON_GetObjectItemCaseSensitive` would pass a nullpointer to `strcmp` when called on an array, see [#315](https://github.com/DaveGamble/cJSON/issues/315). Thanks @yuweol for reporting. +* Fix error in `cJSON_Utils` where the case sensitivity was not respected, see [#317](https://github.com/DaveGamble/cJSON/pull/317). Thanks @yuta-oxo for fixing. +* Fix some warnings detected by the Visual Studio Static Analyzer, see [#307](https://github.com/DaveGamble/cJSON/pull/307). Thanks @bnason-nf + +1.7.8 (Sep 22, 2018) +====== +Fixes: +------ +* cJSON now works with the `__stdcall` calling convention on Windows, see [#295](https://github.com/DaveGamble/cJSON/pull/295), thanks @zhindes for contributing + +1.7.7 (May 22, 2018) +===== +Fixes: +------ +* Fix a memory leak when realloc fails, see [#267](https://github.com/DaveGamble/cJSON/issues/267), thanks @AlfieDeng for reporting +* Fix a typo in the header file, see [#266](https://github.com/DaveGamble/cJSON/pull/266), thanks @zhaozhixu + +1.7.6 (Apr 13, 2018) +===== +Fixes: +------ +* Add `SONAME` to the ELF files built by the Makefile, see [#252](https://github.com/DaveGamble/cJSON/issues/252), thanks @YanhaoMo for reporting +* Add include guards and `extern "C"` to `cJSON_Utils.h`, see [#256](https://github.com/DaveGamble/cJSON/issues/256), thanks @daschfg for reporting + +Other changes: +* Mark the Makefile as deprecated in the README. + +1.7.5 (Mar 23, 2018) +===== +Fixes: +------ +* Fix a bug in the JSON Patch implementation of `cJSON Utils`, see [#251](https://github.com/DaveGamble/cJSON/pull/251), thanks @bobkocisko. + +1.7.4 (Mar 3, 2018) +===== +Fixes: +------ +* Fix potential use after free if the `string` parameter to `cJSON_AddItemToObject` is an alias of the `string` property of the object that is added,see [#248](https://github.com/DaveGamble/cJSON/issues/248). Thanks @hhallen for reporting. + +1.7.3 (Feb 8, 2018) +===== +Fixes: +------ +* Fix potential double free, thanks @projectgus for reporting [#241](https://github.com/DaveGamble/cJSON/issues/241) + +1.7.2 (Feb 6, 2018) +===== +Fixes: +------ +* Fix the use of GNUInstallDirs variables and the pkgconfig file. Thanks @zeerd for reporting [#240](https://github.com/DaveGamble/cJSON/pull/240) + +1.7.1 (Jan 10, 2018) +===== +Fixes: +------ +* Fixed an Off-By-One error that could lead to an out of bounds write. Thanks @liuyunbin for reporting [#230](https://github.com/DaveGamble/cJSON/issues/230) +* Fixed two errors with buffered printing. Thanks @liuyunbin for reporting [#230](https://github.com/DaveGamble/cJSON/issues/230) + +1.7.0 (Dec 31, 2017) +===== +Features: +--------- +* Large rewrite of the documentation, see [#215](https://github.com/DaveGamble/cJSON/pull/215) +* Added the `cJSON_GetStringValue` function +* Added the `cJSON_CreateStringReference` function +* Added the `cJSON_CreateArrayReference` function +* Added the `cJSON_CreateObjectReference` function +* The `cJSON_Add...ToObject` macros are now functions that return a pointer to the added item, see [#226](https://github.com/DaveGamble/cJSON/pull/226) + +Fixes: +------ +* Fix a problem with `GNUInstallDirs` in the CMakeLists.txt, thanks @yangfl, see [#210](https://github.com/DaveGamble/cJSON/pull/210) +* Fix linking the tests when building as static library, see [#213](https://github.com/DaveGamble/cJSON/issues/213) +* New overrides for the CMake option `BUILD_SHARED_LIBS`, see [#207](https://github.com/DaveGamble/cJSON/issues/207) + +Other Changes: +-------------- +* Readme: Explain how to include cJSON, see [#211](https://github.com/DaveGamble/cJSON/pull/211) +* Removed some trailing spaces in the code, thanks @yangfl, see [#212](https://github.com/DaveGamble/cJSON/pull/212) +* Updated [Unity](https://github.com/ThrowTheSwitch/Unity) and [json-patch-tests](https://github.com/json-patch/json-patch-tests) + +1.6.0 (Oct 9, 2017) +===== +Features: +--------- +* You can now build cJSON as both shared and static library at once with CMake using `-DBUILD_SHARED_AND_STATIC_LIBS=On`, see [#178](https://github.com/DaveGamble/cJSON/issues/178) +* UTF-8 byte order marks are now ignored, see [#184](https://github.com/DaveGamble/cJSON/issues/184) +* Locales can now be disabled with the option `-DENABLE_LOCALES=Off`, see [#202](https://github.com/DaveGamble/cJSON/issues/202), thanks @Casperinous +* Better support for MSVC and Visual Studio + +Other Changes: +-------------- +* Add the new warnings `-Wswitch-enum`, `-Wused-but-makred-unused`, `-Wmissing-variable-declarations`, `-Wunused-macro` +* More number printing tests. +* Continuous integration testing with AppVeyor (semi automatic at this point), thanks @simon-p-r + +1.5.9 (Sep 8, 2017) +===== +Fixes: +------ +* Set the global error pointer even if `return_parse_end` is passed to `cJSON_ParseWithOpts`, see [#200](https://github.com/DaveGamble/cJSON/pull/200), thanks @rmallins + +1.5.8 (Aug 21, 2017) +===== +Fixes: +------ +* Fix `make test` in the Makefile, thanks @YanhaoMo for reporting this [#195](https://github.com/DaveGamble/cJSON/issues/195) + +1.5.7 (Jul 13, 2017) +===== +Fixes: +------ +* Fix a bug where realloc failing would return a pointer to an invalid memory address. This is a security issue as it could potentially be used by an attacker to write to arbitrary memory addresses, see [#189](https://github.com/DaveGamble/cJSON/issues/189), fixed in [954d61e](https://github.com/DaveGamble/cJSON/commit/954d61e5e7cb9dc6c480fc28ac1cdceca07dd5bd), big thanks @timothyjohncarney for reporting this issue +* Fix a spelling mistake in the AFL fuzzer dictionary, see [#185](https://github.com/DaveGamble/cJSON/pull/185), thanks @jwilk + +1.5.6 (Jun 28, 2017) +===== +Fixes: +------ +* Make cJSON a lot more tolerant about passing NULL pointers to its functions, it should now fail safely instead of dereferencing the pointer, see [#183](https://github.com/DaveGamble/cJSON/pull/183). Thanks @msichal for reporting [#182](https://github.com/DaveGamble/cJSON/issues/182) + +1.5.5 (Jun 15, 2017) +===== +Fixes: +------ +* Fix pointers to nested arrays in cJSON_Utils, see [9abe](https://github.com/DaveGamble/cJSON/commit/9abe75e072050f34732a7169740989a082b65134) +* Fix an error with case sensitivity handling in cJSON_Utils, see [b9cc911](https://github.com/DaveGamble/cJSON/commit/b9cc911831b0b3e1bb72f142389428e59f882b38) +* Fix cJSON_Compare for arrays that are prefixes of the other and objects that are a subset of the other, see [03ba72f](https://github.com/DaveGamble/cJSON/commit/03ba72faec115160d1f3aea5582d9b6af5d3e473) and [#180](https://github.com/DaveGamble/cJSON/issues/180), thanks @zhengqb for reporting + +1.5.4 (Jun 5, 2017) +====== +Fixes: +------ +* Fix build with GCC 7.1.1 and optimization level `-O2`, see [bfbd8fe](https://github.com/DaveGamble/cJSON/commit/bfbd8fe0d85f1dd21e508748fc10fc4c27cc51be) + +Other Changes: +-------------- +* Update [Unity](https://github.com/ThrowTheSwitch/Unity) to 3b69beaa58efc41bbbef70a32a46893cae02719d + +1.5.3 (May 23, 2017) +===== +Fixes: +------ +* Fix `cJSON_ReplaceItemInObject` not keeping the name of an item, see [#174](https://github.com/DaveGamble/cJSON/issues/174) + +1.5.2 (May 10, 2017) +===== +Fixes: +------ +* Fix a reading buffer overflow in `parse_string`, see [a167d9e](https://github.com/DaveGamble/cJSON/commit/a167d9e381e5c84bc03de4e261757b031c0c690d) +* Fix compiling with -Wcomma, see [186cce3](https://github.com/DaveGamble/cJSON/commit/186cce3ece6ce6dfcb58ac8b2a63f7846c3493ad) +* Remove leftover attribute from tests, see [b537ca7](https://github.com/DaveGamble/cJSON/commit/b537ca70a35680db66f1f5b8b437f7114daa699a) + +1.5.1 (May 6, 2017) +===== +Fixes: +------ +* Add gcc version guard to the Makefile, see [#164](https://github.com/DaveGamble/cJSON/pull/164), thanks @juvasquezg +* Fix incorrect free in `cJSON_Utils` if custom memory allocator is used, see [#166](https://github.com/DaveGamble/cJSON/pull/166), thanks @prefetchnta + +1.5.0 (May 2, 2017) +===== +Features: +* cJSON finally prints numbers without losing precision, see [#153](https://github.com/DaveGamble/cJSON/pull/153), thanks @DeboraG +* `cJSON_Compare` recursively checks if two cJSON items contain the same values, see [#148](https://github.com/DaveGamble/cJSON/pull/148) +* Provide case sensitive versions of every function where it matters, see [#158](https://github.com/DaveGamble/cJSON/pull/158) and [#159](https://github.com/DaveGamble/cJSON/pull/159) +* Added `cJSON_ReplaceItemViaPointer` and `cJSON_DetachItemViaPointer` +* Added `cJSON_free` and `cJSON_malloc` that expose the internal configured memory allocators. see [02a05ee](https://github.com/DaveGamble/cJSON/commit/02a05eea4e6ba41811f130b322660bea8918e1a0) + + +Enhancements: +------------- +* Parse into a buffer, this will allow parsing `\u0000` in the future (not quite yet though) +* General simplifications and readability improvements +* More unit tests +* Update [unity](https://github.com/ThrowTheSwitch/Unity) testing library to 2.4.1 +* Add the [json-patch-tests](https://github.com/json-patch/json-patch-tests) test suite to test cJSON_Utils. +* Move all tests from `test_utils.c` to unit tests with unity. + +Fixes: +------ +* Fix some warnings with the Microsoft compiler, see [#139](https://github.com/DaveGamble/cJSON/pull/139), thanks @PawelWMS +* Fix several bugs in cJSON_Utils, mostly found with [json-patch-tests](https://github.com/json-patch/json-patch-tests) +* Prevent a stack overflow by specifying a maximum nesting depth `CJSON_NESTING_LIMIT` + +Other Changes: +-------------- +* Move generated files in the `library_config` subdirectory. + +1.4.7 (Apr 19, 2017) +===== +Fixes: +------ +* Fix `cJSONUtils_ApplyPatches`, it was completely broken and apparently nobody noticed (or at least reported it), see [075a06f](https://github.com/DaveGamble/cJSON/commit/075a06f40bdc4f836c7dd7cad690d253a57cfc50) +* Fix inconsistent prototype for `cJSON_GetObjectItemCaseSensitive`, see [51d3df6](https://github.com/DaveGamble/cJSON/commit/51d3df6c9f7b56b860c8fb24abe7bab255cd4fa9), thanks @PawelWMS + +1.4.6 (Apr 9, 2017) +===== +Fixes: +------ +* Several corrections in the README +* Making clear that `valueint` should not be written to +* Fix overflow detection in `ensure`, see [2683d4d](https://github.com/DaveGamble/cJSON/commit/2683d4d9873df87c4bdccc523903ddd78d1ad250) +* Fix a potential null pointer dereference in cJSON_Utils, see [795c3ac](https://github.com/DaveGamble/cJSON/commit/795c3acabed25c9672006b2c0f40be8845064827) +* Replace incorrect `sizeof('\0')` with `sizeof("")`, see [84237ff](https://github.com/DaveGamble/cJSON/commit/84237ff48e69825c94261c624eb0376d0c328139) +* Add caveats section to the README, see [50b3c30](https://github.com/DaveGamble/cJSON/commit/50b3c30dfa89830f8f477ce33713500740ac3b79) +* Make cJSON locale independent, see [#146](https://github.com/DaveGamble/cJSON/pull/146), Thanks @peterh for reporting +* Fix compiling without CMake with MSVC, see [#147](https://github.com/DaveGamble/cJSON/pull/147), Thanks @dertuxmalwieder for reporting + +1.4.5 (Mar 28, 2017) +===== +Fixes: +------ +* Fix bug in `cJSON_SetNumberHelper`, thanks @mmkeeper, see [#138](https://github.com/DaveGamble/cJSON/issues/138) and [ef34500](https://github.com/DaveGamble/cJSON/commit/ef34500693e8c4a2849d41a4bd66fd19c9ec46c2) +* Workaround for internal compiler error in GCC 5.4.0 and 6.3.1 on x86 (2f65e80a3471d053fdc3f8aed23d01dd1782a5cb [GCC bugreport](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80097)) + +1.4.4 (Mar 24, 2017) +===== +Fixes: +------ +* Fix a theoretical integer overflow, (not sure if it is possible on actual hardware), see [e58f7ec](https://github.com/DaveGamble/cJSON/commit/e58f7ec027d00b7cdcbf63e518c1b5268b29b3da) +* Fix an off by one error, see [cc84a44](https://github.com/DaveGamble/cJSON/commit/cc84a446be20cc283bafdc4d94c050ba1111ac02), thanks @gatzka +* Double check the offset of the print buffer in `ensure`, see [1934059](https://github.com/DaveGamble/cJSON/commit/1934059554b9a0971e00f79e96900f422cfdd114) + +Improvements: +* Add a note in the header about required buffer size when using `cJSON_PrintPreallocated`, see [4bfb8800](https://github.com/DaveGamble/cJSON/commit/4bfb88009342fb568295a7f6dc4b7fee74fbf022) + +1.4.3 (Mar 19, 2017) +===== +Fixes: +------ +* Fix compilation of the tests on 32 bit PowerPC and potentially other systems, see [4ec6e76](https://github.com/DaveGamble/cJSON/commit/4ec6e76ea2eec16f54b58e8c95b4c734e59481e4) +* Fix compilation with old GCC compilers (4.3+ were tested), see [227d33](https://github.com/DaveGamble/cJSON/commit/227d3398d6b967879761ebe02c1b63dbd6ea6e0d), [466eb8e](https://github.com/DaveGamble/cJSON/commit/466eb8e3f8a65080f2b3ca4a79ab7b72bd539dba), see also [#126](https://github.com/DaveGamble/cJSON/issues/126) + +1.4.2 (Mar 16, 2017) +===== +Fixes: +------ +* Fix minimum required cmake version, see [30e1e7a](https://github.com/DaveGamble/cJSON/commit/30e1e7af7c63db9b55f5a3cda977a6c032f0b132) +* Fix detection of supported compiler flags, see [76e5296](https://github.com/DaveGamble/cJSON/commit/76e5296d0d05ceb3018a9901639e0e171b44a557) +* Run `cJSON_test` and `cJSON_test_utils` along with unity tests, see [c597601](https://github.com/DaveGamble/cJSON/commit/c597601cf151a757dcf800548f18034d4ddfe2cb) + +1.4.1 (Mar 16, 2017) +===== +Fixes: +------ +* Make `print_number` abort with a failure in out of memory situations, see [cf1842](https://github.com/DaveGamble/cJSON/commit/cf1842dc6f64c49451a022308b4415e4d468be0a) + +1.4.0 (Mar 4, 2017) +===== +Features +-------- +* Functions to check the type of an item, see [#120](https://github.com/DaveGamble/cJSON/pull/120) +* Use dllexport on windows and fvisibility on Unix systems for public functions, see [#116](https://github.com/DaveGamble/cJSON/pull/116), thanks @mjerris +* Remove trailing zeroes from printed numbers, see [#123](https://github.com/DaveGamble/cJSON/pull/123) +* Expose the internal boolean type `cJSON_bool` in the header, see [2d3520e](https://github.com/DaveGamble/cJSON/commit/2d3520e0b9d0eb870e8886e8a21c571eeddbb310) + +Fixes +* Fix handling of NULL pointers in `cJSON_ArrayForEach`, see [b47d0e3](https://github.com/DaveGamble/cJSON/commit/b47d0e34caaef298edfb7bd09a72cfff21d231ff) +* Make it compile with GCC 7 (fix -Wimplicit-fallthrough warning), see [9d07917](https://github.com/DaveGamble/cJSON/commit/9d07917feb1b613544a7513d19233d4c851ad7ad) + +Other Improvements +* internally use realloc if available ([#110](https://github.com/DaveGamble/cJSON/pull/110)) +* builtin support for fuzzing with [afl](http://lcamtuf.coredump.cx/afl/) ([#111](https://github.com/DaveGamble/cJSON/pull/111)) +* unit tests for the print functions ([#112](https://github.com/DaveGamble/cJSON/pull/112)) +* Always use buffered printing ([#113](https://github.com/DaveGamble/cJSON/pull/113)) +* simplify the print functions ([#114](https://github.com/DaveGamble/cJSON/pull/114)) +* Add the compiler flags `-Wdouble-conversion`, `-Wparentheses` and `-Wcomma` ([#122](https://github.com/DaveGamble/cJSON/pull/122)) + +1.3.2 (Mar 1, 2017) +===== +Fixes: +------ +* Don't build the unity library if testing is disabled, see [#121](https://github.com/DaveGamble/cJSON/pull/121). Thanks @ffontaine + +1.3.1 (Feb 27, 2017) +===== +Fixes: +------ +* Bugfix release that fixes an out of bounds read, see [#118](https://github.com/DaveGamble/cJSON/pull/118). This shouldn't have any security implications. + +1.3.0 (Feb 17, 2017) +===== +This release includes a lot of rework in the parser and includes the Cunity unit testing framework, as well as some fixes. I increased the minor version number because there were quite a lot of internal changes. + +Features: +* New type for cJSON structs: `cJSON_Invalid`, see [#108](https://github.com/DaveGamble/cJSON/pull/108) + +Fixes: +------ +* runtime checks for a lot of potential integer overflows +* fix incorrect return in cJSON_PrintBuffered [cf9d57d](https://github.com/DaveGamble/cJSON/commit/cf9d57d56cac21fc59465b8d26cf29bf6d2a87b3) +* fix several potential issues found by [Coverity](https://scan.coverity.com/projects/cjson) +* fix potentially undefined behavior when assigning big numbers to `valueint` ([41e2837](https://github.com/DaveGamble/cJSON/commit/41e2837df1b1091643aff073f2313f6ff3cc10f4)) + * Numbers exceeding `INT_MAX` or lower than `INT_MIN` will be explicitly assigned to `valueint` as `INT_MAX` and `INT_MIN` respectively (saturation on overflow). + * fix the `cJSON_SetNumberValue` macro ([87f7727](https://github.com/DaveGamble/cJSON/commit/87f77274de6b3af00fb9b9a7f3b900ef382296c2)), this slightly changes the behavior, see commit message + +Introduce unit tests +-------------------- + +* Started writing unit tests with the [Cunity](https://github.com/ThrowTheSwitch/Unity) testing framework. Currently this covers the parser functions. + +Also: +* Support for running the tests with [Valgrind](http://valgrind.org) +* Support for compiling the tests with [AddressSanitizer](https://github.com/google/sanitizers) and [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html). +* `travis.yml` file for running unit tests on travis. (not enabled for the repository yet though [#102](https://github.com/DaveGamble/cJSON/issues/102) + +Simplifications +--------------- + +After having unit tests for the parser function in place, I started refactoring the parser functions (as well as others) and making them easier to read and maintain. +* Use `strtod` from the standard library for parsing numbers ([0747669](https://github.com/DaveGamble/cJSON/commit/074766997246481dfc72bfa78f07898a2716473f)) +* Use goto-fail in several parser functions ([#100](https://github.com/DaveGamble/cJSON/pull/100)) +* Rewrite/restructure all of the parsing functions to be easier to understand and have less code paths doing the same as another. ([#109](https://github.com/DaveGamble/cJSON/pull/109)) +* Simplify the buffer allocation strategy to always doubling the needed amount ([9f6fa94](https://github.com/DaveGamble/cJSON/commit/9f6fa94c91a87b71e4c6868dbf2ce431a48517b0)) +* Combined `cJSON_AddItemToObject` and `cJSON_AddItemToObjectCS` to one function ([cf862d](https://github.com/DaveGamble/cJSON/commit/cf862d0fed7f9407e4b046d78d3d8050d2080d12)) + +Other changes +------------- +* Prevent the usage of incompatible C and header versions via preprocessor directive ([123bb1](https://github.com/DaveGamble/cJSON/commit/123bb1af7bfae41d805337fef4b41045ef6c7d25)) +* Let CMake automatically detect compiler flags +* Add new compiler flags (`-Wundef`, `-Wswitch-default`, `-Wconversion`, `-fstack-protector-strong`) ([#98](https://github.com/DaveGamble/cJSON/pull/98)) +* Change internal sizes from `int` to `size_t` ([ecd5678](https://github.com/DaveGamble/cJSON/commit/ecd5678527a6bc422da694e5be9e9979878fe6a0)) +* Change internal strings from `char*` to `unsigned char*` ([28b9ba4](https://github.com/DaveGamble/cJSON/commit/28b9ba4334e0f7309e867e874a31f395c0ac2474)) +* Add `const` in more places + +1.2.1 (Jan 31, 2017) +===== +Fixes: +------ +* Fixes a potential null pointer dereference in cJSON_Utils, discovered using clang's static analyzer by @bnason-nf, see [#96](https://github.com/DaveGamble/cJSON/issues/96) + +1.2.0 (Jan 9, 2017) +===== +Features: +--------- +* Add a new type of cJSON item for raw JSON and support printing it. Thanks @loigu, see [#65](https://github.com/DaveGamble/cJSON/pull/65), [#90](https://github.com/DaveGamble/cJSON/pull/90) + +Fixes: +------ +* Compiler warning if const is casted away, Thanks @gatzka, see [#83](https://github.com/DaveGamble/cJSON/pull/83) +* Fix compile error with strict-overflow on PowerPC, see [#85](https://github.com/DaveGamble/cJSON/issues/85) +* Fix typo in the README, thanks @MicroJoe, see [#88](https://github.com/DaveGamble/cJSON/pull/88) +* Add compile flag for compatibility with C++ compilers + +1.1.0 (Dec 6, 2016) +===== +* Add a function `cJSON_PrintPreallocated` to print to a preallocated buffer, thanks @ChisholmKyle, see [#72](https://github.com/DaveGamble/cJSON/pull/72) +* More compiler warnings when using Clang or GCC, thanks @gatzka, see [#75](https://github.com/DaveGamble/cJSON/pull/75), [#78](https://github.com/DaveGamble/cJSON/pull/78) +* fixed a memory leak in `cJSON_Duplicate`, thanks @alperakcan, see [#81](https://github.com/DaveGamble/cJSON/pull/81) +* fix the `ENABLE_CUSTOM_COMPILER_FLAGS` cmake option + +1.0.2 (Nov 25, 2016) +===== +* Rename internal boolean type, see [#71](https://github.com/DaveGamble/cJSON/issues/71). + +1.0.1 (Nov 20, 2016) +===== +Small bugfix release. +* Fixes a bug with the use of the cJSON structs type in cJSON_Utils, see [d47339e](https://github.com/DaveGamble/cJSON/commit/d47339e2740360e6e0994527d5e4752007480f3a) +* improve code readability +* initialize all variables + +1.0.0 (Nov 17, 2016) +===== +This is the first official versioned release of cJSON. It provides an API version for the shared library and improved Makefile and CMake build files. diff --git a/components/spotify/cspot/bell/cJSON/CMakeLists.txt b/components/spotify/cspot/bell/cJSON/CMakeLists.txt new file mode 100644 index 00000000..c26acbef --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/CMakeLists.txt @@ -0,0 +1,270 @@ +set(CMAKE_LEGACY_CYGWIN_WIN32 0) +cmake_minimum_required(VERSION 3.0) + +project(cJSON + VERSION 1.7.15 + LANGUAGES C) + +cmake_policy(SET CMP0054 NEW) # set CMP0054 policy + +include(GNUInstallDirs) + +set(CJSON_VERSION_SO 1) +set(CJSON_UTILS_VERSION_SO 1) + +set(custom_compiler_flags) + +include(CheckCCompilerFlag) +option(ENABLE_CUSTOM_COMPILER_FLAGS "Enables custom compiler flags" ON) +if (ENABLE_CUSTOM_COMPILER_FLAGS) + if (("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") OR ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")) + list(APPEND custom_compiler_flags + -std=c89 + -pedantic + -Wall + -Wextra + -Werror + -Wstrict-prototypes + -Wwrite-strings + -Wshadow + -Winit-self + -Wcast-align + -Wformat=2 + -Wmissing-prototypes + -Wstrict-overflow=2 + -Wcast-qual + -Wundef + -Wswitch-default + -Wconversion + -Wc++-compat + -fstack-protector-strong + -Wcomma + -Wdouble-promotion + -Wparentheses + -Wformat-overflow + -Wunused-macros + -Wmissing-variable-declarations + -Wused-but-marked-unused + -Wswitch-enum + ) + elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") + # Disable warning c4001 - nonstandard extension 'single line comment' was used + # Define _CRT_SECURE_NO_WARNINGS to disable deprecation warnings for "insecure" C library functions + list(APPEND custom_compiler_flags + /GS + /Za + /sdl + /W4 + /wd4001 + /D_CRT_SECURE_NO_WARNINGS + ) + endif() +endif() + +option(ENABLE_SANITIZERS "Enables AddressSanitizer and UndefinedBehaviorSanitizer." OFF) +if (ENABLE_SANITIZERS) + list(APPEND custom_compiler_flags + -fno-omit-frame-pointer + -fsanitize=address + -fsanitize=undefined + -fsanitize=float-cast-overflow + -fsanitize-address-use-after-scope + -fsanitize=integer + -01 + -fno-sanitize-recover + ) +endif() + +option(ENABLE_SAFE_STACK "Enables the SafeStack instrumentation pass by the Code Pointer Integrity Project" OFF) +if (ENABLE_SAFE_STACK) + if (ENABLE_SANITIZERS) + message(FATAL_ERROR "ENABLE_SAFE_STACK cannot be used in combination with ENABLE_SANITIZERS") + endif() + list(APPEND custom_compiler_flags + -fsanitize=safe-stack + ) +endif() + +option(ENABLE_PUBLIC_SYMBOLS "Export library symbols." On) +if (ENABLE_PUBLIC_SYMBOLS) + list(APPEND custom_compiler_flags -fvisibility=hidden) + add_definitions(-DCJSON_EXPORT_SYMBOLS -DCJSON_API_VISIBILITY) +endif() +option(ENABLE_HIDDEN_SYMBOLS "Hide library symbols." Off) +if (ENABLE_HIDDEN_SYMBOLS) + add_definitions(-DCJSON_HIDE_SYMBOLS -UCJSON_API_VISIBILITY) +endif() + +# apply custom compiler flags +foreach(compiler_flag ${custom_compiler_flags}) + #remove problematic characters + string(REGEX REPLACE "[^a-zA-Z0-9]" "" current_variable ${compiler_flag}) + + CHECK_C_COMPILER_FLAG(${compiler_flag} "FLAG_SUPPORTED_${current_variable}") + if (FLAG_SUPPORTED_${current_variable}) + list(APPEND supported_compiler_flags) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${compiler_flag}") + endif() +endforeach() + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${supported_compiler_flags}") + +option(BUILD_SHARED_LIBS "Build shared libraries" ON) +option(ENABLE_TARGET_EXPORT "Enable exporting of CMake targets. Disable when it causes problems!" ON) + +#cJSON +set(CJSON_LIB cjson) + +file(GLOB HEADERS cJSON.h) +set(SOURCES cJSON.c) + +option(BUILD_SHARED_AND_STATIC_LIBS "Build both shared and static libraries" Off) +option(CJSON_OVERRIDE_BUILD_SHARED_LIBS "Override BUILD_SHARED_LIBS with CJSON_BUILD_SHARED_LIBS" OFF) +option(CJSON_BUILD_SHARED_LIBS "Overrides BUILD_SHARED_LIBS if CJSON_OVERRIDE_BUILD_SHARED_LIBS is enabled" ON) + +if ((CJSON_OVERRIDE_BUILD_SHARED_LIBS AND CJSON_BUILD_SHARED_LIBS) OR ((NOT CJSON_OVERRIDE_BUILD_SHARED_LIBS) AND BUILD_SHARED_LIBS)) + set(CJSON_LIBRARY_TYPE SHARED) +else() + set(CJSON_LIBRARY_TYPE STATIC) +endif() + + +if (NOT BUILD_SHARED_AND_STATIC_LIBS) + add_library("${CJSON_LIB}" "${CJSON_LIBRARY_TYPE}" "${HEADERS}" "${SOURCES}") +else() + # See https://cmake.org/Wiki/CMake_FAQ#How_do_I_make_my_shared_and_static_libraries_have_the_same_root_name.2C_but_different_suffixes.3F + add_library("${CJSON_LIB}" SHARED "${HEADERS}" "${SOURCES}") + add_library("${CJSON_LIB}-static" STATIC "${HEADERS}" "${SOURCES}") + set_target_properties("${CJSON_LIB}-static" PROPERTIES OUTPUT_NAME "${CJSON_LIB}") + set_target_properties("${CJSON_LIB}-static" PROPERTIES PREFIX "lib") +endif() +if (NOT WIN32) + target_link_libraries("${CJSON_LIB}" m) +endif() + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/library_config/libcjson.pc.in" + "${CMAKE_CURRENT_BINARY_DIR}/libcjson.pc" @ONLY) + +install(FILES cJSON.h DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}/cjson") +install (FILES "${CMAKE_CURRENT_BINARY_DIR}/libcjson.pc" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/pkgconfig") +install(TARGETS "${CJSON_LIB}" + EXPORT "${CJSON_LIB}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" + INCLUDES DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}" +) +if (BUILD_SHARED_AND_STATIC_LIBS) + install(TARGETS "${CJSON_LIB}-static" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}") +endif() +if(ENABLE_TARGET_EXPORT) + # export library information for CMake projects + install(EXPORT "${CJSON_LIB}" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cJSON") +endif() + +set_target_properties("${CJSON_LIB}" + PROPERTIES + SOVERSION "${CJSON_VERSION_SO}" + VERSION "${PROJECT_VERSION}") + +#cJSON_Utils +option(ENABLE_CJSON_UTILS "Enable building the cJSON_Utils library." OFF) +if(ENABLE_CJSON_UTILS) + set(CJSON_UTILS_LIB cjson_utils) + + file(GLOB HEADERS_UTILS cJSON_Utils.h) + set(SOURCES_UTILS cJSON_Utils.c) + + if (NOT BUILD_SHARED_AND_STATIC_LIBS) + add_library("${CJSON_UTILS_LIB}" "${CJSON_LIBRARY_TYPE}" "${HEADERS_UTILS}" "${SOURCES_UTILS}") + target_link_libraries("${CJSON_UTILS_LIB}" "${CJSON_LIB}") + else() + add_library("${CJSON_UTILS_LIB}" SHARED "${HEADERS_UTILS}" "${SOURCES_UTILS}") + target_link_libraries("${CJSON_UTILS_LIB}" "${CJSON_LIB}") + add_library("${CJSON_UTILS_LIB}-static" STATIC "${HEADERS_UTILS}" "${SOURCES_UTILS}") + target_link_libraries("${CJSON_UTILS_LIB}-static" "${CJSON_LIB}-static") + set_target_properties("${CJSON_UTILS_LIB}-static" PROPERTIES OUTPUT_NAME "${CJSON_UTILS_LIB}") + set_target_properties("${CJSON_UTILS_LIB}-static" PROPERTIES PREFIX "lib") + endif() + + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/library_config/libcjson_utils.pc.in" + "${CMAKE_CURRENT_BINARY_DIR}/libcjson_utils.pc" @ONLY) + + install(TARGETS "${CJSON_UTILS_LIB}" + EXPORT "${CJSON_UTILS_LIB}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" + INCLUDES DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}" + ) + if (BUILD_SHARED_AND_STATIC_LIBS) + install(TARGETS "${CJSON_UTILS_LIB}-static" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}") + endif() + install(FILES cJSON_Utils.h DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}/cjson") + install (FILES "${CMAKE_CURRENT_BINARY_DIR}/libcjson_utils.pc" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/pkgconfig") + if(ENABLE_TARGET_EXPORT) + # export library information for CMake projects + install(EXPORT "${CJSON_UTILS_LIB}" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cJSON") + endif() + + set_target_properties("${CJSON_UTILS_LIB}" + PROPERTIES + SOVERSION "${CJSON_UTILS_VERSION_SO}" + VERSION "${PROJECT_VERSION}") +endif() + +# create the other package config files +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/library_config/cJSONConfig.cmake.in" + ${PROJECT_BINARY_DIR}/cJSONConfig.cmake @ONLY) +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/library_config/cJSONConfigVersion.cmake.in" + ${PROJECT_BINARY_DIR}/cJSONConfigVersion.cmake @ONLY) + +if(ENABLE_TARGET_EXPORT) + # Install package config files + install(FILES ${PROJECT_BINARY_DIR}/cJSONConfig.cmake + ${PROJECT_BINARY_DIR}/cJSONConfigVersion.cmake + DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cJSON") +endif() + +option(ENABLE_CJSON_TEST "Enable building cJSON test" ON) +if(ENABLE_CJSON_TEST) + enable_testing() + + set(TEST_CJSON cJSON_test) + add_executable("${TEST_CJSON}" test.c) + target_link_libraries("${TEST_CJSON}" "${CJSON_LIB}") + + add_test(NAME ${TEST_CJSON} COMMAND "${CMAKE_CURRENT_BINARY_DIR}/${TEST_CJSON}") + + # Disable -fsanitize=float-divide-by-zero for cJSON_test + if (FLAG_SUPPORTED_fsanitizefloatdividebyzero) + if ("${CMAKE_VERSION}" VERSION_LESS "2.8.12") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-sanitize=float-divide-by-zero") + else() + target_compile_options(${TEST_CJSON} PRIVATE "-fno-sanitize=float-divide-by-zero") + endif() + endif() + + #"check" target that automatically builds everything and runs the tests + add_custom_target(check + COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure + DEPENDS ${TEST_CJSON}) +endif() + +#Create the uninstall target +option(ENABLE_CJSON_UNINSTALL "Enable creating uninstall target" ON) +if(ENABLE_CJSON_UNINSTALL) + add_custom_target(uninstall "${CMAKE_COMMAND}" -P + "${PROJECT_SOURCE_DIR}/library_config/uninstall.cmake") +endif() + +# Enable the use of locales +option(ENABLE_LOCALES "Enable the use of locales" ON) +if(ENABLE_LOCALES) + add_definitions(-DENABLE_LOCALES) +endif() + +add_subdirectory(tests) +add_subdirectory(fuzzing) diff --git a/components/spotify/cspot/bell/cJSON/CONTRIBUTORS.md b/components/spotify/cspot/bell/cJSON/CONTRIBUTORS.md new file mode 100644 index 00000000..f113ab22 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/CONTRIBUTORS.md @@ -0,0 +1,79 @@ +Contributors +============ + +Original Author: +- [Dave Gamble](https://github.com/DaveGamble) + +Current Maintainer: +- [Max Bruckner](https://github.com/FSMaxB) +- [Alan Wang](https://github.com/Alanscut) + +Contributors: +* [Ajay Bhargav](https://github.com/ajaybhargav) +* [Alper Akcan](https://github.com/alperakcan) +* [Andrew Tang](https://github.com/singku) +* [Anton Sergeev](https://github.com/anton-sergeev) +* [Benbuck Nason](https://github.com/bnason-nf) +* [Bernt Johan Damslora](https://github.com/bjda) +* [Bob Kocisko](https://github.com/bobkocisko) +* [Christian Schulze](https://github.com/ChristianSch) +* [Casperinous](https://github.com/Casperinous) +* [ChenYuan](https://github.com/zjuchenyuan) +* [Debora Grosse](https://github.com/DeboraG) +* [dieyushi](https://github.com/dieyushi) +* [DÅngwén Huáng (黄东文)](https://github.com/DongwenHuang) +* [Donough Liu](https://github.com/ldm0) +* [Erez Oxman](https://github.com/erez-o) +* Eswar Yaganti +* [Evan Todd](https://github.com/etodd) +* [Fabrice Fontaine](https://github.com/ffontaine) +* Ian Mobley +* Irwan Djadjadi +* [HuKeping](https://github.com/HuKeping) +* [IvanVoid](https://github.com/npi3pak) +* [Jakub Wilk](https://github.com/jwilk) +* [Jiri Zouhar](https://github.com/loigu) +* [Jonathan Fether](https://github.com/jfether) +* [Julian Ste](https://github.com/julian-st) +* [Julián Vásquez](https://github.com/juvasquezg) +* [Kevin Branigan](https://github.com/kbranigan) +* [Kevin Sapper](https://github.com/sappo) +* [Kyle Chisholm](https://github.com/ChisholmKyle) +* [Linus Wallgren](https://github.com/ecksun) +* [Mateusz Szafoni](https://github.com/raiden00pl) +* Mike Pontillo +* [miaoerduo](https://github.com/miaoerduo) +* [Mike Jerris](https://github.com/mjerris) +* [Mike Robinson](https://github.com/mhrobinson) +* [Moorthy](https://github.com/moorthy-bs) +* [myd7349](https://github.com/myd7349) +* [NancyLi1013](https://github.com/NancyLi1013) +* Paulo Antonio Alvarez +* [PaweÅ‚ Malowany](https://github.com/PawelMalowany) +* [Pawel Winogrodzki](https://github.com/PawelWMS) +* [prefetchnta](https://github.com/prefetchnta) +* [Rafael Leal Dias](https://github.com/rafaeldias) +* [Randy](https://github.com/randy408) +* [raiden00pl](https://github.com/raiden00pl) +* [Robin Mallinson](https://github.com/rmallins) +* [Rod Vagg](https://github.com/rvagg) +* [Roland Meertens](https://github.com/rmeertens) +* [Romain Porte](https://github.com/MicroJoe) +* [SANJEEV BA](https://github.com/basanjeev) +* [Sang-Heon Jeon](https://github.com/lntuition) +* [Simon Sobisch](https://github.com/GitMensch) +* [Simon Ricaldone](https://github.com/simon-p-r) +* [Square789](https://github.com/Square789) +* [Stephan Gatzka](https://github.com/gatzka) +* [Vemake](https://github.com/vemakereporter) +* [Wei Tan](https://github.com/tan-wei) +* [Weston Schmidt](https://github.com/schmidtw) +* [xiaomianhehe](https://github.com/xiaomianhehe) +* [yangfl](https://github.com/yangfl) +* [yuta-oxo](https://github.com/yuta-oxo) +* [Zach Hindes](https://github.com/zhindes) +* [Zhao Zhixu](https://github.com/zhaozhixu) + +And probably more people on [SourceForge](https://sourceforge.net/p/cjson/bugs/search/?q=status%3Aclosed-rejected+or+status%3Aclosed-out-of-date+or+status%3Awont-fix+or+status%3Aclosed-fixed+or+status%3Aclosed&page=0) + +Also thanks to all the people who reported bugs and suggested new features. diff --git a/components/spotify/cspot/bell/cJSON/LICENSE b/components/spotify/cspot/bell/cJSON/LICENSE new file mode 100644 index 00000000..78deb040 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/components/spotify/cspot/bell/cJSON/Makefile b/components/spotify/cspot/bell/cJSON/Makefile new file mode 100644 index 00000000..3bc04ae2 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/Makefile @@ -0,0 +1,163 @@ +CJSON_OBJ = cJSON.o +UTILS_OBJ = cJSON_Utils.o +CJSON_LIBNAME = libcjson +UTILS_LIBNAME = libcjson_utils +CJSON_TEST = cJSON_test + +CJSON_TEST_SRC = cJSON.c test.c + +LDLIBS = -lm + +LIBVERSION = 1.7.15 +CJSON_SOVERSION = 1 +UTILS_SOVERSION = 1 + +CJSON_SO_LDFLAG=-Wl,-soname=$(CJSON_LIBNAME).so.$(CJSON_SOVERSION) +UTILS_SO_LDFLAG=-Wl,-soname=$(UTILS_LIBNAME).so.$(UTILS_SOVERSION) + +PREFIX ?= /usr/local +INCLUDE_PATH ?= include/cjson +LIBRARY_PATH ?= lib + +INSTALL_INCLUDE_PATH = $(DESTDIR)$(PREFIX)/$(INCLUDE_PATH) +INSTALL_LIBRARY_PATH = $(DESTDIR)$(PREFIX)/$(LIBRARY_PATH) + +INSTALL ?= cp -a + +CC = gcc -std=c89 + +# validate gcc version for use fstack-protector-strong +MIN_GCC_VERSION = "4.9" +GCC_VERSION := "`$(CC) -dumpversion`" +IS_GCC_ABOVE_MIN_VERSION := $(shell expr "$(GCC_VERSION)" ">=" "$(MIN_GCC_VERSION)") +ifeq "$(IS_GCC_ABOVE_MIN_VERSION)" "1" + CFLAGS += -fstack-protector-strong +else + CFLAGS += -fstack-protector +endif + +PIC_FLAGS = -fPIC +R_CFLAGS = $(PIC_FLAGS) -pedantic -Wall -Werror -Wstrict-prototypes -Wwrite-strings -Wshadow -Winit-self -Wcast-align -Wformat=2 -Wmissing-prototypes -Wstrict-overflow=2 -Wcast-qual -Wc++-compat -Wundef -Wswitch-default -Wconversion $(CFLAGS) + +uname := $(shell sh -c 'uname -s 2>/dev/null || echo false') + +#library file extensions +SHARED = so +STATIC = a + +## create dynamic (shared) library on Darwin (base OS for MacOSX and IOS) +ifeq (Darwin, $(uname)) + SHARED = dylib + CJSON_SO_LDFLAG = "" + UTILS_SO_LDFLAG = "" +endif + +#cJSON library names +CJSON_SHARED = $(CJSON_LIBNAME).$(SHARED) +CJSON_SHARED_VERSION = $(CJSON_LIBNAME).$(SHARED).$(LIBVERSION) +CJSON_SHARED_SO = $(CJSON_LIBNAME).$(SHARED).$(CJSON_SOVERSION) +CJSON_STATIC = $(CJSON_LIBNAME).$(STATIC) + +#cJSON_Utils library names +UTILS_SHARED = $(UTILS_LIBNAME).$(SHARED) +UTILS_SHARED_VERSION = $(UTILS_LIBNAME).$(SHARED).$(LIBVERSION) +UTILS_SHARED_SO = $(UTILS_LIBNAME).$(SHARED).$(UTILS_SOVERSION) +UTILS_STATIC = $(UTILS_LIBNAME).$(STATIC) + +SHARED_CMD = $(CC) -shared -o + +.PHONY: all shared static tests clean install + +all: shared static tests + +shared: $(CJSON_SHARED) $(UTILS_SHARED) + +static: $(CJSON_STATIC) $(UTILS_STATIC) + +tests: $(CJSON_TEST) + +test: tests + ./$(CJSON_TEST) + +.c.o: + $(CC) -c $(R_CFLAGS) $< + +#tests +#cJSON +$(CJSON_TEST): $(CJSON_TEST_SRC) cJSON.h + $(CC) $(R_CFLAGS) $(CJSON_TEST_SRC) -o $@ $(LDLIBS) -I. + +#static libraries +#cJSON +$(CJSON_STATIC): $(CJSON_OBJ) + $(AR) rcs $@ $< +#cJSON_Utils +$(UTILS_STATIC): $(UTILS_OBJ) + $(AR) rcs $@ $< + +#shared libraries .so.1.0.0 +#cJSON +$(CJSON_SHARED_VERSION): $(CJSON_OBJ) + $(CC) -shared -o $@ $< $(CJSON_SO_LDFLAG) $(LDFLAGS) +#cJSON_Utils +$(UTILS_SHARED_VERSION): $(UTILS_OBJ) + $(CC) -shared -o $@ $< $(CJSON_OBJ) $(UTILS_SO_LDFLAG) $(LDFLAGS) + +#objects +#cJSON +$(CJSON_OBJ): cJSON.c cJSON.h +#cJSON_Utils +$(UTILS_OBJ): cJSON_Utils.c cJSON_Utils.h cJSON.h + + +#links .so -> .so.1 -> .so.1.0.0 +#cJSON +$(CJSON_SHARED_SO): $(CJSON_SHARED_VERSION) + ln -s $(CJSON_SHARED_VERSION) $(CJSON_SHARED_SO) +$(CJSON_SHARED): $(CJSON_SHARED_SO) + ln -s $(CJSON_SHARED_SO) $(CJSON_SHARED) +#cJSON_Utils +$(UTILS_SHARED_SO): $(UTILS_SHARED_VERSION) + ln -s $(UTILS_SHARED_VERSION) $(UTILS_SHARED_SO) +$(UTILS_SHARED): $(UTILS_SHARED_SO) + ln -s $(UTILS_SHARED_SO) $(UTILS_SHARED) + +#install +#cJSON +install-cjson: + mkdir -p $(INSTALL_LIBRARY_PATH) $(INSTALL_INCLUDE_PATH) + $(INSTALL) cJSON.h $(INSTALL_INCLUDE_PATH) + $(INSTALL) $(CJSON_SHARED) $(CJSON_SHARED_SO) $(CJSON_SHARED_VERSION) $(INSTALL_LIBRARY_PATH) +#cJSON_Utils +install-utils: install-cjson + $(INSTALL) cJSON_Utils.h $(INSTALL_INCLUDE_PATH) + $(INSTALL) $(UTILS_SHARED) $(UTILS_SHARED_SO) $(UTILS_SHARED_VERSION) $(INSTALL_LIBRARY_PATH) + +install: install-cjson install-utils + +#uninstall +#cJSON +uninstall-cjson: uninstall-utils + $(RM) $(INSTALL_LIBRARY_PATH)/$(CJSON_SHARED) + $(RM) $(INSTALL_LIBRARY_PATH)/$(CJSON_SHARED_VERSION) + $(RM) $(INSTALL_LIBRARY_PATH)/$(CJSON_SHARED_SO) + $(RM) $(INSTALL_INCLUDE_PATH)/cJSON.h + +#cJSON_Utils +uninstall-utils: + $(RM) $(INSTALL_LIBRARY_PATH)/$(UTILS_SHARED) + $(RM) $(INSTALL_LIBRARY_PATH)/$(UTILS_SHARED_VERSION) + $(RM) $(INSTALL_LIBRARY_PATH)/$(UTILS_SHARED_SO) + $(RM) $(INSTALL_INCLUDE_PATH)/cJSON_Utils.h + +remove-dir: + $(if $(wildcard $(INSTALL_LIBRARY_PATH)/*.*),,rmdir $(INSTALL_LIBRARY_PATH)) + $(if $(wildcard $(INSTALL_INCLUDE_PATH)/*.*),,rmdir $(INSTALL_INCLUDE_PATH)) + +uninstall: uninstall-utils uninstall-cjson remove-dir + +clean: + $(RM) $(CJSON_OBJ) $(UTILS_OBJ) #delete object files + $(RM) $(CJSON_SHARED) $(CJSON_SHARED_VERSION) $(CJSON_SHARED_SO) $(CJSON_STATIC) #delete cJSON + $(RM) $(UTILS_SHARED) $(UTILS_SHARED_VERSION) $(UTILS_SHARED_SO) $(UTILS_STATIC) #delete cJSON_Utils + $(RM) $(CJSON_TEST) #delete test diff --git a/components/spotify/cspot/bell/cJSON/README.md b/components/spotify/cspot/bell/cJSON/README.md new file mode 100644 index 00000000..cd18c7c5 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/README.md @@ -0,0 +1,571 @@ +# cJSON + +Ultralightweight JSON parser in ANSI C. + +## Table of contents +* [License](#license) +* [Usage](#usage) + * [Welcome to cJSON](#welcome-to-cjson) + * [Building](#building) + * [Copying the source](#copying-the-source) + * [CMake](#cmake) + * [Makefile](#makefile) + * [Vcpkg](#Vcpkg) + * [Including cJSON](#including-cjson) + * [Data Structure](#data-structure) + * [Working with the data structure](#working-with-the-data-structure) + * [Basic types](#basic-types) + * [Arrays](#arrays) + * [Objects](#objects) + * [Parsing JSON](#parsing-json) + * [Printing JSON](#printing-json) + * [Example](#example) + * [Printing](#printing) + * [Parsing](#parsing) + * [Caveats](#caveats) + * [Zero Character](#zero-character) + * [Character Encoding](#character-encoding) + * [C Standard](#c-standard) + * [Floating Point Numbers](#floating-point-numbers) + * [Deep Nesting Of Arrays And Objects](#deep-nesting-of-arrays-and-objects) + * [Thread Safety](#thread-safety) + * [Case Sensitivity](#case-sensitivity) + * [Duplicate Object Members](#duplicate-object-members) + * [Enjoy cJSON!](#enjoy-cjson) + +## License + +MIT License + +> Copyright (c) 2009-2017 Dave Gamble and cJSON contributors +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in +> all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +> THE SOFTWARE. + +## Usage + +### Welcome to cJSON. + +cJSON aims to be the dumbest possible parser that you can get your job done with. +It's a single file of C, and a single header file. + +JSON is described best here: http://www.json.org/ +It's like XML, but fat-free. You use it to move data around, store things, or just +generally represent your program's state. + +As a library, cJSON exists to take away as much legwork as it can, but not get in your way. +As a point of pragmatism (i.e. ignoring the truth), I'm going to say that you can use it +in one of two modes: Auto and Manual. Let's have a quick run-through. + +I lifted some JSON from this page: http://www.json.org/fatfree.html +That page inspired me to write cJSON, which is a parser that tries to share the same +philosophy as JSON itself. Simple, dumb, out of the way. + +### Building + +There are several ways to incorporate cJSON into your project. + +#### copying the source + +Because the entire library is only one C file and one header file, you can just copy `cJSON.h` and `cJSON.c` to your projects source and start using it. + +cJSON is written in ANSI C (C89) in order to support as many platforms and compilers as possible. + +#### CMake + +With CMake, cJSON supports a full blown build system. This way you get the most features. CMake with an equal or higher version than 2.8.5 is supported. With CMake it is recommended to do an out of tree build, meaning the compiled files are put in a directory separate from the source files. So in order to build cJSON with CMake on a Unix platform, make a `build` directory and run CMake inside it. + +``` +mkdir build +cd build +cmake .. +``` + +This will create a Makefile and a bunch of other files. You can then compile it: + +``` +make +``` + +And install it with `make install` if you want. By default it installs the headers `/usr/local/include/cjson` and the libraries to `/usr/local/lib`. It also installs files for pkg-config to make it easier to detect and use an existing installation of CMake. And it installs CMake config files, that can be used by other CMake based projects to discover the library. + +You can change the build process with a list of different options that you can pass to CMake. Turn them on with `On` and off with `Off`: + +* `-DENABLE_CJSON_TEST=On`: Enable building the tests. (on by default) +* `-DENABLE_CJSON_UTILS=On`: Enable building cJSON_Utils. (off by default) +* `-DENABLE_TARGET_EXPORT=On`: Enable the export of CMake targets. Turn off if it makes problems. (on by default) +* `-DENABLE_CUSTOM_COMPILER_FLAGS=On`: Enable custom compiler flags (currently for Clang, GCC and MSVC). Turn off if it makes problems. (on by default) +* `-DENABLE_VALGRIND=On`: Run tests with [valgrind](http://valgrind.org). (off by default) +* `-DENABLE_SANITIZERS=On`: Compile cJSON with [AddressSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer) and [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html) enabled (if possible). (off by default) +* `-DENABLE_SAFE_STACK`: Enable the [SafeStack](https://clang.llvm.org/docs/SafeStack.html) instrumentation pass. Currently only works with the Clang compiler. (off by default) +* `-DBUILD_SHARED_LIBS=On`: Build the shared libraries. (on by default) +* `-DBUILD_SHARED_AND_STATIC_LIBS=On`: Build both shared and static libraries. (off by default) +* `-DCMAKE_INSTALL_PREFIX=/usr`: Set a prefix for the installation. +* `-DENABLE_LOCALES=On`: Enable the usage of localeconv method. ( on by default ) +* `-DCJSON_OVERRIDE_BUILD_SHARED_LIBS=On`: Enable overriding the value of `BUILD_SHARED_LIBS` with `-DCJSON_BUILD_SHARED_LIBS`. + +If you are packaging cJSON for a distribution of Linux, you would probably take these steps for example: +``` +mkdir build +cd build +cmake .. -DENABLE_CJSON_UTILS=On -DENABLE_CJSON_TEST=Off -DCMAKE_INSTALL_PREFIX=/usr +make +make DESTDIR=$pkgdir install +``` + +On Windows CMake is usually used to create a Visual Studio solution file by running it inside the Developer Command Prompt for Visual Studio, for exact steps follow the official documentation from CMake and Microsoft and use the online search engine of your choice. The descriptions of the the options above still generally apply, although not all of them work on Windows. + +#### Makefile + +**NOTE:** This Method is deprecated. Use CMake if at all possible. Makefile support is limited to fixing bugs. + +If you don't have CMake available, but still have GNU make. You can use the makefile to build cJSON: + +Run this command in the directory with the source code and it will automatically compile static and shared libraries and a little test program (not the full test suite). + +``` +make all +``` + +If you want, you can install the compiled library to your system using `make install`. By default it will install the headers in `/usr/local/include/cjson` and the libraries in `/usr/local/lib`. But you can change this behavior by setting the `PREFIX` and `DESTDIR` variables: `make PREFIX=/usr DESTDIR=temp install`. And uninstall them with: `make PREFIX=/usr DESTDIR=temp uninstall`. + +#### Vcpkg + +You can download and install cJSON using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: +``` +git clone https://github.com/Microsoft/vcpkg.git +cd vcpkg +./bootstrap-vcpkg.sh +./vcpkg integrate install +vcpkg install cjson +``` + +The cJSON port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. + +### Including cJSON + +If you installed it via CMake or the Makefile, you can include cJSON like this: + +```c +#include +``` + +### Data Structure + +cJSON represents JSON data using the `cJSON` struct data type: + +```c +/* The cJSON structure: */ +typedef struct cJSON +{ + struct cJSON *next; + struct cJSON *prev; + struct cJSON *child; + int type; + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + double valuedouble; + char *string; +} cJSON; +``` + +An item of this type represents a JSON value. The type is stored in `type` as a bit-flag (**this means that you cannot find out the type by just comparing the value of `type`**). + +To check the type of an item, use the corresponding `cJSON_Is...` function. It does a `NULL` check followed by a type check and returns a boolean value if the item is of this type. + +The type can be one of the following: + +* `cJSON_Invalid` (check with `cJSON_IsInvalid`): Represents an invalid item that doesn't contain any value. You automatically have this type if you set the item to all zero bytes. +* `cJSON_False` (check with `cJSON_IsFalse`): Represents a `false` boolean value. You can also check for boolean values in general with `cJSON_IsBool`. +* `cJSON_True` (check with `cJSON_IsTrue`): Represents a `true` boolean value. You can also check for boolean values in general with `cJSON_IsBool`. +* `cJSON_NULL` (check with `cJSON_IsNull`): Represents a `null` value. +* `cJSON_Number` (check with `cJSON_IsNumber`): Represents a number value. The value is stored as a double in `valuedouble` and also in `valueint`. If the number is outside of the range of an integer, `INT_MAX` or `INT_MIN` are used for `valueint`. +* `cJSON_String` (check with `cJSON_IsString`): Represents a string value. It is stored in the form of a zero terminated string in `valuestring`. +* `cJSON_Array` (check with `cJSON_IsArray`): Represent an array value. This is implemented by pointing `child` to a linked list of `cJSON` items that represent the values in the array. The elements are linked together using `next` and `prev`, where the first element has `prev.next == NULL` and the last element `next == NULL`. +* `cJSON_Object` (check with `cJSON_IsObject`): Represents an object value. Objects are stored same way as an array, the only difference is that the items in the object store their keys in `string`. +* `cJSON_Raw` (check with `cJSON_IsRaw`): Represents any kind of JSON that is stored as a zero terminated array of characters in `valuestring`. This can be used, for example, to avoid printing the same static JSON over and over again to save performance. cJSON will never create this type when parsing. Also note that cJSON doesn't check if it is valid JSON. + +Additionally there are the following two flags: + +* `cJSON_IsReference`: Specifies that the item that `child` points to and/or `valuestring` is not owned by this item, it is only a reference. So `cJSON_Delete` and other functions will only deallocate this item, not its `child`/`valuestring`. +* `cJSON_StringIsConst`: This means that `string` points to a constant string. This means that `cJSON_Delete` and other functions will not try to deallocate `string`. + +### Working with the data structure + +For every value type there is a `cJSON_Create...` function that can be used to create an item of that type. +All of these will allocate a `cJSON` struct that can later be deleted with `cJSON_Delete`. +Note that you have to delete them at some point, otherwise you will get a memory leak. +**Important**: If you have added an item to an array or an object already, you **mustn't** delete it with `cJSON_Delete`. Adding it to an array or object transfers its ownership so that when that array or object is deleted, +it gets deleted as well. You also could use `cJSON_SetValuestring` to change a `cJSON_String`'s `valuestring`, and you needn't to free the previous `valuestring` manually. + +#### Basic types + +* **null** is created with `cJSON_CreateNull` +* **booleans** are created with `cJSON_CreateTrue`, `cJSON_CreateFalse` or `cJSON_CreateBool` +* **numbers** are created with `cJSON_CreateNumber`. This will set both `valuedouble` and `valueint`. If the number is outside of the range of an integer, `INT_MAX` or `INT_MIN` are used for `valueint` +* **strings** are created with `cJSON_CreateString` (copies the string) or with `cJSON_CreateStringReference` (directly points to the string. This means that `valuestring` won't be deleted by `cJSON_Delete` and you are responsible for its lifetime, useful for constants) + +#### Arrays + +You can create an empty array with `cJSON_CreateArray`. `cJSON_CreateArrayReference` can be used to create an array that doesn't "own" its content, so its content doesn't get deleted by `cJSON_Delete`. + +To add items to an array, use `cJSON_AddItemToArray` to append items to the end. +Using `cJSON_AddItemReferenceToArray` an element can be added as a reference to another item, array or string. This means that `cJSON_Delete` will not delete that items `child` or `valuestring` properties, so no double frees are occurring if they are already used elsewhere. +To insert items in the middle, use `cJSON_InsertItemInArray`. It will insert an item at the given 0 based index and shift all the existing items to the right. + +If you want to take an item out of an array at a given index and continue using it, use `cJSON_DetachItemFromArray`, it will return the detached item, so be sure to assign it to a pointer, otherwise you will have a memory leak. + +Deleting items is done with `cJSON_DeleteItemFromArray`. It works like `cJSON_DetachItemFromArray`, but deletes the detached item via `cJSON_Delete`. + +You can also replace an item in an array in place. Either with `cJSON_ReplaceItemInArray` using an index or with `cJSON_ReplaceItemViaPointer` given a pointer to an element. `cJSON_ReplaceItemViaPointer` will return `0` if it fails. What this does internally is to detach the old item, delete it and insert the new item in its place. + +To get the size of an array, use `cJSON_GetArraySize`. Use `cJSON_GetArrayItem` to get an element at a given index. + +Because an array is stored as a linked list, iterating it via index is inefficient (`O(n²)`), so you can iterate over an array using the `cJSON_ArrayForEach` macro in `O(n)` time complexity. + +#### Objects + +You can create an empty object with `cJSON_CreateObject`. `cJSON_CreateObjectReference` can be used to create an object that doesn't "own" its content, so its content doesn't get deleted by `cJSON_Delete`. + +To add items to an object, use `cJSON_AddItemToObject`. Use `cJSON_AddItemToObjectCS` to add an item to an object with a name that is a constant or reference (key of the item, `string` in the `cJSON` struct), so that it doesn't get freed by `cJSON_Delete`. +Using `cJSON_AddItemReferenceToArray` an element can be added as a reference to another object, array or string. This means that `cJSON_Delete` will not delete that items `child` or `valuestring` properties, so no double frees are occurring if they are already used elsewhere. + +If you want to take an item out of an object, use `cJSON_DetachItemFromObjectCaseSensitive`, it will return the detached item, so be sure to assign it to a pointer, otherwise you will have a memory leak. + +Deleting items is done with `cJSON_DeleteItemFromObjectCaseSensitive`. It works like `cJSON_DetachItemFromObjectCaseSensitive` followed by `cJSON_Delete`. + +You can also replace an item in an object in place. Either with `cJSON_ReplaceItemInObjectCaseSensitive` using a key or with `cJSON_ReplaceItemViaPointer` given a pointer to an element. `cJSON_ReplaceItemViaPointer` will return `0` if it fails. What this does internally is to detach the old item, delete it and insert the new item in its place. + +To get the size of an object, you can use `cJSON_GetArraySize`, this works because internally objects are stored as arrays. + +If you want to access an item in an object, use `cJSON_GetObjectItemCaseSensitive`. + +To iterate over an object, you can use the `cJSON_ArrayForEach` macro the same way as for arrays. + +cJSON also provides convenient helper functions for quickly creating a new item and adding it to an object, like `cJSON_AddNullToObject`. They return a pointer to the new item or `NULL` if they failed. + +### Parsing JSON + +Given some JSON in a zero terminated string, you can parse it with `cJSON_Parse`. + +```c +cJSON *json = cJSON_Parse(string); +``` + +Given some JSON in a string (whether zero terminated or not), you can parse it with `cJSON_ParseWithLength`. + +```c +cJSON *json = cJSON_ParseWithLength(string, buffer_length); +``` + +It will parse the JSON and allocate a tree of `cJSON` items that represents it. Once it returns, you are fully responsible for deallocating it after use with `cJSON_Delete`. + +The allocator used by `cJSON_Parse` is `malloc` and `free` by default but can be changed (globally) with `cJSON_InitHooks`. + +If an error occurs a pointer to the position of the error in the input string can be accessed using `cJSON_GetErrorPtr`. Note though that this can produce race conditions in multithreading scenarios, in that case it is better to use `cJSON_ParseWithOpts` with `return_parse_end`. +By default, characters in the input string that follow the parsed JSON will not be considered as an error. + +If you want more options, use `cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)`. +`return_parse_end` returns a pointer to the end of the JSON in the input string or the position that an error occurs at (thereby replacing `cJSON_GetErrorPtr` in a thread safe way). `require_null_terminated`, if set to `1` will make it an error if the input string contains data after the JSON. + +If you want more options giving buffer length, use `cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)`. + +### Printing JSON + +Given a tree of `cJSON` items, you can print them as a string using `cJSON_Print`. + +```c +char *string = cJSON_Print(json); +``` + +It will allocate a string and print a JSON representation of the tree into it. Once it returns, you are fully responsible for deallocating it after use with your allocator. (usually `free`, depends on what has been set with `cJSON_InitHooks`). + +`cJSON_Print` will print with whitespace for formatting. If you want to print without formatting, use `cJSON_PrintUnformatted`. + +If you have a rough idea of how big your resulting string will be, you can use `cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)`. `fmt` is a boolean to turn formatting with whitespace on and off. `prebuffer` specifies the first buffer size to use for printing. `cJSON_Print` currently uses 256 bytes for its first buffer size. Once printing runs out of space, a new buffer is allocated and the old gets copied over before printing is continued. + +These dynamic buffer allocations can be completely avoided by using `cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)`. It takes a buffer to a pointer to print to and its length. If the length is reached, printing will fail and it returns `0`. In case of success, `1` is returned. Note that you should provide 5 bytes more than is actually needed, because cJSON is not 100% accurate in estimating if the provided memory is enough. + +### Example + +In this example we want to build and parse the following JSON: + +```json +{ + "name": "Awesome 4K", + "resolutions": [ + { + "width": 1280, + "height": 720 + }, + { + "width": 1920, + "height": 1080 + }, + { + "width": 3840, + "height": 2160 + } + ] +} +``` + +#### Printing + +Let's build the above JSON and print it to a string: + +```c +//create a monitor with a list of supported resolutions +//NOTE: Returns a heap allocated string, you are required to free it after use. +char *create_monitor(void) +{ + const unsigned int resolution_numbers[3][2] = { + {1280, 720}, + {1920, 1080}, + {3840, 2160} + }; + char *string = NULL; + cJSON *name = NULL; + cJSON *resolutions = NULL; + cJSON *resolution = NULL; + cJSON *width = NULL; + cJSON *height = NULL; + size_t index = 0; + + cJSON *monitor = cJSON_CreateObject(); + if (monitor == NULL) + { + goto end; + } + + name = cJSON_CreateString("Awesome 4K"); + if (name == NULL) + { + goto end; + } + /* after creation was successful, immediately add it to the monitor, + * thereby transferring ownership of the pointer to it */ + cJSON_AddItemToObject(monitor, "name", name); + + resolutions = cJSON_CreateArray(); + if (resolutions == NULL) + { + goto end; + } + cJSON_AddItemToObject(monitor, "resolutions", resolutions); + + for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index) + { + resolution = cJSON_CreateObject(); + if (resolution == NULL) + { + goto end; + } + cJSON_AddItemToArray(resolutions, resolution); + + width = cJSON_CreateNumber(resolution_numbers[index][0]); + if (width == NULL) + { + goto end; + } + cJSON_AddItemToObject(resolution, "width", width); + + height = cJSON_CreateNumber(resolution_numbers[index][1]); + if (height == NULL) + { + goto end; + } + cJSON_AddItemToObject(resolution, "height", height); + } + + string = cJSON_Print(monitor); + if (string == NULL) + { + fprintf(stderr, "Failed to print monitor.\n"); + } + +end: + cJSON_Delete(monitor); + return string; +} +``` + +Alternatively we can use the `cJSON_Add...ToObject` helper functions to make our lifes a little easier: + +```c +//NOTE: Returns a heap allocated string, you are required to free it after use. +char *create_monitor_with_helpers(void) +{ + const unsigned int resolution_numbers[3][2] = { + {1280, 720}, + {1920, 1080}, + {3840, 2160} + }; + char *string = NULL; + cJSON *resolutions = NULL; + size_t index = 0; + + cJSON *monitor = cJSON_CreateObject(); + + if (cJSON_AddStringToObject(monitor, "name", "Awesome 4K") == NULL) + { + goto end; + } + + resolutions = cJSON_AddArrayToObject(monitor, "resolutions"); + if (resolutions == NULL) + { + goto end; + } + + for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index) + { + cJSON *resolution = cJSON_CreateObject(); + + if (cJSON_AddNumberToObject(resolution, "width", resolution_numbers[index][0]) == NULL) + { + goto end; + } + + if (cJSON_AddNumberToObject(resolution, "height", resolution_numbers[index][1]) == NULL) + { + goto end; + } + + cJSON_AddItemToArray(resolutions, resolution); + } + + string = cJSON_Print(monitor); + if (string == NULL) + { + fprintf(stderr, "Failed to print monitor.\n"); + } + +end: + cJSON_Delete(monitor); + return string; +} +``` + +#### Parsing + +In this example we will parse a JSON in the above format and check if the monitor supports a Full HD resolution while printing some diagnostic output: + +```c +/* return 1 if the monitor supports full hd, 0 otherwise */ +int supports_full_hd(const char * const monitor) +{ + const cJSON *resolution = NULL; + const cJSON *resolutions = NULL; + const cJSON *name = NULL; + int status = 0; + cJSON *monitor_json = cJSON_Parse(monitor); + if (monitor_json == NULL) + { + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) + { + fprintf(stderr, "Error before: %s\n", error_ptr); + } + status = 0; + goto end; + } + + name = cJSON_GetObjectItemCaseSensitive(monitor_json, "name"); + if (cJSON_IsString(name) && (name->valuestring != NULL)) + { + printf("Checking monitor \"%s\"\n", name->valuestring); + } + + resolutions = cJSON_GetObjectItemCaseSensitive(monitor_json, "resolutions"); + cJSON_ArrayForEach(resolution, resolutions) + { + cJSON *width = cJSON_GetObjectItemCaseSensitive(resolution, "width"); + cJSON *height = cJSON_GetObjectItemCaseSensitive(resolution, "height"); + + if (!cJSON_IsNumber(width) || !cJSON_IsNumber(height)) + { + status = 0; + goto end; + } + + if ((width->valuedouble == 1920) && (height->valuedouble == 1080)) + { + status = 1; + goto end; + } + } + +end: + cJSON_Delete(monitor_json); + return status; +} +``` + +Note that there are no NULL checks except for the result of `cJSON_Parse` because `cJSON_GetObjectItemCaseSensitive` checks for `NULL` inputs already, so a `NULL` value is just propagated and `cJSON_IsNumber` and `cJSON_IsString` return `0` if the input is `NULL`. + +### Caveats + +#### Zero Character + +cJSON doesn't support strings that contain the zero character `'\0'` or `\u0000`. This is impossible with the current API because strings are zero terminated. + +#### Character Encoding + +cJSON only supports UTF-8 encoded input. In most cases it doesn't reject invalid UTF-8 as input though, it just propagates it through as is. As long as the input doesn't contain invalid UTF-8, the output will always be valid UTF-8. + +#### C Standard + +cJSON is written in ANSI C (or C89, C90). If your compiler or C library doesn't follow this standard, correct behavior is not guaranteed. + +NOTE: ANSI C is not C++ therefore it shouldn't be compiled with a C++ compiler. You can compile it with a C compiler and link it with your C++ code however. Although compiling with a C++ compiler might work, correct behavior is not guaranteed. + +#### Floating Point Numbers + +cJSON does not officially support any `double` implementations other than IEEE754 double precision floating point numbers. It might still work with other implementations but bugs with these will be considered invalid. + +The maximum length of a floating point literal that cJSON supports is currently 63 characters. + +#### Deep Nesting Of Arrays And Objects + +cJSON doesn't support arrays and objects that are nested too deeply because this would result in a stack overflow. To prevent this cJSON limits the depth to `CJSON_NESTING_LIMIT` which is 1000 by default but can be changed at compile time. + +#### Thread Safety + +In general cJSON is **not thread safe**. + +However it is thread safe under the following conditions: + +* `cJSON_GetErrorPtr` is never used (the `return_parse_end` parameter of `cJSON_ParseWithOpts` can be used instead) +* `cJSON_InitHooks` is only ever called before using cJSON in any threads. +* `setlocale` is never called before all calls to cJSON functions have returned. + +#### Case Sensitivity + +When cJSON was originally created, it didn't follow the JSON standard and didn't make a distinction between uppercase and lowercase letters. If you want the correct, standard compliant, behavior, you need to use the `CaseSensitive` functions where available. + +#### Duplicate Object Members + +cJSON supports parsing and printing JSON that contains objects that have multiple members with the same name. `cJSON_GetObjectItemCaseSensitive` however will always only return the first one. + +# Enjoy cJSON! + +- Dave Gamble (original author) +- Max Bruckner and Alan Wang (current maintainer) +- and the other [cJSON contributors](CONTRIBUTORS.md) diff --git a/components/spotify/cspot/bell/cJSON/appveyor.yml b/components/spotify/cspot/bell/cJSON/appveyor.yml new file mode 100644 index 00000000..464bf038 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/appveyor.yml @@ -0,0 +1,86 @@ +os: Visual Studio 2015 + +# ENABLE_CUSTOM_COMPILER_FLAGS - on by default +# ENABLE_SANITIZERS - off by default +# ENABLE_PUBLIC_SYMBOLS - on by default +# BUILD_SHARED_LIBS - on by default +# ENABLE_TARGET_EXPORT - on by default +# ENABLE_CJSON_UTILS - off by default +# ENABLE_CJSON_TEST -on by default +# ENABLE_VALGRIND - off by default +# ENABLE_FUZZING - off by default + +environment: + matrix: + - GENERATOR: "Visual Studio 14 2015" + BUILD_SHARED_LIBS: ON + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + - GENERATOR: "Visual Studio 14 2015" + BUILD_SHARED_LIBS: OFF + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + - GENERATOR: "Visual Studio 12 2013" + BUILD_SHARED_LIBS: ON + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + - GENERATOR: "Visual Studio 12 2013" + BUILD_SHARED_LIBS: OFF + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + - GENERATOR: "Visual Studio 11 2012" + BUILD_SHARED_LIBS: ON + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + - GENERATOR: "Visual Studio 11 2012" + BUILD_SHARED_LIBS: OFF + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + - GENERATOR: "Visual Studio 10 2010" + BUILD_SHARED_LIBS: ON + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + - GENERATOR: "Visual Studio 10 2010" + BUILD_SHARED_LIBS: OFF + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + - GENERATOR: "Visual Studio 9 2008" + BUILD_SHARED_LIBS: ON + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + - GENERATOR: "Visual Studio 9 2008" + BUILD_SHARED_LIBS: OFF + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + +platform: + - x86 + - x64 +matrix: + exclude: + - platform: x64 + GENERATOR: "Visual Studio 9 2008" + +configuration: + - Release + + +build_script: + - ps: if($env:PLATFORM -eq "x64") { $env:CMAKE_GEN_SUFFIX=" Win64" } + - cmake "-G%GENERATOR%%CMAKE_GEN_SUFFIX%" -DBUILD_SHARED_LIBS=%BUILD_SHARED_LIBS% -DENABLE_CJSON_TEST=%ENABLE_CJSON_TEST% -H. -Bbuild + - cmake --build build --config "%CONFIGURATION%" + + +on_failure: + - ps: if(Test-Path builds/CMakeFiles/CMakeOutput.log) { cat builds/CMakeFiles/CMakeOutput.log } + - ps: if(Test-Path builds/CMakeFiles/CMakeError.log) { cat builds/CMakeFiles/CMakeError.log } \ No newline at end of file diff --git a/components/spotify/cspot/bell/cJSON/cJSON.c b/components/spotify/cspot/bell/cJSON/cJSON.c new file mode 100644 index 00000000..3063f74e --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/cJSON.c @@ -0,0 +1,3110 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#ifdef true +#undef true +#endif +#define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif +#define false ((cJSON_bool)0) + +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + +#ifndef NAN +#ifdef _WIN32 +#define NAN sqrt(-1.0) +#else +#define NAN 0.0/0.0 +#endif +#endif + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) +{ + if (!cJSON_IsString(item)) + { + return NULL; + } + + return item->valuestring; +} + +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) +{ + if (!cJSON_IsNumber(item)) + { + return (double) NAN; + } + + return item->valuedouble; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 15) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(CJSON_CDECL *allocate)(size_t size); + void (CJSON_CDECL *deallocate)(void *pointer); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void * CJSON_CDECL internal_malloc(size_t size) +{ + return malloc(size); +} +static void CJSON_CDECL internal_free(void *pointer) +{ + free(pointer); +} +static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) +{ + char *copy = NULL; + /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ + if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) + { + return NULL; + } + if (strlen(valuestring) <= strlen(object->valuestring)) + { + strcpy(object->valuestring, valuestring); + return object->valuestring; + } + copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); + if (copy == NULL) + { + return NULL; + } + if (object->valuestring != NULL) + { + cJSON_free(object->valuestring); + } + object->valuestring = copy; + + return copy; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + + memcpy(newbuffer, p->buffer, p->offset + 1); + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) +{ + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test = 0.0; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if (isnan(d) || isinf(d)) + { + length = sprintf((char*)number_buffer, "null"); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occurred */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + if (cannot_access_at_index(buffer, 0)) + { + return buffer; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + size_t buffer_length; + + if (NULL == value) + { + return NULL; + } + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL || 0 == buffer_length) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = buffer_length; + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) +{ + return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +} + +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((length < 0) || (buffer == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buffer; + p.length = (size_t)length; + p.offset = 0; + p.noalloc = true; + p.format = format; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + if ((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL) || (array == item)) + { + return false; + } + + child = array->child; + /* + * To find the last item in array quickly, we use prev in array + */ + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + item->prev = item; + item->next = NULL; + } + else + { + /* append to the end */ + if (child->prev) + { + suffix_object(child->prev, item); + array->child->prev = item; + } + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + return add_item_to_array(array, item); +} + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) +{ + return (void*)string; +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +{ + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) + { + return false; + } + + if (constant_key) + { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return false; + } + + return add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return false; + } + + return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) + { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) +{ + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item != parent->child) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + else if (item->next == NULL) + { + /* last element */ + parent->child->prev = item->prev; + } + + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return false; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + return add_item_to_array(array, newitem); + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (parent->child == item) + { + if (parent->child->prev == parent->child) + { + replacement->prev = replacement; + } + parent->child = replacement; + } + else + { /* + * To find the last item in array quickly, we use prev in array. + * We can't modify the last item's next pointer where this item was the parent's child + */ + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (replacement->next == NULL) + { + parent->child->prev = replacement; + } + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return false; + } + + return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = boolean ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + if (newitem && newitem->child) + { + newitem->child->prev = newchild; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +static void skip_oneline_comment(char **input) +{ + *input += static_strlen("//"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if ((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; + } + } +} + +static void skip_multiline_comment(char **input) +{ + *input += static_strlen("/*"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if (((*input)[0] == '*') && ((*input)[1] == '/')) + { + *input += static_strlen("*/"); + return; + } + } +} + +static void minify_string(char **input, char **output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + + for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if ((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); + } + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + char *into = json; + + if (json == NULL) + { + return; + } + + while (json[0] != '\0') + { + switch (json[0]) + { + case ' ': + case '\t': + case '\r': + case '\n': + json++; + break; + + case '/': + if (json[1] == '/') + { + skip_oneline_comment(&json); + } + else if (json[1] == '*') + { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (compare_double(a->valuedouble, b->valuedouble)) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); +} diff --git a/components/spotify/cspot/bell/cJSON/cJSON.h b/components/spotify/cspot/bell/cJSON/cJSON.h new file mode 100644 index 00000000..92907a2c --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/cJSON.h @@ -0,0 +1,293 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 15 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void *(CJSON_CDECL *malloc_fn)(size_t sz); + void (CJSON_CDECL *free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check item type and return its value */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/array that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. + * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detach items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will + * need to be released. With recurse!=0, it will duplicate any children connected to the item. + * The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. + * The input pointer json cannot point to a read-only address area, such as a string constant, + * but should point to a readable and writable address area. */ +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) +/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/spotify/cspot/bell/cJSON/cJSON_Utils.c b/components/spotify/cspot/bell/cJSON/cJSON_Utils.c new file mode 100644 index 00000000..63651dfb --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/cJSON_Utils.c @@ -0,0 +1,1481 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUCC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUCC__ +#pragma GCC visibility pop +#endif + +#include "cJSON_Utils.h" + +/* define our own boolean type */ +#ifdef true +#undef true +#endif +#define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif +#define false ((cJSON_bool)0) + +static unsigned char* cJSONUtils_strdup(const unsigned char* const string) +{ + size_t length = 0; + unsigned char *copy = NULL; + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*) cJSON_malloc(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +/* string comparison which doesn't consider NULL pointers equal */ +static int compare_strings(const unsigned char *string1, const unsigned char *string2, const cJSON_bool case_sensitive) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + if (case_sensitive) + { + return strcmp((const char*)string1, (const char*)string2); + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) +{ + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); +} + + +/* Compare the next path element of two JSON pointers, two NULL pointers are considered unequal: */ +static cJSON_bool compare_pointers(const unsigned char *name, const unsigned char *pointer, const cJSON_bool case_sensitive) +{ + if ((name == NULL) || (pointer == NULL)) + { + return false; + } + + for (; (*name != '\0') && (*pointer != '\0') && (*pointer != '/'); (void)name++, pointer++) /* compare until next '/' */ + { + if (*pointer == '~') + { + /* check for escaped '~' (~0) and '/' (~1) */ + if (((pointer[1] != '0') || (*name != '~')) && ((pointer[1] != '1') || (*name != '/'))) + { + /* invalid escape sequence or wrong character in *name */ + return false; + } + else + { + pointer++; + } + } + else if ((!case_sensitive && (tolower(*name) != tolower(*pointer))) || (case_sensitive && (*name != *pointer))) + { + return false; + } + } + if (((*pointer != 0) && (*pointer != '/')) != (*name != 0)) + { + /* one string has ended, the other not */ + return false;; + } + + return true; +} + +/* calculate the length of a string if encoded as JSON pointer with ~0 and ~1 escape sequences */ +static size_t pointer_encoded_length(const unsigned char *string) +{ + size_t length; + for (length = 0; *string != '\0'; (void)string++, length++) + { + /* character needs to be escaped? */ + if ((*string == '~') || (*string == '/')) + { + length++; + } + } + + return length; +} + +/* copy a string while escaping '~' and '/' with ~0 and ~1 JSON pointer escape codes */ +static void encode_string_as_pointer(unsigned char *destination, const unsigned char *source) +{ + for (; source[0] != '\0'; (void)source++, destination++) + { + if (source[0] == '/') + { + destination[0] = '~'; + destination[1] = '1'; + destination++; + } + else if (source[0] == '~') + { + destination[0] = '~'; + destination[1] = '0'; + destination++; + } + else + { + destination[0] = source[0]; + } + } + + destination[0] = '\0'; +} + +CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target) +{ + size_t child_index = 0; + cJSON *current_child = 0; + + if ((object == NULL) || (target == NULL)) + { + return NULL; + } + + if (object == target) + { + /* found */ + return (char*)cJSONUtils_strdup((const unsigned char*)""); + } + + /* recursively search all children of the object or array */ + for (current_child = object->child; current_child != NULL; (void)(current_child = current_child->next), child_index++) + { + unsigned char *target_pointer = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(current_child, target); + /* found the target? */ + if (target_pointer != NULL) + { + if (cJSON_IsArray(object)) + { + /* reserve enough memory for a 64 bit integer + '/' and '\0' */ + unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + 20 + sizeof("/")); + /* check if conversion to unsigned long is valid + * This should be eliminated at compile time by dead code elimination + * if size_t is an alias of unsigned long, or if it is bigger */ + if (child_index > ULONG_MAX) + { + cJSON_free(target_pointer); + cJSON_free(full_pointer); + return NULL; + } + sprintf((char*)full_pointer, "/%lu%s", (unsigned long)child_index, target_pointer); /* / */ + cJSON_free(target_pointer); + + return (char*)full_pointer; + } + + if (cJSON_IsObject(object)) + { + unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + pointer_encoded_length((unsigned char*)current_child->string) + 2); + full_pointer[0] = '/'; + encode_string_as_pointer(full_pointer + 1, (unsigned char*)current_child->string); + strcat((char*)full_pointer, (char*)target_pointer); + cJSON_free(target_pointer); + + return (char*)full_pointer; + } + + /* reached leaf of the tree, found nothing */ + cJSON_free(target_pointer); + return NULL; + } + } + + /* not found */ + return NULL; +} + +/* non broken version of cJSON_GetArrayItem */ +static cJSON *get_array_item(const cJSON *array, size_t item) +{ + cJSON *child = array ? array->child : NULL; + while ((child != NULL) && (item > 0)) + { + item--; + child = child->next; + } + + return child; +} + +static cJSON_bool decode_array_index_from_pointer(const unsigned char * const pointer, size_t * const index) +{ + size_t parsed_index = 0; + size_t position = 0; + + if ((pointer[0] == '0') && ((pointer[1] != '\0') && (pointer[1] != '/'))) + { + /* leading zeroes are not permitted */ + return 0; + } + + for (position = 0; (pointer[position] >= '0') && (pointer[0] <= '9'); position++) + { + parsed_index = (10 * parsed_index) + (size_t)(pointer[position] - '0'); + + } + + if ((pointer[position] != '\0') && (pointer[position] != '/')) + { + return 0; + } + + *index = parsed_index; + + return 1; +} + +static cJSON *get_item_from_pointer(cJSON * const object, const char * pointer, const cJSON_bool case_sensitive) +{ + cJSON *current_element = object; + + if (pointer == NULL) + { + return NULL; + } + + /* follow path of the pointer */ + while ((pointer[0] == '/') && (current_element != NULL)) + { + pointer++; + if (cJSON_IsArray(current_element)) + { + size_t index = 0; + if (!decode_array_index_from_pointer((const unsigned char*)pointer, &index)) + { + return NULL; + } + + current_element = get_array_item(current_element, index); + } + else if (cJSON_IsObject(current_element)) + { + current_element = current_element->child; + /* GetObjectItem. */ + while ((current_element != NULL) && !compare_pointers((unsigned char*)current_element->string, (const unsigned char*)pointer, case_sensitive)) + { + current_element = current_element->next; + } + } + else + { + return NULL; + } + + /* skip to the next path token or end of string */ + while ((pointer[0] != '\0') && (pointer[0] != '/')) + { + pointer++; + } + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer) +{ + return get_item_from_pointer(object, pointer, false); +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer) +{ + return get_item_from_pointer(object, pointer, true); +} + +/* JSON Patch implementation. */ +static void decode_pointer_inplace(unsigned char *string) +{ + unsigned char *decoded_string = string; + + if (string == NULL) { + return; + } + + for (; *string; (void)decoded_string++, string++) + { + if (string[0] == '~') + { + if (string[1] == '0') + { + decoded_string[0] = '~'; + } + else if (string[1] == '1') + { + decoded_string[1] = '/'; + } + else + { + /* invalid escape sequence */ + return; + } + + string++; + } + } + + decoded_string[0] = '\0'; +} + +/* non-broken cJSON_DetachItemFromArray */ +static cJSON *detach_item_from_array(cJSON *array, size_t which) +{ + cJSON *c = array->child; + while (c && (which > 0)) + { + c = c->next; + which--; + } + if (!c) + { + /* item doesn't exist */ + return NULL; + } + if (c != array->child) + { + /* not the first element */ + c->prev->next = c->next; + } + if (c->next) + { + c->next->prev = c->prev; + } + if (c == array->child) + { + array->child = c->next; + } + else if (c->next == NULL) + { + array->child->prev = c->prev; + } + /* make sure the detached item doesn't point anywhere anymore */ + c->prev = c->next = NULL; + + return c; +} + +/* detach an item at the given path */ +static cJSON *detach_path(cJSON *object, const unsigned char *path, const cJSON_bool case_sensitive) +{ + unsigned char *parent_pointer = NULL; + unsigned char *child_pointer = NULL; + cJSON *parent = NULL; + cJSON *detached_item = NULL; + + /* copy path and split it in parent and child */ + parent_pointer = cJSONUtils_strdup(path); + if (parent_pointer == NULL) { + goto cleanup; + } + + child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); /* last '/' */ + if (child_pointer == NULL) + { + goto cleanup; + } + /* split strings */ + child_pointer[0] = '\0'; + child_pointer++; + + parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive); + decode_pointer_inplace(child_pointer); + + if (cJSON_IsArray(parent)) + { + size_t index = 0; + if (!decode_array_index_from_pointer(child_pointer, &index)) + { + goto cleanup; + } + detached_item = detach_item_from_array(parent, index); + } + else if (cJSON_IsObject(parent)) + { + detached_item = cJSON_DetachItemFromObject(parent, (char*)child_pointer); + } + else + { + /* Couldn't find object to remove child from. */ + goto cleanup; + } + +cleanup: + if (parent_pointer != NULL) + { + cJSON_free(parent_pointer); + } + + return detached_item; +} + +/* sort lists using mergesort */ +static cJSON *sort_list(cJSON *list, const cJSON_bool case_sensitive) +{ + cJSON *first = list; + cJSON *second = list; + cJSON *current_item = list; + cJSON *result = list; + cJSON *result_tail = NULL; + + if ((list == NULL) || (list->next == NULL)) + { + /* One entry is sorted already. */ + return result; + } + + while ((current_item != NULL) && (current_item->next != NULL) && (compare_strings((unsigned char*)current_item->string, (unsigned char*)current_item->next->string, case_sensitive) < 0)) + { + /* Test for list sorted. */ + current_item = current_item->next; + } + if ((current_item == NULL) || (current_item->next == NULL)) + { + /* Leave sorted lists unmodified. */ + return result; + } + + /* reset pointer to the beginning */ + current_item = list; + while (current_item != NULL) + { + /* Walk two pointers to find the middle. */ + second = second->next; + current_item = current_item->next; + /* advances current_item two steps at a time */ + if (current_item != NULL) + { + current_item = current_item->next; + } + } + if ((second != NULL) && (second->prev != NULL)) + { + /* Split the lists */ + second->prev->next = NULL; + second->prev = NULL; + } + + /* Recursively sort the sub-lists. */ + first = sort_list(first, case_sensitive); + second = sort_list(second, case_sensitive); + result = NULL; + + /* Merge the sub-lists */ + while ((first != NULL) && (second != NULL)) + { + cJSON *smaller = NULL; + if (compare_strings((unsigned char*)first->string, (unsigned char*)second->string, case_sensitive) < 0) + { + smaller = first; + } + else + { + smaller = second; + } + + if (result == NULL) + { + /* start merged list with the smaller element */ + result_tail = smaller; + result = smaller; + } + else + { + /* add smaller element to the list */ + result_tail->next = smaller; + smaller->prev = result_tail; + result_tail = smaller; + } + + if (first == smaller) + { + first = first->next; + } + else + { + second = second->next; + } + } + + if (first != NULL) + { + /* Append rest of first list. */ + if (result == NULL) + { + return first; + } + result_tail->next = first; + first->prev = result_tail; + } + if (second != NULL) + { + /* Append rest of second list */ + if (result == NULL) + { + return second; + } + result_tail->next = second; + second->prev = result_tail; + } + + return result; +} + +static void sort_object(cJSON * const object, const cJSON_bool case_sensitive) +{ + if (object == NULL) + { + return; + } + object->child = sort_list(object->child, case_sensitive); +} + +static cJSON_bool compare_json(cJSON *a, cJSON *b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) + { + /* mismatched type. */ + return false; + } + switch (a->type & 0xFF) + { + case cJSON_Number: + /* numeric mismatch. */ + if ((a->valueint != b->valueint) || (!compare_double(a->valuedouble, b->valuedouble))) + { + return false; + } + else + { + return true; + } + + case cJSON_String: + /* string mismatch. */ + if (strcmp(a->valuestring, b->valuestring) != 0) + { + return false; + } + else + { + return true; + } + + case cJSON_Array: + for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next) + { + cJSON_bool identical = compare_json(a, b, case_sensitive); + if (!identical) + { + return false; + } + } + + /* array size mismatch? (one of both children is not NULL) */ + if ((a != NULL) || (b != NULL)) + { + return false; + } + else + { + return true; + } + + case cJSON_Object: + sort_object(a, case_sensitive); + sort_object(b, case_sensitive); + for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next) + { + cJSON_bool identical = false; + /* compare object keys */ + if (compare_strings((unsigned char*)a->string, (unsigned char*)b->string, case_sensitive)) + { + /* missing member */ + return false; + } + identical = compare_json(a, b, case_sensitive); + if (!identical) + { + return false; + } + } + + /* object length mismatch (one of both children is not null) */ + if ((a != NULL) || (b != NULL)) + { + return false; + } + else + { + return true; + } + + default: + break; + } + + /* null, true or false */ + return true; +} + +/* non broken version of cJSON_InsertItemInArray */ +static cJSON_bool insert_item_in_array(cJSON *array, size_t which, cJSON *newitem) +{ + cJSON *child = array->child; + while (child && (which > 0)) + { + child = child->next; + which--; + } + if (which > 0) + { + /* item is after the end of the array */ + return 0; + } + if (child == NULL) + { + cJSON_AddItemToArray(array, newitem); + return 1; + } + + /* insert into the linked list */ + newitem->next = child; + newitem->prev = child->prev; + child->prev = newitem; + + /* was it at the beginning */ + if (child == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } + + return 1; +} + +static cJSON *get_object_item(const cJSON * const object, const char* name, const cJSON_bool case_sensitive) +{ + if (case_sensitive) + { + return cJSON_GetObjectItemCaseSensitive(object, name); + } + + return cJSON_GetObjectItem(object, name); +} + +enum patch_operation { INVALID, ADD, REMOVE, REPLACE, MOVE, COPY, TEST }; + +static enum patch_operation decode_patch_operation(const cJSON * const patch, const cJSON_bool case_sensitive) +{ + cJSON *operation = get_object_item(patch, "op", case_sensitive); + if (!cJSON_IsString(operation)) + { + return INVALID; + } + + if (strcmp(operation->valuestring, "add") == 0) + { + return ADD; + } + + if (strcmp(operation->valuestring, "remove") == 0) + { + return REMOVE; + } + + if (strcmp(operation->valuestring, "replace") == 0) + { + return REPLACE; + } + + if (strcmp(operation->valuestring, "move") == 0) + { + return MOVE; + } + + if (strcmp(operation->valuestring, "copy") == 0) + { + return COPY; + } + + if (strcmp(operation->valuestring, "test") == 0) + { + return TEST; + } + + return INVALID; +} + +/* overwrite and existing item with another one and free resources on the way */ +static void overwrite_item(cJSON * const root, const cJSON replacement) +{ + if (root == NULL) + { + return; + } + + if (root->string != NULL) + { + cJSON_free(root->string); + } + if (root->valuestring != NULL) + { + cJSON_free(root->valuestring); + } + if (root->child != NULL) + { + cJSON_Delete(root->child); + } + + memcpy(root, &replacement, sizeof(cJSON)); +} + +static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_sensitive) +{ + cJSON *path = NULL; + cJSON *value = NULL; + cJSON *parent = NULL; + enum patch_operation opcode = INVALID; + unsigned char *parent_pointer = NULL; + unsigned char *child_pointer = NULL; + int status = 0; + + path = get_object_item(patch, "path", case_sensitive); + if (!cJSON_IsString(path)) + { + /* malformed patch. */ + status = 2; + goto cleanup; + } + + opcode = decode_patch_operation(patch, case_sensitive); + if (opcode == INVALID) + { + status = 3; + goto cleanup; + } + else if (opcode == TEST) + { + /* compare value: {...} with the given path */ + status = !compare_json(get_item_from_pointer(object, path->valuestring, case_sensitive), get_object_item(patch, "value", case_sensitive), case_sensitive); + goto cleanup; + } + + /* special case for replacing the root */ + if (path->valuestring[0] == '\0') + { + if (opcode == REMOVE) + { + static const cJSON invalid = { NULL, NULL, NULL, cJSON_Invalid, NULL, 0, 0, NULL}; + + overwrite_item(object, invalid); + + status = 0; + goto cleanup; + } + + if ((opcode == REPLACE) || (opcode == ADD)) + { + value = get_object_item(patch, "value", case_sensitive); + if (value == NULL) + { + /* missing "value" for add/replace. */ + status = 7; + goto cleanup; + } + + value = cJSON_Duplicate(value, 1); + if (value == NULL) + { + /* out of memory for add/replace. */ + status = 8; + goto cleanup; + } + + overwrite_item(object, *value); + + /* delete the duplicated value */ + cJSON_free(value); + value = NULL; + + /* the string "value" isn't needed */ + if (object->string != NULL) + { + cJSON_free(object->string); + object->string = NULL; + } + + status = 0; + goto cleanup; + } + } + + if ((opcode == REMOVE) || (opcode == REPLACE)) + { + /* Get rid of old. */ + cJSON *old_item = detach_path(object, (unsigned char*)path->valuestring, case_sensitive); + if (old_item == NULL) + { + status = 13; + goto cleanup; + } + cJSON_Delete(old_item); + if (opcode == REMOVE) + { + /* For Remove, this job is done. */ + status = 0; + goto cleanup; + } + } + + /* Copy/Move uses "from". */ + if ((opcode == MOVE) || (opcode == COPY)) + { + cJSON *from = get_object_item(patch, "from", case_sensitive); + if (from == NULL) + { + /* missing "from" for copy/move. */ + status = 4; + goto cleanup; + } + + if (opcode == MOVE) + { + value = detach_path(object, (unsigned char*)from->valuestring, case_sensitive); + } + if (opcode == COPY) + { + value = get_item_from_pointer(object, from->valuestring, case_sensitive); + } + if (value == NULL) + { + /* missing "from" for copy/move. */ + status = 5; + goto cleanup; + } + if (opcode == COPY) + { + value = cJSON_Duplicate(value, 1); + } + if (value == NULL) + { + /* out of memory for copy/move. */ + status = 6; + goto cleanup; + } + } + else /* Add/Replace uses "value". */ + { + value = get_object_item(patch, "value", case_sensitive); + if (value == NULL) + { + /* missing "value" for add/replace. */ + status = 7; + goto cleanup; + } + value = cJSON_Duplicate(value, 1); + if (value == NULL) + { + /* out of memory for add/replace. */ + status = 8; + goto cleanup; + } + } + + /* Now, just add "value" to "path". */ + + /* split pointer in parent and child */ + parent_pointer = cJSONUtils_strdup((unsigned char*)path->valuestring); + if (parent_pointer) { + child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); + } + if (child_pointer != NULL) + { + child_pointer[0] = '\0'; + child_pointer++; + } + parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive); + decode_pointer_inplace(child_pointer); + + /* add, remove, replace, move, copy, test. */ + if ((parent == NULL) || (child_pointer == NULL)) + { + /* Couldn't find object to add to. */ + status = 9; + goto cleanup; + } + else if (cJSON_IsArray(parent)) + { + if (strcmp((char*)child_pointer, "-") == 0) + { + cJSON_AddItemToArray(parent, value); + value = NULL; + } + else + { + size_t index = 0; + if (!decode_array_index_from_pointer(child_pointer, &index)) + { + status = 11; + goto cleanup; + } + + if (!insert_item_in_array(parent, index, value)) + { + status = 10; + goto cleanup; + } + value = NULL; + } + } + else if (cJSON_IsObject(parent)) + { + if (case_sensitive) + { + cJSON_DeleteItemFromObjectCaseSensitive(parent, (char*)child_pointer); + } + else + { + cJSON_DeleteItemFromObject(parent, (char*)child_pointer); + } + cJSON_AddItemToObject(parent, (char*)child_pointer, value); + value = NULL; + } + else /* parent is not an object */ + { + /* Couldn't find object to add to. */ + status = 9; + goto cleanup; + } + +cleanup: + if (value != NULL) + { + cJSON_Delete(value); + } + if (parent_pointer != NULL) + { + cJSON_free(parent_pointer); + } + + return status; +} + +CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches) +{ + const cJSON *current_patch = NULL; + int status = 0; + + if (!cJSON_IsArray(patches)) + { + /* malformed patches. */ + return 1; + } + + if (patches != NULL) + { + current_patch = patches->child; + } + + while (current_patch != NULL) + { + status = apply_patch(object, current_patch, false); + if (status != 0) + { + return status; + } + current_patch = current_patch->next; + } + + return 0; +} + +CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches) +{ + const cJSON *current_patch = NULL; + int status = 0; + + if (!cJSON_IsArray(patches)) + { + /* malformed patches. */ + return 1; + } + + if (patches != NULL) + { + current_patch = patches->child; + } + + while (current_patch != NULL) + { + status = apply_patch(object, current_patch, true); + if (status != 0) + { + return status; + } + current_patch = current_patch->next; + } + + return 0; +} + +static void compose_patch(cJSON * const patches, const unsigned char * const operation, const unsigned char * const path, const unsigned char *suffix, const cJSON * const value) +{ + cJSON *patch = NULL; + + if ((patches == NULL) || (operation == NULL) || (path == NULL)) + { + return; + } + + patch = cJSON_CreateObject(); + if (patch == NULL) + { + return; + } + cJSON_AddItemToObject(patch, "op", cJSON_CreateString((const char*)operation)); + + if (suffix == NULL) + { + cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)path)); + } + else + { + size_t suffix_length = pointer_encoded_length(suffix); + size_t path_length = strlen((const char*)path); + unsigned char *full_path = (unsigned char*)cJSON_malloc(path_length + suffix_length + sizeof("/")); + + sprintf((char*)full_path, "%s/", (const char*)path); + encode_string_as_pointer(full_path + path_length + 1, suffix); + + cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)full_path)); + cJSON_free(full_path); + } + + if (value != NULL) + { + cJSON_AddItemToObject(patch, "value", cJSON_Duplicate(value, 1)); + } + cJSON_AddItemToArray(patches, patch); +} + +CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value) +{ + compose_patch(array, (const unsigned char*)operation, (const unsigned char*)path, NULL, value); +} + +static void create_patches(cJSON * const patches, const unsigned char * const path, cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive) +{ + if ((from == NULL) || (to == NULL)) + { + return; + } + + if ((from->type & 0xFF) != (to->type & 0xFF)) + { + compose_patch(patches, (const unsigned char*)"replace", path, 0, to); + return; + } + + switch (from->type & 0xFF) + { + case cJSON_Number: + if ((from->valueint != to->valueint) || !compare_double(from->valuedouble, to->valuedouble)) + { + compose_patch(patches, (const unsigned char*)"replace", path, NULL, to); + } + return; + + case cJSON_String: + if (strcmp(from->valuestring, to->valuestring) != 0) + { + compose_patch(patches, (const unsigned char*)"replace", path, NULL, to); + } + return; + + case cJSON_Array: + { + size_t index = 0; + cJSON *from_child = from->child; + cJSON *to_child = to->child; + unsigned char *new_path = (unsigned char*)cJSON_malloc(strlen((const char*)path) + 20 + sizeof("/")); /* Allow space for 64bit int. log10(2^64) = 20 */ + + /* generate patches for all array elements that exist in both "from" and "to" */ + for (index = 0; (from_child != NULL) && (to_child != NULL); (void)(from_child = from_child->next), (void)(to_child = to_child->next), index++) + { + /* check if conversion to unsigned long is valid + * This should be eliminated at compile time by dead code elimination + * if size_t is an alias of unsigned long, or if it is bigger */ + if (index > ULONG_MAX) + { + cJSON_free(new_path); + return; + } + sprintf((char*)new_path, "%s/%lu", path, (unsigned long)index); /* path of the current array element */ + create_patches(patches, new_path, from_child, to_child, case_sensitive); + } + + /* remove leftover elements from 'from' that are not in 'to' */ + for (; (from_child != NULL); (void)(from_child = from_child->next)) + { + /* check if conversion to unsigned long is valid + * This should be eliminated at compile time by dead code elimination + * if size_t is an alias of unsigned long, or if it is bigger */ + if (index > ULONG_MAX) + { + cJSON_free(new_path); + return; + } + sprintf((char*)new_path, "%lu", (unsigned long)index); + compose_patch(patches, (const unsigned char*)"remove", path, new_path, NULL); + } + /* add new elements in 'to' that were not in 'from' */ + for (; (to_child != NULL); (void)(to_child = to_child->next), index++) + { + compose_patch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to_child); + } + cJSON_free(new_path); + return; + } + + case cJSON_Object: + { + cJSON *from_child = NULL; + cJSON *to_child = NULL; + sort_object(from, case_sensitive); + sort_object(to, case_sensitive); + + from_child = from->child; + to_child = to->child; + /* for all object values in the object with more of them */ + while ((from_child != NULL) || (to_child != NULL)) + { + int diff; + if (from_child == NULL) + { + diff = 1; + } + else if (to_child == NULL) + { + diff = -1; + } + else + { + diff = compare_strings((unsigned char*)from_child->string, (unsigned char*)to_child->string, case_sensitive); + } + + if (diff == 0) + { + /* both object keys are the same */ + size_t path_length = strlen((const char*)path); + size_t from_child_name_length = pointer_encoded_length((unsigned char*)from_child->string); + unsigned char *new_path = (unsigned char*)cJSON_malloc(path_length + from_child_name_length + sizeof("/")); + + sprintf((char*)new_path, "%s/", path); + encode_string_as_pointer(new_path + path_length + 1, (unsigned char*)from_child->string); + + /* create a patch for the element */ + create_patches(patches, new_path, from_child, to_child, case_sensitive); + cJSON_free(new_path); + + from_child = from_child->next; + to_child = to_child->next; + } + else if (diff < 0) + { + /* object element doesn't exist in 'to' --> remove it */ + compose_patch(patches, (const unsigned char*)"remove", path, (unsigned char*)from_child->string, NULL); + + from_child = from_child->next; + } + else + { + /* object element doesn't exist in 'from' --> add it */ + compose_patch(patches, (const unsigned char*)"add", path, (unsigned char*)to_child->string, to_child); + + to_child = to_child->next; + } + } + return; + } + + default: + break; + } +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to) +{ + cJSON *patches = NULL; + + if ((from == NULL) || (to == NULL)) + { + return NULL; + } + + patches = cJSON_CreateArray(); + create_patches(patches, (const unsigned char*)"", from, to, false); + + return patches; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to) +{ + cJSON *patches = NULL; + + if ((from == NULL) || (to == NULL)) + { + return NULL; + } + + patches = cJSON_CreateArray(); + create_patches(patches, (const unsigned char*)"", from, to, true); + + return patches; +} + +CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object) +{ + sort_object(object, false); +} + +CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object) +{ + sort_object(object, true); +} + +static cJSON *merge_patch(cJSON *target, const cJSON * const patch, const cJSON_bool case_sensitive) +{ + cJSON *patch_child = NULL; + + if (!cJSON_IsObject(patch)) + { + /* scalar value, array or NULL, just duplicate */ + cJSON_Delete(target); + return cJSON_Duplicate(patch, 1); + } + + if (!cJSON_IsObject(target)) + { + cJSON_Delete(target); + target = cJSON_CreateObject(); + } + + patch_child = patch->child; + while (patch_child != NULL) + { + if (cJSON_IsNull(patch_child)) + { + /* NULL is the indicator to remove a value, see RFC7396 */ + if (case_sensitive) + { + cJSON_DeleteItemFromObjectCaseSensitive(target, patch_child->string); + } + else + { + cJSON_DeleteItemFromObject(target, patch_child->string); + } + } + else + { + cJSON *replace_me = NULL; + cJSON *replacement = NULL; + + if (case_sensitive) + { + replace_me = cJSON_DetachItemFromObjectCaseSensitive(target, patch_child->string); + } + else + { + replace_me = cJSON_DetachItemFromObject(target, patch_child->string); + } + + replacement = merge_patch(replace_me, patch_child, case_sensitive); + if (replacement == NULL) + { + cJSON_Delete(target); + return NULL; + } + + cJSON_AddItemToObject(target, patch_child->string, replacement); + } + patch_child = patch_child->next; + } + return target; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch) +{ + return merge_patch(target, patch, false); +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch) +{ + return merge_patch(target, patch, true); +} + +static cJSON *generate_merge_patch(cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive) +{ + cJSON *from_child = NULL; + cJSON *to_child = NULL; + cJSON *patch = NULL; + if (to == NULL) + { + /* patch to delete everything */ + return cJSON_CreateNull(); + } + if (!cJSON_IsObject(to) || !cJSON_IsObject(from)) + { + return cJSON_Duplicate(to, 1); + } + + sort_object(from, case_sensitive); + sort_object(to, case_sensitive); + + from_child = from->child; + to_child = to->child; + patch = cJSON_CreateObject(); + if (patch == NULL) + { + return NULL; + } + while (from_child || to_child) + { + int diff; + if (from_child != NULL) + { + if (to_child != NULL) + { + diff = strcmp(from_child->string, to_child->string); + } + else + { + diff = -1; + } + } + else + { + diff = 1; + } + + if (diff < 0) + { + /* from has a value that to doesn't have -> remove */ + cJSON_AddItemToObject(patch, from_child->string, cJSON_CreateNull()); + + from_child = from_child->next; + } + else if (diff > 0) + { + /* to has a value that from doesn't have -> add to patch */ + cJSON_AddItemToObject(patch, to_child->string, cJSON_Duplicate(to_child, 1)); + + to_child = to_child->next; + } + else + { + /* object key exists in both objects */ + if (!compare_json(from_child, to_child, case_sensitive)) + { + /* not identical --> generate a patch */ + cJSON_AddItemToObject(patch, to_child->string, cJSONUtils_GenerateMergePatch(from_child, to_child)); + } + + /* next key in the object */ + from_child = from_child->next; + to_child = to_child->next; + } + } + if (patch->child == NULL) + { + /* no patch generated */ + cJSON_Delete(patch); + return NULL; + } + + return patch; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to) +{ + return generate_merge_patch(from, to, false); +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to) +{ + return generate_merge_patch(from, to, true); +} diff --git a/components/spotify/cspot/bell/cJSON/cJSON_Utils.h b/components/spotify/cspot/bell/cJSON/cJSON_Utils.h new file mode 100644 index 00000000..a970c650 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/cJSON_Utils.h @@ -0,0 +1,88 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON_Utils__h +#define cJSON_Utils__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "cJSON.h" + +/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */ +CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer); +CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer); + +/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */ +/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */ +CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to); +CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to); +/* Utility for generating patch array entries. */ +CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value); +/* Returns 0 for success. */ +CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches); +CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches); + +/* +// Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use: +//int cJSONUtils_AtomicApplyPatches(cJSON **object, cJSON *patches) +//{ +// cJSON *modme = cJSON_Duplicate(*object, 1); +// int error = cJSONUtils_ApplyPatches(modme, patches); +// if (!error) +// { +// cJSON_Delete(*object); +// *object = modme; +// } +// else +// { +// cJSON_Delete(modme); +// } +// +// return error; +//} +// Code not added to library since this strategy is a LOT slower. +*/ + +/* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */ +/* target will be modified by patch. return value is new ptr for target. */ +CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch); +CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch); +/* generates a patch to move from -> to */ +/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */ +CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to); +CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to); + +/* Given a root object and a target object, construct a pointer from one to the other. */ +CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target); + +/* Sorts the members of the object into alphabetical order. */ +CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object); +CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/.gitignore b/components/spotify/cspot/bell/cJSON/fuzzing/.gitignore new file mode 100644 index 00000000..c82a2620 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/.gitignore @@ -0,0 +1 @@ +afl-build diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/CMakeLists.txt b/components/spotify/cspot/bell/cJSON/fuzzing/CMakeLists.txt new file mode 100644 index 00000000..1166000d --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/CMakeLists.txt @@ -0,0 +1,34 @@ +option(ENABLE_FUZZING "Create executables and targets for fuzzing cJSON with afl." Off) +if (ENABLE_FUZZING) + find_program(AFL_FUZZ afl-fuzz) + if ("${AFL_FUZZ}" MATCHES "AFL_FUZZ-NOTFOUND") + message(FATAL_ERROR "Couldn't find afl-fuzz.") + endif() + + add_executable(afl-main afl.c) + target_link_libraries(afl-main "${CJSON_LIB}") + + if (NOT ENABLE_SANITIZERS) + message(FATAL_ERROR "Enable sanitizers with -DENABLE_SANITIZERS=On to do fuzzing.") + endif() + + option(ENABLE_FUZZING_PRINT "Fuzz printing functions together with parser." On) + set(fuzz_print_parameter "no") + if (ENABLE_FUZZING_PRINT) + set(fuzz_print_parameter "yes") + endif() + + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error") + + add_custom_target(afl + COMMAND "${AFL_FUZZ}" -i "${CMAKE_CURRENT_SOURCE_DIR}/inputs" -o "${CMAKE_CURRENT_BINARY_DIR}/findings" -x "${CMAKE_CURRENT_SOURCE_DIR}/json.dict" -- "${CMAKE_CURRENT_BINARY_DIR}/afl-main" "@@" "${fuzz_print_parameter}" + DEPENDS afl-main) + + +endif() + +if(ENABLE_CJSON_TEST) + ADD_EXECUTABLE(fuzz_main fuzz_main.c cjson_read_fuzzer.c) + TARGET_LINK_LIBRARIES(fuzz_main cjson) +endif() + diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/afl-prepare-linux.sh b/components/spotify/cspot/bell/cJSON/fuzzing/afl-prepare-linux.sh new file mode 100644 index 00000000..41c9b899 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/afl-prepare-linux.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -x +echo core | sudo tee /proc/sys/kernel/core_pattern +echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/afl.c b/components/spotify/cspot/bell/cJSON/fuzzing/afl.c new file mode 100644 index 00000000..036ed14b --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/afl.c @@ -0,0 +1,176 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "../cJSON.h" + +static char *read_file(const char *filename) +{ + FILE *file = NULL; + long length = 0; + char *content = NULL; + size_t read_chars = 0; + + /* open in read binary mode */ + file = fopen(filename, "rb"); + if (file == NULL) + { + goto cleanup; + } + + /* get the length */ + if (fseek(file, 0, SEEK_END) != 0) + { + goto cleanup; + } + length = ftell(file); + if (length < 0) + { + goto cleanup; + } + if (fseek(file, 0, SEEK_SET) != 0) + { + goto cleanup; + } + + /* allocate content buffer */ + content = (char*)malloc((size_t)length + sizeof("")); + if (content == NULL) + { + goto cleanup; + } + + /* read the file into memory */ + read_chars = fread(content, sizeof(char), (size_t)length, file); + if ((long)read_chars != length) + { + free(content); + content = NULL; + goto cleanup; + } + content[read_chars] = '\0'; + + +cleanup: + if (file != NULL) + { + fclose(file); + } + + return content; +} + +int main(int argc, char** argv) +{ + const char *filename = NULL; + cJSON *item = NULL; + char *json = NULL; + int status = EXIT_FAILURE; + char *printed_json = NULL; + + if ((argc < 2) || (argc > 3)) + { + printf("Usage:\n"); + printf("%s input_file [enable_printing]\n", argv[0]); + printf("\t input_file: file containing the test data\n"); + printf("\t enable_printing: print after parsing, 'yes' or 'no', defaults to 'no'\n"); + goto cleanup; + } + + filename = argv[1]; + +#if __AFL_HAVE_MANUAL_CONTROL + while (__AFL_LOOP(1000)) + { +#endif + status = EXIT_SUCCESS; + + json = read_file(filename); + if ((json == NULL) || (json[0] == '\0') || (json[1] == '\0')) + { + status = EXIT_FAILURE; + goto cleanup; + } + item = cJSON_Parse(json + 2); + if (item == NULL) + { + goto cleanup; + } + + if ((argc == 3) && (strncmp(argv[2], "yes", 3) == 0)) + { + int do_format = 0; + if (json[1] == 'f') + { + do_format = 1; + } + + if (json[0] == 'b') + { + /* buffered printing */ + printed_json = cJSON_PrintBuffered(item, 1, do_format); + } + else + { + /* unbuffered printing */ + if (do_format) + { + printed_json = cJSON_Print(item); + } + else + { + printed_json = cJSON_PrintUnformatted(item); + } + } + if (printed_json == NULL) + { + status = EXIT_FAILURE; + goto cleanup; + } + printf("%s\n", printed_json); + } + +cleanup: + if (item != NULL) + { + cJSON_Delete(item); + item = NULL; + } + if (json != NULL) + { + free(json); + json = NULL; + } + if (printed_json != NULL) + { + free(printed_json); + printed_json = NULL; + } +#if __AFL_HAVE_MANUAL_CONTROL + } +#endif + + return status; +} diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/afl.sh b/components/spotify/cspot/bell/cJSON/fuzzing/afl.sh new file mode 100644 index 00000000..8f0f02fb --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/afl.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +mkdir -p afl-build || exit 1 +cd afl-build || exit 1 +#cleanup +rm -r -- * + +CC=afl-clang-fast cmake ../.. -DENABLE_FUZZING=On -DENABLE_SANITIZERS=On -DBUILD_SHARED_LIBS=Off +make afl diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/cjson_read_fuzzer.c b/components/spotify/cspot/bell/cJSON/fuzzing/cjson_read_fuzzer.c new file mode 100644 index 00000000..aa9c7ba7 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/cjson_read_fuzzer.c @@ -0,0 +1,77 @@ +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../cJSON.h" + +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); /* required by C89 */ + +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + cJSON *json; + size_t offset = 4; + unsigned char *copied; + char *printed_json = NULL; + int minify, require_termination, formatted, buffered; + + + if(size <= offset) return 0; + if(data[size-1] != '\0') return 0; + if(data[0] != '1' && data[0] != '0') return 0; + if(data[1] != '1' && data[1] != '0') return 0; + if(data[2] != '1' && data[2] != '0') return 0; + if(data[3] != '1' && data[3] != '0') return 0; + + minify = data[0] == '1' ? 1 : 0; + require_termination = data[1] == '1' ? 1 : 0; + formatted = data[2] == '1' ? 1 : 0; + buffered = data[3] == '1' ? 1 : 0; + + json = cJSON_ParseWithOpts((const char*)data + offset, NULL, require_termination); + + if(json == NULL) return 0; + + if(buffered) + { + printed_json = cJSON_PrintBuffered(json, 1, formatted); + } + else + { + /* unbuffered printing */ + if(formatted) + { + printed_json = cJSON_Print(json); + } + else + { + printed_json = cJSON_PrintUnformatted(json); + } + } + + if(printed_json != NULL) free(printed_json); + + if(minify) + { + copied = (unsigned char*)malloc(size); + if(copied == NULL) return 0; + + memcpy(copied, data, size); + + cJSON_Minify((char*)copied + offset); + + free(copied); + } + + cJSON_Delete(json); + + return 0; +} + +#ifdef __cplusplus +} +#endif + diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/fuzz_main.c b/components/spotify/cspot/bell/cJSON/fuzzing/fuzz_main.c new file mode 100644 index 00000000..09dc1565 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/fuzz_main.c @@ -0,0 +1,54 @@ +#include +#include +#include + +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); /* required by C89 */ + +/* fuzz target entry point, works without libFuzzer */ + +int main(int argc, char **argv) +{ + FILE *f; + char *buf = NULL; + long siz_buf; + + if(argc < 2) + { + fprintf(stderr, "no input file\n"); + goto err; + } + + f = fopen(argv[1], "rb"); + if(f == NULL) + { + fprintf(stderr, "error opening input file %s\n", argv[1]); + goto err; + } + + fseek(f, 0, SEEK_END); + + siz_buf = ftell(f); + rewind(f); + + if(siz_buf < 1) goto err; + + buf = (char*)malloc((size_t)siz_buf); + if(buf == NULL) + { + fprintf(stderr, "malloc() failed\n"); + goto err; + } + + if(fread(buf, (size_t)siz_buf, 1, f) != 1) + { + fprintf(stderr, "fread() failed\n"); + goto err; + } + + (void)LLVMFuzzerTestOneInput((uint8_t*)buf, (size_t)siz_buf); + +err: + free(buf); + + return 0; +} diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test1 b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test1 new file mode 100644 index 00000000..6a0c0d7c --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test1 @@ -0,0 +1,22 @@ +bf{ + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Markup Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": ["GML", "XML"] + }, + "GlossSee": "markup" + } + } + } + } +} diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test10 b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test10 new file mode 100644 index 00000000..01e9a82f --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test10 @@ -0,0 +1 @@ +bf["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test11 b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test11 new file mode 100644 index 00000000..e81d60d7 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test11 @@ -0,0 +1,8 @@ +bf{ +"name": "Jack (\"Bee\") Nimble", +"format": {"type": "rect", +"width": 1920, +"height": 1080, +"interlace": false,"frame rate": 24 +} +} diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test2 b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test2 new file mode 100644 index 00000000..3fdf8cb7 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test2 @@ -0,0 +1,11 @@ +bf{"menu": { + "id": "file", + "value": "File", + "popup": { + "menuitem": [ + {"value": "New", "onclick": "CreateNewDoc()"}, + {"value": "Open", "onclick": "OpenDoc()"}, + {"value": "Close", "onclick": "CloseDoc()"} + ] + } +}} diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test3 b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test3 new file mode 100644 index 00000000..11edd2f2 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test3 @@ -0,0 +1,26 @@ +bf{"widget": { + "debug": "on", + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "name": "sun1", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "name": "text1", + "hOffset": 250, + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } +}} diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test3.bu b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test3.bu new file mode 100644 index 00000000..eaac4e31 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test3.bu @@ -0,0 +1,26 @@ +bu{"widget": { + "debug": "on", + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "name": "sun1", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "name": "text1", + "hOffset": 250, + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } +}} diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test3.uf b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test3.uf new file mode 100644 index 00000000..edad9778 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test3.uf @@ -0,0 +1,26 @@ +uf{"widget": { + "debug": "on", + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "name": "sun1", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "name": "text1", + "hOffset": 250, + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } +}} diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test3.uu b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test3.uu new file mode 100644 index 00000000..1356b6d9 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test3.uu @@ -0,0 +1,26 @@ +uu{"widget": { + "debug": "on", + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "name": "sun1", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "name": "text1", + "hOffset": 250, + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } +}} diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test4 b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test4 new file mode 100644 index 00000000..943943d2 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test4 @@ -0,0 +1,88 @@ +bf{"web-app": { + "servlet": [ + { + "servlet-name": "cofaxCDS", + "servlet-class": "org.cofax.cds.CDSServlet", + "init-param": { + "configGlossary:installationAt": "Philadelphia, PA", + "configGlossary:adminEmail": "ksm@pobox.com", + "configGlossary:poweredBy": "Cofax", + "configGlossary:poweredByIcon": "/images/cofax.gif", + "configGlossary:staticPath": "/content/static", + "templateProcessorClass": "org.cofax.WysiwygTemplate", + "templateLoaderClass": "org.cofax.FilesTemplateLoader", + "templatePath": "templates", + "templateOverridePath": "", + "defaultListTemplate": "listTemplate.htm", + "defaultFileTemplate": "articleTemplate.htm", + "useJSP": false, + "jspListTemplate": "listTemplate.jsp", + "jspFileTemplate": "articleTemplate.jsp", + "cachePackageTagsTrack": 200, + "cachePackageTagsStore": 200, + "cachePackageTagsRefresh": 60, + "cacheTemplatesTrack": 100, + "cacheTemplatesStore": 50, + "cacheTemplatesRefresh": 15, + "cachePagesTrack": 200, + "cachePagesStore": 100, + "cachePagesRefresh": 10, + "cachePagesDirtyRead": 10, + "searchEngineListTemplate": "forSearchEnginesList.htm", + "searchEngineFileTemplate": "forSearchEngines.htm", + "searchEngineRobotsDb": "WEB-INF/robots.db", + "useDataStore": true, + "dataStoreClass": "org.cofax.SqlDataStore", + "redirectionClass": "org.cofax.SqlRedirection", + "dataStoreName": "cofax", + "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", + "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", + "dataStoreUser": "sa", + "dataStorePassword": "dataStoreTestQuery", + "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", + "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", + "dataStoreInitConns": 10, + "dataStoreMaxConns": 100, + "dataStoreConnUsageLimit": 100, + "dataStoreLogLevel": "debug", + "maxUrlLength": 500}}, + { + "servlet-name": "cofaxEmail", + "servlet-class": "org.cofax.cds.EmailServlet", + "init-param": { + "mailHost": "mail1", + "mailHostOverride": "mail2"}}, + { + "servlet-name": "cofaxAdmin", + "servlet-class": "org.cofax.cds.AdminServlet"}, + + { + "servlet-name": "fileServlet", + "servlet-class": "org.cofax.cds.FileServlet"}, + { + "servlet-name": "cofaxTools", + "servlet-class": "org.cofax.cms.CofaxToolsServlet", + "init-param": { + "templatePath": "toolstemplates/", + "log": 1, + "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", + "logMaxSize": "", + "dataLog": 1, + "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", + "dataLogMaxSize": "", + "removePageCache": "/content/admin/remove?cache=pages&id=", + "removeTemplateCache": "/content/admin/remove?cache=templates&id=", + "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", + "lookInContext": 1, + "adminGroupID": 4, + "betaServer": true}}], + "servlet-mapping": { + "cofaxCDS": "/", + "cofaxEmail": "/cofaxutil/aemail/*", + "cofaxAdmin": "/admin/*", + "fileServlet": "/static/*", + "cofaxTools": "/tools/*"}, + + "taglib": { + "taglib-uri": "cofax.tld", + "taglib-location": "/WEB-INF/tlds/cofax.tld"}}} diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test5 b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test5 new file mode 100644 index 00000000..f6cc84e1 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test5 @@ -0,0 +1,27 @@ +bf{"menu": { + "header": "SVG Viewer", + "items": [ + {"id": "Open"}, + {"id": "OpenNew", "label": "Open New"}, + null, + {"id": "ZoomIn", "label": "Zoom In"}, + {"id": "ZoomOut", "label": "Zoom Out"}, + {"id": "OriginalView", "label": "Original View"}, + null, + {"id": "Quality"}, + {"id": "Pause"}, + {"id": "Mute"}, + null, + {"id": "Find", "label": "Find..."}, + {"id": "FindAgain", "label": "Find Again"}, + {"id": "Copy"}, + {"id": "CopyAgain", "label": "Copy Again"}, + {"id": "CopySVG", "label": "Copy SVG"}, + {"id": "ViewSVG", "label": "View SVG"}, + {"id": "ViewSource", "label": "View Source"}, + {"id": "SaveAs", "label": "Save As"}, + null, + {"id": "Help"}, + {"id": "About", "label": "About Adobe CVG Viewer..."} + ] +}} diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test6 b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test6 new file mode 100644 index 00000000..af279752 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test6 @@ -0,0 +1,16 @@ +bf + + + + + Application Error + + + + + diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test7 b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test7 new file mode 100644 index 00000000..4a3c0b7a --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test7 @@ -0,0 +1,22 @@ +bf[ + { + "precision": "zip", + "Latitude": 37.7668, + "Longitude": -122.3959, + "Address": "", + "City": "SAN FRANCISCO", + "State": "CA", + "Zip": "94107", + "Country": "US" + }, + { + "precision": "zip", + "Latitude": 37.371991, + "Longitude": -122.026020, + "Address": "", + "City": "SUNNYVALE", + "State": "CA", + "Zip": "94085", + "Country": "US" + } + ] diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test8 b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test8 new file mode 100644 index 00000000..3ffe570c --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test8 @@ -0,0 +1,13 @@ +bf{ + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http:/*www.example.com/image/481989943", + "Height": 125, + "Width": "100" + }, + "IDs": [116, 943, 234, 38793] + } + } diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test9 b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test9 new file mode 100644 index 00000000..28c9033a --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/inputs/test9 @@ -0,0 +1,5 @@ +bf[ + [0, -1, 0], + [1, 0, 0], + [0, 0, 1] + ] diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/json.dict b/components/spotify/cspot/bell/cJSON/fuzzing/json.dict new file mode 100644 index 00000000..9bb03d7f --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/json.dict @@ -0,0 +1,47 @@ +# +# AFL dictionary for JSON +# ----------------------------- +# + +object_start="{" +object_end="}" +object_empty="{}" +object_one_element="{\"one\":1}" +object_two_elements="{\"1\":1,\"2\":2}" +object_separator=":" + +array_start="[" +array_end="]" +array_empty="[]" +array_one_element="[1]" +array_two_elements="[1,2]" + +separator="," + +escape_sequence_b="\\b" +escape_sequence_f="\\f" +escape_sequence_n="\\n" +escape_sequence_r="\\r" +escape_sequence_t="\\t" +escape_sequence_quote="\\\"" +escape_sequence_backslash="\\\\" +escape_sequence_slash="\\/" +escape_sequence_utf16_base="\\u" +escape_sequence_utf16="\\u12ab" + +number_integer="1" +number_double="1.0" +number_negative_integer="-1" +number_negative_double="-1.0" +number_engineering1="1e1" +number_engineering2="1e-1" +number_positive_integer="+1" +number_positive_double="+1.0" +number_e="e" +number_plus="+" +number_minus="-" +number_separator="." + +null="null" +true="true" +false="false" diff --git a/components/spotify/cspot/bell/cJSON/fuzzing/ossfuzz.sh b/components/spotify/cspot/bell/cJSON/fuzzing/ossfuzz.sh new file mode 100644 index 00000000..a2da64bf --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/fuzzing/ossfuzz.sh @@ -0,0 +1,18 @@ +#!/bin/bash -eu + +# This script is meant to be run by +# https://github.com/google/oss-fuzz/blob/master/projects/cjson/Dockerfile + +mkdir build +cd build +cmake -DBUILD_SHARED_LIBS=OFF -DENABLE_CJSON_TEST=OFF .. +make -j$(nproc) + +$CXX $CXXFLAGS $SRC/cjson/fuzzing/cjson_read_fuzzer.c -I. \ + -o $OUT/cjson_read_fuzzer \ + $LIB_FUZZING_ENGINE $SRC/cjson/build/libcjson.a + +find $SRC/cjson/fuzzing/inputs -name "*" | \ + xargs zip $OUT/cjson_read_fuzzer_seed_corpus.zip + +cp $SRC/cjson/fuzzing/json.dict $OUT/cjson_read_fuzzer.dict diff --git a/components/spotify/cspot/bell/cJSON/library_config/cJSONConfig.cmake.in b/components/spotify/cspot/bell/cJSON/library_config/cJSONConfig.cmake.in new file mode 100644 index 00000000..909f7a9a --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/library_config/cJSONConfig.cmake.in @@ -0,0 +1,29 @@ +# Whether the utils lib was build. +set(CJSON_UTILS_FOUND @ENABLE_CJSON_UTILS@) + +# The include directories used by cJSON +set(CJSON_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@") +set(CJSON_INCLUDE_DIR "@CMAKE_INSTALL_FULL_INCLUDEDIR@") + +get_filename_component(_dir "${CMAKE_CURRENT_LIST_FILE}" PATH) + +# The cJSON library +set(CJSON_LIBRARY "@CJSON_LIB@") +if(@ENABLE_TARGET_EXPORT@) + # Include the target + include("${_dir}/cjson.cmake") +endif() + +if(CJSON_UTILS_FOUND) + # The cJSON utils library + set(CJSON_UTILS_LIBRARY @CJSON_UTILS_LIB@) + # All cJSON libraries + set(CJSON_LIBRARIES "@CJSON_UTILS_LIB@" "@CJSON_LIB@") + if(@ENABLE_TARGET_EXPORT@) + # Include the target + include("${_dir}/cjson_utils.cmake") + endif() +else() + # All cJSON libraries + set(CJSON_LIBRARIES "@CJSON_LIB@") +endif() diff --git a/components/spotify/cspot/bell/cJSON/library_config/cJSONConfigVersion.cmake.in b/components/spotify/cspot/bell/cJSON/library_config/cJSONConfigVersion.cmake.in new file mode 100644 index 00000000..22ffec0f --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/library_config/cJSONConfigVersion.cmake.in @@ -0,0 +1,11 @@ +set(PACKAGE_VERSION "@PROJECT_VERSION@") + +# Check whether the requested PACKAGE_FIND_VERSION is compatible +if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() diff --git a/components/spotify/cspot/bell/cJSON/library_config/libcjson.pc.in b/components/spotify/cspot/bell/cJSON/library_config/libcjson.pc.in new file mode 100644 index 00000000..de48fe05 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/library_config/libcjson.pc.in @@ -0,0 +1,10 @@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ + +Name: libcjson +Version: @PROJECT_VERSION@ +Description: Ultralightweight JSON parser in ANSI C +URL: https://github.com/DaveGamble/cJSON +Libs: -L${libdir} -lcjson +Libs.private: -lm +Cflags: -I${includedir} -I${includedir}/cjson diff --git a/components/spotify/cspot/bell/cJSON/library_config/libcjson_utils.pc.in b/components/spotify/cspot/bell/cJSON/library_config/libcjson_utils.pc.in new file mode 100644 index 00000000..df2b4e5b --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/library_config/libcjson_utils.pc.in @@ -0,0 +1,10 @@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ + +Name: libcjson_utils +Version: @PROJECT_VERSION@ +Description: An implementation of JSON Pointer, Patch and Merge Patch based on cJSON. +URL: https://github.com/DaveGamble/cJSON +Libs: -L${libdir} -lcjson_utils +Cflags: -I${includedir} -I${includedir}/cjson +Requires: libcjson diff --git a/components/spotify/cspot/bell/cJSON/library_config/uninstall.cmake b/components/spotify/cspot/bell/cJSON/library_config/uninstall.cmake new file mode 100644 index 00000000..e751ec47 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/library_config/uninstall.cmake @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 2.8.5) + +set(MANIFEST "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt") + +if(NOT EXISTS ${MANIFEST}) + message(FATAL_ERROR "Cannot find install mainfest: ${MANIFEST}") +endif() + +file(STRINGS ${MANIFEST} files) +foreach(file ${files}) + if(EXISTS ${file} OR IS_SYMLINK ${file}) + message(STATUS "Removing: ${file}") + + execute_process(COMMAND rm -f ${file} + RESULT_VARIABLE result + OUTPUT_QUIET + ERROR_VARIABLE stderr + ERROR_STRIP_TRAILING_WHITESPACE + ) + + if(NOT ${result} EQUAL 0) + message(FATAL_ERROR "${stderr}") + endif() + else() + message(STATUS "Does-not-exist: ${file}") + endif() +endforeach(file) diff --git a/components/spotify/cspot/bell/cJSON/test.c b/components/spotify/cspot/bell/cJSON/test.c new file mode 100644 index 00000000..986fc6eb --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/test.c @@ -0,0 +1,268 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include +#include "cJSON.h" + +/* Used by some code below as an example datatype. */ +struct record +{ + const char *precision; + double lat; + double lon; + const char *address; + const char *city; + const char *state; + const char *zip; + const char *country; +}; + + +/* Create a bunch of objects as demonstration. */ +static int print_preallocated(cJSON *root) +{ + /* declarations */ + char *out = NULL; + char *buf = NULL; + char *buf_fail = NULL; + size_t len = 0; + size_t len_fail = 0; + + /* formatted print */ + out = cJSON_Print(root); + + /* create buffer to succeed */ + /* the extra 5 bytes are because of inaccuracies when reserving memory */ + len = strlen(out) + 5; + buf = (char*)malloc(len); + if (buf == NULL) + { + printf("Failed to allocate memory.\n"); + exit(1); + } + + /* create buffer to fail */ + len_fail = strlen(out); + buf_fail = (char*)malloc(len_fail); + if (buf_fail == NULL) + { + printf("Failed to allocate memory.\n"); + exit(1); + } + + /* Print to buffer */ + if (!cJSON_PrintPreallocated(root, buf, (int)len, 1)) { + printf("cJSON_PrintPreallocated failed!\n"); + if (strcmp(out, buf) != 0) { + printf("cJSON_PrintPreallocated not the same as cJSON_Print!\n"); + printf("cJSON_Print result:\n%s\n", out); + printf("cJSON_PrintPreallocated result:\n%s\n", buf); + } + free(out); + free(buf_fail); + free(buf); + return -1; + } + + /* success */ + printf("%s\n", buf); + + /* force it to fail */ + if (cJSON_PrintPreallocated(root, buf_fail, (int)len_fail, 1)) { + printf("cJSON_PrintPreallocated failed to show error with insufficient memory!\n"); + printf("cJSON_Print result:\n%s\n", out); + printf("cJSON_PrintPreallocated result:\n%s\n", buf_fail); + free(out); + free(buf_fail); + free(buf); + return -1; + } + + free(out); + free(buf_fail); + free(buf); + return 0; +} + +/* Create a bunch of objects as demonstration. */ +static void create_objects(void) +{ + /* declare a few. */ + cJSON *root = NULL; + cJSON *fmt = NULL; + cJSON *img = NULL; + cJSON *thm = NULL; + cJSON *fld = NULL; + int i = 0; + + /* Our "days of the week" array: */ + const char *strings[7] = + { + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday" + }; + /* Our matrix: */ + int numbers[3][3] = + { + {0, -1, 0}, + {1, 0, 0}, + {0 ,0, 1} + }; + /* Our "gallery" item: */ + int ids[4] = { 116, 943, 234, 38793 }; + /* Our array of "records": */ + struct record fields[2] = + { + { + "zip", + 37.7668, + -1.223959e+2, + "", + "SAN FRANCISCO", + "CA", + "94107", + "US" + }, + { + "zip", + 37.371991, + -1.22026e+2, + "", + "SUNNYVALE", + "CA", + "94085", + "US" + } + }; + volatile double zero = 0.0; + + /* Here we construct some JSON standards, from the JSON site. */ + + /* Our "Video" datatype: */ + root = cJSON_CreateObject(); + cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble")); + cJSON_AddItemToObject(root, "format", fmt = cJSON_CreateObject()); + cJSON_AddStringToObject(fmt, "type", "rect"); + cJSON_AddNumberToObject(fmt, "width", 1920); + cJSON_AddNumberToObject(fmt, "height", 1080); + cJSON_AddFalseToObject (fmt, "interlace"); + cJSON_AddNumberToObject(fmt, "frame rate", 24); + + /* Print to text */ + if (print_preallocated(root) != 0) { + cJSON_Delete(root); + exit(EXIT_FAILURE); + } + cJSON_Delete(root); + + /* Our "days of the week" array: */ + root = cJSON_CreateStringArray(strings, 7); + + if (print_preallocated(root) != 0) { + cJSON_Delete(root); + exit(EXIT_FAILURE); + } + cJSON_Delete(root); + + /* Our matrix: */ + root = cJSON_CreateArray(); + for (i = 0; i < 3; i++) + { + cJSON_AddItemToArray(root, cJSON_CreateIntArray(numbers[i], 3)); + } + + /* cJSON_ReplaceItemInArray(root, 1, cJSON_CreateString("Replacement")); */ + + if (print_preallocated(root) != 0) { + cJSON_Delete(root); + exit(EXIT_FAILURE); + } + cJSON_Delete(root); + + /* Our "gallery" item: */ + root = cJSON_CreateObject(); + cJSON_AddItemToObject(root, "Image", img = cJSON_CreateObject()); + cJSON_AddNumberToObject(img, "Width", 800); + cJSON_AddNumberToObject(img, "Height", 600); + cJSON_AddStringToObject(img, "Title", "View from 15th Floor"); + cJSON_AddItemToObject(img, "Thumbnail", thm = cJSON_CreateObject()); + cJSON_AddStringToObject(thm, "Url", "http:/*www.example.com/image/481989943"); + cJSON_AddNumberToObject(thm, "Height", 125); + cJSON_AddStringToObject(thm, "Width", "100"); + cJSON_AddItemToObject(img, "IDs", cJSON_CreateIntArray(ids, 4)); + + if (print_preallocated(root) != 0) { + cJSON_Delete(root); + exit(EXIT_FAILURE); + } + cJSON_Delete(root); + + /* Our array of "records": */ + root = cJSON_CreateArray(); + for (i = 0; i < 2; i++) + { + cJSON_AddItemToArray(root, fld = cJSON_CreateObject()); + cJSON_AddStringToObject(fld, "precision", fields[i].precision); + cJSON_AddNumberToObject(fld, "Latitude", fields[i].lat); + cJSON_AddNumberToObject(fld, "Longitude", fields[i].lon); + cJSON_AddStringToObject(fld, "Address", fields[i].address); + cJSON_AddStringToObject(fld, "City", fields[i].city); + cJSON_AddStringToObject(fld, "State", fields[i].state); + cJSON_AddStringToObject(fld, "Zip", fields[i].zip); + cJSON_AddStringToObject(fld, "Country", fields[i].country); + } + + /* cJSON_ReplaceItemInObject(cJSON_GetArrayItem(root, 1), "City", cJSON_CreateIntArray(ids, 4)); */ + + if (print_preallocated(root) != 0) { + cJSON_Delete(root); + exit(EXIT_FAILURE); + } + cJSON_Delete(root); + + root = cJSON_CreateObject(); + cJSON_AddNumberToObject(root, "number", 1.0 / zero); + + if (print_preallocated(root) != 0) { + cJSON_Delete(root); + exit(EXIT_FAILURE); + } + cJSON_Delete(root); +} + +int CJSON_CDECL main(void) +{ + /* print the version */ + printf("Version: %s\n", cJSON_Version()); + + /* Now some samplecode for building objects concisely: */ + create_objects(); + + return 0; +} diff --git a/components/spotify/cspot/bell/cJSON/tests/CMakeLists.txt b/components/spotify/cspot/bell/cJSON/tests/CMakeLists.txt new file mode 100644 index 00000000..c7592213 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/CMakeLists.txt @@ -0,0 +1,119 @@ +if(ENABLE_CJSON_TEST) + add_library(unity STATIC unity/src/unity.c) + + # Disable -Werror for Unity + if (FLAG_SUPPORTED_Werror) + if ("${CMAKE_VERSION}" VERSION_LESS "2.8.12") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error") + else() + target_compile_options(unity PRIVATE "-Wno-error") + endif() + endif() + # Disable -fvisibility=hidden for Unity + if (FLAG_SUPPORTED_fvisibilityhidden) + if ("${CMAKE_VERSION}" VERSION_LESS "2.8.12") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=default") + else() + target_compile_options(unity PRIVATE "-fvisibility=default") + endif() + endif() + # Disable -fsanitize=float-divide-by-zero for Unity (GCC bug on x86 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80097) + if (FLAG_SUPPORTED_fsanitizefloatdividebyzero AND (CMAKE_C_COMPILER_ID STREQUAL "GNU")) + if ("${CMAKE_VERSION}" VERSION_LESS "2.8.12") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-sanitize=float-divide-by-zero") + else() + target_compile_options(unity PRIVATE "-fno-sanitize=float-divide-by-zero") + endif() + endif() + # Disable -Wswitch-enum for Unity + if (FLAG_SUPPORTED_Wswitchenum) + if ("${CMAKE_VERSION}" VERSION_LESS "2.8.12") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-switch-enum") + else() + target_compile_options(unity PRIVATE "-Wno-switch-enum") + endif() + endif() + + #copy test files + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/inputs") + file(GLOB test_files "inputs/*") + file(COPY ${test_files} DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/inputs/") + + set(unity_tests + parse_examples + parse_number + parse_hex4 + parse_string + parse_array + parse_object + parse_value + print_string + print_number + print_array + print_object + print_value + misc_tests + parse_with_opts + compare_tests + cjson_add + readme_examples + minify_tests + ) + + option(ENABLE_VALGRIND OFF "Enable the valgrind memory checker for the tests.") + if (ENABLE_VALGRIND) + find_program(MEMORYCHECK_COMMAND valgrind) + if ("${MEMORYCHECK_COMMAND}" MATCHES "MEMORYCHECK_COMMAND-NOTFOUND") + message(WARNING "Valgrind couldn't be found.") + unset(MEMORYCHECK_COMMAND) + else() + set(MEMORYCHECK_COMMAND_OPTIONS --trace-children=yes --leak-check=full --error-exitcode=1 --suppressions=${CMAKE_CURRENT_SOURCE_DIR}/../valgrind.supp) + endif() + endif() + + foreach(unity_test ${unity_tests}) + add_executable("${unity_test}" "${unity_test}.c") + if("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") + target_sources(${unity_test} PRIVATE unity_setup.c) + endif() + target_link_libraries("${unity_test}" "${CJSON_LIB}" unity) + if(MEMORYCHECK_COMMAND) + add_test(NAME "${unity_test}" + COMMAND "${MEMORYCHECK_COMMAND}" ${MEMORYCHECK_COMMAND_OPTIONS} "${CMAKE_CURRENT_BINARY_DIR}/${unity_test}") + else() + add_test(NAME "${unity_test}" + COMMAND "./${unity_test}") + endif() + endforeach() + + add_dependencies(check ${unity_tests}) + + if (ENABLE_CJSON_UTILS) + #copy test files + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/json-patch-tests") + file(GLOB test_files "json-patch-tests/*") + file(COPY ${test_files} DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/json-patch-tests/") + + set (cjson_utils_tests + json_patch_tests + old_utils_tests + misc_utils_tests) + + foreach (cjson_utils_test ${cjson_utils_tests}) + add_executable("${cjson_utils_test}" "${cjson_utils_test}.c") + target_link_libraries("${cjson_utils_test}" "${CJSON_LIB}" "${CJSON_UTILS_LIB}" unity) + if("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") + target_sources(${cjson_utils_test} PRIVATE unity_setup.c) + endif() + if(MEMORYCHECK_COMMAND) + add_test(NAME "${cjson_utils_test}" + COMMAND "${MEMORYCHECK_COMMAND}" ${MEMORYCHECK_COMMAND_OPTIONS} "${CMAKE_CURRENT_BINARY_DIR}/${cjson_utils_test}") + else() + add_test(NAME "${cjson_utils_test}" + COMMAND "./${cjson_utils_test}") + endif() + endforeach() + + add_dependencies(check ${cjson_utils_tests}) + endif() +endif() diff --git a/components/spotify/cspot/bell/cJSON/tests/cjson_add.c b/components/spotify/cspot/bell/cJSON/tests/cjson_add.c new file mode 100644 index 00000000..b02f1e27 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/cjson_add.c @@ -0,0 +1,471 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static void * CJSON_CDECL failing_malloc(size_t size) +{ + (void)size; + return NULL; +} + +/* work around MSVC error C2322: '...' address of dillimport '...' is not static */ +static void CJSON_CDECL normal_free(void *pointer) +{ + free(pointer); +} + +static cJSON_Hooks failing_hooks = { + failing_malloc, + normal_free +}; + +static void cjson_add_null_should_add_null(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *null = NULL; + + cJSON_AddNullToObject(root, "null"); + + TEST_ASSERT_NOT_NULL(null = cJSON_GetObjectItemCaseSensitive(root, "null")); + TEST_ASSERT_EQUAL_INT(null->type, cJSON_NULL); + + cJSON_Delete(root); +} + +static void cjson_add_null_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddNullToObject(NULL, "null")); + TEST_ASSERT_NULL(cJSON_AddNullToObject(root, NULL)); + + cJSON_Delete(root); +} + +static void cjson_add_null_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddNullToObject(root, "null")); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} + +static void cjson_add_true_should_add_true(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *true_item = NULL; + + cJSON_AddTrueToObject(root, "true"); + + TEST_ASSERT_NOT_NULL(true_item = cJSON_GetObjectItemCaseSensitive(root, "true")); + TEST_ASSERT_EQUAL_INT(true_item->type, cJSON_True); + + cJSON_Delete(root); +} + +static void cjson_add_true_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddTrueToObject(NULL, "true")); + TEST_ASSERT_NULL(cJSON_AddTrueToObject(root, NULL)); + + cJSON_Delete(root); +} + +static void cjson_add_true_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddTrueToObject(root, "true")); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} + +static void cjson_create_int_array_should_fail_on_allocation_failure(void) +{ + int numbers[] = {1, 2, 3}; + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_CreateIntArray(numbers, 3)); + + cJSON_InitHooks(NULL); +} + +static void cjson_create_float_array_should_fail_on_allocation_failure(void) +{ + float numbers[] = {1.0f, 2.0f, 3.0f}; + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_CreateFloatArray(numbers, 3)); + + cJSON_InitHooks(NULL); +} + +static void cjson_create_double_array_should_fail_on_allocation_failure(void) +{ + double numbers[] = {1.0, 2.0, 3.0}; + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_CreateDoubleArray(numbers, 3)); + + cJSON_InitHooks(NULL); +} + +static void cjson_create_string_array_should_fail_on_allocation_failure(void) +{ + const char* strings[] = {"1", "2", "3"}; + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_CreateStringArray(strings, 3)); + + cJSON_InitHooks(NULL); +} + +static void cjson_add_false_should_add_false(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *false_item = NULL; + + cJSON_AddFalseToObject(root, "false"); + + TEST_ASSERT_NOT_NULL(false_item = cJSON_GetObjectItemCaseSensitive(root, "false")); + TEST_ASSERT_EQUAL_INT(false_item->type, cJSON_False); + + cJSON_Delete(root); +} + +static void cjson_add_false_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddFalseToObject(NULL, "false")); + TEST_ASSERT_NULL(cJSON_AddFalseToObject(root, NULL)); + + cJSON_Delete(root); +} + +static void cjson_add_false_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddFalseToObject(root, "false")); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} + +static void cjson_add_bool_should_add_bool(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *true_item = NULL; + cJSON *false_item = NULL; + + /* true */ + cJSON_AddBoolToObject(root, "true", true); + TEST_ASSERT_NOT_NULL(true_item = cJSON_GetObjectItemCaseSensitive(root, "true")); + TEST_ASSERT_EQUAL_INT(true_item->type, cJSON_True); + + /* false */ + cJSON_AddBoolToObject(root, "false", false); + TEST_ASSERT_NOT_NULL(false_item = cJSON_GetObjectItemCaseSensitive(root, "false")); + TEST_ASSERT_EQUAL_INT(false_item->type, cJSON_False); + + cJSON_Delete(root); +} + +static void cjson_add_bool_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddBoolToObject(NULL, "false", false)); + TEST_ASSERT_NULL(cJSON_AddBoolToObject(root, NULL, false)); + + cJSON_Delete(root); +} + +static void cjson_add_bool_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddBoolToObject(root, "false", false)); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} + +static void cjson_add_number_should_add_number(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *number = NULL; + + cJSON_AddNumberToObject(root, "number", 42); + + TEST_ASSERT_NOT_NULL(number = cJSON_GetObjectItemCaseSensitive(root, "number")); + + TEST_ASSERT_EQUAL_INT(number->type, cJSON_Number); + TEST_ASSERT_EQUAL_DOUBLE(number->valuedouble, 42); + TEST_ASSERT_EQUAL_INT(number->valueint, 42); + + cJSON_Delete(root); +} + +static void cjson_add_number_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddNumberToObject(NULL, "number", 42)); + TEST_ASSERT_NULL(cJSON_AddNumberToObject(root, NULL, 42)); + + cJSON_Delete(root); +} + +static void cjson_add_number_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddNumberToObject(root, "number", 42)); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} + +static void cjson_add_string_should_add_string(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *string = NULL; + + cJSON_AddStringToObject(root, "string", "Hello World!"); + + TEST_ASSERT_NOT_NULL(string = cJSON_GetObjectItemCaseSensitive(root, "string")); + TEST_ASSERT_EQUAL_INT(string->type, cJSON_String); + TEST_ASSERT_EQUAL_STRING(string->valuestring, "Hello World!"); + + cJSON_Delete(root); +} + +static void cjson_add_string_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddStringToObject(NULL, "string", "string")); + TEST_ASSERT_NULL(cJSON_AddStringToObject(root, NULL, "string")); + + cJSON_Delete(root); +} + +static void cjson_add_string_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddStringToObject(root, "string", "string")); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} + +static void cjson_add_raw_should_add_raw(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *raw = NULL; + + cJSON_AddRawToObject(root, "raw", "{}"); + + TEST_ASSERT_NOT_NULL(raw = cJSON_GetObjectItemCaseSensitive(root, "raw")); + TEST_ASSERT_EQUAL_INT(raw->type, cJSON_Raw); + TEST_ASSERT_EQUAL_STRING(raw->valuestring, "{}"); + + cJSON_Delete(root); +} + +static void cjson_add_raw_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddRawToObject(NULL, "raw", "{}")); + TEST_ASSERT_NULL(cJSON_AddRawToObject(root, NULL, "{}")); + + cJSON_Delete(root); +} + +static void cjson_add_raw_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddRawToObject(root, "raw", "{}")); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} + +static void cJSON_add_object_should_add_object(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *object = NULL; + + cJSON_AddObjectToObject(root, "object"); + TEST_ASSERT_NOT_NULL(object = cJSON_GetObjectItemCaseSensitive(root, "object")); + TEST_ASSERT_EQUAL_INT(object->type, cJSON_Object); + + cJSON_Delete(root); +} + +static void cjson_add_object_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddObjectToObject(NULL, "object")); + TEST_ASSERT_NULL(cJSON_AddObjectToObject(root, NULL)); + + cJSON_Delete(root); +} + +static void cjson_add_object_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddObjectToObject(root, "object")); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} + +static void cJSON_add_array_should_add_array(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *array = NULL; + + cJSON_AddArrayToObject(root, "array"); + TEST_ASSERT_NOT_NULL(array = cJSON_GetObjectItemCaseSensitive(root, "array")); + TEST_ASSERT_EQUAL_INT(array->type, cJSON_Array); + + cJSON_Delete(root); +} + +static void cjson_add_array_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddArrayToObject(NULL, "array")); + TEST_ASSERT_NULL(cJSON_AddArrayToObject(root, NULL)); + + cJSON_Delete(root); +} + +static void cjson_add_array_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddArrayToObject(root, "array")); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} + +int CJSON_CDECL main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(cjson_add_null_should_add_null); + RUN_TEST(cjson_add_null_should_fail_with_null_pointers); + RUN_TEST(cjson_add_null_should_fail_on_allocation_failure); + + RUN_TEST(cjson_add_true_should_add_true); + RUN_TEST(cjson_add_true_should_fail_with_null_pointers); + RUN_TEST(cjson_add_true_should_fail_on_allocation_failure); + + RUN_TEST(cjson_create_int_array_should_fail_on_allocation_failure); + RUN_TEST(cjson_create_float_array_should_fail_on_allocation_failure); + RUN_TEST(cjson_create_double_array_should_fail_on_allocation_failure); + RUN_TEST(cjson_create_string_array_should_fail_on_allocation_failure); + + RUN_TEST(cjson_add_false_should_add_false); + RUN_TEST(cjson_add_false_should_fail_with_null_pointers); + RUN_TEST(cjson_add_false_should_fail_on_allocation_failure); + + RUN_TEST(cjson_add_bool_should_add_bool); + RUN_TEST(cjson_add_bool_should_fail_with_null_pointers); + RUN_TEST(cjson_add_bool_should_fail_on_allocation_failure); + + RUN_TEST(cjson_add_number_should_add_number); + RUN_TEST(cjson_add_number_should_fail_with_null_pointers); + RUN_TEST(cjson_add_number_should_fail_on_allocation_failure); + + RUN_TEST(cjson_add_string_should_add_string); + RUN_TEST(cjson_add_string_should_fail_with_null_pointers); + RUN_TEST(cjson_add_string_should_fail_on_allocation_failure); + + RUN_TEST(cjson_add_raw_should_add_raw); + RUN_TEST(cjson_add_raw_should_fail_with_null_pointers); + RUN_TEST(cjson_add_raw_should_fail_on_allocation_failure); + + RUN_TEST(cJSON_add_object_should_add_object); + RUN_TEST(cjson_add_object_should_fail_with_null_pointers); + RUN_TEST(cjson_add_object_should_fail_on_allocation_failure); + + RUN_TEST(cJSON_add_array_should_add_array); + RUN_TEST(cjson_add_array_should_fail_with_null_pointers); + RUN_TEST(cjson_add_array_should_fail_on_allocation_failure); + + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/common.h b/components/spotify/cspot/bell/cJSON/tests/common.h new file mode 100644 index 00000000..4db6bf8c --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/common.h @@ -0,0 +1,122 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef CJSON_TESTS_COMMON_H +#define CJSON_TESTS_COMMON_H + +#include "../cJSON.c" + +void reset(cJSON *item); +void reset(cJSON *item) { + if ((item != NULL) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if ((item->valuestring != NULL) && !(item->type & cJSON_IsReference)) + { + global_hooks.deallocate(item->valuestring); + } + if ((item->string != NULL) && !(item->type & cJSON_StringIsConst)) + { + global_hooks.deallocate(item->string); + } + + memset(item, 0, sizeof(cJSON)); +} + +char* read_file(const char *filename); +char* read_file(const char *filename) { + FILE *file = NULL; + long length = 0; + char *content = NULL; + size_t read_chars = 0; + + /* open in read binary mode */ + file = fopen(filename, "rb"); + if (file == NULL) + { + goto cleanup; + } + + /* get the length */ + if (fseek(file, 0, SEEK_END) != 0) + { + goto cleanup; + } + length = ftell(file); + if (length < 0) + { + goto cleanup; + } + if (fseek(file, 0, SEEK_SET) != 0) + { + goto cleanup; + } + + /* allocate content buffer */ + content = (char*)malloc((size_t)length + sizeof("")); + if (content == NULL) + { + goto cleanup; + } + + /* read the file into memory */ + read_chars = fread(content, sizeof(char), (size_t)length, file); + if ((long)read_chars != length) + { + free(content); + content = NULL; + goto cleanup; + } + content[read_chars] = '\0'; + + +cleanup: + if (file != NULL) + { + fclose(file); + } + + return content; +} + +/* assertion helper macros */ +#define assert_has_type(item, item_type) TEST_ASSERT_BITS_MESSAGE(0xFF, item_type, item->type, "Item doesn't have expected type.") +#define assert_has_no_reference(item) TEST_ASSERT_BITS_MESSAGE(cJSON_IsReference, 0, item->type, "Item should not have a string as reference.") +#define assert_has_no_const_string(item) TEST_ASSERT_BITS_MESSAGE(cJSON_StringIsConst, 0, item->type, "Item should not have a const string.") +#define assert_has_valuestring(item) TEST_ASSERT_NOT_NULL_MESSAGE(item->valuestring, "Valuestring is NULL.") +#define assert_has_no_valuestring(item) TEST_ASSERT_NULL_MESSAGE(item->valuestring, "Valuestring is not NULL.") +#define assert_has_string(item) TEST_ASSERT_NOT_NULL_MESSAGE(item->string, "String is NULL") +#define assert_has_no_string(item) TEST_ASSERT_NULL_MESSAGE(item->string, "String is not NULL.") +#define assert_not_in_list(item) \ + TEST_ASSERT_NULL_MESSAGE(item->next, "Linked list next pointer is not NULL.");\ + TEST_ASSERT_NULL_MESSAGE(item->prev, "Linked list previous pointer is not NULL.") +#define assert_has_child(item) TEST_ASSERT_NOT_NULL_MESSAGE(item->child, "Item doesn't have a child.") +#define assert_has_no_child(item) TEST_ASSERT_NULL_MESSAGE(item->child, "Item has a child.") +#define assert_is_invalid(item) \ + assert_has_type(item, cJSON_Invalid);\ + assert_not_in_list(item);\ + assert_has_no_child(item);\ + assert_has_no_string(item);\ + assert_has_no_valuestring(item) + +#endif diff --git a/components/spotify/cspot/bell/cJSON/tests/compare_tests.c b/components/spotify/cspot/bell/cJSON/tests/compare_tests.c new file mode 100644 index 00000000..797c7740 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/compare_tests.c @@ -0,0 +1,208 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static cJSON_bool compare_from_string(const char * const a, const char * const b, const cJSON_bool case_sensitive) +{ + cJSON *a_json = NULL; + cJSON *b_json = NULL; + cJSON_bool result = false; + + a_json = cJSON_Parse(a); + TEST_ASSERT_NOT_NULL_MESSAGE(a_json, "Failed to parse a."); + b_json = cJSON_Parse(b); + TEST_ASSERT_NOT_NULL_MESSAGE(b_json, "Failed to parse b."); + + result = cJSON_Compare(a_json, b_json, case_sensitive); + + cJSON_Delete(a_json); + cJSON_Delete(b_json); + + return result; +} + +static void cjson_compare_should_compare_null_pointer_as_not_equal(void) +{ + TEST_ASSERT_FALSE(cJSON_Compare(NULL, NULL, true)); + TEST_ASSERT_FALSE(cJSON_Compare(NULL, NULL, false)); +} + +static void cjson_compare_should_compare_invalid_as_not_equal(void) +{ + cJSON invalid[1]; + memset(invalid, '\0', sizeof(invalid)); + + TEST_ASSERT_FALSE(cJSON_Compare(invalid, invalid, false)); + TEST_ASSERT_FALSE(cJSON_Compare(invalid, invalid, true)); +} + +static void cjson_compare_should_compare_numbers(void) +{ + TEST_ASSERT_TRUE(compare_from_string("1", "1", true)); + TEST_ASSERT_TRUE(compare_from_string("1", "1", false)); + TEST_ASSERT_TRUE(compare_from_string("0.0001", "0.0001", true)); + TEST_ASSERT_TRUE(compare_from_string("0.0001", "0.0001", false)); + TEST_ASSERT_TRUE(compare_from_string("1E100", "10E99", false)); + + TEST_ASSERT_FALSE(compare_from_string("0.5E-100", "0.5E-101", false)); + + TEST_ASSERT_FALSE(compare_from_string("1", "2", true)); + TEST_ASSERT_FALSE(compare_from_string("1", "2", false)); +} + +static void cjson_compare_should_compare_booleans(void) +{ + /* true */ + TEST_ASSERT_TRUE(compare_from_string("true", "true", true)); + TEST_ASSERT_TRUE(compare_from_string("true", "true", false)); + + /* false */ + TEST_ASSERT_TRUE(compare_from_string("false", "false", true)); + TEST_ASSERT_TRUE(compare_from_string("false", "false", false)); + + /* mixed */ + TEST_ASSERT_FALSE(compare_from_string("true", "false", true)); + TEST_ASSERT_FALSE(compare_from_string("true", "false", false)); + TEST_ASSERT_FALSE(compare_from_string("false", "true", true)); + TEST_ASSERT_FALSE(compare_from_string("false", "true", false)); +} + +static void cjson_compare_should_compare_null(void) +{ + TEST_ASSERT_TRUE(compare_from_string("null", "null", true)); + TEST_ASSERT_TRUE(compare_from_string("null", "null", false)); + + TEST_ASSERT_FALSE(compare_from_string("null", "true", true)); + TEST_ASSERT_FALSE(compare_from_string("null", "true", false)); +} + +static void cjson_compare_should_not_accept_invalid_types(void) +{ + cJSON invalid[1]; + memset(invalid, '\0', sizeof(invalid)); + + invalid->type = cJSON_Number | cJSON_String; + + TEST_ASSERT_FALSE(cJSON_Compare(invalid, invalid, true)); + TEST_ASSERT_FALSE(cJSON_Compare(invalid, invalid, false)); +} + +static void cjson_compare_should_compare_strings(void) +{ + TEST_ASSERT_TRUE(compare_from_string("\"abcdefg\"", "\"abcdefg\"", true)); + TEST_ASSERT_TRUE(compare_from_string("\"abcdefg\"", "\"abcdefg\"", false)); + + TEST_ASSERT_FALSE(compare_from_string("\"ABCDEFG\"", "\"abcdefg\"", true)); + TEST_ASSERT_FALSE(compare_from_string("\"ABCDEFG\"", "\"abcdefg\"", false)); +} + +static void cjson_compare_should_compare_raw(void) +{ + cJSON *raw1 = NULL; + cJSON *raw2 = NULL; + + raw1 = cJSON_Parse("\"[true, false]\""); + TEST_ASSERT_NOT_NULL(raw1); + raw2 = cJSON_Parse("\"[true, false]\""); + TEST_ASSERT_NOT_NULL(raw2); + + raw1->type = cJSON_Raw; + raw2->type = cJSON_Raw; + + TEST_ASSERT_TRUE(cJSON_Compare(raw1, raw2, true)); + TEST_ASSERT_TRUE(cJSON_Compare(raw1, raw2, false)); + + cJSON_Delete(raw1); + cJSON_Delete(raw2); +} + +static void cjson_compare_should_compare_arrays(void) +{ + TEST_ASSERT_TRUE(compare_from_string("[]", "[]", true)); + TEST_ASSERT_TRUE(compare_from_string("[]", "[]", false)); + + TEST_ASSERT_TRUE(compare_from_string("[false,true,null,42,\"string\",[],{}]", "[false, true, null, 42, \"string\", [], {}]", true)); + TEST_ASSERT_TRUE(compare_from_string("[false,true,null,42,\"string\",[],{}]", "[false, true, null, 42, \"string\", [], {}]", false)); + + TEST_ASSERT_TRUE(compare_from_string("[[[1], 2]]", "[[[1], 2]]", true)); + TEST_ASSERT_TRUE(compare_from_string("[[[1], 2]]", "[[[1], 2]]", false)); + + TEST_ASSERT_FALSE(compare_from_string("[true,null,42,\"string\",[],{}]", "[false, true, null, 42, \"string\", [], {}]", true)); + TEST_ASSERT_FALSE(compare_from_string("[true,null,42,\"string\",[],{}]", "[false, true, null, 42, \"string\", [], {}]", false)); + + /* Arrays that are a prefix of another array */ + TEST_ASSERT_FALSE(compare_from_string("[1,2,3]", "[1,2]", true)); + TEST_ASSERT_FALSE(compare_from_string("[1,2,3]", "[1,2]", false)); +} + +static void cjson_compare_should_compare_objects(void) +{ + TEST_ASSERT_TRUE(compare_from_string("{}", "{}", true)); + TEST_ASSERT_TRUE(compare_from_string("{}", "{}", false)); + + TEST_ASSERT_TRUE(compare_from_string( + "{\"false\": false, \"true\": true, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}", + "{\"true\": true, \"false\": false, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}", + true)); + TEST_ASSERT_FALSE(compare_from_string( + "{\"False\": false, \"true\": true, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}", + "{\"true\": true, \"false\": false, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}", + true)); + TEST_ASSERT_TRUE(compare_from_string( + "{\"False\": false, \"true\": true, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}", + "{\"true\": true, \"false\": false, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}", + false)); + TEST_ASSERT_FALSE(compare_from_string( + "{\"Flse\": false, \"true\": true, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}", + "{\"true\": true, \"false\": false, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}", + false)); + /* test objects that are a subset of each other */ + TEST_ASSERT_FALSE(compare_from_string( + "{\"one\": 1, \"two\": 2}", + "{\"one\": 1, \"two\": 2, \"three\": 3}", + true)) + TEST_ASSERT_FALSE(compare_from_string( + "{\"one\": 1, \"two\": 2}", + "{\"one\": 1, \"two\": 2, \"three\": 3}", + false)) +} + +int CJSON_CDECL main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(cjson_compare_should_compare_null_pointer_as_not_equal); + RUN_TEST(cjson_compare_should_compare_invalid_as_not_equal); + RUN_TEST(cjson_compare_should_compare_numbers); + RUN_TEST(cjson_compare_should_compare_booleans); + RUN_TEST(cjson_compare_should_compare_null); + RUN_TEST(cjson_compare_should_not_accept_invalid_types); + RUN_TEST(cjson_compare_should_compare_strings); + RUN_TEST(cjson_compare_should_compare_raw); + RUN_TEST(cjson_compare_should_compare_arrays); + RUN_TEST(cjson_compare_should_compare_objects); + + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test1 b/components/spotify/cspot/bell/cJSON/tests/inputs/test1 new file mode 100644 index 00000000..eacfbf5e --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test1 @@ -0,0 +1,22 @@ +{ + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Markup Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": ["GML", "XML"] + }, + "GlossSee": "markup" + } + } + } + } +} diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test1.expected b/components/spotify/cspot/bell/cJSON/tests/inputs/test1.expected new file mode 100644 index 00000000..a5839e84 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test1.expected @@ -0,0 +1,22 @@ +{ + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Markup Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": ["GML", "XML"] + }, + "GlossSee": "markup" + } + } + } + } +} \ No newline at end of file diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test10 b/components/spotify/cspot/bell/cJSON/tests/inputs/test10 new file mode 100644 index 00000000..d19eb8b5 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test10 @@ -0,0 +1 @@ +["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test10.expected b/components/spotify/cspot/bell/cJSON/tests/inputs/test10.expected new file mode 100644 index 00000000..747ba62c --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test10.expected @@ -0,0 +1 @@ +["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] \ No newline at end of file diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test11 b/components/spotify/cspot/bell/cJSON/tests/inputs/test11 new file mode 100644 index 00000000..039c61b9 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test11 @@ -0,0 +1,8 @@ +{ +"name": "Jack (\"Bee\") Nimble", +"format": {"type": "rect", +"width": 1920, +"height": 1080, +"interlace": false,"frame rate": 24 +} +} diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test11.expected b/components/spotify/cspot/bell/cJSON/tests/inputs/test11.expected new file mode 100644 index 00000000..24e72ece --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test11.expected @@ -0,0 +1,10 @@ +{ + "name": "Jack (\"Bee\") Nimble", + "format": { + "type": "rect", + "width": 1920, + "height": 1080, + "interlace": false, + "frame rate": 24 + } +} \ No newline at end of file diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test2 b/components/spotify/cspot/bell/cJSON/tests/inputs/test2 new file mode 100644 index 00000000..5600991a --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test2 @@ -0,0 +1,11 @@ +{"menu": { + "id": "file", + "value": "File", + "popup": { + "menuitem": [ + {"value": "New", "onclick": "CreateNewDoc()"}, + {"value": "Open", "onclick": "OpenDoc()"}, + {"value": "Close", "onclick": "CloseDoc()"} + ] + } +}} diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test2.expected b/components/spotify/cspot/bell/cJSON/tests/inputs/test2.expected new file mode 100644 index 00000000..d42a0170 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test2.expected @@ -0,0 +1,18 @@ +{ + "menu": { + "id": "file", + "value": "File", + "popup": { + "menuitem": [{ + "value": "New", + "onclick": "CreateNewDoc()" + }, { + "value": "Open", + "onclick": "OpenDoc()" + }, { + "value": "Close", + "onclick": "CloseDoc()" + }] + } + } +} \ No newline at end of file diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test3 b/components/spotify/cspot/bell/cJSON/tests/inputs/test3 new file mode 100644 index 00000000..7f74acaa --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test3 @@ -0,0 +1,26 @@ +{"widget": { + "debug": "on", + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "name": "sun1", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "name": "text1", + "hOffset": 250, + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } +}} \ No newline at end of file diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test3.expected b/components/spotify/cspot/bell/cJSON/tests/inputs/test3.expected new file mode 100644 index 00000000..a9b9c55a --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test3.expected @@ -0,0 +1,28 @@ +{ + "widget": { + "debug": "on", + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "name": "sun1", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "name": "text1", + "hOffset": 250, + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } + } +} \ No newline at end of file diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test4 b/components/spotify/cspot/bell/cJSON/tests/inputs/test4 new file mode 100644 index 00000000..55cd56b1 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test4 @@ -0,0 +1,88 @@ +{"web-app": { + "servlet": [ + { + "servlet-name": "cofaxCDS", + "servlet-class": "org.cofax.cds.CDSServlet", + "init-param": { + "configGlossary:installationAt": "Philadelphia, PA", + "configGlossary:adminEmail": "ksm@pobox.com", + "configGlossary:poweredBy": "Cofax", + "configGlossary:poweredByIcon": "/images/cofax.gif", + "configGlossary:staticPath": "/content/static", + "templateProcessorClass": "org.cofax.WysiwygTemplate", + "templateLoaderClass": "org.cofax.FilesTemplateLoader", + "templatePath": "templates", + "templateOverridePath": "", + "defaultListTemplate": "listTemplate.htm", + "defaultFileTemplate": "articleTemplate.htm", + "useJSP": false, + "jspListTemplate": "listTemplate.jsp", + "jspFileTemplate": "articleTemplate.jsp", + "cachePackageTagsTrack": 200, + "cachePackageTagsStore": 200, + "cachePackageTagsRefresh": 60, + "cacheTemplatesTrack": 100, + "cacheTemplatesStore": 50, + "cacheTemplatesRefresh": 15, + "cachePagesTrack": 200, + "cachePagesStore": 100, + "cachePagesRefresh": 10, + "cachePagesDirtyRead": 10, + "searchEngineListTemplate": "forSearchEnginesList.htm", + "searchEngineFileTemplate": "forSearchEngines.htm", + "searchEngineRobotsDb": "WEB-INF/robots.db", + "useDataStore": true, + "dataStoreClass": "org.cofax.SqlDataStore", + "redirectionClass": "org.cofax.SqlRedirection", + "dataStoreName": "cofax", + "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", + "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", + "dataStoreUser": "sa", + "dataStorePassword": "dataStoreTestQuery", + "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", + "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", + "dataStoreInitConns": 10, + "dataStoreMaxConns": 100, + "dataStoreConnUsageLimit": 100, + "dataStoreLogLevel": "debug", + "maxUrlLength": 500}}, + { + "servlet-name": "cofaxEmail", + "servlet-class": "org.cofax.cds.EmailServlet", + "init-param": { + "mailHost": "mail1", + "mailHostOverride": "mail2"}}, + { + "servlet-name": "cofaxAdmin", + "servlet-class": "org.cofax.cds.AdminServlet"}, + + { + "servlet-name": "fileServlet", + "servlet-class": "org.cofax.cds.FileServlet"}, + { + "servlet-name": "cofaxTools", + "servlet-class": "org.cofax.cms.CofaxToolsServlet", + "init-param": { + "templatePath": "toolstemplates/", + "log": 1, + "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", + "logMaxSize": "", + "dataLog": 1, + "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", + "dataLogMaxSize": "", + "removePageCache": "/content/admin/remove?cache=pages&id=", + "removeTemplateCache": "/content/admin/remove?cache=templates&id=", + "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", + "lookInContext": 1, + "adminGroupID": 4, + "betaServer": true}}], + "servlet-mapping": { + "cofaxCDS": "/", + "cofaxEmail": "/cofaxutil/aemail/*", + "cofaxAdmin": "/admin/*", + "fileServlet": "/static/*", + "cofaxTools": "/tools/*"}, + + "taglib": { + "taglib-uri": "cofax.tld", + "taglib-location": "/WEB-INF/tlds/cofax.tld"}}} \ No newline at end of file diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test4.expected b/components/spotify/cspot/bell/cJSON/tests/inputs/test4.expected new file mode 100644 index 00000000..3648a95b --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test4.expected @@ -0,0 +1,94 @@ +{ + "web-app": { + "servlet": [{ + "servlet-name": "cofaxCDS", + "servlet-class": "org.cofax.cds.CDSServlet", + "init-param": { + "configGlossary:installationAt": "Philadelphia, PA", + "configGlossary:adminEmail": "ksm@pobox.com", + "configGlossary:poweredBy": "Cofax", + "configGlossary:poweredByIcon": "/images/cofax.gif", + "configGlossary:staticPath": "/content/static", + "templateProcessorClass": "org.cofax.WysiwygTemplate", + "templateLoaderClass": "org.cofax.FilesTemplateLoader", + "templatePath": "templates", + "templateOverridePath": "", + "defaultListTemplate": "listTemplate.htm", + "defaultFileTemplate": "articleTemplate.htm", + "useJSP": false, + "jspListTemplate": "listTemplate.jsp", + "jspFileTemplate": "articleTemplate.jsp", + "cachePackageTagsTrack": 200, + "cachePackageTagsStore": 200, + "cachePackageTagsRefresh": 60, + "cacheTemplatesTrack": 100, + "cacheTemplatesStore": 50, + "cacheTemplatesRefresh": 15, + "cachePagesTrack": 200, + "cachePagesStore": 100, + "cachePagesRefresh": 10, + "cachePagesDirtyRead": 10, + "searchEngineListTemplate": "forSearchEnginesList.htm", + "searchEngineFileTemplate": "forSearchEngines.htm", + "searchEngineRobotsDb": "WEB-INF/robots.db", + "useDataStore": true, + "dataStoreClass": "org.cofax.SqlDataStore", + "redirectionClass": "org.cofax.SqlRedirection", + "dataStoreName": "cofax", + "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", + "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", + "dataStoreUser": "sa", + "dataStorePassword": "dataStoreTestQuery", + "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", + "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", + "dataStoreInitConns": 10, + "dataStoreMaxConns": 100, + "dataStoreConnUsageLimit": 100, + "dataStoreLogLevel": "debug", + "maxUrlLength": 500 + } + }, { + "servlet-name": "cofaxEmail", + "servlet-class": "org.cofax.cds.EmailServlet", + "init-param": { + "mailHost": "mail1", + "mailHostOverride": "mail2" + } + }, { + "servlet-name": "cofaxAdmin", + "servlet-class": "org.cofax.cds.AdminServlet" + }, { + "servlet-name": "fileServlet", + "servlet-class": "org.cofax.cds.FileServlet" + }, { + "servlet-name": "cofaxTools", + "servlet-class": "org.cofax.cms.CofaxToolsServlet", + "init-param": { + "templatePath": "toolstemplates/", + "log": 1, + "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", + "logMaxSize": "", + "dataLog": 1, + "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", + "dataLogMaxSize": "", + "removePageCache": "/content/admin/remove?cache=pages&id=", + "removeTemplateCache": "/content/admin/remove?cache=templates&id=", + "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", + "lookInContext": 1, + "adminGroupID": 4, + "betaServer": true + } + }], + "servlet-mapping": { + "cofaxCDS": "/", + "cofaxEmail": "/cofaxutil/aemail/*", + "cofaxAdmin": "/admin/*", + "fileServlet": "/static/*", + "cofaxTools": "/tools/*" + }, + "taglib": { + "taglib-uri": "cofax.tld", + "taglib-location": "/WEB-INF/tlds/cofax.tld" + } + } +} \ No newline at end of file diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test5 b/components/spotify/cspot/bell/cJSON/tests/inputs/test5 new file mode 100644 index 00000000..49980ca2 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test5 @@ -0,0 +1,27 @@ +{"menu": { + "header": "SVG Viewer", + "items": [ + {"id": "Open"}, + {"id": "OpenNew", "label": "Open New"}, + null, + {"id": "ZoomIn", "label": "Zoom In"}, + {"id": "ZoomOut", "label": "Zoom Out"}, + {"id": "OriginalView", "label": "Original View"}, + null, + {"id": "Quality"}, + {"id": "Pause"}, + {"id": "Mute"}, + null, + {"id": "Find", "label": "Find..."}, + {"id": "FindAgain", "label": "Find Again"}, + {"id": "Copy"}, + {"id": "CopyAgain", "label": "Copy Again"}, + {"id": "CopySVG", "label": "Copy SVG"}, + {"id": "ViewSVG", "label": "View SVG"}, + {"id": "ViewSource", "label": "View Source"}, + {"id": "SaveAs", "label": "Save As"}, + null, + {"id": "Help"}, + {"id": "About", "label": "About Adobe CVG Viewer..."} + ] +}} diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test5.expected b/components/spotify/cspot/bell/cJSON/tests/inputs/test5.expected new file mode 100644 index 00000000..886ed58f --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test5.expected @@ -0,0 +1,54 @@ +{ + "menu": { + "header": "SVG Viewer", + "items": [{ + "id": "Open" + }, { + "id": "OpenNew", + "label": "Open New" + }, null, { + "id": "ZoomIn", + "label": "Zoom In" + }, { + "id": "ZoomOut", + "label": "Zoom Out" + }, { + "id": "OriginalView", + "label": "Original View" + }, null, { + "id": "Quality" + }, { + "id": "Pause" + }, { + "id": "Mute" + }, null, { + "id": "Find", + "label": "Find..." + }, { + "id": "FindAgain", + "label": "Find Again" + }, { + "id": "Copy" + }, { + "id": "CopyAgain", + "label": "Copy Again" + }, { + "id": "CopySVG", + "label": "Copy SVG" + }, { + "id": "ViewSVG", + "label": "View SVG" + }, { + "id": "ViewSource", + "label": "View Source" + }, { + "id": "SaveAs", + "label": "Save As" + }, null, { + "id": "Help" + }, { + "id": "About", + "label": "About Adobe CVG Viewer..." + }] + } +} \ No newline at end of file diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test6 b/components/spotify/cspot/bell/cJSON/tests/inputs/test6 new file mode 100644 index 00000000..d5cb28f6 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test6 @@ -0,0 +1,16 @@ + + + + + + Application Error + + + + + \ No newline at end of file diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test7 b/components/spotify/cspot/bell/cJSON/tests/inputs/test7 new file mode 100644 index 00000000..33085366 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test7 @@ -0,0 +1,22 @@ +[ + { + "precision": "zip", + "Latitude": 37.7668, + "Longitude": -122.3959, + "Address": "", + "City": "SAN FRANCISCO", + "State": "CA", + "Zip": "94107", + "Country": "US" + }, + { + "precision": "zip", + "Latitude": 37.371991, + "Longitude": -122.026020, + "Address": "", + "City": "SUNNYVALE", + "State": "CA", + "Zip": "94085", + "Country": "US" + } + ] diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test7.expected b/components/spotify/cspot/bell/cJSON/tests/inputs/test7.expected new file mode 100644 index 00000000..15adf793 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test7.expected @@ -0,0 +1,19 @@ +[{ + "precision": "zip", + "Latitude": 37.7668, + "Longitude": -122.3959, + "Address": "", + "City": "SAN FRANCISCO", + "State": "CA", + "Zip": "94107", + "Country": "US" + }, { + "precision": "zip", + "Latitude": 37.371991, + "Longitude": -122.02602, + "Address": "", + "City": "SUNNYVALE", + "State": "CA", + "Zip": "94085", + "Country": "US" + }] \ No newline at end of file diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test8 b/components/spotify/cspot/bell/cJSON/tests/inputs/test8 new file mode 100644 index 00000000..4b1f5b97 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test8 @@ -0,0 +1,13 @@ +{ + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http:/*www.example.com/image/481989943", + "Height": 125, + "Width": "100" + }, + "IDs": [116, 943, 234, 38793] + } + } diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test8.expected b/components/spotify/cspot/bell/cJSON/tests/inputs/test8.expected new file mode 100644 index 00000000..2e6b731f --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test8.expected @@ -0,0 +1,13 @@ +{ + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http:/*www.example.com/image/481989943", + "Height": 125, + "Width": "100" + }, + "IDs": [116, 943, 234, 38793] + } +} \ No newline at end of file diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test9 b/components/spotify/cspot/bell/cJSON/tests/inputs/test9 new file mode 100644 index 00000000..2a939b96 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test9 @@ -0,0 +1,5 @@ +[ + [0, -1, 0], + [1, 0, 0], + [0, 0, 1] + ] diff --git a/components/spotify/cspot/bell/cJSON/tests/inputs/test9.expected b/components/spotify/cspot/bell/cJSON/tests/inputs/test9.expected new file mode 100644 index 00000000..0eea3c94 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/inputs/test9.expected @@ -0,0 +1 @@ +[[0, -1, 0], [1, 0, 0], [0, 0, 1]] \ No newline at end of file diff --git a/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/.editorconfig b/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/.editorconfig new file mode 100644 index 00000000..dc79da48 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/.editorconfig @@ -0,0 +1,10 @@ +# EditorConfig is awesome: http://EditorConfig.org + +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true +indent_style = space diff --git a/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/.gitignore b/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/.gitignore new file mode 100644 index 00000000..cd2b23de --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/.gitignore @@ -0,0 +1,4 @@ +*~ +\#* +!.editorconfig +!.gitignore diff --git a/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/.npmignore b/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/.npmignore new file mode 100644 index 00000000..2d6cdcb2 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/.npmignore @@ -0,0 +1,2 @@ +.editorconfig +.gitignore diff --git a/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/README.md b/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/README.md new file mode 100644 index 00000000..fb9e4478 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/README.md @@ -0,0 +1,75 @@ +JSON Patch Tests +================ + +These are test cases for implementations of [IETF JSON Patch (RFC6902)](http://tools.ietf.org/html/rfc6902). + +Some implementations can be found at [jsonpatch.com](http://jsonpatch.com). + + +Test Format +----------- + +Each test file is a JSON document that contains an array of test records. A +test record is an object with the following members: + +- doc: The JSON document to test against +- patch: The patch(es) to apply +- expected: The expected resulting document, OR +- error: A string describing an expected error +- comment: A string describing the test +- disabled: True if the test should be skipped + +All fields except 'doc' and 'patch' are optional. Test records consisting only +of a comment are also OK. + + +Files +----- + +- tests.json: the main test file +- spec_tests.json: tests from the RFC6902 spec + + +Writing Tests +------------- + +All tests should have a descriptive comment. Tests should be as +simple as possible - just what's required to test a specific piece of +behavior. If you want to test interacting behaviors, create tests for +each behavior as well as the interaction. + +If an 'error' member is specified, the error text should describe the +error the implementation should raise - *not* what's being tested. +Implementation error strings will vary, but the suggested error should +be easily matched to the implementation error string. Try to avoid +creating error tests that might pass because an incorrect error was +reported. + +Please feel free to contribute! + + +Credits +------- + +The seed test set was adapted from Byron Ruth's +[jsonpatch-js](https://github.com/bruth/jsonpatch-js/blob/master/test.js) and +extended by [Mike McCabe](https://github.com/mikemccabe). + + +License +------- + + Copyright 2014 The Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/cjson-utils-tests.json b/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/cjson-utils-tests.json new file mode 100644 index 00000000..28a5e307 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/cjson-utils-tests.json @@ -0,0 +1,91 @@ +[ + { + "comment": "1", + "doc": { "foo": "bar"}, + "patch": [{ "op": "add", "path": "/baz", "value": "qux" }], + "expected": {"baz": "qux", "foo": "bar"} + }, + { + "comment": "2", + "doc": { "foo": [ "bar", "baz" ] }, + "patch": [{ "op": "add", "path": "/foo/1", "value": "qux" }], + "expected": {"foo": [ "bar", "qux", "baz" ] } + }, + { + "comment": "3", + "doc": {"baz": "qux","foo": "bar"}, + "patch": [{ "op": "remove", "path": "/baz" }], + "expected": {"foo": "bar" } + }, + { + "comment": "4", + "doc": { "foo": [ "bar", "qux", "baz" ] }, + "patch": [{ "op": "remove", "path": "/foo/1" }], + "expected": {"foo": [ "bar", "baz" ] } + }, + { + "comment": "5", + "doc": { "baz": "qux","foo": "bar"}, + "patch": [{ "op": "replace", "path": "/baz", "value": "boo" }], + "expected": {"baz": "boo","foo": "bar"} + }, + { + "comment": "6", + "doc": {"foo": {"bar": "baz","waldo": "fred"},"qux": {"corge": "grault"}}, + "patch": [{ "op": "move", "from": "/foo/waldo", "path": "/qux/thud" }], + "expected": {"foo": {"bar": "baz"},"qux": {"corge": "grault","thud": "fred"}} + }, + { + "comment": "7", + "doc": { "foo": [ "all", "grass", "cows", "eat" ] }, + "patch": [ { "op": "move", "from": "/foo/1", "path": "/foo/3" }], + "expected": { "foo": [ "all", "cows", "eat", "grass" ] } + }, + { + "comment": "8", + "doc": {"baz": "qux","foo": [ "a", 2, "c" ]}, + "patch": [{ "op": "test", "path": "/baz", "value": "qux" },{ "op": "test", "path": "/foo/1", "value": 2 }] + }, + { + "comment": "9", + "doc": { "baz": "qux" }, + "patch": [ { "op": "test", "path": "/baz", "value": "bar" }], + "error": "\"bar\" doesn't exist" + }, + { + "comment": "10", + "doc": { "foo": "bar" }, + "patch": [{ "op": "add", "path": "/child", "value": { "grandchild": { } } }], + "expected": {"foo": "bar","child": {"grandchild": {}}} + }, + { + "comment": "11", + "doc": { "foo": "bar" }, + "patch": [{ "op": "add", "path": "/baz", "value": "qux", "xyz": 123 }], + "expected": {"foo": "bar","baz": "qux"} + }, + { + "comment": "12", + "doc": { "foo": "bar" }, + "patch": [{ "op": "add", "path": "/baz/bat", "value": "qux" }], + "error": "Can't add to nonexistent object" + }, + { + "comment": "13", + "doc": {"/": 9,"~1": 10}, + "patch": [{"op": "test", "path": "/~01", "value": 10}] + }, + { + "comment": "14", + "doc": { "foo": ["bar"] }, + "patch": [ { "op": "add", "path": "/foo/-", "value": ["abc", "def"] }], + "expected": {"foo": ["bar", ["abc", "def"]] } + }, + + { + "comment": "15", + "doc": {"foo": {"bar": 1}}, + "patch": [{"op": "add", "path": "/foo/bar/baz", "value": "5"}], + "error": "attempting to add to subfield of non-object" + } +] diff --git a/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/package.json b/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/package.json new file mode 100644 index 00000000..256f9368 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/package.json @@ -0,0 +1,15 @@ +{ + "name": "json-patch-test-suite", + "version": "1.1.0", + "description": "JSON Patch RFC 6902 test suite", + "repository": "github:json-patch/json-patch-tests", + "homepage": "https://github.com/json-patch/json-patch-tests", + "bugs": "https://github.com/json-patch/json-patch-tests/issues", + "keywords": [ + "JSON", + "Patch", + "test", + "suite" + ], + "license": "Apache-2.0" +} diff --git a/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/spec_tests.json b/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/spec_tests.json new file mode 100644 index 00000000..c160535b --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/spec_tests.json @@ -0,0 +1,233 @@ +[ + { + "comment": "4.1. add with missing object", + "doc": { "q": { "bar": 2 } }, + "patch": [ {"op": "add", "path": "/a/b", "value": 1} ], + "error": + "path /a does not exist -- missing objects are not created recursively" + }, + + { + "comment": "A.1. Adding an Object Member", + "doc": { + "foo": "bar" +}, + "patch": [ + { "op": "add", "path": "/baz", "value": "qux" } +], + "expected": { + "baz": "qux", + "foo": "bar" +} + }, + + { + "comment": "A.2. Adding an Array Element", + "doc": { + "foo": [ "bar", "baz" ] +}, + "patch": [ + { "op": "add", "path": "/foo/1", "value": "qux" } +], + "expected": { + "foo": [ "bar", "qux", "baz" ] +} + }, + + { + "comment": "A.3. Removing an Object Member", + "doc": { + "baz": "qux", + "foo": "bar" +}, + "patch": [ + { "op": "remove", "path": "/baz" } +], + "expected": { + "foo": "bar" +} + }, + + { + "comment": "A.4. Removing an Array Element", + "doc": { + "foo": [ "bar", "qux", "baz" ] +}, + "patch": [ + { "op": "remove", "path": "/foo/1" } +], + "expected": { + "foo": [ "bar", "baz" ] +} + }, + + { + "comment": "A.5. Replacing a Value", + "doc": { + "baz": "qux", + "foo": "bar" +}, + "patch": [ + { "op": "replace", "path": "/baz", "value": "boo" } +], + "expected": { + "baz": "boo", + "foo": "bar" +} + }, + + { + "comment": "A.6. Moving a Value", + "doc": { + "foo": { + "bar": "baz", + "waldo": "fred" + }, + "qux": { + "corge": "grault" + } +}, + "patch": [ + { "op": "move", "from": "/foo/waldo", "path": "/qux/thud" } +], + "expected": { + "foo": { + "bar": "baz" + }, + "qux": { + "corge": "grault", + "thud": "fred" + } +} + }, + + { + "comment": "A.7. Moving an Array Element", + "doc": { + "foo": [ "all", "grass", "cows", "eat" ] +}, + "patch": [ + { "op": "move", "from": "/foo/1", "path": "/foo/3" } +], + "expected": { + "foo": [ "all", "cows", "eat", "grass" ] +} + + }, + + { + "comment": "A.8. Testing a Value: Success", + "doc": { + "baz": "qux", + "foo": [ "a", 2, "c" ] +}, + "patch": [ + { "op": "test", "path": "/baz", "value": "qux" }, + { "op": "test", "path": "/foo/1", "value": 2 } +], + "expected": { + "baz": "qux", + "foo": [ "a", 2, "c" ] + } + }, + + { + "comment": "A.9. Testing a Value: Error", + "doc": { + "baz": "qux" +}, + "patch": [ + { "op": "test", "path": "/baz", "value": "bar" } +], + "error": "string not equivalent" + }, + + { + "comment": "A.10. Adding a nested Member Object", + "doc": { + "foo": "bar" +}, + "patch": [ + { "op": "add", "path": "/child", "value": { "grandchild": { } } } +], + "expected": { + "foo": "bar", + "child": { + "grandchild": { + } + } +} + }, + + { + "comment": "A.11. Ignoring Unrecognized Elements", + "doc": { + "foo":"bar" +}, + "patch": [ + { "op": "add", "path": "/baz", "value": "qux", "xyz": 123 } +], + "expected": { + "foo":"bar", + "baz":"qux" +} + }, + + { + "comment": "A.12. Adding to a Non-existent Target", + "doc": { + "foo": "bar" +}, + "patch": [ + { "op": "add", "path": "/baz/bat", "value": "qux" } +], + "error": "add to a non-existent target" + }, + + { + "comment": "A.13 Invalid JSON Patch Document", + "doc": { + "foo": "bar" + }, + "patch": [ + { "op": "add", "path": "/baz", "value": "qux", "op": "remove" } +], + "error": "operation has two 'op' members", + "disabled": true + }, + + { + "comment": "A.14. ~ Escape Ordering", + "doc": { + "/": 9, + "~1": 10 + }, + "patch": [{"op": "test", "path": "/~01", "value": 10}], + "expected": { + "/": 9, + "~1": 10 + } + }, + + { + "comment": "A.15. Comparing Strings and Numbers", + "doc": { + "/": 9, + "~1": 10 + }, + "patch": [{"op": "test", "path": "/~01", "value": "10"}], + "error": "number is not equal to string" + }, + + { + "comment": "A.16. Adding an Array Value", + "doc": { + "foo": ["bar"] + }, + "patch": [{ "op": "add", "path": "/foo/-", "value": ["abc", "def"] }], + "expected": { + "foo": ["bar", ["abc", "def"]] + } + } + +] diff --git a/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/tests.json b/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/tests.json new file mode 100644 index 00000000..25540781 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/json-patch-tests/tests.json @@ -0,0 +1,464 @@ +[ + { "comment": "empty list, empty docs", + "doc": {}, + "patch": [], + "expected": {} }, + + { "comment": "empty patch list", + "doc": {"foo": 1}, + "patch": [], + "expected": {"foo": 1} }, + + { "comment": "rearrangements OK?", + "doc": {"foo": 1, "bar": 2}, + "patch": [], + "expected": {"bar":2, "foo": 1} }, + + { "comment": "rearrangements OK? How about one level down ... array", + "doc": [{"foo": 1, "bar": 2}], + "patch": [], + "expected": [{"bar":2, "foo": 1}] }, + + { "comment": "rearrangements OK? How about one level down...", + "doc": {"foo":{"foo": 1, "bar": 2}}, + "patch": [], + "expected": {"foo":{"bar":2, "foo": 1}} }, + + { "comment": "add replaces any existing field", + "doc": {"foo": null}, + "patch": [{"op": "add", "path": "/foo", "value":1}], + "expected": {"foo": 1} }, + + { "comment": "toplevel array", + "doc": [], + "patch": [{"op": "add", "path": "/0", "value": "foo"}], + "expected": ["foo"] }, + + { "comment": "toplevel array, no change", + "doc": ["foo"], + "patch": [], + "expected": ["foo"] }, + + { "comment": "toplevel object, numeric string", + "doc": {}, + "patch": [{"op": "add", "path": "/foo", "value": "1"}], + "expected": {"foo":"1"} }, + + { "comment": "toplevel object, integer", + "doc": {}, + "patch": [{"op": "add", "path": "/foo", "value": 1}], + "expected": {"foo":1} }, + + { "comment": "Toplevel scalar values OK?", + "doc": "foo", + "patch": [{"op": "replace", "path": "", "value": "bar"}], + "expected": "bar", + "disabled": true }, + + { "comment": "replace object document with array document?", + "doc": {}, + "patch": [{"op": "add", "path": "", "value": []}], + "expected": [] }, + + { "comment": "replace array document with object document?", + "doc": [], + "patch": [{"op": "add", "path": "", "value": {}}], + "expected": {} }, + + { "comment": "append to root array document?", + "doc": [], + "patch": [{"op": "add", "path": "/-", "value": "hi"}], + "expected": ["hi"] }, + + { "comment": "Add, / target", + "doc": {}, + "patch": [ {"op": "add", "path": "/", "value":1 } ], + "expected": {"":1} }, + + { "comment": "Add, /foo/ deep target (trailing slash)", + "doc": {"foo": {}}, + "patch": [ {"op": "add", "path": "/foo/", "value":1 } ], + "expected": {"foo":{"": 1}} }, + + { "comment": "Add composite value at top level", + "doc": {"foo": 1}, + "patch": [{"op": "add", "path": "/bar", "value": [1, 2]}], + "expected": {"foo": 1, "bar": [1, 2]} }, + + { "comment": "Add into composite value", + "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, + "patch": [{"op": "add", "path": "/baz/0/foo", "value": "world"}], + "expected": {"foo": 1, "baz": [{"qux": "hello", "foo": "world"}]} }, + + { "doc": {"bar": [1, 2]}, + "patch": [{"op": "add", "path": "/bar/8", "value": "5"}], + "error": "Out of bounds (upper)" }, + + { "doc": {"bar": [1, 2]}, + "patch": [{"op": "add", "path": "/bar/-1", "value": "5"}], + "error": "Out of bounds (lower)" }, + + { "doc": {"foo": 1}, + "patch": [{"op": "add", "path": "/bar", "value": true}], + "expected": {"foo": 1, "bar": true} }, + + { "doc": {"foo": 1}, + "patch": [{"op": "add", "path": "/bar", "value": false}], + "expected": {"foo": 1, "bar": false} }, + + { "doc": {"foo": 1}, + "patch": [{"op": "add", "path": "/bar", "value": null}], + "expected": {"foo": 1, "bar": null} }, + + { "comment": "0 can be an array index or object element name", + "doc": {"foo": 1}, + "patch": [{"op": "add", "path": "/0", "value": "bar"}], + "expected": {"foo": 1, "0": "bar" } }, + + { "doc": ["foo"], + "patch": [{"op": "add", "path": "/1", "value": "bar"}], + "expected": ["foo", "bar"] }, + + { "doc": ["foo", "sil"], + "patch": [{"op": "add", "path": "/1", "value": "bar"}], + "expected": ["foo", "bar", "sil"] }, + + { "doc": ["foo", "sil"], + "patch": [{"op": "add", "path": "/0", "value": "bar"}], + "expected": ["bar", "foo", "sil"] }, + + { "comment": "push item to array via last index + 1", + "doc": ["foo", "sil"], + "patch": [{"op":"add", "path": "/2", "value": "bar"}], + "expected": ["foo", "sil", "bar"] }, + + { "comment": "add item to array at index > length should fail", + "doc": ["foo", "sil"], + "patch": [{"op":"add", "path": "/3", "value": "bar"}], + "error": "index is greater than number of items in array" }, + + { "comment": "test against implementation-specific numeric parsing", + "doc": {"1e0": "foo"}, + "patch": [{"op": "test", "path": "/1e0", "value": "foo"}], + "expected": {"1e0": "foo"} }, + + { "comment": "test with bad number should fail", + "doc": ["foo", "bar"], + "patch": [{"op": "test", "path": "/1e0", "value": "bar"}], + "error": "test op shouldn't get array element 1" }, + + { "doc": ["foo", "sil"], + "patch": [{"op": "add", "path": "/bar", "value": 42}], + "error": "Object operation on array target" }, + + { "doc": ["foo", "sil"], + "patch": [{"op": "add", "path": "/1", "value": ["bar", "baz"]}], + "expected": ["foo", ["bar", "baz"], "sil"], + "comment": "value in array add not flattened" }, + + { "doc": {"foo": 1, "bar": [1, 2, 3, 4]}, + "patch": [{"op": "remove", "path": "/bar"}], + "expected": {"foo": 1} }, + + { "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, + "patch": [{"op": "remove", "path": "/baz/0/qux"}], + "expected": {"foo": 1, "baz": [{}]} }, + + { "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, + "patch": [{"op": "replace", "path": "/foo", "value": [1, 2, 3, 4]}], + "expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "hello"}]} }, + + { "doc": {"foo": [1, 2, 3, 4], "baz": [{"qux": "hello"}]}, + "patch": [{"op": "replace", "path": "/baz/0/qux", "value": "world"}], + "expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "world"}]} }, + + { "doc": ["foo"], + "patch": [{"op": "replace", "path": "/0", "value": "bar"}], + "expected": ["bar"] }, + + { "doc": [""], + "patch": [{"op": "replace", "path": "/0", "value": 0}], + "expected": [0] }, + + { "doc": [""], + "patch": [{"op": "replace", "path": "/0", "value": true}], + "expected": [true] }, + + { "doc": [""], + "patch": [{"op": "replace", "path": "/0", "value": false}], + "expected": [false] }, + + { "doc": [""], + "patch": [{"op": "replace", "path": "/0", "value": null}], + "expected": [null] }, + + { "doc": ["foo", "sil"], + "patch": [{"op": "replace", "path": "/1", "value": ["bar", "baz"]}], + "expected": ["foo", ["bar", "baz"]], + "comment": "value in array replace not flattened" }, + + { "comment": "replace whole document", + "doc": {"foo": "bar"}, + "patch": [{"op": "replace", "path": "", "value": {"baz": "qux"}}], + "expected": {"baz": "qux"} }, + + { "comment": "test replace with missing parent key should fail", + "doc": {"bar": "baz"}, + "patch": [{"op": "replace", "path": "/foo/bar", "value": false}], + "error": "replace op should fail with missing parent key" }, + + { "comment": "spurious patch properties", + "doc": {"foo": 1}, + "patch": [{"op": "test", "path": "/foo", "value": 1, "spurious": 1}], + "expected": {"foo": 1} }, + + { "doc": {"foo": null}, + "patch": [{"op": "test", "path": "/foo", "value": null}], + "expected": {"foo": null}, + "comment": "null value should be valid obj property" }, + + { "doc": {"foo": null}, + "patch": [{"op": "replace", "path": "/foo", "value": "truthy"}], + "expected": {"foo": "truthy"}, + "comment": "null value should be valid obj property to be replaced with something truthy" }, + + { "doc": {"foo": null}, + "patch": [{"op": "move", "from": "/foo", "path": "/bar"}], + "expected": {"bar": null}, + "comment": "null value should be valid obj property to be moved" }, + + { "doc": {"foo": null}, + "patch": [{"op": "copy", "from": "/foo", "path": "/bar"}], + "expected": {"foo": null, "bar": null}, + "comment": "null value should be valid obj property to be copied" }, + + { "doc": {"foo": null}, + "patch": [{"op": "remove", "path": "/foo"}], + "expected": {}, + "comment": "null value should be valid obj property to be removed" }, + + { "doc": {"foo": "bar"}, + "patch": [{"op": "replace", "path": "/foo", "value": null}], + "expected": {"foo": null}, + "comment": "null value should still be valid obj property replace other value" }, + + { "doc": {"foo": {"foo": 1, "bar": 2}}, + "patch": [{"op": "test", "path": "/foo", "value": {"bar": 2, "foo": 1}}], + "expected": {"foo": {"foo": 1, "bar": 2}}, + "comment": "test should pass despite rearrangement" }, + + { "doc": {"foo": [{"foo": 1, "bar": 2}]}, + "patch": [{"op": "test", "path": "/foo", "value": [{"bar": 2, "foo": 1}]}], + "expected": {"foo": [{"foo": 1, "bar": 2}]}, + "comment": "test should pass despite (nested) rearrangement" }, + + { "doc": {"foo": {"bar": [1, 2, 5, 4]}}, + "patch": [{"op": "test", "path": "/foo", "value": {"bar": [1, 2, 5, 4]}}], + "expected": {"foo": {"bar": [1, 2, 5, 4]}}, + "comment": "test should pass - no error" }, + + { "doc": {"foo": {"bar": [1, 2, 5, 4]}}, + "patch": [{"op": "test", "path": "/foo", "value": [1, 2]}], + "error": "test op should fail" }, + + { "comment": "Whole document", + "doc": { "foo": 1 }, + "patch": [{"op": "test", "path": "", "value": {"foo": 1}}], + "disabled": true }, + + { "comment": "Empty-string element", + "doc": { "": 1 }, + "patch": [{"op": "test", "path": "/", "value": 1}], + "expected": { "": 1 } }, + + { "doc": { + "foo": ["bar", "baz"], + "": 0, + "a/b": 1, + "c%d": 2, + "e^f": 3, + "g|h": 4, + "i\\j": 5, + "k\"l": 6, + " ": 7, + "m~n": 8 + }, + "patch": [{"op": "test", "path": "/foo", "value": ["bar", "baz"]}, + {"op": "test", "path": "/foo/0", "value": "bar"}, + {"op": "test", "path": "/", "value": 0}, + {"op": "test", "path": "/a~1b", "value": 1}, + {"op": "test", "path": "/c%d", "value": 2}, + {"op": "test", "path": "/e^f", "value": 3}, + {"op": "test", "path": "/g|h", "value": 4}, + {"op": "test", "path": "/i\\j", "value": 5}, + {"op": "test", "path": "/k\"l", "value": 6}, + {"op": "test", "path": "/ ", "value": 7}, + {"op": "test", "path": "/m~0n", "value": 8}], + "expected": { + "": 0, + " ": 7, + "a/b": 1, + "c%d": 2, + "e^f": 3, + "foo": [ + "bar", + "baz" + ], + "g|h": 4, + "i\\j": 5, + "k\"l": 6, + "m~n": 8 + } + }, + { "comment": "Move to same location has no effect", + "doc": {"foo": 1}, + "patch": [{"op": "move", "from": "/foo", "path": "/foo"}], + "expected": {"foo": 1} }, + + { "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, + "patch": [{"op": "move", "from": "/foo", "path": "/bar"}], + "expected": {"baz": [{"qux": "hello"}], "bar": 1} }, + + { "doc": {"baz": [{"qux": "hello"}], "bar": 1}, + "patch": [{"op": "move", "from": "/baz/0/qux", "path": "/baz/1"}], + "expected": {"baz": [{}, "hello"], "bar": 1} }, + + { "doc": {"baz": [{"qux": "hello"}], "bar": 1}, + "patch": [{"op": "copy", "from": "/baz/0", "path": "/boo"}], + "expected": {"baz":[{"qux":"hello"}],"bar":1,"boo":{"qux":"hello"}} }, + + { "comment": "replacing the root of the document is possible with add", + "doc": {"foo": "bar"}, + "patch": [{"op": "add", "path": "", "value": {"baz": "qux"}}], + "expected": {"baz":"qux"}}, + + { "comment": "Adding to \"/-\" adds to the end of the array", + "doc": [ 1, 2 ], + "patch": [ { "op": "add", "path": "/-", "value": { "foo": [ "bar", "baz" ] } } ], + "expected": [ 1, 2, { "foo": [ "bar", "baz" ] } ]}, + + { "comment": "Adding to \"/-\" adds to the end of the array, even n levels down", + "doc": [ 1, 2, [ 3, [ 4, 5 ] ] ], + "patch": [ { "op": "add", "path": "/2/1/-", "value": { "foo": [ "bar", "baz" ] } } ], + "expected": [ 1, 2, [ 3, [ 4, 5, { "foo": [ "bar", "baz" ] } ] ] ]}, + + { "comment": "test remove with bad number should fail", + "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, + "patch": [{"op": "remove", "path": "/baz/1e0/qux"}], + "error": "remove op shouldn't remove from array with bad number" }, + + { "comment": "test remove on array", + "doc": [1, 2, 3, 4], + "patch": [{"op": "remove", "path": "/0"}], + "expected": [2, 3, 4] }, + + { "comment": "test repeated removes", + "doc": [1, 2, 3, 4], + "patch": [{ "op": "remove", "path": "/1" }, + { "op": "remove", "path": "/2" }], + "expected": [1, 3] }, + + { "comment": "test remove with bad index should fail", + "doc": [1, 2, 3, 4], + "patch": [{"op": "remove", "path": "/1e0"}], + "error": "remove op shouldn't remove from array with bad number" }, + + { "comment": "test replace with bad number should fail", + "doc": [""], + "patch": [{"op": "replace", "path": "/1e0", "value": false}], + "error": "replace op shouldn't replace in array with bad number" }, + + { "comment": "test copy with bad number should fail", + "doc": {"baz": [1,2,3], "bar": 1}, + "patch": [{"op": "copy", "from": "/baz/1e0", "path": "/boo"}], + "error": "copy op shouldn't work with bad number" }, + + { "comment": "test move with bad number should fail", + "doc": {"foo": 1, "baz": [1,2,3,4]}, + "patch": [{"op": "move", "from": "/baz/1e0", "path": "/foo"}], + "error": "move op shouldn't work with bad number" }, + + { "comment": "test add with bad number should fail", + "doc": ["foo", "sil"], + "patch": [{"op": "add", "path": "/1e0", "value": "bar"}], + "error": "add op shouldn't add to array with bad number" }, + + { "comment": "missing 'value' parameter to add", + "doc": [ 1 ], + "patch": [ { "op": "add", "path": "/-" } ], + "error": "missing 'value' parameter" }, + + { "comment": "missing 'value' parameter to replace", + "doc": [ 1 ], + "patch": [ { "op": "replace", "path": "/0" } ], + "error": "missing 'value' parameter" }, + + { "comment": "missing 'value' parameter to test", + "doc": [ null ], + "patch": [ { "op": "test", "path": "/0" } ], + "error": "missing 'value' parameter" }, + + { "comment": "missing value parameter to test - where undef is falsy", + "doc": [ false ], + "patch": [ { "op": "test", "path": "/0" } ], + "error": "missing 'value' parameter" }, + + { "comment": "missing from parameter to copy", + "doc": [ 1 ], + "patch": [ { "op": "copy", "path": "/-" } ], + "error": "missing 'from' parameter" }, + + { "comment": "missing from location to copy", + "doc": { "foo": 1 }, + "patch": [ { "op": "copy", "from": "/bar", "path": "/foo" } ], + "error": "missing 'from' location" }, + + { "comment": "missing from parameter to move", + "doc": { "foo": 1 }, + "patch": [ { "op": "move", "path": "" } ], + "error": "missing 'from' parameter" }, + + { "comment": "missing from location to move", + "doc": { "foo": 1 }, + "patch": [ { "op": "move", "from": "/bar", "path": "/foo" } ], + "error": "missing 'from' location" }, + + { "comment": "duplicate ops", + "doc": { "foo": "bar" }, + "patch": [ { "op": "add", "path": "/baz", "value": "qux", + "op": "move", "from":"/foo" } ], + "error": "patch has two 'op' members", + "disabled": true }, + + { "comment": "unrecognized op should fail", + "doc": {"foo": 1}, + "patch": [{"op": "spam", "path": "/foo", "value": 1}], + "error": "Unrecognized op 'spam'" }, + + { "comment": "test with bad array number that has leading zeros", + "doc": ["foo", "bar"], + "patch": [{"op": "test", "path": "/00", "value": "foo"}], + "error": "test op should reject the array value, it has leading zeros" }, + + { "comment": "test with bad array number that has leading zeros", + "doc": ["foo", "bar"], + "patch": [{"op": "test", "path": "/01", "value": "bar"}], + "error": "test op should reject the array value, it has leading zeros" }, + + { "comment": "Removing nonexistent field", + "doc": {"foo" : "bar"}, + "patch": [{"op": "remove", "path": "/baz"}], + "error": "removing a nonexistent field should fail" }, + + { "comment": "Removing nonexistent index", + "doc": ["foo", "bar"], + "patch": [{"op": "remove", "path": "/2"}], + "error": "removing a nonexistent index should fail" }, + + { "comment": "Patch with different capitalisation than doc", + "doc": {"foo":"bar"}, + "patch": [{"op": "add", "path": "/FOO", "value": "BAR"}], + "expected": {"foo": "bar", "FOO": "BAR"} + } + +] diff --git a/components/spotify/cspot/bell/cJSON/tests/json_patch_tests.c b/components/spotify/cspot/bell/cJSON/tests/json_patch_tests.c new file mode 100644 index 00000000..7c3d6aed --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/json_patch_tests.c @@ -0,0 +1,243 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" +#include "../cJSON_Utils.h" + +static cJSON *parse_test_file(const char * const filename) +{ + char *file = NULL; + cJSON *json = NULL; + + file = read_file(filename); + TEST_ASSERT_NOT_NULL_MESSAGE(file, "Failed to read file."); + + json = cJSON_Parse(file); + TEST_ASSERT_NOT_NULL_MESSAGE(json, "Failed to parse test json."); + TEST_ASSERT_TRUE_MESSAGE(cJSON_IsArray(json), "Json is not an array."); + + free(file); + + return json; +} + +static cJSON_bool test_apply_patch(const cJSON * const test) +{ + cJSON *doc = NULL; + cJSON *patch = NULL; + cJSON *expected = NULL; + cJSON *error_element = NULL; + cJSON *comment = NULL; + cJSON *disabled = NULL; + + cJSON *object = NULL; + cJSON_bool successful = false; + + /* extract all the data out of the test */ + comment = cJSON_GetObjectItemCaseSensitive(test, "comment"); + if (cJSON_IsString(comment)) + { + printf("Testing \"%s\"\n", comment->valuestring); + } + else + { + printf("Testing unknown\n"); + } + + disabled = cJSON_GetObjectItemCaseSensitive(test, "disabled"); + if (cJSON_IsTrue(disabled)) + { + printf("SKIPPED\n"); + return true; + } + + doc = cJSON_GetObjectItemCaseSensitive(test, "doc"); + TEST_ASSERT_NOT_NULL_MESSAGE(doc, "No \"doc\" in the test."); + patch = cJSON_GetObjectItemCaseSensitive(test, "patch"); + TEST_ASSERT_NOT_NULL_MESSAGE(patch, "No \"patch\"in the test."); + /* Make a working copy of 'doc' */ + object = cJSON_Duplicate(doc, true); + TEST_ASSERT_NOT_NULL(object); + + expected = cJSON_GetObjectItemCaseSensitive(test, "expected"); + error_element = cJSON_GetObjectItemCaseSensitive(test, "error"); + if (error_element != NULL) + { + /* excepting an error */ + TEST_ASSERT_TRUE_MESSAGE(0 != cJSONUtils_ApplyPatchesCaseSensitive(object, patch), "Test didn't fail as it's supposed to."); + + successful = true; + } + else + { + /* apply the patch */ + TEST_ASSERT_EQUAL_INT_MESSAGE(0, cJSONUtils_ApplyPatchesCaseSensitive(object, patch), "Failed to apply patches."); + successful = true; + + if (expected != NULL) + { + successful = cJSON_Compare(object, expected, true); + } + } + + cJSON_Delete(object); + + if (successful) + { + printf("OK\n"); + } + else + { + printf("FAILED\n"); + } + + return successful; +} + +static cJSON_bool test_generate_test(cJSON *test) +{ + cJSON *doc = NULL; + cJSON *patch = NULL; + cJSON *expected = NULL; + cJSON *disabled = NULL; + + cJSON *object = NULL; + cJSON_bool successful = false; + + char *printed_patch = NULL; + + disabled = cJSON_GetObjectItemCaseSensitive(test, "disabled"); + if (cJSON_IsTrue(disabled)) + { + printf("SKIPPED\n"); + return true; + } + + doc = cJSON_GetObjectItemCaseSensitive(test, "doc"); + TEST_ASSERT_NOT_NULL_MESSAGE(doc, "No \"doc\" in the test."); + + /* Make a working copy of 'doc' */ + object = cJSON_Duplicate(doc, true); + TEST_ASSERT_NOT_NULL(object); + + expected = cJSON_GetObjectItemCaseSensitive(test, "expected"); + if (expected == NULL) + { + cJSON_Delete(object); + /* if there is no expected output, this test doesn't make sense */ + return true; + } + + patch = cJSONUtils_GeneratePatchesCaseSensitive(doc, expected); + TEST_ASSERT_NOT_NULL_MESSAGE(patch, "Failed to generate patches."); + + printed_patch = cJSON_Print(patch); + printf("%s\n", printed_patch); + free(printed_patch); + + /* apply the generated patch */ + TEST_ASSERT_EQUAL_INT_MESSAGE(0, cJSONUtils_ApplyPatchesCaseSensitive(object, patch), "Failed to apply generated patch."); + + successful = cJSON_Compare(object, expected, true); + + cJSON_Delete(patch); + cJSON_Delete(object); + + if (successful) + { + printf("generated patch: OK\n"); + } + else + { + printf("generated patch: FAILED\n"); + } + + return successful; +} + +static void cjson_utils_should_pass_json_patch_test_tests(void) +{ + cJSON *tests = parse_test_file("json-patch-tests/tests.json"); + cJSON *test = NULL; + + cJSON_bool failed = false; + cJSON_ArrayForEach(test, tests) + { + failed |= !test_apply_patch(test); + failed |= !test_generate_test(test); + } + + cJSON_Delete(tests); + + TEST_ASSERT_FALSE_MESSAGE(failed, "Some tests failed."); +} + +static void cjson_utils_should_pass_json_patch_test_spec_tests(void) +{ + cJSON *tests = parse_test_file("json-patch-tests/spec_tests.json"); + cJSON *test = NULL; + + cJSON_bool failed = false; + cJSON_ArrayForEach(test, tests) + { + failed |= !test_apply_patch(test); + failed |= !test_generate_test(test); + } + + cJSON_Delete(tests); + + TEST_ASSERT_FALSE_MESSAGE(failed, "Some tests failed."); +} + +static void cjson_utils_should_pass_json_patch_test_cjson_utils_tests(void) +{ + cJSON *tests = parse_test_file("json-patch-tests/cjson-utils-tests.json"); + cJSON *test = NULL; + + cJSON_bool failed = false; + cJSON_ArrayForEach(test, tests) + { + failed |= !test_apply_patch(test); + failed |= !test_generate_test(test); + } + + cJSON_Delete(tests); + + TEST_ASSERT_FALSE_MESSAGE(failed, "Some tests failed."); +} + +int main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(cjson_utils_should_pass_json_patch_test_tests); + RUN_TEST(cjson_utils_should_pass_json_patch_test_spec_tests); + RUN_TEST(cjson_utils_should_pass_json_patch_test_cjson_utils_tests); + + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/minify_tests.c b/components/spotify/cspot/bell/cJSON/tests/minify_tests.c new file mode 100644 index 00000000..000821db --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/minify_tests.c @@ -0,0 +1,174 @@ +/* + Copyright (c) 2009-2019 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + + +static void cjson_minify_should_not_overflow_buffer(void) +{ + char unclosed_multiline_comment[] = "/* bla"; + char pending_escape[] = "\"\\"; + + cJSON_Minify(unclosed_multiline_comment); + TEST_ASSERT_EQUAL_STRING("", unclosed_multiline_comment); + + cJSON_Minify(pending_escape); + TEST_ASSERT_EQUAL_STRING("\"\\", pending_escape); +} + +static void cjson_minify_should_remove_single_line_comments(void) +{ + const char to_minify[] = "{// this is {} \"some kind\" of [] comment /*, don't you see\n}"; + + char* minified = (char*) malloc(sizeof(to_minify)); + TEST_ASSERT_NOT_NULL(minified); + strcpy(minified, to_minify); + + cJSON_Minify(minified); + TEST_ASSERT_EQUAL_STRING("{}", minified); + + free(minified); +} + +static void cjson_minify_should_remove_spaces(void) +{ + const char to_minify[] = "{ \"key\":\ttrue\r\n }"; + + char* minified = (char*) malloc(sizeof(to_minify)); + TEST_ASSERT_NOT_NULL(minified); + strcpy(minified, to_minify); + + cJSON_Minify(minified); + TEST_ASSERT_EQUAL_STRING("{\"key\":true}", minified); + + free(minified); +} + +static void cjson_minify_should_remove_multiline_comments(void) +{ + const char to_minify[] = "{/* this is\n a /* multi\n //line \n {comment \"\\\" */}"; + + char* minified = (char*) malloc(sizeof(to_minify)); + TEST_ASSERT_NOT_NULL(minified); + strcpy(minified, to_minify); + + cJSON_Minify(minified); + TEST_ASSERT_EQUAL_STRING("{}", minified); + + free(minified); +} + +static void cjson_minify_should_not_modify_strings(void) +{ + const char to_minify[] = "\"this is a string \\\" \\t bla\""; + + char* minified = (char*) malloc(sizeof(to_minify)); + TEST_ASSERT_NOT_NULL(minified); + strcpy(minified, to_minify); + + cJSON_Minify(minified); + TEST_ASSERT_EQUAL_STRING(to_minify, minified); + + free(minified); +} + +static void cjson_minify_should_minify_json(void) { + const char to_minify[] = + "{\n" + " \"glossary\": { // comment\n" + " \"title\": \"example glossary\",\n" + " /* multi\n" + " line */\n" + " \"GlossDiv\": {\n" + " \"title\": \"S\",\n" + " \"GlossList\": {\n" + " \"GlossEntry\": {\n" + " \"ID\": \"SGML\",\n" + " \"SortAs\": \"SGML\",\n" + " \"Acronym\": \"SGML\",\n" + " \"Abbrev\": \"ISO 8879:1986\",\n" + " \"GlossDef\": {\n" + " \"GlossSeeAlso\": [\"GML\", \"XML\"]\n" + " },\n" + " \"GlossSee\": \"markup\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}"; + const char* minified = + "{" + "\"glossary\":{" + "\"title\":\"example glossary\"," + "\"GlossDiv\":{" + "\"title\":\"S\"," + "\"GlossList\":{" + "\"GlossEntry\":{" + "\"ID\":\"SGML\"," + "\"SortAs\":\"SGML\"," + "\"Acronym\":\"SGML\"," + "\"Abbrev\":\"ISO 8879:1986\"," + "\"GlossDef\":{" + "\"GlossSeeAlso\":[\"GML\",\"XML\"]" + "}," + "\"GlossSee\":\"markup\"" + "}" + "}" + "}" + "}" + "}"; + + char *buffer = (char*) malloc(sizeof(to_minify)); + strcpy(buffer, to_minify); + + cJSON_Minify(buffer); + TEST_ASSERT_EQUAL_STRING(minified, buffer); + + free(buffer); +} + +static void cjson_minify_should_not_loop_infinitely(void) { + char string[] = { '8', ' ', '/', ' ', '5', '\n', '\0' }; + /* this should not be an infinite loop */ + cJSON_Minify(string); +} + +int CJSON_CDECL main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(cjson_minify_should_not_overflow_buffer); + RUN_TEST(cjson_minify_should_minify_json); + RUN_TEST(cjson_minify_should_remove_single_line_comments); + RUN_TEST(cjson_minify_should_remove_multiline_comments); + RUN_TEST(cjson_minify_should_remove_spaces); + RUN_TEST(cjson_minify_should_not_modify_strings); + RUN_TEST(cjson_minify_should_not_loop_infinitely); + + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/misc_tests.c b/components/spotify/cspot/bell/cJSON/tests/misc_tests.c new file mode 100644 index 00000000..3bf0a1cc --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/misc_tests.c @@ -0,0 +1,684 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + + +static void cjson_array_foreach_should_loop_over_arrays(void) +{ + cJSON array[1]; + cJSON elements[10]; + cJSON *element_pointer = NULL; + size_t i = 0; + + memset(array, 0, sizeof(array)); + memset(elements, 0, sizeof(elements)); + + /* create array */ + array[0].child = &elements[0]; + elements[0].prev = NULL; + elements[9].next = NULL; + for (i = 0; i < 9; i++) + { + elements[i].next = &elements[i + 1]; + elements[i + 1].prev = &elements[i]; + } + + i = 0; + cJSON_ArrayForEach(element_pointer, array) + { + TEST_ASSERT_TRUE_MESSAGE(element_pointer == &elements[i], "Not iterating over array properly"); + i++; + } +} + +static void cjson_array_foreach_should_not_dereference_null_pointer(void) +{ + cJSON *array = NULL; + cJSON *element = NULL; + cJSON_ArrayForEach(element, array); +} + +static void cjson_get_object_item_should_get_object_items(void) +{ + cJSON *item = NULL; + cJSON *found = NULL; + + item = cJSON_Parse("{\"one\":1, \"Two\":2, \"tHree\":3}"); + + found = cJSON_GetObjectItem(NULL, "test"); + TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL pointer."); + + found = cJSON_GetObjectItem(item, NULL); + TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL string."); + + + found = cJSON_GetObjectItem(item, "one"); + TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item."); + TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 1); + + found = cJSON_GetObjectItem(item, "tWo"); + TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item."); + TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 2); + + found = cJSON_GetObjectItem(item, "three"); + TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find item."); + TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 3); + + found = cJSON_GetObjectItem(item, "four"); + TEST_ASSERT_NULL_MESSAGE(found, "Should not find something that isn't there."); + + cJSON_Delete(item); +} + +static void cjson_get_object_item_case_sensitive_should_get_object_items(void) +{ + cJSON *item = NULL; + cJSON *found = NULL; + + item = cJSON_Parse("{\"one\":1, \"Two\":2, \"tHree\":3}"); + + found = cJSON_GetObjectItemCaseSensitive(NULL, "test"); + TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL pointer."); + + found = cJSON_GetObjectItemCaseSensitive(item, NULL); + TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL string."); + + found = cJSON_GetObjectItemCaseSensitive(item, "one"); + TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item."); + TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 1); + + found = cJSON_GetObjectItemCaseSensitive(item, "Two"); + TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item."); + TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 2); + + found = cJSON_GetObjectItemCaseSensitive(item, "tHree"); + TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find item."); + TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 3); + + found = cJSON_GetObjectItemCaseSensitive(item, "One"); + TEST_ASSERT_NULL_MESSAGE(found, "Should not find something that isn't there."); + + cJSON_Delete(item); +} + +static void cjson_get_object_item_should_not_crash_with_array(void) { + cJSON *array = NULL; + cJSON *found = NULL; + array = cJSON_Parse("[1]"); + + found = cJSON_GetObjectItem(array, "name"); + TEST_ASSERT_NULL(found); + + cJSON_Delete(array); +} + +static void cjson_get_object_item_case_sensitive_should_not_crash_with_array(void) { + cJSON *array = NULL; + cJSON *found = NULL; + array = cJSON_Parse("[1]"); + + found = cJSON_GetObjectItemCaseSensitive(array, "name"); + TEST_ASSERT_NULL(found); + + cJSON_Delete(array); +} + +static void typecheck_functions_should_check_type(void) +{ + cJSON invalid[1]; + cJSON item[1]; + invalid->type = cJSON_Invalid; + invalid->type |= cJSON_StringIsConst; + item->type = cJSON_False; + item->type |= cJSON_StringIsConst; + + TEST_ASSERT_FALSE(cJSON_IsInvalid(NULL)); + TEST_ASSERT_FALSE(cJSON_IsInvalid(item)); + TEST_ASSERT_TRUE(cJSON_IsInvalid(invalid)); + + item->type = cJSON_False | cJSON_StringIsConst; + TEST_ASSERT_FALSE(cJSON_IsFalse(NULL)); + TEST_ASSERT_FALSE(cJSON_IsFalse(invalid)); + TEST_ASSERT_TRUE(cJSON_IsFalse(item)); + TEST_ASSERT_TRUE(cJSON_IsBool(item)); + + item->type = cJSON_True | cJSON_StringIsConst; + TEST_ASSERT_FALSE(cJSON_IsTrue(NULL)); + TEST_ASSERT_FALSE(cJSON_IsTrue(invalid)); + TEST_ASSERT_TRUE(cJSON_IsTrue(item)); + TEST_ASSERT_TRUE(cJSON_IsBool(item)); + + item->type = cJSON_NULL | cJSON_StringIsConst; + TEST_ASSERT_FALSE(cJSON_IsNull(NULL)); + TEST_ASSERT_FALSE(cJSON_IsNull(invalid)); + TEST_ASSERT_TRUE(cJSON_IsNull(item)); + + item->type = cJSON_Number | cJSON_StringIsConst; + TEST_ASSERT_FALSE(cJSON_IsNumber(NULL)); + TEST_ASSERT_FALSE(cJSON_IsNumber(invalid)); + TEST_ASSERT_TRUE(cJSON_IsNumber(item)); + + item->type = cJSON_String | cJSON_StringIsConst; + TEST_ASSERT_FALSE(cJSON_IsString(NULL)); + TEST_ASSERT_FALSE(cJSON_IsString(invalid)); + TEST_ASSERT_TRUE(cJSON_IsString(item)); + + item->type = cJSON_Array | cJSON_StringIsConst; + TEST_ASSERT_FALSE(cJSON_IsArray(NULL)); + TEST_ASSERT_FALSE(cJSON_IsArray(invalid)); + TEST_ASSERT_TRUE(cJSON_IsArray(item)); + + item->type = cJSON_Object | cJSON_StringIsConst; + TEST_ASSERT_FALSE(cJSON_IsObject(NULL)); + TEST_ASSERT_FALSE(cJSON_IsObject(invalid)); + TEST_ASSERT_TRUE(cJSON_IsObject(item)); + + item->type = cJSON_Raw | cJSON_StringIsConst; + TEST_ASSERT_FALSE(cJSON_IsRaw(NULL)); + TEST_ASSERT_FALSE(cJSON_IsRaw(invalid)); + TEST_ASSERT_TRUE(cJSON_IsRaw(item)); +} + +static void cjson_should_not_parse_to_deeply_nested_jsons(void) +{ + char deep_json[CJSON_NESTING_LIMIT + 1]; + size_t position = 0; + + for (position = 0; position < sizeof(deep_json); position++) + { + deep_json[position] = '['; + } + deep_json[sizeof(deep_json) - 1] = '\0'; + + TEST_ASSERT_NULL_MESSAGE(cJSON_Parse(deep_json), "To deep JSONs should not be parsed."); +} + +static void cjson_set_number_value_should_set_numbers(void) +{ + cJSON number[1] = {{NULL, NULL, NULL, cJSON_Number, NULL, 0, 0, NULL}}; + + cJSON_SetNumberValue(number, 1.5); + TEST_ASSERT_EQUAL(1, number->valueint); + TEST_ASSERT_EQUAL_DOUBLE(1.5, number->valuedouble); + + cJSON_SetNumberValue(number, -1.5); + TEST_ASSERT_EQUAL(-1, number->valueint); + TEST_ASSERT_EQUAL_DOUBLE(-1.5, number->valuedouble); + + cJSON_SetNumberValue(number, 1 + (double)INT_MAX); + TEST_ASSERT_EQUAL(INT_MAX, number->valueint); + TEST_ASSERT_EQUAL_DOUBLE(1 + (double)INT_MAX, number->valuedouble); + + cJSON_SetNumberValue(number, -1 + (double)INT_MIN); + TEST_ASSERT_EQUAL(INT_MIN, number->valueint); + TEST_ASSERT_EQUAL_DOUBLE(-1 + (double)INT_MIN, number->valuedouble); +} + +static void cjson_detach_item_via_pointer_should_detach_items(void) +{ + cJSON list[4]; + cJSON parent[1]; + + memset(list, '\0', sizeof(list)); + + /* link the list */ + list[0].next = &(list[1]); + list[1].next = &(list[2]); + list[2].next = &(list[3]); + + list[3].prev = &(list[2]); + list[2].prev = &(list[1]); + list[1].prev = &(list[0]); + list[0].prev = &(list[3]); + + parent->child = &list[0]; + + /* detach in the middle (list[1]) */ + TEST_ASSERT_TRUE_MESSAGE(cJSON_DetachItemViaPointer(parent, &(list[1])) == &(list[1]), "Failed to detach in the middle."); + TEST_ASSERT_TRUE_MESSAGE((list[1].prev == NULL) && (list[1].next == NULL), "Didn't set pointers of detached item to NULL."); + TEST_ASSERT_TRUE((list[0].next == &(list[2])) && (list[2].prev == &(list[0]))); + + /* detach beginning (list[0]) */ + TEST_ASSERT_TRUE_MESSAGE(cJSON_DetachItemViaPointer(parent, &(list[0])) == &(list[0]), "Failed to detach beginning."); + TEST_ASSERT_TRUE_MESSAGE((list[0].prev == NULL) && (list[0].next == NULL), "Didn't set pointers of detached item to NULL."); + TEST_ASSERT_TRUE_MESSAGE((list[2].prev == &(list[3])) && (parent->child == &(list[2])), "Didn't set the new beginning."); + + /* detach end (list[3])*/ + TEST_ASSERT_TRUE_MESSAGE(cJSON_DetachItemViaPointer(parent, &(list[3])) == &(list[3]), "Failed to detach end."); + TEST_ASSERT_TRUE_MESSAGE((list[3].prev == NULL) && (list[3].next == NULL), "Didn't set pointers of detached item to NULL."); + TEST_ASSERT_TRUE_MESSAGE((list[2].next == NULL) && (parent->child == &(list[2])), "Didn't set the new end"); + + /* detach single item (list[2]) */ + TEST_ASSERT_TRUE_MESSAGE(cJSON_DetachItemViaPointer(parent, &list[2]) == &list[2], "Failed to detach single item."); + TEST_ASSERT_TRUE_MESSAGE((list[2].prev == NULL) && (list[2].next == NULL), "Didn't set pointers of detached item to NULL."); + TEST_ASSERT_NULL_MESSAGE(parent->child, "Child of the parent wasn't set to NULL."); +} + +static void cjson_replace_item_via_pointer_should_replace_items(void) +{ + cJSON replacements[3]; + cJSON *beginning = NULL; + cJSON *middle = NULL; + cJSON *end = NULL; + cJSON *array = NULL; + + beginning = cJSON_CreateNull(); + TEST_ASSERT_NOT_NULL(beginning); + middle = cJSON_CreateNull(); + TEST_ASSERT_NOT_NULL(middle); + end = cJSON_CreateNull(); + TEST_ASSERT_NOT_NULL(end); + + array = cJSON_CreateArray(); + TEST_ASSERT_NOT_NULL(array); + + cJSON_AddItemToArray(array, beginning); + cJSON_AddItemToArray(array, middle); + cJSON_AddItemToArray(array, end); + + + memset(replacements, '\0', sizeof(replacements)); + + /* replace beginning */ + TEST_ASSERT_TRUE(cJSON_ReplaceItemViaPointer(array, beginning, &(replacements[0]))); + TEST_ASSERT_TRUE(replacements[0].prev == end); + TEST_ASSERT_TRUE(replacements[0].next == middle); + TEST_ASSERT_TRUE(middle->prev == &(replacements[0])); + TEST_ASSERT_TRUE(array->child == &(replacements[0])); + + /* replace middle */ + TEST_ASSERT_TRUE(cJSON_ReplaceItemViaPointer(array, middle, &(replacements[1]))); + TEST_ASSERT_TRUE(replacements[1].prev == &(replacements[0])); + TEST_ASSERT_TRUE(replacements[1].next == end); + TEST_ASSERT_TRUE(end->prev == &(replacements[1])); + + /* replace end */ + TEST_ASSERT_TRUE(cJSON_ReplaceItemViaPointer(array, end, &(replacements[2]))); + TEST_ASSERT_TRUE(replacements[2].prev == &(replacements[1])); + TEST_ASSERT_NULL(replacements[2].next); + TEST_ASSERT_TRUE(replacements[1].next == &(replacements[2])); + + cJSON_free(array); +} + +static void cjson_replace_item_in_object_should_preserve_name(void) +{ + cJSON root[1] = {{ NULL, NULL, NULL, 0, NULL, 0, 0, NULL }}; + cJSON *child = NULL; + cJSON *replacement = NULL; + cJSON_bool flag = false; + + child = cJSON_CreateNumber(1); + TEST_ASSERT_NOT_NULL(child); + replacement = cJSON_CreateNumber(2); + TEST_ASSERT_NOT_NULL(replacement); + + flag = cJSON_AddItemToObject(root, "child", child); + TEST_ASSERT_TRUE_MESSAGE(flag, "add item to object failed"); + cJSON_ReplaceItemInObject(root, "child", replacement); + + TEST_ASSERT_TRUE(root->child == replacement); + TEST_ASSERT_EQUAL_STRING("child", replacement->string); + + cJSON_Delete(replacement); +} + +static void cjson_functions_should_not_crash_with_null_pointers(void) +{ + char buffer[10]; + cJSON *item = cJSON_CreateString("item"); + + cJSON_InitHooks(NULL); + TEST_ASSERT_NULL(cJSON_Parse(NULL)); + TEST_ASSERT_NULL(cJSON_ParseWithOpts(NULL, NULL, true)); + TEST_ASSERT_NULL(cJSON_Print(NULL)); + TEST_ASSERT_NULL(cJSON_PrintUnformatted(NULL)); + TEST_ASSERT_NULL(cJSON_PrintBuffered(NULL, 10, true)); + TEST_ASSERT_FALSE(cJSON_PrintPreallocated(NULL, buffer, sizeof(buffer), true)); + TEST_ASSERT_FALSE(cJSON_PrintPreallocated(item, NULL, 1, true)); + cJSON_Delete(NULL); + cJSON_GetArraySize(NULL); + TEST_ASSERT_NULL(cJSON_GetArrayItem(NULL, 0)); + TEST_ASSERT_NULL(cJSON_GetObjectItem(NULL, "item")); + TEST_ASSERT_NULL(cJSON_GetObjectItem(item, NULL)); + TEST_ASSERT_NULL(cJSON_GetObjectItemCaseSensitive(NULL, "item")); + TEST_ASSERT_NULL(cJSON_GetObjectItemCaseSensitive(item, NULL)); + TEST_ASSERT_FALSE(cJSON_HasObjectItem(NULL, "item")); + TEST_ASSERT_FALSE(cJSON_HasObjectItem(item, NULL)); + TEST_ASSERT_FALSE(cJSON_IsInvalid(NULL)); + TEST_ASSERT_FALSE(cJSON_IsFalse(NULL)); + TEST_ASSERT_FALSE(cJSON_IsTrue(NULL)); + TEST_ASSERT_FALSE(cJSON_IsBool(NULL)); + TEST_ASSERT_FALSE(cJSON_IsNull(NULL)); + TEST_ASSERT_FALSE(cJSON_IsNumber(NULL)); + TEST_ASSERT_FALSE(cJSON_IsString(NULL)); + TEST_ASSERT_FALSE(cJSON_IsArray(NULL)); + TEST_ASSERT_FALSE(cJSON_IsObject(NULL)); + TEST_ASSERT_FALSE(cJSON_IsRaw(NULL)); + TEST_ASSERT_NULL(cJSON_CreateString(NULL)); + TEST_ASSERT_NULL(cJSON_CreateRaw(NULL)); + TEST_ASSERT_NULL(cJSON_CreateIntArray(NULL, 10)); + TEST_ASSERT_NULL(cJSON_CreateFloatArray(NULL, 10)); + TEST_ASSERT_NULL(cJSON_CreateDoubleArray(NULL, 10)); + TEST_ASSERT_NULL(cJSON_CreateStringArray(NULL, 10)); + cJSON_AddItemToArray(NULL, item); + cJSON_AddItemToArray(item, NULL); + cJSON_AddItemToObject(item, "item", NULL); + cJSON_AddItemToObject(item, NULL, item); + cJSON_AddItemToObject(NULL, "item", item); + cJSON_AddItemToObjectCS(item, "item", NULL); + cJSON_AddItemToObjectCS(item, NULL, item); + cJSON_AddItemToObjectCS(NULL, "item", item); + cJSON_AddItemReferenceToArray(NULL, item); + cJSON_AddItemReferenceToArray(item, NULL); + cJSON_AddItemReferenceToObject(item, "item", NULL); + cJSON_AddItemReferenceToObject(item, NULL, item); + cJSON_AddItemReferenceToObject(NULL, "item", item); + TEST_ASSERT_NULL(cJSON_DetachItemViaPointer(NULL, item)); + TEST_ASSERT_NULL(cJSON_DetachItemViaPointer(item, NULL)); + TEST_ASSERT_NULL(cJSON_DetachItemFromArray(NULL, 0)); + cJSON_DeleteItemFromArray(NULL, 0); + TEST_ASSERT_NULL(cJSON_DetachItemFromObject(NULL, "item")); + TEST_ASSERT_NULL(cJSON_DetachItemFromObject(item, NULL)); + TEST_ASSERT_NULL(cJSON_DetachItemFromObjectCaseSensitive(NULL, "item")); + TEST_ASSERT_NULL(cJSON_DetachItemFromObjectCaseSensitive(item, NULL)); + cJSON_DeleteItemFromObject(NULL, "item"); + cJSON_DeleteItemFromObject(item, NULL); + cJSON_DeleteItemFromObjectCaseSensitive(NULL, "item"); + cJSON_DeleteItemFromObjectCaseSensitive(item, NULL); + TEST_ASSERT_FALSE(cJSON_InsertItemInArray(NULL, 0, item)); + TEST_ASSERT_FALSE(cJSON_InsertItemInArray(item, 0, NULL)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemViaPointer(NULL, item, item)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemViaPointer(item, NULL, item)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemViaPointer(item, item, NULL)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemInArray(item, 0, NULL)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemInArray(NULL, 0, item)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemInObject(NULL, "item", item)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemInObject(item, NULL, item)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemInObject(item, "item", NULL)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemInObjectCaseSensitive(NULL, "item", item)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemInObjectCaseSensitive(item, NULL, item)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemInObjectCaseSensitive(item, "item", NULL)); + TEST_ASSERT_NULL(cJSON_Duplicate(NULL, true)); + TEST_ASSERT_FALSE(cJSON_Compare(item, NULL, false)); + TEST_ASSERT_FALSE(cJSON_Compare(NULL, item, false)); + cJSON_Minify(NULL); + /* skipped because it is only used via a macro that checks for NULL */ + /* cJSON_SetNumberHelper(NULL, 0); */ + + cJSON_Delete(item); +} + +static void * CJSON_CDECL failing_realloc(void *pointer, size_t size) +{ + (void)size; + (void)pointer; + return NULL; +} + +static void ensure_should_fail_on_failed_realloc(void) +{ + printbuffer buffer = {NULL, 10, 0, 0, false, false, {&malloc, &free, &failing_realloc}}; + buffer.buffer = (unsigned char*)malloc(100); + TEST_ASSERT_NOT_NULL(buffer.buffer); + + TEST_ASSERT_NULL_MESSAGE(ensure(&buffer, 200), "Ensure didn't fail with failing realloc."); +} + +static void skip_utf8_bom_should_skip_bom(void) +{ + const unsigned char string[] = "\xEF\xBB\xBF{}"; + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + buffer.content = string; + buffer.length = sizeof(string); + buffer.hooks = global_hooks; + + TEST_ASSERT_TRUE(skip_utf8_bom(&buffer) == &buffer); + TEST_ASSERT_EQUAL_UINT(3U, (unsigned int)buffer.offset); +} + +static void skip_utf8_bom_should_not_skip_bom_if_not_at_beginning(void) +{ + const unsigned char string[] = " \xEF\xBB\xBF{}"; + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + buffer.content = string; + buffer.length = sizeof(string); + buffer.hooks = global_hooks; + buffer.offset = 1; + + TEST_ASSERT_NULL(skip_utf8_bom(&buffer)); +} + +static void cjson_get_string_value_should_get_a_string(void) +{ + cJSON *string = cJSON_CreateString("test"); + cJSON *number = cJSON_CreateNumber(1); + + TEST_ASSERT_TRUE(cJSON_GetStringValue(string) == string->valuestring); + TEST_ASSERT_NULL(cJSON_GetStringValue(number)); + TEST_ASSERT_NULL(cJSON_GetStringValue(NULL)); + + cJSON_Delete(number); + cJSON_Delete(string); +} + +static void cjson_get_number_value_should_get_a_number(void) +{ + cJSON *string = cJSON_CreateString("test"); + cJSON *number = cJSON_CreateNumber(1); + + TEST_ASSERT_EQUAL_DOUBLE(cJSON_GetNumberValue(number), number->valuedouble); + TEST_ASSERT_DOUBLE_IS_NAN(cJSON_GetNumberValue(string)); + TEST_ASSERT_DOUBLE_IS_NAN(cJSON_GetNumberValue(NULL)); + + cJSON_Delete(number); + cJSON_Delete(string); +} + +static void cjson_create_string_reference_should_create_a_string_reference(void) { + const char *string = "I am a string!"; + + cJSON *string_reference = cJSON_CreateStringReference(string); + TEST_ASSERT_TRUE(string_reference->valuestring == string); + TEST_ASSERT_EQUAL_INT(cJSON_IsReference | cJSON_String, string_reference->type); + + cJSON_Delete(string_reference); +} + +static void cjson_create_object_reference_should_create_an_object_reference(void) { + cJSON *number_reference = NULL; + cJSON *number_object = cJSON_CreateObject(); + cJSON *number = cJSON_CreateNumber(42); + const char key[] = "number"; + + TEST_ASSERT_TRUE(cJSON_IsNumber(number)); + TEST_ASSERT_TRUE(cJSON_IsObject(number_object)); + cJSON_AddItemToObjectCS(number_object, key, number); + + number_reference = cJSON_CreateObjectReference(number); + TEST_ASSERT_TRUE(number_reference->child == number); + TEST_ASSERT_EQUAL_INT(cJSON_Object | cJSON_IsReference, number_reference->type); + + cJSON_Delete(number_object); + cJSON_Delete(number_reference); +} + +static void cjson_create_array_reference_should_create_an_array_reference(void) { + cJSON *number_reference = NULL; + cJSON *number_array = cJSON_CreateArray(); + cJSON *number = cJSON_CreateNumber(42); + + TEST_ASSERT_TRUE(cJSON_IsNumber(number)); + TEST_ASSERT_TRUE(cJSON_IsArray(number_array)); + cJSON_AddItemToArray(number_array, number); + + number_reference = cJSON_CreateArrayReference(number); + TEST_ASSERT_TRUE(number_reference->child == number); + TEST_ASSERT_EQUAL_INT(cJSON_Array | cJSON_IsReference, number_reference->type); + + cJSON_Delete(number_array); + cJSON_Delete(number_reference); +} + +static void cjson_add_item_to_object_or_array_should_not_add_itself(void) +{ + cJSON *object = cJSON_CreateObject(); + cJSON *array = cJSON_CreateArray(); + cJSON_bool flag = false; + + flag = cJSON_AddItemToObject(object, "key", object); + TEST_ASSERT_FALSE_MESSAGE(flag, "add an object to itself should fail"); + + flag = cJSON_AddItemToArray(array, array); + TEST_ASSERT_FALSE_MESSAGE(flag, "add an array to itself should fail"); + + cJSON_Delete(object); + cJSON_Delete(array); +} + +static void cjson_add_item_to_object_should_not_use_after_free_when_string_is_aliased(void) +{ + cJSON *object = cJSON_CreateObject(); + cJSON *number = cJSON_CreateNumber(42); + char *name = (char*)cJSON_strdup((const unsigned char*)"number", &global_hooks); + + TEST_ASSERT_NOT_NULL(object); + TEST_ASSERT_NOT_NULL(number); + TEST_ASSERT_NOT_NULL(name); + + number->string = name; + + /* The following should not have a use after free + * that would show up in valgrind or with AddressSanitizer */ + cJSON_AddItemToObject(object, number->string, number); + + cJSON_Delete(object); +} + +static void cjson_delete_item_from_array_should_not_broken_list_structure(void) +{ + const char expected_json1[] = "{\"rd\":[{\"a\":\"123\"}]}"; + const char expected_json2[] = "{\"rd\":[{\"a\":\"123\"},{\"b\":\"456\"}]}"; + const char expected_json3[] = "{\"rd\":[{\"b\":\"456\"}]}"; + char *str1 = NULL; + char *str2 = NULL; + char *str3 = NULL; + + cJSON *root = cJSON_Parse("{}"); + + cJSON *array = cJSON_AddArrayToObject(root, "rd"); + cJSON *item1 = cJSON_Parse("{\"a\":\"123\"}"); + cJSON *item2 = cJSON_Parse("{\"b\":\"456\"}"); + + cJSON_AddItemToArray(array, item1); + str1 = cJSON_PrintUnformatted(root); + TEST_ASSERT_EQUAL_STRING(expected_json1, str1); + free(str1); + + cJSON_AddItemToArray(array, item2); + str2 = cJSON_PrintUnformatted(root); + TEST_ASSERT_EQUAL_STRING(expected_json2, str2); + free(str2); + + /* this should not broken list structure */ + cJSON_DeleteItemFromArray(array, 0); + str3 = cJSON_PrintUnformatted(root); + TEST_ASSERT_EQUAL_STRING(expected_json3, str3); + free(str3); + + cJSON_Delete(root); +} + +static void cjson_set_valuestring_to_object_should_not_leak_memory(void) +{ + cJSON *root = cJSON_Parse("{}"); + const char *stringvalue = "valuestring could be changed safely"; + const char *reference_valuestring = "reference item should be freed by yourself"; + const char *short_valuestring = "shorter valuestring"; + const char *long_valuestring = "new valuestring which much longer than previous should be changed safely"; + cJSON *item1 = cJSON_CreateString(stringvalue); + cJSON *item2 = cJSON_CreateStringReference(reference_valuestring); + char *ptr1 = NULL; + char *return_value = NULL; + + cJSON_AddItemToObject(root, "one", item1); + cJSON_AddItemToObject(root, "two", item2); + + ptr1 = item1->valuestring; + return_value = cJSON_SetValuestring(cJSON_GetObjectItem(root, "one"), short_valuestring); + TEST_ASSERT_NOT_NULL(return_value); + TEST_ASSERT_EQUAL_PTR_MESSAGE(ptr1, return_value, "new valuestring shorter than old should not reallocate memory"); + TEST_ASSERT_EQUAL_STRING(short_valuestring, cJSON_GetObjectItem(root, "one")->valuestring); + + /* we needn't to free the original valuestring manually */ + ptr1 = item1->valuestring; + return_value = cJSON_SetValuestring(cJSON_GetObjectItem(root, "one"), long_valuestring); + TEST_ASSERT_NOT_NULL(return_value); + TEST_ASSERT_NOT_EQUAL_MESSAGE(ptr1, return_value, "new valuestring longer than old should reallocate memory") + TEST_ASSERT_EQUAL_STRING(long_valuestring, cJSON_GetObjectItem(root, "one")->valuestring); + + return_value = cJSON_SetValuestring(cJSON_GetObjectItem(root, "two"), long_valuestring); + TEST_ASSERT_NULL_MESSAGE(return_value, "valuestring of reference object should not be changed"); + TEST_ASSERT_EQUAL_STRING(reference_valuestring, cJSON_GetObjectItem(root, "two")->valuestring); + + cJSON_Delete(root); +} + +int CJSON_CDECL main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(cjson_array_foreach_should_loop_over_arrays); + RUN_TEST(cjson_array_foreach_should_not_dereference_null_pointer); + RUN_TEST(cjson_get_object_item_should_get_object_items); + RUN_TEST(cjson_get_object_item_case_sensitive_should_get_object_items); + RUN_TEST(cjson_get_object_item_should_not_crash_with_array); + RUN_TEST(cjson_get_object_item_case_sensitive_should_not_crash_with_array); + RUN_TEST(typecheck_functions_should_check_type); + RUN_TEST(cjson_should_not_parse_to_deeply_nested_jsons); + RUN_TEST(cjson_set_number_value_should_set_numbers); + RUN_TEST(cjson_detach_item_via_pointer_should_detach_items); + RUN_TEST(cjson_replace_item_via_pointer_should_replace_items); + RUN_TEST(cjson_replace_item_in_object_should_preserve_name); + RUN_TEST(cjson_functions_should_not_crash_with_null_pointers); + RUN_TEST(ensure_should_fail_on_failed_realloc); + RUN_TEST(skip_utf8_bom_should_skip_bom); + RUN_TEST(skip_utf8_bom_should_not_skip_bom_if_not_at_beginning); + RUN_TEST(cjson_get_string_value_should_get_a_string); + RUN_TEST(cjson_get_number_value_should_get_a_number); + RUN_TEST(cjson_create_string_reference_should_create_a_string_reference); + RUN_TEST(cjson_create_object_reference_should_create_an_object_reference); + RUN_TEST(cjson_create_array_reference_should_create_an_array_reference); + RUN_TEST(cjson_add_item_to_object_or_array_should_not_add_itself); + RUN_TEST(cjson_add_item_to_object_should_not_use_after_free_when_string_is_aliased); + RUN_TEST(cjson_delete_item_from_array_should_not_broken_list_structure); + RUN_TEST(cjson_set_valuestring_to_object_should_not_leak_memory); + + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/misc_utils_tests.c b/components/spotify/cspot/bell/cJSON/tests/misc_utils_tests.c new file mode 100644 index 00000000..7d300bc8 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/misc_utils_tests.c @@ -0,0 +1,80 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" +#include "../cJSON_Utils.h" + +static void cjson_utils_functions_shouldnt_crash_with_null_pointers(void) +{ + cJSON *item = cJSON_CreateString("item"); + TEST_ASSERT_NOT_NULL(item); + + TEST_ASSERT_NULL(cJSONUtils_GetPointer(item, NULL)); + TEST_ASSERT_NULL(cJSONUtils_GetPointer(NULL, "pointer")); + TEST_ASSERT_NULL(cJSONUtils_GetPointerCaseSensitive(NULL, "pointer")); + TEST_ASSERT_NULL(cJSONUtils_GetPointerCaseSensitive(item, NULL)); + TEST_ASSERT_NULL(cJSONUtils_GeneratePatches(item, NULL)); + TEST_ASSERT_NULL(cJSONUtils_GeneratePatches(NULL, item)); + TEST_ASSERT_NULL(cJSONUtils_GeneratePatchesCaseSensitive(item, NULL)); + TEST_ASSERT_NULL(cJSONUtils_GeneratePatchesCaseSensitive(NULL, item)); + cJSONUtils_AddPatchToArray(item, "path", "add", NULL); + cJSONUtils_AddPatchToArray(item, "path", NULL, item); + cJSONUtils_AddPatchToArray(item, NULL, "add", item); + cJSONUtils_AddPatchToArray(NULL, "path", "add", item); + cJSONUtils_ApplyPatches(item, NULL); + cJSONUtils_ApplyPatches(NULL, item); + cJSONUtils_ApplyPatchesCaseSensitive(item, NULL); + cJSONUtils_ApplyPatchesCaseSensitive(NULL, item); + TEST_ASSERT_NULL(cJSONUtils_MergePatch(item, NULL)); + item = cJSON_CreateString("item"); + TEST_ASSERT_NULL(cJSONUtils_MergePatchCaseSensitive(item, NULL)); + item = cJSON_CreateString("item"); + /* these calls are actually valid */ + /* cJSONUtils_MergePatch(NULL, item); */ + /* cJSONUtils_MergePatchCaseSensitive(NULL, item);*/ + /* cJSONUtils_GenerateMergePatch(item, NULL); */ + /* cJSONUtils_GenerateMergePatch(NULL, item); */ + /* cJSONUtils_GenerateMergePatchCaseSensitive(item, NULL); */ + /* cJSONUtils_GenerateMergePatchCaseSensitive(NULL, item); */ + + TEST_ASSERT_NULL(cJSONUtils_FindPointerFromObjectTo(item, NULL)); + TEST_ASSERT_NULL(cJSONUtils_FindPointerFromObjectTo(NULL, item)); + cJSONUtils_SortObject(NULL); + cJSONUtils_SortObjectCaseSensitive(NULL); + + cJSON_Delete(item); +} + +int main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(cjson_utils_functions_shouldnt_crash_with_null_pointers); + + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/old_utils_tests.c b/components/spotify/cspot/bell/cJSON/tests/old_utils_tests.c new file mode 100644 index 00000000..690dbb58 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/old_utils_tests.c @@ -0,0 +1,225 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" +#include "../cJSON_Utils.h" + +/* JSON Apply Merge tests: */ +static const char *merges[15][3] = +{ + {"{\"a\":\"b\"}", "{\"a\":\"c\"}", "{\"a\":\"c\"}"}, + {"{\"a\":\"b\"}", "{\"b\":\"c\"}", "{\"a\":\"b\",\"b\":\"c\"}"}, + {"{\"a\":\"b\"}", "{\"a\":null}", "{}"}, + {"{\"a\":\"b\",\"b\":\"c\"}", "{\"a\":null}", "{\"b\":\"c\"}"}, + {"{\"a\":[\"b\"]}", "{\"a\":\"c\"}", "{\"a\":\"c\"}"}, + {"{\"a\":\"c\"}", "{\"a\":[\"b\"]}", "{\"a\":[\"b\"]}"}, + {"{\"a\":{\"b\":\"c\"}}", "{\"a\":{\"b\":\"d\",\"c\":null}}", "{\"a\":{\"b\":\"d\"}}"}, + {"{\"a\":[{\"b\":\"c\"}]}", "{\"a\":[1]}", "{\"a\":[1]}"}, + {"[\"a\",\"b\"]", "[\"c\",\"d\"]", "[\"c\",\"d\"]"}, + {"{\"a\":\"b\"}", "[\"c\"]", "[\"c\"]"}, + {"{\"a\":\"foo\"}", "null", "null"}, + {"{\"a\":\"foo\"}", "\"bar\"", "\"bar\""}, + {"{\"e\":null}", "{\"a\":1}", "{\"e\":null,\"a\":1}"}, + {"[1,2]", "{\"a\":\"b\",\"c\":null}", "{\"a\":\"b\"}"}, + {"{}","{\"a\":{\"bb\":{\"ccc\":null}}}", "{\"a\":{\"bb\":{}}}"} +}; + +static void json_pointer_tests(void) +{ + cJSON *root = NULL; + const char *json= + "{" + "\"foo\": [\"bar\", \"baz\"]," + "\"\": 0," + "\"a/b\": 1," + "\"c%d\": 2," + "\"e^f\": 3," + "\"g|h\": 4," + "\"i\\\\j\": 5," + "\"k\\\"l\": 6," + "\" \": 7," + "\"m~n\": 8" + "}"; + + root = cJSON_Parse(json); + + TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, ""), root); + TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/foo"), cJSON_GetObjectItem(root, "foo")); + TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/foo/0"), cJSON_GetObjectItem(root, "foo")->child); + TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/foo/0"), cJSON_GetObjectItem(root, "foo")->child); + TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/"), cJSON_GetObjectItem(root, "")); + TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/a~1b"), cJSON_GetObjectItem(root, "a/b")); + TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/c%d"), cJSON_GetObjectItem(root, "c%d")); + TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/c^f"), cJSON_GetObjectItem(root, "c^f")); + TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/c|f"), cJSON_GetObjectItem(root, "c|f")); + TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/i\\j"), cJSON_GetObjectItem(root, "i\\j")); + TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/k\"l"), cJSON_GetObjectItem(root, "k\"l")); + TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/ "), cJSON_GetObjectItem(root, " ")); + TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/m~0n"), cJSON_GetObjectItem(root, "m~n")); + + cJSON_Delete(root); +} + +static void misc_tests(void) +{ + /* Misc tests */ + int numbers[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + cJSON *object = NULL; + cJSON *object1 = NULL; + cJSON *object2 = NULL; + cJSON *object3 = NULL; + cJSON *object4 = NULL; + cJSON *nums = NULL; + cJSON *num6 = NULL; + char *pointer = NULL; + + printf("JSON Pointer construct\n"); + object = cJSON_CreateObject(); + nums = cJSON_CreateIntArray(numbers, 10); + num6 = cJSON_GetArrayItem(nums, 6); + cJSON_AddItemToObject(object, "numbers", nums); + + pointer = cJSONUtils_FindPointerFromObjectTo(object, num6); + TEST_ASSERT_EQUAL_STRING("/numbers/6", pointer); + free(pointer); + + pointer = cJSONUtils_FindPointerFromObjectTo(object, nums); + TEST_ASSERT_EQUAL_STRING("/numbers", pointer); + free(pointer); + + pointer = cJSONUtils_FindPointerFromObjectTo(object, object); + TEST_ASSERT_EQUAL_STRING("", pointer); + free(pointer); + + object1 = cJSON_CreateObject(); + object2 = cJSON_CreateString("m~n"); + cJSON_AddItemToObject(object1, "m~n", object2); + pointer = cJSONUtils_FindPointerFromObjectTo(object1, object2); + TEST_ASSERT_EQUAL_STRING("/m~0n",pointer); + free(pointer); + + object3 = cJSON_CreateObject(); + object4 = cJSON_CreateString("m/n"); + cJSON_AddItemToObject(object3, "m/n", object4); + pointer = cJSONUtils_FindPointerFromObjectTo(object3, object4); + TEST_ASSERT_EQUAL_STRING("/m~1n",pointer); + free(pointer); + + cJSON_Delete(object); + cJSON_Delete(object1); + cJSON_Delete(object3); +} + +static void sort_tests(void) +{ + /* Misc tests */ + const char *random = "QWERTYUIOPASDFGHJKLZXCVBNM"; + char buf[2] = {'\0', '\0'}; + cJSON *sortme = NULL; + size_t i = 0; + cJSON *current_element = NULL; + + /* JSON Sort test: */ + sortme = cJSON_CreateObject(); + for (i = 0; i < 26; i++) + { + buf[0] = random[i]; + cJSON_AddItemToObject(sortme, buf, cJSON_CreateNumber(1)); + } + + cJSONUtils_SortObject(sortme); + + /* check sorting */ + current_element = sortme->child->next; + for (i = 1; (i < 26) && (current_element != NULL) && (current_element->prev != NULL); i++) + { + TEST_ASSERT_TRUE(current_element->string[0] >= current_element->prev->string[0]); + current_element = current_element->next; + } + + cJSON_Delete(sortme); +} + +static void merge_tests(void) +{ + size_t i = 0; + char *patchtext = NULL; + char *after = NULL; + + /* Merge tests: */ + printf("JSON Merge Patch tests\n"); + for (i = 0; i < 15; i++) + { + cJSON *object_to_be_merged = cJSON_Parse(merges[i][0]); + cJSON *patch = cJSON_Parse(merges[i][1]); + patchtext = cJSON_PrintUnformatted(patch); + object_to_be_merged = cJSONUtils_MergePatch(object_to_be_merged, patch); + after = cJSON_PrintUnformatted(object_to_be_merged); + TEST_ASSERT_EQUAL_STRING(merges[i][2], after); + + free(patchtext); + free(after); + cJSON_Delete(object_to_be_merged); + cJSON_Delete(patch); + } +} + +static void generate_merge_tests(void) +{ + size_t i = 0; + char *patchedtext = NULL; + + /* Generate Merge tests: */ + for (i = 0; i < 15; i++) + { + cJSON *from = cJSON_Parse(merges[i][0]); + cJSON *to = cJSON_Parse(merges[i][2]); + cJSON *patch = cJSONUtils_GenerateMergePatch(from,to); + from = cJSONUtils_MergePatch(from,patch); + patchedtext = cJSON_PrintUnformatted(from); + TEST_ASSERT_EQUAL_STRING(merges[i][2], patchedtext); + + cJSON_Delete(from); + cJSON_Delete(to); + cJSON_Delete(patch); + free(patchedtext); + } +} + +int main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(json_pointer_tests); + RUN_TEST(misc_tests); + RUN_TEST(sort_tests); + RUN_TEST(merge_tests); + RUN_TEST(generate_merge_tests); + + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/parse_array.c b/components/spotify/cspot/bell/cJSON/tests/parse_array.c new file mode 100644 index 00000000..d013f224 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/parse_array.c @@ -0,0 +1,167 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static cJSON item[1]; + +static void assert_is_array(cJSON *array_item) +{ + TEST_ASSERT_NOT_NULL_MESSAGE(array_item, "Item is NULL."); + + assert_not_in_list(array_item); + assert_has_type(array_item, cJSON_Array); + assert_has_no_reference(array_item); + assert_has_no_const_string(array_item); + assert_has_no_valuestring(array_item); + assert_has_no_string(array_item); +} + +static void assert_not_array(const char *json) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + buffer.content = (const unsigned char*)json; + buffer.length = strlen(json) + sizeof(""); + buffer.hooks = global_hooks; + + TEST_ASSERT_FALSE(parse_array(item, &buffer)); + assert_is_invalid(item); +} + +static void assert_parse_array(const char *json) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + buffer.content = (const unsigned char*)json; + buffer.length = strlen(json) + sizeof(""); + buffer.hooks = global_hooks; + + TEST_ASSERT_TRUE(parse_array(item, &buffer)); + assert_is_array(item); +} + +static void parse_array_should_parse_empty_arrays(void) +{ + assert_parse_array("[]"); + assert_has_no_child(item); + + assert_parse_array("[\n\t]"); + assert_has_no_child(item); +} + + +static void parse_array_should_parse_arrays_with_one_element(void) +{ + + assert_parse_array("[1]"); + assert_has_child(item); + assert_has_type(item->child, cJSON_Number); + reset(item); + + assert_parse_array("[\"hello!\"]"); + assert_has_child(item); + assert_has_type(item->child, cJSON_String); + TEST_ASSERT_EQUAL_STRING("hello!", item->child->valuestring); + reset(item); + + assert_parse_array("[[]]"); + assert_has_child(item); + TEST_ASSERT_NOT_NULL(item->child); + assert_has_type(item->child, cJSON_Array); + assert_has_no_child(item->child); + reset(item); + + assert_parse_array("[null]"); + assert_has_child(item); + assert_has_type(item->child, cJSON_NULL); + reset(item); +} + +static void parse_array_should_parse_arrays_with_multiple_elements(void) +{ + assert_parse_array("[1\t,\n2, 3]"); + assert_has_child(item); + TEST_ASSERT_NOT_NULL(item->child->next); + TEST_ASSERT_NOT_NULL(item->child->next->next); + TEST_ASSERT_NULL(item->child->next->next->next); + assert_has_type(item->child, cJSON_Number); + assert_has_type(item->child->next, cJSON_Number); + assert_has_type(item->child->next->next, cJSON_Number); + reset(item); + + { + size_t i = 0; + cJSON *node = NULL; + int expected_types[7] = + { + cJSON_Number, + cJSON_NULL, + cJSON_True, + cJSON_False, + cJSON_Array, + cJSON_String, + cJSON_Object + }; + assert_parse_array("[1, null, true, false, [], \"hello\", {}]"); + + node = item->child; + for ( + i = 0; + (i < (sizeof(expected_types)/sizeof(int))) + && (node != NULL); + (void)i++, node = node->next) + { + TEST_ASSERT_BITS(0xFF, expected_types[i], node->type); + } + TEST_ASSERT_EQUAL_INT(i, 7); + reset(item); + } +} + +static void parse_array_should_not_parse_non_arrays(void) +{ + assert_not_array(""); + assert_not_array("["); + assert_not_array("]"); + assert_not_array("{\"hello\":[]}"); + assert_not_array("42"); + assert_not_array("3.14"); + assert_not_array("\"[]hello world!\n\""); +} + +int CJSON_CDECL main(void) +{ + /* initialize cJSON item */ + memset(item, 0, sizeof(cJSON)); + + UNITY_BEGIN(); + RUN_TEST(parse_array_should_parse_empty_arrays); + RUN_TEST(parse_array_should_parse_arrays_with_one_element); + RUN_TEST(parse_array_should_parse_arrays_with_multiple_elements); + RUN_TEST(parse_array_should_not_parse_non_arrays); + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/parse_examples.c b/components/spotify/cspot/bell/cJSON/tests/parse_examples.c new file mode 100644 index 00000000..95a09590 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/parse_examples.c @@ -0,0 +1,271 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static cJSON *parse_file(const char *filename) +{ + cJSON *parsed = NULL; + char *content = read_file(filename); + + parsed = cJSON_Parse(content); + + if (content != NULL) + { + free(content); + } + + return parsed; +} + +static void do_test(const char *test_name) +{ + char *expected = NULL; + char *actual = NULL; + cJSON *tree = NULL; + + size_t test_name_length = 0; + /* path of the test input */ + char *test_path = NULL; + /* path of the expected output */ + char *expected_path = NULL; + + test_name_length = strlen(test_name); + + /* allocate file paths */ +#define TEST_DIR_PATH "inputs/" + test_path = (char*)malloc(sizeof(TEST_DIR_PATH) + test_name_length); + TEST_ASSERT_NOT_NULL_MESSAGE(test_path, "Failed to allocate test_path buffer."); + expected_path = (char*)malloc(sizeof(TEST_DIR_PATH) + test_name_length + sizeof(".expected")); + TEST_ASSERT_NOT_NULL_MESSAGE(expected_path, "Failed to allocate expected_path buffer."); + + /* create file paths */ + sprintf(test_path, TEST_DIR_PATH"%s", test_name); + sprintf(expected_path, TEST_DIR_PATH"%s.expected", test_name); + + /* read expected output */ + expected = read_file(expected_path); + TEST_ASSERT_NOT_NULL_MESSAGE(expected, "Failed to read expected output."); + + /* read and parse test */ + tree = parse_file(test_path); + TEST_ASSERT_NOT_NULL_MESSAGE(tree, "Failed to read of parse test."); + + /* print the parsed tree */ + actual = cJSON_Print(tree); + TEST_ASSERT_NOT_NULL_MESSAGE(actual, "Failed to print tree back to JSON."); + + + TEST_ASSERT_EQUAL_STRING(expected, actual); + + /* cleanup resources */ + if (expected != NULL) + { + free(expected); + } + if (tree != NULL) + { + cJSON_Delete(tree); + } + if (actual != NULL) + { + free(actual); + } + if (test_path != NULL) + { + free(test_path); + } + if (expected_path != NULL) + { + free(expected_path); + } +} + +static void file_test1_should_be_parsed_and_printed(void) +{ + do_test("test1"); +} + +static void file_test2_should_be_parsed_and_printed(void) +{ + do_test("test2"); +} + +static void file_test3_should_be_parsed_and_printed(void) +{ + do_test("test3"); +} + +static void file_test4_should_be_parsed_and_printed(void) +{ + do_test("test4"); +} + +static void file_test5_should_be_parsed_and_printed(void) +{ + do_test("test5"); +} + +static void file_test6_should_not_be_parsed(void) +{ + char *test6 = NULL; + cJSON *tree = NULL; + + test6 = read_file("inputs/test6"); + TEST_ASSERT_NOT_NULL_MESSAGE(test6, "Failed to read test6 data."); + + tree = cJSON_Parse(test6); + TEST_ASSERT_NULL_MESSAGE(tree, "Should fail to parse what is not JSON."); + + TEST_ASSERT_EQUAL_PTR_MESSAGE(test6, cJSON_GetErrorPtr(), "Error pointer is incorrect."); + + if (test6 != NULL) + { + free(test6); + } + if (tree != NULL) + { + cJSON_Delete(tree); + } +} + +static void file_test7_should_be_parsed_and_printed(void) +{ + do_test("test7"); +} + +static void file_test8_should_be_parsed_and_printed(void) +{ + do_test("test8"); +} + +static void file_test9_should_be_parsed_and_printed(void) +{ + do_test("test9"); +} + +static void file_test10_should_be_parsed_and_printed(void) +{ + do_test("test10"); +} + +static void file_test11_should_be_parsed_and_printed(void) +{ + do_test("test11"); +} + +static void test12_should_not_be_parsed(void) +{ + const char *test12 = "{ \"name\": "; + cJSON *tree = NULL; + + tree = cJSON_Parse(test12); + TEST_ASSERT_NULL_MESSAGE(tree, "Should fail to parse incomplete JSON."); + + TEST_ASSERT_EQUAL_PTR_MESSAGE(test12 + strlen(test12), cJSON_GetErrorPtr(), "Error pointer is incorrect."); + + if (tree != NULL) + { + cJSON_Delete(tree); + } +} + +static void test13_should_be_parsed_without_null_termination(void) +{ + cJSON *tree = NULL; + const char test_13[] = "{" \ + "\"Image\":{" \ + "\"Width\":800," \ + "\"Height\":600," \ + "\"Title\":\"Viewfrom15thFloor\"," \ + "\"Thumbnail\":{" \ + "\"Url\":\"http:/*www.example.com/image/481989943\"," \ + "\"Height\":125," \ + "\"Width\":\"100\"" \ + "}," \ + "\"IDs\":[116,943,234,38793]" \ + "}" \ + "}"; + + char test_13_wo_null[sizeof(test_13) - 1]; + memcpy(test_13_wo_null, test_13, sizeof(test_13) - 1); + + tree = cJSON_ParseWithLength(test_13_wo_null, sizeof(test_13) - 1); + TEST_ASSERT_NOT_NULL_MESSAGE(tree, "Failed to parse valid json."); + + if (tree != NULL) + { + cJSON_Delete(tree); + } +} + +static void test14_should_not_be_parsed(void) +{ + cJSON *tree = NULL; + const char test_14[] = "{" \ + "\"Image\":{" \ + "\"Width\":800," \ + "\"Height\":600," \ + "\"Title\":\"Viewfrom15thFloor\"," \ + "\"Thumbnail\":{" \ + "\"Url\":\"http:/*www.example.com/image/481989943\"," \ + "\"Height\":125," \ + "\"Width\":\"100\"" \ + "}," \ + "\"IDs\":[116,943,234,38793]" \ + "}" \ + "}"; + + tree = cJSON_ParseWithLength(test_14, sizeof(test_14) - 2); + TEST_ASSERT_NULL_MESSAGE(tree, "Should not continue after buffer_length is reached."); + + if (tree != NULL) + { + cJSON_Delete(tree); + } +} + +int CJSON_CDECL main(void) +{ + UNITY_BEGIN(); + RUN_TEST(file_test1_should_be_parsed_and_printed); + RUN_TEST(file_test2_should_be_parsed_and_printed); + RUN_TEST(file_test3_should_be_parsed_and_printed); + RUN_TEST(file_test4_should_be_parsed_and_printed); + RUN_TEST(file_test5_should_be_parsed_and_printed); + RUN_TEST(file_test6_should_not_be_parsed); + RUN_TEST(file_test7_should_be_parsed_and_printed); + RUN_TEST(file_test8_should_be_parsed_and_printed); + RUN_TEST(file_test9_should_be_parsed_and_printed); + RUN_TEST(file_test10_should_be_parsed_and_printed); + RUN_TEST(file_test11_should_be_parsed_and_printed); + RUN_TEST(test12_should_not_be_parsed); + RUN_TEST(test13_should_be_parsed_without_null_termination); + RUN_TEST(test14_should_not_be_parsed); + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/parse_hex4.c b/components/spotify/cspot/bell/cJSON/tests/parse_hex4.c new file mode 100644 index 00000000..83cbe658 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/parse_hex4.c @@ -0,0 +1,73 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static void parse_hex4_should_parse_all_combinations(void) +{ + unsigned int number = 0; + unsigned char digits_lower[6]; + unsigned char digits_upper[6]; + /* test all combinations */ + for (number = 0; number <= 0xFFFF; number++) + { + TEST_ASSERT_EQUAL_INT_MESSAGE(4, sprintf((char*)digits_lower, "%.4x", number), "sprintf failed."); + TEST_ASSERT_EQUAL_INT_MESSAGE(4, sprintf((char*)digits_upper, "%.4X", number), "sprintf failed."); + + TEST_ASSERT_EQUAL_INT_MESSAGE(number, parse_hex4(digits_lower), "Failed to parse lowercase digits."); + TEST_ASSERT_EQUAL_INT_MESSAGE(number, parse_hex4(digits_upper), "Failed to parse uppercase digits."); + } +} + +static void parse_hex4_should_parse_mixed_case(void) +{ + TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"beef")); + TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"beeF")); + TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"beEf")); + TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"beEF")); + TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"bEef")); + TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"bEeF")); + TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"bEEf")); + TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"bEEF")); + TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"Beef")); + TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BeeF")); + TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BeEf")); + TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BeEF")); + TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BEef")); + TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BEeF")); + TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BEEf")); + TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BEEF")); +} + +int CJSON_CDECL main(void) +{ + UNITY_BEGIN(); + RUN_TEST(parse_hex4_should_parse_all_combinations); + RUN_TEST(parse_hex4_should_parse_mixed_case); + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/parse_number.c b/components/spotify/cspot/bell/cJSON/tests/parse_number.c new file mode 100644 index 00000000..4cb72ec2 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/parse_number.c @@ -0,0 +1,110 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static cJSON item[1]; + +static void assert_is_number(cJSON *number_item) +{ + TEST_ASSERT_NOT_NULL_MESSAGE(number_item, "Item is NULL."); + + assert_not_in_list(number_item); + assert_has_no_child(number_item); + assert_has_type(number_item, cJSON_Number); + assert_has_no_reference(number_item); + assert_has_no_const_string(number_item); + assert_has_no_valuestring(number_item); + assert_has_no_string(number_item); +} + +static void assert_parse_number(const char *string, int integer, double real) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + buffer.content = (const unsigned char*)string; + buffer.length = strlen(string) + sizeof(""); + + TEST_ASSERT_TRUE(parse_number(item, &buffer)); + assert_is_number(item); + TEST_ASSERT_EQUAL_INT(integer, item->valueint); + TEST_ASSERT_EQUAL_DOUBLE(real, item->valuedouble); +} + +static void parse_number_should_parse_zero(void) +{ + assert_parse_number("0", 0, 0); + assert_parse_number("0.0", 0, 0.0); + assert_parse_number("-0", 0, -0.0); +} + +static void parse_number_should_parse_negative_integers(void) +{ + assert_parse_number("-1", -1, -1); + assert_parse_number("-32768", -32768, -32768.0); + assert_parse_number("-2147483648", (int)-2147483648.0, -2147483648.0); +} + +static void parse_number_should_parse_positive_integers(void) +{ + assert_parse_number("1", 1, 1); + assert_parse_number("32767", 32767, 32767.0); + assert_parse_number("2147483647", (int)2147483647.0, 2147483647.0); +} + +static void parse_number_should_parse_positive_reals(void) +{ + assert_parse_number("0.001", 0, 0.001); + assert_parse_number("10e-10", 0, 10e-10); + assert_parse_number("10E-10", 0, 10e-10); + assert_parse_number("10e10", INT_MAX, 10e10); + assert_parse_number("123e+127", INT_MAX, 123e127); + assert_parse_number("123e-128", 0, 123e-128); +} + +static void parse_number_should_parse_negative_reals(void) +{ + assert_parse_number("-0.001", 0, -0.001); + assert_parse_number("-10e-10", 0, -10e-10); + assert_parse_number("-10E-10", 0, -10e-10); + assert_parse_number("-10e20", INT_MIN, -10e20); + assert_parse_number("-123e+127", INT_MIN, -123e127); + assert_parse_number("-123e-128", 0, -123e-128); +} + +int CJSON_CDECL main(void) +{ + /* initialize cJSON item */ + memset(item, 0, sizeof(cJSON)); + UNITY_BEGIN(); + RUN_TEST(parse_number_should_parse_zero); + RUN_TEST(parse_number_should_parse_negative_integers); + RUN_TEST(parse_number_should_parse_positive_integers); + RUN_TEST(parse_number_should_parse_positive_reals); + RUN_TEST(parse_number_should_parse_negative_reals); + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/parse_object.c b/components/spotify/cspot/bell/cJSON/tests/parse_object.c new file mode 100644 index 00000000..5f8e7cf1 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/parse_object.c @@ -0,0 +1,176 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static cJSON item[1]; + +static void assert_is_object(cJSON *object_item) +{ + TEST_ASSERT_NOT_NULL_MESSAGE(object_item, "Item is NULL."); + + assert_not_in_list(object_item); + assert_has_type(object_item, cJSON_Object); + assert_has_no_reference(object_item); + assert_has_no_const_string(object_item); + assert_has_no_valuestring(object_item); + assert_has_no_string(object_item); +} + +static void assert_is_child(cJSON *child_item, const char *name, int type) +{ + TEST_ASSERT_NOT_NULL_MESSAGE(child_item, "Child item is NULL."); + TEST_ASSERT_NOT_NULL_MESSAGE(child_item->string, "Child item doesn't have a name."); + TEST_ASSERT_EQUAL_STRING_MESSAGE(name, child_item->string, "Child item has the wrong name."); + TEST_ASSERT_BITS(0xFF, type, child_item->type); +} + +static void assert_not_object(const char *json) +{ + parse_buffer parsebuffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + parsebuffer.content = (const unsigned char*)json; + parsebuffer.length = strlen(json) + sizeof(""); + parsebuffer.hooks = global_hooks; + + TEST_ASSERT_FALSE(parse_object(item, &parsebuffer)); + assert_is_invalid(item); + reset(item); +} + +static void assert_parse_object(const char *json) +{ + parse_buffer parsebuffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + parsebuffer.content = (const unsigned char*)json; + parsebuffer.length = strlen(json) + sizeof(""); + parsebuffer.hooks = global_hooks; + + TEST_ASSERT_TRUE(parse_object(item, &parsebuffer)); + assert_is_object(item); +} + +static void parse_object_should_parse_empty_objects(void) +{ + assert_parse_object("{}"); + assert_has_no_child(item); + reset(item); + + assert_parse_object("{\n\t}"); + assert_has_no_child(item); + reset(item); +} + +static void parse_object_should_parse_objects_with_one_element(void) +{ + + assert_parse_object("{\"one\":1}"); + assert_is_child(item->child, "one", cJSON_Number); + reset(item); + + assert_parse_object("{\"hello\":\"world!\"}"); + assert_is_child(item->child, "hello", cJSON_String); + reset(item); + + assert_parse_object("{\"array\":[]}"); + assert_is_child(item->child, "array", cJSON_Array); + reset(item); + + assert_parse_object("{\"null\":null}"); + assert_is_child(item->child, "null", cJSON_NULL); + reset(item); +} + +static void parse_object_should_parse_objects_with_multiple_elements(void) +{ + assert_parse_object("{\"one\":1\t,\t\"two\"\n:2, \"three\":3}"); + assert_is_child(item->child, "one", cJSON_Number); + assert_is_child(item->child->next, "two", cJSON_Number); + assert_is_child(item->child->next->next, "three", cJSON_Number); + reset(item); + + { + size_t i = 0; + cJSON *node = NULL; + int expected_types[7] = + { + cJSON_Number, + cJSON_NULL, + cJSON_True, + cJSON_False, + cJSON_Array, + cJSON_String, + cJSON_Object + }; + const char *expected_names[7] = + { + "one", + "NULL", + "TRUE", + "FALSE", + "array", + "world", + "object" + }; + assert_parse_object("{\"one\":1, \"NULL\":null, \"TRUE\":true, \"FALSE\":false, \"array\":[], \"world\":\"hello\", \"object\":{}}"); + + node = item->child; + for ( + i = 0; + (i < (sizeof(expected_types)/sizeof(int))) + && (node != NULL); + (void)i++, node = node->next) + { + assert_is_child(node, expected_names[i], expected_types[i]); + } + TEST_ASSERT_EQUAL_INT(i, 7); + reset(item); + } +} + +static void parse_object_should_not_parse_non_objects(void) +{ + assert_not_object(""); + assert_not_object("{"); + assert_not_object("}"); + assert_not_object("[\"hello\",{}]"); + assert_not_object("42"); + assert_not_object("3.14"); + assert_not_object("\"{}hello world!\n\""); +} + +int CJSON_CDECL main(void) +{ + /* initialize cJSON item */ + memset(item, 0, sizeof(cJSON)); + + UNITY_BEGIN(); + RUN_TEST(parse_object_should_parse_empty_objects); + RUN_TEST(parse_object_should_not_parse_non_objects); + RUN_TEST(parse_object_should_parse_objects_with_multiple_elements); + RUN_TEST(parse_object_should_parse_objects_with_one_element); + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/parse_string.c b/components/spotify/cspot/bell/cJSON/tests/parse_string.c new file mode 100644 index 00000000..ce1c138e --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/parse_string.c @@ -0,0 +1,135 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static cJSON item[1]; + +static void assert_is_string(cJSON *string_item) +{ + TEST_ASSERT_NOT_NULL_MESSAGE(string_item, "Item is NULL."); + + assert_not_in_list(string_item); + assert_has_no_child(string_item); + assert_has_type(string_item, cJSON_String); + assert_has_no_reference(string_item); + assert_has_no_const_string(string_item); + assert_has_valuestring(string_item); + assert_has_no_string(string_item); +} + +static void assert_parse_string(const char *string, const char *expected) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + buffer.content = (const unsigned char*)string; + buffer.length = strlen(string) + sizeof(""); + buffer.hooks = global_hooks; + + TEST_ASSERT_TRUE_MESSAGE(parse_string(item, &buffer), "Couldn't parse string."); + assert_is_string(item); + TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, item->valuestring, "The parsed result isn't as expected."); + global_hooks.deallocate(item->valuestring); + item->valuestring = NULL; +} + +static void assert_not_parse_string(const char * const string) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + buffer.content = (const unsigned char*)string; + buffer.length = strlen(string) + sizeof(""); + buffer.hooks = global_hooks; + + TEST_ASSERT_FALSE_MESSAGE(parse_string(item, &buffer), "Malformed string should not be accepted."); + assert_is_invalid(item); +} + + + +static void parse_string_should_parse_strings(void) +{ + assert_parse_string("\"\"", ""); + assert_parse_string( + "\" !\\\"#$%&'()*+,-./\\/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_'abcdefghijklmnopqrstuvwxyz{|}~\"", + " !\"#$%&'()*+,-.//0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_'abcdefghijklmnopqrstuvwxyz{|}~"); + assert_parse_string( + "\"\\\"\\\\\\/\\b\\f\\n\\r\\t\\u20AC\\u732b\"", + "\"\\/\b\f\n\r\t€猫"); + reset(item); + assert_parse_string("\"\b\f\n\r\t\"", "\b\f\n\r\t"); + reset(item); +} + +static void parse_string_should_parse_utf16_surrogate_pairs(void) +{ + assert_parse_string("\"\\uD83D\\udc31\"", "ðŸ±"); + reset(item); +} + +static void parse_string_should_not_parse_non_strings(void) +{ + assert_not_parse_string("this\" is not a string\""); + reset(item); + assert_not_parse_string(""); + reset(item); +} + +static void parse_string_should_not_parse_invalid_backslash(void) +{ + assert_not_parse_string("Abcdef\\123"); + reset(item); + assert_not_parse_string("Abcdef\\e23"); + reset(item); +} + +static void parse_string_should_not_overflow_with_closing_backslash(void) +{ + assert_not_parse_string("\"000000000000000000\\"); + reset(item); +} + +static void parse_string_should_parse_bug_94(void) +{ + const char string[] = "\"~!@\\\\#$%^&*()\\\\\\\\-\\\\+{}[]:\\\\;\\\\\\\"\\\\<\\\\>?/.,DC=ad,DC=com\""; + assert_parse_string(string, "~!@\\#$%^&*()\\\\-\\+{}[]:\\;\\\"\\<\\>?/.,DC=ad,DC=com"); + reset(item); +} + +int CJSON_CDECL main(void) +{ + /* initialize cJSON item and error pointer */ + memset(item, 0, sizeof(cJSON)); + + UNITY_BEGIN(); + RUN_TEST(parse_string_should_parse_strings); + RUN_TEST(parse_string_should_parse_utf16_surrogate_pairs); + RUN_TEST(parse_string_should_not_parse_non_strings); + RUN_TEST(parse_string_should_not_parse_invalid_backslash); + RUN_TEST(parse_string_should_parse_bug_94); + RUN_TEST(parse_string_should_not_overflow_with_closing_backslash); + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/parse_value.c b/components/spotify/cspot/bell/cJSON/tests/parse_value.c new file mode 100644 index 00000000..6b6b0955 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/parse_value.c @@ -0,0 +1,112 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static cJSON item[1]; + +static void assert_is_value(cJSON *value_item, int type) +{ + TEST_ASSERT_NOT_NULL_MESSAGE(value_item, "Item is NULL."); + + assert_not_in_list(value_item); + assert_has_type(value_item, type); + assert_has_no_reference(value_item); + assert_has_no_const_string(value_item); + assert_has_no_string(value_item); +} + +static void assert_parse_value(const char *string, int type) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + buffer.content = (const unsigned char*) string; + buffer.length = strlen(string) + sizeof(""); + buffer.hooks = global_hooks; + + TEST_ASSERT_TRUE(parse_value(item, &buffer)); + assert_is_value(item, type); +} + +static void parse_value_should_parse_null(void) +{ + assert_parse_value("null", cJSON_NULL); + reset(item); +} + +static void parse_value_should_parse_true(void) +{ + assert_parse_value("true", cJSON_True); + reset(item); +} + +static void parse_value_should_parse_false(void) +{ + assert_parse_value("false", cJSON_False); + reset(item); +} + +static void parse_value_should_parse_number(void) +{ + assert_parse_value("1.5", cJSON_Number); + reset(item); +} + +static void parse_value_should_parse_string(void) +{ + assert_parse_value("\"\"", cJSON_String); + reset(item); + assert_parse_value("\"hello\"", cJSON_String); + reset(item); +} + +static void parse_value_should_parse_array(void) +{ + assert_parse_value("[]", cJSON_Array); + reset(item); +} + +static void parse_value_should_parse_object(void) +{ + assert_parse_value("{}", cJSON_Object); + reset(item); +} + +int CJSON_CDECL main(void) +{ + /* initialize cJSON item */ + memset(item, 0, sizeof(cJSON)); + UNITY_BEGIN(); + RUN_TEST(parse_value_should_parse_null); + RUN_TEST(parse_value_should_parse_true); + RUN_TEST(parse_value_should_parse_false); + RUN_TEST(parse_value_should_parse_number); + RUN_TEST(parse_value_should_parse_string); + RUN_TEST(parse_value_should_parse_array); + RUN_TEST(parse_value_should_parse_object); + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/parse_with_opts.c b/components/spotify/cspot/bell/cJSON/tests/parse_with_opts.c new file mode 100644 index 00000000..e390b92d --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/parse_with_opts.c @@ -0,0 +1,112 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static void parse_with_opts_should_handle_null(void) +{ + const char *error_pointer = NULL; + cJSON *item = NULL; + TEST_ASSERT_NULL_MESSAGE(cJSON_ParseWithOpts(NULL, &error_pointer, false), "Failed to handle NULL input."); + item = cJSON_ParseWithOpts("{}", NULL, false); + TEST_ASSERT_NOT_NULL_MESSAGE(item, "Failed to handle NULL error pointer."); + cJSON_Delete(item); + TEST_ASSERT_NULL_MESSAGE(cJSON_ParseWithOpts(NULL, NULL, false), "Failed to handle both NULL."); + TEST_ASSERT_NULL_MESSAGE(cJSON_ParseWithOpts("{", NULL, false), "Failed to handle NULL error pointer with parse error."); +} + +static void parse_with_opts_should_handle_empty_strings(void) +{ + const char empty_string[] = ""; + const char *error_pointer = NULL; + + TEST_ASSERT_NULL(cJSON_ParseWithOpts(empty_string, NULL, false)); + TEST_ASSERT_EQUAL_PTR(empty_string, cJSON_GetErrorPtr()); + + TEST_ASSERT_NULL(cJSON_ParseWithOpts(empty_string, &error_pointer, false)); + TEST_ASSERT_EQUAL_PTR(empty_string, error_pointer); + TEST_ASSERT_EQUAL_PTR(empty_string, cJSON_GetErrorPtr()); +} + +static void parse_with_opts_should_handle_incomplete_json(void) +{ + const char json[] = "{ \"name\": "; + const char *parse_end = NULL; + + TEST_ASSERT_NULL(cJSON_ParseWithOpts(json, &parse_end, false)); + TEST_ASSERT_EQUAL_PTR(json + strlen(json), parse_end); + TEST_ASSERT_EQUAL_PTR(json + strlen(json), cJSON_GetErrorPtr()); +} + +static void parse_with_opts_should_require_null_if_requested(void) +{ + cJSON *item = cJSON_ParseWithOpts("{}", NULL, true); + TEST_ASSERT_NOT_NULL(item); + cJSON_Delete(item); + item = cJSON_ParseWithOpts("{} \n", NULL, true); + TEST_ASSERT_NOT_NULL(item); + cJSON_Delete(item); + TEST_ASSERT_NULL(cJSON_ParseWithOpts("{}x", NULL, true)); +} + +static void parse_with_opts_should_return_parse_end(void) +{ + const char json[] = "[] empty array XD"; + const char *parse_end = NULL; + + cJSON *item = cJSON_ParseWithOpts(json, &parse_end, false); + TEST_ASSERT_NOT_NULL(item); + TEST_ASSERT_EQUAL_PTR(json + 2, parse_end); + cJSON_Delete(item); +} + +static void parse_with_opts_should_parse_utf8_bom(void) +{ + cJSON *with_bom = NULL; + cJSON *without_bom = NULL; + + with_bom = cJSON_ParseWithOpts("\xEF\xBB\xBF{}", NULL, true); + TEST_ASSERT_NOT_NULL(with_bom); + without_bom = cJSON_ParseWithOpts("{}", NULL, true); + TEST_ASSERT_NOT_NULL(with_bom); + + TEST_ASSERT_TRUE(cJSON_Compare(with_bom, without_bom, true)); + + cJSON_Delete(with_bom); + cJSON_Delete(without_bom); +} + +int CJSON_CDECL main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(parse_with_opts_should_handle_null); + RUN_TEST(parse_with_opts_should_handle_empty_strings); + RUN_TEST(parse_with_opts_should_handle_incomplete_json); + RUN_TEST(parse_with_opts_should_require_null_if_requested); + RUN_TEST(parse_with_opts_should_return_parse_end); + RUN_TEST(parse_with_opts_should_parse_utf8_bom); + + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/print_array.c b/components/spotify/cspot/bell/cJSON/tests/print_array.c new file mode 100644 index 00000000..7d40f2e2 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/print_array.c @@ -0,0 +1,100 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static void assert_print_array(const char * const expected, const char * const input) +{ + unsigned char printed_unformatted[1024]; + unsigned char printed_formatted[1024]; + + cJSON item[1]; + + printbuffer formatted_buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + printbuffer unformatted_buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + parse_buffer parsebuffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + parsebuffer.content = (const unsigned char*)input; + parsebuffer.length = strlen(input) + sizeof(""); + parsebuffer.hooks = global_hooks; + + /* buffer for formatted printing */ + formatted_buffer.buffer = printed_formatted; + formatted_buffer.length = sizeof(printed_formatted); + formatted_buffer.offset = 0; + formatted_buffer.noalloc = true; + formatted_buffer.hooks = global_hooks; + + /* buffer for unformatted printing */ + unformatted_buffer.buffer = printed_unformatted; + unformatted_buffer.length = sizeof(printed_unformatted); + unformatted_buffer.offset = 0; + unformatted_buffer.noalloc = true; + unformatted_buffer.hooks = global_hooks; + + memset(item, 0, sizeof(item)); + TEST_ASSERT_TRUE_MESSAGE(parse_array(item, &parsebuffer), "Failed to parse array."); + + unformatted_buffer.format = false; + TEST_ASSERT_TRUE_MESSAGE(print_array(item, &unformatted_buffer), "Failed to print unformatted string."); + TEST_ASSERT_EQUAL_STRING_MESSAGE(input, printed_unformatted, "Unformatted array is not correct."); + + formatted_buffer.format = true; + TEST_ASSERT_TRUE_MESSAGE(print_array(item, &formatted_buffer), "Failed to print formatted string."); + TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed_formatted, "Formatted array is not correct."); + + reset(item); +} + +static void print_array_should_print_empty_arrays(void) +{ + assert_print_array("[]", "[]"); +} + +static void print_array_should_print_arrays_with_one_element(void) +{ + + assert_print_array("[1]", "[1]"); + assert_print_array("[\"hello!\"]", "[\"hello!\"]"); + assert_print_array("[[]]", "[[]]"); + assert_print_array("[null]", "[null]"); +} + +static void print_array_should_print_arrays_with_multiple_elements(void) +{ + assert_print_array("[1, 2, 3]", "[1,2,3]"); + assert_print_array("[1, null, true, false, [], \"hello\", {\n\t}]", "[1,null,true,false,[],\"hello\",{}]"); +} + +int CJSON_CDECL main(void) +{ + /* initialize cJSON item */ + UNITY_BEGIN(); + + RUN_TEST(print_array_should_print_empty_arrays); + RUN_TEST(print_array_should_print_arrays_with_one_element); + RUN_TEST(print_array_should_print_arrays_with_multiple_elements); + + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/print_number.c b/components/spotify/cspot/bell/cJSON/tests/print_number.c new file mode 100644 index 00000000..3fbf9cb6 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/print_number.c @@ -0,0 +1,125 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static void assert_print_number(const char *expected, double input) +{ + unsigned char printed[1024]; + unsigned char new_buffer[26]; + unsigned int i = 0; + cJSON item[1]; + printbuffer buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + buffer.buffer = printed; + buffer.length = sizeof(printed); + buffer.offset = 0; + buffer.noalloc = true; + buffer.hooks = global_hooks; + buffer.buffer = new_buffer; + + memset(item, 0, sizeof(item)); + memset(new_buffer, 0, sizeof(new_buffer)); + cJSON_SetNumberValue(item, input); + TEST_ASSERT_TRUE_MESSAGE(print_number(item, &buffer), "Failed to print number."); + + /* In MinGW or visual studio(before 2015),the exponten is represented using three digits,like:"1e-009","1e+017" + * remove extra "0" to output "1e-09" or "1e+17",which makes testcase PASS */ + for(i = 0;i 3 && new_buffer[i] =='0') + { + if((new_buffer[i-3] =='e' && new_buffer[i-2] == '-' && new_buffer[i] =='0') ||(new_buffer[i-2] =='e' && new_buffer[i-1] =='+')) + { + while(new_buffer[i] !='\0') + { + new_buffer[i] = new_buffer[i+1]; + i++; + } + } + } + } + TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, buffer.buffer, "Printed number is not as expected."); +} + +static void print_number_should_print_zero(void) +{ + assert_print_number("0", 0); +} + +static void print_number_should_print_negative_integers(void) +{ + assert_print_number("-1", -1.0); + assert_print_number("-32768", -32768.0); + assert_print_number("-2147483648", -2147483648.0); +} + +static void print_number_should_print_positive_integers(void) +{ + assert_print_number("1", 1.0); + assert_print_number("32767", 32767.0); + assert_print_number("2147483647", 2147483647.0); +} + +static void print_number_should_print_positive_reals(void) +{ + assert_print_number("0.123", 0.123); + assert_print_number("1e-09", 10e-10); + assert_print_number("1000000000000", 10e11); + assert_print_number("1.23e+129", 123e+127); + assert_print_number("1.23e-126", 123e-128); + assert_print_number("3.1415926535897931", 3.1415926535897931); +} + +static void print_number_should_print_negative_reals(void) +{ + assert_print_number("-0.0123", -0.0123); + assert_print_number("-1e-09", -10e-10); + assert_print_number("-1e+21", -10e20); + assert_print_number("-1.23e+129", -123e+127); + assert_print_number("-1.23e-126", -123e-128); +} + +static void print_number_should_print_non_number(void) +{ + TEST_IGNORE(); + /* FIXME: Cannot test this easily in C89! */ + /* assert_print_number("null", NaN); */ + /* assert_print_number("null", INFTY); */ + /* assert_print_number("null", -INFTY); */ +} + +int CJSON_CDECL main(void) +{ + /* initialize cJSON item */ + UNITY_BEGIN(); + + RUN_TEST(print_number_should_print_zero); + RUN_TEST(print_number_should_print_negative_integers); + RUN_TEST(print_number_should_print_positive_integers); + RUN_TEST(print_number_should_print_positive_reals); + RUN_TEST(print_number_should_print_negative_reals); + RUN_TEST(print_number_should_print_non_number); + + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/print_object.c b/components/spotify/cspot/bell/cJSON/tests/print_object.c new file mode 100644 index 00000000..3ed0bfed --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/print_object.c @@ -0,0 +1,101 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static void assert_print_object(const char * const expected, const char * const input) +{ + unsigned char printed_unformatted[1024]; + unsigned char printed_formatted[1024]; + + cJSON item[1]; + + printbuffer formatted_buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + printbuffer unformatted_buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + parse_buffer parsebuffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + + /* buffer for parsing */ + parsebuffer.content = (const unsigned char*)input; + parsebuffer.length = strlen(input) + sizeof(""); + parsebuffer.hooks = global_hooks; + + /* buffer for formatted printing */ + formatted_buffer.buffer = printed_formatted; + formatted_buffer.length = sizeof(printed_formatted); + formatted_buffer.offset = 0; + formatted_buffer.noalloc = true; + formatted_buffer.hooks = global_hooks; + + /* buffer for unformatted printing */ + unformatted_buffer.buffer = printed_unformatted; + unformatted_buffer.length = sizeof(printed_unformatted); + unformatted_buffer.offset = 0; + unformatted_buffer.noalloc = true; + unformatted_buffer.hooks = global_hooks; + + memset(item, 0, sizeof(item)); + TEST_ASSERT_TRUE_MESSAGE(parse_object(item, &parsebuffer), "Failed to parse object."); + + unformatted_buffer.format = false; + TEST_ASSERT_TRUE_MESSAGE(print_object(item, &unformatted_buffer), "Failed to print unformatted string."); + TEST_ASSERT_EQUAL_STRING_MESSAGE(input, printed_unformatted, "Unformatted object is not correct."); + + formatted_buffer.format = true; + TEST_ASSERT_TRUE_MESSAGE(print_object(item, &formatted_buffer), "Failed to print formatted string."); + TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed_formatted, "Formatted ojbect is not correct."); + + reset(item); +} + +static void print_object_should_print_empty_objects(void) +{ + assert_print_object("{\n}", "{}"); +} + +static void print_object_should_print_objects_with_one_element(void) +{ + + assert_print_object("{\n\t\"one\":\t1\n}", "{\"one\":1}"); + assert_print_object("{\n\t\"hello\":\t\"world!\"\n}", "{\"hello\":\"world!\"}"); + assert_print_object("{\n\t\"array\":\t[]\n}", "{\"array\":[]}"); + assert_print_object("{\n\t\"null\":\tnull\n}", "{\"null\":null}"); +} + +static void print_object_should_print_objects_with_multiple_elements(void) +{ + assert_print_object("{\n\t\"one\":\t1,\n\t\"two\":\t2,\n\t\"three\":\t3\n}", "{\"one\":1,\"two\":2,\"three\":3}"); + assert_print_object("{\n\t\"one\":\t1,\n\t\"NULL\":\tnull,\n\t\"TRUE\":\ttrue,\n\t\"FALSE\":\tfalse,\n\t\"array\":\t[],\n\t\"world\":\t\"hello\",\n\t\"object\":\t{\n\t}\n}", "{\"one\":1,\"NULL\":null,\"TRUE\":true,\"FALSE\":false,\"array\":[],\"world\":\"hello\",\"object\":{}}"); +} + +int CJSON_CDECL main(void) +{ + /* initialize cJSON item */ + UNITY_BEGIN(); + + RUN_TEST(print_object_should_print_empty_objects); + RUN_TEST(print_object_should_print_objects_with_one_element); + RUN_TEST(print_object_should_print_objects_with_multiple_elements); + + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/print_string.c b/components/spotify/cspot/bell/cJSON/tests/print_string.c new file mode 100644 index 00000000..e6f5b92d --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/print_string.c @@ -0,0 +1,78 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static void assert_print_string(const char *expected, const char *input) +{ + unsigned char printed[1024]; + printbuffer buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + buffer.buffer = printed; + buffer.length = sizeof(printed); + buffer.offset = 0; + buffer.noalloc = true; + buffer.hooks = global_hooks; + + TEST_ASSERT_TRUE_MESSAGE(print_string_ptr((const unsigned char*)input, &buffer), "Failed to print string."); + TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed, "The printed string isn't as expected."); +} + +static void print_string_should_print_empty_strings(void) +{ + assert_print_string("\"\"", ""); + assert_print_string("\"\"", NULL); +} + +static void print_string_should_print_ascii(void) +{ + char ascii[0x7F]; + size_t i = 1; + + /* create ascii table */ + for (i = 1; i < 0x7F; i++) + { + ascii[i-1] = (char)i; + } + ascii[0x7F-1] = '\0'; + + assert_print_string("\"\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\"", + ascii); +} + +static void print_string_should_print_utf8(void) +{ + assert_print_string("\"ü猫慕\"", "ü猫慕"); +} + +int CJSON_CDECL main(void) +{ + /* initialize cJSON item */ + UNITY_BEGIN(); + + RUN_TEST(print_string_should_print_empty_strings); + RUN_TEST(print_string_should_print_ascii); + RUN_TEST(print_string_should_print_utf8); + + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/print_value.c b/components/spotify/cspot/bell/cJSON/tests/print_value.c new file mode 100644 index 00000000..b54db5b1 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/print_value.c @@ -0,0 +1,107 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static void assert_print_value(const char *input) +{ + unsigned char printed[1024]; + cJSON item[1]; + printbuffer buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + parse_buffer parsebuffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + buffer.buffer = printed; + buffer.length = sizeof(printed); + buffer.offset = 0; + buffer.noalloc = true; + buffer.hooks = global_hooks; + + parsebuffer.content = (const unsigned char*)input; + parsebuffer.length = strlen(input) + sizeof(""); + parsebuffer.hooks = global_hooks; + + memset(item, 0, sizeof(item)); + + TEST_ASSERT_TRUE_MESSAGE(parse_value(item, &parsebuffer), "Failed to parse value."); + + TEST_ASSERT_TRUE_MESSAGE(print_value(item, &buffer), "Failed to print value."); + TEST_ASSERT_EQUAL_STRING_MESSAGE(input, buffer.buffer, "Printed value is not as expected."); + + reset(item); +} + +static void print_value_should_print_null(void) +{ + assert_print_value("null"); +} + +static void print_value_should_print_true(void) +{ + assert_print_value("true"); +} + +static void print_value_should_print_false(void) +{ + assert_print_value("false"); +} + +static void print_value_should_print_number(void) +{ + assert_print_value("1.5"); +} + +static void print_value_should_print_string(void) +{ + assert_print_value("\"\""); + assert_print_value("\"hello\""); +} + +static void print_value_should_print_array(void) +{ + assert_print_value("[]"); +} + +static void print_value_should_print_object(void) +{ + assert_print_value("{}"); +} + +int CJSON_CDECL main(void) +{ + /* initialize cJSON item */ + UNITY_BEGIN(); + + RUN_TEST(print_value_should_print_null); + RUN_TEST(print_value_should_print_true); + RUN_TEST(print_value_should_print_false); + RUN_TEST(print_value_should_print_number); + RUN_TEST(print_value_should_print_string); + RUN_TEST(print_value_should_print_array); + RUN_TEST(print_value_should_print_object); + + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/readme_examples.c b/components/spotify/cspot/bell/cJSON/tests/readme_examples.c new file mode 100644 index 00000000..09850cd4 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/readme_examples.c @@ -0,0 +1,258 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static const char *json = "{\n\ +\t\"name\":\t\"Awesome 4K\",\n\ +\t\"resolutions\":\t[{\n\ +\t\t\t\"width\":\t1280,\n\ +\t\t\t\"height\":\t720\n\ +\t\t}, {\n\ +\t\t\t\"width\":\t1920,\n\ +\t\t\t\"height\":\t1080\n\ +\t\t}, {\n\ +\t\t\t\"width\":\t3840,\n\ +\t\t\t\"height\":\t2160\n\ +\t\t}]\n\ +}"; + +static char* create_monitor(void) +{ + const unsigned int resolution_numbers[3][2] = { + {1280, 720}, + {1920, 1080}, + {3840, 2160} + }; + char *string = NULL; + cJSON *name = NULL; + cJSON *resolutions = NULL; + cJSON *resolution = NULL; + cJSON *width = NULL; + cJSON *height = NULL; + size_t index = 0; + + cJSON *monitor = cJSON_CreateObject(); + if (monitor == NULL) + { + goto end; + } + + name = cJSON_CreateString("Awesome 4K"); + if (name == NULL) + { + goto end; + } + /* after creation was successful, immediately add it to the monitor, + * thereby transferring ownership of the pointer to it */ + cJSON_AddItemToObject(monitor, "name", name); + + resolutions = cJSON_CreateArray(); + if (resolutions == NULL) + { + goto end; + } + cJSON_AddItemToObject(monitor, "resolutions", resolutions); + + for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index) + { + resolution = cJSON_CreateObject(); + if (resolution == NULL) + { + goto end; + } + cJSON_AddItemToArray(resolutions, resolution); + + width = cJSON_CreateNumber(resolution_numbers[index][0]); + if (width == NULL) + { + goto end; + } + cJSON_AddItemToObject(resolution, "width", width); + + height = cJSON_CreateNumber(resolution_numbers[index][1]); + if (height == NULL) + { + goto end; + } + cJSON_AddItemToObject(resolution, "height", height); + } + + string = cJSON_Print(monitor); + if (string == NULL) + { + fprintf(stderr, "Failed to print monitor.\n"); + } + +end: + cJSON_Delete(monitor); + return string; +} + +static char *create_monitor_with_helpers(void) +{ + const unsigned int resolution_numbers[3][2] = { + {1280, 720}, + {1920, 1080}, + {3840, 2160} + }; + char *string = NULL; + cJSON *resolutions = NULL; + size_t index = 0; + + cJSON *monitor = cJSON_CreateObject(); + + if (cJSON_AddStringToObject(monitor, "name", "Awesome 4K") == NULL) + { + goto end; + } + + resolutions = cJSON_AddArrayToObject(monitor, "resolutions"); + if (resolutions == NULL) + { + goto end; + } + + for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index) + { + cJSON *resolution = cJSON_CreateObject(); + + if (cJSON_AddNumberToObject(resolution, "width", resolution_numbers[index][0]) == NULL) + { + goto end; + } + + if(cJSON_AddNumberToObject(resolution, "height", resolution_numbers[index][1]) == NULL) + { + goto end; + } + + cJSON_AddItemToArray(resolutions, resolution); + } + + string = cJSON_Print(monitor); + if (string == NULL) { + fprintf(stderr, "Failed to print monitor.\n"); + } + +end: + cJSON_Delete(monitor); + return string; +} + +/* return 1 if the monitor supports full hd, 0 otherwise */ +static int supports_full_hd(const char * const monitor) +{ + const cJSON *resolution = NULL; + const cJSON *resolutions = NULL; + const cJSON *name = NULL; + int status = 0; + cJSON *monitor_json = cJSON_Parse(monitor); + if (monitor_json == NULL) + { + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) + { + fprintf(stderr, "Error before: %s\n", error_ptr); + } + status = 0; + goto end; + } + + name = cJSON_GetObjectItemCaseSensitive(monitor_json, "name"); + if (cJSON_IsString(name) && (name->valuestring != NULL)) + { + printf("Checking monitor \"%s\"\n", name->valuestring); + } + + resolutions = cJSON_GetObjectItemCaseSensitive(monitor_json, "resolutions"); + cJSON_ArrayForEach(resolution, resolutions) + { + cJSON *width = cJSON_GetObjectItemCaseSensitive(resolution, "width"); + cJSON *height = cJSON_GetObjectItemCaseSensitive(resolution, "height"); + + if (!cJSON_IsNumber(width) || !cJSON_IsNumber(height)) + { + status = 0; + goto end; + } + + if (compare_double(width->valuedouble, 1920) && compare_double(height->valuedouble, 1080)) + { + status = 1; + goto end; + } + } + +end: + cJSON_Delete(monitor_json); + return status; +} + +static void create_monitor_should_create_a_monitor(void) +{ + char *monitor = create_monitor(); + + TEST_ASSERT_EQUAL_STRING(monitor, json); + + free(monitor); +} + +static void create_monitor_with_helpers_should_create_a_monitor(void) +{ + char *monitor = create_monitor_with_helpers(); + + TEST_ASSERT_EQUAL_STRING(json, monitor); + + free(monitor); +} + +static void supports_full_hd_should_check_for_full_hd_support(void) +{ + static const char *monitor_without_hd = "{\n\ +\t\t\"name\": \"lame monitor\",\n\ +\t\t\"resolutions\":\t[{\n\ +\t\t\t\"width\":\t640,\n\ +\t\t\t\"height\":\t480\n\ +\t\t}]\n\ +}"; + + TEST_ASSERT(supports_full_hd(json)); + TEST_ASSERT_FALSE(supports_full_hd(monitor_without_hd)); +} + +int CJSON_CDECL main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(create_monitor_should_create_a_monitor); + RUN_TEST(create_monitor_with_helpers_should_create_a_monitor); + RUN_TEST(supports_full_hd_should_check_for_full_hd_support); + + return UNITY_END(); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/.gitattributes b/components/spotify/cspot/bell/cJSON/tests/unity/.gitattributes new file mode 100644 index 00000000..ad952260 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/.gitattributes @@ -0,0 +1,30 @@ +* text=auto + +# These files are text and should be normalized (convert crlf to lf) +*.rb text +*.test text +*.c text +*.cpp text +*.h text +*.txt text +*.yml text +*.s79 text +*.bat text +*.xcl text +*.inc text +*.info text +*.md text +makefile text +rakefile text + + +#These files are binary and should not be normalized +*.doc binary +*.odt binary +*.pdf binary +*.ewd binary +*.eww binary +*.dni binary +*.wsdt binary +*.dbgdt binary +*.mac binary diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/.gitignore b/components/spotify/cspot/bell/cJSON/tests/unity/.gitignore new file mode 100644 index 00000000..a383c3cc --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/.gitignore @@ -0,0 +1,9 @@ +build/ +test/sandbox +.DS_Store +examples/example_1/test1.exe +examples/example_1/test2.exe +examples/example_2/all_tests.exe +examples/example_1/test1.out +examples/example_1/test2.out +examples/example_2/all_tests.out diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/.travis.yml b/components/spotify/cspot/bell/cJSON/tests/unity/.travis.yml new file mode 100644 index 00000000..bd165b1e --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/.travis.yml @@ -0,0 +1,29 @@ +language: c + +matrix: + include: + - os: osx + compiler: clang + osx_image: xcode7.3 + - os: linux + dist: trusty + compiler: gcc + +before_install: + - if [ "$TRAVIS_OS_NAME" == "osx" ]; then rvm install 2.1 && rvm use 2.1 && ruby -v; fi + - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get install --assume-yes --quiet gcc-multilib; fi +install: + - gem install rspec + - gem install rubocop +script: + - cd test && rake ci + - make -s + - make -s DEBUG=-m32 #32-bit architecture with 64-bit support + - make -s DEBUG=-m32 UNITY_SUPPORT_64= #32-bit build without 64-bit types + - make -s UNITY_INCLUDE_DOUBLE= # without double + - cd ../extras/fixture/test && rake ci + - make -s default noStdlibMalloc + - make -s C89 + - cd ../../../examples/example_1 && make -s ci + - cd ../example_2 && make -s ci + - cd ../example_3 && rake diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/README.md b/components/spotify/cspot/bell/cJSON/tests/unity/README.md new file mode 100644 index 00000000..ec73b4a1 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/README.md @@ -0,0 +1,231 @@ +Unity Test API +============== + +[![Unity Build Status](https://api.travis-ci.org/ThrowTheSwitch/Unity.png?branch=master)](https://travis-ci.org/ThrowTheSwitch/Unity) +__Copyright (c) 2007 - 2017 Unity Project by Mike Karlesky, Mark VanderVoord, and Greg Williams__ + +Running Tests +------------- + + RUN_TEST(func, linenum) + +Each Test is run within the macro `RUN_TEST`. This macro performs necessary setup before the test is called and handles cleanup and result tabulation afterwards. + +Ignoring Tests +-------------- + +There are times when a test is incomplete or not valid for some reason. At these times, TEST_IGNORE can be called. Control will immediately be returned to the caller of the test, and no failures will be returned. + + TEST_IGNORE() + +Ignore this test and return immediately + + TEST_IGNORE_MESSAGE (message) + +Ignore this test and return immediately. Output a message stating why the test was ignored. + +Aborting Tests +-------------- + +There are times when a test will contain an infinite loop on error conditions, or there may be reason to escape from the test early without executing the rest of the test. A pair of macros support this functionality in Unity. The first `TEST_PROTECT` sets up the feature, and handles emergency abort cases. `TEST_ABORT` can then be used at any time within the tests to return to the last `TEST_PROTECT` call. + + TEST_PROTECT() + +Setup and Catch macro + + TEST_ABORT() + +Abort Test macro + +Example: + + main() + { + if (TEST_PROTECT()) + { + MyTest(); + } + } + +If MyTest calls `TEST_ABORT`, program control will immediately return to `TEST_PROTECT` with a return value of zero. + + +Unity Assertion Summary +======================= + +Basic Validity Tests +-------------------- + + TEST_ASSERT_TRUE(condition) + +Evaluates whatever code is in condition and fails if it evaluates to false + + TEST_ASSERT_FALSE(condition) + +Evaluates whatever code is in condition and fails if it evaluates to true + + TEST_ASSERT(condition) + +Another way of calling `TEST_ASSERT_TRUE` + + TEST_ASSERT_UNLESS(condition) + +Another way of calling `TEST_ASSERT_FALSE` + + TEST_FAIL() + TEST_FAIL_MESSAGE(message) + +This test is automatically marked as a failure. The message is output stating why. + +Numerical Assertions: Integers +------------------------------ + + TEST_ASSERT_EQUAL_INT(expected, actual) + TEST_ASSERT_EQUAL_INT8(expected, actual) + TEST_ASSERT_EQUAL_INT16(expected, actual) + TEST_ASSERT_EQUAL_INT32(expected, actual) + TEST_ASSERT_EQUAL_INT64(expected, actual) + +Compare two integers for equality and display errors as signed integers. A cast will be performed +to your natural integer size so often this can just be used. When you need to specify the exact size, +like when comparing arrays, you can use a specific version: + + TEST_ASSERT_EQUAL_UINT(expected, actual) + TEST_ASSERT_EQUAL_UINT8(expected, actual) + TEST_ASSERT_EQUAL_UINT16(expected, actual) + TEST_ASSERT_EQUAL_UINT32(expected, actual) + TEST_ASSERT_EQUAL_UINT64(expected, actual) + +Compare two integers for equality and display errors as unsigned integers. Like INT, there are +variants for different sizes also. + + TEST_ASSERT_EQUAL_HEX(expected, actual) + TEST_ASSERT_EQUAL_HEX8(expected, actual) + TEST_ASSERT_EQUAL_HEX16(expected, actual) + TEST_ASSERT_EQUAL_HEX32(expected, actual) + TEST_ASSERT_EQUAL_HEX64(expected, actual) + +Compares two integers for equality and display errors as hexadecimal. Like the other integer comparisons, +you can specify the size... here the size will also effect how many nibbles are shown (for example, `HEX16` +will show 4 nibbles). + + TEST_ASSERT_EQUAL(expected, actual) + +Another way of calling TEST_ASSERT_EQUAL_INT + + TEST_ASSERT_INT_WITHIN(delta, expected, actual) + +Asserts that the actual value is within plus or minus delta of the expected value. This also comes in +size specific variants. + + + TEST_ASSERT_GREATER_THAN(threshold, actual) + +Asserts that the actual value is greater than the threshold. This also comes in size specific variants. + + + TEST_ASSERT_LESS_THAN(threshold, actual) + +Asserts that the actual value is less than the threshold. This also comes in size specific variants. + + +Arrays +------ + + _ARRAY + +You can append `_ARRAY` to any of these macros to make an array comparison of that type. Here you will +need to care a bit more about the actual size of the value being checked. You will also specify an +additional argument which is the number of elements to compare. For example: + + TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, elements) + + _EACH_EQUAL + +Another array comparison option is to check that EVERY element of an array is equal to a single expected +value. You do this by specifying the EACH_EQUAL macro. For example: + + TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, elements) + +Numerical Assertions: Bitwise +----------------------------- + + TEST_ASSERT_BITS(mask, expected, actual) + +Use an integer mask to specify which bits should be compared between two other integers. High bits in the mask are compared, low bits ignored. + + TEST_ASSERT_BITS_HIGH(mask, actual) + +Use an integer mask to specify which bits should be inspected to determine if they are all set high. High bits in the mask are compared, low bits ignored. + + TEST_ASSERT_BITS_LOW(mask, actual) + +Use an integer mask to specify which bits should be inspected to determine if they are all set low. High bits in the mask are compared, low bits ignored. + + TEST_ASSERT_BIT_HIGH(bit, actual) + +Test a single bit and verify that it is high. The bit is specified 0-31 for a 32-bit integer. + + TEST_ASSERT_BIT_LOW(bit, actual) + +Test a single bit and verify that it is low. The bit is specified 0-31 for a 32-bit integer. + +Numerical Assertions: Floats +---------------------------- + + TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual) + +Asserts that the actual value is within plus or minus delta of the expected value. + + TEST_ASSERT_EQUAL_FLOAT(expected, actual) + TEST_ASSERT_EQUAL_DOUBLE(expected, actual) + +Asserts that two floating point values are "equal" within a small % delta of the expected value. + +String Assertions +----------------- + + TEST_ASSERT_EQUAL_STRING(expected, actual) + +Compare two null-terminate strings. Fail if any character is different or if the lengths are different. + + TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len) + +Compare two strings. Fail if any character is different, stop comparing after len characters. + + TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, actual, message) + +Compare two null-terminate strings. Fail if any character is different or if the lengths are different. Output a custom message on failure. + + TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(expected, actual, len, message) + +Compare two strings. Fail if any character is different, stop comparing after len characters. Output a custom message on failure. + +Pointer Assertions +------------------ + +Most pointer operations can be performed by simply using the integer comparisons above. However, a couple of special cases are added for clarity. + + TEST_ASSERT_NULL(pointer) + +Fails if the pointer is not equal to NULL + + TEST_ASSERT_NOT_NULL(pointer) + +Fails if the pointer is equal to NULL + +Memory Assertions +----------------- + + TEST_ASSERT_EQUAL_MEMORY(expected, actual, len) + +Compare two blocks of memory. This is a good generic assertion for types that can't be coerced into acting like +standard types... but since it's a memory compare, you have to be careful that your data types are packed. + +_MESSAGE +-------- + +you can append _MESSAGE to any of the macros to make them take an additional argument. This argument +is a string that will be printed at the end of the failure strings. This is useful for specifying more +information about the problem. + diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/auto/colour_prompt.rb b/components/spotify/cspot/bell/cJSON/tests/unity/auto/colour_prompt.rb new file mode 100644 index 00000000..0f1dc4e0 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/auto/colour_prompt.rb @@ -0,0 +1,118 @@ +# ========================================== +# Unity Project - A Test Framework for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +if RUBY_PLATFORM =~ /(win|w)32$/ + begin + require 'Win32API' + rescue LoadError + puts 'ERROR! "Win32API" library not found' + puts '"Win32API" is required for colour on a windows machine' + puts ' try => "gem install Win32API" on the command line' + puts + end + # puts + # puts 'Windows Environment Detected...' + # puts 'Win32API Library Found.' + # puts +end + +class ColourCommandLine + def initialize + return unless RUBY_PLATFORM =~ /(win|w)32$/ + get_std_handle = Win32API.new('kernel32', 'GetStdHandle', ['L'], 'L') + @set_console_txt_attrb = + Win32API.new('kernel32', 'SetConsoleTextAttribute', %w(L N), 'I') + @hout = get_std_handle.call(-11) + end + + def change_to(new_colour) + if RUBY_PLATFORM =~ /(win|w)32$/ + @set_console_txt_attrb.call(@hout, win32_colour(new_colour)) + else + "\033[30;#{posix_colour(new_colour)};22m" + end + end + + def win32_colour(colour) + case colour + when :black then 0 + when :dark_blue then 1 + when :dark_green then 2 + when :dark_cyan then 3 + when :dark_red then 4 + when :dark_purple then 5 + when :dark_yellow, :narrative then 6 + when :default_white, :default, :dark_white then 7 + when :silver then 8 + when :blue then 9 + when :green, :success then 10 + when :cyan, :output then 11 + when :red, :failure then 12 + when :purple then 13 + when :yellow then 14 + when :white then 15 + else + 0 + end + end + + def posix_colour(colour) + # ANSI Escape Codes - Foreground colors + # | Code | Color | + # | 39 | Default foreground color | + # | 30 | Black | + # | 31 | Red | + # | 32 | Green | + # | 33 | Yellow | + # | 34 | Blue | + # | 35 | Magenta | + # | 36 | Cyan | + # | 37 | Light gray | + # | 90 | Dark gray | + # | 91 | Light red | + # | 92 | Light green | + # | 93 | Light yellow | + # | 94 | Light blue | + # | 95 | Light magenta | + # | 96 | Light cyan | + # | 97 | White | + + case colour + when :black then 30 + when :red, :failure then 31 + when :green, :success then 32 + when :yellow then 33 + when :blue, :narrative then 34 + when :purple, :magenta then 35 + when :cyan, :output then 36 + when :white, :default_white then 37 + when :default then 39 + else + 39 + end + end + + def out_c(mode, colour, str) + case RUBY_PLATFORM + when /(win|w)32$/ + change_to(colour) + $stdout.puts str if mode == :puts + $stdout.print str if mode == :print + change_to(:default_white) + else + $stdout.puts("#{change_to(colour)}#{str}\033[0m") if mode == :puts + $stdout.print("#{change_to(colour)}#{str}\033[0m") if mode == :print + end + end +end # ColourCommandLine + +def colour_puts(role, str) + ColourCommandLine.new.out_c(:puts, role, str) +end + +def colour_print(role, str) + ColourCommandLine.new.out_c(:print, role, str) +end diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/auto/colour_reporter.rb b/components/spotify/cspot/bell/cJSON/tests/unity/auto/colour_reporter.rb new file mode 100644 index 00000000..bb1fbfce --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/auto/colour_reporter.rb @@ -0,0 +1,39 @@ +# ========================================== +# Unity Project - A Test Framework for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +require "#{File.expand_path(File.dirname(__FILE__))}/colour_prompt" + +$colour_output = true + +def report(message) + if !$colour_output + $stdout.puts(message) + else + message = message.join('\n') if message.class == Array + message.each_line do |line| + line.chomp! + colour = case line + when /(?:total\s+)?tests:?\s+(\d+)\s+(?:total\s+)?failures:?\s+\d+\s+Ignored:?/i + Regexp.last_match(1).to_i.zero? ? :green : :red + when /PASS/ + :green + when /^OK$/ + :green + when /(?:FAIL|ERROR)/ + :red + when /IGNORE/ + :yellow + when /^(?:Creating|Compiling|Linking)/ + :white + else + :silver + end + colour_puts(colour, line) + end + end + $stdout.flush + $stderr.flush +end diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/auto/generate_config.yml b/components/spotify/cspot/bell/cJSON/tests/unity/auto/generate_config.yml new file mode 100644 index 00000000..4a5e4742 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/auto/generate_config.yml @@ -0,0 +1,36 @@ +#this is a sample configuration file for generate_module +#you would use it by calling generate_module with the -ygenerate_config.yml option +#files like this are useful for customizing generate_module to your environment +:generate_module: + :defaults: + #these defaults are used in place of any missing options at the command line + :path_src: ../src/ + :path_inc: ../src/ + :path_tst: ../test/ + :update_svn: true + :includes: + #use [] for no additional includes, otherwise list the includes on separate lines + :src: + - Defs.h + - Board.h + :inc: [] + :tst: + - Defs.h + - Board.h + - Exception.h + :boilerplates: + #these are inserted at the top of generated files. + #just comment out or remove if not desired. + #use %1$s where you would like the file name to appear (path/extension not included) + :src: | + //------------------------------------------- + // %1$s.c + //------------------------------------------- + :inc: | + //------------------------------------------- + // %1$s.h + //------------------------------------------- + :tst: | + //------------------------------------------- + // Test%1$s.c : Units tests for %1$s.c + //------------------------------------------- diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/auto/generate_module.rb b/components/spotify/cspot/bell/cJSON/tests/unity/auto/generate_module.rb new file mode 100644 index 00000000..13b4cc74 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/auto/generate_module.rb @@ -0,0 +1,308 @@ +# ========================================== +# Unity Project - A Test Framework for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +# This script creates all the files with start code necessary for a new module. +# A simple module only requires a source file, header file, and test file. +# Triad modules require a source, header, and test file for each triad type (like model, conductor, and hardware). + +require 'rubygems' +require 'fileutils' +require 'pathname' + +# TEMPLATE_TST +TEMPLATE_TST ||= '#include "unity.h" +%2$s#include "%1$s.h" + +void setUp(void) +{ +} + +void tearDown(void) +{ +} + +void test_%1$s_NeedToImplement(void) +{ + TEST_IGNORE_MESSAGE("Need to Implement %1$s"); +} +'.freeze + +# TEMPLATE_SRC +TEMPLATE_SRC ||= '%2$s#include "%1$s.h" +'.freeze + +# TEMPLATE_INC +TEMPLATE_INC ||= '#ifndef _%3$s_H +#define _%3$s_H +%2$s + +#endif // _%3$s_H +'.freeze + +class UnityModuleGenerator + ############################ + def initialize(options = nil) + here = File.expand_path(File.dirname(__FILE__)) + '/' + + @options = UnityModuleGenerator.default_options + case options + when NilClass then @options + when String then @options.merge!(UnityModuleGenerator.grab_config(options)) + when Hash then @options.merge!(options) + else raise 'If you specify arguments, it should be a filename or a hash of options' + end + + # Create default file paths if none were provided + @options[:path_src] = here + '../src/' if @options[:path_src].nil? + @options[:path_inc] = @options[:path_src] if @options[:path_inc].nil? + @options[:path_tst] = here + '../test/' if @options[:path_tst].nil? + @options[:path_src] += '/' unless @options[:path_src][-1] == 47 + @options[:path_inc] += '/' unless @options[:path_inc][-1] == 47 + @options[:path_tst] += '/' unless @options[:path_tst][-1] == 47 + + # Built in patterns + @patterns = { + 'src' => { + '' => { inc: [] } + }, + 'test' => { + '' => { inc: [] } + }, + 'dh' => { + 'Driver' => { inc: [create_filename('%1$s', 'Hardware.h')] }, + 'Hardware' => { inc: [] } + }, + 'dih' => { + 'Driver' => { inc: [create_filename('%1$s', 'Hardware.h'), create_filename('%1$s', 'Interrupt.h')] }, + 'Interrupt' => { inc: [create_filename('%1$s', 'Hardware.h')] }, + 'Hardware' => { inc: [] } + }, + 'mch' => { + 'Model' => { inc: [] }, + 'Conductor' => { inc: [create_filename('%1$s', 'Model.h'), create_filename('%1$s', 'Hardware.h')] }, + 'Hardware' => { inc: [] } + }, + 'mvp' => { + 'Model' => { inc: [] }, + 'Presenter' => { inc: [create_filename('%1$s', 'Model.h'), create_filename('%1$s', 'View.h')] }, + 'View' => { inc: [] } + } + } + end + + ############################ + def self.default_options + { + pattern: 'src', + includes: { + src: [], + inc: [], + tst: [] + }, + update_svn: false, + boilerplates: {}, + test_prefix: 'Test', + mock_prefix: 'Mock' + } + end + + ############################ + def self.grab_config(config_file) + options = default_options + unless config_file.nil? || config_file.empty? + require 'yaml' + yaml_guts = YAML.load_file(config_file) + options.merge!(yaml_guts[:unity] || yaml_guts[:cmock]) + raise "No :unity or :cmock section found in #{config_file}" unless options + end + options + end + + ############################ + def files_to_operate_on(module_name, pattern = nil) + # strip any leading path information from the module name and save for later + subfolder = File.dirname(module_name) + module_name = File.basename(module_name) + + # create triad definition + prefix = @options[:test_prefix] || 'Test' + triad = [{ ext: '.c', path: @options[:path_src], prefix: '', template: TEMPLATE_SRC, inc: :src, boilerplate: @options[:boilerplates][:src] }, + { ext: '.h', path: @options[:path_inc], prefix: '', template: TEMPLATE_INC, inc: :inc, boilerplate: @options[:boilerplates][:inc] }, + { ext: '.c', path: @options[:path_tst], prefix: prefix, template: TEMPLATE_TST, inc: :tst, boilerplate: @options[:boilerplates][:tst] }] + + # prepare the pattern for use + pattern = (pattern || @options[:pattern] || 'src').downcase + patterns = @patterns[pattern] + raise "ERROR: The design pattern '#{pattern}' specified isn't one that I recognize!" if patterns.nil? + + # single file patterns (currently just 'test') can reject the other parts of the triad + triad.select! { |v| v[:inc] == :tst } if pattern == 'test' + + # Assemble the path/names of the files we need to work with. + files = [] + triad.each do |cfg| + patterns.each_pair do |pattern_file, pattern_traits| + submodule_name = create_filename(module_name, pattern_file) + filename = cfg[:prefix] + submodule_name + cfg[:ext] + files << { + path: (Pathname.new("#{cfg[:path]}#{subfolder}") + filename).cleanpath, + name: submodule_name, + template: cfg[:template], + boilerplate: cfg[:boilerplate], + includes: case (cfg[:inc]) + when :src then (@options[:includes][:src] || []) | (pattern_traits[:inc].map { |f| format(f, module_name) }) + when :inc then (@options[:includes][:inc] || []) + when :tst then (@options[:includes][:tst] || []) | (pattern_traits[:inc].map { |f| format("#{@options[:mock_prefix]}#{f}", module_name) }) + end + } + end + end + + files + end + + ############################ + def create_filename(part1, part2 = '') + if part2.empty? + case (@options[:naming]) + when 'bumpy' then part1 + when 'camel' then part1 + when 'snake' then part1.downcase + when 'caps' then part1.upcase + else part1 + end + else + case (@options[:naming]) + when 'bumpy' then part1 + part2 + when 'camel' then part1 + part2 + when 'snake' then part1.downcase + '_' + part2.downcase + when 'caps' then part1.upcase + '_' + part2.upcase + else part1 + '_' + part2 + end + end + end + + ############################ + def generate(module_name, pattern = nil) + files = files_to_operate_on(module_name, pattern) + + # Abort if all of the module files already exist + all_files_exist = true + files.each do |file| + all_files_exist = false unless File.exist?(file[:path]) + end + raise "ERROR: File #{files[0][:name]} already exists. Exiting." if all_files_exist + + # Create Source Modules + files.each_with_index do |file, _i| + # If this file already exists, don't overwrite it. + if File.exist?(file[:path]) + puts "File #{file[:path]} already exists!" + next + end + # Create the path first if necessary. + FileUtils.mkdir_p(File.dirname(file[:path]), verbose: false) + File.open(file[:path], 'w') do |f| + f.write("#{file[:boilerplate]}\n" % [file[:name]]) unless file[:boilerplate].nil? + f.write(file[:template] % [file[:name], + file[:includes].map { |ff| "#include \"#{ff}\"\n" }.join, + file[:name].upcase]) + end + if @options[:update_svn] + `svn add \"#{file[:path]}\"` + if $!.exitstatus.zero? + puts "File #{file[:path]} created and added to source control" + else + puts "File #{file[:path]} created but FAILED adding to source control!" + end + else + puts "File #{file[:path]} created" + end + end + puts 'Generate Complete' + end + + ############################ + def destroy(module_name, pattern = nil) + files_to_operate_on(module_name, pattern).each do |filespec| + file = filespec[:path] + if File.exist?(file) + if @options[:update_svn] + `svn delete \"#{file}\" --force` + puts "File #{file} deleted and removed from source control" + else + FileUtils.remove(file) + puts "File #{file} deleted" + end + else + puts "File #{file} does not exist so cannot be removed." + end + end + puts 'Destroy Complete' + end +end + +############################ +# Handle As Command Line If Called That Way +if $0 == __FILE__ + destroy = false + options = {} + module_name = nil + + # Parse the command line parameters. + ARGV.each do |arg| + case arg + when /^-d/ then destroy = true + when /^-u/ then options[:update_svn] = true + when /^-p\"?(\w+)\"?/ then options[:pattern] = Regexp.last_match(1) + when /^-s\"?(.+)\"?/ then options[:path_src] = Regexp.last_match(1) + when /^-i\"?(.+)\"?/ then options[:path_inc] = Regexp.last_match(1) + when /^-t\"?(.+)\"?/ then options[:path_tst] = Regexp.last_match(1) + when /^-n\"?(.+)\"?/ then options[:naming] = Regexp.last_match(1) + when /^-y\"?(.+)\"?/ then options = UnityModuleGenerator.grab_config(Regexp.last_match(1)) + when /^(\w+)/ + raise "ERROR: You can't have more than one Module name specified!" unless module_name.nil? + module_name = arg + when /^-(h|-help)/ + ARGV = [].freeze + else + raise "ERROR: Unknown option specified '#{arg}'" + end + end + + unless ARGV[0] + puts ["\nGENERATE MODULE\n-------- ------", + "\nUsage: ruby generate_module [options] module_name", + " -i\"include\" sets the path to output headers to 'include' (DEFAULT ../src)", + " -s\"../src\" sets the path to output source to '../src' (DEFAULT ../src)", + " -t\"C:/test\" sets the path to output source to 'C:/test' (DEFAULT ../test)", + ' -p"MCH" sets the output pattern to MCH.', + ' dh - driver hardware.', + ' dih - driver interrupt hardware.', + ' mch - model conductor hardware.', + ' mvp - model view presenter.', + ' src - just a source module, header and test. (DEFAULT)', + ' test - just a test file.', + ' -d destroy module instead of creating it.', + ' -n"camel" sets the file naming convention.', + ' bumpy - BumpyCaseFilenames.', + ' camel - camelCaseFilenames.', + ' snake - snake_case_filenames.', + ' caps - CAPS_CASE_FILENAMES.', + ' -u update subversion too (requires subversion command line)', + ' -y"my.yml" selects a different yaml config file for module generation', + ''].join("\n") + exit + end + + raise 'ERROR: You must have a Module name specified! (use option -h for help)' if module_name.nil? + if destroy + UnityModuleGenerator.new(options).destroy(module_name) + else + UnityModuleGenerator.new(options).generate(module_name) + end + +end diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/auto/generate_test_runner.rb b/components/spotify/cspot/bell/cJSON/tests/unity/auto/generate_test_runner.rb new file mode 100644 index 00000000..344a2d0f --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/auto/generate_test_runner.rb @@ -0,0 +1,454 @@ +# ========================================== +# Unity Project - A Test Framework for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +File.expand_path(File.join(File.dirname(__FILE__), 'colour_prompt')) + +class UnityTestRunnerGenerator + def initialize(options = nil) + @options = UnityTestRunnerGenerator.default_options + case options + when NilClass then @options + when String then @options.merge!(UnityTestRunnerGenerator.grab_config(options)) + when Hash then @options.merge!(options) + else raise 'If you specify arguments, it should be a filename or a hash of options' + end + require "#{File.expand_path(File.dirname(__FILE__))}/type_sanitizer" + end + + def self.default_options + { + includes: [], + defines: [], + plugins: [], + framework: :unity, + test_prefix: 'test|spec|should', + mock_prefix: 'Mock', + setup_name: 'setUp', + teardown_name: 'tearDown', + main_name: 'main', # set to :auto to automatically generate each time + main_export_decl: '', + cmdline_args: false, + use_param_tests: false + } + end + + def self.grab_config(config_file) + options = default_options + unless config_file.nil? || config_file.empty? + require 'yaml' + yaml_guts = YAML.load_file(config_file) + options.merge!(yaml_guts[:unity] || yaml_guts[:cmock]) + raise "No :unity or :cmock section found in #{config_file}" unless options + end + options + end + + def run(input_file, output_file, options = nil) + @options.merge!(options) unless options.nil? + + # pull required data from source file + source = File.read(input_file) + source = source.force_encoding('ISO-8859-1').encode('utf-8', replace: nil) + tests = find_tests(source) + headers = find_includes(source) + testfile_includes = (headers[:local] + headers[:system]) + used_mocks = find_mocks(testfile_includes) + testfile_includes = (testfile_includes - used_mocks) + testfile_includes.delete_if { |inc| inc =~ /(unity|cmock)/ } + + # build runner file + generate(input_file, output_file, tests, used_mocks, testfile_includes) + + # determine which files were used to return them + all_files_used = [input_file, output_file] + all_files_used += testfile_includes.map { |filename| filename + '.c' } unless testfile_includes.empty? + all_files_used += @options[:includes] unless @options[:includes].empty? + all_files_used += headers[:linkonly] unless headers[:linkonly].empty? + all_files_used.uniq + end + + def generate(input_file, output_file, tests, used_mocks, testfile_includes) + File.open(output_file, 'w') do |output| + create_header(output, used_mocks, testfile_includes) + create_externs(output, tests, used_mocks) + create_mock_management(output, used_mocks) + create_suite_setup(output) + create_suite_teardown(output) + create_reset(output, used_mocks) + create_main(output, input_file, tests, used_mocks) + end + + return unless @options[:header_file] && !@options[:header_file].empty? + + File.open(@options[:header_file], 'w') do |output| + create_h_file(output, @options[:header_file], tests, testfile_includes, used_mocks) + end + end + + def find_tests(source) + tests_and_line_numbers = [] + + source_scrubbed = source.clone + source_scrubbed = source_scrubbed.gsub(/"[^"\n]*"/, '') # remove things in strings + source_scrubbed = source_scrubbed.gsub(/\/\/.*$/, '') # remove line comments + source_scrubbed = source_scrubbed.gsub(/\/\*.*?\*\//m, '') # remove block comments + lines = source_scrubbed.split(/(^\s*\#.*$) # Treat preprocessor directives as a logical line + | (;|\{|\}) /x) # Match ;, {, and } as end of lines + + lines.each_with_index do |line, _index| + # find tests + next unless line =~ /^((?:\s*TEST_CASE\s*\(.*?\)\s*)*)\s*void\s+((?:#{@options[:test_prefix]}).*)\s*\(\s*(.*)\s*\)/ + arguments = Regexp.last_match(1) + name = Regexp.last_match(2) + call = Regexp.last_match(3) + params = Regexp.last_match(4) + args = nil + if @options[:use_param_tests] && !arguments.empty? + args = [] + arguments.scan(/\s*TEST_CASE\s*\((.*)\)\s*$/) { |a| args << a[0] } + end + tests_and_line_numbers << { test: name, args: args, call: call, params: params, line_number: 0 } + end + tests_and_line_numbers.uniq! { |v| v[:test] } + + # determine line numbers and create tests to run + source_lines = source.split("\n") + source_index = 0 + tests_and_line_numbers.size.times do |i| + source_lines[source_index..-1].each_with_index do |line, index| + next unless line =~ /\s+#{tests_and_line_numbers[i][:test]}(?:\s|\()/ + source_index += index + tests_and_line_numbers[i][:line_number] = source_index + 1 + break + end + end + + tests_and_line_numbers + end + + def find_includes(source) + # remove comments (block and line, in three steps to ensure correct precedence) + source.gsub!(/\/\/(?:.+\/\*|\*(?:$|[^\/])).*$/, '') # remove line comments that comment out the start of blocks + source.gsub!(/\/\*.*?\*\//m, '') # remove block comments + source.gsub!(/\/\/.*$/, '') # remove line comments (all that remain) + + # parse out includes + includes = { + local: source.scan(/^\s*#include\s+\"\s*(.+)\.[hH]\s*\"/).flatten, + system: source.scan(/^\s*#include\s+<\s*(.+)\s*>/).flatten.map { |inc| "<#{inc}>" }, + linkonly: source.scan(/^TEST_FILE\(\s*\"\s*(.+)\.[cC]\w*\s*\"/).flatten + } + includes + end + + def find_mocks(includes) + mock_headers = [] + includes.each do |include_path| + include_file = File.basename(include_path) + mock_headers << include_path if include_file =~ /^#{@options[:mock_prefix]}/i + end + mock_headers + end + + def create_header(output, mocks, testfile_includes = []) + output.puts('/* AUTOGENERATED FILE. DO NOT EDIT. */') + create_runtest(output, mocks) + output.puts("\n/*=======Automagically Detected Files To Include=====*/") + output.puts('#ifdef __WIN32__') + output.puts('#define UNITY_INCLUDE_SETUP_STUBS') + output.puts('#endif') + output.puts("#include \"#{@options[:framework]}.h\"") + output.puts('#include "cmock.h"') unless mocks.empty? + output.puts('#include ') + output.puts('#include ') + if @options[:defines] && !@options[:defines].empty? + @options[:defines].each { |d| output.puts("#define #{d}") } + end + if @options[:header_file] && !@options[:header_file].empty? + output.puts("#include \"#{File.basename(@options[:header_file])}\"") + else + @options[:includes].flatten.uniq.compact.each do |inc| + output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h', '')}.h\""}") + end + testfile_includes.each do |inc| + output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h', '')}.h\""}") + end + end + mocks.each do |mock| + output.puts("#include \"#{mock.gsub('.h', '')}.h\"") + end + output.puts('#include "CException.h"') if @options[:plugins].include?(:cexception) + + return unless @options[:enforce_strict_ordering] + + output.puts('') + output.puts('int GlobalExpectCount;') + output.puts('int GlobalVerifyOrder;') + output.puts('char* GlobalOrderError;') + end + + def create_externs(output, tests, _mocks) + output.puts("\n/*=======External Functions This Runner Calls=====*/") + output.puts("extern void #{@options[:setup_name]}(void);") + output.puts("extern void #{@options[:teardown_name]}(void);") + tests.each do |test| + output.puts("extern void #{test[:test]}(#{test[:call] || 'void'});") + end + output.puts('') + end + + def create_mock_management(output, mock_headers) + return if mock_headers.empty? + + output.puts("\n/*=======Mock Management=====*/") + output.puts('static void CMock_Init(void)') + output.puts('{') + + if @options[:enforce_strict_ordering] + output.puts(' GlobalExpectCount = 0;') + output.puts(' GlobalVerifyOrder = 0;') + output.puts(' GlobalOrderError = NULL;') + end + + mocks = mock_headers.map { |mock| File.basename(mock) } + mocks.each do |mock| + mock_clean = TypeSanitizer.sanitize_c_identifier(mock) + output.puts(" #{mock_clean}_Init();") + end + output.puts("}\n") + + output.puts('static void CMock_Verify(void)') + output.puts('{') + mocks.each do |mock| + mock_clean = TypeSanitizer.sanitize_c_identifier(mock) + output.puts(" #{mock_clean}_Verify();") + end + output.puts("}\n") + + output.puts('static void CMock_Destroy(void)') + output.puts('{') + mocks.each do |mock| + mock_clean = TypeSanitizer.sanitize_c_identifier(mock) + output.puts(" #{mock_clean}_Destroy();") + end + output.puts("}\n") + end + + def create_suite_setup(output) + output.puts("\n/*=======Suite Setup=====*/") + output.puts('static void suite_setup(void)') + output.puts('{') + if @options[:suite_setup].nil? + # New style, call suiteSetUp() if we can use weak symbols + output.puts('#if defined(UNITY_WEAK_ATTRIBUTE) || defined(UNITY_WEAK_PRAGMA)') + output.puts(' suiteSetUp();') + output.puts('#endif') + else + # Old style, C code embedded in the :suite_setup option + output.puts(@options[:suite_setup]) + end + output.puts('}') + end + + def create_suite_teardown(output) + output.puts("\n/*=======Suite Teardown=====*/") + output.puts('static int suite_teardown(int num_failures)') + output.puts('{') + if @options[:suite_teardown].nil? + # New style, call suiteTearDown() if we can use weak symbols + output.puts('#if defined(UNITY_WEAK_ATTRIBUTE) || defined(UNITY_WEAK_PRAGMA)') + output.puts(' return suiteTearDown(num_failures);') + output.puts('#else') + output.puts(' return num_failures;') + output.puts('#endif') + else + # Old style, C code embedded in the :suite_teardown option + output.puts(@options[:suite_teardown]) + end + output.puts('}') + end + + def create_runtest(output, used_mocks) + cexception = @options[:plugins].include? :cexception + va_args1 = @options[:use_param_tests] ? ', ...' : '' + va_args2 = @options[:use_param_tests] ? '__VA_ARGS__' : '' + output.puts("\n/*=======Test Runner Used To Run Each Test Below=====*/") + output.puts('#define RUN_TEST_NO_ARGS') if @options[:use_param_tests] + output.puts("#define RUN_TEST(TestFunc, TestLineNum#{va_args1}) \\") + output.puts('{ \\') + output.puts(" Unity.CurrentTestName = #TestFunc#{va_args2.empty? ? '' : " \"(\" ##{va_args2} \")\""}; \\") + output.puts(' Unity.CurrentTestLineNumber = TestLineNum; \\') + output.puts(' if (UnityTestMatches()) { \\') if @options[:cmdline_args] + output.puts(' Unity.NumberOfTests++; \\') + output.puts(' CMock_Init(); \\') unless used_mocks.empty? + output.puts(' UNITY_CLR_DETAILS(); \\') unless used_mocks.empty? + output.puts(' if (TEST_PROTECT()) \\') + output.puts(' { \\') + output.puts(' CEXCEPTION_T e; \\') if cexception + output.puts(' Try { \\') if cexception + output.puts(" #{@options[:setup_name]}(); \\") + output.puts(" TestFunc(#{va_args2}); \\") + output.puts(' } Catch(e) { TEST_ASSERT_EQUAL_HEX32_MESSAGE(CEXCEPTION_NONE, e, "Unhandled Exception!"); } \\') if cexception + output.puts(' } \\') + output.puts(' if (TEST_PROTECT()) \\') + output.puts(' { \\') + output.puts(" #{@options[:teardown_name]}(); \\") + output.puts(' CMock_Verify(); \\') unless used_mocks.empty? + output.puts(' } \\') + output.puts(' CMock_Destroy(); \\') unless used_mocks.empty? + output.puts(' UnityConcludeTest(); \\') + output.puts(' } \\') if @options[:cmdline_args] + output.puts("}\n") + end + + def create_reset(output, used_mocks) + output.puts("\n/*=======Test Reset Option=====*/") + output.puts('void resetTest(void);') + output.puts('void resetTest(void)') + output.puts('{') + output.puts(' CMock_Verify();') unless used_mocks.empty? + output.puts(' CMock_Destroy();') unless used_mocks.empty? + output.puts(" #{@options[:teardown_name]}();") + output.puts(' CMock_Init();') unless used_mocks.empty? + output.puts(" #{@options[:setup_name]}();") + output.puts('}') + end + + def create_main(output, filename, tests, used_mocks) + output.puts("\n\n/*=======MAIN=====*/") + main_name = @options[:main_name].to_sym == :auto ? "main_#{filename.gsub('.c', '')}" : (@options[:main_name]).to_s + if @options[:cmdline_args] + if main_name != 'main' + output.puts("#{@options[:main_export_decl]} int #{main_name}(int argc, char** argv);") + end + output.puts("#{@options[:main_export_decl]} int #{main_name}(int argc, char** argv)") + output.puts('{') + output.puts(' int parse_status = UnityParseOptions(argc, argv);') + output.puts(' if (parse_status != 0)') + output.puts(' {') + output.puts(' if (parse_status < 0)') + output.puts(' {') + output.puts(" UnityPrint(\"#{filename.gsub('.c', '')}.\");") + output.puts(' UNITY_PRINT_EOL();') + if @options[:use_param_tests] + tests.each do |test| + if test[:args].nil? || test[:args].empty? + output.puts(" UnityPrint(\" #{test[:test]}(RUN_TEST_NO_ARGS)\");") + output.puts(' UNITY_PRINT_EOL();') + else + test[:args].each do |args| + output.puts(" UnityPrint(\" #{test[:test]}(#{args})\");") + output.puts(' UNITY_PRINT_EOL();') + end + end + end + else + tests.each { |test| output.puts(" UnityPrint(\" #{test[:test]}\");\n UNITY_PRINT_EOL();") } + end + output.puts(' return 0;') + output.puts(' }') + output.puts(' return parse_status;') + output.puts(' }') + else + if main_name != 'main' + output.puts("#{@options[:main_export_decl]} int #{main_name}(void);") + end + output.puts("int #{main_name}(void)") + output.puts('{') + end + output.puts(' suite_setup();') + output.puts(" UnityBegin(\"#{filename.gsub(/\\/, '\\\\\\')}\");") + if @options[:use_param_tests] + tests.each do |test| + if test[:args].nil? || test[:args].empty? + output.puts(" RUN_TEST(#{test[:test]}, #{test[:line_number]}, RUN_TEST_NO_ARGS);") + else + test[:args].each { |args| output.puts(" RUN_TEST(#{test[:test]}, #{test[:line_number]}, #{args});") } + end + end + else + tests.each { |test| output.puts(" RUN_TEST(#{test[:test]}, #{test[:line_number]});") } + end + output.puts + output.puts(' CMock_Guts_MemFreeFinal();') unless used_mocks.empty? + output.puts(" return suite_teardown(UnityEnd());") + output.puts('}') + end + + def create_h_file(output, filename, tests, testfile_includes, used_mocks) + filename = File.basename(filename).gsub(/[-\/\\\.\,\s]/, '_').upcase + output.puts('/* AUTOGENERATED FILE. DO NOT EDIT. */') + output.puts("#ifndef _#{filename}") + output.puts("#define _#{filename}\n\n") + output.puts("#include \"#{@options[:framework]}.h\"") + output.puts('#include "cmock.h"') unless used_mocks.empty? + @options[:includes].flatten.uniq.compact.each do |inc| + output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h', '')}.h\""}") + end + testfile_includes.each do |inc| + output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h', '')}.h\""}") + end + output.puts "\n" + tests.each do |test| + if test[:params].nil? || test[:params].empty? + output.puts("void #{test[:test]}(void);") + else + output.puts("void #{test[:test]}(#{test[:params]});") + end + end + output.puts("#endif\n\n") + end +end + +if $0 == __FILE__ + options = { includes: [] } + + # parse out all the options first (these will all be removed as we go) + ARGV.reject! do |arg| + case arg + when '-cexception' + options[:plugins] = [:cexception] + true + when /\.*\.ya?ml/ + options = UnityTestRunnerGenerator.grab_config(arg) + true + when /--(\w+)=\"?(.*)\"?/ + options[Regexp.last_match(1).to_sym] = Regexp.last_match(2) + true + when /\.*\.h/ + options[:includes] << arg + true + else false + end + end + + # make sure there is at least one parameter left (the input file) + unless ARGV[0] + puts ["\nusage: ruby #{__FILE__} (files) (options) input_test_file (output)", + "\n input_test_file - this is the C file you want to create a runner for", + ' output - this is the name of the runner file to generate', + ' defaults to (input_test_file)_Runner', + ' files:', + ' *.yml / *.yaml - loads configuration from here in :unity or :cmock', + ' *.h - header files are added as #includes in runner', + ' options:', + ' -cexception - include cexception support', + ' --setup_name="" - redefine setUp func name to something else', + ' --teardown_name="" - redefine tearDown func name to something else', + ' --main_name="" - redefine main func name to something else', + ' --test_prefix="" - redefine test prefix from default test|spec|should', + ' --suite_setup="" - code to execute for setup of entire suite', + ' --suite_teardown="" - code to execute for teardown of entire suite', + ' --use_param_tests=1 - enable parameterized tests (disabled by default)', + ' --header_file="" - path/name of test header file to generate too'].join("\n") + exit 1 + end + + # create the default test runner name if not specified + ARGV[1] = ARGV[0].gsub('.c', '_Runner.c') unless ARGV[1] + + UnityTestRunnerGenerator.new(options).run(ARGV[0], ARGV[1]) +end diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/auto/parse_output.rb b/components/spotify/cspot/bell/cJSON/tests/unity/auto/parse_output.rb new file mode 100644 index 00000000..f16cdb03 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/auto/parse_output.rb @@ -0,0 +1,220 @@ +#============================================================ +# Author: John Theofanopoulos +# A simple parser. Takes the output files generated during the build process and +# extracts information relating to the tests. +# +# Notes: +# To capture an output file under VS builds use the following: +# devenv [build instructions] > Output.txt & type Output.txt +# +# To capture an output file under GCC/Linux builds use the following: +# make | tee Output.txt +# +# To use this parser use the following command +# ruby parseOutput.rb [options] [file] +# options: -xml : produce a JUnit compatible XML file +# file : file to scan for results +#============================================================ + +class ParseOutput + def initialize + @test_flag = false + @xml_out = false + @array_list = false + @total_tests = false + @class_index = false + end + + # Set the flag to indicate if there will be an XML output file or not + def set_xml_output + @xml_out = true + end + + # if write our output to XML + def write_xml_output + output = File.open('report.xml', 'w') + output << "\n" + @array_list.each do |item| + output << item << "\n" + end + output << "\n" + end + + # This function will try and determine when the suite is changed. This is + # is the name that gets added to the classname parameter. + def test_suite_verify(test_suite_name) + return if @test_flag + + @test_flag = true + # Split the path name + test_name = test_suite_name.split('/') + # Remove the extension + base_name = test_name[test_name.size - 1].split('.') + @test_suite = 'test.' + base_name[0] + printf "New Test: %s\n", @test_suite + end + + # Test was flagged as having passed so format the output + def test_passed(array) + last_item = array.length - 1 + test_name = array[last_item - 1] + test_suite_verify(array[@class_name]) + printf "%-40s PASS\n", test_name + + return unless @xml_out + + @array_list.push ' ' + end + + # Test was flagged as having passed so format the output. + # This is using the Unity fixture output and not the original Unity output. + def test_passed_unity_fixture(array) + test_suite = array[0].sub('TEST(', '') + test_suite = test_suite.sub(',', '') + test_name = array[1].sub(')', '') + + return unless @xml_out + + @array_list.push ' ' + end + + # Test was flagged as being ingored so format the output + def test_ignored(array) + last_item = array.length - 1 + test_name = array[last_item - 2] + reason = array[last_item].chomp + test_suite_verify(array[@class_name]) + printf "%-40s IGNORED\n", test_name + + if test_name.start_with? 'TEST(' + array2 = test_name.split(' ') + @test_suite = array2[0].sub('TEST(', '') + @test_suite = @test_suite.sub(',', '') + test_name = array2[1].sub(')', '') + end + + return unless @xml_out + + @array_list.push ' ' + @array_list.push ' ' + reason + ' ' + @array_list.push ' ' + end + + # Test was flagged as having failed so format the line + def test_failed(array) + last_item = array.length - 1 + test_name = array[last_item - 2] + reason = array[last_item].chomp + ' at line: ' + array[last_item - 3] + test_suite_verify(array[@class_name]) + printf "%-40s FAILED\n", test_name + + if test_name.start_with? 'TEST(' + array2 = test_name.split(' ') + @test_suite = array2[0].sub('TEST(', '') + @test_suite = @test_suite.sub(',', '') + test_name = array2[1].sub(')', '') + end + + return unless @xml_out + + @array_list.push ' ' + @array_list.push ' ' + reason + ' ' + @array_list.push ' ' + end + + # Figure out what OS we are running on. For now we are assuming if it's not Windows it must + # be Unix based. + def detect_os + os = RUBY_PLATFORM.split('-') + @class_name = if os.size == 2 + if os[1] == 'mingw32' + 1 + else + 0 + end + else + 0 + end + end + + # Main function used to parse the file that was captured. + def process(name) + @test_flag = false + @array_list = [] + + detect_os + + puts 'Parsing file: ' + name + + test_pass = 0 + test_fail = 0 + test_ignore = 0 + puts '' + puts '=================== RESULTS =====================' + puts '' + File.open(name).each do |line| + # Typical test lines look like this: + # /.c:36:test_tc1000_opsys:FAIL: Expected 1 Was 0 + # /.c:112:test_tc5004_initCanChannel:IGNORE: Not Yet Implemented + # /.c:115:test_tc5100_initCanVoidPtrs:PASS + # + # where path is different on Unix vs Windows devices (Windows leads with a drive letter) + line_array = line.split(':') + + # If we were able to split the line then we can look to see if any of our target words + # were found. Case is important. + if (line_array.size >= 4) || (line.start_with? 'TEST(') + # Determine if this test passed + if line.include? ':PASS' + test_passed(line_array) + test_pass += 1 + elsif line.include? ':FAIL:' + test_failed(line_array) + test_fail += 1 + elsif line.include? ':IGNORE:' + test_ignored(line_array) + test_ignore += 1 + elsif line.start_with? 'TEST(' + if line.include? ' PASS' + line_array = line.split(' ') + test_passed_unity_fixture(line_array) + test_pass += 1 + end + # If none of the keywords are found there are no more tests for this suite so clear + # the test flag + else + @test_flag = false + end + else + @test_flag = false + end + end + puts '' + puts '=================== SUMMARY =====================' + puts '' + puts 'Tests Passed : ' + test_pass.to_s + puts 'Tests Failed : ' + test_fail.to_s + puts 'Tests Ignored : ' + test_ignore.to_s + @total_tests = test_pass + test_fail + test_ignore + + return unless @xml_out + + heading = '' + @array_list.insert(0, heading) + write_xml_output + end +end + +# If the command line has no values in, used a default value of Output.txt +parse_my_file = ParseOutput.new + +if ARGV.size >= 1 + ARGV.each do |a| + if a == '-xml' + parse_my_file.set_xml_output + else + parse_my_file.process(a) + break + end + end +end diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/auto/stylize_as_junit.rb b/components/spotify/cspot/bell/cJSON/tests/unity/auto/stylize_as_junit.rb new file mode 100644 index 00000000..b3d8f409 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/auto/stylize_as_junit.rb @@ -0,0 +1,252 @@ +#!/usr/bin/ruby +# +# unity_to_junit.rb +# +require 'fileutils' +require 'optparse' +require 'ostruct' +require 'set' + +require 'pp' + +VERSION = 1.0 + +class ArgvParser + # + # Return a structure describing the options. + # + def self.parse(args) + # The options specified on the command line will be collected in *options*. + # We set default values here. + options = OpenStruct.new + options.results_dir = '.' + options.root_path = '.' + options.out_file = 'results.xml' + + opts = OptionParser.new do |o| + o.banner = 'Usage: unity_to_junit.rb [options]' + + o.separator '' + o.separator 'Specific options:' + + o.on('-r', '--results ', 'Look for Unity Results files here.') do |results| + # puts "results #{results}" + options.results_dir = results + end + + o.on('-p', '--root_path ', 'Prepend this path to files in results.') do |root_path| + options.root_path = root_path + end + + o.on('-o', '--output ', 'XML file to generate.') do |out_file| + # puts "out_file: #{out_file}" + options.out_file = out_file + end + + o.separator '' + o.separator 'Common options:' + + # No argument, shows at tail. This will print an options summary. + o.on_tail('-h', '--help', 'Show this message') do + puts o + exit + end + + # Another typical switch to print the version. + o.on_tail('--version', 'Show version') do + puts "unity_to_junit.rb version #{VERSION}" + exit + end + end + + opts.parse!(args) + options + end # parse() +end # class OptparseExample + +class UnityToJUnit + include FileUtils::Verbose + attr_reader :report, :total_tests, :failures, :ignored + attr_writer :targets, :root, :out_file + + def initialize + @report = '' + @unit_name = '' + end + + def run + # Clean up result file names + results = @targets.map { |target| target.tr('\\', '/') } + # puts "Output File: #{@out_file}" + f = File.new(@out_file, 'w') + write_xml_header(f) + write_suites_header(f) + results.each do |result_file| + lines = File.readlines(result_file).map(&:chomp) + + raise "Empty test result file: #{result_file}" if lines.empty? + + result_output = get_details(result_file, lines) + tests, failures, ignored = parse_test_summary(lines) + result_output[:counts][:total] = tests + result_output[:counts][:failed] = failures + result_output[:counts][:ignored] = ignored + result_output[:counts][:passed] = (result_output[:counts][:total] - result_output[:counts][:failed] - result_output[:counts][:ignored]) + + # use line[0] from the test output to get the test_file path and name + test_file_str = lines[0].tr('\\', '/') + test_file_str = test_file_str.split(':') + test_file = if test_file_str.length < 2 + result_file + else + test_file_str[0] + ':' + test_file_str[1] + end + result_output[:source][:path] = File.dirname(test_file) + result_output[:source][:file] = File.basename(test_file) + + # save result_output + @unit_name = File.basename(test_file, '.*') + + write_suite_header(result_output[:counts], f) + write_failures(result_output, f) + write_tests(result_output, f) + write_ignored(result_output, f) + write_suite_footer(f) + end + write_suites_footer(f) + f.close + end + + def usage(err_msg = nil) + puts "\nERROR: " + puts err_msg if err_msg + puts 'Usage: unity_to_junit.rb [options]' + puts '' + puts 'Specific options:' + puts ' -r, --results Look for Unity Results files here.' + puts ' -p, --root_path Prepend this path to files in results.' + puts ' -o, --output XML file to generate.' + puts '' + puts 'Common options:' + puts ' -h, --help Show this message' + puts ' --version Show version' + + exit 1 + end + + protected + + def get_details(_result_file, lines) + results = results_structure + lines.each do |line| + line = line.tr('\\', '/') + _src_file, src_line, test_name, status, msg = line.split(/:/) + case status + when 'IGNORE' then results[:ignores] << { test: test_name, line: src_line, message: msg } + when 'FAIL' then results[:failures] << { test: test_name, line: src_line, message: msg } + when 'PASS' then results[:successes] << { test: test_name, line: src_line, message: msg } + end + end + results + end + + def parse_test_summary(summary) + raise "Couldn't parse test results: #{summary}" unless summary.find { |v| v =~ /(\d+) Tests (\d+) Failures (\d+) Ignored/ } + [Regexp.last_match(1).to_i, Regexp.last_match(2).to_i, Regexp.last_match(3).to_i] + end + + def here + File.expand_path(File.dirname(__FILE__)) + end + + private + + def results_structure + { + source: { path: '', file: '' }, + successes: [], + failures: [], + ignores: [], + counts: { total: 0, passed: 0, failed: 0, ignored: 0 }, + stdout: [] + } + end + + def write_xml_header(stream) + stream.puts "" + end + + def write_suites_header(stream) + stream.puts '' + end + + def write_suite_header(counts, stream) + stream.puts "\t" + end + + def write_failures(results, stream) + result = results[:failures] + result.each do |item| + filename = File.join(results[:source][:path], File.basename(results[:source][:file], '.*')) + stream.puts "\t\t" + stream.puts "\t\t\t" + stream.puts "\t\t\t [File] #{filename} [Line] #{item[:line]} " + stream.puts "\t\t" + end + end + + def write_tests(results, stream) + result = results[:successes] + result.each do |item| + stream.puts "\t\t" + end + end + + def write_ignored(results, stream) + result = results[:ignores] + result.each do |item| + filename = File.join(results[:source][:path], File.basename(results[:source][:file], '.*')) + puts "Writing ignored tests for test harness: #{filename}" + stream.puts "\t\t" + stream.puts "\t\t\t" + stream.puts "\t\t\t [File] #{filename} [Line] #{item[:line]} " + stream.puts "\t\t" + end + end + + def write_suite_footer(stream) + stream.puts "\t" + end + + def write_suites_footer(stream) + stream.puts '' + end +end # UnityToJUnit + +if __FILE__ == $0 + # parse out the command options + options = ArgvParser.parse(ARGV) + + # create an instance to work with + utj = UnityToJUnit.new + begin + # look in the specified or current directory for result files + targets = "#{options.results_dir.tr('\\', '/')}**/*.test*" + + results = Dir[targets] + raise "No *.testpass, *.testfail, or *.testresults files found in '#{targets}'" if results.empty? + utj.targets = results + + # set the root path + utj.root = options.root_path + + # set the output XML file name + # puts "Output File from options: #{options.out_file}" + utj.out_file = options.out_file + + # run the summarizer + puts utj.run + rescue StandardError => e + utj.usage e.message + end +end diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/auto/test_file_filter.rb b/components/spotify/cspot/bell/cJSON/tests/unity/auto/test_file_filter.rb new file mode 100644 index 00000000..aad28e38 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/auto/test_file_filter.rb @@ -0,0 +1,25 @@ +# ========================================== +# Unity Project - A Test Framework for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +require'yaml' + +module RakefileHelpers + class TestFileFilter + def initialize(all_files = false) + @all_files = all_files + + return false unless @all_files + return false unless File.exist?('test_file_filter.yml') + + filters = YAML.load_file('test_file_filter.yml') + @all_files = filters[:all_files] + @only_files = filters[:only_files] + @exclude_files = filters[:exclude_files] + end + + attr_accessor :all_files, :only_files, :exclude_files + end +end diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/auto/type_sanitizer.rb b/components/spotify/cspot/bell/cJSON/tests/unity/auto/type_sanitizer.rb new file mode 100644 index 00000000..dafb8826 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/auto/type_sanitizer.rb @@ -0,0 +1,6 @@ +module TypeSanitizer + def self.sanitize_c_identifier(unsanitized) + # convert filename to valid C identifier by replacing invalid chars with '_' + unsanitized.gsub(/[-\/\\\.\,\s]/, '_') + end +end diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/auto/unity_test_summary.py b/components/spotify/cspot/bell/cJSON/tests/unity/auto/unity_test_summary.py new file mode 100644 index 00000000..4c20e528 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/auto/unity_test_summary.py @@ -0,0 +1,139 @@ +#! python3 +# ========================================== +# Unity Project - A Test Framework for C +# Copyright (c) 2015 Alexander Mueller / XelaRellum@web.de +# [Released under MIT License. Please refer to license.txt for details] +# Based on the ruby script by Mike Karlesky, Mark VanderVoord, Greg Williams +# ========================================== +import sys +import os +import re +from glob import glob + +class UnityTestSummary: + def __init__(self): + self.report = '' + self.total_tests = 0 + self.failures = 0 + self.ignored = 0 + + def run(self): + # Clean up result file names + results = [] + for target in self.targets: + results.append(target.replace('\\', '/')) + + # Dig through each result file, looking for details on pass/fail: + failure_output = [] + ignore_output = [] + + for result_file in results: + lines = list(map(lambda line: line.rstrip(), open(result_file, "r").read().split('\n'))) + if len(lines) == 0: + raise Exception("Empty test result file: %s" % result_file) + + details = self.get_details(result_file, lines) + failures = details['failures'] + ignores = details['ignores'] + if len(failures) > 0: failure_output.append('\n'.join(failures)) + if len(ignores) > 0: ignore_output.append('n'.join(ignores)) + tests,failures,ignored = self.parse_test_summary('\n'.join(lines)) + self.total_tests += tests + self.failures += failures + self.ignored += ignored + + if self.ignored > 0: + self.report += "\n" + self.report += "--------------------------\n" + self.report += "UNITY IGNORED TEST SUMMARY\n" + self.report += "--------------------------\n" + self.report += "\n".join(ignore_output) + + if self.failures > 0: + self.report += "\n" + self.report += "--------------------------\n" + self.report += "UNITY FAILED TEST SUMMARY\n" + self.report += "--------------------------\n" + self.report += '\n'.join(failure_output) + + self.report += "\n" + self.report += "--------------------------\n" + self.report += "OVERALL UNITY TEST SUMMARY\n" + self.report += "--------------------------\n" + self.report += "{total_tests} TOTAL TESTS {failures} TOTAL FAILURES {ignored} IGNORED\n".format(total_tests = self.total_tests, failures=self.failures, ignored=self.ignored) + self.report += "\n" + + return self.report + + def set_targets(self, target_array): + self.targets = target_array + + def set_root_path(self, path): + self.root = path + + def usage(self, err_msg=None): + print("\nERROR: ") + if err_msg: + print(err_msg) + print("\nUsage: unity_test_summary.py result_file_directory/ root_path/") + print(" result_file_directory - The location of your results files.") + print(" Defaults to current directory if not specified.") + print(" Should end in / if specified.") + print(" root_path - Helpful for producing more verbose output if using relative paths.") + sys.exit(1) + + def get_details(self, result_file, lines): + results = { 'failures': [], 'ignores': [], 'successes': [] } + for line in lines: + parts = line.split(':') + if len(parts) == 5: + src_file,src_line,test_name,status,msg = parts + elif len(parts) == 4: + src_file,src_line,test_name,status = parts + msg = '' + else: + continue + if len(self.root) > 0: + line_out = "%s%s" % (self.root, line) + else: + line_out = line + if status == 'IGNORE': + results['ignores'].append(line_out) + elif status == 'FAIL': + results['failures'].append(line_out) + elif status == 'PASS': + results['successes'].append(line_out) + return results + + def parse_test_summary(self, summary): + m = re.search(r"([0-9]+) Tests ([0-9]+) Failures ([0-9]+) Ignored", summary) + if not m: + raise Exception("Couldn't parse test results: %s" % summary) + + return int(m.group(1)), int(m.group(2)), int(m.group(3)) + + +if __name__ == '__main__': + uts = UnityTestSummary() + try: + #look in the specified or current directory for result files + if len(sys.argv) > 1: + targets_dir = sys.argv[1] + else: + targets_dir = './' + targets = list(map(lambda x: x.replace('\\', '/'), glob(targets_dir + '*.test*'))) + if len(targets) == 0: + raise Exception("No *.testpass or *.testfail files found in '%s'" % targets_dir) + uts.set_targets(targets) + + #set the root path + if len(sys.argv) > 2: + root_path = sys.argv[2] + else: + root_path = os.path.split(__file__)[0] + uts.set_root_path(root_path) + + #run the summarizer + print(uts.run()) + except Exception as e: + uts.usage(e) diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/auto/unity_test_summary.rb b/components/spotify/cspot/bell/cJSON/tests/unity/auto/unity_test_summary.rb new file mode 100644 index 00000000..b37dc5fa --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/auto/unity_test_summary.rb @@ -0,0 +1,136 @@ +# ========================================== +# Unity Project - A Test Framework for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +# !/usr/bin/ruby +# +# unity_test_summary.rb +# +require 'fileutils' +require 'set' + +class UnityTestSummary + include FileUtils::Verbose + + attr_reader :report, :total_tests, :failures, :ignored + attr_writer :targets, :root + + def initialize(_opts = {}) + @report = '' + @total_tests = 0 + @failures = 0 + @ignored = 0 + end + + def run + # Clean up result file names + results = @targets.map { |target| target.tr('\\', '/') } + + # Dig through each result file, looking for details on pass/fail: + failure_output = [] + ignore_output = [] + + results.each do |result_file| + lines = File.readlines(result_file).map(&:chomp) + + raise "Empty test result file: #{result_file}" if lines.empty? + + output = get_details(result_file, lines) + failure_output << output[:failures] unless output[:failures].empty? + ignore_output << output[:ignores] unless output[:ignores].empty? + tests, failures, ignored = parse_test_summary(lines) + @total_tests += tests + @failures += failures + @ignored += ignored + end + + if @ignored > 0 + @report += "\n" + @report += "--------------------------\n" + @report += "UNITY IGNORED TEST SUMMARY\n" + @report += "--------------------------\n" + @report += ignore_output.flatten.join("\n") + end + + if @failures > 0 + @report += "\n" + @report += "--------------------------\n" + @report += "UNITY FAILED TEST SUMMARY\n" + @report += "--------------------------\n" + @report += failure_output.flatten.join("\n") + end + + @report += "\n" + @report += "--------------------------\n" + @report += "OVERALL UNITY TEST SUMMARY\n" + @report += "--------------------------\n" + @report += "#{@total_tests} TOTAL TESTS #{@failures} TOTAL FAILURES #{@ignored} IGNORED\n" + @report += "\n" + end + + def usage(err_msg = nil) + puts "\nERROR: " + puts err_msg if err_msg + puts "\nUsage: unity_test_summary.rb result_file_directory/ root_path/" + puts ' result_file_directory - The location of your results files.' + puts ' Defaults to current directory if not specified.' + puts ' Should end in / if specified.' + puts ' root_path - Helpful for producing more verbose output if using relative paths.' + exit 1 + end + + protected + + def get_details(_result_file, lines) + results = { failures: [], ignores: [], successes: [] } + lines.each do |line| + _src_file, _src_line, _test_name, status, _msg = line.split(/:/) + line_out = (@root && (@root != 0) ? "#{@root}#{line}" : line).gsub(/\//, '\\') + case status + when 'IGNORE' then results[:ignores] << line_out + when 'FAIL' then results[:failures] << line_out + when 'PASS' then results[:successes] << line_out + end + end + results + end + + def parse_test_summary(summary) + raise "Couldn't parse test results: #{summary}" unless summary.find { |v| v =~ /(\d+) Tests (\d+) Failures (\d+) Ignored/ } + [Regexp.last_match(1).to_i, Regexp.last_match(2).to_i, Regexp.last_match(3).to_i] + end + + def here + File.expand_path(File.dirname(__FILE__)) + end +end + +if $0 == __FILE__ + + # parse out the command options + opts, args = ARGV.partition { |v| v =~ /^--\w+/ } + opts.map! { |v| v[2..-1].to_sym } + + # create an instance to work with + uts = UnityTestSummary.new(opts) + + begin + # look in the specified or current directory for result files + args[0] ||= './' + targets = "#{ARGV[0].tr('\\', '/')}**/*.test*" + results = Dir[targets] + raise "No *.testpass, *.testfail, or *.testresults files found in '#{targets}'" if results.empty? + uts.targets = results + + # set the root path + args[1] ||= Dir.pwd + '/' + uts.root = ARGV[1] + + # run the summarizer + puts uts.run + rescue StandardError => e + uts.usage e.message + end +end diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/auto/unity_to_junit.py b/components/spotify/cspot/bell/cJSON/tests/unity/auto/unity_to_junit.py new file mode 100644 index 00000000..71dd5688 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/auto/unity_to_junit.py @@ -0,0 +1,146 @@ +import sys +import os +from glob import glob + +from pyparsing import * +from junit_xml import TestSuite, TestCase + + +class UnityTestSummary: + def __init__(self): + self.report = '' + self.total_tests = 0 + self.failures = 0 + self.ignored = 0 + self.targets = 0 + self.root = None + self.test_suites = dict() + + def run(self): + # Clean up result file names + results = [] + for target in self.targets: + results.append(target.replace('\\', '/')) + + # Dig through each result file, looking for details on pass/fail: + for result_file in results: + lines = list(map(lambda line: line.rstrip(), open(result_file, "r").read().split('\n'))) + if len(lines) == 0: + raise Exception("Empty test result file: %s" % result_file) + + # define an expression for your file reference + entry_one = Combine( + oneOf(list(alphas)) + ':/' + + Word(alphanums + '_-./')) + + entry_two = Word(printables + ' ', excludeChars=':') + entry = entry_one | entry_two + + delimiter = Literal(':').suppress() + tc_result_line = Group(entry.setResultsName('tc_file_name') + delimiter + entry.setResultsName( + 'tc_line_nr') + delimiter + entry.setResultsName('tc_name') + delimiter + entry.setResultsName( + 'tc_status') + Optional( + delimiter + entry.setResultsName('tc_msg'))).setResultsName("tc_line") + + eol = LineEnd().suppress() + sol = LineStart().suppress() + blank_line = sol + eol + + tc_summary_line = Group(Word(nums).setResultsName("num_of_tests") + "Tests" + Word(nums).setResultsName( + "num_of_fail") + "Failures" + Word(nums).setResultsName("num_of_ignore") + "Ignored").setResultsName( + "tc_summary") + tc_end_line = Or(Literal("FAIL"), Literal('Ok')).setResultsName("tc_result") + + # run it and see... + pp1 = tc_result_line | Optional(tc_summary_line | tc_end_line) + pp1.ignore(blank_line | OneOrMore("-")) + + result = list() + for l in lines: + result.append((pp1.parseString(l)).asDict()) + # delete empty results + result = filter(None, result) + + tc_list = list() + for r in result: + if 'tc_line' in r: + tmp_tc_line = r['tc_line'] + + # get only the file name which will be used as the classname + file_name = tmp_tc_line['tc_file_name'].split('\\').pop().split('/').pop().rsplit('.', 1)[0] + tmp_tc = TestCase(name=tmp_tc_line['tc_name'], classname=file_name) + if 'tc_status' in tmp_tc_line: + if str(tmp_tc_line['tc_status']) == 'IGNORE': + if 'tc_msg' in tmp_tc_line: + tmp_tc.add_skipped_info(message=tmp_tc_line['tc_msg'], + output=r'[File]={0}, [Line]={1}'.format( + tmp_tc_line['tc_file_name'], tmp_tc_line['tc_line_nr'])) + else: + tmp_tc.add_skipped_info(message=" ") + elif str(tmp_tc_line['tc_status']) == 'FAIL': + if 'tc_msg' in tmp_tc_line: + tmp_tc.add_failure_info(message=tmp_tc_line['tc_msg'], + output=r'[File]={0}, [Line]={1}'.format( + tmp_tc_line['tc_file_name'], tmp_tc_line['tc_line_nr'])) + else: + tmp_tc.add_failure_info(message=" ") + + tc_list.append((str(result_file), tmp_tc)) + + for k, v in tc_list: + try: + self.test_suites[k].append(v) + except KeyError: + self.test_suites[k] = [v] + ts = [] + for suite_name in self.test_suites: + ts.append(TestSuite(suite_name, self.test_suites[suite_name])) + + with open('result.xml', 'w') as f: + TestSuite.to_file(f, ts, prettyprint='True', encoding='utf-8') + + return self.report + + def set_targets(self, target_array): + self.targets = target_array + + def set_root_path(self, path): + self.root = path + + @staticmethod + def usage(err_msg=None): + print("\nERROR: ") + if err_msg: + print(err_msg) + print("\nUsage: unity_test_summary.py result_file_directory/ root_path/") + print(" result_file_directory - The location of your results files.") + print(" Defaults to current directory if not specified.") + print(" Should end in / if specified.") + print(" root_path - Helpful for producing more verbose output if using relative paths.") + sys.exit(1) + + +if __name__ == '__main__': + uts = UnityTestSummary() + try: + # look in the specified or current directory for result files + if len(sys.argv) > 1: + targets_dir = sys.argv[1] + else: + targets_dir = './' + targets = list(map(lambda x: x.replace('\\', '/'), glob(targets_dir + '*.test*'))) + if len(targets) == 0: + raise Exception("No *.testpass or *.testfail files found in '%s'" % targets_dir) + uts.set_targets(targets) + + # set the root path + if len(sys.argv) > 2: + root_path = sys.argv[2] + else: + root_path = os.path.split(__file__)[0] + uts.set_root_path(root_path) + + # run the summarizer + print(uts.run()) + except Exception as e: + UnityTestSummary.usage(e) diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/docs/ThrowTheSwitchCodingStandard.md b/components/spotify/cspot/bell/cJSON/tests/unity/docs/ThrowTheSwitchCodingStandard.md new file mode 100644 index 00000000..a85adef3 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/docs/ThrowTheSwitchCodingStandard.md @@ -0,0 +1,207 @@ +# ThrowTheSwitch.org Coding Standard + +Hi. Welcome to the coding standard for ThrowTheSwitch.org. For the most part, +we try to follow these standards to unify our contributors' code into a cohesive +unit (puns intended). You might find places where these standards aren't +followed. We're not perfect. Please be polite where you notice these discrepancies +and we'll try to be polite when we notice yours. + +;) + + +## Why Have A Coding Standard? + +Being consistent makes code easier to understand. We've made an attempt to keep +our standard simple because we also believe that we can only expect someone to +follow something that is understandable. Please do your best. + + +## Our Philosophy + +Before we get into details on syntax, let's take a moment to talk about our +vision for these tools. We're C developers and embedded software developers. +These tools are great to test any C code, but catering to embedded software has +made us more tolerant of compiler quirks. There are a LOT of quirky compilers +out there. By quirky I mean "doesn't follow standards because they feel like +they have a license to do as they wish." + +Our philosophy is "support every compiler we can". Most often, this means that +we aim for writing C code that is standards compliant (often C89... that seems +to be a sweet spot that is almost always compatible). But it also means these +tools are tolerant of things that aren't common. Some that aren't even +compliant. There are configuration options to override the size of standard +types. There are configuration options to force Unity to not use certain +standard library functions. A lot of Unity is configurable and we have worked +hard to make it not TOO ugly in the process. + +Similarly, our tools that parse C do their best. They aren't full C parsers +(yet) and, even if they were, they would still have to accept non-standard +additions like gcc extensions or specifying `@0x1000` to force a variable to +compile to a particular location. It's just what we do, because we like +everything to Just Workâ„¢. + +Speaking of having things Just Workâ„¢, that's our second philosophy. By that, we +mean that we do our best to have EVERY configuration option have a logical +default. We believe that if you're working with a simple compiler and target, +you shouldn't need to configure very much... we try to make the tools guess as +much as they can, but give the user the power to override it when it's wrong. + + +## Naming Things + +Let's talk about naming things. Programming is all about naming things. We name +files, functions, variables, and so much more. While we're not always going to +find the best name for something, we actually put quite a bit of effort into +finding *What Something WANTS to be Called*â„¢. + +When naming things, we more or less follow this hierarchy, the first being the +most important to us (but we do all four whenever possible): +1. Readable +2. Descriptive +3. Consistent +4. Memorable + + +#### Readable + +We want to read our code. This means we like names and flow that are more +naturally read. We try to avoid double negatives. We try to avoid cryptic +abbreviations (sticking to ones we feel are common). + + +#### Descriptive + +We like descriptive names for things, especially functions and variables. +Finding the right name for something is an important endeavor. You might notice +from poking around our code that this often results in names that are a little +longer than the average. Guilty. We're okay with a tiny bit more typing if it +means our code is easier to understand. + +There are two exceptions to this rule that we also stick to as religiously as +possible: + +First, while we realize hungarian notation (and similar systems for encoding +type information into variable names) is providing a more descriptive name, we +feel that (for the average developer) it takes away from readability and +therefore is to be avoided. + +Second, loop counters and other local throw-away variables often have a purpose +which is obvious. There's no need, therefore, to get carried away with complex +naming. We find i, j, and k are better loop counters than loopCounterVar or +whatnot. We only break this rule when we see that more description could improve +understanding of an algorithm. + + +#### Consistent + +We like consistency, but we're not really obsessed with it. We try to name our +configuration macros in a consistent fashion... you'll notice a repeated use of +UNITY_EXCLUDE_BLAH or UNITY_USES_BLAH macros. This helps users avoid having to +remember each macro's details. + + +#### Memorable + +Where ever it doesn't violate the above principles, we try to apply memorable +names. Sometimes this means using something that is simply descriptive, but +often we strive for descriptive AND unique... we like quirky names that stand +out in our memory and are easier to search for. Take a look through the file +names in Ceedling and you'll get a good idea of what we are talking about here. +Why use preprocess when you can use preprocessinator? Or what better describes a +module in charge of invoking tasks during releases than release_invoker? Don't +get carried away. The names are still descriptive and fulfill the above +requirements, but they don't feel stale. + + +## C and C++ Details + +We don't really want to add to the style battles out there. Tabs or spaces? +How many spaces? Where do the braces go? These are age-old questions that will +never be answered... or at least not answered in a way that will make everyone +happy. + +We've decided on our own style preferences. If you'd like to contribute to these +projects (and we hope that you do), then we ask if you do your best to follow +the same. It will only hurt a little. We promise. + + +#### Whitespace + +Our C-style is to use spaces and to use 4 of them per indent level. It's a nice +power-of-2 number that looks decent on a wide screen. We have no more reason +than that. We break that rule when we have lines that wrap (macros or function +arguments or whatnot). When that happens, we like to indent further to line +things up in nice tidy columns. + +```C + if (stuff_happened) + { + do_something(); + } +``` + + +#### Case + +- Files - all lower case with underscores. +- Variables - all lower case with underscores +- Macros - all caps with underscores. +- Typedefs - all caps with underscores. (also ends with _T). +- Functions - camel cased. Usually named ModuleName_FuncName +- Constants and Globals - camel cased. + + +#### Braces + +The left brace is on the next line after the declaration. The right brace is +directly below that. Everything in between in indented one level. If you're +catching an error and you have a one-line, go ahead and to it on the same line. + +```C + while (blah) + { + //Like so. Even if only one line, we use braces. + } +``` + + +#### Comments + +Do you know what we hate? Old-school C block comments. BUT, we're using them +anyway. As we mentioned, our goal is to support every compiler we can, +especially embedded compilers. There are STILL C compilers out there that only +support old-school block comments. So that is what we're using. We apologize. We +think they are ugly too. + + +## Ruby Details + +Is there really such thing as a Ruby coding standard? Ruby is such a free form +language, it seems almost sacrilegious to suggest that people should comply to +one method! We'll keep it really brief! + + +#### Whitespace + +Our Ruby style is to use spaces and to use 2 of them per indent level. It's a +nice power-of-2 number that really grooves with Ruby's compact style. We have no +more reason than that. We break that rule when we have lines that wrap. When +that happens, we like to indent further to line things up in nice tidy columns. + + +#### Case + +- Files - all lower case with underscores. +- Variables - all lower case with underscores +- Classes, Modules, etc - Camel cased. +- Functions - all lower case with underscores +- Constants - all upper case with underscores + + +## Documentation + +Egad. Really? We use markdown and we like pdf files because they can be made to +look nice while still being portable. Good enough? + + +*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)* diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/docs/UnityAssertionsCheatSheetSuitableforPrintingandPossiblyFraming.pdf b/components/spotify/cspot/bell/cJSON/tests/unity/docs/UnityAssertionsCheatSheetSuitableforPrintingandPossiblyFraming.pdf new file mode 100644 index 00000000..28f0c321 Binary files /dev/null and b/components/spotify/cspot/bell/cJSON/tests/unity/docs/UnityAssertionsCheatSheetSuitableforPrintingandPossiblyFraming.pdf differ diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/docs/UnityAssertionsReference.md b/components/spotify/cspot/bell/cJSON/tests/unity/docs/UnityAssertionsReference.md new file mode 100644 index 00000000..2dcf5e3a --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/docs/UnityAssertionsReference.md @@ -0,0 +1,770 @@ +# Unity Assertions Reference + +## Background and Overview + +### Super Condensed Version + +- An assertion establishes truth (i.e. boolean True) for a single condition. +Upon boolean False, an assertion stops execution and reports the failure. +- Unity is mainly a rich collection of assertions and the support to gather up +and easily execute those assertions. +- The structure of Unity allows you to easily separate test assertions from +source code in, well, test code. +- Unity's assertions: +- Come in many, many flavors to handle different C types and assertion cases. +- Use context to provide detailed and helpful failure messages. +- Document types, expected values, and basic behavior in your source code for +free. + + +### Unity Is Several Things But Mainly It's Assertions + +One way to think of Unity is simply as a rich collection of assertions you can +use to establish whether your source code behaves the way you think it does. +Unity provides a framework to easily organize and execute those assertions in +test code separate from your source code. + + +### What's an Assertion? + +At their core, assertions are an establishment of truth - boolean truth. Was this +thing equal to that thing? Does that code doohickey have such-and-such property +or not? You get the idea. Assertions are executable code (to appreciate the big +picture on this read up on the difference between +[link:Dynamic Verification and Static Analysis]). A failing assertion stops +execution and reports an error through some appropriate I/O channel (e.g. +stdout, GUI, file, blinky light). + +Fundamentally, for dynamic verification all you need is a single assertion +mechanism. In fact, that's what the [assert() macro in C's standard library](http://en.wikipedia.org/en/wiki/Assert.h) +is for. So why not just use it? Well, we can do far better in the reporting +department. C's `assert()` is pretty dumb as-is and is particularly poor for +handling common data types like arrays, structs, etc. And, without some other +support, it's far too tempting to litter source code with C's `assert()`'s. It's +generally much cleaner, manageable, and more useful to separate test and source +code in the way Unity facilitates. + + +### Unity's Assertions: Helpful Messages _and_ Free Source Code Documentation + +Asserting a simple truth condition is valuable, but using the context of the +assertion is even more valuable. For instance, if you know you're comparing bit +flags and not just integers, then why not use that context to give explicit, +readable, bit-level feedback when an assertion fails? + +That's what Unity's collection of assertions do - capture context to give you +helpful, meaningful assertion failure messages. In fact, the assertions +themselves also serve as executable documentation about types and values in your +source code. So long as your tests remain current with your source and all those +tests pass, you have a detailed, up-to-date view of the intent and mechanisms in +your source code. And due to a wondrous mystery, well-tested code usually tends +to be well designed code. + + +## Assertion Conventions and Configurations + +### Naming and Parameter Conventions + +The convention of assertion parameters generally follows this order: + + TEST_ASSERT_X( {modifiers}, {expected}, actual, {size/count} ) + +The very simplest assertion possible uses only a single "actual" parameter (e.g. +a simple null check). + +"Actual" is the value being tested and unlike the other parameters in an +assertion construction is the only parameter present in all assertion variants. +"Modifiers" are masks, ranges, bit flag specifiers, floating point deltas. +"Expected" is your expected value (duh) to compare to an "actual" value; it's +marked as an optional parameter because some assertions only need a single +"actual" parameter (e.g. null check). +"Size/count" refers to string lengths, number of array elements, etc. + +Many of Unity's assertions are apparent duplications in that the same data type +is handled by several assertions. The differences among these are in how failure +messages are presented. For instance, a `_HEX` variant of an assertion prints +the expected and actual values of that assertion formatted as hexadecimal. + + +#### TEST_ASSERT_X_MESSAGE Variants + +_All_ assertions are complemented with a variant that includes a simple string +message as a final parameter. The string you specify is appended to an assertion +failure message in Unity output. + +For brevity, the assertion variants with a message parameter are not listed +below. Just tack on `_MESSAGE` as the final component to any assertion name in +the reference list below and add a string as the final parameter. + +_Example:_ + + TEST_ASSERT_X( {modifiers}, {expected}, actual, {size/count} ) + +becomes messageified like thus... + + TEST_ASSERT_X_MESSAGE( {modifiers}, {expected}, actual, {size/count}, message ) + + +#### TEST_ASSERT_X_ARRAY Variants + +Unity provides a collection of assertions for arrays containing a variety of +types. These are documented in the Array section below. These are almost on par +with the `_MESSAGE`variants of Unity's Asserts in that for pretty much any Unity +type assertion you can tack on `_ARRAY` and run assertions on an entire block of +memory. + + TEST_ASSERT_EQUAL_TYPEX_ARRAY( expected, actual, {size/count} ) + +"Expected" is an array itself. +"Size/count" is one or two parameters necessary to establish the number of array +elements and perhaps the length of elements within the array. + +Notes: +- The `_MESSAGE` variant convention still applies here to array assertions. The +`_MESSAGE` variants of the `_ARRAY` assertions have names ending with +`_ARRAY_MESSAGE`. +- Assertions for handling arrays of floating point values are grouped with float +and double assertions (see immediately following section). + + +### TEST_ASSERT_EACH_EQUAL_X Variants + +Unity provides a collection of assertions for arrays containing a variety of +types which can be compared to a single value as well. These are documented in +the Each Equal section below. these are almost on par with the `_MESSAGE` +variants of Unity's Asserts in that for pretty much any Unity type assertion you +can inject _EACH_EQUAL and run assertions on an entire block of memory. + + TEST_ASSERT_EACH_EQUAL_TYPEX( expected, actual, {size/count} ) + +"Expected" is a single value to compare to. +"Actual" is an array where each element will be compared to the expected value. +"Size/count" is one of two parameters necessary to establish the number of array +elements and perhaps the length of elements within the array. + +Notes: +- The `_MESSAGE` variant convention still applies here to Each Equal assertions. +- Assertions for handling Each Equal of floating point values are grouped with +float and double assertions (see immediately following section). + + +### Configuration + +#### Floating Point Support Is Optional + +Support for floating point types is configurable. That is, by defining the +appropriate preprocessor symbols, floats and doubles can be individually enabled +or disabled in Unity code. This is useful for embedded targets with no floating +point math support (i.e. Unity compiles free of errors for fixed point only +platforms). See Unity documentation for specifics. + + +#### Maximum Data Type Width Is Configurable + +Not all targets support 64 bit wide types or even 32 bit wide types. Define the +appropriate preprocessor symbols and Unity will omit all operations from +compilation that exceed the maximum width of your target. See Unity +documentation for specifics. + + +## The Assertions in All Their Blessed Glory + +### Basic Fail and Ignore + +##### `TEST_FAIL()` + +This fella is most often used in special conditions where your test code is +performing logic beyond a simple assertion. That is, in practice, `TEST_FAIL()` +will always be found inside a conditional code block. + +_Examples:_ +- Executing a state machine multiple times that increments a counter your test +code then verifies as a final step. +- Triggering an exception and verifying it (as in Try / Catch / Throw - see the +[CException](https://github.com/ThrowTheSwitch/CException) project). + +##### `TEST_IGNORE()` + +Marks a test case (i.e. function meant to contain test assertions) as ignored. +Usually this is employed as a breadcrumb to come back and implement a test case. +An ignored test case has effects if other assertions are in the enclosing test +case (see Unity documentation for more). + +### Boolean + +##### `TEST_ASSERT (condition)` + +##### `TEST_ASSERT_TRUE (condition)` + +##### `TEST_ASSERT_FALSE (condition)` + +##### `TEST_ASSERT_UNLESS (condition)` + +A simple wording variation on `TEST_ASSERT_FALSE`.The semantics of +`TEST_ASSERT_UNLESS` aid readability in certain test constructions or +conditional statements. + +##### `TEST_ASSERT_NULL (pointer)` + +##### `TEST_ASSERT_NOT_NULL (pointer)` + + +### Signed and Unsigned Integers (of all sizes) + +Large integer sizes can be disabled for build targets that do not support them. +For example, if your target only supports up to 16 bit types, by defining the +appropriate symbols Unity can be configured to omit 32 and 64 bit operations +that would break compilation (see Unity documentation for more). Refer to +Advanced Asserting later in this document for advice on dealing with other word +sizes. + +##### `TEST_ASSERT_EQUAL_INT (expected, actual)` + +##### `TEST_ASSERT_EQUAL_INT8 (expected, actual)` + +##### `TEST_ASSERT_EQUAL_INT16 (expected, actual)` + +##### `TEST_ASSERT_EQUAL_INT32 (expected, actual)` + +##### `TEST_ASSERT_EQUAL_INT64 (expected, actual)` + +##### `TEST_ASSERT_EQUAL (expected, actual)` + +##### `TEST_ASSERT_NOT_EQUAL (expected, actual)` + +##### `TEST_ASSERT_EQUAL_UINT (expected, actual)` + +##### `TEST_ASSERT_EQUAL_UINT8 (expected, actual)` + +##### `TEST_ASSERT_EQUAL_UINT16 (expected, actual)` + +##### `TEST_ASSERT_EQUAL_UINT32 (expected, actual)` + +##### `TEST_ASSERT_EQUAL_UINT64 (expected, actual)` + + +### Unsigned Integers (of all sizes) in Hexadecimal + +All `_HEX` assertions are identical in function to unsigned integer assertions +but produce failure messages with the `expected` and `actual` values formatted +in hexadecimal. Unity output is big endian. + +##### `TEST_ASSERT_EQUAL_HEX (expected, actual)` + +##### `TEST_ASSERT_EQUAL_HEX8 (expected, actual)` + +##### `TEST_ASSERT_EQUAL_HEX16 (expected, actual)` + +##### `TEST_ASSERT_EQUAL_HEX32 (expected, actual)` + +##### `TEST_ASSERT_EQUAL_HEX64 (expected, actual)` + + +### Masked and Bit-level Assertions + +Masked and bit-level assertions produce output formatted in hexadecimal. Unity +output is big endian. + + +##### `TEST_ASSERT_BITS (mask, expected, actual)` + +Only compares the masked (i.e. high) bits of `expected` and `actual` parameters. + + +##### `TEST_ASSERT_BITS_HIGH (mask, actual)` + +Asserts the masked bits of the `actual` parameter are high. + + +##### `TEST_ASSERT_BITS_LOW (mask, actual)` + +Asserts the masked bits of the `actual` parameter are low. + + +##### `TEST_ASSERT_BIT_HIGH (bit, actual)` + +Asserts the specified bit of the `actual` parameter is high. + + +##### `TEST_ASSERT_BIT_LOW (bit, actual)` + +Asserts the specified bit of the `actual` parameter is low. + +### Integer Less Than / Greater Than + +These assertions verify that the `actual` parameter is less than or greater +than `threshold` (exclusive). For example, if the threshold value is 0 for the +greater than assertion will fail if it is 0 or less. + +##### `TEST_ASSERT_GREATER_THAN (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_INT (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_INT8 (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_INT16 (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_INT32 (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_UINT (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_UINT8 (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_UINT16 (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_UINT32 (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_HEX8 (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_HEX16 (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_HEX32 (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_INT (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_INT8 (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_INT16 (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_INT32 (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_UINT (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_UINT8 (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_UINT16 (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_UINT32 (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_HEX8 (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_HEX16 (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_HEX32 (threshold, actual)` + + +### Integer Ranges (of all sizes) + +These assertions verify that the `expected` parameter is within +/- `delta` +(inclusive) of the `actual` parameter. For example, if the expected value is 10 +and the delta is 3 then the assertion will fail for any value outside the range +of 7 - 13. + +##### `TEST_ASSERT_INT_WITHIN (delta, expected, actual)` + +##### `TEST_ASSERT_INT8_WITHIN (delta, expected, actual)` + +##### `TEST_ASSERT_INT16_WITHIN (delta, expected, actual)` + +##### `TEST_ASSERT_INT32_WITHIN (delta, expected, actual)` + +##### `TEST_ASSERT_INT64_WITHIN (delta, expected, actual)` + +##### `TEST_ASSERT_UINT_WITHIN (delta, expected, actual)` + +##### `TEST_ASSERT_UINT8_WITHIN (delta, expected, actual)` + +##### `TEST_ASSERT_UINT16_WITHIN (delta, expected, actual)` + +##### `TEST_ASSERT_UINT32_WITHIN (delta, expected, actual)` + +##### `TEST_ASSERT_UINT64_WITHIN (delta, expected, actual)` + +##### `TEST_ASSERT_HEX_WITHIN (delta, expected, actual)` + +##### `TEST_ASSERT_HEX8_WITHIN (delta, expected, actual)` + +##### `TEST_ASSERT_HEX16_WITHIN (delta, expected, actual)` + +##### `TEST_ASSERT_HEX32_WITHIN (delta, expected, actual)` + +##### `TEST_ASSERT_HEX64_WITHIN (delta, expected, actual)` + + +### Structs and Strings + +##### `TEST_ASSERT_EQUAL_PTR (expected, actual)` + +Asserts that the pointers point to the same memory location. + + +##### `TEST_ASSERT_EQUAL_STRING (expected, actual)` + +Asserts that the null terminated (`'\0'`)strings are identical. If strings are +of different lengths or any portion of the strings before their terminators +differ, the assertion fails. Two NULL strings (i.e. zero length) are considered +equivalent. + + +##### `TEST_ASSERT_EQUAL_MEMORY (expected, actual, len)` + +Asserts that the contents of the memory specified by the `expected` and `actual` +pointers is identical. The size of the memory blocks in bytes is specified by +the `len` parameter. + + +### Arrays + +`expected` and `actual` parameters are both arrays. `num_elements` specifies the +number of elements in the arrays to compare. + +`_HEX` assertions produce failure messages with expected and actual array +contents formatted in hexadecimal. + +For array of strings comparison behavior, see comments for +`TEST_ASSERT_EQUAL_STRING` in the preceding section. + +Assertions fail upon the first element in the compared arrays found not to +match. Failure messages specify the array index of the failed comparison. + +##### `TEST_ASSERT_EQUAL_INT_ARRAY (expected, actual, num_elements)` + +##### `TEST_ASSERT_EQUAL_INT8_ARRAY (expected, actual, num_elements)` + +##### `TEST_ASSERT_EQUAL_INT16_ARRAY (expected, actual, num_elements)` + +##### `TEST_ASSERT_EQUAL_INT32_ARRAY (expected, actual, num_elements)` + +##### `TEST_ASSERT_EQUAL_INT64_ARRAY (expected, actual, num_elements)` + +##### `TEST_ASSERT_EQUAL_UINT_ARRAY (expected, actual, num_elements)` + +##### `TEST_ASSERT_EQUAL_UINT8_ARRAY (expected, actual, num_elements)` + +##### `TEST_ASSERT_EQUAL_UINT16_ARRAY (expected, actual, num_elements)` + +##### `TEST_ASSERT_EQUAL_UINT32_ARRAY (expected, actual, num_elements)` + +##### `TEST_ASSERT_EQUAL_UINT64_ARRAY (expected, actual, num_elements)` + +##### `TEST_ASSERT_EQUAL_HEX_ARRAY (expected, actual, num_elements)` + +##### `TEST_ASSERT_EQUAL_HEX8_ARRAY (expected, actual, num_elements)` + +##### `TEST_ASSERT_EQUAL_HEX16_ARRAY (expected, actual, num_elements)` + +##### `TEST_ASSERT_EQUAL_HEX32_ARRAY (expected, actual, num_elements)` + +##### `TEST_ASSERT_EQUAL_HEX64_ARRAY (expected, actual, num_elements)` + +##### `TEST_ASSERT_EQUAL_PTR_ARRAY (expected, actual, num_elements)` + +##### `TEST_ASSERT_EQUAL_STRING_ARRAY (expected, actual, num_elements)` + +##### `TEST_ASSERT_EQUAL_MEMORY_ARRAY (expected, actual, len, num_elements)` + +`len` is the memory in bytes to be compared at each array element. + + +### Each Equal (Arrays to Single Value) + +`expected` are single values and `actual` are arrays. `num_elements` specifies +the number of elements in the arrays to compare. + +`_HEX` assertions produce failure messages with expected and actual array +contents formatted in hexadecimal. + +Assertions fail upon the first element in the compared arrays found not to +match. Failure messages specify the array index of the failed comparison. + +#### `TEST_ASSERT_EACH_EQUAL_INT (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_INT8 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_INT16 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_INT32 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_INT64 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_UINT (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_UINT8 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_UINT16 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_UINT32 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_UINT64 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_HEX (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_HEX8 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_HEX16 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_HEX32 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_HEX64 (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_PTR (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_STRING (expected, actual, num_elements)` + +#### `TEST_ASSERT_EACH_EQUAL_MEMORY (expected, actual, len, num_elements)` + +`len` is the memory in bytes to be compared at each array element. + + +### Floating Point (If enabled) + +##### `TEST_ASSERT_FLOAT_WITHIN (delta, expected, actual)` + +Asserts that the `actual` value is within +/- `delta` of the `expected` value. +The nature of floating point representation is such that exact evaluations of +equality are not guaranteed. + + +##### `TEST_ASSERT_EQUAL_FLOAT (expected, actual)` + +Asserts that the ?actual?value is "close enough to be considered equal" to the +`expected` value. If you are curious about the details, refer to the Advanced +Asserting section for more details on this. Omitting a user-specified delta in a +floating point assertion is both a shorthand convenience and a requirement of +code generation conventions for CMock. + + +##### `TEST_ASSERT_EQUAL_FLOAT_ARRAY (expected, actual, num_elements)` + +See Array assertion section for details. Note that individual array element +float comparisons are executed using T?EST_ASSERT_EQUAL_FLOAT?.That is, user +specified delta comparison values requires a custom-implemented floating point +array assertion. + + +##### `TEST_ASSERT_FLOAT_IS_INF (actual)` + +Asserts that `actual` parameter is equivalent to positive infinity floating +point representation. + + +##### `TEST_ASSERT_FLOAT_IS_NEG_INF (actual)` + +Asserts that `actual` parameter is equivalent to negative infinity floating +point representation. + + +##### `TEST_ASSERT_FLOAT_IS_NAN (actual)` + +Asserts that `actual` parameter is a Not A Number floating point representation. + + +##### `TEST_ASSERT_FLOAT_IS_DETERMINATE (actual)` + +Asserts that ?actual?parameter is a floating point representation usable for +mathematical operations. That is, the `actual` parameter is neither positive +infinity nor negative infinity nor Not A Number floating point representations. + + +##### `TEST_ASSERT_FLOAT_IS_NOT_INF (actual)` + +Asserts that `actual` parameter is a value other than positive infinity floating +point representation. + + +##### `TEST_ASSERT_FLOAT_IS_NOT_NEG_INF (actual)` + +Asserts that `actual` parameter is a value other than negative infinity floating +point representation. + + +##### `TEST_ASSERT_FLOAT_IS_NOT_NAN (actual)` + +Asserts that `actual` parameter is a value other than Not A Number floating +point representation. + + +##### `TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE (actual)` + +Asserts that `actual` parameter is not usable for mathematical operations. That +is, the `actual` parameter is either positive infinity or negative infinity or +Not A Number floating point representations. + + +### Double (If enabled) + +##### `TEST_ASSERT_DOUBLE_WITHIN (delta, expected, actual)` + +Asserts that the `actual` value is within +/- `delta` of the `expected` value. +The nature of floating point representation is such that exact evaluations of +equality are not guaranteed. + + +##### `TEST_ASSERT_EQUAL_DOUBLE (expected, actual)` + +Asserts that the `actual` value is "close enough to be considered equal" to the +`expected` value. If you are curious about the details, refer to the Advanced +Asserting section for more details. Omitting a user-specified delta in a +floating point assertion is both a shorthand convenience and a requirement of +code generation conventions for CMock. + + +##### `TEST_ASSERT_EQUAL_DOUBLE_ARRAY (expected, actual, num_elements)` + +See Array assertion section for details. Note that individual array element +double comparisons are executed using `TEST_ASSERT_EQUAL_DOUBLE`.That is, user +specified delta comparison values requires a custom implemented double array +assertion. + + +##### `TEST_ASSERT_DOUBLE_IS_INF (actual)` + +Asserts that `actual` parameter is equivalent to positive infinity floating +point representation. + + +##### `TEST_ASSERT_DOUBLE_IS_NEG_INF (actual)` + +Asserts that `actual` parameter is equivalent to negative infinity floating point +representation. + + +##### `TEST_ASSERT_DOUBLE_IS_NAN (actual)` + +Asserts that `actual` parameter is a Not A Number floating point representation. + + +##### `TEST_ASSERT_DOUBLE_IS_DETERMINATE (actual)` + +Asserts that `actual` parameter is a floating point representation usable for +mathematical operations. That is, the ?actual?parameter is neither positive +infinity nor negative infinity nor Not A Number floating point representations. + + +##### `TEST_ASSERT_DOUBLE_IS_NOT_INF (actual)` + +Asserts that `actual` parameter is a value other than positive infinity floating +point representation. + + +##### `TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF (actual)` + +Asserts that `actual` parameter is a value other than negative infinity floating +point representation. + + +##### `TEST_ASSERT_DOUBLE_IS_NOT_NAN (actual)` + +Asserts that `actual` parameter is a value other than Not A Number floating +point representation. + + +##### `TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE (actual)` + +Asserts that `actual` parameter is not usable for mathematical operations. That +is, the `actual` parameter is either positive infinity or negative infinity or +Not A Number floating point representations. + + +## Advanced Asserting: Details On Tricky Assertions + +This section helps you understand how to deal with some of the trickier +assertion situations you may run into. It will give you a glimpse into some of +the under-the-hood details of Unity's assertion mechanisms. If you're one of +those people who likes to know what is going on in the background, read on. If +not, feel free to ignore the rest of this document until you need it. + + +### How do the EQUAL assertions work for FLOAT and DOUBLE? + +As you may know, directly checking for equality between a pair of floats or a +pair of doubles is sloppy at best and an outright no-no at worst. Floating point +values can often be represented in multiple ways, particularly after a series of +operations on a value. Initializing a variable to the value of 2.0 is likely to +result in a floating point representation of 2 x 20,but a series of +mathematical operations might result in a representation of 8 x 2-2 +that also evaluates to a value of 2. At some point repeated operations cause +equality checks to fail. + +So Unity doesn't do direct floating point comparisons for equality. Instead, it +checks if two floating point values are "really close." If you leave Unity +running with defaults, "really close" means "within a significant bit or two." +Under the hood, `TEST_ASSERT_EQUAL_FLOAT` is really `TEST_ASSERT_FLOAT_WITHIN` +with the `delta` parameter calculated on the fly. For single precision, delta is +the expected value multiplied by 0.00001, producing a very small proportional +range around the expected value. + +If you are expecting a value of 20,000.0 the delta is calculated to be 0.2. So +any value between 19,999.8 and 20,000.2 will satisfy the equality check. This +works out to be roughly a single bit of range for a single-precision number, and +that's just about as tight a tolerance as you can reasonably get from a floating +point value. + +So what happens when it's zero? Zero - even more than other floating point +values - can be represented many different ways. It doesn't matter if you have +0 x 20or 0 x 263.It's still zero, right? Luckily, if you +subtract these values from each other, they will always produce a difference of +zero, which will still fall between 0 plus or minus a delta of 0. So it still +works! + +Double precision floating point numbers use a much smaller multiplier, again +approximating a single bit of error. + +If you don't like these ranges and you want to make your floating point equality +assertions less strict, you can change these multipliers to whatever you like by +defining UNITY_FLOAT_PRECISION and UNITY_DOUBLE_PRECISION. See Unity +documentation for more. + + +### How do we deal with targets with non-standard int sizes? + +It's "fun" that C is a standard where something as fundamental as an integer +varies by target. According to the C standard, an `int` is to be the target's +natural register size, and it should be at least 16-bits and a multiple of a +byte. It also guarantees an order of sizes: + +```C +char <= short <= int <= long <= long long +``` + +Most often, `int` is 32-bits. In many cases in the embedded world, `int` is +16-bits. There are rare microcontrollers out there that have 24-bit integers, +and this remains perfectly standard C. + +To make things even more interesting, there are compilers and targets out there +that have a hard choice to make. What if their natural register size is 10-bits +or 12-bits? Clearly they can't fulfill _both_ the requirement to be at least +16-bits AND the requirement to match the natural register size. In these +situations, they often choose the natural register size, leaving us with +something like this: + +```C +char (8 bit) <= short (12 bit) <= int (12 bit) <= long (16 bit) +``` + +Um... yikes. It's obviously breaking a rule or two... but they had to break SOME +rules, so they made a choice. + +When the C99 standard rolled around, it introduced alternate standard-size types. +It also introduced macros for pulling in MIN/MAX values for your integer types. +It's glorious! Unfortunately, many embedded compilers can't be relied upon to +use the C99 types (Sometimes because they have weird register sizes as described +above. Sometimes because they don't feel like it?). + +A goal of Unity from the beginning was to support every combination of +microcontroller or microprocessor and C compiler. Over time, we've gotten really +close to this. There are a few tricks that you should be aware of, though, if +you're going to do this effectively on some of these more idiosyncratic targets. + +First, when setting up Unity for a new target, you're going to want to pay +special attention to the macros for automatically detecting types +(where available) or manually configuring them yourself. You can get information +on both of these in Unity's documentation. + +What about the times where you suddenly need to deal with something odd, like a +24-bit `int`? The simplest solution is to use the next size up. If you have a +24-bit `int`, configure Unity to use 32-bit integers. If you have a 12-bit +`int`, configure Unity to use 16 bits. There are two ways this is going to +affect you: + +1. When Unity displays errors for you, it's going to pad the upper unused bits +with zeros. +2. You're going to have to be careful of assertions that perform signed +operations, particularly `TEST_ASSERT_INT_WITHIN`.Such assertions might wrap +your `int` in the wrong place, and you could experience false failures. You can +always back down to a simple `TEST_ASSERT` and do the operations yourself. + + +*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)* diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/docs/UnityConfigurationGuide.md b/components/spotify/cspot/bell/cJSON/tests/unity/docs/UnityConfigurationGuide.md new file mode 100644 index 00000000..96e5358d --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/docs/UnityConfigurationGuide.md @@ -0,0 +1,435 @@ +# Unity Configuration Guide + +## C Standards, Compilers and Microcontrollers + +The embedded software world contains its challenges. Compilers support different +revisions of the C Standard. They ignore requirements in places, sometimes to +make the language more usable in some special regard. Sometimes it's to simplify +their support. Sometimes it's due to specific quirks of the microcontroller they +are targeting. Simulators add another dimension to this menagerie. + +Unity is designed to run on almost anything that is targeted by a C compiler. It +would be awesome if this could be done with zero configuration. While there are +some targets that come close to this dream, it is sadly not universal. It is +likely that you are going to need at least a couple of the configuration options +described in this document. + +All of Unity's configuration options are `#defines`. Most of these are simple +definitions. A couple are macros with arguments. They live inside the +unity_internals.h header file. We don't necessarily recommend opening that file +unless you really need to. That file is proof that a cross-platform library is +challenging to build. From a more positive perspective, it is also proof that a +great deal of complexity can be centralized primarily to one place in order to +provide a more consistent and simple experience elsewhere. + + +### Using These Options + +It doesn't matter if you're using a target-specific compiler and a simulator or +a native compiler. In either case, you've got a couple choices for configuring +these options: + +1. Because these options are specified via C defines, you can pass most of these +options to your compiler through command line compiler flags. Even if you're +using an embedded target that forces you to use their overbearing IDE for all +configuration, there will be a place somewhere in your project to configure +defines for your compiler. +2. You can create a custom `unity_config.h` configuration file (present in your +toolchain's search paths). In this file, you will list definitions and macros +specific to your target. All you must do is define `UNITY_INCLUDE_CONFIG_H` and +Unity will rely on `unity_config.h` for any further definitions it may need. + + +## The Options + +### Integer Types + +If you've been a C developer for long, you probably already know that C's +concept of an integer varies from target to target. The C Standard has rules +about the `int` matching the register size of the target microprocessor. It has +rules about the `int` and how its size relates to other integer types. An `int` +on one target might be 16 bits while on another target it might be 64. There are +more specific types in compilers compliant with C99 or later, but that's +certainly not every compiler you are likely to encounter. Therefore, Unity has a +number of features for helping to adjust itself to match your required integer +sizes. It starts off by trying to do it automatically. + + +##### `UNITY_EXCLUDE_STDINT_H` + +The first thing that Unity does to guess your types is check `stdint.h`. +This file includes defines like `UINT_MAX` that Unity can make use of to +learn a lot about your system. It's possible you don't want it to do this +(um. why not?) or (more likely) it's possible that your system doesn't +support `stdint.h`. If that's the case, you're going to want to define this. +That way, Unity will know to skip the inclusion of this file and you won't +be left with a compiler error. + +_Example:_ + #define UNITY_EXCLUDE_STDINT_H + + +##### `UNITY_EXCLUDE_LIMITS_H` + +The second attempt to guess your types is to check `limits.h`. Some compilers +that don't support `stdint.h` could include `limits.h` instead. If you don't +want Unity to check this file either, define this to make it skip the inclusion. + +_Example:_ + #define UNITY_EXCLUDE_LIMITS_H + + +If you've disabled both of the automatic options above, you're going to have to +do the configuration yourself. Don't worry. Even this isn't too bad... there are +just a handful of defines that you are going to specify if you don't like the +defaults. + + +##### `UNITY_INT_WIDTH` + +Define this to be the number of bits an `int` takes up on your system. The +default, if not autodetected, is 32 bits. + +_Example:_ + #define UNITY_INT_WIDTH 16 + + +##### `UNITY_LONG_WIDTH` + +Define this to be the number of bits a `long` takes up on your system. The +default, if not autodetected, is 32 bits. This is used to figure out what kind +of 64-bit support your system can handle. Does it need to specify a `long` or a +`long long` to get a 64-bit value. On 16-bit systems, this option is going to be +ignored. + +_Example:_ + #define UNITY_LONG_WIDTH 16 + + +##### `UNITY_POINTER_WIDTH` + +Define this to be the number of bits a pointer takes up on your system. The +default, if not autodetected, is 32-bits. If you're getting ugly compiler +warnings about casting from pointers, this is the one to look at. + +_Example:_ + #define UNITY_POINTER_WIDTH 64 + + +##### `UNITY_SUPPORT_64` + +Unity will automatically include 64-bit support if it auto-detects it, or if +your `int`, `long`, or pointer widths are greater than 32-bits. Define this to +enable 64-bit support if none of the other options already did it for you. There +can be a significant size and speed impact to enabling 64-bit support on small +targets, so don't define it if you don't need it. + +_Example:_ + #define UNITY_SUPPORT_64 + + +### Floating Point Types + +In the embedded world, it's not uncommon for targets to have no support for +floating point operations at all or to have support that is limited to only +single precision. We are able to guess integer sizes on the fly because integers +are always available in at least one size. Floating point, on the other hand, is +sometimes not available at all. Trying to include `float.h` on these platforms +would result in an error. This leaves manual configuration as the only option. + + +##### `UNITY_INCLUDE_FLOAT` + +##### `UNITY_EXCLUDE_FLOAT` + +##### `UNITY_INCLUDE_DOUBLE` + +##### `UNITY_EXCLUDE_DOUBLE` + +By default, Unity guesses that you will want single precision floating point +support, but not double precision. It's easy to change either of these using the +include and exclude options here. You may include neither, either, or both, as +suits your needs. For features that are enabled, the following floating point +options also become available. + +_Example:_ + + //what manner of strange processor is this? + #define UNITY_EXCLUDE_FLOAT + #define UNITY_INCLUDE_DOUBLE + + +##### `UNITY_EXCLUDE_FLOAT_PRINT` + +Unity aims for as small of a footprint as possible and avoids most standard +library calls (some embedded platforms don’t have a standard library!). Because +of this, its routines for printing integer values are minimalist and hand-coded. +Therefore, the display of floating point values during a failure are optional. +By default, Unity will print the actual results of floating point assertion +failure (e.g. â€Expected 4.56 Was 4.68â€). To not include this extra support, you +can use this define to instead respond to a failed assertion with a message like +â€Values Not Within Deltaâ€. If you would like verbose failure messages for floating +point assertions, use these options to give more explicit failure messages. + +_Example:_ + #define UNITY_EXCLUDE_FLOAT_PRINT + + +##### `UNITY_FLOAT_TYPE` + +If enabled, Unity assumes you want your `FLOAT` asserts to compare standard C +floats. If your compiler supports a specialty floating point type, you can +always override this behavior by using this definition. + +_Example:_ + #define UNITY_FLOAT_TYPE float16_t + + +##### `UNITY_DOUBLE_TYPE` + +If enabled, Unity assumes you want your `DOUBLE` asserts to compare standard C +doubles. If you would like to change this, you can specify something else by +using this option. For example, defining `UNITY_DOUBLE_TYPE` to `long double` +could enable gargantuan floating point types on your 64-bit processor instead of +the standard `double`. + +_Example:_ + #define UNITY_DOUBLE_TYPE long double + + +##### `UNITY_FLOAT_PRECISION` + +##### `UNITY_DOUBLE_PRECISION` + +If you look up `UNITY_ASSERT_EQUAL_FLOAT` and `UNITY_ASSERT_EQUAL_DOUBLE` as +documented in the big daddy Unity Assertion Guide, you will learn that they are +not really asserting that two values are equal but rather that two values are +"close enough" to equal. "Close enough" is controlled by these precision +configuration options. If you are working with 32-bit floats and/or 64-bit +doubles (the normal on most processors), you should have no need to change these +options. They are both set to give you approximately 1 significant bit in either +direction. The float precision is 0.00001 while the double is 10-12. +For further details on how this works, see the appendix of the Unity Assertion +Guide. + +_Example:_ + #define UNITY_FLOAT_PRECISION 0.001f + + +### Toolset Customization + +In addition to the options listed above, there are a number of other options +which will come in handy to customize Unity's behavior for your specific +toolchain. It is possible that you may not need to touch any of these... but +certain platforms, particularly those running in simulators, may need to jump +through extra hoops to operate properly. These macros will help in those +situations. + + +##### `UNITY_OUTPUT_CHAR(a)` + +##### `UNITY_OUTPUT_FLUSH()` + +##### `UNITY_OUTPUT_START()` + +##### `UNITY_OUTPUT_COMPLETE()` + +By default, Unity prints its results to `stdout` as it runs. This works +perfectly fine in most situations where you are using a native compiler for +testing. It works on some simulators as well so long as they have `stdout` +routed back to the command line. There are times, however, where the simulator +will lack support for dumping results or you will want to route results +elsewhere for other reasons. In these cases, you should define the +`UNITY_OUTPUT_CHAR` macro. This macro accepts a single character at a time (as +an `int`, since this is the parameter type of the standard C `putchar` function +most commonly used). You may replace this with whatever function call you like. + +_Example:_ +Say you are forced to run your test suite on an embedded processor with no +`stdout` option. You decide to route your test result output to a custom serial +`RS232_putc()` function you wrote like thus: + + #define UNITY_OUTPUT_CHAR(a) RS232_putc(a) + #define UNITY_OUTPUT_START() RS232_config(115200,1,8,0) + #define UNITY_OUTPUT_FLUSH() RS232_flush() + #define UNITY_OUTPUT_COMPLETE() RS232_close() + +_Note:_ +`UNITY_OUTPUT_FLUSH()` can be set to the standard out flush function simply by +specifying `UNITY_USE_FLUSH_STDOUT`. No other defines are required. If you +specify a custom flush function instead with `UNITY_OUTPUT_FLUSH` directly, it +will declare an instance of your function by default. If you want to disable +this behavior, add `UNITY_OMIT_OUTPUT_FLUSH_HEADER_DECLARATION`. + + +##### `UNITY_WEAK_ATTRIBUTE` + +##### `UNITY_WEAK_PRAGMA` + +##### `UNITY_NO_WEAK` + +For some targets, Unity can make the otherwise required setUp() and tearDown() +functions optional. This is a nice convenience for test writers since setUp and +tearDown don’t often actually do anything. If you’re using gcc or clang, this +option is automatically defined for you. Other compilers can also support this +behavior, if they support a C feature called weak functions. A weak function is +a function that is compiled into your executable unless a non-weak version of +the same function is defined elsewhere. If a non-weak version is found, the weak +version is ignored as if it never existed. If your compiler supports this feature, +you can let Unity know by defining UNITY_WEAK_ATTRIBUTE or UNITY_WEAK_PRAGMA as +the function attributes that would need to be applied to identify a function as +weak. If your compiler lacks support for weak functions, you will always need to +define setUp and tearDown functions (though they can be and often will be just +empty). You can also force Unity to NOT use weak functions by defining +UNITY_NO_WEAK. The most common options for this feature are: + +_Example:_ + #define UNITY_WEAK_ATTRIBUTE weak + #define UNITY_WEAK_ATTRIBUTE __attribute__((weak)) + #define UNITY_WEAK_PRAGMA + #define UNITY_NO_WEAK + + +##### `UNITY_PTR_ATTRIBUTE` + +Some compilers require a custom attribute to be assigned to pointers, like +`near` or `far`. In these cases, you can give Unity a safe default for these by +defining this option with the attribute you would like. + +_Example:_ + #define UNITY_PTR_ATTRIBUTE __attribute__((far)) + #define UNITY_PTR_ATTRIBUTE near + + +##### `UNITY_PRINT_EOL` + +By default, Unity outputs \n at the end of each line of output. This is easy +to parse by the scripts, by Ceedling, etc, but it might not be ideal for YOUR +system. Feel free to override this and to make it whatever you wish. + +_Example:_ + #define UNITY_PRINT_EOL { UNITY_OUTPUT_CHAR('\r'); UNITY_OUTPUT_CHAR('\n') } + + + +##### `UNITY_EXCLUDE_DETAILS` + +This is an option for if you absolutely must squeeze every byte of memory out of +your system. Unity stores a set of internal scratchpads which are used to pass +extra detail information around. It's used by systems like CMock in order to +report which function or argument flagged an error. If you're not using CMock and +you're not using these details for other things, then you can exclude them. + +_Example:_ + #define UNITY_EXCLUDE_DETAILS + + + +##### `UNITY_EXCLUDE_SETJMP` + +If your embedded system doesn't support the standard library setjmp, you can +exclude Unity's reliance on this by using this define. This dropped dependence +comes at a price, though. You will be unable to use custom helper functions for +your tests, and you will be unable to use tools like CMock. Very likely, if your +compiler doesn't support setjmp, you wouldn't have had the memory space for those +things anyway, though... so this option exists for those situations. + +_Example:_ + #define UNITY_EXCLUDE_SETJMP + +##### `UNITY_OUTPUT_COLOR` + +If you want to add color using ANSI escape codes you can use this define. +t +_Example:_ + #define UNITY_OUTPUT_COLOR + + + +## Getting Into The Guts + +There will be cases where the options above aren't quite going to get everything +perfect. They are likely sufficient for any situation where you are compiling +and executing your tests with a native toolchain (e.g. clang on Mac). These +options may even get you through the majority of cases encountered in working +with a target simulator run from your local command line. But especially if you +must run your test suite on your target hardware, your Unity configuration will +require special help. This special help will usually reside in one of two +places: the `main()` function or the `RUN_TEST` macro. Let's look at how these +work. + + +##### `main()` + +Each test module is compiled and run on its own, separate from the other test +files in your project. Each test file, therefore, has a `main` function. This +`main` function will need to contain whatever code is necessary to initialize +your system to a workable state. This is particularly true for situations where +you must set up a memory map or initialize a communication channel for the +output of your test results. + +A simple main function looks something like this: + + int main(void) { + UNITY_BEGIN(); + RUN_TEST(test_TheFirst); + RUN_TEST(test_TheSecond); + RUN_TEST(test_TheThird); + return UNITY_END(); + } + +You can see that our main function doesn't bother taking any arguments. For our +most barebones case, we'll never have arguments because we just run all the +tests each time. Instead, we start by calling `UNITY_BEGIN`. We run each test +(in whatever order we wish). Finally, we call `UNITY_END`, returning its return +value (which is the total number of failures). + +It should be easy to see that you can add code before any test cases are run or +after all the test cases have completed. This allows you to do any needed +system-wide setup or teardown that might be required for your special +circumstances. + + +##### `RUN_TEST` + +The `RUN_TEST` macro is called with each test case function. Its job is to +perform whatever setup and teardown is necessary for executing a single test +case function. This includes catching failures, calling the test module's +`setUp()` and `tearDown()` functions, and calling `UnityConcludeTest()`. If +using CMock or test coverage, there will be additional stubs in use here. A +simple minimalist RUN_TEST macro looks something like this: + + #define RUN_TEST(testfunc) \ + UNITY_NEW_TEST(#testfunc) \ + if (TEST_PROTECT()) { \ + setUp(); \ + testfunc(); \ + } \ + if (TEST_PROTECT() && (!TEST_IS_IGNORED)) \ + tearDown(); \ + UnityConcludeTest(); + +So that's quite a macro, huh? It gives you a glimpse of what kind of stuff Unity +has to deal with for every single test case. For each test case, we declare that +it is a new test. Then we run `setUp` and our test function. These are run +within a `TEST_PROTECT` block, the function of which is to handle failures that +occur during the test. Then, assuming our test is still running and hasn't been +ignored, we run `tearDown`. No matter what, our last step is to conclude this +test before moving on to the next. + +Let's say you need to add a call to `fsync` to force all of your output data to +flush to a file after each test. You could easily insert this after your +`UnityConcludeTest` call. Maybe you want to write an xml tag before and after +each result set. Again, you could do this by adding lines to this macro. Updates +to this macro are for the occasions when you need an action before or after +every single test case throughout your entire suite of tests. + + +## Happy Porting + +The defines and macros in this guide should help you port Unity to just about +any C target we can imagine. If you run into a snag or two, don't be afraid of +asking for help on the forums. We love a good challenge! + + +*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)* diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/docs/UnityGettingStartedGuide.md b/components/spotify/cspot/bell/cJSON/tests/unity/docs/UnityGettingStartedGuide.md new file mode 100644 index 00000000..50fc91c7 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/docs/UnityGettingStartedGuide.md @@ -0,0 +1,191 @@ +# Unity - Getting Started + +## Welcome + +Congratulations. You're now the proud owner of your very own pile of bits! What +are you going to do with all these ones and zeros? This document should be able +to help you decide just that. + +Unity is a unit test framework. The goal has been to keep it small and +functional. The core Unity test framework is three files: a single C file and a +couple header files. These team up to provide functions and macros to make +testing easier. + +Unity was designed to be cross platform. It works hard to stick with C standards +while still providing support for the many embedded C compilers that bend the +rules. Unity has been used with many compilers, including GCC, IAR, Clang, +Green Hills, Microchip, and MS Visual Studio. It's not much work to get it to +work with a new target. + + +### Overview of the Documents + +#### Unity Assertions reference + +This document will guide you through all the assertion options provided by +Unity. This is going to be your unit testing bread and butter. You'll spend more +time with assertions than any other part of Unity. + + +#### Unity Assertions Cheat Sheet + +This document contains an abridged summary of the assertions described in the +previous document. It's perfect for printing and referencing while you +familiarize yourself with Unity's options. + + +#### Unity Configuration Guide + +This document is the one to reference when you are going to use Unity with a new +target or compiler. It'll guide you through the configuration options and will +help you customize your testing experience to meet your needs. + + +#### Unity Helper Scripts + +This document describes the helper scripts that are available for simplifying +your testing workflow. It describes the collection of optional Ruby scripts +included in the auto directory of your Unity installation. Neither Ruby nor +these scripts are necessary for using Unity. They are provided as a convenience +for those who wish to use them. + + +#### Unity License + +What's an open source project without a license file? This brief document +describes the terms you're agreeing to when you use this software. Basically, we +want it to be useful to you in whatever context you want to use it, but please +don't blame us if you run into problems. + + +### Overview of the Folders + +If you have obtained Unity through Github or something similar, you might be +surprised by just how much stuff you suddenly have staring you in the face. +Don't worry, Unity itself is very small. The rest of it is just there to make +your life easier. You can ignore it or use it at your convenience. Here's an +overview of everything in the project. + +- `src` - This is the code you care about! This folder contains a C file and two +header files. These three files _are_ Unity. +- `docs` - You're reading this document, so it's possible you have found your way +into this folder already. This is where all the handy documentation can be +found. +- `examples` - This contains a few examples of using Unity. +- `extras` - These are optional add ons to Unity that are not part of the core +project. If you've reached us through James Grenning's book, you're going to +want to look here. +- `test` - This is how Unity and its scripts are all tested. If you're just using +Unity, you'll likely never need to go in here. If you are the lucky team member +who gets to port Unity to a new toolchain, this is a good place to verify +everything is configured properly. +- `auto` - Here you will find helpful Ruby scripts for simplifying your test +workflow. They are purely optional and are not required to make use of Unity. + + +## How to Create A Test File + +Test files are C files. Most often you will create a single test file for each C +module that you want to test. The test file should include unity.h and the +header for your C module to be tested. + +Next, a test file will include a `setUp()` and `tearDown()` function. The setUp +function can contain anything you would like to run before each test. The +tearDown function can contain anything you would like to run after each test. +Both functions accept no arguments and return nothing. You may leave either or +both of these blank if you have no need for them. If you're using a compiler +that is configured to make these functions optional, you may leave them off +completely. Not sure? Give it a try. If you compiler complains that it can't +find setUp or tearDown when it links, you'll know you need to at least include +an empty function for these. + +The majority of the file will be a series of test functions. Test functions +follow the convention of starting with the word "test" or "spec". You don't HAVE +to name them this way, but it makes it clear what functions are tests for other +developers. Test functions take no arguments and return nothing. All test +accounting is handled internally in Unity. + +Finally, at the bottom of your test file, you will write a `main()` function. +This function will call `UNITY_BEGIN()`, then `RUN_TEST` for each test, and +finally `UNITY_END()`.This is what will actually trigger each of those test +functions to run, so it is important that each function gets its own `RUN_TEST` +call. + +Remembering to add each test to the main function can get to be tedious. If you +enjoy using helper scripts in your build process, you might consider making use +of our handy generate_test_runner.rb script. This will create the main function +and all the calls for you, assuming that you have followed the suggested naming +conventions. In this case, there is no need for you to include the main function +in your test file at all. + +When you're done, your test file will look something like this: + +```C +#include "unity.h" +#include "file_to_test.h" + +void setUp(void) { + // set stuff up here +} + +void tearDown(void) { + // clean stuff up here +} + +void test_function_should_doBlahAndBlah(void) { + //test stuff +} + +void test_function_should_doAlsoDoBlah(void) { + //more test stuff +} + +int main(void) { + UNITY_BEGIN(); + RUN_TEST(test_function_should_doBlahAndBlah); + RUN_TEST(test_function_should_doAlsoDoBlah); + return UNITY_END(); +} +``` + +It's possible that you will require more customization than this, eventually. +For that sort of thing, you're going to want to look at the configuration guide. +This should be enough to get you going, though. + + +## How to Build and Run A Test File + +This is the single biggest challenge to picking up a new unit testing framework, +at least in a language like C or C++. These languages are REALLY good at getting +you "close to the metal" (why is the phrase metal? Wouldn't it be more accurate +to say "close to the silicon"?). While this feature is usually a good thing, it +can make testing more challenging. + +You have two really good options for toolchains. Depending on where you're +coming from, it might surprise you that neither of these options is running the +unit tests on your hardware. +There are many reasons for this, but here's a short version: +- On hardware, you have too many constraints (processing power, memory, etc), +- On hardware, you don't have complete control over all registers, +- On hardware, unit testing is more challenging, +- Unit testing isn't System testing. Keep them separate. + +Instead of running your tests on your actual hardware, most developers choose to +develop them as native applications (using gcc or MSVC for example) or as +applications running on a simulator. Either is a good option. Native apps have +the advantages of being faster and easier to set up. Simulator apps have the +advantage of working with the same compiler as your target application. The +options for configuring these are discussed in the configuration guide. + +To get either to work, you might need to make a few changes to the file +containing your register set (discussed later). + +In either case, a test is built by linking unity, the test file, and the C +file(s) being tested. These files create an executable which can be run as the +test set for that module. Then, this process is repeated for the next test file. +This flexibility of separating tests into individual executables allows us to +much more thoroughly unit test our system and it keeps all the test code out of +our final release! + + +*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)* diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/docs/UnityHelperScriptsGuide.md b/components/spotify/cspot/bell/cJSON/tests/unity/docs/UnityHelperScriptsGuide.md new file mode 100644 index 00000000..42429900 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/docs/UnityHelperScriptsGuide.md @@ -0,0 +1,254 @@ +# Unity Helper Scripts + +## With a Little Help From Our Friends + +Sometimes what it takes to be a really efficient C programmer is a little non-C. +The Unity project includes a couple Ruby scripts for making your life just a tad +easier. They are completely optional. If you choose to use them, you'll need a +copy of Ruby, of course. Just install whatever the latest version is, and it is +likely to work. You can find Ruby at [ruby-lang.org](https://ruby-labg.org/). + + +### `generate_test_runner.rb` + +Are you tired of creating your own `main` function in your test file? Do you +keep forgetting to add a `RUN_TEST` call when you add a new test case to your +suite? Do you want to use CMock or other fancy add-ons but don't want to figure +out how to create your own `RUN_TEST` macro? + +Well then we have the perfect script for you! + +The `generate_test_runner` script processes a given test file and automatically +creates a separate test runner file that includes ?main?to execute the test +cases within the scanned test file. All you do then is add the generated runner +to your list of files to be compiled and linked, and presto you're done! + +This script searches your test file for void function signatures having a +function name beginning with "test" or "spec". It treats each of these +functions as a test case and builds up a test suite of them. For example, the +following includes three test cases: + +```C +void testVerifyThatUnityIsAwesomeAndWillMakeYourLifeEasier(void) +{ + ASSERT_TRUE(1); +} +void test_FunctionName_should_WorkProperlyAndReturn8(void) { + ASSERT_EQUAL_INT(8, FunctionName()); +} +void spec_Function_should_DoWhatItIsSupposedToDo(void) { + ASSERT_NOT_NULL(Function(5)); +} +``` + +You can run this script a couple of ways. The first is from the command line: + +```Shell +ruby generate_test_runner.rb TestFile.c NameOfRunner.c +``` + +Alternatively, if you include only the test file parameter, the script will copy +the name of the test file and automatically append "_Runner" to the name of the +generated file. The example immediately below will create TestFile_Runner.c. + +```Shell +ruby generate_test_runner.rb TestFile.c +``` + +You can also add a [YAML](http://www.yaml.org/) file to configure extra options. +Conveniently, this YAML file is of the same format as that used by Unity and +CMock. So if you are using YAML files already, you can simply pass the very same +file into the generator script. + +```Shell +ruby generate_test_runner.rb TestFile.c my_config.yml +``` + +The contents of the YAML file `my_config.yml` could look something like the +example below. If you're wondering what some of these options do, you're going +to love the next section of this document. + +```YAML +:unity: + :includes: + - stdio.h + - microdefs.h + :cexception: 1 + :suit_setup: "blah = malloc(1024);" + :suite_teardown: "free(blah);" +``` + +If you would like to force your generated test runner to include one or more +header files, you can just include those at the command line too. Just make sure +these are _after_ the YAML file, if you are using one: + +```Shell +ruby generate_test_runner.rb TestFile.c my_config.yml extras.h +``` + +Another option, particularly if you are already using Ruby to orchestrate your +builds - or more likely the Ruby-based build tool Rake - is requiring this +script directly. Anything that you would have specified in a YAML file can be +passed to the script as part of a hash. Let's push the exact same requirement +set as we did above but this time through Ruby code directly: + +```Ruby +require "generate_test_runner.rb" +options = { + :includes => ["stdio.h", "microdefs.h"], + :cexception => 1, + :suite_setup => "blah = malloc(1024);", + :suite_teardown => "free(blah);" +} +UnityTestRunnerGenerator.new.run(testfile, runner_name, options) +``` + +If you have multiple files to generate in a build script (such as a Rakefile), +you might want to instantiate a generator object with your options and call it +to generate each runner thereafter. Like thus: + +```Ruby +gen = UnityTestRunnerGenerator.new(options) +test_files.each do |f| + gen.run(f, File.basename(f,'.c')+"Runner.c" +end +``` + +#### Options accepted by generate_test_runner.rb: + +The following options are available when executing `generate_test_runner`. You +may pass these as a Ruby hash directly or specify them in a YAML file, both of +which are described above. In the `examples` directory, Example 3's Rakefile +demonstrates using a Ruby hash. + + +##### `:includes` + +This option specifies an array of file names to be `#include`'d at the top of +your runner C file. You might use it to reference custom types or anything else +universally needed in your generated runners. + + +##### `:suite_setup` + +Define this option with C code to be executed _before any_ test cases are run. + +Alternatively, if your C compiler supports weak symbols, you can leave this +option unset and instead provide a `void suiteSetUp(void)` function in your test +suite. The linker will look for this symbol and fall back to a Unity-provided +stub if it is not found. + + +##### `:suite_teardown` + +Define this option with C code to be executed _after all_ test cases have +finished. An integer variable `num_failures` is available for diagnostics. +The code should end with a `return` statement; the value returned will become +the exit code of `main`. You can normally just return `num_failures`. + +Alternatively, if your C compiler supports weak symbols, you can leave this +option unset and instead provide a `int suiteTearDown(int num_failures)` +function in your test suite. The linker will look for this symbol and fall +back to a Unity-provided stub if it is not found. + + +##### `:enforce_strict_ordering` + +This option should be defined if you have the strict order feature enabled in +CMock (see CMock documentation). This generates extra variables required for +everything to run smoothly. If you provide the same YAML to the generator as +used in CMock's configuration, you've already configured the generator properly. + + +##### `:plugins` + +This option specifies an array of plugins to be used (of course, the array can +contain only a single plugin). This is your opportunity to enable support for +CException support, which will add a check for unhandled exceptions in each +test, reporting a failure if one is detected. To enable this feature using Ruby: + +```Ruby +:plugins => [ :cexception ] +``` + +Or as a yaml file: + +```YAML +:plugins: + -:cexception +``` + +If you are using CMock, it is very likely that you are already passing an array +of plugins to CMock. You can just use the same array here. This script will just +ignore the plugins that don't require additional support. + + +### `unity_test_summary.rb` + +A Unity test file contains one or more test case functions. Each test case can +pass, fail, or be ignored. Each test file is run individually producing results +for its collection of test cases. A given project will almost certainly be +composed of multiple test files. Therefore, the suite of tests is comprised of +one or more test cases spread across one or more test files. This script +aggregates individual test file results to generate a summary of all executed +test cases. The output includes how many tests were run, how many were ignored, +and how many failed. In addition, the output includes a listing of which +specific tests were ignored and failed. A good example of the breadth and +details of these results can be found in the `examples` directory. Intentionally +ignored and failing tests in this project generate corresponding entries in the +summary report. + +If you're interested in other (prettier?) output formats, check into the +Ceedling build tool project (ceedling.sourceforge.net) that works with Unity and +CMock and supports xunit-style xml as well as other goodies. + +This script assumes the existence of files ending with the extensions +`.testpass` and `.testfail`.The contents of these files includes the test +results summary corresponding to each test file executed with the extension set +according to the presence or absence of failures for that test file. The script +searches a specified path for these files, opens each one it finds, parses the +results, and aggregates and prints a summary. Calling it from the command line +looks like this: + +```Shell +ruby unity_test_summary.rb build/test/ +``` + +You can optionally specify a root path as well. This is really helpful when you +are using relative paths in your tools' setup, but you want to pull the summary +into an IDE like Eclipse for clickable shortcuts. + +```Shell +ruby unity_test_summary.rb build/test/ ~/projects/myproject/ +``` + +Or, if you're more of a Windows sort of person: + +```Shell +ruby unity_test_summary.rb build\teat\ C:\projects\myproject\ +``` + +When configured correctly, you'll see a final summary, like so: + +```Shell +-------------------------- +UNITY IGNORED TEST SUMMARY +-------------------------- +blah.c:22:test_sandwiches_should_HaveBreadOnTwoSides:IGNORE + +------------------------- +UNITY FAILED TEST SUMMARY +------------------------- +blah.c:87:test_sandwiches_should_HaveCondiments:FAIL:Expected 1 was 0 +meh.c:38:test_soda_should_BeCalledPop:FAIL:Expected "pop" was "coke" + +-------------------------- +OVERALL UNITY TEST SUMMARY +-------------------------- +45 TOTAL TESTS 2 TOTAL FAILURES 1 IGNORED +``` + +How convenient is that? + + +*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)* diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/docs/license.txt b/components/spotify/cspot/bell/cJSON/tests/unity/docs/license.txt new file mode 100644 index 00000000..d66fba53 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/docs/license.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2007-14 Mike Karlesky, Mark VanderVoord, Greg Williams + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_1/makefile b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_1/makefile new file mode 100644 index 00000000..cca79b42 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_1/makefile @@ -0,0 +1,71 @@ +# ========================================== +# Unity Project - A Test Framework for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +#We try to detect the OS we are running on, and adjust commands as needed +ifeq ($(OS),Windows_NT) + ifeq ($(shell uname -s),) # not in a bash-like shell + CLEANUP = del /F /Q + MKDIR = mkdir + else # in a bash-like shell, like msys + CLEANUP = rm -f + MKDIR = mkdir -p + endif + TARGET_EXTENSION=.exe +else + CLEANUP = rm -f + MKDIR = mkdir -p + TARGET_EXTENSION=.out +endif + +C_COMPILER=gcc +ifeq ($(shell uname -s), Darwin) +C_COMPILER=clang +endif + +UNITY_ROOT=../.. + +CFLAGS=-std=c89 +CFLAGS += -Wall +CFLAGS += -Wextra +CFLAGS += -Wpointer-arith +CFLAGS += -Wcast-align +CFLAGS += -Wwrite-strings +CFLAGS += -Wswitch-default +CFLAGS += -Wunreachable-code +CFLAGS += -Winit-self +CFLAGS += -Wmissing-field-initializers +CFLAGS += -Wno-unknown-pragmas +CFLAGS += -Wstrict-prototypes +CFLAGS += -Wundef +CFLAGS += -Wold-style-definition + +TARGET_BASE1=test1 +TARGET_BASE2=test2 +TARGET1 = $(TARGET_BASE1)$(TARGET_EXTENSION) +TARGET2 = $(TARGET_BASE2)$(TARGET_EXTENSION) +SRC_FILES1=$(UNITY_ROOT)/src/unity.c src/ProductionCode.c test/TestProductionCode.c test/test_runners/TestProductionCode_Runner.c +SRC_FILES2=$(UNITY_ROOT)/src/unity.c src/ProductionCode2.c test/TestProductionCode2.c test/test_runners/TestProductionCode2_Runner.c +INC_DIRS=-Isrc -I$(UNITY_ROOT)/src +SYMBOLS= + +all: clean default + +default: $(SRC_FILES1) $(SRC_FILES2) + $(C_COMPILER) $(CFLAGS) $(INC_DIRS) $(SYMBOLS) $(SRC_FILES1) -o $(TARGET1) + $(C_COMPILER) $(CFLAGS) $(INC_DIRS) $(SYMBOLS) $(SRC_FILES2) -o $(TARGET2) + - ./$(TARGET1) + ./$(TARGET2) + +test/test_runners/TestProductionCode_Runner.c: test/TestProductionCode.c + ruby $(UNITY_ROOT)/auto/generate_test_runner.rb test/TestProductionCode.c test/test_runners/TestProductionCode_Runner.c +test/test_runners/TestProductionCode2_Runner.c: test/TestProductionCode2.c + ruby $(UNITY_ROOT)/auto/generate_test_runner.rb test/TestProductionCode2.c test/test_runners/TestProductionCode2_Runner.c + +clean: + $(CLEANUP) $(TARGET1) $(TARGET2) + +ci: CFLAGS += -Werror +ci: default diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_1/readme.txt b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_1/readme.txt new file mode 100644 index 00000000..dfed8150 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_1/readme.txt @@ -0,0 +1,5 @@ +Example 1 +========= + +Close to the simplest possible example of Unity, using only basic features. +Run make to build & run the example tests. \ No newline at end of file diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_1/src/ProductionCode.c b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_1/src/ProductionCode.c new file mode 100644 index 00000000..d039c3e8 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_1/src/ProductionCode.c @@ -0,0 +1,24 @@ + +#include "ProductionCode.h" + +int Counter = 0; +int NumbersToFind[9] = { 0, 34, 55, 66, 32, 11, 1, 77, 888 }; /* some obnoxious array to search that is 1-based indexing instead of 0. */ + +/* This function is supposed to search through NumbersToFind and find a particular number. + * If it finds it, the index is returned. Otherwise 0 is returned which sorta makes sense since + * NumbersToFind is indexed from 1. Unfortunately it's broken + * (and should therefore be caught by our tests) */ +int FindFunction_WhichIsBroken(int NumberToFind) +{ + int i = 0; + while (i <= 8) /* Notice I should have been in braces */ + i++; + if (NumbersToFind[i] == NumberToFind) /* Yikes! I'm getting run after the loop finishes instead of during it! */ + return i; + return 0; +} + +int FunctionWhichReturnsLocalVariable(void) +{ + return Counter; +} diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_1/src/ProductionCode.h b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_1/src/ProductionCode.h new file mode 100644 index 00000000..250ca0dc --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_1/src/ProductionCode.h @@ -0,0 +1,3 @@ + +int FindFunction_WhichIsBroken(int NumberToFind); +int FunctionWhichReturnsLocalVariable(void); diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_1/src/ProductionCode2.c b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_1/src/ProductionCode2.c new file mode 100644 index 00000000..98ee7eeb --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_1/src/ProductionCode2.c @@ -0,0 +1,11 @@ + +#include "ProductionCode2.h" + +char* ThisFunctionHasNotBeenTested(int Poor, char* LittleFunction) +{ + (void)Poor; + (void)LittleFunction; + /* Since There Are No Tests Yet, This Function Could Be Empty For All We Know. + * Which isn't terribly useful... but at least we put in a TEST_IGNORE so we won't forget */ + return (char*)0; +} diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_1/src/ProductionCode2.h b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_1/src/ProductionCode2.h new file mode 100644 index 00000000..34ae980d --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_1/src/ProductionCode2.h @@ -0,0 +1,2 @@ + +char* ThisFunctionHasNotBeenTested(int Poor, char* LittleFunction); diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_2/makefile b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_2/makefile new file mode 100644 index 00000000..99d8d968 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_2/makefile @@ -0,0 +1,70 @@ +# ========================================== +# Unity Project - A Test Framework for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +#We try to detect the OS we are running on, and adjust commands as needed +ifeq ($(OS),Windows_NT) + ifeq ($(shell uname -s),) # not in a bash-like shell + CLEANUP = del /F /Q + MKDIR = mkdir + else # in a bash-like shell, like msys + CLEANUP = rm -f + MKDIR = mkdir -p + endif + TARGET_EXTENSION=.exe +else + CLEANUP = rm -f + MKDIR = mkdir -p + TARGET_EXTENSION=.out +endif + +C_COMPILER=gcc +ifeq ($(shell uname -s), Darwin) +C_COMPILER=clang +endif + +UNITY_ROOT=../.. + +CFLAGS=-std=c99 +CFLAGS += -Wall +CFLAGS += -Wextra +CFLAGS += -Wpointer-arith +CFLAGS += -Wcast-align +CFLAGS += -Wwrite-strings +CFLAGS += -Wswitch-default +CFLAGS += -Wunreachable-code +CFLAGS += -Winit-self +CFLAGS += -Wmissing-field-initializers +CFLAGS += -Wno-unknown-pragmas +CFLAGS += -Wstrict-prototypes +CFLAGS += -Wundef +CFLAGS += -Wold-style-definition + +TARGET_BASE1=all_tests +TARGET1 = $(TARGET_BASE1)$(TARGET_EXTENSION) +SRC_FILES1=\ + $(UNITY_ROOT)/src/unity.c \ + $(UNITY_ROOT)/extras/fixture/src/unity_fixture.c \ + src/ProductionCode.c \ + src/ProductionCode2.c \ + test/TestProductionCode.c \ + test/TestProductionCode2.c \ + test/test_runners/TestProductionCode_Runner.c \ + test/test_runners/TestProductionCode2_Runner.c \ + test/test_runners/all_tests.c +INC_DIRS=-Isrc -I$(UNITY_ROOT)/src -I$(UNITY_ROOT)/extras/fixture/src +SYMBOLS= + +all: clean default + +default: + $(C_COMPILER) $(CFLAGS) $(INC_DIRS) $(SYMBOLS) $(SRC_FILES1) -o $(TARGET1) + - ./$(TARGET1) -v + +clean: + $(CLEANUP) $(TARGET1) + +ci: CFLAGS += -Werror +ci: default diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_2/readme.txt b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_2/readme.txt new file mode 100644 index 00000000..f0fce657 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_2/readme.txt @@ -0,0 +1,5 @@ +Example 2 +========= + +Same as the first example, but now using Unity's test fixture to group tests +together. Using the test fixture also makes writing test runners much easier. \ No newline at end of file diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_2/src/ProductionCode.c b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_2/src/ProductionCode.c new file mode 100644 index 00000000..500b44b5 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_2/src/ProductionCode.c @@ -0,0 +1,24 @@ + +#include "ProductionCode.h" + +int Counter = 0; +int NumbersToFind[9] = { 0, 34, 55, 66, 32, 11, 1, 77, 888 }; //some obnoxious array to search that is 1-based indexing instead of 0. + +// This function is supposed to search through NumbersToFind and find a particular number. +// If it finds it, the index is returned. Otherwise 0 is returned which sorta makes sense since +// NumbersToFind is indexed from 1. Unfortunately it's broken +// (and should therefore be caught by our tests) +int FindFunction_WhichIsBroken(int NumberToFind) +{ + int i = 0; + while (i <= 8) //Notice I should have been in braces + i++; + if (NumbersToFind[i] == NumberToFind) //Yikes! I'm getting run after the loop finishes instead of during it! + return i; + return 0; +} + +int FunctionWhichReturnsLocalVariable(void) +{ + return Counter; +} diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_2/src/ProductionCode.h b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_2/src/ProductionCode.h new file mode 100644 index 00000000..250ca0dc --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_2/src/ProductionCode.h @@ -0,0 +1,3 @@ + +int FindFunction_WhichIsBroken(int NumberToFind); +int FunctionWhichReturnsLocalVariable(void); diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_2/src/ProductionCode2.c b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_2/src/ProductionCode2.c new file mode 100644 index 00000000..77c969f1 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_2/src/ProductionCode2.c @@ -0,0 +1,11 @@ + +#include "ProductionCode2.h" + +char* ThisFunctionHasNotBeenTested(int Poor, char* LittleFunction) +{ + (void)Poor; + (void)LittleFunction; + //Since There Are No Tests Yet, This Function Could Be Empty For All We Know. + // Which isn't terribly useful... but at least we put in a TEST_IGNORE so we won't forget + return (char*)0; +} diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_2/src/ProductionCode2.h b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_2/src/ProductionCode2.h new file mode 100644 index 00000000..34ae980d --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_2/src/ProductionCode2.h @@ -0,0 +1,2 @@ + +char* ThisFunctionHasNotBeenTested(int Poor, char* LittleFunction); diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/helper/UnityHelper.c b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/helper/UnityHelper.c new file mode 100644 index 00000000..9cf42c67 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/helper/UnityHelper.c @@ -0,0 +1,10 @@ +#include "unity.h" +#include "UnityHelper.h" +#include +#include + +void AssertEqualExampleStruct(const EXAMPLE_STRUCT_T expected, const EXAMPLE_STRUCT_T actual, const unsigned short line) +{ + UNITY_TEST_ASSERT_EQUAL_INT(expected.x, actual.x, line, "Example Struct Failed For Field x"); + UNITY_TEST_ASSERT_EQUAL_INT(expected.y, actual.y, line, "Example Struct Failed For Field y"); +} diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/helper/UnityHelper.h b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/helper/UnityHelper.h new file mode 100644 index 00000000..15161115 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/helper/UnityHelper.h @@ -0,0 +1,12 @@ +#ifndef _TESTHELPER_H +#define _TESTHELPER_H + +#include "Types.h" + +void AssertEqualExampleStruct(const EXAMPLE_STRUCT_T expected, const EXAMPLE_STRUCT_T actual, const unsigned short line); + +#define UNITY_TEST_ASSERT_EQUAL_EXAMPLE_STRUCT_T(expected, actual, line, message) AssertEqualExampleStruct(expected, actual, line); + +#define TEST_ASSERT_EQUAL_EXAMPLE_STRUCT_T(expected, actual) UNITY_TEST_ASSERT_EQUAL_EXAMPLE_STRUCT_T(expected, actual, __LINE__, NULL); + +#endif // _TESTHELPER_H diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/rakefile.rb b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/rakefile.rb new file mode 100644 index 00000000..bf9f42be --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/rakefile.rb @@ -0,0 +1,43 @@ +HERE = File.expand_path(File.dirname(__FILE__)) + '/' +UNITY_ROOT = File.expand_path(File.dirname(__FILE__)) + '/../..' + +require 'rake' +require 'rake/clean' +require HERE + 'rakefile_helper' + +TEMP_DIRS = [ + File.join(HERE, 'build') +].freeze + +TEMP_DIRS.each do |dir| + directory(dir) + CLOBBER.include(dir) +end + +task prepare_for_tests: TEMP_DIRS + +include RakefileHelpers + +# Load default configuration, for now +DEFAULT_CONFIG_FILE = 'target_gcc_32.yml'.freeze +configure_toolchain(DEFAULT_CONFIG_FILE) + +task unit: [:prepare_for_tests] do + run_tests unit_test_files +end + +desc 'Generate test summary' +task :summary do + report_summary +end + +desc 'Build and test Unity' +task all: %i(clean unit summary) +task default: %i(clobber all) +task ci: [:default] +task cruise: [:default] + +desc 'Load configuration' +task :config, :config_file do |_t, args| + configure_toolchain(args[:config_file]) +end diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/rakefile_helper.rb b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/rakefile_helper.rb new file mode 100644 index 00000000..a186cf0f --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/rakefile_helper.rb @@ -0,0 +1,249 @@ +require 'yaml' +require 'fileutils' +require UNITY_ROOT + '/auto/unity_test_summary' +require UNITY_ROOT + '/auto/generate_test_runner' +require UNITY_ROOT + '/auto/colour_reporter' + +module RakefileHelpers + C_EXTENSION = '.c'.freeze + + def load_configuration(config_file) + $cfg_file = config_file + $cfg = YAML.load(File.read($cfg_file)) + end + + def configure_clean + CLEAN.include($cfg['compiler']['build_path'] + '*.*') unless $cfg['compiler']['build_path'].nil? + end + + def configure_toolchain(config_file = DEFAULT_CONFIG_FILE) + config_file += '.yml' unless config_file =~ /\.yml$/ + load_configuration(config_file) + configure_clean + end + + def unit_test_files + path = $cfg['compiler']['unit_tests_path'] + 'Test*' + C_EXTENSION + path.tr!('\\', '/') + FileList.new(path) + end + + def local_include_dirs + include_dirs = $cfg['compiler']['includes']['items'].dup + include_dirs.delete_if { |dir| dir.is_a?(Array) } + include_dirs + end + + def extract_headers(filename) + includes = [] + lines = File.readlines(filename) + lines.each do |line| + m = line.match(/^\s*#include\s+\"\s*(.+\.[hH])\s*\"/) + includes << m[1] unless m.nil? + end + includes + end + + def find_source_file(header, paths) + paths.each do |dir| + src_file = dir + header.ext(C_EXTENSION) + return src_file if File.exist?(src_file) + end + nil + end + + def tackit(strings) + result = if strings.is_a?(Array) + "\"#{strings.join}\"" + else + strings + end + result + end + + def squash(prefix, items) + result = '' + items.each { |item| result += " #{prefix}#{tackit(item)}" } + result + end + + def build_compiler_fields + command = tackit($cfg['compiler']['path']) + defines = if $cfg['compiler']['defines']['items'].nil? + '' + else + squash($cfg['compiler']['defines']['prefix'], $cfg['compiler']['defines']['items']) + end + options = squash('', $cfg['compiler']['options']) + includes = squash($cfg['compiler']['includes']['prefix'], $cfg['compiler']['includes']['items']) + includes = includes.gsub(/\\ /, ' ').gsub(/\\\"/, '"').gsub(/\\$/, '') # Remove trailing slashes (for IAR) + + { command: command, defines: defines, options: options, includes: includes } + end + + def compile(file, _defines = []) + compiler = build_compiler_fields + cmd_str = "#{compiler[:command]}#{compiler[:defines]}#{compiler[:options]}#{compiler[:includes]} #{file} " \ + "#{$cfg['compiler']['object_files']['prefix']}#{$cfg['compiler']['object_files']['destination']}" + obj_file = "#{File.basename(file, C_EXTENSION)}#{$cfg['compiler']['object_files']['extension']}" + execute(cmd_str + obj_file) + obj_file + end + + def build_linker_fields + command = tackit($cfg['linker']['path']) + options = if $cfg['linker']['options'].nil? + '' + else + squash('', $cfg['linker']['options']) + end + includes = if $cfg['linker']['includes'].nil? || $cfg['linker']['includes']['items'].nil? + '' + else + squash($cfg['linker']['includes']['prefix'], $cfg['linker']['includes']['items']) + end.gsub(/\\ /, ' ').gsub(/\\\"/, '"').gsub(/\\$/, '') # Remove trailing slashes (for IAR) + + { command: command, options: options, includes: includes } + end + + def link_it(exe_name, obj_list) + linker = build_linker_fields + cmd_str = "#{linker[:command]}#{linker[:options]}#{linker[:includes]} " + + (obj_list.map { |obj| "#{$cfg['linker']['object_files']['path']}#{obj} " }).join + + $cfg['linker']['bin_files']['prefix'] + ' ' + + $cfg['linker']['bin_files']['destination'] + + exe_name + $cfg['linker']['bin_files']['extension'] + execute(cmd_str) + end + + def build_simulator_fields + return nil if $cfg['simulator'].nil? + command = if $cfg['simulator']['path'].nil? + '' + else + (tackit($cfg['simulator']['path']) + ' ') + end + pre_support = if $cfg['simulator']['pre_support'].nil? + '' + else + squash('', $cfg['simulator']['pre_support']) + end + post_support = if $cfg['simulator']['post_support'].nil? + '' + else + squash('', $cfg['simulator']['post_support']) + end + + { command: command, pre_support: pre_support, post_support: post_support } + end + + def execute(command_string, verbose = true, raise_on_fail = true) + report command_string + output = `#{command_string}`.chomp + report(output) if verbose && !output.nil? && !output.empty? + if !$?.exitstatus.zero? && raise_on_fail + raise "Command failed. (Returned #{$?.exitstatus})" + end + output + end + + def report_summary + summary = UnityTestSummary.new + summary.root = HERE + results_glob = "#{$cfg['compiler']['build_path']}*.test*" + results_glob.tr!('\\', '/') + results = Dir[results_glob] + summary.targets = results + summary.run + fail_out 'FAIL: There were failures' if summary.failures > 0 + end + + def run_tests(test_files) + report 'Running system tests...' + + # Tack on TEST define for compiling unit tests + load_configuration($cfg_file) + test_defines = ['TEST'] + $cfg['compiler']['defines']['items'] = [] if $cfg['compiler']['defines']['items'].nil? + $cfg['compiler']['defines']['items'] << 'TEST' + + include_dirs = local_include_dirs + + # Build and execute each unit test + test_files.each do |test| + obj_list = [] + + # Detect dependencies and build required required modules + extract_headers(test).each do |header| + # Compile corresponding source file if it exists + src_file = find_source_file(header, include_dirs) + obj_list << compile(src_file, test_defines) unless src_file.nil? + end + + # Build the test runner (generate if configured to do so) + test_base = File.basename(test, C_EXTENSION) + runner_name = test_base + '_Runner.c' + if $cfg['compiler']['runner_path'].nil? + runner_path = $cfg['compiler']['build_path'] + runner_name + test_gen = UnityTestRunnerGenerator.new($cfg_file) + test_gen.run(test, runner_path) + else + runner_path = $cfg['compiler']['runner_path'] + runner_name + end + + obj_list << compile(runner_path, test_defines) + + # Build the test module + obj_list << compile(test, test_defines) + + # Link the test executable + link_it(test_base, obj_list) + + # Execute unit test and generate results file + simulator = build_simulator_fields + executable = $cfg['linker']['bin_files']['destination'] + test_base + $cfg['linker']['bin_files']['extension'] + cmd_str = if simulator.nil? + executable + else + "#{simulator[:command]} #{simulator[:pre_support]} #{executable} #{simulator[:post_support]}" + end + output = execute(cmd_str, true, false) + test_results = $cfg['compiler']['build_path'] + test_base + test_results += if output.match(/OK$/m).nil? + '.testfail' + else + '.testpass' + end + File.open(test_results, 'w') { |f| f.print output } + end + end + + def build_application(main) + report 'Building application...' + + obj_list = [] + load_configuration($cfg_file) + main_path = $cfg['compiler']['source_path'] + main + C_EXTENSION + + # Detect dependencies and build required required modules + include_dirs = get_local_include_dirs + extract_headers(main_path).each do |header| + src_file = find_source_file(header, include_dirs) + obj_list << compile(src_file) unless src_file.nil? + end + + # Build the main source file + main_base = File.basename(main_path, C_EXTENSION) + obj_list << compile(main_path) + + # Create the executable + link_it(main_base, obj_list) + end + + def fail_out(msg) + puts msg + puts 'Not returning exit code so continuous integration can pass' + # exit(-1) # Only removed to pass example_3, which has failing tests on purpose. + # Still fail if the build fails for any other reason. + end +end diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/readme.txt b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/readme.txt new file mode 100644 index 00000000..7371fea0 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/readme.txt @@ -0,0 +1,13 @@ +Example 3 +========= + +This example project gives an example of some passing, ignored, and failing tests. +It's simple and meant for you to look over and get an idea for what all of this stuff does. + +You can build and test using rake. The rake version will let you test with gcc or a couple +versions of IAR. You can tweak the yaml files to get those versions running. + +Ruby is required if you're using the rake version (obviously). This version shows off most of +Unity's advanced features (automatically creating test runners, fancy summaries, etc.) +Without ruby, you have to maintain your own test runners. Do that for a while and you'll learn +why you really want to start using the Ruby tools. diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/src/ProductionCode.c b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/src/ProductionCode.c new file mode 100644 index 00000000..500b44b5 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/src/ProductionCode.c @@ -0,0 +1,24 @@ + +#include "ProductionCode.h" + +int Counter = 0; +int NumbersToFind[9] = { 0, 34, 55, 66, 32, 11, 1, 77, 888 }; //some obnoxious array to search that is 1-based indexing instead of 0. + +// This function is supposed to search through NumbersToFind and find a particular number. +// If it finds it, the index is returned. Otherwise 0 is returned which sorta makes sense since +// NumbersToFind is indexed from 1. Unfortunately it's broken +// (and should therefore be caught by our tests) +int FindFunction_WhichIsBroken(int NumberToFind) +{ + int i = 0; + while (i <= 8) //Notice I should have been in braces + i++; + if (NumbersToFind[i] == NumberToFind) //Yikes! I'm getting run after the loop finishes instead of during it! + return i; + return 0; +} + +int FunctionWhichReturnsLocalVariable(void) +{ + return Counter; +} diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/src/ProductionCode.h b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/src/ProductionCode.h new file mode 100644 index 00000000..250ca0dc --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/src/ProductionCode.h @@ -0,0 +1,3 @@ + +int FindFunction_WhichIsBroken(int NumberToFind); +int FunctionWhichReturnsLocalVariable(void); diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/src/ProductionCode2.c b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/src/ProductionCode2.c new file mode 100644 index 00000000..77c969f1 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/src/ProductionCode2.c @@ -0,0 +1,11 @@ + +#include "ProductionCode2.h" + +char* ThisFunctionHasNotBeenTested(int Poor, char* LittleFunction) +{ + (void)Poor; + (void)LittleFunction; + //Since There Are No Tests Yet, This Function Could Be Empty For All We Know. + // Which isn't terribly useful... but at least we put in a TEST_IGNORE so we won't forget + return (char*)0; +} diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/src/ProductionCode2.h b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/src/ProductionCode2.h new file mode 100644 index 00000000..34ae980d --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/src/ProductionCode2.h @@ -0,0 +1,2 @@ + +char* ThisFunctionHasNotBeenTested(int Poor, char* LittleFunction); diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/target_gcc_32.yml b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/target_gcc_32.yml new file mode 100644 index 00000000..f155508c --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/example_3/target_gcc_32.yml @@ -0,0 +1,46 @@ +# Copied from ~Unity/targets/gcc_32.yml +unity_root: &unity_root '../..' +compiler: + path: gcc + source_path: 'src/' + unit_tests_path: &unit_tests_path 'test/' + build_path: &build_path 'build/' + options: + - '-c' + - '-m32' + - '-Wall' + - '-Wno-address' + - '-std=c99' + - '-pedantic' + includes: + prefix: '-I' + items: + - 'src/' + - '../../src/' + - *unit_tests_path + defines: + prefix: '-D' + items: + - UNITY_INCLUDE_DOUBLE + - UNITY_SUPPORT_TEST_CASES + object_files: + prefix: '-o' + extension: '.o' + destination: *build_path +linker: + path: gcc + options: + - -lm + - '-m32' + includes: + prefix: '-I' + object_files: + path: *build_path + extension: '.o' + bin_files: + prefix: '-o' + extension: '.exe' + destination: *build_path +colour: true +:unity: + :plugins: [] diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/examples/unity_config.h b/components/spotify/cspot/bell/cJSON/tests/unity/examples/unity_config.h new file mode 100644 index 00000000..a2f161a7 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/examples/unity_config.h @@ -0,0 +1,239 @@ +/* Unity Configuration + * As of May 11th, 2016 at ThrowTheSwitch/Unity commit 837c529 + * Update: December 29th, 2016 + * See Also: Unity/docs/UnityConfigurationGuide.pdf + * + * Unity is designed to run on almost anything that is targeted by a C compiler. + * It would be awesome if this could be done with zero configuration. While + * there are some targets that come close to this dream, it is sadly not + * universal. It is likely that you are going to need at least a couple of the + * configuration options described in this document. + * + * All of Unity's configuration options are `#defines`. Most of these are simple + * definitions. A couple are macros with arguments. They live inside the + * unity_internals.h header file. We don't necessarily recommend opening that + * file unless you really need to. That file is proof that a cross-platform + * library is challenging to build. From a more positive perspective, it is also + * proof that a great deal of complexity can be centralized primarily to one + * place in order to provide a more consistent and simple experience elsewhere. + * + * Using These Options + * It doesn't matter if you're using a target-specific compiler and a simulator + * or a native compiler. In either case, you've got a couple choices for + * configuring these options: + * + * 1. Because these options are specified via C defines, you can pass most of + * these options to your compiler through command line compiler flags. Even + * if you're using an embedded target that forces you to use their + * overbearing IDE for all configuration, there will be a place somewhere in + * your project to configure defines for your compiler. + * 2. You can create a custom `unity_config.h` configuration file (present in + * your toolchain's search paths). In this file, you will list definitions + * and macros specific to your target. All you must do is define + * `UNITY_INCLUDE_CONFIG_H` and Unity will rely on `unity_config.h` for any + * further definitions it may need. + */ + +#ifndef UNITY_CONFIG_H +#define UNITY_CONFIG_H + +/* ************************* AUTOMATIC INTEGER TYPES *************************** + * C's concept of an integer varies from target to target. The C Standard has + * rules about the `int` matching the register size of the target + * microprocessor. It has rules about the `int` and how its size relates to + * other integer types. An `int` on one target might be 16 bits while on another + * target it might be 64. There are more specific types in compilers compliant + * with C99 or later, but that's certainly not every compiler you are likely to + * encounter. Therefore, Unity has a number of features for helping to adjust + * itself to match your required integer sizes. It starts off by trying to do it + * automatically. + **************************************************************************** */ + +/* The first attempt to guess your types is to check `limits.h`. Some compilers + * that don't support `stdint.h` could include `limits.h`. If you don't + * want Unity to check this file, define this to make it skip the inclusion. + * Unity looks at UINT_MAX & ULONG_MAX, which were available since C89. + */ +/* #define UNITY_EXCLUDE_LIMITS_H */ + +/* The second thing that Unity does to guess your types is check `stdint.h`. + * This file defines `UINTPTR_MAX`, since C99, that Unity can make use of to + * learn about your system. It's possible you don't want it to do this or it's + * possible that your system doesn't support `stdint.h`. If that's the case, + * you're going to want to define this. That way, Unity will know to skip the + * inclusion of this file and you won't be left with a compiler error. + */ +/* #define UNITY_EXCLUDE_STDINT_H */ + +/* ********************** MANUAL INTEGER TYPE DEFINITION *********************** + * If you've disabled all of the automatic options above, you're going to have + * to do the configuration yourself. There are just a handful of defines that + * you are going to specify if you don't like the defaults. + **************************************************************************** */ + + /* Define this to be the number of bits an `int` takes up on your system. The + * default, if not auto-detected, is 32 bits. + * + * Example: + */ +/* #define UNITY_INT_WIDTH 16 */ + +/* Define this to be the number of bits a `long` takes up on your system. The + * default, if not autodetected, is 32 bits. This is used to figure out what + * kind of 64-bit support your system can handle. Does it need to specify a + * `long` or a `long long` to get a 64-bit value. On 16-bit systems, this option + * is going to be ignored. + * + * Example: + */ +/* #define UNITY_LONG_WIDTH 16 */ + +/* Define this to be the number of bits a pointer takes up on your system. The + * default, if not autodetected, is 32-bits. If you're getting ugly compiler + * warnings about casting from pointers, this is the one to look at. + * + * Example: + */ +/* #define UNITY_POINTER_WIDTH 64 */ + +/* Unity will automatically include 64-bit support if it auto-detects it, or if + * your `int`, `long`, or pointer widths are greater than 32-bits. Define this + * to enable 64-bit support if none of the other options already did it for you. + * There can be a significant size and speed impact to enabling 64-bit support + * on small targets, so don't define it if you don't need it. + */ +/* #define UNITY_INCLUDE_64 */ + + +/* *************************** FLOATING POINT TYPES **************************** + * In the embedded world, it's not uncommon for targets to have no support for + * floating point operations at all or to have support that is limited to only + * single precision. We are able to guess integer sizes on the fly because + * integers are always available in at least one size. Floating point, on the + * other hand, is sometimes not available at all. Trying to include `float.h` on + * these platforms would result in an error. This leaves manual configuration as + * the only option. + **************************************************************************** */ + + /* By default, Unity guesses that you will want single precision floating point + * support, but not double precision. It's easy to change either of these using + * the include and exclude options here. You may include neither, just float, + * or both, as suits your needs. + */ +/* #define UNITY_EXCLUDE_FLOAT */ +#define UNITY_INCLUDE_DOUBLE +/* #define UNITY_EXCLUDE_DOUBLE */ + +/* For features that are enabled, the following floating point options also + * become available. + */ + +/* Unity aims for as small of a footprint as possible and avoids most standard + * library calls (some embedded platforms don't have a standard library!). + * Because of this, its routines for printing integer values are minimalist and + * hand-coded. To keep Unity universal, though, we eventually chose to develop + * our own floating point print routines. Still, the display of floating point + * values during a failure are optional. By default, Unity will print the + * actual results of floating point assertion failures. So a failed assertion + * will produce a message like "Expected 4.0 Was 4.25". If you would like less + * verbose failure messages for floating point assertions, use this option to + * give a failure message `"Values Not Within Delta"` and trim the binary size. + */ +/* #define UNITY_EXCLUDE_FLOAT_PRINT */ + +/* If enabled, Unity assumes you want your `FLOAT` asserts to compare standard C + * floats. If your compiler supports a specialty floating point type, you can + * always override this behavior by using this definition. + * + * Example: + */ +/* #define UNITY_FLOAT_TYPE float16_t */ + +/* If enabled, Unity assumes you want your `DOUBLE` asserts to compare standard + * C doubles. If you would like to change this, you can specify something else + * by using this option. For example, defining `UNITY_DOUBLE_TYPE` to `long + * double` could enable gargantuan floating point types on your 64-bit processor + * instead of the standard `double`. + * + * Example: + */ +/* #define UNITY_DOUBLE_TYPE long double */ + +/* If you look up `UNITY_ASSERT_EQUAL_FLOAT` and `UNITY_ASSERT_EQUAL_DOUBLE` as + * documented in the Unity Assertion Guide, you will learn that they are not + * really asserting that two values are equal but rather that two values are + * "close enough" to equal. "Close enough" is controlled by these precision + * configuration options. If you are working with 32-bit floats and/or 64-bit + * doubles (the normal on most processors), you should have no need to change + * these options. They are both set to give you approximately 1 significant bit + * in either direction. The float precision is 0.00001 while the double is + * 10^-12. For further details on how this works, see the appendix of the Unity + * Assertion Guide. + * + * Example: + */ +/* #define UNITY_FLOAT_PRECISION 0.001f */ +/* #define UNITY_DOUBLE_PRECISION 0.001f */ + + +/* *************************** TOOLSET CUSTOMIZATION *************************** + * In addition to the options listed above, there are a number of other options + * which will come in handy to customize Unity's behavior for your specific + * toolchain. It is possible that you may not need to touch any of these but + * certain platforms, particularly those running in simulators, may need to jump + * through extra hoops to operate properly. These macros will help in those + * situations. + **************************************************************************** */ + +/* By default, Unity prints its results to `stdout` as it runs. This works + * perfectly fine in most situations where you are using a native compiler for + * testing. It works on some simulators as well so long as they have `stdout` + * routed back to the command line. There are times, however, where the + * simulator will lack support for dumping results or you will want to route + * results elsewhere for other reasons. In these cases, you should define the + * `UNITY_OUTPUT_CHAR` macro. This macro accepts a single character at a time + * (as an `int`, since this is the parameter type of the standard C `putchar` + * function most commonly used). You may replace this with whatever function + * call you like. + * + * Example: + * Say you are forced to run your test suite on an embedded processor with no + * `stdout` option. You decide to route your test result output to a custom + * serial `RS232_putc()` function you wrote like thus: + */ +/* #define UNITY_OUTPUT_CHAR(a) RS232_putc(a) */ +/* #define UNITY_OUTPUT_CHAR_HEADER_DECLARATION RS232_putc(int) */ +/* #define UNITY_OUTPUT_FLUSH() RS232_flush() */ +/* #define UNITY_OUTPUT_FLUSH_HEADER_DECLARATION RS232_flush(void) */ +/* #define UNITY_OUTPUT_START() RS232_config(115200,1,8,0) */ +/* #define UNITY_OUTPUT_COMPLETE() RS232_close() */ + +/* For some targets, Unity can make the otherwise required `setUp()` and + * `tearDown()` functions optional. This is a nice convenience for test writers + * since `setUp` and `tearDown` don't often actually _do_ anything. If you're + * using gcc or clang, this option is automatically defined for you. Other + * compilers can also support this behavior, if they support a C feature called + * weak functions. A weak function is a function that is compiled into your + * executable _unless_ a non-weak version of the same function is defined + * elsewhere. If a non-weak version is found, the weak version is ignored as if + * it never existed. If your compiler supports this feature, you can let Unity + * know by defining `UNITY_SUPPORT_WEAK` as the function attributes that would + * need to be applied to identify a function as weak. If your compiler lacks + * support for weak functions, you will always need to define `setUp` and + * `tearDown` functions (though they can be and often will be just empty). The + * most common options for this feature are: + */ +/* #define UNITY_SUPPORT_WEAK weak */ +/* #define UNITY_SUPPORT_WEAK __attribute__((weak)) */ +/* #define UNITY_NO_WEAK */ + +/* Some compilers require a custom attribute to be assigned to pointers, like + * `near` or `far`. In these cases, you can give Unity a safe default for these + * by defining this option with the attribute you would like. + * + * Example: + */ +/* #define UNITY_PTR_ATTRIBUTE __attribute__((far)) */ +/* #define UNITY_PTR_ATTRIBUTE near */ + +#endif /* UNITY_CONFIG_H */ diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/extras/eclipse/error_parsers.txt b/components/spotify/cspot/bell/cJSON/tests/unity/extras/eclipse/error_parsers.txt new file mode 100644 index 00000000..94e34ff3 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/extras/eclipse/error_parsers.txt @@ -0,0 +1,26 @@ +Eclipse error parsers +===================== + +These are a godsend for extracting & quickly navigating to +warnings & error messages from console output. Unforunately +I don't know how to write an Eclipse plugin so you'll have +to add them manually. + +To add a console parser to Eclipse, go to Window --> Preferences +--> C/C++ --> Build --> Settings. Click on the 'Error Parsers' +tab and then click the 'Add...' button. See the table below for +the parser fields to add. + +Eclipse will only parse the console output during a build, so +running your unit tests must be part of your build process. +Either add this to your make/rakefile, or add it as a post- +build step in your Eclipse project settings. + + +Unity unit test error parsers +----------------------------- +Severity Pattern File Line Description +------------------------------------------------------------------------------- +Error (\.+)(.*?):(\d+):(.*?):FAIL: (.*) $2 $3 $5 +Warning (\.+)(.*?):(\d+):(.*?):IGNORE: (.*) $2 $3 $5 +Warning (\.+)(.*?):(\d+):(.*?):IGNORE\s*$ $2 $3 Ignored test diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/rakefile.rb b/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/rakefile.rb new file mode 100644 index 00000000..7603e574 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/rakefile.rb @@ -0,0 +1,48 @@ +# ========================================== +# Unity Project - A Test Framework for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +HERE = File.expand_path(File.dirname(__FILE__)) + '/' + +require 'rake' +require 'rake/clean' +require 'rake/testtask' +require HERE + 'rakefile_helper' + +TEMP_DIRS = [ + File.join(HERE, 'build') +].freeze + +TEMP_DIRS.each do |dir| + directory(dir) + CLOBBER.include(dir) +end + +task prepare_for_tests: TEMP_DIRS + +include RakefileHelpers + +# Load default configuration, for now +DEFAULT_CONFIG_FILE = 'gcc_auto_stdint.yml'.freeze +configure_toolchain(DEFAULT_CONFIG_FILE) + +task unit: [:prepare_for_tests] do + run_tests +end + +desc 'Build and test Unity Framework' +task all: %i(clean unit) +task default: %i(clobber all) +task ci: %i(no_color default) +task cruise: %i(no_color default) + +desc 'Load configuration' +task :config, :config_file do |_t, args| + configure_toolchain(args[:config_file]) +end + +task :no_color do + $colour_output = false +end diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/rakefile_helper.rb b/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/rakefile_helper.rb new file mode 100644 index 00000000..c45b2393 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/rakefile_helper.rb @@ -0,0 +1,178 @@ +# ========================================== +# Unity Project - A Test Framework for C +# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== + +require 'yaml' +require 'fileutils' +require HERE + '../../auto/unity_test_summary' +require HERE + '../../auto/generate_test_runner' +require HERE + '../../auto/colour_reporter' + +module RakefileHelpers + C_EXTENSION = '.c'.freeze + + def load_configuration(config_file) + return if $configured + + $cfg_file = HERE + "../../test/targets/#{config_file}" unless config_file =~ /[\\|\/]/ + $cfg = YAML.load(File.read($cfg_file)) + $colour_output = false unless $cfg['colour'] + $configured = true if config_file != DEFAULT_CONFIG_FILE + end + + def configure_clean + CLEAN.include($cfg['compiler']['build_path'] + '*.*') unless $cfg['compiler']['build_path'].nil? + end + + def configure_toolchain(config_file = DEFAULT_CONFIG_FILE) + config_file += '.yml' unless config_file =~ /\.yml$/ + config_file = config_file unless config_file =~ /[\\|\/]/ + load_configuration(config_file) + configure_clean + end + + def tackit(strings) + result = if strings.is_a?(Array) + "\"#{strings.join}\"" + else + strings + end + result + end + + def squash(prefix, items) + result = '' + items.each { |item| result += " #{prefix}#{tackit(item)}" } + result + end + + def build_compiler_fields + command = tackit($cfg['compiler']['path']) + defines = if $cfg['compiler']['defines']['items'].nil? + '' + else + squash($cfg['compiler']['defines']['prefix'], $cfg['compiler']['defines']['items'] + ['UNITY_OUTPUT_CHAR=UnityOutputCharSpy_OutputChar'] + ['UNITY_OUTPUT_CHAR_HEADER_DECLARATION=UnityOutputCharSpy_OutputChar\(int\)']) + end + options = squash('', $cfg['compiler']['options']) + includes = squash($cfg['compiler']['includes']['prefix'], $cfg['compiler']['includes']['items']) + includes = includes.gsub(/\\ /, ' ').gsub(/\\\"/, '"').gsub(/\\$/, '') # Remove trailing slashes (for IAR) + + { command: command, defines: defines, options: options, includes: includes } + end + + def compile(file, _defines = []) + compiler = build_compiler_fields + unity_include = $cfg['compiler']['includes']['prefix'] + '../../src' + cmd_str = "#{compiler[:command]}#{compiler[:defines]}#{compiler[:options]}#{compiler[:includes]} #{unity_include} #{file} " \ + "#{$cfg['compiler']['object_files']['prefix']}#{$cfg['compiler']['object_files']['destination']}" \ + "#{File.basename(file, C_EXTENSION)}#{$cfg['compiler']['object_files']['extension']}" + + execute(cmd_str) + end + + def build_linker_fields + command = tackit($cfg['linker']['path']) + options = if $cfg['linker']['options'].nil? + '' + else + squash('', $cfg['linker']['options']) + end + includes = if $cfg['linker']['includes'].nil? || $cfg['linker']['includes']['items'].nil? + '' + else + squash($cfg['linker']['includes']['prefix'], $cfg['linker']['includes']['items']) + end.gsub(/\\ /, ' ').gsub(/\\\"/, '"').gsub(/\\$/, '') # Remove trailing slashes (for IAR) + + { command: command, options: options, includes: includes } + end + + def link_it(exe_name, obj_list) + linker = build_linker_fields + cmd_str = "#{linker[:command]}#{linker[:options]}#{linker[:includes]} " + + (obj_list.map { |obj| "#{$cfg['linker']['object_files']['path']}#{obj} " }).join + + $cfg['linker']['bin_files']['prefix'] + ' ' + + $cfg['linker']['bin_files']['destination'] + + exe_name + $cfg['linker']['bin_files']['extension'] + execute(cmd_str) + end + + def build_simulator_fields + return nil if $cfg['simulator'].nil? + command = if $cfg['simulator']['path'].nil? + '' + else + (tackit($cfg['simulator']['path']) + ' ') + end + pre_support = if $cfg['simulator']['pre_support'].nil? + '' + else + squash('', $cfg['simulator']['pre_support']) + end + post_support = if $cfg['simulator']['post_support'].nil? + '' + else + squash('', $cfg['simulator']['post_support']) + end + { command: command, pre_support: pre_support, post_support: post_support } + end + + def execute(command_string, verbose = true) + report command_string + output = `#{command_string}`.chomp + report(output) if verbose && !output.nil? && !output.empty? + raise "Command failed. (Returned #{$?.exitstatus})" if $?.exitstatus != 0 + output + end + + def report_summary + summary = UnityTestSummary.new + summary.root = HERE + results_glob = "#{$cfg['compiler']['build_path']}*.test*" + results_glob.tr!('\\', '/') + results = Dir[results_glob] + summary.targets = results + summary.run + end + + def run_tests + report 'Running Unity system tests...' + + # Tack on TEST define for compiling unit tests + load_configuration($cfg_file) + test_defines = ['TEST'] + $cfg['compiler']['defines']['items'] = [] if $cfg['compiler']['defines']['items'].nil? + + # Get a list of all source files needed + src_files = Dir[HERE + 'src/*.c'] + src_files += Dir[HERE + 'test/*.c'] + src_files += Dir[HERE + 'test/main/*.c'] + src_files << '../../src/unity.c' + + # Build object files + src_files.each { |f| compile(f, test_defines) } + obj_list = src_files.map { |f| File.basename(f.ext($cfg['compiler']['object_files']['extension'])) } + + # Link the test executable + test_base = 'framework_test' + link_it(test_base, obj_list) + + # Execute unit test and generate results file + simulator = build_simulator_fields + executable = $cfg['linker']['bin_files']['destination'] + test_base + $cfg['linker']['bin_files']['extension'] + cmd_str = if simulator.nil? + executable + ' -v -r' + else + "#{simulator[:command]} #{simulator[:pre_support]} #{executable} #{simulator[:post_support]}" + end + output = execute(cmd_str) + test_results = $cfg['compiler']['build_path'] + test_base + test_results += if output.match(/OK$/m).nil? + '.testfail' + else + '.testpass' + end + File.open(test_results, 'w') { |f| f.print output } + end +end diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/readme.txt b/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/readme.txt new file mode 100644 index 00000000..6b9a78c1 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/readme.txt @@ -0,0 +1,9 @@ +Copyright (c) 2010 James Grenning and Contributed to Unity Project + +Unity Project - A Test Framework for C +Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams +[Released under MIT License. Please refer to license.txt for details] + +This Framework is an optional add-on to Unity. By including unity_framework.h in place of unity.h, +you may now work with Unity in a manner similar to CppUTest. This framework adds the concepts of +test groups and gives finer control of your tests over the command line. \ No newline at end of file diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/src/unity_fixture.c b/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/src/unity_fixture.c new file mode 100644 index 00000000..3872bd8f --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/src/unity_fixture.c @@ -0,0 +1,432 @@ +/* Copyright (c) 2010 James Grenning and Contributed to Unity Project + * ========================================== + * Unity Project - A Test Framework for C + * Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams + * [Released under MIT License. Please refer to license.txt for details] + * ========================================== */ + +#include "unity_fixture.h" +#include "unity_internals.h" +#include + +struct UNITY_FIXTURE_T UnityFixture; + +/* If you decide to use the function pointer approach. + * Build with -D UNITY_OUTPUT_CHAR=outputChar and include + * int (*outputChar)(int) = putchar; */ + +#if !defined(UNITY_WEAK_ATTRIBUTE) && !defined(UNITY_WEAK_PRAGMA) +void setUp(void) { /*does nothing*/ } +void tearDown(void) { /*does nothing*/ } +#endif + +static void announceTestRun(unsigned int runNumber) +{ + UnityPrint("Unity test run "); + UnityPrintNumberUnsigned(runNumber+1); + UnityPrint(" of "); + UnityPrintNumberUnsigned(UnityFixture.RepeatCount); + UNITY_PRINT_EOL(); +} + +int UnityMain(int argc, const char* argv[], void (*runAllTests)(void)) +{ + int result = UnityGetCommandLineOptions(argc, argv); + unsigned int r; + if (result != 0) + return result; + + for (r = 0; r < UnityFixture.RepeatCount; r++) + { + UnityBegin(argv[0]); + announceTestRun(r); + runAllTests(); + if (!UnityFixture.Verbose) UNITY_PRINT_EOL(); + UnityEnd(); + } + + return (int)Unity.TestFailures; +} + +static int selected(const char* filter, const char* name) +{ + if (filter == 0) + return 1; + return strstr(name, filter) ? 1 : 0; +} + +static int testSelected(const char* test) +{ + return selected(UnityFixture.NameFilter, test); +} + +static int groupSelected(const char* group) +{ + return selected(UnityFixture.GroupFilter, group); +} + +void UnityTestRunner(unityfunction* setup, + unityfunction* testBody, + unityfunction* teardown, + const char* printableName, + const char* group, + const char* name, + const char* file, + unsigned int line) +{ + if (testSelected(name) && groupSelected(group)) + { + Unity.TestFile = file; + Unity.CurrentTestName = printableName; + Unity.CurrentTestLineNumber = line; + if (!UnityFixture.Verbose) + UNITY_OUTPUT_CHAR('.'); + else + { + UnityPrint(printableName); + #ifndef UNITY_REPEAT_TEST_NAME + Unity.CurrentTestName = NULL; + #endif + } + + Unity.NumberOfTests++; + UnityMalloc_StartTest(); + UnityPointer_Init(); + + if (TEST_PROTECT()) + { + setup(); + testBody(); + } + if (TEST_PROTECT()) + { + teardown(); + } + if (TEST_PROTECT()) + { + UnityPointer_UndoAllSets(); + if (!Unity.CurrentTestFailed) + UnityMalloc_EndTest(); + } + UnityConcludeFixtureTest(); + } +} + +void UnityIgnoreTest(const char* printableName, const char* group, const char* name) +{ + if (testSelected(name) && groupSelected(group)) + { + Unity.NumberOfTests++; + Unity.TestIgnores++; + if (!UnityFixture.Verbose) + UNITY_OUTPUT_CHAR('!'); + else + { + UnityPrint(printableName); + UNITY_PRINT_EOL(); + } + } +} + + +/*------------------------------------------------- */ +/* Malloc and free stuff */ +#define MALLOC_DONT_FAIL -1 +static int malloc_count; +static int malloc_fail_countdown = MALLOC_DONT_FAIL; + +void UnityMalloc_StartTest(void) +{ + malloc_count = 0; + malloc_fail_countdown = MALLOC_DONT_FAIL; +} + +void UnityMalloc_EndTest(void) +{ + malloc_fail_countdown = MALLOC_DONT_FAIL; + if (malloc_count != 0) + { + UNITY_TEST_FAIL(Unity.CurrentTestLineNumber, "This test leaks!"); + } +} + +void UnityMalloc_MakeMallocFailAfterCount(int countdown) +{ + malloc_fail_countdown = countdown; +} + +/* These definitions are always included from unity_fixture_malloc_overrides.h */ +/* We undef to use them or avoid conflict with per the C standard */ +#undef malloc +#undef free +#undef calloc +#undef realloc + +#ifdef UNITY_EXCLUDE_STDLIB_MALLOC +static unsigned char unity_heap[UNITY_INTERNAL_HEAP_SIZE_BYTES]; +static size_t heap_index; +#else +#include +#endif + +typedef struct GuardBytes +{ + size_t size; + size_t guard_space; +} Guard; + + +static const char end[] = "END"; + +void* unity_malloc(size_t size) +{ + char* mem; + Guard* guard; + size_t total_size = size + sizeof(Guard) + sizeof(end); + + if (malloc_fail_countdown != MALLOC_DONT_FAIL) + { + if (malloc_fail_countdown == 0) + return NULL; + malloc_fail_countdown--; + } + + if (size == 0) return NULL; +#ifdef UNITY_EXCLUDE_STDLIB_MALLOC + if (heap_index + total_size > UNITY_INTERNAL_HEAP_SIZE_BYTES) + { + guard = NULL; + } + else + { + guard = (Guard*)&unity_heap[heap_index]; + heap_index += total_size; + } +#else + guard = (Guard*)UNITY_FIXTURE_MALLOC(total_size); +#endif + if (guard == NULL) return NULL; + malloc_count++; + guard->size = size; + guard->guard_space = 0; + mem = (char*)&(guard[1]); + memcpy(&mem[size], end, sizeof(end)); + + return (void*)mem; +} + +static int isOverrun(void* mem) +{ + Guard* guard = (Guard*)mem; + char* memAsChar = (char*)mem; + guard--; + + return guard->guard_space != 0 || strcmp(&memAsChar[guard->size], end) != 0; +} + +static void release_memory(void* mem) +{ + Guard* guard = (Guard*)mem; + guard--; + + malloc_count--; +#ifdef UNITY_EXCLUDE_STDLIB_MALLOC + if (mem == unity_heap + heap_index - guard->size - sizeof(end)) + { + heap_index -= (guard->size + sizeof(Guard) + sizeof(end)); + } +#else + UNITY_FIXTURE_FREE(guard); +#endif +} + +void unity_free(void* mem) +{ + int overrun; + + if (mem == NULL) + { + return; + } + + overrun = isOverrun(mem); + release_memory(mem); + if (overrun) + { + UNITY_TEST_FAIL(Unity.CurrentTestLineNumber, "Buffer overrun detected during free()"); + } +} + +void* unity_calloc(size_t num, size_t size) +{ + void* mem = unity_malloc(num * size); + if (mem == NULL) return NULL; + memset(mem, 0, num * size); + return mem; +} + +void* unity_realloc(void* oldMem, size_t size) +{ + Guard* guard = (Guard*)oldMem; + void* newMem; + + if (oldMem == NULL) return unity_malloc(size); + + guard--; + if (isOverrun(oldMem)) + { + release_memory(oldMem); + UNITY_TEST_FAIL(Unity.CurrentTestLineNumber, "Buffer overrun detected during realloc()"); + } + + if (size == 0) + { + release_memory(oldMem); + return NULL; + } + + if (guard->size >= size) return oldMem; + +#ifdef UNITY_EXCLUDE_STDLIB_MALLOC /* Optimization if memory is expandable */ + if (oldMem == unity_heap + heap_index - guard->size - sizeof(end) && + heap_index + size - guard->size <= UNITY_INTERNAL_HEAP_SIZE_BYTES) + { + release_memory(oldMem); /* Not thread-safe, like unity_heap generally */ + return unity_malloc(size); /* No memcpy since data is in place */ + } +#endif + newMem = unity_malloc(size); + if (newMem == NULL) return NULL; /* Do not release old memory */ + memcpy(newMem, oldMem, guard->size); + release_memory(oldMem); + return newMem; +} + + +/*-------------------------------------------------------- */ +/*Automatic pointer restoration functions */ +struct PointerPair +{ + void** pointer; + void* old_value; +}; + +static struct PointerPair pointer_store[UNITY_MAX_POINTERS]; +static int pointer_index = 0; + +void UnityPointer_Init(void) +{ + pointer_index = 0; +} + +void UnityPointer_Set(void** pointer, void* newValue, UNITY_LINE_TYPE line) +{ + if (pointer_index >= UNITY_MAX_POINTERS) + { + UNITY_TEST_FAIL(line, "Too many pointers set"); + } + else + { + pointer_store[pointer_index].pointer = pointer; + pointer_store[pointer_index].old_value = *pointer; + *pointer = newValue; + pointer_index++; + } +} + +void UnityPointer_UndoAllSets(void) +{ + while (pointer_index > 0) + { + pointer_index--; + *(pointer_store[pointer_index].pointer) = + pointer_store[pointer_index].old_value; + } +} + +int UnityGetCommandLineOptions(int argc, const char* argv[]) +{ + int i; + UnityFixture.Verbose = 0; + UnityFixture.GroupFilter = 0; + UnityFixture.NameFilter = 0; + UnityFixture.RepeatCount = 1; + + if (argc == 1) + return 0; + + for (i = 1; i < argc; ) + { + if (strcmp(argv[i], "-v") == 0) + { + UnityFixture.Verbose = 1; + i++; + } + else if (strcmp(argv[i], "-g") == 0) + { + i++; + if (i >= argc) + return 1; + UnityFixture.GroupFilter = argv[i]; + i++; + } + else if (strcmp(argv[i], "-n") == 0) + { + i++; + if (i >= argc) + return 1; + UnityFixture.NameFilter = argv[i]; + i++; + } + else if (strcmp(argv[i], "-r") == 0) + { + UnityFixture.RepeatCount = 2; + i++; + if (i < argc) + { + if (*(argv[i]) >= '0' && *(argv[i]) <= '9') + { + unsigned int digit = 0; + UnityFixture.RepeatCount = 0; + while (argv[i][digit] >= '0' && argv[i][digit] <= '9') + { + UnityFixture.RepeatCount *= 10; + UnityFixture.RepeatCount += (unsigned int)argv[i][digit++] - '0'; + } + i++; + } + } + } + else + { + /* ignore unknown parameter */ + i++; + } + } + return 0; +} + +void UnityConcludeFixtureTest(void) +{ + if (Unity.CurrentTestIgnored) + { + Unity.TestIgnores++; + UNITY_PRINT_EOL(); + } + else if (!Unity.CurrentTestFailed) + { + if (UnityFixture.Verbose) + { + UnityPrint(" PASS"); + UNITY_PRINT_EOL(); + } + } + else /* Unity.CurrentTestFailed */ + { + Unity.TestFailures++; + UNITY_PRINT_EOL(); + } + + Unity.CurrentTestFailed = 0; + Unity.CurrentTestIgnored = 0; +} diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/src/unity_fixture.h b/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/src/unity_fixture.h new file mode 100644 index 00000000..6f8d6234 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/src/unity_fixture.h @@ -0,0 +1,83 @@ +/* Copyright (c) 2010 James Grenning and Contributed to Unity Project + * ========================================== + * Unity Project - A Test Framework for C + * Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams + * [Released under MIT License. Please refer to license.txt for details] + * ========================================== */ + +#ifndef UNITY_FIXTURE_H_ +#define UNITY_FIXTURE_H_ + +#include "unity.h" +#include "unity_internals.h" +#include "unity_fixture_malloc_overrides.h" +#include "unity_fixture_internals.h" + +int UnityMain(int argc, const char* argv[], void (*runAllTests)(void)); + + +#define TEST_GROUP(group)\ + static const char* TEST_GROUP_##group = #group + +#define TEST_SETUP(group) void TEST_##group##_SETUP(void);\ + void TEST_##group##_SETUP(void) + +#define TEST_TEAR_DOWN(group) void TEST_##group##_TEAR_DOWN(void);\ + void TEST_##group##_TEAR_DOWN(void) + + +#define TEST(group, name) \ + void TEST_##group##_##name##_(void);\ + void TEST_##group##_##name##_run(void);\ + void TEST_##group##_##name##_run(void)\ + {\ + UnityTestRunner(TEST_##group##_SETUP,\ + TEST_##group##_##name##_,\ + TEST_##group##_TEAR_DOWN,\ + "TEST(" #group ", " #name ")",\ + TEST_GROUP_##group, #name,\ + __FILE__, __LINE__);\ + }\ + void TEST_##group##_##name##_(void) + +#define IGNORE_TEST(group, name) \ + void TEST_##group##_##name##_(void);\ + void TEST_##group##_##name##_run(void);\ + void TEST_##group##_##name##_run(void)\ + {\ + UnityIgnoreTest("IGNORE_TEST(" #group ", " #name ")", TEST_GROUP_##group, #name);\ + }\ + void TEST_##group##_##name##_(void) + +/* Call this for each test, insider the group runner */ +#define RUN_TEST_CASE(group, name) \ + { void TEST_##group##_##name##_run(void);\ + TEST_##group##_##name##_run(); } + +/* This goes at the bottom of each test file or in a separate c file */ +#define TEST_GROUP_RUNNER(group)\ + void TEST_##group##_GROUP_RUNNER(void);\ + void TEST_##group##_GROUP_RUNNER(void) + +/* Call this from main */ +#define RUN_TEST_GROUP(group)\ + { void TEST_##group##_GROUP_RUNNER(void);\ + TEST_##group##_GROUP_RUNNER(); } + +/* CppUTest Compatibility Macros */ +#ifndef UNITY_EXCLUDE_CPPUTEST_ASSERTS +/* Sets a pointer and automatically restores it to its old value after teardown */ +#define UT_PTR_SET(ptr, newPointerValue) UnityPointer_Set((void**)&(ptr), (void*)(newPointerValue), __LINE__) +#define TEST_ASSERT_POINTERS_EQUAL(expected, actual) TEST_ASSERT_EQUAL_PTR((expected), (actual)) +#define TEST_ASSERT_BYTES_EQUAL(expected, actual) TEST_ASSERT_EQUAL_HEX8(0xff & (expected), 0xff & (actual)) +#define FAIL(message) TEST_FAIL_MESSAGE((message)) +#define CHECK(condition) TEST_ASSERT_TRUE((condition)) +#define LONGS_EQUAL(expected, actual) TEST_ASSERT_EQUAL_INT((expected), (actual)) +#define STRCMP_EQUAL(expected, actual) TEST_ASSERT_EQUAL_STRING((expected), (actual)) +#define DOUBLES_EQUAL(expected, actual, delta) TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual)) +#endif + +/* You must compile with malloc replacement, as defined in unity_fixture_malloc_overrides.h */ +void UnityMalloc_MakeMallocFailAfterCount(int count); + +#endif /* UNITY_FIXTURE_H_ */ diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/src/unity_fixture_internals.h b/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/src/unity_fixture_internals.h new file mode 100644 index 00000000..aa0d9e7c --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/src/unity_fixture_internals.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2010 James Grenning and Contributed to Unity Project + * ========================================== + * Unity Project - A Test Framework for C + * Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams + * [Released under MIT License. Please refer to license.txt for details] + * ========================================== */ + +#ifndef UNITY_FIXTURE_INTERNALS_H_ +#define UNITY_FIXTURE_INTERNALS_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct UNITY_FIXTURE_T +{ + int Verbose; + unsigned int RepeatCount; + const char* NameFilter; + const char* GroupFilter; +}; +extern struct UNITY_FIXTURE_T UnityFixture; + +typedef void unityfunction(void); +void UnityTestRunner(unityfunction* setup, + unityfunction* body, + unityfunction* teardown, + const char* printableName, + const char* group, + const char* name, + const char* file, unsigned int line); + +void UnityIgnoreTest(const char* printableName, const char* group, const char* name); +void UnityMalloc_StartTest(void); +void UnityMalloc_EndTest(void); +int UnityGetCommandLineOptions(int argc, const char* argv[]); +void UnityConcludeFixtureTest(void); + +void UnityPointer_Set(void** ptr, void* newValue, UNITY_LINE_TYPE line); +void UnityPointer_UndoAllSets(void); +void UnityPointer_Init(void); +#ifndef UNITY_MAX_POINTERS +#define UNITY_MAX_POINTERS 5 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* UNITY_FIXTURE_INTERNALS_H_ */ diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/src/unity_fixture_malloc_overrides.h b/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/src/unity_fixture_malloc_overrides.h new file mode 100644 index 00000000..7daba50a --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/extras/fixture/src/unity_fixture_malloc_overrides.h @@ -0,0 +1,47 @@ +/* Copyright (c) 2010 James Grenning and Contributed to Unity Project + * ========================================== + * Unity Project - A Test Framework for C + * Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams + * [Released under MIT License. Please refer to license.txt for details] + * ========================================== */ + +#ifndef UNITY_FIXTURE_MALLOC_OVERRIDES_H_ +#define UNITY_FIXTURE_MALLOC_OVERRIDES_H_ + +#include + +#ifdef UNITY_EXCLUDE_STDLIB_MALLOC +/* Define this macro to remove the use of stdlib.h, malloc, and free. + * Many embedded systems do not have a heap or malloc/free by default. + * This internal unity_malloc() provides allocated memory deterministically from + * the end of an array only, unity_free() only releases from end-of-array, + * blocks are not coalesced, and memory not freed in LIFO order is stranded. */ + #ifndef UNITY_INTERNAL_HEAP_SIZE_BYTES + #define UNITY_INTERNAL_HEAP_SIZE_BYTES 256 + #endif +#endif + +/* These functions are used by the Unity Fixture to allocate and release memory + * on the heap and can be overridden with platform-specific implementations. + * For example, when using FreeRTOS UNITY_FIXTURE_MALLOC becomes pvPortMalloc() + * and UNITY_FIXTURE_FREE becomes vPortFree(). */ +#if !defined(UNITY_FIXTURE_MALLOC) || !defined(UNITY_FIXTURE_FREE) + #include + #define UNITY_FIXTURE_MALLOC(size) malloc(size) + #define UNITY_FIXTURE_FREE(ptr) free(ptr) +#else + extern void* UNITY_FIXTURE_MALLOC(size_t size); + extern void UNITY_FIXTURE_FREE(void* ptr); +#endif + +#define malloc unity_malloc +#define calloc unity_calloc +#define realloc unity_realloc +#define free unity_free + +void* unity_malloc(size_t size); +void* unity_calloc(size_t num, size_t size); +void* unity_realloc(void * oldMem, size_t size); +void unity_free(void * mem); + +#endif /* UNITY_FIXTURE_MALLOC_OVERRIDES_H_ */ diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/release/build.info b/components/spotify/cspot/bell/cJSON/tests/unity/release/build.info new file mode 100644 index 00000000..56d59128 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/release/build.info @@ -0,0 +1,2 @@ +122 + diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/release/version.info b/components/spotify/cspot/bell/cJSON/tests/unity/release/version.info new file mode 100644 index 00000000..cf12b30d --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/release/version.info @@ -0,0 +1,2 @@ +2.4.3 + diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/src/unity.c b/components/spotify/cspot/bell/cJSON/tests/unity/src/unity.c new file mode 100644 index 00000000..d02610a7 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/src/unity.c @@ -0,0 +1,1570 @@ +/* ========================================================================= + Unity Project - A Test Framework for C + Copyright (c) 2007-14 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +============================================================================ */ + +#define UNITY_INCLUDE_SETUP_STUBS +#include "unity.h" +#include + +/* If omitted from header, declare overrideable prototypes here so they're ready for use */ +#ifdef UNITY_OMIT_OUTPUT_CHAR_HEADER_DECLARATION +void UNITY_OUTPUT_CHAR(int); +#endif + +/* Helpful macros for us to use here in Assert functions */ +#define UNITY_FAIL_AND_BAIL { Unity.CurrentTestFailed = 1; TEST_ABORT(); } +#define UNITY_IGNORE_AND_BAIL { Unity.CurrentTestIgnored = 1; TEST_ABORT(); } +#define RETURN_IF_FAIL_OR_IGNORE if (Unity.CurrentTestFailed || Unity.CurrentTestIgnored) return + +struct UNITY_STORAGE_T Unity; + +#ifdef UNITY_OUTPUT_COLOR +static const char UnityStrOk[] = "\033[42mOK\033[00m"; +static const char UnityStrPass[] = "\033[42mPASS\033[00m"; +static const char UnityStrFail[] = "\033[41mFAIL\033[00m"; +static const char UnityStrIgnore[] = "\033[43mIGNORE\033[00m"; +#else +static const char UnityStrOk[] = "OK"; +static const char UnityStrPass[] = "PASS"; +static const char UnityStrFail[] = "FAIL"; +static const char UnityStrIgnore[] = "IGNORE"; +#endif +static const char UnityStrNull[] = "NULL"; +static const char UnityStrSpacer[] = ". "; +static const char UnityStrExpected[] = " Expected "; +static const char UnityStrWas[] = " Was "; +static const char UnityStrGt[] = " to be greater than "; +static const char UnityStrLt[] = " to be less than "; +static const char UnityStrOrEqual[] = "or equal to "; +static const char UnityStrElement[] = " Element "; +static const char UnityStrByte[] = " Byte "; +static const char UnityStrMemory[] = " Memory Mismatch."; +static const char UnityStrDelta[] = " Values Not Within Delta "; +static const char UnityStrPointless[] = " You Asked Me To Compare Nothing, Which Was Pointless."; +static const char UnityStrNullPointerForExpected[] = " Expected pointer to be NULL"; +static const char UnityStrNullPointerForActual[] = " Actual pointer was NULL"; +#ifndef UNITY_EXCLUDE_FLOAT +static const char UnityStrNot[] = "Not "; +static const char UnityStrInf[] = "Infinity"; +static const char UnityStrNegInf[] = "Negative Infinity"; +static const char UnityStrNaN[] = "NaN"; +static const char UnityStrDet[] = "Determinate"; +static const char UnityStrInvalidFloatTrait[] = "Invalid Float Trait"; +#endif +const char UnityStrErrFloat[] = "Unity Floating Point Disabled"; +const char UnityStrErrDouble[] = "Unity Double Precision Disabled"; +const char UnityStrErr64[] = "Unity 64-bit Support Disabled"; +static const char UnityStrBreaker[] = "-----------------------"; +static const char UnityStrResultsTests[] = " Tests "; +static const char UnityStrResultsFailures[] = " Failures "; +static const char UnityStrResultsIgnored[] = " Ignored "; +static const char UnityStrDetail1Name[] = UNITY_DETAIL1_NAME " "; +static const char UnityStrDetail2Name[] = " " UNITY_DETAIL2_NAME " "; + +/*----------------------------------------------- + * Pretty Printers & Test Result Output Handlers + *-----------------------------------------------*/ + +void UnityPrint(const char* string) +{ + const char* pch = string; + + if (pch != NULL) + { + while (*pch) + { + /* printable characters plus CR & LF are printed */ + if ((*pch <= 126) && (*pch >= 32)) + { + UNITY_OUTPUT_CHAR(*pch); + } + /* write escaped carriage returns */ + else if (*pch == 13) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('r'); + } + /* write escaped line feeds */ + else if (*pch == 10) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('n'); + } +#ifdef UNITY_OUTPUT_COLOR + /* print ANSI escape code */ + else if (*pch == 27 && *(pch + 1) == '[') + { + while (*pch && *pch != 'm') + { + UNITY_OUTPUT_CHAR(*pch); + pch++; + } + UNITY_OUTPUT_CHAR('m'); + } +#endif + /* unprintable characters are shown as codes */ + else + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)*pch, 2); + } + pch++; + } + } +} + +void UnityPrintLen(const char* string, const UNITY_UINT32 length) +{ + const char* pch = string; + + if (pch != NULL) + { + while (*pch && (UNITY_UINT32)(pch - string) < length) + { + /* printable characters plus CR & LF are printed */ + if ((*pch <= 126) && (*pch >= 32)) + { + UNITY_OUTPUT_CHAR(*pch); + } + /* write escaped carriage returns */ + else if (*pch == 13) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('r'); + } + /* write escaped line feeds */ + else if (*pch == 10) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('n'); + } + /* unprintable characters are shown as codes */ + else + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)*pch, 2); + } + pch++; + } + } +} + +/*-----------------------------------------------*/ +void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style) +{ + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + UnityPrintNumber(number); + } + else if ((style & UNITY_DISPLAY_RANGE_UINT) == UNITY_DISPLAY_RANGE_UINT) + { + UnityPrintNumberUnsigned((UNITY_UINT)number); + } + else + { + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)number, (char)((style & 0xF) * 2)); + } +} + +/*-----------------------------------------------*/ +void UnityPrintNumber(const UNITY_INT number_to_print) +{ + UNITY_UINT number = (UNITY_UINT)number_to_print; + + if (number_to_print < 0) + { + /* A negative number, including MIN negative */ + UNITY_OUTPUT_CHAR('-'); + number = (UNITY_UINT)(-number_to_print); + } + UnityPrintNumberUnsigned(number); +} + +/*----------------------------------------------- + * basically do an itoa using as little ram as possible */ +void UnityPrintNumberUnsigned(const UNITY_UINT number) +{ + UNITY_UINT divisor = 1; + + /* figure out initial divisor */ + while (number / divisor > 9) + { + divisor *= 10; + } + + /* now mod and print, then divide divisor */ + do + { + UNITY_OUTPUT_CHAR((char)('0' + (number / divisor % 10))); + divisor /= 10; + } while (divisor > 0); +} + +/*-----------------------------------------------*/ +void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print) +{ + int nibble; + char nibbles = nibbles_to_print; + if ((unsigned)nibbles > (2 * sizeof(number))) + nibbles = 2 * sizeof(number); + + while (nibbles > 0) + { + nibbles--; + nibble = (int)(number >> (nibbles * 4)) & 0x0F; + if (nibble <= 9) + { + UNITY_OUTPUT_CHAR((char)('0' + nibble)); + } + else + { + UNITY_OUTPUT_CHAR((char)('A' - 10 + nibble)); + } + } +} + +/*-----------------------------------------------*/ +void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number) +{ + UNITY_UINT current_bit = (UNITY_UINT)1 << (UNITY_INT_WIDTH - 1); + UNITY_INT32 i; + + for (i = 0; i < UNITY_INT_WIDTH; i++) + { + if (current_bit & mask) + { + if (current_bit & number) + { + UNITY_OUTPUT_CHAR('1'); + } + else + { + UNITY_OUTPUT_CHAR('0'); + } + } + else + { + UNITY_OUTPUT_CHAR('X'); + } + current_bit = current_bit >> 1; + } +} + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +/* This function prints a floating-point value in a format similar to + * printf("%.6g"). It can work with either single- or double-precision, + * but for simplicity, it prints only 6 significant digits in either case. + * Printing more than 6 digits accurately is hard (at least in the single- + * precision case) and isn't attempted here. */ +void UnityPrintFloat(const UNITY_DOUBLE input_number) +{ + UNITY_DOUBLE number = input_number; + + /* print minus sign (including for negative zero) */ + if (number < (double)0.0f || (number == (double)0.0f && (double)1.0f / number < (double)0.0f)) + { + UNITY_OUTPUT_CHAR('-'); + number = -number; + } + + /* handle zero, NaN, and +/- infinity */ + if (number == (double)0.0f) UnityPrint("0"); + else if (isnan(number)) UnityPrint("nan"); + else if (isinf(number)) UnityPrint("inf"); + else + { + int exponent = 0; + int decimals, digits; + UNITY_INT32 n; + char buf[16]; + + /* scale up or down by powers of 10 */ + while (number < (double)(100000.0f / 1e6f)) { number *= (double)1e6f; exponent -= 6; } + while (number < (double)100000.0f) { number *= (double)10.0f; exponent--; } + while (number > (double)(1000000.0f * 1e6f)) { number /= (double)1e6f; exponent += 6; } + while (number > (double)1000000.0f) { number /= (double)10.0f; exponent++; } + + /* round to nearest integer */ + n = ((UNITY_INT32)(number + number) + 1) / 2; + if (n > 999999) + { + n = 100000; + exponent++; + } + + /* determine where to place decimal point */ + decimals = (exponent <= 0 && exponent >= -9) ? -exponent : 5; + exponent += decimals; + + /* truncate trailing zeroes after decimal point */ + while (decimals > 0 && n % 10 == 0) + { + n /= 10; + decimals--; + } + + /* build up buffer in reverse order */ + digits = 0; + while (n != 0 || digits < decimals + 1) + { + buf[digits++] = (char)('0' + n % 10); + n /= 10; + } + while (digits > 0) + { + if(digits == decimals) UNITY_OUTPUT_CHAR('.'); + UNITY_OUTPUT_CHAR(buf[--digits]); + } + + /* print exponent if needed */ + if (exponent != 0) + { + UNITY_OUTPUT_CHAR('e'); + + if(exponent < 0) + { + UNITY_OUTPUT_CHAR('-'); + exponent = -exponent; + } + else + { + UNITY_OUTPUT_CHAR('+'); + } + + digits = 0; + while (exponent != 0 || digits < 2) + { + buf[digits++] = (char)('0' + exponent % 10); + exponent /= 10; + } + while (digits > 0) + { + UNITY_OUTPUT_CHAR(buf[--digits]); + } + } + } +} +#endif /* ! UNITY_EXCLUDE_FLOAT_PRINT */ + +/*-----------------------------------------------*/ +static void UnityTestResultsBegin(const char* file, const UNITY_LINE_TYPE line) +{ + UnityPrint(file); + UNITY_OUTPUT_CHAR(':'); + UnityPrintNumber((UNITY_INT)line); + UNITY_OUTPUT_CHAR(':'); + UnityPrint(Unity.CurrentTestName); + UNITY_OUTPUT_CHAR(':'); +} + +/*-----------------------------------------------*/ +static void UnityTestResultsFailBegin(const UNITY_LINE_TYPE line) +{ + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint(UnityStrFail); + UNITY_OUTPUT_CHAR(':'); +} + +/*-----------------------------------------------*/ +void UnityConcludeTest(void) +{ + if (Unity.CurrentTestIgnored) + { + Unity.TestIgnores++; + } + else if (!Unity.CurrentTestFailed) + { + UnityTestResultsBegin(Unity.TestFile, Unity.CurrentTestLineNumber); + UnityPrint(UnityStrPass); + } + else + { + Unity.TestFailures++; + } + + Unity.CurrentTestFailed = 0; + Unity.CurrentTestIgnored = 0; + UNITY_PRINT_EOL(); + UNITY_FLUSH_CALL(); +} + +/*-----------------------------------------------*/ +static void UnityAddMsgIfSpecified(const char* msg) +{ + if (msg) + { + UnityPrint(UnityStrSpacer); +#ifndef UNITY_EXCLUDE_DETAILS + if (Unity.CurrentDetail1) + { + UnityPrint(UnityStrDetail1Name); + UnityPrint(Unity.CurrentDetail1); + if (Unity.CurrentDetail2) + { + UnityPrint(UnityStrDetail2Name); + UnityPrint(Unity.CurrentDetail2); + } + UnityPrint(UnityStrSpacer); + } +#endif + UnityPrint(msg); + } +} + +/*-----------------------------------------------*/ +static void UnityPrintExpectedAndActualStrings(const char* expected, const char* actual) +{ + UnityPrint(UnityStrExpected); + if (expected != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrint(expected); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } + UnityPrint(UnityStrWas); + if (actual != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrint(actual); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } +} + +/*-----------------------------------------------*/ +static void UnityPrintExpectedAndActualStringsLen(const char* expected, + const char* actual, + const UNITY_UINT32 length) +{ + UnityPrint(UnityStrExpected); + if (expected != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrintLen(expected, length); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } + UnityPrint(UnityStrWas); + if (actual != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrintLen(actual, length); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } +} + +/*----------------------------------------------- + * Assertion & Control Helpers + *-----------------------------------------------*/ + +static int UnityIsOneArrayNull(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_LINE_TYPE lineNumber, + const char* msg) +{ + if (expected == actual) return 0; /* Both are NULL or same pointer */ + + /* print and return true if just expected is NULL */ + if (expected == NULL) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrNullPointerForExpected); + UnityAddMsgIfSpecified(msg); + return 1; + } + + /* print and return true if just actual is NULL */ + if (actual == NULL) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrNullPointerForActual); + UnityAddMsgIfSpecified(msg); + return 1; + } + + return 0; /* return false if neither is NULL */ +} + +/*----------------------------------------------- + * Assertion Functions + *-----------------------------------------------*/ + +void UnityAssertBits(const UNITY_INT mask, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if ((mask & expected) != (mask & actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)expected); + UnityPrint(UnityStrWas); + UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualNumber(const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (expected != actual) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expected, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold, + const UNITY_INT actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + int failed = 0; + RETURN_IF_FAIL_OR_IGNORE; + + if (threshold == actual && compare & UNITY_EQUAL_TO) return; + if (threshold == actual) failed = 1; + + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if (actual > threshold && compare & UNITY_SMALLER_THAN) failed = 1; + if (actual < threshold && compare & UNITY_GREATER_THAN) failed = 1; + } + else /* UINT or HEX */ + { + if ((UNITY_UINT)actual > (UNITY_UINT)threshold && compare & UNITY_SMALLER_THAN) failed = 1; + if ((UNITY_UINT)actual < (UNITY_UINT)threshold && compare & UNITY_GREATER_THAN) failed = 1; + } + + if (failed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(actual, style); + if (compare & UNITY_GREATER_THAN) UnityPrint(UnityStrGt); + if (compare & UNITY_SMALLER_THAN) UnityPrint(UnityStrLt); + if (compare & UNITY_EQUAL_TO) UnityPrint(UnityStrOrEqual); + UnityPrintNumberByStyle(threshold, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#define UnityPrintPointlessAndBail() \ +{ \ + UnityTestResultsFailBegin(lineNumber); \ + UnityPrint(UnityStrPointless); \ + UnityAddMsgIfSpecified(msg); \ + UNITY_FAIL_AND_BAIL; } + +/*-----------------------------------------------*/ +void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + unsigned int length = style & 0xF; + + RETURN_IF_FAIL_OR_IGNORE; + + if (num_elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) return; /* Both are NULL or same pointer */ + if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) + UNITY_FAIL_AND_BAIL; + + while (elements--) + { + UNITY_INT expect_val; + UNITY_INT actual_val; + switch (length) + { + case 1: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)actual; + break; + case 2: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)actual; + break; +#ifdef UNITY_SUPPORT_64 + case 8: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)actual; + break; +#endif + default: /* length 4 bytes */ + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)actual; + length = 4; + break; + } + + if (expect_val != actual_val) + { + if (style & UNITY_DISPLAY_RANGE_UINT && length < sizeof(expect_val)) + { /* For UINT, remove sign extension (padding 1's) from signed type casts above */ + UNITY_INT mask = 1; + mask = (mask << 8 * length) - 1; + expect_val &= mask; + actual_val &= mask; + } + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expect_val, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual_val, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + if (flags == UNITY_ARRAY_TO_ARRAY) + { + expected = (UNITY_INTERNAL_PTR)(length + (const char*)expected); + } + actual = (UNITY_INTERNAL_PTR)(length + (const char*)actual); + } +} + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_FLOAT +/* Wrap this define in a function with variable types as float or double */ +#define UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff) \ + if (isinf(expected) && isinf(actual) && ((expected < 0) == (actual < 0))) return 1; \ + if (UNITY_NAN_CHECK) return 1; \ + diff = actual - expected; \ + if (diff < 0) diff = -diff; \ + if (delta < 0) delta = -delta; \ + return !(isnan(diff) || isinf(diff) || (diff > delta)) + /* This first part of this condition will catch any NaN or Infinite values */ +#ifndef UNITY_NAN_NOT_EQUAL_NAN + #define UNITY_NAN_CHECK isnan(expected) && isnan(actual) +#else + #define UNITY_NAN_CHECK 0 +#endif + +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ + { \ + UnityPrint(UnityStrExpected); \ + UnityPrintFloat(expected); \ + UnityPrint(UnityStrWas); \ + UnityPrintFloat(actual); } +#else + #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ + UnityPrint(UnityStrDelta) +#endif /* UNITY_EXCLUDE_FLOAT_PRINT */ + +static int UnityFloatsWithin(UNITY_FLOAT delta, UNITY_FLOAT expected, UNITY_FLOAT actual) +{ + UNITY_FLOAT diff; + UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff); +} + +void UnityAssertEqualFloatArray(UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected, + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_expected = expected; + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_actual = actual; + + RETURN_IF_FAIL_OR_IGNORE; + + if (elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) return; /* Both are NULL or same pointer */ + if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) + UNITY_FAIL_AND_BAIL; + + while (elements--) + { + if (!UnityFloatsWithin(*ptr_expected * UNITY_FLOAT_PRECISION, *ptr_expected, *ptr_actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)*ptr_expected, (UNITY_DOUBLE)*ptr_actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + if (flags == UNITY_ARRAY_TO_ARRAY) + { + ptr_expected++; + } + ptr_actual++; + } +} + +/*-----------------------------------------------*/ +void UnityAssertFloatsWithin(const UNITY_FLOAT delta, + const UNITY_FLOAT expected, + const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + + if (!UnityFloatsWithin(delta, expected, actual)) + { + UnityTestResultsFailBegin(lineNumber); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)expected, (UNITY_DOUBLE)actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertFloatSpecial(const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style) +{ + const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet}; + UNITY_INT should_be_trait = ((UNITY_INT)style & 1); + UNITY_INT is_trait = !should_be_trait; + UNITY_INT trait_index = (UNITY_INT)(style >> 1); + + RETURN_IF_FAIL_OR_IGNORE; + + switch (style) + { + case UNITY_FLOAT_IS_INF: + case UNITY_FLOAT_IS_NOT_INF: + is_trait = isinf(actual) && (actual > 0); + break; + case UNITY_FLOAT_IS_NEG_INF: + case UNITY_FLOAT_IS_NOT_NEG_INF: + is_trait = isinf(actual) && (actual < 0); + break; + + case UNITY_FLOAT_IS_NAN: + case UNITY_FLOAT_IS_NOT_NAN: + is_trait = isnan(actual) ? 1 : 0; + break; + + case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */ + case UNITY_FLOAT_IS_NOT_DET: + is_trait = !isinf(actual) && !isnan(actual); + break; + + default: + trait_index = 0; + trait_names[0] = UnityStrInvalidFloatTrait; + break; + } + + if (is_trait != should_be_trait) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + if (!should_be_trait) + UnityPrint(UnityStrNot); + UnityPrint(trait_names[trait_index]); + UnityPrint(UnityStrWas); +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + UnityPrintFloat((UNITY_DOUBLE)actual); +#else + if (should_be_trait) + UnityPrint(UnityStrNot); + UnityPrint(trait_names[trait_index]); +#endif + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#endif /* not UNITY_EXCLUDE_FLOAT */ + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_DOUBLE +static int UnityDoublesWithin(UNITY_DOUBLE delta, UNITY_DOUBLE expected, UNITY_DOUBLE actual) +{ + UNITY_DOUBLE diff; + UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff); +} + +void UnityAssertEqualDoubleArray(UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected, + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_expected = expected; + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_actual = actual; + + RETURN_IF_FAIL_OR_IGNORE; + + if (elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) return; /* Both are NULL or same pointer */ + if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) + UNITY_FAIL_AND_BAIL; + + while (elements--) + { + if (!UnityDoublesWithin(*ptr_expected * UNITY_DOUBLE_PRECISION, *ptr_expected, *ptr_actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(*ptr_expected, *ptr_actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + if (flags == UNITY_ARRAY_TO_ARRAY) + { + ptr_expected++; + } + ptr_actual++; + } +} + +/*-----------------------------------------------*/ +void UnityAssertDoublesWithin(const UNITY_DOUBLE delta, + const UNITY_DOUBLE expected, + const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (!UnityDoublesWithin(delta, expected, actual)) + { + UnityTestResultsFailBegin(lineNumber); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ + +void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style) +{ + const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet}; + UNITY_INT should_be_trait = ((UNITY_INT)style & 1); + UNITY_INT is_trait = !should_be_trait; + UNITY_INT trait_index = (UNITY_INT)(style >> 1); + + RETURN_IF_FAIL_OR_IGNORE; + + switch (style) + { + case UNITY_FLOAT_IS_INF: + case UNITY_FLOAT_IS_NOT_INF: + is_trait = isinf(actual) && (actual > 0); + break; + case UNITY_FLOAT_IS_NEG_INF: + case UNITY_FLOAT_IS_NOT_NEG_INF: + is_trait = isinf(actual) && (actual < 0); + break; + + case UNITY_FLOAT_IS_NAN: + case UNITY_FLOAT_IS_NOT_NAN: + is_trait = isnan(actual) ? 1 : 0; + break; + + case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */ + case UNITY_FLOAT_IS_NOT_DET: + is_trait = !isinf(actual) && !isnan(actual); + break; + + default: + trait_index = 0; + trait_names[0] = UnityStrInvalidFloatTrait; + break; + } + + if (is_trait != should_be_trait) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + if (!should_be_trait) + UnityPrint(UnityStrNot); + UnityPrint(trait_names[trait_index]); + UnityPrint(UnityStrWas); +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + UnityPrintFloat(actual); +#else + if (should_be_trait) + UnityPrint(UnityStrNot); + UnityPrint(trait_names[trait_index]); +#endif + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#endif /* not UNITY_EXCLUDE_DOUBLE */ + +/*-----------------------------------------------*/ +void UnityAssertNumbersWithin(const UNITY_UINT delta, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if (actual > expected) + Unity.CurrentTestFailed = (UNITY_UINT)((UNITY_UINT)(actual - expected) > delta); + else + Unity.CurrentTestFailed = (UNITY_UINT)((UNITY_UINT)(expected - actual) > delta); + } + else + { + if ((UNITY_UINT)actual > (UNITY_UINT)expected) + Unity.CurrentTestFailed = (UNITY_UINT)((UNITY_UINT)(actual - expected) > delta); + else + Unity.CurrentTestFailed = (UNITY_UINT)((UNITY_UINT)(expected - actual) > delta); + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrDelta); + UnityPrintNumberByStyle((UNITY_INT)delta, style); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expected, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualString(const char* expected, + const char* actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + UNITY_UINT32 i; + + RETURN_IF_FAIL_OR_IGNORE; + + /* if both pointers not null compare the strings */ + if (expected && actual) + { + for (i = 0; expected[i] || actual[i]; i++) + { + if (expected[i] != actual[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* handle case of one pointers being null (if both null, test should pass) */ + if (expected != actual) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrintExpectedAndActualStrings(expected, actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualStringLen(const char* expected, + const char* actual, + const UNITY_UINT32 length, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + UNITY_UINT32 i; + + RETURN_IF_FAIL_OR_IGNORE; + + /* if both pointers not null compare the strings */ + if (expected && actual) + { + for (i = 0; (i < length) && (expected[i] || actual[i]); i++) + { + if (expected[i] != actual[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* handle case of one pointers being null (if both null, test should pass) */ + if (expected != actual) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrintExpectedAndActualStringsLen(expected, actual, length); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualStringArray(UNITY_INTERNAL_PTR expected, + const char** actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 i = 0; + UNITY_UINT32 j = 0; + const char* expd = NULL; + const char* act = NULL; + + RETURN_IF_FAIL_OR_IGNORE; + + /* if no elements, it's an error */ + if (num_elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if ((const void*)expected == (const void*)actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + if (flags != UNITY_ARRAY_TO_ARRAY) + { + expd = (const char*)expected; + } + + do + { + act = actual[j]; + if (flags == UNITY_ARRAY_TO_ARRAY) + { + expd = ((const char* const*)expected)[j]; + } + + /* if both pointers not null compare the strings */ + if (expd && act) + { + for (i = 0; expd[i] || act[i]; i++) + { + if (expd[i] != act[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* handle case of one pointers being null (if both null, test should pass) */ + if (expd != act) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + if (num_elements > 1) + { + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(j); + } + UnityPrintExpectedAndActualStrings(expd, act); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + } while (++j < num_elements); +} + +/*-----------------------------------------------*/ +void UnityAssertEqualMemory(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 length, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_PTR_ATTRIBUTE const unsigned char* ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected; + UNITY_PTR_ATTRIBUTE const unsigned char* ptr_act = (UNITY_PTR_ATTRIBUTE const unsigned char*)actual; + UNITY_UINT32 elements = num_elements; + UNITY_UINT32 bytes; + + RETURN_IF_FAIL_OR_IGNORE; + + if ((elements == 0) || (length == 0)) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) return; /* Both are NULL or same pointer */ + if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) + UNITY_FAIL_AND_BAIL; + + while (elements--) + { + bytes = length; + while (bytes--) + { + if (*ptr_exp != *ptr_act) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrMemory); + if (num_elements > 1) + { + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + } + UnityPrint(UnityStrByte); + UnityPrintNumberUnsigned(length - bytes - 1); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(*ptr_exp, UNITY_DISPLAY_STYLE_HEX8); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(*ptr_act, UNITY_DISPLAY_STYLE_HEX8); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + ptr_exp++; + ptr_act++; + } + if (flags == UNITY_ARRAY_TO_VAL) + { + ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected; + } + } +} + +/*-----------------------------------------------*/ + +static union +{ + UNITY_INT8 i8; + UNITY_INT16 i16; + UNITY_INT32 i32; +#ifdef UNITY_SUPPORT_64 + UNITY_INT64 i64; +#endif +#ifndef UNITY_EXCLUDE_FLOAT + float f; +#endif +#ifndef UNITY_EXCLUDE_DOUBLE + double d; +#endif +} UnityQuickCompare; + +UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size) +{ + switch(size) + { + case 1: + UnityQuickCompare.i8 = (UNITY_INT8)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i8); + + case 2: + UnityQuickCompare.i16 = (UNITY_INT16)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i16); + +#ifdef UNITY_SUPPORT_64 + case 8: + UnityQuickCompare.i64 = (UNITY_INT64)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i64); +#endif + default: /* 4 bytes */ + UnityQuickCompare.i32 = (UNITY_INT32)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i32); + } +} + +#ifndef UNITY_EXCLUDE_FLOAT +UNITY_INTERNAL_PTR UnityFloatToPtr(const float num) +{ + UnityQuickCompare.f = num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.f); +} +#endif + +#ifndef UNITY_EXCLUDE_DOUBLE +UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num) +{ + UnityQuickCompare.d = num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.d); +} +#endif + +/*----------------------------------------------- + * Control Functions + *-----------------------------------------------*/ + +void UnityFail(const char* msg, const UNITY_LINE_TYPE line) +{ + RETURN_IF_FAIL_OR_IGNORE; + + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint(UnityStrFail); + if (msg != NULL) + { + UNITY_OUTPUT_CHAR(':'); + +#ifndef UNITY_EXCLUDE_DETAILS + if (Unity.CurrentDetail1) + { + UnityPrint(UnityStrDetail1Name); + UnityPrint(Unity.CurrentDetail1); + if (Unity.CurrentDetail2) + { + UnityPrint(UnityStrDetail2Name); + UnityPrint(Unity.CurrentDetail2); + } + UnityPrint(UnityStrSpacer); + } +#endif + if (msg[0] != ' ') + { + UNITY_OUTPUT_CHAR(' '); + } + UnityPrint(msg); + } + + UNITY_FAIL_AND_BAIL; +} + +/*-----------------------------------------------*/ +void UnityIgnore(const char* msg, const UNITY_LINE_TYPE line) +{ + RETURN_IF_FAIL_OR_IGNORE; + + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint(UnityStrIgnore); + if (msg != NULL) + { + UNITY_OUTPUT_CHAR(':'); + UNITY_OUTPUT_CHAR(' '); + UnityPrint(msg); + } + UNITY_IGNORE_AND_BAIL; +} + +/*-----------------------------------------------*/ +void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum) +{ + Unity.CurrentTestName = FuncName; + Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)FuncLineNum; + Unity.NumberOfTests++; + UNITY_CLR_DETAILS(); + if (TEST_PROTECT()) + { + setUp(); + Func(); + } + if (TEST_PROTECT()) + { + tearDown(); + } + UnityConcludeTest(); +} + +/*-----------------------------------------------*/ +void UnityBegin(const char* filename) +{ + Unity.TestFile = filename; + Unity.CurrentTestName = NULL; + Unity.CurrentTestLineNumber = 0; + Unity.NumberOfTests = 0; + Unity.TestFailures = 0; + Unity.TestIgnores = 0; + Unity.CurrentTestFailed = 0; + Unity.CurrentTestIgnored = 0; + + UNITY_CLR_DETAILS(); + UNITY_OUTPUT_START(); +} + +/*-----------------------------------------------*/ +int UnityEnd(void) +{ + UNITY_PRINT_EOL(); + UnityPrint(UnityStrBreaker); + UNITY_PRINT_EOL(); + UnityPrintNumber((UNITY_INT)(Unity.NumberOfTests)); + UnityPrint(UnityStrResultsTests); + UnityPrintNumber((UNITY_INT)(Unity.TestFailures)); + UnityPrint(UnityStrResultsFailures); + UnityPrintNumber((UNITY_INT)(Unity.TestIgnores)); + UnityPrint(UnityStrResultsIgnored); + UNITY_PRINT_EOL(); + if (Unity.TestFailures == 0U) + { + UnityPrint(UnityStrOk); + } + else + { + UnityPrint(UnityStrFail); +#ifdef UNITY_DIFFERENTIATE_FINAL_FAIL + UNITY_OUTPUT_CHAR('E'); UNITY_OUTPUT_CHAR('D'); +#endif + } + UNITY_PRINT_EOL(); + UNITY_FLUSH_CALL(); + UNITY_OUTPUT_COMPLETE(); + return (int)(Unity.TestFailures); +} + +/*----------------------------------------------- + * Command Line Argument Support + *-----------------------------------------------*/ +#ifdef UNITY_USE_COMMAND_LINE_ARGS + +char* UnityOptionIncludeNamed = NULL; +char* UnityOptionExcludeNamed = NULL; +int UnityVerbosity = 1; + +int UnityParseOptions(int argc, char** argv) +{ + UnityOptionIncludeNamed = NULL; + UnityOptionExcludeNamed = NULL; + + for (int i = 1; i < argc; i++) + { + if (argv[i][0] == '-') + { + switch (argv[i][1]) + { + case 'l': /* list tests */ + return -1; + case 'n': /* include tests with name including this string */ + case 'f': /* an alias for -n */ + if (argv[i][2] == '=') + UnityOptionIncludeNamed = &argv[i][3]; + else if (++i < argc) + UnityOptionIncludeNamed = argv[i]; + else + { + UnityPrint("ERROR: No Test String to Include Matches For"); + UNITY_PRINT_EOL(); + return 1; + } + break; + case 'q': /* quiet */ + UnityVerbosity = 0; + break; + case 'v': /* verbose */ + UnityVerbosity = 2; + break; + case 'x': /* exclude tests with name including this string */ + if (argv[i][2] == '=') + UnityOptionExcludeNamed = &argv[i][3]; + else if (++i < argc) + UnityOptionExcludeNamed = argv[i]; + else + { + UnityPrint("ERROR: No Test String to Exclude Matches For"); + UNITY_PRINT_EOL(); + return 1; + } + break; + default: + UnityPrint("ERROR: Unknown Option "); + UNITY_OUTPUT_CHAR(argv[i][1]); + UNITY_PRINT_EOL(); + return 1; + } + } + } + + return 0; +} + +int IsStringInBiggerString(const char* longstring, const char* shortstring) +{ + const char* lptr = longstring; + const char* sptr = shortstring; + const char* lnext = lptr; + + if (*sptr == '*') + return 1; + + while (*lptr) + { + lnext = lptr + 1; + + /* If they current bytes match, go on to the next bytes */ + while (*lptr && *sptr && (*lptr == *sptr)) + { + lptr++; + sptr++; + + /* We're done if we match the entire string or up to a wildcard */ + if (*sptr == '*') + return 1; + if (*sptr == ',') + return 1; + if (*sptr == '"') + return 1; + if (*sptr == '\'') + return 1; + if (*sptr == ':') + return 2; + if (*sptr == 0) + return 1; + } + + /* Otherwise we start in the long pointer 1 character further and try again */ + lptr = lnext; + sptr = shortstring; + } + return 0; +} + +int UnityStringArgumentMatches(const char* str) +{ + int retval; + const char* ptr1; + const char* ptr2; + const char* ptrf; + + /* Go through the options and get the substrings for matching one at a time */ + ptr1 = str; + while (ptr1[0] != 0) + { + if ((ptr1[0] == '"') || (ptr1[0] == '\'')) + ptr1++; + + /* look for the start of the next partial */ + ptr2 = ptr1; + ptrf = 0; + do + { + ptr2++; + if ((ptr2[0] == ':') && (ptr2[1] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ',')) + ptrf = &ptr2[1]; + } while ((ptr2[0] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ',')); + while ((ptr2[0] != 0) && ((ptr2[0] == ':') || (ptr2[0] == '\'') || (ptr2[0] == '"') || (ptr2[0] == ','))) + ptr2++; + + /* done if complete filename match */ + retval = IsStringInBiggerString(Unity.TestFile, ptr1); + if (retval == 1) + return retval; + + /* done if testname match after filename partial match */ + if ((retval == 2) && (ptrf != 0)) + { + if (IsStringInBiggerString(Unity.CurrentTestName, ptrf)) + return 1; + } + + /* done if complete testname match */ + if (IsStringInBiggerString(Unity.CurrentTestName, ptr1) == 1) + return 1; + + ptr1 = ptr2; + } + + /* we couldn't find a match for any substrings */ + return 0; +} + +int UnityTestMatches(void) +{ + /* Check if this test name matches the included test pattern */ + int retval; + if (UnityOptionIncludeNamed) + { + retval = UnityStringArgumentMatches(UnityOptionIncludeNamed); + } + else + retval = 1; + + /* Check if this test name matches the excluded test pattern */ + if (UnityOptionExcludeNamed) + { + if (UnityStringArgumentMatches(UnityOptionExcludeNamed)) + retval = 0; + } + return retval; +} + +#endif /* UNITY_USE_COMMAND_LINE_ARGS */ +/*-----------------------------------------------*/ diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/src/unity.h b/components/spotify/cspot/bell/cJSON/tests/unity/src/unity.h new file mode 100644 index 00000000..32ff0e6d --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/src/unity.h @@ -0,0 +1,503 @@ +/* ========================================== + Unity Project - A Test Framework for C + Copyright (c) 2007-14 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +========================================== */ + +#ifndef UNITY_FRAMEWORK_H +#define UNITY_FRAMEWORK_H +#define UNITY + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "unity_internals.h" + +/*------------------------------------------------------- + * Test Setup / Teardown + *-------------------------------------------------------*/ + +/* These functions are intended to be called before and after each test. */ +void setUp(void); +void tearDown(void); + +/* These functions are intended to be called at the beginning and end of an + * entire test suite. suiteTearDown() is passed the number of tests that + * failed, and its return value becomes the exit code of main(). */ +void suiteSetUp(void); +int suiteTearDown(int num_failures); + +/* If the compiler supports it, the following block provides stub + * implementations of the above functions as weak symbols. Note that on + * some platforms (MinGW for example), weak function implementations need + * to be in the same translation unit they are called from. This can be + * achieved by defining UNITY_INCLUDE_SETUP_STUBS before including unity.h. */ +#ifdef UNITY_INCLUDE_SETUP_STUBS + #ifdef UNITY_WEAK_ATTRIBUTE + UNITY_WEAK_ATTRIBUTE void setUp(void) { } + UNITY_WEAK_ATTRIBUTE void tearDown(void) { } + UNITY_WEAK_ATTRIBUTE void suiteSetUp(void) { } + UNITY_WEAK_ATTRIBUTE int suiteTearDown(int num_failures) { return num_failures; } + #elif defined(UNITY_WEAK_PRAGMA) + #pragma weak setUp + void setUp(void) { } + #pragma weak tearDown + void tearDown(void) { } + #pragma weak suiteSetUp + void suiteSetUp(void) { } + #pragma weak suiteTearDown + int suiteTearDown(int num_failures) { return num_failures; } + #endif +#endif + +/*------------------------------------------------------- + * Configuration Options + *------------------------------------------------------- + * All options described below should be passed as a compiler flag to all files using Unity. If you must add #defines, place them BEFORE the #include above. + + * Integers/longs/pointers + * - Unity attempts to automatically discover your integer sizes + * - define UNITY_EXCLUDE_STDINT_H to stop attempting to look in + * - define UNITY_EXCLUDE_LIMITS_H to stop attempting to look in + * - If you cannot use the automatic methods above, you can force Unity by using these options: + * - define UNITY_SUPPORT_64 + * - set UNITY_INT_WIDTH + * - set UNITY_LONG_WIDTH + * - set UNITY_POINTER_WIDTH + + * Floats + * - define UNITY_EXCLUDE_FLOAT to disallow floating point comparisons + * - define UNITY_FLOAT_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_FLOAT + * - define UNITY_FLOAT_TYPE to specify doubles instead of single precision floats + * - define UNITY_INCLUDE_DOUBLE to allow double floating point comparisons + * - define UNITY_EXCLUDE_DOUBLE to disallow double floating point comparisons (default) + * - define UNITY_DOUBLE_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_DOUBLE + * - define UNITY_DOUBLE_TYPE to specify something other than double + * - define UNITY_EXCLUDE_FLOAT_PRINT to trim binary size, won't print floating point values in errors + + * Output + * - by default, Unity prints to standard out with putchar. define UNITY_OUTPUT_CHAR(a) with a different function if desired + * - define UNITY_DIFFERENTIATE_FINAL_FAIL to print FAILED (vs. FAIL) at test end summary - for automated search for failure + + * Optimization + * - by default, line numbers are stored in unsigned shorts. Define UNITY_LINE_TYPE with a different type if your files are huge + * - by default, test and failure counters are unsigned shorts. Define UNITY_COUNTER_TYPE with a different type if you want to save space or have more than 65535 Tests. + + * Test Cases + * - define UNITY_SUPPORT_TEST_CASES to include the TEST_CASE macro, though really it's mostly about the runner generator script + + * Parameterized Tests + * - you'll want to create a define of TEST_CASE(...) which basically evaluates to nothing + + * Tests with Arguments + * - you'll want to define UNITY_USE_COMMAND_LINE_ARGS if you have the test runner passing arguments to Unity + + *------------------------------------------------------- + * Basic Fail and Ignore + *-------------------------------------------------------*/ + +#define TEST_FAIL_MESSAGE(message) UNITY_TEST_FAIL(__LINE__, (message)) +#define TEST_FAIL() UNITY_TEST_FAIL(__LINE__, NULL) +#define TEST_IGNORE_MESSAGE(message) UNITY_TEST_IGNORE(__LINE__, (message)) +#define TEST_IGNORE() UNITY_TEST_IGNORE(__LINE__, NULL) +#define TEST_ONLY() + +/* It is not necessary for you to call PASS. A PASS condition is assumed if nothing fails. + * This method allows you to abort a test immediately with a PASS state, ignoring the remainder of the test. */ +#define TEST_PASS() TEST_ABORT() + +/* This macro does nothing, but it is useful for build tools (like Ceedling) to make use of this to figure out + * which files should be linked to in order to perform a test. Use it like TEST_FILE("sandwiches.c") */ +#define TEST_FILE(a) + +/*------------------------------------------------------- + * Test Asserts (simple) + *-------------------------------------------------------*/ + +/* Boolean */ +#define TEST_ASSERT(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expression Evaluated To FALSE") +#define TEST_ASSERT_TRUE(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expected TRUE Was FALSE") +#define TEST_ASSERT_UNLESS(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expression Evaluated To TRUE") +#define TEST_ASSERT_FALSE(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expected FALSE Was TRUE") +#define TEST_ASSERT_NULL(pointer) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, " Expected NULL") +#define TEST_ASSERT_NOT_NULL(pointer) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, " Expected Non-NULL") + +/* Integers (of all sizes) */ +#define TEST_ASSERT_EQUAL_INT(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal") +#define TEST_ASSERT_EQUAL_UINT(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX8(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX16(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX32(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX64(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS(mask, expected, actual) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS_HIGH(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(-1), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS_LOW(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(0), (actual), __LINE__, NULL) +#define TEST_ASSERT_BIT_HIGH(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(-1), (actual), __LINE__, NULL) +#define TEST_ASSERT_BIT_LOW(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, NULL) + +/* Integer Greater Than/ Less Than (of all sizes) */ +#define TEST_ASSERT_GREATER_THAN(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_LESS_THAN(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_GREATER_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_LESS_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) + +/* Integer Ranges (of all sizes) */ +#define TEST_ASSERT_INT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, NULL) + +/* Structs and Strings */ +#define TEST_ASSERT_EQUAL_PTR(expected, actual) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING(expected, actual) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_MEMORY(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, NULL) + +/* Arrays */ +#define TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, NULL) + +/* Arrays Compared To Single Value */ +#define TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, NULL) + +/* Floating Point (If Enabled) */ +#define TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_FLOAT(expected, actual) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, NULL) + +/* Double (If Enabled) */ +#define TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_DOUBLE(expected, actual) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, NULL) + +/*------------------------------------------------------- + * Test Asserts (with additional messages) + *-------------------------------------------------------*/ + +/* Boolean */ +#define TEST_ASSERT_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message)) +#define TEST_ASSERT_TRUE_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message)) +#define TEST_ASSERT_UNLESS_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) +#define TEST_ASSERT_FALSE_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) +#define TEST_ASSERT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, (message)) +#define TEST_ASSERT_NOT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, (message)) + +/* Integers (of all sizes) */ +#define TEST_ASSERT_EQUAL_INT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_MESSAGE(mask, expected, actual, message) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_HIGH_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(-1), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_LOW_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(0), (actual), __LINE__, (message)) +#define TEST_ASSERT_BIT_HIGH_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(-1), (actual), __LINE__, (message)) +#define TEST_ASSERT_BIT_LOW_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, (message)) + +/* Integer Greater Than/ Less Than (of all sizes) */ +#define TEST_ASSERT_GREATER_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_LESS_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_GREATER_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_LESS_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, (message)) + +/* Integer Ranges (of all sizes) */ +#define TEST_ASSERT_INT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, (message)) + +/* Structs and Strings */ +#define TEST_ASSERT_EQUAL_PTR_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_MEMORY_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, (message)) + +/* Arrays */ +#define TEST_ASSERT_EQUAL_INT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_PTR_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_MEMORY_ARRAY_MESSAGE(expected, actual, len, num_elements, message) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, (message)) + +/* Arrays Compared To Single Value*/ +#define TEST_ASSERT_EACH_EQUAL_INT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_PTR_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_STRING_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_MEMORY_MESSAGE(expected, actual, len, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, (message)) + +/* Floating Point (If Enabled) */ +#define TEST_ASSERT_FLOAT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_FLOAT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_FLOAT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, (message)) + +/* Double (If Enabled) */ +#define TEST_ASSERT_DOUBLE_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_DOUBLE_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_DOUBLE_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, (message)) + +/* end of UNITY_FRAMEWORK_H */ +#ifdef __cplusplus +} +#endif +#endif diff --git a/components/spotify/cspot/bell/cJSON/tests/unity/src/unity_internals.h b/components/spotify/cspot/bell/cJSON/tests/unity/src/unity_internals.h new file mode 100644 index 00000000..f78cebac --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity/src/unity_internals.h @@ -0,0 +1,870 @@ +/* ========================================== + Unity Project - A Test Framework for C + Copyright (c) 2007-14 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +========================================== */ + +#ifndef UNITY_INTERNALS_H +#define UNITY_INTERNALS_H + +#include "../examples/unity_config.h" + +#ifndef UNITY_EXCLUDE_SETJMP_H +#include +#endif + +#ifndef UNITY_EXCLUDE_MATH_H +#include +#endif + +/* Unity Attempts to Auto-Detect Integer Types + * Attempt 1: UINT_MAX, ULONG_MAX in , or default to 32 bits + * Attempt 2: UINTPTR_MAX in , or default to same size as long + * The user may override any of these derived constants: + * UNITY_INT_WIDTH, UNITY_LONG_WIDTH, UNITY_POINTER_WIDTH */ +#ifndef UNITY_EXCLUDE_STDINT_H +#include +#endif + +#ifndef UNITY_EXCLUDE_LIMITS_H +#include +#endif + +/*------------------------------------------------------- + * Guess Widths If Not Specified + *-------------------------------------------------------*/ + +/* Determine the size of an int, if not already specified. + * We cannot use sizeof(int), because it is not yet defined + * at this stage in the translation of the C program. + * Therefore, infer it from UINT_MAX if possible. */ +#ifndef UNITY_INT_WIDTH + #ifdef UINT_MAX + #if (UINT_MAX == 0xFFFF) + #define UNITY_INT_WIDTH (16) + #elif (UINT_MAX == 0xFFFFFFFF) + #define UNITY_INT_WIDTH (32) + #elif (UINT_MAX == 0xFFFFFFFFFFFFFFFF) + #define UNITY_INT_WIDTH (64) + #endif + #else /* Set to default */ + #define UNITY_INT_WIDTH (32) + #endif /* UINT_MAX */ +#endif + +/* Determine the size of a long, if not already specified. */ +#ifndef UNITY_LONG_WIDTH + #ifdef ULONG_MAX + #if (ULONG_MAX == 0xFFFF) + #define UNITY_LONG_WIDTH (16) + #elif (ULONG_MAX == 0xFFFFFFFF) + #define UNITY_LONG_WIDTH (32) + #elif (ULONG_MAX == 0xFFFFFFFFFFFFFFFF) + #define UNITY_LONG_WIDTH (64) + #endif + #else /* Set to default */ + #define UNITY_LONG_WIDTH (32) + #endif /* ULONG_MAX */ +#endif + +/* Determine the size of a pointer, if not already specified. */ +#ifndef UNITY_POINTER_WIDTH + #ifdef UINTPTR_MAX + #if (UINTPTR_MAX <= 0xFFFF) + #define UNITY_POINTER_WIDTH (16) + #elif (UINTPTR_MAX <= 0xFFFFFFFF) + #define UNITY_POINTER_WIDTH (32) + #elif (UINTPTR_MAX <= 0xFFFFFFFFFFFFFFFF) + #define UNITY_POINTER_WIDTH (64) + #endif + #else /* Set to default */ + #define UNITY_POINTER_WIDTH UNITY_LONG_WIDTH + #endif /* UINTPTR_MAX */ +#endif + +/*------------------------------------------------------- + * Int Support (Define types based on detected sizes) + *-------------------------------------------------------*/ + +#if (UNITY_INT_WIDTH == 32) + typedef unsigned char UNITY_UINT8; + typedef unsigned short UNITY_UINT16; + typedef unsigned int UNITY_UINT32; + typedef signed char UNITY_INT8; + typedef signed short UNITY_INT16; + typedef signed int UNITY_INT32; +#elif (UNITY_INT_WIDTH == 16) + typedef unsigned char UNITY_UINT8; + typedef unsigned int UNITY_UINT16; + typedef unsigned long UNITY_UINT32; + typedef signed char UNITY_INT8; + typedef signed int UNITY_INT16; + typedef signed long UNITY_INT32; +#else + #error Invalid UNITY_INT_WIDTH specified! (16 or 32 are supported) +#endif + +/*------------------------------------------------------- + * 64-bit Support + *-------------------------------------------------------*/ + +#ifndef UNITY_SUPPORT_64 + #if UNITY_LONG_WIDTH == 64 || UNITY_POINTER_WIDTH == 64 + #define UNITY_SUPPORT_64 + #endif +#endif + +#ifndef UNITY_SUPPORT_64 + /* No 64-bit Support */ + typedef UNITY_UINT32 UNITY_UINT; + typedef UNITY_INT32 UNITY_INT; +#else + + /* 64-bit Support */ + #if (UNITY_LONG_WIDTH == 32) + typedef unsigned long long UNITY_UINT64; + typedef signed long long UNITY_INT64; + #elif (UNITY_LONG_WIDTH == 64) + typedef unsigned long UNITY_UINT64; + typedef signed long UNITY_INT64; + #else + #error Invalid UNITY_LONG_WIDTH specified! (32 or 64 are supported) + #endif + typedef UNITY_UINT64 UNITY_UINT; + typedef UNITY_INT64 UNITY_INT; + +#endif + +/*------------------------------------------------------- + * Pointer Support + *-------------------------------------------------------*/ + +#if (UNITY_POINTER_WIDTH == 32) +#define UNITY_PTR_TO_INT UNITY_INT32 +#define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX32 +#elif (UNITY_POINTER_WIDTH == 64) +#define UNITY_PTR_TO_INT UNITY_INT64 +#define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX64 +#elif (UNITY_POINTER_WIDTH == 16) +#define UNITY_PTR_TO_INT UNITY_INT16 +#define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX16 +#else + #error Invalid UNITY_POINTER_WIDTH specified! (16, 32 or 64 are supported) +#endif + +#ifndef UNITY_PTR_ATTRIBUTE +#define UNITY_PTR_ATTRIBUTE +#endif + +#ifndef UNITY_INTERNAL_PTR +#define UNITY_INTERNAL_PTR UNITY_PTR_ATTRIBUTE const void* +#endif + +/*------------------------------------------------------- + * Float Support + *-------------------------------------------------------*/ + +#ifdef UNITY_EXCLUDE_FLOAT + +/* No Floating Point Support */ +#ifndef UNITY_EXCLUDE_DOUBLE +#define UNITY_EXCLUDE_DOUBLE /* Remove double when excluding float support */ +#endif +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +#define UNITY_EXCLUDE_FLOAT_PRINT +#endif + +#else + +/* Floating Point Support */ +#ifndef UNITY_FLOAT_PRECISION +#define UNITY_FLOAT_PRECISION (0.00001f) +#endif +#ifndef UNITY_FLOAT_TYPE +#define UNITY_FLOAT_TYPE float +#endif +typedef UNITY_FLOAT_TYPE UNITY_FLOAT; + +/* isinf & isnan macros should be provided by math.h */ +#ifndef isinf +/* The value of Inf - Inf is NaN */ +#define isinf(n) (isnan((n) - (n)) && !isnan(n)) +#endif + +#ifndef isnan +/* NaN is the only floating point value that does NOT equal itself. + * Therefore if n != n, then it is NaN. */ +#define isnan(n) ((n != n) ? 1 : 0) +#endif + +#endif + +/*------------------------------------------------------- + * Double Float Support + *-------------------------------------------------------*/ + +/* unlike float, we DON'T include by default */ +#if defined(UNITY_EXCLUDE_DOUBLE) || !defined(UNITY_INCLUDE_DOUBLE) + + /* No Floating Point Support */ + #ifndef UNITY_EXCLUDE_DOUBLE + #define UNITY_EXCLUDE_DOUBLE + #else + #undef UNITY_INCLUDE_DOUBLE + #endif + + #ifndef UNITY_EXCLUDE_FLOAT + #ifndef UNITY_DOUBLE_TYPE + #define UNITY_DOUBLE_TYPE double + #endif + typedef UNITY_FLOAT UNITY_DOUBLE; + /* For parameter in UnityPrintFloat(UNITY_DOUBLE), which aliases to double or float */ + #endif + +#else + + /* Double Floating Point Support */ + #ifndef UNITY_DOUBLE_PRECISION + #define UNITY_DOUBLE_PRECISION (1e-12) + #endif + + #ifndef UNITY_DOUBLE_TYPE + #define UNITY_DOUBLE_TYPE double + #endif + typedef UNITY_DOUBLE_TYPE UNITY_DOUBLE; + +#endif + +/*------------------------------------------------------- + * Output Method: stdout (DEFAULT) + *-------------------------------------------------------*/ +#ifndef UNITY_OUTPUT_CHAR +/* Default to using putchar, which is defined in stdio.h */ +#include +#define UNITY_OUTPUT_CHAR(a) (void)putchar(a) +#else + /* If defined as something else, make sure we declare it here so it's ready for use */ + #ifdef UNITY_OUTPUT_CHAR_HEADER_DECLARATION +extern void UNITY_OUTPUT_CHAR_HEADER_DECLARATION; + #endif +#endif + +#ifndef UNITY_OUTPUT_FLUSH +#ifdef UNITY_USE_FLUSH_STDOUT +/* We want to use the stdout flush utility */ +#include +#define UNITY_OUTPUT_FLUSH() (void)fflush(stdout) +#else +/* We've specified nothing, therefore flush should just be ignored */ +#define UNITY_OUTPUT_FLUSH() +#endif +#else +/* We've defined flush as something else, so make sure we declare it here so it's ready for use */ +#ifdef UNITY_OUTPUT_FLUSH_HEADER_DECLARATION +extern void UNITY_OMIT_OUTPUT_FLUSH_HEADER_DECLARATION; +#endif +#endif + +#ifndef UNITY_OUTPUT_FLUSH +#define UNITY_FLUSH_CALL() +#else +#define UNITY_FLUSH_CALL() UNITY_OUTPUT_FLUSH() +#endif + +#ifndef UNITY_PRINT_EOL +#define UNITY_PRINT_EOL() UNITY_OUTPUT_CHAR('\n') +#endif + +#ifndef UNITY_OUTPUT_START +#define UNITY_OUTPUT_START() +#endif + +#ifndef UNITY_OUTPUT_COMPLETE +#define UNITY_OUTPUT_COMPLETE() +#endif + +/*------------------------------------------------------- + * Footprint + *-------------------------------------------------------*/ + +#ifndef UNITY_LINE_TYPE +#define UNITY_LINE_TYPE UNITY_UINT +#endif + +#ifndef UNITY_COUNTER_TYPE +#define UNITY_COUNTER_TYPE UNITY_UINT +#endif + +/*------------------------------------------------------- + * Language Features Available + *-------------------------------------------------------*/ +#if !defined(UNITY_WEAK_ATTRIBUTE) && !defined(UNITY_WEAK_PRAGMA) +# if defined(__GNUC__) || defined(__ghs__) /* __GNUC__ includes clang */ +# if !(defined(__WIN32__) && defined(__clang__)) && !defined(__TMS470__) +# define UNITY_WEAK_ATTRIBUTE __attribute__((weak)) +# endif +# endif +#endif + +#ifdef UNITY_NO_WEAK +# undef UNITY_WEAK_ATTRIBUTE +# undef UNITY_WEAK_PRAGMA +#endif + + +/*------------------------------------------------------- + * Internal Structs Needed + *-------------------------------------------------------*/ + +typedef void (*UnityTestFunction)(void); + +#define UNITY_DISPLAY_RANGE_INT (0x10) +#define UNITY_DISPLAY_RANGE_UINT (0x20) +#define UNITY_DISPLAY_RANGE_HEX (0x40) + +typedef enum +{ +UNITY_DISPLAY_STYLE_INT = sizeof(int)+ UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT8 = 1 + UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT16 = 2 + UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT32 = 4 + UNITY_DISPLAY_RANGE_INT, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_INT64 = 8 + UNITY_DISPLAY_RANGE_INT, +#endif + +UNITY_DISPLAY_STYLE_UINT = sizeof(unsigned) + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT8 = 1 + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT16 = 2 + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT32 = 4 + UNITY_DISPLAY_RANGE_UINT, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_UINT64 = 8 + UNITY_DISPLAY_RANGE_UINT, +#endif + + UNITY_DISPLAY_STYLE_HEX8 = 1 + UNITY_DISPLAY_RANGE_HEX, + UNITY_DISPLAY_STYLE_HEX16 = 2 + UNITY_DISPLAY_RANGE_HEX, + UNITY_DISPLAY_STYLE_HEX32 = 4 + UNITY_DISPLAY_RANGE_HEX, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_HEX64 = 8 + UNITY_DISPLAY_RANGE_HEX, +#endif + + UNITY_DISPLAY_STYLE_UNKNOWN +} UNITY_DISPLAY_STYLE_T; + +typedef enum +{ + UNITY_EQUAL_TO = 1, + UNITY_GREATER_THAN = 2, + UNITY_GREATER_OR_EQUAL = 2 + UNITY_EQUAL_TO, + UNITY_SMALLER_THAN = 4, + UNITY_SMALLER_OR_EQUAL = 4 + UNITY_EQUAL_TO +} UNITY_COMPARISON_T; + +#ifndef UNITY_EXCLUDE_FLOAT +typedef enum UNITY_FLOAT_TRAIT +{ + UNITY_FLOAT_IS_NOT_INF = 0, + UNITY_FLOAT_IS_INF, + UNITY_FLOAT_IS_NOT_NEG_INF, + UNITY_FLOAT_IS_NEG_INF, + UNITY_FLOAT_IS_NOT_NAN, + UNITY_FLOAT_IS_NAN, + UNITY_FLOAT_IS_NOT_DET, + UNITY_FLOAT_IS_DET, + UNITY_FLOAT_INVALID_TRAIT +} UNITY_FLOAT_TRAIT_T; +#endif + +typedef enum +{ + UNITY_ARRAY_TO_VAL = 0, + UNITY_ARRAY_TO_ARRAY +} UNITY_FLAGS_T; + +struct UNITY_STORAGE_T +{ + const char* TestFile; + const char* CurrentTestName; +#ifndef UNITY_EXCLUDE_DETAILS + const char* CurrentDetail1; + const char* CurrentDetail2; +#endif + UNITY_LINE_TYPE CurrentTestLineNumber; + UNITY_COUNTER_TYPE NumberOfTests; + UNITY_COUNTER_TYPE TestFailures; + UNITY_COUNTER_TYPE TestIgnores; + UNITY_COUNTER_TYPE CurrentTestFailed; + UNITY_COUNTER_TYPE CurrentTestIgnored; +#ifndef UNITY_EXCLUDE_SETJMP_H + jmp_buf AbortFrame; +#endif +}; + +extern struct UNITY_STORAGE_T Unity; + +/*------------------------------------------------------- + * Test Suite Management + *-------------------------------------------------------*/ + +void UnityBegin(const char* filename); +int UnityEnd(void); +void UnityConcludeTest(void); +void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum); + +/*------------------------------------------------------- + * Details Support + *-------------------------------------------------------*/ + +#ifdef UNITY_EXCLUDE_DETAILS +#define UNITY_CLR_DETAILS() +#define UNITY_SET_DETAIL(d1) +#define UNITY_SET_DETAILS(d1,d2) +#else +#define UNITY_CLR_DETAILS() { Unity.CurrentDetail1 = 0; Unity.CurrentDetail2 = 0; } +#define UNITY_SET_DETAIL(d1) { Unity.CurrentDetail1 = d1; Unity.CurrentDetail2 = 0; } +#define UNITY_SET_DETAILS(d1,d2) { Unity.CurrentDetail1 = d1; Unity.CurrentDetail2 = d2; } + +#ifndef UNITY_DETAIL1_NAME +#define UNITY_DETAIL1_NAME "Function" +#endif + +#ifndef UNITY_DETAIL2_NAME +#define UNITY_DETAIL2_NAME "Argument" +#endif +#endif + +/*------------------------------------------------------- + * Test Output + *-------------------------------------------------------*/ + +void UnityPrint(const char* string); +void UnityPrintLen(const char* string, const UNITY_UINT32 length); +void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number); +void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style); +void UnityPrintNumber(const UNITY_INT number); +void UnityPrintNumberUnsigned(const UNITY_UINT number); +void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles); + +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +void UnityPrintFloat(const UNITY_DOUBLE input_number); +#endif + +/*------------------------------------------------------- + * Test Assertion Functions + *------------------------------------------------------- + * Use the macros below this section instead of calling + * these directly. The macros have a consistent naming + * convention and will pull in file and line information + * for you. */ + +void UnityAssertEqualNumber(const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold, + const UNITY_INT actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags); + +void UnityAssertBits(const UNITY_INT mask, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualString(const char* expected, + const char* actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualStringLen(const char* expected, + const char* actual, + const UNITY_UINT32 length, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualStringArray( UNITY_INTERNAL_PTR expected, + const char** actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertEqualMemory( UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 length, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertNumbersWithin(const UNITY_UINT delta, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityFail(const char* message, const UNITY_LINE_TYPE line); + +void UnityIgnore(const char* message, const UNITY_LINE_TYPE line); + +#ifndef UNITY_EXCLUDE_FLOAT +void UnityAssertFloatsWithin(const UNITY_FLOAT delta, + const UNITY_FLOAT expected, + const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualFloatArray(UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected, + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertFloatSpecial(const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style); +#endif + +#ifndef UNITY_EXCLUDE_DOUBLE +void UnityAssertDoublesWithin(const UNITY_DOUBLE delta, + const UNITY_DOUBLE expected, + const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualDoubleArray(UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected, + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style); +#endif + +/*------------------------------------------------------- + * Helpers + *-------------------------------------------------------*/ + +UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size); +#ifndef UNITY_EXCLUDE_FLOAT +UNITY_INTERNAL_PTR UnityFloatToPtr(const float num); +#endif +#ifndef UNITY_EXCLUDE_DOUBLE +UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num); +#endif + +/*------------------------------------------------------- + * Error Strings We Might Need + *-------------------------------------------------------*/ + +extern const char UnityStrErrFloat[]; +extern const char UnityStrErrDouble[]; +extern const char UnityStrErr64[]; + +/*------------------------------------------------------- + * Test Running Macros + *-------------------------------------------------------*/ + +#ifndef UNITY_EXCLUDE_SETJMP_H +#define TEST_PROTECT() (setjmp(Unity.AbortFrame) == 0) +#define TEST_ABORT() longjmp(Unity.AbortFrame, 1) +#else +#define TEST_PROTECT() 1 +#define TEST_ABORT() return +#endif + +/* This tricky series of macros gives us an optional line argument to treat it as RUN_TEST(func, num=__LINE__) */ +#ifndef RUN_TEST +#ifdef __STDC_VERSION__ +#if __STDC_VERSION__ >= 199901L +#define RUN_TEST(...) UnityDefaultTestRun(RUN_TEST_FIRST(__VA_ARGS__), RUN_TEST_SECOND(__VA_ARGS__)) +#define RUN_TEST_FIRST(...) RUN_TEST_FIRST_HELPER(__VA_ARGS__, throwaway) +#define RUN_TEST_FIRST_HELPER(first, ...) (first), #first +#define RUN_TEST_SECOND(...) RUN_TEST_SECOND_HELPER(__VA_ARGS__, __LINE__, throwaway) +#define RUN_TEST_SECOND_HELPER(first, second, ...) (second) +#endif +#endif +#endif + +/* If we can't do the tricky version, we'll just have to require them to always include the line number */ +#ifndef RUN_TEST +#ifdef CMOCK +#define RUN_TEST(func, num) UnityDefaultTestRun(func, #func, num) +#else +#define RUN_TEST(func) UnityDefaultTestRun(func, #func, __LINE__) +#endif +#endif + +#define TEST_LINE_NUM (Unity.CurrentTestLineNumber) +#define TEST_IS_IGNORED (Unity.CurrentTestIgnored) +#define UNITY_NEW_TEST(a) \ + Unity.CurrentTestName = (a); \ + Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)(__LINE__); \ + Unity.NumberOfTests++; + +#ifndef UNITY_BEGIN +#define UNITY_BEGIN() UnityBegin(__FILE__) +#endif + +#ifndef UNITY_END +#define UNITY_END() UnityEnd() +#endif + +/*----------------------------------------------- + * Command Line Argument Support + *-----------------------------------------------*/ + +#ifdef UNITY_USE_COMMAND_LINE_ARGS +int UnityParseOptions(int argc, char** argv); +int UnityTestMatches(void); +#endif + +/*------------------------------------------------------- + * Basic Fail and Ignore + *-------------------------------------------------------*/ + +#define UNITY_TEST_FAIL(line, message) UnityFail( (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_IGNORE(line, message) UnityIgnore( (message), (UNITY_LINE_TYPE)(line)) + +/*------------------------------------------------------- + * Test Asserts + *-------------------------------------------------------*/ + +#define UNITY_TEST_ASSERT(condition, line, message) if (condition) {} else {UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), (message));} +#define UNITY_TEST_ASSERT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) == NULL), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_NOT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) != NULL), (UNITY_LINE_TYPE)(line), (message)) + +#define UNITY_TEST_ASSERT_EQUAL_INT(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_EQUAL_INT8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_EQUAL_INT16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_EQUAL_INT32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_EQUAL_UINT(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_EQUAL_UINT8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_EQUAL_UINT16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_EQUAL_UINT32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_EQUAL_HEX8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_EQUAL_HEX16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_EQUAL_HEX32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_BITS(mask, expected, actual, line, message) UnityAssertBits((UNITY_INT)(mask), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line)) + +#define UNITY_TEST_ASSERT_GREATER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) + +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) + +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) + +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) + +#define UNITY_TEST_ASSERT_INT_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_INT8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_INT16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_INT32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_UINT_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_UINT8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_UINT16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_UINT32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_HEX8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_HEX16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_HEX32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) + +#define UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, line, message) UnityAssertEqualNumber((UNITY_PTR_TO_INT)(expected), (UNITY_PTR_TO_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER) +#define UNITY_TEST_ASSERT_EQUAL_STRING(expected, actual, line, message) UnityAssertEqualString((const char*)(expected), (const char*)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len, line, message) UnityAssertEqualStringLen((const char*)(expected), (const char*)(actual), (UNITY_UINT32)(len), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_MEMORY(expected, actual, len, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), 1, (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) + +#define UNITY_TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) + +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT) expected, sizeof(int)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )expected, 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )expected, 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )expected, 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT) expected, sizeof(unsigned int)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT8 )expected, 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT16)expected, 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT32)expected, 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )expected, 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )expected, 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )expected, 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_PTR_TO_INT) expected, sizeof(int*)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements, line, message) UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) + +#ifdef UNITY_SUPPORT_64 +#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)expected, 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT64)expected, 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)expected, 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#else +#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#endif + +#ifdef UNITY_EXCLUDE_FLOAT +#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#else +#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UnityAssertFloatsWithin((UNITY_FLOAT)(delta), (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((UNITY_FLOAT)(expected) * (UNITY_FLOAT)UNITY_FLOAT_PRECISION, (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualFloatArray((UNITY_FLOAT*)(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message) UnityAssertEqualFloatArray(UnityFloatToPtr(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN) +#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET) +#endif + +#ifdef UNITY_EXCLUDE_DOUBLE +#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#else +#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UnityAssertDoublesWithin((UNITY_DOUBLE)(delta), (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)line) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((UNITY_DOUBLE)(expected) * (UNITY_DOUBLE)UNITY_DOUBLE_PRECISION, (UNITY_DOUBLE)expected, (UNITY_DOUBLE)actual, (UNITY_LINE_TYPE)(line), message) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualDoubleArray((UNITY_DOUBLE*)(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)line, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message) UnityAssertEqualDoubleArray(UnityDoubleToPtr(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)line, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN) +#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET) +#endif + +/* End of UNITY_INTERNALS_H */ +#endif diff --git a/components/spotify/cspot/bell/cJSON/tests/unity_setup.c b/components/spotify/cspot/bell/cJSON/tests/unity_setup.c new file mode 100644 index 00000000..1d828b0b --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/tests/unity_setup.c @@ -0,0 +1,3 @@ +// msvc doesn't support weak-linking, so we need to define these functions. +void setUp(void) { } +void tearDown(void) { } diff --git a/components/spotify/cspot/bell/cJSON/valgrind.supp b/components/spotify/cspot/bell/cJSON/valgrind.supp new file mode 100644 index 00000000..79b55c67 --- /dev/null +++ b/components/spotify/cspot/bell/cJSON/valgrind.supp @@ -0,0 +1,6 @@ +{ + suppress_ld_on_armv7 + Memcheck:Cond + ... + obj:*/ld-*.so +} diff --git a/components/spotify/cspot/bell/include/BaseHTTPServer.h b/components/spotify/cspot/bell/include/BaseHTTPServer.h new file mode 100644 index 00000000..6189a217 --- /dev/null +++ b/components/spotify/cspot/bell/include/BaseHTTPServer.h @@ -0,0 +1,102 @@ +#ifndef BELL_BASE_HTTP_SERV +#define BELL_BASE_HTTP_SERV + +#include +#include +#include +#include +#include + +namespace bell { + +class ResponseReader { + public: + ResponseReader(){}; + virtual ~ResponseReader() = default; + + virtual size_t getTotalSize() = 0; + virtual size_t read(char *buffer, size_t size) = 0; +}; + +class FileResponseReader : public ResponseReader { + public: + FILE *file; + size_t fileSize; + FileResponseReader(std::string fileName) { + file = fopen(fileName.c_str(), "r"); + fseek(file, 0, SEEK_END); // seek to end of file + fileSize = ftell(file); // get current file pointer + fseek(file, 0, SEEK_SET); // seek back to beginning of file + }; + ~FileResponseReader() { fclose(file); }; + + size_t read(char *buffer, size_t size) { + return fread(buffer, 1, size, file); + } + + size_t getTotalSize() { return fileSize; } +}; + +enum class RequestType { GET, POST }; + +struct HTTPRequest { + std::map urlParams; + std::map queryParams; + std::string body; + int handlerId; + int connection; +}; + +struct HTTPResponse { + int connectionFd; + int status; + bool useGzip = false; + std::string body; + std::string contentType; + std::unique_ptr responseReader; +}; + +typedef std::function httpHandler; +struct HTTPRoute { + RequestType requestType; + httpHandler handler; +}; + +struct HTTPConnection { + std::vector buffer; + std::string currentLine = ""; + int contentLength = 0; + bool isReadingBody = false; + std::string httpMethod; + bool toBeClosed = false; + bool isEventConnection = false; +}; + +class BaseHTTPServer { +public: + BaseHTTPServer() {}; + virtual ~BaseHTTPServer() = default; + + /** + * Should contain server's bind port + */ + int serverPort; + + /** + * Called when handler is being registered on the http server + * + * @param requestType GET or POST + * @param endpoint registering under + * httpHandler lambda to be called when given endpoint gets executed + */ + virtual void registerHandler(RequestType requestType, const std::string & endpoint, + httpHandler) = 0; + + /** + * Writes given response to a fd + */ + virtual void respond(const HTTPResponse &) = 0; +}; +} // namespace bell + +#endif diff --git a/components/spotify/cspot/bell/include/BellLogger.h b/components/spotify/cspot/bell/include/BellLogger.h new file mode 100644 index 00000000..341a1895 --- /dev/null +++ b/components/spotify/cspot/bell/include/BellLogger.h @@ -0,0 +1,117 @@ +#ifndef BELL_LOGGER_H +#define BELL_LOGGER_H + +#include +#include +#include +#include + +namespace bell +{ + + class AbstractLogger + { + public: + bool enableSubmodule = false; + virtual void debug(std::string filename, int line, std::string submodule, const char *format, ...) = 0; + virtual void error(std::string filename, int line, std::string submodule, const char *format, ...) = 0; + virtual void info(std::string filename, int line, std::string submodule, const char *format, ...) = 0; + }; + + extern std::shared_ptr bellGlobalLogger; + class BellLogger : public bell::AbstractLogger + { + public: + // static bool enableColors = true; + void debug(std::string filename, int line, std::string submodule, const char *format, ...) + { + + printf(colorRed); + printf("D "); + if (enableSubmodule) { + printf(colorReset); + printf("[%s] ", submodule.c_str()); + } + printFilename(filename); + printf(":%d: ", line); + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); + printf("\n"); + }; + + void error(std::string filename, int line, std::string submodule, const char *format, ...) + { + + printf(colorRed); + printf("E "); + if (enableSubmodule) { + printf(colorReset); + printf("[%s] ", submodule.c_str()); + } + printFilename(filename); + printf(":%d: ", line); + printf(colorRed); + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); + printf("\n"); + }; + + void info(std::string filename, int line, std::string submodule, const char *format, ...) + { + + printf(colorBlue); + printf("I "); + if (enableSubmodule) { + printf(colorReset); + printf("[%s] ", submodule.c_str()); + } + printFilename(filename); + printf(":%d: ", line); + printf(colorReset); + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); + printf("\n"); + }; + + void printFilename(std::string filename) + { + std::string basenameStr(filename.substr(filename.rfind("/") + 1)); + unsigned long hash = 5381; + int c; + + for (char const &c : basenameStr) + { + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + } + + printf("\e[0;%dm", allColors[hash % NColors]); + + printf("%s", basenameStr.c_str()); + printf(colorReset); + } + + private: + static constexpr const char *colorReset = "\e[0m"; + static constexpr const char *colorRed = "\e[0;31m"; + static constexpr const char *colorBlue = "\e[0;34m"; + static constexpr const int NColors = 15; + static constexpr int allColors[NColors] = {31, 32, 33, 34, 35, 36, 37, 90, 91, 92, 93, 94, 95, 96, 97}; + }; + + void setDefaultLogger(); + void enableSubmoduleLogging(); +} + +#define BELL_LOG(type, ...) \ + do \ + { \ + bell::bellGlobalLogger->type(__FILE__, __LINE__, __VA_ARGS__); \ + } while (0) + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/BellSocket.h b/components/spotify/cspot/bell/include/BellSocket.h new file mode 100644 index 00000000..86b38a44 --- /dev/null +++ b/components/spotify/cspot/bell/include/BellSocket.h @@ -0,0 +1,19 @@ +#ifndef BELL_SOCKET_H +#define BELL_SOCKET_H + +#include + +namespace bell { + class Socket { + public: + Socket() {}; + virtual ~Socket() = default; + + virtual void open(std::string url) = 0; + virtual size_t write(uint8_t* buf, size_t len) = 0; + virtual size_t read(uint8_t* buf, size_t len) = 0; + virtual void close() = 0; + }; +} + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/BellUtils.h b/components/spotify/cspot/bell/include/BellUtils.h new file mode 100644 index 00000000..060e4f7b --- /dev/null +++ b/components/spotify/cspot/bell/include/BellUtils.h @@ -0,0 +1,27 @@ +#ifndef EUPHONIUM_BELL_UTILS +#define EUPHONIUM_BELL_UTILS + +#include +#include +#include + +namespace bell { + +std::string generateRandomUUID(); + +} // namespace bell + +#ifdef ESP_PLATFORM +#include + +#define BELL_SLEEP_MS(ms) vTaskDelay(ms / portTICK_PERIOD_MS) +#define BELL_YIELD() vTaskYield() + +#else +#include + +#define BELL_SLEEP_MS(ms) usleep(ms * 1000) +#define BELL_YIELD() () + +#endif +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/BinaryReader.h b/components/spotify/cspot/bell/include/BinaryReader.h new file mode 100644 index 00000000..8993b6bd --- /dev/null +++ b/components/spotify/cspot/bell/include/BinaryReader.h @@ -0,0 +1,34 @@ +#ifndef BELL_BINARY_READER_H +#define BELL_BINARY_READER_H + +#include +#include +#include +#include +#include +#include +#include "ByteStream.h" + +namespace bell +{ + class BinaryReader + { + std::shared_ptr stream; + size_t currentPos = 0; + + public: + BinaryReader(std::shared_ptr stream); + int32_t readInt(); + int16_t readShort(); + uint32_t readUInt(); + long long readLong(); + void close(); + uint8_t readByte(); + size_t size(); + size_t position(); + std::vector readBytes(size_t); + void skip(size_t); + }; +} + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/BiquadFilter.h b/components/spotify/cspot/bell/include/BiquadFilter.h new file mode 100644 index 00000000..bbf041d2 --- /dev/null +++ b/components/spotify/cspot/bell/include/BiquadFilter.h @@ -0,0 +1,128 @@ +#ifndef BELL_BIQUADFILTER_H +#define BELL_BIQUADFILTER_H + +#include +#include +#include "esp_platform.h" + +extern "C" int dsps_biquad_f32_ae32(const float* input, float* output, int len, float* coef, float* w); + +class BiquadFilter +{ +private: + std::mutex processMutex; + float coeffs[5]; + float w[2]; + +public: + BiquadFilter(){}; + + void generateHighShelfCoEffs(float f, float gain, float q) + { + if (q <= 0.0001) + { + q = 0.0001; + } + float Fs = 1; + + float A = sqrtf(pow(10, (double)gain / 20.0)); + float w0 = 2 * M_PI * f / Fs; + float c = cosf(w0); + float s = sinf(w0); + float alpha = s / (2 * q); + + float b0 = A * ((A + 1) + (A - 1) * c + 2 * sqrtf(A) * alpha); + float b1 = -2 * A * ((A - 1) + (A + 1) * c); + float b2 = A * ((A + 1) + (A - 1) * c - 2 * sqrtf(A) * alpha); + float a0 = (A + 1) - (A - 1) * c + 2 * sqrtf(A) * alpha; + float a1 = 2 * ((A - 1) - (A + 1) * c); + float a2 = (A + 1) - (A - 1) * c - 2 * sqrtf(A) * alpha; + + std::lock_guard lock(processMutex); + coeffs[0] = b0 / a0; + coeffs[1] = b1 / a0; + coeffs[2] = b2 / a0; + coeffs[3] = a1 / a0; + coeffs[4] = a2 / a0; + } + + // Generates coefficients for a low shelf biquad filter + void generateLowShelfCoEffs(float f, float gain, float q) + { + if (q <= 0.0001) + { + q = 0.0001; + } + float Fs = 1; + + float A = sqrtf(pow(10, (double)gain / 20.0)); + float w0 = 2 * M_PI * f / Fs; + float c = cosf(w0); + float s = sinf(w0); + float alpha = s / (2 * q); + + float b0 = A * ((A + 1) - (A - 1) * c + 2 * sqrtf(A) * alpha); + float b1 = 2 * A * ((A - 1) - (A + 1) * c); + float b2 = A * ((A + 1) - (A - 1) * c - 2 * sqrtf(A) * alpha); + float a0 = (A + 1) + (A - 1) * c + 2 * sqrtf(A) * alpha; + float a1 = -2 * ((A - 1) + (A + 1) * c); + float a2 = (A + 1) + (A - 1) * c - 2 * sqrtf(A) * alpha; + + std::lock_guard lock(processMutex); + coeffs[0] = b0 / a0; + coeffs[1] = b1 / a0; + coeffs[2] = b2 / a0; + coeffs[3] = a1 / a0; + coeffs[4] = a2 / a0; + } + + // Generates coefficients for a notch biquad filter + void generateNotchCoEffs(float f, float gain, float q) + { + if (q <= 0.0001) + { + q = 0.0001; + } + float Fs = 1; + + float A = sqrtf(pow(10, (double)gain / 20.0)); + float w0 = 2 * M_PI * f / Fs; + float c = cosf(w0); + float s = sinf(w0); + float alpha = s / (2 * q); + + float b0 = 1 + alpha * A; + float b1 = -2 * c; + float b2 = 1 - alpha * A; + float a0 = 1 + alpha; + float a1 = -2 * c; + float a2 = 1 - alpha; + + std::scoped_lock lock(processMutex); + coeffs[0] = b0 / a0; + coeffs[1] = b1 / a0; + coeffs[2] = b2 / a0; + coeffs[3] = a1 / a0; + coeffs[4] = a2 / a0; + } + + void processSamples(float *input, int numSamples) + { + std::scoped_lock lock(processMutex); + +#ifdef ESP_PLATFORM + dsps_biquad_f32_ae32(input, input, numSamples, coeffs, w); +#else + // Apply the set coefficients + for (int i = 0; i < numSamples; i++) + { + float d0 = input[i] - coeffs[3] * w[0] - coeffs[4] * w[1]; + input[i] = coeffs[0] * d0 + coeffs[1] * w[0] + coeffs[2] * w[1]; + w[1] = w[0]; + w[0] = d0; + } +#endif + } +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/ByteStream.h b/components/spotify/cspot/bell/include/ByteStream.h new file mode 100644 index 00000000..9e242a98 --- /dev/null +++ b/components/spotify/cspot/bell/include/ByteStream.h @@ -0,0 +1,26 @@ +#ifndef BELL_BYTE_READER_H +#define BELL_BYTE_READER_H + +#include "stdlib.h" + +/** + * A class for reading bytes from a stream. Further implemented in HTTPStream.h + */ +namespace bell +{ + class ByteStream + { + public: + ByteStream(){}; + ~ByteStream(){}; + + virtual size_t read(uint8_t *buf, size_t nbytes) = 0; + virtual size_t skip(size_t nbytes) = 0; + + virtual size_t position() = 0; + virtual size_t size() = 0; + virtual void close() = 0; + }; +} + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/Crypto.h b/components/spotify/cspot/bell/include/Crypto.h new file mode 100644 index 00000000..b6188bb9 --- /dev/null +++ b/components/spotify/cspot/bell/include/Crypto.h @@ -0,0 +1,14 @@ +#ifndef BELL_CRYPTO_H +#define BELL_CRYPTO_H + +#include +#include + +#ifdef BELL_USE_MBEDTLS +#include "CryptoMbedTLS.h" +#define Crypto CryptoMbedTLS +#else +#include "CryptoOpenSSL.h" +#define Crypto CryptoOpenSSL +#endif +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/CryptoMbedTLS.h b/components/spotify/cspot/bell/include/CryptoMbedTLS.h new file mode 100644 index 00000000..089a3bad --- /dev/null +++ b/components/spotify/cspot/bell/include/CryptoMbedTLS.h @@ -0,0 +1,78 @@ +#ifndef BELL_CRYPTOMBEDTLS_H +#define BELL_CRYPTOMBEDTLS_H + +#ifdef BELL_USE_MBEDTLS +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +#define DH_KEY_SIZE 96 + +static unsigned char DHPrime[] = { + /* Well-known Group 1, 768-bit prime */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, + 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6, + 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, + 0x08, 0x8a, 0x67, 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6, + 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, + 0x34, 0x04, 0xdd, 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, + 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, + 0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, + 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, 0xf4, + 0x4c, 0x42, 0xe9, 0xa6, 0x3a, 0x36, 0x20, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +static unsigned char DHGenerator[1] = {2}; + +class CryptoMbedTLS { +private: + mbedtls_md_context_t sha1Context; + mbedtls_aes_context aesCtx; +public: + CryptoMbedTLS(); + ~CryptoMbedTLS(); + // Base64 + std::vector base64Decode(const std::string& data); + std::string base64Encode(const std::vector& data); + + // Sha1 + void sha1Init(); + void sha1Update(const std::string& s); + void sha1Update(const std::vector& vec); + std::string sha1Final(); + std::vector sha1FinalBytes(); + + // HMAC SHA1 + 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); + + // AES ECB + void aesECBdecrypt(const std::vector& key, std::vector& data); + + // Diffie Hellman + std::vector publicKey; + std::vector privateKey; + void dhInit(); + std::vector dhCalculateShared(const std::vector& remoteKey); + + // PBKDF2 + std::vector pbkdf2HmacSha1(const std::vector& password, const std::vector& salt, int iterations, int digestSize); + + // Random stuff + std::vector generateVectorWithRandomData(size_t length); +}; + +#endif +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/CryptoOpenSSL.h b/components/spotify/cspot/bell/include/CryptoOpenSSL.h new file mode 100644 index 00000000..abc8d01c --- /dev/null +++ b/components/spotify/cspot/bell/include/CryptoOpenSSL.h @@ -0,0 +1,84 @@ +#ifndef BELL_CRYPTOOPENSSL_H +#define BELL_CRYPTOOPENSSL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DH_KEY_SIZE 96 + +static unsigned char DHPrime[] = { + /* Well-known Group 1, 768-bit prime */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, + 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6, + 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, + 0x08, 0x8a, 0x67, 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6, + 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, + 0x34, 0x04, 0xdd, 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, + 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, + 0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, + 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, 0xf4, + 0x4c, 0x42, 0xe9, 0xa6, 0x3a, 0x36, 0x20, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +static unsigned char DHGenerator[1] = {2}; + +class CryptoOpenSSL { +private: + DH* dhContext = nullptr; + SHA_CTX sha1Context; +public: + CryptoOpenSSL(); + ~CryptoOpenSSL(); + // Base64 + std::vector base64Decode(const std::string& data); + std::string base64Encode(const std::vector& data); + + // Sha1 + void sha1Init(); + void sha1Update(const std::string& s); + void sha1Update(const std::vector& vec); + + void connectSSL(std::string url); + int readSSL(uint8_t* buf, int len); + int writeSSL(uint8_t* buf, int len); + void closeSSL(); + + std::string sha1Final(); + std::vector sha1FinalBytes(); + + // HMAC SHA1 + 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); + + // AES ECB + void aesECBdecrypt(const std::vector& key, std::vector& data); + + // Diffie Hellman + std::vector publicKey; + std::vector privateKey; + void dhInit(); + std::vector dhCalculateShared(const std::vector& remoteKey); + + // PBKDF2 + std::vector pbkdf2HmacSha1(const std::vector& password, const std::vector& salt, int iterations, int digestSize); + + // Random stuff + std::vector generateVectorWithRandomData(size_t length); +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/DecoderGlobals.h b/components/spotify/cspot/bell/include/DecoderGlobals.h new file mode 100644 index 00000000..47bcf0bb --- /dev/null +++ b/components/spotify/cspot/bell/include/DecoderGlobals.h @@ -0,0 +1,52 @@ +#ifndef BELL_DISABLE_CODECS +#ifndef DECODER_GLOBALS_H +#define DECODER_GLOBALS_H + +#define AAC_READBUF_SIZE (4 * AAC_MAINBUF_SIZE * AAC_MAX_NCHANS) +#define MP3_READBUF_SIZE (2 * 1024); + +#include +#include +#include +#include "aacdec.h" +#include "mp3dec.h" + +namespace bell +{ + class DecodersInstance + { + public: + DecodersInstance(){}; + ~DecodersInstance() + { + MP3FreeDecoder(mp3Decoder); + AACFreeDecoder(aacDecoder); + }; + + HAACDecoder aacDecoder = NULL; + HMP3Decoder mp3Decoder = NULL; + + void ensureAAC() + { + if (aacDecoder == NULL) + { + aacDecoder = AACInitDecoder(); + } + } + + void ensureMP3() + { + if (mp3Decoder == NULL) + { + mp3Decoder = MP3InitDecoder(); + } + } + }; + + extern std::shared_ptr decodersInstance; + + void createDecoders(); +} + +#endif +#endif diff --git a/components/spotify/cspot/bell/include/HTTPServer.h b/components/spotify/cspot/bell/include/HTTPServer.h new file mode 100644 index 00000000..9285d372 --- /dev/null +++ b/components/spotify/cspot/bell/include/HTTPServer.h @@ -0,0 +1,70 @@ +#ifndef BELL_HTTP_SERVER_H +#define BELL_HTTP_SERVER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "BaseHTTPServer.h" + +#ifndef SOCK_NONBLOCK +#define SOCK_NONBLOCK O_NONBLOCK +#endif + +namespace bell +{ + class HTTPServer : public bell::BaseHTTPServer + { + private: + std::regex routerPattern = std::regex(":([^\\/]+)?"); + fd_set master; + fd_set readFds; + fd_set activeFdSet, readFdSet; + + bool isClosed = true; + bool writingResponse = false; + + std::map> routes; + std::map connections; + void writeResponse(const HTTPResponse &); + void writeResponseEvents(int connFd); + void findAndHandleRoute(std::string &, std::string &, int connectionFd); + + std::vector splitUrl(const std::string &url, char delimiter); + std::mutex responseMutex; + std::vector responseBuffer = std::vector(128); + + void readFromClient(int clientFd); + std::map parseQueryString(const std::string &queryString); + unsigned char h2int(char c); + std::string urlDecode(std::string str); + + public: + HTTPServer(int serverPort); + + void registerHandler(RequestType requestType, const std::string &, httpHandler); + void respond(const HTTPResponse &); + void publishEvent(std::string eventName, std::string eventData); + void closeConnection(int connection); + void listen(); + }; +} +#endif diff --git a/components/spotify/cspot/bell/include/HTTPStream.h b/components/spotify/cspot/bell/include/HTTPStream.h new file mode 100644 index 00000000..33a4f049 --- /dev/null +++ b/components/spotify/cspot/bell/include/HTTPStream.h @@ -0,0 +1,72 @@ +#ifndef BELL_HTTP_STREAM_H +#define BELL_HTTP_STREAM_H + +#include +#include +#include +#include +#include +#include + +/* +* HTTPStream +* +* A class for reading and writing HTTP streams implementing the ByteStream interface. +* +*/ +namespace bell +{ + enum class StreamStatus + { + OPENING, + READING_HEADERS, + READING_DATA, + CLOSED + }; + + class HTTPStream : public ByteStream + { + public: + HTTPStream(); + ~HTTPStream(); + + std::unique_ptr socket; + + + bool hasFixedSize = false; + size_t contentLength = -1; + size_t currentPos = -1; + + StreamStatus status = StreamStatus::OPENING; + + /* + * opens connection to given url and reads header + * + * @param url the http url to connect to + */ + void connectToUrl(std::string url, bool disableSSL = false); + + /* + * Reads data from the stream. + * + * @param buf The buffer to read data into. + * @param nbytes The size of the buffer. + * @return The number of bytes read. + * @throws std::runtime_error if the stream is closed. + */ + size_t read(uint8_t *buf, size_t nbytes); + + /* + * Skips nbytes bytes in the stream. + */ + size_t skip(size_t nbytes); + + size_t position() { return currentPos; } + + size_t size() { return contentLength; } + + // Closes the connection + void close(); + }; +} +#endif diff --git a/components/spotify/cspot/bell/include/JSONObject.h b/components/spotify/cspot/bell/include/JSONObject.h new file mode 100644 index 00000000..8cecacd9 --- /dev/null +++ b/components/spotify/cspot/bell/include/JSONObject.h @@ -0,0 +1,33 @@ +#ifndef JSONOBJECT_H +#define JSONOBJECT_H +#include +#include + +namespace bell { + class JSONValue + { + public: + JSONValue(cJSON* body, std::string key); + void operator=(const std::string val); + void operator=(const char* val); + void operator=(int val); + + private: + cJSON* body; + std::string key; + }; + + class JSONObject + { + public: + JSONObject(); + ~JSONObject(); + JSONValue operator[](std::string index); + std::string toString(); + + private: + cJSON* body; + }; +} + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/MpegDashDemuxer.h b/components/spotify/cspot/bell/include/MpegDashDemuxer.h new file mode 100644 index 00000000..be0f4913 --- /dev/null +++ b/components/spotify/cspot/bell/include/MpegDashDemuxer.h @@ -0,0 +1,55 @@ +#include "BinaryReader.h" +#include "ByteStream.h" +#include "MpegDashTypes.h" +#include + +namespace bell::mpeg +{ + class MpegDashDemuxer + { + std::shared_ptr reader; + std::unique_ptr lastBox; + std::unique_ptr lastMoof; + std::vector tracks; + bool chunkZero = false; + + public: + MpegDashDemuxer(std::shared_ptr stream); + ~MpegDashDemuxer() { + reader->close(); + } + void parse(); + std::unique_ptr readBox(); + // std::vector getTracks(); + std::vector readFullBox(std::unique_ptr &); + void ensureBox(std::unique_ptr &); + size_t position(); + void close(); + std::unique_ptr parseMvhd(); + std::unique_ptr getNextChunk(bool infoOnly); + std::unique_ptr getTrunEntry(std::unique_ptr &, int); + std::unique_ptr getAbsoluteTrunEntry(std::unique_ptr &, int, std::unique_ptr &); + std::vector parseMvex(std::unique_ptr &, int possibleTrackCount); + std::unique_ptr getNextSample(std::unique_ptr &); + bool hasFlag(int flags, int mask); + std::unique_ptr parseHdlr(std::unique_ptr &); + std::unique_ptr parseMoov(std::unique_ptr &); + int parseMfhd(); + std::unique_ptr parseTraf(std::unique_ptr &, int); + std::unique_ptr parseMoof(std::unique_ptr &, int); + std::unique_ptr parseTfhd(int); + long parseTfdt(); + std::unique_ptr parseTrun(); + std::unique_ptr parseTkhd(); + std::unique_ptr parseTrak(std::unique_ptr &); + bell::mpeg::Trex parseTrex(); + std::unique_ptr parseMdia(std::unique_ptr &); + std::unique_ptr parseMinf(std::unique_ptr &); + std::unique_ptr parseEdts(std::unique_ptr &); + std::vector parseStbl(std::unique_ptr &); + std::unique_ptr untilBox(std::unique_ptr &, int, int, int); + std::unique_ptr untilBox(std::unique_ptr &, int, int); + std::unique_ptr untilBox(std::unique_ptr &, int); + std::unique_ptr untilAnyBox(std::unique_ptr &); + }; +} \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/MpegDashTypes.h b/components/spotify/cspot/bell/include/MpegDashTypes.h new file mode 100644 index 00000000..19b684b0 --- /dev/null +++ b/components/spotify/cspot/bell/include/MpegDashTypes.h @@ -0,0 +1,179 @@ +#ifndef BELL_MPEG_DASH_TYPES_H +#define BELL_MPEG_DASH_TYPES_H + +#include +#include + +namespace bell::mpeg +{ + + struct Tkhd + { + int trackId; + long duration; + short bVolume; + int bWidth; + int bHeight; + std::vector matrix; + short bLayer; + short bAlternateGroup; + }; + + struct Elst + { + long mediaTime; + int bMediaRate; + }; + + struct Trex + { + int trackId; + int defaultSampleDescriptionIndex; + int defaultSampleDuration; + int defaultSampleSize; + int defaultSampleFlags; + }; + + struct Mvhd + { + long timeScale; + long nextTrackId; + }; + + struct Hdlr + { + int type; + int subType; + std::vector bReserved; + }; + + struct Minf + { + std::vector dinf; + std::vector stblStsd; + std::vector mhd; + }; + + struct Tfhd + { + int bFlags; + int trackId; + int defaultSampleDuration; + int defaultSampleSize; + int defaultSampleFlags; + }; + + struct TrunEntry + { + int sampleDuration; + int sampleSize; + int sampleFlags; + int sampleCompositionTimeOffset; + bool hasCompositionTimeOffset; + bool isKeyframe; + }; + + struct Trun + { + int chunkDuration; + int chunkSize; + int bFlags; + int bFirstSampleFlags; + int dataOffset; + int entryCount; + std::vector bEntries; + int entriesRowSize; + }; + + struct Traf + { + std::unique_ptr tfhd; + long tfdt = -1; + std::unique_ptr trun; + }; + + struct Moof + { + int mfgdSequenceNumber; + std::unique_ptr traf; + }; + struct Chunk + { + std::vector data = std::vector(); + std::unique_ptr moof; + int i = 0; + int size = 0; + int sampleRead = 0; + }; + + struct Mp4DashSample + { + std::unique_ptr info; + std::vector data = std::vector(); + }; + + struct Mdia + { + int mhdTimeScale; + std::vector mdhd; + std::unique_ptr hdlr; + std::unique_ptr minf; + }; + + struct Trak + { + std::unique_ptr tkhd; + std::unique_ptr edstElst; + std::unique_ptr mdia; + }; + + struct Mp4Track + { + std::unique_ptr trak; + }; + + struct Moov + { + std::unique_ptr mvhd; + std::vector> trak; + std::vector mvexTrex; + }; + + struct MpegBox + { + long size = 0; + int32_t type = 0; + long offset; + }; + +} + +#define ATOM_MOOF 0x6D6F6F66 +#define ATOM_MFHD 0x6D666864 +#define ATOM_TRAF 0x74726166 +#define ATOM_TFHD 0x74666864 +#define ATOM_TFDT 0x74666474 +#define ATOM_TRUN 0x7472756E +#define ATOM_MDIA 0x6D646961 +#define ATOM_FTYP 0x66747970 +#define ATOM_SIDX 0x73696478 +#define ATOM_MOOV 0x6D6F6F76 +#define ATOM_MDAT 0x6D646174 +#define ATOM_MVHD 0x6D766864 +#define ATOM_TRAK 0x7472616B +#define ATOM_MVEX 0x6D766578 +#define ATOM_TREX 0x74726578 +#define ATOM_TKHD 0x746B6864 +#define ATOM_MFRA 0x6D667261 +#define ATOM_MDHD 0x6D646864 +#define ATOM_EDTS 0x65647473 +#define ATOM_ELST 0x656C7374 +#define ATOM_HDLR 0x68646C72 +#define ATOM_MINF 0x6D696E66 +#define ATOM_DINF 0x64696E66 +#define ATOM_STBL 0x7374626C +#define ATOM_STSD 0x73747364 +#define ATOM_VMHD 0x766D6864 +#define ATOM_SMHD 0x736D6864 + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/Queue.h b/components/spotify/cspot/bell/include/Queue.h new file mode 100644 index 00000000..c9ebc573 --- /dev/null +++ b/components/spotify/cspot/bell/include/Queue.h @@ -0,0 +1,115 @@ +#ifndef BELL_QUEUE_H +#define BELL_QUEUE_H + +#include +#include +#include +#include + +namespace bell +{ + template + class Queue + { + private: + /// Queue + std::queue m_queue; + /// Mutex to controll multiple access + std::mutex m_mutex; + /// Conditional variable used to fire event + std::condition_variable m_cv; + /// Atomic variable used to terminate immediately wpop and wtpop functions + std::atomic m_forceExit = false; + + public: + /// Add a new element in the queue. + /// New element. + void push(dataType const &data) + { + m_forceExit.store(false); + std::unique_lock lk(m_mutex); + m_queue.push(data); + lk.unlock(); + m_cv.notify_one(); + } + /// Check queue empty. + /// True if the queue is empty. + bool isEmpty() const + { + std::unique_lock lk(m_mutex); + return m_queue.empty(); + } + /// Pop element from queue. + /// [in,out] Element. + /// false if the queue is empty. + bool pop(dataType &popped_value) + { + std::unique_lock lk(m_mutex); + if (m_queue.empty()) + { + return false; + } + else + { + popped_value = m_queue.front(); + m_queue.pop(); + return true; + } + } + /// Wait and pop an element in the queue. + /// [in,out] Element. + /// False for forced exit. + bool wpop(dataType &popped_value) + { + std::unique_lock lk(m_mutex); + m_cv.wait(lk, [&]() -> bool + { return !m_queue.empty() || m_forceExit.load(); }); + if (m_forceExit.load()) + return false; + popped_value = m_queue.front(); + m_queue.pop(); + return true; + } + /// Timed wait and pop an element in the queue. + /// [in,out] Element. + /// [in] Wait time. + /// False for timeout or forced exit. + bool wtpop(dataType &popped_value, long milliseconds = 1000) + { + std::unique_lock lk(m_mutex); + m_cv.wait_for(lk, std::chrono::milliseconds(milliseconds), [&]() -> bool + { return !m_queue.empty() || m_forceExit.load(); }); + if (m_forceExit.load()) + return false; + if (m_queue.empty()) + return false; + popped_value = m_queue.front(); + m_queue.pop(); + return true; + } + /// Queue size. + int size() + { + std::unique_lock lk(m_mutex); + return static_cast(m_queue.size()); + } + /// Free the queue and force stop. + void clear() + { + m_forceExit.store(true); + std::unique_lock lk(m_mutex); + while (!m_queue.empty()) + { + //delete m_queue.front(); + m_queue.pop(); + } + } + /// Check queue in forced exit state. + bool isExit() const + { + return m_forceExit.load(); + } + }; +} + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/TCPSocket.h b/components/spotify/cspot/bell/include/TCPSocket.h new file mode 100644 index 00000000..091670c9 --- /dev/null +++ b/components/spotify/cspot/bell/include/TCPSocket.h @@ -0,0 +1,110 @@ +#ifndef BELL_BASIC_SOCKET_H +#define BELL_BASIC_SOCKET_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bell +{ + class TCPSocket : public bell::Socket + { + private: + int sockFd; + bool isClosed = false; + + public: + TCPSocket() {}; + ~TCPSocket() { + close(); + }; + + void open(std::string url) + { + // remove https or http from url + url.erase(0, url.find("://") + 3); + + // split by first "/" in url + std::string hostUrl = url.substr(0, url.find('/')); + std::string pathUrl = url.substr(url.find('/')); + + std::string portString = "80"; + + // check if hostUrl contains ':' + if (hostUrl.find(':') != std::string::npos) + { + // split by ':' + std::string host = hostUrl.substr(0, hostUrl.find(':')); + portString = hostUrl.substr(hostUrl.find(':') + 1); + hostUrl = host; + } + + int domain = AF_INET; + int socketType = SOCK_STREAM; + + addrinfo hints, *addr; + //fine-tune hints according to which socket you want to open + hints.ai_family = domain; + hints.ai_socktype = socketType; + hints.ai_protocol = IPPROTO_IP; // no enum : possible value can be read in /etc/protocols + hints.ai_flags = AI_CANONNAME | AI_ALL | AI_ADDRCONFIG; + + BELL_LOG(info, "http", "%s %s", hostUrl.c_str(), portString.c_str()); + + if (getaddrinfo(hostUrl.c_str(), portString.c_str(), &hints, &addr) != 0) + { + BELL_LOG(error, "webradio", "DNS lookup error"); + throw std::runtime_error("Resolve failed"); + } + + sockFd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); + + if (connect(sockFd, addr->ai_addr, addr->ai_addrlen) < 0) + { + close(); + BELL_LOG(error, "http", "Could not connect to %s", url.c_str()); + throw std::runtime_error("Resolve failed"); + } + + int flag = 1; + setsockopt(sockFd, /* socket affected */ + IPPROTO_TCP, /* set option at TCP level */ + TCP_NODELAY, /* name of option */ + (char *)&flag, /* the cast is historical cruft */ + sizeof(int)); /* length of option value */ + + freeaddrinfo(addr); + } + + size_t read(uint8_t *buf, size_t len) { + return recv(sockFd, buf, len, 0); + } + + size_t write(uint8_t *buf, size_t len) { + return send(sockFd, buf, len, 0); + } + + void close() { + if (!isClosed) { + ::close(sockFd); + isClosed = true; + } + } + }; + +} + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/Task.h b/components/spotify/cspot/bell/include/Task.h new file mode 100644 index 00000000..f9f1fc63 --- /dev/null +++ b/components/spotify/cspot/bell/include/Task.h @@ -0,0 +1,118 @@ +#ifndef BELL_TASK_H +#define BELL_TASK_H + +#ifdef ESP_PLATFORM +#include +#include +#include +#include +#endif + +#include + +namespace bell +{ + class Task + { + public: + std::string taskName; + int stackSize, core; + bool isRunning = false; + bool runOnPSRAM; + Task(std::string taskName, int stackSize, int priority, int core, bool runOnPSRAM = true) + { + this->taskName = taskName; + this->stackSize = stackSize; + this->core = core; + this->runOnPSRAM = runOnPSRAM; +#ifdef ESP_PLATFORM + this->priority = ESP_TASK_PRIO_MIN + 1 + priority; + if (this->priority < 0) this->priority = ESP_TASK_PRIO_MIN + 1; +#endif + } + virtual ~Task() {} + + bool startTask() + { +#ifdef ESP_PLATFORM + if (runOnPSRAM) + { + + xTaskBuffer = (StaticTask_t *)heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + xStack = (StackType_t *)heap_caps_malloc(this->stackSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + + return (xTaskCreateStaticPinnedToCore(taskEntryFuncPSRAM, this->taskName.c_str(), this->stackSize, this, 2, xStack, xTaskBuffer, this->core) != NULL); + } + else + { + esp_pthread_cfg_t cfg = esp_pthread_get_default_config(); + cfg.stack_size = stackSize; + cfg.inherit_cfg = true; + cfg.thread_name = this->taskName.c_str(); + cfg.pin_to_core = core; + cfg.prio = priority; + esp_pthread_set_cfg(&cfg); + } +#endif + return (pthread_create(&_thread, NULL, taskEntryFunc, this) == 0); + } + void waitForTaskToReturn() + { +#ifdef ESP_PLATFORM + if (runOnPSRAM) + { + while (isRunning) + { + vTaskDelay(100 / portTICK_PERIOD_MS); + } + heap_caps_free(xStack); + // TCB are cleanup in IDLE task, so give it some time + TimerHandle_t timer = xTimerCreate( "cleanup", pdMS_TO_TICKS(5000), pdFALSE, xTaskBuffer, + [](TimerHandle_t xTimer) { + heap_caps_free(pvTimerGetTimerID(xTimer)); + xTimerDelete(xTimer, portMAX_DELAY); + } ); + xTimerStart(timer, portMAX_DELAY); + } + else + { + (void)pthread_join(_thread, NULL); + } +#else + (void)pthread_join(_thread, NULL); +#endif + } + + protected: + virtual void runTask() = 0; + + private: +#ifdef ESP_PLATFORM + int priority; + StaticTask_t *xTaskBuffer; + StackType_t *xStack; +#endif + static void *taskEntryFunc(void *This) + { + ((Task *)This)->runTask(); + return NULL; + } +#ifdef ESP_PLATFORM + static void taskEntryFuncPSRAM(void *This) + { + + ((Task *)This)->isRunning = true; + + Task *self = (Task *)This; + self->isRunning = true; + self->runTask(); + self->isRunning = false; + vTaskDelete(NULL); + } +#endif + + pthread_t _thread; + }; +} + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/esp_platform.h b/components/spotify/cspot/bell/include/esp_platform.h new file mode 100644 index 00000000..b6f38969 --- /dev/null +++ b/components/spotify/cspot/bell/include/esp_platform.h @@ -0,0 +1,4 @@ +#ifdef __XTENSA__ +#include +#include +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/platform/TLSSocket.h b/components/spotify/cspot/bell/include/platform/TLSSocket.h new file mode 100644 index 00000000..8fb51cbf --- /dev/null +++ b/components/spotify/cspot/bell/include/platform/TLSSocket.h @@ -0,0 +1,71 @@ +#ifndef BELL_TLS_SOCKET_H +#define BELL_TLS_SOCKET_H + +#include "BellLogger.h" +#include "BellSocket.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BELL_USE_MBEDTLS + +#include "mbedtls/net_sockets.h" +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/debug.h" +#else +#include +#include +#include +#endif + +namespace bell { +class TLSSocket : public bell::Socket { +private: +#ifdef BELL_USE_MBEDTLS + mbedtls_net_context server_fd; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; +#else + + BIO *sbio, *out; + int len; + char tmpbuf[1024]; + SSL_CTX *ctx; + SSL *ssl; + + int sockFd; + int sslFd; +#endif + + bool isClosed = false; +public: + TLSSocket(); + ~TLSSocket() { close(); }; + + void open(std::string host); + + size_t read(uint8_t *buf, size_t len); + size_t write(uint8_t *buf, size_t len); + + void close(); +}; + +} // namespace bell + +#endif diff --git a/components/spotify/cspot/bell/include/platform/WrappedMutex.h b/components/spotify/cspot/bell/include/platform/WrappedMutex.h new file mode 100644 index 00000000..31fd4443 --- /dev/null +++ b/components/spotify/cspot/bell/include/platform/WrappedMutex.h @@ -0,0 +1,5 @@ +#ifdef _WIN32 +#include "win32/WrappedMutex.h" +#else +#include "unixlike/WrappedMutex.h" +#endif diff --git a/components/spotify/cspot/bell/include/platform/WrappedSemaphore.h b/components/spotify/cspot/bell/include/platform/WrappedSemaphore.h new file mode 100644 index 00000000..74b41c82 --- /dev/null +++ b/components/spotify/cspot/bell/include/platform/WrappedSemaphore.h @@ -0,0 +1,32 @@ +#ifndef WRAPPEDSEMAPHORE_H +#define WRAPPEDSEMAPHORE_H + +#ifdef ESP_PLATFORM +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#elif __APPLE__ +#include +#else +#include +#endif + +class WrappedSemaphore +{ +private: +#ifdef ESP_PLATFORM + xSemaphoreHandle semaphoreHandle; +#elif __APPLE__ + dispatch_semaphore_t semaphoreHandle; +#else + sem_t semaphoreHandle; +#endif +public: + WrappedSemaphore(int maxVal = 200); + ~WrappedSemaphore(); + + int wait(); + int twait(long milliseconds = 10); + void give(); +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/platform/unixlike/WrappedMutex.h b/components/spotify/cspot/bell/include/platform/unixlike/WrappedMutex.h new file mode 100644 index 00000000..a1ad6c0d --- /dev/null +++ b/components/spotify/cspot/bell/include/platform/unixlike/WrappedMutex.h @@ -0,0 +1,36 @@ +#ifndef UNIXLIKE_WRAPPED_MUTEX_H +#define UNIXLIKE_WRAPPED_MUTEX_H +#include + +/** + * Wraps a mutex on unixlike patforms (linux, macOS, esp32 etc.). + * Header only since all the methods call one function, we want them to be inlined + **/ +class WrappedMutex +{ +public: + WrappedMutex() + { + pthread_mutex_init(&this->_mutex, NULL); + } + + ~WrappedMutex() + { + pthread_mutex_destroy(&this->_mutex); + } + + void lock() + { + pthread_mutex_lock(&this->_mutex); + } + + void unlock() + { + pthread_mutex_unlock(&this->_mutex); + } + +private: + pthread_mutex_t _mutex; +}; + +#endif diff --git a/components/spotify/cspot/bell/include/platform/win32/WrappedMutex.h b/components/spotify/cspot/bell/include/platform/win32/WrappedMutex.h new file mode 100644 index 00000000..af1be32b --- /dev/null +++ b/components/spotify/cspot/bell/include/platform/win32/WrappedMutex.h @@ -0,0 +1,39 @@ +#ifndef WIN32_WRAPPED_MUTEX_H +#define WIN32_WRAPPED_MUTEX_H +#include + +/** + * Wraps a windows mutex. + * Header only since all the methods call one function, we want them to be inlined + **/ +class WrappedMutex +{ +public: + WrappedMutex() + { + this->_mutex = CreateMutex( + NULL, // default security attributes + FALSE, // initially not owned + NULL); // unnamed mutex + } + + ~WrappedMutex() + { + CloseHandle(_mutex); + } + + void lock() + { + WaitForSingleObject(_mutex, INFINITE); + } + + void unlock() + { + ReleaseMutex(_mutex); + } + +private: + HANDLE _mutex; +}; + +#endif diff --git a/components/spotify/cspot/bell/libhelix-aac/aaccommon.h b/components/spotify/cspot/bell/libhelix-aac/aaccommon.h new file mode 100644 index 00000000..9d862fab --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/aaccommon.h @@ -0,0 +1,213 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: aaccommon.h,v 1.1 2005/02/26 01:47:34 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * aaccommon.h - implementation-independent API's, datatypes, and definitions + **************************************************************************************/ + +#ifndef _AACCOMMON_H +#define _AACCOMMON_H + +#ifdef ESP8266 +# include "pgmspace.h" +#elif defined(ESP_PLATFORM) && __has_include() +# include +#else +# define PROGMEM +# define pgm_read_byte(addr) (*(const unsigned char *)(addr)) +# define pgm_read_word(addr) (*(const unsigned short *)(addr)) +#endif +// Can't fit in ESP8266 RAM +#ifndef ESP8266 + #define AAC_ENABLE_SBR 1 +#endif + +#pragma GCC optimize ("O3") + +#include "aacdec.h" +#include "statname.h" + +/* 12-bit syncword */ +#define SYNCWORDH 0xff +#define SYNCWORDL 0xf0 + +#define MAX_NCHANS_ELEM 2 /* max number of channels in any single bitstream element (SCE,CPE,CCE,LFE) */ + +#define ADTS_HEADER_BYTES 7 +#define NUM_SAMPLE_RATES 12 +#define NUM_DEF_CHAN_MAPS 8 +#define NUM_ELEMENTS 8 +#define MAX_NUM_PCE_ADIF 16 + +#define MAX_WIN_GROUPS 8 +#define MAX_SFB_SHORT 15 +#define MAX_SF_BANDS (MAX_SFB_SHORT*MAX_WIN_GROUPS) /* worst case = 15 sfb's * 8 windows for short block */ +#define MAX_MS_MASK_BYTES ((MAX_SF_BANDS + 7) >> 3) +#define MAX_PRED_SFB 41 +#define MAX_TNS_FILTERS 8 +#define MAX_TNS_COEFS 60 +#define MAX_TNS_ORDER 20 +#define MAX_PULSES 4 +#define MAX_GAIN_BANDS 3 +#define MAX_GAIN_WIN 8 +#define MAX_GAIN_ADJUST 7 + +#define NSAMPS_LONG 1024 +#define NSAMPS_SHORT 128 + +#define NUM_SYN_ID_BITS 3 +#define NUM_INST_TAG_BITS 4 + +#define EXT_SBR_DATA 0x0d +#define EXT_SBR_DATA_CRC 0x0e + +#define IS_ADIF(p) ((p)[0] == 'A' && (p)[1] == 'D' && (p)[2] == 'I' && (p)[3] == 'F') +#define GET_ELE_ID(p) ((AACElementID)(*(p) >> (8-NUM_SYN_ID_BITS))) + +/* AAC file format */ +enum { + AAC_FF_Unknown = 0, /* should be 0 on init */ + + AAC_FF_ADTS = 1, + AAC_FF_ADIF = 2, + AAC_FF_RAW = 3 + +}; + +/* syntactic element type */ +enum { + AAC_ID_INVALID = -1, + + AAC_ID_SCE = 0, + AAC_ID_CPE = 1, + AAC_ID_CCE = 2, + AAC_ID_LFE = 3, + AAC_ID_DSE = 4, + AAC_ID_PCE = 5, + AAC_ID_FIL = 6, + AAC_ID_END = 7 +}; + +typedef struct _AACDecInfo { + /* pointers to platform-specific state information */ + void *psInfoBase; /* baseline MPEG-4 LC decoding */ + void *psInfoSBR; /* MPEG-4 SBR decoding */ + + /* raw decoded data, before rounding to 16-bit PCM (for postprocessing such as SBR) */ + void *rawSampleBuf[AAC_MAX_NCHANS]; + int rawSampleBytes; + int rawSampleFBits; + + /* fill data (can be used for processing SBR or other extensions) */ + unsigned char *fillBuf; + int fillCount; + int fillExtType; + + /* block information */ + int prevBlockID; + int currBlockID; + int currInstTag; + int sbDeinterleaveReqd[MAX_NCHANS_ELEM]; + int adtsBlocksLeft; + + /* user-accessible info */ + int bitRate; + int nChans; + int sampRate; + int profile; + int format; + int sbrEnabled; + int tnsUsed; + int pnsUsed; + int frameCount; + +} AACDecInfo; + +/* decoder functions which must be implemented for each platform */ +AACDecInfo *AllocateBuffers(void); +AACDecInfo *AllocateBuffersPre(void **space, int *len); +void FreeBuffers(AACDecInfo *aacDecInfo); +void ClearBuffer(void *buf, int nBytes); + +int UnpackADTSHeader(AACDecInfo *aacDecInfo, unsigned char **buf, int *bitOffset, int *bitsAvail); +int GetADTSChannelMapping(AACDecInfo *aacDecInfo, unsigned char *buf, int bitOffset, int bitsAvail); +int UnpackADIFHeader(AACDecInfo *aacDecInfo, unsigned char **buf, int *bitOffset, int *bitsAvail); +int SetRawBlockParams(AACDecInfo *aacDecInfo, int copyLast, int nChans, int sampRate, int profile); +int PrepareRawBlock(AACDecInfo *aacDecInfo); +int FlushCodec(AACDecInfo *aacDecInfo); + +int DecodeNextElement(AACDecInfo *aacDecInfo, unsigned char **buf, int *bitOffset, int *bitsAvail); +int DecodeNoiselessData(AACDecInfo *aacDecInfo, unsigned char **buf, int *bitOffset, int *bitsAvail, int ch); + +int Dequantize(AACDecInfo *aacDecInfo, int ch); +int StereoProcess(AACDecInfo *aacDecInfo); +int DeinterleaveShortBlocks(AACDecInfo *aacDecInfo, int ch); +int PNS(AACDecInfo *aacDecInfo, int ch); +int TNSFilter(AACDecInfo *aacDecInfo, int ch); +int IMDCT(AACDecInfo *aacDecInfo, int ch, int chBase, short *outbuf); + +/* SBR specific functions */ +int InitSBR(AACDecInfo *aacDecInfo); +int InitSBRPre(AACDecInfo *aacDecInfo, void **ptr, int *sz); +void FreeSBR(AACDecInfo *aacDecInfo); +int DecodeSBRBitstream(AACDecInfo *aacDecInfo, int chBase); +int DecodeSBRData(AACDecInfo *aacDecInfo, int chBase, short *outbuf); +int FlushCodecSBR(AACDecInfo *aacDecInfo); + +/* aactabs.c - global ROM tables */ +extern const int sampRateTab[NUM_SAMPLE_RATES]; +extern const int predSFBMax[NUM_SAMPLE_RATES]; +extern const int channelMapTab[NUM_DEF_CHAN_MAPS]; +extern const int elementNumChans[NUM_ELEMENTS]; +extern const unsigned /*char*/ int sfBandTotalShort[NUM_SAMPLE_RATES]; +extern const unsigned /*char*/ int sfBandTotalLong[NUM_SAMPLE_RATES]; +extern const int sfBandTabShortOffset[NUM_SAMPLE_RATES]; +extern const /*short*/ int sfBandTabShort[76]; +extern const int sfBandTabLongOffset[NUM_SAMPLE_RATES]; +extern const /*short*/ int sfBandTabLong[325]; +extern const int tnsMaxBandsShortOffset[AAC_NUM_PROFILES]; +extern const unsigned /*char*/ int tnsMaxBandsShort[2*NUM_SAMPLE_RATES]; +extern const unsigned /*char*/ int tnsMaxOrderShort[AAC_NUM_PROFILES]; +extern const int tnsMaxBandsLongOffset[AAC_NUM_PROFILES]; +extern const unsigned /*char*/ int tnsMaxBandsLong[2*NUM_SAMPLE_RATES]; +extern const unsigned /*char*/ int tnsMaxOrderLong[AAC_NUM_PROFILES]; + +#endif /* _AACCOMMON_H */ diff --git a/components/spotify/cspot/bell/libhelix-aac/aacdec.c b/components/spotify/cspot/bell/libhelix-aac/aacdec.c new file mode 100644 index 00000000..cb0e5041 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/aacdec.c @@ -0,0 +1,476 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: aacdec.c,v 1.1 2005/02/26 01:47:31 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * February 2005 + * + * aacdec.c - platform-independent top level decoder API + **************************************************************************************/ + +#include "aaccommon.h" + +//#include "profile.h" + +#define PROFILE_START(x) +#define PROFILE_END() + +/************************************************************************************** + * Function: AACInitDecoder + * + * Description: allocate memory for platform-specific data + * clear all the user-accessible fields + * initialize SBR decoder if enabled + * + * Inputs: none + * + * Outputs: none + * + * Return: handle to AAC decoder instance, 0 if malloc fails + **************************************************************************************/ +HAACDecoder AACInitDecoder(void) +{ + AACDecInfo *aacDecInfo; + + aacDecInfo = AllocateBuffers(); + if (!aacDecInfo) + return 0; + +#ifdef AAC_ENABLE_SBR + if (InitSBR(aacDecInfo)) { + AACFreeDecoder(aacDecInfo); + return 0; + } +#endif + + return (HAACDecoder)aacDecInfo; +} + +HAACDecoder AACInitDecoderPre(void *ptr, int sz) +{ + AACDecInfo *aacDecInfo; + + aacDecInfo = AllocateBuffersPre(&ptr, &sz); + if (!aacDecInfo) + return 0; + +#ifdef AAC_ENABLE_SBR + if (InitSBRPre(aacDecInfo, &ptr, &sz)) { + return 0; + } +#endif + + return (HAACDecoder)aacDecInfo; +} + +/************************************************************************************** + * Function: AACFreeDecoder + * + * Description: free platform-specific data allocated by AACInitDecoder + * free SBR decoder if enabled + * + * Inputs: valid AAC decoder instance pointer (HAACDecoder) + * + * Outputs: none + * + * Return: none + **************************************************************************************/ +void AACFreeDecoder(HAACDecoder hAACDecoder) +{ + AACDecInfo *aacDecInfo = (AACDecInfo *)hAACDecoder; + + if (!aacDecInfo) + return; + +#ifdef AAC_ENABLE_SBR + FreeSBR(aacDecInfo); +#endif + FreeBuffers(aacDecInfo); +} + +/************************************************************************************** + * Function: AACFindSyncWord + * + * Description: locate the next byte-alinged sync word in the raw AAC stream + * + * Inputs: buffer to search for sync word + * max number of bytes to search in buffer + * + * Outputs: none + * + * Return: offset to first sync word (bytes from start of buf) + * -1 if sync not found after searching nBytes + **************************************************************************************/ +int AACFindSyncWord(unsigned char *buf, int nBytes) +{ + int i; + + /* find byte-aligned syncword (12 bits = 0xFFF) */ + for (i = 0; i < nBytes - 1; i++) { + if ( (buf[i+0] & SYNCWORDH) == SYNCWORDH && (buf[i+1] & SYNCWORDL) == SYNCWORDL ) + return i; + } + + return -1; +} + +/************************************************************************************** + * Function: AACGetLastFrameInfo + * + * Description: get info about last AAC frame decoded (number of samples decoded, + * sample rate, bit rate, etc.) + * + * Inputs: valid AAC decoder instance pointer (HAACDecoder) + * pointer to AACFrameInfo struct + * + * Outputs: filled-in AACFrameInfo struct + * + * Return: none + * + * Notes: call this right after calling AACDecode() + **************************************************************************************/ +void AACGetLastFrameInfo(HAACDecoder hAACDecoder, AACFrameInfo *aacFrameInfo) +{ + AACDecInfo *aacDecInfo = (AACDecInfo *)hAACDecoder; + + if (!aacDecInfo) { + aacFrameInfo->bitRate = 0; + aacFrameInfo->nChans = 0; + aacFrameInfo->sampRateCore = 0; + aacFrameInfo->sampRateOut = 0; + aacFrameInfo->bitsPerSample = 0; + aacFrameInfo->outputSamps = 0; + aacFrameInfo->profile = 0; + aacFrameInfo->tnsUsed = 0; + aacFrameInfo->pnsUsed = 0; + } else { + aacFrameInfo->bitRate = aacDecInfo->bitRate; + aacFrameInfo->nChans = aacDecInfo->nChans; + aacFrameInfo->sampRateCore = aacDecInfo->sampRate; + aacFrameInfo->sampRateOut = aacDecInfo->sampRate * (aacDecInfo->sbrEnabled ? 2 : 1); + aacFrameInfo->bitsPerSample = 16; + aacFrameInfo->outputSamps = aacDecInfo->nChans * AAC_MAX_NSAMPS * (aacDecInfo->sbrEnabled ? 2 : 1); + aacFrameInfo->profile = aacDecInfo->profile; + aacFrameInfo->tnsUsed = aacDecInfo->tnsUsed; + aacFrameInfo->pnsUsed = aacDecInfo->pnsUsed; + } +} + +/************************************************************************************** + * Function: AACSetRawBlockParams + * + * Description: set internal state variables for decoding a stream of raw data blocks + * + * Inputs: valid AAC decoder instance pointer (HAACDecoder) + * flag indicating source of parameters + * AACFrameInfo struct, with the members nChans, sampRate, and profile + * optionally filled-in + * + * Outputs: updated codec state + * + * Return: 0 if successful, error code (< 0) if error + * + * Notes: if copyLast == 1, then the codec sets up its internal state (for + * decoding raw blocks) based on previously-decoded ADTS header info + * if copyLast == 0, then the codec uses the values passed in + * aacFrameInfo to configure its internal state (useful when the + * source is MP4 format, for example) + **************************************************************************************/ +int AACSetRawBlockParams(HAACDecoder hAACDecoder, int copyLast, AACFrameInfo *aacFrameInfo) +{ + AACDecInfo *aacDecInfo = (AACDecInfo *)hAACDecoder; + + if (!aacDecInfo) + return ERR_AAC_NULL_POINTER; + + aacDecInfo->format = AAC_FF_RAW; + if (copyLast) + return SetRawBlockParams(aacDecInfo, 1, 0, 0, 0); + else + return SetRawBlockParams(aacDecInfo, 0, aacFrameInfo->nChans, aacFrameInfo->sampRateCore, aacFrameInfo->profile); +} + +/************************************************************************************** + * Function: AACFlushCodec + * + * Description: flush internal codec state (after seeking, for example) + * + * Inputs: valid AAC decoder instance pointer (HAACDecoder) + * + * Outputs: updated state variables in aacDecInfo + * + * Return: 0 if successful, error code (< 0) if error + **************************************************************************************/ +int AACFlushCodec(HAACDecoder hAACDecoder) +{ + int ch; + AACDecInfo *aacDecInfo = (AACDecInfo *)hAACDecoder; + + if (!aacDecInfo) + return ERR_AAC_NULL_POINTER; + + /* reset common state variables which change per-frame + * don't touch state variables which are (usually) constant for entire clip + * (nChans, sampRate, profile, format, sbrEnabled) + */ + aacDecInfo->prevBlockID = AAC_ID_INVALID; + aacDecInfo->currBlockID = AAC_ID_INVALID; + aacDecInfo->currInstTag = -1; + for (ch = 0; ch < MAX_NCHANS_ELEM; ch++) + aacDecInfo->sbDeinterleaveReqd[ch] = 0; + aacDecInfo->adtsBlocksLeft = 0; + aacDecInfo->tnsUsed = 0; + aacDecInfo->pnsUsed = 0; + + /* reset internal codec state (flush overlap buffers, etc.) */ + FlushCodec(aacDecInfo); +#ifdef AAC_ENABLE_SBR + FlushCodecSBR(aacDecInfo); +#endif + + return ERR_AAC_NONE; +} + +/************************************************************************************** + * Function: AACDecode + * + * Description: decode AAC frame + * + * Inputs: valid AAC decoder instance pointer (HAACDecoder) + * double pointer to buffer of AAC data + * pointer to number of valid bytes remaining in inbuf + * pointer to outbuf, big enough to hold one frame of decoded PCM samples + * (outbuf must be double-sized if SBR enabled) + * + * Outputs: PCM data in outbuf, interleaved LRLRLR... if stereo + * number of output samples = 1024 per channel (2048 if SBR enabled) + * updated inbuf pointer + * updated bytesLeft + * + * Return: 0 if successful, error code (< 0) if error + * + * Notes: inbuf pointer and bytesLeft are not updated until whole frame is + * successfully decoded, so if ERR_AAC_INDATA_UNDERFLOW is returned + * just call AACDecode again with more data in inbuf + **************************************************************************************/ +int AACDecode(HAACDecoder hAACDecoder, unsigned char **inbuf, int *bytesLeft, short *outbuf) +{ + int err, offset, bitOffset, bitsAvail; + int ch, baseChan, elementChans; + unsigned char *inptr; + AACDecInfo *aacDecInfo = (AACDecInfo *)hAACDecoder; +#ifdef AAC_ENABLE_SBR + int baseChanSBR, elementChansSBR; +#endif + + if (!aacDecInfo) + return ERR_AAC_NULL_POINTER; + + /* make local copies (see "Notes" above) */ + inptr = *inbuf; + bitOffset = 0; + bitsAvail = (*bytesLeft) << 3; + + /* first time through figure out what the file format is */ + if (aacDecInfo->format == AAC_FF_Unknown) { + if (bitsAvail < 32) + return ERR_AAC_INDATA_UNDERFLOW; + + if (IS_ADIF(inptr)) { + /* unpack ADIF header */ + aacDecInfo->format = AAC_FF_ADIF; + err = UnpackADIFHeader(aacDecInfo, &inptr, &bitOffset, &bitsAvail); + if (err) + return err; + } else { + /* assume ADTS by default */ + aacDecInfo->format = AAC_FF_ADTS; + } + } + + + + /* if ADTS, search for start of next frame */ + if (aacDecInfo->format == AAC_FF_ADTS) { + /* can have 1-4 raw data blocks per ADTS frame (header only present for first one) */ + if (aacDecInfo->adtsBlocksLeft == 0) { + offset = AACFindSyncWord(inptr, bitsAvail >> 3); + if (offset < 0) + return ERR_AAC_INDATA_UNDERFLOW; + inptr += offset; + bitsAvail -= (offset << 3); + + err = UnpackADTSHeader(aacDecInfo, &inptr, &bitOffset, &bitsAvail); + if (err) + return err; + + if (aacDecInfo->nChans == -1) { + /* figure out implicit channel mapping if necessary */ + err = GetADTSChannelMapping(aacDecInfo, inptr, bitOffset, bitsAvail); + if (err) + return err; + } + } + aacDecInfo->adtsBlocksLeft--; + } else if (aacDecInfo->format == AAC_FF_RAW) { + err = PrepareRawBlock(aacDecInfo); + if (err) + return err; + } + + + + /* check for valid number of channels */ + if (aacDecInfo->nChans > AAC_MAX_NCHANS || aacDecInfo->nChans <= 0) + return ERR_AAC_NCHANS_TOO_HIGH; + + /* will be set later if active in this frame */ + aacDecInfo->tnsUsed = 0; + aacDecInfo->pnsUsed = 0; + + bitOffset = 0; + baseChan = 0; +#ifdef AAC_ENABLE_SBR + baseChanSBR = 0; +#endif + do { + + + + /* parse next syntactic element */ + err = DecodeNextElement(aacDecInfo, &inptr, &bitOffset, &bitsAvail); + if (err) + return err; + + elementChans = elementNumChans[aacDecInfo->currBlockID]; + if (baseChan + elementChans > AAC_MAX_NCHANS) + return ERR_AAC_NCHANS_TOO_HIGH; + + /* noiseless decoder and dequantizer */ + for (ch = 0; ch < elementChans; ch++) { + PROFILE_START("noiseless decoder"); + err = DecodeNoiselessData(aacDecInfo, &inptr, &bitOffset, &bitsAvail, ch); + PROFILE_END(); + + if (err) + return err; + + PROFILE_START("dequant"); + if (Dequantize(aacDecInfo, ch)) + return ERR_AAC_DEQUANT; + PROFILE_END(); + } + + PROFILE_START("mid-side and intensity stereo"); + /* mid-side and intensity stereo */ + if (aacDecInfo->currBlockID == AAC_ID_CPE) { + if (StereoProcess(aacDecInfo)) + return ERR_AAC_STEREO_PROCESS; + } + PROFILE_END(); + + + /* PNS, TNS, inverse transform */ + for (ch = 0; ch < elementChans; ch++) { + PROFILE_START("PNS"); + if (PNS(aacDecInfo, ch)) + return ERR_AAC_PNS; + PROFILE_END(); + + if (aacDecInfo->sbDeinterleaveReqd[ch]) { + /* deinterleave short blocks, if required */ + if (DeinterleaveShortBlocks(aacDecInfo, ch)) + return ERR_AAC_SHORT_BLOCK_DEINT; + aacDecInfo->sbDeinterleaveReqd[ch] = 0; + } + + PROFILE_START("TNS"); + if (TNSFilter(aacDecInfo, ch)) + return ERR_AAC_TNS; + PROFILE_END(); + + PROFILE_START("IMDCT"); + if (IMDCT(aacDecInfo, ch, baseChan + ch, outbuf)) + return ERR_AAC_IMDCT; + PROFILE_END(); + } + +#ifdef AAC_ENABLE_SBR + if (aacDecInfo->sbrEnabled && (aacDecInfo->currBlockID == AAC_ID_FIL || aacDecInfo->currBlockID == AAC_ID_LFE)) { + if (aacDecInfo->currBlockID == AAC_ID_LFE) + elementChansSBR = elementNumChans[AAC_ID_LFE]; + else if (aacDecInfo->currBlockID == AAC_ID_FIL && (aacDecInfo->prevBlockID == AAC_ID_SCE || aacDecInfo->prevBlockID == AAC_ID_CPE)) + elementChansSBR = elementNumChans[aacDecInfo->prevBlockID]; + else + elementChansSBR = 0; + + if (baseChanSBR + elementChansSBR > AAC_MAX_NCHANS) + return ERR_AAC_SBR_NCHANS_TOO_HIGH; + + /* parse SBR extension data if present (contained in a fill element) */ + if (DecodeSBRBitstream(aacDecInfo, baseChanSBR)) + return ERR_AAC_SBR_BITSTREAM; + + /* apply SBR */ + if (DecodeSBRData(aacDecInfo, baseChanSBR, outbuf)) + return ERR_AAC_SBR_DATA; + + baseChanSBR += elementChansSBR; + } +#endif + + baseChan += elementChans; + } while (aacDecInfo->currBlockID != AAC_ID_END); + + /* byte align after each raw_data_block */ + if (bitOffset) { + inptr++; + bitsAvail -= (8-bitOffset); + bitOffset = 0; + if (bitsAvail < 0) + return ERR_AAC_INDATA_UNDERFLOW; + } + + /* update pointers */ + aacDecInfo->frameCount++; + *bytesLeft -= (inptr - *inbuf); + *inbuf = inptr; + + return ERR_AAC_NONE; +} + diff --git a/components/spotify/cspot/bell/libhelix-aac/aacdec.h b/components/spotify/cspot/bell/libhelix-aac/aacdec.h new file mode 100644 index 00000000..9cc738e7 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/aacdec.h @@ -0,0 +1,175 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: aacdec.h,v 1.8 2005/11/10 00:15:08 margotm Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * aacdec.h - public C API for AAC decoder + **************************************************************************************/ + +#ifndef _AACDEC_H +#define _AACDEC_H + +#if defined(_WIN32) && !defined(_WIN32_WCE) +# +#elif defined(_WIN32) && defined(_WIN32_WCE) && defined(ARM) +# +#elif defined(_WIN32) && defined(WINCE_EMULATOR) +# +#elif defined (__arm) && defined (__ARMCC_VERSION) +# +#elif defined(_SYMBIAN) && defined(__WINS__) +# +#elif defined(__GNUC__) && defined(__arm__) +# +#elif defined(__GNUC__) && defined(__i386__) +# +#elif defined(__APPLE__) +# +#elif defined(__GNUC__) && defined(__amd64__) +# +#elif defined(__GNUC__) && (defined(__powerpc__) || defined(__POWERPC__)) +# +#elif defined(_OPENWAVE_SIMULATOR) || defined(_OPENWAVE_ARMULATOR) +# +#elif defined(_SOLARIS) && !defined(__GNUC__) +# +#elif defined(ESP_PLATFORM) +# +#else +#error No platform defined. See valid options in aacdec.h +#endif + +#ifndef USE_DEFAULT_STDLIB +#define USE_DEFAULT_STDLIB +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* according to spec (13818-7 section 8.2.2, 14496-3 section 4.5.3) + * max size of input buffer = + * 6144 bits = 768 bytes per SCE or CCE-I + * 12288 bits = 1536 bytes per CPE + * 0 bits = 0 bytes per CCE-D (uses bits from the SCE/CPE/CCE-I it is coupled to) + */ +#ifndef AAC_MAX_NCHANS /* if max channels isn't set in makefile, */ +#define AAC_MAX_NCHANS 2 /* set to default max number of channels */ +#endif +#define AAC_MAX_NSAMPS 1024 +#define AAC_MAINBUF_SIZE (768 * AAC_MAX_NCHANS) + +#define AAC_NUM_PROFILES 3 +#define AAC_PROFILE_MP 0 +#define AAC_PROFILE_LC 1 +#define AAC_PROFILE_SSR 2 + +/* define these to enable decoder features */ +#if defined(HELIX_FEATURE_AUDIO_CODEC_AAC_SBR) +#define AAC_ENABLE_SBR +#endif // HELIX_FEATURE_AUDIO_CODEC_AAC_SBR. +#define AAC_ENABLE_MPEG4 + +enum { + ERR_AAC_NONE = 0, + ERR_AAC_INDATA_UNDERFLOW = -1, + ERR_AAC_NULL_POINTER = -2, + ERR_AAC_INVALID_ADTS_HEADER = -3, + ERR_AAC_INVALID_ADIF_HEADER = -4, + ERR_AAC_INVALID_FRAME = -5, + ERR_AAC_MPEG4_UNSUPPORTED = -6, + ERR_AAC_CHANNEL_MAP = -7, + ERR_AAC_SYNTAX_ELEMENT = -8, + + ERR_AAC_DEQUANT = -9, + ERR_AAC_STEREO_PROCESS = -10, + ERR_AAC_PNS = -11, + ERR_AAC_SHORT_BLOCK_DEINT = -12, + ERR_AAC_TNS = -13, + ERR_AAC_IMDCT = -14, + ERR_AAC_NCHANS_TOO_HIGH = -15, + + ERR_AAC_SBR_INIT = -16, + ERR_AAC_SBR_BITSTREAM = -17, + ERR_AAC_SBR_DATA = -18, + ERR_AAC_SBR_PCM_FORMAT = -19, + ERR_AAC_SBR_NCHANS_TOO_HIGH = -20, + ERR_AAC_SBR_SINGLERATE_UNSUPPORTED = -21, + + ERR_AAC_RAWBLOCK_PARAMS = -22, + + ERR_AAC_UNKNOWN = -9999 +}; + +typedef struct _AACFrameInfo { + int bitRate; + int nChans; + int sampRateCore; + int sampRateOut; + int bitsPerSample; + int outputSamps; + int profile; + int tnsUsed; + int pnsUsed; +} AACFrameInfo; + +typedef void *HAACDecoder; + +/* public C API */ +HAACDecoder AACInitDecoder(void); +HAACDecoder AACInitDecoderPre(void *ptr, int sz); +void AACFreeDecoder(HAACDecoder hAACDecoder); +int AACDecode(HAACDecoder hAACDecoder, unsigned char **inbuf, int *bytesLeft, short *outbuf); + +int AACFindSyncWord(unsigned char *buf, int nBytes); +void AACGetLastFrameInfo(HAACDecoder hAACDecoder, AACFrameInfo *aacFrameInfo); +int AACSetRawBlockParams(HAACDecoder hAACDecoder, int copyLast, AACFrameInfo *aacFrameInfo); +int AACFlushCodec(HAACDecoder hAACDecoder); + +#ifdef HELIX_CONFIG_AAC_GENERATE_TRIGTABS_FLOAT +int AACInitTrigtabsFloat(void); +void AACFreeTrigtabsFloat(void); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _AACDEC_H */ diff --git a/components/spotify/cspot/bell/libhelix-aac/aactabs.c b/components/spotify/cspot/bell/libhelix-aac/aactabs.c new file mode 100644 index 00000000..1efa20ca --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/aactabs.c @@ -0,0 +1,157 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: aactabs.c,v 1.1 2005/02/26 01:47:31 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * February 2005 + * + * aactabs.c - platform-independent tables for AAC decoder (global, read-only) + **************************************************************************************/ + +#include "aaccommon.h" + +/* sample rates (table 4.5.1) */ +const int sampRateTab[NUM_SAMPLE_RATES] PROGMEM = { + 96000, 88200, 64000, 48000, 44100, 32000, + 24000, 22050, 16000, 12000, 11025, 8000 +}; + +/* max scalefactor band for prediction (main profile only) */ +const int predSFBMax[NUM_SAMPLE_RATES] PROGMEM = { + 33, 33, 38, 40, 40, 40, 41, 41, 37, 37, 37, 34 +}; + +/* channel mapping (table 1.6.3.4) (-1 = unknown, so need to determine mapping based on rules in 8.5.1) */ +const int channelMapTab[NUM_DEF_CHAN_MAPS] PROGMEM = { + -1, 1, 2, 3, 4, 5, 6, 8 +}; + +/* number of channels in each element (SCE, CPE, etc.) + * see AACElementID in aaccommon.h + */ +const int elementNumChans[NUM_ELEMENTS] PROGMEM = { + 1, 2, 0, 1, 0, 0, 0, 0 +}; + +/* total number of scale factor bands in one window */ +const unsigned int /*char*/ sfBandTotalShort[NUM_SAMPLE_RATES] PROGMEM = { + 12, 12, 12, 14, 14, 14, 15, 15, 15, 15, 15, 15 +}; + +const unsigned int /*char*/ sfBandTotalLong[NUM_SAMPLE_RATES] PROGMEM = { + 41, 41, 47, 49, 49, 51, 47, 47, 43, 43, 43, 40 +}; + +/* scale factor band tables */ +const int sfBandTabShortOffset[NUM_SAMPLE_RATES] PROGMEM = {0, 0, 0, 13, 13, 13, 28, 28, 44, 44, 44, 60}; + +const /*short*/ int sfBandTabShort[76] PROGMEM = { + /* short block 64, 88, 96 kHz [13] (tables 4.5.24, 4.5.26) */ + 0, 4, 8, 12, 16, 20, 24, 32, 40, 48, 64, 92, 128, + + /* short block 32, 44, 48 kHz [15] (table 4.5.15) */ + 0, 4, 8, 12, 16, 20, 28, 36, 44, 56, 68, 80, 96, 112, 128, + + /* short block 22, 24 kHz [16] (table 4.5.22) */ + 0, 4, 8, 12, 16, 20, 24, 28, 36, 44, 52, 64, 76, 92, 108, 128, + + /* short block 11, 12, 16 kHz [16] (table 4.5.20) */ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 40, 48, 60, 72, 88, 108, 128, + + /* short block 8 kHz [16] (table 4.5.18) */ + 0, 4, 8, 12, 16, 20, 24, 28, 36, 44, 52, 60, 72, 88, 108, 128 +}; + +const int sfBandTabLongOffset[NUM_SAMPLE_RATES] PROGMEM = {0, 0, 42, 90, 90, 140, 192, 192, 240, 240, 240, 284}; + +const /*short*/ int sfBandTabLong[325] PROGMEM = { + /* long block 88, 96 kHz [42] (table 4.5.25) */ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, + 56, 64, 72, 80, 88, 96, 108, 120, 132, 144, 156, 172, 188, 212, + 240, 276, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960, 1024, + + /* long block 64 kHz [48] (table 4.5.13) */ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 64, + 72, 80, 88, 100, 112, 124, 140, 156, 172, 192, 216, 240, 268, 304, 344, 384, + 424, 464, 504, 544, 584, 624, 664, 704, 744, 784, 824, 864, 904, 944, 984, 1024, + + /* long block 44, 48 kHz [50] (table 4.5.14) */ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 48, 56, 64, 72, 80, 88, + 96, 108, 120, 132, 144, 160, 176, 196, 216, 240, 264, 292, 320, 352, 384, 416, 448, + 480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800, 832, 864, 896, 928, 1024, + + /* long block 32 kHz [52] (table 4.5.16) */ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 48, 56, 64, 72, 80, 88, 96, + 108, 120, 132, 144, 160, 176, 196, 216, 240, 264, 292, 320, 352, 384, 416, 448, 480, 512, + 544, 576, 608, 640, 672, 704, 736, 768, 800, 832, 864, 896, 928, 960, 992, 1024, + + /* long block 22, 24 kHz [48] (table 4.5.21) */ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 52, 60, 68, 76, + 84, 92, 100, 108, 116, 124, 136, 148, 160, 172, 188, 204, 220, 240, 260, 284, + 308, 336, 364, 396, 432, 468, 508, 552, 600, 652, 704, 768, 832, 896, 960, 1024, + + /* long block 11, 12, 16 kHz [44] (table 4.5.19) */ + 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 100, 112, 124, + 136, 148, 160, 172, 184, 196, 212, 228, 244, 260, 280, 300, 320, 344, 368, + 396, 424, 456, 492, 532, 572, 616, 664, 716, 772, 832, 896, 960, 1024, + + /* long block 8 kHz [41] (table 4.5.17) */ + 0, 12, 24, 36, 48, 60, 72, 84, 96, 108, 120, 132, 144, 156, + 172, 188, 204, 220, 236, 252, 268, 288, 308, 328, 348, 372, 396, 420, + 448, 476, 508, 544, 580, 620, 664, 712, 764, 820, 880, 944, 1024 +}; + + +/* TNS max bands (table 4.139) and max order (table 4.138) */ +const int tnsMaxBandsShortOffset[AAC_NUM_PROFILES] PROGMEM = {0, 0, 12}; + +const unsigned /*char*/ int tnsMaxBandsShort[2*NUM_SAMPLE_RATES] PROGMEM = { + 9, 9, 10, 14, 14, 14, 14, 14, 14, 14, 14, 14, /* short block, Main/LC */ + 7, 7, 7, 6, 6, 6, 7, 7, 8, 8, 8, 7 /* short block, SSR */ +}; + +const unsigned /*char*/ int tnsMaxOrderShort[AAC_NUM_PROFILES] PROGMEM = {7, 7, 7}; + +const int tnsMaxBandsLongOffset[AAC_NUM_PROFILES] PROGMEM = {0, 0, 12}; + +const unsigned int /*char*/ tnsMaxBandsLong[2*NUM_SAMPLE_RATES] PROGMEM = { + 31, 31, 34, 40, 42, 51, 46, 46, 42, 42, 42, 39, /* long block, Main/LC */ + 28, 28, 27, 26, 26, 26, 29, 29, 23, 23, 23, 19, /* long block, SSR */ +}; + +const unsigned /*char*/ int tnsMaxOrderLong[AAC_NUM_PROFILES] PROGMEM = {20, 12, 12}; diff --git a/components/spotify/cspot/bell/libhelix-aac/assembly.h b/components/spotify/cspot/bell/libhelix-aac/assembly.h new file mode 100644 index 00000000..1d769f2b --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/assembly.h @@ -0,0 +1,638 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: assembly.h,v 1.7 2005/11/10 00:04:40 margotm Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * assembly.h - inline assembly language functions and prototypes + * + * MULSHIFT32(x, y) signed multiply of two 32-bit integers (x and y), + * returns top 32-bits of 64-bit result + * CLIPTOSHORT(x) convert 32-bit integer to 16-bit short, + * clipping to [-32768, 32767] + * FASTABS(x) branchless absolute value of signed integer x + * CLZ(x) count leading zeros on signed integer x + * MADD64(sum64, x, y) 64-bit multiply accumulate: sum64 += (x*y) + **************************************************************************************/ + +#ifndef _ASSEMBLY_H +#define _ASSEMBLY_H + +/* toolchain: MSFT Visual C++ + * target architecture: x86 + */ +#if (defined (_WIN32) && !defined (_WIN32_WCE)) || (defined (__WINS__) && defined (_SYMBIAN)) || (defined (WINCE_EMULATOR)) || (defined (_OPENWAVE_SIMULATOR)) + +#pragma warning( disable : 4035 ) /* complains about inline asm not returning a value */ + +static __inline int MULSHIFT32(int x, int y) +{ + __asm { + mov eax, x + imul y + mov eax, edx + } +} + +static __inline short CLIPTOSHORT(int x) +{ + int sign; + + /* clip to [-32768, 32767] */ + sign = x >> 31; + if (sign != (x >> 15)) + x = sign ^ ((1 << 15) - 1); + + return (short)x; +} + +static __inline int FASTABS(int x) +{ + int sign; + + sign = x >> (sizeof(int) * 8 - 1); + x ^= sign; + x -= sign; + + return x; +} + +static __inline int CLZ(int x) +{ + int numZeros; + + if (!x) + return 32; + + /* count leading zeros with binary search */ + numZeros = 1; + if (!((unsigned int)x >> 16)) { numZeros += 16; x <<= 16; } + if (!((unsigned int)x >> 24)) { numZeros += 8; x <<= 8; } + if (!((unsigned int)x >> 28)) { numZeros += 4; x <<= 4; } + if (!((unsigned int)x >> 30)) { numZeros += 2; x <<= 2; } + + numZeros -= ((unsigned int)x >> 31); + + return numZeros; +} + +#ifdef __CW32__ +typedef long long Word64; +#else +typedef __int64 Word64; +#endif + +typedef union _U64 { + Word64 w64; + struct { + /* x86 = little endian */ + unsigned int lo32; + signed int hi32; + } r; +} U64; + +/* returns 64-bit value in [edx:eax] */ +static __inline Word64 MADD64(Word64 sum64, int x, int y) +{ +#if (defined (_SYMBIAN_61_) || defined (_SYMBIAN_70_)) && defined (__WINS__) && !defined (__CW32__) +/* Workaround for the Symbian emulator because of non existing longlong.lib and + * hence __allmul not defined. */ + __asm { + mov eax, x + imul y + add dword ptr sum64, eax + adc dword ptr sum64 + 4, edx + } +#else + sum64 += (Word64)x * (Word64)y; +#endif + + return sum64; +} + +/* toolchain: MSFT Embedded Visual C++ + * target architecture: ARM v.4 and above (require 'M' type processor for 32x32->64 multiplier) + */ +#elif defined (_WIN32) && defined (_WIN32_WCE) && defined (ARM) + +static __inline short CLIPTOSHORT(int x) +{ + int sign; + + /* clip to [-32768, 32767] */ + sign = x >> 31; + if (sign != (x >> 15)) + x = sign ^ ((1 << 15) - 1); + + return (short)x; +} + +static __inline int FASTABS(int x) +{ + int sign; + + sign = x >> (sizeof(int) * 8 - 1); + x ^= sign; + x -= sign; + + return x; +} + +static __inline int CLZ(int x) +{ + int numZeros; + + if (!x) + return 32; + + /* count leading zeros with binary search (function should be 17 ARM instructions total) */ + numZeros = 1; + if (!((unsigned int)x >> 16)) { numZeros += 16; x <<= 16; } + if (!((unsigned int)x >> 24)) { numZeros += 8; x <<= 8; } + if (!((unsigned int)x >> 28)) { numZeros += 4; x <<= 4; } + if (!((unsigned int)x >> 30)) { numZeros += 2; x <<= 2; } + + numZeros -= ((unsigned int)x >> 31); + + return numZeros; +} + +/* implemented in asmfunc.s */ +#ifdef __cplusplus +extern "C" { +#endif + +typedef __int64 Word64; + +typedef union _U64 { + Word64 w64; + struct { + /* ARM WinCE = little endian */ + unsigned int lo32; + signed int hi32; + } r; +} U64; + +/* manual name mangling for just this platform (must match labels in .s file) */ +#define MULSHIFT32 raac_MULSHIFT32 +#define MADD64 raac_MADD64 + +int MULSHIFT32(int x, int y); +Word64 MADD64(Word64 sum64, int x, int y); + +#ifdef __cplusplus +} +#endif + +/* toolchain: ARM ADS or RealView + * target architecture: ARM v.4 and above (requires 'M' type processor for 32x32->64 multiplier) + */ +#elif defined (XXX__arm) && defined (__ARMCC_VERSION) + +static __inline int MULSHIFT32(int x, int y) +{ + /* rules for smull RdLo, RdHi, Rm, Rs: + * RdHi != Rm + * RdLo != Rm + * RdHi != RdLo + */ + int zlow; + __asm { + smull zlow,y,x,y + } + + return y; +} + +static __inline short CLIPTOSHORT(int x) +{ + int sign; + + /* clip to [-32768, 32767] */ + sign = x >> 31; + if (sign != (x >> 15)) + x = sign ^ ((1 << 15) - 1); + + return (short)x; +} + +static __inline int FASTABS(int x) +{ + int sign; + + sign = x >> (sizeof(int) * 8 - 1); + x ^= sign; + x -= sign; + + return x; +} + +static __inline int CLZ(int x) +{ + int numZeros; + + if (!x) + return 32; + + /* count leading zeros with binary search (function should be 17 ARM instructions total) */ + numZeros = 1; + if (!((unsigned int)x >> 16)) { numZeros += 16; x <<= 16; } + if (!((unsigned int)x >> 24)) { numZeros += 8; x <<= 8; } + if (!((unsigned int)x >> 28)) { numZeros += 4; x <<= 4; } + if (!((unsigned int)x >> 30)) { numZeros += 2; x <<= 2; } + + numZeros -= ((unsigned int)x >> 31); + + return numZeros; + +/* ARM code would look like this, but do NOT use inline asm in ADS for this, + because you can't safely use the status register flags intermixed with C code + + __asm { + mov numZeros, #1 + tst x, 0xffff0000 + addeq numZeros, numZeros, #16 + moveq x, x, lsl #16 + tst x, 0xff000000 + addeq numZeros, numZeros, #8 + moveq x, x, lsl #8 + tst x, 0xf0000000 + addeq numZeros, numZeros, #4 + moveq x, x, lsl #4 + tst x, 0xc0000000 + addeq numZeros, numZeros, #2 + moveq x, x, lsl #2 + sub numZeros, numZeros, x, lsr #31 + } +*/ +/* reference: + numZeros = 0; + while (!(x & 0x80000000)) { + numZeros++; + x <<= 1; + } +*/ +} + +typedef __int64 Word64; + +typedef union _U64 { + Word64 w64; + struct { + /* ARM ADS = little endian */ + unsigned int lo32; + signed int hi32; + } r; +} U64; + +static __inline Word64 MADD64(Word64 sum64, int x, int y) +{ + U64 u; + u.w64 = sum64; + + __asm { + smlal u.r.lo32, u.r.hi32, x, y + } + + return u.w64; +} + +/* toolchain: ARM gcc + * target architecture: ARM v.4 and above (requires 'M' type processor for 32x32->64 multiplier) + */ +#elif defined(__GNUC__) && defined(XXXX__arm__) + +static inline int MULSHIFT32(int x, int y) +{ + int zlow; + asm ("smull %0,%1,%2,%3" : "=&r" (zlow), "=r" (y) : "r" (x), "1" (y) : "cc"); + return y; +} +/* +static inline short CLIPTOSHORT(int x) +{ + int sign; + + // clip to [-32768, 32767] // + sign = x >> 31; + if (sign != (x >> 15)) + x = sign ^ ((1 << 15) - 1); + + return (short)x; +} +*/ +static inline short CLIPTOSHORT(int x) +{ + asm ("ssat %0, #16, %1" : "=r" (x) : "r" (x)); + return x; +} + +/* From coder.h, ORIGINAL: +clip to [-2^n, 2^n-1], valid range of n = [1, 30] +//TODO (FB) Is there a better way ? +*/ +#define CLIP_2N(y, n) { \ + int sign = (y) >> 31; \ + if (sign != (y) >> (n)) { \ + (y) = sign ^ ((1 << (n)) - 1); \ + } \ +} + +/* From coder.h, ORIGINAL: + do y <<= n, clipping to range [-2^30, 2^30 - 1] (i.e. output has one guard bit) +*/ +//TODO (FB) Is there a better way ? +#define CLIP_2N_SHIFT(y, n) { \ + int sign = (y) >> 31; \ + if (sign != (y) >> (30 - (n))) { \ + (y) = sign ^ (0x3fffffff); \ + } else { \ + (y) = (y) << (n); \ + } \ + } + + + +#define FASTABS(x) abs(x) //FB +#define CLZ(x) __builtin_clz(x) //FB + +//Reverse byte order (16 bit) //FB +static inline unsigned int REV16( unsigned int value) +{ + asm ("rev16 %0, %1" : "=r" (value) : "r" (value) ); + return(value); +} + +//Reverse byte order (32 bit) //FB +static inline unsigned int REV32( unsigned int value) +{ + asm ("rev %0, %1" : "=r" (value) : "r" (value) ); + return(value); +} + + +typedef long long Word64; + +typedef union _U64 { + Word64 w64; + struct { + /* little endian */ + unsigned int lo32; + signed int hi32; + } r; +} U64; + +static inline Word64 MADD64(Word64 sum64, int x, int y) +{ + U64 u; + u.w64 = sum64; + asm ("smlal %0,%1,%2,%3" : "+&r" (u.r.lo32), "+&r" (u.r.hi32) : "r" (x), "r" (y) : "cc"); + return u.w64; +} + +/* toolchain: x86 gcc + * target architecture: x86 + */ +#elif defined(__APPLE__) || defined(__GNUC__) && (defined(__i386__) || defined(__amd64__)) || (defined (_SOLARIS) && !defined (__GNUC__) && defined(_SOLARISX86)) + +typedef long long Word64; + +static __inline__ int MULSHIFT32(int x, int y) +{ + int z; + + z = (Word64)x * (Word64)y >> 32; + + return z; +} + +static __inline short CLIPTOSHORT(int x) +{ + int sign; + + /* clip to [-32768, 32767] */ + sign = x >> 31; + if (sign != (x >> 15)) + x = sign ^ ((1 << 15) - 1); + + return (short)x; +} + +static __inline int FASTABS(int x) +{ + int sign; + + sign = x >> (sizeof(int) * 8 - 1); + x ^= sign; + x -= sign; + + return x; +} + +static __inline int CLZ(int x) +{ + int numZeros; + + if (!x) + return 32; + + /* count leading zeros with binary search (function should be 17 ARM instructions total) */ + numZeros = 1; + if (!((unsigned int)x >> 16)) { numZeros += 16; x <<= 16; } + if (!((unsigned int)x >> 24)) { numZeros += 8; x <<= 8; } + if (!((unsigned int)x >> 28)) { numZeros += 4; x <<= 4; } + if (!((unsigned int)x >> 30)) { numZeros += 2; x <<= 2; } + + numZeros -= ((unsigned int)x >> 31); + + return numZeros; +} + +typedef union _U64 { + Word64 w64; + struct { + /* x86 = little endian */ + unsigned int lo32; + signed int hi32; + } r; +} U64; + +static __inline Word64 MADD64(Word64 sum64, int x, int y) +{ + sum64 += (Word64)x * (Word64)y; + + return sum64; +} + +#elif defined(ESP_PLATFORM) || defined(__GNUC__) && (defined(__powerpc__) || defined(__POWERPC__)) || (defined (_SOLARIS) && !defined (__GNUC__) && !defined (_SOLARISX86)) + +typedef long long Word64; + +static __inline__ int MULSHIFT32(int x, int y) +{ + int z; + + z = (Word64)x * (Word64)y >> 32; + + return z; +} + +static __inline short CLIPTOSHORT(int x) +{ + int sign; + + /* clip to [-32768, 32767] */ + sign = x >> 31; + if (sign != (x >> 15)) + x = sign ^ ((1 << 15) - 1); + + return (short)x; +} + +static __inline int FASTABS(int x) +{ + int sign; + + sign = x >> (sizeof(int) * 8 - 1); + x ^= sign; + x -= sign; + + return x; +} + +static __inline int CLZ(int x) +{ + int numZeros; + + if (!x) + return 32; + + /* count leading zeros with binary search (function should be 17 ARM instructions total) */ + numZeros = 1; + if (!((unsigned int)x >> 16)) { numZeros += 16; x <<= 16; } + if (!((unsigned int)x >> 24)) { numZeros += 8; x <<= 8; } + if (!((unsigned int)x >> 28)) { numZeros += 4; x <<= 4; } + if (!((unsigned int)x >> 30)) { numZeros += 2; x <<= 2; } + + numZeros -= ((unsigned int)x >> 31); + + return numZeros; +} + +typedef union _U64 { + Word64 w64; + struct { +#ifdef __XTENSA__ + unsigned int lo32; + signed int hi32; +#else + /* PowerPC = big endian */ + signed int hi32; + unsigned int lo32; +#endif + } r; +} U64; + +static __inline Word64 MADD64(Word64 sum64, int x, int y) +{ + sum64 += (Word64)x * (Word64)y; + + return sum64; +} + +/* From coder.h, ORIGINAL: +clip to [-2^n, 2^n-1], valid range of n = [1, 30] +//TODO (FB) Is there a better way ? +*/ +#define CLIP_2N(y, n) { \ + int sign = (y) >> 31; \ + if (sign != (y) >> (n)) { \ + (y) = sign ^ ((1 << (n)) - 1); \ + } \ +} + +/* From coder.h, ORIGINAL: + do y <<= n, clipping to range [-2^30, 2^30 - 1] (i.e. output has one guard bit) +*/ +//TODO (FB) Is there a better way ? +#define CLIP_2N_SHIFT(y, n) { \ + int sign = (y) >> 31; \ + if (sign != (y) >> (30 - (n))) { \ + (y) = sign ^ (0x3fffffff); \ + } else { \ + (y) = (y) << (n); \ + } \ + } + + + +//#define FASTABS(x) abs(x) //FB +//#define CLZ(x) __builtin_clz(x) //FB + +#else + +#error Unsupported platform in assembly.h + +#endif /* platforms */ + +#ifndef CLIP_2N +#define CLIP_2N(y, n) { \ + int sign = (y) >> 31; \ + if (sign != (y) >> (n)) { \ + (y) = sign ^ ((1 << (n)) - 1); \ + } \ +} +#endif + +#ifndef CLIP_2N_SHIFT +/* From coder.h, ORIGINAL: + do y <<= n, clipping to range [-2^30, 2^30 - 1] (i.e. output has one guard bit) +*/ +//TODO (FB) Is there a better way ? +#define CLIP_2N_SHIFT(y, n) { \ + int sign = (y) >> 31; \ + if (sign != (y) >> (30 - (n))) { \ + (y) = sign ^ (0x3fffffff); \ + } else { \ + (y) = (y) << (n); \ + } \ + } +#endif + +#endif /* _ASSEMBLY_H */ diff --git a/components/spotify/cspot/bell/libhelix-aac/bitstream.c b/components/spotify/cspot/bell/libhelix-aac/bitstream.c new file mode 100644 index 00000000..09cb3f65 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/bitstream.c @@ -0,0 +1,261 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: bitstream.c,v 1.2 2005/09/27 20:31:11 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * bitstream.c - bitstream parsing functions + **************************************************************************************/ + +#include "bitstream.h" + +/************************************************************************************** + * Function: SetBitstreamPointer + * + * Description: initialize bitstream reader + * + * Inputs: pointer to BitStreamInfo struct + * number of bytes in bitstream + * pointer to byte-aligned buffer of data to read from + * + * Outputs: initialized bitstream info struct + * + * Return: none + **************************************************************************************/ +void SetBitstreamPointer(BitStreamInfo *bsi, int nBytes, unsigned char *buf) +{ + /* init bitstream */ + bsi->bytePtr = buf; + bsi->iCache = 0; /* 4-byte unsigned int */ + bsi->cachedBits = 0; /* i.e. zero bits in cache */ + bsi->nBytes = nBytes; +} + +/************************************************************************************** + * Function: RefillBitstreamCache + * + * Description: read new data from bitstream buffer into 32-bit cache + * + * Inputs: pointer to initialized BitStreamInfo struct + * + * Outputs: updated bitstream info struct + * + * Return: none + * + * Notes: only call when iCache is completely drained (resets bitOffset to 0) + * always loads 4 new bytes except when bsi->nBytes < 4 (end of buffer) + * stores data as big-endian in cache, regardless of machine endian-ness + **************************************************************************************/ +//Optimized for REV16, REV32 (FB) +static __inline void RefillBitstreamCache(BitStreamInfo *bsi) +{ + int nBytes = bsi->nBytes; + if (nBytes >= 4) { + /* optimize for common case, independent of machine endian-ness */ + bsi->iCache = (*bsi->bytePtr++) << 24; + bsi->iCache |= (*bsi->bytePtr++) << 16; + bsi->iCache |= (*bsi->bytePtr++) << 8; + bsi->iCache |= (*bsi->bytePtr++); + + bsi->cachedBits = 32; + bsi->nBytes -= 4; + } else { + bsi->iCache = 0; + while (nBytes--) { + bsi->iCache |= (*bsi->bytePtr++); + bsi->iCache <<= 8; + } + bsi->iCache <<= ((3 - bsi->nBytes)*8); + bsi->cachedBits = 8*bsi->nBytes; + bsi->nBytes = 0; + } +} + +/************************************************************************************** + * Function: GetBits + * + * Description: get bits from bitstream, advance bitstream pointer + * + * Inputs: pointer to initialized BitStreamInfo struct + * number of bits to get from bitstream + * + * Outputs: updated bitstream info struct + * + * Return: the next nBits bits of data from bitstream buffer + * + * Notes: nBits must be in range [0, 31], nBits outside this range masked by 0x1f + * for speed, does not indicate error if you overrun bit buffer + * if nBits == 0, returns 0 + **************************************************************************************/ +unsigned int GetBits(BitStreamInfo *bsi, int nBits) +{ + unsigned int data, lowBits; + + nBits &= 0x1f; /* nBits mod 32 to avoid unpredictable results like >> by negative amount */ + data = bsi->iCache >> (31 - nBits); /* unsigned >> so zero-extend */ + data >>= 1; /* do as >> 31, >> 1 so that nBits = 0 works okay (returns 0) */ + bsi->iCache <<= nBits; /* left-justify cache */ + bsi->cachedBits -= nBits; /* how many bits have we drawn from the cache so far */ + + /* if we cross an int boundary, refill the cache */ + if (bsi->cachedBits < 0) { + lowBits = -bsi->cachedBits; + RefillBitstreamCache(bsi); + data |= bsi->iCache >> (32 - lowBits); /* get the low-order bits */ + + bsi->cachedBits -= lowBits; /* how many bits have we drawn from the cache so far */ + bsi->iCache <<= lowBits; /* left-justify cache */ + } + + return data; +} + +/************************************************************************************** + * Function: GetBitsNoAdvance + * + * Description: get bits from bitstream, do not advance bitstream pointer + * + * Inputs: pointer to initialized BitStreamInfo struct + * number of bits to get from bitstream + * + * Outputs: none (state of BitStreamInfo struct left unchanged) + * + * Return: the next nBits bits of data from bitstream buffer + * + * Notes: nBits must be in range [0, 31], nBits outside this range masked by 0x1f + * for speed, does not indicate error if you overrun bit buffer + * if nBits == 0, returns 0 + **************************************************************************************/ +unsigned int GetBitsNoAdvance(BitStreamInfo *bsi, int nBits) +{ + unsigned char *buf; + unsigned int data, iCache; + signed int lowBits; + + nBits &= 0x1f; /* nBits mod 32 to avoid unpredictable results like >> by negative amount */ + data = bsi->iCache >> (31 - nBits); /* unsigned >> so zero-extend */ + data >>= 1; /* do as >> 31, >> 1 so that nBits = 0 works okay (returns 0) */ + lowBits = nBits - bsi->cachedBits; /* how many bits do we have left to read */ + + /* if we cross an int boundary, read next bytes in buffer */ + if (lowBits > 0) { + iCache = 0; + buf = bsi->bytePtr; + while (lowBits > 0) { + iCache <<= 8; + if (buf < bsi->bytePtr + bsi->nBytes) + iCache |= (unsigned int)*buf++; + lowBits -= 8; + } + lowBits = -lowBits; + data |= iCache >> lowBits; + } + + return data; +} + +/************************************************************************************** + * Function: AdvanceBitstream + * + * Description: move bitstream pointer ahead + * + * Inputs: pointer to initialized BitStreamInfo struct + * number of bits to advance bitstream + * + * Outputs: updated bitstream info struct + * + * Return: none + * + * Notes: generally used following GetBitsNoAdvance(bsi, maxBits) + **************************************************************************************/ +void AdvanceBitstream(BitStreamInfo *bsi, int nBits) +{ + nBits &= 0x1f; + if (nBits > bsi->cachedBits) { + nBits -= bsi->cachedBits; + RefillBitstreamCache(bsi); + } + bsi->iCache <<= nBits; + bsi->cachedBits -= nBits; +} + +/************************************************************************************** + * Function: CalcBitsUsed + * + * Description: calculate how many bits have been read from bitstream + * + * Inputs: pointer to initialized BitStreamInfo struct + * pointer to start of bitstream buffer + * bit offset into first byte of startBuf (0-7) + * + * Outputs: none + * + * Return: number of bits read from bitstream, as offset from startBuf:startOffset + **************************************************************************************/ +int CalcBitsUsed(BitStreamInfo *bsi, unsigned char *startBuf, int startOffset) +{ + int bitsUsed; + + bitsUsed = (bsi->bytePtr - startBuf) * 8; + bitsUsed -= bsi->cachedBits; + bitsUsed -= startOffset; + + return bitsUsed; +} + +/************************************************************************************** + * Function: ByteAlignBitstream + * + * Description: bump bitstream pointer to start of next byte + * + * Inputs: pointer to initialized BitStreamInfo struct + * + * Outputs: byte-aligned bitstream BitStreamInfo struct + * + * Return: none + * + * Notes: if bitstream is already byte-aligned, do nothing + **************************************************************************************/ +void ByteAlignBitstream(BitStreamInfo *bsi) +{ + int offset; + + offset = bsi->cachedBits & 0x07; + AdvanceBitstream(bsi, offset); +} diff --git a/components/spotify/cspot/bell/libhelix-aac/bitstream.h b/components/spotify/cspot/bell/libhelix-aac/bitstream.h new file mode 100644 index 00000000..de68a989 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/bitstream.h @@ -0,0 +1,74 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: bitstream.h,v 1.1 2005/02/26 01:47:34 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * bitstream.h - definitions of bitstream handling functions + **************************************************************************************/ + +#ifndef _BITSTREAM_H +#define _BITSTREAM_H + +#include "aaccommon.h" + +/* additional external symbols to name-mangle for static linking */ +#define SetBitstreamPointer STATNAME(SetBitstreamPointer) +#define GetBits STATNAME(GetBits) +#define GetBitsNoAdvance STATNAME(GetBitsNoAdvance) +#define AdvanceBitstream STATNAME(AdvanceBitstream) +#define CalcBitsUsed STATNAME(CalcBitsUsed) +#define ByteAlignBitstream STATNAME(ByteAlignBitstream) + +typedef struct _BitStreamInfo { + unsigned char *bytePtr; + unsigned int iCache; + int cachedBits; + int nBytes; +} BitStreamInfo; + +/* bitstream.c */ +void SetBitstreamPointer(BitStreamInfo *bsi, int nBytes, unsigned char *buf); +unsigned int GetBits(BitStreamInfo *bsi, int nBits); +unsigned int GetBitsNoAdvance(BitStreamInfo *bsi, int nBits); +void AdvanceBitstream(BitStreamInfo *bsi, int nBits); +int CalcBitsUsed(BitStreamInfo *bsi, unsigned char *startBuf, int startOffset); +void ByteAlignBitstream(BitStreamInfo *bsi); + +#endif /* _BITSTREAM_H */ diff --git a/components/spotify/cspot/bell/libhelix-aac/buffers.c b/components/spotify/cspot/bell/libhelix-aac/buffers.c new file mode 100644 index 00000000..9ed39119 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/buffers.c @@ -0,0 +1,165 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: buffers.c,v 1.1 2005/02/26 01:47:34 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * buffers.c - allocation and deallocation of internal AAC decoder buffers + **************************************************************************************/ + +#if defined(USE_DEFAULT_STDLIB) || defined(ESP_PLATFORM) +#include +#else +#include "hlxclib/stdlib.h" +#endif + +#include "coder.h" + +/************************************************************************************** + * Function: ClearBuffer + * + * Description: fill buffer with 0's + * + * Inputs: pointer to buffer + * number of bytes to fill with 0 + * + * Outputs: cleared buffer + * + * Return: none + * + * Notes: slow, platform-independent equivalent to memset(buf, 0, nBytes) + **************************************************************************************/ +#include + void ClearBuffer(void *buf, int nBytes) +{ +/* int i; + unsigned char *cbuf = (unsigned char *)buf; + + for (i = 0; i < nBytes; i++) + cbuf[i] = 0; + + return; + */ + memset(buf, 0, nBytes); +} + +/************************************************************************************** + * Function: AllocateBuffers + * + * Description: allocate all the memory needed for the AAC decoder + * + * Inputs: none + * + * Outputs: none + * + * Return: pointer to AACDecInfo structure, cleared to all 0's (except for + * pointer to platform-specific data structure) + * + * Notes: if one or more mallocs fail, function frees any buffers already + * allocated before returning + **************************************************************************************/ +AACDecInfo *AllocateBuffers(void) +{ + AACDecInfo *aacDecInfo; + + aacDecInfo = (AACDecInfo *)malloc(sizeof(AACDecInfo)); + if (!aacDecInfo) + return 0; + ClearBuffer(aacDecInfo, sizeof(AACDecInfo)); + + aacDecInfo->psInfoBase = malloc(sizeof(PSInfoBase)); + if (!aacDecInfo->psInfoBase) { + FreeBuffers(aacDecInfo); + return 0; + } + ClearBuffer(aacDecInfo->psInfoBase, sizeof(PSInfoBase)); + + return aacDecInfo; +} + +AACDecInfo *AllocateBuffersPre(void **ptr, int *sz) +{ + AACDecInfo *aacDecInfo; + + char *p = (char*)*ptr; + aacDecInfo = (AACDecInfo *)p; + p += (sizeof(AACDecInfo) +7 ) & ~7; + *sz -= (sizeof(AACDecInfo) +7 ) & ~7; + if (*sz < 0) + return 0; + ClearBuffer(aacDecInfo, sizeof(AACDecInfo)); + + aacDecInfo->psInfoBase = (PSInfoBase*)p; + p += (sizeof(PSInfoBase) + 7) & ~7; + *sz -= (sizeof(PSInfoBase) + 7) & ~7; + if (*sz <0) { + return 0; + } + ClearBuffer(aacDecInfo->psInfoBase, sizeof(PSInfoBase)); + + *ptr = p; + + return aacDecInfo; +} + +#ifndef SAFE_FREE +#define SAFE_FREE(x) {if (x) free(x); (x) = 0;} /* helper macro */ +#endif + +/************************************************************************************** + * Function: FreeBuffers + * + * Description: frees all the memory used by the AAC decoder + * + * Inputs: pointer to initialized AACDecInfo structure + * + * Outputs: none + * + * Return: none + * + * Notes: safe to call even if some buffers were not allocated (uses SAFE_FREE) + **************************************************************************************/ +void FreeBuffers(AACDecInfo *aacDecInfo) +{ + if (!aacDecInfo) + return; + + SAFE_FREE(aacDecInfo->psInfoBase); + SAFE_FREE(aacDecInfo); +} diff --git a/components/spotify/cspot/bell/libhelix-aac/coder.h b/components/spotify/cspot/bell/libhelix-aac/coder.h new file mode 100644 index 00000000..9609e393 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/coder.h @@ -0,0 +1,373 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: coder.h,v 1.2 2005/06/27 21:06:00 gwright Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * coder.h - definitions of platform-specific data structures, functions, and tables + **************************************************************************************/ + +#ifndef _CODER_H +#define _CODER_H + +#include "aaccommon.h" +#include "bitstream.h" + +#ifndef ASSERT +#if defined(_WIN32) && defined(_M_IX86) && (defined (_DEBUG) || defined (REL_ENABLE_ASSERTS)) +#define ASSERT(x) if (!(x)) __asm int 3; +#else +#define ASSERT(x) /* do nothing */ +#endif +#endif + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#define NWINDOWS_LONG 1 +#define NWINDOWS_SHORT 8 + +#define DATA_BUF_SIZE 510 /* max count = 255 + 255 */ +#define FILL_BUF_SIZE 269 /* max count = 15 + 255 - 1*/ +#define ADIF_COPYID_SIZE 9 +#define MAX_COMMENT_BYTES 255 + +#define MAX_NUM_FCE 15 +#define MAX_NUM_SCE 15 +#define MAX_NUM_BCE 15 +#define MAX_NUM_LCE 3 +#define MAX_NUM_ADE 7 +#define MAX_NUM_CCE 15 + +#define CHAN_ELEM_IS_CPE(x) (((x) & 0x10) >> 4) /* bit 4 = SCE/CPE flag */ +#define CHAN_ELEM_GET_TAG(x) (((x) & 0x0f) >> 0) /* bits 3-0 = instance tag */ + +#define CHAN_ELEM_SET_CPE(x) (((x) & 0x01) << 4) /* bit 4 = SCE/CPE flag */ +#define CHAN_ELEM_SET_TAG(x) (((x) & 0x0f) << 0) /* bits 3-0 = instance tag */ + +#define MAX_HUFF_BITS 20 +#define HUFFTAB_SPEC_OFFSET 1 + +/* do y <<= n, clipping to range [-2^30, 2^30 - 1] (i.e. output has one guard bit) */ +/* +#define CLIP_2N_SHIFT(y, n) { \ + int sign = (y) >> 31; \ + if (sign != (y) >> (30 - (n))) { \ + (y) = sign ^ (0x3fffffff); \ + } else { \ + (y) = (y) << (n); \ + } \ + } +*/ +/* clip to [-2^n, 2^n-1], valid range of n = [1, 30] */ +/* +#define CLIP_2N(val, n) { \ + if ((val) >> 31 != (val) >> (n)) \ + (val) = ((val) >> 31) ^ ((1 << (n)) - 1); \ + } +*/ + + +#define SF_DQ_OFFSET 15 +#define FBITS_OUT_DQ 20 +#define FBITS_OUT_DQ_OFF (FBITS_OUT_DQ - SF_DQ_OFFSET) /* number of fraction bits out of dequant, including 2^15 bias */ + +#define FBITS_IN_IMDCT FBITS_OUT_DQ_OFF /* number of fraction bits into IMDCT */ +#define GBITS_IN_DCT4 4 /* min guard bits in for DCT4 */ + +#define FBITS_LOST_DCT4 1 /* number of fraction bits lost (>> out) in DCT-IV */ +#define FBITS_LOST_WND 1 /* number of fraction bits lost (>> out) in synthesis window (neg = gain frac bits) */ +#define FBITS_LOST_IMDCT (FBITS_LOST_DCT4 + FBITS_LOST_WND) +#define FBITS_OUT_IMDCT (FBITS_IN_IMDCT - FBITS_LOST_IMDCT) + +#define NUM_IMDCT_SIZES 2 + +/* additional external symbols to name-mangle for static linking */ +#define DecodeProgramConfigElement STATNAME(DecodeProgramConfigElement) +#define DecodeHuffmanScalar STATNAME(DecodeHuffmanScalar) +#define DecodeSpectrumLong STATNAME(DecodeSpectrumLong) +#define DecodeSpectrumShort STATNAME(DecodeSpectrumShort) +#define DecodeICSInfo STATNAME(DecodeICSInfo) +#define DCT4 STATNAME(DCT4) +#define R4FFT STATNAME(R4FFT) + +#define DecWindowOverlapNoClip STATNAME(DecWindowOverlapNoClip) +#define DecWindowOverlapLongStartNoClip STATNAME(DecWindowOverlapLongStartNoClip) +#define DecWindowOverlapLongStopNoClip STATNAME(DecWindowOverlapLongStopNoClip) +#define DecWindowOverlapShortNoClip STATNAME(DecWindowOverlapShortNoClip) + +#define huffTabSpecInfo STATNAME(huffTabSpecInfo) +#define huffTabSpec STATNAME(huffTabSpec) +#define huffTabScaleFactInfo STATNAME(huffTabScaleFactInfo) +#define huffTabScaleFact STATNAME(huffTabScaleFact) +#define cos4sin4tab STATNAME(cos4sin4tab) +#define cos4sin4tabOffset STATNAME(cos4sin4tabOffset) +#define cos1sin1tab STATNAME(cos1sin1tab) +#define sinWindow STATNAME(sinWindow) +#define sinWindowOffset STATNAME(sinWindowOffset) +#define kbdWindow STATNAME(kbdWindow) +#define kbdWindowOffset STATNAME(kbdWindowOffset) +#define bitrevtab STATNAME(bitrevtab) +#define bitrevtabOffset STATNAME(bitrevtabOffset) +#define uniqueIDTab STATNAME(uniqueIDTab) +#define twidTabEven STATNAME(twidTabEven) +#define twidTabOdd STATNAME(twidTabOdd) + +typedef struct _HuffInfo { + int maxBits; /* number of bits in longest codeword */ + unsigned /*char*/ int count[MAX_HUFF_BITS]; /* count[i] = number of codes with length i+1 bits */ + int offset; /* offset into symbol table */ +} HuffInfo; + +typedef struct _PulseInfo { + unsigned char pulseDataPresent; + unsigned char numPulse; + unsigned char startSFB; + unsigned char offset[MAX_PULSES]; + unsigned char amp[MAX_PULSES]; +} PulseInfo; + +typedef struct _TNSInfo { + unsigned char tnsDataPresent; + unsigned char numFilt[MAX_TNS_FILTERS]; /* max 1 filter each for 8 short windows, or 3 filters for 1 long window */ + unsigned char coefRes[MAX_TNS_FILTERS]; + unsigned char length[MAX_TNS_FILTERS]; + unsigned char order[MAX_TNS_FILTERS]; + unsigned char dir[MAX_TNS_FILTERS]; + signed char coef[MAX_TNS_COEFS]; /* max 3 filters * 20 coefs for 1 long window, or 1 filter * 7 coefs for each of 8 short windows */ +} TNSInfo; + +typedef struct _GainControlInfo { + unsigned char gainControlDataPresent; + unsigned char maxBand; + unsigned char adjNum[MAX_GAIN_BANDS][MAX_GAIN_WIN]; + unsigned char alevCode[MAX_GAIN_BANDS][MAX_GAIN_WIN][MAX_GAIN_ADJUST]; + unsigned char alocCode[MAX_GAIN_BANDS][MAX_GAIN_WIN][MAX_GAIN_ADJUST]; +} GainControlInfo; + +typedef struct _ICSInfo { + unsigned char icsResBit; + unsigned char winSequence; + unsigned char winShape; + unsigned char maxSFB; + unsigned char sfGroup; + unsigned char predictorDataPresent; + unsigned char predictorReset; + unsigned char predictorResetGroupNum; + unsigned char predictionUsed[MAX_PRED_SFB]; + unsigned char numWinGroup; + unsigned char winGroupLen[MAX_WIN_GROUPS]; +} ICSInfo; + +typedef struct _ADTSHeader { + /* fixed */ + unsigned char id; /* MPEG bit - should be 1 */ + unsigned char layer; /* MPEG layer - should be 0 */ + unsigned char protectBit; /* 0 = CRC word follows, 1 = no CRC word */ + unsigned char profile; /* 0 = main, 1 = LC, 2 = SSR, 3 = reserved */ + unsigned char sampRateIdx; /* sample rate index range = [0, 11] */ + unsigned char privateBit; /* ignore */ + unsigned char channelConfig; /* 0 = implicit, >0 = use default table */ + unsigned char origCopy; /* 0 = copy, 1 = original */ + unsigned char home; /* ignore */ + + /* variable */ + unsigned char copyBit; /* 1 bit of the 72-bit copyright ID (transmitted as 1 bit per frame) */ + unsigned char copyStart; /* 1 = this bit starts the 72-bit ID, 0 = it does not */ + int frameLength; /* length of frame */ + int bufferFull; /* number of 32-bit words left in enc buffer, 0x7FF = VBR */ + unsigned char numRawDataBlocks; /* number of raw data blocks in frame */ + + /* CRC */ + int crcCheckWord; /* 16-bit CRC check word (present if protectBit == 0) */ +} ADTSHeader; + +typedef struct _ADIFHeader { + unsigned char copyBit; /* 0 = no copyright ID, 1 = 72-bit copyright ID follows immediately */ + unsigned char origCopy; /* 0 = copy, 1 = original */ + unsigned char home; /* ignore */ + unsigned char bsType; /* bitstream type: 0 = CBR, 1 = VBR */ + int bitRate; /* bitRate: CBR = bits/sec, VBR = peak bits/frame, 0 = unknown */ + unsigned char numPCE; /* number of program config elements (max = 16) */ + int bufferFull; /* bits left in bit reservoir */ + unsigned char copyID[ADIF_COPYID_SIZE]; /* optional 72-bit copyright ID */ +} ADIFHeader; + +/* sizeof(ProgConfigElement) = 82 bytes (if KEEP_PCE_COMMENTS not defined) */ +typedef struct _ProgConfigElement { + unsigned char elemInstTag; /* element instance tag */ + unsigned char profile; /* 0 = main, 1 = LC, 2 = SSR, 3 = reserved */ + unsigned char sampRateIdx; /* sample rate index range = [0, 11] */ + unsigned char numFCE; /* number of front channel elements (max = 15) */ + unsigned char numSCE; /* number of side channel elements (max = 15) */ + unsigned char numBCE; /* number of back channel elements (max = 15) */ + unsigned char numLCE; /* number of LFE channel elements (max = 3) */ + unsigned char numADE; /* number of associated data elements (max = 7) */ + unsigned char numCCE; /* number of valid channel coupling elements (max = 15) */ + unsigned char monoMixdown; /* mono mixdown: bit 4 = present flag, bits 3-0 = element number */ + unsigned char stereoMixdown; /* stereo mixdown: bit 4 = present flag, bits 3-0 = element number */ + unsigned char matrixMixdown; /* matrix mixdown: bit 4 = present flag, bit 3 = unused, + bits 2-1 = index, bit 0 = pseudo-surround enable */ + unsigned char fce[MAX_NUM_FCE]; /* front element channel pair: bit 4 = SCE/CPE flag, bits 3-0 = inst tag */ + unsigned char sce[MAX_NUM_SCE]; /* side element channel pair: bit 4 = SCE/CPE flag, bits 3-0 = inst tag */ + unsigned char bce[MAX_NUM_BCE]; /* back element channel pair: bit 4 = SCE/CPE flag, bits 3-0 = inst tag */ + unsigned char lce[MAX_NUM_LCE]; /* instance tag for LFE elements */ + unsigned char ade[MAX_NUM_ADE]; /* instance tag for ADE elements */ + unsigned char cce[MAX_NUM_BCE]; /* channel coupling elements: bit 4 = switching flag, bits 3-0 = inst tag */ + +#ifdef KEEP_PCE_COMMENTS + /* make this optional - if not enabled, decoder will just skip comments */ + unsigned char commentBytes; + unsigned char commentField[MAX_COMMENT_BYTES]; +#endif + +} ProgConfigElement; + +/* state info struct for baseline (MPEG-4 LC) decoding */ +typedef struct _PSInfoBase { + /* header information */ + ADTSHeader fhADTS; + ADIFHeader fhADIF; + ProgConfigElement pce[MAX_NUM_PCE_ADIF]; + int dataCount; + unsigned char dataBuf[DATA_BUF_SIZE]; + int fillCount; + unsigned char fillBuf[FILL_BUF_SIZE]; + + /* state information which is the same throughout whole frame */ + int nChans; + int useImpChanMap; + int sampRateIdx; + + /* state information which can be overwritten by subsequent elements within frame */ + ICSInfo icsInfo[MAX_NCHANS_ELEM]; + + int commonWin; + short scaleFactors[MAX_NCHANS_ELEM][MAX_SF_BANDS]; + unsigned char sfbCodeBook[MAX_NCHANS_ELEM][MAX_SF_BANDS]; + + int msMaskPresent; + unsigned char msMaskBits[MAX_MS_MASK_BYTES]; + + int pnsUsed[MAX_NCHANS_ELEM]; + int pnsLastVal; + int intensityUsed[MAX_NCHANS_ELEM]; + + PulseInfo pulseInfo[MAX_NCHANS_ELEM]; + + TNSInfo tnsInfo[MAX_NCHANS_ELEM]; + int tnsLPCBuf[MAX_TNS_ORDER]; + int tnsWorkBuf[MAX_TNS_ORDER]; + + GainControlInfo gainControlInfo[MAX_NCHANS_ELEM]; + + int gbCurrent[MAX_NCHANS_ELEM]; + int coef[MAX_NCHANS_ELEM][AAC_MAX_NSAMPS]; +#ifdef AAC_ENABLE_SBR + int sbrWorkBuf[MAX_NCHANS_ELEM][AAC_MAX_NSAMPS]; +#endif + /* state information which must be saved for each element and used in next frame */ + int overlap[AAC_MAX_NCHANS][AAC_MAX_NSAMPS]; + int prevWinShape[AAC_MAX_NCHANS]; + +} PSInfoBase; + +/* private implementation-specific functions */ + +/* decelmnt.c */ +int DecodeProgramConfigElement(ProgConfigElement *pce, BitStreamInfo *bsi); + +/* huffman.c */ +int DecodeHuffmanScalar(const signed short *huffTab, const HuffInfo *huffTabInfo, unsigned int bitBuf, signed int *val); +void DecodeSpectrumLong(PSInfoBase *psi, BitStreamInfo *bsi, int ch); +void DecodeSpectrumShort(PSInfoBase *psi, BitStreamInfo *bsi, int ch); + +/* noiseless.c */ +void DecodeICSInfo(BitStreamInfo *bsi, ICSInfo *icsInfo, int sampRateIdx); + +/* dct4.c */ +void DCT4(int tabidx, int *coef, int gb); + +/* fft.c */ +void R4FFT(int tabidx, int *x); + +/* sbrimdct.c */ +void DecWindowOverlapNoClip(int *buf0, int *over0, int *out0, int winTypeCurr, int winTypePrev); +void DecWindowOverlapLongStartNoClip(int *buf0, int *over0, int *out0, int winTypeCurr, int winTypePrev); +void DecWindowOverlapLongStopNoClip(int *buf0, int *over0, int *out0, int winTypeCurr, int winTypePrev); +void DecWindowOverlapShortNoClip(int *buf0, int *over0, int *out0, int winTypeCurr, int winTypePrev); + +/* hufftabs.c */ +extern const HuffInfo huffTabSpecInfo[11]; +extern const signed short huffTabSpec[1241]; +extern const HuffInfo huffTabScaleFactInfo; +extern const signed short huffTabScaleFact[121]; + +/* trigtabs.c */ +extern const int cos4sin4tabOffset[NUM_IMDCT_SIZES]; +extern const int sinWindowOffset[NUM_IMDCT_SIZES]; +extern const int kbdWindowOffset[NUM_IMDCT_SIZES]; +extern const unsigned char bitrevtab[17 + 129]; +extern const int bitrevtabOffset[NUM_IMDCT_SIZES]; + +#ifdef HELIX_CONFIG_AAC_GENERATE_TRIGTABS_FLOAT +/* trigtabs_fltgen.c */ +extern int cos4sin4tab[128 + 1024]; +extern int cos1sin1tab[514]; +extern int sinWindow[128 + 1024]; +extern int kbdWindow[128 + 1024]; +extern int twidTabEven[4*6 + 16*6 + 64*6]; +extern int twidTabOdd[8*6 + 32*6 + 128*6]; +#else +/* trigtabs.c */ +extern const int cos4sin4tab[128 + 1024]; +extern const int cos1sin1tab[514]; +extern const int sinWindow[128 + 1024]; +extern const int kbdWindow[128 + 1024]; +extern const int twidTabEven[4*6 + 16*6 + 64*6]; +extern const int twidTabOdd[8*6 + 32*6 + 128*6]; +#endif + +#endif /* _CODER_H */ + diff --git a/components/spotify/cspot/bell/libhelix-aac/dct4.c b/components/spotify/cspot/bell/libhelix-aac/dct4.c new file mode 100644 index 00000000..698f54b6 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/dct4.c @@ -0,0 +1,337 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: dct4.c,v 1.1 2005/02/26 01:47:34 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * February 2005 + * + * dct4.c - optimized DCT-IV + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +static const int nmdctTab[NUM_IMDCT_SIZES] PROGMEM = {128, 1024}; +static const int postSkip[NUM_IMDCT_SIZES] PROGMEM = {15, 1}; + +/************************************************************************************** + * Function: PreMultiply + * + * Description: pre-twiddle stage of DCT4 + * + * Inputs: table index (for transform size) + * buffer of nmdct samples + * + * Outputs: processed samples in same buffer + * + * Return: none + * + * Notes: minimum 1 GB in, 2 GB out, gains 5 (short) or 8 (long) frac bits + * i.e. gains 2-7= -5 int bits (short) or 2-10 = -8 int bits (long) + * normalization by -1/N is rolled into tables here (see trigtabs.c) + * uses 3-mul, 3-add butterflies instead of 4-mul, 2-add + **************************************************************************************/ +static void PreMultiply(int tabidx, int *zbuf1) +{ + int i, nmdct, ar1, ai1, ar2, ai2, z1, z2; + int t, cms2, cps2a, sin2a, cps2b, sin2b; + int *zbuf2; + const int *csptr; + + nmdct = nmdctTab[tabidx]; + zbuf2 = zbuf1 + nmdct - 1; + csptr = cos4sin4tab + cos4sin4tabOffset[tabidx]; + + /* whole thing should fit in registers - verify that compiler does this */ + for (i = nmdct >> 2; i != 0; i--) { + /* cps2 = (cos+sin), sin2 = sin, cms2 = (cos-sin) */ + cps2a = *csptr++; + sin2a = *csptr++; + cps2b = *csptr++; + sin2b = *csptr++; + + ar1 = *(zbuf1 + 0); + ai2 = *(zbuf1 + 1); + ai1 = *(zbuf2 + 0); + ar2 = *(zbuf2 - 1); + + /* gain 2 ints bit from MULSHIFT32 by Q30, but drop 7 or 10 int bits from table scaling of 1/M + * max per-sample gain (ignoring implicit scaling) = MAX(sin(angle)+cos(angle)) = 1.414 + * i.e. gain 1 GB since worst case is sin(angle) = cos(angle) = 0.707 (Q30), gain 2 from + * extra sign bits, and eat one in adding + */ + t = MULSHIFT32(sin2a, ar1 + ai1); + z2 = MULSHIFT32(cps2a, ai1) - t; + cms2 = cps2a - 2*sin2a; + z1 = MULSHIFT32(cms2, ar1) + t; + *zbuf1++ = z1; /* cos*ar1 + sin*ai1 */ + *zbuf1++ = z2; /* cos*ai1 - sin*ar1 */ + + t = MULSHIFT32(sin2b, ar2 + ai2); + z2 = MULSHIFT32(cps2b, ai2) - t; + cms2 = cps2b - 2*sin2b; + z1 = MULSHIFT32(cms2, ar2) + t; + *zbuf2-- = z2; /* cos*ai2 - sin*ar2 */ + *zbuf2-- = z1; /* cos*ar2 + sin*ai2 */ + } +} + +/************************************************************************************** + * Function: PostMultiply + * + * Description: post-twiddle stage of DCT4 + * + * Inputs: table index (for transform size) + * buffer of nmdct samples + * + * Outputs: processed samples in same buffer + * + * Return: none + * + * Notes: minimum 1 GB in, 2 GB out - gains 2 int bits + * uses 3-mul, 3-add butterflies instead of 4-mul, 2-add + **************************************************************************************/ +static void PostMultiply(int tabidx, int *fft1) +{ + int i, nmdct, ar1, ai1, ar2, ai2, skipFactor; + int t, cms2, cps2, sin2; + int *fft2; + const int *csptr; + + nmdct = nmdctTab[tabidx]; + csptr = cos1sin1tab; + skipFactor = postSkip[tabidx]; + fft2 = fft1 + nmdct - 1; + + /* load coeffs for first pass + * cps2 = (cos+sin), sin2 = sin, cms2 = (cos-sin) + */ + cps2 = *csptr++; + sin2 = *csptr; + csptr += skipFactor; + cms2 = cps2 - 2*sin2; + + for (i = nmdct >> 2; i != 0; i--) { + ar1 = *(fft1 + 0); + ai1 = *(fft1 + 1); + ar2 = *(fft2 - 1); + ai2 = *(fft2 + 0); + + /* gain 2 ints bit from MULSHIFT32 by Q30 + * max per-sample gain = MAX(sin(angle)+cos(angle)) = 1.414 + * i.e. gain 1 GB since worst case is sin(angle) = cos(angle) = 0.707 (Q30), gain 2 from + * extra sign bits, and eat one in adding + */ + t = MULSHIFT32(sin2, ar1 + ai1); + *fft2-- = t - MULSHIFT32(cps2, ai1); /* sin*ar1 - cos*ai1 */ + *fft1++ = t + MULSHIFT32(cms2, ar1); /* cos*ar1 + sin*ai1 */ + cps2 = *csptr++; + sin2 = *csptr; + csptr += skipFactor; + + ai2 = -ai2; + t = MULSHIFT32(sin2, ar2 + ai2); + *fft2-- = t - MULSHIFT32(cps2, ai2); /* sin*ar1 - cos*ai1 */ + cms2 = cps2 - 2*sin2; + *fft1++ = t + MULSHIFT32(cms2, ar2); /* cos*ar1 + sin*ai1 */ + } +} + +/************************************************************************************** + * Function: PreMultiplyRescale + * + * Description: pre-twiddle stage of DCT4, with rescaling for extra guard bits + * + * Inputs: table index (for transform size) + * buffer of nmdct samples + * number of guard bits to add to input before processing + * + * Outputs: processed samples in same buffer + * + * Return: none + * + * Notes: see notes on PreMultiply(), above + **************************************************************************************/ + /* __attribute__ ((section (".data"))) */ static void PreMultiplyRescale(int tabidx, int *zbuf1, int es) +{ + int i, nmdct, ar1, ai1, ar2, ai2, z1, z2; + int t, cms2, cps2a, sin2a, cps2b, sin2b; + int *zbuf2; + const int *csptr; + + nmdct = nmdctTab[tabidx]; + zbuf2 = zbuf1 + nmdct - 1; + csptr = cos4sin4tab + cos4sin4tabOffset[tabidx]; + + /* whole thing should fit in registers - verify that compiler does this */ + for (i = nmdct >> 2; i != 0; i--) { + /* cps2 = (cos+sin), sin2 = sin, cms2 = (cos-sin) */ + cps2a = *csptr++; + sin2a = *csptr++; + cps2b = *csptr++; + sin2b = *csptr++; + + ar1 = *(zbuf1 + 0) >> es; + ai1 = *(zbuf2 + 0) >> es; + ai2 = *(zbuf1 + 1) >> es; + + t = MULSHIFT32(sin2a, ar1 + ai1); + z2 = MULSHIFT32(cps2a, ai1) - t; + cms2 = cps2a - 2*sin2a; + z1 = MULSHIFT32(cms2, ar1) + t; + *zbuf1++ = z1; + *zbuf1++ = z2; + + ar2 = *(zbuf2 - 1) >> es; /* do here to free up register used for es */ + + t = MULSHIFT32(sin2b, ar2 + ai2); + z2 = MULSHIFT32(cps2b, ai2) - t; + cms2 = cps2b - 2*sin2b; + z1 = MULSHIFT32(cms2, ar2) + t; + *zbuf2-- = z2; + *zbuf2-- = z1; + + } +} + +/************************************************************************************** + * Function: PostMultiplyRescale + * + * Description: post-twiddle stage of DCT4, with rescaling for extra guard bits + * + * Inputs: table index (for transform size) + * buffer of nmdct samples + * number of guard bits to remove from output + * + * Outputs: processed samples in same buffer + * + * Return: none + * + * Notes: clips output to [-2^30, 2^30 - 1], guaranteeing at least 1 guard bit + * see notes on PostMultiply(), above + **************************************************************************************/ + /* __attribute__ ((section (".data"))) */ static void PostMultiplyRescale(int tabidx, int *fft1, int es) +{ + int i, nmdct, ar1, ai1, ar2, ai2, skipFactor, z; + int t, cs2, sin2; + int *fft2; + const int *csptr; + + nmdct = nmdctTab[tabidx]; + csptr = cos1sin1tab; + skipFactor = postSkip[tabidx]; + fft2 = fft1 + nmdct - 1; + + /* load coeffs for first pass + * cps2 = (cos+sin), sin2 = sin, cms2 = (cos-sin) + */ + cs2 = *csptr++; + sin2 = *csptr; + csptr += skipFactor; + + for (i = nmdct >> 2; i != 0; i--) { + ar1 = *(fft1 + 0); + ai1 = *(fft1 + 1); + ai2 = *(fft2 + 0); + + t = MULSHIFT32(sin2, ar1 + ai1); + z = t - MULSHIFT32(cs2, ai1); + CLIP_2N_SHIFT(z, es); + *fft2-- = z; + cs2 -= 2*sin2; + z = t + MULSHIFT32(cs2, ar1); + CLIP_2N_SHIFT(z, es); + *fft1++ = z; + + cs2 = *csptr++; + sin2 = *csptr; + csptr += skipFactor; + + ar2 = *fft2; + ai2 = -ai2; + t = MULSHIFT32(sin2, ar2 + ai2); + z = t - MULSHIFT32(cs2, ai2); + CLIP_2N_SHIFT(z, es); + *fft2-- = z; + cs2 -= 2*sin2; + z = t + MULSHIFT32(cs2, ar2); + CLIP_2N_SHIFT(z, es); + *fft1++ = z; + cs2 += 2*sin2; + } +} + +/************************************************************************************** + * Function: DCT4 + * + * Description: type-IV DCT + * + * Inputs: table index (for transform size) + * buffer of nmdct samples + * number of guard bits in the input buffer + * + * Outputs: processed samples in same buffer + * + * Return: none + * + * Notes: operates in-place + * if number of guard bits in input is < GBITS_IN_DCT4, the input is + * scaled (>>) before the DCT4 and rescaled (<<, with clipping) after + * the DCT4 (rare) + * the output has FBITS_LOST_DCT4 fewer fraction bits than the input + * the output will always have at least 1 guard bit (GBITS_IN_DCT4 >= 4) + * int bits gained per stage (PreMul + FFT + PostMul) + * short blocks = (-5 + 4 + 2) = 1 total + * long blocks = (-8 + 7 + 2) = 1 total + **************************************************************************************/ +void DCT4(int tabidx, int *coef, int gb) +{ + int es; + + /* fast in-place DCT-IV - adds guard bits if necessary */ + if (gb < GBITS_IN_DCT4) { + es = GBITS_IN_DCT4 - gb; + PreMultiplyRescale(tabidx, coef, es); + R4FFT(tabidx, coef); + PostMultiplyRescale(tabidx, coef, es); + } else { + PreMultiply(tabidx, coef); + R4FFT(tabidx, coef); + PostMultiply(tabidx, coef); + } +} diff --git a/components/spotify/cspot/bell/libhelix-aac/decelmnt.c b/components/spotify/cspot/bell/libhelix-aac/decelmnt.c new file mode 100644 index 00000000..8c319121 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/decelmnt.c @@ -0,0 +1,425 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: decelmnt.c,v 1.1 2005/02/26 01:47:34 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * decelmnt.c - syntactic element decoding + **************************************************************************************/ + +#include "coder.h" + +/************************************************************************************** + * Function: DecodeSingleChannelElement + * + * Description: decode one SCE + * + * Inputs: BitStreamInfo struct pointing to start of SCE (14496-3, table 4.4.4) + * + * Outputs: updated element instance tag + * + * Return: 0 if successful, -1 if error + * + * Notes: doesn't decode individual channel stream (part of DecodeNoiselessData) + **************************************************************************************/ +static int DecodeSingleChannelElement(AACDecInfo *aacDecInfo, BitStreamInfo *bsi) +{ + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoBase) + return -1; + + /* read instance tag */ + aacDecInfo->currInstTag = GetBits(bsi, NUM_INST_TAG_BITS); + + return 0; +} + +/************************************************************************************** + * Function: DecodeChannelPairElement + * + * Description: decode one CPE + * + * Inputs: BitStreamInfo struct pointing to start of CPE (14496-3, table 4.4.5) + * + * Outputs: updated element instance tag + * updated commonWin + * updated ICS info, if commonWin == 1 + * updated mid-side stereo info, if commonWin == 1 + * + * Return: 0 if successful, -1 if error + * + * Notes: doesn't decode individual channel stream (part of DecodeNoiselessData) + **************************************************************************************/ +static int DecodeChannelPairElement(AACDecInfo *aacDecInfo, BitStreamInfo *bsi) +{ + int sfb, gp, maskOffset; + unsigned char currBit, *maskPtr; + PSInfoBase *psi; + ICSInfo *icsInfo; + + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoBase) + return -1; + psi = (PSInfoBase *)(aacDecInfo->psInfoBase); + icsInfo = psi->icsInfo; + + /* read instance tag */ + aacDecInfo->currInstTag = GetBits(bsi, NUM_INST_TAG_BITS); + + /* read common window flag and mid-side info (if present) + * store msMask bits in psi->msMaskBits[] as follows: + * long blocks - pack bits for each SFB in range [0, maxSFB) starting with lsb of msMaskBits[0] + * short blocks - pack bits for each SFB in range [0, maxSFB), for each group [0, 7] + * msMaskPresent = 0 means no M/S coding + * = 1 means psi->msMaskBits contains 1 bit per SFB to toggle M/S coding + * = 2 means all SFB's are M/S coded (so psi->msMaskBits is not needed) + */ + psi->commonWin = GetBits(bsi, 1); + if (psi->commonWin) { + DecodeICSInfo(bsi, icsInfo, psi->sampRateIdx); + psi->msMaskPresent = GetBits(bsi, 2); + if (psi->msMaskPresent == 1) { + maskPtr = psi->msMaskBits; + *maskPtr = 0; + maskOffset = 0; + for (gp = 0; gp < icsInfo->numWinGroup; gp++) { + for (sfb = 0; sfb < icsInfo->maxSFB; sfb++) { + currBit = (unsigned char)GetBits(bsi, 1); + *maskPtr |= currBit << maskOffset; + if (++maskOffset == 8) { + maskPtr++; + *maskPtr = 0; + maskOffset = 0; + } + } + } + } + } + + return 0; +} + +/************************************************************************************** + * Function: DecodeLFEChannelElement + * + * Description: decode one LFE + * + * Inputs: BitStreamInfo struct pointing to start of LFE (14496-3, table 4.4.9) + * + * Outputs: updated element instance tag + * + * Return: 0 if successful, -1 if error + * + * Notes: doesn't decode individual channel stream (part of DecodeNoiselessData) + **************************************************************************************/ +static int DecodeLFEChannelElement(AACDecInfo *aacDecInfo, BitStreamInfo *bsi) +{ + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoBase) + return -1; + + /* read instance tag */ + aacDecInfo->currInstTag = GetBits(bsi, NUM_INST_TAG_BITS); + + return 0; +} + +/************************************************************************************** + * Function: DecodeDataStreamElement + * + * Description: decode one DSE + * + * Inputs: BitStreamInfo struct pointing to start of DSE (14496-3, table 4.4.10) + * + * Outputs: updated element instance tag + * filled in data stream buffer + * + * Return: 0 if successful, -1 if error + **************************************************************************************/ +static int DecodeDataStreamElement(AACDecInfo *aacDecInfo, BitStreamInfo *bsi) +{ + unsigned int byteAlign, dataCount; + unsigned char *dataBuf; + PSInfoBase *psi; + + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoBase) + return -1; + psi = (PSInfoBase *)(aacDecInfo->psInfoBase); + + aacDecInfo->currInstTag = GetBits(bsi, NUM_INST_TAG_BITS); + byteAlign = GetBits(bsi, 1); + dataCount = GetBits(bsi, 8); + if (dataCount == 255) + dataCount += GetBits(bsi, 8); + + if (byteAlign) + ByteAlignBitstream(bsi); + + psi->dataCount = dataCount; + dataBuf = psi->dataBuf; + while (dataCount--) + *dataBuf++ = GetBits(bsi, 8); + + return 0; +} + +/************************************************************************************** + * Function: DecodeProgramConfigElement + * + * Description: decode one PCE + * + * Inputs: BitStreamInfo struct pointing to start of PCE (14496-3, table 4.4.2) + * + * Outputs: filled-in ProgConfigElement struct + * updated BitStreamInfo struct + * + * Return: 0 if successful, error code (< 0) if error + * + * Notes: #define KEEP_PCE_COMMENTS to save the comment field of the PCE + * (otherwise we just skip it in the bitstream, to save memory) + **************************************************************************************/ +int DecodeProgramConfigElement(ProgConfigElement *pce, BitStreamInfo *bsi) +{ + int i; + + pce->elemInstTag = GetBits(bsi, 4); + pce->profile = GetBits(bsi, 2); + pce->sampRateIdx = GetBits(bsi, 4); + pce->numFCE = GetBits(bsi, 4); + pce->numSCE = GetBits(bsi, 4); + pce->numBCE = GetBits(bsi, 4); + pce->numLCE = GetBits(bsi, 2); + pce->numADE = GetBits(bsi, 3); + pce->numCCE = GetBits(bsi, 4); + + pce->monoMixdown = GetBits(bsi, 1) << 4; /* present flag */ + if (pce->monoMixdown) + pce->monoMixdown |= GetBits(bsi, 4); /* element number */ + + pce->stereoMixdown = GetBits(bsi, 1) << 4; /* present flag */ + if (pce->stereoMixdown) + pce->stereoMixdown |= GetBits(bsi, 4); /* element number */ + + pce->matrixMixdown = GetBits(bsi, 1) << 4; /* present flag */ + if (pce->matrixMixdown) { + pce->matrixMixdown |= GetBits(bsi, 2) << 1; /* index */ + pce->matrixMixdown |= GetBits(bsi, 1); /* pseudo-surround enable */ + } + + for (i = 0; i < pce->numFCE; i++) { + pce->fce[i] = GetBits(bsi, 1) << 4; /* is_cpe flag */ + pce->fce[i] |= GetBits(bsi, 4); /* tag select */ + } + + for (i = 0; i < pce->numSCE; i++) { + pce->sce[i] = GetBits(bsi, 1) << 4; /* is_cpe flag */ + pce->sce[i] |= GetBits(bsi, 4); /* tag select */ + } + + for (i = 0; i < pce->numBCE; i++) { + pce->bce[i] = GetBits(bsi, 1) << 4; /* is_cpe flag */ + pce->bce[i] |= GetBits(bsi, 4); /* tag select */ + } + + for (i = 0; i < pce->numLCE; i++) + pce->lce[i] = GetBits(bsi, 4); /* tag select */ + + for (i = 0; i < pce->numADE; i++) + pce->ade[i] = GetBits(bsi, 4); /* tag select */ + + for (i = 0; i < pce->numCCE; i++) { + pce->cce[i] = GetBits(bsi, 1) << 4; /* independent/dependent flag */ + pce->cce[i] |= GetBits(bsi, 4); /* tag select */ + } + + + ByteAlignBitstream(bsi); + +#ifdef KEEP_PCE_COMMENTS + pce->commentBytes = GetBits(bsi, 8); + for (i = 0; i < pce->commentBytes; i++) + pce->commentField[i] = GetBits(bsi, 8); +#else + /* eat comment bytes and throw away */ + i = GetBits(bsi, 8); + while (i--) + GetBits(bsi, 8); +#endif + + return 0; +} + +/************************************************************************************** + * Function: DecodeFillElement + * + * Description: decode one fill element + * + * Inputs: BitStreamInfo struct pointing to start of fill element + * (14496-3, table 4.4.11) + * + * Outputs: updated element instance tag + * unpacked extension payload + * + * Return: 0 if successful, -1 if error + **************************************************************************************/ +static int DecodeFillElement(AACDecInfo *aacDecInfo, BitStreamInfo *bsi) +{ + unsigned int fillCount; + unsigned char *fillBuf; + PSInfoBase *psi; + + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoBase) + return -1; + psi = (PSInfoBase *)(aacDecInfo->psInfoBase); + + fillCount = GetBits(bsi, 4); + if (fillCount == 15) + fillCount += (GetBits(bsi, 8) - 1); + + psi->fillCount = fillCount; + fillBuf = psi->fillBuf; + while (fillCount--) + *fillBuf++ = GetBits(bsi, 8); + + aacDecInfo->currInstTag = -1; /* fill elements don't have instance tag */ + aacDecInfo->fillExtType = 0; + +#ifdef AAC_ENABLE_SBR + /* check for SBR + * aacDecInfo->sbrEnabled is sticky (reset each raw_data_block), so for multichannel + * need to verify that all SCE/CPE/ICCE have valid SBR fill element following, and + * must upsample by 2 for LFE + */ + if (psi->fillCount > 0) { + aacDecInfo->fillExtType = (int)((psi->fillBuf[0] >> 4) & 0x0f); + if (aacDecInfo->fillExtType == EXT_SBR_DATA || aacDecInfo->fillExtType == EXT_SBR_DATA_CRC) + aacDecInfo->sbrEnabled = 1; + } +#endif + + aacDecInfo->fillBuf = psi->fillBuf; + aacDecInfo->fillCount = psi->fillCount; + + return 0; +} + +/************************************************************************************** + * Function: DecodeNextElement + * + * Description: decode next syntactic element in AAC frame + * + * Inputs: valid AACDecInfo struct + * double pointer to buffer containing next element + * pointer to bit offset + * pointer to number of valid bits remaining in buf + * + * Outputs: type of element decoded (aacDecInfo->currBlockID) + * type of element decoded last time (aacDecInfo->prevBlockID) + * updated aacDecInfo state, depending on which element was decoded + * updated buffer pointer + * updated bit offset + * updated number of available bits + * + * Return: 0 if successful, error code (< 0) if error + **************************************************************************************/ +int DecodeNextElement(AACDecInfo *aacDecInfo, unsigned char **buf, int *bitOffset, int *bitsAvail) +{ + int err, bitsUsed; + PSInfoBase *psi; + BitStreamInfo bsi; + + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoBase) + return ERR_AAC_NULL_POINTER; + psi = (PSInfoBase *)(aacDecInfo->psInfoBase); + + /* init bitstream reader */ + SetBitstreamPointer(&bsi, (*bitsAvail + 7) >> 3, *buf); + GetBits(&bsi, *bitOffset); + + /* read element ID (save last ID for SBR purposes) */ + aacDecInfo->prevBlockID = aacDecInfo->currBlockID; + aacDecInfo->currBlockID = GetBits(&bsi, NUM_SYN_ID_BITS); + + /* set defaults (could be overwritten by DecodeXXXElement(), depending on currBlockID) */ + psi->commonWin = 0; + + err = 0; + switch (aacDecInfo->currBlockID) { + case AAC_ID_SCE: + err = DecodeSingleChannelElement(aacDecInfo, &bsi); + break; + case AAC_ID_CPE: + err = DecodeChannelPairElement(aacDecInfo, &bsi); + break; + case AAC_ID_CCE: + /* TODO - implement CCE decoding */ + break; + case AAC_ID_LFE: + err = DecodeLFEChannelElement(aacDecInfo, &bsi); + break; + case AAC_ID_DSE: + err = DecodeDataStreamElement(aacDecInfo, &bsi); + break; + case AAC_ID_PCE: + err = DecodeProgramConfigElement(psi->pce + 0, &bsi); + break; + case AAC_ID_FIL: + err = DecodeFillElement(aacDecInfo, &bsi); + break; + case AAC_ID_END: + break; + } + if (err) + return ERR_AAC_SYNTAX_ELEMENT; + + /* update bitstream reader */ + bitsUsed = CalcBitsUsed(&bsi, *buf, *bitOffset); + *buf += (bitsUsed + *bitOffset) >> 3; + *bitOffset = (bitsUsed + *bitOffset) & 0x07; + *bitsAvail -= bitsUsed; + + if (*bitsAvail < 0) + return ERR_AAC_INDATA_UNDERFLOW; + + return ERR_AAC_NONE; +} + diff --git a/components/spotify/cspot/bell/libhelix-aac/dequant.c b/components/spotify/cspot/bell/libhelix-aac/dequant.c new file mode 100644 index 00000000..5f511664 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/dequant.c @@ -0,0 +1,373 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: dequant.c,v 1.2 2005/05/20 18:05:41 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * February 2005 + * + * dequant.c - transform coefficient dequantization and short-block deinterleaving + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +#define SF_OFFSET 100 + +/* pow(2, i/4.0) for i = [0,1,2,3], format = Q30 */ +static const int pow14[4] PROGMEM = { + 0x40000000, 0x4c1bf829, 0x5a82799a, 0x6ba27e65 +}; + +/* pow(2, i/4.0) * pow(j, 4.0/3.0) for i = [0,1,2,3], j = [0,1,2,...,15] + * format = Q28 for j = [0-3], Q25 for j = [4-15] + */ +static const int pow43_14[4][16] PROGMEM = { + { + 0x00000000, 0x10000000, 0x285145f3, 0x453a5cdb, /* Q28 */ + 0x0cb2ff53, 0x111989d6, 0x15ce31c8, 0x1ac7f203, /* Q25 */ + 0x20000000, 0x257106b9, 0x2b16b4a3, 0x30ed74b4, /* Q25 */ + 0x36f23fa5, 0x3d227bd3, 0x437be656, 0x49fc823c, /* Q25 */ + }, + { + 0x00000000, 0x1306fe0a, 0x2ff221af, 0x52538f52, + 0x0f1a1bf4, 0x1455ccc2, 0x19ee62a8, 0x1fd92396, + 0x260dfc14, 0x2c8694d8, 0x333dcb29, 0x3a2f5c7a, + 0x4157aed5, 0x48b3aaa3, 0x50409f76, 0x57fc3010, + }, + { + 0x00000000, 0x16a09e66, 0x39047c0f, 0x61e734aa, + 0x11f59ac4, 0x182ec633, 0x1ed66a45, 0x25dfc55a, + 0x2d413ccd, 0x34f3462d, 0x3cefc603, 0x4531ab69, + 0x4db4adf8, 0x56752054, 0x5f6fcfcd, 0x68a1eca1, + }, + { + 0x00000000, 0x1ae89f99, 0x43ce3e4b, 0x746d57b2, + 0x155b8109, 0x1cc21cdc, 0x24ac1839, 0x2d0a479e, + 0x35d13f33, 0x3ef80748, 0x48775c93, 0x524938cd, + 0x5c68841d, 0x66d0df0a, 0x717e7bfe, 0x7c6e0305, + }, +}; + +/* pow(j, 4.0 / 3.0) for j = [16,17,18,...,63], format = Q23 */ +static const int pow43[48] PROGMEM = { + 0x1428a2fa, 0x15db1bd6, 0x1796302c, 0x19598d85, + 0x1b24e8bb, 0x1cf7fcfa, 0x1ed28af2, 0x20b4582a, + 0x229d2e6e, 0x248cdb55, 0x26832fda, 0x28800000, + 0x2a832287, 0x2c8c70a8, 0x2e9bc5d8, 0x30b0ff99, + 0x32cbfd4a, 0x34eca001, 0x3712ca62, 0x393e6088, + 0x3b6f47e0, 0x3da56717, 0x3fe0a5fc, 0x4220ed72, + 0x44662758, 0x46b03e7c, 0x48ff1e87, 0x4b52b3f3, + 0x4daaebfd, 0x5007b497, 0x5268fc62, 0x54ceb29c, + 0x5738c721, 0x59a72a59, 0x5c19cd35, 0x5e90a129, + 0x610b9821, 0x638aa47f, 0x660db90f, 0x6894c90b, + 0x6b1fc80c, 0x6daeaa0d, 0x70416360, 0x72d7e8b0, + 0x75722ef9, 0x78102b85, 0x7ab1d3ec, 0x7d571e09, +}; + +/* sqrt(0.5), format = Q31 */ +#define SQRTHALF 0x5a82799a + +/* Minimax polynomial approximation to pow(x, 4/3), over the range + * poly43lo: x = [0.5, 0.7071] + * poly43hi: x = [0.7071, 1.0] + * + * Relative error < 1E-7 + * Coefs are scaled by 4, 2, 1, 0.5, 0.25 + */ + +//fb +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnarrowing" +static const int poly43lo[5] PROGMEM = { 0x29a0bda9, 0xb02e4828, 0x5957aa1b, 0x236c498d, 0xff581859 }; +static const int poly43hi[5] PROGMEM = { 0x10852163, 0xd333f6a4, 0x46e9408b, 0x27c2cef0, 0xfef577b4 }; +#pragma GCC diagnostic pop + +/* pow2exp[i] = pow(2, i*4/3) exponent */ +static const int pow2exp[8] PROGMEM = { 14, 13, 11, 10, 9, 7, 6, 5 }; + +/* pow2exp[i] = pow(2, i*4/3) fraction */ +static const int pow2frac[8] PROGMEM = { + 0x6597fa94, 0x50a28be6, 0x7fffffff, 0x6597fa94, + 0x50a28be6, 0x7fffffff, 0x6597fa94, 0x50a28be6 +}; + +/************************************************************************************** + * Function: DequantBlock + * + * Description: dequantize one block of transform coefficients (in-place) + * + * Inputs: quantized transform coefficients, range = [0, 8191] + * number of samples to dequantize + * scalefactor for this block of data, range = [0, 256] + * + * Outputs: dequantized transform coefficients in Q(FBITS_OUT_DQ_OFF) + * + * Return: guard bit mask (OR of abs value of all dequantized coefs) + * + * Notes: applies dequant formula y = pow(x, 4.0/3.0) * pow(2, (scale - 100)/4.0) + * * pow(2, FBITS_OUT_DQ_OFF) + * clips outputs to Q(FBITS_OUT_DQ_OFF) + * output has no minimum number of guard bits + **************************************************************************************/ +static int DequantBlock(int *inbuf, int nSamps, int scale) +{ + int iSamp, scalef, scalei, x, y, gbMask, shift, tab4[4]; + const int *tab16, *coef; + + if (nSamps <= 0) + return 0; + + scale -= SF_OFFSET; /* new range = [-100, 156] */ + + /* with two's complement numbers, scalei/scalef factorization works for pos and neg values of scale: + * [+4...+7] >> 2 = +1, [ 0...+3] >> 2 = 0, [-4...-1] >> 2 = -1, [-8...-5] >> 2 = -2 ... + * (-1 & 0x3) = 3, (-2 & 0x3) = 2, (-3 & 0x3) = 1, (0 & 0x3) = 0 + * + * Example: 2^(-5/4) = 2^(-1) * 2^(-1/4) = 2^-2 * 2^(3/4) + */ + tab16 = pow43_14[scale & 0x3]; + scalef = pow14[scale & 0x3]; + scalei = (scale >> 2) + FBITS_OUT_DQ_OFF; + + /* cache first 4 values: + * tab16[j] = Q28 for j = [0,3] + * tab4[x] = x^(4.0/3.0) * 2^(0.25*scale), Q(FBITS_OUT_DQ_OFF) + */ + shift = 28 - scalei; + if (shift > 31) { + tab4[0] = tab4[1] = tab4[2] = tab4[3] = 0; + } else if (shift <= 0) { + shift = -shift; + if (shift > 31) + shift = 31; + for (x = 0; x < 4; x++) { + y = tab16[x]; + if (y > (0x7fffffff >> shift)) + y = 0x7fffffff; /* clip (rare) */ + else + y <<= shift; + tab4[x] = y; + } + } else { + tab4[0] = 0; + tab4[1] = tab16[1] >> shift; + tab4[2] = tab16[2] >> shift; + tab4[3] = tab16[3] >> shift; + } + + gbMask = 0; + do { + iSamp = *inbuf; + x = FASTABS(iSamp); + + if (x < 4) { + y = tab4[x]; + } else { + + if (x < 16) { + /* result: y = Q25 (tab16 = Q25) */ + y = tab16[x]; + shift = 25 - scalei; + } else if (x < 64) { + /* result: y = Q21 (pow43tab[j] = Q23, scalef = Q30) */ + y = pow43[x-16]; + shift = 21 - scalei; + y = MULSHIFT32(y, scalef); + } else { + /* normalize to [0x40000000, 0x7fffffff] + * input x = [64, 8191] = [64, 2^13-1] + * ranges: + * shift = 7: 64 - 127 + * shift = 6: 128 - 255 + * shift = 5: 256 - 511 + * shift = 4: 512 - 1023 + * shift = 3: 1024 - 2047 + * shift = 2: 2048 - 4095 + * shift = 1: 4096 - 8191 + */ + x <<= 17; + shift = 0; + if (x < 0x08000000) + x <<= 4, shift += 4; + if (x < 0x20000000) + x <<= 2, shift += 2; + if (x < 0x40000000) + x <<= 1, shift += 1; + + coef = (x < SQRTHALF) ? poly43lo : poly43hi; + + /* polynomial */ + y = coef[0]; + y = MULSHIFT32(y, x) + coef[1]; + y = MULSHIFT32(y, x) + coef[2]; + y = MULSHIFT32(y, x) + coef[3]; + y = MULSHIFT32(y, x) + coef[4]; + y = MULSHIFT32(y, pow2frac[shift]) << 3; + + /* fractional scale + * result: y = Q21 (pow43tab[j] = Q23, scalef = Q30) + */ + y = MULSHIFT32(y, scalef); /* now y is Q24 */ + shift = 24 - scalei - pow2exp[shift]; + } + + /* integer scale */ + if (shift <= 0) { + shift = -shift; + if (shift > 31) + shift = 31; + + if (y > (0x7fffffff >> shift)) + y = 0x7fffffff; /* clip (rare) */ + else + y <<= shift; + } else { + if (shift > 31) + shift = 31; + y >>= shift; + } + } + + /* sign and store (gbMask used to count GB's) */ + gbMask |= y; + + /* apply sign */ + iSamp >>= 31; + y ^= iSamp; + y -= iSamp; + + *inbuf++ = y; + } while (--nSamps); + + return gbMask; +} + +/************************************************************************************** + * Function: Dequantize + * + * Description: dequantize all transform coefficients for one channel + * + * Inputs: valid AACDecInfo struct (including unpacked, quantized coefficients) + * index of current channel + * + * Outputs: dequantized coefficients, including short-block deinterleaving + * flags indicating if intensity and/or PNS is active + * minimum guard bit count for dequantized coefficients + * + * Return: 0 if successful, error code (< 0) if error + **************************************************************************************/ +int Dequantize(AACDecInfo *aacDecInfo, int ch) +{ + int gp, cb, sfb, win, width, nSamps, gbMask; + int *coef; + const int /*short*/ *sfbTab; + unsigned char *sfbCodeBook; + short *scaleFactors; + PSInfoBase *psi; + ICSInfo *icsInfo; + + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoBase) + return ERR_AAC_NULL_POINTER; + psi = (PSInfoBase *)(aacDecInfo->psInfoBase); + icsInfo = (ch == 1 && psi->commonWin == 1) ? &(psi->icsInfo[0]) : &(psi->icsInfo[ch]); + + if (icsInfo->winSequence == 2) { + sfbTab = sfBandTabShort + sfBandTabShortOffset[psi->sampRateIdx]; + nSamps = NSAMPS_SHORT; + } else { + sfbTab = sfBandTabLong + sfBandTabLongOffset[psi->sampRateIdx]; + nSamps = NSAMPS_LONG; + } + coef = psi->coef[ch]; + sfbCodeBook = psi->sfbCodeBook[ch]; + scaleFactors = psi->scaleFactors[ch]; + + psi->intensityUsed[ch] = 0; + psi->pnsUsed[ch] = 0; + gbMask = 0; + for (gp = 0; gp < icsInfo->numWinGroup; gp++) { + for (win = 0; win < icsInfo->winGroupLen[gp]; win++) { + for (sfb = 0; sfb < icsInfo->maxSFB; sfb++) { + /* dequantize one scalefactor band (not necessary if codebook is intensity or PNS) + * for zero codebook, still run dequantizer in case non-zero pulse data was added + */ + cb = (int)(sfbCodeBook[sfb]); + width = sfbTab[sfb+1] - sfbTab[sfb]; + if (cb >= 0 && cb <= 11) + gbMask |= DequantBlock(coef, width, scaleFactors[sfb]); + else if (cb == 13) + psi->pnsUsed[ch] = 1; + else if (cb == 14 || cb == 15) + psi->intensityUsed[ch] = 1; /* should only happen if ch == 1 */ + coef += width; + } + coef += (nSamps - sfbTab[icsInfo->maxSFB]); + } + sfbCodeBook += icsInfo->maxSFB; + scaleFactors += icsInfo->maxSFB; + } + aacDecInfo->pnsUsed |= psi->pnsUsed[ch]; /* set flag if PNS used for any channel */ + + /* calculate number of guard bits in dequantized data */ + psi->gbCurrent[ch] = CLZ(gbMask) - 1; + + return ERR_AAC_NONE; +} + +/************************************************************************************** + * Function: DeinterleaveShortBlocks + * + * Description: deinterleave transform coefficients in short blocks for one channel + * + * Inputs: valid AACDecInfo struct (including unpacked, quantized coefficients) + * index of current channel + * + * Outputs: deinterleaved coefficients (window groups into 8 separate windows) + * + * Return: 0 if successful, error code (< 0) if error + * + * Notes: only necessary if deinterleaving not part of Huffman decoding + **************************************************************************************/ +int DeinterleaveShortBlocks(AACDecInfo *aacDecInfo, int ch) +{ + (void)aacDecInfo; + (void)ch; + /* not used for this implementation - short block deinterleaving performed during Huffman decoding */ + return ERR_AAC_NONE; +} diff --git a/components/spotify/cspot/bell/libhelix-aac/fft.c b/components/spotify/cspot/bell/libhelix-aac/fft.c new file mode 100644 index 00000000..0fc934ca --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/fft.c @@ -0,0 +1,393 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: fft.c,v 1.1 2005/02/26 01:47:34 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * February 2005 + * + * fft.c - Ken's optimized radix-4 DIT FFT, optional radix-8 first pass for odd log2(N) + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +#define NUM_FFT_SIZES 2 +static const int nfftTab[NUM_FFT_SIZES] PROGMEM ={64, 512}; +static const int nfftlog2Tab[NUM_FFT_SIZES] PROGMEM = {6, 9}; + +#define SQRT1_2 0x5a82799a /* sqrt(1/2) in Q31 */ + +#define swapcplx(p0,p1) \ + t = p0; t1 = *(&(p0)+1); p0 = p1; *(&(p0)+1) = *(&(p1)+1); p1 = t; *(&(p1)+1) = t1 + +/************************************************************************************** + * Function: BitReverse + * + * Description: Ken's fast in-place bit reverse, using super-small table + * + * Inputs: buffer of samples + * table index (for transform size) + * + * Outputs: bit-reversed samples in same buffer + * + * Return: none + **************************************************************************************/ + /*__attribute__ ((section (".data"))) */ static void BitReverse(int *inout, int tabidx) +{ + int *part0, *part1; + int a,b, t,t1; + const unsigned char* tab = bitrevtab + bitrevtabOffset[tabidx]; + int nbits = nfftlog2Tab[tabidx]; + + part0 = inout; + part1 = inout + (1 << nbits); + + while ((a = pgm_read_byte(tab++)) != 0) { + b = pgm_read_byte(tab++); + + swapcplx(part0[4*a+0], part0[4*b+0]); /* 0xxx0 <-> 0yyy0 */ + swapcplx(part0[4*a+2], part1[4*b+0]); /* 0xxx1 <-> 1yyy0 */ + swapcplx(part1[4*a+0], part0[4*b+2]); /* 1xxx0 <-> 0yyy1 */ + swapcplx(part1[4*a+2], part1[4*b+2]); /* 1xxx1 <-> 1yyy1 */ + } + + do { + swapcplx(part0[4*a+2], part1[4*a+0]); /* 0xxx1 <-> 1xxx0 */ + } while ((a = pgm_read_byte(tab++)) != 0); + + +} + +/************************************************************************************** + * Function: R4FirstPass + * + * Description: radix-4 trivial pass for decimation-in-time FFT + * + * Inputs: buffer of (bit-reversed) samples + * number of R4 butterflies per group (i.e. nfft / 4) + * + * Outputs: processed samples in same buffer + * + * Return: none + * + * Notes: assumes 2 guard bits, gains no integer bits, + * guard bits out = guard bits in - 2 + **************************************************************************************/ + /* __attribute__ ((section (".data"))) */ static void R4FirstPass(int *x, int bg) +{ + int ar, ai, br, bi, cr, ci, dr, di; + + for (; bg != 0; bg--) { + + ar = x[0] + x[2]; + br = x[0] - x[2]; + ai = x[1] + x[3]; + bi = x[1] - x[3]; + cr = x[4] + x[6]; + dr = x[4] - x[6]; + ci = x[5] + x[7]; + di = x[5] - x[7]; + + /* max per-sample gain = 4.0 (adding 4 inputs together) */ + x[0] = ar + cr; + x[4] = ar - cr; + x[1] = ai + ci; + x[5] = ai - ci; + x[2] = br + di; + x[6] = br - di; + x[3] = bi - dr; + x[7] = bi + dr; + + x += 8; + } +} + +/************************************************************************************** + * Function: R8FirstPass + * + * Description: radix-8 trivial pass for decimation-in-time FFT + * + * Inputs: buffer of (bit-reversed) samples + * number of R8 butterflies per group (i.e. nfft / 8) + * + * Outputs: processed samples in same buffer + * + * Return: none + * + * Notes: assumes 3 guard bits, gains 1 integer bit + * guard bits out = guard bits in - 3 (if inputs are full scale) + * or guard bits in - 2 (if inputs bounded to +/- sqrt(2)/2) + * see scaling comments in code + **************************************************************************************/ + /* __attribute__ ((section (".data"))) */ static void R8FirstPass(int *x, int bg) +{ + int ar, ai, br, bi, cr, ci, dr, di; + int sr, si, tr, ti, ur, ui, vr, vi; + int wr, wi, xr, xi, yr, yi, zr, zi; + + for (; bg != 0; bg--) { + + ar = x[0] + x[2]; + br = x[0] - x[2]; + ai = x[1] + x[3]; + bi = x[1] - x[3]; + cr = x[4] + x[6]; + dr = x[4] - x[6]; + ci = x[5] + x[7]; + di = x[5] - x[7]; + + sr = ar + cr; + ur = ar - cr; + si = ai + ci; + ui = ai - ci; + tr = br - di; + vr = br + di; + ti = bi + dr; + vi = bi - dr; + + ar = x[ 8] + x[10]; + br = x[ 8] - x[10]; + ai = x[ 9] + x[11]; + bi = x[ 9] - x[11]; + cr = x[12] + x[14]; + dr = x[12] - x[14]; + ci = x[13] + x[15]; + di = x[13] - x[15]; + + /* max gain of wr/wi/yr/yi vs input = 2 + * (sum of 4 samples >> 1) + */ + wr = (ar + cr) >> 1; + yr = (ar - cr) >> 1; + wi = (ai + ci) >> 1; + yi = (ai - ci) >> 1; + + /* max gain of output vs input = 4 + * (sum of 4 samples >> 1 + sum of 4 samples >> 1) + */ + x[ 0] = (sr >> 1) + wr; + x[ 8] = (sr >> 1) - wr; + x[ 1] = (si >> 1) + wi; + x[ 9] = (si >> 1) - wi; + x[ 4] = (ur >> 1) + yi; + x[12] = (ur >> 1) - yi; + x[ 5] = (ui >> 1) - yr; + x[13] = (ui >> 1) + yr; + + ar = br - di; + cr = br + di; + ai = bi + dr; + ci = bi - dr; + + /* max gain of xr/xi/zr/zi vs input = 4*sqrt(2)/2 = 2*sqrt(2) + * (sum of 8 samples, multiply by sqrt(2)/2, implicit >> 1 from Q31) + */ + xr = MULSHIFT32(SQRT1_2, ar - ai); + xi = MULSHIFT32(SQRT1_2, ar + ai); + zr = MULSHIFT32(SQRT1_2, cr - ci); + zi = MULSHIFT32(SQRT1_2, cr + ci); + + /* max gain of output vs input = (2 + 2*sqrt(2) ~= 4.83) + * (sum of 4 samples >> 1, plus xr/xi/zr/zi with gain of 2*sqrt(2)) + * in absolute terms, we have max gain of appx 9.656 (4 + 0.707*8) + * but we also gain 1 int bit (from MULSHIFT32 or from explicit >> 1) + */ + x[ 6] = (tr >> 1) - xr; + x[14] = (tr >> 1) + xr; + x[ 7] = (ti >> 1) - xi; + x[15] = (ti >> 1) + xi; + x[ 2] = (vr >> 1) + zi; + x[10] = (vr >> 1) - zi; + x[ 3] = (vi >> 1) - zr; + x[11] = (vi >> 1) + zr; + + x += 16; + } +} + +/************************************************************************************** + * Function: R4Core + * + * Description: radix-4 pass for decimation-in-time FFT + * + * Inputs: buffer of samples + * number of R4 butterflies per group + * number of R4 groups per pass + * pointer to twiddle factors tables + * + * Outputs: processed samples in same buffer + * + * Return: none + * + * Notes: gain 2 integer bits per pass (see scaling comments in code) + * min 1 GB in + * gbOut = gbIn - 1 (short block) or gbIn - 2 (long block) + * uses 3-mul, 3-add butterflies instead of 4-mul, 2-add + **************************************************************************************/ + /* __attribute__ ((section (".data"))) */ static void R4Core(int *x, int bg, int gp, int *wtab) +{ + int ar, ai, br, bi, cr, ci, dr, di, tr, ti; + int wd, ws, wi; + int i, j, step; + int *xptr, *wptr; + + for (; bg != 0; gp <<= 2, bg >>= 2) { + + step = 2*gp; + xptr = x; + + /* max per-sample gain, per group < 1 + 3*sqrt(2) ~= 5.25 if inputs x are full-scale + * do 3 groups for long block, 2 groups for short block (gain 2 int bits per group) + * + * very conservative scaling: + * group 1: max gain = 5.25, int bits gained = 2, gb used = 1 (2^3 = 8) + * group 2: max gain = 5.25^2 = 27.6, int bits gained = 4, gb used = 1 (2^5 = 32) + * group 3: max gain = 5.25^3 = 144.7, int bits gained = 6, gb used = 2 (2^8 = 256) + */ + for (i = bg; i != 0; i--) { + + wptr = wtab; + + for (j = gp; j != 0; j--) { + + ar = xptr[0]; + ai = xptr[1]; + xptr += step; + + /* gain 2 int bits for br/bi, cr/ci, dr/di (MULSHIFT32 by Q30) + * gain 1 net GB + */ + ws = wptr[0]; + wi = wptr[1]; + br = xptr[0]; + bi = xptr[1]; + wd = ws + 2*wi; + tr = MULSHIFT32(wi, br + bi); + br = MULSHIFT32(wd, br) - tr; /* cos*br + sin*bi */ + bi = MULSHIFT32(ws, bi) + tr; /* cos*bi - sin*br */ + xptr += step; + + ws = wptr[2]; + wi = wptr[3]; + cr = xptr[0]; + ci = xptr[1]; + wd = ws + 2*wi; + tr = MULSHIFT32(wi, cr + ci); + cr = MULSHIFT32(wd, cr) - tr; + ci = MULSHIFT32(ws, ci) + tr; + xptr += step; + + ws = wptr[4]; + wi = wptr[5]; + dr = xptr[0]; + di = xptr[1]; + wd = ws + 2*wi; + tr = MULSHIFT32(wi, dr + di); + dr = MULSHIFT32(wd, dr) - tr; + di = MULSHIFT32(ws, di) + tr; + wptr += 6; + + tr = ar; + ti = ai; + ar = (tr >> 2) - br; + ai = (ti >> 2) - bi; + br = (tr >> 2) + br; + bi = (ti >> 2) + bi; + + tr = cr; + ti = ci; + cr = tr + dr; + ci = di - ti; + dr = tr - dr; + di = di + ti; + + xptr[0] = ar + ci; + xptr[1] = ai + dr; + xptr -= step; + xptr[0] = br - cr; + xptr[1] = bi - di; + xptr -= step; + xptr[0] = ar - ci; + xptr[1] = ai - dr; + xptr -= step; + xptr[0] = br + cr; + xptr[1] = bi + di; + xptr += 2; + } + xptr += 3*step; + } + wtab += 3*step; + } +} + + +/************************************************************************************** + * Function: R4FFT + * + * Description: Ken's very fast in-place radix-4 decimation-in-time FFT + * + * Inputs: table index (for transform size) + * buffer of samples (non bit-reversed) + * + * Outputs: processed samples in same buffer + * + * Return: none + * + * Notes: assumes 5 guard bits in for nfft <= 512 + * gbOut = gbIn - 4 (assuming input is from PreMultiply) + * gains log2(nfft) - 2 int bits total + * so gain 7 int bits (LONG), 4 int bits (SHORT) + **************************************************************************************/ +void R4FFT(int tabidx, int *x) +{ + int order = nfftlog2Tab[tabidx]; + int nfft = nfftTab[tabidx]; + + /* decimation in time */ + BitReverse(x, tabidx); + + if (order & 0x1) { + /* long block: order = 9, nfft = 512 */ + R8FirstPass(x, nfft >> 3); /* gain 1 int bit, lose 2 GB */ + R4Core(x, nfft >> 5, 8, (int *)twidTabOdd); /* gain 6 int bits, lose 2 GB */ + } else { + /* short block: order = 6, nfft = 64 */ + R4FirstPass(x, nfft >> 2); /* gain 0 int bits, lose 2 GB */ + R4Core(x, nfft >> 4, 4, (int *)twidTabEven); /* gain 4 int bits, lose 1 GB */ + } +} diff --git a/components/spotify/cspot/bell/libhelix-aac/filefmt.c b/components/spotify/cspot/bell/libhelix-aac/filefmt.c new file mode 100644 index 00000000..ccfac596 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/filefmt.c @@ -0,0 +1,496 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: filefmt.c,v 1.1 2005/02/26 01:47:34 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * filefmt.c - ADIF and ADTS header decoding, raw block handling + **************************************************************************************/ + +#include "coder.h" + + /************************************************************************************** + * Function: UnpackADTSHeader + * + * Description: parse the ADTS frame header and initialize decoder state + * + * Inputs: valid AACDecInfo struct + * double pointer to buffer with complete ADTS frame header (byte aligned) + * header size = 7 bytes, plus 2 if CRC + * + * Outputs: filled in ADTS struct + * updated buffer pointer + * updated bit offset + * updated number of available bits + * + * Return: 0 if successful, error code (< 0) if error + * + * TODO: test CRC + * verify that fixed fields don't change between frames + **************************************************************************************/ +int UnpackADTSHeader(AACDecInfo *aacDecInfo, unsigned char **buf, int *bitOffset, int *bitsAvail) +{ + int bitsUsed; + PSInfoBase *psi; + BitStreamInfo bsi; + ADTSHeader *fhADTS; + + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoBase) + return ERR_AAC_NULL_POINTER; + psi = (PSInfoBase *)(aacDecInfo->psInfoBase); + fhADTS = &(psi->fhADTS); + + /* init bitstream reader */ + SetBitstreamPointer(&bsi, (*bitsAvail + 7) >> 3, *buf); + GetBits(&bsi, *bitOffset); + + /* verify that first 12 bits of header are syncword */ + if (GetBits(&bsi, 12) != 0x0fff) { + return ERR_AAC_INVALID_ADTS_HEADER; + } + + /* fixed fields - should not change from frame to frame */ + fhADTS->id = GetBits(&bsi, 1); + fhADTS->layer = GetBits(&bsi, 2); + fhADTS->protectBit = GetBits(&bsi, 1); + fhADTS->profile = GetBits(&bsi, 2); + fhADTS->sampRateIdx = GetBits(&bsi, 4); + fhADTS->privateBit = GetBits(&bsi, 1); + fhADTS->channelConfig = GetBits(&bsi, 3); + fhADTS->origCopy = GetBits(&bsi, 1); + fhADTS->home = GetBits(&bsi, 1); + + /* variable fields - can change from frame to frame */ + fhADTS->copyBit = GetBits(&bsi, 1); + fhADTS->copyStart = GetBits(&bsi, 1); + fhADTS->frameLength = GetBits(&bsi, 13); + fhADTS->bufferFull = GetBits(&bsi, 11); + fhADTS->numRawDataBlocks = GetBits(&bsi, 2) + 1; + + /* note - MPEG4 spec, correction 1 changes how CRC is handled when protectBit == 0 and numRawDataBlocks > 1 */ + if (fhADTS->protectBit == 0) + fhADTS->crcCheckWord = GetBits(&bsi, 16); + + /* byte align */ + ByteAlignBitstream(&bsi); /* should always be aligned anyway */ + + /* check validity of header */ + if (fhADTS->layer != 0 || fhADTS->profile != AAC_PROFILE_LC || + fhADTS->sampRateIdx >= NUM_SAMPLE_RATES || fhADTS->channelConfig >= NUM_DEF_CHAN_MAPS) + return ERR_AAC_INVALID_ADTS_HEADER; + +#ifndef AAC_ENABLE_MPEG4 + if (fhADTS->id != 1) + return ERR_AAC_MPEG4_UNSUPPORTED; +#endif + + /* update codec info */ + psi->sampRateIdx = fhADTS->sampRateIdx; + if (!psi->useImpChanMap) + psi->nChans = channelMapTab[fhADTS->channelConfig]; + + /* syntactic element fields will be read from bitstream for each element */ + aacDecInfo->prevBlockID = AAC_ID_INVALID; + aacDecInfo->currBlockID = AAC_ID_INVALID; + aacDecInfo->currInstTag = -1; + + /* fill in user-accessible data (TODO - calc bitrate, handle tricky channel config cases) */ + aacDecInfo->bitRate = 0; + aacDecInfo->nChans = psi->nChans; + aacDecInfo->sampRate = sampRateTab[psi->sampRateIdx]; + aacDecInfo->profile = fhADTS->profile; + aacDecInfo->sbrEnabled = 0; + aacDecInfo->adtsBlocksLeft = fhADTS->numRawDataBlocks; + + /* update bitstream reader */ + bitsUsed = CalcBitsUsed(&bsi, *buf, *bitOffset); + *buf += (bitsUsed + *bitOffset) >> 3; + *bitOffset = (bitsUsed + *bitOffset) & 0x07; + *bitsAvail -= bitsUsed ; + if (*bitsAvail < 0) + return ERR_AAC_INDATA_UNDERFLOW; + + return ERR_AAC_NONE; +} + +/************************************************************************************** + * Function: GetADTSChannelMapping + * + * Description: determine the number of channels from implicit mapping rules + * + * Inputs: valid AACDecInfo struct + * pointer to start of raw_data_block + * bit offset + * bits available + * + * Outputs: updated number of channels + * + * Return: 0 if successful, error code (< 0) if error + * + * Notes: calculates total number of channels using rules in 14496-3, 4.5.1.2.1 + * does not attempt to deduce speaker geometry + **************************************************************************************/ +int GetADTSChannelMapping(AACDecInfo *aacDecInfo, unsigned char *buf, int bitOffset, int bitsAvail) +{ + int ch, nChans, elementChans, err; + PSInfoBase *psi; + + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoBase) + return ERR_AAC_NULL_POINTER; + psi = (PSInfoBase *)(aacDecInfo->psInfoBase); + + nChans = 0; + do { + /* parse next syntactic element */ + err = DecodeNextElement(aacDecInfo, &buf, &bitOffset, &bitsAvail); + if (err) + return err; + + elementChans = elementNumChans[aacDecInfo->currBlockID]; + nChans += elementChans; + + for (ch = 0; ch < elementChans; ch++) { + err = DecodeNoiselessData(aacDecInfo, &buf, &bitOffset, &bitsAvail, ch); + if (err) + return err; + } + } while (aacDecInfo->currBlockID != AAC_ID_END); + + if (nChans <= 0) + return ERR_AAC_CHANNEL_MAP; + + /* update number of channels in codec state and user-accessible info structs */ + psi->nChans = nChans; + aacDecInfo->nChans = psi->nChans; + psi->useImpChanMap = 1; + + return ERR_AAC_NONE; +} + +/************************************************************************************** + * Function: GetNumChannelsADIF + * + * Description: get number of channels from program config elements in an ADIF file + * + * Inputs: array of filled-in program config element structures + * number of PCE's + * + * Outputs: none + * + * Return: total number of channels in file + * -1 if error (invalid number of PCE's or unsupported mode) + **************************************************************************************/ +static int GetNumChannelsADIF(ProgConfigElement *fhPCE, int nPCE) +{ + int i, j, nChans; + + if (nPCE < 1 || nPCE > MAX_NUM_PCE_ADIF) + return -1; + + nChans = 0; + for (i = 0; i < nPCE; i++) { + /* for now: only support LC, no channel coupling */ + if (fhPCE[i].profile != AAC_PROFILE_LC || fhPCE[i].numCCE > 0) + return -1; + + /* add up number of channels in all channel elements (assume all single-channel) */ + nChans += fhPCE[i].numFCE; + nChans += fhPCE[i].numSCE; + nChans += fhPCE[i].numBCE; + nChans += fhPCE[i].numLCE; + + /* add one more for every element which is a channel pair */ + for (j = 0; j < fhPCE[i].numFCE; j++) { + if (CHAN_ELEM_IS_CPE(fhPCE[i].fce[j])) + nChans++; + } + for (j = 0; j < fhPCE[i].numSCE; j++) { + if (CHAN_ELEM_IS_CPE(fhPCE[i].sce[j])) + nChans++; + } + for (j = 0; j < fhPCE[i].numBCE; j++) { + if (CHAN_ELEM_IS_CPE(fhPCE[i].bce[j])) + nChans++; + } + + } + + return nChans; +} + +/************************************************************************************** + * Function: GetSampleRateIdxADIF + * + * Description: get sampling rate index from program config elements in an ADIF file + * + * Inputs: array of filled-in program config element structures + * number of PCE's + * + * Outputs: none + * + * Return: sample rate of file + * -1 if error (invalid number of PCE's or sample rate mismatch) + **************************************************************************************/ +static int GetSampleRateIdxADIF(ProgConfigElement *fhPCE, int nPCE) +{ + int i, idx; + + if (nPCE < 1 || nPCE > MAX_NUM_PCE_ADIF) + return -1; + + /* make sure all PCE's have the same sample rate */ + idx = fhPCE[0].sampRateIdx; + for (i = 1; i < nPCE; i++) { + if (fhPCE[i].sampRateIdx != idx) + return -1; + } + + return idx; +} + +/************************************************************************************** + * Function: UnpackADIFHeader + * + * Description: parse the ADIF file header and initialize decoder state + * + * Inputs: valid AACDecInfo struct + * double pointer to buffer with complete ADIF header + * (starting at 'A' in 'ADIF' tag) + * pointer to bit offset + * pointer to number of valid bits remaining in inbuf + * + * Outputs: filled-in ADIF struct + * updated buffer pointer + * updated bit offset + * updated number of available bits + * + * Return: 0 if successful, error code (< 0) if error + **************************************************************************************/ +int UnpackADIFHeader(AACDecInfo *aacDecInfo, unsigned char **buf, int *bitOffset, int *bitsAvail) +{ + int i, bitsUsed; + PSInfoBase *psi; + BitStreamInfo bsi; + ADIFHeader *fhADIF; + ProgConfigElement *pce; + + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoBase) + return ERR_AAC_NULL_POINTER; + psi = (PSInfoBase *)(aacDecInfo->psInfoBase); + + /* init bitstream reader */ + SetBitstreamPointer(&bsi, (*bitsAvail + 7) >> 3, *buf); + GetBits(&bsi, *bitOffset); + + /* unpack ADIF file header */ + fhADIF = &(psi->fhADIF); + pce = psi->pce; + + /* verify that first 32 bits of header are "ADIF" */ + if (GetBits(&bsi, 8) != 'A' || GetBits(&bsi, 8) != 'D' || GetBits(&bsi, 8) != 'I' || GetBits(&bsi, 8) != 'F') + return ERR_AAC_INVALID_ADIF_HEADER; + + /* read ADIF header fields */ + fhADIF->copyBit = GetBits(&bsi, 1); + if (fhADIF->copyBit) { + for (i = 0; i < ADIF_COPYID_SIZE; i++) + fhADIF->copyID[i] = GetBits(&bsi, 8); + } + fhADIF->origCopy = GetBits(&bsi, 1); + fhADIF->home = GetBits(&bsi, 1); + fhADIF->bsType = GetBits(&bsi, 1); + fhADIF->bitRate = GetBits(&bsi, 23); + fhADIF->numPCE = GetBits(&bsi, 4) + 1; /* add 1 (so range = [1, 16]) */ + if (fhADIF->bsType == 0) + fhADIF->bufferFull = GetBits(&bsi, 20); + + /* parse all program config elements */ + for (i = 0; i < fhADIF->numPCE; i++) + DecodeProgramConfigElement(pce + i, &bsi); + + /* byte align */ + ByteAlignBitstream(&bsi); + + /* update codec info */ + psi->nChans = GetNumChannelsADIF(pce, fhADIF->numPCE); + psi->sampRateIdx = GetSampleRateIdxADIF(pce, fhADIF->numPCE); + + /* check validity of header */ + if (psi->nChans < 0 || psi->sampRateIdx < 0 || psi->sampRateIdx >= NUM_SAMPLE_RATES) + return ERR_AAC_INVALID_ADIF_HEADER; + + /* syntactic element fields will be read from bitstream for each element */ + aacDecInfo->prevBlockID = AAC_ID_INVALID; + aacDecInfo->currBlockID = AAC_ID_INVALID; + aacDecInfo->currInstTag = -1; + + /* fill in user-accessible data */ + aacDecInfo->bitRate = 0; + aacDecInfo->nChans = psi->nChans; + aacDecInfo->sampRate = sampRateTab[psi->sampRateIdx]; + aacDecInfo->profile = pce[0].profile; + aacDecInfo->sbrEnabled = 0; + + /* update bitstream reader */ + bitsUsed = CalcBitsUsed(&bsi, *buf, *bitOffset); + *buf += (bitsUsed + *bitOffset) >> 3; + *bitOffset = (bitsUsed + *bitOffset) & 0x07; + *bitsAvail -= bitsUsed ; + if (*bitsAvail < 0) + return ERR_AAC_INDATA_UNDERFLOW; + + return ERR_AAC_NONE; +} + +/************************************************************************************** + * Function: SetRawBlockParams + * + * Description: set internal state variables for decoding a stream of raw data blocks + * + * Inputs: valid AACDecInfo struct + * flag indicating source of parameters (from previous headers or passed + * explicitly by caller) + * number of channels + * sample rate + * profile ID + * + * Outputs: updated state variables in aacDecInfo + * + * Return: 0 if successful, error code (< 0) if error + * + * Notes: if copyLast == 1, then psi->nChans, psi->sampRateIdx, and + * aacDecInfo->profile are not changed (it's assumed that we already + * set them, such as by a previous call to UnpackADTSHeader()) + * if copyLast == 0, then the parameters we passed in are used instead + **************************************************************************************/ +int SetRawBlockParams(AACDecInfo *aacDecInfo, int copyLast, int nChans, int sampRate, int profile) +{ + int idx; + PSInfoBase *psi; + + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoBase) + return ERR_AAC_NULL_POINTER; + psi = (PSInfoBase *)(aacDecInfo->psInfoBase); + + if (!copyLast) { + aacDecInfo->profile = profile; + psi->nChans = nChans; + for (idx = 0; idx < NUM_SAMPLE_RATES; idx++) { + if (sampRate == sampRateTab[idx]) { + psi->sampRateIdx = idx; + break; + } + } + if (idx == NUM_SAMPLE_RATES) + return ERR_AAC_INVALID_FRAME; + } + aacDecInfo->nChans = psi->nChans; + aacDecInfo->sampRate = sampRateTab[psi->sampRateIdx]; + + /* check validity of header */ + if (psi->sampRateIdx >= NUM_SAMPLE_RATES || psi->sampRateIdx < 0 || aacDecInfo->profile != AAC_PROFILE_LC) + return ERR_AAC_RAWBLOCK_PARAMS; + + return ERR_AAC_NONE; +} + +/************************************************************************************** + * Function: PrepareRawBlock + * + * Description: reset per-block state variables for raw blocks (no ADTS/ADIF headers) + * + * Inputs: valid AACDecInfo struct + * + * Outputs: updated state variables in aacDecInfo + * + * Return: 0 if successful, error code (< 0) if error + **************************************************************************************/ +int PrepareRawBlock(AACDecInfo *aacDecInfo) +{ +// PSInfoBase *psi; + + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoBase) + return ERR_AAC_NULL_POINTER; +// psi = (PSInfoBase *)(aacDecInfo->psInfoBase); + + /* syntactic element fields will be read from bitstream for each element */ + aacDecInfo->prevBlockID = AAC_ID_INVALID; + aacDecInfo->currBlockID = AAC_ID_INVALID; + aacDecInfo->currInstTag = -1; + + /* fill in user-accessible data */ + aacDecInfo->bitRate = 0; + aacDecInfo->sbrEnabled = 0; + + return ERR_AAC_NONE; +} + +/************************************************************************************** + * Function: FlushCodec + * + * Description: flush internal codec state (after seeking, for example) + * + * Inputs: valid AACDecInfo struct + * + * Outputs: updated state variables in aacDecInfo + * + * Return: 0 if successful, error code (< 0) if error + * + * Notes: only need to clear data which is persistent between frames + * (such as overlap buffer) + **************************************************************************************/ +int FlushCodec(AACDecInfo *aacDecInfo) +{ + PSInfoBase *psi; + + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoBase) + return ERR_AAC_NULL_POINTER; + psi = (PSInfoBase *)(aacDecInfo->psInfoBase); + + ClearBuffer(psi->overlap, AAC_MAX_NCHANS * AAC_MAX_NSAMPS * sizeof(int)); + ClearBuffer(psi->prevWinShape, AAC_MAX_NCHANS * sizeof(int)); + + return ERR_AAC_NONE; +} diff --git a/components/spotify/cspot/bell/libhelix-aac/huffman.c b/components/spotify/cspot/bell/libhelix-aac/huffman.c new file mode 100644 index 00000000..57cc45c9 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/huffman.c @@ -0,0 +1,415 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: huffman.c,v 1.2 2005/05/24 16:01:55 albertofloyd Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * huffman.c - Huffman decoding + **************************************************************************************/ + +#include "coder.h" + +/************************************************************************************** + * Function: DecodeHuffmanScalar + * + * Description: decode one Huffman symbol from bitstream + * + * Inputs: pointers to Huffman table and info struct + * left-aligned bit buffer with >= huffTabInfo->maxBits bits + * + * Outputs: decoded symbol in *val + * + * Return: number of bits in symbol + * + * Notes: assumes canonical Huffman codes: + * first CW always 0, we have "count" CW's of length "nBits" bits + * starting CW for codes of length nBits+1 = + * (startCW[nBits] + count[nBits]) << 1 + * if there are no codes at nBits, then we just keep << 1 each time + * (since count[nBits] = 0) + **************************************************************************************/ +/* __attribute__ ((section (".data"))) */ int DecodeHuffmanScalar(const signed short *huffTab, const HuffInfo *huffTabInfo, unsigned int bitBuf, signed int *val) +{ + unsigned int count, start, shift, t; + const unsigned /*char*/ int *countPtr; + const signed short *map; + + map = huffTab + huffTabInfo->offset; + countPtr = huffTabInfo->count; + + start = 0; + count = 0; + shift = 32; + do { + start += count; + start <<= 1; + map += count; + count = *countPtr++; + shift--; + t = (bitBuf >> shift) - start; + } while (t >= count); + + *val = (signed int)pgm_read_word(&map[t]); + return (countPtr - huffTabInfo->count); +} + +#define APPLY_SIGN(v, s) {(v) ^= ((signed int)(s) >> 31); (v) -= ((signed int)(s) >> 31);} + +#define GET_QUAD_SIGNBITS(v) (((unsigned int)(v) << 17) >> 29) /* bits 14-12, unsigned */ +#define GET_QUAD_W(v) (((signed int)(v) << 20) >> 29) /* bits 11-9, sign-extend */ +#define GET_QUAD_X(v) (((signed int)(v) << 23) >> 29) /* bits 8-6, sign-extend */ +#define GET_QUAD_Y(v) (((signed int)(v) << 26) >> 29) /* bits 5-3, sign-extend */ +#define GET_QUAD_Z(v) (((signed int)(v) << 29) >> 29) /* bits 2-0, sign-extend */ + +#define GET_PAIR_SIGNBITS(v) (((unsigned int)(v) << 20) >> 30) /* bits 11-10, unsigned */ +#define GET_PAIR_Y(v) (((signed int)(v) << 22) >> 27) /* bits 9-5, sign-extend */ +#define GET_PAIR_Z(v) (((signed int)(v) << 27) >> 27) /* bits 4-0, sign-extend */ + +#define GET_ESC_SIGNBITS(v) (((unsigned int)(v) << 18) >> 30) /* bits 13-12, unsigned */ +#define GET_ESC_Y(v) (((signed int)(v) << 20) >> 26) /* bits 11-6, sign-extend */ +#define GET_ESC_Z(v) (((signed int)(v) << 26) >> 26) /* bits 5-0, sign-extend */ + +/************************************************************************************** + * Function: UnpackZeros + * + * Description: fill a section of coefficients with zeros + * + * Inputs: number of coefficients + * + * Outputs: nVals zeros, starting at coef + * + * Return: none + * + * Notes: assumes nVals is always a multiple of 4 because all scalefactor bands + * are a multiple of 4 coefficients long + **************************************************************************************/ +static void UnpackZeros(int nVals, int *coef) +{ + while (nVals > 0) { + *coef++ = 0; + *coef++ = 0; + *coef++ = 0; + *coef++ = 0; + nVals -= 4; + } +} + +/************************************************************************************** + * Function: UnpackQuads + * + * Description: decode a section of 4-way vector Huffman coded coefficients + * + * Inputs BitStreamInfo struct pointing to start of codewords for this section + * index of Huffman codebook + * number of coefficients + * + * Outputs: nVals coefficients, starting at coef + * + * Return: none + * + * Notes: assumes nVals is always a multiple of 4 because all scalefactor bands + * are a multiple of 4 coefficients long + **************************************************************************************/ +/* __attribute__ ((section (".data"))) */ static void UnpackQuads(BitStreamInfo *bsi, int cb, int nVals, int *coef) +{ + int w, x, y, z, maxBits, nCodeBits, nSignBits, val; + unsigned int bitBuf; + + maxBits = huffTabSpecInfo[cb - HUFFTAB_SPEC_OFFSET].maxBits + 4; + while (nVals > 0) { + /* decode quad */ + bitBuf = GetBitsNoAdvance(bsi, maxBits) << (32 - maxBits); + nCodeBits = DecodeHuffmanScalar(huffTabSpec, &huffTabSpecInfo[cb - HUFFTAB_SPEC_OFFSET], bitBuf, &val); + + w = GET_QUAD_W(val); + x = GET_QUAD_X(val); + y = GET_QUAD_Y(val); + z = GET_QUAD_Z(val); + + bitBuf <<= nCodeBits; + nSignBits = (int)GET_QUAD_SIGNBITS(val); + AdvanceBitstream(bsi, nCodeBits + nSignBits); + if (nSignBits) { + if (w) {APPLY_SIGN(w, bitBuf); bitBuf <<= 1;} + if (x) {APPLY_SIGN(x, bitBuf); bitBuf <<= 1;} + if (y) {APPLY_SIGN(y, bitBuf); bitBuf <<= 1;} + if (z) {APPLY_SIGN(z, bitBuf); bitBuf <<= 1;} + } + *coef++ = w; *coef++ = x; *coef++ = y; *coef++ = z; + nVals -= 4; + } +} + +/************************************************************************************** + * Function: UnpackPairsNoEsc + * + * Description: decode a section of 2-way vector Huffman coded coefficients, + * using non-esc tables (5 through 10) + * + * Inputs BitStreamInfo struct pointing to start of codewords for this section + * index of Huffman codebook (must not be the escape codebook) + * number of coefficients + * + * Outputs: nVals coefficients, starting at coef + * + * Return: none + * + * Notes: assumes nVals is always a multiple of 2 because all scalefactor bands + * are a multiple of 4 coefficients long + **************************************************************************************/ +/* __attribute__ ((section (".data"))) */ static void UnpackPairsNoEsc(BitStreamInfo *bsi, int cb, int nVals, int *coef) +{ + int y, z, maxBits, nCodeBits, nSignBits, val; + unsigned int bitBuf; + + maxBits = huffTabSpecInfo[cb - HUFFTAB_SPEC_OFFSET].maxBits + 2; + while (nVals > 0) { + /* decode pair */ + bitBuf = GetBitsNoAdvance(bsi, maxBits) << (32 - maxBits); + nCodeBits = DecodeHuffmanScalar(huffTabSpec, &huffTabSpecInfo[cb-HUFFTAB_SPEC_OFFSET], bitBuf, &val); + + y = GET_PAIR_Y(val); + z = GET_PAIR_Z(val); + + bitBuf <<= nCodeBits; + nSignBits = GET_PAIR_SIGNBITS(val); + AdvanceBitstream(bsi, nCodeBits + nSignBits); + if (nSignBits) { + if (y) {APPLY_SIGN(y, bitBuf); bitBuf <<= 1;} + if (z) {APPLY_SIGN(z, bitBuf); bitBuf <<= 1;} + } + *coef++ = y; *coef++ = z; + nVals -= 2; + } +} + +/************************************************************************************** + * Function: UnpackPairsEsc + * + * Description: decode a section of 2-way vector Huffman coded coefficients, + * using esc table (11) + * + * Inputs BitStreamInfo struct pointing to start of codewords for this section + * index of Huffman codebook (must be the escape codebook) + * number of coefficients + * + * Outputs: nVals coefficients, starting at coef + * + * Return: none + * + * Notes: assumes nVals is always a multiple of 2 because all scalefactor bands + * are a multiple of 4 coefficients long + **************************************************************************************/ +/* __attribute__ ((section (".data"))) */ static void UnpackPairsEsc(BitStreamInfo *bsi, int cb, int nVals, int *coef) +{ + int y, z, maxBits, nCodeBits, nSignBits, n, val; + unsigned int bitBuf; + + maxBits = huffTabSpecInfo[cb - HUFFTAB_SPEC_OFFSET].maxBits + 2; + while (nVals > 0) { + /* decode pair with escape value */ + bitBuf = GetBitsNoAdvance(bsi, maxBits) << (32 - maxBits); + nCodeBits = DecodeHuffmanScalar(huffTabSpec, &huffTabSpecInfo[cb-HUFFTAB_SPEC_OFFSET], bitBuf, &val); + + y = GET_ESC_Y(val); + z = GET_ESC_Z(val); + + bitBuf <<= nCodeBits; + nSignBits = GET_ESC_SIGNBITS(val); + AdvanceBitstream(bsi, nCodeBits + nSignBits); + + if (y == 16) { + n = 4; + while (GetBits(bsi, 1) == 1) + n++; + y = (1 << n) + GetBits(bsi, n); + } + if (z == 16) { + n = 4; + while (GetBits(bsi, 1) == 1) + n++; + z = (1 << n) + GetBits(bsi, n); + } + + if (nSignBits) { + if (y) {APPLY_SIGN(y, bitBuf); bitBuf <<= 1;} + if (z) {APPLY_SIGN(z, bitBuf); bitBuf <<= 1;} + } + + *coef++ = y; *coef++ = z; + nVals -= 2; + } +} + +/************************************************************************************** + * Function: DecodeSpectrumLong + * + * Description: decode transform coefficients for frame with one long block + * + * Inputs: platform specific info struct + * BitStreamInfo struct pointing to start of spectral data + * (14496-3, table 4.4.29) + * index of current channel + * + * Outputs: decoded, quantized coefficients for this channel + * + * Return: none + * + * Notes: adds in pulse data if present + * fills coefficient buffer with zeros in any region not coded with + * codebook in range [1, 11] (including sfb's above sfbMax) + **************************************************************************************/ +/* __attribute__ ((section (".data"))) */ void DecodeSpectrumLong(PSInfoBase *psi, BitStreamInfo *bsi, int ch) +{ + int i, sfb, cb, nVals, offset; + const /*short*/ int *sfbTab; + unsigned char *sfbCodeBook; + int *coef; + ICSInfo *icsInfo; + PulseInfo *pi; + + coef = psi->coef[ch]; + icsInfo = (ch == 1 && psi->commonWin == 1) ? &(psi->icsInfo[0]) : &(psi->icsInfo[ch]); + + /* decode long block */ + sfbTab = sfBandTabLong + sfBandTabLongOffset[psi->sampRateIdx]; + sfbCodeBook = psi->sfbCodeBook[ch]; + for (sfb = 0; sfb < icsInfo->maxSFB; sfb++) { + cb = *sfbCodeBook++; + nVals = sfbTab[sfb+1] - sfbTab[sfb]; + + if (cb == 0) + UnpackZeros(nVals, coef); + else if (cb <= 4) + UnpackQuads(bsi, cb, nVals, coef); + else if (cb <= 10) + UnpackPairsNoEsc(bsi, cb, nVals, coef); + else if (cb == 11) + UnpackPairsEsc(bsi, cb, nVals, coef); + else + UnpackZeros(nVals, coef); + + coef += nVals; + } + + /* fill with zeros above maxSFB */ + nVals = NSAMPS_LONG - sfbTab[sfb]; + UnpackZeros(nVals, coef); + + /* add pulse data, if present */ + pi = &psi->pulseInfo[ch]; + if (pi->pulseDataPresent) { + coef = psi->coef[ch]; + offset = sfbTab[pi->startSFB]; + for (i = 0; i < pi->numPulse; i++) { + offset += pi->offset[i]; + if (coef[offset] > 0) + coef[offset] += pi->amp[i]; + else + coef[offset] -= pi->amp[i]; + } + ASSERT(offset < NSAMPS_LONG); + } +} + +/************************************************************************************** + * Function: DecodeSpectrumShort + * + * Description: decode transform coefficients for frame with eight short blocks + * + * Inputs: platform specific info struct + * BitStreamInfo struct pointing to start of spectral data + * (14496-3, table 4.4.29) + * index of current channel + * + * Outputs: decoded, quantized coefficients for this channel + * + * Return: none + * + * Notes: fills coefficient buffer with zeros in any region not coded with + * codebook in range [1, 11] (including sfb's above sfbMax) + * deinterleaves window groups into 8 windows + **************************************************************************************/ +/* __attribute__ ((section (".data"))) */ void DecodeSpectrumShort(PSInfoBase *psi, BitStreamInfo *bsi, int ch) +{ + int gp, cb, nVals=0, win, offset, sfb; + const /*short*/ int *sfbTab; + unsigned char *sfbCodeBook; + int *coef; + ICSInfo *icsInfo; + + coef = psi->coef[ch]; + icsInfo = (ch == 1 && psi->commonWin == 1) ? &(psi->icsInfo[0]) : &(psi->icsInfo[ch]); + + /* decode short blocks, deinterleaving in-place */ + sfbTab = sfBandTabShort + sfBandTabShortOffset[psi->sampRateIdx]; + sfbCodeBook = psi->sfbCodeBook[ch]; + for (gp = 0; gp < icsInfo->numWinGroup; gp++) { + for (sfb = 0; sfb < icsInfo->maxSFB; sfb++) { + nVals = sfbTab[sfb+1] - sfbTab[sfb]; + cb = *sfbCodeBook++; + + for (win = 0; win < icsInfo->winGroupLen[gp]; win++) { + offset = win*NSAMPS_SHORT; + if (cb == 0) + UnpackZeros(nVals, coef + offset); + else if (cb <= 4) + UnpackQuads(bsi, cb, nVals, coef + offset); + else if (cb <= 10) + UnpackPairsNoEsc(bsi, cb, nVals, coef + offset); + else if (cb == 11) + UnpackPairsEsc(bsi, cb, nVals, coef + offset); + else + UnpackZeros(nVals, coef + offset); + } + coef += nVals; + } + + /* fill with zeros above maxSFB */ + for (win = 0; win < icsInfo->winGroupLen[gp]; win++) { + offset = win*NSAMPS_SHORT; + nVals = NSAMPS_SHORT - sfbTab[sfb]; + UnpackZeros(nVals, coef + offset); + } + coef += nVals; + coef += (icsInfo->winGroupLen[gp] - 1)*NSAMPS_SHORT; + } + + ASSERT(coef == psi->coef[ch] + NSAMPS_LONG); +} diff --git a/components/spotify/cspot/bell/libhelix-aac/hufftabs.c b/components/spotify/cspot/bell/libhelix-aac/hufftabs.c new file mode 100644 index 00000000..91cc50ce --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/hufftabs.c @@ -0,0 +1,177 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: hufftabs.c,v 1.1 2005/02/26 01:47:34 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * hufftabs.c - Huffman symbol tables + **************************************************************************************/ + +#include "coder.h" + +const HuffInfo huffTabSpecInfo[11] PROGMEM = { + /* table 0 not used */ + {11, { 1, 0, 0, 0, 8, 0, 24, 0, 24, 8, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0}, + { 9, { 0, 0, 1, 1, 7, 24, 15, 19, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 81}, + {16, { 1, 0, 0, 4, 2, 6, 3, 5, 15, 15, 8, 9, 3, 3, 5, 2, 0, 0, 0, 0}, 162}, + {12, { 0, 0, 0, 10, 6, 0, 9, 21, 8, 14, 11, 2, 0, 0, 0, 0, 0, 0, 0, 0}, 243}, + {13, { 1, 0, 0, 4, 4, 0, 4, 12, 12, 12, 18, 10, 4, 0, 0, 0, 0, 0, 0, 0}, 324}, + {11, { 0, 0, 0, 9, 0, 16, 13, 8, 23, 8, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 405}, + {12, { 1, 0, 2, 1, 0, 4, 5, 10, 14, 15, 8, 4, 0, 0, 0, 0, 0, 0, 0, 0}, 486}, + {10, { 0, 0, 1, 5, 7, 10, 14, 15, 8, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 550}, + {15, { 1, 0, 2, 1, 0, 4, 3, 8, 11, 20, 31, 38, 32, 14, 4, 0, 0, 0, 0, 0}, 614}, + {12, { 0, 0, 0, 3, 8, 14, 17, 25, 31, 41, 22, 8, 0, 0, 0, 0, 0, 0, 0, 0}, 783}, + {12, { 0, 0, 0, 2, 6, 7, 16, 59, 55, 95, 43, 6, 0, 0, 0, 0, 0, 0, 0, 0}, 952}, +}; + +const signed short huffTabSpec[1241] PROGMEM = { + /* spectrum table 1 [81] (signed) */ + 0x0000, 0x0200, 0x0e00, 0x0007, 0x0040, 0x0001, 0x0038, 0x0008, 0x01c0, 0x03c0, 0x0e40, 0x0039, 0x0078, 0x01c8, 0x000f, 0x0240, + 0x003f, 0x0fc0, 0x01f8, 0x0238, 0x0047, 0x0e08, 0x0009, 0x0208, 0x01c1, 0x0048, 0x0041, 0x0e38, 0x0201, 0x0e07, 0x0207, 0x0e01, + 0x01c7, 0x0278, 0x0e78, 0x03c8, 0x004f, 0x0079, 0x01c9, 0x01cf, 0x03f8, 0x0239, 0x007f, 0x0e48, 0x0e0f, 0x0fc8, 0x01f9, 0x03c1, + 0x03c7, 0x0e47, 0x0ff8, 0x01ff, 0x0049, 0x020f, 0x0241, 0x0e41, 0x0248, 0x0fc1, 0x0e3f, 0x0247, 0x023f, 0x0e39, 0x0fc7, 0x0e09, + 0x0209, 0x03cf, 0x0e79, 0x0e4f, 0x03f9, 0x0249, 0x0fc9, 0x027f, 0x0fcf, 0x0fff, 0x0279, 0x03c9, 0x0e49, 0x0e7f, 0x0ff9, 0x03ff, + 0x024f, + /* spectrum table 2 [81] (signed) */ + 0x0000, 0x0200, 0x0e00, 0x0001, 0x0038, 0x0007, 0x01c0, 0x0008, 0x0040, 0x01c8, 0x0e40, 0x0078, 0x000f, 0x0047, 0x0039, 0x0e07, + 0x03c0, 0x0238, 0x0fc0, 0x003f, 0x0208, 0x0201, 0x01c1, 0x0e08, 0x0041, 0x01f8, 0x0e01, 0x01c7, 0x0e38, 0x0240, 0x0048, 0x0009, + 0x0207, 0x0079, 0x0239, 0x0e78, 0x01cf, 0x03c8, 0x0247, 0x0209, 0x0e48, 0x01f9, 0x0248, 0x0e0f, 0x0ff8, 0x0e39, 0x03f8, 0x0278, + 0x03c1, 0x0e47, 0x0fc8, 0x0e09, 0x0fc1, 0x0fc7, 0x01ff, 0x020f, 0x023f, 0x007f, 0x0049, 0x0e41, 0x0e3f, 0x004f, 0x03c7, 0x01c9, + 0x0241, 0x03cf, 0x0e79, 0x03f9, 0x0fff, 0x0e4f, 0x0e49, 0x0249, 0x0fcf, 0x03c9, 0x0e7f, 0x0fc9, 0x027f, 0x03ff, 0x0ff9, 0x0279, + 0x024f, + /* spectrum table 3 [81] (unsigned) */ + 0x0000, 0x1200, 0x1001, 0x1040, 0x1008, 0x2240, 0x2009, 0x2048, 0x2041, 0x2208, 0x3049, 0x2201, 0x3248, 0x4249, 0x3209, 0x3241, + 0x1400, 0x1002, 0x200a, 0x2440, 0x3288, 0x2011, 0x3051, 0x2280, 0x304a, 0x3448, 0x1010, 0x2088, 0x2050, 0x1080, 0x2042, 0x2408, + 0x4289, 0x3089, 0x3250, 0x4251, 0x3281, 0x2210, 0x3211, 0x2081, 0x4449, 0x424a, 0x3441, 0x320a, 0x2012, 0x3052, 0x3488, 0x3290, + 0x2202, 0x2401, 0x3091, 0x2480, 0x4291, 0x3242, 0x3409, 0x4252, 0x4489, 0x2090, 0x308a, 0x3212, 0x3481, 0x3450, 0x3490, 0x3092, + 0x4491, 0x4451, 0x428a, 0x4292, 0x2082, 0x2410, 0x3282, 0x3411, 0x444a, 0x3442, 0x4492, 0x448a, 0x4452, 0x340a, 0x2402, 0x3482, + 0x3412, + /* spectrum table 4 [81] (unsigned) */ + 0x4249, 0x3049, 0x3241, 0x3248, 0x3209, 0x1200, 0x2240, 0x0000, 0x2009, 0x2208, 0x2201, 0x2048, 0x1001, 0x2041, 0x1008, 0x1040, + 0x4449, 0x4251, 0x4289, 0x424a, 0x3448, 0x3441, 0x3288, 0x3409, 0x3051, 0x304a, 0x3250, 0x3089, 0x320a, 0x3281, 0x3242, 0x3211, + 0x2440, 0x2408, 0x2280, 0x2401, 0x2042, 0x2088, 0x200a, 0x2050, 0x2081, 0x2202, 0x2011, 0x2210, 0x1400, 0x1002, 0x1080, 0x1010, + 0x4291, 0x4489, 0x4451, 0x4252, 0x428a, 0x444a, 0x3290, 0x3488, 0x3450, 0x3091, 0x3052, 0x3481, 0x308a, 0x3411, 0x3212, 0x4491, + 0x3282, 0x340a, 0x3442, 0x4292, 0x4452, 0x448a, 0x2090, 0x2480, 0x2012, 0x2410, 0x2082, 0x2402, 0x4492, 0x3092, 0x3490, 0x3482, + 0x3412, + /* spectrum table 5 [81] (signed) */ + 0x0000, 0x03e0, 0x0020, 0x0001, 0x001f, 0x003f, 0x03e1, 0x03ff, 0x0021, 0x03c0, 0x0002, 0x0040, 0x001e, 0x03df, 0x0041, 0x03fe, + 0x0022, 0x03c1, 0x005f, 0x03e2, 0x003e, 0x03a0, 0x0060, 0x001d, 0x0003, 0x03bf, 0x0023, 0x0061, 0x03fd, 0x03a1, 0x007f, 0x003d, + 0x03e3, 0x03c2, 0x0042, 0x03de, 0x005e, 0x03be, 0x007e, 0x03c3, 0x005d, 0x0062, 0x0043, 0x03a2, 0x03dd, 0x001c, 0x0380, 0x0081, + 0x0080, 0x039f, 0x0004, 0x009f, 0x03fc, 0x0024, 0x03e4, 0x0381, 0x003c, 0x007d, 0x03bd, 0x03a3, 0x03c4, 0x039e, 0x0082, 0x005c, + 0x0044, 0x0063, 0x0382, 0x03dc, 0x009e, 0x007c, 0x039d, 0x0383, 0x0064, 0x03a4, 0x0083, 0x009d, 0x03bc, 0x009c, 0x0384, 0x0084, + 0x039c, + /* spectrum table 6 [81] (signed) */ + 0x0000, 0x0020, 0x001f, 0x0001, 0x03e0, 0x0021, 0x03e1, 0x003f, 0x03ff, 0x005f, 0x0041, 0x03c1, 0x03df, 0x03c0, 0x03e2, 0x0040, + 0x003e, 0x0022, 0x001e, 0x03fe, 0x0002, 0x005e, 0x03c2, 0x03de, 0x0042, 0x03a1, 0x0061, 0x007f, 0x03e3, 0x03bf, 0x0023, 0x003d, + 0x03fd, 0x0060, 0x03a0, 0x001d, 0x0003, 0x0062, 0x03be, 0x03c3, 0x0043, 0x007e, 0x005d, 0x03dd, 0x03a2, 0x0063, 0x007d, 0x03bd, + 0x03a3, 0x003c, 0x03fc, 0x0081, 0x0381, 0x039f, 0x0024, 0x009f, 0x03e4, 0x001c, 0x0382, 0x039e, 0x0044, 0x03dc, 0x0380, 0x0082, + 0x009e, 0x03c4, 0x0080, 0x005c, 0x0004, 0x03bc, 0x03a4, 0x007c, 0x009d, 0x0064, 0x0083, 0x0383, 0x039d, 0x0084, 0x0384, 0x039c, + 0x009c, + /* spectrum table 7 [64] (unsigned) */ + 0x0000, 0x0420, 0x0401, 0x0821, 0x0841, 0x0822, 0x0440, 0x0402, 0x0861, 0x0823, 0x0842, 0x0460, 0x0403, 0x0843, 0x0862, 0x0824, + 0x0881, 0x0825, 0x08a1, 0x0863, 0x0844, 0x0404, 0x0480, 0x0882, 0x0845, 0x08a2, 0x0405, 0x08c1, 0x04a0, 0x0826, 0x0883, 0x0865, + 0x0864, 0x08a3, 0x0846, 0x08c2, 0x0827, 0x0866, 0x0406, 0x04c0, 0x0884, 0x08e1, 0x0885, 0x08e2, 0x08a4, 0x08c3, 0x0847, 0x08e3, + 0x08c4, 0x08a5, 0x0886, 0x0867, 0x04e0, 0x0407, 0x08c5, 0x08a6, 0x08e4, 0x0887, 0x08a7, 0x08e5, 0x08e6, 0x08c6, 0x08c7, 0x08e7, + /* spectrum table 8 [64] (unsigned) */ + 0x0821, 0x0841, 0x0420, 0x0822, 0x0401, 0x0842, 0x0000, 0x0440, 0x0402, 0x0861, 0x0823, 0x0862, 0x0843, 0x0863, 0x0881, 0x0824, + 0x0882, 0x0844, 0x0460, 0x0403, 0x0883, 0x0864, 0x08a2, 0x08a1, 0x0845, 0x0825, 0x08a3, 0x0865, 0x0884, 0x08a4, 0x0404, 0x0885, + 0x0480, 0x0846, 0x08c2, 0x08c1, 0x0826, 0x0866, 0x08c3, 0x08a5, 0x04a0, 0x08c4, 0x0405, 0x0886, 0x08e1, 0x08e2, 0x0847, 0x08c5, + 0x08e3, 0x0827, 0x08a6, 0x0867, 0x08c6, 0x08e4, 0x04c0, 0x0887, 0x0406, 0x08e5, 0x08e6, 0x08c7, 0x08a7, 0x04e0, 0x0407, 0x08e7, + /* spectrum table 9 [169] (unsigned) */ + 0x0000, 0x0420, 0x0401, 0x0821, 0x0841, 0x0822, 0x0440, 0x0402, 0x0861, 0x0842, 0x0823, 0x0460, 0x0403, 0x0843, 0x0862, 0x0824, + 0x0881, 0x0844, 0x0825, 0x0882, 0x0863, 0x0404, 0x0480, 0x08a1, 0x0845, 0x0826, 0x0864, 0x08a2, 0x08c1, 0x0883, 0x0405, 0x0846, + 0x04a0, 0x0827, 0x0865, 0x0828, 0x0901, 0x0884, 0x08a3, 0x08c2, 0x08e1, 0x0406, 0x0902, 0x0848, 0x0866, 0x0847, 0x0885, 0x0921, + 0x0829, 0x08e2, 0x04c0, 0x08a4, 0x08c3, 0x0903, 0x0407, 0x0922, 0x0868, 0x0886, 0x0867, 0x0408, 0x0941, 0x08c4, 0x0849, 0x08a5, + 0x0500, 0x04e0, 0x08e3, 0x0942, 0x0923, 0x0904, 0x082a, 0x08e4, 0x08c5, 0x08a6, 0x0888, 0x0887, 0x0869, 0x0961, 0x08a8, 0x0520, + 0x0905, 0x0943, 0x084a, 0x0409, 0x0962, 0x0924, 0x08c6, 0x0981, 0x0889, 0x0906, 0x082b, 0x0925, 0x0944, 0x08a7, 0x08e5, 0x084b, + 0x082c, 0x0982, 0x0963, 0x086a, 0x08a9, 0x08c7, 0x0907, 0x0964, 0x040a, 0x08e6, 0x0983, 0x0540, 0x0945, 0x088a, 0x08c8, 0x084c, + 0x0926, 0x0927, 0x088b, 0x0560, 0x08c9, 0x086b, 0x08aa, 0x0908, 0x08e8, 0x0985, 0x086c, 0x0965, 0x08e7, 0x0984, 0x0966, 0x0946, + 0x088c, 0x08e9, 0x08ab, 0x040b, 0x0986, 0x08ca, 0x0580, 0x0947, 0x08ac, 0x08ea, 0x0928, 0x040c, 0x0967, 0x0909, 0x0929, 0x0948, + 0x08eb, 0x0987, 0x08cb, 0x090b, 0x0968, 0x08ec, 0x08cc, 0x090a, 0x0949, 0x090c, 0x092a, 0x092b, 0x092c, 0x094b, 0x0989, 0x094a, + 0x0969, 0x0988, 0x096a, 0x098a, 0x098b, 0x094c, 0x096b, 0x096c, 0x098c, + /* spectrum table 10 [169] (unsigned) */ + 0x0821, 0x0822, 0x0841, 0x0842, 0x0420, 0x0401, 0x0823, 0x0862, 0x0861, 0x0843, 0x0863, 0x0440, 0x0402, 0x0844, 0x0882, 0x0824, + 0x0881, 0x0000, 0x0883, 0x0864, 0x0460, 0x0403, 0x0884, 0x0845, 0x08a2, 0x0825, 0x08a1, 0x08a3, 0x0865, 0x08a4, 0x0885, 0x08c2, + 0x0846, 0x08c3, 0x0480, 0x08c1, 0x0404, 0x0826, 0x0866, 0x08a5, 0x08c4, 0x0886, 0x08c5, 0x08e2, 0x0867, 0x0847, 0x08a6, 0x0902, + 0x08e3, 0x04a0, 0x08e1, 0x0405, 0x0901, 0x0827, 0x0903, 0x08e4, 0x0887, 0x0848, 0x08c6, 0x08e5, 0x0828, 0x0868, 0x0904, 0x0888, + 0x08a7, 0x0905, 0x08a8, 0x08e6, 0x08c7, 0x0922, 0x04c0, 0x08c8, 0x0923, 0x0869, 0x0921, 0x0849, 0x0406, 0x0906, 0x0924, 0x0889, + 0x0942, 0x0829, 0x08e7, 0x0907, 0x0925, 0x08e8, 0x0943, 0x08a9, 0x0944, 0x084a, 0x0941, 0x086a, 0x0926, 0x08c9, 0x0500, 0x088a, + 0x04e0, 0x0962, 0x08e9, 0x0963, 0x0946, 0x082a, 0x0961, 0x0927, 0x0407, 0x0908, 0x0945, 0x086b, 0x08aa, 0x0909, 0x0965, 0x0408, + 0x0964, 0x084b, 0x08ea, 0x08ca, 0x0947, 0x088b, 0x082b, 0x0982, 0x0928, 0x0983, 0x0966, 0x08ab, 0x0984, 0x0967, 0x0985, 0x086c, + 0x08cb, 0x0520, 0x0948, 0x0540, 0x0981, 0x0409, 0x088c, 0x0929, 0x0986, 0x084c, 0x090a, 0x092a, 0x082c, 0x0968, 0x0987, 0x08eb, + 0x08ac, 0x08cc, 0x0949, 0x090b, 0x0988, 0x040a, 0x08ec, 0x0560, 0x094a, 0x0969, 0x096a, 0x040b, 0x096b, 0x092b, 0x094b, 0x0580, + 0x090c, 0x0989, 0x094c, 0x092c, 0x096c, 0x098b, 0x040c, 0x098a, 0x098c, + /* spectrum table 11 [289] (unsigned) */ + 0x0000, 0x2041, 0x2410, 0x1040, 0x1001, 0x2081, 0x2042, 0x2082, 0x2043, 0x20c1, 0x20c2, 0x1080, 0x2083, 0x1002, 0x20c3, 0x2101, + 0x2044, 0x2102, 0x2084, 0x2103, 0x20c4, 0x10c0, 0x1003, 0x2141, 0x2142, 0x2085, 0x2104, 0x2045, 0x2143, 0x20c5, 0x2144, 0x2105, + 0x2182, 0x2086, 0x2181, 0x2183, 0x20c6, 0x2046, 0x2110, 0x20d0, 0x2405, 0x2403, 0x2404, 0x2184, 0x2406, 0x1100, 0x2106, 0x1004, + 0x2090, 0x2145, 0x2150, 0x2407, 0x2402, 0x2408, 0x2087, 0x21c2, 0x20c7, 0x2185, 0x2146, 0x2190, 0x240a, 0x21c3, 0x21c1, 0x2409, + 0x21d0, 0x2050, 0x2047, 0x2107, 0x240b, 0x21c4, 0x240c, 0x2210, 0x2401, 0x2186, 0x2250, 0x2088, 0x2147, 0x2290, 0x240d, 0x2203, + 0x2202, 0x20c8, 0x1140, 0x240e, 0x22d0, 0x21c5, 0x2108, 0x2187, 0x21c6, 0x1005, 0x2204, 0x240f, 0x2310, 0x2048, 0x2201, 0x2390, + 0x2148, 0x2350, 0x20c9, 0x2205, 0x21c7, 0x2089, 0x2206, 0x2242, 0x2243, 0x23d0, 0x2109, 0x2188, 0x1180, 0x2244, 0x2149, 0x2207, + 0x21c8, 0x2049, 0x2283, 0x1006, 0x2282, 0x2241, 0x2245, 0x210a, 0x208a, 0x2246, 0x20ca, 0x2189, 0x2284, 0x2208, 0x2285, 0x2247, + 0x22c3, 0x204a, 0x11c0, 0x2286, 0x21c9, 0x20cb, 0x214a, 0x2281, 0x210b, 0x22c2, 0x2342, 0x218a, 0x2343, 0x208b, 0x1400, 0x214b, + 0x22c5, 0x22c4, 0x2248, 0x21ca, 0x2209, 0x1010, 0x210d, 0x1007, 0x20cd, 0x22c6, 0x2341, 0x2344, 0x2303, 0x208d, 0x2345, 0x220a, + 0x218b, 0x2288, 0x2287, 0x2382, 0x2304, 0x204b, 0x210c, 0x22c1, 0x20cc, 0x204d, 0x2302, 0x21cb, 0x20ce, 0x214c, 0x214d, 0x2384, + 0x210e, 0x22c7, 0x2383, 0x2305, 0x2346, 0x2306, 0x1200, 0x22c8, 0x208c, 0x2249, 0x2385, 0x218d, 0x228a, 0x23c2, 0x220b, 0x224a, + 0x2386, 0x2289, 0x214e, 0x22c9, 0x2381, 0x208e, 0x218c, 0x204c, 0x2348, 0x1008, 0x2347, 0x21cc, 0x2307, 0x21cd, 0x23c3, 0x2301, + 0x218e, 0x208f, 0x23c5, 0x23c4, 0x204e, 0x224b, 0x210f, 0x2387, 0x220d, 0x2349, 0x220c, 0x214f, 0x20cf, 0x228b, 0x22ca, 0x2308, + 0x23c6, 0x23c7, 0x220e, 0x23c1, 0x21ce, 0x1240, 0x1009, 0x224d, 0x224c, 0x2309, 0x2388, 0x228d, 0x2389, 0x230a, 0x218f, 0x21cf, + 0x224e, 0x23c8, 0x22cb, 0x22ce, 0x204f, 0x228c, 0x228e, 0x234b, 0x234a, 0x22cd, 0x22cc, 0x220f, 0x238b, 0x234c, 0x230d, 0x23c9, + 0x238a, 0x1280, 0x230b, 0x224f, 0x100a, 0x230c, 0x12c0, 0x230e, 0x228f, 0x234d, 0x100d, 0x238c, 0x23ca, 0x23cb, 0x22cf, 0x238d, + 0x1340, 0x100b, 0x234e, 0x23cc, 0x23cd, 0x230f, 0x1380, 0x238e, 0x234f, 0x1300, 0x238f, 0x100e, 0x100c, 0x23ce, 0x13c0, 0x100f, + 0x23cf, +}; + +const HuffInfo huffTabScaleFactInfo PROGMEM = + {19, { 1, 0, 1, 3, 2, 4, 3, 5, 4, 6, 6, 6, 5, 8, 4, 7, 3, 7, 46, 0}, 0}; + +/* note - includes offset of -60 (4.6.2.3 in spec) */ +const signed short huffTabScaleFact[121] PROGMEM = { + /* scale factor table [121] */ + 0, -1, 1, -2, 2, -3, 3, -4, 4, -5, 5, 6, -6, 7, -7, 8, + -8, 9, -9, 10, -10, -11, 11, 12, -12, 13, -13, 14, -14, 16, 15, 17, + 18, -15, -17, -16, 19, -18, -19, 20, -20, 21, -21, 22, -22, 23, -23, -25, + 25, -27, -24, -26, 24, -28, 27, 29, -30, -29, 26, -31, -34, -33, -32, -36, + 28, -35, -38, -37, 30, -39, -41, -57, -59, -58, -60, 38, 39, 40, 41, 42, + 57, 37, 31, 32, 33, 34, 35, 36, 44, 51, 52, 53, 54, 55, 56, 50, + 45, 46, 47, 48, 49, 58, -54, -52, -51, -50, -55, 43, 60, 59, -56, -53, + -45, -44, -42, -40, -43, -49, -48, -46, -47, +}; + diff --git a/components/spotify/cspot/bell/libhelix-aac/imdct.c b/components/spotify/cspot/bell/libhelix-aac/imdct.c new file mode 100644 index 00000000..1ff9e4ab --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/imdct.c @@ -0,0 +1,589 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: imdct.c,v 1.1 2005/02/26 01:47:35 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * imdct.c - inverse MDCT + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +#define RND_VAL (1 << (FBITS_OUT_IMDCT-1)) + +#ifndef AAC_ENABLE_SBR + +/************************************************************************************** + * Function: DecWindowOverlap + * + * Description: apply synthesis window, do overlap-add, clip to 16-bit PCM, + * for winSequence LONG-LONG + * + * Inputs: input buffer (output of type-IV DCT) + * overlap buffer (saved from last time) + * number of channels + * window type (sin or KBD) for input buffer + * window type (sin or KBD) for overlap buffer + * + * Outputs: one channel, one frame of 16-bit PCM, interleaved by nChans + * + * Return: none + * + * Notes: this processes one channel at a time, but skips every other sample in + * the output buffer (pcm) for stereo interleaving + * this should fit in registers on ARM + * + * TODO: ARM5E version with saturating overlap/add (QADD) + * asm code with free pointer updates, better load scheduling + **************************************************************************************/ +/*__attribute__ ((section (".data")))*/ static void DecWindowOverlap(int *buf0, int *over0, short *pcm0, int nChans, int winTypeCurr, int winTypePrev) +{ + int in, w0, w1, f0, f1; + int *buf1, *over1; + short *pcm1; + const int *wndPrev, *wndCurr; + + buf0 += (1024 >> 1); + buf1 = buf0 - 1; + pcm1 = pcm0 + (1024 - 1) * nChans; + over1 = over0 + 1024 - 1; + + wndPrev = (winTypePrev == 1 ? kbdWindow + kbdWindowOffset[1] : sinWindow + sinWindowOffset[1]); + if (winTypeCurr == winTypePrev) { + /* cut window loads in half since current and overlap sections use same symmetric window */ + do { + w0 = *wndPrev++; + w1 = *wndPrev++; + in = *buf0++; + + f0 = MULSHIFT32(w0, in); + f1 = MULSHIFT32(w1, in); + + in = *over0; + *pcm0 = CLIPTOSHORT( (in - f0 + RND_VAL) >> FBITS_OUT_IMDCT ); + pcm0 += nChans; + + in = *over1; + *pcm1 = CLIPTOSHORT( (in + f1 + RND_VAL) >> FBITS_OUT_IMDCT ); + pcm1 -= nChans; + + in = *buf1--; + *over1-- = MULSHIFT32(w0, in); + *over0++ = MULSHIFT32(w1, in); + } while (over0 < over1); + } else { + /* different windows for current and overlap parts - should still fit in registers on ARM w/o stack spill */ + wndCurr = (winTypeCurr == 1 ? kbdWindow + kbdWindowOffset[1] : sinWindow + sinWindowOffset[1]); + do { + w0 = *wndPrev++; + w1 = *wndPrev++; + in = *buf0++; + + f0 = MULSHIFT32(w0, in); + f1 = MULSHIFT32(w1, in); + + in = *over0; + *pcm0 = CLIPTOSHORT( (in - f0 + RND_VAL) >> FBITS_OUT_IMDCT ); + pcm0 += nChans; + + in = *over1; + *pcm1 = CLIPTOSHORT( (in + f1 + RND_VAL) >> FBITS_OUT_IMDCT ); + pcm1 -= nChans; + + w0 = *wndCurr++; + w1 = *wndCurr++; + in = *buf1--; + + *over1-- = MULSHIFT32(w0, in); + *over0++ = MULSHIFT32(w1, in); + } while (over0 < over1); + } +} + +/************************************************************************************** + * Function: DecWindowOverlapLongStart + * + * Description: apply synthesis window, do overlap-add, clip to 16-bit PCM, + * for winSequence LONG-START + * + * Inputs: input buffer (output of type-IV DCT) + * overlap buffer (saved from last time) + * number of channels + * window type (sin or KBD) for input buffer + * window type (sin or KBD) for overlap buffer + * + * Outputs: one channel, one frame of 16-bit PCM, interleaved by nChans + * + * Return: none + * + * Notes: this processes one channel at a time, but skips every other sample in + * the output buffer (pcm) for stereo interleaving + * this should fit in registers on ARM + * + * TODO: ARM5E version with saturating overlap/add (QADD) + * asm code with free pointer updates, better load scheduling + **************************************************************************************/ + /*__attribute__ ((section (".data")))*/ static void DecWindowOverlapLongStart(int *buf0, int *over0, short *pcm0, int nChans, int winTypeCurr, int winTypePrev) +{ + int i, in, w0, w1, f0, f1; + int *buf1, *over1; + short *pcm1; + const int *wndPrev, *wndCurr; + + buf0 += (1024 >> 1); + buf1 = buf0 - 1; + pcm1 = pcm0 + (1024 - 1) * nChans; + over1 = over0 + 1024 - 1; + + wndPrev = (winTypePrev == 1 ? kbdWindow + kbdWindowOffset[1] : sinWindow + sinWindowOffset[1]); + i = 448; /* 2 outputs, 2 overlaps per loop */ + do { + w0 = *wndPrev++; + w1 = *wndPrev++; + in = *buf0++; + + f0 = MULSHIFT32(w0, in); + f1 = MULSHIFT32(w1, in); + + in = *over0; + *pcm0 = CLIPTOSHORT( (in - f0 + RND_VAL) >> FBITS_OUT_IMDCT ); + pcm0 += nChans; + + in = *over1; + *pcm1 = CLIPTOSHORT( (in + f1 + RND_VAL) >> FBITS_OUT_IMDCT ); + pcm1 -= nChans; + + in = *buf1--; + + *over1-- = 0; /* Wn = 0 for n = (2047, 2046, ... 1600) */ + *over0++ = in >> 1; /* Wn = 1 for n = (1024, 1025, ... 1471) */ + } while (--i); + + wndCurr = (winTypeCurr == 1 ? kbdWindow + kbdWindowOffset[0] : sinWindow + sinWindowOffset[0]); + + /* do 64 more loops - 2 outputs, 2 overlaps per loop */ + do { + w0 = *wndPrev++; + w1 = *wndPrev++; + in = *buf0++; + + f0 = MULSHIFT32(w0, in); + f1 = MULSHIFT32(w1, in); + + in = *over0; + *pcm0 = CLIPTOSHORT( (in - f0 + RND_VAL) >> FBITS_OUT_IMDCT ); + pcm0 += nChans; + + in = *over1; + *pcm1 = CLIPTOSHORT( (in + f1 + RND_VAL) >> FBITS_OUT_IMDCT ); + pcm1 -= nChans; + + w0 = *wndCurr++; /* W[0], W[1], ... --> W[255], W[254], ... */ + w1 = *wndCurr++; /* W[127], W[126], ... --> W[128], W[129], ... */ + in = *buf1--; + + *over1-- = MULSHIFT32(w0, in); /* Wn = short window for n = (1599, 1598, ... , 1536) */ + *over0++ = MULSHIFT32(w1, in); /* Wn = short window for n = (1472, 1473, ... , 1535) */ + } while (over0 < over1); +} + +/************************************************************************************** + * Function: DecWindowOverlapLongStop + * + * Description: apply synthesis window, do overlap-add, clip to 16-bit PCM, + * for winSequence LONG-STOP + * + * Inputs: input buffer (output of type-IV DCT) + * overlap buffer (saved from last time) + * number of channels + * window type (sin or KBD) for input buffer + * window type (sin or KBD) for overlap buffer + * + * Outputs: one channel, one frame of 16-bit PCM, interleaved by nChans + * + * Return: none + * + * Notes: this processes one channel at a time, but skips every other sample in + * the output buffer (pcm) for stereo interleaving + * this should fit in registers on ARM + * + * TODO: ARM5E version with saturating overlap/add (QADD) + * asm code with free pointer updates, better load scheduling + **************************************************************************************/ + /*__attribute__ ((section (".data")))*/ static void DecWindowOverlapLongStop(int *buf0, int *over0, short *pcm0, int nChans, int winTypeCurr, int winTypePrev) +{ + int i, in, w0, w1, f0, f1; + int *buf1, *over1; + short *pcm1; + const int *wndPrev, *wndCurr; + + buf0 += (1024 >> 1); + buf1 = buf0 - 1; + pcm1 = pcm0 + (1024 - 1) * nChans; + over1 = over0 + 1024 - 1; + + wndPrev = (winTypePrev == 1 ? kbdWindow + kbdWindowOffset[0] : sinWindow + sinWindowOffset[0]); + wndCurr = (winTypeCurr == 1 ? kbdWindow + kbdWindowOffset[1] : sinWindow + sinWindowOffset[1]); + + i = 448; /* 2 outputs, 2 overlaps per loop */ + do { + /* Wn = 0 for n = (0, 1, ... 447) */ + /* Wn = 1 for n = (576, 577, ... 1023) */ + in = *buf0++; + f1 = in >> 1; /* scale since skipping multiply by Q31 */ + + in = *over0; + *pcm0 = CLIPTOSHORT( (in + RND_VAL) >> FBITS_OUT_IMDCT ); + pcm0 += nChans; + + in = *over1; + *pcm1 = CLIPTOSHORT( (in + f1 + RND_VAL) >> FBITS_OUT_IMDCT ); + pcm1 -= nChans; + + w0 = *wndCurr++; + w1 = *wndCurr++; + in = *buf1--; + + *over1-- = MULSHIFT32(w0, in); + *over0++ = MULSHIFT32(w1, in); + } while (--i); + + /* do 64 more loops - 2 outputs, 2 overlaps per loop */ + do { + w0 = *wndPrev++; /* W[0], W[1], ...W[63] */ + w1 = *wndPrev++; /* W[127], W[126], ... W[64] */ + in = *buf0++; + + f0 = MULSHIFT32(w0, in); + f1 = MULSHIFT32(w1, in); + + in = *over0; + *pcm0 = CLIPTOSHORT( (in - f0 + RND_VAL) >> FBITS_OUT_IMDCT ); + pcm0 += nChans; + + in = *over1; + *pcm1 = CLIPTOSHORT( (in + f1 + RND_VAL) >> FBITS_OUT_IMDCT ); + pcm1 -= nChans; + + w0 = *wndCurr++; + w1 = *wndCurr++; + in = *buf1--; + + *over1-- = MULSHIFT32(w0, in); + *over0++ = MULSHIFT32(w1, in); + } while (over0 < over1); +} + +/************************************************************************************** + * Function: DecWindowOverlapShort + * + * Description: apply synthesis window, do overlap-add, clip to 16-bit PCM, + * for winSequence EIGHT-SHORT (does all 8 short blocks) + * + * Inputs: input buffer (output of type-IV DCT) + * overlap buffer (saved from last time) + * number of channels + * window type (sin or KBD) for input buffer + * window type (sin or KBD) for overlap buffer + * + * Outputs: one channel, one frame of 16-bit PCM, interleaved by nChans + * + * Return: none + * + * Notes: this processes one channel at a time, but skips every other sample in + * the output buffer (pcm) for stereo interleaving + * this should fit in registers on ARM + * + * TODO: ARM5E version with saturating overlap/add (QADD) + * asm code with free pointer updates, better load scheduling + **************************************************************************************/ + /*__attribute__ ((section (".data"))) */ static void DecWindowOverlapShort(int *buf0, int *over0, short *pcm0, int nChans, int winTypeCurr, int winTypePrev) +{ + int i, in, w0, w1, f0, f1; + int *buf1, *over1; + short *pcm1; + const int *wndPrev, *wndCurr; + + wndPrev = (winTypePrev == 1 ? kbdWindow + kbdWindowOffset[0] : sinWindow + sinWindowOffset[0]); + wndCurr = (winTypeCurr == 1 ? kbdWindow + kbdWindowOffset[0] : sinWindow + sinWindowOffset[0]); + + /* pcm[0-447] = 0 + overlap[0-447] */ + i = 448; + do { + f0 = *over0++; + f1 = *over0++; + *pcm0 = CLIPTOSHORT( (f0 + RND_VAL) >> FBITS_OUT_IMDCT ); pcm0 += nChans; + *pcm0 = CLIPTOSHORT( (f1 + RND_VAL) >> FBITS_OUT_IMDCT ); pcm0 += nChans; + i -= 2; + } while (i); + + /* pcm[448-575] = Wp[0-127] * block0[0-127] + overlap[448-575] */ + pcm1 = pcm0 + (128 - 1) * nChans; + over1 = over0 + 128 - 1; + buf0 += 64; + buf1 = buf0 - 1; + do { + w0 = *wndPrev++; /* W[0], W[1], ...W[63] */ + w1 = *wndPrev++; /* W[127], W[126], ... W[64] */ + in = *buf0++; + + f0 = MULSHIFT32(w0, in); + f1 = MULSHIFT32(w1, in); + + in = *over0; + *pcm0 = CLIPTOSHORT( (in - f0 + RND_VAL) >> FBITS_OUT_IMDCT ); + pcm0 += nChans; + + in = *over1; + *pcm1 = CLIPTOSHORT( (in + f1 + RND_VAL) >> FBITS_OUT_IMDCT ); + pcm1 -= nChans; + + w0 = *wndCurr++; + w1 = *wndCurr++; + in = *buf1--; + + /* save over0/over1 for next short block, in the slots just vacated */ + *over1-- = MULSHIFT32(w0, in); + *over0++ = MULSHIFT32(w1, in); + } while (over0 < over1); + + /* pcm[576-703] = Wc[128-255] * block0[128-255] + Wc[0-127] * block1[0-127] + overlap[576-703] + * pcm[704-831] = Wc[128-255] * block1[128-255] + Wc[0-127] * block2[0-127] + overlap[704-831] + * pcm[832-959] = Wc[128-255] * block2[128-255] + Wc[0-127] * block3[0-127] + overlap[832-959] + */ + for (i = 0; i < 3; i++) { + pcm0 += 64 * nChans; + pcm1 = pcm0 + (128 - 1) * nChans; + over0 += 64; + over1 = over0 + 128 - 1; + buf0 += 64; + buf1 = buf0 - 1; + wndCurr -= 128; + + do { + w0 = *wndCurr++; /* W[0], W[1], ...W[63] */ + w1 = *wndCurr++; /* W[127], W[126], ... W[64] */ + in = *buf0++; + + f0 = MULSHIFT32(w0, in); + f1 = MULSHIFT32(w1, in); + + in = *(over0 - 128); /* from last short block */ + in += *(over0 + 0); /* from last full frame */ + *pcm0 = CLIPTOSHORT( (in - f0 + RND_VAL) >> FBITS_OUT_IMDCT ); + pcm0 += nChans; + + in = *(over1 - 128); /* from last short block */ + in += *(over1 + 0); /* from last full frame */ + *pcm1 = CLIPTOSHORT( (in + f1 + RND_VAL) >> FBITS_OUT_IMDCT ); + pcm1 -= nChans; + + /* save over0/over1 for next short block, in the slots just vacated */ + in = *buf1--; + *over1-- = MULSHIFT32(w0, in); + *over0++ = MULSHIFT32(w1, in); + } while (over0 < over1); + } + + /* pcm[960-1023] = Wc[128-191] * block3[128-191] + Wc[0-63] * block4[0-63] + overlap[960-1023] + * over[0-63] = Wc[192-255] * block3[192-255] + Wc[64-127] * block4[64-127] + */ + pcm0 += 64 * nChans; + over0 -= 832; /* points at overlap[64] */ + over1 = over0 + 128 - 1; /* points at overlap[191] */ + buf0 += 64; + buf1 = buf0 - 1; + wndCurr -= 128; + do { + w0 = *wndCurr++; /* W[0], W[1], ...W[63] */ + w1 = *wndCurr++; /* W[127], W[126], ... W[64] */ + in = *buf0++; + + f0 = MULSHIFT32(w0, in); + f1 = MULSHIFT32(w1, in); + + in = *(over0 + 768); /* from last short block */ + in += *(over0 + 896); /* from last full frame */ + *pcm0 = CLIPTOSHORT( (in - f0 + RND_VAL) >> FBITS_OUT_IMDCT ); + pcm0 += nChans; + + in = *(over1 + 768); /* from last short block */ + *(over1 - 128) = in + f1; + + in = *buf1--; + *over1-- = MULSHIFT32(w0, in); /* save in overlap[128-191] */ + *over0++ = MULSHIFT32(w1, in); /* save in overlap[64-127] */ + } while (over0 < over1); + + /* over0 now points at overlap[128] */ + + /* over[64-191] = Wc[128-255] * block4[128-255] + Wc[0-127] * block5[0-127] + * over[192-319] = Wc[128-255] * block5[128-255] + Wc[0-127] * block6[0-127] + * over[320-447] = Wc[128-255] * block6[128-255] + Wc[0-127] * block7[0-127] + * over[448-576] = Wc[128-255] * block7[128-255] + */ + for (i = 0; i < 3; i++) { + over0 += 64; + over1 = over0 + 128 - 1; + buf0 += 64; + buf1 = buf0 - 1; + wndCurr -= 128; + do { + w0 = *wndCurr++; /* W[0], W[1], ...W[63] */ + w1 = *wndCurr++; /* W[127], W[126], ... W[64] */ + in = *buf0++; + + f0 = MULSHIFT32(w0, in); + f1 = MULSHIFT32(w1, in); + + /* from last short block */ + *(over0 - 128) -= f0; + *(over1 - 128)+= f1; + + in = *buf1--; + *over1-- = MULSHIFT32(w0, in); + *over0++ = MULSHIFT32(w1, in); + } while (over0 < over1); + } + + /* over[576-1024] = 0 */ + i = 448; + over0 += 64; + do { + *over0++ = 0; + *over0++ = 0; + *over0++ = 0; + *over0++ = 0; + i -= 4; + } while (i); +} + +#endif /* !AAC_ENABLE_SBR */ + +/************************************************************************************** + * Function: IMDCT + * + * Description: inverse transform and convert to 16-bit PCM + * + * Inputs: valid AACDecInfo struct + * index of current channel (0 for SCE/LFE, 0 or 1 for CPE) + * output channel (range = [0, nChans-1]) + * + * Outputs: complete frame of decoded PCM, after inverse transform + * + * Return: 0 if successful, -1 if error + * + * Notes: If AAC_ENABLE_SBR is defined at compile time then window + overlap + * does NOT clip to 16-bit PCM and does NOT interleave channels + * If AAC_ENABLE_SBR is NOT defined at compile time, then window + overlap + * does clip to 16-bit PCM and interleaves channels + * If SBR is enabled at compile time, but we don't know whether it is + * actually used for this frame (e.g. the first frame of a stream), + * we need to produce both clipped 16-bit PCM in outbuf AND + * unclipped 32-bit PCM in the SBR input buffer. In this case we make + * a separate pass over the 32-bit PCM to produce 16-bit PCM output. + * This inflicts a slight performance hit when decoding non-SBR files. + **************************************************************************************/ +int IMDCT(AACDecInfo *aacDecInfo, int ch, int chOut, short *outbuf) +{ + int i; + PSInfoBase *psi; + ICSInfo *icsInfo; + + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoBase) + return -1; + psi = (PSInfoBase *)(aacDecInfo->psInfoBase); + icsInfo = (ch == 1 && psi->commonWin == 1) ? &(psi->icsInfo[0]) : &(psi->icsInfo[ch]); + outbuf += chOut; + + /* optimized type-IV DCT (operates inplace) */ + if (icsInfo->winSequence == 2) { + /* 8 short blocks */ + for (i = 0; i < 8; i++) + DCT4(0, psi->coef[ch] + i*128, psi->gbCurrent[ch]); + } else { + /* 1 long block */ + DCT4(1, psi->coef[ch], psi->gbCurrent[ch]); + } + +#ifdef AAC_ENABLE_SBR + /* window, overlap-add, don't clip to short (send to SBR decoder) + * store the decoded 32-bit samples in top half (second AAC_MAX_NSAMPS samples) of coef buffer + */ + if (icsInfo->winSequence == 0) + DecWindowOverlapNoClip(psi->coef[ch], psi->overlap[chOut], psi->sbrWorkBuf[ch], icsInfo->winShape, psi->prevWinShape[chOut]); + else if (icsInfo->winSequence == 1) + DecWindowOverlapLongStartNoClip(psi->coef[ch], psi->overlap[chOut], psi->sbrWorkBuf[ch], icsInfo->winShape, psi->prevWinShape[chOut]); + else if (icsInfo->winSequence == 2) + DecWindowOverlapShortNoClip(psi->coef[ch], psi->overlap[chOut], psi->sbrWorkBuf[ch], icsInfo->winShape, psi->prevWinShape[chOut]); + else if (icsInfo->winSequence == 3) + DecWindowOverlapLongStopNoClip(psi->coef[ch], psi->overlap[chOut], psi->sbrWorkBuf[ch], icsInfo->winShape, psi->prevWinShape[chOut]); + + if (!aacDecInfo->sbrEnabled) { + for (i = 0; i < AAC_MAX_NSAMPS; i++) { + *outbuf = CLIPTOSHORT((psi->sbrWorkBuf[ch][i] + RND_VAL) >> FBITS_OUT_IMDCT); + outbuf += aacDecInfo->nChans; + } + } + + aacDecInfo->rawSampleBuf[ch] = psi->sbrWorkBuf[ch]; + aacDecInfo->rawSampleBytes = sizeof(int); + aacDecInfo->rawSampleFBits = FBITS_OUT_IMDCT; +#else + /* window, overlap-add, round to PCM - optimized for each window sequence */ + if (icsInfo->winSequence == 0) + DecWindowOverlap(psi->coef[ch], psi->overlap[chOut], outbuf, aacDecInfo->nChans, icsInfo->winShape, psi->prevWinShape[chOut]); + else if (icsInfo->winSequence == 1) + DecWindowOverlapLongStart(psi->coef[ch], psi->overlap[chOut], outbuf, aacDecInfo->nChans, icsInfo->winShape, psi->prevWinShape[chOut]); + else if (icsInfo->winSequence == 2) + DecWindowOverlapShort(psi->coef[ch], psi->overlap[chOut], outbuf, aacDecInfo->nChans, icsInfo->winShape, psi->prevWinShape[chOut]); + else if (icsInfo->winSequence == 3) + DecWindowOverlapLongStop(psi->coef[ch], psi->overlap[chOut], outbuf, aacDecInfo->nChans, icsInfo->winShape, psi->prevWinShape[chOut]); + + aacDecInfo->rawSampleBuf[ch] = 0; + aacDecInfo->rawSampleBytes = 0; + aacDecInfo->rawSampleFBits = 0; +#endif + + psi->prevWinShape[chOut] = icsInfo->winShape; + + return 0; +} diff --git a/components/spotify/cspot/bell/libhelix-aac/noiseless.c b/components/spotify/cspot/bell/libhelix-aac/noiseless.c new file mode 100644 index 00000000..5d3a65df --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/noiseless.c @@ -0,0 +1,484 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: noiseless.c,v 1.1 2005/02/26 01:47:35 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * noiseless.c - decode channel info, scalefactors, quantized coefficients, + * scalefactor band codebook, and TNS coefficients from bitstream + **************************************************************************************/ + +#include "coder.h" + +//#include "profile.h" + +//#define PROFILE_START(x) +//#define PROFILE_END() + +/************************************************************************************** + * Function: DecodeICSInfo + * + * Description: decode individual channel stream info + * + * Inputs: BitStreamInfo struct pointing to start of ICS info + * (14496-3, table 4.4.6) + * sample rate index + * + * Outputs: updated icsInfo struct + * + * Return: none + **************************************************************************************/ +/* __attribute__ ((section (".data"))) */ void DecodeICSInfo(BitStreamInfo *bsi, ICSInfo *icsInfo, int sampRateIdx) +{ + int sfb, g, mask; + + icsInfo->icsResBit = GetBits(bsi, 1); + icsInfo->winSequence = GetBits(bsi, 2); + icsInfo->winShape = GetBits(bsi, 1); + if (icsInfo->winSequence == 2) { + /* short block */ + icsInfo->maxSFB = GetBits(bsi, 4); + icsInfo->sfGroup = GetBits(bsi, 7); + icsInfo->numWinGroup = 1; + icsInfo->winGroupLen[0] = 1; + mask = 0x40; /* start with bit 6 */ + for (g = 0; g < 7; g++) { + if (icsInfo->sfGroup & mask) { + icsInfo->winGroupLen[icsInfo->numWinGroup - 1]++; + } else { + icsInfo->numWinGroup++; + icsInfo->winGroupLen[icsInfo->numWinGroup - 1] = 1; + } + mask >>= 1; + } + } else { + /* long block */ + icsInfo->maxSFB = GetBits(bsi, 6); + icsInfo->predictorDataPresent = GetBits(bsi, 1); + if (icsInfo->predictorDataPresent) { + icsInfo->predictorReset = GetBits(bsi, 1); + if (icsInfo->predictorReset) + icsInfo->predictorResetGroupNum = GetBits(bsi, 5); + for (sfb = 0; sfb < MIN(icsInfo->maxSFB, predSFBMax[sampRateIdx]); sfb++) + icsInfo->predictionUsed[sfb] = GetBits(bsi, 1); + } + icsInfo->numWinGroup = 1; + icsInfo->winGroupLen[0] = 1; + } +} + +/************************************************************************************** + * Function: DecodeSectionData + * + * Description: decode section data (scale factor band groupings and + * associated Huffman codebooks) + * + * Inputs: BitStreamInfo struct pointing to start of ICS info + * (14496-3, table 4.4.25) + * window sequence (short or long blocks) + * number of window groups (1 for long blocks, 1-8 for short blocks) + * max coded scalefactor band + * + * Outputs: index of Huffman codebook for each scalefactor band in each section + * + * Return: none + * + * Notes: sectCB, sectEnd, sfbCodeBook, ordered by window groups for short blocks + **************************************************************************************/ +/* __attribute__ ((section (".data"))) */ static void DecodeSectionData(BitStreamInfo *bsi, int winSequence, int numWinGrp, int maxSFB, unsigned char *sfbCodeBook) +{ + int g, cb, sfb; + int sectLen, sectLenBits, sectLenIncr, sectEscapeVal; + + sectLenBits = (winSequence == 2 ? 3 : 5); + sectEscapeVal = (1 << sectLenBits) - 1; + + for (g = 0; g < numWinGrp; g++) { + sfb = 0; + while (sfb < maxSFB) { + cb = GetBits(bsi, 4); /* next section codebook */ + sectLen = 0; + do { + sectLenIncr = GetBits(bsi, sectLenBits); + sectLen += sectLenIncr; + } while (sectLenIncr == sectEscapeVal); + + sfb += sectLen; + while (sectLen--) + *sfbCodeBook++ = (unsigned char)cb; + } + ASSERT(sfb == maxSFB); + } +} + +/************************************************************************************** + * Function: DecodeOneScaleFactor + * + * Description: decode one scalefactor using scalefactor Huffman codebook + * + * Inputs: BitStreamInfo struct pointing to start of next coded scalefactor + * + * Outputs: updated BitstreamInfo struct + * + * Return: one decoded scalefactor, including index_offset of -60 + **************************************************************************************/ +static int DecodeOneScaleFactor(BitStreamInfo *bsi) +{ + int nBits, val; + unsigned int bitBuf; + + /* decode next scalefactor from bitstream */ + bitBuf = GetBitsNoAdvance(bsi, huffTabScaleFactInfo.maxBits) << (32 - huffTabScaleFactInfo.maxBits); + //PROFILE_START("DecodeHuffmanScalar"); + nBits = DecodeHuffmanScalar(huffTabScaleFact, &huffTabScaleFactInfo, bitBuf, &val); + AdvanceBitstream(bsi, nBits); + //PROFILE_END(); + return val; +} + +/************************************************************************************** + * Function: DecodeScaleFactors + * + * Description: decode scalefactors, PNS energy, and intensity stereo weights + * + * Inputs: BitStreamInfo struct pointing to start of ICS info + * (14496-3, table 4.4.26) + * number of window groups (1 for long blocks, 1-8 for short blocks) + * max coded scalefactor band + * global gain (starting value for differential scalefactor coding) + * index of Huffman codebook for each scalefactor band in each section + * + * Outputs: decoded scalefactor for each section + * + * Return: none + * + * Notes: sfbCodeBook, scaleFactors ordered by window groups for short blocks + * for section with codebook 13, scaleFactors buffer has decoded PNS + * energy instead of regular scalefactor + * for section with codebook 14 or 15, scaleFactors buffer has intensity + * stereo weight instead of regular scalefactor + **************************************************************************************/ +/* __attribute__ ((section (".data"))) */ static void DecodeScaleFactors(BitStreamInfo *bsi, int numWinGrp, int maxSFB, int globalGain, + unsigned char *sfbCodeBook, short *scaleFactors) +{ + int g, sfbCB, nrg, npf, val, sf, is; + + /* starting values for differential coding */ + sf = globalGain; + is = 0; + nrg = globalGain - 90 - 256; + npf = 1; + + for (g = 0; g < numWinGrp * maxSFB; g++) { + sfbCB = *sfbCodeBook++; + + if (sfbCB == 14 || sfbCB == 15) { + /* intensity stereo - differential coding */ + val = DecodeOneScaleFactor(bsi); + is += val; + *scaleFactors++ = (short)is; + } else if (sfbCB == 13) { + /* PNS - first energy is directly coded, rest are Huffman coded (npf = noise_pcm_flag) */ + if (npf) { + val = GetBits(bsi, 9); + npf = 0; + } else { + val = DecodeOneScaleFactor(bsi); + } + nrg += val; + *scaleFactors++ = (short)nrg; + } else if (sfbCB >= 1 && sfbCB <= 11) { + /* regular (non-zero) region - differential coding */ + val = DecodeOneScaleFactor(bsi); + sf += val; + *scaleFactors++ = (short)sf; + } else { + /* inactive scalefactor band if codebook 0 */ + *scaleFactors++ = 0; + } + } +} + +/************************************************************************************** + * Function: DecodePulseInfo + * + * Description: decode pulse information + * + * Inputs: BitStreamInfo struct pointing to start of pulse info + * (14496-3, table 4.4.7) + * + * Outputs: updated PulseInfo struct + * + * Return: none + **************************************************************************************/ +/* __attribute__ ((section (".data"))) */ static void DecodePulseInfo(BitStreamInfo *bsi, PulseInfo *pi) +{ + int i; + + pi->numPulse = GetBits(bsi, 2) + 1; /* add 1 here */ + pi->startSFB = GetBits(bsi, 6); + for (i = 0; i < pi->numPulse; i++) { + pi->offset[i] = GetBits(bsi, 5); + pi->amp[i] = GetBits(bsi, 4); + } +} + +/************************************************************************************** + * Function: DecodeTNSInfo + * + * Description: decode TNS filter information + * + * Inputs: BitStreamInfo struct pointing to start of TNS info + * (14496-3, table 4.4.27) + * window sequence (short or long blocks) + * + * Outputs: updated TNSInfo struct + * buffer of decoded (signed) TNS filter coefficients + * + * Return: none + **************************************************************************************/ +static const signed char sgnMask[3] = {0x02, 0x04, 0x08}; +static const signed char negMask[3] = {~0x03, ~0x07, ~0x0f}; + +static void DecodeTNSInfo(BitStreamInfo *bsi, int winSequence, TNSInfo *ti, signed char *tnsCoef) +{ + int i, w, f, coefBits, compress; + signed char c, s, n; + + unsigned char *filtLength, *filtOrder, *filtDir; + + filtLength = ti->length; + filtOrder = ti->order; + filtDir = ti->dir; + + if (winSequence == 2) { + /* short blocks */ + for (w = 0; w < NWINDOWS_SHORT; w++) { + ti->numFilt[w] = GetBits(bsi, 1); + if (ti->numFilt[w]) { + ti->coefRes[w] = GetBits(bsi, 1) + 3; + *filtLength = GetBits(bsi, 4); + *filtOrder = GetBits(bsi, 3); + if (*filtOrder) { + *filtDir++ = GetBits(bsi, 1); + compress = GetBits(bsi, 1); + coefBits = (int)ti->coefRes[w] - compress; /* 2, 3, or 4 */ + s = sgnMask[coefBits - 2]; + n = negMask[coefBits - 2]; + for (i = 0; i < *filtOrder; i++) { + c = GetBits(bsi, coefBits); + if (c & s) c |= n; + *tnsCoef++ = c; + } + } + filtLength++; + filtOrder++; + } + } + } else { + /* long blocks */ + ti->numFilt[0] = GetBits(bsi, 2); + if (ti->numFilt[0]) + ti->coefRes[0] = GetBits(bsi, 1) + 3; + for (f = 0; f < ti->numFilt[0]; f++) { + *filtLength = GetBits(bsi, 6); + *filtOrder = GetBits(bsi, 5); + if (*filtOrder) { + *filtDir++ = GetBits(bsi, 1); + compress = GetBits(bsi, 1); + coefBits = (int)ti->coefRes[0] - compress; /* 2, 3, or 4 */ + s = sgnMask[coefBits - 2]; + n = negMask[coefBits - 2]; + for (i = 0; i < *filtOrder; i++) { + c = GetBits(bsi, coefBits); + if (c & s) c |= n; + *tnsCoef++ = c; + } + } + filtLength++; + filtOrder++; + } + } +} + +/* bitstream field lengths for gain control data: + * gainBits[winSequence][0] = maxWindow (how many gain windows there are) + * gainBits[winSequence][1] = locBitsZero (bits for alocCode if window == 0) + * gainBits[winSequence][2] = locBits (bits for alocCode if window != 0) + */ +static const unsigned char gainBits[4][3] = { + {1, 5, 5}, /* long */ + {2, 4, 2}, /* start */ + {8, 2, 2}, /* short */ + {2, 4, 5}, /* stop */ +}; + +/************************************************************************************** + * Function: DecodeGainControlInfo + * + * Description: decode gain control information (SSR profile only) + * + * Inputs: BitStreamInfo struct pointing to start of gain control info + * (14496-3, table 4.4.12) + * window sequence (short or long blocks) + * + * Outputs: updated GainControlInfo struct + * + * Return: none + **************************************************************************************/ +static void DecodeGainControlInfo(BitStreamInfo *bsi, int winSequence, GainControlInfo *gi) +{ + int bd, wd, ad; + int locBits, locBitsZero, maxWin; + + gi->maxBand = GetBits(bsi, 2); + maxWin = (int)gainBits[winSequence][0]; + locBitsZero = (int)gainBits[winSequence][1]; + locBits = (int)gainBits[winSequence][2]; + + for (bd = 1; bd <= gi->maxBand; bd++) { + for (wd = 0; wd < maxWin; wd++) { + gi->adjNum[bd][wd] = GetBits(bsi, 3); + for (ad = 0; ad < gi->adjNum[bd][wd]; ad++) { + gi->alevCode[bd][wd][ad] = GetBits(bsi, 4); + gi->alocCode[bd][wd][ad] = GetBits(bsi, (wd == 0 ? locBitsZero : locBits)); + } + } + } +} + +/************************************************************************************** + * Function: DecodeICS + * + * Description: decode individual channel stream + * + * Inputs: platform specific info struct + * BitStreamInfo struct pointing to start of individual channel stream + * (14496-3, table 4.4.24) + * index of current channel + * + * Outputs: updated section data, scale factor data, pulse data, TNS data, + * and gain control data + * + * Return: none + **************************************************************************************/ +static void DecodeICS(PSInfoBase *psi, BitStreamInfo *bsi, int ch) +{ + int globalGain; + ICSInfo *icsInfo; + PulseInfo *pi; + TNSInfo *ti; + GainControlInfo *gi; + + icsInfo = (ch == 1 && psi->commonWin == 1) ? &(psi->icsInfo[0]) : &(psi->icsInfo[ch]); + + globalGain = GetBits(bsi, 8); + if (!psi->commonWin) + DecodeICSInfo(bsi, icsInfo, psi->sampRateIdx); + + DecodeSectionData(bsi, icsInfo->winSequence, icsInfo->numWinGroup, icsInfo->maxSFB, psi->sfbCodeBook[ch]); + + DecodeScaleFactors(bsi, icsInfo->numWinGroup, icsInfo->maxSFB, globalGain, psi->sfbCodeBook[ch], psi->scaleFactors[ch]); + + pi = &psi->pulseInfo[ch]; + pi->pulseDataPresent = GetBits(bsi, 1); + if (pi->pulseDataPresent) + DecodePulseInfo(bsi, pi); + + ti = &psi->tnsInfo[ch]; + ti->tnsDataPresent = GetBits(bsi, 1); + if (ti->tnsDataPresent) + DecodeTNSInfo(bsi, icsInfo->winSequence, ti, ti->coef); + + gi = &psi->gainControlInfo[ch]; + gi->gainControlDataPresent = GetBits(bsi, 1); + if (gi->gainControlDataPresent) + DecodeGainControlInfo(bsi, icsInfo->winSequence, gi); +} + +/************************************************************************************** + * Function: DecodeNoiselessData + * + * Description: decode noiseless data (side info and transform coefficients) + * + * Inputs: valid AACDecInfo struct + * double pointer to buffer pointing to start of individual channel stream + * (14496-3, table 4.4.24) + * pointer to bit offset + * pointer to number of valid bits remaining in buf + * index of current channel + * + * Outputs: updated global gain, section data, scale factor data, pulse data, + * TNS data, gain control data, and spectral data + * + * Return: 0 if successful, error code (< 0) if error + **************************************************************************************/ +int DecodeNoiselessData(AACDecInfo *aacDecInfo, unsigned char **buf, int *bitOffset, int *bitsAvail, int ch) +{ + int bitsUsed; + BitStreamInfo bsi; + PSInfoBase *psi; + ICSInfo *icsInfo; + + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoBase) + return ERR_AAC_NULL_POINTER; + psi = (PSInfoBase *)(aacDecInfo->psInfoBase); + icsInfo = (ch == 1 && psi->commonWin == 1) ? &(psi->icsInfo[0]) : &(psi->icsInfo[ch]); + + SetBitstreamPointer(&bsi, (*bitsAvail+7) >> 3, *buf); + GetBits(&bsi, *bitOffset); + + DecodeICS(psi, &bsi, ch); + + if (icsInfo->winSequence == 2) + DecodeSpectrumShort(psi, &bsi, ch); + else + DecodeSpectrumLong(psi, &bsi, ch); + + bitsUsed = CalcBitsUsed(&bsi, *buf, *bitOffset); + *buf += ((bitsUsed + *bitOffset) >> 3); + *bitOffset = ((bitsUsed + *bitOffset) & 0x07); + *bitsAvail -= bitsUsed; + + aacDecInfo->sbDeinterleaveReqd[ch] = 0; + aacDecInfo->tnsUsed |= psi->tnsInfo[ch].tnsDataPresent; /* set flag if TNS used for any channel */ + + return ERR_AAC_NONE; +} diff --git a/components/spotify/cspot/bell/libhelix-aac/pns.c b/components/spotify/cspot/bell/libhelix-aac/pns.c new file mode 100644 index 00000000..3594c76d --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/pns.c @@ -0,0 +1,357 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: pns.c,v 1.2 2005/03/10 17:01:56 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * pns.c - perceptual noise substitution + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +/************************************************************************************** + * Function: Get32BitVal + * + * Description: generate 32-bit unsigned random number + * + * Inputs: last number calculated (seed, first time through) + * + * Outputs: new number, saved in *last + * + * Return: 32-bit number, uniformly distributed between [0, 2^32) + * + * Notes: uses simple linear congruential generator + **************************************************************************************/ +static unsigned int Get32BitVal(unsigned int *last) +{ + unsigned int r = *last; + + /* use same coefs as MPEG reference code (classic LCG) + * use unsigned multiply to force reliable wraparound behavior in C (mod 2^32) + */ + r = (1664525U * r) + 1013904223U; + *last = r; + + return r; +} + + +#define NUM_ITER_INVSQRT 4 + +#define X0_COEF_2 0xc0000000 /* Q29: -2.0 */ +#define X0_OFF_2 0x60000000 /* Q29: 3.0 */ +#define Q26_3 0x0c000000 /* Q26: 3.0 */ + +/************************************************************************************** + * Function: InvRootR + * + * Description: use Newton's method to solve for x = 1/sqrt(r) + * + * Inputs: r in Q30 format, range = [0.25, 1] (normalize inputs to this range) + * + * Outputs: none + * + * Return: x = Q29, range = (1, 2) + * + * Notes: guaranteed to converge and not overflow for any r in this range + * + * xn+1 = xn - f(xn)/f'(xn) + * f(x) = 1/sqrt(r) - x = 0 (find root) + * = 1/x^2 - r + * f'(x) = -2/x^3 + * + * so xn+1 = xn/2 * (3 - r*xn^2) + * + * NUM_ITER_INVSQRT = 3, maxDiff = 1.3747e-02 + * NUM_ITER_INVSQRT = 4, maxDiff = 3.9832e-04 + **************************************************************************************/ +static int InvRootR(int r) +{ + int i, xn, t; + + /* use linear equation for initial guess + * x0 = -2*r + 3 (so x0 always >= correct answer in range [0.25, 1)) + * xn = Q29 (at every step) + */ + xn = (MULSHIFT32(r, X0_COEF_2) << 2) + X0_OFF_2; + + for (i = 0; i < NUM_ITER_INVSQRT; i++) { + t = MULSHIFT32(xn, xn); /* Q26 = Q29*Q29 */ + t = Q26_3 - (MULSHIFT32(r, t) << 2); /* Q26 = Q26 - (Q31*Q26 << 1) */ + xn = MULSHIFT32(xn, t) << (6 - 1); /* Q29 = (Q29*Q26 << 6), and -1 for division by 2 */ + } + + /* clip to range (1.0, 2.0) + * (because of rounding, this can converge to xn slightly > 2.0 when r is near 0.25) + */ + if (xn >> 30) + xn = (1 << 30) - 1; + + return xn; +} + +/************************************************************************************** + * Function: ScaleNoiseVector + * + * Description: apply scaling to vector of noise coefficients for one scalefactor band + * + * Inputs: unscaled coefficients + * number of coefficients in vector (one scalefactor band of coefs) + * scalefactor for this band (i.e. noise energy) + * + * Outputs: nVals coefficients in Q(FBITS_OUT_DQ_OFF) + * + * Return: guard bit mask (OR of abs value of all noise coefs) + **************************************************************************************/ +static int ScaleNoiseVector(int *coef, int nVals, int sf) +{ + +/* pow(2, i/4.0) for i = [0,1,2,3], format = Q30 */ +static const int pow14[4] PROGMEM = { + 0x40000000, 0x4c1bf829, 0x5a82799a, 0x6ba27e65 +}; + + int i, c, spec, energy, sq, scalef, scalei, invSqrtEnergy, z, gbMask; + + energy = 0; + for (i = 0; i < nVals; i++) { + spec = coef[i]; + + /* max nVals = max SFB width = 96, so energy can gain < 2^7 bits in accumulation */ + sq = (spec * spec) >> 8; /* spec*spec range = (-2^30, 2^30) */ + energy += sq; + } + + /* unless nVals == 1 (or the number generator is broken...), this should not happen */ + if (energy == 0) + return 0; /* coef[i] must = 0 for i = [0, nVals-1], so gbMask = 0 */ + + /* pow(2, sf/4) * pow(2, FBITS_OUT_DQ_OFF) */ + scalef = pow14[sf & 0x3]; + scalei = (sf >> 2) + FBITS_OUT_DQ_OFF; + + /* energy has implied factor of 2^-8 since we shifted the accumulator + * normalize energy to range [0.25, 1.0), calculate 1/sqrt(1), and denormalize + * i.e. divide input by 2^(30-z) and convert to Q30 + * output of 1/sqrt(i) now has extra factor of 2^((30-z)/2) + * for energy > 0, z is an even number between 0 and 28 + * final scaling of invSqrtEnergy: + * 2^(15 - z/2) to compensate for implicit 2^(30-z) factor in input + * +4 to compensate for implicit 2^-8 factor in input + */ + z = CLZ(energy) - 2; /* energy has at least 2 leading zeros (see acc loop) */ + z &= 0xfffffffe; /* force even */ + invSqrtEnergy = InvRootR(energy << z); /* energy << z must be in range [0x10000000, 0x40000000] */ + scalei -= (15 - z/2 + 4); /* nInt = 1/sqrt(energy) in Q29 */ + + /* normalize for final scaling */ + z = CLZ(invSqrtEnergy) - 1; + invSqrtEnergy <<= z; + scalei -= (z - 3 - 2); /* -2 for scalef, z-3 for invSqrtEnergy */ + scalef = MULSHIFT32(scalef, invSqrtEnergy); /* scalef (input) = Q30, invSqrtEnergy = Q29 * 2^z */ + gbMask = 0; + + if (scalei < 0) { + scalei = -scalei; + if (scalei > 31) + scalei = 31; + for (i = 0; i < nVals; i++) { + c = MULSHIFT32(coef[i], scalef) >> scalei; + gbMask |= FASTABS(c); + coef[i] = c; + } + } else { + /* for scalei <= 16, no clipping possible (coef[i] is < 2^15 before scaling) + * for scalei > 16, just saturate exponent (rare) + * scalef is close to full-scale (since we normalized invSqrtEnergy) + * remember, we are just producing noise here + */ + if (scalei > 16) + scalei = 16; + for (i = 0; i < nVals; i++) { + c = MULSHIFT32(coef[i] << scalei, scalef); + coef[i] = c; + gbMask |= FASTABS(c); + } + } + + return gbMask; +} + +/************************************************************************************** + * Function: GenerateNoiseVector + * + * Description: create vector of noise coefficients for one scalefactor band + * + * Inputs: seed for number generator + * number of coefficients to generate + * + * Outputs: buffer of nVals coefficients, range = [-2^15, 2^15) + * updated seed for number generator + * + * Return: none + **************************************************************************************/ +static void GenerateNoiseVector(int *coef, int *last, int nVals) +{ + int i; + + for (i = 0; i < nVals; i++) + coef[i] = ((signed int)Get32BitVal((unsigned int *)last)) >> 16; +} + +/************************************************************************************** + * Function: CopyNoiseVector + * + * Description: copy vector of noise coefficients for one scalefactor band from L to R + * + * Inputs: buffer of left coefficients + * number of coefficients to copy + * + * Outputs: buffer of right coefficients + * + * Return: none + **************************************************************************************/ +static void CopyNoiseVector(int *coefL, int *coefR, int nVals) +{ + int i; + + for (i = 0; i < nVals; i++) + coefR[i] = coefL[i]; +} + +/************************************************************************************** + * Function: PNS + * + * Description: apply perceptual noise substitution, if enabled (MPEG-4 only) + * + * Inputs: valid AACDecInfo struct + * index of current channel + * + * Outputs: shaped noise in scalefactor bands where PNS is active + * updated minimum guard bit count for this channel + * + * Return: 0 if successful, -1 if error + **************************************************************************************/ +int PNS(AACDecInfo *aacDecInfo, int ch) +{ + int gp, sfb, win, width, nSamps, gb, gbMask; + int *coef; + const /*short*/ int *sfbTab; + unsigned char *sfbCodeBook; + short *scaleFactors; + int msMaskOffset, checkCorr, genNew; + unsigned char msMask; + unsigned char *msMaskPtr; + PSInfoBase *psi; + ICSInfo *icsInfo; + + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoBase) + return -1; + psi = (PSInfoBase *)(aacDecInfo->psInfoBase); + icsInfo = (ch == 1 && psi->commonWin == 1) ? &(psi->icsInfo[0]) : &(psi->icsInfo[ch]); + + if (!psi->pnsUsed[ch]) + return 0; + + if (icsInfo->winSequence == 2) { + sfbTab = sfBandTabShort + sfBandTabShortOffset[psi->sampRateIdx]; + nSamps = NSAMPS_SHORT; + } else { + sfbTab = sfBandTabLong + sfBandTabLongOffset[psi->sampRateIdx]; + nSamps = NSAMPS_LONG; + } + coef = psi->coef[ch]; + sfbCodeBook = psi->sfbCodeBook[ch]; + scaleFactors = psi->scaleFactors[ch]; + checkCorr = (aacDecInfo->currBlockID == AAC_ID_CPE && psi->commonWin == 1 ? 1 : 0); + + gbMask = 0; + for (gp = 0; gp < icsInfo->numWinGroup; gp++) { + for (win = 0; win < icsInfo->winGroupLen[gp]; win++) { + msMaskPtr = psi->msMaskBits + ((gp*icsInfo->maxSFB) >> 3); + msMaskOffset = ((gp*icsInfo->maxSFB) & 0x07); + msMask = (*msMaskPtr++) >> msMaskOffset; + + for (sfb = 0; sfb < icsInfo->maxSFB; sfb++) { + width = sfbTab[sfb+1] - sfbTab[sfb]; + if (sfbCodeBook[sfb] == 13) { + if (ch == 0) { + /* generate new vector, copy into ch 1 if it's possible that the channels will be correlated + * if ch 1 has PNS enabled for this SFB but it's uncorrelated (i.e. ms_used == 0), + * the copied values will be overwritten when we process ch 1 + */ + GenerateNoiseVector(coef, &psi->pnsLastVal, width); + if (checkCorr && psi->sfbCodeBook[1][gp*icsInfo->maxSFB + sfb] == 13) + CopyNoiseVector(coef, psi->coef[1] + (coef - psi->coef[0]), width); + } else { + /* generate new vector if no correlation between channels */ + genNew = 1; + if (checkCorr && psi->sfbCodeBook[0][gp*icsInfo->maxSFB + sfb] == 13) { + if ( (psi->msMaskPresent == 1 && (msMask & 0x01)) || psi->msMaskPresent == 2 ) + genNew = 0; + } + if (genNew) + GenerateNoiseVector(coef, &psi->pnsLastVal, width); + } + gbMask |= ScaleNoiseVector(coef, width, psi->scaleFactors[ch][gp*icsInfo->maxSFB + sfb]); + } + coef += width; + + /* get next mask bit (should be branchless on ARM) */ + msMask >>= 1; + if (++msMaskOffset == 8) { + msMask = *msMaskPtr++; + msMaskOffset = 0; + } + } + coef += (nSamps - sfbTab[icsInfo->maxSFB]); + } + sfbCodeBook += icsInfo->maxSFB; + scaleFactors += icsInfo->maxSFB; + } + + /* update guard bit count if necessary */ + gb = CLZ(gbMask) - 1; + if (psi->gbCurrent[ch] > gb) + psi->gbCurrent[ch] = gb; + + return 0; +} diff --git a/components/spotify/cspot/bell/libhelix-aac/readme.txt b/components/spotify/cspot/bell/libhelix-aac/readme.txt new file mode 100644 index 00000000..de4ca0ee --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/readme.txt @@ -0,0 +1,127 @@ +Fixed-point HE-AAC decoder +Developed by RealNetworks, 2005 +=============================== + +Overview +-------- +This module contains a high-performance HE-AAC decoder for 32-bit fixed-point +processors. The following is a summary of what is and is not supported: + +Supported: + - MPEG2, MPEG4 low complexity decoding (intensity stereo, M-S, TNS, PNS) + - spectral band replication (SBR), high-quality mode + - mono, stereo, and multichannel modes + - ADTS, ADIF, and raw data block file formats + +Not currently supported: + - main or SSR profile, LTP + - coupling channel elements (CCE) + - 960/1920-sample frame size + - low-power mode SBR + - downsampled (single-rate) SBR + - parametric stereo + +Highlights +---------- + - highly optimized for ARM processors (details in docs/ subdirectory) + - reference x86 implementation + - C and assembly code only (C++ not required for codec library) + - reentrant, statically linkable + - low memory (details in docs/ subdirectory) + - option to use Intel Integrated Performance Primitives (details below) + +Supported platforms and toolchains +---------------------------------- +This codec should run on any 32-bit fixed-point processor which can perform a full 32x32-bit +multiply (providing a 64-bit result). The following processors and toolchains are supported: + - x86, Microsoft Visual C++ + - x86, GNU toolchain (gcc) + - ARM, ARM Developer Suite (ADS) + - ARM, Microsoft Embedded Visual C++ + - ARM, GNU toolchain (gcc) + +ARM refers to any processor supporting ARM architecture v.4 or above. Thumb is not required. + +Generally ADS produces the fastest code. EVC 3 does not support inline assembly code for +ARM targets, so calls to MULSHIFT32 (smull on ARM) are left as function calls. This incurs +a significant performance penalty. For the fastest code on targets which do not normally use +ADS consider compiling with ADS, using the -S option to output assembly code, and +feeding this assembly code to the assembler of your choice. This might require some +syntax changes in the .S file. + +Adding support for a new processor is fairly simple. Simply add a new block to the file +real/assembly.h which implements the required inline assembly functions for your processor. +Something like + +... +#elif defined NEW_PROCESSOR + +/* you implement MULSHIFT32() and so forth */ + +#else +#error Unsupported platform in assembly.h +#endif + +Optionally you can rewrite or add assembly language files optimized for your platform. Note +that many of the algorithms are designed for an ARM-type processor, so performance of the +unmodified C code might be noticeably worse on other architectures. + +Adding support for a new toolchain is straightforward. Use the sample projects or the +Helix makefiles as a template for which source files to include. + +Multichannel +------------ +For multichannel, just set AAC_MAX_NCHANS in pub/aacdec.h to the desired max number +of channels (default = 2) and recompile. This increases RAM usage since more memory +is required to save state for multiple channels. See docs/memory.xls for details. + +Directory structure +------------------- +fixpt/ platform-independent code and tables, public API +fixpt/docs memory and CPU usage figures, callgraphs +fixpt/hxwrap Helix wrapper code and makefiles +fixpt/ipp source code which uses IPP for decoding (see the "IPP" section below) +fixpt/pub public header files +fixpt/real source code for RealNetworks' AAC decoder +fixpt/testwrap sample code to build a command-line test application + +Code organization +----------------- +fixpt/ + aacdec.c main decode functions, exports C-only API + aactabs.c common tables used by all implementations +fixpt/pub/ + aaccommon.h low-level codec API which aacdec.c calls + aacdec.h high-level codec API which applications call + statname.h symbols which get name-mangled by C preprocessor to allow static linking +fixpt/ipp source code for wrapper files which link in IPP libraries +fixpt/real full source code for RealNetworks AAC decoder, including SBR +fixpt/real/asm optimized assembly code files for certain platforms + +To build an AAC decoder library, you'll need to compile the top-level files and EITHER +real/*.c OR ipp/*.c and the appropriate IPP library. + +Decoder using Real code: aacdec.c + aactabs.c + real/*.c + real/asm/[platform]/*.s (if necessary) +Decoder using IPP code: aacdec.c + aactabs.c + ipp/*.c + ippac*.lib + +IPP +--- +For certain platforms Intel® has created highly-optimized object code libraries of DSP +routines. These are called the Intel® Integrated Performance Primitives (IPP). If IPP +libraries are available for a platform, this AAC decoder can link them in and use them +instead of the RealNetworks source code. To use IPP, you still need to build the top-level +files (aacdec.c, aactabs.c). You also build the files in ipp/*.c. These are just thin +wrappers which provide the glue logic between the top-level decode functions in +aacdec.c and the optimized DSP primitives in the IPP libraries. IPP libraries are not +included in this module. You must obtain them WITH A LICENSE directly from Intel. +Further info on the latest versions of IPP (as of the date of this readme) is available +from the URL below: + +http://www.intel.com/software/products/ipp/ + +This site explains how to obtain IPP and the terms under which IPP libraries may be used. +The code in this module is merely wrapper code which calls IPP functions. You are fully +responsible for adhering to the license agreement under which you obtain the IPP +libraries from Intel. + +readme.txt last updated 02/25/05 diff --git a/components/spotify/cspot/bell/libhelix-aac/sbr.c b/components/spotify/cspot/bell/libhelix-aac/sbr.c new file mode 100644 index 00000000..691694d0 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/sbr.c @@ -0,0 +1,431 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: sbr.c,v 1.3 2005/05/24 16:01:55 albertofloyd Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * sbr.c - top level functions for SBR + **************************************************************************************/ + +#if defined(USE_DEFAULT_STDLIB) || defined(ESP_PLATFORM) +#include +#include +#else +#include "hlxclib/stdlib.h" +#endif + +#include "sbr.h" + +/************************************************************************************** + * Function: InitSBRState + * + * Description: initialize PSInfoSBR struct at start of stream or after flush + * + * Inputs: valid AACDecInfo struct + * + * Outputs: PSInfoSBR struct with proper initial state + * + * Return: none + **************************************************************************************/ +static void InitSBRState(PSInfoSBR *psi) +{ + int i, ch; + unsigned char *c; + + if (!psi) + return; + + /* clear SBR state structure */ + c = (unsigned char *)psi; + for (i = 0; i < (int)sizeof(PSInfoSBR); i++) + *c++ = 0; + + /* initialize non-zero state variables */ + for (ch = 0; ch < AAC_MAX_NCHANS; ch++) { + psi->sbrChan[ch].reset = 1; + psi->sbrChan[ch].laPrev = -1; + } +} + +/************************************************************************************** + * Function: InitSBR + * + * Description: initialize SBR decoder + * + * Inputs: valid AACDecInfo struct + * + * Outputs: PSInfoSBR struct to hold SBR state information + * + * Return: 0 if successful, error code (< 0) if error + * + * Note: memory allocation for SBR is only done here + **************************************************************************************/ +int InitSBR(AACDecInfo *aacDecInfo) +{ + PSInfoSBR *psi; + + if (!aacDecInfo) + return ERR_AAC_NULL_POINTER; + + /* allocate SBR state structure */ + psi = (PSInfoSBR *)malloc(sizeof(PSInfoSBR)); + if (!psi) { + printf("OOM in SBR, can't allocate %d bytes\n", sizeof(PSInfoSBR)); + return ERR_AAC_SBR_INIT; + } + InitSBRState(psi); + + aacDecInfo->psInfoSBR = psi; + return ERR_AAC_NONE; +} + +int InitSBRPre(AACDecInfo *aacDecInfo, void **ptr, int *sz) +{ + PSInfoSBR *psi; + + if (!aacDecInfo) + return ERR_AAC_NULL_POINTER; + + /* allocate SBR state structure */ + psi = (PSInfoSBR *)*ptr; + *sz -= sizeof(PSInfoSBR); + if (*sz < 0) { + printf("OOM in SBR, can't allocate %d bytes\n", sizeof(PSInfoSBR)); + return ERR_AAC_SBR_INIT; + } + InitSBRState(psi); + + *ptr = (void*)((char*)(*ptr) + sizeof(PSInfoSBR)); + aacDecInfo->psInfoSBR = psi; + return ERR_AAC_NONE; +} + + + +/************************************************************************************** + * Function: FreeSBR + * + * Description: free SBR decoder + * + * Inputs: valid AACDecInfo struct + * + * Outputs: none + * + * Return: none + * + * Note: memory deallocation for SBR is only done here + **************************************************************************************/ +void FreeSBR(AACDecInfo *aacDecInfo) +{ + if (aacDecInfo && aacDecInfo->psInfoSBR) + free(aacDecInfo->psInfoSBR); + + return; +} + +/************************************************************************************** + * Function: DecodeSBRBitstream + * + * Description: decode sideband information for SBR + * + * Inputs: valid AACDecInfo struct + * fill buffer with SBR extension block + * number of bytes in fill buffer + * base output channel (range = [0, nChans-1]) + * + * Outputs: initialized state structs (SBRHdr, SBRGrid, SBRFreq, SBRChan) + * + * Return: 0 if successful, error code (< 0) if error + * + * Notes: SBR payload should be in aacDecInfo->fillBuf + * returns with no error if fill buffer is not an SBR extension block, + * or if current block is not a fill block (e.g. for LFE upsampling) + **************************************************************************************/ +int DecodeSBRBitstream(AACDecInfo *aacDecInfo, int chBase) +{ + int headerFlag; + BitStreamInfo bsi; + PSInfoSBR *psi; + + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoSBR) + return ERR_AAC_NULL_POINTER; + psi = (PSInfoSBR *)(aacDecInfo->psInfoSBR); + + if (aacDecInfo->currBlockID != AAC_ID_FIL || (aacDecInfo->fillExtType != EXT_SBR_DATA && aacDecInfo->fillExtType != EXT_SBR_DATA_CRC)) + return ERR_AAC_NONE; + + SetBitstreamPointer(&bsi, aacDecInfo->fillCount, aacDecInfo->fillBuf); + if (GetBits(&bsi, 4) != (unsigned int)aacDecInfo->fillExtType) + return ERR_AAC_SBR_BITSTREAM; + + if (aacDecInfo->fillExtType == EXT_SBR_DATA_CRC) + psi->crcCheckWord = GetBits(&bsi, 10); + + headerFlag = GetBits(&bsi, 1); + if (headerFlag) { + /* get sample rate index for output sample rate (2x base rate) */ + psi->sampRateIdx = GetSampRateIdx(2 * aacDecInfo->sampRate); + if (psi->sampRateIdx < 0 || psi->sampRateIdx >= NUM_SAMPLE_RATES) + return ERR_AAC_SBR_BITSTREAM; + else if (psi->sampRateIdx >= NUM_SAMPLE_RATES_SBR) + return ERR_AAC_SBR_SINGLERATE_UNSUPPORTED; + + /* reset flag = 1 if header values changed */ + if (UnpackSBRHeader(&bsi, &(psi->sbrHdr[chBase]))) + psi->sbrChan[chBase].reset = 1; + + /* first valid SBR header should always trigger CalcFreqTables(), since psi->reset was set in InitSBR() */ + if (psi->sbrChan[chBase].reset) + CalcFreqTables(&(psi->sbrHdr[chBase+0]), &(psi->sbrFreq[chBase]), psi->sampRateIdx); + + /* copy and reset state to right channel for CPE */ + if (aacDecInfo->prevBlockID == AAC_ID_CPE) + psi->sbrChan[chBase+1].reset = psi->sbrChan[chBase+0].reset; + } + + + /* if no header has been received, upsample only */ + if (psi->sbrHdr[chBase].count == 0) + return ERR_AAC_NONE; + + if (aacDecInfo->prevBlockID == AAC_ID_SCE) { + UnpackSBRSingleChannel(&bsi, psi, chBase); + } else if (aacDecInfo->prevBlockID == AAC_ID_CPE) { + UnpackSBRChannelPair(&bsi, psi, chBase); + } else { + return ERR_AAC_SBR_BITSTREAM; + } + + ByteAlignBitstream(&bsi); + + return ERR_AAC_NONE; +} + +/************************************************************************************** + * Function: DecodeSBRData + * + * Description: apply SBR to one frame of PCM data + * + * Inputs: 1024 samples of decoded 32-bit PCM, before SBR + * size of input PCM samples (must be 4 bytes) + * number of fraction bits in input PCM samples + * base output channel (range = [0, nChans-1]) + * initialized state structs (SBRHdr, SBRGrid, SBRFreq, SBRChan) + * + * Outputs: 2048 samples of decoded 16-bit PCM, after SBR + * + * Return: 0 if successful, error code (< 0) if error + **************************************************************************************/ +int DecodeSBRData(AACDecInfo *aacDecInfo, int chBase, short *outbuf) +{ + int k, l, ch, chBlock, qmfaBands, qmfsBands; + int upsampleOnly, gbIdx, gbMask; + int *inbuf; + short *outptr; + PSInfoSBR *psi; + SBRHeader *sbrHdr; + SBRGrid *sbrGrid; + SBRFreq *sbrFreq; + SBRChan *sbrChan; + + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoSBR) + return ERR_AAC_NULL_POINTER; + psi = (PSInfoSBR *)(aacDecInfo->psInfoSBR); + + /* same header and freq tables for both channels in CPE */ + sbrHdr = &(psi->sbrHdr[chBase]); + sbrFreq = &(psi->sbrFreq[chBase]); + + /* upsample only if we haven't received an SBR header yet or if we have an LFE block */ + if (aacDecInfo->currBlockID == AAC_ID_LFE) { + chBlock = 1; + upsampleOnly = 1; + } else if (aacDecInfo->currBlockID == AAC_ID_FIL) { + if (aacDecInfo->prevBlockID == AAC_ID_SCE) + chBlock = 1; + else if (aacDecInfo->prevBlockID == AAC_ID_CPE) + chBlock = 2; + else + return ERR_AAC_NONE; + + upsampleOnly = (sbrHdr->count == 0 ? 1 : 0); + if (aacDecInfo->fillExtType != EXT_SBR_DATA && aacDecInfo->fillExtType != EXT_SBR_DATA_CRC) + return ERR_AAC_NONE; + } else { + /* ignore non-SBR blocks */ + return ERR_AAC_NONE; + } + + if (upsampleOnly) { + sbrFreq->kStart = 32; + sbrFreq->numQMFBands = 0; + } + + for (ch = 0; ch < chBlock; ch++) { + sbrGrid = &(psi->sbrGrid[chBase + ch]); + sbrChan = &(psi->sbrChan[chBase + ch]); + + if (aacDecInfo->rawSampleBuf[ch] == 0 || aacDecInfo->rawSampleBytes != 4) + return ERR_AAC_SBR_PCM_FORMAT; + inbuf = (int *)aacDecInfo->rawSampleBuf[ch]; + outptr = outbuf + chBase + ch; + + /* restore delay buffers (could use ring buffer or keep in temp buffer for nChans == 1) */ + for (l = 0; l < HF_GEN; l++) { + for (k = 0; k < 64; k++) { + psi->XBuf[l][k][0] = psi->XBufDelay[chBase + ch][l][k][0]; + psi->XBuf[l][k][1] = psi->XBufDelay[chBase + ch][l][k][1]; + } + } + + /* step 1 - analysis QMF */ + qmfaBands = sbrFreq->kStart; + for (l = 0; l < 32; l++) { + gbMask = QMFAnalysis(inbuf + l*32, psi->delayQMFA[chBase + ch], psi->XBuf[l + HF_GEN][0], + aacDecInfo->rawSampleFBits, &(psi->delayIdxQMFA[chBase + ch]), qmfaBands); + + gbIdx = ((l + HF_GEN) >> 5) & 0x01; + sbrChan->gbMask[gbIdx] |= gbMask; /* gbIdx = (0 if i < 32), (1 if i >= 32) */ + } + + if (upsampleOnly) { + /* no SBR - just run synthesis QMF to upsample by 2x */ + qmfsBands = 32; + for (l = 0; l < 32; l++) { + /* step 4 - synthesis QMF */ + QMFSynthesis(psi->XBuf[l + HF_ADJ][0], psi->delayQMFS[chBase + ch], &(psi->delayIdxQMFS[chBase + ch]), qmfsBands, outptr, aacDecInfo->nChans); + outptr += 64*aacDecInfo->nChans; + } + } else { + /* if previous frame had lower SBR starting freq than current, zero out the synthesized QMF + * bands so they aren't used as sources for patching + * after patch generation, restore from delay buffer + * can only happen after header reset + */ + for (k = sbrFreq->kStartPrev; k < sbrFreq->kStart; k++) { + for (l = 0; l < sbrGrid->envTimeBorder[0] + HF_ADJ; l++) { + psi->XBuf[l][k][0] = 0; + psi->XBuf[l][k][1] = 0; + } + } + + /* step 2 - HF generation */ + GenerateHighFreq(psi, sbrGrid, sbrFreq, sbrChan, ch); + + /* restore SBR bands that were cleared before patch generation (time slots 0, 1 no longer needed) */ + for (k = sbrFreq->kStartPrev; k < sbrFreq->kStart; k++) { + for (l = HF_ADJ; l < sbrGrid->envTimeBorder[0] + HF_ADJ; l++) { + psi->XBuf[l][k][0] = psi->XBufDelay[chBase + ch][l][k][0]; + psi->XBuf[l][k][1] = psi->XBufDelay[chBase + ch][l][k][1]; + } + } + + /* step 3 - HF adjustment */ + AdjustHighFreq(psi, sbrHdr, sbrGrid, sbrFreq, sbrChan, ch); + + /* step 4 - synthesis QMF */ + qmfsBands = sbrFreq->kStartPrev + sbrFreq->numQMFBandsPrev; + for (l = 0; l < sbrGrid->envTimeBorder[0]; l++) { + /* if new envelope starts mid-frame, use old settings until start of first envelope in this frame */ + QMFSynthesis(psi->XBuf[l + HF_ADJ][0], psi->delayQMFS[chBase + ch], &(psi->delayIdxQMFS[chBase + ch]), qmfsBands, outptr, aacDecInfo->nChans); + outptr += 64*aacDecInfo->nChans; + } + + qmfsBands = sbrFreq->kStart + sbrFreq->numQMFBands; + for ( ; l < 32; l++) { + /* use new settings for rest of frame (usually the entire frame, unless the first envelope starts mid-frame) */ + QMFSynthesis(psi->XBuf[l + HF_ADJ][0], psi->delayQMFS[chBase + ch], &(psi->delayIdxQMFS[chBase + ch]), qmfsBands, outptr, aacDecInfo->nChans); + outptr += 64*aacDecInfo->nChans; + } + } + + /* save delay */ + for (l = 0; l < HF_GEN; l++) { + for (k = 0; k < 64; k++) { + psi->XBufDelay[chBase + ch][l][k][0] = psi->XBuf[l+32][k][0]; + psi->XBufDelay[chBase + ch][l][k][1] = psi->XBuf[l+32][k][1]; + } + } + sbrChan->gbMask[0] = sbrChan->gbMask[1]; + sbrChan->gbMask[1] = 0; + + if (sbrHdr->count > 0) + sbrChan->reset = 0; + } + sbrFreq->kStartPrev = sbrFreq->kStart; + sbrFreq->numQMFBandsPrev = sbrFreq->numQMFBands; + + if (aacDecInfo->nChans > 0 && (chBase + ch) == aacDecInfo->nChans) + psi->frameCount++; + + return ERR_AAC_NONE; +} + +/************************************************************************************** + * Function: FlushCodecSBR + * + * Description: flush internal SBR codec state (after seeking, for example) + * + * Inputs: valid AACDecInfo struct + * + * Outputs: updated state variables for SBR + * + * Return: 0 if successful, error code (< 0) if error + * + * Notes: SBR is heavily dependent on state from previous frames + * (e.g. delta coded scalefactors, previous envelope boundaries, etc.) + * On flush, we reset everything as if SBR had just been initialized + * for the first time. This triggers "upsample-only" mode until + * the first valid SBR header is received. Then SBR starts as usual. + **************************************************************************************/ +int FlushCodecSBR(AACDecInfo *aacDecInfo) +{ + PSInfoSBR *psi; + + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoSBR) + return ERR_AAC_NULL_POINTER; + psi = (PSInfoSBR *)(aacDecInfo->psInfoSBR); + + InitSBRState(psi); + + return 0; +} diff --git a/components/spotify/cspot/bell/libhelix-aac/sbr.h b/components/spotify/cspot/bell/libhelix-aac/sbr.h new file mode 100644 index 00000000..6c898a13 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/sbr.h @@ -0,0 +1,383 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: sbr.h,v 1.2 2005/05/20 18:05:41 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * sbr.h - definitions of platform-specific SBR data structures, functions, and tables + **************************************************************************************/ + +#ifndef _SBR_H +#define _SBR_H + +#include "aaccommon.h" +#include "bitstream.h" + +#ifndef ASSERT +#if defined(_WIN32) && defined(_M_IX86) && (defined (_DEBUG) || defined (REL_ENABLE_ASSERTS)) +#define ASSERT(x) if (!(x)) __asm int 3; +#else +#define ASSERT(x) /* do nothing */ +#endif +#endif + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#define NUM_TIME_SLOTS 16 +#define SAMPLES_PER_SLOT 2 /* RATE in spec */ +#define NUM_SAMPLE_RATES_SBR 9 /* downsampled (single-rate) mode unsupported, so only use Fs_sbr >= 16 kHz */ + +#define MAX_NUM_ENV 5 +#define MAX_NUM_NOISE_FLOORS 2 +#define MAX_NUM_NOISE_FLOOR_BANDS 5 /* max Nq, see 4.6.18.3.6 */ +#define MAX_NUM_PATCHES 5 +#define MAX_NUM_SMOOTH_COEFS 5 + +#define HF_GEN 8 +#define HF_ADJ 2 + +#define MAX_QMF_BANDS 48 /* max QMF subbands covered by SBR (4.6.18.3.6) */ + +#define FBITS_IN_QMFA 14 +#define FBITS_LOST_QMFA (1 + 2 + 3 + 2 + 1) /* 1 from cTab, 2 in premul, 3 in FFT, 2 in postmul, 1 for implicit scaling by 2.0 */ +#define FBITS_OUT_QMFA (FBITS_IN_QMFA - FBITS_LOST_QMFA) + +#define MIN_GBITS_IN_QMFS 2 +#define FBITS_IN_QMFS FBITS_OUT_QMFA +#define FBITS_LOST_DCT4_64 (2 + 3 + 2) /* 2 in premul, 3 in FFT, 2 in postmul */ + +#define FBITS_OUT_DQ_ENV 29 /* dequantized env scalefactors are Q(29 - envDataDequantScale) */ +#define FBITS_OUT_DQ_NOISE 24 /* range of Q_orig = [2^-24, 2^6] */ +#define NOISE_FLOOR_OFFSET 6 + +/* see comments in ApplyBoost() */ +#define FBITS_GLIM_BOOST 24 +#define FBITS_QLIM_BOOST 14 + +#define MAX_HUFF_BITS 20 +#define NUM_QMF_DELAY_BUFS 10 +#define DELAY_SAMPS_QMFA (NUM_QMF_DELAY_BUFS * 32) +#define DELAY_SAMPS_QMFS (NUM_QMF_DELAY_BUFS * 128) + +/* additional external symbols to name-mangle for static linking */ +#define FFT32C STATNAME(FFT32C) +#define CalcFreqTables STATNAME(CalcFreqTables) +#define AdjustHighFreq STATNAME(AdjustHighFreq) +#define GenerateHighFreq STATNAME(GenerateHighFreq) +#define DecodeSBREnvelope STATNAME(DecodeSBREnvelope) +#define DecodeSBRNoise STATNAME(DecodeSBRNoise) +#define UncoupleSBREnvelope STATNAME(UncoupleSBREnvelope) +#define UncoupleSBRNoise STATNAME(UncoupleSBRNoise) +#define InvRNormalized STATNAME(InvRNormalized) +#define RatioPowInv STATNAME(RatioPowInv) +#define SqrtFix STATNAME(SqrtFix) +#define QMFAnalysis STATNAME(QMFAnalysis) +#define QMFSynthesis STATNAME(QMFSynthesis) +#define GetSampRateIdx STATNAME(GetSampRateIdx) +#define UnpackSBRHeader STATNAME(UnpackSBRHeader) +#define UnpackSBRSingleChannel STATNAME(UnpackSBRSingleChannel) +#define UnpackSBRChannelPair STATNAME(UnpackSBRChannelPair) + +/* asm functions */ +#define CVKernel1 STATNAME(CVKernel1) +#define CVKernel2 STATNAME(CVKernel2) +#define QMFAnalysisConv STATNAME(QMFAnalysisConv) +#define QMFSynthesisConv STATNAME(QMFSynthesisConv) + +#define k0Tab STATNAME(k0Tab) +#define k2Tab STATNAME(k2Tab) +#define goalSBTab STATNAME(goalSBTab) +#define huffTabSBR STATNAME(huffTabSBR) +#define huffTabSBRInfo STATNAME(huffTabSBRInfo) +#define log2Tab STATNAME(log2Tab) +#define noiseTab STATNAME(noiseTab) +#define cTabA STATNAME(cTabA) +#define cTabS STATNAME(cTabS) + +/* do y <<= n, clipping to range [-2^30, 2^30 - 1] (i.e. output has one guard bit) */ +#define CLIP_2N_SHIFT30(y, n) { \ + int sign = (y) >> 31; \ + if (sign != (y) >> (30 - (n))) { \ + (y) = sign ^ (0x3fffffff); \ + } else { \ + (y) = (y) << (n); \ + } \ +} + +/* +#define CLIP_2N(y, n) { \ + int sign = (y) >> 31; \ + if (sign != ((y) >> (n))) { \ + (y) = sign ^ ((1 << (n)) - 1); \ + } \ +} +*/ + +enum { + SBR_GRID_FIXFIX = 0, + SBR_GRID_FIXVAR = 1, + SBR_GRID_VARFIX = 2, + SBR_GRID_VARVAR = 3 +}; + +enum { + HuffTabSBR_tEnv15 = 0, + HuffTabSBR_fEnv15 = 1, + HuffTabSBR_tEnv15b = 2, + HuffTabSBR_fEnv15b = 3, + HuffTabSBR_tEnv30 = 4, + HuffTabSBR_fEnv30 = 5, + HuffTabSBR_tEnv30b = 6, + HuffTabSBR_fEnv30b = 7, + HuffTabSBR_tNoise30 = 8, + HuffTabSBR_fNoise30 = 5, + HuffTabSBR_tNoise30b = 9, + HuffTabSBR_fNoise30b = 7 +}; + +typedef struct _HuffInfo { + int maxBits; /* number of bits in longest codeword */ + unsigned /*char*/ int count[MAX_HUFF_BITS]; /* count[i] = number of codes with length i+1 bits */ + int offset; /* offset into symbol table */ +} HuffInfo; + +/* need one SBRHeader per element (SCE/CPE), updated only on new header */ +typedef struct _SBRHeader { + int count; + + unsigned char ampRes; + unsigned char startFreq; + unsigned char stopFreq; + unsigned char crossOverBand; + unsigned char resBitsHdr; + unsigned char hdrExtra1; + unsigned char hdrExtra2; + + unsigned char freqScale; + unsigned char alterScale; + unsigned char noiseBands; + + unsigned char limiterBands; + unsigned char limiterGains; + unsigned char interpFreq; + unsigned char smoothMode; +} SBRHeader; + +/* need one SBRGrid per channel, updated every frame */ +typedef struct _SBRGrid { + unsigned char frameClass; + unsigned char ampResFrame; + unsigned char pointer; + + unsigned char numEnv; /* L_E */ + unsigned char envTimeBorder[MAX_NUM_ENV+1]; /* t_E */ + unsigned char freqRes[MAX_NUM_ENV]; /* r */ + + unsigned char numNoiseFloors; /* L_Q */ + unsigned char noiseTimeBorder[MAX_NUM_NOISE_FLOORS+1]; /* t_Q */ + + unsigned char numEnvPrev; + unsigned char numNoiseFloorsPrev; + unsigned char freqResPrev; +} SBRGrid; + +/* need one SBRFreq per element (SCE/CPE/LFE), updated only on header reset */ +typedef struct _SBRFreq { + int kStart; /* k_x */ + int nMaster; + int nHigh; + int nLow; + int nLimiter; /* N_l */ + int numQMFBands; /* M */ + int numNoiseFloorBands; /* Nq */ + + int kStartPrev; + int numQMFBandsPrev; + + unsigned char freqMaster[MAX_QMF_BANDS + 1]; /* not necessary to save this after derived tables are generated */ + unsigned char freqHigh[MAX_QMF_BANDS + 1]; + unsigned char freqLow[MAX_QMF_BANDS / 2 + 1]; /* nLow = nHigh - (nHigh >> 1) */ + unsigned char freqNoise[MAX_NUM_NOISE_FLOOR_BANDS+1]; + unsigned char freqLimiter[MAX_QMF_BANDS / 2 + MAX_NUM_PATCHES]; /* max (intermediate) size = nLow + numPatches - 1 */ + + unsigned char numPatches; + unsigned char patchNumSubbands[MAX_NUM_PATCHES + 1]; + unsigned char patchStartSubband[MAX_NUM_PATCHES + 1]; +} SBRFreq; + +typedef struct _SBRChan { + int reset; + unsigned char deltaFlagEnv[MAX_NUM_ENV]; + unsigned char deltaFlagNoise[MAX_NUM_NOISE_FLOORS]; + + signed char envDataQuant[MAX_NUM_ENV][MAX_QMF_BANDS]; /* range = [0, 127] */ + signed char noiseDataQuant[MAX_NUM_NOISE_FLOORS][MAX_NUM_NOISE_FLOOR_BANDS]; + + unsigned char invfMode[2][MAX_NUM_NOISE_FLOOR_BANDS]; /* invfMode[0/1][band] = prev/curr */ + int chirpFact[MAX_NUM_NOISE_FLOOR_BANDS]; /* bwArray */ + unsigned char addHarmonicFlag[2]; /* addHarmonicFlag[0/1] = prev/curr */ + unsigned char addHarmonic[2][64]; /* addHarmonic[0/1][band] = prev/curr */ + + int gbMask[2]; /* gbMask[0/1] = XBuf[0-31]/XBuf[32-39] */ + signed char laPrev; + + int noiseTabIndex; + int sinIndex; + int gainNoiseIndex; + int gTemp[MAX_NUM_SMOOTH_COEFS][MAX_QMF_BANDS]; + int qTemp[MAX_NUM_SMOOTH_COEFS][MAX_QMF_BANDS]; + +} SBRChan; + +typedef struct _PSInfoSBR { + /* save for entire file */ + int frameCount; + int sampRateIdx; + + /* state info that must be saved for each channel */ + SBRHeader sbrHdr[AAC_MAX_NCHANS]; + SBRGrid sbrGrid[AAC_MAX_NCHANS]; + SBRFreq sbrFreq[AAC_MAX_NCHANS]; + SBRChan sbrChan[AAC_MAX_NCHANS]; + + /* temp variables, no need to save between blocks */ + unsigned char dataExtra; + unsigned char resBitsData; + unsigned char extendedDataPresent; + int extendedDataSize; + + signed char envDataDequantScale[MAX_NCHANS_ELEM][MAX_NUM_ENV]; + int envDataDequant[MAX_NCHANS_ELEM][MAX_NUM_ENV][MAX_QMF_BANDS]; + int noiseDataDequant[MAX_NCHANS_ELEM][MAX_NUM_NOISE_FLOORS][MAX_NUM_NOISE_FLOOR_BANDS]; + + int eCurr[MAX_QMF_BANDS]; + unsigned char eCurrExp[MAX_QMF_BANDS]; + unsigned char eCurrExpMax; + signed char la; + + int crcCheckWord; + int couplingFlag; + int envBand; + int eOMGainMax; + int gainMax; + int gainMaxFBits; + int noiseFloorBand; + int qp1Inv; + int qqp1Inv; + int sMapped; + int sBand; + int highBand; + + int sumEOrigMapped; + int sumECurrGLim; + int sumSM; + int sumQM; + int gLimBoost[MAX_QMF_BANDS]; + int qmLimBoost[MAX_QMF_BANDS]; + int smBoost[MAX_QMF_BANDS]; + + int smBuf[MAX_QMF_BANDS]; + int qmLimBuf[MAX_QMF_BANDS]; + int gLimBuf[MAX_QMF_BANDS]; + int gLimFbits[MAX_QMF_BANDS]; + + int gFiltLast[MAX_QMF_BANDS]; + int qFiltLast[MAX_QMF_BANDS]; + + /* large buffers */ + int delayIdxQMFA[AAC_MAX_NCHANS]; + int delayQMFA[AAC_MAX_NCHANS][DELAY_SAMPS_QMFA]; + int delayIdxQMFS[AAC_MAX_NCHANS]; + int delayQMFS[AAC_MAX_NCHANS][DELAY_SAMPS_QMFS]; + int XBufDelay[AAC_MAX_NCHANS][HF_GEN][64][2]; + int XBuf[32+8][64][2]; + +} PSInfoSBR; + +/* sbrfft.c */ +void FFT32C(int *x); + +/* sbrfreq.c */ +int CalcFreqTables(SBRHeader *sbrHdr, SBRFreq *sbrFreq, int sampRateIdx); + +/* sbrhfadj.c */ +void AdjustHighFreq(PSInfoSBR *psi, SBRHeader *sbrHdr, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch); + +/* sbrhfgen.c */ +void GenerateHighFreq(PSInfoSBR *psi, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch); + +/* sbrhuff.c */ +void DecodeSBREnvelope(BitStreamInfo *bsi, PSInfoSBR *psi, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch); +void DecodeSBRNoise(BitStreamInfo *bsi, PSInfoSBR *psi, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch); +void UncoupleSBREnvelope(PSInfoSBR *psi, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChanR); +void UncoupleSBRNoise(PSInfoSBR *psi, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChanR); + +/* sbrmath.c */ +int InvRNormalized(int r); +int RatioPowInv(int a, int b, int c); +int SqrtFix(int x, int fBitsIn, int *fBitsOut); + +/* sbrqmf.c */ +int QMFAnalysis(int *inbuf, int *delay, int *XBuf, int fBitsIn, int *delayIdx, int qmfaBands); +void QMFSynthesis(int *inbuf, int *delay, int *delayIdx, int qmfsBands, short *outbuf, int nChans); + +/* sbrside.c */ +int GetSampRateIdx(int sampRate); +int UnpackSBRHeader(BitStreamInfo *bsi, SBRHeader *sbrHdr); +void UnpackSBRSingleChannel(BitStreamInfo *bsi, PSInfoSBR *psi, int chOut); +void UnpackSBRChannelPair(BitStreamInfo *bsi, PSInfoSBR *psi, int chOut); + +/* sbrtabs.c */ +extern const unsigned char k0Tab[NUM_SAMPLE_RATES_SBR][16]; +extern const unsigned char k2Tab[NUM_SAMPLE_RATES_SBR][14]; +extern const unsigned char goalSBTab[NUM_SAMPLE_RATES_SBR]; +extern const HuffInfo huffTabSBRInfo[10]; +extern const signed int /*short*/ huffTabSBR[604]; +extern const int log2Tab[65]; +extern const int noiseTab[512*2]; +extern const int cTabA[165]; +extern const int cTabS[640]; + +#endif /* _SBR_H */ diff --git a/components/spotify/cspot/bell/libhelix-aac/sbrfft.c b/components/spotify/cspot/bell/libhelix-aac/sbrfft.c new file mode 100644 index 00000000..23b36ac3 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/sbrfft.c @@ -0,0 +1,368 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: sbrfft.c,v 1.1 2005/02/26 01:47:35 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * February 2005 + * + * sbrfft.c - optimized FFT for SBR QMF filters + **************************************************************************************/ + +#include "sbr.h" +#include "assembly.h" + +#define SQRT1_2 0x5a82799a + +/* swap RE{p0} with RE{p1} and IM{P0} with IM{P1} */ +#define swapcplx(p0,p1) \ + t = p0; t1 = *(&(p0)+1); p0 = p1; *(&(p0)+1) = *(&(p1)+1); p1 = t; *(&(p1)+1) = t1 + +/* nfft = 32, hard coded since small, fixed size FFT +static const unsigned char bitrevtab32[9] = { + 0x01, 0x04, 0x03, 0x06, 0x00, 0x02, 0x05, 0x07, 0x00, +}; +*/ + +/* twiddle table for radix 4 pass, format = Q31 */ +static const int twidTabOdd32[8*6] = { + 0x40000000, 0x00000000, 0x40000000, 0x00000000, 0x40000000, 0x00000000, 0x539eba45, 0xe7821d59, + 0x4b418bbe, 0xf383a3e2, 0x58c542c5, 0xdc71898d, 0x5a82799a, 0xd2bec333, 0x539eba45, 0xe7821d59, + 0x539eba45, 0xc4df2862, 0x539eba45, 0xc4df2862, 0x58c542c5, 0xdc71898d, 0x3248d382, 0xc13ad060, + 0x40000000, 0xc0000000, 0x5a82799a, 0xd2bec333, 0x00000000, 0xd2bec333, 0x22a2f4f8, 0xc4df2862, + 0x58c542c5, 0xcac933ae, 0xcdb72c7e, 0xf383a3e2, 0x00000000, 0xd2bec333, 0x539eba45, 0xc4df2862, + 0xac6145bb, 0x187de2a7, 0xdd5d0b08, 0xe7821d59, 0x4b418bbe, 0xc13ad060, 0xa73abd3b, 0x3536cc52, +}; + +/************************************************************************************** + * Function: BitReverse32 + * + * Description: Ken's fast in-place bit reverse + * + * Inputs: buffer of 32 complex samples + * + * Outputs: bit-reversed samples in same buffer + * + * Return: none +**************************************************************************************/ +static void BitReverse32(int *inout) +{ + int t, t1; + + swapcplx(inout[2], inout[32]); + swapcplx(inout[4], inout[16]); + swapcplx(inout[6], inout[48]); + swapcplx(inout[10], inout[40]); + swapcplx(inout[12], inout[24]); + swapcplx(inout[14], inout[56]); + swapcplx(inout[18], inout[36]); + swapcplx(inout[22], inout[52]); + swapcplx(inout[26], inout[44]); + swapcplx(inout[30], inout[60]); + swapcplx(inout[38], inout[50]); + swapcplx(inout[46], inout[58]); +} + +/************************************************************************************** + * Function: R8FirstPass32 + * + * Description: radix-8 trivial pass for decimation-in-time FFT (log2(N) = 5) + * + * Inputs: buffer of (bit-reversed) samples + * + * Outputs: processed samples in same buffer + * + * Return: none + * + * Notes: assumes 3 guard bits, gains 1 integer bit + * guard bits out = guard bits in - 3 (if inputs are full scale) + * or guard bits in - 2 (if inputs bounded to +/- sqrt(2)/2) + * see scaling comments in fft.c for base AAC + * should compile with no stack spills on ARM (verify compiled output) + * current instruction count (per pass): 16 LDR, 16 STR, 4 SMULL, 61 ALU + **************************************************************************************/ +static void R8FirstPass32(int *r0) +{ + int r1, r2, r3, r4, r5, r6, r7; + int r8, r9, r10, r11, r12, r14; + + /* number of passes = fft size / 8 = 32 / 8 = 4 */ + r1 = (32 >> 3); + do { + + r2 = r0[8]; + r3 = r0[9]; + r4 = r0[10]; + r5 = r0[11]; + r6 = r0[12]; + r7 = r0[13]; + r8 = r0[14]; + r9 = r0[15]; + + r10 = r2 + r4; + r11 = r3 + r5; + r12 = r6 + r8; + r14 = r7 + r9; + + r2 -= r4; + r3 -= r5; + r6 -= r8; + r7 -= r9; + + r4 = r2 - r7; + r5 = r2 + r7; + r8 = r3 - r6; + r9 = r3 + r6; + + r2 = r4 - r9; + r3 = r4 + r9; + r6 = r5 - r8; + r7 = r5 + r8; + + r2 = MULSHIFT32(SQRT1_2, r2); /* can use r4, r5, r8, or r9 for constant and lo32 scratch reg */ + r3 = MULSHIFT32(SQRT1_2, r3); + r6 = MULSHIFT32(SQRT1_2, r6); + r7 = MULSHIFT32(SQRT1_2, r7); + + r4 = r10 + r12; + r5 = r10 - r12; + r8 = r11 + r14; + r9 = r11 - r14; + + r10 = r0[0]; + r11 = r0[2]; + r12 = r0[4]; + r14 = r0[6]; + + r10 += r11; + r12 += r14; + + r4 >>= 1; + r10 += r12; + r4 += (r10 >> 1); + r0[ 0] = r4; + r4 -= (r10 >> 1); + r4 = (r10 >> 1) - r4; + r0[ 8] = r4; + + r9 >>= 1; + r10 -= 2*r12; + r4 = (r10 >> 1) + r9; + r0[ 4] = r4; + r4 = (r10 >> 1) - r9; + r0[12] = r4; + r10 += r12; + + r10 -= 2*r11; + r12 -= 2*r14; + + r4 = r0[1]; + r9 = r0[3]; + r11 = r0[5]; + r14 = r0[7]; + + r4 += r9; + r11 += r14; + + r8 >>= 1; + r4 += r11; + r8 += (r4 >> 1); + r0[ 1] = r8; + r8 -= (r4 >> 1); + r8 = (r4 >> 1) - r8; + r0[ 9] = r8; + + r5 >>= 1; + r4 -= 2*r11; + r8 = (r4 >> 1) - r5; + r0[ 5] = r8; + r8 = (r4 >> 1) + r5; + r0[13] = r8; + r4 += r11; + + r4 -= 2*r9; + r11 -= 2*r14; + + r9 = r10 - r11; + r10 += r11; + r14 = r4 + r12; + r4 -= r12; + + r5 = (r10 >> 1) + r7; + r8 = (r4 >> 1) - r6; + r0[ 2] = r5; + r0[ 3] = r8; + + r5 = (r9 >> 1) - r2; + r8 = (r14 >> 1) - r3; + r0[ 6] = r5; + r0[ 7] = r8; + + r5 = (r10 >> 1) - r7; + r8 = (r4 >> 1) + r6; + r0[10] = r5; + r0[11] = r8; + + r5 = (r9 >> 1) + r2; + r8 = (r14 >> 1) + r3; + r0[14] = r5; + r0[15] = r8; + + r0 += 16; + r1--; + } while (r1 != 0); +} + +/************************************************************************************** + * Function: R4Core32 + * + * Description: radix-4 pass for 32-point decimation-in-time FFT + * + * Inputs: buffer of samples + * + * Outputs: processed samples in same buffer + * + * Return: none + * + * Notes: gain 2 integer bits + * guard bits out = guard bits in - 1 (if inputs are full scale) + * see scaling comments in fft.c for base AAC + * uses 3-mul, 3-add butterflies instead of 4-mul, 2-add + * should compile with no stack spills on ARM (verify compiled output) + * current instruction count (per pass): 16 LDR, 16 STR, 4 SMULL, 61 ALU + **************************************************************************************/ +static void R4Core32(int *r0) +{ + int r2, r3, r4, r5, r6, r7; + int r8, r9, r10, r12, r14; + int *r1; + + r1 = (int *)twidTabOdd32; + r10 = 8; + do { + /* can use r14 for lo32 scratch register in all MULSHIFT32 */ + r2 = r1[0]; + r3 = r1[1]; + r4 = r0[16]; + r5 = r0[17]; + r12 = r4 + r5; + r12 = MULSHIFT32(r3, r12); + r5 = MULSHIFT32(r2, r5) + r12; + r2 += 2*r3; + r4 = MULSHIFT32(r2, r4) - r12; + + r2 = r1[2]; + r3 = r1[3]; + r6 = r0[32]; + r7 = r0[33]; + r12 = r6 + r7; + r12 = MULSHIFT32(r3, r12); + r7 = MULSHIFT32(r2, r7) + r12; + r2 += 2*r3; + r6 = MULSHIFT32(r2, r6) - r12; + + r2 = r1[4]; + r3 = r1[5]; + r8 = r0[48]; + r9 = r0[49]; + r12 = r8 + r9; + r12 = MULSHIFT32(r3, r12); + r9 = MULSHIFT32(r2, r9) + r12; + r2 += 2*r3; + r8 = MULSHIFT32(r2, r8) - r12; + + r2 = r0[0]; + r3 = r0[1]; + + r12 = r6 + r8; + r8 = r6 - r8; + r14 = r9 - r7; + r9 = r9 + r7; + + r6 = (r2 >> 2) - r4; + r7 = (r3 >> 2) - r5; + r4 += (r2 >> 2); + r5 += (r3 >> 2); + + r2 = r4 + r12; + r3 = r5 + r9; + r0[0] = r2; + r0[1] = r3; + r2 = r6 - r14; + r3 = r7 - r8; + r0[16] = r2; + r0[17] = r3; + r2 = r4 - r12; + r3 = r5 - r9; + r0[32] = r2; + r0[33] = r3; + r2 = r6 + r14; + r3 = r7 + r8; + r0[48] = r2; + r0[49] = r3; + + r0 += 2; + r1 += 6; + r10--; + } while (r10 != 0); +} + +/************************************************************************************** + * Function: FFT32C + * + * Description: Ken's very fast in-place radix-4 decimation-in-time FFT + * + * Inputs: buffer of 32 complex samples (before bit-reversal) + * + * Outputs: processed samples in same buffer + * + * Return: none + * + * Notes: assumes 3 guard bits in, gains 3 integer bits + * guard bits out = guard bits in - 2 + * (guard bit analysis includes assumptions about steps immediately + * before and after, i.e. PreMul and PostMul for DCT) + **************************************************************************************/ +void FFT32C(int *x) +{ + /* decimation in time */ + BitReverse32(x); + + /* 32-point complex FFT */ + R8FirstPass32(x); /* gain 1 int bit, lose 2 GB (making assumptions about input) */ + R4Core32(x); /* gain 2 int bits, lose 0 GB (making assumptions about input) */ +} diff --git a/components/spotify/cspot/bell/libhelix-aac/sbrfreq.c b/components/spotify/cspot/bell/libhelix-aac/sbrfreq.c new file mode 100644 index 00000000..26a76063 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/sbrfreq.c @@ -0,0 +1,641 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: sbrfreq.c,v 1.2 2005/05/20 18:05:41 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * sbrfreq.c - frequency band table calculation for SBR + **************************************************************************************/ + +#include "sbr.h" +#include "assembly.h" + +/************************************************************************************** + * Function: BubbleSort + * + * Description: in-place sort of unsigned chars + * + * Inputs: buffer of elements to sort + * number of elements to sort + * + * Outputs: sorted buffer + * + * Return: none + **************************************************************************************/ +static void BubbleSort(unsigned char *v, int nItems) +{ + int i; + unsigned char t; + + while (nItems >= 2) { + for (i = 0; i < nItems-1; i++) { + if (v[i+1] < v[i]) { + t = v[i+1]; + v[i+1] = v[i]; + v[i] = t; + } + } + nItems--; + } +} + +/************************************************************************************** + * Function: VMin + * + * Description: find smallest element in a buffer of unsigned chars + * + * Inputs: buffer of elements to search + * number of elements to search + * + * Outputs: none + * + * Return: smallest element in buffer + **************************************************************************************/ +static unsigned char VMin(unsigned char *v, int nItems) +{ + int i; + unsigned char vMin; + + vMin = v[0]; + for (i = 1; i < nItems; i++) { + if (v[i] < vMin) + vMin = v[i]; + } + return vMin; +} + +/************************************************************************************** + * Function: VMax + * + * Description: find largest element in a buffer of unsigned chars + * + * Inputs: buffer of elements to search + * number of elements to search + * + * Outputs: none + * + * Return: largest element in buffer + **************************************************************************************/ +static unsigned char VMax(unsigned char *v, int nItems) +{ + int i; + unsigned char vMax; + + vMax = v[0]; + for (i = 1; i < nItems; i++) { + if (v[i] > vMax) + vMax = v[i]; + } + return vMax; +} + +/************************************************************************************** + * Function: CalcFreqMasterScaleZero + * + * Description: calculate master frequency table when freqScale == 0 + * (4.6.18.3.2.1, figure 4.39) + * + * Inputs: alterScale flag + * index of first QMF subband in master freq table (k0) + * index of last QMF subband (k2) + * + * Outputs: master frequency table + * + * Return: number of bands in master frequency table + * + * Notes: assumes k2 - k0 <= 48 and k2 >= k0 (4.6.18.3.6) + **************************************************************************************/ +static int CalcFreqMasterScaleZero(unsigned char *freqMaster, int alterScale, int k0, int k2) +{ + int nMaster, k, nBands, k2Achieved, dk, vDk[64], k2Diff; + + if (alterScale) { + dk = 2; + nBands = 2 * ((k2 - k0 + 2) >> 2); + } else { + dk = 1; + nBands = 2 * ((k2 - k0) >> 1); + } + + if (nBands <= 0) + return 0; + + k2Achieved = k0 + nBands * dk; + k2Diff = k2 - k2Achieved; + for (k = 0; k < nBands; k++) + vDk[k] = dk; + + if (k2Diff > 0) { + k = nBands - 1; + while (k2Diff) { + vDk[k]++; + k--; + k2Diff--; + } + } else if (k2Diff < 0) { + k = 0; + while (k2Diff) { + vDk[k]--; + k++; + k2Diff++; + } + } + + nMaster = nBands; + freqMaster[0] = k0; + for (k = 1; k <= nBands; k++) + freqMaster[k] = freqMaster[k-1] + vDk[k-1]; + + return nMaster; +} + +/* mBandTab[i] = temp1[i] / 2 */ +static const int mBandTab[3] PROGMEM = {6, 5, 4}; + +/* invWarpTab[i] = 1.0 / temp2[i], Q30 (see 4.6.18.3.2.1) */ +static const int invWarpTab[2] PROGMEM = {0x40000000, 0x313b13b1}; + +/************************************************************************************** + * Function: CalcFreqMasterScale + * + * Description: calculate master frequency table when freqScale > 0 + * (4.6.18.3.2.1, figure 4.39) + * + * Inputs: alterScale flag + * freqScale flag + * index of first QMF subband in master freq table (k0) + * index of last QMF subband (k2) + * + * Outputs: master frequency table + * + * Return: number of bands in master frequency table + * + * Notes: assumes k2 - k0 <= 48 and k2 >= k0 (4.6.18.3.6) + **************************************************************************************/ +static int CalcFreqMaster(unsigned char *freqMaster, int freqScale, int alterScale, int k0, int k2) +{ + int bands, twoRegions, k, k1, t, vLast, vCurr, pCurr; + int invWarp, nBands0, nBands1, change; + unsigned char vDk1Min, vDk0Max; + unsigned char *vDelta; + + if (freqScale < 1 || freqScale > 3) + return -1; + + bands = mBandTab[freqScale - 1]; + invWarp = invWarpTab[alterScale]; + + /* tested for all k0 = [5, 64], k2 = [k0, 64] */ + if (k2*10000 > 22449*k0) { + twoRegions = 1; + k1 = 2*k0; + } else { + twoRegions = 0; + k1 = k2; + } + + /* tested for all k0 = [5, 64], k1 = [k0, 64], freqScale = [1,3] */ + t = (log2Tab[k1] - log2Tab[k0]) >> 3; /* log2(k1/k0), Q28 to Q25 */ + nBands0 = 2 * (((bands * t) + (1 << 24)) >> 25); /* multiply by bands/2, round to nearest int (mBandTab has factor of 1/2 rolled in) */ + + /* tested for all valid combinations of k0, k1, nBands (from sampRate, freqScale, alterScale) + * roundoff error can be a problem with fixpt (e.g. pCurr = 12.499999 instead of 12.50003) + * because successive multiplication always undershoots a little bit, but this + * doesn't occur in any of the ratios we encounter from the valid k0/k1 bands in the spec + */ + t = RatioPowInv(k1, k0, nBands0); + pCurr = k0 << 24; + vLast = k0; + vDelta = freqMaster + 1; /* operate in-place */ + for (k = 0; k < nBands0; k++) { + pCurr = MULSHIFT32(pCurr, t) << 8; /* keep in Q24 */ + vCurr = (pCurr + (1 << 23)) >> 24; + vDelta[k] = (vCurr - vLast); + vLast = vCurr; + } + + /* sort the deltas and find max delta for first region */ + BubbleSort(vDelta, nBands0); + vDk0Max = VMax(vDelta, nBands0); + + /* fill master frequency table with bands from first region */ + freqMaster[0] = k0; + for (k = 1; k <= nBands0; k++) + freqMaster[k] += freqMaster[k-1]; + + /* if only one region, then the table is complete */ + if (!twoRegions) + return nBands0; + + /* tested for all k1 = [10, 64], k2 = [k0, 64], freqScale = [1,3] */ + t = (log2Tab[k2] - log2Tab[k1]) >> 3; /* log2(k1/k0), Q28 to Q25 */ + t = MULSHIFT32(bands * t, invWarp) << 2; /* multiply by bands/2, divide by warp factor, keep Q25 */ + nBands1 = 2 * ((t + (1 << 24)) >> 25); /* round to nearest int */ + + /* see comments above for calculations in first region */ + t = RatioPowInv(k2, k1, nBands1); + pCurr = k1 << 24; + vLast = k1; + vDelta = freqMaster + nBands0 + 1; /* operate in-place */ + for (k = 0; k < nBands1; k++) { + pCurr = MULSHIFT32(pCurr, t) << 8; /* keep in Q24 */ + vCurr = (pCurr + (1 << 23)) >> 24; + vDelta[k] = (vCurr - vLast); + vLast = vCurr; + } + + /* sort the deltas, adjusting first and last if the second region has smaller deltas than the first */ + vDk1Min = VMin(vDelta, nBands1); + if (vDk1Min < vDk0Max) { + BubbleSort(vDelta, nBands1); + change = vDk0Max - vDelta[0]; + if (change > ((vDelta[nBands1 - 1] - vDelta[0]) >> 1)) + change = ((vDelta[nBands1 - 1] - vDelta[0]) >> 1); + vDelta[0] += change; + vDelta[nBands1-1] -= change; + } + BubbleSort(vDelta, nBands1); + + /* fill master frequency table with bands from second region + * Note: freqMaster[nBands0] = k1 + */ + for (k = 1; k <= nBands1; k++) + freqMaster[k + nBands0] += freqMaster[k + nBands0 - 1]; + + return (nBands0 + nBands1); +} + +/************************************************************************************** + * Function: CalcFreqHigh + * + * Description: calculate high resolution frequency table (4.6.18.3.2.2) + * + * Inputs: master frequency table + * number of bands in master frequency table + * crossover band from header + * + * Outputs: high resolution frequency table + * + * Return: number of bands in high resolution frequency table + **************************************************************************************/ +static int CalcFreqHigh(unsigned char *freqHigh, unsigned char *freqMaster, int nMaster, int crossOverBand) +{ + int k, nHigh; + + nHigh = nMaster - crossOverBand; + + for (k = 0; k <= nHigh; k++) + freqHigh[k] = freqMaster[k + crossOverBand]; + + return nHigh; +} + +/************************************************************************************** + * Function: CalcFreqLow + * + * Description: calculate low resolution frequency table (4.6.18.3.2.2) + * + * Inputs: high resolution frequency table + * number of bands in high resolution frequency table + * + * Outputs: low resolution frequency table + * + * Return: number of bands in low resolution frequency table + **************************************************************************************/ +static int CalcFreqLow(unsigned char *freqLow, unsigned char *freqHigh, int nHigh) +{ + int k, nLow, oddFlag; + + nLow = nHigh - (nHigh >> 1); + freqLow[0] = freqHigh[0]; + oddFlag = nHigh & 0x01; + + for (k = 1; k <= nLow; k++) + freqLow[k] = freqHigh[2*k - oddFlag]; + + return nLow; +} + +/************************************************************************************** + * Function: CalcFreqNoise + * + * Description: calculate noise floor frequency table (4.6.18.3.2.2) + * + * Inputs: low resolution frequency table + * number of bands in low resolution frequency table + * index of starting QMF subband for SBR (kStart) + * index of last QMF subband (k2) + * number of noise bands + * + * Outputs: noise floor frequency table + * + * Return: number of bands in noise floor frequency table + **************************************************************************************/ +static int CalcFreqNoise(unsigned char *freqNoise, unsigned char *freqLow, int nLow, int kStart, int k2, int noiseBands) +{ + int i, iLast, k, nQ, lTop, lBottom; + + lTop = log2Tab[k2]; + lBottom = log2Tab[kStart]; + nQ = noiseBands*((lTop - lBottom) >> 2); /* Q28 to Q26, noiseBands = [0,3] */ + nQ = (nQ + (1 << 25)) >> 26; + if (nQ < 1) + nQ = 1; + + ASSERT(nQ <= MAX_NUM_NOISE_FLOOR_BANDS); /* required from 4.6.18.3.6 */ + + iLast = 0; + freqNoise[0] = freqLow[0]; + for (k = 1; k <= nQ; k++) { + i = iLast + (nLow - iLast) / (nQ + 1 - k); /* truncating division */ + freqNoise[k] = freqLow[i]; + iLast = i; + } + + return nQ; +} + +/************************************************************************************** + * Function: BuildPatches + * + * Description: build high frequency patches (4.6.18.6.3) + * + * Inputs: master frequency table + * number of bands in low resolution frequency table + * index of first QMF subband in master freq table (k0) + * index of starting QMF subband for SBR (kStart) + * number of QMF bands in high resolution frequency table + * sample rate index + * + * Outputs: starting subband for each patch + * number of subbands in each patch + * + * Return: number of patches + **************************************************************************************/ +static int BuildPatches(unsigned char *patchNumSubbands, unsigned char *patchStartSubband, unsigned char *freqMaster, + int nMaster, int k0, int kStart, int numQMFBands, int sampRateIdx) +{ + int i, j, k; + int msb, sb, usb, numPatches, goalSB, oddFlag; + + msb = k0; + usb = kStart; + numPatches = 0; + goalSB = goalSBTab[sampRateIdx]; + + if (nMaster == 0) { + patchNumSubbands[0] = 0; + patchStartSubband[0] = 0; + return 0; + } + + if (goalSB < kStart + numQMFBands) { + k = 0; + for (i = 0; freqMaster[i] < goalSB; i++) + k = i+1; + } else { + k = nMaster; + } + + do { + j = k+1; + do { + j--; + sb = freqMaster[j]; + oddFlag = (sb - 2 + k0) & 0x01; + } while (sb > k0 - 1 + msb - oddFlag); + + patchNumSubbands[numPatches] = MAX(sb - usb, 0); + patchStartSubband[numPatches] = k0 - oddFlag - patchNumSubbands[numPatches]; + + /* from MPEG reference code - slightly different from spec */ + if ((patchNumSubbands[numPatches] < 3) && (numPatches > 0)) + break; + + if (patchNumSubbands[numPatches] > 0) { + usb = sb; + msb = sb; + numPatches++; + } else { + msb = kStart; + } + + if (freqMaster[k] - sb < 3) + k = nMaster; + + } while (sb != (kStart + numQMFBands) && numPatches <= MAX_NUM_PATCHES); + + return numPatches; +} + +/************************************************************************************** + * Function: FindFreq + * + * Description: search buffer of unsigned chars for a specific value + * + * Inputs: buffer of elements to search + * number of elements to search + * value to search for + * + * Outputs: none + * + * Return: non-zero if the value is found anywhere in the buffer, zero otherwise + **************************************************************************************/ +static int FindFreq(unsigned char *freq, int nFreq, unsigned char val) +{ + int k; + + for (k = 0; k < nFreq; k++) { + if (freq[k] == val) + return 1; + } + + return 0; +} + +/************************************************************************************** + * Function: RemoveFreq + * + * Description: remove one element from a buffer of unsigned chars + * + * Inputs: buffer of elements + * number of elements + * index of element to remove + * + * Outputs: new buffer of length nFreq-1 + * + * Return: none + **************************************************************************************/ +static void RemoveFreq(unsigned char *freq, int nFreq, int removeIdx) +{ + int k; + + if (removeIdx >= nFreq) + return; + + for (k = removeIdx; k < nFreq - 1; k++) + freq[k] = freq[k+1]; +} + +/************************************************************************************** + * Function: CalcFreqLimiter + * + * Description: calculate limiter frequency table (4.6.18.3.2.3) + * + * Inputs: number of subbands in each patch + * low resolution frequency table + * number of bands in low resolution frequency table + * index of starting QMF subband for SBR (kStart) + * number of limiter bands + * number of patches + * + * Outputs: limiter frequency table + * + * Return: number of bands in limiter frequency table + **************************************************************************************/ +static int CalcFreqLimiter(unsigned char *freqLimiter, unsigned char *patchNumSubbands, unsigned char *freqLow, + int nLow, int kStart, int limiterBands, int numPatches) +{ + int k, bands, nLimiter, nOctaves; + int limBandsPerOctave[3] = {120, 200, 300}; /* [1.2, 2.0, 3.0] * 100 */ + unsigned char patchBorders[MAX_NUM_PATCHES + 1]; + + /* simple case */ + if (limiterBands == 0) { + freqLimiter[0] = freqLow[0] - kStart; + freqLimiter[1] = freqLow[nLow] - kStart; + return 1; + } + + bands = limBandsPerOctave[limiterBands - 1]; + patchBorders[0] = kStart; + + /* from MPEG reference code - slightly different from spec (top border) */ + for (k = 1; k < numPatches; k++) + patchBorders[k] = patchBorders[k-1] + patchNumSubbands[k-1]; + patchBorders[k] = freqLow[nLow]; + + for (k = 0; k <= nLow; k++) + freqLimiter[k] = freqLow[k]; + + for (k = 1; k < numPatches; k++) + freqLimiter[k+nLow] = patchBorders[k]; + + k = 1; + nLimiter = nLow + numPatches - 1; + BubbleSort(freqLimiter, nLimiter + 1); + + while (k <= nLimiter) { + nOctaves = log2Tab[freqLimiter[k]] - log2Tab[freqLimiter[k-1]]; /* Q28 */ + nOctaves = (nOctaves >> 9) * bands; /* Q19, max bands = 300 < 2^9 */ + if (nOctaves < (49 << 19)) { /* compare with 0.49*100, in Q19 */ + if (freqLimiter[k] == freqLimiter[k-1] || FindFreq(patchBorders, numPatches + 1, freqLimiter[k]) == 0) { + RemoveFreq(freqLimiter, nLimiter + 1, k); + nLimiter--; + } else if (FindFreq(patchBorders, numPatches + 1, freqLimiter[k-1]) == 0) { + RemoveFreq(freqLimiter, nLimiter + 1, k-1); + nLimiter--; + } else { + k++; + } + } else { + k++; + } + } + + /* store limiter boundaries as offsets from kStart */ + for (k = 0; k <= nLimiter; k++) + freqLimiter[k] -= kStart; + + return nLimiter; +} + +/************************************************************************************** + * Function: CalcFreqTables + * + * Description: calulate master and derived frequency tables, and patches + * + * Inputs: initialized SBRHeader struct for this SCE/CPE block + * initialized SBRFreq struct for this SCE/CPE block + * sample rate index of output sample rate (after SBR) + * + * Outputs: master and derived frequency tables, and patches + * + * Return: non-zero if error, zero otherwise + **************************************************************************************/ +int CalcFreqTables(SBRHeader *sbrHdr, SBRFreq *sbrFreq, int sampRateIdx) +{ + int k0, k2; + + k0 = k0Tab[sampRateIdx][sbrHdr->startFreq]; + + if (sbrHdr->stopFreq == 14) + k2 = 2*k0; + else if (sbrHdr->stopFreq == 15) + k2 = 3*k0; + else + k2 = k2Tab[sampRateIdx][sbrHdr->stopFreq]; + if (k2 > 64) + k2 = 64; + + /* calculate master frequency table */ + if (sbrHdr->freqScale == 0) + sbrFreq->nMaster = CalcFreqMasterScaleZero(sbrFreq->freqMaster, sbrHdr->alterScale, k0, k2); + else + sbrFreq->nMaster = CalcFreqMaster(sbrFreq->freqMaster, sbrHdr->freqScale, sbrHdr->alterScale, k0, k2); + + /* calculate high frequency table and related parameters */ + sbrFreq->nHigh = CalcFreqHigh(sbrFreq->freqHigh, sbrFreq->freqMaster, sbrFreq->nMaster, sbrHdr->crossOverBand); + sbrFreq->numQMFBands = sbrFreq->freqHigh[sbrFreq->nHigh] - sbrFreq->freqHigh[0]; + sbrFreq->kStart = sbrFreq->freqHigh[0]; + + /* calculate low frequency table */ + sbrFreq->nLow = CalcFreqLow(sbrFreq->freqLow, sbrFreq->freqHigh, sbrFreq->nHigh); + + /* calculate noise floor frequency table */ + sbrFreq->numNoiseFloorBands = CalcFreqNoise(sbrFreq->freqNoise, sbrFreq->freqLow, sbrFreq->nLow, sbrFreq->kStart, k2, sbrHdr->noiseBands); + + /* calculate limiter table */ + sbrFreq->numPatches = BuildPatches(sbrFreq->patchNumSubbands, sbrFreq->patchStartSubband, sbrFreq->freqMaster, + sbrFreq->nMaster, k0, sbrFreq->kStart, sbrFreq->numQMFBands, sampRateIdx); + sbrFreq->nLimiter = CalcFreqLimiter(sbrFreq->freqLimiter, sbrFreq->patchNumSubbands, sbrFreq->freqLow, sbrFreq->nLow, sbrFreq->kStart, + sbrHdr->limiterBands, sbrFreq->numPatches); + + return 0; +} diff --git a/components/spotify/cspot/bell/libhelix-aac/sbrhfadj.c b/components/spotify/cspot/bell/libhelix-aac/sbrhfadj.c new file mode 100644 index 00000000..f16cb07b --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/sbrhfadj.c @@ -0,0 +1,853 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: sbrhfadj.c,v 1.3 2005/05/24 16:01:55 albertofloyd Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * sbrhfadj.c - high frequency adjustment for SBR + **************************************************************************************/ + +#include "sbr.h" +#include "assembly.h" + +/* invBandTab[i] = 1.0 / (i + 1), Q31 */ +static const int invBandTab[64] PROGMEM = { + 0x7fffffff, 0x40000000, 0x2aaaaaab, 0x20000000, 0x1999999a, 0x15555555, 0x12492492, 0x10000000, + 0x0e38e38e, 0x0ccccccd, 0x0ba2e8ba, 0x0aaaaaab, 0x09d89d8a, 0x09249249, 0x08888889, 0x08000000, + 0x07878788, 0x071c71c7, 0x06bca1af, 0x06666666, 0x06186186, 0x05d1745d, 0x0590b216, 0x05555555, + 0x051eb852, 0x04ec4ec5, 0x04bda12f, 0x04924925, 0x0469ee58, 0x04444444, 0x04210842, 0x04000000, + 0x03e0f83e, 0x03c3c3c4, 0x03a83a84, 0x038e38e4, 0x03759f23, 0x035e50d8, 0x03483483, 0x03333333, + 0x031f3832, 0x030c30c3, 0x02fa0be8, 0x02e8ba2f, 0x02d82d83, 0x02c8590b, 0x02b93105, 0x02aaaaab, + 0x029cbc15, 0x028f5c29, 0x02828283, 0x02762762, 0x026a439f, 0x025ed098, 0x0253c825, 0x02492492, + 0x023ee090, 0x0234f72c, 0x022b63cc, 0x02222222, 0x02192e2a, 0x02108421, 0x02082082, 0x02000000, +}; + +/************************************************************************************** + * Function: EstimateEnvelope + * + * Description: estimate power of generated HF QMF bands in one time-domain envelope + * (4.6.18.7.3) + * + * Inputs: initialized PSInfoSBR struct + * initialized SBRHeader struct for this SCE/CPE block + * initialized SBRGrid struct for this channel + * initialized SBRFreq struct for this SCE/CPE block + * index of current envelope + * + * Outputs: power of each QMF subband, stored as integer (Q0) * 2^N, N >= 0 + * + * Return: none + **************************************************************************************/ +static void EstimateEnvelope(PSInfoSBR *psi, SBRHeader *sbrHdr, SBRGrid *sbrGrid, SBRFreq *sbrFreq, int env) +{ + int i, m, iStart, iEnd, xre, xim, nScale, expMax; + int p, n, mStart, mEnd, invFact, t; + int *XBuf; + U64 eCurr; + unsigned char *freqBandTab; + + /* estimate current envelope */ + iStart = sbrGrid->envTimeBorder[env] + HF_ADJ; + iEnd = sbrGrid->envTimeBorder[env+1] + HF_ADJ; + if (sbrGrid->freqRes[env]) { + n = sbrFreq->nHigh; + freqBandTab = sbrFreq->freqHigh; + } else { + n = sbrFreq->nLow; + freqBandTab = sbrFreq->freqLow; + } + + /* ADS should inline MADD64 (smlal) properly, but check to make sure */ + expMax = 0; + if (sbrHdr->interpFreq) { + for (m = 0; m < sbrFreq->numQMFBands; m++) { + eCurr.w64 = 0; + XBuf = psi->XBuf[iStart][sbrFreq->kStart + m]; + for (i = iStart; i < iEnd; i++) { + /* scale to int before calculating power (precision not critical, and avoids overflow) */ + xre = (*XBuf) >> FBITS_OUT_QMFA; XBuf += 1; + xim = (*XBuf) >> FBITS_OUT_QMFA; XBuf += (2*64 - 1); + eCurr.w64 = MADD64(eCurr.w64, xre, xre); + eCurr.w64 = MADD64(eCurr.w64, xim, xim); + } + + /* eCurr.w64 is now Q(64 - 2*FBITS_OUT_QMFA) (64-bit word) + * if energy is too big to fit in 32-bit word (> 2^31) scale down by power of 2 + */ + nScale = 0; + if (eCurr.r.hi32) { + nScale = (32 - CLZ(eCurr.r.hi32)) + 1; + t = (int)(eCurr.r.lo32 >> nScale); /* logical (unsigned) >> */ + t |= eCurr.r.hi32 << (32 - nScale); + } else if (eCurr.r.lo32 >> 31) { + nScale = 1; + t = (int)(eCurr.r.lo32 >> nScale); /* logical (unsigned) >> */ + } else { + t = (int)eCurr.r.lo32; + } + + invFact = invBandTab[(iEnd - iStart)-1]; + psi->eCurr[m] = MULSHIFT32(t, invFact); + psi->eCurrExp[m] = nScale + 1; /* +1 for invFact = Q31 */ + if (psi->eCurrExp[m] > expMax) + expMax = psi->eCurrExp[m]; + } + } else { + for (p = 0; p < n; p++) { + mStart = freqBandTab[p]; + mEnd = freqBandTab[p+1]; + eCurr.w64 = 0; + for (i = iStart; i < iEnd; i++) { + XBuf = psi->XBuf[i][mStart]; + for (m = mStart; m < mEnd; m++) { + xre = (*XBuf++) >> FBITS_OUT_QMFA; + xim = (*XBuf++) >> FBITS_OUT_QMFA; + eCurr.w64 = MADD64(eCurr.w64, xre, xre); + eCurr.w64 = MADD64(eCurr.w64, xim, xim); + } + } + + nScale = 0; + if (eCurr.r.hi32) { + nScale = (32 - CLZ(eCurr.r.hi32)) + 1; + t = (int)(eCurr.r.lo32 >> nScale); /* logical (unsigned) >> */ + t |= eCurr.r.hi32 << (32 - nScale); + } else if (eCurr.r.lo32 >> 31) { + nScale = 1; + t = (int)(eCurr.r.lo32 >> nScale); /* logical (unsigned) >> */ + } else { + t = (int)eCurr.r.lo32; + } + + invFact = invBandTab[(iEnd - iStart)-1]; + invFact = MULSHIFT32(invBandTab[(mEnd - mStart)-1], invFact) << 1; + t = MULSHIFT32(t, invFact); + + for (m = mStart; m < mEnd; m++) { + psi->eCurr[m - sbrFreq->kStart] = t; + psi->eCurrExp[m - sbrFreq->kStart] = nScale + 1; /* +1 for invFact = Q31 */ + } + if (psi->eCurrExp[mStart - sbrFreq->kStart] > expMax) + expMax = psi->eCurrExp[mStart - sbrFreq->kStart]; + } + } + psi->eCurrExpMax = expMax; +} + +/************************************************************************************** + * Function: GetSMapped + * + * Description: calculate SMapped (4.6.18.7.2) + * + * Inputs: initialized PSInfoSBR struct + * initialized SBRGrid struct for this channel + * initialized SBRFreq struct for this SCE/CPE block + * initialized SBRChan struct for this channel + * index of current envelope + * index of current QMF band + * la flag for this envelope + * + * Outputs: none + * + * Return: 1 if a sinusoid is present in this band, 0 if not + **************************************************************************************/ +static int GetSMapped(SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int env, int band, int la) +{ + int bandStart, bandEnd, oddFlag, r; + + if (sbrGrid->freqRes[env]) { + /* high resolution */ + bandStart = band; + bandEnd = band+1; + } else { + /* low resolution (see CalcFreqLow() for mapping) */ + oddFlag = sbrFreq->nHigh & 0x01; + bandStart = (band > 0 ? 2*band - oddFlag : 0); /* starting index for freqLow[band] */ + bandEnd = 2*(band+1) - oddFlag; /* ending index for freqLow[band+1] */ + } + + /* sMapped = 1 if sIndexMapped == 1 for any frequency in this band */ + for (band = bandStart; band < bandEnd; band++) { + if (sbrChan->addHarmonic[1][band]) { + r = ((sbrFreq->freqHigh[band+1] + sbrFreq->freqHigh[band]) >> 1); + if (env >= la || sbrChan->addHarmonic[0][r] == 1) + return 1; + } + } + return 0; +} + +#define GBOOST_MAX 0x2830afd3 /* Q28, 1.584893192 squared */ +#define ACC_SCALE 6 + +/* squared version of table in 4.6.18.7.5 */ +static const int limGainTab[4] PROGMEM = {0x20138ca7, 0x40000000, 0x7fb27dce, 0x80000000}; /* Q30 (0x80000000 = sentinel for GMAX) */ + +/************************************************************************************** + * Function: CalcMaxGain + * + * Description: calculate max gain in one limiter band (4.6.18.7.5) + * + * Inputs: initialized PSInfoSBR struct + * initialized SBRHeader struct for this SCE/CPE block + * initialized SBRGrid struct for this channel + * initialized SBRFreq struct for this SCE/CPE block + * index of current channel (0 for SCE, 0 or 1 for CPE) + * index of current envelope + * index of current limiter band + * number of fraction bits in dequantized envelope + * (max = Q(FBITS_OUT_DQ_ENV - 6) = Q23, can go negative) + * + * Outputs: updated gainMax, gainMaxFBits, and sumEOrigMapped in PSInfoSBR struct + * + * Return: none + **************************************************************************************/ +static void CalcMaxGain(PSInfoSBR *psi, SBRHeader *sbrHdr, SBRGrid *sbrGrid, SBRFreq *sbrFreq, int ch, int env, int lim, int fbitsDQ) +{ + int m, mStart, mEnd, q, z, r; + int sumEOrigMapped, sumECurr, gainMax, eOMGainMax, envBand; + unsigned char eCurrExpMax; + unsigned char *freqBandTab; + + mStart = sbrFreq->freqLimiter[lim]; /* these are offsets from kStart */ + mEnd = sbrFreq->freqLimiter[lim + 1]; + freqBandTab = (sbrGrid->freqRes[env] ? sbrFreq->freqHigh : sbrFreq->freqLow); + + /* calculate max gain to apply to signal in this limiter band */ + sumECurr = 0; + sumEOrigMapped = 0; + eCurrExpMax = psi->eCurrExpMax; + eOMGainMax = psi->eOMGainMax; + envBand = psi->envBand; + for (m = mStart; m < mEnd; m++) { + /* map current QMF band to appropriate envelope band */ + if (m == freqBandTab[envBand + 1] - sbrFreq->kStart) { + envBand++; + eOMGainMax = psi->envDataDequant[ch][env][envBand] >> ACC_SCALE; /* summing max 48 bands */ + } + sumEOrigMapped += eOMGainMax; + + /* easy test for overflow on ARM */ + sumECurr += (psi->eCurr[m] >> (eCurrExpMax - psi->eCurrExp[m])); + if (sumECurr >> 30) { + sumECurr >>= 1; + eCurrExpMax++; + } + } + psi->eOMGainMax = eOMGainMax; + psi->envBand = envBand; + + psi->gainMaxFBits = 30; /* Q30 tables */ + if (sumECurr == 0) { + /* any non-zero numerator * 1/EPS_0 is > G_MAX */ + gainMax = (sumEOrigMapped == 0 ? (int)limGainTab[sbrHdr->limiterGains] : (int)0x80000000); + } else if (sumEOrigMapped == 0) { + /* 1/(any non-zero denominator) * EPS_0 * limGainTab[x] is appx. 0 */ + gainMax = 0; + } else { + /* sumEOrigMapped = Q(fbitsDQ - ACC_SCALE), sumECurr = Q(-eCurrExpMax) */ + gainMax = limGainTab[sbrHdr->limiterGains]; + if (sbrHdr->limiterGains != 3) { + q = MULSHIFT32(sumEOrigMapped, gainMax); /* Q(fbitsDQ - ACC_SCALE - 2), gainMax = Q30 */ + z = CLZ(sumECurr) - 1; + r = InvRNormalized(sumECurr << z); /* in = Q(z - eCurrExpMax), out = Q(29 + 31 - z + eCurrExpMax) */ + gainMax = MULSHIFT32(q, r); /* Q(29 + 31 - z + eCurrExpMax + fbitsDQ - ACC_SCALE - 2 - 32) */ + psi->gainMaxFBits = 26 - z + eCurrExpMax + fbitsDQ - ACC_SCALE; + } + } + psi->sumEOrigMapped = sumEOrigMapped; + psi->gainMax = gainMax; +} + +/************************************************************************************** + * Function: CalcNoiseDivFactors + * + * Description: calculate 1/(1+Q) and Q/(1+Q) (4.6.18.7.4; 4.6.18.7.5) + * + * Inputs: dequantized noise floor scalefactor + * + * Outputs: 1/(1+Q) and Q/(1+Q), format = Q31 + * + * Return: none + **************************************************************************************/ +static void CalcNoiseDivFactors(int q, int *qp1Inv, int *qqp1Inv) +{ + int z, qp1, t, s; + + /* 1 + Q_orig */ + qp1 = (q >> 1); + qp1 += (1 << (FBITS_OUT_DQ_NOISE - 1)); /* >> 1 to avoid overflow when adding 1.0 */ + z = CLZ(qp1) - 1; /* z <= 31 - FBITS_OUT_DQ_NOISE */ + qp1 <<= z; /* Q(FBITS_OUT_DQ_NOISE + z) = Q31 * 2^-(31 - (FBITS_OUT_DQ_NOISE + z)) */ + t = InvRNormalized(qp1) << 1; /* Q30 * 2^(31 - (FBITS_OUT_DQ_NOISE + z)), guaranteed not to overflow */ + + /* normalize to Q31 */ + s = (31 - (FBITS_OUT_DQ_NOISE - 1) - z - 1); /* clearly z >= 0, z <= (30 - (FBITS_OUT_DQ_NOISE - 1)) */ + *qp1Inv = (t >> s); /* s = [0, 31 - FBITS_OUT_DQ_NOISE] */ + *qqp1Inv = MULSHIFT32(t, q) << (32 - FBITS_OUT_DQ_NOISE - s); +} + +/************************************************************************************** + * Function: CalcComponentGains + * + * Description: calculate gain of envelope, sinusoids, and noise in one limiter band + * (4.6.18.7.5) + * + * Inputs: initialized PSInfoSBR struct + * initialized SBRHeader struct for this SCE/CPE block + * initialized SBRGrid struct for this channel + * initialized SBRFreq struct for this SCE/CPE block + * initialized SBRChan struct for this channel + * index of current channel (0 for SCE, 0 or 1 for CPE) + * index of current envelope + * index of current limiter band + * number of fraction bits in dequantized envelope + * + * Outputs: gains for envelope, sinusoids and noise + * number of fraction bits for envelope gain + * sum of the total gain for each component in this band + * other updated state variables + * + * Return: none + **************************************************************************************/ +static void CalcComponentGains(PSInfoSBR *psi, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch, int env, int lim, int fbitsDQ) +{ + int d, m, mStart, mEnd, q, qm, noiseFloor, sIndexMapped; + int shift, eCurr, maxFlag, gainMax, gainMaxFBits; + int gain, sm, z, r, fbitsGain, gainScale; + unsigned char *freqBandTab; + + mStart = sbrFreq->freqLimiter[lim]; /* these are offsets from kStart */ + mEnd = sbrFreq->freqLimiter[lim + 1]; + + gainMax = psi->gainMax; + gainMaxFBits = psi->gainMaxFBits; + + d = (env == psi->la || env == sbrChan->laPrev ? 0 : 1); + freqBandTab = (sbrGrid->freqRes[env] ? sbrFreq->freqHigh : sbrFreq->freqLow); + + /* figure out which noise floor this envelope is in (only 1 or 2 noise floors allowed) */ + noiseFloor = 0; + if (sbrGrid->numNoiseFloors == 2 && sbrGrid->noiseTimeBorder[1] <= sbrGrid->envTimeBorder[env]) + noiseFloor++; + + psi->sumECurrGLim = 0; + psi->sumSM = 0; + psi->sumQM = 0; + /* calculate energy of noise to add in this limiter band */ + for (m = mStart; m < mEnd; m++) { + if (m == sbrFreq->freqNoise[psi->noiseFloorBand + 1] - sbrFreq->kStart) { + /* map current QMF band to appropriate noise floor band (NOTE: freqLimiter[0] == freqLow[0] = freqHigh[0]) */ + psi->noiseFloorBand++; + CalcNoiseDivFactors(psi->noiseDataDequant[ch][noiseFloor][psi->noiseFloorBand], &(psi->qp1Inv), &(psi->qqp1Inv)); + } + if (m == sbrFreq->freqHigh[psi->highBand + 1] - sbrFreq->kStart) + psi->highBand++; + if (m == freqBandTab[psi->sBand + 1] - sbrFreq->kStart) { + psi->sBand++; + psi->sMapped = GetSMapped(sbrGrid, sbrFreq, sbrChan, env, psi->sBand, psi->la); + } + + /* get sIndexMapped for this QMF subband */ + sIndexMapped = 0; + r = ((sbrFreq->freqHigh[psi->highBand+1] + sbrFreq->freqHigh[psi->highBand]) >> 1); + if (m + sbrFreq->kStart == r) { + /* r = center frequency, deltaStep = (env >= la || sIndexMapped'(r, numEnv'-1) == 1) */ + if (env >= psi->la || sbrChan->addHarmonic[0][r] == 1) + sIndexMapped = sbrChan->addHarmonic[1][psi->highBand]; + } + + /* save sine flags from last envelope in this frame: + * addHarmonic[0][0...63] = saved sine present flag from previous frame, for each QMF subband + * addHarmonic[1][0...nHigh-1] = addHarmonic bit from current frame, for each high-res frequency band + * from MPEG reference code - slightly different from spec + * (sIndexMapped'(m,LE'-1) can still be 0 when numEnv == psi->la) + */ + if (env == sbrGrid->numEnv - 1) { + if (m + sbrFreq->kStart == r) + sbrChan->addHarmonic[0][m + sbrFreq->kStart] = sbrChan->addHarmonic[1][psi->highBand]; + else + sbrChan->addHarmonic[0][m + sbrFreq->kStart] = 0; + } + + gain = psi->envDataDequant[ch][env][psi->sBand]; + qm = MULSHIFT32(gain, psi->qqp1Inv) << 1; + sm = (sIndexMapped ? MULSHIFT32(gain, psi->qp1Inv) << 1 : 0); + + /* three cases: (sMapped == 0 && delta == 1), (sMapped == 0 && delta == 0), (sMapped == 1) */ + if (d == 1 && psi->sMapped == 0) + gain = MULSHIFT32(psi->qp1Inv, gain) << 1; + else if (psi->sMapped != 0) + gain = MULSHIFT32(psi->qqp1Inv, gain) << 1; + + /* gain, qm, sm = Q(fbitsDQ), gainMax = Q(fbitsGainMax) */ + eCurr = psi->eCurr[m]; + if (eCurr) { + z = CLZ(eCurr) - 1; + r = InvRNormalized(eCurr << z); /* in = Q(z - eCurrExp), out = Q(29 + 31 - z + eCurrExp) */ + gainScale = MULSHIFT32(gain, r); /* out = Q(29 + 31 - z + eCurrExp + fbitsDQ - 32) */ + fbitsGain = 29 + 31 - z + psi->eCurrExp[m] + fbitsDQ - 32; + } else { + /* if eCurr == 0, then gain is unchanged (divide by EPS = 1) */ + gainScale = gain; + fbitsGain = fbitsDQ; + } + + /* see if gain for this band exceeds max gain */ + maxFlag = 0; + if (gainMax != (int)0x80000000) { + if (fbitsGain >= gainMaxFBits) { + shift = MIN(fbitsGain - gainMaxFBits, 31); + maxFlag = ((gainScale >> shift) > gainMax ? 1 : 0); + } else { + shift = MIN(gainMaxFBits - fbitsGain, 31); + maxFlag = (gainScale > (gainMax >> shift) ? 1 : 0); + } + } + + if (maxFlag) { + /* gainScale > gainMax, calculate ratio with 32/16 division */ + q = 0; + r = gainScale; /* guaranteed > 0, else maxFlag could not have been set */ + z = CLZ(r); + if (z < 16) { + q = 16 - z; + r >>= q; /* out = Q(fbitsGain - q) */ + } + + z = CLZ(gainMax) - 1; + r = (gainMax << z) / r; /* out = Q((fbitsGainMax + z) - (fbitsGain - q)) */ + q = (gainMaxFBits + z) - (fbitsGain - q); /* r = Q(q) */ + if (q > 30) { + r >>= MIN(q - 30, 31); + } else { + z = MIN(30 - q, 30); + CLIP_2N_SHIFT30(r, z); /* let r = Q30 since range = [0.0, 1.0) (clip to 0x3fffffff = 0.99999) */ + } + + qm = MULSHIFT32(qm, r) << 2; + gain = MULSHIFT32(gain, r) << 2; + psi->gLimBuf[m] = gainMax; + psi->gLimFbits[m] = gainMaxFBits; + } else { + psi->gLimBuf[m] = gainScale; + psi->gLimFbits[m] = fbitsGain; + } + + /* sumSM, sumQM, sumECurrGLim = Q(fbitsDQ - ACC_SCALE) */ + psi->smBuf[m] = sm; + psi->sumSM += (sm >> ACC_SCALE); + + psi->qmLimBuf[m] = qm; + if (env != psi->la && env != sbrChan->laPrev && sm == 0) + psi->sumQM += (qm >> ACC_SCALE); + + /* eCurr * gain^2 same as gain^2, before division by eCurr + * (but note that gain != 0 even if eCurr == 0, since it's divided by eps) + */ + if (eCurr) + psi->sumECurrGLim += (gain >> ACC_SCALE); + } +} + +/************************************************************************************** + * Function: ApplyBoost + * + * Description: calculate and apply boost factor for envelope, sinusoids, and noise + * in this limiter band (4.6.18.7.5) + * + * Inputs: initialized PSInfoSBR struct + * initialized SBRFreq struct for this SCE/CPE block + * index of current limiter band + * number of fraction bits in dequantized envelope + * + * Outputs: envelope gain, sinusoids and noise after scaling by gBoost + * format = Q(FBITS_GLIM_BOOST) for envelope gain, + * = Q(FBITS_QLIM_BOOST) for noise + * = Q(FBITS_OUT_QMFA) for sinusoids + * + * Return: none + * + * Notes: after scaling, each component has at least 1 GB + **************************************************************************************/ +static void ApplyBoost(PSInfoSBR *psi, SBRFreq *sbrFreq, int lim, int fbitsDQ) +{ + int m, mStart, mEnd, q, z, r; + int sumEOrigMapped, gBoost; + + mStart = sbrFreq->freqLimiter[lim]; /* these are offsets from kStart */ + mEnd = sbrFreq->freqLimiter[lim + 1]; + + sumEOrigMapped = psi->sumEOrigMapped >> 1; + r = (psi->sumECurrGLim >> 1) + (psi->sumSM >> 1) + (psi->sumQM >> 1); /* 1 GB fine (sm and qm are mutually exclusive in acc) */ + if (r < (1 << (31-28))) { + /* any non-zero numerator * 1/EPS_0 is > GBOOST_MAX + * round very small r to zero to avoid scaling problems + */ + gBoost = (sumEOrigMapped == 0 ? (1 << 28) : GBOOST_MAX); + z = 0; + } else if (sumEOrigMapped == 0) { + /* 1/(any non-zero denominator) * EPS_0 is appx. 0 */ + gBoost = 0; + z = 0; + } else { + /* numerator (sumEOrigMapped) and denominator (r) have same Q format (before << z) */ + z = CLZ(r) - 1; /* z = [0, 27] */ + r = InvRNormalized(r << z); + gBoost = MULSHIFT32(sumEOrigMapped, r); + } + + /* gBoost = Q(28 - z) */ + if (gBoost > (GBOOST_MAX >> z)) { + gBoost = GBOOST_MAX; + z = 0; + } + gBoost <<= z; /* gBoost = Q28, minimum 1 GB */ + + /* convert gain, noise, sinusoids to fixed Q format, clipping if necessary + * (rare, usually only happens at very low bitrates, introduces slight + * distortion into final HF mapping, but should be inaudible) + */ + for (m = mStart; m < mEnd; m++) { + /* let gLimBoost = Q24, since in practice the max values are usually 16 to 20 + * unless limiterGains == 3 (limiter off) and eCurr ~= 0 (i.e. huge gain, but only + * because the envelope has 0 power anyway) + */ + q = MULSHIFT32(psi->gLimBuf[m], gBoost) << 2; /* Q(gLimFbits) * Q(28) --> Q(gLimFbits[m]-2) */ + r = SqrtFix(q, psi->gLimFbits[m] - 2, &z); + z -= FBITS_GLIM_BOOST; + if (z >= 0) { + psi->gLimBoost[m] = r >> MIN(z, 31); + } else { + z = MIN(30, -z); + CLIP_2N_SHIFT30(r, z); + psi->gLimBoost[m] = r; + } + + q = MULSHIFT32(psi->qmLimBuf[m], gBoost) << 2; /* Q(fbitsDQ) * Q(28) --> Q(fbitsDQ-2) */ + r = SqrtFix(q, fbitsDQ - 2, &z); + z -= FBITS_QLIM_BOOST; /* << by 14, since integer sqrt of x < 2^16, and we want to leave 1 GB */ + if (z >= 0) { + psi->qmLimBoost[m] = r >> MIN(31, z); + } else { + z = MIN(30, -z); + CLIP_2N_SHIFT30(r, z); + psi->qmLimBoost[m] = r; + } + + q = MULSHIFT32(psi->smBuf[m], gBoost) << 2; /* Q(fbitsDQ) * Q(28) --> Q(fbitsDQ-2) */ + r = SqrtFix(q, fbitsDQ - 2, &z); + z -= FBITS_OUT_QMFA; /* justify for adding to signal (xBuf) later */ + if (z >= 0) { + psi->smBoost[m] = r >> MIN(31, z); + } else { + z = MIN(30, -z); + CLIP_2N_SHIFT30(r, z); + psi->smBoost[m] = r; + } + } +} + +/************************************************************************************** + * Function: CalcGain + * + * Description: calculate and apply proper gain to HF components in one envelope + * (4.6.18.7.5) + * + * Inputs: initialized PSInfoSBR struct + * initialized SBRHeader struct for this SCE/CPE block + * initialized SBRGrid struct for this channel + * initialized SBRFreq struct for this SCE/CPE block + * initialized SBRChan struct for this channel + * index of current channel (0 for SCE, 0 or 1 for CPE) + * index of current envelope + * + * Outputs: envelope gain, sinusoids and noise after scaling + * + * Return: none + **************************************************************************************/ +static void CalcGain(PSInfoSBR *psi, SBRHeader *sbrHdr, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch, int env) +{ + int lim, fbitsDQ; + + /* initialize to -1 so that mapping limiter bands to env/noise bands works right on first pass */ + psi->envBand = -1; + psi->noiseFloorBand = -1; + psi->sBand = -1; + psi->highBand = -1; + + fbitsDQ = (FBITS_OUT_DQ_ENV - psi->envDataDequantScale[ch][env]); /* Q(29 - optional scalefactor) */ + for (lim = 0; lim < sbrFreq->nLimiter; lim++) { + /* the QMF bands are divided into lim regions (consecutive, non-overlapping) */ + CalcMaxGain(psi, sbrHdr, sbrGrid, sbrFreq, ch, env, lim, fbitsDQ); + CalcComponentGains(psi, sbrGrid, sbrFreq, sbrChan, ch, env, lim, fbitsDQ); + ApplyBoost(psi, sbrFreq, lim, fbitsDQ); + } +} + +/* hSmooth table from 4.7.18.7.6, format = Q31 */ +static const int hSmoothCoef[MAX_NUM_SMOOTH_COEFS] PROGMEM = { + 0x2aaaaaab, 0x2697a512, 0x1becfa68, 0x0ebdb043, 0x04130598, +}; + +/************************************************************************************** + * Function: MapHF + * + * Description: map HF components to proper QMF bands, with optional gain smoothing + * filter (4.6.18.7.6) + * + * Inputs: initialized PSInfoSBR struct + * initialized SBRHeader struct for this SCE/CPE block + * initialized SBRGrid struct for this channel + * initialized SBRFreq struct for this SCE/CPE block + * initialized SBRChan struct for this channel + * index of current envelope + * reset flag (can be non-zero for first envelope only) + * + * Outputs: complete reconstructed subband QMF samples for this envelope + * + * Return: none + * + * Notes: ensures that output has >= MIN_GBITS_IN_QMFS guard bits, + * so it's not necessary to check anything in the synth QMF + **************************************************************************************/ +static void MapHF(PSInfoSBR *psi, SBRHeader *sbrHdr, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int env, int hfReset) +{ + int noiseTabIndex, sinIndex, gainNoiseIndex, hSL; + int i, iStart, iEnd, m, idx, j, s, n, smre, smim; + int gFilt, qFilt, xre, xim, gbMask, gbIdx; + int *XBuf; + + noiseTabIndex = sbrChan->noiseTabIndex; + sinIndex = sbrChan->sinIndex; + gainNoiseIndex = sbrChan->gainNoiseIndex; /* oldest entries in filter delay buffer */ + + if (hfReset) + noiseTabIndex = 2; /* starts at 1, double since complex */ + hSL = (sbrHdr->smoothMode ? 0 : 4); + + if (hfReset) { + for (i = 0; i < hSL; i++) { + for (m = 0; m < sbrFreq->numQMFBands; m++) { + sbrChan->gTemp[gainNoiseIndex][m] = psi->gLimBoost[m]; + sbrChan->qTemp[gainNoiseIndex][m] = psi->qmLimBoost[m]; + } + gainNoiseIndex++; + if (gainNoiseIndex == MAX_NUM_SMOOTH_COEFS) + gainNoiseIndex = 0; + } + ASSERT(env == 0); /* should only be reset when env == 0 */ + } + + iStart = sbrGrid->envTimeBorder[env]; + iEnd = sbrGrid->envTimeBorder[env+1]; + for (i = iStart; i < iEnd; i++) { + /* save new values in temp buffers (delay) + * we only store MAX_NUM_SMOOTH_COEFS most recent values, + * so don't keep storing the same value over and over + */ + if (i - iStart < MAX_NUM_SMOOTH_COEFS) { + for (m = 0; m < sbrFreq->numQMFBands; m++) { + sbrChan->gTemp[gainNoiseIndex][m] = psi->gLimBoost[m]; + sbrChan->qTemp[gainNoiseIndex][m] = psi->qmLimBoost[m]; + } + } + + /* see 4.6.18.7.6 */ + XBuf = psi->XBuf[i + HF_ADJ][sbrFreq->kStart]; + gbMask = 0; + for (m = 0; m < sbrFreq->numQMFBands; m++) { + if (env == psi->la || env == sbrChan->laPrev) { + /* no smoothing filter for gain, and qFilt = 0 (only need to do once) */ + if (i == iStart) { + psi->gFiltLast[m] = sbrChan->gTemp[gainNoiseIndex][m]; + psi->qFiltLast[m] = 0; + } + } else if (hSL == 0) { + /* no smoothing filter for gain, (only need to do once) */ + if (i == iStart) { + psi->gFiltLast[m] = sbrChan->gTemp[gainNoiseIndex][m]; + psi->qFiltLast[m] = sbrChan->qTemp[gainNoiseIndex][m]; + } + } else { + /* apply smoothing filter to gain and noise (after MAX_NUM_SMOOTH_COEFS, it's always the same) */ + if (i - iStart < MAX_NUM_SMOOTH_COEFS) { + gFilt = 0; + qFilt = 0; + idx = gainNoiseIndex; + for (j = 0; j < MAX_NUM_SMOOTH_COEFS; j++) { + /* sum(abs(hSmoothCoef[j])) for all j < 1.0 */ + gFilt += MULSHIFT32(sbrChan->gTemp[idx][m], hSmoothCoef[j]); + qFilt += MULSHIFT32(sbrChan->qTemp[idx][m], hSmoothCoef[j]); + idx--; + if (idx < 0) + idx += MAX_NUM_SMOOTH_COEFS; + } + psi->gFiltLast[m] = gFilt << 1; /* restore to Q(FBITS_GLIM_BOOST) (gain of filter < 1.0, so no overflow) */ + psi->qFiltLast[m] = qFilt << 1; /* restore to Q(FBITS_QLIM_BOOST) */ + } + } + + if (psi->smBoost[m] != 0) { + /* add scaled signal and sinusoid, don't add noise (qFilt = 0) */ + smre = psi->smBoost[m]; + smim = smre; + + /* sinIndex: [0] xre += sm [1] xim += sm*s [2] xre -= sm [3] xim -= sm*s */ + s = (sinIndex >> 1); /* if 2 or 3, flip sign to subtract sm */ + s <<= 31; + smre ^= (s >> 31); + smre -= (s >> 31); + s ^= ((m + sbrFreq->kStart) << 31); + smim ^= (s >> 31); + smim -= (s >> 31); + + /* if sinIndex == 0 or 2, smim = 0; if sinIndex == 1 or 3, smre = 0 */ + s = sinIndex << 31; + smim &= (s >> 31); + s ^= 0x80000000; + smre &= (s >> 31); + + noiseTabIndex += 2; /* noise filtered by 0, but still need to bump index */ + } else { + /* add scaled signal and scaled noise */ + qFilt = psi->qFiltLast[m]; + n = noiseTab[noiseTabIndex++]; + smre = MULSHIFT32(n, qFilt) >> (FBITS_QLIM_BOOST - 1 - FBITS_OUT_QMFA); + + n = noiseTab[noiseTabIndex++]; + smim = MULSHIFT32(n, qFilt) >> (FBITS_QLIM_BOOST - 1 - FBITS_OUT_QMFA); + } + noiseTabIndex &= 1023; /* 512 complex numbers */ + + gFilt = psi->gFiltLast[m]; + xre = MULSHIFT32(gFilt, XBuf[0]); + xim = MULSHIFT32(gFilt, XBuf[1]); + CLIP_2N_SHIFT30(xre, 32 - FBITS_GLIM_BOOST); + CLIP_2N_SHIFT30(xim, 32 - FBITS_GLIM_BOOST); + + xre += smre; *XBuf++ = xre; + xim += smim; *XBuf++ = xim; + + gbMask |= FASTABS(xre); + gbMask |= FASTABS(xim); + } + /* update circular buffer index */ + gainNoiseIndex++; + if (gainNoiseIndex == MAX_NUM_SMOOTH_COEFS) + gainNoiseIndex = 0; + + sinIndex++; + sinIndex &= 3; + + /* ensure MIN_GBITS_IN_QMFS guard bits in output + * almost never occurs in practice, but checking here makes synth QMF logic very simple + */ + if (gbMask >> (31 - MIN_GBITS_IN_QMFS)) { + XBuf = psi->XBuf[i + HF_ADJ][sbrFreq->kStart]; + for (m = 0; m < sbrFreq->numQMFBands; m++) { + xre = XBuf[0]; xim = XBuf[1]; + CLIP_2N(xre, (31 - MIN_GBITS_IN_QMFS)); + CLIP_2N(xim, (31 - MIN_GBITS_IN_QMFS)); + *XBuf++ = xre; *XBuf++ = xim; + } + CLIP_2N(gbMask, (31 - MIN_GBITS_IN_QMFS)); + } + gbIdx = ((i + HF_ADJ) >> 5) & 0x01; + sbrChan->gbMask[gbIdx] |= gbMask; + } + sbrChan->noiseTabIndex = noiseTabIndex; + sbrChan->sinIndex = sinIndex; + sbrChan->gainNoiseIndex = gainNoiseIndex; +} + +/************************************************************************************** + * Function: AdjustHighFreq + * + * Description: adjust high frequencies and add noise and sinusoids (4.6.18.7) + * + * Inputs: initialized PSInfoSBR struct + * initialized SBRHeader struct for this SCE/CPE block + * initialized SBRGrid struct for this channel + * initialized SBRFreq struct for this SCE/CPE block + * initialized SBRChan struct for this channel + * index of current channel (0 for SCE, 0 or 1 for CPE) + * + * Outputs: complete reconstructed subband QMF samples for this channel + * + * Return: none + **************************************************************************************/ +void AdjustHighFreq(PSInfoSBR *psi, SBRHeader *sbrHdr, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch) +{ + int i, env, hfReset; + unsigned char frameClass, pointer; + + frameClass = sbrGrid->frameClass; + pointer = sbrGrid->pointer; + + /* derive la from table 4.159 */ + if ((frameClass == SBR_GRID_FIXVAR || frameClass == SBR_GRID_VARVAR) && pointer > 0) + psi->la = sbrGrid->numEnv + 1 - pointer; + else if (frameClass == SBR_GRID_VARFIX && pointer > 1) + psi->la = pointer - 1; + else + psi->la = -1; + + /* for each envelope, estimate gain and adjust SBR QMF bands */ + hfReset = sbrChan->reset; + for (env = 0; env < sbrGrid->numEnv; env++) { + EstimateEnvelope(psi, sbrHdr, sbrGrid, sbrFreq, env); + CalcGain(psi, sbrHdr, sbrGrid, sbrFreq, sbrChan, ch, env); + MapHF(psi, sbrHdr, sbrGrid, sbrFreq, sbrChan, env, hfReset); + hfReset = 0; /* only set for first envelope after header reset */ + } + + /* set saved sine flags to 0 for QMF bands outside of current frequency range */ + for (i = 0; i < sbrFreq->freqLimiter[0] + sbrFreq->kStart; i++) + sbrChan->addHarmonic[0][i] = 0; + for (i = sbrFreq->freqLimiter[sbrFreq->nLimiter] + sbrFreq->kStart; i < 64; i++) + sbrChan->addHarmonic[0][i] = 0; + sbrChan->addHarmonicFlag[0] = sbrChan->addHarmonicFlag[1]; + + /* save la for next frame */ + if (psi->la == sbrGrid->numEnv) + sbrChan->laPrev = 0; + else + sbrChan->laPrev = -1; +} diff --git a/components/spotify/cspot/bell/libhelix-aac/sbrhfgen.c b/components/spotify/cspot/bell/libhelix-aac/sbrhfgen.c new file mode 100644 index 00000000..e5f27eb6 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/sbrhfgen.c @@ -0,0 +1,616 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: sbrhfgen.c,v 1.2 2005/05/19 20:45:20 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * sbrhfgen.c - high frequency generation for SBR + **************************************************************************************/ + +#include "sbr.h" +#include "assembly.h" + +#define FBITS_LPCOEFS 29 /* Q29 for range of (-4, 4) */ +#define MAG_16 (16 * (1 << (32 - (2*(32-FBITS_LPCOEFS))))) /* i.e. 16 in Q26 format */ +#define RELAX_COEF 0x7ffff79c /* 1.0 / (1.0 + 1e-6), Q31 */ + +/* newBWTab[prev invfMode][curr invfMode], format = Q31 (table 4.158) + * sample file which uses all of these: al_sbr_sr_64_2_fsaac32.aac + */ +static const int newBWTab[4][4] PROGMEM = { + {0x00000000, 0x4ccccccd, 0x73333333, 0x7d70a3d7}, + {0x4ccccccd, 0x60000000, 0x73333333, 0x7d70a3d7}, + {0x00000000, 0x60000000, 0x73333333, 0x7d70a3d7}, + {0x00000000, 0x60000000, 0x73333333, 0x7d70a3d7}, +}; + +/************************************************************************************** + * Function: CVKernel1 + * + * Description: kernel of covariance matrix calculation for p01, p11, p12, p22 + * + * Inputs: buffer of low-freq samples, starting at time index = 0, + * freq index = patch subband + * + * Outputs: 64-bit accumulators for p01re, p01im, p12re, p12im, p11re, p22re + * stored in accBuf + * + * Return: none + * + * Notes: this is carefully written to be efficient on ARM + * use the assembly code version in sbrcov.s when building for ARM! + **************************************************************************************/ +#if (defined (XXXX__arm) && defined (__ARMCC_VERSION)) || (defined (_WIN32) && defined (_WIN32_WCE) && defined (ARM)) || (defined(__GNUC__) && defined(XXXX__arm__)) +#ifdef __cplusplus +extern "C" +#endif +void CVKernel1(int *XBuf, int *accBuf); +#else +void CVKernel1(int *XBuf, int *accBuf) +{ + U64 p01re, p01im, p12re, p12im, p11re, p22re; + int n, x0re, x0im, x1re, x1im; + + x0re = XBuf[0]; + x0im = XBuf[1]; + XBuf += (2*64); + x1re = XBuf[0]; + x1im = XBuf[1]; + XBuf += (2*64); + + p01re.w64 = p01im.w64 = 0; + p12re.w64 = p12im.w64 = 0; + p11re.w64 = 0; + p22re.w64 = 0; + + p12re.w64 = MADD64(p12re.w64, x1re, x0re); + p12re.w64 = MADD64(p12re.w64, x1im, x0im); + p12im.w64 = MADD64(p12im.w64, x0re, x1im); + p12im.w64 = MADD64(p12im.w64, -x0im, x1re); + p22re.w64 = MADD64(p22re.w64, x0re, x0re); + p22re.w64 = MADD64(p22re.w64, x0im, x0im); + for (n = (NUM_TIME_SLOTS*SAMPLES_PER_SLOT + 6); n != 0; n--) { + /* 4 input, 3*2 acc, 1 ptr, 1 loop counter = 12 registers (use same for x0im, -x0im) */ + x0re = x1re; + x0im = x1im; + x1re = XBuf[0]; + x1im = XBuf[1]; + + p01re.w64 = MADD64(p01re.w64, x1re, x0re); + p01re.w64 = MADD64(p01re.w64, x1im, x0im); + p01im.w64 = MADD64(p01im.w64, x0re, x1im); + p01im.w64 = MADD64(p01im.w64, -x0im, x1re); + p11re.w64 = MADD64(p11re.w64, x0re, x0re); + p11re.w64 = MADD64(p11re.w64, x0im, x0im); + + XBuf += (2*64); + } + /* these can be derived by slight changes to account for boundary conditions */ + p12re.w64 += p01re.w64; + p12re.w64 = MADD64(p12re.w64, x1re, -x0re); + p12re.w64 = MADD64(p12re.w64, x1im, -x0im); + p12im.w64 += p01im.w64; + p12im.w64 = MADD64(p12im.w64, x0re, -x1im); + p12im.w64 = MADD64(p12im.w64, x0im, x1re); + p22re.w64 += p11re.w64; + p22re.w64 = MADD64(p22re.w64, x0re, -x0re); + p22re.w64 = MADD64(p22re.w64, x0im, -x0im); + + accBuf[0] = p01re.r.lo32; accBuf[1] = p01re.r.hi32; + accBuf[2] = p01im.r.lo32; accBuf[3] = p01im.r.hi32; + accBuf[4] = p11re.r.lo32; accBuf[5] = p11re.r.hi32; + accBuf[6] = p12re.r.lo32; accBuf[7] = p12re.r.hi32; + accBuf[8] = p12im.r.lo32; accBuf[9] = p12im.r.hi32; + accBuf[10] = p22re.r.lo32; accBuf[11] = p22re.r.hi32; +} +#endif + +/************************************************************************************** + * Function: CalcCovariance1 + * + * Description: calculate covariance matrix for p01, p12, p11, p22 (4.6.18.6.2) + * + * Inputs: buffer of low-freq samples, starting at time index 0, + * freq index = patch subband + * + * Outputs: complex covariance elements p01re, p01im, p12re, p12im, p11re, p22re + * (p11im = p22im = 0) + * format = integer (Q0) * 2^N, with scalefactor N >= 0 + * + * Return: scalefactor N + * + * Notes: outputs are normalized to have 1 GB (sign in at least top 2 bits) + **************************************************************************************/ +static int CalcCovariance1(int *XBuf, int *p01reN, int *p01imN, int *p12reN, int *p12imN, int *p11reN, int *p22reN) +{ + int accBuf[2*6]; + int n, z, s, loShift, hiShift, gbMask; + U64 p01re, p01im, p12re, p12im, p11re, p22re; + + CVKernel1(XBuf, accBuf); + p01re.r.lo32 = accBuf[0]; p01re.r.hi32 = accBuf[1]; + p01im.r.lo32 = accBuf[2]; p01im.r.hi32 = accBuf[3]; + p11re.r.lo32 = accBuf[4]; p11re.r.hi32 = accBuf[5]; + p12re.r.lo32 = accBuf[6]; p12re.r.hi32 = accBuf[7]; + p12im.r.lo32 = accBuf[8]; p12im.r.hi32 = accBuf[9]; + p22re.r.lo32 = accBuf[10]; p22re.r.hi32 = accBuf[11]; + + /* 64-bit accumulators now have 2*FBITS_OUT_QMFA fraction bits + * want to scale them down to integers (32-bit signed, Q0) + * with scale factor of 2^n, n >= 0 + * leave 2 GB's for calculating determinant, so take top 30 non-zero bits + */ + gbMask = ((p01re.r.hi32) ^ (p01re.r.hi32 >> 31)) | ((p01im.r.hi32) ^ (p01im.r.hi32 >> 31)); + gbMask |= ((p12re.r.hi32) ^ (p12re.r.hi32 >> 31)) | ((p12im.r.hi32) ^ (p12im.r.hi32 >> 31)); + gbMask |= ((p11re.r.hi32) ^ (p11re.r.hi32 >> 31)) | ((p22re.r.hi32) ^ (p22re.r.hi32 >> 31)); + if (gbMask == 0) { + s = p01re.r.hi32 >> 31; gbMask = (p01re.r.lo32 ^ s) - s; + s = p01im.r.hi32 >> 31; gbMask |= (p01im.r.lo32 ^ s) - s; + s = p12re.r.hi32 >> 31; gbMask |= (p12re.r.lo32 ^ s) - s; + s = p12im.r.hi32 >> 31; gbMask |= (p12im.r.lo32 ^ s) - s; + s = p11re.r.hi32 >> 31; gbMask |= (p11re.r.lo32 ^ s) - s; + s = p22re.r.hi32 >> 31; gbMask |= (p22re.r.lo32 ^ s) - s; + z = 32 + CLZ(gbMask); + } else { + gbMask = FASTABS(p01re.r.hi32) | FASTABS(p01im.r.hi32); + gbMask |= FASTABS(p12re.r.hi32) | FASTABS(p12im.r.hi32); + gbMask |= FASTABS(p11re.r.hi32) | FASTABS(p22re.r.hi32); + z = CLZ(gbMask); + } + + n = 64 - z; /* number of non-zero bits in bottom of 64-bit word */ + if (n <= 30) { + loShift = (30 - n); + *p01reN = p01re.r.lo32 << loShift; *p01imN = p01im.r.lo32 << loShift; + *p12reN = p12re.r.lo32 << loShift; *p12imN = p12im.r.lo32 << loShift; + *p11reN = p11re.r.lo32 << loShift; *p22reN = p22re.r.lo32 << loShift; + return -(loShift + 2*FBITS_OUT_QMFA); + } else if (n < 32 + 30) { + loShift = (n - 30); + hiShift = 32 - loShift; + *p01reN = (p01re.r.hi32 << hiShift) | (p01re.r.lo32 >> loShift); + *p01imN = (p01im.r.hi32 << hiShift) | (p01im.r.lo32 >> loShift); + *p12reN = (p12re.r.hi32 << hiShift) | (p12re.r.lo32 >> loShift); + *p12imN = (p12im.r.hi32 << hiShift) | (p12im.r.lo32 >> loShift); + *p11reN = (p11re.r.hi32 << hiShift) | (p11re.r.lo32 >> loShift); + *p22reN = (p22re.r.hi32 << hiShift) | (p22re.r.lo32 >> loShift); + return (loShift - 2*FBITS_OUT_QMFA); + } else { + hiShift = n - (32 + 30); + *p01reN = p01re.r.hi32 >> hiShift; *p01imN = p01im.r.hi32 >> hiShift; + *p12reN = p12re.r.hi32 >> hiShift; *p12imN = p12im.r.hi32 >> hiShift; + *p11reN = p11re.r.hi32 >> hiShift; *p22reN = p22re.r.hi32 >> hiShift; + return (32 - 2*FBITS_OUT_QMFA - hiShift); + } + + return 0; +} + +/************************************************************************************** + * Function: CVKernel2 + * + * Description: kernel of covariance matrix calculation for p02 + * + * Inputs: buffer of low-freq samples, starting at time index = 0, + * freq index = patch subband + * + * Outputs: 64-bit accumulators for p02re, p02im stored in accBuf + * + * Return: none + * + * Notes: this is carefully written to be efficient on ARM + * use the assembly code version in sbrcov.s when building for ARM! + **************************************************************************************/ +#if (defined (XXXX__arm) && defined (__ARMCC_VERSION)) || (defined (_WIN32) && defined (_WIN32_WCE) && defined (ARM)) || (defined(__GNUC__) && defined(XXXX__arm__)) +#ifdef __cplusplus +extern "C" +#endif +void CVKernel2(int *XBuf, int *accBuf); +#else +void CVKernel2(int *XBuf, int *accBuf) +{ + U64 p02re, p02im; + int n, x0re, x0im, x1re, x1im, x2re, x2im; + + p02re.w64 = p02im.w64 = 0; + + x0re = XBuf[0]; + x0im = XBuf[1]; + XBuf += (2*64); + x1re = XBuf[0]; + x1im = XBuf[1]; + XBuf += (2*64); + + for (n = (NUM_TIME_SLOTS*SAMPLES_PER_SLOT + 6); n != 0; n--) { + /* 6 input, 2*2 acc, 1 ptr, 1 loop counter = 12 registers (use same for x0im, -x0im) */ + x2re = XBuf[0]; + x2im = XBuf[1]; + + p02re.w64 = MADD64(p02re.w64, x2re, x0re); + p02re.w64 = MADD64(p02re.w64, x2im, x0im); + p02im.w64 = MADD64(p02im.w64, x0re, x2im); + p02im.w64 = MADD64(p02im.w64, -x0im, x2re); + + x0re = x1re; + x0im = x1im; + x1re = x2re; + x1im = x2im; + XBuf += (2*64); + } + + accBuf[0] = p02re.r.lo32; + accBuf[1] = p02re.r.hi32; + accBuf[2] = p02im.r.lo32; + accBuf[3] = p02im.r.hi32; +} +#endif + +/************************************************************************************** + * Function: CalcCovariance2 + * + * Description: calculate covariance matrix for p02 (4.6.18.6.2) + * + * Inputs: buffer of low-freq samples, starting at time index = 0, + * freq index = patch subband + * + * Outputs: complex covariance element p02re, p02im + * format = integer (Q0) * 2^N, with scalefactor N >= 0 + * + * Return: scalefactor N + * + * Notes: outputs are normalized to have 1 GB (sign in at least top 2 bits) + **************************************************************************************/ +static int CalcCovariance2(int *XBuf, int *p02reN, int *p02imN) +{ + U64 p02re, p02im; + int n, z, s, loShift, hiShift, gbMask; + int accBuf[2*2]; + + CVKernel2(XBuf, accBuf); + p02re.r.lo32 = accBuf[0]; + p02re.r.hi32 = accBuf[1]; + p02im.r.lo32 = accBuf[2]; + p02im.r.hi32 = accBuf[3]; + + /* 64-bit accumulators now have 2*FBITS_OUT_QMFA fraction bits + * want to scale them down to integers (32-bit signed, Q0) + * with scale factor of 2^n, n >= 0 + * leave 1 GB for calculating determinant, so take top 30 non-zero bits + */ + gbMask = ((p02re.r.hi32) ^ (p02re.r.hi32 >> 31)) | ((p02im.r.hi32) ^ (p02im.r.hi32 >> 31)); + if (gbMask == 0) { + s = p02re.r.hi32 >> 31; gbMask = (p02re.r.lo32 ^ s) - s; + s = p02im.r.hi32 >> 31; gbMask |= (p02im.r.lo32 ^ s) - s; + z = 32 + CLZ(gbMask); + } else { + gbMask = FASTABS(p02re.r.hi32) | FASTABS(p02im.r.hi32); + z = CLZ(gbMask); + } + n = 64 - z; /* number of non-zero bits in bottom of 64-bit word */ + + if (n <= 30) { + loShift = (30 - n); + *p02reN = p02re.r.lo32 << loShift; + *p02imN = p02im.r.lo32 << loShift; + return -(loShift + 2*FBITS_OUT_QMFA); + } else if (n < 32 + 30) { + loShift = (n - 30); + hiShift = 32 - loShift; + *p02reN = (p02re.r.hi32 << hiShift) | (p02re.r.lo32 >> loShift); + *p02imN = (p02im.r.hi32 << hiShift) | (p02im.r.lo32 >> loShift); + return (loShift - 2*FBITS_OUT_QMFA); + } else { + hiShift = n - (32 + 30); + *p02reN = p02re.r.hi32 >> hiShift; + *p02imN = p02im.r.hi32 >> hiShift; + return (32 - 2*FBITS_OUT_QMFA - hiShift); + } + + return 0; +} + +/************************************************************************************** + * Function: CalcLPCoefs + * + * Description: calculate linear prediction coefficients for one subband (4.6.18.6.2) + * + * Inputs: buffer of low-freq samples, starting at time index = 0, + * freq index = patch subband + * number of guard bits in input sample buffer + * + * Outputs: complex LP coefficients a0re, a0im, a1re, a1im, format = Q29 + * + * Return: none + * + * Notes: output coefficients (a0re, a0im, a1re, a1im) clipped to range (-4, 4) + * if the comples coefficients have magnitude >= 4.0, they are all + * set to 0 (see spec) + **************************************************************************************/ +static void CalcLPCoefs(int *XBuf, int *a0re, int *a0im, int *a1re, int *a1im, int gb) +{ + int zFlag, n1, n2, nd, d, dInv, tre, tim; + int p01re, p01im, p02re, p02im, p12re, p12im, p11re, p22re; + + /* pre-scale to avoid overflow - probably never happens in practice (see QMFA) + * max bit growth per accumulator = 38*2 = 76 mul-adds (X * X) + * using 64-bit MADD, so if X has n guard bits, X*X has 2n+1 guard bits + * gain 1 extra sign bit per multiply, so ensure ceil(log2(76/2) / 2) = 3 guard bits on inputs + */ + if (gb < 3) { + nd = 3 - gb; + for (n1 = (NUM_TIME_SLOTS*SAMPLES_PER_SLOT + 6 + 2); n1 != 0; n1--) { + XBuf[0] >>= nd; XBuf[1] >>= nd; + XBuf += (2*64); + } + XBuf -= (2*64*(NUM_TIME_SLOTS*SAMPLES_PER_SLOT + 6 + 2)); + } + + /* calculate covariance elements */ + n1 = CalcCovariance1(XBuf, &p01re, &p01im, &p12re, &p12im, &p11re, &p22re); + n2 = CalcCovariance2(XBuf, &p02re, &p02im); + + /* normalize everything to larger power of 2 scalefactor, call it n1 */ + if (n1 < n2) { + nd = MIN(n2 - n1, 31); + p01re >>= nd; p01im >>= nd; + p12re >>= nd; p12im >>= nd; + p11re >>= nd; p22re >>= nd; + n1 = n2; + } else if (n1 > n2) { + nd = MIN(n1 - n2, 31); + p02re >>= nd; p02im >>= nd; + } + + /* calculate determinant of covariance matrix (at least 1 GB in pXX) */ + d = MULSHIFT32(p12re, p12re) + MULSHIFT32(p12im, p12im); + d = MULSHIFT32(d, RELAX_COEF) << 1; + d = MULSHIFT32(p11re, p22re) - d; + ASSERT(d >= 0); /* should never be < 0 */ + + zFlag = 0; + *a0re = *a0im = 0; + *a1re = *a1im = 0; + if (d > 0) { + /* input = Q31 d = Q(-2*n1 - 32 + nd) = Q31 * 2^(31 + 2*n1 + 32 - nd) + * inverse = Q29 dInv = Q29 * 2^(-31 - 2*n1 - 32 + nd) = Q(29 + 31 + 2*n1 + 32 - nd) + * + * numerator has same Q format as d, since it's sum of normalized squares + * so num * inverse = Q(-2*n1 - 32) * Q(29 + 31 + 2*n1 + 32 - nd) + * = Q(29 + 31 - nd), drop low 32 in MULSHIFT32 + * = Q(29 + 31 - 32 - nd) = Q(28 - nd) + */ + nd = CLZ(d) - 1; + d <<= nd; + dInv = InvRNormalized(d); + + /* 1 GB in pXX */ + tre = MULSHIFT32(p01re, p12re) - MULSHIFT32(p01im, p12im) - MULSHIFT32(p02re, p11re); + tre = MULSHIFT32(tre, dInv); + tim = MULSHIFT32(p01re, p12im) + MULSHIFT32(p01im, p12re) - MULSHIFT32(p02im, p11re); + tim = MULSHIFT32(tim, dInv); + + /* if d is extremely small, just set coefs to 0 (would have poor precision anyway) */ + if (nd > 28 || (FASTABS(tre) >> (28 - nd)) >= 4 || (FASTABS(tim) >> (28 - nd)) >= 4) { + zFlag = 1; + } else { + *a1re = tre << (FBITS_LPCOEFS - 28 + nd); /* i.e. convert Q(28 - nd) to Q(29) */ + *a1im = tim << (FBITS_LPCOEFS - 28 + nd); + } + } + + if (p11re) { + /* input = Q31 p11re = Q(-n1 + nd) = Q31 * 2^(31 + n1 - nd) + * inverse = Q29 dInv = Q29 * 2^(-31 - n1 + nd) = Q(29 + 31 + n1 - nd) + * + * numerator is Q(-n1 - 3) + * so num * inverse = Q(-n1 - 3) * Q(29 + 31 + n1 - nd) + * = Q(29 + 31 - 3 - nd), drop low 32 in MULSHIFT32 + * = Q(29 + 31 - 3 - 32 - nd) = Q(25 - nd) + */ + nd = CLZ(p11re) - 1; /* assume positive */ + p11re <<= nd; + dInv = InvRNormalized(p11re); + + /* a1re, a1im = Q29, so scaled by (n1 + 3) */ + tre = (p01re >> 3) + MULSHIFT32(p12re, *a1re) + MULSHIFT32(p12im, *a1im); + tre = -MULSHIFT32(tre, dInv); + tim = (p01im >> 3) - MULSHIFT32(p12im, *a1re) + MULSHIFT32(p12re, *a1im); + tim = -MULSHIFT32(tim, dInv); + + if (nd > 25 || (FASTABS(tre) >> (25 - nd)) >= 4 || (FASTABS(tim) >> (25 - nd)) >= 4) { + zFlag = 1; + } else { + *a0re = tre << (FBITS_LPCOEFS - 25 + nd); /* i.e. convert Q(25 - nd) to Q(29) */ + *a0im = tim << (FBITS_LPCOEFS - 25 + nd); + } + } + + /* see 4.6.18.6.2 - if magnitude of a0 or a1 >= 4 then a0 = a1 = 0 + * i.e. a0re < 4, a0im < 4, a1re < 4, a1im < 4 + * Q29*Q29 = Q26 + */ + if (zFlag || MULSHIFT32(*a0re, *a0re) + MULSHIFT32(*a0im, *a0im) >= MAG_16 || MULSHIFT32(*a1re, *a1re) + MULSHIFT32(*a1im, *a1im) >= MAG_16) { + *a0re = *a0im = 0; + *a1re = *a1im = 0; + } + + /* no need to clip - we never changed the XBuf data, just used it to calculate a0 and a1 */ + if (gb < 3) { + nd = 3 - gb; + for (n1 = (NUM_TIME_SLOTS*SAMPLES_PER_SLOT + 6 + 2); n1 != 0; n1--) { + XBuf[0] <<= nd; XBuf[1] <<= nd; + XBuf += (2*64); + } + } +} + +/************************************************************************************** + * Function: GenerateHighFreq + * + * Description: generate high frequencies with SBR (4.6.18.6) + * + * Inputs: initialized PSInfoSBR struct + * initialized SBRGrid struct for this channel + * initialized SBRFreq struct for this SCE/CPE block + * initialized SBRChan struct for this channel + * index of current channel (0 for SCE, 0 or 1 for CPE) + * + * Outputs: new high frequency samples starting at frequency kStart + * + * Return: none + **************************************************************************************/ +void GenerateHighFreq(PSInfoSBR *psi, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch) +{ + int band, newBW, c, t, gb, gbMask, gbIdx; + int currPatch, p, x, k, g, i, iStart, iEnd, bw, bwsq; + int a0re, a0im, a1re, a1im; + int x1re, x1im, x2re, x2im; + int ACCre, ACCim; + int *XBufLo, *XBufHi; + (void) ch; + + /* calculate array of chirp factors */ + for (band = 0; band < sbrFreq->numNoiseFloorBands; band++) { + c = sbrChan->chirpFact[band]; /* previous (bwArray') */ + newBW = newBWTab[sbrChan->invfMode[0][band]][sbrChan->invfMode[1][band]]; + + /* weighted average of new and old (can't overflow - total gain = 1.0) */ + if (newBW < c) + t = MULSHIFT32(newBW, 0x60000000) + MULSHIFT32(0x20000000, c); /* new is smaller: 0.75*new + 0.25*old */ + else + t = MULSHIFT32(newBW, 0x74000000) + MULSHIFT32(0x0c000000, c); /* new is larger: 0.90625*new + 0.09375*old */ + t <<= 1; + + if (t < 0x02000000) /* below 0.015625, clip to 0 */ + t = 0; + if (t > 0x7f800000) /* clip to 0.99609375 */ + t = 0x7f800000; + + /* save curr as prev for next time */ + sbrChan->chirpFact[band] = t; + sbrChan->invfMode[0][band] = sbrChan->invfMode[1][band]; + } + + iStart = sbrGrid->envTimeBorder[0] + HF_ADJ; + iEnd = sbrGrid->envTimeBorder[sbrGrid->numEnv] + HF_ADJ; + + /* generate new high freqs from low freqs, patches, and chirp factors */ + k = sbrFreq->kStart; + g = 0; + bw = sbrChan->chirpFact[g]; + bwsq = MULSHIFT32(bw, bw) << 1; + + gbMask = (sbrChan->gbMask[0] | sbrChan->gbMask[1]); /* older 32 | newer 8 */ + gb = CLZ(gbMask) - 1; + + for (currPatch = 0; currPatch < sbrFreq->numPatches; currPatch++) { + for (x = 0; x < sbrFreq->patchNumSubbands[currPatch]; x++) { + /* map k to corresponding noise floor band */ + if (k >= sbrFreq->freqNoise[g+1]) { + g++; + bw = sbrChan->chirpFact[g]; /* Q31 */ + bwsq = MULSHIFT32(bw, bw) << 1; /* Q31 */ + } + + p = sbrFreq->patchStartSubband[currPatch] + x; /* low QMF band */ + XBufHi = psi->XBuf[iStart][k]; + if (bw) { + CalcLPCoefs(psi->XBuf[0][p], &a0re, &a0im, &a1re, &a1im, gb); + + a0re = MULSHIFT32(bw, a0re); /* Q31 * Q29 = Q28 */ + a0im = MULSHIFT32(bw, a0im); + a1re = MULSHIFT32(bwsq, a1re); + a1im = MULSHIFT32(bwsq, a1im); + + XBufLo = psi->XBuf[iStart-2][p]; + + x2re = XBufLo[0]; /* RE{XBuf[n-2]} */ + x2im = XBufLo[1]; /* IM{XBuf[n-2]} */ + XBufLo += (64*2); + + x1re = XBufLo[0]; /* RE{XBuf[n-1]} */ + x1im = XBufLo[1]; /* IM{XBuf[n-1]} */ + XBufLo += (64*2); + + for (i = iStart; i < iEnd; i++) { + /* a0re/im, a1re/im are Q28 with at least 1 GB, + * so the summing for AACre/im is fine (1 GB in, plus 1 from MULSHIFT32) + */ + ACCre = MULSHIFT32(x2re, a1re) - MULSHIFT32(x2im, a1im); + ACCim = MULSHIFT32(x2re, a1im) + MULSHIFT32(x2im, a1re); + x2re = x1re; + x2im = x1im; + + ACCre += MULSHIFT32(x1re, a0re) - MULSHIFT32(x1im, a0im); + ACCim += MULSHIFT32(x1re, a0im) + MULSHIFT32(x1im, a0re); + x1re = XBufLo[0]; /* RE{XBuf[n]} */ + x1im = XBufLo[1]; /* IM{XBuf[n]} */ + XBufLo += (64*2); + + /* lost 4 fbits when scaling by a0re/im, a1re/im (Q28) */ + CLIP_2N_SHIFT30(ACCre, 4); + ACCre += x1re; + CLIP_2N_SHIFT30(ACCim, 4); + ACCim += x1im; + + XBufHi[0] = ACCre; + XBufHi[1] = ACCim; + XBufHi += (64*2); + + /* update guard bit masks */ + gbMask = FASTABS(ACCre); + gbMask |= FASTABS(ACCim); + gbIdx = (i >> 5) & 0x01; /* 0 if i < 32, 1 if i >= 32 */ + sbrChan->gbMask[gbIdx] |= gbMask; + } + } else { + XBufLo = (int *)psi->XBuf[iStart][p]; + for (i = iStart; i < iEnd; i++) { + XBufHi[0] = XBufLo[0]; + XBufHi[1] = XBufLo[1]; + XBufLo += (64*2); + XBufHi += (64*2); + } + } + k++; /* high QMF band */ + } + } +} + + diff --git a/components/spotify/cspot/bell/libhelix-aac/sbrhuff.c b/components/spotify/cspot/bell/libhelix-aac/sbrhuff.c new file mode 100644 index 00000000..a174e1e8 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/sbrhuff.c @@ -0,0 +1,476 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: sbrhuff.c,v 1.1 2005/02/26 01:47:35 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * sbrhuff.c - functions for unpacking Huffman-coded envelope and noise data + **************************************************************************************/ + +#include "sbr.h" +#include "assembly.h" + +/************************************************************************************** + * Function: DecodeHuffmanScalar + * + * Description: decode one Huffman symbol from bitstream + * + * Inputs: pointers to Huffman table and info struct + * left-aligned bit buffer with >= huffTabInfo->maxBits bits + * + * Outputs: decoded symbol in *val + * + * Return: number of bits in symbol + * + * Notes: assumes canonical Huffman codes: + * first CW always 0, we have "count" CW's of length "nBits" bits + * starting CW for codes of length nBits+1 = + * (startCW[nBits] + count[nBits]) << 1 + * if there are no codes at nBits, then we just keep << 1 each time + * (since count[nBits] = 0) + **************************************************************************************/ +static int DecodeHuffmanScalar(const signed /*short*/ int *huffTab, const HuffInfo *huffTabInfo, unsigned int bitBuf, signed int *val) +{ + unsigned int count, start, shift, t; + const unsigned int /*char*/ *countPtr; + const signed int /*short*/ *map; + + map = huffTab + huffTabInfo->offset; + countPtr = huffTabInfo->count; + + start = 0; + count = 0; + shift = 32; + do { + start += count; + start <<= 1; + map += count; + count = *countPtr++; + shift--; + t = (bitBuf >> shift) - start; + } while (t >= count); + + *val = (signed int)map[t]; + return (countPtr - huffTabInfo->count); +} + +/************************************************************************************** + * Function: DecodeOneSymbol + * + * Description: dequantize one Huffman symbol from bitstream, + * using table huffTabSBR[huffTabIndex] + * + * Inputs: BitStreamInfo struct pointing to start of next Huffman codeword + * index of Huffman table + * + * Outputs: bitstream advanced by number of bits in codeword + * + * Return: one decoded symbol + **************************************************************************************/ +static int DecodeOneSymbol(BitStreamInfo *bsi, int huffTabIndex) +{ + int nBits, val; + unsigned int bitBuf; + const HuffInfo *hi; + + hi = &(huffTabSBRInfo[huffTabIndex]); + + bitBuf = GetBitsNoAdvance(bsi, hi->maxBits) << (32 - hi->maxBits); + nBits = DecodeHuffmanScalar(huffTabSBR, hi, bitBuf, &val); + AdvanceBitstream(bsi, nBits); + + return val; +} + +/* [1.0, sqrt(2)], format = Q29 (one guard bit for decoupling) */ +static const int envDQTab[2] PROGMEM = {0x20000000, 0x2d413ccc}; + +/************************************************************************************** + * Function: DequantizeEnvelope + * + * Description: dequantize envelope scalefactors + * + * Inputs: number of scalefactors to process + * amplitude resolution flag for this frame (0 or 1) + * quantized envelope scalefactors + * + * Outputs: dequantized envelope scalefactors + * + * Return: extra int bits in output (6 + expMax) + * in other words, output format = Q(FBITS_OUT_DQ_ENV - (6 + expMax)) + * + * Notes: dequantized scalefactors have at least 2 GB + **************************************************************************************/ +static int DequantizeEnvelope(int nBands, int ampRes, signed char *envQuant, int *envDequant) +{ + int exp, expMax, i, scalei; + + if (nBands <= 0) + return 0; + + /* scan for largest dequant value (do separately from envelope decoding to keep code cleaner) */ + expMax = 0; + for (i = 0; i < nBands; i++) { + if (envQuant[i] > expMax) + expMax = envQuant[i]; + } + + /* dequantized envelope gains + * envDequant = 64*2^(envQuant / alpha) = 2^(6 + envQuant / alpha) + * if ampRes == 0, alpha = 2 and range of envQuant = [0, 127] + * if ampRes == 1, alpha = 1 and range of envQuant = [0, 63] + * also if coupling is on, envDequant is scaled by something in range [0, 2] + * so range of envDequant = [2^6, 2^69] (no coupling), [2^6, 2^70] (with coupling) + * + * typical range (from observation) of envQuant/alpha = [0, 27] --> largest envQuant ~= 2^33 + * output: Q(29 - (6 + expMax)) + * + * reference: 14496-3:2001(E)/4.6.18.3.5 and 14496-4:200X/FPDAM8/5.6.5.1.2.1.5 + */ + if (ampRes) { + do { + exp = *envQuant++; + scalei = MIN(expMax - exp, 31); + *envDequant++ = envDQTab[0] >> scalei; + } while (--nBands); + + return (6 + expMax); + } else { + expMax >>= 1; + do { + exp = *envQuant++; + scalei = MIN(expMax - (exp >> 1), 31); + *envDequant++ = envDQTab[exp & 0x01] >> scalei; + } while (--nBands); + + return (6 + expMax); + } + +} + +/************************************************************************************** + * Function: DequantizeNoise + * + * Description: dequantize noise scalefactors + * + * Inputs: number of scalefactors to process + * quantized noise scalefactors + * + * Outputs: dequantized noise scalefactors, format = Q(FBITS_OUT_DQ_NOISE) + * + * Return: none + * + * Notes: dequantized scalefactors have at least 2 GB + **************************************************************************************/ +static void DequantizeNoise(int nBands, signed char *noiseQuant, int *noiseDequant) +{ + int exp, scalei; + + if (nBands <= 0) + return; + + /* dequantize noise floor gains (4.6.18.3.5): + * noiseDequant = 2^(NOISE_FLOOR_OFFSET - noiseQuant) + * + * range of noiseQuant = [0, 30] (see 4.6.18.3.6), NOISE_FLOOR_OFFSET = 6 + * so range of noiseDequant = [2^-24, 2^6] + */ + do { + exp = *noiseQuant++; + scalei = NOISE_FLOOR_OFFSET - exp + FBITS_OUT_DQ_NOISE; /* 6 + 24 - exp, exp = [0,30] */ + + if (scalei < 0) + *noiseDequant++ = 0; + else if (scalei < 30) + *noiseDequant++ = 1 << scalei; + else + *noiseDequant++ = 0x3fffffff; /* leave 2 GB */ + + } while (--nBands); +} + +/************************************************************************************** + * Function: DecodeSBREnvelope + * + * Description: decode delta Huffman coded envelope scalefactors from bitstream + * + * Inputs: BitStreamInfo struct pointing to start of env data + * initialized PSInfoSBR struct + * initialized SBRGrid struct for this channel + * initialized SBRFreq struct for this SCE/CPE block + * initialized SBRChan struct for this channel + * index of current channel (0 for SCE, 0 or 1 for CPE) + * + * Outputs: dequantized env scalefactors for left channel (before decoupling) + * dequantized env scalefactors for right channel (if coupling off) + * or raw decoded env scalefactors for right channel (if coupling on) + * + * Return: none + **************************************************************************************/ +void DecodeSBREnvelope(BitStreamInfo *bsi, PSInfoSBR *psi, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch) +{ + int huffIndexTime, huffIndexFreq, env, envStartBits, band, nBands, sf, lastEnv; + int freqRes, freqResPrev, dShift, i; + + if (psi->couplingFlag && ch) { + dShift = 1; + if (sbrGrid->ampResFrame) { + huffIndexTime = HuffTabSBR_tEnv30b; + huffIndexFreq = HuffTabSBR_fEnv30b; + envStartBits = 5; + } else { + huffIndexTime = HuffTabSBR_tEnv15b; + huffIndexFreq = HuffTabSBR_fEnv15b; + envStartBits = 6; + } + } else { + dShift = 0; + if (sbrGrid->ampResFrame) { + huffIndexTime = HuffTabSBR_tEnv30; + huffIndexFreq = HuffTabSBR_fEnv30; + envStartBits = 6; + } else { + huffIndexTime = HuffTabSBR_tEnv15; + huffIndexFreq = HuffTabSBR_fEnv15; + envStartBits = 7; + } + } + + /* range of envDataQuant[] = [0, 127] (see comments in DequantizeEnvelope() for reference) */ + for (env = 0; env < sbrGrid->numEnv; env++) { + nBands = (sbrGrid->freqRes[env] ? sbrFreq->nHigh : sbrFreq->nLow); + freqRes = (sbrGrid->freqRes[env]); + freqResPrev = (env == 0 ? sbrGrid->freqResPrev : sbrGrid->freqRes[env-1]); + lastEnv = (env == 0 ? sbrGrid->numEnvPrev-1 : env-1); + if (lastEnv < 0) + lastEnv = 0; /* first frame */ + + ASSERT(nBands <= MAX_QMF_BANDS); + + if (sbrChan->deltaFlagEnv[env] == 0) { + /* delta coding in freq */ + sf = GetBits(bsi, envStartBits) << dShift; + sbrChan->envDataQuant[env][0] = sf; + for (band = 1; band < nBands; band++) { + sf = DecodeOneSymbol(bsi, huffIndexFreq) << dShift; + sbrChan->envDataQuant[env][band] = sf + sbrChan->envDataQuant[env][band-1]; + } + } else if (freqRes == freqResPrev) { + /* delta coding in time - same freq resolution for both frames */ + for (band = 0; band < nBands; band++) { + sf = DecodeOneSymbol(bsi, huffIndexTime) << dShift; + sbrChan->envDataQuant[env][band] = sf + sbrChan->envDataQuant[lastEnv][band]; + } + } else if (freqRes == 0 && freqResPrev == 1) { + /* delta coding in time - low freq resolution for new frame, high freq resolution for old frame */ + for (band = 0; band < nBands; band++) { + sf = DecodeOneSymbol(bsi, huffIndexTime) << dShift; + sbrChan->envDataQuant[env][band] = sf; + for (i = 0; i < sbrFreq->nHigh; i++) { + if (sbrFreq->freqHigh[i] == sbrFreq->freqLow[band]) { + sbrChan->envDataQuant[env][band] += sbrChan->envDataQuant[lastEnv][i]; + break; + } + } + } + } else if (freqRes == 1 && freqResPrev == 0) { + /* delta coding in time - high freq resolution for new frame, low freq resolution for old frame */ + for (band = 0; band < nBands; band++) { + sf = DecodeOneSymbol(bsi, huffIndexTime) << dShift; + sbrChan->envDataQuant[env][band] = sf; + for (i = 0; i < sbrFreq->nLow; i++) { + if (sbrFreq->freqLow[i] <= sbrFreq->freqHigh[band] && sbrFreq->freqHigh[band] < sbrFreq->freqLow[i+1] ) { + sbrChan->envDataQuant[env][band] += sbrChan->envDataQuant[lastEnv][i]; + break; + } + } + } + } + + /* skip coupling channel */ + if (ch != 1 || psi->couplingFlag != 1) + psi->envDataDequantScale[ch][env] = DequantizeEnvelope(nBands, sbrGrid->ampResFrame, sbrChan->envDataQuant[env], psi->envDataDequant[ch][env]); + } + sbrGrid->numEnvPrev = sbrGrid->numEnv; + sbrGrid->freqResPrev = sbrGrid->freqRes[sbrGrid->numEnv-1]; +} + +/************************************************************************************** + * Function: DecodeSBRNoise + * + * Description: decode delta Huffman coded noise scalefactors from bitstream + * + * Inputs: BitStreamInfo struct pointing to start of noise data + * initialized PSInfoSBR struct + * initialized SBRGrid struct for this channel + * initialized SBRFreq struct for this SCE/CPE block + * initialized SBRChan struct for this channel + * index of current channel (0 for SCE, 0 or 1 for CPE) + * + * Outputs: dequantized noise scalefactors for left channel (before decoupling) + * dequantized noise scalefactors for right channel (if coupling off) + * or raw decoded noise scalefactors for right channel (if coupling on) + * + * Return: none + **************************************************************************************/ +void DecodeSBRNoise(BitStreamInfo *bsi, PSInfoSBR *psi, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChan, int ch) +{ + int huffIndexTime, huffIndexFreq, noiseFloor, band, dShift, sf, lastNoiseFloor; + + if (psi->couplingFlag && ch) { + dShift = 1; + huffIndexTime = HuffTabSBR_tNoise30b; + huffIndexFreq = HuffTabSBR_fNoise30b; + } else { + dShift = 0; + huffIndexTime = HuffTabSBR_tNoise30; + huffIndexFreq = HuffTabSBR_fNoise30; + } + + for (noiseFloor = 0; noiseFloor < sbrGrid->numNoiseFloors; noiseFloor++) { + lastNoiseFloor = (noiseFloor == 0 ? sbrGrid->numNoiseFloorsPrev-1 : noiseFloor-1); + if (lastNoiseFloor < 0) + lastNoiseFloor = 0; /* first frame */ + + ASSERT(sbrFreq->numNoiseFloorBands <= MAX_QMF_BANDS); + + if (sbrChan->deltaFlagNoise[noiseFloor] == 0) { + /* delta coding in freq */ + sbrChan->noiseDataQuant[noiseFloor][0] = GetBits(bsi, 5) << dShift; + for (band = 1; band < sbrFreq->numNoiseFloorBands; band++) { + sf = DecodeOneSymbol(bsi, huffIndexFreq) << dShift; + sbrChan->noiseDataQuant[noiseFloor][band] = sf + sbrChan->noiseDataQuant[noiseFloor][band-1]; + } + } else { + /* delta coding in time */ + for (band = 0; band < sbrFreq->numNoiseFloorBands; band++) { + sf = DecodeOneSymbol(bsi, huffIndexTime) << dShift; + sbrChan->noiseDataQuant[noiseFloor][band] = sf + sbrChan->noiseDataQuant[lastNoiseFloor][band]; + } + } + + /* skip coupling channel */ + if (ch != 1 || psi->couplingFlag != 1) + DequantizeNoise(sbrFreq->numNoiseFloorBands, sbrChan->noiseDataQuant[noiseFloor], psi->noiseDataDequant[ch][noiseFloor]); + } + sbrGrid->numNoiseFloorsPrev = sbrGrid->numNoiseFloors; +} + +/* dqTabCouple[i] = 2 / (1 + 2^(12 - i)), format = Q30 */ +static const int dqTabCouple[25] PROGMEM = { + 0x0007ff80, 0x000ffe00, 0x001ff802, 0x003fe010, 0x007f8080, 0x00fe03f8, 0x01f81f82, 0x03e0f83e, + 0x07878788, 0x0e38e38e, 0x1999999a, 0x2aaaaaab, 0x40000000, 0x55555555, 0x66666666, 0x71c71c72, + 0x78787878, 0x7c1f07c2, 0x7e07e07e, 0x7f01fc08, 0x7f807f80, 0x7fc01ff0, 0x7fe007fe, 0x7ff00200, + 0x7ff80080, +}; + +/************************************************************************************** + * Function: UncoupleSBREnvelope + * + * Description: scale dequantized envelope scalefactors according to channel + * coupling rules + * + * Inputs: initialized PSInfoSBR struct including + * dequantized envelope data for left channel + * initialized SBRGrid struct for this channel + * initialized SBRFreq struct for this SCE/CPE block + * initialized SBRChan struct for right channel including + * quantized envelope scalefactors + * + * Outputs: dequantized envelope data for left channel (after decoupling) + * dequantized envelope data for right channel (after decoupling) + * + * Return: none + **************************************************************************************/ +void UncoupleSBREnvelope(PSInfoSBR *psi, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChanR) +{ + int env, band, nBands, scalei, E_1; + + scalei = (sbrGrid->ampResFrame ? 0 : 1); + for (env = 0; env < sbrGrid->numEnv; env++) { + nBands = (sbrGrid->freqRes[env] ? sbrFreq->nHigh : sbrFreq->nLow); + psi->envDataDequantScale[1][env] = psi->envDataDequantScale[0][env]; /* same scalefactor for L and R */ + for (band = 0; band < nBands; band++) { + /* clip E_1 to [0, 24] (scalefactors approach 0 or 2) */ + E_1 = sbrChanR->envDataQuant[env][band] >> scalei; + if (E_1 < 0) E_1 = 0; + if (E_1 > 24) E_1 = 24; + + /* envDataDequant[0] has 1 GB, so << by 2 is okay */ + psi->envDataDequant[1][env][band] = MULSHIFT32(psi->envDataDequant[0][env][band], dqTabCouple[24 - E_1]) << 2; + psi->envDataDequant[0][env][band] = MULSHIFT32(psi->envDataDequant[0][env][band], dqTabCouple[E_1]) << 2; + } + } +} + +/************************************************************************************** + * Function: UncoupleSBRNoise + * + * Description: scale dequantized noise floor scalefactors according to channel + * coupling rules + * + * Inputs: initialized PSInfoSBR struct including + * dequantized noise data for left channel + * initialized SBRGrid struct for this channel + * initialized SBRFreq struct for this SCE/CPE block + * initialized SBRChan struct for this channel including + * quantized noise scalefactors + * + * Outputs: dequantized noise data for left channel (after decoupling) + * dequantized noise data for right channel (after decoupling) + * + * Return: none + **************************************************************************************/ +void UncoupleSBRNoise(PSInfoSBR *psi, SBRGrid *sbrGrid, SBRFreq *sbrFreq, SBRChan *sbrChanR) +{ + int noiseFloor, band, Q_1; + + for (noiseFloor = 0; noiseFloor < sbrGrid->numNoiseFloors; noiseFloor++) { + for (band = 0; band < sbrFreq->numNoiseFloorBands; band++) { + /* Q_1 should be in range [0, 24] according to 4.6.18.3.6, but check to make sure */ + Q_1 = sbrChanR->noiseDataQuant[noiseFloor][band]; + if (Q_1 < 0) Q_1 = 0; + if (Q_1 > 24) Q_1 = 24; + + /* noiseDataDequant[0] has 1 GB, so << by 2 is okay */ + psi->noiseDataDequant[1][noiseFloor][band] = MULSHIFT32(psi->noiseDataDequant[0][noiseFloor][band], dqTabCouple[24 - Q_1]) << 2; + psi->noiseDataDequant[0][noiseFloor][band] = MULSHIFT32(psi->noiseDataDequant[0][noiseFloor][band], dqTabCouple[Q_1]) << 2; + } + } +} diff --git a/components/spotify/cspot/bell/libhelix-aac/sbrimdct.c b/components/spotify/cspot/bell/libhelix-aac/sbrimdct.c new file mode 100644 index 00000000..365ff02a --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/sbrimdct.c @@ -0,0 +1,447 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: sbrimdct.c,v 1.1 2005/02/26 01:47:35 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * sbrimdct.c - inverse MDCT without clipping or interleaving, for input to SBR + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +/************************************************************************************** + * Function: DecWindowOverlapNoClip + * + * Description: apply synthesis window, do overlap-add without clipping, + * for winSequence LONG-LONG + * + * Inputs: input buffer (output of type-IV DCT) + * overlap buffer (saved from last time) + * window type (sin or KBD) for input buffer + * window type (sin or KBD) for overlap buffer + * + * Outputs: one channel, one frame of 32-bit PCM, non-interleaved + * + * Return: none + * + * Notes: use this function when the decoded PCM is going to the SBR decoder + **************************************************************************************/ +void DecWindowOverlapNoClip(int *buf0, int *over0, int *out0, int winTypeCurr, int winTypePrev) +{ + int in, w0, w1, f0, f1; + int *buf1, *over1, *out1; + const int *wndPrev, *wndCurr; + + buf0 += (1024 >> 1); + buf1 = buf0 - 1; + out1 = out0 + 1024 - 1; + over1 = over0 + 1024 - 1; + + wndPrev = (winTypePrev == 1 ? kbdWindow + kbdWindowOffset[1] : sinWindow + sinWindowOffset[1]); + if (winTypeCurr == winTypePrev) { + /* cut window loads in half since current and overlap sections use same symmetric window */ + do { + w0 = *wndPrev++; + w1 = *wndPrev++; + in = *buf0++; + + f0 = MULSHIFT32(w0, in); + f1 = MULSHIFT32(w1, in); + + in = *over0; + *out0++ = in - f0; + + in = *over1; + *out1-- = in + f1; + + in = *buf1--; + *over1-- = MULSHIFT32(w0, in); + *over0++ = MULSHIFT32(w1, in); + } while (over0 < over1); + } else { + /* different windows for current and overlap parts - should still fit in registers on ARM w/o stack spill */ + wndCurr = (winTypeCurr == 1 ? kbdWindow + kbdWindowOffset[1] : sinWindow + sinWindowOffset[1]); + do { + w0 = *wndPrev++; + w1 = *wndPrev++; + in = *buf0++; + + f0 = MULSHIFT32(w0, in); + f1 = MULSHIFT32(w1, in); + + in = *over0; + *out0++ = in - f0; + + in = *over1; + *out1-- = in + f1; + + w0 = *wndCurr++; + w1 = *wndCurr++; + in = *buf1--; + + *over1-- = MULSHIFT32(w0, in); + *over0++ = MULSHIFT32(w1, in); + } while (over0 < over1); + } +} + +/************************************************************************************** + * Function: DecWindowOverlapLongStart + * + * Description: apply synthesis window, do overlap-add, without clipping + * for winSequence LONG-START + * + * Inputs: input buffer (output of type-IV DCT) + * overlap buffer (saved from last time) + * window type (sin or KBD) for input buffer + * window type (sin or KBD) for overlap buffer + * + * Outputs: one channel, one frame of 32-bit PCM, non-interleaved + * + * Return: none + * + * Notes: use this function when the decoded PCM is going to the SBR decoder + **************************************************************************************/ +void DecWindowOverlapLongStartNoClip(int *buf0, int *over0, int *out0, int winTypeCurr, int winTypePrev) +{ + int i, in, w0, w1, f0, f1; + int *buf1, *over1, *out1; + const int *wndPrev, *wndCurr; + + buf0 += (1024 >> 1); + buf1 = buf0 - 1; + out1 = out0 + 1024 - 1; + over1 = over0 + 1024 - 1; + + wndPrev = (winTypePrev == 1 ? kbdWindow + kbdWindowOffset[1] : sinWindow + sinWindowOffset[1]); + i = 448; /* 2 outputs, 2 overlaps per loop */ + do { + w0 = *wndPrev++; + w1 = *wndPrev++; + in = *buf0++; + + f0 = MULSHIFT32(w0, in); + f1 = MULSHIFT32(w1, in); + + in = *over0; + *out0++ = in - f0; + + in = *over1; + *out1-- = in + f1; + + in = *buf1--; + + *over1-- = 0; /* Wn = 0 for n = (2047, 2046, ... 1600) */ + *over0++ = in >> 1; /* Wn = 1 for n = (1024, 1025, ... 1471) */ + } while (--i); + + wndCurr = (winTypeCurr == 1 ? kbdWindow + kbdWindowOffset[0] : sinWindow + sinWindowOffset[0]); + + /* do 64 more loops - 2 outputs, 2 overlaps per loop */ + do { + w0 = *wndPrev++; + w1 = *wndPrev++; + in = *buf0++; + + f0 = MULSHIFT32(w0, in); + f1 = MULSHIFT32(w1, in); + + in = *over0; + *out0++ = in - f0; + + in = *over1; + *out1-- = in + f1; + + w0 = *wndCurr++; /* W[0], W[1], ... --> W[255], W[254], ... */ + w1 = *wndCurr++; /* W[127], W[126], ... --> W[128], W[129], ... */ + in = *buf1--; + + *over1-- = MULSHIFT32(w0, in); /* Wn = short window for n = (1599, 1598, ... , 1536) */ + *over0++ = MULSHIFT32(w1, in); /* Wn = short window for n = (1472, 1473, ... , 1535) */ + } while (over0 < over1); +} + +/************************************************************************************** + * Function: DecWindowOverlapLongStop + * + * Description: apply synthesis window, do overlap-add, without clipping + * for winSequence LONG-STOP + * + * Inputs: input buffer (output of type-IV DCT) + * overlap buffer (saved from last time) + * window type (sin or KBD) for input buffer + * window type (sin or KBD) for overlap buffer + * + * Outputs: one channel, one frame of 32-bit PCM, non-interleaved + * + * Return: none + * + * Notes: use this function when the decoded PCM is going to the SBR decoder + **************************************************************************************/ +void DecWindowOverlapLongStopNoClip(int *buf0, int *over0, int *out0, int winTypeCurr, int winTypePrev) +{ + int i, in, w0, w1, f0, f1; + int *buf1, *over1, *out1; + const int *wndPrev, *wndCurr; + + buf0 += (1024 >> 1); + buf1 = buf0 - 1; + out1 = out0 + 1024 - 1; + over1 = over0 + 1024 - 1; + + wndPrev = (winTypePrev == 1 ? kbdWindow + kbdWindowOffset[0] : sinWindow + sinWindowOffset[0]); + wndCurr = (winTypeCurr == 1 ? kbdWindow + kbdWindowOffset[1] : sinWindow + sinWindowOffset[1]); + + i = 448; /* 2 outputs, 2 overlaps per loop */ + do { + /* Wn = 0 for n = (0, 1, ... 447) */ + /* Wn = 1 for n = (576, 577, ... 1023) */ + in = *buf0++; + f1 = in >> 1; /* scale since skipping multiply by Q31 */ + + in = *over0; + *out0++ = in; + + in = *over1; + *out1-- = in + f1; + + w0 = *wndCurr++; + w1 = *wndCurr++; + in = *buf1--; + + *over1-- = MULSHIFT32(w0, in); + *over0++ = MULSHIFT32(w1, in); + } while (--i); + + /* do 64 more loops - 2 outputs, 2 overlaps per loop */ + do { + w0 = *wndPrev++; /* W[0], W[1], ...W[63] */ + w1 = *wndPrev++; /* W[127], W[126], ... W[64] */ + in = *buf0++; + + f0 = MULSHIFT32(w0, in); + f1 = MULSHIFT32(w1, in); + + in = *over0; + *out0++ = in - f0; + + in = *over1; + *out1-- = in + f1; + + w0 = *wndCurr++; + w1 = *wndCurr++; + in = *buf1--; + + *over1-- = MULSHIFT32(w0, in); + *over0++ = MULSHIFT32(w1, in); + } while (over0 < over1); +} + +/************************************************************************************** + * Function: DecWindowOverlapShort + * + * Description: apply synthesis window, do overlap-add, without clipping + * for winSequence EIGHT-SHORT (does all 8 short blocks) + * + * Inputs: input buffer (output of type-IV DCT) + * overlap buffer (saved from last time) + * window type (sin or KBD) for input buffer + * window type (sin or KBD) for overlap buffer + * + * Outputs: one channel, one frame of 32-bit PCM, non-interleaved + * + * Return: none + * + * Notes: use this function when the decoded PCM is going to the SBR decoder + **************************************************************************************/ +void DecWindowOverlapShortNoClip(int *buf0, int *over0, int *out0, int winTypeCurr, int winTypePrev) +{ + int i, in, w0, w1, f0, f1; + int *buf1, *over1, *out1; + const int *wndPrev, *wndCurr; + + wndPrev = (winTypePrev == 1 ? kbdWindow + kbdWindowOffset[0] : sinWindow + sinWindowOffset[0]); + wndCurr = (winTypeCurr == 1 ? kbdWindow + kbdWindowOffset[0] : sinWindow + sinWindowOffset[0]); + + /* pcm[0-447] = 0 + overlap[0-447] */ + i = 448; + do { + f0 = *over0++; + f1 = *over0++; + *out0++ = f0; + *out0++ = f1; + i -= 2; + } while (i); + + /* pcm[448-575] = Wp[0-127] * block0[0-127] + overlap[448-575] */ + out1 = out0 + (128 - 1); + over1 = over0 + 128 - 1; + buf0 += 64; + buf1 = buf0 - 1; + do { + w0 = *wndPrev++; /* W[0], W[1], ...W[63] */ + w1 = *wndPrev++; /* W[127], W[126], ... W[64] */ + in = *buf0++; + + f0 = MULSHIFT32(w0, in); + f1 = MULSHIFT32(w1, in); + + in = *over0; + *out0++ = in - f0; + + in = *over1; + *out1-- = in + f1; + + w0 = *wndCurr++; + w1 = *wndCurr++; + in = *buf1--; + + /* save over0/over1 for next short block, in the slots just vacated */ + *over1-- = MULSHIFT32(w0, in); + *over0++ = MULSHIFT32(w1, in); + } while (over0 < over1); + + /* pcm[576-703] = Wc[128-255] * block0[128-255] + Wc[0-127] * block1[0-127] + overlap[576-703] + * pcm[704-831] = Wc[128-255] * block1[128-255] + Wc[0-127] * block2[0-127] + overlap[704-831] + * pcm[832-959] = Wc[128-255] * block2[128-255] + Wc[0-127] * block3[0-127] + overlap[832-959] + */ + for (i = 0; i < 3; i++) { + out0 += 64; + out1 = out0 + 128 - 1; + over0 += 64; + over1 = over0 + 128 - 1; + buf0 += 64; + buf1 = buf0 - 1; + wndCurr -= 128; + + do { + w0 = *wndCurr++; /* W[0], W[1], ...W[63] */ + w1 = *wndCurr++; /* W[127], W[126], ... W[64] */ + in = *buf0++; + + f0 = MULSHIFT32(w0, in); + f1 = MULSHIFT32(w1, in); + + in = *(over0 - 128); /* from last short block */ + in += *(over0 + 0); /* from last full frame */ + *out0++ = in - f0; + + in = *(over1 - 128); /* from last short block */ + in += *(over1 + 0); /* from last full frame */ + *out1-- = in + f1; + + /* save over0/over1 for next short block, in the slots just vacated */ + in = *buf1--; + *over1-- = MULSHIFT32(w0, in); + *over0++ = MULSHIFT32(w1, in); + } while (over0 < over1); + } + + /* pcm[960-1023] = Wc[128-191] * block3[128-191] + Wc[0-63] * block4[0-63] + overlap[960-1023] + * over[0-63] = Wc[192-255] * block3[192-255] + Wc[64-127] * block4[64-127] + */ + out0 += 64; + over0 -= 832; /* points at overlap[64] */ + over1 = over0 + 128 - 1; /* points at overlap[191] */ + buf0 += 64; + buf1 = buf0 - 1; + wndCurr -= 128; + do { + w0 = *wndCurr++; /* W[0], W[1], ...W[63] */ + w1 = *wndCurr++; /* W[127], W[126], ... W[64] */ + in = *buf0++; + + f0 = MULSHIFT32(w0, in); + f1 = MULSHIFT32(w1, in); + + in = *(over0 + 768); /* from last short block */ + in += *(over0 + 896); /* from last full frame */ + *out0++ = in - f0; + + in = *(over1 + 768); /* from last short block */ + *(over1 - 128) = in + f1; + + in = *buf1--; + *over1-- = MULSHIFT32(w0, in); /* save in overlap[128-191] */ + *over0++ = MULSHIFT32(w1, in); /* save in overlap[64-127] */ + } while (over0 < over1); + + /* over0 now points at overlap[128] */ + + /* over[64-191] = Wc[128-255] * block4[128-255] + Wc[0-127] * block5[0-127] + * over[192-319] = Wc[128-255] * block5[128-255] + Wc[0-127] * block6[0-127] + * over[320-447] = Wc[128-255] * block6[128-255] + Wc[0-127] * block7[0-127] + * over[448-576] = Wc[128-255] * block7[128-255] + */ + for (i = 0; i < 3; i++) { + over0 += 64; + over1 = over0 + 128 - 1; + buf0 += 64; + buf1 = buf0 - 1; + wndCurr -= 128; + do { + w0 = *wndCurr++; /* W[0], W[1], ...W[63] */ + w1 = *wndCurr++; /* W[127], W[126], ... W[64] */ + in = *buf0++; + + f0 = MULSHIFT32(w0, in); + f1 = MULSHIFT32(w1, in); + + /* from last short block */ + *(over0 - 128) -= f0; + *(over1 - 128)+= f1; + + in = *buf1--; + *over1-- = MULSHIFT32(w0, in); + *over0++ = MULSHIFT32(w1, in); + } while (over0 < over1); + } + + /* over[576-1024] = 0 */ + i = 448; + over0 += 64; + do { + *over0++ = 0; + *over0++ = 0; + *over0++ = 0; + *over0++ = 0; + i -= 4; + } while (i); +} diff --git a/components/spotify/cspot/bell/libhelix-aac/sbrmath.c b/components/spotify/cspot/bell/libhelix-aac/sbrmath.c new file mode 100644 index 00000000..930889d8 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/sbrmath.c @@ -0,0 +1,195 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: sbrmath.c,v 1.1 2005/02/26 01:47:35 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * sbrmath.c - fixed-point math functions for SBR + **************************************************************************************/ + +#include "sbr.h" +#include "assembly.h" + +#define Q28_2 0x20000000 /* Q28: 2.0 */ +#define Q28_15 0x30000000 /* Q28: 1.5 */ + +#define NUM_ITER_IRN 5 + +/************************************************************************************** + * Function: InvRNormalized + * + * Description: use Newton's method to solve for x = 1/r + * + * Inputs: r = Q31, range = [0.5, 1) (normalize your inputs to this range) + * + * Outputs: none + * + * Return: x = Q29, range ~= [1.0, 2.0] + * + * Notes: guaranteed to converge and not overflow for any r in [0.5, 1) + * + * xn+1 = xn - f(xn)/f'(xn) + * f(x) = 1/r - x = 0 (find root) + * = 1/x - r + * f'(x) = -1/x^2 + * + * so xn+1 = xn - (1/xn - r) / (-1/xn^2) + * = xn * (2 - r*xn) + * + * NUM_ITER_IRN = 2, maxDiff = 6.2500e-02 (precision of about 4 bits) + * NUM_ITER_IRN = 3, maxDiff = 3.9063e-03 (precision of about 8 bits) + * NUM_ITER_IRN = 4, maxDiff = 1.5288e-05 (precision of about 16 bits) + * NUM_ITER_IRN = 5, maxDiff = 3.0034e-08 (precision of about 24 bits) + **************************************************************************************/ +int InvRNormalized(int r) +{ + int i, xn, t; + + /* r = [0.5, 1.0) + * 1/r = (1.0, 2.0] + * so use 1.5 as initial guess + */ + xn = Q28_15; + + /* xn = xn*(2.0 - r*xn) */ + for (i = NUM_ITER_IRN; i != 0; i--) { + t = MULSHIFT32(r, xn); /* Q31*Q29 = Q28 */ + t = Q28_2 - t; /* Q28 */ + xn = MULSHIFT32(xn, t) << 4; /* Q29*Q28 << 4 = Q29 */ + } + + return xn; +} + +#define NUM_TERMS_RPI 5 +#define LOG2_EXP_INV 0x58b90bfc /* 1/log2(e), Q31 */ + +/* invTab[x] = 1/(x+1), format = Q30 */ +static const int invTab[NUM_TERMS_RPI] PROGMEM = {0x40000000, 0x20000000, 0x15555555, 0x10000000, 0x0ccccccd}; + +/************************************************************************************** + * Function: RatioPowInv + * + * Description: use Taylor (MacLaurin) series expansion to calculate (a/b) ^ (1/c) + * + * Inputs: a = [1, 64], b = [1, 64], c = [1, 64], a >= b + * + * Outputs: none + * + * Return: y = Q24, range ~= [0.015625, 64] + **************************************************************************************/ +int RatioPowInv(int a, int b, int c) +{ + int lna, lnb, i, p, t, y; + + if (a < 1 || b < 1 || c < 1 || a > 64 || b > 64 || c > 64 || a < b) + return 0; + + lna = MULSHIFT32(log2Tab[a], LOG2_EXP_INV) << 1; /* ln(a), Q28 */ + lnb = MULSHIFT32(log2Tab[b], LOG2_EXP_INV) << 1; /* ln(b), Q28 */ + p = (lna - lnb) / c; /* Q28 */ + + /* sum in Q24 */ + y = (1 << 24); + t = p >> 4; /* t = p^1 * 1/1! (Q24)*/ + y += t; + + for (i = 2; i <= NUM_TERMS_RPI; i++) { + t = MULSHIFT32(invTab[i-1], t) << 2; + t = MULSHIFT32(p, t) << 4; /* t = p^i * 1/i! (Q24) */ + y += t; + } + + return y; +} + +/************************************************************************************** + * Function: SqrtFix + * + * Description: use binary search to calculate sqrt(q) + * + * Inputs: q = Q30 + * number of fraction bits in input + * + * Outputs: number of fraction bits in output + * + * Return: lo = Q(fBitsOut) + * + * Notes: absolute precision varies depending on fBitsIn + * normalizes input to range [0x200000000, 0x7fffffff] and takes + * floor(sqrt(input)), and sets fBitsOut appropriately + **************************************************************************************/ +int SqrtFix(int q, int fBitsIn, int *fBitsOut) +{ + int z, lo, hi, mid; + + if (q <= 0) { + *fBitsOut = fBitsIn; + return 0; + } + + /* force even fBitsIn */ + z = fBitsIn & 0x01; + q >>= z; + fBitsIn -= z; + + /* for max precision, normalize to [0x20000000, 0x7fffffff] */ + z = (CLZ(q) - 1); + z >>= 1; + q <<= (2*z); + + /* choose initial bounds */ + lo = 1; + if (q >= 0x10000000) + lo = 16384; /* (int)sqrt(0x10000000) */ + hi = 46340; /* (int)sqrt(0x7fffffff) */ + + /* do binary search with 32x32->32 multiply test */ + do { + mid = (lo + hi) >> 1; + if (mid*mid > q) + hi = mid - 1; + else + lo = mid + 1; + } while (hi >= lo); + lo--; + + *fBitsOut = ((fBitsIn + 2*z) >> 1); + return lo; +} diff --git a/components/spotify/cspot/bell/libhelix-aac/sbrqmf.c b/components/spotify/cspot/bell/libhelix-aac/sbrqmf.c new file mode 100644 index 00000000..83cf14a4 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/sbrqmf.c @@ -0,0 +1,527 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: sbrqmf.c,v 1.2 2005/05/19 20:45:20 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * sbrqmf.c - analysis and synthesis QMF filters for SBR + **************************************************************************************/ + +#include "sbr.h" +#include "assembly.h" + +/* PreMultiply64() table + * format = Q30 + * reordered for sequential access + * + * for (i = 0; i < 64/4; i++) { + * angle = (i + 0.25) * M_PI / nmdct; + * x = (cos(angle) + sin(angle)); + * x = sin(angle); + * + * angle = (nmdct/2 - 1 - i + 0.25) * M_PI / nmdct; + * x = (cos(angle) + sin(angle)); + * x = sin(angle); + * } + */ +static const int cos4sin4tab64[64] PROGMEM = { + 0x40c7d2bd, 0x00c90e90, 0x424ff28f, 0x3ff4e5e0, 0x43cdd89a, 0x03ecadcf, 0x454149fc, 0x3fc395f9, + 0x46aa0d6d, 0x070de172, 0x4807eb4b, 0x3f6af2e3, 0x495aada2, 0x0a2abb59, 0x4aa22036, 0x3eeb3347, + 0x4bde1089, 0x0d415013, 0x4d0e4de2, 0x3e44a5ef, 0x4e32a956, 0x104fb80e, 0x4f4af5d1, 0x3d77b192, + 0x50570819, 0x135410c3, 0x5156b6d9, 0x3c84d496, 0x5249daa2, 0x164c7ddd, 0x53304df6, 0x3b6ca4c4, + 0x5409ed4b, 0x19372a64, 0x54d69714, 0x3a2fcee8, 0x55962bc0, 0x1c1249d8, 0x56488dc5, 0x38cf1669, + 0x56eda1a0, 0x1edc1953, 0x57854ddd, 0x374b54ce, 0x580f7b19, 0x2192e09b, 0x588c1404, 0x35a5793c, + 0x58fb0568, 0x2434f332, 0x595c3e2a, 0x33de87de, 0x59afaf4c, 0x26c0b162, 0x59f54bee, 0x31f79948, + 0x5a2d0957, 0x29348937, 0x5a56deec, 0x2ff1d9c7, 0x5a72c63b, 0x2b8ef77d, 0x5a80baf6, 0x2dce88aa, +}; + +/* PostMultiply64() table + * format = Q30 + * reordered for sequential access + * + * for (i = 0; i <= (32/2); i++) { + * angle = i * M_PI / 64; + * x = (cos(angle) + sin(angle)); + * x = sin(angle); + * } + */ +static const int cos1sin1tab64[34] PROGMEM = { + 0x40000000, 0x00000000, 0x43103085, 0x0323ecbe, 0x45f704f7, 0x0645e9af, 0x48b2b335, 0x09640837, + 0x4b418bbe, 0x0c7c5c1e, 0x4da1fab5, 0x0f8cfcbe, 0x4fd288dc, 0x1294062f, 0x51d1dc80, 0x158f9a76, + 0x539eba45, 0x187de2a7, 0x553805f2, 0x1b5d100a, 0x569cc31b, 0x1e2b5d38, 0x57cc15bc, 0x20e70f32, + 0x58c542c5, 0x238e7673, 0x5987b08a, 0x261feffa, 0x5a12e720, 0x2899e64a, 0x5a6690ae, 0x2afad269, + 0x5a82799a, 0x2d413ccd, +}; + +/************************************************************************************** + * Function: PreMultiply64 + * + * Description: pre-twiddle stage of 64-point DCT-IV + * + * Inputs: buffer of 64 samples + * + * Outputs: processed samples in same buffer + * + * Return: none + * + * Notes: minimum 1 GB in, 2 GB out, gains 2 int bits + * gbOut = gbIn + 1 + * output is limited to sqrt(2)/2 plus GB in full GB + * uses 3-mul, 3-add butterflies instead of 4-mul, 2-add + **************************************************************************************/ +static void PreMultiply64(int *zbuf1) +{ + int i, ar1, ai1, ar2, ai2, z1, z2; + int t, cms2, cps2a, sin2a, cps2b, sin2b; + int *zbuf2; + const int *csptr; + + zbuf2 = zbuf1 + 64 - 1; + csptr = cos4sin4tab64; + + /* whole thing should fit in registers - verify that compiler does this */ + for (i = 64 >> 2; i != 0; i--) { + /* cps2 = (cos+sin), sin2 = sin, cms2 = (cos-sin) */ + cps2a = *csptr++; + sin2a = *csptr++; + cps2b = *csptr++; + sin2b = *csptr++; + + ar1 = *(zbuf1 + 0); + ai2 = *(zbuf1 + 1); + ai1 = *(zbuf2 + 0); + ar2 = *(zbuf2 - 1); + + /* gain 2 ints bit from MULSHIFT32 by Q30 + * max per-sample gain (ignoring implicit scaling) = MAX(sin(angle)+cos(angle)) = 1.414 + * i.e. gain 1 GB since worst case is sin(angle) = cos(angle) = 0.707 (Q30), gain 2 from + * extra sign bits, and eat one in adding + */ + t = MULSHIFT32(sin2a, ar1 + ai1); + z2 = MULSHIFT32(cps2a, ai1) - t; + cms2 = cps2a - 2*sin2a; + z1 = MULSHIFT32(cms2, ar1) + t; + *zbuf1++ = z1; /* cos*ar1 + sin*ai1 */ + *zbuf1++ = z2; /* cos*ai1 - sin*ar1 */ + + t = MULSHIFT32(sin2b, ar2 + ai2); + z2 = MULSHIFT32(cps2b, ai2) - t; + cms2 = cps2b - 2*sin2b; + z1 = MULSHIFT32(cms2, ar2) + t; + *zbuf2-- = z2; /* cos*ai2 - sin*ar2 */ + *zbuf2-- = z1; /* cos*ar2 + sin*ai2 */ + } +} + +/************************************************************************************** + * Function: PostMultiply64 + * + * Description: post-twiddle stage of 64-point type-IV DCT + * + * Inputs: buffer of 64 samples + * number of output samples to calculate + * + * Outputs: processed samples in same buffer + * + * Return: none + * + * Notes: minimum 1 GB in, 2 GB out, gains 2 int bits + * gbOut = gbIn + 1 + * output is limited to sqrt(2)/2 plus GB in full GB + * nSampsOut is rounded up to next multiple of 4, since we calculate + * 4 samples per loop + **************************************************************************************/ +static void PostMultiply64(int *fft1, int nSampsOut) +{ + int i, ar1, ai1, ar2, ai2; + int t, cms2, cps2, sin2; + int *fft2; + const int *csptr; + + csptr = cos1sin1tab64; + fft2 = fft1 + 64 - 1; + + /* load coeffs for first pass + * cps2 = (cos+sin)/2, sin2 = sin/2, cms2 = (cos-sin)/2 + */ + cps2 = *csptr++; + sin2 = *csptr++; + cms2 = cps2 - 2*sin2; + + for (i = (nSampsOut + 3) >> 2; i != 0; i--) { + ar1 = *(fft1 + 0); + ai1 = *(fft1 + 1); + ar2 = *(fft2 - 1); + ai2 = *(fft2 + 0); + + /* gain 2 int bits (multiplying by Q30), max gain = sqrt(2) */ + t = MULSHIFT32(sin2, ar1 + ai1); + *fft2-- = t - MULSHIFT32(cps2, ai1); + *fft1++ = t + MULSHIFT32(cms2, ar1); + + cps2 = *csptr++; + sin2 = *csptr++; + + ai2 = -ai2; + t = MULSHIFT32(sin2, ar2 + ai2); + *fft2-- = t - MULSHIFT32(cps2, ai2); + cms2 = cps2 - 2*sin2; + *fft1++ = t + MULSHIFT32(cms2, ar2); + } +} + +/************************************************************************************** + * Function: QMFAnalysisConv + * + * Description: convolution kernel for analysis QMF + * + * Inputs: pointer to coefficient table, reordered for sequential access + * delay buffer of size 32*10 = 320 real-valued PCM samples + * index for delay ring buffer (range = [0, 9]) + * + * Outputs: 64 consecutive 32-bit samples + * + * Return: none + * + * Notes: this is carefully written to be efficient on ARM + * use the assembly code version in sbrqmfak.s when building for ARM! + **************************************************************************************/ +#if (defined (__arm) && defined (__ARMCC_VERSION)) || (defined (_WIN32) && defined (_WIN32_WCE) && defined (ARM)) || (defined(__GNUC__) && defined(__arm__)) +#ifdef __cplusplus +extern "C" +#endif +void QMFAnalysisConv(int *cTab, int *delay, int dIdx, int *uBuf); +#else +void QMFAnalysisConv(int *cTab, int *delay, int dIdx, int *uBuf) +{ + int k, dOff; + int *cPtr0, *cPtr1; + U64 u64lo, u64hi; + + dOff = dIdx*32 + 31; + cPtr0 = cTab; + cPtr1 = cTab + 33*5 - 1; + + /* special first pass since we need to flip sign to create cTab[384], cTab[512] */ + u64lo.w64 = 0; + u64hi.w64 = 0; + u64lo.w64 = MADD64(u64lo.w64, *cPtr0++, delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + u64hi.w64 = MADD64(u64hi.w64, *cPtr0++, delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + u64lo.w64 = MADD64(u64lo.w64, *cPtr0++, delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + u64hi.w64 = MADD64(u64hi.w64, *cPtr0++, delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + u64lo.w64 = MADD64(u64lo.w64, *cPtr0++, delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + u64hi.w64 = MADD64(u64hi.w64, *cPtr1--, delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + u64lo.w64 = MADD64(u64lo.w64, -(*cPtr1--), delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + u64hi.w64 = MADD64(u64hi.w64, *cPtr1--, delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + u64lo.w64 = MADD64(u64lo.w64, -(*cPtr1--), delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + u64hi.w64 = MADD64(u64hi.w64, *cPtr1--, delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + + uBuf[0] = u64lo.r.hi32; + uBuf[32] = u64hi.r.hi32; + uBuf++; + dOff--; + + /* max gain for any sample in uBuf, after scaling by cTab, ~= 0.99 + * so we can just sum the uBuf values with no overflow problems + */ + for (k = 1; k <= 31; k++) { + u64lo.w64 = 0; + u64hi.w64 = 0; + u64lo.w64 = MADD64(u64lo.w64, *cPtr0++, delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + u64hi.w64 = MADD64(u64hi.w64, *cPtr0++, delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + u64lo.w64 = MADD64(u64lo.w64, *cPtr0++, delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + u64hi.w64 = MADD64(u64hi.w64, *cPtr0++, delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + u64lo.w64 = MADD64(u64lo.w64, *cPtr0++, delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + u64hi.w64 = MADD64(u64hi.w64, *cPtr1--, delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + u64lo.w64 = MADD64(u64lo.w64, *cPtr1--, delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + u64hi.w64 = MADD64(u64hi.w64, *cPtr1--, delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + u64lo.w64 = MADD64(u64lo.w64, *cPtr1--, delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + u64hi.w64 = MADD64(u64hi.w64, *cPtr1--, delay[dOff]); dOff -= 32; if (dOff < 0) {dOff += 320;} + + uBuf[0] = u64lo.r.hi32; + uBuf[32] = u64hi.r.hi32; + uBuf++; + dOff--; + } +} +#endif + +/************************************************************************************** + * Function: QMFAnalysis + * + * Description: 32-subband analysis QMF (4.6.18.4.1) + * + * Inputs: 32 consecutive samples of decoded 32-bit PCM, format = Q(fBitsIn) + * delay buffer of size 32*10 = 320 PCM samples + * number of fraction bits in input PCM + * index for delay ring buffer (range = [0, 9]) + * number of subbands to calculate (range = [0, 32]) + * + * Outputs: qmfaBands complex subband samples, format = Q(FBITS_OUT_QMFA) + * updated delay buffer + * updated delay index + * + * Return: guard bit mask + * + * Notes: output stored as RE{X0}, IM{X0}, RE{X1}, IM{X1}, ... RE{X31}, IM{X31} + * output stored in int buffer of size 64*2 = 128 + * (zero-filled from XBuf[2*qmfaBands] to XBuf[127]) + **************************************************************************************/ +int QMFAnalysis(int *inbuf, int *delay, int *XBuf, int fBitsIn, int *delayIdx, int qmfaBands) +{ + int n, y, shift, gbMask; + int *delayPtr, *uBuf, *tBuf; + + /* use XBuf[128] as temp buffer for reordering */ + uBuf = XBuf; /* first 64 samples */ + tBuf = XBuf + 64; /* second 64 samples */ + + /* overwrite oldest PCM with new PCM + * delay[n] has 1 GB after shifting (either << or >>) + */ + delayPtr = delay + (*delayIdx * 32); + if (fBitsIn > FBITS_IN_QMFA) { + shift = MIN(fBitsIn - FBITS_IN_QMFA, 31); + for (n = 32; n != 0; n--) { + y = (*inbuf) >> shift; + inbuf++; + *delayPtr++ = y; + } + } else { + shift = MIN(FBITS_IN_QMFA - fBitsIn, 30); + for (n = 32; n != 0; n--) { + y = *inbuf++; + CLIP_2N_SHIFT30(y, shift); + *delayPtr++ = y; + } + } + + QMFAnalysisConv((int *)cTabA, delay, *delayIdx, uBuf); + + /* uBuf has at least 2 GB right now (1 from clipping to Q(FBITS_IN_QMFA), one from + * the scaling by cTab (MULSHIFT32(*delayPtr--, *cPtr++), with net gain of < 1.0) + * TODO - fuse with QMFAnalysisConv to avoid separate reordering + */ + tBuf[2*0 + 0] = uBuf[0]; + tBuf[2*0 + 1] = uBuf[1]; + for (n = 1; n < 31; n++) { + tBuf[2*n + 0] = -uBuf[64-n]; + tBuf[2*n + 1] = uBuf[n+1]; + } + tBuf[2*31 + 1] = uBuf[32]; + tBuf[2*31 + 0] = -uBuf[33]; + + /* fast in-place DCT-IV - only need 2*qmfaBands output samples */ + PreMultiply64(tBuf); /* 2 GB in, 3 GB out */ + FFT32C(tBuf); /* 3 GB in, 1 GB out */ + PostMultiply64(tBuf, qmfaBands*2); /* 1 GB in, 2 GB out */ + + /* TODO - roll into PostMultiply (if enough registers) */ + gbMask = 0; + for (n = 0; n < qmfaBands; n++) { + XBuf[2*n+0] = tBuf[ n + 0]; /* implicit scaling of 2 in our output Q format */ + gbMask |= FASTABS(XBuf[2*n+0]); + XBuf[2*n+1] = -tBuf[63 - n]; + gbMask |= FASTABS(XBuf[2*n+1]); + } + + /* fill top section with zeros for HF generation */ + for ( ; n < 64; n++) { + XBuf[2*n+0] = 0; + XBuf[2*n+1] = 0; + } + + *delayIdx = (*delayIdx == NUM_QMF_DELAY_BUFS - 1 ? 0 : *delayIdx + 1); + + /* minimum of 2 GB in output */ + return gbMask; +} + +/* lose FBITS_LOST_DCT4_64 in DCT4, gain 6 for implicit scaling by 1/64, lose 1 for cTab multiply (Q31) */ +#define FBITS_OUT_QMFS (FBITS_IN_QMFS - FBITS_LOST_DCT4_64 + 6 - 1) +#define RND_VAL (1 << (FBITS_OUT_QMFS-1)) + +/************************************************************************************** + * Function: QMFSynthesisConv + * + * Description: final convolution kernel for synthesis QMF + * + * Inputs: pointer to coefficient table, reordered for sequential access + * delay buffer of size 64*10 = 640 complex samples (1280 ints) + * index for delay ring buffer (range = [0, 9]) + * number of QMF subbands to process (range = [0, 64]) + * number of channels + * + * Outputs: 64 consecutive 16-bit PCM samples, interleaved by factor of nChans + * + * Return: none + * + * Notes: this is carefully written to be efficient on ARM + * use the assembly code version in sbrqmfsk.s when building for ARM! + **************************************************************************************/ +#if (defined (__arm) && defined (__ARMCC_VERSION)) || (defined (_WIN32) && defined (_WIN32_WCE) && defined (ARM)) || (defined(__GNUC__) && defined(__arm__)) +#ifdef __cplusplus +extern "C" +#endif +void QMFSynthesisConv(int *cPtr, int *delay, int dIdx, short *outbuf, int nChans); +#else +void QMFSynthesisConv(int *cPtr, int *delay, int dIdx, short *outbuf, int nChans) +{ + int k, dOff0, dOff1; + U64 sum64; + + dOff0 = (dIdx)*128; + dOff1 = dOff0 - 1; + if (dOff1 < 0) + dOff1 += 1280; + + /* scaling note: total gain of coefs (cPtr[0]-cPtr[9] for any k) is < 2.0, so 1 GB in delay values is adequate */ + for (k = 0; k <= 63; k++) { + sum64.w64 = 0; + sum64.w64 = MADD64(sum64.w64, *cPtr++, delay[dOff0]); dOff0 -= 256; if (dOff0 < 0) {dOff0 += 1280;} + sum64.w64 = MADD64(sum64.w64, *cPtr++, delay[dOff1]); dOff1 -= 256; if (dOff1 < 0) {dOff1 += 1280;} + sum64.w64 = MADD64(sum64.w64, *cPtr++, delay[dOff0]); dOff0 -= 256; if (dOff0 < 0) {dOff0 += 1280;} + sum64.w64 = MADD64(sum64.w64, *cPtr++, delay[dOff1]); dOff1 -= 256; if (dOff1 < 0) {dOff1 += 1280;} + sum64.w64 = MADD64(sum64.w64, *cPtr++, delay[dOff0]); dOff0 -= 256; if (dOff0 < 0) {dOff0 += 1280;} + sum64.w64 = MADD64(sum64.w64, *cPtr++, delay[dOff1]); dOff1 -= 256; if (dOff1 < 0) {dOff1 += 1280;} + sum64.w64 = MADD64(sum64.w64, *cPtr++, delay[dOff0]); dOff0 -= 256; if (dOff0 < 0) {dOff0 += 1280;} + sum64.w64 = MADD64(sum64.w64, *cPtr++, delay[dOff1]); dOff1 -= 256; if (dOff1 < 0) {dOff1 += 1280;} + sum64.w64 = MADD64(sum64.w64, *cPtr++, delay[dOff0]); dOff0 -= 256; if (dOff0 < 0) {dOff0 += 1280;} + sum64.w64 = MADD64(sum64.w64, *cPtr++, delay[dOff1]); dOff1 -= 256; if (dOff1 < 0) {dOff1 += 1280;} + + dOff0++; + dOff1--; + *outbuf = CLIPTOSHORT((sum64.r.hi32 + RND_VAL) >> FBITS_OUT_QMFS); + outbuf += nChans; + } +} +#endif + +/************************************************************************************** + * Function: QMFSynthesis + * + * Description: 64-subband synthesis QMF (4.6.18.4.2) + * + * Inputs: 64 consecutive complex subband QMF samples, format = Q(FBITS_IN_QMFS) + * delay buffer of size 64*10 = 640 complex samples (1280 ints) + * index for delay ring buffer (range = [0, 9]) + * number of QMF subbands to process (range = [0, 64]) + * number of channels + * + * Outputs: 64 consecutive 16-bit PCM samples, interleaved by factor of nChans + * updated delay buffer + * updated delay index + * + * Return: none + * + * Notes: assumes MIN_GBITS_IN_QMFS guard bits in input, either from + * QMFAnalysis (if upsampling only) or from MapHF (if SBR on) + **************************************************************************************/ +void QMFSynthesis(int *inbuf, int *delay, int *delayIdx, int qmfsBands, short *outbuf, int nChans) +{ + int n, a0, a1, b0, b1, dOff0, dOff1, dIdx; + int *tBufLo, *tBufHi; + + dIdx = *delayIdx; + tBufLo = delay + dIdx*128 + 0; + tBufHi = delay + dIdx*128 + 127; + + /* reorder inputs to DCT-IV, only use first qmfsBands (complex) samples + * TODO - fuse with PreMultiply64 to avoid separate reordering steps + */ + for (n = 0; n < qmfsBands >> 1; n++) { + a0 = *inbuf++; + b0 = *inbuf++; + a1 = *inbuf++; + b1 = *inbuf++; + *tBufLo++ = a0; + *tBufLo++ = a1; + *tBufHi-- = b0; + *tBufHi-- = b1; + } + if (qmfsBands & 0x01) { + a0 = *inbuf++; + b0 = *inbuf++; + *tBufLo++ = a0; + *tBufHi-- = b0; + *tBufLo++ = 0; + *tBufHi-- = 0; + n++; + } + for ( ; n < 32; n++) { + *tBufLo++ = 0; + *tBufHi-- = 0; + *tBufLo++ = 0; + *tBufHi-- = 0; + } + + tBufLo = delay + dIdx*128 + 0; + tBufHi = delay + dIdx*128 + 64; + + /* 2 GB in, 3 GB out */ + PreMultiply64(tBufLo); + PreMultiply64(tBufHi); + + /* 3 GB in, 1 GB out */ + FFT32C(tBufLo); + FFT32C(tBufHi); + + /* 1 GB in, 2 GB out */ + PostMultiply64(tBufLo, 64); + PostMultiply64(tBufHi, 64); + + /* could fuse with PostMultiply64 to avoid separate pass */ + dOff0 = dIdx*128; + dOff1 = dIdx*128 + 64; + for (n = 32; n != 0; n--) { + a0 = (*tBufLo++); + a1 = (*tBufLo++); + b0 = (*tBufHi++); + b1 = -(*tBufHi++); + + delay[dOff0++] = (b0 - a0); + delay[dOff0++] = (b1 - a1); + delay[dOff1++] = (b0 + a0); + delay[dOff1++] = (b1 + a1); + } + + QMFSynthesisConv((int *)cTabS, delay, dIdx, outbuf, nChans); + + *delayIdx = (*delayIdx == NUM_QMF_DELAY_BUFS - 1 ? 0 : *delayIdx + 1); +} diff --git a/components/spotify/cspot/bell/libhelix-aac/sbrside.c b/components/spotify/cspot/bell/libhelix-aac/sbrside.c new file mode 100644 index 00000000..6bb49f31 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/sbrside.c @@ -0,0 +1,575 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: sbrside.c,v 1.2 2005/05/24 16:01:55 albertofloyd Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * sbrside.c - functions for unpacking side info from SBR bitstream + **************************************************************************************/ + +#include "sbr.h" + +/************************************************************************************** + * Function: GetSampRateIdx + * + * Description: get index of given sample rate + * + * Inputs: sample rate (in Hz) + * + * Outputs: none + * + * Return: index of sample rate (table 1.15 in 14496-3:2001(E)) + * -1 if sample rate not found in table + **************************************************************************************/ +int GetSampRateIdx(int sampRate) +{ + int idx; + + for (idx = 0; idx < NUM_SAMPLE_RATES; idx++) { + if (sampRate == sampRateTab[idx]) + return idx; + } + + return -1; +} + +/************************************************************************************** + * Function: UnpackSBRHeader + * + * Description: unpack SBR header (table 4.56) + * + * Inputs: BitStreamInfo struct pointing to start of SBR header + * + * Outputs: initialized SBRHeader struct for this SCE/CPE block + * + * Return: non-zero if frame reset is triggered, zero otherwise + **************************************************************************************/ +int UnpackSBRHeader(BitStreamInfo *bsi, SBRHeader *sbrHdr) +{ + SBRHeader sbrHdrPrev; + + /* save previous values so we know whether to reset decoder */ + sbrHdrPrev.startFreq = sbrHdr->startFreq; + sbrHdrPrev.stopFreq = sbrHdr->stopFreq; + sbrHdrPrev.freqScale = sbrHdr->freqScale; + sbrHdrPrev.alterScale = sbrHdr->alterScale; + sbrHdrPrev.crossOverBand = sbrHdr->crossOverBand; + sbrHdrPrev.noiseBands = sbrHdr->noiseBands; + + sbrHdr->ampRes = GetBits(bsi, 1); + sbrHdr->startFreq = GetBits(bsi, 4); + sbrHdr->stopFreq = GetBits(bsi, 4); + sbrHdr->crossOverBand = GetBits(bsi, 3); + sbrHdr->resBitsHdr = GetBits(bsi, 2); + sbrHdr->hdrExtra1 = GetBits(bsi, 1); + sbrHdr->hdrExtra2 = GetBits(bsi, 1); + + if (sbrHdr->hdrExtra1) { + sbrHdr->freqScale = GetBits(bsi, 2); + sbrHdr->alterScale = GetBits(bsi, 1); + sbrHdr->noiseBands = GetBits(bsi, 2); + } else { + /* defaults */ + sbrHdr->freqScale = 2; + sbrHdr->alterScale = 1; + sbrHdr->noiseBands = 2; + } + + if (sbrHdr->hdrExtra2) { + sbrHdr->limiterBands = GetBits(bsi, 2); + sbrHdr->limiterGains = GetBits(bsi, 2); + sbrHdr->interpFreq = GetBits(bsi, 1); + sbrHdr->smoothMode = GetBits(bsi, 1); + } else { + /* defaults */ + sbrHdr->limiterBands = 2; + sbrHdr->limiterGains = 2; + sbrHdr->interpFreq = 1; + sbrHdr->smoothMode = 1; + } + sbrHdr->count++; + + /* if any of these have changed from previous frame, reset the SBR module */ + if (sbrHdr->startFreq != sbrHdrPrev.startFreq || sbrHdr->stopFreq != sbrHdrPrev.stopFreq || + sbrHdr->freqScale != sbrHdrPrev.freqScale || sbrHdr->alterScale != sbrHdrPrev.alterScale || + sbrHdr->crossOverBand != sbrHdrPrev.crossOverBand || sbrHdr->noiseBands != sbrHdrPrev.noiseBands + ) + return -1; + else + return 0; +} + +/* cLog2[i] = ceil(log2(i)) (disregard i == 0) */ +static const unsigned char cLog2[9] = {0, 0, 1, 2, 2, 3, 3, 3, 3}; + +/************************************************************************************** + * Function: UnpackSBRGrid + * + * Description: unpack SBR grid (table 4.62) + * + * Inputs: BitStreamInfo struct pointing to start of SBR grid + * initialized SBRHeader struct for this SCE/CPE block + * + * Outputs: initialized SBRGrid struct for this channel + * + * Return: none + **************************************************************************************/ +static void UnpackSBRGrid(BitStreamInfo *bsi, SBRHeader *sbrHdr, SBRGrid *sbrGrid) +{ + int numEnvRaw, env, rel, pBits, border, middleBorder=0; + unsigned char relBordLead[MAX_NUM_ENV], relBordTrail[MAX_NUM_ENV]; + unsigned char relBorder0[3], relBorder1[3], relBorder[3]; + unsigned char numRelBorder0, numRelBorder1, numRelBorder, numRelLead=0, numRelTrail; + unsigned char absBordLead=0, absBordTrail=0, absBorder; + + sbrGrid->ampResFrame = sbrHdr->ampRes; + sbrGrid->frameClass = GetBits(bsi, 2); + switch (sbrGrid->frameClass) { + + case SBR_GRID_FIXFIX: + numEnvRaw = GetBits(bsi, 2); + sbrGrid->numEnv = (1 << numEnvRaw); + if (sbrGrid->numEnv == 1) + sbrGrid->ampResFrame = 0; + + ASSERT(sbrGrid->numEnv == 1 || sbrGrid->numEnv == 2 || sbrGrid->numEnv == 4); + + sbrGrid->freqRes[0] = GetBits(bsi, 1); + for (env = 1; env < sbrGrid->numEnv; env++) + sbrGrid->freqRes[env] = sbrGrid->freqRes[0]; + + absBordLead = 0; + absBordTrail = NUM_TIME_SLOTS; + numRelLead = sbrGrid->numEnv - 1; + numRelTrail = 0; + + /* numEnv = 1, 2, or 4 */ + if (sbrGrid->numEnv == 1) border = NUM_TIME_SLOTS / 1; + else if (sbrGrid->numEnv == 2) border = NUM_TIME_SLOTS / 2; + else border = NUM_TIME_SLOTS / 4; + + for (rel = 0; rel < numRelLead; rel++) + relBordLead[rel] = border; + + middleBorder = (sbrGrid->numEnv >> 1); + + break; + + case SBR_GRID_FIXVAR: + absBorder = GetBits(bsi, 2) + NUM_TIME_SLOTS; + numRelBorder = GetBits(bsi, 2); + sbrGrid->numEnv = numRelBorder + 1; + for (rel = 0; rel < numRelBorder; rel++) + relBorder[rel] = 2*GetBits(bsi, 2) + 2; + + pBits = cLog2[sbrGrid->numEnv + 1]; + sbrGrid->pointer = GetBits(bsi, pBits); + + for (env = sbrGrid->numEnv - 1; env >= 0; env--) + sbrGrid->freqRes[env] = GetBits(bsi, 1); + + absBordLead = 0; + absBordTrail = absBorder; + numRelLead = 0; + numRelTrail = numRelBorder; + + for (rel = 0; rel < numRelTrail; rel++) + relBordTrail[rel] = relBorder[rel]; + + if (sbrGrid->pointer > 1) middleBorder = sbrGrid->numEnv + 1 - sbrGrid->pointer; + else middleBorder = sbrGrid->numEnv - 1; + + break; + + case SBR_GRID_VARFIX: + absBorder = GetBits(bsi, 2); + numRelBorder = GetBits(bsi, 2); + sbrGrid->numEnv = numRelBorder + 1; + for (rel = 0; rel < numRelBorder; rel++) + relBorder[rel] = 2*GetBits(bsi, 2) + 2; + + pBits = cLog2[sbrGrid->numEnv + 1]; + sbrGrid->pointer = GetBits(bsi, pBits); + + for (env = 0; env < sbrGrid->numEnv; env++) + sbrGrid->freqRes[env] = GetBits(bsi, 1); + + absBordLead = absBorder; + absBordTrail = NUM_TIME_SLOTS; + numRelLead = numRelBorder; + numRelTrail = 0; + + for (rel = 0; rel < numRelLead; rel++) + relBordLead[rel] = relBorder[rel]; + + if (sbrGrid->pointer == 0) middleBorder = 1; + else if (sbrGrid->pointer == 1) middleBorder = sbrGrid->numEnv - 1; + else middleBorder = sbrGrid->pointer - 1; + + break; + + case SBR_GRID_VARVAR: + absBordLead = GetBits(bsi, 2); /* absBorder0 */ + absBordTrail = GetBits(bsi, 2) + NUM_TIME_SLOTS; /* absBorder1 */ + numRelBorder0 = GetBits(bsi, 2); + numRelBorder1 = GetBits(bsi, 2); + + sbrGrid->numEnv = numRelBorder0 + numRelBorder1 + 1; + ASSERT(sbrGrid->numEnv <= 5); + + for (rel = 0; rel < numRelBorder0; rel++) + relBorder0[rel] = 2*GetBits(bsi, 2) + 2; + + for (rel = 0; rel < numRelBorder1; rel++) + relBorder1[rel] = 2*GetBits(bsi, 2) + 2; + + pBits = cLog2[numRelBorder0 + numRelBorder1 + 2]; + sbrGrid->pointer = GetBits(bsi, pBits); + + for (env = 0; env < sbrGrid->numEnv; env++) + sbrGrid->freqRes[env] = GetBits(bsi, 1); + + numRelLead = numRelBorder0; + numRelTrail = numRelBorder1; + + for (rel = 0; rel < numRelLead; rel++) + relBordLead[rel] = relBorder0[rel]; + + for (rel = 0; rel < numRelTrail; rel++) + relBordTrail[rel] = relBorder1[rel]; + + if (sbrGrid->pointer > 1) middleBorder = sbrGrid->numEnv + 1 - sbrGrid->pointer; + else middleBorder = sbrGrid->numEnv - 1; + + break; + } + + /* build time border vector */ + sbrGrid->envTimeBorder[0] = absBordLead * SAMPLES_PER_SLOT; + + rel = 0; + border = absBordLead; + for (env = 1; env <= numRelLead; env++) { + border += relBordLead[rel++]; + sbrGrid->envTimeBorder[env] = border * SAMPLES_PER_SLOT; + } + + rel = 0; + border = absBordTrail; + for (env = sbrGrid->numEnv - 1; env > numRelLead; env--) { + border -= relBordTrail[rel++]; + sbrGrid->envTimeBorder[env] = border * SAMPLES_PER_SLOT; + } + + sbrGrid->envTimeBorder[sbrGrid->numEnv] = absBordTrail * SAMPLES_PER_SLOT; + + if (sbrGrid->numEnv > 1) { + sbrGrid->numNoiseFloors = 2; + sbrGrid->noiseTimeBorder[0] = sbrGrid->envTimeBorder[0]; + sbrGrid->noiseTimeBorder[1] = sbrGrid->envTimeBorder[middleBorder]; + sbrGrid->noiseTimeBorder[2] = sbrGrid->envTimeBorder[sbrGrid->numEnv]; + } else { + sbrGrid->numNoiseFloors = 1; + sbrGrid->noiseTimeBorder[0] = sbrGrid->envTimeBorder[0]; + sbrGrid->noiseTimeBorder[1] = sbrGrid->envTimeBorder[1]; + } +} + +/************************************************************************************** + * Function: UnpackDeltaTimeFreq + * + * Description: unpack time/freq flags for delta coding of SBR envelopes (table 4.63) + * + * Inputs: BitStreamInfo struct pointing to start of dt/df flags + * number of envelopes + * number of noise floors + * + * Outputs: delta flags for envelope and noise floors + * + * Return: none + **************************************************************************************/ +static void UnpackDeltaTimeFreq(BitStreamInfo *bsi, int numEnv, unsigned char *deltaFlagEnv, + int numNoiseFloors, unsigned char *deltaFlagNoise) +{ + int env, noiseFloor; + + for (env = 0; env < numEnv; env++) + deltaFlagEnv[env] = GetBits(bsi, 1); + + for (noiseFloor = 0; noiseFloor < numNoiseFloors; noiseFloor++) + deltaFlagNoise[noiseFloor] = GetBits(bsi, 1); +} + +/************************************************************************************** + * Function: UnpackInverseFilterMode + * + * Description: unpack invf flags for chirp factor calculation (table 4.64) + * + * Inputs: BitStreamInfo struct pointing to start of invf flags + * number of noise floor bands + * + * Outputs: invf flags for noise floor bands + * + * Return: none + **************************************************************************************/ +static void UnpackInverseFilterMode(BitStreamInfo *bsi, int numNoiseFloorBands, unsigned char *mode) +{ + int n; + + for (n = 0; n < numNoiseFloorBands; n++) + mode[n] = GetBits(bsi, 2); +} + +/************************************************************************************** + * Function: UnpackSinusoids + * + * Description: unpack sinusoid (harmonic) flags for each SBR subband (table 4.67) + * + * Inputs: BitStreamInfo struct pointing to start of sinusoid flags + * number of high resolution SBR subbands (nHigh) + * + * Outputs: sinusoid flags for each SBR subband, zero-filled above nHigh + * + * Return: none + **************************************************************************************/ +static void UnpackSinusoids(BitStreamInfo *bsi, int nHigh, int addHarmonicFlag, unsigned char *addHarmonic) +{ + int n; + + n = 0; + if (addHarmonicFlag) { + for ( ; n < nHigh; n++) + addHarmonic[n] = GetBits(bsi, 1); + } + + /* zero out unused bands */ + for ( ; n < MAX_QMF_BANDS; n++) + addHarmonic[n] = 0; +} + +/************************************************************************************** + * Function: CopyCouplingGrid + * + * Description: copy grid parameters from left to right for channel coupling + * + * Inputs: initialized SBRGrid struct for left channel + * + * Outputs: initialized SBRGrid struct for right channel + * + * Return: none + **************************************************************************************/ +static void CopyCouplingGrid(SBRGrid *sbrGridLeft, SBRGrid *sbrGridRight) +{ + int env, noiseFloor; + + sbrGridRight->frameClass = sbrGridLeft->frameClass; + sbrGridRight->ampResFrame = sbrGridLeft->ampResFrame; + sbrGridRight->pointer = sbrGridLeft->pointer; + + sbrGridRight->numEnv = sbrGridLeft->numEnv; + for (env = 0; env < sbrGridLeft->numEnv; env++) { + sbrGridRight->envTimeBorder[env] = sbrGridLeft->envTimeBorder[env]; + sbrGridRight->freqRes[env] = sbrGridLeft->freqRes[env]; + } + sbrGridRight->envTimeBorder[env] = sbrGridLeft->envTimeBorder[env]; /* borders are [0, numEnv] inclusive */ + + sbrGridRight->numNoiseFloors = sbrGridLeft->numNoiseFloors; + for (noiseFloor = 0; noiseFloor <= sbrGridLeft->numNoiseFloors; noiseFloor++) + sbrGridRight->noiseTimeBorder[noiseFloor] = sbrGridLeft->noiseTimeBorder[noiseFloor]; + + /* numEnvPrev, numNoiseFloorsPrev, freqResPrev are updated in DecodeSBREnvelope() and DecodeSBRNoise() */ +} + +/************************************************************************************** + * Function: CopyCouplingInverseFilterMode + * + * Description: copy invf flags from left to right for channel coupling + * + * Inputs: invf flags for left channel + * number of noise floor bands + * + * Outputs: invf flags for right channel + * + * Return: none + **************************************************************************************/ +static void CopyCouplingInverseFilterMode(int numNoiseFloorBands, unsigned char *modeLeft, unsigned char *modeRight) +{ + int band; + + for (band = 0; band < numNoiseFloorBands; band++) + modeRight[band] = modeLeft[band]; +} + +/************************************************************************************** + * Function: UnpackSBRSingleChannel + * + * Description: unpack sideband info (grid, delta flags, invf flags, envelope and + * noise floor configuration, sinusoids) for a single channel + * + * Inputs: BitStreamInfo struct pointing to start of sideband info + * initialized PSInfoSBR struct (after parsing SBR header and building + * frequency tables) + * base output channel (range = [0, nChans-1]) + * + * Outputs: updated PSInfoSBR struct (SBRGrid and SBRChan) + * + * Return: none + **************************************************************************************/ +void UnpackSBRSingleChannel(BitStreamInfo *bsi, PSInfoSBR *psi, int chBase) +{ + int bitsLeft; + SBRHeader *sbrHdr = &(psi->sbrHdr[chBase]); + SBRGrid *sbrGridL = &(psi->sbrGrid[chBase+0]); + SBRFreq *sbrFreq = &(psi->sbrFreq[chBase]); + SBRChan *sbrChanL = &(psi->sbrChan[chBase+0]); + + psi->dataExtra = GetBits(bsi, 1); + if (psi->dataExtra) + psi->resBitsData = GetBits(bsi, 4); + + UnpackSBRGrid(bsi, sbrHdr, sbrGridL); + UnpackDeltaTimeFreq(bsi, sbrGridL->numEnv, sbrChanL->deltaFlagEnv, sbrGridL->numNoiseFloors, sbrChanL->deltaFlagNoise); + UnpackInverseFilterMode(bsi, sbrFreq->numNoiseFloorBands, sbrChanL->invfMode[1]); + + DecodeSBREnvelope(bsi, psi, sbrGridL, sbrFreq, sbrChanL, 0); + DecodeSBRNoise(bsi, psi, sbrGridL, sbrFreq, sbrChanL, 0); + + sbrChanL->addHarmonicFlag[1] = GetBits(bsi, 1); + UnpackSinusoids(bsi, sbrFreq->nHigh, sbrChanL->addHarmonicFlag[1], sbrChanL->addHarmonic[1]); + + psi->extendedDataPresent = GetBits(bsi, 1); + if (psi->extendedDataPresent) { + psi->extendedDataSize = GetBits(bsi, 4); + if (psi->extendedDataSize == 15) + psi->extendedDataSize += GetBits(bsi, 8); + + bitsLeft = 8 * psi->extendedDataSize; + + /* get ID, unpack extension info, do whatever is necessary with it... */ + while (bitsLeft > 0) { + GetBits(bsi, 8); + bitsLeft -= 8; + } + } +} + +/************************************************************************************** + * Function: UnpackSBRChannelPair + * + * Description: unpack sideband info (grid, delta flags, invf flags, envelope and + * noise floor configuration, sinusoids) for a channel pair + * + * Inputs: BitStreamInfo struct pointing to start of sideband info + * initialized PSInfoSBR struct (after parsing SBR header and building + * frequency tables) + * base output channel (range = [0, nChans-1]) + * + * Outputs: updated PSInfoSBR struct (SBRGrid and SBRChan for both channels) + * + * Return: none + **************************************************************************************/ +void UnpackSBRChannelPair(BitStreamInfo *bsi, PSInfoSBR *psi, int chBase) +{ + int bitsLeft; + SBRHeader *sbrHdr = &(psi->sbrHdr[chBase]); + SBRGrid *sbrGridL = &(psi->sbrGrid[chBase+0]), *sbrGridR = &(psi->sbrGrid[chBase+1]); + SBRFreq *sbrFreq = &(psi->sbrFreq[chBase]); + SBRChan *sbrChanL = &(psi->sbrChan[chBase+0]), *sbrChanR = &(psi->sbrChan[chBase+1]); + + psi->dataExtra = GetBits(bsi, 1); + if (psi->dataExtra) { + psi->resBitsData = GetBits(bsi, 4); + psi->resBitsData = GetBits(bsi, 4); + } + + psi->couplingFlag = GetBits(bsi, 1); + if (psi->couplingFlag) { + UnpackSBRGrid(bsi, sbrHdr, sbrGridL); + CopyCouplingGrid(sbrGridL, sbrGridR); + + UnpackDeltaTimeFreq(bsi, sbrGridL->numEnv, sbrChanL->deltaFlagEnv, sbrGridL->numNoiseFloors, sbrChanL->deltaFlagNoise); + UnpackDeltaTimeFreq(bsi, sbrGridR->numEnv, sbrChanR->deltaFlagEnv, sbrGridR->numNoiseFloors, sbrChanR->deltaFlagNoise); + + UnpackInverseFilterMode(bsi, sbrFreq->numNoiseFloorBands, sbrChanL->invfMode[1]); + CopyCouplingInverseFilterMode(sbrFreq->numNoiseFloorBands, sbrChanL->invfMode[1], sbrChanR->invfMode[1]); + + DecodeSBREnvelope(bsi, psi, sbrGridL, sbrFreq, sbrChanL, 0); + DecodeSBRNoise(bsi, psi, sbrGridL, sbrFreq, sbrChanL, 0); + DecodeSBREnvelope(bsi, psi, sbrGridR, sbrFreq, sbrChanR, 1); + DecodeSBRNoise(bsi, psi, sbrGridR, sbrFreq, sbrChanR, 1); + + /* pass RIGHT sbrChan struct */ + UncoupleSBREnvelope(psi, sbrGridL, sbrFreq, sbrChanR); + UncoupleSBRNoise(psi, sbrGridL, sbrFreq, sbrChanR); + + } else { + UnpackSBRGrid(bsi, sbrHdr, sbrGridL); + UnpackSBRGrid(bsi, sbrHdr, sbrGridR); + UnpackDeltaTimeFreq(bsi, sbrGridL->numEnv, sbrChanL->deltaFlagEnv, sbrGridL->numNoiseFloors, sbrChanL->deltaFlagNoise); + UnpackDeltaTimeFreq(bsi, sbrGridR->numEnv, sbrChanR->deltaFlagEnv, sbrGridR->numNoiseFloors, sbrChanR->deltaFlagNoise); + UnpackInverseFilterMode(bsi, sbrFreq->numNoiseFloorBands, sbrChanL->invfMode[1]); + UnpackInverseFilterMode(bsi, sbrFreq->numNoiseFloorBands, sbrChanR->invfMode[1]); + + DecodeSBREnvelope(bsi, psi, sbrGridL, sbrFreq, sbrChanL, 0); + DecodeSBREnvelope(bsi, psi, sbrGridR, sbrFreq, sbrChanR, 1); + DecodeSBRNoise(bsi, psi, sbrGridL, sbrFreq, sbrChanL, 0); + DecodeSBRNoise(bsi, psi, sbrGridR, sbrFreq, sbrChanR, 1); + } + + sbrChanL->addHarmonicFlag[1] = GetBits(bsi, 1); + UnpackSinusoids(bsi, sbrFreq->nHigh, sbrChanL->addHarmonicFlag[1], sbrChanL->addHarmonic[1]); + + sbrChanR->addHarmonicFlag[1] = GetBits(bsi, 1); + UnpackSinusoids(bsi, sbrFreq->nHigh, sbrChanR->addHarmonicFlag[1], sbrChanR->addHarmonic[1]); + + psi->extendedDataPresent = GetBits(bsi, 1); + if (psi->extendedDataPresent) { + psi->extendedDataSize = GetBits(bsi, 4); + if (psi->extendedDataSize == 15) + psi->extendedDataSize += GetBits(bsi, 8); + + bitsLeft = 8 * psi->extendedDataSize; + + /* get ID, unpack extension info, do whatever is necessary with it... */ + while (bitsLeft > 0) { + GetBits(bsi, 8); + bitsLeft -= 8; + } + } +} diff --git a/components/spotify/cspot/bell/libhelix-aac/sbrtabs.c b/components/spotify/cspot/bell/libhelix-aac/sbrtabs.c new file mode 100644 index 00000000..01da8617 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/sbrtabs.c @@ -0,0 +1,400 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: sbrtabs.c,v 1.1 2005/02/26 01:47:35 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * sbrtabs.c - platform-independent tables for SBR (global, read-only) + **************************************************************************************/ + +#include "sbr.h" + +/* k0Tab[sampRateIdx][k] = k0 = startMin + offset(bs_start_freq) for given sample rate (4.6.18.3.2.1) + * downsampled (single-rate) SBR not currently supported + */ +const unsigned char k0Tab[NUM_SAMPLE_RATES_SBR][16] = { + { 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 23, 27, 31 }, /* 96 kHz */ + { 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 23, 27, 31 }, /* 88 kHz */ + { 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 21, 23, 26, 30 }, /* 64 kHz */ + { 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 22, 24, 27, 31 }, /* 48 kHz */ + { 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 23, 25, 28, 32 }, /* 44 kHz */ + { 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 27, 29, 32 }, /* 32 kHz */ + { 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 27, 29, 32 }, /* 24 kHz */ + { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 26, 28, 30 }, /* 22 kHz */ + { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }, /* 16 kHz */ +}; + +/* k2Tab[sampRateIdx][k] = stopVector(bs_stop_freq) for given sample rate, bs_stop_freq = [0, 13] (4.6.18.3.2.1) + * generated with Matlab script calc_stopvec.m + * downsampled (single-rate) SBR not currently supported + */ +const unsigned char k2Tab[NUM_SAMPLE_RATES_SBR][14] = { + { 13, 15, 17, 19, 21, 24, 27, 31, 35, 39, 44, 50, 57, 64 }, /* 96 kHz */ + { 15, 17, 19, 21, 23, 26, 29, 33, 37, 41, 46, 51, 57, 64 }, /* 88 kHz */ + { 20, 22, 24, 26, 28, 31, 34, 37, 41, 45, 49, 54, 59, 64 }, /* 64 kHz */ + { 21, 23, 25, 27, 29, 32, 35, 38, 41, 45, 49, 54, 59, 64 }, /* 48 kHz */ + { 23, 25, 27, 29, 31, 34, 37, 40, 43, 47, 51, 55, 59, 64 }, /* 44 kHz */ + { 32, 34, 36, 38, 40, 42, 44, 46, 49, 52, 55, 58, 61, 64 }, /* 32 kHz */ + { 32, 34, 36, 38, 40, 42, 44, 46, 49, 52, 55, 58, 61, 64 }, /* 24 kHz */ + { 35, 36, 38, 40, 42, 44, 46, 48, 50, 52, 55, 58, 61, 64 }, /* 22 kHz */ + { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 60, 62, 64 }, /* 16 kHz */ +}; + +/* NINT(2.048E6 / Fs) (figure 4.47) + * downsampled (single-rate) SBR not currently supported + */ +const unsigned char goalSBTab[NUM_SAMPLE_RATES_SBR] = { + 21, 23, 32, 43, 46, 64, 85, 93, 128 +}; + +const HuffInfo huffTabSBRInfo[10] PROGMEM = { + {19, { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 2, 7, 4, 8, 72, 0}, 0}, + {20, { 0, 2, 2, 2, 2, 2, 1, 3, 3, 2, 4, 4, 4, 3, 2, 5, 6, 13, 15, 46}, 121}, + {17, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 0, 0, 1, 25, 10, 0, 0, 0}, 242}, + {19, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 1, 0, 1, 1, 2, 1, 29, 2, 0}, 291}, + {19, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 1, 2, 5, 1, 4, 2, 3, 34, 0}, 340}, + {20, { 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 1, 2, 3, 4, 4, 7, 10, 16}, 403}, + {14, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 13, 2, 0, 0, 0, 0, 0, 0}, 466}, + {14, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 8, 0, 0, 0, 0, 0, 0}, 491}, + {14, { 1, 1, 1, 1, 1, 1, 0, 2, 0, 1, 1, 0, 51, 2, 0, 0, 0, 0, 0, 0}, 516}, + { 8, { 1, 1, 1, 0, 1, 1, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 579}, +}; + +/* Huffman tables from appendix 4.A.6.1, includes offset of -LAV[i] for table i */ +const signed int /*short*/ huffTabSBR[604] PROGMEM = { + /* SBR table sbr_tenv15 [121] (signed) */ + 0, -1, 1, -2, 2, -3, 3, -4, 4, -5, 5, -6, 6, -7, 7, -8, + -9, 8, -10, 9, -11, 10, -12, -13, 11, -14, 12, -15, -16, 13, -19, -18, + -17, 14, -24, -20, 16, -26, -21, 15, -23, -25, -22, -60, -59, -58, -57, -56, + -55, -54, -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, + -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, + /* SBR table sbr_fenv15 [121] (signed) */ + 0, -1, 1, -2, -3, 2, -4, 3, -5, 4, -6, 5, -7, 6, -8, 7, + -9, 8, -10, 9, -11, 10, 11, -12, 12, -13, 13, 14, -14, -15, 15, 16, + 17, -16, -17, -18, -19, 18, 19, -20, -21, 20, 21, -24, -23, -22, -26, -28, + 22, 23, 25, -41, -25, 26, 27, -30, -27, 24, 28, 44, -51, -46, -44, -43, + -37, -33, -31, -29, 30, 37, 42, 47, 48, -60, -59, -58, -57, -56, -55, -54, + -53, -52, -50, -49, -48, -47, -45, -42, -40, -39, -38, -36, -35, -34, -32, 29, + 31, 32, 33, 34, 35, 36, 38, 39, 40, 41, 43, 45, 46, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, + /* SBR table sbr_tenv15b [49] (signed) */ + 0, 1, -1, 2, -2, 3, -3, 4, -4, -5, 5, -6, 6, 7, -7, 8, + -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, + -8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, + /* SBR table sbr_fenv15b [49] (signed) */ + 0, -1, 1, -2, 2, 3, -3, -4, 4, -5, 5, -6, 6, -7, 7, 8, + -9, -8, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, + -10, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, + /* SBR table sbr_tenv30 [63] (signed) */ + 0, -1, 1, -2, 2, -3, 3, -4, 4, -5, 5, -6, -7, 6, -8, 7, + -9, -10, 8, 9, 10, -13, -11, -12, -14, 11, 12, -31, -30, -29, -28, -27, + -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + /* SBR table sbr_fenv30 [63] (signed) */ + 0, -1, 1, -2, 2, -3, 3, -4, 4, -5, 5, -6, 6, -7, 7, -8, + 8, 9, -9, -10, 10, 11, -11, -12, 12, 13, -13, -15, 14, 15, -14, 18, + -18, -24, -19, 16, 17, -22, -21, -16, 20, 21, 22, 25, -23, -20, 24, -31, + -30, -29, -28, -27, -26, -25, -17, 19, 23, 26, 27, 28, 29, 30, 31, + /* SBR table sbr_tenv30b [25] (signed) */ + 0, 1, -1, -2, 2, 3, -3, -4, 4, -5, -12, -11, -10, -9, -8, -7, + -6, 5, 6, 7, 8, 9, 10, 11, 12, + /* SBR table sbr_fenv30b [25] (signed) */ + 0, -1, 1, -2, 2, 3, -3, -4, 4, -5, 5, 6, -12, -11, -10, -9, + -8, -7, -6, 7, 8, 9, 10, 11, 12, + /* SBR table sbr_tnoise30 [63] (signed) */ + 0, 1, -1, -2, 2, -3, 3, -4, 4, -5, 5, 11, -31, -30, -29, -28, + -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, + -11, -10, -9, -8, -7, -6, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + /* SBR table sbr_tnoise30b [25] (signed) */ + 0, -1, 1, -2, 2, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, +}; + +/* log2Tab[x] = floor(log2(x)), format = Q28 */ +const int log2Tab[65] PROGMEM = { + 0x00000000, 0x00000000, 0x10000000, 0x195c01a3, 0x20000000, 0x25269e12, 0x295c01a3, 0x2ceaecfe, + 0x30000000, 0x32b80347, 0x35269e12, 0x3759d4f8, 0x395c01a3, 0x3b350047, 0x3ceaecfe, 0x3e829fb6, + 0x40000000, 0x41663f6f, 0x42b80347, 0x43f782d7, 0x45269e12, 0x4646eea2, 0x4759d4f8, 0x48608280, + 0x495c01a3, 0x4a4d3c25, 0x4b350047, 0x4c1404ea, 0x4ceaecfe, 0x4dba4a47, 0x4e829fb6, 0x4f446359, + 0x50000000, 0x50b5d69b, 0x51663f6f, 0x52118b11, 0x52b80347, 0x5359ebc5, 0x53f782d7, 0x549101ea, + 0x55269e12, 0x55b88873, 0x5646eea2, 0x56d1fafd, 0x5759d4f8, 0x57dea15a, 0x58608280, 0x58df988f, + 0x595c01a3, 0x59d5d9fd, 0x5a4d3c25, 0x5ac24113, 0x5b350047, 0x5ba58feb, 0x5c1404ea, 0x5c80730b, + 0x5ceaecfe, 0x5d53847a, 0x5dba4a47, 0x5e1f4e51, 0x5e829fb6, 0x5ee44cd5, 0x5f446359, 0x5fa2f045, + 0x60000000 +}; + +/* coefficient table 4.A.87, format = Q31 + * reordered as: + * cTab[0], cTab[64], cTab[128], cTab[192], cTab[256], + * cTab[2], cTab[66], cTab[130], cTab[194], cTab[258], + * ... + * cTab[64], cTab[128], cTab[192], cTab[256], cTab[320] + * + * NOTE: cTab[1, 2, ... , 318, 319] = cTab[639, 638, ... 322, 321] + * except cTab[384] = -cTab[256], cTab[512] = -cTab[128] + */ +const int cTabA[165] PROGMEM = { + 0x00000000, 0x0055dba1, 0x01b2e41d, 0x09015651, 0x2e3a7532, 0xffed978a, 0x006090c4, 0x01fd3ba0, 0x08a24899, 0x311af3a4, + 0xfff0065d, 0x006b47fa, 0x024bf7a1, 0x082f552e, 0x33ff670e, 0xffef7b8b, 0x0075fded, 0x029e35b4, 0x07a8127d, 0x36e69691, + 0xffee1650, 0x00807994, 0x02f3e48d, 0x070bbf58, 0x39ce0477, 0xffecc31b, 0x008a7dd7, 0x034d01f0, 0x06593912, 0x3cb41219, + 0xffeb50b2, 0x009424c6, 0x03a966bb, 0x0590a67d, 0x3f962fb8, 0xffe9ca76, 0x009d10bf, 0x04083fec, 0x04b0adcb, 0x4272a385, + 0xffe88ba8, 0x00a520bb, 0x04694101, 0x03b8f8dc, 0x4547daea, 0xffe79e16, 0x00abe79e, 0x04cc2fcf, 0x02a99097, 0x4812f848, + 0xffe6d466, 0x00b1978d, 0x05303f87, 0x01816e06, 0x4ad237a2, 0xffe65416, 0x00b5c867, 0x05950122, 0x0040c496, 0x4d83976c, + 0xffe66dd0, 0x00b8394b, 0x05f9c051, 0xfee723c6, 0x5024d70e, 0xffe69423, 0x00b8c6b0, 0x065dd56a, 0xfd7475d8, 0x52b449de, + 0xffe75361, 0x00b73ab0, 0x06c0f0c0, 0xfbe8f5bd, 0x552f8ff7, 0xffe85b4b, 0x00b36acd, 0x0721bf22, 0xfa44a069, 0x579505f5, + 0xffea353a, 0x00acbd2f, 0x077fedb3, 0xf887507c, 0x59e2f69e, 0xffec8409, 0x00a3508f, 0x07da2b7f, 0xf6b1f3c3, 0x5c16d0ae, + 0xffef2395, 0x0096dcc2, 0x08303897, 0xf4c473c6, 0x5e2f6367, 0xfff294c3, 0x00872c63, 0x0880ffdd, 0xf2bf6ea4, 0x602b0c7f, + 0xfff681d6, 0x007400b8, 0x08cb4e23, 0xf0a3959f, 0x6207f220, 0xfffb42b0, 0x005d36df, 0x090ec1fc, 0xee71b2fe, 0x63c45243, + 0x00007134, 0x00426f36, 0x0949eaac, 0xec2a3f5f, 0x655f63f2, 0x0006b1cf, 0x0023b989, 0x097c1ee8, 0xe9cea84a, 0x66d76725, + 0x000d31b5, 0x0000e790, 0x09a3e163, 0xe75f8bb8, 0x682b39a4, 0x001471f8, 0xffda17f2, 0x09c0e59f, 0xe4de0cb0, 0x6959709d, + 0x001c3549, 0xffaea5d6, 0x09d19ca9, 0xe24b8f66, 0x6a619c5e, 0x0024dd50, 0xff7ee3f1, 0x09d5560b, 0xdfa93ab5, 0x6b42a864, + 0x002d8e42, 0xff4aabc8, 0x09caeb0f, 0xdcf898fb, 0x6bfbdd98, 0x003745f9, 0xff120d70, 0x09b18a1d, 0xda3b176a, 0x6c8c4c7a, + 0x004103f4, 0xfed4bec3, 0x09881dc5, 0xd7722f04, 0x6cf4073e, 0x004b6c46, 0xfe933dc0, 0x094d7ec2, 0xd49fd55f, 0x6d32730f, + 0x0055dba1, 0x01b2e41d, 0x09015651, 0x2e3a7532, 0x6d474e1d, +}; + +/* coefficient table 4.A.87, format = Q31 + * reordered as cTab[0], cTab[64], cTab[128], ... cTab[576], cTab[1], cTab[65], cTab[129], ... cTab[639] + * keeping full table (not using symmetry) to allow sequential access in synth filter inner loop + * format = Q31 + */ +const int cTabS[640] PROGMEM = { + 0x00000000, 0x0055dba1, 0x01b2e41d, 0x09015651, 0x2e3a7532, 0x6d474e1d, 0xd1c58ace, 0x09015651, 0xfe4d1be3, 0x0055dba1, + 0xffede50e, 0x005b5371, 0x01d78bfc, 0x08d3e41b, 0x2faa221c, 0x6d41d963, 0xd3337b3d, 0x09299ead, 0xfe70b8d1, 0x0050b177, + 0xffed978a, 0x006090c4, 0x01fd3ba0, 0x08a24899, 0x311af3a4, 0x6d32730f, 0xd49fd55f, 0x094d7ec2, 0xfe933dc0, 0x004b6c46, + 0xffefc9b9, 0x0065fde5, 0x02244a24, 0x086b1eeb, 0x328cc6f0, 0x6d18520e, 0xd60a46e5, 0x096d0e21, 0xfeb48d0d, 0x00465348, + 0xfff0065d, 0x006b47fa, 0x024bf7a1, 0x082f552e, 0x33ff670e, 0x6cf4073e, 0xd7722f04, 0x09881dc5, 0xfed4bec3, 0x004103f4, + 0xffeff6ca, 0x0070c8a5, 0x0274ba43, 0x07ee507c, 0x3572ec70, 0x6cc59bab, 0xd8d7f21f, 0x099ec3dc, 0xfef3f6ab, 0x003c1fa4, + 0xffef7b8b, 0x0075fded, 0x029e35b4, 0x07a8127d, 0x36e69691, 0x6c8c4c7a, 0xda3b176a, 0x09b18a1d, 0xff120d70, 0x003745f9, + 0xffeedfa4, 0x007b3875, 0x02c89901, 0x075ca90c, 0x385a49c4, 0x6c492217, 0xdb9b5b12, 0x09c018ce, 0xff2ef725, 0x00329ab6, + 0xffee1650, 0x00807994, 0x02f3e48d, 0x070bbf58, 0x39ce0477, 0x6bfbdd98, 0xdcf898fb, 0x09caeb0f, 0xff4aabc8, 0x002d8e42, + 0xffed651d, 0x0085c217, 0x03201116, 0x06b559c3, 0x3b415115, 0x6ba4629f, 0xde529086, 0x09d1fa23, 0xff6542d1, 0x00293718, + 0xffecc31b, 0x008a7dd7, 0x034d01f0, 0x06593912, 0x3cb41219, 0x6b42a864, 0xdfa93ab5, 0x09d5560b, 0xff7ee3f1, 0x0024dd50, + 0xffebe77b, 0x008f4bfc, 0x037ad438, 0x05f7fb90, 0x3e25b17e, 0x6ad73e8d, 0xe0fc421e, 0x09d52709, 0xff975c01, 0x002064f8, + 0xffeb50b2, 0x009424c6, 0x03a966bb, 0x0590a67d, 0x3f962fb8, 0x6a619c5e, 0xe24b8f66, 0x09d19ca9, 0xffaea5d6, 0x001c3549, + 0xffea9192, 0x0098b855, 0x03d8afe6, 0x05237f9d, 0x41058bc6, 0x69e29784, 0xe396a45d, 0x09cab9f2, 0xffc4e365, 0x0018703f, + 0xffe9ca76, 0x009d10bf, 0x04083fec, 0x04b0adcb, 0x4272a385, 0x6959709d, 0xe4de0cb0, 0x09c0e59f, 0xffda17f2, 0x001471f8, + 0xffe940f4, 0x00a1039c, 0x043889c6, 0x0437fb0a, 0x43de620a, 0x68c7269b, 0xe620c476, 0x09b3d77f, 0xffee183b, 0x0010bc63, + 0xffe88ba8, 0x00a520bb, 0x04694101, 0x03b8f8dc, 0x4547daea, 0x682b39a4, 0xe75f8bb8, 0x09a3e163, 0x0000e790, 0x000d31b5, + 0xffe83a07, 0x00a8739d, 0x049aa82f, 0x03343533, 0x46aea856, 0x6785c24d, 0xe89971b7, 0x099140a7, 0x00131c75, 0x0009aa3f, + 0xffe79e16, 0x00abe79e, 0x04cc2fcf, 0x02a99097, 0x4812f848, 0x66d76725, 0xe9cea84a, 0x097c1ee8, 0x0023b989, 0x0006b1cf, + 0xffe7746e, 0x00af374c, 0x04fe20be, 0x02186a91, 0x4973fef1, 0x661fd6b8, 0xeafee7f1, 0x0963ed46, 0x0033b927, 0x00039609, + 0xffe6d466, 0x00b1978d, 0x05303f87, 0x01816e06, 0x4ad237a2, 0x655f63f2, 0xec2a3f5f, 0x0949eaac, 0x00426f36, 0x00007134, + 0xffe6afee, 0x00b3d15c, 0x05626209, 0x00e42fa2, 0x4c2ca3df, 0x64964063, 0xed50a31d, 0x092d7970, 0x00504f41, 0xfffdfa25, + 0xffe65416, 0x00b5c867, 0x05950122, 0x0040c496, 0x4d83976c, 0x63c45243, 0xee71b2fe, 0x090ec1fc, 0x005d36df, 0xfffb42b0, + 0xffe681c6, 0x00b74c37, 0x05c76fed, 0xff96db90, 0x4ed62be3, 0x62ea6474, 0xef8d4d7b, 0x08edfeaa, 0x006928a0, 0xfff91fca, + 0xffe66dd0, 0x00b8394b, 0x05f9c051, 0xfee723c6, 0x5024d70e, 0x6207f220, 0xf0a3959f, 0x08cb4e23, 0x007400b8, 0xfff681d6, + 0xffe66fac, 0x00b8fe0d, 0x062bf5ec, 0xfe310657, 0x516eefb9, 0x611d58a3, 0xf1b461ab, 0x08a75da4, 0x007e0393, 0xfff48700, + 0xffe69423, 0x00b8c6b0, 0x065dd56a, 0xfd7475d8, 0x52b449de, 0x602b0c7f, 0xf2bf6ea4, 0x0880ffdd, 0x00872c63, 0xfff294c3, + 0xffe6fed4, 0x00b85f70, 0x068f8b44, 0xfcb1d740, 0x53f495aa, 0x5f30ff5f, 0xf3c4e887, 0x08594887, 0x008f87aa, 0xfff0e7ef, + 0xffe75361, 0x00b73ab0, 0x06c0f0c0, 0xfbe8f5bd, 0x552f8ff7, 0x5e2f6367, 0xf4c473c6, 0x08303897, 0x0096dcc2, 0xffef2395, + 0xffe80414, 0x00b58c8c, 0x06f1825d, 0xfb19b7bd, 0x56654bdd, 0x5d26be9b, 0xf5be0fa9, 0x08061671, 0x009da526, 0xffedc418, + 0xffe85b4b, 0x00b36acd, 0x0721bf22, 0xfa44a069, 0x579505f5, 0x5c16d0ae, 0xf6b1f3c3, 0x07da2b7f, 0x00a3508f, 0xffec8409, + 0xffe954d0, 0x00b06b68, 0x075112a2, 0xf96916f5, 0x58befacd, 0x5b001db8, 0xf79fa13a, 0x07ad8c26, 0x00a85e94, 0xffeb3849, + 0xffea353a, 0x00acbd2f, 0x077fedb3, 0xf887507c, 0x59e2f69e, 0x59e2f69e, 0xf887507c, 0x077fedb3, 0x00acbd2f, 0xffea353a, + 0xffeb3849, 0x00a85e94, 0x07ad8c26, 0xf79fa13a, 0x5b001db8, 0x58befacd, 0xf96916f5, 0x075112a2, 0x00b06b68, 0xffe954d0, + 0xffec8409, 0x00a3508f, 0x07da2b7f, 0xf6b1f3c3, 0x5c16d0ae, 0x579505f5, 0xfa44a069, 0x0721bf22, 0x00b36acd, 0xffe85b4b, + 0xffedc418, 0x009da526, 0x08061671, 0xf5be0fa9, 0x5d26be9b, 0x56654bdd, 0xfb19b7bd, 0x06f1825d, 0x00b58c8c, 0xffe80414, + 0xffef2395, 0x0096dcc2, 0x08303897, 0xf4c473c6, 0x5e2f6367, 0x552f8ff7, 0xfbe8f5bd, 0x06c0f0c0, 0x00b73ab0, 0xffe75361, + 0xfff0e7ef, 0x008f87aa, 0x08594887, 0xf3c4e887, 0x5f30ff5f, 0x53f495aa, 0xfcb1d740, 0x068f8b44, 0x00b85f70, 0xffe6fed4, + 0xfff294c3, 0x00872c63, 0x0880ffdd, 0xf2bf6ea4, 0x602b0c7f, 0x52b449de, 0xfd7475d8, 0x065dd56a, 0x00b8c6b0, 0xffe69423, + 0xfff48700, 0x007e0393, 0x08a75da4, 0xf1b461ab, 0x611d58a3, 0x516eefb9, 0xfe310657, 0x062bf5ec, 0x00b8fe0d, 0xffe66fac, + 0xfff681d6, 0x007400b8, 0x08cb4e23, 0xf0a3959f, 0x6207f220, 0x5024d70e, 0xfee723c6, 0x05f9c051, 0x00b8394b, 0xffe66dd0, + 0xfff91fca, 0x006928a0, 0x08edfeaa, 0xef8d4d7b, 0x62ea6474, 0x4ed62be3, 0xff96db90, 0x05c76fed, 0x00b74c37, 0xffe681c6, + 0xfffb42b0, 0x005d36df, 0x090ec1fc, 0xee71b2fe, 0x63c45243, 0x4d83976c, 0x0040c496, 0x05950122, 0x00b5c867, 0xffe65416, + 0xfffdfa25, 0x00504f41, 0x092d7970, 0xed50a31d, 0x64964063, 0x4c2ca3df, 0x00e42fa2, 0x05626209, 0x00b3d15c, 0xffe6afee, + 0x00007134, 0x00426f36, 0x0949eaac, 0xec2a3f5f, 0x655f63f2, 0x4ad237a2, 0x01816e06, 0x05303f87, 0x00b1978d, 0xffe6d466, + 0x00039609, 0x0033b927, 0x0963ed46, 0xeafee7f1, 0x661fd6b8, 0x4973fef1, 0x02186a91, 0x04fe20be, 0x00af374c, 0xffe7746e, + 0x0006b1cf, 0x0023b989, 0x097c1ee8, 0xe9cea84a, 0x66d76725, 0x4812f848, 0x02a99097, 0x04cc2fcf, 0x00abe79e, 0xffe79e16, + 0x0009aa3f, 0x00131c75, 0x099140a7, 0xe89971b7, 0x6785c24d, 0x46aea856, 0x03343533, 0x049aa82f, 0x00a8739d, 0xffe83a07, + 0x000d31b5, 0x0000e790, 0x09a3e163, 0xe75f8bb8, 0x682b39a4, 0x4547daea, 0x03b8f8dc, 0x04694101, 0x00a520bb, 0xffe88ba8, + 0x0010bc63, 0xffee183b, 0x09b3d77f, 0xe620c476, 0x68c7269b, 0x43de620a, 0x0437fb0a, 0x043889c6, 0x00a1039c, 0xffe940f4, + 0x001471f8, 0xffda17f2, 0x09c0e59f, 0xe4de0cb0, 0x6959709d, 0x4272a385, 0x04b0adcb, 0x04083fec, 0x009d10bf, 0xffe9ca76, + 0x0018703f, 0xffc4e365, 0x09cab9f2, 0xe396a45d, 0x69e29784, 0x41058bc6, 0x05237f9d, 0x03d8afe6, 0x0098b855, 0xffea9192, + 0x001c3549, 0xffaea5d6, 0x09d19ca9, 0xe24b8f66, 0x6a619c5e, 0x3f962fb8, 0x0590a67d, 0x03a966bb, 0x009424c6, 0xffeb50b2, + 0x002064f8, 0xff975c01, 0x09d52709, 0xe0fc421e, 0x6ad73e8d, 0x3e25b17e, 0x05f7fb90, 0x037ad438, 0x008f4bfc, 0xffebe77b, + 0x0024dd50, 0xff7ee3f1, 0x09d5560b, 0xdfa93ab5, 0x6b42a864, 0x3cb41219, 0x06593912, 0x034d01f0, 0x008a7dd7, 0xffecc31b, + 0x00293718, 0xff6542d1, 0x09d1fa23, 0xde529086, 0x6ba4629f, 0x3b415115, 0x06b559c3, 0x03201116, 0x0085c217, 0xffed651d, + 0x002d8e42, 0xff4aabc8, 0x09caeb0f, 0xdcf898fb, 0x6bfbdd98, 0x39ce0477, 0x070bbf58, 0x02f3e48d, 0x00807994, 0xffee1650, + 0x00329ab6, 0xff2ef725, 0x09c018ce, 0xdb9b5b12, 0x6c492217, 0x385a49c4, 0x075ca90c, 0x02c89901, 0x007b3875, 0xffeedfa4, + 0x003745f9, 0xff120d70, 0x09b18a1d, 0xda3b176a, 0x6c8c4c7a, 0x36e69691, 0x07a8127d, 0x029e35b4, 0x0075fded, 0xffef7b8b, + 0x003c1fa4, 0xfef3f6ab, 0x099ec3dc, 0xd8d7f21f, 0x6cc59bab, 0x3572ec70, 0x07ee507c, 0x0274ba43, 0x0070c8a5, 0xffeff6ca, + 0x004103f4, 0xfed4bec3, 0x09881dc5, 0xd7722f04, 0x6cf4073e, 0x33ff670e, 0x082f552e, 0x024bf7a1, 0x006b47fa, 0xfff0065d, + 0x00465348, 0xfeb48d0d, 0x096d0e21, 0xd60a46e5, 0x6d18520e, 0x328cc6f0, 0x086b1eeb, 0x02244a24, 0x0065fde5, 0xffefc9b9, + 0x004b6c46, 0xfe933dc0, 0x094d7ec2, 0xd49fd55f, 0x6d32730f, 0x311af3a4, 0x08a24899, 0x01fd3ba0, 0x006090c4, 0xffed978a, + 0x0050b177, 0xfe70b8d1, 0x09299ead, 0xd3337b3d, 0x6d41d963, 0x2faa221c, 0x08d3e41b, 0x01d78bfc, 0x005b5371, 0xffede50f, +}; + +/* noise table 4.A.88, format = Q31 */ +const int noiseTab[512*2] PROGMEM = { + 0x8010fd38, 0xb3dc7948, 0x7c4e2301, 0xa9904192, 0x121622a7, 0x86489625, 0xc3d53d25, 0xd0343fa9, + 0x674d6f70, 0x25f4e9fd, 0xce1a8c8b, 0x72a726c5, 0xfea6efc6, 0xaa4adb1a, 0x8b2dd628, 0xf14029e4, + 0x46321c1a, 0x604889a0, 0x33363b63, 0x815ed069, 0x802b4315, 0x8f2bf7f3, 0x85b86073, 0x745cfb46, + 0xc57886b3, 0xb76731f0, 0xa2a66772, 0x828ca631, 0x60cc145e, 0x1ad1010f, 0x090c83d4, 0x9bd7ba87, + 0x5f5aeea2, 0x8b4dbd99, 0x848e7b1e, 0x86bb9fa2, 0x26f18ae5, 0xc0b81194, 0x553407bf, 0x52c17953, + 0x755f468d, 0x166b04f8, 0xa5687981, 0x4343248b, 0xa6558d5e, 0xc5f6fab7, 0x80a4fb8c, 0x8cb53cb7, + 0x7da68a54, 0x9cd8df8a, 0xba05376c, 0xfcb58ee2, 0xfdd657a4, 0x005e35ca, 0x91c75c55, 0x367651e6, + 0x816abf85, 0x8f831c4f, 0x423f9c9c, 0x55aa919e, 0x80779834, 0xb59f4244, 0x800a095c, 0x7de9e0cc, + 0x46bda5cb, 0x4c184464, 0x2c438f71, 0x797216b5, 0x5035cee6, 0xa0c3a26e, 0x9d3f95fa, 0xd4a100c0, + 0x8ac30dac, 0x04b87397, 0x9e5ac516, 0x8b0b442e, 0x66210ad6, 0x88ba7598, 0x45b9bd33, 0xf0be5087, + 0x9261b85e, 0x364f6a31, 0x891c4b50, 0x23ad08ce, 0xf10366a6, 0x80414276, 0x1b562e06, 0x8be21591, + 0x9e798195, 0x7fb4045c, 0x7d9506cf, 0x854e691f, 0x9207f092, 0x7a94c9d5, 0x88911536, 0x3f45cc61, + 0x27059279, 0xa5b57109, 0x6d2bb67b, 0x3bdc5379, 0x74e662d8, 0x80348f8c, 0xf875e638, 0x5a8caea1, + 0x2459ae75, 0x2c54b939, 0x79ee3203, 0xb9bc8683, 0x9b6f630c, 0x9f45b351, 0x8563b2b9, 0xe5dbba41, + 0x697c7d0d, 0x7bb7c90e, 0xac900866, 0x8e6b5177, 0x8822dd37, 0x7fd5a91e, 0x7506da05, 0x82302aca, + 0xa5e4be04, 0x4b4288eb, 0x00b8bc9f, 0x4f1033e4, 0x7200d612, 0x43900c8c, 0xa815b900, 0x676ed1d4, + 0x5c5f23b2, 0xa758ee11, 0xaf73abfa, 0x11714ec0, 0x265239e0, 0xc50de679, 0x8a84e341, 0xa1438354, + 0x7f1a341f, 0x343ec96b, 0x696e71b0, 0xa13bde39, 0x81e75094, 0x80091111, 0x853a73bf, 0x80f9c1ee, + 0xe4980086, 0x886a8e28, 0xa7e89426, 0xdd93edd7, 0x7592100d, 0x0bfa8123, 0x850a26d4, 0x2e34f395, + 0x421b6c00, 0xa4a462e4, 0x4e3f5090, 0x3c189f4c, 0x3c971a56, 0xdd0376d2, 0x747a5367, 0x7bcbc9d7, + 0x3966be6a, 0x7efda616, 0x55445e15, 0x7ba2ab3f, 0x5fe684f2, 0x8cf42af9, 0x808c61c3, 0x4390c27b, + 0x7cac62ff, 0xea6cab22, 0x5d0902ad, 0xc27b7208, 0x7a27389d, 0x5820a357, 0xa29bbe59, 0x9df0f1fd, + 0x92bd67e5, 0x7195b587, 0x97cac65b, 0x8339807e, 0x8f72d832, 0x5fad8685, 0xa462d9d3, 0x81d46214, + 0x6ae93e1d, 0x6b23a5b9, 0xc2732874, 0x81795268, 0x7c568cb6, 0x668513ea, 0x428d024e, 0x66b78b3a, + 0xfee9ef03, 0x9ddcbb82, 0xa605f07e, 0x46dc55e0, 0x85415054, 0xc89ec271, 0x7c42edfb, 0x0befe59b, + 0x89b8f607, 0x6d732a1a, 0xa7081ebd, 0x7e403258, 0x21feeb7b, 0x5dd7a1e7, 0x23e3a31a, 0x129bc896, + 0xa11a6b54, 0x7f1e031c, 0xfdc1a4d1, 0x96402e53, 0xb9700f1a, 0x8168ecd6, 0x7d63d3cc, 0x87a70d65, + 0x81075a7a, 0x55c8caa7, 0xa95d00b5, 0x102b1652, 0x0bb30215, 0xe5b63237, 0xa446ca44, 0x82d4c333, + 0x67b2e094, 0x44c3d661, 0x33fd6036, 0xde1ea2a1, 0xa95e8e47, 0x78f66eb9, 0x6f2aef1e, 0xe8887247, + 0x80a3b70e, 0xfca0d9d3, 0x6bf0fd20, 0x0d5226de, 0xf4341c87, 0x5902df05, 0x7ff1a38d, 0xf02e5a5b, + 0x99f129af, 0x8ac63d01, 0x7b53f599, 0x7bb32532, 0x99ac59b0, 0x5255a80f, 0xf1320a41, 0x2497aa5c, + 0xcce60bd8, 0x787c634b, 0x7ed58c5b, 0x8a28eb3a, 0x24a5e647, 0x8b79a2c1, 0x955f5ce5, 0xa9d12bc4, + 0x7a1e20c6, 0x3eeda7ac, 0xf7be823a, 0x042924ce, 0x808b3f03, 0x364248da, 0xac2895e5, 0x69a8b5fa, + 0x97fe8b63, 0xbdeac9aa, 0x8073e0ad, 0x6c25dba7, 0x005e51d2, 0x52e74389, 0x59d3988c, 0xe5d1f39c, + 0x7b57dc91, 0x341adbe7, 0xa7d42b8d, 0x74e9f335, 0xd35bf7d8, 0x5b7c0a4b, 0x75bc0874, 0x552129bf, + 0x8144b70d, 0x6de93bbb, 0x5825f14b, 0x473ec5ca, 0x80a8f37c, 0xe6552d69, 0x7898360b, 0x806379b0, + 0xa9b59339, 0x3f6bf60c, 0xc367d731, 0x920ade99, 0x125592f7, 0x877e5ed1, 0xda895d95, 0x075f2ece, + 0x380e5f5e, 0x9b006b62, 0xd17a6dd2, 0x530a0e13, 0xf4cc9a14, 0x7d0a0ed4, 0x847c6e3f, 0xbaee4975, + 0x47131163, 0x64fb2cac, 0x5e2100a6, 0x7b756a42, 0xd87609f4, 0x98bfe48c, 0x0493745e, 0x836c5784, + 0x7e5ccb40, 0x3df6b476, 0x97700d28, 0x8bbd93fd, 0x56de9cdb, 0x680b4e65, 0xebc3d90e, 0x6d286793, + 0x6753712e, 0xe05c98a7, 0x3d2b6b85, 0xc4b18ddb, 0x7b59b869, 0x31435688, 0x811888e9, 0xe011ee7a, + 0x6a5844f9, 0x86ae35ea, 0xb4cbc10b, 0x01a6f5d6, 0x7a49ed64, 0x927caa49, 0x847ddaed, 0xae0d9bb6, + 0x836bdb04, 0x0fd810a6, 0x74fe126b, 0x4a346b5f, 0x80184d36, 0x5afd153c, 0x90cc8102, 0xe606d0e6, + 0xde69aa58, 0xa89f1222, 0xe06df715, 0x8fd16144, 0x0317c3e8, 0x22ce92fc, 0x690c3eca, 0x93166f02, + 0x71573414, 0x8d43cffb, 0xe8bd0bb6, 0xde86770f, 0x0bf99a41, 0x4633a661, 0xba064108, 0x7adafae3, + 0x2f6cde5d, 0xb350a52c, 0xa5ebfb0b, 0x74c57b46, 0xd3b603b5, 0x80b70892, 0xa7f7fa53, 0xd94b566c, + 0xdda3fd86, 0x6a635793, 0x3ed005ca, 0xc5f087d8, 0x31e3a746, 0x7a4278f9, 0x82def1f9, 0x06caa2b2, + 0xe9d2c349, 0x8940e7f7, 0x7feef8dd, 0x4a9b01f0, 0xacde69f8, 0x57ddc280, 0xf09e4ba4, 0xb6d9f729, + 0xb48c18f2, 0xd3654aa9, 0xca7a03c8, 0x14d57545, 0x7fda87a5, 0x0e411366, 0xb77d0df0, 0x8c2aa467, + 0x787f2590, 0x2d292db1, 0x9f12682c, 0x44ac364d, 0x1a4b31a6, 0x871f7ded, 0x7ff99167, 0x6630a1d5, + 0x25385eb9, 0x2d4dd549, 0xaf8a7004, 0x319ebe0f, 0x379ab730, 0x81dc56a4, 0x822d8523, 0x1ae8554c, + 0x18fa0786, 0x875f7de4, 0x85ca350f, 0x7de818dc, 0x7786a38f, 0xa5456355, 0x92e60f88, 0xf5526122, + 0x916039bc, 0xc561e2de, 0x31c42042, 0x7c82e290, 0x75d158b2, 0xb015bda1, 0x7220c750, 0x46565441, + 0xd0da1fdd, 0x7b777481, 0x782e73c6, 0x8cd72b7b, 0x7f1006aa, 0xfb30e51e, 0x87994818, 0x34e7c7db, + 0x7faae06b, 0xea74fbc0, 0xd20c7af4, 0xc44f396b, 0x06b4234e, 0xdf2e2a93, 0x2efb07c8, 0xce861911, + 0x7550ea05, 0xd8d90bbb, 0x58522eec, 0x746b3520, 0xce844ce9, 0x7f5cacc3, 0xda8f17e0, 0x2fedf9cb, + 0xb2f77ec4, 0x6f13f4c0, 0x834de085, 0x7b7ace4b, 0x713b16ac, 0x499c5ab0, 0x06a7961d, 0x1b39a48a, + 0xbb853e6e, 0x7c781cc1, 0xc0baebf5, 0x7dace394, 0x815ceebc, 0xcc7b27d4, 0x8274b181, 0xa2be40a2, + 0xdd01d5dc, 0x7fefeb14, 0x0813ec78, 0xba3077cc, 0xe5cf1e1c, 0xedcfacae, 0x54c43a9b, 0x5cd62a42, + 0x93806b55, 0x03095c5b, 0x8e076ae3, 0x71bfcd2a, 0x7ac1989b, 0x623bc71a, 0x5e15d4d2, 0xfb341dd1, + 0xd75dfbca, 0xd0da32be, 0xd4569063, 0x337869da, 0x3d30606a, 0xcd89cca2, 0x7dd2ae36, 0x028c03cd, + 0xd85e052c, 0xe8dc9ec5, 0x7ffd9241, 0xde5bf4c6, 0x88c4b235, 0x8228be2e, 0x7fe6ec64, 0x996abe6a, + 0xdeb0666d, 0x9eb86611, 0xd249b922, 0x18b3e26b, 0x80211168, 0x5f8bb99c, 0x6ecb0dd2, 0x4728ff8d, + 0x2ac325b8, 0x6e5169d2, 0x7ebbd68d, 0x05e41d17, 0xaaa19f28, 0x8ab238a6, 0x51f105be, 0x140809cc, + 0x7f7345d9, 0x3aae5a9d, 0xaecec6e4, 0x1afb3473, 0xf6229ed1, 0x8d55f467, 0x7e32003a, 0x70f30c14, + 0x6686f33f, 0xd0d45ed8, 0x644fab57, 0x3a3fbbd3, 0x0b255fc4, 0x679a1701, 0x90e17b6e, 0x325d537b, + 0xcd7b9b87, 0xaa7be2a2, 0x7d47c966, 0xa33dbce5, 0x8659c3bb, 0x72a41367, 0x15c446e0, 0x45fe8b0a, + 0x9d8ddf26, 0x84d47643, 0x7fabe0da, 0x36a70122, 0x7a28ebfe, 0x7c29b8b8, 0x7f760406, 0xbabe4672, + 0x23ea216e, 0x92bcc50a, 0x6d20dba2, 0xad5a7c7e, 0xbf3897f5, 0xabb793e1, 0x8391fc7e, 0xe270291c, + 0x7a248d58, 0x80f8fd15, 0x83ef19f3, 0x5e6ece7d, 0x278430c1, 0x35239f4d, 0xe09c073b, 0x50e78cb5, + 0xd4b811bd, 0xce834ee0, 0xf88aaa34, 0xf71da5a9, 0xe2b0a1d5, 0x7c3aef31, 0xe84eabca, 0x3ce25964, + 0xf29336d3, 0x8fa78b2c, 0xa3fc3415, 0x63e1313d, 0x7fbc74e0, 0x7340bc93, 0x49ae583b, 0x8b79de4b, + 0x25011ce9, 0x7b462279, 0x36007db0, 0x3da1599c, 0x77780772, 0xc845c9bb, 0x83ba68be, 0x6ee507d1, + 0x2f0159b8, 0x5392c4ed, 0x98336ff6, 0x0b3c7f11, 0xde697aac, 0x893fc8d0, 0x6b83f8f3, 0x47799a0d, + 0x801d9dfc, 0x8516a83e, 0x5f8d22ec, 0x0f8ba384, 0xa049dc4b, 0xdd920b05, 0x7a99bc9f, 0x9ad19344, + 0x7a345dba, 0xf501a13f, 0x3e58bf19, 0x7fffaf9a, 0x3b4e1511, 0x0e08b991, 0x9e157620, 0x7230a326, + 0x4977f9ff, 0x2d2bbae1, 0x607aa7fc, 0x7bc85d5f, 0xb441bbbe, 0x8d8fa5f2, 0x601cce26, 0xda1884f2, + 0x81c82d64, 0x200b709c, 0xcbd36abe, 0x8cbdddd3, 0x55ab61d3, 0x7e3ee993, 0x833f18aa, 0xffc1aaea, + 0x7362e16a, 0x7fb85db2, 0x904ee04c, 0x7f04dca6, 0x8ad7a046, 0xebe7d8f7, 0xfbc4c687, 0xd0609458, + 0x093ed977, 0x8e546085, 0x7f5b8236, 0x7c47e118, 0xa01f2641, 0x7ffb3e48, 0x05de7cda, 0x7fc281b9, + 0x8e0278fc, 0xd74e6d07, 0x94c24450, 0x7cf9e641, 0x2ad27871, 0x919fa815, 0x805fd205, 0x7758397f, + 0xe2c7e02c, 0x1828e194, 0x5613d6fe, 0xfb55359f, 0xf9699516, 0x8978ee26, 0x7feebad9, 0x77d71d82, + 0x55b28b60, 0x7e997600, 0x80821a6b, 0xc6d78af1, 0x691822ab, 0x7f6982a0, 0x7ef56f99, 0x5c307f40, + 0xac6f8b76, 0x42cc8ba4, 0x782c61d9, 0xa0224dd0, 0x7bd234d1, 0x74576e3b, 0xe38cfe9a, 0x491e66ef, + 0xc78291c5, 0x895bb87f, 0x924f7889, 0x71b89394, 0x757b779d, 0xc4a9c604, 0x5cdf7829, 0x8020e9df, + 0x805e8245, 0x4a82c398, 0x6360bd62, 0x78bb60fc, 0x09e0d014, 0x4b0ea180, 0xb841978b, 0x69a0e864, + 0x7df35977, 0x3284b0dd, 0x3cdc2efd, 0x57d31f5e, 0x541069cc, 0x1776e92e, 0x04309ea3, 0xa015eb2d, + 0xce7bfabc, 0x41b638f8, 0x8365932e, 0x846ab44c, 0xbbcc80cb, 0x8afa6cac, 0x7fc422ea, 0x4e403fc0, + 0xbfac9aee, 0x8e4c6709, 0x028e01fb, 0x6d160a9b, 0x7fe93004, 0x790f9cdc, 0x6a1f37a0, 0xf7e7ef30, + 0xb4ea0f04, 0x7bf4c8e6, 0xe981701f, 0xc258a9d3, 0x6acbbfba, 0xef5479c7, 0x079c8bd8, 0x1a410f56, + 0x6853b799, 0x86cd4f01, 0xc66e23b6, 0x34585565, 0x8d1fe00d, 0x7fcdba1a, 0x32c9717b, 0xa02f9f48, + 0xf64940db, 0x5ed7d8f1, 0x61b823b2, 0x356f8918, 0xa0a7151e, 0x793fc969, 0x530beaeb, 0x34e93270, + 0x4fc4ddb5, 0x88d58b6c, 0x36094774, 0xf620ac80, 0x03763a72, 0xf910c9a6, 0x6666fb2d, 0x752c8be8, + 0x9a6dfdd8, 0xd1a7117d, 0x51c1b1d4, 0x0a67773d, 0x43b32a79, 0x4cdcd085, 0x5f067d30, 0x05bfe92a, + 0x7ed7d203, 0xe71a3c85, 0x99127ce2, 0x8eb3cac4, 0xad4bbcea, 0x5c6a0fd0, 0x0eec04af, 0x94e95cd4, + 0x8654f921, 0x83eabb5d, 0xb058d7ca, 0x69f12d3c, 0x03d881b2, 0x80558ef7, 0x82938cb3, 0x2ec0e1d6, + 0x80044422, 0xd1e47051, 0x720fc6ff, 0x82b20316, 0x0d527b02, 0x63049a15, 0x7ad5b9ad, 0xd2a4641d, + 0x41144f86, 0x7b04917a, 0x15c4a2c0, 0x9da07916, 0x211df54a, 0x7fdd09af, 0xfe924f3f, 0x7e132cfe, + 0x9a1d18d6, 0x7c56508b, 0x80f0f0af, 0x8095ced6, 0x8037d0d7, 0x026719d1, 0xa55fec43, 0x2b1c7cb7, + 0xa5cd5ac1, 0x77639fad, 0x7fcd8b62, 0x81a18c27, 0xaee4912e, 0xeae9eebe, 0xeb3081de, 0x8532aada, + 0xc822362e, 0x86a649a9, 0x8031a71d, 0x7b319dc6, 0xea8022e6, 0x814bc5a9, 0x8f62f7a1, 0xa430ea17, + 0x388deafb, 0x883b5185, 0x776fe13c, 0x801c683f, 0x87c11b98, 0xb7cbc644, 0x8e9ad3e8, 0x3cf5a10c, + 0x7ff6a634, 0x949ef096, 0x9f84aa7c, 0x010af13f, 0x782d1de8, 0xf18e492a, 0x6cf63b01, 0x4301cd81, + 0x32d15c9e, 0x68ad8cef, 0xd09bd2d6, 0x908c5c15, 0xd1e36260, 0x2c5bfdd0, 0x88765a99, 0x93deba1e, + 0xac6ae342, 0xe865b84c, 0x0f4f2847, 0x7fdf0499, 0x78b1c9b3, 0x6a73261e, 0x601a96f6, 0xd2847933, + 0x489aa888, 0xe12e8093, 0x3bfa5a5f, 0xd96ba5f7, 0x7c8f4c8d, 0x80940c6f, 0xcef9dd1a, 0x7e1a055f, + 0x3483558b, 0x02b59cc4, 0x0c56333e, 0x05a5b813, 0x92d66287, 0x7516b679, 0x71bfe03f, 0x8056bf68, + 0xc24d0724, 0x8416bcf3, 0x234afbdb, 0x4b0d6f9c, 0xaba97333, 0x4b4f42b6, 0x7e8343ab, 0x7ffe2603, + 0xe590f73c, 0x45e10c76, 0xb07a6a78, 0xb35609d3, 0x1a027dfd, 0x90cb6e20, 0x82d3fe38, 0x7b409257, + 0x0e395afa, 0x1b802093, 0xcb0c6c59, 0x241e17e7, 0x1ee3ea0a, 0x41a82302, 0xab04350a, 0xf570beb7, + 0xbb444b9b, 0x83021459, 0x838d65dc, 0x1c439c84, 0x6fdcc454, 0xef9ef325, 0x18626c1c, 0x020d251f, + 0xc4aae786, 0x8614cb48, 0xf6f53ca6, 0x8710dbab, 0x89abec0d, 0xf29d41c1, 0x94b50336, 0xfdd49178, + 0x604658d1, 0x800e85be, 0xca1bb079, 0x7fa48eeb, 0xa3b7fafe, 0xd330436b, 0x64eb604c, 0x43a658ae, + 0x7caa1337, 0xddd445e6, 0x7efbf955, 0xb706ec71, 0x624a6b53, 0x9e0e231f, 0x97097248, 0xa1e1a17a, + 0x68dd2e44, 0x7f9d2e14, 0xddcc7074, 0x58324197, 0xc88fc426, 0x6d3640ae, 0x7ef83600, 0x759a0270, + 0x98b6d854, 0xd63c9b84, 0x372474a2, 0xe3f18cfd, 0x56ab0bdb, 0x85c9be7e, 0x47dfcfeb, 0xa5830d41, + 0x0ddd6283, 0xf4f480ad, 0x74c60e38, 0xab8943c3, 0xc1508fe7, 0x480cdc39, 0x8e097362, 0xa44793be, + 0x538b7e18, 0x545f5b41, 0x56529175, 0x9771a97e, 0xc2da7421, 0xea8265f2, 0x805d1163, 0x883c5d28, + 0x8ba94c48, 0x4f676e65, 0xf78735b3, 0xe1853671, 0x7f454f53, 0x18147f85, 0x7d09e15d, 0xdb4f3494, + 0x795c8973, 0x83310632, 0x85d8061c, 0x9a1a0ebf, 0xc125583c, 0x2a1b1a95, 0x7fd9103f, 0x71e98c72, + 0x40932ed7, 0x91ed227a, 0x3c5e560e, 0xe816dee9, 0xb0891b80, 0x600038ba, 0xc7d9a80d, 0x7fff5e09, + 0x7e3f4351, 0xbb6b4424, 0xb14448d4, 0x8d6bb7e1, 0xfb153626, 0xa68ad537, 0xd9782006, 0xf62f6991, + 0x359ba8c1, 0x02ccff0b, 0x91bf2256, 0x7ea71c4d, 0x560ce5df, 0xeeba289b, 0xa574c4e7, 0x9e04f6ee, + 0x7860a5ec, 0x0b8db4a2, 0x968ba3d7, 0x0b6c77df, 0xd6f3157d, 0x402eff1a, 0x49b820b3, 0x8152aebb, + 0xd180b0b6, 0x098604d4, 0x7ff92224, 0xede9c996, 0x89c58061, 0x829624c4, 0xc6e71ea7, 0xba94d915, + 0x389c3cf6, 0x5b4c5a06, 0x04b335e6, 0x516a8aab, 0x42c8d7d9, 0x92b12af6, 0x86c8549f, 0xfda98acf, + 0x819673b6, 0x69545dac, 0x6feaa230, 0x726e6d3f, 0x886ebdfe, 0x34f5730a, 0x7af63ba2, 0x77307bbf, + 0x7cd80630, 0x6e45efe0, 0x7f8ad7eb, 0x59d7df99, 0x86c70946, 0xda233629, 0x753f6cbf, 0x825eeb40, +}; diff --git a/components/spotify/cspot/bell/libhelix-aac/statname.h b/components/spotify/cspot/bell/libhelix-aac/statname.h new file mode 100644 index 00000000..a27f04d5 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/statname.h @@ -0,0 +1,115 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: statname.h,v 1.1 2005/02/26 01:47:34 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * statname.h - name mangling macros for static linking + **************************************************************************************/ + +#ifndef _STATNAME_H +#define _STATNAME_H + +/* define STAT_PREFIX to a unique name for static linking + * all the C functions and global variables will be mangled by the preprocessor + * e.g. void DCT4(...) becomes void raac_DCT4(...) + */ +#define STAT_PREFIX raac + +#define STATCC1(x,y,z) STATCC2(x,y,z) +#define STATCC2(x,y,z) x##y##z + +#ifdef STAT_PREFIX +#define STATNAME(func) STATCC1(STAT_PREFIX, _, func) +#else +#define STATNAME(func) func +#endif + +/* these symbols are common to all implementations */ +#define AllocateBuffers STATNAME(AllocateBuffers) +#define FreeBuffers STATNAME(FreeBuffers) +#define ClearBuffer STATNAME(ClearBuffer) + +#define SetRawBlockParams STATNAME(SetRawBlockParams) +#define PrepareRawBlock STATNAME(PrepareRawBlock) +#define FlushCodec STATNAME(FlushCodec) + +#define UnpackADTSHeader STATNAME(UnpackADTSHeader) +#define GetADTSChannelMapping STATNAME(GetADTSChannelMapping) +#define UnpackADIFHeader STATNAME(UnpackADIFHeader) +#define DecodeNextElement STATNAME(DecodeNextElement) +#define DecodeNoiselessData STATNAME(DecodeNoiselessData) +#define Dequantize STATNAME(Dequantize) +#define StereoProcess STATNAME(StereoProcess) +#define DeinterleaveShortBlocks STATNAME(DeinterleaveShortBlocks) +#define PNS STATNAME(PNS) +#define TNSFilter STATNAME(TNSFilter) +#define IMDCT STATNAME(IMDCT) + +#define InitSBR STATNAME(InitSBR) +#define DecodeSBRBitstream STATNAME(DecodeSBRBitstream) +#define DecodeSBRData STATNAME(DecodeSBRData) +#define FreeSBR STATNAME(FreeSBR) +#define FlushCodecSBR STATNAME(FlushCodecSBR) + +/* global ROM tables */ +#define sampRateTab STATNAME(sampRateTab) +#define predSFBMax STATNAME(predSFBMax) +#define channelMapTab STATNAME(channelMapTab) +#define elementNumChans STATNAME(elementNumChans) +#define sfBandTotalShort STATNAME(sfBandTotalShort) +#define sfBandTotalLong STATNAME(sfBandTotalLong) +#define sfBandTabShortOffset STATNAME(sfBandTabShortOffset) +#define sfBandTabShort STATNAME(sfBandTabShort) +#define sfBandTabLongOffset STATNAME(sfBandTabLongOffset) +#define sfBandTabLong STATNAME(sfBandTabLong) +#define tnsMaxBandsShortOffset STATNAME(tnsMaxBandsShortOffset) +#define tnsMaxBandsShort STATNAME(tnsMaxBandsShort) +#define tnsMaxOrderShort STATNAME(tnsMaxOrderShort) +#define tnsMaxBandsLongOffset STATNAME(tnsMaxBandsLongOffset) +#define tnsMaxBandsLong STATNAME(tnsMaxBandsLong) +#define tnsMaxOrderLong STATNAME(tnsMaxOrderLong) + +/* in your implementation's top-level include file (e.g. real\coder.h) you should + * add new #define sym STATNAME(sym) lines for all the + * additional global functions or variables which your + * implementation uses + */ + +#endif /* _STATNAME_H */ diff --git a/components/spotify/cspot/bell/libhelix-aac/stproc.c b/components/spotify/cspot/bell/libhelix-aac/stproc.c new file mode 100644 index 00000000..93854e45 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/stproc.c @@ -0,0 +1,246 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: stproc.c,v 1.3 2005/05/24 16:01:55 albertofloyd Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * stproc.c - mid-side and intensity stereo processing + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +/* pow14[0][i] = -pow(2, i/4.0) + * pow14[1][i] = +pow(2, i/4.0) + * + * i = [0,1,2,3] + * format = Q30 + */ + + +/************************************************************************************** + * Function: StereoProcessGroup + * + * Description: apply mid-side and intensity stereo to group of transform coefficients + * + * Inputs: dequantized transform coefficients for both channels + * pointer to appropriate scalefactor band table + * mid-side mask enabled flag + * buffer with mid-side mask (one bit for each scalefactor band) + * bit offset into mid-side mask buffer + * max coded scalefactor band + * buffer of codebook indices for right channel + * buffer of scalefactors for right channel, range = [0, 256] + * + * Outputs: updated transform coefficients in Q(FBITS_OUT_DQ_OFF) + * updated minimum guard bit count for both channels + * + * Return: none + * + * Notes: assume no guard bits in input + * gains 0 int bits + **************************************************************************************/ +static void StereoProcessGroup(int *coefL, int *coefR, const /*short*/ int *sfbTab, + int msMaskPres, unsigned char *msMaskPtr, int msMaskOffset, int maxSFB, + unsigned char *cbRight, short *sfRight, int *gbCurrent) +{ +//fb +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnarrowing" +static const int pow14[2][4] PROGMEM = { + { 0xc0000000, 0xb3e407d7, 0xa57d8666, 0x945d819b }, + { 0x40000000, 0x4c1bf829, 0x5a82799a, 0x6ba27e65 } +}; +#pragma GCC diagnostic pop + + int sfb, width, cbIdx, sf, cl, cr, scalef, scalei; + int gbMaskL, gbMaskR; + unsigned char msMask; + + msMask = (*msMaskPtr++) >> msMaskOffset; + gbMaskL = 0; + gbMaskR = 0; + + for (sfb = 0; sfb < maxSFB; sfb++) { + width = sfbTab[sfb+1] - sfbTab[sfb]; /* assume >= 0 (see sfBandTabLong/sfBandTabShort) */ + cbIdx = cbRight[sfb]; + + if (cbIdx == 14 || cbIdx == 15) { + /* intensity stereo */ + if (msMaskPres == 1 && (msMask & 0x01)) + cbIdx ^= 0x01; /* invert_intensity(): 14 becomes 15, or 15 becomes 14 */ + sf = -sfRight[sfb]; /* negative since we use identity 0.5^(x) = 2^(-x) (see spec) */ + cbIdx &= 0x01; /* choose - or + scale factor */ + scalef = pow14[cbIdx][sf & 0x03]; + scalei = (sf >> 2) + 2; /* +2 to compensate for scalef = Q30 */ + + if (scalei > 0) { + if (scalei > 30) + scalei = 30; + do { + cr = MULSHIFT32(*coefL++, scalef); + CLIP_2N(cr, 31-scalei); + cr <<= scalei; + gbMaskR |= FASTABS(cr); + *coefR++ = cr; + } while (--width); + } else { + scalei = -scalei; + if (scalei > 31) + scalei = 31; + do { + cr = MULSHIFT32(*coefL++, scalef) >> scalei; + gbMaskR |= FASTABS(cr); + *coefR++ = cr; + } while (--width); + } + } else if ( cbIdx != 13 && ((msMaskPres == 1 && (msMask & 0x01)) || msMaskPres == 2) ) { + /* mid-side stereo (assumes no GB in inputs) */ + do { + cl = *coefL; + cr = *coefR; + + if ( (FASTABS(cl) | FASTABS(cr)) >> 30 ) { + /* avoid overflow (rare) */ + cl >>= 1; + sf = cl + (cr >> 1); CLIP_2N(sf, 30); sf <<= 1; + cl = cl - (cr >> 1); CLIP_2N(cl, 30); cl <<= 1; + } else { + /* usual case */ + sf = cl + cr; + cl -= cr; + } + + *coefL++ = sf; + gbMaskL |= FASTABS(sf); + *coefR++ = cl; + gbMaskR |= FASTABS(cl); + } while (--width); + + } else { + /* nothing to do */ + coefL += width; + coefR += width; + } + + /* get next mask bit (should be branchless on ARM) */ + msMask >>= 1; + if (++msMaskOffset == 8) { + msMask = *msMaskPtr++; + msMaskOffset = 0; + } + } + + cl = CLZ(gbMaskL) - 1; + if (gbCurrent[0] > cl) + gbCurrent[0] = cl; + + cr = CLZ(gbMaskR) - 1; + if (gbCurrent[1] > cr) + gbCurrent[1] = cr; + + return; +} + +/************************************************************************************** + * Function: StereoProcess + * + * Description: apply mid-side and intensity stereo, if enabled + * + * Inputs: valid AACDecInfo struct (including dequantized transform coefficients) + * + * Outputs: updated transform coefficients in Q(FBITS_OUT_DQ_OFF) + * updated minimum guard bit count for both channels + * + * Return: 0 if successful, -1 if error + **************************************************************************************/ +int StereoProcess(AACDecInfo *aacDecInfo) +{ + PSInfoBase *psi; + ICSInfo *icsInfo; + int gp, win, nSamps, msMaskOffset; + int *coefL, *coefR; + unsigned char *msMaskPtr; + const /*short*/ int *sfbTab; + + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoBase) + return -1; + psi = (PSInfoBase *)(aacDecInfo->psInfoBase); + + /* mid-side and intensity stereo require common_window == 1 (see MPEG4 spec, Correction 2, 2004) */ + if (psi->commonWin != 1 || aacDecInfo->currBlockID != AAC_ID_CPE) + return 0; + + /* nothing to do */ + if (!psi->msMaskPresent && !psi->intensityUsed[1]) + return 0; + + icsInfo = &(psi->icsInfo[0]); + if (icsInfo->winSequence == 2) { + sfbTab = sfBandTabShort + sfBandTabShortOffset[psi->sampRateIdx]; + nSamps = NSAMPS_SHORT; + } else { + sfbTab = sfBandTabLong + sfBandTabLongOffset[psi->sampRateIdx]; + nSamps = NSAMPS_LONG; + } + coefL = psi->coef[0]; + coefR = psi->coef[1]; + + /* do fused mid-side/intensity processing for each block (one long or eight short) */ + msMaskOffset = 0; + msMaskPtr = psi->msMaskBits; + for (gp = 0; gp < icsInfo->numWinGroup; gp++) { + for (win = 0; win < icsInfo->winGroupLen[gp]; win++) { + StereoProcessGroup(coefL, coefR, sfbTab, psi->msMaskPresent, + msMaskPtr, msMaskOffset, icsInfo->maxSFB, psi->sfbCodeBook[1] + gp*icsInfo->maxSFB, + psi->scaleFactors[1] + gp*icsInfo->maxSFB, psi->gbCurrent); + coefL += nSamps; + coefR += nSamps; + } + /* we use one bit per sfb, so there are maxSFB bits for each window group */ + msMaskPtr += (msMaskOffset + icsInfo->maxSFB) >> 3; + msMaskOffset = (msMaskOffset + icsInfo->maxSFB) & 0x07; + } + + ASSERT(coefL == psi->coef[0] + 1024); + ASSERT(coefR == psi->coef[1] + 1024); + + return 0; +} diff --git a/components/spotify/cspot/bell/libhelix-aac/tns.c b/components/spotify/cspot/bell/libhelix-aac/tns.c new file mode 100644 index 00000000..87726067 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/tns.c @@ -0,0 +1,300 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: tns.c,v 1.2 2005/05/24 16:01:55 albertofloyd Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com) + * February 2005 + * + * tns.c - apply TNS to spectrum + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +#define FBITS_LPC_COEFS 20 + +//fb +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnarrowing" + +/* inverse quantization tables for TNS filter coefficients, format = Q31 + * see bottom of file for table generation + * negative (vs. spec) since we use MADD for filter kernel + */ +static const int invQuant3[16] PROGMEM = { + 0x00000000, 0xc8767f65, 0x9becf22c, 0x83358feb, 0x83358feb, 0x9becf22c, 0xc8767f65, 0x00000000, + 0x2bc750e9, 0x5246dd49, 0x6ed9eba1, 0x7e0e2e32, 0x7e0e2e32, 0x6ed9eba1, 0x5246dd49, 0x2bc750e9, +}; + +static const int invQuant4[16] PROGMEM = { + 0x00000000, 0xe5632654, 0xcbf00dbe, 0xb4c373ee, 0xa0e0a15f, 0x9126145f, 0x8643c7b3, 0x80b381ac, + 0x7f7437ad, 0x7b1d1a49, 0x7294b5f2, 0x66256db2, 0x563ba8aa, 0x4362210e, 0x2e3d2abb, 0x17851aad, +}; + +#pragma GCC diagnostic pop + +/************************************************************************************** + * Function: DecodeLPCCoefs + * + * Description: decode LPC coefficients for TNS + * + * Inputs: order of TNS filter + * resolution of coefficients (3 or 4 bits) + * coefficients unpacked from bitstream + * scratch buffer (b) of size >= order + * + * Outputs: LPC coefficients in Q(FBITS_LPC_COEFS), in 'a' + * + * Return: none + * + * Notes: assumes no guard bits in input transform coefficients + * a[i] = Q(FBITS_LPC_COEFS), don't store a0 = 1.0 + * (so a[0] = first delay tap, etc.) + * max abs(a[i]) < log2(order), so for max order = 20 a[i] < 4.4 + * (up to 3 bits of gain) so a[i] has at least 31 - FBITS_LPC_COEFS - 3 + * guard bits + * to ensure no intermediate overflow in all-pole filter, set + * FBITS_LPC_COEFS such that number of guard bits >= log2(max order) + **************************************************************************************/ +static void DecodeLPCCoefs(int order, int res, signed char *filtCoef, int *a, int *b) +{ + int i, m, t; + const int *invQuantTab; + + if (res == 3) invQuantTab = invQuant3; + else if (res == 4) invQuantTab = invQuant4; + else return; + + for (m = 0; m < order; m++) { + t = invQuantTab[filtCoef[m] & 0x0f]; /* t = Q31 */ + for (i = 0; i < m; i++) + b[i] = a[i] - (MULSHIFT32(t, a[m-i-1]) << 1); + for (i = 0; i < m; i++) + a[i] = b[i]; + a[m] = t >> (31 - FBITS_LPC_COEFS); + } +} + +/************************************************************************************** + * Function: FilterRegion + * + * Description: apply LPC filter to one region of coefficients + * + * Inputs: number of transform coefficients in this region + * direction flag (forward = 1, backward = -1) + * order of filter + * 'size' transform coefficients + * 'order' LPC coefficients in Q(FBITS_LPC_COEFS) + * scratch buffer for history (must be >= order samples long) + * + * Outputs: filtered transform coefficients + * + * Return: guard bit mask (OR of abs value of all filtered transform coefs) + * + * Notes: assumes no guard bits in input transform coefficients + * gains 0 int bits + * history buffer does not need to be preserved between regions + **************************************************************************************/ +static int FilterRegion(int size, int dir, int order, int *audioCoef, int *a, int *hist) +{ + int i, j, y, hi32, inc, gbMask; + U64 sum64; + + /* init history to 0 every time */ + for (i = 0; i < order; i++) + hist[i] = 0; + + sum64.w64 = 0; /* avoid warning */ + gbMask = 0; + inc = (dir ? -1 : 1); + do { + /* sum64 = a0*y[n] = 1.0*y[n] */ + y = *audioCoef; + sum64.r.hi32 = y >> (32 - FBITS_LPC_COEFS); + sum64.r.lo32 = y << FBITS_LPC_COEFS; + + /* sum64 += (a1*y[n-1] + a2*y[n-2] + ... + a[order-1]*y[n-(order-1)]) */ + for (j = order - 1; j > 0; j--) { + sum64.w64 = MADD64(sum64.w64, hist[j], a[j]); + hist[j] = hist[j-1]; + } + sum64.w64 = MADD64(sum64.w64, hist[0], a[0]); + y = (sum64.r.hi32 << (32 - FBITS_LPC_COEFS)) | (sum64.r.lo32 >> FBITS_LPC_COEFS); + + /* clip output (rare) */ + hi32 = sum64.r.hi32; + if ((hi32 >> 31) != (hi32 >> (FBITS_LPC_COEFS-1))) + y = (hi32 >> 31) ^ 0x7fffffff; + + hist[0] = y; + *audioCoef = y; + audioCoef += inc; + gbMask |= FASTABS(y); + } while (--size); + + return gbMask; +} + +/************************************************************************************** + * Function: TNSFilter + * + * Description: apply temporal noise shaping, if enabled + * + * Inputs: valid AACDecInfo struct + * index of current channel + * + * Outputs: updated transform coefficients + * updated minimum guard bit count for this channel + * + * Return: 0 if successful, -1 if error + **************************************************************************************/ +int TNSFilter(AACDecInfo *aacDecInfo, int ch) +{ + int win, winLen, nWindows, nSFB, filt, bottom, top, order, maxOrder, dir; + int start, end, size, tnsMaxBand, numFilt, gbMask; + int *audioCoef; + unsigned char *filtLength, *filtOrder, *filtRes, *filtDir; + signed char *filtCoef; + const unsigned /*char*/ int *tnsMaxBandTab; + const /*short*/ int *sfbTab; + ICSInfo *icsInfo; + TNSInfo *ti; + PSInfoBase *psi; + + /* validate pointers */ + if (!aacDecInfo || !aacDecInfo->psInfoBase) + return -1; + psi = (PSInfoBase *)(aacDecInfo->psInfoBase); + icsInfo = (ch == 1 && psi->commonWin == 1) ? &(psi->icsInfo[0]) : &(psi->icsInfo[ch]); + ti = &psi->tnsInfo[ch]; + + if (!ti->tnsDataPresent) + return 0; + + if (icsInfo->winSequence == 2) { + nWindows = NWINDOWS_SHORT; + winLen = NSAMPS_SHORT; + nSFB = sfBandTotalShort[psi->sampRateIdx]; + maxOrder = tnsMaxOrderShort[aacDecInfo->profile]; + sfbTab = sfBandTabShort + sfBandTabShortOffset[psi->sampRateIdx]; + tnsMaxBandTab = tnsMaxBandsShort + tnsMaxBandsShortOffset[aacDecInfo->profile]; + tnsMaxBand = tnsMaxBandTab[psi->sampRateIdx]; + } else { + nWindows = NWINDOWS_LONG; + winLen = NSAMPS_LONG; + nSFB = sfBandTotalLong[psi->sampRateIdx]; + maxOrder = tnsMaxOrderLong[aacDecInfo->profile]; + sfbTab = sfBandTabLong + sfBandTabLongOffset[psi->sampRateIdx]; + tnsMaxBandTab = tnsMaxBandsLong + tnsMaxBandsLongOffset[aacDecInfo->profile]; + tnsMaxBand = tnsMaxBandTab[psi->sampRateIdx]; + } + + if (tnsMaxBand > icsInfo->maxSFB) + tnsMaxBand = icsInfo->maxSFB; + + filtRes = ti->coefRes; + filtLength = ti->length; + filtOrder = ti->order; + filtDir = ti->dir; + filtCoef = ti->coef; + + gbMask = 0; + audioCoef = psi->coef[ch]; + for (win = 0; win < nWindows; win++) { + bottom = nSFB; + numFilt = ti->numFilt[win]; + for (filt = 0; filt < numFilt; filt++) { + top = bottom; + bottom = top - *filtLength++; + bottom = MAX(bottom, 0); + order = *filtOrder++; + order = MIN(order, maxOrder); + + if (order) { + start = sfbTab[MIN(bottom, tnsMaxBand)]; + end = sfbTab[MIN(top, tnsMaxBand)]; + size = end - start; + if (size > 0) { + dir = *filtDir++; + if (dir) + start = end - 1; + + DecodeLPCCoefs(order, filtRes[win], filtCoef, psi->tnsLPCBuf, psi->tnsWorkBuf); + gbMask |= FilterRegion(size, dir, order, audioCoef + start, psi->tnsLPCBuf, psi->tnsWorkBuf); + } + filtCoef += order; + } + } + audioCoef += winLen; + } + + /* update guard bit count if necessary */ + size = CLZ(gbMask) - 1; + if (psi->gbCurrent[ch] > size) + psi->gbCurrent[ch] = size; + + return 0; +} + +/* Code to generate invQuantXXX[] tables + * { + * int res, i, t; + * double powScale, iqfac, iqfac_m, d; + * + * powScale = pow(2.0, 31) * -1.0; / ** make coefficients negative for using MADD in kernel ** / + * for (res = 3; res <= 4; res++) { + * iqfac = ( ((1 << (res-1)) - 0.5) * (2.0 / M_PI) ); + * iqfac_m = ( ((1 << (res-1)) + 0.5) * (2.0 / M_PI) ); + * printf("static const int invQuant%d[16] = {\n", res); + * for (i = 0; i < 16; i++) { + * / ** extend bottom 4 bits into signed, 2's complement number ** / + * t = (i << 28) >> 28; + * + * if (t >= 0) d = sin(t / iqfac); + * else d = sin(t / iqfac_m); + * + * d *= powScale; + * printf("0x%08x, ", (int)(d > 0 ? d + 0.5 : d - 0.5)); + * if ((i & 0x07) == 0x07) + * printf("\n"); + * } + * printf("};\n\n"); + * } + * } + */ + diff --git a/components/spotify/cspot/bell/libhelix-aac/trigtabs.c b/components/spotify/cspot/bell/libhelix-aac/trigtabs.c new file mode 100644 index 00000000..43012904 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-aac/trigtabs.c @@ -0,0 +1,1004 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Source last modified: $Id: trigtabs.c,v 1.1 2005/02/26 01:47:35 jrecker Exp $ + * + * Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, + * are subject to the current version of the RealNetworks Public + * Source License (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the current version of the RealNetworks Community + * Source License (the "RCSL") available at + * http://www.helixcommunity.org/content/rcsl, in which case the RCSL + * will apply. You may also obtain the license terms directly from + * RealNetworks. You may not use this file except in compliance with + * the RPSL or, if you have a valid RCSL with RealNetworks applicable + * to this file, the RCSL. Please see the applicable RPSL or RCSL for + * the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point HE-AAC decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * February 2005 + * + * trigtabs.c - tables of sin, cos, etc. for IMDCT + **************************************************************************************/ + +#include "coder.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnarrowing" + +const int cos4sin4tabOffset[NUM_IMDCT_SIZES] PROGMEM = {0, 128}; + +/* PreMultiply() tables + * format = Q30 * 2^[-7, -10] for nmdct = [128, 1024] + * reordered for sequential access + * + * invM = -1.0 / nmdct; + * for (i = 0; i < nmdct/4; i++) { + * angle = (i + 0.25) * M_PI / nmdct; + * x = invM * (cos(angle) + sin(angle)); + * x = invM * sin(angle); + * + * angle = (nmdct/2 - 1 - i + 0.25) * M_PI / nmdct; + * x = invM * (cos(angle) + sin(angle)); + * x = invM * sin(angle); + * } + */ +const int cos4sin4tab[128 + 1024] PROGMEM = { +/* 128 - format = Q30 * 2^-7 */ +0xbf9bc731, 0xff9b783c, 0xbed5332c, 0xc002c697, 0xbe112251, 0xfe096c8d, 0xbd4f9c30, 0xc00f1c4a, +0xbc90a83f, 0xfc77ae5e, 0xbbd44dd9, 0xc0254e27, 0xbb1a9443, 0xfae67ba2, 0xba6382a6, 0xc04558c0, +0xb9af200f, 0xf9561237, 0xb8fd7373, 0xc06f3726, 0xb84e83ac, 0xf7c6afdc, 0xb7a25779, 0xc0a2e2e3, +0xb6f8f57c, 0xf6389228, 0xb652643e, 0xc0e05401, 0xb5aeaa2a, 0xf4abf67e, 0xb50dcd90, 0xc1278104, +0xb46fd4a4, 0xf3211a07, 0xb3d4c57c, 0xc1785ef4, 0xb33ca614, 0xf19839a6, 0xb2a77c49, 0xc1d2e158, +0xb2154dda, 0xf01191f3, 0xb186206b, 0xc236fa3b, 0xb0f9f981, 0xee8d5f29, 0xb070de82, 0xc2a49a2e, +0xafead4b9, 0xed0bdd25, 0xaf67e14f, 0xc31bb049, 0xaee80952, 0xeb8d475b, 0xae6b51ae, 0xc39c2a2f, +0xadf1bf34, 0xea11d8c8, 0xad7b5692, 0xc425f410, 0xad081c5a, 0xe899cbf1, 0xac9814fd, 0xc4b8f8ad, +0xac2b44cc, 0xe7255ad1, 0xabc1aff9, 0xc555215a, 0xab5b5a96, 0xe5b4bed8, 0xaaf84896, 0xc5fa5603, +0xaa987dca, 0xe44830dd, 0xaa3bfde3, 0xc6a87d2d, 0xa9e2cc73, 0xe2dfe917, 0xa98cece9, 0xc75f7bfe, +0xa93a6296, 0xe17c1f15, 0xa8eb30a7, 0xc81f363d, 0xa89f5a2b, 0xe01d09b4, 0xa856e20e, 0xc8e78e5b, +0xa811cb1b, 0xdec2df18, 0xa7d017fc, 0xc9b86572, 0xa791cb39, 0xdd6dd4a2, 0xa756e73a, 0xca919b4e, +0xa71f6e43, 0xdc1e1ee9, 0xa6eb6279, 0xcb730e70, 0xa6bac5dc, 0xdad3f1b1, 0xa68d9a4c, 0xcc5c9c14, +0xa663e188, 0xd98f7fe6, 0xa63d9d2b, 0xcd4e2037, 0xa61aceaf, 0xd850fb8e, 0xa5fb776b, 0xce47759a, +0xa5df9894, 0xd71895c9, 0xa5c7333e, 0xcf4875ca, 0xa5b2485a, 0xd5e67ec1, 0xa5a0d8b5, 0xd050f926, +0xa592e4fd, 0xd4bae5ab, 0xa5886dba, 0xd160d6e5, 0xa5817354, 0xd395f8ba, 0xa57df60f, 0xd277e518, +/* 1024 - format = Q30 * 2^-10 */ +0xbff3703e, 0xfff36f02, 0xbfda5824, 0xc0000b1a, 0xbfc149ed, 0xffc12b16, 0xbfa845a0, 0xc0003c74, +0xbf8f4b3e, 0xff8ee750, 0xbf765acc, 0xc0009547, 0xbf5d744e, 0xff5ca3d0, 0xbf4497c8, 0xc0011594, +0xbf2bc53d, 0xff2a60b4, 0xbf12fcb2, 0xc001bd5c, 0xbefa3e2a, 0xfef81e1d, 0xbee189a8, 0xc0028c9c, +0xbec8df32, 0xfec5dc28, 0xbeb03eca, 0xc0038356, 0xbe97a875, 0xfe939af5, 0xbe7f1c36, 0xc004a188, +0xbe669a10, 0xfe615aa3, 0xbe4e2209, 0xc005e731, 0xbe35b423, 0xfe2f1b50, 0xbe1d5062, 0xc0075452, +0xbe04f6cb, 0xfdfcdd1d, 0xbdeca760, 0xc008e8e8, 0xbdd46225, 0xfdcaa027, 0xbdbc2720, 0xc00aa4f3, +0xbda3f652, 0xfd98648d, 0xbd8bcfbf, 0xc00c8872, 0xbd73b36d, 0xfd662a70, 0xbd5ba15d, 0xc00e9364, +0xbd439995, 0xfd33f1ed, 0xbd2b9c17, 0xc010c5c7, 0xbd13a8e7, 0xfd01bb24, 0xbcfbc00a, 0xc0131f9b, +0xbce3e182, 0xfccf8634, 0xbccc0d53, 0xc015a0dd, 0xbcb44382, 0xfc9d533b, 0xbc9c8411, 0xc018498c, +0xbc84cf05, 0xfc6b2259, 0xbc6d2461, 0xc01b19a7, 0xbc558428, 0xfc38f3ac, 0xbc3dee5f, 0xc01e112b, +0xbc266309, 0xfc06c754, 0xbc0ee22a, 0xc0213018, 0xbbf76bc4, 0xfbd49d70, 0xbbdfffdd, 0xc024766a, +0xbbc89e77, 0xfba2761e, 0xbbb14796, 0xc027e421, 0xbb99fb3e, 0xfb70517d, 0xbb82b972, 0xc02b7939, +0xbb6b8235, 0xfb3e2fac, 0xbb54558d, 0xc02f35b1, 0xbb3d337b, 0xfb0c10cb, 0xbb261c04, 0xc0331986, +0xbb0f0f2b, 0xfad9f4f8, 0xbaf80cf4, 0xc03724b6, 0xbae11561, 0xfaa7dc52, 0xbaca2878, 0xc03b573f, +0xbab3463b, 0xfa75c6f8, 0xba9c6eae, 0xc03fb11d, 0xba85a1d4, 0xfa43b508, 0xba6edfb1, 0xc044324f, +0xba582849, 0xfa11a6a3, 0xba417b9e, 0xc048dad1, 0xba2ad9b5, 0xf9df9be6, 0xba144291, 0xc04daaa1, +0xb9fdb635, 0xf9ad94f0, 0xb9e734a4, 0xc052a1bb, 0xb9d0bde4, 0xf97b91e1, 0xb9ba51f6, 0xc057c01d, +0xb9a3f0de, 0xf94992d7, 0xb98d9aa0, 0xc05d05c3, 0xb9774f3f, 0xf91797f0, 0xb9610ebe, 0xc06272aa, +0xb94ad922, 0xf8e5a14d, 0xb934ae6d, 0xc06806ce, 0xb91e8ea3, 0xf8b3af0c, 0xb90879c7, 0xc06dc22e, +0xb8f26fdc, 0xf881c14b, 0xb8dc70e7, 0xc073a4c3, 0xb8c67cea, 0xf84fd829, 0xb8b093ea, 0xc079ae8c, +0xb89ab5e8, 0xf81df3c5, 0xb884e2e9, 0xc07fdf85, 0xb86f1af0, 0xf7ec143e, 0xb8595e00, 0xc08637a9, +0xb843ac1d, 0xf7ba39b3, 0xb82e0549, 0xc08cb6f5, 0xb818698a, 0xf7886442, 0xb802d8e0, 0xc0935d64, +0xb7ed5351, 0xf756940a, 0xb7d7d8df, 0xc09a2af3, 0xb7c2698e, 0xf724c92a, 0xb7ad0561, 0xc0a11f9d, +0xb797ac5b, 0xf6f303c0, 0xb7825e80, 0xc0a83b5e, 0xb76d1bd2, 0xf6c143ec, 0xb757e455, 0xc0af7e33, +0xb742b80d, 0xf68f89cb, 0xb72d96fd, 0xc0b6e815, 0xb7188127, 0xf65dd57d, 0xb7037690, 0xc0be7901, +0xb6ee773a, 0xf62c2721, 0xb6d98328, 0xc0c630f2, 0xb6c49a5e, 0xf5fa7ed4, 0xb6afbce0, 0xc0ce0fe3, +0xb69aeab0, 0xf5c8dcb6, 0xb68623d1, 0xc0d615cf, 0xb6716847, 0xf59740e5, 0xb65cb815, 0xc0de42b2, +0xb648133e, 0xf565ab80, 0xb63379c5, 0xc0e69686, 0xb61eebae, 0xf5341ca5, 0xb60a68fb, 0xc0ef1147, +0xb5f5f1b1, 0xf5029473, 0xb5e185d1, 0xc0f7b2ee, 0xb5cd255f, 0xf4d11308, 0xb5b8d05f, 0xc1007b77, +0xb5a486d2, 0xf49f9884, 0xb59048be, 0xc1096add, 0xb57c1624, 0xf46e2504, 0xb567ef08, 0xc1128119, +0xb553d36c, 0xf43cb8a7, 0xb53fc355, 0xc11bbe26, 0xb52bbec4, 0xf40b538b, 0xb517c5be, 0xc12521ff, +0xb503d845, 0xf3d9f5cf, 0xb4eff65c, 0xc12eac9d, 0xb4dc2007, 0xf3a89f92, 0xb4c85548, 0xc1385dfb, +0xb4b49622, 0xf37750f2, 0xb4a0e299, 0xc1423613, 0xb48d3ab0, 0xf3460a0d, 0xb4799e69, 0xc14c34df, +0xb4660dc8, 0xf314cb02, 0xb45288cf, 0xc1565a58, 0xb43f0f82, 0xf2e393ef, 0xb42ba1e4, 0xc160a678, +0xb4183ff7, 0xf2b264f2, 0xb404e9bf, 0xc16b193a, 0xb3f19f3e, 0xf2813e2a, 0xb3de6078, 0xc175b296, +0xb3cb2d70, 0xf2501fb5, 0xb3b80628, 0xc1807285, 0xb3a4eaa4, 0xf21f09b1, 0xb391dae6, 0xc18b5903, +0xb37ed6f1, 0xf1edfc3d, 0xb36bdec9, 0xc1966606, 0xb358f26f, 0xf1bcf777, 0xb34611e8, 0xc1a1998a, +0xb3333d36, 0xf18bfb7d, 0xb320745c, 0xc1acf386, 0xb30db75d, 0xf15b086d, 0xb2fb063b, 0xc1b873f5, +0xb2e860fa, 0xf12a1e66, 0xb2d5c79d, 0xc1c41ace, 0xb2c33a26, 0xf0f93d86, 0xb2b0b898, 0xc1cfe80a, +0xb29e42f6, 0xf0c865ea, 0xb28bd943, 0xc1dbdba3, 0xb2797b82, 0xf09797b2, 0xb26729b5, 0xc1e7f591, +0xb254e3e0, 0xf066d2fa, 0xb242aa05, 0xc1f435cc, 0xb2307c27, 0xf03617e2, 0xb21e5a49, 0xc2009c4e, +0xb20c446d, 0xf0056687, 0xb1fa3a97, 0xc20d290d, 0xb1e83cc9, 0xefd4bf08, 0xb1d64b06, 0xc219dc03, +0xb1c46551, 0xefa42181, 0xb1b28bad, 0xc226b528, 0xb1a0be1b, 0xef738e12, 0xb18efca0, 0xc233b473, +0xb17d473d, 0xef4304d8, 0xb16b9df6, 0xc240d9de, 0xb15a00cd, 0xef1285f2, 0xb1486fc5, 0xc24e255e, +0xb136eae1, 0xeee2117c, 0xb1257223, 0xc25b96ee, 0xb114058e, 0xeeb1a796, 0xb102a524, 0xc2692e83, +0xb0f150e9, 0xee81485c, 0xb0e008e0, 0xc276ec16, 0xb0cecd09, 0xee50f3ed, 0xb0bd9d6a, 0xc284cf9f, +0xb0ac7a03, 0xee20aa67, 0xb09b62d8, 0xc292d914, 0xb08a57eb, 0xedf06be6, 0xb079593f, 0xc2a1086d, +0xb06866d7, 0xedc0388a, 0xb05780b5, 0xc2af5da2, 0xb046a6db, 0xed901070, 0xb035d94e, 0xc2bdd8a9, +0xb025180e, 0xed5ff3b5, 0xb014631e, 0xc2cc7979, 0xb003ba82, 0xed2fe277, 0xaff31e3b, 0xc2db400a, +0xafe28e4d, 0xecffdcd4, 0xafd20ab9, 0xc2ea2c53, 0xafc19383, 0xeccfe2ea, 0xafb128ad, 0xc2f93e4a, +0xafa0ca39, 0xec9ff4d6, 0xaf90782a, 0xc30875e5, 0xaf803283, 0xec7012b5, 0xaf6ff945, 0xc317d31c, +0xaf5fcc74, 0xec403ca5, 0xaf4fac12, 0xc32755e5, 0xaf3f9822, 0xec1072c4, 0xaf2f90a5, 0xc336fe37, +0xaf1f959f, 0xebe0b52f, 0xaf0fa712, 0xc346cc07, 0xaeffc500, 0xebb10404, 0xaeefef6c, 0xc356bf4d, +0xaee02658, 0xeb815f60, 0xaed069c7, 0xc366d7fd, 0xaec0b9bb, 0xeb51c760, 0xaeb11636, 0xc377160f, +0xaea17f3b, 0xeb223c22, 0xae91f4cd, 0xc3877978, 0xae8276ed, 0xeaf2bdc3, 0xae73059f, 0xc398022f, +0xae63a0e3, 0xeac34c60, 0xae5448be, 0xc3a8b028, 0xae44fd31, 0xea93e817, 0xae35be3f, 0xc3b9835a, +0xae268be9, 0xea649105, 0xae176633, 0xc3ca7bba, 0xae084d1f, 0xea354746, 0xadf940ae, 0xc3db993e, +0xadea40e4, 0xea060af9, 0xaddb4dc2, 0xc3ecdbdc, 0xadcc674b, 0xe9d6dc3b, 0xadbd8d82, 0xc3fe4388, +0xadaec067, 0xe9a7bb28, 0xad9fffff, 0xc40fd037, 0xad914c4b, 0xe978a7dd, 0xad82a54c, 0xc42181e0, +0xad740b07, 0xe949a278, 0xad657d7c, 0xc4335877, 0xad56fcaf, 0xe91aab16, 0xad4888a0, 0xc44553f2, +0xad3a2153, 0xe8ebc1d3, 0xad2bc6ca, 0xc4577444, 0xad1d7907, 0xe8bce6cd, 0xad0f380c, 0xc469b963, +0xad0103db, 0xe88e1a20, 0xacf2dc77, 0xc47c2344, 0xace4c1e2, 0xe85f5be9, 0xacd6b41e, 0xc48eb1db, +0xacc8b32c, 0xe830ac45, 0xacbabf10, 0xc4a1651c, 0xacacd7cb, 0xe8020b52, 0xac9efd60, 0xc4b43cfd, +0xac912fd1, 0xe7d3792b, 0xac836f1f, 0xc4c73972, 0xac75bb4d, 0xe7a4f5ed, 0xac68145d, 0xc4da5a6f, +0xac5a7a52, 0xe77681b6, 0xac4ced2c, 0xc4ed9fe7, 0xac3f6cef, 0xe7481ca1, 0xac31f99d, 0xc50109d0, +0xac249336, 0xe719c6cb, 0xac1739bf, 0xc514981d, 0xac09ed38, 0xe6eb8052, 0xabfcada3, 0xc5284ac3, +0xabef7b04, 0xe6bd4951, 0xabe2555b, 0xc53c21b4, 0xabd53caa, 0xe68f21e5, 0xabc830f5, 0xc5501ce5, +0xabbb323c, 0xe6610a2a, 0xabae4082, 0xc5643c4a, 0xaba15bc9, 0xe633023e, 0xab948413, 0xc5787fd6, +0xab87b962, 0xe6050a3b, 0xab7afbb7, 0xc58ce77c, 0xab6e4b15, 0xe5d72240, 0xab61a77d, 0xc5a17330, +0xab5510f3, 0xe5a94a67, 0xab488776, 0xc5b622e6, 0xab3c0b0b, 0xe57b82cd, 0xab2f9bb1, 0xc5caf690, +0xab23396c, 0xe54dcb8f, 0xab16e43d, 0xc5dfee22, 0xab0a9c27, 0xe52024c9, 0xaafe612a, 0xc5f5098f, +0xaaf23349, 0xe4f28e96, 0xaae61286, 0xc60a48c9, 0xaad9fee3, 0xe4c50914, 0xaacdf861, 0xc61fabc4, +0xaac1ff03, 0xe497945d, 0xaab612ca, 0xc6353273, 0xaaaa33b8, 0xe46a308f, 0xaa9e61cf, 0xc64adcc7, +0xaa929d10, 0xe43cddc4, 0xaa86e57e, 0xc660aab5, 0xaa7b3b1b, 0xe40f9c1a, 0xaa6f9de7, 0xc6769c2e, +0xaa640de6, 0xe3e26bac, 0xaa588b18, 0xc68cb124, 0xaa4d157f, 0xe3b54c95, 0xaa41ad1e, 0xc6a2e98b, +0xaa3651f6, 0xe3883ef2, 0xaa2b0409, 0xc6b94554, 0xaa1fc358, 0xe35b42df, 0xaa148fe6, 0xc6cfc472, +0xaa0969b3, 0xe32e5876, 0xa9fe50c2, 0xc6e666d7, 0xa9f34515, 0xe3017fd5, 0xa9e846ad, 0xc6fd2c75, +0xa9dd558b, 0xe2d4b916, 0xa9d271b2, 0xc714153e, 0xa9c79b23, 0xe2a80456, 0xa9bcd1e0, 0xc72b2123, +0xa9b215ea, 0xe27b61af, 0xa9a76744, 0xc7425016, 0xa99cc5ee, 0xe24ed13d, 0xa99231eb, 0xc759a20a, +0xa987ab3c, 0xe222531c, 0xa97d31e3, 0xc77116f0, 0xa972c5e1, 0xe1f5e768, 0xa9686738, 0xc788aeb9, +0xa95e15e9, 0xe1c98e3b, 0xa953d1f7, 0xc7a06957, 0xa9499b62, 0xe19d47b1, 0xa93f722c, 0xc7b846ba, +0xa9355658, 0xe17113e5, 0xa92b47e5, 0xc7d046d6, 0xa92146d7, 0xe144f2f3, 0xa917532e, 0xc7e8699a, +0xa90d6cec, 0xe118e4f6, 0xa9039413, 0xc800aef7, 0xa8f9c8a4, 0xe0ecea09, 0xa8f00aa0, 0xc81916df, +0xa8e65a0a, 0xe0c10247, 0xa8dcb6e2, 0xc831a143, 0xa8d3212a, 0xe0952dcb, 0xa8c998e3, 0xc84a4e14, +0xa8c01e10, 0xe0696cb0, 0xa8b6b0b1, 0xc8631d42, 0xa8ad50c8, 0xe03dbf11, 0xa8a3fe57, 0xc87c0ebd, +0xa89ab95e, 0xe012250a, 0xa89181df, 0xc8952278, 0xa88857dc, 0xdfe69eb4, 0xa87f3b57, 0xc8ae5862, +0xa8762c4f, 0xdfbb2c2c, 0xa86d2ac8, 0xc8c7b06b, 0xa86436c2, 0xdf8fcd8b, 0xa85b503e, 0xc8e12a84, +0xa852773f, 0xdf6482ed, 0xa849abc4, 0xc8fac69e, 0xa840edd1, 0xdf394c6b, 0xa8383d66, 0xc91484a8, +0xa82f9a84, 0xdf0e2a22, 0xa827052d, 0xc92e6492, 0xa81e7d62, 0xdee31c2b, 0xa8160324, 0xc948664d, +0xa80d9675, 0xdeb822a1, 0xa8053756, 0xc96289c9, 0xa7fce5c9, 0xde8d3d9e, 0xa7f4a1ce, 0xc97ccef5, +0xa7ec6b66, 0xde626d3e, 0xa7e44294, 0xc99735c2, 0xa7dc2759, 0xde37b199, 0xa7d419b4, 0xc9b1be1e, +0xa7cc19a9, 0xde0d0acc, 0xa7c42738, 0xc9cc67fa, 0xa7bc4262, 0xdde278ef, 0xa7b46b29, 0xc9e73346, +0xa7aca18e, 0xddb7fc1e, 0xa7a4e591, 0xca021fef, 0xa79d3735, 0xdd8d9472, 0xa795967a, 0xca1d2de7, +0xa78e0361, 0xdd634206, 0xa7867dec, 0xca385d1d, 0xa77f061c, 0xdd3904f4, 0xa7779bf2, 0xca53ad7e, +0xa7703f70, 0xdd0edd55, 0xa768f095, 0xca6f1efc, 0xa761af64, 0xdce4cb44, 0xa75a7bdd, 0xca8ab184, +0xa7535602, 0xdcbacedb, 0xa74c3dd4, 0xcaa66506, 0xa7453353, 0xdc90e834, 0xa73e3681, 0xcac23971, +0xa7374760, 0xdc671768, 0xa73065ef, 0xcade2eb3, 0xa7299231, 0xdc3d5c91, 0xa722cc25, 0xcafa44bc, +0xa71c13ce, 0xdc13b7c9, 0xa715692c, 0xcb167b79, 0xa70ecc41, 0xdbea292b, 0xa7083d0d, 0xcb32d2da, +0xa701bb91, 0xdbc0b0ce, 0xa6fb47ce, 0xcb4f4acd, 0xa6f4e1c6, 0xdb974ece, 0xa6ee8979, 0xcb6be341, +0xa6e83ee8, 0xdb6e0342, 0xa6e20214, 0xcb889c23, 0xa6dbd2ff, 0xdb44ce46, 0xa6d5b1a9, 0xcba57563, +0xa6cf9e13, 0xdb1baff2, 0xa6c9983e, 0xcbc26eee, 0xa6c3a02b, 0xdaf2a860, 0xa6bdb5da, 0xcbdf88b3, +0xa6b7d94e, 0xdac9b7a9, 0xa6b20a86, 0xcbfcc29f, 0xa6ac4984, 0xdaa0dde7, 0xa6a69649, 0xcc1a1ca0, +0xa6a0f0d5, 0xda781b31, 0xa69b5929, 0xcc3796a5, 0xa695cf46, 0xda4f6fa3, 0xa690532d, 0xcc55309b, +0xa68ae4df, 0xda26db54, 0xa685845c, 0xcc72ea70, 0xa68031a6, 0xd9fe5e5e, 0xa67aecbd, 0xcc90c412, +0xa675b5a3, 0xd9d5f8d9, 0xa6708c57, 0xccaebd6e, 0xa66b70db, 0xd9adaadf, 0xa6666330, 0xccccd671, +0xa6616355, 0xd9857489, 0xa65c714d, 0xcceb0f0a, 0xa6578d18, 0xd95d55ef, 0xa652b6b6, 0xcd096725, +0xa64dee28, 0xd9354f2a, 0xa6493370, 0xcd27deb0, 0xa644868d, 0xd90d6053, 0xa63fe781, 0xcd467599, +0xa63b564c, 0xd8e58982, 0xa636d2ee, 0xcd652bcb, 0xa6325d6a, 0xd8bdcad0, 0xa62df5bf, 0xcd840134, +0xa6299bed, 0xd8962456, 0xa6254ff7, 0xcda2f5c2, 0xa62111db, 0xd86e962b, 0xa61ce19c, 0xcdc20960, +0xa618bf39, 0xd8472069, 0xa614aab3, 0xcde13bfd, 0xa610a40c, 0xd81fc328, 0xa60cab43, 0xce008d84, +0xa608c058, 0xd7f87e7f, 0xa604e34e, 0xce1ffde2, 0xa6011424, 0xd7d15288, 0xa5fd52db, 0xce3f8d05, +0xa5f99f73, 0xd7aa3f5a, 0xa5f5f9ed, 0xce5f3ad8, 0xa5f2624a, 0xd783450d, 0xa5eed88a, 0xce7f0748, +0xa5eb5cae, 0xd75c63ba, 0xa5e7eeb6, 0xce9ef241, 0xa5e48ea3, 0xd7359b78, 0xa5e13c75, 0xcebefbb0, +0xa5ddf82d, 0xd70eec60, 0xa5dac1cb, 0xcedf2380, 0xa5d79950, 0xd6e85689, 0xa5d47ebc, 0xceff699f, +0xa5d17210, 0xd6c1da0b, 0xa5ce734d, 0xcf1fcdf8, 0xa5cb8272, 0xd69b76fe, 0xa5c89f80, 0xcf405077, +0xa5c5ca77, 0xd6752d79, 0xa5c30359, 0xcf60f108, 0xa5c04a25, 0xd64efd94, 0xa5bd9edc, 0xcf81af97, +0xa5bb017f, 0xd628e767, 0xa5b8720d, 0xcfa28c10, 0xa5b5f087, 0xd602eb0a, 0xa5b37cee, 0xcfc3865e, +0xa5b11741, 0xd5dd0892, 0xa5aebf82, 0xcfe49e6d, 0xa5ac75b0, 0xd5b74019, 0xa5aa39cd, 0xd005d42a, +0xa5a80bd7, 0xd59191b5, 0xa5a5ebd0, 0xd027277e, 0xa5a3d9b8, 0xd56bfd7d, 0xa5a1d590, 0xd0489856, +0xa59fdf57, 0xd5468389, 0xa59df70e, 0xd06a269d, 0xa59c1cb5, 0xd52123f0, 0xa59a504c, 0xd08bd23f, +0xa59891d4, 0xd4fbdec9, 0xa596e14e, 0xd0ad9b26, 0xa5953eb8, 0xd4d6b42b, 0xa593aa14, 0xd0cf813e, +0xa5922362, 0xd4b1a42c, 0xa590aaa2, 0xd0f18472, 0xa58f3fd4, 0xd48caee4, 0xa58de2f8, 0xd113a4ad, +0xa58c940f, 0xd467d469, 0xa58b5319, 0xd135e1d9, 0xa58a2016, 0xd44314d3, 0xa588fb06, 0xd1583be2, +0xa587e3ea, 0xd41e7037, 0xa586dac1, 0xd17ab2b3, 0xa585df8c, 0xd3f9e6ad, 0xa584f24b, 0xd19d4636, +0xa58412fe, 0xd3d5784a, 0xa58341a5, 0xd1bff656, 0xa5827e40, 0xd3b12526, 0xa581c8d0, 0xd1e2c2fd, +0xa5812154, 0xd38ced57, 0xa58087cd, 0xd205ac17, 0xa57ffc3b, 0xd368d0f3, 0xa57f7e9d, 0xd228b18d, +0xa57f0ef5, 0xd344d011, 0xa57ead41, 0xd24bd34a, 0xa57e5982, 0xd320eac6, 0xa57e13b8, 0xd26f1138, +0xa57ddbe4, 0xd2fd2129, 0xa57db204, 0xd2926b41, 0xa57d961a, 0xd2d97350, 0xa57d8825, 0xd2b5e151, +}; + +/* PostMultiply() tables + * format = Q30 + * reordered for sequential access + * decimate (skip by 16 instead of 2) for small transform (128) + * + * for (i = 0; i <= (512/2); i++) { + * angle = i * M_PI / 1024; + * x = (cos(angle) + sin(angle)); + * x = sin(angle); + * } + */ +const int cos1sin1tab[514] PROGMEM = { +/* format = Q30 */ +0x40000000, 0x00000000, 0x40323034, 0x003243f1, 0x406438cf, 0x006487c4, 0x409619b2, 0x0096cb58, +0x40c7d2bd, 0x00c90e90, 0x40f963d3, 0x00fb514b, 0x412accd4, 0x012d936c, 0x415c0da3, 0x015fd4d2, +0x418d2621, 0x0192155f, 0x41be162f, 0x01c454f5, 0x41eeddaf, 0x01f69373, 0x421f7c84, 0x0228d0bb, +0x424ff28f, 0x025b0caf, 0x42803fb2, 0x028d472e, 0x42b063d0, 0x02bf801a, 0x42e05ecb, 0x02f1b755, +0x43103085, 0x0323ecbe, 0x433fd8e1, 0x03562038, 0x436f57c1, 0x038851a2, 0x439ead09, 0x03ba80df, +0x43cdd89a, 0x03ecadcf, 0x43fcda59, 0x041ed854, 0x442bb227, 0x0451004d, 0x445a5fe8, 0x0483259d, +0x4488e37f, 0x04b54825, 0x44b73ccf, 0x04e767c5, 0x44e56bbd, 0x0519845e, 0x4513702a, 0x054b9dd3, +0x454149fc, 0x057db403, 0x456ef916, 0x05afc6d0, 0x459c7d5a, 0x05e1d61b, 0x45c9d6af, 0x0613e1c5, +0x45f704f7, 0x0645e9af, 0x46240816, 0x0677edbb, 0x4650dff1, 0x06a9edc9, 0x467d8c6d, 0x06dbe9bb, +0x46aa0d6d, 0x070de172, 0x46d662d6, 0x073fd4cf, 0x47028c8d, 0x0771c3b3, 0x472e8a76, 0x07a3adff, +0x475a5c77, 0x07d59396, 0x47860275, 0x08077457, 0x47b17c54, 0x08395024, 0x47dcc9f9, 0x086b26de, +0x4807eb4b, 0x089cf867, 0x4832e02d, 0x08cec4a0, 0x485da887, 0x09008b6a, 0x4888443d, 0x09324ca7, +0x48b2b335, 0x09640837, 0x48dcf556, 0x0995bdfd, 0x49070a84, 0x09c76dd8, 0x4930f2a6, 0x09f917ac, +0x495aada2, 0x0a2abb59, 0x49843b5f, 0x0a5c58c0, 0x49ad9bc2, 0x0a8defc3, 0x49d6ceb3, 0x0abf8043, +0x49ffd417, 0x0af10a22, 0x4a28abd6, 0x0b228d42, 0x4a5155d6, 0x0b540982, 0x4a79d1ff, 0x0b857ec7, +0x4aa22036, 0x0bb6ecef, 0x4aca4065, 0x0be853de, 0x4af23270, 0x0c19b374, 0x4b19f641, 0x0c4b0b94, +0x4b418bbe, 0x0c7c5c1e, 0x4b68f2cf, 0x0cada4f5, 0x4b902b5c, 0x0cdee5f9, 0x4bb7354d, 0x0d101f0e, +0x4bde1089, 0x0d415013, 0x4c04bcf8, 0x0d7278eb, 0x4c2b3a84, 0x0da39978, 0x4c518913, 0x0dd4b19a, +0x4c77a88e, 0x0e05c135, 0x4c9d98de, 0x0e36c82a, 0x4cc359ec, 0x0e67c65a, 0x4ce8eb9f, 0x0e98bba7, +0x4d0e4de2, 0x0ec9a7f3, 0x4d33809c, 0x0efa8b20, 0x4d5883b7, 0x0f2b650f, 0x4d7d571c, 0x0f5c35a3, +0x4da1fab5, 0x0f8cfcbe, 0x4dc66e6a, 0x0fbdba40, 0x4deab226, 0x0fee6e0d, 0x4e0ec5d1, 0x101f1807, +0x4e32a956, 0x104fb80e, 0x4e565c9f, 0x10804e06, 0x4e79df95, 0x10b0d9d0, 0x4e9d3222, 0x10e15b4e, +0x4ec05432, 0x1111d263, 0x4ee345ad, 0x11423ef0, 0x4f06067f, 0x1172a0d7, 0x4f289692, 0x11a2f7fc, +0x4f4af5d1, 0x11d3443f, 0x4f6d2427, 0x12038584, 0x4f8f217e, 0x1233bbac, 0x4fb0edc1, 0x1263e699, +0x4fd288dc, 0x1294062f, 0x4ff3f2bb, 0x12c41a4f, 0x50152b47, 0x12f422db, 0x5036326e, 0x13241fb6, +0x50570819, 0x135410c3, 0x5077ac37, 0x1383f5e3, 0x50981eb1, 0x13b3cefa, 0x50b85f74, 0x13e39be9, +0x50d86e6d, 0x14135c94, 0x50f84b87, 0x144310dd, 0x5117f6ae, 0x1472b8a5, 0x51376fd0, 0x14a253d1, +0x5156b6d9, 0x14d1e242, 0x5175cbb5, 0x150163dc, 0x5194ae52, 0x1530d881, 0x51b35e9b, 0x15604013, +0x51d1dc80, 0x158f9a76, 0x51f027eb, 0x15bee78c, 0x520e40cc, 0x15ee2738, 0x522c270f, 0x161d595d, +0x5249daa2, 0x164c7ddd, 0x52675b72, 0x167b949d, 0x5284a96e, 0x16aa9d7e, 0x52a1c482, 0x16d99864, +0x52beac9f, 0x17088531, 0x52db61b0, 0x173763c9, 0x52f7e3a6, 0x1766340f, 0x5314326d, 0x1794f5e6, +0x53304df6, 0x17c3a931, 0x534c362d, 0x17f24dd3, 0x5367eb03, 0x1820e3b0, 0x53836c66, 0x184f6aab, +0x539eba45, 0x187de2a7, 0x53b9d48f, 0x18ac4b87, 0x53d4bb34, 0x18daa52f, 0x53ef6e23, 0x1908ef82, +0x5409ed4b, 0x19372a64, 0x5424389d, 0x196555b8, 0x543e5007, 0x19937161, 0x5458337a, 0x19c17d44, +0x5471e2e6, 0x19ef7944, 0x548b5e3b, 0x1a1d6544, 0x54a4a56a, 0x1a4b4128, 0x54bdb862, 0x1a790cd4, +0x54d69714, 0x1aa6c82b, 0x54ef4171, 0x1ad47312, 0x5507b76a, 0x1b020d6c, 0x551ff8ef, 0x1b2f971e, +0x553805f2, 0x1b5d100a, 0x554fde64, 0x1b8a7815, 0x55678236, 0x1bb7cf23, 0x557ef15a, 0x1be51518, +0x55962bc0, 0x1c1249d8, 0x55ad315b, 0x1c3f6d47, 0x55c4021d, 0x1c6c7f4a, 0x55da9df7, 0x1c997fc4, +0x55f104dc, 0x1cc66e99, 0x560736bd, 0x1cf34baf, 0x561d338d, 0x1d2016e9, 0x5632fb3f, 0x1d4cd02c, +0x56488dc5, 0x1d79775c, 0x565deb11, 0x1da60c5d, 0x56731317, 0x1dd28f15, 0x568805c9, 0x1dfeff67, +0x569cc31b, 0x1e2b5d38, 0x56b14b00, 0x1e57a86d, 0x56c59d6a, 0x1e83e0eb, 0x56d9ba4e, 0x1eb00696, +0x56eda1a0, 0x1edc1953, 0x57015352, 0x1f081907, 0x5714cf59, 0x1f340596, 0x572815a8, 0x1f5fdee6, +0x573b2635, 0x1f8ba4dc, 0x574e00f2, 0x1fb7575c, 0x5760a5d5, 0x1fe2f64c, 0x577314d2, 0x200e8190, +0x57854ddd, 0x2039f90f, 0x579750ec, 0x20655cac, 0x57a91df2, 0x2090ac4d, 0x57bab4e6, 0x20bbe7d8, +0x57cc15bc, 0x20e70f32, 0x57dd406a, 0x21122240, 0x57ee34e5, 0x213d20e8, 0x57fef323, 0x21680b0f, +0x580f7b19, 0x2192e09b, 0x581fccbc, 0x21bda171, 0x582fe804, 0x21e84d76, 0x583fcce6, 0x2212e492, +0x584f7b58, 0x223d66a8, 0x585ef351, 0x2267d3a0, 0x586e34c7, 0x22922b5e, 0x587d3fb0, 0x22bc6dca, +0x588c1404, 0x22e69ac8, 0x589ab1b9, 0x2310b23e, 0x58a918c6, 0x233ab414, 0x58b74923, 0x2364a02e, +0x58c542c5, 0x238e7673, 0x58d305a6, 0x23b836ca, 0x58e091bd, 0x23e1e117, 0x58ede700, 0x240b7543, +0x58fb0568, 0x2434f332, 0x5907eced, 0x245e5acc, 0x59149d87, 0x2487abf7, 0x5921172e, 0x24b0e699, +0x592d59da, 0x24da0a9a, 0x59396584, 0x250317df, 0x59453a24, 0x252c0e4f, 0x5950d7b3, 0x2554edd1, +0x595c3e2a, 0x257db64c, 0x59676d82, 0x25a667a7, 0x597265b4, 0x25cf01c8, 0x597d26b8, 0x25f78497, +0x5987b08a, 0x261feffa, 0x59920321, 0x264843d9, 0x599c1e78, 0x2670801a, 0x59a60288, 0x2698a4a6, +0x59afaf4c, 0x26c0b162, 0x59b924bc, 0x26e8a637, 0x59c262d5, 0x2710830c, 0x59cb698f, 0x273847c8, +0x59d438e5, 0x275ff452, 0x59dcd0d3, 0x27878893, 0x59e53151, 0x27af0472, 0x59ed5a5c, 0x27d667d5, +0x59f54bee, 0x27fdb2a7, 0x59fd0603, 0x2824e4cc, 0x5a048895, 0x284bfe2f, 0x5a0bd3a1, 0x2872feb6, +0x5a12e720, 0x2899e64a, 0x5a19c310, 0x28c0b4d2, 0x5a20676c, 0x28e76a37, 0x5a26d42f, 0x290e0661, +0x5a2d0957, 0x29348937, 0x5a3306de, 0x295af2a3, 0x5a38ccc2, 0x2981428c, 0x5a3e5afe, 0x29a778db, +0x5a43b190, 0x29cd9578, 0x5a48d074, 0x29f3984c, 0x5a4db7a6, 0x2a19813f, 0x5a526725, 0x2a3f503a, +0x5a56deec, 0x2a650525, 0x5a5b1efa, 0x2a8a9fea, 0x5a5f274b, 0x2ab02071, 0x5a62f7dd, 0x2ad586a3, +0x5a6690ae, 0x2afad269, 0x5a69f1bb, 0x2b2003ac, 0x5a6d1b03, 0x2b451a55, 0x5a700c84, 0x2b6a164d, +0x5a72c63b, 0x2b8ef77d, 0x5a754827, 0x2bb3bdce, 0x5a779246, 0x2bd8692b, 0x5a79a498, 0x2bfcf97c, +0x5a7b7f1a, 0x2c216eaa, 0x5a7d21cc, 0x2c45c8a0, 0x5a7e8cac, 0x2c6a0746, 0x5a7fbfbb, 0x2c8e2a87, +0x5a80baf6, 0x2cb2324c, 0x5a817e5d, 0x2cd61e7f, 0x5a8209f1, 0x2cf9ef09, 0x5a825db0, 0x2d1da3d5, +0x5a82799a, 0x2d413ccd, +}; + +const int sinWindowOffset[NUM_IMDCT_SIZES] PROGMEM = {0, 128}; + +/* Synthesis window - SIN + * format = Q31 for nmdct = [128, 1024] + * reordered for sequential access + * + * for (i = 0; i < nmdct/2; i++) { + * angle = (i + 0.5) * M_PI / (2.0 * nmdct); + * x = sin(angle); + * + * angle = (nmdct - 1 - i + 0.5) * M_PI / (2.0 * nmdct); + * x = sin(angle); + * } + */ +const int sinWindow[128 + 1024] PROGMEM = { +/* 128 - format = Q31 * 2^0 */ +0x00c90f88, 0x7fff6216, 0x025b26d7, 0x7ffa72d1, 0x03ed26e6, 0x7ff09478, 0x057f0035, 0x7fe1c76b, +0x0710a345, 0x7fce0c3e, 0x08a2009a, 0x7fb563b3, 0x0a3308bd, 0x7f97cebd, 0x0bc3ac35, 0x7f754e80, +0x0d53db92, 0x7f4de451, 0x0ee38766, 0x7f2191b4, 0x1072a048, 0x7ef05860, 0x120116d5, 0x7eba3a39, +0x138edbb1, 0x7e7f3957, 0x151bdf86, 0x7e3f57ff, 0x16a81305, 0x7dfa98a8, 0x183366e9, 0x7db0fdf8, +0x19bdcbf3, 0x7d628ac6, 0x1b4732ef, 0x7d0f4218, 0x1ccf8cb3, 0x7cb72724, 0x1e56ca1e, 0x7c5a3d50, +0x1fdcdc1b, 0x7bf88830, 0x2161b3a0, 0x7b920b89, 0x22e541af, 0x7b26cb4f, 0x24677758, 0x7ab6cba4, +0x25e845b6, 0x7a4210d8, 0x27679df4, 0x79c89f6e, 0x28e5714b, 0x794a7c12, 0x2a61b101, 0x78c7aba2, +0x2bdc4e6f, 0x78403329, 0x2d553afc, 0x77b417df, 0x2ecc681e, 0x77235f2d, 0x3041c761, 0x768e0ea6, +0x31b54a5e, 0x75f42c0b, 0x3326e2c3, 0x7555bd4c, 0x34968250, 0x74b2c884, 0x36041ad9, 0x740b53fb, +0x376f9e46, 0x735f6626, 0x38d8fe93, 0x72af05a7, 0x3a402dd2, 0x71fa3949, 0x3ba51e29, 0x71410805, +0x3d07c1d6, 0x708378ff, 0x3e680b2c, 0x6fc19385, 0x3fc5ec98, 0x6efb5f12, 0x4121589b, 0x6e30e34a, +0x427a41d0, 0x6d6227fa, 0x43d09aed, 0x6c8f351c, 0x452456bd, 0x6bb812d1, 0x46756828, 0x6adcc964, +0x47c3c22f, 0x69fd614a, 0x490f57ee, 0x6919e320, 0x4a581c9e, 0x683257ab, 0x4b9e0390, 0x6746c7d8, +0x4ce10034, 0x66573cbb, 0x4e210617, 0x6563bf92, 0x4f5e08e3, 0x646c59bf, 0x5097fc5e, 0x637114cc, +0x51ced46e, 0x6271fa69, 0x53028518, 0x616f146c, 0x5433027d, 0x60686ccf, 0x556040e2, 0x5f5e0db3, +0x568a34a9, 0x5e50015d, 0x57b0d256, 0x5d3e5237, 0x58d40e8c, 0x5c290acc, 0x59f3de12, 0x5b1035cf, +/* 1024 - format = Q31 * 2^0 */ +0x001921fb, 0x7ffffd88, 0x004b65ee, 0x7fffe9cb, 0x007da9d4, 0x7fffc251, 0x00afeda8, 0x7fff8719, +0x00e23160, 0x7fff3824, 0x011474f6, 0x7ffed572, 0x0146b860, 0x7ffe5f03, 0x0178fb99, 0x7ffdd4d7, +0x01ab3e97, 0x7ffd36ee, 0x01dd8154, 0x7ffc8549, 0x020fc3c6, 0x7ffbbfe6, 0x024205e8, 0x7ffae6c7, +0x027447b0, 0x7ff9f9ec, 0x02a68917, 0x7ff8f954, 0x02d8ca16, 0x7ff7e500, 0x030b0aa4, 0x7ff6bcf0, +0x033d4abb, 0x7ff58125, 0x036f8a51, 0x7ff4319d, 0x03a1c960, 0x7ff2ce5b, 0x03d407df, 0x7ff1575d, +0x040645c7, 0x7fefcca4, 0x04388310, 0x7fee2e30, 0x046abfb3, 0x7fec7c02, 0x049cfba7, 0x7feab61a, +0x04cf36e5, 0x7fe8dc78, 0x05017165, 0x7fe6ef1c, 0x0533ab20, 0x7fe4ee06, 0x0565e40d, 0x7fe2d938, +0x05981c26, 0x7fe0b0b1, 0x05ca5361, 0x7fde7471, 0x05fc89b8, 0x7fdc247a, 0x062ebf22, 0x7fd9c0ca, +0x0660f398, 0x7fd74964, 0x06932713, 0x7fd4be46, 0x06c5598a, 0x7fd21f72, 0x06f78af6, 0x7fcf6ce8, +0x0729bb4e, 0x7fcca6a7, 0x075bea8c, 0x7fc9ccb2, 0x078e18a7, 0x7fc6df08, 0x07c04598, 0x7fc3dda9, +0x07f27157, 0x7fc0c896, 0x08249bdd, 0x7fbd9fd0, 0x0856c520, 0x7fba6357, 0x0888ed1b, 0x7fb7132b, +0x08bb13c5, 0x7fb3af4e, 0x08ed3916, 0x7fb037bf, 0x091f5d06, 0x7facac7f, 0x09517f8f, 0x7fa90d8e, +0x0983a0a7, 0x7fa55aee, 0x09b5c048, 0x7fa1949e, 0x09e7de6a, 0x7f9dbaa0, 0x0a19fb04, 0x7f99ccf4, +0x0a4c1610, 0x7f95cb9a, 0x0a7e2f85, 0x7f91b694, 0x0ab0475c, 0x7f8d8de1, 0x0ae25d8d, 0x7f895182, +0x0b147211, 0x7f850179, 0x0b4684df, 0x7f809dc5, 0x0b7895f0, 0x7f7c2668, 0x0baaa53b, 0x7f779b62, +0x0bdcb2bb, 0x7f72fcb4, 0x0c0ebe66, 0x7f6e4a5e, 0x0c40c835, 0x7f698461, 0x0c72d020, 0x7f64aabf, +0x0ca4d620, 0x7f5fbd77, 0x0cd6da2d, 0x7f5abc8a, 0x0d08dc3f, 0x7f55a7fa, 0x0d3adc4e, 0x7f507fc7, +0x0d6cda53, 0x7f4b43f2, 0x0d9ed646, 0x7f45f47b, 0x0dd0d01f, 0x7f409164, 0x0e02c7d7, 0x7f3b1aad, +0x0e34bd66, 0x7f359057, 0x0e66b0c3, 0x7f2ff263, 0x0e98a1e9, 0x7f2a40d2, 0x0eca90ce, 0x7f247ba5, +0x0efc7d6b, 0x7f1ea2dc, 0x0f2e67b8, 0x7f18b679, 0x0f604faf, 0x7f12b67c, 0x0f923546, 0x7f0ca2e7, +0x0fc41876, 0x7f067bba, 0x0ff5f938, 0x7f0040f6, 0x1027d784, 0x7ef9f29d, 0x1059b352, 0x7ef390ae, +0x108b8c9b, 0x7eed1b2c, 0x10bd6356, 0x7ee69217, 0x10ef377d, 0x7edff570, 0x11210907, 0x7ed94538, +0x1152d7ed, 0x7ed28171, 0x1184a427, 0x7ecbaa1a, 0x11b66dad, 0x7ec4bf36, 0x11e83478, 0x7ebdc0c6, +0x1219f880, 0x7eb6aeca, 0x124bb9be, 0x7eaf8943, 0x127d7829, 0x7ea85033, 0x12af33ba, 0x7ea1039b, +0x12e0ec6a, 0x7e99a37c, 0x1312a230, 0x7e922fd6, 0x13445505, 0x7e8aa8ac, 0x137604e2, 0x7e830dff, +0x13a7b1bf, 0x7e7b5fce, 0x13d95b93, 0x7e739e1d, 0x140b0258, 0x7e6bc8eb, 0x143ca605, 0x7e63e03b, +0x146e4694, 0x7e5be40c, 0x149fe3fc, 0x7e53d462, 0x14d17e36, 0x7e4bb13c, 0x1503153a, 0x7e437a9c, +0x1534a901, 0x7e3b3083, 0x15663982, 0x7e32d2f4, 0x1597c6b7, 0x7e2a61ed, 0x15c95097, 0x7e21dd73, +0x15fad71b, 0x7e194584, 0x162c5a3b, 0x7e109a24, 0x165dd9f0, 0x7e07db52, 0x168f5632, 0x7dff0911, +0x16c0cef9, 0x7df62362, 0x16f2443e, 0x7ded2a47, 0x1723b5f9, 0x7de41dc0, 0x17552422, 0x7ddafdce, +0x17868eb3, 0x7dd1ca75, 0x17b7f5a3, 0x7dc883b4, 0x17e958ea, 0x7dbf298d, 0x181ab881, 0x7db5bc02, +0x184c1461, 0x7dac3b15, 0x187d6c82, 0x7da2a6c6, 0x18aec0db, 0x7d98ff17, 0x18e01167, 0x7d8f4409, +0x19115e1c, 0x7d85759f, 0x1942a6f3, 0x7d7b93da, 0x1973ebe6, 0x7d719eba, 0x19a52ceb, 0x7d679642, +0x19d669fc, 0x7d5d7a74, 0x1a07a311, 0x7d534b50, 0x1a38d823, 0x7d4908d9, 0x1a6a0929, 0x7d3eb30f, +0x1a9b361d, 0x7d3449f5, 0x1acc5ef6, 0x7d29cd8c, 0x1afd83ad, 0x7d1f3dd6, 0x1b2ea43a, 0x7d149ad5, +0x1b5fc097, 0x7d09e489, 0x1b90d8bb, 0x7cff1af5, 0x1bc1ec9e, 0x7cf43e1a, 0x1bf2fc3a, 0x7ce94dfb, +0x1c240786, 0x7cde4a98, 0x1c550e7c, 0x7cd333f3, 0x1c861113, 0x7cc80a0f, 0x1cb70f43, 0x7cbcccec, +0x1ce80906, 0x7cb17c8d, 0x1d18fe54, 0x7ca618f3, 0x1d49ef26, 0x7c9aa221, 0x1d7adb73, 0x7c8f1817, +0x1dabc334, 0x7c837ad8, 0x1ddca662, 0x7c77ca65, 0x1e0d84f5, 0x7c6c06c0, 0x1e3e5ee5, 0x7c602fec, +0x1e6f342c, 0x7c5445e9, 0x1ea004c1, 0x7c4848ba, 0x1ed0d09d, 0x7c3c3860, 0x1f0197b8, 0x7c3014de, +0x1f325a0b, 0x7c23de35, 0x1f63178f, 0x7c179467, 0x1f93d03c, 0x7c0b3777, 0x1fc4840a, 0x7bfec765, +0x1ff532f2, 0x7bf24434, 0x2025dcec, 0x7be5ade6, 0x205681f1, 0x7bd9047c, 0x208721f9, 0x7bcc47fa, +0x20b7bcfe, 0x7bbf7860, 0x20e852f6, 0x7bb295b0, 0x2118e3dc, 0x7ba59fee, 0x21496fa7, 0x7b989719, +0x2179f64f, 0x7b8b7b36, 0x21aa77cf, 0x7b7e4c45, 0x21daf41d, 0x7b710a49, 0x220b6b32, 0x7b63b543, +0x223bdd08, 0x7b564d36, 0x226c4996, 0x7b48d225, 0x229cb0d5, 0x7b3b4410, 0x22cd12bd, 0x7b2da2fa, +0x22fd6f48, 0x7b1feee5, 0x232dc66d, 0x7b1227d3, 0x235e1826, 0x7b044dc7, 0x238e646a, 0x7af660c2, +0x23beab33, 0x7ae860c7, 0x23eeec78, 0x7ada4dd8, 0x241f2833, 0x7acc27f7, 0x244f5e5c, 0x7abdef25, +0x247f8eec, 0x7aafa367, 0x24afb9da, 0x7aa144bc, 0x24dfdf20, 0x7a92d329, 0x250ffeb7, 0x7a844eae, +0x25401896, 0x7a75b74f, 0x25702cb7, 0x7a670d0d, 0x25a03b11, 0x7a584feb, 0x25d0439f, 0x7a497feb, +0x26004657, 0x7a3a9d0f, 0x26304333, 0x7a2ba75a, 0x26603a2c, 0x7a1c9ece, 0x26902b39, 0x7a0d836d, +0x26c01655, 0x79fe5539, 0x26effb76, 0x79ef1436, 0x271fda96, 0x79dfc064, 0x274fb3ae, 0x79d059c8, +0x277f86b5, 0x79c0e062, 0x27af53a6, 0x79b15435, 0x27df1a77, 0x79a1b545, 0x280edb23, 0x79920392, +0x283e95a1, 0x79823f20, 0x286e49ea, 0x797267f2, 0x289df7f8, 0x79627e08, 0x28cd9fc1, 0x79528167, +0x28fd4140, 0x79427210, 0x292cdc6d, 0x79325006, 0x295c7140, 0x79221b4b, 0x298bffb2, 0x7911d3e2, +0x29bb87bc, 0x790179cd, 0x29eb0957, 0x78f10d0f, 0x2a1a847b, 0x78e08dab, 0x2a49f920, 0x78cffba3, +0x2a796740, 0x78bf56f9, 0x2aa8ced3, 0x78ae9fb0, 0x2ad82fd2, 0x789dd5cb, 0x2b078a36, 0x788cf94c, +0x2b36ddf7, 0x787c0a36, 0x2b662b0e, 0x786b088c, 0x2b957173, 0x7859f44f, 0x2bc4b120, 0x7848cd83, +0x2bf3ea0d, 0x7837942b, 0x2c231c33, 0x78264849, 0x2c52478a, 0x7814e9df, 0x2c816c0c, 0x780378f1, +0x2cb089b1, 0x77f1f581, 0x2cdfa071, 0x77e05f91, 0x2d0eb046, 0x77ceb725, 0x2d3db928, 0x77bcfc3f, +0x2d6cbb10, 0x77ab2ee2, 0x2d9bb5f6, 0x77994f11, 0x2dcaa9d5, 0x77875cce, 0x2df996a3, 0x7775581d, +0x2e287c5a, 0x776340ff, 0x2e575af3, 0x77511778, 0x2e863267, 0x773edb8b, 0x2eb502ae, 0x772c8d3a, +0x2ee3cbc1, 0x771a2c88, 0x2f128d99, 0x7707b979, 0x2f41482e, 0x76f5340e, 0x2f6ffb7a, 0x76e29c4b, +0x2f9ea775, 0x76cff232, 0x2fcd4c19, 0x76bd35c7, 0x2ffbe95d, 0x76aa670d, 0x302a7f3a, 0x76978605, +0x30590dab, 0x768492b4, 0x308794a6, 0x76718d1c, 0x30b61426, 0x765e7540, 0x30e48c22, 0x764b4b23, +0x3112fc95, 0x76380ec8, 0x31416576, 0x7624c031, 0x316fc6be, 0x76115f63, 0x319e2067, 0x75fdec60, +0x31cc7269, 0x75ea672a, 0x31fabcbd, 0x75d6cfc5, 0x3228ff5c, 0x75c32634, 0x32573a3f, 0x75af6a7b, +0x32856d5e, 0x759b9c9b, 0x32b398b3, 0x7587bc98, 0x32e1bc36, 0x7573ca75, 0x330fd7e1, 0x755fc635, +0x333debab, 0x754bafdc, 0x336bf78f, 0x7537876c, 0x3399fb85, 0x75234ce8, 0x33c7f785, 0x750f0054, +0x33f5eb89, 0x74faa1b3, 0x3423d78a, 0x74e63108, 0x3451bb81, 0x74d1ae55, 0x347f9766, 0x74bd199f, +0x34ad6b32, 0x74a872e8, 0x34db36df, 0x7493ba34, 0x3508fa66, 0x747eef85, 0x3536b5be, 0x746a12df, +0x356468e2, 0x74552446, 0x359213c9, 0x744023bc, 0x35bfb66e, 0x742b1144, 0x35ed50c9, 0x7415ece2, +0x361ae2d3, 0x7400b69a, 0x36486c86, 0x73eb6e6e, 0x3675edd9, 0x73d61461, 0x36a366c6, 0x73c0a878, +0x36d0d746, 0x73ab2ab4, 0x36fe3f52, 0x73959b1b, 0x372b9ee3, 0x737ff9ae, 0x3758f5f2, 0x736a4671, +0x37864477, 0x73548168, 0x37b38a6d, 0x733eaa96, 0x37e0c7cc, 0x7328c1ff, 0x380dfc8d, 0x7312c7a5, +0x383b28a9, 0x72fcbb8c, 0x38684c19, 0x72e69db7, 0x389566d6, 0x72d06e2b, 0x38c278d9, 0x72ba2cea, +0x38ef821c, 0x72a3d9f7, 0x391c8297, 0x728d7557, 0x39497a43, 0x7276ff0d, 0x39766919, 0x7260771b, +0x39a34f13, 0x7249dd86, 0x39d02c2a, 0x72333251, 0x39fd0056, 0x721c7580, 0x3a29cb91, 0x7205a716, +0x3a568dd4, 0x71eec716, 0x3a834717, 0x71d7d585, 0x3aaff755, 0x71c0d265, 0x3adc9e86, 0x71a9bdba, +0x3b093ca3, 0x71929789, 0x3b35d1a5, 0x717b5fd3, 0x3b625d86, 0x7164169d, 0x3b8ee03e, 0x714cbbeb, +0x3bbb59c7, 0x71354fc0, 0x3be7ca1a, 0x711dd220, 0x3c143130, 0x7106430e, 0x3c408f03, 0x70eea28e, +0x3c6ce38a, 0x70d6f0a4, 0x3c992ec0, 0x70bf2d53, 0x3cc5709e, 0x70a7589f, 0x3cf1a91c, 0x708f728b, +0x3d1dd835, 0x70777b1c, 0x3d49fde1, 0x705f7255, 0x3d761a19, 0x70475839, 0x3da22cd7, 0x702f2ccd, +0x3dce3614, 0x7016f014, 0x3dfa35c8, 0x6ffea212, 0x3e262bee, 0x6fe642ca, 0x3e52187f, 0x6fcdd241, +0x3e7dfb73, 0x6fb5507a, 0x3ea9d4c3, 0x6f9cbd79, 0x3ed5a46b, 0x6f841942, 0x3f016a61, 0x6f6b63d8, +0x3f2d26a0, 0x6f529d40, 0x3f58d921, 0x6f39c57d, 0x3f8481dd, 0x6f20dc92, 0x3fb020ce, 0x6f07e285, +0x3fdbb5ec, 0x6eeed758, 0x40074132, 0x6ed5bb10, 0x4032c297, 0x6ebc8db0, 0x405e3a16, 0x6ea34f3d, +0x4089a7a8, 0x6e89ffb9, 0x40b50b46, 0x6e709f2a, 0x40e064ea, 0x6e572d93, 0x410bb48c, 0x6e3daaf8, +0x4136fa27, 0x6e24175c, 0x416235b2, 0x6e0a72c5, 0x418d6729, 0x6df0bd35, 0x41b88e84, 0x6dd6f6b1, +0x41e3abbc, 0x6dbd1f3c, 0x420ebecb, 0x6da336dc, 0x4239c7aa, 0x6d893d93, 0x4264c653, 0x6d6f3365, +0x428fbabe, 0x6d551858, 0x42baa4e6, 0x6d3aec6e, 0x42e584c3, 0x6d20afac, 0x43105a50, 0x6d066215, +0x433b2585, 0x6cec03af, 0x4365e65b, 0x6cd1947c, 0x43909ccd, 0x6cb71482, 0x43bb48d4, 0x6c9c83c3, +0x43e5ea68, 0x6c81e245, 0x44108184, 0x6c67300b, 0x443b0e21, 0x6c4c6d1a, 0x44659039, 0x6c319975, +0x449007c4, 0x6c16b521, 0x44ba74bd, 0x6bfbc021, 0x44e4d71c, 0x6be0ba7b, 0x450f2edb, 0x6bc5a431, +0x45397bf4, 0x6baa7d49, 0x4563be60, 0x6b8f45c7, 0x458df619, 0x6b73fdae, 0x45b82318, 0x6b58a503, +0x45e24556, 0x6b3d3bcb, 0x460c5cce, 0x6b21c208, 0x46366978, 0x6b0637c1, 0x46606b4e, 0x6aea9cf8, +0x468a624a, 0x6acef1b2, 0x46b44e65, 0x6ab335f4, 0x46de2f99, 0x6a9769c1, 0x470805df, 0x6a7b8d1e, +0x4731d131, 0x6a5fa010, 0x475b9188, 0x6a43a29a, 0x478546de, 0x6a2794c1, 0x47aef12c, 0x6a0b7689, +0x47d8906d, 0x69ef47f6, 0x48022499, 0x69d3090e, 0x482badab, 0x69b6b9d3, 0x48552b9b, 0x699a5a4c, +0x487e9e64, 0x697dea7b, 0x48a805ff, 0x69616a65, 0x48d16265, 0x6944da10, 0x48fab391, 0x6928397e, +0x4923f97b, 0x690b88b5, 0x494d341e, 0x68eec7b9, 0x49766373, 0x68d1f68f, 0x499f8774, 0x68b5153a, +0x49c8a01b, 0x689823bf, 0x49f1ad61, 0x687b2224, 0x4a1aaf3f, 0x685e106c, 0x4a43a5b0, 0x6840ee9b, +0x4a6c90ad, 0x6823bcb7, 0x4a957030, 0x68067ac3, 0x4abe4433, 0x67e928c5, 0x4ae70caf, 0x67cbc6c0, +0x4b0fc99d, 0x67ae54ba, 0x4b387af9, 0x6790d2b6, 0x4b6120bb, 0x677340ba, 0x4b89badd, 0x67559eca, +0x4bb24958, 0x6737ecea, 0x4bdacc28, 0x671a2b20, 0x4c034345, 0x66fc596f, 0x4c2baea9, 0x66de77dc, +0x4c540e4e, 0x66c0866d, 0x4c7c622d, 0x66a28524, 0x4ca4aa41, 0x66847408, 0x4ccce684, 0x6666531d, +0x4cf516ee, 0x66482267, 0x4d1d3b7a, 0x6629e1ec, 0x4d455422, 0x660b91af, 0x4d6d60df, 0x65ed31b5, +0x4d9561ac, 0x65cec204, 0x4dbd5682, 0x65b0429f, 0x4de53f5a, 0x6591b38c, 0x4e0d1c30, 0x657314cf, +0x4e34ecfc, 0x6554666d, 0x4e5cb1b9, 0x6535a86b, 0x4e846a60, 0x6516dacd, 0x4eac16eb, 0x64f7fd98, +0x4ed3b755, 0x64d910d1, 0x4efb4b96, 0x64ba147d, 0x4f22d3aa, 0x649b08a0, 0x4f4a4f89, 0x647bed3f, +0x4f71bf2e, 0x645cc260, 0x4f992293, 0x643d8806, 0x4fc079b1, 0x641e3e38, 0x4fe7c483, 0x63fee4f8, +0x500f0302, 0x63df7c4d, 0x50363529, 0x63c0043b, 0x505d5af1, 0x63a07cc7, 0x50847454, 0x6380e5f6, +0x50ab814d, 0x63613fcd, 0x50d281d5, 0x63418a50, 0x50f975e6, 0x6321c585, 0x51205d7b, 0x6301f171, +0x5147388c, 0x62e20e17, 0x516e0715, 0x62c21b7e, 0x5194c910, 0x62a219aa, 0x51bb7e75, 0x628208a1, +0x51e22740, 0x6261e866, 0x5208c36a, 0x6241b8ff, 0x522f52ee, 0x62217a72, 0x5255d5c5, 0x62012cc2, +0x527c4bea, 0x61e0cff5, 0x52a2b556, 0x61c06410, 0x52c91204, 0x619fe918, 0x52ef61ee, 0x617f5f12, +0x5315a50e, 0x615ec603, 0x533bdb5d, 0x613e1df0, 0x536204d7, 0x611d66de, 0x53882175, 0x60fca0d2, +0x53ae3131, 0x60dbcbd1, 0x53d43406, 0x60bae7e1, 0x53fa29ed, 0x6099f505, 0x542012e1, 0x6078f344, +0x5445eedb, 0x6057e2a2, 0x546bbdd7, 0x6036c325, 0x54917fce, 0x601594d1, 0x54b734ba, 0x5ff457ad, +0x54dcdc96, 0x5fd30bbc, 0x5502775c, 0x5fb1b104, 0x55280505, 0x5f90478a, 0x554d858d, 0x5f6ecf53, +0x5572f8ed, 0x5f4d4865, 0x55985f20, 0x5f2bb2c5, 0x55bdb81f, 0x5f0a0e77, 0x55e303e6, 0x5ee85b82, +0x5608426e, 0x5ec699e9, 0x562d73b2, 0x5ea4c9b3, 0x565297ab, 0x5e82eae5, 0x5677ae54, 0x5e60fd84, +0x569cb7a8, 0x5e3f0194, 0x56c1b3a1, 0x5e1cf71c, 0x56e6a239, 0x5dfade20, 0x570b8369, 0x5dd8b6a7, +0x5730572e, 0x5db680b4, 0x57551d80, 0x5d943c4e, 0x5779d65b, 0x5d71e979, 0x579e81b8, 0x5d4f883b, +0x57c31f92, 0x5d2d189a, 0x57e7afe4, 0x5d0a9a9a, 0x580c32a7, 0x5ce80e41, 0x5830a7d6, 0x5cc57394, +0x58550f6c, 0x5ca2ca99, 0x58796962, 0x5c801354, 0x589db5b3, 0x5c5d4dcc, 0x58c1f45b, 0x5c3a7a05, +0x58e62552, 0x5c179806, 0x590a4893, 0x5bf4a7d2, 0x592e5e19, 0x5bd1a971, 0x595265df, 0x5bae9ce7, +0x59765fde, 0x5b8b8239, 0x599a4c12, 0x5b68596d, 0x59be2a74, 0x5b452288, 0x59e1faff, 0x5b21dd90, +0x5a05bdae, 0x5afe8a8b, 0x5a29727b, 0x5adb297d, 0x5a4d1960, 0x5ab7ba6c, 0x5a70b258, 0x5a943d5e, +}; + +const int kbdWindowOffset[NUM_IMDCT_SIZES] PROGMEM = {0, 128}; + +/* Synthesis window - KBD + * format = Q31 for nmdct = [128, 1024] + * reordered for sequential access + * + * aacScaleFact = -sqrt(1.0 / (2.0 * nmdct)); + * for (i = 0; i < nmdct/2; i++) { + * x = kbdWindowRef[i] * aacScaleFact; + * x = kbdWindowRef[nmdct - 1 - i] * aacScaleFact; + * } + * Note: see below for code to generate kbdWindowRef[] + */ +const int kbdWindow[128 + 1024] PROGMEM = { +/* 128 - format = Q31 * 2^0 */ +0x00016f63, 0x7ffffffe, 0x0003e382, 0x7ffffff1, 0x00078f64, 0x7fffffc7, 0x000cc323, 0x7fffff5d, +0x0013d9ed, 0x7ffffe76, 0x001d3a9d, 0x7ffffcaa, 0x0029581f, 0x7ffff953, 0x0038b1bd, 0x7ffff372, +0x004bd34d, 0x7fffe98b, 0x00635538, 0x7fffd975, 0x007fdc64, 0x7fffc024, 0x00a219f1, 0x7fff995b, +0x00cacad0, 0x7fff5f5b, 0x00fab72d, 0x7fff0a75, 0x0132b1af, 0x7ffe9091, 0x01739689, 0x7ffde49e, +0x01be4a63, 0x7ffcf5ef, 0x0213b910, 0x7ffbaf84, 0x0274d41e, 0x7ff9f73a, 0x02e2913a, 0x7ff7acf1, +0x035de86c, 0x7ff4a99a, 0x03e7d233, 0x7ff0be3d, 0x0481457c, 0x7febb2f1, 0x052b357c, 0x7fe545d4, +0x05e68f77, 0x7fdd2a02, 0x06b4386f, 0x7fd30695, 0x07950acb, 0x7fc675b4, 0x0889d3ef, 0x7fb703be, +0x099351e0, 0x7fa42e89, 0x0ab230e0, 0x7f8d64d8, 0x0be70923, 0x7f7205f8, 0x0d325c93, 0x7f516195, +0x0e9494ae, 0x7f2ab7d0, 0x100e0085, 0x7efd3997, 0x119ed2ef, 0x7ec8094a, 0x134720d8, 0x7e8a3ba7, +0x1506dfdc, 0x7e42d906, 0x16dde50b, 0x7df0dee4, 0x18cbe3f7, 0x7d9341b4, 0x1ad06e07, 0x7d28ef02, +0x1ceaf215, 0x7cb0cfcc, 0x1f1abc4f, 0x7c29cb20, 0x215ef677, 0x7b92c8eb, 0x23b6a867, 0x7aeab4ec, +0x2620b8ec, 0x7a3081d0, 0x289beef5, 0x79632c5a, 0x2b26f30b, 0x7881be95, 0x2dc0511f, 0x778b5304, +0x30667aa2, 0x767f17c0, 0x3317c8dd, 0x755c5178, 0x35d27f98, 0x74225e50, 0x3894cff3, 0x72d0b887, +0x3b5cdb7b, 0x7166f8e7, 0x3e28b770, 0x6fe4d8e8, 0x40f6702a, 0x6e4a3491, 0x43c40caa, 0x6c970bfc, +0x468f9231, 0x6acb8483, 0x495707f5, 0x68e7e994, 0x4c187ac7, 0x66ecad1c, 0x4ed200c5, 0x64da6797, +0x5181bcea, 0x62b1d7b7, 0x5425e28e, 0x6073e1ae, 0x56bcb8c2, 0x5e218e16, 0x59449d76, 0x5bbc0875, +/* 1024 - format = Q31 * 2^0 */ +0x0009962f, 0x7fffffa4, 0x000e16fb, 0x7fffff39, 0x0011ea65, 0x7ffffebf, 0x0015750e, 0x7ffffe34, +0x0018dc74, 0x7ffffd96, 0x001c332e, 0x7ffffce5, 0x001f83f5, 0x7ffffc1f, 0x0022d59a, 0x7ffffb43, +0x00262cc2, 0x7ffffa4f, 0x00298cc4, 0x7ffff942, 0x002cf81f, 0x7ffff81a, 0x003070c4, 0x7ffff6d6, +0x0033f840, 0x7ffff573, 0x00378fd9, 0x7ffff3f1, 0x003b38a1, 0x7ffff24d, 0x003ef381, 0x7ffff085, +0x0042c147, 0x7fffee98, 0x0046a2a8, 0x7fffec83, 0x004a9847, 0x7fffea44, 0x004ea2b7, 0x7fffe7d8, +0x0052c283, 0x7fffe53f, 0x0056f829, 0x7fffe274, 0x005b4422, 0x7fffdf76, 0x005fa6dd, 0x7fffdc43, +0x006420c8, 0x7fffd8d6, 0x0068b249, 0x7fffd52f, 0x006d5bc4, 0x7fffd149, 0x00721d9a, 0x7fffcd22, +0x0076f828, 0x7fffc8b6, 0x007bebca, 0x7fffc404, 0x0080f8d9, 0x7fffbf06, 0x00861fae, 0x7fffb9bb, +0x008b609e, 0x7fffb41e, 0x0090bbff, 0x7fffae2c, 0x00963224, 0x7fffa7e1, 0x009bc362, 0x7fffa13a, +0x00a17009, 0x7fff9a32, 0x00a7386c, 0x7fff92c5, 0x00ad1cdc, 0x7fff8af0, 0x00b31da8, 0x7fff82ad, +0x00b93b21, 0x7fff79f9, 0x00bf7596, 0x7fff70cf, 0x00c5cd57, 0x7fff672a, 0x00cc42b1, 0x7fff5d05, +0x00d2d5f3, 0x7fff525c, 0x00d9876c, 0x7fff4729, 0x00e05769, 0x7fff3b66, 0x00e74638, 0x7fff2f10, +0x00ee5426, 0x7fff221f, 0x00f58182, 0x7fff148e, 0x00fcce97, 0x7fff0658, 0x01043bb3, 0x7ffef776, +0x010bc923, 0x7ffee7e2, 0x01137733, 0x7ffed795, 0x011b4631, 0x7ffec68a, 0x01233669, 0x7ffeb4ba, +0x012b4827, 0x7ffea21d, 0x01337bb8, 0x7ffe8eac, 0x013bd167, 0x7ffe7a61, 0x01444982, 0x7ffe6533, +0x014ce454, 0x7ffe4f1c, 0x0155a229, 0x7ffe3813, 0x015e834d, 0x7ffe2011, 0x0167880c, 0x7ffe070d, +0x0170b0b2, 0x7ffdecff, 0x0179fd8b, 0x7ffdd1df, 0x01836ee1, 0x7ffdb5a2, 0x018d0500, 0x7ffd9842, +0x0196c035, 0x7ffd79b3, 0x01a0a0ca, 0x7ffd59ee, 0x01aaa70a, 0x7ffd38e8, 0x01b4d341, 0x7ffd1697, +0x01bf25b9, 0x7ffcf2f2, 0x01c99ebd, 0x7ffccdee, 0x01d43e99, 0x7ffca780, 0x01df0597, 0x7ffc7f9e, +0x01e9f401, 0x7ffc563d, 0x01f50a22, 0x7ffc2b51, 0x02004844, 0x7ffbfecf, 0x020baeb1, 0x7ffbd0ab, +0x02173db4, 0x7ffba0da, 0x0222f596, 0x7ffb6f4f, 0x022ed6a1, 0x7ffb3bfd, 0x023ae11f, 0x7ffb06d8, +0x02471558, 0x7ffacfd3, 0x02537397, 0x7ffa96e0, 0x025ffc25, 0x7ffa5bf2, 0x026caf4a, 0x7ffa1efc, +0x02798d4f, 0x7ff9dfee, 0x0286967c, 0x7ff99ebb, 0x0293cb1b, 0x7ff95b55, 0x02a12b72, 0x7ff915ab, +0x02aeb7cb, 0x7ff8cdaf, 0x02bc706d, 0x7ff88351, 0x02ca559f, 0x7ff83682, 0x02d867a9, 0x7ff7e731, +0x02e6a6d2, 0x7ff7954e, 0x02f51361, 0x7ff740c8, 0x0303ad9c, 0x7ff6e98e, 0x031275ca, 0x7ff68f8f, +0x03216c30, 0x7ff632ba, 0x03309116, 0x7ff5d2fb, 0x033fe4bf, 0x7ff57042, 0x034f6773, 0x7ff50a7a, +0x035f1975, 0x7ff4a192, 0x036efb0a, 0x7ff43576, 0x037f0c78, 0x7ff3c612, 0x038f4e02, 0x7ff35353, +0x039fbfeb, 0x7ff2dd24, 0x03b06279, 0x7ff26370, 0x03c135ed, 0x7ff1e623, 0x03d23a8b, 0x7ff16527, +0x03e37095, 0x7ff0e067, 0x03f4d84e, 0x7ff057cc, 0x040671f7, 0x7fefcb40, 0x04183dd3, 0x7fef3aad, +0x042a3c22, 0x7feea5fa, 0x043c6d25, 0x7fee0d11, 0x044ed11d, 0x7fed6fda, 0x04616849, 0x7fecce3d, +0x047432eb, 0x7fec2821, 0x04873140, 0x7feb7d6c, 0x049a6388, 0x7feace07, 0x04adca01, 0x7fea19d6, +0x04c164ea, 0x7fe960c0, 0x04d53481, 0x7fe8a2aa, 0x04e93902, 0x7fe7df79, 0x04fd72aa, 0x7fe71712, +0x0511e1b6, 0x7fe6495a, 0x05268663, 0x7fe57634, 0x053b60eb, 0x7fe49d83, 0x05507189, 0x7fe3bf2b, +0x0565b879, 0x7fe2db0f, 0x057b35f4, 0x7fe1f110, 0x0590ea35, 0x7fe10111, 0x05a6d574, 0x7fe00af3, +0x05bcf7ea, 0x7fdf0e97, 0x05d351cf, 0x7fde0bdd, 0x05e9e35c, 0x7fdd02a6, 0x0600acc8, 0x7fdbf2d2, +0x0617ae48, 0x7fdadc40, 0x062ee814, 0x7fd9becf, 0x06465a62, 0x7fd89a5e, 0x065e0565, 0x7fd76eca, +0x0675e954, 0x7fd63bf1, 0x068e0662, 0x7fd501b0, 0x06a65cc3, 0x7fd3bfe4, 0x06beecaa, 0x7fd2766a, +0x06d7b648, 0x7fd1251e, 0x06f0b9d1, 0x7fcfcbda, 0x0709f775, 0x7fce6a7a, 0x07236f65, 0x7fcd00d8, +0x073d21d2, 0x7fcb8ecf, 0x07570eea, 0x7fca1439, 0x077136dd, 0x7fc890ed, 0x078b99da, 0x7fc704c7, +0x07a6380d, 0x7fc56f9d, 0x07c111a4, 0x7fc3d147, 0x07dc26cc, 0x7fc2299e, 0x07f777b1, 0x7fc07878, +0x0813047d, 0x7fbebdac, 0x082ecd5b, 0x7fbcf90f, 0x084ad276, 0x7fbb2a78, 0x086713f7, 0x7fb951bc, +0x08839206, 0x7fb76eaf, 0x08a04ccb, 0x7fb58126, 0x08bd446e, 0x7fb388f4, 0x08da7915, 0x7fb185ee, +0x08f7eae7, 0x7faf77e5, 0x09159a09, 0x7fad5ead, 0x0933869f, 0x7fab3a17, 0x0951b0cd, 0x7fa909f6, +0x097018b7, 0x7fa6ce1a, 0x098ebe7f, 0x7fa48653, 0x09ada248, 0x7fa23273, 0x09ccc431, 0x7f9fd249, +0x09ec245b, 0x7f9d65a4, 0x0a0bc2e7, 0x7f9aec53, 0x0a2b9ff3, 0x7f986625, 0x0a4bbb9e, 0x7f95d2e7, +0x0a6c1604, 0x7f933267, 0x0a8caf43, 0x7f908472, 0x0aad8776, 0x7f8dc8d5, 0x0ace9eb9, 0x7f8aff5c, +0x0aeff526, 0x7f8827d3, 0x0b118ad8, 0x7f854204, 0x0b335fe6, 0x7f824dbb, 0x0b557469, 0x7f7f4ac3, +0x0b77c879, 0x7f7c38e4, 0x0b9a5c2b, 0x7f7917e9, 0x0bbd2f97, 0x7f75e79b, 0x0be042d0, 0x7f72a7c3, +0x0c0395ec, 0x7f6f5828, 0x0c2728fd, 0x7f6bf892, 0x0c4afc16, 0x7f6888c9, 0x0c6f0f4a, 0x7f650894, +0x0c9362a8, 0x7f6177b9, 0x0cb7f642, 0x7f5dd5ff, 0x0cdcca26, 0x7f5a232a, 0x0d01de63, 0x7f565f00, +0x0d273307, 0x7f528947, 0x0d4cc81f, 0x7f4ea1c2, 0x0d729db7, 0x7f4aa835, 0x0d98b3da, 0x7f469c65, +0x0dbf0a92, 0x7f427e13, 0x0de5a1e9, 0x7f3e4d04, 0x0e0c79e7, 0x7f3a08f9, 0x0e339295, 0x7f35b1b4, +0x0e5aebfa, 0x7f3146f8, 0x0e82861a, 0x7f2cc884, 0x0eaa60fd, 0x7f28361b, 0x0ed27ca5, 0x7f238f7c, +0x0efad917, 0x7f1ed467, 0x0f237656, 0x7f1a049d, 0x0f4c5462, 0x7f151fdc, 0x0f75733d, 0x7f1025e3, +0x0f9ed2e6, 0x7f0b1672, 0x0fc8735e, 0x7f05f146, 0x0ff254a1, 0x7f00b61d, 0x101c76ae, 0x7efb64b4, +0x1046d981, 0x7ef5fcca, 0x10717d15, 0x7ef07e19, 0x109c6165, 0x7eeae860, 0x10c7866a, 0x7ee53b5b, +0x10f2ec1e, 0x7edf76c4, 0x111e9279, 0x7ed99a58, 0x114a7971, 0x7ed3a5d1, 0x1176a0fc, 0x7ecd98eb, +0x11a30910, 0x7ec77360, 0x11cfb1a1, 0x7ec134eb, 0x11fc9aa2, 0x7ebadd44, 0x1229c406, 0x7eb46c27, +0x12572dbf, 0x7eade14c, 0x1284d7bc, 0x7ea73c6c, 0x12b2c1ed, 0x7ea07d41, 0x12e0ec42, 0x7e99a382, +0x130f56a8, 0x7e92aee7, 0x133e010b, 0x7e8b9f2a, 0x136ceb59, 0x7e847402, 0x139c157b, 0x7e7d2d25, +0x13cb7f5d, 0x7e75ca4c, 0x13fb28e6, 0x7e6e4b2d, 0x142b1200, 0x7e66af7f, 0x145b3a92, 0x7e5ef6f8, +0x148ba281, 0x7e572150, 0x14bc49b4, 0x7e4f2e3b, 0x14ed300f, 0x7e471d70, 0x151e5575, 0x7e3eeea5, +0x154fb9c9, 0x7e36a18e, 0x15815ced, 0x7e2e35e2, 0x15b33ec1, 0x7e25ab56, 0x15e55f25, 0x7e1d019e, +0x1617bdf9, 0x7e14386e, 0x164a5b19, 0x7e0b4f7d, 0x167d3662, 0x7e02467e, 0x16b04fb2, 0x7df91d25, +0x16e3a6e2, 0x7defd327, 0x17173bce, 0x7de66837, 0x174b0e4d, 0x7ddcdc0a, 0x177f1e39, 0x7dd32e53, +0x17b36b69, 0x7dc95ec6, 0x17e7f5b3, 0x7dbf6d17, 0x181cbcec, 0x7db558f9, 0x1851c0e9, 0x7dab221f, +0x1887017d, 0x7da0c83c, 0x18bc7e7c, 0x7d964b05, 0x18f237b6, 0x7d8baa2b, 0x19282cfd, 0x7d80e563, +0x195e5e20, 0x7d75fc5e, 0x1994caee, 0x7d6aeed0, 0x19cb7335, 0x7d5fbc6d, 0x1a0256c2, 0x7d5464e6, +0x1a397561, 0x7d48e7ef, 0x1a70cede, 0x7d3d453b, 0x1aa86301, 0x7d317c7c, 0x1ae03195, 0x7d258d65, +0x1b183a63, 0x7d1977aa, 0x1b507d30, 0x7d0d3afc, 0x1b88f9c5, 0x7d00d710, 0x1bc1afe6, 0x7cf44b97, +0x1bfa9f58, 0x7ce79846, 0x1c33c7e0, 0x7cdabcce, 0x1c6d293f, 0x7ccdb8e4, 0x1ca6c337, 0x7cc08c39, +0x1ce0958a, 0x7cb33682, 0x1d1a9ff8, 0x7ca5b772, 0x1d54e240, 0x7c980ebd, 0x1d8f5c21, 0x7c8a3c14, +0x1dca0d56, 0x7c7c3f2e, 0x1e04f59f, 0x7c6e17bc, 0x1e4014b4, 0x7c5fc573, 0x1e7b6a53, 0x7c514807, +0x1eb6f633, 0x7c429f2c, 0x1ef2b80f, 0x7c33ca96, 0x1f2eaf9e, 0x7c24c9fa, 0x1f6adc98, 0x7c159d0d, +0x1fa73eb2, 0x7c064383, 0x1fe3d5a3, 0x7bf6bd11, 0x2020a11e, 0x7be7096c, 0x205da0d8, 0x7bd7284a, +0x209ad483, 0x7bc71960, 0x20d83bd1, 0x7bb6dc65, 0x2115d674, 0x7ba6710d, 0x2153a41b, 0x7b95d710, +0x2191a476, 0x7b850e24, 0x21cfd734, 0x7b7415ff, 0x220e3c02, 0x7b62ee59, 0x224cd28d, 0x7b5196e9, +0x228b9a82, 0x7b400f67, 0x22ca938a, 0x7b2e578a, 0x2309bd52, 0x7b1c6f0b, 0x23491783, 0x7b0a55a1, +0x2388a1c4, 0x7af80b07, 0x23c85bbf, 0x7ae58ef5, 0x2408451a, 0x7ad2e124, 0x24485d7c, 0x7ac0014e, +0x2488a48a, 0x7aacef2e, 0x24c919e9, 0x7a99aa7e, 0x2509bd3d, 0x7a8632f8, 0x254a8e29, 0x7a728858, +0x258b8c50, 0x7a5eaa5a, 0x25ccb753, 0x7a4a98b9, 0x260e0ed3, 0x7a365333, 0x264f9271, 0x7a21d983, +0x269141cb, 0x7a0d2b68, 0x26d31c80, 0x79f8489e, 0x2715222f, 0x79e330e4, 0x27575273, 0x79cde3f8, +0x2799acea, 0x79b8619a, 0x27dc3130, 0x79a2a989, 0x281ededf, 0x798cbb85, 0x2861b591, 0x7976974e, +0x28a4b4e0, 0x79603ca5, 0x28e7dc65, 0x7949ab4c, 0x292b2bb8, 0x7932e304, 0x296ea270, 0x791be390, +0x29b24024, 0x7904acb3, 0x29f6046b, 0x78ed3e30, 0x2a39eed8, 0x78d597cc, 0x2a7dff02, 0x78bdb94a, +0x2ac2347c, 0x78a5a270, 0x2b068eda, 0x788d5304, 0x2b4b0dae, 0x7874cacb, 0x2b8fb08a, 0x785c098d, +0x2bd47700, 0x78430f11, 0x2c1960a1, 0x7829db1f, 0x2c5e6cfd, 0x78106d7f, 0x2ca39ba3, 0x77f6c5fb, +0x2ce8ec23, 0x77dce45c, 0x2d2e5e0b, 0x77c2c86e, 0x2d73f0e8, 0x77a871fa, 0x2db9a449, 0x778de0cd, +0x2dff77b8, 0x777314b2, 0x2e456ac4, 0x77580d78, 0x2e8b7cf6, 0x773ccaeb, 0x2ed1addb, 0x77214cdb, +0x2f17fcfb, 0x77059315, 0x2f5e69e2, 0x76e99d69, 0x2fa4f419, 0x76cd6ba9, 0x2feb9b27, 0x76b0fda4, +0x30325e96, 0x7694532e, 0x30793dee, 0x76776c17, 0x30c038b5, 0x765a4834, 0x31074e72, 0x763ce759, +0x314e7eab, 0x761f4959, 0x3195c8e6, 0x76016e0b, 0x31dd2ca9, 0x75e35545, 0x3224a979, 0x75c4fedc, +0x326c3ed8, 0x75a66aab, 0x32b3ec4d, 0x75879887, 0x32fbb159, 0x7568884b, 0x33438d81, 0x754939d1, +0x338b8045, 0x7529acf4, 0x33d3892a, 0x7509e18e, 0x341ba7b1, 0x74e9d77d, 0x3463db5a, 0x74c98e9e, +0x34ac23a7, 0x74a906cd, 0x34f48019, 0x74883fec, 0x353cf02f, 0x746739d8, 0x3585736a, 0x7445f472, +0x35ce0949, 0x74246f9c, 0x3616b14c, 0x7402ab37, 0x365f6af0, 0x73e0a727, 0x36a835b5, 0x73be6350, +0x36f11118, 0x739bdf95, 0x3739fc98, 0x73791bdd, 0x3782f7b2, 0x7356180e, 0x37cc01e3, 0x7332d410, +0x38151aa8, 0x730f4fc9, 0x385e417e, 0x72eb8b24, 0x38a775e1, 0x72c7860a, 0x38f0b74d, 0x72a34066, +0x393a053e, 0x727eba24, 0x39835f30, 0x7259f331, 0x39ccc49e, 0x7234eb79, 0x3a163503, 0x720fa2eb, +0x3a5fafda, 0x71ea1977, 0x3aa9349e, 0x71c44f0c, 0x3af2c2ca, 0x719e439d, 0x3b3c59d7, 0x7177f71a, +0x3b85f940, 0x71516978, 0x3bcfa07e, 0x712a9aaa, 0x3c194f0d, 0x71038aa4, 0x3c630464, 0x70dc395e, +0x3cacbfff, 0x70b4a6cd, 0x3cf68155, 0x708cd2e9, 0x3d4047e1, 0x7064bdab, 0x3d8a131c, 0x703c670d, +0x3dd3e27e, 0x7013cf0a, 0x3e1db580, 0x6feaf59c, 0x3e678b9b, 0x6fc1dac1, 0x3eb16449, 0x6f987e76, +0x3efb3f01, 0x6f6ee0b9, 0x3f451b3d, 0x6f45018b, 0x3f8ef874, 0x6f1ae0eb, 0x3fd8d620, 0x6ef07edb, +0x4022b3b9, 0x6ec5db5d, 0x406c90b7, 0x6e9af675, 0x40b66c93, 0x6e6fd027, 0x410046c5, 0x6e446879, +0x414a1ec6, 0x6e18bf71, 0x4193f40d, 0x6decd517, 0x41ddc615, 0x6dc0a972, 0x42279455, 0x6d943c8d, +0x42715e45, 0x6d678e71, 0x42bb235f, 0x6d3a9f2a, 0x4304e31a, 0x6d0d6ec5, 0x434e9cf1, 0x6cdffd4f, +0x4398505b, 0x6cb24ad6, 0x43e1fcd1, 0x6c84576b, 0x442ba1cd, 0x6c56231c, 0x44753ec7, 0x6c27adfd, +0x44bed33a, 0x6bf8f81e, 0x45085e9d, 0x6bca0195, 0x4551e06b, 0x6b9aca75, 0x459b581e, 0x6b6b52d5, +0x45e4c52f, 0x6b3b9ac9, 0x462e2717, 0x6b0ba26b, 0x46777d52, 0x6adb69d3, 0x46c0c75a, 0x6aaaf11b, +0x470a04a9, 0x6a7a385c, 0x475334b9, 0x6a493fb3, 0x479c5707, 0x6a18073d, 0x47e56b0c, 0x69e68f17, +0x482e7045, 0x69b4d761, 0x4877662c, 0x6982e039, 0x48c04c3f, 0x6950a9c0, 0x490921f8, 0x691e341a, +0x4951e6d5, 0x68eb7f67, 0x499a9a51, 0x68b88bcd, 0x49e33beb, 0x68855970, 0x4a2bcb1f, 0x6851e875, +0x4a74476b, 0x681e3905, 0x4abcb04c, 0x67ea4b47, 0x4b050541, 0x67b61f63, 0x4b4d45c9, 0x6781b585, +0x4b957162, 0x674d0dd6, 0x4bdd878c, 0x67182883, 0x4c2587c6, 0x66e305b8, 0x4c6d7190, 0x66ada5a5, +0x4cb5446a, 0x66780878, 0x4cfcffd5, 0x66422e60, 0x4d44a353, 0x660c1790, 0x4d8c2e64, 0x65d5c439, +0x4dd3a08c, 0x659f348e, 0x4e1af94b, 0x656868c3, 0x4e623825, 0x6531610d, 0x4ea95c9d, 0x64fa1da3, +0x4ef06637, 0x64c29ebb, 0x4f375477, 0x648ae48d, 0x4f7e26e1, 0x6452ef53, 0x4fc4dcfb, 0x641abf46, +0x500b7649, 0x63e254a2, 0x5051f253, 0x63a9afa2, 0x5098509f, 0x6370d083, 0x50de90b3, 0x6337b784, +0x5124b218, 0x62fe64e3, 0x516ab455, 0x62c4d8e0, 0x51b096f3, 0x628b13bc, 0x51f6597b, 0x625115b8, +0x523bfb78, 0x6216df18, 0x52817c72, 0x61dc701f, 0x52c6dbf5, 0x61a1c912, 0x530c198d, 0x6166ea36, +0x535134c5, 0x612bd3d2, 0x53962d2a, 0x60f0862d, 0x53db024a, 0x60b50190, 0x541fb3b1, 0x60794644, +0x546440ef, 0x603d5494, 0x54a8a992, 0x60012cca, 0x54eced2b, 0x5fc4cf33, 0x55310b48, 0x5f883c1c, +0x5575037c, 0x5f4b73d2, 0x55b8d558, 0x5f0e76a5, 0x55fc806f, 0x5ed144e5, 0x56400452, 0x5e93dee1, +0x56836096, 0x5e5644ec, 0x56c694cf, 0x5e187757, 0x5709a092, 0x5dda7677, 0x574c8374, 0x5d9c429f, +0x578f3d0d, 0x5d5ddc24, 0x57d1ccf2, 0x5d1f435d, 0x581432bd, 0x5ce078a0, 0x58566e04, 0x5ca17c45, +0x58987e63, 0x5c624ea4, 0x58da6372, 0x5c22f016, 0x591c1ccc, 0x5be360f6, 0x595daa0d, 0x5ba3a19f, +0x599f0ad1, 0x5b63b26c, 0x59e03eb6, 0x5b2393ba, 0x5a214558, 0x5ae345e7, 0x5a621e56, 0x5aa2c951, +}; + + + +/* bit reverse tables for FFT */ + +const int bitrevtabOffset[NUM_IMDCT_SIZES] PROGMEM = {0, 17}; + +const unsigned char bitrevtab[17 + 129] PROGMEM = { +/* nfft = 64 */ +0x01, 0x08, 0x02, 0x04, 0x03, 0x0c, 0x05, 0x0a, 0x07, 0x0e, 0x0b, 0x0d, 0x00, 0x06, 0x09, 0x0f, +0x00, + +/* nfft = 512 */ +0x01, 0x40, 0x02, 0x20, 0x03, 0x60, 0x04, 0x10, 0x05, 0x50, 0x06, 0x30, 0x07, 0x70, 0x09, 0x48, +0x0a, 0x28, 0x0b, 0x68, 0x0c, 0x18, 0x0d, 0x58, 0x0e, 0x38, 0x0f, 0x78, 0x11, 0x44, 0x12, 0x24, +0x13, 0x64, 0x15, 0x54, 0x16, 0x34, 0x17, 0x74, 0x19, 0x4c, 0x1a, 0x2c, 0x1b, 0x6c, 0x1d, 0x5c, +0x1e, 0x3c, 0x1f, 0x7c, 0x21, 0x42, 0x23, 0x62, 0x25, 0x52, 0x26, 0x32, 0x27, 0x72, 0x29, 0x4a, +0x2b, 0x6a, 0x2d, 0x5a, 0x2e, 0x3a, 0x2f, 0x7a, 0x31, 0x46, 0x33, 0x66, 0x35, 0x56, 0x37, 0x76, +0x39, 0x4e, 0x3b, 0x6e, 0x3d, 0x5e, 0x3f, 0x7e, 0x43, 0x61, 0x45, 0x51, 0x47, 0x71, 0x4b, 0x69, +0x4d, 0x59, 0x4f, 0x79, 0x53, 0x65, 0x57, 0x75, 0x5b, 0x6d, 0x5f, 0x7d, 0x67, 0x73, 0x6f, 0x7b, +0x00, 0x08, 0x14, 0x1c, 0x22, 0x2a, 0x36, 0x3e, 0x41, 0x49, 0x55, 0x5d, 0x63, 0x6b, 0x77, 0x7f, +0x00, + +}; + +const unsigned char uniqueIDTab[8] = {0x5f, 0x4b, 0x43, 0x5f, 0x5f, 0x4a, 0x52, 0x5f}; + +/* Twiddle tables for FFT + * format = Q30 + * + * for (k = 4; k <= N/4; k <<= 1) { + * for (j = 0; j < k; j++) { + * double wr1, wi1, wr2, wi2, wr3, wi3; + * + * wr1 = cos(1.0 * M_PI * j / (2*k)); + * wi1 = sin(1.0 * M_PI * j / (2*k)); + * wr1 = (wr1 + wi1); + * wi1 = -wi1; + * + * wr2 = cos(2.0 * M_PI * j / (2*k)); + * wi2 = sin(2.0 * M_PI * j / (2*k)); + * wr2 = (wr2 + wi2); + * wi2 = -wi2; + * + * wr3 = cos(3.0 * M_PI * j / (2*k)); + * wi3 = sin(3.0 * M_PI * j / (2*k)); + * wr3 = (wr3 + wi3); + * wi3 = -wi3; + * + * if (k & 0xaaaaaaaa) { + * w_odd[iodd++] = (float)wr2; + * w_odd[iodd++] = (float)wi2; + * w_odd[iodd++] = (float)wr1; + * w_odd[iodd++] = (float)wi1; + * w_odd[iodd++] = (float)wr3; + * w_odd[iodd++] = (float)wi3; + * } else { + * w_even[ieven++] = (float)wr2; + * w_even[ieven++] = (float)wi2; + * w_even[ieven++] = (float)wr1; + * w_even[ieven++] = (float)wi1; + * w_even[ieven++] = (float)wr3; + * w_even[ieven++] = (float)wi3; + * } + * } + * } + */ +const int twidTabOdd[8*6 + 32*6 + 128*6] PROGMEM = { + 0x40000000, 0x00000000, 0x40000000, 0x00000000, 0x40000000, 0x00000000, 0x539eba45, 0xe7821d59, + 0x4b418bbe, 0xf383a3e2, 0x58c542c5, 0xdc71898d, 0x5a82799a, 0xd2bec333, 0x539eba45, 0xe7821d59, + 0x539eba45, 0xc4df2862, 0x539eba45, 0xc4df2862, 0x58c542c5, 0xdc71898d, 0x3248d382, 0xc13ad060, + 0x40000000, 0xc0000000, 0x5a82799a, 0xd2bec333, 0x00000000, 0xd2bec333, 0x22a2f4f8, 0xc4df2862, + 0x58c542c5, 0xcac933ae, 0xcdb72c7e, 0xf383a3e2, 0x00000000, 0xd2bec333, 0x539eba45, 0xc4df2862, + 0xac6145bb, 0x187de2a7, 0xdd5d0b08, 0xe7821d59, 0x4b418bbe, 0xc13ad060, 0xa73abd3b, 0x3536cc52, + + 0x40000000, 0x00000000, 0x40000000, 0x00000000, 0x40000000, 0x00000000, 0x45f704f7, 0xf9ba1651, + 0x43103085, 0xfcdc1342, 0x48b2b335, 0xf69bf7c9, 0x4b418bbe, 0xf383a3e2, 0x45f704f7, 0xf9ba1651, + 0x4fd288dc, 0xed6bf9d1, 0x4fd288dc, 0xed6bf9d1, 0x48b2b335, 0xf69bf7c9, 0x553805f2, 0xe4a2eff6, + 0x539eba45, 0xe7821d59, 0x4b418bbe, 0xf383a3e2, 0x58c542c5, 0xdc71898d, 0x569cc31b, 0xe1d4a2c8, + 0x4da1fab5, 0xf0730342, 0x5a6690ae, 0xd5052d97, 0x58c542c5, 0xdc71898d, 0x4fd288dc, 0xed6bf9d1, + 0x5a12e720, 0xce86ff2a, 0x5a12e720, 0xd76619b6, 0x51d1dc80, 0xea70658a, 0x57cc15bc, 0xc91af976, + 0x5a82799a, 0xd2bec333, 0x539eba45, 0xe7821d59, 0x539eba45, 0xc4df2862, 0x5a12e720, 0xce86ff2a, + 0x553805f2, 0xe4a2eff6, 0x4da1fab5, 0xc1eb0209, 0x58c542c5, 0xcac933ae, 0x569cc31b, 0xe1d4a2c8, + 0x45f704f7, 0xc04ee4b8, 0x569cc31b, 0xc78e9a1d, 0x57cc15bc, 0xdf18f0ce, 0x3cc85709, 0xc013bc39, + 0x539eba45, 0xc4df2862, 0x58c542c5, 0xdc71898d, 0x3248d382, 0xc13ad060, 0x4fd288dc, 0xc2c17d52, + 0x5987b08a, 0xd9e01006, 0x26b2a794, 0xc3bdbdf6, 0x4b418bbe, 0xc13ad060, 0x5a12e720, 0xd76619b6, + 0x1a4608ab, 0xc78e9a1d, 0x45f704f7, 0xc04ee4b8, 0x5a6690ae, 0xd5052d97, 0x0d47d096, 0xcc983f70, + 0x40000000, 0xc0000000, 0x5a82799a, 0xd2bec333, 0x00000000, 0xd2bec333, 0x396b3199, 0xc04ee4b8, + 0x5a6690ae, 0xd09441bb, 0xf2b82f6a, 0xd9e01006, 0x3248d382, 0xc13ad060, 0x5a12e720, 0xce86ff2a, + 0xe5b9f755, 0xe1d4a2c8, 0x2aaa7c7f, 0xc2c17d52, 0x5987b08a, 0xcc983f70, 0xd94d586c, 0xea70658a, + 0x22a2f4f8, 0xc4df2862, 0x58c542c5, 0xcac933ae, 0xcdb72c7e, 0xf383a3e2, 0x1a4608ab, 0xc78e9a1d, + 0x57cc15bc, 0xc91af976, 0xc337a8f7, 0xfcdc1342, 0x11a855df, 0xcac933ae, 0x569cc31b, 0xc78e9a1d, + 0xba08fb09, 0x0645e9af, 0x08df1a8c, 0xce86ff2a, 0x553805f2, 0xc6250a18, 0xb25e054b, 0x0f8cfcbe, + 0x00000000, 0xd2bec333, 0x539eba45, 0xc4df2862, 0xac6145bb, 0x187de2a7, 0xf720e574, 0xd76619b6, + 0x51d1dc80, 0xc3bdbdf6, 0xa833ea44, 0x20e70f32, 0xee57aa21, 0xdc71898d, 0x4fd288dc, 0xc2c17d52, + 0xa5ed18e0, 0x2899e64a, 0xe5b9f755, 0xe1d4a2c8, 0x4da1fab5, 0xc1eb0209, 0xa5996f52, 0x2f6bbe45, + 0xdd5d0b08, 0xe7821d59, 0x4b418bbe, 0xc13ad060, 0xa73abd3b, 0x3536cc52, 0xd5558381, 0xed6bf9d1, + 0x48b2b335, 0xc0b15502, 0xaac7fa0e, 0x39daf5e8, 0xcdb72c7e, 0xf383a3e2, 0x45f704f7, 0xc04ee4b8, + 0xb02d7724, 0x3d3e82ae, 0xc694ce67, 0xf9ba1651, 0x43103085, 0xc013bc39, 0xb74d4ccb, 0x3f4eaafe, + + 0x40000000, 0x00000000, 0x40000000, 0x00000000, 0x40000000, 0x00000000, 0x418d2621, 0xfe6deaa1, + 0x40c7d2bd, 0xff36f170, 0x424ff28f, 0xfda4f351, 0x43103085, 0xfcdc1342, 0x418d2621, 0xfe6deaa1, + 0x4488e37f, 0xfb4ab7db, 0x4488e37f, 0xfb4ab7db, 0x424ff28f, 0xfda4f351, 0x46aa0d6d, 0xf8f21e8e, + 0x45f704f7, 0xf9ba1651, 0x43103085, 0xfcdc1342, 0x48b2b335, 0xf69bf7c9, 0x475a5c77, 0xf82a6c6a, + 0x43cdd89a, 0xfc135231, 0x4aa22036, 0xf4491311, 0x48b2b335, 0xf69bf7c9, 0x4488e37f, 0xfb4ab7db, + 0x4c77a88e, 0xf1fa3ecb, 0x49ffd417, 0xf50ef5de, 0x454149fc, 0xfa824bfd, 0x4e32a956, 0xefb047f2, + 0x4b418bbe, 0xf383a3e2, 0x45f704f7, 0xf9ba1651, 0x4fd288dc, 0xed6bf9d1, 0x4c77a88e, 0xf1fa3ecb, + 0x46aa0d6d, 0xf8f21e8e, 0x5156b6d9, 0xeb2e1dbe, 0x4da1fab5, 0xf0730342, 0x475a5c77, 0xf82a6c6a, + 0x52beac9f, 0xe8f77acf, 0x4ec05432, 0xeeee2d9d, 0x4807eb4b, 0xf7630799, 0x5409ed4b, 0xe6c8d59c, + 0x4fd288dc, 0xed6bf9d1, 0x48b2b335, 0xf69bf7c9, 0x553805f2, 0xe4a2eff6, 0x50d86e6d, 0xebeca36c, + 0x495aada2, 0xf5d544a7, 0x56488dc5, 0xe28688a4, 0x51d1dc80, 0xea70658a, 0x49ffd417, 0xf50ef5de, + 0x573b2635, 0xe0745b24, 0x52beac9f, 0xe8f77acf, 0x4aa22036, 0xf4491311, 0x580f7b19, 0xde6d1f65, + 0x539eba45, 0xe7821d59, 0x4b418bbe, 0xf383a3e2, 0x58c542c5, 0xdc71898d, 0x5471e2e6, 0xe61086bc, + 0x4bde1089, 0xf2beafed, 0x595c3e2a, 0xda8249b4, 0x553805f2, 0xe4a2eff6, 0x4c77a88e, 0xf1fa3ecb, + 0x59d438e5, 0xd8a00bae, 0x55f104dc, 0xe3399167, 0x4d0e4de2, 0xf136580d, 0x5a2d0957, 0xd6cb76c9, + 0x569cc31b, 0xe1d4a2c8, 0x4da1fab5, 0xf0730342, 0x5a6690ae, 0xd5052d97, 0x573b2635, 0xe0745b24, + 0x4e32a956, 0xefb047f2, 0x5a80baf6, 0xd34dcdb4, 0x57cc15bc, 0xdf18f0ce, 0x4ec05432, 0xeeee2d9d, + 0x5a7b7f1a, 0xd1a5ef90, 0x584f7b58, 0xddc29958, 0x4f4af5d1, 0xee2cbbc1, 0x5a56deec, 0xd00e2639, + 0x58c542c5, 0xdc71898d, 0x4fd288dc, 0xed6bf9d1, 0x5a12e720, 0xce86ff2a, 0x592d59da, 0xdb25f566, + 0x50570819, 0xecabef3d, 0x59afaf4c, 0xcd110216, 0x5987b08a, 0xd9e01006, 0x50d86e6d, 0xebeca36c, + 0x592d59da, 0xcbacb0bf, 0x59d438e5, 0xd8a00bae, 0x5156b6d9, 0xeb2e1dbe, 0x588c1404, 0xca5a86c4, + 0x5a12e720, 0xd76619b6, 0x51d1dc80, 0xea70658a, 0x57cc15bc, 0xc91af976, 0x5a43b190, 0xd6326a88, + 0x5249daa2, 0xe9b38223, 0x56eda1a0, 0xc7ee77b3, 0x5a6690ae, 0xd5052d97, 0x52beac9f, 0xe8f77acf, + 0x55f104dc, 0xc6d569be, 0x5a7b7f1a, 0xd3de9156, 0x53304df6, 0xe83c56cf, 0x54d69714, 0xc5d03118, + 0x5a82799a, 0xd2bec333, 0x539eba45, 0xe7821d59, 0x539eba45, 0xc4df2862, 0x5a7b7f1a, 0xd1a5ef90, + 0x5409ed4b, 0xe6c8d59c, 0x5249daa2, 0xc402a33c, 0x5a6690ae, 0xd09441bb, 0x5471e2e6, 0xe61086bc, + 0x50d86e6d, 0xc33aee27, 0x5a43b190, 0xcf89e3e8, 0x54d69714, 0xe55937d5, 0x4f4af5d1, 0xc2884e6e, + 0x5a12e720, 0xce86ff2a, 0x553805f2, 0xe4a2eff6, 0x4da1fab5, 0xc1eb0209, 0x59d438e5, 0xcd8bbb6d, + 0x55962bc0, 0xe3edb628, 0x4bde1089, 0xc1633f8a, 0x5987b08a, 0xcc983f70, 0x55f104dc, 0xe3399167, + 0x49ffd417, 0xc0f1360b, 0x592d59da, 0xcbacb0bf, 0x56488dc5, 0xe28688a4, 0x4807eb4b, 0xc0950d1d, + 0x58c542c5, 0xcac933ae, 0x569cc31b, 0xe1d4a2c8, 0x45f704f7, 0xc04ee4b8, 0x584f7b58, 0xc9edeb50, + 0x56eda1a0, 0xe123e6ad, 0x43cdd89a, 0xc01ed535, 0x57cc15bc, 0xc91af976, 0x573b2635, 0xe0745b24, + 0x418d2621, 0xc004ef3f, 0x573b2635, 0xc8507ea7, 0x57854ddd, 0xdfc606f1, 0x3f35b59d, 0xc0013bd3, + 0x569cc31b, 0xc78e9a1d, 0x57cc15bc, 0xdf18f0ce, 0x3cc85709, 0xc013bc39, 0x55f104dc, 0xc6d569be, + 0x580f7b19, 0xde6d1f65, 0x3a45e1f7, 0xc03c6a07, 0x553805f2, 0xc6250a18, 0x584f7b58, 0xddc29958, + 0x37af354c, 0xc07b371e, 0x5471e2e6, 0xc57d965d, 0x588c1404, 0xdd196538, 0x350536f1, 0xc0d00db6, + 0x539eba45, 0xc4df2862, 0x58c542c5, 0xdc71898d, 0x3248d382, 0xc13ad060, 0x52beac9f, 0xc449d892, + 0x58fb0568, 0xdbcb0cce, 0x2f7afdfc, 0xc1bb5a11, 0x51d1dc80, 0xc3bdbdf6, 0x592d59da, 0xdb25f566, + 0x2c9caf6c, 0xc2517e31, 0x50d86e6d, 0xc33aee27, 0x595c3e2a, 0xda8249b4, 0x29aee694, 0xc2fd08a9, + 0x4fd288dc, 0xc2c17d52, 0x5987b08a, 0xd9e01006, 0x26b2a794, 0xc3bdbdf6, 0x4ec05432, 0xc2517e31, + 0x59afaf4c, 0xd93f4e9e, 0x23a8fb93, 0xc4935b3c, 0x4da1fab5, 0xc1eb0209, 0x59d438e5, 0xd8a00bae, + 0x2092f05f, 0xc57d965d, 0x4c77a88e, 0xc18e18a7, 0x59f54bee, 0xd8024d59, 0x1d719810, 0xc67c1e18, + 0x4b418bbe, 0xc13ad060, 0x5a12e720, 0xd76619b6, 0x1a4608ab, 0xc78e9a1d, 0x49ffd417, 0xc0f1360b, + 0x5a2d0957, 0xd6cb76c9, 0x17115bc0, 0xc8b4ab32, 0x48b2b335, 0xc0b15502, 0x5a43b190, 0xd6326a88, + 0x13d4ae08, 0xc9edeb50, 0x475a5c77, 0xc07b371e, 0x5a56deec, 0xd59afadb, 0x10911f04, 0xcb39edca, + 0x45f704f7, 0xc04ee4b8, 0x5a6690ae, 0xd5052d97, 0x0d47d096, 0xcc983f70, 0x4488e37f, 0xc02c64a6, + 0x5a72c63b, 0xd4710883, 0x09f9e6a1, 0xce0866b8, 0x43103085, 0xc013bc39, 0x5a7b7f1a, 0xd3de9156, + 0x06a886a0, 0xcf89e3e8, 0x418d2621, 0xc004ef3f, 0x5a80baf6, 0xd34dcdb4, 0x0354d741, 0xd11c3142, + 0x40000000, 0xc0000000, 0x5a82799a, 0xd2bec333, 0x00000000, 0xd2bec333, 0x3e68fb62, 0xc004ef3f, + 0x5a80baf6, 0xd2317756, 0xfcab28bf, 0xd4710883, 0x3cc85709, 0xc013bc39, 0x5a7b7f1a, 0xd1a5ef90, + 0xf9577960, 0xd6326a88, 0x3b1e5335, 0xc02c64a6, 0x5a72c63b, 0xd11c3142, 0xf606195f, 0xd8024d59, + 0x396b3199, 0xc04ee4b8, 0x5a6690ae, 0xd09441bb, 0xf2b82f6a, 0xd9e01006, 0x37af354c, 0xc07b371e, + 0x5a56deec, 0xd00e2639, 0xef6ee0fc, 0xdbcb0cce, 0x35eaa2c7, 0xc0b15502, 0x5a43b190, 0xcf89e3e8, + 0xec2b51f8, 0xddc29958, 0x341dbfd3, 0xc0f1360b, 0x5a2d0957, 0xcf077fe1, 0xe8eea440, 0xdfc606f1, + 0x3248d382, 0xc13ad060, 0x5a12e720, 0xce86ff2a, 0xe5b9f755, 0xe1d4a2c8, 0x306c2624, 0xc18e18a7, + 0x59f54bee, 0xce0866b8, 0xe28e67f0, 0xe3edb628, 0x2e88013a, 0xc1eb0209, 0x59d438e5, 0xcd8bbb6d, + 0xdf6d0fa1, 0xe61086bc, 0x2c9caf6c, 0xc2517e31, 0x59afaf4c, 0xcd110216, 0xdc57046d, 0xe83c56cf, + 0x2aaa7c7f, 0xc2c17d52, 0x5987b08a, 0xcc983f70, 0xd94d586c, 0xea70658a, 0x28b1b544, 0xc33aee27, + 0x595c3e2a, 0xcc217822, 0xd651196c, 0xecabef3d, 0x26b2a794, 0xc3bdbdf6, 0x592d59da, 0xcbacb0bf, + 0xd3635094, 0xeeee2d9d, 0x24ada23d, 0xc449d892, 0x58fb0568, 0xcb39edca, 0xd0850204, 0xf136580d, + 0x22a2f4f8, 0xc4df2862, 0x58c542c5, 0xcac933ae, 0xcdb72c7e, 0xf383a3e2, 0x2092f05f, 0xc57d965d, + 0x588c1404, 0xca5a86c4, 0xcafac90f, 0xf5d544a7, 0x1e7de5df, 0xc6250a18, 0x584f7b58, 0xc9edeb50, + 0xc850cab4, 0xf82a6c6a, 0x1c6427a9, 0xc6d569be, 0x580f7b19, 0xc9836582, 0xc5ba1e09, 0xfa824bfd, + 0x1a4608ab, 0xc78e9a1d, 0x57cc15bc, 0xc91af976, 0xc337a8f7, 0xfcdc1342, 0x1823dc7d, 0xc8507ea7, + 0x57854ddd, 0xc8b4ab32, 0xc0ca4a63, 0xff36f170, 0x15fdf758, 0xc91af976, 0x573b2635, 0xc8507ea7, + 0xbe72d9df, 0x0192155f, 0x13d4ae08, 0xc9edeb50, 0x56eda1a0, 0xc7ee77b3, 0xbc322766, 0x03ecadcf, + 0x11a855df, 0xcac933ae, 0x569cc31b, 0xc78e9a1d, 0xba08fb09, 0x0645e9af, 0x0f7944a7, 0xcbacb0bf, + 0x56488dc5, 0xc730e997, 0xb7f814b5, 0x089cf867, 0x0d47d096, 0xcc983f70, 0x55f104dc, 0xc6d569be, + 0xb6002be9, 0x0af10a22, 0x0b145041, 0xcd8bbb6d, 0x55962bc0, 0xc67c1e18, 0xb421ef77, 0x0d415013, + 0x08df1a8c, 0xce86ff2a, 0x553805f2, 0xc6250a18, 0xb25e054b, 0x0f8cfcbe, 0x06a886a0, 0xcf89e3e8, + 0x54d69714, 0xc5d03118, 0xb0b50a2f, 0x11d3443f, 0x0470ebdc, 0xd09441bb, 0x5471e2e6, 0xc57d965d, + 0xaf279193, 0x14135c94, 0x0238a1c6, 0xd1a5ef90, 0x5409ed4b, 0xc52d3d18, 0xadb6255e, 0x164c7ddd, + 0x00000000, 0xd2bec333, 0x539eba45, 0xc4df2862, 0xac6145bb, 0x187de2a7, 0xfdc75e3a, 0xd3de9156, + 0x53304df6, 0xc4935b3c, 0xab2968ec, 0x1aa6c82b, 0xfb8f1424, 0xd5052d97, 0x52beac9f, 0xc449d892, + 0xaa0efb24, 0x1cc66e99, 0xf9577960, 0xd6326a88, 0x5249daa2, 0xc402a33c, 0xa9125e60, 0x1edc1953, + 0xf720e574, 0xd76619b6, 0x51d1dc80, 0xc3bdbdf6, 0xa833ea44, 0x20e70f32, 0xf4ebafbf, 0xd8a00bae, + 0x5156b6d9, 0xc37b2b6a, 0xa773ebfc, 0x22e69ac8, 0xf2b82f6a, 0xd9e01006, 0x50d86e6d, 0xc33aee27, + 0xa6d2a626, 0x24da0a9a, 0xf086bb59, 0xdb25f566, 0x50570819, 0xc2fd08a9, 0xa65050b4, 0x26c0b162, + 0xee57aa21, 0xdc71898d, 0x4fd288dc, 0xc2c17d52, 0xa5ed18e0, 0x2899e64a, 0xec2b51f8, 0xddc29958, + 0x4f4af5d1, 0xc2884e6e, 0xa5a92114, 0x2a650525, 0xea0208a8, 0xdf18f0ce, 0x4ec05432, 0xc2517e31, + 0xa58480e6, 0x2c216eaa, 0xe7dc2383, 0xe0745b24, 0x4e32a956, 0xc21d0eb8, 0xa57f450a, 0x2dce88aa, + 0xe5b9f755, 0xe1d4a2c8, 0x4da1fab5, 0xc1eb0209, 0xa5996f52, 0x2f6bbe45, 0xe39bd857, 0xe3399167, + 0x4d0e4de2, 0xc1bb5a11, 0xa5d2f6a9, 0x30f8801f, 0xe1821a21, 0xe4a2eff6, 0x4c77a88e, 0xc18e18a7, + 0xa62bc71b, 0x32744493, 0xdf6d0fa1, 0xe61086bc, 0x4bde1089, 0xc1633f8a, 0xa6a3c1d6, 0x33de87de, + 0xdd5d0b08, 0xe7821d59, 0x4b418bbe, 0xc13ad060, 0xa73abd3b, 0x3536cc52, 0xdb525dc3, 0xe8f77acf, + 0x4aa22036, 0xc114ccb9, 0xa7f084e7, 0x367c9a7e, 0xd94d586c, 0xea70658a, 0x49ffd417, 0xc0f1360b, + 0xa8c4d9cb, 0x37af8159, 0xd74e4abc, 0xebeca36c, 0x495aada2, 0xc0d00db6, 0xa9b7723b, 0x38cf1669, + 0xd5558381, 0xed6bf9d1, 0x48b2b335, 0xc0b15502, 0xaac7fa0e, 0x39daf5e8, 0xd3635094, 0xeeee2d9d, + 0x4807eb4b, 0xc0950d1d, 0xabf612b5, 0x3ad2c2e8, 0xd177fec6, 0xf0730342, 0x475a5c77, 0xc07b371e, + 0xad415361, 0x3bb6276e, 0xcf93d9dc, 0xf1fa3ecb, 0x46aa0d6d, 0xc063d405, 0xaea94927, 0x3c84d496, + 0xcdb72c7e, 0xf383a3e2, 0x45f704f7, 0xc04ee4b8, 0xb02d7724, 0x3d3e82ae, 0xcbe2402d, 0xf50ef5de, + 0x454149fc, 0xc03c6a07, 0xb1cd56aa, 0x3de2f148, 0xca155d39, 0xf69bf7c9, 0x4488e37f, 0xc02c64a6, + 0xb3885772, 0x3e71e759, 0xc850cab4, 0xf82a6c6a, 0x43cdd89a, 0xc01ed535, 0xb55ddfca, 0x3eeb3347, + 0xc694ce67, 0xf9ba1651, 0x43103085, 0xc013bc39, 0xb74d4ccb, 0x3f4eaafe, 0xc4e1accb, 0xfb4ab7db, + 0x424ff28f, 0xc00b1a20, 0xb955f293, 0x3f9c2bfb, 0xc337a8f7, 0xfcdc1342, 0x418d2621, 0xc004ef3f, + 0xbb771c81, 0x3fd39b5a, 0xc197049e, 0xfe6deaa1, 0x40c7d2bd, 0xc0013bd3, 0xbdb00d71, 0x3ff4e5e0, +}; + +const int twidTabEven[4*6 + 16*6 + 64*6] PROGMEM = { + 0x40000000, 0x00000000, 0x40000000, 0x00000000, 0x40000000, 0x00000000, 0x5a82799a, 0xd2bec333, + 0x539eba45, 0xe7821d59, 0x539eba45, 0xc4df2862, 0x40000000, 0xc0000000, 0x5a82799a, 0xd2bec333, + 0x00000000, 0xd2bec333, 0x00000000, 0xd2bec333, 0x539eba45, 0xc4df2862, 0xac6145bb, 0x187de2a7, + + 0x40000000, 0x00000000, 0x40000000, 0x00000000, 0x40000000, 0x00000000, 0x4b418bbe, 0xf383a3e2, + 0x45f704f7, 0xf9ba1651, 0x4fd288dc, 0xed6bf9d1, 0x539eba45, 0xe7821d59, 0x4b418bbe, 0xf383a3e2, + 0x58c542c5, 0xdc71898d, 0x58c542c5, 0xdc71898d, 0x4fd288dc, 0xed6bf9d1, 0x5a12e720, 0xce86ff2a, + 0x5a82799a, 0xd2bec333, 0x539eba45, 0xe7821d59, 0x539eba45, 0xc4df2862, 0x58c542c5, 0xcac933ae, + 0x569cc31b, 0xe1d4a2c8, 0x45f704f7, 0xc04ee4b8, 0x539eba45, 0xc4df2862, 0x58c542c5, 0xdc71898d, + 0x3248d382, 0xc13ad060, 0x4b418bbe, 0xc13ad060, 0x5a12e720, 0xd76619b6, 0x1a4608ab, 0xc78e9a1d, + 0x40000000, 0xc0000000, 0x5a82799a, 0xd2bec333, 0x00000000, 0xd2bec333, 0x3248d382, 0xc13ad060, + 0x5a12e720, 0xce86ff2a, 0xe5b9f755, 0xe1d4a2c8, 0x22a2f4f8, 0xc4df2862, 0x58c542c5, 0xcac933ae, + 0xcdb72c7e, 0xf383a3e2, 0x11a855df, 0xcac933ae, 0x569cc31b, 0xc78e9a1d, 0xba08fb09, 0x0645e9af, + 0x00000000, 0xd2bec333, 0x539eba45, 0xc4df2862, 0xac6145bb, 0x187de2a7, 0xee57aa21, 0xdc71898d, + 0x4fd288dc, 0xc2c17d52, 0xa5ed18e0, 0x2899e64a, 0xdd5d0b08, 0xe7821d59, 0x4b418bbe, 0xc13ad060, + 0xa73abd3b, 0x3536cc52, 0xcdb72c7e, 0xf383a3e2, 0x45f704f7, 0xc04ee4b8, 0xb02d7724, 0x3d3e82ae, + + 0x40000000, 0x00000000, 0x40000000, 0x00000000, 0x40000000, 0x00000000, 0x43103085, 0xfcdc1342, + 0x418d2621, 0xfe6deaa1, 0x4488e37f, 0xfb4ab7db, 0x45f704f7, 0xf9ba1651, 0x43103085, 0xfcdc1342, + 0x48b2b335, 0xf69bf7c9, 0x48b2b335, 0xf69bf7c9, 0x4488e37f, 0xfb4ab7db, 0x4c77a88e, 0xf1fa3ecb, + 0x4b418bbe, 0xf383a3e2, 0x45f704f7, 0xf9ba1651, 0x4fd288dc, 0xed6bf9d1, 0x4da1fab5, 0xf0730342, + 0x475a5c77, 0xf82a6c6a, 0x52beac9f, 0xe8f77acf, 0x4fd288dc, 0xed6bf9d1, 0x48b2b335, 0xf69bf7c9, + 0x553805f2, 0xe4a2eff6, 0x51d1dc80, 0xea70658a, 0x49ffd417, 0xf50ef5de, 0x573b2635, 0xe0745b24, + 0x539eba45, 0xe7821d59, 0x4b418bbe, 0xf383a3e2, 0x58c542c5, 0xdc71898d, 0x553805f2, 0xe4a2eff6, + 0x4c77a88e, 0xf1fa3ecb, 0x59d438e5, 0xd8a00bae, 0x569cc31b, 0xe1d4a2c8, 0x4da1fab5, 0xf0730342, + 0x5a6690ae, 0xd5052d97, 0x57cc15bc, 0xdf18f0ce, 0x4ec05432, 0xeeee2d9d, 0x5a7b7f1a, 0xd1a5ef90, + 0x58c542c5, 0xdc71898d, 0x4fd288dc, 0xed6bf9d1, 0x5a12e720, 0xce86ff2a, 0x5987b08a, 0xd9e01006, + 0x50d86e6d, 0xebeca36c, 0x592d59da, 0xcbacb0bf, 0x5a12e720, 0xd76619b6, 0x51d1dc80, 0xea70658a, + 0x57cc15bc, 0xc91af976, 0x5a6690ae, 0xd5052d97, 0x52beac9f, 0xe8f77acf, 0x55f104dc, 0xc6d569be, + 0x5a82799a, 0xd2bec333, 0x539eba45, 0xe7821d59, 0x539eba45, 0xc4df2862, 0x5a6690ae, 0xd09441bb, + 0x5471e2e6, 0xe61086bc, 0x50d86e6d, 0xc33aee27, 0x5a12e720, 0xce86ff2a, 0x553805f2, 0xe4a2eff6, + 0x4da1fab5, 0xc1eb0209, 0x5987b08a, 0xcc983f70, 0x55f104dc, 0xe3399167, 0x49ffd417, 0xc0f1360b, + 0x58c542c5, 0xcac933ae, 0x569cc31b, 0xe1d4a2c8, 0x45f704f7, 0xc04ee4b8, 0x57cc15bc, 0xc91af976, + 0x573b2635, 0xe0745b24, 0x418d2621, 0xc004ef3f, 0x569cc31b, 0xc78e9a1d, 0x57cc15bc, 0xdf18f0ce, + 0x3cc85709, 0xc013bc39, 0x553805f2, 0xc6250a18, 0x584f7b58, 0xddc29958, 0x37af354c, 0xc07b371e, + 0x539eba45, 0xc4df2862, 0x58c542c5, 0xdc71898d, 0x3248d382, 0xc13ad060, 0x51d1dc80, 0xc3bdbdf6, + 0x592d59da, 0xdb25f566, 0x2c9caf6c, 0xc2517e31, 0x4fd288dc, 0xc2c17d52, 0x5987b08a, 0xd9e01006, + 0x26b2a794, 0xc3bdbdf6, 0x4da1fab5, 0xc1eb0209, 0x59d438e5, 0xd8a00bae, 0x2092f05f, 0xc57d965d, + 0x4b418bbe, 0xc13ad060, 0x5a12e720, 0xd76619b6, 0x1a4608ab, 0xc78e9a1d, 0x48b2b335, 0xc0b15502, + 0x5a43b190, 0xd6326a88, 0x13d4ae08, 0xc9edeb50, 0x45f704f7, 0xc04ee4b8, 0x5a6690ae, 0xd5052d97, + 0x0d47d096, 0xcc983f70, 0x43103085, 0xc013bc39, 0x5a7b7f1a, 0xd3de9156, 0x06a886a0, 0xcf89e3e8, + 0x40000000, 0xc0000000, 0x5a82799a, 0xd2bec333, 0x00000000, 0xd2bec333, 0x3cc85709, 0xc013bc39, + 0x5a7b7f1a, 0xd1a5ef90, 0xf9577960, 0xd6326a88, 0x396b3199, 0xc04ee4b8, 0x5a6690ae, 0xd09441bb, + 0xf2b82f6a, 0xd9e01006, 0x35eaa2c7, 0xc0b15502, 0x5a43b190, 0xcf89e3e8, 0xec2b51f8, 0xddc29958, + 0x3248d382, 0xc13ad060, 0x5a12e720, 0xce86ff2a, 0xe5b9f755, 0xe1d4a2c8, 0x2e88013a, 0xc1eb0209, + 0x59d438e5, 0xcd8bbb6d, 0xdf6d0fa1, 0xe61086bc, 0x2aaa7c7f, 0xc2c17d52, 0x5987b08a, 0xcc983f70, + 0xd94d586c, 0xea70658a, 0x26b2a794, 0xc3bdbdf6, 0x592d59da, 0xcbacb0bf, 0xd3635094, 0xeeee2d9d, + 0x22a2f4f8, 0xc4df2862, 0x58c542c5, 0xcac933ae, 0xcdb72c7e, 0xf383a3e2, 0x1e7de5df, 0xc6250a18, + 0x584f7b58, 0xc9edeb50, 0xc850cab4, 0xf82a6c6a, 0x1a4608ab, 0xc78e9a1d, 0x57cc15bc, 0xc91af976, + 0xc337a8f7, 0xfcdc1342, 0x15fdf758, 0xc91af976, 0x573b2635, 0xc8507ea7, 0xbe72d9df, 0x0192155f, + 0x11a855df, 0xcac933ae, 0x569cc31b, 0xc78e9a1d, 0xba08fb09, 0x0645e9af, 0x0d47d096, 0xcc983f70, + 0x55f104dc, 0xc6d569be, 0xb6002be9, 0x0af10a22, 0x08df1a8c, 0xce86ff2a, 0x553805f2, 0xc6250a18, + 0xb25e054b, 0x0f8cfcbe, 0x0470ebdc, 0xd09441bb, 0x5471e2e6, 0xc57d965d, 0xaf279193, 0x14135c94, + 0x00000000, 0xd2bec333, 0x539eba45, 0xc4df2862, 0xac6145bb, 0x187de2a7, 0xfb8f1424, 0xd5052d97, + 0x52beac9f, 0xc449d892, 0xaa0efb24, 0x1cc66e99, 0xf720e574, 0xd76619b6, 0x51d1dc80, 0xc3bdbdf6, + 0xa833ea44, 0x20e70f32, 0xf2b82f6a, 0xd9e01006, 0x50d86e6d, 0xc33aee27, 0xa6d2a626, 0x24da0a9a, + 0xee57aa21, 0xdc71898d, 0x4fd288dc, 0xc2c17d52, 0xa5ed18e0, 0x2899e64a, 0xea0208a8, 0xdf18f0ce, + 0x4ec05432, 0xc2517e31, 0xa58480e6, 0x2c216eaa, 0xe5b9f755, 0xe1d4a2c8, 0x4da1fab5, 0xc1eb0209, + 0xa5996f52, 0x2f6bbe45, 0xe1821a21, 0xe4a2eff6, 0x4c77a88e, 0xc18e18a7, 0xa62bc71b, 0x32744493, + 0xdd5d0b08, 0xe7821d59, 0x4b418bbe, 0xc13ad060, 0xa73abd3b, 0x3536cc52, 0xd94d586c, 0xea70658a, + 0x49ffd417, 0xc0f1360b, 0xa8c4d9cb, 0x37af8159, 0xd5558381, 0xed6bf9d1, 0x48b2b335, 0xc0b15502, + 0xaac7fa0e, 0x39daf5e8, 0xd177fec6, 0xf0730342, 0x475a5c77, 0xc07b371e, 0xad415361, 0x3bb6276e, + 0xcdb72c7e, 0xf383a3e2, 0x45f704f7, 0xc04ee4b8, 0xb02d7724, 0x3d3e82ae, 0xca155d39, 0xf69bf7c9, + 0x4488e37f, 0xc02c64a6, 0xb3885772, 0x3e71e759, 0xc694ce67, 0xf9ba1651, 0x43103085, 0xc013bc39, + 0xb74d4ccb, 0x3f4eaafe, 0xc337a8f7, 0xfcdc1342, 0x418d2621, 0xc004ef3f, 0xbb771c81, 0x3fd39b5a, +}; + +/* for reference, here's the code to generate the bitreverse tables + short blocks: nbits = 4 (nfft = 64) + long blocks: nbits = 7 (nfft = 512) + +static int bitrev(int n, int nbits) +{ + int r, i; + + r = 0; + for (i = 0; i < nbits; i++) { + r <<= 1; + r |= (n & 1); + n >>= 1; + } + + return r; +} + +static void InitBitrevTable(unsigned char *out, int nbits) +{ + int i, t; + + for (i = 0; i < (1< KBD_THRESH); + + return i0; +} + +static double CalcW(double nRef, double n, double a) +{ + double i0Base, i0Curr, nTemp; + + i0Base = CalcI0(M_PI * a); + + nTemp = (n - nRef/4) / (nRef/4); + i0Curr = CalcI0( M_PI * a * sqrt(1.0 - nTemp*nTemp) ); + + return i0Curr / i0Base; +} + +void InitKBDWindow(int nmdct) +{ + int n, nRef; + double a, wBase, wCurr; + + nRef = nmdct * 2; + + / *** kbd window *** / + if (nmdct == 128) + a = 6.0; + else + a = 4.0; + + wBase = 0; + for (n = 0; n <= nRef/2; n++) + wBase += CalcW(nRef, n, a); + + / *** left *** / + wCurr = 0; + for (n = 0; n < nRef/2; n++) { + wCurr += CalcW(nRef, n, a); + kbdWindowRef[n] = sqrt(wCurr / wBase); + } + + / *** + * symmetry: + * kbd_right(n) = kbd_ldef(N_REF - 1 - n), n = [N_REF/2, N_REF - 1] + * + * wCurr = 0; + * for (n = N_REF-1; n >= N_REF/2; n--) { + * wCurr += CalcW(N_REF-n-1, a); + * kbdWindowRef[n] = sqrt(wCurr / wBase); + * } + * + *** / + return; +} +*/ +#pragma GCC diagnostic pop diff --git a/components/spotify/cspot/bell/libhelix-mp3/LICENSE.txt b/components/spotify/cspot/bell/libhelix-mp3/LICENSE.txt new file mode 100644 index 00000000..12e5372a --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/LICENSE.txt @@ -0,0 +1,30 @@ + Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + + The contents of this directory, and (except where otherwise + indicated) the directories included within this directory, are + subject to the current version of the RealNetworks Public Source + License (the "RPSL") available at RPSL.txt in this directory, unless + you have licensed the directory under the current version of the + RealNetworks Community Source License (the "RCSL") available at + RCSL.txt in this directory, in which case the RCSL will apply. You + may also obtain the license terms directly from RealNetworks. You + may not use the files in this directory except in compliance with the + RPSL or, if you have a valid RCSL with RealNetworks applicable to + this directory, the RCSL. Please see the applicable RPSL or RCSL for + the rights, obligations and limitations governing use of the contents + of the directory. + + This directory is part of the Helix DNA Technology. RealNetworks is + the developer of the Original Code and owns the copyrights in the + portions it created. + + This directory, and the directories included with this directory, are + distributed and made available on an 'AS IS' basis, WITHOUT WARRANTY + OF ANY KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY + DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, + QUIET ENJOYMENT OR NON-INFRINGEMENT. + + Technology Compatibility Kit Test Suite(s) Location: + http://www.helixcommunity.org/content/tck + diff --git a/components/spotify/cspot/bell/libhelix-mp3/RCSL.txt b/components/spotify/cspot/bell/libhelix-mp3/RCSL.txt new file mode 100644 index 00000000..a809759a --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/RCSL.txt @@ -0,0 +1,948 @@ +The RCSL is made up of a base agreement and a few Attachments. + +For Research and Development use, you agree to the terms of the +RCSL R&D License (base RCSL and Attachments A, B, and C) + +For Commercial Use (either distribution or internal commercial +deployment) of the Helix DNA with or without support for RealNetworks' +RealAudio and RealVideo Add-on Technology, you agree to the +terms of the same RCSL R&D license +and execute one or more additional Commercial Use License attachments +. + +------------------------------------------------------------------------ + + + REALNETWORKS COMMUNITY SOURCE LICENSE + +Version 1.2 (Rev. Date: January 22, 2003). + + + RECITALS + +Original Contributor has developed Specifications, Source Code +implementations and Executables of certain Technology; and + +Original Contributor desires to license the Technology to a large +community to facilitate research, innovation and product development +while maintaining compatibility of such products with the Technology as +delivered by Original Contributor; and + +Original Contributor desires to license certain Trademarks for the +purpose of branding products that are compatible with the relevant +Technology delivered by Original Contributor; and + +You desire to license the Technology and possibly certain Trademarks +from Original Contributor on the terms and conditions specified in this +License. + +In consideration for the mutual covenants contained herein, You and +Original Contributor agree as follows: + + + AGREEMENT + +*1. Introduction.* + +The RealNetworks Community Source License ("RCSL") and effective +attachments ("License") may include five distinct licenses: + +i) Research Use license -- License plus Attachments A, B and C only. + +ii) Commercial Use and Trademark License, which may be for Internal +Deployment Use or external distribution, or both -- License plus +Attachments A, B, C, and D. + +iii) Technology Compatibility Kit (TCK) license -- Attachment C. + +iv) Add-On Technology License (Executable) Commercial Use License +-Attachment F. + +v) Add-On Technology Source Code Porting and Optimization +License-Attachment G. + +The Research Use license is effective when You click and accept this +License. The TCK is effective when You click and accept this License, +unless otherwise specified in the TCK attachments. The Commercial Use +and Trademark, Add-On Technology License, and the Add-On Technology +Source Code Porting and Optimization licenses must each be signed by You +and Original Contributor to become effective. Once effective, these +licenses and the associated requirements and responsibilities are +cumulative. Capitalized terms used in this License are defined in the +Glossary. + +*2. License Grants.* + +2.1 Original Contributor Grant. + +Subject to Your compliance with Sections 3, 8.10 and Attachment A of +this License, Original Contributor grants to You a worldwide, +royalty-free, non-exclusive license, to the extent of Original +Contributor's Intellectual Property Rights covering the Original Code, +Upgraded Code and Specifications, to do the following: + +(a) Research Use License: + +(i) use, reproduce and modify the Original Code, Upgraded Code and +Specifications to create Modifications and Reformatted Specifications +for Research Use by You; + +(ii) publish and display Original Code, Upgraded Code and Specifications +with, or as part of Modifications, as permitted under Section 3.1(b) below; + +(iii) reproduce and distribute copies of Original Code and Upgraded Code +to Licensees and students for Research Use by You; + +(iv) compile, reproduce and distribute Original Code and Upgraded Code +in Executable form, and Reformatted Specifications to anyone for +Research Use by You. + +(b) Other than the licenses expressly granted in this License, Original +Contributor retains all right, title, and interest in Original Code and +Upgraded Code and Specifications. + +2.2 Your Grants. + +(a) To Other Licensees. You hereby grant to each Licensee a license to +Your Error Corrections and Shared Modifications, of the same scope and +extent as Original Contributor's licenses under Section 2.1 a) above +relative to Research Use and Attachment D relative to Commercial Use. + +(b) To Original Contributor. You hereby grant to Original Contributor a +worldwide, royalty-free, non-exclusive, perpetual and irrevocable +license, to the extent of Your Intellectual Property Rights covering +Your Error Corrections, Shared Modifications and Reformatted +Specifications, to use, reproduce, modify, display and distribute Your +Error Corrections, Shared Modifications and Reformatted Specifications, +in any form, including the right to sublicense such rights through +multiple tiers of distribution. + +(c) Other than the licenses expressly granted in Sections 2.2(a) and (b) +above, and the restrictions set forth in Section 3.1(d)(iv) below, You +retain all right, title, and interest in Your Error Corrections, Shared +Modifications and Reformatted Specifications. + +2.3 Contributor Modifications. + +You may use, reproduce, modify, display and distribute Contributor Error +Corrections, Shared Modifications and Reformatted Specifications, +obtained by You under this License, to the same scope and extent as with +Original Code, Upgraded Code and Specifications. + +2.4 Subcontracting. + +You may deliver the Source Code of Covered Code to other Licensees +having at least a Research Use license, for the sole purpose of +furnishing development services to You in connection with Your rights +granted in this License. All such Licensees must execute appropriate +documents with respect to such work consistent with the terms of this +License, and acknowledging their work-made-for-hire status or assigning +exclusive right to the work product and associated Intellectual Property +Rights to You. + +*3. Requirements and Responsibilities*. + +3.1 Research Use License. + +As a condition of exercising the rights granted under Section 2.1(a) +above, You agree to comply with the following: + +(a) Your Contribution to the Community. All Error Corrections and Shared +Modifications which You create or contribute to are automatically +subject to the licenses granted under Section 2.2 above. You are +encouraged to license all of Your other Modifications under Section 2.2 +as Shared Modifications, but are not required to do so. You agree to +notify Original Contributor of any errors in the Specification. + +(b) Source Code Availability. You agree to provide all Your Error +Corrections to Original Contributor as soon as reasonably practicable +and, in any event, prior to Internal Deployment Use or Commercial Use, +if applicable. Original Contributor may, at its discretion, post Source +Code for Your Error Corrections and Shared Modifications on the +Community Webserver. You may also post Error Corrections and Shared +Modifications on a web-server of Your choice; provided, that You must +take reasonable precautions to ensure that only Licensees have access to +such Error Corrections and Shared Modifications. Such precautions shall +include, without limitation, a password protection scheme limited to +Licensees and a click-on, download certification of Licensee status +required of those attempting to download from the server. An example of +an acceptable certification is attached as Attachment A-2. + +(c) Notices. All Error Corrections and Shared Modifications You create +or contribute to must include a file documenting the additions and +changes You made and the date of such additions and changes. You must +also include the notice set forth in Attachment A-1 in the file header. +If it is not possible to put the notice in a particular Source Code file +due to its structure, then You must include the notice in a location +(such as a relevant directory file), where a recipient would be most +likely to look for such a notice. + +(d) Redistribution. + +(i) Source. Covered Code may be distributed in Source Code form only to +another Licensee (except for students as provided below). You may not +offer or impose any terms on any Covered Code that alter the rights, +requirements, or responsibilities of such Licensee. You may distribute +Covered Code to students for use in connection with their course work +and research projects undertaken at accredited educational institutions. +Such students need not be Licensees, but must be given a copy of the +notice set forth in Attachment A-3 and such notice must also be included +in a file header or prominent location in the Source Code made available +to such students. + +(ii) Executable. You may distribute Executable version(s) of Covered +Code to Licensees and other third parties only for the purpose of +evaluation and comment in connection with Research Use by You and under +a license of Your choice, but which limits use of such Executable +version(s) of Covered Code only to that purpose. + +(iii) Modified Class, Interface and Package Naming. In connection with +Research Use by You only, You may use Original Contributor's class, +Interface and package names only to accurately reference or invoke the +Source Code files You modify. Original Contributor grants to You a +limited license to the extent necessary for such purposes. + +(iv) You expressly agree that any distribution, in whole or in part, of +Modifications developed by You shall only be done pursuant to the terms +and conditions of this License. + +(e) Extensions. + +(i) Covered Code. You may not include any Source Code of Community Code +in any Extensions. You may include the compiled Header Files of +Community Code in an Extension provided that Your use of the Covered +Code, including Heading Files, complies with the Commercial Use License, +the TCK and all other terms of this License. + +(ii) Publication. No later than the date on which You first distribute +such Extension for Commercial Use, You must publish to the industry, on +a non-confidential basis and free of all copyright restrictions with +respect to reproduction and use, an accurate and current specification +for any Extension. In addition, You must make available an appropriate +test suite, pursuant to the same rights as the specification, +sufficiently detailed to allow any third party reasonably skilled in the +technology to produce implementations of the Extension compatible with +the specification. Such test suites must be made available as soon as +reasonably practicable but, in no event, later than ninety (90) days +after Your first Commercial Use of the Extension. You must use +reasonable efforts to promptly clarify and correct the specification and +the test suite upon written request by Original Contributor. + +(iii) Open. You agree to refrain from enforcing any Intellectual +Property Rights You may have covering any interface(s) of Your +Extension, which would prevent the implementation of such interface(s) +by Original Contributor or any Licensee. This obligation does not +prevent You from enforcing any Intellectual Property Right You have that +would otherwise be infringed by an implementation of Your Extension. + +(iv) Interface Modifications and Naming. You may not modify or add to +the GUID space * * "xxxxxxxx-0901-11d1-8B06-00A024406D59" or any other +GUID space designated by Original Contributor. You may not modify any +Interface prefix provided with the Covered Code or any other prefix +designated by Original Contributor.* * + +* * + +(f) You agree that any Specifications provided to You by Original +Contributor are confidential and proprietary information of Original +Contributor. You must maintain the confidentiality of the Specifications +and may not disclose them to any third party without Original +Contributor's prior written consent. You may only use the Specifications +under the terms of this License and only for the purpose of implementing +the terms of this License with respect to Covered Code. You agree not +use, copy or distribute any such Specifications except as provided in +writing by Original Contributor. + +3.2 Commercial Use License. + +You may not make Commercial Use of any Covered Code unless You and +Original Contributor have executed a copy of the Commercial Use and +Trademark License attached as Attachment D. + +*4. Versions of the License.* + +4.1 License Versions. + +Original Contributor may publish revised versions of the License from +time to time. Each version will be given a distinguishing version number. + +4.2 Effect. + +Once a particular version of Covered Code has been provided under a +version of the License, You may always continue to use such Covered Code +under the terms of that version of the License. You may also choose to +use such Covered Code under the terms of any subsequent version of the +License. No one other than Original Contributor has the right to +promulgate License versions. + +4.3 Multiple-Licensed Code. + +Original Contributor may designate portions of the Covered Code as +"Multiple-Licensed." "Multiple-Licensed" means that the Original +Contributor permits You to utilize those designated portions of the +Covered Code under Your choice of this License or the alternative +license(s), if any, specified by the Original Contributor in an +Attachment to this License. + +*5. Disclaimer of Warranty.* + +5.1 COVERED CODE PROVIDED AS IS. + +COVERED CODE IS PROVIDED UNDER THIS LICENSE "AS IS," WITHOUT WARRANTY OF +ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, +WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT +FOR A PARTICULAR PURPOSE OR NON-INFRINGING. YOU AGREE TO BEAR THE ENTIRE +RISK IN CONNECTION WITH YOUR USE AND DISTRIBUTION OF COVERED CODE UNDER +THIS LICENSE. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART +OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER +EXCEPT SUBJECT TO THIS DISCLAIMER. + +5.2 Not Designed for High Risk Activities. + +You acknowledge that Original Code, Upgraded Code and Specifications are +not designed or intended for use in high risk activities including, but +not limited to: (i) on-line control of aircraft, air traffic, aircraft +navigation or aircraft communications; or (ii) in the design, +construction, operation or maintenance of any nuclear facility. Original +Contributor disclaims any express or implied warranty of fitness for +such uses. + +*6. Termination.* + +6.1 By You. + +You may terminate this Research Use license at anytime by providing +written notice to Original Contributor. + +6.2 By Original Contributor. + +This License and the rights granted hereunder will terminate: + +(i) automatically if You fail to comply with the terms of this License +and fail to cure such breach within 30 days of receipt of written notice +of the breach; + +(ii) immediately in the event of circumstances specified in Sections 7.1 +and 8.4; or + +(iii) at Original Contributor's discretion upon any action initiated by +You (including by cross-claim or counter claim) alleging that use or +distribution by Original Contributor or any Licensee, of Original Code, +Upgraded Code, Error Corrections, Shared Modifications or Specifications +infringe a patent owned or controlled by You. + +6.3 Effective of Termination. + +Upon termination, You agree to discontinue use of and destroy all copies +of Covered Code in Your possession. All sublicenses to the Covered Code +which You have properly granted shall survive any termination of this +License. Provisions that, by their nature, should remain in effect +beyond the termination of this License shall survive including, without +limitation, Sections 2.2, 3, 5, 7 and 8. + +6.4 No Compensation. + +Each party waives and releases the other from any claim to compensation +or indemnity for permitted or lawful termination of the business +relationship established by this License. + +*7. Liability.* + +7.1 Infringement. Should any of the Original Code, Upgraded Code, TCK or +Specifications ("Materials") become the subject of a claim of +infringement, Original Contributor may, at its sole option, (i) attempt +to procure the rights necessary for You to continue using the Materials, +(ii) modify the Materials so that they are no longer infringing, or +(iii) terminate Your right to use the Materials, immediately upon +written notice, and refund to You the amount, if any, having then +actually been paid by You to Original Contributor for the Original Code, +Upgraded Code and TCK, depreciated on a straight line, five year basis. + +7.2 LIMITATION OF LIABILITY. TO THE FULL EXTENT ALLOWED BY APPLICABLE +LAW, ORIGINAL CONTRIBUTOR'S LIABILITY TO YOU FOR CLAIMS RELATING TO THIS +LICENSE, WHETHER FOR BREACH OR IN TORT, SHALL BE LIMITED TO ONE HUNDRED +PERCENT (100%) OF THE AMOUNT HAVING THEN ACTUALLY BEEN PAID BY YOU TO +ORIGINAL CONTRIBUTOR FOR ALL COPIES LICENSED HEREUNDER OF THE PARTICULAR +ITEMS GIVING RISE TO SUCH CLAIM, IF ANY, DURING THE TWELVE MONTHS +PRECEDING THE CLAIMED BREACH. IN NO EVENT WILL YOU (RELATIVE TO YOUR +SHARED MODIFICATIONS OR ERROR CORRECTIONS) OR ORIGINAL CONTRIBUTOR BE +LIABLE FOR ANY INDIRECT, PUNITIVE, SPECIAL, INCIDENTAL OR CONSEQUENTIAL +DAMAGES IN CONNECTION WITH OR RISING OUT OF THIS LICENSE (INCLUDING, +WITHOUT LIMITATION, LOSS OF PROFITS, USE, DATA, OR OTHER ECONOMIC +ADVANTAGE), HOWEVER IT ARISES AND ON ANY THEORY OF LIABILITY, WHETHER IN +AN ACTION FOR CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE) +OR OTHERWISE, WHETHER OR NOT YOU OR ORIGINAL CONTRIBUTOR HAS BEEN +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE AND NOTWITHSTANDING THE +FAILURE OF ESSENTIAL PURPOSE OF ANY REMEDY. + +*8. Miscellaneous.* + +8.1 Trademark. + +You shall not use any Trademark unless You and Original Contributor +execute a copy of the Commercial Use and Trademark License Agreement +attached hereto as Attachment D. Except as expressly provided in the +License, You are granted no right, title or license to, or interest in, +any Trademarks. Whether or not You and Original Contributor enter into +the Trademark License, You agree not to (i) challenge Original +Contributor's ownership or use of Trademarks; (ii) attempt to register +any Trademarks, or any mark or logo substantially similar thereto; or +(iii) incorporate any Trademarks into Your own trademarks, product +names, service marks, company names, or domain names. + +8.2 Integration. + +This License represents the complete agreement concerning the subject +matter hereof. + +8.3 Assignment. + +Original Contributor may assign this License, and its rights and +obligations hereunder, in its sole discretion. You may assign the +Research Use portions of this License and the TCK license to a third +party upon prior written notice to Original Contributor (which may be +provided electronically via the Community Web-Server). You may not +assign the Commercial Use and Trademark license, the Add-On Technology +License, or the Add-On Technology Source Code Porting License, including +by way of merger (regardless of whether You are the surviving entity) or +acquisition, without Original Contributor's prior written consent. + +8.4 Severability. + +If any provision of this License is held to be unenforceable, such +provision shall be reformed only to the extent necessary to make it +enforceable. Notwithstanding the foregoing, if You are prohibited by law +from fully and specifically complying with Sections 2.2 or 3, this +License will immediately terminate and You must immediately discontinue +any use of Covered Code. + +8.5 Governing Law. + +This License shall be governed by the laws of the United States and the +State of Washington, as applied to contracts entered into and to be +performed in Washington between Washington residents. The application of +the United Nations Convention on Contracts for the International Sale of +Goods is expressly excluded. You agree that the state and federal courts +located in Seattle, Washington have exclusive jurisdiction over any +claim relating to the License, including contract and tort claims. + +8.6 Dispute Resolution. + +a) Arbitration. Any dispute arising out of or relating to this License +shall be finally settled by arbitration as set out herein, except that +either party may bring any action, in a court of competent jurisdiction +(which jurisdiction shall be exclusive), with respect to any dispute +relating to such party's Intellectual Property Rights or with respect to +Your compliance with the TCK license. Arbitration shall be administered: +(i) by the American Arbitration Association (AAA), (ii) in accordance +with the rules of the United Nations Commission on International Trade +Law (UNCITRAL) (the "Rules") in effect at the time of arbitration as +modified herein; and (iii) the arbitrator will apply the substantive +laws of Washington and the United States. Judgment upon the award +rendered by the arbitrator may be entered in any court having +jurisdiction to enforce such award. + +b) Arbitration language, venue and damages. All arbitration proceedings +shall be conducted in English by a single arbitrator selected in +accordance with the Rules, who must be fluent in English and be either a +retired judge or practicing attorney having at least ten (10) years +litigation experience and be reasonably familiar with the technology +matters relative to the dispute. Unless otherwise agreed, arbitration +venue shall be in Seattle, Washington. The arbitrator may award monetary +damages only and nothing shall preclude either party from seeking +provisional or emergency relief from a court of competent jurisdiction. +The arbitrator shall have no authority to award damages in excess of +those permitted in this License and any such award in excess is void. +All awards will be payable in U.S. dollars and may include, for the +prevailing party (i) pre-judgment award interest, (ii) reasonable +attorneys' fees incurred in connection with the arbitration, and (iii) +reasonable costs and expenses incurred in enforcing the award. The +arbitrator will order each party to produce identified documents and +respond to no more than twenty-five single question interrogatories. + +8.7 Construction. + +Any law or regulation, which provides that the language of a contract +shall be construed against the drafter, shall not apply to this License. + +8.8 U.S. Government End Users. + +The Covered Code is a "commercial item," as that term is defined in 48 +C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" +and "commercial computer software documentation," as such terms are used +in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and +48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government +End Users acquire Covered Code with only those rights set forth herein. +You agree to pass this notice to our licensees. + +8.9 Marketing Activities. + +Licensee hereby grants Original Contributor a non-exclusive, +non-transferable, limited license to use the Licensee's company name and +logo ("Licensee Marks") in any presentations, press releases, or +marketing materials solely for the purpose of identifying Licensee as a +member of the Helix Community. Licensee shall provide samples of +Licensee Marks to Original Contributor upon request by Original +Contributor. Original Contributor acknowledges that the Licensee Marks +are the trademarks of Licensee. Original Contributor shall not use the +Licensee Marks in a way that may imply that Original Contributor is an +agency or branch of Licensee. Original Contributor understands and +agrees that the use of any Licensee Marks in connection with this +Agreement shall not create any right, title or interest, in, or to the +Licensee Marks or any Licensee trademarks and that all such use and +goodwill associated with any such trademarks will inure to the benefit +of Licensee. Further the Original Contributor will stop usage of the +Licensee Marks upon Licensee's request. + +8.10 Press Announcements. + +You may make press announcements or other public statements regarding +this License without the prior written consent of the Original +Contributor, if Your statement is limited to announcing the licensing of +the Covered Code or the availability of Your Product and its +compatibility with the Covered Code. All other public announcements +regarding this license require the prior written consent of the Original +Contributor. Consent requests are welcome at press@helixcommunity.org. + +8.11 International Use. + +a) Export/Import laws. Covered Code is subject to U.S. export control +laws and may be subject to export or import regulations in other +countries. Each party agrees to comply strictly with all such laws and +regulations and acknowledges their responsibility to obtain such +licenses to export, re-export, or import as may be required. You agree +to pass these obligations to Your licensees. + +b) Intellectual Property Protection. Due to limited intellectual +property protection and enforcement in certain countries, You agree not +to redistribute the Original Code, Upgraded Code, TCK and Specifications +to any country on the list of restricted countries on the Community Web +Server. + +8.12 Language. + +This License is in the English language only, which language shall be +controlling in all respects, and all versions of this License in any +other language shall be for accommodation only and shall not be binding +on the parties to this License. All communications and notices made or +given pursuant to this License, and all documentation and support to be +provided, unless otherwise noted, shall be in the English language. + +PLEASE READ THE TERMS OF THIS LICENSE CAREFULLY. BY CLICKING ON THE +"ACCEPT" BUTTON BELOW YOU ARE ACCEPTING AND AGREEING TO THE TERMS AND +CONDITIONS OF THIS LICENSE WITH REALNETWORKS, INC. IF YOU ARE AGREEING +TO THIS LICENSE ON BEHALF OF A COMPANY, YOU REPRESENT THAT YOU ARE +AUTHORIZED TO BIND THE COMPANY TO SUCH A LICENSE. WHETHER YOU ARE ACTING +ON YOUR OWN BEHALF, OR REPRESENTING A COMPANY, YOU MUST BE OF MAJORITY +AGE AND BE OTHERWISE COMPETENT TO ENTER INTO CONTRACTS. IF YOU DO NOT +MEET THIS CRITERIA OR YOU DO NOT AGREE TO ANY OF THE TERMS AND +CONDITIONS OF THIS LICENSE, CLICK ON THE REJECT BUTTON TO EXIT. + + + GLOSSARY + +1. *"Added Value"* means code which: + +(i) has a principal purpose which is substantially different from that +of the stand-alone Technology; + +(ii) represents a significant functional and value enhancement to the +Technology; + +(iii) operates in conjunction with the Technology; and + +(iv) is not marketed as a technology which replaces or substitutes for +the Technology + +2. "*Applicable Patent Rights*" mean: (a) in the case where Original +Contributor is the grantor of rights, claims of patents that (i) are now +or hereafter acquired, owned by or assigned to Original Contributor and +(ii) are necessarily infringed by using or making the Original Code or +Upgraded Code, including Modifications provided by Original Contributor, +alone and not in combination with other software or hardware; and (b) in +the case where Licensee is the grantor of rights, claims of patents that +(i) are now or hereafter acquired, owned by or assigned to Licensee and +(ii) are infringed (directly or indirectly) by using or making +Licensee's Modifications or Error Corrections, taken alone or in +combination with Covered Code. + +3. "*Application Programming Interfaces (APIs)"* means the interfaces, +associated header files, service provider interfaces, and protocols that +enable a device, application, Operating System, or other program to +obtain services from or make requests of (or provide services in +response to requests from) other programs, and to use, benefit from, or +rely on the resources, facilities, and capabilities of the relevant +programs using the APIs. APIs includes the technical documentation +describing the APIs, the Source Code constituting the API, and any +Header Files used with the APIs. + +4. "*Commercial Use*" means any use (internal or external), copying, +sublicensing or distribution (internal or external), directly or +indirectly of Covered Code by You other than Your Research Use of +Covered Code within Your business or organization or in conjunction with +other Licensees with equivalent Research Use rights. Commercial Use +includes any use of the Covered Code for direct or indirect commercial +or strategic gain, advantage or other business purpose. Any Commercial +Use requires execution of Attachment D by You and Original Contributor. + +5. "*Community Code*" means the Original Code, Upgraded Code, Error +Corrections, Shared Modifications, or any combination thereof. + +6. "*Community Webserver(s)"* means the webservers designated by +Original Contributor for access to the Original Code, Upgraded Code, TCK +and Specifications and for posting Error Corrections and Shared +Modifications. + +7. "*Compliant Covered Code*" means Covered Code that complies with the +requirements of the TCK. + +8. "*Contributor*" means each Licensee that creates or contributes to +the creation of any Error Correction or Shared Modification. + +9. "*Covered Code*" means the Original Code, Upgraded Code, +Modifications, or any combination thereof. + +10. "*Error Correction*" means any change made to Community Code which +conforms to the Specification and corrects the adverse effect of a +failure of Community Code to perform any function set forth in or +required by the Specifications. + +11. "*Executable*" means Covered Code that has been converted from +Source Code to the preferred form for execution by a computer or digital +processor (e.g. binary form). + +12. "*Extension(s)"* means any additional Interfaces developed by or for +You which: (i) are designed for use with the Technology; (ii) constitute +an API for a library of computing functions or services; and (iii) are +disclosed or otherwise made available to third party software developers +for the purpose of developing software which invokes such additional +Interfaces. The foregoing shall not apply to software developed by Your +subcontractors to be exclusively used by You. + +13. "*Header File(s)"* means that portion of the Source Code that +provides the names and types of member functions, data members, class +definitions, and interface definitions necessary to implement the APIs +for the Covered Code. Header Files include, files specifically +designated by Original Contributor as Header Files. Header Files do not +include the code necessary to implement the functionality underlying the +Interface. + +14. *"Helix DNA Server Technology"* means the program(s) that implement +the Helix Universal Server streaming engine for the Technology as +defined in the Specification. + +15. *"Helix DNA Client Technology"* means the Covered Code that +implements the RealOne Player engine as defined in the Specification. + +16. *"Helix DNA Producer Technology"* means the Covered Code that +implements the Helix Producer engine as defined in the Specification. + +17. *"Helix DNA Technology"* means the Helix DNA Server Technology, the +Helix DNA Client Technology, the Helix DNA Producer Technology and other +Helix technologies designated by Original Contributor. + +18. "*Intellectual Property Rights*" means worldwide statutory and +common law rights associated solely with (i) Applicable Patent Rights; +(ii) works of authorship including copyrights, copyright applications, +copyright registrations and "moral rights"; (iii) the protection of +trade and industrial secrets and confidential information; and (iv) +divisions, continuations, renewals, and re-issuances of the foregoing +now existing or acquired in the future. + +19. *"Interface*" means interfaces, functions, properties, class +definitions, APIs, Header Files, GUIDs, V-Tables, and/or protocols +allowing one piece of software, firmware or hardware to communicate or +interoperate with another piece of software, firmware or hardware. + +20. "*Internal Deployment Use*" means use of Compliant Covered Code +(excluding Research Use) within Your business or organization only by +Your employees and/or agents on behalf of Your business or organization, +but not to provide services, including content distribution, to third +parties, subject to execution of Attachment D by You and Original +Contributor, if required. + +21. "*Licensee*" means any party that has entered into and has in effect +a version of this License with Original Contributor. + +22. "*MIME type*" means a description of what type of media or other +content is in a file, including by way of example but not limited to +'audio/x-pn-realaudio-plugin.' + +23. "*Modification(s)"* means (i) any addition to, deletion from and/or +change to the substance and/or structure of the Covered Code, including +Interfaces; (ii) the combination of any Covered Code and any previous +Modifications; (iii) any new file or other representation of computer +program statements that contains any portion of Covered Code; and/or +(iv) any new Source Code implementing any portion of the Specifications. + +24. "*MP3 Patents*" means any patents necessary to make, use or sell +technology implementing any portion of the specification developed by +the Moving Picture Experts Group known as MPEG-1 Audio Layer-3 or MP3, +including but not limited to all past and future versions, profiles, +extensions, parts and amendments relating to the MP3 specification. + +25. "*MPEG-4 Patents*" means any patents necessary to make, use or sell +technology implementing any portion of the specification developed by +the Moving Pictures Experts Group known as MPEG-4, including but not +limited to all past and future versions, profiles, extensions, parts and +amendments relating to the MPEG-4 specification. + +26. "*Original Code*" means the initial Source Code for the Technology +as described on the Community Web Server. + +27. "*Original Contributor*" means RealNetworks, Inc., its affiliates +and its successors and assigns. + +28. "*Original Contributor MIME Type*" means the MIME registry, browser +preferences, or local file/protocol associations invoking any Helix DNA +Client-based application, including the RealOne Player, for playback of +RealAudio, RealVideo, other RealMedia MIME types or datatypes (e.g., +.ram, .rnx, .rpm, .ra, .rm, .rp, .rt, .rf, .prx, .mpe, .rmp, .rmj, .rav, +.rjs, .rmx, .rjt, .rms), and any other Original Contributor-specific or +proprietary MIME types that Original Contributor may introduce in the +future. + +29. "*Personal Use*" means use of Covered Code by an individual solely +for his or her personal, private and non-commercial purposes. An +individual's use of Covered Code in his or her capacity as an officer, +employee, member, independent contractor or agent of a corporation, +business or organization (commercial or non-commercial) does not qualify +as Personal Use. + +30. "*RealMedia File Format*" means the file format designed and +developed by RealNetworks for storing multimedia data and used to store +RealAudio and RealVideo encoded streams. Valid RealMedia File Format +extensions include: .rm, .rmj, .rmc, .rmvb, .rms. + +31. "*RCSL Webpage*" means the RealNetworks Community Source License +webpage located at https://www.helixcommunity.org/content/rcsl or such +other URL that Original Contributor may designate from time to time. + +32. "*Reformatted Specifications*" means any revision to the +Specifications which translates or reformats the Specifications (as for +example in connection with Your documentation) but which does not alter, +subset or superset * *the functional or operational aspects of the +Specifications. + +33. "*Research Use*" means use and distribution of Covered Code only for +Your Personal Use, research or development use and expressly excludes +Internal Deployment Use and Commercial Use. Research Use also includes +use of Covered Code to teach individuals how to use Covered Code. + +34. "*Shared Modifications*" means Modifications that You distribute or +use for a Commercial Use, in addition to any Modifications provided by +You, at Your option, pursuant to Section 2.2, or received by You from a +Contributor pursuant to Section 2.3. + +35. "*Source Code*" means the preferred form of the Covered Code for +making modifications to it, including all modules it contains, plus any +associated interface definition files, scripts used to control +compilation and installation of an Executable, or source code +differential comparisons against either the Original Code or another +well known, available Covered Code of the Contributor's choice. The +Source Code can be in a compressed or archival form, provided the +appropriate decompression or de-archiving software is widely available +for no charge. + +36. "*Specifications*" means the specifications for the Technology and +other documentation, as designated on the Community Web Server, as may +be revised by Original Contributor from time to time. + +37. "*Trademarks*" means Original Contributor's trademarks and logos, +including, but not limited to, RealNetworks, RealAudio, RealVideo, +RealOne, RealSystem, SureStream, Helix, Helix DNA and other trademarks +whether now used or adopted in the future. + +38. "*Technology*" means the technology described in Attachment B, and +Upgrades. + +39. "*Technology Compatibility Kit"* or *"TCK*" means the test programs, +procedures, acceptance criteria and/or other requirements, designated by +Original Contributor for use in verifying compliance of Covered Code +with the Specifications, in conjunction with the Original Code and +Upgraded Code. Original Contributor may, in its sole discretion and from +time to time, revise a TCK to correct errors and/or omissions and in +connection with Upgrades. + +40. "*Upgrade(s)"* means new versions of Technology designated +exclusively by Original Contributor as an "Upgrade" and released by +Original Contributor from time to time under the terms of the License. + +41. "*Upgraded Code*" means the Source Code and/or Executables for +Upgrades, possibly including Modifications made by Contributors. + +42. *"User's Guide"* means the users guide for the TCK which Original +Contributor makes available to You to provide direction in how to run +the TCK and properly interpret the results, as may be revised by +Original Contributor from time to time. + +43. "*You(r)*" means an individual, or a legal entity acting by and +through an individual or individuals, exercising rights either under +this License or under a future version of this License issued pursuant +to Section 4.1. For legal entities, "You(r)" includes any entity that by +majority voting interest controls, is controlled by, or is under common +control with You. + +44. "*Your Products*" means any (i) hardware products You distribute +integrating the Covered Code; (ii) any software products You distribute +with the Covered Code that utilize the APIs of the Covered Code; or +(iii) any services You provide using the Covered Code. + + + ATTACHMENT A + +REQUIRED NOTICES + + + ATTACHMENT A-1 + +REQUIRED IN ALL CASES + +Notice to be included in header file of all Error Corrections and Shared +Modifications: + +Portions Copyright 1994-2003 © RealNetworks, Inc. All rights reserved. + +The contents of this file, and the files included with this file, are +subject to the current version of RealNetworks Community Source License +Version 1.1 (the "License"). You may not use this file except in +compliance with the License executed by both You and RealNetworks. You +may obtain a copy of the License at * +https://www.helixcommunity.org/content/rcsl.* You may also obtain a copy +of the License by contacting RealNetworks directly. Please see the +License for the rights, obligations and limitations governing use of the +contents of the file. + +This file is part of the Helix DNA technology. RealNetworks, Inc., is +the developer of the Original code and owns the copyrights in the +portions it created. + +This file, and the files included with this file, are distributed on an +'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, +AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT +LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + +Contributor(s): + +_______________________________________________ + +Technology Compatibility Kit Test Suite(s) Location: + +________________________________ + + + ATTACHMENT A-2 + +SAMPLE LICENSEE CERTIFICATION + +"By clicking the `Agree' button below, You certify that You are a +Licensee in good standing under the RealNetworks Community Source +License, ("License") and that Your access, use and distribution of code +and information You may obtain at this site is subject to the License. +If You are not a Licensee under the RealNetworks Community Source +License You agree not to download, copy or use the Helix DNA technology. + + + ATTACHMENT A-3 + +REQUIRED STUDENT NOTIFICATION + +"This software and related documentation has been obtained by Your +educational institution subject to the RealNetworks Community Source +License. You have been provided access to the software and related +documentation for use only in connection with your course work and +research activities as a matriculated student of Your educational +institution. Any other use is expressly prohibited. + +THIS SOFTWARE AND RELATED DOCUMENTATION CONTAINS PROPRIETARY MATERIAL OF +REALNETWORKS, INC, WHICH ARE PROTECTED BY VARIOUS INTELLECTUAL PROPERTY +RIGHTS. + +You may not use this file except in compliance with the License. You may +obtain a copy of the License on the web at +https://www.helixcommunity.org/content/rcsl. + +* +* + + + ATTACHMENT B + +Description of Technology + +Helix DNA, which consists of Helix DNA Client, Helix DNA Server and +Helix DNA Producer. + +Description of "Technology" + +Helix DNA Technology v1.0 as described on the Community Web Server. + + + ATTACHMENT C + +TECHNOLOGY COMPATIBILITY KIT LICENSE + +The following license is effective for the *Helix DNA* Technology +Compatibility Kit - as described on the Community Web Server. The +Technology Compatibility Kit(s) for the Technology specified in +Attachment B may be accessed at the Community Web Server. + +1. TCK License. + +1.1 Grants to use TCK + +Subject to the terms and restrictions set forth below and the +RealNetworks Community Source License, and the Research Use license, +Original Contributor grants to You a worldwide, non-exclusive, +non-transferable license, to the extent of Original Contributor's +Intellectual Property Rights in the TCK (without the right to +sublicense), to use the TCK to develop and test Covered Code. + +1.2 TCK Use Restrictions. + +You are not authorized to create derivative works of the TCK or use the +TCK to test any implementation of the Specification that is not Covered +Code. You may not publish Your test results or make claims of +comparative compatibility with respect to other implementations of the +Specification. In consideration for the license grant in Section 1.1 +above You agree not to develop Your own tests that are intended to +validate conformation with the Specification. + +2. Test Results. + +You agree to provide to Original Contributor or the third party test +facility if applicable, Your test results that demonstrate that Covered +Code is Compliant Covered Code and that Original Contributor may publish +or otherwise distribute such test results. + +PLEASE READ THE TERMS OF THIS LICENSE CAREFULLY. BY CLICKING ON THE +"ACCEPT" BUTTON BELOW YOU ARE ACCEPTING AND AGREEING TO THE TERMS AND +CONDITIONS OF THIS LICENSE WITH THE ORIGINAL CONTRIBUTOR, REALNETWORKS, +INC. IF YOU ARE AGREEING TO THIS LICENSE ON BEHALF OF A COMPANY, YOU +REPRESENT THAT YOU ARE AUTHORIZED TO BIND THE COMPANY TO SUCH A LICENSE. +WHETHER YOU ARE ACTING ON YOUR OWN BEHALF, OR REPRESENTING A COMPANY, +YOU MUST BE OF MAJORITY AGE AND BE OTHERWISE COMPETENT TO ENTER INTO +CONTRACTS. IF YOU DO NOT MEET THIS CRITERIA OR YOU DO NOT AGREE TO ANY +OF THE TERMS AND CONDITIONS OF THIS LICENSE, CLICK ON THE REJECT BUTTON +TO EXIT. + +*ACCEPT / REJECT +* + +* +* + +*To agree to the R&D/academic terms of this license, please register + on the site -- +you will then be given a chance to agree to the clickwrap RCSL + +R&D License + +and gain access to the RCSL-licensed source code. To build or deploy +commercial applications based on the RCSL, you will need to agree to the +Commercial Use license attachments +* + + + diff --git a/components/spotify/cspot/bell/libhelix-mp3/RPSL.txt b/components/spotify/cspot/bell/libhelix-mp3/RPSL.txt new file mode 100644 index 00000000..d040a452 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/RPSL.txt @@ -0,0 +1,518 @@ +RealNetworks Public Source License Version 1.0 +(Rev. Date October 28, 2002) + +1. General Definitions. This License applies to any program or other work which +RealNetworks, Inc., or any other entity that elects to use this license, +("Licensor") makes publicly available and which contains a notice placed by +Licensor identifying such program or work as "Original Code" and stating that it +is subject to the terms of this RealNetworks Public Source License version 1.0 +(or subsequent version thereof) ("License"). You are not required to accept this +License. However, nothing else grants You permission to use, copy, modify or +distribute the software or its derivative works. These actions are prohibited by +law if You do not accept this License. Therefore, by modifying, copying or +distributing the software (or any work based on the software), You indicate your +acceptance of this License to do so, and all its terms and conditions. In +addition, you agree to the terms of this License by clicking the Accept button +or downloading the software. As used in this License: + +1.1 "Applicable Patent Rights" mean: (a) in the case where Licensor is the +grantor of rights, claims of patents that (i) are now or hereafter acquired, +owned by or assigned to Licensor and (ii) are necessarily infringed by using or +making the Original Code alone and not in combination with other software or +hardware; and (b) in the case where You are the grantor of rights, claims of +patents that (i) are now or hereafter acquired, owned by or assigned to You and +(ii) are infringed (directly or indirectly) by using or making Your +Modifications, taken alone or in combination with Original Code. + +1.2 "Compatible Source License" means any one of the licenses listed on Exhibit +B or at https://www.helixcommunity.org/content/complicense or other licenses +specifically identified by Licensor in writing. Notwithstanding any term to the +contrary in any Compatible Source License, any code covered by any Compatible +Source License that is used with Covered Code must be made readily available in +Source Code format for royalty-free use under the terms of the Compatible Source +License or this License. + +1.3 "Contributor" means any person or entity that creates or contributes to the +creation of Modifications. + +1.4 "Covered Code" means the Original Code, Modifications, the combination of +Original Code and any Modifications, and/or any respective portions thereof. + +1.5 "Deploy" means to use, sublicense or distribute Covered Code other than for +Your internal research and development (R&D) and/or Personal Use, and includes +without limitation, any and all internal use or distribution of Covered Code +within Your business or organization except for R&D use and/or Personal Use, as +well as direct or indirect sublicensing or distribution of Covered Code by You +to any third party in any form or manner. + +1.6 "Derivative Work" means either the Covered Code or any derivative work under +United States copyright law, and including any work containing or including any +portion of the Covered Code or Modifications, either verbatim or with +modifications and/or translated into another language. Derivative Work also +includes any work which combines any portion of Covered Code or Modifications +with code not otherwise governed by the terms of this License. + +1.7 "Externally Deploy" means to Deploy the Covered Code in any way that may be +accessed or used by anyone other than You, used to provide any services to +anyone other than You, or used in any way to deliver any content to anyone other +than You, whether the Covered Code is distributed to those parties, made +available as an application intended for use over a computer network, or used to +provide services or otherwise deliver content to anyone other than You. + +1.8. "Interface" means interfaces, functions, properties, class definitions, +APIs, header files, GUIDs, V-Tables, and/or protocols allowing one piece of +software, firmware or hardware to communicate or interoperate with another piece +of software, firmware or hardware. + +1.9 "Modifications" mean any addition to, deletion from, and/or change to, the +substance and/or structure of the Original Code, any previous Modifications, the +combination of Original Code and any previous Modifications, and/or any +respective portions thereof. When code is released as a series of files, a +Modification is: (a) any addition to or deletion from the contents of a file +containing Covered Code; and/or (b) any new file or other representation of +computer program statements that contains any part of Covered Code. + +1.10 "Original Code" means (a) the Source Code of a program or other work as +originally made available by Licensor under this License, including the Source +Code of any updates or upgrades to such programs or works made available by +Licensor under this License, and that has been expressly identified by Licensor +as such in the header file(s) of such work; and (b) the object code compiled +from such Source Code and originally made available by Licensor under this +License. + +1.11 "Personal Use" means use of Covered Code by an individual solely for his or +her personal, private and non-commercial purposes. An individual's use of +Covered Code in his or her capacity as an officer, employee, member, independent +contractor or agent of a corporation, business or organization (commercial or +non-commercial) does not qualify as Personal Use. + +1.12 "Source Code" means the human readable form of a program or other work that +is suitable for making modifications to it, including all modules it contains, +plus any associated interface definition files, scripts used to control +compilation and installation of an executable (object code). + +1.13 "You" or "Your" means an individual or a legal entity exercising rights +under this License. For legal entities, "You" or "Your" includes any entity +which controls, is controlled by, or is under common control with, You, where +"control" means (a) the power, direct or indirect, to cause the direction or +management of such entity, whether by contract or otherwise, or (b) ownership of +fifty percent (50%) or more of the outstanding shares or beneficial ownership of +such entity. + +2. Permitted Uses; Conditions & Restrictions. Subject to the terms and +conditions of this License, Licensor hereby grants You, effective on the date +You accept this License (via downloading or using Covered Code or otherwise +indicating your acceptance of this License), a worldwide, royalty-free, +non-exclusive copyright license, to the extent of Licensor's copyrights cover +the Original Code, to do the following: + +2.1 You may reproduce, display, perform, modify and Deploy Covered Code, +provided that in each instance: + +(a) You must retain and reproduce in all copies of Original Code the copyright +and other proprietary notices and disclaimers of Licensor as they appear in the +Original Code, and keep intact all notices in the Original Code that refer to +this License; + +(b) You must include a copy of this License with every copy of Source Code of +Covered Code and documentation You distribute, and You may not offer or impose +any terms on such Source Code that alter or restrict this License or the +recipients' rights hereunder, except as permitted under Section 6; + +(c) You must duplicate, to the extent it does not already exist, the notice in +Exhibit A in each file of the Source Code of all Your Modifications, and cause +the modified files to carry prominent notices stating that You changed the files +and the date of any change; + +(d) You must make Source Code of all Your Externally Deployed Modifications +publicly available under the terms of this License, including the license grants +set forth in Section 3 below, for as long as you Deploy the Covered Code or +twelve (12) months from the date of initial Deployment, whichever is longer. You +should preferably distribute the Source Code of Your Deployed Modifications +electronically (e.g. download from a web site); and + +(e) if You Deploy Covered Code in object code, executable form only, You must +include a prominent notice, in the code itself as well as in related +documentation, stating that Source Code of the Covered Code is available under +the terms of this License with information on how and where to obtain such +Source Code. You must also include the Object Code Notice set forth in Exhibit A +in the "about" box or other appropriate place where other copyright notices are +placed, including any packaging materials. + +2.2 You expressly acknowledge and agree that although Licensor and each +Contributor grants the licenses to their respective portions of the Covered Code +set forth herein, no assurances are provided by Licensor or any Contributor that +the Covered Code does not infringe the patent or other intellectual property +rights of any other entity. Licensor and each Contributor disclaim any liability +to You for claims brought by any other entity based on infringement of +intellectual property rights or otherwise. As a condition to exercising the +rights and licenses granted hereunder, You hereby assume sole responsibility to +secure any other intellectual property rights needed, if any. For example, if a +third party patent license is required to allow You to make, use, sell, import +or offer for sale the Covered Code, it is Your responsibility to acquire such +license(s). + +2.3 Subject to the terms and conditions of this License, Licensor hereby grants +You, effective on the date You accept this License (via downloading or using +Covered Code or otherwise indicating your acceptance of this License), a +worldwide, royalty-free, perpetual, non-exclusive patent license under +Licensor's Applicable Patent Rights to make, use, sell, offer for sale and +import the Covered Code, provided that in each instance you comply with the +terms of this License. + +3. Your Grants. In consideration of, and as a condition to, the licenses granted +to You under this License: + +(a) You grant to Licensor and all third parties a non-exclusive, perpetual, +irrevocable, royalty free license under Your Applicable Patent Rights and other +intellectual property rights owned or controlled by You, to make, sell, offer +for sale, use, import, reproduce, display, perform, modify, distribute and +Deploy Your Modifications of the same scope and extent as Licensor's licenses +under Sections 2.1 and 2.2; and + +(b) You grant to Licensor and its subsidiaries a non-exclusive, worldwide, +royalty-free, perpetual and irrevocable license, under Your Applicable Patent +Rights and other intellectual property rights owned or controlled by You, to +make, use, sell, offer for sale, import, reproduce, display, perform, +distribute, modify or have modified (for Licensor and/or its subsidiaries), +sublicense and distribute Your Modifications, in any form and for any purpose, +through multiple tiers of distribution. + +(c) You agree not use any information derived from Your use and review of the +Covered Code, including but not limited to any algorithms or inventions that may +be contained in the Covered Code, for the purpose of asserting any of Your +patent rights, or assisting a third party to assert any of its patent rights, +against Licensor or any Contributor. + +4. Derivative Works. You may create a Derivative Work by combining Covered Code +with other code not otherwise governed by the terms of this License and +distribute the Derivative Work as an integrated product. In each such instance, +You must make sure the requirements of this License are fulfilled for the +Covered Code or any portion thereof, including all Modifications. + +4.1 You must cause any Derivative Work that you distribute, publish or +Externally Deploy, that in whole or in part contains or is derived from the +Covered Code or any part thereof, to be licensed as a whole at no charge to all +third parties under the terms of this License and no other license except as +provided in Section 4.2. You also must make Source Code available for the +Derivative Work under the same terms as Modifications, described in Sections 2 +and 3, above. + +4.2 Compatible Source Licenses. Software modules that have been independently +developed without any use of Covered Code and which contain no portion of the +Covered Code, Modifications or other Derivative Works, but are used or combined +in any way wtih the Covered Code or any Derivative Work to form a larger +Derivative Work, are exempt from the conditions described in Section 4.1 but +only to the extent that: the software module, including any software that is +linked to, integrated with, or part of the same applications as, the software +module by any method must be wholly subject to one of the Compatible Source +Licenses. Notwithstanding the foregoing, all Covered Code must be subject to the +terms of this License. Thus, the entire Derivative Work must be licensed under a +combination of the RPSL (for Covered Code) and a Compatible Source License for +any independently developed software modules within the Derivative Work. The +foregoing requirement applies even if the Compatible Source License would +ordinarily allow the software module to link with, or form larger works with, +other software that is not subject to the Compatible Source License. For +example, although the Mozilla Public License v1.1 allows Mozilla code to be +combined with proprietary software that is not subject to the MPL, if +MPL-licensed code is used with Covered Code the MPL-licensed code could not be +combined or linked with any code not governed by the MPL. The general intent of +this section 4.2 is to enable use of Covered Code with applications that are +wholly subject to an acceptable open source license. You are responsible for +determining whether your use of software with Covered Code is allowed under Your +license to such software. + +4.3 Mere aggregation of another work not based on the Covered Code with the +Covered Code (or with a work based on the Covered Code) on a volume of a storage +or distribution medium does not bring the other work under the scope of this +License. If You deliver the Covered Code for combination and/or integration with +an application previously provided by You (for example, via automatic updating +technology), such combination and/or integration constitutes a Derivative Work +subject to the terms of this License. + +5. Exclusions From License Grant. Nothing in this License shall be deemed to +grant any rights to trademarks, copyrights, patents, trade secrets or any other +intellectual property of Licensor or any Contributor except as expressly stated +herein. No right is granted to the trademarks of Licensor or any Contributor +even if such marks are included in the Covered Code. Nothing in this License +shall be interpreted to prohibit Licensor from licensing under different terms +from this License any code that Licensor otherwise would have a right to +license. Modifications, Derivative Works and/or any use or combination of +Covered Code with other technology provided by Licensor or third parties may +require additional patent licenses from Licensor which Licensor may grant in its +sole discretion. No patent license is granted separate from the Original Code or +combinations of the Original Code with other software or hardware. + +5.1. Trademarks. This License does not grant any rights to use the trademarks or +trade names owned by Licensor ("Licensor Marks" defined in Exhibit C) or to any +trademark or trade name belonging to any Contributor. No Licensor Marks may be +used to endorse or promote products derived from the Original Code other than as +permitted by the Licensor Trademark Policy defined in Exhibit C. + +6. Additional Terms. You may choose to offer, and to charge a fee for, warranty, +support, indemnity or liability obligations and/or other rights consistent with +the scope of the license granted herein ("Additional Terms") to one or more +recipients of Covered Code. However, You may do so only on Your own behalf and +as Your sole responsibility, and not on behalf of Licensor or any Contributor. +You must obtain the recipient's agreement that any such Additional Terms are +offered by You alone, and You hereby agree to indemnify, defend and hold +Licensor and every Contributor harmless for any liability incurred by or claims +asserted against Licensor or such Contributor by reason of any such Additional +Terms. + +7. Versions of the License. Licensor may publish revised and/or new versions of +this License from time to time. Each version will be given a distinguishing +version number. Once Original Code has been published under a particular version +of this License, You may continue to use it under the terms of that version. You +may also choose to use such Original Code under the terms of any subsequent +version of this License published by Licensor. No one other than Licensor has +the right to modify the terms applicable to Covered Code created under this +License. + +8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in part +pre-release, untested, or not fully tested works. The Covered Code may contain +errors that could cause failures or loss of data, and may be incomplete or +contain inaccuracies. You expressly acknowledge and agree that use of the +Covered Code, or any portion thereof, is at Your sole and entire risk. THE +COVERED CODE IS PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT OF +ANY KIND AND LICENSOR AND LICENSOR'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS +"LICENSOR" FOR THE PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY +DISCLAIM ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF MERCHANTABILITY, OF +SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY, OF QUIET +ENJOYMENT, AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. LICENSOR AND EACH +CONTRIBUTOR DOES NOT WARRANT AGAINST INTERFERENCE WITH YOUR ENJOYMENT OF THE +COVERED CODE, THAT THE FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR +REQUIREMENTS, THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR +ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO ORAL OR +WRITTEN DOCUMENTATION, INFORMATION OR ADVICE GIVEN BY LICENSOR, A LICENSOR +AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. You +acknowledge that the Covered Code is not intended for use in high risk +activities, including, but not limited to, the design, construction, operation +or maintenance of nuclear facilities, aircraft navigation, aircraft +communication systems, or air traffic control machines in which case the failure +of the Covered Code could lead to death, personal injury, or severe physical or +environmental damage. Licensor disclaims any express or implied warranty of +fitness for such uses. + +9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT +SHALL LICENSOR OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, SPECIAL, +INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING TO THIS LICENSE OR +YOUR USE OR INABILITY TO USE THE COVERED CODE, OR ANY PORTION THEREOF, WHETHER +UNDER A THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE OR STRICT +LIABILITY), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF LICENSOR OR SUCH +CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES AND +NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY REMEDY. SOME +JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF INCIDENTAL OR +CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY TO YOU. In no event +shall Licensor's total liability to You for all damages (other than as may be +required by applicable law) under this License exceed the amount of ten dollars +($10.00). + +10. Ownership. Subject to the licenses granted under this License, each +Contributor retains all rights, title and interest in and to any Modifications +made by such Contributor. Licensor retains all rights, title and interest in and +to the Original Code and any Modifications made by or on behalf of Licensor +("Licensor Modifications"), and such Licensor Modifications will not be +automatically subject to this License. Licensor may, at its sole discretion, +choose to license such Licensor Modifications under this License, or on +different terms from those contained in this License or may choose not to +license them at all. + +11. Termination. + +11.1 Term and Termination. The term of this License is perpetual unless +terminated as provided below. This License and the rights granted hereunder will +terminate: + +(a) automatically without notice from Licensor if You fail to comply with any +term(s) of this License and fail to cure such breach within 30 days of becoming +aware of such breach; + +(b) immediately in the event of the circumstances described in Section 12.5(b); +or + +(c) automatically without notice from Licensor if You, at any time during the +term of this License, commence an action for patent infringement against +Licensor (including by cross-claim or counter claim in a lawsuit); + +(d) upon written notice from Licensor if You, at any time during the term of +this License, commence an action for patent infringement against any third party +alleging that the Covered Code itself (excluding combinations with other +software or hardware) infringes any patent (including by cross-claim or counter +claim in a lawsuit). + +11.2 Effect of Termination. Upon termination, You agree to immediately stop any +further use, reproduction, modification, sublicensing and distribution of the +Covered Code and to destroy all copies of the Covered Code that are in your +possession or control. All sublicenses to the Covered Code which have been +properly granted prior to termination shall survive any termination of this +License. Provisions which, by their nature, should remain in effect beyond the +termination of this License shall survive, including but not limited to Sections +3, 5, 8, 9, 10, 11, 12.2 and 13. No party will be liable to any other for +compensation, indemnity or damages of any sort solely as a result of terminating +this License in accordance with its terms, and termination of this License will +be without prejudice to any other right or remedy of any party. + +12. Miscellaneous. + +12.1 Government End Users. The Covered Code is a "commercial item" as defined in +FAR 2.101. Government software and technical data rights in the Covered Code +include only those rights customarily provided to the public as defined in this +License. This customary commercial license in technical data and software is +provided in accordance with FAR 12.211 (Technical Data) and 12.212 (Computer +Software) and, for Department of Defense purchases, DFAR 252.227-7015 (Technical +Data -- Commercial Items) and 227.7202-3 (Rights in Commercial Computer Software +or Computer Software Documentation). Accordingly, all U.S. Government End Users +acquire Covered Code with only those rights set forth herein. + +12.2 Relationship of Parties. This License will not be construed as creating an +agency, partnership, joint venture or any other form of legal association +between or among You, Licensor or any Contributor, and You will not represent to +the contrary, whether expressly, by implication, appearance or otherwise. + +12.3 Independent Development. Nothing in this License will impair Licensor's +right to acquire, license, develop, have others develop for it, market and/or +distribute technology or products that perform the same or similar functions as, +or otherwise compete with, Modifications, Derivative Works, technology or +products that You may develop, produce, market or distribute. + +12.4 Waiver; Construction. Failure by Licensor or any Contributor to enforce any +provision of this License will not be deemed a waiver of future enforcement of +that or any other provision. Any law or regulation which provides that the +language of a contract shall be construed against the drafter will not apply to +this License. + +12.5 Severability. (a) If for any reason a court of competent jurisdiction finds +any provision of this License, or portion thereof, to be unenforceable, that +provision of the License will be enforced to the maximum extent permissible so +as to effect the economic benefits and intent of the parties, and the remainder +of this License will continue in full force and effect. (b) Notwithstanding the +foregoing, if applicable law prohibits or restricts You from fully and/or +specifically complying with Sections 2 and/or 3 or prevents the enforceability +of either of those Sections, this License will immediately terminate and You +must immediately discontinue any use of the Covered Code and destroy all copies +of it that are in your possession or control. + +12.6 Dispute Resolution. Any litigation or other dispute resolution between You +and Licensor relating to this License shall take place in the Seattle, +Washington, and You and Licensor hereby consent to the personal jurisdiction of, +and venue in, the state and federal courts within that District with respect to +this License. The application of the United Nations Convention on Contracts for +the International Sale of Goods is expressly excluded. + +12.7 Export/Import Laws. This software is subject to all export and import laws +and restrictions and regulations of the country in which you receive the Covered +Code and You are solely responsible for ensuring that You do not export, +re-export or import the Covered Code or any direct product thereof in violation +of any such restrictions, laws or regulations, or without all necessary +authorizations. + +12.8 Entire Agreement; Governing Law. This License constitutes the entire +agreement between the parties with respect to the subject matter hereof. This +License shall be governed by the laws of the United States and the State of +Washington. + +Where You are located in the province of Quebec, Canada, the following clause +applies: The parties hereby confirm that they have requested that this License +and all related documents be drafted in English. Les parties ont exigé +que le présent contrat et tous les documents connexes soient +rédigés en anglais. + + EXHIBIT A. + +"Copyright © 1995-2002 +RealNetworks, Inc. and/or its licensors. All Rights Reserved. + +The contents of this file, and the files included with this file, are subject to +the current version of the RealNetworks Public Source License Version 1.0 (the +"RPSL") available at https://www.helixcommunity.org/content/rpsl unless you have +licensed the file under the RealNetworks Community Source License Version 1.0 +(the "RCSL") available at https://www.helixcommunity.org/content/rcsl, in which +case the RCSL will apply. You may also obtain the license terms directly from +RealNetworks. You may not use this file except in compliance with the RPSL or, +if you have a valid RCSL with RealNetworks applicable to this file, the RCSL. +Please see the applicable RPSL or RCSL for the rights, obligations and +limitations governing use of the contents of the file. + +This file is part of the Helix DNA Technology. RealNetworks is the developer of +the Original code and owns the copyrights in the portions it created. + +This file, and the files included with this file, is distributed and made +available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR +IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING +WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + +Contributor(s): ____________________________________ + +Technology Compatibility Kit Test +Suite(s) Location (if licensed under the RCSL): ______________________________ + +Object Code Notice: Helix DNA Client technology included. Copyright (c) +RealNetworks, Inc., 1995-2002. All rights reserved. + + + EXHIBIT B + +Compatible Source Licenses for the RealNetworks Public Source License. The +following list applies to the most recent version of the license as of October +25, 2002, unless otherwise indicated. + +* Academic Free License +* Apache Software License +* Apple Public Source License +* Artistic license +* Attribution Assurance Licenses +* BSD license +* Common Public License (1) +* Eiffel Forum License +* GNU General Public License (GPL) (1) +* GNU Library or "Lesser" General Public License (LGPL) (1) +* IBM Public License +* Intel Open Source License +* Jabber Open Source License +* MIT license +* MITRE Collaborative Virtual Workspace License (CVW License) +* Motosoto License +* Mozilla Public License 1.0 (MPL) +* Mozilla Public License 1.1 (MPL) +* Nokia Open Source License +* Open Group Test Suite License +* Python Software Foundation License +* Ricoh Source Code Public License +* Sun Industry Standards Source License (SISSL) +* Sun Public License +* University of Illinois/NCSA Open Source License +* Vovida Software License v. 1.0 +* W3C License +* X.Net License +* Zope Public License +* zlib/libpng license + +(1) Note: because this license contains certain reciprocal licensing terms that +purport to extend to independently developed code, You may be prohibited under +the terms of this otherwise compatible license from using code licensed under +its terms with Covered Code because Covered Code may only be licensed under the +RealNetworks Public Source License. Any attempt to apply non RPSL license terms, +including without limitation the GPL, to Covered Code is expressly forbidden. +You are responsible for ensuring that Your use of Compatible Source Licensed +code does not violate either the RPSL or the Compatible Source License. + +The latest version of this list can be found at: +https://www.helixcommunity.org/content/complicense + + EXHIBIT C + +RealNetworks' Trademark policy. + +RealNetworks defines the following trademarks collectively as "Licensor +Trademarks": "RealNetworks", "RealPlayer", "RealJukebox", "RealSystem", +"RealAudio", "RealVideo", "RealOne Player", "RealMedia", "Helix" or any other +trademarks or trade names belonging to RealNetworks. + +RealNetworks "Licensor Trademark Policy" forbids any use of Licensor Trademarks +except as permitted by and in strict compliance at all times with RealNetworks' +third party trademark usage guidelines which are posted at +http://www.realnetworks.com/info/helixlogo.html. + diff --git a/components/spotify/cspot/bell/libhelix-mp3/assembly.h b/components/spotify/cspot/bell/libhelix-mp3/assembly.h new file mode 100644 index 00000000..ff7979b6 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/assembly.h @@ -0,0 +1,375 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * assembly.h - assembly language functions and prototypes for supported platforms + * + * - inline rountines with access to 64-bit multiply results + * - x86 (_WIN32) and ARM (ARM_ADS, _WIN32_WCE) versions included + * - some inline functions are mix of asm and C for speed + * - some functions are in native asm files, so only the prototype is given here + * + * MULSHIFT32(x, y) signed multiply of two 32-bit integers (x and y), returns top 32 bits of 64-bit result + * FASTABS(x) branchless absolute value of signed integer x + * CLZ(x) count leading zeros in x + * MADD64(sum, x, y) (Windows only) sum [64-bit] += x [32-bit] * y [32-bit] + * SHL64(sum, x, y) (Windows only) 64-bit left shift using __int64 + * SAR64(sum, x, y) (Windows only) 64-bit right shift using __int64 + */ + +#ifndef _ASSEMBLY_H +#define _ASSEMBLY_H + +#if (defined _WIN32 && !defined _WIN32_WCE) || (defined __WINS__ && defined _SYMBIAN) || defined(_OPENWAVE_SIMULATOR) || defined(WINCE_EMULATOR) /* Symbian emulator for Ix86 */ + +#pragma warning( disable : 4035 ) /* complains about inline asm not returning a value */ + +static __inline int MULSHIFT32(int x, int y) +{ + __asm { + mov eax, x + imul y + mov eax, edx + } +} + +static __inline int FASTABS(int x) +{ + int sign; + + sign = x >> (sizeof(int) * 8 - 1); + x ^= sign; + x -= sign; + + return x; +} + +static __inline int CLZ(int x) +{ + int numZeros; + + if (!x) + return (sizeof(int) * 8); + + numZeros = 0; + while (!(x & 0x80000000)) { + numZeros++; + x <<= 1; + } + + return numZeros; +} + +/* MADD64, SHL64, SAR64: + * write in assembly to avoid dependency on run-time lib for 64-bit shifts, muls + * (sometimes compiler thunks to function calls instead of code generating) + * required for Symbian emulator + */ +#ifdef __CW32__ +typedef long long Word64; +#else +typedef __int64 Word64; +#endif + +static __inline Word64 MADD64(Word64 sum, int x, int y) +{ + unsigned int sumLo = ((unsigned int *)&sum)[0]; + int sumHi = ((int *)&sum)[1]; + + __asm { + mov eax, x + imul y + add eax, sumLo + adc edx, sumHi + } + + /* equivalent to return (sum + ((__int64)x * y)); */ +} + +static __inline Word64 SHL64(Word64 x, int n) +{ + unsigned int xLo = ((unsigned int *)&x)[0]; + int xHi = ((int *)&x)[1]; + unsigned char nb = (unsigned char)n; + + if (n < 32) { + __asm { + mov edx, xHi + mov eax, xLo + mov cl, nb + shld edx, eax, cl + shl eax, cl + } + } else if (n < 64) { + /* shl masks cl to 0x1f */ + __asm { + mov edx, xLo + mov cl, nb + xor eax, eax + shl edx, cl + } + } else { + __asm { + xor edx, edx + xor eax, eax + } + } +} + +static __inline Word64 SAR64(Word64 x, int n) +{ + unsigned int xLo = ((unsigned int *)&x)[0]; + int xHi = ((int *)&x)[1]; + unsigned char nb = (unsigned char)n; + + if (n < 32) { + __asm { + mov edx, xHi + mov eax, xLo + mov cl, nb + shrd eax, edx, cl + sar edx, cl + } + } else if (n < 64) { + /* sar masks cl to 0x1f */ + __asm { + mov edx, xHi + mov eax, xHi + mov cl, nb + sar edx, 31 + sar eax, cl + } + } else { + __asm { + sar xHi, 31 + mov eax, xHi + mov edx, xHi + } + } +} + +#elif (defined _WIN32) && (defined _WIN32_WCE) + +/* use asm function for now (EVC++ 3.0 does horrible job compiling __int64 version) */ +#define MULSHIFT32 xmp3_MULSHIFT32 +int MULSHIFT32(int x, int y); + +static __inline int FASTABS(int x) +{ + int sign; + + sign = x >> (sizeof(int) * 8 - 1); + x ^= sign; + x -= sign; + + return x; +} + +static __inline int CLZ(int x) +{ + int numZeros; + + if (!x) + return (sizeof(int) * 8); + + numZeros = 0; + while (!(x & 0x80000000)) { + numZeros++; + x <<= 1; + } + + return numZeros; +} + +#elif defined XXXARM_ADS + +static __inline int MULSHIFT32(int x, int y) +{ + /* important rules for smull RdLo, RdHi, Rm, Rs: + * RdHi and Rm can't be the same register + * RdLo and Rm can't be the same register + * RdHi and RdLo can't be the same register + * Note: Rs determines early termination (leading sign bits) so if you want to specify + * which operand is Rs, put it in the SECOND argument (y) + * For inline assembly, x and y are not assumed to be R0, R1 so it shouldn't matter + * which one is returned. (If this were a function call, returning y (R1) would + * require an extra "mov r0, r1") + */ + int zlow; + __asm { + smull zlow,y,x,y + } + + return y; +} + +static __inline int FASTABS(int x) +{ + int t=0; /*Really is not necessary to initialiaze only to avoid warning*/ + + __asm { + eor t, x, x, asr #31 + sub t, t, x, asr #31 + } + + return t; +} + +static __inline int CLZ(int x) +{ + int numZeros; + + if (!x) + return (sizeof(int) * 8); + + numZeros = 0; + while (!(x & 0x80000000)) { + numZeros++; + x <<= 1; + } + + return numZeros; +} + +#elif defined(__GNUC__) && defined(XXXX__thumb__) + + +static __inline int MULSHIFT32(int x, int y) +{ + // important rules for smull RdLo, RdHi, Rm, Rs: + // RdHi and Rm can't be the same register + // RdLo and Rm can't be the same register + // RdHi and RdLo can't be the same register + // Note: Rs determines early termination (leading sign bits) so if you want to specify + // which operand is Rs, put it in the SECOND argument (y) + // For inline assembly, x and y are not assumed to be R0, R1 so it shouldn't matter + // which one is returned. (If this were a function call, returning y (R1) would + // require an extra "mov r0, r1") + + int zlow; + __asm__ volatile ("smull %0,%1,%2,%3" : "=&r" (zlow), "=r" (y) : "r" (x), "1" (y)) ; + return y; +} + +//fb +#include +static __inline int FASTABS(int x) +{ + return abs(x); +} + +static __inline int CLZ(int x) +{ + return __builtin_clz(x); +} +//fb +//mw +//TODO: Check Compiler output on these.. (fb) +static __inline Word64 xMADD64(Word64 sum, int x, int y) +{ + return (sum + ((int64_t)x * y)); +} +static __inline Word64 xHL64(Word64 x, int n) +{ + return x << n; +} + +static __inline Word64 xSAR64(Word64 x, int n) +{ + return x >> n; +} +//mw + +#elif defined(__APPLE__) || defined(ESP_PLATFORM) || defined(__x86_64__) + +static __inline int FASTABS(int x) +{ + int sign; + + sign = x >> (sizeof(int) * 8 - 1); + x ^= sign; + x -= sign; + + return x; +} + +static __inline int CLZ(int x) +{ + int numZeros; + + if (!x) + return (sizeof(int) * 8); + + numZeros = 0; + while (!(x & 0x80000000)) { + numZeros++; + x <<= 1; + } + + return numZeros; +} + +/* returns 64-bit value in [edx:eax] */ +static __inline Word64 MADD64(Word64 sum64, int x, int y) +{ + sum64 += (Word64)x * (Word64)y; + return sum64; +} + +static __inline__ int MULSHIFT32(int x, int y) +{ + int z; + + z = (Word64)x * (Word64)y >> 32; + + return z; +} + +static __inline Word64 SAR64(Word64 x, int n) +{ + return x >> n; +} + +#else + +#error Unsupported platform in assembly.h + +#endif /* platforms */ + +#endif /* _ASSEMBLY_H */ diff --git a/components/spotify/cspot/bell/libhelix-mp3/bitstream.c b/components/spotify/cspot/bell/libhelix-mp3/bitstream.c new file mode 100644 index 00000000..608c39cc --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/bitstream.c @@ -0,0 +1,389 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * bitstream.c - bitstream unpacking, frame header parsing, side info parsing + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +/************************************************************************************** + * Function: SetBitstreamPointer + * + * Description: initialize bitstream reader + * + * Inputs: pointer to BitStreamInfo struct + * number of bytes in bitstream + * pointer to byte-aligned buffer of data to read from + * + * Outputs: filled bitstream info struct + * + * Return: none + **************************************************************************************/ +void SetBitstreamPointer(BitStreamInfo *bsi, int nBytes, unsigned char *buf) +{ + /* init bitstream */ + bsi->bytePtr = buf; + bsi->iCache = 0; /* 4-byte unsigned int */ + bsi->cachedBits = 0; /* i.e. zero bits in cache */ + bsi->nBytes = nBytes; +} + +/************************************************************************************** + * Function: RefillBitstreamCache + * + * Description: read new data from bitstream buffer into bsi cache + * + * Inputs: pointer to initialized BitStreamInfo struct + * + * Outputs: updated bitstream info struct + * + * Return: none + * + * Notes: only call when iCache is completely drained (resets bitOffset to 0) + * always loads 4 new bytes except when bsi->nBytes < 4 (end of buffer) + * stores data as big-endian in cache, regardless of machine endian-ness + * + * TODO: optimize for ARM + * possibly add little/big-endian modes for doing 32-bit loads + **************************************************************************************/ +static __inline void RefillBitstreamCache(BitStreamInfo *bsi) +{ + int nBytes = bsi->nBytes; + + /* optimize for common case, independent of machine endian-ness */ + if (nBytes >= 4) { + bsi->iCache = (*bsi->bytePtr++) << 24; + bsi->iCache |= (*bsi->bytePtr++) << 16; + bsi->iCache |= (*bsi->bytePtr++) << 8; + bsi->iCache |= (*bsi->bytePtr++); + bsi->cachedBits = 32; + bsi->nBytes -= 4; + } else { + bsi->iCache = 0; + while (nBytes--) { + bsi->iCache |= (*bsi->bytePtr++); + bsi->iCache <<= 8; + } + bsi->iCache <<= ((3 - bsi->nBytes)*8); + bsi->cachedBits = 8*bsi->nBytes; + bsi->nBytes = 0; + } +} + +/************************************************************************************** + * Function: GetBits + * + * Description: get bits from bitstream, advance bitstream pointer + * + * Inputs: pointer to initialized BitStreamInfo struct + * number of bits to get from bitstream + * + * Outputs: updated bitstream info struct + * + * Return: the next nBits bits of data from bitstream buffer + * + * Notes: nBits must be in range [0, 31], nBits outside this range masked by 0x1f + * for speed, does not indicate error if you overrun bit buffer + * if nBits = 0, returns 0 (useful for scalefactor unpacking) + * + * TODO: optimize for ARM + **************************************************************************************/ +unsigned int GetBits(BitStreamInfo *bsi, int nBits) +{ + unsigned int data, lowBits; + + nBits &= 0x1f; /* nBits mod 32 to avoid unpredictable results like >> by negative amount */ + data = bsi->iCache >> (31 - nBits); /* unsigned >> so zero-extend */ + data >>= 1; /* do as >> 31, >> 1 so that nBits = 0 works okay (returns 0) */ + bsi->iCache <<= nBits; /* left-justify cache */ + bsi->cachedBits -= nBits; /* how many bits have we drawn from the cache so far */ + + /* if we cross an int boundary, refill the cache */ + if (bsi->cachedBits < 0) { + lowBits = -bsi->cachedBits; + RefillBitstreamCache(bsi); + data |= bsi->iCache >> (32 - lowBits); /* get the low-order bits */ + + bsi->cachedBits -= lowBits; /* how many bits have we drawn from the cache so far */ + bsi->iCache <<= lowBits; /* left-justify cache */ + } + + return data; +} + +/************************************************************************************** + * Function: CalcBitsUsed + * + * Description: calculate how many bits have been read from bitstream + * + * Inputs: pointer to initialized BitStreamInfo struct + * pointer to start of bitstream buffer + * bit offset into first byte of startBuf (0-7) + * + * Outputs: none + * + * Return: number of bits read from bitstream, as offset from startBuf:startOffset + **************************************************************************************/ +int CalcBitsUsed(BitStreamInfo *bsi, unsigned char *startBuf, int startOffset) +{ + int bitsUsed; + + bitsUsed = (bsi->bytePtr - startBuf) * 8; + bitsUsed -= bsi->cachedBits; + bitsUsed -= startOffset; + + return bitsUsed; +} + +/************************************************************************************** + * Function: CheckPadBit + * + * Description: check whether padding byte is present in an MP3 frame + * + * Inputs: MP3DecInfo struct with valid FrameHeader struct + * (filled by UnpackFrameHeader()) + * + * Outputs: none + * + * Return: 1 if pad bit is set, 0 if not, -1 if null input pointer + **************************************************************************************/ +int CheckPadBit(MP3DecInfo *mp3DecInfo) +{ + FrameHeader *fh; + + /* validate pointers */ + if (!mp3DecInfo || !mp3DecInfo->FrameHeaderPS) + return -1; + + fh = ((FrameHeader *)(mp3DecInfo->FrameHeaderPS)); + + return (fh->paddingBit ? 1 : 0); +} + +/************************************************************************************** + * Function: UnpackFrameHeader + * + * Description: parse the fields of the MP3 frame header + * + * Inputs: buffer pointing to a complete MP3 frame header (4 bytes, plus 2 if CRC) + * + * Outputs: filled frame header info in the MP3DecInfo structure + * updated platform-specific FrameHeader struct + * + * Return: length (in bytes) of frame header (for caller to calculate offset to + * first byte following frame header) + * -1 if null frameHeader or invalid header + * + * TODO: check for valid modes, depending on capabilities of decoder + * test CRC on actual stream (verify no endian problems) + **************************************************************************************/ +int UnpackFrameHeader(MP3DecInfo *mp3DecInfo, unsigned char *buf) +{ + + int verIdx; + FrameHeader *fh; + + /* validate pointers and sync word */ + if (!mp3DecInfo || !mp3DecInfo->FrameHeaderPS || (buf[0] & SYNCWORDH) != SYNCWORDH || (buf[1] & SYNCWORDL) != SYNCWORDL) + return -1; + + fh = ((FrameHeader *)(mp3DecInfo->FrameHeaderPS)); + + /* read header fields - use bitmasks instead of GetBits() for speed, since format never varies */ + verIdx = (buf[1] >> 3) & 0x03; + fh->ver = (MPEGVersion)( verIdx == 0 ? MPEG25 : ((verIdx & 0x01) ? MPEG1 : MPEG2) ); + fh->layer = 4 - ((buf[1] >> 1) & 0x03); /* easy mapping of index to layer number, 4 = error */ + fh->crc = 1 - ((buf[1] >> 0) & 0x01); + fh->brIdx = (buf[2] >> 4) & 0x0f; + fh->srIdx = (buf[2] >> 2) & 0x03; + fh->paddingBit = (buf[2] >> 1) & 0x01; + fh->privateBit = (buf[2] >> 0) & 0x01; + fh->sMode = (StereoMode)((buf[3] >> 6) & 0x03); /* maps to correct enum (see definition) */ + fh->modeExt = (buf[3] >> 4) & 0x03; + fh->copyFlag = (buf[3] >> 3) & 0x01; + fh->origFlag = (buf[3] >> 2) & 0x01; + fh->emphasis = (buf[3] >> 0) & 0x03; + + /* check parameters to avoid indexing tables with bad values */ + if (fh->srIdx == 3 || fh->layer == 4 || fh->brIdx == 15) + return -1; + + fh->sfBand = &sfBandTable[fh->ver][fh->srIdx]; /* for readability (we reference sfBandTable many times in decoder) */ + if (fh->sMode != Joint) /* just to be safe (dequant, stproc check fh->modeExt) */ + fh->modeExt = 0; + + /* init user-accessible data */ + mp3DecInfo->nChans = (fh->sMode == Mono ? 1 : 2); + mp3DecInfo->samprate = samplerateTab[fh->ver][fh->srIdx]; + mp3DecInfo->nGrans = (fh->ver == MPEG1 ? NGRANS_MPEG1 : NGRANS_MPEG2); + mp3DecInfo->nGranSamps = ((int)samplesPerFrameTab[fh->ver][fh->layer - 1]) / mp3DecInfo->nGrans; + mp3DecInfo->layer = fh->layer; + mp3DecInfo->version = fh->ver; + + /* get bitrate and nSlots from table, unless brIdx == 0 (free mode) in which case caller must figure it out himself + * question - do we want to overwrite mp3DecInfo->bitrate with 0 each time if it's free mode, and + * copy the pre-calculated actual free bitrate into it in mp3dec.c (according to the spec, + * this shouldn't be necessary, since it should be either all frames free or none free) + */ + if (fh->brIdx) { + mp3DecInfo->bitrate = ((int)bitrateTab[fh->ver][fh->layer - 1][fh->brIdx]) * 1000; + + /* nSlots = total frame bytes (from table) - sideInfo bytes - header - CRC (if present) + pad (if present) */ + mp3DecInfo->nSlots = (int)slotTab[fh->ver][fh->srIdx][fh->brIdx] - + (int)sideBytesTab[fh->ver][(fh->sMode == Mono ? 0 : 1)] - + 4 - (fh->crc ? 2 : 0) + (fh->paddingBit ? 1 : 0); + } + + /* load crc word, if enabled, and return length of frame header (in bytes) */ + if (fh->crc) { + fh->CRCWord = ((int)buf[4] << 8 | (int)buf[5] << 0); + return 6; + } else { + fh->CRCWord = 0; + return 4; + } +} + +/************************************************************************************** + * Function: UnpackSideInfo + * + * Description: parse the fields of the MP3 side info header + * + * Inputs: MP3DecInfo structure filled by UnpackFrameHeader() + * buffer pointing to the MP3 side info data + * + * Outputs: updated mainDataBegin in MP3DecInfo struct + * updated private (platform-specific) SideInfo struct + * + * Return: length (in bytes) of side info data + * -1 if null input pointers + **************************************************************************************/ +int UnpackSideInfo(MP3DecInfo *mp3DecInfo, unsigned char *buf) +{ + int gr, ch, bd, nBytes; + BitStreamInfo bitStreamInfo, *bsi; + FrameHeader *fh; + SideInfo *si; + SideInfoSub *sis; + + /* validate pointers and sync word */ + if (!mp3DecInfo || !mp3DecInfo->FrameHeaderPS || !mp3DecInfo->SideInfoPS) + return -1; + + fh = ((FrameHeader *)(mp3DecInfo->FrameHeaderPS)); + si = ((SideInfo *)(mp3DecInfo->SideInfoPS)); + + bsi = &bitStreamInfo; + if (fh->ver == MPEG1) { + /* MPEG 1 */ + nBytes = (fh->sMode == Mono ? SIBYTES_MPEG1_MONO : SIBYTES_MPEG1_STEREO); + SetBitstreamPointer(bsi, nBytes, buf); + si->mainDataBegin = GetBits(bsi, 9); + si->privateBits = GetBits(bsi, (fh->sMode == Mono ? 5 : 3)); + + for (ch = 0; ch < mp3DecInfo->nChans; ch++) + for (bd = 0; bd < MAX_SCFBD; bd++) + si->scfsi[ch][bd] = GetBits(bsi, 1); + } else { + /* MPEG 2, MPEG 2.5 */ + nBytes = (fh->sMode == Mono ? SIBYTES_MPEG2_MONO : SIBYTES_MPEG2_STEREO); + SetBitstreamPointer(bsi, nBytes, buf); + si->mainDataBegin = GetBits(bsi, 8); + si->privateBits = GetBits(bsi, (fh->sMode == Mono ? 1 : 2)); + } + + for(gr =0; gr < mp3DecInfo->nGrans; gr++) { + for (ch = 0; ch < mp3DecInfo->nChans; ch++) { + sis = &si->sis[gr][ch]; /* side info subblock for this granule, channel */ + + sis->part23Length = GetBits(bsi, 12); + sis->nBigvals = GetBits(bsi, 9); + sis->globalGain = GetBits(bsi, 8); + sis->sfCompress = GetBits(bsi, (fh->ver == MPEG1 ? 4 : 9)); + sis->winSwitchFlag = GetBits(bsi, 1); + + if(sis->winSwitchFlag) { + /* this is a start, stop, short, or mixed block */ + sis->blockType = GetBits(bsi, 2); /* 0 = normal, 1 = start, 2 = short, 3 = stop */ + sis->mixedBlock = GetBits(bsi, 1); /* 0 = not mixed, 1 = mixed */ + sis->tableSelect[0] = GetBits(bsi, 5); + sis->tableSelect[1] = GetBits(bsi, 5); + sis->tableSelect[2] = 0; /* unused */ + sis->subBlockGain[0] = GetBits(bsi, 3); + sis->subBlockGain[1] = GetBits(bsi, 3); + sis->subBlockGain[2] = GetBits(bsi, 3); + + /* TODO - check logic */ + if (sis->blockType == 0) { + /* this should not be allowed, according to spec */ + sis->nBigvals = 0; + sis->part23Length = 0; + sis->sfCompress = 0; + } else if (sis->blockType == 2 && sis->mixedBlock == 0) { + /* short block, not mixed */ + sis->region0Count = 8; + } else { + /* start, stop, or short-mixed */ + sis->region0Count = 7; + } + sis->region1Count = 20 - sis->region0Count; + } else { + /* this is a normal block */ + sis->blockType = 0; + sis->mixedBlock = 0; + sis->tableSelect[0] = GetBits(bsi, 5); + sis->tableSelect[1] = GetBits(bsi, 5); + sis->tableSelect[2] = GetBits(bsi, 5); + sis->region0Count = GetBits(bsi, 4); + sis->region1Count = GetBits(bsi, 3); + } + sis->preFlag = (fh->ver == MPEG1 ? GetBits(bsi, 1) : 0); + sis->sfactScale = GetBits(bsi, 1); + sis->count1TableSelect = GetBits(bsi, 1); + } + } + mp3DecInfo->mainDataBegin = si->mainDataBegin; /* needed by main decode loop */ + + ASSERT(nBytes == CalcBitsUsed(bsi, buf, 0) >> 3); + + return nBytes; +} + diff --git a/components/spotify/cspot/bell/libhelix-mp3/buffers.c b/components/spotify/cspot/bell/libhelix-mp3/buffers.c new file mode 100644 index 00000000..52b9bcf7 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/buffers.c @@ -0,0 +1,177 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * buffers.c - allocation and freeing of internal MP3 decoder buffers + * + * All memory allocation for the codec is done in this file, so if you don't want + * to use other the default system malloc() and free() for heap management this is + * the only file you'll need to change. + **************************************************************************************/ + +//#include "hlxclib/stdlib.h" /* for malloc, free */ +#include +#include +#include "coder.h" + +/************************************************************************************** + * Function: ClearBuffer + * + * Description: fill buffer with 0's + * + * Inputs: pointer to buffer + * number of bytes to fill with 0 + * + * Outputs: cleared buffer + * + * Return: none + * + * Notes: slow, platform-independent equivalent to memset(buf, 0, nBytes) + **************************************************************************************/ +#define ClearBuffer(buf, nBytes) memset(buf, 0, nBytes) //fb +/* +static void ClearBuffer(void *buf, int nBytes) +{ + int i; + unsigned char *cbuf = (unsigned char *)buf; + + for (i = 0; i < nBytes; i++) + cbuf[i] = 0; + + //fb + memset(buf, 0, nBytes) + + return; +} +*/ +/************************************************************************************** + * Function: AllocateBuffers + * + * Description: allocate all the memory needed for the MP3 decoder + * + * Inputs: none + * + * Outputs: none + * + * Return: pointer to MP3DecInfo structure (initialized with pointers to all + * the internal buffers needed for decoding, all other members of + * MP3DecInfo structure set to 0) + * + * Notes: if one or more mallocs fail, function frees any buffers already + * allocated before returning + **************************************************************************************/ +MP3DecInfo *AllocateBuffers(void) +{ + MP3DecInfo *mp3DecInfo; + FrameHeader *fh; + SideInfo *si; + ScaleFactorInfo *sfi; + HuffmanInfo *hi; + DequantInfo *di; + IMDCTInfo *mi; + SubbandInfo *sbi; + + mp3DecInfo = (MP3DecInfo *)malloc(sizeof(MP3DecInfo)); + if (!mp3DecInfo) + return 0; + ClearBuffer(mp3DecInfo, sizeof(MP3DecInfo)); + + fh = (FrameHeader *) malloc(sizeof(FrameHeader)); + si = (SideInfo *) malloc(sizeof(SideInfo)); + sfi = (ScaleFactorInfo *) malloc(sizeof(ScaleFactorInfo)); + hi = (HuffmanInfo *) malloc(sizeof(HuffmanInfo)); + di = (DequantInfo *) malloc(sizeof(DequantInfo)); + mi = (IMDCTInfo *) malloc(sizeof(IMDCTInfo)); + sbi = (SubbandInfo *) malloc(sizeof(SubbandInfo)); + + mp3DecInfo->FrameHeaderPS = (void *)fh; + mp3DecInfo->SideInfoPS = (void *)si; + mp3DecInfo->ScaleFactorInfoPS = (void *)sfi; + mp3DecInfo->HuffmanInfoPS = (void *)hi; + mp3DecInfo->DequantInfoPS = (void *)di; + mp3DecInfo->IMDCTInfoPS = (void *)mi; + mp3DecInfo->SubbandInfoPS = (void *)sbi; + + if (!fh || !si || !sfi || !hi || !di || !mi || !sbi) { + FreeBuffers(mp3DecInfo); /* safe to call - only frees memory that was successfully allocated */ + return 0; + } + + /* important to do this - DSP primitives assume a bunch of state variables are 0 on first use */ + ClearBuffer(fh, sizeof(FrameHeader)); + ClearBuffer(si, sizeof(SideInfo)); + ClearBuffer(sfi, sizeof(ScaleFactorInfo)); + ClearBuffer(hi, sizeof(HuffmanInfo)); + ClearBuffer(di, sizeof(DequantInfo)); + ClearBuffer(mi, sizeof(IMDCTInfo)); + ClearBuffer(sbi, sizeof(SubbandInfo)); + + return mp3DecInfo; +} + +#define SAFE_FREE(x) {if (x) free(x); (x) = 0;} /* helper macro */ + +/************************************************************************************** + * Function: FreeBuffers + * + * Description: frees all the memory used by the MP3 decoder + * + * Inputs: pointer to initialized MP3DecInfo structure + * + * Outputs: none + * + * Return: none + * + * Notes: safe to call even if some buffers were not allocated (uses SAFE_FREE) + **************************************************************************************/ +void FreeBuffers(MP3DecInfo *mp3DecInfo) +{ + if (!mp3DecInfo) + return; + + SAFE_FREE(mp3DecInfo->FrameHeaderPS); + SAFE_FREE(mp3DecInfo->SideInfoPS); + SAFE_FREE(mp3DecInfo->ScaleFactorInfoPS); + SAFE_FREE(mp3DecInfo->HuffmanInfoPS); + SAFE_FREE(mp3DecInfo->DequantInfoPS); + SAFE_FREE(mp3DecInfo->IMDCTInfoPS); + SAFE_FREE(mp3DecInfo->SubbandInfoPS); + + SAFE_FREE(mp3DecInfo); +} diff --git a/components/spotify/cspot/bell/libhelix-mp3/coder.h b/components/spotify/cspot/bell/libhelix-mp3/coder.h new file mode 100644 index 00000000..5cc3ae43 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/coder.h @@ -0,0 +1,309 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * coder.h - private, implementation-specific header file + **************************************************************************************/ + +#ifndef _CODER_H +#define _CODER_H + +#pragma GCC optimize ("O3") + +#include "mp3common.h" + +#if defined(ASSERT) +#undef ASSERT +#endif +#if defined(_WIN32) && defined(_M_IX86) && (defined (_DEBUG) || defined (REL_ENABLE_ASSERTS)) +#define ASSERT(x) if (!(x)) __asm int 3; +#else +#define ASSERT(x) /* do nothing */ +#endif + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* clip to range [-2^n, 2^n - 1] */ +#define CLIP_2N(y, n) { \ + int sign = (y) >> 31; \ + if (sign != (y) >> (n)) { \ + (y) = sign ^ ((1 << (n)) - 1); \ + } \ +} + +#define SIBYTES_MPEG1_MONO 17 +#define SIBYTES_MPEG1_STEREO 32 +#define SIBYTES_MPEG2_MONO 9 +#define SIBYTES_MPEG2_STEREO 17 + +/* number of fraction bits for pow43Tab (see comments there) */ +#define POW43_FRACBITS_LOW 22 +#define POW43_FRACBITS_HIGH 12 + +#define DQ_FRACBITS_OUT 25 /* number of fraction bits in output of dequant */ +#define IMDCT_SCALE 2 /* additional scaling (by sqrt(2)) for fast IMDCT36 */ + +#define HUFF_PAIRTABS 32 +#define BLOCK_SIZE 18 +#define NBANDS 32 +#define MAX_REORDER_SAMPS ((192-126)*3) /* largest critical band for short blocks (see sfBandTable) */ +#define VBUF_LENGTH (17 * 2 * NBANDS) /* for double-sized vbuf FIFO */ + +/* additional external symbols to name-mangle for static linking */ +#define SetBitstreamPointer STATNAME(SetBitstreamPointer) +#define GetBits STATNAME(GetBits) +#define CalcBitsUsed STATNAME(CalcBitsUsed) +#define DequantChannel STATNAME(DequantChannel) +#define MidSideProc STATNAME(MidSideProc) +#define IntensityProcMPEG1 STATNAME(IntensityProcMPEG1) +#define IntensityProcMPEG2 STATNAME(IntensityProcMPEG2) +#define PolyphaseMono STATNAME(PolyphaseMono) +#define PolyphaseStereo STATNAME(PolyphaseStereo) +#define FDCT32 STATNAME(FDCT32) + +#define ISFMpeg1 STATNAME(ISFMpeg1) +#define ISFMpeg2 STATNAME(ISFMpeg2) +#define ISFIIP STATNAME(ISFIIP) +#define uniqueIDTab STATNAME(uniqueIDTab) +#define coef32 STATNAME(coef32) +#define polyCoef STATNAME(polyCoef) +#define csa STATNAME(csa) +#define imdctWin STATNAME(imdctWin) + +#define huffTable STATNAME(huffTable) +#define huffTabOffset STATNAME(huffTabOffset) +#define huffTabLookup STATNAME(huffTabLookup) +#define quadTable STATNAME(quadTable) +#define quadTabOffset STATNAME(quadTabOffset) +#define quadTabMaxBits STATNAME(quadTabMaxBits) + +/* map these to the corresponding 2-bit values in the frame header */ +typedef enum { + Stereo = 0x00, /* two independent channels, but L and R frames might have different # of bits */ + Joint = 0x01, /* coupled channels - layer III: mix of M-S and intensity, Layers I/II: intensity and direct coding only */ + Dual = 0x02, /* two independent channels, L and R always have exactly 1/2 the total bitrate */ + Mono = 0x03 /* one channel */ +} StereoMode; + +typedef struct _BitStreamInfo { + unsigned char *bytePtr; + unsigned int iCache; + int cachedBits; + int nBytes; +} BitStreamInfo; + +typedef struct _FrameHeader { + MPEGVersion ver; /* version ID */ + int layer; /* layer index (1, 2, or 3) */ + int crc; /* CRC flag: 0 = disabled, 1 = enabled */ + int brIdx; /* bitrate index (0 - 15) */ + int srIdx; /* sample rate index (0 - 2) */ + int paddingBit; /* padding flag: 0 = no padding, 1 = single pad byte */ + int privateBit; /* unused */ + StereoMode sMode; /* mono/stereo mode */ + int modeExt; /* used to decipher joint stereo mode */ + int copyFlag; /* copyright flag: 0 = no, 1 = yes */ + int origFlag; /* original flag: 0 = copy, 1 = original */ + int emphasis; /* deemphasis mode */ + int CRCWord; /* CRC word (16 bits, 0 if crc not enabled) */ + + const SFBandTable *sfBand; +} FrameHeader; + +typedef struct _SideInfoSub { + int part23Length; /* number of bits in main data */ + int nBigvals; /* 2x this = first set of Huffman cw's (maximum amplitude can be > 1) */ + int globalGain; /* overall gain for dequantizer */ + int sfCompress; /* unpacked to figure out number of bits in scale factors */ + int winSwitchFlag; /* window switching flag */ + int blockType; /* block type */ + int mixedBlock; /* 0 = regular block (all short or long), 1 = mixed block */ + int tableSelect[3]; /* index of Huffman tables for the big values regions */ + int subBlockGain[3]; /* subblock gain offset, relative to global gain */ + int region0Count; /* 1+region0Count = num scale factor bands in first region of bigvals */ + int region1Count; /* 1+region1Count = num scale factor bands in second region of bigvals */ + int preFlag; /* for optional high frequency boost */ + int sfactScale; /* scaling of the scalefactors */ + int count1TableSelect; /* index of Huffman table for quad codewords */ +} SideInfoSub; + +typedef struct _SideInfo { + int mainDataBegin; + int privateBits; + int scfsi[MAX_NCHAN][MAX_SCFBD]; /* 4 scalefactor bands per channel */ + + SideInfoSub sis[MAX_NGRAN][MAX_NCHAN]; +} SideInfo; + +typedef struct { + int cbType; /* pure long = 0, pure short = 1, mixed = 2 */ + int cbEndS[3]; /* number nonzero short cb's, per subbblock */ + int cbEndSMax; /* max of cbEndS[] */ + int cbEndL; /* number nonzero long cb's */ +} CriticalBandInfo; + +typedef struct _DequantInfo { + int workBuf[MAX_REORDER_SAMPS]; /* workbuf for reordering short blocks */ + CriticalBandInfo cbi[MAX_NCHAN]; /* filled in dequantizer, used in joint stereo reconstruction */ +} DequantInfo; + +typedef struct _HuffmanInfo { + int huffDecBuf[MAX_NCHAN][MAX_NSAMP]; /* used both for decoded Huffman values and dequantized coefficients */ + int nonZeroBound[MAX_NCHAN]; /* number of coeffs in huffDecBuf[ch] which can be > 0 */ + int gb[MAX_NCHAN]; /* minimum number of guard bits in huffDecBuf[ch] */ +} HuffmanInfo; + +typedef enum _HuffTabType { + noBits, + oneShot, + loopNoLinbits, + loopLinbits, + quadA, + quadB, + invalidTab +} HuffTabType; + +typedef struct _HuffTabLookup { + int linBits; + int /*HuffTabType*/ tabType; +} HuffTabLookup; + +typedef struct _IMDCTInfo { + int outBuf[MAX_NCHAN][BLOCK_SIZE][NBANDS]; /* output of IMDCT */ + int overBuf[MAX_NCHAN][MAX_NSAMP / 2]; /* overlap-add buffer (by symmetry, only need 1/2 size) */ + int numPrevIMDCT[MAX_NCHAN]; /* how many IMDCT's calculated in this channel on prev. granule */ + int prevType[MAX_NCHAN]; + int prevWinSwitch[MAX_NCHAN]; + int gb[MAX_NCHAN]; +} IMDCTInfo; + +typedef struct _BlockCount { + int nBlocksLong; + int nBlocksTotal; + int nBlocksPrev; + int prevType; + int prevWinSwitch; + int currWinSwitch; + int gbIn; + int gbOut; +} BlockCount; + +/* max bits in scalefactors = 5, so use char's to save space */ +typedef struct _ScaleFactorInfoSub { + char l[23]; /* [band] */ + char s[13][3]; /* [band][window] */ +} ScaleFactorInfoSub; + +/* used in MPEG 2, 2.5 intensity (joint) stereo only */ +typedef struct _ScaleFactorJS { + int intensityScale; + int slen[4]; + int nr[4]; +} ScaleFactorJS; + +typedef struct _ScaleFactorInfo { + ScaleFactorInfoSub sfis[MAX_NGRAN][MAX_NCHAN]; + ScaleFactorJS sfjs; +} ScaleFactorInfo; + +/* NOTE - could get by with smaller vbuf if memory is more important than speed + * (in Subband, instead of replicating each block in FDCT32 you would do a memmove on the + * last 15 blocks to shift them down one, a hardware style FIFO) + */ +typedef struct _SubbandInfo { + int vbuf[MAX_NCHAN * VBUF_LENGTH]; /* vbuf for fast DCT-based synthesis PQMF - double size for speed (no modulo indexing) */ + int vindex; /* internal index for tracking position in vbuf */ +} SubbandInfo; + +/* bitstream.c */ +void SetBitstreamPointer(BitStreamInfo *bsi, int nBytes, unsigned char *buf); +unsigned int GetBits(BitStreamInfo *bsi, int nBits); +int CalcBitsUsed(BitStreamInfo *bsi, unsigned char *startBuf, int startOffset); + +/* dequant.c, dqchan.c, stproc.c */ +int DequantChannel(int *sampleBuf, int *workBuf, int *nonZeroBound, FrameHeader *fh, SideInfoSub *sis, + ScaleFactorInfoSub *sfis, CriticalBandInfo *cbi); +void MidSideProc(int x[MAX_NCHAN][MAX_NSAMP], int nSamps, int mOut[2]); +void IntensityProcMPEG1(int x[MAX_NCHAN][MAX_NSAMP], int nSamps, FrameHeader *fh, ScaleFactorInfoSub *sfis, + CriticalBandInfo *cbi, int midSideFlag, int mixFlag, int mOut[2]); +void IntensityProcMPEG2(int x[MAX_NCHAN][MAX_NSAMP], int nSamps, FrameHeader *fh, ScaleFactorInfoSub *sfis, + CriticalBandInfo *cbi, ScaleFactorJS *sfjs, int midSideFlag, int mixFlag, int mOut[2]); + +/* dct32.c */ +// about 1 ms faster in RAM, but very large +void FDCT32(int *x, int *d, int offset, int oddBlock, int gb);// __attribute__ ((section (".data"))); + +/* hufftabs.c */ +extern const HuffTabLookup huffTabLookup[HUFF_PAIRTABS]; +extern const int huffTabOffset[HUFF_PAIRTABS]; +extern const unsigned short huffTable[]; +extern const unsigned char quadTable[64+16]; +extern const int quadTabOffset[2]; +extern const int quadTabMaxBits[2]; + +/* polyphase.c (or asmpoly.s) + * some platforms require a C++ compile of all source files, + * so if we're compiling C as C++ and using native assembly + * for these functions we need to prevent C++ name mangling. + */ +#ifdef __cplusplus +extern "C" { +#endif +void PolyphaseMono(short *pcm, int *vbuf, const int *coefBase); +void PolyphaseStereo(short *pcm, int *vbuf, const int *coefBase); +#ifdef __cplusplus +} +#endif + +/* trigtabs.c */ +extern const int imdctWin[4][36]; +extern const int ISFMpeg1[2][7]; +extern const int ISFMpeg2[2][2][16]; +extern const int ISFIIP[2][2]; +extern const int csa[8][2]; +extern const int coef32[31]; +extern const int polyCoef[264]; + +#endif /* _CODER_H */ diff --git a/components/spotify/cspot/bell/libhelix-mp3/dct32.c b/components/spotify/cspot/bell/libhelix-mp3/dct32.c new file mode 100644 index 00000000..4dacff7f --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/dct32.c @@ -0,0 +1,280 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * dct32.c - optimized implementations of 32-point DCT for matrixing stage of + * polyphase filter + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +#define COS0_0 0x4013c251 /* Q31 */ +#define COS0_1 0x40b345bd /* Q31 */ +#define COS0_2 0x41fa2d6d /* Q31 */ +#define COS0_3 0x43f93421 /* Q31 */ +#define COS0_4 0x46cc1bc4 /* Q31 */ +#define COS0_5 0x4a9d9cf0 /* Q31 */ +#define COS0_6 0x4fae3711 /* Q31 */ +#define COS0_7 0x56601ea7 /* Q31 */ +#define COS0_8 0x5f4cf6eb /* Q31 */ +#define COS0_9 0x6b6fcf26 /* Q31 */ +#define COS0_10 0x7c7d1db3 /* Q31 */ +#define COS0_11 0x4ad81a97 /* Q30 */ +#define COS0_12 0x5efc8d96 /* Q30 */ +#define COS0_13 0x41d95790 /* Q29 */ +#define COS0_14 0x6d0b20cf /* Q29 */ +#define COS0_15 0x518522fb /* Q27 */ + +#define COS1_0 0x404f4672 /* Q31 */ +#define COS1_1 0x42e13c10 /* Q31 */ +#define COS1_2 0x48919f44 /* Q31 */ +#define COS1_3 0x52cb0e63 /* Q31 */ +#define COS1_4 0x64e2402e /* Q31 */ +#define COS1_5 0x43e224a9 /* Q30 */ +#define COS1_6 0x6e3c92c1 /* Q30 */ +#define COS1_7 0x519e4e04 /* Q28 */ + +#define COS2_0 0x4140fb46 /* Q31 */ +#define COS2_1 0x4cf8de88 /* Q31 */ +#define COS2_2 0x73326bbf /* Q31 */ +#define COS2_3 0x52036742 /* Q29 */ + +#define COS3_0 0x4545e9ef /* Q31 */ +#define COS3_1 0x539eba45 /* Q30 */ + +#define COS4_0 0x5a82799a /* Q31 */ + +// faster in ROM +static const int dcttab[48] PROGMEM = { + /* first pass */ + COS0_0, COS0_15, COS1_0, /* 31, 27, 31 */ + COS0_1, COS0_14, COS1_1, /* 31, 29, 31 */ + COS0_2, COS0_13, COS1_2, /* 31, 29, 31 */ + COS0_3, COS0_12, COS1_3, /* 31, 30, 31 */ + COS0_4, COS0_11, COS1_4, /* 31, 30, 31 */ + COS0_5, COS0_10, COS1_5, /* 31, 31, 30 */ + COS0_6, COS0_9, COS1_6, /* 31, 31, 30 */ + COS0_7, COS0_8, COS1_7, /* 31, 31, 28 */ + /* second pass */ + COS2_0, COS2_3, COS3_0, /* 31, 29, 31 */ + COS2_1, COS2_2, COS3_1, /* 31, 31, 30 */ + -COS2_0, -COS2_3, COS3_0, /* 31, 29, 31 */ + -COS2_1, -COS2_2, COS3_1, /* 31, 31, 30 */ + COS2_0, COS2_3, COS3_0, /* 31, 29, 31 */ + COS2_1, COS2_2, COS3_1, /* 31, 31, 30 */ + -COS2_0, -COS2_3, COS3_0, /* 31, 29, 31 */ + -COS2_1, -COS2_2, COS3_1, /* 31, 31, 30 */ +}; + +#define D32FP(i, s0, s1, s2) { \ + a0 = buf[i]; a3 = buf[31-i]; \ + a1 = buf[15-i]; a2 = buf[16+i]; \ + b0 = a0 + a3; b3 = MULSHIFT32(*cptr++, a0 - a3) << (s0); \ + b1 = a1 + a2; b2 = MULSHIFT32(*cptr++, a1 - a2) << (s1); \ + buf[i] = b0 + b1; buf[15-i] = MULSHIFT32(*cptr, b0 - b1) << (s2); \ + buf[16+i] = b2 + b3; buf[31-i] = MULSHIFT32(*cptr++, b3 - b2) << (s2); \ +} + +/************************************************************************************** + * Function: FDCT32 + * + * Description: Ken's highly-optimized 32-point DCT (radix-4 + radix-8) + * + * Inputs: input buffer, length = 32 samples + * require at least 6 guard bits in input vector x to avoid possibility + * of overflow in internal calculations (see bbtest_imdct test app) + * buffer offset and oddblock flag for polyphase filter input buffer + * number of guard bits in input + * + * Outputs: output buffer, data copied and interleaved for polyphase filter + * no guarantees about number of guard bits in output + * + * Return: none + * + * Notes: number of muls = 4*8 + 12*4 = 80 + * final stage of DCT is hardcoded to shuffle data into the proper order + * for the polyphase filterbank + * fully unrolled stage 1, for max precision (scale the 1/cos() factors + * differently, depending on magnitude) + * guard bit analysis verified by exhaustive testing of all 2^32 + * combinations of max pos/max neg values in x[] + * + * TODO: code organization and optimization for ARM + * possibly interleave stereo (cut # of coef loads in half - may not have + * enough registers) + **************************************************************************************/ +// about 1ms faster in RAM +/* attribute__ ((section (".data"))) */ void FDCT32(int *buf, int *dest, int offset, int oddBlock, int gb) +{ + int i, s, tmp, es; + const int *cptr = dcttab; + int a0, a1, a2, a3, a4, a5, a6, a7; + int b0, b1, b2, b3, b4, b5, b6, b7; + int *d; + + /* scaling - ensure at least 6 guard bits for DCT + * (in practice this is already true 99% of time, so this code is + * almost never triggered) + */ + es = 0; + if (gb < 6) { + es = 6 - gb; + for (i = 0; i < 32; i++) + buf[i] >>= es; + } + + /* first pass */ + D32FP(0, 1, 5, 1); + D32FP(1, 1, 3, 1); + D32FP(2, 1, 3, 1); + D32FP(3, 1, 2, 1); + D32FP(4, 1, 2, 1); + D32FP(5, 1, 1, 2); + D32FP(6, 1, 1, 2); + D32FP(7, 1, 1, 4); + + /* second pass */ + for (i = 4; i > 0; i--) { + a0 = buf[0]; a7 = buf[7]; a3 = buf[3]; a4 = buf[4]; + b0 = a0 + a7; b7 = MULSHIFT32(*cptr++, a0 - a7) << 1; + b3 = a3 + a4; b4 = MULSHIFT32(*cptr++, a3 - a4) << 3; + a0 = b0 + b3; a3 = MULSHIFT32(*cptr, b0 - b3) << 1; + a4 = b4 + b7; a7 = MULSHIFT32(*cptr++, b7 - b4) << 1; + + a1 = buf[1]; a6 = buf[6]; a2 = buf[2]; a5 = buf[5]; + b1 = a1 + a6; b6 = MULSHIFT32(*cptr++, a1 - a6) << 1; + b2 = a2 + a5; b5 = MULSHIFT32(*cptr++, a2 - a5) << 1; + a1 = b1 + b2; a2 = MULSHIFT32(*cptr, b1 - b2) << 2; + a5 = b5 + b6; a6 = MULSHIFT32(*cptr++, b6 - b5) << 2; + + b0 = a0 + a1; b1 = MULSHIFT32(COS4_0, a0 - a1) << 1; + b2 = a2 + a3; b3 = MULSHIFT32(COS4_0, a3 - a2) << 1; + buf[0] = b0; buf[1] = b1; + buf[2] = b2 + b3; buf[3] = b3; + + b4 = a4 + a5; b5 = MULSHIFT32(COS4_0, a4 - a5) << 1; + b6 = a6 + a7; b7 = MULSHIFT32(COS4_0, a7 - a6) << 1; + b6 += b7; + buf[4] = b4 + b6; buf[5] = b5 + b7; + buf[6] = b5 + b6; buf[7] = b7; + + buf += 8; + } + buf -= 32; /* reset */ + + /* sample 0 - always delayed one block */ + d = dest + 64*16 + ((offset - oddBlock) & 7) + (oddBlock ? 0 : VBUF_LENGTH); + s = buf[ 0]; d[0] = d[8] = s; + + /* samples 16 to 31 */ + d = dest + offset + (oddBlock ? VBUF_LENGTH : 0); + + s = buf[ 1]; d[0] = d[8] = s; d += 64; + + tmp = buf[25] + buf[29]; + s = buf[17] + tmp; d[0] = d[8] = s; d += 64; + s = buf[ 9] + buf[13]; d[0] = d[8] = s; d += 64; + s = buf[21] + tmp; d[0] = d[8] = s; d += 64; + + tmp = buf[29] + buf[27]; + s = buf[ 5]; d[0] = d[8] = s; d += 64; + s = buf[21] + tmp; d[0] = d[8] = s; d += 64; + s = buf[13] + buf[11]; d[0] = d[8] = s; d += 64; + s = buf[19] + tmp; d[0] = d[8] = s; d += 64; + + tmp = buf[27] + buf[31]; + s = buf[ 3]; d[0] = d[8] = s; d += 64; + s = buf[19] + tmp; d[0] = d[8] = s; d += 64; + s = buf[11] + buf[15]; d[0] = d[8] = s; d += 64; + s = buf[23] + tmp; d[0] = d[8] = s; d += 64; + + tmp = buf[31]; + s = buf[ 7]; d[0] = d[8] = s; d += 64; + s = buf[23] + tmp; d[0] = d[8] = s; d += 64; + s = buf[15]; d[0] = d[8] = s; d += 64; + s = tmp; d[0] = d[8] = s; + + /* samples 16 to 1 (sample 16 used again) */ + d = dest + 16 + ((offset - oddBlock) & 7) + (oddBlock ? 0 : VBUF_LENGTH); + + s = buf[ 1]; d[0] = d[8] = s; d += 64; + + tmp = buf[30] + buf[25]; + s = buf[17] + tmp; d[0] = d[8] = s; d += 64; + s = buf[14] + buf[ 9]; d[0] = d[8] = s; d += 64; + s = buf[22] + tmp; d[0] = d[8] = s; d += 64; + s = buf[ 6]; d[0] = d[8] = s; d += 64; + + tmp = buf[26] + buf[30]; + s = buf[22] + tmp; d[0] = d[8] = s; d += 64; + s = buf[10] + buf[14]; d[0] = d[8] = s; d += 64; + s = buf[18] + tmp; d[0] = d[8] = s; d += 64; + s = buf[ 2]; d[0] = d[8] = s; d += 64; + + tmp = buf[28] + buf[26]; + s = buf[18] + tmp; d[0] = d[8] = s; d += 64; + s = buf[12] + buf[10]; d[0] = d[8] = s; d += 64; + s = buf[20] + tmp; d[0] = d[8] = s; d += 64; + s = buf[ 4]; d[0] = d[8] = s; d += 64; + + tmp = buf[24] + buf[28]; + s = buf[20] + tmp; d[0] = d[8] = s; d += 64; + s = buf[ 8] + buf[12]; d[0] = d[8] = s; d += 64; + s = buf[16] + tmp; d[0] = d[8] = s; + + /* this is so rarely invoked that it's not worth making two versions of the output + * shuffle code (one for no shift, one for clip + variable shift) like in IMDCT + * here we just load, clip, shift, and store on the rare instances that es != 0 + */ + if (es) { + d = dest + 64*16 + ((offset - oddBlock) & 7) + (oddBlock ? 0 : VBUF_LENGTH); + s = d[0]; CLIP_2N(s, 31 - es); d[0] = d[8] = (s << es); + + d = dest + offset + (oddBlock ? VBUF_LENGTH : 0); + for (i = 16; i <= 31; i++) { + s = d[0]; CLIP_2N(s, 31 - es); d[0] = d[8] = (s << es); d += 64; + } + + d = dest + 16 + ((offset - oddBlock) & 7) + (oddBlock ? 0 : VBUF_LENGTH); + for (i = 15; i >= 0; i--) { + s = d[0]; CLIP_2N(s, 31 - es); d[0] = d[8] = (s << es); d += 64; + } + } +} diff --git a/components/spotify/cspot/bell/libhelix-mp3/dequant.c b/components/spotify/cspot/bell/libhelix-mp3/dequant.c new file mode 100644 index 00000000..b989b7de --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/dequant.c @@ -0,0 +1,158 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * dequant.c - dequantization, stereo processing (intensity, mid-side), short-block + * coefficient reordering + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +/************************************************************************************** + * Function: Dequantize + * + * Description: dequantize coefficients, decode stereo, reorder short blocks + * (one granule-worth) + * + * Inputs: MP3DecInfo structure filled by UnpackFrameHeader(), UnpackSideInfo(), + * UnpackScaleFactors(), and DecodeHuffman() (for this granule) + * index of current granule + * + * Outputs: dequantized and reordered coefficients in hi->huffDecBuf + * (one granule-worth, all channels), format = Q26 + * operates in-place on huffDecBuf but also needs di->workBuf + * updated hi->nonZeroBound index for both channels + * + * Return: 0 on success, -1 if null input pointers + * + * Notes: In calling output Q(DQ_FRACBITS_OUT), we assume an implicit bias + * of 2^15. Some (floating-point) reference implementations factor this + * into the 2^(0.25 * gain) scaling explicitly. But to avoid precision + * loss, we don't do that. Instead take it into account in the final + * round to PCM (>> by 15 less than we otherwise would have). + * Equivalently, we can think of the dequantized coefficients as + * Q(DQ_FRACBITS_OUT - 15) with no implicit bias. + **************************************************************************************/ +int Dequantize(MP3DecInfo *mp3DecInfo, int gr) +{ + int i, ch, nSamps, mOut[2]; + FrameHeader *fh; + SideInfo *si; + ScaleFactorInfo *sfi; + HuffmanInfo *hi; + DequantInfo *di; + CriticalBandInfo *cbi; + + /* validate pointers */ + if (!mp3DecInfo || !mp3DecInfo->FrameHeaderPS || !mp3DecInfo->SideInfoPS || !mp3DecInfo->ScaleFactorInfoPS || + !mp3DecInfo->HuffmanInfoPS || !mp3DecInfo->DequantInfoPS) + return -1; + + fh = (FrameHeader *)(mp3DecInfo->FrameHeaderPS); + + /* si is an array of up to 4 structs, stored as gr0ch0, gr0ch1, gr1ch0, gr1ch1 */ + si = (SideInfo *)(mp3DecInfo->SideInfoPS); + sfi = (ScaleFactorInfo *)(mp3DecInfo->ScaleFactorInfoPS); + hi = (HuffmanInfo *)mp3DecInfo->HuffmanInfoPS; + di = (DequantInfo *)mp3DecInfo->DequantInfoPS; + cbi = di->cbi; + mOut[0] = mOut[1] = 0; + + /* dequantize all the samples in each channel */ + for (ch = 0; ch < mp3DecInfo->nChans; ch++) { + hi->gb[ch] = DequantChannel(hi->huffDecBuf[ch], di->workBuf, &hi->nonZeroBound[ch], fh, + &si->sis[gr][ch], &sfi->sfis[gr][ch], &cbi[ch]); + } + + /* joint stereo processing assumes one guard bit in input samples + * it's extremely rare not to have at least one gb, so if this is the case + * just make a pass over the data and clip to [-2^30+1, 2^30-1] + * in practice this may never happen + */ + if (fh->modeExt && (hi->gb[0] < 1 || hi->gb[1] < 1)) { + for (i = 0; i < hi->nonZeroBound[0]; i++) { + if (hi->huffDecBuf[0][i] < -0x3fffffff) hi->huffDecBuf[0][i] = -0x3fffffff; + if (hi->huffDecBuf[0][i] > 0x3fffffff) hi->huffDecBuf[0][i] = 0x3fffffff; + } + for (i = 0; i < hi->nonZeroBound[1]; i++) { + if (hi->huffDecBuf[1][i] < -0x3fffffff) hi->huffDecBuf[1][i] = -0x3fffffff; + if (hi->huffDecBuf[1][i] > 0x3fffffff) hi->huffDecBuf[1][i] = 0x3fffffff; + } + } + + /* do mid-side stereo processing, if enabled */ + if (fh->modeExt >> 1) { + if (fh->modeExt & 0x01) { + /* intensity stereo enabled - run mid-side up to start of right zero region */ + if (cbi[1].cbType == 0) + nSamps = fh->sfBand->l[cbi[1].cbEndL + 1]; + else + nSamps = 3 * fh->sfBand->s[cbi[1].cbEndSMax + 1]; + } else { + /* intensity stereo disabled - run mid-side on whole spectrum */ + nSamps = MAX(hi->nonZeroBound[0], hi->nonZeroBound[1]); + } + MidSideProc(hi->huffDecBuf, nSamps, mOut); + } + + /* do intensity stereo processing, if enabled */ + if (fh->modeExt & 0x01) { + nSamps = hi->nonZeroBound[0]; + if (fh->ver == MPEG1) { + IntensityProcMPEG1(hi->huffDecBuf, nSamps, fh, &sfi->sfis[gr][1], di->cbi, + fh->modeExt >> 1, si->sis[gr][1].mixedBlock, mOut); + } else { + IntensityProcMPEG2(hi->huffDecBuf, nSamps, fh, &sfi->sfis[gr][1], di->cbi, &sfi->sfjs, + fh->modeExt >> 1, si->sis[gr][1].mixedBlock, mOut); + } + } + + /* adjust guard bit count and nonZeroBound if we did any stereo processing */ + if (fh->modeExt) { + hi->gb[0] = CLZ(mOut[0]) - 1; + hi->gb[1] = CLZ(mOut[1]) - 1; + nSamps = MAX(hi->nonZeroBound[0], hi->nonZeroBound[1]); + hi->nonZeroBound[0] = nSamps; + hi->nonZeroBound[1] = nSamps; + } + + /* output format Q(DQ_FRACBITS_OUT) */ + return 0; +} diff --git a/components/spotify/cspot/bell/libhelix-mp3/dqchan.c b/components/spotify/cspot/bell/libhelix-mp3/dqchan.c new file mode 100644 index 00000000..db88e11b --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/dqchan.c @@ -0,0 +1,376 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * August 2003 + * + * dqchan.c - dequantization of transform coefficients + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +typedef int ARRAY3[3]; /* for short-block reordering */ + +/* optional pre-emphasis for high-frequency scale factor bands */ +static const char preTab[22] = { 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,2,2,3,3,3,2,0 }; + +/* pow(2,-i/4) for i=0..3, Q31 format */ +const int pow14[4] PROGMEM = { + 0x7fffffff, 0x6ba27e65, 0x5a82799a, 0x4c1bf829 +}; + +/* pow(2,-i/4) * pow(j,4/3) for i=0..3 j=0..15, Q25 format */ +const int pow43_14[4][16] PROGMEM = { +{ 0x00000000, 0x10000000, 0x285145f3, 0x453a5cdb, /* Q28 */ + 0x0cb2ff53, 0x111989d6, 0x15ce31c8, 0x1ac7f203, + 0x20000000, 0x257106b9, 0x2b16b4a3, 0x30ed74b4, + 0x36f23fa5, 0x3d227bd3, 0x437be656, 0x49fc823c, }, + +{ 0x00000000, 0x0d744fcd, 0x21e71f26, 0x3a36abd9, + 0x0aadc084, 0x0e610e6e, 0x12560c1d, 0x168523cf, + 0x1ae89f99, 0x1f7c03a4, 0x243bae49, 0x29249c67, + 0x2e34420f, 0x33686f85, 0x38bf3dff, 0x3e370182, }, + +{ 0x00000000, 0x0b504f33, 0x1c823e07, 0x30f39a55, + 0x08facd62, 0x0c176319, 0x0f6b3522, 0x12efe2ad, + 0x16a09e66, 0x1a79a317, 0x1e77e301, 0x2298d5b4, + 0x26da56fc, 0x2b3a902a, 0x2fb7e7e7, 0x3450f650, }, + +{ 0x00000000, 0x09837f05, 0x17f910d7, 0x2929c7a9, + 0x078d0dfa, 0x0a2ae661, 0x0cf73154, 0x0fec91cb, + 0x1306fe0a, 0x16434a6c, 0x199ee595, 0x1d17ae3d, + 0x20abd76a, 0x2459d551, 0x28204fbb, 0x2bfe1808, }, +}; + +/* pow(j,4/3) for j=16..63, Q23 format */ +const int pow43[] PROGMEM = { + 0x1428a2fa, 0x15db1bd6, 0x1796302c, 0x19598d85, + 0x1b24e8bb, 0x1cf7fcfa, 0x1ed28af2, 0x20b4582a, + 0x229d2e6e, 0x248cdb55, 0x26832fda, 0x28800000, + 0x2a832287, 0x2c8c70a8, 0x2e9bc5d8, 0x30b0ff99, + 0x32cbfd4a, 0x34eca001, 0x3712ca62, 0x393e6088, + 0x3b6f47e0, 0x3da56717, 0x3fe0a5fc, 0x4220ed72, + 0x44662758, 0x46b03e7c, 0x48ff1e87, 0x4b52b3f3, + 0x4daaebfd, 0x5007b497, 0x5268fc62, 0x54ceb29c, + 0x5738c721, 0x59a72a59, 0x5c19cd35, 0x5e90a129, + 0x610b9821, 0x638aa47f, 0x660db90f, 0x6894c90b, + 0x6b1fc80c, 0x6daeaa0d, 0x70416360, 0x72d7e8b0, + 0x75722ef9, 0x78102b85, 0x7ab1d3ec, 0x7d571e09, +}; + +/* sqrt(0.5) in Q31 format */ +#define SQRTHALF 0x5a82799a + +/* + * Minimax polynomial approximation to pow(x, 4/3), over the range + * poly43lo: x = [0.5, 0.7071] + * poly43hi: x = [0.7071, 1.0] + * + * Relative error < 1E-7 + * Coefs are scaled by 4, 2, 1, 0.5, 0.25 + */ +static const unsigned int poly43lo[5] PROGMEM = { 0x29a0bda9, 0xb02e4828, 0x5957aa1b, 0x236c498d, 0xff581859 }; +static const unsigned int poly43hi[5] PROGMEM = { 0x10852163, 0xd333f6a4, 0x46e9408b, 0x27c2cef0, 0xfef577b4 }; + +/* pow(2, i*4/3) as exp and frac */ +const int pow2exp[8] PROGMEM = { 14, 13, 11, 10, 9, 7, 6, 5 }; + +const int pow2frac[8] PROGMEM = { + 0x6597fa94, 0x50a28be6, 0x7fffffff, 0x6597fa94, + 0x50a28be6, 0x7fffffff, 0x6597fa94, 0x50a28be6 +}; + +/************************************************************************************** + * Function: DequantBlock + * + * Description: Ken's highly-optimized, low memory dequantizer performing the operation + * y = pow(x, 4.0/3.0) * pow(2, 25 - scale/4.0) + * + * Inputs: input buffer of decode Huffman codewords (signed-magnitude) + * output buffer of same length (in-place (outbuf = inbuf) is allowed) + * number of samples + * + * Outputs: dequantized samples in Q25 format + * + * Return: bitwise-OR of the unsigned outputs (for guard bit calculations) + **************************************************************************************/ +/* __attribute__ ((section (".data"))) */ static int DequantBlock(int *inbuf, int *outbuf, int num, int scale) +{ + int tab4[4]; + int scalef, scalei, shift; + int sx, x, y; + int mask = 0; + const int *tab16; + const unsigned int *coef; + + tab16 = pow43_14[scale & 0x3]; + scalef = pow14[scale & 0x3]; + scalei = MIN(scale >> 2, 31); /* smallest input scale = -47, so smallest scalei = -12 */ + + /* cache first 4 values */ + shift = MIN(scalei + 3, 31); + shift = MAX(shift, 0); + tab4[0] = 0; + tab4[1] = tab16[1] >> shift; + tab4[2] = tab16[2] >> shift; + tab4[3] = tab16[3] >> shift; + + do { + + sx = *inbuf++; + x = sx & 0x7fffffff; /* sx = sign|mag */ + + if (x < 4) { + + y = tab4[x]; + + } else if (x < 16) { + + y = tab16[x]; + y = (scalei < 0) ? y << -scalei : y >> scalei; + + } else { + + if (x < 64) { + + y = pow43[x-16]; + + /* fractional scale */ + y = MULSHIFT32(y, scalef); + shift = scalei - 3; + + } else { + + /* normalize to [0x40000000, 0x7fffffff] */ + x <<= 17; + shift = 0; + if (x < 0x08000000) + x <<= 4, shift += 4; + if (x < 0x20000000) + x <<= 2, shift += 2; + if (x < 0x40000000) + x <<= 1, shift += 1; + + coef = (x < SQRTHALF) ? poly43lo : poly43hi; + + /* polynomial */ + y = coef[0]; + y = MULSHIFT32(y, x) + coef[1]; + y = MULSHIFT32(y, x) + coef[2]; + y = MULSHIFT32(y, x) + coef[3]; + y = MULSHIFT32(y, x) + coef[4]; + y = MULSHIFT32(y, pow2frac[shift]) << 3; + + /* fractional scale */ + y = MULSHIFT32(y, scalef); + shift = scalei - pow2exp[shift]; + } + + /* integer scale */ + if (shift < 0) { + shift = -shift; + if (y > (0x7fffffff >> shift)) + y = 0x7fffffff; /* clip */ + else + y <<= shift; + } else { + y >>= shift; + } + } + + /* sign and store */ + mask |= y; + *outbuf++ = (sx < 0) ? -y : y; + + } while (--num); + + return mask; +} + +/************************************************************************************** + * Function: DequantChannel + * + * Description: dequantize one granule, one channel worth of decoded Huffman codewords + * + * Inputs: sample buffer (decoded Huffman codewords), length = MAX_NSAMP samples + * work buffer for reordering short-block, length = MAX_REORDER_SAMPS + * samples (3 * width of largest short-block critical band) + * non-zero bound for this channel/granule + * valid FrameHeader, SideInfoSub, ScaleFactorInfoSub, and CriticalBandInfo + * structures for this channel/granule + * + * Outputs: MAX_NSAMP dequantized samples in sampleBuf + * updated non-zero bound (indicating which samples are != 0 after DQ) + * filled-in cbi structure indicating start and end critical bands + * + * Return: minimum number of guard bits in dequantized sampleBuf + * + * Notes: dequantized samples in Q(DQ_FRACBITS_OUT) format + **************************************************************************************/ +/* __attribute__ ((section (".data"))) */ int DequantChannel(int *sampleBuf, int *workBuf, int *nonZeroBound, FrameHeader *fh, SideInfoSub *sis, + ScaleFactorInfoSub *sfis, CriticalBandInfo *cbi) +{ + int i, j, w, cb; + int /* cbStartL, */ cbEndL, cbStartS, cbEndS; + int nSamps, nonZero, sfactMultiplier, gbMask; + int globalGain, gainI; + int cbMax[3]; + ARRAY3 *buf; /* short block reorder */ + + /* set default start/end points for short/long blocks - will update with non-zero cb info */ + if (sis->blockType == 2) { + // cbStartL = 0; + if (sis->mixedBlock) { + cbEndL = (fh->ver == MPEG1 ? 8 : 6); + cbStartS = 3; + } else { + cbEndL = 0; + cbStartS = 0; + } + cbEndS = 13; + } else { + /* long block */ + //cbStartL = 0; + cbEndL = 22; + cbStartS = 13; + cbEndS = 13; + } + cbMax[2] = cbMax[1] = cbMax[0] = 0; + gbMask = 0; + i = 0; + + /* sfactScale = 0 --> quantizer step size = 2 + * sfactScale = 1 --> quantizer step size = sqrt(2) + * so sfactMultiplier = 2 or 4 (jump through globalGain by powers of 2 or sqrt(2)) + */ + sfactMultiplier = 2 * (sis->sfactScale + 1); + + /* offset globalGain by -2 if midSide enabled, for 1/sqrt(2) used in MidSideProc() + * (DequantBlock() does 0.25 * gainI so knocking it down by two is the same as + * dividing every sample by sqrt(2) = multiplying by 2^-.5) + */ + globalGain = sis->globalGain; + if (fh->modeExt >> 1) + globalGain -= 2; + globalGain += IMDCT_SCALE; /* scale everything by sqrt(2), for fast IMDCT36 */ + + /* long blocks */ + for (cb = 0; cb < cbEndL; cb++) { + + nonZero = 0; + nSamps = fh->sfBand->l[cb + 1] - fh->sfBand->l[cb]; + gainI = 210 - globalGain + sfactMultiplier * (sfis->l[cb] + (sis->preFlag ? (int)preTab[cb] : 0)); + + nonZero |= DequantBlock(sampleBuf + i, sampleBuf + i, nSamps, gainI); + i += nSamps; + + /* update highest non-zero critical band */ + if (nonZero) + cbMax[0] = cb; + gbMask |= nonZero; + + if (i >= *nonZeroBound) + break; + } + + /* set cbi (Type, EndS[], EndSMax will be overwritten if we proceed to do short blocks) */ + cbi->cbType = 0; /* long only */ + cbi->cbEndL = cbMax[0]; + cbi->cbEndS[0] = cbi->cbEndS[1] = cbi->cbEndS[2] = 0; + cbi->cbEndSMax = 0; + + /* early exit if no short blocks */ + if (cbStartS >= 12) + return CLZ(gbMask) - 1; + + /* short blocks */ + cbMax[2] = cbMax[1] = cbMax[0] = cbStartS; + for (cb = cbStartS; cb < cbEndS; cb++) { + + nSamps = fh->sfBand->s[cb + 1] - fh->sfBand->s[cb]; + for (w = 0; w < 3; w++) { + nonZero = 0; + gainI = 210 - globalGain + 8*sis->subBlockGain[w] + sfactMultiplier*(sfis->s[cb][w]); + + nonZero |= DequantBlock(sampleBuf + i + nSamps*w, workBuf + nSamps*w, nSamps, gainI); + + /* update highest non-zero critical band */ + if (nonZero) + cbMax[w] = cb; + gbMask |= nonZero; + } + + /* reorder blocks */ + buf = (ARRAY3 *)(sampleBuf + i); + i += 3*nSamps; + for (j = 0; j < nSamps; j++) { + buf[j][0] = workBuf[0*nSamps + j]; + buf[j][1] = workBuf[1*nSamps + j]; + buf[j][2] = workBuf[2*nSamps + j]; + } + + ASSERT(3*nSamps <= MAX_REORDER_SAMPS); + + if (i >= *nonZeroBound) + break; + } + + /* i = last non-zero INPUT sample processed, which corresponds to highest possible non-zero + * OUTPUT sample (after reorder) + * however, the original nzb is no longer necessarily true + * for each cb, buf[][] is updated with 3*nSamps samples (i increases 3*nSamps each time) + * (buf[j + 1][0] = 3 (input) samples ahead of buf[j][0]) + * so update nonZeroBound to i + */ + *nonZeroBound = i; + + ASSERT(*nonZeroBound <= MAX_NSAMP); + + cbi->cbType = (sis->mixedBlock ? 2 : 1); /* 2 = mixed short/long, 1 = short only */ + + cbi->cbEndS[0] = cbMax[0]; + cbi->cbEndS[1] = cbMax[1]; + cbi->cbEndS[2] = cbMax[2]; + + cbi->cbEndSMax = cbMax[0]; + cbi->cbEndSMax = MAX(cbi->cbEndSMax, cbMax[1]); + cbi->cbEndSMax = MAX(cbi->cbEndSMax, cbMax[2]); + + return CLZ(gbMask) - 1; +} + diff --git a/components/spotify/cspot/bell/libhelix-mp3/huffman.c b/components/spotify/cspot/bell/libhelix-mp3/huffman.c new file mode 100644 index 00000000..28f61d4a --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/huffman.c @@ -0,0 +1,462 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * July 2003 + * + * huffman.c - Huffman decoding of transform coefficients + **************************************************************************************/ + +#include "coder.h" +#define PGM_READ_UNALIGNED 0 // Only support aligned reads, faster + +/* helper macros - see comments in hufftabs.c about the format of the huffman tables */ +#define GetMaxbits(x) ((int)( (((unsigned short)(x)) >> 0) & 0x000f)) +#define GetHLen(x) ((int)( (((unsigned short)(x)) >> 12) & 0x000f)) +#define GetCWY(x) ((int)( (((unsigned short)(x)) >> 8) & 0x000f)) +#define GetCWX(x) ((int)( (((unsigned short)(x)) >> 4) & 0x000f)) +#define GetSignBits(x) ((int)( (((unsigned short)(x)) >> 0) & 0x000f)) + +#define GetHLenQ(x) ((int)( (((unsigned char)(x)) >> 4) & 0x0f)) +#define GetCWVQ(x) ((int)( (((unsigned char)(x)) >> 3) & 0x01)) +#define GetCWWQ(x) ((int)( (((unsigned char)(x)) >> 2) & 0x01)) +#define GetCWXQ(x) ((int)( (((unsigned char)(x)) >> 1) & 0x01)) +#define GetCWYQ(x) ((int)( (((unsigned char)(x)) >> 0) & 0x01)) + +/* apply sign of s to the positive number x (save in MSB, will do two's complement in dequant) */ +#define ApplySign(x, s) { (x) |= ((s) & 0x80000000); } + +/************************************************************************************** + * Function: DecodeHuffmanPairs + * + * Description: decode 2-way vector Huffman codes in the "bigValues" region of spectrum + * + * Inputs: valid BitStreamInfo struct, pointing to start of pair-wise codes + * pointer to xy buffer to received decoded values + * number of codewords to decode + * index of Huffman table to use + * number of bits remaining in bitstream + * + * Outputs: pairs of decoded coefficients in vwxy + * updated BitStreamInfo struct + * + * Return: number of bits used, or -1 if out of bits + * + * Notes: assumes that nVals is an even number + * si_huff.bit tests every Huffman codeword in every table (though not + * necessarily all linBits outputs for x,y > 15) + **************************************************************************************/ +// no improvement with section=data +static int DecodeHuffmanPairs(int *xy, int nVals, int tabIdx, int bitsLeft, unsigned char *buf, int bitOffset) +{ + int i, x, y; + int cachedBits, padBits, len, startBits, linBits, maxBits, minBits; + HuffTabType tabType; + unsigned short cw, *tBase, *tCurr; + unsigned int cache; + + if(nVals <= 0) + return 0; + + if (bitsLeft < 0) + return -1; + startBits = bitsLeft; + + tBase = (unsigned short *)(huffTable + huffTabOffset[tabIdx]); + linBits = huffTabLookup[tabIdx].linBits; + tabType = huffTabLookup[tabIdx].tabType; + + ASSERT(!(nVals & 0x01)); + ASSERT(tabIdx < HUFF_PAIRTABS); + ASSERT(tabIdx >= 0); + ASSERT(tabType != invalidTab); + + /* initially fill cache with any partial byte */ + cache = 0; + cachedBits = (8 - bitOffset) & 0x07; + if (cachedBits) + cache = (unsigned int)(*buf++) << (32 - cachedBits); + bitsLeft -= cachedBits; + + if (tabType == noBits) { + /* table 0, no data, x = y = 0 */ + for (i = 0; i < nVals; i+=2) { + xy[i+0] = 0; + xy[i+1] = 0; + } + return 0; + } else if (tabType == oneShot) { + /* single lookup, no escapes */ + maxBits = GetMaxbits(pgm_read_word(&tBase[0])); + tBase++; + padBits = 0; + while (nVals > 0) { + /* refill cache - assumes cachedBits <= 16 */ + if (bitsLeft >= 16) { + /* load 2 new bytes into left-justified cache */ + cache |= (unsigned int)(*buf++) << (24 - cachedBits); + cache |= (unsigned int)(*buf++) << (16 - cachedBits); + cachedBits += 16; + bitsLeft -= 16; + } else { + /* last time through, pad cache with zeros and drain cache */ + if (cachedBits + bitsLeft <= 0) return -1; + if (bitsLeft > 0) cache |= (unsigned int)(*buf++) << (24 - cachedBits); + if (bitsLeft > 8) cache |= (unsigned int)(*buf++) << (16 - cachedBits); + cachedBits += bitsLeft; + bitsLeft = 0; + + cache &= (signed int)0x80000000 >> (cachedBits - 1); + padBits = 11; + cachedBits += padBits; /* okay if this is > 32 (0's automatically shifted in from right) */ + } + + /* largest maxBits = 9, plus 2 for sign bits, so make sure cache has at least 11 bits */ + while (nVals > 0 && cachedBits >= 11 ) { + cw = pgm_read_word(&tBase[cache >> (32 - maxBits)]); + len = GetHLen(cw); + cachedBits -= len; + cache <<= len; + + x = GetCWX(cw); if (x) {ApplySign(x, cache); cache <<= 1; cachedBits--;} + y = GetCWY(cw); if (y) {ApplySign(y, cache); cache <<= 1; cachedBits--;} + + /* ran out of bits - should never have consumed padBits */ + if (cachedBits < padBits) + return -1; + + *xy++ = x; + *xy++ = y; + nVals -= 2; + } + } + bitsLeft += (cachedBits - padBits); + return (startBits - bitsLeft); + } else if (tabType == loopLinbits || tabType == loopNoLinbits) { + tCurr = tBase; + padBits = 0; + while (nVals > 0) { + /* refill cache - assumes cachedBits <= 16 */ + if (bitsLeft >= 16) { + /* load 2 new bytes into left-justified cache */ + cache |= (unsigned int)(*buf++) << (24 - cachedBits); + cache |= (unsigned int)(*buf++) << (16 - cachedBits); + cachedBits += 16; + bitsLeft -= 16; + } else { + /* last time through, pad cache with zeros and drain cache */ + if (cachedBits + bitsLeft <= 0) return -1; + if (bitsLeft > 0) cache |= (unsigned int)(*buf++) << (24 - cachedBits); + if (bitsLeft > 8) cache |= (unsigned int)(*buf++) << (16 - cachedBits); + cachedBits += bitsLeft; + bitsLeft = 0; + + cache &= (signed int)0x80000000 >> (cachedBits - 1); + padBits = 11; + cachedBits += padBits; /* okay if this is > 32 (0's automatically shifted in from right) */ + } + + /* largest maxBits = 9, plus 2 for sign bits, so make sure cache has at least 11 bits */ + while (nVals > 0 && cachedBits >= 11 ) { + maxBits = GetMaxbits(pgm_read_word(&tCurr[0])); + cw = pgm_read_word(&tCurr[(cache >> (32 - maxBits)) + 1]); + len = GetHLen(cw); + if (!len) { + cachedBits -= maxBits; + cache <<= maxBits; + tCurr += cw; + continue; + } + cachedBits -= len; + cache <<= len; + + x = GetCWX(cw); + y = GetCWY(cw); + + if (x == 15 && tabType == loopLinbits) { + minBits = linBits + 1 + (y ? 1 : 0); + if (cachedBits + bitsLeft < minBits) + return -1; + while (cachedBits < minBits) { + cache |= (unsigned int)(*buf++) << (24 - cachedBits); + cachedBits += 8; + bitsLeft -= 8; + } + if (bitsLeft < 0) { + cachedBits += bitsLeft; + bitsLeft = 0; + cache &= (signed int)0x80000000 >> (cachedBits - 1); + } + x += (int)(cache >> (32 - linBits)); + cachedBits -= linBits; + cache <<= linBits; + } + if (x) {ApplySign(x, cache); cache <<= 1; cachedBits--;} + + if (y == 15 && tabType == loopLinbits) { + minBits = linBits + 1; + if (cachedBits + bitsLeft < minBits) + return -1; + while (cachedBits < minBits) { + cache |= (unsigned int)(*buf++) << (24 - cachedBits); + cachedBits += 8; + bitsLeft -= 8; + } + if (bitsLeft < 0) { + cachedBits += bitsLeft; + bitsLeft = 0; + cache &= (signed int)0x80000000 >> (cachedBits - 1); + } + y += (int)(cache >> (32 - linBits)); + cachedBits -= linBits; + cache <<= linBits; + } + if (y) {ApplySign(y, cache); cache <<= 1; cachedBits--;} + + /* ran out of bits - should never have consumed padBits */ + if (cachedBits < padBits) + return -1; + + *xy++ = x; + *xy++ = y; + nVals -= 2; + tCurr = tBase; + } + } + bitsLeft += (cachedBits - padBits); + return (startBits - bitsLeft); + } + + /* error in bitstream - trying to access unused Huffman table */ + return -1; +} + +/************************************************************************************** + * Function: DecodeHuffmanQuads + * + * Description: decode 4-way vector Huffman codes in the "count1" region of spectrum + * + * Inputs: valid BitStreamInfo struct, pointing to start of quadword codes + * pointer to vwxy buffer to received decoded values + * maximum number of codewords to decode + * index of quadword table (0 = table A, 1 = table B) + * number of bits remaining in bitstream + * + * Outputs: quadruples of decoded coefficients in vwxy + * updated BitStreamInfo struct + * + * Return: index of the first "zero_part" value (index of the first sample + * of the quad word after which all samples are 0) + * + * Notes: si_huff.bit tests every vwxy output in both quad tables + **************************************************************************************/ +// no improvement with section=data +static int DecodeHuffmanQuads(int *vwxy, int nVals, int tabIdx, int bitsLeft, unsigned char *buf, int bitOffset) +{ + int i, v, w, x, y; + int len, maxBits, cachedBits, padBits; + unsigned int cache; + unsigned char cw, *tBase; + + if (bitsLeft <= 0) + return 0; + + tBase = (unsigned char *)quadTable + quadTabOffset[tabIdx]; + maxBits = quadTabMaxBits[tabIdx]; + + /* initially fill cache with any partial byte */ + cache = 0; + cachedBits = (8 - bitOffset) & 0x07; + if (cachedBits) + cache = (unsigned int)(*buf++) << (32 - cachedBits); + bitsLeft -= cachedBits; + + i = padBits = 0; + while (i < (nVals - 3)) { + /* refill cache - assumes cachedBits <= 16 */ + if (bitsLeft >= 16) { + /* load 2 new bytes into left-justified cache */ + cache |= (unsigned int)(*buf++) << (24 - cachedBits); + cache |= (unsigned int)(*buf++) << (16 - cachedBits); + cachedBits += 16; + bitsLeft -= 16; + } else { + /* last time through, pad cache with zeros and drain cache */ + if (cachedBits + bitsLeft <= 0) return i; + if (bitsLeft > 0) cache |= (unsigned int)(*buf++) << (24 - cachedBits); + if (bitsLeft > 8) cache |= (unsigned int)(*buf++) << (16 - cachedBits); + cachedBits += bitsLeft; + bitsLeft = 0; + + cache &= (signed int)0x80000000 >> (cachedBits - 1); + padBits = 10; + cachedBits += padBits; /* okay if this is > 32 (0's automatically shifted in from right) */ + } + + /* largest maxBits = 6, plus 4 for sign bits, so make sure cache has at least 10 bits */ + while (i < (nVals - 3) && cachedBits >= 10 ) { + cw = pgm_read_byte(&tBase[cache >> (32 - maxBits)]); + len = GetHLenQ(cw); + cachedBits -= len; + cache <<= len; + + v = GetCWVQ(cw); if(v) {ApplySign(v, cache); cache <<= 1; cachedBits--;} + w = GetCWWQ(cw); if(w) {ApplySign(w, cache); cache <<= 1; cachedBits--;} + x = GetCWXQ(cw); if(x) {ApplySign(x, cache); cache <<= 1; cachedBits--;} + y = GetCWYQ(cw); if(y) {ApplySign(y, cache); cache <<= 1; cachedBits--;} + + /* ran out of bits - okay (means we're done) */ + if (cachedBits < padBits) + return i; + + *vwxy++ = v; + *vwxy++ = w; + *vwxy++ = x; + *vwxy++ = y; + i += 4; + } + } + + /* decoded max number of quad values */ + return i; +} + +/************************************************************************************** + * Function: DecodeHuffman + * + * Description: decode one granule, one channel worth of Huffman codes + * + * Inputs: MP3DecInfo structure filled by UnpackFrameHeader(), UnpackSideInfo(), + * and UnpackScaleFactors() (for this granule) + * buffer pointing to start of Huffman data in MP3 frame + * pointer to bit offset (0-7) indicating starting bit in buf[0] + * number of bits in the Huffman data section of the frame + * (could include padding bits) + * index of current granule and channel + * + * Outputs: decoded coefficients in hi->huffDecBuf[ch] (hi pointer in mp3DecInfo) + * updated bitOffset + * + * Return: length (in bytes) of Huffman codes + * bitOffset also returned in parameter (0 = MSB, 7 = LSB of + * byte located at buf + offset) + * -1 if null input pointers, huffBlockBits < 0, or decoder runs + * out of bits prematurely (invalid bitstream) + **************************************************************************************/ +// .data about 1ms faster per frame +/* __attribute__ ((section (".data"))) */ int DecodeHuffman(MP3DecInfo *mp3DecInfo, unsigned char *buf, int *bitOffset, int huffBlockBits, int gr, int ch) +{ + int r1Start, r2Start, rEnd[4]; /* region boundaries */ + int i, w, bitsUsed, bitsLeft; + unsigned char *startBuf = buf; + + FrameHeader *fh; + SideInfo *si; + SideInfoSub *sis; + //ScaleFactorInfo *sfi; + HuffmanInfo *hi; + + /* validate pointers */ + if (!mp3DecInfo || !mp3DecInfo->FrameHeaderPS || !mp3DecInfo->SideInfoPS || !mp3DecInfo->ScaleFactorInfoPS || !mp3DecInfo->HuffmanInfoPS) + return -1; + + fh = ((FrameHeader *)(mp3DecInfo->FrameHeaderPS)); + si = ((SideInfo *)(mp3DecInfo->SideInfoPS)); + sis = &si->sis[gr][ch]; + //sfi = ((ScaleFactorInfo *)(mp3DecInfo->ScaleFactorInfoPS)); + hi = (HuffmanInfo*)(mp3DecInfo->HuffmanInfoPS); + + if (huffBlockBits < 0) + return -1; + + /* figure out region boundaries (the first 2*bigVals coefficients divided into 3 regions) */ + if (sis->winSwitchFlag && sis->blockType == 2) { + if (sis->mixedBlock == 0) { + r1Start = fh->sfBand->s[(sis->region0Count + 1)/3] * 3; + } else { + if (fh->ver == MPEG1) { + r1Start = fh->sfBand->l[sis->region0Count + 1]; + } else { + /* see MPEG2 spec for explanation */ + w = fh->sfBand->s[4] - fh->sfBand->s[3]; + r1Start = fh->sfBand->l[6] + 2*w; + } + } + r2Start = MAX_NSAMP; /* short blocks don't have region 2 */ + } else { + r1Start = fh->sfBand->l[sis->region0Count + 1]; + r2Start = fh->sfBand->l[sis->region0Count + 1 + sis->region1Count + 1]; + } + + /* offset rEnd index by 1 so first region = rEnd[1] - rEnd[0], etc. */ + rEnd[3] = MIN(MAX_NSAMP, 2 * sis->nBigvals); + rEnd[2] = MIN(r2Start, rEnd[3]); + rEnd[1] = MIN(r1Start, rEnd[3]); + rEnd[0] = 0; + + /* rounds up to first all-zero pair (we don't check last pair for (x,y) == (non-zero, zero)) */ + hi->nonZeroBound[ch] = rEnd[3]; + + /* decode Huffman pairs (rEnd[i] are always even numbers) */ + bitsLeft = huffBlockBits; + for (i = 0; i < 3; i++) { + bitsUsed = DecodeHuffmanPairs(hi->huffDecBuf[ch] + rEnd[i], rEnd[i+1] - rEnd[i], sis->tableSelect[i], bitsLeft, buf, *bitOffset); + if (bitsUsed < 0 || bitsUsed > bitsLeft) /* error - overran end of bitstream */ + return -1; + + /* update bitstream position */ + buf += (bitsUsed + *bitOffset) >> 3; + *bitOffset = (bitsUsed + *bitOffset) & 0x07; + bitsLeft -= bitsUsed; + } + + /* decode Huffman quads (if any) */ + hi->nonZeroBound[ch] += DecodeHuffmanQuads(hi->huffDecBuf[ch] + rEnd[3], MAX_NSAMP - rEnd[3], sis->count1TableSelect, bitsLeft, buf, *bitOffset); + + ASSERT(hi->nonZeroBound[ch] <= MAX_NSAMP); + for (i = hi->nonZeroBound[ch]; i < MAX_NSAMP; i++) + hi->huffDecBuf[ch][i] = 0; + + /* If bits used for 576 samples < huffBlockBits, then the extras are considered + * to be stuffing bits (throw away, but need to return correct bitstream position) + */ + buf += (bitsLeft + *bitOffset) >> 3; + *bitOffset = (bitsLeft + *bitOffset) & 0x07; + + return (buf - startBuf); +} + diff --git a/components/spotify/cspot/bell/libhelix-mp3/hufftabs.c b/components/spotify/cspot/bell/libhelix-mp3/hufftabs.c new file mode 100644 index 00000000..90c9e257 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/hufftabs.c @@ -0,0 +1,755 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * hufftabs.c - compressed Huffman code tables + **************************************************************************************/ + +#include "coder.h" + +/* NOTE - regenerated tables to use shorts instead of ints + * (all needed data can fit in 16 bits - see below) + * + * format 0xABCD + * A = length of codeword + * B = y value + * C = x value + * D = number of sign bits (0, 1, or 2) + * + * to read a CW, the code reads maxbits from the stream (dep. on + * table index), but doesn't remove them from the bitstream reader + * then it gets the correct CW by direct lookup into the table + * of length (2^maxbits) (more complicated for non-oneShot...) + * for CW's with hlen < maxbits, there are multiple entries in the + * table (extra bits are don't cares) + * the bitstream reader then "purges" (or removes) only the correct + * number of bits for the chosen CW + * + * entries starting with F are special: D (signbits) is maxbits, + * so the decoder always checks huffTableXX[0] first, gets the + * signbits, and reads that many bits from the bitstream + * (sometimes it takes > 1 read to get the value, so maxbits is + * can get updated by jumping to another value starting with 0xF) + * entries starting with 0 are also special: A = hlen = 0, rest of + * value is an offset to jump higher in the table (for tables of + * type loopNoLinbits or loopLinbits) + */ + +/* store Huffman codes as one big table plus table of offsets, since some platforms + * don't properly support table-of-tables (table of pointers to other const tables) + */ +const unsigned short huffTable[] PROGMEM = { + /* huffTable01[9] */ + 0xf003, 0x3112, 0x3101, 0x2011, 0x2011, 0x1000, 0x1000, 0x1000, + 0x1000, + + /* huffTable02[65] */ + 0xf006, 0x6222, 0x6201, 0x5212, 0x5212, 0x5122, 0x5122, 0x5021, + 0x5021, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, + + /* huffTable03[65] */ + 0xf006, 0x6222, 0x6201, 0x5212, 0x5212, 0x5122, 0x5122, 0x5021, + 0x5021, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, + 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, 0x2101, + 0x2101, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, + + /* huffTable05[257] */ + 0xf008, 0x8332, 0x8322, 0x7232, 0x7232, 0x6132, 0x6132, 0x6132, + 0x6132, 0x7312, 0x7312, 0x7301, 0x7301, 0x7031, 0x7031, 0x7222, + 0x7222, 0x6212, 0x6212, 0x6212, 0x6212, 0x6122, 0x6122, 0x6122, + 0x6122, 0x6201, 0x6201, 0x6201, 0x6201, 0x6021, 0x6021, 0x6021, + 0x6021, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, + + /* huffTable06[129] */ + 0xf007, 0x7332, 0x7301, 0x6322, 0x6322, 0x6232, 0x6232, 0x6031, + 0x6031, 0x5312, 0x5312, 0x5312, 0x5312, 0x5132, 0x5132, 0x5132, + 0x5132, 0x5222, 0x5222, 0x5222, 0x5222, 0x5201, 0x5201, 0x5201, + 0x5201, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, + 0x4212, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, + 0x4122, 0x4021, 0x4021, 0x4021, 0x4021, 0x4021, 0x4021, 0x4021, + 0x4021, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, + 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, + 0x3000, + + /* huffTable07[110] */ + 0xf006, 0x0041, 0x0052, 0x005b, 0x0060, 0x0063, 0x0068, 0x006b, + 0x6212, 0x5122, 0x5122, 0x6201, 0x6021, 0x4112, 0x4112, 0x4112, + 0x4112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0xf004, 0x4552, 0x4542, 0x4452, 0x4352, 0x3532, 0x3532, + 0x3442, 0x3442, 0x3522, 0x3522, 0x3252, 0x3252, 0x2512, 0x2512, + 0x2512, 0x2512, 0xf003, 0x2152, 0x2152, 0x3501, 0x3432, 0x2051, + 0x2051, 0x3342, 0x3332, 0xf002, 0x2422, 0x2242, 0x1412, 0x1412, + 0xf001, 0x1142, 0x1041, 0xf002, 0x2401, 0x2322, 0x2232, 0x2301, + 0xf001, 0x1312, 0x1132, 0xf001, 0x1031, 0x1222, + + /* huffTable08[280] */ + 0xf008, 0x0101, 0x010a, 0x010f, 0x8512, 0x8152, 0x0112, 0x0115, + 0x8422, 0x8242, 0x8412, 0x7142, 0x7142, 0x8401, 0x8041, 0x8322, + 0x8232, 0x8312, 0x8132, 0x8301, 0x8031, 0x6222, 0x6222, 0x6222, + 0x6222, 0x6201, 0x6201, 0x6201, 0x6201, 0x6021, 0x6021, 0x6021, + 0x6021, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, + 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, + 0x4212, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, + 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, + 0x4122, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, 0x2112, + 0x2112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0xf003, 0x3552, 0x3452, 0x2542, 0x2542, 0x1352, 0x1352, + 0x1352, 0x1352, 0xf002, 0x2532, 0x2442, 0x1522, 0x1522, 0xf001, + 0x1252, 0x1501, 0xf001, 0x1432, 0x1342, 0xf001, 0x1051, 0x1332, + + /* huffTable09[93] */ + 0xf006, 0x0041, 0x004a, 0x004f, 0x0052, 0x0057, 0x005a, 0x6412, + 0x6142, 0x6322, 0x6232, 0x5312, 0x5312, 0x5132, 0x5132, 0x6301, + 0x6031, 0x5222, 0x5222, 0x5201, 0x5201, 0x4212, 0x4212, 0x4212, + 0x4212, 0x4122, 0x4122, 0x4122, 0x4122, 0x4021, 0x4021, 0x4021, + 0x4021, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, + 0x3000, 0xf003, 0x3552, 0x3542, 0x2532, 0x2532, 0x2352, 0x2352, + 0x3452, 0x3501, 0xf002, 0x2442, 0x2522, 0x2252, 0x2512, 0xf001, + 0x1152, 0x1432, 0xf002, 0x1342, 0x1342, 0x2051, 0x2401, 0xf001, + 0x1422, 0x1242, 0xf001, 0x1332, 0x1041, + + /* huffTable10[320] */ + 0xf008, 0x0101, 0x010a, 0x010f, 0x0118, 0x011b, 0x0120, 0x0125, + 0x8712, 0x8172, 0x012a, 0x012d, 0x0132, 0x8612, 0x8162, 0x8061, + 0x0137, 0x013a, 0x013d, 0x8412, 0x8142, 0x8041, 0x8322, 0x8232, + 0x8301, 0x7312, 0x7312, 0x7132, 0x7132, 0x7031, 0x7031, 0x7222, + 0x7222, 0x6212, 0x6212, 0x6212, 0x6212, 0x6122, 0x6122, 0x6122, + 0x6122, 0x6201, 0x6201, 0x6201, 0x6201, 0x6021, 0x6021, 0x6021, + 0x6021, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, + 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, + 0x4112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0xf003, 0x3772, 0x3762, 0x3672, 0x3752, 0x3572, 0x3662, + 0x2742, 0x2742, 0xf002, 0x2472, 0x2652, 0x2562, 0x2732, 0xf003, + 0x2372, 0x2372, 0x2642, 0x2642, 0x3552, 0x3452, 0x2362, 0x2362, + 0xf001, 0x1722, 0x1272, 0xf002, 0x2462, 0x2701, 0x1071, 0x1071, + 0xf002, 0x1262, 0x1262, 0x2542, 0x2532, 0xf002, 0x1601, 0x1601, + 0x2352, 0x2442, 0xf001, 0x1632, 0x1622, 0xf002, 0x2522, 0x2252, + 0x1512, 0x1512, 0xf002, 0x1152, 0x1152, 0x2432, 0x2342, 0xf001, + 0x1501, 0x1051, 0xf001, 0x1422, 0x1242, 0xf001, 0x1332, 0x1401, + + /* huffTable11[296] */ + 0xf008, 0x0101, 0x0106, 0x010f, 0x0114, 0x0117, 0x8722, 0x8272, + 0x011c, 0x7172, 0x7172, 0x8712, 0x8071, 0x8632, 0x8362, 0x8061, + 0x011f, 0x0122, 0x8512, 0x7262, 0x7262, 0x8622, 0x8601, 0x7612, + 0x7612, 0x7162, 0x7162, 0x8152, 0x8432, 0x8051, 0x0125, 0x8422, + 0x8242, 0x8412, 0x8142, 0x8401, 0x8041, 0x7322, 0x7322, 0x7232, + 0x7232, 0x6312, 0x6312, 0x6312, 0x6312, 0x6132, 0x6132, 0x6132, + 0x6132, 0x7301, 0x7301, 0x7031, 0x7031, 0x6222, 0x6222, 0x6222, + 0x6222, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, + 0x5122, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, + 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, + 0x4212, 0x5201, 0x5201, 0x5201, 0x5201, 0x5201, 0x5201, 0x5201, + 0x5201, 0x5021, 0x5021, 0x5021, 0x5021, 0x5021, 0x5021, 0x5021, + 0x5021, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0xf002, 0x2772, 0x2762, 0x2672, 0x2572, 0xf003, 0x2662, + 0x2662, 0x2742, 0x2742, 0x2472, 0x2472, 0x3752, 0x3552, 0xf002, + 0x2652, 0x2562, 0x1732, 0x1732, 0xf001, 0x1372, 0x1642, 0xf002, + 0x2542, 0x2452, 0x2532, 0x2352, 0xf001, 0x1462, 0x1701, 0xf001, + 0x1442, 0x1522, 0xf001, 0x1252, 0x1501, 0xf001, 0x1342, 0x1332, + + /* huffTable12[185] */ + 0xf007, 0x0081, 0x008a, 0x008f, 0x0092, 0x0097, 0x009a, 0x009d, + 0x00a2, 0x00a5, 0x00a8, 0x7622, 0x7262, 0x7162, 0x00ad, 0x00b0, + 0x00b3, 0x7512, 0x7152, 0x7432, 0x7342, 0x00b6, 0x7422, 0x7242, + 0x7412, 0x6332, 0x6332, 0x6142, 0x6142, 0x6322, 0x6322, 0x6232, + 0x6232, 0x7041, 0x7301, 0x6031, 0x6031, 0x5312, 0x5312, 0x5312, + 0x5312, 0x5132, 0x5132, 0x5132, 0x5132, 0x5222, 0x5222, 0x5222, + 0x5222, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, 0x4212, + 0x4212, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, 0x4122, + 0x4122, 0x5201, 0x5201, 0x5201, 0x5201, 0x5021, 0x5021, 0x5021, + 0x5021, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, + 0x4000, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, 0x3101, + 0x3101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0xf003, 0x3772, 0x3762, 0x2672, 0x2672, 0x2752, 0x2752, + 0x2572, 0x2572, 0xf002, 0x2662, 0x2742, 0x2472, 0x2562, 0xf001, + 0x1652, 0x1732, 0xf002, 0x2372, 0x2552, 0x1722, 0x1722, 0xf001, + 0x1272, 0x1642, 0xf001, 0x1462, 0x1712, 0xf002, 0x1172, 0x1172, + 0x2701, 0x2071, 0xf001, 0x1632, 0x1362, 0xf001, 0x1542, 0x1452, + 0xf002, 0x1442, 0x1442, 0x2601, 0x2501, 0xf001, 0x1612, 0x1061, + 0xf001, 0x1532, 0x1352, 0xf001, 0x1522, 0x1252, 0xf001, 0x1051, + 0x1401, + + /* huffTable13[497] */ + 0xf006, 0x0041, 0x0082, 0x00c3, 0x00e4, 0x0105, 0x0116, 0x011f, + 0x0130, 0x0139, 0x013e, 0x0143, 0x0146, 0x6212, 0x6122, 0x6201, + 0x6021, 0x4112, 0x4112, 0x4112, 0x4112, 0x4101, 0x4101, 0x4101, + 0x4101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0xf006, 0x0108, 0x0111, 0x011a, 0x0123, 0x012c, 0x0131, + 0x0136, 0x013f, 0x0144, 0x0147, 0x014c, 0x0151, 0x0156, 0x015b, + 0x6f12, 0x61f2, 0x60f1, 0x0160, 0x0163, 0x0166, 0x62e2, 0x0169, + 0x6e12, 0x61e2, 0x016c, 0x016f, 0x0172, 0x0175, 0x0178, 0x017b, + 0x66c2, 0x6d32, 0x017e, 0x6d22, 0x62d2, 0x6d12, 0x67b2, 0x0181, + 0x0184, 0x63c2, 0x0187, 0x6b42, 0x51d2, 0x51d2, 0x6d01, 0x60d1, + 0x6a82, 0x68a2, 0x6c42, 0x64c2, 0x6b62, 0x66b2, 0x5c32, 0x5c32, + 0x5c22, 0x5c22, 0x52c2, 0x52c2, 0x5b52, 0x5b52, 0x65b2, 0x6982, + 0x5c12, 0x5c12, 0xf006, 0x51c2, 0x51c2, 0x6892, 0x6c01, 0x50c1, + 0x50c1, 0x64b2, 0x6a62, 0x66a2, 0x6972, 0x5b32, 0x5b32, 0x53b2, + 0x53b2, 0x6882, 0x6a52, 0x5b22, 0x5b22, 0x65a2, 0x6962, 0x54a2, + 0x54a2, 0x6872, 0x6782, 0x5492, 0x5492, 0x6772, 0x6672, 0x42b2, + 0x42b2, 0x42b2, 0x42b2, 0x4b12, 0x4b12, 0x4b12, 0x4b12, 0x41b2, + 0x41b2, 0x41b2, 0x41b2, 0x5b01, 0x5b01, 0x50b1, 0x50b1, 0x5692, + 0x5692, 0x5a42, 0x5a42, 0x5a32, 0x5a32, 0x53a2, 0x53a2, 0x5952, + 0x5952, 0x5592, 0x5592, 0x4a22, 0x4a22, 0x4a22, 0x4a22, 0x42a2, + 0x42a2, 0x42a2, 0x42a2, 0xf005, 0x4a12, 0x4a12, 0x41a2, 0x41a2, + 0x5a01, 0x5862, 0x40a1, 0x40a1, 0x5682, 0x5942, 0x4392, 0x4392, + 0x5932, 0x5852, 0x5582, 0x5762, 0x4922, 0x4922, 0x4292, 0x4292, + 0x5752, 0x5572, 0x4832, 0x4832, 0x4382, 0x4382, 0x5662, 0x5742, + 0x5472, 0x5652, 0x5562, 0x5372, 0xf005, 0x3912, 0x3912, 0x3912, + 0x3912, 0x3192, 0x3192, 0x3192, 0x3192, 0x4901, 0x4901, 0x4091, + 0x4091, 0x4842, 0x4842, 0x4482, 0x4482, 0x4272, 0x4272, 0x5642, + 0x5462, 0x3822, 0x3822, 0x3822, 0x3822, 0x3282, 0x3282, 0x3282, + 0x3282, 0x3812, 0x3812, 0x3812, 0x3812, 0xf004, 0x4732, 0x4722, + 0x3712, 0x3712, 0x3172, 0x3172, 0x4552, 0x4701, 0x4071, 0x4632, + 0x4362, 0x4542, 0x4452, 0x4622, 0x4262, 0x4532, 0xf003, 0x2182, + 0x2182, 0x3801, 0x3081, 0x3612, 0x3162, 0x3601, 0x3061, 0xf004, + 0x4352, 0x4442, 0x3522, 0x3522, 0x3252, 0x3252, 0x3501, 0x3501, + 0x2512, 0x2512, 0x2512, 0x2512, 0x2152, 0x2152, 0x2152, 0x2152, + 0xf003, 0x3432, 0x3342, 0x3051, 0x3422, 0x3242, 0x3332, 0x2412, + 0x2412, 0xf002, 0x1142, 0x1142, 0x2401, 0x2041, 0xf002, 0x2322, + 0x2232, 0x1312, 0x1312, 0xf001, 0x1132, 0x1301, 0xf001, 0x1031, + 0x1222, 0xf003, 0x0082, 0x008b, 0x008e, 0x0091, 0x0094, 0x0097, + 0x3ce2, 0x3dd2, 0xf003, 0x0093, 0x3eb2, 0x3be2, 0x3f92, 0x39f2, + 0x3ae2, 0x3db2, 0x3bd2, 0xf003, 0x3f82, 0x38f2, 0x3cc2, 0x008d, + 0x3e82, 0x0090, 0x27f2, 0x27f2, 0xf003, 0x2ad2, 0x2ad2, 0x3da2, + 0x3cb2, 0x3bc2, 0x36f2, 0x2f62, 0x2f62, 0xf002, 0x28e2, 0x2f52, + 0x2d92, 0x29d2, 0xf002, 0x25f2, 0x27e2, 0x2ca2, 0x2bb2, 0xf003, + 0x2f42, 0x2f42, 0x24f2, 0x24f2, 0x3ac2, 0x36e2, 0x23f2, 0x23f2, + 0xf002, 0x1f32, 0x1f32, 0x2d82, 0x28d2, 0xf001, 0x1f22, 0x12f2, + 0xf002, 0x2e62, 0x2c92, 0x1f01, 0x1f01, 0xf002, 0x29c2, 0x2e52, + 0x1ba2, 0x1ba2, 0xf002, 0x2d72, 0x27d2, 0x1e42, 0x1e42, 0xf002, + 0x28c2, 0x26d2, 0x1e32, 0x1e32, 0xf002, 0x19b2, 0x19b2, 0x2b92, + 0x2aa2, 0xf001, 0x1ab2, 0x15e2, 0xf001, 0x14e2, 0x1c82, 0xf001, + 0x1d62, 0x13e2, 0xf001, 0x1e22, 0x1e01, 0xf001, 0x10e1, 0x1d52, + 0xf001, 0x15d2, 0x1c72, 0xf001, 0x17c2, 0x1d42, 0xf001, 0x1b82, + 0x18b2, 0xf001, 0x14d2, 0x1a92, 0xf001, 0x19a2, 0x1c62, 0xf001, + 0x13d2, 0x1b72, 0xf001, 0x1c52, 0x15c2, 0xf001, 0x1992, 0x1a72, + 0xf001, 0x17a2, 0x1792, 0xf003, 0x0023, 0x3df2, 0x2de2, 0x2de2, + 0x1ff2, 0x1ff2, 0x1ff2, 0x1ff2, 0xf001, 0x1fe2, 0x1fd2, 0xf001, + 0x1ee2, 0x1fc2, 0xf001, 0x1ed2, 0x1fb2, 0xf001, 0x1bf2, 0x1ec2, + 0xf002, 0x1cd2, 0x1cd2, 0x2fa2, 0x29e2, 0xf001, 0x1af2, 0x1dc2, + 0xf001, 0x1ea2, 0x1e92, 0xf001, 0x1f72, 0x1e72, 0xf001, 0x1ef2, + 0x1cf2, + + /* huffTable15[580] */ + 0xf008, 0x0101, 0x0122, 0x0143, 0x0154, 0x0165, 0x0176, 0x017f, + 0x0188, 0x0199, 0x01a2, 0x01ab, 0x01b4, 0x01bd, 0x01c2, 0x01cb, + 0x01d4, 0x01d9, 0x01de, 0x01e3, 0x01e8, 0x01ed, 0x01f2, 0x01f7, + 0x01fc, 0x0201, 0x0204, 0x0207, 0x020a, 0x020f, 0x0212, 0x0215, + 0x021a, 0x021d, 0x0220, 0x8192, 0x0223, 0x0226, 0x0229, 0x022c, + 0x022f, 0x8822, 0x8282, 0x8812, 0x8182, 0x0232, 0x0235, 0x0238, + 0x023b, 0x8722, 0x8272, 0x8462, 0x8712, 0x8552, 0x8172, 0x023e, + 0x8632, 0x8362, 0x8542, 0x8452, 0x8622, 0x8262, 0x8612, 0x0241, + 0x8532, 0x7162, 0x7162, 0x8352, 0x8442, 0x7522, 0x7522, 0x7252, + 0x7252, 0x7512, 0x7512, 0x7152, 0x7152, 0x8501, 0x8051, 0x7432, + 0x7432, 0x7342, 0x7342, 0x7422, 0x7422, 0x7242, 0x7242, 0x7332, + 0x7332, 0x6142, 0x6142, 0x6142, 0x6142, 0x7412, 0x7412, 0x7401, + 0x7401, 0x6322, 0x6322, 0x6322, 0x6322, 0x6232, 0x6232, 0x6232, + 0x6232, 0x7041, 0x7041, 0x7301, 0x7301, 0x6312, 0x6312, 0x6312, + 0x6312, 0x6132, 0x6132, 0x6132, 0x6132, 0x6031, 0x6031, 0x6031, + 0x6031, 0x5222, 0x5222, 0x5222, 0x5222, 0x5222, 0x5222, 0x5222, + 0x5222, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, + 0x5212, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, + 0x5122, 0x5201, 0x5201, 0x5201, 0x5201, 0x5201, 0x5201, 0x5201, + 0x5201, 0x5021, 0x5021, 0x5021, 0x5021, 0x5021, 0x5021, 0x5021, + 0x5021, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, 0x3112, + 0x3112, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, + 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, + 0x4101, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, + 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, + 0x4011, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, + 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, + 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, + 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, + 0x3000, 0xf005, 0x5ff2, 0x5fe2, 0x5ef2, 0x5fd2, 0x4ee2, 0x4ee2, + 0x5df2, 0x5fc2, 0x5cf2, 0x5ed2, 0x5de2, 0x5fb2, 0x4bf2, 0x4bf2, + 0x5ec2, 0x5ce2, 0x4dd2, 0x4dd2, 0x4fa2, 0x4fa2, 0x4af2, 0x4af2, + 0x4eb2, 0x4eb2, 0x4be2, 0x4be2, 0x4dc2, 0x4dc2, 0x4cd2, 0x4cd2, + 0x4f92, 0x4f92, 0xf005, 0x49f2, 0x49f2, 0x4ae2, 0x4ae2, 0x4db2, + 0x4db2, 0x4bd2, 0x4bd2, 0x4f82, 0x4f82, 0x48f2, 0x48f2, 0x4cc2, + 0x4cc2, 0x4e92, 0x4e92, 0x49e2, 0x49e2, 0x4f72, 0x4f72, 0x47f2, + 0x47f2, 0x4da2, 0x4da2, 0x4ad2, 0x4ad2, 0x4cb2, 0x4cb2, 0x4f62, + 0x4f62, 0x5ea2, 0x5f01, 0xf004, 0x3bc2, 0x3bc2, 0x36f2, 0x36f2, + 0x4e82, 0x48e2, 0x4f52, 0x4d92, 0x35f2, 0x35f2, 0x3e72, 0x3e72, + 0x37e2, 0x37e2, 0x3ca2, 0x3ca2, 0xf004, 0x3ac2, 0x3ac2, 0x3bb2, + 0x3bb2, 0x49d2, 0x4d82, 0x3f42, 0x3f42, 0x34f2, 0x34f2, 0x3f32, + 0x3f32, 0x33f2, 0x33f2, 0x38d2, 0x38d2, 0xf004, 0x36e2, 0x36e2, + 0x3f22, 0x3f22, 0x32f2, 0x32f2, 0x4e62, 0x40f1, 0x3f12, 0x3f12, + 0x31f2, 0x31f2, 0x3c92, 0x3c92, 0x39c2, 0x39c2, 0xf003, 0x3e52, + 0x3ba2, 0x3ab2, 0x35e2, 0x3d72, 0x37d2, 0x3e42, 0x34e2, 0xf003, + 0x3c82, 0x38c2, 0x3e32, 0x3d62, 0x36d2, 0x33e2, 0x3b92, 0x39b2, + 0xf004, 0x3e22, 0x3e22, 0x3aa2, 0x3aa2, 0x32e2, 0x32e2, 0x3e12, + 0x3e12, 0x31e2, 0x31e2, 0x4e01, 0x40e1, 0x3d52, 0x3d52, 0x35d2, + 0x35d2, 0xf003, 0x3c72, 0x37c2, 0x3d42, 0x3b82, 0x24d2, 0x24d2, + 0x38b2, 0x3a92, 0xf003, 0x39a2, 0x3c62, 0x36c2, 0x3d32, 0x23d2, + 0x23d2, 0x22d2, 0x22d2, 0xf003, 0x3d22, 0x3d01, 0x2d12, 0x2d12, + 0x2b72, 0x2b72, 0x27b2, 0x27b2, 0xf003, 0x21d2, 0x21d2, 0x3c52, + 0x30d1, 0x25c2, 0x25c2, 0x2a82, 0x2a82, 0xf002, 0x28a2, 0x2c42, + 0x24c2, 0x2b62, 0xf003, 0x26b2, 0x26b2, 0x3992, 0x3c01, 0x2c32, + 0x2c32, 0x23c2, 0x23c2, 0xf003, 0x2a72, 0x2a72, 0x27a2, 0x27a2, + 0x26a2, 0x26a2, 0x30c1, 0x3b01, 0xf002, 0x12c2, 0x12c2, 0x2c22, + 0x2b52, 0xf002, 0x25b2, 0x2c12, 0x2982, 0x2892, 0xf002, 0x21c2, + 0x2b42, 0x24b2, 0x2a62, 0xf002, 0x2b32, 0x2972, 0x13b2, 0x13b2, + 0xf002, 0x2792, 0x2882, 0x2b22, 0x2a52, 0xf002, 0x12b2, 0x12b2, + 0x25a2, 0x2b12, 0xf002, 0x11b2, 0x11b2, 0x20b1, 0x2962, 0xf002, + 0x2692, 0x2a42, 0x24a2, 0x2872, 0xf002, 0x2782, 0x2a32, 0x13a2, + 0x13a2, 0xf001, 0x1952, 0x1592, 0xf001, 0x1a22, 0x12a2, 0xf001, + 0x1a12, 0x11a2, 0xf002, 0x2a01, 0x20a1, 0x1862, 0x1862, 0xf001, + 0x1682, 0x1942, 0xf001, 0x1492, 0x1932, 0xf002, 0x1392, 0x1392, + 0x2772, 0x2901, 0xf001, 0x1852, 0x1582, 0xf001, 0x1922, 0x1762, + 0xf001, 0x1672, 0x1292, 0xf001, 0x1912, 0x1091, 0xf001, 0x1842, + 0x1482, 0xf001, 0x1752, 0x1572, 0xf001, 0x1832, 0x1382, 0xf001, + 0x1662, 0x1742, 0xf001, 0x1472, 0x1801, 0xf001, 0x1081, 0x1652, + 0xf001, 0x1562, 0x1732, 0xf001, 0x1372, 0x1642, 0xf001, 0x1701, + 0x1071, 0xf001, 0x1601, 0x1061, + + /* huffTable16[651] */ + 0xf008, 0x0101, 0x010a, 0x0113, 0x8ff2, 0x0118, 0x011d, 0x0120, + 0x82f2, 0x0131, 0x8f12, 0x81f2, 0x0134, 0x0145, 0x0156, 0x0167, + 0x0178, 0x0189, 0x019a, 0x01a3, 0x01ac, 0x01b5, 0x01be, 0x01c7, + 0x01d0, 0x01d9, 0x01de, 0x01e3, 0x01e6, 0x01eb, 0x01f0, 0x8152, + 0x01f3, 0x01f6, 0x01f9, 0x01fc, 0x8412, 0x8142, 0x01ff, 0x8322, + 0x8232, 0x7312, 0x7312, 0x7132, 0x7132, 0x8301, 0x8031, 0x7222, + 0x7222, 0x6212, 0x6212, 0x6212, 0x6212, 0x6122, 0x6122, 0x6122, + 0x6122, 0x6201, 0x6201, 0x6201, 0x6201, 0x6021, 0x6021, 0x6021, + 0x6021, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, + 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, + 0x4112, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, + 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, + 0x4101, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, 0x3011, + 0x3011, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0xf003, 0x3fe2, 0x3ef2, 0x3fd2, 0x3df2, 0x3fc2, 0x3cf2, + 0x3fb2, 0x3bf2, 0xf003, 0x2fa2, 0x2fa2, 0x3af2, 0x3f92, 0x39f2, + 0x38f2, 0x2f82, 0x2f82, 0xf002, 0x2f72, 0x27f2, 0x2f62, 0x26f2, + 0xf002, 0x2f52, 0x25f2, 0x1f42, 0x1f42, 0xf001, 0x14f2, 0x13f2, + 0xf004, 0x10f1, 0x10f1, 0x10f1, 0x10f1, 0x10f1, 0x10f1, 0x10f1, + 0x10f1, 0x2f32, 0x2f32, 0x2f32, 0x2f32, 0x00e2, 0x00f3, 0x00fc, + 0x0105, 0xf001, 0x1f22, 0x1f01, 0xf004, 0x00fa, 0x00ff, 0x0104, + 0x0109, 0x010c, 0x0111, 0x0116, 0x0119, 0x011e, 0x0123, 0x0128, + 0x43e2, 0x012d, 0x0130, 0x0133, 0x0136, 0xf004, 0x0128, 0x012b, + 0x012e, 0x4d01, 0x0131, 0x0134, 0x0137, 0x4c32, 0x013a, 0x4c12, + 0x40c1, 0x013d, 0x32e2, 0x32e2, 0x4e22, 0x4e12, 0xf004, 0x43d2, + 0x4d22, 0x42d2, 0x41d2, 0x4b32, 0x012f, 0x3d12, 0x3d12, 0x44c2, + 0x4b62, 0x43c2, 0x47a2, 0x3c22, 0x3c22, 0x42c2, 0x45b2, 0xf004, + 0x41c2, 0x4c01, 0x4b42, 0x44b2, 0x4a62, 0x46a2, 0x33b2, 0x33b2, + 0x4a52, 0x45a2, 0x3b22, 0x3b22, 0x32b2, 0x32b2, 0x3b12, 0x3b12, + 0xf004, 0x31b2, 0x31b2, 0x4b01, 0x40b1, 0x4962, 0x4692, 0x4a42, + 0x44a2, 0x4872, 0x4782, 0x33a2, 0x33a2, 0x4a32, 0x4952, 0x3a22, + 0x3a22, 0xf004, 0x4592, 0x4862, 0x31a2, 0x31a2, 0x4682, 0x4772, + 0x3492, 0x3492, 0x4942, 0x4752, 0x3762, 0x3762, 0x22a2, 0x22a2, + 0x22a2, 0x22a2, 0xf003, 0x2a12, 0x2a12, 0x3a01, 0x30a1, 0x3932, + 0x3392, 0x3852, 0x3582, 0xf003, 0x2922, 0x2922, 0x2292, 0x2292, + 0x3672, 0x3901, 0x2912, 0x2912, 0xf003, 0x2192, 0x2192, 0x3091, + 0x3842, 0x3482, 0x3572, 0x3832, 0x3382, 0xf003, 0x3662, 0x3822, + 0x2282, 0x2282, 0x3742, 0x3472, 0x2812, 0x2812, 0xf003, 0x2182, + 0x2182, 0x2081, 0x2081, 0x3801, 0x3652, 0x2732, 0x2732, 0xf003, + 0x2372, 0x2372, 0x3562, 0x3642, 0x2722, 0x2722, 0x2272, 0x2272, + 0xf003, 0x3462, 0x3552, 0x2701, 0x2701, 0x1712, 0x1712, 0x1712, + 0x1712, 0xf002, 0x1172, 0x1172, 0x2071, 0x2632, 0xf002, 0x2362, + 0x2542, 0x2452, 0x2622, 0xf001, 0x1262, 0x1612, 0xf002, 0x1162, + 0x1162, 0x2601, 0x2061, 0xf002, 0x1352, 0x1352, 0x2532, 0x2442, + 0xf001, 0x1522, 0x1252, 0xf001, 0x1512, 0x1501, 0xf001, 0x1432, + 0x1342, 0xf001, 0x1051, 0x1422, 0xf001, 0x1242, 0x1332, 0xf001, + 0x1401, 0x1041, 0xf004, 0x4ec2, 0x0086, 0x3ed2, 0x3ed2, 0x39e2, + 0x39e2, 0x4ae2, 0x49d2, 0x2ee2, 0x2ee2, 0x2ee2, 0x2ee2, 0x3de2, + 0x3de2, 0x3be2, 0x3be2, 0xf003, 0x2eb2, 0x2eb2, 0x2dc2, 0x2dc2, + 0x3cd2, 0x3bd2, 0x2ea2, 0x2ea2, 0xf003, 0x2cc2, 0x2cc2, 0x3da2, + 0x3ad2, 0x3e72, 0x3ca2, 0x2ac2, 0x2ac2, 0xf003, 0x39c2, 0x3d72, + 0x2e52, 0x2e52, 0x1db2, 0x1db2, 0x1db2, 0x1db2, 0xf002, 0x1e92, + 0x1e92, 0x2cb2, 0x2bc2, 0xf002, 0x2e82, 0x28e2, 0x2d92, 0x27e2, + 0xf002, 0x2bb2, 0x2d82, 0x28d2, 0x2e62, 0xf001, 0x16e2, 0x1c92, + 0xf002, 0x2ba2, 0x2ab2, 0x25e2, 0x27d2, 0xf002, 0x1e42, 0x1e42, + 0x24e2, 0x2c82, 0xf001, 0x18c2, 0x1e32, 0xf002, 0x1d62, 0x1d62, + 0x26d2, 0x2b92, 0xf002, 0x29b2, 0x2aa2, 0x11e2, 0x11e2, 0xf002, + 0x14d2, 0x14d2, 0x28b2, 0x29a2, 0xf002, 0x1b72, 0x1b72, 0x27b2, + 0x20d1, 0xf001, 0x1e01, 0x10e1, 0xf001, 0x1d52, 0x15d2, 0xf001, + 0x1c72, 0x17c2, 0xf001, 0x1d42, 0x1b82, 0xf001, 0x1a92, 0x1c62, + 0xf001, 0x16c2, 0x1d32, 0xf001, 0x1c52, 0x15c2, 0xf001, 0x1a82, + 0x18a2, 0xf001, 0x1992, 0x1c42, 0xf001, 0x16b2, 0x1a72, 0xf001, + 0x1b52, 0x1982, 0xf001, 0x1892, 0x1972, 0xf001, 0x1792, 0x1882, + 0xf001, 0x1ce2, 0x1dd2, + + /* huffTable24[705] */ + 0xf009, 0x8fe2, 0x8fe2, 0x8ef2, 0x8ef2, 0x8fd2, 0x8fd2, 0x8df2, + 0x8df2, 0x8fc2, 0x8fc2, 0x8cf2, 0x8cf2, 0x8fb2, 0x8fb2, 0x8bf2, + 0x8bf2, 0x7af2, 0x7af2, 0x7af2, 0x7af2, 0x8fa2, 0x8fa2, 0x8f92, + 0x8f92, 0x79f2, 0x79f2, 0x79f2, 0x79f2, 0x78f2, 0x78f2, 0x78f2, + 0x78f2, 0x8f82, 0x8f82, 0x8f72, 0x8f72, 0x77f2, 0x77f2, 0x77f2, + 0x77f2, 0x7f62, 0x7f62, 0x7f62, 0x7f62, 0x76f2, 0x76f2, 0x76f2, + 0x76f2, 0x7f52, 0x7f52, 0x7f52, 0x7f52, 0x75f2, 0x75f2, 0x75f2, + 0x75f2, 0x7f42, 0x7f42, 0x7f42, 0x7f42, 0x74f2, 0x74f2, 0x74f2, + 0x74f2, 0x7f32, 0x7f32, 0x7f32, 0x7f32, 0x73f2, 0x73f2, 0x73f2, + 0x73f2, 0x7f22, 0x7f22, 0x7f22, 0x7f22, 0x72f2, 0x72f2, 0x72f2, + 0x72f2, 0x71f2, 0x71f2, 0x71f2, 0x71f2, 0x8f12, 0x8f12, 0x80f1, + 0x80f1, 0x9f01, 0x0201, 0x0206, 0x020b, 0x0210, 0x0215, 0x021a, + 0x021f, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, + 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, + 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, + 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, 0x4ff2, + 0x4ff2, 0x0224, 0x0229, 0x0232, 0x0237, 0x023a, 0x023f, 0x0242, + 0x0245, 0x024a, 0x024d, 0x0250, 0x0253, 0x0256, 0x0259, 0x025c, + 0x025f, 0x0262, 0x0265, 0x0268, 0x026b, 0x026e, 0x0271, 0x0274, + 0x0277, 0x027a, 0x027d, 0x0280, 0x0283, 0x0288, 0x028b, 0x028e, + 0x0291, 0x0294, 0x0297, 0x029a, 0x029f, 0x94b2, 0x02a4, 0x02a7, + 0x02aa, 0x93b2, 0x9882, 0x02af, 0x92b2, 0x02b2, 0x02b5, 0x9692, + 0x94a2, 0x02b8, 0x9782, 0x9a32, 0x93a2, 0x9952, 0x9592, 0x9a22, + 0x92a2, 0x91a2, 0x9862, 0x9682, 0x9772, 0x9942, 0x9492, 0x9932, + 0x9392, 0x9852, 0x9582, 0x9922, 0x9762, 0x9672, 0x9292, 0x9912, + 0x9192, 0x9842, 0x9482, 0x9752, 0x9572, 0x9832, 0x9382, 0x9662, + 0x9822, 0x9282, 0x9812, 0x9742, 0x9472, 0x9182, 0x02bb, 0x9652, + 0x9562, 0x9712, 0x02be, 0x8372, 0x8372, 0x9732, 0x9722, 0x8272, + 0x8272, 0x8642, 0x8642, 0x8462, 0x8462, 0x8552, 0x8552, 0x8172, + 0x8172, 0x8632, 0x8632, 0x8362, 0x8362, 0x8542, 0x8542, 0x8452, + 0x8452, 0x8622, 0x8622, 0x8262, 0x8262, 0x8612, 0x8612, 0x8162, + 0x8162, 0x9601, 0x9061, 0x8532, 0x8532, 0x8352, 0x8352, 0x8442, + 0x8442, 0x8522, 0x8522, 0x8252, 0x8252, 0x8512, 0x8512, 0x9501, + 0x9051, 0x7152, 0x7152, 0x7152, 0x7152, 0x8432, 0x8432, 0x8342, + 0x8342, 0x7422, 0x7422, 0x7422, 0x7422, 0x7242, 0x7242, 0x7242, + 0x7242, 0x7332, 0x7332, 0x7332, 0x7332, 0x7412, 0x7412, 0x7412, + 0x7412, 0x7142, 0x7142, 0x7142, 0x7142, 0x8401, 0x8401, 0x8041, + 0x8041, 0x7322, 0x7322, 0x7322, 0x7322, 0x7232, 0x7232, 0x7232, + 0x7232, 0x6312, 0x6312, 0x6312, 0x6312, 0x6312, 0x6312, 0x6312, + 0x6312, 0x6132, 0x6132, 0x6132, 0x6132, 0x6132, 0x6132, 0x6132, + 0x6132, 0x7301, 0x7301, 0x7301, 0x7301, 0x7031, 0x7031, 0x7031, + 0x7031, 0x6222, 0x6222, 0x6222, 0x6222, 0x6222, 0x6222, 0x6222, + 0x6222, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, + 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, 0x5212, + 0x5212, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, + 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, + 0x5122, 0x6201, 0x6201, 0x6201, 0x6201, 0x6201, 0x6201, 0x6201, + 0x6201, 0x6021, 0x6021, 0x6021, 0x6021, 0x6021, 0x6021, 0x6021, + 0x6021, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, + 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, + 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, + 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, 0x4112, + 0x4112, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, + 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, + 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, + 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, 0x4101, + 0x4101, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, + 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, + 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, + 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, 0x4011, + 0x4011, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, + 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, + 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, + 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, + 0x4000, 0xf002, 0x2ee2, 0x2ed2, 0x2de2, 0x2ec2, 0xf002, 0x2ce2, + 0x2dd2, 0x2eb2, 0x2be2, 0xf002, 0x2dc2, 0x2cd2, 0x2ea2, 0x2ae2, + 0xf002, 0x2db2, 0x2bd2, 0x2cc2, 0x2e92, 0xf002, 0x29e2, 0x2da2, + 0x2ad2, 0x2cb2, 0xf002, 0x2bc2, 0x2e82, 0x28e2, 0x2d92, 0xf002, + 0x29d2, 0x2e72, 0x27e2, 0x2ca2, 0xf002, 0x2ac2, 0x2bb2, 0x2d82, + 0x28d2, 0xf003, 0x3e01, 0x30e1, 0x2d01, 0x2d01, 0x16e2, 0x16e2, + 0x16e2, 0x16e2, 0xf002, 0x2e62, 0x2c92, 0x19c2, 0x19c2, 0xf001, + 0x1e52, 0x1ab2, 0xf002, 0x15e2, 0x15e2, 0x2ba2, 0x2d72, 0xf001, + 0x17d2, 0x14e2, 0xf001, 0x1c82, 0x18c2, 0xf002, 0x2e42, 0x2e22, + 0x1e32, 0x1e32, 0xf001, 0x1d62, 0x16d2, 0xf001, 0x13e2, 0x1b92, + 0xf001, 0x19b2, 0x1aa2, 0xf001, 0x12e2, 0x1e12, 0xf001, 0x11e2, + 0x1d52, 0xf001, 0x15d2, 0x1c72, 0xf001, 0x17c2, 0x1d42, 0xf001, + 0x1b82, 0x18b2, 0xf001, 0x14d2, 0x1a92, 0xf001, 0x19a2, 0x1c62, + 0xf001, 0x16c2, 0x1d32, 0xf001, 0x13d2, 0x1d22, 0xf001, 0x12d2, + 0x1d12, 0xf001, 0x1b72, 0x17b2, 0xf001, 0x11d2, 0x1c52, 0xf001, + 0x15c2, 0x1a82, 0xf001, 0x18a2, 0x1992, 0xf001, 0x1c42, 0x14c2, + 0xf001, 0x1b62, 0x16b2, 0xf002, 0x20d1, 0x2c01, 0x1c32, 0x1c32, + 0xf001, 0x13c2, 0x1a72, 0xf001, 0x17a2, 0x1c22, 0xf001, 0x12c2, + 0x1b52, 0xf001, 0x15b2, 0x1c12, 0xf001, 0x1982, 0x1892, 0xf001, + 0x11c2, 0x1b42, 0xf002, 0x20c1, 0x2b01, 0x1b32, 0x1b32, 0xf002, + 0x20b1, 0x2a01, 0x1a12, 0x1a12, 0xf001, 0x1a62, 0x16a2, 0xf001, + 0x1972, 0x1792, 0xf002, 0x20a1, 0x2901, 0x1091, 0x1091, 0xf001, + 0x1b22, 0x1a52, 0xf001, 0x15a2, 0x1b12, 0xf001, 0x11b2, 0x1962, + 0xf001, 0x1a42, 0x1872, 0xf001, 0x1801, 0x1081, 0xf001, 0x1701, + 0x1071, +}; + +#define HUFF_OFFSET_01 0 +#define HUFF_OFFSET_02 ( 9 + HUFF_OFFSET_01) +#define HUFF_OFFSET_03 ( 65 + HUFF_OFFSET_02) +#define HUFF_OFFSET_05 ( 65 + HUFF_OFFSET_03) +#define HUFF_OFFSET_06 (257 + HUFF_OFFSET_05) +#define HUFF_OFFSET_07 (129 + HUFF_OFFSET_06) +#define HUFF_OFFSET_08 (110 + HUFF_OFFSET_07) +#define HUFF_OFFSET_09 (280 + HUFF_OFFSET_08) +#define HUFF_OFFSET_10 ( 93 + HUFF_OFFSET_09) +#define HUFF_OFFSET_11 (320 + HUFF_OFFSET_10) +#define HUFF_OFFSET_12 (296 + HUFF_OFFSET_11) +#define HUFF_OFFSET_13 (185 + HUFF_OFFSET_12) +#define HUFF_OFFSET_15 (497 + HUFF_OFFSET_13) +#define HUFF_OFFSET_16 (580 + HUFF_OFFSET_15) +#define HUFF_OFFSET_24 (651 + HUFF_OFFSET_16) + +const int huffTabOffset[HUFF_PAIRTABS] PROGMEM = { + 0, + HUFF_OFFSET_01, + HUFF_OFFSET_02, + HUFF_OFFSET_03, + 0, + HUFF_OFFSET_05, + HUFF_OFFSET_06, + HUFF_OFFSET_07, + HUFF_OFFSET_08, + HUFF_OFFSET_09, + HUFF_OFFSET_10, + HUFF_OFFSET_11, + HUFF_OFFSET_12, + HUFF_OFFSET_13, + 0, + HUFF_OFFSET_15, + HUFF_OFFSET_16, + HUFF_OFFSET_16, + HUFF_OFFSET_16, + HUFF_OFFSET_16, + HUFF_OFFSET_16, + HUFF_OFFSET_16, + HUFF_OFFSET_16, + HUFF_OFFSET_16, + HUFF_OFFSET_24, + HUFF_OFFSET_24, + HUFF_OFFSET_24, + HUFF_OFFSET_24, + HUFF_OFFSET_24, + HUFF_OFFSET_24, + HUFF_OFFSET_24, + HUFF_OFFSET_24, +}; + +const HuffTabLookup huffTabLookup[HUFF_PAIRTABS] PROGMEM = { + { 0, noBits }, + { 0, oneShot }, + { 0, oneShot }, + { 0, oneShot }, + { 0, invalidTab }, + { 0, oneShot }, + { 0, oneShot }, + { 0, loopNoLinbits }, + { 0, loopNoLinbits }, + { 0, loopNoLinbits }, + { 0, loopNoLinbits }, + { 0, loopNoLinbits }, + { 0, loopNoLinbits }, + { 0, loopNoLinbits }, + { 0, invalidTab }, + { 0, loopNoLinbits }, + { 1, loopLinbits }, + { 2, loopLinbits }, + { 3, loopLinbits }, + { 4, loopLinbits }, + { 6, loopLinbits }, + { 8, loopLinbits }, + { 10, loopLinbits }, + { 13, loopLinbits }, + { 4, loopLinbits }, + { 5, loopLinbits }, + { 6, loopLinbits }, + { 7, loopLinbits }, + { 8, loopLinbits }, + { 9, loopLinbits }, + { 11, loopLinbits }, + { 13, loopLinbits }, +}; + +/* tables for quadruples + * format 0xAB + * A = length of codeword + * B = codeword + */ +const unsigned char quadTable[64+16] PROGMEM = { + /* table A */ + 0x6b, 0x6f, 0x6d, 0x6e, 0x67, 0x65, 0x59, 0x59, + 0x56, 0x56, 0x53, 0x53, 0x5a, 0x5a, 0x5c, 0x5c, + 0x42, 0x42, 0x42, 0x42, 0x41, 0x41, 0x41, 0x41, + 0x44, 0x44, 0x44, 0x44, 0x48, 0x48, 0x48, 0x48, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + /* table B */ + 0x4f, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x48, + 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, +}; + +const int quadTabOffset[2] PROGMEM = {0, 64}; +const int quadTabMaxBits[2] PROGMEM = {6, 4}; diff --git a/components/spotify/cspot/bell/libhelix-mp3/imdct.c b/components/spotify/cspot/bell/libhelix-mp3/imdct.c new file mode 100644 index 00000000..e24c50ae --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/imdct.c @@ -0,0 +1,787 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * imdct.c - antialias, inverse transform (short/long/mixed), windowing, + * overlap-add, frequency inversion + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" +#include + +/************************************************************************************** + * Function: AntiAlias + * + * Description: smooth transition across DCT block boundaries (every 18 coefficients) + * + * Inputs: vector of dequantized coefficients, length = (nBfly+1) * 18 + * number of "butterflies" to perform (one butterfly means one + * inter-block smoothing operation) + * + * Outputs: updated coefficient vector x + * + * Return: none + * + * Notes: weighted average of opposite bands (pairwise) from the 8 samples + * before and after each block boundary + * nBlocks = (nonZeroBound + 7) / 18, since nZB is the first ZERO sample + * above which all other samples are also zero + * max gain per sample = 1.372 + * MAX(i) (abs(csa[i][0]) + abs(csa[i][1])) + * bits gained = 0 + * assume at least 1 guard bit in x[] to avoid overflow + * (should be guaranteed from dequant, and max gain from stproc * max + * gain from AntiAlias < 2.0) + **************************************************************************************/ +// a little bit faster in RAM (< 1 ms per block) +/* __attribute__ ((section (".data"))) */ static void AntiAlias(int *x, int nBfly) +{ + int k, a0, b0, c0, c1; + const int *c; + + /* csa = Q31 */ + for (k = nBfly; k > 0; k--) { + c = csa[0]; + x += 18; + + a0 = x[-1]; c0 = *c; c++; b0 = x[0]; c1 = *c; c++; + x[-1] = (MULSHIFT32(c0, a0) - MULSHIFT32(c1, b0)) << 1; + x[0] = (MULSHIFT32(c0, b0) + MULSHIFT32(c1, a0)) << 1; + + a0 = x[-2]; c0 = *c; c++; b0 = x[1]; c1 = *c; c++; + x[-2] = (MULSHIFT32(c0, a0) - MULSHIFT32(c1, b0)) << 1; + x[1] = (MULSHIFT32(c0, b0) + MULSHIFT32(c1, a0)) << 1; + + a0 = x[-3]; c0 = *c; c++; b0 = x[2]; c1 = *c; c++; + x[-3] = (MULSHIFT32(c0, a0) - MULSHIFT32(c1, b0)) << 1; + x[2] = (MULSHIFT32(c0, b0) + MULSHIFT32(c1, a0)) << 1; + + a0 = x[-4]; c0 = *c; c++; b0 = x[3]; c1 = *c; c++; + x[-4] = (MULSHIFT32(c0, a0) - MULSHIFT32(c1, b0)) << 1; + x[3] = (MULSHIFT32(c0, b0) + MULSHIFT32(c1, a0)) << 1; + + a0 = x[-5]; c0 = *c; c++; b0 = x[4]; c1 = *c; c++; + x[-5] = (MULSHIFT32(c0, a0) - MULSHIFT32(c1, b0)) << 1; + x[4] = (MULSHIFT32(c0, b0) + MULSHIFT32(c1, a0)) << 1; + + a0 = x[-6]; c0 = *c; c++; b0 = x[5]; c1 = *c; c++; + x[-6] = (MULSHIFT32(c0, a0) - MULSHIFT32(c1, b0)) << 1; + x[5] = (MULSHIFT32(c0, b0) + MULSHIFT32(c1, a0)) << 1; + + a0 = x[-7]; c0 = *c; c++; b0 = x[6]; c1 = *c; c++; + x[-7] = (MULSHIFT32(c0, a0) - MULSHIFT32(c1, b0)) << 1; + x[6] = (MULSHIFT32(c0, b0) + MULSHIFT32(c1, a0)) << 1; + + a0 = x[-8]; c0 = *c; c++; b0 = x[7]; c1 = *c; c++; + x[-8] = (MULSHIFT32(c0, a0) - MULSHIFT32(c1, b0)) << 1; + x[7] = (MULSHIFT32(c0, b0) + MULSHIFT32(c1, a0)) << 1; + } +} + +/************************************************************************************** + * Function: WinPrevious + * + * Description: apply specified window to second half of previous IMDCT (overlap part) + * + * Inputs: vector of 9 coefficients (xPrev) + * + * Outputs: 18 windowed output coefficients (gain 1 integer bit) + * window type (0, 1, 2, 3) + * + * Return: none + * + * Notes: produces 9 output samples from 18 input samples via symmetry + * all blocks gain at least 1 guard bit via window (long blocks get extra + * sign bit, short blocks can have one addition but max gain < 1.0) + **************************************************************************************/ +/*__attribute__ ((section (".data"))) */ static void WinPrevious(int *xPrev, int *xPrevWin, int btPrev) +{ + int i, x, *xp, *xpwLo, *xpwHi, wLo, wHi; + const int *wpLo, *wpHi; + + xp = xPrev; + /* mapping (see IMDCT12x3): xPrev[0-2] = sum[6-8], xPrev[3-8] = sum[12-17] */ + if (btPrev == 2) { + /* this could be reordered for minimum loads/stores */ + wpLo = imdctWin[btPrev]; + xPrevWin[ 0] = MULSHIFT32(wpLo[ 6], xPrev[2]) + MULSHIFT32(wpLo[0], xPrev[6]); + xPrevWin[ 1] = MULSHIFT32(wpLo[ 7], xPrev[1]) + MULSHIFT32(wpLo[1], xPrev[7]); + xPrevWin[ 2] = MULSHIFT32(wpLo[ 8], xPrev[0]) + MULSHIFT32(wpLo[2], xPrev[8]); + xPrevWin[ 3] = MULSHIFT32(wpLo[ 9], xPrev[0]) + MULSHIFT32(wpLo[3], xPrev[8]); + xPrevWin[ 4] = MULSHIFT32(wpLo[10], xPrev[1]) + MULSHIFT32(wpLo[4], xPrev[7]); + xPrevWin[ 5] = MULSHIFT32(wpLo[11], xPrev[2]) + MULSHIFT32(wpLo[5], xPrev[6]); + xPrevWin[ 6] = MULSHIFT32(wpLo[ 6], xPrev[5]); + xPrevWin[ 7] = MULSHIFT32(wpLo[ 7], xPrev[4]); + xPrevWin[ 8] = MULSHIFT32(wpLo[ 8], xPrev[3]); + xPrevWin[ 9] = MULSHIFT32(wpLo[ 9], xPrev[3]); + xPrevWin[10] = MULSHIFT32(wpLo[10], xPrev[4]); + xPrevWin[11] = MULSHIFT32(wpLo[11], xPrev[5]); + xPrevWin[12] = xPrevWin[13] = xPrevWin[14] = xPrevWin[15] = xPrevWin[16] = xPrevWin[17] = 0; + } else { + /* use ARM-style pointers (*ptr++) so that ADS compiles well */ + wpLo = imdctWin[btPrev] + 18; + wpHi = wpLo + 17; + xpwLo = xPrevWin; + xpwHi = xPrevWin + 17; + for (i = 9; i > 0; i--) { + x = *xp++; wLo = *wpLo++; wHi = *wpHi--; + *xpwLo++ = MULSHIFT32(wLo, x); + *xpwHi-- = MULSHIFT32(wHi, x); + } + } +} + +/************************************************************************************** + * Function: FreqInvertRescale + * + * Description: do frequency inversion (odd samples of odd blocks) and rescale + * if necessary (extra guard bits added before IMDCT) + * + * Inputs: output vector y (18 new samples, spaced NBANDS apart) + * previous sample vector xPrev (9 samples) + * index of current block + * number of extra shifts added before IMDCT (usually 0) + * + * Outputs: inverted and rescaled (as necessary) outputs + * rescaled (as necessary) previous samples + * + * Return: updated mOut (from new outputs y) + **************************************************************************************/ +/*__attribute__ ((section (".data")))*/ static int FreqInvertRescale(int *y, int *xPrev, int blockIdx, int es) +{ + int i, d, mOut; + int y0, y1, y2, y3, y4, y5, y6, y7, y8; + + if (es == 0) { + /* fast case - frequency invert only (no rescaling) - can fuse into overlap-add for speed, if desired */ + if (blockIdx & 0x01) { + y += NBANDS; + y0 = *y; y += 2*NBANDS; + y1 = *y; y += 2*NBANDS; + y2 = *y; y += 2*NBANDS; + y3 = *y; y += 2*NBANDS; + y4 = *y; y += 2*NBANDS; + y5 = *y; y += 2*NBANDS; + y6 = *y; y += 2*NBANDS; + y7 = *y; y += 2*NBANDS; + y8 = *y; y += 2*NBANDS; + + y -= 18*NBANDS; + *y = -y0; y += 2*NBANDS; + *y = -y1; y += 2*NBANDS; + *y = -y2; y += 2*NBANDS; + *y = -y3; y += 2*NBANDS; + *y = -y4; y += 2*NBANDS; + *y = -y5; y += 2*NBANDS; + *y = -y6; y += 2*NBANDS; + *y = -y7; y += 2*NBANDS; + *y = -y8; y += 2*NBANDS; + } + return 0; + } else { + /* undo pre-IMDCT scaling, clipping if necessary */ + mOut = 0; + if (blockIdx & 0x01) { + /* frequency invert */ + for (i = 0; i < 18; i+=2) { + d = *y; CLIP_2N(d, 31 - es); *y = d << es; mOut |= FASTABS(*y); y += NBANDS; + d = -*y; CLIP_2N(d, 31 - es); *y = d << es; mOut |= FASTABS(*y); y += NBANDS; + d = *xPrev; CLIP_2N(d, 31 - es); *xPrev++ = d << es; + } + } else { + for (i = 0; i < 18; i+=2) { + d = *y; CLIP_2N(d, 31 - es); *y = d << es; mOut |= FASTABS(*y); y += NBANDS; + d = *y; CLIP_2N(d, 31 - es); *y = d << es; mOut |= FASTABS(*y); y += NBANDS; + d = *xPrev; CLIP_2N(d, 31 - es); *xPrev++ = d << es; + } + } + return mOut; + } +} + +/* format = Q31 + * #define M_PI 3.14159265358979323846 + * double u = 2.0 * M_PI / 9.0; + * float c0 = sqrt(3.0) / 2.0; + * float c1 = cos(u); + * float c2 = cos(2*u); + * float c3 = sin(u); + * float c4 = sin(2*u); + */ + +static const int c9_0 = 0x6ed9eba1; +static const int c9_1 = 0x620dbe8b; +static const int c9_2 = 0x163a1a7e; +static const int c9_3 = 0x5246dd49; +static const int c9_4 = 0x7e0e2e32; + +/* format = Q31 + * cos(((0:8) + 0.5) * (pi/18)) + */ +static const int c18[9] PROGMEM = { + 0x7f834ed0, 0x7ba3751d, 0x7401e4c1, 0x68d9f964, 0x5a82799a, 0x496af3e2, 0x36185aee, 0x2120fb83, 0x0b27eb5c, +}; + +/* require at least 3 guard bits in x[] to ensure no overflow */ +static __inline void idct9(int *x) +{ + int a1, a2, a3, a4, a5, a6, a7, a8, a9; + int a10, a11, a12, a13, a14, a15, a16, a17, a18; + int a19, a20, a21, a22, a23, a24, a25, a26, a27; + int m1, m3, m5, m6, m7, m8, m9, m10, m11, m12; + int x0, x1, x2, x3, x4, x5, x6, x7, x8; + + x0 = x[0]; x1 = x[1]; x2 = x[2]; x3 = x[3]; x4 = x[4]; + x5 = x[5]; x6 = x[6]; x7 = x[7]; x8 = x[8]; + + a1 = x0 - x6; + a2 = x1 - x5; + a3 = x1 + x5; + a4 = x2 - x4; + a5 = x2 + x4; + a6 = x2 + x8; + a7 = x1 + x7; + + a8 = a6 - a5; /* ie x[8] - x[4] */ + a9 = a3 - a7; /* ie x[5] - x[7] */ + a10 = a2 - x7; /* ie x[1] - x[5] - x[7] */ + a11 = a4 - x8; /* ie x[2] - x[4] - x[8] */ + + /* do the << 1 as constant shifts where mX is actually used (free, no stall or extra inst.) */ + m1 = MULSHIFT32(c9_0, x3); + m3 = MULSHIFT32(c9_0, a10); + m5 = MULSHIFT32(c9_1, a5); + m6 = MULSHIFT32(c9_2, a6); + m7 = MULSHIFT32(c9_1, a8); + m8 = MULSHIFT32(c9_2, a5); + m9 = MULSHIFT32(c9_3, a9); + m10 = MULSHIFT32(c9_4, a7); + m11 = MULSHIFT32(c9_3, a3); + m12 = MULSHIFT32(c9_4, a9); + + a12 = x[0] + (x[6] >> 1); + a13 = a12 + ( m1 << 1); + a14 = a12 - ( m1 << 1); + a15 = a1 + ( a11 >> 1); + a16 = ( m5 << 1) + (m6 << 1); + a17 = ( m7 << 1) - (m8 << 1); + a18 = a16 + a17; + a19 = ( m9 << 1) + (m10 << 1); + a20 = (m11 << 1) - (m12 << 1); + + a21 = a20 - a19; + a22 = a13 + a16; + a23 = a14 + a16; + a24 = a14 + a17; + a25 = a13 + a17; + a26 = a14 - a18; + a27 = a13 - a18; + + x0 = a22 + a19; x[0] = x0; + x1 = a15 + (m3 << 1); x[1] = x1; + x2 = a24 + a20; x[2] = x2; + x3 = a26 - a21; x[3] = x3; + x4 = a1 - a11; x[4] = x4; + x5 = a27 + a21; x[5] = x5; + x6 = a25 - a20; x[6] = x6; + x7 = a15 - (m3 << 1); x[7] = x7; + x8 = a23 - a19; x[8] = x8; +} + +/* let c(j) = cos(M_PI/36 * ((j)+0.5)), s(j) = sin(M_PI/36 * ((j)+0.5)) + * then fastWin[2*j+0] = c(j)*(s(j) + c(j)), j = [0, 8] + * fastWin[2*j+1] = c(j)*(s(j) - c(j)) + * format = Q30 + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnarrowing" +const int fastWin36[18] PROGMEM = { + 0x42aace8b, 0xc2e92724, 0x47311c28, 0xc95f619a, 0x4a868feb, 0xd0859d8c, + 0x4c913b51, 0xd8243ea0, 0x4d413ccc, 0xe0000000, 0x4c913b51, 0xe7dbc161, + 0x4a868feb, 0xef7a6275, 0x47311c28, 0xf6a09e67, 0x42aace8b, 0xfd16d8dd, +}; +#pragma GCC diagnostic pop +/************************************************************************************** + * Function: IMDCT36 + * + * Description: 36-point modified DCT, with windowing and overlap-add (50% overlap) + * + * Inputs: vector of 18 coefficients (N/2 inputs produces N outputs, by symmetry) + * overlap part of last IMDCT (9 samples - see output comments) + * window type (0,1,2,3) of current and previous block + * current block index (for deciding whether to do frequency inversion) + * number of guard bits in input vector + * + * Outputs: 18 output samples, after windowing and overlap-add with last frame + * second half of (unwindowed) 36-point IMDCT - save for next time + * only save 9 xPrev samples, using symmetry (see WinPrevious()) + * + * Notes: this is Ken's hyper-fast algorithm, including symmetric sin window + * optimization, if applicable + * total number of multiplies, general case: + * 2*10 (idct9) + 9 (last stage imdct) + 36 (for windowing) = 65 + * total number of multiplies, btCurr == 0 && btPrev == 0: + * 2*10 (idct9) + 9 (last stage imdct) + 18 (for windowing) = 47 + * + * blockType == 0 is by far the most common case, so it should be + * possible to use the fast path most of the time + * this is the fastest known algorithm for performing + * long IMDCT + windowing + overlap-add in MP3 + * + * Return: mOut (OR of abs(y) for all y calculated here) + * + * TODO: optimize for ARM (reorder window coefs, ARM-style pointers in C, + * inline asm may or may not be helpful) + **************************************************************************************/ +// barely faster in RAM +/*__attribute__ ((section (".data")))*/ static int IMDCT36(int *xCurr, int *xPrev, int *y, int btCurr, int btPrev, int blockIdx, int gb) +{ + int i, es, xBuf[18], xPrevWin[18]; + int acc1, acc2, s, d, t, mOut; + int xo, xe, c, *xp, yLo, yHi; + const int *cp, *wp; + + acc1 = acc2 = 0; + xCurr += 17; + + /* 7 gb is always adequate for antialias + accumulator loop + idct9 */ + if (gb < 7) { + /* rarely triggered - 5% to 10% of the time on normal clips (with Q25 input) */ + es = 7 - gb; + for (i = 8; i >= 0; i--) { + acc1 = ((*xCurr--) >> es) - acc1; + acc2 = acc1 - acc2; + acc1 = ((*xCurr--) >> es) - acc1; + xBuf[i+9] = acc2; /* odd */ + xBuf[i+0] = acc1; /* even */ + xPrev[i] >>= es; + } + } else { + es = 0; + /* max gain = 18, assume adequate guard bits */ + for (i = 8; i >= 0; i--) { + acc1 = (*xCurr--) - acc1; + acc2 = acc1 - acc2; + acc1 = (*xCurr--) - acc1; + xBuf[i+9] = acc2; /* odd */ + xBuf[i+0] = acc1; /* even */ + } + } + /* xEven[0] and xOdd[0] scaled by 0.5 */ + xBuf[9] >>= 1; + xBuf[0] >>= 1; + + /* do 9-point IDCT on even and odd */ + idct9(xBuf+0); /* even */ + idct9(xBuf+9); /* odd */ + + xp = xBuf + 8; + cp = c18 + 8; + mOut = 0; + if (btPrev == 0 && btCurr == 0) { + /* fast path - use symmetry of sin window to reduce windowing multiplies to 18 (N/2) */ + wp = fastWin36; + for (i = 0; i < 9; i++) { + /* do ARM-style pointer arithmetic (i still needed for y[] indexing - compiler spills if 2 y pointers) */ + c = *cp--; xo = *(xp + 9); xe = *xp--; + /* gain 2 int bits here */ + xo = MULSHIFT32(c, xo); /* 2*c18*xOdd (mul by 2 implicit in scaling) */ + xe >>= 2; + + s = -(*xPrev); /* sum from last block (always at least 2 guard bits) */ + d = -(xe - xo); /* gain 2 int bits, don't shift xo (effective << 1 to eat sign bit, << 1 for mul by 2) */ + (*xPrev++) = xe + xo; /* symmetry - xPrev[i] = xPrev[17-i] for long blocks */ + t = s - d; + + yLo = (d + (MULSHIFT32(t, *wp++) << 2)); + yHi = (s + (MULSHIFT32(t, *wp++) << 2)); + y[(i)*NBANDS] = yLo; + y[(17-i)*NBANDS] = yHi; + mOut |= FASTABS(yLo); + mOut |= FASTABS(yHi); + } + } else { + /* slower method - either prev or curr is using window type != 0 so do full 36-point window + * output xPrevWin has at least 3 guard bits (xPrev has 2, gain 1 in WinPrevious) + */ + WinPrevious(xPrev, xPrevWin, btPrev); + + wp = imdctWin[btCurr]; + for (i = 0; i < 9; i++) { + c = *cp--; xo = *(xp + 9); xe = *xp--; + /* gain 2 int bits here */ + xo = MULSHIFT32(c, xo); /* 2*c18*xOdd (mul by 2 implicit in scaling) */ + xe >>= 2; + + d = xe - xo; + (*xPrev++) = xe + xo; /* symmetry - xPrev[i] = xPrev[17-i] for long blocks */ + + yLo = (xPrevWin[i] + MULSHIFT32(d, wp[i])) << 2; + yHi = (xPrevWin[17-i] + MULSHIFT32(d, wp[17-i])) << 2; + y[(i)*NBANDS] = yLo; + y[(17-i)*NBANDS] = yHi; + mOut |= FASTABS(yLo); + mOut |= FASTABS(yHi); + } + } + + xPrev -= 9; + mOut |= FreqInvertRescale(y, xPrev, blockIdx, es); + + return mOut; +} + +static int c3_0 = 0x6ed9eba1; /* format = Q31, cos(pi/6) */ +static int c6[3] = { 0x7ba3751d, 0x5a82799a, 0x2120fb83 }; /* format = Q31, cos(((0:2) + 0.5) * (pi/6)) */ + +/* 12-point inverse DCT, used in IMDCT12x3() + * 4 input guard bits will ensure no overflow + */ +static __inline void imdct12 (int *x, int *out) +{ + int a0, a1, a2; + int x0, x1, x2, x3, x4, x5; + + x0 = *x; x+=3; x1 = *x; x+=3; + x2 = *x; x+=3; x3 = *x; x+=3; + x4 = *x; x+=3; x5 = *x; x+=3; + + x4 -= x5; + x3 -= x4; + x2 -= x3; + x3 -= x5; + x1 -= x2; + x0 -= x1; + x1 -= x3; + + x0 >>= 1; + x1 >>= 1; + + a0 = MULSHIFT32(c3_0, x2) << 1; + a1 = x0 + (x4 >> 1); + a2 = x0 - x4; + x0 = a1 + a0; + x2 = a2; + x4 = a1 - a0; + + a0 = MULSHIFT32(c3_0, x3) << 1; + a1 = x1 + (x5 >> 1); + a2 = x1 - x5; + + /* cos window odd samples, mul by 2, eat sign bit */ + x1 = MULSHIFT32(c6[0], a1 + a0) << 2; + x3 = MULSHIFT32(c6[1], a2) << 2; + x5 = MULSHIFT32(c6[2], a1 - a0) << 2; + + *out = x0 + x1; out++; + *out = x2 + x3; out++; + *out = x4 + x5; out++; + *out = x4 - x5; out++; + *out = x2 - x3; out++; + *out = x0 - x1; +} + +/************************************************************************************** + * Function: IMDCT12x3 + * + * Description: three 12-point modified DCT's for short blocks, with windowing, + * short block concatenation, and overlap-add + * + * Inputs: 3 interleaved vectors of 6 samples each + * (block0[0], block1[0], block2[0], block0[1], block1[1]....) + * overlap part of last IMDCT (9 samples - see output comments) + * window type (0,1,2,3) of previous block + * current block index (for deciding whether to do frequency inversion) + * number of guard bits in input vector + * + * Outputs: updated sample vector x, net gain of 1 integer bit + * second half of (unwindowed) IMDCT's - save for next time + * only save 9 xPrev samples, using symmetry (see WinPrevious()) + * + * Return: mOut (OR of abs(y) for all y calculated here) + * + * TODO: optimize for ARM + **************************************************************************************/ + // barely faster in RAM +/*__attribute__ ((section (".data")))*/ static int IMDCT12x3(int *xCurr, int *xPrev, int *y, int btPrev, int blockIdx, int gb) +{ + int i, es, mOut, yLo, xBuf[18], xPrevWin[18]; /* need temp buffer for reordering short blocks */ + const int *wp; + + es = 0; + /* 7 gb is always adequate for accumulator loop + idct12 + window + overlap */ + if (gb < 7) { + es = 7 - gb; + for (i = 0; i < 18; i+=2) { + xCurr[i+0] >>= es; + xCurr[i+1] >>= es; + *xPrev++ >>= es; + } + xPrev -= 9; + } + + /* requires 4 input guard bits for each imdct12 */ + imdct12(xCurr + 0, xBuf + 0); + imdct12(xCurr + 1, xBuf + 6); + imdct12(xCurr + 2, xBuf + 12); + + /* window previous from last time */ + WinPrevious(xPrev, xPrevWin, btPrev); + + /* could unroll this for speed, minimum loads (short blocks usually rare, so doesn't make much overall difference) + * xPrevWin[i] << 2 still has 1 gb always, max gain of windowed xBuf stuff also < 1.0 and gain the sign bit + * so y calculations won't overflow + */ + wp = imdctWin[2]; + mOut = 0; + for (i = 0; i < 3; i++) { + yLo = (xPrevWin[ 0+i] << 2); + mOut |= FASTABS(yLo); y[( 0+i)*NBANDS] = yLo; + yLo = (xPrevWin[ 3+i] << 2); + mOut |= FASTABS(yLo); y[( 3+i)*NBANDS] = yLo; + yLo = (xPrevWin[ 6+i] << 2) + (MULSHIFT32(wp[0+i], xBuf[3+i])); + mOut |= FASTABS(yLo); y[( 6+i)*NBANDS] = yLo; + yLo = (xPrevWin[ 9+i] << 2) + (MULSHIFT32(wp[3+i], xBuf[5-i])); + mOut |= FASTABS(yLo); y[( 9+i)*NBANDS] = yLo; + yLo = (xPrevWin[12+i] << 2) + (MULSHIFT32(wp[6+i], xBuf[2-i]) + MULSHIFT32(wp[0+i], xBuf[(6+3)+i])); + mOut |= FASTABS(yLo); y[(12+i)*NBANDS] = yLo; + yLo = (xPrevWin[15+i] << 2) + (MULSHIFT32(wp[9+i], xBuf[0+i]) + MULSHIFT32(wp[3+i], xBuf[(6+5)-i])); + mOut |= FASTABS(yLo); y[(15+i)*NBANDS] = yLo; + } + + /* save previous (unwindowed) for overlap - only need samples 6-8, 12-17 */ + for (i = 6; i < 9; i++) + *xPrev++ = xBuf[i] >> 2; + for (i = 12; i < 18; i++) + *xPrev++ = xBuf[i] >> 2; + + xPrev -= 9; + mOut |= FreqInvertRescale(y, xPrev, blockIdx, es); + + return mOut; +} + +/************************************************************************************** + * Function: HybridTransform + * + * Description: IMDCT's, windowing, and overlap-add on long/short/mixed blocks + * + * Inputs: vector of input coefficients, length = nBlocksTotal * 18) + * vector of overlap samples from last time, length = nBlocksPrev * 9) + * buffer for output samples, length = MAXNSAMP + * SideInfoSub struct for this granule/channel + * BlockCount struct with necessary info + * number of non-zero input and overlap blocks + * number of long blocks in input vector (rest assumed to be short blocks) + * number of blocks which use long window (type) 0 in case of mixed block + * (bc->currWinSwitch, 0 for non-mixed blocks) + * + * Outputs: transformed, windowed, and overlapped sample buffer + * does frequency inversion on odd blocks + * updated buffer of samples for overlap + * + * Return: number of non-zero IMDCT blocks calculated in this call + * (including overlap-add) + * + * TODO: examine mixedBlock/winSwitch logic carefully (test he_mode.bit) + **************************************************************************************/ +/* __attribute__ ((section (".data"))) */ static int HybridTransform(int *xCurr, int *xPrev, int y[BLOCK_SIZE][NBANDS], SideInfoSub *sis, BlockCount *bc) +{ + int xPrevWin[18], currWinIdx, prevWinIdx; + int i, j, nBlocksOut, nonZero, mOut; + int fiBit, xp; + + ASSERT(bc->nBlocksLong <= NBANDS); + ASSERT(bc->nBlocksTotal <= NBANDS); + ASSERT(bc->nBlocksPrev <= NBANDS); + + mOut = 0; + + /* do long blocks, if any */ + for(i = 0; i < bc->nBlocksLong; i++) { + /* currWinIdx picks the right window for long blocks (if mixed, long blocks use window type 0) */ + currWinIdx = sis->blockType; + if (sis->mixedBlock && i < bc->currWinSwitch) + currWinIdx = 0; + + prevWinIdx = bc->prevType; + if (i < bc->prevWinSwitch) + prevWinIdx = 0; + + /* do 36-point IMDCT, including windowing and overlap-add */ + mOut |= IMDCT36(xCurr, xPrev, &(y[0][i]), currWinIdx, prevWinIdx, i, bc->gbIn); + xCurr += 18; + xPrev += 9; + } + + /* do short blocks (if any) */ + for ( ; i < bc->nBlocksTotal; i++) { + ASSERT(sis->blockType == 2); + + prevWinIdx = bc->prevType; + if (i < bc->prevWinSwitch) + prevWinIdx = 0; + + mOut |= IMDCT12x3(xCurr, xPrev, &(y[0][i]), prevWinIdx, i, bc->gbIn); + xCurr += 18; + xPrev += 9; + } + nBlocksOut = i; + + /* window and overlap prev if prev longer that current */ + for ( ; i < bc->nBlocksPrev; i++) { + prevWinIdx = bc->prevType; + if (i < bc->prevWinSwitch) + prevWinIdx = 0; + WinPrevious(xPrev, xPrevWin, prevWinIdx); + + nonZero = 0; + fiBit = i << 31; + for (j = 0; j < 9; j++) { + xp = xPrevWin[2*j+0] << 2; /* << 2 temp for scaling */ + nonZero |= xp; + y[2*j+0][i] = xp; + mOut |= FASTABS(xp); + + /* frequency inversion on odd blocks/odd samples (flip sign if i odd, j odd) */ + xp = xPrevWin[2*j+1] << 2; + xp = (xp ^ (fiBit >> 31)) + (i & 0x01); + nonZero |= xp; + y[2*j+1][i] = xp; + mOut |= FASTABS(xp); + + xPrev[j] = 0; + } + xPrev += 9; + if (nonZero) + nBlocksOut = i; + } + + /* clear rest of blocks */ + for ( ; i < 32; i++) { + for (j = 0; j < 18; j++) + y[j][i] = 0; + } + + bc->gbOut = CLZ(mOut) - 1; + + return nBlocksOut; +} + +/************************************************************************************** + * Function: IMDCT + * + * Description: do alias reduction, inverse MDCT, overlap-add, and frequency inversion + * + * Inputs: MP3DecInfo structure filled by UnpackFrameHeader(), UnpackSideInfo(), + * UnpackScaleFactors(), and DecodeHuffman() (for this granule, channel) + * includes PCM samples in overBuf (from last call to IMDCT) for OLA + * index of current granule and channel + * + * Outputs: PCM samples in outBuf, for input to subband transform + * PCM samples in overBuf, for OLA next time + * updated hi->nonZeroBound index for this channel + * + * Return: 0 on success, -1 if null input pointers + **************************************************************************************/ + // a bit faster in RAM +/*__attribute__ ((section (".data")))*/ int IMDCT(MP3DecInfo *mp3DecInfo, int gr, int ch) +{ + int nBfly, blockCutoff; + FrameHeader *fh; + SideInfo *si; + HuffmanInfo *hi; + IMDCTInfo *mi; + BlockCount bc; + + /* validate pointers */ + if (!mp3DecInfo || !mp3DecInfo->FrameHeaderPS || !mp3DecInfo->SideInfoPS || + !mp3DecInfo->HuffmanInfoPS || !mp3DecInfo->IMDCTInfoPS) + return -1; + + /* si is an array of up to 4 structs, stored as gr0ch0, gr0ch1, gr1ch0, gr1ch1 */ + fh = (FrameHeader *)(mp3DecInfo->FrameHeaderPS); + si = (SideInfo *)(mp3DecInfo->SideInfoPS); + hi = (HuffmanInfo*)(mp3DecInfo->HuffmanInfoPS); + mi = (IMDCTInfo *)(mp3DecInfo->IMDCTInfoPS); + + /* anti-aliasing done on whole long blocks only + * for mixed blocks, nBfly always 1, except 3 for 8 kHz MPEG 2.5 (see sfBandTab) + * nLongBlocks = number of blocks with (possibly) non-zero power + * nBfly = number of butterflies to do (nLongBlocks - 1, unless no long blocks) + */ + blockCutoff = fh->sfBand->l[(fh->ver == MPEG1 ? 8 : 6)] / 18; /* same as 3* num short sfb's in spec */ + if (si->sis[gr][ch].blockType != 2) { + /* all long transforms */ + bc.nBlocksLong = MIN((hi->nonZeroBound[ch] + 7) / 18 + 1, 32); + nBfly = bc.nBlocksLong - 1; + } else if (si->sis[gr][ch].blockType == 2 && si->sis[gr][ch].mixedBlock) { + /* mixed block - long transforms until cutoff, then short transforms */ + bc.nBlocksLong = blockCutoff; + nBfly = bc.nBlocksLong - 1; + } else { + /* all short transforms */ + bc.nBlocksLong = 0; + nBfly = 0; + } + + AntiAlias(hi->huffDecBuf[ch], nBfly); + hi->nonZeroBound[ch] = MAX(hi->nonZeroBound[ch], (nBfly * 18) + 8); + + ASSERT(hi->nonZeroBound[ch] <= MAX_NSAMP); + + /* for readability, use a struct instead of passing a million parameters to HybridTransform() */ + bc.nBlocksTotal = (hi->nonZeroBound[ch] + 17) / 18; + bc.nBlocksPrev = mi->numPrevIMDCT[ch]; + bc.prevType = mi->prevType[ch]; + bc.prevWinSwitch = mi->prevWinSwitch[ch]; + bc.currWinSwitch = (si->sis[gr][ch].mixedBlock ? blockCutoff : 0); /* where WINDOW switches (not nec. transform) */ + bc.gbIn = hi->gb[ch]; + + mi->numPrevIMDCT[ch] = HybridTransform(hi->huffDecBuf[ch], mi->overBuf[ch], mi->outBuf[ch], &si->sis[gr][ch], &bc); + mi->prevType[ch] = si->sis[gr][ch].blockType; + mi->prevWinSwitch[ch] = bc.currWinSwitch; /* 0 means not a mixed block (either all short or all long) */ + mi->gb[ch] = bc.gbOut; + + ASSERT(mi->numPrevIMDCT[ch] <= NBANDS); + + /* output has gained 2 int bits */ + return 0; +} diff --git a/components/spotify/cspot/bell/libhelix-mp3/mp3common.h b/components/spotify/cspot/bell/libhelix-mp3/mp3common.h new file mode 100644 index 00000000..07548abf --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/mp3common.h @@ -0,0 +1,124 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * mp3common.h - implementation-independent API's, datatypes, and definitions + **************************************************************************************/ + +#ifndef _MP3COMMON_H +#define _MP3COMMON_H + +#include "mp3dec.h" +#include "statname.h" /* do name-mangling for static linking */ + +#define MAX_SCFBD 4 /* max scalefactor bands per channel */ +#define NGRANS_MPEG1 2 +#define NGRANS_MPEG2 1 + +/* 11-bit syncword if MPEG 2.5 extensions are enabled */ +/* +#define SYNCWORDH 0xff +#define SYNCWORDL 0xe0 +*/ + +/* 12-bit syncword if MPEG 1,2 only are supported */ +#define SYNCWORDH 0xff +#define SYNCWORDL 0xf0 + +typedef struct _MP3DecInfo { + /* pointers to platform-specific data structures */ + void *FrameHeaderPS; + void *SideInfoPS; + void *ScaleFactorInfoPS; + void *HuffmanInfoPS; + void *DequantInfoPS; + void *IMDCTInfoPS; + void *SubbandInfoPS; + + /* buffer which must be large enough to hold largest possible main_data section */ + unsigned char mainBuf[MAINBUF_SIZE]; + + /* special info for "free" bitrate files */ + int freeBitrateFlag; + int freeBitrateSlots; + + /* user-accessible info */ + int bitrate; + int nChans; + int samprate; + int nGrans; /* granules per frame */ + int nGranSamps; /* samples per granule */ + int nSlots; + int layer; + MPEGVersion version; + + int mainDataBegin; + int mainDataBytes; + + int part23Length[MAX_NGRAN][MAX_NCHAN]; + +} MP3DecInfo; + +typedef struct _SFBandTable { + int/*short*/ l[23]; + int/*short*/ s[14]; +} SFBandTable; + +/* decoder functions which must be implemented for each platform */ +MP3DecInfo *AllocateBuffers(void); +void FreeBuffers(MP3DecInfo *mp3DecInfo); +int CheckPadBit(MP3DecInfo *mp3DecInfo); +int UnpackFrameHeader(MP3DecInfo *mp3DecInfo, unsigned char *buf); +int UnpackSideInfo(MP3DecInfo *mp3DecInfo, unsigned char *buf); +int DecodeHuffman(MP3DecInfo *mp3DecInfo, unsigned char *buf, int *bitOffset, int huffBlockBits, int gr, int ch); +int Dequantize(MP3DecInfo *mp3DecInfo, int gr); +int IMDCT(MP3DecInfo *mp3DecInfo, int gr, int ch); +int UnpackScaleFactors(MP3DecInfo *mp3DecInfo, unsigned char *buf, int *bitOffset, int bitsAvail, int gr, int ch); +int Subband(MP3DecInfo *mp3DecInfo, short *pcmBuf); + +/* mp3tabs.c - global ROM tables */ +extern const int samplerateTab[3][3]; +extern const int/*short*/ bitrateTab[3][3][15]; +extern const int/*short*/ samplesPerFrameTab[3][3]; +extern const short bitsPerSlotTab[3]; +extern const int/*short*/ sideBytesTab[3][2]; +extern const int/*short*/ slotTab[3][3][15]; +extern const SFBandTable sfBandTable[3][3]; + +#endif /* _MP3COMMON_H */ diff --git a/components/spotify/cspot/bell/libhelix-mp3/mp3dec.c b/components/spotify/cspot/bell/libhelix-mp3/mp3dec.c new file mode 100644 index 00000000..5e68c7af --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/mp3dec.c @@ -0,0 +1,484 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * mp3dec.c - platform-independent top level MP3 decoder API + **************************************************************************************/ + +#include "string.h" +//#include "hlxclib/string.h" /* for memmove, memcpy (can replace with different implementations if desired) */ +#include "mp3common.h" /* includes mp3dec.h (public API) and internal, platform-independent API */ + + +//#define PROFILE +#ifdef PROFILE +#include "systime.h" +#endif + +/************************************************************************************** + * Function: MP3InitDecoder + * + * Description: allocate memory for platform-specific data + * clear all the user-accessible fields + * + * Inputs: none + * + * Outputs: none + * + * Return: handle to mp3 decoder instance, 0 if malloc fails + **************************************************************************************/ +HMP3Decoder MP3InitDecoder(void) +{ + MP3DecInfo *mp3DecInfo; + + mp3DecInfo = AllocateBuffers(); + + return (HMP3Decoder)mp3DecInfo; +} + +/************************************************************************************** + * Function: MP3FreeDecoder + * + * Description: free platform-specific data allocated by InitMP3Decoder + * zero out the contents of MP3DecInfo struct + * + * Inputs: valid MP3 decoder instance pointer (HMP3Decoder) + * + * Outputs: none + * + * Return: none + **************************************************************************************/ +void MP3FreeDecoder(HMP3Decoder hMP3Decoder) +{ + MP3DecInfo *mp3DecInfo = (MP3DecInfo *)hMP3Decoder; + + if (!mp3DecInfo) + return; + + FreeBuffers(mp3DecInfo); +} + +/************************************************************************************** + * Function: MP3FindSyncWord + * + * Description: locate the next byte-alinged sync word in the raw mp3 stream + * + * Inputs: buffer to search for sync word + * max number of bytes to search in buffer + * + * Outputs: none + * + * Return: offset to first sync word (bytes from start of buf) + * -1 if sync not found after searching nBytes + **************************************************************************************/ +int MP3FindSyncWord(unsigned char *buf, int nBytes) +{ + int i; + + /* find byte-aligned syncword - need 12 (MPEG 1,2) or 11 (MPEG 2.5) matching bits */ + for (i = 0; i < nBytes - 1; i++) { + if ( (buf[i+0] & SYNCWORDH) == SYNCWORDH && (buf[i+1] & SYNCWORDL) == SYNCWORDL ) + return i; + } + + return -1; +} + +/************************************************************************************** + * Function: MP3FindFreeSync + * + * Description: figure out number of bytes between adjacent sync words in "free" mode + * + * Inputs: buffer to search for next sync word + * the 4-byte frame header starting at the current sync word + * max number of bytes to search in buffer + * + * Outputs: none + * + * Return: offset to next sync word, minus any pad byte (i.e. nSlots) + * -1 if sync not found after searching nBytes + * + * Notes: this checks that the first 22 bits of the next frame header are the + * same as the current frame header, but it's still not foolproof + * (could accidentally find a sequence in the bitstream which + * appears to match but is not actually the next frame header) + * this could be made more error-resilient by checking several frames + * in a row and verifying that nSlots is the same in each case + * since free mode requires CBR (see spec) we generally only call + * this function once (first frame) then store the result (nSlots) + * and just use it from then on + **************************************************************************************/ +static int MP3FindFreeSync(unsigned char *buf, unsigned char firstFH[4], int nBytes) +{ + int offset = 0; + unsigned char *bufPtr = buf; + + /* loop until we either: + * - run out of nBytes (FindMP3SyncWord() returns -1) + * - find the next valid frame header (sync word, version, layer, CRC flag, bitrate, and sample rate + * in next header must match current header) + */ + while (1) { + offset = MP3FindSyncWord(bufPtr, nBytes); + bufPtr += offset; + if (offset < 0) { + return -1; + } else if ( (bufPtr[0] == firstFH[0]) && (bufPtr[1] == firstFH[1]) && ((bufPtr[2] & 0xfc) == (firstFH[2] & 0xfc)) ) { + /* want to return number of bytes per frame, NOT counting the padding byte, so subtract one if padFlag == 1 */ + if ((firstFH[2] >> 1) & 0x01) + bufPtr--; + return bufPtr - buf; + } + bufPtr += 3; + nBytes -= (offset + 3); + }; + + return -1; +} + +/************************************************************************************** + * Function: MP3GetLastFrameInfo + * + * Description: get info about last MP3 frame decoded (number of sampled decoded, + * sample rate, bitrate, etc.) + * + * Inputs: valid MP3 decoder instance pointer (HMP3Decoder) + * pointer to MP3FrameInfo struct + * + * Outputs: filled-in MP3FrameInfo struct + * + * Return: none + * + * Notes: call this right after calling MP3Decode + **************************************************************************************/ +void MP3GetLastFrameInfo(HMP3Decoder hMP3Decoder, MP3FrameInfo *mp3FrameInfo) +{ + MP3DecInfo *mp3DecInfo = (MP3DecInfo *)hMP3Decoder; + + if (!mp3DecInfo || mp3DecInfo->layer != 3) { + mp3FrameInfo->bitrate = 0; + mp3FrameInfo->nChans = 0; + mp3FrameInfo->samprate = 0; + mp3FrameInfo->bitsPerSample = 0; + mp3FrameInfo->outputSamps = 0; + mp3FrameInfo->layer = 0; + mp3FrameInfo->version = 0; + } else { + mp3FrameInfo->bitrate = mp3DecInfo->bitrate; + mp3FrameInfo->nChans = mp3DecInfo->nChans; + mp3FrameInfo->samprate = mp3DecInfo->samprate; + mp3FrameInfo->bitsPerSample = 16; + mp3FrameInfo->outputSamps = mp3DecInfo->nChans * (int)samplesPerFrameTab[mp3DecInfo->version][mp3DecInfo->layer - 1]; + mp3FrameInfo->layer = mp3DecInfo->layer; + mp3FrameInfo->version = mp3DecInfo->version; + } +} + +/************************************************************************************** + * Function: MP3GetNextFrameInfo + * + * Description: parse MP3 frame header + * + * Inputs: valid MP3 decoder instance pointer (HMP3Decoder) + * pointer to MP3FrameInfo struct + * pointer to buffer containing valid MP3 frame header (located using + * MP3FindSyncWord(), above) + * + * Outputs: filled-in MP3FrameInfo struct + * + * Return: error code, defined in mp3dec.h (0 means no error, < 0 means error) + **************************************************************************************/ +int MP3GetNextFrameInfo(HMP3Decoder hMP3Decoder, MP3FrameInfo *mp3FrameInfo, unsigned char *buf) +{ + MP3DecInfo *mp3DecInfo = (MP3DecInfo *)hMP3Decoder; + + if (!mp3DecInfo) + return ERR_MP3_NULL_POINTER; + + if (UnpackFrameHeader(mp3DecInfo, buf) == -1 || mp3DecInfo->layer != 3) + return ERR_MP3_INVALID_FRAMEHEADER; + + MP3GetLastFrameInfo(mp3DecInfo, mp3FrameInfo); + + return ERR_MP3_NONE; +} + +/************************************************************************************** + * Function: MP3ClearBadFrame + * + * Description: zero out pcm buffer if error decoding MP3 frame + * + * Inputs: mp3DecInfo struct with correct frame size parameters filled in + * pointer pcm output buffer + * + * Outputs: zeroed out pcm buffer + * + * Return: none + **************************************************************************************/ +static void MP3ClearBadFrame(MP3DecInfo *mp3DecInfo, short *outbuf) +{ + int i; + + if (!mp3DecInfo) + return; + + for (i = 0; i < mp3DecInfo->nGrans * mp3DecInfo->nGranSamps * mp3DecInfo->nChans; i++) + outbuf[i] = 0; +} + +/************************************************************************************** + * Function: MP3Decode + * + * Description: decode one frame of MP3 data + * + * Inputs: valid MP3 decoder instance pointer (HMP3Decoder) + * double pointer to buffer of MP3 data (containing headers + mainData) + * number of valid bytes remaining in inbuf + * pointer to outbuf, big enough to hold one frame of decoded PCM samples + * flag indicating whether MP3 data is normal MPEG format (useSize = 0) + * or reformatted as "self-contained" frames (useSize = 1) + * + * Outputs: PCM data in outbuf, interleaved LRLRLR... if stereo + * number of output samples = nGrans * nGranSamps * nChans + * updated inbuf pointer, updated bytesLeft + * + * Return: error code, defined in mp3dec.h (0 means no error, < 0 means error) + * + * Notes: switching useSize on and off between frames in the same stream + * is not supported (bit reservoir is not maintained if useSize on) + **************************************************************************************/ +int MP3Decode(HMP3Decoder hMP3Decoder, unsigned char **inbuf, int *bytesLeft, short *outbuf, int useSize) +{ + int offset, bitOffset, mainBits, gr, ch, fhBytes, siBytes, freeFrameBytes; + int prevBitOffset, sfBlockBits, huffBlockBits; + unsigned char *mainPtr; + MP3DecInfo *mp3DecInfo = (MP3DecInfo *)hMP3Decoder; + + #ifdef PROFILE + long time; + #endif + + if (!mp3DecInfo) + return ERR_MP3_NULL_POINTER; + + /* unpack frame header */ + fhBytes = UnpackFrameHeader(mp3DecInfo, *inbuf); + if (fhBytes < 0) + return ERR_MP3_INVALID_FRAMEHEADER; /* don't clear outbuf since we don't know size (failed to parse header) */ + *inbuf += fhBytes; + +#ifdef PROFILE + time = systime_get(); +#endif + /* unpack side info */ + siBytes = UnpackSideInfo(mp3DecInfo, *inbuf); + if (siBytes < 0) { + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_INVALID_SIDEINFO; + } + *inbuf += siBytes; + *bytesLeft -= (fhBytes + siBytes); +#ifdef PROFILE + time = systime_get() - time; + printf("UnpackSideInfo: %i ms\n", time); +#endif + + + /* if free mode, need to calculate bitrate and nSlots manually, based on frame size */ + if (mp3DecInfo->bitrate == 0 || mp3DecInfo->freeBitrateFlag) { + if (!mp3DecInfo->freeBitrateFlag) { + /* first time through, need to scan for next sync word and figure out frame size */ + mp3DecInfo->freeBitrateFlag = 1; + mp3DecInfo->freeBitrateSlots = MP3FindFreeSync(*inbuf, *inbuf - fhBytes - siBytes, *bytesLeft); + if (mp3DecInfo->freeBitrateSlots < 0) { + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_FREE_BITRATE_SYNC; + } + freeFrameBytes = mp3DecInfo->freeBitrateSlots + fhBytes + siBytes; + mp3DecInfo->bitrate = (freeFrameBytes * mp3DecInfo->samprate * 8) / (mp3DecInfo->nGrans * mp3DecInfo->nGranSamps); + } + mp3DecInfo->nSlots = mp3DecInfo->freeBitrateSlots + CheckPadBit(mp3DecInfo); /* add pad byte, if required */ + } + + /* useSize != 0 means we're getting reformatted (RTP) packets (see RFC 3119) + * - calling function assembles "self-contained" MP3 frames by shifting any main_data + * from the bit reservoir (in previous frames) to AFTER the sync word and side info + * - calling function should set mainDataBegin to 0, and tell us exactly how large this + * frame is (in bytesLeft) + */ + if (useSize) { + mp3DecInfo->nSlots = *bytesLeft; + if (mp3DecInfo->mainDataBegin != 0 || mp3DecInfo->nSlots <= 0) { + /* error - non self-contained frame, or missing frame (size <= 0), could do loss concealment here */ + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_INVALID_FRAMEHEADER; + } + + /* can operate in-place on reformatted frames */ + mp3DecInfo->mainDataBytes = mp3DecInfo->nSlots; + mainPtr = *inbuf; + *inbuf += mp3DecInfo->nSlots; + *bytesLeft -= (mp3DecInfo->nSlots); + } else { + /* out of data - assume last or truncated frame */ + if (mp3DecInfo->nSlots > *bytesLeft) { + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_INDATA_UNDERFLOW; + } + +#ifdef PROFILE + time = systime_get(); +#endif + /* fill main data buffer with enough new data for this frame */ + if (mp3DecInfo->mainDataBytes >= mp3DecInfo->mainDataBegin) { + /* adequate "old" main data available (i.e. bit reservoir) */ + memmove(mp3DecInfo->mainBuf, mp3DecInfo->mainBuf + mp3DecInfo->mainDataBytes - mp3DecInfo->mainDataBegin, mp3DecInfo->mainDataBegin); + memcpy(mp3DecInfo->mainBuf + mp3DecInfo->mainDataBegin, *inbuf, mp3DecInfo->nSlots); + + mp3DecInfo->mainDataBytes = mp3DecInfo->mainDataBegin + mp3DecInfo->nSlots; + *inbuf += mp3DecInfo->nSlots; + *bytesLeft -= (mp3DecInfo->nSlots); + mainPtr = mp3DecInfo->mainBuf; + } else { + /* not enough data in bit reservoir from previous frames (perhaps starting in middle of file) */ + memcpy(mp3DecInfo->mainBuf + mp3DecInfo->mainDataBytes, *inbuf, mp3DecInfo->nSlots); + mp3DecInfo->mainDataBytes += mp3DecInfo->nSlots; + *inbuf += mp3DecInfo->nSlots; + *bytesLeft -= (mp3DecInfo->nSlots); + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_MAINDATA_UNDERFLOW; + } +#ifdef PROFILE + time = systime_get() - time; + printf("data buffer filling: %i ms\n", time); +#endif + + } + bitOffset = 0; + mainBits = mp3DecInfo->mainDataBytes * 8; + + /* decode one complete frame */ + for (gr = 0; gr < mp3DecInfo->nGrans; gr++) { + for (ch = 0; ch < mp3DecInfo->nChans; ch++) { + + #ifdef PROFILE + time = systime_get(); + #endif + /* unpack scale factors and compute size of scale factor block */ + prevBitOffset = bitOffset; + offset = UnpackScaleFactors(mp3DecInfo, mainPtr, &bitOffset, mainBits, gr, ch); + #ifdef PROFILE + time = systime_get() - time; + printf("UnpackScaleFactors: %i ms\n", time); + #endif + + sfBlockBits = 8*offset - prevBitOffset + bitOffset; + huffBlockBits = mp3DecInfo->part23Length[gr][ch] - sfBlockBits; + mainPtr += offset; + mainBits -= sfBlockBits; + + if (offset < 0 || mainBits < huffBlockBits) { + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_INVALID_SCALEFACT; + } + + #ifdef PROFILE + time = systime_get(); + #endif + /* decode Huffman code words */ + prevBitOffset = bitOffset; + offset = DecodeHuffman(mp3DecInfo, mainPtr, &bitOffset, huffBlockBits, gr, ch); + if (offset < 0) { + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_INVALID_HUFFCODES; + } + #ifdef PROFILE + time = systime_get() - time; + printf("Huffman: %i ms\n", time); + #endif + + mainPtr += offset; + mainBits -= (8*offset - prevBitOffset + bitOffset); + } + + #ifdef PROFILE + time = systime_get(); + #endif + /* dequantize coefficients, decode stereo, reorder short blocks */ + if (Dequantize(mp3DecInfo, gr) < 0) { + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_INVALID_DEQUANTIZE; + } + #ifdef PROFILE + time = systime_get() - time; + printf("Dequantize: %i ms\n", time); + #endif + + /* alias reduction, inverse MDCT, overlap-add, frequency inversion */ + for (ch = 0; ch < mp3DecInfo->nChans; ch++) + { + #ifdef PROFILE + time = systime_get(); + #endif + if (IMDCT(mp3DecInfo, gr, ch) < 0) { + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_INVALID_IMDCT; + } + #ifdef PROFILE + time = systime_get() - time; + printf("IMDCT: %i ms\n", time); + #endif + } + + #ifdef PROFILE + time = systime_get(); + #endif + /* subband transform - if stereo, interleaves pcm LRLRLR */ + if (Subband(mp3DecInfo, outbuf + gr*mp3DecInfo->nGranSamps*mp3DecInfo->nChans) < 0) { + MP3ClearBadFrame(mp3DecInfo, outbuf); + return ERR_MP3_INVALID_SUBBAND; + } + #ifdef PROFILE + time = systime_get() - time; + printf("Subband: %i ms\n", time); + #endif + + } + return ERR_MP3_NONE; +} diff --git a/components/spotify/cspot/bell/libhelix-mp3/mp3dec.h b/components/spotify/cspot/bell/libhelix-mp3/mp3dec.h new file mode 100644 index 00000000..4e28b1e7 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/mp3dec.h @@ -0,0 +1,146 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * mp3dec.h - public C API for MP3 decoder + **************************************************************************************/ + +#include +#define Word64 uint64_t + #ifdef ESP8266 +# include "pgmspace.h" +#elif defined(ESP_PLATFORM) && __has_include() +# include +#else +# define PROGMEM +# define pgm_read_byte(addr) (*(const unsigned char *)(addr)) +# define pgm_read_word(addr) (*(const unsigned short *)(addr)) +#endif +#ifndef _MP3DEC_H +#define _MP3DEC_H + +#if defined(_WIN32) && !defined(_WIN32_WCE) +# +#elif defined(_WIN32) && defined(WINCE_EMULATOR) +# +#elif defined(ARM_ADS) +# +#elif defined(__APPLE__) +# +#elif defined(_SYMBIAN) && defined(__WINS__) /* Symbian emulator for Ix86 */ +# +#elif defined(__GNUC__) && defined(__thumb__) +# +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +# +#elif defined(_OPENWAVE_SIMULATOR) || defined(_OPENWAVE_ARMULATOR) +# +#elif defined (ESP_PLATFORM) +# +#else +#error No platform defined. See valid options in mp3dec.h +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/* determining MAINBUF_SIZE: + * max mainDataBegin = (2^9 - 1) bytes (since 9-bit offset) = 511 + * max nSlots (concatenated with mainDataBegin bytes from before) = 1440 - 9 - 4 + 1 = 1428 + * 511 + 1428 = 1939, round up to 1940 (4-byte align) + */ +#define MAINBUF_SIZE 1940 + +#define MAX_NGRAN 2 /* max granules */ +#define MAX_NCHAN 2 /* max channels */ +#define MAX_NSAMP 576 /* max samples per channel, per granule */ + +/* map to 0,1,2 to make table indexing easier */ +typedef enum { + MPEG1 = 0, + MPEG2 = 1, + MPEG25 = 2 +} MPEGVersion; + +typedef void *HMP3Decoder; + +enum { + ERR_MP3_NONE = 0, + ERR_MP3_INDATA_UNDERFLOW = -1, + ERR_MP3_MAINDATA_UNDERFLOW = -2, + ERR_MP3_FREE_BITRATE_SYNC = -3, + ERR_MP3_OUT_OF_MEMORY = -4, + ERR_MP3_NULL_POINTER = -5, + ERR_MP3_INVALID_FRAMEHEADER = -6, + ERR_MP3_INVALID_SIDEINFO = -7, + ERR_MP3_INVALID_SCALEFACT = -8, + ERR_MP3_INVALID_HUFFCODES = -9, + ERR_MP3_INVALID_DEQUANTIZE = -10, + ERR_MP3_INVALID_IMDCT = -11, + ERR_MP3_INVALID_SUBBAND = -12, + + ERR_UNKNOWN = -9999 +}; + +typedef struct _MP3FrameInfo { + int bitrate; + int nChans; + int samprate; + int bitsPerSample; + int outputSamps; + int layer; + int version; +} MP3FrameInfo; + +/* public API */ +HMP3Decoder MP3InitDecoder(void); +void MP3FreeDecoder(HMP3Decoder hMP3Decoder); +int MP3Decode(HMP3Decoder hMP3Decoder, unsigned char **inbuf, int *bytesLeft, short *outbuf, int useSize); + +void MP3GetLastFrameInfo(HMP3Decoder hMP3Decoder, MP3FrameInfo *mp3FrameInfo); +int MP3GetNextFrameInfo(HMP3Decoder hMP3Decoder, MP3FrameInfo *mp3FrameInfo, unsigned char *buf); +int MP3FindSyncWord(unsigned char *buf, int nBytes); + +#ifdef __cplusplus +} +#endif + +#endif /* _MP3DEC_H */ diff --git a/components/spotify/cspot/bell/libhelix-mp3/mp3tabs.c b/components/spotify/cspot/bell/libhelix-mp3/mp3tabs.c new file mode 100644 index 00000000..7d5fec22 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/mp3tabs.c @@ -0,0 +1,182 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * mp3tabs.c - platform-independent tables for MP3 decoder (global, read-only) + **************************************************************************************/ + +#include "mp3common.h" +#include + +/* indexing = [version][samplerate index] + * sample rate of frame (Hz) + */ +const int samplerateTab[3][3] PROGMEM = { + {44100, 48000, 32000}, /* MPEG-1 */ + {22050, 24000, 16000}, /* MPEG-2 */ + {11025, 12000, 8000}, /* MPEG-2.5 */ +}; + +/* indexing = [version][layer][bitrate index] + * bitrate (kbps) of frame + * - bitrate index == 0 is "free" mode (bitrate determined on the fly by + * counting bits between successive sync words) + */ +const int/*short*/ bitrateTab[3][3][15] PROGMEM = { + { + /* MPEG-1 */ + { 0, 32, 64, 96,128,160,192,224,256,288,320,352,384,416,448}, /* Layer 1 */ + { 0, 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384}, /* Layer 2 */ + { 0, 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224,256,320}, /* Layer 3 */ + }, + { + /* MPEG-2 */ + { 0, 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256}, /* Layer 1 */ + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160}, /* Layer 2 */ + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160}, /* Layer 3 */ + }, + { + /* MPEG-2.5 */ + { 0, 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256}, /* Layer 1 */ + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160}, /* Layer 2 */ + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160}, /* Layer 3 */ + }, +}; + +/* indexing = [version][layer] + * number of samples in one frame (per channel) + */ +const int/*short*/ samplesPerFrameTab[3][3] PROGMEM = { + {384, 1152, 1152 }, /* MPEG1 */ + {384, 1152, 576 }, /* MPEG2 */ + {384, 1152, 576 }, /* MPEG2.5 */ +}; + +/* layers 1, 2, 3 */ +const short bitsPerSlotTab[3] = {32, 8, 8}; + +/* indexing = [version][mono/stereo] + * number of bytes in side info section of bitstream + */ +const int/*short*/ sideBytesTab[3][2] PROGMEM = { + {17, 32}, /* MPEG-1: mono, stereo */ + { 9, 17}, /* MPEG-2: mono, stereo */ + { 9, 17}, /* MPEG-2.5: mono, stereo */ +}; + +/* indexing = [version][sampleRate][bitRate] + * for layer3, nSlots = floor(samps/frame * bitRate / sampleRate / 8) + * - add one pad slot if necessary + */ +const int/*short*/ slotTab[3][3][15] PROGMEM = { + { + /* MPEG-1 */ + { 0, 104, 130, 156, 182, 208, 261, 313, 365, 417, 522, 626, 731, 835,1044 }, /* 44 kHz */ + { 0, 96, 120, 144, 168, 192, 240, 288, 336, 384, 480, 576, 672, 768, 960 }, /* 48 kHz */ + { 0, 144, 180, 216, 252, 288, 360, 432, 504, 576, 720, 864,1008,1152,1440 }, /* 32 kHz */ + }, + { + /* MPEG-2 */ + { 0, 26, 52, 78, 104, 130, 156, 182, 208, 261, 313, 365, 417, 470, 522 }, /* 22 kHz */ + { 0, 24, 48, 72, 96, 120, 144, 168, 192, 240, 288, 336, 384, 432, 480 }, /* 24 kHz */ + { 0, 36, 72, 108, 144, 180, 216, 252, 288, 360, 432, 504, 576, 648, 720 }, /* 16 kHz */ + }, + { + /* MPEG-2.5 */ + { 0, 52, 104, 156, 208, 261, 313, 365, 417, 522, 626, 731, 835, 940,1044 }, /* 11 kHz */ + { 0, 48, 96, 144, 192, 240, 288, 336, 384, 480, 576, 672, 768, 864, 960 }, /* 12 kHz */ + { 0, 72, 144, 216, 288, 360, 432, 504, 576, 720, 864,1008,1152,1296,1440 }, /* 8 kHz */ + }, +}; + +/* indexing = [version][sampleRate][long (.l) or short (.s) block] + * sfBandTable[v][s].l[cb] = index of first bin in critical band cb (long blocks) + * sfBandTable[v][s].s[cb] = index of first bin in critical band cb (short blocks) + */ +const SFBandTable sfBandTable[3][3] PROGMEM = { + { + /* MPEG-1 (44, 48, 32 kHz) */ + { + { 0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 52, 62, 74, 90,110,134,162,196,238,288,342,418,576 }, + { 0, 4, 8, 12, 16, 22, 30, 40, 52, 66, 84,106,136,192 } + }, + { + { 0, 4, 8, 12, 16, 20, 24, 30, 36, 42, 50, 60, 72, 88,106,128,156,190,230,276,330,384,576 }, + { 0, 4, 8, 12, 16, 22, 28, 38, 50, 64, 80,100,126,192 } + }, + { + { 0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 54, 66, 82,102,126,156,194,240,296,364,448,550,576 }, + { 0, 4, 8, 12, 16, 22, 30, 42, 58, 78,104,138,180,192 } + } + }, + + { + /* MPEG-2 (22, 24, 16 kHz) */ + { + { 0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96,116,140,168,200,238,284,336,396,464,522,576 }, + { 0, 4, 8, 12, 18, 24, 32, 42, 56, 74,100,132,174,192 } + }, + { + { 0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96,114,136,162,194,232,278,332,394,464,540,576 }, + { 0, 4, 8, 12, 18, 26, 36, 48, 62, 80,104,136,180,192 } + }, + { + { 0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96,116,140,168,200,238,284,336,396,464,522,576 }, + { 0, 4, 8, 12, 18, 26, 36, 48, 62, 80,104,134,174,192 } + }, + }, + + { + /* MPEG-2.5 (11, 12, 8 kHz) */ + { + { 0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96,116,140,168,200,238,284,336,396,464,522,576 }, + { 0, 4, 8, 12, 18, 26, 36, 48, 62, 80,104,134,174,192 } + }, + { + { 0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96,116,140,168,200,238,284,336,396,464,522,576 }, + { 0, 4, 8, 12, 18, 26, 36, 48, 62, 80,104,134,174,192 } + }, + { + { 0, 12, 24, 36, 48, 60, 72, 88,108,132,160,192,232,280,336,400,476,566,568,570,572,574,576 }, + { 0, 8, 16, 24, 36, 52, 72, 96,124,160,162,164,166,192 } + }, + }, +}; + + diff --git a/components/spotify/cspot/bell/libhelix-mp3/mpadecobjfixpt.h b/components/spotify/cspot/bell/libhelix-mp3/mpadecobjfixpt.h new file mode 100644 index 00000000..a8a5c40f --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/mpadecobjfixpt.h @@ -0,0 +1,108 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _MPADECOBJFIXPT_H_ +#define _MPADECOBJFIXPT_H_ + +#include "mp3dec.h" /* public C API for new MP3 decoder */ + +class CMpaDecObj +{ +public: + CMpaDecObj(); + ~CMpaDecObj(); + + /////////////////////////////////////////////////////////////////////////// + // Function: Init_n + // Purpose: Initialize the mp3 decoder. + // Parameters: pSync a pointer to a syncword + // ulSize the size of the buffer pSync points to + // bUseSize this tells the decoder to use the input frame + // size on the decode instead of calculating + // the frame size. This is necessary when + // our formatted mp3 data (main_data_begin always + // equal to 0). + // + // Returns: returns 1 on success, 0 on failure + /////////////////////////////////////////////////////////////////////////// + int Init_n(unsigned char *pSync, + unsigned long ulSize, + unsigned char bUseSize=0); + + /////////////////////////////////////////////////////////////////////////// + // Function: DecodeFrame_v + // Purpose: Decodes one mp3 frame + // Parameters: pSource pointer to an mp3 frame (at a syncword) + // pulSize size of the buffer pSource points to. It will + // contain the number of mp3 bytes decoded upon + // return. + // pPCM pointer to a buffer to decode into + // pulPCMSize size of the PCM buffer. It will contain the + // number of PCM bytes prodced upon return. + /////////////////////////////////////////////////////////////////////////// + void DecodeFrame_v(unsigned char *pSource, + unsigned long *pulSize, + unsigned char *pPCM, + unsigned long *pulPCMSize); + + // overloaded new version that returns error code in errCode + void DecodeFrame_v(unsigned char *pSource, + unsigned long *pulSize, + unsigned char *pPCM, + unsigned long *pulPCMSize, + int *errCode); + + void GetPCMInfo_v(unsigned long &ulSampRate, + int &nChannels, + int &nBitsPerSample); + + // return number of samples per frame, PER CHANNEL (renderer multiplies this result by nChannels) + int GetSamplesPerFrame_n(); + + void SetTrustPackets(unsigned char bTrust) { m_bTrustPackets = bTrust; } + +private: + void * m_pDec; // generic void ptr + + void * m_pDecL1; // not implemented (could use old Xing mpadecl1.cpp) + void * m_pDecL2; // not implemented (could use old Xing mpadecl2.cpp) + HMP3Decoder m_pDecL3; + + MP3FrameInfo m_lastMP3FrameInfo; + unsigned char m_bUseFrameSize; + unsigned char m_bTrustPackets; +}; + +#endif /* _MPADECOBJFIXPT_H_ */ diff --git a/components/spotify/cspot/bell/libhelix-mp3/pgmspace.h b/components/spotify/cspot/bell/libhelix-mp3/pgmspace.h new file mode 100644 index 00000000..b5ace1fe --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/pgmspace.h @@ -0,0 +1,9 @@ +#ifdef ESP8266 +# include "pgmspace.h" +#elif defined(ESP_PLATFORM) && __has_include() +# include +#else +# define PROGMEM +# define pgm_read_byte(addr) (*(const unsigned char *)(addr)) +# define pgm_read_word(addr) (*(const unsigned short *)(addr)) +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/libhelix-mp3/player.h b/components/spotify/cspot/bell/libhelix-mp3/player.h new file mode 100644 index 00000000..40f939e7 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/player.h @@ -0,0 +1,13 @@ + + + +//SPI +#define PIN_SPI_SCK 14 +#define PIN_SPI_MOSI 7 +#define PIN_SPI_SDCARD_CS 10 //SD-Card CS +#define PIN_SPI_MEM_CS 6 //Flashmem CS + +//3V3 Voltage Regulator +#define PIN_SHUTDOWNPWR3V3 5 +#define PWR3V3_ON HIGH +#define PWR3V3_OFF LOW \ No newline at end of file diff --git a/components/spotify/cspot/bell/libhelix-mp3/polyphase.c b/components/spotify/cspot/bell/libhelix-mp3/polyphase.c new file mode 100644 index 00000000..bd331dfe --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/polyphase.c @@ -0,0 +1,295 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * polyphase.c - final stage of subband transform (polyphase synthesis filter) + * + * This is the C reference version using __int64 + * Look in the appropriate subdirectories for optimized asm implementations + * (e.g. arm/asmpoly.s) + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +/* input to Polyphase = Q(DQ_FRACBITS_OUT-2), gain 2 bits in convolution + * we also have the implicit bias of 2^15 to add back, so net fraction bits = + * DQ_FRACBITS_OUT - 2 - 2 - 15 + * (see comment on Dequantize() for more info) + */ +#define DEF_NFRACBITS (DQ_FRACBITS_OUT - 2 - 2 - 15) +#define CSHIFT 12 /* coefficients have 12 leading sign bits for early-terminating mulitplies */ + +static __inline short ClipToShort(int x, int fracBits) +{ + int sign; + + /* assumes you've already rounded (x += (1 << (fracBits-1))) */ + x >>= fracBits; + + /* Ken's trick: clips to [-32768, 32767] */ + sign = x >> 31; + if (sign != (x >> 15)) + x = sign ^ ((1 << 15) - 1); + + return (short)x; +} + +#define MC0M(x) { \ + c1 = *coef; coef++; c2 = *coef; coef++; \ + vLo = *(vb1+(x)); vHi = *(vb1+(23-(x))); \ + sum1L = MADD64(sum1L, vLo, c1); sum1L = MADD64(sum1L, vHi, -c2); \ +} + +#define MC1M(x) { \ + c1 = *coef; coef++; \ + vLo = *(vb1+(x)); \ + sum1L = MADD64(sum1L, vLo, c1); \ +} + +#define MC2M(x) { \ + c1 = *coef; coef++; c2 = *coef; coef++; \ + vLo = *(vb1+(x)); vHi = *(vb1+(23-(x))); \ + sum1L = MADD64(sum1L, vLo, c1); sum2L = MADD64(sum2L, vLo, c2); \ + sum1L = MADD64(sum1L, vHi, -c2); sum2L = MADD64(sum2L, vHi, c1); \ +} + +/************************************************************************************** + * Function: PolyphaseMono + * + * Description: filter one subband and produce 32 output PCM samples for one channel + * + * Inputs: pointer to PCM output buffer + * number of "extra shifts" (vbuf format = Q(DQ_FRACBITS_OUT-2)) + * pointer to start of vbuf (preserved from last call) + * start of filter coefficient table (in proper, shuffled order) + * no minimum number of guard bits is required for input vbuf + * (see additional scaling comments below) + * + * Outputs: 32 samples of one channel of decoded PCM data, (i.e. Q16.0) + * + * Return: none + * + * TODO: add 32-bit version for platforms where 64-bit mul-acc is not supported + * (note max filter gain - see polyCoef[] comments) + **************************************************************************************/ +void PolyphaseMono(short *pcm, int *vbuf, const int *coefBase) +{ + int i; + const int *coef; + int *vb1; + int vLo, vHi, c1, c2; + Word64 sum1L, sum2L, rndVal; + + rndVal = (Word64)( 1 << (DEF_NFRACBITS - 1 + (32 - CSHIFT)) ); + + /* special case, output sample 0 */ + coef = coefBase; + vb1 = vbuf; + sum1L = rndVal; + + MC0M(0) + MC0M(1) + MC0M(2) + MC0M(3) + MC0M(4) + MC0M(5) + MC0M(6) + MC0M(7) + + *(pcm + 0) = ClipToShort((int)SAR64(sum1L, (32-CSHIFT)), DEF_NFRACBITS); + + /* special case, output sample 16 */ + coef = coefBase + 256; + vb1 = vbuf + 64*16; + sum1L = rndVal; + + MC1M(0) + MC1M(1) + MC1M(2) + MC1M(3) + MC1M(4) + MC1M(5) + MC1M(6) + MC1M(7) + + *(pcm + 16) = ClipToShort((int)SAR64(sum1L, (32-CSHIFT)), DEF_NFRACBITS); + + /* main convolution loop: sum1L = samples 1, 2, 3, ... 15 sum2L = samples 31, 30, ... 17 */ + coef = coefBase + 16; + vb1 = vbuf + 64; + pcm++; + + /* right now, the compiler creates bad asm from this... */ + for (i = 15; i > 0; i--) { + sum1L = sum2L = rndVal; + + MC2M(0) + MC2M(1) + MC2M(2) + MC2M(3) + MC2M(4) + MC2M(5) + MC2M(6) + MC2M(7) + + vb1 += 64; + *(pcm) = ClipToShort((int)SAR64(sum1L, (32-CSHIFT)), DEF_NFRACBITS); + *(pcm + 2*i) = ClipToShort((int)SAR64(sum2L, (32-CSHIFT)), DEF_NFRACBITS); + pcm++; + } +} + +#define MC0S(x) { \ + c1 = *coef; coef++; c2 = *coef; coef++; \ + vLo = *(vb1+(x)); vHi = *(vb1+(23-(x))); \ + sum1L = MADD64(sum1L, vLo, c1); sum1L = MADD64(sum1L, vHi, -c2); \ + vLo = *(vb1+32+(x)); vHi = *(vb1+32+(23-(x))); \ + sum1R = MADD64(sum1R, vLo, c1); sum1R = MADD64(sum1R, vHi, -c2); \ +} + +#define MC1S(x) { \ + c1 = *coef; coef++; \ + vLo = *(vb1+(x)); \ + sum1L = MADD64(sum1L, vLo, c1); \ + vLo = *(vb1+32+(x)); \ + sum1R = MADD64(sum1R, vLo, c1); \ +} + +#define MC2S(x) { \ + c1 = *coef; coef++; c2 = *coef; coef++; \ + vLo = *(vb1+(x)); vHi = *(vb1+(23-(x))); \ + sum1L = MADD64(sum1L, vLo, c1); sum2L = MADD64(sum2L, vLo, c2); \ + sum1L = MADD64(sum1L, vHi, -c2); sum2L = MADD64(sum2L, vHi, c1); \ + vLo = *(vb1+32+(x)); vHi = *(vb1+32+(23-(x))); \ + sum1R = MADD64(sum1R, vLo, c1); sum2R = MADD64(sum2R, vLo, c2); \ + sum1R = MADD64(sum1R, vHi, -c2); sum2R = MADD64(sum2R, vHi, c1); \ +} + +/************************************************************************************** + * Function: PolyphaseStereo + * + * Description: filter one subband and produce 32 output PCM samples for each channel + * + * Inputs: pointer to PCM output buffer + * number of "extra shifts" (vbuf format = Q(DQ_FRACBITS_OUT-2)) + * pointer to start of vbuf (preserved from last call) + * start of filter coefficient table (in proper, shuffled order) + * no minimum number of guard bits is required for input vbuf + * (see additional scaling comments below) + * + * Outputs: 32 samples of two channels of decoded PCM data, (i.e. Q16.0) + * + * Return: none + * + * Notes: interleaves PCM samples LRLRLR... + * + * TODO: add 32-bit version for platforms where 64-bit mul-acc is not supported + **************************************************************************************/ +void PolyphaseStereo(short *pcm, int *vbuf, const int *coefBase) +{ + int i; + const int *coef; + int *vb1; + int vLo, vHi, c1, c2; + Word64 sum1L, sum2L, sum1R, sum2R, rndVal; + + rndVal = (Word64)( 1 << (DEF_NFRACBITS - 1 + (32 - CSHIFT)) ); + + /* special case, output sample 0 */ + coef = coefBase; + vb1 = vbuf; + sum1L = sum1R = rndVal; + + MC0S(0) + MC0S(1) + MC0S(2) + MC0S(3) + MC0S(4) + MC0S(5) + MC0S(6) + MC0S(7) + + *(pcm + 0) = ClipToShort((int)SAR64(sum1L, (32-CSHIFT)), DEF_NFRACBITS); + *(pcm + 1) = ClipToShort((int)SAR64(sum1R, (32-CSHIFT)), DEF_NFRACBITS); + + /* special case, output sample 16 */ + coef = coefBase + 256; + vb1 = vbuf + 64*16; + sum1L = sum1R = rndVal; + + MC1S(0) + MC1S(1) + MC1S(2) + MC1S(3) + MC1S(4) + MC1S(5) + MC1S(6) + MC1S(7) + + *(pcm + 2*16 + 0) = ClipToShort((int)SAR64(sum1L, (32-CSHIFT)), DEF_NFRACBITS); + *(pcm + 2*16 + 1) = ClipToShort((int)SAR64(sum1R, (32-CSHIFT)), DEF_NFRACBITS); + + /* main convolution loop: sum1L = samples 1, 2, 3, ... 15 sum2L = samples 31, 30, ... 17 */ + coef = coefBase + 16; + vb1 = vbuf + 64; + pcm += 2; + + /* right now, the compiler creates bad asm from this... */ + for (i = 15; i > 0; i--) { + sum1L = sum2L = rndVal; + sum1R = sum2R = rndVal; + + MC2S(0) + MC2S(1) + MC2S(2) + MC2S(3) + MC2S(4) + MC2S(5) + MC2S(6) + MC2S(7) + + vb1 += 64; + *(pcm + 0) = ClipToShort((int)SAR64(sum1L, (32-CSHIFT)), DEF_NFRACBITS); + *(pcm + 1) = ClipToShort((int)SAR64(sum1R, (32-CSHIFT)), DEF_NFRACBITS); + *(pcm + 2*2*i + 0) = ClipToShort((int)SAR64(sum2L, (32-CSHIFT)), DEF_NFRACBITS); + *(pcm + 2*2*i + 1) = ClipToShort((int)SAR64(sum2R, (32-CSHIFT)), DEF_NFRACBITS); + pcm += 2; + } +} diff --git a/components/spotify/cspot/bell/libhelix-mp3/scalfact.c b/components/spotify/cspot/bell/libhelix-mp3/scalfact.c new file mode 100644 index 00000000..4937e453 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/scalfact.c @@ -0,0 +1,392 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * scalfact.c - scalefactor unpacking functions + **************************************************************************************/ + +#include "coder.h" + +/* scale factor lengths (num bits) */ +static const char SFLenTab[16][2] = { + {0, 0}, {0, 1}, + {0, 2}, {0, 3}, + {3, 0}, {1, 1}, + {1, 2}, {1, 3}, + {2, 1}, {2, 2}, + {2, 3}, {3, 1}, + {3, 2}, {3, 3}, + {4, 2}, {4, 3}, +}; + +/************************************************************************************** + * Function: UnpackSFMPEG1 + * + * Description: unpack MPEG 1 scalefactors from bitstream + * + * Inputs: BitStreamInfo, SideInfoSub, ScaleFactorInfoSub structs for this + * granule/channel + * vector of scfsi flags from side info, length = 4 (MAX_SCFBD) + * index of current granule + * ScaleFactorInfoSub from granule 0 (for granule 1, if scfsi[i] is set, + * then we just replicate the scale factors from granule 0 in the + * i'th set of scalefactor bands) + * + * Outputs: updated BitStreamInfo struct + * scalefactors in sfis (short and/or long arrays, as appropriate) + * + * Return: none + * + * Notes: set order of short blocks to s[band][window] instead of s[window][band] + * so that we index through consectutive memory locations when unpacking + * (make sure dequantizer follows same convention) + * Illegal Intensity Position = 7 (always) for MPEG1 scale factors + **************************************************************************************/ +static void UnpackSFMPEG1(BitStreamInfo *bsi, SideInfoSub *sis, ScaleFactorInfoSub *sfis, int *scfsi, int gr, ScaleFactorInfoSub *sfisGr0) +{ + int sfb; + int slen0, slen1; + + /* these can be 0, so make sure GetBits(bsi, 0) returns 0 (no >> 32 or anything) */ + slen0 = (int)SFLenTab[sis->sfCompress][0]; + slen1 = (int)SFLenTab[sis->sfCompress][1]; + + if (sis->blockType == 2) { + /* short block, type 2 (implies winSwitchFlag == 1) */ + if (sis->mixedBlock) { + /* do long block portion */ + for (sfb = 0; sfb < 8; sfb++) + sfis->l[sfb] = (char)GetBits(bsi, slen0); + sfb = 3; + } else { + /* all short blocks */ + sfb = 0; + } + + for ( ; sfb < 6; sfb++) { + sfis->s[sfb][0] = (char)GetBits(bsi, slen0); + sfis->s[sfb][1] = (char)GetBits(bsi, slen0); + sfis->s[sfb][2] = (char)GetBits(bsi, slen0); + } + + for ( ; sfb < 12; sfb++) { + sfis->s[sfb][0] = (char)GetBits(bsi, slen1); + sfis->s[sfb][1] = (char)GetBits(bsi, slen1); + sfis->s[sfb][2] = (char)GetBits(bsi, slen1); + } + + /* last sf band not transmitted */ + sfis->s[12][0] = sfis->s[12][1] = sfis->s[12][2] = 0; + } else { + /* long blocks, type 0, 1, or 3 */ + if(gr == 0) { + /* first granule */ + for (sfb = 0; sfb < 11; sfb++) + sfis->l[sfb] = (char)GetBits(bsi, slen0); + for (sfb = 11; sfb < 21; sfb++) + sfis->l[sfb] = (char)GetBits(bsi, slen1); + return; + } else { + /* second granule + * scfsi: 0 = different scalefactors for each granule, 1 = copy sf's from granule 0 into granule 1 + * for block type == 2, scfsi is always 0 + */ + sfb = 0; + if(scfsi[0]) for( ; sfb < 6 ; sfb++) sfis->l[sfb] = sfisGr0->l[sfb]; + else for( ; sfb < 6 ; sfb++) sfis->l[sfb] = (char)GetBits(bsi, slen0); + if(scfsi[1]) for( ; sfb <11 ; sfb++) sfis->l[sfb] = sfisGr0->l[sfb]; + else for( ; sfb <11 ; sfb++) sfis->l[sfb] = (char)GetBits(bsi, slen0); + if(scfsi[2]) for( ; sfb <16 ; sfb++) sfis->l[sfb] = sfisGr0->l[sfb]; + else for( ; sfb <16 ; sfb++) sfis->l[sfb] = (char)GetBits(bsi, slen1); + if(scfsi[3]) for( ; sfb <21 ; sfb++) sfis->l[sfb] = sfisGr0->l[sfb]; + else for( ; sfb <21 ; sfb++) sfis->l[sfb] = (char)GetBits(bsi, slen1); + } + /* last sf band not transmitted */ + sfis->l[21] = 0; + sfis->l[22] = 0; + } +} + +/* NRTab[size + 3*is_right][block type][partition] + * block type index: 0 = (bt0,bt1,bt3), 1 = bt2 non-mixed, 2 = bt2 mixed + * partition: scale factor groups (sfb1 through sfb4) + * for block type = 2 (mixed or non-mixed) / by 3 is rolled into this table + * (for 3 short blocks per long block) + * see 2.4.3.2 in MPEG 2 (low sample rate) spec + * stuff rolled into this table: + * NRTab[x][1][y] --> (NRTab[x][1][y]) / 3 + * NRTab[x][2][>=1] --> (NRTab[x][2][>=1]) / 3 (first partition is long block) + */ +static const char NRTab[6][3][4] = { + /* non-intensity stereo */ + { {6, 5, 5, 5}, + {3, 3, 3, 3}, /* includes / 3 */ + {6, 3, 3, 3}, /* includes / 3 except for first entry */ + }, + { {6, 5, 7, 3}, + {3, 3, 4, 2}, + {6, 3, 4, 2}, + }, + { {11, 10, 0, 0}, + {6, 6, 0, 0}, + {6, 3, 6, 0}, /* spec = [15,18,0,0], but 15 = 6L + 9S, so move 9/3=3 into col 1, 18/3=6 into col 2 and adj. slen[1,2] below */ + }, + /* intensity stereo, right chan */ + { {7, 7, 7, 0}, + {4, 4, 4, 0}, + {6, 5, 4, 0}, + }, + { {6, 6, 6, 3}, + {4, 3, 3, 2}, + {6, 4, 3, 2}, + }, + { {8, 8, 5, 0}, + {5, 4, 3, 0}, + {6, 6, 3, 0}, + } +}; + +/************************************************************************************** + * Function: UnpackSFMPEG2 + * + * Description: unpack MPEG 2 scalefactors from bitstream + * + * Inputs: BitStreamInfo, SideInfoSub, ScaleFactorInfoSub structs for this + * granule/channel + * index of current granule and channel + * ScaleFactorInfoSub from this granule + * modeExt field from frame header, to tell whether intensity stereo is on + * ScaleFactorJS struct for storing IIP info used in Dequant() + * + * Outputs: updated BitStreamInfo struct + * scalefactors in sfis (short and/or long arrays, as appropriate) + * updated intensityScale and preFlag flags + * + * Return: none + * + * Notes: Illegal Intensity Position = (2^slen) - 1 for MPEG2 scale factors + * + * TODO: optimize the / and % stuff (only do one divide, get modulo x + * with (x / m) * m, etc.) + **************************************************************************************/ +static void UnpackSFMPEG2(BitStreamInfo *bsi, SideInfoSub *sis, ScaleFactorInfoSub *sfis, int gr, int ch, int modeExt, ScaleFactorJS *sfjs) +{ + + int i, sfb, sfcIdx, btIdx, nrIdx;// iipTest; + int slen[4], nr[4]; + int sfCompress, preFlag, intensityScale; + (void)gr; + + sfCompress = sis->sfCompress; + preFlag = 0; + intensityScale = 0; + + /* stereo mode bits (1 = on): bit 1 = mid-side on/off, bit 0 = intensity on/off */ + if (! ((modeExt & 0x01) && (ch == 1)) ) { + /* in other words: if ((modeExt & 0x01) == 0 || ch == 0) */ + if (sfCompress < 400) { + /* max slen = floor[(399/16) / 5] = 4 */ + slen[0] = (sfCompress >> 4) / 5; + slen[1]= (sfCompress >> 4) % 5; + slen[2]= (sfCompress & 0x0f) >> 2; + slen[3]= (sfCompress & 0x03); + sfcIdx = 0; + } else if (sfCompress < 500) { + /* max slen = floor[(99/4) / 5] = 4 */ + sfCompress -= 400; + slen[0] = (sfCompress >> 2) / 5; + slen[1]= (sfCompress >> 2) % 5; + slen[2]= (sfCompress & 0x03); + slen[3]= 0; + sfcIdx = 1; + } else { + /* max slen = floor[11/3] = 3 (sfCompress = 9 bits in MPEG2) */ + sfCompress -= 500; + slen[0] = sfCompress / 3; + slen[1] = sfCompress % 3; + slen[2] = slen[3] = 0; + if (sis->mixedBlock) { + /* adjust for long/short mix logic (see comment above in NRTab[] definition) */ + slen[2] = slen[1]; + slen[1] = slen[0]; + } + preFlag = 1; + sfcIdx = 2; + } + } else { + /* intensity stereo ch = 1 (right) */ + intensityScale = sfCompress & 0x01; + sfCompress >>= 1; + if (sfCompress < 180) { + /* max slen = floor[35/6] = 5 (from mod 36) */ + slen[0] = (sfCompress / 36); + slen[1] = (sfCompress % 36) / 6; + slen[2] = (sfCompress % 36) % 6; + slen[3] = 0; + sfcIdx = 3; + } else if (sfCompress < 244) { + /* max slen = floor[63/16] = 3 */ + sfCompress -= 180; + slen[0] = (sfCompress & 0x3f) >> 4; + slen[1] = (sfCompress & 0x0f) >> 2; + slen[2] = (sfCompress & 0x03); + slen[3] = 0; + sfcIdx = 4; + } else { + /* max slen = floor[11/3] = 3 (max sfCompress >> 1 = 511/2 = 255) */ + sfCompress -= 244; + slen[0] = (sfCompress / 3); + slen[1] = (sfCompress % 3); + slen[2] = slen[3] = 0; + sfcIdx = 5; + } + } + + /* set index based on block type: (0,1,3) --> 0, (2 non-mixed) --> 1, (2 mixed) ---> 2 */ + btIdx = 0; + if (sis->blockType == 2) + btIdx = (sis->mixedBlock ? 2 : 1); + for (i = 0; i < 4; i++) + nr[i] = (int)NRTab[sfcIdx][btIdx][i]; + + /* save intensity stereo scale factor info */ + if( (modeExt & 0x01) && (ch == 1) ) { + for (i = 0; i < 4; i++) { + sfjs->slen[i] = slen[i]; + sfjs->nr[i] = nr[i]; + } + sfjs->intensityScale = intensityScale; + } + sis->preFlag = preFlag; + + /* short blocks */ + if(sis->blockType == 2) { + if(sis->mixedBlock) { + /* do long block portion */ + //iipTest = (1 << slen[0]) - 1; + for (sfb=0; sfb < 6; sfb++) { + sfis->l[sfb] = (char)GetBits(bsi, slen[0]); + } + sfb = 3; /* start sfb for short */ + nrIdx = 1; + } else { + /* all short blocks, so start nr, sfb at 0 */ + sfb = 0; + nrIdx = 0; + } + + /* remaining short blocks, sfb just keeps incrementing */ + for ( ; nrIdx <= 3; nrIdx++) { + //iipTest = (1 << slen[nrIdx]) - 1; + for (i=0; i < nr[nrIdx]; i++, sfb++) { + sfis->s[sfb][0] = (char)GetBits(bsi, slen[nrIdx]); + sfis->s[sfb][1] = (char)GetBits(bsi, slen[nrIdx]); + sfis->s[sfb][2] = (char)GetBits(bsi, slen[nrIdx]); + } + } + /* last sf band not transmitted */ + sfis->s[12][0] = sfis->s[12][1] = sfis->s[12][2] = 0; + } else { + /* long blocks */ + sfb = 0; + for (nrIdx = 0; nrIdx <= 3; nrIdx++) { + //iipTest = (1 << slen[nrIdx]) - 1; + for(i=0; i < nr[nrIdx]; i++, sfb++) { + sfis->l[sfb] = (char)GetBits(bsi, slen[nrIdx]); + } + } + /* last sf band not transmitted */ + sfis->l[21] = sfis->l[22] = 0; + + } +} + +/************************************************************************************** + * Function: UnpackScaleFactors + * + * Description: parse the fields of the MP3 scale factor data section + * + * Inputs: MP3DecInfo structure filled by UnpackFrameHeader() and UnpackSideInfo() + * buffer pointing to the MP3 scale factor data + * pointer to bit offset (0-7) indicating starting bit in buf[0] + * number of bits available in data buffer + * index of current granule and channel + * + * Outputs: updated platform-specific ScaleFactorInfo struct + * updated bitOffset + * + * Return: length (in bytes) of scale factor data, -1 if null input pointers + **************************************************************************************/ +int UnpackScaleFactors(MP3DecInfo *mp3DecInfo, unsigned char *buf, int *bitOffset, int bitsAvail, int gr, int ch) +{ + int bitsUsed; + unsigned char *startBuf; + BitStreamInfo bitStreamInfo, *bsi; + FrameHeader *fh; + SideInfo *si; + ScaleFactorInfo *sfi; + + /* validate pointers */ + if (!mp3DecInfo || !mp3DecInfo->FrameHeaderPS || !mp3DecInfo->SideInfoPS || !mp3DecInfo->ScaleFactorInfoPS) + return -1; + fh = ((FrameHeader *)(mp3DecInfo->FrameHeaderPS)); + si = ((SideInfo *)(mp3DecInfo->SideInfoPS)); + sfi = ((ScaleFactorInfo *)(mp3DecInfo->ScaleFactorInfoPS)); + + /* init GetBits reader */ + startBuf = buf; + bsi = &bitStreamInfo; + SetBitstreamPointer(bsi, (bitsAvail + *bitOffset + 7) / 8, buf); + if (*bitOffset) + GetBits(bsi, *bitOffset); + + if (fh->ver == MPEG1) + UnpackSFMPEG1(bsi, &si->sis[gr][ch], &sfi->sfis[gr][ch], si->scfsi[ch], gr, &sfi->sfis[0][ch]); + else + UnpackSFMPEG2(bsi, &si->sis[gr][ch], &sfi->sfis[gr][ch], gr, ch, fh->modeExt, &sfi->sfjs); + + mp3DecInfo->part23Length[gr][ch] = si->sis[gr][ch].part23Length; + + bitsUsed = CalcBitsUsed(bsi, buf, *bitOffset); + buf += (bitsUsed + *bitOffset) >> 3; + *bitOffset = (bitsUsed + *bitOffset) & 0x07; + + return (buf - startBuf); +} + diff --git a/components/spotify/cspot/bell/libhelix-mp3/statname.h b/components/spotify/cspot/bell/libhelix-mp3/statname.h new file mode 100644 index 00000000..c7f985ea --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/statname.h @@ -0,0 +1,89 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * statname.h - name mangling macros for static linking + **************************************************************************************/ + +#ifndef _STATNAME_H +#define _STATNAME_H + +/* define STAT_PREFIX to a unique name for static linking + * all the C functions and global variables will be mangled by the preprocessor + * e.g. void FFT(int *fftbuf) becomes void cook_FFT(int *fftbuf) + */ +#define STAT_PREFIX xmp3 + + +#define STATCC1(x,y,z) STATCC2(x,y,z) +#define STATCC2(x,y,z) x##y##z + +#ifdef STAT_PREFIX +#define STATNAME(func) STATCC1(STAT_PREFIX, _, func) +#else +#define STATNAME(func) func +#endif + +/* these symbols are common to all implementations */ +#define CheckPadBit STATNAME(CheckPadBit) +#define UnpackFrameHeader STATNAME(UnpackFrameHeader) +#define UnpackSideInfo STATNAME(UnpackSideInfo) +#define AllocateBuffers STATNAME(AllocateBuffers) +#define FreeBuffers STATNAME(FreeBuffers) +#define DecodeHuffman STATNAME(DecodeHuffman) +#define Dequantize STATNAME(Dequantize) +#define IMDCT STATNAME(IMDCT) +#define UnpackScaleFactors STATNAME(UnpackScaleFactors) +#define Subband STATNAME(Subband) + +#define samplerateTab STATNAME(samplerateTab) +#define bitrateTab STATNAME(bitrateTab) +#define samplesPerFrameTab STATNAME(samplesPerFrameTab) +#define bitsPerSlotTab STATNAME(bitsPerSlotTab) +#define sideBytesTab STATNAME(sideBytesTab) +#define slotTab STATNAME(slotTab) +#define sfBandTable STATNAME(sfBandTable) + +/* in your implementation's top-level include file (e.g. real\coder.h) you should + * add new #define sym STATNAME(sym) lines for all the + * additional global functions or variables which your + * implementation uses + */ + +#endif /* _STATNAME_H */ diff --git a/components/spotify/cspot/bell/libhelix-mp3/stproc.c b/components/spotify/cspot/bell/libhelix-mp3/stproc.c new file mode 100644 index 00000000..7782a21b --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/stproc.c @@ -0,0 +1,299 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * stproc.c - mid-side and intensity (MPEG1 and MPEG2) stereo processing + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +/************************************************************************************** + * Function: MidSideProc + * + * Description: sum-difference stereo reconstruction + * + * Inputs: vector x with dequantized samples from left and right channels + * number of non-zero samples (MAX of left and right) + * assume 1 guard bit in input + * guard bit mask (left and right channels) + * + * Outputs: updated sample vector x + * updated guard bit mask + * + * Return: none + * + * Notes: assume at least 1 GB in input + **************************************************************************************/ +void MidSideProc(int x[MAX_NCHAN][MAX_NSAMP], int nSamps, int mOut[2]) +{ + int i, xr, xl, mOutL, mOutR; + + /* L = (M+S)/sqrt(2), R = (M-S)/sqrt(2) + * NOTE: 1/sqrt(2) done in DequantChannel() - see comments there + */ + mOutL = mOutR = 0; + for(i = 0; i < nSamps; i++) { + xl = x[0][i]; + xr = x[1][i]; + x[0][i] = xl + xr; + x[1][i] = xl - xr; + mOutL |= FASTABS(x[0][i]); + mOutR |= FASTABS(x[1][i]); + } + mOut[0] |= mOutL; + mOut[1] |= mOutR; +} + +/************************************************************************************** + * Function: IntensityProcMPEG1 + * + * Description: intensity stereo processing for MPEG1 + * + * Inputs: vector x with dequantized samples from left and right channels + * number of non-zero samples in left channel + * valid FrameHeader struct + * two each of ScaleFactorInfoSub, CriticalBandInfo structs (both channels) + * flags indicating midSide on/off, mixedBlock on/off + * guard bit mask (left and right channels) + * + * Outputs: updated sample vector x + * updated guard bit mask + * + * Return: none + * + * Notes: assume at least 1 GB in input + * + * TODO: combine MPEG1/2 into one function (maybe) + * make sure all the mixed-block and IIP logic is right + **************************************************************************************/ +void IntensityProcMPEG1(int x[MAX_NCHAN][MAX_NSAMP], int nSamps, FrameHeader *fh, ScaleFactorInfoSub *sfis, + CriticalBandInfo *cbi, int midSideFlag, int mixFlag, int mOut[2]) +{ + int i=0, j=0, n=0, cb=0, w=0; + int sampsLeft, isf, mOutL, mOutR, xl, xr; + int fl, fr, fls[3], frs[3]; + int cbStartL=0, cbStartS=0, cbEndL=0, cbEndS=0; + int *isfTab; + (void)mixFlag; + + /* NOTE - this works fine for mixed blocks, as long as the switch point starts in the + * short block section (i.e. on or after sample 36 = sfBand->l[8] = 3*sfBand->s[3] + * is this a safe assumption? + * TODO - intensity + mixed not quite right (diff = 11 on he_mode) + * figure out correct implementation (spec ambiguous about when to do short block reorder) + */ + if (cbi[1].cbType == 0) { + /* long block */ + cbStartL = cbi[1].cbEndL + 1; + cbEndL = cbi[0].cbEndL + 1; + cbStartS = cbEndS = 0; + i = fh->sfBand->l[cbStartL]; + } else if (cbi[1].cbType == 1 || cbi[1].cbType == 2) { + /* short or mixed block */ + cbStartS = cbi[1].cbEndSMax + 1; + cbEndS = cbi[0].cbEndSMax + 1; + cbStartL = cbEndL = 0; + i = 3 * fh->sfBand->s[cbStartS]; + } + + sampsLeft = nSamps - i; /* process to length of left */ + isfTab = (int *)ISFMpeg1[midSideFlag]; + mOutL = mOutR = 0; + + /* long blocks */ + for (cb = cbStartL; cb < cbEndL && sampsLeft > 0; cb++) { + isf = sfis->l[cb]; + if (isf == 7) { + fl = ISFIIP[midSideFlag][0]; + fr = ISFIIP[midSideFlag][1]; + } else { + fl = isfTab[isf]; + fr = isfTab[6] - isfTab[isf]; + } + + n = fh->sfBand->l[cb + 1] - fh->sfBand->l[cb]; + for (j = 0; j < n && sampsLeft > 0; j++, i++) { + xr = MULSHIFT32(fr, x[0][i]) << 2; x[1][i] = xr; mOutR |= FASTABS(xr); + xl = MULSHIFT32(fl, x[0][i]) << 2; x[0][i] = xl; mOutL |= FASTABS(xl); + sampsLeft--; + } + } + + /* short blocks */ + for (cb = cbStartS; cb < cbEndS && sampsLeft >= 3; cb++) { + for (w = 0; w < 3; w++) { + isf = sfis->s[cb][w]; + if (isf == 7) { + fls[w] = ISFIIP[midSideFlag][0]; + frs[w] = ISFIIP[midSideFlag][1]; + } else { + fls[w] = isfTab[isf]; + frs[w] = isfTab[6] - isfTab[isf]; + } + } + + n = fh->sfBand->s[cb + 1] - fh->sfBand->s[cb]; + for (j = 0; j < n && sampsLeft >= 3; j++, i+=3) { + xr = MULSHIFT32(frs[0], x[0][i+0]) << 2; x[1][i+0] = xr; mOutR |= FASTABS(xr); + xl = MULSHIFT32(fls[0], x[0][i+0]) << 2; x[0][i+0] = xl; mOutL |= FASTABS(xl); + xr = MULSHIFT32(frs[1], x[0][i+1]) << 2; x[1][i+1] = xr; mOutR |= FASTABS(xr); + xl = MULSHIFT32(fls[1], x[0][i+1]) << 2; x[0][i+1] = xl; mOutL |= FASTABS(xl); + xr = MULSHIFT32(frs[2], x[0][i+2]) << 2; x[1][i+2] = xr; mOutR |= FASTABS(xr); + xl = MULSHIFT32(fls[2], x[0][i+2]) << 2; x[0][i+2] = xl; mOutL |= FASTABS(xl); + sampsLeft -= 3; + } + } + mOut[0] = mOutL; + mOut[1] = mOutR; + + return; +} + +/************************************************************************************** + * Function: IntensityProcMPEG2 + * + * Description: intensity stereo processing for MPEG2 + * + * Inputs: vector x with dequantized samples from left and right channels + * number of non-zero samples in left channel + * valid FrameHeader struct + * two each of ScaleFactorInfoSub, CriticalBandInfo structs (both channels) + * ScaleFactorJS struct with joint stereo info from UnpackSFMPEG2() + * flags indicating midSide on/off, mixedBlock on/off + * guard bit mask (left and right channels) + * + * Outputs: updated sample vector x + * updated guard bit mask + * + * Return: none + * + * Notes: assume at least 1 GB in input + * + * TODO: combine MPEG1/2 into one function (maybe) + * make sure all the mixed-block and IIP logic is right + * probably redo IIP logic to be simpler + **************************************************************************************/ +void IntensityProcMPEG2(int x[MAX_NCHAN][MAX_NSAMP], int nSamps, FrameHeader *fh, ScaleFactorInfoSub *sfis, + CriticalBandInfo *cbi, ScaleFactorJS *sfjs, int midSideFlag, int mixFlag, int mOut[2]) +{ + int i, j, k, n, r, cb, w; + int fl, fr, mOutL, mOutR, xl, xr; + int sampsLeft; + int isf, sfIdx, tmp, il[23]; + int *isfTab; + int cbStartL, cbStartS, cbEndL, cbEndS; + + (void)mixFlag; + + isfTab = (int *)ISFMpeg2[sfjs->intensityScale][midSideFlag]; + mOutL = mOutR = 0; + + /* fill buffer with illegal intensity positions (depending on slen) */ + for (k = r = 0; r < 4; r++) { + tmp = (1 << sfjs->slen[r]) - 1; + for (j = 0; j < sfjs->nr[r]; j++, k++) + il[k] = tmp; + } + + if (cbi[1].cbType == 0) { + /* long blocks */ + il[21] = il[22] = 1; + cbStartL = cbi[1].cbEndL + 1; /* start at end of right */ + cbEndL = cbi[0].cbEndL + 1; /* process to end of left */ + i = fh->sfBand->l[cbStartL]; + sampsLeft = nSamps - i; + + for(cb = cbStartL; cb < cbEndL; cb++) { + sfIdx = sfis->l[cb]; + if (sfIdx == il[cb]) { + fl = ISFIIP[midSideFlag][0]; + fr = ISFIIP[midSideFlag][1]; + } else { + isf = (sfis->l[cb] + 1) >> 1; + fl = isfTab[(sfIdx & 0x01 ? isf : 0)]; + fr = isfTab[(sfIdx & 0x01 ? 0 : isf)]; + } + n = MIN(fh->sfBand->l[cb + 1] - fh->sfBand->l[cb], sampsLeft); + + for(j = 0; j < n; j++, i++) { + xr = MULSHIFT32(fr, x[0][i]) << 2; x[1][i] = xr; mOutR |= FASTABS(xr); + xl = MULSHIFT32(fl, x[0][i]) << 2; x[0][i] = xl; mOutL |= FASTABS(xl); + } + + /* early exit once we've used all the non-zero samples */ + sampsLeft -= n; + if (sampsLeft == 0) + break; + } + } else { + /* short or mixed blocks */ + il[12] = 1; + + for(w = 0; w < 3; w++) { + cbStartS = cbi[1].cbEndS[w] + 1; /* start at end of right */ + cbEndS = cbi[0].cbEndS[w] + 1; /* process to end of left */ + i = 3 * fh->sfBand->s[cbStartS] + w; + + /* skip through sample array by 3, so early-exit logic would be more tricky */ + for(cb = cbStartS; cb < cbEndS; cb++) { + sfIdx = sfis->s[cb][w]; + if (sfIdx == il[cb]) { + fl = ISFIIP[midSideFlag][0]; + fr = ISFIIP[midSideFlag][1]; + } else { + isf = (sfis->s[cb][w] + 1) >> 1; + fl = isfTab[(sfIdx & 0x01 ? isf : 0)]; + fr = isfTab[(sfIdx & 0x01 ? 0 : isf)]; + } + n = fh->sfBand->s[cb + 1] - fh->sfBand->s[cb]; + + for(j = 0; j < n; j++, i+=3) { + xr = MULSHIFT32(fr, x[0][i]) << 2; x[1][i] = xr; mOutR |= FASTABS(xr); + xl = MULSHIFT32(fl, x[0][i]) << 2; x[0][i] = xl; mOutL |= FASTABS(xl); + } + } + } + } + mOut[0] = mOutL; + mOut[1] = mOutR; + + return; +} + diff --git a/components/spotify/cspot/bell/libhelix-mp3/subband.c b/components/spotify/cspot/bell/libhelix-mp3/subband.c new file mode 100644 index 00000000..e982a9fe --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/subband.c @@ -0,0 +1,96 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * subband.c - subband transform (synthesis filterbank implemented via 32-point DCT + * followed by polyphase filter) + **************************************************************************************/ + +#include "coder.h" +#include "assembly.h" + +/************************************************************************************** + * Function: Subband + * + * Description: do subband transform on all the blocks in one granule, all channels + * + * Inputs: filled MP3DecInfo structure, after calling IMDCT for all channels + * vbuf[ch] and vindex[ch] must be preserved between calls + * + * Outputs: decoded PCM data, interleaved LRLRLR... if stereo + * + * Return: 0 on success, -1 if null input pointers + **************************************************************************************/ +/*__attribute__ ((section (".data"))) */ int Subband(MP3DecInfo *mp3DecInfo, short *pcmBuf) +{ + int b; + //HuffmanInfo *hi; + IMDCTInfo *mi; + SubbandInfo *sbi; + + /* validate pointers */ + if (!mp3DecInfo || !mp3DecInfo->HuffmanInfoPS || !mp3DecInfo->IMDCTInfoPS || !mp3DecInfo->SubbandInfoPS) + return -1; + + //hi = (HuffmanInfo *)mp3DecInfo->HuffmanInfoPS; + mi = (IMDCTInfo *)(mp3DecInfo->IMDCTInfoPS); + sbi = (SubbandInfo*)(mp3DecInfo->SubbandInfoPS); + + if (mp3DecInfo->nChans == 2) { + /* stereo */ + for (b = 0; b < BLOCK_SIZE; b++) { + FDCT32(mi->outBuf[0][b], sbi->vbuf + 0*32, sbi->vindex, (b & 0x01), mi->gb[0]); + FDCT32(mi->outBuf[1][b], sbi->vbuf + 1*32, sbi->vindex, (b & 0x01), mi->gb[1]); + PolyphaseStereo(pcmBuf, sbi->vbuf + sbi->vindex + VBUF_LENGTH * (b & 0x01), polyCoef); + sbi->vindex = (sbi->vindex - (b & 0x01)) & 7; + pcmBuf += (2 * NBANDS); + } + } else { + /* mono */ + for (b = 0; b < BLOCK_SIZE; b++) { + FDCT32(mi->outBuf[0][b], sbi->vbuf + 0*32, sbi->vindex, (b & 0x01), mi->gb[0]); + PolyphaseMono(pcmBuf, sbi->vbuf + sbi->vindex + VBUF_LENGTH * (b & 0x01), polyCoef); + sbi->vindex = (sbi->vindex - (b & 0x01)) & 7; + pcmBuf += NBANDS; + } + } + + return 0; +} + diff --git a/components/spotify/cspot/bell/libhelix-mp3/trigtabs.c b/components/spotify/cspot/bell/libhelix-mp3/trigtabs.c new file mode 100644 index 00000000..8686e816 --- /dev/null +++ b/components/spotify/cspot/bell/libhelix-mp3/trigtabs.c @@ -0,0 +1,319 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +/************************************************************************************** + * Fixed-point MP3 decoder + * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com) + * June 2003 + * + * trigtabs.c - global ROM tables for pre-calculated trig coefficients + **************************************************************************************/ + +// constants in RAM are not significantly faster + +#include "coder.h" +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnarrowing" + +/* post-IMDCT window, win[blockType][i] + * format = Q31 + * Fused sin window with final stage of IMDCT + * includes 1/sqrt(2) scaling, since we scale by sqrt(2) in dequant in order + * for fast IMDCT36 to be usable + * + * for(i=0;i<9;i++) win[0][i] = sin(pi/36 *(i+0.5)); + * for(i=9;i<36;i++) win[0][i] = -sin(pi/36 *(i+0.5)); + * + * for(i=0;i<9;i++) win[1][i] = sin(pi/36 *(i+0.5)); + * for(i=9;i<18;i++) win[1][i] = -sin(pi/36 *(i+0.5)); + * for(i=18;i<24;i++) win[1][i] = -1; + * for(i=24;i<30;i++) win[1][i] = -sin(pi/12 *(i+0.5-18)); + * for(i=30;i<36;i++) win[1][i] = 0; + * + * for(i=0;i<6;i++) win[3][i] = 0; + * for(i=6;i<9;i++) win[3][i] = sin(pi/12 *(i+0.5-6)); + * for(i=9;i<12;i++) win[3][i] = -sin(pi/12 *(i+0.5-6)); + * for(i=12;i<18;i++) win[3][i] = -1; + * for(i=18;i<36;i++) win[3][i] = -sin(pi/36*(i+0.5)); + * + * for(i=0;i<3;i++) win[2][i] = sin(pi/12*(i+0.5)); + * for(i=3;i<12;i++) win[2][i] = -sin(pi/12*(i+0.5)); + * for(i=12;i<36;i++) win[2][i] = 0; + * + * for (i = 0; i < 4; i++) { + * if (i == 2) { + * win[i][8] *= cos(pi/12 * (0+0.5)); + * win[i][9] *= cos(pi/12 * (0+0.5)); + * win[i][7] *= cos(pi/12 * (1+0.5)); + * win[i][10] *= cos(pi/12 * (1+0.5)); + * win[i][6] *= cos(pi/12 * (2+0.5)); + * win[i][11] *= cos(pi/12 * (2+0.5)); + * win[i][0] *= cos(pi/12 * (3+0.5)); + * win[i][5] *= cos(pi/12 * (3+0.5)); + * win[i][1] *= cos(pi/12 * (4+0.5)); + * win[i][4] *= cos(pi/12 * (4+0.5)); + * win[i][2] *= cos(pi/12 * (5+0.5)); + * win[i][3] *= cos(pi/12 * (5+0.5)); + * } else { + * for (j = 0; j < 9; j++) { + * win[i][8-j] *= cos(pi/36 * (17-j+0.5)); + * win[i][9+j] *= cos(pi/36 * (17-j+0.5)); + * } + * for (j = 0; j < 9; j++) { + * win[i][18+8-j] *= cos(pi/36 * (j+0.5)); + * win[i][18+9+j] *= cos(pi/36 * (j+0.5)); + * } + * } + * } + * for (i = 0; i < 4; i++) + * for (j = 0; j < 36; j++) + * win[i][j] *= 1.0 / sqrt(2); + */ + +const int imdctWin[4][36] PROGMEM = { + { + 0x02aace8b, 0x07311c28, 0x0a868fec, 0x0c913b52, 0x0d413ccd, 0x0c913b52, 0x0a868fec, 0x07311c28, + 0x02aace8b, 0xfd16d8dd, 0xf6a09e66, 0xef7a6275, 0xe7dbc161, 0xe0000000, 0xd8243e9f, 0xd0859d8b, + 0xc95f619a, 0xc2e92723, 0xbd553175, 0xb8cee3d8, 0xb5797014, 0xb36ec4ae, 0xb2bec333, 0xb36ec4ae, + 0xb5797014, 0xb8cee3d8, 0xbd553175, 0xc2e92723, 0xc95f619a, 0xd0859d8b, 0xd8243e9f, 0xe0000000, + 0xe7dbc161, 0xef7a6275, 0xf6a09e66, 0xfd16d8dd, + }, + { + 0x02aace8b, 0x07311c28, 0x0a868fec, 0x0c913b52, 0x0d413ccd, 0x0c913b52, 0x0a868fec, 0x07311c28, + 0x02aace8b, 0xfd16d8dd, 0xf6a09e66, 0xef7a6275, 0xe7dbc161, 0xe0000000, 0xd8243e9f, 0xd0859d8b, + 0xc95f619a, 0xc2e92723, 0xbd44ef14, 0xb831a052, 0xb3aa3837, 0xafb789a4, 0xac6145bb, 0xa9adecdc, + 0xa864491f, 0xad1868f0, 0xb8431f49, 0xc8f42236, 0xdda8e6b1, 0xf47755dc, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + }, + { + 0x07311c28, 0x0d413ccd, 0x07311c28, 0xf6a09e66, 0xe0000000, 0xc95f619a, 0xb8cee3d8, 0xb2bec333, + 0xb8cee3d8, 0xc95f619a, 0xe0000000, 0xf6a09e66, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + }, + { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x028e9709, 0x04855ec0, + 0x026743a1, 0xfcde2c10, 0xf515dc82, 0xec93e53b, 0xe4c880f8, 0xdd5d0b08, 0xd63510b7, 0xcf5e834a, + 0xc8e6b562, 0xc2da4105, 0xbd553175, 0xb8cee3d8, 0xb5797014, 0xb36ec4ae, 0xb2bec333, 0xb36ec4ae, + 0xb5797014, 0xb8cee3d8, 0xbd553175, 0xc2e92723, 0xc95f619a, 0xd0859d8b, 0xd8243e9f, 0xe0000000, + 0xe7dbc161, 0xef7a6275, 0xf6a09e66, 0xfd16d8dd, + }, +}; + +/* indexing = [mid-side off/on][intensity scale factor] + * format = Q30, range = [0.0, 1.414] + * + * mid-side off: + * ISFMpeg1[0][i] = tan(i*pi/12) / [1 + tan(i*pi/12)] (left scalefactor) + * = 1 / [1 + tan(i*pi/12)] (right scalefactor) + * + * mid-side on: + * ISFMpeg1[1][i] = sqrt(2) * ISFMpeg1[0][i] + * + * output L = ISFMpeg1[midSide][isf][0] * input L + * output R = ISFMpeg1[midSide][isf][1] * input L + * + * obviously left scalefactor + right scalefactor = 1 (m-s off) or sqrt(2) (m-s on) + * so just store left and calculate right as 1 - left + * (can derive as right = ISFMpeg1[x][6] - left) + * + * if mid-side enabled, multiply joint stereo scale factors by sqrt(2) + * - we scaled whole spectrum by 1/sqrt(2) in Dequant for the M+S/sqrt(2) in MidSideProc + * - but the joint stereo part of the spectrum doesn't need this, so we have to undo it + * + * if scale factor is and illegal intensity position, this becomes a passthrough + * - gain = [1, 0] if mid-side off, since L is coded directly and R = 0 in this region + * - gain = [1, 1] if mid-side on, since L = (M+S)/sqrt(2), R = (M-S)/sqrt(2) + * - and since S = 0 in the joint stereo region (above NZB right) then L = R = M * 1.0 + */ +const int ISFMpeg1[2][7] PROGMEM = { + {0x00000000, 0x0d8658ba, 0x176cf5d0, 0x20000000, 0x28930a2f, 0x3279a745, 0x40000000}, + {0x00000000, 0x13207f5c, 0x2120fb83, 0x2d413ccc, 0x39617e16, 0x4761fa3d, 0x5a827999} +}; + +/* indexing = [intensity scale on/off][mid-side off/on][intensity scale factor] + * format = Q30, range = [0.0, 1.414] + * + * if (isf == 0) kl = 1.0 kr = 1.0 + * else if (isf & 0x01 == 0x01) kl = i0^((isf+1)/2), kr = 1.0 + * else if (isf & 0x01 == 0x00) kl = 1.0, kr = i0^(isf/2) + * + * if (intensityScale == 1) i0 = 1/sqrt(2) = 0x2d413ccc (Q30) + * else i0 = 1/sqrt(sqrt(2)) = 0x35d13f32 (Q30) + * + * see comments for ISFMpeg1 (just above) regarding scaling, sqrt(2), etc. + * + * compress the MPEG2 table using the obvious identities above... + * for isf = [0, 1, 2, ... 30], let sf = table[(isf+1) >> 1] + * - if isf odd, L = sf*L, R = tab[0]*R + * - if isf even, L = tab[0]*L, R = sf*R + */ +const int ISFMpeg2[2][2][16] PROGMEM = { +{ + { + /* intensityScale off, mid-side off */ + 0x40000000, 0x35d13f32, 0x2d413ccc, 0x260dfc14, 0x1fffffff, 0x1ae89f99, 0x16a09e66, 0x1306fe0a, + 0x0fffffff, 0x0d744fcc, 0x0b504f33, 0x09837f05, 0x07ffffff, 0x06ba27e6, 0x05a82799, 0x04c1bf82, + }, + { + /* intensityScale off, mid-side on */ + 0x5a827999, 0x4c1bf827, 0x3fffffff, 0x35d13f32, 0x2d413ccc, 0x260dfc13, 0x1fffffff, 0x1ae89f99, + 0x16a09e66, 0x1306fe09, 0x0fffffff, 0x0d744fcc, 0x0b504f33, 0x09837f04, 0x07ffffff, 0x06ba27e6, + }, +}, +{ + { + /* intensityScale on, mid-side off */ + 0x40000000, 0x2d413ccc, 0x20000000, 0x16a09e66, 0x10000000, 0x0b504f33, 0x08000000, 0x05a82799, + 0x04000000, 0x02d413cc, 0x02000000, 0x016a09e6, 0x01000000, 0x00b504f3, 0x00800000, 0x005a8279, + }, + /* intensityScale on, mid-side on */ + { + 0x5a827999, 0x3fffffff, 0x2d413ccc, 0x1fffffff, 0x16a09e66, 0x0fffffff, 0x0b504f33, 0x07ffffff, + 0x05a82799, 0x03ffffff, 0x02d413cc, 0x01ffffff, 0x016a09e6, 0x00ffffff, 0x00b504f3, 0x007fffff, + } +} +}; + +/* indexing = [intensity scale on/off][left/right] + * format = Q30, range = [0.0, 1.414] + * + * illegal intensity position scalefactors (see comments on ISFMpeg1) + */ +const int ISFIIP[2][2] PROGMEM = { + {0x40000000, 0x00000000}, /* mid-side off */ + {0x40000000, 0x40000000}, /* mid-side on */ +}; + +const unsigned char uniqueIDTab[8] = {0x5f, 0x4b, 0x43, 0x5f, 0x5f, 0x4a, 0x52, 0x5f}; + +/* anti-alias coefficients - see spec Annex B, table 3-B.9 + * csa[0][i] = CSi, csa[1][i] = CAi + * format = Q31 + */ +const int csa[8][2] PROGMEM = { + {0x6dc253f0, 0xbe2500aa}, + {0x70dcebe4, 0xc39e4949}, + {0x798d6e73, 0xd7e33f4a}, + {0x7ddd40a7, 0xe8b71176}, + {0x7f6d20b7, 0xf3e4fe2f}, + {0x7fe47e40, 0xfac1a3c7}, + {0x7ffcb263, 0xfe2ebdc6}, + {0x7fffc694, 0xff86c25d}, +}; + +/* format = Q30, range = [0.0981, 1.9976] + * + * n = 16; + * k = 0; + * for(i=0; i<5; i++, n=n/2) { + * for(p=0; p + +unsigned char bell::BaseHTTPServer::h2int(char c) +{ + if (c >= '0' && c <= '9') + { + return ((unsigned char)c - '0'); + } + if (c >= 'a' && c <= 'f') + { + return ((unsigned char)c - 'a' + 10); + } + if (c >= 'A' && c <= 'F') + { + return ((unsigned char)c - 'A' + 10); + } + return (0); +} + +std::string bell::BaseHTTPServer::urlDecode(std::string str) +{ + std::string encodedString = ""; + char c; + char code0; + char code1; + for (int i = 0; i < str.length(); i++) + { + c = str[i]; + if (c == '+') + { + encodedString += ' '; + } + else if (c == '%') + { + i++; + code0 = str[i]; + i++; + code1 = str[i]; + c = (h2int(code0) << 4) | h2int(code1); + encodedString += c; + } + else + { + + encodedString += c; + } + } + + return encodedString; +} + +std::vector bell::BaseHTTPServer::splitUrl(const std::string &url, char delimiter) +{ + std::stringstream ssb(url); + std::string segment; + std::vector seglist; + + while (std::getline(ssb, segment, delimiter)) + { + seglist.push_back(segment); + } + return seglist; +} + +std::map bell::BaseHTTPServer::parseQueryString(const std::string &queryString) +{ + std::map query; + auto prefixedString = "&" + queryString; + while (prefixedString.find("&") != std::string::npos) + { + auto keyStart = prefixedString.find("&"); + auto keyEnd = prefixedString.find("="); + // Find second occurence of "&" in prefixedString + auto valueEnd = prefixedString.find("&", keyStart + 1); + if (valueEnd == std::string::npos) + { + valueEnd = prefixedString.size(); + } + + auto key = prefixedString.substr(keyStart + 1, keyEnd - 1); + auto value = prefixedString.substr(keyEnd + 1, valueEnd - keyEnd - 1); + query[key] = urlDecode(value); + prefixedString = prefixedString.substr(valueEnd); + } + + return query; +} \ No newline at end of file diff --git a/components/spotify/cspot/bell/src/BellLogger.cpp b/components/spotify/cspot/bell/src/BellLogger.cpp new file mode 100644 index 00000000..1023d575 --- /dev/null +++ b/components/spotify/cspot/bell/src/BellLogger.cpp @@ -0,0 +1,11 @@ +#include "BellLogger.h" + +std::shared_ptr bell::bellGlobalLogger; + +void bell::setDefaultLogger() { + bell::bellGlobalLogger = std::make_shared(); +} + +void bell::enableSubmoduleLogging() { + bell::bellGlobalLogger->enableSubmodule = true; +} \ No newline at end of file diff --git a/components/spotify/cspot/bell/src/BellUtils.cpp b/components/spotify/cspot/bell/src/BellUtils.cpp new file mode 100644 index 00000000..f3be492c --- /dev/null +++ b/components/spotify/cspot/bell/src/BellUtils.cpp @@ -0,0 +1,20 @@ +#include "BellUtils.h" + +std::string bell::generateRandomUUID() { + static std::random_device dev; + static std::mt19937 rng(dev()); + + std::uniform_int_distribution dist(0, 15); + + const char *v = "0123456789abcdef"; + const bool dash[] = {0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0}; + + std::string res; + for (int i = 0; i < 16; i++) { + if (dash[i]) + res += "-"; + res += v[dist(rng)]; + res += v[dist(rng)]; + } + return res; +} diff --git a/components/spotify/cspot/bell/src/BinaryReader.cpp b/components/spotify/cspot/bell/src/BinaryReader.cpp new file mode 100644 index 00000000..cf5e8760 --- /dev/null +++ b/components/spotify/cspot/bell/src/BinaryReader.cpp @@ -0,0 +1,69 @@ +#include "BinaryReader.h" +#include + +bell::BinaryReader::BinaryReader(std::shared_ptr stream) { + this->stream = stream; +} + +size_t bell::BinaryReader::position() { + return stream->position(); +} + +size_t bell::BinaryReader::size() { + return stream->size(); +} + +void bell::BinaryReader::close() { + stream->close(); +} + +void bell::BinaryReader::skip(size_t pos) { + uint8_t b[pos]; + stream->read((uint8_t *)b, pos); +} + +int32_t bell::BinaryReader::readInt() { + uint8_t b[4]; + stream->read((uint8_t *) b,4); + + return static_cast( + (b[3]) | + (b[2] << 8) | + (b[1] << 16)| + (b[0] << 24) ); +} + +int16_t bell::BinaryReader::readShort() { + uint8_t b[2]; + stream->read((uint8_t *) b,2); + + return static_cast( + (b[1]) | + (b[1] << 8)); +} + + +uint32_t bell::BinaryReader::readUInt() { + return readInt() & 0xffffffffL; +} + +uint8_t bell::BinaryReader::readByte() { + uint8_t b[1]; + stream->read((uint8_t *) b,1); + return b[0]; +} + +std::vector bell::BinaryReader::readBytes(size_t size) { + std::vector data(size); + stream->read(&data[0], size); + return data; +} + +long long bell::BinaryReader::readLong() { + long high = readInt(); + long low = readInt(); + + return static_cast( + (high << 32) | low ); +} + diff --git a/components/spotify/cspot/bell/src/CryptoMBedTLS.cpp b/components/spotify/cspot/bell/src/CryptoMBedTLS.cpp new file mode 100644 index 00000000..f52c9913 --- /dev/null +++ b/components/spotify/cspot/bell/src/CryptoMBedTLS.cpp @@ -0,0 +1,227 @@ +#ifdef BELL_USE_MBEDTLS +#include "CryptoMbedTLS.h" + +CryptoMbedTLS::CryptoMbedTLS() +{ + mbedtls_aes_init(&aesCtx); +} + +CryptoMbedTLS::~CryptoMbedTLS() +{ + mbedtls_aes_free(&aesCtx); +} + +std::vector CryptoMbedTLS::base64Decode(const std::string &data) +{ + // Calculate max decode length + size_t requiredSize; + + mbedtls_base64_encode(nullptr, 0, &requiredSize, (unsigned char *)data.c_str(), data.size()); + + std::vector output(requiredSize); + size_t outputLen = 0; + mbedtls_base64_decode(output.data(), requiredSize, &outputLen, (unsigned char *)data.c_str(), data.size()); + + return std::vector(output.begin(), output.begin() + outputLen); +} + +std::string CryptoMbedTLS::base64Encode(const std::vector &data) +{ + // Calculate max output length + size_t requiredSize; + mbedtls_base64_encode(nullptr, 0, &requiredSize, data.data(), data.size()); + + std::vector output(requiredSize); + size_t outputLen = 0; + + mbedtls_base64_encode(output.data(), requiredSize, &outputLen, data.data(), data.size()); + + return std::string(output.begin(), output.begin() + outputLen); +} + +// Sha1 +void CryptoMbedTLS::sha1Init() +{ + // Init mbedtls md context, pick sha1 + mbedtls_md_init(&sha1Context); + mbedtls_md_setup(&sha1Context, mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), 1); + mbedtls_md_starts(&sha1Context); +} + +void CryptoMbedTLS::sha1Update(const std::string &s) +{ + sha1Update(std::vector(s.begin(), s.end())); +} +void CryptoMbedTLS::sha1Update(const std::vector &vec) +{ + mbedtls_md_update(&sha1Context, vec.data(), vec.size()); +} + +std::vector CryptoMbedTLS::sha1FinalBytes() +{ + std::vector digest(20); // SHA1 digest size + + mbedtls_md_finish(&sha1Context, digest.data()); + mbedtls_md_free(&sha1Context); + + return digest; +} + +std::string CryptoMbedTLS::sha1Final() +{ + auto digest = sha1FinalBytes(); + return std::string(digest.begin(), digest.end()); +} + +// HMAC SHA1 +std::vector CryptoMbedTLS::sha1HMAC(const std::vector &inputKey, const std::vector &message) +{ + std::vector digest(20); // SHA1 digest size + + sha1Init(); + mbedtls_md_hmac_starts(&sha1Context, inputKey.data(), inputKey.size()); + mbedtls_md_hmac_update(&sha1Context, message.data(), message.size()); + mbedtls_md_hmac_finish(&sha1Context, digest.data()); + mbedtls_md_free(&sha1Context); + + return digest; +} + +// AES CTR +void CryptoMbedTLS::aesCTRXcrypt(const std::vector &key, std::vector &iv, std::vector &data) +{ + // needed for internal cache + size_t off = 0; + unsigned char streamBlock[16] = {0}; + + // set IV + mbedtls_aes_setkey_enc(&aesCtx, key.data(), key.size() * 8); + + // Perform decrypt + mbedtls_aes_crypt_ctr(&aesCtx, + data.size(), + &off, + iv.data(), + streamBlock, + data.data(), + data.data()); +} + +void CryptoMbedTLS::aesECBdecrypt(const std::vector &key, std::vector &data) +{ + // Set 192bit key + mbedtls_aes_setkey_dec(&aesCtx, key.data(), key.size() * 8); + + // Mbedtls's decrypt only works on 16 byte blocks + for (unsigned int x = 0; x < data.size() / 16; x++) + { + // Perform decrypt + mbedtls_aes_crypt_ecb(&aesCtx, + MBEDTLS_AES_DECRYPT, + data.data() + (x * 16), + data.data() + (x * 16)); + } +} + +// PBKDF2 +std::vector CryptoMbedTLS::pbkdf2HmacSha1(const std::vector &password, const std::vector &salt, int iterations, int digestSize) +{ + auto digest = std::vector(digestSize); + + // Init sha context + sha1Init(); + mbedtls_pkcs5_pbkdf2_hmac(&sha1Context, password.data(), password.size(), salt.data(), salt.size(), iterations, digestSize, digest.data()); + + // Free sha context + mbedtls_md_free(&sha1Context); + + return digest; +} + +void CryptoMbedTLS::dhInit() +{ + privateKey = generateVectorWithRandomData(DH_KEY_SIZE); + + // initialize big num + mbedtls_mpi prime, generator, res, privKey; + mbedtls_mpi_init(&prime); + mbedtls_mpi_init(&generator); + mbedtls_mpi_init(&privKey); + mbedtls_mpi_init(&res); + + // Read bin into big num mpi + mbedtls_mpi_read_binary(&prime, DHPrime, sizeof(DHPrime)); + mbedtls_mpi_read_binary(&generator, DHGenerator, sizeof(DHGenerator)); + mbedtls_mpi_read_binary(&privKey, privateKey.data(), DH_KEY_SIZE); + + // perform diffie hellman G^X mod P + mbedtls_mpi_exp_mod(&res, &generator, &privKey, &prime, NULL); + + // Write generated public key to vector + this->publicKey = std::vector(DH_KEY_SIZE); + mbedtls_mpi_write_binary(&res, publicKey.data(), DH_KEY_SIZE); + + // Release memory + mbedtls_mpi_free(&prime); + mbedtls_mpi_free(&generator); + mbedtls_mpi_free(&privKey); + //mbedtls_mpi_free(&res); +} + +std::vector CryptoMbedTLS::dhCalculateShared(const std::vector &remoteKey) +{ + // initialize big num + mbedtls_mpi prime, remKey, res, privKey; + mbedtls_mpi_init(&prime); + mbedtls_mpi_init(&remKey); + mbedtls_mpi_init(&privKey); + mbedtls_mpi_init(&res); + + // Read bin into big num mpi + mbedtls_mpi_read_binary(&prime, DHPrime, sizeof(DHPrime)); + mbedtls_mpi_read_binary(&remKey, remoteKey.data(), remoteKey.size()); + mbedtls_mpi_read_binary(&privKey, privateKey.data(), DH_KEY_SIZE); + + // perform diffie hellman (G^Y)^X mod P (for shared secret) + mbedtls_mpi_exp_mod(&res, &remKey, &privKey, &prime, NULL); + + auto sharedKey = std::vector(DH_KEY_SIZE); + mbedtls_mpi_write_binary(&res, sharedKey.data(), DH_KEY_SIZE); + + // Release memory + mbedtls_mpi_free(&prime); + mbedtls_mpi_free(&remKey); + mbedtls_mpi_free(&privKey); + mbedtls_mpi_free(&res); + + return sharedKey; +} + +// Random stuff +std::vector CryptoMbedTLS::generateVectorWithRandomData(size_t length) +{ + std::vector randomVector(length); + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctrDrbg; + // Personification string + const char *pers = "cspotGen"; + + // init entropy and random num generator + mbedtls_entropy_init(&entropy); + mbedtls_ctr_drbg_init(&ctrDrbg); + + // Seed the generator + mbedtls_ctr_drbg_seed(&ctrDrbg, mbedtls_entropy_func, &entropy, + (const unsigned char *)pers, + 7); + + // Generate random bytes + mbedtls_ctr_drbg_random(&ctrDrbg, randomVector.data(), length); + + // Release memory + mbedtls_entropy_free(&entropy); + mbedtls_ctr_drbg_free(&ctrDrbg); + + return randomVector; +} +#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/src/CryptoOpenSSL.cpp b/components/spotify/cspot/bell/src/CryptoOpenSSL.cpp new file mode 100644 index 00000000..2f52bf60 --- /dev/null +++ b/components/spotify/cspot/bell/src/CryptoOpenSSL.cpp @@ -0,0 +1,183 @@ +#include "CryptoOpenSSL.h" +namespace +{ + struct BIOFreeAll + { + void operator()(BIO *p) { BIO_free_all(p); } + }; +} // namespace + +CryptoOpenSSL::CryptoOpenSSL() +{ + // OpenSSL init + ENGINE_load_builtin_engines(); + ENGINE_register_all_complete(); + this->publicKey = std::vector(DH_KEY_SIZE); + this->privateKey = generateVectorWithRandomData(DH_KEY_SIZE); +} + +CryptoOpenSSL::~CryptoOpenSSL() +{ + if (this->dhContext != nullptr) + { + DH_free(this->dhContext); + } +} + +std::vector CryptoOpenSSL::base64Decode(const std::string& data) +{ + // base64 in openssl is an absolute mess lmao + std::unique_ptr b64(BIO_new(BIO_f_base64())); + BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL); + BIO *source = BIO_new_mem_buf(data.c_str(), -1); // read-only source + BIO_push(b64.get(), source); + const int maxlen = data.size() / 4 * 3 + 1; + std::vector decoded(maxlen); + const int len = BIO_read(b64.get(), decoded.data(), maxlen); + decoded.resize(len); + return decoded; +} + +std::string CryptoOpenSSL::base64Encode(const std::vector& data) +{ + // base64 in openssl is an absolute mess lmao x 2 + std::unique_ptr b64(BIO_new(BIO_f_base64())); + + // No newline mode, put all the data into sink + BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL); + BIO *sink = BIO_new(BIO_s_mem()); + BIO_push(b64.get(), sink); + BIO_write(b64.get(), data.data(), data.size()); + BIO_flush(b64.get()); + const char *encoded; + const long len = BIO_get_mem_data(sink, &encoded); + return std::string(encoded, len); +} + +// Sha1 +void CryptoOpenSSL::sha1Init() +{ + SHA1_Init(&sha1Context); +} + +void CryptoOpenSSL::sha1Update(const std::string& s) +{ + sha1Update(std::vector(s.begin(), s.end())); +} +void CryptoOpenSSL::sha1Update(const std::vector& vec) +{ + SHA1_Update(&sha1Context, vec.data(), vec.size()); +} + +std::vector CryptoOpenSSL::sha1FinalBytes() +{ + std::vector digest(20); // 20 is 160 bits + SHA1_Final(digest.data(), &sha1Context); + return digest; +} + +std::string CryptoOpenSSL::sha1Final() +{ + auto digest = sha1FinalBytes(); + return std::string(digest.begin(), digest.end()); +} + +// HMAC SHA1 +std::vector CryptoOpenSSL::sha1HMAC(const std::vector& inputKey, const std::vector& message) +{ + std::vector digest(20); // 20 is 160 bits + auto hmacContext = HMAC_CTX_new(); + HMAC_Init_ex(hmacContext, inputKey.data(), inputKey.size(), EVP_sha1(), NULL); + HMAC_Update(hmacContext, message.data(), message.size()); + + unsigned int resLen = 0; + HMAC_Final(hmacContext, digest.data(), &resLen); + + HMAC_CTX_free(hmacContext); + + return digest; +} + +// AES CTR +void CryptoOpenSSL::aesCTRXcrypt(const std::vector& key, std::vector& iv, std::vector &data) +{ + // Prepare AES_KEY + auto cryptoKey = AES_KEY(); + AES_set_encrypt_key(key.data(), 128, &cryptoKey); + + // Needed for openssl internal cache + unsigned char ecountBuf[16] = {0}; + unsigned int offsetInBlock = 0; + + CRYPTO_ctr128_encrypt( + data.data(), + data.data(), + data.size(), + &cryptoKey, + iv.data(), + ecountBuf, + &offsetInBlock, + block128_f(AES_encrypt)); +} + +void CryptoOpenSSL::aesECBdecrypt(const std::vector& key, std::vector& data) +{ + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + EVP_CIPHER_CTX_init(ctx); + int len = 0; + + EVP_DecryptInit_ex(ctx, EVP_aes_192_ecb(), NULL, key.data(), NULL); + EVP_DecryptUpdate(ctx, data.data(), &len, data.data(), data.size()); + EVP_DecryptFinal_ex(ctx, data.data() + len, &len); + + EVP_CIPHER_CTX_free(ctx); +} + +// PBKDF2 +std::vector CryptoOpenSSL::pbkdf2HmacSha1(const std::vector& password, const std::vector& salt, int iterations, int digestSize) +{ + std::vector digest(digestSize); + + // Generate PKDF2 digest + PKCS5_PBKDF2_HMAC_SHA1((const char *)password.data(), password.size(), + (const unsigned char *)salt.data(), salt.size(), iterations, + digestSize, digest.data()); + return digest; +} + +void CryptoOpenSSL::dhInit() +{ + // Free old context + if (this->dhContext != nullptr) + { + DH_free(this->dhContext); + } + this->dhContext = DH_new(); + + // Set prime and the generator + DH_set0_pqg(this->dhContext, BN_bin2bn(DHPrime, DH_KEY_SIZE, NULL), NULL, BN_bin2bn(DHGenerator, 1, NULL)); + + // Generate public and private keys and copy them to vectors + DH_generate_key(this->dhContext); + BN_bn2bin(DH_get0_pub_key(dhContext), this->publicKey.data()); +} + +std::vector CryptoOpenSSL::dhCalculateShared(const std::vector& remoteKey) +{ + auto sharedKey = std::vector(DH_KEY_SIZE); + // Convert remote key to bignum and compute shared key + auto pubKey = BN_bin2bn(&remoteKey[0], DH_KEY_SIZE, NULL); + DH_compute_key(sharedKey.data(), pubKey, this->dhContext); + BN_free(pubKey); + return sharedKey; +} + +// Random stuff +std::vector CryptoOpenSSL::generateVectorWithRandomData(size_t length) +{ + std::vector randomVec(length); + if(RAND_bytes(randomVec.data(), length) == 0) + { + } + return randomVec; +} diff --git a/components/spotify/cspot/bell/src/DecoderGlobals.cpp b/components/spotify/cspot/bell/src/DecoderGlobals.cpp new file mode 100644 index 00000000..5ecc03fa --- /dev/null +++ b/components/spotify/cspot/bell/src/DecoderGlobals.cpp @@ -0,0 +1,8 @@ +#include "DecoderGlobals.h" + +std::shared_ptr bell::decodersInstance; + +void bell::createDecoders() +{ + bell::decodersInstance = std::make_shared(); +} \ No newline at end of file diff --git a/components/spotify/cspot/bell/src/HTTPServer.cpp b/components/spotify/cspot/bell/src/HTTPServer.cpp new file mode 100644 index 00000000..c4d5ad4f --- /dev/null +++ b/components/spotify/cspot/bell/src/HTTPServer.cpp @@ -0,0 +1,469 @@ +#include "HTTPServer.h" + +bell::HTTPServer::HTTPServer(int serverPort) +{ + this->serverPort = serverPort; +} + +unsigned char bell::HTTPServer::h2int(char c) +{ + if (c >= '0' && c <= '9') + { + return ((unsigned char)c - '0'); + } + if (c >= 'a' && c <= 'f') + { + return ((unsigned char)c - 'a' + 10); + } + if (c >= 'A' && c <= 'F') + { + return ((unsigned char)c - 'A' + 10); + } + return (0); +} + +std::string bell::HTTPServer::urlDecode(std::string str) +{ + std::string encodedString = ""; + char c; + char code0; + char code1; + for (int i = 0; i < str.length(); i++) + { + c = str[i]; + if (c == '+') + { + encodedString += ' '; + } + else if (c == '%') + { + i++; + code0 = str[i]; + i++; + code1 = str[i]; + c = (h2int(code0) << 4) | h2int(code1); + encodedString += c; + } + else + { + + encodedString += c; + } + } + + return encodedString; +} + +std::vector bell::HTTPServer::splitUrl(const std::string &url, char delimiter) +{ + std::stringstream ssb(url); + std::string segment; + std::vector seglist; + + while (std::getline(ssb, segment, delimiter)) + { + seglist.push_back(segment); + } + return seglist; +} + +void bell::HTTPServer::registerHandler(RequestType requestType, const std::string &routeUrl, httpHandler handler) +{ + if (routes.find(routeUrl) == routes.end()) + { + routes.insert({routeUrl, std::vector()}); + } + this->routes[routeUrl].push_back(HTTPRoute{ + .requestType = requestType, + .handler = handler, + }); +} + +void bell::HTTPServer::listen() +{ + BELL_LOG(info, "http", "Starting configuration server at port %d", this->serverPort); + + // setup address + struct addrinfo hints, *server; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + getaddrinfo(NULL, std::to_string(serverPort).c_str(), &hints, &server); + + int sockfd = socket(server->ai_family, + server->ai_socktype, server->ai_protocol); + struct sockaddr_in clientname; + socklen_t incomingSockSize; + int i; + int yes = true; + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); + bind(sockfd, server->ai_addr, server->ai_addrlen); + ::listen(sockfd, 10); + + FD_ZERO(&activeFdSet); + FD_SET(sockfd, &activeFdSet); + + for (;;) + { + /* Block until input arrives on one or more active sockets. */ + readFdSet = activeFdSet; + if (select(FD_SETSIZE, &readFdSet, NULL, NULL, NULL) < 0) + { + BELL_LOG(error, "http", "Error in select"); + perror("select"); + // exit(EXIT_FAILURE); + } + + /* Service all the sockets with input pending. */ + for (i = 0; i < FD_SETSIZE; ++i) + if (FD_ISSET(i, &readFdSet)) + { + if (i == sockfd) + { + /* Connection request on original socket. */ + int newFd; + incomingSockSize = sizeof(clientname); + newFd = accept(sockfd, (struct sockaddr *)&clientname, &incomingSockSize); + if (newFd < 0) + { + perror("accept"); + exit(EXIT_FAILURE); + } + + FD_SET(newFd, &activeFdSet); + + HTTPConnection conn = { + .buffer = std::vector(128), + .httpMethod = ""}; + + this->connections.insert({newFd, conn}); + } + else + { + /* Data arriving on an already-connected socket. */ + readFromClient(i); + } + } + + for (auto it = this->connections.cbegin(); it != this->connections.cend() /* not hoisted */; /* no increment */) + { + if ((*it).second.toBeClosed) + { + close((*it).first); + FD_CLR((*it).first, &activeFdSet); + this->connections.erase(it++); // or "it = m.erase(it)" since C++11 + } + else + { + ++it; + } + } + } +} + +void bell::HTTPServer::readFromClient(int clientFd) +{ + HTTPConnection &conn = this->connections[clientFd]; + + int nbytes = recv(clientFd, &conn.buffer[0], conn.buffer.size(), 0); + if (nbytes < 0) + { + BELL_LOG(error, "http", "Error reading from client"); + perror("recv"); + this->closeConnection(clientFd); + } + else if (nbytes == 0) + { + this->closeConnection(clientFd); + } + else + { + conn.currentLine += std::string(conn.buffer.data(), conn.buffer.data() + nbytes); + READBODY: + if (!conn.isReadingBody) + { + while (conn.currentLine.find("\r\n") != std::string::npos) + { + auto line = conn.currentLine.substr(0, conn.currentLine.find("\r\n")); + conn.currentLine = conn.currentLine.substr(conn.currentLine.find("\r\n") + 2, conn.currentLine.size()); + if (line.find("GET ") != std::string::npos || line.find("POST ") != std::string::npos || line.find("OPTIONS ") != std::string::npos) + { + conn.httpMethod = line; + } + + if (line.find("Content-Length: ") != std::string::npos) + { + conn.contentLength = std::stoi(line.substr(16, line.size() - 1)); + BELL_LOG(info, "http", "Content-Length: %d", conn.contentLength); + } + if (line.size() == 0) + { + if (conn.contentLength != 0) + { + conn.isReadingBody = true; + goto READBODY; + } + else + { + findAndHandleRoute(conn.httpMethod, conn.currentLine, clientFd); + } + } + } + } + else + { + if (conn.currentLine.size() >= conn.contentLength) + { + findAndHandleRoute(conn.httpMethod, conn.currentLine, clientFd); + } + } + } +} + +void bell::HTTPServer::closeConnection(int connection) +{ + + this->connections[connection].toBeClosed = true; +} + +void bell::HTTPServer::writeResponseEvents(int connFd) +{ + std::lock_guard lock(this->responseMutex); + + std::stringstream stream; + stream << "HTTP/1.1 200 OK\r\n"; + stream << "Server: EUPHONIUM\r\n"; + stream << "Connection: keep-alive\r\n"; + stream << "Content-type: text/event-stream\r\n"; + stream << "Cache-Control: no-cache\r\n"; + stream << "Access-Control-Allow-Origin: *\r\n"; + stream << "Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS\r\n"; + stream << "Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token\r\n"; + stream << "\r\n"; + + auto responseStr = stream.str(); + + write(connFd, responseStr.c_str(), responseStr.size()); + this->connections[connFd].isEventConnection = true; +} + +void bell::HTTPServer::writeResponse(const HTTPResponse &response) +{ + std::lock_guard lock(this->responseMutex); + + auto fileSize = response.body.size(); + + if (response.responseReader != nullptr) + { + fileSize = response.responseReader->getTotalSize(); + } + + std::stringstream stream; + stream << "HTTP/1.1 " << response.status << " OK\r\n"; + stream << "Server: EUPHONIUM\r\n"; + stream << "Connection: close\r\n"; + stream << "Content-type: " << response.contentType << "\r\n"; + + if (response.useGzip) + { + stream << "Content-encoding: gzip" + << "\r\n"; + } + + stream << "Content-length:" << fileSize << "\r\n"; + stream << "Access-Control-Allow-Origin: *\r\n"; + stream << "Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS\r\n"; + stream << "Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token\r\n"; + stream << "\r\n"; + + if (response.body.size() > 0) + { + stream << response.body; + } + + auto responseStr = stream.str(); + + write(response.connectionFd, responseStr.c_str(), responseStr.size()); + + if (response.responseReader != nullptr) + { + size_t read; + do + { + read = response.responseReader->read(responseBuffer.data(), responseBuffer.size()); + if (read > 0) + { + write(response.connectionFd, responseBuffer.data(), read); + } + } while (read > 0); + } + this->closeConnection(response.connectionFd); +} + +void bell::HTTPServer::respond(const HTTPResponse &response) +{ + writeResponse(response); +} + +void bell::HTTPServer::publishEvent(std::string eventName, std::string eventData) +{ + std::lock_guard lock(this->responseMutex); + BELL_LOG(info, "http", "Publishing event"); + + std::stringstream stream; + stream << "event: " << eventName << "\n"; + stream << "data: " << eventData << "\n\n"; + auto responseStr = stream.str(); + + // Reply to all event-connections + for (auto it = this->connections.cbegin(); it != this->connections.cend(); ++it) + { + if ((*it).second.isEventConnection) + { + write(it->first, responseStr.c_str(), responseStr.size()); + } + } +} + +std::map bell::HTTPServer::parseQueryString(const std::string &queryString) +{ + std::map query; + auto prefixedString = "&" + queryString; + while (prefixedString.find("&") != std::string::npos) + { + auto keyStart = prefixedString.find("&"); + auto keyEnd = prefixedString.find("="); + // Find second occurence of "&" in prefixedString + auto valueEnd = prefixedString.find("&", keyStart + 1); + if (valueEnd == std::string::npos) + { + valueEnd = prefixedString.size(); + } + + auto key = prefixedString.substr(keyStart + 1, keyEnd - 1); + auto value = prefixedString.substr(keyEnd + 1, valueEnd - keyEnd - 1); + query[key] = urlDecode(value); + prefixedString = prefixedString.substr(valueEnd); + } + + return query; +} + +void bell::HTTPServer::findAndHandleRoute(std::string &url, std::string &body, int connectionFd) +{ + std::map pathParams; + std::map queryParams; + + if (url.find("OPTIONS /") != std::string::npos) + { + std::stringstream stream; + stream << "HTTP/1.1 200 OK\r\n"; + stream << "Server: EUPHONIUM\r\n"; + stream << "Allow: OPTIONS, GET, HEAD, POST\r\n"; + stream << "Connection: close\r\n"; + stream << "Access-Control-Allow-Origin: *\r\n"; + stream << "Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n"; + stream << "Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token\r\n"; + stream << "\r\n"; + + auto responseStr = stream.str(); + + write(connectionFd, responseStr.c_str(), responseStr.size()); + closeConnection(connectionFd); + return; + } + + if (url.find("GET /events") != std::string::npos) + { + // Handle SSE endpoint here + writeResponseEvents(connectionFd); + return; + } + + for (const auto &routeSet : this->routes) + { + for (const auto &route : routeSet.second) + { + + std::string path = url; + if (url.find("GET ") != std::string::npos && route.requestType == RequestType::GET) + { + path = path.substr(4); + } + else if (url.find("POST ") != std::string::npos && route.requestType == RequestType::POST) + { + path = path.substr(5); + } + else + { + continue; + } + + path = path.substr(0, path.find(" ")); + + if (path.find("?") != std::string::npos) + { + auto urlEncodedSplit = splitUrl(path, '?'); + path = urlEncodedSplit[0]; + queryParams = this->parseQueryString(urlEncodedSplit[1]); + } + + auto routeSplit = splitUrl(routeSet.first, '/'); + auto urlSplit = splitUrl(path, '/'); + bool matches = true; + + pathParams.clear(); + + if (routeSplit.size() == urlSplit.size()) + { + for (int x = 0; x < routeSplit.size(); x++) + { + if (routeSplit[x] != urlSplit[x]) + { + if (routeSplit[x][0] == ':') + { + pathParams.insert({routeSplit[x].substr(1), urlSplit[x]}); + } + else + { + matches = false; + } + } + } + } + else + { + matches = false; + } + + if (routeSplit.back().find("*") != std::string::npos && urlSplit[1] == routeSplit[1]) + { + matches = true; + } + + if (matches) + { + if (body.find("&") != std::string::npos) + { + queryParams = this->parseQueryString(body); + } + + HTTPRequest req = { + .urlParams = pathParams, + .queryParams = queryParams, + .body = body, + .handlerId = 0, + .connection = connectionFd}; + + route.handler(req); + return; + } + } + } + writeResponse(HTTPResponse{ + .connectionFd = connectionFd, + .status = 404, + .body = "Not found", + }); +} diff --git a/components/spotify/cspot/bell/src/HTTPStream.cpp b/components/spotify/cspot/bell/src/HTTPStream.cpp new file mode 100644 index 00000000..ebda808b --- /dev/null +++ b/components/spotify/cspot/bell/src/HTTPStream.cpp @@ -0,0 +1,186 @@ +#include "HTTPStream.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +bell::HTTPStream::HTTPStream() +{ +} + +bell::HTTPStream::~HTTPStream() +{ + close(); +} + +void bell::HTTPStream::close() +{ + if (status != StreamStatus::CLOSED) + { + status = StreamStatus::CLOSED; + BELL_LOG(info, "httpStream", "Closing socket"); + socket->close(); + BELL_LOG(info, "httpStream", "Closed socket"); + } +} + +void bell::HTTPStream::connectToUrl(std::string url, bool disableSSL) +{ + std::string portString; + // check if url contains "https" + if (url.find("https") != std::string::npos && !disableSSL) + { + socket = std::make_unique(); + portString = "443"; + } + else + { + socket = std::make_unique(); + portString = "80"; + } + + socket->open(url); + + // remove https or http from url + url.erase(0, url.find("://") + 3); + + // split by first "/" in url + std::string hostUrl = url.substr(0, url.find('/')); + std::string pathUrl = url.substr(url.find('/')); + + // check if hostUrl contains ':' + if (hostUrl.find(':') != std::string::npos) + { + // split by ':' + std::string host = hostUrl.substr(0, hostUrl.find(':')); + portString = hostUrl.substr(hostUrl.find(':') + 1); + hostUrl = host; + } + + // Prepare HTTP get header + std::stringstream ss; + ss << "GET " << pathUrl << " HTTP/1.1\r\n" + << "Host: " << hostUrl << ":" << portString << "\r\n" + << "Accept: */*\r\n" + << "\r\n\r\n"; + + std::string request = ss.str(); + + // Send the request + if (socket->write((uint8_t*)request.c_str(), request.length()) != (int)request.length()) + { + close(); + BELL_LOG(error, "http", "Can't send request"); + throw std::runtime_error("Resolve failed"); + } + + status = StreamStatus::READING_HEADERS; + auto buffer = std::vector(128); + auto currentLine = std::string(); + auto statusOkay = false; + auto readingData = false; + // Read data on socket sockFd line after line + int nbytes; + + while (status == StreamStatus::READING_HEADERS) + { + nbytes = socket->read(&buffer[0], buffer.size()); + if (nbytes < 0) + { + BELL_LOG(error, "http", "Error reading from client"); + perror("recv"); + exit(EXIT_FAILURE); + } + else if (nbytes == 0) + { + BELL_LOG(error, "http", "Client disconnected"); + close(); + } + else + { + currentLine += std::string(buffer.data(), buffer.data() + nbytes); + while (currentLine.find("\r\n") != std::string::npos) + { + auto line = currentLine.substr(0, currentLine.find("\r\n")); + currentLine = currentLine.substr(currentLine.find("\r\n") + 2, currentLine.size()); + BELL_LOG(info, "http", "Line: %s", line.c_str()); + + // handle redirects: + if (line.find("Location:") != std::string::npos) + { + auto newUrl = line.substr(10); + BELL_LOG(info, "http", "Redirecting to %s", newUrl.c_str()); + + close(); + return connectToUrl(newUrl); + } + // handle content-length + if (line.find("Content-Length:") != std::string::npos) + { + auto contentLengthStr = line.substr(16); + BELL_LOG(info, "http", "Content size %s", contentLengthStr.c_str()); + + // convert contentLengthStr to size_t + this->contentLength = std::stoi(contentLengthStr); + hasFixedSize = true; + } + else if (line.find("200 OK") != std::string::npos) + { + statusOkay = true; + } + else if (line.size() == 0 && statusOkay) + { + BELL_LOG(info, "http", "Ready to receive data!"); + status = StreamStatus::READING_DATA; + } + } + } + } +} + +size_t bell::HTTPStream::read(uint8_t *buf, size_t nbytes) +{ + if (status != StreamStatus::READING_DATA) + { + BELL_LOG(error, "http", "Not ready to read data"); + return 0; + } + + int nread = socket->read(buf, nbytes); + if (nread < 0) + { + BELL_LOG(error, "http", "Error reading from client"); + close(); + + perror("recv"); + exit(EXIT_FAILURE); + } + + if (this->hasFixedSize) + { + this->currentPos += nread; + } + + if (nread < nbytes) + { + return read(buf + nread, nbytes - nread); + } + return nread; +} + +size_t bell::HTTPStream::skip(size_t nbytes) +{ + return 0; +} diff --git a/components/spotify/cspot/bell/src/JSONObject.cpp b/components/spotify/cspot/bell/src/JSONObject.cpp new file mode 100644 index 00000000..8e303a28 --- /dev/null +++ b/components/spotify/cspot/bell/src/JSONObject.cpp @@ -0,0 +1,45 @@ +#include "JSONObject.h" +#include + +bell::JSONValue::JSONValue(cJSON *body, std::string key) +{ + this->body = body; + this->key = key; +} + +void bell::JSONValue::operator=(const std::string val) +{ + this->operator=(val.c_str()); +} +void bell::JSONValue::operator=(const char *val) +{ + cJSON_AddStringToObject(this->body, this->key.c_str(), val); +} +void bell::JSONValue::operator=(int val) +{ + cJSON_AddNumberToObject(this->body, this->key.c_str(), val); +} + +bell::JSONObject::JSONObject() +{ + this->body = cJSON_CreateObject(); +} + +bell::JSONObject::~JSONObject() +{ + cJSON_Delete(this->body); +} + +bell::JSONValue bell::JSONObject::operator[](std::string index) +{ + return bell::JSONValue(this->body, index); +} + +std::string bell::JSONObject::toString() +{ + char *body = cJSON_Print(this->body); + std::string retVal = std::string(body); + free(body); + return retVal; + +} \ No newline at end of file diff --git a/components/spotify/cspot/bell/src/MpegDashDemuxer.cpp b/components/spotify/cspot/bell/src/MpegDashDemuxer.cpp new file mode 100644 index 00000000..4e389d10 --- /dev/null +++ b/components/spotify/cspot/bell/src/MpegDashDemuxer.cpp @@ -0,0 +1,712 @@ +#include "MpegDashDemuxer.h" + +using namespace bell::mpeg; + +MpegDashDemuxer::MpegDashDemuxer(std::shared_ptr stream) +{ + this->reader = std::make_shared(stream); +} + +int readIntFromVector(std::vector c, size_t offset) +{ + + return ((c[0] << 24) | (c[1] << 16) | (c[2] << 8) | (c[3])) & 0xffffffffL; +} + +void MpegDashDemuxer::close() { + reader->close(); +} +void MpegDashDemuxer::parse() +{ + // Skip FTYP box + lastBox = readBox(); + ensureBox(lastBox); + + auto moov = std::make_unique(); + + while (lastBox->type != ATOM_MOOF) + { + ensureBox(lastBox); + lastBox = readBox(); + if (lastBox->type == ATOM_MOOV) + { + // parse moov + moov = parseMoov(lastBox); + } + } + + tracks = std::vector(moov->trak.size()); + + for (int i = 0; i < tracks.size(); i++) + { + tracks[i] = Mp4Track(); + tracks[i].trak = std::move(moov->trak[i]); + } +} + +int MpegDashDemuxer::parseMfhd() +{ + reader->skip(4); + return reader->readInt(); +} + +long MpegDashDemuxer::parseTfdt() +{ + uint8_t version = reader->readByte(); + reader->skip(3); + return version == 0 ? reader->readUInt() : reader->readLong(); +} + +std::unique_ptr MpegDashDemuxer::getNextSample(std::unique_ptr &chunk) +{ + auto sample = std::make_unique(); + if (chunk->size == 0) + { + return nullptr; + } + + if (chunk->i >= chunk->moof->traf->trun->entryCount) + { + return nullptr; + } + + sample->info = getAbsoluteTrunEntry(chunk->moof->traf->trun, chunk->i++, chunk->moof->traf->tfhd); + sample->data = reader->readBytes(sample->info->sampleSize); + chunk->sampleRead += sample->info->sampleSize; + + return sample; +} + +std::unique_ptr MpegDashDemuxer::getNextChunk(bool infoOnly) +{ + + while (reader->position() < reader->size()) + { + if (chunkZero) + { + ensureBox(lastBox); + lastBox = readBox(); + } + else + { + chunkZero = true; + } + + if (lastBox->type == ATOM_MOOF) + { + lastMoof = parseMoof(lastBox, tracks[0].trak->tkhd->trackId); + + if (lastMoof->traf != nullptr) + { + if (hasFlag(lastMoof->traf->trun->bFlags, 0x0001)) + { + + lastMoof->traf->trun->dataOffset -= lastBox->size + 8; + } + } + + if (lastMoof->traf->trun->chunkSize < 1) + { + if (hasFlag(lastMoof->traf->tfhd->bFlags, 0x10)) + { + lastMoof->traf->trun->chunkSize = lastMoof->traf->tfhd->defaultSampleSize * lastMoof->traf->trun->entryCount; + } + else + { + lastMoof->traf->trun->chunkSize = lastBox->size = 8; + } + } + + if (!hasFlag(lastMoof->traf->trun->bFlags, 0x900) && lastMoof->traf->trun->chunkDuration == 0) + { + if (hasFlag(lastMoof->traf->tfhd->bFlags, 0x20)) + { + lastMoof->traf->trun->chunkDuration = lastMoof->traf->tfhd->defaultSampleDuration * lastMoof->traf->trun->entryCount; + } + } + } + + if (lastBox->type == ATOM_MDAT) + { + if (lastMoof->traf == nullptr) + { + lastMoof = nullptr; + continue; + } + + auto chunk = std::make_unique(); + chunk->moof = std::move(lastMoof); + if (!infoOnly) + { + chunk->size = chunk->moof->traf->trun->chunkSize; + } + + lastMoof = nullptr; + reader->skip(chunk->moof->traf->trun->dataOffset); + return chunk; + } + } + + return std::unique_ptr(nullptr); +} + +size_t MpegDashDemuxer::position() { + return reader->position(); +} + +std::unique_ptr MpegDashDemuxer::parseMoof(std::unique_ptr &ref, int trackId) +{ + auto moof = std::make_unique(); + auto box = readBox(); + moof->mfgdSequenceNumber = parseMfhd(); + ensureBox(box); + + box = untilBox(ref, ATOM_TRAF); + while (box) + { + moof->traf = parseTraf(box, trackId); + ensureBox(box); + + if (moof->traf->tfdt != -1) + { + return moof; + } + box = untilBox(ref, ATOM_TRAF); + } + + return moof; +} + +std::unique_ptr MpegDashDemuxer::parseTraf(std::unique_ptr &ref, int trackId) +{ + auto traf = std::make_unique(); + auto box = readBox(); + traf->tfhd = parseTfhd(trackId); + ensureBox(box); + + box = untilBox(ref, ATOM_TRUN, ATOM_TFDT); + + if (box->type == ATOM_TFDT) + { + traf->tfdt = parseTfdt(); + ensureBox(box); + box = readBox(); + } + + traf->trun = parseTrun(); + ensureBox(box); + + return traf; +} + +std::unique_ptr MpegDashDemuxer::parseTrun() +{ + auto trun = std::make_unique(); + trun->bFlags = reader->readInt(); + trun->entryCount = reader->readInt(); + + trun->entriesRowSize = 0; + + if (hasFlag(trun->bFlags, 0x0100)) + { + trun->entriesRowSize += 4; + } + if (hasFlag(trun->bFlags, 0x0200)) + { + trun->entriesRowSize += 4; + } + if (hasFlag(trun->bFlags, 0x0400)) + { + trun->entriesRowSize += 4; + } + if (hasFlag(trun->bFlags, 0x0800)) + { + trun->entriesRowSize += 4; + } + + if (hasFlag(trun->bFlags, 0x0001)) + { + trun->dataOffset = reader->readInt(); + } + + if (hasFlag(trun->bFlags, 0x0004)) + { + trun->bFirstSampleFlags = reader->readInt(); + } + + trun->bEntries = reader->readBytes(trun->entriesRowSize * trun->entryCount); + trun->chunkSize = 0; + + for (int i = 0; i < trun->entryCount; i++) + { + auto entry = getTrunEntry(trun, i); + if (hasFlag(trun->bFlags, 0x0100)) + { + trun->chunkDuration += entry->sampleDuration; + } + + if (hasFlag(trun->bFlags, 0x200)) + { + + trun->chunkSize += entry->sampleSize; + } + + if (hasFlag(trun->bFlags, 0x0800)) + { + if (!hasFlag(trun->bFlags, 0x0100)) + { + trun->chunkDuration += entry->sampleCompositionTimeOffset; + } + } + } + + return trun; +} + +std::unique_ptr MpegDashDemuxer::getTrunEntry(std::unique_ptr &trun, int i) +{ + std::vector subBuffer( + trun->bEntries.begin() + (i * trun->entriesRowSize), + trun->bEntries.begin() + ((i + 1) * trun->entriesRowSize)); + + auto entry = std::make_unique(); + + if (hasFlag(trun->bFlags, 0x0100)) + { + entry->sampleDuration = readIntFromVector(subBuffer, 0); + } + + if (hasFlag(trun->bFlags, 0x0200)) + { + entry->sampleSize = readIntFromVector(subBuffer, 0); + } + + if (hasFlag(trun->bFlags, 0x0400)) + { + entry->sampleFlags = readIntFromVector(subBuffer, 0); + } + + if (hasFlag(trun->bFlags, 0x800)) + { + entry->sampleCompositionTimeOffset = readIntFromVector(subBuffer, 0); + } + + entry->hasCompositionTimeOffset = hasFlag(trun->bFlags, 0x0800); + entry->isKeyframe = !hasFlag(entry->sampleFlags, 0x10000); + + return entry; +} + +std::unique_ptr MpegDashDemuxer::getAbsoluteTrunEntry(std::unique_ptr &trun, int i, std::unique_ptr &header) +{ + std::unique_ptr entry = getTrunEntry(trun, i); + if (!hasFlag(trun->bFlags, 0x0100) && hasFlag(header->bFlags, 0x20)) + { + entry->sampleFlags = header->defaultSampleFlags; + } + + if (!hasFlag(trun->bFlags, 0x0200) && hasFlag(header->bFlags, 0x10)) + { + entry->sampleSize = header->defaultSampleSize; + } + + if (!hasFlag(trun->bFlags, 0x0100) && hasFlag(header->bFlags, 0x08)) + { + entry->sampleDuration = header->defaultSampleDuration; + } + + if (i == 0 && hasFlag(trun->bFlags, 0x0004)) + { + entry->sampleFlags = trun->bFirstSampleFlags; + } + return entry; +} + +std::unique_ptr MpegDashDemuxer::parseTfhd(int trackId) +{ + auto tfhd = std::make_unique(); + tfhd->bFlags = reader->readInt(); + tfhd->trackId = reader->readInt(); + + if (trackId != -1 && tfhd->trackId != trackId) + { + return tfhd; + } + + if (hasFlag(tfhd->bFlags, 0x01)) + { + reader->skip(8); + } + + if (hasFlag(tfhd->bFlags, 0x02)) + { + reader->skip(4); + } + + if (hasFlag(tfhd->bFlags, 0x08)) + { + tfhd->defaultSampleDuration = reader->readInt(); + } + + if (hasFlag(tfhd->bFlags, 0x10)) + { + tfhd->defaultSampleSize = reader->readInt(); + } + + if (hasFlag(tfhd->bFlags, 0x20)) + { + tfhd->defaultSampleFlags = reader->readInt(); + } + + return tfhd; +} + +bool MpegDashDemuxer::hasFlag(int flags, int mask) +{ + return (flags & mask) == mask; +} + +std::unique_ptr MpegDashDemuxer::parseMoov(std::unique_ptr &ref) +{ + auto moov = std::make_unique(); + + auto box = readBox(); + + moov->mvhd = parseMvhd(); + moov->mvexTrex = std::vector(); + ensureBox(box); + + moov->trak = std::vector>(); + + box = untilBox(ref, ATOM_TRAK, ATOM_MVEX); + while (box) + { + + if (box->type == ATOM_TRAK) + { + moov->trak.push_back(parseTrak(box)); + } + + if (box->type == ATOM_MVEX) + { + moov->mvexTrex = parseMvex(box, moov->mvhd->nextTrackId); + } + + ensureBox(box); + box = untilBox(ref, ATOM_TRAK, ATOM_MVEX); + } + + return moov; +} + +Trex MpegDashDemuxer::parseTrex() +{ + reader->skip(4); + Trex trex; + trex.trackId = reader->readInt(); + trex.defaultSampleDescriptionIndex = reader->readInt(); + trex.defaultSampleDuration = reader->readInt(); + trex.defaultSampleSize = reader->readInt(); + trex.defaultSampleFlags = reader->readInt(); + + return trex; +} + +std::vector MpegDashDemuxer::parseMvex(std::unique_ptr &ref, int possibleTrackCount) +{ + auto trexs = std::vector(); + + auto box = untilBox(ref, ATOM_TREX); + while (box) + { + trexs.push_back(parseTrex()); + ensureBox(box); + box = untilBox(ref, ATOM_TREX); + } + + return trexs; +} + +std::vector MpegDashDemuxer::readFullBox(std::unique_ptr &ref) +{ + auto size = ref->size; + std::vector header(8); + header.at(0) = *((uint8_t *)&ref->size + 0); + header.at(1) = *((uint8_t *)&ref->size + 1); + header.at(2) = *((uint8_t *)&ref->size + 2); + header.at(3) = *((uint8_t *)&ref->size + 3); + header.at(4) = *((uint8_t *)&ref->type + 0); + header.at(5) = *((uint8_t *)&ref->type + 1); + header.at(6) = *((uint8_t *)&ref->type + 2); + header.at(7) = *((uint8_t *)&ref->type + 3); + + std::vector data = reader->readBytes(size - 8); + + header.insert(header.end(), data.begin(), data.end()); + + return header; +} + +std::unique_ptr MpegDashDemuxer::parseTkhd() +{ + auto tkhd = std::make_unique(); + uint8_t version = reader->readByte(); + + // flags + // creation entries_time + // modification entries_time + reader->skip(3 + (2 * (version == 0 ? 4 : 8))); + tkhd->trackId = reader->readInt(); + reader->skip(4); + tkhd->duration = version == 0 ? reader->readUInt() : reader->readLong(); + reader->skip(2 * 4); + tkhd->bLayer = reader->readShort(); + tkhd->bAlternateGroup = reader->readShort(); + tkhd->bVolume = reader->readShort(); + + reader->skip(2); + tkhd->matrix = reader->readBytes(9 * 4); + tkhd->bWidth = reader->readInt(); + tkhd->bHeight = reader->readInt(); + + return tkhd; +} + +std::unique_ptr MpegDashDemuxer::parseTrak(std::unique_ptr &ref) +{ + auto trak = std::make_unique(); + auto box = readBox(); + trak->tkhd = parseTkhd(); + ensureBox(box); + + box = untilBox(ref, ATOM_MDIA, ATOM_EDTS); + while (box) + { + if (box->type == ATOM_MDIA) + { + trak->mdia = parseMdia(box); + } + + if (box->type == ATOM_EDTS) + { + trak->edstElst = parseEdts(box); + } + + ensureBox(box); + box = untilBox(ref, ATOM_MDIA, ATOM_EDTS); + } + + return trak; +} + +std::unique_ptr MpegDashDemuxer::parseMinf(std::unique_ptr &ref) +{ + auto minf = std::make_unique(); + auto box = untilAnyBox(ref); + while (box) + { + if (box->type == ATOM_DINF) + { + minf->dinf = readFullBox(box); + } + + if (box->type == ATOM_STBL) + { + minf->stblStsd = parseStbl(box); + } + if (box->type == ATOM_VMHD || box->type == ATOM_SMHD) + { + minf->mhd = readFullBox(box); + } + + ensureBox(box); + box = untilAnyBox(ref); + } + + return minf; +} + +std::vector MpegDashDemuxer::parseStbl(std::unique_ptr &ref) +{ + auto box = untilBox(ref, ATOM_STSD); + + return readFullBox(box); +} + +std::unique_ptr MpegDashDemuxer::parseEdts(std::unique_ptr &ref) +{ + auto elst = std::make_unique(); + auto box = untilBox(ref, ATOM_ELST); + + bool v1 = reader->readByte() == 1; + reader->skip(3); // flags + + int entryCount = reader->readInt(); + if (entryCount < 1) + { + elst->bMediaRate = 0x00010000; + return elst; + } + + if (v1) + { + reader->skip(8); // duration + elst->mediaTime = reader->readLong(); + // ignore all entries + reader->skip((entryCount - 1) * 16); + } + else + { + reader->skip(4); // segment duration + elst->mediaTime = reader->readInt(); + } + + elst->bMediaRate = reader->readInt(); + return elst; +} + +std::unique_ptr MpegDashDemuxer::parseHdlr(std::unique_ptr &box) +{ + auto hdlr = std::make_unique(); + reader->skip(4); + + hdlr->type = reader->readInt(); + hdlr->subType = reader->readInt(); + hdlr->bReserved = reader->readBytes(12); + reader->skip((box->offset + box->size) - reader->position()); + return hdlr; +} + +std::unique_ptr MpegDashDemuxer::parseMdia(std::unique_ptr &ref) +{ + auto mdia = std::make_unique(); + + auto box = untilBox(ref, ATOM_MDHD, ATOM_HDLR, ATOM_MINF); + while (box) + { + if (box->type == ATOM_MDHD) + { + mdia->mdhd = readFullBox(box); + } + + if (box->type == ATOM_HDLR) + { + mdia->hdlr = parseHdlr(box); + } + + if (box->type == ATOM_MINF) + { + mdia->minf = parseMinf(box); + } + + ensureBox(box); + box = untilBox(ref, ATOM_MDHD, ATOM_HDLR, ATOM_MINF); + } + + return mdia; +} + +std::unique_ptr MpegDashDemuxer::parseMvhd() +{ + auto mvhd = std::make_unique(); + uint8_t version = reader->readByte(); + reader->skip(3); // flags + + // creation entries_time + // modification entries_time + reader->skip(2 * (version == 0 ? 4 : 8)); + + mvhd->timeScale = reader->readUInt(); + + // chunkDuration + reader->skip(version == 0 ? 4 : 8); + + // rate + // volume + // reserved + // matrix array + // predefined + reader->skip(76); + + mvhd->nextTrackId = reader->readUInt(); + + return mvhd; +} + +void MpegDashDemuxer::ensureBox(std::unique_ptr &box) +{ + reader->skip(box->offset + box->size - reader->position()); +} + +std::unique_ptr MpegDashDemuxer::readBox() +{ + auto box = std::make_unique(); + box->offset = reader->position(); + box->size = reader->readUInt(); + box->type = reader->readInt(); + if (box->size == 1) + { + box->size = reader->readLong(); + } + + return box; +} + +std::unique_ptr MpegDashDemuxer::untilBox(std::unique_ptr &ref, int boxType1, int boxType2, int boxType3) +{ + auto box = std::make_unique(); + while (reader->position() < (ref->offset + ref->size)) + { + box = readBox(); + if (box->type == boxType1 || box->type == boxType2 || box->type == boxType3) + { + return box; + } + ensureBox(box); + } + + return std::unique_ptr(nullptr); +} + +std::unique_ptr MpegDashDemuxer::untilBox(std::unique_ptr &ref, int boxType1, int boxType2) +{ + auto box = std::make_unique(); + while (reader->position() < (ref->offset + ref->size)) + { + box = readBox(); + if (box->type == boxType1 || box->type == boxType2) + { + return box; + } + ensureBox(box); + } + + return std::unique_ptr(nullptr); +} + +std::unique_ptr MpegDashDemuxer::untilBox(std::unique_ptr &ref, int boxType1) +{ + auto box = std::make_unique(); + + while (reader->position() < (ref->offset + ref->size)) + { + box = readBox(); + if (box->type == boxType1) + { + return box; + } + ensureBox(box); + } + + return std::unique_ptr(nullptr); +} + +std::unique_ptr MpegDashDemuxer::untilAnyBox(std::unique_ptr &ref) +{ + if (reader->position() >= (ref->offset + ref->size)) + { + return std::unique_ptr(nullptr); + } + + return readBox(); +} \ No newline at end of file diff --git a/components/spotify/cspot/bell/src/asm/biquad_f32_ae32.S b/components/spotify/cspot/bell/src/asm/biquad_f32_ae32.S new file mode 100644 index 00000000..0df15b92 --- /dev/null +++ b/components/spotify/cspot/bell/src/asm/biquad_f32_ae32.S @@ -0,0 +1,87 @@ +// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp_platform.h" + +// This is bi quad filter form II for ESP32 processor. + .text + .align 4 + .global dsps_biquad_f32_ae32 + .type dsps_biquad_f32_ae32,@function +// The function implements the following C code: +//esp_err_t dsps_biquad_f32_ae32(const float* input, float* output, int len, float* coef, float* w) +// { +// for (int i=0 ; i< len ; i++) +// { +// float d0 = input[i] - coef[3]*w[0] - coef[4]*w[1]; (input[i] - a[1]*w[0] - a[2]*w[1];) +// output[i] = coef[0]*d0 + coef[1]*w[0] + coef[2]*w[1]; +// w[1] = w[0]; +// w[0] = d0; +// } +// return ESP_OK; +// } + +dsps_biquad_f32_ae32: +// input - a2 +// output - a3 +// len - a4 +// coeffs - a5 +// w- a6 + +// f0 - b0 +// f1 - b1 +// f2 - b2 +// f3 - a1 +// f4 - a2 + +// f5 - w0 +// f6 - w1 + + entry a1, 16 + // Array increment for floating point data should be 4 + lsi f0, a5, 0 + lsi f1, a5, 4 + lsi f2, a5, 8 + lsi f3, a5, 12 + lsi f4, a5, 16 + + + neg.s f5, f3 // -a[1] + neg.s f6, f4 // -a[2] + + lsi f7, a6, 0 // w[0] + lsi f8, a6, 4 // w[1] + + addi a3, a3, -4 // i-- // preset a3 + lsi f9, a2, 0 // f9 = x[i] + loopnez a4, loop_bq_end_m_ae32 + madd.s f9, f7, f5 // f9 += -a1*w0 + addi a3, a3, 4 // out++; + mul.s f10, f1, f7 // f10 = b1*w0 + madd.s f9, f8, f6 // f9 += -a2*w1 + madd.s f10, f9, f0 // f10 += b0*d0 + addi a2, a2, 4 // in++; + madd.s f10, f2, f8 // f10+= b2*w1, f10 - result + mov.s f8, f7 // w1 = w0 + mov.s f7, f9 // w0 = d0 + lsi f9, a2, 0 // f9 = x[i] + ssi f10, a3, 0 // y[i] = result +loop_bq_end_m_ae32: + // Store delay line + ssi f7, a6, 0 + ssi f8, a6, 4 + + movi.n a2, 0 // return status ESP_OK + retw.n + diff --git a/components/spotify/cspot/bell/src/platform/.DS_Store b/components/spotify/cspot/bell/src/platform/.DS_Store new file mode 100644 index 00000000..c77653c9 Binary files /dev/null and b/components/spotify/cspot/bell/src/platform/.DS_Store differ diff --git a/components/spotify/cspot/bell/src/platform/apple/WrappedSemaphore.cpp b/components/spotify/cspot/bell/src/platform/apple/WrappedSemaphore.cpp new file mode 100644 index 00000000..a2f43e19 --- /dev/null +++ b/components/spotify/cspot/bell/src/platform/apple/WrappedSemaphore.cpp @@ -0,0 +1,29 @@ +#include "platform/WrappedSemaphore.h" + +WrappedSemaphore::WrappedSemaphore(int count) +{ + semaphoreHandle = dispatch_semaphore_create(0); +} + +WrappedSemaphore::~WrappedSemaphore() +{ + dispatch_release(semaphoreHandle); +} + +int WrappedSemaphore::wait() +{ + + return dispatch_semaphore_wait(semaphoreHandle, DISPATCH_TIME_FOREVER); +} + +int WrappedSemaphore::twait(long milliseconds) +{ + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (NSEC_PER_SEC / 1000) * milliseconds); + + return dispatch_semaphore_wait(semaphoreHandle, timeout); +} + +void WrappedSemaphore::give() +{ + dispatch_semaphore_signal(semaphoreHandle); +} diff --git a/components/spotify/cspot/bell/src/platform/esp/TLSSocket.cpp b/components/spotify/cspot/bell/src/platform/esp/TLSSocket.cpp new file mode 100644 index 00000000..92010918 --- /dev/null +++ b/components/spotify/cspot/bell/src/platform/esp/TLSSocket.cpp @@ -0,0 +1,92 @@ +#include "platform/TLSSocket.h" + +/** + * Platform TLSSocket implementation for the mbedtls + */ +bell::TLSSocket::TLSSocket() +{ + mbedtls_net_init(&server_fd); + mbedtls_ssl_init(&ssl); + mbedtls_ssl_config_init(&conf); + mbedtls_ctr_drbg_init(&ctr_drbg); + mbedtls_entropy_init(&entropy); + const char *pers = "euphonium"; + int ret; + if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + (const unsigned char *)pers, + strlen(pers))) != 0) + { + BELL_LOG(error, "http_tls", + "failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret); + } +} + +void bell::TLSSocket::open(std::string url) +{ + // initialize + int ret; + url.erase(0, url.find("://") + 3); + std::string hostUrl = url.substr(0, url.find('/')); + std::string pathUrl = url.substr(url.find('/')); + + std::string portString = "443"; + // check if hostUrl contains ':' + if (hostUrl.find(':') != std::string::npos) + { + // split by ':' + std::string host = hostUrl.substr(0, hostUrl.find(':')); + portString = hostUrl.substr(hostUrl.find(':') + 1); + hostUrl = host; + } + + if ((ret = mbedtls_net_connect(&server_fd, hostUrl.c_str(), "443", + MBEDTLS_NET_PROTO_TCP)) != 0) + { + BELL_LOG(error, "http_tls", "failed! connect returned %d\n", ret); + } + + if ((ret = mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0) + { + + BELL_LOG(error, "http_tls", "failed! config returned %d\n", ret); + } + + mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_NONE); + mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg); + mbedtls_ssl_setup(&ssl, &conf); + if ((ret = mbedtls_ssl_set_hostname(&ssl, "Mbed TLS Server 1")) != 0) + { + BELL_LOG(info, "oh", "kocz"); + } + mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, + NULL); + + while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) + { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) + { + BELL_LOG(error, "http_tls", "failed! config returned %d\n", ret); + } + } +} + +size_t bell::TLSSocket::read(uint8_t *buf, size_t len) +{ + return mbedtls_ssl_read(&ssl, buf, len); +} + +size_t bell::TLSSocket::write(uint8_t *buf, size_t len) +{ + return mbedtls_ssl_write(&ssl, buf, len); +} + +void bell::TLSSocket::close() +{ + mbedtls_net_free(&server_fd); + mbedtls_ssl_free(&ssl); + mbedtls_ssl_config_free(&conf); + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); +} diff --git a/components/spotify/cspot/bell/src/platform/esp/WrappedSemaphore.cpp b/components/spotify/cspot/bell/src/platform/esp/WrappedSemaphore.cpp new file mode 100644 index 00000000..589b1e3d --- /dev/null +++ b/components/spotify/cspot/bell/src/platform/esp/WrappedSemaphore.cpp @@ -0,0 +1,39 @@ +#include "platform/WrappedSemaphore.h" + +/** + * Platform semaphopre implementation for the esp-idf. + */ + +WrappedSemaphore::WrappedSemaphore(int count) +{ + semaphoreHandle = xSemaphoreCreateCounting(count, 0); +} + +WrappedSemaphore::~WrappedSemaphore() +{ + vSemaphoreDelete(semaphoreHandle); +} + +int WrappedSemaphore::wait() +{ + if (xSemaphoreTake(semaphoreHandle, portMAX_DELAY) == pdTRUE) { + return 0; + } + + return 1; +} + +int WrappedSemaphore::twait(long milliseconds) +{ + if (xSemaphoreTake(semaphoreHandle, milliseconds / portTICK_PERIOD_MS) == pdTRUE) { + return 0; + } + + return 1; +} + +void WrappedSemaphore::give() +{ + + xSemaphoreGive(semaphoreHandle); +} diff --git a/components/spotify/cspot/bell/src/platform/linux/TLSSocket.cpp b/components/spotify/cspot/bell/src/platform/linux/TLSSocket.cpp new file mode 100644 index 00000000..4a895536 --- /dev/null +++ b/components/spotify/cspot/bell/src/platform/linux/TLSSocket.cpp @@ -0,0 +1,72 @@ +#include "platform/TLSSocket.h" + +/** + * Platform TLSSocket implementation for the openssl + */ + +bell::TLSSocket::TLSSocket() { + ERR_load_crypto_strings(); + ERR_load_SSL_strings(); + OpenSSL_add_all_algorithms(); + ctx = SSL_CTX_new(SSLv23_client_method()); +} + +void bell::TLSSocket::open(std::string url) { + + /* We'd normally set some stuff like the verify paths and + * mode here because as things stand this will connect to + * any server whose certificate is signed by any CA. + */ + + sbio = BIO_new_ssl_connect(ctx); + + BIO_get_ssl(sbio, &ssl); + SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); + + url.erase(0, url.find("://") + 3); + std::string hostUrl = url.substr(0, url.find('/')); + std::string pathUrl = url.substr(url.find('/')); + + std::string portString = "443"; + // check if hostUrl contains ':' + if (hostUrl.find(':') != std::string::npos) { + // split by ':' + std::string host = hostUrl.substr(0, hostUrl.find(':')); + portString = hostUrl.substr(hostUrl.find(':') + 1); + hostUrl = host; + } + + BELL_LOG(info, "http_tls", "Connecting with %s", hostUrl.c_str()); + BIO_set_conn_hostname(sbio, std::string(hostUrl + ":443").c_str()); + + out = BIO_new_fp(stdout, BIO_NOCLOSE); + if (BIO_do_connect(sbio) <= 0) { + BELL_LOG(error, "http_tls", "Error connecting with server"); + /* whatever ... */ + } + + if (BIO_do_handshake(sbio) <= 0) { + + BELL_LOG(error, "http_tls", "Error TLS connection"); + + /* whatever ... */ + } // remove https or http from url + + // split by first "/" in url +} + +size_t bell::TLSSocket::read(uint8_t *buf, size_t len) { + return BIO_read(sbio, buf, len); +} + +size_t bell::TLSSocket::write(uint8_t *buf, size_t len) { + return BIO_write(sbio, buf, len); +} + +void bell::TLSSocket::close() { + if (!isClosed) { + BIO_free_all(sbio); + BIO_free(out); + isClosed = true; + } +} diff --git a/components/spotify/cspot/bell/src/platform/linux/WrappedSemaphore.cpp b/components/spotify/cspot/bell/src/platform/linux/WrappedSemaphore.cpp new file mode 100644 index 00000000..43c3ed92 --- /dev/null +++ b/components/spotify/cspot/bell/src/platform/linux/WrappedSemaphore.cpp @@ -0,0 +1,31 @@ +#include "platform/WrappedSemaphore.h" + +WrappedSemaphore::WrappedSemaphore(int count) +{ + sem_init(&this->semaphoreHandle, 0, 0); // eek pointer +} + +WrappedSemaphore::~WrappedSemaphore() +{ + sem_destroy(&this->semaphoreHandle); +} + +int WrappedSemaphore::wait() +{ + sem_wait(&this->semaphoreHandle); + return 0; +} + +int WrappedSemaphore::twait(long milliseconds) +{ + // wait on semaphore with timeout + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_nsec += (milliseconds % 1000) * 1000000; + return sem_timedwait(&this->semaphoreHandle, &ts); +} + +void WrappedSemaphore::give() +{ + sem_post(&this->semaphoreHandle); +} diff --git a/components/spotify/cspot/bell/tremor/CHANGELOG b/components/spotify/cspot/bell/tremor/CHANGELOG new file mode 100644 index 00000000..53f23351 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/CHANGELOG @@ -0,0 +1,19 @@ +*** 20020517: 1.0.2 *** + + Playback bugfix to floor1; mode mistakenly used for sizing instead + of blockflag + +*** 20020515: 1.0.1 *** + + Added complete API documentation to source tarball. No code + changes. + +*** 20020412: 1.0.1 *** + + Fixed a clipping bug that affected ARM processors; negative + overflows were being properly clipped, but then clobbered to + positive by the positive overflow chec (asm_arm.h:CLIP_TO_15) + +*** 20020403: 1.0.0 *** + + Initial version \ No newline at end of file diff --git a/components/spotify/cspot/bell/tremor/COPYING b/components/spotify/cspot/bell/tremor/COPYING new file mode 100644 index 00000000..6111c6c5 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/COPYING @@ -0,0 +1,28 @@ +Copyright (c) 2002, Xiph.org Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/components/spotify/cspot/bell/tremor/Makefile.am b/components/spotify/cspot/bell/tremor/Makefile.am new file mode 100644 index 00000000..1d18b1a7 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/Makefile.am @@ -0,0 +1,43 @@ +AUTOMAKE_OPTIONS = foreign + +INCLUDES = -I./ + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = vorbisidec.pc + +lib_LTLIBRARIES = libvorbisidec.la + +libvorbisidec_la_SOURCES = mdct.c dsp.c info.c misc.c \ + floor1.c floor0.c vorbisfile.c \ + res012.c mapping0.c codebook.c \ + framing.c bitwise.c \ + codebook.h misc.h mdct_lookup.h\ + os.h mdct.h ivorbisfile.h lsp_lookup.h\ + window_lookup.h floor_lookup.c \ + codec_internal.h ogg.h \ + asm_arm.h ivorbiscodec.h +libvorbisidec_la_LDFLAGS = -version-info @V_LIB_CURRENT@:@V_LIB_REVISION@:@V_LIB_AGE@ + +EXTRA_PROGRAMS = ivorbisfile_example +CLEANFILES = $(EXTRA_PROGRAMS) $(lib_LTLIBRARIES) + +ivorbisfile_example_SOURCES = ivorbisfile_example.c +ivorbisfile_example_LDFLAGS = -static +ivorbisfile_example_LDADD = libvorbisidec.la + +includedir = $(prefix)/include/tremor + +include_HEADERS = ivorbiscodec.h ivorbisfile.h ogg.h os_types.h config_types.h + +EXTRA_DIST = vorbisidec.pc.in \ + $(srcdir)/doc/*.html + +example: + -ln -fs . vorbis + $(MAKE) ivorbisfile_example + +debug: + $(MAKE) all CFLAGS="@DEBUG@" + +profile: + $(MAKE) all CFLAGS="@PROFILE@" diff --git a/components/spotify/cspot/bell/tremor/README b/components/spotify/cspot/bell/tremor/README new file mode 100644 index 00000000..13211753 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/README @@ -0,0 +1,46 @@ +This README covers the Ogg Vorbis 'Tremor' integer playback codec +source as of date 2002 09 02, version 1.0.0. + + ****** + +The C source in this package will build on any ANSI C compiler and +function completely and properly on any platform. The included build +system assumes GNU build system and make tools (m4, automake, +autoconf, libtool and gmake). GCC is not required, although GCC is +the most tested compiler. To build using GNU tools, type in the +source directory: + +./autogen.sh +make + +Currently, the source implements playback in pure C on all platforms +except ARM, where a [currently] small amount of assembly (see +asm_arm.h) is used to implement 64 bit math operations and fast LSP +computation. If building on ARM without the benefit of GNU build +system tools, be sure that '_ARM_ASSEM_' is #defined by the build +system if this assembly is desired, else the resulting library will +use whatever 64 bit math builtins the compiler implements. + +No math library is required by this source. No floating point +operations are used at any point in either setup or decode. This +decoder library will properly decode any past, current or future +Vorbis I file or stream. + + ******** + +The build system produces a static and [when supported by the OS] +dynamic library named 'libvorbisidec'. This library exposes an API +nearly identical to the BSD reference library's 'libvorbisfile', +including all the features familiar to users of vorbisfile. This API +is similar enough that the proper header file to include is named +'ivorbisfile.h' [included in the source build directory]. Lower level +libvorbis-style headers and structures are in 'ivorbiscodec.h' +[included in the source build directory]. A simple example program, +ivorbisfile_example.c, can be built with 'make example'. + + ******** + +Detailed Tremor API Documentation begins at doc/index.html + +Monty +xiph.org diff --git a/components/spotify/cspot/bell/tremor/TODO b/components/spotify/cspot/bell/tremor/TODO new file mode 100644 index 00000000..0e542a37 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/TODO @@ -0,0 +1,2 @@ +Add explicit 64 bit integer support rather than relying on compiler +Roll in optional use of bounded heap memory manager diff --git a/components/spotify/cspot/bell/tremor/Version_script.in b/components/spotify/cspot/bell/tremor/Version_script.in new file mode 100644 index 00000000..85d76539 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/Version_script.in @@ -0,0 +1,61 @@ +# +# Export file for libvorbisidec +# +# Only the symbols listed in the global section will be callable from +# applications linking to libvorbisidec. +# + +@PACKAGE@.so.1 +{ + global: + ov_clear; + ov_open; + ov_open_callbacks; + ov_test; + ov_test_callbacks; + ov_test_open; + ov_bitrate; + ov_bitrate_instant; + ov_streams; + ov_seekable; + ov_serialnumber; + ov_raw_total; + ov_pcm_total; + ov_time_total; + ov_raw_seek; + ov_pcm_seek; + ov_pcm_seek_page; + ov_time_seek; + ov_time_seek_page; + ov_raw_tell; + ov_pcm_tell; + ov_time_tell; + ov_info; + ov_comment; + ov_read; + + vorbis_info_init; + vorbis_info_clear; + vorbis_info_blocksize; + vorbis_comment_init; + vorbis_comment_add; + vorbis_comment_add_tag; + vorbis_comment_query; + vorbis_comment_query_count; + vorbis_comment_clear; + vorbis_block_init; + vorbis_block_clear; + vorbis_dsp_clear; + vorbis_synthesis_idheader; + vorbis_synthesis_headerin; + vorbis_synthesis_init; + vorbis_synthesis_restart; + vorbis_synthesis; + vorbis_synthesis_blockin; + vorbis_synthesis_pcmout; + vorbis_synthesis_read; + vorbis_packet_blocksize; + + local: + *; +}; diff --git a/components/spotify/cspot/bell/tremor/asm_arm.h b/components/spotify/cspot/bell/tremor/asm_arm.h new file mode 100644 index 00000000..3a3716df --- /dev/null +++ b/components/spotify/cspot/bell/tremor/asm_arm.h @@ -0,0 +1,243 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: arm7 and later wide math functions + + ********************************************************************/ + +#ifdef _ARM_ASSEM_ + +#if !defined(_V_WIDE_MATH) && !defined(_LOW_ACCURACY_) +#define _V_WIDE_MATH + +static inline ogg_int32_t MULT32(ogg_int32_t x, ogg_int32_t y) { + int lo,hi; + asm volatile("smull\t%0, %1, %2, %3" + : "=&r"(lo),"=&r"(hi) + : "%r"(x),"r"(y) + : "cc"); + return(hi); +} + +static inline ogg_int32_t MULT31(ogg_int32_t x, ogg_int32_t y) { + return MULT32(x,y)<<1; +} + +static inline ogg_int32_t MULT31_SHIFT15(ogg_int32_t x, ogg_int32_t y) { + int lo,hi; + asm volatile("smull %0, %1, %2, %3\n\t" + "movs %0, %0, lsr #15\n\t" + "adc %1, %0, %1, lsl #17\n\t" + : "=&r"(lo),"=&r"(hi) + : "%r"(x),"r"(y) + : "cc"); + return(hi); +} + +#define MB() asm volatile ("" : : : "memory") + +static inline void XPROD32(ogg_int32_t a, ogg_int32_t b, + ogg_int32_t t, ogg_int32_t v, + ogg_int32_t *x, ogg_int32_t *y) +{ + int x1, y1, l; + asm( "smull %0, %1, %4, %6\n\t" + "smlal %0, %1, %5, %7\n\t" + "rsb %3, %4, #0\n\t" + "smull %0, %2, %5, %6\n\t" + "smlal %0, %2, %3, %7" + : "=&r" (l), "=&r" (x1), "=&r" (y1), "=r" (a) + : "3" (a), "r" (b), "r" (t), "r" (v) + : "cc" ); + *x = x1; + MB(); + *y = y1; +} + +static inline void XPROD31(ogg_int32_t a, ogg_int32_t b, + ogg_int32_t t, ogg_int32_t v, + ogg_int32_t *x, ogg_int32_t *y) +{ + int x1, y1, l; + asm( "smull %0, %1, %4, %6\n\t" + "smlal %0, %1, %5, %7\n\t" + "rsb %3, %4, #0\n\t" + "smull %0, %2, %5, %6\n\t" + "smlal %0, %2, %3, %7" + : "=&r" (l), "=&r" (x1), "=&r" (y1), "=r" (a) + : "3" (a), "r" (b), "r" (t), "r" (v) + : "cc" ); + *x = x1 << 1; + MB(); + *y = y1 << 1; +} + +static inline void XNPROD31(ogg_int32_t a, ogg_int32_t b, + ogg_int32_t t, ogg_int32_t v, + ogg_int32_t *x, ogg_int32_t *y) +{ + int x1, y1, l; + asm( "rsb %2, %4, #0\n\t" + "smull %0, %1, %3, %5\n\t" + "smlal %0, %1, %2, %6\n\t" + "smull %0, %2, %4, %5\n\t" + "smlal %0, %2, %3, %6" + : "=&r" (l), "=&r" (x1), "=&r" (y1) + : "r" (a), "r" (b), "r" (t), "r" (v) + : "cc" ); + *x = x1 << 1; + MB(); + *y = y1 << 1; +} + +#endif + +#ifndef _V_CLIP_MATH +#define _V_CLIP_MATH + +static inline ogg_int32_t CLIP_TO_15(ogg_int32_t x) { + int tmp; + asm volatile("subs %1, %0, #32768\n\t" + "movpl %0, #0x7f00\n\t" + "orrpl %0, %0, #0xff\n" + "adds %1, %0, #32768\n\t" + "movmi %0, #0x8000" + : "+r"(x),"=r"(tmp) + : + : "cc"); + return(x); +} + +#endif + +#ifndef _V_LSP_MATH_ASM +#define _V_LSP_MATH_ASM + +static inline void lsp_loop_asm(ogg_uint32_t *qip,ogg_uint32_t *pip, + ogg_int32_t *qexpp, + ogg_int32_t *ilsp,ogg_int32_t wi, + ogg_int32_t m){ + + ogg_uint32_t qi=*qip,pi=*pip; + ogg_int32_t qexp=*qexpp; + + asm("mov r0,%3;" + "mov r1,%5,asr#1;" + "add r0,r0,r1,lsl#3;" + "1:" + + "ldmdb r0!,{r1,r3};" + "subs r1,r1,%4;" //ilsp[j]-wi + "rsbmi r1,r1,#0;" //labs(ilsp[j]-wi) + "umull %0,r2,r1,%0;" //qi*=labs(ilsp[j]-wi) + + "subs r1,r3,%4;" //ilsp[j+1]-wi + "rsbmi r1,r1,#0;" //labs(ilsp[j+1]-wi) + "umull %1,r3,r1,%1;" //pi*=labs(ilsp[j+1]-wi) + + "cmn r2,r3;" // shift down 16? + "beq 0f;" + "add %2,%2,#16;" + "mov %0,%0,lsr #16;" + "orr %0,%0,r2,lsl #16;" + "mov %1,%1,lsr #16;" + "orr %1,%1,r3,lsl #16;" + "0:" + "cmp r0,%3;\n" + "bhi 1b;\n" + + // odd filter assymetry + "ands r0,%5,#1;\n" + "beq 2f;\n" + "add r0,%3,%5,lsl#2;\n" + + "ldr r1,[r0,#-4];\n" + "mov r0,#0x4000;\n" + + "subs r1,r1,%4;\n" //ilsp[j]-wi + "rsbmi r1,r1,#0;\n" //labs(ilsp[j]-wi) + "umull %0,r2,r1,%0;\n" //qi*=labs(ilsp[j]-wi) + "umull %1,r3,r0,%1;\n" //pi*=labs(ilsp[j+1]-wi) + + "cmn r2,r3;\n" // shift down 16? + "beq 2f;\n" + "add %2,%2,#16;\n" + "mov %0,%0,lsr #16;\n" + "orr %0,%0,r2,lsl #16;\n" + "mov %1,%1,lsr #16;\n" + "orr %1,%1,r3,lsl #16;\n" + + //qi=(pi>>shift)*labs(ilsp[j]-wi); + //pi=(qi>>shift)*labs(ilsp[j+1]-wi); + //qexp+=shift; + + //} + + /* normalize to max 16 sig figs */ + "2:" + "mov r2,#0;" + "orr r1,%0,%1;" + "tst r1,#0xff000000;" + "addne r2,r2,#8;" + "movne r1,r1,lsr #8;" + "tst r1,#0x00f00000;" + "addne r2,r2,#4;" + "movne r1,r1,lsr #4;" + "tst r1,#0x000c0000;" + "addne r2,r2,#2;" + "movne r1,r1,lsr #2;" + "tst r1,#0x00020000;" + "addne r2,r2,#1;" + "movne r1,r1,lsr #1;" + "tst r1,#0x00010000;" + "addne r2,r2,#1;" + "mov %0,%0,lsr r2;" + "mov %1,%1,lsr r2;" + "add %2,%2,r2;" + + : "+r"(qi),"+r"(pi),"+r"(qexp) + : "r"(ilsp),"r"(wi),"r"(m) + : "r0","r1","r2","r3","cc"); + + *qip=qi; + *pip=pi; + *qexpp=qexp; +} + +static inline void lsp_norm_asm(ogg_uint32_t *qip,ogg_int32_t *qexpp){ + + ogg_uint32_t qi=*qip; + ogg_int32_t qexp=*qexpp; + + asm("tst %0,#0x0000ff00;" + "moveq %0,%0,lsl #8;" + "subeq %1,%1,#8;" + "tst %0,#0x0000f000;" + "moveq %0,%0,lsl #4;" + "subeq %1,%1,#4;" + "tst %0,#0x0000c000;" + "moveq %0,%0,lsl #2;" + "subeq %1,%1,#2;" + "tst %0,#0x00008000;" + "moveq %0,%0,lsl #1;" + "subeq %1,%1,#1;" + : "+r"(qi),"+r"(qexp) + : + : "cc"); + *qip=qi; + *qexpp=qexp; +} + +#endif +#endif + diff --git a/components/spotify/cspot/bell/tremor/autogen.sh b/components/spotify/cspot/bell/tremor/autogen.sh new file mode 100644 index 00000000..e2e6f6b9 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/autogen.sh @@ -0,0 +1,61 @@ +#!/bin/sh +# Run this to set up the build system: configure, makefiles, etc. +# (based on the version in enlightenment's cvs) + +package="vorbisidec" + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +cd "$srcdir" +DIE=0 + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have autoconf installed to compile $package." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have automake installed to compile $package." + echo "Download the appropriate package for your system," + echo "or get the source from one of the GNU ftp sites" + echo "listed in http://www.gnu.org/order/ftp.html" + DIE=1 +} + +(libtool --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have libtool installed to compile $package." + echo "Download the appropriate package for your system," + echo "or get the source from one of the GNU ftp sites" + echo "listed in http://www.gnu.org/order/ftp.html" + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +if test -z "$*"; then + echo "I am going to run ./configure with no arguments - if you wish " + echo "to pass any to it, please specify them on the $0 command line." +fi + +echo "Generating configuration files for $package, please wait...." + +echo " aclocal $ACLOCAL_FLAGS" +aclocal $ACLOCAL_FLAGS || exit 1 +echo " autoheader" +autoheader || exit 1 +echo " libtoolize --automake" +libtoolize --automake || exit 1 +echo " automake --add-missing $AUTOMAKE_FLAGS" +automake --add-missing $AUTOMAKE_FLAGS || exit 1 +echo " autoconf" +autoconf || exit 1 + +$srcdir/configure "$@" && echo diff --git a/components/spotify/cspot/bell/tremor/bitwise.c b/components/spotify/cspot/bell/tremor/bitwise.c new file mode 100644 index 00000000..e193957c --- /dev/null +++ b/components/spotify/cspot/bell/tremor/bitwise.c @@ -0,0 +1,675 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: packing variable sized words into an octet stream + + ********************************************************************/ + +/* We're 'LSb' endian; if we write a word but read individual bits, + then we'll read the lsb first */ + +#include +#include +#include "misc.h" +#include "ogg.h" + +static unsigned long mask[]= +{0x00000000,0x00000001,0x00000003,0x00000007,0x0000000f, + 0x0000001f,0x0000003f,0x0000007f,0x000000ff,0x000001ff, + 0x000003ff,0x000007ff,0x00000fff,0x00001fff,0x00003fff, + 0x00007fff,0x0000ffff,0x0001ffff,0x0003ffff,0x0007ffff, + 0x000fffff,0x001fffff,0x003fffff,0x007fffff,0x00ffffff, + 0x01ffffff,0x03ffffff,0x07ffffff,0x0fffffff,0x1fffffff, + 0x3fffffff,0x7fffffff,0xffffffff }; + +/* spans forward, skipping as many bytes as headend is negative; if + headend is zero, simply finds next byte. If we're up to the end + of the buffer, leaves headend at zero. If we've read past the end, + halt the decode process. */ + +static void _span(oggpack_buffer *b){ + while(b->headend-(b->headbit>>3)<1){ + b->headend-=b->headbit>>3; + b->headbit&=0x7; + + if(b->head->next){ + b->count+=b->head->length; + b->head=b->head->next; + + if(b->headend+b->head->length>0) + b->headptr=b->head->buffer->data+b->head->begin-b->headend; + + b->headend+=b->head->length; + }else{ + /* we've either met the end of decode, or gone past it. halt + only if we're past */ + if(b->headend*8headbit) + /* read has fallen off the end */ + b->headend=-1; + break; + } + } +} + +void oggpack_readinit(oggpack_buffer *b,ogg_reference *r){ + memset(b,0,sizeof(*b)); + + b->tail=b->head=r; + b->count=0; + b->headptr=b->head->buffer->data+b->head->begin; + b->headend=b->head->length; + _span(b); +} + +#define _lookspan() while(!end){\ + head=head->next;\ + if(!head) return -1;\ + ptr=head->buffer->data + head->begin;\ + end=head->length;\ + } + +/* Read in bits without advancing the bitptr; bits <= 32 */ +long oggpack_look(oggpack_buffer *b,int bits){ + unsigned long m=mask[bits]; + unsigned long ret; + + bits+=b->headbit; + + if(bits >= b->headend<<3){ + int end=b->headend; + unsigned char *ptr=b->headptr; + ogg_reference *head=b->head; + + if(end<0)return -1; + + if(bits){ + _lookspan(); + ret=*ptr++>>b->headbit; + if(bits>8){ + --end; + _lookspan(); + ret|=*ptr++<<(8-b->headbit); + if(bits>16){ + --end; + _lookspan(); + ret|=*ptr++<<(16-b->headbit); + if(bits>24){ + --end; + _lookspan(); + ret|=*ptr++<<(24-b->headbit); + if(bits>32 && b->headbit){ + --end; + _lookspan(); + ret|=*ptr<<(32-b->headbit); + } + } + } + } + } + + }else{ + + /* make this a switch jump-table */ + ret=b->headptr[0]>>b->headbit; + if(bits>8){ + ret|=b->headptr[1]<<(8-b->headbit); + if(bits>16){ + ret|=b->headptr[2]<<(16-b->headbit); + if(bits>24){ + ret|=b->headptr[3]<<(24-b->headbit); + if(bits>32 && b->headbit) + ret|=b->headptr[4]<<(32-b->headbit); + } + } + } + } + + ret&=m; + return ret; +} + +/* limited to 32 at a time */ +void oggpack_adv(oggpack_buffer *b,int bits){ + bits+=b->headbit; + b->headbit=bits&7; + b->headend-=(bits>>3); + b->headptr+=(bits>>3); + if(b->headend<1)_span(b); +} + +int oggpack_eop(oggpack_buffer *b){ + if(b->headend<0)return -1; + return 0; +} + +/* bits <= 32 */ +long oggpack_read(oggpack_buffer *b,int bits){ + long ret=oggpack_look(b,bits); + oggpack_adv(b,bits); + return(ret); +} + +long oggpack_bytes(oggpack_buffer *b){ + if(b->headend<0)return b->count+b->head->length; + return b->count + b->head->length-b->headend + + (b->headbit+7)/8; +} + +long oggpack_bits(oggpack_buffer *b){ + if(b->headend<0)return (b->count+b->head->length)*8; + return (b->count + b->head->length-b->headend)*8 + + b->headbit; +} + +/* Self test of the bitwise routines; everything else is based on + them, so they damned well better be solid. */ + +#ifdef _V_BIT_TEST +#include +#include +#include +#include "framing.c" + +static int ilog(unsigned long v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +oggpack_buffer r; +oggpack_buffer o; +ogg_buffer_state *bs; +ogg_reference *or; +#define TESTWORDS 256 + +void report(char *in){ + fprintf(stderr,"%s",in); + exit(1); +} + +int getbyte(ogg_reference *or,int position){ + while(or && position>=or->length){ + position-=or->length; + or=or->next; + if(or==NULL){ + fprintf(stderr,"\n\tERROR: getbyte ran off end of buffer.\n"); + exit(1); + } + } + + if((position+or->begin)&1) + return (or->buffer->data[(position+or->begin)>>1])&0xff; + else + return (or->buffer->data[(position+or->begin)>>1]>>8)&0xff; +} + +void cliptest(unsigned long *b,int vals,int bits,int *comp,int compsize){ + long i,bitcount=0; + ogg_reference *or=ogg_buffer_alloc(bs,64); + for(i=0;ibuffer->data[i]= comp[i]; + or->length=i; + + oggpack_readinit(&r,or); + for(i=0;i7) + report("\nERROR: too many bits reported left over.\n"); + + /* does reading to exactly byte alignment *not* trip EOF? */ + if(oggpack_read(&o,leftover)==-1) + report("\nERROR: read to but not past exact end tripped EOF.\n"); + if(oggpack_bits(&o)!=count*8) + report("\nERROR: read to but not past exact end reported bad bitcount.\n"); + + /* does EOF trip properly after a single additional bit? */ + if(oggpack_read(&o,1)!=-1) + report("\nERROR: read past exact end did not trip EOF.\n"); + if(oggpack_bits(&o)!=count*8) + report("\nERROR: read past exact end reported bad bitcount.\n"); + + /* does EOF stay set over additional bit reads? */ + for(i=0;i<=32;i++){ + if(oggpack_read(&o,i)!=-1) + report("\nERROR: EOF did not stay set on stream.\n"); + if(oggpack_bits(&o)!=count*8) + report("\nERROR: read past exact end reported bad bitcount.\n"); + } +} + +void _end_verify2(int count){ + int i; + + /* are the proper number of bits left over? */ + int leftover=count*8-oggpack_bits(&o); + if(leftover>7) + report("\nERROR: too many bits reported left over.\n"); + + /* does reading to exactly byte alignment *not* trip EOF? */ + oggpack_adv(&o,leftover); + if(o.headend!=0) + report("\nERROR: read to but not past exact end tripped EOF.\n"); + if(oggpack_bits(&o)!=count*8) + report("\nERROR: read to but not past exact end reported bad bitcount.\n"); + + /* does EOF trip properly after a single additional bit? */ + oggpack_adv(&o,1); + if(o.headend>=0) + report("\nERROR: read past exact end did not trip EOF.\n"); + if(oggpack_bits(&o)!=count*8) + report("\nERROR: read past exact end reported bad bitcount.\n"); + + /* does EOF stay set over additional bit reads? */ + for(i=0;i<=32;i++){ + oggpack_adv(&o,i); + if(o.headend>=0) + report("\nERROR: EOF did not stay set on stream.\n"); + if(oggpack_bits(&o)!=count*8) + report("\nERROR: read past exact end reported bad bitcount.\n"); + } +} + +long ogg_buffer_length(ogg_reference *or){ + int count=0; + while(or){ + count+=or->length; + or=or->next; + } + return count; +} + +ogg_reference *ogg_buffer_extend(ogg_reference *or,long bytes){ + if(or){ + while(or->next){ + or=or->next; + } + or->next=ogg_buffer_alloc(or->buffer->ptr.owner,bytes); + return(or->next); + } + return 0; +} + +void ogg_buffer_posttruncate(ogg_reference *or,long pos){ + /* walk to the point where we want to begin truncate */ + while(or && pos>or->length){ + pos-=or->length; + or=or->next; + } + if(or){ + ogg_buffer_release(or->next); + or->next=0; + or->length=pos; + } +} + +int main(void){ + long i; + static unsigned long testbuffer1[]= + {18,12,103948,4325,543,76,432,52,3,65,4,56,32,42,34,21,1,23,32,546,456,7, + 567,56,8,8,55,3,52,342,341,4,265,7,67,86,2199,21,7,1,5,1,4}; + int test1size=43; + + static unsigned long testbuffer2[]= + {216531625L,1237861823,56732452,131,3212421,12325343,34547562,12313212, + 1233432,534,5,346435231,14436467,7869299,76326614,167548585, + 85525151,0,12321,1,349528352}; + int test2size=21; + + static unsigned long testbuffer3[]= + {1,0,14,0,1,0,12,0,1,0,0,0,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,1,1,1,1,0,0,1, + 0,1,30,1,1,1,0,0,1,0,0,0,12,0,11,0,1,0,0,1}; + int test3size=56; + + static unsigned long large[]= + {2136531625L,2137861823,56732452,131,3212421,12325343,34547562,12313212, + 1233432,534,5,2146435231,14436467,7869299,76326614,167548585, + 85525151,0,12321,1,2146528352}; + + int onesize=33; + static int one[33]={146,25,44,151,195,15,153,176,233,131,196,65,85,172,47,40, + 34,242,223,136,35,222,211,86,171,50,225,135,214,75,172, + 223,4}; + + int twosize=6; + static int two[6]={61,255,255,251,231,29}; + + int threesize=54; + static int three[54]={169,2,232,252,91,132,156,36,89,13,123,176,144,32,254, + 142,224,85,59,121,144,79,124,23,67,90,90,216,79,23,83, + 58,135,196,61,55,129,183,54,101,100,170,37,127,126,10, + 100,52,4,14,18,86,77,1}; + + int foursize=38; + static int four[38]={18,6,163,252,97,194,104,131,32,1,7,82,137,42,129,11,72, + 132,60,220,112,8,196,109,64,179,86,9,137,195,208,122,169, + 28,2,133,0,1}; + + int fivesize=45; + static int five[45]={169,2,126,139,144,172,30,4,80,72,240,59,130,218,73,62, + 241,24,210,44,4,20,0,248,116,49,135,100,110,130,181,169, + 84,75,159,2,1,0,132,192,8,0,0,18,22}; + + int sixsize=7; + static int six[7]={17,177,170,242,169,19,148}; + + /* Test read/write together */ + /* Later we test against pregenerated bitstreams */ + bs=ogg_buffer_create(); + + fprintf(stderr,"\nSmall preclipped packing (LSb): "); + cliptest(testbuffer1,test1size,0,one,onesize); + fprintf(stderr,"ok."); + + fprintf(stderr,"\nNull bit call (LSb): "); + cliptest(testbuffer3,test3size,0,two,twosize); + fprintf(stderr,"ok."); + + fprintf(stderr,"\nLarge preclipped packing (LSb): "); + cliptest(testbuffer2,test2size,0,three,threesize); + fprintf(stderr,"ok."); + + fprintf(stderr,"\n32 bit preclipped packing (LSb): "); + + or=ogg_buffer_alloc(bs,128); + for(i=0;ibuffer->data[i*4] = large[i]&0xff; + or->buffer->data[i*4+1] = (large[i]>>8)&0xff; + or->buffer->data[i*4+2] = (large[i]>>16)&0xff; + or->buffer->data[i*4+3] = (large[i]>>24)&0xff; + } + or->length=test2size*4; + oggpack_readinit(&r,or); + for(i=0;i>k)&0x1)<7){ + bit=0; + word++; + } + } + } + } + count2=(bitcount+7)>>3; + + /* construct random-length buffer chain from flat vector; random + byte starting offset within the length of the vector */ + { + ogg_reference *or=NULL,*orl=NULL; + long pos=0; + + /* build buffer chain */ + while(count2){ + int ilen=(rand()%32),k; + int ibegin=(rand()%32); + + + if(ilen>count2)ilen=count2; + + if(or) + orl=ogg_buffer_extend(orl,64); + else + or=orl=ogg_buffer_alloc(bs,64); + + orl->length=ilen; + orl->begin=ibegin; + + for(k=0;kbuffer->data[ibegin++]= flat[pos++]; + + count2-=ilen; + } + + if(ogg_buffer_length(or)!=(bitcount+7)/8){ + fprintf(stderr,"\nERROR: buffer length incorrect after build.\n"); + exit(1); + } + + + { + int begin=0; //=(rand()%TESTWORDS); + int ilen=(rand()%(TESTWORDS-begin)); + int bitoffset,bitcount=0; + unsigned long temp; + + for(j=0;j +#include +#include +#include "ogg.h" +#include "ivorbiscodec.h" +#include "codebook.h" +#include "misc.h" +#include "os.h" + + +/**** pack/unpack helpers ******************************************/ +int _ilog(unsigned int v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +static ogg_uint32_t decpack(long entry,long used_entry,long quantvals, + codebook *b,oggpack_buffer *opb,int maptype){ + ogg_uint32_t ret=0; + int j; + + switch(b->dec_type){ + + case 0: + return (ogg_uint32_t)entry; + + case 1: + if(maptype==1){ + /* vals are already read into temporary column vector here */ + for(j=0;jdim;j++){ + ogg_uint32_t off=entry%quantvals; + entry/=quantvals; + ret|=((ogg_uint16_t *)(b->q_val))[off]<<(b->q_bits*j); + } + }else{ + for(j=0;jdim;j++) + ret|=oggpack_read(opb,b->q_bits)<<(b->q_bits*j); + } + return ret; + + case 2: + for(j=0;jdim;j++){ + ogg_uint32_t off=entry%quantvals; + entry/=quantvals; + ret|=off<<(b->q_pack*j); + } + return ret; + + case 3: + return (ogg_uint32_t)used_entry; + + } + return 0; /* silence compiler */ +} + +/* 32 bit float (not IEEE; nonnormalized mantissa + + biased exponent) : neeeeeee eeemmmmm mmmmmmmm mmmmmmmm + Why not IEEE? It's just not that important here. */ + +static ogg_int32_t _float32_unpack(long val,int *point){ + long mant=val&0x1fffff; + int sign=val&0x80000000; + + *point=((val&0x7fe00000L)>>21)-788; + + if(mant){ + while(!(mant&0x40000000)){ + mant<<=1; + *point-=1; + } + if(sign)mant= -mant; + }else{ + *point=-9999; + } + return mant; +} + +/* choose the smallest supported node size that fits our decode table. + Legal bytewidths are 1/1 1/2 2/2 2/4 4/4 */ +static int _determine_node_bytes(long used, int leafwidth){ + + /* special case small books to size 4 to avoid multiple special + cases in repack */ + if(used<2) + return 4; + + if(leafwidth==3)leafwidth=4; + if(_ilog(3*used-6)+1 <= leafwidth*4) + return leafwidth/2?leafwidth/2:1; + return leafwidth; +} + +/* convenience/clarity; leaves are specified as multiple of node word + size (1 or 2) */ +static int _determine_leaf_words(int nodeb, int leafwidth){ + if(leafwidth>nodeb)return 2; + return 1; +} + +/* given a list of word lengths, number of used entries, and byte + width of a leaf, generate the decode table */ +static int _make_words(char *l,long n,ogg_uint32_t *r,long quantvals, + codebook *b, oggpack_buffer *opb,int maptype){ + long i,j,count=0; + long top=0; + ogg_uint32_t marker[33]; + + if(n<2){ + r[0]=0x80000000; + }else{ + memset(marker,0,sizeof(marker)); + + for(i=0;i>(length-j-1))&1; + if(chase>=top){ + top++; + r[chase*2]=top; + r[chase*2+1]=0; + }else + if(!r[chase*2+bit]) + r[chase*2+bit]=top; + chase=r[chase*2+bit]; + } + { + int bit=(entry>>(length-j-1))&1; + if(chase>=top){ + top++; + r[chase*2+1]=0; + } + r[chase*2+bit]= decpack(i,count++,quantvals,b,opb,maptype) | + 0x80000000; + } + + /* Look to see if the next shorter marker points to the node + above. if so, update it and repeat. */ + for(j=length;j>0;j--){ + if(marker[j]&1){ + marker[j]=marker[j-1]<<1; + break; + } + marker[j]++; + } + + /* prune the tree; the implicit invariant says all the longer + markers were dangling from our just-taken node. Dangle them + from our *new* node. */ + for(j=length+1;j<33;j++) + if((marker[j]>>1) == entry){ + entry=marker[j]; + marker[j]=marker[j-1]<<1; + }else + break; + } + } + } + + return 0; +} + +static int _make_decode_table(codebook *s,char *lengthlist,long quantvals, + oggpack_buffer *opb,int maptype){ + int i; + ogg_uint32_t *work; + + if(s->dec_nodeb==4){ + s->dec_table=_ogg_malloc((s->used_entries*2+1)*sizeof(*work)); + /* +1 (rather than -2) is to accommodate 0 and 1 sized books, + which are specialcased to nodeb==4 */ + if(_make_words(lengthlist,s->entries, + s->dec_table,quantvals,s,opb,maptype))return 1; + + return 0; + } + + work=alloca((s->used_entries*2-2)*sizeof(*work)); + if(_make_words(lengthlist,s->entries,work,quantvals,s,opb,maptype))return 1; + s->dec_table=_ogg_malloc((s->used_entries*(s->dec_leafw+1)-2)* + s->dec_nodeb); + + if(s->dec_leafw==1){ + switch(s->dec_nodeb){ + case 1: + for(i=0;iused_entries*2-2;i++) + ((unsigned char *)s->dec_table)[i]= + ((work[i] & 0x80000000UL) >> 24) | work[i]; + break; + case 2: + for(i=0;iused_entries*2-2;i++) + ((ogg_uint16_t *)s->dec_table)[i]= + ((work[i] & 0x80000000UL) >> 16) | work[i]; + break; + } + + }else{ + /* more complex; we have to do a two-pass repack that updates the + node indexing. */ + long top=s->used_entries*3-2; + if(s->dec_nodeb==1){ + unsigned char *out=(unsigned char *)s->dec_table; + + for(i=s->used_entries*2-4;i>=0;i-=2){ + if(work[i]&0x80000000UL){ + if(work[i+1]&0x80000000UL){ + top-=4; + out[top]=(work[i]>>8 & 0x7f)|0x80; + out[top+1]=(work[i+1]>>8 & 0x7f)|0x80; + out[top+2]=work[i] & 0xff; + out[top+3]=work[i+1] & 0xff; + }else{ + top-=3; + out[top]=(work[i]>>8 & 0x7f)|0x80; + out[top+1]=work[work[i+1]*2]; + out[top+2]=work[i] & 0xff; + } + }else{ + if(work[i+1]&0x80000000UL){ + top-=3; + out[top]=work[work[i]*2]; + out[top+1]=(work[i+1]>>8 & 0x7f)|0x80; + out[top+2]=work[i+1] & 0xff; + }else{ + top-=2; + out[top]=work[work[i]*2]; + out[top+1]=work[work[i+1]*2]; + } + } + work[i]=top; + } + }else{ + ogg_uint16_t *out=(ogg_uint16_t *)s->dec_table; + for(i=s->used_entries*2-4;i>=0;i-=2){ + if(work[i]&0x80000000UL){ + if(work[i+1]&0x80000000UL){ + top-=4; + out[top]=(work[i]>>16 & 0x7fff)|0x8000; + out[top+1]=(work[i+1]>>16 & 0x7fff)|0x8000; + out[top+2]=work[i] & 0xffff; + out[top+3]=work[i+1] & 0xffff; + }else{ + top-=3; + out[top]=(work[i]>>16 & 0x7fff)|0x8000; + out[top+1]=work[work[i+1]*2]; + out[top+2]=work[i] & 0xffff; + } + }else{ + if(work[i+1]&0x80000000UL){ + top-=3; + out[top]=work[work[i]*2]; + out[top+1]=(work[i+1]>>16 & 0x7fff)|0x8000; + out[top+2]=work[i+1] & 0xffff; + }else{ + top-=2; + out[top]=work[work[i]*2]; + out[top+1]=work[work[i+1]*2]; + } + } + work[i]=top; + } + } + } + + return 0; +} + +/* most of the time, entries%dimensions == 0, but we need to be + well defined. We define that the possible vales at each + scalar is values == entries/dim. If entries%dim != 0, we'll + have 'too few' values (values*dimentries); + int vals=b->entries>>((bits-1)*(b->dim-1)/b->dim); + + while(1){ + long acc=1; + long acc1=1; + int i; + for(i=0;idim;i++){ + acc*=vals; + acc1*=vals+1; + } + if(acc<=b->entries && acc1>b->entries){ + return(vals); + }else{ + if(acc>b->entries){ + vals--; + }else{ + vals++; + } + } + } +} + +void vorbis_book_clear(codebook *b){ + /* static book is not cleared; we're likely called on the lookup and + the static codebook belongs to the info struct */ + if(b->q_val)_ogg_free(b->q_val); + if(b->dec_table)_ogg_free(b->dec_table); + + memset(b,0,sizeof(*b)); +} + +int vorbis_book_unpack(oggpack_buffer *opb,codebook *s){ + char *lengthlist=NULL; + int quantvals=0; + long i,j; + int maptype; + + memset(s,0,sizeof(*s)); + + /* make sure alignment is correct */ + if(oggpack_read(opb,24)!=0x564342)goto _eofout; + + /* first the basic parameters */ + s->dim=oggpack_read(opb,16); + s->entries=oggpack_read(opb,24); + if(s->entries==-1)goto _eofout; + + /* codeword ordering.... length ordered or unordered? */ + switch((int)oggpack_read(opb,1)){ + case 0: + /* unordered */ + lengthlist=(char *)alloca(sizeof(*lengthlist)*s->entries); + + /* allocated but unused entries? */ + if(oggpack_read(opb,1)){ + /* yes, unused entries */ + + for(i=0;ientries;i++){ + if(oggpack_read(opb,1)){ + long num=oggpack_read(opb,5); + if(num==-1)goto _eofout; + lengthlist[i]=num+1; + s->used_entries++; + if(num+1>s->dec_maxlength)s->dec_maxlength=num+1; + }else + lengthlist[i]=0; + } + }else{ + /* all entries used; no tagging */ + s->used_entries=s->entries; + for(i=0;ientries;i++){ + long num=oggpack_read(opb,5); + if(num==-1)goto _eofout; + lengthlist[i]=num+1; + if(num+1>s->dec_maxlength)s->dec_maxlength=num+1; + } + } + + break; + case 1: + /* ordered */ + { + long length=oggpack_read(opb,5)+1; + + s->used_entries=s->entries; + lengthlist=(char *)alloca(sizeof(*lengthlist)*s->entries); + + for(i=0;ientries;){ + long num=oggpack_read(opb,_ilog(s->entries-i)); + if(num==-1)goto _eofout; + for(j=0;jentries;j++,i++) + lengthlist[i]=length; + s->dec_maxlength=length; + length++; + } + } + break; + default: + /* EOF */ + goto _eofout; + } + + + /* Do we have a mapping to unpack? */ + + if((maptype=oggpack_read(opb,4))>0){ + s->q_min=_float32_unpack(oggpack_read(opb,32),&s->q_minp); + s->q_del=_float32_unpack(oggpack_read(opb,32),&s->q_delp); + s->q_bits=oggpack_read(opb,4)+1; + s->q_seq=oggpack_read(opb,1); + + s->q_del>>=s->q_bits; + s->q_delp+=s->q_bits; + } + + switch(maptype){ + case 0: + + /* no mapping; decode type 0 */ + + /* how many bytes for the indexing? */ + /* this is the correct boundary here; we lose one bit to + node/leaf mark */ + s->dec_nodeb=_determine_node_bytes(s->used_entries,_ilog(s->entries)/8+1); + s->dec_leafw=_determine_leaf_words(s->dec_nodeb,_ilog(s->entries)/8+1); + s->dec_type=0; + + if(_make_decode_table(s,lengthlist,quantvals,opb,maptype)) goto _errout; + break; + + case 1: + + /* mapping type 1; implicit values by lattice position */ + quantvals=_book_maptype1_quantvals(s); + + /* dec_type choices here are 1,2; 3 doesn't make sense */ + { + /* packed values */ + long total1=(s->q_bits*s->dim+8)/8; /* remember flag bit */ + /* vector of column offsets; remember flag bit */ + long total2=(_ilog(quantvals-1)*s->dim+8)/8+(s->q_bits+7)/8; + + + if(total1<=4 && total1<=total2){ + /* use dec_type 1: vector of packed values */ + + /* need quantized values before */ + s->q_val=alloca(sizeof(ogg_uint16_t)*quantvals); + for(i=0;iq_val)[i]=oggpack_read(opb,s->q_bits); + + if(oggpack_eop(opb)){ + s->q_val=0; /* cleanup must not free alloca memory */ + goto _eofout; + } + + s->dec_type=1; + s->dec_nodeb=_determine_node_bytes(s->used_entries, + (s->q_bits*s->dim+8)/8); + s->dec_leafw=_determine_leaf_words(s->dec_nodeb, + (s->q_bits*s->dim+8)/8); + if(_make_decode_table(s,lengthlist,quantvals,opb,maptype)){ + s->q_val=0; /* cleanup must not free alloca memory */ + goto _errout; + } + + s->q_val=0; /* about to go out of scope; _make_decode_table + was using it */ + + }else{ + /* use dec_type 2: packed vector of column offsets */ + + /* need quantized values before */ + if(s->q_bits<=8){ + s->q_val=_ogg_malloc(quantvals); + for(i=0;iq_val)[i]=oggpack_read(opb,s->q_bits); + }else{ + s->q_val=_ogg_malloc(quantvals*2); + for(i=0;iq_val)[i]=oggpack_read(opb,s->q_bits); + } + + if(oggpack_eop(opb))goto _eofout; + + s->q_pack=_ilog(quantvals-1); + s->dec_type=2; + s->dec_nodeb=_determine_node_bytes(s->used_entries, + (_ilog(quantvals-1)*s->dim+8)/8); + s->dec_leafw=_determine_leaf_words(s->dec_nodeb, + (_ilog(quantvals-1)*s->dim+8)/8); + if(_make_decode_table(s,lengthlist,quantvals,opb,maptype))goto _errout; + + } + } + break; + case 2: + + /* mapping type 2; explicit array of values */ + quantvals=s->entries*s->dim; + /* dec_type choices here are 1,3; 2 is not possible */ + + if( (s->q_bits*s->dim+8)/8 <=4){ /* remember flag bit */ + /* use dec_type 1: vector of packed values */ + + s->dec_type=1; + s->dec_nodeb=_determine_node_bytes(s->used_entries,(s->q_bits*s->dim+8)/8); + s->dec_leafw=_determine_leaf_words(s->dec_nodeb,(s->q_bits*s->dim+8)/8); + if(_make_decode_table(s,lengthlist,quantvals,opb,maptype))goto _errout; + + }else{ + /* use dec_type 3: scalar offset into packed value array */ + + s->dec_type=3; + s->dec_nodeb=_determine_node_bytes(s->used_entries,_ilog(s->used_entries-1)/8+1); + s->dec_leafw=_determine_leaf_words(s->dec_nodeb,_ilog(s->used_entries-1)/8+1); + if(_make_decode_table(s,lengthlist,quantvals,opb,maptype))goto _errout; + + /* get the vals & pack them */ + s->q_pack=(s->q_bits+7)/8*s->dim; + s->q_val=_ogg_malloc(s->q_pack*s->used_entries); + + if(s->q_bits<=8){ + for(i=0;iused_entries*s->dim;i++) + ((unsigned char *)(s->q_val))[i]=oggpack_read(opb,s->q_bits); + }else{ + for(i=0;iused_entries*s->dim;i++) + ((ogg_uint16_t *)(s->q_val))[i]=oggpack_read(opb,s->q_bits); + } + } + break; + default: + goto _errout; + } + + if(oggpack_eop(opb))goto _eofout; + + return 0; + _errout: + _eofout: + vorbis_book_clear(s); + return -1; +} + +static inline ogg_uint32_t decode_packed_entry_number(codebook *book, + oggpack_buffer *b){ + ogg_uint32_t chase=0; + int read=book->dec_maxlength; + long lok = oggpack_look(b,read),i; + + while(lok<0 && read>1) + lok = oggpack_look(b, --read); + + if(lok<0){ + oggpack_adv(b,1); /* force eop */ + return -1; + } + + /* chase the tree with the bits we got */ + if(book->dec_nodeb==1){ + if(book->dec_leafw==1){ + + /* 8/8 */ + unsigned char *t=(unsigned char *)book->dec_table; + for(i=0;i>i)&1)]; + if(chase&0x80UL)break; + } + chase&=0x7fUL; + + }else{ + + /* 8/16 */ + unsigned char *t=(unsigned char *)book->dec_table; + for(i=0;i>i)&1; + int next=t[chase+bit]; + if(next&0x80){ + chase= (next<<8) | t[chase+bit+1+(!bit || t[chase]&0x80)]; + break; + } + chase=next; + } + chase&=0x7fffUL; + } + + }else{ + if(book->dec_nodeb==2){ + if(book->dec_leafw==1){ + + /* 16/16 */ + for(i=0;idec_table))[chase*2+((lok>>i)&1)]; + if(chase&0x8000UL)break; + } + chase&=0x7fffUL; + + }else{ + + /* 16/32 */ + ogg_uint16_t *t=(ogg_uint16_t *)book->dec_table; + for(i=0;i>i)&1; + int next=t[chase+bit]; + if(next&0x8000){ + chase= (next<<16) | t[chase+bit+1+(!bit || t[chase]&0x8000)]; + break; + } + chase=next; + } + chase&=0x7fffffffUL; + } + + }else{ + + for(i=0;idec_table))[chase*2+((lok>>i)&1)]; + if(chase&0x80000000UL)break; + } + chase&=0x7fffffffUL; + + } + } + + if(idec_type)return -1; + return decode_packed_entry_number(book,b); +} + +int decode_map(codebook *s, oggpack_buffer *b, ogg_int32_t *v, int point){ + ogg_uint32_t entry = decode_packed_entry_number(s,b); + int i; + if(oggpack_eop(b))return(-1); + + /* according to decode type */ + switch(s->dec_type){ + case 1:{ + /* packed vector of values */ + int mask=(1<q_bits)-1; + for(i=0;idim;i++){ + v[i]=entry&mask; + entry>>=s->q_bits; + } + break; + } + case 2:{ + /* packed vector of column offsets */ + int mask=(1<q_pack)-1; + for(i=0;idim;i++){ + if(s->q_bits<=8) + v[i]=((unsigned char *)(s->q_val))[entry&mask]; + else + v[i]=((ogg_uint16_t *)(s->q_val))[entry&mask]; + entry>>=s->q_pack; + } + break; + } + case 3:{ + /* offset into array */ + void *ptr=s->q_val+entry*s->q_pack; + + if(s->q_bits<=8){ + for(i=0;idim;i++) + v[i]=((unsigned char *)ptr)[i]; + }else{ + for(i=0;idim;i++) + v[i]=((ogg_uint16_t *)ptr)[i]; + } + break; + } + default: + return -1; + } + + /* we have the unpacked multiplicands; compute final vals */ + { + int shiftM=point-s->q_delp; + ogg_int32_t add=point-s->q_minp; + if(add>0) + add= s->q_min >> add; + else + add= s->q_min << -add; + + if(shiftM>0) + for(i=0;idim;i++) + v[i]= add + ((v[i] * s->q_del) >> shiftM); + else + for(i=0;idim;i++) + v[i]= add + ((v[i] * s->q_del) << -shiftM); + + if(s->q_seq) + for(i=1;idim;i++) + v[i]+=v[i-1]; + } + + return 0; +} + +/* returns 0 on OK or -1 on eof *************************************/ +/* decode vector / dim granularity guarding is done in the upper layer */ +long vorbis_book_decodevs_add(codebook *book,ogg_int32_t *a, + oggpack_buffer *b,int n,int point){ + if(book->used_entries>0){ + int step=n/book->dim; + ogg_int32_t *v = (ogg_int32_t *)alloca(sizeof(*v)*book->dim); + int i,j,o; + + for (j=0;jdim;i++,o+=step) + a[o]+=v[i]; + } + } + return 0; +} + +/* decode vector / dim granularity guarding is done in the upper layer */ +long vorbis_book_decodev_add(codebook *book,ogg_int32_t *a, + oggpack_buffer *b,int n,int point){ + if(book->used_entries>0){ + ogg_int32_t *v = (ogg_int32_t *)alloca(sizeof(*v)*book->dim); + int i,j; + + for(i=0;idim;j++) + a[i++]+=v[j]; + } + } + return 0; +} + +/* unlike the others, we guard against n not being an integer number + * of internally rather than in the upper layer (called only by + * floor0) */ +long vorbis_book_decodev_set(codebook *book,ogg_int32_t *a, + oggpack_buffer *b,int n,int point){ + if(book->used_entries>0){ + ogg_int32_t *v = (ogg_int32_t *)alloca(sizeof(*v)*book->dim); + int i,j; + + for(i=0;idim;j++) + a[i++]=v[j]; + } + }else{ + int i; + + for(i=0;iused_entries>0){ + + ogg_int32_t *v = (ogg_int32_t *)alloca(sizeof(*v)*book->dim); + long i,j; + int chptr=0; + long m=offset+n; + + for(i=offset;idim;j++){ + a[chptr++][i]+=v[j]; + if(chptr==ch){ + chptr=0; + i++; + } + } + } + } + + return 0; +} diff --git a/components/spotify/cspot/bell/tremor/codebook.h b/components/spotify/cspot/bell/tremor/codebook.h new file mode 100644 index 00000000..7519597c --- /dev/null +++ b/components/spotify/cspot/bell/tremor/codebook.h @@ -0,0 +1,65 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: basic shared codebook operations + + ********************************************************************/ + +#ifndef _V_CODEBOOK_H_ +#define _V_CODEBOOK_H_ + +#include "ogg.h" + +typedef struct codebook{ + long dim; /* codebook dimensions (elements per vector) */ + long entries; /* codebook entries */ + long used_entries; /* populated codebook entries */ + + int dec_maxlength; + void *dec_table; + int dec_nodeb; + int dec_leafw; + int dec_type; /* 0 = entry number + 1 = packed vector of values + 2 = packed vector of column offsets, maptype 1 + 3 = scalar offset into value array, maptype 2 */ + + ogg_int32_t q_min; + int q_minp; + ogg_int32_t q_del; + int q_delp; + int q_seq; + int q_bits; + int q_pack; + void *q_val; + +} codebook; + +extern void vorbis_book_clear(codebook *b); +extern int vorbis_book_unpack(oggpack_buffer *b,codebook *c); + +extern long vorbis_book_decode(codebook *book, oggpack_buffer *b); +extern long vorbis_book_decodevs_add(codebook *book, ogg_int32_t *a, + oggpack_buffer *b,int n,int point); +extern long vorbis_book_decodev_set(codebook *book, ogg_int32_t *a, + oggpack_buffer *b,int n,int point); +extern long vorbis_book_decodev_add(codebook *book, ogg_int32_t *a, + oggpack_buffer *b,int n,int point); +extern long vorbis_book_decodevv_add(codebook *book, ogg_int32_t **a, + long off,int ch, + oggpack_buffer *b,int n,int point); + +extern int _ilog(unsigned int v); + + +#endif diff --git a/components/spotify/cspot/bell/tremor/codec_internal.h b/components/spotify/cspot/bell/tremor/codec_internal.h new file mode 100644 index 00000000..5dd05998 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/codec_internal.h @@ -0,0 +1,213 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: libvorbis codec headers + + ********************************************************************/ + +#ifndef _V_CODECI_H_ +#define _V_CODECI_H_ + +#define CHUNKSIZE 1024 + +#include "codebook.h" +#include "ivorbiscodec.h" + +#define VI_TRANSFORMB 1 +#define VI_WINDOWB 1 +#define VI_TIMEB 1 +#define VI_FLOORB 2 +#define VI_RESB 3 +#define VI_MAPB 1 + +typedef void vorbis_info_floor; + +/* vorbis_dsp_state buffers the current vorbis audio + analysis/synthesis state. The DSP state belongs to a specific + logical bitstream ****************************************************/ +struct vorbis_dsp_state{ + vorbis_info *vi; + oggpack_buffer opb; + + ogg_int32_t **work; + ogg_int32_t **mdctright; + int out_begin; + int out_end; + + long lW; + long W; + + ogg_int64_t granulepos; + ogg_int64_t sequence; + ogg_int64_t sample_count; + +}; + + +/* Floor backend generic *****************************************/ + +extern vorbis_info_floor *floor0_info_unpack(vorbis_info *,oggpack_buffer *); +extern void floor0_free_info(vorbis_info_floor *); +extern int floor0_memosize(vorbis_info_floor *); +extern ogg_int32_t *floor0_inverse1(struct vorbis_dsp_state *, + vorbis_info_floor *,ogg_int32_t *); +extern int floor0_inverse2 (struct vorbis_dsp_state *,vorbis_info_floor *, + ogg_int32_t *buffer,ogg_int32_t *); + +extern vorbis_info_floor *floor1_info_unpack(vorbis_info *,oggpack_buffer *); +extern void floor1_free_info(vorbis_info_floor *); +extern int floor1_memosize(vorbis_info_floor *); +extern ogg_int32_t *floor1_inverse1(struct vorbis_dsp_state *, + vorbis_info_floor *,ogg_int32_t *); +extern int floor1_inverse2 (struct vorbis_dsp_state *,vorbis_info_floor *, + ogg_int32_t *buffer,ogg_int32_t *); + +typedef struct{ + int order; + long rate; + long barkmap; + + int ampbits; + int ampdB; + + int numbooks; /* <= 16 */ + char books[16]; + +} vorbis_info_floor0; + +typedef struct{ + char class_dim; /* 1 to 8 */ + char class_subs; /* 0,1,2,3 (bits: 1< Wed, 09 Oct 2002 22:00:00 -0500 + +Local variables: +mode: debian-changelog +End: diff --git a/components/spotify/cspot/bell/tremor/debian/control b/components/spotify/cspot/bell/tremor/debian/control new file mode 100644 index 00000000..f286e91c --- /dev/null +++ b/components/spotify/cspot/bell/tremor/debian/control @@ -0,0 +1,22 @@ +Source: libvorbisidec +Section: libs +Priority: optional +Maintainer: Christopher L Cheney +Build-Depends: autotools-dev, debhelper (>> 4.0.18), devscripts, gawk +Standards-Version: 3.5.7.0 + +Package: libvorbisidec1 +Architecture: any +Section: libs +Depends: ${shlibs:Depends} +Description: Ogg Bitstream Library + Libogg is a library for manipulating ogg bitstreams. It handles + both making ogg bitstreams and getting packets from ogg bitstreams. + +Package: libvorbisidec-dev +Architecture: any +Section: devel +Depends: libvorbisidec1 (= ${Source-Version}), libc6-dev +Description: Ogg Bitstream Library Development + The libogg-dev package contains the header files and documentation + needed to develop applications with libogg. diff --git a/components/spotify/cspot/bell/tremor/debian/copyright b/components/spotify/cspot/bell/tremor/debian/copyright new file mode 100644 index 00000000..ef98ddd3 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/debian/copyright @@ -0,0 +1,37 @@ +This package was debianized by Christopher L Cheney on +Wed, 09 Oct 2002 22:00:00 -0500. + +It was downloaded from cvs. + +Upstream Author(s): Monty + +Copyright: +Copyright (c) 2002, Xiph.org Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.Org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/components/spotify/cspot/bell/tremor/debian/libvorbisidec-dev.install b/components/spotify/cspot/bell/tremor/debian/libvorbisidec-dev.install new file mode 100644 index 00000000..5c3ccf93 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/debian/libvorbisidec-dev.install @@ -0,0 +1,8 @@ +debian/tmp/usr/include/tremor/config_types.h +debian/tmp/usr/include/tremor/ivorbiscodec.h +debian/tmp/usr/include/tremor/ivorbisfile.h +debian/tmp/usr/include/tremor/ogg.h +debian/tmp/usr/include/tremor/os_types.h +debian/tmp/usr/lib/libvorbisidec.a +debian/tmp/usr/lib/libvorbisidec.la +debian/tmp/usr/lib/libvorbisidec.so diff --git a/components/spotify/cspot/bell/tremor/debian/libvorbisidec1.install b/components/spotify/cspot/bell/tremor/debian/libvorbisidec1.install new file mode 100644 index 00000000..b824d1eb --- /dev/null +++ b/components/spotify/cspot/bell/tremor/debian/libvorbisidec1.install @@ -0,0 +1 @@ +debian/tmp/usr/lib/libvorbisidec.so.* diff --git a/components/spotify/cspot/bell/tremor/debian/rules b/components/spotify/cspot/bell/tremor/debian/rules new file mode 100644 index 00000000..c684884e --- /dev/null +++ b/components/spotify/cspot/bell/tremor/debian/rules @@ -0,0 +1,151 @@ +#!/usr/bin/make -f +# Sample debian/rules that uses debhelper. +# GNU copyright 1997 to 1999 by Joey Hess. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +# This is the debhelper compatibility version to use. +export DH_COMPAT=4 + +# This has to be exported to make some magic below work. +export DH_OPTIONS + +# These are used for cross-compiling and for saving the configure script +# from having to guess our platform (since we know it already) +DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) + +objdir = $(CURDIR)/obj-$(DEB_BUILD_GNU_TYPE) + +ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS))) + CFLAGS += -g +endif +ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) + INSTALL_PROGRAM += -s +endif + +configure: configure-stamp +configure-stamp: + dh_testdir + + # make build directory + mkdir $(objdir) + + # run configure with build tree $(objdir) + # change ../configure to ../autogen.sh for CVS build + cd $(objdir) && \ + ../configure --build=$(DEB_BUILD_GNU_TYPE) --host=$(DEB_HOST_GNU_TYPE) \ + --prefix=/usr + + touch configure-stamp + +build: build-stamp +build-stamp: configure-stamp + dh_testdir + + cd $(objdir) && \ + $(MAKE) + + touch build-stamp + +autotools: + OLDDATESUB=`./config.sub -t | tr -d -` ;\ + OLDDATEGUESS=`./config.guess -t | tr -d -` ;\ + NEWDATESUB=`/usr/share/misc/config.sub -t | tr -d -` ;\ + NEWDATEGUESS=`/usr/share/misc/config.guess -t | tr -d -` ;\ + if [ $$OLDDATESUB -lt $$NEWDATESUB -o \ + $$OLDDATEGUESS -lt $$NEWDATEGUESS ]; then \ + dch -a -p "GNU config automated update: config.sub\ + ($$OLDDATESUB to $$NEWDATESUB), config.guess\ + ($$OLDDATEGUESS to $$NEWDATEGUESS)" ;\ + cp -f /usr/share/misc/config.sub config.sub ;\ + cp -f /usr/share/misc/config.guess config.guess ;\ + echo WARNING: GNU config scripts updated from master copies 1>&2 ;\ + fi + +debian-clean: + dh_testdir + dh_testroot + + dh_clean + +clean: autotools + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + + # Remove build tree + rm -rf $(objdir) + + # if Makefile exists run distclean + if test -f Makefile; then \ + $(MAKE) distclean; \ + fi + + #if test -d CVS; then \ + $(MAKE) cvs-clean ;\ + fi + + dh_clean + +install: DH_OPTIONS= +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + cd $(objdir) && \ + $(MAKE) install DESTDIR=$(CURDIR)/debian/tmp + + dh_install --list-missing + +# This single target is used to build all the packages, all at once, or +# one at a time. So keep in mind: any options passed to commands here will +# affect _all_ packages. Anything you want to only affect one package +# should be put in another target, such as the install target. +binary-common: + dh_testdir + dh_testroot +# dh_installxfonts + dh_installchangelogs + dh_installdocs + dh_installexamples +# dh_installmenu +# dh_installdebconf +# dh_installlogrotate +# dh_installemacsen +# dh_installpam +# dh_installmime +# dh_installinit +# dh_installcron +# dh_installinfo +# dh_undocumented + dh_installman + dh_strip + dh_link + dh_compress + dh_fixperms + dh_makeshlibs -V + dh_installdeb +# dh_perl + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +# Build architecture independant packages using the common target. +binary-indep: build install +# $(MAKE) -f debian/rules DH_OPTIONS=-i binary-common + +# Build architecture dependant packages using the common target. +binary-arch: build install + $(MAKE) -f debian/rules DH_OPTIONS=-a binary-common + +# Any other binary targets build just one binary package at a time. +binary-%: build install + $(MAKE) -f debian/rules binary-common DH_OPTIONS=-p$* + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure diff --git a/components/spotify/cspot/bell/tremor/doc/OggVorbis_File.html b/components/spotify/cspot/bell/tremor/doc/OggVorbis_File.html new file mode 100644 index 00000000..9201d18f --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/OggVorbis_File.html @@ -0,0 +1,132 @@ + + + +Tremor - datatype - OggVorbis_File + + + + + + + + + +

Tremor documentation

Tremor version 1.0 - 20020403

+ +

OggVorbis_File

+ +

declared in "ivorbisfile.h"

+ +

+The OggVorbis_File structure defines an Ogg Vorbis file. +

+ +This structure is used in all libvorbisidec routines. Before it can be used, +it must be initialized by ov_open() or ov_open_callbacks(). + +

+After use, the OggVorbis_File structure must be deallocated with a +call to ov_clear(). + +

+Once a file or data source is opened successfully by libvorbisidec +(using ov_open() or ov_open_callbacks()), it is owned by +libvorbisidec. The file should not be used by any other applications or +functions outside of the libvorbisidec API. The file must not be closed +directly by the application at any time after a successful open; +libvorbisidec expects to close the file within ov_clear(). +

+If the call to ov_open() or ov_open_callbacks() fails, +libvorbisidec does not assume ownership of the file and the +application is expected to close it if necessary. + +

+ + + + +
+
typedef struct {
+  void             *datasource; /* Pointer to a FILE *, etc. */
+  int              seekable;
+  ogg_int64_t      offset;
+  ogg_int64_t      end;
+  ogg_sync_state   oy; 
+
+  /* If the FILE handle isn't seekable (eg, a pipe), only the current
+     stream appears */
+  int              links;
+  ogg_int64_t      *offsets;
+  ogg_int64_t      *dataoffsets;
+  long             *serialnos;
+  ogg_int64_t      *pcmlengths;
+  vorbis_info      *vi;
+  vorbis_comment   *vc;
+
+  /* Decoding working state local storage */
+  ogg_int64_t      pcm_offset;
+  int              ready_state;
+  long             current_serialno;
+  int              current_link;
+
+  ogg_int64_t      bittrack;
+  ogg_int64_t      samptrack;
+
+  ogg_stream_state os; /* take physical pages, weld into a logical
+                          stream of packets */
+  vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
+  vorbis_block     vb; /* local working space for packet->PCM decode */
+
+  ov_callbacks callbacks;
+
+} OggVorbis_File;
+
+ +

Relevant Struct Members

+
+
datasource
+ +
Pointer to file or other ogg source. When using stdio based +file/stream access, this field contains a FILE pointer. When using +custom IO via callbacks, libvorbisidec treats this void pointer as a +black box only to be passed to the callback routines provided by the +application.
+ +
seekable
+
Read-only int indicating whether file is seekable. E.g., a physical file is seekable, a pipe isn't.
+
links
+
Read-only int indicating the number of logical bitstreams within the physical bitstream.
+
ov_callbacks
+
Collection of file manipulation routines to be used on this data source. When using stdio/FILE access via ov_open(), the callbacks will be filled in with stdio calls or wrappers to stdio calls.
+
+ +

Notes

+ +

Tremor requires a native 64 bit integer type to compile and +function; The GNU build system will locate and typedef +ogg_int64_t to the appropriate native type. If not using the +GNU build tools, you will need to define ogg_int64_t as a +64-bit type inside your system's project file/Makefile, etc. On win32, +for example, this should be defined as __int64. +

+ + +

+


+ + + + + + + + +

copyright © 2002 Xiph.org

Ogg Vorbis

Tremor documentation

Tremor version 1.0 - 20020403

+ + + + diff --git a/components/spotify/cspot/bell/tremor/doc/build.html b/components/spotify/cspot/bell/tremor/doc/build.html new file mode 100644 index 00000000..6f0f4eea --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/build.html @@ -0,0 +1,111 @@ + + + +Tremor - Build + + + + + + + + + +

Tremor documentation

Tremor version 1.0 - 20020403

+ +

Tremor: Building libvorbisidec

+ +

+ +The C source in the Tremor package will build on any ANSI C compiler +and function completely and properly on any platform. The included +build system assumes GNU build system and make tools (m4, automake, +autoconf, libtool and gmake). GCC is not required, although GCC is +the most tested compiler. To build using GNU tools, type in the +source directory: + +

+


+./autogen.sh
+gmake
+
+

+or if GNU make is the standard make on the build system: +


+./autogen.sh
+make
+
+ +

+Currently, the source implements playback in pure C on all platforms +except ARM, where a [currently] small amount of assembly (see the file +asm_arm.h) is used to implement 64 bit math operations and +fast LSP computation. If building on ARM without the benefit of GNU +build system tools, be sure that _ARM_ASSEM_ is #defined by +the build system if this assembly is desired, else the resulting +library will use whatever 64 bit math builtins the compiler +implements. + +

+No math library is required by this source. No floating point +operations are used at any point in either setup or decode. This +decoder library will properly decode any past, current or future +Vorbis I file or stream. + +

+The GNU build system produces static and, when supported by the OS, +dynamic libraries named 'libvorbisidec'. This library exposes an API +nearly identical to the BSD reference library's 'libvorbisfile', +including all the features familiar to users of vorbisfile. This API +is similar enough that the proper header file to include is named +'ivorbisfile.h', included in the source build directory. +Lower level libvorbis-style headers and structures are +in 'ivorbiscodec.h', also included in the source build directory. A +simple example program, ivorbisfile_example.c, can be built with 'make +ivorbisfile_example'. +

+(We've summarized differences between the free, +reference vorbisfile library and Tremor's libvorbisidec in a separate +document.) + +

Notes

+ +

Tremor requires a native 64 bit integer type to compile and +function; The GNU build system will locate and typedef +ogg_int64_t to the appropriate native type. If not using the +GNU build tools, you will need to define ogg_int64_t as a +64-bit type inside your system's project file/Makefile, etc. On win32, +for example, this should be defined as __int64. +

+ +

+


+ + + + + + + + +

copyright © 2002 Xiph.org

Ogg Vorbis

Tremor documentation

Tremor version 1.0 - 20020403

+ + + + + + + + + + + + + + + + + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/callbacks.html b/components/spotify/cspot/bell/tremor/doc/callbacks.html new file mode 100644 index 00000000..9a6d3920 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/callbacks.html @@ -0,0 +1,113 @@ + + + +Tremor - Callbacks and non-stdio I/O + + + + + + + + + +

Tremor documentation

Tremor version 1.0 - 20020403

+ +

Callbacks and non-stdio I/O

+ +Although stdio is convenient and nearly universally implemented as per +ANSI C, it is not suited to all or even most potential uses of Vorbis. +For additional flexibility, embedded applications may provide their +own I/O functions for use with Tremor when stdio is unavailable or not +suitable. One common example is decoding a Vorbis stream from a +memory buffer.

+ +Use custom I/O functions by populating an ov_callbacks structure and calling ov_open_callbacks() or ov_test_callbacks() rather than the +typical ov_open() or ov_test(). Past the open call, use of +libvorbisidec is identical to using it with stdio. + +

Read function

+ +The read-like function provided in the read_func field is +used to fetch the requested amount of data. It expects the fetch +operation to function similar to file-access, that is, a multiple read +operations will retrieve contiguous sequential pieces of data, +advancing a position cursor after each read.

+ +The following behaviors are also expected:

+

    +
  • a return of '0' indicates end-of-data (if the by-thread errno is unset) +
  • short reads mean nothing special (short reads are not treated as error conditions) +
  • a return of zero with the by-thread errno set to nonzero indicates a read error +
+

+ +

Seek function

+ +The seek-like function provided in the seek_func field is +used to request non-sequential data access by libvorbisidec, moving +the access cursor to the requested position.

+ +libvorbisidec expects the following behavior: +

    +
  • The seek function must always return -1 (failure) if the given +data abstraction is not seekable. It may choose to always return -1 +if the application desires libvorbisidec to treat the Vorbis data +strictly as a stream (which makes for a less expensive open +operation).

    + +

  • If the seek function initially indicates seekability, it must +always succeed upon being given a valid seek request.

    + +

  • The seek function must implement all of SEEK_SET, SEEK_CUR and +SEEK_END. The implementation of SEEK_END should set the access cursor +one past the last byte of accessible data, as would stdio +fseek()

    +

+ +

Close function

+ +The close function should deallocate any access state used by the +passed in instance of the data access abstraction and invalidate the +instance handle. The close function is assumed to succeed.

+ +One common use of callbacks and the close function is to change the +behavior of libvorbisidec with respect to file closure for applications +that must fclose data files themselves. By passing +the normal stdio calls as callback functions, but passing a +close_func that does nothing, an application may call ov_clear() and then fclose() the +file originally passed to libvorbisidec. + +

Tell function

+ +The tell function is intended to mimic the +behavior of ftell() and must return the byte position of the +next data byte that would be read. If the data access cursor is at +the end of the 'file' (pointing to one past the last byte of data, as +it would be after calling fseek(file,SEEK_END,0)), the tell +function must return the data position (and thus the total file size), +not an error.

+ +The tell function need not be provided if the data IO abstraction is +not seekable.
+


+ + + + + + + + +

copyright © 2002 Xiph.org

Ogg Vorbis

Tremor documentation

Tremor version 1.0 - 20020403

+ + + + diff --git a/components/spotify/cspot/bell/tremor/doc/datastructures.html b/components/spotify/cspot/bell/tremor/doc/datastructures.html new file mode 100644 index 00000000..2b3da078 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/datastructures.html @@ -0,0 +1,61 @@ + + + +Tremor - Base Data Structures + + + + + + + + + +

Tremor documentation

Tremor version 1.0 - 20020403

+ +

Base Data Structures

+

There are several data structures used to hold file and bitstream information during libvorbisidec decoding. These structures are declared in "ivorbisfile.h" and "ivorbiscodec.h". +

+

When using libvorbisidec, it's not necessary to know about most of the contents of these data structures, but it may be helpful to understand what they contain. +

+ + + + + + + + + + + + + + + + + + + + + + +
datatypepurpose
OggVorbis_FileThis structure represents the basic file information. It contains + a pointer to the physical file or bitstream and various information about that bitstream.
vorbis_commentThis structure contains the file comments. It contains + a pointer to unlimited user comments, information about the number of comments, and a vendor description.
vorbis_infoThis structure contains encoder-related information about the bitstream. It includes encoder info, channel info, and bitrate limits.
ov_callbacksThis structure contains pointers to the application-specified file manipulation routines set for use by ov_open_callbacks(). See also the provided document on using application-provided callbacks instead of stdio.
+ +

+


+ + + + + + + + +

copyright © 2002 Xiph.org

Ogg Vorbis

Tremor documentation

Tremor version 1.0 - 20020403

+ + + + diff --git a/components/spotify/cspot/bell/tremor/doc/decoding.html b/components/spotify/cspot/bell/tremor/doc/decoding.html new file mode 100644 index 00000000..1f61b47f --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/decoding.html @@ -0,0 +1,82 @@ + + + +Tremor - Decoding + + + + + + + + + +

Tremor documentation

Tremor version 1.0 - 20020403

+ +

Decoding

+ +

+All libivorbisdec decoding routines are declared in "ivorbisfile.h". +

+ +After initialization, decoding audio +is as simple as calling ov_read(). This +function works similarly to reading from a normal file using +read().

+ +However, a few differences are worth noting: + +

multiple stream links

+ +A Vorbis stream may consist of multiple sections (called links) that +encode differing numbers of channels or sample rates. It is vitally +important to pay attention to the link numbers returned by ov_read and handle audio changes that may +occur at link boundaries. Such multi-section files do exist in the +wild and are not merely a specification curiosity. + +

returned data amount

+ +ov_read does not attempt to completely fill +a large, passed in data buffer; it merely guarantees that the passed +back data does not overflow the passed in buffer size. Large buffers +may be filled by iteratively looping over calls to ov_read (incrementing the buffer pointer) +until the original buffer is filled. + +

file cursor position

+ +Vorbis files do not necessarily start at a sample number or time offset +of zero. Do not be surprised if a file begins at a positive offset of +several minutes or hours, such as would happen if a large stream (such +as a concert recording) is chopped into multiple seperate files. + +

+ + + + + + + + + +
functionpurpose
ov_readThis function makes up the main chunk of a decode loop. It takes an +OggVorbis_File structure, which must have been initialized by a previous +call to ov_open().
+ +

+


+ + + + + + + + +

copyright © 2002 Xiph.org

Ogg Vorbis

Tremor documentation

Tremor version 1.0 - 20020403

+ + + + diff --git a/components/spotify/cspot/bell/tremor/doc/diff.html b/components/spotify/cspot/bell/tremor/doc/diff.html new file mode 100644 index 00000000..ae0b908f --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/diff.html @@ -0,0 +1,67 @@ + + + +Tremor - Vorbisfile Differences + + + + + + + + + +

Tremor documentation

Tremor version 1.0 - 20020403

+ +

Tremor / Vorbisfile API Differences

+ +

+ +The Tremor libvorbisidec library exposes an API intended to be as +similar as possible to the familiar 'vorbisfile' library included with +the open source Vorbis reference libraries distributed for free by +Xiph.org. Differences are summarized below.

+ +

OggVorbis_File structure

+ +The bittrack and samptrack fields in the OggVorbis_File structure are changed to +64 bit integers in Tremor, from doubles in vorbisfile. + +

Time-related seek and tell function calls

+ +The ov_time_total() and ov_time_tell() functions return milliseconds as +64 bit integers in Tremor. In vorbisfile, these functions returned +seconds as doubles.

+ +In Tremor, the ov_time_seek() and ov_time_seek_page() calls take +seeking positions in milliseconds as 64 bit integers, rather than in +seconds as doubles as in Vorbisfile.

+ +

Reading decoded data

+ +Tremor ov_read() always returns data as +signed 16 bit interleaved PCM in host byte order. As such, it does not +take arguments to request specific signedness, byte order or bit depth +as in Vorbisfile.

+ +Tremor does not implement ov_read_float().

+ + +

+


+ + + + + + + + +

copyright © 2002 Xiph.org

Ogg Vorbis

Tremor documentation

Tremor version 1.0 - 20020403

+ + + + diff --git a/components/spotify/cspot/bell/tremor/doc/example.html b/components/spotify/cspot/bell/tremor/doc/example.html new file mode 100644 index 00000000..2b9a1dd8 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/example.html @@ -0,0 +1,205 @@ + + + +Tremor - Example Code + + + + + + + + + +

Tremor documentation

Tremor version 1.0 - 20020403

+ +

Example Code

+ +

+The following is a run-through of the decoding example program supplied +with libvorbisidec, ivorbisfile_example.c. +This program takes a vorbis bitstream from stdin and writes raw pcm to stdout. + +

+First, relevant headers, including vorbis-specific "ivorbiscodec.h" and "ivorbisfile.h" have to be included. + +

+ + + + +
+

+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "ivorbiscodec.h"
+#include "ivorbisfile.h"
+
+
+

+We also have to make a concession to Windows users here. If we are using windows for decoding, we must declare these libraries so that we can set stdin/stdout to binary. +

+ + + + +
+

+#ifdef _WIN32
+#include <io.h>
+#include <fcntl.h>
+#endif
+
+
+

+Next, a buffer for the pcm audio output is declared. + +

+ + + + +
+

+char pcmout[4096];
+
+
+ +

Inside main(), we declare our primary OggVorbis_File structure. We also declare a few other helpful variables to track out progress within the file. +Also, we make our final concession to Windows users by setting the stdin and stdout to binary mode. +

+ + + + +
+

+int main(int argc, char **argv){
+  OggVorbis_File vf;
+  int eof=0;
+  int current_section;
+
+#ifdef _WIN32
+  _setmode( _fileno( stdin ), _O_BINARY );
+  _setmode( _fileno( stdout ), _O_BINARY );
+#endif
+
+
+ +

ov_open() must be +called to initialize the OggVorbis_File structure with default values. +ov_open() also checks to ensure that we're reading Vorbis format and not something else. + +

+ + + + +
+

+  if(ov_open(stdin, &vf, NULL, 0) < 0) {
+      fprintf(stderr,"Input does not appear to be an Ogg bitstream.\n");
+      exit(1);
+  }
+
+
+
+ +

+We're going to pull the channel and bitrate info from the file using ov_info() and show them to the user. +We also want to pull out and show the user a comment attached to the file using ov_comment(). + +

+ + + + +
+

+  {
+    char **ptr=ov_comment(&vf,-1)->user_comments;
+    vorbis_info *vi=ov_info(&vf,-1);
+    while(*ptr){
+      fprintf(stderr,"%s\n",*ptr);
+      ++ptr;
+    }
+    fprintf(stderr,"\nBitstream is %d channel, %ldHz\n",vi->channels,vi->rate);
+    fprintf(stderr,"\nDecoded length: %ld samples\n",
+            (long)ov_pcm_total(&vf,-1));
+    fprintf(stderr,"Encoded by: %s\n\n",ov_comment(&vf,-1)->vendor);
+  }
+  
+
+
+ +

+Here's the read loop: + +

+ + + + +
+

+
+  while(!eof){
+    long ret=ov_read(&vf,pcmout,sizeof(pcmout),¤t_section);
+    if (ret == 0) {
+      /* EOF */
+      eof=1;
+    } else if (ret < 0) {
+      /* error in the stream.  Not a problem, just reporting it in
+	 case we (the app) cares.  In this case, we don't. */
+    } else {
+      /* we don't bother dealing with sample rate changes, etc, but
+	 you'll have to*/
+      fwrite(pcmout,1,ret,stdout);
+    }
+  }
+
+  
+
+
+ +

+The code is reading blocks of data using ov_read(). +Based on the value returned, we know if we're at the end of the file or have invalid data. If we have valid data, we write it to the pcm output. + +

+Now that we've finished playing, we can pack up and go home. It's important to call ov_clear() when we're finished. + +

+ + + + +
+

+
+  ov_clear(&vf);
+    
+  fprintf(stderr,"Done.\n");
+  return(0);
+}
+
+
+ +

+ +

+


+ + + + + + + + +

copyright © 2002 Xiph.org

Ogg Vorbis

Tremor documentation

Tremor version 1.0 - 20020403

+ + + + diff --git a/components/spotify/cspot/bell/tremor/doc/fileinfo.html b/components/spotify/cspot/bell/tremor/doc/fileinfo.html new file mode 100644 index 00000000..53dfd380 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/fileinfo.html @@ -0,0 +1,95 @@ + + + +Tremor - File Information + + + + + + + + + +

Tremor documentation

Tremor version 1.0 - 20020403

+ +

File Information

+

Libvorbisidec contains many functions to get information about bitstream attributes and decoding status. +

+All libvorbisidec file information routines are declared in "ivorbisfile.h". +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
functionpurpose
ov_bitrateReturns the average bitrate of the current logical bitstream.
ov_bitrate_instantReturns the exact bitrate since the last call of this function, or -1 if at the beginning of the bitream or no new information is available.
ov_streamsGives the number of logical bitstreams within the current physical bitstream.
ov_seekableIndicates whether the bitstream is seekable.
ov_serialnumberReturns the unique serial number of the specified logical bitstream.
ov_raw_totalReturns the total (compressed) bytes in a physical or logical seekable bitstream.
ov_pcm_totalReturns the total number of samples in a physical or logical seekable bitstream.
ov_time_totalReturns the total time length in seconds of a physical or logical seekable bitstream.
ov_raw_tellReturns the byte location of the next sample to be read, giving the approximate location in the stream that the decoding engine has reached.
ov_pcm_tellReturns the sample location of the next sample to be read, giving the approximate location in the stream that the decoding engine has reached.
ov_time_tellReturns the time location of the next sample to be read, giving the approximate location in the stream that the decoding engine has reached.
ov_infoReturns the vorbis_info struct for a specific bitstream section.
ov_commentReturns attached comments for the current bitstream.
+ +

+


+ + + + + + + + +

copyright © 2002 Xiph.org

Ogg Vorbis

Tremor documentation

Tremor version 1.0 - 20020403

+ + + + diff --git a/components/spotify/cspot/bell/tremor/doc/index.html b/components/spotify/cspot/bell/tremor/doc/index.html new file mode 100644 index 00000000..671f13fb --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/index.html @@ -0,0 +1,53 @@ + + + +Tremor - Documentation + + + + + + + + + +

Tremor documentation

Tremor version 1.0 - 20020403

+ +

Tremor Documentation

+ +

+ +The Tremor Vorbis I stream and file decoder provides an embeddable, +integer-only library [libvorbisidec] intended for decoding all current +and future Vorbis I compliant streams. The Tremor libvorbisidec +library exposes an API intended to be as similar as possible to the +familiar 'vorbisfile' library included with the open source Vorbis +reference libraries distributed for free by Xiph.org.

+ +Tremor can be used along with any ANSI compliant stdio implementation +for file/stream access, or use custom stream i/o routines provided by +the embedded environment. Both uses are described in detail in this +documentation. + +

+Building libvorbisidec
+API overview
+API reference
+Example code
+Tremor / vorbisfile API differences
+ +

+


+ + + + + + + + +

copyright © 2002 Xiph.org

Ogg Vorbis

Tremor documentation

Tremor version 1.0 - 20020403

+ + + + diff --git a/components/spotify/cspot/bell/tremor/doc/initialization.html b/components/spotify/cspot/bell/tremor/doc/initialization.html new file mode 100644 index 00000000..f9f68073 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/initialization.html @@ -0,0 +1,101 @@ + + + +Tremor - Setup/Teardown + + + + + + + + + +

Tremor documentation

Tremor version 1.0 - 20020403

+ +

Setup/Teardown

In order to decode audio using +libvorbisidec, a bitstream containing Vorbis audio must be properly +initialized before decoding and cleared when decoding is finished. +The simplest possible case is to use fopen() to open a Vorbis +file and then pass the FILE * to an ov_open() call. A successful return code from ov_open() indicates the file is ready for use. +Once the file is no longer needed, ov_clear() is used to close the file and +deallocate decoding resources. Do not call fclose() on the +file; libvorbisidec does this in the ov_clear() call. + +

+All libvorbisidec initialization and deallocation routines are declared in "ivorbisfile.h". +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
functionpurpose
ov_openInitializes the Ogg Vorbis bitstream with a pointer to a bitstream and default values. This must be called before other functions in the library may be + used.
ov_open_callbacksInitializes the Ogg Vorbis bitstream with a pointer to a bitstream, default values, and custom file/bitstream manipulation routines. Used instead of ov_open() when working with other than stdio based I/O.
ov_testPartially opens a file just far enough to determine if the file +is an Ogg Vorbis file or not. A successful return indicates that the +file appears to be an Ogg Vorbis file, but the OggVorbis_File struct is not yet fully +initialized for actual decoding. After a successful return, the file +may be closed using ov_clear() or fully +opened for decoding using ov_test_open().

This call is intended to +be used as a less expensive file open test than a full ov_open().

+Note that libvorbisidec owns the passed in file resource is it returns success; do not fclose() files owned by libvorbisidec.

ov_test_callbacksAs above but allowing application-define I/O callbacks.

+Note that libvorbisidec owns the passed in file resource is it returns success; do not fclose() files owned by libvorbisidec.

ov_test_open +Finish opening a file after a successful call to ov_test() or ov_test_callbacks().
ov_clear Closes the + bitstream and cleans up loose ends. Must be called when + finished with the bitstream. After return, the OggVorbis_File struct is + invalid and may not be used before being initialized again + before begin reinitialized. + +
+ +

+


+ + + + + + + + +

copyright © 2002 Xiph.org

Ogg Vorbis

Tremor documentation

Tremor version 1.0 - 20020403

+ + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_bitrate.html b/components/spotify/cspot/bell/tremor/doc/ov_bitrate.html new file mode 100644 index 00000000..65ebfc39 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_bitrate.html @@ -0,0 +1,72 @@ + + + +Tremor - function - ov_bitrate + + + + + + + + + +

Tremor documentation

Tremor version 1.0 - 20020403

+ +

ov_bitrate

+ +

declared in "ivorbisfile.h";

+ +

This function returns the average bitrate for the specified logical bitstream. This may be different from the ov_info->nominal_bitrate value, as it is based on the actual average for this bitstream if the file is seekable. +

Nonseekable files will return the nominal bitrate setting or the average of the upper and lower bounds, if any of these values are set. +

+ +

+ + + + +
+

+long ov_bitrate(OggVorbis_File *vf,int i);
+
+
+ +

Parameters

+
+
vf
+
A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
+
i
+
Link to the desired logical bitstream. For nonseekable files, this argument is ignored. To retrieve the bitrate for the entire bitstream, this parameter should be set to -1.
+
+ + +

Return Values

+
+
  • OV_EINVAL indicates that an invalid argument value was submitted or that the stream represented by vf is not open.
  • +
  • OV_FALSE means the call returned a 'false' status, which in this case most likely indicates that the file is nonseekable and the upper, lower, and nominal bitrates were unset. +
  • n indicates the bitrate for the given logical bitstream or the entire + physical bitstream. If the file is open for random (seekable) access, it will + find the *actual* average bitrate. If the file is streaming (nonseekable), it + returns the nominal bitrate (if set) or else the average of the + upper/lower bounds (if set).
  • +
    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_bitrate_instant.html b/components/spotify/cspot/bell/tremor/doc/ov_bitrate_instant.html new file mode 100644 index 00000000..874671fa --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_bitrate_instant.html @@ -0,0 +1,65 @@ + + + +Tremor - function - ov_bitrate + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_bitrate_instant

    + +

    declared in "ivorbisfile.h";

    + +

    Used to find the most recent bitrate played back within the file. Will return 0 if the bitrate has not changed or it is the beginning of the file. + +

    + + + + +
    +
    
    +long ov_bitrate_instant(OggVorbis_File *vf);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions. +
    + + +

    Return Values

    +
    +
  • 0 indicates the beginning of the file or unchanged bitrate info.
  • +
  • n indicates the actual bitrate since the last call.
  • +
  • OV_FALSE indicates that playback is not in progress, and thus there is no instantaneous bitrate information to report.
  • +
  • OV_EINVAL indicates that the stream represented by vf is not open.
  • +
    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_callbacks.html b/components/spotify/cspot/bell/tremor/doc/ov_callbacks.html new file mode 100644 index 00000000..776352d6 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_callbacks.html @@ -0,0 +1,78 @@ + + + +Tremor - datatype - ov_callbacks + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_callbacks

    + +

    declared in "ivorbiscodec.h"

    + +

    +The ov_callbacks structure contains file manipulation function prototypes necessary for opening, closing, seeking, and location. + +

    +The ov_callbacks structure does not need to be user-defined if you are +working with stdio-based file manipulation; the ov_open() call provides default callbacks for +stdio. ov_callbacks are defined and passed to ov_open_callbacks() when +implementing non-stdio based stream manipulation (such as playback +from a memory buffer). +

    + + + + + +
    +
    typedef struct {
    +  size_t (*read_func)  (void *ptr, size_t size, size_t nmemb, void *datasource);
    +  int    (*seek_func)  (void *datasource, ogg_int64_t offset, int whence);
    +  int    (*close_func) (void *datasource);
    +  long   (*tell_func)  (void *datasource);
    +} ov_callbacks;
    +
    + +

    Relevant Struct Members

    +
    +
    read_func
    +
    Pointer to custom data reading function.
    +
    seek_func
    +
    Pointer to custom data seeking function. If the data source is not seekable (or the application wants the data source to be treated as unseekable at all times), the provided seek callback should always return -1 (failure).
    +
    close_func
    +
    Pointer to custom data source closure function.
    +
    tell_func
    +
    Pointer to custom data location function.
    +
    + +

    + +See the callbacks and non-stdio I/O document for more +detailed information on required behavior of the various callback +functions.

    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_clear.html b/components/spotify/cspot/bell/tremor/doc/ov_clear.html new file mode 100644 index 00000000..7c51bb7f --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_clear.html @@ -0,0 +1,64 @@ + + + +Tremor - function - ov_clear + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_clear

    + +

    declared in "ivorbisfile.h";

    + +

    After a bitstream has been opened using ov_open()/ov_open_callbacks() and decoding is complete, the application must call ov_clear() to clear +the decoder's buffers and close the file.

    + +ov_clear() must also be called after a successful call to ov_test() or ov_test_callbacks().

    + +

    + + + + +
    +
    
    +int ov_clear(OggVorbis_File *vf);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions. After ov_clear has been called, the structure is deallocated and can no longer be used.
    +
    + + +

    Return Values

    +
    +
  • 0 for success
  • +
    + + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_comment.html b/components/spotify/cspot/bell/tremor/doc/ov_comment.html new file mode 100644 index 00000000..5d9cc0b0 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_comment.html @@ -0,0 +1,66 @@ + + + +Tremor - function - ov_bitrate + + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_comment

    + +

    declared in "ivorbisfile.h";

    + +

    Returns a pointer to the vorbis_comment struct for the specified bitstream. For nonseekable streams, returns the struct for the current bitstream. +

    + +

    + + + + +
    +
    
    +vorbis_comment *ov_comment(OggVorbis_File *vf,int link);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    i
    +
    Link to the desired logical bitstream. For nonseekable files, this argument is ignored. To retrieve the vorbis_comment struct for the current bitstream, this parameter should be set to -1.
    +
    + + +

    Return Values

    +
    +
  • Returns the vorbis_comment struct for the specified bitstream.
  • +
  • NULL if the specified bitstream does not exist or the file has been initialized improperly.
  • +
    +

    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_info.html b/components/spotify/cspot/bell/tremor/doc/ov_info.html new file mode 100644 index 00000000..d783bf3e --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_info.html @@ -0,0 +1,64 @@ + + + +Tremor - function - ov_info + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_info

    + +

    declared in "ivorbisfile.h";

    + +

    Returns the vorbis_info struct for the specified bitstream. For nonseekable files, always returns the current vorbis_info struct. + +

    + + + + +
    +
    
    +vorbis_info *ov_info(OggVorbis_File *vf,int link);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    i
    +
    Link to the desired logical bitstream. For nonseekable files, this argument is ignored. To retrieve the vorbis_info struct for the current bitstream, this parameter should be set to -1.
    +
    + + +

    Return Values

    +
    +
  • Returns the vorbis_info struct for the specified bitstream. Returns vorbis_info for current bitstream if the file is nonseekable or i=-1.
  • +
  • NULL if the specified bitstream does not exist or the file has been initialized improperly.
  • +
    +

    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_open.html b/components/spotify/cspot/bell/tremor/doc/ov_open.html new file mode 100644 index 00000000..654cae80 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_open.html @@ -0,0 +1,115 @@ + + + +Tremor - function - ov_open + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_open

    + +

    declared in "ivorbisfile.h";

    + +

    This is the main function used to open and initialize an OggVorbis_File +structure. It sets up all the related decoding structure. +

    The first argument must be a file pointer to an already opened file +or pipe (it need not be seekable--though this obviously restricts what +can be done with the bitstream). vf should be a pointer to the +OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions. Once this has been called, the same OggVorbis_File +struct should be passed to all the libvorbisidec functions. +

    Also, you should be aware that ov_open(), once successful, takes complete possession of the file resource. After you have opened a file using ov_open(), you MUST close it using ov_clear(), not fclose() or any other function. +

    +It is often useful to call ov_open() +simply to determine whether a given file is a vorbis bitstream. If the +ov_open() +call fails, then the file is not recognizable as such. +When you use ov_open() +for +this, you should fclose() the file pointer if, and only if, the +ov_open() +call fails. If it succeeds, you must call ov_clear() to clear +the decoder's buffers and close the file for you.

    + +(Note that ov_test() provides a less expensive way to test a file for Vorbisness.)

    + +

    + + + + +
    +
    
    +int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes);
    +
    +
    + +

    Parameters

    +
    +
    f
    +
    File pointer to an already opened file +or pipe (it need not be seekable--though this obviously restricts what +can be done with the bitstream).
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions. Once this has been called, the same OggVorbis_File +struct should be passed to all the libvorbisidec functions.
    +
    initial
    +
    Typically set to NULL. This parameter is useful if some data has already been +read from the file and the stream is not seekable. It is used in conjunction with ibytes. In this case, initial +should be a pointer to a buffer containing the data read.
    +
    ibytes
    +
    Typically set to 0. This parameter is useful if some data has already been +read from the file and the stream is not seekable. In this case, ibytes +should contain the length (in bytes) of the buffer. Used together with initial
    +
    + + +

    Return Values

    +
    +
  • 0 indicates success
  • + +
  • less than zero for failure:
  • +
      +
    • OV_EREAD - A read from media returned an error.
    • +
    • OV_ENOTVORBIS - Bitstream is not Vorbis data.
    • +
    • OV_EVERSION - Vorbis version mismatch.
    • +
    • OV_EBADHEADER - Invalid Vorbis bitstream header.
    • +
    • OV_EFAULT - Internal logic fault; indicates a bug or heap/stack corruption.
    • +
    +
    +

    + +

    Notes

    +

    If your decoder is threaded, it is recommended that you NOT call +ov_open() +in the main control thread--instead, call ov_open() IN your decode/playback +thread. This is important because ov_open() may be a fairly time-consuming +call, given that the full structure of the file is determined at this point, +which may require reading large parts of the file under certain circumstances +(determining all the logical bitstreams in one physical bitstream, for +example). See Thread Safety for other information on using libvorbisidec with threads. + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_open_callbacks.html b/components/spotify/cspot/bell/tremor/doc/ov_open_callbacks.html new file mode 100644 index 00000000..64a2a921 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_open_callbacks.html @@ -0,0 +1,110 @@ + + + +Tremor - function - ov_open_callbacks + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_open_callbacks

    + +

    declared in "ivorbisfile.h";

    + +

    This is an alternative function used to open and initialize an OggVorbis_File +structure when using a data source other than a file. It allows you to specify custom file manipulation routines and sets up all the related decoding structure. +

    Once this has been called, the same OggVorbis_File +struct should be passed to all the libvorbisidec functions. +

    +It is often useful to call ov_open_callbacks() +simply to determine whether a given file is a vorbis bitstream. If the +ov_open_callbacks() +call fails, then the file is not recognizable as such. When you use ov_open_callbacks() +for +this, you should fclose() the file pointer if, and only if, the +ov_open_callbacks() +call fails. If it succeeds, you must call ov_clear() to clear +the decoder's buffers and close the file for you.

    + +See also Callbacks and Non-stdio I/O for information on designing and specifying the required callback functions.

    + +

    + + + + +
    +
    
    +int ov_open_callbacks(void *datasource, OggVorbis_File *vf, char *initial, long ibytes, ov_callbacks callbacks);
    +
    +
    + +

    Parameters

    +
    +
    f
    +
    File pointer to an already opened file +or pipe (it need not be seekable--though this obviously restricts what +can be done with the bitstream).
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions. Once this has been called, the same OggVorbis_File +struct should be passed to all the libvorbisidec functions.
    +
    initial
    +
    Typically set to NULL. This parameter is useful if some data has already been +read from the file and the stream is not seekable. It is used in conjunction with ibytes. In this case, initial +should be a pointer to a buffer containing the data read.
    +
    ibytes
    +
    Typically set to 0. This parameter is useful if some data has already been +read from the file and the stream is not seekable. In this case, ibytes +should contain the length (in bytes) of the buffer. Used together with initial.
    +
    callbacks
    +
    Pointer to a completed ov_callbacks struct which indicates desired custom file manipulation routines.
    +
    + + +

    Return Values

    +
    +
  • 0 for success
  • +
  • less than zero for failure:
  • +
      +
    • OV_EREAD - A read from media returned an error.
    • +
    • OV_ENOTVORBIS - Bitstream is not Vorbis data.
    • +
    • OV_EVERSION - Vorbis version mismatch.
    • +
    • OV_EBADHEADER - Invalid Vorbis bitstream header.
    • +
    • OV_EFAULT - Internal logic fault; indicates a bug or heap/stack corruption.
    • +
    +
    +

    + +

    Notes

    +

    If your decoder is threaded, it is recommended that you NOT call +ov_open_callbacks() +in the main control thread--instead, call ov_open_callbacks() IN your decode/playback +thread. This is important because ov_open_callbacks() may be a fairly time-consuming +call, given that the full structure of the file is determined at this point, +which may require reading large parts of the file under certain circumstances +(determining all the logical bitstreams in one physical bitstream, for +example). +See Thread Safety for other information on using libvorbisidec with threads. + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_pcm_seek.html b/components/spotify/cspot/bell/tremor/doc/ov_pcm_seek.html new file mode 100644 index 00000000..cf0351ee --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_pcm_seek.html @@ -0,0 +1,81 @@ + + + +Tremor - function - ov_pcm_seek + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_pcm_seek

    + +

    declared in "ivorbisfile.h";

    + +

    Seeks to the offset specified (in pcm samples) within the physical bitstream. This function only works for seekable streams. +

    This also updates everything needed within the +decoder, so you can immediately call ov_read() and get data from +the newly seeked to position. +

    + +

    + + + + +
    +
    
    +int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    pos
    +
    Position in pcm samples to seek to in the bitstream.
    +
    + + +

    Return Values

    +
    +
  • 0 for success
  • + +
  • +nonzero indicates failure, described by several error codes:
  • +
      +
    • OV_ENOSEEK - Bitstream is not seekable. +
    • +
    • OV_EINVAL - Invalid argument value. +
    • +
    • OV_EREAD - A read from media returned an error. +
    • +
    • OV_EFAULT - Internal logic fault; indicates a bug or heap/stack + corruption. +
    • +
    • OV_EBADLINK - Invalid stream section supplied to libvorbisidec, or the requested link is corrupt. +
    • +
    + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_pcm_seek_page.html b/components/spotify/cspot/bell/tremor/doc/ov_pcm_seek_page.html new file mode 100644 index 00000000..44468a8b --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_pcm_seek_page.html @@ -0,0 +1,83 @@ + + + +Tremor - function - ov_pcm_seek_page + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_pcm_seek_page

    + +

    declared in "ivorbisfile.h";

    + +

    Seeks to the closest page preceding the specified location (in pcm samples) within the physical bitstream. This function only works for seekable streams. +

    This function is faster than ov_pcm_seek because the function can begin decoding at a page boundary rather than seeking through any remaining samples before the specified location. However, it is less accurate. +

    This also updates everything needed within the +decoder, so you can immediately call ov_read() and get data from +the newly seeked to position. +

    + +

    + + + + +
    +
    
    +int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    pos
    +
    Position in pcm samples to seek to in the bitstream.
    +
    + + +

    Return Values

    +
    +
  • +0 for success
  • + +
  • +nonzero indicates failure, described by several error codes:
  • +
      +
    • OV_ENOSEEK - Bitstream is not seekable. +
    • +
    • OV_EINVAL - Invalid argument value. +
    • +
    • OV_EREAD - A read from media returned an error. +
    • +
    • OV_EFAULT - Internal logic fault; indicates a bug or heap/stack + corruption. +
    • +
    • OV_EBADLINK - Invalid stream section supplied to libvorbisidec, or the requested link is corrupt. +
    • +
    + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_pcm_tell.html b/components/spotify/cspot/bell/tremor/doc/ov_pcm_tell.html new file mode 100644 index 00000000..0bb98d7b --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_pcm_tell.html @@ -0,0 +1,63 @@ + + + +Tremor - function - ov_pcm_tell + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_pcm_tell

    + +

    declared in "ivorbisfile.h";

    + +

    Returns the current offset in samples. + +

    + + + + +
    +
    
    +ogg_int64_t ov_pcm_tell(OggVorbis_File *vf);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    + + +

    Return Values

    +
    +
  • n indicates the current offset in samples.
  • +
  • OV_EINVAL means that the argument was invalid. In this case, the requested bitstream did not exist.
  • +
    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_pcm_total.html b/components/spotify/cspot/bell/tremor/doc/ov_pcm_total.html new file mode 100644 index 00000000..a19744a1 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_pcm_total.html @@ -0,0 +1,67 @@ + + + +Tremor - function - ov_pcm_total + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_pcm_total

    + +

    declared in "ivorbisfile.h";

    + +

    Returns the total pcm samples of the physical bitstream or a specified logical bitstream. + +

    + + + + +
    +
    
    +ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    i
    +
    Link to the desired logical bitstream. To retrieve the total pcm samples for the entire physical bitstream, this parameter should be set to -1.
    +
    + + +

    Return Values

    +
    +
  • OV_EINVAL means that the argument was invalid. In this case, the requested bitstream did not exist or the bitstream is unseekable.
  • +
  • +total length in pcm samples of content if i=-1.
  • +
  • length in pcm samples of logical bitstream if i=1 to n.
  • +
    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_raw_seek.html b/components/spotify/cspot/bell/tremor/doc/ov_raw_seek.html new file mode 100644 index 00000000..e7f0bd30 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_raw_seek.html @@ -0,0 +1,75 @@ + + + +Tremor - function - ov_raw_seek + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_raw_seek

    + +

    declared in "ivorbisfile.h";

    + +

    Seeks to the offset specified (in compressed raw bytes) within the physical bitstream. This function only works for seekable streams. +

    This also updates everything needed within the +decoder, so you can immediately call ov_read() and get data from +the newly seeked to position. +

    When seek speed is a priority, this is the best seek funtion to use. +

    + + + + +
    +
    
    +int ov_raw_seek(OggVorbis_File *vf,long pos);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    pos
    +
    Position in compressed bytes to seek to in the bitstream.
    +
    + + +

    Return Values

    +
    +
  • 0 indicates success
  • +
  • nonzero indicates failure, described by several error codes:
  • +
      +
    • OV_ENOSEEK - Bitstream is not seekable. +
    • +
    • OV_EINVAL - Invalid argument value. +
    • +
    • OV_EBADLINK - Invalid stream section supplied to libvorbisidec, or the requested link is corrupt. +
    • +
    +
    +

    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_raw_tell.html b/components/spotify/cspot/bell/tremor/doc/ov_raw_tell.html new file mode 100644 index 00000000..f0d1f6ab --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_raw_tell.html @@ -0,0 +1,63 @@ + + + +Tremor - function - ov_raw_tell + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_raw_tell

    + +

    declared in "ivorbisfile.h";

    + +

    Returns the current offset in raw compressed bytes. + +

    + + + + +
    +
    
    +ogg_int64_t ov_raw_tell(OggVorbis_File *vf);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    + + +

    Return Values

    +
    +
  • n indicates the current offset in bytes.
  • +
  • OV_EINVAL means that the argument was invalid. In this case, the requested bitstream did not exist.
  • +
    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_raw_total.html b/components/spotify/cspot/bell/tremor/doc/ov_raw_total.html new file mode 100644 index 00000000..d0af35fb --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_raw_total.html @@ -0,0 +1,68 @@ + + + +Tremor - function - ov_raw_total + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_raw_total

    + +

    declared in "ivorbisfile.h";

    + +

    Returns the total (compressed) bytes of the physical bitstream or a specified logical bitstream. + +

    + + + + +
    +
    
    +ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    i
    +
    Link to the desired logical bitstream. To retrieve the total bytes for the entire physical bitstream, this parameter should be set to -1.
    +
    + + +

    Return Values

    +
    +
  • OV_EINVAL means that the argument was invalid. In this case, the requested bitstream did not exist or the bitstream is nonseekable
  • +
  • n +total length in compressed bytes of content if i=-1.
  • +
  • n length in compressed bytes of logical bitstream if i=1 to n.
  • +
    +

    + + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_read.html b/components/spotify/cspot/bell/tremor/doc/ov_read.html new file mode 100644 index 00000000..208ef18b --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_read.html @@ -0,0 +1,115 @@ + + + +Tremor - function - ov_read + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_read()

    + +

    declared in "ivorbisfile.h";

    + +

    + This is the main function used to decode a Vorbis file within a + loop. It returns up to the specified number of bytes of decoded audio + in host-endian, signed 16 bit PCM format. If the audio is + multichannel, the channels are interleaved in the output buffer. + If the passed in buffer is large, ov_read() will not fill + it; the passed in buffer size is treated as a limit and + not a request. +

    + +Note that up to this point, the Tremor API could more or less hide the + multiple logical bitstream nature of chaining from the toplevel + application if the toplevel application didn't particularly care. + However, when reading audio back, the application must be aware + that multiple bitstream sections do not necessarily use the same + number of channels or sampling rate.

    ov_read() passes + back the index of the sequential logical bitstream currently being + decoded (in *bitstream) along with the PCM data in order + that the toplevel application can handle channel and/or sample + rate changes. This number will be incremented at chaining + boundaries even for non-seekable streams. For seekable streams, it + represents the actual chaining index within the physical bitstream. +

    + +

    + + + + +
    +
    
    +long ov_read(OggVorbis_File *vf, char *buffer, int length, int *bitstream);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    buffer
    +
    A pointer to an output buffer. The decoded output is inserted into this buffer.
    +
    length
    +
    Number of bytes to be read into the buffer. Should be the same size as the buffer. A typical value is 4096.
    +
    bitstream
    +
    A pointer to the number of the current logical bitstream.
    +
    + + +

    Return Values

    +
    +
    +
    OV_HOLE
    +
    indicates there was an interruption in the data. +
    (one of: garbage between pages, loss of sync followed by + recapture, or a corrupt page)
    +
    OV_EBADLINK
    +
    indicates that an invalid stream section was supplied to + libvorbisidec, or the requested link is corrupt.
    +
    0
    +
    indicates EOF
    +
    n
    +
    indicates actual number of bytes read. ov_read() will + decode at most one vorbis packet per invocation, so the value + returned will generally be less than length. +
    +
    + +

    Notes

    +

    Typical usage: +

    +bytes_read = ov_read(&vf, +buffer, 4096,&current_section) +
    + +This reads up to 4096 bytes into a buffer, with signed 16-bit +little-endian samples. +

    + + + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_seekable.html b/components/spotify/cspot/bell/tremor/doc/ov_seekable.html new file mode 100644 index 00000000..9bd7fc3d --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_seekable.html @@ -0,0 +1,63 @@ + + + +Tremor - function - ov_seekable + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_seekable

    + +

    declared in "ivorbisfile.h";

    + +

    This indicates whether or not the bitstream is seekable. + + +

    + + + + +
    +
    
    +long ov_seekable(OggVorbis_File *vf);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    + + +

    Return Values

    +
    +
  • 0 indicates that the file is not seekable.
  • +
  • nonzero indicates that the file is seekable.
  • +
    +

    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_serialnumber.html b/components/spotify/cspot/bell/tremor/doc/ov_serialnumber.html new file mode 100644 index 00000000..d7d7c622 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_serialnumber.html @@ -0,0 +1,67 @@ + + + +Tremor - function - ov_serialnumber + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_serialnumber

    + +

    declared in "ivorbisfile.h";

    + +

    Returns the serialnumber of the specified logical bitstream link number within the overall physical bitstream. + +

    + + + + +
    +
    
    +long ov_serialnumber(OggVorbis_File *vf,int i);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    i
    +
    Link to the desired logical bitstream. For nonseekable files, this argument is ignored. To retrieve the serial number of the current bitstream, this parameter should be set to -1.
    +
    + + +

    Return Values

    +
    +
  • +-1 if the specified logical bitstream i does not exist.
  • + +
  • Returns the serial number of the logical bitstream i or the serial number of the current bitstream if the file is nonseekable.
  • +
    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_streams.html b/components/spotify/cspot/bell/tremor/doc/ov_streams.html new file mode 100644 index 00000000..7ffee42a --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_streams.html @@ -0,0 +1,64 @@ + + + +Tremor - function - ov_streams + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_streams

    + +

    declared in "ivorbisfile.h";

    + +

    Returns the number of logical bitstreams within our physical bitstream. + +

    + + + + +
    +
    
    +long ov_streams(OggVorbis_File *vf);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    + + +

    Return Values

    +
    +
  • +1 indicates a single logical bitstream or an unseekable file.
  • +
  • n indicates the number of logical bitstreams.
  • +
    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_test.html b/components/spotify/cspot/bell/tremor/doc/ov_test.html new file mode 100644 index 00000000..96a9af04 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_test.html @@ -0,0 +1,89 @@ + + + +Tremor - function - ov_test + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_test

    + +

    declared in "ivorbisfile.h";

    + +

    +This partially opens a vorbis file to test for Vorbis-ness. It loads +the headers for the first chain, and tests for seekability (but does not seek). +Use ov_test_open() to finish opening the file +or ov_clear to close/free it. +

    + + + + + +
    +
    
    +int ov_test(FILE *f,OggVorbis_File *vf,char *initial,long ibytes);
    +
    +
    + +

    Parameters

    +
    +
    f
    +
    File pointer to an already opened file +or pipe (it need not be seekable--though this obviously restricts what +can be done with the bitstream).
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions. Once this has been called, the same OggVorbis_File +struct should be passed to all the libvorbisidec functions.
    +
    initial
    +
    Typically set to NULL. This parameter is useful if some data has already been +read from the file and the stream is not seekable. It is used in conjunction with ibytes. In this case, initial +should be a pointer to a buffer containing the data read.
    +
    ibytes
    +
    Typically set to 0. This parameter is useful if some data has already been +read from the file and the stream is not seekable. In this case, ibytes +should contain the length (in bytes) of the buffer. Used together with initial
    +
    + + +

    Return Values

    +
    +
  • 0 for success
  • + +
  • less than zero for failure:
  • +
      +
    • OV_EREAD - A read from media returned an error.
    • +
    • OV_ENOTVORBIS - Bitstream is not Vorbis data.
    • +
    • OV_EVERSION - Vorbis version mismatch.
    • +
    • OV_EBADHEADER - Invalid Vorbis bitstream header.
    • +
    • OV_EFAULT - Internal logic fault; indicates a bug or heap/stack corruption.
    • +
    +
    +

    + + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_test_callbacks.html b/components/spotify/cspot/bell/tremor/doc/ov_test_callbacks.html new file mode 100644 index 00000000..40495480 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_test_callbacks.html @@ -0,0 +1,90 @@ + + + +Tremor - function - ov_test_callbacks + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_test_callbacks

    + +

    declared in "ivorbisfile.h";

    + +

    This is an alternative function used to open and test an OggVorbis_File +structure when using a data source other than a file. It allows you to specify custom file manipulation routines and sets up all the related decoding structures. +

    Once this has been called, the same OggVorbis_File +struct should be passed to all the libvorbisidec functions. +

    +

    + + + + +
    +
    
    +int ov_test_callbacks(void *datasource, OggVorbis_File *vf, char *initial, long ibytes, ov_callbacks callbacks);
    +
    +
    + +

    Parameters

    +
    +
    f
    +
    File pointer to an already opened file +or pipe (it need not be seekable--though this obviously restricts what +can be done with the bitstream).
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions. Once this has been called, the same OggVorbis_File +struct should be passed to all the libvorbisidec functions.
    +
    initial
    +
    Typically set to NULL. This parameter is useful if some data has already been +read from the file and the stream is not seekable. It is used in conjunction with ibytes. In this case, initial +should be a pointer to a buffer containing the data read.
    +
    ibytes
    +
    Typically set to 0. This parameter is useful if some data has already been +read from the file and the stream is not seekable. In this case, ibytes +should contain the length (in bytes) of the buffer. Used together with initial.
    +
    callbacks
    +
    Pointer to a completed ov_callbacks struct which indicates desired custom file manipulation routines.
    +
    + + +

    Return Values

    +
    +
  • 0 for success
  • +
  • less than zero for failure:
  • +
      +
    • OV_EREAD - A read from media returned an error.
    • +
    • OV_ENOTVORBIS - Bitstream is not Vorbis data.
    • +
    • OV_EVERSION - Vorbis version mismatch.
    • +
    • OV_EBADHEADER - Invalid Vorbis bitstream header.
    • +
    • OV_EFAULT - Internal logic fault; indicates a bug or heap/stack corruption.
    • +
    +
    +

    + + + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_test_open.html b/components/spotify/cspot/bell/tremor/doc/ov_test_open.html new file mode 100644 index 00000000..74f44101 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_test_open.html @@ -0,0 +1,82 @@ + + + +Tremor - function - ov_test_open + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_test_open

    + +

    declared in "ivorbisfile.h";

    + +

    +Finish opening a file partially opened with ov_test() +or ov_test_callbacks(). +

    + + + + + +
    +
    
    +int ov_test_open(OggVorbis_File *vf);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions. Once this has been called, the same OggVorbis_File +struct should be passed to all the libvorbisidec functions.
    +
    + + +

    Return Values

    +
    +
  • +0 for success
  • + +
  • less than zero for failure:
  • +
      +
    • OV_EREAD - A read from media returned an error.
    • +
    • OV_ENOTVORBIS - Bitstream is not Vorbis data.
    • +
    • OV_EVERSION - Vorbis version mismatch.
    • +
    • OV_EBADHEADER - Invalid Vorbis bitstream header.
    • +
    • OV_EFAULT - Internal logic fault; indicates a bug or heap/stack corruption.
    • +
    +
    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + + + + + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_time_seek.html b/components/spotify/cspot/bell/tremor/doc/ov_time_seek.html new file mode 100644 index 00000000..6dfa130a --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_time_seek.html @@ -0,0 +1,70 @@ + + + +Tremor - function - ov_time_seek + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_time_seek

    + +

    declared in "ivorbisfile.h";

    + +

    For seekable +streams, this seeks to the given time. For implementing seeking in a player, +this is the only function generally needed. This also updates everything needed within the +decoder, so you can immediately call ov_read() and get data from +the newly seeked to position. This function does not work for unseekable streams. + +

    + + + + +
    +
    
    +int ov_time_seek(OggVorbis_File *vf, ogg_int64_t ms);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    Pointer to our already opened and initialized OggVorbis_File structure.
    +
    ms
    +
    Location to seek to within the file, specified in milliseconds.
    +
    + + +

    Return Values

    +
    +
  • +0 for success
  • + +
  • +Nonzero for failure
  • +
    + + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_time_seek_page.html b/components/spotify/cspot/bell/tremor/doc/ov_time_seek_page.html new file mode 100644 index 00000000..83cfefbd --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_time_seek_page.html @@ -0,0 +1,83 @@ + + + +Tremor - function - ov_time_seek_page + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_time_seek_page

    + +

    declared in "ivorbisfile.h";

    + +

    For seekable +streams, this seeks to closest full page preceding the given time. This function is faster than ov_time_seek because it doesn't seek through the last few samples to reach an exact time, but it is also less accurate. This should be used when speed is important. +

    This function also updates everything needed within the +decoder, so you can immediately call ov_read() and get data from +the newly seeked to position. +

    This function does not work for unseekable streams. + +

    + + + + +
    +
    
    +int ov_time_seek_page(OggVorbis_File *vf, ogg_int64_t ms);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    Pointer to our already opened and initialized OggVorbis_File structure.
    +
    ms
    +
    Location to seek to within the file, specified in milliseconds.
    +
    + + +

    Return Values

    +
    +
  • +0 for success
  • + +
  • +nonzero indicates failure, described by several error codes:
  • +
      +
    • OV_ENOSEEK - Bitstream is not seekable. +
    • +
    • OV_EINVAL - Invalid argument value. +
    • +
    • OV_EREAD - A read from media returned an error. +
    • +
    • OV_EFAULT - Internal logic fault; indicates a bug or heap/stack + corruption. +
    • +
    • OV_EBADLINK - Invalid stream section supplied to libvorbisidec, or the requested link is corrupt. +
    • +
    + + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_time_tell.html b/components/spotify/cspot/bell/tremor/doc/ov_time_tell.html new file mode 100644 index 00000000..25d159b6 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_time_tell.html @@ -0,0 +1,63 @@ + + + +Tremor - function - ov_bitrate + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_time_tell

    + +

    declared in "ivorbisfile.h";

    + +

    Returns the current decoding offset in milliseconds. + +

    + + + + +
    +
    
    +ogg_int64_t ov_time_tell(OggVorbis_File *vf);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    + + +

    Return Values

    +
    +
  • n indicates the current decoding time offset in milliseconds.
  • +
  • OV_EINVAL means that the argument was invalid. In this case, the requested bitstream did not exist.
  • +
    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/ov_time_total.html b/components/spotify/cspot/bell/tremor/doc/ov_time_total.html new file mode 100644 index 00000000..7c26b924 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/ov_time_total.html @@ -0,0 +1,67 @@ + + + +Tremor - function - ov_time_total + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_time_total

    + +

    declared in "ivorbisfile.h";

    + + +

    Returns the total time in seconds of the physical bitstream or a specified logical bitstream. + + +

    + + + + +
    +
    
    +ogg_int64_t ov_time_total(OggVorbis_File *vf,int i);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    i
    +
    Link to the desired logical bitstream. To retrieve the time total for the entire physical bitstream, this parameter should be set to -1.
    +
    + + +

    Return Values

    +
    +
  • OV_EINVAL means that the argument was invalid. In this case, the requested bitstream did not exist or the bitstream is nonseekable.
  • +
  • n total length in milliseconds of content if i=-1.
  • +
  • n length in milliseconds of logical bitstream if i=1 to n.
  • +
    +

    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/overview.html b/components/spotify/cspot/bell/tremor/doc/overview.html new file mode 100644 index 00000000..0c82cb20 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/overview.html @@ -0,0 +1,61 @@ + + + +Tremor - API Overview + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    Tremor API Overview

    + +

    The makeup of the Tremor libvorbisidec library API is relatively +simple. It revolves around a single file resource. This file resource is +passed to libvorbisidec, where it is opened, manipulated, and closed, +in the form of an OggVorbis_File +struct. +

    +The Tremor API consists of the following functional categories: +

    +

    +

    +In addition, the following subjects deserve attention additional to +the above general overview: +

    +

    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/reference.html b/components/spotify/cspot/bell/tremor/doc/reference.html new file mode 100644 index 00000000..20e0a5f9 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/reference.html @@ -0,0 +1,75 @@ + + + +Tremor API Reference + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    Tremor API Reference

    + +

    +Data Structures
    +OggVorbis_File
    +vorbis_comment
    +vorbis_info
    +ov_callbacks
    +
    +Setup/Teardown
    +ov_open()
    +ov_open_callbacks()
    +ov_clear()
    +ov_test()
    +ov_test_callbacks()
    +ov_test_open()
    +
    +Decoding
    +ov_read()
    +
    +Seeking
    +ov_raw_seek()
    +ov_pcm_seek()
    +ov_time_seek()
    +ov_pcm_seek_page()
    +ov_time_seek_page()
    +
    +File Information
    +ov_bitrate()
    +ov_bitrate_instant()
    +ov_streams()
    +ov_seekable()
    +ov_serialnumber()
    +ov_raw_total()
    +ov_pcm_total()
    +ov_time_total()
    +ov_raw_tell()
    +ov_pcm_tell()
    +ov_time_tell()
    +ov_info()
    +ov_comment()
    +
    +Return Codes
    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/return.html b/components/spotify/cspot/bell/tremor/doc/return.html new file mode 100644 index 00000000..0a3f96c0 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/return.html @@ -0,0 +1,77 @@ + + + +Tremor - Return Codes + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    Return Codes

    + +

    + +The following return codes are #defined in "ivorbiscodec.h" +may be returned by libvorbisidec. Descriptions of a code relevant to +a specific function are found in the reference description of that +function. + +

    + +
    OV_FALSE
    +
    Not true, or no data available
    + +
    OV_HOLE
    +
    Tremor encoutered missing or corrupt data in the bitstream. Recovery +is normally automatic and this return code is for informational purposes only.
    + +
    OV_EREAD
    +
    Read error while fetching compressed data for decode
    + +
    OV_EFAULT
    +
    Internal inconsistency in decode state. Continuing is likely not possible.
    + +
    OV_EIMPL
    +
    Feature not implemented
    + +
    OV_EINVAL
    +
    Either an invalid argument, or incompletely initialized argument passed to libvorbisidec call
    + +
    OV_ENOTVORBIS
    +
    The given file/data was not recognized as Ogg Vorbis data.
    + +
    OV_EBADHEADER
    +
    The file/data is apparently an Ogg Vorbis stream, but contains a corrupted or undecipherable header.
    + +
    OV_EVERSION
    +
    The bitstream format revision of the given stream is not supported.
    + +
    OV_EBADLINK
    +
    The given link exists in the Vorbis data stream, but is not decipherable due to garbacge or corruption.
    + +
    OV_ENOSEEK
    +
    The given stream is not seekable
    + +
    + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/seeking.html b/components/spotify/cspot/bell/tremor/doc/seeking.html new file mode 100644 index 00000000..652368af --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/seeking.html @@ -0,0 +1,74 @@ + + + +Tremor - Seeking + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    Seeking

    +

    Seeking functions allow you to specify a specific point in the stream to begin or continue decoding. +

    +All libvorbisidec seeking routines are declared in "ivorbisfile.h". + +

    Certain seeking functions are best suited to different situations. +When speed is important and exact positioning isn't required, +page-level seeking should be used. Note also that Vorbis files do not +necessarily start at a sample number or time offset of zero. Do not +be surprised if a file begins at a positive offset of several minutes +or hours, such as would happen if a large stream (such as a concert +recording) is chopped into multiple separate files. Requesting to +seek to a position before the beginning of such a file will seek to +the position where audio begins.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    functionpurpose
    ov_raw_seekThis function seeks to a position specified in the compressed bitstream, specified in bytes.
    ov_pcm_seekThis function seeks to a specific audio sample number, specified in pcm samples.
    ov_pcm_seek_pageThis function seeks to the closest page preceding the specified audio sample number, specified in pcm samples.
    ov_time_seekThis function seeks to the specific time location in the bitstream, specified in integer milliseconds. Note that this differs from the reference vorbisfile implementation, which takes seconds as a float.
    ov_time_seek_pageThis function seeks to the closest page preceding the specified time position in the bitstream, specified in integer milliseconds.
    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/style.css b/components/spotify/cspot/bell/tremor/doc/style.css new file mode 100644 index 00000000..81cf4179 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/style.css @@ -0,0 +1,7 @@ +BODY { font-family: Helvetica, sans-serif } +TD { font-family: Helvetica, sans-serif } +P { font-family: Helvetica, sans-serif } +H1 { font-family: Helvetica, sans-serif } +H2 { font-family: Helvetica, sans-serif } +H4 { font-family: Helvetica, sans-serif } +P.tiny { font-size: 8pt } diff --git a/components/spotify/cspot/bell/tremor/doc/threads.html b/components/spotify/cspot/bell/tremor/doc/threads.html new file mode 100644 index 00000000..53ed76a7 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/threads.html @@ -0,0 +1,50 @@ + + + +Tremor - Thread Safety + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    Thread Safety

    + +Tremor's libvorbisidec may be used safely in a threading environment +so long as thread access to individual OggVorbis_File instances is serialized. +
      + +
    • Only one thread at a time may enter a function that takes a given OggVorbis_File instance, even if the +functions involved appear to be read-only.

      + +

    • Multiple threads may enter +libvorbisidec at a given time, so long as each thread's function calls +are using different OggVorbis_File +instances.

      + +

    • Any one OggVorbis_File instance may be used safely from multiple threads so long as only one thread at a time is making calls using that instance.

      +

    + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/vorbis_comment.html b/components/spotify/cspot/bell/tremor/doc/vorbis_comment.html new file mode 100644 index 00000000..3232d962 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/vorbis_comment.html @@ -0,0 +1,70 @@ + + + +Tremor - datatype - vorbis_comment + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    vorbis_comment

    + +

    declared in "ivorbiscodec.h"

    + +

    +The vorbis_comment structure defines an Ogg Vorbis comment. +

    +Only the fields the program needs must be defined. If a field isn't +defined by the application, it will either be blank (if it's a string value) +or set to some reasonable default (usually 0). +

    + + + + + +
    +
    typedef struct vorbis_comment{
    +  /* unlimited user comment fields. */
    +  char **user_comments;
    +  int  *comment_lengths;
    +  int  comments;
    +  char *vendor;
    +
    +} vorbis_comment;
    +
    + +

    Parameters

    +
    +
    user_comments
    +
    Unlimited user comment array. The individual strings in the array are 8 bit clean, by the Vorbis specification, and as such the comment_lengths array should be consulted to determine string length. For convenience, each string is also NULL-terminated by the decode library (although Vorbis comments are not NULL terminated within the bitstream itself).
    +
    comment_lengths
    +
    An int array that stores the length of each comment string
    +
    comments
    +
    Int signifying number of user comments in user_comments field.
    +
    vendor
    +
    Information about the creator of the file. Stored in a standard C 0-terminated string.
    +
    + + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/doc/vorbis_info.html b/components/spotify/cspot/bell/tremor/doc/vorbis_info.html new file mode 100644 index 00000000..bd938cdc --- /dev/null +++ b/components/spotify/cspot/bell/tremor/doc/vorbis_info.html @@ -0,0 +1,80 @@ + + + +Tremor - datatype - vorbis_info + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    vorbis_info

    + +

    declared in "ivorbiscodec.h"

    + +

    +The vorbis_info structure contains basic information about the audio in a vorbis bitstream. +

    + + + + + +
    +
    typedef struct vorbis_info{
    +  int version;
    +  int channels;
    +  long rate;
    +  
    +  long bitrate_upper;
    +  long bitrate_nominal;
    +  long bitrate_lower;
    +  long bitrate_window;
    +
    +  void *codec_setup;
    +
    +} vorbis_info;
    +
    + +

    Relevant Struct Members

    +
    +
    version
    +
    Vorbis encoder version used to create this bitstream.
    +
    channels
    +
    Int signifying number of channels in bitstream.
    +
    rate
    +
    Sampling rate of the bitstream.
    +
    bitrate_upper
    +
    Specifies the upper limit in a VBR bitstream. If the value matches the bitrate_nominal and bitrate_lower parameters, the stream is fixed bitrate. May be unset if no limit exists.
    +
    bitrate_nominal
    +
    Specifies the average bitrate for a VBR bitstream. May be unset. If the bitrate_upper and bitrate_lower parameters match, the stream is fixed bitrate.
    +
    bitrate_lower
    +
    Specifies the lower limit in a VBR bitstream. If the value matches the bitrate_nominal and bitrate_upper parameters, the stream is fixed bitrate. May be unset if no limit exists.
    +
    bitrate_window
    +
    Currently unset.
    + +
    codec_setup
    +
    Internal structure that contains the detailed/unpacked configuration for decoding the current Vorbis bitstream.
    +
    + + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/components/spotify/cspot/bell/tremor/dsp.c b/components/spotify/cspot/bell/tremor/dsp.c new file mode 100644 index 00000000..882b6854 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/dsp.c @@ -0,0 +1,298 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: PCM data vector blocking, windowing and dis/reassembly + + ********************************************************************/ + +#include +#include "ogg.h" +#include "mdct.h" +#include "ivorbiscodec.h" +#include "codec_internal.h" +#include "misc.h" +#include "window_lookup.h" + +int vorbis_dsp_restart(vorbis_dsp_state *v){ + if(!v)return -1; + { + vorbis_info *vi=v->vi; + codec_setup_info *ci; + + if(!vi)return -1; + ci=vi->codec_setup; + if(!ci)return -1; + + v->out_end=-1; + v->out_begin=-1; + + v->granulepos=-1; + v->sequence=-1; + v->sample_count=-1; + } + return 0; +} + +vorbis_dsp_state *vorbis_dsp_create(vorbis_info *vi){ + int i; + + vorbis_dsp_state *v=_ogg_calloc(1,sizeof(*v)); + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + + v->vi=vi; + + v->work=(ogg_int32_t **)_ogg_malloc(vi->channels*sizeof(*v->work)); + v->mdctright=(ogg_int32_t **)_ogg_malloc(vi->channels*sizeof(*v->mdctright)); + for(i=0;ichannels;i++){ + v->work[i]=(ogg_int32_t *)_ogg_calloc(1,(ci->blocksizes[1]>>1)* + sizeof(*v->work[i])); + v->mdctright[i]=(ogg_int32_t *)_ogg_calloc(1,(ci->blocksizes[1]>>2)* + sizeof(*v->mdctright[i])); + } + + v->lW=0; /* previous window size */ + v->W=0; /* current window size */ + + vorbis_dsp_restart(v); + return v; +} + +void vorbis_dsp_destroy(vorbis_dsp_state *v){ + int i; + if(v){ + vorbis_info *vi=v->vi; + + if(v->work){ + for(i=0;ichannels;i++) + if(v->work[i])_ogg_free(v->work[i]); + _ogg_free(v->work); + } + if(v->mdctright){ + for(i=0;ichannels;i++) + if(v->mdctright[i])_ogg_free(v->mdctright[i]); + _ogg_free(v->mdctright); + } + + _ogg_free(v); + } +} + +static LOOKUP_T *_vorbis_window(int left){ + switch(left){ + case 32: + return vwin64; + case 64: + return vwin128; + case 128: + return vwin256; + case 256: + return vwin512; + case 512: + return vwin1024; + case 1024: + return vwin2048; + case 2048: + return vwin4096; +#ifndef LIMIT_TO_64kHz + case 4096: + return vwin8192; +#endif + default: + return(0); + } +} + +/* pcm==0 indicates we just want the pending samples, no more */ +int vorbis_dsp_pcmout(vorbis_dsp_state *v,ogg_int16_t *pcm,int samples){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + if(v->out_begin>-1 && v->out_beginout_end){ + int n=v->out_end-v->out_begin; + if(pcm){ + int i; + if(n>samples)n=samples; + for(i=0;ichannels;i++) + mdct_unroll_lap(ci->blocksizes[0],ci->blocksizes[1], + v->lW,v->W,v->work[i],v->mdctright[i], + _vorbis_window(ci->blocksizes[0]>>1), + _vorbis_window(ci->blocksizes[1]>>1), + pcm+i,vi->channels, + v->out_begin,v->out_begin+n); + } + return(n); + } + return(0); +} + +int vorbis_dsp_read(vorbis_dsp_state *v,int s){ + if(s && v->out_begin+s>v->out_end)return(OV_EINVAL); + v->out_begin+=s; + return(0); +} + +long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op){ + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + oggpack_buffer opb; + int mode; + int modebits=0; + int v=ci->modes; + + oggpack_readinit(&opb,op->packet); + + /* Check the packet type */ + if(oggpack_read(&opb,1)!=0){ + /* Oops. This is not an audio data packet */ + return(OV_ENOTAUDIO); + } + + while(v>1){ + modebits++; + v>>=1; + } + + /* read our mode and pre/post windowsize */ + mode=oggpack_read(&opb,modebits); + if(mode==-1)return(OV_EBADPACKET); + return(ci->blocksizes[ci->mode_param[mode].blockflag]); +} + + +static int ilog(ogg_uint32_t v){ + int ret=0; + if(v)--v; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +int vorbis_dsp_synthesis(vorbis_dsp_state *vd,ogg_packet *op,int decodep){ + vorbis_info *vi=vd->vi; + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + int mode,i; + + oggpack_readinit(&vd->opb,op->packet); + + /* Check the packet type */ + if(oggpack_read(&vd->opb,1)!=0){ + /* Oops. This is not an audio data packet */ + return OV_ENOTAUDIO ; + } + + /* read our mode and pre/post windowsize */ + mode=oggpack_read(&vd->opb,ilog(ci->modes)); + if(mode==-1 || mode>=ci->modes) return OV_EBADPACKET; + + /* shift information we still need from last window */ + vd->lW=vd->W; + vd->W=ci->mode_param[mode].blockflag; + for(i=0;ichannels;i++) + mdct_shift_right(ci->blocksizes[vd->lW],vd->work[i],vd->mdctright[i]); + + if(vd->W){ + int temp; + oggpack_read(&vd->opb,1); + temp=oggpack_read(&vd->opb,1); + if(temp==-1) return OV_EBADPACKET; + } + + /* packet decode and portions of synthesis that rely on only this block */ + if(decodep){ + mapping_inverse(vd,ci->map_param+ci->mode_param[mode].mapping); + + if(vd->out_begin==-1){ + vd->out_begin=0; + vd->out_end=0; + }else{ + vd->out_begin=0; + vd->out_end=ci->blocksizes[vd->lW]/4+ci->blocksizes[vd->W]/4; + } + } + + /* track the frame number... This is for convenience, but also + making sure our last packet doesn't end with added padding. + + This is not foolproof! It will be confused if we begin + decoding at the last page after a seek or hole. In that case, + we don't have a starting point to judge where the last frame + is. For this reason, vorbisfile will always try to make sure + it reads the last two marked pages in proper sequence */ + + /* if we're out of sequence, dump granpos tracking until we sync back up */ + if(vd->sequence==-1 || vd->sequence+1 != op->packetno-3){ + /* out of sequence; lose count */ + vd->granulepos=-1; + vd->sample_count=-1; + } + + vd->sequence=op->packetno; + vd->sequence=vd->sequence-3; + + if(vd->sample_count==-1){ + vd->sample_count=0; + }else{ + vd->sample_count+= + ci->blocksizes[vd->lW]/4+ci->blocksizes[vd->W]/4; + } + + if(vd->granulepos==-1){ + if(op->granulepos!=-1){ /* only set if we have a + position to set to */ + + vd->granulepos=op->granulepos; + + /* is this a short page? */ + if(vd->sample_count>vd->granulepos){ + /* corner case; if this is both the first and last audio page, + then spec says the end is cut, not beginning */ + if(op->e_o_s){ + /* trim the end */ + /* no preceeding granulepos; assume we started at zero (we'd + have to in a short single-page stream) */ + /* granulepos could be -1 due to a seek, but that would result + in a long coun t, not short count */ + + vd->out_end-=vd->sample_count-vd->granulepos; + }else{ + /* trim the beginning */ + vd->out_begin+=vd->sample_count-vd->granulepos; + if(vd->out_begin>vd->out_end) + vd->out_begin=vd->out_end; + } + + } + + } + }else{ + vd->granulepos+= + ci->blocksizes[vd->lW]/4+ci->blocksizes[vd->W]/4; + if(op->granulepos!=-1 && vd->granulepos!=op->granulepos){ + + if(vd->granulepos>op->granulepos){ + long extra=vd->granulepos-op->granulepos; + + if(extra) + if(op->e_o_s){ + /* partial last frame. Strip the extra samples off */ + vd->out_end-=extra; + } /* else {Shouldn't happen *unless* the bitstream is out of + spec. Either way, believe the bitstream } */ + } /* else {Shouldn't happen *unless* the bitstream is out of + spec. Either way, believe the bitstream } */ + vd->granulepos=op->granulepos; + } + } + + return(0); +} diff --git a/components/spotify/cspot/bell/tremor/floor0.c b/components/spotify/cspot/bell/tremor/floor0.c new file mode 100644 index 00000000..46510bff --- /dev/null +++ b/components/spotify/cspot/bell/tremor/floor0.c @@ -0,0 +1,427 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: floor backend 0 implementation + + ********************************************************************/ + +#include +#include +#include +#include "ogg.h" +#include "ivorbiscodec.h" +#include "codec_internal.h" +#include "codebook.h" +#include "misc.h" +#include "os.h" + +#define LSP_FRACBITS 14 +extern const ogg_int32_t FLOOR_fromdB_LOOKUP[]; + +/*************** LSP decode ********************/ + +#include "lsp_lookup.h" + +/* interpolated 1./sqrt(p) where .5 <= a < 1. (.100000... to .111111...) in + 16.16 format + returns in m.8 format */ + +static long ADJUST_SQRT2[2]={8192,5792}; +static inline ogg_int32_t vorbis_invsqlook_i(long a,long e){ + long i=(a&0x7fff)>>(INVSQ_LOOKUP_I_SHIFT-1); + long d=a&INVSQ_LOOKUP_I_MASK; /* 0.10 */ + long val=INVSQ_LOOKUP_I[i]- /* 1.16 */ + ((INVSQ_LOOKUP_IDel[i]*d)>>INVSQ_LOOKUP_I_SHIFT); /* result 1.16 */ + val*=ADJUST_SQRT2[e&1]; + e=(e>>1)+21; + return(val>>e); +} + +/* interpolated lookup based fromdB function, domain -140dB to 0dB only */ +/* a is in n.12 format */ +#ifdef _LOW_ACCURACY_ +static inline ogg_int32_t vorbis_fromdBlook_i(long a){ + if(a>0) return 0x7fffffff; + if(a<(-140<<12)) return 0; + return FLOOR_fromdB_LOOKUP[((a+140)*467)>>20]<<9; +} +#else +static inline ogg_int32_t vorbis_fromdBlook_i(long a){ + if(a>0) return 0x7fffffff; + if(a<(-140<<12)) return 0; + return FLOOR_fromdB_LOOKUP[((a+(140<<12))*467)>>20]; +} +#endif + +/* interpolated lookup based cos function, domain 0 to PI only */ +/* a is in 0.16 format, where 0==0, 2^^16-1==PI, return 0.14 */ +static inline ogg_int32_t vorbis_coslook_i(long a){ + int i=a>>COS_LOOKUP_I_SHIFT; + int d=a&COS_LOOKUP_I_MASK; + return COS_LOOKUP_I[i]- ((d*(COS_LOOKUP_I[i]-COS_LOOKUP_I[i+1]))>> + COS_LOOKUP_I_SHIFT); +} + +/* interpolated half-wave lookup based cos function */ +/* a is in 0.16 format, where 0==0, 2^^16==PI, return .LSP_FRACBITS */ +static inline ogg_int32_t vorbis_coslook2_i(long a){ + int i=a>>COS_LOOKUP_I_SHIFT; + int d=a&COS_LOOKUP_I_MASK; + return ((COS_LOOKUP_I[i]<> + (COS_LOOKUP_I_SHIFT-LSP_FRACBITS+14); +} + +static const ogg_uint16_t barklook[54]={ + 0,51,102,154, 206,258,311,365, + 420,477,535,594, 656,719,785,854, + 926,1002,1082,1166, 1256,1352,1454,1564, + 1683,1812,1953,2107, 2276,2463,2670,2900, + 3155,3440,3756,4106, 4493,4919,5387,5901, + 6466,7094,7798,8599, 9528,10623,11935,13524, + 15453,17775,20517,23667, 27183,31004 +}; + +/* used in init only; interpolate the long way */ +static inline ogg_int32_t toBARK(int n){ + int i; + for(i=0;i<54;i++) + if(n>=barklook[i] && n>17); + } +} + +static const unsigned char MLOOP_1[64]={ + 0,10,11,11, 12,12,12,12, 13,13,13,13, 13,13,13,13, + 14,14,14,14, 14,14,14,14, 14,14,14,14, 14,14,14,14, + 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, + 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, +}; + +static const unsigned char MLOOP_2[64]={ + 0,4,5,5, 6,6,6,6, 7,7,7,7, 7,7,7,7, + 8,8,8,8, 8,8,8,8, 8,8,8,8, 8,8,8,8, + 9,9,9,9, 9,9,9,9, 9,9,9,9, 9,9,9,9, + 9,9,9,9, 9,9,9,9, 9,9,9,9, 9,9,9,9, +}; + +static const unsigned char MLOOP_3[8]={0,1,2,2,3,3,3,3}; + +void vorbis_lsp_to_curve(ogg_int32_t *curve,int n,int ln, + ogg_int32_t *lsp,int m, + ogg_int32_t amp, + ogg_int32_t ampoffset, + ogg_int32_t nyq){ + + /* 0 <= m < 256 */ + + /* set up for using all int later */ + int i; + int ampoffseti=ampoffset*4096; + int ampi=amp; + ogg_int32_t *ilsp=(ogg_int32_t *)alloca(m*sizeof(*ilsp)); + + ogg_uint32_t inyq= (1UL<<31) / toBARK(nyq); + ogg_uint32_t imap= (1UL<<31) / ln; + ogg_uint32_t tBnyq1 = toBARK(nyq)<<1; + + /* Besenham for frequency scale to avoid a division */ + int f=0; + int fdx=n; + int fbase=nyq/fdx; + int ferr=0; + int fdy=nyq-fbase*fdx; + int map=0; + +#ifdef _LOW_ACCURACY_ + ogg_uint32_t nextbark=((tBnyq1<<11)/ln)>>12; +#else + ogg_uint32_t nextbark=MULT31(imap>>1,tBnyq1); +#endif + int nextf=barklook[nextbark>>14]+(((nextbark&0x3fff)* + (barklook[(nextbark>>14)+1]-barklook[nextbark>>14]))>>14); + + /* lsp is in 8.24, range 0 to PI; coslook wants it in .16 0 to 1*/ + for(i=0;i>10)*0x517d)>>14; +#endif + + /* safeguard against a malicious stream */ + if(val<0 || (val>>COS_LOOKUP_I_SHIFT)>=COS_LOOKUP_I_SZ){ + memset(curve,0,sizeof(*curve)*n); + return; + } + + ilsp[i]=vorbis_coslook_i(val); + } + + i=0; + while(i>15); + + +#ifdef _V_LSP_MATH_ASM + lsp_loop_asm(&qi,&pi,&qexp,ilsp,wi,m); + + pi=((pi*pi)>>16); + qi=((qi*qi)>>16); + + if(m&1){ + qexp= qexp*2-28*((m+1)>>1)+m; + pi*=(1<<14)-((wi*wi)>>14); + qi+=pi>>14; + }else{ + qexp= qexp*2-13*m; + + pi*=(1<<14)-wi; + qi*=(1<<14)+wi; + + qi=(qi+pi)>>14; + } + + if(qi&0xffff0000){ /* checks for 1.xxxxxxxxxxxxxxxx */ + qi>>=1; qexp++; + }else + lsp_norm_asm(&qi,&qexp); + +#else + + qi*=labs(ilsp[0]-wi); + pi*=labs(ilsp[1]-wi); + + for(j=3;j>25])) + if(!(shift=MLOOP_2[(pi|qi)>>19])) + shift=MLOOP_3[(pi|qi)>>16]; + + qi=(qi>>shift)*labs(ilsp[j-1]-wi); + pi=(pi>>shift)*labs(ilsp[j]-wi); + qexp+=shift; + } + if(!(shift=MLOOP_1[(pi|qi)>>25])) + if(!(shift=MLOOP_2[(pi|qi)>>19])) + shift=MLOOP_3[(pi|qi)>>16]; + + /* pi,qi normalized collectively, both tracked using qexp */ + + if(m&1){ + /* odd order filter; slightly assymetric */ + /* the last coefficient */ + qi=(qi>>shift)*labs(ilsp[j-1]-wi); + pi=(pi>>shift)<<14; + qexp+=shift; + + if(!(shift=MLOOP_1[(pi|qi)>>25])) + if(!(shift=MLOOP_2[(pi|qi)>>19])) + shift=MLOOP_3[(pi|qi)>>16]; + + pi>>=shift; + qi>>=shift; + qexp+=shift-14*((m+1)>>1); + + pi=((pi*pi)>>16); + qi=((qi*qi)>>16); + qexp=qexp*2+m; + + pi*=(1<<14)-((wi*wi)>>14); + qi+=pi>>14; + + }else{ + /* even order filter; still symmetric */ + + /* p*=p(1-w), q*=q(1+w), let normalization drift because it isn't + worth tracking step by step */ + + pi>>=shift; + qi>>=shift; + qexp+=shift-7*m; + + pi=((pi*pi)>>16); + qi=((qi*qi)>>16); + qexp=qexp*2+m; + + pi*=(1<<14)-wi; + qi*=(1<<14)+wi; + qi=(qi+pi)>>14; + + } + + + /* we've let the normalization drift because it wasn't important; + however, for the lookup, things must be normalized again. We + need at most one right shift or a number of left shifts */ + + if(qi&0xffff0000){ /* checks for 1.xxxxxxxxxxxxxxxx */ + qi>>=1; qexp++; + }else + while(qi && !(qi&0x8000)){ /* checks for 0.0xxxxxxxxxxxxxxx or less*/ + qi<<=1; qexp--; + } + +#endif + + amp=vorbis_fromdBlook_i(ampi* /* n.4 */ + vorbis_invsqlook_i(qi,qexp)- + /* m.8, m+n<=8 */ + ampoffseti); /* 8.12[0] */ + +#ifdef _LOW_ACCURACY_ + amp>>=9; +#endif + curve[i]= MULT31_SHIFT15(curve[i],amp); + + while(++i=fdx){ + ferr-=fdx; + f++; + } + f+=fbase; + + if(f>=nextf)break; + + curve[i]= MULT31_SHIFT15(curve[i],amp); + } + + while(1){ + map++; + + if(map+1>12; +#else + nextbark=MULT31((map+1)*(imap>>1),tBnyq1); +#endif + nextf=barklook[nextbark>>14]+ + (((nextbark&0x3fff)* + (barklook[(nextbark>>14)+1]-barklook[nextbark>>14]))>>14); + if(f<=nextf)break; + + }else{ + nextf=9999999; + break; + } + } + if(map>=ln){ + map=ln-1; /* guard against the approximation */ + nextf=9999999; + } + } +} + +/*************** vorbis decode glue ************/ + +void floor0_free_info(vorbis_info_floor *i){ + vorbis_info_floor0 *info=(vorbis_info_floor0 *)i; + if(info)_ogg_free(info); +} + +vorbis_info_floor *floor0_info_unpack (vorbis_info *vi,oggpack_buffer *opb){ + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + int j; + + vorbis_info_floor0 *info=(vorbis_info_floor0 *)_ogg_malloc(sizeof(*info)); + info->order=oggpack_read(opb,8); + info->rate=oggpack_read(opb,16); + info->barkmap=oggpack_read(opb,16); + info->ampbits=oggpack_read(opb,6); + info->ampdB=oggpack_read(opb,8); + info->numbooks=oggpack_read(opb,4)+1; + + if(info->order<1)goto err_out; + if(info->rate<1)goto err_out; + if(info->barkmap<1)goto err_out; + + for(j=0;jnumbooks;j++){ + info->books[j]=oggpack_read(opb,8); + if(info->books[j]>=ci->books)goto err_out; + } + + if(oggpack_eop(opb))goto err_out; + return(info); + + err_out: + floor0_free_info(info); + return(NULL); +} + +int floor0_memosize(vorbis_info_floor *i){ + vorbis_info_floor0 *info=(vorbis_info_floor0 *)i; + return info->order+1; +} + +ogg_int32_t *floor0_inverse1(vorbis_dsp_state *vd,vorbis_info_floor *i, + ogg_int32_t *lsp){ + vorbis_info_floor0 *info=(vorbis_info_floor0 *)i; + int j,k; + + int ampraw=oggpack_read(&vd->opb,info->ampbits); + if(ampraw>0){ /* also handles the -1 out of data case */ + long maxval=(1<ampbits)-1; + int amp=((ampraw*info->ampdB)<<4)/maxval; + int booknum=oggpack_read(&vd->opb,_ilog(info->numbooks)); + + if(booknum!=-1 && booknumnumbooks){ /* be paranoid */ + codec_setup_info *ci=(codec_setup_info *)vd->vi->codec_setup; + codebook *b=ci->book_param+info->books[booknum]; + ogg_int32_t last=0; + + if(vorbis_book_decodev_set(b,lsp,&vd->opb,info->order,-24)==-1)goto eop; + for(j=0;jorder;){ + for(k=0;jorder && kdim;k++,j++)lsp[j]+=last; + last=lsp[j-1]; + } + + lsp[info->order]=amp; + return(lsp); + } + } + eop: + return(NULL); +} + +int floor0_inverse2(vorbis_dsp_state *vd,vorbis_info_floor *i, + ogg_int32_t *lsp,ogg_int32_t *out){ + vorbis_info_floor0 *info=(vorbis_info_floor0 *)i; + codec_setup_info *ci=(codec_setup_info *)vd->vi->codec_setup; + + if(lsp){ + ogg_int32_t amp=lsp[info->order]; + + /* take the coefficients back to a spectral envelope curve */ + vorbis_lsp_to_curve(out,ci->blocksizes[vd->W]/2,info->barkmap, + lsp,info->order,amp,info->ampdB, + info->rate>>1); + return(1); + } + memset(out,0,sizeof(*out)*ci->blocksizes[vd->W]/2); + return(0); +} + diff --git a/components/spotify/cspot/bell/tremor/floor1.c b/components/spotify/cspot/bell/tremor/floor1.c new file mode 100644 index 00000000..b75442f9 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/floor1.c @@ -0,0 +1,352 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: floor backend 1 implementation + + ********************************************************************/ + +#include +#include +#include +#include "ogg.h" +#include "ivorbiscodec.h" +#include "codec_internal.h" +#include "codebook.h" +#include "misc.h" + +extern const ogg_int32_t FLOOR_fromdB_LOOKUP[]; +#define floor1_rangedB 140 /* floor 1 fixed at -140dB to 0dB range */ +#define VIF_POSIT 63 + +/***********************************************/ + +void floor1_free_info(vorbis_info_floor *i){ + vorbis_info_floor1 *info=(vorbis_info_floor1 *)i; + if(info){ + if(info->class)_ogg_free(info->class); + if(info->partitionclass)_ogg_free(info->partitionclass); + if(info->postlist)_ogg_free(info->postlist); + if(info->forward_index)_ogg_free(info->forward_index); + if(info->hineighbor)_ogg_free(info->hineighbor); + if(info->loneighbor)_ogg_free(info->loneighbor); + memset(info,0,sizeof(*info)); + _ogg_free(info); + } +} + +static int ilog(unsigned int v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +static void vorbis_mergesort(char *index,ogg_uint16_t *vals,ogg_uint16_t n){ + ogg_uint16_t i,j; + char *temp,*A=index,*B=_ogg_malloc(n*sizeof(*B)); + + for(i=1;icodec_setup; + int j,k,count=0,maxclass=-1,rangebits; + + vorbis_info_floor1 *info=(vorbis_info_floor1 *)_ogg_calloc(1,sizeof(*info)); + /* read partitions */ + info->partitions=oggpack_read(opb,5); /* only 0 to 31 legal */ + info->partitionclass= + (char *)_ogg_malloc(info->partitions*sizeof(*info->partitionclass)); + for(j=0;jpartitions;j++){ + info->partitionclass[j]=oggpack_read(opb,4); /* only 0 to 15 legal */ + if(maxclasspartitionclass[j])maxclass=info->partitionclass[j]; + } + + /* read partition classes */ + info->class= + (floor1class *)_ogg_malloc((maxclass+1)*sizeof(*info->class)); + for(j=0;jclass[j].class_dim=oggpack_read(opb,3)+1; /* 1 to 8 */ + info->class[j].class_subs=oggpack_read(opb,2); /* 0,1,2,3 bits */ + if(oggpack_eop(opb)<0) goto err_out; + if(info->class[j].class_subs) + info->class[j].class_book=oggpack_read(opb,8); + else + info->class[j].class_book=0; + if(info->class[j].class_book>=ci->books)goto err_out; + for(k=0;k<(1<class[j].class_subs);k++){ + info->class[j].class_subbook[k]=oggpack_read(opb,8)-1; + if(info->class[j].class_subbook[k]>=ci->books && + info->class[j].class_subbook[k]!=0xff)goto err_out; + } + } + + /* read the post list */ + info->mult=oggpack_read(opb,2)+1; /* only 1,2,3,4 legal now */ + rangebits=oggpack_read(opb,4); + + for(j=0,k=0;jpartitions;j++) + count+=info->class[info->partitionclass[j]].class_dim; + info->postlist= + (ogg_uint16_t *)_ogg_malloc((count+2)*sizeof(*info->postlist)); + info->forward_index= + (char *)_ogg_malloc((count+2)*sizeof(*info->forward_index)); + info->loneighbor= + (char *)_ogg_malloc(count*sizeof(*info->loneighbor)); + info->hineighbor= + (char *)_ogg_malloc(count*sizeof(*info->hineighbor)); + + count=0; + for(j=0,k=0;jpartitions;j++){ + count+=info->class[info->partitionclass[j]].class_dim; + if(count>VIF_POSIT)goto err_out; + for(;kpostlist[k+2]=oggpack_read(opb,rangebits); + if(t>=(1<postlist[0]=0; + info->postlist[1]=1<posts=count+2; + + /* also store a sorted position index */ + for(j=0;jposts;j++)info->forward_index[j]=j; + vorbis_mergesort(info->forward_index,info->postlist,info->posts); + + /* discover our neighbors for decode where we don't use fit flags + (that would push the neighbors outward) */ + for(j=0;jposts-2;j++){ + int lo=0; + int hi=1; + int lx=0; + int hx=info->postlist[1]; + int currentx=info->postlist[j+2]; + for(k=0;kpostlist[k]; + if(x>lx && xcurrentx){ + hi=k; + hx=x; + } + } + info->loneighbor[j]=lo; + info->hineighbor[j]=hi; + } + + return(info); + + err_out: + floor1_free_info(info); + return(NULL); +} + +static int render_point(int x0,int x1,int y0,int y1,int x){ + y0&=0x7fff; /* mask off flag */ + y1&=0x7fff; + + { + int dy=y1-y0; + int adx=x1-x0; + int ady=abs(dy); + int err=ady*(x-x0); + + int off=err/adx; + if(dy<0)return(y0-off); + return(y0+off); + } +} + +static void render_line(int n,int x0,int x1,int y0,int y1,ogg_int32_t *d){ + int dy=y1-y0; + int adx=x1-x0; + int ady=abs(dy); + int base=dy/adx; + int sy=(dy<0?base-1:base+1); + int x=x0; + int y=y0; + int err=0; + + if(n>x1)n=x1; + ady-=abs(base*adx); + + if(x=adx){ + err-=adx; + y+=sy; + }else{ + y+=base; + } + d[x]= MULT31_SHIFT15(d[x],FLOOR_fromdB_LOOKUP[y]); + } +} + +int floor1_memosize(vorbis_info_floor *i){ + vorbis_info_floor1 *info=(vorbis_info_floor1 *)i; + return info->posts; +} + +static int quant_look[4]={256,128,86,64}; + +ogg_int32_t *floor1_inverse1(vorbis_dsp_state *vd,vorbis_info_floor *in, + ogg_int32_t *fit_value){ + vorbis_info_floor1 *info=(vorbis_info_floor1 *)in; + codec_setup_info *ci=(codec_setup_info *)vd->vi->codec_setup; + + int i,j,k; + codebook *books=ci->book_param; + int quant_q=quant_look[info->mult-1]; + + /* unpack wrapped/predicted values from stream */ + if(oggpack_read(&vd->opb,1)==1){ + fit_value[0]=oggpack_read(&vd->opb,ilog(quant_q-1)); + fit_value[1]=oggpack_read(&vd->opb,ilog(quant_q-1)); + + /* partition by partition */ + /* partition by partition */ + for(i=0,j=2;ipartitions;i++){ + int classv=info->partitionclass[i]; + int cdim=info->class[classv].class_dim; + int csubbits=info->class[classv].class_subs; + int csub=1<class[classv].class_book,&vd->opb); + + if(cval==-1)goto eop; + } + + for(k=0;kclass[classv].class_subbook[cval&(csub-1)]; + cval>>=csubbits; + if(book!=0xff){ + if((fit_value[j+k]=vorbis_book_decode(books+book,&vd->opb))==-1) + goto eop; + }else{ + fit_value[j+k]=0; + } + } + j+=cdim; + } + + /* unwrap positive values and reconsitute via linear interpolation */ + for(i=2;iposts;i++){ + int predicted=render_point(info->postlist[info->loneighbor[i-2]], + info->postlist[info->hineighbor[i-2]], + fit_value[info->loneighbor[i-2]], + fit_value[info->hineighbor[i-2]], + info->postlist[i]); + int hiroom=quant_q-predicted; + int loroom=predicted; + int room=(hiroom=room){ + if(hiroom>loroom){ + val = val-loroom; + }else{ + val = -1-(val-hiroom); + } + }else{ + if(val&1){ + val= -((val+1)>>1); + }else{ + val>>=1; + } + } + + fit_value[i]=val+predicted; + fit_value[info->loneighbor[i-2]]&=0x7fff; + fit_value[info->hineighbor[i-2]]&=0x7fff; + + }else{ + fit_value[i]=predicted|0x8000; + } + + } + + return(fit_value); + } + eop: + return(NULL); +} + +int floor1_inverse2(vorbis_dsp_state *vd,vorbis_info_floor *in, + ogg_int32_t *fit_value,ogg_int32_t *out){ + vorbis_info_floor1 *info=(vorbis_info_floor1 *)in; + + codec_setup_info *ci=(codec_setup_info *)vd->vi->codec_setup; + int n=ci->blocksizes[vd->W]/2; + int j; + + if(fit_value){ + /* render the lines */ + int hx=0; + int lx=0; + int ly=fit_value[0]*info->mult; + for(j=1;jposts;j++){ + int current=info->forward_index[j]; + int hy=fit_value[current]&0x7fff; + if(hy==fit_value[current]){ + + hy*=info->mult; + hx=info->postlist[current]; + + render_line(n,lx,hx,ly,hy,out); + + lx=hx; + ly=hy; + } + } + for(j=hx;j>8)+1)>>1) +#else +# define XdB(n) (n) +#endif + +const ogg_int32_t FLOOR_fromdB_LOOKUP[256]={ + XdB(0x000000e5), XdB(0x000000f4), XdB(0x00000103), XdB(0x00000114), + XdB(0x00000126), XdB(0x00000139), XdB(0x0000014e), XdB(0x00000163), + XdB(0x0000017a), XdB(0x00000193), XdB(0x000001ad), XdB(0x000001c9), + XdB(0x000001e7), XdB(0x00000206), XdB(0x00000228), XdB(0x0000024c), + XdB(0x00000272), XdB(0x0000029b), XdB(0x000002c6), XdB(0x000002f4), + XdB(0x00000326), XdB(0x0000035a), XdB(0x00000392), XdB(0x000003cd), + XdB(0x0000040c), XdB(0x00000450), XdB(0x00000497), XdB(0x000004e4), + XdB(0x00000535), XdB(0x0000058c), XdB(0x000005e8), XdB(0x0000064a), + XdB(0x000006b3), XdB(0x00000722), XdB(0x00000799), XdB(0x00000818), + XdB(0x0000089e), XdB(0x0000092e), XdB(0x000009c6), XdB(0x00000a69), + XdB(0x00000b16), XdB(0x00000bcf), XdB(0x00000c93), XdB(0x00000d64), + XdB(0x00000e43), XdB(0x00000f30), XdB(0x0000102d), XdB(0x0000113a), + XdB(0x00001258), XdB(0x0000138a), XdB(0x000014cf), XdB(0x00001629), + XdB(0x0000179a), XdB(0x00001922), XdB(0x00001ac4), XdB(0x00001c82), + XdB(0x00001e5c), XdB(0x00002055), XdB(0x0000226f), XdB(0x000024ac), + XdB(0x0000270e), XdB(0x00002997), XdB(0x00002c4b), XdB(0x00002f2c), + XdB(0x0000323d), XdB(0x00003581), XdB(0x000038fb), XdB(0x00003caf), + XdB(0x000040a0), XdB(0x000044d3), XdB(0x0000494c), XdB(0x00004e10), + XdB(0x00005323), XdB(0x0000588a), XdB(0x00005e4b), XdB(0x0000646b), + XdB(0x00006af2), XdB(0x000071e5), XdB(0x0000794c), XdB(0x0000812e), + XdB(0x00008993), XdB(0x00009283), XdB(0x00009c09), XdB(0x0000a62d), + XdB(0x0000b0f9), XdB(0x0000bc79), XdB(0x0000c8b9), XdB(0x0000d5c4), + XdB(0x0000e3a9), XdB(0x0000f274), XdB(0x00010235), XdB(0x000112fd), + XdB(0x000124dc), XdB(0x000137e4), XdB(0x00014c29), XdB(0x000161bf), + XdB(0x000178bc), XdB(0x00019137), XdB(0x0001ab4a), XdB(0x0001c70e), + XdB(0x0001e4a1), XdB(0x0002041f), XdB(0x000225aa), XdB(0x00024962), + XdB(0x00026f6d), XdB(0x000297f0), XdB(0x0002c316), XdB(0x0002f109), + XdB(0x000321f9), XdB(0x00035616), XdB(0x00038d97), XdB(0x0003c8b4), + XdB(0x000407a7), XdB(0x00044ab2), XdB(0x00049218), XdB(0x0004de23), + XdB(0x00052f1e), XdB(0x0005855c), XdB(0x0005e135), XdB(0x00064306), + XdB(0x0006ab33), XdB(0x00071a24), XdB(0x0007904b), XdB(0x00080e20), + XdB(0x00089422), XdB(0x000922da), XdB(0x0009bad8), XdB(0x000a5cb6), + XdB(0x000b091a), XdB(0x000bc0b1), XdB(0x000c8436), XdB(0x000d5471), + XdB(0x000e3233), XdB(0x000f1e5f), XdB(0x001019e4), XdB(0x001125c1), + XdB(0x00124306), XdB(0x001372d5), XdB(0x0014b663), XdB(0x00160ef7), + XdB(0x00177df0), XdB(0x001904c1), XdB(0x001aa4f9), XdB(0x001c603d), + XdB(0x001e384f), XdB(0x00202f0f), XdB(0x0022467a), XdB(0x002480b1), + XdB(0x0026dff7), XdB(0x002966b3), XdB(0x002c1776), XdB(0x002ef4fc), + XdB(0x0032022d), XdB(0x00354222), XdB(0x0038b828), XdB(0x003c67c2), + XdB(0x004054ae), XdB(0x004482e8), XdB(0x0048f6af), XdB(0x004db488), + XdB(0x0052c142), XdB(0x005821ff), XdB(0x005ddc33), XdB(0x0063f5b0), + XdB(0x006a74a7), XdB(0x00715faf), XdB(0x0078bdce), XdB(0x0080967f), + XdB(0x0088f1ba), XdB(0x0091d7f9), XdB(0x009b5247), XdB(0x00a56a41), + XdB(0x00b02a27), XdB(0x00bb9ce2), XdB(0x00c7ce12), XdB(0x00d4ca17), + XdB(0x00e29e20), XdB(0x00f15835), XdB(0x0101074b), XdB(0x0111bb4e), + XdB(0x01238531), XdB(0x01367704), XdB(0x014aa402), XdB(0x016020a7), + XdB(0x017702c3), XdB(0x018f6190), XdB(0x01a955cb), XdB(0x01c4f9cf), + XdB(0x01e269a8), XdB(0x0201c33b), XdB(0x0223265a), XdB(0x0246b4ea), + XdB(0x026c9302), XdB(0x0294e716), XdB(0x02bfda13), XdB(0x02ed9793), + XdB(0x031e4e09), XdB(0x03522ee4), XdB(0x03896ed0), XdB(0x03c445e2), + XdB(0x0402efd6), XdB(0x0445ac4b), XdB(0x048cbefc), XdB(0x04d87013), + XdB(0x05290c67), XdB(0x057ee5ca), XdB(0x05da5364), XdB(0x063bb204), + XdB(0x06a36485), XdB(0x0711d42b), XdB(0x0787710e), XdB(0x0804b299), + XdB(0x088a17ef), XdB(0x0918287e), XdB(0x09af747c), XdB(0x0a50957e), + XdB(0x0afc2f19), XdB(0x0bb2ef7f), XdB(0x0c759034), XdB(0x0d44d6ca), + XdB(0x0e2195bc), XdB(0x0f0cad0d), XdB(0x10070b62), XdB(0x1111aeea), + XdB(0x122da66c), XdB(0x135c120f), XdB(0x149e24d9), XdB(0x15f525b1), + XdB(0x176270e3), XdB(0x18e7794b), XdB(0x1a85c9ae), XdB(0x1c3f06d1), + XdB(0x1e14f07d), XdB(0x200963d7), XdB(0x221e5ccd), XdB(0x2455f870), + XdB(0x26b2770b), XdB(0x29363e2b), XdB(0x2be3db5c), XdB(0x2ebe06b6), + XdB(0x31c7a55b), XdB(0x3503ccd4), XdB(0x3875c5aa), XdB(0x3c210f44), + XdB(0x4009632b), XdB(0x4432b8cf), XdB(0x48a149bc), XdB(0x4d59959e), + XdB(0x52606733), XdB(0x57bad899), XdB(0x5d6e593a), XdB(0x6380b298), + XdB(0x69f80e9a), XdB(0x70dafda8), XdB(0x78307d76), XdB(0x7fffffff), +}; + diff --git a/components/spotify/cspot/bell/tremor/framing.c b/components/spotify/cspot/bell/tremor/framing.c new file mode 100644 index 00000000..4842143d --- /dev/null +++ b/components/spotify/cspot/bell/tremor/framing.c @@ -0,0 +1,1117 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: decode Ogg streams back into raw packets + + note: The CRC code is directly derived from public domain code by + Ross Williams (ross@guest.adelaide.edu.au). See docs/framing.html + for details. + + ********************************************************************/ + +#include +#include +#include "ogg.h" +#include "misc.h" + + +/* A complete description of Ogg framing exists in docs/framing.html */ + +/* basic, centralized Ogg memory management based on linked lists of + references to refcounted memory buffers. References and buffers + are both recycled. Buffers are passed around and consumed in + reference form. */ + +static ogg_buffer_state *ogg_buffer_create(void){ + ogg_buffer_state *bs=_ogg_calloc(1,sizeof(*bs)); + return bs; +} + +/* destruction is 'lazy'; there may be memory references outstanding, + and yanking the buffer state out from underneath would be + antisocial. Dealloc what is currently unused and have + _release_one watch for the stragglers to come in. When they do, + finish destruction. */ + +/* call the helper while holding lock */ +static void _ogg_buffer_destroy(ogg_buffer_state *bs){ + ogg_buffer *bt; + ogg_reference *rt; + + if(bs->shutdown){ + + bt=bs->unused_buffers; + rt=bs->unused_references; + + while(bt){ + ogg_buffer *b=bt; + bt=b->ptr.next; + if(b->data)_ogg_free(b->data); + _ogg_free(b); + } + bs->unused_buffers=0; + while(rt){ + ogg_reference *r=rt; + rt=r->next; + _ogg_free(r); + } + bs->unused_references=0; + + if(!bs->outstanding) + _ogg_free(bs); + + } +} + +static void ogg_buffer_destroy(ogg_buffer_state *bs){ + bs->shutdown=1; + _ogg_buffer_destroy(bs); +} + +static ogg_buffer *_fetch_buffer(ogg_buffer_state *bs,long bytes){ + ogg_buffer *ob; + bs->outstanding++; + + /* do we have an unused buffer sitting in the pool? */ + if(bs->unused_buffers){ + ob=bs->unused_buffers; + bs->unused_buffers=ob->ptr.next; + + /* if the unused buffer is too small, grow it */ + if(ob->sizedata=_ogg_realloc(ob->data,bytes); + ob->size=bytes; + } + }else{ + /* allocate a new buffer */ + ob=_ogg_malloc(sizeof(*ob)); + ob->data=_ogg_malloc(bytes<16?16:bytes); + ob->size=bytes; + } + + ob->refcount=1; + ob->ptr.owner=bs; + return ob; +} + +static ogg_reference *_fetch_ref(ogg_buffer_state *bs){ + ogg_reference *or; + bs->outstanding++; + + /* do we have an unused reference sitting in the pool? */ + if(bs->unused_references){ + or=bs->unused_references; + bs->unused_references=or->next; + }else{ + /* allocate a new reference */ + or=_ogg_malloc(sizeof(*or)); + } + + or->begin=0; + or->length=0; + or->next=0; + return or; +} + +/* fetch a reference pointing to a fresh, initially continguous buffer + of at least [bytes] length */ +static ogg_reference *ogg_buffer_alloc(ogg_buffer_state *bs,long bytes){ + ogg_buffer *ob=_fetch_buffer(bs,bytes); + ogg_reference *or=_fetch_ref(bs); + or->buffer=ob; + return or; +} + +/* enlarge the data buffer in the current link */ +static void ogg_buffer_realloc(ogg_reference *or,long bytes){ + ogg_buffer *ob=or->buffer; + + /* if the unused buffer is too small, grow it */ + if(ob->sizedata=_ogg_realloc(ob->data,bytes); + ob->size=bytes; + } +} + +static void _ogg_buffer_mark_one(ogg_reference *or){ + or->buffer->refcount++; +} + +/* increase the refcount of the buffers to which the reference points */ +static void ogg_buffer_mark(ogg_reference *or){ + while(or){ + _ogg_buffer_mark_one(or); + or=or->next; + } +} + +/* duplicate a reference (pointing to the same actual buffer memory) + and increment buffer refcount. If the desired segment is zero + length, a zero length ref is returned. */ +static ogg_reference *ogg_buffer_sub(ogg_reference *or,long length){ + ogg_reference *ret=0,*head=0; + + /* duplicate the reference chain; increment refcounts */ + while(or && length){ + ogg_reference *temp=_fetch_ref(or->buffer->ptr.owner); + if(head) + head->next=temp; + else + ret=temp; + head=temp; + head->buffer=or->buffer; + head->begin=or->begin; + head->length=length; + if(head->length>or->length) + head->length=or->length; + + length-=head->length; + or=or->next; + } + + ogg_buffer_mark(ret); + return ret; +} + +ogg_reference *ogg_buffer_dup(ogg_reference *or){ + ogg_reference *ret=0,*head=0; + /* duplicate the reference chain; increment refcounts */ + while(or){ + ogg_reference *temp=_fetch_ref(or->buffer->ptr.owner); + if(head) + head->next=temp; + else + ret=temp; + head=temp; + head->buffer=or->buffer; + head->begin=or->begin; + head->length=or->length; + or=or->next; + } + + ogg_buffer_mark(ret); + return ret; +} + +/* split a reference into two references; 'return' is a reference to + the buffer preceeding pos and 'head'/'tail' are the buffer past the + split. If pos is at or past the end of the passed in segment, + 'head/tail' are NULL */ +static ogg_reference *ogg_buffer_split(ogg_reference **tail, + ogg_reference **head,long pos){ + + /* walk past any preceeding fragments to one of: + a) the exact boundary that seps two fragments + b) the fragment that needs split somewhere in the middle */ + ogg_reference *ret=*tail; + ogg_reference *or=*tail; + + while(or && pos>or->length){ + pos-=or->length; + or=or->next; + } + + if(!or || pos==0){ + + return 0; + + }else{ + + if(pos>=or->length){ + /* exact split, or off the end? */ + if(or->next){ + + /* a split */ + *tail=or->next; + or->next=0; + + }else{ + + /* off or at the end */ + *tail=*head=0; + + } + }else{ + + /* split within a fragment */ + long lengthA=pos; + long beginB=or->begin+pos; + long lengthB=or->length-pos; + + /* make a new reference to tail the second piece */ + *tail=_fetch_ref(or->buffer->ptr.owner); + + (*tail)->buffer=or->buffer; + (*tail)->begin=beginB; + (*tail)->length=lengthB; + (*tail)->next=or->next; + _ogg_buffer_mark_one(*tail); + if(head && or==*head)*head=*tail; + + /* update the first piece */ + or->next=0; + or->length=lengthA; + + } + } + return ret; +} + +static void ogg_buffer_release_one(ogg_reference *or){ + ogg_buffer *ob=or->buffer; + ogg_buffer_state *bs=ob->ptr.owner; + + ob->refcount--; + if(ob->refcount==0){ + bs->outstanding--; /* for the returned buffer */ + ob->ptr.next=bs->unused_buffers; + bs->unused_buffers=ob; + } + + bs->outstanding--; /* for the returned reference */ + or->next=bs->unused_references; + bs->unused_references=or; + + _ogg_buffer_destroy(bs); /* lazy cleanup (if needed) */ + +} + +/* release the references, decrease the refcounts of buffers to which + they point, release any buffers with a refcount that drops to zero */ +static void ogg_buffer_release(ogg_reference *or){ + while(or){ + ogg_reference *next=or->next; + ogg_buffer_release_one(or); + or=next; + } +} + +static ogg_reference *ogg_buffer_pretruncate(ogg_reference *or,long pos){ + /* release preceeding fragments we don't want */ + while(or && pos>=or->length){ + ogg_reference *next=or->next; + pos-=or->length; + ogg_buffer_release_one(or); + or=next; + } + if (or) { + or->begin+=pos; + or->length-=pos; + } + return or; +} + +static ogg_reference *ogg_buffer_walk(ogg_reference *or){ + if(!or)return NULL; + while(or->next){ + or=or->next; + } + return(or); +} + +/* *head is appended to the front end (head) of *tail; both continue to + be valid pointers, with *tail at the tail and *head at the head */ +static ogg_reference *ogg_buffer_cat(ogg_reference *tail, ogg_reference *head){ + if(!tail)return head; + + while(tail->next){ + tail=tail->next; + } + tail->next=head; + return ogg_buffer_walk(head); +} + +static void _positionB(oggbyte_buffer *b,int pos){ + if(pospos){ + /* start at beginning, scan forward */ + b->ref=b->baseref; + b->pos=0; + b->end=b->pos+b->ref->length; + b->ptr=b->ref->buffer->data+b->ref->begin; + } +} + +static void _positionF(oggbyte_buffer *b,int pos){ + /* scan forward for position */ + while(pos>=b->end){ + /* just seek forward */ + b->pos+=b->ref->length; + b->ref=b->ref->next; + b->end=b->ref->length+b->pos; + b->ptr=b->ref->buffer->data+b->ref->begin; + } +} + +static int oggbyte_init(oggbyte_buffer *b,ogg_reference *or){ + memset(b,0,sizeof(*b)); + if(or){ + b->ref=b->baseref=or; + b->pos=0; + b->end=b->ref->length; + b->ptr=b->ref->buffer->data+b->ref->begin; + return 0; + }else + return -1; +} + +static void oggbyte_set4(oggbyte_buffer *b,ogg_uint32_t val,int pos){ + int i; + _positionB(b,pos); + for(i=0;i<4;i++){ + _positionF(b,pos); + b->ptr[pos-b->pos]=val; + val>>=8; + ++pos; + } +} + +static unsigned char oggbyte_read1(oggbyte_buffer *b,int pos){ + _positionB(b,pos); + _positionF(b,pos); + return b->ptr[pos-b->pos]; +} + +static ogg_uint32_t oggbyte_read4(oggbyte_buffer *b,int pos){ + ogg_uint32_t ret; + _positionB(b,pos); + _positionF(b,pos); + ret=b->ptr[pos-b->pos]; + _positionF(b,++pos); + ret|=b->ptr[pos-b->pos]<<8; + _positionF(b,++pos); + ret|=b->ptr[pos-b->pos]<<16; + _positionF(b,++pos); + ret|=b->ptr[pos-b->pos]<<24; + return ret; +} + +static ogg_int64_t oggbyte_read8(oggbyte_buffer *b,int pos){ + ogg_int64_t ret; + unsigned char t[7]; + int i; + _positionB(b,pos); + for(i=0;i<7;i++){ + _positionF(b,pos); + t[i]=b->ptr[pos++ -b->pos]; + } + + _positionF(b,pos); + ret=b->ptr[pos-b->pos]; + + for(i=6;i>=0;--i) + ret= ret<<8 | t[i]; + + return ret; +} + +/* Now we get to the actual framing code */ + +int ogg_page_version(ogg_page *og){ + oggbyte_buffer ob; + if(oggbyte_init(&ob,og->header))return -1; + return oggbyte_read1(&ob,4); +} + +int ogg_page_continued(ogg_page *og){ + oggbyte_buffer ob; + if(oggbyte_init(&ob,og->header))return -1; + return oggbyte_read1(&ob,5)&0x01; +} + +int ogg_page_bos(ogg_page *og){ + oggbyte_buffer ob; + if(oggbyte_init(&ob,og->header))return -1; + return oggbyte_read1(&ob,5)&0x02; +} + +int ogg_page_eos(ogg_page *og){ + oggbyte_buffer ob; + if(oggbyte_init(&ob,og->header))return -1; + return oggbyte_read1(&ob,5)&0x04; +} + +ogg_int64_t ogg_page_granulepos(ogg_page *og){ + oggbyte_buffer ob; + if(oggbyte_init(&ob,og->header))return -1; + return oggbyte_read8(&ob,6); +} + +ogg_uint32_t ogg_page_serialno(ogg_page *og){ + oggbyte_buffer ob; + if(oggbyte_init(&ob,og->header)) return 0xffffffffUL; + return oggbyte_read4(&ob,14); +} + +ogg_uint32_t ogg_page_pageno(ogg_page *og){ + oggbyte_buffer ob; + if(oggbyte_init(&ob,og->header))return 0xffffffffUL; + return oggbyte_read4(&ob,18); +} + +/* returns the number of packets that are completed on this page (if + the leading packet is begun on a previous page, but ends on this + page, it's counted */ + +/* NOTE: +If a page consists of a packet begun on a previous page, and a new +packet begun (but not completed) on this page, the return will be: + ogg_page_packets(page) ==1, + ogg_page_continued(page) !=0 + +If a page happens to be a single packet that was begun on a +previous page, and spans to the next page (in the case of a three or +more page packet), the return will be: + ogg_page_packets(page) ==0, + ogg_page_continued(page) !=0 +*/ + +int ogg_page_packets(ogg_page *og){ + int i; + int n; + int count=0; + oggbyte_buffer ob; + oggbyte_init(&ob,og->header); + + n=oggbyte_read1(&ob,26); + for(i=0;ibufferpool=ogg_buffer_create(); + return oy; +} + +int ogg_sync_destroy(ogg_sync_state *oy){ + if(oy){ + ogg_sync_reset(oy); + ogg_buffer_destroy(oy->bufferpool); + memset(oy,0,sizeof(*oy)); + _ogg_free(oy); + } + return OGG_SUCCESS; +} + +unsigned char *ogg_sync_bufferin(ogg_sync_state *oy, long bytes){ + + /* [allocate and] expose a buffer for data submission. + + If there is no head fragment + allocate one and expose it + else + if the current head fragment has sufficient unused space + expose it + else + if the current head fragment is unused + resize and expose it + else + allocate new fragment and expose it + */ + + /* base case; fifo uninitialized */ + if(!oy->fifo_head){ + oy->fifo_head=oy->fifo_tail=ogg_buffer_alloc(oy->bufferpool,bytes); + return oy->fifo_head->buffer->data; + } + + /* space left in current fragment case */ + if(oy->fifo_head->buffer->size- + oy->fifo_head->length- + oy->fifo_head->begin >= bytes) + return oy->fifo_head->buffer->data+ + oy->fifo_head->length+oy->fifo_head->begin; + + /* current fragment is unused, but too small */ + if(!oy->fifo_head->length){ + ogg_buffer_realloc(oy->fifo_head,bytes); + return oy->fifo_head->buffer->data+oy->fifo_head->begin; + } + + /* current fragment used/full; get new fragment */ + { + ogg_reference *new=ogg_buffer_alloc(oy->bufferpool,bytes); + oy->fifo_head->next=new; + oy->fifo_head=new; + } + return oy->fifo_head->buffer->data; +} + +int ogg_sync_wrote(ogg_sync_state *oy, long bytes){ + if(!oy->fifo_head)return OGG_EINVAL; + if(oy->fifo_head->buffer->size-oy->fifo_head->length-oy->fifo_head->begin < + bytes)return OGG_EINVAL; + oy->fifo_head->length+=bytes; + oy->fifo_fill+=bytes; + return OGG_SUCCESS; +} + +static ogg_uint32_t _checksum(ogg_reference *or, int bytes){ + ogg_uint32_t crc_reg=0; + int j,post; + + while(or){ + unsigned char *data=or->buffer->data+or->begin; + post=(byteslength?bytes:or->length); + for(j=0;j> 24)&0xff)^data[j]]; + bytes-=j; + or=or->next; + } + + return crc_reg; +} + + +/* sync the stream. This is meant to be useful for finding page + boundaries. + + return values for this: + -n) skipped n bytes + 0) page not ready; more data (no bytes skipped) + n) page synced at current location; page length n bytes + +*/ + +long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ + oggbyte_buffer page; + long bytes,ret=0; + + ogg_page_release(og); + + bytes=oy->fifo_fill; + oggbyte_init(&page,oy->fifo_tail); + + if(oy->headerbytes==0){ + if(bytes<27)goto sync_out; /* not enough for even a minimal header */ + + /* verify capture pattern */ + if(oggbyte_read1(&page,0)!=(int)'O' || + oggbyte_read1(&page,1)!=(int)'g' || + oggbyte_read1(&page,2)!=(int)'g' || + oggbyte_read1(&page,3)!=(int)'S' ) goto sync_fail; + + oy->headerbytes=oggbyte_read1(&page,26)+27; + } + if(bytesheaderbytes)goto sync_out; /* not enough for header + + seg table */ + if(oy->bodybytes==0){ + int i; + /* count up body length in the segment table */ + for(i=0;iheaderbytes-27;i++) + oy->bodybytes+=oggbyte_read1(&page,27+i); + } + + if(oy->bodybytes+oy->headerbytes>bytes)goto sync_out; + + /* we have what appears to be a complete page; last test: verify + checksum */ + { + ogg_uint32_t chksum=oggbyte_read4(&page,22); + oggbyte_set4(&page,0,22); + + /* Compare checksums; memory continues to be common access */ + if(chksum!=_checksum(oy->fifo_tail,oy->bodybytes+oy->headerbytes)){ + + /* D'oh. Mismatch! Corrupt page (or miscapture and not a page + at all). replace the computed checksum with the one actually + read in; remember all the memory is common access */ + + oggbyte_set4(&page,chksum,22); + goto sync_fail; + } + oggbyte_set4(&page,chksum,22); + } + + /* We have a page. Set up page return. */ + if(og){ + /* set up page output */ + og->header=ogg_buffer_split(&oy->fifo_tail,&oy->fifo_head,oy->headerbytes); + og->header_len=oy->headerbytes; + og->body=ogg_buffer_split(&oy->fifo_tail,&oy->fifo_head,oy->bodybytes); + og->body_len=oy->bodybytes; + }else{ + /* simply advance */ + oy->fifo_tail= + ogg_buffer_pretruncate(oy->fifo_tail,oy->headerbytes+oy->bodybytes); + if(!oy->fifo_tail)oy->fifo_head=0; + } + + ret=oy->headerbytes+oy->bodybytes; + oy->unsynced=0; + oy->headerbytes=0; + oy->bodybytes=0; + oy->fifo_fill-=ret; + + return ret; + + sync_fail: + + oy->headerbytes=0; + oy->bodybytes=0; + oy->fifo_tail=ogg_buffer_pretruncate(oy->fifo_tail,1); + ret--; + + /* search forward through fragments for possible capture */ + while(oy->fifo_tail){ + /* invariant: fifo_cursor points to a position in fifo_tail */ + unsigned char *now=oy->fifo_tail->buffer->data+oy->fifo_tail->begin; + unsigned char *next=memchr(now, 'O', oy->fifo_tail->length); + + if(next){ + /* possible capture in this segment */ + long bytes=next-now; + oy->fifo_tail=ogg_buffer_pretruncate(oy->fifo_tail,bytes); + ret-=bytes; + break; + }else{ + /* no capture. advance to next segment */ + long bytes=oy->fifo_tail->length; + ret-=bytes; + oy->fifo_tail=ogg_buffer_pretruncate(oy->fifo_tail,bytes); + } + } + if(!oy->fifo_tail)oy->fifo_head=0; + oy->fifo_fill+=ret; + + sync_out: + return ret; +} + +/* sync the stream and get a page. Keep trying until we find a page. + Supress 'sync errors' after reporting the first. + + return values: + OGG_HOLE) recapture (hole in data) + 0) need more data + 1) page returned + + Returns pointers into buffered data; invalidated by next call to + _stream, _clear, _init, or _buffer */ + +int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og){ + + /* all we need to do is verify a page at the head of the stream + buffer. If it doesn't verify, we look for the next potential + frame */ + + while(1){ + long ret=ogg_sync_pageseek(oy,og); + if(ret>0){ + /* have a page */ + return 1; + } + if(ret==0){ + /* need more data */ + return 0; + } + + /* head did not start a synced page... skipped some bytes */ + if(!oy->unsynced){ + oy->unsynced=1; + return OGG_HOLE; + } + + /* loop. keep looking */ + + } +} + +/* clear things to an initial state. Good to call, eg, before seeking */ +int ogg_sync_reset(ogg_sync_state *oy){ + + ogg_buffer_release(oy->fifo_tail); + oy->fifo_tail=0; + oy->fifo_head=0; + oy->fifo_fill=0; + + oy->unsynced=0; + oy->headerbytes=0; + oy->bodybytes=0; + return OGG_SUCCESS; +} + +ogg_stream_state *ogg_stream_create(int serialno){ + ogg_stream_state *os=_ogg_calloc(1,sizeof(*os)); + os->serialno=serialno; + os->pageno=-1; + return os; +} + +int ogg_stream_destroy(ogg_stream_state *os){ + if(os){ + ogg_buffer_release(os->header_tail); + ogg_buffer_release(os->body_tail); + memset(os,0,sizeof(*os)); + _ogg_free(os); + } + return OGG_SUCCESS; +} + + +#define FINFLAG 0x80000000UL +#define FINMASK 0x7fffffffUL + +static void _next_lace(oggbyte_buffer *ob,ogg_stream_state *os){ + /* search ahead one lace */ + os->body_fill_next=0; + while(os->laceptrlacing_fill){ + int val=oggbyte_read1(ob,27+os->laceptr++); + os->body_fill_next+=val; + if(val<255){ + os->body_fill_next|=FINFLAG; + os->clearflag=1; + break; + } + } +} + +static void _span_queued_page(ogg_stream_state *os){ + while( !(os->body_fill&FINFLAG) ){ + + if(!os->header_tail)break; + + /* first flush out preceeding page header (if any). Body is + flushed as it's consumed, so that's not done here. */ + + if(os->lacing_fill>=0) + os->header_tail=ogg_buffer_pretruncate(os->header_tail, + os->lacing_fill+27); + os->lacing_fill=0; + os->laceptr=0; + os->clearflag=0; + + if(!os->header_tail){ + os->header_head=0; + break; + }else{ + + /* process/prepare next page, if any */ + + long pageno; + oggbyte_buffer ob; + ogg_page og; /* only for parsing header values */ + og.header=os->header_tail; /* only for parsing header values */ + pageno=ogg_page_pageno(&og); + + oggbyte_init(&ob,os->header_tail); + os->lacing_fill=oggbyte_read1(&ob,26); + + /* are we in sequence? */ + if(pageno!=os->pageno){ + if(os->pageno==-1) /* indicates seek or reset */ + os->holeflag=1; /* set for internal use */ + else + os->holeflag=2; /* set for external reporting */ + + os->body_tail=ogg_buffer_pretruncate(os->body_tail, + os->body_fill); + if(os->body_tail==0)os->body_head=0; + os->body_fill=0; + + } + + if(ogg_page_continued(&og)){ + if(os->body_fill==0){ + /* continued packet, but no preceeding data to continue */ + /* dump the first partial packet on the page */ + _next_lace(&ob,os); + os->body_tail= + ogg_buffer_pretruncate(os->body_tail,os->body_fill_next&FINMASK); + if(os->body_tail==0)os->body_head=0; + /* set span flag */ + if(!os->spanflag && !os->holeflag)os->spanflag=2; + } + }else{ + if(os->body_fill>0){ + /* preceeding data to continue, but not a continued page */ + /* dump body_fill */ + os->body_tail=ogg_buffer_pretruncate(os->body_tail, + os->body_fill); + if(os->body_tail==0)os->body_head=0; + os->body_fill=0; + + /* set espan flag */ + if(!os->spanflag && !os->holeflag)os->spanflag=2; + } + } + + if(os->laceptrlacing_fill){ + os->granulepos=ogg_page_granulepos(&og); + + /* get current packet size & flag */ + _next_lace(&ob,os); + os->body_fill+=os->body_fill_next; /* addition handles the flag fine; + unsigned on purpose */ + /* ...and next packet size & flag */ + _next_lace(&ob,os); + + } + + os->pageno=pageno+1; + os->e_o_s=ogg_page_eos(&og); + os->b_o_s=ogg_page_bos(&og); + + } + } +} + +/* add the incoming page to the stream state; we decompose the page + into packet segments here as well. */ + +int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ + + int serialno=ogg_page_serialno(og); + int version=ogg_page_version(og); + + /* check the serial number */ + if(serialno!=os->serialno){ + ogg_page_release(og); + return OGG_ESERIAL; + } + if(version>0){ + ogg_page_release(og); + return OGG_EVERSION; + } + + /* add to fifos */ + if(!os->body_tail){ + os->body_tail=og->body; + os->body_head=ogg_buffer_walk(og->body); + }else{ + os->body_head=ogg_buffer_cat(os->body_head,og->body); + } + if(!os->header_tail){ + os->header_tail=og->header; + os->header_head=ogg_buffer_walk(og->header); + os->lacing_fill=-27; + }else{ + os->header_head=ogg_buffer_cat(os->header_head,og->header); + } + + memset(og,0,sizeof(*og)); + return OGG_SUCCESS; +} + +int ogg_stream_reset(ogg_stream_state *os){ + + ogg_buffer_release(os->header_tail); + ogg_buffer_release(os->body_tail); + os->header_tail=os->header_head=0; + os->body_tail=os->body_head=0; + + os->e_o_s=0; + os->b_o_s=0; + os->pageno=-1; + os->packetno=0; + os->granulepos=0; + + os->body_fill=0; + os->lacing_fill=0; + + os->holeflag=0; + os->spanflag=0; + os->clearflag=0; + os->laceptr=0; + os->body_fill_next=0; + + return OGG_SUCCESS; +} + +int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno){ + ogg_stream_reset(os); + os->serialno=serialno; + return OGG_SUCCESS; +} + +static int _packetout(ogg_stream_state *os,ogg_packet *op,int adv){ + + ogg_packet_release(op); + _span_queued_page(os); + + if(os->holeflag){ + int temp=os->holeflag; + if(os->clearflag) + os->holeflag=0; + else + os->holeflag=1; + if(temp==2){ + os->packetno++; + return OGG_HOLE; + } + } + if(os->spanflag){ + int temp=os->spanflag; + if(os->clearflag) + os->spanflag=0; + else + os->spanflag=1; + if(temp==2){ + os->packetno++; + return OGG_SPAN; + } + } + + if(!(os->body_fill&FINFLAG)) return 0; + if(!op && !adv)return 1; /* just using peek as an inexpensive way + to ask if there's a whole packet + waiting */ + if(op){ + op->b_o_s=os->b_o_s; + if(os->e_o_s && os->body_fill_next==0) + op->e_o_s=os->e_o_s; + else + op->e_o_s=0; + if( (os->body_fill&FINFLAG) && !(os->body_fill_next&FINFLAG) ) + op->granulepos=os->granulepos; + else + op->granulepos=-1; + op->packetno=os->packetno; + } + + if(adv){ + oggbyte_buffer ob; + oggbyte_init(&ob,os->header_tail); + + /* split the body contents off */ + if(op){ + op->packet=ogg_buffer_split(&os->body_tail,&os->body_head, + os->body_fill&FINMASK); + op->bytes=os->body_fill&FINMASK; + }else{ + os->body_tail=ogg_buffer_pretruncate(os->body_tail, + os->body_fill&FINMASK); + if(os->body_tail==0)os->body_head=0; + } + + /* update lacing pointers */ + os->body_fill=os->body_fill_next; + _next_lace(&ob,os); + }else{ + if(op){ + op->packet=ogg_buffer_sub(os->body_tail,os->body_fill&FINMASK); + op->bytes=os->body_fill&FINMASK; + } + } + + if(adv){ + os->packetno++; + os->b_o_s=0; + } + + return 1; +} + +int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op){ + return _packetout(os,op,1); +} + +int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op){ + return _packetout(os,op,0); +} + +int ogg_packet_release(ogg_packet *op) { + if(op){ + ogg_buffer_release(op->packet); + memset(op, 0, sizeof(*op)); + } + return OGG_SUCCESS; +} + +int ogg_page_release(ogg_page *og) { + if(og){ + ogg_buffer_release(og->header); + ogg_buffer_release(og->body); + memset(og, 0, sizeof(*og)); + } + return OGG_SUCCESS; +} + +void ogg_page_dup(ogg_page *dup,ogg_page *orig){ + dup->header_len=orig->header_len; + dup->body_len=orig->body_len; + dup->header=ogg_buffer_dup(orig->header); + dup->body=ogg_buffer_dup(orig->body); +} + diff --git a/components/spotify/cspot/bell/tremor/info.c b/components/spotify/cspot/bell/tremor/info.c new file mode 100644 index 00000000..be5df496 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/info.c @@ -0,0 +1,348 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: maintain the info structure, info <-> header packets + + ********************************************************************/ + +/* general handling of the header and the vorbis_info structure (and + substructures) */ + +#include +#include +#include +#include "ogg.h" +#include "ivorbiscodec.h" +#include "codec_internal.h" +#include "codebook.h" +#include "misc.h" +#include "os.h" + +/* helpers */ +static void _v_readstring(oggpack_buffer *o,char *buf,int bytes){ + while(bytes--){ + *buf++=oggpack_read(o,8); + } +} + +void vorbis_comment_init(vorbis_comment *vc){ + memset(vc,0,sizeof(*vc)); +} + +/* This is more or less the same as strncasecmp - but that doesn't exist + * everywhere, and this is a fairly trivial function, so we include it */ +static int tagcompare(const char *s1, const char *s2, int n){ + int c=0; + while(c < n){ + if(toupper(s1[c]) != toupper(s2[c])) + return !0; + c++; + } + return 0; +} + +char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count){ + long i; + int found = 0; + int taglen = strlen(tag)+1; /* +1 for the = we append */ + char *fulltag = (char *)alloca(taglen+ 1); + + strcpy(fulltag, tag); + strcat(fulltag, "="); + + for(i=0;icomments;i++){ + if(!tagcompare(vc->user_comments[i], fulltag, taglen)){ + if(count == found) + /* We return a pointer to the data, not a copy */ + return vc->user_comments[i] + taglen; + else + found++; + } + } + return NULL; /* didn't find anything */ +} + +int vorbis_comment_query_count(vorbis_comment *vc, char *tag){ + int i,count=0; + int taglen = strlen(tag)+1; /* +1 for the = we append */ + char *fulltag = (char *)alloca(taglen+1); + strcpy(fulltag,tag); + strcat(fulltag, "="); + + for(i=0;icomments;i++){ + if(!tagcompare(vc->user_comments[i], fulltag, taglen)) + count++; + } + + return count; +} + +void vorbis_comment_clear(vorbis_comment *vc){ + if(vc){ + long i; + for(i=0;icomments;i++) + if(vc->user_comments[i])_ogg_free(vc->user_comments[i]); + if(vc->user_comments)_ogg_free(vc->user_comments); + if(vc->comment_lengths)_ogg_free(vc->comment_lengths); + if(vc->vendor)_ogg_free(vc->vendor); + } + memset(vc,0,sizeof(*vc)); +} + +/* blocksize 0 is guaranteed to be short, 1 is guarantted to be long. + They may be equal, but short will never ge greater than long */ +int vorbis_info_blocksize(vorbis_info *vi,int zo){ + codec_setup_info *ci = (codec_setup_info *)vi->codec_setup; + return ci ? ci->blocksizes[zo] : -1; +} + +/* used by synthesis, which has a full, alloced vi */ +void vorbis_info_init(vorbis_info *vi){ + memset(vi,0,sizeof(*vi)); + vi->codec_setup=(codec_setup_info *)_ogg_calloc(1,sizeof(codec_setup_info)); +} + +void vorbis_info_clear(vorbis_info *vi){ + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + int i; + + if(ci){ + + if(ci->mode_param)_ogg_free(ci->mode_param); + + if(ci->map_param){ + for(i=0;imaps;i++) /* unpack does the range checking */ + mapping_clear_info(ci->map_param+i); + _ogg_free(ci->map_param); + } + + if(ci->floor_param){ + for(i=0;ifloors;i++) /* unpack does the range checking */ + if(ci->floor_type[i]) + floor1_free_info(ci->floor_param[i]); + else + floor0_free_info(ci->floor_param[i]); + _ogg_free(ci->floor_param); + _ogg_free(ci->floor_type); + } + + if(ci->residue_param){ + for(i=0;iresidues;i++) /* unpack does the range checking */ + res_clear_info(ci->residue_param+i); + _ogg_free(ci->residue_param); + } + + if(ci->book_param){ + for(i=0;ibooks;i++) + vorbis_book_clear(ci->book_param+i); + _ogg_free(ci->book_param); + } + + _ogg_free(ci); + } + + memset(vi,0,sizeof(*vi)); +} + +/* Header packing/unpacking ********************************************/ + +static int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb){ + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + if(!ci)return(OV_EFAULT); + + vi->version=oggpack_read(opb,32); + if(vi->version!=0)return(OV_EVERSION); + + vi->channels=oggpack_read(opb,8); + vi->rate=oggpack_read(opb,32); + + vi->bitrate_upper=oggpack_read(opb,32); + vi->bitrate_nominal=oggpack_read(opb,32); + vi->bitrate_lower=oggpack_read(opb,32); + + ci->blocksizes[0]=1<blocksizes[1]=1<rate>=64000 || ci->blocksizes[1]>4096)goto err_out; +#else + if(vi->rate<64000 && ci->blocksizes[1]>4096)goto err_out; +#endif + + if(vi->rate<1)goto err_out; + if(vi->channels<1)goto err_out; + if(ci->blocksizes[0]<64)goto err_out; + if(ci->blocksizes[1]blocksizes[0])goto err_out; + if(ci->blocksizes[1]>8192)goto err_out; + + if(oggpack_read(opb,1)!=1)goto err_out; /* EOP check */ + + return(0); + err_out: + vorbis_info_clear(vi); + return(OV_EBADHEADER); +} + +static int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb){ + int i; + int vendorlen=oggpack_read(opb,32); + if(vendorlen<0)goto err_out; + vc->vendor=(char *)_ogg_calloc(vendorlen+1,1); + _v_readstring(opb,vc->vendor,vendorlen); + vc->comments=oggpack_read(opb,32); + if(vc->comments<0)goto err_out; + vc->user_comments=(char **)_ogg_calloc(vc->comments+1,sizeof(*vc->user_comments)); + vc->comment_lengths=(int *)_ogg_calloc(vc->comments+1, sizeof(*vc->comment_lengths)); + + for(i=0;icomments;i++){ + int len=oggpack_read(opb,32); + if(len<0)goto err_out; + vc->comment_lengths[i]=len; + vc->user_comments[i]=(char *)_ogg_calloc(len+1,1); + _v_readstring(opb,vc->user_comments[i],len); + } + if(oggpack_read(opb,1)!=1)goto err_out; /* EOP check */ + + return(0); + err_out: + vorbis_comment_clear(vc); + return(OV_EBADHEADER); +} + +/* all of the real encoding details are here. The modes, books, + everything */ +static int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb){ + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + int i; + if(!ci)return(OV_EFAULT); + + /* codebooks */ + ci->books=oggpack_read(opb,8)+1; + ci->book_param=(codebook *)_ogg_calloc(ci->books,sizeof(*ci->book_param)); + for(i=0;ibooks;i++) + if(vorbis_book_unpack(opb,ci->book_param+i))goto err_out; + + /* time backend settings, not actually used */ + i=oggpack_read(opb,6); + for(;i>=0;i--) + if(oggpack_read(opb,16)!=0)goto err_out; + + /* floor backend settings */ + ci->floors=oggpack_read(opb,6)+1; + ci->floor_param=_ogg_malloc(sizeof(*ci->floor_param)*ci->floors); + ci->floor_type=_ogg_malloc(sizeof(*ci->floor_type)*ci->floors); + for(i=0;ifloors;i++){ + ci->floor_type[i]=oggpack_read(opb,16); + if(ci->floor_type[i]<0 || ci->floor_type[i]>=VI_FLOORB)goto err_out; + if(ci->floor_type[i]) + ci->floor_param[i]=floor1_info_unpack(vi,opb); + else + ci->floor_param[i]=floor0_info_unpack(vi,opb); + if(!ci->floor_param[i])goto err_out; + } + + /* residue backend settings */ + ci->residues=oggpack_read(opb,6)+1; + ci->residue_param=_ogg_malloc(sizeof(*ci->residue_param)*ci->residues); + for(i=0;iresidues;i++) + if(res_unpack(ci->residue_param+i,vi,opb))goto err_out; + + /* map backend settings */ + ci->maps=oggpack_read(opb,6)+1; + ci->map_param=_ogg_malloc(sizeof(*ci->map_param)*ci->maps); + for(i=0;imaps;i++){ + if(oggpack_read(opb,16)!=0)goto err_out; + if(mapping_info_unpack(ci->map_param+i,vi,opb))goto err_out; + } + + /* mode settings */ + ci->modes=oggpack_read(opb,6)+1; + ci->mode_param= + (vorbis_info_mode *)_ogg_malloc(ci->modes*sizeof(*ci->mode_param)); + for(i=0;imodes;i++){ + ci->mode_param[i].blockflag=oggpack_read(opb,1); + if(oggpack_read(opb,16))goto err_out; + if(oggpack_read(opb,16))goto err_out; + ci->mode_param[i].mapping=oggpack_read(opb,8); + if(ci->mode_param[i].mapping>=ci->maps)goto err_out; + } + + if(oggpack_read(opb,1)!=1)goto err_out; /* top level EOP check */ + + return(0); + err_out: + vorbis_info_clear(vi); + return(OV_EBADHEADER); +} + +/* The Vorbis header is in three packets; the initial small packet in + the first page that identifies basic parameters, a second packet + with bitstream comments and a third packet that holds the + codebook. */ + +int vorbis_dsp_headerin(vorbis_info *vi,vorbis_comment *vc,ogg_packet *op){ + oggpack_buffer opb; + + if(op){ + oggpack_readinit(&opb,op->packet); + + /* Which of the three types of header is this? */ + /* Also verify header-ness, vorbis */ + { + char buffer[6]; + int packtype=oggpack_read(&opb,8); + memset(buffer,0,6); + _v_readstring(&opb,buffer,6); + if(memcmp(buffer,"vorbis",6)){ + /* not a vorbis header */ + return(OV_ENOTVORBIS); + } + switch(packtype){ + case 0x01: /* least significant *bit* is read first */ + if(!op->b_o_s){ + /* Not the initial packet */ + return(OV_EBADHEADER); + } + if(vi->rate!=0){ + /* previously initialized info header */ + return(OV_EBADHEADER); + } + + return(_vorbis_unpack_info(vi,&opb)); + + case 0x03: /* least significant *bit* is read first */ + if(vi->rate==0){ + /* um... we didn't get the initial header */ + return(OV_EBADHEADER); + } + + return(_vorbis_unpack_comment(vc,&opb)); + + case 0x05: /* least significant *bit* is read first */ + if(vi->rate==0 || vc->vendor==NULL){ + /* um... we didn;t get the initial header or comments yet */ + return(OV_EBADHEADER); + } + + return(_vorbis_unpack_books(vi,&opb)); + + default: + /* Not a valid vorbis header type */ + return(OV_EBADHEADER); + break; + } + } + } + return(OV_EBADHEADER); +} + diff --git a/components/spotify/cspot/bell/tremor/ivorbiscodec.h b/components/spotify/cspot/bell/tremor/ivorbiscodec.h new file mode 100644 index 00000000..0eea9eb8 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/ivorbiscodec.h @@ -0,0 +1,104 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: libvorbis codec headers + + ********************************************************************/ + +#ifndef _vorbis_codec_h_ +#define _vorbis_codec_h_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include "ogg.h" + +struct vorbis_dsp_state; +typedef struct vorbis_dsp_state vorbis_dsp_state; + +typedef struct vorbis_info{ + int version; + int channels; + long rate; + + /* The below bitrate declarations are *hints*. + Combinations of the three values carry the following implications: + + all three set to the same value: + implies a fixed rate bitstream + only nominal set: + implies a VBR stream that averages the nominal bitrate. No hard + upper/lower limit + upper and or lower set: + implies a VBR bitstream that obeys the bitrate limits. nominal + may also be set to give a nominal rate. + none set: + the coder does not care to speculate. + */ + + long bitrate_upper; + long bitrate_nominal; + long bitrate_lower; + long bitrate_window; + + void *codec_setup; +} vorbis_info; + +typedef struct vorbis_comment{ + char **user_comments; + int *comment_lengths; + int comments; + char *vendor; + +} vorbis_comment; + + +/* Vorbis PRIMITIVES: general ***************************************/ + +extern void vorbis_info_init(vorbis_info *vi); +extern void vorbis_info_clear(vorbis_info *vi); +extern int vorbis_info_blocksize(vorbis_info *vi,int zo); +extern void vorbis_comment_init(vorbis_comment *vc); +extern void vorbis_comment_add(vorbis_comment *vc, char *comment); +extern void vorbis_comment_add_tag(vorbis_comment *vc, + char *tag, char *contents); +extern char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count); +extern int vorbis_comment_query_count(vorbis_comment *vc, char *tag); +extern void vorbis_comment_clear(vorbis_comment *vc); + +/* Vorbis ERRORS and return codes ***********************************/ + +#define OV_FALSE -1 +#define OV_EOF -2 +#define OV_HOLE -3 + +#define OV_EREAD -128 +#define OV_EFAULT -129 +#define OV_EIMPL -130 +#define OV_EINVAL -131 +#define OV_ENOTVORBIS -132 +#define OV_EBADHEADER -133 +#define OV_EVERSION -134 +#define OV_ENOTAUDIO -135 +#define OV_EBADPACKET -136 +#define OV_EBADLINK -137 +#define OV_ENOSEEK -138 + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + diff --git a/components/spotify/cspot/bell/tremor/ivorbisfile.h b/components/spotify/cspot/bell/tremor/ivorbisfile.h new file mode 100644 index 00000000..7ebc0427 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/ivorbisfile.h @@ -0,0 +1,122 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: stdio-based convenience library for opening/seeking/decoding + + ********************************************************************/ + +#ifndef _OV_FILE_H_ +#define _OV_FILE_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include +#include "ivorbiscodec.h" + +/* The function prototypes for the callbacks are basically the same as for + * the stdio functions fread, fseek, fclose, ftell. + * The one difference is that the FILE * arguments have been replaced with + * a void * - this is to be used as a pointer to whatever internal data these + * functions might need. In the stdio case, it's just a FILE * cast to a void * + * + * If you use other functions, check the docs for these functions and return + * the right values. For seek_func(), you *MUST* return -1 if the stream is + * unseekable + */ +typedef struct { + size_t (*read_func) (void *ptr, size_t size, size_t nmemb, void *datasource); + int (*seek_func) (void *datasource, ogg_int64_t offset, int whence); + int (*close_func) (void *datasource); + long (*tell_func) (void *datasource); +} ov_callbacks; + +typedef struct OggVorbis_File { + void *datasource; /* Pointer to a FILE *, etc. */ + int seekable; + ogg_int64_t offset; + ogg_int64_t end; + ogg_sync_state *oy; + + /* If the FILE handle isn't seekable (eg, a pipe), only the current + stream appears */ + int links; + ogg_int64_t *offsets; + ogg_int64_t *dataoffsets; + ogg_uint32_t *serialnos; + ogg_int64_t *pcmlengths; + vorbis_info vi; + vorbis_comment vc; + + /* Decoding working state local storage */ + ogg_int64_t pcm_offset; + int ready_state; + ogg_uint32_t current_serialno; + int current_link; + + ogg_int64_t bittrack; + ogg_int64_t samptrack; + + ogg_stream_state *os; /* take physical pages, weld into a logical + stream of packets */ + vorbis_dsp_state *vd; /* central working state for the packet->PCM decoder */ + + ov_callbacks callbacks; + +} OggVorbis_File; + +extern int ov_clear(OggVorbis_File *vf); +extern int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes); +extern int ov_open_callbacks(void *datasource, OggVorbis_File *vf, + char *initial, long ibytes, ov_callbacks callbacks); + +extern int ov_test(FILE *f,OggVorbis_File *vf,char *initial,long ibytes); +extern int ov_test_callbacks(void *datasource, OggVorbis_File *vf, + char *initial, long ibytes, ov_callbacks callbacks); +extern int ov_test_open(OggVorbis_File *vf); + +extern long ov_bitrate(OggVorbis_File *vf,int i); +extern long ov_bitrate_instant(OggVorbis_File *vf); +extern long ov_streams(OggVorbis_File *vf); +extern long ov_seekable(OggVorbis_File *vf); +extern long ov_serialnumber(OggVorbis_File *vf,int i); + +extern ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i); +extern ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i); +extern ogg_int64_t ov_time_total(OggVorbis_File *vf,int i); + +extern int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_time_seek(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_time_seek_page(OggVorbis_File *vf,ogg_int64_t pos); + +extern ogg_int64_t ov_raw_tell(OggVorbis_File *vf); +extern ogg_int64_t ov_pcm_tell(OggVorbis_File *vf); +extern ogg_int64_t ov_time_tell(OggVorbis_File *vf); + +extern vorbis_info *ov_info(OggVorbis_File *vf,int link); +extern vorbis_comment *ov_comment(OggVorbis_File *vf,int link); + +extern long ov_read(OggVorbis_File *vf,void *buffer,int length, + int *bitstream); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + + diff --git a/components/spotify/cspot/bell/tremor/ivorbisfile_example.c b/components/spotify/cspot/bell/tremor/ivorbisfile_example.c new file mode 100644 index 00000000..c99cfd25 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/ivorbisfile_example.c @@ -0,0 +1,86 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: simple example decoder using vorbisidec + + ********************************************************************/ + +/* Takes a vorbis bitstream from stdin and writes raw stereo PCM to + stdout using vorbisfile. Using vorbisfile is much simpler than + dealing with libvorbis. */ + +#include +#include +#include "ivorbiscodec.h" +#include "ivorbisfile.h" + +#ifdef _WIN32 /* We need the following two to set stdin/stdout to binary */ +#include +#include +#endif + +char pcmout[4096]; /* take 4k out of the data segment, not the stack */ + +int main(){ + OggVorbis_File vf; + int eof=0; + int current_section; + +#ifdef _WIN32 /* We need to set stdin/stdout to binary mode. Damn windows. */ + /* Beware the evil ifdef. We avoid these where we can, but this one we + cannot. Don't add any more, you'll probably go to hell if you do. */ + _setmode( _fileno( stdin ), _O_BINARY ); + _setmode( _fileno( stdout ), _O_BINARY ); +#endif + + if(ov_open(stdin, &vf, NULL, 0) < 0) { + fprintf(stderr,"Input does not appear to be an Ogg bitstream.\n"); + exit(1); + } + + /* Throw the comments plus a few lines about the bitstream we're + decoding */ + { + char **ptr=ov_comment(&vf,-1)->user_comments; + vorbis_info *vi=ov_info(&vf,-1); + while(*ptr){ + fprintf(stderr,"%s\n",*ptr); + ++ptr; + } + fprintf(stderr,"\nBitstream is %d channel, %ldHz\n",vi->channels,vi->rate); + fprintf(stderr,"\nDecoded length: %ld samples\n", + (long)ov_pcm_total(&vf,-1)); + fprintf(stderr,"Encoded by: %s\n\n",ov_comment(&vf,-1)->vendor); + } + + while(!eof){ + long ret=ov_read(&vf,pcmout,sizeof(pcmout),¤t_section); + if (ret == 0) { + /* EOF */ + eof=1; + } else if (ret < 0) { + /* error in the stream. Not a problem, just reporting it in + case we (the app) cares. In this case, we don't. */ + } else { + /* we don't bother dealing with sample rate changes, etc, but + you'll have to*/ + fwrite(pcmout,1,ret,stdout); + } + } + + /* cleanup */ + ov_clear(&vf); + + fprintf(stderr,"Done.\n"); + return(0); +} diff --git a/components/spotify/cspot/bell/tremor/lsp_lookup.h b/components/spotify/cspot/bell/tremor/lsp_lookup.h new file mode 100644 index 00000000..72c7581a --- /dev/null +++ b/components/spotify/cspot/bell/tremor/lsp_lookup.h @@ -0,0 +1,108 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: lookup data + + ********************************************************************/ + +#ifndef _V_LOOKUP_DATA_H_ +#define _V_LOOKUP_DATA_H_ + +#include "os_types.h" + +#define INVSQ_LOOKUP_I_SHIFT 10 +#define INVSQ_LOOKUP_I_MASK 1023 +static const long INVSQ_LOOKUP_I[64+1]={ + 92682, 91966, 91267, 90583, + 89915, 89261, 88621, 87995, + 87381, 86781, 86192, 85616, + 85051, 84497, 83953, 83420, + 82897, 82384, 81880, 81385, + 80899, 80422, 79953, 79492, + 79039, 78594, 78156, 77726, + 77302, 76885, 76475, 76072, + 75674, 75283, 74898, 74519, + 74146, 73778, 73415, 73058, + 72706, 72359, 72016, 71679, + 71347, 71019, 70695, 70376, + 70061, 69750, 69444, 69141, + 68842, 68548, 68256, 67969, + 67685, 67405, 67128, 66855, + 66585, 66318, 66054, 65794, + 65536, +}; + +static const long INVSQ_LOOKUP_IDel[64]={ + 716, 699, 684, 668, + 654, 640, 626, 614, + 600, 589, 576, 565, + 554, 544, 533, 523, + 513, 504, 495, 486, + 477, 469, 461, 453, + 445, 438, 430, 424, + 417, 410, 403, 398, + 391, 385, 379, 373, + 368, 363, 357, 352, + 347, 343, 337, 332, + 328, 324, 319, 315, + 311, 306, 303, 299, + 294, 292, 287, 284, + 280, 277, 273, 270, + 267, 264, 260, 258, +}; + +#define COS_LOOKUP_I_SHIFT 9 +#define COS_LOOKUP_I_MASK 511 +#define COS_LOOKUP_I_SZ 128 +static const ogg_int32_t COS_LOOKUP_I[COS_LOOKUP_I_SZ+1]={ + 16384, 16379, 16364, 16340, + 16305, 16261, 16207, 16143, + 16069, 15986, 15893, 15791, + 15679, 15557, 15426, 15286, + 15137, 14978, 14811, 14635, + 14449, 14256, 14053, 13842, + 13623, 13395, 13160, 12916, + 12665, 12406, 12140, 11866, + 11585, 11297, 11003, 10702, + 10394, 10080, 9760, 9434, + 9102, 8765, 8423, 8076, + 7723, 7366, 7005, 6639, + 6270, 5897, 5520, 5139, + 4756, 4370, 3981, 3590, + 3196, 2801, 2404, 2006, + 1606, 1205, 804, 402, + 0, -401, -803, -1204, + -1605, -2005, -2403, -2800, + -3195, -3589, -3980, -4369, + -4755, -5138, -5519, -5896, + -6269, -6638, -7004, -7365, + -7722, -8075, -8422, -8764, + -9101, -9433, -9759, -10079, + -10393, -10701, -11002, -11296, + -11584, -11865, -12139, -12405, + -12664, -12915, -13159, -13394, + -13622, -13841, -14052, -14255, + -14448, -14634, -14810, -14977, + -15136, -15285, -15425, -15556, + -15678, -15790, -15892, -15985, + -16068, -16142, -16206, -16260, + -16304, -16339, -16363, -16378, + -16383, +}; + +#endif + + + + + diff --git a/components/spotify/cspot/bell/tremor/mapping0.c b/components/spotify/cspot/bell/tremor/mapping0.c new file mode 100644 index 00000000..23e3b8e9 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/mapping0.c @@ -0,0 +1,241 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: channel mapping 0 implementation + + ********************************************************************/ + +#include +#include +#include +#include +#include "ogg.h" +#include "os.h" +#include "ivorbiscodec.h" +#include "mdct.h" +#include "codec_internal.h" +#include "codebook.h" +#include "misc.h" + +void mapping_clear_info(vorbis_info_mapping *info){ + if(info){ + if(info->chmuxlist)_ogg_free(info->chmuxlist); + if(info->submaplist)_ogg_free(info->submaplist); + if(info->coupling)_ogg_free(info->coupling); + memset(info,0,sizeof(*info)); + } +} + +static int ilog(unsigned int v){ + int ret=0; + if(v)--v; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +/* also responsible for range checking */ +int mapping_info_unpack(vorbis_info_mapping *info,vorbis_info *vi, + oggpack_buffer *opb){ + int i; + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + memset(info,0,sizeof(*info)); + + if(oggpack_read(opb,1)) + info->submaps=oggpack_read(opb,4)+1; + else + info->submaps=1; + + if(oggpack_read(opb,1)){ + info->coupling_steps=oggpack_read(opb,8)+1; + info->coupling= + _ogg_malloc(info->coupling_steps*sizeof(*info->coupling)); + + for(i=0;icoupling_steps;i++){ + int testM=info->coupling[i].mag=oggpack_read(opb,ilog(vi->channels)); + int testA=info->coupling[i].ang=oggpack_read(opb,ilog(vi->channels)); + + if(testM<0 || + testA<0 || + testM==testA || + testM>=vi->channels || + testA>=vi->channels) goto err_out; + } + + } + + if(oggpack_read(opb,2)>0)goto err_out; /* 2,3:reserved */ + + if(info->submaps>1){ + info->chmuxlist=_ogg_malloc(sizeof(*info->chmuxlist)*vi->channels); + for(i=0;ichannels;i++){ + info->chmuxlist[i]=oggpack_read(opb,4); + if(info->chmuxlist[i]>=info->submaps)goto err_out; + } + } + + info->submaplist=_ogg_malloc(sizeof(*info->submaplist)*info->submaps); + for(i=0;isubmaps;i++){ + int temp=oggpack_read(opb,8); + info->submaplist[i].floor=oggpack_read(opb,8); + if(info->submaplist[i].floor>=ci->floors)goto err_out; + info->submaplist[i].residue=oggpack_read(opb,8); + if(info->submaplist[i].residue>=ci->residues)goto err_out; + } + + return 0; + + err_out: + mapping_clear_info(info); + return -1; +} + +int mapping_inverse(vorbis_dsp_state *vd,vorbis_info_mapping *info){ + vorbis_info *vi=vd->vi; + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + + int i,j; + long n=ci->blocksizes[vd->W]; + + ogg_int32_t **pcmbundle= + alloca(sizeof(*pcmbundle)*vi->channels); + int *zerobundle= + alloca(sizeof(*zerobundle)*vi->channels); + int *nonzero= + alloca(sizeof(*nonzero)*vi->channels); + ogg_int32_t **floormemo= + alloca(sizeof(*floormemo)*vi->channels); + + /* recover the spectral envelope; store it in the PCM vector for now */ + for(i=0;ichannels;i++){ + int submap=0; + int floorno; + + if(info->submaps>1) + submap=info->chmuxlist[i]; + floorno=info->submaplist[submap].floor; + + if(ci->floor_type[floorno]){ + /* floor 1 */ + floormemo[i]=alloca(sizeof(*floormemo[i])* + floor1_memosize(ci->floor_param[floorno])); + floormemo[i]=floor1_inverse1(vd,ci->floor_param[floorno],floormemo[i]); + }else{ + /* floor 0 */ + floormemo[i]=alloca(sizeof(*floormemo[i])* + floor0_memosize(ci->floor_param[floorno])); + floormemo[i]=floor0_inverse1(vd,ci->floor_param[floorno],floormemo[i]); + } + + if(floormemo[i]) + nonzero[i]=1; + else + nonzero[i]=0; + memset(vd->work[i],0,sizeof(*vd->work[i])*n/2); + } + + /* channel coupling can 'dirty' the nonzero listing */ + for(i=0;icoupling_steps;i++){ + if(nonzero[info->coupling[i].mag] || + nonzero[info->coupling[i].ang]){ + nonzero[info->coupling[i].mag]=1; + nonzero[info->coupling[i].ang]=1; + } + } + + /* recover the residue into our working vectors */ + for(i=0;isubmaps;i++){ + int ch_in_bundle=0; + for(j=0;jchannels;j++){ + if(!info->chmuxlist || info->chmuxlist[j]==i){ + if(nonzero[j]) + zerobundle[ch_in_bundle]=1; + else + zerobundle[ch_in_bundle]=0; + pcmbundle[ch_in_bundle++]=vd->work[j]; + } + } + + res_inverse(vd,ci->residue_param+info->submaplist[i].residue, + pcmbundle,zerobundle,ch_in_bundle); + } + + //for(j=0;jchannels;j++) + //_analysis_output("coupled",seq+j,vb->pcm[j],-8,n/2,0,0); + + /* channel coupling */ + for(i=info->coupling_steps-1;i>=0;i--){ + ogg_int32_t *pcmM=vd->work[info->coupling[i].mag]; + ogg_int32_t *pcmA=vd->work[info->coupling[i].ang]; + + for(j=0;j0) + if(ang>0){ + pcmM[j]=mag; + pcmA[j]=mag-ang; + }else{ + pcmA[j]=mag; + pcmM[j]=mag+ang; + } + else + if(ang>0){ + pcmM[j]=mag; + pcmA[j]=mag+ang; + }else{ + pcmA[j]=mag; + pcmM[j]=mag-ang; + } + } + } + + //for(j=0;jchannels;j++) + //_analysis_output("residue",seq+j,vb->pcm[j],-8,n/2,0,0); + + /* compute and apply spectral envelope */ + for(i=0;ichannels;i++){ + ogg_int32_t *pcm=vd->work[i]; + int submap=0; + int floorno; + + if(info->submaps>1) + submap=info->chmuxlist[i]; + floorno=info->submaplist[submap].floor; + + if(ci->floor_type[floorno]){ + /* floor 1 */ + floor1_inverse2(vd,ci->floor_param[floorno],floormemo[i],pcm); + }else{ + /* floor 0 */ + floor0_inverse2(vd,ci->floor_param[floorno],floormemo[i],pcm); + } + } + + //for(j=0;jchannels;j++) + //_analysis_output("mdct",seq+j,vb->pcm[j],-24,n/2,0,1); + + /* transform the PCM data; takes PCM vector, vb; modifies PCM vector */ + /* only MDCT right now.... */ + for(i=0;ichannels;i++) + mdct_backward(n,vd->work[i]); + + //for(j=0;jchannels;j++) + //_analysis_output("imdct",seq+j,vb->pcm[j],-24,n,0,0); + + /* all done! */ + return(0); +} diff --git a/components/spotify/cspot/bell/tremor/mdct.c b/components/spotify/cspot/bell/tremor/mdct.c new file mode 100644 index 00000000..22015314 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/mdct.c @@ -0,0 +1,486 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: normalized modified discrete cosine transform + power of two length transform only [64 <= n ] + last mod: $Id: mdct.c,v 1.9.6.5 2003/04/29 04:03:27 xiphmont Exp $ + + Original algorithm adapted long ago from _The use of multirate filter + banks for coding of high quality digital audio_, by T. Sporer, + K. Brandenburg and B. Edler, collection of the European Signal + Processing Conference (EUSIPCO), Amsterdam, June 1992, Vol.1, pp + 211-214 + + The below code implements an algorithm that no longer looks much like + that presented in the paper, but the basic structure remains if you + dig deep enough to see it. + + This module DOES NOT INCLUDE code to generate/apply the window + function. Everybody has their own weird favorite including me... I + happen to like the properties of y=sin(.5PI*sin^2(x)), but others may + vehemently disagree. + + ********************************************************************/ + +#include "ivorbiscodec.h" +#include "os.h" +#include "misc.h" +#include "mdct.h" +#include "mdct_lookup.h" + +STIN void presymmetry(DATA_TYPE *in,int n2,int step){ + DATA_TYPE *aX; + DATA_TYPE *bX; + LOOKUP_T *T; + int n4=n2>>1; + + aX = in+n2-3; + T = sincos_lookup0; + + do{ + REG_TYPE r0= aX[0]; + REG_TYPE r2= aX[2]; + XPROD31( r0, r2, T[0], T[1], &aX[0], &aX[2] ); T+=step; + aX-=4; + }while(aX>=in+n4); + do{ + REG_TYPE r0= aX[0]; + REG_TYPE r2= aX[2]; + XPROD31( r0, r2, T[1], T[0], &aX[0], &aX[2] ); T-=step; + aX-=4; + }while(aX>=in); + + aX = in+n2-4; + bX = in; + T = sincos_lookup0; + do{ + REG_TYPE ri0= aX[0]; + REG_TYPE ri2= aX[2]; + REG_TYPE ro0= bX[0]; + REG_TYPE ro2= bX[2]; + + XNPROD31( ro2, ro0, T[1], T[0], &aX[0], &aX[2] ); T+=step; + XNPROD31( ri2, ri0, T[0], T[1], &bX[0], &bX[2] ); + + aX-=4; + bX+=4; + }while(aX>=in+n4); + +} + +/* 8 point butterfly (in place) */ +STIN void mdct_butterfly_8(DATA_TYPE *x){ + + REG_TYPE r0 = x[0] + x[1]; + REG_TYPE r1 = x[0] - x[1]; + REG_TYPE r2 = x[2] + x[3]; + REG_TYPE r3 = x[2] - x[3]; + REG_TYPE r4 = x[4] + x[5]; + REG_TYPE r5 = x[4] - x[5]; + REG_TYPE r6 = x[6] + x[7]; + REG_TYPE r7 = x[6] - x[7]; + + x[0] = r5 + r3; + x[1] = r7 - r1; + x[2] = r5 - r3; + x[3] = r7 + r1; + x[4] = r4 - r0; + x[5] = r6 - r2; + x[6] = r4 + r0; + x[7] = r6 + r2; + MB(); +} + +/* 16 point butterfly (in place, 4 register) */ +STIN void mdct_butterfly_16(DATA_TYPE *x){ + + REG_TYPE r0, r1, r2, r3; + + r0 = x[ 8] - x[ 9]; x[ 8] += x[ 9]; + r1 = x[10] - x[11]; x[10] += x[11]; + r2 = x[ 1] - x[ 0]; x[ 9] = x[ 1] + x[0]; + r3 = x[ 3] - x[ 2]; x[11] = x[ 3] + x[2]; + x[ 0] = MULT31((r0 - r1) , cPI2_8); + x[ 1] = MULT31((r2 + r3) , cPI2_8); + x[ 2] = MULT31((r0 + r1) , cPI2_8); + x[ 3] = MULT31((r3 - r2) , cPI2_8); + MB(); + + r2 = x[12] - x[13]; x[12] += x[13]; + r3 = x[14] - x[15]; x[14] += x[15]; + r0 = x[ 4] - x[ 5]; x[13] = x[ 5] + x[ 4]; + r1 = x[ 7] - x[ 6]; x[15] = x[ 7] + x[ 6]; + x[ 4] = r2; x[ 5] = r1; + x[ 6] = r3; x[ 7] = r0; + MB(); + + mdct_butterfly_8(x); + mdct_butterfly_8(x+8); +} + +/* 32 point butterfly (in place, 4 register) */ +STIN void mdct_butterfly_32(DATA_TYPE *x){ + + REG_TYPE r0, r1, r2, r3; + + r0 = x[16] - x[17]; x[16] += x[17]; + r1 = x[18] - x[19]; x[18] += x[19]; + r2 = x[ 1] - x[ 0]; x[17] = x[ 1] + x[ 0]; + r3 = x[ 3] - x[ 2]; x[19] = x[ 3] + x[ 2]; + XNPROD31( r0, r1, cPI3_8, cPI1_8, &x[ 0], &x[ 2] ); + XPROD31 ( r2, r3, cPI1_8, cPI3_8, &x[ 1], &x[ 3] ); + MB(); + + r0 = x[20] - x[21]; x[20] += x[21]; + r1 = x[22] - x[23]; x[22] += x[23]; + r2 = x[ 5] - x[ 4]; x[21] = x[ 5] + x[ 4]; + r3 = x[ 7] - x[ 6]; x[23] = x[ 7] + x[ 6]; + x[ 4] = MULT31((r0 - r1) , cPI2_8); + x[ 5] = MULT31((r3 + r2) , cPI2_8); + x[ 6] = MULT31((r0 + r1) , cPI2_8); + x[ 7] = MULT31((r3 - r2) , cPI2_8); + MB(); + + r0 = x[24] - x[25]; x[24] += x[25]; + r1 = x[26] - x[27]; x[26] += x[27]; + r2 = x[ 9] - x[ 8]; x[25] = x[ 9] + x[ 8]; + r3 = x[11] - x[10]; x[27] = x[11] + x[10]; + XNPROD31( r0, r1, cPI1_8, cPI3_8, &x[ 8], &x[10] ); + XPROD31 ( r2, r3, cPI3_8, cPI1_8, &x[ 9], &x[11] ); + MB(); + + r0 = x[28] - x[29]; x[28] += x[29]; + r1 = x[30] - x[31]; x[30] += x[31]; + r2 = x[12] - x[13]; x[29] = x[13] + x[12]; + r3 = x[15] - x[14]; x[31] = x[15] + x[14]; + x[12] = r0; x[13] = r3; + x[14] = r1; x[15] = r2; + MB(); + + mdct_butterfly_16(x); + mdct_butterfly_16(x+16); +} + +/* N/stage point generic N stage butterfly (in place, 2 register) */ +STIN void mdct_butterfly_generic(DATA_TYPE *x,int points,int step){ + + LOOKUP_T *T = sincos_lookup0; + DATA_TYPE *x1 = x + points - 4; + DATA_TYPE *x2 = x + (points>>1) - 4; + REG_TYPE r0, r1, r2, r3; + + do{ + r0 = x1[0] - x1[1]; x1[0] += x1[1]; + r1 = x1[3] - x1[2]; x1[2] += x1[3]; + r2 = x2[1] - x2[0]; x1[1] = x2[1] + x2[0]; + r3 = x2[3] - x2[2]; x1[3] = x2[3] + x2[2]; + XPROD31( r1, r0, T[0], T[1], &x2[0], &x2[2] ); + XPROD31( r2, r3, T[0], T[1], &x2[1], &x2[3] ); T+=step; + x1-=4; + x2-=4; + }while(Tsincos_lookup0); +} + +STIN void mdct_butterflies(DATA_TYPE *x,int points,int shift){ + + int stages=8-shift; + int i,j; + + for(i=0;--stages>0;i++){ + for(j=0;j<(1<>i)*j,points>>i,4<<(i+shift)); + } + + for(j=0;j>8]|(bitrev[(x&0x0f0)>>4]<<4)|(((int)bitrev[x&0x00f])<<8); +} + +STIN void mdct_bitreverse(DATA_TYPE *x,int n,int shift){ + int bit = 0; + DATA_TYPE *w = x+(n>>1); + + do{ + DATA_TYPE b = bitrev12(bit++); + DATA_TYPE *xx = x + (b>>shift); + REG_TYPE r; + + w -= 2; + + if(w>xx){ + + r = xx[0]; + xx[0] = w[0]; + w[0] = r; + + r = xx[1]; + xx[1] = w[1]; + w[1] = r; + } + }while(w>x); +} + +STIN void mdct_step7(DATA_TYPE *x,int n,int step){ + DATA_TYPE *w0 = x; + DATA_TYPE *w1 = x+(n>>1); + LOOKUP_T *T = (step>=4)?(sincos_lookup0+(step>>1)):sincos_lookup1; + LOOKUP_T *Ttop = T+1024; + REG_TYPE r0, r1, r2, r3; + + do{ + w1 -= 2; + + r0 = w0[0] + w1[0]; + r1 = w1[1] - w0[1]; + r2 = MULT32(r0, T[1]) + MULT32(r1, T[0]); + r3 = MULT32(r1, T[1]) - MULT32(r0, T[0]); + T+=step; + + r0 = (w0[1] + w1[1])>>1; + r1 = (w0[0] - w1[0])>>1; + w0[0] = r0 + r2; + w0[1] = r1 + r3; + w1[0] = r0 - r2; + w1[1] = r3 - r1; + + w0 += 2; + }while(T>1; + r1 = (w0[0] - w1[0])>>1; + w0[0] = r0 + r2; + w0[1] = r1 + r3; + w1[0] = r0 - r2; + w1[1] = r3 - r1; + + w0 += 2; + }while(w0>1); + step>>=2; + + switch(step) { + default: + T=(step>=4)?(sincos_lookup0+(step>>1)):sincos_lookup1; + do{ + REG_TYPE r0 = x[0]; + REG_TYPE r1 = -x[1]; + XPROD31( r0, r1, T[0], T[1], x, x+1); T+=step; + x +=2; + }while(x>1; + t1 = (*T++)>>1; + do{ + r0 = x[0]; + r1 = -x[1]; + t0 += (v0 = (*V++)>>1); + t1 += (v1 = (*V++)>>1); + XPROD31( r0, r1, t0, t1, x, x+1 ); + + r0 = x[2]; + r1 = -x[3]; + v0 += (t0 = (*T++)>>1); + v1 += (t1 = (*T++)>>1); + XPROD31( r0, r1, v0, v1, x+2, x+3 ); + + x += 4; + }while(x>2); + t1 += (q1 = (v1-t1)>>2); + r0 = x[0]; + r1 = -x[1]; + XPROD31( r0, r1, t0, t1, x, x+1 ); + t0 = v0-q0; + t1 = v1-q1; + r0 = x[2]; + r1 = -x[3]; + XPROD31( r0, r1, t0, t1, x+2, x+3 ); + + t0 = *T++; + t1 = *T++; + v0 += (q0 = (t0-v0)>>2); + v1 += (q1 = (t1-v1)>>2); + r0 = x[4]; + r1 = -x[5]; + XPROD31( r0, r1, v0, v1, x+4, x+5 ); + v0 = t0-q0; + v1 = t1-q1; + r0 = x[6]; + r1 = -x[7]; + XPROD31( r0, r1, v0, v1, x+5, x+6 ); + + x+=8; + }while(x>1,step); + mdct_butterflies(in,n>>1,shift); + mdct_bitreverse(in,n,shift); + mdct_step7(in,n,step); + mdct_step8(in,n,step); +} + +void mdct_shift_right(int n, DATA_TYPE *in, DATA_TYPE *right){ + int i; + n>>=2; + in+=1; + + for(i=0;i>1 : n0>>1); + DATA_TYPE *r=right+(lW ? n1>>2 : n0>>2); + DATA_TYPE *post; + LOOKUP_T *wR=(W && lW ? w1+(n1>>1) : w0+(n0>>1)); + LOOKUP_T *wL=(W && lW ? w1 : w0 ); + + int preLap=(lW && !W ? (n1>>2)-(n0>>2) : 0 ); + int halfLap=(lW && W ? (n1>>2) : (n0>>2) ); + int postLap=(!lW && W ? (n1>>2)-(n0>>2) : 0 ); + int n,off; + + /* preceeding direct-copy lapping from previous frame, if any */ + if(preLap){ + n = (endpost){ + *out = CLIP_TO_15((*--r)>>9); + out+=step; + } + } + + /* cross-lap; two halves due to wrap-around */ + n = (endpost){ + l-=2; + *out = CLIP_TO_15((MULT31(*--r,*--wR) + MULT31(*l,*wL++))>>9); + out+=step; + } + + n = (end>9); + out+=step; + l+=2; + } + + /* preceeding direct-copy lapping from previous frame, if any */ + if(postLap){ + n = (end>9); + out+=step; + l+=2; + } + } +} + diff --git a/components/spotify/cspot/bell/tremor/mdct.h b/components/spotify/cspot/bell/tremor/mdct.h new file mode 100644 index 00000000..5e865d06 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/mdct.h @@ -0,0 +1,59 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: modified discrete cosine transform prototypes + + ********************************************************************/ + +#ifndef _OGG_mdct_H_ +#define _OGG_mdct_H_ + +#include "ivorbiscodec.h" +#include "misc.h" + +#define DATA_TYPE ogg_int32_t +#define REG_TYPE register ogg_int32_t + +#ifdef _LOW_ACCURACY_ +#define cPI3_8 (0x0062) +#define cPI2_8 (0x00b5) +#define cPI1_8 (0x00ed) +#else +#define cPI3_8 (0x30fbc54d) +#define cPI2_8 (0x5a82799a) +#define cPI1_8 (0x7641af3d) +#endif + +extern void mdct_backward(int n, DATA_TYPE *in); +extern void mdct_shift_right(int n, DATA_TYPE *in, DATA_TYPE *right); +extern void mdct_unroll_lap(int n0,int n1, + int lW,int W, + DATA_TYPE *in,DATA_TYPE *right, + LOOKUP_T *w0,LOOKUP_T *w1, + ogg_int16_t *out, + int step, + int start,int end /* samples, this frame */); + +#endif + + + + + + + + + + + + diff --git a/components/spotify/cspot/bell/tremor/mdct_lookup.h b/components/spotify/cspot/bell/tremor/mdct_lookup.h new file mode 100644 index 00000000..970e199f --- /dev/null +++ b/components/spotify/cspot/bell/tremor/mdct_lookup.h @@ -0,0 +1,540 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: sin,cos lookup tables + + ********************************************************************/ + +#include "os_types.h" + +/* {sin(2*i*PI/4096), cos(2*i*PI/4096)}, with i = 0 to 512 */ +static LOOKUP_T sincos_lookup0[1026] = { + X(0x00000000), X(0x7fffffff), X(0x003243f5), X(0x7ffff621), + X(0x006487e3), X(0x7fffd886), X(0x0096cbc1), X(0x7fffa72c), + X(0x00c90f88), X(0x7fff6216), X(0x00fb5330), X(0x7fff0943), + X(0x012d96b1), X(0x7ffe9cb2), X(0x015fda03), X(0x7ffe1c65), + X(0x01921d20), X(0x7ffd885a), X(0x01c45ffe), X(0x7ffce093), + X(0x01f6a297), X(0x7ffc250f), X(0x0228e4e2), X(0x7ffb55ce), + X(0x025b26d7), X(0x7ffa72d1), X(0x028d6870), X(0x7ff97c18), + X(0x02bfa9a4), X(0x7ff871a2), X(0x02f1ea6c), X(0x7ff75370), + X(0x03242abf), X(0x7ff62182), X(0x03566a96), X(0x7ff4dbd9), + X(0x0388a9ea), X(0x7ff38274), X(0x03bae8b2), X(0x7ff21553), + X(0x03ed26e6), X(0x7ff09478), X(0x041f6480), X(0x7feeffe1), + X(0x0451a177), X(0x7fed5791), X(0x0483ddc3), X(0x7feb9b85), + X(0x04b6195d), X(0x7fe9cbc0), X(0x04e8543e), X(0x7fe7e841), + X(0x051a8e5c), X(0x7fe5f108), X(0x054cc7b1), X(0x7fe3e616), + X(0x057f0035), X(0x7fe1c76b), X(0x05b137df), X(0x7fdf9508), + X(0x05e36ea9), X(0x7fdd4eec), X(0x0615a48b), X(0x7fdaf519), + X(0x0647d97c), X(0x7fd8878e), X(0x067a0d76), X(0x7fd6064c), + X(0x06ac406f), X(0x7fd37153), X(0x06de7262), X(0x7fd0c8a3), + X(0x0710a345), X(0x7fce0c3e), X(0x0742d311), X(0x7fcb3c23), + X(0x077501be), X(0x7fc85854), X(0x07a72f45), X(0x7fc560cf), + X(0x07d95b9e), X(0x7fc25596), X(0x080b86c2), X(0x7fbf36aa), + X(0x083db0a7), X(0x7fbc040a), X(0x086fd947), X(0x7fb8bdb8), + X(0x08a2009a), X(0x7fb563b3), X(0x08d42699), X(0x7fb1f5fc), + X(0x09064b3a), X(0x7fae7495), X(0x09386e78), X(0x7faadf7c), + X(0x096a9049), X(0x7fa736b4), X(0x099cb0a7), X(0x7fa37a3c), + X(0x09cecf89), X(0x7f9faa15), X(0x0a00ece8), X(0x7f9bc640), + X(0x0a3308bd), X(0x7f97cebd), X(0x0a6522fe), X(0x7f93c38c), + X(0x0a973ba5), X(0x7f8fa4b0), X(0x0ac952aa), X(0x7f8b7227), + X(0x0afb6805), X(0x7f872bf3), X(0x0b2d7baf), X(0x7f82d214), + X(0x0b5f8d9f), X(0x7f7e648c), X(0x0b919dcf), X(0x7f79e35a), + X(0x0bc3ac35), X(0x7f754e80), X(0x0bf5b8cb), X(0x7f70a5fe), + X(0x0c27c389), X(0x7f6be9d4), X(0x0c59cc68), X(0x7f671a05), + X(0x0c8bd35e), X(0x7f62368f), X(0x0cbdd865), X(0x7f5d3f75), + X(0x0cefdb76), X(0x7f5834b7), X(0x0d21dc87), X(0x7f531655), + X(0x0d53db92), X(0x7f4de451), X(0x0d85d88f), X(0x7f489eaa), + X(0x0db7d376), X(0x7f434563), X(0x0de9cc40), X(0x7f3dd87c), + X(0x0e1bc2e4), X(0x7f3857f6), X(0x0e4db75b), X(0x7f32c3d1), + X(0x0e7fa99e), X(0x7f2d1c0e), X(0x0eb199a4), X(0x7f2760af), + X(0x0ee38766), X(0x7f2191b4), X(0x0f1572dc), X(0x7f1baf1e), + X(0x0f475bff), X(0x7f15b8ee), X(0x0f7942c7), X(0x7f0faf25), + X(0x0fab272b), X(0x7f0991c4), X(0x0fdd0926), X(0x7f0360cb), + X(0x100ee8ad), X(0x7efd1c3c), X(0x1040c5bb), X(0x7ef6c418), + X(0x1072a048), X(0x7ef05860), X(0x10a4784b), X(0x7ee9d914), + X(0x10d64dbd), X(0x7ee34636), X(0x11082096), X(0x7edc9fc6), + X(0x1139f0cf), X(0x7ed5e5c6), X(0x116bbe60), X(0x7ecf1837), + X(0x119d8941), X(0x7ec8371a), X(0x11cf516a), X(0x7ec14270), + X(0x120116d5), X(0x7eba3a39), X(0x1232d979), X(0x7eb31e78), + X(0x1264994e), X(0x7eabef2c), X(0x1296564d), X(0x7ea4ac58), + X(0x12c8106f), X(0x7e9d55fc), X(0x12f9c7aa), X(0x7e95ec1a), + X(0x132b7bf9), X(0x7e8e6eb2), X(0x135d2d53), X(0x7e86ddc6), + X(0x138edbb1), X(0x7e7f3957), X(0x13c0870a), X(0x7e778166), + X(0x13f22f58), X(0x7e6fb5f4), X(0x1423d492), X(0x7e67d703), + X(0x145576b1), X(0x7e5fe493), X(0x148715ae), X(0x7e57dea7), + X(0x14b8b17f), X(0x7e4fc53e), X(0x14ea4a1f), X(0x7e47985b), + X(0x151bdf86), X(0x7e3f57ff), X(0x154d71aa), X(0x7e37042a), + X(0x157f0086), X(0x7e2e9cdf), X(0x15b08c12), X(0x7e26221f), + X(0x15e21445), X(0x7e1d93ea), X(0x16139918), X(0x7e14f242), + X(0x16451a83), X(0x7e0c3d29), X(0x1676987f), X(0x7e0374a0), + X(0x16a81305), X(0x7dfa98a8), X(0x16d98a0c), X(0x7df1a942), + X(0x170afd8d), X(0x7de8a670), X(0x173c6d80), X(0x7ddf9034), + X(0x176dd9de), X(0x7dd6668f), X(0x179f429f), X(0x7dcd2981), + X(0x17d0a7bc), X(0x7dc3d90d), X(0x1802092c), X(0x7dba7534), + X(0x183366e9), X(0x7db0fdf8), X(0x1864c0ea), X(0x7da77359), + X(0x18961728), X(0x7d9dd55a), X(0x18c7699b), X(0x7d9423fc), + X(0x18f8b83c), X(0x7d8a5f40), X(0x192a0304), X(0x7d808728), + X(0x195b49ea), X(0x7d769bb5), X(0x198c8ce7), X(0x7d6c9ce9), + X(0x19bdcbf3), X(0x7d628ac6), X(0x19ef0707), X(0x7d58654d), + X(0x1a203e1b), X(0x7d4e2c7f), X(0x1a517128), X(0x7d43e05e), + X(0x1a82a026), X(0x7d3980ec), X(0x1ab3cb0d), X(0x7d2f0e2b), + X(0x1ae4f1d6), X(0x7d24881b), X(0x1b161479), X(0x7d19eebf), + X(0x1b4732ef), X(0x7d0f4218), X(0x1b784d30), X(0x7d048228), + X(0x1ba96335), X(0x7cf9aef0), X(0x1bda74f6), X(0x7ceec873), + X(0x1c0b826a), X(0x7ce3ceb2), X(0x1c3c8b8c), X(0x7cd8c1ae), + X(0x1c6d9053), X(0x7ccda169), X(0x1c9e90b8), X(0x7cc26de5), + X(0x1ccf8cb3), X(0x7cb72724), X(0x1d00843d), X(0x7cabcd28), + X(0x1d31774d), X(0x7ca05ff1), X(0x1d6265dd), X(0x7c94df83), + X(0x1d934fe5), X(0x7c894bde), X(0x1dc4355e), X(0x7c7da505), + X(0x1df5163f), X(0x7c71eaf9), X(0x1e25f282), X(0x7c661dbc), + X(0x1e56ca1e), X(0x7c5a3d50), X(0x1e879d0d), X(0x7c4e49b7), + X(0x1eb86b46), X(0x7c4242f2), X(0x1ee934c3), X(0x7c362904), + X(0x1f19f97b), X(0x7c29fbee), X(0x1f4ab968), X(0x7c1dbbb3), + X(0x1f7b7481), X(0x7c116853), X(0x1fac2abf), X(0x7c0501d2), + X(0x1fdcdc1b), X(0x7bf88830), X(0x200d888d), X(0x7bebfb70), + X(0x203e300d), X(0x7bdf5b94), X(0x206ed295), X(0x7bd2a89e), + X(0x209f701c), X(0x7bc5e290), X(0x20d0089c), X(0x7bb9096b), + X(0x21009c0c), X(0x7bac1d31), X(0x21312a65), X(0x7b9f1de6), + X(0x2161b3a0), X(0x7b920b89), X(0x219237b5), X(0x7b84e61f), + X(0x21c2b69c), X(0x7b77ada8), X(0x21f3304f), X(0x7b6a6227), + X(0x2223a4c5), X(0x7b5d039e), X(0x225413f8), X(0x7b4f920e), + X(0x22847de0), X(0x7b420d7a), X(0x22b4e274), X(0x7b3475e5), + X(0x22e541af), X(0x7b26cb4f), X(0x23159b88), X(0x7b190dbc), + X(0x2345eff8), X(0x7b0b3d2c), X(0x23763ef7), X(0x7afd59a4), + X(0x23a6887f), X(0x7aef6323), X(0x23d6cc87), X(0x7ae159ae), + X(0x24070b08), X(0x7ad33d45), X(0x243743fa), X(0x7ac50dec), + X(0x24677758), X(0x7ab6cba4), X(0x2497a517), X(0x7aa8766f), + X(0x24c7cd33), X(0x7a9a0e50), X(0x24f7efa2), X(0x7a8b9348), + X(0x25280c5e), X(0x7a7d055b), X(0x2558235f), X(0x7a6e648a), + X(0x2588349d), X(0x7a5fb0d8), X(0x25b84012), X(0x7a50ea47), + X(0x25e845b6), X(0x7a4210d8), X(0x26184581), X(0x7a332490), + X(0x26483f6c), X(0x7a24256f), X(0x26783370), X(0x7a151378), + X(0x26a82186), X(0x7a05eead), X(0x26d809a5), X(0x79f6b711), + X(0x2707ebc7), X(0x79e76ca7), X(0x2737c7e3), X(0x79d80f6f), + X(0x27679df4), X(0x79c89f6e), X(0x27976df1), X(0x79b91ca4), + X(0x27c737d3), X(0x79a98715), X(0x27f6fb92), X(0x7999dec4), + X(0x2826b928), X(0x798a23b1), X(0x2856708d), X(0x797a55e0), + X(0x288621b9), X(0x796a7554), X(0x28b5cca5), X(0x795a820e), + X(0x28e5714b), X(0x794a7c12), X(0x29150fa1), X(0x793a6361), + X(0x2944a7a2), X(0x792a37fe), X(0x29743946), X(0x7919f9ec), + X(0x29a3c485), X(0x7909a92d), X(0x29d34958), X(0x78f945c3), + X(0x2a02c7b8), X(0x78e8cfb2), X(0x2a323f9e), X(0x78d846fb), + X(0x2a61b101), X(0x78c7aba2), X(0x2a911bdc), X(0x78b6fda8), + X(0x2ac08026), X(0x78a63d11), X(0x2aefddd8), X(0x789569df), + X(0x2b1f34eb), X(0x78848414), X(0x2b4e8558), X(0x78738bb3), + X(0x2b7dcf17), X(0x786280bf), X(0x2bad1221), X(0x7851633b), + X(0x2bdc4e6f), X(0x78403329), X(0x2c0b83fa), X(0x782ef08b), + X(0x2c3ab2b9), X(0x781d9b65), X(0x2c69daa6), X(0x780c33b8), + X(0x2c98fbba), X(0x77fab989), X(0x2cc815ee), X(0x77e92cd9), + X(0x2cf72939), X(0x77d78daa), X(0x2d263596), X(0x77c5dc01), + X(0x2d553afc), X(0x77b417df), X(0x2d843964), X(0x77a24148), + X(0x2db330c7), X(0x7790583e), X(0x2de2211e), X(0x777e5cc3), + X(0x2e110a62), X(0x776c4edb), X(0x2e3fec8b), X(0x775a2e89), + X(0x2e6ec792), X(0x7747fbce), X(0x2e9d9b70), X(0x7735b6af), + X(0x2ecc681e), X(0x77235f2d), X(0x2efb2d95), X(0x7710f54c), + X(0x2f29ebcc), X(0x76fe790e), X(0x2f58a2be), X(0x76ebea77), + X(0x2f875262), X(0x76d94989), X(0x2fb5fab2), X(0x76c69647), + X(0x2fe49ba7), X(0x76b3d0b4), X(0x30133539), X(0x76a0f8d2), + X(0x3041c761), X(0x768e0ea6), X(0x30705217), X(0x767b1231), + X(0x309ed556), X(0x76680376), X(0x30cd5115), X(0x7654e279), + X(0x30fbc54d), X(0x7641af3d), X(0x312a31f8), X(0x762e69c4), + X(0x3158970e), X(0x761b1211), X(0x3186f487), X(0x7607a828), + X(0x31b54a5e), X(0x75f42c0b), X(0x31e39889), X(0x75e09dbd), + X(0x3211df04), X(0x75ccfd42), X(0x32401dc6), X(0x75b94a9c), + X(0x326e54c7), X(0x75a585cf), X(0x329c8402), X(0x7591aedd), + X(0x32caab6f), X(0x757dc5ca), X(0x32f8cb07), X(0x7569ca99), + X(0x3326e2c3), X(0x7555bd4c), X(0x3354f29b), X(0x75419de7), + X(0x3382fa88), X(0x752d6c6c), X(0x33b0fa84), X(0x751928e0), + X(0x33def287), X(0x7504d345), X(0x340ce28b), X(0x74f06b9e), + X(0x343aca87), X(0x74dbf1ef), X(0x3468aa76), X(0x74c7663a), + X(0x34968250), X(0x74b2c884), X(0x34c4520d), X(0x749e18cd), + X(0x34f219a8), X(0x7489571c), X(0x351fd918), X(0x74748371), + X(0x354d9057), X(0x745f9dd1), X(0x357b3f5d), X(0x744aa63f), + X(0x35a8e625), X(0x74359cbd), X(0x35d684a6), X(0x74208150), + X(0x36041ad9), X(0x740b53fb), X(0x3631a8b8), X(0x73f614c0), + X(0x365f2e3b), X(0x73e0c3a3), X(0x368cab5c), X(0x73cb60a8), + X(0x36ba2014), X(0x73b5ebd1), X(0x36e78c5b), X(0x73a06522), + X(0x3714f02a), X(0x738acc9e), X(0x37424b7b), X(0x73752249), + X(0x376f9e46), X(0x735f6626), X(0x379ce885), X(0x73499838), + X(0x37ca2a30), X(0x7333b883), X(0x37f76341), X(0x731dc70a), + X(0x382493b0), X(0x7307c3d0), X(0x3851bb77), X(0x72f1aed9), + X(0x387eda8e), X(0x72db8828), X(0x38abf0ef), X(0x72c54fc1), + X(0x38d8fe93), X(0x72af05a7), X(0x39060373), X(0x7298a9dd), + X(0x3932ff87), X(0x72823c67), X(0x395ff2c9), X(0x726bbd48), + X(0x398cdd32), X(0x72552c85), X(0x39b9bebc), X(0x723e8a20), + X(0x39e6975e), X(0x7227d61c), X(0x3a136712), X(0x7211107e), + X(0x3a402dd2), X(0x71fa3949), X(0x3a6ceb96), X(0x71e35080), + X(0x3a99a057), X(0x71cc5626), X(0x3ac64c0f), X(0x71b54a41), + X(0x3af2eeb7), X(0x719e2cd2), X(0x3b1f8848), X(0x7186fdde), + X(0x3b4c18ba), X(0x716fbd68), X(0x3b78a007), X(0x71586b74), + X(0x3ba51e29), X(0x71410805), X(0x3bd19318), X(0x7129931f), + X(0x3bfdfecd), X(0x71120cc5), X(0x3c2a6142), X(0x70fa74fc), + X(0x3c56ba70), X(0x70e2cbc6), X(0x3c830a50), X(0x70cb1128), + X(0x3caf50da), X(0x70b34525), X(0x3cdb8e09), X(0x709b67c0), + X(0x3d07c1d6), X(0x708378ff), X(0x3d33ec39), X(0x706b78e3), + X(0x3d600d2c), X(0x70536771), X(0x3d8c24a8), X(0x703b44ad), + X(0x3db832a6), X(0x7023109a), X(0x3de4371f), X(0x700acb3c), + X(0x3e10320d), X(0x6ff27497), X(0x3e3c2369), X(0x6fda0cae), + X(0x3e680b2c), X(0x6fc19385), X(0x3e93e950), X(0x6fa90921), + X(0x3ebfbdcd), X(0x6f906d84), X(0x3eeb889c), X(0x6f77c0b3), + X(0x3f1749b8), X(0x6f5f02b2), X(0x3f430119), X(0x6f463383), + X(0x3f6eaeb8), X(0x6f2d532c), X(0x3f9a5290), X(0x6f1461b0), + X(0x3fc5ec98), X(0x6efb5f12), X(0x3ff17cca), X(0x6ee24b57), + X(0x401d0321), X(0x6ec92683), X(0x40487f94), X(0x6eaff099), + X(0x4073f21d), X(0x6e96a99d), X(0x409f5ab6), X(0x6e7d5193), + X(0x40cab958), X(0x6e63e87f), X(0x40f60dfb), X(0x6e4a6e66), + X(0x4121589b), X(0x6e30e34a), X(0x414c992f), X(0x6e174730), + X(0x4177cfb1), X(0x6dfd9a1c), X(0x41a2fc1a), X(0x6de3dc11), + X(0x41ce1e65), X(0x6dca0d14), X(0x41f93689), X(0x6db02d29), + X(0x42244481), X(0x6d963c54), X(0x424f4845), X(0x6d7c3a98), + X(0x427a41d0), X(0x6d6227fa), X(0x42a5311b), X(0x6d48047e), + X(0x42d0161e), X(0x6d2dd027), X(0x42faf0d4), X(0x6d138afb), + X(0x4325c135), X(0x6cf934fc), X(0x4350873c), X(0x6cdece2f), + X(0x437b42e1), X(0x6cc45698), X(0x43a5f41e), X(0x6ca9ce3b), + X(0x43d09aed), X(0x6c8f351c), X(0x43fb3746), X(0x6c748b3f), + X(0x4425c923), X(0x6c59d0a9), X(0x4450507e), X(0x6c3f055d), + X(0x447acd50), X(0x6c242960), X(0x44a53f93), X(0x6c093cb6), + X(0x44cfa740), X(0x6bee3f62), X(0x44fa0450), X(0x6bd3316a), + X(0x452456bd), X(0x6bb812d1), X(0x454e9e80), X(0x6b9ce39b), + X(0x4578db93), X(0x6b81a3cd), X(0x45a30df0), X(0x6b66536b), + X(0x45cd358f), X(0x6b4af279), X(0x45f7526b), X(0x6b2f80fb), + X(0x4621647d), X(0x6b13fef5), X(0x464b6bbe), X(0x6af86c6c), + X(0x46756828), X(0x6adcc964), X(0x469f59b4), X(0x6ac115e2), + X(0x46c9405c), X(0x6aa551e9), X(0x46f31c1a), X(0x6a897d7d), + X(0x471cece7), X(0x6a6d98a4), X(0x4746b2bc), X(0x6a51a361), + X(0x47706d93), X(0x6a359db9), X(0x479a1d67), X(0x6a1987b0), + X(0x47c3c22f), X(0x69fd614a), X(0x47ed5be6), X(0x69e12a8c), + X(0x4816ea86), X(0x69c4e37a), X(0x48406e08), X(0x69a88c19), + X(0x4869e665), X(0x698c246c), X(0x48935397), X(0x696fac78), + X(0x48bcb599), X(0x69532442), X(0x48e60c62), X(0x69368bce), + X(0x490f57ee), X(0x6919e320), X(0x49389836), X(0x68fd2a3d), + X(0x4961cd33), X(0x68e06129), X(0x498af6df), X(0x68c387e9), + X(0x49b41533), X(0x68a69e81), X(0x49dd282a), X(0x6889a4f6), + X(0x4a062fbd), X(0x686c9b4b), X(0x4a2f2be6), X(0x684f8186), + X(0x4a581c9e), X(0x683257ab), X(0x4a8101de), X(0x68151dbe), + X(0x4aa9dba2), X(0x67f7d3c5), X(0x4ad2a9e2), X(0x67da79c3), + X(0x4afb6c98), X(0x67bd0fbd), X(0x4b2423be), X(0x679f95b7), + X(0x4b4ccf4d), X(0x67820bb7), X(0x4b756f40), X(0x676471c0), + X(0x4b9e0390), X(0x6746c7d8), X(0x4bc68c36), X(0x67290e02), + X(0x4bef092d), X(0x670b4444), X(0x4c177a6e), X(0x66ed6aa1), + X(0x4c3fdff4), X(0x66cf8120), X(0x4c6839b7), X(0x66b187c3), + X(0x4c9087b1), X(0x66937e91), X(0x4cb8c9dd), X(0x6675658c), + X(0x4ce10034), X(0x66573cbb), X(0x4d092ab0), X(0x66390422), + X(0x4d31494b), X(0x661abbc5), X(0x4d595bfe), X(0x65fc63a9), + X(0x4d8162c4), X(0x65ddfbd3), X(0x4da95d96), X(0x65bf8447), + X(0x4dd14c6e), X(0x65a0fd0b), X(0x4df92f46), X(0x65826622), + X(0x4e210617), X(0x6563bf92), X(0x4e48d0dd), X(0x6545095f), + X(0x4e708f8f), X(0x6526438f), X(0x4e984229), X(0x65076e25), + X(0x4ebfe8a5), X(0x64e88926), X(0x4ee782fb), X(0x64c99498), + X(0x4f0f1126), X(0x64aa907f), X(0x4f369320), X(0x648b7ce0), + X(0x4f5e08e3), X(0x646c59bf), X(0x4f857269), X(0x644d2722), + X(0x4faccfab), X(0x642de50d), X(0x4fd420a4), X(0x640e9386), + X(0x4ffb654d), X(0x63ef3290), X(0x50229da1), X(0x63cfc231), + X(0x5049c999), X(0x63b0426d), X(0x5070e92f), X(0x6390b34a), + X(0x5097fc5e), X(0x637114cc), X(0x50bf031f), X(0x635166f9), + X(0x50e5fd6d), X(0x6331a9d4), X(0x510ceb40), X(0x6311dd64), + X(0x5133cc94), X(0x62f201ac), X(0x515aa162), X(0x62d216b3), + X(0x518169a5), X(0x62b21c7b), X(0x51a82555), X(0x6292130c), + X(0x51ced46e), X(0x6271fa69), X(0x51f576ea), X(0x6251d298), + X(0x521c0cc2), X(0x62319b9d), X(0x524295f0), X(0x6211557e), + X(0x5269126e), X(0x61f1003f), X(0x528f8238), X(0x61d09be5), + X(0x52b5e546), X(0x61b02876), X(0x52dc3b92), X(0x618fa5f7), + X(0x53028518), X(0x616f146c), X(0x5328c1d0), X(0x614e73da), + X(0x534ef1b5), X(0x612dc447), X(0x537514c2), X(0x610d05b7), + X(0x539b2af0), X(0x60ec3830), X(0x53c13439), X(0x60cb5bb7), + X(0x53e73097), X(0x60aa7050), X(0x540d2005), X(0x60897601), + X(0x5433027d), X(0x60686ccf), X(0x5458d7f9), X(0x604754bf), + X(0x547ea073), X(0x60262dd6), X(0x54a45be6), X(0x6004f819), + X(0x54ca0a4b), X(0x5fe3b38d), X(0x54efab9c), X(0x5fc26038), + X(0x55153fd4), X(0x5fa0fe1f), X(0x553ac6ee), X(0x5f7f8d46), + X(0x556040e2), X(0x5f5e0db3), X(0x5585adad), X(0x5f3c7f6b), + X(0x55ab0d46), X(0x5f1ae274), X(0x55d05faa), X(0x5ef936d1), + X(0x55f5a4d2), X(0x5ed77c8a), X(0x561adcb9), X(0x5eb5b3a2), + X(0x56400758), X(0x5e93dc1f), X(0x566524aa), X(0x5e71f606), + X(0x568a34a9), X(0x5e50015d), X(0x56af3750), X(0x5e2dfe29), + X(0x56d42c99), X(0x5e0bec6e), X(0x56f9147e), X(0x5de9cc33), + X(0x571deefa), X(0x5dc79d7c), X(0x5742bc06), X(0x5da5604f), + X(0x57677b9d), X(0x5d8314b1), X(0x578c2dba), X(0x5d60baa7), + X(0x57b0d256), X(0x5d3e5237), X(0x57d5696d), X(0x5d1bdb65), + X(0x57f9f2f8), X(0x5cf95638), X(0x581e6ef1), X(0x5cd6c2b5), + X(0x5842dd54), X(0x5cb420e0), X(0x58673e1b), X(0x5c9170bf), + X(0x588b9140), X(0x5c6eb258), X(0x58afd6bd), X(0x5c4be5b0), + X(0x58d40e8c), X(0x5c290acc), X(0x58f838a9), X(0x5c0621b2), + X(0x591c550e), X(0x5be32a67), X(0x594063b5), X(0x5bc024f0), + X(0x59646498), X(0x5b9d1154), X(0x598857b2), X(0x5b79ef96), + X(0x59ac3cfd), X(0x5b56bfbd), X(0x59d01475), X(0x5b3381ce), + X(0x59f3de12), X(0x5b1035cf), X(0x5a1799d1), X(0x5aecdbc5), + X(0x5a3b47ab), X(0x5ac973b5), X(0x5a5ee79a), X(0x5aa5fda5), + X(0x5a82799a), X(0x5a82799a) + }; + + /* {sin((2*i+1)*PI/4096), cos((2*i+1)*PI/4096)}, with i = 0 to 511 */ +static LOOKUP_T sincos_lookup1[1024] = { + X(0x001921fb), X(0x7ffffd88), X(0x004b65ee), X(0x7fffe9cb), + X(0x007da9d4), X(0x7fffc251), X(0x00afeda8), X(0x7fff8719), + X(0x00e23160), X(0x7fff3824), X(0x011474f6), X(0x7ffed572), + X(0x0146b860), X(0x7ffe5f03), X(0x0178fb99), X(0x7ffdd4d7), + X(0x01ab3e97), X(0x7ffd36ee), X(0x01dd8154), X(0x7ffc8549), + X(0x020fc3c6), X(0x7ffbbfe6), X(0x024205e8), X(0x7ffae6c7), + X(0x027447b0), X(0x7ff9f9ec), X(0x02a68917), X(0x7ff8f954), + X(0x02d8ca16), X(0x7ff7e500), X(0x030b0aa4), X(0x7ff6bcf0), + X(0x033d4abb), X(0x7ff58125), X(0x036f8a51), X(0x7ff4319d), + X(0x03a1c960), X(0x7ff2ce5b), X(0x03d407df), X(0x7ff1575d), + X(0x040645c7), X(0x7fefcca4), X(0x04388310), X(0x7fee2e30), + X(0x046abfb3), X(0x7fec7c02), X(0x049cfba7), X(0x7feab61a), + X(0x04cf36e5), X(0x7fe8dc78), X(0x05017165), X(0x7fe6ef1c), + X(0x0533ab20), X(0x7fe4ee06), X(0x0565e40d), X(0x7fe2d938), + X(0x05981c26), X(0x7fe0b0b1), X(0x05ca5361), X(0x7fde7471), + X(0x05fc89b8), X(0x7fdc247a), X(0x062ebf22), X(0x7fd9c0ca), + X(0x0660f398), X(0x7fd74964), X(0x06932713), X(0x7fd4be46), + X(0x06c5598a), X(0x7fd21f72), X(0x06f78af6), X(0x7fcf6ce8), + X(0x0729bb4e), X(0x7fcca6a7), X(0x075bea8c), X(0x7fc9ccb2), + X(0x078e18a7), X(0x7fc6df08), X(0x07c04598), X(0x7fc3dda9), + X(0x07f27157), X(0x7fc0c896), X(0x08249bdd), X(0x7fbd9fd0), + X(0x0856c520), X(0x7fba6357), X(0x0888ed1b), X(0x7fb7132b), + X(0x08bb13c5), X(0x7fb3af4e), X(0x08ed3916), X(0x7fb037bf), + X(0x091f5d06), X(0x7facac7f), X(0x09517f8f), X(0x7fa90d8e), + X(0x0983a0a7), X(0x7fa55aee), X(0x09b5c048), X(0x7fa1949e), + X(0x09e7de6a), X(0x7f9dbaa0), X(0x0a19fb04), X(0x7f99ccf4), + X(0x0a4c1610), X(0x7f95cb9a), X(0x0a7e2f85), X(0x7f91b694), + X(0x0ab0475c), X(0x7f8d8de1), X(0x0ae25d8d), X(0x7f895182), + X(0x0b147211), X(0x7f850179), X(0x0b4684df), X(0x7f809dc5), + X(0x0b7895f0), X(0x7f7c2668), X(0x0baaa53b), X(0x7f779b62), + X(0x0bdcb2bb), X(0x7f72fcb4), X(0x0c0ebe66), X(0x7f6e4a5e), + X(0x0c40c835), X(0x7f698461), X(0x0c72d020), X(0x7f64aabf), + X(0x0ca4d620), X(0x7f5fbd77), X(0x0cd6da2d), X(0x7f5abc8a), + X(0x0d08dc3f), X(0x7f55a7fa), X(0x0d3adc4e), X(0x7f507fc7), + X(0x0d6cda53), X(0x7f4b43f2), X(0x0d9ed646), X(0x7f45f47b), + X(0x0dd0d01f), X(0x7f409164), X(0x0e02c7d7), X(0x7f3b1aad), + X(0x0e34bd66), X(0x7f359057), X(0x0e66b0c3), X(0x7f2ff263), + X(0x0e98a1e9), X(0x7f2a40d2), X(0x0eca90ce), X(0x7f247ba5), + X(0x0efc7d6b), X(0x7f1ea2dc), X(0x0f2e67b8), X(0x7f18b679), + X(0x0f604faf), X(0x7f12b67c), X(0x0f923546), X(0x7f0ca2e7), + X(0x0fc41876), X(0x7f067bba), X(0x0ff5f938), X(0x7f0040f6), + X(0x1027d784), X(0x7ef9f29d), X(0x1059b352), X(0x7ef390ae), + X(0x108b8c9b), X(0x7eed1b2c), X(0x10bd6356), X(0x7ee69217), + X(0x10ef377d), X(0x7edff570), X(0x11210907), X(0x7ed94538), + X(0x1152d7ed), X(0x7ed28171), X(0x1184a427), X(0x7ecbaa1a), + X(0x11b66dad), X(0x7ec4bf36), X(0x11e83478), X(0x7ebdc0c6), + X(0x1219f880), X(0x7eb6aeca), X(0x124bb9be), X(0x7eaf8943), + X(0x127d7829), X(0x7ea85033), X(0x12af33ba), X(0x7ea1039b), + X(0x12e0ec6a), X(0x7e99a37c), X(0x1312a230), X(0x7e922fd6), + X(0x13445505), X(0x7e8aa8ac), X(0x137604e2), X(0x7e830dff), + X(0x13a7b1bf), X(0x7e7b5fce), X(0x13d95b93), X(0x7e739e1d), + X(0x140b0258), X(0x7e6bc8eb), X(0x143ca605), X(0x7e63e03b), + X(0x146e4694), X(0x7e5be40c), X(0x149fe3fc), X(0x7e53d462), + X(0x14d17e36), X(0x7e4bb13c), X(0x1503153a), X(0x7e437a9c), + X(0x1534a901), X(0x7e3b3083), X(0x15663982), X(0x7e32d2f4), + X(0x1597c6b7), X(0x7e2a61ed), X(0x15c95097), X(0x7e21dd73), + X(0x15fad71b), X(0x7e194584), X(0x162c5a3b), X(0x7e109a24), + X(0x165dd9f0), X(0x7e07db52), X(0x168f5632), X(0x7dff0911), + X(0x16c0cef9), X(0x7df62362), X(0x16f2443e), X(0x7ded2a47), + X(0x1723b5f9), X(0x7de41dc0), X(0x17552422), X(0x7ddafdce), + X(0x17868eb3), X(0x7dd1ca75), X(0x17b7f5a3), X(0x7dc883b4), + X(0x17e958ea), X(0x7dbf298d), X(0x181ab881), X(0x7db5bc02), + X(0x184c1461), X(0x7dac3b15), X(0x187d6c82), X(0x7da2a6c6), + X(0x18aec0db), X(0x7d98ff17), X(0x18e01167), X(0x7d8f4409), + X(0x19115e1c), X(0x7d85759f), X(0x1942a6f3), X(0x7d7b93da), + X(0x1973ebe6), X(0x7d719eba), X(0x19a52ceb), X(0x7d679642), + X(0x19d669fc), X(0x7d5d7a74), X(0x1a07a311), X(0x7d534b50), + X(0x1a38d823), X(0x7d4908d9), X(0x1a6a0929), X(0x7d3eb30f), + X(0x1a9b361d), X(0x7d3449f5), X(0x1acc5ef6), X(0x7d29cd8c), + X(0x1afd83ad), X(0x7d1f3dd6), X(0x1b2ea43a), X(0x7d149ad5), + X(0x1b5fc097), X(0x7d09e489), X(0x1b90d8bb), X(0x7cff1af5), + X(0x1bc1ec9e), X(0x7cf43e1a), X(0x1bf2fc3a), X(0x7ce94dfb), + X(0x1c240786), X(0x7cde4a98), X(0x1c550e7c), X(0x7cd333f3), + X(0x1c861113), X(0x7cc80a0f), X(0x1cb70f43), X(0x7cbcccec), + X(0x1ce80906), X(0x7cb17c8d), X(0x1d18fe54), X(0x7ca618f3), + X(0x1d49ef26), X(0x7c9aa221), X(0x1d7adb73), X(0x7c8f1817), + X(0x1dabc334), X(0x7c837ad8), X(0x1ddca662), X(0x7c77ca65), + X(0x1e0d84f5), X(0x7c6c06c0), X(0x1e3e5ee5), X(0x7c602fec), + X(0x1e6f342c), X(0x7c5445e9), X(0x1ea004c1), X(0x7c4848ba), + X(0x1ed0d09d), X(0x7c3c3860), X(0x1f0197b8), X(0x7c3014de), + X(0x1f325a0b), X(0x7c23de35), X(0x1f63178f), X(0x7c179467), + X(0x1f93d03c), X(0x7c0b3777), X(0x1fc4840a), X(0x7bfec765), + X(0x1ff532f2), X(0x7bf24434), X(0x2025dcec), X(0x7be5ade6), + X(0x205681f1), X(0x7bd9047c), X(0x208721f9), X(0x7bcc47fa), + X(0x20b7bcfe), X(0x7bbf7860), X(0x20e852f6), X(0x7bb295b0), + X(0x2118e3dc), X(0x7ba59fee), X(0x21496fa7), X(0x7b989719), + X(0x2179f64f), X(0x7b8b7b36), X(0x21aa77cf), X(0x7b7e4c45), + X(0x21daf41d), X(0x7b710a49), X(0x220b6b32), X(0x7b63b543), + X(0x223bdd08), X(0x7b564d36), X(0x226c4996), X(0x7b48d225), + X(0x229cb0d5), X(0x7b3b4410), X(0x22cd12bd), X(0x7b2da2fa), + X(0x22fd6f48), X(0x7b1feee5), X(0x232dc66d), X(0x7b1227d3), + X(0x235e1826), X(0x7b044dc7), X(0x238e646a), X(0x7af660c2), + X(0x23beab33), X(0x7ae860c7), X(0x23eeec78), X(0x7ada4dd8), + X(0x241f2833), X(0x7acc27f7), X(0x244f5e5c), X(0x7abdef25), + X(0x247f8eec), X(0x7aafa367), X(0x24afb9da), X(0x7aa144bc), + X(0x24dfdf20), X(0x7a92d329), X(0x250ffeb7), X(0x7a844eae), + X(0x25401896), X(0x7a75b74f), X(0x25702cb7), X(0x7a670d0d), + X(0x25a03b11), X(0x7a584feb), X(0x25d0439f), X(0x7a497feb), + X(0x26004657), X(0x7a3a9d0f), X(0x26304333), X(0x7a2ba75a), + X(0x26603a2c), X(0x7a1c9ece), X(0x26902b39), X(0x7a0d836d), + X(0x26c01655), X(0x79fe5539), X(0x26effb76), X(0x79ef1436), + X(0x271fda96), X(0x79dfc064), X(0x274fb3ae), X(0x79d059c8), + X(0x277f86b5), X(0x79c0e062), X(0x27af53a6), X(0x79b15435), + X(0x27df1a77), X(0x79a1b545), X(0x280edb23), X(0x79920392), + X(0x283e95a1), X(0x79823f20), X(0x286e49ea), X(0x797267f2), + X(0x289df7f8), X(0x79627e08), X(0x28cd9fc1), X(0x79528167), + X(0x28fd4140), X(0x79427210), X(0x292cdc6d), X(0x79325006), + X(0x295c7140), X(0x79221b4b), X(0x298bffb2), X(0x7911d3e2), + X(0x29bb87bc), X(0x790179cd), X(0x29eb0957), X(0x78f10d0f), + X(0x2a1a847b), X(0x78e08dab), X(0x2a49f920), X(0x78cffba3), + X(0x2a796740), X(0x78bf56f9), X(0x2aa8ced3), X(0x78ae9fb0), + X(0x2ad82fd2), X(0x789dd5cb), X(0x2b078a36), X(0x788cf94c), + X(0x2b36ddf7), X(0x787c0a36), X(0x2b662b0e), X(0x786b088c), + X(0x2b957173), X(0x7859f44f), X(0x2bc4b120), X(0x7848cd83), + X(0x2bf3ea0d), X(0x7837942b), X(0x2c231c33), X(0x78264849), + X(0x2c52478a), X(0x7814e9df), X(0x2c816c0c), X(0x780378f1), + X(0x2cb089b1), X(0x77f1f581), X(0x2cdfa071), X(0x77e05f91), + X(0x2d0eb046), X(0x77ceb725), X(0x2d3db928), X(0x77bcfc3f), + X(0x2d6cbb10), X(0x77ab2ee2), X(0x2d9bb5f6), X(0x77994f11), + X(0x2dcaa9d5), X(0x77875cce), X(0x2df996a3), X(0x7775581d), + X(0x2e287c5a), X(0x776340ff), X(0x2e575af3), X(0x77511778), + X(0x2e863267), X(0x773edb8b), X(0x2eb502ae), X(0x772c8d3a), + X(0x2ee3cbc1), X(0x771a2c88), X(0x2f128d99), X(0x7707b979), + X(0x2f41482e), X(0x76f5340e), X(0x2f6ffb7a), X(0x76e29c4b), + X(0x2f9ea775), X(0x76cff232), X(0x2fcd4c19), X(0x76bd35c7), + X(0x2ffbe95d), X(0x76aa670d), X(0x302a7f3a), X(0x76978605), + X(0x30590dab), X(0x768492b4), X(0x308794a6), X(0x76718d1c), + X(0x30b61426), X(0x765e7540), X(0x30e48c22), X(0x764b4b23), + X(0x3112fc95), X(0x76380ec8), X(0x31416576), X(0x7624c031), + X(0x316fc6be), X(0x76115f63), X(0x319e2067), X(0x75fdec60), + X(0x31cc7269), X(0x75ea672a), X(0x31fabcbd), X(0x75d6cfc5), + X(0x3228ff5c), X(0x75c32634), X(0x32573a3f), X(0x75af6a7b), + X(0x32856d5e), X(0x759b9c9b), X(0x32b398b3), X(0x7587bc98), + X(0x32e1bc36), X(0x7573ca75), X(0x330fd7e1), X(0x755fc635), + X(0x333debab), X(0x754bafdc), X(0x336bf78f), X(0x7537876c), + X(0x3399fb85), X(0x75234ce8), X(0x33c7f785), X(0x750f0054), + X(0x33f5eb89), X(0x74faa1b3), X(0x3423d78a), X(0x74e63108), + X(0x3451bb81), X(0x74d1ae55), X(0x347f9766), X(0x74bd199f), + X(0x34ad6b32), X(0x74a872e8), X(0x34db36df), X(0x7493ba34), + X(0x3508fa66), X(0x747eef85), X(0x3536b5be), X(0x746a12df), + X(0x356468e2), X(0x74552446), X(0x359213c9), X(0x744023bc), + X(0x35bfb66e), X(0x742b1144), X(0x35ed50c9), X(0x7415ece2), + X(0x361ae2d3), X(0x7400b69a), X(0x36486c86), X(0x73eb6e6e), + X(0x3675edd9), X(0x73d61461), X(0x36a366c6), X(0x73c0a878), + X(0x36d0d746), X(0x73ab2ab4), X(0x36fe3f52), X(0x73959b1b), + X(0x372b9ee3), X(0x737ff9ae), X(0x3758f5f2), X(0x736a4671), + X(0x37864477), X(0x73548168), X(0x37b38a6d), X(0x733eaa96), + X(0x37e0c7cc), X(0x7328c1ff), X(0x380dfc8d), X(0x7312c7a5), + X(0x383b28a9), X(0x72fcbb8c), X(0x38684c19), X(0x72e69db7), + X(0x389566d6), X(0x72d06e2b), X(0x38c278d9), X(0x72ba2cea), + X(0x38ef821c), X(0x72a3d9f7), X(0x391c8297), X(0x728d7557), + X(0x39497a43), X(0x7276ff0d), X(0x39766919), X(0x7260771b), + X(0x39a34f13), X(0x7249dd86), X(0x39d02c2a), X(0x72333251), + X(0x39fd0056), X(0x721c7580), X(0x3a29cb91), X(0x7205a716), + X(0x3a568dd4), X(0x71eec716), X(0x3a834717), X(0x71d7d585), + X(0x3aaff755), X(0x71c0d265), X(0x3adc9e86), X(0x71a9bdba), + X(0x3b093ca3), X(0x71929789), X(0x3b35d1a5), X(0x717b5fd3), + X(0x3b625d86), X(0x7164169d), X(0x3b8ee03e), X(0x714cbbeb), + X(0x3bbb59c7), X(0x71354fc0), X(0x3be7ca1a), X(0x711dd220), + X(0x3c143130), X(0x7106430e), X(0x3c408f03), X(0x70eea28e), + X(0x3c6ce38a), X(0x70d6f0a4), X(0x3c992ec0), X(0x70bf2d53), + X(0x3cc5709e), X(0x70a7589f), X(0x3cf1a91c), X(0x708f728b), + X(0x3d1dd835), X(0x70777b1c), X(0x3d49fde1), X(0x705f7255), + X(0x3d761a19), X(0x70475839), X(0x3da22cd7), X(0x702f2ccd), + X(0x3dce3614), X(0x7016f014), X(0x3dfa35c8), X(0x6ffea212), + X(0x3e262bee), X(0x6fe642ca), X(0x3e52187f), X(0x6fcdd241), + X(0x3e7dfb73), X(0x6fb5507a), X(0x3ea9d4c3), X(0x6f9cbd79), + X(0x3ed5a46b), X(0x6f841942), X(0x3f016a61), X(0x6f6b63d8), + X(0x3f2d26a0), X(0x6f529d40), X(0x3f58d921), X(0x6f39c57d), + X(0x3f8481dd), X(0x6f20dc92), X(0x3fb020ce), X(0x6f07e285), + X(0x3fdbb5ec), X(0x6eeed758), X(0x40074132), X(0x6ed5bb10), + X(0x4032c297), X(0x6ebc8db0), X(0x405e3a16), X(0x6ea34f3d), + X(0x4089a7a8), X(0x6e89ffb9), X(0x40b50b46), X(0x6e709f2a), + X(0x40e064ea), X(0x6e572d93), X(0x410bb48c), X(0x6e3daaf8), + X(0x4136fa27), X(0x6e24175c), X(0x416235b2), X(0x6e0a72c5), + X(0x418d6729), X(0x6df0bd35), X(0x41b88e84), X(0x6dd6f6b1), + X(0x41e3abbc), X(0x6dbd1f3c), X(0x420ebecb), X(0x6da336dc), + X(0x4239c7aa), X(0x6d893d93), X(0x4264c653), X(0x6d6f3365), + X(0x428fbabe), X(0x6d551858), X(0x42baa4e6), X(0x6d3aec6e), + X(0x42e584c3), X(0x6d20afac), X(0x43105a50), X(0x6d066215), + X(0x433b2585), X(0x6cec03af), X(0x4365e65b), X(0x6cd1947c), + X(0x43909ccd), X(0x6cb71482), X(0x43bb48d4), X(0x6c9c83c3), + X(0x43e5ea68), X(0x6c81e245), X(0x44108184), X(0x6c67300b), + X(0x443b0e21), X(0x6c4c6d1a), X(0x44659039), X(0x6c319975), + X(0x449007c4), X(0x6c16b521), X(0x44ba74bd), X(0x6bfbc021), + X(0x44e4d71c), X(0x6be0ba7b), X(0x450f2edb), X(0x6bc5a431), + X(0x45397bf4), X(0x6baa7d49), X(0x4563be60), X(0x6b8f45c7), + X(0x458df619), X(0x6b73fdae), X(0x45b82318), X(0x6b58a503), + X(0x45e24556), X(0x6b3d3bcb), X(0x460c5cce), X(0x6b21c208), + X(0x46366978), X(0x6b0637c1), X(0x46606b4e), X(0x6aea9cf8), + X(0x468a624a), X(0x6acef1b2), X(0x46b44e65), X(0x6ab335f4), + X(0x46de2f99), X(0x6a9769c1), X(0x470805df), X(0x6a7b8d1e), + X(0x4731d131), X(0x6a5fa010), X(0x475b9188), X(0x6a43a29a), + X(0x478546de), X(0x6a2794c1), X(0x47aef12c), X(0x6a0b7689), + X(0x47d8906d), X(0x69ef47f6), X(0x48022499), X(0x69d3090e), + X(0x482badab), X(0x69b6b9d3), X(0x48552b9b), X(0x699a5a4c), + X(0x487e9e64), X(0x697dea7b), X(0x48a805ff), X(0x69616a65), + X(0x48d16265), X(0x6944da10), X(0x48fab391), X(0x6928397e), + X(0x4923f97b), X(0x690b88b5), X(0x494d341e), X(0x68eec7b9), + X(0x49766373), X(0x68d1f68f), X(0x499f8774), X(0x68b5153a), + X(0x49c8a01b), X(0x689823bf), X(0x49f1ad61), X(0x687b2224), + X(0x4a1aaf3f), X(0x685e106c), X(0x4a43a5b0), X(0x6840ee9b), + X(0x4a6c90ad), X(0x6823bcb7), X(0x4a957030), X(0x68067ac3), + X(0x4abe4433), X(0x67e928c5), X(0x4ae70caf), X(0x67cbc6c0), + X(0x4b0fc99d), X(0x67ae54ba), X(0x4b387af9), X(0x6790d2b6), + X(0x4b6120bb), X(0x677340ba), X(0x4b89badd), X(0x67559eca), + X(0x4bb24958), X(0x6737ecea), X(0x4bdacc28), X(0x671a2b20), + X(0x4c034345), X(0x66fc596f), X(0x4c2baea9), X(0x66de77dc), + X(0x4c540e4e), X(0x66c0866d), X(0x4c7c622d), X(0x66a28524), + X(0x4ca4aa41), X(0x66847408), X(0x4ccce684), X(0x6666531d), + X(0x4cf516ee), X(0x66482267), X(0x4d1d3b7a), X(0x6629e1ec), + X(0x4d455422), X(0x660b91af), X(0x4d6d60df), X(0x65ed31b5), + X(0x4d9561ac), X(0x65cec204), X(0x4dbd5682), X(0x65b0429f), + X(0x4de53f5a), X(0x6591b38c), X(0x4e0d1c30), X(0x657314cf), + X(0x4e34ecfc), X(0x6554666d), X(0x4e5cb1b9), X(0x6535a86b), + X(0x4e846a60), X(0x6516dacd), X(0x4eac16eb), X(0x64f7fd98), + X(0x4ed3b755), X(0x64d910d1), X(0x4efb4b96), X(0x64ba147d), + X(0x4f22d3aa), X(0x649b08a0), X(0x4f4a4f89), X(0x647bed3f), + X(0x4f71bf2e), X(0x645cc260), X(0x4f992293), X(0x643d8806), + X(0x4fc079b1), X(0x641e3e38), X(0x4fe7c483), X(0x63fee4f8), + X(0x500f0302), X(0x63df7c4d), X(0x50363529), X(0x63c0043b), + X(0x505d5af1), X(0x63a07cc7), X(0x50847454), X(0x6380e5f6), + X(0x50ab814d), X(0x63613fcd), X(0x50d281d5), X(0x63418a50), + X(0x50f975e6), X(0x6321c585), X(0x51205d7b), X(0x6301f171), + X(0x5147388c), X(0x62e20e17), X(0x516e0715), X(0x62c21b7e), + X(0x5194c910), X(0x62a219aa), X(0x51bb7e75), X(0x628208a1), + X(0x51e22740), X(0x6261e866), X(0x5208c36a), X(0x6241b8ff), + X(0x522f52ee), X(0x62217a72), X(0x5255d5c5), X(0x62012cc2), + X(0x527c4bea), X(0x61e0cff5), X(0x52a2b556), X(0x61c06410), + X(0x52c91204), X(0x619fe918), X(0x52ef61ee), X(0x617f5f12), + X(0x5315a50e), X(0x615ec603), X(0x533bdb5d), X(0x613e1df0), + X(0x536204d7), X(0x611d66de), X(0x53882175), X(0x60fca0d2), + X(0x53ae3131), X(0x60dbcbd1), X(0x53d43406), X(0x60bae7e1), + X(0x53fa29ed), X(0x6099f505), X(0x542012e1), X(0x6078f344), + X(0x5445eedb), X(0x6057e2a2), X(0x546bbdd7), X(0x6036c325), + X(0x54917fce), X(0x601594d1), X(0x54b734ba), X(0x5ff457ad), + X(0x54dcdc96), X(0x5fd30bbc), X(0x5502775c), X(0x5fb1b104), + X(0x55280505), X(0x5f90478a), X(0x554d858d), X(0x5f6ecf53), + X(0x5572f8ed), X(0x5f4d4865), X(0x55985f20), X(0x5f2bb2c5), + X(0x55bdb81f), X(0x5f0a0e77), X(0x55e303e6), X(0x5ee85b82), + X(0x5608426e), X(0x5ec699e9), X(0x562d73b2), X(0x5ea4c9b3), + X(0x565297ab), X(0x5e82eae5), X(0x5677ae54), X(0x5e60fd84), + X(0x569cb7a8), X(0x5e3f0194), X(0x56c1b3a1), X(0x5e1cf71c), + X(0x56e6a239), X(0x5dfade20), X(0x570b8369), X(0x5dd8b6a7), + X(0x5730572e), X(0x5db680b4), X(0x57551d80), X(0x5d943c4e), + X(0x5779d65b), X(0x5d71e979), X(0x579e81b8), X(0x5d4f883b), + X(0x57c31f92), X(0x5d2d189a), X(0x57e7afe4), X(0x5d0a9a9a), + X(0x580c32a7), X(0x5ce80e41), X(0x5830a7d6), X(0x5cc57394), + X(0x58550f6c), X(0x5ca2ca99), X(0x58796962), X(0x5c801354), + X(0x589db5b3), X(0x5c5d4dcc), X(0x58c1f45b), X(0x5c3a7a05), + X(0x58e62552), X(0x5c179806), X(0x590a4893), X(0x5bf4a7d2), + X(0x592e5e19), X(0x5bd1a971), X(0x595265df), X(0x5bae9ce7), + X(0x59765fde), X(0x5b8b8239), X(0x599a4c12), X(0x5b68596d), + X(0x59be2a74), X(0x5b452288), X(0x59e1faff), X(0x5b21dd90), + X(0x5a05bdae), X(0x5afe8a8b), X(0x5a29727b), X(0x5adb297d), + X(0x5a4d1960), X(0x5ab7ba6c), X(0x5a70b258), X(0x5a943d5e), +}; + diff --git a/components/spotify/cspot/bell/tremor/misc.c b/components/spotify/cspot/bell/tremor/misc.c new file mode 100644 index 00000000..66bf523d --- /dev/null +++ b/components/spotify/cspot/bell/tremor/misc.c @@ -0,0 +1,208 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the XIPHOPHORUS Company http://www.xiph.org/ * + * * + ********************************************************************/ + +#define HEAD_ALIGN 64 +#include +#include +#include +#define MISC_C +#include "misc.h" +#include + +static void **pointers=NULL; +static long *insertlist=NULL; /* We can't embed this in the pointer list; + a pointer can have any value... */ + +static char **files=NULL; +static long *file_bytes=NULL; +static int filecount=0; + +static int ptop=0; +static int palloced=0; +static int pinsert=0; + +typedef struct { + char *file; + long line; + long ptr; + long bytes; +} head; + +long global_bytes=0; +long start_time=-1; + +static void *_insert(void *ptr,long bytes,char *file,long line){ + ((head *)ptr)->file=file; + ((head *)ptr)->line=line; + ((head *)ptr)->ptr=pinsert; + ((head *)ptr)->bytes=bytes-HEAD_ALIGN; + + if(pinsert>=palloced){ + palloced+=64; + if(pointers){ + pointers=(void **)realloc(pointers,sizeof(void **)*palloced); + insertlist=(long *)realloc(insertlist,sizeof(long *)*palloced); + }else{ + pointers=(void **)malloc(sizeof(void **)*palloced); + insertlist=(long *)malloc(sizeof(long *)*palloced); + } + } + + pointers[pinsert]=ptr; + + if(pinsert==ptop) + pinsert=++ptop; + else + pinsert=insertlist[pinsert]; + +#ifdef _VDBG_GRAPHFILE + { + FILE *out; + struct timeval tv; + static struct timezone tz; + int i; + char buffer[80]; + gettimeofday(&tv,&tz); + + for(i=0;ifile; + long bytes =((head *)ptr)->bytes; + int i; + + gettimeofday(&tv,&tz); + fprintf(out,"%ld, %ld\n",-start_time+(tv.tv_sec*1000)+(tv.tv_usec/1000), + global_bytes); + fprintf(out,"%ld, %ld\n",-start_time+(tv.tv_sec*1000)+(tv.tv_usec/1000), + global_bytes-((head *)ptr)->bytes); + fclose(out); + + for(i=0;ibytes; + + insert=((head *)ptr)->ptr; + insertlist[insert]=pinsert; + pinsert=insert; + + if(pointers[insert]==NULL){ + fprintf(stderr,"DEBUGGING MALLOC ERROR: freeing previously freed memory\n"); + fprintf(stderr,"\t%s %ld\n",((head *)ptr)->file,((head *)ptr)->line); + } + + if(global_bytes<0){ + fprintf(stderr,"DEBUGGING MALLOC ERROR: freeing unmalloced memory\n"); + } + + pointers[insert]=NULL; +} + +void _VDBG_dump(void){ + int i; + for(i=0;ifile,ptr->line); + } + +} + +extern void *_VDBG_malloc(void *ptr,long bytes,char *file,long line){ + bytes+=HEAD_ALIGN; + if(ptr){ + ptr-=HEAD_ALIGN; + _ripremove(ptr); + ptr=realloc(ptr,bytes); + }else{ + ptr=malloc(bytes); + memset(ptr,0,bytes); + } + return _insert(ptr,bytes,file,line); +} + +extern void _VDBG_free(void *ptr,char *file,long line){ + if(ptr){ + ptr-=HEAD_ALIGN; + _ripremove(ptr); + free(ptr); + } +} + diff --git a/components/spotify/cspot/bell/tremor/misc.h b/components/spotify/cspot/bell/tremor/misc.h new file mode 100644 index 00000000..d0a522a3 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/misc.h @@ -0,0 +1,192 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: miscellaneous math and prototypes + + ********************************************************************/ + +#ifndef _V_RANDOM_H_ +#define _V_RANDOM_H_ +#include "ivorbiscodec.h" +#include "os_types.h" + +/*#define _VDBG_GRAPHFILE "_0.m"*/ + + +#ifdef _VDBG_GRAPHFILE +extern void *_VDBG_malloc(void *ptr,long bytes,char *file,long line); +extern void _VDBG_free(void *ptr,char *file,long line); + +#undef _ogg_malloc +#undef _ogg_calloc +#undef _ogg_realloc +#undef _ogg_free + +#define _ogg_malloc(x) _VDBG_malloc(NULL,(x),__FILE__,__LINE__) +#define _ogg_calloc(x,y) _VDBG_malloc(NULL,(x)*(y),__FILE__,__LINE__) +#define _ogg_realloc(x,y) _VDBG_malloc((x),(y),__FILE__,__LINE__) +#define _ogg_free(x) _VDBG_free((x),__FILE__,__LINE__) +#endif + +#include "asm_arm.h" + +#ifndef _V_WIDE_MATH +#define _V_WIDE_MATH + +#ifndef _LOW_ACCURACY_ +/* 64 bit multiply */ + +#include + +#if BYTE_ORDER==LITTLE_ENDIAN +union magic { + struct { + ogg_int32_t lo; + ogg_int32_t hi; + } halves; + ogg_int64_t whole; +}; +#endif + +#if BYTE_ORDER==BIG_ENDIAN +union magic { + struct { + ogg_int32_t hi; + ogg_int32_t lo; + } halves; + ogg_int64_t whole; +}; +#endif + +static inline ogg_int32_t MULT32(ogg_int32_t x, ogg_int32_t y) { + union magic magic; + magic.whole = (ogg_int64_t)x * y; + return magic.halves.hi; +} + +static inline ogg_int32_t MULT31(ogg_int32_t x, ogg_int32_t y) { + return MULT32(x,y)<<1; +} + +static inline ogg_int32_t MULT31_SHIFT15(ogg_int32_t x, ogg_int32_t y) { + union magic magic; + magic.whole = (ogg_int64_t)x * y; + return ((ogg_uint32_t)(magic.halves.lo)>>15) | ((magic.halves.hi)<<17); +} + +#else +/* 32 bit multiply, more portable but less accurate */ + +/* + * Note: Precision is biased towards the first argument therefore ordering + * is important. Shift values were chosen for the best sound quality after + * many listening tests. + */ + +/* + * For MULT32 and MULT31: The second argument is always a lookup table + * value already preshifted from 31 to 8 bits. We therefore take the + * opportunity to save on text space and use unsigned char for those + * tables in this case. + */ + +static inline ogg_int32_t MULT32(ogg_int32_t x, ogg_int32_t y) { + return (x >> 9) * y; /* y preshifted >>23 */ +} + +static inline ogg_int32_t MULT31(ogg_int32_t x, ogg_int32_t y) { + return (x >> 8) * y; /* y preshifted >>23 */ +} + +static inline ogg_int32_t MULT31_SHIFT15(ogg_int32_t x, ogg_int32_t y) { + return (x >> 6) * y; /* y preshifted >>9 */ +} + +#endif + +/* + * This should be used as a memory barrier, forcing all cached values in + * registers to wr writen back to memory. Might or might not be beneficial + * depending on the architecture and compiler. + */ +#define MB() + +/* + * The XPROD functions are meant to optimize the cross products found all + * over the place in mdct.c by forcing memory operation ordering to avoid + * unnecessary register reloads as soon as memory is being written to. + * However this is only beneficial on CPUs with a sane number of general + * purpose registers which exclude the Intel x86. On Intel, better let the + * compiler actually reload registers directly from original memory by using + * macros. + */ + +#ifdef __i386__ + +#define XPROD32(_a, _b, _t, _v, _x, _y) \ + { *(_x)=MULT32(_a,_t)+MULT32(_b,_v); \ + *(_y)=MULT32(_b,_t)-MULT32(_a,_v); } +#define XPROD31(_a, _b, _t, _v, _x, _y) \ + { *(_x)=MULT31(_a,_t)+MULT31(_b,_v); \ + *(_y)=MULT31(_b,_t)-MULT31(_a,_v); } +#define XNPROD31(_a, _b, _t, _v, _x, _y) \ + { *(_x)=MULT31(_a,_t)-MULT31(_b,_v); \ + *(_y)=MULT31(_b,_t)+MULT31(_a,_v); } + +#else + +static inline void XPROD32(ogg_int32_t a, ogg_int32_t b, + ogg_int32_t t, ogg_int32_t v, + ogg_int32_t *x, ogg_int32_t *y) +{ + *x = MULT32(a, t) + MULT32(b, v); + *y = MULT32(b, t) - MULT32(a, v); +} + +static inline void XPROD31(ogg_int32_t a, ogg_int32_t b, + ogg_int32_t t, ogg_int32_t v, + ogg_int32_t *x, ogg_int32_t *y) +{ + *x = MULT31(a, t) + MULT31(b, v); + *y = MULT31(b, t) - MULT31(a, v); +} + +static inline void XNPROD31(ogg_int32_t a, ogg_int32_t b, + ogg_int32_t t, ogg_int32_t v, + ogg_int32_t *x, ogg_int32_t *y) +{ + *x = MULT31(a, t) - MULT31(b, v); + *y = MULT31(b, t) + MULT31(a, v); +} + +#endif + +#endif + +#ifndef _V_CLIP_MATH +#define _V_CLIP_MATH + +static inline ogg_int32_t CLIP_TO_15(ogg_int32_t x) { + int ret=x; + ret-= ((x<=32767)-1)&(x-32767); + ret-= ((x>=-32768)-1)&(x+32768); + return(ret); +} + +#endif + +#endif + + + + diff --git a/components/spotify/cspot/bell/tremor/ogg.h b/components/spotify/cspot/bell/tremor/ogg.h new file mode 100644 index 00000000..85cb41b6 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/ogg.h @@ -0,0 +1,206 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: subsumed libogg includes + + ********************************************************************/ +#ifndef _OGG_H +#define _OGG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "os_types.h" + +typedef struct ogg_buffer_state{ + struct ogg_buffer *unused_buffers; + struct ogg_reference *unused_references; + int outstanding; + int shutdown; +} ogg_buffer_state; + +typedef struct ogg_buffer { + unsigned char *data; + long size; + int refcount; + + union { + ogg_buffer_state *owner; + struct ogg_buffer *next; + } ptr; +} ogg_buffer; + +typedef struct ogg_reference { + ogg_buffer *buffer; + long begin; + long length; + + struct ogg_reference *next; +} ogg_reference; + +typedef struct oggpack_buffer { + int headbit; + unsigned char *headptr; + long headend; + + /* memory management */ + ogg_reference *head; + ogg_reference *tail; + + /* render the byte/bit counter API constant time */ + long count; /* doesn't count the tail */ +} oggpack_buffer; + +typedef struct oggbyte_buffer { + ogg_reference *baseref; + + ogg_reference *ref; + unsigned char *ptr; + long pos; + long end; +} oggbyte_buffer; + +typedef struct ogg_sync_state { + /* decode memory management pool */ + ogg_buffer_state *bufferpool; + + /* stream buffers */ + ogg_reference *fifo_head; + ogg_reference *fifo_tail; + long fifo_fill; + + /* stream sync management */ + int unsynced; + int headerbytes; + int bodybytes; + +} ogg_sync_state; + +typedef struct ogg_stream_state { + ogg_reference *header_head; + ogg_reference *header_tail; + ogg_reference *body_head; + ogg_reference *body_tail; + + int e_o_s; /* set when we have buffered the last + packet in the logical bitstream */ + int b_o_s; /* set after we've written the initial page + of a logical bitstream */ + long serialno; + long pageno; + ogg_int64_t packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ + ogg_int64_t granulepos; + + int lacing_fill; + ogg_uint32_t body_fill; + + /* decode-side state data */ + int holeflag; + int spanflag; + int clearflag; + int laceptr; + ogg_uint32_t body_fill_next; + +} ogg_stream_state; + +typedef struct { + ogg_reference *packet; + long bytes; + long b_o_s; + long e_o_s; + ogg_int64_t granulepos; + ogg_int64_t packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ +} ogg_packet; + +typedef struct { + ogg_reference *header; + int header_len; + ogg_reference *body; + long body_len; +} ogg_page; + +/* Ogg BITSTREAM PRIMITIVES: bitstream ************************/ + +extern void oggpack_readinit(oggpack_buffer *b,ogg_reference *r); +extern long oggpack_look(oggpack_buffer *b,int bits); +extern void oggpack_adv(oggpack_buffer *b,int bits); +extern long oggpack_read(oggpack_buffer *b,int bits); +extern long oggpack_bytes(oggpack_buffer *b); +extern long oggpack_bits(oggpack_buffer *b); +extern int oggpack_eop(oggpack_buffer *b); + +/* Ogg BITSTREAM PRIMITIVES: decoding **************************/ + +extern ogg_sync_state *ogg_sync_create(void); +extern int ogg_sync_destroy(ogg_sync_state *oy); +extern int ogg_sync_reset(ogg_sync_state *oy); + +extern unsigned char *ogg_sync_bufferin(ogg_sync_state *oy, long size); +extern int ogg_sync_wrote(ogg_sync_state *oy, long bytes); +extern long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og); +extern int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og); +extern int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og); +extern int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op); +extern int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op); + +/* Ogg BITSTREAM PRIMITIVES: general ***************************/ + +extern ogg_stream_state *ogg_stream_create(int serialno); +extern int ogg_stream_destroy(ogg_stream_state *os); +extern int ogg_stream_reset(ogg_stream_state *os); +extern int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno); +extern int ogg_stream_eos(ogg_stream_state *os); + +extern int ogg_page_checksum_set(ogg_page *og); + +extern int ogg_page_version(ogg_page *og); +extern int ogg_page_continued(ogg_page *og); +extern int ogg_page_bos(ogg_page *og); +extern int ogg_page_eos(ogg_page *og); +extern ogg_int64_t ogg_page_granulepos(ogg_page *og); +extern ogg_uint32_t ogg_page_serialno(ogg_page *og); +extern ogg_uint32_t ogg_page_pageno(ogg_page *og); +extern int ogg_page_packets(ogg_page *og); +extern int ogg_page_getbuffer(ogg_page *og, unsigned char **buffer); + +extern int ogg_packet_release(ogg_packet *op); +extern int ogg_page_release(ogg_page *og); + +extern void ogg_page_dup(ogg_page *d, ogg_page *s); + +/* Ogg BITSTREAM PRIMITIVES: return codes ***************************/ + +#define OGG_SUCCESS 0 + +#define OGG_HOLE -10 +#define OGG_SPAN -11 +#define OGG_EVERSION -12 +#define OGG_ESERIAL -13 +#define OGG_EINVAL -14 +#define OGG_EEOS -15 + + +#ifdef __cplusplus +} +#endif + +#endif /* _OGG_H */ diff --git a/components/spotify/cspot/bell/tremor/os.h b/components/spotify/cspot/bell/tremor/os.h new file mode 100644 index 00000000..65a4992f --- /dev/null +++ b/components/spotify/cspot/bell/tremor/os.h @@ -0,0 +1,62 @@ +#ifndef _OS_H +#define _OS_H +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: #ifdef jail to whip a few platforms into the UNIX ideal. + + ********************************************************************/ + +#include +#include "os_types.h" + +#ifndef _V_IFDEFJAIL_H_ +# define _V_IFDEFJAIL_H_ + +# ifdef __GNUC__ +# define STIN static __inline__ +# elif _WIN32 +# define STIN static __inline +# endif +#else +# define STIN static +#endif + +#ifndef M_PI +# define M_PI (3.1415926536f) +#endif + +#ifdef _WIN32 +# include +# define rint(x) (floor((x)+0.5f)) +# define NO_FLOAT_MATH_LIB +# define FAST_HYPOT(a, b) sqrt((a)*(a) + (b)*(b)) +#endif + +#ifdef HAVE_ALLOCA_H +# include +#endif + +#ifdef USE_MEMORY_H +# include +#endif + +#ifndef min +# define min(x,y) ((x)>(y)?(y):(x)) +#endif + +#ifndef max +# define max(x,y) ((x)<(y)?(y):(x)) +#endif + +#endif /* _OS_H */ diff --git a/components/spotify/cspot/bell/tremor/os_types.h b/components/spotify/cspot/bell/tremor/os_types.h new file mode 100644 index 00000000..8d444d66 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/os_types.h @@ -0,0 +1,93 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: #ifdef jail to whip a few platforms into the UNIX ideal. + + ********************************************************************/ +#ifndef _OS_TYPES_H +#define _OS_TYPES_H + +#ifdef _LOW_ACCURACY_ +# define X(n) (((((n)>>22)+1)>>1) - ((((n)>>22)+1)>>9)) +# define LOOKUP_T const unsigned char +#else +# define X(n) (n) +# define LOOKUP_T const ogg_int32_t +#endif + +/* make it easy on the folks that want to compile the libs with a + different malloc than stdlib */ +#define _ogg_malloc malloc +#define _ogg_calloc calloc +#define _ogg_realloc realloc +#define _ogg_free free + +#ifdef _WIN32 + +# ifndef __GNUC__ + /* MSVC/Borland */ + typedef __int64 ogg_int64_t; + typedef __int32 ogg_int32_t; + typedef unsigned __int32 ogg_uint32_t; + typedef __int16 ogg_int16_t; + typedef unsigned __int16 ogg_uint16_t; +# else + /* Cygwin */ + #include <_G_config.h> + typedef _G_int64_t ogg_int64_t; + typedef _G_int32_t ogg_int32_t; + typedef _G_uint32_t ogg_uint32_t; + typedef _G_int16_t ogg_int16_t; + typedef _G_uint16_t ogg_uint16_t; +# endif + +#elif defined(__MACOS__) + +# include + typedef SInt16 ogg_int16_t; + typedef UInt16 ogg_uint16_t; + typedef SInt32 ogg_int32_t; + typedef UInt32 ogg_uint32_t; + typedef SInt64 ogg_int64_t; + +#elif defined(__MACOSX__) /* MacOS X Framework build */ + +# include + typedef int16_t ogg_int16_t; + typedef u_int16_t ogg_uint16_t; + typedef int32_t ogg_int32_t; + typedef u_int32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + +#elif defined(__BEOS__) + + /* Be */ +# include + +#elif defined (__EMX__) + + /* OS/2 GCC */ + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + +#else + +# include +# include "config_types.h" + +#endif + +#endif /* _OS_TYPES_H */ diff --git a/components/spotify/cspot/bell/tremor/res012.c b/components/spotify/cspot/bell/tremor/res012.c new file mode 100644 index 00000000..fffda8e8 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/res012.c @@ -0,0 +1,225 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: residue backend 0, 1 and 2 implementation + + ********************************************************************/ + +#include +#include +#include +#include "ogg.h" +#include "ivorbiscodec.h" +#include "codec_internal.h" +#include "codebook.h" +#include "misc.h" +#include "os.h" + +void res_clear_info(vorbis_info_residue *info){ + if(info){ + if(info->stagemasks)_ogg_free(info->stagemasks); + if(info->stagebooks)_ogg_free(info->stagebooks); + memset(info,0,sizeof(*info)); + } +} + + +/* vorbis_info is for range checking */ +int res_unpack(vorbis_info_residue *info, + vorbis_info *vi,oggpack_buffer *opb){ + int j,k; + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + memset(info,0,sizeof(*info)); + + info->type=oggpack_read(opb,16); + if(info->type>2 || info->type<0)goto errout; + info->begin=oggpack_read(opb,24); + info->end=oggpack_read(opb,24); + info->grouping=oggpack_read(opb,24)+1; + info->partitions=oggpack_read(opb,6)+1; + info->groupbook=oggpack_read(opb,8); + if(info->groupbook>=ci->books)goto errout; + + info->stagemasks=_ogg_malloc(info->partitions*sizeof(*info->stagemasks)); + info->stagebooks=_ogg_malloc(info->partitions*8*sizeof(*info->stagebooks)); + + for(j=0;jpartitions;j++){ + int cascade=oggpack_read(opb,3); + if(oggpack_read(opb,1)) + cascade|=(oggpack_read(opb,5)<<3); + info->stagemasks[j]=cascade; + } + + for(j=0;jpartitions;j++){ + for(k=0;k<8;k++){ + if((info->stagemasks[j]>>k)&1){ + unsigned char book=oggpack_read(opb,8); + if(book>=ci->books)goto errout; + info->stagebooks[j*8+k]=book; + if(k+1>info->stages)info->stages=k+1; + }else + info->stagebooks[j*8+k]=0xff; + } + } + + if(oggpack_eop(opb))goto errout; + + return 0; + errout: + res_clear_info(info); + return 1; +} + +int res_inverse(vorbis_dsp_state *vd,vorbis_info_residue *info, + ogg_int32_t **in,int *nonzero,int ch){ + + int i,j,k,s,used=0; + codec_setup_info *ci=(codec_setup_info *)vd->vi->codec_setup; + codebook *phrasebook=ci->book_param+info->groupbook; + int samples_per_partition=info->grouping; + int partitions_per_word=phrasebook->dim; + int pcmend=ci->blocksizes[vd->W]; + + if(info->type<2){ + int max=pcmend>>1; + int end=(info->endend:max); + int n=end-info->begin; + + if(n>0){ + int partvals=n/samples_per_partition; + int partwords=(partvals+partitions_per_word-1)/partitions_per_word; + + for(i=0;istages;s++){ + + for(i=0;i=0;k--) + partword[0][i+k]=partword[0][i+k+1]*info->partitions; + + for(j=1;j=0;k--) + partword[j][i+k]=partword[j-1][i+k]; + + for(j=0;jopb); + if(temp==-1)goto eopbreak; + + /* this can be done quickly in assembly due to the quotient + always being at most six bits */ + for(k=0;kbegin+i*samples_per_partition; + if(info->stagemasks[(int)partword[j][i]]&(1<book_param+ + info->stagebooks[(partword[j][i]<<3)+s]; + if(info->type){ + if(vorbis_book_decodev_add(stagebook,in[j]+offset,&vd->opb, + samples_per_partition,-8)==-1) + goto eopbreak; + }else{ + if(vorbis_book_decodevs_add(stagebook,in[j]+offset,&vd->opb, + samples_per_partition,-8)==-1) + goto eopbreak; + } + } + } + } + } + } + } + }else{ + int max=(pcmend*ch)>>1; + int end=(info->endend:max); + int n=end-info->begin; + + if(n>0){ + int partvals=n/samples_per_partition; + int partwords=(partvals+partitions_per_word-1)/partitions_per_word; + + char *partword= + (char *)alloca(partwords*partitions_per_word*sizeof(*partword)); + int beginoff=info->begin/ch; + + for(i=0;istages;s++){ + for(i=0;i=0;k--) + partword[i+k]=partword[i+k+1]*info->partitions; + + /* fetch the partition word */ + temp=vorbis_book_decode(phrasebook,&vd->opb); + if(temp==-1)goto eopbreak; + + /* this can be done quickly in assembly due to the quotient + always being at most six bits */ + for(k=0;kstagemasks[(int)partword[i]]&(1<book_param+ + info->stagebooks[(partword[i]<<3)+s]; + if(vorbis_book_decodevv_add(stagebook,in, + i*samples_per_partition+beginoff,ch, + &vd->opb, + samples_per_partition,-8)==-1) + goto eopbreak; + } + } + } + } + } + errout: + eopbreak: + + return 0; +} + diff --git a/components/spotify/cspot/bell/tremor/vorbisfile.c b/components/spotify/cspot/bell/tremor/vorbisfile.c new file mode 100644 index 00000000..aafc3087 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/vorbisfile.c @@ -0,0 +1,1589 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: stdio-based convenience library for opening/seeking/decoding + last mod: $Id: vorbisfile.c,v 1.6.2.5 2003/11/20 06:16:17 xiphmont Exp $ + + ********************************************************************/ + +#include +#include +#include +#include +#include + +#include "codec_internal.h" +#include "ivorbisfile.h" + +#include "os.h" +#include "misc.h" + +#define NOTOPEN 0 +#define PARTOPEN 1 +#define OPENED 2 +#define STREAMSET 3 /* serialno and link set, but not to current link */ +#define LINKSET 4 /* serialno and link set to current link */ +#define INITSET 5 + +/* A 'chained bitstream' is a Vorbis bitstream that contains more than + one logical bitstream arranged end to end (the only form of Ogg + multiplexing allowed in a Vorbis bitstream; grouping [parallel + multiplexing] is not allowed in Vorbis) */ + +/* A Vorbis file can be played beginning to end (streamed) without + worrying ahead of time about chaining (see decoder_example.c). If + we have the whole file, however, and want random access + (seeking/scrubbing) or desire to know the total length/time of a + file, we need to account for the possibility of chaining. */ + +/* We can handle things a number of ways; we can determine the entire + bitstream structure right off the bat, or find pieces on demand. + This example determines and caches structure for the entire + bitstream, but builds a virtual decoder on the fly when moving + between links in the chain. */ + +/* There are also different ways to implement seeking. Enough + information exists in an Ogg bitstream to seek to + sample-granularity positions in the output. Or, one can seek by + picking some portion of the stream roughly in the desired area if + we only want coarse navigation through the stream. */ + +/************************************************************************* + * Many, many internal helpers. The intention is not to be confusing; + * rampant duplication and monolithic function implementation would be + * harder to understand anyway. The high level functions are last. Begin + * grokking near the end of the file */ + + +/* read a little more data from the file/pipe into the ogg_sync framer */ +static long _get_data(OggVorbis_File *vf){ + errno=0; + if(vf->datasource){ + unsigned char *buffer=ogg_sync_bufferin(vf->oy,CHUNKSIZE); + long bytes=(vf->callbacks.read_func)(buffer,1,CHUNKSIZE,vf->datasource); + if(bytes>0)ogg_sync_wrote(vf->oy,bytes); + if(bytes==0 && errno)return -1; + return bytes; + }else + return 0; +} + +/* save a tiny smidge of verbosity to make the code more readable */ +static void _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){ + if(vf->datasource){ + (vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET); + vf->offset=offset; + ogg_sync_reset(vf->oy); + }else{ + /* shouldn't happen unless someone writes a broken callback */ + return; + } +} + +/* The read/seek functions track absolute position within the stream */ + +/* from the head of the stream, get the next page. boundary specifies + if the function is allowed to fetch more data from the stream (and + how much) or only use internally buffered data. + + boundary: -1) unbounded search + 0) read no additional data; use cached only + n) search for a new page beginning for n bytes + + return: <0) did not find a page (OV_FALSE, OV_EOF, OV_EREAD) + n) found a page at absolute offset n + + produces a refcounted page */ + +static ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og, + ogg_int64_t boundary){ + if(boundary>0)boundary+=vf->offset; + while(1){ + long more; + + if(boundary>0 && vf->offset>=boundary)return OV_FALSE; + more=ogg_sync_pageseek(vf->oy,og); + + if(more<0){ + /* skipped n bytes */ + vf->offset-=more; + }else{ + if(more==0){ + /* send more paramedics */ + if(!boundary)return OV_FALSE; + { + long ret=_get_data(vf); + if(ret==0)return OV_EOF; + if(ret<0)return OV_EREAD; + } + }else{ + /* got a page. Return the offset at the page beginning, + advance the internal offset past the page end */ + ogg_int64_t ret=vf->offset; + vf->offset+=more; + return ret; + + } + } + } +} + +/* find the latest page beginning before the current stream cursor + position. Much dirtier than the above as Ogg doesn't have any + backward search linkage. no 'readp' as it will certainly have to + read. */ +/* returns offset or OV_EREAD, OV_FAULT and produces a refcounted page */ + +static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_page *og){ + ogg_int64_t begin=vf->offset; + ogg_int64_t end=begin; + ogg_int64_t ret; + ogg_int64_t offset=-1; + + while(offset==-1){ + begin-=CHUNKSIZE; + if(begin<0) + begin=0; + _seek_helper(vf,begin); + while(vf->offsetoffset); + if(ret==OV_EREAD)return OV_EREAD; + if(ret<0){ + break; + }else{ + offset=ret; + } + } + } + + /* we have the offset. Actually snork and hold the page now */ + _seek_helper(vf,offset); + ret=_get_next_page(vf,og,CHUNKSIZE); + if(ret<0) + /* this shouldn't be possible */ + return OV_EFAULT; + + return offset; +} + +/* finds each bitstream link one at a time using a bisection search + (has to begin by knowing the offset of the lb's initial page). + Recurses for each link so it can alloc the link storage after + finding them all, then unroll and fill the cache at the same time */ +static int _bisect_forward_serialno(OggVorbis_File *vf, + ogg_int64_t begin, + ogg_int64_t searched, + ogg_int64_t end, + ogg_uint32_t currentno, + long m){ + ogg_int64_t endsearched=end; + ogg_int64_t next=end; + ogg_page og={0,0,0,0}; + ogg_int64_t ret; + + /* the below guards against garbage seperating the last and + first pages of two links. */ + while(searched=0)next=ret; + }else{ + searched=ret+og.header_len+og.body_len; + } + ogg_page_release(&og); + } + + _seek_helper(vf,next); + ret=_get_next_page(vf,&og,-1); + if(ret==OV_EREAD)return OV_EREAD; + + if(searched>=end || ret<0){ + ogg_page_release(&og); + vf->links=m+1; + vf->offsets=_ogg_malloc((vf->links+1)*sizeof(*vf->offsets)); + vf->serialnos=_ogg_malloc(vf->links*sizeof(*vf->serialnos)); + vf->offsets[m+1]=searched; + }else{ + ret=_bisect_forward_serialno(vf,next,vf->offset, + end,ogg_page_serialno(&og),m+1); + ogg_page_release(&og); + if(ret==OV_EREAD)return OV_EREAD; + } + + vf->offsets[m]=begin; + vf->serialnos[m]=currentno; + return 0; +} + +static int _decode_clear(OggVorbis_File *vf){ + if(vf->ready_state==INITSET){ + vorbis_dsp_destroy(vf->vd); + vf->vd=0; + vf->ready_state=STREAMSET; + } + + if(vf->ready_state>=STREAMSET){ + vorbis_info_clear(&vf->vi); + vorbis_comment_clear(&vf->vc); + vf->ready_state=OPENED; + } + return 0; +} + +/* uses the local ogg_stream storage in vf; this is important for + non-streaming input sources */ +/* consumes the page that's passed in (if any) */ +/* state is LINKSET upon successful return */ + +static int _fetch_headers(OggVorbis_File *vf, + vorbis_info *vi, + vorbis_comment *vc, + ogg_uint32_t *serialno, + ogg_page *og_ptr){ + ogg_page og={0,0,0,0}; + ogg_packet op={0,0,0,0,0,0}; + int i,ret; + + if(vf->ready_state>OPENED)_decode_clear(vf); + + if(!og_ptr){ + ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE); + if(llret==OV_EREAD)return OV_EREAD; + if(llret<0)return OV_ENOTVORBIS; + og_ptr=&og; + } + + ogg_stream_reset_serialno(vf->os,ogg_page_serialno(og_ptr)); + if(serialno)*serialno=vf->os->serialno; + + /* extract the initial header from the first page and verify that the + Ogg bitstream is in fact Vorbis data */ + + vorbis_info_init(vi); + vorbis_comment_init(vc); + + i=0; + while(i<3){ + ogg_stream_pagein(vf->os,og_ptr); + while(i<3){ + int result=ogg_stream_packetout(vf->os,&op); + if(result==0)break; + if(result==-1){ + ret=OV_EBADHEADER; + goto bail_header; + } + if((ret=vorbis_dsp_headerin(vi,vc,&op))){ + goto bail_header; + } + i++; + } + if(i<3) + if(_get_next_page(vf,og_ptr,CHUNKSIZE)<0){ + ret=OV_EBADHEADER; + goto bail_header; + } + } + + ogg_packet_release(&op); + ogg_page_release(&og); + vf->ready_state=LINKSET; + return 0; + + bail_header: + ogg_packet_release(&op); + ogg_page_release(&og); + vorbis_info_clear(vi); + vorbis_comment_clear(vc); + vf->ready_state=OPENED; + + return ret; +} + +/* we no longer preload all vorbis_info (and the associated + codec_setup) structs. Call this to seek and fetch the info from + the bitstream, if needed */ +static int _set_link_number(OggVorbis_File *vf,int link){ + if(link != vf->current_link) _decode_clear(vf); + if(vf->ready_stateoffsets[link]); + ogg_stream_reset_serialno(vf->os,vf->serialnos[link]); + vf->current_serialno=vf->serialnos[link]; + vf->current_link=link; + return _fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,NULL); + } + return 0; +} + +static int _set_link_number_preserve_pos(OggVorbis_File *vf,int link){ + ogg_int64_t pos=vf->offset; + int ret=_set_link_number(vf,link); + if(ret)return ret; + _seek_helper(vf,pos); + if(posoffsets[link] || pos>=vf->offsets[link+1]) + vf->ready_state=STREAMSET; + return 0; +} + +/* last step of the OggVorbis_File initialization; get all the offset + positions. Only called by the seekable initialization (local + stream storage is hacked slightly; pay attention to how that's + done) */ + +/* this is void and does not propogate errors up because we want to be + able to open and use damaged bitstreams as well as we can. Just + watch out for missing information for links in the OggVorbis_File + struct */ +static void _prefetch_all_offsets(OggVorbis_File *vf, ogg_int64_t dataoffset){ + ogg_page og={0,0,0,0}; + int i; + ogg_int64_t ret; + + vf->dataoffsets=_ogg_malloc(vf->links*sizeof(*vf->dataoffsets)); + vf->pcmlengths=_ogg_malloc(vf->links*2*sizeof(*vf->pcmlengths)); + + for(i=0;ilinks;i++){ + if(i==0){ + /* we already grabbed the initial header earlier. Just set the offset */ + vf->dataoffsets[i]=dataoffset; + _seek_helper(vf,dataoffset); + + }else{ + + /* seek to the location of the initial header */ + + _seek_helper(vf,vf->offsets[i]); + if(_fetch_headers(vf,&vf->vi,&vf->vc,NULL,NULL)<0){ + vf->dataoffsets[i]=-1; + }else{ + vf->dataoffsets[i]=vf->offset; + } + } + + /* fetch beginning PCM offset */ + + if(vf->dataoffsets[i]!=-1){ + ogg_int64_t accumulated=0,pos; + long lastblock=-1; + int result; + + ogg_stream_reset_serialno(vf->os,vf->serialnos[i]); + + while(1){ + ogg_packet op={0,0,0,0,0,0}; + + ret=_get_next_page(vf,&og,-1); + if(ret<0) + /* this should not be possible unless the file is + truncated/mangled */ + break; + + if(ogg_page_serialno(&og)!=vf->serialnos[i]) + break; + + pos=ogg_page_granulepos(&og); + + /* count blocksizes of all frames in the page */ + ogg_stream_pagein(vf->os,&og); + while((result=ogg_stream_packetout(vf->os,&op))){ + if(result>0){ /* ignore holes */ + long thisblock=vorbis_packet_blocksize(&vf->vi,&op); + if(lastblock!=-1) + accumulated+=(lastblock+thisblock)>>2; + lastblock=thisblock; + } + } + ogg_packet_release(&op); + + if(pos!=-1){ + /* pcm offset of last packet on the first audio page */ + accumulated= pos-accumulated; + break; + } + } + + /* less than zero? This is a stream with samples trimmed off + the beginning, a normal occurrence; set the offset to zero */ + if(accumulated<0)accumulated=0; + + vf->pcmlengths[i*2]=accumulated; + } + + /* get the PCM length of this link. To do this, + get the last page of the stream */ + { + ogg_int64_t end=vf->offsets[i+1]; + _seek_helper(vf,end); + + while(1){ + ret=_get_prev_page(vf,&og); + if(ret<0){ + /* this should not be possible */ + vorbis_info_clear(&vf->vi); + vorbis_comment_clear(&vf->vc); + break; + } + if(ogg_page_granulepos(&og)!=-1){ + vf->pcmlengths[i*2+1]=ogg_page_granulepos(&og)-vf->pcmlengths[i*2]; + break; + } + vf->offset=ret; + } + } + } + ogg_page_release(&og); +} + +static int _make_decode_ready(OggVorbis_File *vf){ + int i; + switch(vf->ready_state){ + case OPENED: + case STREAMSET: + for(i=0;ilinks;i++) + if(vf->offsets[i+1]>=vf->offset)break; + if(i==vf->links)return -1; + i=_set_link_number_preserve_pos(vf,i); + if(i)return i; + /* fall through */ + case LINKSET: + vf->vd=vorbis_dsp_create(&vf->vi); + vf->ready_state=INITSET; + vf->bittrack=0; + vf->samptrack=0; + case INITSET: + return 0; + default: + return -1; + } + +} + +static int _open_seekable2(OggVorbis_File *vf){ + ogg_uint32_t serialno=vf->current_serialno; + ogg_uint32_t tempserialno; + ogg_int64_t dataoffset=vf->offset, end; + ogg_page og={0,0,0,0}; + + /* we're partially open and have a first link header state in + storage in vf */ + /* we can seek, so set out learning all about this file */ + (vf->callbacks.seek_func)(vf->datasource,0,SEEK_END); + vf->offset=vf->end=(vf->callbacks.tell_func)(vf->datasource); + + /* We get the offset for the last page of the physical bitstream. + Most OggVorbis files will contain a single logical bitstream */ + end=_get_prev_page(vf,&og); + if(end<0)return end; + + /* more than one logical bitstream? */ + tempserialno=ogg_page_serialno(&og); + ogg_page_release(&og); + + if(tempserialno!=serialno){ + + /* Chained bitstream. Bisect-search each logical bitstream + section. Do so based on serial number only */ + if(_bisect_forward_serialno(vf,0,0,end+1,serialno,0)<0)return OV_EREAD; + + }else{ + + /* Only one logical bitstream */ + if(_bisect_forward_serialno(vf,0,end,end+1,serialno,0))return OV_EREAD; + + } + + /* the initial header memory is referenced by vf after; don't free it */ + _prefetch_all_offsets(vf,dataoffset); + return ov_raw_seek(vf,0); +} + +/* fetch and process a packet. Handles the case where we're at a + bitstream boundary and dumps the decoding machine. If the decoding + machine is unloaded, it loads it. It also keeps pcm_offset up to + date (seek and read both use this. seek uses a special hack with + readp). + + return: <0) error, OV_HOLE (lost packet) or OV_EOF + 0) need more data (only if readp==0) + 1) got a packet +*/ + +static int _fetch_and_process_packet(OggVorbis_File *vf, + int readp, + int spanp){ + ogg_page og={0,0,0,0}; + ogg_packet op={0,0,0,0,0,0}; + int ret=0; + + /* handle one packet. Try to fetch it from current stream state */ + /* extract packets from page */ + while(1){ + + /* process a packet if we can. If the machine isn't loaded, + neither is a page */ + if(vf->ready_state==INITSET){ + while(1) { + int result=ogg_stream_packetout(vf->os,&op); + ogg_int64_t granulepos; + + if(result<0){ + ret=OV_HOLE; /* hole in the data. */ + goto cleanup; + } + if(result>0){ + /* got a packet. process it */ + granulepos=op.granulepos; + if(!vorbis_dsp_synthesis(vf->vd,&op,1)){ /* lazy check for lazy + header handling. The + header packets aren't + audio, so if/when we + submit them, + vorbis_synthesis will + reject them */ + + vf->samptrack+=vorbis_dsp_pcmout(vf->vd,NULL,0); + vf->bittrack+=op.bytes*8; + + /* update the pcm offset. */ + if(granulepos!=-1 && !op.e_o_s){ + int link=(vf->seekable?vf->current_link:0); + int i,samples; + + /* this packet has a pcm_offset on it (the last packet + completed on a page carries the offset) After processing + (above), we know the pcm position of the *last* sample + ready to be returned. Find the offset of the *first* + + As an aside, this trick is inaccurate if we begin + reading anew right at the last page; the end-of-stream + granulepos declares the last frame in the stream, and the + last packet of the last page may be a partial frame. + So, we need a previous granulepos from an in-sequence page + to have a reference point. Thus the !op.e_o_s clause + above */ + + if(vf->seekable && link>0) + granulepos-=vf->pcmlengths[link*2]; + if(granulepos<0)granulepos=0; /* actually, this + shouldn't be possible + here unless the stream + is very broken */ + + samples=vorbis_dsp_pcmout(vf->vd,NULL,0); + + granulepos-=samples; + for(i=0;ipcmlengths[i*2+1]; + vf->pcm_offset=granulepos; + } + ret=1; + goto cleanup; + } + } + else + break; + } + } + + if(vf->ready_state>=OPENED){ + int ret; + if(!readp){ + ret=0; + goto cleanup; + } + if((ret=_get_next_page(vf,&og,-1))<0){ + ret=OV_EOF; /* eof. leave unitialized */ + goto cleanup; + } + + /* bitrate tracking; add the header's bytes here, the body bytes + are done by packet above */ + vf->bittrack+=og.header_len*8; + + /* has our decoding just traversed a bitstream boundary? */ + if(vf->ready_state==INITSET){ + if(vf->current_serialno!=ogg_page_serialno(&og)){ + if(!spanp){ + ret=OV_EOF; + goto cleanup; + } + + _decode_clear(vf); + } + } + } + + /* Do we need to load a new machine before submitting the page? */ + /* This is different in the seekable and non-seekable cases. + + In the seekable case, we already have all the header + information loaded and cached; we just initialize the machine + with it and continue on our merry way. + + In the non-seekable (streaming) case, we'll only be at a + boundary if we just left the previous logical bitstream and + we're now nominally at the header of the next bitstream + */ + + if(vf->ready_state!=INITSET){ + int link,ret; + + if(vf->ready_stateseekable){ + vf->current_serialno=ogg_page_serialno(&og); + + /* match the serialno to bitstream section. We use this rather than + offset positions to avoid problems near logical bitstream + boundaries */ + for(link=0;linklinks;link++) + if(vf->serialnos[link]==vf->current_serialno)break; + if(link==vf->links){ + ret=OV_EBADLINK; /* sign of a bogus stream. error out, + leave machine uninitialized */ + goto cleanup; + } + + vf->current_link=link; + ret=_fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,&og); + if(ret) goto cleanup; + + }else{ + /* we're streaming */ + /* fetch the three header packets, build the info struct */ + + int ret=_fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,&og); + if(ret) goto cleanup; + vf->current_link++; + } + } + + if(_make_decode_ready(vf)) return OV_EBADLINK; + } + ogg_stream_pagein(vf->os,&og); + } + cleanup: + ogg_packet_release(&op); + ogg_page_release(&og); + return ret; +} + +/* if, eg, 64 bit stdio is configured by default, this will build with + fseek64 */ +static int _fseek64_wrap(FILE *f,ogg_int64_t off,int whence){ + if(f==NULL)return -1; + return fseek(f,off,whence); +} + +static int _ov_open1(void *f,OggVorbis_File *vf,char *initial, + long ibytes, ov_callbacks callbacks){ + int offsettest=(f?callbacks.seek_func(f,0,SEEK_CUR):-1); + int ret; + + memset(vf,0,sizeof(*vf)); + + /* Tremor assumes in multiple places that right shift of a signed + integer is an arithmetic shift */ + if( (-1>>1) != -1) return OV_EIMPL; + + vf->datasource=f; + vf->callbacks = callbacks; + + /* init the framing state */ + vf->oy=ogg_sync_create(); + + /* perhaps some data was previously read into a buffer for testing + against other stream types. Allow initialization from this + previously read data (as we may be reading from a non-seekable + stream) */ + if(initial){ + unsigned char *buffer=ogg_sync_bufferin(vf->oy,ibytes); + memcpy(buffer,initial,ibytes); + ogg_sync_wrote(vf->oy,ibytes); + } + + /* can we seek? Stevens suggests the seek test was portable */ + if(offsettest!=-1)vf->seekable=1; + + /* No seeking yet; Set up a 'single' (current) logical bitstream + entry for partial open */ + vf->links=1; + vf->os=ogg_stream_create(-1); /* fill in the serialno later */ + + /* Try to fetch the headers, maintaining all the storage */ + if((ret=_fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,NULL))<0){ + vf->datasource=NULL; + ov_clear(vf); + }else if(vf->ready_state < PARTOPEN) + vf->ready_state=PARTOPEN; + return ret; +} + +static int _ov_open2(OggVorbis_File *vf){ + if(vf->ready_state < OPENED) + vf->ready_state=OPENED; + if(vf->seekable){ + int ret=_open_seekable2(vf); + if(ret){ + vf->datasource=NULL; + ov_clear(vf); + } + return ret; + } + return 0; +} + + +/* clear out the OggVorbis_File struct */ +int ov_clear(OggVorbis_File *vf){ + if(vf){ + vorbis_dsp_destroy(vf->vd); + vf->vd=0; + ogg_stream_destroy(vf->os); + vorbis_info_clear(&vf->vi); + vorbis_comment_clear(&vf->vc); + if(vf->dataoffsets)_ogg_free(vf->dataoffsets); + if(vf->pcmlengths)_ogg_free(vf->pcmlengths); + if(vf->serialnos)_ogg_free(vf->serialnos); + if(vf->offsets)_ogg_free(vf->offsets); + ogg_sync_destroy(vf->oy); + + if(vf->datasource)(vf->callbacks.close_func)(vf->datasource); + memset(vf,0,sizeof(*vf)); + } +#ifdef DEBUG_LEAKS + _VDBG_dump(); +#endif + return 0; +} + +/* inspects the OggVorbis file and finds/documents all the logical + bitstreams contained in it. Tries to be tolerant of logical + bitstream sections that are truncated/woogie. + + return: -1) error + 0) OK +*/ + +int ov_open_callbacks(void *f,OggVorbis_File *vf,char *initial,long ibytes, + ov_callbacks callbacks){ + int ret=_ov_open1(f,vf,initial,ibytes,callbacks); + if(ret)return ret; + return _ov_open2(vf); +} + +int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes){ + ov_callbacks callbacks = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) _fseek64_wrap, + (int (*)(void *)) fclose, + (long (*)(void *)) ftell + }; + + return ov_open_callbacks((void *)f, vf, initial, ibytes, callbacks); +} + +/* Only partially open the vorbis file; test for Vorbisness, and load + the headers for the first chain. Do not seek (although test for + seekability). Use ov_test_open to finish opening the file, else + ov_clear to close/free it. Same return codes as open. */ + +int ov_test_callbacks(void *f,OggVorbis_File *vf,char *initial,long ibytes, + ov_callbacks callbacks) +{ + return _ov_open1(f,vf,initial,ibytes,callbacks); +} + +int ov_test(FILE *f,OggVorbis_File *vf,char *initial,long ibytes){ + ov_callbacks callbacks = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) _fseek64_wrap, + (int (*)(void *)) fclose, + (long (*)(void *)) ftell + }; + + return ov_test_callbacks((void *)f, vf, initial, ibytes, callbacks); +} + +int ov_test_open(OggVorbis_File *vf){ + if(vf->ready_state!=PARTOPEN)return OV_EINVAL; + return _ov_open2(vf); +} + +/* How many logical bitstreams in this physical bitstream? */ +long ov_streams(OggVorbis_File *vf){ + return vf->links; +} + +/* Is the FILE * associated with vf seekable? */ +long ov_seekable(OggVorbis_File *vf){ + return vf->seekable; +} + +/* returns the bitrate for a given logical bitstream or the entire + physical bitstream. If the file is open for random access, it will + find the *actual* average bitrate. If the file is streaming, it + returns the nominal bitrate (if set) else the average of the + upper/lower bounds (if set) else -1 (unset). + + If you want the actual bitrate field settings, get them from the + vorbis_info structs */ + +long ov_bitrate(OggVorbis_File *vf,int i){ + if(vf->ready_state=vf->links)return OV_EINVAL; + if(!vf->seekable && i!=0)return ov_bitrate(vf,0); + if(i<0){ + ogg_int64_t bits=0; + int i; + for(i=0;ilinks;i++) + bits+=(vf->offsets[i+1]-vf->dataoffsets[i])*8; + /* This once read: return(rint(bits/ov_time_total(vf,-1))); + * gcc 3.x on x86 miscompiled this at optimisation level 2 and above, + * so this is slightly transformed to make it work. + */ + return bits*1000/ov_time_total(vf,-1); + }else{ + if(vf->seekable){ + /* return the actual bitrate */ + return (vf->offsets[i+1]-vf->dataoffsets[i])*8000/ov_time_total(vf,i); + }else{ + /* return nominal if set */ + if(vf->vi.bitrate_nominal>0){ + return vf->vi.bitrate_nominal; + }else{ + if(vf->vi.bitrate_upper>0){ + if(vf->vi.bitrate_lower>0){ + return (vf->vi.bitrate_upper+vf->vi.bitrate_lower)/2; + }else{ + return vf->vi.bitrate_upper; + } + } + return OV_FALSE; + } + } + } +} + +/* returns the actual bitrate since last call. returns -1 if no + additional data to offer since last call (or at beginning of stream), + EINVAL if stream is only partially open +*/ +long ov_bitrate_instant(OggVorbis_File *vf){ + long ret; + if(vf->ready_statesamptrack==0)return OV_FALSE; + ret=vf->bittrack/vf->samptrack*vf->vi.rate; + vf->bittrack=0; + vf->samptrack=0; + return ret; +} + +/* Guess */ +long ov_serialnumber(OggVorbis_File *vf,int i){ + if(i>=vf->links)return ov_serialnumber(vf,vf->links-1); + if(!vf->seekable && i>=0)return ov_serialnumber(vf,-1); + if(i<0){ + return vf->current_serialno; + }else{ + return vf->serialnos[i]; + } +} + +/* returns: total raw (compressed) length of content if i==-1 + raw (compressed) length of that logical bitstream for i==0 to n + OV_EINVAL if the stream is not seekable (we can't know the length) + or if stream is only partially open +*/ +ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i){ + if(vf->ready_stateseekable || i>=vf->links)return OV_EINVAL; + if(i<0){ + ogg_int64_t acc=0; + int i; + for(i=0;ilinks;i++) + acc+=ov_raw_total(vf,i); + return acc; + }else{ + return vf->offsets[i+1]-vf->offsets[i]; + } +} + +/* returns: total PCM length (samples) of content if i==-1 PCM length + (samples) of that logical bitstream for i==0 to n + OV_EINVAL if the stream is not seekable (we can't know the + length) or only partially open +*/ +ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i){ + if(vf->ready_stateseekable || i>=vf->links)return OV_EINVAL; + if(i<0){ + ogg_int64_t acc=0; + int i; + for(i=0;ilinks;i++) + acc+=ov_pcm_total(vf,i); + return acc; + }else{ + return vf->pcmlengths[i*2+1]; + } +} + +/* returns: total milliseconds of content if i==-1 + milliseconds in that logical bitstream for i==0 to n + OV_EINVAL if the stream is not seekable (we can't know the + length) or only partially open +*/ +ogg_int64_t ov_time_total(OggVorbis_File *vf,int i){ + if(vf->ready_stateseekable || i>=vf->links)return OV_EINVAL; + if(i<0){ + ogg_int64_t acc=0; + int i; + for(i=0;ilinks;i++) + acc+=ov_time_total(vf,i); + return acc; + }else{ + return ((ogg_int64_t)vf->pcmlengths[i*2+1])*1000/vf->vi.rate; + } +} + +/* seek to an offset relative to the *compressed* data. This also + scans packets to update the PCM cursor. It will cross a logical + bitstream boundary, but only if it can't get any packets out of the + tail of the bitstream we seek to (so no surprises). + + returns zero on success, nonzero on failure */ + +int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ + ogg_stream_state *work_os=NULL; + ogg_page og={0,0,0,0}; + ogg_packet op={0,0,0,0,0,0}; + + if(vf->ready_stateseekable) + return OV_ENOSEEK; /* don't dump machine if we can't seek */ + + if(pos<0 || pos>vf->end)return OV_EINVAL; + + /* don't yet clear out decoding machine (if it's initialized), in + the case we're in the same link. Restart the decode lapping, and + let _fetch_and_process_packet deal with a potential bitstream + boundary */ + vf->pcm_offset=-1; + ogg_stream_reset_serialno(vf->os, + vf->current_serialno); /* must set serialno */ + vorbis_dsp_restart(vf->vd); + + _seek_helper(vf,pos); + + /* we need to make sure the pcm_offset is set, but we don't want to + advance the raw cursor past good packets just to get to the first + with a granulepos. That's not equivalent behavior to beginning + decoding as immediately after the seek position as possible. + + So, a hack. We use two stream states; a local scratch state and + the shared vf->os stream state. We use the local state to + scan, and the shared state as a buffer for later decode. + + Unfortuantely, on the last page we still advance to last packet + because the granulepos on the last page is not necessarily on a + packet boundary, and we need to make sure the granpos is + correct. + */ + + { + int lastblock=0; + int accblock=0; + int thisblock; + int eosflag; + + work_os=ogg_stream_create(vf->current_serialno); /* get the memory ready */ + while(1){ + if(vf->ready_state>=STREAMSET){ + /* snarf/scan a packet if we can */ + int result=ogg_stream_packetout(work_os,&op); + + if(result>0){ + + if(vf->vi.codec_setup){ + thisblock=vorbis_packet_blocksize(&vf->vi,&op); + if(thisblock<0){ + ogg_stream_packetout(vf->os,NULL); + thisblock=0; + }else{ + + if(eosflag) + ogg_stream_packetout(vf->os,NULL); + else + if(lastblock)accblock+=(lastblock+thisblock)>>2; + } + + if(op.granulepos!=-1){ + int i,link=vf->current_link; + ogg_int64_t granulepos=op.granulepos-vf->pcmlengths[link*2]; + if(granulepos<0)granulepos=0; + + for(i=0;ipcmlengths[i*2+1]; + vf->pcm_offset=granulepos-accblock; + break; + } + lastblock=thisblock; + continue; + }else + ogg_stream_packetout(vf->os,NULL); + } + } + + if(!lastblock){ + if(_get_next_page(vf,&og,-1)<0){ + vf->pcm_offset=ov_pcm_total(vf,-1); + break; + } + }else{ + /* huh? Bogus stream with packets but no granulepos */ + vf->pcm_offset=-1; + break; + } + + /* did we just grab a page from other than current link? */ + if(vf->ready_state>=STREAMSET) + if(vf->current_serialno!=ogg_page_serialno(&og)){ + _decode_clear(vf); /* clear out stream state */ + ogg_stream_destroy(work_os); + } + + if(vf->ready_statecurrent_serialno=ogg_page_serialno(&og); + for(link=0;linklinks;link++) + if(vf->serialnos[link]==vf->current_serialno)break; + if(link==vf->links) + goto seek_error; /* sign of a bogus stream. error out, + leave machine uninitialized */ + + /* need to initialize machine to this link */ + { + int ret=_set_link_number_preserve_pos(vf,link); + if(ret) goto seek_error; + } + ogg_stream_reset_serialno(vf->os,vf->current_serialno); + ogg_stream_reset_serialno(work_os,vf->current_serialno); + + + } + + { + ogg_page dup; + ogg_page_dup(&dup,&og); + eosflag=ogg_page_eos(&og); + ogg_stream_pagein(vf->os,&og); + ogg_stream_pagein(work_os,&dup); + } + } + } + + ogg_packet_release(&op); + ogg_page_release(&og); + ogg_stream_destroy(work_os); + vf->bittrack=0; + vf->samptrack=0; + return 0; + + seek_error: + ogg_packet_release(&op); + ogg_page_release(&og); + + /* dump the machine so we're in a known state */ + vf->pcm_offset=-1; + ogg_stream_destroy(work_os); + _decode_clear(vf); + return OV_EBADLINK; +} + +/* Page granularity seek (faster than sample granularity because we + don't do the last bit of decode to find a specific sample). + + Seek to the last [granule marked] page preceeding the specified pos + location, such that decoding past the returned point will quickly + arrive at the requested position. */ +int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ + int link=-1; + ogg_int64_t result=0; + ogg_int64_t total=ov_pcm_total(vf,-1); + ogg_page og={0,0,0,0}; + ogg_packet op={0,0,0,0,0,0}; + + if(vf->ready_stateseekable)return OV_ENOSEEK; + if(pos<0 || pos>total)return OV_EINVAL; + + /* which bitstream section does this pcm offset occur in? */ + for(link=vf->links-1;link>=0;link--){ + total-=vf->pcmlengths[link*2+1]; + if(pos>=total)break; + } + + + if(link!=vf->current_link){ + int ret=_set_link_number(vf,link); + if(ret) goto seek_error; + }else{ + vorbis_dsp_restart(vf->vd); + } + + ogg_stream_reset_serialno(vf->os,vf->serialnos[link]); + + /* search within the logical bitstream for the page with the highest + pcm_pos preceeding (or equal to) pos. There is a danger here; + missing pages or incorrect frame number information in the + bitstream could make our task impossible. Account for that (it + would be an error condition) */ + + /* new search algorithm by HB (Nicholas Vinen) */ + { + ogg_int64_t end=vf->offsets[link+1]; + ogg_int64_t begin=vf->offsets[link]; + ogg_int64_t begintime = vf->pcmlengths[link*2]; + ogg_int64_t endtime = vf->pcmlengths[link*2+1]+begintime; + ogg_int64_t target=pos-total+begintime; + ogg_int64_t best=begin; + + while(beginoffset); + if(result==OV_EREAD) goto seek_error; + if(result<0){ + if(bisect<=begin+1) + end=begin; /* found it */ + else{ + if(bisect==0) goto seek_error; + bisect-=CHUNKSIZE; + if(bisect<=begin)bisect=begin+1; + _seek_helper(vf,bisect); + } + }else{ + ogg_int64_t granulepos=ogg_page_granulepos(&og); + if(granulepos==-1)continue; + if(granuleposoffset; /* raw offset of next page */ + begintime=granulepos; + + if(target-begintime>44100)break; + bisect=begin; /* *not* begin + 1 */ + }else{ + if(bisect<=begin+1) + end=begin; /* found it */ + else{ + if(end==vf->offset){ /* we're pretty close - we'd be stuck in */ + end=result; + bisect-=CHUNKSIZE; /* an endless loop otherwise. */ + if(bisect<=begin)bisect=begin+1; + _seek_helper(vf,bisect); + }else{ + end=result; + endtime=granulepos; + break; + } + } + } + } + } + } + + /* found our page. seek to it, update pcm offset. Easier case than + raw_seek, don't keep packets preceeding granulepos. */ + { + + /* seek */ + _seek_helper(vf,best); + vf->pcm_offset=-1; + + if(_get_next_page(vf,&og,-1)<0){ + ogg_page_release(&og); + return OV_EOF; /* shouldn't happen */ + } + + ogg_stream_pagein(vf->os,&og); + + /* pull out all but last packet; the one with granulepos */ + while(1){ + result=ogg_stream_packetpeek(vf->os,&op); + if(result==0){ + /* !!! the packet finishing this page originated on a + preceeding page. Keep fetching previous pages until we + get one with a granulepos or without the 'continued' flag + set. Then just use raw_seek for simplicity. */ + + _seek_helper(vf,best); + + while(1){ + result=_get_prev_page(vf,&og); + if(result<0) goto seek_error; + if(ogg_page_granulepos(&og)>-1 || + !ogg_page_continued(&og)){ + return ov_raw_seek(vf,result); + } + vf->offset=result; + } + } + if(result<0){ + result = OV_EBADPACKET; + goto seek_error; + } + if(op.granulepos!=-1){ + vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2]; + if(vf->pcm_offset<0)vf->pcm_offset=0; + vf->pcm_offset+=total; + break; + }else + result=ogg_stream_packetout(vf->os,NULL); + } + } + } + + /* verify result */ + if(vf->pcm_offset>pos || pos>ov_pcm_total(vf,-1)){ + result=OV_EFAULT; + goto seek_error; + } + vf->bittrack=0; + vf->samptrack=0; + + ogg_page_release(&og); + ogg_packet_release(&op); + return 0; + + seek_error: + + ogg_page_release(&og); + ogg_packet_release(&op); + + /* dump machine so we're in a known state */ + vf->pcm_offset=-1; + _decode_clear(vf); + return (int)result; +} + +/* seek to a sample offset relative to the decompressed pcm stream + returns zero on success, nonzero on failure */ + +int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){ + ogg_packet op={0,0,0,0,0,0}; + ogg_page og={0,0,0,0}; + int thisblock,lastblock=0; + int ret=ov_pcm_seek_page(vf,pos); + if(ret<0)return ret; + if(_make_decode_ready(vf))return OV_EBADLINK; + + /* discard leading packets we don't need for the lapping of the + position we want; don't decode them */ + + while(1){ + + int ret=ogg_stream_packetpeek(vf->os,&op); + if(ret>0){ + thisblock=vorbis_packet_blocksize(&vf->vi,&op); + if(thisblock<0){ + ogg_stream_packetout(vf->os,NULL); + continue; /* non audio packet */ + } + if(lastblock)vf->pcm_offset+=(lastblock+thisblock)>>2; + + if(vf->pcm_offset+((thisblock+ + vorbis_info_blocksize(&vf->vi,1))>>2)>=pos)break; + + /* remove the packet from packet queue and track its granulepos */ + ogg_stream_packetout(vf->os,NULL); + vorbis_dsp_synthesis(vf->vd,&op,0); /* set up a vb with + only tracking, no + pcm_decode */ + + /* end of logical stream case is hard, especially with exact + length positioning. */ + + if(op.granulepos>-1){ + int i; + /* always believe the stream markers */ + vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2]; + if(vf->pcm_offset<0)vf->pcm_offset=0; + for(i=0;icurrent_link;i++) + vf->pcm_offset+=vf->pcmlengths[i*2+1]; + } + + lastblock=thisblock; + + }else{ + if(ret<0 && ret!=OV_HOLE)break; + + /* suck in a new page */ + if(_get_next_page(vf,&og,-1)<0)break; + if(vf->current_serialno!=ogg_page_serialno(&og))_decode_clear(vf); + + if(vf->ready_statecurrent_serialno=ogg_page_serialno(&og); + for(link=0;linklinks;link++) + if(vf->serialnos[link]==vf->current_serialno)break; + if(link==vf->links){ + ogg_page_release(&og); + ogg_packet_release(&op); + return OV_EBADLINK; + } + + + vf->current_link=link; + ret=_fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,&og); + if(ret) return ret; + if(_make_decode_ready(vf))return OV_EBADLINK; + lastblock=0; + } + + ogg_stream_pagein(vf->os,&og); + } + } + + vf->bittrack=0; + vf->samptrack=0; + /* discard samples until we reach the desired position. Crossing a + logical bitstream boundary with abandon is OK. */ + while(vf->pcm_offsetpcm_offset; + long samples=vorbis_dsp_pcmout(vf->vd,NULL,0); + + if(samples>target)samples=target; + vorbis_dsp_read(vf->vd,samples); + vf->pcm_offset+=samples; + + if(samplespcm_offset=ov_pcm_total(vf,-1); /* eof */ + } + + ogg_page_release(&og); + ogg_packet_release(&op); + return 0; +} + +/* seek to a playback time relative to the decompressed pcm stream + returns zero on success, nonzero on failure */ +int ov_time_seek(OggVorbis_File *vf,ogg_int64_t milliseconds){ + /* translate time to PCM position and call ov_pcm_seek */ + + int link=-1; + ogg_int64_t pcm_total=ov_pcm_total(vf,-1); + ogg_int64_t time_total=ov_time_total(vf,-1); + + if(vf->ready_stateseekable)return OV_ENOSEEK; + if(milliseconds<0 || milliseconds>time_total)return OV_EINVAL; + + /* which bitstream section does this time offset occur in? */ + for(link=vf->links-1;link>=0;link--){ + pcm_total-=vf->pcmlengths[link*2+1]; + time_total-=ov_time_total(vf,link); + if(milliseconds>=time_total)break; + } + + /* enough information to convert time offset to pcm offset */ + { + int ret=_set_link_number(vf,link); + if(ret)return ret; + return + ov_pcm_seek(vf,pcm_total+(milliseconds-time_total)* + vf->vi.rate/1000); + } +} + +/* page-granularity version of ov_time_seek + returns zero on success, nonzero on failure */ +int ov_time_seek_page(OggVorbis_File *vf,ogg_int64_t milliseconds){ + /* translate time to PCM position and call ov_pcm_seek */ + + int link=-1; + ogg_int64_t pcm_total=ov_pcm_total(vf,-1); + ogg_int64_t time_total=ov_time_total(vf,-1); + + if(vf->ready_stateseekable)return OV_ENOSEEK; + if(milliseconds<0 || milliseconds>time_total)return OV_EINVAL; + + /* which bitstream section does this time offset occur in? */ + for(link=vf->links-1;link>=0;link--){ + pcm_total-=vf->pcmlengths[link*2+1]; + time_total-=ov_time_total(vf,link); + if(milliseconds>=time_total)break; + } + + /* enough information to convert time offset to pcm offset */ + { + int ret=_set_link_number(vf,link); + if(ret)return ret; + return + ov_pcm_seek_page(vf,pcm_total+(milliseconds-time_total)* + vf->vi.rate/1000); + } +} + +/* tell the current stream offset cursor. Note that seek followed by + tell will likely not give the set offset due to caching */ +ogg_int64_t ov_raw_tell(OggVorbis_File *vf){ + if(vf->ready_stateoffset; +} + +/* return PCM offset (sample) of next PCM sample to be read */ +ogg_int64_t ov_pcm_tell(OggVorbis_File *vf){ + if(vf->ready_statepcm_offset; +} + +/* return time offset (milliseconds) of next PCM sample to be read */ +ogg_int64_t ov_time_tell(OggVorbis_File *vf){ + int link=0; + ogg_int64_t pcm_total=0; + ogg_int64_t time_total=0; + + if(vf->ready_stateseekable){ + pcm_total=ov_pcm_total(vf,-1); + time_total=ov_time_total(vf,-1); + + /* which bitstream section does this time offset occur in? */ + for(link=vf->links-1;link>=0;link--){ + pcm_total-=vf->pcmlengths[link*2+1]; + time_total-=ov_time_total(vf,link); + if(vf->pcm_offset>=pcm_total)break; + } + } + + return time_total+(1000*vf->pcm_offset-pcm_total)/vf->vi.rate; +} + +/* link: -1) return the vorbis_info struct for the bitstream section + currently being decoded + 0-n) to request information for a specific bitstream section + + In the case of a non-seekable bitstream, any call returns the + current bitstream. NULL in the case that the machine is not + initialized */ + +vorbis_info *ov_info(OggVorbis_File *vf,int link){ + if(vf->seekable){ + if(link>=vf->links)return NULL; + if(link>=0){ + int ret=_set_link_number_preserve_pos(vf,link); + if(ret)return NULL; + } + } + return &vf->vi; +} + +/* grr, strong typing, grr, no templates/inheritence, grr */ +vorbis_comment *ov_comment(OggVorbis_File *vf,int link){ + if(vf->seekable){ + if(link>=vf->links)return NULL; + if(link>=0){ + int ret=_set_link_number_preserve_pos(vf,link); + if(ret)return NULL; + } + } + return &vf->vc; +} + +/* up to this point, everything could more or less hide the multiple + logical bitstream nature of chaining from the toplevel application + if the toplevel application didn't particularly care. However, at + the point that we actually read audio back, the multiple-section + nature must surface: Multiple bitstream sections do not necessarily + have to have the same number of channels or sampling rate. + + ov_read returns the sequential logical bitstream number currently + being decoded along with the PCM data in order that the toplevel + application can take action on channel/sample rate changes. This + number will be incremented even for streamed (non-seekable) streams + (for seekable streams, it represents the actual logical bitstream + index within the physical bitstream. Note that the accessor + functions above are aware of this dichotomy). + + input values: buffer) a buffer to hold packed PCM data for return + length) the byte length requested to be placed into buffer + + return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL) + 0) EOF + n) number of bytes of PCM actually returned. The + below works on a packet-by-packet basis, so the + return length is not related to the 'length' passed + in, just guaranteed to fit. + + *section) set to the logical bitstream number */ + +long ov_read(OggVorbis_File *vf,void *buffer,int bytes_req,int *bitstream){ + + long samples; + long channels; + + if(vf->ready_stateready_state==INITSET){ + channels=vf->vi.channels; + samples=vorbis_dsp_pcmout(vf->vd,buffer,(bytes_req>>1)/channels); + if(samples){ + if(samples>0){ + vorbis_dsp_read(vf->vd,samples); + vf->pcm_offset+=samples; + if(bitstream)*bitstream=vf->current_link; + return samples*2*channels; + } + return samples; + } + } + + /* suck in another packet */ + { + int ret=_fetch_and_process_packet(vf,1,1); + if(ret==OV_EOF) + return 0; + if(ret<=0) + return ret; + } + + } +} diff --git a/components/spotify/cspot/bell/tremor/vorbisidec.pc.in b/components/spotify/cspot/bell/tremor/vorbisidec.pc.in new file mode 100644 index 00000000..9c095242 --- /dev/null +++ b/components/spotify/cspot/bell/tremor/vorbisidec.pc.in @@ -0,0 +1,14 @@ +# libvorbisidec pkg-config source file + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: vorbisidec +Description: vorbisidec is the integer Ogg Vorbis library +Version: @VERSION@ +Requires: ogg +Conflicts: +Libs: -L${libdir} -lvorbisidec -lm +Cflags: -I${includedir} diff --git a/components/spotify/cspot/bell/tremor/window_lookup.h b/components/spotify/cspot/bell/tremor/window_lookup.h new file mode 100644 index 00000000..1fd171bc --- /dev/null +++ b/components/spotify/cspot/bell/tremor/window_lookup.h @@ -0,0 +1,2087 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: window lookup tables + + ********************************************************************/ + + +#include "os_types.h" + +static LOOKUP_T vwin64[32] = { + X(0x001f0003), X(0x01168c98), X(0x030333c8), X(0x05dfe3a4), + X(0x09a49562), X(0x0e45df18), X(0x13b47ef2), X(0x19dcf676), + X(0x20a74d83), X(0x27f7137c), X(0x2fabb05a), X(0x37a1105a), + X(0x3fb0ab28), X(0x47b2dcd1), X(0x4f807bc6), X(0x56f48e70), + X(0x5dedfc79), X(0x64511653), X(0x6a08cfff), X(0x6f079328), + X(0x734796f4), X(0x76cab7f2), X(0x7999d6e8), X(0x7bc3cf9f), + X(0x7d5c20c1), X(0x7e7961df), X(0x7f33a567), X(0x7fa2e1d0), + X(0x7fdd78a5), X(0x7ff6ec6d), X(0x7ffed0e9), X(0x7ffffc3f), +}; + +static LOOKUP_T vwin128[64] = { + X(0x0007c04d), X(0x0045bb89), X(0x00c18b87), X(0x017ae294), + X(0x02714a4e), X(0x03a4217a), X(0x05129952), X(0x06bbb24f), + X(0x089e38a1), X(0x0ab8c073), X(0x0d09a228), X(0x0f8ef6bd), + X(0x12469488), X(0x152e0c7a), X(0x1842a81c), X(0x1b81686d), + X(0x1ee705d9), X(0x226ff15d), X(0x26185705), X(0x29dc21cc), + X(0x2db700fe), X(0x31a46f08), X(0x359fb9c1), X(0x39a40c0c), + X(0x3dac78b6), X(0x41b40674), X(0x45b5bcb0), X(0x49acb109), + X(0x4d94152b), X(0x516744bd), X(0x5521d320), X(0x58bf98a5), + X(0x5c3cbef4), X(0x5f95cc5d), X(0x62c7add7), X(0x65cfbf64), + X(0x68abd2ba), X(0x6b5a3405), X(0x6dd9acab), X(0x7029840d), + X(0x72497e38), X(0x7439d8ac), X(0x75fb4532), X(0x778ee30a), + X(0x78f6367e), X(0x7a331f1a), X(0x7b47cccd), X(0x7c36b416), + X(0x7d028192), X(0x7dae0d18), X(0x7e3c4caa), X(0x7eb04763), + X(0x7f0d08a7), X(0x7f5593b7), X(0x7f8cd7d5), X(0x7fb5a513), + X(0x7fd2a1fc), X(0x7fe64212), X(0x7ff2bd4c), X(0x7ffa0890), + X(0x7ffdcf39), X(0x7fff6dac), X(0x7fffed01), X(0x7fffffc4), +}; + +static LOOKUP_T vwin256[128] = { + X(0x0001f018), X(0x00117066), X(0x00306e9e), X(0x005ee5f1), + X(0x009ccf26), X(0x00ea208b), X(0x0146cdea), X(0x01b2c87f), + X(0x022dfedf), X(0x02b85ced), X(0x0351cbbd), X(0x03fa317f), + X(0x04b17167), X(0x05776b90), X(0x064bfcdc), X(0x072efedd), + X(0x082047b4), X(0x091fa9f1), X(0x0a2cf477), X(0x0b47f25d), + X(0x0c706ad2), X(0x0da620ff), X(0x0ee8d3ef), X(0x10383e75), + X(0x11941716), X(0x12fc0ff6), X(0x146fd6c8), X(0x15ef14c2), + X(0x17796e8e), X(0x190e844f), X(0x1aadf196), X(0x1c574d6e), + X(0x1e0a2a62), X(0x1fc61688), X(0x218a9b9c), X(0x23573f12), + X(0x252b823d), X(0x2706e269), X(0x28e8d913), X(0x2ad0dc0e), + X(0x2cbe5dc1), X(0x2eb0cd60), X(0x30a79733), X(0x32a224d5), + X(0x349fdd8b), X(0x36a02690), X(0x38a2636f), X(0x3aa5f65e), + X(0x3caa409e), X(0x3eaea2df), X(0x40b27da6), X(0x42b531b8), + X(0x44b62086), X(0x46b4ac99), X(0x48b03a05), X(0x4aa82ed5), + X(0x4c9bf37d), X(0x4e8af349), X(0x50749ccb), X(0x52586246), + X(0x5435ba1c), X(0x560c1f31), X(0x57db1152), X(0x59a21591), + X(0x5b60b6a3), X(0x5d168535), X(0x5ec31839), X(0x60660d36), + X(0x61ff0886), X(0x638db595), X(0x6511c717), X(0x668af734), + X(0x67f907b0), X(0x695bc207), X(0x6ab2f787), X(0x6bfe815a), + X(0x6d3e4090), X(0x6e721e16), X(0x6f9a0ab5), X(0x70b5fef8), + X(0x71c5fb16), X(0x72ca06cd), X(0x73c2313d), X(0x74ae90b2), + X(0x758f4275), X(0x76646a85), X(0x772e335c), X(0x77eccda0), + X(0x78a06fd7), X(0x79495613), X(0x79e7c19c), X(0x7a7bf894), + X(0x7b064596), X(0x7b86f757), X(0x7bfe6044), X(0x7c6cd615), + X(0x7cd2b16e), X(0x7d304d71), X(0x7d860756), X(0x7dd43e06), + X(0x7e1b51ad), X(0x7e5ba355), X(0x7e95947e), X(0x7ec986bb), + X(0x7ef7db4a), X(0x7f20f2b9), X(0x7f452c7f), X(0x7f64e6a7), + X(0x7f807d71), X(0x7f984aff), X(0x7faca700), X(0x7fbde662), + X(0x7fcc5b04), X(0x7fd85372), X(0x7fe21a99), X(0x7fe9f791), + X(0x7ff02d58), X(0x7ff4fa9e), X(0x7ff89990), X(0x7ffb3faa), + X(0x7ffd1d8b), X(0x7ffe5ecc), X(0x7fff29e0), X(0x7fff9ff3), + X(0x7fffdcd2), X(0x7ffff6d6), X(0x7ffffed0), X(0x7ffffffc), +}; + +static LOOKUP_T vwin512[256] = { + X(0x00007c06), X(0x00045c32), X(0x000c1c62), X(0x0017bc4c), + X(0x00273b7a), X(0x003a9955), X(0x0051d51c), X(0x006cede7), + X(0x008be2a9), X(0x00aeb22a), X(0x00d55b0d), X(0x00ffdbcc), + X(0x012e32b6), X(0x01605df5), X(0x01965b85), X(0x01d02939), + X(0x020dc4ba), X(0x024f2b83), X(0x02945ae6), X(0x02dd5004), + X(0x032a07d3), X(0x037a7f19), X(0x03ceb26e), X(0x04269e37), + X(0x04823eab), X(0x04e18fcc), X(0x05448d6d), X(0x05ab3329), + X(0x06157c68), X(0x0683645e), X(0x06f4e607), X(0x0769fc25), + X(0x07e2a146), X(0x085ecfbc), X(0x08de819f), X(0x0961b0cc), + X(0x09e856e3), X(0x0a726d46), X(0x0affed1d), X(0x0b90cf4c), + X(0x0c250c79), X(0x0cbc9d0b), X(0x0d577926), X(0x0df598aa), + X(0x0e96f337), X(0x0f3b8026), X(0x0fe3368f), X(0x108e0d42), + X(0x113bfaca), X(0x11ecf56b), X(0x12a0f324), X(0x1357e9ac), + X(0x1411ce70), X(0x14ce9698), X(0x158e3702), X(0x1650a444), + X(0x1715d2aa), X(0x17ddb638), X(0x18a842aa), X(0x19756b72), + X(0x1a4523b9), X(0x1b175e62), X(0x1bec0e04), X(0x1cc324f0), + X(0x1d9c9532), X(0x1e78508a), X(0x1f564876), X(0x20366e2e), + X(0x2118b2a2), X(0x21fd0681), X(0x22e35a37), X(0x23cb9dee), + X(0x24b5c18e), X(0x25a1b4c0), X(0x268f66f1), X(0x277ec74e), + X(0x286fc4cc), X(0x29624e23), X(0x2a5651d7), X(0x2b4bbe34), + X(0x2c428150), X(0x2d3a8913), X(0x2e33c332), X(0x2f2e1d35), + X(0x30298478), X(0x3125e62d), X(0x32232f61), X(0x33214cfc), + X(0x34202bc2), X(0x351fb85a), X(0x361fdf4f), X(0x37208d10), + X(0x3821adf7), X(0x39232e49), X(0x3a24fa3c), X(0x3b26fdf6), + X(0x3c292593), X(0x3d2b5d29), X(0x3e2d90c8), X(0x3f2fac7f), + X(0x40319c5f), X(0x41334c81), X(0x4234a905), X(0x43359e16), + X(0x443617f3), X(0x453602eb), X(0x46354b65), X(0x4733dde1), + X(0x4831a6ff), X(0x492e937f), X(0x4a2a9045), X(0x4b258a5f), + X(0x4c1f6f06), X(0x4d182ba2), X(0x4e0fadce), X(0x4f05e35b), + X(0x4ffaba53), X(0x50ee20fd), X(0x51e005e1), X(0x52d057ca), + X(0x53bf05ca), X(0x54abff3b), X(0x559733c7), X(0x56809365), + X(0x57680e62), X(0x584d955d), X(0x59311952), X(0x5a128b96), + X(0x5af1dddd), X(0x5bcf023a), X(0x5ca9eb27), X(0x5d828b81), + X(0x5e58d68d), X(0x5f2cbffc), X(0x5ffe3be9), X(0x60cd3edf), + X(0x6199bdda), X(0x6263ae45), X(0x632b0602), X(0x63efbb66), + X(0x64b1c53f), X(0x65711ad0), X(0x662db3d7), X(0x66e7888d), + X(0x679e91a5), X(0x6852c84e), X(0x69042635), X(0x69b2a582), + X(0x6a5e40dd), X(0x6b06f36c), X(0x6bacb8d2), X(0x6c4f8d30), + X(0x6cef6d26), X(0x6d8c55d4), X(0x6e2644d4), X(0x6ebd3840), + X(0x6f512ead), X(0x6fe2272e), X(0x7070214f), X(0x70fb1d17), + X(0x71831b06), X(0x72081c16), X(0x728a21b5), X(0x73092dc8), + X(0x738542a6), X(0x73fe631b), X(0x74749261), X(0x74e7d421), + X(0x75582c72), X(0x75c59fd5), X(0x76303333), X(0x7697ebdd), + X(0x76fccf85), X(0x775ee443), X(0x77be308a), X(0x781abb2e), + X(0x78748b59), X(0x78cba88e), X(0x79201aa7), X(0x7971e9cd), + X(0x79c11e79), X(0x7a0dc170), X(0x7a57dbc2), X(0x7a9f76c1), + X(0x7ae49c07), X(0x7b27556b), X(0x7b67ad02), X(0x7ba5ad1b), + X(0x7be1603a), X(0x7c1ad118), X(0x7c520a9e), X(0x7c8717e1), + X(0x7cba0421), X(0x7ceadac3), X(0x7d19a74f), X(0x7d46756e), + X(0x7d7150e5), X(0x7d9a4592), X(0x7dc15f69), X(0x7de6aa71), + X(0x7e0a32c0), X(0x7e2c0479), X(0x7e4c2bc7), X(0x7e6ab4db), + X(0x7e87abe9), X(0x7ea31d24), X(0x7ebd14be), X(0x7ed59edd), + X(0x7eecc7a3), X(0x7f029b21), X(0x7f17255a), X(0x7f2a723f), + X(0x7f3c8daa), X(0x7f4d835d), X(0x7f5d5f00), X(0x7f6c2c1b), + X(0x7f79f617), X(0x7f86c83a), X(0x7f92ada2), X(0x7f9db146), + X(0x7fa7ddf3), X(0x7fb13e46), X(0x7fb9dcb0), X(0x7fc1c36c), + X(0x7fc8fc83), X(0x7fcf91c7), X(0x7fd58cd2), X(0x7fdaf702), + X(0x7fdfd979), X(0x7fe43d1c), X(0x7fe82a8b), X(0x7febaa29), + X(0x7feec412), X(0x7ff1801c), X(0x7ff3e5d6), X(0x7ff5fc86), + X(0x7ff7cb29), X(0x7ff9586f), X(0x7ffaaaba), X(0x7ffbc81e), + X(0x7ffcb660), X(0x7ffd7af3), X(0x7ffe1afa), X(0x7ffe9b42), + X(0x7fff0047), X(0x7fff4e2f), X(0x7fff88c9), X(0x7fffb390), + X(0x7fffd1a6), X(0x7fffe5d7), X(0x7ffff296), X(0x7ffff9fd), + X(0x7ffffdcd), X(0x7fffff6d), X(0x7fffffed), X(0x7fffffff), +}; + +static LOOKUP_T vwin1024[512] = { + X(0x00001f02), X(0x0001170e), X(0x00030724), X(0x0005ef40), + X(0x0009cf59), X(0x000ea767), X(0x0014775e), X(0x001b3f2e), + X(0x0022fec8), X(0x002bb618), X(0x00356508), X(0x00400b81), + X(0x004ba968), X(0x00583ea0), X(0x0065cb0a), X(0x00744e84), + X(0x0083c8ea), X(0x00943a14), X(0x00a5a1da), X(0x00b80010), + X(0x00cb5488), X(0x00df9f10), X(0x00f4df76), X(0x010b1584), + X(0x01224101), X(0x013a61b2), X(0x01537759), X(0x016d81b6), + X(0x01888087), X(0x01a47385), X(0x01c15a69), X(0x01df34e6), + X(0x01fe02b1), X(0x021dc377), X(0x023e76e7), X(0x02601ca9), + X(0x0282b466), X(0x02a63dc1), X(0x02cab85d), X(0x02f023d6), + X(0x03167fcb), X(0x033dcbd3), X(0x03660783), X(0x038f3270), + X(0x03b94c29), X(0x03e4543a), X(0x04104a2e), X(0x043d2d8b), + X(0x046afdd5), X(0x0499ba8c), X(0x04c9632d), X(0x04f9f734), + X(0x052b7615), X(0x055ddf46), X(0x05913237), X(0x05c56e53), + X(0x05fa9306), X(0x06309fb6), X(0x066793c5), X(0x069f6e93), + X(0x06d82f7c), X(0x0711d5d9), X(0x074c60fe), X(0x0787d03d), + X(0x07c422e4), X(0x0801583e), X(0x083f6f91), X(0x087e681f), + X(0x08be4129), X(0x08fef9ea), X(0x0940919a), X(0x0983076d), + X(0x09c65a92), X(0x0a0a8a38), X(0x0a4f9585), X(0x0a957b9f), + X(0x0adc3ba7), X(0x0b23d4b9), X(0x0b6c45ee), X(0x0bb58e5a), + X(0x0bffad0f), X(0x0c4aa11a), X(0x0c966982), X(0x0ce3054d), + X(0x0d30737b), X(0x0d7eb308), X(0x0dcdc2eb), X(0x0e1da21a), + X(0x0e6e4f83), X(0x0ebfca11), X(0x0f1210ad), X(0x0f652238), + X(0x0fb8fd91), X(0x100da192), X(0x10630d11), X(0x10b93ee0), + X(0x111035cb), X(0x1167f09a), X(0x11c06e13), X(0x1219acf5), + X(0x1273abfb), X(0x12ce69db), X(0x1329e54a), X(0x13861cf3), + X(0x13e30f80), X(0x1440bb97), X(0x149f1fd8), X(0x14fe3ade), + X(0x155e0b40), X(0x15be8f92), X(0x161fc662), X(0x1681ae38), + X(0x16e4459b), X(0x17478b0b), X(0x17ab7d03), X(0x181019fb), + X(0x18756067), X(0x18db4eb3), X(0x1941e34a), X(0x19a91c92), + X(0x1a10f8ea), X(0x1a7976af), X(0x1ae29439), X(0x1b4c4fda), + X(0x1bb6a7e2), X(0x1c219a9a), X(0x1c8d2649), X(0x1cf9492e), + X(0x1d660188), X(0x1dd34d8e), X(0x1e412b74), X(0x1eaf996a), + X(0x1f1e959b), X(0x1f8e1e2f), X(0x1ffe3146), X(0x206ecd01), + X(0x20dfef78), X(0x215196c2), X(0x21c3c0f0), X(0x22366c10), + X(0x22a9962a), X(0x231d3d45), X(0x23915f60), X(0x2405fa7a), + X(0x247b0c8c), X(0x24f09389), X(0x25668d65), X(0x25dcf80c), + X(0x2653d167), X(0x26cb175e), X(0x2742c7d0), X(0x27bae09e), + X(0x28335fa2), X(0x28ac42b3), X(0x292587a5), X(0x299f2c48), + X(0x2a192e69), X(0x2a938bd1), X(0x2b0e4247), X(0x2b894f8d), + X(0x2c04b164), X(0x2c806588), X(0x2cfc69b2), X(0x2d78bb9a), + X(0x2df558f4), X(0x2e723f6f), X(0x2eef6cbb), X(0x2f6cde83), + X(0x2fea9270), X(0x30688627), X(0x30e6b74e), X(0x31652385), + X(0x31e3c86b), X(0x3262a39e), X(0x32e1b2b8), X(0x3360f352), + X(0x33e06303), X(0x345fff5e), X(0x34dfc5f8), X(0x355fb462), + X(0x35dfc82a), X(0x365ffee0), X(0x36e0560f), X(0x3760cb43), + X(0x37e15c05), X(0x386205df), X(0x38e2c657), X(0x39639af5), + X(0x39e4813e), X(0x3a6576b6), X(0x3ae678e3), X(0x3b678547), + X(0x3be89965), X(0x3c69b2c1), X(0x3ceacedc), X(0x3d6beb37), + X(0x3ded0557), X(0x3e6e1abb), X(0x3eef28e6), X(0x3f702d5a), + X(0x3ff1259a), X(0x40720f29), X(0x40f2e789), X(0x4173ac3f), + X(0x41f45ad0), X(0x4274f0c2), X(0x42f56b9a), X(0x4375c8e0), + X(0x43f6061d), X(0x447620db), X(0x44f616a5), X(0x4575e509), + X(0x45f58994), X(0x467501d6), X(0x46f44b62), X(0x477363cb), + X(0x47f248a6), X(0x4870f78e), X(0x48ef6e1a), X(0x496da9e8), + X(0x49eba897), X(0x4a6967c8), X(0x4ae6e521), X(0x4b641e47), + X(0x4be110e5), X(0x4c5dbaa7), X(0x4cda193f), X(0x4d562a5f), + X(0x4dd1ebbd), X(0x4e4d5b15), X(0x4ec87623), X(0x4f433aa9), + X(0x4fbda66c), X(0x5037b734), X(0x50b16acf), X(0x512abf0e), + X(0x51a3b1c5), X(0x521c40ce), X(0x52946a06), X(0x530c2b50), + X(0x53838292), X(0x53fa6db8), X(0x5470eab3), X(0x54e6f776), + X(0x555c91fc), X(0x55d1b844), X(0x56466851), X(0x56baa02f), + X(0x572e5deb), X(0x57a19f98), X(0x58146352), X(0x5886a737), + X(0x58f8696d), X(0x5969a81c), X(0x59da6177), X(0x5a4a93b4), + X(0x5aba3d0f), X(0x5b295bcb), X(0x5b97ee30), X(0x5c05f28d), + X(0x5c736738), X(0x5ce04a8d), X(0x5d4c9aed), X(0x5db856c1), + X(0x5e237c78), X(0x5e8e0a89), X(0x5ef7ff6f), X(0x5f6159b0), + X(0x5fca17d4), X(0x6032386e), X(0x6099ba15), X(0x61009b69), + X(0x6166db11), X(0x61cc77b9), X(0x62317017), X(0x6295c2e7), + X(0x62f96eec), X(0x635c72f1), X(0x63becdc8), X(0x64207e4b), + X(0x6481835a), X(0x64e1dbde), X(0x654186c8), X(0x65a0830e), + X(0x65fecfb1), X(0x665c6bb7), X(0x66b95630), X(0x67158e30), + X(0x677112d7), X(0x67cbe34b), X(0x6825feb9), X(0x687f6456), + X(0x68d81361), X(0x69300b1e), X(0x69874ada), X(0x69ddd1ea), + X(0x6a339fab), X(0x6a88b382), X(0x6add0cdb), X(0x6b30ab2a), + X(0x6b838dec), X(0x6bd5b4a6), X(0x6c271ee2), X(0x6c77cc36), + X(0x6cc7bc3d), X(0x6d16ee9b), X(0x6d6562fb), X(0x6db31911), + X(0x6e001099), X(0x6e4c4955), X(0x6e97c311), X(0x6ee27d9f), + X(0x6f2c78d9), X(0x6f75b4a2), X(0x6fbe30e4), X(0x7005ed91), + X(0x704ceaa1), X(0x70932816), X(0x70d8a5f8), X(0x711d6457), + X(0x7161634b), X(0x71a4a2f3), X(0x71e72375), X(0x7228e500), + X(0x7269e7c8), X(0x72aa2c0a), X(0x72e9b209), X(0x73287a12), + X(0x73668476), X(0x73a3d18f), X(0x73e061bc), X(0x741c3566), + X(0x74574cfa), X(0x7491a8ee), X(0x74cb49be), X(0x75042fec), + X(0x753c5c03), X(0x7573ce92), X(0x75aa882f), X(0x75e08979), + X(0x7615d313), X(0x764a65a7), X(0x767e41e5), X(0x76b16884), + X(0x76e3da40), X(0x771597dc), X(0x7746a221), X(0x7776f9dd), + X(0x77a69fe6), X(0x77d59514), X(0x7803da49), X(0x7831706a), + X(0x785e5861), X(0x788a9320), X(0x78b6219c), X(0x78e104cf), + X(0x790b3dbb), X(0x7934cd64), X(0x795db4d5), X(0x7985f51d), + X(0x79ad8f50), X(0x79d48486), X(0x79fad5de), X(0x7a208478), + X(0x7a45917b), X(0x7a69fe12), X(0x7a8dcb6c), X(0x7ab0fabb), + X(0x7ad38d36), X(0x7af5841a), X(0x7b16e0a3), X(0x7b37a416), + X(0x7b57cfb8), X(0x7b7764d4), X(0x7b9664b6), X(0x7bb4d0b0), + X(0x7bd2aa14), X(0x7beff23b), X(0x7c0caa7f), X(0x7c28d43c), + X(0x7c4470d2), X(0x7c5f81a5), X(0x7c7a081a), X(0x7c940598), + X(0x7cad7b8b), X(0x7cc66b5e), X(0x7cded680), X(0x7cf6be64), + X(0x7d0e247b), X(0x7d250a3c), X(0x7d3b711c), X(0x7d515a95), + X(0x7d66c822), X(0x7d7bbb3c), X(0x7d903563), X(0x7da43814), + X(0x7db7c4d0), X(0x7dcadd16), X(0x7ddd826a), X(0x7defb64d), + X(0x7e017a44), X(0x7e12cfd3), X(0x7e23b87f), X(0x7e3435cc), + X(0x7e444943), X(0x7e53f467), X(0x7e6338c0), X(0x7e7217d5), + X(0x7e80932b), X(0x7e8eac49), X(0x7e9c64b7), X(0x7ea9bdf8), + X(0x7eb6b994), X(0x7ec35910), X(0x7ecf9def), X(0x7edb89b6), + X(0x7ee71de9), X(0x7ef25c09), X(0x7efd4598), X(0x7f07dc16), + X(0x7f122103), X(0x7f1c15dc), X(0x7f25bc1f), X(0x7f2f1547), + X(0x7f3822cd), X(0x7f40e62b), X(0x7f4960d6), X(0x7f519443), + X(0x7f5981e7), X(0x7f612b31), X(0x7f689191), X(0x7f6fb674), + X(0x7f769b45), X(0x7f7d416c), X(0x7f83aa51), X(0x7f89d757), + X(0x7f8fc9df), X(0x7f958348), X(0x7f9b04ef), X(0x7fa0502e), + X(0x7fa56659), X(0x7faa48c7), X(0x7faef8c7), X(0x7fb377a7), + X(0x7fb7c6b3), X(0x7fbbe732), X(0x7fbfda67), X(0x7fc3a196), + X(0x7fc73dfa), X(0x7fcab0ce), X(0x7fcdfb4a), X(0x7fd11ea0), + X(0x7fd41c00), X(0x7fd6f496), X(0x7fd9a989), X(0x7fdc3bff), + X(0x7fdead17), X(0x7fe0fdee), X(0x7fe32f9d), X(0x7fe54337), + X(0x7fe739ce), X(0x7fe9146c), X(0x7fead41b), X(0x7fec79dd), + X(0x7fee06b2), X(0x7fef7b94), X(0x7ff0d97b), X(0x7ff22158), + X(0x7ff35417), X(0x7ff472a3), X(0x7ff57de0), X(0x7ff676ac), + X(0x7ff75de3), X(0x7ff8345a), X(0x7ff8fae4), X(0x7ff9b24b), + X(0x7ffa5b58), X(0x7ffaf6cd), X(0x7ffb8568), X(0x7ffc07e2), + X(0x7ffc7eed), X(0x7ffceb38), X(0x7ffd4d6d), X(0x7ffda631), + X(0x7ffdf621), X(0x7ffe3dd8), X(0x7ffe7dea), X(0x7ffeb6e7), + X(0x7ffee959), X(0x7fff15c4), X(0x7fff3ca9), X(0x7fff5e80), + X(0x7fff7bc0), X(0x7fff94d6), X(0x7fffaa2d), X(0x7fffbc29), + X(0x7fffcb29), X(0x7fffd786), X(0x7fffe195), X(0x7fffe9a3), + X(0x7fffeffa), X(0x7ffff4dd), X(0x7ffff889), X(0x7ffffb37), + X(0x7ffffd1a), X(0x7ffffe5d), X(0x7fffff29), X(0x7fffffa0), + X(0x7fffffdd), X(0x7ffffff7), X(0x7fffffff), X(0x7fffffff), +}; + +static LOOKUP_T vwin2048[1024] = { + X(0x000007c0), X(0x000045c4), X(0x0000c1ca), X(0x00017bd3), + X(0x000273de), X(0x0003a9eb), X(0x00051df9), X(0x0006d007), + X(0x0008c014), X(0x000aee1e), X(0x000d5a25), X(0x00100428), + X(0x0012ec23), X(0x00161216), X(0x001975fe), X(0x001d17da), + X(0x0020f7a8), X(0x00251564), X(0x0029710c), X(0x002e0a9e), + X(0x0032e217), X(0x0037f773), X(0x003d4ab0), X(0x0042dbca), + X(0x0048aabe), X(0x004eb788), X(0x00550224), X(0x005b8a8f), + X(0x006250c5), X(0x006954c1), X(0x0070967e), X(0x007815f9), + X(0x007fd32c), X(0x0087ce13), X(0x009006a9), X(0x00987ce9), + X(0x00a130cc), X(0x00aa224f), X(0x00b3516b), X(0x00bcbe1a), + X(0x00c66856), X(0x00d0501a), X(0x00da755f), X(0x00e4d81f), + X(0x00ef7853), X(0x00fa55f4), X(0x010570fc), X(0x0110c963), + X(0x011c5f22), X(0x01283232), X(0x0134428c), X(0x01409027), + X(0x014d1afb), X(0x0159e302), X(0x0166e831), X(0x01742a82), + X(0x0181a9ec), X(0x018f6665), X(0x019d5fe5), X(0x01ab9663), + X(0x01ba09d6), X(0x01c8ba34), X(0x01d7a775), X(0x01e6d18d), + X(0x01f63873), X(0x0205dc1e), X(0x0215bc82), X(0x0225d997), + X(0x02363350), X(0x0246c9a3), X(0x02579c86), X(0x0268abed), + X(0x0279f7cc), X(0x028b801a), X(0x029d44c9), X(0x02af45ce), + X(0x02c1831d), X(0x02d3fcaa), X(0x02e6b269), X(0x02f9a44c), + X(0x030cd248), X(0x03203c4f), X(0x0333e255), X(0x0347c44b), + X(0x035be225), X(0x03703bd5), X(0x0384d14d), X(0x0399a280), + X(0x03aeaf5e), X(0x03c3f7d9), X(0x03d97be4), X(0x03ef3b6e), + X(0x0405366a), X(0x041b6cc8), X(0x0431de78), X(0x04488b6c), + X(0x045f7393), X(0x047696dd), X(0x048df53b), X(0x04a58e9b), + X(0x04bd62ee), X(0x04d57223), X(0x04edbc28), X(0x050640ed), + X(0x051f0060), X(0x0537fa70), X(0x05512f0a), X(0x056a9e1e), + X(0x05844798), X(0x059e2b67), X(0x05b84978), X(0x05d2a1b8), + X(0x05ed3414), X(0x06080079), X(0x062306d3), X(0x063e470f), + X(0x0659c119), X(0x067574dd), X(0x06916247), X(0x06ad8941), + X(0x06c9e9b8), X(0x06e68397), X(0x070356c8), X(0x07206336), + X(0x073da8cb), X(0x075b2772), X(0x0778df15), X(0x0796cf9c), + X(0x07b4f8f3), X(0x07d35b01), X(0x07f1f5b1), X(0x0810c8eb), + X(0x082fd497), X(0x084f189e), X(0x086e94e9), X(0x088e495e), + X(0x08ae35e6), X(0x08ce5a68), X(0x08eeb6cc), X(0x090f4af8), + X(0x093016d3), X(0x09511a44), X(0x09725530), X(0x0993c77f), + X(0x09b57115), X(0x09d751d8), X(0x09f969ae), X(0x0a1bb87c), + X(0x0a3e3e26), X(0x0a60fa91), X(0x0a83eda2), X(0x0aa7173c), + X(0x0aca7743), X(0x0aee0d9b), X(0x0b11da28), X(0x0b35dccc), + X(0x0b5a156a), X(0x0b7e83e5), X(0x0ba3281f), X(0x0bc801fa), + X(0x0bed1159), X(0x0c12561c), X(0x0c37d025), X(0x0c5d7f55), + X(0x0c83638d), X(0x0ca97cae), X(0x0ccfca97), X(0x0cf64d2a), + X(0x0d1d0444), X(0x0d43efc7), X(0x0d6b0f92), X(0x0d926383), + X(0x0db9eb79), X(0x0de1a752), X(0x0e0996ee), X(0x0e31ba29), + X(0x0e5a10e2), X(0x0e829af6), X(0x0eab5841), X(0x0ed448a2), + X(0x0efd6bf4), X(0x0f26c214), X(0x0f504ade), X(0x0f7a062e), + X(0x0fa3f3df), X(0x0fce13cd), X(0x0ff865d2), X(0x1022e9ca), + X(0x104d9f8e), X(0x107886f9), X(0x10a39fe5), X(0x10ceea2c), + X(0x10fa65a6), X(0x1126122d), X(0x1151ef9a), X(0x117dfdc5), + X(0x11aa3c87), X(0x11d6abb6), X(0x12034b2c), X(0x12301ac0), + X(0x125d1a48), X(0x128a499b), X(0x12b7a891), X(0x12e536ff), + X(0x1312f4bb), X(0x1340e19c), X(0x136efd75), X(0x139d481e), + X(0x13cbc16a), X(0x13fa692f), X(0x14293f40), X(0x14584371), + X(0x14877597), X(0x14b6d585), X(0x14e6630d), X(0x15161e04), + X(0x1546063b), X(0x15761b85), X(0x15a65db3), X(0x15d6cc99), + X(0x16076806), X(0x16382fcd), X(0x166923bf), X(0x169a43ab), + X(0x16cb8f62), X(0x16fd06b5), X(0x172ea973), X(0x1760776b), + X(0x1792706e), X(0x17c49449), X(0x17f6e2cb), X(0x18295bc3), + X(0x185bfeff), X(0x188ecc4c), X(0x18c1c379), X(0x18f4e452), + X(0x19282ea4), X(0x195ba23c), X(0x198f3ee6), X(0x19c3046e), + X(0x19f6f2a1), X(0x1a2b094a), X(0x1a5f4833), X(0x1a93af28), + X(0x1ac83df3), X(0x1afcf460), X(0x1b31d237), X(0x1b66d744), + X(0x1b9c034e), X(0x1bd15621), X(0x1c06cf84), X(0x1c3c6f40), + X(0x1c72351e), X(0x1ca820e6), X(0x1cde3260), X(0x1d146953), + X(0x1d4ac587), X(0x1d8146c3), X(0x1db7eccd), X(0x1deeb76c), + X(0x1e25a667), X(0x1e5cb982), X(0x1e93f085), X(0x1ecb4b33), + X(0x1f02c953), X(0x1f3a6aaa), X(0x1f722efb), X(0x1faa160b), + X(0x1fe21f9e), X(0x201a4b79), X(0x2052995d), X(0x208b0910), + X(0x20c39a53), X(0x20fc4cea), X(0x21352097), X(0x216e151c), + X(0x21a72a3a), X(0x21e05fb5), X(0x2219b54d), X(0x22532ac3), + X(0x228cbfd8), X(0x22c6744d), X(0x230047e2), X(0x233a3a58), + X(0x23744b6d), X(0x23ae7ae3), X(0x23e8c878), X(0x242333ec), + X(0x245dbcfd), X(0x24986369), X(0x24d326f1), X(0x250e0750), + X(0x25490446), X(0x25841d90), X(0x25bf52ec), X(0x25faa417), + X(0x263610cd), X(0x267198cc), X(0x26ad3bcf), X(0x26e8f994), + X(0x2724d1d6), X(0x2760c451), X(0x279cd0c0), X(0x27d8f6e0), + X(0x2815366a), X(0x28518f1b), X(0x288e00ac), X(0x28ca8ad8), + X(0x29072d5a), X(0x2943e7eb), X(0x2980ba45), X(0x29bda422), + X(0x29faa53c), X(0x2a37bd4a), X(0x2a74ec07), X(0x2ab2312b), + X(0x2aef8c6f), X(0x2b2cfd8b), X(0x2b6a8437), X(0x2ba8202c), + X(0x2be5d120), X(0x2c2396cc), X(0x2c6170e7), X(0x2c9f5f29), + X(0x2cdd6147), X(0x2d1b76fa), X(0x2d599ff7), X(0x2d97dbf5), + X(0x2dd62aab), X(0x2e148bcf), X(0x2e52ff16), X(0x2e918436), + X(0x2ed01ae5), X(0x2f0ec2d9), X(0x2f4d7bc6), X(0x2f8c4562), + X(0x2fcb1f62), X(0x300a097a), X(0x3049035f), X(0x30880cc6), + X(0x30c72563), X(0x31064cea), X(0x3145830f), X(0x3184c786), + X(0x31c41a03), X(0x32037a39), X(0x3242e7dc), X(0x3282629f), + X(0x32c1ea36), X(0x33017e53), X(0x33411ea9), X(0x3380caec), + X(0x33c082ce), X(0x34004602), X(0x34401439), X(0x347fed27), + X(0x34bfd07e), X(0x34ffbdf0), X(0x353fb52e), X(0x357fb5ec), + X(0x35bfbfda), X(0x35ffd2aa), X(0x363fee0f), X(0x368011b9), + X(0x36c03d5a), X(0x370070a4), X(0x3740ab48), X(0x3780ecf7), + X(0x37c13562), X(0x3801843a), X(0x3841d931), X(0x388233f7), + X(0x38c2943d), X(0x3902f9b4), X(0x3943640d), X(0x3983d2f8), + X(0x39c44626), X(0x3a04bd48), X(0x3a45380e), X(0x3a85b62a), + X(0x3ac6374a), X(0x3b06bb20), X(0x3b47415c), X(0x3b87c9ae), + X(0x3bc853c7), X(0x3c08df57), X(0x3c496c0f), X(0x3c89f99f), + X(0x3cca87b6), X(0x3d0b1605), X(0x3d4ba43d), X(0x3d8c320e), + X(0x3dccbf27), X(0x3e0d4b3a), X(0x3e4dd5f6), X(0x3e8e5f0c), + X(0x3ecee62b), X(0x3f0f6b05), X(0x3f4fed49), X(0x3f906ca8), + X(0x3fd0e8d2), X(0x40116177), X(0x4051d648), X(0x409246f6), + X(0x40d2b330), X(0x41131aa7), X(0x41537d0c), X(0x4193da10), + X(0x41d43162), X(0x421482b4), X(0x4254cdb7), X(0x4295121b), + X(0x42d54f91), X(0x431585ca), X(0x4355b477), X(0x4395db49), + X(0x43d5f9f1), X(0x44161021), X(0x44561d8a), X(0x449621dd), + X(0x44d61ccc), X(0x45160e08), X(0x4555f544), X(0x4595d230), + X(0x45d5a47f), X(0x46156be3), X(0x4655280e), X(0x4694d8b2), + X(0x46d47d82), X(0x4714162f), X(0x4753a26d), X(0x479321ef), + X(0x47d29466), X(0x4811f987), X(0x48515104), X(0x48909a91), + X(0x48cfd5e1), X(0x490f02a7), X(0x494e2098), X(0x498d2f66), + X(0x49cc2ec7), X(0x4a0b1e6f), X(0x4a49fe11), X(0x4a88cd62), + X(0x4ac78c18), X(0x4b0639e6), X(0x4b44d683), X(0x4b8361a2), + X(0x4bc1dafa), X(0x4c004241), X(0x4c3e972c), X(0x4c7cd970), + X(0x4cbb08c5), X(0x4cf924e1), X(0x4d372d7a), X(0x4d752247), + X(0x4db30300), X(0x4df0cf5a), X(0x4e2e870f), X(0x4e6c29d6), + X(0x4ea9b766), X(0x4ee72f78), X(0x4f2491c4), X(0x4f61de02), + X(0x4f9f13ec), X(0x4fdc333b), X(0x50193ba8), X(0x50562ced), + X(0x509306c3), X(0x50cfc8e5), X(0x510c730d), X(0x514904f6), + X(0x51857e5a), X(0x51c1def5), X(0x51fe2682), X(0x523a54bc), + X(0x52766961), X(0x52b2642c), X(0x52ee44d9), X(0x532a0b26), + X(0x5365b6d0), X(0x53a14793), X(0x53dcbd2f), X(0x54181760), + X(0x545355e5), X(0x548e787d), X(0x54c97ee6), X(0x550468e1), + X(0x553f362c), X(0x5579e687), X(0x55b479b3), X(0x55eeef70), + X(0x5629477f), X(0x566381a1), X(0x569d9d97), X(0x56d79b24), + X(0x57117a0a), X(0x574b3a0a), X(0x5784dae9), X(0x57be5c69), + X(0x57f7be4d), X(0x5831005a), X(0x586a2254), X(0x58a32400), + X(0x58dc0522), X(0x5914c57f), X(0x594d64de), X(0x5985e305), + X(0x59be3fba), X(0x59f67ac3), X(0x5a2e93e9), X(0x5a668af2), + X(0x5a9e5fa6), X(0x5ad611ce), X(0x5b0da133), X(0x5b450d9d), + X(0x5b7c56d7), X(0x5bb37ca9), X(0x5bea7ede), X(0x5c215d41), + X(0x5c58179d), X(0x5c8eadbe), X(0x5cc51f6f), X(0x5cfb6c7c), + X(0x5d3194b2), X(0x5d6797de), X(0x5d9d75cf), X(0x5dd32e51), + X(0x5e08c132), X(0x5e3e2e43), X(0x5e737551), X(0x5ea8962d), + X(0x5edd90a7), X(0x5f12648e), X(0x5f4711b4), X(0x5f7b97ea), + X(0x5faff702), X(0x5fe42ece), X(0x60183f20), X(0x604c27cc), + X(0x607fe8a6), X(0x60b38180), X(0x60e6f22f), X(0x611a3a89), + X(0x614d5a62), X(0x61805190), X(0x61b31fe9), X(0x61e5c545), + X(0x62184179), X(0x624a945d), X(0x627cbdca), X(0x62aebd98), + X(0x62e0939f), X(0x63123fba), X(0x6343c1c1), X(0x6375198f), + X(0x63a646ff), X(0x63d749ec), X(0x64082232), X(0x6438cfad), + X(0x64695238), X(0x6499a9b3), X(0x64c9d5f9), X(0x64f9d6ea), + X(0x6529ac63), X(0x65595643), X(0x6588d46a), X(0x65b826b8), + X(0x65e74d0e), X(0x6616474b), X(0x66451552), X(0x6673b704), + X(0x66a22c44), X(0x66d074f4), X(0x66fe90f8), X(0x672c8033), + X(0x675a428a), X(0x6787d7e1), X(0x67b5401f), X(0x67e27b27), + X(0x680f88e1), X(0x683c6934), X(0x68691c05), X(0x6895a13e), + X(0x68c1f8c7), X(0x68ee2287), X(0x691a1e68), X(0x6945ec54), + X(0x69718c35), X(0x699cfdf5), X(0x69c8417f), X(0x69f356c0), + X(0x6a1e3da3), X(0x6a48f615), X(0x6a738002), X(0x6a9ddb5a), + X(0x6ac80808), X(0x6af205fd), X(0x6b1bd526), X(0x6b457575), + X(0x6b6ee6d8), X(0x6b982940), X(0x6bc13c9f), X(0x6bea20e5), + X(0x6c12d605), X(0x6c3b5bf1), X(0x6c63b29c), X(0x6c8bd9fb), + X(0x6cb3d200), X(0x6cdb9aa0), X(0x6d0333d0), X(0x6d2a9d86), + X(0x6d51d7b7), X(0x6d78e25a), X(0x6d9fbd67), X(0x6dc668d3), + X(0x6dece498), X(0x6e1330ad), X(0x6e394d0c), X(0x6e5f39ae), + X(0x6e84f68d), X(0x6eaa83a2), X(0x6ecfe0ea), X(0x6ef50e5e), + X(0x6f1a0bfc), X(0x6f3ed9bf), X(0x6f6377a4), X(0x6f87e5a8), + X(0x6fac23c9), X(0x6fd03206), X(0x6ff4105c), X(0x7017becc), + X(0x703b3d54), X(0x705e8bf5), X(0x7081aaaf), X(0x70a49984), + X(0x70c75874), X(0x70e9e783), X(0x710c46b2), X(0x712e7605), + X(0x7150757f), X(0x71724523), X(0x7193e4f6), X(0x71b554fd), + X(0x71d6953e), X(0x71f7a5bd), X(0x72188681), X(0x72393792), + X(0x7259b8f5), X(0x727a0ab2), X(0x729a2cd2), X(0x72ba1f5d), + X(0x72d9e25c), X(0x72f975d8), X(0x7318d9db), X(0x73380e6f), + X(0x735713a0), X(0x7375e978), X(0x73949003), X(0x73b3074c), + X(0x73d14f61), X(0x73ef684f), X(0x740d5222), X(0x742b0ce9), + X(0x744898b1), X(0x7465f589), X(0x74832381), X(0x74a022a8), + X(0x74bcf30e), X(0x74d994c3), X(0x74f607d8), X(0x75124c5f), + X(0x752e6268), X(0x754a4a05), X(0x7566034b), X(0x75818e4a), + X(0x759ceb16), X(0x75b819c4), X(0x75d31a66), X(0x75eded12), + X(0x760891dc), X(0x762308da), X(0x763d5221), X(0x76576dc8), + X(0x76715be4), X(0x768b1c8c), X(0x76a4afd9), X(0x76be15e0), + X(0x76d74ebb), X(0x76f05a82), X(0x7709394d), X(0x7721eb35), + X(0x773a7054), X(0x7752c8c4), X(0x776af49f), X(0x7782f400), + X(0x779ac701), X(0x77b26dbd), X(0x77c9e851), X(0x77e136d8), + X(0x77f8596f), X(0x780f5032), X(0x78261b3f), X(0x783cbab2), + X(0x78532eaa), X(0x78697745), X(0x787f94a0), X(0x789586db), + X(0x78ab4e15), X(0x78c0ea6d), X(0x78d65c03), X(0x78eba2f7), + X(0x7900bf68), X(0x7915b179), X(0x792a7949), X(0x793f16fb), + X(0x79538aaf), X(0x7967d488), X(0x797bf4a8), X(0x798feb31), + X(0x79a3b846), X(0x79b75c0a), X(0x79cad6a1), X(0x79de282e), + X(0x79f150d5), X(0x7a0450bb), X(0x7a172803), X(0x7a29d6d3), + X(0x7a3c5d50), X(0x7a4ebb9f), X(0x7a60f1e6), X(0x7a73004a), + X(0x7a84e6f2), X(0x7a96a604), X(0x7aa83da7), X(0x7ab9ae01), + X(0x7acaf73a), X(0x7adc1979), X(0x7aed14e6), X(0x7afde9a8), + X(0x7b0e97e8), X(0x7b1f1fcd), X(0x7b2f8182), X(0x7b3fbd2d), + X(0x7b4fd2f9), X(0x7b5fc30f), X(0x7b6f8d98), X(0x7b7f32bd), + X(0x7b8eb2a9), X(0x7b9e0d85), X(0x7bad437d), X(0x7bbc54b9), + X(0x7bcb4166), X(0x7bda09ae), X(0x7be8adbc), X(0x7bf72dbc), + X(0x7c0589d8), X(0x7c13c23d), X(0x7c21d716), X(0x7c2fc88f), + X(0x7c3d96d5), X(0x7c4b4214), X(0x7c58ca78), X(0x7c66302d), + X(0x7c737362), X(0x7c809443), X(0x7c8d92fc), X(0x7c9a6fbc), + X(0x7ca72aaf), X(0x7cb3c404), X(0x7cc03be8), X(0x7ccc9288), + X(0x7cd8c814), X(0x7ce4dcb9), X(0x7cf0d0a5), X(0x7cfca406), + X(0x7d08570c), X(0x7d13e9e5), X(0x7d1f5cbf), X(0x7d2aafca), + X(0x7d35e335), X(0x7d40f72e), X(0x7d4bebe4), X(0x7d56c188), + X(0x7d617848), X(0x7d6c1054), X(0x7d7689db), X(0x7d80e50e), + X(0x7d8b221b), X(0x7d954133), X(0x7d9f4286), X(0x7da92643), + X(0x7db2ec9b), X(0x7dbc95bd), X(0x7dc621da), X(0x7dcf9123), + X(0x7dd8e3c6), X(0x7de219f6), X(0x7deb33e2), X(0x7df431ba), + X(0x7dfd13af), X(0x7e05d9f2), X(0x7e0e84b4), X(0x7e171424), + X(0x7e1f8874), X(0x7e27e1d4), X(0x7e302074), X(0x7e384487), + X(0x7e404e3c), X(0x7e483dc4), X(0x7e501350), X(0x7e57cf11), + X(0x7e5f7138), X(0x7e66f9f4), X(0x7e6e6979), X(0x7e75bff5), + X(0x7e7cfd9a), X(0x7e842298), X(0x7e8b2f22), X(0x7e922366), + X(0x7e98ff97), X(0x7e9fc3e4), X(0x7ea6707f), X(0x7ead0598), + X(0x7eb38360), X(0x7eb9ea07), X(0x7ec039bf), X(0x7ec672b7), + X(0x7ecc9521), X(0x7ed2a12c), X(0x7ed8970a), X(0x7ede76ea), + X(0x7ee440fd), X(0x7ee9f573), X(0x7eef947d), X(0x7ef51e4b), + X(0x7efa930d), X(0x7efff2f2), X(0x7f053e2b), X(0x7f0a74e8), + X(0x7f0f9758), X(0x7f14a5ac), X(0x7f19a013), X(0x7f1e86bc), + X(0x7f2359d8), X(0x7f281995), X(0x7f2cc623), X(0x7f315fb1), + X(0x7f35e66e), X(0x7f3a5a8a), X(0x7f3ebc33), X(0x7f430b98), + X(0x7f4748e7), X(0x7f4b7450), X(0x7f4f8e01), X(0x7f539629), + X(0x7f578cf5), X(0x7f5b7293), X(0x7f5f4732), X(0x7f630b00), + X(0x7f66be2b), X(0x7f6a60df), X(0x7f6df34b), X(0x7f71759b), + X(0x7f74e7fe), X(0x7f784aa0), X(0x7f7b9daf), X(0x7f7ee156), + X(0x7f8215c3), X(0x7f853b22), X(0x7f88519f), X(0x7f8b5967), + X(0x7f8e52a6), X(0x7f913d87), X(0x7f941a36), X(0x7f96e8df), + X(0x7f99a9ad), X(0x7f9c5ccb), X(0x7f9f0265), X(0x7fa19aa5), + X(0x7fa425b5), X(0x7fa6a3c1), X(0x7fa914f3), X(0x7fab7974), + X(0x7fadd16f), X(0x7fb01d0d), X(0x7fb25c78), X(0x7fb48fd9), + X(0x7fb6b75a), X(0x7fb8d323), X(0x7fbae35d), X(0x7fbce831), + X(0x7fbee1c7), X(0x7fc0d047), X(0x7fc2b3d9), X(0x7fc48ca5), + X(0x7fc65ad3), X(0x7fc81e88), X(0x7fc9d7ee), X(0x7fcb872a), + X(0x7fcd2c63), X(0x7fcec7bf), X(0x7fd05966), X(0x7fd1e17c), + X(0x7fd36027), X(0x7fd4d58d), X(0x7fd641d3), X(0x7fd7a51e), + X(0x7fd8ff94), X(0x7fda5157), X(0x7fdb9a8e), X(0x7fdcdb5b), + X(0x7fde13e2), X(0x7fdf4448), X(0x7fe06caf), X(0x7fe18d3b), + X(0x7fe2a60e), X(0x7fe3b74b), X(0x7fe4c114), X(0x7fe5c38b), + X(0x7fe6bed2), X(0x7fe7b30a), X(0x7fe8a055), X(0x7fe986d4), + X(0x7fea66a7), X(0x7feb3ff0), X(0x7fec12cd), X(0x7fecdf5f), + X(0x7feda5c5), X(0x7fee6620), X(0x7fef208d), X(0x7fefd52c), + X(0x7ff0841c), X(0x7ff12d7a), X(0x7ff1d164), X(0x7ff26ff9), + X(0x7ff30955), X(0x7ff39d96), X(0x7ff42cd9), X(0x7ff4b739), + X(0x7ff53cd4), X(0x7ff5bdc5), X(0x7ff63a28), X(0x7ff6b217), + X(0x7ff725af), X(0x7ff7950a), X(0x7ff80043), X(0x7ff86773), + X(0x7ff8cab4), X(0x7ff92a21), X(0x7ff985d1), X(0x7ff9dddf), + X(0x7ffa3262), X(0x7ffa8374), X(0x7ffad12c), X(0x7ffb1ba1), + X(0x7ffb62ec), X(0x7ffba723), X(0x7ffbe85c), X(0x7ffc26b0), + X(0x7ffc6233), X(0x7ffc9afb), X(0x7ffcd11e), X(0x7ffd04b1), + X(0x7ffd35c9), X(0x7ffd647b), X(0x7ffd90da), X(0x7ffdbafa), + X(0x7ffde2f0), X(0x7ffe08ce), X(0x7ffe2ca7), X(0x7ffe4e8e), + X(0x7ffe6e95), X(0x7ffe8cce), X(0x7ffea94a), X(0x7ffec41b), + X(0x7ffedd52), X(0x7ffef4ff), X(0x7fff0b33), X(0x7fff1ffd), + X(0x7fff336e), X(0x7fff4593), X(0x7fff567d), X(0x7fff663a), + X(0x7fff74d8), X(0x7fff8265), X(0x7fff8eee), X(0x7fff9a81), + X(0x7fffa52b), X(0x7fffaef8), X(0x7fffb7f5), X(0x7fffc02d), + X(0x7fffc7ab), X(0x7fffce7c), X(0x7fffd4a9), X(0x7fffda3e), + X(0x7fffdf44), X(0x7fffe3c6), X(0x7fffe7cc), X(0x7fffeb60), + X(0x7fffee8a), X(0x7ffff153), X(0x7ffff3c4), X(0x7ffff5e3), + X(0x7ffff7b8), X(0x7ffff94b), X(0x7ffffaa1), X(0x7ffffbc1), + X(0x7ffffcb2), X(0x7ffffd78), X(0x7ffffe19), X(0x7ffffe9a), + X(0x7ffffeff), X(0x7fffff4e), X(0x7fffff89), X(0x7fffffb3), + X(0x7fffffd2), X(0x7fffffe6), X(0x7ffffff3), X(0x7ffffffa), + X(0x7ffffffe), X(0x7fffffff), X(0x7fffffff), X(0x7fffffff), +}; + +static LOOKUP_T vwin4096[2048] = { + X(0x000001f0), X(0x00001171), X(0x00003072), X(0x00005ef5), + X(0x00009cf8), X(0x0000ea7c), X(0x00014780), X(0x0001b405), + X(0x0002300b), X(0x0002bb91), X(0x00035698), X(0x0004011e), + X(0x0004bb25), X(0x000584ac), X(0x00065db3), X(0x0007463a), + X(0x00083e41), X(0x000945c7), X(0x000a5ccc), X(0x000b8350), + X(0x000cb954), X(0x000dfed7), X(0x000f53d8), X(0x0010b857), + X(0x00122c55), X(0x0013afd1), X(0x001542ca), X(0x0016e541), + X(0x00189735), X(0x001a58a7), X(0x001c2995), X(0x001e09ff), + X(0x001ff9e6), X(0x0021f948), X(0x00240826), X(0x00262680), + X(0x00285454), X(0x002a91a3), X(0x002cde6c), X(0x002f3aaf), + X(0x0031a66b), X(0x003421a0), X(0x0036ac4f), X(0x00394675), + X(0x003bf014), X(0x003ea92a), X(0x004171b7), X(0x004449bb), + X(0x00473135), X(0x004a2824), X(0x004d2e8a), X(0x00504463), + X(0x005369b2), X(0x00569e74), X(0x0059e2aa), X(0x005d3652), + X(0x0060996d), X(0x00640bf9), X(0x00678df7), X(0x006b1f66), + X(0x006ec045), X(0x00727093), X(0x00763051), X(0x0079ff7d), + X(0x007dde16), X(0x0081cc1d), X(0x0085c991), X(0x0089d671), + X(0x008df2bc), X(0x00921e71), X(0x00965991), X(0x009aa41a), + X(0x009efe0c), X(0x00a36766), X(0x00a7e028), X(0x00ac6850), + X(0x00b0ffde), X(0x00b5a6d1), X(0x00ba5d28), X(0x00bf22e4), + X(0x00c3f802), X(0x00c8dc83), X(0x00cdd065), X(0x00d2d3a8), + X(0x00d7e64a), X(0x00dd084c), X(0x00e239ac), X(0x00e77a69), + X(0x00ecca83), X(0x00f229f9), X(0x00f798ca), X(0x00fd16f5), + X(0x0102a479), X(0x01084155), X(0x010ded89), X(0x0113a913), + X(0x011973f3), X(0x011f4e27), X(0x012537af), X(0x012b308a), + X(0x013138b7), X(0x01375035), X(0x013d7702), X(0x0143ad1f), + X(0x0149f289), X(0x01504741), X(0x0156ab44), X(0x015d1e92), + X(0x0163a12a), X(0x016a330b), X(0x0170d433), X(0x017784a3), + X(0x017e4458), X(0x01851351), X(0x018bf18e), X(0x0192df0d), + X(0x0199dbcd), X(0x01a0e7cd), X(0x01a8030c), X(0x01af2d89), + X(0x01b66743), X(0x01bdb038), X(0x01c50867), X(0x01cc6fd0), + X(0x01d3e670), X(0x01db6c47), X(0x01e30153), X(0x01eaa593), + X(0x01f25907), X(0x01fa1bac), X(0x0201ed81), X(0x0209ce86), + X(0x0211beb8), X(0x0219be17), X(0x0221cca2), X(0x0229ea56), + X(0x02321733), X(0x023a5337), X(0x02429e60), X(0x024af8af), + X(0x02536220), X(0x025bdab3), X(0x02646267), X(0x026cf93a), + X(0x02759f2a), X(0x027e5436), X(0x0287185d), X(0x028feb9d), + X(0x0298cdf4), X(0x02a1bf62), X(0x02aabfe5), X(0x02b3cf7b), + X(0x02bcee23), X(0x02c61bdb), X(0x02cf58a2), X(0x02d8a475), + X(0x02e1ff55), X(0x02eb693e), X(0x02f4e230), X(0x02fe6a29), + X(0x03080127), X(0x0311a729), X(0x031b5c2d), X(0x03252031), + X(0x032ef334), X(0x0338d534), X(0x0342c630), X(0x034cc625), + X(0x0356d512), X(0x0360f2f6), X(0x036b1fce), X(0x03755b99), + X(0x037fa655), X(0x038a0001), X(0x0394689a), X(0x039ee020), + X(0x03a9668f), X(0x03b3fbe6), X(0x03bea024), X(0x03c95347), + X(0x03d4154d), X(0x03dee633), X(0x03e9c5f9), X(0x03f4b49b), + X(0x03ffb219), X(0x040abe71), X(0x0415d9a0), X(0x042103a5), + X(0x042c3c7d), X(0x04378428), X(0x0442daa2), X(0x044e3fea), + X(0x0459b3fd), X(0x046536db), X(0x0470c880), X(0x047c68eb), + X(0x0488181a), X(0x0493d60b), X(0x049fa2bc), X(0x04ab7e2a), + X(0x04b76854), X(0x04c36137), X(0x04cf68d1), X(0x04db7f21), + X(0x04e7a424), X(0x04f3d7d8), X(0x05001a3b), X(0x050c6b4a), + X(0x0518cb04), X(0x05253966), X(0x0531b66e), X(0x053e421a), + X(0x054adc68), X(0x05578555), X(0x05643cdf), X(0x05710304), + X(0x057dd7c1), X(0x058abb15), X(0x0597acfd), X(0x05a4ad76), + X(0x05b1bc7f), X(0x05beda14), X(0x05cc0635), X(0x05d940dd), + X(0x05e68a0b), X(0x05f3e1bd), X(0x060147f0), X(0x060ebca1), + X(0x061c3fcf), X(0x0629d176), X(0x06377194), X(0x06452027), + X(0x0652dd2c), X(0x0660a8a2), X(0x066e8284), X(0x067c6ad1), + X(0x068a6186), X(0x069866a1), X(0x06a67a1e), X(0x06b49bfc), + X(0x06c2cc38), X(0x06d10acf), X(0x06df57bf), X(0x06edb304), + X(0x06fc1c9d), X(0x070a9487), X(0x07191abe), X(0x0727af40), + X(0x0736520b), X(0x0745031c), X(0x0753c270), X(0x07629004), + X(0x07716bd6), X(0x078055e2), X(0x078f4e26), X(0x079e549f), + X(0x07ad694b), X(0x07bc8c26), X(0x07cbbd2e), X(0x07dafc5f), + X(0x07ea49b7), X(0x07f9a533), X(0x08090ed1), X(0x0818868c), + X(0x08280c62), X(0x0837a051), X(0x08474255), X(0x0856f26b), + X(0x0866b091), X(0x08767cc3), X(0x088656fe), X(0x08963f3f), + X(0x08a63584), X(0x08b639c8), X(0x08c64c0a), X(0x08d66c45), + X(0x08e69a77), X(0x08f6d69d), X(0x090720b3), X(0x091778b7), + X(0x0927dea5), X(0x0938527a), X(0x0948d433), X(0x095963cc), + X(0x096a0143), X(0x097aac94), X(0x098b65bb), X(0x099c2cb6), + X(0x09ad0182), X(0x09bde41a), X(0x09ced47d), X(0x09dfd2a5), + X(0x09f0de90), X(0x0a01f83b), X(0x0a131fa3), X(0x0a2454c3), + X(0x0a359798), X(0x0a46e820), X(0x0a584656), X(0x0a69b237), + X(0x0a7b2bc0), X(0x0a8cb2ec), X(0x0a9e47ba), X(0x0aafea24), + X(0x0ac19a29), X(0x0ad357c3), X(0x0ae522ef), X(0x0af6fbab), + X(0x0b08e1f1), X(0x0b1ad5c0), X(0x0b2cd712), X(0x0b3ee5e5), + X(0x0b510234), X(0x0b632bfd), X(0x0b75633b), X(0x0b87a7eb), + X(0x0b99fa08), X(0x0bac5990), X(0x0bbec67e), X(0x0bd140cf), + X(0x0be3c87e), X(0x0bf65d89), X(0x0c08ffeb), X(0x0c1bafa1), + X(0x0c2e6ca6), X(0x0c4136f6), X(0x0c540e8f), X(0x0c66f36c), + X(0x0c79e588), X(0x0c8ce4e1), X(0x0c9ff172), X(0x0cb30b37), + X(0x0cc6322c), X(0x0cd9664d), X(0x0ceca797), X(0x0cfff605), + X(0x0d135193), X(0x0d26ba3d), X(0x0d3a2fff), X(0x0d4db2d5), + X(0x0d6142ba), X(0x0d74dfac), X(0x0d8889a5), X(0x0d9c40a1), + X(0x0db0049d), X(0x0dc3d593), X(0x0dd7b380), X(0x0deb9e60), + X(0x0dff962f), X(0x0e139ae7), X(0x0e27ac85), X(0x0e3bcb05), + X(0x0e4ff662), X(0x0e642e98), X(0x0e7873a2), X(0x0e8cc57d), + X(0x0ea12423), X(0x0eb58f91), X(0x0eca07c2), X(0x0ede8cb1), + X(0x0ef31e5b), X(0x0f07bcba), X(0x0f1c67cb), X(0x0f311f88), + X(0x0f45e3ee), X(0x0f5ab4f7), X(0x0f6f92a0), X(0x0f847ce3), + X(0x0f9973bc), X(0x0fae7726), X(0x0fc3871e), X(0x0fd8a39d), + X(0x0fedcca1), X(0x10030223), X(0x1018441f), X(0x102d9291), + X(0x1042ed74), X(0x105854c3), X(0x106dc879), X(0x10834892), + X(0x1098d508), X(0x10ae6dd8), X(0x10c412fc), X(0x10d9c46f), + X(0x10ef822d), X(0x11054c30), X(0x111b2274), X(0x113104f5), + X(0x1146f3ac), X(0x115cee95), X(0x1172f5ab), X(0x118908e9), + X(0x119f284a), X(0x11b553ca), X(0x11cb8b62), X(0x11e1cf0f), + X(0x11f81ecb), X(0x120e7a90), X(0x1224e25a), X(0x123b5624), + X(0x1251d5e9), X(0x126861a3), X(0x127ef94e), X(0x12959ce3), + X(0x12ac4c5f), X(0x12c307bb), X(0x12d9cef2), X(0x12f0a200), + X(0x130780df), X(0x131e6b8a), X(0x133561fa), X(0x134c642c), + X(0x1363721a), X(0x137a8bbe), X(0x1391b113), X(0x13a8e214), + X(0x13c01eba), X(0x13d76702), X(0x13eebae5), X(0x14061a5e), + X(0x141d8567), X(0x1434fbfb), X(0x144c7e14), X(0x14640bae), + X(0x147ba4c1), X(0x14934949), X(0x14aaf941), X(0x14c2b4a2), + X(0x14da7b67), X(0x14f24d8a), X(0x150a2b06), X(0x152213d5), + X(0x153a07f1), X(0x15520755), X(0x156a11fb), X(0x158227dd), + X(0x159a48f5), X(0x15b2753d), X(0x15caacb1), X(0x15e2ef49), + X(0x15fb3d01), X(0x161395d2), X(0x162bf9b6), X(0x164468a8), + X(0x165ce2a1), X(0x1675679c), X(0x168df793), X(0x16a69280), + X(0x16bf385c), X(0x16d7e922), X(0x16f0a4cc), X(0x17096b54), + X(0x17223cb4), X(0x173b18e5), X(0x1753ffe2), X(0x176cf1a5), + X(0x1785ee27), X(0x179ef562), X(0x17b80750), X(0x17d123eb), + X(0x17ea4b2d), X(0x18037d10), X(0x181cb98d), X(0x1836009e), + X(0x184f523c), X(0x1868ae63), X(0x1882150a), X(0x189b862c), + X(0x18b501c4), X(0x18ce87c9), X(0x18e81836), X(0x1901b305), + X(0x191b582f), X(0x193507ad), X(0x194ec17a), X(0x1968858f), + X(0x198253e5), X(0x199c2c75), X(0x19b60f3a), X(0x19cffc2d), + X(0x19e9f347), X(0x1a03f482), X(0x1a1dffd7), X(0x1a381540), + X(0x1a5234b5), X(0x1a6c5e31), X(0x1a8691ac), X(0x1aa0cf21), + X(0x1abb1687), X(0x1ad567da), X(0x1aefc311), X(0x1b0a2826), + X(0x1b249712), X(0x1b3f0fd0), X(0x1b599257), X(0x1b741ea1), + X(0x1b8eb4a7), X(0x1ba95462), X(0x1bc3fdcd), X(0x1bdeb0de), + X(0x1bf96d91), X(0x1c1433dd), X(0x1c2f03bc), X(0x1c49dd27), + X(0x1c64c017), X(0x1c7fac85), X(0x1c9aa269), X(0x1cb5a1be), + X(0x1cd0aa7c), X(0x1cebbc9c), X(0x1d06d816), X(0x1d21fce4), + X(0x1d3d2aff), X(0x1d586260), X(0x1d73a2fe), X(0x1d8eecd4), + X(0x1daa3fda), X(0x1dc59c09), X(0x1de1015a), X(0x1dfc6fc5), + X(0x1e17e743), X(0x1e3367cd), X(0x1e4ef15b), X(0x1e6a83e7), + X(0x1e861f6a), X(0x1ea1c3da), X(0x1ebd7133), X(0x1ed9276b), + X(0x1ef4e67c), X(0x1f10ae5e), X(0x1f2c7f0a), X(0x1f485879), + X(0x1f643aa2), X(0x1f80257f), X(0x1f9c1908), X(0x1fb81536), + X(0x1fd41a00), X(0x1ff02761), X(0x200c3d4f), X(0x20285bc3), + X(0x204482b7), X(0x2060b221), X(0x207ce9fb), X(0x20992a3e), + X(0x20b572e0), X(0x20d1c3dc), X(0x20ee1d28), X(0x210a7ebe), + X(0x2126e895), X(0x21435aa6), X(0x215fd4ea), X(0x217c5757), + X(0x2198e1e8), X(0x21b57493), X(0x21d20f51), X(0x21eeb21b), + X(0x220b5ce7), X(0x22280fb0), X(0x2244ca6c), X(0x22618d13), + X(0x227e579f), X(0x229b2a06), X(0x22b80442), X(0x22d4e649), + X(0x22f1d015), X(0x230ec19d), X(0x232bbad9), X(0x2348bbc1), + X(0x2365c44c), X(0x2382d474), X(0x239fec30), X(0x23bd0b78), + X(0x23da3244), X(0x23f7608b), X(0x24149646), X(0x2431d36c), + X(0x244f17f5), X(0x246c63da), X(0x2489b711), X(0x24a71193), + X(0x24c47358), X(0x24e1dc57), X(0x24ff4c88), X(0x251cc3e2), + X(0x253a425e), X(0x2557c7f4), X(0x2575549a), X(0x2592e848), + X(0x25b082f7), X(0x25ce249e), X(0x25ebcd34), X(0x26097cb2), + X(0x2627330e), X(0x2644f040), X(0x2662b441), X(0x26807f07), + X(0x269e5089), X(0x26bc28c1), X(0x26da07a4), X(0x26f7ed2b), + X(0x2715d94d), X(0x2733cc02), X(0x2751c540), X(0x276fc500), + X(0x278dcb39), X(0x27abd7e2), X(0x27c9eaf3), X(0x27e80463), + X(0x28062429), X(0x28244a3e), X(0x28427697), X(0x2860a92d), + X(0x287ee1f7), X(0x289d20eb), X(0x28bb6603), X(0x28d9b134), + X(0x28f80275), X(0x291659c0), X(0x2934b709), X(0x29531a49), + X(0x29718378), X(0x298ff28b), X(0x29ae677b), X(0x29cce23e), + X(0x29eb62cb), X(0x2a09e91b), X(0x2a287523), X(0x2a4706dc), + X(0x2a659e3c), X(0x2a843b39), X(0x2aa2ddcd), X(0x2ac185ec), + X(0x2ae0338f), X(0x2afee6ad), X(0x2b1d9f3c), X(0x2b3c5d33), + X(0x2b5b208b), X(0x2b79e939), X(0x2b98b734), X(0x2bb78a74), + X(0x2bd662ef), X(0x2bf5409d), X(0x2c142374), X(0x2c330b6b), + X(0x2c51f87a), X(0x2c70ea97), X(0x2c8fe1b9), X(0x2caeddd6), + X(0x2ccddee7), X(0x2cece4e1), X(0x2d0befbb), X(0x2d2aff6d), + X(0x2d4a13ec), X(0x2d692d31), X(0x2d884b32), X(0x2da76de4), + X(0x2dc69540), X(0x2de5c13d), X(0x2e04f1d0), X(0x2e2426f0), + X(0x2e436095), X(0x2e629eb4), X(0x2e81e146), X(0x2ea1283f), + X(0x2ec07398), X(0x2edfc347), X(0x2eff1742), X(0x2f1e6f80), + X(0x2f3dcbf8), X(0x2f5d2ca0), X(0x2f7c916f), X(0x2f9bfa5c), + X(0x2fbb675d), X(0x2fdad869), X(0x2ffa4d76), X(0x3019c67b), + X(0x3039436f), X(0x3058c448), X(0x307848fc), X(0x3097d183), + X(0x30b75dd3), X(0x30d6ede2), X(0x30f681a6), X(0x31161917), + X(0x3135b42b), X(0x315552d8), X(0x3174f514), X(0x31949ad7), + X(0x31b44417), X(0x31d3f0ca), X(0x31f3a0e6), X(0x32135462), + X(0x32330b35), X(0x3252c555), X(0x327282b7), X(0x32924354), + X(0x32b20720), X(0x32d1ce13), X(0x32f19823), X(0x33116546), + X(0x33313573), X(0x3351089f), X(0x3370dec2), X(0x3390b7d1), + X(0x33b093c3), X(0x33d0728f), X(0x33f05429), X(0x3410388a), + X(0x34301fa7), X(0x34500977), X(0x346ff5ef), X(0x348fe506), + X(0x34afd6b3), X(0x34cfcaeb), X(0x34efc1a5), X(0x350fbad7), + X(0x352fb678), X(0x354fb47d), X(0x356fb4dd), X(0x358fb78e), + X(0x35afbc86), X(0x35cfc3bc), X(0x35efcd25), X(0x360fd8b8), + X(0x362fe66c), X(0x364ff636), X(0x3670080c), X(0x36901be5), + X(0x36b031b7), X(0x36d04978), X(0x36f0631e), X(0x37107ea0), + X(0x37309bf3), X(0x3750bb0e), X(0x3770dbe6), X(0x3790fe73), + X(0x37b122aa), X(0x37d14881), X(0x37f16fee), X(0x381198e8), + X(0x3831c365), X(0x3851ef5a), X(0x38721cbe), X(0x38924b87), + X(0x38b27bac), X(0x38d2ad21), X(0x38f2dfde), X(0x391313d8), + X(0x39334906), X(0x39537f5d), X(0x3973b6d4), X(0x3993ef60), + X(0x39b428f9), X(0x39d46393), X(0x39f49f25), X(0x3a14dba6), + X(0x3a35190a), X(0x3a555748), X(0x3a759657), X(0x3a95d62c), + X(0x3ab616be), X(0x3ad65801), X(0x3af699ed), X(0x3b16dc78), + X(0x3b371f97), X(0x3b576341), X(0x3b77a76c), X(0x3b97ec0d), + X(0x3bb8311b), X(0x3bd8768b), X(0x3bf8bc55), X(0x3c19026d), + X(0x3c3948ca), X(0x3c598f62), X(0x3c79d62b), X(0x3c9a1d1b), + X(0x3cba6428), X(0x3cdaab48), X(0x3cfaf271), X(0x3d1b3999), + X(0x3d3b80b6), X(0x3d5bc7be), X(0x3d7c0ea8), X(0x3d9c5569), + X(0x3dbc9bf7), X(0x3ddce248), X(0x3dfd2852), X(0x3e1d6e0c), + X(0x3e3db36c), X(0x3e5df866), X(0x3e7e3cf2), X(0x3e9e8106), + X(0x3ebec497), X(0x3edf079b), X(0x3eff4a09), X(0x3f1f8bd7), + X(0x3f3fccfa), X(0x3f600d69), X(0x3f804d1a), X(0x3fa08c02), + X(0x3fc0ca19), X(0x3fe10753), X(0x400143a7), X(0x40217f0a), + X(0x4041b974), X(0x4061f2da), X(0x40822b32), X(0x40a26272), + X(0x40c29891), X(0x40e2cd83), X(0x41030140), X(0x412333bd), + X(0x414364f1), X(0x416394d2), X(0x4183c355), X(0x41a3f070), + X(0x41c41c1b), X(0x41e4464a), X(0x42046ef4), X(0x42249610), + X(0x4244bb92), X(0x4264df72), X(0x428501a5), X(0x42a52222), + X(0x42c540de), X(0x42e55dd0), X(0x430578ed), X(0x4325922d), + X(0x4345a985), X(0x4365beeb), X(0x4385d255), X(0x43a5e3ba), + X(0x43c5f30f), X(0x43e6004b), X(0x44060b65), X(0x44261451), + X(0x44461b07), X(0x44661f7c), X(0x448621a7), X(0x44a6217d), + X(0x44c61ef6), X(0x44e61a07), X(0x450612a6), X(0x452608ca), + X(0x4545fc69), X(0x4565ed79), X(0x4585dbf1), X(0x45a5c7c6), + X(0x45c5b0ef), X(0x45e59761), X(0x46057b15), X(0x46255bfe), + X(0x46453a15), X(0x4665154f), X(0x4684eda2), X(0x46a4c305), + X(0x46c4956e), X(0x46e464d3), X(0x4704312b), X(0x4723fa6c), + X(0x4743c08d), X(0x47638382), X(0x47834344), X(0x47a2ffc9), + X(0x47c2b906), X(0x47e26ef2), X(0x48022183), X(0x4821d0b1), + X(0x48417c71), X(0x486124b9), X(0x4880c981), X(0x48a06abe), + X(0x48c00867), X(0x48dfa272), X(0x48ff38d6), X(0x491ecb8a), + X(0x493e5a84), X(0x495de5b9), X(0x497d6d22), X(0x499cf0b4), + X(0x49bc7066), X(0x49dbec2e), X(0x49fb6402), X(0x4a1ad7db), + X(0x4a3a47ad), X(0x4a59b370), X(0x4a791b1a), X(0x4a987ea1), + X(0x4ab7ddfd), X(0x4ad73924), X(0x4af6900c), X(0x4b15e2ad), + X(0x4b3530fc), X(0x4b547af1), X(0x4b73c082), X(0x4b9301a6), + X(0x4bb23e53), X(0x4bd17681), X(0x4bf0aa25), X(0x4c0fd937), + X(0x4c2f03ae), X(0x4c4e297f), X(0x4c6d4aa3), X(0x4c8c670f), + X(0x4cab7eba), X(0x4cca919c), X(0x4ce99fab), X(0x4d08a8de), + X(0x4d27ad2c), X(0x4d46ac8b), X(0x4d65a6f3), X(0x4d849c5a), + X(0x4da38cb7), X(0x4dc27802), X(0x4de15e31), X(0x4e003f3a), + X(0x4e1f1b16), X(0x4e3df1ba), X(0x4e5cc31e), X(0x4e7b8f3a), + X(0x4e9a5603), X(0x4eb91771), X(0x4ed7d37b), X(0x4ef68a18), + X(0x4f153b3f), X(0x4f33e6e7), X(0x4f528d08), X(0x4f712d97), + X(0x4f8fc88e), X(0x4fae5de1), X(0x4fcced8a), X(0x4feb777f), + X(0x5009fbb6), X(0x50287a28), X(0x5046f2cc), X(0x50656598), + X(0x5083d284), X(0x50a23988), X(0x50c09a9a), X(0x50def5b1), + X(0x50fd4ac7), X(0x511b99d0), X(0x5139e2c5), X(0x5158259e), + X(0x51766251), X(0x519498d6), X(0x51b2c925), X(0x51d0f334), + X(0x51ef16fb), X(0x520d3473), X(0x522b4b91), X(0x52495c4e), + X(0x526766a2), X(0x52856a83), X(0x52a367e9), X(0x52c15ecd), + X(0x52df4f24), X(0x52fd38e8), X(0x531b1c10), X(0x5338f892), + X(0x5356ce68), X(0x53749d89), X(0x539265eb), X(0x53b02788), + X(0x53cde257), X(0x53eb964f), X(0x54094369), X(0x5426e99c), + X(0x544488df), X(0x5462212c), X(0x547fb279), X(0x549d3cbe), + X(0x54babff4), X(0x54d83c12), X(0x54f5b110), X(0x55131ee7), + X(0x5530858d), X(0x554de4fc), X(0x556b3d2a), X(0x55888e11), + X(0x55a5d7a8), X(0x55c319e7), X(0x55e054c7), X(0x55fd883f), + X(0x561ab447), X(0x5637d8d8), X(0x5654f5ea), X(0x56720b75), + X(0x568f1971), X(0x56ac1fd7), X(0x56c91e9e), X(0x56e615c0), + X(0x57030534), X(0x571fecf2), X(0x573cccf3), X(0x5759a530), + X(0x577675a0), X(0x57933e3c), X(0x57affefd), X(0x57ccb7db), + X(0x57e968ce), X(0x580611cf), X(0x5822b2d6), X(0x583f4bdd), + X(0x585bdcdb), X(0x587865c9), X(0x5894e69f), X(0x58b15f57), + X(0x58cdcfe9), X(0x58ea384e), X(0x5906987d), X(0x5922f071), + X(0x593f4022), X(0x595b8788), X(0x5977c69c), X(0x5993fd57), + X(0x59b02bb2), X(0x59cc51a6), X(0x59e86f2c), X(0x5a04843c), + X(0x5a2090d0), X(0x5a3c94e0), X(0x5a589065), X(0x5a748359), + X(0x5a906db4), X(0x5aac4f70), X(0x5ac82884), X(0x5ae3f8ec), + X(0x5affc09f), X(0x5b1b7f97), X(0x5b3735cd), X(0x5b52e33a), + X(0x5b6e87d8), X(0x5b8a239f), X(0x5ba5b689), X(0x5bc1408f), + X(0x5bdcc1aa), X(0x5bf839d5), X(0x5c13a907), X(0x5c2f0f3b), + X(0x5c4a6c6a), X(0x5c65c08d), X(0x5c810b9e), X(0x5c9c4d97), + X(0x5cb78670), X(0x5cd2b623), X(0x5ceddcaa), X(0x5d08f9ff), + X(0x5d240e1b), X(0x5d3f18f8), X(0x5d5a1a8f), X(0x5d7512da), + X(0x5d9001d3), X(0x5daae773), X(0x5dc5c3b5), X(0x5de09692), + X(0x5dfb6004), X(0x5e162004), X(0x5e30d68d), X(0x5e4b8399), + X(0x5e662721), X(0x5e80c11f), X(0x5e9b518e), X(0x5eb5d867), + X(0x5ed055a4), X(0x5eeac940), X(0x5f053334), X(0x5f1f937b), + X(0x5f39ea0f), X(0x5f5436ea), X(0x5f6e7a06), X(0x5f88b35d), + X(0x5fa2e2e9), X(0x5fbd08a6), X(0x5fd7248d), X(0x5ff13698), + X(0x600b3ec2), X(0x60253d05), X(0x603f315b), X(0x60591bc0), + X(0x6072fc2d), X(0x608cd29e), X(0x60a69f0b), X(0x60c06171), + X(0x60da19ca), X(0x60f3c80f), X(0x610d6c3d), X(0x6127064d), + X(0x6140963a), X(0x615a1bff), X(0x61739797), X(0x618d08fc), + X(0x61a67029), X(0x61bfcd1a), X(0x61d91fc8), X(0x61f2682f), + X(0x620ba64a), X(0x6224da13), X(0x623e0386), X(0x6257229d), + X(0x62703754), X(0x628941a6), X(0x62a2418e), X(0x62bb3706), + X(0x62d4220a), X(0x62ed0296), X(0x6305d8a3), X(0x631ea42f), + X(0x63376533), X(0x63501bab), X(0x6368c793), X(0x638168e5), + X(0x6399ff9e), X(0x63b28bb8), X(0x63cb0d2f), X(0x63e383ff), + X(0x63fbf022), X(0x64145195), X(0x642ca853), X(0x6444f457), + X(0x645d359e), X(0x64756c22), X(0x648d97e0), X(0x64a5b8d3), + X(0x64bdcef6), X(0x64d5da47), X(0x64eddabf), X(0x6505d05c), + X(0x651dbb19), X(0x65359af2), X(0x654d6fe3), X(0x656539e7), + X(0x657cf8fb), X(0x6594ad1b), X(0x65ac5643), X(0x65c3f46e), + X(0x65db8799), X(0x65f30fc0), X(0x660a8ce0), X(0x6621fef3), + X(0x663965f7), X(0x6650c1e7), X(0x666812c1), X(0x667f5880), + X(0x66969320), X(0x66adc29e), X(0x66c4e6f7), X(0x66dc0026), + X(0x66f30e28), X(0x670a10fa), X(0x67210898), X(0x6737f4ff), + X(0x674ed62b), X(0x6765ac19), X(0x677c76c5), X(0x6793362c), + X(0x67a9ea4b), X(0x67c0931f), X(0x67d730a3), X(0x67edc2d6), + X(0x680449b3), X(0x681ac538), X(0x68313562), X(0x68479a2d), + X(0x685df396), X(0x6874419b), X(0x688a8438), X(0x68a0bb6a), + X(0x68b6e72e), X(0x68cd0782), X(0x68e31c63), X(0x68f925cd), + X(0x690f23be), X(0x69251633), X(0x693afd29), X(0x6950d89e), + X(0x6966a88f), X(0x697c6cf8), X(0x699225d9), X(0x69a7d32d), + X(0x69bd74f3), X(0x69d30b27), X(0x69e895c8), X(0x69fe14d2), + X(0x6a138844), X(0x6a28f01b), X(0x6a3e4c54), X(0x6a539ced), + X(0x6a68e1e4), X(0x6a7e1b37), X(0x6a9348e3), X(0x6aa86ae6), + X(0x6abd813d), X(0x6ad28be7), X(0x6ae78ae2), X(0x6afc7e2b), + X(0x6b1165c0), X(0x6b26419f), X(0x6b3b11c7), X(0x6b4fd634), + X(0x6b648ee6), X(0x6b793bda), X(0x6b8ddd0e), X(0x6ba27281), + X(0x6bb6fc31), X(0x6bcb7a1b), X(0x6bdfec3e), X(0x6bf45299), + X(0x6c08ad29), X(0x6c1cfbed), X(0x6c313ee4), X(0x6c45760a), + X(0x6c59a160), X(0x6c6dc0e4), X(0x6c81d493), X(0x6c95dc6d), + X(0x6ca9d86f), X(0x6cbdc899), X(0x6cd1acea), X(0x6ce5855f), + X(0x6cf951f7), X(0x6d0d12b1), X(0x6d20c78c), X(0x6d347087), + X(0x6d480da0), X(0x6d5b9ed6), X(0x6d6f2427), X(0x6d829d94), + X(0x6d960b1a), X(0x6da96cb9), X(0x6dbcc270), X(0x6dd00c3c), + X(0x6de34a1f), X(0x6df67c16), X(0x6e09a221), X(0x6e1cbc3f), + X(0x6e2fca6e), X(0x6e42ccaf), X(0x6e55c300), X(0x6e68ad60), + X(0x6e7b8bd0), X(0x6e8e5e4d), X(0x6ea124d8), X(0x6eb3df70), + X(0x6ec68e13), X(0x6ed930c3), X(0x6eebc77d), X(0x6efe5242), + X(0x6f10d111), X(0x6f2343e9), X(0x6f35aacb), X(0x6f4805b5), + X(0x6f5a54a8), X(0x6f6c97a2), X(0x6f7ecea4), X(0x6f90f9ae), + X(0x6fa318be), X(0x6fb52bd6), X(0x6fc732f4), X(0x6fd92e19), + X(0x6feb1d44), X(0x6ffd0076), X(0x700ed7ad), X(0x7020a2eb), + X(0x7032622f), X(0x7044157a), X(0x7055bcca), X(0x70675821), + X(0x7078e77e), X(0x708a6ae2), X(0x709be24c), X(0x70ad4dbd), + X(0x70bead36), X(0x70d000b5), X(0x70e1483d), X(0x70f283cc), + X(0x7103b363), X(0x7114d704), X(0x7125eead), X(0x7136fa60), + X(0x7147fa1c), X(0x7158ede4), X(0x7169d5b6), X(0x717ab193), + X(0x718b817d), X(0x719c4573), X(0x71acfd76), X(0x71bda988), + X(0x71ce49a8), X(0x71deddd7), X(0x71ef6617), X(0x71ffe267), + X(0x721052ca), X(0x7220b73e), X(0x72310fc6), X(0x72415c62), + X(0x72519d14), X(0x7261d1db), X(0x7271faba), X(0x728217b1), + X(0x729228c0), X(0x72a22dea), X(0x72b22730), X(0x72c21491), + X(0x72d1f611), X(0x72e1cbaf), X(0x72f1956c), X(0x7301534c), + X(0x7311054d), X(0x7320ab72), X(0x733045bc), X(0x733fd42d), + X(0x734f56c5), X(0x735ecd86), X(0x736e3872), X(0x737d9789), + X(0x738ceacf), X(0x739c3243), X(0x73ab6de7), X(0x73ba9dbe), + X(0x73c9c1c8), X(0x73d8da08), X(0x73e7e67f), X(0x73f6e72e), + X(0x7405dc17), X(0x7414c53c), X(0x7423a29f), X(0x74327442), + X(0x74413a26), X(0x744ff44d), X(0x745ea2b9), X(0x746d456c), + X(0x747bdc68), X(0x748a67ae), X(0x7498e741), X(0x74a75b23), + X(0x74b5c356), X(0x74c41fdb), X(0x74d270b6), X(0x74e0b5e7), + X(0x74eeef71), X(0x74fd1d57), X(0x750b3f9a), X(0x7519563c), + X(0x75276140), X(0x753560a8), X(0x75435477), X(0x75513cae), + X(0x755f1951), X(0x756cea60), X(0x757aafdf), X(0x758869d1), + X(0x75961837), X(0x75a3bb14), X(0x75b1526a), X(0x75bede3c), + X(0x75cc5e8d), X(0x75d9d35f), X(0x75e73cb5), X(0x75f49a91), + X(0x7601ecf6), X(0x760f33e6), X(0x761c6f65), X(0x76299f74), + X(0x7636c417), X(0x7643dd51), X(0x7650eb24), X(0x765ded93), + X(0x766ae4a0), X(0x7677d050), X(0x7684b0a4), X(0x7691859f), + X(0x769e4f45), X(0x76ab0d98), X(0x76b7c09c), X(0x76c46852), + X(0x76d104bf), X(0x76dd95e6), X(0x76ea1bc9), X(0x76f6966b), + X(0x770305d0), X(0x770f69fb), X(0x771bc2ef), X(0x772810af), + X(0x7734533e), X(0x77408aa0), X(0x774cb6d7), X(0x7758d7e8), + X(0x7764edd5), X(0x7770f8a2), X(0x777cf852), X(0x7788ece8), + X(0x7794d668), X(0x77a0b4d5), X(0x77ac8833), X(0x77b85085), + X(0x77c40dce), X(0x77cfc013), X(0x77db6756), X(0x77e7039b), + X(0x77f294e6), X(0x77fe1b3b), X(0x7809969c), X(0x7815070e), + X(0x78206c93), X(0x782bc731), X(0x783716ea), X(0x78425bc3), + X(0x784d95be), X(0x7858c4e1), X(0x7863e92d), X(0x786f02a8), + X(0x787a1156), X(0x78851539), X(0x78900e56), X(0x789afcb1), + X(0x78a5e04d), X(0x78b0b92f), X(0x78bb875b), X(0x78c64ad4), + X(0x78d1039e), X(0x78dbb1be), X(0x78e65537), X(0x78f0ee0e), + X(0x78fb7c46), X(0x7905ffe4), X(0x791078ec), X(0x791ae762), + X(0x79254b4a), X(0x792fa4a7), X(0x7939f380), X(0x794437d7), + X(0x794e71b0), X(0x7958a111), X(0x7962c5fd), X(0x796ce078), + X(0x7976f087), X(0x7980f62f), X(0x798af173), X(0x7994e258), + X(0x799ec8e2), X(0x79a8a515), X(0x79b276f7), X(0x79bc3e8b), + X(0x79c5fbd6), X(0x79cfaedc), X(0x79d957a2), X(0x79e2f62c), + X(0x79ec8a7f), X(0x79f6149f), X(0x79ff9492), X(0x7a090a5a), + X(0x7a1275fe), X(0x7a1bd781), X(0x7a252ee9), X(0x7a2e7c39), + X(0x7a37bf77), X(0x7a40f8a7), X(0x7a4a27ce), X(0x7a534cf0), + X(0x7a5c6813), X(0x7a65793b), X(0x7a6e806d), X(0x7a777dad), + X(0x7a807100), X(0x7a895a6b), X(0x7a9239f4), X(0x7a9b0f9e), + X(0x7aa3db6f), X(0x7aac9d6b), X(0x7ab55597), X(0x7abe03f9), + X(0x7ac6a895), X(0x7acf4370), X(0x7ad7d48f), X(0x7ae05bf6), + X(0x7ae8d9ac), X(0x7af14db5), X(0x7af9b815), X(0x7b0218d2), + X(0x7b0a6ff2), X(0x7b12bd78), X(0x7b1b016a), X(0x7b233bce), + X(0x7b2b6ca7), X(0x7b3393fc), X(0x7b3bb1d1), X(0x7b43c62c), + X(0x7b4bd111), X(0x7b53d286), X(0x7b5bca90), X(0x7b63b935), + X(0x7b6b9e78), X(0x7b737a61), X(0x7b7b4cf3), X(0x7b831634), + X(0x7b8ad629), X(0x7b928cd8), X(0x7b9a3a45), X(0x7ba1de77), + X(0x7ba97972), X(0x7bb10b3c), X(0x7bb893d9), X(0x7bc01350), + X(0x7bc789a6), X(0x7bcef6e0), X(0x7bd65b03), X(0x7bddb616), + X(0x7be5081c), X(0x7bec511c), X(0x7bf3911b), X(0x7bfac81f), + X(0x7c01f62c), X(0x7c091b49), X(0x7c10377b), X(0x7c174ac7), + X(0x7c1e5532), X(0x7c2556c4), X(0x7c2c4f80), X(0x7c333f6c), + X(0x7c3a268e), X(0x7c4104ec), X(0x7c47da8a), X(0x7c4ea76f), + X(0x7c556ba1), X(0x7c5c2724), X(0x7c62d9fe), X(0x7c698435), + X(0x7c7025cf), X(0x7c76bed0), X(0x7c7d4f40), X(0x7c83d723), + X(0x7c8a567f), X(0x7c90cd5a), X(0x7c973bb9), X(0x7c9da1a2), + X(0x7ca3ff1b), X(0x7caa542a), X(0x7cb0a0d3), X(0x7cb6e51e), + X(0x7cbd210f), X(0x7cc354ac), X(0x7cc97ffc), X(0x7ccfa304), + X(0x7cd5bdc9), X(0x7cdbd051), X(0x7ce1daa3), X(0x7ce7dcc3), + X(0x7cedd6b8), X(0x7cf3c888), X(0x7cf9b238), X(0x7cff93cf), + X(0x7d056d51), X(0x7d0b3ec5), X(0x7d110830), X(0x7d16c99a), + X(0x7d1c8306), X(0x7d22347c), X(0x7d27de00), X(0x7d2d7f9a), + X(0x7d33194f), X(0x7d38ab24), X(0x7d3e351f), X(0x7d43b748), + X(0x7d4931a2), X(0x7d4ea435), X(0x7d540f06), X(0x7d59721b), + X(0x7d5ecd7b), X(0x7d64212a), X(0x7d696d2f), X(0x7d6eb190), + X(0x7d73ee53), X(0x7d79237e), X(0x7d7e5117), X(0x7d837723), + X(0x7d8895a9), X(0x7d8dacae), X(0x7d92bc3a), X(0x7d97c451), + X(0x7d9cc4f9), X(0x7da1be39), X(0x7da6b017), X(0x7dab9a99), + X(0x7db07dc4), X(0x7db5599e), X(0x7dba2e2f), X(0x7dbefb7b), + X(0x7dc3c189), X(0x7dc8805e), X(0x7dcd3802), X(0x7dd1e879), + X(0x7dd691ca), X(0x7ddb33fb), X(0x7ddfcf12), X(0x7de46315), + X(0x7de8f00a), X(0x7ded75f8), X(0x7df1f4e3), X(0x7df66cd3), + X(0x7dfaddcd), X(0x7dff47d7), X(0x7e03aaf8), X(0x7e080735), + X(0x7e0c5c95), X(0x7e10ab1e), X(0x7e14f2d5), X(0x7e1933c1), + X(0x7e1d6de8), X(0x7e21a150), X(0x7e25cdff), X(0x7e29f3fc), + X(0x7e2e134c), X(0x7e322bf5), X(0x7e363dfd), X(0x7e3a496b), + X(0x7e3e4e45), X(0x7e424c90), X(0x7e464454), X(0x7e4a3595), + X(0x7e4e205a), X(0x7e5204aa), X(0x7e55e289), X(0x7e59b9ff), + X(0x7e5d8b12), X(0x7e6155c7), X(0x7e651a24), X(0x7e68d831), + X(0x7e6c8ff2), X(0x7e70416e), X(0x7e73ecac), X(0x7e7791b0), + X(0x7e7b3082), X(0x7e7ec927), X(0x7e825ba6), X(0x7e85e804), + X(0x7e896e48), X(0x7e8cee77), X(0x7e906899), X(0x7e93dcb2), + X(0x7e974aca), X(0x7e9ab2e5), X(0x7e9e150b), X(0x7ea17141), + X(0x7ea4c78e), X(0x7ea817f7), X(0x7eab6283), X(0x7eaea737), + X(0x7eb1e61a), X(0x7eb51f33), X(0x7eb85285), X(0x7ebb8019), + X(0x7ebea7f4), X(0x7ec1ca1d), X(0x7ec4e698), X(0x7ec7fd6d), + X(0x7ecb0ea1), X(0x7ece1a3a), X(0x7ed1203f), X(0x7ed420b6), + X(0x7ed71ba4), X(0x7eda110f), X(0x7edd00ff), X(0x7edfeb78), + X(0x7ee2d081), X(0x7ee5b01f), X(0x7ee88a5a), X(0x7eeb5f36), + X(0x7eee2eba), X(0x7ef0f8ed), X(0x7ef3bdd3), X(0x7ef67d73), + X(0x7ef937d3), X(0x7efbecf9), X(0x7efe9ceb), X(0x7f0147ae), + X(0x7f03ed4a), X(0x7f068dc4), X(0x7f092922), X(0x7f0bbf69), + X(0x7f0e50a1), X(0x7f10dcce), X(0x7f1363f7), X(0x7f15e622), + X(0x7f186355), X(0x7f1adb95), X(0x7f1d4ee9), X(0x7f1fbd57), + X(0x7f2226e4), X(0x7f248b96), X(0x7f26eb74), X(0x7f294683), + X(0x7f2b9cc9), X(0x7f2dee4d), X(0x7f303b13), X(0x7f328322), + X(0x7f34c680), X(0x7f370533), X(0x7f393f40), X(0x7f3b74ad), + X(0x7f3da581), X(0x7f3fd1c1), X(0x7f41f972), X(0x7f441c9c), + X(0x7f463b43), X(0x7f48556d), X(0x7f4a6b21), X(0x7f4c7c64), + X(0x7f4e893c), X(0x7f5091ae), X(0x7f5295c1), X(0x7f54957a), + X(0x7f5690e0), X(0x7f5887f7), X(0x7f5a7ac5), X(0x7f5c6951), + X(0x7f5e53a0), X(0x7f6039b8), X(0x7f621b9e), X(0x7f63f958), + X(0x7f65d2ed), X(0x7f67a861), X(0x7f6979ba), X(0x7f6b46ff), + X(0x7f6d1034), X(0x7f6ed560), X(0x7f709687), X(0x7f7253b1), + X(0x7f740ce1), X(0x7f75c21f), X(0x7f777370), X(0x7f7920d8), + X(0x7f7aca5f), X(0x7f7c7008), X(0x7f7e11db), X(0x7f7fafdd), + X(0x7f814a13), X(0x7f82e082), X(0x7f847331), X(0x7f860224), + X(0x7f878d62), X(0x7f8914f0), X(0x7f8a98d4), X(0x7f8c1912), + X(0x7f8d95b0), X(0x7f8f0eb5), X(0x7f908425), X(0x7f91f605), + X(0x7f93645c), X(0x7f94cf2f), X(0x7f963683), X(0x7f979a5d), + X(0x7f98fac4), X(0x7f9a57bb), X(0x7f9bb14a), X(0x7f9d0775), + X(0x7f9e5a41), X(0x7f9fa9b4), X(0x7fa0f5d3), X(0x7fa23ea4), + X(0x7fa3842b), X(0x7fa4c66f), X(0x7fa60575), X(0x7fa74141), + X(0x7fa879d9), X(0x7fa9af42), X(0x7faae182), X(0x7fac109e), + X(0x7fad3c9a), X(0x7fae657d), X(0x7faf8b4c), X(0x7fb0ae0b), + X(0x7fb1cdc0), X(0x7fb2ea70), X(0x7fb40420), X(0x7fb51ad5), + X(0x7fb62e95), X(0x7fb73f64), X(0x7fb84d48), X(0x7fb95846), + X(0x7fba6062), X(0x7fbb65a2), X(0x7fbc680c), X(0x7fbd67a3), + X(0x7fbe646d), X(0x7fbf5e70), X(0x7fc055af), X(0x7fc14a31), + X(0x7fc23bf9), X(0x7fc32b0d), X(0x7fc41773), X(0x7fc5012e), + X(0x7fc5e844), X(0x7fc6ccba), X(0x7fc7ae94), X(0x7fc88dd8), + X(0x7fc96a8a), X(0x7fca44af), X(0x7fcb1c4c), X(0x7fcbf167), + X(0x7fccc403), X(0x7fcd9425), X(0x7fce61d3), X(0x7fcf2d11), + X(0x7fcff5e3), X(0x7fd0bc4f), X(0x7fd1805a), X(0x7fd24207), + X(0x7fd3015c), X(0x7fd3be5d), X(0x7fd47910), X(0x7fd53178), + X(0x7fd5e79b), X(0x7fd69b7c), X(0x7fd74d21), X(0x7fd7fc8e), + X(0x7fd8a9c8), X(0x7fd954d4), X(0x7fd9fdb5), X(0x7fdaa471), + X(0x7fdb490b), X(0x7fdbeb89), X(0x7fdc8bef), X(0x7fdd2a42), + X(0x7fddc685), X(0x7fde60be), X(0x7fdef8f0), X(0x7fdf8f20), + X(0x7fe02353), X(0x7fe0b58d), X(0x7fe145d3), X(0x7fe1d428), + X(0x7fe26091), X(0x7fe2eb12), X(0x7fe373b0), X(0x7fe3fa6f), + X(0x7fe47f53), X(0x7fe50260), X(0x7fe5839b), X(0x7fe60308), + X(0x7fe680ab), X(0x7fe6fc88), X(0x7fe776a4), X(0x7fe7ef02), + X(0x7fe865a7), X(0x7fe8da97), X(0x7fe94dd6), X(0x7fe9bf68), + X(0x7fea2f51), X(0x7fea9d95), X(0x7feb0a39), X(0x7feb7540), + X(0x7febdeae), X(0x7fec4687), X(0x7fecaccf), X(0x7fed118b), + X(0x7fed74be), X(0x7fedd66c), X(0x7fee3698), X(0x7fee9548), + X(0x7feef27e), X(0x7fef4e3f), X(0x7fefa88e), X(0x7ff0016f), + X(0x7ff058e7), X(0x7ff0aef8), X(0x7ff103a6), X(0x7ff156f6), + X(0x7ff1a8eb), X(0x7ff1f988), X(0x7ff248d2), X(0x7ff296cc), + X(0x7ff2e37a), X(0x7ff32edf), X(0x7ff378ff), X(0x7ff3c1de), + X(0x7ff4097e), X(0x7ff44fe5), X(0x7ff49515), X(0x7ff4d911), + X(0x7ff51bde), X(0x7ff55d7f), X(0x7ff59df7), X(0x7ff5dd4a), + X(0x7ff61b7b), X(0x7ff6588d), X(0x7ff69485), X(0x7ff6cf65), + X(0x7ff70930), X(0x7ff741eb), X(0x7ff77998), X(0x7ff7b03b), + X(0x7ff7e5d7), X(0x7ff81a6f), X(0x7ff84e06), X(0x7ff880a1), + X(0x7ff8b241), X(0x7ff8e2ea), X(0x7ff912a0), X(0x7ff94165), + X(0x7ff96f3d), X(0x7ff99c2b), X(0x7ff9c831), X(0x7ff9f354), + X(0x7ffa1d95), X(0x7ffa46f9), X(0x7ffa6f81), X(0x7ffa9731), + X(0x7ffabe0d), X(0x7ffae416), X(0x7ffb0951), X(0x7ffb2dbf), + X(0x7ffb5164), X(0x7ffb7442), X(0x7ffb965d), X(0x7ffbb7b8), + X(0x7ffbd854), X(0x7ffbf836), X(0x7ffc175f), X(0x7ffc35d3), + X(0x7ffc5394), X(0x7ffc70a5), X(0x7ffc8d09), X(0x7ffca8c2), + X(0x7ffcc3d4), X(0x7ffcde3f), X(0x7ffcf809), X(0x7ffd1132), + X(0x7ffd29be), X(0x7ffd41ae), X(0x7ffd5907), X(0x7ffd6fc9), + X(0x7ffd85f9), X(0x7ffd9b97), X(0x7ffdb0a7), X(0x7ffdc52b), + X(0x7ffdd926), X(0x7ffdec99), X(0x7ffdff88), X(0x7ffe11f4), + X(0x7ffe23e0), X(0x7ffe354f), X(0x7ffe4642), X(0x7ffe56bc), + X(0x7ffe66bf), X(0x7ffe764e), X(0x7ffe856a), X(0x7ffe9416), + X(0x7ffea254), X(0x7ffeb026), X(0x7ffebd8e), X(0x7ffeca8f), + X(0x7ffed72a), X(0x7ffee362), X(0x7ffeef38), X(0x7ffefaaf), + X(0x7fff05c9), X(0x7fff1087), X(0x7fff1aec), X(0x7fff24f9), + X(0x7fff2eb1), X(0x7fff3816), X(0x7fff4128), X(0x7fff49eb), + X(0x7fff5260), X(0x7fff5a88), X(0x7fff6266), X(0x7fff69fc), + X(0x7fff714b), X(0x7fff7854), X(0x7fff7f1a), X(0x7fff859f), + X(0x7fff8be3), X(0x7fff91ea), X(0x7fff97b3), X(0x7fff9d41), + X(0x7fffa296), X(0x7fffa7b3), X(0x7fffac99), X(0x7fffb14b), + X(0x7fffb5c9), X(0x7fffba15), X(0x7fffbe31), X(0x7fffc21d), + X(0x7fffc5dc), X(0x7fffc96f), X(0x7fffccd8), X(0x7fffd016), + X(0x7fffd32d), X(0x7fffd61c), X(0x7fffd8e7), X(0x7fffdb8d), + X(0x7fffde0f), X(0x7fffe071), X(0x7fffe2b1), X(0x7fffe4d2), + X(0x7fffe6d5), X(0x7fffe8bb), X(0x7fffea85), X(0x7fffec34), + X(0x7fffedc9), X(0x7fffef45), X(0x7ffff0aa), X(0x7ffff1f7), + X(0x7ffff330), X(0x7ffff453), X(0x7ffff562), X(0x7ffff65f), + X(0x7ffff749), X(0x7ffff823), X(0x7ffff8ec), X(0x7ffff9a6), + X(0x7ffffa51), X(0x7ffffaee), X(0x7ffffb7e), X(0x7ffffc02), + X(0x7ffffc7a), X(0x7ffffce7), X(0x7ffffd4a), X(0x7ffffda3), + X(0x7ffffdf4), X(0x7ffffe3c), X(0x7ffffe7c), X(0x7ffffeb6), + X(0x7ffffee8), X(0x7fffff15), X(0x7fffff3c), X(0x7fffff5e), + X(0x7fffff7b), X(0x7fffff95), X(0x7fffffaa), X(0x7fffffbc), + X(0x7fffffcb), X(0x7fffffd7), X(0x7fffffe2), X(0x7fffffea), + X(0x7ffffff0), X(0x7ffffff5), X(0x7ffffff9), X(0x7ffffffb), + X(0x7ffffffd), X(0x7ffffffe), X(0x7fffffff), X(0x7fffffff), + X(0x7fffffff), X(0x7fffffff), X(0x7fffffff), X(0x7fffffff), +}; + +#ifndef LIMIT_TO_64kHz + +static LOOKUP_T vwin8192[4096] = { + X(0x0000007c), X(0x0000045c), X(0x00000c1d), X(0x000017bd), + X(0x0000273e), X(0x00003a9f), X(0x000051e0), X(0x00006d02), + X(0x00008c03), X(0x0000aee5), X(0x0000d5a7), X(0x00010049), + X(0x00012ecb), X(0x0001612d), X(0x00019770), X(0x0001d193), + X(0x00020f96), X(0x00025178), X(0x0002973c), X(0x0002e0df), + X(0x00032e62), X(0x00037fc5), X(0x0003d509), X(0x00042e2c), + X(0x00048b30), X(0x0004ec13), X(0x000550d7), X(0x0005b97a), + X(0x000625fe), X(0x00069661), X(0x00070aa4), X(0x000782c8), + X(0x0007fecb), X(0x00087eae), X(0x00090271), X(0x00098a14), + X(0x000a1597), X(0x000aa4f9), X(0x000b383b), X(0x000bcf5d), + X(0x000c6a5f), X(0x000d0941), X(0x000dac02), X(0x000e52a3), + X(0x000efd23), X(0x000fab84), X(0x00105dc3), X(0x001113e3), + X(0x0011cde2), X(0x00128bc0), X(0x00134d7e), X(0x0014131b), + X(0x0014dc98), X(0x0015a9f4), X(0x00167b30), X(0x0017504a), + X(0x00182945), X(0x0019061e), X(0x0019e6d7), X(0x001acb6f), + X(0x001bb3e6), X(0x001ca03c), X(0x001d9071), X(0x001e8485), + X(0x001f7c79), X(0x0020784b), X(0x002177fc), X(0x00227b8c), + X(0x002382fb), X(0x00248e49), X(0x00259d76), X(0x0026b081), + X(0x0027c76b), X(0x0028e234), X(0x002a00dc), X(0x002b2361), + X(0x002c49c6), X(0x002d7409), X(0x002ea22a), X(0x002fd42a), + X(0x00310a08), X(0x003243c5), X(0x00338160), X(0x0034c2d9), + X(0x00360830), X(0x00375165), X(0x00389e78), X(0x0039ef6a), + X(0x003b4439), X(0x003c9ce6), X(0x003df971), X(0x003f59da), + X(0x0040be20), X(0x00422645), X(0x00439247), X(0x00450226), + X(0x004675e3), X(0x0047ed7e), X(0x004968f5), X(0x004ae84b), + X(0x004c6b7d), X(0x004df28d), X(0x004f7d7a), X(0x00510c44), + X(0x00529eeb), X(0x00543570), X(0x0055cfd1), X(0x00576e0f), + X(0x00591029), X(0x005ab621), X(0x005c5ff5), X(0x005e0da6), + X(0x005fbf33), X(0x0061749d), X(0x00632de4), X(0x0064eb06), + X(0x0066ac05), X(0x006870e0), X(0x006a3998), X(0x006c062b), + X(0x006dd69b), X(0x006faae6), X(0x0071830d), X(0x00735f10), + X(0x00753eef), X(0x007722a9), X(0x00790a3f), X(0x007af5b1), + X(0x007ce4fe), X(0x007ed826), X(0x0080cf29), X(0x0082ca08), + X(0x0084c8c2), X(0x0086cb57), X(0x0088d1c7), X(0x008adc11), + X(0x008cea37), X(0x008efc37), X(0x00911212), X(0x00932bc7), + X(0x00954957), X(0x00976ac2), X(0x00999006), X(0x009bb925), + X(0x009de61e), X(0x00a016f1), X(0x00a24b9e), X(0x00a48425), + X(0x00a6c086), X(0x00a900c0), X(0x00ab44d4), X(0x00ad8cc2), + X(0x00afd889), X(0x00b22829), X(0x00b47ba2), X(0x00b6d2f5), + X(0x00b92e21), X(0x00bb8d26), X(0x00bdf004), X(0x00c056ba), + X(0x00c2c149), X(0x00c52fb1), X(0x00c7a1f1), X(0x00ca180a), + X(0x00cc91fb), X(0x00cf0fc5), X(0x00d19166), X(0x00d416df), + X(0x00d6a031), X(0x00d92d5a), X(0x00dbbe5b), X(0x00de5333), + X(0x00e0ebe3), X(0x00e3886b), X(0x00e628c9), X(0x00e8ccff), + X(0x00eb750c), X(0x00ee20f0), X(0x00f0d0ab), X(0x00f3843d), + X(0x00f63ba5), X(0x00f8f6e4), X(0x00fbb5fa), X(0x00fe78e5), + X(0x01013fa7), X(0x01040a3f), X(0x0106d8ae), X(0x0109aaf2), + X(0x010c810c), X(0x010f5afb), X(0x011238c0), X(0x01151a5b), + X(0x0117ffcb), X(0x011ae910), X(0x011dd62a), X(0x0120c719), + X(0x0123bbdd), X(0x0126b476), X(0x0129b0e4), X(0x012cb126), + X(0x012fb53c), X(0x0132bd27), X(0x0135c8e6), X(0x0138d879), + X(0x013bebdf), X(0x013f031a), X(0x01421e28), X(0x01453d0a), + X(0x01485fbf), X(0x014b8648), X(0x014eb0a4), X(0x0151ded2), + X(0x015510d4), X(0x015846a8), X(0x015b8050), X(0x015ebdc9), + X(0x0161ff15), X(0x01654434), X(0x01688d24), X(0x016bd9e6), + X(0x016f2a7b), X(0x01727ee1), X(0x0175d718), X(0x01793321), + X(0x017c92fc), X(0x017ff6a7), X(0x01835e24), X(0x0186c972), + X(0x018a3890), X(0x018dab7f), X(0x0191223f), X(0x01949ccf), + X(0x01981b2f), X(0x019b9d5f), X(0x019f235f), X(0x01a2ad2f), + X(0x01a63acf), X(0x01a9cc3e), X(0x01ad617c), X(0x01b0fa8a), + X(0x01b49767), X(0x01b83813), X(0x01bbdc8d), X(0x01bf84d6), + X(0x01c330ee), X(0x01c6e0d4), X(0x01ca9488), X(0x01ce4c0b), + X(0x01d2075b), X(0x01d5c679), X(0x01d98964), X(0x01dd501d), + X(0x01e11aa3), X(0x01e4e8f6), X(0x01e8bb17), X(0x01ec9104), + X(0x01f06abd), X(0x01f44844), X(0x01f82996), X(0x01fc0eb5), + X(0x01fff7a0), X(0x0203e456), X(0x0207d4d9), X(0x020bc926), + X(0x020fc140), X(0x0213bd24), X(0x0217bcd4), X(0x021bc04e), + X(0x021fc793), X(0x0223d2a3), X(0x0227e17d), X(0x022bf421), + X(0x02300a90), X(0x023424c8), X(0x023842ca), X(0x023c6495), + X(0x02408a2a), X(0x0244b389), X(0x0248e0b0), X(0x024d11a0), + X(0x02514659), X(0x02557eda), X(0x0259bb24), X(0x025dfb35), + X(0x02623f0f), X(0x026686b1), X(0x026ad21a), X(0x026f214b), + X(0x02737443), X(0x0277cb02), X(0x027c2588), X(0x028083d5), + X(0x0284e5e9), X(0x02894bc2), X(0x028db562), X(0x029222c8), + X(0x029693f4), X(0x029b08e6), X(0x029f819d), X(0x02a3fe19), + X(0x02a87e5b), X(0x02ad0261), X(0x02b18a2c), X(0x02b615bb), + X(0x02baa50f), X(0x02bf3827), X(0x02c3cf03), X(0x02c869a3), + X(0x02cd0807), X(0x02d1aa2d), X(0x02d65017), X(0x02daf9c4), + X(0x02dfa734), X(0x02e45866), X(0x02e90d5b), X(0x02edc612), + X(0x02f2828b), X(0x02f742c6), X(0x02fc06c3), X(0x0300ce80), + X(0x030599ff), X(0x030a6940), X(0x030f3c40), X(0x03141302), + X(0x0318ed84), X(0x031dcbc6), X(0x0322adc8), X(0x0327938a), + X(0x032c7d0c), X(0x03316a4c), X(0x03365b4d), X(0x033b500c), + X(0x03404889), X(0x034544c6), X(0x034a44c0), X(0x034f4879), + X(0x03544ff0), X(0x03595b24), X(0x035e6a16), X(0x03637cc5), + X(0x03689331), X(0x036dad5a), X(0x0372cb40), X(0x0377ece2), + X(0x037d1240), X(0x03823b5a), X(0x03876830), X(0x038c98c1), + X(0x0391cd0e), X(0x03970516), X(0x039c40d8), X(0x03a18055), + X(0x03a6c38d), X(0x03ac0a7f), X(0x03b1552b), X(0x03b6a390), + X(0x03bbf5af), X(0x03c14b88), X(0x03c6a519), X(0x03cc0263), + X(0x03d16366), X(0x03d6c821), X(0x03dc3094), X(0x03e19cc0), + X(0x03e70ca2), X(0x03ec803d), X(0x03f1f78e), X(0x03f77296), + X(0x03fcf155), X(0x040273cb), X(0x0407f9f7), X(0x040d83d9), + X(0x04131170), X(0x0418a2bd), X(0x041e37c0), X(0x0423d077), + X(0x04296ce4), X(0x042f0d04), X(0x0434b0da), X(0x043a5863), + X(0x044003a0), X(0x0445b290), X(0x044b6534), X(0x04511b8b), + X(0x0456d595), X(0x045c9352), X(0x046254c1), X(0x046819e1), + X(0x046de2b4), X(0x0473af39), X(0x04797f6e), X(0x047f5355), + X(0x04852aec), X(0x048b0635), X(0x0490e52d), X(0x0496c7d6), + X(0x049cae2e), X(0x04a29836), X(0x04a885ed), X(0x04ae7753), + X(0x04b46c68), X(0x04ba652b), X(0x04c0619d), X(0x04c661bc), + X(0x04cc658a), X(0x04d26d04), X(0x04d8782c), X(0x04de8701), + X(0x04e49983), X(0x04eaafb0), X(0x04f0c98a), X(0x04f6e710), + X(0x04fd0842), X(0x05032d1e), X(0x050955a6), X(0x050f81d8), + X(0x0515b1b5), X(0x051be53d), X(0x05221c6e), X(0x05285748), + X(0x052e95cd), X(0x0534d7fa), X(0x053b1dd0), X(0x0541674e), + X(0x0547b475), X(0x054e0544), X(0x055459bb), X(0x055ab1d9), + X(0x05610d9e), X(0x05676d0a), X(0x056dd01c), X(0x057436d5), + X(0x057aa134), X(0x05810f38), X(0x058780e2), X(0x058df631), + X(0x05946f25), X(0x059aebbe), X(0x05a16bfa), X(0x05a7efdb), + X(0x05ae775f), X(0x05b50287), X(0x05bb9152), X(0x05c223c0), + X(0x05c8b9d0), X(0x05cf5382), X(0x05d5f0d6), X(0x05dc91cc), + X(0x05e33663), X(0x05e9de9c), X(0x05f08a75), X(0x05f739ee), + X(0x05fded07), X(0x0604a3c0), X(0x060b5e19), X(0x06121c11), + X(0x0618dda8), X(0x061fa2dd), X(0x06266bb1), X(0x062d3822), + X(0x06340831), X(0x063adbde), X(0x0641b328), X(0x06488e0e), + X(0x064f6c91), X(0x06564eaf), X(0x065d346a), X(0x06641dc0), + X(0x066b0ab1), X(0x0671fb3d), X(0x0678ef64), X(0x067fe724), + X(0x0686e27f), X(0x068de173), X(0x0694e400), X(0x069bea27), + X(0x06a2f3e6), X(0x06aa013d), X(0x06b1122c), X(0x06b826b3), + X(0x06bf3ed1), X(0x06c65a86), X(0x06cd79d1), X(0x06d49cb3), + X(0x06dbc32b), X(0x06e2ed38), X(0x06ea1adb), X(0x06f14c13), + X(0x06f880df), X(0x06ffb940), X(0x0706f535), X(0x070e34bd), + X(0x071577d9), X(0x071cbe88), X(0x072408c9), X(0x072b569d), + X(0x0732a802), X(0x0739fcf9), X(0x07415582), X(0x0748b19b), + X(0x07501145), X(0x0757747f), X(0x075edb49), X(0x076645a3), + X(0x076db38c), X(0x07752503), X(0x077c9a09), X(0x0784129e), + X(0x078b8ec0), X(0x07930e70), X(0x079a91ac), X(0x07a21876), + X(0x07a9a2cc), X(0x07b130ad), X(0x07b8c21b), X(0x07c05714), + X(0x07c7ef98), X(0x07cf8ba6), X(0x07d72b3f), X(0x07dece62), + X(0x07e6750e), X(0x07ee1f43), X(0x07f5cd01), X(0x07fd7e48), + X(0x08053316), X(0x080ceb6d), X(0x0814a74a), X(0x081c66af), + X(0x0824299a), X(0x082bf00c), X(0x0833ba03), X(0x083b8780), + X(0x08435882), X(0x084b2d09), X(0x08530514), X(0x085ae0a3), + X(0x0862bfb6), X(0x086aa24c), X(0x08728865), X(0x087a7201), + X(0x08825f1e), X(0x088a4fbe), X(0x089243de), X(0x089a3b80), + X(0x08a236a2), X(0x08aa3545), X(0x08b23767), X(0x08ba3d09), + X(0x08c2462a), X(0x08ca52c9), X(0x08d262e7), X(0x08da7682), + X(0x08e28d9c), X(0x08eaa832), X(0x08f2c645), X(0x08fae7d4), + X(0x09030cdf), X(0x090b3566), X(0x09136168), X(0x091b90e5), + X(0x0923c3dc), X(0x092bfa4d), X(0x09343437), X(0x093c719b), + X(0x0944b277), X(0x094cf6cc), X(0x09553e99), X(0x095d89dd), + X(0x0965d899), X(0x096e2acb), X(0x09768073), X(0x097ed991), + X(0x09873625), X(0x098f962e), X(0x0997f9ac), X(0x09a0609e), + X(0x09a8cb04), X(0x09b138dd), X(0x09b9aa29), X(0x09c21ee8), + X(0x09ca9719), X(0x09d312bc), X(0x09db91d0), X(0x09e41456), + X(0x09ec9a4b), X(0x09f523b1), X(0x09fdb087), X(0x0a0640cc), + X(0x0a0ed47f), X(0x0a176ba2), X(0x0a200632), X(0x0a28a42f), + X(0x0a31459a), X(0x0a39ea72), X(0x0a4292b5), X(0x0a4b3e65), + X(0x0a53ed80), X(0x0a5ca006), X(0x0a6555f7), X(0x0a6e0f51), + X(0x0a76cc16), X(0x0a7f8c44), X(0x0a884fda), X(0x0a9116d9), + X(0x0a99e140), X(0x0aa2af0e), X(0x0aab8043), X(0x0ab454df), + X(0x0abd2ce1), X(0x0ac60849), X(0x0acee716), X(0x0ad7c948), + X(0x0ae0aedf), X(0x0ae997d9), X(0x0af28437), X(0x0afb73f7), + X(0x0b04671b), X(0x0b0d5da0), X(0x0b165788), X(0x0b1f54d0), + X(0x0b285579), X(0x0b315983), X(0x0b3a60ec), X(0x0b436bb5), + X(0x0b4c79dd), X(0x0b558b63), X(0x0b5ea048), X(0x0b67b88a), + X(0x0b70d429), X(0x0b79f324), X(0x0b83157c), X(0x0b8c3b30), + X(0x0b95643f), X(0x0b9e90a8), X(0x0ba7c06c), X(0x0bb0f38a), + X(0x0bba2a01), X(0x0bc363d1), X(0x0bcca0f9), X(0x0bd5e17a), + X(0x0bdf2552), X(0x0be86c81), X(0x0bf1b706), X(0x0bfb04e2), + X(0x0c045613), X(0x0c0daa99), X(0x0c170274), X(0x0c205da3), + X(0x0c29bc25), X(0x0c331dfb), X(0x0c3c8323), X(0x0c45eb9e), + X(0x0c4f576a), X(0x0c58c688), X(0x0c6238f6), X(0x0c6baeb5), + X(0x0c7527c3), X(0x0c7ea421), X(0x0c8823cd), X(0x0c91a6c8), + X(0x0c9b2d10), X(0x0ca4b6a6), X(0x0cae4389), X(0x0cb7d3b8), + X(0x0cc16732), X(0x0ccafdf8), X(0x0cd49809), X(0x0cde3564), + X(0x0ce7d609), X(0x0cf179f7), X(0x0cfb212e), X(0x0d04cbad), + X(0x0d0e7974), X(0x0d182a83), X(0x0d21ded8), X(0x0d2b9673), + X(0x0d355154), X(0x0d3f0f7b), X(0x0d48d0e6), X(0x0d529595), + X(0x0d5c5d88), X(0x0d6628be), X(0x0d6ff737), X(0x0d79c8f2), + X(0x0d839dee), X(0x0d8d762c), X(0x0d9751aa), X(0x0da13068), + X(0x0dab1266), X(0x0db4f7a3), X(0x0dbee01e), X(0x0dc8cbd8), + X(0x0dd2bace), X(0x0ddcad02), X(0x0de6a272), X(0x0df09b1e), + X(0x0dfa9705), X(0x0e049627), X(0x0e0e9883), X(0x0e189e19), + X(0x0e22a6e8), X(0x0e2cb2f0), X(0x0e36c230), X(0x0e40d4a8), + X(0x0e4aea56), X(0x0e55033b), X(0x0e5f1f56), X(0x0e693ea7), + X(0x0e73612c), X(0x0e7d86e5), X(0x0e87afd3), X(0x0e91dbf3), + X(0x0e9c0b47), X(0x0ea63dcc), X(0x0eb07383), X(0x0ebaac6b), + X(0x0ec4e883), X(0x0ecf27cc), X(0x0ed96a44), X(0x0ee3afea), + X(0x0eedf8bf), X(0x0ef844c2), X(0x0f0293f2), X(0x0f0ce64e), + X(0x0f173bd6), X(0x0f21948a), X(0x0f2bf069), X(0x0f364f72), + X(0x0f40b1a5), X(0x0f4b1701), X(0x0f557f86), X(0x0f5feb32), + X(0x0f6a5a07), X(0x0f74cc02), X(0x0f7f4124), X(0x0f89b96b), + X(0x0f9434d8), X(0x0f9eb369), X(0x0fa9351e), X(0x0fb3b9f7), + X(0x0fbe41f3), X(0x0fc8cd11), X(0x0fd35b51), X(0x0fddecb2), + X(0x0fe88134), X(0x0ff318d6), X(0x0ffdb397), X(0x10085177), + X(0x1012f275), X(0x101d9691), X(0x10283dca), X(0x1032e81f), + X(0x103d9591), X(0x1048461e), X(0x1052f9c5), X(0x105db087), + X(0x10686a62), X(0x10732756), X(0x107de763), X(0x1088aa87), + X(0x109370c2), X(0x109e3a14), X(0x10a9067c), X(0x10b3d5f9), + X(0x10bea88b), X(0x10c97e31), X(0x10d456eb), X(0x10df32b8), + X(0x10ea1197), X(0x10f4f387), X(0x10ffd889), X(0x110ac09b), + X(0x1115abbe), X(0x112099ef), X(0x112b8b2f), X(0x11367f7d), + X(0x114176d9), X(0x114c7141), X(0x11576eb6), X(0x11626f36), + X(0x116d72c1), X(0x11787957), X(0x118382f6), X(0x118e8f9e), + X(0x11999f4f), X(0x11a4b208), X(0x11afc7c7), X(0x11bae08e), + X(0x11c5fc5a), X(0x11d11b2c), X(0x11dc3d02), X(0x11e761dd), + X(0x11f289ba), X(0x11fdb49b), X(0x1208e27e), X(0x12141362), + X(0x121f4748), X(0x122a7e2d), X(0x1235b812), X(0x1240f4f6), + X(0x124c34d9), X(0x125777b9), X(0x1262bd96), X(0x126e0670), + X(0x12795245), X(0x1284a115), X(0x128ff2e0), X(0x129b47a5), + X(0x12a69f63), X(0x12b1fa19), X(0x12bd57c7), X(0x12c8b86c), + X(0x12d41c08), X(0x12df829a), X(0x12eaec21), X(0x12f6589d), + X(0x1301c80c), X(0x130d3a6f), X(0x1318afc4), X(0x1324280b), + X(0x132fa344), X(0x133b216d), X(0x1346a286), X(0x1352268e), + X(0x135dad85), X(0x1369376a), X(0x1374c43c), X(0x138053fb), + X(0x138be6a5), X(0x13977c3b), X(0x13a314bc), X(0x13aeb026), + X(0x13ba4e79), X(0x13c5efb5), X(0x13d193d9), X(0x13dd3ae4), + X(0x13e8e4d6), X(0x13f491ad), X(0x1400416a), X(0x140bf40b), + X(0x1417a98f), X(0x142361f7), X(0x142f1d41), X(0x143adb6d), + X(0x14469c7a), X(0x14526067), X(0x145e2734), X(0x1469f0df), + X(0x1475bd69), X(0x14818cd0), X(0x148d5f15), X(0x14993435), + X(0x14a50c31), X(0x14b0e708), X(0x14bcc4b8), X(0x14c8a542), + X(0x14d488a5), X(0x14e06edf), X(0x14ec57f1), X(0x14f843d9), + X(0x15043297), X(0x1510242b), X(0x151c1892), X(0x15280fcd), + X(0x153409dc), X(0x154006bc), X(0x154c066e), X(0x155808f1), + X(0x15640e44), X(0x15701666), X(0x157c2157), X(0x15882f16), + X(0x15943fa2), X(0x15a052fb), X(0x15ac691f), X(0x15b8820f), + X(0x15c49dc8), X(0x15d0bc4c), X(0x15dcdd98), X(0x15e901ad), + X(0x15f52888), X(0x1601522b), X(0x160d7e93), X(0x1619adc1), + X(0x1625dfb3), X(0x16321469), X(0x163e4be2), X(0x164a861d), + X(0x1656c31a), X(0x166302d8), X(0x166f4555), X(0x167b8a92), + X(0x1687d28e), X(0x16941d47), X(0x16a06abe), X(0x16acbaf0), + X(0x16b90ddf), X(0x16c56388), X(0x16d1bbeb), X(0x16de1708), + X(0x16ea74dd), X(0x16f6d56a), X(0x170338ae), X(0x170f9ea8), + X(0x171c0758), X(0x172872bd), X(0x1734e0d6), X(0x174151a2), + X(0x174dc520), X(0x175a3b51), X(0x1766b432), X(0x17732fc4), + X(0x177fae05), X(0x178c2ef4), X(0x1798b292), X(0x17a538dd), + X(0x17b1c1d4), X(0x17be4d77), X(0x17cadbc5), X(0x17d76cbc), + X(0x17e4005e), X(0x17f096a7), X(0x17fd2f98), X(0x1809cb31), + X(0x1816696f), X(0x18230a53), X(0x182faddc), X(0x183c5408), + X(0x1848fcd8), X(0x1855a849), X(0x1862565d), X(0x186f0711), + X(0x187bba64), X(0x18887057), X(0x189528e9), X(0x18a1e418), + X(0x18aea1e3), X(0x18bb624b), X(0x18c8254e), X(0x18d4eaeb), + X(0x18e1b321), X(0x18ee7df1), X(0x18fb4b58), X(0x19081b57), + X(0x1914edec), X(0x1921c317), X(0x192e9ad6), X(0x193b7529), + X(0x19485210), X(0x19553189), X(0x19621393), X(0x196ef82e), + X(0x197bdf59), X(0x1988c913), X(0x1995b55c), X(0x19a2a432), + X(0x19af9595), X(0x19bc8983), X(0x19c97ffd), X(0x19d67900), + X(0x19e3748e), X(0x19f072a3), X(0x19fd7341), X(0x1a0a7665), + X(0x1a177c10), X(0x1a248440), X(0x1a318ef4), X(0x1a3e9c2c), + X(0x1a4babe7), X(0x1a58be24), X(0x1a65d2e2), X(0x1a72ea20), + X(0x1a8003de), X(0x1a8d201a), X(0x1a9a3ed5), X(0x1aa7600c), + X(0x1ab483bf), X(0x1ac1a9ee), X(0x1aced297), X(0x1adbfdba), + X(0x1ae92b56), X(0x1af65b69), X(0x1b038df4), X(0x1b10c2f5), + X(0x1b1dfa6b), X(0x1b2b3456), X(0x1b3870b5), X(0x1b45af87), + X(0x1b52f0ca), X(0x1b60347f), X(0x1b6d7aa4), X(0x1b7ac339), + X(0x1b880e3c), X(0x1b955bad), X(0x1ba2ab8b), X(0x1baffdd5), + X(0x1bbd528a), X(0x1bcaa9a9), X(0x1bd80332), X(0x1be55f24), + X(0x1bf2bd7d), X(0x1c001e3d), X(0x1c0d8164), X(0x1c1ae6ef), + X(0x1c284edf), X(0x1c35b932), X(0x1c4325e7), X(0x1c5094fe), + X(0x1c5e0677), X(0x1c6b7a4f), X(0x1c78f086), X(0x1c86691b), + X(0x1c93e40d), X(0x1ca1615c), X(0x1caee107), X(0x1cbc630c), + X(0x1cc9e76b), X(0x1cd76e23), X(0x1ce4f733), X(0x1cf2829a), + X(0x1d001057), X(0x1d0da06a), X(0x1d1b32d1), X(0x1d28c78c), + X(0x1d365e9a), X(0x1d43f7f9), X(0x1d5193a9), X(0x1d5f31aa), + X(0x1d6cd1f9), X(0x1d7a7497), X(0x1d881982), X(0x1d95c0ba), + X(0x1da36a3d), X(0x1db1160a), X(0x1dbec422), X(0x1dcc7482), + X(0x1dda272b), X(0x1de7dc1a), X(0x1df59350), X(0x1e034ccb), + X(0x1e11088a), X(0x1e1ec68c), X(0x1e2c86d1), X(0x1e3a4958), + X(0x1e480e20), X(0x1e55d527), X(0x1e639e6d), X(0x1e7169f1), + X(0x1e7f37b2), X(0x1e8d07b0), X(0x1e9ad9e8), X(0x1ea8ae5b), + X(0x1eb68507), X(0x1ec45dec), X(0x1ed23908), X(0x1ee0165b), + X(0x1eedf5e4), X(0x1efbd7a1), X(0x1f09bb92), X(0x1f17a1b6), + X(0x1f258a0d), X(0x1f337494), X(0x1f41614b), X(0x1f4f5032), + X(0x1f5d4147), X(0x1f6b3489), X(0x1f7929f7), X(0x1f872192), + X(0x1f951b56), X(0x1fa31744), X(0x1fb1155b), X(0x1fbf159a), + X(0x1fcd17ff), X(0x1fdb1c8b), X(0x1fe9233b), X(0x1ff72c0f), + X(0x20053706), X(0x20134420), X(0x2021535a), X(0x202f64b4), + X(0x203d782e), X(0x204b8dc6), X(0x2059a57c), X(0x2067bf4e), + X(0x2075db3b), X(0x2083f943), X(0x20921964), X(0x20a03b9e), + X(0x20ae5fef), X(0x20bc8657), X(0x20caaed5), X(0x20d8d967), + X(0x20e7060e), X(0x20f534c7), X(0x21036592), X(0x2111986e), + X(0x211fcd59), X(0x212e0454), X(0x213c3d5d), X(0x214a7873), + X(0x2158b594), X(0x2166f4c1), X(0x217535f8), X(0x21837938), + X(0x2191be81), X(0x21a005d0), X(0x21ae4f26), X(0x21bc9a81), + X(0x21cae7e0), X(0x21d93743), X(0x21e788a8), X(0x21f5dc0e), + X(0x22043174), X(0x221288da), X(0x2220e23e), X(0x222f3da0), + X(0x223d9afe), X(0x224bfa58), X(0x225a5bac), X(0x2268bef9), + X(0x2277243f), X(0x22858b7d), X(0x2293f4b0), X(0x22a25fda), + X(0x22b0ccf8), X(0x22bf3c09), X(0x22cdad0d), X(0x22dc2002), + X(0x22ea94e8), X(0x22f90bbe), X(0x23078482), X(0x2315ff33), + X(0x23247bd1), X(0x2332fa5b), X(0x23417acf), X(0x234ffd2c), + X(0x235e8173), X(0x236d07a0), X(0x237b8fb4), X(0x238a19ae), + X(0x2398a58c), X(0x23a7334d), X(0x23b5c2f1), X(0x23c45477), + X(0x23d2e7dd), X(0x23e17d22), X(0x23f01446), X(0x23fead47), + X(0x240d4825), X(0x241be4dd), X(0x242a8371), X(0x243923dd), + X(0x2447c622), X(0x24566a3e), X(0x24651031), X(0x2473b7f8), + X(0x24826194), X(0x24910d03), X(0x249fba44), X(0x24ae6957), + X(0x24bd1a39), X(0x24cbccea), X(0x24da816a), X(0x24e937b7), + X(0x24f7efcf), X(0x2506a9b3), X(0x25156560), X(0x252422d6), + X(0x2532e215), X(0x2541a31a), X(0x255065e4), X(0x255f2a74), + X(0x256df0c7), X(0x257cb8dd), X(0x258b82b4), X(0x259a4e4c), + X(0x25a91ba4), X(0x25b7eaba), X(0x25c6bb8e), X(0x25d58e1e), + X(0x25e46269), X(0x25f3386e), X(0x2602102d), X(0x2610e9a4), + X(0x261fc4d3), X(0x262ea1b7), X(0x263d8050), X(0x264c609e), + X(0x265b429e), X(0x266a2650), X(0x26790bb3), X(0x2687f2c6), + X(0x2696db88), X(0x26a5c5f7), X(0x26b4b213), X(0x26c39fda), + X(0x26d28f4c), X(0x26e18067), X(0x26f0732b), X(0x26ff6796), + X(0x270e5da7), X(0x271d555d), X(0x272c4eb7), X(0x273b49b5), + X(0x274a4654), X(0x27594495), X(0x27684475), X(0x277745f4), + X(0x27864910), X(0x27954dc9), X(0x27a4541e), X(0x27b35c0d), + X(0x27c26596), X(0x27d170b7), X(0x27e07d6f), X(0x27ef8bbd), + X(0x27fe9ba0), X(0x280dad18), X(0x281cc022), X(0x282bd4be), + X(0x283aeaeb), X(0x284a02a7), X(0x28591bf2), X(0x286836cb), + X(0x28775330), X(0x28867120), X(0x2895909b), X(0x28a4b19e), + X(0x28b3d42a), X(0x28c2f83d), X(0x28d21dd5), X(0x28e144f3), + X(0x28f06d94), X(0x28ff97b8), X(0x290ec35d), X(0x291df082), + X(0x292d1f27), X(0x293c4f4a), X(0x294b80eb), X(0x295ab407), + X(0x2969e89e), X(0x29791eaf), X(0x29885639), X(0x29978f3b), + X(0x29a6c9b3), X(0x29b605a0), X(0x29c54302), X(0x29d481d7), + X(0x29e3c21e), X(0x29f303d6), X(0x2a0246fd), X(0x2a118b94), + X(0x2a20d198), X(0x2a301909), X(0x2a3f61e6), X(0x2a4eac2c), + X(0x2a5df7dc), X(0x2a6d44f4), X(0x2a7c9374), X(0x2a8be359), + X(0x2a9b34a2), X(0x2aaa8750), X(0x2ab9db60), X(0x2ac930d1), + X(0x2ad887a3), X(0x2ae7dfd3), X(0x2af73962), X(0x2b06944e), + X(0x2b15f096), X(0x2b254e38), X(0x2b34ad34), X(0x2b440d89), + X(0x2b536f34), X(0x2b62d236), X(0x2b72368d), X(0x2b819c38), + X(0x2b910336), X(0x2ba06b86), X(0x2bafd526), X(0x2bbf4015), + X(0x2bceac53), X(0x2bde19de), X(0x2bed88b5), X(0x2bfcf8d7), + X(0x2c0c6a43), X(0x2c1bdcf7), X(0x2c2b50f3), X(0x2c3ac635), + X(0x2c4a3cbd), X(0x2c59b488), X(0x2c692d97), X(0x2c78a7e7), + X(0x2c882378), X(0x2c97a049), X(0x2ca71e58), X(0x2cb69da4), + X(0x2cc61e2c), X(0x2cd59ff0), X(0x2ce522ed), X(0x2cf4a723), + X(0x2d042c90), X(0x2d13b334), X(0x2d233b0d), X(0x2d32c41a), + X(0x2d424e5a), X(0x2d51d9cc), X(0x2d61666e), X(0x2d70f440), + X(0x2d808340), X(0x2d90136e), X(0x2d9fa4c7), X(0x2daf374c), + X(0x2dbecafa), X(0x2dce5fd1), X(0x2dddf5cf), X(0x2ded8cf4), + X(0x2dfd253d), X(0x2e0cbeab), X(0x2e1c593b), X(0x2e2bf4ed), + X(0x2e3b91c0), X(0x2e4b2fb1), X(0x2e5acec1), X(0x2e6a6eee), + X(0x2e7a1037), X(0x2e89b29b), X(0x2e995618), X(0x2ea8faad), + X(0x2eb8a05a), X(0x2ec8471c), X(0x2ed7eef4), X(0x2ee797df), + X(0x2ef741dc), X(0x2f06eceb), X(0x2f16990a), X(0x2f264639), + X(0x2f35f475), X(0x2f45a3bd), X(0x2f555412), X(0x2f650570), + X(0x2f74b7d8), X(0x2f846b48), X(0x2f941fbe), X(0x2fa3d53a), + X(0x2fb38bbb), X(0x2fc3433f), X(0x2fd2fbc5), X(0x2fe2b54c), + X(0x2ff26fd3), X(0x30022b58), X(0x3011e7db), X(0x3021a55a), + X(0x303163d4), X(0x30412348), X(0x3050e3b5), X(0x3060a519), + X(0x30706773), X(0x30802ac3), X(0x308fef06), X(0x309fb43d), + X(0x30af7a65), X(0x30bf417d), X(0x30cf0985), X(0x30ded27a), + X(0x30ee9c5d), X(0x30fe672b), X(0x310e32e3), X(0x311dff85), + X(0x312dcd0f), X(0x313d9b80), X(0x314d6ad7), X(0x315d3b12), + X(0x316d0c30), X(0x317cde31), X(0x318cb113), X(0x319c84d4), + X(0x31ac5974), X(0x31bc2ef1), X(0x31cc054b), X(0x31dbdc7f), + X(0x31ebb48e), X(0x31fb8d74), X(0x320b6733), X(0x321b41c7), + X(0x322b1d31), X(0x323af96e), X(0x324ad67e), X(0x325ab45f), + X(0x326a9311), X(0x327a7291), X(0x328a52e0), X(0x329a33fb), + X(0x32aa15e1), X(0x32b9f892), X(0x32c9dc0c), X(0x32d9c04d), + X(0x32e9a555), X(0x32f98b22), X(0x330971b4), X(0x33195909), + X(0x3329411f), X(0x333929f6), X(0x3349138c), X(0x3358fde1), + X(0x3368e8f2), X(0x3378d4c0), X(0x3388c147), X(0x3398ae89), + X(0x33a89c82), X(0x33b88b32), X(0x33c87a98), X(0x33d86ab2), + X(0x33e85b80), X(0x33f84d00), X(0x34083f30), X(0x34183210), + X(0x3428259f), X(0x343819db), X(0x34480ec3), X(0x34580455), + X(0x3467fa92), X(0x3477f176), X(0x3487e902), X(0x3497e134), + X(0x34a7da0a), X(0x34b7d384), X(0x34c7cda0), X(0x34d7c85e), + X(0x34e7c3bb), X(0x34f7bfb7), X(0x3507bc50), X(0x3517b985), + X(0x3527b756), X(0x3537b5c0), X(0x3547b4c3), X(0x3557b45d), + X(0x3567b48d), X(0x3577b552), X(0x3587b6aa), X(0x3597b895), + X(0x35a7bb12), X(0x35b7be1e), X(0x35c7c1b9), X(0x35d7c5e1), + X(0x35e7ca96), X(0x35f7cfd6), X(0x3607d5a0), X(0x3617dbf3), + X(0x3627e2cd), X(0x3637ea2d), X(0x3647f212), X(0x3657fa7b), + X(0x36680366), X(0x36780cd2), X(0x368816bf), X(0x3698212b), + X(0x36a82c14), X(0x36b83779), X(0x36c8435a), X(0x36d84fb4), + X(0x36e85c88), X(0x36f869d2), X(0x37087793), X(0x371885c9), + X(0x37289473), X(0x3738a38f), X(0x3748b31d), X(0x3758c31a), + X(0x3768d387), X(0x3778e461), X(0x3788f5a7), X(0x37990759), + X(0x37a91975), X(0x37b92bf9), X(0x37c93ee4), X(0x37d95236), + X(0x37e965ed), X(0x37f97a08), X(0x38098e85), X(0x3819a363), + X(0x3829b8a2), X(0x3839ce3f), X(0x3849e43a), X(0x3859fa91), + X(0x386a1143), X(0x387a284f), X(0x388a3fb4), X(0x389a5770), + X(0x38aa6f83), X(0x38ba87ea), X(0x38caa0a5), X(0x38dab9b2), + X(0x38ead311), X(0x38faecbf), X(0x390b06bc), X(0x391b2107), + X(0x392b3b9e), X(0x393b5680), X(0x394b71ac), X(0x395b8d20), + X(0x396ba8dc), X(0x397bc4dd), X(0x398be124), X(0x399bfdae), + X(0x39ac1a7a), X(0x39bc3788), X(0x39cc54d5), X(0x39dc7261), + X(0x39ec902a), X(0x39fcae2f), X(0x3a0ccc70), X(0x3a1ceaea), + X(0x3a2d099c), X(0x3a3d2885), X(0x3a4d47a5), X(0x3a5d66f9), + X(0x3a6d8680), X(0x3a7da63a), X(0x3a8dc625), X(0x3a9de63f), + X(0x3aae0688), X(0x3abe26fe), X(0x3ace47a0), X(0x3ade686d), + X(0x3aee8963), X(0x3afeaa82), X(0x3b0ecbc7), X(0x3b1eed32), + X(0x3b2f0ec2), X(0x3b3f3075), X(0x3b4f524a), X(0x3b5f7440), + X(0x3b6f9656), X(0x3b7fb889), X(0x3b8fdada), X(0x3b9ffd46), + X(0x3bb01fce), X(0x3bc0426e), X(0x3bd06526), X(0x3be087f6), + X(0x3bf0aada), X(0x3c00cdd4), X(0x3c10f0e0), X(0x3c2113fe), + X(0x3c31372d), X(0x3c415a6b), X(0x3c517db7), X(0x3c61a110), + X(0x3c71c475), X(0x3c81e7e4), X(0x3c920b5c), X(0x3ca22edc), + X(0x3cb25262), X(0x3cc275ee), X(0x3cd2997e), X(0x3ce2bd11), + X(0x3cf2e0a6), X(0x3d03043b), X(0x3d1327cf), X(0x3d234b61), + X(0x3d336ef0), X(0x3d43927a), X(0x3d53b5ff), X(0x3d63d97c), + X(0x3d73fcf1), X(0x3d84205c), X(0x3d9443bd), X(0x3da46711), + X(0x3db48a58), X(0x3dc4ad91), X(0x3dd4d0ba), X(0x3de4f3d1), + X(0x3df516d7), X(0x3e0539c9), X(0x3e155ca6), X(0x3e257f6d), + X(0x3e35a21d), X(0x3e45c4b4), X(0x3e55e731), X(0x3e660994), + X(0x3e762bda), X(0x3e864e03), X(0x3e96700d), X(0x3ea691f7), + X(0x3eb6b3bf), X(0x3ec6d565), X(0x3ed6f6e8), X(0x3ee71845), + X(0x3ef7397c), X(0x3f075a8c), X(0x3f177b73), X(0x3f279c30), + X(0x3f37bcc2), X(0x3f47dd27), X(0x3f57fd5f), X(0x3f681d68), + X(0x3f783d40), X(0x3f885ce7), X(0x3f987c5c), X(0x3fa89b9c), + X(0x3fb8baa7), X(0x3fc8d97c), X(0x3fd8f819), X(0x3fe9167e), + X(0x3ff934a8), X(0x40095296), X(0x40197049), X(0x40298dbd), + X(0x4039aaf2), X(0x4049c7e7), X(0x4059e49a), X(0x406a010a), + X(0x407a1d36), X(0x408a391d), X(0x409a54bd), X(0x40aa7015), + X(0x40ba8b25), X(0x40caa5ea), X(0x40dac063), X(0x40eada90), + X(0x40faf46e), X(0x410b0dfe), X(0x411b273d), X(0x412b402a), + X(0x413b58c4), X(0x414b710a), X(0x415b88fa), X(0x416ba093), + X(0x417bb7d5), X(0x418bcebe), X(0x419be54c), X(0x41abfb7e), + X(0x41bc1153), X(0x41cc26ca), X(0x41dc3be2), X(0x41ec5099), + X(0x41fc64ef), X(0x420c78e1), X(0x421c8c6f), X(0x422c9f97), + X(0x423cb258), X(0x424cc4b2), X(0x425cd6a2), X(0x426ce827), + X(0x427cf941), X(0x428d09ee), X(0x429d1a2c), X(0x42ad29fb), + X(0x42bd3959), X(0x42cd4846), X(0x42dd56bf), X(0x42ed64c3), + X(0x42fd7252), X(0x430d7f6a), X(0x431d8c0a), X(0x432d9831), + X(0x433da3dd), X(0x434daf0d), X(0x435db9c0), X(0x436dc3f5), + X(0x437dcdab), X(0x438dd6df), X(0x439ddf92), X(0x43ade7c1), + X(0x43bdef6c), X(0x43cdf691), X(0x43ddfd2f), X(0x43ee0345), + X(0x43fe08d2), X(0x440e0dd4), X(0x441e124b), X(0x442e1634), + X(0x443e198f), X(0x444e1c5a), X(0x445e1e95), X(0x446e203e), + X(0x447e2153), X(0x448e21d5), X(0x449e21c0), X(0x44ae2115), + X(0x44be1fd1), X(0x44ce1df4), X(0x44de1b7d), X(0x44ee186a), + X(0x44fe14ba), X(0x450e106b), X(0x451e0b7e), X(0x452e05ef), + X(0x453dffbf), X(0x454df8eb), X(0x455df173), X(0x456de956), + X(0x457de092), X(0x458dd726), X(0x459dcd10), X(0x45adc251), + X(0x45bdb6e5), X(0x45cdaacd), X(0x45dd9e06), X(0x45ed9091), + X(0x45fd826a), X(0x460d7392), X(0x461d6407), X(0x462d53c8), + X(0x463d42d4), X(0x464d3129), X(0x465d1ec6), X(0x466d0baa), + X(0x467cf7d3), X(0x468ce342), X(0x469ccdf3), X(0x46acb7e7), + X(0x46bca11c), X(0x46cc8990), X(0x46dc7143), X(0x46ec5833), + X(0x46fc3e5f), X(0x470c23c6), X(0x471c0867), X(0x472bec40), + X(0x473bcf50), X(0x474bb196), X(0x475b9311), X(0x476b73c0), + X(0x477b53a1), X(0x478b32b4), X(0x479b10f6), X(0x47aaee67), + X(0x47bacb06), X(0x47caa6d1), X(0x47da81c7), X(0x47ea5be7), + X(0x47fa3530), X(0x480a0da1), X(0x4819e537), X(0x4829bbf3), + X(0x483991d3), X(0x484966d6), X(0x48593afb), X(0x48690e3f), + X(0x4878e0a3), X(0x4888b225), X(0x489882c4), X(0x48a8527e), + X(0x48b82153), X(0x48c7ef41), X(0x48d7bc47), X(0x48e78863), + X(0x48f75396), X(0x49071ddc), X(0x4916e736), X(0x4926afa2), + X(0x4936771f), X(0x49463dac), X(0x49560347), X(0x4965c7ef), + X(0x49758ba4), X(0x49854e63), X(0x4995102c), X(0x49a4d0fe), + X(0x49b490d7), X(0x49c44fb6), X(0x49d40d9a), X(0x49e3ca82), + X(0x49f3866c), X(0x4a034159), X(0x4a12fb45), X(0x4a22b430), + X(0x4a326c19), X(0x4a4222ff), X(0x4a51d8e1), X(0x4a618dbd), + X(0x4a714192), X(0x4a80f45f), X(0x4a90a623), X(0x4aa056dd), + X(0x4ab0068b), X(0x4abfb52c), X(0x4acf62c0), X(0x4adf0f44), + X(0x4aeebab9), X(0x4afe651c), X(0x4b0e0e6c), X(0x4b1db6a9), + X(0x4b2d5dd1), X(0x4b3d03e2), X(0x4b4ca8dd), X(0x4b5c4cbf), + X(0x4b6bef88), X(0x4b7b9136), X(0x4b8b31c8), X(0x4b9ad13d), + X(0x4baa6f93), X(0x4bba0ccb), X(0x4bc9a8e2), X(0x4bd943d7), + X(0x4be8dda9), X(0x4bf87658), X(0x4c080de1), X(0x4c17a444), + X(0x4c27397f), X(0x4c36cd92), X(0x4c46607b), X(0x4c55f239), + X(0x4c6582cb), X(0x4c75122f), X(0x4c84a065), X(0x4c942d6c), + X(0x4ca3b942), X(0x4cb343e6), X(0x4cc2cd57), X(0x4cd25594), + X(0x4ce1dc9c), X(0x4cf1626d), X(0x4d00e707), X(0x4d106a68), + X(0x4d1fec8f), X(0x4d2f6d7a), X(0x4d3eed2a), X(0x4d4e6b9d), + X(0x4d5de8d1), X(0x4d6d64c5), X(0x4d7cdf79), X(0x4d8c58eb), + X(0x4d9bd11a), X(0x4dab4804), X(0x4dbabdaa), X(0x4dca3209), + X(0x4dd9a520), X(0x4de916ef), X(0x4df88774), X(0x4e07f6ae), + X(0x4e17649c), X(0x4e26d13c), X(0x4e363c8f), X(0x4e45a692), + X(0x4e550f44), X(0x4e6476a4), X(0x4e73dcb2), X(0x4e83416c), + X(0x4e92a4d1), X(0x4ea206df), X(0x4eb16796), X(0x4ec0c6f5), + X(0x4ed024fa), X(0x4edf81a5), X(0x4eeedcf3), X(0x4efe36e5), + X(0x4f0d8f79), X(0x4f1ce6ad), X(0x4f2c3c82), X(0x4f3b90f4), + X(0x4f4ae405), X(0x4f5a35b1), X(0x4f6985fa), X(0x4f78d4dc), + X(0x4f882257), X(0x4f976e6a), X(0x4fa6b914), X(0x4fb60254), + X(0x4fc54a28), X(0x4fd49090), X(0x4fe3d58b), X(0x4ff31917), + X(0x50025b33), X(0x50119bde), X(0x5020db17), X(0x503018dd), + X(0x503f552f), X(0x504e900b), X(0x505dc971), X(0x506d0160), + X(0x507c37d7), X(0x508b6cd3), X(0x509aa055), X(0x50a9d25b), + X(0x50b902e4), X(0x50c831ef), X(0x50d75f7b), X(0x50e68b87), + X(0x50f5b612), X(0x5104df1a), X(0x5114069f), X(0x51232ca0), + X(0x5132511a), X(0x5141740f), X(0x5150957b), X(0x515fb55f), + X(0x516ed3b8), X(0x517df087), X(0x518d0bca), X(0x519c257f), + X(0x51ab3da7), X(0x51ba543f), X(0x51c96947), X(0x51d87cbd), + X(0x51e78ea1), X(0x51f69ef1), X(0x5205adad), X(0x5214bad3), + X(0x5223c662), X(0x5232d05a), X(0x5241d8b9), X(0x5250df7d), + X(0x525fe4a7), X(0x526ee835), X(0x527dea26), X(0x528cea78), + X(0x529be92c), X(0x52aae63f), X(0x52b9e1b0), X(0x52c8db80), + X(0x52d7d3ac), X(0x52e6ca33), X(0x52f5bf15), X(0x5304b251), + X(0x5313a3e5), X(0x532293d0), X(0x53318212), X(0x53406ea8), + X(0x534f5993), X(0x535e42d2), X(0x536d2a62), X(0x537c1043), + X(0x538af475), X(0x5399d6f6), X(0x53a8b7c4), X(0x53b796e0), + X(0x53c67447), X(0x53d54ffa), X(0x53e429f6), X(0x53f3023b), + X(0x5401d8c8), X(0x5410ad9c), X(0x541f80b5), X(0x542e5213), + X(0x543d21b5), X(0x544bef9a), X(0x545abbc0), X(0x54698627), + X(0x54784ece), X(0x548715b3), X(0x5495dad6), X(0x54a49e35), + X(0x54b35fd0), X(0x54c21fa6), X(0x54d0ddb5), X(0x54df99fd), + X(0x54ee547c), X(0x54fd0d32), X(0x550bc41d), X(0x551a793d), + X(0x55292c91), X(0x5537de16), X(0x55468dce), X(0x55553bb6), + X(0x5563e7cd), X(0x55729213), X(0x55813a87), X(0x558fe127), + X(0x559e85f2), X(0x55ad28e9), X(0x55bbca08), X(0x55ca6950), + X(0x55d906c0), X(0x55e7a257), X(0x55f63c13), X(0x5604d3f4), + X(0x561369f8), X(0x5621fe1f), X(0x56309067), X(0x563f20d1), + X(0x564daf5a), X(0x565c3c02), X(0x566ac6c7), X(0x56794faa), + X(0x5687d6a8), X(0x56965bc1), X(0x56a4def4), X(0x56b36040), + X(0x56c1dfa4), X(0x56d05d1f), X(0x56ded8af), X(0x56ed5255), + X(0x56fbca0f), X(0x570a3fdc), X(0x5718b3bc), X(0x572725ac), + X(0x573595ad), X(0x574403bd), X(0x57526fdb), X(0x5760da07), + X(0x576f423f), X(0x577da883), X(0x578c0cd1), X(0x579a6f29), + X(0x57a8cf8a), X(0x57b72df2), X(0x57c58a61), X(0x57d3e4d6), + X(0x57e23d50), X(0x57f093cd), X(0x57fee84e), X(0x580d3ad1), + X(0x581b8b54), X(0x5829d9d8), X(0x5838265c), X(0x584670dd), + X(0x5854b95c), X(0x5862ffd8), X(0x5871444f), X(0x587f86c1), + X(0x588dc72c), X(0x589c0591), X(0x58aa41ed), X(0x58b87c40), + X(0x58c6b489), X(0x58d4eac7), X(0x58e31ef9), X(0x58f1511f), + X(0x58ff8137), X(0x590daf40), X(0x591bdb3a), X(0x592a0524), + X(0x59382cfc), X(0x594652c2), X(0x59547675), X(0x59629815), + X(0x5970b79f), X(0x597ed513), X(0x598cf071), X(0x599b09b7), + X(0x59a920e5), X(0x59b735f9), X(0x59c548f4), X(0x59d359d2), + X(0x59e16895), X(0x59ef753b), X(0x59fd7fc4), X(0x5a0b882d), + X(0x5a198e77), X(0x5a2792a0), X(0x5a3594a9), X(0x5a43948e), + X(0x5a519251), X(0x5a5f8df0), X(0x5a6d876a), X(0x5a7b7ebe), + X(0x5a8973ec), X(0x5a9766f2), X(0x5aa557d0), X(0x5ab34685), + X(0x5ac1330f), X(0x5acf1d6f), X(0x5add05a3), X(0x5aeaebaa), + X(0x5af8cf84), X(0x5b06b12f), X(0x5b1490ab), X(0x5b226df7), + X(0x5b304912), X(0x5b3e21fc), X(0x5b4bf8b2), X(0x5b59cd35), + X(0x5b679f84), X(0x5b756f9e), X(0x5b833d82), X(0x5b91092e), + X(0x5b9ed2a3), X(0x5bac99e0), X(0x5bba5ee3), X(0x5bc821ac), + X(0x5bd5e23a), X(0x5be3a08c), X(0x5bf15ca1), X(0x5bff1679), + X(0x5c0cce12), X(0x5c1a836c), X(0x5c283686), X(0x5c35e760), + X(0x5c4395f7), X(0x5c51424c), X(0x5c5eec5e), X(0x5c6c942b), + X(0x5c7a39b4), X(0x5c87dcf7), X(0x5c957df3), X(0x5ca31ca8), + X(0x5cb0b915), X(0x5cbe5338), X(0x5ccbeb12), X(0x5cd980a1), + X(0x5ce713e5), X(0x5cf4a4dd), X(0x5d023387), X(0x5d0fbfe4), + X(0x5d1d49f2), X(0x5d2ad1b1), X(0x5d38571f), X(0x5d45da3c), + X(0x5d535b08), X(0x5d60d981), X(0x5d6e55a7), X(0x5d7bcf78), + X(0x5d8946f5), X(0x5d96bc1c), X(0x5da42eec), X(0x5db19f65), + X(0x5dbf0d86), X(0x5dcc794e), X(0x5dd9e2bd), X(0x5de749d1), + X(0x5df4ae8a), X(0x5e0210e7), X(0x5e0f70e7), X(0x5e1cce8a), + X(0x5e2a29ce), X(0x5e3782b4), X(0x5e44d93a), X(0x5e522d5f), + X(0x5e5f7f23), X(0x5e6cce85), X(0x5e7a1b85), X(0x5e876620), + X(0x5e94ae58), X(0x5ea1f42a), X(0x5eaf3797), X(0x5ebc789d), + X(0x5ec9b73c), X(0x5ed6f372), X(0x5ee42d41), X(0x5ef164a5), + X(0x5efe999f), X(0x5f0bcc2f), X(0x5f18fc52), X(0x5f262a09), + X(0x5f335553), X(0x5f407e2f), X(0x5f4da49d), X(0x5f5ac89b), + X(0x5f67ea29), X(0x5f750946), X(0x5f8225f2), X(0x5f8f402b), + X(0x5f9c57f2), X(0x5fa96d44), X(0x5fb68023), X(0x5fc3908c), + X(0x5fd09e7f), X(0x5fdda9fc), X(0x5feab302), X(0x5ff7b990), + X(0x6004bda5), X(0x6011bf40), X(0x601ebe62), X(0x602bbb09), + X(0x6038b534), X(0x6045ace4), X(0x6052a216), X(0x605f94cb), + X(0x606c8502), X(0x607972b9), X(0x60865df2), X(0x609346aa), + X(0x60a02ce1), X(0x60ad1096), X(0x60b9f1c9), X(0x60c6d079), + X(0x60d3aca5), X(0x60e0864d), X(0x60ed5d70), X(0x60fa320d), + X(0x61070424), X(0x6113d3b4), X(0x6120a0bc), X(0x612d6b3c), + X(0x613a3332), X(0x6146f89f), X(0x6153bb82), X(0x61607bd9), + X(0x616d39a5), X(0x6179f4e5), X(0x6186ad98), X(0x619363bd), + X(0x61a01753), X(0x61acc85b), X(0x61b976d3), X(0x61c622bc), + X(0x61d2cc13), X(0x61df72d8), X(0x61ec170c), X(0x61f8b8ad), + X(0x620557ba), X(0x6211f434), X(0x621e8e18), X(0x622b2568), + X(0x6237ba21), X(0x62444c44), X(0x6250dbd0), X(0x625d68c4), + X(0x6269f320), X(0x62767ae2), X(0x6283000b), X(0x628f829a), + X(0x629c028e), X(0x62a87fe6), X(0x62b4faa2), X(0x62c172c2), + X(0x62cde844), X(0x62da5b29), X(0x62e6cb6e), X(0x62f33915), + X(0x62ffa41c), X(0x630c0c83), X(0x63187248), X(0x6324d56d), + X(0x633135ef), X(0x633d93ce), X(0x6349ef0b), X(0x635647a3), + X(0x63629d97), X(0x636ef0e6), X(0x637b418f), X(0x63878f92), + X(0x6393daef), X(0x63a023a4), X(0x63ac69b1), X(0x63b8ad15), + X(0x63c4edd1), X(0x63d12be3), X(0x63dd674b), X(0x63e9a008), + X(0x63f5d61a), X(0x64020980), X(0x640e3a39), X(0x641a6846), + X(0x642693a5), X(0x6432bc56), X(0x643ee258), X(0x644b05ab), + X(0x6457264e), X(0x64634441), X(0x646f5f83), X(0x647b7814), + X(0x64878df3), X(0x6493a120), X(0x649fb199), X(0x64abbf5f), + X(0x64b7ca71), X(0x64c3d2ce), X(0x64cfd877), X(0x64dbdb69), + X(0x64e7dba6), X(0x64f3d92b), X(0x64ffd3fa), X(0x650bcc11), + X(0x6517c16f), X(0x6523b415), X(0x652fa402), X(0x653b9134), + X(0x65477bad), X(0x6553636a), X(0x655f486d), X(0x656b2ab3), + X(0x65770a3d), X(0x6582e70a), X(0x658ec11a), X(0x659a986d), + X(0x65a66d00), X(0x65b23ed5), X(0x65be0deb), X(0x65c9da41), + X(0x65d5a3d7), X(0x65e16aac), X(0x65ed2ebf), X(0x65f8f011), + X(0x6604aea1), X(0x66106a6e), X(0x661c2377), X(0x6627d9be), + X(0x66338d40), X(0x663f3dfd), X(0x664aebf5), X(0x66569728), + X(0x66623f95), X(0x666de53b), X(0x6679881b), X(0x66852833), + X(0x6690c583), X(0x669c600b), X(0x66a7f7ca), X(0x66b38cc0), + X(0x66bf1eec), X(0x66caae4f), X(0x66d63ae6), X(0x66e1c4b3), + X(0x66ed4bb4), X(0x66f8cfea), X(0x67045153), X(0x670fcfef), + X(0x671b4bbe), X(0x6726c4bf), X(0x67323af3), X(0x673dae58), + X(0x67491eee), X(0x67548cb5), X(0x675ff7ab), X(0x676b5fd2), + X(0x6776c528), X(0x678227ad), X(0x678d8761), X(0x6798e443), + X(0x67a43e52), X(0x67af958f), X(0x67bae9f9), X(0x67c63b8f), + X(0x67d18a52), X(0x67dcd640), X(0x67e81f59), X(0x67f3659d), + X(0x67fea90c), X(0x6809e9a5), X(0x68152768), X(0x68206254), + X(0x682b9a68), X(0x6836cfa6), X(0x6842020b), X(0x684d3199), + X(0x68585e4d), X(0x68638829), X(0x686eaf2b), X(0x6879d354), + X(0x6884f4a2), X(0x68901316), X(0x689b2eb0), X(0x68a6476d), + X(0x68b15d50), X(0x68bc7056), X(0x68c78080), X(0x68d28dcd), + X(0x68dd983e), X(0x68e89fd0), X(0x68f3a486), X(0x68fea65d), + X(0x6909a555), X(0x6914a16f), X(0x691f9aa9), X(0x692a9104), + X(0x69358480), X(0x6940751b), X(0x694b62d5), X(0x69564daf), + X(0x696135a7), X(0x696c1abe), X(0x6976fcf3), X(0x6981dc46), + X(0x698cb8b6), X(0x69979243), X(0x69a268ed), X(0x69ad3cb4), + X(0x69b80d97), X(0x69c2db96), X(0x69cda6b0), X(0x69d86ee5), + X(0x69e33436), X(0x69edf6a1), X(0x69f8b626), X(0x6a0372c5), + X(0x6a0e2c7e), X(0x6a18e350), X(0x6a23973c), X(0x6a2e4840), + X(0x6a38f65d), X(0x6a43a191), X(0x6a4e49de), X(0x6a58ef42), + X(0x6a6391be), X(0x6a6e3151), X(0x6a78cdfa), X(0x6a8367ba), + X(0x6a8dfe90), X(0x6a98927c), X(0x6aa3237d), X(0x6aadb194), + X(0x6ab83cc0), X(0x6ac2c500), X(0x6acd4a55), X(0x6ad7ccbf), + X(0x6ae24c3c), X(0x6aecc8cd), X(0x6af74271), X(0x6b01b929), + X(0x6b0c2cf4), X(0x6b169dd1), X(0x6b210bc1), X(0x6b2b76c2), + X(0x6b35ded6), X(0x6b4043fc), X(0x6b4aa632), X(0x6b55057a), + X(0x6b5f61d3), X(0x6b69bb3d), X(0x6b7411b7), X(0x6b7e6541), + X(0x6b88b5db), X(0x6b930385), X(0x6b9d4e3f), X(0x6ba79607), + X(0x6bb1dadf), X(0x6bbc1cc6), X(0x6bc65bbb), X(0x6bd097bf), + X(0x6bdad0d0), X(0x6be506f0), X(0x6bef3a1d), X(0x6bf96a58), + X(0x6c0397a0), X(0x6c0dc1f5), X(0x6c17e957), X(0x6c220dc6), + X(0x6c2c2f41), X(0x6c364dc9), X(0x6c40695c), X(0x6c4a81fc), + X(0x6c5497a7), X(0x6c5eaa5d), X(0x6c68ba1f), X(0x6c72c6eb), + X(0x6c7cd0c3), X(0x6c86d7a6), X(0x6c90db92), X(0x6c9adc8a), + X(0x6ca4da8b), X(0x6caed596), X(0x6cb8cdab), X(0x6cc2c2ca), + X(0x6cccb4f2), X(0x6cd6a424), X(0x6ce0905e), X(0x6cea79a1), + X(0x6cf45fee), X(0x6cfe4342), X(0x6d0823a0), X(0x6d120105), + X(0x6d1bdb73), X(0x6d25b2e8), X(0x6d2f8765), X(0x6d3958ea), + X(0x6d432777), X(0x6d4cf30a), X(0x6d56bba5), X(0x6d608147), + X(0x6d6a43f0), X(0x6d7403a0), X(0x6d7dc056), X(0x6d877a13), + X(0x6d9130d6), X(0x6d9ae4a0), X(0x6da4956f), X(0x6dae4345), + X(0x6db7ee20), X(0x6dc19601), X(0x6dcb3ae7), X(0x6dd4dcd3), + X(0x6dde7bc4), X(0x6de817bb), X(0x6df1b0b6), X(0x6dfb46b7), + X(0x6e04d9bc), X(0x6e0e69c7), X(0x6e17f6d5), X(0x6e2180e9), + X(0x6e2b0801), X(0x6e348c1d), X(0x6e3e0d3d), X(0x6e478b62), + X(0x6e51068a), X(0x6e5a7eb7), X(0x6e63f3e7), X(0x6e6d661b), + X(0x6e76d552), X(0x6e80418e), X(0x6e89aacc), X(0x6e93110f), + X(0x6e9c7454), X(0x6ea5d49d), X(0x6eaf31e9), X(0x6eb88c37), + X(0x6ec1e389), X(0x6ecb37de), X(0x6ed48936), X(0x6eddd790), + X(0x6ee722ee), X(0x6ef06b4d), X(0x6ef9b0b0), X(0x6f02f315), + X(0x6f0c327c), X(0x6f156ee6), X(0x6f1ea852), X(0x6f27dec1), + X(0x6f311232), X(0x6f3a42a5), X(0x6f43701a), X(0x6f4c9a91), + X(0x6f55c20a), X(0x6f5ee686), X(0x6f680803), X(0x6f712682), + X(0x6f7a4203), X(0x6f835a86), X(0x6f8c700b), X(0x6f958291), + X(0x6f9e921a), X(0x6fa79ea4), X(0x6fb0a830), X(0x6fb9aebd), + X(0x6fc2b24c), X(0x6fcbb2dd), X(0x6fd4b06f), X(0x6fddab03), + X(0x6fe6a299), X(0x6fef9730), X(0x6ff888c9), X(0x70017763), + X(0x700a62ff), X(0x70134b9c), X(0x701c313b), X(0x702513dc), + X(0x702df37e), X(0x7036d021), X(0x703fa9c6), X(0x7048806d), + X(0x70515415), X(0x705a24bf), X(0x7062f26b), X(0x706bbd17), + X(0x707484c6), X(0x707d4976), X(0x70860b28), X(0x708ec9dc), + X(0x70978591), X(0x70a03e48), X(0x70a8f400), X(0x70b1a6bb), + X(0x70ba5677), X(0x70c30335), X(0x70cbacf5), X(0x70d453b6), + X(0x70dcf77a), X(0x70e59840), X(0x70ee3607), X(0x70f6d0d1), + X(0x70ff689d), X(0x7107fd6b), X(0x71108f3b), X(0x71191e0d), + X(0x7121a9e2), X(0x712a32b9), X(0x7132b892), X(0x713b3b6e), + X(0x7143bb4c), X(0x714c382d), X(0x7154b211), X(0x715d28f7), + X(0x71659ce0), X(0x716e0dcc), X(0x71767bbb), X(0x717ee6ac), + X(0x71874ea1), X(0x718fb399), X(0x71981594), X(0x71a07493), + X(0x71a8d094), X(0x71b1299a), X(0x71b97fa2), X(0x71c1d2af), + X(0x71ca22bf), X(0x71d26fd2), X(0x71dab9ea), X(0x71e30106), + X(0x71eb4526), X(0x71f3864a), X(0x71fbc472), X(0x7203ff9e), + X(0x720c37cf), X(0x72146d05), X(0x721c9f3f), X(0x7224ce7e), + X(0x722cfac2), X(0x7235240b), X(0x723d4a59), X(0x72456dad), + X(0x724d8e05), X(0x7255ab63), X(0x725dc5c7), X(0x7265dd31), + X(0x726df1a0), X(0x72760315), X(0x727e1191), X(0x72861d12), + X(0x728e259a), X(0x72962b28), X(0x729e2dbd), X(0x72a62d59), + X(0x72ae29fc), X(0x72b623a5), X(0x72be1a56), X(0x72c60e0e), + X(0x72cdfece), X(0x72d5ec95), X(0x72ddd764), X(0x72e5bf3b), + X(0x72eda41a), X(0x72f58601), X(0x72fd64f1), X(0x730540e9), + X(0x730d19e9), X(0x7314eff3), X(0x731cc305), X(0x73249321), + X(0x732c6046), X(0x73342a75), X(0x733bf1ad), X(0x7343b5ef), + X(0x734b773b), X(0x73533591), X(0x735af0f2), X(0x7362a95d), + X(0x736a5ed3), X(0x73721153), X(0x7379c0df), X(0x73816d76), + X(0x73891719), X(0x7390bdc7), X(0x73986181), X(0x73a00247), + X(0x73a7a01a), X(0x73af3af8), X(0x73b6d2e4), X(0x73be67dc), + X(0x73c5f9e1), X(0x73cd88f3), X(0x73d51513), X(0x73dc9e40), + X(0x73e4247c), X(0x73eba7c5), X(0x73f3281c), X(0x73faa582), + X(0x74021ff7), X(0x7409977b), X(0x74110c0d), X(0x74187daf), + X(0x741fec61), X(0x74275822), X(0x742ec0f3), X(0x743626d5), + X(0x743d89c7), X(0x7444e9c9), X(0x744c46dd), X(0x7453a101), + X(0x745af837), X(0x74624c7f), X(0x74699dd8), X(0x7470ec44), + X(0x747837c2), X(0x747f8052), X(0x7486c5f5), X(0x748e08ac), + X(0x74954875), X(0x749c8552), X(0x74a3bf43), X(0x74aaf648), + X(0x74b22a62), X(0x74b95b90), X(0x74c089d2), X(0x74c7b52a), + X(0x74cedd97), X(0x74d6031a), X(0x74dd25b2), X(0x74e44561), + X(0x74eb6226), X(0x74f27c02), X(0x74f992f5), X(0x7500a6ff), + X(0x7507b820), X(0x750ec659), X(0x7515d1aa), X(0x751cda14), + X(0x7523df96), X(0x752ae231), X(0x7531e1e5), X(0x7538deb2), + X(0x753fd89a), X(0x7546cf9b), X(0x754dc3b7), X(0x7554b4ed), + X(0x755ba33e), X(0x75628eaa), X(0x75697732), X(0x75705cd5), + X(0x75773f95), X(0x757e1f71), X(0x7584fc6a), X(0x758bd67f), + X(0x7592adb2), X(0x75998203), X(0x75a05371), X(0x75a721fe), + X(0x75adeda9), X(0x75b4b673), X(0x75bb7c5c), X(0x75c23f65), + X(0x75c8ff8d), X(0x75cfbcd6), X(0x75d6773f), X(0x75dd2ec8), + X(0x75e3e373), X(0x75ea953f), X(0x75f1442d), X(0x75f7f03d), + X(0x75fe996f), X(0x76053fc5), X(0x760be33d), X(0x761283d8), + X(0x76192197), X(0x761fbc7b), X(0x76265482), X(0x762ce9af), + X(0x76337c01), X(0x763a0b78), X(0x76409814), X(0x764721d7), + X(0x764da8c1), X(0x76542cd1), X(0x765aae08), X(0x76612c67), + X(0x7667a7ee), X(0x766e209d), X(0x76749675), X(0x767b0975), + X(0x7681799f), X(0x7687e6f3), X(0x768e5170), X(0x7694b918), + X(0x769b1deb), X(0x76a17fe9), X(0x76a7df13), X(0x76ae3b68), + X(0x76b494ea), X(0x76baeb98), X(0x76c13f74), X(0x76c7907c), + X(0x76cddeb3), X(0x76d42a18), X(0x76da72ab), X(0x76e0b86d), + X(0x76e6fb5e), X(0x76ed3b7f), X(0x76f378d0), X(0x76f9b352), + X(0x76ffeb05), X(0x77061fe8), X(0x770c51fe), X(0x77128145), + X(0x7718adbf), X(0x771ed76c), X(0x7724fe4c), X(0x772b225f), + X(0x773143a7), X(0x77376223), X(0x773d7dd3), X(0x774396ba), + X(0x7749acd5), X(0x774fc027), X(0x7755d0af), X(0x775bde6f), + X(0x7761e965), X(0x7767f193), X(0x776df6fa), X(0x7773f998), + X(0x7779f970), X(0x777ff681), X(0x7785f0cd), X(0x778be852), + X(0x7791dd12), X(0x7797cf0d), X(0x779dbe43), X(0x77a3aab6), + X(0x77a99465), X(0x77af7b50), X(0x77b55f79), X(0x77bb40e0), + X(0x77c11f85), X(0x77c6fb68), X(0x77ccd48a), X(0x77d2aaec), + X(0x77d87e8d), X(0x77de4f6f), X(0x77e41d92), X(0x77e9e8f5), + X(0x77efb19b), X(0x77f57782), X(0x77fb3aad), X(0x7800fb1a), + X(0x7806b8ca), X(0x780c73bf), X(0x78122bf7), X(0x7817e175), + X(0x781d9438), X(0x78234440), X(0x7828f18f), X(0x782e9c25), + X(0x78344401), X(0x7839e925), X(0x783f8b92), X(0x78452b46), + X(0x784ac844), X(0x7850628b), X(0x7855fa1c), X(0x785b8ef8), + X(0x7861211e), X(0x7866b090), X(0x786c3d4d), X(0x7871c757), + X(0x78774ead), X(0x787cd351), X(0x78825543), X(0x7887d483), + X(0x788d5111), X(0x7892caef), X(0x7898421c), X(0x789db69a), + X(0x78a32868), X(0x78a89787), X(0x78ae03f8), X(0x78b36dbb), + X(0x78b8d4d1), X(0x78be393a), X(0x78c39af6), X(0x78c8fa06), + X(0x78ce566c), X(0x78d3b026), X(0x78d90736), X(0x78de5b9c), + X(0x78e3ad58), X(0x78e8fc6c), X(0x78ee48d7), X(0x78f3929b), + X(0x78f8d9b7), X(0x78fe1e2c), X(0x79035ffb), X(0x79089f24), + X(0x790ddba8), X(0x79131587), X(0x79184cc2), X(0x791d8159), + X(0x7922b34d), X(0x7927e29e), X(0x792d0f4d), X(0x7932395a), + X(0x793760c6), X(0x793c8591), X(0x7941a7bd), X(0x7946c749), + X(0x794be435), X(0x7950fe84), X(0x79561634), X(0x795b2b47), + X(0x79603dbc), X(0x79654d96), X(0x796a5ad4), X(0x796f6576), + X(0x79746d7e), X(0x797972eb), X(0x797e75bf), X(0x798375f9), + X(0x7988739b), X(0x798d6ea5), X(0x79926717), X(0x79975cf2), + X(0x799c5037), X(0x79a140e6), X(0x79a62f00), X(0x79ab1a85), + X(0x79b00376), X(0x79b4e9d3), X(0x79b9cd9d), X(0x79beaed4), + X(0x79c38d79), X(0x79c8698d), X(0x79cd4310), X(0x79d21a03), + X(0x79d6ee66), X(0x79dbc03a), X(0x79e08f7f), X(0x79e55c36), + X(0x79ea265f), X(0x79eeedfc), X(0x79f3b30c), X(0x79f87590), + X(0x79fd3589), X(0x7a01f2f7), X(0x7a06addc), X(0x7a0b6636), + X(0x7a101c08), X(0x7a14cf52), X(0x7a198013), X(0x7a1e2e4d), + X(0x7a22da01), X(0x7a27832f), X(0x7a2c29d7), X(0x7a30cdfa), + X(0x7a356f99), X(0x7a3a0eb4), X(0x7a3eab4c), X(0x7a434561), + X(0x7a47dcf5), X(0x7a4c7207), X(0x7a510498), X(0x7a5594a9), + X(0x7a5a223a), X(0x7a5ead4d), X(0x7a6335e0), X(0x7a67bbf6), + X(0x7a6c3f8f), X(0x7a70c0ab), X(0x7a753f4b), X(0x7a79bb6f), + X(0x7a7e3519), X(0x7a82ac48), X(0x7a8720fe), X(0x7a8b933b), + X(0x7a9002ff), X(0x7a94704b), X(0x7a98db20), X(0x7a9d437e), + X(0x7aa1a967), X(0x7aa60cd9), X(0x7aaa6dd7), X(0x7aaecc61), + X(0x7ab32877), X(0x7ab7821b), X(0x7abbd94b), X(0x7ac02e0a), + X(0x7ac48058), X(0x7ac8d035), X(0x7acd1da3), X(0x7ad168a1), + X(0x7ad5b130), X(0x7ad9f751), X(0x7ade3b05), X(0x7ae27c4c), + X(0x7ae6bb27), X(0x7aeaf796), X(0x7aef319a), X(0x7af36934), + X(0x7af79e64), X(0x7afbd12c), X(0x7b00018a), X(0x7b042f81), + X(0x7b085b10), X(0x7b0c8439), X(0x7b10aafc), X(0x7b14cf5a), + X(0x7b18f153), X(0x7b1d10e8), X(0x7b212e1a), X(0x7b2548e9), + X(0x7b296155), X(0x7b2d7761), X(0x7b318b0b), X(0x7b359c55), + X(0x7b39ab3f), X(0x7b3db7cb), X(0x7b41c1f8), X(0x7b45c9c8), + X(0x7b49cf3b), X(0x7b4dd251), X(0x7b51d30b), X(0x7b55d16b), + X(0x7b59cd70), X(0x7b5dc71b), X(0x7b61be6d), X(0x7b65b366), + X(0x7b69a608), X(0x7b6d9653), X(0x7b718447), X(0x7b756fe5), + X(0x7b79592e), X(0x7b7d4022), X(0x7b8124c3), X(0x7b850710), + X(0x7b88e70a), X(0x7b8cc4b3), X(0x7b90a00a), X(0x7b947911), + X(0x7b984fc8), X(0x7b9c242f), X(0x7b9ff648), X(0x7ba3c612), + X(0x7ba79390), X(0x7bab5ec1), X(0x7baf27a5), X(0x7bb2ee3f), + X(0x7bb6b28e), X(0x7bba7493), X(0x7bbe344e), X(0x7bc1f1c1), + X(0x7bc5acec), X(0x7bc965cf), X(0x7bcd1c6c), X(0x7bd0d0c3), + X(0x7bd482d4), X(0x7bd832a1), X(0x7bdbe02a), X(0x7bdf8b70), + X(0x7be33473), X(0x7be6db34), X(0x7bea7fb4), X(0x7bee21f4), + X(0x7bf1c1f3), X(0x7bf55fb3), X(0x7bf8fb35), X(0x7bfc9479), + X(0x7c002b7f), X(0x7c03c04a), X(0x7c0752d8), X(0x7c0ae32b), + X(0x7c0e7144), X(0x7c11fd23), X(0x7c1586c9), X(0x7c190e36), + X(0x7c1c936c), X(0x7c20166b), X(0x7c239733), X(0x7c2715c6), + X(0x7c2a9224), X(0x7c2e0c4e), X(0x7c318444), X(0x7c34fa07), + X(0x7c386d98), X(0x7c3bdef8), X(0x7c3f4e26), X(0x7c42bb25), + X(0x7c4625f4), X(0x7c498e95), X(0x7c4cf507), X(0x7c50594c), + X(0x7c53bb65), X(0x7c571b51), X(0x7c5a7913), X(0x7c5dd4aa), + X(0x7c612e17), X(0x7c64855b), X(0x7c67da76), X(0x7c6b2d6a), + X(0x7c6e7e37), X(0x7c71ccdd), X(0x7c75195e), X(0x7c7863ba), + X(0x7c7babf1), X(0x7c7ef206), X(0x7c8235f7), X(0x7c8577c6), + X(0x7c88b774), X(0x7c8bf502), X(0x7c8f306f), X(0x7c9269bd), + X(0x7c95a0ec), X(0x7c98d5fe), X(0x7c9c08f2), X(0x7c9f39cb), + X(0x7ca26887), X(0x7ca59528), X(0x7ca8bfb0), X(0x7cabe81d), + X(0x7caf0e72), X(0x7cb232af), X(0x7cb554d4), X(0x7cb874e2), + X(0x7cbb92db), X(0x7cbeaebe), X(0x7cc1c88d), X(0x7cc4e047), + X(0x7cc7f5ef), X(0x7ccb0984), X(0x7cce1b08), X(0x7cd12a7b), + X(0x7cd437dd), X(0x7cd74330), X(0x7cda4c74), X(0x7cdd53aa), + X(0x7ce058d3), X(0x7ce35bef), X(0x7ce65cff), X(0x7ce95c04), + X(0x7cec58ff), X(0x7cef53f0), X(0x7cf24cd7), X(0x7cf543b7), + X(0x7cf8388f), X(0x7cfb2b60), X(0x7cfe1c2b), X(0x7d010af1), + X(0x7d03f7b2), X(0x7d06e26f), X(0x7d09cb29), X(0x7d0cb1e0), + X(0x7d0f9696), X(0x7d12794b), X(0x7d1559ff), X(0x7d1838b4), + X(0x7d1b156a), X(0x7d1df022), X(0x7d20c8dd), X(0x7d239f9b), + X(0x7d26745e), X(0x7d294725), X(0x7d2c17f1), X(0x7d2ee6c4), + X(0x7d31b39f), X(0x7d347e81), X(0x7d37476b), X(0x7d3a0e5f), + X(0x7d3cd35d), X(0x7d3f9665), X(0x7d425779), X(0x7d451699), + X(0x7d47d3c6), X(0x7d4a8f01), X(0x7d4d484b), X(0x7d4fffa3), + X(0x7d52b50c), X(0x7d556885), X(0x7d581a0f), X(0x7d5ac9ac), + X(0x7d5d775c), X(0x7d60231f), X(0x7d62ccf6), X(0x7d6574e3), + X(0x7d681ae6), X(0x7d6abeff), X(0x7d6d612f), X(0x7d700178), + X(0x7d729fd9), X(0x7d753c54), X(0x7d77d6e9), X(0x7d7a6f9a), + X(0x7d7d0666), X(0x7d7f9b4f), X(0x7d822e55), X(0x7d84bf79), + X(0x7d874ebc), X(0x7d89dc1e), X(0x7d8c67a1), X(0x7d8ef144), + X(0x7d91790a), X(0x7d93fef2), X(0x7d9682fd), X(0x7d99052d), + X(0x7d9b8581), X(0x7d9e03fb), X(0x7da0809b), X(0x7da2fb62), + X(0x7da57451), X(0x7da7eb68), X(0x7daa60a8), X(0x7dacd413), + X(0x7daf45a9), X(0x7db1b56a), X(0x7db42357), X(0x7db68f71), + X(0x7db8f9b9), X(0x7dbb6230), X(0x7dbdc8d6), X(0x7dc02dac), + X(0x7dc290b3), X(0x7dc4f1eb), X(0x7dc75156), X(0x7dc9aef4), + X(0x7dcc0ac5), X(0x7dce64cc), X(0x7dd0bd07), X(0x7dd31379), + X(0x7dd56821), X(0x7dd7bb01), X(0x7dda0c1a), X(0x7ddc5b6b), + X(0x7ddea8f7), X(0x7de0f4bd), X(0x7de33ebe), X(0x7de586fc), + X(0x7de7cd76), X(0x7dea122e), X(0x7dec5525), X(0x7dee965a), + X(0x7df0d5d0), X(0x7df31386), X(0x7df54f7e), X(0x7df789b8), + X(0x7df9c235), X(0x7dfbf8f5), X(0x7dfe2dfa), X(0x7e006145), + X(0x7e0292d5), X(0x7e04c2ac), X(0x7e06f0cb), X(0x7e091d32), + X(0x7e0b47e1), X(0x7e0d70db), X(0x7e0f981f), X(0x7e11bdaf), + X(0x7e13e18a), X(0x7e1603b3), X(0x7e182429), X(0x7e1a42ed), + X(0x7e1c6001), X(0x7e1e7b64), X(0x7e209518), X(0x7e22ad1d), + X(0x7e24c375), X(0x7e26d81f), X(0x7e28eb1d), X(0x7e2afc70), + X(0x7e2d0c17), X(0x7e2f1a15), X(0x7e31266a), X(0x7e333115), + X(0x7e353a1a), X(0x7e374177), X(0x7e39472e), X(0x7e3b4b3f), + X(0x7e3d4dac), X(0x7e3f4e75), X(0x7e414d9a), X(0x7e434b1e), + X(0x7e4546ff), X(0x7e474140), X(0x7e4939e0), X(0x7e4b30e2), + X(0x7e4d2644), X(0x7e4f1a09), X(0x7e510c30), X(0x7e52fcbc), + X(0x7e54ebab), X(0x7e56d900), X(0x7e58c4bb), X(0x7e5aaedd), + X(0x7e5c9766), X(0x7e5e7e57), X(0x7e6063b2), X(0x7e624776), + X(0x7e6429a5), X(0x7e660a3f), X(0x7e67e945), X(0x7e69c6b8), + X(0x7e6ba299), X(0x7e6d7ce7), X(0x7e6f55a5), X(0x7e712cd3), + X(0x7e730272), X(0x7e74d682), X(0x7e76a904), X(0x7e7879f9), + X(0x7e7a4962), X(0x7e7c173f), X(0x7e7de392), X(0x7e7fae5a), + X(0x7e817799), X(0x7e833f50), X(0x7e85057f), X(0x7e86ca27), + X(0x7e888d49), X(0x7e8a4ee5), X(0x7e8c0efd), X(0x7e8dcd91), + X(0x7e8f8aa1), X(0x7e914630), X(0x7e93003c), X(0x7e94b8c8), + X(0x7e966fd4), X(0x7e982560), X(0x7e99d96e), X(0x7e9b8bfe), + X(0x7e9d3d10), X(0x7e9eeca7), X(0x7ea09ac2), X(0x7ea24762), + X(0x7ea3f288), X(0x7ea59c35), X(0x7ea7446a), X(0x7ea8eb27), + X(0x7eaa906c), X(0x7eac343c), X(0x7eadd696), X(0x7eaf777b), + X(0x7eb116ed), X(0x7eb2b4eb), X(0x7eb45177), X(0x7eb5ec91), + X(0x7eb7863b), X(0x7eb91e74), X(0x7ebab53e), X(0x7ebc4a99), + X(0x7ebdde87), X(0x7ebf7107), X(0x7ec1021b), X(0x7ec291c3), + X(0x7ec42001), X(0x7ec5acd5), X(0x7ec7383f), X(0x7ec8c241), + X(0x7eca4adb), X(0x7ecbd20d), X(0x7ecd57da), X(0x7ecedc41), + X(0x7ed05f44), X(0x7ed1e0e2), X(0x7ed3611d), X(0x7ed4dff6), + X(0x7ed65d6d), X(0x7ed7d983), X(0x7ed95438), X(0x7edacd8f), + X(0x7edc4586), X(0x7eddbc20), X(0x7edf315c), X(0x7ee0a53c), + X(0x7ee217c1), X(0x7ee388ea), X(0x7ee4f8b9), X(0x7ee6672f), + X(0x7ee7d44c), X(0x7ee94012), X(0x7eeaaa80), X(0x7eec1397), + X(0x7eed7b59), X(0x7eeee1c6), X(0x7ef046df), X(0x7ef1aaa5), + X(0x7ef30d18), X(0x7ef46e39), X(0x7ef5ce09), X(0x7ef72c88), + X(0x7ef889b8), X(0x7ef9e599), X(0x7efb402c), X(0x7efc9972), + X(0x7efdf16b), X(0x7eff4818), X(0x7f009d79), X(0x7f01f191), + X(0x7f03445f), X(0x7f0495e4), X(0x7f05e620), X(0x7f073516), + X(0x7f0882c5), X(0x7f09cf2d), X(0x7f0b1a51), X(0x7f0c6430), + X(0x7f0daccc), X(0x7f0ef425), X(0x7f103a3b), X(0x7f117f11), + X(0x7f12c2a5), X(0x7f1404fa), X(0x7f15460f), X(0x7f1685e6), + X(0x7f17c47f), X(0x7f1901db), X(0x7f1a3dfb), X(0x7f1b78e0), + X(0x7f1cb28a), X(0x7f1deafa), X(0x7f1f2231), X(0x7f20582f), + X(0x7f218cf5), X(0x7f22c085), X(0x7f23f2de), X(0x7f252401), + X(0x7f2653f0), X(0x7f2782ab), X(0x7f28b032), X(0x7f29dc87), + X(0x7f2b07aa), X(0x7f2c319c), X(0x7f2d5a5e), X(0x7f2e81f0), + X(0x7f2fa853), X(0x7f30cd88), X(0x7f31f18f), X(0x7f33146a), + X(0x7f343619), X(0x7f35569c), X(0x7f3675f6), X(0x7f379425), + X(0x7f38b12c), X(0x7f39cd0a), X(0x7f3ae7c0), X(0x7f3c0150), + X(0x7f3d19ba), X(0x7f3e30fe), X(0x7f3f471e), X(0x7f405c1a), + X(0x7f416ff3), X(0x7f4282a9), X(0x7f43943e), X(0x7f44a4b2), + X(0x7f45b405), X(0x7f46c239), X(0x7f47cf4e), X(0x7f48db45), + X(0x7f49e61f), X(0x7f4aefdc), X(0x7f4bf87e), X(0x7f4d0004), + X(0x7f4e0670), X(0x7f4f0bc2), X(0x7f500ffb), X(0x7f51131c), + X(0x7f521525), X(0x7f531618), X(0x7f5415f4), X(0x7f5514bb), + X(0x7f56126e), X(0x7f570f0c), X(0x7f580a98), X(0x7f590511), + X(0x7f59fe78), X(0x7f5af6ce), X(0x7f5bee14), X(0x7f5ce44a), + X(0x7f5dd972), X(0x7f5ecd8b), X(0x7f5fc097), X(0x7f60b296), + X(0x7f61a389), X(0x7f629370), X(0x7f63824e), X(0x7f647021), + X(0x7f655ceb), X(0x7f6648ad), X(0x7f673367), X(0x7f681d19), + X(0x7f6905c6), X(0x7f69ed6d), X(0x7f6ad40f), X(0x7f6bb9ad), + X(0x7f6c9e48), X(0x7f6d81e0), X(0x7f6e6475), X(0x7f6f460a), + X(0x7f70269d), X(0x7f710631), X(0x7f71e4c6), X(0x7f72c25c), + X(0x7f739ef4), X(0x7f747a8f), X(0x7f75552e), X(0x7f762ed1), + X(0x7f770779), X(0x7f77df27), X(0x7f78b5db), X(0x7f798b97), + X(0x7f7a605a), X(0x7f7b3425), X(0x7f7c06fa), X(0x7f7cd8d9), + X(0x7f7da9c2), X(0x7f7e79b7), X(0x7f7f48b8), X(0x7f8016c5), + X(0x7f80e3e0), X(0x7f81b009), X(0x7f827b40), X(0x7f834588), + X(0x7f840edf), X(0x7f84d747), X(0x7f859ec1), X(0x7f86654d), + X(0x7f872aec), X(0x7f87ef9e), X(0x7f88b365), X(0x7f897641), + X(0x7f8a3832), X(0x7f8af93a), X(0x7f8bb959), X(0x7f8c7890), + X(0x7f8d36df), X(0x7f8df448), X(0x7f8eb0ca), X(0x7f8f6c67), + X(0x7f90271e), X(0x7f90e0f2), X(0x7f9199e2), X(0x7f9251f0), + X(0x7f93091b), X(0x7f93bf65), X(0x7f9474ce), X(0x7f952958), + X(0x7f95dd01), X(0x7f968fcd), X(0x7f9741ba), X(0x7f97f2ca), + X(0x7f98a2fd), X(0x7f995254), X(0x7f9a00d0), X(0x7f9aae71), + X(0x7f9b5b38), X(0x7f9c0726), X(0x7f9cb23b), X(0x7f9d5c78), + X(0x7f9e05de), X(0x7f9eae6e), X(0x7f9f5627), X(0x7f9ffd0b), + X(0x7fa0a31b), X(0x7fa14856), X(0x7fa1ecbf), X(0x7fa29054), + X(0x7fa33318), X(0x7fa3d50b), X(0x7fa4762c), X(0x7fa5167e), + X(0x7fa5b601), X(0x7fa654b5), X(0x7fa6f29b), X(0x7fa78fb3), + X(0x7fa82bff), X(0x7fa8c77f), X(0x7fa96234), X(0x7fa9fc1e), + X(0x7faa953e), X(0x7fab2d94), X(0x7fabc522), X(0x7fac5be8), + X(0x7facf1e6), X(0x7fad871d), X(0x7fae1b8f), X(0x7faeaf3b), + X(0x7faf4222), X(0x7fafd445), X(0x7fb065a4), X(0x7fb0f641), + X(0x7fb1861b), X(0x7fb21534), X(0x7fb2a38c), X(0x7fb33124), + X(0x7fb3bdfb), X(0x7fb44a14), X(0x7fb4d56f), X(0x7fb5600c), + X(0x7fb5e9ec), X(0x7fb6730f), X(0x7fb6fb76), X(0x7fb78323), + X(0x7fb80a15), X(0x7fb8904d), X(0x7fb915cc), X(0x7fb99a92), + X(0x7fba1ea0), X(0x7fbaa1f7), X(0x7fbb2497), X(0x7fbba681), + X(0x7fbc27b5), X(0x7fbca835), X(0x7fbd2801), X(0x7fbda719), + X(0x7fbe257e), X(0x7fbea331), X(0x7fbf2032), X(0x7fbf9c82), + X(0x7fc01821), X(0x7fc09311), X(0x7fc10d52), X(0x7fc186e4), + X(0x7fc1ffc8), X(0x7fc277ff), X(0x7fc2ef89), X(0x7fc36667), + X(0x7fc3dc9a), X(0x7fc45221), X(0x7fc4c6ff), X(0x7fc53b33), + X(0x7fc5aebe), X(0x7fc621a0), X(0x7fc693db), X(0x7fc7056f), + X(0x7fc7765c), X(0x7fc7e6a3), X(0x7fc85645), X(0x7fc8c542), + X(0x7fc9339b), X(0x7fc9a150), X(0x7fca0e63), X(0x7fca7ad3), + X(0x7fcae6a2), X(0x7fcb51cf), X(0x7fcbbc5c), X(0x7fcc2649), + X(0x7fcc8f97), X(0x7fccf846), X(0x7fcd6058), X(0x7fcdc7cb), + X(0x7fce2ea2), X(0x7fce94dd), X(0x7fcefa7b), X(0x7fcf5f7f), + X(0x7fcfc3e8), X(0x7fd027b7), X(0x7fd08aed), X(0x7fd0ed8b), + X(0x7fd14f90), X(0x7fd1b0fd), X(0x7fd211d4), X(0x7fd27214), + X(0x7fd2d1bf), X(0x7fd330d4), X(0x7fd38f55), X(0x7fd3ed41), + X(0x7fd44a9a), X(0x7fd4a761), X(0x7fd50395), X(0x7fd55f37), + X(0x7fd5ba48), X(0x7fd614c9), X(0x7fd66eba), X(0x7fd6c81b), + X(0x7fd720ed), X(0x7fd77932), X(0x7fd7d0e8), X(0x7fd82812), + X(0x7fd87eae), X(0x7fd8d4bf), X(0x7fd92a45), X(0x7fd97f40), + X(0x7fd9d3b0), X(0x7fda2797), X(0x7fda7af5), X(0x7fdacdca), + X(0x7fdb2018), X(0x7fdb71dd), X(0x7fdbc31c), X(0x7fdc13d5), + X(0x7fdc6408), X(0x7fdcb3b6), X(0x7fdd02df), X(0x7fdd5184), + X(0x7fdd9fa5), X(0x7fdded44), X(0x7fde3a60), X(0x7fde86fb), + X(0x7fded314), X(0x7fdf1eac), X(0x7fdf69c4), X(0x7fdfb45d), + X(0x7fdffe76), X(0x7fe04811), X(0x7fe0912e), X(0x7fe0d9ce), + X(0x7fe121f0), X(0x7fe16996), X(0x7fe1b0c1), X(0x7fe1f770), + X(0x7fe23da4), X(0x7fe2835f), X(0x7fe2c89f), X(0x7fe30d67), + X(0x7fe351b5), X(0x7fe3958c), X(0x7fe3d8ec), X(0x7fe41bd4), + X(0x7fe45e46), X(0x7fe4a042), X(0x7fe4e1c8), X(0x7fe522da), + X(0x7fe56378), X(0x7fe5a3a1), X(0x7fe5e358), X(0x7fe6229b), + X(0x7fe6616d), X(0x7fe69fcc), X(0x7fe6ddbb), X(0x7fe71b39), + X(0x7fe75847), X(0x7fe794e5), X(0x7fe7d114), X(0x7fe80cd5), + X(0x7fe84827), X(0x7fe8830c), X(0x7fe8bd84), X(0x7fe8f78f), + X(0x7fe9312f), X(0x7fe96a62), X(0x7fe9a32b), X(0x7fe9db8a), + X(0x7fea137e), X(0x7fea4b09), X(0x7fea822b), X(0x7feab8e5), + X(0x7feaef37), X(0x7feb2521), X(0x7feb5aa4), X(0x7feb8fc1), + X(0x7febc478), X(0x7febf8ca), X(0x7fec2cb6), X(0x7fec603e), + X(0x7fec9363), X(0x7fecc623), X(0x7fecf881), X(0x7fed2a7c), + X(0x7fed5c16), X(0x7fed8d4e), X(0x7fedbe24), X(0x7fedee9b), + X(0x7fee1eb1), X(0x7fee4e68), X(0x7fee7dc0), X(0x7feeacb9), + X(0x7feedb54), X(0x7fef0991), X(0x7fef3771), X(0x7fef64f5), + X(0x7fef921d), X(0x7fefbee8), X(0x7fefeb59), X(0x7ff0176f), + X(0x7ff0432a), X(0x7ff06e8c), X(0x7ff09995), X(0x7ff0c444), + X(0x7ff0ee9c), X(0x7ff1189b), X(0x7ff14243), X(0x7ff16b94), + X(0x7ff1948e), X(0x7ff1bd32), X(0x7ff1e581), X(0x7ff20d7b), + X(0x7ff2351f), X(0x7ff25c70), X(0x7ff2836d), X(0x7ff2aa17), + X(0x7ff2d06d), X(0x7ff2f672), X(0x7ff31c24), X(0x7ff34185), + X(0x7ff36695), X(0x7ff38b55), X(0x7ff3afc4), X(0x7ff3d3e4), + X(0x7ff3f7b4), X(0x7ff41b35), X(0x7ff43e69), X(0x7ff4614e), + X(0x7ff483e6), X(0x7ff4a631), X(0x7ff4c82f), X(0x7ff4e9e1), + X(0x7ff50b47), X(0x7ff52c62), X(0x7ff54d33), X(0x7ff56db9), + X(0x7ff58df5), X(0x7ff5ade7), X(0x7ff5cd90), X(0x7ff5ecf1), + X(0x7ff60c09), X(0x7ff62ada), X(0x7ff64963), X(0x7ff667a5), + X(0x7ff685a1), X(0x7ff6a357), X(0x7ff6c0c7), X(0x7ff6ddf1), + X(0x7ff6fad7), X(0x7ff71778), X(0x7ff733d6), X(0x7ff74fef), + X(0x7ff76bc6), X(0x7ff78759), X(0x7ff7a2ab), X(0x7ff7bdba), + X(0x7ff7d888), X(0x7ff7f315), X(0x7ff80d61), X(0x7ff8276c), + X(0x7ff84138), X(0x7ff85ac4), X(0x7ff87412), X(0x7ff88d20), + X(0x7ff8a5f0), X(0x7ff8be82), X(0x7ff8d6d7), X(0x7ff8eeef), + X(0x7ff906c9), X(0x7ff91e68), X(0x7ff935cb), X(0x7ff94cf2), + X(0x7ff963dd), X(0x7ff97a8f), X(0x7ff99105), X(0x7ff9a742), + X(0x7ff9bd45), X(0x7ff9d30f), X(0x7ff9e8a0), X(0x7ff9fdf9), + X(0x7ffa131a), X(0x7ffa2803), X(0x7ffa3cb4), X(0x7ffa512f), + X(0x7ffa6573), X(0x7ffa7981), X(0x7ffa8d59), X(0x7ffaa0fc), + X(0x7ffab46a), X(0x7ffac7a3), X(0x7ffadaa8), X(0x7ffaed78), + X(0x7ffb0015), X(0x7ffb127f), X(0x7ffb24b6), X(0x7ffb36bb), + X(0x7ffb488d), X(0x7ffb5a2e), X(0x7ffb6b9d), X(0x7ffb7cdb), + X(0x7ffb8de9), X(0x7ffb9ec6), X(0x7ffbaf73), X(0x7ffbbff1), + X(0x7ffbd03f), X(0x7ffbe05e), X(0x7ffbf04f), X(0x7ffc0012), + X(0x7ffc0fa6), X(0x7ffc1f0d), X(0x7ffc2e47), X(0x7ffc3d54), + X(0x7ffc4c35), X(0x7ffc5ae9), X(0x7ffc6971), X(0x7ffc77ce), + X(0x7ffc8600), X(0x7ffc9407), X(0x7ffca1e4), X(0x7ffcaf96), + X(0x7ffcbd1f), X(0x7ffcca7e), X(0x7ffcd7b4), X(0x7ffce4c1), + X(0x7ffcf1a5), X(0x7ffcfe62), X(0x7ffd0af6), X(0x7ffd1763), + X(0x7ffd23a9), X(0x7ffd2fc8), X(0x7ffd3bc1), X(0x7ffd4793), + X(0x7ffd533f), X(0x7ffd5ec5), X(0x7ffd6a27), X(0x7ffd7563), + X(0x7ffd807a), X(0x7ffd8b6e), X(0x7ffd963d), X(0x7ffda0e8), + X(0x7ffdab70), X(0x7ffdb5d5), X(0x7ffdc017), X(0x7ffdca36), + X(0x7ffdd434), X(0x7ffdde0f), X(0x7ffde7c9), X(0x7ffdf161), + X(0x7ffdfad8), X(0x7ffe042f), X(0x7ffe0d65), X(0x7ffe167b), + X(0x7ffe1f71), X(0x7ffe2848), X(0x7ffe30ff), X(0x7ffe3997), + X(0x7ffe4211), X(0x7ffe4a6c), X(0x7ffe52a9), X(0x7ffe5ac8), + X(0x7ffe62c9), X(0x7ffe6aae), X(0x7ffe7275), X(0x7ffe7a1f), + X(0x7ffe81ad), X(0x7ffe891f), X(0x7ffe9075), X(0x7ffe97b0), + X(0x7ffe9ece), X(0x7ffea5d2), X(0x7ffeacbb), X(0x7ffeb38a), + X(0x7ffeba3e), X(0x7ffec0d8), X(0x7ffec758), X(0x7ffecdbf), + X(0x7ffed40d), X(0x7ffeda41), X(0x7ffee05d), X(0x7ffee660), + X(0x7ffeec4b), X(0x7ffef21f), X(0x7ffef7da), X(0x7ffefd7e), + X(0x7fff030b), X(0x7fff0881), X(0x7fff0de0), X(0x7fff1328), + X(0x7fff185b), X(0x7fff1d77), X(0x7fff227e), X(0x7fff276f), + X(0x7fff2c4b), X(0x7fff3112), X(0x7fff35c4), X(0x7fff3a62), + X(0x7fff3eeb), X(0x7fff4360), X(0x7fff47c2), X(0x7fff4c0f), + X(0x7fff504a), X(0x7fff5471), X(0x7fff5885), X(0x7fff5c87), + X(0x7fff6076), X(0x7fff6452), X(0x7fff681d), X(0x7fff6bd6), + X(0x7fff6f7d), X(0x7fff7313), X(0x7fff7698), X(0x7fff7a0c), + X(0x7fff7d6f), X(0x7fff80c2), X(0x7fff8404), X(0x7fff8736), + X(0x7fff8a58), X(0x7fff8d6b), X(0x7fff906e), X(0x7fff9362), + X(0x7fff9646), X(0x7fff991c), X(0x7fff9be3), X(0x7fff9e9c), + X(0x7fffa146), X(0x7fffa3e2), X(0x7fffa671), X(0x7fffa8f1), + X(0x7fffab65), X(0x7fffadca), X(0x7fffb023), X(0x7fffb26f), + X(0x7fffb4ae), X(0x7fffb6e0), X(0x7fffb906), X(0x7fffbb20), + X(0x7fffbd2e), X(0x7fffbf30), X(0x7fffc126), X(0x7fffc311), + X(0x7fffc4f1), X(0x7fffc6c5), X(0x7fffc88f), X(0x7fffca4d), + X(0x7fffcc01), X(0x7fffcdab), X(0x7fffcf4a), X(0x7fffd0e0), + X(0x7fffd26b), X(0x7fffd3ec), X(0x7fffd564), X(0x7fffd6d2), + X(0x7fffd838), X(0x7fffd993), X(0x7fffdae6), X(0x7fffdc31), + X(0x7fffdd72), X(0x7fffdeab), X(0x7fffdfdb), X(0x7fffe104), + X(0x7fffe224), X(0x7fffe33c), X(0x7fffe44d), X(0x7fffe556), + X(0x7fffe657), X(0x7fffe751), X(0x7fffe844), X(0x7fffe930), + X(0x7fffea15), X(0x7fffeaf3), X(0x7fffebca), X(0x7fffec9b), + X(0x7fffed66), X(0x7fffee2a), X(0x7fffeee8), X(0x7fffefa0), + X(0x7ffff053), X(0x7ffff0ff), X(0x7ffff1a6), X(0x7ffff247), + X(0x7ffff2e4), X(0x7ffff37a), X(0x7ffff40c), X(0x7ffff499), + X(0x7ffff520), X(0x7ffff5a3), X(0x7ffff621), X(0x7ffff69b), + X(0x7ffff710), X(0x7ffff781), X(0x7ffff7ee), X(0x7ffff857), + X(0x7ffff8bb), X(0x7ffff91c), X(0x7ffff979), X(0x7ffff9d2), + X(0x7ffffa27), X(0x7ffffa79), X(0x7ffffac8), X(0x7ffffb13), + X(0x7ffffb5b), X(0x7ffffba0), X(0x7ffffbe2), X(0x7ffffc21), + X(0x7ffffc5d), X(0x7ffffc96), X(0x7ffffccd), X(0x7ffffd01), + X(0x7ffffd32), X(0x7ffffd61), X(0x7ffffd8e), X(0x7ffffdb8), + X(0x7ffffde0), X(0x7ffffe07), X(0x7ffffe2b), X(0x7ffffe4d), + X(0x7ffffe6d), X(0x7ffffe8b), X(0x7ffffea8), X(0x7ffffec3), + X(0x7ffffedc), X(0x7ffffef4), X(0x7fffff0a), X(0x7fffff1f), + X(0x7fffff33), X(0x7fffff45), X(0x7fffff56), X(0x7fffff66), + X(0x7fffff75), X(0x7fffff82), X(0x7fffff8f), X(0x7fffff9a), + X(0x7fffffa5), X(0x7fffffaf), X(0x7fffffb8), X(0x7fffffc0), + X(0x7fffffc8), X(0x7fffffce), X(0x7fffffd5), X(0x7fffffda), + X(0x7fffffdf), X(0x7fffffe4), X(0x7fffffe8), X(0x7fffffeb), + X(0x7fffffef), X(0x7ffffff1), X(0x7ffffff4), X(0x7ffffff6), + X(0x7ffffff8), X(0x7ffffff9), X(0x7ffffffb), X(0x7ffffffc), + X(0x7ffffffd), X(0x7ffffffd), X(0x7ffffffe), X(0x7fffffff), + X(0x7fffffff), X(0x7fffffff), X(0x7fffffff), X(0x7fffffff), + X(0x7fffffff), X(0x7fffffff), X(0x7fffffff), X(0x7fffffff), + X(0x7fffffff), X(0x7fffffff), X(0x7fffffff), X(0x7fffffff), +}; + +#endif diff --git a/components/spotify/cspot/cpp-reflection/.gitignore b/components/spotify/cspot/cpp-reflection/.gitignore new file mode 100644 index 00000000..11c1c778 --- /dev/null +++ b/components/spotify/cspot/cpp-reflection/.gitignore @@ -0,0 +1,6 @@ +.vscode +protoc-gen-cpprefl +generator/ +` +out/ +protos/ diff --git a/components/spotify/cspot/cpp-reflection/README.md b/components/spotify/cspot/cpp-reflection/README.md new file mode 100644 index 00000000..4659be26 --- /dev/null +++ b/components/spotify/cspot/cpp-reflection/README.md @@ -0,0 +1,7 @@ +# Running + +```sh +$ go build -o protoc-gen-cpprefl . && protoc --plugin=protoc-gen-cpprefl=protoc-gen-cpprefl --cpprefl_out okon protos/*.proto --proto_path protos/ +``` + +Will get protos from `protos/` and output to `out/protobuf.h` diff --git a/components/spotify/cspot/cpp-reflection/alias_type.go b/components/spotify/cspot/cpp-reflection/alias_type.go new file mode 100644 index 00000000..de82e936 --- /dev/null +++ b/components/spotify/cspot/cpp-reflection/alias_type.go @@ -0,0 +1,21 @@ +package main + +type AliasType struct { + Of GeneratableType + cppType string +} + +func (at *AliasType) IdentifierName() string { + return at.Of.IdentifierName() +} + +func (at *AliasType) CppType() string { + return at.cppType +} + +func (at *AliasType) WriteDeclarations(gen *CppGenerator) { + +} +func (at *AliasType) WriteReflection(gen *CppGenerator) { + +} diff --git a/components/spotify/cspot/cpp-reflection/any_types.go b/components/spotify/cspot/cpp-reflection/any_types.go new file mode 100644 index 00000000..d787aed9 --- /dev/null +++ b/components/spotify/cspot/cpp-reflection/any_types.go @@ -0,0 +1,192 @@ +package main + +import ( + "fmt" + "log" + "text/template" +) + +func GenerateAnyTypes(gen *CppGenerator, primitiveTypes []GeneratableType, allTypes []GeneratableType) { + + exceptionalTypes := []GeneratableType{} + allTypesWithoutAliases := []GeneratableType{} + for _, t := range allTypes { + _, isPrimitve := t.(*PrimitiveType) + _, isEnum := t.(*EnumType) + _, isGeneric := t.(GenericType) + if isPrimitve || isEnum || isGeneric { + exceptionalTypes = append(exceptionalTypes, t) + } + if _, ok := t.(*AliasType); !ok { + log.Printf("%#v", t) + allTypesWithoutAliases = append(allTypesWithoutAliases, t) + } + } + + template.Must(template.New("any.cpp").Parse(` + + + class AnyRef { + public: + ReflectTypeID typeID; + AnyRef() {}; + AnyRef(ReflectTypeID typeID, void *obj) { + this->typeID = typeID; + this->value.voidptr = obj; + } + template + T *as() { + // if(T::_TYPE_ID != this->typeID) { + // throw "invalid as call"; + // } + return (T*) this->value.voidptr; + } + + template + bool is() { + {{range .PrimitiveTypes}}if constexpr(std::is_same::value) { + return ReflectTypeID::{{.IdentifierName}} == this->typeID; + } else + {{end}} { + return T::_TYPE_ID == this->typeID; + } + } + + + ReflectType *reflectType(); + AnyRef getField(int i); + template + static AnyRef of(T *obj) + { + ReflectTypeID typeID; + {{range .PrimitiveTypes}}if constexpr(std::is_same::value) { + typeID = ReflectTypeID::{{.IdentifierName}}; + } else + {{end}} { + typeID = T::_TYPE_ID; + } + AnyRef a; + a.typeID = typeID; + a.value.voidptr = (void*) obj; + return a; + } + + union ReflectedTypes { + void *voidptr; + {{range .allTypes}}{{.CppType}} *u_{{.IdentifierName}}; + {{end}} + } value; + private: + + }; + + + `)).Execute(gen.Body, map[string]interface{}{ + "PrimitiveTypes": exceptionalTypes, + "allTypes": allTypesWithoutAliases, + }) +} + +func GenerateAnyTypesImplementation(gen *CppGenerator) { + fmt.Fprintf(gen.SubFile("AnyRefImpl.cpp", false).AddLocalInclude(gen.Filename).Body, ` + ReflectType *AnyRef::reflectType() { + return &reflectTypeInfo[static_cast(this->typeID)]; + } + AnyRef AnyRef::getField(int i) { + auto info = this->reflectType(); + if(info->kind != ReflectTypeKind::Class) { + throw "not a class"; + } + return AnyRef(info->fields[i].typeID, static_cast(this->value.voidptr) + info->fields[i].offset); + } +`) + + fmt.Fprintf(gen.Body, ` + + + class UniqueAny: public AnyRef { + public: + UniqueAny() { + this->value.voidptr = nullptr; + }; + UniqueAny(ReflectTypeID typeID) { + this->typeID = typeID; + auto typeInfo = &reflectTypeInfo[static_cast(typeID)]; + AnyRef a; + this->value.voidptr = new unsigned char[typeInfo->size]; + typeInfo->_Construct(this->value.voidptr); + }; + ~UniqueAny() { + auto typeInfo = &reflectTypeInfo[static_cast(typeID)]; + typeInfo->_Destruct(this->value.voidptr); + delete reinterpret_cast(this->value.voidptr); + }; + }; + + class AnyVectorRef { + public: + AnyRef ref; + AnyVectorRef(AnyRef r): ref(r) {} + void push_back(AnyRef &v) { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + typeInfo->vectorOps.push_back(ref, v); + } + size_t size() { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + return typeInfo->vectorOps.size(ref); + } + + void emplace_back() { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + typeInfo->vectorOps.emplace_back(ref); + } + + void clear() { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + typeInfo->vectorOps.clear(ref); + } + + void reserve(size_t n) { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + typeInfo->vectorOps.reserve(ref, n); + } + + + AnyRef at(size_t index) { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + return typeInfo->vectorOps.at(ref, index); + } + }; + + class AnyOptionalRef { + public: + AnyRef ref; + AnyOptionalRef(AnyRef r): ref(r) {} + + AnyRef get() { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + return typeInfo->optionalOps.get(ref); + } + + bool has_value() { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + return typeInfo->optionalOps.has_value(ref); + } + void set(AnyRef &o) { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + typeInfo->optionalOps.set(ref, o); + } + void reset() { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + typeInfo->optionalOps.reset(ref); + } + + void emplaceEmpty() { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + typeInfo->optionalOps.emplaceEmpty(ref); + } + + }; + + `) +} diff --git a/components/spotify/cspot/cpp-reflection/class_type.go b/components/spotify/cspot/cpp-reflection/class_type.go new file mode 100644 index 00000000..a25cd45a --- /dev/null +++ b/components/spotify/cspot/cpp-reflection/class_type.go @@ -0,0 +1,69 @@ +package main + +import ( + "fmt" +) + +type ClassField struct { + Name string + ProtobufTag uint32 + Type GeneratableType +} + +type ClassType struct { + Name string + Fields []ClassField + AdditionalCode string + AdditionalLibraryIncludes []string + ProtoName string +} + +func (ct *ClassType) IdentifierName() string { + return "Class" + ct.Name +} + +func (ct *ClassType) CppType() string { + return ct.Name +} + +func (et *ClassType) DeclarationOrder() int { + return -10 +} + +func (ct *ClassType) ForwardDeclaration() string { + return fmt.Sprintf("class %v;", ct.Name) +} + +func (ct *ClassType) WriteDeclarations(gen *CppGenerator) { + classSubfile := gen.SubFile(ct.ProtoName+".h", true) + gen.AddLocalInclude(classSubfile.Filename) + for _, f := range ct.Fields { + AddIncludeForType(f.Type, classSubfile) + } + for _, i := range ct.AdditionalLibraryIncludes { + classSubfile.AddLibraryInclude(i) + } + classSubfile.OutputClass(ct.Name, func() { + for _, t := range ct.Fields { + classSubfile.OutputClassField(t.Type.CppType(), t.Name) + } + classSubfile.OutputClassTypeID(ct.IdentifierName()) + fmt.Fprint(classSubfile.Body, ct.AdditionalCode) + }) +} +func (ct *ClassType) WriteReflection(gen *CppGenerator) { + gen.AddLibraryInclude("vector") + fieldsContents := "" + for _, f := range ct.Fields { + fieldsContents += fmt.Sprintf("ReflectField( /* typeID */ ReflectTypeID::%v, /* name */ %v, /* offset */ offsetof(%v, %v), /* protobuf tag */ %v),\n", + f.Type.IdentifierName(), gen.EscapeCppString(f.Name), ct.CppType(), f.Name, f.ProtobufTag) + } + fmt.Fprintf(gen.Body, `ReflectType::ofClass( + /* mine type id */ ReflectTypeID::%v, + /* name */ %v, + /* fields */ std::move(std::vector{%v}), + /* size */ sizeof(%v), + __reflectConstruct<%v>, + __reflectDestruct<%v>)`, + ct.IdentifierName(), gen.EscapeCppString(ct.CppType()), fieldsContents, ct.CppType(), ct.CppType(), ct.CppType()) +} diff --git a/components/spotify/cspot/cpp-reflection/corntext.go b/components/spotify/cspot/cpp-reflection/corntext.go new file mode 100644 index 00000000..bf22526d --- /dev/null +++ b/components/spotify/cspot/cpp-reflection/corntext.go @@ -0,0 +1,417 @@ +package main + +import ( + "fmt" + "log" + "reflect" + "strings" + + "sort" + + "github.com/golang/protobuf/protoc-gen-go/descriptor" + plugin "github.com/golang/protobuf/protoc-gen-go/plugin" +) + +type GeneratableType interface { + IdentifierName() string + CppType() string + WriteDeclarations(gen *CppGenerator) + WriteReflection(gen *CppGenerator) +} + +type GenericType interface { + GeneratableType + GetInnerType() GeneratableType +} + +type ForwardDeclarable interface { + ForwardDeclaration() string +} + +type DelcarationOrderable interface { + DeclarationOrder() int +} + +type Corntext struct { + CG *CppGenerator + Request *plugin.CodeGeneratorRequest // The input. + Response *plugin.CodeGeneratorResponse // The output. + AllTypes []GeneratableType + PrimitiveTypes map[string]GeneratableType + + // types required for reflection + TypeIDEnum GeneratableType + ReflectType GeneratableType + ReflectField GeneratableType + ReflectEnumValue GeneratableType + TypeKindEnum GeneratableType + vectorOfReflectFields GeneratableType + vectorOfReflectEnumValues GeneratableType +} + +func NewCorntext() *Corntext { + i64 := &PrimitiveType{identifierName: "Int64", cppType: "int64_t"} + return &Corntext{ + Request: new(plugin.CodeGeneratorRequest), + Response: new(plugin.CodeGeneratorResponse), + CG: NewCppGenerator("protobuf.h"), + AllTypes: []GeneratableType{}, + PrimitiveTypes: map[string]GeneratableType{ + "int": &PrimitiveType{identifierName: "Int", cppType: "int"}, + // "unsigned int": &PrimitiveType{identifierName: "UnsignedInt", cppType: "unsigned int"}, + "char": &PrimitiveType{identifierName: "Char", cppType: "char"}, + "unsigned char": &PrimitiveType{identifierName: "UnsignedChar", cppType: "unsigned char"}, + "double": &PrimitiveType{identifierName: "Double", cppType: "double"}, + "float": &PrimitiveType{identifierName: "Float", cppType: "float"}, + "bool": &PrimitiveType{identifierName: "Bool", cppType: "bool"}, + "std::string": &PrimitiveType{identifierName: "String", cppType: "std::string"}, + "size_t": &AliasType{Of: i64, cppType: "size_t"}, + "int32_t": &PrimitiveType{identifierName: "Int32", cppType: "int32_t"}, + "int64_t": i64, + "uint32_t": &PrimitiveType{identifierName: "Uint32", cppType: "uint32_t"}, + "uint64_t": &PrimitiveType{identifierName: "Uint64", cppType: "uint64_t"}, + "uint8_t": &PrimitiveType{identifierName: "Uint8", cppType: "uint8_t"}, + }, + } +} + +func (c *Corntext) outputTypes() { + c.CG.AddLibraryInclude("utility") + c.CG.AddLibraryInclude("vector") + vht := &VectorHelperTypes{} + oht := &OptionalHelperTypes{} + fmt.Fprintln(c.CG.Body, ` + template + void __reflectConstruct(void *mem) { + new(mem) T; + } + template + void __reflectDestruct(void *obj) { + ((T*) obj)->~T(); + } + + `) + vht.GenerateVectorOperationsStruct(c.CG) + oht.GenerateOptionalOperationsStruct(c.CG) + for _, t := range c.AllTypes { + if fwd, ok := t.(ForwardDeclarable); ok { + fmt.Fprintf(c.CG.Body, "%v\n", fwd.ForwardDeclaration()) + } + + } + c.CG.AddLibraryInclude("type_traits") + + typesToDeclare := make([]GeneratableType, len(c.AllTypes)) + for i := range c.AllTypes { + typesToDeclare[i] = c.AllTypes[i] + } + sort.SliceStable(typesToDeclare, func(i, j int) bool { + var ival, jval int + if orderable, ok := typesToDeclare[i].(DelcarationOrderable); ok { + ival = orderable.DeclarationOrder() + } + if orderable, ok := typesToDeclare[j].(DelcarationOrderable); ok { + jval = orderable.DeclarationOrder() + } + return ival < jval + }) + for _, t := range typesToDeclare { + t.WriteDeclarations(c.CG) + } + + primitiveList := make([]GeneratableType, 0, len(c.PrimitiveTypes)) + for _, pt := range c.PrimitiveTypes { + primitiveList = append(primitiveList, pt) + } + GenerateAnyTypes(c.CG, primitiveList, c.AllTypes) + vht.GenerateVectorManipulator(c.CG) + oht.GenerateOptionalManipulator(c.CG) + + c.CG.OutputArrayVariableExtern(c.ReflectType.CppType(), "reflectTypeInfo", len(c.AllTypes)) + dataFile := c.CG.SubFile("ReflectTypeInfo.cpp", false).AddLocalInclude(c.CG.Filename) + dataFile.OutputArrayVariable(c.ReflectType.CppType(), "reflectTypeInfo", len(c.AllTypes), func() { + + for _, t := range c.AllTypes { + if _, ok := t.(*AliasType); ok { + continue + } + t.WriteReflection(dataFile) + fmt.Fprintf(dataFile.Body, ",\n") + } + }) + + GenerateAnyTypesImplementation(c.CG) + + c.CG.OutputToDirectory("protos/") +} + +func (c *Corntext) buildAllTypes() { + c.generateReflectionTypes() + c.AllTypes = append(c.AllTypes, + c.TypeIDEnum, + c.ReflectField, + c.ReflectEnumValue, + c.ReflectType, + c.TypeKindEnum, + c.vectorOfReflectFields, + c.vectorOfReflectEnumValues, + ) + for _, t := range c.PrimitiveTypes { + c.AllTypes = append(c.AllTypes, t) + } + c.generateProtobufTypes() + i := 0 + for _, t := range c.AllTypes { + if _, ok := t.(*AliasType); ok { + continue + } + c.TypeIDEnum.(*EnumType).Values = append(c.TypeIDEnum.(*EnumType).Values, EnumValue{Name: t.IdentifierName(), Value: fmt.Sprintf("%v", i)}) + i++ + } +} + +func (c *Corntext) generateProtobufTypes() { + var pbType2reflection = map[descriptor.FieldDescriptorProto_Type]GeneratableType{ + descriptor.FieldDescriptorProto_TYPE_INT32: c.PrimitiveTypes["int32_t"], + descriptor.FieldDescriptorProto_TYPE_SINT32: c.PrimitiveTypes["int32_t"], + descriptor.FieldDescriptorProto_TYPE_SINT64: c.PrimitiveTypes["int64_t"], + descriptor.FieldDescriptorProto_TYPE_INT64: c.PrimitiveTypes["int64_t"], + descriptor.FieldDescriptorProto_TYPE_UINT32: c.PrimitiveTypes["uint32_t"], + descriptor.FieldDescriptorProto_TYPE_UINT64: c.PrimitiveTypes["uint64_t"], + descriptor.FieldDescriptorProto_TYPE_BOOL: c.PrimitiveTypes["bool"], + descriptor.FieldDescriptorProto_TYPE_BYTES: c.genericOf(NewVectorType, c.PrimitiveTypes["uint8_t"]), + descriptor.FieldDescriptorProto_TYPE_STRING: c.PrimitiveTypes["std::string"], + } + for _, f := range c.Request.ProtoFile { + log.Printf("Doing file %v", *f.Name) + typeMappings := map[string]GeneratableType{} + for _, e := range f.EnumType { + values := make([]EnumValue, 0, len(e.Value)) + for _, v := range e.Value { + values = append(values, EnumValue{ + Name: *v.Name, + Value: fmt.Sprint(*v.Number), + }) + } + log.Printf("name: %v", *f.Name) + et := &EnumType{ + Name: *e.Name, + Values: values, + ProtoName: StripExtenstion(*f.Name), + } + typeMappings[*e.Name] = et + c.AllTypes = append(c.AllTypes, et) + } + + for _, m := range f.MessageType { + + ct := &ClassType{ + Name: *m.Name, + ProtoName: StripExtenstion(*f.Name), + } + typeMappings[*m.Name] = ct + c.AllTypes = append(c.AllTypes, ct) + } + + for _, m := range f.MessageType { + + fields := []ClassField{} + for _, f := range m.Field { + isMessage := *f.Type == descriptor.FieldDescriptorProto_TYPE_MESSAGE + isEnum := *f.Type == descriptor.FieldDescriptorProto_TYPE_ENUM + + var fieldType GeneratableType + if isMessage || isEnum { + fqn := strings.Split(*f.TypeName, ".") + className := fqn[1] + + fieldType = typeMappings[className] + } else { + primitiveType, ok := pbType2reflection[*f.Type] + if !ok { + log.Fatal("unsupported proto type", (*f.Type).String()) + } + fieldType = primitiveType + + // log.Printf("%#v == %v", primitiveType, *f.Type) + + } + if f.Label != nil && *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED { + fieldType = c.genericOf(NewVectorType, fieldType) + } else if f.Label != nil && *f.Label != descriptor.FieldDescriptorProto_LABEL_REQUIRED { + fieldType = c.genericOf(NewOptionalType, fieldType) + } + + fields = append(fields, ClassField{ + Name: *f.Name, + Type: fieldType, + ProtobufTag: uint32(*f.Number), + }) + } + ct := typeMappings[*m.Name].(*ClassType) + ct.Fields = fields + } + } +} + +func (c *Corntext) genericOf(constructor func(inner GeneratableType) GenericType, inner GeneratableType) (ret GeneratableType) { + sample := constructor(nil) + for _, t := range c.AllTypes { + if v, ok := t.(GenericType); ok { + + if reflect.TypeOf(sample).String() == reflect.TypeOf(v).String() && v.GetInnerType() == inner { + ret = v + + } + } + } + if ret == nil { + ret = constructor(inner) + + c.AllTypes = append(c.AllTypes, ret) + } + return +} + +func (c *Corntext) generateReflectionTypes() { + c.TypeIDEnum = &EnumType{ + Name: "ReflectTypeID", + ProtoName: "ReflectionInternal", + Values: []EnumValue{}, + } + c.TypeKindEnum = &EnumType{ + Name: "ReflectTypeKind", + ProtoName: "ReflectionInternal", + Values: []EnumValue{ + {Name: "Primitive", Value: "0"}, + {Name: "Enum", Value: "1"}, + {Name: "Class", Value: "2"}, + {Name: "Vector", Value: "3"}, + {Name: "Optional", Value: "4"}, + }, + } + c.ReflectField = &ClassType{ + Name: "ReflectField", + ProtoName: "ReflectionInternal", + Fields: []ClassField{ + {"typeID", 0, c.TypeIDEnum}, + {"name", 0, c.PrimitiveTypes["std::string"]}, + {"offset", 0, c.PrimitiveTypes["size_t"]}, + {"protobufTag", 0, c.PrimitiveTypes["uint32_t"]}, + }, + AdditionalLibraryIncludes: []string{ + "string", + }, + AdditionalCode: ` + ReflectField() {}; + ReflectField(ReflectTypeID typeID, std::string name, size_t offset, uint32_t protobufTag) { + this->typeID = typeID; + this->name = name; + this->offset = offset; + this->protobufTag = protobufTag; + } + `, + } + c.ReflectEnumValue = &ClassType{ + Name: "ReflectEnumValue", + ProtoName: "ReflectionInternal", + Fields: []ClassField{ + {"name", 0, c.PrimitiveTypes["std::string"]}, + {"value", 0, c.PrimitiveTypes["int"]}, + }, + AdditionalLibraryIncludes: []string{ + "string", + }, + AdditionalCode: ` + ReflectEnumValue(){}; + ReflectEnumValue( std::string name, int value) { + this->name = name; + this->value = value; + } + `, + } + c.vectorOfReflectFields = &VectorType{ + InnerType: c.ReflectField, + } + c.vectorOfReflectEnumValues = &VectorType{ + InnerType: c.ReflectEnumValue, + } + c.ReflectType = &ClassType{ + Name: "ReflectType", + ProtoName: "ReflectionInternal", + Fields: []ClassField{ + {"typeID", 0, c.TypeIDEnum}, + {"name", 0, c.PrimitiveTypes["std::string"]}, + {"kind", 0, c.TypeKindEnum}, + {"size", 0, c.PrimitiveTypes["size_t"]}, + {"innerType", 0, c.TypeIDEnum}, + {"fields", 0, c.vectorOfReflectFields}, + {"enumValues", 0, c.vectorOfReflectEnumValues}, + }, + AdditionalLibraryIncludes: []string{ + "string", + "vector", + }, + AdditionalCode: ` + void (*_Construct)(void *mem); + void (*_Destruct)(void *obj); + VectorOperations vectorOps; + OptionalOperations optionalOps; + static ReflectType ofPrimitive(ReflectTypeID id, std::string name, size_t size) { + ReflectType t; + t.kind = ReflectTypeKind::Primitive; + t.typeID = id; + t.name = name; + t.size = size; + return t; + } + static ReflectType ofEnum(ReflectTypeID id, std::string name, std::vector enumValues, size_t size) { + ReflectType t; + t.kind = ReflectTypeKind::Enum; + t.typeID = id; + t.name = name; + t.size = size; + t.enumValues = enumValues; + return t; + } + static ReflectType ofVector(ReflectTypeID id, ReflectTypeID innerType, size_t size, + VectorOperations vectorOps, + void (*_Construct)(void *mem), void (*_Destruct)(void *obj)) { + ReflectType t; + t.kind = ReflectTypeKind::Vector; + t.typeID = id; + t.innerType = innerType; + t.size = size; + t._Construct = _Construct; + t._Destruct = _Destruct; + t.vectorOps = vectorOps; + return t; + } + static ReflectType ofOptional(ReflectTypeID id, ReflectTypeID innerType, size_t size, + OptionalOperations optionalOps, + void (*_Construct)(void *mem), void (*_Destruct)(void *obj)) { + ReflectType t; + t.kind = ReflectTypeKind::Optional; + t.typeID = id; + t.innerType = innerType; + t.size = size; + t._Construct = _Construct; + t._Destruct = _Destruct; + t.optionalOps = optionalOps; + return t; + } + static ReflectType ofClass(ReflectTypeID id, std::string name, std::vector fields, size_t size, void (*_Construct)(void *mem), void (*_Destruct)(void *obj)) { + ReflectType t; + t.kind = ReflectTypeKind::Class; + t.name = name; + t.typeID = id; + t.size = size; + t.fields = std::move(fields); + t._Construct = _Construct; + t._Destruct = _Destruct; + return t; + } + + `, + } +} diff --git a/components/spotify/cspot/cpp-reflection/cpp_generator.go b/components/spotify/cspot/cpp-reflection/cpp_generator.go new file mode 100644 index 00000000..898b5267 --- /dev/null +++ b/components/spotify/cspot/cpp-reflection/cpp_generator.go @@ -0,0 +1,165 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "os" + "path" + "strings" + "unicode" +) + +type CppGenerator struct { + includes []string + files map[string]*CppGenerator + Body *bytes.Buffer + BodyBeforeLocalIncludes *bytes.Buffer + Filename string + IsHeader bool +} + +// NewCppGenerator docsy bo ci wywali sie blad xd +func NewCppGenerator(filename string) *CppGenerator { + isHeader := true + if strings.HasSuffix(filename, ".cpp") { + isHeader = false + } + return &CppGenerator{ + includes: []string{}, + Body: &bytes.Buffer{}, + BodyBeforeLocalIncludes: &bytes.Buffer{}, + files: make(map[string]*CppGenerator), + Filename: filename, + IsHeader: isHeader, + } +} + +func (cg *CppGenerator) SubFile(filename string, isHeader bool) *CppGenerator { + if gen, ok := cg.files[filename]; ok { + return gen + } + gen := NewCppGenerator(filename) + gen.IsHeader = isHeader + gen.files = cg.files + cg.files[filename] = gen + return gen +} + +// AddLibraryInclude yes +func (cg *CppGenerator) AddLibraryInclude(name string) *CppGenerator { + resultingLine := fmt.Sprintf("#include <%s>", name) + for _, a := range cg.includes { + if a == resultingLine { + return cg + } + } + cg.includes = append(cg.includes, resultingLine) + return cg +} + +func (cg *CppGenerator) AddLocalInclude(name string) *CppGenerator { + resultingLine := fmt.Sprintf("#include \"%s\"", name) + for _, a := range cg.includes { + if a == resultingLine { + return cg + } + } + cg.includes = append(cg.includes, resultingLine) + return cg +} + +// OutputClassField yes +func (cg *CppGenerator) OutputClassField(theType string, name string) { + fmt.Fprintf(cg.Body, "%v %v;\n", theType, name) +} + +// OutputClassTypeID yes +func (cg *CppGenerator) OutputClassTypeID(theID string) { + fmt.Fprintf(cg.Body, "static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::%v;\n", theID) +} + +// OutputClass yes +func (cg *CppGenerator) OutputClass(name string, cb func()) { + fmt.Fprintf(cg.Body, "class %v {\npublic:\n", name) + cb() + fmt.Fprintf(cg.Body, "};\n\n") +} + +// OutputEnumClass +func (cg *CppGenerator) OutputEnumClass(name string, cb func()) { + fmt.Fprintf(cg.Body, "enum class %v {\n", name) + cb() + fmt.Fprintf(cg.Body, "};\n\n") +} + +func (cg *CppGenerator) OutputArrayVariable(t string, name string, length int, cb func()) { + fmt.Fprintf(cg.Body, "%v %v[%d] = {\n", t, name, length) + cb() + fmt.Fprintf(cg.Body, "};\n\n") +} + +func (cg *CppGenerator) OutputArrayVariableExtern(t string, name string, length int) { + fmt.Fprintf(cg.Body, "extern %v %v[%d];", t, name, length) +} + +func (cg *CppGenerator) OutputEnumClassField(name string, value string) { + fmt.Fprintf(cg.Body, "%v", name) + if value != "" { + fmt.Fprintf(cg.Body, " = %v", value) + } + fmt.Fprintf(cg.Body, ",\n") +} + +func (cg *CppGenerator) EscapeCppString(str string) string { + d, _ := json.Marshal(str) + + return string(d) +} + +func (cg *CppGenerator) WriteToWriter(w io.Writer) { + fmt.Fprintf(w, "// THIS CORNFILE IS GENERATED. DO NOT EDIT! 🌽\n") + guardString := "_" + for _, c := range []rune(cg.Filename) { + if unicode.IsUpper(c) { + guardString += "_" + } + if unicode.IsLetter(c) { + guardString += strings.ToUpper(string([]rune{c})) + } + } + if cg.IsHeader { + + fmt.Fprintf(w, "#ifndef %v\n", guardString) + fmt.Fprintf(w, "#define %v\n", guardString) + } + for _, a := range cg.includes { + if strings.Contains(a, "<") { + fmt.Fprintf(w, "%v\n", a) + } + } + io.Copy(w, cg.BodyBeforeLocalIncludes) + for _, a := range cg.includes { + if !strings.Contains(a, "<") && a != fmt.Sprintf("#include \"%v\"", cg.Filename) { + fmt.Fprintf(w, "%v\n", a) + } + } + io.Copy(w, cg.Body) + if cg.IsHeader { + fmt.Fprintf(w, "#endif\n") + } +} + +func (cg *CppGenerator) OutputToDirectory(dirPath string) { + f, _ := os.Create(path.Join(dirPath, cg.Filename)) + defer f.Close() + cg.WriteToWriter(f) + + for _, fileToOutput := range cg.files { + f, _ := os.Create(path.Join(dirPath, fileToOutput.Filename)) + defer f.Close() + fileToOutput.WriteToWriter(f) + } + +} diff --git a/components/spotify/cspot/cpp-reflection/enum_type.go b/components/spotify/cspot/cpp-reflection/enum_type.go new file mode 100644 index 00000000..00d58d9a --- /dev/null +++ b/components/spotify/cspot/cpp-reflection/enum_type.go @@ -0,0 +1,44 @@ +package main + +import "fmt" + +type EnumValue struct { + Name string + Value string +} + +type EnumType struct { + Name string + Values []EnumValue + ProtoName string +} + +func (et *EnumType) IdentifierName() string { + return "Enum" + et.Name +} + +func (et *EnumType) DeclarationOrder() int { + return -20 +} + +func (et *EnumType) CppType() string { + return et.Name +} + +func (et *EnumType) WriteDeclarations(gen *CppGenerator) { + enumSubfile := gen.SubFile(et.ProtoName+".h", true) + gen.AddLocalInclude(enumSubfile.Filename) + enumSubfile.OutputEnumClass(et.Name, func() { + for _, v := range et.Values { + enumSubfile.OutputEnumClassField(v.Name, v.Value) + } + }) +} +func (et *EnumType) WriteReflection(gen *CppGenerator) { + enumValues := "" + for _, v := range et.Values { + enumValues += fmt.Sprintf(" ReflectEnumValue(%v, %v),\n", gen.EscapeCppString(v.Name), v.Value) + } + fmt.Fprintf(gen.Body, "ReflectType::ofEnum(/* mine id */ ReflectTypeID::%v, /* name */ %v, /* enum values */ std::move(std::vector{%v}), /* size */ sizeof(%v))", + et.IdentifierName(), gen.EscapeCppString(et.CppType()), enumValues, et.CppType()) +} diff --git a/components/spotify/cspot/cpp-reflection/go.sum b/components/spotify/cspot/cpp-reflection/go.sum new file mode 100644 index 00000000..d59f5e96 --- /dev/null +++ b/components/spotify/cspot/cpp-reflection/go.sum @@ -0,0 +1,21 @@ +github.com/celer-network/pb3-gen-sol v1.1.1 h1:MaWWsz17plSDCh538l6QaJQ1cOORqzx82lt/slz9pkM= +github.com/celer-network/pb3-gen-sol v1.1.1/go.mod h1:mJkD650gSksCeWrGLzKfj1nMc7y0cFlP3ZC2qaqOk3g= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= diff --git a/components/spotify/cspot/cpp-reflection/main.go b/components/spotify/cspot/cpp-reflection/main.go new file mode 100644 index 00000000..9465fbc0 --- /dev/null +++ b/components/spotify/cspot/cpp-reflection/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "io/ioutil" + "log" + "os" + + "github.com/celer-network/pb3-gen-sol/generator" + "google.golang.org/protobuf/proto" +) + +func main() { + c := NewCorntext() + + data, err := ioutil.ReadAll(os.Stdin) + if err != nil { + generator.Error(err, "reading input") + } + + if err := proto.Unmarshal(data, c.Request); err != nil { + generator.Error(err, "parsing input proto") + } + c.buildAllTypes() + c.outputTypes() + // Send back the results. + data, err = proto.Marshal(c.Response) + if err != nil { + log.Fatal(err, "failed to marshal output proto") + } + _, err = os.Stdout.Write(data) + if err != nil { + log.Fatal(err, "failed to write output proto") + } +} diff --git a/components/spotify/cspot/cpp-reflection/optional_helper_types.go b/components/spotify/cspot/cpp-reflection/optional_helper_types.go new file mode 100644 index 00000000..c0b11ce3 --- /dev/null +++ b/components/spotify/cspot/cpp-reflection/optional_helper_types.go @@ -0,0 +1,53 @@ +package main + +import "fmt" + +type OptionalHelperTypes struct { +} + +func (oht *OptionalHelperTypes) GenerateOptionalOperationsStruct(cg *CppGenerator) { + fmt.Fprintln(cg.BodyBeforeLocalIncludes, ` + class AnyRef; + struct OptionalOperations { + AnyRef (*get)(AnyRef &opt); + bool (*has_value)(AnyRef &opt); + void (*set)(AnyRef &opt, AnyRef &val); + void (*reset)(AnyRef &opt); + void (*emplaceEmpty)(AnyRef &opt); + }; + + `) +} + +func (oht *OptionalHelperTypes) GenerateOptionalManipulator(cg *CppGenerator) { + fmt.Fprintln(cg.Body, ` + template + class __OptionalManipulator { + public: + static AnyRef get(AnyRef &opt) { + auto theOptional = reinterpret_cast*>(opt.value.voidptr); + return AnyRef::of(&**theOptional); + } + static bool has_value(AnyRef &opt) { + auto theOptional = reinterpret_cast*>(opt.value.voidptr); + return theOptional->has_value(); + } + static void set(AnyRef &opt, AnyRef &val) { + auto theOptional = reinterpret_cast*>(opt.value.voidptr); + auto theValue = reinterpret_cast(val.value.voidptr); + *theOptional = *theValue; + } + + static void reset(AnyRef &opt) { + auto theOptional = reinterpret_cast*>(opt.value.voidptr); + theOptional->reset(); + } + + static void emplaceEmpty(AnyRef &opt) { + auto theOptional = reinterpret_cast*>(opt.value.voidptr); + theOptional->emplace(); + } + }; + + `) +} diff --git a/components/spotify/cspot/cpp-reflection/optional_type.go b/components/spotify/cspot/cpp-reflection/optional_type.go new file mode 100644 index 00000000..beaabaaa --- /dev/null +++ b/components/spotify/cspot/cpp-reflection/optional_type.go @@ -0,0 +1,54 @@ +package main + +import ( + "fmt" + "text/template" +) + +type OptionalType struct { + InnerType GeneratableType +} + +func NewOptionalType(inner GeneratableType) GenericType { + return &OptionalType{ + InnerType: inner, + } +} + +func (vt *OptionalType) GetInnerType() GeneratableType { + return vt.InnerType +} + +func (vt *OptionalType) IdentifierName() string { + return "OptionalOf" + vt.InnerType.IdentifierName() +} +func (vt *OptionalType) CppType() string { + return fmt.Sprintf("std::optional<%v>", vt.InnerType.CppType()) +} + +func (vt *OptionalType) WriteDeclarations(gen *CppGenerator) { + gen.AddLibraryInclude("optional") +} +func (vt *OptionalType) WriteReflection(gen *CppGenerator) { + + template.Must(template.New("any.cpp").Parse(` + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::{{ .IdentifierName }}, + /* inner type id */ ReflectTypeID::{{ .InnerType.IdentifierName }}, + /* size */ sizeof({{ .CppType }}), + /* option */ OptionalOperations{ + .get = __OptionalManipulator<{{ .InnerType.CppType }}>::get, + .has_value = __OptionalManipulator<{{ .InnerType.CppType }}>::has_value, + .set = __OptionalManipulator<{{ .InnerType.CppType }}>::set, + .reset = __OptionalManipulator<{{ .InnerType.CppType }}>::reset, + .emplaceEmpty = __OptionalManipulator<{{ .InnerType.CppType }}>::emplaceEmpty, + }, + __reflectConstruct<{{ .CppType }}>, + __reflectDestruct<{{ .CppType }}> + ) + + + + `)).Execute(gen.Body, vt) + +} diff --git a/components/spotify/cspot/cpp-reflection/primitive_type.go b/components/spotify/cspot/cpp-reflection/primitive_type.go new file mode 100644 index 00000000..d6dd47bf --- /dev/null +++ b/components/spotify/cspot/cpp-reflection/primitive_type.go @@ -0,0 +1,26 @@ +package main + +import "fmt" + +type PrimitiveType struct { + identifierName string + cppType string +} + +func (pt *PrimitiveType) IdentifierName() string { + return pt.identifierName +} + +func (pt *PrimitiveType) CppType() string { + return pt.cppType +} + +func (pt *PrimitiveType) WriteDeclarations(gen *CppGenerator) { + if pt.cppType == "std::string" { + gen.AddLibraryInclude("string") + } +} +func (pt *PrimitiveType) WriteReflection(gen *CppGenerator) { + fmt.Fprintf(gen.Body, "ReflectType::ofPrimitive(/* type id */ ReflectTypeID::%v, /* name */ %v, /* size */ sizeof(%v))", + pt.IdentifierName(), gen.EscapeCppString(pt.CppType()), pt.CppType()) +} diff --git a/components/spotify/cspot/cpp-reflection/reflection_types.go b/components/spotify/cspot/cpp-reflection/reflection_types.go new file mode 100644 index 00000000..06ab7d0f --- /dev/null +++ b/components/spotify/cspot/cpp-reflection/reflection_types.go @@ -0,0 +1 @@ +package main diff --git a/components/spotify/cspot/cpp-reflection/util.go b/components/spotify/cspot/cpp-reflection/util.go new file mode 100644 index 00000000..7200e309 --- /dev/null +++ b/components/spotify/cspot/cpp-reflection/util.go @@ -0,0 +1,25 @@ +package main + +import ( + "path/filepath" + "strings" +) + +func AddIncludeForType(t GeneratableType, gen *CppGenerator) { + switch v := t.(type) { + case *ClassType: + gen.AddLocalInclude(v.ProtoName + ".h") + case *EnumType: + gen.AddLocalInclude(v.ProtoName + ".h") + case *VectorType: + gen.AddLibraryInclude("vector") + AddIncludeForType(v.InnerType, gen) + case *OptionalType: + gen.AddLibraryInclude("optional") + AddIncludeForType(v.InnerType, gen) + } +} + +func StripExtenstion(filename string) string { + return strings.TrimSuffix(filename, filepath.Ext(filename)) +} diff --git a/components/spotify/cspot/cpp-reflection/vector_helper_types.go b/components/spotify/cspot/cpp-reflection/vector_helper_types.go new file mode 100644 index 00000000..b327851e --- /dev/null +++ b/components/spotify/cspot/cpp-reflection/vector_helper_types.go @@ -0,0 +1,55 @@ +package main + +import "fmt" + +type VectorHelperTypes struct { +} + +func (vht *VectorHelperTypes) GenerateVectorOperationsStruct(cg *CppGenerator) { + fmt.Fprintln(cg.BodyBeforeLocalIncludes, ` + class AnyRef; + struct VectorOperations { + void (*push_back)(AnyRef &vec, AnyRef &val); + AnyRef (*at)(AnyRef &vec, size_t index); + size_t (*size)(AnyRef &vec); + void (*emplace_back)(AnyRef &vec); + void (*clear)(AnyRef &vec); + void (*reserve)(AnyRef &vec, size_t n); + }; + + `) +} + +func (vht *VectorHelperTypes) GenerateVectorManipulator(cg *CppGenerator) { + fmt.Fprintln(cg.Body, ` + template + class __VectorManipulator { + public: + static void push_back(AnyRef &vec, AnyRef &val) { + auto theVector = reinterpret_cast*>(vec.value.voidptr); + auto theValue = *reinterpret_cast(val.value.voidptr); + theVector->push_back(theValue); + }; + static AnyRef at(AnyRef &vec, size_t index) { + auto theVector = reinterpret_cast*>(vec.value.voidptr); + return AnyRef::of(&(*theVector)[index]); + }; + static size_t size(AnyRef &vec) { + auto theVector = reinterpret_cast*>(vec.value.voidptr); + return theVector->size(); + }; + static void emplace_back(AnyRef &vec) { + auto theVector = reinterpret_cast*>(vec.value.voidptr); + theVector->emplace_back(); + }; + static void clear(AnyRef &vec) { + auto theVector = reinterpret_cast*>(vec.value.voidptr); + theVector->clear(); + }; + static void reserve(AnyRef &vec, size_t n) { + auto theVector = reinterpret_cast*>(vec.value.voidptr); + theVector->reserve(n); + }; + }; + `) +} diff --git a/components/spotify/cspot/cpp-reflection/vector_type.go b/components/spotify/cspot/cpp-reflection/vector_type.go new file mode 100644 index 00000000..32575ddb --- /dev/null +++ b/components/spotify/cspot/cpp-reflection/vector_type.go @@ -0,0 +1,65 @@ +package main + +import ( + "fmt" + "text/template" +) + +type VectorType struct { + InnerType GeneratableType +} + +func NewVectorType(inner GeneratableType) GenericType { + return &VectorType{ + InnerType: inner, + } +} + +func (vt *VectorType) GetInnerType() GeneratableType { + return vt.InnerType +} + +func (vt *VectorType) IdentifierName() string { + return "VectorOf" + vt.InnerType.IdentifierName() +} +func (vt *VectorType) CppType() string { + return fmt.Sprintf("std::vector<%v>", vt.InnerType.CppType()) +} + +func (vt *VectorType) WriteDeclarations(gen *CppGenerator) { + gen.AddLibraryInclude("vector") +} +func (vt *VectorType) WriteReflection(gen *CppGenerator) { + + template.Must(template.New("any.cpp").Parse(` + ReflectType::ofVector( + /* mine typeId */ ReflectTypeID::{{ .IdentifierName }}, + /* inner type id */ ReflectTypeID::{{ .InnerType.IdentifierName }}, + /* size */ sizeof({{ .CppType }}), + VectorOperations{ + .push_back = __VectorManipulator<{{ .InnerType.CppType }}>::push_back, + .at = __VectorManipulator<{{ .InnerType.CppType }}>::at, + .size = __VectorManipulator<{{ .InnerType.CppType }}>::size, + .emplace_back = __VectorManipulator<{{ .InnerType.CppType }}>::emplace_back, + .clear = __VectorManipulator<{{ .InnerType.CppType }}>::clear, + .reserve = __VectorManipulator<{{ .InnerType.CppType }}>::reserve, + }, + __reflectConstruct<{{ .CppType }}>, + __reflectDestruct<{{ .CppType }}> + ) + + + + `)).Execute(gen.Body, vt) + // fmt.Fprintf(gen.Body, `ReflectType::ofVector( + // /* mine typeId */ ReflectTypeID::%v, + // /* inner type id */ ReflectTypeID::%v, + // /* size */ sizeof(%v), + // VectorOperations{ + // .push_back = __VectorManipulator<%v> + // }, + // __reflectConstruct<%v>, + // __reflectDestruct<%v> + // )`, + // vt.IdentifierName(), vt.InnerType.IdentifierName(), vt.CppType(), vt.CppType(), vt.CppType(), vt.CppType()) +} diff --git a/components/spotify/cspot/include/ApResolve.h b/components/spotify/cspot/include/ApResolve.h new file mode 100644 index 00000000..287f3c14 --- /dev/null +++ b/components/spotify/cspot/include/ApResolve.h @@ -0,0 +1,21 @@ +#ifndef APRESOLVE_H +#define APRESOLVE_H + +#include + +class ApResolve { +private: + std::string getApList(); + +public: + ApResolve(); + + /** + * @brief Connects to spotify's servers and returns first valid ap address + * + * @return std::string Address in form of url:port + */ + std::string fetchFirstApAddress(); +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/include/AudioChunk.h b/components/spotify/cspot/include/AudioChunk.h new file mode 100644 index 00000000..b633bd10 --- /dev/null +++ b/components/spotify/cspot/include/AudioChunk.h @@ -0,0 +1,74 @@ +#ifndef AUDIOCHUNK_H +#define AUDIOCHUNK_H +#include +#include +#include +#include +#include "pthread.h" +#include "platform/WrappedSemaphore.h" +#include "Crypto.h" +#include "Utils.h" +#include + +class AudioChunk { +private: + /** + * @brief Calculates a correct IV by performing bignum addition. + * + * @param num Number to add to IV. + * @return std::vector + */ + std::vector getIVSum(uint32_t num); + +public: + std::unique_ptr crypto; + std::vector decryptedData; + std::vector audioKey; + bool keepInMemory = false; + pthread_mutex_t loadingMutex; + std::mutex dataAccessMutex; + uint32_t startPosition; + uint32_t endPosition; + uint16_t seqId; + + size_t headerFileSize = -1; + bool isLoaded = false; + bool isFailed = false; + + /** + * @brief Triggered when audiochunk is fully downloaded and decrypted. + */ + std::unique_ptr isLoadedSemaphore; + + /** + * @brief + */ + std::unique_ptr isHeaderFileSizeLoadedSemaphore; + + + /** + * @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 + * @param predictedEndPosition Predicted end position of given chunk. This is not final positon. + */ + AudioChunk(uint16_t seqId, std::vector &audioKey, uint32_t startPosition, uint32_t predictedEndPosition); + ~AudioChunk(); + + /** + * @brief Appends incoming chunked data to local cache. + * + * @param data encrypted binary audio data. + */ + void appendData(std::vector &data); + + /** + * @brief Performs AES CTR decryption of received data. + * + */ + void decrypt(); +}; + +#endif diff --git a/components/spotify/cspot/include/AudioChunkManager.h b/components/spotify/cspot/include/AudioChunkManager.h new file mode 100644 index 00000000..d8956fd6 --- /dev/null +++ b/components/spotify/cspot/include/AudioChunkManager.h @@ -0,0 +1,57 @@ +#ifndef AUDIOCHUNKMANAGER_H +#define AUDIOCHUNKMANAGER_H + +#include +#include +#include +#include +#include "Utils.h" +#include "AudioChunk.h" +#include "Queue.h" +#include "Task.h" + +#define DATA_SIZE_HEADER 24 +#define DATA_SIZE_FOOTER 2 + +class AudioChunkManager : public bell::Task { + std::vector> chunks; + bell::Queue, bool>> audioChunkDataQueue; + void runTask(); +public: + AudioChunkManager(); + std::atomic isRunning = false; + std::mutex runningMutex; + /** + * @brief Registers a new audio chunk request. + * + * Registering an audiochunk will trigger a request to spotify servers. + * All the incoming data will be redirected to this given audiochunk. + * + * @param seqId sequence identifier of given audio chunk. + * @param audioKey audio key of given file, used for decryption. + * @param startPos start position of audio chunk + * @param endPos end position of audio chunk. end - pos % 4 must be 0. + * @return std::shared_ptr registered audio chunk. Does not contain the data yet. + */ + std::shared_ptr registerNewChunk(uint16_t seqId, std::vector &audioKey, uint32_t startPos, uint32_t endPos); + + /** + * @brief Pushes binary data from spotify's servers containing audio chunks. + * + * This method pushes received data to a queue that is then received by manager's thread. + * That thread parses the data and passes it to a matching audio chunk. + * + * @param data binary data received from spotify's servers + * @param failed whenever given chunk request failed + */ + void handleChunkData(std::vector& data, bool failed = false); + + /** + * @brief Fails all requested chunks, used for reconnection. + */ + void failAllChunks(); + + void close(); +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/include/AudioSink.h b/components/spotify/cspot/include/AudioSink.h new file mode 100644 index 00000000..a2f2a0a7 --- /dev/null +++ b/components/spotify/cspot/include/AudioSink.h @@ -0,0 +1,18 @@ +#ifndef AUDIOSINK_H +#define AUDIOSINK_H + +#include +#include + +class AudioSink +{ +public: + AudioSink() {} + virtual ~AudioSink() {} + virtual void feedPCMFrames(std::vector &data) = 0; + virtual void volumeChanged(uint16_t volume) {} + bool softwareVolumeControl = true; + bool usign = false; +}; + +#endif diff --git a/components/spotify/cspot/include/ChunkedAudioStream.h b/components/spotify/cspot/include/ChunkedAudioStream.h new file mode 100644 index 00000000..e0b8f5ad --- /dev/null +++ b/components/spotify/cspot/include/ChunkedAudioStream.h @@ -0,0 +1,75 @@ +#ifndef CHUNKEDAUDIOSTREAM_H +#define CHUNKEDAUDIOSTREAM_H + +#include +#include +#include +#include +#include +#include +#include "ivorbisfile.h" +#include "MercuryManager.h" +#include "AudioSink.h" +#include "AudioChunk.h" +#include "platform/WrappedMutex.h" + +#define SPOTIFY_HEADER_SIZE 167 +#define BUFFER_SIZE 0x20000 * 1.5 +typedef std::function&)> pcmDataCallback; + +enum class Whence +{ + START, + CURRENT, + END +}; + +class ChunkedAudioStream +{ +private: + // Vorbis related + OggVorbis_File vorbisFile; + ov_callbacks vorbisCallbacks; + int currentSection; + + // Audio chunking + std::vector audioKey; + std::vector> chunks; + + // Audio data + uint32_t duration; + + bool loadingChunks = false; + uint16_t lastSequenceId = 0; + + std::shared_ptr manager; + std::vector fileId; + uint32_t startPositionMs; + + std::shared_ptr requestChunk(size_t chunkIndex); + void fetchTraillingPacket(); + std::shared_ptr findChunkForPosition(size_t position); + +public: + ChunkedAudioStream(std::vector fileId, std::vector audioKey, uint32_t duration, std::shared_ptr manager, uint32_t startPositionMs, bool isPaused); + ~ChunkedAudioStream(); + int requestedChunkIndex = 0; + std::function streamFinishedCallback; + size_t pos = SPOTIFY_HEADER_SIZE; // size of some spotify header + uint32_t fileSize; + uint32_t readBeforeSeek = 0; + bool loadingMeta = true; + std::atomic isPaused = false; + std::atomic isRunning = false; + std::atomic finished = false; + pcmDataCallback pcmCallback; + std::shared_ptr audioSink; + WrappedMutex seekMutex; + + std::vector read(size_t bytes); + void seekMs(uint32_t positionMs); + void seek(size_t pos, Whence whence); + void startPlaybackLoop(); +}; + +#endif diff --git a/components/spotify/cspot/include/ConfigJSON.h b/components/spotify/cspot/include/ConfigJSON.h new file mode 100644 index 00000000..8ebaeaf1 --- /dev/null +++ b/components/spotify/cspot/include/ConfigJSON.h @@ -0,0 +1,26 @@ +#ifndef CONFIGJSON_H +#define CONFIGJSON_H + +#include +#include +#include "FileHelper.h" +#include "ProtoHelper.h" + +class ConfigJSON +{ +private: + std::shared_ptr _file; + std::string _jsonFileName; +public: + ConfigJSON(std::string jsonFileName, std::shared_ptr file); + bool load(); + bool save(); + + uint16_t volume; + std::string deviceName; + AudioFormat format; +}; + +extern std::shared_ptr configMan; + +#endif diff --git a/components/spotify/cspot/include/ConstantParameters.h b/components/spotify/cspot/include/ConstantParameters.h new file mode 100644 index 00000000..4b4ccd62 --- /dev/null +++ b/components/spotify/cspot/include/ConstantParameters.h @@ -0,0 +1,15 @@ +#ifndef CONSTANTPARAMETERS_H +#define CONSTANTPARAMETERS_H + +#define MAX_VOLUME 65536 + +// Hardcoded information sent to spotify servers +const char * const deviceId = "162137fd329622137a14901634264e6f332e2422"; +const char * const informationString = "cspot"; +const char * const brandName = "corn"; +const char * const versionString = "cspot-1.0"; +const char * const protocolVersion = "2.7.1"; +const char * const defaultDeviceName = "CSpot"; +const char * const swVersion = "1.0.0"; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/include/CspotAssert.h b/components/spotify/cspot/include/CspotAssert.h new file mode 100644 index 00000000..de535984 --- /dev/null +++ b/components/spotify/cspot/include/CspotAssert.h @@ -0,0 +1,16 @@ +#ifndef CSPOT_ASSERT_H +#define CSPOT_ASSERT_H +#include +#include + +#define CSPOT_ASSERT(CONDITION, MESSAGE) \ + do \ + { \ + if (!(CONDITION)) \ + { \ + printf("At %s in %s:%d\n Assertion %s failed: %s", __func__, __FILE__, __LINE__, #CONDITION, MESSAGE); \ + abort(); \ + } \ + } while (0) + +#endif diff --git a/components/spotify/cspot/include/FileHelper.h b/components/spotify/cspot/include/FileHelper.h new file mode 100644 index 00000000..c8ab1ad0 --- /dev/null +++ b/components/spotify/cspot/include/FileHelper.h @@ -0,0 +1,16 @@ +#ifndef FILEHELPER_H +#define FILEHELPER_H + +#include +#include + +class FileHelper +{ +public: + FileHelper() {} + virtual ~FileHelper() {} + virtual bool readFile(std::string filename, std::string &fileContent) = 0; + virtual bool writeFile(std::string filename, std::string fileContent) = 0; +}; + +#endif diff --git a/components/spotify/cspot/include/Keyexchange.h b/components/spotify/cspot/include/Keyexchange.h new file mode 100644 index 00000000..6bd79b81 --- /dev/null +++ b/components/spotify/cspot/include/Keyexchange.h @@ -0,0 +1,465 @@ +// AUTOGENERATED FILE, DO NOT EDIT BY HAND +#ifndef PB_KEYEXCHANGE_H +#define PB_KEYEXCHANGE_H + +#include +#include +#include +#include +#include + +enum class Product : uint32_t { + PRODUCT_CLIENT = 0, + PRODUCT_LIBSPOTIFY = 1, + PRODUCT_MOBILE = 2, + PRODUCT_PARTNER = 3, + PRODUCT_LIBSPOTIFY_EMBEDDED = 5 +}; + +enum class Platform : uint32_t { + PLATFORM_WIN32_X86 = 0, + PLATFORM_OSX_X86 = 1, + PLATFORM_LINUX_X86 = 2, + PLATFORM_IPHONE_ARM = 3, + PLATFORM_S60_ARM = 4, + PLATFORM_OSX_PPC = 5, + PLATFORM_ANDROID_ARM = 6, + PLATFORM_WINDOWS_CE_ARM = 7, + PLATFORM_LINUX_X86_64 = 8, + PLATFORM_OSX_X86_64 = 9, + PLATFORM_PALM_ARM = 10, + PLATFORM_LINUX_SH = 11, + PLATFORM_FREEBSD_X86 = 12, + PLATFORM_FREEBSD_X86_64 = 13, + PLATFORM_BLACKBERRY_ARM = 14, + PLATFORM_SONOS = 15, + PLATFORM_LINUX_MIPS = 16, + PLATFORM_LINUX_ARM = 17, + PLATFORM_LOGITECH_ARM = 18, + PLATFORM_LINUX_BLACKFIN = 19, + PLATFORM_WP7_ARM = 20, + PLATFORM_ONKYO_ARM = 21, + PLATFORM_QNXNTO_ARM = 22, + PLATFORM_BCO_ARM = 23 +}; + +enum class Cryptosuite : uint32_t { + CRYPTO_SUITE_SHANNON = 0, + CRYPTO_SUITE_RC4_SHA1_HMAC = 1 +}; + +class LoginCryptoDiffieHellmanChallenge : public BaseProtobufMessage { +private: +public: + LoginCryptoDiffieHellmanChallenge() {}; + std::vector gs; + + bool decodeField(std::shared_ptr reader) { + switch (reader->currentTag) + { + case 10: + reader->decodeVector(gs); + break; + default: + return false; + } + return true; + } + + void encodeWithWriter(std::shared_ptr writer) { + writer->addVector(10, gs); + } +}; + +class LoginCryptoChallengeUnion : public BaseProtobufMessage { +private: +public: + LoginCryptoChallengeUnion() {}; + LoginCryptoDiffieHellmanChallenge diffie_hellman; + + bool decodeField(std::shared_ptr reader) { + switch (reader->currentTag) + { + case 10: + lastMessagePosition = reader->pos + reader->decodeVarInt(); + diffie_hellman.parseWithReader(reader); + reader->maxPosition = lastMessagePosition; + break; + default: + return false; + } + return true; + } + + void encodeWithWriter(std::shared_ptr writer) { + lastMessagePosition = writer->startMessage(); + diffie_hellman.encodeWithWriter(writer); + writer->finishMessage(10, lastMessagePosition); + } +}; + +class LoginCryptoDiffieHellmanHello : public BaseProtobufMessage { +private: +public: + LoginCryptoDiffieHellmanHello() {}; + std::vector gc; + uint32_t server_keys_known; + + bool decodeField(std::shared_ptr reader) { + switch (reader->currentTag) + { + case 10: + reader->decodeVector(gc); + break; + case 20: + server_keys_known = reader->decodeVarInt(); + break; + default: + return false; + } + return true; + } + + void encodeWithWriter(std::shared_ptr writer) { + writer->addVector(10, gc); + writer->addVarInt(20, server_keys_known); + } +}; + +class LoginCryptoHelloUnion : public BaseProtobufMessage { +private: +public: + LoginCryptoHelloUnion() {}; + LoginCryptoDiffieHellmanHello diffie_hellman; + + bool decodeField(std::shared_ptr reader) { + switch (reader->currentTag) + { + case 10: + lastMessagePosition = reader->pos + reader->decodeVarInt(); + diffie_hellman.parseWithReader(reader); + reader->maxPosition = lastMessagePosition; + break; + default: + return false; + } + return true; + } + + void encodeWithWriter(std::shared_ptr writer) { + lastMessagePosition = writer->startMessage(); + diffie_hellman.encodeWithWriter(writer); + writer->finishMessage(10, lastMessagePosition); + } +}; + +class BuildInfo : public BaseProtobufMessage { +private: +public: + BuildInfo() {}; + Product product; + Platform platform; + uint64_t version; + + bool decodeField(std::shared_ptr reader) { + switch (reader->currentTag) + { + case 10: + product = static_cast(reader->decodeVarInt()); + break; + case 30: + platform = static_cast(reader->decodeVarInt()); + break; + case 40: + version = reader->decodeVarInt(); + break; + default: + return false; + } + return true; + } + + void encodeWithWriter(std::shared_ptr writer) { + writer->addVarInt(10, static_cast(product)); + writer->addVarInt(30, static_cast(platform)); + writer->addVarInt(40, version); + } +}; + +class FeatureSet : public BaseProtobufMessage { +private: +public: + FeatureSet() {}; + bool autoupdate2; + + bool decodeField(std::shared_ptr reader) { + switch (reader->currentTag) + { + case 1: + autoupdate2 = reader->decodeVarInt(); + break; + default: + return false; + } + return true; + } + + void encodeWithWriter(std::shared_ptr writer) { + writer->addVarInt(1, autoupdate2); + } +}; + +class APChallenge : public BaseProtobufMessage { +private: +public: + APChallenge() {}; + LoginCryptoChallengeUnion login_crypto_challenge; + + bool decodeField(std::shared_ptr reader) { + switch (reader->currentTag) + { + case 10: + lastMessagePosition = reader->pos + reader->decodeVarInt(); + login_crypto_challenge.parseWithReader(reader); + reader->maxPosition = lastMessagePosition; + break; + default: + return false; + } + return true; + } + + void encodeWithWriter(std::shared_ptr writer) { + lastMessagePosition = writer->startMessage(); + login_crypto_challenge.encodeWithWriter(writer); + writer->finishMessage(10, lastMessagePosition); + } +}; + +class APResponseMessage : public BaseProtobufMessage { +private: +public: + APResponseMessage() {}; + APChallenge challenge; + + bool decodeField(std::shared_ptr reader) { + switch (reader->currentTag) + { + case 10: + lastMessagePosition = reader->pos + reader->decodeVarInt(); + challenge.parseWithReader(reader); + reader->maxPosition = lastMessagePosition; + break; + default: + return false; + } + return true; + } + + void encodeWithWriter(std::shared_ptr writer) { + lastMessagePosition = writer->startMessage(); + challenge.encodeWithWriter(writer); + writer->finishMessage(10, lastMessagePosition); + } +}; + +class LoginCryptoDiffieHellmanResponse : public BaseProtobufMessage { +private: +public: + LoginCryptoDiffieHellmanResponse() {}; + std::vector hmac; + + bool decodeField(std::shared_ptr reader) { + switch (reader->currentTag) + { + case 10: + reader->decodeVector(hmac); + break; + default: + return false; + } + return true; + } + + void encodeWithWriter(std::shared_ptr writer) { + writer->addVector(10, hmac); + } +}; + +class LoginCryptoResponseUnion : public BaseProtobufMessage { +private: +public: + LoginCryptoResponseUnion() {}; + LoginCryptoDiffieHellmanResponse diffie_hellman; + + bool decodeField(std::shared_ptr reader) { + switch (reader->currentTag) + { + case 10: + lastMessagePosition = reader->pos + reader->decodeVarInt(); + diffie_hellman.parseWithReader(reader); + reader->maxPosition = lastMessagePosition; + break; + default: + return false; + } + return true; + } + + void encodeWithWriter(std::shared_ptr writer) { + lastMessagePosition = writer->startMessage(); + diffie_hellman.encodeWithWriter(writer); + writer->finishMessage(10, lastMessagePosition); + } +}; + +class CryptoResponseUnion : public BaseProtobufMessage { +private: +public: + CryptoResponseUnion() {}; + + bool decodeField(std::shared_ptr reader) { + switch (reader->currentTag) + { + default: + return false; + } + return true; + } + + void encodeWithWriter(std::shared_ptr writer) { + } +}; + +class PoWResponseUnion : public BaseProtobufMessage { +private: +public: + PoWResponseUnion() {}; + + bool decodeField(std::shared_ptr reader) { + switch (reader->currentTag) + { + default: + return false; + } + return true; + } + + void encodeWithWriter(std::shared_ptr writer) { + } +}; + +class ClientResponsePlaintext : public BaseProtobufMessage { +private: +public: + ClientResponsePlaintext() {}; + LoginCryptoResponseUnion login_crypto_response; + PoWResponseUnion pow_response; + CryptoResponseUnion crypto_response; + + bool decodeField(std::shared_ptr reader) { + switch (reader->currentTag) + { + case 10: + lastMessagePosition = reader->pos + reader->decodeVarInt(); + login_crypto_response.parseWithReader(reader); + reader->maxPosition = lastMessagePosition; + break; + case 20: + lastMessagePosition = reader->pos + reader->decodeVarInt(); + pow_response.parseWithReader(reader); + reader->maxPosition = lastMessagePosition; + break; + case 30: + lastMessagePosition = reader->pos + reader->decodeVarInt(); + crypto_response.parseWithReader(reader); + reader->maxPosition = lastMessagePosition; + break; + default: + return false; + } + return true; + } + + void encodeWithWriter(std::shared_ptr writer) { + lastMessagePosition = writer->startMessage(); + login_crypto_response.encodeWithWriter(writer); + writer->finishMessage(10, lastMessagePosition); + lastMessagePosition = writer->startMessage(); + pow_response.encodeWithWriter(writer); + writer->finishMessage(20, lastMessagePosition); + lastMessagePosition = writer->startMessage(); + crypto_response.encodeWithWriter(writer); + writer->finishMessage(30, lastMessagePosition); + } +}; + +class ClientHello : public BaseProtobufMessage { +private: +public: + ClientHello() {}; + BuildInfo build_info; + LoginCryptoHelloUnion login_crypto_hello; + std::vector cryptosuites_supported; + std::vector client_nonce; + std::vector padding; + FeatureSet feature_set; + + bool decodeField(std::shared_ptr reader) { + if (firstField) { + cryptosuites_supported.clear(); + } + switch (reader->currentTag) + { + case 10: + lastMessagePosition = reader->pos + reader->decodeVarInt(); + build_info.parseWithReader(reader); + reader->maxPosition = lastMessagePosition; + break; + case 50: + lastMessagePosition = reader->pos + reader->decodeVarInt(); + login_crypto_hello.parseWithReader(reader); + reader->maxPosition = lastMessagePosition; + break; + case 30: + cryptosuites_supported.push_back(Cryptosuite()); + cryptosuites_supported[cryptosuites_supported.size()-1] = static_cast(reader->decodeVarInt()); + break; + case 60: + reader->decodeVector(client_nonce); + break; + case 70: + reader->decodeVector(padding); + break; + case 80: + lastMessagePosition = reader->pos + reader->decodeVarInt(); + feature_set.parseWithReader(reader); + reader->maxPosition = lastMessagePosition; + break; + default: + return false; + } + return true; + } + + void encodeWithWriter(std::shared_ptr writer) { + lastMessagePosition = writer->startMessage(); + build_info.encodeWithWriter(writer); + writer->finishMessage(10, lastMessagePosition); + lastMessagePosition = writer->startMessage(); + login_crypto_hello.encodeWithWriter(writer); + writer->finishMessage(50, lastMessagePosition); + lastMessagePosition = writer->startMessage(); + for (int i = 0; i < cryptosuites_supported.size(); i++) { + writer->encodeVarInt(static_cast(cryptosuites_supported[i])); + } + writer->finishMessage(30, lastMessagePosition); + writer->addVector(60, client_nonce); + writer->addVector(70, padding); + lastMessagePosition = writer->startMessage(); + feature_set.encodeWithWriter(writer); + writer->finishMessage(80, lastMessagePosition); + } +}; + +#endif diff --git a/components/spotify/cspot/include/Logger.h b/components/spotify/cspot/include/Logger.h new file mode 100644 index 00000000..b45a3147 --- /dev/null +++ b/components/spotify/cspot/include/Logger.h @@ -0,0 +1,12 @@ +#ifndef LOGGER_H +#define LOGGER_H + +#include + +#define CSPOT_LOG(type, ...) \ + do \ + { \ + bell::bellGlobalLogger->type(__FILE__, __LINE__, "cspot", __VA_ARGS__); \ + } while (0) + +#endif diff --git a/components/spotify/cspot/include/LoginBlob.h b/components/spotify/cspot/include/LoginBlob.h new file mode 100644 index 00000000..fe77a6e9 --- /dev/null +++ b/components/spotify/cspot/include/LoginBlob.h @@ -0,0 +1,35 @@ +#ifndef LOGINBLOB_H +#define LOGINBLOB_H + +#include +#include +#include +#include "Crypto.h" +#include "ProtoHelper.h" + +class LoginBlob +{ +private: + int blobSkipPosition = 0; + std::unique_ptr crypto; + + + uint32_t readBlobInt(const std::vector& loginData); + std::vector decodeBlob(const std::vector& blob, const std::vector& sharedKey); + std::vector decodeBlobSecondary(const std::vector& blob, const std::string& username, const std::string& deviceId); + +public: + LoginBlob(); + std::vector authData; + std::string username; + int authType; + + // Loading + void loadZeroconf(const std::vector& blob, const std::vector& sharedKey, const std::string& deviceId, const std::string& username); + void loadUserPass(const std::string& username, const std::string& password); + void loadJson(const std::string& json); + + std::string toJson(); +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/include/MercuryManager.h b/components/spotify/cspot/include/MercuryManager.h new file mode 100644 index 00000000..afe11b6a --- /dev/null +++ b/components/spotify/cspot/include/MercuryManager.h @@ -0,0 +1,100 @@ +#ifndef MERCURY_MANAGER_H +#define MERCURY_MANAGER_H + +#include +#include +#include +#include +#include +#include "ShannonConnection.h" +#include "MercuryResponse.h" +#include "Packet.h" +#include "Utils.h" +#include "ProtoHelper.h" +#include "MercuryManager.h" +#include "AudioChunk.h" +#include "AudioChunkManager.h" +#include +#include "Task.h" +#include "platform/WrappedSemaphore.h" +#include "TimeProvider.h" +#include "Session.h" + +#include +#include + +#define AUDIOCHUNK_TIMEOUT_MS 5 * 1000 +#define RECONNECTION_RETRY_MS 5 * 1000 +#define PING_TIMEOUT_MS 2 * 60 * 1000 + 5000 + +typedef std::function)> mercuryCallback; +typedef std::function)> audioKeyCallback; +typedef std::function voidCallback; + +#define AUDIO_CHUNK_SIZE 0x20000 + +enum class MercuryType : uint8_t +{ + SUB = 0xb3, + UNSUB = 0xb4, + SUBRES = 0xb5, + SEND = 0xb2, + GET = 0xFF, // Shitty workaround, it's value is actually same as SEND + PING = 0x04, + PONG_ACK = 0x4a, + AUDIO_CHUNK_REQUEST_COMMAND = 0x08, + AUDIO_CHUNK_SUCCESS_RESPONSE = 0x09, + AUDIO_CHUNK_FAILURE_RESPONSE = 0x0A, + AUDIO_KEY_REQUEST_COMMAND = 0x0C, + AUDIO_KEY_SUCCESS_RESPONSE = 0x0D, + AUDIO_KEY_FAILURE_RESPONSE = 0x0E, + COUNTRY_CODE_RESPONSE = 0x1B, +}; + +extern std::map MercuryTypeMap; + +class MercuryManager : public bell::Task +{ +private: + std::map callbacks; + std::mutex reconnectionMutex; + std::mutex runningMutex; + std::map subscriptions; + std::unique_ptr session; + std::shared_ptr lastAuthBlob; + std::unique_ptr audioChunkManager; + std::vector> queue; + std::unique_ptr queueSemaphore; + unsigned long long lastRequestTimestamp = -1; + unsigned long long lastPingTimestamp = -1; + std::atomic isRunning = false; + uint64_t sequenceId; + uint32_t audioKeySequence; + audioKeyCallback keyCallback; + + void runTask(); +public: + MercuryManager(std::unique_ptr session); + voidCallback reconnectedCallback; + uint16_t audioChunkSequence; + std::shared_ptr timeProvider; + std::string countryCode; + + bool timeoutHandler(); + uint64_t execute(MercuryType method, std::string uri, mercuryCallback &callback, mercuryCallback &subscription, mercuryParts &payload); + uint64_t execute(MercuryType method, std::string uri, mercuryCallback &callback, mercuryCallback &subscription); + uint64_t execute(MercuryType method, std::string uri, mercuryCallback &callback, mercuryParts &payload); + uint64_t execute(MercuryType method, std::string uri, mercuryCallback &callback); + void updateQueue(); + void stop(); + void handleQueue(); + void requestAudioKey(std::vector trackId, std::vector fileId, audioKeyCallback &audioCallback); + std::shared_ptr fetchAudioChunk(std::vector fileId, std::vector &audioKey, uint16_t index); + std::shared_ptr fetchAudioChunk(std::vector fileId, std::vector &audioKey, uint32_t startPos, uint32_t endPos); + void unregisterAudioCallback(uint16_t seqId); + void unregisterMercuryCallback(uint64_t seqId); + void freeAudioKeyCallback(); + void reconnect(); +}; + +#endif diff --git a/components/spotify/cspot/include/MercuryResponse.h b/components/spotify/cspot/include/MercuryResponse.h new file mode 100644 index 00000000..39b8a4a0 --- /dev/null +++ b/components/spotify/cspot/include/MercuryResponse.h @@ -0,0 +1,28 @@ + +#ifndef MERCURYRESPONSE_H +#define MERCURYRESPONSE_H + +#include +#include +#include +#include +#include "ProtoHelper.h" +#include "Utils.h" + +typedef std::vector> mercuryParts; + +class MercuryResponse +{ +private: + void parseResponse(std::vector &data); + std::vector data; +public: + MercuryResponse(std::vector &data); + void decodeHeader(); + Header mercuryHeader; + uint8_t flags; + mercuryParts parts; + uint64_t sequenceId; +}; + +#endif diff --git a/components/spotify/cspot/include/Packet.h b/components/spotify/cspot/include/Packet.h new file mode 100644 index 00000000..7635db87 --- /dev/null +++ b/components/spotify/cspot/include/Packet.h @@ -0,0 +1,17 @@ +#ifndef PACKET_H +#define PACKET_H + +#include +#include + +class Packet +{ +private: + +public: + Packet(uint8_t command, std::vector &data); + uint8_t command; + std::vector data; +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/include/PbCommon.h b/components/spotify/cspot/include/PbCommon.h new file mode 100644 index 00000000..5b3eb747 --- /dev/null +++ b/components/spotify/cspot/include/PbCommon.h @@ -0,0 +1,42 @@ +#ifndef PBCOMMON_H +#define PBCOMMON_H + +#include +#include +#include +#include +class BaseProtobufMessage +{ +private: +public: + bool firstField = true; + uint32_t lastMessagePosition; + void parseFromVector(std::vector const &rawData) + { + auto reader = std::make_shared(rawData); + parseWithReader(reader); + } + void encodeToVector(std::vector &rawData) + { + auto writer = std::make_shared(rawData); + encodeWithWriter(writer); + } + + void parseWithReader(std::shared_ptr reader) + { + firstField = true; + while (reader->next()) + { + if (!decodeField(reader)) + { + reader->skip(); + } else { + firstField = false; + } + } + } + virtual void encodeWithWriter(std::shared_ptr writer) = 0; + virtual bool decodeField(std::shared_ptr reader) = 0; +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/include/PbReader.h b/components/spotify/cspot/include/PbReader.h new file mode 100644 index 00000000..6d7dec17 --- /dev/null +++ b/components/spotify/cspot/include/PbReader.h @@ -0,0 +1,42 @@ +#ifndef PBREADER_H +#define PBREADER_H + +#include +#include +#include +#include + +class PbReader +{ +private: + std::vector const &rawData; + uint32_t currentWireValue = 0; + uint64_t skipVarIntDump = 0; + uint32_t nextFieldLength = 0; + int64_t decodeZigzag(uint64_t value); + +public: + PbReader(std::vector const &rawData); + uint32_t maxPosition = 0; + + PbWireType currentWireType = PbWireType::unknown; + uint32_t currentTag = 0; + uint32_t pos = 0; + + template + T decodeVarInt(); + + template + T decodeFixed(); + + template + T decodeSVarInt(); + void decodeString(std::string &target); + void decodeVector(std::vector &target); + + bool next(); + void skip(); + void resetMaxPosition(); +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/include/PbWireType.h b/components/spotify/cspot/include/PbWireType.h new file mode 100644 index 00000000..60852612 --- /dev/null +++ b/components/spotify/cspot/include/PbWireType.h @@ -0,0 +1,15 @@ +#ifndef PBWIRETYPE_H +#define PBWIRETYPE_H + +#include + +enum class PbWireType : uint32_t +{ + varint = 0, // int32/64, uint32/64, sint32/64, bool, enum + fixed64 = 1, // fixed64, sfixed64, double + length_delimited = 2, // string, bytes, nested messages, packed repeated fields + fixed32 = 5, // fixed32, sfixed32, float + unknown = 99 +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/include/PbWriter.h b/components/spotify/cspot/include/PbWriter.h new file mode 100644 index 00000000..e6465541 --- /dev/null +++ b/components/spotify/cspot/include/PbWriter.h @@ -0,0 +1,39 @@ +#ifndef PBWRITER_H +#define PBWRITER_H + +#include +#include +#include +#include + +class PbWriter +{ +private: + std::vector &rawData; + uint32_t pos; + uint32_t msgStartPos = 0; + void encodeVarInt(uint32_t low, uint32_t high, int32_t atIndex = 0); + uint32_t encodeZigzag32(int32_t value); + uint64_t encodeZigzag64(int64_t value); +public: + PbWriter(std::vector &rawData); + + template + void encodeVarInt(T, int32_t atIndex = 0); + template + void encodeFixed(T); + void addSVarInt32(uint32_t tag, int32_t); + void addSVarInt64(uint32_t tag, int64_t); + void addString(uint32_t tag, std::string &target); + void addVector(uint32_t tag, std::vector &target); + + template + void addVarInt(uint32_t tag, T intType); + void addBool(uint32_t tag, bool value); + + void addField(uint32_t tag, PbWireType wiretype); + uint32_t startMessage(); + void finishMessage(uint32_t tag, uint32_t pos); +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/include/PlainConnection.h b/components/spotify/cspot/include/PlainConnection.h new file mode 100644 index 00000000..370806d4 --- /dev/null +++ b/components/spotify/cspot/include/PlainConnection.h @@ -0,0 +1,31 @@ +#ifndef PLAINCONNECTION_H +#define PLAINCONNECTION_H + +#include "sys/socket.h" +#include +#include +#include +#include +#include +#include +#include "Packet.h" +#include "Utils.h" + +typedef std::function timeoutCallback; + +class PlainConnection +{ +public: + PlainConnection(); + ~PlainConnection(); + int apSock; + void connectToAp(std::string apAddress); + void closeSocket(); + timeoutCallback timeoutHandler; + std::vector sendPrefixPacket(const std::vector &prefix, const std::vector &data); + std::vector recvPacket(); + std::vector readBlock(size_t size); + size_t writeBlock(const std::vector &data); +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/include/Player.h b/components/spotify/cspot/include/Player.h new file mode 100644 index 00000000..3154f93d --- /dev/null +++ b/components/spotify/cspot/include/Player.h @@ -0,0 +1,50 @@ +#ifndef PLAYER_H +#define PLAYER_H + +#include +#include +#include +#include +#include + +#include "Utils.h" +#include "MercuryManager.h" +#include "TrackReference.h" +#include "Session.h" +#include "SpotifyTrack.h" +#include "AudioSink.h" +#include +#include "Queue.h" +#include "Task.h" + +class Player : public bell::Task { +private: + std::shared_ptr manager; + std::shared_ptr currentTrack = nullptr; + std::shared_ptr audioSink; + std::mutex loadTrackMutex; + // @TODO: Use some actual structure here + bell::Queue> trackQueue; + void runTask(); + +public: + Player(std::shared_ptr manager, std::shared_ptr audioSink); + std::function endOfFileCallback; + int volume = 255; + uint32_t logVolume; + std::atomic isRunning = false; + trackChangedCallback trackChanged; + std::mutex runningMutex; + + void setVolume(uint32_t volume); + void handleLoad(std::shared_ptr track, std::function &trackLoadedCallback, uint32_t position_ms, bool isPaused); + void pause(); + void cancelCurrentTrack(); + void seekMs(size_t positionMs); + void feedPCM(std::vector &data); + void play(); + void stop(); + +}; + +#endif diff --git a/components/spotify/cspot/include/PlayerState.h b/components/spotify/cspot/include/PlayerState.h new file mode 100644 index 00000000..f510da3f --- /dev/null +++ b/components/spotify/cspot/include/PlayerState.h @@ -0,0 +1,135 @@ +#ifndef PLAYERSTATE_H +#define PLAYERSTATE_H + +#include +#include +#include +#include "ProtoHelper.h" +#include "Utils.h" +#include "TimeProvider.h" +#include "ConstantParameters.h" +#include "CspotAssert.h" +#include "TrackReference.h" +#include "ConfigJSON.h" + +enum class PlaybackState { + Playing, + Stopped, + Loading, + Paused +}; + +class PlayerState +{ +private: + uint32_t seqNum = 0; + uint8_t capabilityIndex = 0; + std::vector frameData; + std::shared_ptr timeProvider; + std::shared_ptr config; + + void addCapability(CapabilityType typ, int intValue = -1, std::vector stringsValue = std::vector()); +public: + Frame innerFrame = Frame(); + Frame remoteFrame = Frame(); + + /** + * @brief Player state represents the current state of player. + * + * Responsible for keeping track of player's state. Doesn't control the playback itself. + * + * @param timeProvider synced time provider + */ + PlayerState(std::shared_ptr timeProvider); + + /** + * @brief Updates state according to current playback state. + * + * @param state playback state + */ + void setPlaybackState(const PlaybackState state); + + /** + * @brief Sets player activity + * + * @param isActive activity status + */ + void setActive(bool isActive); + + /** + * @brief Simple getter + * + * @return true player is active + * @return false player is inactive + */ + bool isActive(); + + /** + * @brief Updates local track position. + * + * @param position position in milliseconds + */ + void updatePositionMs(uint32_t position); + + /** + * @brief Sets local volume on internal state. + * + * @param volume volume between 0 and UINT16 max + */ + void setVolume(uint32_t volume); + + /** + * @brief Enables queue shuffling. + * + * Sets shuffle parameter on local frame, and in case shuffling is enabled, + * it will randomize the entire local queue. + * + * @param shuffle whenever should shuffle + */ + void setShuffle(bool shuffle); + + /** + * @brief Enables repeat + * + * @param repeat should repeat param + */ + void setRepeat(bool repeat); + + /** + * @brief Updates local track queue from remote data. + */ + void updateTracks(); + + /** + * @brief Changes playback to next queued track. + * + * Will go back to first track if current track is last track in queue. + * In that case, it will pause if repeat is disabled. + */ + bool nextTrack(); + + /** + * @brief Changes playback to previous queued track. + * + * Will stop if current track is the first track in queue and repeat is disabled. + * If repeat is enabled, it will loop back to the last track in queue. + */ + void prevTrack(); + + /** + * @brief Gets the current track reference. + * + * @return std::shared_ptr pointer to track reference + */ + std::shared_ptr getCurrentTrack(); + + /** + * @brief Encodes current frame into binary data via protobuf. + * + * @param typ message type to include in frame type + * @return std::vector binary frame data + */ + std::vector encodeCurrentFrame(MessageType typ); +}; + +#endif diff --git a/components/spotify/cspot/include/ProtoHelper.h b/components/spotify/cspot/include/ProtoHelper.h new file mode 100644 index 00000000..1b53bb4b --- /dev/null +++ b/components/spotify/cspot/include/ProtoHelper.h @@ -0,0 +1,37 @@ +#ifndef PROTOBUF_H +#define PROTOBUF_H +#include +#include +#include "protobuf.h" +#include +#include + +std::optional findFieldWithProtobufTag(AnyRef ref, uint32_t tag); +void decodeField(std::shared_ptr reader, AnyRef any); +void decodeProtobuf(std::shared_ptr reader, AnyRef any); +void encodeProtobuf(std::shared_ptr writer, AnyRef any, uint32_t protobufTag = 0); + +template +std::vector encodePb(T & data) +{ + auto ref = AnyRef::of(&data); + std::vector rawData;; + auto writer = std::make_shared(rawData); + encodeProtobuf(writer, ref); + + return rawData; +} + + +template +T decodePb(std::vector & bytes) +{ + T data = {}; + auto ref = AnyRef::of(&data); + auto writer = std::make_shared(bytes); + decodeProtobuf(writer, ref); + + return data; +} + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/include/Session.h b/components/spotify/cspot/include/Session.h new file mode 100644 index 00000000..f657a264 --- /dev/null +++ b/components/spotify/cspot/include/Session.h @@ -0,0 +1,49 @@ +#ifndef SESSION_H +#define SESSION_H + +#include +#include +#include +#include +#include +#include +#include "Utils.h" +#include "stdlib.h" +#include "ShannonConnection.h" +#include "LoginBlob.h" +#include "ApResolve.h" +#include "PlainConnection.h" +#include "Packet.h" +#include "ConstantParameters.h" +#include "Crypto.h" +#include "ProtoHelper.h" + +#define SPOTIFY_VERSION 0x10800000000 +#define LOGIN_REQUEST_COMMAND 0xAB +#define AUTH_SUCCESSFUL_COMMAND 0xAC +#define AUTH_DECLINED_COMMAND 0xAD + +class Session +{ +private: + ClientResponseEncrypted authRequest; + ClientResponsePlaintext clientResPlaintext; + ClientHello clientHello; + APResponseMessage apResponse; + + std::shared_ptr conn; + std::unique_ptr crypto; + std::vector sendClientHelloRequest(); + void processAPHelloResponse(std::vector &helloPacket); + +public: + Session(); + std::shared_ptr shanConn; + std::shared_ptr authBlob; + void connect(std::unique_ptr connection); + void connectWithRandomAp(); + void close(); + std::vector authenticate(std::shared_ptr blob); +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/include/Shannon.h b/components/spotify/cspot/include/Shannon.h new file mode 100644 index 00000000..df9a5eb2 --- /dev/null +++ b/components/spotify/cspot/include/Shannon.h @@ -0,0 +1,43 @@ +#ifndef SHANNON_H +#define SHANNON_H + +#include +#include +class Shannon +{ +public: + static constexpr unsigned int N = 16; + + void key(const std::vector &key); /* set key */ + void nonce(const std::vector &nonce); /* set Init Vector */ + 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 finish(std::vector &buf); /* finalise MAC */ + +private: + static constexpr unsigned int FOLD = Shannon::N; + static constexpr unsigned int INITKONST = 0x6996c53a; + static constexpr unsigned int KEYP = 13; + uint32_t R[Shannon::N]; + uint32_t CRC[Shannon::N]; + uint32_t initR[Shannon::N]; + uint32_t konst; + uint32_t sbuf; + uint32_t mbuf; + int nbuf; + static uint32_t sbox1(uint32_t w); + static uint32_t sbox2(uint32_t w); + void cycle(); + void crcfunc(uint32_t i); + void macfunc(uint32_t i); + void initState(); + void saveState(); + void reloadState(); + void genkonst(); + void diffuse(); + void loadKey(const std::vector &key); +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/include/ShannonConnection.h b/components/spotify/cspot/include/ShannonConnection.h new file mode 100644 index 00000000..58c85627 --- /dev/null +++ b/components/spotify/cspot/include/ShannonConnection.h @@ -0,0 +1,41 @@ +#ifndef SHANNONCONNECTION_H +#define SHANNONCONNECTION_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform/WrappedMutex.h" +#include "Utils.h" +#include "Shannon.h" +#include "PlainConnection.h" +#include "Packet.h" + +#define MAC_SIZE 4 + + +class ShannonConnection +{ +private: + std::unique_ptr sendCipher; + std::unique_ptr recvCipher; + uint32_t sendNonce = 0; + uint32_t recvNonce = 0; + std::vector cipherPacket(uint8_t cmd, std::vector &data); + WrappedMutex writeMutex; + WrappedMutex readMutex; + +public: + ShannonConnection(); + ~ShannonConnection(); + void wrapConnection(std::shared_ptr conn, std::vector &sendKey, std::vector &recvKey); + void sendPacket(uint8_t cmd, std::vector &data); + std::shared_ptr conn; + std::unique_ptr recvPacket(); +}; + +#endif diff --git a/components/spotify/cspot/include/SpircController.h b/components/spotify/cspot/include/SpircController.h new file mode 100644 index 00000000..16d2e37d --- /dev/null +++ b/components/spotify/cspot/include/SpircController.h @@ -0,0 +1,104 @@ +#ifndef SPIRCCONTROLLER_H +#define SPIRCCONTROLLER_H + +#include +#include +#include +#include "Utils.h" +#include "MercuryManager.h" +#include "ProtoHelper.h" +#include "Session.h" +#include "PlayerState.h" +#include "SpotifyTrack.h" +#include "ConstantParameters.h" +#include "Player.h" +#include "ConfigJSON.h" +#include +#include + +enum class CSpotEventType { + PLAY_PAUSE, + VOLUME, + TRACK_INFO, + DISC, + NEXT, + PREV, + SEEK, +}; + +struct CSpotEvent { + CSpotEventType eventType; + std::variant data; +}; + +typedef std::function cspotEventHandler; + +class SpircController { +private: + std::shared_ptr manager; + std::string username; + std::unique_ptr player; + std::unique_ptr state; + std::shared_ptr audioSink; + std::shared_ptr config; + + cspotEventHandler eventHandler; + void sendCmd(MessageType typ); + void notify(); + void sendEvent(CSpotEventType eventType, std::variant data = 0); + void handleFrame(std::vector &data); + void loadTrack(uint32_t position_ms = 0, bool isPaused = 0); +public: + SpircController(std::shared_ptr manager, std::string username, std::shared_ptr audioSink); + void subscribe(); + + /** + * @brief Pauses / Plays current song + * + * Calling this function will pause or resume playback, setting the + * necessary state value and notifying spotify SPIRC. + * + * @param pause if true pause content, play otherwise + */ + void setPause(bool pause, bool notifyPlayer = true); + + /** + * @brief Toggle Play/Pause + */ + void playToggle(); + + /** + * @brief Notifies spotify servers about volume change + * + * @param volume int between 0 and `MAX_VOLUME` + */ + void setRemoteVolume(int volume); + + /** + * @brief Set device volume and notifies spotify + * + * @param volume int between 0 and `MAX_VOLUME` + */ + void setVolume(int volume); + + /** + * @brief change volume by a given value + * + * @param volume int between 0 and `MAX_VOLUME` + */ + void adjustVolume(int by); + + /** + * @brief Goes back to previous track and notfies spotify SPIRC + */ + void prevSong(); + + /** + * @brief Skips to next track and notfies spotify SPIRC + */ + void nextSong(); + void setEventHandler(cspotEventHandler handler); + void stopPlayer(); +}; + +#endif diff --git a/components/spotify/cspot/include/SpotifyTrack.h b/components/spotify/cspot/include/SpotifyTrack.h new file mode 100644 index 00000000..f8f112f8 --- /dev/null +++ b/components/spotify/cspot/include/SpotifyTrack.h @@ -0,0 +1,54 @@ +#ifndef SPOTIFYTRACK_H +#define SPOTIFYTRACK_H + +#include +#include +#include +#include "MercuryManager.h" +#include "ProtoHelper.h" +#include "Utils.h" +#include "MercuryResponse.h" +#include +#include "Crypto.h" +#include +#include "ChunkedAudioStream.h" +#include "TrackReference.h" +#include + +struct TrackInfo { + std::string name; + std::string album; + std::string artist; + std::string imageUrl; +}; + +typedef std::function trackChangedCallback; + +class SpotifyTrack +{ +private: + std::shared_ptr manager; + void trackInformationCallback(std::unique_ptr response, uint32_t position_ms, bool isPaused); + void episodeInformationCallback(std::unique_ptr response, uint32_t position_ms, bool isPaused); + void requestAudioKey(std::vector fileId, std::vector trackId, int32_t trackDuration, uint32_t position_ms, bool isPaused); + bool countryListContains(std::string countryList, std::string country); + bool canPlayTrack(std::vector &restrictions); + Track trackInfo; + Episode episodeInfo; + + std::vector fileId; + std::vector currentChunkData; + std::vector currentChunkHeader; +public: + SpotifyTrack(std::shared_ptr manager, std::shared_ptr trackRef, uint32_t position_ms, bool isPaused); + ~SpotifyTrack(); + uint64_t reqSeqNum = -1; + std::function loadedTrackCallback; + std::unique_ptr audioStream; + trackChangedCallback trackInfoReceived; + audioKeyCallback audioKeyLambda; + mercuryCallback responseLambda; +}; + + +#endif diff --git a/components/spotify/cspot/include/TimeProvider.h b/components/spotify/cspot/include/TimeProvider.h new file mode 100644 index 00000000..81ce670d --- /dev/null +++ b/components/spotify/cspot/include/TimeProvider.h @@ -0,0 +1,34 @@ +#ifndef TIMEPROVIDER_H +#define TIMEPROVIDER_H + +#include +#include + +class TimeProvider +{ +private: + unsigned long long timestampDiff; + +public: + /** + * @brief Bypasses the need for NTP server sync by syncing with spotify's servers + * + */ + TimeProvider(); + + /** + * @brief Syncs the TimeProvider with spotify server's timestamp + * + * @param pongPacket pong packet containing timestamp + */ + void syncWithPingPacket(const std::vector& pongPacket); + + /** + * @brief Get current timestamp synced with spotify servers + * + * @return unsigned long long timestamp + */ + unsigned long long getSyncedTimestamp(); +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/include/TrackReference.h b/components/spotify/cspot/include/TrackReference.h new file mode 100644 index 00000000..4887f786 --- /dev/null +++ b/components/spotify/cspot/include/TrackReference.h @@ -0,0 +1,34 @@ +#ifndef TRACKREFERENCE_H +#define TRACKREFERENCE_H + +#include +#include "Utils.h" +#include "ProtoHelper.h" +#include +#include + +class TrackReference +{ +private: + std::string alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + std::vector base62Decode(std::string uri); + +public: + TrackReference(TrackRef *ref); + ~TrackReference(); + + TrackRef* ref; + + std::vector gid; + + bool isEpisode = false; + + /** + * @brief Returns an uri that can be allowed to query track information. + * + * @return std::string + */ + std::string getMercuryRequestUri(); +}; + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/include/Utils.h b/components/spotify/cspot/include/Utils.h new file mode 100644 index 00000000..d73c0e66 --- /dev/null +++ b/components/spotify/cspot/include/Utils.h @@ -0,0 +1,98 @@ +#ifndef UTILS_H +#define UTILS_H + +#include +#include +#include "sys/socket.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HMAC_SHA1_BLOCKSIZE 64 + +/** + * @brief Returns current timestamp + * + * @return unsigned long long resulting timestamp in milliseconds from unix time zero + */ +unsigned long long getCurrentTimestamp(); + +/** + * @brief portable 64bit equivalent of htons / htonl. aka endianess swap + * + * @param value input value to swap + * @return uint64_t swapped result + */ +uint64_t hton64(uint64_t value); + +/** + * @brief Performs big number multiplication on two numbers + * + * @param num big num in vector format + * @param n secondary number + * @return std::vector resulting number + */ +std::vector bigNumMultiply(std::vector num, int n); + +/** + * @brief Performs big number addition on two numbers + * + * @param num big num in vector format + * @param n secondary number + * @return std::vector resulting number + */ +std::vector bigNumAdd(std::vector num, int n); + +unsigned char h2int(char c); + + +std::string urlDecode(std::string str); + + +/** + * @brief Converts provided bytes into a human readable hex string + * + * @param bytes vector containing binary data + * @return std::string string containing hex representation of inputted data + */ +std::string bytesToHexString(std::vector &bytes); + +/** + * @brief Extracts given type from binary data + * + * @tparam T type to extract + * @param v vector containing binary data to extract from + * @param pos position offset + * @return T extracted type + */ +template +T extract(const std::vector &v, int pos) +{ + T value; + memcpy(&value, &v[pos], sizeof(T)); + return value; +} + +/** + * @brief Packs given type into binary data + * + * @tparam T type of data to pack + * @param data data to pack + * @return std::vector resulting vector containing binary data + */ +template +std::vector pack(T data) +{ + std::vector rawData( (std::uint8_t*)&data, (std::uint8_t*)&(data) + sizeof(T)); + + return rawData; +} + + +#endif \ No newline at end of file diff --git a/components/spotify/cspot/include/ZeroconfAuthenticator.h b/components/spotify/cspot/include/ZeroconfAuthenticator.h new file mode 100644 index 00000000..1e9e9f10 --- /dev/null +++ b/components/spotify/cspot/include/ZeroconfAuthenticator.h @@ -0,0 +1,49 @@ +#ifndef ZEROCONFAUTHENTICATOR_H +#define ZEROCONFAUTHENTICATOR_H + +#include +#include +#include +#include +#include +#include "Utils.h" +#include "LoginBlob.h" +#include "Crypto.h" +#include "Task.h" +#include "ConstantParameters.h" + + +#ifdef ESP_PLATFORM +#include "mdns.h" +#else +#include "dns_sd.h" +#include +#endif + +#ifndef SOCK_NONBLOCK +#define SOCK_NONBLOCK O_NONBLOCK +#endif + +#define SERVER_PORT_MAX 65535 // Max usable tcp port +#define SERVER_PORT_MIN 1024 // 0-1024 services ports + +typedef std::function)> authCallback; + +class ZeroconfAuthenticator { +private: + int serverPort; + bool authorized = false; + std::unique_ptr crypto; + std::shared_ptr server; + authCallback gotBlobCallback; + void startServer(); + std::string buildJsonInfo(); + void handleAddUser(std::map& queryMap); + void registerZeroconf(); + std::string getParameterFromUrlEncoded(std::string data, std::string param); +public: + ZeroconfAuthenticator(authCallback callback, std::shared_ptr httpServer); + void registerHandlers(); +}; + +#endif diff --git a/components/spotify/cspot/protocol/authentication.proto b/components/spotify/cspot/protocol/authentication.proto new file mode 100644 index 00000000..f69cde34 --- /dev/null +++ b/components/spotify/cspot/protocol/authentication.proto @@ -0,0 +1,66 @@ +enum CpuFamily { + CPU_UNKNOWN = 0x0; + CPU_X86 = 0x1; + CPU_X86_64 = 0x2; + CPU_PPC = 0x3; + CPU_PPC_64 = 0x4; + CPU_ARM = 0x5; + CPU_IA64 = 0x6; + CPU_SH = 0x7; + CPU_MIPS = 0x8; + CPU_BLACKFIN = 0x9; +} + +enum Os { + OS_UNKNOWN = 0x0; + OS_WINDOWS = 0x1; + OS_OSX = 0x2; + OS_IPHONE = 0x3; + OS_S60 = 0x4; + OS_LINUX = 0x5; + OS_WINDOWS_CE = 0x6; + OS_ANDROID = 0x7; + OS_PALM = 0x8; + OS_FREEBSD = 0x9; + OS_BLACKBERRY = 0xa; + OS_SONOS = 0xb; + OS_LOGITECH = 0xc; + OS_WP7 = 0xd; + OS_ONKYO = 0xe; + OS_PHILIPS = 0xf; + OS_WD = 0x10; + OS_VOLVO = 0x11; + OS_TIVO = 0x12; + OS_AWOX = 0x13; + OS_MEEGO = 0x14; + OS_QNXNTO = 0x15; + OS_BCO = 0x16; +} + +enum AuthenticationType { + AUTHENTICATION_USER_PASS = 0x0; + AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS = 0x1; + AUTHENTICATION_STORED_FACEBOOK_CREDENTIALS = 0x2; + AUTHENTICATION_SPOTIFY_TOKEN = 0x3; + AUTHENTICATION_FACEBOOK_TOKEN = 0x4; +} + +message SystemInfo { + required CpuFamily cpu_family = 0xa; + required Os os = 0x3c; + optional string system_information_string = 0x5a; + optional string device_id = 0x64; +} + +message LoginCredentials { + optional string username = 0xa; + required AuthenticationType typ = 0x14; + optional bytes auth_data = 0x1e; +} + +message ClientResponseEncrypted { + required LoginCredentials login_credentials = 0xa; + required SystemInfo system_info = 0x32; + optional string version_string = 0x46; +} + diff --git a/components/spotify/cspot/protocol/keyexchange.proto b/components/spotify/cspot/protocol/keyexchange.proto new file mode 100644 index 00000000..06d1b618 --- /dev/null +++ b/components/spotify/cspot/protocol/keyexchange.proto @@ -0,0 +1,102 @@ +message LoginCryptoDiffieHellmanChallenge { + required bytes gs = 0xa; +} + +message LoginCryptoChallengeUnion { + optional LoginCryptoDiffieHellmanChallenge diffie_hellman = 0xa; +} + +enum Product { + PRODUCT_CLIENT = 0x0; + PRODUCT_LIBSPOTIFY= 0x1; + PRODUCT_MOBILE = 0x2; + PRODUCT_PARTNER = 0x3; + PRODUCT_LIBSPOTIFY_EMBEDDED = 0x5; +} + +message LoginCryptoDiffieHellmanHello { + required bytes gc = 0xa; + required uint32 server_keys_known = 0x14; +} + +message LoginCryptoHelloUnion { + optional LoginCryptoDiffieHellmanHello diffie_hellman = 0xa; +} + + +enum Platform { + PLATFORM_WIN32_X86 = 0x0; + PLATFORM_OSX_X86 = 0x1; + PLATFORM_LINUX_X86 = 0x2; + PLATFORM_IPHONE_ARM = 0x3; + PLATFORM_S60_ARM = 0x4; + PLATFORM_OSX_PPC = 0x5; + PLATFORM_ANDROID_ARM = 0x6; + PLATFORM_WINDOWS_CE_ARM = 0x7; + PLATFORM_LINUX_X86_64 = 0x8; + PLATFORM_OSX_X86_64 = 0x9; + PLATFORM_PALM_ARM = 0xa; + PLATFORM_LINUX_SH = 0xb; + PLATFORM_FREEBSD_X86 = 0xc; + PLATFORM_FREEBSD_X86_64 = 0xd; + PLATFORM_BLACKBERRY_ARM = 0xe; + PLATFORM_SONOS = 0xf; + PLATFORM_LINUX_MIPS = 0x10; + PLATFORM_LINUX_ARM = 0x11; + PLATFORM_LOGITECH_ARM = 0x12; + PLATFORM_LINUX_BLACKFIN = 0x13; + PLATFORM_WP7_ARM = 0x14; + PLATFORM_ONKYO_ARM = 0x15; + PLATFORM_QNXNTO_ARM = 0x16; + PLATFORM_BCO_ARM = 0x17; +} + +enum Cryptosuite { + CRYPTO_SUITE_SHANNON = 0x0; + CRYPTO_SUITE_RC4_SHA1_HMAC = 0x1; +} + +message BuildInfo { + required Product product = 0xa; + required Platform platform = 0x1e; + required uint64 version = 0x28; +} + +message FeatureSet { + optional bool autoupdate2 = 0x1; +} + +message APChallenge { + required LoginCryptoChallengeUnion login_crypto_challenge = 0xa; +} + +message APResponseMessage { + optional APChallenge challenge = 0xa; +} + +message LoginCryptoDiffieHellmanResponse { + required bytes hmac = 0xa; +} + +message LoginCryptoResponseUnion { + optional LoginCryptoDiffieHellmanResponse diffie_hellman = 0xa; +} +message CryptoResponseUnion { +} +message PoWResponseUnion { +} + +message ClientResponsePlaintext { + required LoginCryptoResponseUnion login_crypto_response = 0xa; + required PoWResponseUnion pow_response = 0x14; + required CryptoResponseUnion crypto_response = 0x1e; +} + +message ClientHello { + required BuildInfo build_info = 0xa; + required LoginCryptoHelloUnion login_crypto_hello = 0x32; + repeated Cryptosuite cryptosuites_supported = 0x1e; + required bytes client_nonce = 0x3c; + optional bytes padding = 0x46; + optional FeatureSet feature_set = 0x50; +} diff --git a/components/spotify/cspot/protocol/mercury.proto b/components/spotify/cspot/protocol/mercury.proto new file mode 100644 index 00000000..72138948 --- /dev/null +++ b/components/spotify/cspot/protocol/mercury.proto @@ -0,0 +1,4 @@ +message Header { + optional string uri = 0x01; + optional string method = 0x03; +} diff --git a/components/spotify/cspot/protocol/metadata.proto b/components/spotify/cspot/protocol/metadata.proto new file mode 100644 index 00000000..e30bc6b8 --- /dev/null +++ b/components/spotify/cspot/protocol/metadata.proto @@ -0,0 +1,61 @@ +syntax = "proto2"; + +message AudioFile { + optional bytes file_id = 1; + optional AudioFormat format = 2; +} + +message Restriction { + optional string countries_allowed = 0x2; + optional string countries_forbidden = 0x3; +} + +message Image { + optional bytes file_id = 0x1; +} + +message ImageGroup { + repeated Image image = 0x1; +} + +message Album { + optional bytes gid = 0x1; + optional string name = 0x2; + optional ImageGroup cover_group = 0x11; +} + +message Artist { + optional bytes gid = 0x1; + optional string name = 0x2; +} + +message Track { + optional bytes gid = 1; + optional string name = 2; + optional Album album = 0x3; + repeated Artist artist = 0x4; + optional sint32 duration = 7; + repeated Restriction restriction = 0xb; + repeated AudioFile file = 0xc; + repeated Track alternative = 0xd; +} + +message Episode { + optional bytes gid = 1; + optional string name = 2; + optional sint32 duration = 7; + repeated AudioFile audio = 12; +} + +enum AudioFormat { + OGG_VORBIS_96 = 0; + OGG_VORBIS_160 = 1; + OGG_VORBIS_320 = 2; + MP3_256 = 3; + MP3_320 = 4; + MP3_160 = 5; + MP3_96 = 6; + MP3_160_ENC = 7; + AAC_24 = 8; + AAC_48 = 9; +} diff --git a/components/spotify/cspot/protocol/spirc.proto b/components/spotify/cspot/protocol/spirc.proto new file mode 100644 index 00000000..4339949c --- /dev/null +++ b/components/spotify/cspot/protocol/spirc.proto @@ -0,0 +1,94 @@ +enum MessageType { + kMessageTypeHello = 0x1; + kMessageTypeGoodbye = 0x2; + kMessageTypeProbe = 0x3; + kMessageTypeNotify = 0xa; + kMessageTypeLoad = 0x14; + kMessageTypePlay = 0x15; + kMessageTypePause = 0x16; + kMessageTypePlayPause = 0x17; + kMessageTypeSeek = 0x18; + kMessageTypePrev = 0x19; + kMessageTypeNext = 0x1a; + kMessageTypeVolume = 0x1b; + kMessageTypeShuffle = 0x1c; + kMessageTypeRepeat = 0x1d; + kMessageTypeVolumeDown = 0x1f; + kMessageTypeVolumeUp = 0x20; + kMessageTypeReplace = 0x21; + kMessageTypeLogout = 0x22; + kMessageTypeAction = 0x23; +} + +enum PlayStatus { + kPlayStatusStop = 0x0; + kPlayStatusPlay = 0x1; + kPlayStatusPause = 0x2; + kPlayStatusLoading = 0x3; +} + +message TrackRef { + optional bytes gid = 0x1; + optional string uri = 0x2; + optional bool queued = 0x3; + optional string context = 0x4; +} + +message State { + optional string context_uri = 0x2; + optional uint32 index = 0x3; + optional uint32 position_ms = 0x4; + optional PlayStatus status = 0x5; + optional uint64 position_measured_at = 0x7; + optional string context_description = 0x8; + optional bool shuffle = 0xd; + optional bool repeat = 0xe; + optional uint32 playing_track_index = 0x1a; + repeated TrackRef track = 0x1b; +} + +enum CapabilityType { + kSupportedContexts = 0x1; + kCanBePlayer = 0x2; + kRestrictToLocal = 0x3; + kDeviceType = 0x4; + kGaiaEqConnectId = 0x5; + kSupportsLogout = 0x6; + kIsObservable = 0x7; + kVolumeSteps = 0x8; + kSupportedTypes = 0x9; + kCommandAcks = 0xa; +} + +message Capability { + optional CapabilityType typ = 0x1; + repeated int64 intValue = 0x2; + repeated string stringValue = 0x3; +} + +message DeviceState { + optional string sw_version = 0x1; + optional bool is_active = 0xa; + optional bool can_play = 0xb; + optional uint32 volume = 0xc; + optional string name = 0xd; + optional uint32 error_code = 0xe; + optional int64 became_active_at = 0xf; + optional string error_message = 0x10; + repeated Capability capabilities = 0x11; + repeated string local_uris = 0x12; +} + +message Frame { + optional uint32 version = 0x1; + optional string ident = 0x2; + optional string protocol_version = 0x3; + optional uint32 seq_nr = 0x4; + optional MessageType typ = 0x5; + optional DeviceState device_state = 0x7; + optional State state = 0xc; + optional uint32 position = 0xd; + optional uint32 volume = 0xe; + optional int64 state_update_id = 0x11; + repeated string recipient = 0x12; +} diff --git a/components/spotify/cspot/protos/AnyRefImpl.cpp b/components/spotify/cspot/protos/AnyRefImpl.cpp new file mode 100644 index 00000000..d2ddb255 --- /dev/null +++ b/components/spotify/cspot/protos/AnyRefImpl.cpp @@ -0,0 +1,13 @@ +// THIS CORNFILE IS GENERATED. DO NOT EDIT! 🌽 +#include "protobuf.h" + + ReflectType *AnyRef::reflectType() { + return &reflectTypeInfo[static_cast(this->typeID)]; + } + AnyRef AnyRef::getField(int i) { + auto info = this->reflectType(); + if(info->kind != ReflectTypeKind::Class) { + throw "not a class"; + } + return AnyRef(info->fields[i].typeID, static_cast(this->value.voidptr) + info->fields[i].offset); + } diff --git a/components/spotify/cspot/protos/ReflectTypeInfo.cpp b/components/spotify/cspot/protos/ReflectTypeInfo.cpp new file mode 100644 index 00000000..c9499c14 --- /dev/null +++ b/components/spotify/cspot/protos/ReflectTypeInfo.cpp @@ -0,0 +1,1224 @@ +// THIS CORNFILE IS GENERATED. DO NOT EDIT! 🌽 +#include +#include "protobuf.h" +ReflectType reflectTypeInfo[92] = { +ReflectType::ofEnum(/* mine id */ ReflectTypeID::EnumReflectTypeID, /* name */ "ReflectTypeID", /* enum values */ std::move(std::vector{ ReflectEnumValue("EnumReflectTypeID", 0), + ReflectEnumValue("ClassReflectField", 1), + ReflectEnumValue("ClassReflectEnumValue", 2), + ReflectEnumValue("ClassReflectType", 3), + ReflectEnumValue("EnumReflectTypeKind", 4), + ReflectEnumValue("VectorOfClassReflectField", 5), + ReflectEnumValue("VectorOfClassReflectEnumValue", 6), + ReflectEnumValue("Double", 7), + ReflectEnumValue("Uint32", 8), + ReflectEnumValue("Char", 9), + ReflectEnumValue("UnsignedChar", 10), + ReflectEnumValue("Float", 11), + ReflectEnumValue("Bool", 12), + ReflectEnumValue("String", 13), + ReflectEnumValue("Int32", 14), + ReflectEnumValue("Int64", 15), + ReflectEnumValue("Int", 16), + ReflectEnumValue("Uint8", 17), + ReflectEnumValue("Uint64", 18), + ReflectEnumValue("VectorOfUint8", 19), + ReflectEnumValue("EnumCpuFamily", 20), + ReflectEnumValue("EnumOs", 21), + ReflectEnumValue("EnumAuthenticationType", 22), + ReflectEnumValue("ClassSystemInfo", 23), + ReflectEnumValue("ClassLoginCredentials", 24), + ReflectEnumValue("ClassClientResponseEncrypted", 25), + ReflectEnumValue("OptionalOfString", 26), + ReflectEnumValue("OptionalOfVectorOfUint8", 27), + ReflectEnumValue("EnumProduct", 28), + ReflectEnumValue("EnumPlatform", 29), + ReflectEnumValue("EnumCryptosuite", 30), + ReflectEnumValue("ClassLoginCryptoDiffieHellmanChallenge", 31), + ReflectEnumValue("ClassLoginCryptoChallengeUnion", 32), + ReflectEnumValue("ClassLoginCryptoDiffieHellmanHello", 33), + ReflectEnumValue("ClassLoginCryptoHelloUnion", 34), + ReflectEnumValue("ClassBuildInfo", 35), + ReflectEnumValue("ClassFeatureSet", 36), + ReflectEnumValue("ClassAPChallenge", 37), + ReflectEnumValue("ClassAPResponseMessage", 38), + ReflectEnumValue("ClassLoginCryptoDiffieHellmanResponse", 39), + ReflectEnumValue("ClassLoginCryptoResponseUnion", 40), + ReflectEnumValue("ClassCryptoResponseUnion", 41), + ReflectEnumValue("ClassPoWResponseUnion", 42), + ReflectEnumValue("ClassClientResponsePlaintext", 43), + ReflectEnumValue("ClassClientHello", 44), + ReflectEnumValue("OptionalOfClassLoginCryptoDiffieHellmanChallenge", 45), + ReflectEnumValue("OptionalOfClassLoginCryptoDiffieHellmanHello", 46), + ReflectEnumValue("OptionalOfBool", 47), + ReflectEnumValue("OptionalOfClassAPChallenge", 48), + ReflectEnumValue("OptionalOfClassLoginCryptoDiffieHellmanResponse", 49), + ReflectEnumValue("VectorOfEnumCryptosuite", 50), + ReflectEnumValue("OptionalOfClassFeatureSet", 51), + ReflectEnumValue("ClassHeader", 52), + ReflectEnumValue("EnumAudioFormat", 53), + ReflectEnumValue("ClassAudioFile", 54), + ReflectEnumValue("ClassRestriction", 55), + ReflectEnumValue("ClassImage", 56), + ReflectEnumValue("ClassImageGroup", 57), + ReflectEnumValue("ClassAlbum", 58), + ReflectEnumValue("ClassArtist", 59), + ReflectEnumValue("ClassTrack", 60), + ReflectEnumValue("ClassEpisode", 61), + ReflectEnumValue("OptionalOfEnumAudioFormat", 62), + ReflectEnumValue("VectorOfClassImage", 63), + ReflectEnumValue("OptionalOfClassImageGroup", 64), + ReflectEnumValue("OptionalOfClassAlbum", 65), + ReflectEnumValue("VectorOfClassArtist", 66), + ReflectEnumValue("OptionalOfInt32", 67), + ReflectEnumValue("VectorOfClassRestriction", 68), + ReflectEnumValue("VectorOfClassAudioFile", 69), + ReflectEnumValue("VectorOfClassTrack", 70), + ReflectEnumValue("EnumMessageType", 71), + ReflectEnumValue("EnumPlayStatus", 72), + ReflectEnumValue("EnumCapabilityType", 73), + ReflectEnumValue("ClassTrackRef", 74), + ReflectEnumValue("ClassState", 75), + ReflectEnumValue("ClassCapability", 76), + ReflectEnumValue("ClassDeviceState", 77), + ReflectEnumValue("ClassFrame", 78), + ReflectEnumValue("OptionalOfUint32", 79), + ReflectEnumValue("OptionalOfEnumPlayStatus", 80), + ReflectEnumValue("OptionalOfUint64", 81), + ReflectEnumValue("VectorOfClassTrackRef", 82), + ReflectEnumValue("OptionalOfEnumCapabilityType", 83), + ReflectEnumValue("VectorOfInt64", 84), + ReflectEnumValue("VectorOfString", 85), + ReflectEnumValue("OptionalOfInt64", 86), + ReflectEnumValue("VectorOfClassCapability", 87), + ReflectEnumValue("OptionalOfEnumMessageType", 88), + ReflectEnumValue("OptionalOfClassDeviceState", 89), + ReflectEnumValue("OptionalOfClassState", 90), +}), /* size */ sizeof(ReflectTypeID)), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassReflectField, + /* name */ "ReflectField", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::EnumReflectTypeID, /* name */ "typeID", /* offset */ offsetof(ReflectField, typeID), /* protobuf tag */ 0), +ReflectField( /* typeID */ ReflectTypeID::String, /* name */ "name", /* offset */ offsetof(ReflectField, name), /* protobuf tag */ 0), +ReflectField( /* typeID */ ReflectTypeID::Int64, /* name */ "offset", /* offset */ offsetof(ReflectField, offset), /* protobuf tag */ 0), +ReflectField( /* typeID */ ReflectTypeID::Uint32, /* name */ "protobufTag", /* offset */ offsetof(ReflectField, protobufTag), /* protobuf tag */ 0), +}), + /* size */ sizeof(ReflectField), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassReflectEnumValue, + /* name */ "ReflectEnumValue", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::String, /* name */ "name", /* offset */ offsetof(ReflectEnumValue, name), /* protobuf tag */ 0), +ReflectField( /* typeID */ ReflectTypeID::Int, /* name */ "value", /* offset */ offsetof(ReflectEnumValue, value), /* protobuf tag */ 0), +}), + /* size */ sizeof(ReflectEnumValue), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassReflectType, + /* name */ "ReflectType", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::EnumReflectTypeID, /* name */ "typeID", /* offset */ offsetof(ReflectType, typeID), /* protobuf tag */ 0), +ReflectField( /* typeID */ ReflectTypeID::String, /* name */ "name", /* offset */ offsetof(ReflectType, name), /* protobuf tag */ 0), +ReflectField( /* typeID */ ReflectTypeID::EnumReflectTypeKind, /* name */ "kind", /* offset */ offsetof(ReflectType, kind), /* protobuf tag */ 0), +ReflectField( /* typeID */ ReflectTypeID::Int64, /* name */ "size", /* offset */ offsetof(ReflectType, size), /* protobuf tag */ 0), +ReflectField( /* typeID */ ReflectTypeID::EnumReflectTypeID, /* name */ "innerType", /* offset */ offsetof(ReflectType, innerType), /* protobuf tag */ 0), +ReflectField( /* typeID */ ReflectTypeID::VectorOfClassReflectField, /* name */ "fields", /* offset */ offsetof(ReflectType, fields), /* protobuf tag */ 0), +ReflectField( /* typeID */ ReflectTypeID::VectorOfClassReflectEnumValue, /* name */ "enumValues", /* offset */ offsetof(ReflectType, enumValues), /* protobuf tag */ 0), +}), + /* size */ sizeof(ReflectType), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofEnum(/* mine id */ ReflectTypeID::EnumReflectTypeKind, /* name */ "ReflectTypeKind", /* enum values */ std::move(std::vector{ ReflectEnumValue("Primitive", 0), + ReflectEnumValue("Enum", 1), + ReflectEnumValue("Class", 2), + ReflectEnumValue("Vector", 3), + ReflectEnumValue("Optional", 4), +}), /* size */ sizeof(ReflectTypeKind)), + + ReflectType::ofVector( + /* mine typeId */ ReflectTypeID::VectorOfClassReflectField, + /* inner type id */ ReflectTypeID::ClassReflectField, + /* size */ sizeof(std::vector), + VectorOperations{ + .push_back = __VectorManipulator::push_back, + .at = __VectorManipulator::at, + .size = __VectorManipulator::size, + .emplace_back = __VectorManipulator::emplace_back, + .clear = __VectorManipulator::clear, + .reserve = __VectorManipulator::reserve, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofVector( + /* mine typeId */ ReflectTypeID::VectorOfClassReflectEnumValue, + /* inner type id */ ReflectTypeID::ClassReflectEnumValue, + /* size */ sizeof(std::vector), + VectorOperations{ + .push_back = __VectorManipulator::push_back, + .at = __VectorManipulator::at, + .size = __VectorManipulator::size, + .emplace_back = __VectorManipulator::emplace_back, + .clear = __VectorManipulator::clear, + .reserve = __VectorManipulator::reserve, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , +ReflectType::ofPrimitive(/* type id */ ReflectTypeID::Double, /* name */ "double", /* size */ sizeof(double)), +ReflectType::ofPrimitive(/* type id */ ReflectTypeID::Uint32, /* name */ "uint32_t", /* size */ sizeof(uint32_t)), +ReflectType::ofPrimitive(/* type id */ ReflectTypeID::Char, /* name */ "char", /* size */ sizeof(char)), +ReflectType::ofPrimitive(/* type id */ ReflectTypeID::UnsignedChar, /* name */ "unsigned char", /* size */ sizeof(unsigned char)), +ReflectType::ofPrimitive(/* type id */ ReflectTypeID::Float, /* name */ "float", /* size */ sizeof(float)), +ReflectType::ofPrimitive(/* type id */ ReflectTypeID::Bool, /* name */ "bool", /* size */ sizeof(bool)), +ReflectType::ofPrimitive(/* type id */ ReflectTypeID::String, /* name */ "std::string", /* size */ sizeof(std::string)), +ReflectType::ofPrimitive(/* type id */ ReflectTypeID::Int32, /* name */ "int32_t", /* size */ sizeof(int32_t)), +ReflectType::ofPrimitive(/* type id */ ReflectTypeID::Int64, /* name */ "int64_t", /* size */ sizeof(int64_t)), +ReflectType::ofPrimitive(/* type id */ ReflectTypeID::Int, /* name */ "int", /* size */ sizeof(int)), +ReflectType::ofPrimitive(/* type id */ ReflectTypeID::Uint8, /* name */ "uint8_t", /* size */ sizeof(uint8_t)), +ReflectType::ofPrimitive(/* type id */ ReflectTypeID::Uint64, /* name */ "uint64_t", /* size */ sizeof(uint64_t)), + + ReflectType::ofVector( + /* mine typeId */ ReflectTypeID::VectorOfUint8, + /* inner type id */ ReflectTypeID::Uint8, + /* size */ sizeof(std::vector), + VectorOperations{ + .push_back = __VectorManipulator::push_back, + .at = __VectorManipulator::at, + .size = __VectorManipulator::size, + .emplace_back = __VectorManipulator::emplace_back, + .clear = __VectorManipulator::clear, + .reserve = __VectorManipulator::reserve, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , +ReflectType::ofEnum(/* mine id */ ReflectTypeID::EnumCpuFamily, /* name */ "CpuFamily", /* enum values */ std::move(std::vector{ ReflectEnumValue("CPU_UNKNOWN", 0), + ReflectEnumValue("CPU_X86", 1), + ReflectEnumValue("CPU_X86_64", 2), + ReflectEnumValue("CPU_PPC", 3), + ReflectEnumValue("CPU_PPC_64", 4), + ReflectEnumValue("CPU_ARM", 5), + ReflectEnumValue("CPU_IA64", 6), + ReflectEnumValue("CPU_SH", 7), + ReflectEnumValue("CPU_MIPS", 8), + ReflectEnumValue("CPU_BLACKFIN", 9), +}), /* size */ sizeof(CpuFamily)), +ReflectType::ofEnum(/* mine id */ ReflectTypeID::EnumOs, /* name */ "Os", /* enum values */ std::move(std::vector{ ReflectEnumValue("OS_UNKNOWN", 0), + ReflectEnumValue("OS_WINDOWS", 1), + ReflectEnumValue("OS_OSX", 2), + ReflectEnumValue("OS_IPHONE", 3), + ReflectEnumValue("OS_S60", 4), + ReflectEnumValue("OS_LINUX", 5), + ReflectEnumValue("OS_WINDOWS_CE", 6), + ReflectEnumValue("OS_ANDROID", 7), + ReflectEnumValue("OS_PALM", 8), + ReflectEnumValue("OS_FREEBSD", 9), + ReflectEnumValue("OS_BLACKBERRY", 10), + ReflectEnumValue("OS_SONOS", 11), + ReflectEnumValue("OS_LOGITECH", 12), + ReflectEnumValue("OS_WP7", 13), + ReflectEnumValue("OS_ONKYO", 14), + ReflectEnumValue("OS_PHILIPS", 15), + ReflectEnumValue("OS_WD", 16), + ReflectEnumValue("OS_VOLVO", 17), + ReflectEnumValue("OS_TIVO", 18), + ReflectEnumValue("OS_AWOX", 19), + ReflectEnumValue("OS_MEEGO", 20), + ReflectEnumValue("OS_QNXNTO", 21), + ReflectEnumValue("OS_BCO", 22), +}), /* size */ sizeof(Os)), +ReflectType::ofEnum(/* mine id */ ReflectTypeID::EnumAuthenticationType, /* name */ "AuthenticationType", /* enum values */ std::move(std::vector{ ReflectEnumValue("AUTHENTICATION_USER_PASS", 0), + ReflectEnumValue("AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS", 1), + ReflectEnumValue("AUTHENTICATION_STORED_FACEBOOK_CREDENTIALS", 2), + ReflectEnumValue("AUTHENTICATION_SPOTIFY_TOKEN", 3), + ReflectEnumValue("AUTHENTICATION_FACEBOOK_TOKEN", 4), +}), /* size */ sizeof(AuthenticationType)), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassSystemInfo, + /* name */ "SystemInfo", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::EnumCpuFamily, /* name */ "cpu_family", /* offset */ offsetof(SystemInfo, cpu_family), /* protobuf tag */ 10), +ReflectField( /* typeID */ ReflectTypeID::EnumOs, /* name */ "os", /* offset */ offsetof(SystemInfo, os), /* protobuf tag */ 60), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "system_information_string", /* offset */ offsetof(SystemInfo, system_information_string), /* protobuf tag */ 90), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "device_id", /* offset */ offsetof(SystemInfo, device_id), /* protobuf tag */ 100), +}), + /* size */ sizeof(SystemInfo), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassLoginCredentials, + /* name */ "LoginCredentials", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "username", /* offset */ offsetof(LoginCredentials, username), /* protobuf tag */ 10), +ReflectField( /* typeID */ ReflectTypeID::EnumAuthenticationType, /* name */ "typ", /* offset */ offsetof(LoginCredentials, typ), /* protobuf tag */ 20), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfVectorOfUint8, /* name */ "auth_data", /* offset */ offsetof(LoginCredentials, auth_data), /* protobuf tag */ 30), +}), + /* size */ sizeof(LoginCredentials), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassClientResponseEncrypted, + /* name */ "ClientResponseEncrypted", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::ClassLoginCredentials, /* name */ "login_credentials", /* offset */ offsetof(ClientResponseEncrypted, login_credentials), /* protobuf tag */ 10), +ReflectField( /* typeID */ ReflectTypeID::ClassSystemInfo, /* name */ "system_info", /* offset */ offsetof(ClientResponseEncrypted, system_info), /* protobuf tag */ 50), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "version_string", /* offset */ offsetof(ClientResponseEncrypted, version_string), /* protobuf tag */ 70), +}), + /* size */ sizeof(ClientResponseEncrypted), + __reflectConstruct, + __reflectDestruct), + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfString, + /* inner type id */ ReflectTypeID::String, + /* size */ sizeof(std::optional), + /* option */ OptionalOperations{ + .get = __OptionalManipulator::get, + .has_value = __OptionalManipulator::has_value, + .set = __OptionalManipulator::set, + .reset = __OptionalManipulator::reset, + .emplaceEmpty = __OptionalManipulator::emplaceEmpty, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfVectorOfUint8, + /* inner type id */ ReflectTypeID::VectorOfUint8, + /* size */ sizeof(std::optional>), + /* option */ OptionalOperations{ + .get = __OptionalManipulator>::get, + .has_value = __OptionalManipulator>::has_value, + .set = __OptionalManipulator>::set, + .reset = __OptionalManipulator>::reset, + .emplaceEmpty = __OptionalManipulator>::emplaceEmpty, + }, + __reflectConstruct>>, + __reflectDestruct>> + ) + + + + , +ReflectType::ofEnum(/* mine id */ ReflectTypeID::EnumProduct, /* name */ "Product", /* enum values */ std::move(std::vector{ ReflectEnumValue("PRODUCT_CLIENT", 0), + ReflectEnumValue("PRODUCT_LIBSPOTIFY", 1), + ReflectEnumValue("PRODUCT_MOBILE", 2), + ReflectEnumValue("PRODUCT_PARTNER", 3), + ReflectEnumValue("PRODUCT_LIBSPOTIFY_EMBEDDED", 5), +}), /* size */ sizeof(Product)), +ReflectType::ofEnum(/* mine id */ ReflectTypeID::EnumPlatform, /* name */ "Platform", /* enum values */ std::move(std::vector{ ReflectEnumValue("PLATFORM_WIN32_X86", 0), + ReflectEnumValue("PLATFORM_OSX_X86", 1), + ReflectEnumValue("PLATFORM_LINUX_X86", 2), + ReflectEnumValue("PLATFORM_IPHONE_ARM", 3), + ReflectEnumValue("PLATFORM_S60_ARM", 4), + ReflectEnumValue("PLATFORM_OSX_PPC", 5), + ReflectEnumValue("PLATFORM_ANDROID_ARM", 6), + ReflectEnumValue("PLATFORM_WINDOWS_CE_ARM", 7), + ReflectEnumValue("PLATFORM_LINUX_X86_64", 8), + ReflectEnumValue("PLATFORM_OSX_X86_64", 9), + ReflectEnumValue("PLATFORM_PALM_ARM", 10), + ReflectEnumValue("PLATFORM_LINUX_SH", 11), + ReflectEnumValue("PLATFORM_FREEBSD_X86", 12), + ReflectEnumValue("PLATFORM_FREEBSD_X86_64", 13), + ReflectEnumValue("PLATFORM_BLACKBERRY_ARM", 14), + ReflectEnumValue("PLATFORM_SONOS", 15), + ReflectEnumValue("PLATFORM_LINUX_MIPS", 16), + ReflectEnumValue("PLATFORM_LINUX_ARM", 17), + ReflectEnumValue("PLATFORM_LOGITECH_ARM", 18), + ReflectEnumValue("PLATFORM_LINUX_BLACKFIN", 19), + ReflectEnumValue("PLATFORM_WP7_ARM", 20), + ReflectEnumValue("PLATFORM_ONKYO_ARM", 21), + ReflectEnumValue("PLATFORM_QNXNTO_ARM", 22), + ReflectEnumValue("PLATFORM_BCO_ARM", 23), +}), /* size */ sizeof(Platform)), +ReflectType::ofEnum(/* mine id */ ReflectTypeID::EnumCryptosuite, /* name */ "Cryptosuite", /* enum values */ std::move(std::vector{ ReflectEnumValue("CRYPTO_SUITE_SHANNON", 0), + ReflectEnumValue("CRYPTO_SUITE_RC4_SHA1_HMAC", 1), +}), /* size */ sizeof(Cryptosuite)), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassLoginCryptoDiffieHellmanChallenge, + /* name */ "LoginCryptoDiffieHellmanChallenge", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::VectorOfUint8, /* name */ "gs", /* offset */ offsetof(LoginCryptoDiffieHellmanChallenge, gs), /* protobuf tag */ 10), +}), + /* size */ sizeof(LoginCryptoDiffieHellmanChallenge), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassLoginCryptoChallengeUnion, + /* name */ "LoginCryptoChallengeUnion", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::OptionalOfClassLoginCryptoDiffieHellmanChallenge, /* name */ "diffie_hellman", /* offset */ offsetof(LoginCryptoChallengeUnion, diffie_hellman), /* protobuf tag */ 10), +}), + /* size */ sizeof(LoginCryptoChallengeUnion), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassLoginCryptoDiffieHellmanHello, + /* name */ "LoginCryptoDiffieHellmanHello", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::VectorOfUint8, /* name */ "gc", /* offset */ offsetof(LoginCryptoDiffieHellmanHello, gc), /* protobuf tag */ 10), +ReflectField( /* typeID */ ReflectTypeID::Uint32, /* name */ "server_keys_known", /* offset */ offsetof(LoginCryptoDiffieHellmanHello, server_keys_known), /* protobuf tag */ 20), +}), + /* size */ sizeof(LoginCryptoDiffieHellmanHello), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassLoginCryptoHelloUnion, + /* name */ "LoginCryptoHelloUnion", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::OptionalOfClassLoginCryptoDiffieHellmanHello, /* name */ "diffie_hellman", /* offset */ offsetof(LoginCryptoHelloUnion, diffie_hellman), /* protobuf tag */ 10), +}), + /* size */ sizeof(LoginCryptoHelloUnion), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassBuildInfo, + /* name */ "BuildInfo", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::EnumProduct, /* name */ "product", /* offset */ offsetof(BuildInfo, product), /* protobuf tag */ 10), +ReflectField( /* typeID */ ReflectTypeID::EnumPlatform, /* name */ "platform", /* offset */ offsetof(BuildInfo, platform), /* protobuf tag */ 30), +ReflectField( /* typeID */ ReflectTypeID::Uint64, /* name */ "version", /* offset */ offsetof(BuildInfo, version), /* protobuf tag */ 40), +}), + /* size */ sizeof(BuildInfo), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassFeatureSet, + /* name */ "FeatureSet", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::OptionalOfBool, /* name */ "autoupdate2", /* offset */ offsetof(FeatureSet, autoupdate2), /* protobuf tag */ 1), +}), + /* size */ sizeof(FeatureSet), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassAPChallenge, + /* name */ "APChallenge", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::ClassLoginCryptoChallengeUnion, /* name */ "login_crypto_challenge", /* offset */ offsetof(APChallenge, login_crypto_challenge), /* protobuf tag */ 10), +}), + /* size */ sizeof(APChallenge), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassAPResponseMessage, + /* name */ "APResponseMessage", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::OptionalOfClassAPChallenge, /* name */ "challenge", /* offset */ offsetof(APResponseMessage, challenge), /* protobuf tag */ 10), +}), + /* size */ sizeof(APResponseMessage), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassLoginCryptoDiffieHellmanResponse, + /* name */ "LoginCryptoDiffieHellmanResponse", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::VectorOfUint8, /* name */ "hmac", /* offset */ offsetof(LoginCryptoDiffieHellmanResponse, hmac), /* protobuf tag */ 10), +}), + /* size */ sizeof(LoginCryptoDiffieHellmanResponse), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassLoginCryptoResponseUnion, + /* name */ "LoginCryptoResponseUnion", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::OptionalOfClassLoginCryptoDiffieHellmanResponse, /* name */ "diffie_hellman", /* offset */ offsetof(LoginCryptoResponseUnion, diffie_hellman), /* protobuf tag */ 10), +}), + /* size */ sizeof(LoginCryptoResponseUnion), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassCryptoResponseUnion, + /* name */ "CryptoResponseUnion", + /* fields */ std::move(std::vector{}), + /* size */ sizeof(CryptoResponseUnion), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassPoWResponseUnion, + /* name */ "PoWResponseUnion", + /* fields */ std::move(std::vector{}), + /* size */ sizeof(PoWResponseUnion), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassClientResponsePlaintext, + /* name */ "ClientResponsePlaintext", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::ClassLoginCryptoResponseUnion, /* name */ "login_crypto_response", /* offset */ offsetof(ClientResponsePlaintext, login_crypto_response), /* protobuf tag */ 10), +ReflectField( /* typeID */ ReflectTypeID::ClassPoWResponseUnion, /* name */ "pow_response", /* offset */ offsetof(ClientResponsePlaintext, pow_response), /* protobuf tag */ 20), +ReflectField( /* typeID */ ReflectTypeID::ClassCryptoResponseUnion, /* name */ "crypto_response", /* offset */ offsetof(ClientResponsePlaintext, crypto_response), /* protobuf tag */ 30), +}), + /* size */ sizeof(ClientResponsePlaintext), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassClientHello, + /* name */ "ClientHello", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::ClassBuildInfo, /* name */ "build_info", /* offset */ offsetof(ClientHello, build_info), /* protobuf tag */ 10), +ReflectField( /* typeID */ ReflectTypeID::ClassLoginCryptoHelloUnion, /* name */ "login_crypto_hello", /* offset */ offsetof(ClientHello, login_crypto_hello), /* protobuf tag */ 50), +ReflectField( /* typeID */ ReflectTypeID::VectorOfEnumCryptosuite, /* name */ "cryptosuites_supported", /* offset */ offsetof(ClientHello, cryptosuites_supported), /* protobuf tag */ 30), +ReflectField( /* typeID */ ReflectTypeID::VectorOfUint8, /* name */ "client_nonce", /* offset */ offsetof(ClientHello, client_nonce), /* protobuf tag */ 60), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfVectorOfUint8, /* name */ "padding", /* offset */ offsetof(ClientHello, padding), /* protobuf tag */ 70), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfClassFeatureSet, /* name */ "feature_set", /* offset */ offsetof(ClientHello, feature_set), /* protobuf tag */ 80), +}), + /* size */ sizeof(ClientHello), + __reflectConstruct, + __reflectDestruct), + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfClassLoginCryptoDiffieHellmanChallenge, + /* inner type id */ ReflectTypeID::ClassLoginCryptoDiffieHellmanChallenge, + /* size */ sizeof(std::optional), + /* option */ OptionalOperations{ + .get = __OptionalManipulator::get, + .has_value = __OptionalManipulator::has_value, + .set = __OptionalManipulator::set, + .reset = __OptionalManipulator::reset, + .emplaceEmpty = __OptionalManipulator::emplaceEmpty, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfClassLoginCryptoDiffieHellmanHello, + /* inner type id */ ReflectTypeID::ClassLoginCryptoDiffieHellmanHello, + /* size */ sizeof(std::optional), + /* option */ OptionalOperations{ + .get = __OptionalManipulator::get, + .has_value = __OptionalManipulator::has_value, + .set = __OptionalManipulator::set, + .reset = __OptionalManipulator::reset, + .emplaceEmpty = __OptionalManipulator::emplaceEmpty, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfBool, + /* inner type id */ ReflectTypeID::Bool, + /* size */ sizeof(std::optional), + /* option */ OptionalOperations{ + .get = __OptionalManipulator::get, + .has_value = __OptionalManipulator::has_value, + .set = __OptionalManipulator::set, + .reset = __OptionalManipulator::reset, + .emplaceEmpty = __OptionalManipulator::emplaceEmpty, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfClassAPChallenge, + /* inner type id */ ReflectTypeID::ClassAPChallenge, + /* size */ sizeof(std::optional), + /* option */ OptionalOperations{ + .get = __OptionalManipulator::get, + .has_value = __OptionalManipulator::has_value, + .set = __OptionalManipulator::set, + .reset = __OptionalManipulator::reset, + .emplaceEmpty = __OptionalManipulator::emplaceEmpty, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfClassLoginCryptoDiffieHellmanResponse, + /* inner type id */ ReflectTypeID::ClassLoginCryptoDiffieHellmanResponse, + /* size */ sizeof(std::optional), + /* option */ OptionalOperations{ + .get = __OptionalManipulator::get, + .has_value = __OptionalManipulator::has_value, + .set = __OptionalManipulator::set, + .reset = __OptionalManipulator::reset, + .emplaceEmpty = __OptionalManipulator::emplaceEmpty, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofVector( + /* mine typeId */ ReflectTypeID::VectorOfEnumCryptosuite, + /* inner type id */ ReflectTypeID::EnumCryptosuite, + /* size */ sizeof(std::vector), + VectorOperations{ + .push_back = __VectorManipulator::push_back, + .at = __VectorManipulator::at, + .size = __VectorManipulator::size, + .emplace_back = __VectorManipulator::emplace_back, + .clear = __VectorManipulator::clear, + .reserve = __VectorManipulator::reserve, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfClassFeatureSet, + /* inner type id */ ReflectTypeID::ClassFeatureSet, + /* size */ sizeof(std::optional), + /* option */ OptionalOperations{ + .get = __OptionalManipulator::get, + .has_value = __OptionalManipulator::has_value, + .set = __OptionalManipulator::set, + .reset = __OptionalManipulator::reset, + .emplaceEmpty = __OptionalManipulator::emplaceEmpty, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassHeader, + /* name */ "Header", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "uri", /* offset */ offsetof(Header, uri), /* protobuf tag */ 1), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "method", /* offset */ offsetof(Header, method), /* protobuf tag */ 3), +}), + /* size */ sizeof(Header), + __reflectConstruct
    , + __reflectDestruct
    ), +ReflectType::ofEnum(/* mine id */ ReflectTypeID::EnumAudioFormat, /* name */ "AudioFormat", /* enum values */ std::move(std::vector{ ReflectEnumValue("OGG_VORBIS_96", 0), + ReflectEnumValue("OGG_VORBIS_160", 1), + ReflectEnumValue("OGG_VORBIS_320", 2), + ReflectEnumValue("MP3_256", 3), + ReflectEnumValue("MP3_320", 4), + ReflectEnumValue("MP3_160", 5), + ReflectEnumValue("MP3_96", 6), + ReflectEnumValue("MP3_160_ENC", 7), + ReflectEnumValue("AAC_24", 8), + ReflectEnumValue("AAC_48", 9), +}), /* size */ sizeof(AudioFormat)), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassAudioFile, + /* name */ "AudioFile", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::OptionalOfVectorOfUint8, /* name */ "file_id", /* offset */ offsetof(AudioFile, file_id), /* protobuf tag */ 1), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfEnumAudioFormat, /* name */ "format", /* offset */ offsetof(AudioFile, format), /* protobuf tag */ 2), +}), + /* size */ sizeof(AudioFile), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassRestriction, + /* name */ "Restriction", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "countries_allowed", /* offset */ offsetof(Restriction, countries_allowed), /* protobuf tag */ 2), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "countries_forbidden", /* offset */ offsetof(Restriction, countries_forbidden), /* protobuf tag */ 3), +}), + /* size */ sizeof(Restriction), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassImage, + /* name */ "Image", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::OptionalOfVectorOfUint8, /* name */ "file_id", /* offset */ offsetof(Image, file_id), /* protobuf tag */ 1), +}), + /* size */ sizeof(Image), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassImageGroup, + /* name */ "ImageGroup", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::VectorOfClassImage, /* name */ "image", /* offset */ offsetof(ImageGroup, image), /* protobuf tag */ 1), +}), + /* size */ sizeof(ImageGroup), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassAlbum, + /* name */ "Album", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::OptionalOfVectorOfUint8, /* name */ "gid", /* offset */ offsetof(Album, gid), /* protobuf tag */ 1), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "name", /* offset */ offsetof(Album, name), /* protobuf tag */ 2), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfClassImageGroup, /* name */ "cover_group", /* offset */ offsetof(Album, cover_group), /* protobuf tag */ 17), +}), + /* size */ sizeof(Album), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassArtist, + /* name */ "Artist", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::OptionalOfVectorOfUint8, /* name */ "gid", /* offset */ offsetof(Artist, gid), /* protobuf tag */ 1), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "name", /* offset */ offsetof(Artist, name), /* protobuf tag */ 2), +}), + /* size */ sizeof(Artist), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassTrack, + /* name */ "Track", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::OptionalOfVectorOfUint8, /* name */ "gid", /* offset */ offsetof(Track, gid), /* protobuf tag */ 1), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "name", /* offset */ offsetof(Track, name), /* protobuf tag */ 2), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfClassAlbum, /* name */ "album", /* offset */ offsetof(Track, album), /* protobuf tag */ 3), +ReflectField( /* typeID */ ReflectTypeID::VectorOfClassArtist, /* name */ "artist", /* offset */ offsetof(Track, artist), /* protobuf tag */ 4), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfInt32, /* name */ "duration", /* offset */ offsetof(Track, duration), /* protobuf tag */ 7), +ReflectField( /* typeID */ ReflectTypeID::VectorOfClassRestriction, /* name */ "restriction", /* offset */ offsetof(Track, restriction), /* protobuf tag */ 11), +ReflectField( /* typeID */ ReflectTypeID::VectorOfClassAudioFile, /* name */ "file", /* offset */ offsetof(Track, file), /* protobuf tag */ 12), +ReflectField( /* typeID */ ReflectTypeID::VectorOfClassTrack, /* name */ "alternative", /* offset */ offsetof(Track, alternative), /* protobuf tag */ 13), +}), + /* size */ sizeof(Track), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassEpisode, + /* name */ "Episode", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::OptionalOfVectorOfUint8, /* name */ "gid", /* offset */ offsetof(Episode, gid), /* protobuf tag */ 1), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "name", /* offset */ offsetof(Episode, name), /* protobuf tag */ 2), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfInt32, /* name */ "duration", /* offset */ offsetof(Episode, duration), /* protobuf tag */ 7), +ReflectField( /* typeID */ ReflectTypeID::VectorOfClassAudioFile, /* name */ "audio", /* offset */ offsetof(Episode, audio), /* protobuf tag */ 12), +}), + /* size */ sizeof(Episode), + __reflectConstruct, + __reflectDestruct), + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfEnumAudioFormat, + /* inner type id */ ReflectTypeID::EnumAudioFormat, + /* size */ sizeof(std::optional), + /* option */ OptionalOperations{ + .get = __OptionalManipulator::get, + .has_value = __OptionalManipulator::has_value, + .set = __OptionalManipulator::set, + .reset = __OptionalManipulator::reset, + .emplaceEmpty = __OptionalManipulator::emplaceEmpty, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofVector( + /* mine typeId */ ReflectTypeID::VectorOfClassImage, + /* inner type id */ ReflectTypeID::ClassImage, + /* size */ sizeof(std::vector), + VectorOperations{ + .push_back = __VectorManipulator::push_back, + .at = __VectorManipulator::at, + .size = __VectorManipulator::size, + .emplace_back = __VectorManipulator::emplace_back, + .clear = __VectorManipulator::clear, + .reserve = __VectorManipulator::reserve, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfClassImageGroup, + /* inner type id */ ReflectTypeID::ClassImageGroup, + /* size */ sizeof(std::optional), + /* option */ OptionalOperations{ + .get = __OptionalManipulator::get, + .has_value = __OptionalManipulator::has_value, + .set = __OptionalManipulator::set, + .reset = __OptionalManipulator::reset, + .emplaceEmpty = __OptionalManipulator::emplaceEmpty, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfClassAlbum, + /* inner type id */ ReflectTypeID::ClassAlbum, + /* size */ sizeof(std::optional), + /* option */ OptionalOperations{ + .get = __OptionalManipulator::get, + .has_value = __OptionalManipulator::has_value, + .set = __OptionalManipulator::set, + .reset = __OptionalManipulator::reset, + .emplaceEmpty = __OptionalManipulator::emplaceEmpty, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofVector( + /* mine typeId */ ReflectTypeID::VectorOfClassArtist, + /* inner type id */ ReflectTypeID::ClassArtist, + /* size */ sizeof(std::vector), + VectorOperations{ + .push_back = __VectorManipulator::push_back, + .at = __VectorManipulator::at, + .size = __VectorManipulator::size, + .emplace_back = __VectorManipulator::emplace_back, + .clear = __VectorManipulator::clear, + .reserve = __VectorManipulator::reserve, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfInt32, + /* inner type id */ ReflectTypeID::Int32, + /* size */ sizeof(std::optional), + /* option */ OptionalOperations{ + .get = __OptionalManipulator::get, + .has_value = __OptionalManipulator::has_value, + .set = __OptionalManipulator::set, + .reset = __OptionalManipulator::reset, + .emplaceEmpty = __OptionalManipulator::emplaceEmpty, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofVector( + /* mine typeId */ ReflectTypeID::VectorOfClassRestriction, + /* inner type id */ ReflectTypeID::ClassRestriction, + /* size */ sizeof(std::vector), + VectorOperations{ + .push_back = __VectorManipulator::push_back, + .at = __VectorManipulator::at, + .size = __VectorManipulator::size, + .emplace_back = __VectorManipulator::emplace_back, + .clear = __VectorManipulator::clear, + .reserve = __VectorManipulator::reserve, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofVector( + /* mine typeId */ ReflectTypeID::VectorOfClassAudioFile, + /* inner type id */ ReflectTypeID::ClassAudioFile, + /* size */ sizeof(std::vector), + VectorOperations{ + .push_back = __VectorManipulator::push_back, + .at = __VectorManipulator::at, + .size = __VectorManipulator::size, + .emplace_back = __VectorManipulator::emplace_back, + .clear = __VectorManipulator::clear, + .reserve = __VectorManipulator::reserve, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofVector( + /* mine typeId */ ReflectTypeID::VectorOfClassTrack, + /* inner type id */ ReflectTypeID::ClassTrack, + /* size */ sizeof(std::vector), + VectorOperations{ + .push_back = __VectorManipulator::push_back, + .at = __VectorManipulator::at, + .size = __VectorManipulator::size, + .emplace_back = __VectorManipulator::emplace_back, + .clear = __VectorManipulator::clear, + .reserve = __VectorManipulator::reserve, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , +ReflectType::ofEnum(/* mine id */ ReflectTypeID::EnumMessageType, /* name */ "MessageType", /* enum values */ std::move(std::vector{ ReflectEnumValue("kMessageTypeHello", 1), + ReflectEnumValue("kMessageTypeGoodbye", 2), + ReflectEnumValue("kMessageTypeProbe", 3), + ReflectEnumValue("kMessageTypeNotify", 10), + ReflectEnumValue("kMessageTypeLoad", 20), + ReflectEnumValue("kMessageTypePlay", 21), + ReflectEnumValue("kMessageTypePause", 22), + ReflectEnumValue("kMessageTypePlayPause", 23), + ReflectEnumValue("kMessageTypeSeek", 24), + ReflectEnumValue("kMessageTypePrev", 25), + ReflectEnumValue("kMessageTypeNext", 26), + ReflectEnumValue("kMessageTypeVolume", 27), + ReflectEnumValue("kMessageTypeShuffle", 28), + ReflectEnumValue("kMessageTypeRepeat", 29), + ReflectEnumValue("kMessageTypeVolumeDown", 31), + ReflectEnumValue("kMessageTypeVolumeUp", 32), + ReflectEnumValue("kMessageTypeReplace", 33), + ReflectEnumValue("kMessageTypeLogout", 34), + ReflectEnumValue("kMessageTypeAction", 35), +}), /* size */ sizeof(MessageType)), +ReflectType::ofEnum(/* mine id */ ReflectTypeID::EnumPlayStatus, /* name */ "PlayStatus", /* enum values */ std::move(std::vector{ ReflectEnumValue("kPlayStatusStop", 0), + ReflectEnumValue("kPlayStatusPlay", 1), + ReflectEnumValue("kPlayStatusPause", 2), + ReflectEnumValue("kPlayStatusLoading", 3), +}), /* size */ sizeof(PlayStatus)), +ReflectType::ofEnum(/* mine id */ ReflectTypeID::EnumCapabilityType, /* name */ "CapabilityType", /* enum values */ std::move(std::vector{ ReflectEnumValue("kSupportedContexts", 1), + ReflectEnumValue("kCanBePlayer", 2), + ReflectEnumValue("kRestrictToLocal", 3), + ReflectEnumValue("kDeviceType", 4), + ReflectEnumValue("kGaiaEqConnectId", 5), + ReflectEnumValue("kSupportsLogout", 6), + ReflectEnumValue("kIsObservable", 7), + ReflectEnumValue("kVolumeSteps", 8), + ReflectEnumValue("kSupportedTypes", 9), + ReflectEnumValue("kCommandAcks", 10), +}), /* size */ sizeof(CapabilityType)), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassTrackRef, + /* name */ "TrackRef", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::OptionalOfVectorOfUint8, /* name */ "gid", /* offset */ offsetof(TrackRef, gid), /* protobuf tag */ 1), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "uri", /* offset */ offsetof(TrackRef, uri), /* protobuf tag */ 2), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfBool, /* name */ "queued", /* offset */ offsetof(TrackRef, queued), /* protobuf tag */ 3), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "context", /* offset */ offsetof(TrackRef, context), /* protobuf tag */ 4), +}), + /* size */ sizeof(TrackRef), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassState, + /* name */ "State", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "context_uri", /* offset */ offsetof(State, context_uri), /* protobuf tag */ 2), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfUint32, /* name */ "index", /* offset */ offsetof(State, index), /* protobuf tag */ 3), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfUint32, /* name */ "position_ms", /* offset */ offsetof(State, position_ms), /* protobuf tag */ 4), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfEnumPlayStatus, /* name */ "status", /* offset */ offsetof(State, status), /* protobuf tag */ 5), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfUint64, /* name */ "position_measured_at", /* offset */ offsetof(State, position_measured_at), /* protobuf tag */ 7), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "context_description", /* offset */ offsetof(State, context_description), /* protobuf tag */ 8), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfBool, /* name */ "shuffle", /* offset */ offsetof(State, shuffle), /* protobuf tag */ 13), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfBool, /* name */ "repeat", /* offset */ offsetof(State, repeat), /* protobuf tag */ 14), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfUint32, /* name */ "playing_track_index", /* offset */ offsetof(State, playing_track_index), /* protobuf tag */ 26), +ReflectField( /* typeID */ ReflectTypeID::VectorOfClassTrackRef, /* name */ "track", /* offset */ offsetof(State, track), /* protobuf tag */ 27), +}), + /* size */ sizeof(State), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassCapability, + /* name */ "Capability", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::OptionalOfEnumCapabilityType, /* name */ "typ", /* offset */ offsetof(Capability, typ), /* protobuf tag */ 1), +ReflectField( /* typeID */ ReflectTypeID::VectorOfInt64, /* name */ "intValue", /* offset */ offsetof(Capability, intValue), /* protobuf tag */ 2), +ReflectField( /* typeID */ ReflectTypeID::VectorOfString, /* name */ "stringValue", /* offset */ offsetof(Capability, stringValue), /* protobuf tag */ 3), +}), + /* size */ sizeof(Capability), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassDeviceState, + /* name */ "DeviceState", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "sw_version", /* offset */ offsetof(DeviceState, sw_version), /* protobuf tag */ 1), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfBool, /* name */ "is_active", /* offset */ offsetof(DeviceState, is_active), /* protobuf tag */ 10), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfBool, /* name */ "can_play", /* offset */ offsetof(DeviceState, can_play), /* protobuf tag */ 11), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfUint32, /* name */ "volume", /* offset */ offsetof(DeviceState, volume), /* protobuf tag */ 12), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "name", /* offset */ offsetof(DeviceState, name), /* protobuf tag */ 13), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfUint32, /* name */ "error_code", /* offset */ offsetof(DeviceState, error_code), /* protobuf tag */ 14), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfInt64, /* name */ "became_active_at", /* offset */ offsetof(DeviceState, became_active_at), /* protobuf tag */ 15), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "error_message", /* offset */ offsetof(DeviceState, error_message), /* protobuf tag */ 16), +ReflectField( /* typeID */ ReflectTypeID::VectorOfClassCapability, /* name */ "capabilities", /* offset */ offsetof(DeviceState, capabilities), /* protobuf tag */ 17), +ReflectField( /* typeID */ ReflectTypeID::VectorOfString, /* name */ "local_uris", /* offset */ offsetof(DeviceState, local_uris), /* protobuf tag */ 18), +}), + /* size */ sizeof(DeviceState), + __reflectConstruct, + __reflectDestruct), +ReflectType::ofClass( + /* mine type id */ ReflectTypeID::ClassFrame, + /* name */ "Frame", + /* fields */ std::move(std::vector{ReflectField( /* typeID */ ReflectTypeID::OptionalOfUint32, /* name */ "version", /* offset */ offsetof(Frame, version), /* protobuf tag */ 1), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "ident", /* offset */ offsetof(Frame, ident), /* protobuf tag */ 2), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfString, /* name */ "protocol_version", /* offset */ offsetof(Frame, protocol_version), /* protobuf tag */ 3), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfUint32, /* name */ "seq_nr", /* offset */ offsetof(Frame, seq_nr), /* protobuf tag */ 4), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfEnumMessageType, /* name */ "typ", /* offset */ offsetof(Frame, typ), /* protobuf tag */ 5), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfClassDeviceState, /* name */ "device_state", /* offset */ offsetof(Frame, device_state), /* protobuf tag */ 7), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfClassState, /* name */ "state", /* offset */ offsetof(Frame, state), /* protobuf tag */ 12), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfUint32, /* name */ "position", /* offset */ offsetof(Frame, position), /* protobuf tag */ 13), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfUint32, /* name */ "volume", /* offset */ offsetof(Frame, volume), /* protobuf tag */ 14), +ReflectField( /* typeID */ ReflectTypeID::OptionalOfInt64, /* name */ "state_update_id", /* offset */ offsetof(Frame, state_update_id), /* protobuf tag */ 17), +ReflectField( /* typeID */ ReflectTypeID::VectorOfString, /* name */ "recipient", /* offset */ offsetof(Frame, recipient), /* protobuf tag */ 18), +}), + /* size */ sizeof(Frame), + __reflectConstruct, + __reflectDestruct), + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfUint32, + /* inner type id */ ReflectTypeID::Uint32, + /* size */ sizeof(std::optional), + /* option */ OptionalOperations{ + .get = __OptionalManipulator::get, + .has_value = __OptionalManipulator::has_value, + .set = __OptionalManipulator::set, + .reset = __OptionalManipulator::reset, + .emplaceEmpty = __OptionalManipulator::emplaceEmpty, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfEnumPlayStatus, + /* inner type id */ ReflectTypeID::EnumPlayStatus, + /* size */ sizeof(std::optional), + /* option */ OptionalOperations{ + .get = __OptionalManipulator::get, + .has_value = __OptionalManipulator::has_value, + .set = __OptionalManipulator::set, + .reset = __OptionalManipulator::reset, + .emplaceEmpty = __OptionalManipulator::emplaceEmpty, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfUint64, + /* inner type id */ ReflectTypeID::Uint64, + /* size */ sizeof(std::optional), + /* option */ OptionalOperations{ + .get = __OptionalManipulator::get, + .has_value = __OptionalManipulator::has_value, + .set = __OptionalManipulator::set, + .reset = __OptionalManipulator::reset, + .emplaceEmpty = __OptionalManipulator::emplaceEmpty, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofVector( + /* mine typeId */ ReflectTypeID::VectorOfClassTrackRef, + /* inner type id */ ReflectTypeID::ClassTrackRef, + /* size */ sizeof(std::vector), + VectorOperations{ + .push_back = __VectorManipulator::push_back, + .at = __VectorManipulator::at, + .size = __VectorManipulator::size, + .emplace_back = __VectorManipulator::emplace_back, + .clear = __VectorManipulator::clear, + .reserve = __VectorManipulator::reserve, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfEnumCapabilityType, + /* inner type id */ ReflectTypeID::EnumCapabilityType, + /* size */ sizeof(std::optional), + /* option */ OptionalOperations{ + .get = __OptionalManipulator::get, + .has_value = __OptionalManipulator::has_value, + .set = __OptionalManipulator::set, + .reset = __OptionalManipulator::reset, + .emplaceEmpty = __OptionalManipulator::emplaceEmpty, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofVector( + /* mine typeId */ ReflectTypeID::VectorOfInt64, + /* inner type id */ ReflectTypeID::Int64, + /* size */ sizeof(std::vector), + VectorOperations{ + .push_back = __VectorManipulator::push_back, + .at = __VectorManipulator::at, + .size = __VectorManipulator::size, + .emplace_back = __VectorManipulator::emplace_back, + .clear = __VectorManipulator::clear, + .reserve = __VectorManipulator::reserve, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofVector( + /* mine typeId */ ReflectTypeID::VectorOfString, + /* inner type id */ ReflectTypeID::String, + /* size */ sizeof(std::vector), + VectorOperations{ + .push_back = __VectorManipulator::push_back, + .at = __VectorManipulator::at, + .size = __VectorManipulator::size, + .emplace_back = __VectorManipulator::emplace_back, + .clear = __VectorManipulator::clear, + .reserve = __VectorManipulator::reserve, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfInt64, + /* inner type id */ ReflectTypeID::Int64, + /* size */ sizeof(std::optional), + /* option */ OptionalOperations{ + .get = __OptionalManipulator::get, + .has_value = __OptionalManipulator::has_value, + .set = __OptionalManipulator::set, + .reset = __OptionalManipulator::reset, + .emplaceEmpty = __OptionalManipulator::emplaceEmpty, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofVector( + /* mine typeId */ ReflectTypeID::VectorOfClassCapability, + /* inner type id */ ReflectTypeID::ClassCapability, + /* size */ sizeof(std::vector), + VectorOperations{ + .push_back = __VectorManipulator::push_back, + .at = __VectorManipulator::at, + .size = __VectorManipulator::size, + .emplace_back = __VectorManipulator::emplace_back, + .clear = __VectorManipulator::clear, + .reserve = __VectorManipulator::reserve, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfEnumMessageType, + /* inner type id */ ReflectTypeID::EnumMessageType, + /* size */ sizeof(std::optional), + /* option */ OptionalOperations{ + .get = __OptionalManipulator::get, + .has_value = __OptionalManipulator::has_value, + .set = __OptionalManipulator::set, + .reset = __OptionalManipulator::reset, + .emplaceEmpty = __OptionalManipulator::emplaceEmpty, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfClassDeviceState, + /* inner type id */ ReflectTypeID::ClassDeviceState, + /* size */ sizeof(std::optional), + /* option */ OptionalOperations{ + .get = __OptionalManipulator::get, + .has_value = __OptionalManipulator::has_value, + .set = __OptionalManipulator::set, + .reset = __OptionalManipulator::reset, + .emplaceEmpty = __OptionalManipulator::emplaceEmpty, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , + + ReflectType::ofOptional( + /* mine typeId */ ReflectTypeID::OptionalOfClassState, + /* inner type id */ ReflectTypeID::ClassState, + /* size */ sizeof(std::optional), + /* option */ OptionalOperations{ + .get = __OptionalManipulator::get, + .has_value = __OptionalManipulator::has_value, + .set = __OptionalManipulator::set, + .reset = __OptionalManipulator::reset, + .emplaceEmpty = __OptionalManipulator::emplaceEmpty, + }, + __reflectConstruct>, + __reflectDestruct> + ) + + + + , +}; + diff --git a/components/spotify/cspot/protos/ReflectionInternal.h b/components/spotify/cspot/protos/ReflectionInternal.h new file mode 100644 index 00000000..47d9a65c --- /dev/null +++ b/components/spotify/cspot/protos/ReflectionInternal.h @@ -0,0 +1,210 @@ +// THIS CORNFILE IS GENERATED. DO NOT EDIT! 🌽 +#ifndef __REFLECTION_INTERNALH +#define __REFLECTION_INTERNALH +#include +#include +enum class ReflectTypeID { +EnumReflectTypeID = 0, +ClassReflectField = 1, +ClassReflectEnumValue = 2, +ClassReflectType = 3, +EnumReflectTypeKind = 4, +VectorOfClassReflectField = 5, +VectorOfClassReflectEnumValue = 6, +Double = 7, +Uint32 = 8, +Char = 9, +UnsignedChar = 10, +Float = 11, +Bool = 12, +String = 13, +Int32 = 14, +Int64 = 15, +Int = 16, +Uint8 = 17, +Uint64 = 18, +VectorOfUint8 = 19, +EnumCpuFamily = 20, +EnumOs = 21, +EnumAuthenticationType = 22, +ClassSystemInfo = 23, +ClassLoginCredentials = 24, +ClassClientResponseEncrypted = 25, +OptionalOfString = 26, +OptionalOfVectorOfUint8 = 27, +EnumProduct = 28, +EnumPlatform = 29, +EnumCryptosuite = 30, +ClassLoginCryptoDiffieHellmanChallenge = 31, +ClassLoginCryptoChallengeUnion = 32, +ClassLoginCryptoDiffieHellmanHello = 33, +ClassLoginCryptoHelloUnion = 34, +ClassBuildInfo = 35, +ClassFeatureSet = 36, +ClassAPChallenge = 37, +ClassAPResponseMessage = 38, +ClassLoginCryptoDiffieHellmanResponse = 39, +ClassLoginCryptoResponseUnion = 40, +ClassCryptoResponseUnion = 41, +ClassPoWResponseUnion = 42, +ClassClientResponsePlaintext = 43, +ClassClientHello = 44, +OptionalOfClassLoginCryptoDiffieHellmanChallenge = 45, +OptionalOfClassLoginCryptoDiffieHellmanHello = 46, +OptionalOfBool = 47, +OptionalOfClassAPChallenge = 48, +OptionalOfClassLoginCryptoDiffieHellmanResponse = 49, +VectorOfEnumCryptosuite = 50, +OptionalOfClassFeatureSet = 51, +ClassHeader = 52, +EnumAudioFormat = 53, +ClassAudioFile = 54, +ClassRestriction = 55, +ClassImage = 56, +ClassImageGroup = 57, +ClassAlbum = 58, +ClassArtist = 59, +ClassTrack = 60, +ClassEpisode = 61, +OptionalOfEnumAudioFormat = 62, +VectorOfClassImage = 63, +OptionalOfClassImageGroup = 64, +OptionalOfClassAlbum = 65, +VectorOfClassArtist = 66, +OptionalOfInt32 = 67, +VectorOfClassRestriction = 68, +VectorOfClassAudioFile = 69, +VectorOfClassTrack = 70, +EnumMessageType = 71, +EnumPlayStatus = 72, +EnumCapabilityType = 73, +ClassTrackRef = 74, +ClassState = 75, +ClassCapability = 76, +ClassDeviceState = 77, +ClassFrame = 78, +OptionalOfUint32 = 79, +OptionalOfEnumPlayStatus = 80, +OptionalOfUint64 = 81, +VectorOfClassTrackRef = 82, +OptionalOfEnumCapabilityType = 83, +VectorOfInt64 = 84, +VectorOfString = 85, +OptionalOfInt64 = 86, +VectorOfClassCapability = 87, +OptionalOfEnumMessageType = 88, +OptionalOfClassDeviceState = 89, +OptionalOfClassState = 90, +}; + +enum class ReflectTypeKind { +Primitive = 0, +Enum = 1, +Class = 2, +Vector = 3, +Optional = 4, +}; + +class ReflectField { +public: +ReflectTypeID typeID; +std::string name; +size_t offset; +uint32_t protobufTag; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassReflectField; + + ReflectField() {}; + ReflectField(ReflectTypeID typeID, std::string name, size_t offset, uint32_t protobufTag) { + this->typeID = typeID; + this->name = name; + this->offset = offset; + this->protobufTag = protobufTag; + } + }; + +class ReflectEnumValue { +public: +std::string name; +int value; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassReflectEnumValue; + + ReflectEnumValue(){}; + ReflectEnumValue( std::string name, int value) { + this->name = name; + this->value = value; + } + }; + +class ReflectType { +public: +ReflectTypeID typeID; +std::string name; +ReflectTypeKind kind; +size_t size; +ReflectTypeID innerType; +std::vector fields; +std::vector enumValues; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassReflectType; + + void (*_Construct)(void *mem); + void (*_Destruct)(void *obj); + VectorOperations vectorOps; + OptionalOperations optionalOps; + static ReflectType ofPrimitive(ReflectTypeID id, std::string name, size_t size) { + ReflectType t; + t.kind = ReflectTypeKind::Primitive; + t.typeID = id; + t.name = name; + t.size = size; + return t; + } + static ReflectType ofEnum(ReflectTypeID id, std::string name, std::vector enumValues, size_t size) { + ReflectType t; + t.kind = ReflectTypeKind::Enum; + t.typeID = id; + t.name = name; + t.size = size; + t.enumValues = enumValues; + return t; + } + static ReflectType ofVector(ReflectTypeID id, ReflectTypeID innerType, size_t size, + VectorOperations vectorOps, + void (*_Construct)(void *mem), void (*_Destruct)(void *obj)) { + ReflectType t; + t.kind = ReflectTypeKind::Vector; + t.typeID = id; + t.innerType = innerType; + t.size = size; + t._Construct = _Construct; + t._Destruct = _Destruct; + t.vectorOps = vectorOps; + return t; + } + static ReflectType ofOptional(ReflectTypeID id, ReflectTypeID innerType, size_t size, + OptionalOperations optionalOps, + void (*_Construct)(void *mem), void (*_Destruct)(void *obj)) { + ReflectType t; + t.kind = ReflectTypeKind::Optional; + t.typeID = id; + t.innerType = innerType; + t.size = size; + t._Construct = _Construct; + t._Destruct = _Destruct; + t.optionalOps = optionalOps; + return t; + } + static ReflectType ofClass(ReflectTypeID id, std::string name, std::vector fields, size_t size, void (*_Construct)(void *mem), void (*_Destruct)(void *obj)) { + ReflectType t; + t.kind = ReflectTypeKind::Class; + t.name = name; + t.typeID = id; + t.size = size; + t.fields = std::move(fields); + t._Construct = _Construct; + t._Destruct = _Destruct; + return t; + } + + }; + +#endif diff --git a/components/spotify/cspot/protos/authentication.h b/components/spotify/cspot/protos/authentication.h new file mode 100644 index 00000000..52cc46ee --- /dev/null +++ b/components/spotify/cspot/protos/authentication.h @@ -0,0 +1,78 @@ +// THIS CORNFILE IS GENERATED. DO NOT EDIT! 🌽 +#ifndef _AUTHENTICATIONH +#define _AUTHENTICATIONH +#include +#include +enum class CpuFamily { +CPU_UNKNOWN = 0, +CPU_X86 = 1, +CPU_X86_64 = 2, +CPU_PPC = 3, +CPU_PPC_64 = 4, +CPU_ARM = 5, +CPU_IA64 = 6, +CPU_SH = 7, +CPU_MIPS = 8, +CPU_BLACKFIN = 9, +}; + +enum class Os { +OS_UNKNOWN = 0, +OS_WINDOWS = 1, +OS_OSX = 2, +OS_IPHONE = 3, +OS_S60 = 4, +OS_LINUX = 5, +OS_WINDOWS_CE = 6, +OS_ANDROID = 7, +OS_PALM = 8, +OS_FREEBSD = 9, +OS_BLACKBERRY = 10, +OS_SONOS = 11, +OS_LOGITECH = 12, +OS_WP7 = 13, +OS_ONKYO = 14, +OS_PHILIPS = 15, +OS_WD = 16, +OS_VOLVO = 17, +OS_TIVO = 18, +OS_AWOX = 19, +OS_MEEGO = 20, +OS_QNXNTO = 21, +OS_BCO = 22, +}; + +enum class AuthenticationType { +AUTHENTICATION_USER_PASS = 0, +AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS = 1, +AUTHENTICATION_STORED_FACEBOOK_CREDENTIALS = 2, +AUTHENTICATION_SPOTIFY_TOKEN = 3, +AUTHENTICATION_FACEBOOK_TOKEN = 4, +}; + +class SystemInfo { +public: +CpuFamily cpu_family; +Os os; +std::optional system_information_string; +std::optional device_id; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassSystemInfo; +}; + +class LoginCredentials { +public: +std::optional username; +AuthenticationType typ; +std::optional> auth_data; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassLoginCredentials; +}; + +class ClientResponseEncrypted { +public: +LoginCredentials login_credentials; +SystemInfo system_info; +std::optional version_string; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassClientResponseEncrypted; +}; + +#endif diff --git a/components/spotify/cspot/protos/keyexchange.h b/components/spotify/cspot/protos/keyexchange.h new file mode 100644 index 00000000..24f52228 --- /dev/null +++ b/components/spotify/cspot/protos/keyexchange.h @@ -0,0 +1,138 @@ +// THIS CORNFILE IS GENERATED. DO NOT EDIT! 🌽 +#ifndef _KEYEXCHANGEH +#define _KEYEXCHANGEH +#include +#include +enum class Product { +PRODUCT_CLIENT = 0, +PRODUCT_LIBSPOTIFY = 1, +PRODUCT_MOBILE = 2, +PRODUCT_PARTNER = 3, +PRODUCT_LIBSPOTIFY_EMBEDDED = 5, +}; + +enum class Platform { +PLATFORM_WIN32_X86 = 0, +PLATFORM_OSX_X86 = 1, +PLATFORM_LINUX_X86 = 2, +PLATFORM_IPHONE_ARM = 3, +PLATFORM_S60_ARM = 4, +PLATFORM_OSX_PPC = 5, +PLATFORM_ANDROID_ARM = 6, +PLATFORM_WINDOWS_CE_ARM = 7, +PLATFORM_LINUX_X86_64 = 8, +PLATFORM_OSX_X86_64 = 9, +PLATFORM_PALM_ARM = 10, +PLATFORM_LINUX_SH = 11, +PLATFORM_FREEBSD_X86 = 12, +PLATFORM_FREEBSD_X86_64 = 13, +PLATFORM_BLACKBERRY_ARM = 14, +PLATFORM_SONOS = 15, +PLATFORM_LINUX_MIPS = 16, +PLATFORM_LINUX_ARM = 17, +PLATFORM_LOGITECH_ARM = 18, +PLATFORM_LINUX_BLACKFIN = 19, +PLATFORM_WP7_ARM = 20, +PLATFORM_ONKYO_ARM = 21, +PLATFORM_QNXNTO_ARM = 22, +PLATFORM_BCO_ARM = 23, +}; + +enum class Cryptosuite { +CRYPTO_SUITE_SHANNON = 0, +CRYPTO_SUITE_RC4_SHA1_HMAC = 1, +}; + +class LoginCryptoDiffieHellmanChallenge { +public: +std::vector gs; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassLoginCryptoDiffieHellmanChallenge; +}; + +class LoginCryptoChallengeUnion { +public: +std::optional diffie_hellman; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassLoginCryptoChallengeUnion; +}; + +class LoginCryptoDiffieHellmanHello { +public: +std::vector gc; +uint32_t server_keys_known; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassLoginCryptoDiffieHellmanHello; +}; + +class LoginCryptoHelloUnion { +public: +std::optional diffie_hellman; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassLoginCryptoHelloUnion; +}; + +class BuildInfo { +public: +Product product; +Platform platform; +uint64_t version; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassBuildInfo; +}; + +class FeatureSet { +public: +std::optional autoupdate2; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassFeatureSet; +}; + +class APChallenge { +public: +LoginCryptoChallengeUnion login_crypto_challenge; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassAPChallenge; +}; + +class APResponseMessage { +public: +std::optional challenge; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassAPResponseMessage; +}; + +class LoginCryptoDiffieHellmanResponse { +public: +std::vector hmac; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassLoginCryptoDiffieHellmanResponse; +}; + +class LoginCryptoResponseUnion { +public: +std::optional diffie_hellman; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassLoginCryptoResponseUnion; +}; + +class CryptoResponseUnion { +public: +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassCryptoResponseUnion; +}; + +class PoWResponseUnion { +public: +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassPoWResponseUnion; +}; + +class ClientResponsePlaintext { +public: +LoginCryptoResponseUnion login_crypto_response; +PoWResponseUnion pow_response; +CryptoResponseUnion crypto_response; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassClientResponsePlaintext; +}; + +class ClientHello { +public: +BuildInfo build_info; +LoginCryptoHelloUnion login_crypto_hello; +std::vector cryptosuites_supported; +std::vector client_nonce; +std::optional> padding; +std::optional feature_set; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassClientHello; +}; + +#endif diff --git a/components/spotify/cspot/protos/mercury.h b/components/spotify/cspot/protos/mercury.h new file mode 100644 index 00000000..d518f2ca --- /dev/null +++ b/components/spotify/cspot/protos/mercury.h @@ -0,0 +1,12 @@ +// THIS CORNFILE IS GENERATED. DO NOT EDIT! 🌽 +#ifndef _MERCURYH +#define _MERCURYH +#include +class Header { +public: +std::optional uri; +std::optional method; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassHeader; +}; + +#endif diff --git a/components/spotify/cspot/protos/metadata.h b/components/spotify/cspot/protos/metadata.h new file mode 100644 index 00000000..468a5b4c --- /dev/null +++ b/components/spotify/cspot/protos/metadata.h @@ -0,0 +1,82 @@ +// THIS CORNFILE IS GENERATED. DO NOT EDIT! 🌽 +#ifndef _METADATAH +#define _METADATAH +#include +#include +enum class AudioFormat { +OGG_VORBIS_96 = 0, +OGG_VORBIS_160 = 1, +OGG_VORBIS_320 = 2, +MP3_256 = 3, +MP3_320 = 4, +MP3_160 = 5, +MP3_96 = 6, +MP3_160_ENC = 7, +AAC_24 = 8, +AAC_48 = 9, +}; + +class AudioFile { +public: +std::optional> file_id; +std::optional format; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassAudioFile; +}; + +class Restriction { +public: +std::optional countries_allowed; +std::optional countries_forbidden; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassRestriction; +}; + +class Image { +public: +std::optional> file_id; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassImage; +}; + +class ImageGroup { +public: +std::vector image; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassImageGroup; +}; + +class Album { +public: +std::optional> gid; +std::optional name; +std::optional cover_group; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassAlbum; +}; + +class Artist { +public: +std::optional> gid; +std::optional name; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassArtist; +}; + +class Track { +public: +std::optional> gid; +std::optional name; +std::optional album; +std::vector artist; +std::optional duration; +std::vector restriction; +std::vector file; +std::vector alternative; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassTrack; +}; + +class Episode { +public: +std::optional> gid; +std::optional name; +std::optional duration; +std::vector audio; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassEpisode; +}; + +#endif diff --git a/components/spotify/cspot/protos/protobuf.h b/components/spotify/cspot/protos/protobuf.h new file mode 100644 index 00000000..9911a78c --- /dev/null +++ b/components/spotify/cspot/protos/protobuf.h @@ -0,0 +1,715 @@ +// THIS CORNFILE IS GENERATED. DO NOT EDIT! 🌽 +#ifndef _PROTOBUFH +#define _PROTOBUFH +#include +#include +#include +#include +#include + + class AnyRef; + struct VectorOperations { + void (*push_back)(AnyRef &vec, AnyRef &val); + AnyRef (*at)(AnyRef &vec, size_t index); + size_t (*size)(AnyRef &vec); + void (*emplace_back)(AnyRef &vec); + void (*clear)(AnyRef &vec); + void (*reserve)(AnyRef &vec, size_t n); + }; + + + + class AnyRef; + struct OptionalOperations { + AnyRef (*get)(AnyRef &opt); + bool (*has_value)(AnyRef &opt); + void (*set)(AnyRef &opt, AnyRef &val); + void (*reset)(AnyRef &opt); + void (*emplaceEmpty)(AnyRef &opt); + }; + + +#include "ReflectionInternal.h" +#include "authentication.h" +#include "keyexchange.h" +#include "metadata.h" +#include "spirc.h" +#include "mercury.h" + + template + void __reflectConstruct(void *mem) { + new(mem) T; + } + template + void __reflectDestruct(void *obj) { + ((T*) obj)->~T(); + } + + +class ReflectField; +class ReflectEnumValue; +class ReflectType; +class SystemInfo; +class LoginCredentials; +class ClientResponseEncrypted; +class LoginCryptoDiffieHellmanChallenge; +class LoginCryptoChallengeUnion; +class LoginCryptoDiffieHellmanHello; +class LoginCryptoHelloUnion; +class BuildInfo; +class FeatureSet; +class APChallenge; +class APResponseMessage; +class LoginCryptoDiffieHellmanResponse; +class LoginCryptoResponseUnion; +class CryptoResponseUnion; +class PoWResponseUnion; +class ClientResponsePlaintext; +class ClientHello; +class Header; +class AudioFile; +class Restriction; +class Image; +class ImageGroup; +class Album; +class Artist; +class Track; +class Episode; +class TrackRef; +class State; +class Capability; +class DeviceState; +class Frame; + + + + class AnyRef { + public: + ReflectTypeID typeID; + AnyRef() {}; + AnyRef(ReflectTypeID typeID, void *obj) { + this->typeID = typeID; + this->value.voidptr = obj; + } + template + T *as() { + // if(T::_TYPE_ID != this->typeID) { + // throw "invalid as call"; + // } + return (T*) this->value.voidptr; + } + + template + bool is() { + if constexpr(std::is_same::value) { + return ReflectTypeID::EnumReflectTypeID == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::EnumReflectTypeKind == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::VectorOfClassReflectField == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::VectorOfClassReflectEnumValue == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::Double == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::Uint32 == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::Char == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::UnsignedChar == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::Float == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::Bool == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::String == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::Int32 == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::Int64 == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::Int == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::Uint8 == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::Uint64 == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::VectorOfUint8 == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::EnumCpuFamily == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::EnumOs == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::EnumAuthenticationType == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::OptionalOfString == this->typeID; + } else + if constexpr(std::is_same>>::value) { + return ReflectTypeID::OptionalOfVectorOfUint8 == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::EnumProduct == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::EnumPlatform == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::EnumCryptosuite == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::OptionalOfClassLoginCryptoDiffieHellmanChallenge == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::OptionalOfClassLoginCryptoDiffieHellmanHello == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::OptionalOfBool == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::OptionalOfClassAPChallenge == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::OptionalOfClassLoginCryptoDiffieHellmanResponse == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::VectorOfEnumCryptosuite == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::OptionalOfClassFeatureSet == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::EnumAudioFormat == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::OptionalOfEnumAudioFormat == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::VectorOfClassImage == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::OptionalOfClassImageGroup == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::OptionalOfClassAlbum == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::VectorOfClassArtist == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::OptionalOfInt32 == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::VectorOfClassRestriction == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::VectorOfClassAudioFile == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::VectorOfClassTrack == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::EnumMessageType == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::EnumPlayStatus == this->typeID; + } else + if constexpr(std::is_same::value) { + return ReflectTypeID::EnumCapabilityType == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::OptionalOfUint32 == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::OptionalOfEnumPlayStatus == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::OptionalOfUint64 == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::VectorOfClassTrackRef == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::OptionalOfEnumCapabilityType == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::VectorOfInt64 == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::VectorOfString == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::OptionalOfInt64 == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::VectorOfClassCapability == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::OptionalOfEnumMessageType == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::OptionalOfClassDeviceState == this->typeID; + } else + if constexpr(std::is_same>::value) { + return ReflectTypeID::OptionalOfClassState == this->typeID; + } else + { + return T::_TYPE_ID == this->typeID; + } + } + + + ReflectType *reflectType(); + AnyRef getField(int i); + template + static AnyRef of(T *obj) + { + ReflectTypeID typeID; + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::EnumReflectTypeID; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::EnumReflectTypeKind; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::VectorOfClassReflectField; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::VectorOfClassReflectEnumValue; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::Double; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::Uint32; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::Char; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::UnsignedChar; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::Float; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::Bool; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::String; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::Int32; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::Int64; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::Int; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::Uint8; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::Uint64; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::VectorOfUint8; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::EnumCpuFamily; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::EnumOs; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::EnumAuthenticationType; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::OptionalOfString; + } else + if constexpr(std::is_same>>::value) { + typeID = ReflectTypeID::OptionalOfVectorOfUint8; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::EnumProduct; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::EnumPlatform; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::EnumCryptosuite; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::OptionalOfClassLoginCryptoDiffieHellmanChallenge; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::OptionalOfClassLoginCryptoDiffieHellmanHello; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::OptionalOfBool; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::OptionalOfClassAPChallenge; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::OptionalOfClassLoginCryptoDiffieHellmanResponse; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::VectorOfEnumCryptosuite; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::OptionalOfClassFeatureSet; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::EnumAudioFormat; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::OptionalOfEnumAudioFormat; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::VectorOfClassImage; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::OptionalOfClassImageGroup; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::OptionalOfClassAlbum; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::VectorOfClassArtist; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::OptionalOfInt32; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::VectorOfClassRestriction; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::VectorOfClassAudioFile; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::VectorOfClassTrack; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::EnumMessageType; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::EnumPlayStatus; + } else + if constexpr(std::is_same::value) { + typeID = ReflectTypeID::EnumCapabilityType; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::OptionalOfUint32; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::OptionalOfEnumPlayStatus; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::OptionalOfUint64; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::VectorOfClassTrackRef; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::OptionalOfEnumCapabilityType; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::VectorOfInt64; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::VectorOfString; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::OptionalOfInt64; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::VectorOfClassCapability; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::OptionalOfEnumMessageType; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::OptionalOfClassDeviceState; + } else + if constexpr(std::is_same>::value) { + typeID = ReflectTypeID::OptionalOfClassState; + } else + { + typeID = T::_TYPE_ID; + } + AnyRef a; + a.typeID = typeID; + a.value.voidptr = (void*) obj; + return a; + } + + union ReflectedTypes { + void *voidptr; + ReflectTypeID *u_EnumReflectTypeID; + ReflectField *u_ClassReflectField; + ReflectEnumValue *u_ClassReflectEnumValue; + ReflectType *u_ClassReflectType; + ReflectTypeKind *u_EnumReflectTypeKind; + std::vector *u_VectorOfClassReflectField; + std::vector *u_VectorOfClassReflectEnumValue; + double *u_Double; + uint32_t *u_Uint32; + char *u_Char; + unsigned char *u_UnsignedChar; + float *u_Float; + bool *u_Bool; + std::string *u_String; + int32_t *u_Int32; + int64_t *u_Int64; + int *u_Int; + uint8_t *u_Uint8; + uint64_t *u_Uint64; + std::vector *u_VectorOfUint8; + CpuFamily *u_EnumCpuFamily; + Os *u_EnumOs; + AuthenticationType *u_EnumAuthenticationType; + SystemInfo *u_ClassSystemInfo; + LoginCredentials *u_ClassLoginCredentials; + ClientResponseEncrypted *u_ClassClientResponseEncrypted; + std::optional *u_OptionalOfString; + std::optional> *u_OptionalOfVectorOfUint8; + Product *u_EnumProduct; + Platform *u_EnumPlatform; + Cryptosuite *u_EnumCryptosuite; + LoginCryptoDiffieHellmanChallenge *u_ClassLoginCryptoDiffieHellmanChallenge; + LoginCryptoChallengeUnion *u_ClassLoginCryptoChallengeUnion; + LoginCryptoDiffieHellmanHello *u_ClassLoginCryptoDiffieHellmanHello; + LoginCryptoHelloUnion *u_ClassLoginCryptoHelloUnion; + BuildInfo *u_ClassBuildInfo; + FeatureSet *u_ClassFeatureSet; + APChallenge *u_ClassAPChallenge; + APResponseMessage *u_ClassAPResponseMessage; + LoginCryptoDiffieHellmanResponse *u_ClassLoginCryptoDiffieHellmanResponse; + LoginCryptoResponseUnion *u_ClassLoginCryptoResponseUnion; + CryptoResponseUnion *u_ClassCryptoResponseUnion; + PoWResponseUnion *u_ClassPoWResponseUnion; + ClientResponsePlaintext *u_ClassClientResponsePlaintext; + ClientHello *u_ClassClientHello; + std::optional *u_OptionalOfClassLoginCryptoDiffieHellmanChallenge; + std::optional *u_OptionalOfClassLoginCryptoDiffieHellmanHello; + std::optional *u_OptionalOfBool; + std::optional *u_OptionalOfClassAPChallenge; + std::optional *u_OptionalOfClassLoginCryptoDiffieHellmanResponse; + std::vector *u_VectorOfEnumCryptosuite; + std::optional *u_OptionalOfClassFeatureSet; + Header *u_ClassHeader; + AudioFormat *u_EnumAudioFormat; + AudioFile *u_ClassAudioFile; + Restriction *u_ClassRestriction; + Image *u_ClassImage; + ImageGroup *u_ClassImageGroup; + Album *u_ClassAlbum; + Artist *u_ClassArtist; + Track *u_ClassTrack; + Episode *u_ClassEpisode; + std::optional *u_OptionalOfEnumAudioFormat; + std::vector *u_VectorOfClassImage; + std::optional *u_OptionalOfClassImageGroup; + std::optional *u_OptionalOfClassAlbum; + std::vector *u_VectorOfClassArtist; + std::optional *u_OptionalOfInt32; + std::vector *u_VectorOfClassRestriction; + std::vector *u_VectorOfClassAudioFile; + std::vector *u_VectorOfClassTrack; + MessageType *u_EnumMessageType; + PlayStatus *u_EnumPlayStatus; + CapabilityType *u_EnumCapabilityType; + TrackRef *u_ClassTrackRef; + State *u_ClassState; + Capability *u_ClassCapability; + DeviceState *u_ClassDeviceState; + Frame *u_ClassFrame; + std::optional *u_OptionalOfUint32; + std::optional *u_OptionalOfEnumPlayStatus; + std::optional *u_OptionalOfUint64; + std::vector *u_VectorOfClassTrackRef; + std::optional *u_OptionalOfEnumCapabilityType; + std::vector *u_VectorOfInt64; + std::vector *u_VectorOfString; + std::optional *u_OptionalOfInt64; + std::vector *u_VectorOfClassCapability; + std::optional *u_OptionalOfEnumMessageType; + std::optional *u_OptionalOfClassDeviceState; + std::optional *u_OptionalOfClassState; + + } value; + private: + + }; + + + + template + class __VectorManipulator { + public: + static void push_back(AnyRef &vec, AnyRef &val) { + auto theVector = reinterpret_cast*>(vec.value.voidptr); + auto theValue = *reinterpret_cast(val.value.voidptr); + theVector->push_back(theValue); + }; + static AnyRef at(AnyRef &vec, size_t index) { + auto theVector = reinterpret_cast*>(vec.value.voidptr); + return AnyRef::of(&(*theVector)[index]); + }; + static size_t size(AnyRef &vec) { + auto theVector = reinterpret_cast*>(vec.value.voidptr); + return theVector->size(); + }; + static void emplace_back(AnyRef &vec) { + auto theVector = reinterpret_cast*>(vec.value.voidptr); + theVector->emplace_back(); + }; + static void clear(AnyRef &vec) { + auto theVector = reinterpret_cast*>(vec.value.voidptr); + theVector->clear(); + }; + static void reserve(AnyRef &vec, size_t n) { + auto theVector = reinterpret_cast*>(vec.value.voidptr); + theVector->reserve(n); + }; + }; + + + template + class __OptionalManipulator { + public: + static AnyRef get(AnyRef &opt) { + auto theOptional = reinterpret_cast*>(opt.value.voidptr); + return AnyRef::of(&**theOptional); + } + static bool has_value(AnyRef &opt) { + auto theOptional = reinterpret_cast*>(opt.value.voidptr); + return theOptional->has_value(); + } + static void set(AnyRef &opt, AnyRef &val) { + auto theOptional = reinterpret_cast*>(opt.value.voidptr); + auto theValue = reinterpret_cast(val.value.voidptr); + *theOptional = *theValue; + } + + static void reset(AnyRef &opt) { + auto theOptional = reinterpret_cast*>(opt.value.voidptr); + theOptional->reset(); + } + + static void emplaceEmpty(AnyRef &opt) { + auto theOptional = reinterpret_cast*>(opt.value.voidptr); + theOptional->emplace(); + } + }; + + +extern ReflectType reflectTypeInfo[92]; + + + class UniqueAny: public AnyRef { + public: + UniqueAny() { + this->value.voidptr = nullptr; + }; + UniqueAny(ReflectTypeID typeID) { + this->typeID = typeID; + auto typeInfo = &reflectTypeInfo[static_cast(typeID)]; + AnyRef a; + this->value.voidptr = new unsigned char[typeInfo->size]; + typeInfo->_Construct(this->value.voidptr); + }; + ~UniqueAny() { + auto typeInfo = &reflectTypeInfo[static_cast(typeID)]; + typeInfo->_Destruct(this->value.voidptr); + delete reinterpret_cast(this->value.voidptr); + }; + }; + + class AnyVectorRef { + public: + AnyRef ref; + AnyVectorRef(AnyRef r): ref(r) {} + void push_back(AnyRef &v) { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + typeInfo->vectorOps.push_back(ref, v); + } + size_t size() { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + return typeInfo->vectorOps.size(ref); + } + + void emplace_back() { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + typeInfo->vectorOps.emplace_back(ref); + } + + void clear() { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + typeInfo->vectorOps.clear(ref); + } + + void reserve(size_t n) { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + typeInfo->vectorOps.reserve(ref, n); + } + + + AnyRef at(size_t index) { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + return typeInfo->vectorOps.at(ref, index); + } + }; + + class AnyOptionalRef { + public: + AnyRef ref; + AnyOptionalRef(AnyRef r): ref(r) {} + + AnyRef get() { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + return typeInfo->optionalOps.get(ref); + } + + bool has_value() { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + return typeInfo->optionalOps.has_value(ref); + } + void set(AnyRef &o) { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + typeInfo->optionalOps.set(ref, o); + } + void reset() { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + typeInfo->optionalOps.reset(ref); + } + + void emplaceEmpty() { + auto typeInfo = &reflectTypeInfo[static_cast(this->ref.typeID)]; + typeInfo->optionalOps.emplaceEmpty(ref); + } + + }; + + #endif diff --git a/components/spotify/cspot/protos/spirc.h b/components/spotify/cspot/protos/spirc.h new file mode 100644 index 00000000..334eb2cf --- /dev/null +++ b/components/spotify/cspot/protos/spirc.h @@ -0,0 +1,111 @@ +// THIS CORNFILE IS GENERATED. DO NOT EDIT! 🌽 +#ifndef _SPIRCH +#define _SPIRCH +#include +#include +enum class MessageType { +kMessageTypeHello = 1, +kMessageTypeGoodbye = 2, +kMessageTypeProbe = 3, +kMessageTypeNotify = 10, +kMessageTypeLoad = 20, +kMessageTypePlay = 21, +kMessageTypePause = 22, +kMessageTypePlayPause = 23, +kMessageTypeSeek = 24, +kMessageTypePrev = 25, +kMessageTypeNext = 26, +kMessageTypeVolume = 27, +kMessageTypeShuffle = 28, +kMessageTypeRepeat = 29, +kMessageTypeVolumeDown = 31, +kMessageTypeVolumeUp = 32, +kMessageTypeReplace = 33, +kMessageTypeLogout = 34, +kMessageTypeAction = 35, +}; + +enum class PlayStatus { +kPlayStatusStop = 0, +kPlayStatusPlay = 1, +kPlayStatusPause = 2, +kPlayStatusLoading = 3, +}; + +enum class CapabilityType { +kSupportedContexts = 1, +kCanBePlayer = 2, +kRestrictToLocal = 3, +kDeviceType = 4, +kGaiaEqConnectId = 5, +kSupportsLogout = 6, +kIsObservable = 7, +kVolumeSteps = 8, +kSupportedTypes = 9, +kCommandAcks = 10, +}; + +class TrackRef { +public: +std::optional> gid; +std::optional uri; +std::optional queued; +std::optional context; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassTrackRef; +}; + +class State { +public: +std::optional context_uri; +std::optional index; +std::optional position_ms; +std::optional status; +std::optional position_measured_at; +std::optional context_description; +std::optional shuffle; +std::optional repeat; +std::optional playing_track_index; +std::vector track; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassState; +}; + +class Capability { +public: +std::optional typ; +std::vector intValue; +std::vector stringValue; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassCapability; +}; + +class DeviceState { +public: +std::optional sw_version; +std::optional is_active; +std::optional can_play; +std::optional volume; +std::optional name; +std::optional error_code; +std::optional became_active_at; +std::optional error_message; +std::vector capabilities; +std::vector local_uris; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassDeviceState; +}; + +class Frame { +public: +std::optional version; +std::optional ident; +std::optional protocol_version; +std::optional seq_nr; +std::optional typ; +std::optional device_state; +std::optional state; +std::optional position; +std::optional volume; +std::optional state_update_id; +std::vector recipient; +static constexpr ReflectTypeID _TYPE_ID = ReflectTypeID::ClassFrame; +}; + +#endif diff --git a/components/spotify/cspot/src/ApResolve.cpp b/components/spotify/cspot/src/ApResolve.cpp new file mode 100644 index 00000000..33b47763 --- /dev/null +++ b/components/spotify/cspot/src/ApResolve.cpp @@ -0,0 +1,97 @@ +#include "ApResolve.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Logger.h" +#include + +ApResolve::ApResolve() {} + +std::string ApResolve::getApList() +{ + // hostname lookup + struct hostent *host = gethostbyname("apresolve.spotify.com"); + struct sockaddr_in client; + + if ((host == NULL) || (host->h_addr == NULL)) + { + CSPOT_LOG(error, "apresolve: DNS lookup error"); + throw std::runtime_error("Resolve failed"); + } + + // Prepare socket + bzero(&client, sizeof(client)); + client.sin_family = AF_INET; + client.sin_port = htons(80); + memcpy(&client.sin_addr, host->h_addr, host->h_length); + + int sockFd = socket(AF_INET, SOCK_STREAM, 0); + + // Connect to spotify's server + if (connect(sockFd, (struct sockaddr *)&client, sizeof(client)) < 0) + { + close(sockFd); + CSPOT_LOG(error, "Could not connect to apresolve"); + throw std::runtime_error("Resolve failed"); + } + + // Prepare HTTP get header + std::stringstream ss; + ss << "GET / HTTP/1.1\r\n" + << "Host: apresolve.spotify.com\r\n" + << "Accept: application/json\r\n" + << "Connection: close\r\n" + << "\r\n\r\n"; + + std::string request = ss.str(); + + // Send the request + if (send(sockFd, request.c_str(), request.length(), 0) != (int)request.length()) + { + CSPOT_LOG(error, "apresolve: can't send request"); + throw std::runtime_error("Resolve failed"); + } + + char cur; + + // skip read till json data + while (read(sockFd, &cur, 1) > 0 && cur != '{'); + + auto jsonData = std::string("{"); + + // Read json structure + while (read(sockFd, &cur, 1) > 0) + { + jsonData += cur; + } + + return jsonData; +} + +std::string ApResolve::fetchFirstApAddress() +{ + // Fetch json body + auto jsonData = getApList(); + + // Use cJSON to get first ap address + auto root = cJSON_Parse(jsonData.c_str()); + auto apList = cJSON_GetObjectItemCaseSensitive(root, "ap_list"); + auto firstAp = cJSON_GetArrayItem(apList, 0); + auto data = std::string(firstAp->valuestring); + + // release cjson memory + cJSON_Delete(root); + + return data; +} diff --git a/components/spotify/cspot/src/AudioChunk.cpp b/components/spotify/cspot/src/AudioChunk.cpp new file mode 100644 index 00000000..3df06e67 --- /dev/null +++ b/components/spotify/cspot/src/AudioChunk.cpp @@ -0,0 +1,41 @@ +#include "AudioChunk.h" + +std::vector audioAESIV({0x72, 0xe0, 0x67, 0xfb, 0xdd, 0xcb, 0xcf, 0x77, 0xeb, 0xe8, 0xbc, 0x64, 0x3f, 0x63, 0x0d, 0x93}); + +AudioChunk::AudioChunk(uint16_t seqId, std::vector &audioKey, uint32_t startPosition, uint32_t predictedEndPosition) +{ + this->crypto = std::make_unique(); + this->seqId = seqId; + this->audioKey = audioKey; + this->startPosition = startPosition; + this->endPosition = predictedEndPosition; + this->decryptedData = std::vector(); + this->isHeaderFileSizeLoadedSemaphore = std::make_unique(2); + this->isLoadedSemaphore = std::make_unique(2); +} + +AudioChunk::~AudioChunk() +{ +} + +void AudioChunk::appendData(std::vector &data) +{ + this->decryptedData.insert(this->decryptedData.end(), data.begin(), data.end()); +} + +void AudioChunk::decrypt() +{ + // calculate the IV for right position + auto calculatedIV = this->getIVSum(startPosition / 16); + + crypto->aesCTRXcrypt(this->audioKey, calculatedIV, decryptedData); + + this->startPosition = this->endPosition - this->decryptedData.size(); + this->isLoaded = true; +} + +// Basically just big num addition +std::vector AudioChunk::getIVSum(uint32_t n) +{ + return bigNumAdd(audioAESIV, n); +} diff --git a/components/spotify/cspot/src/AudioChunkManager.cpp b/components/spotify/cspot/src/AudioChunkManager.cpp new file mode 100644 index 00000000..de3d619e --- /dev/null +++ b/components/spotify/cspot/src/AudioChunkManager.cpp @@ -0,0 +1,121 @@ +#include "AudioChunkManager.h" +#include "BellUtils.h" +#include "Logger.h" + +AudioChunkManager::AudioChunkManager() + : bell::Task("AudioChunkManager", 4 * 1024, +0, 0) { + this->chunks = std::vector>(); + startTask(); +} + +std::shared_ptr +AudioChunkManager::registerNewChunk(uint16_t seqId, + std::vector &audioKey, + uint32_t startPos, uint32_t endPos) { + auto chunk = + std::make_shared(seqId, audioKey, startPos * 4, endPos * 4); + this->chunks.push_back(chunk); + CSPOT_LOG(debug, "Chunk requested %d", seqId); + + return chunk; +} +void AudioChunkManager::handleChunkData(std::vector &data, + bool failed) { + auto audioPair = std::pair(data, failed); + audioChunkDataQueue.push(audioPair); +} + +void AudioChunkManager::failAllChunks() { + // Enumerate all the chunks and mark em all failed + for (auto const &chunk : this->chunks) { + if (!chunk->isLoaded) { + chunk->isLoaded = true; + chunk->isFailed = true; + chunk->isHeaderFileSizeLoadedSemaphore->give(); + chunk->isLoadedSemaphore->give(); + } + } +} + +void AudioChunkManager::close() { + this->isRunning = false; + this->failAllChunks(); + this->audioChunkDataQueue.clear(); + std::scoped_lock lock(this->runningMutex); +} + +void AudioChunkManager::runTask() { + this->isRunning = true; + std::scoped_lock lock(this->runningMutex); + while (isRunning) { + std::pair, bool> audioPair; + if (this->audioChunkDataQueue.wtpop(audioPair, 100)) { + auto data = audioPair.first; + auto failed = audioPair.second; + uint16_t seqId = ntohs(extract(data, 0)); + + // Erase all chunks that are not referenced elsewhere anymore + chunks.erase( + std::remove_if(chunks.begin(), chunks.end(), + [](const std::shared_ptr &chunk) { + return chunk.use_count() == 1; + }), + chunks.end()); + + try { + for (auto const &chunk : this->chunks) { + // Found the right chunk + if (chunk != nullptr && chunk->seqId == seqId) { + if (failed) { + // chunk->isFailed = true; + chunk->startPosition = 0; + chunk->endPosition = 0; + chunk->isHeaderFileSizeLoadedSemaphore->give(); + chunk->isLoadedSemaphore->give(); + break; + } + + switch (data.size()) { + case DATA_SIZE_HEADER: { + CSPOT_LOG(debug, "ID: %d: header decrypt!", seqId); + auto headerSize = ntohs(extract(data, 2)); + // Got file size! + chunk->headerFileSize = + ntohl(extract(data, 5)) * 4; + chunk->isHeaderFileSizeLoadedSemaphore->give(); + break; + } + case DATA_SIZE_FOOTER: + if (chunk->endPosition > chunk->headerFileSize) { + chunk->endPosition = chunk->headerFileSize; + } + CSPOT_LOG(debug, "ID: %d: Starting decrypt!", + seqId); + chunk->decrypt(); + chunk->isLoadedSemaphore->give(); + break; + + default: + // printf("ID: %d: Got data chunk!\n", seqId); + // 2 first bytes are size so we skip it + // printf("(_)--- Free memory %d\n", + // esp_get_free_heap_size()); + if (chunk == nullptr) { + return; + } + auto actualData = std::vector( + data.begin() + 2, data.end()); + chunk->appendData(actualData); + break; + } + } + } + + } catch (...) { + } + } else { + } + } + + // Playback finished +} diff --git a/components/spotify/cspot/src/ChunkedAudioStream.cpp b/components/spotify/cspot/src/ChunkedAudioStream.cpp new file mode 100644 index 00000000..4f2c2a8e --- /dev/null +++ b/components/spotify/cspot/src/ChunkedAudioStream.cpp @@ -0,0 +1,331 @@ +#include "ChunkedAudioStream.h" +#include "Logger.h" +#include "BellUtils.h" + +static size_t vorbisReadCb(void *ptr, size_t size, size_t nmemb, ChunkedAudioStream *self) +{ + auto data = self->read(nmemb); + std::copy(data.begin(), data.end(), (char *)ptr); + return data.size(); +} +static int vorbisCloseCb(ChunkedAudioStream *self) +{ + return 0; +} + +static int vorbisSeekCb(ChunkedAudioStream *self, int64_t offset, int whence) +{ + if (whence == 0) + { + offset += SPOTIFY_HEADER_SIZE; + } + + static constexpr std::array seekDirections{ + Whence::START, Whence::CURRENT, Whence::END}; + + self->seek(offset, seekDirections.at(static_cast(whence))); + return 0; +} + +static long vorbisTellCb(ChunkedAudioStream *self) +{ + return static_cast(self->pos); +} + +ChunkedAudioStream::~ChunkedAudioStream() +{ +} + +ChunkedAudioStream::ChunkedAudioStream(std::vector fileId, std::vector audioKey, uint32_t duration, std::shared_ptr manager, uint32_t startPositionMs, bool isPaused) +{ + this->audioKey = audioKey; + this->duration = duration; + this->manager = manager; + this->fileId = fileId; + this->startPositionMs = startPositionMs; + this->isPaused = isPaused; + + auto beginChunk = manager->fetchAudioChunk(fileId, audioKey, 0, 0x4000); + beginChunk->keepInMemory = true; + while(beginChunk->isHeaderFileSizeLoadedSemaphore->twait() != 0); + this->fileSize = beginChunk->headerFileSize; + chunks.push_back(beginChunk); + + // File size is required for this packet to be downloaded + this->fetchTraillingPacket(); + + vorbisFile = { }; + vorbisCallbacks = + { + (decltype(ov_callbacks::read_func)) & vorbisReadCb, + (decltype(ov_callbacks::seek_func)) & vorbisSeekCb, + (decltype(ov_callbacks::close_func)) & vorbisCloseCb, + (decltype(ov_callbacks::tell_func)) & vorbisTellCb, + }; +} + +void ChunkedAudioStream::seekMs(uint32_t positionMs) +{ + + this->seekMutex.lock(); + loadingMeta = true; + ov_time_seek(&vorbisFile, positionMs); + loadingMeta = false; + this->seekMutex.unlock(); + + CSPOT_LOG(debug, "--- Finished seeking!"); +} + +void ChunkedAudioStream::startPlaybackLoop() +{ + + loadingMeta = true; + isRunning = true; + + int32_t r = ov_open_callbacks(this, &vorbisFile, NULL, 0, vorbisCallbacks); + CSPOT_LOG(debug, "--- Loaded file"); + if (this->startPositionMs != 0) + { + ov_time_seek(&vorbisFile, startPositionMs); + } + else + { + this->requestChunk(0); + } + + loadingMeta = false; + bool eof = false; + while (!eof && isRunning) + { + if (!isPaused) + { + std::vector pcmOut(4096 / 4); + + this->seekMutex.lock(); + long ret = ov_read(&vorbisFile, (char *)&pcmOut[0], 4096 / 4, ¤tSection); + this->seekMutex.unlock(); + if (ret == 0) + { + // and done :) + eof = true; + } + else if (ret < 0) + { + CSPOT_LOG(error, "An error has occured in the stream"); + + // Error in the stream + } + else + { + // Write the actual data + auto data = std::vector(pcmOut.begin(), pcmOut.begin() + ret); + pcmCallback(data); + // audioSink->feedPCMFrames(data); + } + } + else + { + BELL_SLEEP_MS(100); + } + } + + ov_clear(&vorbisFile); + vorbisCallbacks = {}; + CSPOT_LOG(debug, "Track finished"); + finished = true; + + if (eof) + { + this->streamFinishedCallback(); + } +} + +void ChunkedAudioStream::fetchTraillingPacket() +{ + auto startPosition = (this->fileSize / 4) - 0x1000; + + // AES block size is 16, so the index must be divisible by it + while ((startPosition * 4) % 16 != 0) + startPosition++; // ik, ugly lol + + auto endChunk = manager->fetchAudioChunk(fileId, audioKey, startPosition, fileSize / 4); + endChunk->keepInMemory = true; + + chunks.push_back(endChunk); + while (endChunk->isLoadedSemaphore->twait() != 0); +} + +std::vector ChunkedAudioStream::read(size_t bytes) +{ + auto toRead = bytes; + auto res = std::vector(); +READ: + while (res.size() < bytes) + { + auto position = pos; + auto isLoadingMeta = loadingMeta; + + // Erase all chunks not close to current position + chunks.erase(std::remove_if( + chunks.begin(), chunks.end(), + [position, &isLoadingMeta](const std::shared_ptr &chunk) { + if (isLoadingMeta) { + return false; + } + + if (chunk->keepInMemory) + { + return false; + } + + if (chunk->isFailed) + { + return true; + } + + if (chunk->endPosition < position || chunk->startPosition > position + BUFFER_SIZE) + { + return true; + } + + return false; + }), + chunks.end()); + + int16_t chunkIndex = this->pos / AUDIO_CHUNK_SIZE; + int32_t offset = this->pos % AUDIO_CHUNK_SIZE; + + if (pos >= fileSize) + { + + CSPOT_LOG(debug, "EOL!"); + return res; + } + + auto chunk = findChunkForPosition(pos); + + if (chunk != nullptr) + { + auto offset = pos - chunk->startPosition; + if (chunk->isLoaded) + { + if (chunk->decryptedData.size() - offset >= toRead) + { + if((chunk->decryptedData.begin() + offset) < chunk->decryptedData.end()) { + res.insert(res.end(), chunk->decryptedData.begin() + offset, + chunk->decryptedData.begin() + offset + toRead); + this->pos += toRead; + } else { + chunk->decrypt(); + } + } + else + { + res.insert(res.end(), chunk->decryptedData.begin() + offset, chunk->decryptedData.end()); + this->pos += chunk->decryptedData.size() - offset; + toRead -= chunk->decryptedData.size() - offset; + } + } + else + { + CSPOT_LOG(debug, "Waiting for chunk to load"); + while (chunk->isLoadedSemaphore->twait() != 0); + if (chunk->isFailed) + { + auto requestChunk = this->requestChunk(chunkIndex); + while (requestChunk->isLoadedSemaphore->twait() != 0); + goto READ; + } + } + } + else + { + CSPOT_LOG(debug, "Actual request %d", chunkIndex); + this->requestChunk(chunkIndex); + } + } + + if (!loadingMeta) + { + + auto requestedOffset = 0; + + while (requestedOffset < BUFFER_SIZE) + { + auto chunk = findChunkForPosition(pos + requestedOffset); + + if (chunk != nullptr) + { + requestedOffset = chunk->endPosition - pos; + + // Don not buffer over EOL - unnecessary "failed chunks" + if ((pos + requestedOffset) >= fileSize) + { + break; + } + } + + else + { + auto chunkReq = manager->fetchAudioChunk(fileId, audioKey, (pos + requestedOffset) / 4, (pos + requestedOffset + AUDIO_CHUNK_SIZE) / 4); + CSPOT_LOG(debug, "Chunk req end pos %d", chunkReq->endPosition); + this->chunks.push_back(chunkReq); + } + } + } + return res; +} + +std::shared_ptr ChunkedAudioStream::findChunkForPosition(size_t position) +{ + for (int i = 0; i < this->chunks.size(); i++) + { + auto chunk = this->chunks[i]; + if (chunk->startPosition <= position && chunk->endPosition > position) + { + return chunk; + } + } + + return nullptr; +} + +void ChunkedAudioStream::seek(size_t dpos, Whence whence) +{ + switch (whence) + { + case Whence::START: + this->pos = dpos; + break; + case Whence::CURRENT: + this->pos += dpos; + break; + case Whence::END: + this->pos = fileSize + dpos; + break; + } + + auto currentChunk = this->pos / AUDIO_CHUNK_SIZE; + + if (findChunkForPosition(this->pos) == nullptr) + { + // Seeking might look back - therefore we preload some past data + auto startPosition = (this->pos / 4) - (AUDIO_CHUNK_SIZE / 4); + + // AES block size is 16, so the index must be divisible by it + while ((startPosition * 4) % 16 != 0) + startPosition++; // ik, ugly lol + + this->chunks.push_back(manager->fetchAudioChunk(fileId, audioKey, startPosition, startPosition + (AUDIO_CHUNK_SIZE / 4))); + } + CSPOT_LOG(debug, "Change in current chunk %d", currentChunk); +} + +std::shared_ptr ChunkedAudioStream::requestChunk(size_t chunkIndex) +{ + + CSPOT_LOG(debug, "Chunk Req %d", chunkIndex); + auto chunk = manager->fetchAudioChunk(fileId, audioKey, chunkIndex); + this->chunks.push_back(chunk); + return chunk; +} diff --git a/components/spotify/cspot/src/ConfigJSON.cpp b/components/spotify/cspot/src/ConfigJSON.cpp new file mode 100644 index 00000000..84df5a3c --- /dev/null +++ b/components/spotify/cspot/src/ConfigJSON.cpp @@ -0,0 +1,94 @@ +#include "ConfigJSON.h" +#include "JSONObject.h" +#include "Logger.h" +#include "ConstantParameters.h" + + +ConfigJSON::ConfigJSON(std::string jsonFileName, std::shared_ptr file) +{ + _file = file; + _jsonFileName = jsonFileName; +} + +bool ConfigJSON::load() +{ + // Config filename check + if(_jsonFileName.length() > 0) + { + std::string jsonConfig; + _file->readFile(_jsonFileName, jsonConfig); + + // Ignore config if empty + if(jsonConfig.length() > 0) + { + auto root = cJSON_Parse(jsonConfig.c_str()); + + if(cJSON_HasObjectItem(root, "deviceName")) + { + auto deviceNameObject = cJSON_GetObjectItemCaseSensitive(root, "deviceName"); + this->deviceName = std::string(cJSON_GetStringValue(deviceNameObject)); + } + if(cJSON_HasObjectItem(root, "bitrate")) + { + auto bitrateObject = cJSON_GetObjectItemCaseSensitive(root, "bitrate"); + switch((uint16_t)cJSON_GetNumberValue(bitrateObject)){ + case 320: + this->format = AudioFormat::OGG_VORBIS_320; + break; + case 160: + this->format = AudioFormat::OGG_VORBIS_160; + break; + case 96: + this->format = AudioFormat::OGG_VORBIS_96; + break; + default: + this->format = AudioFormat::OGG_VORBIS_320; + break; + } + } + if(cJSON_HasObjectItem(root, "volume")) + { + auto volumeObject = cJSON_GetObjectItemCaseSensitive(root, "volume"); + this->volume = cJSON_GetNumberValue(volumeObject); + } + cJSON_Delete(root); + } + else + { + // Config file not found or invalid + // Set default values + this->volume = 32767; + this->deviceName = defaultDeviceName; + this->format = AudioFormat::OGG_VORBIS_160; + } + return true; + } + else + { + return false; + } +} + +bool ConfigJSON::save() +{ + bell::JSONObject obj; + + obj["volume"] = this->volume; + obj["deviceName"] = this->deviceName; + switch(this->format){ + case AudioFormat::OGG_VORBIS_320: + obj["bitrate"] = 320; + break; + case AudioFormat::OGG_VORBIS_160: + obj["bitrate"] = 160; + break; + case AudioFormat::OGG_VORBIS_96: + obj["bitrate"] = 96; + break; + default: + obj["bitrate"] = 160; + break; + } + + return _file->writeFile(_jsonFileName, obj.toString()); +} diff --git a/components/spotify/cspot/src/LoginBlob.cpp b/components/spotify/cspot/src/LoginBlob.cpp new file mode 100644 index 00000000..1aa6f33f --- /dev/null +++ b/components/spotify/cspot/src/LoginBlob.cpp @@ -0,0 +1,133 @@ +#include "LoginBlob.h" +#include "JSONObject.h" +#include "Logger.h" + +LoginBlob::LoginBlob() +{ + this->crypto = std::make_unique(); +} + +std::vector LoginBlob::decodeBlob(const std::vector &blob, const std::vector &sharedKey) +{ + // 0:16 - iv; 17:-20 - blob; -20:0 - checksum + auto iv = std::vector(blob.begin(), blob.begin() + 16); + auto encrypted = std::vector(blob.begin() + 16, blob.end() - 20); + auto checksum = std::vector(blob.end() - 20, blob.end()); + + // baseKey = sha1(sharedKey) 0:16 + crypto->sha1Init(); + crypto->sha1Update(sharedKey); + auto baseKey = crypto->sha1FinalBytes(); + baseKey = std::vector(baseKey.begin(), baseKey.begin() + 16); + + auto checksumMessage = std::string("checksum"); + auto checksumKey = crypto->sha1HMAC(baseKey, std::vector(checksumMessage.begin(), checksumMessage.end())); + + auto encryptionMessage = std::string("encryption"); + auto encryptionKey = crypto->sha1HMAC(baseKey, std::vector(encryptionMessage.begin(), encryptionMessage.end())); + + auto mac = crypto->sha1HMAC(checksumKey, encrypted); + + // Check checksum + if (mac != checksum) + { + CSPOT_LOG(error, "Mac doesn't match!" ); + } + + encryptionKey = std::vector(encryptionKey.begin(), encryptionKey.begin() + 16); + crypto->aesCTRXcrypt(encryptionKey, iv, encrypted); + + return encrypted; +} + +uint32_t LoginBlob::readBlobInt(const std::vector &data) +{ + auto lo = data[blobSkipPosition]; + if ((int)(lo & 0x80) == 0) + { + this->blobSkipPosition += 1; + return lo; + } + + auto hi = data[blobSkipPosition + 1]; + this->blobSkipPosition += 2; + + return (uint32_t)((lo & 0x7f) | (hi << 7)); +} + +std::vector LoginBlob::decodeBlobSecondary(const std::vector &blob, const std::string &username, const std::string &deviceId) +{ + auto encryptedString = std::string(blob.begin(), blob.end()); + auto blobData = crypto->base64Decode(encryptedString); + + crypto->sha1Init(); + crypto->sha1Update(std::vector(deviceId.begin(), deviceId.end())); + auto secret = crypto->sha1FinalBytes(); + auto pkBaseKey = crypto->pbkdf2HmacSha1(secret, std::vector(username.begin(), username.end()), 256, 20); + + crypto->sha1Init(); + crypto->sha1Update(pkBaseKey); + auto key = std::vector({0x00, 0x00, 0x00, 0x14}); // len of base key + auto baseKeyHashed = crypto->sha1FinalBytes(); + key.insert(key.begin(), baseKeyHashed.begin(), baseKeyHashed.end()); + + crypto->aesECBdecrypt(key, blobData); + + auto l = blobData.size(); + + for (int i = 0; i < l - 16; i++) + { + blobData[l - i - 1] ^= blobData[l - i - 17]; + } + + return blobData; +} + +void LoginBlob::loadZeroconf(const std::vector &blob, const std::vector &sharedKey, const std::string &deviceId, const std::string &username) +{ + auto partDecoded = this->decodeBlob(blob, sharedKey); + auto loginData = this->decodeBlobSecondary(partDecoded, username, deviceId); + + // Parse blob + blobSkipPosition = 1; + blobSkipPosition += readBlobInt(loginData); + blobSkipPosition += 1; + this->authType = readBlobInt(loginData); + blobSkipPosition += 1; + auto authSize = readBlobInt(loginData); + this->username = username; + this->authData = std::vector(loginData.begin() + blobSkipPosition, loginData.begin() + blobSkipPosition + authSize); +} + +void LoginBlob::loadUserPass(const std::string &username, const std::string &password) +{ + this->username = username; + this->authData = std::vector(password.begin(), password.end()); + this->authType = static_cast(AuthenticationType::AUTHENTICATION_USER_PASS); +} + +void LoginBlob::loadJson(const std::string &json) +{ + auto root = cJSON_Parse(json.c_str()); + auto authTypeObject = cJSON_GetObjectItemCaseSensitive(root, "authType"); + auto usernameObject = cJSON_GetObjectItemCaseSensitive(root, "username"); + auto authDataObject = cJSON_GetObjectItemCaseSensitive(root, "authData"); + + auto authDataString = std::string(cJSON_GetStringValue(authDataObject)); + this->authData = crypto->base64Decode(authDataString); + + this->username = std::string(cJSON_GetStringValue(usernameObject)); + this->authType = cJSON_GetNumberValue(authTypeObject); + + cJSON_Delete(root); +} + +std::string LoginBlob::toJson() +{ + bell::JSONObject obj; + obj["authData"] = crypto->base64Encode(authData); + obj["authType"] = this->authType; + obj["username"] = this->username; + + return obj.toString(); +} diff --git a/components/spotify/cspot/src/MercuryManager.cpp b/components/spotify/cspot/src/MercuryManager.cpp new file mode 100644 index 00000000..df99140d --- /dev/null +++ b/components/spotify/cspot/src/MercuryManager.cpp @@ -0,0 +1,356 @@ +#include "MercuryManager.h" +#include +#include "Logger.h" + +std::map MercuryTypeMap({ + {MercuryType::GET, "GET"}, + {MercuryType::SEND, "SEND"}, + {MercuryType::SUB, "SUB"}, + {MercuryType::UNSUB, "UNSUB"}, + }); + +MercuryManager::MercuryManager(std::unique_ptr session): bell::Task("mercuryManager", 4 * 1024, +1, 1) +{ + this->timeProvider = std::make_shared(); + this->callbacks = std::map(); + this->subscriptions = std::map(); + this->session = std::move(session); + this->sequenceId = 0x00000001; + this->audioChunkManager = std::make_unique(); + this->audioChunkSequence = 0; + this->audioKeySequence = 0; + this->queue = std::vector>(); + queueSemaphore = std::make_unique(200); + + this->session->shanConn->conn->timeoutHandler = [this]() { + return this->timeoutHandler(); + }; +} + +bool MercuryManager::timeoutHandler() +{ + auto currentTimestamp = timeProvider->getSyncedTimestamp(); + + if (this->lastRequestTimestamp != -1 && currentTimestamp - this->lastRequestTimestamp > AUDIOCHUNK_TIMEOUT_MS) + { + CSPOT_LOG(debug, "Reconnection required, no mercury response"); + return true; + } + + if (currentTimestamp - this->lastPingTimestamp > PING_TIMEOUT_MS) + { + CSPOT_LOG(debug, "Reconnection required, no ping received"); + return true; + } + return false; +} + +void MercuryManager::unregisterMercuryCallback(uint64_t seqId) +{ + auto element = this->callbacks.find(seqId); + if (element != this->callbacks.end()) + { + this->callbacks.erase(element); + } +} + +void MercuryManager::requestAudioKey(std::vector trackId, std::vector fileId, audioKeyCallback& audioCallback) +{ + std::lock_guard guard(reconnectionMutex); + auto buffer = fileId; + this->keyCallback = audioCallback; + // Structure: [FILEID] [TRACKID] [4 BYTES SEQUENCE ID] [0x00, 0x00] + buffer.insert(buffer.end(), trackId.begin(), trackId.end()); + auto audioKeySequence = pack(htonl(this->audioKeySequence)); + buffer.insert(buffer.end(), audioKeySequence.begin(), audioKeySequence.end()); + auto suffix = std::vector({ 0x00, 0x00 }); + buffer.insert(buffer.end(), suffix.begin(), suffix.end()); + + // Bump audio key sequence + this->audioKeySequence += 1; + + // Used for broken connection detection + this->lastRequestTimestamp = timeProvider->getSyncedTimestamp(); + this->session->shanConn->sendPacket(static_cast(MercuryType::AUDIO_KEY_REQUEST_COMMAND), buffer); +} + +void MercuryManager::freeAudioKeyCallback() +{ + this->keyCallback = nullptr; +} + +std::shared_ptr MercuryManager::fetchAudioChunk(std::vector fileId, std::vector& audioKey, uint16_t index) +{ + return this->fetchAudioChunk(fileId, audioKey, index * AUDIO_CHUNK_SIZE / 4, (index + 1) * AUDIO_CHUNK_SIZE / 4); +} + +std::shared_ptr MercuryManager::fetchAudioChunk(std::vector fileId, std::vector& audioKey, uint32_t startPos, uint32_t endPos) +{ + std::lock_guard guard(reconnectionMutex); + auto sampleStartBytes = pack(htonl(startPos)); + auto sampleEndBytes = pack(htonl(endPos)); + + auto buffer = pack(htons(this->audioChunkSequence)); + auto hardcodedData = std::vector( + { 0x00, 0x01, // Channel num, currently just hardcoded to 1 + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, // bytes magic + 0x00, 0x00, 0x9C, 0x40, + 0x00, 0x02, 0x00, 0x00 }); + buffer.insert(buffer.end(), hardcodedData.begin(), hardcodedData.end()); + buffer.insert(buffer.end(), fileId.begin(), fileId.end()); + buffer.insert(buffer.end(), sampleStartBytes.begin(), sampleStartBytes.end()); + buffer.insert(buffer.end(), sampleEndBytes.begin(), sampleEndBytes.end()); + + // Bump chunk sequence + this->audioChunkSequence += 1; + this->session->shanConn->sendPacket(static_cast(MercuryType::AUDIO_CHUNK_REQUEST_COMMAND), buffer); + + // Used for broken connection detection + this->lastRequestTimestamp = this->timeProvider->getSyncedTimestamp(); + return this->audioChunkManager->registerNewChunk(this->audioChunkSequence - 1, audioKey, startPos, endPos); +} + +void MercuryManager::reconnect() +{ + std::lock_guard guard(this->reconnectionMutex); + this->lastPingTimestamp = -1; + this->lastRequestTimestamp = -1; +RECONNECT: + if (!isRunning) return; + CSPOT_LOG(debug, "Trying to reconnect..."); + try + { + if (this->session->shanConn->conn != nullptr) + { + this->session->shanConn->conn->timeoutHandler = nullptr; + } + this->audioChunkManager->failAllChunks(); + if (this->session->authBlob != nullptr) + { + this->lastAuthBlob = this->session->authBlob; + } + this->session = std::make_unique(); + this->session->connectWithRandomAp(); + this->session->authenticate(this->lastAuthBlob); + this->session->shanConn->conn->timeoutHandler = [this]() { + return this->timeoutHandler(); + }; + CSPOT_LOG(debug, "Reconnected successfuly :)"); + } + catch (...) + { + CSPOT_LOG(debug, "Reconnection failed, willl retry in %d secs", RECONNECTION_RETRY_MS / 1000); + usleep(RECONNECTION_RETRY_MS * 1000); + goto RECONNECT; + //reconnect(); + } +} + +void MercuryManager::runTask() +{ + std::scoped_lock(this->runningMutex); + // Listen for mercury replies and handle them accordingly + isRunning = true; + while (isRunning) + { + std::unique_ptr packet; + try + { + packet = this->session->shanConn->recvPacket(); + } + catch (const std::runtime_error& e) + { + // Reconnection required + this->reconnect(); + this->reconnectedCallback(); + continue; + } + if (static_cast(packet->command) == MercuryType::PING) // @TODO: Handle time synchronization through ping + { + CSPOT_LOG(debug, "Got ping, syncing timestamp"); + this->timeProvider->syncWithPingPacket(packet->data); + + this->lastPingTimestamp = this->timeProvider->getSyncedTimestamp(); + this->session->shanConn->sendPacket(0x49, packet->data); + } + else if (static_cast(packet->command) == MercuryType::AUDIO_CHUNK_SUCCESS_RESPONSE) + { + this->lastRequestTimestamp = -1; + this->audioChunkManager->handleChunkData(packet->data, false); + } + else + { + this->queue.push_back(std::move(packet)); + this->queueSemaphore->give(); + } + } +} + +void MercuryManager::stop() { + CSPOT_LOG(debug, "Stopping mercury manager"); + isRunning = false; + audioChunkManager->close(); + std::scoped_lock(audioChunkManager->runningMutex, this->runningMutex); + this->session->close(); + CSPOT_LOG(debug, "mercury stopped"); +} + +void MercuryManager::updateQueue() { + if (queueSemaphore->twait() == 0) { + if (this->queue.size() > 0) + { + auto packet = std::move(this->queue[0]); + this->queue.erase(this->queue.begin()); + CSPOT_LOG(debug, "Received packet with code %d of length %d", packet->command, packet->data.size()); + switch (static_cast(packet->command)) + { + case MercuryType::COUNTRY_CODE_RESPONSE: + { + + countryCode = std::string(packet->data.begin(), packet->data.end()); + CSPOT_LOG(debug, "Received country code: %s", countryCode.c_str()); + break; + } + case MercuryType::AUDIO_KEY_FAILURE_RESPONSE: + case MercuryType::AUDIO_KEY_SUCCESS_RESPONSE: + { + this->lastRequestTimestamp = -1; + + // First four bytes mark the sequence id + auto seqId = ntohl(extract(packet->data, 0)); + if (seqId == (this->audioKeySequence - 1) && this->keyCallback != nullptr) + { + auto success = static_cast(packet->command) == MercuryType::AUDIO_KEY_SUCCESS_RESPONSE; + this->keyCallback(success, packet->data); + } + break; + } + case MercuryType::AUDIO_CHUNK_FAILURE_RESPONSE: + { + CSPOT_LOG(error, "Audio Chunk failure!"); + this->audioChunkManager->handleChunkData(packet->data, true); + this->lastRequestTimestamp = -1; + break; + } + case MercuryType::SEND: + case MercuryType::SUB: + case MercuryType::UNSUB: + { + auto response = std::make_unique(packet->data); + if (response->parts.size() > 0) + { + CSPOT_LOG(debug, " MercuryType::UNSUB response->parts[0].size() = %d", response->parts[0].size()); + } + if (this->callbacks.count(response->sequenceId) > 0) + { + auto seqId = response->sequenceId; + this->callbacks[response->sequenceId](std::move(response)); + this->callbacks.erase(this->callbacks.find(seqId)); + } + break; + } + case MercuryType::SUBRES: + { + auto response = std::make_unique(packet->data); + + if (this->subscriptions.count(response->mercuryHeader.uri.value()) > 0) + { + this->subscriptions[response->mercuryHeader.uri.value()](std::move(response)); + //this->subscriptions.erase(std::string(response->mercuryHeader.uri)); + } + break; + } + default: + break; + } + } + } +} + +void MercuryManager::handleQueue() +{ + while (isRunning) + { + this->updateQueue(); + } +} + +uint64_t MercuryManager::execute(MercuryType method, std::string uri, mercuryCallback& callback, mercuryCallback& subscription, mercuryParts& payload) +{ + if (!isRunning) return -1; + std::lock_guard guard(reconnectionMutex); + // Construct mercury header + + CSPOT_LOG(debug, "executing MercuryType %s", MercuryTypeMap[method].c_str()); + Header mercuryHeader; + mercuryHeader.uri = uri; + mercuryHeader.method = MercuryTypeMap[method]; + + // GET and SEND are actually the same. Therefore the override + // The difference between them is only in header's method + if (method == MercuryType::GET) + { + method = MercuryType::SEND; + } + + auto headerBytes = encodePb(mercuryHeader); + + // Register a subscription when given method is called + if (method == MercuryType::SUB) + { + this->subscriptions.insert({ uri, subscription }); + } + + this->callbacks.insert({ sequenceId, callback }); + + // Structure: [Sequence size] [SequenceId] [0x1] [Payloads number] + // [Header size] [Header] [Payloads (size + data)] + + // Pack sequenceId + auto sequenceIdBytes = pack(hton64(this->sequenceId)); + auto sequenceSizeBytes = pack(htons(sequenceIdBytes.size())); + + sequenceIdBytes.insert(sequenceIdBytes.begin(), sequenceSizeBytes.begin(), sequenceSizeBytes.end()); + sequenceIdBytes.push_back(0x01); + + auto payloadNum = pack(htons(payload.size() + 1)); + sequenceIdBytes.insert(sequenceIdBytes.end(), payloadNum.begin(), payloadNum.end()); + + auto headerSizePayload = pack(htons(headerBytes.size())); + sequenceIdBytes.insert(sequenceIdBytes.end(), headerSizePayload.begin(), headerSizePayload.end()); + sequenceIdBytes.insert(sequenceIdBytes.end(), headerBytes.begin(), headerBytes.end()); + + // Encode all the payload parts + for (int x = 0; x < payload.size(); x++) + { + headerSizePayload = pack(htons(payload[x].size())); + sequenceIdBytes.insert(sequenceIdBytes.end(), headerSizePayload.begin(), headerSizePayload.end()); + sequenceIdBytes.insert(sequenceIdBytes.end(), payload[x].begin(), payload[x].end()); + } + + // Bump sequence id + this->sequenceId += 1; + + this->session->shanConn->sendPacket(static_cast::type>(method), sequenceIdBytes); + + return this->sequenceId - 1; +} + +uint64_t MercuryManager::execute(MercuryType method, std::string uri, mercuryCallback& callback, mercuryParts& payload) +{ + mercuryCallback subscription = nullptr; + return this->execute(method, uri, callback, subscription, payload); +} + +uint64_t MercuryManager::execute(MercuryType method, std::string uri, mercuryCallback& callback, mercuryCallback& subscription) +{ + auto payload = mercuryParts(0); + return this->execute(method, uri, callback, subscription, payload); +} + +uint64_t MercuryManager::execute(MercuryType method, std::string uri, mercuryCallback& callback) +{ + auto payload = mercuryParts(0); + return this->execute(method, uri, callback, payload); +} diff --git a/components/spotify/cspot/src/MercuryResponse.cpp b/components/spotify/cspot/src/MercuryResponse.cpp new file mode 100644 index 00000000..2f0664ed --- /dev/null +++ b/components/spotify/cspot/src/MercuryResponse.cpp @@ -0,0 +1,33 @@ +#include "MercuryResponse.h" + +MercuryResponse::MercuryResponse(std::vector &data) +{ + // this->mercuryHeader = std::make_unique
    (); + this->parts = mercuryParts(0); + this->parseResponse(data); +} + +void MercuryResponse::parseResponse(std::vector &data) +{ + auto sequenceLength = ntohs(extract(data, 0)); + this->sequenceId = hton64(extract(data, 2)); + + auto partsNumber = ntohs(extract(data, 11)); + + auto headerSize = ntohs(extract(data, 13)); + auto headerBytes = std::vector(data.begin() + 15, data.begin() + 15 + headerSize); + + auto pos = 15 + headerSize; + while (pos < data.size()) + { + auto partSize = ntohs(extract(data, pos)); + + this->parts.push_back( + std::vector( + data.begin() + pos + 2, + data.begin() + pos + 2 + partSize)); + pos += 2 + partSize; + } + + this->mercuryHeader = decodePb
    (headerBytes); +} \ No newline at end of file diff --git a/components/spotify/cspot/src/Packet.cpp b/components/spotify/cspot/src/Packet.cpp new file mode 100644 index 00000000..534e7cba --- /dev/null +++ b/components/spotify/cspot/src/Packet.cpp @@ -0,0 +1,6 @@ +#include "Packet.h" + +Packet::Packet(uint8_t command, std::vector &data) { + this->command = command; + this->data = data; +}; \ No newline at end of file diff --git a/components/spotify/cspot/src/PbReader.cpp b/components/spotify/cspot/src/PbReader.cpp new file mode 100644 index 00000000..1a2f10ca --- /dev/null +++ b/components/spotify/cspot/src/PbReader.cpp @@ -0,0 +1,115 @@ +#include "PbReader.h" +#include + +PbReader::PbReader(std::vector const &rawData) : rawData(rawData) +{ + maxPosition = rawData.size(); +} + +template +T PbReader::decodeVarInt() +{ + uint8_t byte; + uint_fast8_t bitpos = 0; + uint64_t storago = 0; + do + { + byte = this->rawData[pos]; + pos++; + + storago |= (uint64_t)(byte & 0x7F) << bitpos; + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + return static_cast(storago); +} + +template +T PbReader::decodeFixed() +{ + pos += sizeof(T); + return *(T*)(&this->rawData[pos - sizeof(T)]); +} + + +template int32_t PbReader::decodeFixed(); +template int64_t PbReader::decodeFixed(); + +template uint32_t PbReader::decodeVarInt(); +template int64_t PbReader::decodeVarInt(); +template bool PbReader::decodeVarInt(); + +void PbReader::resetMaxPosition() +{ + maxPosition = rawData.size(); +} + +void PbReader::decodeString(std::string &target) +{ + nextFieldLength = decodeVarInt(); + target.resize(nextFieldLength); + // std::cout << "rawData.size() = " << rawData.size() << " pos = " << pos << " nextFieldLength =" << nextFieldLength; + // printf("\n%d, \n", currentTag); + // if (pos + nextFieldLength >= rawData.size()) + // { + // std::cout << " \nBAD -- pos + nextFieldLength >= rawData.size() MSVC IS LITERLALLY SHAKING AND CRYING RN"; + // } + // std::cout << std::endl; + std::copy(rawData.begin() + pos, rawData.begin() + pos + nextFieldLength, target.begin()); + pos += nextFieldLength; +} + +void PbReader::decodeVector(std::vector &target) +{ + nextFieldLength = decodeVarInt(); + target.resize(nextFieldLength); + std::copy(rawData.begin() + pos, rawData.begin() + pos + nextFieldLength, target.begin()); + pos += nextFieldLength; +} + +bool PbReader::next() +{ + if (pos >= maxPosition) + return false; + + currentWireValue = decodeVarInt(); + currentTag = currentWireValue >> 3U; + currentWireType = PbWireType(currentWireValue & 0x07U); + return true; +} + +int64_t PbReader::decodeZigzag(uint64_t value) +{ + return static_cast((value >> 1U) ^ static_cast(-static_cast(value & 1U))); +} + +template +T PbReader::decodeSVarInt() +{ + skipVarIntDump = decodeVarInt(); + return static_cast(decodeZigzag(skipVarIntDump)); +} + +template int32_t PbReader::decodeSVarInt(); +template int64_t PbReader::decodeSVarInt(); + +void PbReader::skip() +{ + switch (currentWireType) + { + case PbWireType::varint: + skipVarIntDump = decodeVarInt(); + break; + case PbWireType::fixed64: + pos += 8; + break; + case PbWireType::length_delimited: + nextFieldLength = decodeVarInt(); + pos += nextFieldLength; + break; + case PbWireType::fixed32: + pos += 4; + break; + default: + break; + } +} diff --git a/components/spotify/cspot/src/PbWriter.cpp b/components/spotify/cspot/src/PbWriter.cpp new file mode 100644 index 00000000..b21a80cc --- /dev/null +++ b/components/spotify/cspot/src/PbWriter.cpp @@ -0,0 +1,142 @@ +#include "PbWriter.h" + +PbWriter::PbWriter(std::vector &rawData) : rawData(rawData) +{ +} + +void PbWriter::encodeVarInt(uint32_t low, uint32_t high, int32_t indexOffset) +{ + size_t i = 0; + uint8_t byte = (uint8_t)(low & 0x7F); + low >>= 7; + + while (i < 4 && (low != 0 || high != 0)) + { + byte |= 0x80; + rawData.insert(rawData.end() + indexOffset, byte); + i++; + byte = (uint8_t)(low & 0x7F); + low >>= 7; + } + + if (high) + { + byte = (uint8_t)(byte | ((high & 0x07) << 4)); + high >>= 3; + + while (high) + { + byte |= 0x80; + rawData.insert(rawData.end() + indexOffset, byte); + i++; + byte = (uint8_t)(high & 0x7F); + high >>= 7; + } + } + + rawData.insert(rawData.end() + indexOffset, byte); +} + +template void PbWriter::encodeVarInt(uint8_t, int32_t); +template void PbWriter::encodeVarInt(uint32_t, int32_t); +template void PbWriter::encodeVarInt(uint64_t, int32_t); +template void PbWriter::encodeVarInt(long long, int32_t); + +template +void PbWriter::encodeVarInt(T data, int32_t offset) +{ + auto value = static_cast(data); + if (value <= 0x7F) + { + rawData.insert(rawData.end() + offset, (uint8_t)value); + } + else + { + encodeVarInt((uint32_t)value, (uint32_t)(value >> 32), offset); + } +} + +uint32_t PbWriter::encodeZigzag32(int32_t value) { + return (static_cast(value) << 1U) ^ static_cast(-static_cast(static_cast(value) >> 31U)); +} + +uint64_t PbWriter::encodeZigzag64(int64_t value) { + return (static_cast(value) << 1U) ^ static_cast(-static_cast(static_cast(value) >> 63U)); +} + +void PbWriter::addSVarInt32(uint32_t tag, int32_t data) { + auto val = encodeZigzag32(data); + addVarInt(tag, val); +} + +template +void PbWriter::encodeFixed(T data) { + auto val = reinterpret_cast(&data); + rawData.insert(rawData.end(), val, val + sizeof(T)); +} + +template void PbWriter::encodeFixed(int64_t); +template void PbWriter::encodeFixed(int32_t); + +void PbWriter::addSVarInt64(uint32_t tag, int64_t data) { + auto val = encodeZigzag64(data); + addVarInt(tag, val); +} + +void PbWriter::addString(uint32_t tag, std::string &target) +{ + addField(tag, PbWireType::length_delimited); + uint32_t stringSize = target.size(); + encodeVarInt(stringSize); + + rawData.insert(rawData.end(), target.begin(), target.end()); +} + +void PbWriter::addVector(uint32_t tag, std::vector &target) +{ + addField(tag, PbWireType::length_delimited); + uint32_t vectorSize = target.size(); + encodeVarInt(vectorSize); + + rawData.insert(rawData.end(), target.begin(), target.end()); +} + +template +void PbWriter::addVarInt(uint32_t tag, T intType) +{ + addField(tag, PbWireType::varint); + encodeVarInt(intType); +} + +void PbWriter::addBool(uint32_t tag, bool value) +{ + addField(tag, PbWireType::varint); + rawData.push_back(char(value)); +} + +template void PbWriter::addVarInt(uint32_t, uint8_t); +template void PbWriter::addVarInt(uint32_t, uint32_t); +template void PbWriter::addVarInt(uint32_t, uint64_t); +template void PbWriter::addVarInt(uint32_t, int64_t); +template void PbWriter::addVarInt(uint32_t, bool); + +void PbWriter::addField(uint32_t tag, PbWireType wiretype) +{ + const uint32_t value = (tag << 3U) | uint32_t(wiretype); + encodeVarInt(value); +} + +uint32_t PbWriter::startMessage() +{ + return rawData.size(); +} + +void PbWriter::finishMessage(uint32_t tag, uint32_t lastMessagePosition) +{ + uint32_t finalMessageSize = rawData.size() - lastMessagePosition; + uint32_t msgHeader = (tag << 3U) | uint32_t(PbWireType::length_delimited); + + int32_t offset = -finalMessageSize; + encodeVarInt(msgHeader, offset); + encodeVarInt(finalMessageSize, offset); +} diff --git a/components/spotify/cspot/src/PlainConnection.cpp b/components/spotify/cspot/src/PlainConnection.cpp new file mode 100644 index 00000000..76be44a1 --- /dev/null +++ b/components/spotify/cspot/src/PlainConnection.cpp @@ -0,0 +1,170 @@ + +#include "PlainConnection.h" +#include +#include +#include +#include "Logger.h" + +PlainConnection::PlainConnection(){}; + +PlainConnection::~PlainConnection() +{ + closeSocket(); +}; + +void PlainConnection::connectToAp(std::string apAddress) +{ + struct addrinfo h, *airoot, *ai; + std::string hostname = apAddress.substr(0, apAddress.find(":")); + std::string portStr = apAddress.substr(apAddress.find(":") + 1, apAddress.size()); + memset(&h, 0, sizeof(h)); + h.ai_family = AF_INET; + h.ai_socktype = SOCK_STREAM; + h.ai_protocol = IPPROTO_IP; + + // Lookup host + if (getaddrinfo(hostname.c_str(), portStr.c_str(), &h, &airoot)) + { + CSPOT_LOG(error, "getaddrinfo failed"); + } + + // find the right ai, connect to server + for (ai = airoot; ai; ai = ai->ai_next) + { + if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) + continue; + + this->apSock = socket(ai->ai_family, + ai->ai_socktype, ai->ai_protocol); + if (this->apSock < 0) + continue; + + if (connect(this->apSock, + (struct sockaddr *)ai->ai_addr, + ai->ai_addrlen) != -1) + { + struct timeval tv; + tv.tv_sec = 3; + tv.tv_usec = 0; + setsockopt(this->apSock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv); + setsockopt(this->apSock, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof tv); + + int flag = 1; + setsockopt(this->apSock, /* socket affected */ + IPPROTO_TCP, /* set option at TCP level */ + TCP_NODELAY, /* name of option */ + (char *)&flag, /* the cast is historical cruft */ + sizeof(int)); /* length of option value */ + break; + } + + close(this->apSock); + apSock = -1; + throw std::runtime_error("Can't connect to spotify servers"); + } + + freeaddrinfo(airoot); + CSPOT_LOG(debug, "Connected to spotify server"); +} + +std::vector PlainConnection::recvPacket() +{ + // Read packet size + auto sizeData = readBlock(4); + uint32_t packetSize = ntohl(extract(sizeData, 0)); + + // Read actual data + auto data = readBlock(packetSize - 4); + sizeData.insert(sizeData.end(), data.begin(), data.end()); + + return sizeData; +} + +std::vector PlainConnection::sendPrefixPacket(const std::vector &prefix, const std::vector &data) +{ + // Calculate full packet length + uint32_t actualSize = prefix.size() + data.size() + sizeof(uint32_t); + + // Packet structure [PREFIX] + [SIZE] + [DATA] + auto sizeRaw = pack(htonl(actualSize)); + sizeRaw.insert(sizeRaw.begin(), prefix.begin(), prefix.end()); + sizeRaw.insert(sizeRaw.end(), data.begin(), data.end()); + + // Actually write it to the server + writeBlock(sizeRaw); + + return sizeRaw; +} + +std::vector PlainConnection::readBlock(size_t size) +{ + std::vector buf(size); + unsigned int idx = 0; + ssize_t n; + // printf("START READ\n"); + + while (idx < size) + { + READ: + if ((n = recv(this->apSock, &buf[idx], size - idx, 0)) <= 0) + { + switch (errno) + { + case EAGAIN: + case ETIMEDOUT: + if (timeoutHandler()) + { + CSPOT_LOG(error, "Connection lost, will need to reconnect..."); + throw std::runtime_error("Reconnection required"); + } + goto READ; + case EINTR: + break; + default: + throw std::runtime_error("Corn"); + } + } + idx += n; + } + // printf("FINISH READ\n"); + return buf; +} + +size_t PlainConnection::writeBlock(const std::vector &data) +{ + unsigned int idx = 0; + ssize_t n; + // printf("START WRITE\n"); + + while (idx < data.size()) + { + WRITE: + if ((n = send(this->apSock, &data[idx], data.size() - idx < 64 ? data.size() - idx : 64, 0)) <= 0) + { + switch (errno) + { + case EAGAIN: + case ETIMEDOUT: + if (timeoutHandler()) + { + throw std::runtime_error("Reconnection required"); + } + goto WRITE; + case EINTR: + break; + default: + throw std::runtime_error("Corn"); + } + } + idx += n; + } + + return data.size(); +} + +void PlainConnection::closeSocket() +{ + CSPOT_LOG(info, "Closing socket..."); + shutdown(this->apSock, SHUT_RDWR); + close(this->apSock); +} diff --git a/components/spotify/cspot/src/Player.cpp b/components/spotify/cspot/src/Player.cpp new file mode 100644 index 00000000..6ff3ab06 --- /dev/null +++ b/components/spotify/cspot/src/Player.cpp @@ -0,0 +1,131 @@ +#include "Player.h" +#include "Logger.h" + +// #include + +Player::Player(std::shared_ptr manager, std::shared_ptr audioSink): bell::Task("player", 10 * 1024, +0, 1) +{ + this->audioSink = audioSink; + this->manager = manager; + startTask(); +} + +void Player::pause() +{ + this->currentTrack->audioStream->isPaused = true; +} + +void Player::play() +{ + this->currentTrack->audioStream->isPaused = false; +} + +void Player::setVolume(uint32_t volume) +{ + this->volume = (volume / (double)MAX_VOLUME) * 255; + + // Calculate and cache log volume value + auto vol = 255 - this->volume; + uint32_t value = (log10(255 / ((float)vol + 1)) * 105.54571334); + if (value >= 254) value = 256; + logVolume = value << 8; // *256 + + // Pass volume event to the sink if volume is sink-handled + if (!this->audioSink->softwareVolumeControl) + { + this->audioSink->volumeChanged(volume); + } +} + +void Player::seekMs(size_t positionMs) +{ + this->currentTrack->audioStream->seekMs(positionMs); + // VALGRIND_DO_LEAK_CHECK; +} + +void Player::feedPCM(std::vector& data) +{ + // Simple digital volume control alg + // @TODO actually extract it somewhere + if (this->audioSink->softwareVolumeControl) + { + int16_t* psample; + uint32_t pmax; + psample = (int16_t*)(data.data()); + for (int32_t i = 0; i < (data.size() / 2); i++) + { + int32_t temp; + // Offset data for unsigned sinks + if (this->audioSink->usign) + { + temp = ((int32_t)psample[i] + 0x8000) * logVolume; + } + else + { + temp = ((int32_t)psample[i]) * logVolume; + } + psample[i] = (temp >> 16) & 0xFFFF; + } + } + + this->audioSink->feedPCMFrames(data); +} + +void Player::runTask() +{ + std::scoped_lock lock(this->runningMutex); + this->isRunning = true; + while (isRunning) + { + if (this->trackQueue.wpop(currentTrack)) { + currentTrack->audioStream->startPlaybackLoop(); + currentTrack->loadedTrackCallback = nullptr; + currentTrack->audioStream->streamFinishedCallback = nullptr; + currentTrack->audioStream->audioSink = nullptr; + currentTrack->audioStream->pcmCallback = nullptr; + } + } +} + +void Player::stop() { + this->isRunning = false; + CSPOT_LOG(info, "Stopping player"); + this->trackQueue.clear(); + cancelCurrentTrack(); + CSPOT_LOG(info, "Track cancelled"); + std::scoped_lock lock(this->runningMutex); + CSPOT_LOG(info, "Done"); +} + +void Player::cancelCurrentTrack() +{ + if (currentTrack != nullptr) + { + if (currentTrack->audioStream != nullptr && currentTrack->audioStream->isRunning) + { + currentTrack->audioStream->isRunning = false; + } + } +} + +void Player::handleLoad(std::shared_ptr trackReference, std::function& trackLoadedCallback, uint32_t position_ms, bool isPaused) +{ + std::lock_guard guard(loadTrackMutex); + cancelCurrentTrack(); + + pcmDataCallback framesCallback = [=](std::vector& frames) { + this->feedPCM(frames); + }; + + auto loadedLambda = trackLoadedCallback; + + auto track = std::make_shared(this->manager, trackReference, position_ms, isPaused); + track->trackInfoReceived = this->trackChanged; + track->loadedTrackCallback = [this, track, framesCallback, loadedLambda]() { + loadedLambda(); + track->audioStream->streamFinishedCallback = this->endOfFileCallback; + track->audioStream->audioSink = this->audioSink; + track->audioStream->pcmCallback = framesCallback; + this->trackQueue.push(track); + }; +} diff --git a/components/spotify/cspot/src/PlayerState.cpp b/components/spotify/cspot/src/PlayerState.cpp new file mode 100644 index 00000000..f86ba1a9 --- /dev/null +++ b/components/spotify/cspot/src/PlayerState.cpp @@ -0,0 +1,199 @@ +#include "PlayerState.h" +#include "Logger.h" +#include "ConfigJSON.h" + +PlayerState::PlayerState(std::shared_ptr timeProvider) +{ + this->timeProvider = timeProvider; + + // Prepare default state + innerFrame.state.emplace(); + innerFrame.state->position_ms = 0; + innerFrame.state->status = PlayStatus::kPlayStatusStop; + innerFrame.state->position_measured_at = 0; + innerFrame.state->shuffle = false; + innerFrame.state->repeat = false; + + innerFrame.device_state.emplace(); + innerFrame.device_state->sw_version = swVersion; + innerFrame.device_state->is_active = false; + innerFrame.device_state->can_play = true; + innerFrame.device_state->volume = configMan->volume; + innerFrame.device_state->name = configMan->deviceName; + + // Prepare player's capabilities + innerFrame.device_state->capabilities = std::vector(); + addCapability(CapabilityType::kCanBePlayer, 1); + addCapability(CapabilityType::kDeviceType, 4); + addCapability(CapabilityType::kGaiaEqConnectId, 1); + addCapability(CapabilityType::kSupportsLogout, 0); + addCapability(CapabilityType::kIsObservable, 1); + addCapability(CapabilityType::kVolumeSteps, 64); + addCapability(CapabilityType::kSupportedContexts, -1, + std::vector({"album", "playlist", "search", "inbox", + "toplist", "starred", "publishedstarred", "track"})); + addCapability(CapabilityType::kSupportedTypes, -1, + std::vector({"audio/local", "audio/track", "audio/episode", "local", "track"})); +} + +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.value(); + this->updatePositionMs(innerFrame.state->position_ms.value() + diff); + break; + } +} + +bool PlayerState::isActive() +{ + return innerFrame.device_state->is_active.value(); +} + +bool PlayerState::nextTrack() +{ + innerFrame.state->playing_track_index.value()++; + + if (innerFrame.state->playing_track_index >= innerFrame.state->track.size()) + { + innerFrame.state->playing_track_index = 0; + if (!innerFrame.state->repeat) + { + setPlaybackState(PlaybackState::Paused); + return false; + } + } + + return true; +} + +void PlayerState::prevTrack() +{ + if (innerFrame.state->playing_track_index > 0) + { + innerFrame.state->playing_track_index.value()--; + } + else if (innerFrame.state->repeat) + { + innerFrame.state->playing_track_index = innerFrame.state->track.size() - 1; + } +} + +void PlayerState::setActive(bool isActive) +{ + innerFrame.device_state->is_active = isActive; + if (isActive) + { + innerFrame.device_state->became_active_at = timeProvider->getSyncedTimestamp(); + } +} + +void PlayerState::updatePositionMs(uint32_t position) +{ + innerFrame.state->position_ms = position; + innerFrame.state->position_measured_at = timeProvider->getSyncedTimestamp(); +} +void PlayerState::updateTracks() +{ + CSPOT_LOG(info, "---- Track count %d", remoteFrame.state->track.size()); + // innerFrame.state->context_uri = remoteFrame.state->context_uri == nullptr ? nullptr : strdup(otherFrame->state->context_uri); + + innerFrame.state->track = remoteFrame.state->track; + innerFrame.state->playing_track_index = remoteFrame.state->playing_track_index; + + if (remoteFrame.state->repeat.value()) + { + setRepeat(true); + } + + if (remoteFrame.state->shuffle.value()) + { + setShuffle(true); + } +} + +void PlayerState::setVolume(uint32_t volume) +{ + innerFrame.device_state->volume = volume; + configMan->volume = volume; + configMan->save(); +} + +void PlayerState::setShuffle(bool shuffle) +{ + innerFrame.state->shuffle = shuffle; + if (shuffle) + { + // Put current song at the begining + auto tmp = innerFrame.state->track.at(0); + innerFrame.state->track.at(0) = innerFrame.state->track.at(innerFrame.state->playing_track_index.value()); + innerFrame.state->track.at(innerFrame.state->playing_track_index.value()) = tmp; + + // Shuffle current tracks + for (int x = 1; x < innerFrame.state->track.size() - 1; x++) + { + auto j = x + (std::rand() % (innerFrame.state->track.size() - x)); + tmp = innerFrame.state->track.at(j); + innerFrame.state->track.at(j) = innerFrame.state->track.at(x); + innerFrame.state->track.at(x) = tmp; + } + innerFrame.state->playing_track_index = 0; + } +} + +void PlayerState::setRepeat(bool repeat) +{ + innerFrame.state->repeat = repeat; +} + +std::shared_ptr PlayerState::getCurrentTrack() +{ + // Wrap current track in a class + return std::make_shared(&innerFrame.state->track.at(innerFrame.state->playing_track_index.value())); +} + +std::vector PlayerState::encodeCurrentFrame(MessageType typ) +{ + // Prepare current frame info + innerFrame.version = 1; + innerFrame.ident = deviceId; + innerFrame.seq_nr = this->seqNum; + innerFrame.protocol_version = protocolVersion; + innerFrame.typ = typ; + innerFrame.state_update_id = timeProvider->getSyncedTimestamp(); + + this->seqNum += 1; + auto fram = encodePb(innerFrame); + return fram; +} + +// Wraps messy nanopb setters. @TODO: find a better way to handle this +void PlayerState::addCapability(CapabilityType typ, int intValue, std::vector stringValue) +{ + auto capability = Capability(); + capability.typ = typ; + + if (intValue != -1) + { + capability.intValue = std::vector({intValue}); + } + + capability.stringValue = stringValue; + innerFrame.device_state->capabilities.push_back(capability); +} diff --git a/components/spotify/cspot/src/Protobuf.cpp b/components/spotify/cspot/src/Protobuf.cpp new file mode 100644 index 00000000..cf6f90a9 --- /dev/null +++ b/components/spotify/cspot/src/Protobuf.cpp @@ -0,0 +1,189 @@ +#include "ProtoHelper.h" + + +std::optional findFieldWithProtobufTag(AnyRef ref, uint32_t tag) +{ + auto info = ref.reflectType(); + for (int i = 0; i < info->fields.size(); i++) + { + + if (tag == info->fields[i].protobufTag) + { + return std::make_optional(ref.getField(i)); + } + } + + return std::nullopt; +} + +void decodeField(std::shared_ptr reader, AnyRef any) +{ + auto fieldInfo = any.reflectType(); + + if (fieldInfo->kind == ReflectTypeKind::Optional) + { + auto optionalRef = AnyOptionalRef(any); + optionalRef.emplaceEmpty(); + return decodeField(reader, optionalRef.get()); + } + + if (fieldInfo->kind == ReflectTypeKind::Class) + { + // Handle submessage + auto lastMaxPosition = reader->maxPosition; + auto messageSize = reader->decodeVarInt(); + + reader->maxPosition = messageSize + reader->pos; + while (reader->next()) + { + auto res = findFieldWithProtobufTag(any, reader->currentTag); + if (res.has_value()) + { + decodeField(reader, res.value()); + } + else + { + reader->skip(); + } + } + reader->maxPosition = lastMaxPosition; + return; + } else if (any.is>()) + { + reader->decodeVector(*any.as>()); + } + // Handle repeated + else if (fieldInfo->kind == ReflectTypeKind::Vector) + { + auto aVec = AnyVectorRef(any); + aVec.emplace_back(); + auto value = aVec.at(aVec.size() - 1); + auto valInfo = value.reflectType(); + + // Represents packed repeated encoding + if (valInfo->kind == ReflectTypeKind::Primitive && !value.is() && !value.is>()) + { + // *any.as() = reader->decodeVarInt(); + reader->skip(); + } + else + { + decodeField(reader, value); + } + } + else if (fieldInfo->kind == ReflectTypeKind::Enum) + { + *any.as() = reader->decodeVarInt(); + } + else if (any.is()) + { + reader->decodeString(*any.as()); + } + else if (any.is()) + { + *any.as() = reader->decodeVarInt(); + } + else if (any.is()) + { + *any.as() = reader->decodeVarInt(); + } + else if (any.is()) + { + *any.as() = reader->decodeVarInt(); + } + else + { + reader->skip(); + } +} + +void decodeProtobuf(std::shared_ptr reader, AnyRef any) +{ + while (reader->next()) + { + auto res = findFieldWithProtobufTag(any, reader->currentTag); + if (res.has_value()) + { + decodeField(reader, res.value()); + } + else + { + reader->skip(); + } + } +} + +void encodeProtobuf(std::shared_ptr writer, AnyRef any, uint32_t protobufTag) +{ + auto info = any.reflectType(); + + // Handle optionals, only encode if have value + if (info->kind == ReflectTypeKind::Optional) + { + auto optionalRef = AnyOptionalRef(any); + if (!optionalRef.has_value()) + { + return; + } + else + { + return encodeProtobuf(writer, optionalRef.get(), protobufTag); + } + } + if (info->kind == ReflectTypeKind::Class) + { + uint32_t startMsgPosition; + // 0 - default value, indicating top level message + if (protobufTag > 0) + { + startMsgPosition = writer->startMessage(); + } + + for (int i = 0; i < info->fields.size(); i++) + { + auto field = any.getField(i); + encodeProtobuf(writer, field, info->fields[i].protobufTag); + } + + if (protobufTag > 0) + { + writer->finishMessage(protobufTag, startMsgPosition); + } + } else if (any.is>()) + { + writer->addVector(protobufTag, *any.as>()); + } + else if (info->kind == ReflectTypeKind::Vector) { + auto aVec = AnyVectorRef(any); + auto size = aVec.size(); + for (size_t i = 0; i < size; i++) + { + auto valueAt = aVec.at(i); + encodeProtobuf(writer, valueAt, protobufTag); + } + } + else if (info->kind == ReflectTypeKind::Enum) { + writer->addVarInt(protobufTag, *any.as()); + } + else if (info->kind == ReflectTypeKind::Primitive) + { + if (any.is()) + { + writer->addString(protobufTag, *any.as()); + } + else if (any.is()) + { + writer->addVarInt(protobufTag, *any.as()); + } + else if (any.is()) + { + writer->addVarInt(protobufTag, *any.as()); + } + else if (any.is()) { + writer->addVarInt(protobufTag, *any.as()); + } else if (any.is()) + { + writer->addVarInt(protobufTag, *any.as()); + } + } +} \ No newline at end of file diff --git a/components/spotify/cspot/src/Session.cpp b/components/spotify/cspot/src/Session.cpp new file mode 100644 index 00000000..86d2f394 --- /dev/null +++ b/components/spotify/cspot/src/Session.cpp @@ -0,0 +1,155 @@ +#include "Session.h" +#include "MercuryManager.h" +#include "Logger.h" + +using random_bytes_engine = std::independent_bits_engine; + +Session::Session() +{ + // Generates the public and priv key + this->crypto = std::make_unique(); + this->shanConn = std::make_shared(); +} + +void Session::connect(std::unique_ptr connection) +{ + this->conn = std::move(connection); + auto helloPacket = this->sendClientHelloRequest(); + this->processAPHelloResponse(helloPacket); +} + +void Session::connectWithRandomAp() +{ + auto apResolver = std::make_unique(); + this->conn = std::make_unique(); + + auto apAddr = apResolver->fetchFirstApAddress(); + CSPOT_LOG(debug, "Connecting with AP <%s>", apAddr.c_str()); + this->conn->connectToAp(apAddr); + auto helloPacket = this->sendClientHelloRequest(); + CSPOT_LOG(debug, "Sending APHello packet..."); + this->processAPHelloResponse(helloPacket); +} + +std::vector Session::authenticate(std::shared_ptr blob) +{ + // save auth blob for reconnection purposes + authBlob = blob; + + // prepare authentication request proto + authRequest.login_credentials.username = blob->username; + authRequest.login_credentials.auth_data = blob->authData; + authRequest.login_credentials.typ = static_cast(blob->authType); + authRequest.system_info.cpu_family = CpuFamily::CPU_UNKNOWN; + authRequest.system_info.os = Os::OS_UNKNOWN; + authRequest.system_info.system_information_string = std::string(informationString); + authRequest.system_info.device_id = std::string(deviceId); + authRequest.version_string = std::string(versionString); + + auto data = encodePb(authRequest); + + // Send login request + this->shanConn->sendPacket(LOGIN_REQUEST_COMMAND, data); + + auto packet = this->shanConn->recvPacket(); + switch (packet->command) + { + case AUTH_SUCCESSFUL_COMMAND: + { + CSPOT_LOG(debug, "Authorization successful"); + + // @TODO store the reusable credentials + // PBWrapper welcomePacket(packet->data) + return std::vector({0x1}); // TODO: return actual reusable credentaials to be stored somewhere + break; + } + case AUTH_DECLINED_COMMAND: + { + CSPOT_LOG(error, "Authorization declined"); + break; + } + default: + CSPOT_LOG(error, "Unknown auth fail code %d", packet->command); + } + + return std::vector(0); +} + +void Session::processAPHelloResponse(std::vector &helloPacket) +{ + CSPOT_LOG(debug, "Processing AP hello response..."); + auto data = this->conn->recvPacket(); + CSPOT_LOG(debug, "Received AP hello response"); + // Decode the response + auto skipSize = std::vector(data.begin() + 4, data.end()); + apResponse = decodePb(skipSize); + + auto kkEy = apResponse.challenge->login_crypto_challenge.diffie_hellman->gs; + // Compute the diffie hellman shared key based on the response + auto sharedKey = this->crypto->dhCalculateShared(kkEy); + + // Init client packet + Init server packets are required for the hmac challenge + data.insert(data.begin(), helloPacket.begin(), helloPacket.end()); + + // Solve the hmac challenge + auto resultData = std::vector(0); + for (int x = 1; x < 6; x++) + { + auto challengeVector = std::vector(1); + challengeVector[0] = x; + + challengeVector.insert(challengeVector.begin(), data.begin(), data.end()); + auto digest = crypto->sha1HMAC(sharedKey, challengeVector); + resultData.insert(resultData.end(), digest.begin(), digest.end()); + } + + auto lastVec = std::vector(resultData.begin(), resultData.begin() + 0x14); + + // Digest generated! + clientResPlaintext.login_crypto_response = {}; + clientResPlaintext.login_crypto_response.diffie_hellman.emplace(); + clientResPlaintext.login_crypto_response.diffie_hellman->hmac = crypto->sha1HMAC(lastVec, data); + + auto resultPacket = encodePb(clientResPlaintext); + + auto emptyPrefix = std::vector(0); + + this->conn->sendPrefixPacket(emptyPrefix, resultPacket); + + // Get send and receive keys + auto sendKey = std::vector(resultData.begin() + 0x14, resultData.begin() + 0x34); + auto recvKey = std::vector(resultData.begin() + 0x34, resultData.begin() + 0x54); + + CSPOT_LOG(debug, "Received shannon keys"); + + // Init shanno-encrypted connection + this->shanConn->wrapConnection(this->conn, sendKey, recvKey); +} + +void Session::close() { + this->conn->closeSocket(); +} + +std::vector Session::sendClientHelloRequest() +{ + // Prepare protobuf message + this->crypto->dhInit(); + + // Copy the public key into diffiehellman hello packet + clientHello.login_crypto_hello.diffie_hellman.emplace(); + clientHello.feature_set.emplace(); + clientHello.login_crypto_hello.diffie_hellman->gc = this->crypto->publicKey; + clientHello.login_crypto_hello.diffie_hellman->server_keys_known = 1; + clientHello.build_info.product = Product::PRODUCT_PARTNER; + clientHello.build_info.platform = Platform::PLATFORM_LINUX_X86; + clientHello.build_info.version = 112800721; + clientHello.feature_set->autoupdate2 = true; + clientHello.cryptosuites_supported = std::vector({Cryptosuite::CRYPTO_SUITE_SHANNON}); + clientHello.padding = std::vector({0x1E}); + + // Generate the random nonce + clientHello.client_nonce = crypto->generateVectorWithRandomData(16); + auto vecData = encodePb(clientHello); + auto prefix = std::vector({0x00, 0x04}); + return this->conn->sendPrefixPacket(prefix, vecData); +} diff --git a/components/spotify/cspot/src/Shannon.cpp b/components/spotify/cspot/src/Shannon.cpp new file mode 100644 index 00000000..08e0a773 --- /dev/null +++ b/components/spotify/cspot/src/Shannon.cpp @@ -0,0 +1,443 @@ +#include "Shannon.h" +// #include +#include // for uint32_t +#include // for CHAR_BIT +// #define NDEBUG +#include + +static inline uint32_t rotl(uint32_t n, unsigned int c) +{ + const unsigned int mask = (CHAR_BIT * sizeof(n) - 1); // assumes width is a power of 2. + + // assert ( (c<=mask) &&"rotate by type width or more"); + c &= mask; + return (n << c) | (n >> ((-c) & mask)); +} + +static inline uint32_t rotr(uint32_t n, unsigned int c) +{ + const unsigned int mask = (CHAR_BIT * sizeof(n) - 1); + + // assert ( (c<=mask) &&"rotate by type width or more"); + c &= mask; + return (n >> c) | (n << ((-c) & mask)); +} + +uint32_t Shannon::sbox1(uint32_t w) +{ + w ^= rotl(w, 5) | rotl(w, 7); + w ^= rotl(w, 19) | rotl(w, 22); + return w; +} + +uint32_t Shannon::sbox2(uint32_t w) +{ + w ^= rotl(w, 7) | rotl(w, 22); + w ^= rotl(w, 5) | rotl(w, 19); + return w; +} + +void Shannon::cycle() +{ + uint32_t t; + int i; + + /* nonlinear feedback function */ + t = this->R[12] ^ this->R[13] ^ this->konst; + t = Shannon::sbox1(t) ^ rotl(this->R[0], 1); + /* shift register */ + for (i = 1; i < N; ++i) + this->R[i - 1] = this->R[i]; + this->R[N - 1] = t; + t = Shannon::sbox2(this->R[2] ^ this->R[15]); + this->R[0] ^= t; + this->sbuf = t ^ this->R[8] ^ this->R[12]; +} + +void Shannon::crcfunc(uint32_t i) +{ + uint32_t t; + int j; + + /* Accumulate CRC of input */ + t = this->CRC[0] ^ this->CRC[2] ^ this->CRC[15] ^ i; + for (j = 1; j < N; ++j) + this->CRC[j - 1] = this->CRC[j]; + this->CRC[N - 1] = t; +} + +void Shannon::macfunc(uint32_t i) +{ + this->crcfunc(i); + this->R[KEYP] ^= i; +} + +void Shannon::initState() +{ + int i; + + /* Register initialised to Fibonacci numbers; Counter zeroed. */ + this->R[0] = 1; + this->R[1] = 1; + for (i = 2; i < N; ++i) + this->R[i] = this->R[i - 1] + this->R[i - 2]; + this->konst = Shannon::INITKONST; +} +void Shannon::saveState() +{ + int i; + for (i = 0; i < Shannon::N; ++i) + this->initR[i] = this->R[i]; +} +void Shannon::reloadState() +{ + int i; + + for (i = 0; i < Shannon::N; ++i) + this->R[i] = this->initR[i]; +} +void Shannon::genkonst() +{ + this->konst = this->R[0]; +} +void Shannon::diffuse() +{ + int i; + + for (i = 0; i < Shannon::FOLD; ++i) + this->cycle(); +} + +#define Byte(x, i) ((uint32_t)(((x) >> (8 * (i))) & 0xFF)) +#define BYTE2WORD(b) ( \ + (((uint32_t)(b)[3] & 0xFF) << 24) | \ + (((uint32_t)(b)[2] & 0xFF) << 16) | \ + (((uint32_t)(b)[1] & 0xFF) << 8) | \ + (((uint32_t)(b)[0] & 0xFF))) +#define WORD2BYTE(w, b) \ + { \ + (b)[3] = Byte(w, 3); \ + (b)[2] = Byte(w, 2); \ + (b)[1] = Byte(w, 1); \ + (b)[0] = Byte(w, 0); \ + } +#define XORWORD(w, b) \ + { \ + (b)[3] ^= Byte(w, 3); \ + (b)[2] ^= Byte(w, 2); \ + (b)[1] ^= Byte(w, 1); \ + (b)[0] ^= Byte(w, 0); \ + } + +#define XORWORD(w, b) \ + { \ + (b)[3] ^= Byte(w, 3); \ + (b)[2] ^= Byte(w, 2); \ + (b)[1] ^= Byte(w, 1); \ + (b)[0] ^= Byte(w, 0); \ + } + +/* Load key material into the register + */ +#define ADDKEY(k) \ + this->R[KEYP] ^= (k); + +void Shannon::loadKey(const std::vector &key) +{ + int i, j; + uint32_t k; + uint8_t xtra[4]; + size_t keylen = key.size(); + /* start folding in key */ + for (i = 0; i < (keylen & ~0x3); i += 4) + { + k = BYTE2WORD(&key[i]); + ADDKEY(k); + this->cycle(); + } + + /* if there were any extra key bytes, zero pad to a word */ + if (i < keylen) + { + for (j = 0 /* i unchanged */; i < keylen; ++i) + xtra[j++] = key[i]; + for (/* j unchanged */; j < 4; ++j) + xtra[j] = 0; + k = BYTE2WORD(xtra); + ADDKEY(k); + this->cycle(); + } + + /* also fold in the length of the key */ + ADDKEY(keylen); + this->cycle(); + + /* save a copy of the register */ + for (i = 0; i < N; ++i) + this->CRC[i] = this->R[i]; + + /* now diffuse */ + this->diffuse(); + + /* now xor the copy back -- makes key loading irreversible */ + for (i = 0; i < N; ++i) + this->R[i] ^= this->CRC[i]; +} + +void Shannon::key(const std::vector &key) +{ + this->initState(); + this->loadKey(key); + this->genkonst(); /* in case we proceed to stream generation */ + this->saveState(); + this->nbuf = 0; +} + +void Shannon::nonce(const std::vector &nonce) +{ + this->reloadState(); + this->konst = Shannon::INITKONST; + this->loadKey(nonce); + this->genkonst(); + this->nbuf = 0; +} + +void Shannon::stream(std::vector &bufVec) +{ + uint8_t *endbuf; + size_t nbytes = bufVec.size(); + uint8_t *buf = bufVec.data(); + /* handle any previously buffered bytes */ + while (this->nbuf != 0 && nbytes != 0) + { + *buf++ ^= this->sbuf & 0xFF; + this->sbuf >>= 8; + this->nbuf -= 8; + --nbytes; + } + + /* handle whole words */ + endbuf = &buf[nbytes & ~((uint32_t)0x03)]; + while (buf < endbuf) + { + this->cycle(); + XORWORD(this->sbuf, buf); + buf += 4; + } + + /* handle any trailing bytes */ + nbytes &= 0x03; + if (nbytes != 0) + { + this->cycle(); + this->nbuf = 32; + while (this->nbuf != 0 && nbytes != 0) + { + *buf++ ^= this->sbuf & 0xFF; + this->sbuf >>= 8; + this->nbuf -= 8; + --nbytes; + } + } +} + +void Shannon::maconly(std::vector &bufVec) +{ + size_t nbytes = bufVec.size(); + uint8_t *buf = bufVec.data(); + + uint8_t *endbuf; + + /* handle any previously buffered bytes */ + if (this->nbuf != 0) + { + while (this->nbuf != 0 && nbytes != 0) + { + this->mbuf ^= (*buf++) << (32 - this->nbuf); + this->nbuf -= 8; + --nbytes; + } + if (this->nbuf != 0) /* not a whole word yet */ + return; + /* LFSR already cycled */ + this->macfunc(this->mbuf); + } + + /* handle whole words */ + endbuf = &buf[nbytes & ~((uint32_t)0x03)]; + while (buf < endbuf) + { + this->cycle(); + this->macfunc(BYTE2WORD(buf)); + buf += 4; + } + + /* handle any trailing bytes */ + nbytes &= 0x03; + if (nbytes != 0) + { + this->cycle(); + this->mbuf = 0; + this->nbuf = 32; + while (this->nbuf != 0 && nbytes != 0) + { + this->mbuf ^= (*buf++) << (32 - this->nbuf); + this->nbuf -= 8; + --nbytes; + } + } +} + +void Shannon::encrypt(std::vector &bufVec) +{ + size_t nbytes = bufVec.size(); + uint8_t *buf = bufVec.data(); + uint8_t *endbuf; + uint32_t t = 0; + + /* handle any previously buffered bytes */ + if (this->nbuf != 0) + { + while (this->nbuf != 0 && nbytes != 0) + { + this->mbuf ^= *buf << (32 - this->nbuf); + *buf ^= (this->sbuf >> (32 - this->nbuf)) & 0xFF; + ++buf; + this->nbuf -= 8; + --nbytes; + } + if (this->nbuf != 0) /* not a whole word yet */ + return; + /* LFSR already cycled */ + this->macfunc(this->mbuf); + } + + /* handle whole words */ + endbuf = &buf[nbytes & ~((uint32_t)0x03)]; + while (buf < endbuf) + { + this->cycle(); + t = BYTE2WORD(buf); + this->macfunc(t); + t ^= this->sbuf; + WORD2BYTE(t, buf); + buf += 4; + } + + /* handle any trailing bytes */ + nbytes &= 0x03; + if (nbytes != 0) + { + this->cycle(); + this->mbuf = 0; + this->nbuf = 32; + while (this->nbuf != 0 && nbytes != 0) + { + this->mbuf ^= *buf << (32 - this->nbuf); + *buf ^= (this->sbuf >> (32 - this->nbuf)) & 0xFF; + ++buf; + this->nbuf -= 8; + --nbytes; + } + } +} + + +void Shannon::decrypt(std::vector &bufVec) +{ + size_t nbytes = bufVec.size(); + uint8_t *buf = bufVec.data(); + uint8_t *endbuf; + uint32_t t = 0; + + /* handle any previously buffered bytes */ + if (this->nbuf != 0) + { + while (this->nbuf != 0 && nbytes != 0) + { + *buf ^= (this->sbuf >> (32 - this->nbuf)) & 0xFF; + this->mbuf ^= *buf << (32 - this->nbuf); + ++buf; + this->nbuf -= 8; + --nbytes; + } + if (this->nbuf != 0) /* not a whole word yet */ + return; + /* LFSR already cycled */ + this->macfunc(this->mbuf); + } + + /* handle whole words */ + endbuf = &buf[nbytes & ~((uint32_t)0x03)]; + while (buf < endbuf) + { + this->cycle(); + t = BYTE2WORD(buf) ^ this->sbuf; + this->macfunc(t); + WORD2BYTE(t, buf); + buf += 4; + } + + /* handle any trailing bytes */ + nbytes &= 0x03; + if (nbytes != 0) + { + this->cycle(); + this->mbuf = 0; + this->nbuf = 32; + while (this->nbuf != 0 && nbytes != 0) + { + *buf ^= (this->sbuf >> (32 - this->nbuf)) & 0xFF; + this->mbuf ^= *buf << (32 - this->nbuf); + ++buf; + this->nbuf -= 8; + --nbytes; + } + } +} + +void Shannon::finish(std::vector &bufVec) +{ + size_t nbytes = bufVec.size(); + uint8_t *buf = bufVec.data(); + int i; + + /* handle any previously buffered bytes */ + if (this->nbuf != 0) + { + /* LFSR already cycled */ + this->macfunc(this->mbuf); + } + + /* perturb the MAC to mark end of input. + * Note that only the stream register is updated, not the CRC. This is an + * action that can't be duplicated by passing in plaintext, hence + * defeating any kind of extension attack. + */ + this->cycle(); + ADDKEY(INITKONST ^ (this->nbuf << 3)); + this->nbuf = 0; + + /* now add the CRC to the stream register and diffuse it */ + for (i = 0; i < N; ++i) + this->R[i] ^= this->CRC[i]; + this->diffuse(); + + /* produce output from the stream buffer */ + while (nbytes > 0) + { + this->cycle(); + if (nbytes >= 4) + { + WORD2BYTE(this->sbuf, buf); + nbytes -= 4; + buf += 4; + } + else + { + for (i = 0; i < nbytes; ++i) + buf[i] = Byte(this->sbuf, i); + break; + } + } +} \ No newline at end of file diff --git a/components/spotify/cspot/src/ShannonConnection.cpp b/components/spotify/cspot/src/ShannonConnection.cpp new file mode 100644 index 00000000..ecec8d50 --- /dev/null +++ b/components/spotify/cspot/src/ShannonConnection.cpp @@ -0,0 +1,100 @@ +#include "ShannonConnection.h" +#include "Logger.h" + +ShannonConnection::ShannonConnection() +{ +} + +ShannonConnection::~ShannonConnection() +{ +} + +void ShannonConnection::wrapConnection(std::shared_ptr conn, std::vector &sendKey, std::vector &recvKey) +{ + this->conn = conn; + + this->sendCipher = std::make_unique(); + this->recvCipher = std::make_unique(); + + // Set keys + this->sendCipher->key(sendKey); + this->recvCipher->key(recvKey); + + // Set initial nonce + this->sendCipher->nonce(pack(htonl(0))); + this->recvCipher->nonce(pack(htonl(0))); +} + +void ShannonConnection::sendPacket(uint8_t cmd, std::vector &data) +{ + this->writeMutex.lock(); + auto rawPacket = this->cipherPacket(cmd, data); + + // Shannon encrypt the packet and write it to sock + this->sendCipher->encrypt(rawPacket); + this->conn->writeBlock(rawPacket); + + // Generate mac + std::vector mac(MAC_SIZE); + this->sendCipher->finish(mac); + + // Update the nonce + this->sendNonce += 1; + this->sendCipher->nonce(pack(htonl(this->sendNonce))); + + // Write the mac to sock + this->conn->writeBlock(mac); + this->writeMutex.unlock(); +} + +std::unique_ptr ShannonConnection::recvPacket() +{ + this->readMutex.lock(); + // Receive 3 bytes, cmd + int16 size + auto data = this->conn->readBlock(3); + this->recvCipher->decrypt(data); + + auto packetData = std::vector(); + + auto readSize = ntohs(extract(data, 1)); + + // Read and decode if the packet has an actual body + if (readSize > 0) + { + packetData = this->conn->readBlock(readSize); + this->recvCipher->decrypt(packetData); + } + + // Read mac + auto mac = this->conn->readBlock(MAC_SIZE); + + // Generate mac + std::vector mac2(MAC_SIZE); + this->recvCipher->finish(mac2); + + if (mac != mac2) + { + CSPOT_LOG(error, "Shannon read: Mac doesn't match"); + } + + // Update the nonce + this->recvNonce += 1; + this->recvCipher->nonce(pack(htonl(this->recvNonce))); + + // Unlock the mutex + this->readMutex.unlock(); + + // data[0] == cmd + return std::make_unique(data[0], packetData); +} + +std::vector ShannonConnection::cipherPacket(uint8_t cmd, std::vector &data) +{ + // Generate packet structure, [Command] [Size] [Raw data] + auto sizeRaw = pack(htons(uint16_t(data.size()))); + + sizeRaw.insert(sizeRaw.begin(), cmd); + sizeRaw.insert(sizeRaw.end(), data.begin(), data.end()); + + return sizeRaw; +} diff --git a/components/spotify/cspot/src/SpircController.cpp b/components/spotify/cspot/src/SpircController.cpp new file mode 100644 index 00000000..ff07d8db --- /dev/null +++ b/components/spotify/cspot/src/SpircController.cpp @@ -0,0 +1,232 @@ +#include "SpircController.h" +#include "ConfigJSON.h" +#include "Logger.h" +#include "SpotifyTrack.h" + +SpircController::SpircController(std::shared_ptr manager, + std::string username, + std::shared_ptr audioSink) { + + this->manager = manager; + this->player = std::make_unique(manager, audioSink); + this->state = std::make_unique(manager->timeProvider); + this->username = username; + + player->endOfFileCallback = [=]() { + if (state->nextTrack()) { + loadTrack(); + } + }; + + player->setVolume(configMan->volume); + subscribe(); +} + +void SpircController::subscribe() { + mercuryCallback responseLambda = [=](std::unique_ptr res) { + // this->trackInformationCallback(std::move(res)); + sendCmd(MessageType::kMessageTypeHello); + CSPOT_LOG(debug, "Sent kMessageTypeHello!"); + }; + mercuryCallback subLambda = [=](std::unique_ptr res) { + this->handleFrame(res->parts[0]); + }; + + manager->execute(MercuryType::SUB, + "hm://remote/user/" + this->username + "/", responseLambda, + subLambda); +} + +void SpircController::setPause(bool isPaused, bool notifyPlayer) { + sendEvent(CSpotEventType::PLAY_PAUSE, isPaused); + if (isPaused) { + CSPOT_LOG(debug, "External pause command"); + if (notifyPlayer) player->pause(); + state->setPlaybackState(PlaybackState::Paused); + notify(); + } else { + CSPOT_LOG(debug, "External play command"); + if (notifyPlayer) player->play(); + state->setPlaybackState(PlaybackState::Playing); + notify(); + } +} + +void SpircController::playToggle() { + if (state->innerFrame.state->status.value() == PlayStatus::kPlayStatusPause) { + setPause(false); + } else { + setPause(true); + } +} + +void SpircController::adjustVolume(int by) { + if (state->innerFrame.device_state->volume.has_value()) { + int volume = state->innerFrame.device_state->volume.value() + by; + if (volume < 0) volume = 0; + else if (volume > MAX_VOLUME) volume = MAX_VOLUME; + setVolume(volume); + } +} + +void SpircController::setVolume(int volume) { + setRemoteVolume(volume); + player->setVolume(volume); + configMan->save(); +} + +void SpircController::setRemoteVolume(int volume) { + state->setVolume(volume); + notify(); +} + +void SpircController::nextSong() { + if (state->nextTrack()) { + loadTrack(); + } else { + player->cancelCurrentTrack(); + } + notify(); +} + +void SpircController::prevSong() { + state->prevTrack(); + loadTrack(); + notify(); +} + +void SpircController::handleFrame(std::vector &data) { + state->remoteFrame = decodePb(data); + + switch (state->remoteFrame.typ.value()) { + case MessageType::kMessageTypeNotify: { + CSPOT_LOG(debug, "Notify frame"); + // Pause the playback if another player took control + if (state->isActive() && + state->remoteFrame.device_state->is_active.value()) { + sendEvent(CSpotEventType::DISC); + state->setActive(false); + notify(); + player->cancelCurrentTrack(); + } + break; + } + case MessageType::kMessageTypeSeek: { + CSPOT_LOG(debug, "Seek command"); + sendEvent(CSpotEventType::SEEK, (int) state->remoteFrame.position.value()); + state->updatePositionMs(state->remoteFrame.position.value()); + this->player->seekMs(state->remoteFrame.position.value()); + notify(); + break; + } + case MessageType::kMessageTypeVolume: + sendEvent(CSpotEventType::VOLUME, (int) state->remoteFrame.volume.value()); + setVolume(state->remoteFrame.volume.value()); + break; + case MessageType::kMessageTypePause: + setPause(true); + break; + case MessageType::kMessageTypePlay: + setPause(false); + break; + case MessageType::kMessageTypeNext: + sendEvent(CSpotEventType::NEXT); + nextSong(); + break; + case MessageType::kMessageTypePrev: + sendEvent(CSpotEventType::PREV); + prevSong(); + break; + case MessageType::kMessageTypeLoad: { + CSPOT_LOG(debug, "Load frame!"); + + state->setActive(true); + + // Every sane person on the planet would expect std::move to work here. + // And it does... on every single platform EXCEPT for ESP32 for some + // reason. For which it corrupts memory and makes printf fail. so yeah. + // its cursed. + state->updateTracks(); + + // bool isPaused = (state->remoteFrame.state->status.value() == + // PlayStatus::kPlayStatusPlay) ? false : true; + loadTrack(state->remoteFrame.state->position_ms.value(), false); + state->updatePositionMs(state->remoteFrame.state->position_ms.value()); + + this->notify(); + break; + } + case MessageType::kMessageTypeReplace: { + CSPOT_LOG(debug, "Got replace frame!"); + break; + } + case MessageType::kMessageTypeShuffle: { + CSPOT_LOG(debug, "Got shuffle frame"); + state->setShuffle(state->remoteFrame.state->shuffle.value()); + this->notify(); + break; + } + case MessageType::kMessageTypeRepeat: { + CSPOT_LOG(debug, "Got repeat frame"); + state->setRepeat(state->remoteFrame.state->repeat.value()); + this->notify(); + break; + } + default: + break; + } +} + +void SpircController::loadTrack(uint32_t position_ms, bool isPaused) { + state->setPlaybackState(PlaybackState::Loading); + std::function loadedLambda = [=]() { + // Loading finished, notify that playback started + setPause(isPaused, false); + }; + + player->handleLoad(state->getCurrentTrack(), loadedLambda, position_ms, + isPaused); +} + +void SpircController::notify() { + this->sendCmd(MessageType::kMessageTypeNotify); +} + +void SpircController::sendEvent(CSpotEventType eventType, std::variant data) { + if (eventHandler != nullptr) { + CSpotEvent event = { + .eventType = eventType, + .data = data, + }; + + eventHandler(event); + } +} + +void SpircController::setEventHandler(cspotEventHandler callback) { + this->eventHandler = callback; + + player->trackChanged = ([this](TrackInfo &track) { + TrackInfo info; + info.album = track.album; + info.artist = track.artist; + info.imageUrl = track.imageUrl; + info.name = track.name; + + this->sendEvent(CSpotEventType::TRACK_INFO, info); + }); +} + +void SpircController::stopPlayer() { this->player->stop(); } + +void SpircController::sendCmd(MessageType typ) { + // Serialize current player state + auto encodedFrame = state->encodeCurrentFrame(typ); + + mercuryCallback responseLambda = [=](std::unique_ptr res) { + }; + auto parts = mercuryParts({encodedFrame}); + this->manager->execute(MercuryType::SEND, + "hm://remote/user/" + this->username + "/", + responseLambda, parts); +} diff --git a/components/spotify/cspot/src/SpotifyTrack.cpp b/components/spotify/cspot/src/SpotifyTrack.cpp new file mode 100644 index 00000000..3e289ec7 --- /dev/null +++ b/components/spotify/cspot/src/SpotifyTrack.cpp @@ -0,0 +1,166 @@ +#include "SpotifyTrack.h" +#include "unistd.h" +#include "MercuryManager.h" +#include +#include "CspotAssert.h" +#include "Logger.h" +#include "ConfigJSON.h" + +SpotifyTrack::SpotifyTrack(std::shared_ptr manager, std::shared_ptr trackReference, uint32_t position_ms, bool isPaused) +{ + this->manager = manager; + this->fileId = std::vector(); + + mercuryCallback trackResponseLambda = [=](std::unique_ptr res) { + this->trackInformationCallback(std::move(res), position_ms, isPaused); + }; + + mercuryCallback episodeResponseLambda = [=](std::unique_ptr res) { + this->episodeInformationCallback(std::move(res), position_ms, isPaused); + }; + + if (trackReference->isEpisode) + { + this->reqSeqNum = this->manager->execute(MercuryType::GET, "hm://metadata/3/episode/" + bytesToHexString(trackReference->gid), episodeResponseLambda); + } + else + { + this->reqSeqNum = this->manager->execute(MercuryType::GET, "hm://metadata/3/track/" + bytesToHexString(trackReference->gid), trackResponseLambda); + } +} + +SpotifyTrack::~SpotifyTrack() +{ + this->manager->unregisterMercuryCallback(this->reqSeqNum); + this->manager->freeAudioKeyCallback(); +} + +bool SpotifyTrack::countryListContains(std::string countryList, std::string country) +{ + for (int x = 0; x < countryList.size(); x += 2) + { + if (countryList.substr(x, 2) == country) + { + return true; + } + } + return false; +} + +bool SpotifyTrack::canPlayTrack(std::vector& restrictions) +{ + for (int x = 0; x < restrictions.size(); x++) + { + if (restrictions[x].countries_allowed.has_value()) + { + return countryListContains(restrictions[x].countries_allowed.value(), manager->countryCode); + } + + if (restrictions[x].countries_forbidden.has_value()) + { + return !countryListContains(restrictions[x].countries_forbidden.value(), manager->countryCode); + } + } + + return true; +} + +void SpotifyTrack::trackInformationCallback(std::unique_ptr response, uint32_t position_ms, bool isPaused) +{ + if (this->fileId.size() != 0) + return; + CSPOT_ASSERT(response->parts.size() > 0, "response->parts.size() must be greater than 0"); + + trackInfo = decodePb(response->parts[0]); + + CSPOT_LOG(info, "Track name: %s", trackInfo.name.value().c_str()); + + CSPOT_LOG(debug, "trackInfo.restriction.size() = %d", trackInfo.restriction.size()); + int altIndex = 0; + while (!canPlayTrack(trackInfo.restriction)) + { + trackInfo.restriction = trackInfo.alternative[altIndex].restriction; + trackInfo.gid = trackInfo.alternative[altIndex].gid; + trackInfo.file = trackInfo.alternative[altIndex].file; + altIndex++; + CSPOT_LOG(info, "Trying alternative %d", altIndex); + } + auto trackId = trackInfo.gid.value(); + this->fileId = std::vector(); + + for (int x = 0; x < trackInfo.file.size(); x++) + { + if (trackInfo.file[x].format == configMan->format) + { + this->fileId = trackInfo.file[x].file_id.value(); + break; // If file found stop searching + } + } + + if (trackInfoReceived != nullptr) + { + CSPOT_LOG(info, "Calling %d", trackInfo.album.value().cover_group.value().image.size()); + TrackInfo simpleTrackInfo = { + .name = trackInfo.name.value(), + .album = trackInfo.album.value().name.value(), + .artist = trackInfo.artist[0].name.value(), + .imageUrl = "https://i.scdn.co/image/" + bytesToHexString(trackInfo.album.value().cover_group.value().image[0].file_id.value()) + }; + + trackInfoReceived(simpleTrackInfo); + } + + this->requestAudioKey(this->fileId, trackId, trackInfo.duration.value(), position_ms, isPaused); +} + +void SpotifyTrack::episodeInformationCallback(std::unique_ptr response, uint32_t position_ms, bool isPaused) +{ + if (this->fileId.size() != 0) + return; + CSPOT_LOG(debug, "Got to episode"); + CSPOT_ASSERT(response->parts.size() > 0, "response->parts.size() must be greater than 0"); + episodeInfo = decodePb(response->parts[0]); + + CSPOT_LOG(info, "--- Episode name: %s", episodeInfo.name.value().c_str()); + + this->fileId = std::vector(); + + // TODO: option to set file quality + for (int x = 0; x < episodeInfo.audio.size(); x++) + { + if (episodeInfo.audio[x].format == AudioFormat::OGG_VORBIS_96) + { + this->fileId = episodeInfo.audio[x].file_id.value(); + break; // If file found stop searching + } + } + + this->requestAudioKey(episodeInfo.gid.value(), this->fileId, episodeInfo.duration.value(), position_ms, isPaused); +} + +void SpotifyTrack::requestAudioKey(std::vector fileId, std::vector trackId, int32_t trackDuration, uint32_t position_ms, bool isPaused) +{ + audioKeyCallback audioKeyLambda = [=](bool success, std::vector res) { + if (success) + { + CSPOT_LOG(info, "Successfully got audio key!"); + auto audioKey = std::vector(res.begin() + 4, res.end()); + if (this->fileId.size() > 0) + { + this->audioStream = std::make_unique(this->fileId, audioKey, trackDuration, this->manager, position_ms, isPaused); + loadedTrackCallback(); + } + else + { + CSPOT_LOG(error, "Error while fetching audiokey..."); + } + } + else + { + auto code = ntohs(extract(res, 4)); + CSPOT_LOG(error, "Error while fetching audiokey, error code: %d", code); + } + }; + + this->manager->requestAudioKey(trackId, fileId, audioKeyLambda); +} diff --git a/components/spotify/cspot/src/TimeProvider.cpp b/components/spotify/cspot/src/TimeProvider.cpp new file mode 100644 index 00000000..4f2c4e96 --- /dev/null +++ b/components/spotify/cspot/src/TimeProvider.cpp @@ -0,0 +1,15 @@ +#include "TimeProvider.h" +#include "Utils.h" + +TimeProvider::TimeProvider() { +} + +void TimeProvider::syncWithPingPacket(const std::vector& pongPacket) { + // Spotify's timestamp is in seconds since unix time - convert to millis. + uint64_t remoteTimestamp = ((uint64_t) ntohl(extract(pongPacket, 0))) * 1000; + this->timestampDiff = remoteTimestamp - getCurrentTimestamp(); +} + +unsigned long long TimeProvider::getSyncedTimestamp() { + return getCurrentTimestamp() + this->timestampDiff; +} \ No newline at end of file diff --git a/components/spotify/cspot/src/TrackReference.cpp b/components/spotify/cspot/src/TrackReference.cpp new file mode 100644 index 00000000..1739b997 --- /dev/null +++ b/components/spotify/cspot/src/TrackReference.cpp @@ -0,0 +1,36 @@ +#include "TrackReference.h" +#include "Logger.h" + +TrackReference::TrackReference(TrackRef *ref) +{ + if (ref->gid.has_value()) + { + gid = ref->gid.value(); + } + else if (ref->uri.has_value()) + { + auto uri = ref->uri.value(); + auto idString = uri.substr(uri.find_last_of(":") + 1, uri.size()); + CSPOT_LOG(debug, "idString = %s", idString.c_str()); + gid = base62Decode(idString); + isEpisode = true; + } +} + +TrackReference::~TrackReference() +{ +} + +std::vector TrackReference::base62Decode(std::string uri) +{ + std::vector n = std::vector({0}); + + for (int x = 0; x < uri.size(); x++) + { + size_t d = alphabet.find(uri[x]); + n = bigNumMultiply(n, 62); + n = bigNumAdd(n, d); + } + + return n; +} diff --git a/components/spotify/cspot/src/Utils.cpp b/components/spotify/cspot/src/Utils.cpp new file mode 100644 index 00000000..242e19a3 --- /dev/null +++ b/components/spotify/cspot/src/Utils.cpp @@ -0,0 +1,133 @@ +#include "Utils.h" +#include +#include +#include +#include +#include +#include +#include + +unsigned long long getCurrentTimestamp() +{ + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); +} + +uint64_t hton64(uint64_t value) { + int num = 42; + if (*(char *)&num == 42) { + uint32_t high_part = htonl((uint32_t)(value >> 32)); + uint32_t low_part = htonl((uint32_t)(value & 0xFFFFFFFFLL)); + return (((uint64_t)low_part) << 32) | high_part; + } else { + return value; + } +} + +std::string bytesToHexString(std::vector& v) { + std::stringstream ss; + ss << std::hex << std::setfill('0'); + std::vector::const_iterator it; + + for (it = v.begin(); it != v.end(); it++) { + ss << std::setw(2) << static_cast(*it); + } + + return ss.str(); +} + +std::vector bigNumAdd(std::vector num, int n) +{ + auto carry = n; + for (int x = num.size() - 1; x >= 0; x--) + { + int res = num[x] + carry; + if (res < 256) + { + carry = 0; + num[x] = res; + } + else + { + // Carry the rest of the division + carry = res / 256; + num[x] = res % 256; + + // extend the vector at the last index + if (x == 0) + { + num.insert(num.begin(), carry); + return num; + } + } + } + + return num; +} + +std::vector bigNumMultiply(std::vector num, int n) +{ + auto carry = 0; + for (int x = num.size() - 1; x >= 0; x--) + { + int res = num[x] * n + carry; + if (res < 256) + { + carry = 0; + num[x] = res; + } + else + { + // Carry the rest of the division + carry = res / 256; + num[x] = res % 256; + + // extend the vector at the last index + if (x == 0) + { + num.insert(num.begin(), carry); + return num; + } + } + } + + return num; +} +unsigned char h2int(char c) +{ + if (c >= '0' && c <='9'){ + return((unsigned char)c - '0'); + } + if (c >= 'a' && c <='f'){ + return((unsigned char)c - 'a' + 10); + } + if (c >= 'A' && c <='F'){ + return((unsigned char)c - 'A' + 10); + } + return(0); +} + +std::string urlDecode(std::string str) +{ + std::string encodedString=""; + char c; + char code0; + char code1; + for (int i =0; i < str.length(); i++){ + c=str[i]; + if (c == '+'){ + encodedString+=' '; + }else if (c == '%') { + i++; + code0=str[i]; + i++; + code1=str[i]; + c = (h2int(code0) << 4) | h2int(code1); + encodedString+=c; + } else{ + + encodedString+=c; + } + } + + return encodedString; +} \ No newline at end of file diff --git a/components/spotify/cspot/src/ZeroconfAuthenticator.cpp b/components/spotify/cspot/src/ZeroconfAuthenticator.cpp new file mode 100644 index 00000000..9da1fc08 --- /dev/null +++ b/components/spotify/cspot/src/ZeroconfAuthenticator.cpp @@ -0,0 +1,139 @@ +#include "ZeroconfAuthenticator.h" +#include "JSONObject.h" +#include +#include +#include +#include +#include "Logger.h" +#include "ConfigJSON.h" + +ZeroconfAuthenticator::ZeroconfAuthenticator(authCallback callback, std::shared_ptr httpServer) { + this->gotBlobCallback = callback; + srand((unsigned int)time(NULL)); + + this->crypto = std::make_unique(); + this->crypto->dhInit(); + this->server = httpServer; +} + +void ZeroconfAuthenticator::registerHandlers() { + // Make it discoverable for spoti clients + registerZeroconf(); + auto getInfoHandler = [this](bell::HTTPRequest& request) { + CSPOT_LOG(info, "Got request for info"); + bell::HTTPResponse response = { + .connectionFd = request.connection, + .status = 200, + .body = this->buildJsonInfo(), + .contentType = "application/json", + }; + server->respond(response); + }; + + auto addUserHandler = [this](bell::HTTPRequest& request) { + BELL_LOG(info, "http", "Got request for adding user"); + bell::JSONObject obj; + obj["status"] = 101; + obj["spotifyError"] = 0; + obj["statusString"] = "ERROR-OK"; + + bell::HTTPResponse response = { + .connectionFd = request.connection, + .status = 200, + .body = obj.toString(), + .contentType = "application/json", + }; + server->respond(response); + + auto correctBlob = this->getParameterFromUrlEncoded(request.body, "blob"); + this->handleAddUser(request.queryParams); + }; + + BELL_LOG(info, "cspot", "Zeroconf registering handlers"); + this->server->registerHandler(bell::RequestType::GET, "/spotify_info", getInfoHandler); + this->server->registerHandler(bell::RequestType::POST, "/spotify_info", addUserHandler); +} + +void ZeroconfAuthenticator::registerZeroconf() +{ + const char* service = "_spotify-connect._tcp"; + +#ifdef ESP_PLATFORM + mdns_init(); + mdns_hostname_set("cspot"); + mdns_txt_item_t serviceTxtData[3] = { + {"VERSION", "1.0"}, + {"CPath", "/spotify_info"}, + {"Stack", "SP"} }; + mdns_service_add("cspot", "_spotify-connect", "_tcp", this->server->serverPort, serviceTxtData, 3); + +#else + DNSServiceRef ref = NULL; + TXTRecordRef txtRecord; + TXTRecordCreate(&txtRecord, 0, NULL); + TXTRecordSetValue(&txtRecord, "VERSION", 3, "1.0"); + TXTRecordSetValue(&txtRecord, "CPath", 13, "/spotify_info"); + TXTRecordSetValue(&txtRecord, "Stack", 2, "SP"); + DNSServiceRegister(&ref, 0, 0, (char*)informationString, service, NULL, NULL, htons(this->server->serverPort), TXTRecordGetLength(&txtRecord), TXTRecordGetBytesPtr(&txtRecord), NULL, NULL); + TXTRecordDeallocate(&txtRecord); +#endif +} + +std::string ZeroconfAuthenticator::getParameterFromUrlEncoded(std::string data, std::string param) +{ + auto startStr = data.substr(data.find("&" + param + "=") + param.size() + 2, data.size()); + return urlDecode(startStr.substr(0, startStr.find("&"))); +} + +void ZeroconfAuthenticator::handleAddUser(std::map& queryData) +{ + // Get all urlencoded params + auto username = queryData["userName"]; + auto blobString = queryData["blob"]; + auto clientKeyString = queryData["clientKey"]; + auto deviceName = queryData["deviceName"]; + + // client key and bytes are urlencoded + auto clientKeyBytes = crypto->base64Decode(clientKeyString); + auto blobBytes = crypto->base64Decode(blobString); + + // Generated secret based on earlier generated DH + auto secretKey = crypto->dhCalculateShared(clientKeyBytes); + + auto loginBlob = std::make_shared(); + + std::string deviceIdStr = deviceId; + + loginBlob->loadZeroconf(blobBytes, secretKey, deviceIdStr, username); + + gotBlobCallback(loginBlob); +} + +std::string ZeroconfAuthenticator::buildJsonInfo() +{ + // Encode publicKey into base64 + auto encodedKey = crypto->base64Encode(crypto->publicKey); + + bell::JSONObject obj; + obj["status"] = 101; + obj["statusString"] = "OK"; + obj["version"] = protocolVersion; + obj["spotifyError"] = 0; + obj["libraryVersion"] = swVersion; + obj["accountReq"] = "PREMIUM"; + obj["brandDisplayName"] = brandName; + obj["modelDisplayName"] = configMan->deviceName.c_str(); + obj["voiceSupport"] = "NO"; + obj["availability"] = ""; + obj["productID"] = 0; + obj["tokenType"] = "default"; + obj["groupStatus"] = "NONE"; + obj["resolverVersion"] = "0"; + obj["scope"] = "streaming,client-authorization-universal"; + obj["activeUser"] = ""; + obj["deviceID"] = deviceId; + obj["remoteName"] = configMan->deviceName.c_str(); + obj["publicKey"] = encodedKey; + obj["deviceType"] = "SPEAKER"; + return obj.toString(); +} diff --git a/components/spotify/cspot_private.h b/components/spotify/cspot_private.h new file mode 100644 index 00000000..b7bdb49d --- /dev/null +++ b/components/spotify/cspot_private.h @@ -0,0 +1,26 @@ +/* + * (c) Philippe 2020, philippe_44@outlook.com + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + * + */ + +#pragma once + +#include "cspot_sink.h" + +struct cspot_s; + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct cspot_s* cspot_create(const char *name, cspot_cmd_cb_t cmd_cb, cspot_data_cb_t data_cb); +bool cspot_cmd(struct cspot_s *ctx, cspot_event_t event, void *param); + +#ifdef __cplusplus +} +#endif + diff --git a/components/spotify/cspot_sink.c b/components/spotify/cspot_sink.c new file mode 100644 index 00000000..6a5b6c22 --- /dev/null +++ b/components/spotify/cspot_sink.c @@ -0,0 +1,204 @@ +#include +#include +#include +#include + +#include "mdns.h" +#include "nvs.h" +#include "tcpip_adapter.h" +// IDF-V4++ #include "esp_netif.h" +#include "esp_log.h" +#include "esp_console.h" +#include "esp_pthread.h" +#include "esp_system.h" +#include "freertos/timers.h" +#include "platform_config.h" +#include "audio_controls.h" +#include "display.h" +#include "accessors.h" +#include "cspot_private.h" +#include "cspot_sink.h" + +static EXT_RAM_ATTR struct cspot_cb_s { + cspot_cmd_vcb_t cmd; + cspot_data_cb_t data; +} cspot_cbs; + +static const char TAG[] = "cspot"; +static struct cspot_s *cspot; +static cspot_cmd_vcb_t cmd_handler_chain; + +static void cspot_volume_up(bool pressed) { + if (!pressed) return; + cspot_cmd(cspot, CSPOT_VOLUME_UP, NULL); + ESP_LOGI(TAG, "CSpot volume up"); +} + +static void cspot_volume_down(bool pressed) { + if (!pressed) return; + cspot_cmd(cspot, CSPOT_VOLUME_DOWN, NULL); + ESP_LOGI(TAG, "CSpot volume down"); +} + +static void cspot_toggle(bool pressed) { + if (!pressed) return; + cspot_cmd(cspot, CSPOT_TOGGLE, NULL); + ESP_LOGI(TAG, "CSpot play/pause"); +} + +static void cspot_pause(bool pressed) { + if (!pressed) return; + cspot_cmd(cspot, CSPOT_PAUSE, NULL); + ESP_LOGI(TAG, "CSpot pause"); +} + +static void cspot_play(bool pressed) { + if (!pressed) return; + cspot_cmd(cspot, CSPOT_PLAY, NULL); + ESP_LOGI(TAG, "CSpot play"); +} + +static void cspot_stop(bool pressed) { + if (!pressed) return; + cspot_cmd(cspot, CSPOT_STOP, NULL); + ESP_LOGI(TAG, "CSpot stop"); +} + +static void cspot_prev(bool pressed) { + if (!pressed) return; + cspot_cmd(cspot, CSPOT_PREV, NULL); + ESP_LOGI(TAG, "CSpot previous"); +} + +static void cspot_next(bool pressed) { + if (!pressed) return; + cspot_cmd(cspot, CSPOT_NEXT, NULL); + ESP_LOGI(TAG, "CSpot next"); +} + +const static actrls_t controls = { + NULL, // power + cspot_volume_up, cspot_volume_down, // volume up, volume down + cspot_toggle, cspot_play, // toggle, play + cspot_pause, cspot_stop, // pause, stop + NULL, NULL, // rew, fwd + cspot_prev, cspot_next, // prev, next + NULL, NULL, NULL, NULL, // left, right, up, down + NULL, NULL, NULL, NULL, NULL, NULL, // pre1-6 + cspot_volume_down, cspot_volume_up, cspot_toggle// knob left, knob_right, knob push +}; + +/**************************************************************************************** + * Command handler + */ +static bool cmd_handler(cspot_event_t event, ...) { + va_list args; + + va_start(args, event); + + // handle audio event and stop if forbidden + if (!cmd_handler_chain(event, args)) { + va_end(args); + return false; + } + + // now handle events for display + switch(event) { + case CSPOT_SETUP: + actrls_set(controls, false, NULL, actrls_ir_action); + displayer_control(DISPLAYER_ACTIVATE, "SPOTIFY"); + break; + case CSPOT_PLAY: + displayer_control(DISPLAYER_TIMER_RUN); + break; + case CSPOT_PAUSE: + displayer_control(DISPLAYER_TIMER_PAUSE); + break; + case CSPOT_DISC: + actrls_unset(); + displayer_control(DISPLAYER_SUSPEND); + break; + case CSPOT_SEEK: + displayer_timer(DISPLAYER_ELAPSED, va_arg(args, int), -1); + break; + case CSPOT_TRACK: { + uint32_t sample_rate = va_arg(args, uint32_t); + char *artist = va_arg(args, char*), *album = va_arg(args, char*), *title = va_arg(args, char*); + displayer_metadata(artist, album, title); + displayer_timer(DISPLAYER_ELAPSED, 0, -1); + break; + } + // nothing to do on CSPOT_FLUSH + default: + break; + } + + va_end(args); + + return true; +} + +/**************************************************************************************** + * CSpot sink de-initialization + */ +void cspot_sink_deinit(void) { + mdns_free(); +} + +/**************************************************************************************** + * CSpot sink startup + */ +static bool cspot_sink_start(cspot_cmd_vcb_t cmd_cb, cspot_data_cb_t data_cb) { + const char *hostname = NULL; + tcpip_adapter_ip_info_t ipInfo = { }; + tcpip_adapter_if_t ifs[] = { TCPIP_ADAPTER_IF_ETH, TCPIP_ADAPTER_IF_STA, TCPIP_ADAPTER_IF_AP }; + + // get various IP info + for (int i = 0; i < sizeof(ifs) / sizeof(tcpip_adapter_if_t); i++) + if (tcpip_adapter_get_ip_info(ifs[i], &ipInfo) == ESP_OK && ipInfo.ip.addr != IPADDR_ANY) { + tcpip_adapter_get_hostname(ifs[i], &hostname); + break; + } + + if (!hostname) { + ESP_LOGI(TAG, "No hostname/IP found, can't start CSpot (will retry)"); + return false; + } + + cmd_handler_chain = cmd_cb; + cspot = cspot_create(hostname, cmd_handler, data_cb); + + return true; +} + +/**************************************************************************************** + * CSpot sink timer handler + */ +static void cspot_start_handler( TimerHandle_t xTimer ) { + if (cspot_sink_start(cspot_cbs.cmd, cspot_cbs.data)) { + xTimerDelete(xTimer, portMAX_DELAY); + } +} + +/**************************************************************************************** + * CSpot sink initialization + */ +void cspot_sink_init(cspot_cmd_vcb_t cmd_cb, cspot_data_cb_t data_cb) { + if (!cspot_sink_start(cmd_cb, data_cb)) { + cspot_cbs.cmd = cmd_cb; + cspot_cbs.data = data_cb; + TimerHandle_t timer = xTimerCreate("cspotStart", 5000 / portTICK_RATE_MS, pdTRUE, NULL, cspot_start_handler); + xTimerStart(timer, portMAX_DELAY); + ESP_LOGI(TAG, "Delaying CSPOT start"); + } +} + +/**************************************************************************************** + * CSpot forced disconnection + */ +void cspot_disconnect(void) { + ESP_LOGI(TAG, "forced disconnection"); + displayer_control(DISPLAYER_SHUTDOWN); + cspot_cmd(cspot, CSPOT_FLUSH, NULL); + actrls_unset(); +} diff --git a/components/spotify/cspot_sink.h b/components/spotify/cspot_sink.h new file mode 100644 index 00000000..9916d3ea --- /dev/null +++ b/components/spotify/cspot_sink.h @@ -0,0 +1,44 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +// STOP means remove playlist, FLUSH means flush audio buffer, DISC means bye-bye +typedef enum { CSPOT_SETUP, CSPOT_DISC, CSPOT_FLUSH, CSPOT_STOP, CSPOT_PLAY, CSPOT_PAUSE, CSPOT_SEEK, CSPOT_TRACK, + CSPOT_VOLUME, CSPOT_VOLUME_UP, CSPOT_VOLUME_DOWN, CSPOT_NEXT, CSPOT_PREV, CSPOT_TOGGLE, +} cspot_event_t; + +typedef bool (*cspot_cmd_cb_t)(cspot_event_t event, ...); +typedef bool (*cspot_cmd_vcb_t)(cspot_event_t event, va_list args); +typedef void (*cspot_data_cb_t)(const uint8_t *data, uint32_t len); + +/** + * @brief init sink mode (need to be provided) + */ +void cspot_sink_init(cspot_cmd_vcb_t cmd_cb, cspot_data_cb_t data_cb); + +/** + * @brief deinit sink mode (need to be provided) + */ +void cspot_sink_deinit(void); + +/** + * @brief force disconnection + */ +void cspot_disconnect(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/spotify/linker.lf b/components/spotify/linker.lf new file mode 100644 index 00000000..7a3292f6 --- /dev/null +++ b/components/spotify/linker.lf @@ -0,0 +1,4 @@ +[mapping:cspot] +archive: libcspot.a +entries: + * (extram_bss) \ No newline at end of file diff --git a/components/squeezelite-ota/squeezelite-ota.c b/components/squeezelite-ota/squeezelite-ota.c index 96109c92..d98f4867 100644 --- a/components/squeezelite-ota/squeezelite-ota.c +++ b/components/squeezelite-ota/squeezelite-ota.c @@ -30,7 +30,6 @@ #include "esp_spi_flash.h" #include "sdkconfig.h" #include "messaging.h" -#include "trace.h" #include "esp_ota_ops.h" #include "display.h" #include "gds.h" @@ -39,6 +38,7 @@ #include "platform_esp32.h" #include "lwip/sockets.h" #include "globdefs.h" +#include "tools.h" extern const char * get_certificate(); #define IF_DISPLAY(x) if(display) { x; } diff --git a/components/squeezelite/CMakeLists.txt b/components/squeezelite/CMakeLists.txt index 391774a5..d7422b48 100644 --- a/components/squeezelite/CMakeLists.txt +++ b/components/squeezelite/CMakeLists.txt @@ -8,26 +8,18 @@ idf_component_register( SRC_DIRS . external ac101 tas57xx wm8978 platform_config driver_bt services + spotify raop display tools audio - EMBED_FILES vu.data + EMBED_FILES vu_s.data arrow.data ) -set_source_files_properties(mad.c +set_source_files_properties(mad.c pcm.c flac.c alac.c helix-aac.c vorbis.c opus.c PROPERTIES COMPILE_FLAGS -Wno-maybe-uninitialized ) -set_source_files_properties(pcm.c - PROPERTIES COMPILE_FLAGS - -Wno-maybe-uninitialized -) - -set_source_files_properties(flac.c - PROPERTIES COMPILE_FLAGS - -Wno-maybe-uninitialized -) add_definitions(-DLINKALL -DLOOPBACK -DNO_FAAD -DEMBEDDED -DTREMOR_ONLY -DCUSTOM_VERSION=${BUILD_NUMBER}) diff --git a/components/squeezelite/adac.h b/components/squeezelite/adac.h index 4a61ad10..5a3a8cf6 100644 --- a/components/squeezelite/adac.h +++ b/components/squeezelite/adac.h @@ -33,6 +33,7 @@ extern const struct adac_s dac_external; int adac_init(char *config, int i2c_port); void adac_deinit(void); +esp_err_t adac_write(int i2c_addr, uint8_t reg, uint8_t *data, size_t count); esp_err_t adac_write_byte(int i2c_addr, uint8_t reg, uint8_t val); esp_err_t adac_write_word(int i2c_addr, uint8_t reg, uint16_t val); uint8_t adac_read_byte(int i2c_addr, uint8_t reg); diff --git a/components/squeezelite/adac_core.c b/components/squeezelite/adac_core.c index 9f746f99..27db6f58 100644 --- a/components/squeezelite/adac_core.c +++ b/components/squeezelite/adac_core.c @@ -17,6 +17,11 @@ #include "esp_log.h" #include "adac.h" +#define PARSE_PARAM(S,P,C,V) do { \ + char *__p; \ + if ((__p = strcasestr(S, P)) && (__p = strchr(__p, C))) V = atoi(__p+1); \ +} while (0) + static const char TAG[] = "DAC core"; static int i2c_port = -1; @@ -46,9 +51,9 @@ int adac_init(char *config, int i2c_port_num) { .master.clk_speed = 250000, }; - if ((p = strcasestr(config, "i2c")) != NULL) i2c_addr = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(config, "sda")) != NULL) i2c_config.sda_io_num = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(config, "scl")) != NULL) i2c_config.scl_io_num = atoi(strchr(p, '=') + 1); + PARSE_PARAM(config, "i2c", '=', i2c_addr); + PARSE_PARAM(config, "sda", '=', i2c_config.sda_io_num); + PARSE_PARAM(config, "scl", '=', i2c_config.scl_io_num); if (i2c_config.sda_io_num == -1 || i2c_config.scl_io_num == -1) { ESP_LOGW(TAG, "DAC does not use i2c"); @@ -150,8 +155,7 @@ uint16_t adac_read_word(int i2c_addr, uint8_t reg) { /**************************************************************************************** * */ -esp_err_t adac_write_word(int i2c_addr, uint8_t reg, uint16_t val) -{ +esp_err_t adac_write_word(int i2c_addr, uint8_t reg, uint16_t val) { uint8_t data[] = { i2c_addr << 1, reg, val >> 8, val & 0xff }; @@ -169,4 +173,26 @@ esp_err_t adac_write_word(int i2c_addr, uint8_t reg, uint16_t val) } return ret; -} \ No newline at end of file +} + +/**************************************************************************************** + * + */ +esp_err_t adac_write(int i2c_addr, uint8_t reg, uint8_t *data, size_t count) { + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + + i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK); + i2c_master_write_byte(cmd, reg, I2C_MASTER_NACK); + i2c_master_write(cmd, data, count, I2C_MASTER_NACK); + + i2c_master_stop(cmd); + esp_err_t ret = i2c_master_cmd_begin(i2c_port, cmd, 200 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + + if (ret != ESP_OK) { + ESP_LOGW(TAG, "I2C write failed"); + } + + return ret; +} \ No newline at end of file diff --git a/components/squeezelite/arrow.data b/components/squeezelite/arrow.data new file mode 100644 index 00000000..678712e1 Binary files /dev/null and b/components/squeezelite/arrow.data differ diff --git a/components/squeezelite/buffer.c b/components/squeezelite/buffer.c index d8bb48f4..a2e36743 100644 --- a/components/squeezelite/buffer.c +++ b/components/squeezelite/buffer.c @@ -73,13 +73,11 @@ void _buf_flush(struct buffer *buf) { // adjust buffer to multiple of mod bytes so reading in multiple always wraps on frame boundary void buf_adjust(struct buffer *buf, size_t mod) { - size_t size; mutex_lock(buf->mutex); - size = ((unsigned)(buf->base_size / mod)) * mod; - buf->readp = buf->buf; - buf->writep = buf->buf; - buf->wrap = buf->buf + size; - buf->size = size; + buf->base_size = ((size_t)(buf->size / mod)) * mod; + buf->readp = buf->writep = buf->buf; + buf->wrap = buf->buf + buf->base_size; + buf->size = buf->base_size; mutex_unlock(buf->mutex); } @@ -90,16 +88,23 @@ void _buf_resize(struct buffer *buf, size_t size) { buf->buf = malloc(size); if (!buf->buf) { size = buf->size; - buf->buf= malloc(size); - if (!buf->buf) { - size = 0; - } + buf->buf = malloc(size); + if (!buf->buf) size = 0; } - buf->readp = buf->buf; - buf->writep = buf->buf; + buf->writep = buf->readp = buf->buf; buf->wrap = buf->buf + size; - buf->size = size; - buf->base_size = size; + buf->true_size = buf->base_size = buf->size = size; +} + +size_t _buf_limit(struct buffer *buf, size_t limit) { + if (limit) { + buf->size = limit; + buf->readp = buf->writep = buf->buf; + } else { + buf->size = buf->base_size; + } + buf->wrap = buf->buf + buf->size; + return buf->base_size - buf->size; } void _buf_unwrap(struct buffer *buf, size_t cont) { @@ -130,7 +135,9 @@ void _buf_unwrap(struct buffer *buf, size_t cont) { if (len > by) { memmove(buf->buf, buf->buf + by, len - by); buf->writep -= by; - } else buf->writep += buf->size - by; + } else { + buf->writep += buf->size - by; + } return; } @@ -157,8 +164,7 @@ void buf_init(struct buffer *buf, size_t size) { buf->readp = buf->buf; buf->writep = buf->buf; buf->wrap = buf->buf + size; - buf->size = size; - buf->base_size = size; + buf->true_size = buf->base_size = buf->size = size; mutex_create_p(buf->mutex); } @@ -166,8 +172,7 @@ void buf_destroy(struct buffer *buf) { if (buf->buf) { free(buf->buf); buf->buf = NULL; - buf->size = 0; - buf->base_size = 0; + buf->size = buf->base_size = buf->true_size = 0; mutex_destroy(buf->mutex); } } diff --git a/components/squeezelite/controls.c b/components/squeezelite/controls.c index a0a8a586..682e6178 100644 --- a/components/squeezelite/controls.c +++ b/components/squeezelite/controls.c @@ -218,7 +218,6 @@ static void notify(in_addr_t ip, u16_t hport, u16_t cport) { // close existing CLI connection and open new one if (cli_sock >= 0) closesocket(cli_sock); - cli_sock = socket(AF_INET, SOCK_STREAM, 0); connect_cli_socket(); LOG_INFO("notified server %s hport %hu cport %hu", inet_ntoa(ip), hport, cport); diff --git a/components/squeezelite/decode_external.c b/components/squeezelite/decode_external.c index 87a04cb7..9adb04ef 100644 --- a/components/squeezelite/decode_external.c +++ b/components/squeezelite/decode_external.c @@ -11,24 +11,20 @@ #include "platform_config.h" #include "squeezelite.h" -#include "bt_app_sink.h" -#include "raop_sink.h" #include -#define LOCK_O mutex_lock(outputbuf->mutex) -#define UNLOCK_O mutex_unlock(outputbuf->mutex) -#define LOCK_D mutex_lock(decode.mutex); -#define UNLOCK_D mutex_unlock(decode.mutex); - -enum { DECODE_BT = 1, DECODE_RAOP }; - -extern struct outputstate output; -extern struct decodestate decode; -extern struct buffer *outputbuf; -// this is the only system-wide loglevel variable -extern log_level loglevel; - +#if CONFIG_BT_SINK +#include "bt_app_sink.h" static bool enable_bt_sink; +#endif + +#if CONFIG_CSPOT_SINK +#include "cspot_sink.h" +static bool enable_cspot; +#endif + +#if CONFIG_AIRPLAY_SINK +#include "raop_sink.h" static bool enable_airplay; #define RAOP_OUTPUT_SIZE (((RAOP_SAMPLE_RATE * BYTES_PER_FRAME * 2 * 120) / 100) & ~BYTES_PER_FRAME) @@ -44,6 +40,22 @@ static EXT_RAM_ATTR struct { s32_t len; u32_t start_time, playtime; } raop_sync; +#endif + +static bool abort_sink ; + +#define LOCK_O mutex_lock(outputbuf->mutex) +#define UNLOCK_O mutex_unlock(outputbuf->mutex) +#define LOCK_D mutex_lock(decode.mutex); +#define UNLOCK_D mutex_unlock(decode.mutex); + +enum { DECODE_BT = 1, DECODE_RAOP, DECODE_CSPOT }; + +extern struct outputstate output; +extern struct decodestate decode; +extern struct buffer *outputbuf; +// this is the only system-wide loglevel variable +extern log_level loglevel; /**************************************************************************************** * Common sink data handler @@ -58,11 +70,12 @@ static void sink_data_handler(const uint8_t *data, uint32_t len) LOG_SDEBUG("Cannot use external sink while LMS is controlling player"); return; } - - // there will always be room at some point - while (len) { - LOCK_O; + LOCK_O; + abort_sink = false; + + // there will always be room at some point + while (len && wait && !abort_sink) { bytes = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / (BYTES_PER_FRAME / 4); bytes = min(len, bytes); #if BYTES_PER_FRAME == 4 @@ -81,11 +94,14 @@ static void sink_data_handler(const uint8_t *data, uint32_t len) len -= bytes; data += bytes; - UNLOCK_O; - // allow i2s to empty the buffer if needed - if (len && !space && wait--) usleep(20000); + if (len && !space) { + wait--; + UNLOCK_O; usleep(50000); LOCK_O; + } } + + UNLOCK_O; if (!wait) { LOG_WARN("Waited too long, dropping frames"); @@ -95,12 +111,12 @@ static void sink_data_handler(const uint8_t *data, uint32_t len) /**************************************************************************************** * BT sink command handler */ - +#if CONFIG_BT_SINK static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args) { // don't LOCK_O as there is always a chance that LMS takes control later anyway if (output.external != DECODE_BT && output.state > OUTPUT_STOPPED) { - LOG_WARN("Cannot use BT sink while LMS/AirPlay is controlling player"); + LOG_WARN("Cannot use BT sink while LMS/AirPlay/CSpot are controlling player"); return false; } @@ -110,11 +126,12 @@ static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args) switch(cmd) { case BT_SINK_AUDIO_STARTED: + _buf_flush(outputbuf); + _buf_limit(outputbuf, 0); output.next_sample_rate = output.current_sample_rate = va_arg(args, u32_t); output.external = DECODE_BT; output.state = OUTPUT_STOPPED; output.frames_played = 0; - _buf_flush(outputbuf); if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR; LOG_INFO("BT sink started"); break; @@ -127,17 +144,18 @@ static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args) break; case BT_SINK_PLAY: output.state = OUTPUT_RUNNING; - LOG_INFO("BT playing"); + LOG_INFO("BT play"); break; case BT_SINK_STOP: _buf_flush(outputbuf); output.state = OUTPUT_STOPPED; output.stop_time = gettime_ms(); - LOG_INFO("BT stopped"); + abort_sink = true; + LOG_INFO("BT stop"); break; case BT_SINK_PAUSE: output.stop_time = gettime_ms(); - LOG_INFO("BT paused, just silence"); + LOG_INFO("BT pause, just silence"); break; case BT_SINK_RATE: output.next_sample_rate = output.current_sample_rate = va_arg(args, u32_t); @@ -158,10 +176,12 @@ static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args) return true; } +#endif /**************************************************************************************** * raop sink data handler */ +#if CONFIG_AIRPLAY_SINK static void raop_sink_data_handler(const uint8_t *data, uint32_t len, u32_t playtime) { raop_sync.playtime = playtime; @@ -177,7 +197,7 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args) { // don't LOCK_O as there is always a chance that LMS takes control later anyway if (output.external != DECODE_RAOP && output.state > OUTPUT_STOPPED) { - LOG_WARN("Cannot use Airplay sink while LMS/BT is controlling player"); + LOG_WARN("Cannot use Airplay sink while LMS/BT/CSpot are controlling player"); return false; } @@ -237,15 +257,21 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args) break; } - case RAOP_SETUP: - // we need a fair bit of space for RTP process - _buf_resize(outputbuf, RAOP_OUTPUT_SIZE); + case RAOP_SETUP: { + uint8_t **buffer = va_arg(args, uint8_t**); + size_t *size = va_arg(args, size_t*); + + // steal buffer tail from outputbuf but do not reallocate + *size = _buf_limit(outputbuf, RAOP_OUTPUT_SIZE); + *buffer = outputbuf->writep + RAOP_OUTPUT_SIZE; + output.frames_played = 0; output.external = DECODE_RAOP; output.state = OUTPUT_STOPPED; if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR; LOG_INFO("resizing buffer %u", outputbuf->size); break; + } case RAOP_STREAM: LOG_INFO("Stream", NULL); raop_state = event; @@ -257,11 +283,11 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args) break; case RAOP_STOP: case RAOP_FLUSH: - if (event == RAOP_FLUSH) { LOG_INFO("Flush", NULL); } - else { LOG_INFO("Stop", NULL); } + LOG_INFO("%s", event == RAOP_FLUSH ? "Flush" : "Stop"); + _buf_flush(outputbuf); raop_state = event; - _buf_flush(outputbuf); if (output.state > OUTPUT_STOPPED) output.state = OUTPUT_STOPPED; + abort_sink = true; output.frames_played = 0; output.stop_time = gettime_ms(); break; @@ -292,6 +318,83 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args) UNLOCK_D; return true; } +#endif + +/**************************************************************************************** + * cspot sink command handler + */ +#if CONFIG_CSPOT_SINK +static bool cspot_cmd_handler(cspot_event_t cmd, va_list args) +{ + // don't LOCK_O as there is always a chance that LMS takes control later anyway + if (output.external != DECODE_CSPOT && output.state > OUTPUT_STOPPED) { + LOG_WARN("Cannot use CSpot sink while LMS/BT/Airplay are controlling player"); + return false; + } + + LOCK_D; + + if (cmd != CSPOT_VOLUME) LOCK_O; + + switch(cmd) { + case CSPOT_SETUP: + output.current_sample_rate = output.next_sample_rate = va_arg(args, u32_t); + output.external = DECODE_CSPOT; + output.frames_played = 0; + output.state = OUTPUT_STOPPED; + _buf_flush(outputbuf); + _buf_limit(outputbuf, 0); + if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR; + LOG_INFO("CSpot connected"); + break; + case CSPOT_DISC: + _buf_flush(outputbuf); + abort_sink = true; + output.state = OUTPUT_STOPPED; + output.stop_time = gettime_ms(); + LOG_INFO("CSpot disconnected"); + break; + case CSPOT_TRACK: + LOG_INFO("CSpot sink new track rate %d", output.next_sample_rate); + break; + case CSPOT_PLAY: + output.state = OUTPUT_RUNNING; + LOG_INFO("CSpot play"); + break; + case CSPOT_SEEK: + //TODO: can it be merged with flush (shall we stop) + _buf_flush(outputbuf); + abort_sink = true; + LOG_INFO("CSpot seek by %d", va_arg(args, int)); + break; + case CSPOT_FLUSH: + _buf_flush(outputbuf); + abort_sink = true; + __attribute__ ((fallthrough)); + case CSPOT_PAUSE: + output.state = OUTPUT_STOPPED; + output.stop_time = gettime_ms(); + LOG_INFO("CSpot pause/flush"); + break; + case CSPOT_VOLUME: { + u32_t volume = va_arg(args, u32_t); + LOG_INFO("CSpot volume %u", volume); + //volume = 65536 * powf(volume / 32768.0f, 3); + // TODO spotify seems to volume normalize crazy high + volume = 4096 * powf(volume / 32768.0f, 3); + set_volume(volume, volume); + break; + default: + break; + } + } + + if (cmd != CSPOT_VOLUME) UNLOCK_O; + UNLOCK_D; + + return true; +} +#endif /**************************************************************************************** * We provide the generic codec register option @@ -299,16 +402,28 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args) void register_external(void) { char *p; +#if CONFIG_BT_SINK if ((p = config_alloc_get(NVS_TYPE_STR, "enable_bt_sink")) != NULL) { enable_bt_sink = strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0; free(p); } +#endif +#if CONFIG_AIRPLAY_SINK if ((p = config_alloc_get(NVS_TYPE_STR, "enable_airplay")) != NULL) { enable_airplay = strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0; free(p); } - +#endif + +#if CONFIG_CSPOT_SINK + if ((p = config_alloc_get(NVS_TYPE_STR, "enable_cspot")) != NULL) { + enable_cspot = strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0; + free(p); + } +#endif + +#if CONFIG_BT_SINK if (!strcasestr(output.device, "BT ") ) { if(enable_bt_sink){ bt_sink_init(bt_sink_cmd_handler, sink_data_handler); @@ -317,32 +432,61 @@ void register_external(void) { } else { LOG_WARN("Cannot be a BT sink and source"); } +#endif +#if CONFIG_AIRPLAY_SINK if (enable_airplay){ raop_sink_init(raop_sink_cmd_handler, raop_sink_data_handler); LOG_INFO("Initializing AirPlay sink"); } +#endif + +#if CONFIG_CSPOT_SINK + if (enable_cspot){ + cspot_sink_init(cspot_cmd_handler, sink_data_handler); + LOG_INFO("Initializing CSpot sink"); + } +#endif } void deregister_external(void) { +#if CONFIG_BT_SINK if (!strcasestr(output.device, "BT ") && enable_bt_sink) { LOG_INFO("Stopping BT sink"); bt_sink_deinit(); } +#endif +#if CONFIG_AIRPLAY_SINK if (enable_airplay){ LOG_INFO("Stopping AirPlay sink"); raop_sink_deinit(); } +#endif +#if CONFIG_CSPOT_SINK + if (enable_cspot){ + LOG_INFO("Stopping CSpot sink"); + cspot_sink_deinit(); + } +#endif } void decode_restore(int external) { switch (external) { +#if CONFIG_BT_SINK case DECODE_BT: bt_disconnect(); break; +#endif +#if CONFIG_AIRPLAY_SINK case DECODE_RAOP: raop_disconnect(); raop_state = RAOP_STOP; break; +#endif +#if CONFIG_CSPOT_SINK + case DECODE_CSPOT: + cspot_disconnect(); + break; +#endif } } diff --git a/components/squeezelite/displayer.c b/components/squeezelite/displayer.c index 36d6c650..58b7730b 100644 --- a/components/squeezelite/displayer.c +++ b/components/squeezelite/displayer.c @@ -146,6 +146,7 @@ static uint32_t *grayMap; #define VU_WIDTH 160 #define VU_HEIGHT SB_HEIGHT #define VU_COUNT 48 +#define ARROW_WIDTH 11 #define DISPLAY_BW 20000 @@ -210,7 +211,12 @@ static EXT_RAM_ATTR struct { struct bar_s bars[MAX_BARS] ; } led_visu; -extern const uint8_t vu_bitmap[] asm("_binary_vu_data_start"); +static EXT_RAM_ATTR uint8_t vu_bitmap[VU_WIDTH * VU_HEIGHT]; +extern const uint8_t vu_base[] asm("_binary_vu_s_data_start"); +extern const struct { + uint8_t offset; + uint8_t data[VU_HEIGHT * ARROW_WIDTH]; +} vu_arrow[VU_COUNT] asm("_binary_arrow_data_start"); #define ANIM_NONE 0x00 #define ANIM_TRANSITION 0x01 // A transition animation has finished @@ -328,6 +334,9 @@ bool sb_displayer_init(void) { visu.bar_gap = 1; visu.back.frame = calloc(1, (displayer.width * displayer.height) / 8); + // prepare the VU raw data in PSRAM + memcpy(vu_bitmap, vu_base, sizeof(vu_bitmap)); + // size scroller (width + current screen) scroller.scroll.max = (displayer.width * displayer.height / 8) * (15 + 1); scroller.scroll.frame = malloc(scroller.scroll.max); @@ -601,9 +610,13 @@ static void vfdc_handler( u8_t *_data, int bytes_read) { /**************************************************************************************** * Display VU-Meter (lots of hard-coding) */ -void draw_VU(struct GDS_Device * display, const uint8_t *data, int level, int x, int y, int width, bool rotate) { +void draw_VU(struct GDS_Device * display, int level, int x, int y, int width, bool rotate) { // VU data is by columns and vertical flip to allow block offset - data += level * VU_WIDTH * VU_HEIGHT; + uint8_t *data = vu_bitmap; + int offset = level > 0 ? vu_arrow[level].offset * VU_HEIGHT : 0; + + // place the arrow in base VU + memcpy(data + offset, vu_arrow[level].data, sizeof(vu_arrow[level].data)); // adjust to current display window if (width > VU_WIDTH) { @@ -649,6 +662,9 @@ void draw_VU(struct GDS_Device * display, const uint8_t *data, int level, int x, } } + // restore base VU + memcpy(vu_bitmap + offset, vu_base + offset, sizeof(vu_arrow[level].data)); + // need to manually set dirty flag as DrawPixel does not do it GDS_SetDirty(display); } @@ -978,15 +994,15 @@ void visu_draw(void) { } } else if (displayer.width / 2 >= 3 * VU_WIDTH / 4) { if (visu.rotate) { - draw_VU(display, vu_bitmap, visu.bars[0].current, 0, visu.row, visu.height / 2, visu.rotate); - draw_VU(display, vu_bitmap, visu.bars[1].current, 0, visu.row + visu.height / 2, visu.height / 2, visu.rotate); + draw_VU(display, visu.bars[0].current, 0, visu.row, visu.height / 2, visu.rotate); + draw_VU(display, visu.bars[1].current, 0, visu.row + visu.height / 2, visu.height / 2, visu.rotate); } else { - draw_VU(display, vu_bitmap, visu.bars[0].current, 0, visu.row, visu.width / 2, visu.rotate); - draw_VU(display, vu_bitmap, visu.bars[1].current, visu.width / 2, visu.row, visu.width / 2, visu.rotate); + draw_VU(display, visu.bars[0].current, 0, visu.row, visu.width / 2, visu.rotate); + draw_VU(display, visu.bars[1].current, visu.width / 2, visu.row, visu.width / 2, visu.rotate); } } else { int level = (visu.bars[0].current + visu.bars[1].current) / 2; - draw_VU(display, vu_bitmap, level, 0, visu.row, visu.rotate ? visu.height : visu.width, visu.rotate); + draw_VU(display, level, 0, visu.row, visu.rotate ? visu.height : visu.width, visu.rotate); } } diff --git a/components/squeezelite/external/dac_external.c b/components/squeezelite/external/dac_external.c index c274b065..5502d09f 100644 --- a/components/squeezelite/external/dac_external.c +++ b/components/squeezelite/external/dac_external.c @@ -20,8 +20,8 @@ static const char TAG[] = "DAC external"; -static void speaker(bool active) { } -static void headset(bool active) { } +static void speaker(bool active); +static void headset(bool active); static bool volume(unsigned left, unsigned right) { return false; } static void power(adac_power_e mode); static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config); @@ -95,6 +95,22 @@ static void power(adac_power_e mode) { else i2c_json_execute("poweron"); } +/**************************************************************************************** + * speaker + */ +static void speaker(bool active) { + if (active) i2c_json_execute("speakeron"); + else i2c_json_execute("speakeroff"); +} + +/**************************************************************************************** + * headset + */ +static void headset(bool active) { + if (active) i2c_json_execute("headseton"); + else i2c_json_execute("headsetoff"); +} + /**************************************************************************************** * */ @@ -104,25 +120,40 @@ bool i2c_json_execute(char *set) { if (!json_set) return true; - cJSON_ArrayForEach(item, json_set) - { + cJSON_ArrayForEach(item, json_set) { cJSON *reg = cJSON_GetObjectItemCaseSensitive(item, "reg"); cJSON *val = cJSON_GetObjectItemCaseSensitive(item, "val"); - cJSON *mode = cJSON_GetObjectItemCaseSensitive(item, "mode"); + + if (cJSON_IsArray(val)) { + cJSON *value; + uint8_t *data = malloc(cJSON_GetArraySize(val)); + int count = 0; + + if (!data) continue; + + cJSON_ArrayForEach(value, val) { + data[count++] = value->valueint; + } + + adac_write(i2c_addr, reg->valueint, data, count); + free(data); + } else { + cJSON *mode = cJSON_GetObjectItemCaseSensitive(item, "mode"); - if (!reg || !val) continue; + if (!reg || !val) continue; - if (!mode) { - adac_write_byte(i2c_addr, reg->valueint, val->valueint); - } else if (!strcasecmp(mode->valuestring, "or")) { - uint8_t data = adac_read_byte(i2c_addr,reg->valueint); - data |= (uint8_t) val->valueint; - adac_write_byte(i2c_addr, reg->valueint, data); - } else if (!strcasecmp(mode->valuestring, "and")) { - uint8_t data = adac_read_byte(i2c_addr, reg->valueint); - data &= (uint8_t) val->valueint; - adac_write_byte(i2c_addr, reg->valueint, data); - } + if (!mode) { + adac_write_byte(i2c_addr, reg->valueint, val->valueint); + } else if (!strcasecmp(mode->valuestring, "or")) { + uint8_t data = adac_read_byte(i2c_addr,reg->valueint); + data |= (uint8_t) val->valueint; + adac_write_byte(i2c_addr, reg->valueint, data); + } else if (!strcasecmp(mode->valuestring, "and")) { + uint8_t data = adac_read_byte(i2c_addr, reg->valueint); + data &= (uint8_t) val->valueint; + adac_write_byte(i2c_addr, reg->valueint, data); + } + } } return true; diff --git a/components/squeezelite/output.c b/components/squeezelite/output.c index cf420065..3cdb5fbc 100644 --- a/components/squeezelite/output.c +++ b/components/squeezelite/output.c @@ -354,7 +354,6 @@ void output_init_common(log_level level, const char *device, unsigned output_buf loglevel = level; output_buf_size = output_buf_size - (output_buf_size % BYTES_PER_FRAME); - output.init_size = output_buf_size; LOG_DEBUG("outputbuf size: %u", output_buf_size); buf_init(outputbuf, output_buf_size); diff --git a/components/squeezelite/output_embedded.c b/components/squeezelite/output_embedded.c index 2dbd9437..fc304474 100644 --- a/components/squeezelite/output_embedded.c +++ b/components/squeezelite/output_embedded.c @@ -81,12 +81,14 @@ void output_init_embedded(log_level level, char *device, unsigned output_buf_siz output.start_frames = FRAME_BLOCK; output.rate_delay = rate_delay; - +#if CONFIG_BT_SINK if (strcasestr(device, "BT ") || !strcasecmp(device, "BT")) { LOG_INFO("init Bluetooth"); close_cb = &output_close_bt; output_init_bt(level, device, output_buf_size, params, rates, rate_delay, idle); - } else { + } else +#endif + { LOG_INFO("init I2S/SPDIF"); close_cb = &output_close_i2s; volume_cb = &output_volume_i2s; diff --git a/components/squeezelite/output_i2s.c b/components/squeezelite/output_i2s.c index 3082adb1..e0b950e2 100644 --- a/components/squeezelite/output_i2s.c +++ b/components/squeezelite/output_i2s.c @@ -43,6 +43,7 @@ sure that using rate_delay would fix that #include "led.h" #include "monitor.h" #include "platform_config.h" +#include "gpio_exp.h" #include "accessors.h" #include "equalizer.h" #include "globdefs.h" @@ -129,8 +130,13 @@ static bool handler(u8_t *data, int len){ jack_mutes_amp = pkt->config == 0; config_set_value(NVS_TYPE_STR, "jack_mutes_amp", jack_mutes_amp ? "y" : "n"); - if (jack_mutes_amp && jack_inserted_svc()) adac->speaker(false); - else adac->speaker(true); + if (jack_mutes_amp && jack_inserted_svc()) { + adac->speaker(false); + if (amp_control.gpio != -1) gpio_set_level_x(amp_control.gpio, !amp_control.active); + } else { + adac->speaker(true); + if (amp_control.gpio != -1) gpio_set_level_x(amp_control.gpio, amp_control.active); + } } LOG_INFO("got AUDO %02x", pkt->config); @@ -151,13 +157,12 @@ static void jack_handler(bool inserted) { // jack detection bounces a bit but that seems fine if (jack_mutes_amp) { LOG_INFO("switching amplifier %s", inserted ? "OFF" : "ON"); - if (inserted) adac->speaker(false); - else adac->speaker(true); + adac->speaker(!inserted); + if (amp_control.gpio != -1) gpio_set_level_x(amp_control.gpio, inserted ? !amp_control.active : amp_control.active); } // activate headset - if (inserted) adac->headset(true); - else adac->headset(false); + adac->headset(inserted); // and chain if any if (jack_handler_chain) (jack_handler_chain)(inserted); @@ -173,9 +178,9 @@ static void set_amp_gpio(int gpio, char *value) { amp_control.gpio = gpio; if ((p = strchr(value, ':')) != NULL) amp_control.active = atoi(p + 1); - gpio_pad_select_gpio(amp_control.gpio); - gpio_set_direction(amp_control.gpio, GPIO_MODE_OUTPUT); - gpio_set_level(amp_control.gpio, !amp_control.active); + 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); } @@ -185,11 +190,10 @@ static void set_amp_gpio(int gpio, char *value) { * Set pin from config string */ static void set_i2s_pin(char *config, i2s_pin_config_t *pin_config) { - char *p; pin_config->bck_io_num = pin_config->ws_io_num = pin_config->data_out_num = pin_config->data_in_num = -1; - if ((p = strcasestr(config, "bck")) != NULL) pin_config->bck_io_num = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(config, "ws")) != NULL) pin_config->ws_io_num = atoi(strchr(p, '=') + 1); - if ((p = strcasestr(config, "do")) != NULL) pin_config->data_out_num = atoi(strchr(p, '=') + 1); + PARSE_PARAM(config, "bck", '=', pin_config->bck_io_num); + PARSE_PARAM(config, "ws", '=', pin_config->ws_io_num); + PARSE_PARAM(config, "do", '=', pin_config->data_out_num); } /**************************************************************************************** @@ -343,12 +347,13 @@ 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; + parse_set_GPIO(set_amp_gpio); + if (jack_mutes_amp && jack_inserted_svc()) adac->speaker(false); else adac->speaker(true); adac->headset(jack_inserted_svc()); - parse_set_GPIO(set_amp_gpio); // create task as a FreeRTOS task but uses stack in internal RAM { @@ -365,10 +370,11 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch // memory still used but at least task is not created if (stats) { - static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4))); + // we allocate TCB but stack is static to avoid SPIRAM fragmentation + StaticTask_t* xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); static EXT_RAM_ATTR StackType_t xStack[STAT_STACK_SIZE] __attribute__ ((aligned (4))); stats_task = xTaskCreateStatic( (TaskFunction_t) output_thread_i2s_stats, "output_i2s_sts", STAT_STACK_SIZE, - NULL, ESP_TASK_PRIO_MIN, xStack, &xTaskBuffer); + NULL, ESP_TASK_PRIO_MIN, xStack, xTaskBuffer); } } @@ -449,13 +455,16 @@ static void output_thread_i2s(void *arg) { LOG_INFO("Output state is %d", output.state); if (output.state == OUTPUT_OFF) { led_blink(LED_GREEN, 100, 2500); - if (amp_control.gpio != -1) gpio_set_level(amp_control.gpio, !amp_control.active); + if (amp_control.gpio != -1) gpio_set_level_x(amp_control.gpio, !amp_control.active); LOG_INFO("switching off amp GPIO %d", amp_control.gpio); } else if (output.state == OUTPUT_STOPPED) { adac->speaker(false); led_blink(LED_GREEN, 200, 1000); } else if (output.state == OUTPUT_RUNNING) { - if (!jack_mutes_amp || !jack_inserted_svc()) adac->speaker(true); + if (!jack_mutes_amp || !jack_inserted_svc()) { + if (amp_control.gpio != -1) gpio_set_level_x(amp_control.gpio, amp_control.active); + adac->speaker(true); + } led_on(LED_GREEN); } } @@ -513,7 +522,6 @@ static void output_thread_i2s(void *arg) { i2s_zero_dma_buffer(CONFIG_I2S_NUM); i2s_start(CONFIG_I2S_NUM); adac->power(ADAC_ON); - if (amp_control.gpio != -1) gpio_set_level(amp_control.gpio, amp_control.active); } // this does not work well as set_sample_rates resets the fifos (and it's too early) diff --git a/components/squeezelite/slimproto.c b/components/squeezelite/slimproto.c index 87ac31fa..2fb65d27 100644 --- a/components/squeezelite/slimproto.c +++ b/components/squeezelite/slimproto.c @@ -391,7 +391,7 @@ static void process_strm(u8_t *pkt, int len) { #if EMBEDDED if (output.external) decode_restore(output.external); output.external = 0; - _buf_resize(outputbuf, output.init_size); + _buf_limit(outputbuf, 0); #endif output.threshold = strm->output_threshold; output.next_replay_gain = unpackN(&strm->replay_gain); diff --git a/components/squeezelite/squeezelite.h b/components/squeezelite/squeezelite.h index 21dfc6d8..081cb3d8 100644 --- a/components/squeezelite/squeezelite.h +++ b/components/squeezelite/squeezelite.h @@ -532,6 +532,7 @@ struct buffer { u8_t *wrap; size_t size; size_t base_size; + size_t true_size; mutex_type mutex; }; @@ -547,6 +548,7 @@ void _buf_flush(struct buffer *buf); void _buf_unwrap(struct buffer *buf, size_t cont); void buf_adjust(struct buffer *buf, size_t mod); void _buf_resize(struct buffer *buf, size_t size); +size_t _buf_limit(struct buffer *buf, size_t limit); void buf_init(struct buffer *buf, size_t size); void buf_destroy(struct buffer *buf); @@ -665,7 +667,6 @@ struct outputstate { u8_t channels; const char *device; int external; - u32_t init_size; #if ALSA unsigned buffer; unsigned period; diff --git a/components/squeezelite/vu.bmp b/components/squeezelite/vu.bmp new file mode 100644 index 00000000..065d337b Binary files /dev/null and b/components/squeezelite/vu.bmp differ diff --git a/components/squeezelite/vu_s.data b/components/squeezelite/vu_s.data new file mode 100644 index 00000000..e11e418c Binary files /dev/null and b/components/squeezelite/vu_s.data differ diff --git a/components/telnet/telnet.c b/components/telnet/telnet.c index 0c96071e..a856ae06 100644 --- a/components/telnet/telnet.c +++ b/components/telnet/telnet.c @@ -39,19 +39,18 @@ #include "nvs_utilities.h" #include "platform_esp32.h" #include "messaging.h" -#include "trace.h" - +#include "tools.h" /************************************ * Globals */ -#define TELNET_STACK_SIZE 8048 +#define TELNET_STACK_SIZE 4096 #define TELNET_RX_BUF 1024 const static char TAG[] = "telnet"; static int uart_fd=0; -RingbufHandle_t buf_handle; +static RingbufHandle_t buf_handle; static size_t send_chunk=300; static size_t log_buf_size=2000; //32-bit aligned size @@ -64,9 +63,7 @@ extern bool bypass_network_manager; * Forward declarations */ static void telnet_task(void *data); -static ssize_t stdout_read(int fd, void* data, size_t size); static int stdout_open(const char * path, int flags, int mode); -static int stdout_close(int fd); static int stdout_fstat(int fd, struct stat * st); static ssize_t stdout_write(int fd, const void * data, size_t size); static char *eventToString(telnet_event_type_t type); @@ -79,16 +76,15 @@ struct telnetUserData { char * rxbuf; }; -bool is_serial_suppressed(){ - return bIsEnabled?!bMirrorToUART:false ; -} void init_telnet(){ char *val= get_nvs_value_alloc(NVS_TYPE_STR, "telnet_enable"); + if (!val || strlen(val) == 0 || !strcasestr("YXD",val) ) { ESP_LOGI(TAG,"Telnet support disabled"); if(val) free(val); return; } + // if wifi manager is bypassed, there will possibly be no wifi available // bMirrorToUART = (strcasestr("D",val)!=NULL); @@ -112,12 +108,14 @@ void init_telnet(){ log_buf_size=log_buf_size>0?log_buf_size:4000; } // Redirect the output to our telnet handler as soon as possible - StaticRingbuffer_t *buffer_struct = (StaticRingbuffer_t *)malloc(sizeof(StaticRingbuffer_t) ); + StaticRingbuffer_t *buffer_struct = (StaticRingbuffer_t *) heap_caps_malloc(sizeof(StaticRingbuffer_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); // All non-split ring buffer must have their memory alignment set to 32 bits. uint8_t *buffer_storage = (uint8_t *)heap_caps_malloc(sizeof(uint8_t)*log_buf_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT ); buf_handle = xRingbufferCreateStatic(log_buf_size, RINGBUF_TYPE_BYTEBUF, buffer_storage, buffer_struct); if (buf_handle == NULL) { + ESP_LOGE(TAG,"Failed to create ring buffer for telnet!"); messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Failed to allocate memory for telnet buffer"); + return; } @@ -127,29 +125,31 @@ void init_telnet(){ .write = &stdout_write, .open = &stdout_open, .fstat = &stdout_fstat, - .close = &stdout_close, - .read = &stdout_read, - }; if(bMirrorToUART){ uart_fd=open("/dev/uart/0", O_RDWR); } + ESP_ERROR_CHECK(esp_vfs_register("/dev/pkspstdout", &vfs, NULL)); freopen("/dev/pkspstdout", "w", stdout); freopen("/dev/pkspstdout", "w", stderr); + bIsEnabled=true; } + void start_telnet(void * pvParameter){ static bool isStarted=false; - StaticTask_t *xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)); - StackType_t *xStack = heap_caps_malloc(TELNET_STACK_SIZE,(MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT)); - if(!isStarted && bIsEnabled) { - xTaskCreateStatic( (TaskFunction_t) &telnet_task, "telnet", TELNET_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN, xStack, xTaskBuffer); - isStarted=true; - } + if(isStarted || !bIsEnabled) return; + + StaticTask_t *xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + StackType_t *xStack = heap_caps_malloc(TELNET_STACK_SIZE, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + + xTaskCreateStatic( (TaskFunction_t) &telnet_task, "telnet", TELNET_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN, xStack, xTaskBuffer); + isStarted=true; } + static void telnet_task(void *data) { int serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); struct sockaddr_in serverAddr; @@ -231,30 +231,6 @@ static char *eventToString(telnet_event_type_t type) { /** * Telnet handler. */ -void process_received_data(const char * buffer, size_t size){ - //ESP_LOGD(tag, "received data, len=%d", event->data.size); - - char * command = malloc(size+1); - const char * c=buffer; - - // scrub from any escape command - if(*c == '\e') while (size && size-- && *c++ != '\n'); - memcpy(command,c,size); - command[size]='\0'; - if(command[0]!='\r' && command[0]!='\n'){ - // echo the command buffer out to uart and run - if(bMirrorToUART){ - write(uart_fd, command, size); - } - for(int i=strlen(command);i>=0;i--){ - // strip any cr/lf - if(command[i]== '\n' || command[i]== '\r') command[i]= '\0'; - } - run_command((char *)command); - } - free(command); - -} static void handle_telnet_events( telnet_t *thisTelnet, telnet_event_t *event, @@ -268,17 +244,13 @@ static void handle_telnet_events( //printf("ERROR: (telnet) send: %d (%s)", errno, strerror(errno)); } break; - case TELNET_EV_DATA: - process_received_data(event->data.buffer, event->data.size); + console_push(event->data.buffer, event->data.size); break; case TELNET_EV_TTYPE: printf("telnet event: %s\n", eventToString(event->type)); telnet_ttype_send(telnetUserData->tnHandle); break; - - - default: printf("telnet event: %s\n", eventToString(event->type)); break; @@ -335,10 +307,10 @@ static void handle_telnet_conn() { {TELNET_TELOPT_LINEMODE, TELNET_WONT, TELNET_DO }, { -1, 0, 0 } }; - struct telnetUserData *pTelnetUserData = (struct telnetUserData *)malloc(sizeof(struct telnetUserData)); + struct telnetUserData *pTelnetUserData = (struct telnetUserData *)heap_caps_malloc(sizeof(struct telnetUserData), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); tnHandle = telnet_init(my_telopts, handle_telnet_events, 0, pTelnetUserData); - pTelnetUserData->rxbuf = (char *) heap_caps_malloc(TELNET_RX_BUF, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + pTelnetUserData->rxbuf = (char *) heap_caps_malloc(TELNET_RX_BUF, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); pTelnetUserData->tnHandle = tnHandle; pTelnetUserData->sockfd = partnerSocket; @@ -384,19 +356,10 @@ static ssize_t stdout_write(int fd, const void * data, size_t size) { return bMirrorToUART?write(uart_fd, data, size):size; } -static ssize_t stdout_read(int fd, void* data, size_t size) { - //return read(fd, data, size); - return 0; -} - static int stdout_open(const char * path, int flags, int mode) { return 0; } -static int stdout_close(int fd) { - return 0; -} - static int stdout_fstat(int fd, struct stat * st) { st->st_mode = S_IFCHR; return 0; diff --git a/components/telnet/telnet.h b/components/telnet/telnet.h index eb563ece..255fa3d6 100644 --- a/components/telnet/telnet.h +++ b/components/telnet/telnet.h @@ -1,4 +1,3 @@ void init_telnet(); void start_telnet(void * pvParameter); -extern bool is_serial_suppressed(); diff --git a/components/tools/CMakeLists.txt b/components/tools/CMakeLists.txt index 0cfb6056..14dad4c6 100644 --- a/components/tools/CMakeLists.txt +++ b/components/tools/CMakeLists.txt @@ -1,7 +1,6 @@ -idf_component_register(SRCS operator.cpp utf8.c trace.c - REQUIRES esp_common pthread +idf_component_register(SRCS operator.cpp tools.c + REQUIRES esp_common pthread INCLUDE_DIRS . - PRIV_REQUIRES services freertos pthread esp_common ) #doing our own implementation of new operator for some pre-compiled binaries diff --git a/components/tools/operator.cpp b/components/tools/operator.cpp index 47afbbd1..9234b7a8 100644 --- a/components/tools/operator.cpp +++ b/components/tools/operator.cpp @@ -1,3 +1,22 @@ -#include // for malloc and free -void* operator new(unsigned int size) { return malloc(size); } -void operator delete(void* ptr) { if (ptr) free(ptr); } +#include +#include + +void* operator new(std::size_t count) { + return heap_caps_malloc(count, MALLOC_CAP_SPIRAM); +} + +void operator delete(void* ptr) noexcept { + if (ptr) free(ptr); +} + +/* +// C++17 only +void* operator new (std::size_t count, std::align_val_t alignment) { + return heap_caps_malloc(count, MALLOC_CAP_SPIRAM); +} + +// C++17 only +void operator delete(void* ptr, std::align_val_t alignment) noexcept { + if (ptr) free(ptr); +} +*/ diff --git a/components/tools/platform_esp32.h b/components/tools/platform_esp32.h index 3a8ad39c..d378e2da 100644 --- a/components/tools/platform_esp32.h +++ b/components/tools/platform_esp32.h @@ -27,8 +27,8 @@ #define SQUEEZELITE_ESP32_RELEASE_URL "https://github.com/sle118/squeezelite-esp32/releases" #endif extern bool is_recovery_running; -extern esp_err_t run_command(char * line); extern bool wait_for_wifi(); +extern bool console_push(const char * data, size_t size); extern void console_start(); extern pthread_cond_t wifi_connect_suspend_cond; extern pthread_t wifi_connect_suspend_mutex; diff --git a/components/tools/utf8.c b/components/tools/tools.c similarity index 61% rename from components/tools/utf8.c rename to components/tools/tools.c index 210fa311..4f582b81 100644 --- a/components/tools/utf8.c +++ b/components/tools/tools.c @@ -1,17 +1,32 @@ +/* + * (c) Philippe G. 20201, philippe_44@outlook.com + * see other copyrights below + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + * + */ +#include +#include +#include +#include +#include +#include "tools.h" +#include "esp_heap_caps.h" +#include "esp_log.h" + +const static char TAG[] = "tools"; + +/**************************************************************************************** + * UTF-8 tools + */ + // Copyright (c) 2008-2009 Bjoern Hoehrmann // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. // Copyright (c) 2017 ZephRay // // utf8to1252 - almost equivalent to iconv -f utf-8 -t windows-1252, but better -#include -#include -#include -#include -#include "esp_log.h" - -#define TAG "aa" - #define UTF8_ACCEPT 0 #define UTF8_REJECT 1 @@ -90,4 +105,69 @@ void utf8_decode(char *src) { } *dst = '\0'; -} \ No newline at end of file +} + +/**************************************************************************************** + * URL tools + */ + +static inline char from_hex(char ch) { + return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; +} + +void url_decode(char *url) { + char *p, *src = strdup(url); + for (p = src; *src; url++) { + *url = *src++; + if (*url == '%') { + *url = from_hex(*src++) << 4; + *url |= from_hex(*src++); + } else if (*url == '+') { + *url = ' '; + } + } + *url = '\0'; + free(p); +} + +/**************************************************************************************** + * Memory tools + */ + +void * malloc_init_external(size_t sz){ + void * ptr=NULL; + ptr = heap_caps_malloc(sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if(ptr==NULL){ + ESP_LOGE(TAG,"malloc_init_external: unable to allocate %d bytes of PSRAM!",sz); + } + else { + memset(ptr,0x00,sz); + } + return ptr; +} + +void * clone_obj_psram(void * source, size_t source_sz){ + void * ptr=NULL; + ptr = heap_caps_malloc(source_sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if(ptr==NULL){ + ESP_LOGE(TAG,"clone_obj_psram: unable to allocate %d bytes of PSRAM!",source_sz); + } + else { + memcpy(ptr,source,source_sz); + } + return ptr; +} + +char * strdup_psram(const char * source){ + void * ptr=NULL; + size_t source_sz = strlen(source)+1; + ptr = heap_caps_malloc(source_sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if(ptr==NULL){ + ESP_LOGE(TAG,"strdup_psram: unable to allocate %d bytes of PSRAM! Cannot clone string %s",source_sz,source); + } + else { + memset(ptr,0x00,source_sz); + strcpy(ptr,source); + } + return ptr; +} diff --git a/components/tools/tools.h b/components/tools/tools.h index 5a76150e..ae94336d 100644 --- a/components/tools/tools.h +++ b/components/tools/tools.h @@ -10,4 +10,59 @@ #pragma once -void utf8_decode(char *src); +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef QUOTE +#define QUOTE(name) #name +#endif + +#ifndef STR +#define STR(macro) QUOTE(macro) +#endif + +#ifndef STR_OR_ALT +#define STR_OR_ALT(str,alt) (str?str:alt) +#endif + +#ifndef STR_OR_BLANK +#define STR_OR_BLANK(p) p == NULL ? "" : p +#endif + +#define ESP_LOG_DEBUG_EVENT(tag,e) ESP_LOGD(tag,"evt: " e) + +#ifdef ENABLE_MEMTRACE +#define MEMTRACE_PRINT_DELTA() memtrace_print_delta(NULL,TAG,__FUNCTION__); +#define MEMTRACE_PRINT_DELTA_MESSAGE(x) memtrace_print_delta(x,TAG,__FUNCTION__); +#else +#define MEMTRACE_PRINT_DELTA() +#define MEMTRACE_PRINT_DELTA_MESSAGE(x) ESP_LOGD(TAG,"%s",x); +#endif + +#ifndef FREE_AND_NULL +#define FREE_AND_NULL(x) if(x) { free(x); x=NULL; } +#endif + +#ifndef CASE_TO_STR +#define CASE_TO_STR(x) case x: return STR(x); break; +#endif + +#define ENUM_TO_STRING(g) \ + case g: \ + return STR(g); \ + break; + +void utf8_decode(char *src); +void url_decode(char *url); +void* malloc_init_external(size_t sz); +void* clone_obj_psram(void * source, size_t source_sz); +char* strdup_psram(const char * source); +const char* str_or_unknown(const char * str); +const char* str_or_null(const char * str); + +extern const char unknown_string_placeholder[]; + +#ifdef __cplusplus +} +#endif diff --git a/components/tools/trace.c b/components/tools/trace.c index 679fd4c3..0b479eb1 100644 --- a/components/tools/trace.c +++ b/components/tools/trace.c @@ -8,7 +8,6 @@ #include "freertos/FreeRTOS.h" #include "freertos/queue.h" #include "freertos/task.h" -#include "globdefs.h" #include "esp_event.h" #include "trace.h" diff --git a/components/tools/trace.h b/components/tools/trace.h index 3379a44f..e099aa7a 100644 --- a/components/tools/trace.h +++ b/components/tools/trace.h @@ -11,39 +11,10 @@ #pragma once -#ifndef QUOTE -#define QUOTE(name) #name -#endif -#ifndef STR -#define STR(macro) QUOTE(macro) -#endif -#define ESP_LOG_DEBUG_EVENT(tag,e) ESP_LOGD(tag,"evt: " e) -#ifndef STR_OR_ALT -#define STR_OR_ALT(str,alt) (str?str:alt) -#endif -#ifndef STR_OR_BLANK -#define STR_OR_BLANK(p) p == NULL ? "" : p -#endif -#define ENUM_TO_STRING(g) \ - case g: \ - return STR(g); \ - break; -extern const char unknown_string_placeholder[]; -extern const char * str_or_unknown(const char * str); -extern const char * str_or_null(const char * str); -void memtrace_print_delta(const char * msg, const char * TAG, const char * function); #ifdef ENABLE_MEMTRACE #define MEMTRACE_PRINT_DELTA() memtrace_print_delta(NULL,TAG,__FUNCTION__); #define MEMTRACE_PRINT_DELTA_MESSAGE(x) memtrace_print_delta(x,TAG,__FUNCTION__); #else #define MEMTRACE_PRINT_DELTA() #define MEMTRACE_PRINT_DELTA_MESSAGE(x) ESP_LOGD(TAG,"%s",x); -#endif - -#ifndef FREE_AND_NULL -#define FREE_AND_NULL(x) if(x) { free(x); x=NULL; } -#endif - -#ifndef CASE_TO_STR -#define CASE_TO_STR(x) case x: return STR(x); break; -#endif +#endif \ No newline at end of file diff --git a/components/wifi-manager/CMakeLists.txt b/components/wifi-manager/CMakeLists.txt index bf559d5d..c65e47fb 100644 --- a/components/wifi-manager/CMakeLists.txt +++ b/components/wifi-manager/CMakeLists.txt @@ -1,9 +1,10 @@ set( WEBPACK_DIR webapp/webpack/dist ) + idf_component_register( SRC_DIRS . webapp UML-State-Machine-in-C/src - INCLUDE_DIRS . webapp UML-State-Machine-in-C/src ${IDF_PATH}/components/esp_http_server/src ${IDF_PATH}/components/esp_http_server/src/port/esp32 ${IDF_PATH}/components/esp_http_server/src/util ${IDF_PATH}/components/esp_http_server/src/ + INCLUDE_DIRS . webapp UML-State-Machine-in-C/src REQUIRES squeezelite-ota json mdns - PRIV_REQUIRES tools services platform_config esp_common json newlib freertos spi_flash nvs_flash mdns pthread wpa_supplicant platform_console esp_http_server console driver_bt + PRIV_REQUIRES tools services platform_config esp_common json newlib freertos spi_flash nvs_flash mdns pthread wpa_supplicant platform_console esp_http_server console driver_bt ) include(webapp/webapp.cmake) \ No newline at end of file diff --git a/components/wifi-manager/_esp_http_server.h b/components/wifi-manager/_esp_http_server.h deleted file mode 100644 index e64c1e57..00000000 --- a/components/wifi-manager/_esp_http_server.h +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2018 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef __ESP_HTTP_SERVER_H_ -#define __ESP_HTTP_SERVER_H_ - -#include -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - - - -/** - * @brief Starts the web server - * - * Create an instance of HTTP server and allocate memory/resources for it - * depending upon the specified configuration. - * - * Example usage: - * @code{c} - * - * //Function for starting the webserver - * httpd_handle_t start_webserver(void) - * { - * // Generate default configuration - * httpd_config_t config = HTTPD_DEFAULT_CONFIG(); - * - * // Empty handle to http_server - * httpd_handle_t server = NULL; - * - * // Start the httpd server - * if (httpd_start(&server, &config) == ESP_OK) { - * // Register URI handlers - * httpd_register_uri_handler(server, &uri_get); - * httpd_register_uri_handler(server, &uri_post); - * } - * // If server failed to start, handle will be NULL - * return server; - * } - * - * @endcode - * - * @param[in] config Configuration for new instance of the server - * @param[out] handle Handle to newly created instance of the server. NULL on error - * @return - * - ESP_OK : Instance created successfully - * - ESP_ERR_INVALID_ARG : Null argument(s) - * - ESP_ERR_HTTPD_ALLOC_MEM : Failed to allocate memory for instance - * - ESP_ERR_HTTPD_TASK : Failed to launch server task - */ -esp_err_t __httpd_start(httpd_handle_t *handle, const httpd_config_t *config); -static inline int __httpd_os_thread_create_static(TaskHandle_t *thread, - const char *name, uint16_t stacksize, int prio, - void (*thread_routine)(void *arg), void *arg, - BaseType_t core_id) -{ - - - StaticTask_t *xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)); - StackType_t *xStack = heap_caps_malloc(stacksize,(MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT)); - - - // - *thread = xTaskCreateStaticPinnedToCore(thread_routine, name, stacksize, arg, prio, xStack,xTaskBuffer,core_id); - if (*thread) { - return ESP_OK; - } - return ESP_FAIL; -} - - - - - -#ifdef __cplusplus -} -#endif - -#endif /* ! _ESP_HTTP_SERVER_H_ */ diff --git a/components/wifi-manager/_esp_httpd_main.c b/components/wifi-manager/_esp_httpd_main.c deleted file mode 100644 index 156879f1..00000000 --- a/components/wifi-manager/_esp_httpd_main.c +++ /dev/null @@ -1,374 +0,0 @@ -// Copyright 2018 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include -#include -#include -#include - -#include -#include <_esp_http_server.h> -#include "esp_httpd_priv.h" -#include "ctrl_sock.h" -#include "globdefs.h" - -static const char *TAG = "_httpd"; - - -struct httpd_ctrl_data { - enum httpd_ctrl_msg { - HTTPD_CTRL_SHUTDOWN, - HTTPD_CTRL_WORK, - } hc_msg; - httpd_work_fn_t hc_work; - void *hc_work_arg; -}; - - -static esp_err_t _httpd_server_init(struct httpd_data *hd) -{ - int fd = socket(PF_INET6, SOCK_STREAM, 0); - if (fd < 0) { - ESP_LOGE(TAG, LOG_FMT("error in socket (%d)"), errno); - return ESP_FAIL; - } - - struct in6_addr inaddr_any = IN6ADDR_ANY_INIT; - struct sockaddr_in6 serv_addr = { - .sin6_family = PF_INET6, - .sin6_addr = inaddr_any, - .sin6_port = htons(hd->config.server_port) - }; - - /* Enable SO_REUSEADDR to allow binding to the same - * address and port when restarting the server */ - int enable = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) { - /* This will fail if CONFIG_LWIP_SO_REUSE is not enabled. But - * it does not affect the normal working of the HTTP Server */ - ESP_LOGW(TAG, LOG_FMT("error enabling SO_REUSEADDR (%d)"), errno); - } - - int ret = bind(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); - if (ret < 0) { - ESP_LOGE(TAG, LOG_FMT("error in bind (%d)"), errno); - close(fd); - return ESP_FAIL; - } - - ret = listen(fd, hd->config.backlog_conn); - if (ret < 0) { - ESP_LOGE(TAG, LOG_FMT("error in listen (%d)"), errno); - close(fd); - return ESP_FAIL; - } - - int ctrl_fd = cs_create_ctrl_sock(hd->config.ctrl_port); - if (ctrl_fd < 0) { - ESP_LOGE(TAG, LOG_FMT("error in creating ctrl socket (%d)"), errno); - close(fd); - return ESP_FAIL; - } - - int msg_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (msg_fd < 0) { - ESP_LOGE(TAG, LOG_FMT("error in creating msg socket (%d)"), errno); - close(fd); - close(ctrl_fd); - return ESP_FAIL; - } - - hd->listen_fd = fd; - hd->ctrl_fd = ctrl_fd; - hd->msg_fd = msg_fd; - return ESP_OK; -} - -static void _httpd_process_ctrl_msg(struct httpd_data *hd) -{ - struct httpd_ctrl_data msg; - int ret = recv(hd->ctrl_fd, &msg, sizeof(msg), 0); - if (ret <= 0) { - ESP_LOGW(TAG, LOG_FMT("error in recv (%d)"), errno); - return; - } - if (ret != sizeof(msg)) { - ESP_LOGW(TAG, LOG_FMT("incomplete msg")); - return; - } - - switch (msg.hc_msg) { - case HTTPD_CTRL_WORK: - if (msg.hc_work) { - ESP_LOGD(TAG, LOG_FMT("work")); - (*msg.hc_work)(msg.hc_work_arg); - } - break; - case HTTPD_CTRL_SHUTDOWN: - ESP_LOGD(TAG, LOG_FMT("shutdown")); - hd->hd_td.status = THREAD_STOPPING; - break; - default: - break; - } -} - -static esp_err_t _httpd_accept_conn(struct httpd_data *hd, int listen_fd) -{ - /* If no space is available for new session, close the least recently used one */ - if (hd->config.lru_purge_enable == true) { - if (!httpd_is_sess_available(hd)) { - /* Queue asynchronous closure of the least recently used session */ - return httpd_sess_close_lru(hd); - /* Returning from this allowes the main server thread to process - * the queued asynchronous control message for closing LRU session. - * Since connection request hasn't been addressed yet using accept() - * therefore _httpd_accept_conn() will be called again, but this time - * with space available for one session - */ - } - } - - struct sockaddr_in addr_from; - socklen_t addr_from_len = sizeof(addr_from); - int new_fd = accept(listen_fd, (struct sockaddr *)&addr_from, &addr_from_len); - if (new_fd < 0) { - ESP_LOGW(TAG, LOG_FMT("error in accept (%d)"), errno); - return ESP_FAIL; - } - ESP_LOGD(TAG, LOG_FMT("newfd = %d"), new_fd); - - struct timeval tv; - /* Set recv timeout of this fd as per config */ - tv.tv_sec = hd->config.recv_wait_timeout; - tv.tv_usec = 0; - setsockopt(new_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)); - - /* Set send timeout of this fd as per config */ - tv.tv_sec = hd->config.send_wait_timeout; - tv.tv_usec = 0; - setsockopt(new_fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv)); - - if (ESP_OK != httpd_sess_new(hd, new_fd)) { - ESP_LOGW(TAG, LOG_FMT("session creation failed")); - close(new_fd); - return ESP_FAIL; - } - ESP_LOGD(TAG, LOG_FMT("complete")); - return ESP_OK; -} -/* Manage in-coming connection or data requests */ -static esp_err_t _httpd_server(struct httpd_data *hd) -{ - fd_set read_set; - FD_ZERO(&read_set); - if (hd->config.lru_purge_enable || httpd_is_sess_available(hd)) { - /* Only listen for new connections if server has capacity to - * handle more (or when LRU purge is enabled, in which case - * older connections will be closed) */ - FD_SET(hd->listen_fd, &read_set); - } - FD_SET(hd->ctrl_fd, &read_set); - - int tmp_max_fd; - httpd_sess_set_descriptors(hd, &read_set, &tmp_max_fd); - int maxfd = MAX(hd->listen_fd, tmp_max_fd); - tmp_max_fd = maxfd; - maxfd = MAX(hd->ctrl_fd, tmp_max_fd); - - ESP_LOGD(TAG, LOG_FMT("doing select maxfd+1 = %d"), maxfd + 1); - int active_cnt = select(maxfd + 1, &read_set, NULL, NULL, NULL); - if (active_cnt < 0) { - ESP_LOGE(TAG, LOG_FMT("error in select (%d)"), errno); - httpd_sess_delete_invalid(hd); - return ESP_OK; - } - - /* Case0: Do we have a control message? */ - if (FD_ISSET(hd->ctrl_fd, &read_set)) { - ESP_LOGD(TAG, LOG_FMT("processing ctrl message")); - _httpd_process_ctrl_msg(hd); - if (hd->hd_td.status == THREAD_STOPPING) { - ESP_LOGD(TAG, LOG_FMT("stopping thread")); - return ESP_FAIL; - } - } - - /* Case1: Do we have any activity on the current data - * sessions? */ - int fd = -1; - while ((fd = httpd_sess_iterate(hd, fd)) != -1) { - if (FD_ISSET(fd, &read_set) || (httpd_sess_pending(hd, fd))) { - ESP_LOGD(TAG, LOG_FMT("processing socket %d"), fd); - if (httpd_sess_process(hd, fd) != ESP_OK) { - ESP_LOGD(TAG, LOG_FMT("closing socket %d"), fd); - close(fd); - /* Delete session and update fd to that - * preceding the one being deleted */ - fd = httpd_sess_delete(hd, fd); - } - } - } - - /* Case2: Do we have any incoming connection requests to - * process? */ - if (FD_ISSET(hd->listen_fd, &read_set)) { - ESP_LOGD(TAG, LOG_FMT("processing listen socket %d"), hd->listen_fd); - if (_httpd_accept_conn(hd, hd->listen_fd) != ESP_OK) { - ESP_LOGW(TAG, LOG_FMT("error accepting new connection")); - } - } - return ESP_OK; -} - -static void _httpd_close_all_sessions(struct httpd_data *hd) -{ - int fd = -1; - while ((fd = httpd_sess_iterate(hd, fd)) != -1) { - ESP_LOGD(TAG, LOG_FMT("cleaning up socket %d"), fd); - httpd_sess_delete(hd, fd); - close(fd); - } -} -/* The main HTTPD thread */ -static void _httpd_thread(void *arg) -{ - int ret; - struct httpd_data *hd = (struct httpd_data *) arg; - hd->hd_td.status = THREAD_RUNNING; - - ESP_LOGD(TAG, LOG_FMT("web server started")); - while (1) { - ret = _httpd_server(hd); - if (ret != ESP_OK) { - break; - } - } - - ESP_LOGD(TAG, LOG_FMT("web server exiting")); - close(hd->msg_fd); - cs_free_ctrl_sock(hd->ctrl_fd); - _httpd_close_all_sessions(hd); - close(hd->listen_fd); - hd->hd_td.status = THREAD_STOPPED; - httpd_os_thread_delete(); -} -static struct httpd_data *__httpd_create(const httpd_config_t *config) -{ - /* Allocate memory for httpd instance data */ - struct httpd_data *hd = malloc_init_external(sizeof(struct httpd_data)); - if (!hd) { - ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP server instance")); - return NULL; - } - hd->hd_calls = malloc_init_external(config->max_uri_handlers* sizeof(httpd_uri_t *)); - if (!hd->hd_calls) { - ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP URI handlers")); - free(hd); - return NULL; - } - hd->hd_sd = malloc_init_external(config->max_open_sockets* sizeof(struct sock_db)); - if (!hd->hd_sd) { - ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP session data")); - free(hd->hd_calls); - free(hd); - return NULL; - } - struct httpd_req_aux *ra = &hd->hd_req_aux; - ra->resp_hdrs = malloc_init_external(config->max_resp_headers* sizeof(struct resp_hdr)); - if (!ra->resp_hdrs) { - ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP response headers")); - free(hd->hd_sd); - free(hd->hd_calls); - free(hd); - return NULL; - } - hd->err_handler_fns = malloc_init_external(HTTPD_ERR_CODE_MAX* sizeof(httpd_err_handler_func_t)); - if (!hd->err_handler_fns) { - ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP error handlers")); - free(ra->resp_hdrs); - free(hd->hd_sd); - free(hd->hd_calls); - free(hd); - return NULL; - } - /* Save the configuration for this instance */ - hd->config = *config; - return hd; -} -static void _httpd_delete(struct httpd_data *hd) -{ - struct httpd_req_aux *ra = &hd->hd_req_aux; - /* Free memory of httpd instance data */ - free(hd->err_handler_fns); - free(ra->resp_hdrs); - free(hd->hd_sd); - - /* Free registered URI handlers */ - httpd_unregister_all_uri_handlers(hd); - free(hd->hd_calls); - free(hd); -} -esp_err_t __httpd_start(httpd_handle_t *handle, const httpd_config_t *config) -{ - if (handle == NULL || config == NULL) { - return ESP_ERR_INVALID_ARG; - } - - /* Sanity check about whether LWIP is configured for providing the - * maximum number of open sockets sufficient for the server. Though, - * this check doesn't guarantee that many sockets will actually be - * available at runtime as other processes may use up some sockets. - * Note that server also uses 3 sockets for its internal use : - * 1) listening for new TCP connections - * 2) for sending control messages over UDP - * 3) for receiving control messages over UDP - * So the total number of required sockets is max_open_sockets + 3 - */ - if (CONFIG_LWIP_MAX_SOCKETS < config->max_open_sockets + 3) { - ESP_LOGE(TAG, "Configuration option max_open_sockets is too large (max allowed %d)\n\t" - "Either decrease this or configure LWIP_MAX_SOCKETS to a larger value", - CONFIG_LWIP_MAX_SOCKETS - 3); - return ESP_ERR_INVALID_ARG; - } - - struct httpd_data *hd = __httpd_create(config); - if (hd == NULL) { - /* Failed to allocate memory */ - return ESP_ERR_HTTPD_ALLOC_MEM; - } - - if (_httpd_server_init(hd) != ESP_OK) { - _httpd_delete(hd); - return ESP_FAIL; - } - - httpd_sess_init(hd); - if (__httpd_os_thread_create_static(&hd->hd_td.handle, "httpd", - hd->config.stack_size, - hd->config.task_priority, - _httpd_thread, hd, - hd->config.core_id) != ESP_OK) { - /* Failed to launch task */ - _httpd_delete(hd); - return ESP_ERR_HTTPD_TASK; - } - - *handle = (httpd_handle_t *)hd; - return ESP_OK; -} - diff --git a/components/wifi-manager/http_server_handlers.c b/components/wifi-manager/http_server_handlers.c index 11d51867..ffc7430c 100644 --- a/components/wifi-manager/http_server_handlers.c +++ b/components/wifi-manager/http_server_handlers.c @@ -20,7 +20,6 @@ Copyright (c) 2017-2021 Sebastien L #include "esp_vfs.h" #include "messaging.h" #include "platform_esp32.h" -#include "trace.h" #include "esp_console.h" #include "argtable3/argtable3.h" #include "platform_console.h" @@ -28,7 +27,7 @@ Copyright (c) 2017-2021 Sebastien L #include "webapp/webpack.h" #include "network_wifi.h" #include "network_status.h" -#include "globdefs.h" +#include "tools.h" #define HTTP_STACK_SIZE (5*1024) const char str_na[]="N/A"; @@ -481,7 +480,8 @@ esp_err_t console_cmd_post_handler(httpd_req_t *req){ } else{ // navigate to the first child of the config structure - if(run_command(cJSON_GetStringValue(item))!=ESP_OK){ + char *cmd = cJSON_GetStringValue(item); + if(!console_push(cmd, strlen(cmd) + 1)){ httpd_resp_send(req, (const char *)failed, strlen(failed)); } else { diff --git a/components/wifi-manager/network_driver_W5500.c b/components/wifi-manager/network_driver_W5500.c index 877ba19a..2586894c 100644 --- a/components/wifi-manager/network_driver_W5500.c +++ b/components/wifi-manager/network_driver_W5500.c @@ -1,6 +1,6 @@ #include "esp_eth.h" -#include "globdefs.h" #include "network_ethernet.h" + static EXT_RAM_ATTR network_ethernet_driver_t W5500; static EXT_RAM_ATTR spi_device_interface_config_t devcfg; static EXT_RAM_ATTR esp_netif_config_t cfg_spi; diff --git a/components/wifi-manager/network_ethernet.c b/components/wifi-manager/network_ethernet.c index 4e572035..7b6ac9db 100644 --- a/components/wifi-manager/network_ethernet.c +++ b/components/wifi-manager/network_ethernet.c @@ -3,15 +3,13 @@ #endif #include "network_ethernet.h" #include "freertos/timers.h" -#include "globdefs.h" #include "messaging.h" #include "network_status.h" #include "platform_config.h" -#include "trace.h" +#include "tools.h" #include "accessors.h" #include "esp_log.h" - -//#include "dnserver.h" +#include "globdefs.h" static char TAG[] = "network_ethernet"; TimerHandle_t ETH_timer; @@ -117,40 +115,9 @@ void init_network_ethernet() { xEventGroupClearBits(ethernet_event_group, LINK_UP_BIT); spi_device_handle_t spi_handle = NULL; if (network_driver->spi) { - spi_host_device_t host = SPI3_HOST; - - if (eth.host != -1) { - // don't use system's shared SPI - spi_bus_config_t buscfg = { - .miso_io_num = eth.miso, - .mosi_io_num = eth.mosi, - .sclk_io_num = eth.clk, - .quadwp_io_num = -1, - .quadhd_io_num = -1, - }; - - // can't use SPI0 - if (eth.host == 0) - { - ESP_LOGW(TAG,"Cannot use SPI1 host. Defaulting to SPI2"); - host = SPI2_HOST; - } - else { - host = eth.host; - } - ESP_LOGI(TAG, "Initializing SPI bus on host %d (SPI%d) with mosi %d and miso %d", host,host+1, eth.mosi, eth.miso); - err = spi_bus_initialize(host, &buscfg, SPI_DMA_CH_AUTO); - if (err != ESP_OK) { - ESP_LOGE(TAG, "SPI bus init failed : %s", esp_err_to_name(err)); - } - - } else { - // when we use shared SPI, we assume it has been initialized - host = spi_system_host; - } - if (err == ESP_OK) { - ESP_LOGI(TAG, "Adding ethernet SPI on host %d (SPI%d) with mosi %d and miso %d", host,host+1, eth.mosi, eth.miso); - err = spi_bus_add_device(host, network_driver->devcfg, &spi_handle); + if (err == ESP_OK) { + ESP_LOGI(TAG, "Adding ethernet SPI on host %d (SPI%d) with mosi %d and miso %d", eth.host, eth.host+1, eth.mosi, eth.miso); + err = spi_bus_add_device(eth.host, network_driver->devcfg, &spi_handle); } if (err != ESP_OK) { ESP_LOGE(TAG, "SPI host failed : %s", esp_err_to_name(err)); diff --git a/components/wifi-manager/network_manager.c b/components/wifi-manager/network_manager.c index d701d4be..57670074 100644 --- a/components/wifi-manager/network_manager.c +++ b/components/wifi-manager/network_manager.c @@ -39,11 +39,11 @@ Copyright (c) 2017-2021 Sebastien L #include "messaging.h" #include "platform_config.h" +#include "tools.h" #include "trace.h" #include "accessors.h" #include "esp_err.h" -#include "globdefs.h" #include "http_server_handlers.h" #include "network_manager.h" diff --git a/components/wifi-manager/network_manager_handlers.c b/components/wifi-manager/network_manager_handlers.c index 540c02dc..13aed339 100644 --- a/components/wifi-manager/network_manager_handlers.c +++ b/components/wifi-manager/network_manager_handlers.c @@ -39,7 +39,7 @@ #include "accessors.h" #include "esp_err.h" -#include "globdefs.h" +#include "tools.h" #include "http_server_handlers.h" #include "network_manager.h" diff --git a/components/wifi-manager/network_status.c b/components/wifi-manager/network_status.c index edc9d862..7aacf14c 100644 --- a/components/wifi-manager/network_status.c +++ b/components/wifi-manager/network_status.c @@ -6,7 +6,6 @@ #include #include "bt_app_core.h" #include "esp_log.h" -#include "globdefs.h" #include "lwip/inet.h" #include "monitor.h" #include "network_ethernet.h" diff --git a/components/wifi-manager/network_wifi.c b/components/wifi-manager/network_wifi.c index 392ac6e4..f277db03 100644 --- a/components/wifi-manager/network_wifi.c +++ b/components/wifi-manager/network_wifi.c @@ -10,7 +10,6 @@ #include "esp_system.h" #include "esp_wifi.h" #include "esp_wifi_types.h" -#include "globdefs.h" #include "lwip/sockets.h" #include "messaging.h" #include "network_status.h" diff --git a/components/wifi-manager/wifi_manager_http_server.c b/components/wifi-manager/wifi_manager_http_server.c index f640cf85..649be567 100644 --- a/components/wifi-manager/wifi_manager_http_server.c +++ b/components/wifi-manager/wifi_manager_http_server.c @@ -25,7 +25,6 @@ #include "http_server_handlers.h" #include "esp_log.h" #include "esp_http_server.h" -#include "_esp_http_server.h" #include #include #include @@ -34,14 +33,20 @@ #include "freertos/task.h" #include "messaging.h" #include "platform_esp32.h" -#include "globdefs.h" #include "trace.h" +#include "tools.h" static const char TAG[] = "http_server"; EXT_RAM_ATTR static httpd_handle_t _server = NULL; +EXT_RAM_ATTR static int _port; EXT_RAM_ATTR rest_server_context_t *rest_context = NULL; EXT_RAM_ATTR RingbufHandle_t messaging=NULL; +httpd_handle_t get_http_server(int *port) { + if (port) *port = _port; + return _server; +} + void register_common_handlers(httpd_handle_t server){ httpd_uri_t css_get = { .uri = "/css/*", .method = HTTP_GET, .handler = resource_filehandler, .user_ctx = rest_context }; httpd_register_uri_handler(server, &css_get); @@ -143,14 +148,17 @@ esp_err_t http_server_start() httpd_config_t config = HTTPD_DEFAULT_CONFIG(); config.max_uri_handlers = 30; - config.max_open_sockets = 8; + config.max_open_sockets = 3; + config.lru_purge_enable = true; + config.backlog_conn = 1; config.uri_match_fn = httpd_uri_match_wildcard; config.task_priority = ESP_TASK_PRIO_MIN; + _port = config.server_port; //todo: use the endpoint below to configure session token? // config.open_fn MEMTRACE_PRINT_DELTA_MESSAGE( "Starting HTTP Server"); - esp_err_t err= __httpd_start(&_server, &config); + esp_err_t err= httpd_start(&_server, &config); if(err != ESP_OK){ ESP_LOGE_LOC(TAG,"Start server failed"); } diff --git a/file-size.txt b/file-size.txt deleted file mode 100644 index 5a7aadb9..00000000 Binary files a/file-size.txt and /dev/null differ diff --git a/file_size.txt b/file_size.txt deleted file mode 100644 index e503202c..00000000 Binary files a/file_size.txt and /dev/null differ diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index d5d0d7da..11d1029f 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,7 +1,7 @@ -idf_component_register(SRC_DIRS . +idf_component_register(SRC_DIRS . PRIV_REQUIRES esp_common wifi-manager pthread squeezelite-ota platform_console telnet display - INCLUDE_DIRS . EMBED_FILES ../server_certs/github.pem + LDFRAGMENTS "linker.lf" ) #get_target_property(ill ${COMPONENT_LIB} INTERFACE_LINK_LIBRARIES) #message("${COMPONENT_LIB} INTERFACE_LINK_LIBRARIES = ${ill}") diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 370db63d..c8cbd249 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -1,40 +1,45 @@ +# This is a hack but it overrides some LWIP Kconfig +# parameters because we are first +config LWIP_MAX_SOCKETS + range 1 32 + menu "Squeezelite-ESP32" menu "Logging" config LOGGING_SLIMPROTO - string "logging level for slimproto" - default "info" - help + string "logging level for slimproto" + default "info" + help Set logging level info|debug|sdebug config LOGGING_STREAM - string "logging level for stream" - default "info" - help + string "logging level for stream" + default "info" + help Set logging level info|debug|sdebug config LOGGING_DECODE - string "logging level for decode" + string "logging level for decode" default "info" - help - Set logging level info|debug|sdebug - config LOGGING_OUTPUT - string "logging level for output" - default "info" - help - Set logging level info|debug|sdebug + help + Set logging level info|debug|sdebug + config LOGGING_OUTPUT + string "logging level for output" + default "info" + help + Set logging level info|debug|sdebug endmenu config JACK_LOCKED bool - config BAT_LOCKED + config BAT_LOCKED bool config I2C_LOCKED bool config SPDIF_LOCKED - bool + bool config LED_LOCKED - bool + bool config SPKFAULT_LOCKED - bool + bool config MUTE_GPIO_LEVEL - int + int default 0 menu "Target" choice OUTPUT_TYPE @@ -42,7 +47,7 @@ menu "Squeezelite-ESP32" default BASIC_I2C_BT help Type of hardware platform - config SQUEEZEAMP + config SQUEEZEAMP bool "SqueezeAMP" select JACK_LOCKED select BAT_LOCKED @@ -50,47 +55,50 @@ menu "Squeezelite-ESP32" select LED_LOCKED select SPKFAULT_LOCKED config BASIC_I2C_BT - bool "Generic I2S & Bluetooth" - config TWATCH2020 - bool "T-WATCH2020 by LilyGo" - select I2C_LOCKED - endchoice + bool "Generic I2S & Bluetooth" + config TWATCH2020 + bool "T-WATCH2020 by LilyGo" + select I2C_LOCKED + endchoice config RELEASE_API - string "Software update URL" + string "Software update URL" default "https://api.github.com/repos/sle118/squeezelite-esp32/releases" - help - Set the URL of the API that the front-end UI will use to fetch software updates + help + Set the URL of the API that the front-end UI will use to fetch software updates config SQUEEZELITE_ESP32_RELEASE_URL string "Release URL" default "https://github.com/sle118/squeezelite-esp32/releases" help Set the URL where users can see a list of releases # you can't change default values once they are set so changing "Target" will not reset - # project name if they are visible config - they have to be silent strings + # project name if they are visible config - they have to be silent strings config PROJECT_NAME string default "SqueezeAMP" if SQUEEZEAMP default "Squeezelite-TWATCH" if TWATCH2020 default "Squeezelite-ESP32" config FW_PLATFORM_NAME - string + string default "SqueezeAmp" if SQUEEZEAMP default "TWATCH" if TWATCH2020 default "ESP32" # AGGREGATES - begin - # these parameters are "aggregates" that take precedence. They must have a default value + # these parameters are "aggregates" that take precedence. They must have a default value config DAC_CONFIG - string + 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 "" - config SPDIF_CONFIG + config SPDIF_CONFIG string default "bck=33,ws=25,do=15" if SQUEEZEAMP default "" + config GPIO_EXP_CONFIG + string + default "" config SPI_CONFIG string - default "dc=27,data=19,clk=18" if TWATCH2020 + default "dc=27,data=19,clk=18" if TWATCH2020 default "" config DISPLAY_CONFIG string @@ -102,87 +110,87 @@ menu "Squeezelite-ESP32" 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 "" - # AGGREGATES - end + default "" + # AGGREGATES - end endmenu menu "Audio settings" - menu "DAC settings" + menu "DAC settings" visible if BASIC_I2C_BT menu "I2S settings" - config I2S_NUM - int "I2S channel (0 or 1)." + config I2S_NUM + int "I2S channel (0 or 1)" default 0 help - I2S dma channel to use. - config I2S_BCK_IO - int "I2S Bit clock GPIO number." - default -1 + I2S dma channel to use. + config I2S_BCK_IO + int "I2S Bit clock GPIO number" + default -1 help - I2S Bit Clock gpio pin to use. - config I2S_WS_IO - int "I2S Word Select GPIO number." - default -1 + I2S Bit Clock gpio pin to use. + config I2S_WS_IO + int "I2S Word Select GPIO number" + default -1 help I2S Word Select gpio pin to use. - config I2S_DO_IO - int "I2S Data Output GPIO number." + config I2S_DO_IO + int "I2S Data Output GPIO number" default -1 help I2S data output gpio pin to use. - config I2S_DI_IO - int "I2S Data Input GPIO number." + config I2S_DI_IO + int "I2S Data Input GPIO number" default -1 help - I2S data input gpio pin to use (not used mostly, leave it to -1). + I2S data input gpio pin to use (not used mostly, leave it to -1). endmenu - menu "I2C settings" + menu "I2C settings" config I2C_SDA - int "I2C SDA GPIO number for DAC control." + int "I2C SDA GPIO number for DAC control" default -1 help - I2C data gpio pin to use with DAC (not used mostly, leave it to -1). + I2C data gpio pin to use with DAC (not used mostly, leave it to -1). config I2C_SCL - int "I2C SCL GPIO number for DAC control." + int "I2C SCL GPIO number for DAC control" default -1 help - I2C clock gpio pin to use with DAC (not used mostly, leave it to -1). - endmenu + I2C clock gpio pin to use with DAC (not used mostly, leave it to -1). + endmenu config MUTE_GPIO int "GPIO for muting DAC" - default -1 + default -1 help - GPIO used to mute DAC (not used mostly, leave it to -1). + GPIO used to mute DAC (not used mostly, leave it to -1). config MUTE_GPIO_LEVEL int "Mute GPIO active level" depends on MUTE_GPIO != -1 - default 1 + default 1 endmenu - - menu "SPDIF settings" + + menu "SPDIF settings" visible if BASIC_I2C_BT - config SDIF_NUM + config SDIF_NUM int "I2S channel for SDPIF (0 or 1)" - default 0 + default 0 help - I2S dma channel to use. - config SPDIF_BCK_IO + I2S dma channel to use. + config SPDIF_BCK_IO int "SDPIF Bit clock GPIO number" default I2S_BCK_IO help Must be set as SPDIF re-uses I2S but only needs DO (recommendation: set it to I2S Bit clock value) - config SPDIF_WS_IO + config SPDIF_WS_IO int "SPDIF Word Select GPIO number" default I2S_WS_IO help Must be set as SPDIF re-uses I2S but only needs DO (recommendation: set it to I2S Word select value) - config SPDIF_DO_IO + config SPDIF_DO_IO int "SPDIF Data I/O GPIO number" default -1 help I2S data output IO use to simulate SPDIF endmenu - + menu "A2DP settings" config A2DP_SINK_NAME string "Name of Bluetooth A2DP device" @@ -195,19 +203,18 @@ menu "Squeezelite-ESP32" help This is the name of the device that the Bluetooth speaker will see when it is connected to. config A2DP_CONTROL_DELAY_MS - int "Control loop delay." + int "Control loop delay" default 500 help - Decreasing this will lead to a more responsive BT control, - but might lead to noisy log files if debug is enabled. + Decreasing this will lead to a more responsive BT control, but might lead to noisy log files if debug is enabled. config A2DP_CONNECT_TIMEOUT_MS int "Time out duration when trying to connect to an A2DP audio sink" default 1000 help - Increasing this value will give more chance for less stable connections to be established. + Increasing this value will give more chance for less stable connections to be established. endmenu endmenu - + menu "Audio Input" config BT_SINK bool "Bluetooth receiver" @@ -219,9 +226,9 @@ menu "Squeezelite-ESP32" string "Name of Bluetooth A2DP device" default "ESP32-BT" help - This is the name of the bluetooth speaker that will be broadcasted - config BT_SINK_PIN - depends on BT_SINK + This is the name of the bluetooth speaker that will be broadcasted + config BT_SINK_PIN + depends on BT_SINK int "Bluetooth PIN code" default 1234 config AIRPLAY_SINK @@ -232,14 +239,19 @@ menu "Squeezelite-ESP32" string "Name of AirPlay device" default "ESP32-AirPlay" help - This is the name of the AirPlay speaker that will be broadcasted + This is the name of the AirPlay speaker that will be broadcasted config AIRPLAY_PORT depends on AIRPLAY_SINK string "AirPlay listening port" default "5000" - help + help AirPlay service listening port - endmenu + config CSPOT_SINK + bool "Spotify (cspot) receiver" + default y + help + Enable Spotify connect using CSpot + endmenu menu "Display Screen" depends on !TWATCH2020 @@ -249,8 +261,8 @@ menu "Squeezelite-ESP32" Set parameters for display screen, leave empty for no screen I2C,driver=,width=,height=[address=][,HFlip][,VFlip][,rotate] SPI,driver=,width=,height=,cs=[,HFlip][,VFlip][,rotate] - endmenu - + endmenu + menu "Various I/O" visible if !TWATCH2020 config I2C_CONFIG @@ -263,14 +275,14 @@ menu "Squeezelite-ESP32" string "SPI system configuration" help Set parameters of shared SPI interface - data=,clk=[,d/c=][,host=<0|1|2>] - config SET_GPIO + data=,clk=[,d/c=][,host=<0|1|2>] + config SET_GPIO string "Special GPIO configuration" default "" help - Set parameters of shared GPIO with special values. + Set parameters of shared GPIO with special values. =Vcc|GND|amp[:0|1]|jack[:0|1][,=Vcc|GND|amp[:0|1]|jack[:0|1]] - 'amp' => GPIO that is set when playback starts + 'amp' => GPIO that is set when playback starts 'jack' => GPIO used for audio jack detection 'green', 'red' => GPIO for status LED '[:0|1] means set the active value for that GPIO can be low or high @@ -280,21 +292,26 @@ menu "Squeezelite-ESP32" help Set GPIO for rotary encoder (quadrature phase). See README on SqueezeESP32 project's GitHub for more details A=,B=[,SW=gpio>[[,knobonly[=]|[,volume][,longpress]] + config GPIO_EXP_CONFIG + string "GPIO expander configuration" + help + 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 config LED_GREEN_GPIO int "Green led GPIO" - default 12 if SQUEEZEAMP - default -1 + default 12 if SQUEEZEAMP + default -1 help Set to -1 for no LED config LED_GREEN_GPIO_LEVEL int "Green led ON level" depends on LED_GREEN_GPIO != -1 default 0 if SQUEEZEAMP - default 1 - config LED_RED_GPIO + default 1 + config LED_RED_GPIO int "Red led GPIO" default 13 if SQUEEZEAMP default -1 @@ -306,45 +323,45 @@ menu "Squeezelite-ESP32" default 0 if SQUEEZEAMP default 1 endmenu - menu "Audio JACK" + menu "Audio JACK" visible if !SQUEEZEAMP && !TWATCH2020 - config JACK_GPIO + config JACK_GPIO int "Jack insertion GPIO" default 34 if SQUEEZEAMP default -1 help - GPIO to detect speaker jack insertion. Set to -1 for no detection. + GPIO to detect speaker jack insertion. Set to -1 for no detection. config JACK_GPIO_LEVEL depends on JACK_GPIO != -1 int "Level when inserted (0/1)" default 0 - endmenu - menu "Speaker Fault" + endmenu + menu "Speaker Fault" visible if !SQUEEZEAMP && !TWATCH2020 - config SPKFAULT_GPIO + config SPKFAULT_GPIO int "Speaker fault GPIO" default 2 if SQUEEZEAMP default -1 help - GPIO to detect speaker fault condition. Set to -1 for no detection. + GPIO to detect speaker fault condition. Set to -1 for no detection. config SPKFAULT_GPIO_LEVEL depends on SPKFAULT_GPIO != -1 int "Level when fault (0/1)" default 0 - endmenu - menu "Battery measure" + endmenu + menu "Battery measure" visible if !SQUEEZEAMP && !TWATCH2020 - config BAT_CHANNEL + config BAT_CHANNEL int "Set channel (0..7)" default 7 if SQUEEZEAMP - default -1 + default -1 help Read a value every 10s on ADC1 on set Channel - config BAT_SCALE + config BAT_SCALE string "Set scaling factor" depends on BAT_CHANNEL != -1 default "20.24" if SQUEEZEAMP - default "" + default "" help Set the scaling factor for this 12 bits ADC endmenu diff --git a/main/esp_app_main.c b/main/esp_app_main.c index 22724c3f..8231c87d 100644 --- a/main/esp_app_main.c +++ b/main/esp_app_main.c @@ -45,7 +45,14 @@ #include "display.h" #include "accessors.h" #include "cmd_system.h" -#include "globdefs.h" +#include "tools.h" + +#ifndef CONFIG_DAC_KNOWN_CONFIGURATIONS +#define CONFIG_DAC_KNOWN_CONFIGURATIONS "" +#endif +#ifndef CONFIG_DAC_KNOWN_CONFIGURATIONS_GPIOS +#define CONFIG_DAC_KNOWN_CONFIGURATIONS_GPIOS "" +#endif static const char certs_namespace[] = "certificates"; static const char certs_key[] = "blob"; @@ -230,7 +237,6 @@ const char * get_certificate(){ size_t len; esp_err = nvs_get_blob(handle, certs_key, NULL, &len); if( esp_err == ESP_OK) { - //blob = (char *) heap_caps_malloc(len+1, (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)); blob = (char *) malloc_init_external(len+1); if(!blob){ log_send_messaging(MESSAGING_ERROR,"Unable to retrieve HTTPS certificates. %s","Memory allocation failed"); @@ -288,33 +294,40 @@ void register_default_with_mac(const char* key, char* defval) { } } -#ifndef CONFIG_DAC_KNOWN_CONFIGURATIONS -#define CONFIG_DAC_KNOWN_CONFIGURATIONS "" -#endif -#ifndef CONFIG_DAC_KNOWN_CONFIGURATIONS_GPIOS -#define CONFIG_DAC_KNOWN_CONFIGURATIONS_GPIOS "" -#endif - void register_default_nvs(){ - - register_default_with_mac("bt_name", CONFIG_BT_NAME); - register_default_with_mac("host_name", DEFAULT_HOST_NAME); +#ifdef CONFIG_CSPOT_SINK + register_default_string_val("enable_cspot", STR(CONFIG_CSPOT_SINK)); +#endif + +#ifdef CONFIG_AIRPLAY_SINK register_default_with_mac("airplay_name", CONFIG_AIRPLAY_NAME); + register_default_string_val("airplay_port", CONFIG_AIRPLAY_PORT); + register_default_string_val( "enable_airplay", STR(CONFIG_AIRPLAY_SINK)); +#endif + +#ifdef CONFIG_BT_SINK + register_default_string_val( "bt_sink_pin", STR(CONFIG_BT_SINK_PIN)); + register_default_string_val( "bt_sink_volume", "127"); + register_default_with_mac("bt_name", CONFIG_BT_NAME); + register_default_string_val( "enable_bt_sink", STR(CONFIG_BT_SINK)); + register_default_string_val("a2dp_dev_name", CONFIG_A2DP_DEV_NAME); + register_default_string_val("a2dp_ctmt", STR(CONFIG_A2DP_CONNECT_TIMEOUT_MS)); + register_default_string_val("a2dp_ctrld", STR(CONFIG_A2DP_CONTROL_DELAY_MS)); + register_default_string_val("a2dp_sink_name", CONFIG_A2DP_SINK_NAME); +#endif + + register_default_with_mac("host_name", DEFAULT_HOST_NAME); register_default_with_mac("ap_ssid", CONFIG_DEFAULT_AP_SSID); register_default_string_val("autoexec","1"); register_default_with_mac("autoexec1",CONFIG_DEFAULT_COMMAND_LINE " -n " DEFAULT_HOST_NAME); - register_default_string_val("a2dp_sink_name", CONFIG_A2DP_SINK_NAME); - register_default_string_val("a2dp_ctmt", STR(CONFIG_A2DP_CONNECT_TIMEOUT_MS)); - register_default_string_val("a2dp_ctrld", STR(CONFIG_A2DP_CONTROL_DELAY_MS)); + register_default_string_val("release_url", CONFIG_SQUEEZELITE_ESP32_RELEASE_URL); register_default_string_val("ap_ip_address",CONFIG_DEFAULT_AP_IP); register_default_string_val("ap_ip_gateway",CONFIG_DEFAULT_AP_GATEWAY ); register_default_string_val("ap_ip_netmask",CONFIG_DEFAULT_AP_NETMASK); register_default_string_val("ap_channel",STR(CONFIG_DEFAULT_AP_CHANNEL)); register_default_string_val("ap_pwd", CONFIG_DEFAULT_AP_PASSWORD); - register_default_string_val("airplay_port", CONFIG_AIRPLAY_PORT); - register_default_string_val("a2dp_dev_name", CONFIG_A2DP_DEV_NAME); register_default_string_val("bypass_wm", "0"); register_default_string_val("equalizer", ""); register_default_string_val("actrls_config", ""); @@ -327,10 +340,6 @@ void register_default_nvs(){ register_default_string_val( "ota_stack", number_buffer); snprintf(number_buffer,sizeof(number_buffer)-1,"%d",OTA_TASK_PRIOTITY); register_default_string_val( "ota_prio", number_buffer); - register_default_string_val( "enable_bt_sink", STR(CONFIG_BT_SINK)); - register_default_string_val( "bt_sink_pin", STR(CONFIG_BT_SINK_PIN)); - register_default_string_val( "bt_sink_volume", "127"); - register_default_string_val( "enable_airplay", STR(CONFIG_AIRPLAY_SINK)); register_default_string_val( "display_config", CONFIG_DISPLAY_CONFIG); register_default_string_val( "eth_config", CONFIG_ETH_CONFIG); register_default_string_val( "i2c_config", CONFIG_I2C_CONFIG); @@ -341,6 +350,7 @@ void register_default_nvs(){ register_default_string_val( "dac_config", ""); register_default_string_val( "dac_controlset", ""); register_default_string_val( "jack_mutes_amp", "n"); + register_default_string_val("gpio_exp_config", CONFIG_GPIO_EXP_CONFIG); register_default_string_val( "bat_config", ""); register_default_string_val( "metadata_config", ""); register_default_string_val( "telnet_enable", ""); diff --git a/main/linker.lf b/main/linker.lf new file mode 100644 index 00000000..b76ab440 --- /dev/null +++ b/main/linker.lf @@ -0,0 +1,4 @@ +[mapping:cpp] +archive: libstdc++.a +entries: + * (extram_bss) \ No newline at end of file diff --git a/main/platform_esp32.h b/main/platform_esp32.h index d8939db5..bd788706 100644 --- a/main/platform_esp32.h +++ b/main/platform_esp32.h @@ -16,7 +16,6 @@ #define CONFIG_SQUEEZELITE_ESP32_RELEASE_URL "https://github.com/sle118/squeezelite-esp32/releases" #endif -extern void run_command(char * line); extern bool wait_for_wifi(); extern void console_start(); extern pthread_cond_t wifi_connect_suspend_cond; diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 6ee64b2e..503124c1 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -2,105 +2,134 @@ # Automatically generated file. DO NOT EDIT. # Espressif IoT Development Framework (ESP-IDF) Project Configuration # - -# DSP -CONFIG_DSP_OPTIMIZED=y -CONFIG_DSP_OPTIMIZATION=1 -CONFIG_DSP_MAX_FFT_SIZE_512=y -CONFIG_IDF_TARGET_ESP32=y +CONFIG_IDF_CMAKE=y +CONFIG_IDF_TARGET_ARCH_XTENSA=y CONFIG_IDF_TARGET="esp32" +CONFIG_IDF_TARGET_ESP32=y +CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000 # # SDK tool configuration # CONFIG_SDK_TOOLPREFIX="xtensa-esp32-elf-" -CONFIG_SDK_MAKE_WARN_UNDEFINED_VARIABLES=y +# CONFIG_SDK_TOOLCHAIN_SUPPORTS_TIME_WIDE_64_BITS is not set +# end of SDK tool configuration + +# +# Build type +# +CONFIG_APP_BUILD_TYPE_APP_2NDBOOT=y +# CONFIG_APP_BUILD_TYPE_ELF_RAM is not set +CONFIG_APP_BUILD_GENERATE_BINARIES=y +CONFIG_APP_BUILD_BOOTLOADER=y +CONFIG_APP_BUILD_USE_FLASH_SECTIONS=y +# end of Build type + +# +# Application manager +# CONFIG_APP_COMPILE_TIME_DATE=y +# CONFIG_APP_EXCLUDE_PROJECT_VER_VAR is not set +# CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR is not set +# CONFIG_APP_PROJECT_VER_FROM_CONFIG is not set +CONFIG_APP_RETRIEVE_LEN_ELF_SHA=16 +# end of Application manager - - -CONFIG_OTA_ALLOW_HTTP=y - - +# +# Bootloader config +# +CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x1000 +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG is not set +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF is not set +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y - - +# CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set CONFIG_BOOTLOADER_LOG_LEVEL=3 +# CONFIG_BOOTLOADER_SPI_CUSTOM_WP_PIN is not set CONFIG_BOOTLOADER_SPI_WP_PIN=7 CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y - - +# CONFIG_BOOTLOADER_FACTORY_RESET is not set +# CONFIG_BOOTLOADER_APP_TEST is not set CONFIG_BOOTLOADER_WDT_ENABLE=y - +# CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set CONFIG_BOOTLOADER_WDT_TIME_MS=9000 +# CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_ON_POWER_ON is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_ALWAYS is not set +CONFIG_BOOTLOADER_RESERVE_RTC_SIZE=0 +# CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC is not set +CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT=y +# end of Bootloader config -CONFIG_ESPTOOLPY_BAUD_2MB=y - +# +# Security features +# +# CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT is not set +# CONFIG_SECURE_BOOT is not set +# CONFIG_SECURE_FLASH_ENC_ENABLED is not set +# end of Security features +# +# Serial flasher config +# CONFIG_ESPTOOLPY_BAUD_OTHER_VAL=115200 -CONFIG_ESPTOOLPY_BAUD=2000000 -CONFIG_ESPTOOLPY_COMPRESSED=y +# CONFIG_ESPTOOLPY_NO_STUB is not set CONFIG_ESPTOOLPY_FLASHMODE_QIO=y - - - +# CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set +# CONFIG_ESPTOOLPY_FLASHMODE_DIO is not set +# CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set CONFIG_ESPTOOLPY_FLASHMODE="dio" CONFIG_ESPTOOLPY_FLASHFREQ_80M=y - - - +# CONFIG_ESPTOOLPY_FLASHFREQ_40M is not set +# CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set +# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set CONFIG_ESPTOOLPY_FLASHFREQ="80m" - - +# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y - - +# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set CONFIG_ESPTOOLPY_FLASHSIZE="4MB" CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y CONFIG_ESPTOOLPY_BEFORE_RESET=y - +# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set CONFIG_ESPTOOLPY_BEFORE="default_reset" CONFIG_ESPTOOLPY_AFTER_RESET=y - +# CONFIG_ESPTOOLPY_AFTER_NORESET is not set CONFIG_ESPTOOLPY_AFTER="hard_reset" - - +# CONFIG_ESPTOOLPY_MONITOR_BAUD_CONSOLE is not set +# CONFIG_ESPTOOLPY_MONITOR_BAUD_9600B is not set +# CONFIG_ESPTOOLPY_MONITOR_BAUD_57600B is not set CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y - - - - +# CONFIG_ESPTOOLPY_MONITOR_BAUD_230400B is not set +# CONFIG_ESPTOOLPY_MONITOR_BAUD_921600B is not set +# CONFIG_ESPTOOLPY_MONITOR_BAUD_2MB is not set +# CONFIG_ESPTOOLPY_MONITOR_BAUD_OTHER is not set CONFIG_ESPTOOLPY_MONITOR_BAUD_OTHER_VAL=115200 CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 +# end of Serial flasher config - +# +# Partition Table +# +# CONFIG_PARTITION_TABLE_SINGLE_APP is not set +# CONFIG_PARTITION_TABLE_TWO_OTA is not set CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_OFFSET=0x8000 CONFIG_PARTITION_TABLE_MD5=y -CONFIG_LOGGING_SLIMPROTO="info" -CONFIG_LOGGING_STREAM="info" -CONFIG_LOGGING_DECODE="info" -CONFIG_LOGGING_OUTPUT="info" -CONFIG_INCLUDE_FLAC=y -CONFIG_INCLUDE_FAAD=y -CONFIG_INCLUDE_MAD=y -CONFIG_INCLUDE_VORBIS=y -CONFIG_INCLUDE_ALAC=y +# end of Partition Table -CONFIG_SQUEEZEAMP=y - -CONFIG_A2DP_SINK_NAME="SMSL BT4.2" -CONFIG_A2DP_DEV_NAME="Squeezelite" -CONFIG_A2DP_CONTROL_DELAY_MS=500 -CONFIG_A2DP_CONNECT_TIMEOUT_MS=1000 -CONFIG_BT_SINK=y -CONFIG_BT_SINK_NAME="ESP32-BT" -CONFIG_BT_SINK_PIN=1234 -CONFIG_AIRPLAY_SINK=y -CONFIG_AIRPLAY_NAME="ESP32-AirPlay" -CONFIG_AIRPLAY_PORT="5000" +# +# Wifi Manager Configuration +# CONFIG_WIFI_MANAGER_TASK_PRIORITY=5 CONFIG_WIFI_MANAGER_MAX_RETRY=2 CONFIG_DEFAULT_AP_SSID="squeezelite" @@ -111,566 +140,1025 @@ CONFIG_DEFAULT_AP_GATEWAY="192.168.4.1" CONFIG_DEFAULT_AP_NETMASK="255.255.255.0" CONFIG_DEFAULT_AP_MAX_CONNECTIONS=4 CONFIG_DEFAULT_AP_BEACON_INTERVAL=100 -CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -C 30" +CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -C 30 -W" +# end of Wifi Manager Configuration +# +# Squeezelite-ESP32 +# + +# +# Logging +# +CONFIG_LOGGING_SLIMPROTO="info" +CONFIG_LOGGING_STREAM="info" +CONFIG_LOGGING_DECODE="info" +CONFIG_LOGGING_OUTPUT="info" +# end of Logging + +CONFIG_MUTE_GPIO_LEVEL=0 + +# +# Target +# +# CONFIG_SQUEEZEAMP is not set +CONFIG_BASIC_I2C_BT=y +# CONFIG_TWATCH2020 is not set +CONFIG_RELEASE_API="https://api.github.com/repos/sle118/squeezelite-esp32/releases" +CONFIG_SQUEEZELITE_ESP32_RELEASE_URL="https://github.com/sle118/squeezelite-esp32/releases" +CONFIG_PROJECT_NAME="Squeezelite-ESP32" +CONFIG_FW_PLATFORM_NAME="ESP32" +CONFIG_DAC_CONFIG="" +CONFIG_SPDIF_CONFIG="" +CONFIG_SPI_CONFIG="" +CONFIG_DISPLAY_CONFIG="" +CONFIG_ETH_CONFIG="" +CONFIG_DAC_CONTROLSET="" +# end of Target + +# +# Ethernet Options +# +CONFIG_ETH_NODRIVER=y +# CONFIG_ETH_LAN8720 is not set +# CONFIG_ETH_DM9051 is not set +CONFIG_ETH_PHY_RST_IO=-1 +CONFIG_ETH_MDC_IO=-1 +CONFIG_ETH_MDIO_IO=-1 +CONFIG_ETH_SPI_HOST=-1 +CONFIG_ETH_SPI_INTR_IO=-1 +CONFIG_ETH_SPI_CS_IO=-1 +CONFIG_ETH_SPI_CLK_IO=-1 +CONFIG_ETH_SPI_MOSI_IO=-1 +CONFIG_ETH_SPI_MISO_IO=-1 +CONFIG_ETH_SPI_SPEED=20000000 +# end of Ethernet Options + +# +# Audio settings +# + +# +# DAC settings +# + +# +# I2S settings +# +CONFIG_I2S_NUM=0 +CONFIG_I2S_BCK_IO=-1 +CONFIG_I2S_WS_IO=-1 +CONFIG_I2S_DO_IO=-1 +CONFIG_I2S_DI_IO=-1 +# end of I2S settings + +# +# I2C settings +# +CONFIG_I2C_SDA=-1 +CONFIG_I2C_SCL=-1 +# end of I2C settings + +CONFIG_MUTE_GPIO=-1 +# end of DAC settings + +# +# SPDIF settings +# +CONFIG_SDIF_NUM=0 +CONFIG_SPDIF_BCK_IO=-1 +CONFIG_SPDIF_WS_IO=-1 +CONFIG_SPDIF_DO_IO=-1 +# end of SPDIF settings + +# +# A2DP settings +# +CONFIG_A2DP_SINK_NAME="SMSL BT4.2" +CONFIG_A2DP_DEV_NAME="Squeezelite" +CONFIG_A2DP_CONTROL_DELAY_MS=500 +CONFIG_A2DP_CONNECT_TIMEOUT_MS=1000 +# end of A2DP settings +# end of Audio settings + +# +# Audio Input +# +CONFIG_BT_SINK=y +CONFIG_BT_NAME="ESP32-BT" +CONFIG_BT_SINK_PIN=1234 +CONFIG_AIRPLAY_SINK=y +CONFIG_AIRPLAY_NAME="ESP32-AirPlay" +CONFIG_AIRPLAY_PORT="5000" +# CONFIG_CSPOT_SINK is not set +# end of Audio Input + +# +# Display Screen +# +# end of Display Screen + +# +# Various I/O +# +CONFIG_I2C_CONFIG="" +CONFIG_SET_GPIO="" +CONFIG_ROTARY_ENCODER="" +# end of Various I/O + +# +# LED configuration +# +CONFIG_LED_GREEN_GPIO=-1 +CONFIG_LED_RED_GPIO=-1 +# end of LED configuration + +# +# Audio JACK +# +CONFIG_JACK_GPIO=-1 +# end of Audio JACK + +# +# Speaker Fault +# +CONFIG_SPKFAULT_GPIO=-1 +# end of Speaker Fault + +# +# Battery measure +# +CONFIG_BAT_CHANNEL=-1 +# end of Battery measure +# end of Squeezelite-ESP32 + +# +# Compiler options +# +# CONFIG_COMPILER_OPTIMIZATION_DEFAULT is not set CONFIG_COMPILER_OPTIMIZATION_SIZE=y -CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y - - - +# CONFIG_COMPILER_OPTIMIZATION_PERF is not set +# CONFIG_COMPILER_OPTIMIZATION_NONE is not set +# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE is not set +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y +# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set +CONFIG_COMPILER_CXX_EXCEPTIONS=y +CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=0 +# CONFIG_COMPILER_CXX_RTTI is not set CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y +# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set +# CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set +# CONFIG_COMPILER_STACK_CHECK_MODE_ALL is not set +# CONFIG_COMPILER_WARN_WRITE_STRINGS is not set +# CONFIG_COMPILER_DISABLE_GCC8_WARNINGS is not set +# CONFIG_COMPILER_DUMP_RTL_FILES is not set +# end of Compiler options +# +# Component config +# - - - - - +# +# Application Level Tracing +# +# CONFIG_APPTRACE_DEST_TRAX is not set CONFIG_APPTRACE_DEST_NONE=y - CONFIG_APPTRACE_LOCK_ENABLE=y +# end of Application Level Tracing + +# +# ESP-ASIO +# +# CONFIG_ASIO_SSL_SUPPORT is not set +# end of ESP-ASIO + +# +# Bluetooth +# CONFIG_BT_ENABLED=y +CONFIG_BT_CTRL_ESP32=y +# +# Bluetooth controller(ESP32 Dual Mode Bluetooth) +# +# CONFIG_BTDM_CTRL_MODE_BLE_ONLY is not set CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=y - +# CONFIG_BTDM_CTRL_MODE_BTDM is not set CONFIG_BTDM_CTRL_BR_EDR_MAX_ACL_CONN=2 CONFIG_BTDM_CTRL_BR_EDR_MAX_SYNC_CONN=0 - +# CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_HCI is not set CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_PCM=y CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_EFF=1 +CONFIG_BTDM_CTRL_PCM_ROLE_EDGE_CONFIG=y +CONFIG_BTDM_CTRL_PCM_ROLE_MASTER=y +# CONFIG_BTDM_CTRL_PCM_ROLE_SLAVE is not set +CONFIG_BTDM_CTRL_PCM_POLAR_FALLING_EDGE=y +# CONFIG_BTDM_CTRL_PCM_POLAR_RISING_EDGE is not set +CONFIG_BTDM_CTRL_PCM_ROLE_EFF=0 +CONFIG_BTDM_CTRL_PCM_POLAR_EFF=0 +CONFIG_BTDM_CTRL_LEGACY_AUTH_VENDOR_EVT=y +CONFIG_BTDM_CTRL_LEGACY_AUTH_VENDOR_EVT_EFF=y CONFIG_BTDM_CTRL_BLE_MAX_CONN_EFF=0 CONFIG_BTDM_CTRL_BR_EDR_MAX_ACL_CONN_EFF=2 CONFIG_BTDM_CTRL_BR_EDR_MAX_SYNC_CONN_EFF=0 CONFIG_BTDM_CTRL_PINNED_TO_CORE_0=y - +# CONFIG_BTDM_CTRL_PINNED_TO_CORE_1 is not set CONFIG_BTDM_CTRL_PINNED_TO_CORE=0 CONFIG_BTDM_CTRL_HCI_MODE_VHCI=y +# CONFIG_BTDM_CTRL_HCI_MODE_UART_H4 is not set -CONFIG_BTDM_MODEM_SLEEP=y -CONFIG_BTDM_MODEM_SLEEP_MODE_ORIG=y +# +# MODEM SLEEP Options +# +CONFIG_BTDM_CTRL_MODEM_SLEEP=y +CONFIG_BTDM_CTRL_MODEM_SLEEP_MODE_ORIG=y +# CONFIG_BTDM_CTRL_MODEM_SLEEP_MODE_EVED is not set +CONFIG_BTDM_CTRL_LPCLK_SEL_MAIN_XTAL=y +# end of MODEM SLEEP Options -CONFIG_BTDM_LPCLK_SEL_MAIN_XTAL=y CONFIG_BTDM_BLE_SLEEP_CLOCK_ACCURACY_INDEX_EFF=1 -CONFIG_BT_BLUEDROID_ENABLED=y +# end of Bluetooth controller(ESP32 Dual Mode Bluetooth) +CONFIG_BT_CTRL_MODE_EFF=1 +CONFIG_BT_CTRL_BLE_MAX_ACT=10 +CONFIG_BT_CTRL_BLE_MAX_ACT_EFF=10 +CONFIG_BT_CTRL_BLE_STATIC_ACL_TX_BUF_NB=0 +CONFIG_BT_CTRL_PINNED_TO_CORE=0 +CONFIG_BT_CTRL_HCI_TL=1 +CONFIG_BT_CTRL_ADV_DUP_FILT_MAX=30 +CONFIG_BT_CTRL_HW_CCA_EFF=0 +CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_EFF=0 +CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_SUPP=y +CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_NUM=100 +CONFIG_BT_CTRL_BLE_ADV_REPORT_DISCARD_THRSHOLD=20 +CONFIG_BT_CTRL_BLE_SCAN_DUPL=y +CONFIG_BT_CTRL_SCAN_DUPL_TYPE=0 +CONFIG_BT_CTRL_SCAN_DUPL_CACHE_SIZE=100 + +# +# MODEM SLEEP Options +# +# end of MODEM SLEEP Options + +CONFIG_BT_CTRL_SLEEP_MODE_EFF=0 +CONFIG_BT_CTRL_SLEEP_CLOCK_EFF=0 +CONFIG_BT_CTRL_HCI_TL_EFF=1 + +# +# MODEM SLEEP Options +# +# end of MODEM SLEEP Options + +CONFIG_BT_BLUEDROID_ENABLED=y +# CONFIG_BT_NIMBLE_ENABLED is not set +# CONFIG_BT_CONTROLLER_ONLY is not set + +# +# Bluedroid Options +# CONFIG_BT_BTC_TASK_STACK_SIZE=3072 CONFIG_BT_BLUEDROID_PINNED_TO_CORE_0=y - +# CONFIG_BT_BLUEDROID_PINNED_TO_CORE_1 is not set CONFIG_BT_BLUEDROID_PINNED_TO_CORE=0 CONFIG_BT_BTU_TASK_STACK_SIZE=4096 - +# CONFIG_BT_BLUEDROID_MEM_DEBUG is not set CONFIG_BT_CLASSIC_ENABLED=y CONFIG_BT_A2DP_ENABLE=y -CONFIG_BT_A2DP_SINK_TASK_STACK_SIZE=2048 -CONFIG_BT_A2DP_SOURCE_TASK_STACK_SIZE=2048 - - +# CONFIG_BT_SPP_ENABLED is not set +# CONFIG_BT_HFP_ENABLE is not set +# CONFIG_BT_HID_HOST_ENABLED is not set CONFIG_BT_SSP_ENABLED=y -CONFIG_BT_BLE_ENABLED=n -CONFIG_BT_BLE_SMP_ENABLE=y - - - - -CONFIG_BT_LOG_HCI_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_HCI_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_BTM_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_BTM_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_L2CAP_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_L2CAP_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_SDP_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_SDP_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_GAP_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_GAP_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_BNEP_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_BNEP_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_PAN_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_PAN_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_A2D_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_A2D_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_AVDT_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_AVDT_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_AVCT_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_AVCT_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_AVRC_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_AVRC_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_MCA_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_MCA_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_HID_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_HID_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_APPL_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_APPL_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_GATT_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_GATT_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_SMP_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_SMP_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_BTIF_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_BTIF_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_BTC_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_BTC_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_OSI_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_OSI_TRACE_LEVEL=2 - - -CONFIG_BT_LOG_BLUFI_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BT_LOG_BLUFI_TRACE_LEVEL=2 +# CONFIG_BT_BLE_ENABLED is not set +CONFIG_BT_STACK_NO_LOG=y CONFIG_BT_ACL_CONNECTIONS=4 - - - +# CONFIG_BT_MULTI_CONNECTION_ENBALE is not set +CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST=y +CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY=y +# CONFIG_BT_BLE_HOST_QUEUE_CONG_CHECK is not set CONFIG_BT_SMP_ENABLE=y CONFIG_BT_BLE_ESTAB_LINK_CONN_TOUT=30 +CONFIG_BT_MAX_DEVICE_NAME_LEN=32 +# CONFIG_BT_BLE_RPA_SUPPORTED is not set CONFIG_BT_RESERVE_DRAM=0xdb5c +# end of Bluedroid Options +CONFIG_BT_NIMBLE_USE_ESP_TIMER=y +# end of Bluetooth +# CONFIG_BLE_MESH is not set + +# +# CoAP Configuration +# +CONFIG_COAP_MBEDTLS_PSK=y +# CONFIG_COAP_MBEDTLS_PKI is not set +# CONFIG_COAP_MBEDTLS_DEBUG is not set +CONFIG_COAP_LOG_DEFAULT_LEVEL=0 +# end of CoAP Configuration + +# +# Driver configurations +# + +# +# ADC configuration +# +# CONFIG_ADC_FORCE_XPD_FSM is not set CONFIG_ADC_DISABLE_DAC=y +# end of ADC configuration -CONFIG_SPI_MASTER_ISR_IN_IRAM=y -CONFIG_SPI_SLAVE_ISR_IN_IRAM=y +# +# SPI configuration +# CONFIG_SPI_MASTER_IN_IRAM=y -CONFIG_SPI_SLAVE_IN_IRAM=y +CONFIG_SPI_MASTER_ISR_IN_IRAM=y +# CONFIG_SPI_SLAVE_IN_IRAM is not set +# CONFIG_SPI_SLAVE_ISR_IN_IRAM is not set +# end of SPI configuration +# +# TWAI configuration +# +# CONFIG_TWAI_ISR_IN_IRAM is not set +# CONFIG_TWAI_ERRATA_FIX_BUS_OFF_REC is not set +# CONFIG_TWAI_ERRATA_FIX_TX_INTR_LOST is not set +# CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID is not set +# CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT is not set +# end of TWAI configuration +# +# UART configuration +# +# CONFIG_UART_ISR_IN_IRAM is not set +# end of UART configuration + +# +# RTCIO configuration +# +# CONFIG_RTCIO_SUPPORT_RTC_GPIO_DESC is not set +# end of RTCIO configuration + +# +# GPIO Configuration +# +# CONFIG_GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL is not set +# end of GPIO Configuration +# end of Driver configurations + +# +# eFuse Bit Manager +# +# CONFIG_EFUSE_CUSTOM_TABLE is not set +# CONFIG_EFUSE_VIRTUAL is not set +# CONFIG_EFUSE_CODE_SCHEME_COMPAT_NONE is not set CONFIG_EFUSE_CODE_SCHEME_COMPAT_3_4=y - +# CONFIG_EFUSE_CODE_SCHEME_COMPAT_REPEAT is not set CONFIG_EFUSE_MAX_BLK_LEN=192 +# end of eFuse Bit Manager +# +# ESP-TLS +# +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 +# end of ESP-TLS - +# +# ESP32-specific +# +CONFIG_ESP32_ECO3_CACHE_LOCK_FIX=y +# CONFIG_ESP32_REV_MIN_0 is not set +CONFIG_ESP32_REV_MIN_1=y +# CONFIG_ESP32_REV_MIN_2 is not set +# CONFIG_ESP32_REV_MIN_3 is not set +CONFIG_ESP32_REV_MIN=1 +CONFIG_ESP32_DPORT_WORKAROUND=y +# CONFIG_ESP32_DEFAULT_CPU_FREQ_80 is not set +# CONFIG_ESP32_DEFAULT_CPU_FREQ_160 is not set CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 CONFIG_ESP32_SPIRAM_SUPPORT=y -CONFIG_SPIRAM_BOOT_INIT=y - -CONFIG_SPIRAM_USE_MALLOC=y +# +# SPI RAM config +# CONFIG_SPIRAM_TYPE_AUTO=y - - +# CONFIG_SPIRAM_TYPE_ESPPSRAM16 is not set +# CONFIG_SPIRAM_TYPE_ESPPSRAM32 is not set +# CONFIG_SPIRAM_TYPE_ESPPSRAM64 is not set CONFIG_SPIRAM_SIZE=-1 - +# CONFIG_SPIRAM_SPEED_40M is not set CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_BOOT_INIT=y +# CONFIG_SPIRAM_USE_MEMMAP is not set +# CONFIG_SPIRAM_USE_CAPS_ALLOC is not set +CONFIG_SPIRAM_USE_MALLOC=y CONFIG_SPIRAM_MEMTEST=y -CONFIG_SPIRAM_CACHE_WORKAROUND=y - CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=256 +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=65536 CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y +CONFIG_SPIRAM_CACHE_WORKAROUND=y + +# +# SPIRAM cache workaround debugging +# +CONFIG_SPIRAM_CACHE_WORKAROUND_STRATEGY_MEMW=y +# CONFIG_SPIRAM_CACHE_WORKAROUND_STRATEGY_DUPLDST is not set +# CONFIG_SPIRAM_CACHE_WORKAROUND_STRATEGY_NOPS is not set +# end of SPIRAM cache workaround debugging + +# CONFIG_SPIRAM_BANKSWITCH_ENABLE is not set CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y - - +# CONFIG_SPIRAM_OCCUPY_HSPI_HOST is not set CONFIG_SPIRAM_OCCUPY_VSPI_HOST=y +# CONFIG_SPIRAM_OCCUPY_NO_HOST is not set + +# +# PSRAM clock and cs IO for ESP32-DOWD +# CONFIG_D0WD_PSRAM_CLK_IO=17 CONFIG_D0WD_PSRAM_CS_IO=16 +# end of PSRAM clock and cs IO for ESP32-DOWD + +# +# PSRAM clock and cs IO for ESP32-D2WD +# CONFIG_D2WD_PSRAM_CLK_IO=9 CONFIG_D2WD_PSRAM_CS_IO=10 +# end of PSRAM clock and cs IO for ESP32-D2WD + +# +# PSRAM clock and cs IO for ESP32-PICO +# CONFIG_PICO_PSRAM_CS_IO=10 +# end of PSRAM clock and cs IO for ESP32-PICO +# CONFIG_SPIRAM_2T_MODE is not set +# end of SPI RAM config - +# CONFIG_ESP32_TRAX is not set CONFIG_ESP32_TRACEMEM_RESERVE_DRAM=0x0 - +# CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_TWO is not set CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_FOUR=y CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES=4 - +# CONFIG_ESP32_ULP_COPROC_ENABLED is not set CONFIG_ESP32_ULP_COPROC_RESERVE_MEM=0 - -CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y - - CONFIG_ESP32_DEBUG_OCDAWARE=y - CONFIG_ESP32_BROWNOUT_DET=y CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_0=y - - - - - - - +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_7 is not set CONFIG_ESP32_BROWNOUT_DET_LVL=0 CONFIG_ESP32_REDUCE_PHY_TX_POWER=y CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y - - - +# CONFIG_ESP32_TIME_SYSCALL_USE_RTC is not set +# CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 is not set +# CONFIG_ESP32_TIME_SYSCALL_USE_NONE is not set CONFIG_ESP32_RTC_CLK_SRC_INT_RC=y - - - +# CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS is not set +# CONFIG_ESP32_RTC_CLK_SRC_EXT_OSC is not set +# CONFIG_ESP32_RTC_CLK_SRC_INT_8MD256 is not set CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024 CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=2000 CONFIG_ESP32_XTAL_FREQ_40=y - - +# CONFIG_ESP32_XTAL_FREQ_26 is not set +# CONFIG_ESP32_XTAL_FREQ_AUTO is not set CONFIG_ESP32_XTAL_FREQ=40 +# CONFIG_ESP32_DISABLE_BASIC_ROM_CONSOLE is not set +# CONFIG_ESP32_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set +# CONFIG_ESP32_COMPATIBLE_PRE_V3_1_BOOTLOADERS is not set +# CONFIG_ESP32_USE_FIXED_STATIC_RAM_SIZE is not set +CONFIG_ESP32_DPORT_DIS_INTERRUPT_LVL=5 +# end of ESP32-specific - - - +# +# ADC-Calibration +# CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y CONFIG_ADC_CAL_LUT_ENABLE=y +# end of ADC-Calibration +# +# Common ESP-related +# CONFIG_ESP_ERR_TO_NAME_LOOKUP=y CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32 CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304 CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192 CONFIG_ESP_IPC_TASK_STACK_SIZE=1024 -CONFIG_ESP_TIMER_TASK_STACK_SIZE=3584 +CONFIG_ESP_IPC_USES_CALLERS_PRIORITY=y +CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE=2048 CONFIG_ESP_CONSOLE_UART_DEFAULT=y - - +# CONFIG_ESP_CONSOLE_UART_CUSTOM is not set +# CONFIG_ESP_CONSOLE_NONE is not set +CONFIG_ESP_CONSOLE_UART=y +CONFIG_ESP_CONSOLE_MULTIPLE_UART=y CONFIG_ESP_CONSOLE_UART_NUM=0 CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 CONFIG_ESP_INT_WDT=y CONFIG_ESP_INT_WDT_TIMEOUT_MS=800 CONFIG_ESP_INT_WDT_CHECK_CPU1=y CONFIG_ESP_TASK_WDT=y - +# CONFIG_ESP_TASK_WDT_PANIC is not set CONFIG_ESP_TASK_WDT_TIMEOUT_S=5 CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=y +# CONFIG_ESP_PANIC_HANDLER_IRAM is not set +CONFIG_ESP_MAC_ADDR_UNIVERSE_WIFI_STA=y +CONFIG_ESP_MAC_ADDR_UNIVERSE_WIFI_AP=y +CONFIG_ESP_MAC_ADDR_UNIVERSE_BT=y +CONFIG_ESP_MAC_ADDR_UNIVERSE_ETH=y +CONFIG_ESP_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y +# end of Common ESP-related + +# +# Ethernet +# +CONFIG_ETH_ENABLED=y CONFIG_ETH_USE_ESP32_EMAC=y CONFIG_ETH_PHY_INTERFACE_RMII=y - +# CONFIG_ETH_PHY_INTERFACE_MII is not set CONFIG_ETH_RMII_CLK_INPUT=y - +# CONFIG_ETH_RMII_CLK_OUTPUT is not set CONFIG_ETH_RMII_CLK_IN_GPIO=0 -CONFIG_ETH_SMI_MDC_GPIO=23 -CONFIG_ETH_SMI_MDIO_GPIO=18 -CONFIG_ETH_PHY_USE_RST=y -CONFIG_ETH_PHY_RST_GPIO=5 CONFIG_ETH_DMA_BUFFER_SIZE=512 CONFIG_ETH_DMA_RX_BUFFER_NUM=10 CONFIG_ETH_DMA_TX_BUFFER_NUM=10 +# CONFIG_ETH_USE_SPI_ETHERNET is not set +# CONFIG_ETH_USE_OPENETH is not set +# end of Ethernet +# +# Event Loop Library +# +# CONFIG_ESP_EVENT_LOOP_PROFILING is not set CONFIG_ESP_EVENT_POST_FROM_ISR=y CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y -CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y +# end of Event Loop Library +# +# GDB Stub +# +# end of GDB Stub + +# +# ESP HTTP client +# +# CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS is not set +# CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH is not set +# end of ESP HTTP client + +# +# HTTP Server +# CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024 CONFIG_HTTPD_MAX_URI_LEN=512 CONFIG_HTTPD_ERR_RESP_NO_DELAY=y CONFIG_HTTPD_PURGE_BUF_LEN=32 +# CONFIG_HTTPD_LOG_PURGE_DATA is not set +# CONFIG_HTTPD_WS_SUPPORT is not set +# end of HTTP Server +# +# ESP HTTPS OTA +# +# CONFIG_OTA_ALLOW_HTTP is not set +# end of ESP HTTPS OTA +# +# ESP HTTPS server +# +# CONFIG_ESP_HTTPS_SERVER_ENABLE is not set +# end of ESP HTTPS server +# +# ESP NETIF Adapter +# +CONFIG_ESP_NETIF_IP_LOST_TIMER_INTERVAL=120 +CONFIG_ESP_NETIF_TCPIP_LWIP=y +# CONFIG_ESP_NETIF_LOOPBACK is not set +CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER=y +# end of ESP NETIF Adapter + +# +# Power Management +# +# CONFIG_PM_ENABLE is not set +# end of Power Management + +# +# ESP System Settings +# +# CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT is not set +CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y +# CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set +# CONFIG_ESP_SYSTEM_PANIC_GDBSTUB is not set +# CONFIG_ESP_SYSTEM_PSRAM_LEAKAGE_WORKAROUND is not set +# CONFIG_ESP_SYSTEM_FLASH_LEAKAGE_WORKAROUND is not set + +# +# Memory protection +# +# end of Memory protection +# end of ESP System Settings + +# +# High resolution timer (esp_timer) +# +# CONFIG_ESP_TIMER_PROFILING is not set +CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER=y +CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER=y +CONFIG_ESP_TIMER_TASK_STACK_SIZE=3584 +# CONFIG_ESP_TIMER_IMPL_FRC2 is not set +CONFIG_ESP_TIMER_IMPL_TG0_LAC=y +# end of High resolution timer (esp_timer) + +# +# Wi-Fi +# CONFIG_ESP32_WIFI_SW_COEXIST_ENABLE=y - - -CONFIG_ESP32_WIFI_SW_COEXIST_PREFERENCE_BALANCE=y -CONFIG_ESP32_WIFI_SW_COEXIST_PREFERENCE_VALUE=2 CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=12 CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=40 CONFIG_ESP32_WIFI_STATIC_TX_BUFFER=y CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=0 CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM=12 -CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=n -CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=n - - - +CONFIG_ESP32_WIFI_CACHE_TX_BUFFER_NUM=32 +# CONFIG_ESP32_WIFI_CSI_ENABLED is not set +# CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED is not set +# CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED is not set +# CONFIG_ESP32_WIFI_AMSDU_TX_ENABLED is not set CONFIG_ESP32_WIFI_NVS_ENABLED=y CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y - +# CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1 is not set CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN=752 CONFIG_ESP32_WIFI_MGMT_SBUF_NUM=32 +# CONFIG_WIFI_LOG_DEFAULT_LEVEL_NONE is not set +# CONFIG_WIFI_LOG_DEFAULT_LEVEL_ERROR is not set +# CONFIG_WIFI_LOG_DEFAULT_LEVEL_WARN is not set +CONFIG_WIFI_LOG_DEFAULT_LEVEL_INFO=y +# CONFIG_WIFI_LOG_DEFAULT_LEVEL_DEBUG is not set +# CONFIG_WIFI_LOG_DEFAULT_LEVEL_VERBOSE is not set +# CONFIG_ESP32_WIFI_IRAM_OPT is not set +# CONFIG_ESP32_WIFI_RX_IRAM_OPT is not set +CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE=y +# CONFIG_ESP_WIFI_SLP_IRAM_OPT is not set +# CONFIG_ESP_WIFI_STA_DISCONNECTED_PM_ENABLE is not set +# end of Wi-Fi -#CONFIG_ESP32_WIFI_IRAM_OPT=y +# +# PHY +# CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y - +# CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION is not set CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 CONFIG_ESP32_PHY_MAX_TX_POWER=20 +# end of PHY - +# +# Core dump +# +# CONFIG_ESP_COREDUMP_ENABLE_TO_UART is not set CONFIG_ESP_COREDUMP_ENABLE_TO_NONE=y +# end of Core dump - +# +# FAT Filesystem support +# +# CONFIG_FATFS_CODEPAGE_DYNAMIC is not set CONFIG_FATFS_CODEPAGE_437=y - - - - - - - - - - - - - - - - - - - - +# CONFIG_FATFS_CODEPAGE_720 is not set +# CONFIG_FATFS_CODEPAGE_737 is not set +# CONFIG_FATFS_CODEPAGE_771 is not set +# CONFIG_FATFS_CODEPAGE_775 is not set +# CONFIG_FATFS_CODEPAGE_850 is not set +# CONFIG_FATFS_CODEPAGE_852 is not set +# CONFIG_FATFS_CODEPAGE_855 is not set +# CONFIG_FATFS_CODEPAGE_857 is not set +# CONFIG_FATFS_CODEPAGE_860 is not set +# CONFIG_FATFS_CODEPAGE_861 is not set +# CONFIG_FATFS_CODEPAGE_862 is not set +# CONFIG_FATFS_CODEPAGE_863 is not set +# CONFIG_FATFS_CODEPAGE_864 is not set +# CONFIG_FATFS_CODEPAGE_865 is not set +# CONFIG_FATFS_CODEPAGE_866 is not set +# CONFIG_FATFS_CODEPAGE_869 is not set +# CONFIG_FATFS_CODEPAGE_932 is not set +# CONFIG_FATFS_CODEPAGE_936 is not set +# CONFIG_FATFS_CODEPAGE_949 is not set +# CONFIG_FATFS_CODEPAGE_950 is not set CONFIG_FATFS_CODEPAGE=437 CONFIG_FATFS_LFN_NONE=y - - +# CONFIG_FATFS_LFN_HEAP is not set +# CONFIG_FATFS_LFN_STACK is not set CONFIG_FATFS_FS_LOCK=0 CONFIG_FATFS_TIMEOUT_MS=10000 CONFIG_FATFS_PER_FILE_CACHE=y CONFIG_FATFS_ALLOC_PREFER_EXTRAM=y +# CONFIG_FATFS_USE_FASTSEEK is not set +# end of FAT Filesystem support + +# +# Modbus configuration +# +# CONFIG_FMB_COMM_MODE_TCP_EN is not set +# CONFIG_FMB_COMM_MODE_RTU_EN is not set +CONFIG_FMB_COMM_MODE_ASCII_EN=y CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=150 CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200 CONFIG_FMB_QUEUE_LENGTH=20 -CONFIG_FMB_SERIAL_TASK_STACK_SIZE=2048 +CONFIG_FMB_PORT_TASK_STACK_SIZE=4096 CONFIG_FMB_SERIAL_BUF_SIZE=256 -CONFIG_FMB_SERIAL_TASK_PRIO=10 - +CONFIG_FMB_SERIAL_ASCII_BITS_PER_SYMB=8 +CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS=1000 +CONFIG_FMB_PORT_TASK_PRIO=10 +# CONFIG_FMB_PORT_TASK_AFFINITY_NO_AFFINITY is not set +CONFIG_FMB_PORT_TASK_AFFINITY_CPU0=y +# CONFIG_FMB_PORT_TASK_AFFINITY_CPU1 is not set +CONFIG_FMB_PORT_TASK_AFFINITY=0x0 +# CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT is not set CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT=20 CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 CONFIG_FMB_CONTROLLER_STACK_SIZE=4096 CONFIG_FMB_EVENT_QUEUE_TIMEOUT=20 -CONFIG_FMB_TIMER_PORT_ENABLED=y +# CONFIG_FMB_TIMER_PORT_ENABLED is not set CONFIG_FMB_TIMER_GROUP=0 CONFIG_FMB_TIMER_INDEX=0 +CONFIG_FMB_MASTER_TIMER_GROUP=0 +CONFIG_FMB_MASTER_TIMER_INDEX=0 +# CONFIG_FMB_TIMER_ISR_IN_IRAM is not set +# end of Modbus configuration +# +# FreeRTOS +# +# CONFIG_FREERTOS_UNICORE is not set CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF CONFIG_FREERTOS_CORETIMER_0=y - +# CONFIG_FREERTOS_CORETIMER_1 is not set CONFIG_FREERTOS_HZ=100 CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y - - +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y - +# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y - - +# CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE is not set +# CONFIG_FREERTOS_ASSERT_DISABLE is not set CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 CONFIG_FREERTOS_ISR_STACKSIZE=1536 - +# CONFIG_FREERTOS_LEGACY_HOOKS is not set CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y - +# CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP is not set CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1 -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2432 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2800 CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 - - - +CONFIG_FREERTOS_USE_TRACE_FACILITY=y +CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y +# CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID is not set +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y +CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER=y +# CONFIG_FREERTOS_RUN_TIME_STATS_USING_CPU_CLK is not set CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y +# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set +CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y +CONFIG_FREERTOS_DEBUG_OCDAWARE=y +# CONFIG_FREERTOS_FPU_IN_ISR is not set +# end of FreeRTOS +# +# Heap memory debugging +# CONFIG_HEAP_POISONING_DISABLED=y - - +# CONFIG_HEAP_POISONING_LIGHT is not set +# CONFIG_HEAP_POISONING_COMPREHENSIVE is not set CONFIG_HEAP_TRACING_OFF=y +# CONFIG_HEAP_TRACING_STANDALONE is not set +# CONFIG_HEAP_TRACING_TOHOST is not set +# CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS is not set +# end of Heap memory debugging +# +# jsmn +# +# CONFIG_JSMN_PARENT_LINKS is not set +# CONFIG_JSMN_STRICT is not set +# end of jsmn +# +# libsodium +# +# end of libsodium -CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y - - - +# +# Log output +# +# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set +# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set +# CONFIG_LOG_DEFAULT_LEVEL_WARN is not set CONFIG_LOG_DEFAULT_LEVEL_INFO=y - - +# CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set +# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set CONFIG_LOG_DEFAULT_LEVEL=3 CONFIG_LOG_COLORS=y -CONFIG_LWIP_LOCAL_HOSTNAME="espressif" - +CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y +# CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set +# end of Log output +# +# LWIP +# +CONFIG_LWIP_LOCAL_HOSTNAME="SqueezeESP32" +CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y +# CONFIG_LWIP_L2_TO_L3_COPY is not set +# CONFIG_LWIP_IRAM_OPTIMIZATION is not set CONFIG_LWIP_TIMERS_ONDEMAND=y -CONFIG_LWIP_MAX_SOCKETS=16 - +CONFIG_LWIP_MAX_SOCKETS=20 +# CONFIG_LWIP_USE_ONLY_LWIP_SELECT is not set +# CONFIG_LWIP_SO_LINGER is not set CONFIG_LWIP_SO_REUSE=y CONFIG_LWIP_SO_REUSE_RXTOALL=y -#CONFIG_LWIP_IP_REASSEMBLY is not set - - - - +# CONFIG_LWIP_SO_RCVBUF is not set +# CONFIG_LWIP_NETBUF_RECVINFO is not set +# CONFIG_LWIP_IP4_FRAG is not set +# CONFIG_LWIP_IP6_FRAG is not set +CONFIG_LWIP_IP4_REASSEMBLY=y +CONFIG_LWIP_IP6_REASSEMBLY=y +# CONFIG_LWIP_IP_FORWARD is not set +# CONFIG_LWIP_STATS is not set +# CONFIG_LWIP_ETHARP_TRUST_IP_MAC is not set CONFIG_LWIP_ESP_GRATUITOUS_ARP=y CONFIG_LWIP_GARP_TMR_INTERVAL=60 CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32 CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y +# CONFIG_LWIP_DHCP_DISABLE_CLIENT_ID is not set +CONFIG_LWIP_DHCP_RESTORE_LAST_IP=y +# +# DHCP server +# +CONFIG_LWIP_DHCPS=y CONFIG_LWIP_DHCPS_LEASE_UNIT=60 CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8 +# end of DHCP server +# CONFIG_LWIP_AUTOIP is not set +CONFIG_LWIP_IPV6=y +# CONFIG_LWIP_IPV6_AUTOCONFIG is not set CONFIG_LWIP_NETIF_LOOPBACK=y CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8 + +# +# TCP +# CONFIG_LWIP_MAX_ACTIVE_TCP=16 CONFIG_LWIP_MAX_LISTENING_TCP=16 +CONFIG_LWIP_TCP_HIGH_SPEED_RETRANSMISSION=y CONFIG_LWIP_TCP_MAXRTX=12 CONFIG_LWIP_TCP_SYNMAXRTX=6 CONFIG_LWIP_TCP_MSS=1440 +CONFIG_LWIP_TCP_TMR_INTERVAL=250 CONFIG_LWIP_TCP_MSL=60000 CONFIG_LWIP_TCP_SND_BUF_DEFAULT=8192 CONFIG_LWIP_TCP_WND_DEFAULT=32768 CONFIG_LWIP_TCP_RECVMBOX_SIZE=32 CONFIG_LWIP_TCP_QUEUE_OOSEQ=y - +# CONFIG_LWIP_TCP_SACK_OUT is not set +# CONFIG_LWIP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES is not set CONFIG_LWIP_TCP_OVERSIZE_MSS=y +# CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS is not set +# CONFIG_LWIP_TCP_OVERSIZE_DISABLE is not set +# CONFIG_LWIP_WND_SCALE is not set +CONFIG_LWIP_TCP_RTO_TIME=3000 +# end of TCP - +# +# UDP +# CONFIG_LWIP_MAX_UDP_PCBS=16 CONFIG_LWIP_UDP_RECVMBOX_SIZE=32 +# end of UDP + +# +# Checksums +# +# CONFIG_LWIP_CHECKSUM_CHECK_IP is not set +# CONFIG_LWIP_CHECKSUM_CHECK_UDP is not set +CONFIG_LWIP_CHECKSUM_CHECK_ICMP=y +# end of Checksums + CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=3072 CONFIG_LWIP_TCPIP_TASK_AFFINITY_NO_AFFINITY=y - - +# CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU0 is not set +# CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU1 is not set CONFIG_LWIP_TCPIP_TASK_AFFINITY=0x7FFFFFFF +# CONFIG_LWIP_PPP_SUPPORT is not set +CONFIG_LWIP_IPV6_MEMP_NUM_ND6_QUEUE=3 +CONFIG_LWIP_IPV6_ND6_NUM_NEIGHBORS=5 +# CONFIG_LWIP_SLIP_SUPPORT is not set +# +# ICMP +# +CONFIG_LWIP_ICMP=y +# CONFIG_LWIP_MULTICAST_PING is not set +# CONFIG_LWIP_BROADCAST_PING is not set +# end of ICMP - +# +# LWIP RAW API +# CONFIG_LWIP_MAX_RAW_PCBS=16 +# end of LWIP RAW API + +# +# SNTP +# CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1 CONFIG_LWIP_SNTP_UPDATE_DELAY=3600000 +# end of SNTP +# CONFIG_LWIP_ESP_LWIP_ASSERT is not set + +# +# Hooks +# +# CONFIG_LWIP_HOOK_TCP_ISN_NONE is not set +CONFIG_LWIP_HOOK_TCP_ISN_DEFAULT=y +# CONFIG_LWIP_HOOK_TCP_ISN_CUSTOM is not set +CONFIG_LWIP_HOOK_IP6_ROUTE_NONE=y +# CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT is not set +# CONFIG_LWIP_HOOK_IP6_ROUTE_CUSTOM is not set +CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_NONE=y +# CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_DEFAULT is not set +# CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM is not set +# end of Hooks + +# CONFIG_LWIP_DEBUG is not set +# end of LWIP + +# +# mbedTLS +# +# CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC is not set CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y +# CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC is not set +# CONFIG_MBEDTLS_CUSTOM_MEM_ALLOC is not set +CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y +CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 +CONFIG_MBEDTLS_DYNAMIC_BUFFER=y +# CONFIG_MBEDTLS_DYNAMIC_FREE_PEER_CERT is not set +# CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA is not set +# CONFIG_MBEDTLS_DEBUG is not set +# +# Certificate Bundle +# +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=y +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN is not set +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE is not set +# CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE is not set +# end of Certificate Bundle -CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384 - - +# CONFIG_MBEDTLS_ECP_RESTARTABLE is not set +# CONFIG_MBEDTLS_CMAC_C is not set CONFIG_MBEDTLS_HARDWARE_AES=y - - +CONFIG_MBEDTLS_HARDWARE_MPI=y +CONFIG_MBEDTLS_HARDWARE_SHA=y +CONFIG_MBEDTLS_ROM_MD5=y +# CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN is not set +# CONFIG_MBEDTLS_ATCA_HW_ECDSA_VERIFY is not set CONFIG_MBEDTLS_HAVE_TIME=y - -CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y - - - -CONFIG_MBEDTLS_TLS_SERVER=y +# CONFIG_MBEDTLS_HAVE_TIME_DATE is not set +CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y +CONFIG_MBEDTLS_SHA512_C=y +# CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT is not set +# CONFIG_MBEDTLS_TLS_SERVER_ONLY is not set +CONFIG_MBEDTLS_TLS_CLIENT_ONLY=y +# CONFIG_MBEDTLS_TLS_DISABLED is not set CONFIG_MBEDTLS_TLS_CLIENT=y CONFIG_MBEDTLS_TLS_ENABLED=y +# +# TLS Key Exchange Methods +# +# CONFIG_MBEDTLS_PSK_MODES is not set CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=y CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y @@ -678,33 +1166,51 @@ CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y +# end of TLS Key Exchange Methods + CONFIG_MBEDTLS_SSL_RENEGOTIATION=y - -CONFIG_MBEDTLS_SSL_PROTO_TLS1=y -CONFIG_MBEDTLS_SSL_PROTO_TLS1_1=y +# CONFIG_MBEDTLS_SSL_PROTO_SSL3 is not set +# CONFIG_MBEDTLS_SSL_PROTO_TLS1 is not set +# CONFIG_MBEDTLS_SSL_PROTO_TLS1_1 is not set CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y - +# CONFIG_MBEDTLS_SSL_PROTO_DTLS is not set CONFIG_MBEDTLS_SSL_ALPN=y CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS=y +CONFIG_MBEDTLS_X509_CHECK_KEY_USAGE=y +CONFIG_MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE=y CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS=y + +# +# Symmetric Ciphers +# CONFIG_MBEDTLS_AES_C=y - - +# CONFIG_MBEDTLS_CAMELLIA_C is not set +# CONFIG_MBEDTLS_DES_C is not set CONFIG_MBEDTLS_RC4_DISABLED=y - - - - +# CONFIG_MBEDTLS_RC4_ENABLED_NO_DEFAULT is not set +# CONFIG_MBEDTLS_RC4_ENABLED is not set +# CONFIG_MBEDTLS_BLOWFISH_C is not set +# CONFIG_MBEDTLS_XTEA_C is not set CONFIG_MBEDTLS_CCM_C=y CONFIG_MBEDTLS_GCM_C=y +# CONFIG_MBEDTLS_NIST_KW_C is not set +# end of Symmetric Ciphers +# CONFIG_MBEDTLS_RIPEMD160_C is not set + +# +# Certificates +# CONFIG_MBEDTLS_PEM_PARSE_C=y CONFIG_MBEDTLS_PEM_WRITE_C=y CONFIG_MBEDTLS_X509_CRL_PARSE_C=y CONFIG_MBEDTLS_X509_CSR_PARSE_C=y +# end of Certificates + CONFIG_MBEDTLS_ECP_C=y CONFIG_MBEDTLS_ECDH_C=y CONFIG_MBEDTLS_ECDSA_C=y +# CONFIG_MBEDTLS_ECJPAKE_C is not set CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y @@ -718,120 +1224,284 @@ CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y CONFIG_MBEDTLS_ECP_NIST_OPTIM=y +# CONFIG_MBEDTLS_POLY1305_C is not set +# CONFIG_MBEDTLS_CHACHA20_C is not set +# CONFIG_MBEDTLS_HKDF_C is not set +# CONFIG_MBEDTLS_THREADING_C is not set +# CONFIG_MBEDTLS_LARGE_KEY_SOFTWARE_MPI is not set +# CONFIG_MBEDTLS_SECURITY_RISKS is not set +# end of mbedTLS + +# +# mDNS +# CONFIG_MDNS_MAX_SERVICES=10 -CONFIG_MQTT_PROTOCOL_311=y -CONFIG_MQTT_TRANSPORT_SSL=y -CONFIG_MQTT_TRANSPORT_WEBSOCKET=y -CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y - +CONFIG_MDNS_TASK_PRIORITY=1 +CONFIG_MDNS_TASK_STACK_SIZE=4096 +# CONFIG_MDNS_TASK_AFFINITY_NO_AFFINITY is not set +CONFIG_MDNS_TASK_AFFINITY_CPU0=y +# CONFIG_MDNS_TASK_AFFINITY_CPU1 is not set +CONFIG_MDNS_TASK_AFFINITY=0x0 +CONFIG_MDNS_SERVICE_ADD_TIMEOUT_MS=2000 +# CONFIG_MDNS_STRICT_MODE is not set +CONFIG_MDNS_TIMER_PERIOD_MS=100 +# end of mDNS +# +# ESP-MQTT Configurations +# +# CONFIG_MQTT_PROTOCOL_311 is not set +# CONFIG_MQTT_TRANSPORT_SSL is not set +# CONFIG_MQTT_MSG_ID_INCREMENTAL is not set +# CONFIG_MQTT_SKIP_PUBLISH_IF_DISCONNECTED is not set +# CONFIG_MQTT_REPORT_DELETED_MESSAGES is not set +# CONFIG_MQTT_USE_CUSTOM_CONFIG is not set +# CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED is not set +# CONFIG_MQTT_CUSTOM_OUTBOX is not set +# end of ESP-MQTT Configurations +# +# Newlib +# CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y - - - - +# CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF is not set +# CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR is not set +# CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF is not set +# CONFIG_NEWLIB_STDIN_LINE_ENDING_LF is not set CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y +# CONFIG_NEWLIB_NANO_FORMAT is not set +# end of Newlib +# +# NVS +# +# end of NVS +# +# OpenSSL +# +# CONFIG_OPENSSL_DEBUG is not set +# CONFIG_OPENSSL_ERROR_STACK is not set CONFIG_OPENSSL_ASSERT_DO_NOTHING=y +# CONFIG_OPENSSL_ASSERT_EXIT is not set +# end of OpenSSL +# +# PThreads +# CONFIG_PTHREAD_TASK_PRIO_DEFAULT=5 CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 CONFIG_PTHREAD_STACK_MIN=768 - - +# CONFIG_PTHREAD_DEFAULT_CORE_NO_AFFINITY is not set +# CONFIG_PTHREAD_DEFAULT_CORE_0 is not set CONFIG_PTHREAD_DEFAULT_CORE_1=y CONFIG_PTHREAD_TASK_CORE_DEFAULT=1 CONFIG_PTHREAD_TASK_NAME_DEFAULT="pthread" +# end of PThreads - +# +# SPI Flash driver +# +# CONFIG_SPI_FLASH_VERIFY_WRITE is not set +# CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y +# CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS is not set +# CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED is not set +# CONFIG_SPI_FLASH_USE_LEGACY_IMPL is not set +# CONFIG_SPI_FLASH_SHARE_SPI1_BUS is not set +# CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE is not set +CONFIG_SPI_FLASH_YIELD_DURING_ERASE=y +CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS=20 +CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=1 +CONFIG_SPI_FLASH_WRITE_CHUNK_SIZE=8192 +# CONFIG_SPI_FLASH_SIZE_OVERRIDE is not set +# CONFIG_SPI_FLASH_CHECK_ERASE_TIMEOUT_DISABLED is not set - - +# +# Auto-detect flash chips +# CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_MXIC_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_WINBOND_CHIP=y +# end of Auto-detect flash chips + +CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE=y +# end of SPI Flash driver + +# +# SPIFFS Configuration +# CONFIG_SPIFFS_MAX_PARTITIONS=3 + +# +# SPIFFS Cache Configuration +# CONFIG_SPIFFS_CACHE=y CONFIG_SPIFFS_CACHE_WR=y +# CONFIG_SPIFFS_CACHE_STATS is not set +# end of SPIFFS Cache Configuration CONFIG_SPIFFS_PAGE_CHECK=y CONFIG_SPIFFS_GC_MAX_RUNS=10 - +# CONFIG_SPIFFS_GC_STATS is not set CONFIG_SPIFFS_PAGE_SIZE=256 CONFIG_SPIFFS_OBJ_NAME_LEN=32 +# CONFIG_SPIFFS_FOLLOW_SYMLINKS is not set CONFIG_SPIFFS_USE_MAGIC=y CONFIG_SPIFFS_USE_MAGIC_LENGTH=y CONFIG_SPIFFS_META_LENGTH=4 CONFIG_SPIFFS_USE_MTIME=y +# +# Debug Configuration +# +# CONFIG_SPIFFS_DBG is not set +# CONFIG_SPIFFS_API_DBG is not set +# CONFIG_SPIFFS_GC_DBG is not set +# CONFIG_SPIFFS_CACHE_DBG is not set +# CONFIG_SPIFFS_CHECK_DBG is not set +# CONFIG_SPIFFS_TEST_VISUALISATION is not set +# end of Debug Configuration +# end of SPIFFS Configuration +# +# TCP Transport +# +# +# Websocket +# +# CONFIG_WS_TRANSPORT is not set +# end of Websocket +# end of TCP Transport +# +# TinyUSB +# +# end of TinyUSB - -CONFIG_NETIF_IP_LOST_TIMER_INTERVAL=120 -CONFIG_TCPIP_LWIP=y +# +# Unity unit testing library +# CONFIG_UNITY_ENABLE_FLOAT=y CONFIG_UNITY_ENABLE_DOUBLE=y - +# CONFIG_UNITY_ENABLE_COLOR is not set CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y +# CONFIG_UNITY_ENABLE_FIXTURE is not set +# CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL is not set +# end of Unity unit testing library +# +# Virtual file system +# +CONFIG_VFS_SUPPORT_IO=y +CONFIG_VFS_SUPPORT_DIR=y +# CONFIG_VFS_SUPPORT_SELECT is not set +# CONFIG_VFS_SUPPORT_TERMIOS is not set -CONFIG_VFS_SUPPRESS_SELECT_DEBUG_OUTPUT=y -CONFIG_VFS_SUPPORT_TERMIOS=y +# +# Host File System I/O (Semihosting) +# CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS=1 CONFIG_VFS_SEMIHOSTFS_HOST_PATH_MAX_LEN=128 +# end of Host File System I/O (Semihosting) +# end of Virtual file system -CONFIG_WL_SECTOR_SIZE_512=y -#CONFIG_WL_SECTOR_SIZE_4096 is not defined -CONFIG_WL_SECTOR_SIZE=512 +# +# Wear Levelling +# +# CONFIG_WL_SECTOR_SIZE_512 is not set +CONFIG_WL_SECTOR_SIZE_4096=y +CONFIG_WL_SECTOR_SIZE=4096 +# end of Wear Levelling + +# +# Wi-Fi Provisioning Manager +# CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16 +CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30 +# CONFIG_WIFI_PROV_BLE_BONDING is not set +# end of Wi-Fi Provisioning Manager +# +# Supplicant +# +CONFIG_WPA_MBEDTLS_CRYPTO=y +# CONFIG_WPA_WAPI_PSK is not set +# CONFIG_WPA_DEBUG_PRINT is not set +# CONFIG_WPA_TESTING_OPTIONS is not set +# CONFIG_WPA_WPS_STRICT is not set +# CONFIG_WPA_11KV_SUPPORT is not set +# end of Supplicant +# +# DSP Library +# +# CONFIG_DSP_ANSI is not set +CONFIG_DSP_OPTIMIZED=y +CONFIG_DSP_OPTIMIZATION=1 +CONFIG_DSP_MAX_FFT_SIZE_512=y +# CONFIG_DSP_MAX_FFT_SIZE_1024 is not set +# CONFIG_DSP_MAX_FFT_SIZE_2048 is not set +# CONFIG_DSP_MAX_FFT_SIZE_4096 is not set +# CONFIG_DSP_MAX_FFT_SIZE_8192 is not set +# CONFIG_DSP_MAX_FFT_SIZE_16384 is not set +# CONFIG_DSP_MAX_FFT_SIZE_32768 is not set +CONFIG_DSP_MAX_FFT_SIZE=512 +# end of DSP Library +# end of Component config + +# +# Compatibility options +# +# CONFIG_LEGACY_INCLUDE_COMMON_HEADERS is not set +# end of Compatibility options # Deprecated options for backward compatibility CONFIG_TOOLPREFIX="xtensa-esp32-elf-" -CONFIG_MAKE_WARN_UNDEFINED_VARIABLES=y - - - +# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y - - +# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set CONFIG_LOG_BOOTLOADER_LEVEL=3 - - +# CONFIG_APP_ROLLBACK_ENABLE is not set +# CONFIG_FLASH_ENCRYPTION_ENABLED is not set CONFIG_FLASHMODE_QIO=y - - - - - +# CONFIG_FLASHMODE_QOUT is not set +# CONFIG_FLASHMODE_DIO is not set +# CONFIG_FLASHMODE_DOUT is not set +# CONFIG_MONITOR_BAUD_9600B is not set +# CONFIG_MONITOR_BAUD_57600B is not set CONFIG_MONITOR_BAUD_115200B=y - - - - +# CONFIG_MONITOR_BAUD_230400B is not set +# CONFIG_MONITOR_BAUD_921600B is not set +# CONFIG_MONITOR_BAUD_2MB is not set +# CONFIG_MONITOR_BAUD_OTHER is not set CONFIG_MONITOR_BAUD_OTHER_VAL=115200 CONFIG_MONITOR_BAUD=115200 - -CONFIG_OPTIMIZATION_LEVEL_RELEASE=y -CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y - - - +# CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG is not set +CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y +# CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED is not set +CONFIG_OPTIMIZATION_ASSERTIONS_SILENT=y +# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set +CONFIG_CXX_EXCEPTIONS=y +CONFIG_CXX_EXCEPTIONS_EMG_POOL_SIZE=0 CONFIG_STACK_CHECK_NONE=y - - - - - - - +# CONFIG_STACK_CHECK_NORM is not set +# CONFIG_STACK_CHECK_STRONG is not set +# CONFIG_STACK_CHECK_ALL is not set +# CONFIG_WARN_WRITE_STRINGS is not set +# CONFIG_DISABLE_GCC8_WARNINGS is not set +# CONFIG_ESP32_APPTRACE_DEST_TRAX is not set +CONFIG_ESP32_APPTRACE_DEST_NONE=y +CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y +# CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY is not set CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY=y - +# CONFIG_BTDM_CONTROLLER_MODE_BTDM is not set CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN=2 CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN=0 CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN_EFF=0 @@ -839,267 +1509,97 @@ CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN_EFF=2 CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN_EFF=0 CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0 CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI=y - +# CONFIG_BTDM_CONTROLLER_HCI_MODE_UART_H4 is not set CONFIG_BTDM_CONTROLLER_MODEM_SLEEP=y CONFIG_BLUEDROID_ENABLED=y +# CONFIG_NIMBLE_ENABLED is not set CONFIG_BTC_TASK_STACK_SIZE=3072 CONFIG_BLUEDROID_PINNED_TO_CORE_0=y - +# CONFIG_BLUEDROID_PINNED_TO_CORE_1 is not set CONFIG_BLUEDROID_PINNED_TO_CORE=0 CONFIG_BTU_TASK_STACK_SIZE=4096 - +# CONFIG_BLUEDROID_MEM_DEBUG is not set CONFIG_CLASSIC_BT_ENABLED=y CONFIG_A2DP_ENABLE=y -CONFIG_A2DP_SINK_TASK_STACK_SIZE=2048 -CONFIG_A2DP_SOURCE_TASK_STACK_SIZE=2048 - -CONFIG_GATTS_ENABLE=y - -CONFIG_GATTS_SEND_SERVICE_CHANGE_AUTO=y -CONFIG_GATTS_SEND_SERVICE_CHANGE_MODE=0 -CONFIG_GATTC_ENABLE=y - -CONFIG_BLE_SMP_ENABLE=y - - - -CONFIG_HCI_TRACE_LEVEL_WARNING=y - - - - -CONFIG_HCI_INITIAL_TRACE_LEVEL=2 - - -CONFIG_BTM_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BTM_INITIAL_TRACE_LEVEL=2 - - -CONFIG_L2CAP_TRACE_LEVEL_WARNING=y - - - - -CONFIG_L2CAP_INITIAL_TRACE_LEVEL=2 - - -CONFIG_RFCOMM_TRACE_LEVEL_WARNING=y - - - - -CONFIG_RFCOMM_INITIAL_TRACE_LEVEL=2 - - -CONFIG_SDP_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BTH_LOG_SDP_INITIAL_TRACE_LEVEL=2 - - -CONFIG_GAP_TRACE_LEVEL_WARNING=y - - - - -CONFIG_GAP_INITIAL_TRACE_LEVEL=2 -CONFIG_BNEP_INITIAL_TRACE_LEVEL=2 - - -CONFIG_PAN_TRACE_LEVEL_WARNING=y - - - - -CONFIG_PAN_INITIAL_TRACE_LEVEL=2 - - -CONFIG_A2D_TRACE_LEVEL_WARNING=y - - - - -CONFIG_A2D_INITIAL_TRACE_LEVEL=2 - - -CONFIG_AVDT_TRACE_LEVEL_WARNING=y - - - - -CONFIG_AVDT_INITIAL_TRACE_LEVEL=2 - - -CONFIG_AVCT_TRACE_LEVEL_WARNING=y - - - - -CONFIG_AVCT_INITIAL_TRACE_LEVEL=2 - - -CONFIG_AVRC_TRACE_LEVEL_WARNING=y - - - - -CONFIG_AVRC_INITIAL_TRACE_LEVEL=2 - - -CONFIG_MCA_TRACE_LEVEL_WARNING=y - - - - -CONFIG_MCA_INITIAL_TRACE_LEVEL=2 - - -CONFIG_HID_TRACE_LEVEL_WARNING=y - - - - -CONFIG_HID_INITIAL_TRACE_LEVEL=2 - - -CONFIG_APPL_TRACE_LEVEL_WARNING=y - - - - -CONFIG_APPL_INITIAL_TRACE_LEVEL=2 - - -CONFIG_GATT_TRACE_LEVEL_WARNING=y - - - - -CONFIG_GATT_INITIAL_TRACE_LEVEL=2 - - -CONFIG_SMP_TRACE_LEVEL_WARNING=y - - - - -CONFIG_SMP_INITIAL_TRACE_LEVEL=2 - - -CONFIG_BTIF_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BTIF_INITIAL_TRACE_LEVEL=2 - - -CONFIG_BTC_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BTC_INITIAL_TRACE_LEVEL=2 - - -CONFIG_OSI_TRACE_LEVEL_WARNING=y - - - - -CONFIG_OSI_INITIAL_TRACE_LEVEL=2 - - -CONFIG_BLUFI_TRACE_LEVEL_WARNING=y - - - - -CONFIG_BLUFI_INITIAL_TRACE_LEVEL=2 - +# CONFIG_HFP_ENABLE is not set +# CONFIG_BLE_HOST_QUEUE_CONGESTION_CHECK is not set CONFIG_SMP_ENABLE=y CONFIG_BLE_ESTABLISH_LINK_CONNECTION_TIMEOUT=30 CONFIG_ADC2_DISABLE_DAC=y CONFIG_SPIRAM_SUPPORT=y - - - +CONFIG_WIFI_LWIP_ALLOCATION_FROM_SPIRAM_FIRST=y CONFIG_TRACEMEM_RESERVE_DRAM=0x0 - +# CONFIG_TWO_UNIVERSAL_MAC_ADDRESS is not set CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4 - +# CONFIG_ULP_COPROC_ENABLED is not set CONFIG_ULP_COPROC_RESERVE_MEM=0 CONFIG_BROWNOUT_DET=y CONFIG_BROWNOUT_DET_LVL_SEL_0=y - - - - - - - +# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_7 is not set CONFIG_BROWNOUT_DET_LVL=0 CONFIG_REDUCE_PHY_TX_POWER=y CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y - - - - - +# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL is not set +# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC is not set +# CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256 is not set +# CONFIG_DISABLE_BASIC_ROM_CONSOLE is not set +# CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 CONFIG_MAIN_TASK_STACK_SIZE=8192 CONFIG_IPC_TASK_STACK_SIZE=1024 -CONFIG_TIMER_TASK_STACK_SIZE=3584 CONFIG_CONSOLE_UART_DEFAULT=y - - +# CONFIG_CONSOLE_UART_CUSTOM is not set +# CONFIG_ESP_CONSOLE_UART_NONE is not set +CONFIG_CONSOLE_UART=y CONFIG_CONSOLE_UART_NUM=0 CONFIG_CONSOLE_UART_BAUDRATE=115200 CONFIG_INT_WDT=y CONFIG_INT_WDT_TIMEOUT_MS=800 CONFIG_INT_WDT_CHECK_CPU1=y CONFIG_TASK_WDT=y - +# CONFIG_TASK_WDT_PANIC is not set CONFIG_TASK_WDT_TIMEOUT_S=5 CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y - +# CONFIG_EVENT_LOOP_PROFILING is not set CONFIG_POST_EVENTS_FROM_ISR=y CONFIG_POST_EVENTS_FROM_IRAM_ISR=y +# CONFIG_ESP32S2_PANIC_PRINT_HALT is not set +CONFIG_ESP32S2_PANIC_PRINT_REBOOT=y +# CONFIG_ESP32S2_PANIC_SILENT_REBOOT is not set +# CONFIG_ESP32S2_PANIC_GDBSTUB is not set +CONFIG_TIMER_TASK_STACK_SIZE=3584 CONFIG_SW_COEXIST_ENABLE=y - - -CONFIG_SW_COEXIST_PREFERENCE_BALANCE=y -CONFIG_SW_COEXIST_PREFERENCE_VALUE=2 +# CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set +CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y CONFIG_MB_MASTER_TIMEOUT_MS_RESPOND=150 CONFIG_MB_MASTER_DELAY_MS_CONVERT=200 CONFIG_MB_QUEUE_LENGTH=20 -CONFIG_MB_SERIAL_TASK_STACK_SIZE=2048 +CONFIG_MB_SERIAL_TASK_STACK_SIZE=4096 CONFIG_MB_SERIAL_BUF_SIZE=256 CONFIG_MB_SERIAL_TASK_PRIO=10 - +# CONFIG_MB_CONTROLLER_SLAVE_ID_SUPPORT is not set CONFIG_MB_CONTROLLER_NOTIFY_TIMEOUT=20 CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 CONFIG_MB_CONTROLLER_STACK_SIZE=4096 CONFIG_MB_EVENT_QUEUE_TIMEOUT=20 -CONFIG_MB_TIMER_PORT_ENABLED=y +# CONFIG_MB_TIMER_PORT_ENABLED is not set CONFIG_MB_TIMER_GROUP=0 CONFIG_MB_TIMER_INDEX=0 -CONFIG_SUPPORT_STATIC_ALLOCATION=y - +# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set CONFIG_TIMER_TASK_PRIORITY=1 -CONFIG_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_TIMER_TASK_STACK_DEPTH=2800 CONFIG_TIMER_QUEUE_LENGTH=10 - - +# CONFIG_L2_TO_L3_COPY is not set +# CONFIG_USE_ONLY_LWIP_SELECT is not set CONFIG_ESP_GRATUITOUS_ARP=y CONFIG_GARP_TMR_INTERVAL=60 CONFIG_TCPIP_RECVMBOX_SIZE=32 @@ -1111,22 +1611,29 @@ CONFIG_TCP_SND_BUF_DEFAULT=8192 CONFIG_TCP_WND_DEFAULT=32768 CONFIG_TCP_RECVMBOX_SIZE=32 CONFIG_TCP_QUEUE_OOSEQ=y - +# CONFIG_ESP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES is not set CONFIG_TCP_OVERSIZE_MSS=y - - +# CONFIG_TCP_OVERSIZE_QUARTER_MSS is not set +# CONFIG_TCP_OVERSIZE_DISABLE is not set CONFIG_UDP_RECVMBOX_SIZE=32 CONFIG_TCPIP_TASK_STACK_SIZE=3072 CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y - - +# CONFIG_TCPIP_TASK_AFFINITY_CPU0 is not set +# CONFIG_TCPIP_TASK_AFFINITY_CPU1 is not set CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF - -CONFIG_PTHREAD_STACK_MIN=768 +# CONFIG_PPP_SUPPORT is not set +CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 +CONFIG_ESP32_PTHREAD_STACK_MIN=768 +# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY is not set +# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_0 is not set +CONFIG_ESP32_DEFAULT_PTHREAD_CORE_1=y +CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=1 +CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread" CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y - - -CONFIG_IP_LOST_TIMER_INTERVAL=120 -CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y -CONFIG_SUPPORT_TERMIOS=y +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set +# CONFIG_SUPPORT_TERMIOS is not set +CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS=1 +CONFIG_SEMIHOSTFS_HOST_PATH_MAX_LEN=128 # End of deprecated options