From afd0da16a5835488c745bca2ff65b57e7492b786 Mon Sep 17 00:00:00 2001 From: Wizmo2 Date: Mon, 21 Nov 2022 19:01:19 -0500 Subject: [PATCH 01/19] merge changes from led_visu to v4.3 --- components/led_strip/CMakeLists.txt | 11 + components/led_strip/LICENSE | 202 +++++++++ components/led_strip/led_strip.c | 407 ++++++++++++++++++ components/led_strip/led_strip.h | 96 +++++ components/led_strip/led_vu.c | 363 ++++++++++++++++ components/led_strip/led_vu.h | 31 ++ components/platform_console/cmd_config.c | 95 ++++ components/services/accessors.c | 54 +++ components/services/accessors.h | 10 +- components/squeezelite-ota/CMakeLists.txt | 2 +- components/squeezelite-ota/squeezelite-ota.c | 36 +- components/squeezelite/CMakeLists.txt | 1 + components/squeezelite/displayer.c | 116 +++-- main/CMakeLists.txt | 2 +- main/esp_app_main.c | 19 +- plugin/SqueezeESP32.zip | Bin 18239 -> 19514 bytes .../plugins/SqueezeESP32/settings/player.html | 28 ++ plugin/SqueezeESP32/Player.pm | 12 + plugin/SqueezeESP32/PlayerSettings.pm | 12 + plugin/SqueezeESP32/RgbLed.pm | 188 ++++++++ plugin/SqueezeESP32/strings.txt | 38 ++ 21 files changed, 1676 insertions(+), 47 deletions(-) create mode 100644 components/led_strip/CMakeLists.txt create mode 100644 components/led_strip/LICENSE create mode 100644 components/led_strip/led_strip.c create mode 100644 components/led_strip/led_strip.h create mode 100644 components/led_strip/led_vu.c create mode 100644 components/led_strip/led_vu.h create mode 100644 plugin/SqueezeESP32/RgbLed.pm diff --git a/components/led_strip/CMakeLists.txt b/components/led_strip/CMakeLists.txt new file mode 100644 index 00000000..c7e9f3f0 --- /dev/null +++ b/components/led_strip/CMakeLists.txt @@ -0,0 +1,11 @@ + +idf_component_register(SRC_DIRS . + INCLUDE_DIRS . + REQUIRES platform_config tools esp_common + PRIV_REQUIRES services freertos driver +) + +set_source_files_properties(led_strip.c + PROPERTIES COMPILE_FLAGS + -Wno-format-overflow +) diff --git a/components/led_strip/LICENSE b/components/led_strip/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/components/led_strip/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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/led_strip/led_strip.c b/components/led_strip/led_strip.c new file mode 100644 index 00000000..92b1ba3d --- /dev/null +++ b/components/led_strip/led_strip.c @@ -0,0 +1,407 @@ +/* ---------------------------------------------------------------------------- + File: led_strip.c + Author(s): Lucas Bruder + Date Created: 11/23/2016 + Last modified: 11/26/2016 + + Updated: C. Rohs - The update thread now + only runs when signalled. The double buffer code was modified to copy on show + instead of the ping pong buffer that destroyed the buffers contents. + + The current code is not thread safe, but is more performant, and the thread + safety does not matter the was it is currently used. + + Description: LED Library for driving various led strips on ESP32. + + This library uses double buffering to display the LEDs. + ------------------------------------------------------------------------- */ + +#include "led_strip.h" +#include "freertos/task.h" + +#include + +#define LED_STRIP_TASK_SIZE (1024) +#define LED_STRIP_TASK_PRIORITY (configMAX_PRIORITIES - 1) + +#define LED_STRIP_REFRESH_PERIOD_MS (30U) // TODO: add as parameter to led_strip_init + +#define LED_STRIP_NUM_RMT_ITEMS_PER_LED (24U) // Assumes 24 bit color for each led + +// RMT Clock source is @ 80 MHz. Dividing it by 8 gives us 10 MHz frequency, or 100ns period. +#define LED_STRIP_RMT_CLK_DIV (8) + +/**************************** + WS2812 Timing + ****************************/ +#define LED_STRIP_RMT_TICKS_BIT_1_HIGH_WS2812 9 // 900ns (900ns +/- 150ns per datasheet) +#define LED_STRIP_RMT_TICKS_BIT_1_LOW_WS2812 3 // 300ns (350ns +/- 150ns per datasheet) +#define LED_STRIP_RMT_TICKS_BIT_0_HIGH_WS2812 3 // 300ns (350ns +/- 150ns per datasheet) +#define LED_STRIP_RMT_TICKS_BIT_0_LOW_WS2812 9 // 900ns (900ns +/- 150ns per datasheet) + +/**************************** + SK6812 Timing + ****************************/ +#define LED_STRIP_RMT_TICKS_BIT_1_HIGH_SK6812 6 +#define LED_STRIP_RMT_TICKS_BIT_1_LOW_SK6812 6 +#define LED_STRIP_RMT_TICKS_BIT_0_HIGH_SK6812 3 +#define LED_STRIP_RMT_TICKS_BIT_0_LOW_SK6812 9 + +/**************************** + APA106 Timing + ****************************/ +#define LED_STRIP_RMT_TICKS_BIT_1_HIGH_APA106 14 // 1.36us +/- 150ns per datasheet +#define LED_STRIP_RMT_TICKS_BIT_1_LOW_APA106 3 // 350ns +/- 150ns per datasheet +#define LED_STRIP_RMT_TICKS_BIT_0_HIGH_APA106 3 // 350ns +/- 150ns per datasheet +#define LED_STRIP_RMT_TICKS_BIT_0_LOW_APA106 14 // 1.36us +/- 150ns per datasheet + +// Function pointer for generating waveforms based on different LED drivers +typedef void (*led_fill_rmt_items_fn)(struct led_color_t *led_strip_buf, rmt_item32_t *rmt_items, uint32_t led_strip_length); + +static inline void led_strip_fill_item_level(rmt_item32_t* item, int high_ticks, int low_ticks) +{ + item->level0 = 1; + item->duration0 = high_ticks; + item->level1 = 0; + item->duration1 = low_ticks; +} + +static inline void led_strip_rmt_bit_1_sk6812(rmt_item32_t* item) +{ + led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_1_HIGH_SK6812, LED_STRIP_RMT_TICKS_BIT_1_LOW_SK6812); +} + +static inline void led_strip_rmt_bit_0_sk6812(rmt_item32_t* item) +{ + led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_0_HIGH_SK6812, LED_STRIP_RMT_TICKS_BIT_0_LOW_SK6812); +} + +static void led_strip_fill_rmt_items_sk6812(struct led_color_t *led_strip_buf, rmt_item32_t *rmt_items, uint32_t led_strip_length) +{ + uint32_t rmt_items_index = 0; + for (uint32_t led_index = 0; led_index < led_strip_length; led_index++) { + struct led_color_t led_color = led_strip_buf[led_index]; + + for (uint8_t bit = 8; bit != 0; bit--) { + uint8_t bit_set = (led_color.green >> (bit - 1)) & 1; + if(bit_set) { + led_strip_rmt_bit_1_sk6812(&(rmt_items[rmt_items_index])); + } else { + led_strip_rmt_bit_0_sk6812(&(rmt_items[rmt_items_index])); + } + rmt_items_index++; + } + for (uint8_t bit = 8; bit != 0; bit--) { + uint8_t bit_set = (led_color.red >> (bit - 1)) & 1; + if(bit_set) { + led_strip_rmt_bit_1_sk6812(&(rmt_items[rmt_items_index])); + } else { + led_strip_rmt_bit_0_sk6812(&(rmt_items[rmt_items_index])); + } + rmt_items_index++; + } + for (uint8_t bit = 8; bit != 0; bit--) { + uint8_t bit_set = (led_color.blue >> (bit - 1)) & 1; + if(bit_set) { + led_strip_rmt_bit_1_sk6812(&(rmt_items[rmt_items_index])); + } else { + led_strip_rmt_bit_0_sk6812(&(rmt_items[rmt_items_index])); + } + rmt_items_index++; + } + } +} + +static inline void led_strip_rmt_bit_1_ws2812(rmt_item32_t* item) +{ + led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_1_HIGH_WS2812, LED_STRIP_RMT_TICKS_BIT_1_LOW_WS2812); +} + +static inline void led_strip_rmt_bit_0_ws2812(rmt_item32_t* item) +{ + led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_0_HIGH_WS2812, LED_STRIP_RMT_TICKS_BIT_0_LOW_WS2812); +} + +static void led_strip_fill_rmt_items_ws2812(struct led_color_t *led_strip_buf, rmt_item32_t *rmt_items, uint32_t led_strip_length) +{ + uint32_t rmt_items_index = 0; + for (uint32_t led_index = 0; led_index < led_strip_length; led_index++) { + struct led_color_t led_color = led_strip_buf[led_index]; + + for (uint8_t bit = 8; bit != 0; bit--) { + uint8_t bit_set = (led_color.green >> (bit - 1)) & 1; + if(bit_set) { + led_strip_rmt_bit_1_ws2812(&(rmt_items[rmt_items_index])); + } else { + led_strip_rmt_bit_0_ws2812(&(rmt_items[rmt_items_index])); + } + rmt_items_index++; + } + for (uint8_t bit = 8; bit != 0; bit--) { + uint8_t bit_set = (led_color.red >> (bit - 1)) & 1; + if(bit_set) { + led_strip_rmt_bit_1_ws2812(&(rmt_items[rmt_items_index])); + } else { + led_strip_rmt_bit_0_ws2812(&(rmt_items[rmt_items_index])); + } + rmt_items_index++; + } + for (uint8_t bit = 8; bit != 0; bit--) { + uint8_t bit_set = (led_color.blue >> (bit - 1)) & 1; + if(bit_set) { + led_strip_rmt_bit_1_ws2812(&(rmt_items[rmt_items_index])); + } else { + led_strip_rmt_bit_0_ws2812(&(rmt_items[rmt_items_index])); + } + rmt_items_index++; + } + } +} + +static inline void led_strip_rmt_bit_1_apa106(rmt_item32_t* item) +{ + led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_1_HIGH_APA106, LED_STRIP_RMT_TICKS_BIT_1_LOW_APA106); +} + +static inline void led_strip_rmt_bit_0_apa106(rmt_item32_t* item) +{ + led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_0_HIGH_APA106, LED_STRIP_RMT_TICKS_BIT_0_LOW_APA106); +} + +static void led_strip_fill_rmt_items_apa106(struct led_color_t *led_strip_buf, rmt_item32_t *rmt_items, uint32_t led_strip_length) +{ + uint32_t rmt_items_index = 0; + for (uint32_t led_index = 0; led_index < led_strip_length; led_index++) { + struct led_color_t led_color = led_strip_buf[led_index]; + + for (uint8_t bit = 8; bit != 0; bit--) { + uint8_t bit_set = (led_color.red >> (bit - 1)) & 1; + if(bit_set) { + led_strip_rmt_bit_1_apa106(&(rmt_items[rmt_items_index])); + } else { + led_strip_rmt_bit_0_apa106(&(rmt_items[rmt_items_index])); + } + rmt_items_index++; + } + for (uint8_t bit = 8; bit != 0; bit--) { + uint8_t bit_set = (led_color.green >> (bit - 1)) & 1; + if(bit_set) { + led_strip_rmt_bit_1_apa106(&(rmt_items[rmt_items_index])); + } else { + led_strip_rmt_bit_0_apa106(&(rmt_items[rmt_items_index])); + } + rmt_items_index++; + } + for (uint8_t bit = 8; bit != 0; bit--) { + uint8_t bit_set = (led_color.blue >> (bit - 1)) & 1; + if(bit_set) { + led_strip_rmt_bit_1_apa106(&(rmt_items[rmt_items_index])); + } else { + led_strip_rmt_bit_0_apa106(&(rmt_items[rmt_items_index])); + } + rmt_items_index++; + } + } +} + +static void led_strip_task(void *arg) +{ + struct led_strip_t *led_strip = (struct led_strip_t *)arg; + led_fill_rmt_items_fn led_make_waveform = NULL; + + size_t num_items_malloc = (LED_STRIP_NUM_RMT_ITEMS_PER_LED * led_strip->led_strip_length); + rmt_item32_t *rmt_items = (rmt_item32_t*) malloc(sizeof(rmt_item32_t) * num_items_malloc); + if (!rmt_items) { + vTaskDelete(NULL); + } + + switch (led_strip->rgb_led_type) { + case RGB_LED_TYPE_WS2812: + led_make_waveform = led_strip_fill_rmt_items_ws2812; + break; + + case RGB_LED_TYPE_SK6812: + led_make_waveform = led_strip_fill_rmt_items_sk6812; + break; + + case RGB_LED_TYPE_APA106: + led_make_waveform = led_strip_fill_rmt_items_apa106; + break; + + default: + // Will avoid keeping it point to NULL + led_make_waveform = led_strip_fill_rmt_items_ws2812; + break; + }; + + for(;;) { + rmt_wait_tx_done(led_strip->rmt_channel, portMAX_DELAY); + vTaskDelay(LED_STRIP_REFRESH_PERIOD_MS / portTICK_PERIOD_MS); + + xSemaphoreTake(led_strip->access_semaphore, portMAX_DELAY); + + led_make_waveform(led_strip->led_strip_working, + rmt_items, + led_strip->led_strip_length); + rmt_write_items(led_strip->rmt_channel, + rmt_items, + num_items_malloc, + false); + } + + if (rmt_items) { + free(rmt_items); + } + vTaskDelete(NULL); +} + +static bool led_strip_init_rmt(struct led_strip_t *led_strip) +{ + rmt_config_t rmt_cfg = { + .rmt_mode = RMT_MODE_TX, + .channel = led_strip->rmt_channel, + .clk_div = LED_STRIP_RMT_CLK_DIV, + .gpio_num = led_strip->gpio, + .mem_block_num = 1, + .tx_config = { + .loop_en = false, + .carrier_freq_hz = 100, // Not used, but has to be set to avoid divide by 0 err + .carrier_duty_percent = 50, + .carrier_level = RMT_CARRIER_LEVEL_LOW, + .carrier_en = false, + .idle_level = RMT_IDLE_LEVEL_LOW, + .idle_output_en = true, + } + }; + + esp_err_t cfg_ok = rmt_config(&rmt_cfg); + if (cfg_ok != ESP_OK) { + return false; + } + esp_err_t install_ok = rmt_driver_install(rmt_cfg.channel, 0, 0); + if (install_ok != ESP_OK) { + return false; + } + + return true; +} + +bool led_strip_init(struct led_strip_t *led_strip) +{ + TaskHandle_t led_strip_task_handle; + + if ((led_strip == NULL) || + (led_strip->rmt_channel >= RMT_CHANNEL_MAX) || + (led_strip->gpio > GPIO_NUM_33) || + (led_strip->led_strip_working == NULL) || + (led_strip->led_strip_showing == NULL) || + (led_strip->led_strip_length == 0) || + (led_strip->access_semaphore == NULL)) { + return false; + } + + if(led_strip->led_strip_working == led_strip->led_strip_showing) { + return false; + } + + memset(led_strip->led_strip_working, 0, sizeof(struct led_color_t) * led_strip->led_strip_length); + memset(led_strip->led_strip_showing, 0, sizeof(struct led_color_t) * led_strip->led_strip_length); + + bool init_rmt = led_strip_init_rmt(led_strip); + if (!init_rmt) { + return false; + } + + xSemaphoreGive(led_strip->access_semaphore); + BaseType_t task_created = xTaskCreate(led_strip_task, + "led_strip_task", + LED_STRIP_TASK_SIZE, + led_strip, + LED_STRIP_TASK_PRIORITY, + &led_strip_task_handle); + + if (!task_created) { + return false; + } + + return true; +} + +bool led_strip_set_pixel_color(struct led_strip_t *led_strip, uint32_t pixel_num, struct led_color_t *color) +{ + bool set_led_success = true; + + if ((!led_strip) || (!color) || (pixel_num > led_strip->led_strip_length)) { + return false; + } + + led_strip->led_strip_working[pixel_num] = *color; + + return set_led_success; +} + +bool led_strip_set_pixel_rgb(struct led_strip_t *led_strip, uint32_t pixel_num, uint8_t red, uint8_t green, uint8_t blue) +{ + bool set_led_success = true; + + if ((!led_strip) || (pixel_num > led_strip->led_strip_length)) { + return false; + } + + led_strip->led_strip_working[pixel_num].red = red; + led_strip->led_strip_working[pixel_num].green = green; + led_strip->led_strip_working[pixel_num].blue = blue; + + return set_led_success; +} + +bool led_strip_get_pixel_color(struct led_strip_t *led_strip, uint32_t pixel_num, struct led_color_t *color) +{ + bool get_success = true; + + if ((!led_strip) || + (pixel_num > led_strip->led_strip_length) || + (!color)) { + color = NULL; + return false; + } + + *color = led_strip->led_strip_working[pixel_num]; + + return get_success; +} + +/** + * Updates the led buffer to be shown + */ +bool led_strip_show(struct led_strip_t *led_strip) +{ + bool success = true; + + if (!led_strip) { + return false; + } + /* copy the current buffer for display */ + memcpy(led_strip->led_strip_showing,led_strip->led_strip_working, sizeof(struct led_color_t) * led_strip->led_strip_length); + + xSemaphoreGive(led_strip->access_semaphore); + + return success; +} + +/** + * Clears the LED strip + */ +bool led_strip_clear(struct led_strip_t *led_strip) +{ + bool success = true; + if (!led_strip) { + return false; + } + + memset(led_strip->led_strip_working, + 0, + sizeof(struct led_color_t) * led_strip->led_strip_length); + + return success; +} diff --git a/components/led_strip/led_strip.h b/components/led_strip/led_strip.h new file mode 100644 index 00000000..0809a27f --- /dev/null +++ b/components/led_strip/led_strip.h @@ -0,0 +1,96 @@ +/* --------------------------------------------------------------------------- + File: led_strip.h + Author(s): Lucas Bruder + Date Created: 11/23/2016 + Last modified: 11/26/2016 + + Description: + This library can drive led strips through the RMT module on the ESP32. + ------------------------------------------------------------------------ */ + +#ifndef LED_STRIP_H +#define LED_STRIP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "freertos/FreeRTOS.h" + +#include + +enum rgb_led_type_t { + RGB_LED_TYPE_WS2812 = 0, + RGB_LED_TYPE_SK6812 = 1, + RGB_LED_TYPE_APA106 = 2, + + RGB_LED_TYPE_MAX, +}; + +/** + * RGB LED colors + */ +struct led_color_t { + uint8_t red; + uint8_t green; + uint8_t blue; +}; + +struct led_strip_t { + const enum rgb_led_type_t rgb_led_type; + uint32_t led_strip_length; + + // RMT peripheral settings + rmt_channel_t rmt_channel; + + /* + * Interrupt table is located in soc.h + * As of 11/27/16, reccomended interrupts are: + * 9, 12, 13, 17, 18, 19, 20, 21 or 23 + * Ensure that the same interrupt number isn't used twice + * across all libraries + */ + int rmt_interrupt_num; + + gpio_num_t gpio; // Must be less than GPIO_NUM_33 + + struct led_color_t *led_strip_working; + struct led_color_t *led_strip_showing; + + SemaphoreHandle_t access_semaphore; +}; + +bool led_strip_init(struct led_strip_t *led_strip); + +/** + * Sets the pixel at pixel_num to color. + */ +bool led_strip_set_pixel_color(struct led_strip_t *led_strip, uint32_t pixel_num, struct led_color_t *color); +bool led_strip_set_pixel_rgb(struct led_strip_t *led_strip, uint32_t pixel_num, uint8_t red, uint8_t green, uint8_t blue); +/** + * Get the pixel color at pixel_num for the led strip that is currently being shown! + * NOTE: If you call set_pixel_color then get_pixel_color for the same pixel_num, you will not + * get back the same pixel value. This gets you the color of the pixel currently being shown, not the one + * being updated + * + * If there is an invalid argument, color will point to NULL and this function will return false. + */ +bool led_strip_get_pixel_color(struct led_strip_t *led_strip, uint32_t pixel_num, struct led_color_t *color); + +/** + * Updates the led buffer to be shown using double buffering. + */ +bool led_strip_show(struct led_strip_t *led_strip); + +/** + * Clears the LED strip. + */ +bool led_strip_clear(struct led_strip_t *led_strip); + +#ifdef __cplusplus +} +#endif + +#endif // LED_STRIP_H diff --git a/components/led_strip/led_vu.c b/components/led_strip/led_vu.c new file mode 100644 index 00000000..49ba26a1 --- /dev/null +++ b/components/led_strip/led_vu.c @@ -0,0 +1,363 @@ +/* + * Control of LED strip within squeezelite-esp32 + * + * (c) Wizmo 2021 + * + * Loosely based on code by + * Chuck Rohs 2020, chuck@zethus.ca + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + * + * ToDo: + * Driver does support other led device. Maybe look at supporting in future. + * The VU refresh rate has been decreaced (100->75) to optimize animation of spin dial. Could make + * configurable like text scrolling (or use the same value) + * Look at reserving a status led within the effects. (may require nvs setting for center or end position) + * Artwork function, but not released as very buggy and not really practical + */ + +#include +#include +#include "esp_log.h" + +#include "led_strip.h" +#include "platform_config.h" +#include "led_vu.h" + +static const char *TAG = "led_vu"; + +#define LED_VU_STACK_SIZE (3*1024) +#define LED_VU_RMT_INTR_NUM 20U + +#define LED_VU_PEAK_HOLD 6U + +#define LED_VU_DEFAULT_GPIO 22 +#define LED_VU_DEFAULT_LENGTH 19 +#define LED_VU_MAX_LENGTH 255 + +#define max(a,b) (((a) > (b)) ? (a) : (b)) + +struct led_strip_t* led_display = NULL; +static struct led_strip_t led_strip_config = { + .rgb_led_type = RGB_LED_TYPE_WS2812, + .rmt_channel = RMT_CHANNEL_1, + .rmt_interrupt_num = LED_VU_RMT_INTR_NUM, + .gpio = GPIO_NUM_22, +}; + +static struct { + int gpio; + int length; + int vu_length; + int vu_start_l; + int vu_start_r; + int vu_odd; +} strip; + +static int led_addr(int pos ) { + if (pos < 0) return pos + strip.length; + if (pos >= strip.length) return pos - strip.length; + return pos; +} + +/**************************************************************************************** + * Initialize the led vu strip if configured. + * + */ +void led_vu_init() +{ + char* p; + char* config = config_alloc_get_str("led_vu_config", NULL, "N/A"); + + // Initialize led VU strip + char* drivername = strcasestr(config, "WS2812"); + + if ((p = strcasestr(config, "length")) != NULL) { + strip.length = atoi(strchr(p, '=') + 1); + } // else 0 + if ((p = strcasestr(config, "gpio")) != NULL) { + strip.gpio = atoi(strchr(p, '=') + 1); + } else { + strip.gpio = LED_VU_DEFAULT_GPIO; + } + // check for valid configuration + if (!drivername || !strip.gpio) { + ESP_LOGI(TAG, "led_vu configuration invalid"); + goto done; + } + + if (strip.length > LED_VU_MAX_LENGTH) strip.length = LED_VU_MAX_LENGTH; + // initialize vu settings + //strip.vu_length = (strip.length % 2) ? strip.length / 2 : (strip.length - 1) / 2; + strip.vu_length = (strip.length - 1) / 2; + strip.vu_start_l = strip.vu_length; + strip.vu_start_r = strip.vu_start_l + 1; + strip.vu_odd = strip.length - 1; + + // create driver configuration + led_strip_config.access_semaphore = xSemaphoreCreateBinary(); + led_strip_config.led_strip_length = strip.length; + led_strip_config.led_strip_working = heap_caps_malloc(strip.length * sizeof(struct led_color_t), MALLOC_CAP_8BIT); + led_strip_config.led_strip_showing = heap_caps_malloc(strip.length * sizeof(struct led_color_t), MALLOC_CAP_8BIT); + led_strip_config.gpio = strip.gpio; + + // initialize driver + bool led_init_ok = led_strip_init(&led_strip_config); + if (led_init_ok) { + led_display = &led_strip_config; + ESP_LOGI(TAG, "led_vu using gpio:%d length:%d", strip.gpio, strip.length); + } else { + ESP_LOGE(TAG, "led_vu init failed"); + goto done; + } + + // reserver max memory for remote management systems + rmt_set_mem_block_num(RMT_CHANNEL_1, 7); + + led_vu_clear(led_display); + + done: + free(config); + return; + } + +inline bool inRange(double x, double y, double z) { + return (x > y && x < z); +} + +/**************************************************************************************** + * Returns the led strip length + */ +uint16_t led_vu_string_length() { + if (!led_display) return 0; + return (uint16_t)strip.length; +} + +/**************************************************************************************** + * Turns all LEDs off (Black) + */ +void led_vu_clear() { + if (!led_display) return; + led_strip_clear(led_display); + + led_strip_show(led_display); +} + +/**************************************************************************************** + * Sets all LEDs to one color + * r = red (0-255), g = green (0-255), b - blue (0-255) + * note - all colors are adjusted for brightness + */ +void led_vu_color_all(uint8_t r, uint8_t g, uint8_t b) { + if (!led_display) return; + + struct led_color_t color_on = {.red = r, .green = g, .blue = b}; + + for (int i = 0 ; i < strip.length ; i ++){ + led_strip_set_pixel_color(led_display, i, &color_on); + } + + led_strip_show(led_display); +} + +/**************************************************************************************** + * Sets LEDs based on a data packet consiting of rgb data + * offset - starting LED, + * length - number of leds (3x rgb bytes) + * data - array of rgb values in multiples of 3 bytes + */ +void led_vu_data(uint8_t* data, uint16_t offset, uint16_t length) { + if (!led_display) return; + + uint8_t* p = (uint8_t*) data; + for (int i = 0; i < length; i++) { + led_strip_set_pixel_rgb(led_display, i+offset, *p, *(p+1), *(p+2)); + p+=3; + } + + led_strip_show(led_display); +} + +/**************************************************************************************** + * Progress bar display + * data - array of gain values(0-100) + * offset - starting position + * length - size of array + */ +void led_vu_spectrum(uint8_t* data, int bright, int length, int style) { + if (!led_display) return; + uint8_t gain,r,g,b; + int width = strip.length / length; + int pos = 0; + uint8_t* p = (uint8_t*) data; + for (int i=0; i LED_VU_MAX-step) ? LED_VU_MAX : g + step; + r = (r < step) ? 0 : r - step; + if (r == 0) b = step; + } else if (r == 0) { + b = (b > LED_VU_MAX-step) ? LED_VU_MAX : b + step; + g = (g < step) ? 0 : g- step; + if (g == 0) r = step; + } else { + r = (r > LED_VU_MAX-step) ? LED_VU_MAX : r + step; + b = (b < step) ? 0 : b - step; + if (r == 0) b = step; + } + + uint8_t rp = r * gain / LED_VU_MAX; + uint8_t gp = g * gain / LED_VU_MAX; + uint8_t bp = b * gain / LED_VU_MAX; + + // set led color_ + led_strip_set_pixel_rgb(led_display, led_pos, rp, gp, bp); + if (comet) { + led_strip_set_pixel_rgb(led_display, led_addr(led_pos-1), rp/2, gp/2, bp/2); + led_strip_set_pixel_rgb(led_display, led_addr(led_pos-2), rp/4, gp/4, bp/4); + led_strip_set_pixel_rgb(led_display, led_addr(led_pos-3), rp/8, gp/8, bp/8); + led_strip_set_pixel_rgb(led_display, led_addr(led_pos-4), 0, 0, 0); + } + + // next led + led_pos = led_addr(++led_pos); + + led_strip_show(led_display); +} + +/**************************************************************************************** + * VU meter display + * vu_l - left response (0-100), vu_r - right response (0-100) + * comet - alternate display mode + */ +void led_vu_display(int vu_l, int vu_r, int bright, bool comet) { + static int peak_l = 0; + static int peak_r = 0; + static int decay_l = 0; + static int decay_r = 0; + if (!led_display) return; + + + + // scale vu samples to length + vu_l = vu_l * strip.vu_length / bright; + vu_r = vu_r * strip.vu_length / bright; + + // calculate hold peaks + if (peak_l > vu_l) { + if (decay_l-- < 0) { + decay_l = LED_VU_PEAK_HOLD; + peak_l--; + } + } else { + peak_l = vu_l; + decay_l = LED_VU_PEAK_HOLD; + } + if (peak_r > vu_r) { + if (decay_r-- < 0) { + decay_r = LED_VU_PEAK_HOLD; + peak_r--; + } + } else { + peak_r = vu_r; + decay_r = LED_VU_PEAK_HOLD; + } + + // turn off all leds + led_strip_clear(led_display); + + // set the led bar values + uint8_t step = bright / (strip.vu_length-1); + if (step < 1) step = 1; // dor low brightness or larger strips + uint8_t g = bright * 2 / 3; // more red at top + uint8_t r = 0; + int shift = 0; + for (int i = 0; i < strip.vu_length; i++) { + // set left + if (i == peak_l) { + led_strip_set_pixel_rgb(led_display, strip.vu_start_l - i, r, g, bright); + } else if (i <= vu_l) { + shift = vu_l - i; + if (comet) + led_strip_set_pixel_rgb(led_display, strip.vu_start_l - i, r>>shift, g>>shift, 0); + else + led_strip_set_pixel_rgb(led_display, strip.vu_start_l - i, r, g, 0); + } + // set right + if (i == peak_r) { + led_strip_set_pixel_rgb(led_display, strip.vu_start_r + i, r, g, bright); + } else if (i <= vu_r) { + shift = vu_r - i; + if (comet) + led_strip_set_pixel_rgb(led_display, strip.vu_start_r + i, r>>shift, g>>shift, 0); + else + led_strip_set_pixel_rgb(led_display, strip.vu_start_r + i, r, g, 0); + } + // adjust colors (with limit checks) + r = (r > bright-step) ? bright : r + step; + g = (g < step) ? 0 : g - step; + } + + led_strip_show(led_display); +} + diff --git a/components/led_strip/led_vu.h b/components/led_strip/led_vu.h new file mode 100644 index 00000000..f6c26f12 --- /dev/null +++ b/components/led_strip/led_vu.h @@ -0,0 +1,31 @@ +/* + * Control of LED strip within squeezelite-esp32 + * + * (c) Wizmo 2021 + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + * + */ + +#include + +#define LED_VU_MAX 255U +#define LED_VU_BRIGHT 20U + +#define led_vu_color_red(B) led_vu_color_all(B, 0, 0) +#define led_vu_color_green(B) led_vu_color_all(0, B, 0) +#define led_vu_color_blue(B) led_vu_color_all(0, 0, B) +#define led_vu_color_yellow(B) led_vu_color_all(B/2, B/2, 0) + +extern struct led_strip_t* led_display; + +uint16_t led_vu_string_length(); +void led_vu_progress_bar(int pct, int bright); +void led_vu_display(int vu_l, int vu_r, int bright, bool comet); +void led_vu_spin_dial(int gain, int rate, bool comet); +void led_vu_spectrum(uint8_t* data, int bright, int length, int style); +void led_vu_color_all(uint8_t r, uint8_t g, uint8_t b); +void led_vu_data(uint8_t* data, uint16_t offset, uint16_t length); +void led_vu_clear(); + diff --git a/components/platform_console/cmd_config.c b/components/platform_console/cmd_config.c index f25bc919..19d78e32 100644 --- a/components/platform_console/cmd_config.c +++ b/components/platform_console/cmd_config.c @@ -29,6 +29,7 @@ const char * desc_spdif= "SPDIF Options"; const char * desc_audio= "General Audio Options"; const char * desc_bt_source= "Bluetooth Audio Output Options"; const char * desc_rotary= "Rotary Control"; +const char * desc_ledvu= "Led Strip Options"; extern const struct adac_s *dac_set[]; @@ -108,6 +109,15 @@ static struct { struct arg_end * end; } rotary_args; //config_rotary_get + +static struct { + struct arg_str * type; + struct arg_int * length; + struct arg_int * gpio; + struct arg_lit * clear; + struct arg_end * end; +} ledvu_args; + static struct{ struct arg_str *sink_name; struct arg_str *pin_code; @@ -635,6 +645,54 @@ static int do_cspot_config(int argc, char **argv){ FREE_AND_NULL(buf); return nerrors; } + + +static int do_ledvu_cmd(int argc, char **argv){ + ledvu_struct_t ledvu={ .type = "WS2812", .gpio = -1, .length = 0}; + esp_err_t err=ESP_OK; + int nerrors = arg_parse(argc, argv,(void **)&ledvu_args); + if (ledvu_args.clear->count) { + cmd_send_messaging(argv[0],MESSAGING_WARNING,"ledvu config cleared\n"); + config_set_value(NVS_TYPE_STR, "led_vu_config", ""); + return 0; + } + + char *buf = NULL; + size_t buf_size = 0; + FILE *f = open_memstream(&buf, &buf_size); + if (f == NULL) { + cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n"); + return 1; + } + if(nerrors >0){ + arg_print_errors(f,ledvu_args.end,desc_ledvu); + return 1; + } + + nerrors+=is_output_gpio(ledvu_args.gpio, f, &ledvu.gpio, true); + + if(ledvu_args.length->count==0 || ledvu_args.length->ival[0]<1 || ledvu_args.length->ival[0]>255){ + fprintf(f,"error: strip length must be greater than 0 and no more than 255\n"); + nerrors++; + } + else { + ledvu.length = ledvu_args.length->count>0?ledvu_args.length->ival[0]:0; + } + + if(!nerrors ){ + fprintf(f,"Storing ledvu parameters.\n"); + nerrors+=(config_ledvu_set(&ledvu )!=ESP_OK); + } + if(!nerrors ){ + fprintf(f,"Done.\n"); + } + fflush (f); + cmd_send_messaging(argv[0],nerrors>0?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf); + fclose(f); + FREE_AND_NULL(buf); + return (nerrors==0 && err==ESP_OK)?0:1; +} + static int do_i2s_cmd(int argc, char **argv) { i2s_platform_config_t i2s_dac_pin = { @@ -842,6 +900,24 @@ cJSON * rotary_cb(){ } return values; } + +cJSON * ledvu_cb(){ + cJSON * values = cJSON_CreateObject(); + const ledvu_struct_t *ledvu= config_ledvu_get(); + + if(GPIO_IS_VALID_GPIO(ledvu->gpio) && ledvu->gpio>=0 && ledvu->length > 0){ + cJSON_AddNumberToObject(values,"gpio",ledvu->gpio); + cJSON_AddNumberToObject(values,"length",ledvu->length); + } + if(strlen(ledvu->type)>0){ + cJSON_AddStringToObject(values,"type",ledvu->type); + } + else { + cJSON_AddStringToObject(values,"type","WS2812"); + } + return values; +} + cJSON * audio_cb(){ cJSON * values = cJSON_CreateObject(); char * p = config_alloc_get_default(NVS_TYPE_STR, "jack_mutes_amp", "n", 0); @@ -1252,6 +1328,24 @@ static void register_rotary_config(void){ ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); } +static void register_ledvu_config(void){ + ledvu_args.type = arg_str1(NULL,"type","|WS2812","Led type (supports one rgb strip to display built in effects and allow remote control through 'dmx' messaging)"); + ledvu_args.length = arg_int1(NULL,"length","<1..255>","Strip length (1-255 supported)"); + ledvu_args.gpio = arg_int1(NULL,"gpio","gpio","Data pin"); + ledvu_args.clear = arg_lit0(NULL, "clear", "Clear configuration"); + ledvu_args.end = arg_end(4); + + const esp_console_cmd_t cmd = { + .command = CFG_TYPE_HW("ledvu"), + .help = desc_ledvu, + .hint = NULL, + .func = &do_ledvu_cmd, + .argtable = &ledvu_args + }; + cmd_to_json_with_cb(&cmd,&ledvu_cb); + ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); +} + static void register_audio_config(void){ audio_args.jack_behavior = arg_str0("j", "jack_behavior","Headphones|Subwoofer","On supported DAC, determines the audio jack behavior. Selecting headphones will cause the external amp to be muted on insert, while selecting Subwoofer will keep the amp active all the time."); audio_args.end = arg_end(6); @@ -1340,5 +1434,6 @@ void register_config_cmd(void){ register_spdif_config(); } register_rotary_config(); + register_ledvu_config(); } diff --git a/components/services/accessors.c b/components/services/accessors.c index 8bc4b18d..61113dc0 100644 --- a/components/services/accessors.c +++ b/components/services/accessors.c @@ -316,6 +316,30 @@ esp_err_t config_rotary_set(rotary_struct_t * config){ return err; } +/**************************************************************************************** + * + */ +esp_err_t config_ledvu_set(ledvu_struct_t * config){ + int buffer_size=512; + esp_err_t err=ESP_OK; + char * config_buffer=calloc(buffer_size,1); + char * config_buffer2=calloc(buffer_size,1); + if(config_buffer && config_buffer2) { + snprintf(config_buffer,buffer_size,"%s,length=%i,gpio=%i",config->type, config->length, config->gpio); + log_send_messaging(MESSAGING_INFO,"Updating ledvu configuration to %s",config_buffer); + err = config_set_value(NVS_TYPE_STR, "led_vu_config", config_buffer); + if(err!=ESP_OK){ + log_send_messaging(MESSAGING_ERROR,"Error: %s",esp_err_to_name(err)); + } + } + else { + err = ESP_ERR_NO_MEM; + } + FREE_AND_NULL(config_buffer); + FREE_AND_NULL(config_buffer2); + return err; +} + /**************************************************************************************** * */ @@ -722,6 +746,24 @@ const rotary_struct_t * config_rotary_get() { return &rotary; } +/**************************************************************************************** + * + */ +const ledvu_struct_t * config_ledvu_get() { + + static ledvu_struct_t ledvu={ .type = "WS2812", .gpio = -1, .length = 0}; + char *config = config_alloc_get_default(NVS_TYPE_STR, "led_vu_config", NULL, 0); + if (config && *config) { + char *p; + + // ToDo: Add code for future support of alternate led types + if ((p = strcasestr(config, "gpio")) != NULL) ledvu.gpio = atoi(strchr(p, '=') + 1); + if ((p = strcasestr(config, "length")) != NULL) ledvu.length = atoi(strchr(p, '=') + 1); + free(config); + } + return &ledvu; +} + /**************************************************************************************** * */ @@ -925,6 +967,17 @@ cJSON * get_Rotary_GPIO(cJSON * list){ return llist; } +/**************************************************************************************** + * + */ +cJSON * get_ledvu_GPIO(cJSON * list){ + cJSON * llist = list?list:cJSON_CreateArray(); + + const ledvu_struct_t *ledvu= config_ledvu_get(); + add_gpio_for_value(llist,"gpio",ledvu->gpio, "led_vu", false); + return llist; +} + /**************************************************************************************** * */ @@ -1130,6 +1183,7 @@ cJSON * get_gpio_list(bool refresh) { gpio_list=get_SPI_GPIO(gpio_list); gpio_list=get_I2C_GPIO(gpio_list); gpio_list=get_DAC_GPIO(gpio_list); + gpio_list=get_ledvu_GPIO(gpio_list); gpio_list=get_psram_gpio_list(gpio_list); gpio_list=get_eth_GPIO(gpio_list); return gpio_list; diff --git a/components/services/accessors.h b/components/services/accessors.h index d7c9c640..959af575 100644 --- a/components/services/accessors.h +++ b/components/services/accessors.h @@ -84,6 +84,12 @@ typedef struct { int timer; } rotary_struct_t; +typedef struct { + char type[16]; + int length; + int gpio; +} ledvu_struct_t; + typedef struct { bool fixed; char * name; @@ -114,4 +120,6 @@ cJSON * get_gpio_list(bool refresh); bool is_dac_config_locked(); bool are_statistics_enabled(); const rotary_struct_t * config_rotary_get(); -esp_err_t config_rotary_set(rotary_struct_t * rotary); \ No newline at end of file +esp_err_t config_rotary_set(rotary_struct_t * rotary); +const ledvu_struct_t * config_ledvu_get(); +esp_err_t config_ledvu_set(ledvu_struct_t * rotary); \ No newline at end of file diff --git a/components/squeezelite-ota/CMakeLists.txt b/components/squeezelite-ota/CMakeLists.txt index cf75eab9..e7fefbaa 100644 --- a/components/squeezelite-ota/CMakeLists.txt +++ b/components/squeezelite-ota/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register(SRC_DIRS . INCLUDE_DIRS . REQUIRES app_update esp_https_ota - PRIV_REQUIRES console tools display services platform_config spi_flash vfs console freertos platform_console + PRIV_REQUIRES console tools display led_strip services platform_config spi_flash vfs console freertos platform_console ) diff --git a/components/squeezelite-ota/squeezelite-ota.c b/components/squeezelite-ota/squeezelite-ota.c index d98f4867..488e3cf0 100644 --- a/components/squeezelite-ota/squeezelite-ota.c +++ b/components/squeezelite-ota/squeezelite-ota.c @@ -157,24 +157,26 @@ static progress_t * loc_displayer_get_progress_dft(){ } static void loc_displayer_progressbar(uint8_t pct){ static progress_t * progress_coordinates; - if(!display){ - return; - } - if(!progress_coordinates) progress_coordinates = loc_displayer_get_progress_dft(); - int filler_x=progress_coordinates->filler.x1+(int)((float)progress_coordinates->filler.width*(float)pct/(float)100); + if(display) { + if(!progress_coordinates) progress_coordinates = loc_displayer_get_progress_dft(); + int filler_x=progress_coordinates->filler.x1+(int)((float)progress_coordinates->filler.width*(float)pct/(float)100); - ESP_LOGD(TAG,"Drawing %d,%d,%d,%d",progress_coordinates->border.x1,progress_coordinates->border.y1,progress_coordinates->border.x2,progress_coordinates->border.y2); - GDS_DrawBox(display,progress_coordinates->border.x1,progress_coordinates->border.y1,progress_coordinates->border.x2,progress_coordinates->border.y2,GDS_COLOR_WHITE,false); - ESP_LOGD(TAG,"Drawing %d,%d,%d,%d",progress_coordinates->filler.x1,progress_coordinates->filler.y1,filler_x,progress_coordinates->filler.y2); - if(filler_x > progress_coordinates->filler.x1){ - GDS_DrawBox(display,progress_coordinates->filler.x1,progress_coordinates->filler.y1,filler_x,progress_coordinates->filler.y2,GDS_COLOR_WHITE,true); + ESP_LOGD(TAG,"Drawing %d,%d,%d,%d",progress_coordinates->border.x1,progress_coordinates->border.y1,progress_coordinates->border.x2,progress_coordinates->border.y2); + GDS_DrawBox(display,progress_coordinates->border.x1,progress_coordinates->border.y1,progress_coordinates->border.x2,progress_coordinates->border.y2,GDS_COLOR_WHITE,false); + ESP_LOGD(TAG,"Drawing %d,%d,%d,%d",progress_coordinates->filler.x1,progress_coordinates->filler.y1,filler_x,progress_coordinates->filler.y2); + if(filler_x > progress_coordinates->filler.x1){ + GDS_DrawBox(display,progress_coordinates->filler.x1,progress_coordinates->filler.y1,filler_x,progress_coordinates->filler.y2,GDS_COLOR_WHITE,true); + } + else { + // Clear the inner box + GDS_DrawBox(display,progress_coordinates->filler.x1,progress_coordinates->filler.y1,progress_coordinates->filler.x2,progress_coordinates->filler.y2,GDS_COLOR_BLACK,true); + } + ESP_LOGD(TAG,"Updating Display"); + GDS_Update(display); } - else { - // Clear the inner box - GDS_DrawBox(display,progress_coordinates->filler.x1,progress_coordinates->filler.y1,progress_coordinates->filler.x2,progress_coordinates->filler.y2,GDS_COLOR_BLACK,true); + if (led_display) { + led_vu_progress_bar(pct, LED_VU_BRIGHT); } - ESP_LOGD(TAG,"Updating Display"); - GDS_Update(display); } void sendMessaging(messaging_types type,const char * fmt, ...){ va_list args; @@ -452,6 +454,10 @@ void ota_task_cleanup(const char * message, ...){ va_start(args, message); sendMessaging(MESSAGING_ERROR,message, args); va_end(args); + + if (led_display) led_vu_color_red(LED_VU_BRIGHT); + } else { + if (led_display) led_vu_color_green(LED_VU_BRIGHT); } FREE_RESET(ota_status->ota_write_data); FREE_RESET(ota_status->bin_data); diff --git a/components/squeezelite/CMakeLists.txt b/components/squeezelite/CMakeLists.txt index 5c1f0375..0505c672 100644 --- a/components/squeezelite/CMakeLists.txt +++ b/components/squeezelite/CMakeLists.txt @@ -13,6 +13,7 @@ idf_component_register( SRC_DIRS . external ac101 tas57xx wm8978 display tools audio + led_strip EMBED_FILES vu_s.data arrow.data ) diff --git a/components/squeezelite/displayer.c b/components/squeezelite/displayer.c index 22949bdb..c06ca2c6 100644 --- a/components/squeezelite/displayer.c +++ b/components/squeezelite/displayer.c @@ -16,6 +16,7 @@ #include "gds_text.h" #include "gds_draw.h" #include "gds_image.h" +#include "led_vu.h" #pragma pack(push, 1) @@ -107,13 +108,20 @@ struct visu_packet { }; }; +struct ledv_packet { + char opcode[4]; + u8_t which; + u8_t style; + u8_t bright; +}; + struct ANIC_header { char opcode[4]; u32_t length; u8_t mode; }; -struct dmxt_packet { +struct ledd_packet { char opcode[4]; u16_t x; u16_t length; @@ -206,7 +214,7 @@ static EXT_RAM_ATTR struct { static EXT_RAM_ATTR struct { int mode; - int max; + int n, style, max; u16_t config; struct bar_s bars[MAX_BARS] ; } led_visu; @@ -247,11 +255,10 @@ static void grfs_handler(u8_t *data, int len); static void grfg_handler(u8_t *data, int len); static void grfa_handler(u8_t *data, int len); static void visu_handler(u8_t *data, int len); -static void dmxt_handler(u8_t *data, int len); +static void ledv_handler(u8_t *data, int len); +static void ledd_handler(u8_t *data, int len); static void displayer_task(void* arg); -void *led_display; - /* scrolling undocumented information grfs B: screen number @@ -349,8 +356,7 @@ bool sb_displayer_init(void) { } if (led_display) { - // PLACEHOLDER to init config - led_visu.mode = VISU_VUMETER; + led_visu.config = led_vu_string_length(); } // inform LMS of our screen/led dimensions @@ -428,10 +434,11 @@ static void sendSETD(u16_t width, u16_t height, u16_t led_config) { pkt_header.id = 0xfe; // id 0xfe is width S:P:Squeezebox2 pkt_header.length = htonl(sizeof(pkt_header) + 6 - 8); - LOG_INFO("sending dimension %ux%u", width, height); + LOG_INFO("sending dimension display:%ux%u led_config:%u", width, height, led_config); width = htons(width); height = htons(height); + led_config = htons(led_config); LOCK_P; send_packet((uint8_t *) &pkt_header, sizeof(pkt_header)); @@ -481,8 +488,10 @@ static bool handler(u8_t *data, int len){ grfa_handler(data, len); } else if (!strncmp((char*) data, "visu", 4)) { visu_handler(data, len); - } else if (!strncmp((char*) data, "dmxt", 4)) { - dmxt_handler(data, len); + } else if (!strncmp((char*) data, "ledv", 4)) { + ledv_handler(data, len); + } else if (!strncmp((char*) data, "ledd", 4)) { + ledd_handler(data, len); } else { res = false; } @@ -1074,23 +1083,40 @@ static void displayer_update(void) { } // actualize led_vu - if (led_visu.mode) { - // PLACEHOLDER to handle led_display. you need potentially scaling of spectrum (X and Y) - // and scaling of levels (Y) and then call the + if (led_display && led_visu.mode) { + // scale to correct rgb brightness + if (led_visu.mode == VISU_VUMETER) vu_scale(led_visu.bars, led_visu.max, meters.levels); + else spectrum_scale(led_visu.n, led_visu.bars, led_visu.max, meters.samples); + + // run built in visualizer effects + if (led_visu.mode == VISU_VUMETER) { + led_vu_display(led_visu.bars[0].current, led_visu.bars[1].current, led_visu.max, led_visu.style); + } else if (led_visu.mode == VISU_SPECTRUM) { + uint8_t* led_data = malloc(led_visu.n); + uint8_t* p = (uint8_t*) led_data; + for (int i = 0; i < led_visu.n; i++) { + *p = led_visu.bars[i].current; + p++; + } + led_vu_spectrum(led_data, led_visu.max, led_visu.n, led_visu.style); + free(led_data); + } else if (led_visu.mode == VISU_WAVEFORM) { + led_vu_spin_dial(led_visu.bars[1].current, led_visu.bars[(led_visu.n/2)+1].current * 50 / led_visu.max , led_visu.style); + } } } /**************************************************************************************** * Calculate spectrum spread */ -static void spectrum_limits(int min, int n, int pos) { +static void spectrum_limits(struct bar_s *bars, int min, int n, int pos, float spectrum_scale) { if (n / 2) { - int step = ((DISPLAY_BW - min) * visu.spectrum_scale) / (n/2); - visu.bars[pos].limit = min + step; - for (int i = 1; i < n/2; i++) visu.bars[pos+i].limit = visu.bars[pos+i-1].limit + step; - spectrum_limits(visu.bars[pos + n/2 - 1].limit, n - n/2, pos + n/2); + int step = ((DISPLAY_BW - min) * spectrum_scale) / (n/2); + bars[pos].limit = min + step; + for (int i = 1; i < n/2; i++) bars[pos+i].limit = bars[pos+i-1].limit + step; + spectrum_limits(bars, bars[pos + n/2 - 1].limit, n - n/2, pos + n/2, spectrum_scale); } else { - visu.bars[pos].limit = DISPLAY_BW; + bars[pos].limit = DISPLAY_BW; } } @@ -1103,7 +1129,7 @@ static void visu_fit(int bars, int width, int height) { visu.n = bars ? bars : MAX_BARS; visu.max = height - 1; if (visu.spectrum_scale <= 0 || visu.spectrum_scale > 0.5) visu.spectrum_scale = 0.5; - spectrum_limits(0, visu.n, 0); + spectrum_limits(visu.bars, 0, visu.n, 0, visu.spectrum_scale); } else { visu.n = 2; visu.max = (visu.style ? VU_COUNT : height) - 1; @@ -1236,11 +1262,50 @@ static void visu_handler( u8_t *data, int len) { } /**************************************************************************************** - * Dmx style packet handler + * Led_visu packet handler + */ +static void ledv_handler( u8_t *data, int len) { + struct ledv_packet *pkt = (struct ledv_packet*) data; + + LOG_DEBUG("led_visu %u with parameters", pkt->which); + + xSemaphoreTake(displayer.mutex, portMAX_DELAY); + led_visu.mode = pkt->which; + led_visu.style = pkt->style; + led_visu.max = pkt->bright; + + led_vu_clear(); + if (led_visu.mode) { + if (led_visu.mode == VISU_SPECTRUM) { + led_visu.n = (led_visu.config < MAX_BARS) ? led_visu.config : MAX_BARS; + spectrum_limits(led_visu.bars, 0, led_visu.n, 0, 0.25); + } else if (led_visu.mode == VISU_WAVEFORM) { + led_visu.n = 6; + spectrum_limits(led_visu.bars, 0, led_visu.n, 0, 0.25); + } + + displayer.wake = 1; // wake up + + // reset bars maximum + for (int i = led_visu.n; --i >= 0;) led_visu.bars[i].max = 0; + + LOG_INFO("LED Visualizer mode %u with bars:%u max:%u style:%d", led_visu.mode, led_visu.n, led_visu.max, led_visu.style); + } else { + LOG_INFO("Stopping led visualizer"); + } + + xSemaphoreGive(displayer.mutex); + + // resume displayer task + vTaskResume(displayer.task); +} + +/**************************************************************************************** + * Led_data dmx style packet handler * ToDo: make packet match dmx protocol format */ -static void dmxt_handler( u8_t *data, int len) { - struct dmxt_packet *pkt = (struct dmxt_packet*) data; +static void ledd_handler( u8_t *data, int len) { + struct ledd_packet *pkt = (struct ledd_packet*) data; uint16_t offset = htons(pkt->x); uint16_t length = htons(pkt->length); @@ -1248,8 +1313,9 @@ static void dmxt_handler( u8_t *data, int len) { xSemaphoreTake(displayer.mutex, portMAX_DELAY); - // PLACEHOLDER - //led_vu_data(data + sizeof(struct dmxt_packet), offset, length); + led_vu_data(data + sizeof(struct ledd_packet), offset, length); + + displayer.wake = 1000; // wait a little while xSemaphoreGive(displayer.mutex); } diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index f98926be..80054ede 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register(SRC_DIRS . - PRIV_REQUIRES _override esp_common wifi-manager pthread squeezelite-ota platform_console telnet display targets + PRIV_REQUIRES _override esp_common wifi-manager pthread squeezelite-ota platform_console telnet display targets led_strip EMBED_FILES ../server_certs/github.pem LDFRAGMENTS "linker.lf" ) diff --git a/main/esp_app_main.c b/main/esp_app_main.c index 0343e332..fa829e02 100644 --- a/main/esp_app_main.c +++ b/main/esp_app_main.c @@ -42,6 +42,7 @@ #include "gds_draw.h" #include "gds_text.h" #include "gds_font.h" +#include "led_vu.h" #include "display.h" #include "accessors.h" #include "cmd_system.h" @@ -73,6 +74,7 @@ extern const uint8_t server_cert_pem_end[] asm("_binary_github_pem_end"); // as an exception _init function don't need include extern void services_init(void); extern void display_init(char *welcome); +extern void led_vu_init(void); extern void target_init(char *target); const char * str_or_unknown(const char * str) { return (str?str:unknown_string_placeholder); } const char * str_or_null(const char * str) { return (str?str:null_string_placeholder); } @@ -368,6 +370,7 @@ void register_default_nvs(){ register_default_string_val("ethtmout","8"); register_default_string_val("dhcp_tmout","8"); register_default_string_val("target", CONFIG_TARGET); + register_default_string_val("led_vu_config", ""); #ifdef CONFIG_CSPOT_SINK register_default_string_val("enable_cspot", STR(CONFIG_CSPOT_SINK)); register_default_string_val("cspot_config", ""); @@ -467,10 +470,18 @@ void app_main() target_init(target); free(target); } - if(is_recovery_running && display){ - GDS_ClearExt(display, true); - GDS_SetFont(display, &Font_line_2 ); - GDS_TextPos(display, GDS_FONT_DEFAULT, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "RECOVERY"); + ESP_LOGI(TAG,"Initializing led_vu"); + led_vu_init(); + + if(is_recovery_running) { + if (display) { + GDS_ClearExt(display, true); + GDS_SetFont(display, &Font_line_2 ); + GDS_TextPos(display, GDS_FONT_DEFAULT, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "RECOVERY"); + } + if(led_display) { + led_vu_color_yellow(LED_VU_BRIGHT); + } } diff --git a/plugin/SqueezeESP32.zip b/plugin/SqueezeESP32.zip index cc52e727d79951d7e032a4513b0cd218de157327..79b7f39b9ef5a73613bdb1920e04f5570d198dd3 100644 GIT binary patch literal 19514 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 literal 18239 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 S2>^ib@j3n2;^F`G?f(O_;I#Ds diff --git a/plugin/SqueezeESP32/HTML/EN/plugins/SqueezeESP32/settings/player.html b/plugin/SqueezeESP32/HTML/EN/plugins/SqueezeESP32/settings/player.html index f5e683d5..0ddf2fd6 100644 --- a/plugin/SqueezeESP32/HTML/EN/plugins/SqueezeESP32/settings/player.html +++ b/plugin/SqueezeESP32/HTML/EN/plugins/SqueezeESP32/settings/player.html @@ -106,6 +106,34 @@
[% END %] + [% IF prefs.pref_led_config %] + [% WRAPPER setting title="PLUGIN_SQUEEZEESP32_LED_CONFIG" desc="PLUGIN_SQUEEZEESP32_LED_CONFIG_DESC" %] + + + [% prefs.pref_led_config %] + [% END %] + [% WRAPPER setting title="PLUGIN_SQUEEZEESP32_LED_VISUALIZER" desc="PLUGIN_SQUEEZEESP32_LED_VISUALIZER_DESC" %] + + [% END %] + [% WRAPPER setting title="PLUGIN_SQUEEZEESP32_LED_BRIGHTNESS" desc="PLUGIN_SQUEEZEESP32_LED_BRIGHTNESS_DESC" %] + + [% END %] + + [% WRAPPER setting title="PLUGIN_SQUEEZEESP32_LED_DATA" desc="PLUGIN_SQUEEZEESP32_LED_DATA_DESC" %] +   + [% "PLUGIN_SQUEEZEESP32_LED_DATA_X" | string %]  + + [% "PLUGIN_SQUEEZEESP32_LED_DATA_CMD" | string %]  + + [% END %] +
+ [% END %] + [% IF pref_equalizer %] [% WRAPPER setting title="PLUGIN_SQUEEZEESP32_EQUALIZER" desc="" %]
[% "PLUGIN_SQUEEZEESP32_EQUALIZER_SAVE" | string %]
diff --git a/plugin/SqueezeESP32/Player.pm b/plugin/SqueezeESP32/Player.pm index cbc32d3b..00dd167d 100644 --- a/plugin/SqueezeESP32/Player.pm +++ b/plugin/SqueezeESP32/Player.pm @@ -10,6 +10,7 @@ use Slim::Utils::Log; use Slim::Utils::Prefs; use Plugins::SqueezeESP32::FirmwareHelper; +use Plugins::SqueezeESP32::RgbLed; my $sprefs = preferences('server'); my $prefs = preferences('plugin.squeezeesp32'); @@ -63,6 +64,10 @@ sub maxTreble { 20 } sub minTreble { -13 } sub maxBass { 20 } sub minBass { -13 } +sub hasLED { + my $client = shift; + return $prefs->client($client)->get('led_config') || 0; +} sub init { my $client = shift; @@ -98,6 +103,7 @@ sub init { $client->SUPER::init(@_); Plugins::SqueezeESP32::FirmwareHelper::init($client); + Plugins::SqueezeESP32::RgbLed::init($client); main::INFOLOG && $log->is_info && $log->info("SqueezeESP player connected: " . $client->id); } @@ -110,6 +116,9 @@ sub initPrefs { $prefs->client($client)->init( { equalizer => [(0) x 10], artwork => undef, + led_config => 0, + led_visualizer => 0, + led_brightness => 20, } ); $prefs->setValidate({ @@ -169,6 +178,9 @@ sub playerSettingsFrame { main::INFOLOG && $log->is_info && $log->info("Setting player $value" . "x" . "$height for ", $client->name); } + my $led_config = (unpack('Cnnn',$$data_ref))[3]; + $prefs->client($client)->set('led_config', $led_config); + main::INFOLOG && $log->is_info && $led_config && $log->info("Setting led length $led_config for ", $client->name); } $client->SUPER::playerSettingsFrame($data_ref); diff --git a/plugin/SqueezeESP32/PlayerSettings.pm b/plugin/SqueezeESP32/PlayerSettings.pm index 4265f335..3c1298ae 100644 --- a/plugin/SqueezeESP32/PlayerSettings.pm +++ b/plugin/SqueezeESP32/PlayerSettings.pm @@ -7,6 +7,7 @@ use List::Util qw(first min max); use Slim::Utils::Log; use Slim::Utils::Prefs; +use Slim::Utils::Strings qw(string cstring); my $sprefs = preferences('server'); my $prefs = preferences('plugin.squeezeesp32'); @@ -33,6 +34,7 @@ sub prefs { my ($class, $client) = @_; my @prefs; push @prefs, qw(width small_VU) if $client->displayWidth; + push @prefs, qw(led_config led_visualizer led_brightness);# if $client->hasLED; return ($prefs->client($client), @prefs); } @@ -86,6 +88,12 @@ sub handler { $cprefs->set('equalizer', $equalizer); $client->update_tones($equalizer); } + + if ($client->hasLED) { + $cprefs->set('led_visualizer', $paramRef->{'pref_led_visualizer'} || 0); + $cprefs->set('led_brightness', $paramRef->{'pref_led_brightness'} || 20); + Plugins::SqueezeESP32::RgbLed::updateLED($client); + } } if ($client->displayWidth) { @@ -95,6 +103,10 @@ sub handler { $paramRef->{'pref_artwork'} = $cprefs->get('artwork'); } + if ($client->hasLED) { + $paramRef->{'ledVisualModes'} = Plugins::SqueezeESP32::RgbLed::ledVisualModeOptions($client); + } + $paramRef->{'pref_equalizer'} = $cprefs->get('equalizer') if $client->can('depth') && $client->depth == 16; $paramRef->{'player_ip'} = $client->ip; diff --git a/plugin/SqueezeESP32/RgbLed.pm b/plugin/SqueezeESP32/RgbLed.pm new file mode 100644 index 00000000..99167516 --- /dev/null +++ b/plugin/SqueezeESP32/RgbLed.pm @@ -0,0 +1,188 @@ +package Plugins::SqueezeESP32::RgbLed; + +=head1 NAME + +Plugins::SqueezeESP32::RgbLed + +=head1 DESCRIPTION + +L + +=cut + +use strict; +use Slim::Utils::Strings qw(string cstring); + +use Slim::Utils::Log; +use Slim::Utils::Prefs; +use Plugins::SqueezeESP32::Player + +my $log = logger('player.RgbLed'); + +my $prefs = preferences('plugin.squeezeesp32'); +my $log = logger('plugin.squeezeesp32'); + +sub init { + Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['playlist'], ['open', 'pause', 'resume', 'stop', 'clear'] ]); + + # register led visualizer comands to allow independant update and command line controls. + Slim::Control::Request::addDispatch([ 'dmx', '_data', '_xoff'], [1, 0, 0, \&sendDMX]); + Slim::Control::Request::addDispatch([ 'led_visual', '_mode', '_bright'], [1, 0, 0, \&setLEDVisu]); +} + +my $VISUALIZER_NONE = 0; +my $VISUALIZER_VUMETER = 1; +my $VISUALIZER_SPECTRUM_ANALYZER = 2; +my $VISUALIZER_WAVEFORM = 3; +my @ledvisualizers = ( + { desc => ['BLANK'], + params => [$VISUALIZER_NONE], + }, + { desc => ['VISUALIZER_ANALOG_VUMETER'], + params => [$VISUALIZER_VUMETER, 0], + }, + { desc => ['VISUALIZER_DIGITAL_VUMETER'], + params => [$VISUALIZER_VUMETER, 1], + }, + { desc => ['VISUALIZER_SPECTRUM_ANALYZER'], + params => [$VISUALIZER_SPECTRUM_ANALYZER, 0], + }, + { desc => ['VISUALIZER_SPECTRUM_ANALYZER','2'], + params => [$VISUALIZER_SPECTRUM_ANALYZER, 1], + }, + { desc => ['PLUGIN_SQUEEZEESP32_WAVEFORM'], + params => [$VISUALIZER_WAVEFORM, 0], + }, + { desc => ['PLUGIN_SQUEEZEESP32_WAVEFORM','2'], + params => [$VISUALIZER_WAVEFORM, 1], + }, +); + +my $nledvisualizers = $#ledvisualizers; + +sub ledVisualizerModes { + return \@ledvisualizers; +} + +sub ledVisualizerNModes { + return $nledvisualizers; +} + +sub updateLED { + my $client = shift; + my $cprefs = $prefs->client($client); + + my $visu = $cprefs->get('led_visualizer') || 0; + my $bright = $cprefs->get('led_brightness') || 20; + + $visu = 0 if ($visu < 0 || $visu > ledVisualizerNModes || !(Slim::Player::Source::playmode($client) eq 'play')); + my $modes = ledVisualizerModes; + my $params = $modes->[$visu]{'params'}; + my $data = pack('CCC', $params->[0], $params->[1], $bright); + main::INFOLOG && $log->is_debug && $log->info("Sending visu mode $visu ", $client->name); + + $client->sendFrame( ledv => \$data ); +} + +sub ledVisualParams { + my $client = shift; + + my $visu = $prefs->client($client)->get('led_visualizer') || 0; + + return $ledvisualizers[$visu]{params}; +} + +sub ledVisualModeOptions { + my $client = shift; + + my $display = { + '-1' => ' ' + }; + + my $modes = ledVisualizerModes; + my $nmodes = ledVisualizerNModes; + + for (my $i = 0; $i <= $nmodes; $i++) { + + my $desc = $modes->[$i]{'desc'}; + + for (my $j = 0; $j < scalar @$desc; $j++) { + + $display->{$i} .= ' ' if ($j > 0); + $display->{$i} .= string(@{$desc}[$j]) || @{$desc}[$j]; + } + } + + return $display; +} + +sub sendDMX { + my $request = shift; + + # check this is the correct command. + if ($request->isNotCommand([['dmx']])) { + $request->setStatusBadDispatch(); + return; + } + + # get our parameters + my $client = $request->client(); + + my $count = 0; + my $outData; + my @values = split(',', $request->getParam('_data') || ''); + foreach my $val (@values) { + $outData .= pack ( 'C', $val); + $count++; + } + $count /= 3; + + my $data = pack('nn', $request->getParam('_xoff') || 0, $count ) . $outData; + + # changed from dmxt to ledd (matches 'ledc' for tricolor led in receiver player) + $client->sendFrame( ledd => \$data ); +} + +sub setLEDVisu { + my $request = shift; + + # check this is the correct command. + if ($request->isNotCommand([['led_visual']])) { + $request->setStatusBadDispatch(); + return; + } + + my $client = $request->client(); + return if (!$client->hasLED); + + my $cprefs = $prefs->client($client); + + my $visu = $cprefs->get('led_visualizer') || 0; + my $mode = $request->getParam('_mode') || -1; + if ($mode == -1) { + $visu+=1; + } else { + $visu = $mode; + } + $visu = 0 if ($visu < 0 || $visu > ledVisualizerNModes); + $cprefs->set('led_visualizer', $visu); + + my $bright = $request->getParam('_bright') || -1; + if ($bright >= 0 && $bright < 256) { + $cprefs->set('led_brightness', $bright); + } + + updateLED($client); +} + +sub onNotification { + my $request = shift; + my $client = $request->client || return; + + foreach my $player ($client->syncGroupActiveMembers) { + next unless $player->isa('Plugins::SqueezeESP32::Player'); + updateLED($player) if $player->hasLED; + } +} + +1; diff --git a/plugin/SqueezeESP32/strings.txt b/plugin/SqueezeESP32/strings.txt index bd9daf29..78e3d1b8 100644 --- a/plugin/SqueezeESP32/strings.txt +++ b/plugin/SqueezeESP32/strings.txt @@ -106,6 +106,44 @@ PLUGIN_SQUEEZEESP32_ARTWORK_X PLUGIN_SQUEEZEESP32_ARTWORK_Y EN Y +PLUGIN_SQUEEZEESP32_LED_CONFIG + EN Led RGB Strip + +PLUGIN_SQUEEZEESP32_LED_CONFIG_DESC + EN Length of the Led strip reported by the player + +PLUGIN_SQUEEZEESP32_LED_VISUALIZER + EN Led Visualizer + +PLUGIN_SQUEEZEESP32_LED_VISUALIZER_DESC + EN Select Led Visualizer from the built in effects + +PLUGIN_SQUEEZEESP32_LED_BRIGHTNESS + EN Led Brghtness + +PLUGIN_SQUEEZEESP32_LED_BRIGHTNESS_DESC + EN Sets the brightness of the Led Visualizer effects + +PLUGIN_SQUEEZEESP32_LED_DATA + EN Led Test + +PLUGIN_SQUEEZEESP32_LED_DATA_DESC + EN Sends custom RGB data to the Led Strip. + EN
Enter R,G,B values (comma delimited). Repeat RGB for multiple Led sequences. Use the Offset to specifiy the first LED of the sequence. + EN
Use the Set button the transmit. + +PLUGIN_SQUEEZEESP32_LED_DATA_SEND + EN Set + +PLUGIN_SQUEEZEESP32_LED_DATA_X + EN Offset + +PLUGIN_SQUEEZEESP32_LED_DATA_CMD + EN Command + +PLUGIN_SQUEEZEESP32_WAVEFORM + EN Waveform Visualizer + PLUGIN_SQUEEZEESP32_EQUALIZER DE Grafischer Equalizer EN Graphic equalizer From aa865e17b28c87fe38b950b18e9182a9b7772ddd Mon Sep 17 00:00:00 2001 From: Wizmo2 Date: Thu, 24 Nov 2022 03:46:10 -0500 Subject: [PATCH 02/19] fix for 4.3 --- components/led_strip/led_strip.h | 3 +-- components/squeezelite-ota/squeezelite-ota.c | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/led_strip/led_strip.h b/components/led_strip/led_strip.h index 0809a27f..f1ec881a 100644 --- a/components/led_strip/led_strip.h +++ b/components/led_strip/led_strip.h @@ -17,8 +17,7 @@ extern "C" { #include #include -#include "freertos/FreeRTOS.h" - +#include "freertos/semphr.h" #include enum rgb_led_type_t { diff --git a/components/squeezelite-ota/squeezelite-ota.c b/components/squeezelite-ota/squeezelite-ota.c index 488e3cf0..0c2902f4 100644 --- a/components/squeezelite-ota/squeezelite-ota.c +++ b/components/squeezelite-ota/squeezelite-ota.c @@ -35,6 +35,7 @@ #include "gds.h" #include "gds_text.h" #include "gds_draw.h" +#include "led_vu.h" #include "platform_esp32.h" #include "lwip/sockets.h" #include "globdefs.h" From 8667bd82b166512fd87b2bce99af9e4d8d6cb9a5 Mon Sep 17 00:00:00 2001 From: Wizmo2 Date: Thu, 24 Nov 2022 04:16:12 -0500 Subject: [PATCH 03/19] merge readme chnages --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 690e7f4d..6e9a1ae7 100644 --- a/README.md +++ b/README.md @@ -253,6 +253,21 @@ See §**set_GPIO** for how to set the green and red LEDs. In addition, their bri [green=0..100][,red=0..100] ``` NB: For well-known configuration, this is ignored + +### LED Strip +One LED strip with up to 255 addressable LEDs can be configured to offer enhanced visualizations. The LED strip can also be controlled remotely though the LMS server (using the CLI interface). Currently only WS2812B LEDs are supported. Set the LED Strip configuration (or NVS led_vu_config) to `WS2812,length=,gpio=, where is the number of leds in the strip (1..255), and is the data pin.` + +The latest LMS plugin update is required to set the visualizer mode and brightness, in the ESP32 settings page for the player. The plugin also adds the following CLI command options +``` + led_visual [] [brightness(1-255)] + Toggles or selects the visulaizer mode. + The visualizer brighness can be controled using the optional tag. + + dmx [] + Sets the LED at position "offset" to any RGB color where "R"(red),"G"(green), and "B"(blue) are values from 0(off) to 255(max brightness). + Add additional RGB values to the delimited string to set multiple LEDs. +``` + ### Rotary Encoder One rotary encoder is supported, quadrature shift with press. Such encoders usually have 2 pins for encoders (A and B), and common C that must be set to ground and an optional SW pin for press. A, B and SW must be pulled up, so automatic pull-up is provided by ESP32, but you can add your own resistors. A bit of filtering on A and B (~470nF) helps for debouncing which is not made by software. From c88680adab6822a9919037edc86aa2e87355a109 Mon Sep 17 00:00:00 2001 From: Wizmo2 Date: Thu, 24 Nov 2022 12:27:02 -0500 Subject: [PATCH 04/19] update malloc --- components/services/accessors.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/components/services/accessors.c b/components/services/accessors.c index 61113dc0..d6c5ce05 100644 --- a/components/services/accessors.c +++ b/components/services/accessors.c @@ -322,8 +322,8 @@ esp_err_t config_rotary_set(rotary_struct_t * config){ esp_err_t config_ledvu_set(ledvu_struct_t * config){ int buffer_size=512; esp_err_t err=ESP_OK; - char * config_buffer=calloc(buffer_size,1); - char * config_buffer2=calloc(buffer_size,1); + char * config_buffer=malloc_init_external(buffer_size); + char * config_buffer2=malloc_init_external(buffer_size); if(config_buffer && config_buffer2) { snprintf(config_buffer,buffer_size,"%s,length=%i,gpio=%i",config->type, config->length, config->gpio); log_send_messaging(MESSAGING_INFO,"Updating ledvu configuration to %s",config_buffer); @@ -335,8 +335,6 @@ esp_err_t config_ledvu_set(ledvu_struct_t * config){ else { err = ESP_ERR_NO_MEM; } - FREE_AND_NULL(config_buffer); - FREE_AND_NULL(config_buffer2); return err; } From 3f4bba2443d3844d037c5f11d21945ca71e89d18 Mon Sep 17 00:00:00 2001 From: Wizmo2 Date: Fri, 25 Nov 2022 13:51:04 -0500 Subject: [PATCH 05/19] imporve wavefom effect --- components/led_strip/led_vu.c | 16 +++++++++------- components/led_strip/led_vu.h | 2 +- components/squeezelite/displayer.c | 13 ++++++++----- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/components/led_strip/led_vu.c b/components/led_strip/led_vu.c index 49ba26a1..57098da0 100644 --- a/components/led_strip/led_vu.c +++ b/components/led_strip/led_vu.c @@ -239,7 +239,7 @@ void led_vu_progress_bar(int pct, int bright) { * gain - brightness (0-100), rate - color change speed (0-100) * comet - alternate display mode */ -void led_vu_spin_dial(int gain, int rate, bool comet) +void led_vu_spin_dial(int gain, int rate, int speed, bool comet) { if (!led_display) return; @@ -270,18 +270,20 @@ void led_vu_spin_dial(int gain, int rate, bool comet) uint8_t gp = g * gain / LED_VU_MAX; uint8_t bp = b * gain / LED_VU_MAX; - // set led color_ - led_strip_set_pixel_rgb(led_display, led_pos, rp, gp, bp); + // set led color + speed++; if (comet) { + led_strip_clear(led_display); led_strip_set_pixel_rgb(led_display, led_addr(led_pos-1), rp/2, gp/2, bp/2); led_strip_set_pixel_rgb(led_display, led_addr(led_pos-2), rp/4, gp/4, bp/4); led_strip_set_pixel_rgb(led_display, led_addr(led_pos-3), rp/8, gp/8, bp/8); - led_strip_set_pixel_rgb(led_display, led_addr(led_pos-4), 0, 0, 0); + //led_strip_set_pixel_rgb(led_display, led_addr(led_pos-4), 0, 0, 0); + } + for (int i = 0; i < speed; i++) { + led_strip_set_pixel_rgb(led_display, led_pos, rp, gp, bp); + led_pos = led_addr(++led_pos); } - // next led - led_pos = led_addr(++led_pos); - led_strip_show(led_display); } diff --git a/components/led_strip/led_vu.h b/components/led_strip/led_vu.h index f6c26f12..eedf3b38 100644 --- a/components/led_strip/led_vu.h +++ b/components/led_strip/led_vu.h @@ -23,7 +23,7 @@ extern struct led_strip_t* led_display; uint16_t led_vu_string_length(); void led_vu_progress_bar(int pct, int bright); void led_vu_display(int vu_l, int vu_r, int bright, bool comet); -void led_vu_spin_dial(int gain, int rate, bool comet); +void led_vu_spin_dial(int gain, int rate, int speed, bool comet); void led_vu_spectrum(uint8_t* data, int bright, int length, int style); void led_vu_color_all(uint8_t r, uint8_t g, uint8_t b); void led_vu_data(uint8_t* data, uint16_t offset, uint16_t length); diff --git a/components/squeezelite/displayer.c b/components/squeezelite/displayer.c index c06ca2c6..557d6ca6 100644 --- a/components/squeezelite/displayer.c +++ b/components/squeezelite/displayer.c @@ -1084,14 +1084,12 @@ static void displayer_update(void) { // actualize led_vu if (led_display && led_visu.mode) { - // scale to correct rgb brightness - if (led_visu.mode == VISU_VUMETER) vu_scale(led_visu.bars, led_visu.max, meters.levels); - else spectrum_scale(led_visu.n, led_visu.bars, led_visu.max, meters.samples); - // run built in visualizer effects if (led_visu.mode == VISU_VUMETER) { + vu_scale(led_visu.bars, led_visu.max, meters.levels); led_vu_display(led_visu.bars[0].current, led_visu.bars[1].current, led_visu.max, led_visu.style); } else if (led_visu.mode == VISU_SPECTRUM) { + spectrum_scale(led_visu.n, led_visu.bars, led_visu.max, meters.samples); uint8_t* led_data = malloc(led_visu.n); uint8_t* p = (uint8_t*) led_data; for (int i = 0; i < led_visu.n; i++) { @@ -1101,7 +1099,12 @@ static void displayer_update(void) { led_vu_spectrum(led_data, led_visu.max, led_visu.n, led_visu.style); free(led_data); } else if (led_visu.mode == VISU_WAVEFORM) { - led_vu_spin_dial(led_visu.bars[1].current, led_visu.bars[(led_visu.n/2)+1].current * 50 / led_visu.max , led_visu.style); + spectrum_scale(led_visu.n, led_visu.bars, led_visu.max, meters.samples); + led_vu_spin_dial( + led_visu.bars[1].current, + led_visu.bars[(led_visu.n/2)+1].current * 50 / led_visu.max, + led_visu.bars[led_visu.n-2].current * 5 / led_visu.max, + led_visu.style); } } } From f5207def53f3334ad525cf523c7512a6ee4f868a Mon Sep 17 00:00:00 2001 From: Wizmo2 Date: Fri, 25 Nov 2022 18:01:20 -0500 Subject: [PATCH 06/19] better memory management and more effect tweaks --- components/squeezelite/displayer.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/components/squeezelite/displayer.c b/components/squeezelite/displayer.c index 557d6ca6..ad8870f8 100644 --- a/components/squeezelite/displayer.c +++ b/components/squeezelite/displayer.c @@ -188,7 +188,7 @@ static struct { bool enable, full; } artwork; -#define MAX_BARS 32 +#define MAX_BARS 48 #define VISU_ESP32 0x10 static EXT_RAM_ATTR struct { int bar_gap, bar_width, bar_border; @@ -207,6 +207,8 @@ static EXT_RAM_ATTR struct { } back; } visu; +static uint8_t* led_data; + static EXT_RAM_ATTR struct { float fft[FFT_LEN*2], samples[FFT_LEN*2], hanning[FFT_LEN]; int levels[2]; @@ -357,6 +359,7 @@ bool sb_displayer_init(void) { if (led_display) { led_visu.config = led_vu_string_length(); + led_data = malloc(MAX_BARS); } // inform LMS of our screen/led dimensions @@ -1090,21 +1093,19 @@ static void displayer_update(void) { led_vu_display(led_visu.bars[0].current, led_visu.bars[1].current, led_visu.max, led_visu.style); } else if (led_visu.mode == VISU_SPECTRUM) { spectrum_scale(led_visu.n, led_visu.bars, led_visu.max, meters.samples); - uint8_t* led_data = malloc(led_visu.n); uint8_t* p = (uint8_t*) led_data; for (int i = 0; i < led_visu.n; i++) { *p = led_visu.bars[i].current; p++; } led_vu_spectrum(led_data, led_visu.max, led_visu.n, led_visu.style); - free(led_data); } else if (led_visu.mode == VISU_WAVEFORM) { spectrum_scale(led_visu.n, led_visu.bars, led_visu.max, meters.samples); led_vu_spin_dial( - led_visu.bars[1].current, - led_visu.bars[(led_visu.n/2)+1].current * 50 / led_visu.max, - led_visu.bars[led_visu.n-2].current * 5 / led_visu.max, - led_visu.style); + led_visu.bars[led_visu.n-2].current, + led_visu.bars[(led_visu.n/2)+1].current * 50 / led_visu.max, + led_visu.bars[1].current * 4 / led_visu.max, + led_visu.style); } } } From 7f6406726f7f48183f5ee2b107cbb75554263359 Mon Sep 17 00:00:00 2001 From: Wizmo2 Date: Sun, 16 Apr 2023 10:48:38 -0400 Subject: [PATCH 07/19] update to latest build script --- sdkconfig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sdkconfig b/sdkconfig index 6182580f..c2dcc4b6 100644 --- a/sdkconfig +++ b/sdkconfig @@ -210,6 +210,7 @@ CONFIG_SPKFAULT_GPIO=-1 CONFIG_BAT_CHANNEL=-1 CONFIG_LED_GREEN_GPIO=-1 CONFIG_LED_RED_GPIO=-1 +CONFIG_SET_GPIO="" # end of Target # @@ -285,7 +286,6 @@ CONFIG_CSPOT_SINK=y # Various I/O # CONFIG_I2C_CONFIG="" -CONFIG_SET_GPIO="" CONFIG_ROTARY_ENCODER="" # end of Various I/O @@ -942,11 +942,11 @@ 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_ABORT is not set # CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE is not set -# CONFIG_FREERTOS_ASSERT_DISABLE is not set +CONFIG_FREERTOS_ASSERT_DISABLE=y CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 -CONFIG_FREERTOS_ISR_STACKSIZE=1536 +CONFIG_FREERTOS_ISR_STACKSIZE=2096 # CONFIG_FREERTOS_LEGACY_HOOKS is not set CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y @@ -1119,7 +1119,7 @@ CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1 CONFIG_LWIP_SNTP_UPDATE_DELAY=3600000 # end of SNTP -# CONFIG_LWIP_ESP_LWIP_ASSERT is not set +CONFIG_LWIP_ESP_LWIP_ASSERT=y # # Hooks From f7227975875f2d11c2b6f1bbe56923a7128ae436 Mon Sep 17 00:00:00 2001 From: Wizmo2 Date: Sun, 23 Apr 2023 18:41:47 +0100 Subject: [PATCH 08/19] dos2unix cspot --- .../cspot/bell/external/alac/convert-utility/ALACconvert.sln | 2 +- components/spotify/cspot/bell/external/mdnssvc/mdnssvc.sln | 2 +- .../spotify/cspot/bell/external/opus/win32/VS2015/common.props | 2 +- .../spotify/cspot/bell/external/opus/win32/VS2015/opus.sln | 2 +- .../spotify/cspot/bell/external/opus/win32/VS2015/opus.vcxproj | 2 +- .../cspot/bell/external/opus/win32/VS2015/opus.vcxproj.filters | 2 +- .../cspot/bell/external/opus/win32/VS2015/opus_demo.vcxproj | 2 +- .../bell/external/opus/win32/VS2015/opus_demo.vcxproj.filters | 2 +- .../cspot/bell/external/opus/win32/VS2015/test_opus_api.vcxproj | 2 +- .../external/opus/win32/VS2015/test_opus_api.vcxproj.filters | 2 +- .../bell/external/opus/win32/VS2015/test_opus_decode.vcxproj | 2 +- .../external/opus/win32/VS2015/test_opus_decode.vcxproj.filters | 2 +- .../bell/external/opus/win32/VS2015/test_opus_encode.vcxproj | 2 +- .../external/opus/win32/VS2015/test_opus_encode.vcxproj.filters | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/components/spotify/cspot/bell/external/alac/convert-utility/ALACconvert.sln b/components/spotify/cspot/bell/external/alac/convert-utility/ALACconvert.sln index 4221168f..d1f1c3f0 100644 --- a/components/spotify/cspot/bell/external/alac/convert-utility/ALACconvert.sln +++ b/components/spotify/cspot/bell/external/alac/convert-utility/ALACconvert.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 9.00 # Visual Studio 2005 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "alacConvert", "alacConvert.vcproj", "{B9B08911-AF77-447E-A4DD-692AFFA0E346}" diff --git a/components/spotify/cspot/bell/external/mdnssvc/mdnssvc.sln b/components/spotify/cspot/bell/external/mdnssvc/mdnssvc.sln index e623a7f4..b64f33a9 100644 --- a/components/spotify/cspot/bell/external/mdnssvc/mdnssvc.sln +++ b/components/spotify/cspot/bell/external/mdnssvc/mdnssvc.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.3.32901.215 diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/common.props b/components/spotify/cspot/bell/external/opus/win32/VS2015/common.props index f6e920b6..725c0b60 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/common.props +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/common.props @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.sln b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.sln index 7b678e7f..3a603b08 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.sln +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 VisualStudioVersion = 14.0.25420.1 diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.vcxproj b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.vcxproj index 34b1233c..beefe2f2 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.vcxproj +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.vcxproj @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.vcxproj.filters b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.vcxproj.filters index 8c61d6a0..cd746f6e 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.vcxproj.filters +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.vcxproj.filters @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus_demo.vcxproj b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus_demo.vcxproj index f4344e53..1bfa9383 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus_demo.vcxproj +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus_demo.vcxproj @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus_demo.vcxproj.filters b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus_demo.vcxproj.filters index 2eb113ac..6d11541c 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus_demo.vcxproj.filters +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus_demo.vcxproj.filters @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_api.vcxproj b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_api.vcxproj index 7cae131b..f6d5cf39 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_api.vcxproj +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_api.vcxproj @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_api.vcxproj.filters b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_api.vcxproj.filters index 383d19f7..122ef383 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_api.vcxproj.filters +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_api.vcxproj.filters @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_decode.vcxproj b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_decode.vcxproj index df01dca1..3405295c 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_decode.vcxproj +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_decode.vcxproj @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_decode.vcxproj.filters b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_decode.vcxproj.filters index 3036a4e7..8ac286dc 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_decode.vcxproj.filters +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_decode.vcxproj.filters @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_encode.vcxproj b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_encode.vcxproj index 405efee9..36925926 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_encode.vcxproj +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_encode.vcxproj @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_encode.vcxproj.filters b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_encode.vcxproj.filters index 4ed3bb9e..f6a405b7 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_encode.vcxproj.filters +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_encode.vcxproj.filters @@ -1,4 +1,4 @@ - + From a8174de88b67d622f1eeebe2a742dfebe35eec93 Mon Sep 17 00:00:00 2001 From: wizmo2 Date: Wed, 26 Apr 2023 15:40:48 -0400 Subject: [PATCH 09/19] Update sdkconfig revert sdkconfig --- sdkconfig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sdkconfig b/sdkconfig index c2dcc4b6..6182580f 100644 --- a/sdkconfig +++ b/sdkconfig @@ -210,7 +210,6 @@ CONFIG_SPKFAULT_GPIO=-1 CONFIG_BAT_CHANNEL=-1 CONFIG_LED_GREEN_GPIO=-1 CONFIG_LED_RED_GPIO=-1 -CONFIG_SET_GPIO="" # end of Target # @@ -286,6 +285,7 @@ CONFIG_CSPOT_SINK=y # Various I/O # CONFIG_I2C_CONFIG="" +CONFIG_SET_GPIO="" CONFIG_ROTARY_ENCODER="" # end of Various I/O @@ -942,11 +942,11 @@ 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 is not set +CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y # CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE is not set -CONFIG_FREERTOS_ASSERT_DISABLE=y +# CONFIG_FREERTOS_ASSERT_DISABLE is not set CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 -CONFIG_FREERTOS_ISR_STACKSIZE=2096 +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 @@ -1119,7 +1119,7 @@ CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1 CONFIG_LWIP_SNTP_UPDATE_DELAY=3600000 # end of SNTP -CONFIG_LWIP_ESP_LWIP_ASSERT=y +# CONFIG_LWIP_ESP_LWIP_ASSERT is not set # # Hooks From 290f4b5cb4ad5df427c362d8d8af131f99185152 Mon Sep 17 00:00:00 2001 From: Wizmo2 Date: Sun, 30 Apr 2023 16:05:58 +0100 Subject: [PATCH 10/19] fix white-space --- components/squeezelite/displayer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/squeezelite/displayer.c b/components/squeezelite/displayer.c index ad8870f8..e2cf1bf6 100644 --- a/components/squeezelite/displayer.c +++ b/components/squeezelite/displayer.c @@ -1278,8 +1278,8 @@ static void ledv_handler( u8_t *data, int len) { led_visu.style = pkt->style; led_visu.max = pkt->bright; - led_vu_clear(); - if (led_visu.mode) { + led_vu_clear(); + if (led_visu.mode) { if (led_visu.mode == VISU_SPECTRUM) { led_visu.n = (led_visu.config < MAX_BARS) ? led_visu.config : MAX_BARS; spectrum_limits(led_visu.bars, 0, led_visu.n, 0, 0.25); From 959cbe0c38db26c43d4be1e844dbde86aee3a6eb Mon Sep 17 00:00:00 2001 From: Wizmo2 Date: Mon, 1 May 2023 09:45:10 +0100 Subject: [PATCH 11/19] plugin change to turn leds off on power down --- plugin/SqueezeESP32.zip | Bin 19514 -> 19538 bytes plugin/SqueezeESP32/Player.pm | 3 +++ plugin/SqueezeESP32/RgbLed.pm | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/plugin/SqueezeESP32.zip b/plugin/SqueezeESP32.zip index 79b7f39b9ef5a73613bdb1920e04f5570d198dd3..3098d6ac7d530bed50909023d7b6e9ee5ddf47a3 100644 GIT binary patch delta 7231 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 diff --git a/plugin/SqueezeESP32/Player.pm b/plugin/SqueezeESP32/Player.pm index 00dd167d..f21bf38c 100644 --- a/plugin/SqueezeESP32/Player.pm +++ b/plugin/SqueezeESP32/Player.pm @@ -150,6 +150,9 @@ sub power { $client->update_artwork(1); } else { $client->clear_artwork(1); + if ($client->hasLED) { + Plugins::SqueezeESP32::RgbLed::updateLED($client, 0); + } } return $res; diff --git a/plugin/SqueezeESP32/RgbLed.pm b/plugin/SqueezeESP32/RgbLed.pm index 99167516..a04c3741 100644 --- a/plugin/SqueezeESP32/RgbLed.pm +++ b/plugin/SqueezeESP32/RgbLed.pm @@ -70,12 +70,13 @@ sub ledVisualizerNModes { sub updateLED { my $client = shift; + my $on = shift || 1; my $cprefs = $prefs->client($client); my $visu = $cprefs->get('led_visualizer') || 0; my $bright = $cprefs->get('led_brightness') || 20; - $visu = 0 if ($visu < 0 || $visu > ledVisualizerNModes || !(Slim::Player::Source::playmode($client) eq 'play')); + $visu = 0 if ($visu < 0 || $visu > ledVisualizerNModes || !(Slim::Player::Source::playmode($client) eq 'play') || !$on); my $modes = ledVisualizerModes; my $params = $modes->[$visu]{'params'}; my $data = pack('CCC', $params->[0], $params->[1], $bright); From ac7136c71c92925c1c0b9e38ab0b6b6e9ab7779a Mon Sep 17 00:00:00 2001 From: Wizmo2 Date: Sun, 11 Jun 2023 09:05:13 -0400 Subject: [PATCH 12/19] rr --- .gitignore | 2 ++ sdkconfig | 78 +++++++++++++++--------------------------------------- 2 files changed, 24 insertions(+), 56 deletions(-) diff --git a/.gitignore b/.gitignore index caa347ce..84852a3a 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ components/wifi-manager/UML-State-Machine-in-C envfile.txt artifacts web-installer +sdkconfig +.gitignore \ No newline at end of file diff --git a/sdkconfig b/sdkconfig index c2dcc4b6..1978a94f 100644 --- a/sdkconfig +++ b/sdkconfig @@ -146,13 +146,6 @@ CONFIG_DEFAULT_AP_MAX_CONNECTIONS=4 CONFIG_DEFAULT_AP_BEACON_INTERVAL=100 # end of WiFi Options -# -# Ethernet Options -# -CONFIG_ETH_NODRIVER=y -# CONFIG_ETH_LAN8720 is not set -# CONFIG_ETH_DM9051 is not set -# CONFIG_ETH_W5500 is not set CONFIG_ETH_PHY_RST_IO=-1 CONFIG_ETH_MDC_IO=-1 CONFIG_ETH_MDIO_IO=-1 @@ -163,7 +156,6 @@ 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 # end of Network Manager Configuration CONFIG_LWIP_MAX_SOCKETS=20 @@ -181,46 +173,48 @@ CONFIG_LOGGING_DECODE="info" CONFIG_LOGGING_OUTPUT="info" # end of Logging +CONFIG_JACK_LOCKED=y +CONFIG_BAT_LOCKED=y +CONFIG_I2C_LOCKED=y +CONFIG_LED_LOCKED=y +CONFIG_SPKFAULT_LOCKED=y CONFIG_MUTE_GPIO_LEVEL=0 +CONFIG_TARGET_LOCKED=y # # Target # -# CONFIG_SQUEEZEAMP is not set +CONFIG_SQUEEZEAMP=y # CONFIG_MUSE is not set -CONFIG_BASIC_I2C_BT=y +# CONFIG_BASIC_I2C_BT is not set # 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_PROJECT_NAME="SqueezeAMP" +CONFIG_FW_PLATFORM_NAME="SqueezeAmp" +CONFIG_DAC_CONFIG="model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14:0" +CONFIG_SPDIF_CONFIG="bck=33,ws=25,do=15" CONFIG_GPIO_EXP_CONFIG="" CONFIG_SPI_CONFIG="" CONFIG_DISPLAY_CONFIG="" CONFIG_ETH_CONFIG="" CONFIG_DAC_CONTROLSET="" CONFIG_AUDIO_CONTROLS="" -CONFIG_BAT_CONFIG="" +CONFIG_BAT_CONFIG="channel=7,scale=20.24,atten=0" CONFIG_TARGET="" CONFIG_AMP_GPIO=-1 -CONFIG_JACK_GPIO=-1 -CONFIG_SPKFAULT_GPIO=-1 -CONFIG_BAT_CHANNEL=-1 -CONFIG_LED_GREEN_GPIO=-1 -CONFIG_LED_RED_GPIO=-1 -CONFIG_SET_GPIO="" +CONFIG_JACK_GPIO=34 +CONFIG_SPKFAULT_GPIO=2 +CONFIG_BAT_CHANNEL=7 +CONFIG_LED_GREEN_GPIO=12 +CONFIG_LED_RED_GPIO=13 +CONFIG_SET_GPIO="0=ir" # end of Target # # Audio settings # -# -# DAC settings -# - # # I2S settings # @@ -239,16 +233,10 @@ 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 @@ -289,31 +277,9 @@ CONFIG_I2C_CONFIG="" CONFIG_ROTARY_ENCODER="" # end of Various I/O -# -# LED configuration -# -# end of LED configuration - -# -# Audio JACK -# -# end of Audio JACK - -# -# Amplifier -# -# end of Amplifier - -# -# Speaker Fault -# -# end of Speaker Fault - -# -# Battery measure -# -# end of Battery measure - +CONFIG_LED_RED_GPIO_LEVEL=0 +CONFIG_JACK_GPIO_LEVEL=0 +CONFIG_SPKFAULT_GPIO_LEVEL=0 CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -C 30 -W" # end of Squeezelite-ESP32 From b602d944406838c6e877086551a8dfeec3f54cba Mon Sep 17 00:00:00 2001 From: wizmo2 Date: Mon, 12 Jun 2023 18:11:32 -0400 Subject: [PATCH 13/19] Revert .gitignore Manually revert origin version --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index 84852a3a..caa347ce 100644 --- a/.gitignore +++ b/.gitignore @@ -18,5 +18,3 @@ components/wifi-manager/UML-State-Machine-in-C envfile.txt artifacts web-installer -sdkconfig -.gitignore \ No newline at end of file From bc759c06ef816a7fb0fdda9c34365ed5e802b125 Mon Sep 17 00:00:00 2001 From: wizmo2 Date: Mon, 12 Jun 2023 18:12:46 -0400 Subject: [PATCH 14/19] Revert sdkconfig Manually revert origin version --- sdkconfig | 66 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 16 deletions(-) diff --git a/sdkconfig b/sdkconfig index d5ef5e24..6182580f 100644 --- a/sdkconfig +++ b/sdkconfig @@ -146,6 +146,13 @@ CONFIG_DEFAULT_AP_MAX_CONNECTIONS=4 CONFIG_DEFAULT_AP_BEACON_INTERVAL=100 # end of WiFi Options +# +# Ethernet Options +# +CONFIG_ETH_NODRIVER=y +# CONFIG_ETH_LAN8720 is not set +# CONFIG_ETH_DM9051 is not set +# CONFIG_ETH_W5500 is not set CONFIG_ETH_PHY_RST_IO=-1 CONFIG_ETH_MDC_IO=-1 CONFIG_ETH_MDIO_IO=-1 @@ -156,6 +163,7 @@ 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 # end of Network Manager Configuration CONFIG_LWIP_MAX_SOCKETS=20 @@ -173,34 +181,28 @@ CONFIG_LOGGING_DECODE="info" CONFIG_LOGGING_OUTPUT="info" # end of Logging -CONFIG_JACK_LOCKED=y -CONFIG_BAT_LOCKED=y -CONFIG_I2C_LOCKED=y -CONFIG_LED_LOCKED=y -CONFIG_SPKFAULT_LOCKED=y CONFIG_MUTE_GPIO_LEVEL=0 -CONFIG_TARGET_LOCKED=y # # Target # -CONFIG_SQUEEZEAMP=y +# CONFIG_SQUEEZEAMP is not set # CONFIG_MUSE is not set -# CONFIG_BASIC_I2C_BT 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="SqueezeAMP" -CONFIG_FW_PLATFORM_NAME="SqueezeAmp" -CONFIG_DAC_CONFIG="model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14:0" -CONFIG_SPDIF_CONFIG="bck=33,ws=25,do=15" +CONFIG_PROJECT_NAME="Squeezelite-ESP32" +CONFIG_FW_PLATFORM_NAME="ESP32" +CONFIG_DAC_CONFIG="" +CONFIG_SPDIF_CONFIG="" CONFIG_GPIO_EXP_CONFIG="" CONFIG_SPI_CONFIG="" CONFIG_DISPLAY_CONFIG="" CONFIG_ETH_CONFIG="" CONFIG_DAC_CONTROLSET="" CONFIG_AUDIO_CONTROLS="" -CONFIG_BAT_CONFIG="channel=7,scale=20.24,atten=0" +CONFIG_BAT_CONFIG="" CONFIG_TARGET="" CONFIG_AMP_GPIO=-1 CONFIG_JACK_GPIO=-1 @@ -214,6 +216,10 @@ CONFIG_LED_RED_GPIO=-1 # Audio settings # +# +# DAC settings +# + # # I2S settings # @@ -232,10 +238,16 @@ 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 @@ -277,9 +289,31 @@ CONFIG_SET_GPIO="" CONFIG_ROTARY_ENCODER="" # end of Various I/O -CONFIG_LED_RED_GPIO_LEVEL=0 -CONFIG_JACK_GPIO_LEVEL=0 -CONFIG_SPKFAULT_GPIO_LEVEL=0 +# +# LED configuration +# +# end of LED configuration + +# +# Audio JACK +# +# end of Audio JACK + +# +# Amplifier +# +# end of Amplifier + +# +# Speaker Fault +# +# end of Speaker Fault + +# +# Battery measure +# +# end of Battery measure + CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -C 30 -W" # end of Squeezelite-ESP32 From 5458d4bf0159c01a5d5e59e5bc59f9fd3f486aad Mon Sep 17 00:00:00 2001 From: Wizmo2 Date: Mon, 12 Jun 2023 18:24:12 -0400 Subject: [PATCH 15/19] resolve conflict issue --- main/CMakeLists.txt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 598bf9be..6ce37e02 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,7 +1,4 @@ idf_component_register(SRC_DIRS . PRIV_REQUIRES _override esp_common wifi-manager pthread squeezelite-ota platform_console telnet display targets led_strip - LDFRAGMENTS "linker.lf" - ) -#get_target_property(ill ${COMPONENT_LIB} INTERFACE_LINK_LIBRARIES) -#message("${COMPONENT_LIB} INTERFACE_LINK_LIBRARIES = ${ill}") - + LDFRAGMENTS "linker.lf" + ) \ No newline at end of file From 0f89f8336a5a35a63ce87be91c30124ed2f8d5cb Mon Sep 17 00:00:00 2001 From: Wizmo2 Date: Sat, 29 Jul 2023 11:36:30 -0400 Subject: [PATCH 16/19] resolve buffer issues --- components/services/accessors.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/services/accessors.c b/components/services/accessors.c index 55416bcb..ba52c7db 100644 --- a/components/services/accessors.c +++ b/components/services/accessors.c @@ -323,8 +323,7 @@ esp_err_t config_ledvu_set(ledvu_struct_t * config){ int buffer_size=512; esp_err_t err=ESP_OK; char * config_buffer=malloc_init_external(buffer_size); - char * config_buffer2=malloc_init_external(buffer_size); - if(config_buffer && config_buffer2) { + if(config_buffer) { snprintf(config_buffer,buffer_size,"%s,length=%i,gpio=%i",config->type, config->length, config->gpio); log_send_messaging(MESSAGING_INFO,"Updating ledvu configuration to %s",config_buffer); err = config_set_value(NVS_TYPE_STR, "led_vu_config", config_buffer); @@ -335,6 +334,7 @@ esp_err_t config_ledvu_set(ledvu_struct_t * config){ else { err = ESP_ERR_NO_MEM; } + FREE_AND_NULL(config_buffer); return err; } From a67ec65713cb1584da887ca7fb0973f3155e12b7 Mon Sep 17 00:00:00 2001 From: Wizmo2 Date: Sat, 12 Aug 2023 11:05:48 +0100 Subject: [PATCH 17/19] marge cspot to master --- .../cspot/bell/external/cJSON/.editorconfig | 23 ---- .../cspot/bell/external/cJSON/.gitattributes | 11 -- .../external/cJSON/.github/CONTRIBUTING.md | 54 ---------- .../external/cJSON/.github/workflows/CI.yml | 102 ------------------ .../cspot/bell/external/cJSON/.gitignore | 20 ---- .../cspot/bell/external/cJSON/.travis.yml | 28 ----- .../bell/external/cJSON/fuzzing/.gitignore | 1 - .../tests/json-patch-tests/.editorconfig | 10 -- .../cJSON/tests/json-patch-tests/.gitignore | 4 - .../external/cJSON/tests/unity/.gitattributes | 30 ------ .../external/cJSON/tests/unity/.gitignore | 9 -- .../external/cJSON/tests/unity/.travis.yml | 29 ----- .../cspot/bell/external/opus/.gitattributes | 10 -- .../cspot/bell/external/opus/.gitignore | 90 ---------------- .../cspot/bell/external/opus/update_version | 65 ----------- .../cspot/bell/external/opus/win32/.gitignore | 26 ----- 16 files changed, 512 deletions(-) delete mode 100644 components/spotify/cspot/bell/external/cJSON/.editorconfig delete mode 100644 components/spotify/cspot/bell/external/cJSON/.gitattributes delete mode 100644 components/spotify/cspot/bell/external/cJSON/.github/CONTRIBUTING.md delete mode 100644 components/spotify/cspot/bell/external/cJSON/.github/workflows/CI.yml delete mode 100644 components/spotify/cspot/bell/external/cJSON/.gitignore delete mode 100644 components/spotify/cspot/bell/external/cJSON/.travis.yml delete mode 100644 components/spotify/cspot/bell/external/cJSON/fuzzing/.gitignore delete mode 100644 components/spotify/cspot/bell/external/cJSON/tests/json-patch-tests/.editorconfig delete mode 100644 components/spotify/cspot/bell/external/cJSON/tests/json-patch-tests/.gitignore delete mode 100644 components/spotify/cspot/bell/external/cJSON/tests/unity/.gitattributes delete mode 100644 components/spotify/cspot/bell/external/cJSON/tests/unity/.gitignore delete mode 100644 components/spotify/cspot/bell/external/cJSON/tests/unity/.travis.yml delete mode 100644 components/spotify/cspot/bell/external/opus/.gitattributes delete mode 100644 components/spotify/cspot/bell/external/opus/.gitignore delete mode 100644 components/spotify/cspot/bell/external/opus/update_version delete mode 100644 components/spotify/cspot/bell/external/opus/win32/.gitignore diff --git a/components/spotify/cspot/bell/external/cJSON/.editorconfig b/components/spotify/cspot/bell/external/cJSON/.editorconfig deleted file mode 100644 index befb2c20..00000000 --- a/components/spotify/cspot/bell/external/cJSON/.editorconfig +++ /dev/null @@ -1,23 +0,0 @@ -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/external/cJSON/.gitattributes b/components/spotify/cspot/bell/external/cJSON/.gitattributes deleted file mode 100644 index 883895fb..00000000 --- a/components/spotify/cspot/bell/external/cJSON/.gitattributes +++ /dev/null @@ -1,11 +0,0 @@ -* 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/external/cJSON/.github/CONTRIBUTING.md b/components/spotify/cspot/bell/external/cJSON/.github/CONTRIBUTING.md deleted file mode 100644 index 83d2e599..00000000 --- a/components/spotify/cspot/bell/external/cJSON/.github/CONTRIBUTING.md +++ /dev/null @@ -1,54 +0,0 @@ -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/external/cJSON/.github/workflows/CI.yml b/components/spotify/cspot/bell/external/cJSON/.github/workflows/CI.yml deleted file mode 100644 index dc9d17c6..00000000 --- a/components/spotify/cspot/bell/external/cJSON/.github/workflows/CI.yml +++ /dev/null @@ -1,102 +0,0 @@ -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/external/cJSON/.gitignore b/components/spotify/cspot/bell/external/cJSON/.gitignore deleted file mode 100644 index b3c8d6b4..00000000 --- a/components/spotify/cspot/bell/external/cJSON/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -.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/external/cJSON/.travis.yml b/components/spotify/cspot/bell/external/cJSON/.travis.yml deleted file mode 100644 index e7ff744e..00000000 --- a/components/spotify/cspot/bell/external/cJSON/.travis.yml +++ /dev/null @@ -1,28 +0,0 @@ -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/external/cJSON/fuzzing/.gitignore b/components/spotify/cspot/bell/external/cJSON/fuzzing/.gitignore deleted file mode 100644 index c82a2620..00000000 --- a/components/spotify/cspot/bell/external/cJSON/fuzzing/.gitignore +++ /dev/null @@ -1 +0,0 @@ -afl-build diff --git a/components/spotify/cspot/bell/external/cJSON/tests/json-patch-tests/.editorconfig b/components/spotify/cspot/bell/external/cJSON/tests/json-patch-tests/.editorconfig deleted file mode 100644 index dc79da48..00000000 --- a/components/spotify/cspot/bell/external/cJSON/tests/json-patch-tests/.editorconfig +++ /dev/null @@ -1,10 +0,0 @@ -# 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/external/cJSON/tests/json-patch-tests/.gitignore b/components/spotify/cspot/bell/external/cJSON/tests/json-patch-tests/.gitignore deleted file mode 100644 index cd2b23de..00000000 --- a/components/spotify/cspot/bell/external/cJSON/tests/json-patch-tests/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*~ -\#* -!.editorconfig -!.gitignore diff --git a/components/spotify/cspot/bell/external/cJSON/tests/unity/.gitattributes b/components/spotify/cspot/bell/external/cJSON/tests/unity/.gitattributes deleted file mode 100644 index ad952260..00000000 --- a/components/spotify/cspot/bell/external/cJSON/tests/unity/.gitattributes +++ /dev/null @@ -1,30 +0,0 @@ -* 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/external/cJSON/tests/unity/.gitignore b/components/spotify/cspot/bell/external/cJSON/tests/unity/.gitignore deleted file mode 100644 index a383c3cc..00000000 --- a/components/spotify/cspot/bell/external/cJSON/tests/unity/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -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/external/cJSON/tests/unity/.travis.yml b/components/spotify/cspot/bell/external/cJSON/tests/unity/.travis.yml deleted file mode 100644 index bd165b1e..00000000 --- a/components/spotify/cspot/bell/external/cJSON/tests/unity/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -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/external/opus/.gitattributes b/components/spotify/cspot/bell/external/opus/.gitattributes deleted file mode 100644 index 649c8100..00000000 --- a/components/spotify/cspot/bell/external/opus/.gitattributes +++ /dev/null @@ -1,10 +0,0 @@ -.gitignore export-ignore -.gitattributes export-ignore - -update_version export-ignore - -*.bat eol=crlf -*.sln eol=crlf -*.vcxproj eol=crlf -*.vcxproj.filters eol=crlf -common.props eol=crlf diff --git a/components/spotify/cspot/bell/external/opus/.gitignore b/components/spotify/cspot/bell/external/opus/.gitignore deleted file mode 100644 index 837619f9..00000000 --- a/components/spotify/cspot/bell/external/opus/.gitignore +++ /dev/null @@ -1,90 +0,0 @@ -Doxyfile -Makefile -Makefile.in -TAGS -aclocal.m4 -autom4te.cache -*.kdevelop.pcs -*.kdevses -compile -config.guess -config.h -config.h.in -config.log -config.status -config.sub -configure -depcomp -INSTALL -install-sh -.deps -.libs -.dirstamp -*.a -*.exe -*.la -*-gnu.S -testcelt -libtool -ltmain.sh -missing -m4/libtool.m4 -m4/ltoptions.m4 -m4/ltsugar.m4 -m4/ltversion.m4 -m4/lt~obsolete.m4 -opus_compare -opus_demo -repacketizer_demo -stamp-h1 -test-driver -trivial_example -*.sw* -*.o -*.lo -*.pc -*.tar.gz -*~ -tests/*test -tests/test_opus_api -tests/test_opus_decode -tests/test_opus_encode -tests/test_opus_padding -tests/test_opus_projection -celt/arm/armopts.s -celt/dump_modes/dump_modes -celt/tests/test_unit_cwrs32 -celt/tests/test_unit_dft -celt/tests/test_unit_entropy -celt/tests/test_unit_laplace -celt/tests/test_unit_mathops -celt/tests/test_unit_mdct -celt/tests/test_unit_rotation -celt/tests/test_unit_types -doc/doxygen_sqlite3.db -doc/doxygen-build.stamp -doc/html -doc/latex -doc/man -package_version -version.h -celt/Debug -celt/Release -celt/x64 -silk/Debug -silk/Release -silk/x64 -silk/fixed/Debug -silk/fixed/Release -silk/fixed/x64 -silk/float/Debug -silk/float/Release -silk/float/x64 -silk/tests/test_unit_LPC_inv_pred_gain -src/Debug -src/Release -src/x64 -/*[Bb]uild*/ -.vs/ -.vscode/ -CMakeSettings.json diff --git a/components/spotify/cspot/bell/external/opus/update_version b/components/spotify/cspot/bell/external/opus/update_version deleted file mode 100644 index a9999918..00000000 --- a/components/spotify/cspot/bell/external/opus/update_version +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/bash - -# Creates and updates the package_version information used by configure.ac -# (or other makefiles). When run inside a git repository it will use the -# version information that can be queried from it unless AUTO_UPDATE is set -# to 'no'. If no version is currently known it will be set to 'unknown'. -# -# If called with the argument 'release', the PACKAGE_VERSION will be updated -# even if AUTO_UPDATE=no, but the value of AUTO_UPDATE shall be preserved. -# This is used to force a version update whenever `make dist` is run. -# -# The exit status is 1 if package_version is not modified, else 0 is returned. -# -# This script should NOT be included in distributed tarballs, because if a -# parent directory contains a git repository we do not want to accidentally -# retrieve the version information from it instead. Tarballs should ship -# with only the package_version file. -# -# Ron , 2012. - -SRCDIR=$(dirname $0) - -if [ -e "$SRCDIR/package_version" ]; then - . "$SRCDIR/package_version" -fi - -if [ "$AUTO_UPDATE" = no ]; then - [ "$1" = release ] || exit 1 -else - AUTO_UPDATE=yes -fi - -# We run `git status` before describe here to ensure that we don't get a false -# -dirty from files that have been touched but are not actually altered in the -# working dir. -GIT_VERSION=$(cd "$SRCDIR" && git status > /dev/null 2>&1 \ - && git describe --tags --match 'v*' --dirty 2> /dev/null) -GIT_VERSION=${GIT_VERSION#v} - -if [ -n "$GIT_VERSION" ]; then - - [ "$GIT_VERSION" != "$PACKAGE_VERSION" ] || exit 1 - PACKAGE_VERSION="$GIT_VERSION" - -elif [ -z "$PACKAGE_VERSION" ]; then - # No current package_version and no git ... - # We really shouldn't ever get here, because this script should only be - # included in the git repository, and should usually be export-ignored. - PACKAGE_VERSION="unknown" -else - exit 1 -fi - -cat > "$SRCDIR/package_version" <<-EOF - # Automatically generated by update_version. - # This file may be sourced into a shell script or makefile. - - # Set this to 'no' if you do not wish the version information - # to be checked and updated for every build. Most people will - # never want to change this, it is an option for developers - # making frequent changes that they know will not be released. - AUTO_UPDATE=$AUTO_UPDATE - - PACKAGE_VERSION="$PACKAGE_VERSION" -EOF diff --git a/components/spotify/cspot/bell/external/opus/win32/.gitignore b/components/spotify/cspot/bell/external/opus/win32/.gitignore deleted file mode 100644 index c17feab7..00000000 --- a/components/spotify/cspot/bell/external/opus/win32/.gitignore +++ /dev/null @@ -1,26 +0,0 @@ -# Visual Studio ignores -[Dd]ebug/ -[Dd]ebugDLL/ -[Dd]ebugDLL_fixed/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleaseDLL/ -[Rr]eleaseDLL_fixed/ -[Rr]eleases/ -*.manifest -*.lastbuildstate -*.lib -*.log -*.idb -*.ipdb -*.ilk -*.iobj -*.obj -*.opensdf -*.pdb -*.sdf -*.suo -*.tlog -*.vcxproj.user -*.vc.db -*.vc.opendb From 943d50fe5f932754a55979cb64dcdcc9308b2656 Mon Sep 17 00:00:00 2001 From: Wizmo2 Date: Sat, 12 Aug 2023 12:41:32 +0100 Subject: [PATCH 18/19] merge copy of cspot try2 --- .../alac/convert-utility/ALACconvert.sln | 2 +- .../cspot/bell/external/cJSON/.editorconfig | 23 ++++ .../cspot/bell/external/cJSON/.gitattributes | 11 ++ .../external/cJSON/.github/CONTRIBUTING.md | 54 ++++++++++ .../external/cJSON/.github/workflows/CI.yml | 102 ++++++++++++++++++ .../cspot/bell/external/cJSON/.gitignore | 20 ++++ .../cspot/bell/external/cJSON/.travis.yml | 28 +++++ .../bell/external/cJSON/fuzzing/.gitignore | 1 + .../tests/json-patch-tests/.editorconfig | 10 ++ .../cJSON/tests/json-patch-tests/.gitignore | 4 + .../external/cJSON/tests/unity/.gitattributes | 30 ++++++ .../external/cJSON/tests/unity/.gitignore | 9 ++ .../external/cJSON/tests/unity/.travis.yml | 29 +++++ .../cspot/bell/external/mdnssvc/mdnssvc.sln | 2 +- .../cspot/bell/external/opus/.gitattributes | 10 ++ .../cspot/bell/external/opus/.gitignore | 90 ++++++++++++++++ .../cspot/bell/external/opus/update_version | 65 +++++++++++ .../cspot/bell/external/opus/win32/.gitignore | 26 +++++ .../external/opus/win32/VS2015/common.props | 2 +- .../bell/external/opus/win32/VS2015/opus.sln | 2 +- .../external/opus/win32/VS2015/opus.vcxproj | 2 +- .../opus/win32/VS2015/opus.vcxproj.filters | 2 +- .../opus/win32/VS2015/opus_demo.vcxproj | 2 +- .../win32/VS2015/opus_demo.vcxproj.filters | 2 +- .../opus/win32/VS2015/test_opus_api.vcxproj | 2 +- .../VS2015/test_opus_api.vcxproj.filters | 2 +- .../win32/VS2015/test_opus_decode.vcxproj | 2 +- .../VS2015/test_opus_decode.vcxproj.filters | 2 +- .../win32/VS2015/test_opus_encode.vcxproj | 2 +- .../VS2015/test_opus_encode.vcxproj.filters | 2 +- 30 files changed, 526 insertions(+), 14 deletions(-) create mode 100644 components/spotify/cspot/bell/external/cJSON/.editorconfig create mode 100644 components/spotify/cspot/bell/external/cJSON/.gitattributes create mode 100644 components/spotify/cspot/bell/external/cJSON/.github/CONTRIBUTING.md create mode 100644 components/spotify/cspot/bell/external/cJSON/.github/workflows/CI.yml create mode 100644 components/spotify/cspot/bell/external/cJSON/.gitignore create mode 100644 components/spotify/cspot/bell/external/cJSON/.travis.yml create mode 100644 components/spotify/cspot/bell/external/cJSON/fuzzing/.gitignore create mode 100644 components/spotify/cspot/bell/external/cJSON/tests/json-patch-tests/.editorconfig create mode 100644 components/spotify/cspot/bell/external/cJSON/tests/json-patch-tests/.gitignore create mode 100644 components/spotify/cspot/bell/external/cJSON/tests/unity/.gitattributes create mode 100644 components/spotify/cspot/bell/external/cJSON/tests/unity/.gitignore create mode 100644 components/spotify/cspot/bell/external/cJSON/tests/unity/.travis.yml create mode 100644 components/spotify/cspot/bell/external/opus/.gitattributes create mode 100644 components/spotify/cspot/bell/external/opus/.gitignore create mode 100644 components/spotify/cspot/bell/external/opus/update_version create mode 100644 components/spotify/cspot/bell/external/opus/win32/.gitignore diff --git a/components/spotify/cspot/bell/external/alac/convert-utility/ALACconvert.sln b/components/spotify/cspot/bell/external/alac/convert-utility/ALACconvert.sln index d1f1c3f0..4221168f 100644 --- a/components/spotify/cspot/bell/external/alac/convert-utility/ALACconvert.sln +++ b/components/spotify/cspot/bell/external/alac/convert-utility/ALACconvert.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 9.00 # Visual Studio 2005 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "alacConvert", "alacConvert.vcproj", "{B9B08911-AF77-447E-A4DD-692AFFA0E346}" diff --git a/components/spotify/cspot/bell/external/cJSON/.editorconfig b/components/spotify/cspot/bell/external/cJSON/.editorconfig new file mode 100644 index 00000000..befb2c20 --- /dev/null +++ b/components/spotify/cspot/bell/external/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/external/cJSON/.gitattributes b/components/spotify/cspot/bell/external/cJSON/.gitattributes new file mode 100644 index 00000000..883895fb --- /dev/null +++ b/components/spotify/cspot/bell/external/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/external/cJSON/.github/CONTRIBUTING.md b/components/spotify/cspot/bell/external/cJSON/.github/CONTRIBUTING.md new file mode 100644 index 00000000..83d2e599 --- /dev/null +++ b/components/spotify/cspot/bell/external/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/external/cJSON/.github/workflows/CI.yml b/components/spotify/cspot/bell/external/cJSON/.github/workflows/CI.yml new file mode 100644 index 00000000..dc9d17c6 --- /dev/null +++ b/components/spotify/cspot/bell/external/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/external/cJSON/.gitignore b/components/spotify/cspot/bell/external/cJSON/.gitignore new file mode 100644 index 00000000..b3c8d6b4 --- /dev/null +++ b/components/spotify/cspot/bell/external/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/external/cJSON/.travis.yml b/components/spotify/cspot/bell/external/cJSON/.travis.yml new file mode 100644 index 00000000..e7ff744e --- /dev/null +++ b/components/spotify/cspot/bell/external/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/external/cJSON/fuzzing/.gitignore b/components/spotify/cspot/bell/external/cJSON/fuzzing/.gitignore new file mode 100644 index 00000000..c82a2620 --- /dev/null +++ b/components/spotify/cspot/bell/external/cJSON/fuzzing/.gitignore @@ -0,0 +1 @@ +afl-build diff --git a/components/spotify/cspot/bell/external/cJSON/tests/json-patch-tests/.editorconfig b/components/spotify/cspot/bell/external/cJSON/tests/json-patch-tests/.editorconfig new file mode 100644 index 00000000..dc79da48 --- /dev/null +++ b/components/spotify/cspot/bell/external/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/external/cJSON/tests/json-patch-tests/.gitignore b/components/spotify/cspot/bell/external/cJSON/tests/json-patch-tests/.gitignore new file mode 100644 index 00000000..cd2b23de --- /dev/null +++ b/components/spotify/cspot/bell/external/cJSON/tests/json-patch-tests/.gitignore @@ -0,0 +1,4 @@ +*~ +\#* +!.editorconfig +!.gitignore diff --git a/components/spotify/cspot/bell/external/cJSON/tests/unity/.gitattributes b/components/spotify/cspot/bell/external/cJSON/tests/unity/.gitattributes new file mode 100644 index 00000000..ad952260 --- /dev/null +++ b/components/spotify/cspot/bell/external/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/external/cJSON/tests/unity/.gitignore b/components/spotify/cspot/bell/external/cJSON/tests/unity/.gitignore new file mode 100644 index 00000000..a383c3cc --- /dev/null +++ b/components/spotify/cspot/bell/external/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/external/cJSON/tests/unity/.travis.yml b/components/spotify/cspot/bell/external/cJSON/tests/unity/.travis.yml new file mode 100644 index 00000000..bd165b1e --- /dev/null +++ b/components/spotify/cspot/bell/external/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/external/mdnssvc/mdnssvc.sln b/components/spotify/cspot/bell/external/mdnssvc/mdnssvc.sln index b64f33a9..e623a7f4 100644 --- a/components/spotify/cspot/bell/external/mdnssvc/mdnssvc.sln +++ b/components/spotify/cspot/bell/external/mdnssvc/mdnssvc.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.3.32901.215 diff --git a/components/spotify/cspot/bell/external/opus/.gitattributes b/components/spotify/cspot/bell/external/opus/.gitattributes new file mode 100644 index 00000000..649c8100 --- /dev/null +++ b/components/spotify/cspot/bell/external/opus/.gitattributes @@ -0,0 +1,10 @@ +.gitignore export-ignore +.gitattributes export-ignore + +update_version export-ignore + +*.bat eol=crlf +*.sln eol=crlf +*.vcxproj eol=crlf +*.vcxproj.filters eol=crlf +common.props eol=crlf diff --git a/components/spotify/cspot/bell/external/opus/.gitignore b/components/spotify/cspot/bell/external/opus/.gitignore new file mode 100644 index 00000000..837619f9 --- /dev/null +++ b/components/spotify/cspot/bell/external/opus/.gitignore @@ -0,0 +1,90 @@ +Doxyfile +Makefile +Makefile.in +TAGS +aclocal.m4 +autom4te.cache +*.kdevelop.pcs +*.kdevses +compile +config.guess +config.h +config.h.in +config.log +config.status +config.sub +configure +depcomp +INSTALL +install-sh +.deps +.libs +.dirstamp +*.a +*.exe +*.la +*-gnu.S +testcelt +libtool +ltmain.sh +missing +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 +opus_compare +opus_demo +repacketizer_demo +stamp-h1 +test-driver +trivial_example +*.sw* +*.o +*.lo +*.pc +*.tar.gz +*~ +tests/*test +tests/test_opus_api +tests/test_opus_decode +tests/test_opus_encode +tests/test_opus_padding +tests/test_opus_projection +celt/arm/armopts.s +celt/dump_modes/dump_modes +celt/tests/test_unit_cwrs32 +celt/tests/test_unit_dft +celt/tests/test_unit_entropy +celt/tests/test_unit_laplace +celt/tests/test_unit_mathops +celt/tests/test_unit_mdct +celt/tests/test_unit_rotation +celt/tests/test_unit_types +doc/doxygen_sqlite3.db +doc/doxygen-build.stamp +doc/html +doc/latex +doc/man +package_version +version.h +celt/Debug +celt/Release +celt/x64 +silk/Debug +silk/Release +silk/x64 +silk/fixed/Debug +silk/fixed/Release +silk/fixed/x64 +silk/float/Debug +silk/float/Release +silk/float/x64 +silk/tests/test_unit_LPC_inv_pred_gain +src/Debug +src/Release +src/x64 +/*[Bb]uild*/ +.vs/ +.vscode/ +CMakeSettings.json diff --git a/components/spotify/cspot/bell/external/opus/update_version b/components/spotify/cspot/bell/external/opus/update_version new file mode 100644 index 00000000..a9999918 --- /dev/null +++ b/components/spotify/cspot/bell/external/opus/update_version @@ -0,0 +1,65 @@ +#!/bin/bash + +# Creates and updates the package_version information used by configure.ac +# (or other makefiles). When run inside a git repository it will use the +# version information that can be queried from it unless AUTO_UPDATE is set +# to 'no'. If no version is currently known it will be set to 'unknown'. +# +# If called with the argument 'release', the PACKAGE_VERSION will be updated +# even if AUTO_UPDATE=no, but the value of AUTO_UPDATE shall be preserved. +# This is used to force a version update whenever `make dist` is run. +# +# The exit status is 1 if package_version is not modified, else 0 is returned. +# +# This script should NOT be included in distributed tarballs, because if a +# parent directory contains a git repository we do not want to accidentally +# retrieve the version information from it instead. Tarballs should ship +# with only the package_version file. +# +# Ron , 2012. + +SRCDIR=$(dirname $0) + +if [ -e "$SRCDIR/package_version" ]; then + . "$SRCDIR/package_version" +fi + +if [ "$AUTO_UPDATE" = no ]; then + [ "$1" = release ] || exit 1 +else + AUTO_UPDATE=yes +fi + +# We run `git status` before describe here to ensure that we don't get a false +# -dirty from files that have been touched but are not actually altered in the +# working dir. +GIT_VERSION=$(cd "$SRCDIR" && git status > /dev/null 2>&1 \ + && git describe --tags --match 'v*' --dirty 2> /dev/null) +GIT_VERSION=${GIT_VERSION#v} + +if [ -n "$GIT_VERSION" ]; then + + [ "$GIT_VERSION" != "$PACKAGE_VERSION" ] || exit 1 + PACKAGE_VERSION="$GIT_VERSION" + +elif [ -z "$PACKAGE_VERSION" ]; then + # No current package_version and no git ... + # We really shouldn't ever get here, because this script should only be + # included in the git repository, and should usually be export-ignored. + PACKAGE_VERSION="unknown" +else + exit 1 +fi + +cat > "$SRCDIR/package_version" <<-EOF + # Automatically generated by update_version. + # This file may be sourced into a shell script or makefile. + + # Set this to 'no' if you do not wish the version information + # to be checked and updated for every build. Most people will + # never want to change this, it is an option for developers + # making frequent changes that they know will not be released. + AUTO_UPDATE=$AUTO_UPDATE + + PACKAGE_VERSION="$PACKAGE_VERSION" +EOF diff --git a/components/spotify/cspot/bell/external/opus/win32/.gitignore b/components/spotify/cspot/bell/external/opus/win32/.gitignore new file mode 100644 index 00000000..c17feab7 --- /dev/null +++ b/components/spotify/cspot/bell/external/opus/win32/.gitignore @@ -0,0 +1,26 @@ +# Visual Studio ignores +[Dd]ebug/ +[Dd]ebugDLL/ +[Dd]ebugDLL_fixed/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleaseDLL/ +[Rr]eleaseDLL_fixed/ +[Rr]eleases/ +*.manifest +*.lastbuildstate +*.lib +*.log +*.idb +*.ipdb +*.ilk +*.iobj +*.obj +*.opensdf +*.pdb +*.sdf +*.suo +*.tlog +*.vcxproj.user +*.vc.db +*.vc.opendb diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/common.props b/components/spotify/cspot/bell/external/opus/win32/VS2015/common.props index 725c0b60..f6e920b6 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/common.props +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/common.props @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.sln b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.sln index 3a603b08..7b678e7f 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.sln +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 VisualStudioVersion = 14.0.25420.1 diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.vcxproj b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.vcxproj index beefe2f2..34b1233c 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.vcxproj +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.vcxproj @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.vcxproj.filters b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.vcxproj.filters index cd746f6e..8c61d6a0 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.vcxproj.filters +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus.vcxproj.filters @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus_demo.vcxproj b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus_demo.vcxproj index 1bfa9383..f4344e53 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus_demo.vcxproj +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus_demo.vcxproj @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus_demo.vcxproj.filters b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus_demo.vcxproj.filters index 6d11541c..2eb113ac 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/opus_demo.vcxproj.filters +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/opus_demo.vcxproj.filters @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_api.vcxproj b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_api.vcxproj index f6d5cf39..7cae131b 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_api.vcxproj +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_api.vcxproj @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_api.vcxproj.filters b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_api.vcxproj.filters index 122ef383..383d19f7 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_api.vcxproj.filters +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_api.vcxproj.filters @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_decode.vcxproj b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_decode.vcxproj index 3405295c..df01dca1 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_decode.vcxproj +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_decode.vcxproj @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_decode.vcxproj.filters b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_decode.vcxproj.filters index 8ac286dc..3036a4e7 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_decode.vcxproj.filters +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_decode.vcxproj.filters @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_encode.vcxproj b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_encode.vcxproj index 36925926..405efee9 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_encode.vcxproj +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_encode.vcxproj @@ -1,4 +1,4 @@ - + diff --git a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_encode.vcxproj.filters b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_encode.vcxproj.filters index f6a405b7..4ed3bb9e 100644 --- a/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_encode.vcxproj.filters +++ b/components/spotify/cspot/bell/external/opus/win32/VS2015/test_opus_encode.vcxproj.filters @@ -1,4 +1,4 @@ - + From 5b6c0fb06b30ea16551e7acba99456a69f403d89 Mon Sep 17 00:00:00 2001 From: Wizmo2 Date: Sun, 13 Aug 2023 15:34:46 +0100 Subject: [PATCH 19/19] move task to ext ram --- components/led_strip/led_strip.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/components/led_strip/led_strip.c b/components/led_strip/led_strip.c index 92b1ba3d..90ba5c5c 100644 --- a/components/led_strip/led_strip.c +++ b/components/led_strip/led_strip.c @@ -288,7 +288,9 @@ static bool led_strip_init_rmt(struct led_strip_t *led_strip) bool led_strip_init(struct led_strip_t *led_strip) { - TaskHandle_t led_strip_task_handle; + static EXT_RAM_ATTR TaskHandle_t task_created; + static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4))); + static EXT_RAM_ATTR StackType_t xStack[LED_STRIP_TASK_SIZE] __attribute__ ((aligned (4))); if ((led_strip == NULL) || (led_strip->rmt_channel >= RMT_CHANNEL_MAX) || @@ -313,12 +315,12 @@ bool led_strip_init(struct led_strip_t *led_strip) } xSemaphoreGive(led_strip->access_semaphore); - BaseType_t task_created = xTaskCreate(led_strip_task, + task_created = xTaskCreateStatic(led_strip_task, "led_strip_task", LED_STRIP_TASK_SIZE, led_strip, LED_STRIP_TASK_PRIORITY, - &led_strip_task_handle); + xStack, &xTaskBuffer); if (!task_created) { return false;