From 4eb01805fe3746cd27506a47e7bc154c6dda2875 Mon Sep 17 00:00:00 2001 From: Karl Osterseher Date: Sat, 22 Feb 2025 20:58:38 +0100 Subject: [PATCH] fix low volume due to wrong init of audioDAC_data add support for mixing stereo to mono through dsp_processor (fixes #106) add sdkconfigs to github action workflow suppress warnings from flac git submodule remove unused variable and address some compiler warnings remove some dead code Signed-off-by: Karl Osterseher --- .github/workflows/build.yml | 11 +- components/dsp_processor/CMakeLists.txt | 2 +- components/dsp_processor/Kconfig.projbuild | 6 ++ components/dsp_processor/dsp_processor.c | 94 +++++++++++++--- .../dsp_processor/include/dsp_processor.h | 2 +- components/flac/CMakeLists.txt | 5 +- components/net_functions/net_functions.c | 6 +- components/network_interface/eth_interface.c | 3 +- .../include/wifi_interface.h | 4 + .../network_interface/network_interface.c | 1 - components/network_interface/wifi_interface.c | 5 +- components/ui_http_server/ui_http_server.c | 7 +- main/main.c | 100 +++--------------- 13 files changed, 123 insertions(+), 123 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ea8c478..10f2ca0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,9 +4,10 @@ on: push jobs: build: - + strategy: + matrix: + sdkconfig: [sdkconfig_TAS5805M, sdkconfig_PCM5102A, sdkconfig_lyrat_v4.3, sdkconfig_adau1961] runs-on: ubuntu-latest - steps: - name: Checkout repo uses: actions/checkout@v2 @@ -18,10 +19,10 @@ jobs: uses: espressif/esp-idf-ci-action@v1.1.0 with: # Relative path under $GITHUB_WORKSPACE to place the repository - #path: # optional, default is + #path: # optional, default is # Version of ESP-IDF docker image to use - esp_idf_version: v5.1.1 # optional, default is latest + esp_idf_version: v5.1.5 # optional, default is latest # ESP32 variant to build for target: esp32 # optional, default is esp32 # Command to run inside the docker container (default: builds the project) - command: idf.py build -DSDKCONFIG=./sdkconfig_lyrat_v4.3 # optional, default is idf.py build + command: idf.py build -DSDKCONFIG=${{ matrix.sdkconfig }} # optional, default is idf.py build diff --git a/components/dsp_processor/CMakeLists.txt b/components/dsp_processor/CMakeLists.txt index 6caabe8..d835336 100644 --- a/components/dsp_processor/CMakeLists.txt +++ b/components/dsp_processor/CMakeLists.txt @@ -1,5 +1,5 @@ set(COMPONENT_REQUIRES) -set(COMPONENT_PRIV_REQUIRES esp-dsp) +set(COMPONENT_PRIV_REQUIRES esp-dsp lightsnapcast) list(APPEND COMPONENT_ADD_INCLUDEDIRS ./include) set(COMPONENT_SRCS ./dsp_processor.c) diff --git a/components/dsp_processor/Kconfig.projbuild b/components/dsp_processor/Kconfig.projbuild index 3198e74..74972fb 100644 --- a/components/dsp_processor/Kconfig.projbuild +++ b/components/dsp_processor/Kconfig.projbuild @@ -7,6 +7,12 @@ menu "ESP32 DSP processor config" help enable audio filtering before queueing it to player component + config SNAPCLIENT_MIX_LR_TO_MONO + bool "mix stereo audio to mono" + default false + help + mix stereo audio (left and right channel) to mono and play it on the left AND the right channel + choice SNAPCLIENT_DSP_FLOW prompt "DSP flow" default SNAPCLIENT_DSP_FLOW_STEREO diff --git a/components/dsp_processor/dsp_processor.c b/components/dsp_processor/dsp_processor.c index 293ba7c..9d4647e 100644 --- a/components/dsp_processor/dsp_processor.c +++ b/components/dsp_processor/dsp_processor.c @@ -7,12 +7,12 @@ #include "freertos/FreeRTOS.h" #if CONFIG_USE_DSP_PROCESSOR +#include "dsp_processor.h" #include "dsps_biquad.h" #include "dsps_biquad_gen.h" #include "esp_log.h" #include "freertos/queue.h" - -#include "dsp_processor.h" +#include "player.h" #ifdef CONFIG_USE_BIQUAD_ASM #define BIQUAD dsps_biquad_f32_ae32 @@ -112,7 +112,9 @@ void dsp_processor_init(void) { break; } - default: { break; } + default: { + break; + } } ESP_LOGI(TAG, "%s: init done", __func__); @@ -203,12 +205,36 @@ static int32_t dsp_processor_gen_filter(ptype_t *filter, uint32_t cnt) { /** * */ -int dsp_processor_worker(char *audio, size_t chunk_size, uint32_t samplerate) { - int16_t len = chunk_size / 4; +int dsp_processor_worker(void *p_pcmChnk, const void *p_scSet) { + const snapcastSetting_t *scSet = (const snapcastSetting_t *)p_scSet; + pcm_chunk_message_t *pcmChnk = (pcm_chunk_message_t *)p_pcmChnk; + uint32_t samplerate = scSet->sr; + + if (!pcmChnk || !pcmChnk->fragment->payload) { + return -1; + } + + int bits = scSet->bits; + int ch = scSet->ch; + + if (bits == 0) { + bits = 16; + } + + if (ch == 0) { + ch = 2; + } + + if (samplerate == 0) { + samplerate = 44100; + } + + int16_t len = pcmChnk->fragment->size / ((bits / 8) * ch); int16_t valint; uint16_t i; // volatile needed to ensure 32 bit access - volatile uint32_t *audio_tmp = (volatile uint32_t *)audio; + volatile uint32_t *audio_tmp = + (volatile uint32_t *)(pcmChnk->fragment->payload); dspFlows_t dspFlow; // check if we need to update filters @@ -332,7 +358,9 @@ int dsp_processor_worker(char *audio, size_t chunk_size, uint32_t samplerate) { break; } - default: { break; } + default: { + break; + } } dsp_processor_gen_filter(filter, cnt); @@ -360,6 +388,28 @@ int dsp_processor_worker(char *audio, size_t chunk_size, uint32_t samplerate) { return -1; } +#if CONFIG_SNAPCLIENT_MIX_LR_TO_MONO + if (ch == 2) { + for (int k = 0; k < len; k += DSP_PROCESSOR_LEN) { + volatile uint32_t *tmp = (uint32_t *)(&audio_tmp[k]); + uint32_t max = DSP_PROCESSOR_LEN; + uint32_t test = len - k; + + if (test < DSP_PROCESSOR_LEN) { + max = test; + } + + for (i = 0; i < max; i++) { + int16_t channel0 = (int16_t)((tmp[i] & 0xFFFF0000) >> 16); + int16_t channel1 = (int16_t)(tmp[i] & 0x0000FFFF); + int16_t mixMono = (float)channel0 / 2.0 + (float)channel1 / 2.0; + + tmp[i] = ((uint32_t)mixMono << 16) | ((uint32_t)mixMono & 0x0000FFFF); + } + } + } +#endif + switch (dspFlow) { case dspfEQBassTreble: { for (int k = 0; k < len; k += DSP_PROCESSOR_LEN) { @@ -411,6 +461,7 @@ int dsp_processor_worker(char *audio, size_t chunk_size, uint32_t samplerate) { } case dspfStereo: { +#if SNAPCAST_USE_SOFT_VOL for (int k = 0; k < len; k += DSP_PROCESSOR_LEN) { volatile uint32_t *tmp = (uint32_t *)(&audio_tmp[k]); uint32_t max = DSP_PROCESSOR_LEN; @@ -432,6 +483,7 @@ int dsp_processor_worker(char *audio, size_t chunk_size, uint32_t samplerate) { } } } +#endif break; } @@ -448,8 +500,11 @@ int dsp_processor_worker(char *audio, size_t chunk_size, uint32_t samplerate) { // channel 0 for (i = 0; i < max; i++) { - sbuffer0[i] = dynamic_vol * 0.5 * - ((float)((int16_t)(tmp[i] & 0xFFFF))) / INT16_MAX; + sbuffer0[i] = + 0.5 * ((float)((int16_t)(tmp[i] & 0xFFFF))) / INT16_MAX; +#if SNAPCAST_USE_SOFT_VOL + sbuffer0[i] *= dynamic_vol; +#endif } BIQUAD(sbuffer0, sbufout0, max, filter[0].coeffs, filter[0].w); @@ -460,9 +515,12 @@ int dsp_processor_worker(char *audio, size_t chunk_size, uint32_t samplerate) { // channel 1 for (i = 0; i < max; i++) { - sbuffer0[i] = dynamic_vol * 0.5 * + sbuffer0[i] = 0.5 * ((float)((int16_t)((tmp[i] & 0xFFFF0000) >> 16))) / INT16_MAX; +#if SNAPCAST_USE_SOFT_VOL + sbuffer0[i] *= dynamic_vol; +#endif } BIQUAD(sbuffer0, sbufout0, max, filter[1].coeffs, filter[1].w); @@ -487,8 +545,11 @@ int dsp_processor_worker(char *audio, size_t chunk_size, uint32_t samplerate) { // Process audio ch0 LOW PASS FILTER for (i = 0; i < max; i++) { - sbuffer0[i] = dynamic_vol * 0.5 * - ((float)((int16_t)(tmp[i] & 0xFFFF))) / INT16_MAX; + sbuffer0[i] = + 0.5 * ((float)((int16_t)(tmp[i] & 0xFFFF))) / INT16_MAX; +#if SNAPCAST_USE_SOFT_VOL + sbuffer0[i] *= dynamic_vol; +#endif } BIQUAD(sbuffer0, sbufout0, max, filter[0].coeffs, filter[0].w); BIQUAD(sbufout0, sbuffer0, max, filter[1].coeffs, filter[1].w); @@ -500,9 +561,12 @@ int dsp_processor_worker(char *audio, size_t chunk_size, uint32_t samplerate) { // Process audio ch1 HIGH PASS FILTER for (i = 0; i < max; i++) { - sbuffer0[i] = dynamic_vol * 0.5 * + sbuffer0[i] = 0.5 * ((float)((int16_t)((tmp[i] & 0xFFFF0000) >> 16))) / INT16_MAX; +#if SNAPCAST_USE_SOFT_VOL + sbuffer0[i] *= dynamic_vol; +#endif } BIQUAD(sbuffer0, sbufout0, max, filter[2].coeffs, filter[2].w); BIQUAD(sbufout0, sbuffer0, max, filter[3].coeffs, filter[3].w); @@ -602,7 +666,9 @@ int dsp_processor_worker(char *audio, size_t chunk_size, uint32_t samplerate) { "dspfFunkyHonda, not implemented yet, using stereo instead"); } break; - default: { } break; } + default: { + } break; + } free(sbuffer0); sbuffer0 = NULL; diff --git a/components/dsp_processor/include/dsp_processor.h b/components/dsp_processor/include/dsp_processor.h index 6a2d09e..33f0a1f 100644 --- a/components/dsp_processor/include/dsp_processor.h +++ b/components/dsp_processor/include/dsp_processor.h @@ -64,7 +64,7 @@ typedef struct pnode { void dsp_processor_init(void); void dsp_processor_uninit(void); -int dsp_processor_worker(char *audio, size_t chunk_size, uint32_t samplerate); +int dsp_processor_worker(void *pcmChnk, const void *scSet); esp_err_t dsp_processor_update_filter_params(filterParams_t *params); void dsp_processor_set_volome(double volume); diff --git a/components/flac/CMakeLists.txt b/components/flac/CMakeLists.txt index 49ecaba..c6a8313 100644 --- a/components/flac/CMakeLists.txt +++ b/components/flac/CMakeLists.txt @@ -21,6 +21,7 @@ idf_component_register(SRCS "${srcs}" #set_source_files_properties(opus/silk/quant_LTP_gains.c PROPERTIES COMPILE_FLAGS -Wno-maybe-uninitialized) #target_compile_definitions(${COMPONENT_TARGET} PRIVATE "-DHAVE_CONFIG_H") -set_source_files_properties(flac/src/libFLAC/bitwriter.c PROPERTIES COMPILE_FLAGS -Wno-error=format) -set_source_files_properties(flac/src/libFLAC/bitreader.c PROPERTIES COMPILE_FLAGS -Wno-error=format) +set_source_files_properties(flac/src/libFLAC/bitwriter.c PROPERTIES COMPILE_FLAGS -Wno-format) +set_source_files_properties(flac/src/libFLAC/bitreader.c PROPERTIES COMPILE_FLAGS -Wno-format) +set_source_files_properties(flac/src/libFLAC/stream_decoder.c PROPERTIES COMPILE_FLAGS -Wno-incompatible-pointer-types) target_compile_definitions(${COMPONENT_TARGET} PRIVATE "-DHAVE_CONFIG_H") diff --git a/components/net_functions/net_functions.c b/components/net_functions/net_functions.c index 98f6680..21727b6 100644 --- a/components/net_functions/net_functions.c +++ b/components/net_functions/net_functions.c @@ -22,7 +22,6 @@ static const char *TAG = "NETF"; extern EventGroupHandle_t s_wifi_event_group; -static const char *if_str[] = {"STA", "AP", "ETH", "MAX"}; static const char *ip_protocol_str[] = {"V4", "V6", "MAX"}; void net_mdns_register(const char *clientname) { @@ -34,9 +33,8 @@ void net_mdns_register(const char *clientname) { } void mdns_print_results(const mdns_result_t *results) { - mdns_result_t *r = results; + mdns_result_t *r = (mdns_result_t *)results; mdns_ip_addr_t *a = NULL; - int i = 1, t; while (r) { ESP_LOGI(TAG, "Interface: %s", esp_netif_get_desc(r->esp_netif)); @@ -49,7 +47,7 @@ void mdns_print_results(const mdns_result_t *results) { } if (r->txt_count) { ESP_LOGI(TAG, " TXT : [%u] ", r->txt_count); - for (t = 0; t < r->txt_count; t++) { + for (int t = 0; t < r->txt_count; t++) { ESP_LOGI(TAG, "%s=%s; ", r->txt[t].key, r->txt[t].value); } } diff --git a/components/network_interface/eth_interface.c b/components/network_interface/eth_interface.c index eefe727..bf4f476 100644 --- a/components/network_interface/eth_interface.c +++ b/components/network_interface/eth_interface.c @@ -476,7 +476,6 @@ static void eth_on_got_ipv6(void *arg, esp_event_base_t event_base, void eth_start(void) { // Initialize Ethernet driver esp_eth_handle_t *eth_handles; - esp_netif_t *eth_netif; if (!connIpSemaphoreHandle) { connIpSemaphoreHandle = xSemaphoreCreateMutex(); @@ -486,6 +485,8 @@ void eth_start(void) { #if CONFIG_SNAPCLIENT_USE_INTERNAL_ETHERNET || \ CONFIG_SNAPCLIENT_USE_SPI_ETHERNET + esp_netif_t *eth_netif; + // Create instance(s) of esp-netif for Ethernet(s) if (eth_port_cnt == 1) { // Use ESP_NETIF_DEFAULT_ETH when just one Ethernet interface is used and diff --git a/components/network_interface/include/wifi_interface.h b/components/network_interface/include/wifi_interface.h index 65fa142..37c3cc5 100644 --- a/components/network_interface/include/wifi_interface.h +++ b/components/network_interface/include/wifi_interface.h @@ -1,6 +1,10 @@ #ifndef _WIFI_INTERFACE_H_ #define _WIFI_INTERFACE_H_ +#include + +#include "esp_netif.h" + // use wifi provisioning #define ENABLE_WIFI_PROVISIONING CONFIG_ENABLE_WIFI_PROVISIONING diff --git a/components/network_interface/network_interface.c b/components/network_interface/network_interface.c index 6aa8066..2223b97 100644 --- a/components/network_interface/network_interface.c +++ b/components/network_interface/network_interface.c @@ -22,7 +22,6 @@ #include "freertos/FreeRTOS.h" #include "freertos/event_groups.h" #include "freertos/task.h" -#include "include/eth_interface.h" #include "sdkconfig.h" #if CONFIG_SNAPCLIENT_USE_INTERNAL_ETHERNET || \ diff --git a/components/network_interface/wifi_interface.c b/components/network_interface/wifi_interface.c index 96f3826..3aad361 100644 --- a/components/network_interface/wifi_interface.c +++ b/components/network_interface/wifi_interface.c @@ -4,9 +4,10 @@ Must be taken over/merge with wifi provision */ - #include "wifi_interface.h" +#include // for memcpy + #include "esp_event.h" #include "esp_log.h" #include "esp_mac.h" @@ -20,8 +21,6 @@ #include "nvs_flash.h" #if ENABLE_WIFI_PROVISIONING -#include // for memcpy - #include "wifi_provisioning.h" #endif diff --git a/components/ui_http_server/ui_http_server.c b/components/ui_http_server/ui_http_server.c index b9ba0a4..899b616 100644 --- a/components/ui_http_server/ui_http_server.c +++ b/components/ui_http_server/ui_http_server.c @@ -8,20 +8,19 @@ CONDITIONS OF ANY KIND, either express or implied. */ +#include "ui_http_server.h" + #include +#include "dsp_processor.h" #include "esp_err.h" #include "esp_http_server.h" #include "esp_log.h" #include "esp_spiffs.h" #include "esp_vfs.h" -// #include "esp_wifi.h" - -#include "dsp_processor.h" #include "freertos/FreeRTOS.h" #include "freertos/queue.h" #include "freertos/task.h" -#include "ui_http_server.h" static const char *TAG = "UI_HTTP"; diff --git a/main/main.c b/main/main.c index cc0b590..9d8f9f4 100644 --- a/main/main.c +++ b/main/main.c @@ -137,9 +137,9 @@ typedef struct audioDACdata_s { int volume; } audioDACdata_t; -audioDACdata_t audioDAC_data; +static audioDACdata_t audioDAC_data; static QueueHandle_t audioDACQHdl = NULL; -SemaphoreHandle_t audioDACSemaphore = NULL; +static SemaphoreHandle_t audioDACSemaphore = NULL; typedef struct decoderData_s { uint32_t type; // should be SNAPCAST_MESSAGE_CODEC_HEADER @@ -415,7 +415,7 @@ void init_snapcast(QueueHandle_t audioQHdl) { audioDACQHdl = audioQHdl; audioDACSemaphore = xSemaphoreCreateMutex(); audioDAC_data.mute = true; - audioDAC_data.volume = 100; + audioDAC_data.volume = -1; } /** @@ -442,9 +442,6 @@ void audio_set_volume(int volume) { xSemaphoreGive(audioDACSemaphore); } -#define INTERFACE_ETH 0 -#define INTERFACE_STA 1 - /** * */ @@ -611,6 +608,8 @@ static void http_get_task(void *pvParameters) { netif = re->esp_netif; break; } + + // TODO: fall back to IPv4 if no IPv6 was available #else if (a->addr.type == IPADDR_TYPE_V4) { netif = re->esp_netif; @@ -630,7 +629,6 @@ static void http_get_task(void *pvParameters) { } ip_addr_copy(remote_ip, re->addr->addr); - // remote_ip.type = a->addr.type; remotePort = r->port; mdns_query_results_free(r); @@ -650,76 +648,6 @@ static void http_get_task(void *pvParameters) { ipaddr_ntoa(&remote_ip), remotePort); #endif - // ip_addr_t ipAddr; - // if (remote_ip.type == IPADDR_TYPE_V4) { - // esp_netif_ip_info_t ip_info; - // memset(&ip_info, 0, sizeof(esp_netif_ip_info_t)); - // - // // #if CONFIG_SNAPCLIENT_USE_INTERNAL_ETHERNET || - // // CONFIG_SNAPCLIENT_USE_SPI_ETHERNET - // // if (network_is_netif_up( - // // network_get_netif_from_desc(NETWORK_INTERFACE_DESC_ETH)) ) - // { - // // esp_netif_get_ip_info(eth_netif, &ip_info); - // // } - // // else - // // #endif - // // { - // // if (network_is_netif_up( netif ) ) - //// { - // esp_netif_get_ip_info(netif, &ip_info); - //// } - // // } - // - // ip_addr_t _ipAddr = IPADDR4_INIT(ip_info.ip.addr); - // - // ipAddr = _ipAddr; - // - // char str[INET_ADDRSTRLEN]; - // inet_ntop(AF_INET, &(ipAddr.u_addr.ip4.addr), str, INET_ADDRSTRLEN); - // - // ESP_LOGI(TAG, "IP4 %s", str); - // } else if (remote_ip.type == IPADDR_TYPE_V6) { - // esp_netif_ip6_info_t ip_info; - // memset(&ip_info, 0, sizeof(esp_netif_ip6_info_t)); - // - // // #if CONFIG_SNAPCLIENT_USE_INTERNAL_ETHERNET || - // // CONFIG_SNAPCLIENT_USE_SPI_ETHERNET - // // if (network_is_netif_up( - // // network_get_netif_from_desc(NETWORK_INTERFACE_DESC_ETH)) ) - // { - // // esp_netif_get_ip6_linklocal(eth_netif, &ip_info.ip); - // // } - // // else - // // #endif - // // { - // // if (network_is_netif_up( - // // network_get_netif_from_desc(NETWORK_INTERFACE_DESC_STA)) - // ) { esp_netif_get_ip6_linklocal(netif, &ip_info.ip); - // // } - // // } - // - // ip_addr_t _ipAddr = IPADDR6_INIT(ip_info.ip.addr[0], - // ip_info.ip.addr[1], - // ip_info.ip.addr[2], - // ip_info.ip.addr[3]); - // ipAddr = _ipAddr; - // - // char str[INET6_ADDRSTRLEN]; - // // inet_ntop(AF_INET6, &(ipAddr.u_addr.ip6.addr), str, - // INET6_ADDRSTRLEN); inet_ntop(AF_INET6, &(ip_info.ip.addr), str, - // INET6_ADDRSTRLEN); - // - // // ESP_LOGI(TAG, "Got IPv6 event: address: " IPV6STR, - // // IPV62STR(ip_info->ip)); - // - // // ESP_LOGI(TAG, "IP6 %s", str); - // } else { - // ESP_LOGI(TAG, "wrong remote IP address type %u", remote_ip.type); - // - // continue; - // } - if (remote_ip.type == IPADDR_TYPE_V4) { lwipNetconn = netconn_new(NETCONN_TCP); @@ -787,9 +715,9 @@ static void http_get_task(void *pvParameters) { } uint8_t base_mac[6]; - // Get MAC address for WiFi station #if CONFIG_SNAPCLIENT_USE_INTERNAL_ETHERNET || \ CONFIG_SNAPCLIENT_USE_SPI_ETHERNET + // Get MAC address for Eth Interface char eth_mac_address[18]; esp_read_mac(base_mac, ESP_MAC_ETH); @@ -797,6 +725,7 @@ static void http_get_task(void *pvParameters) { base_mac[1], base_mac[2], base_mac[3], base_mac[4], base_mac[5]); ESP_LOGI(TAG, "eth mac: %s", eth_mac_address); #endif + // Get MAC address for WiFi station char mac_address[18]; esp_read_mac(base_mac, ESP_MAC_WIFI_STA); sprintf(mac_address, "%02X:%02X:%02X:%02X:%02X:%02X", base_mac[0], @@ -1525,9 +1454,8 @@ static void http_get_task(void *pvParameters) { #if CONFIG_USE_DSP_PROCESSOR if (new_pcmChunk->fragment->payload) { - dsp_processor_worker( - new_pcmChunk->fragment->payload, - new_pcmChunk->fragment->size, scSet.sr); + dsp_processor_worker((void *)new_pcmChunk, + (void *)&scSet); } #endif @@ -1636,9 +1564,8 @@ static void http_get_task(void *pvParameters) { #if CONFIG_USE_DSP_PROCESSOR if (new_pcmChunk->fragment->payload) { - dsp_processor_worker( - new_pcmChunk->fragment->payload, - new_pcmChunk->fragment->size, scSet.sr); + dsp_processor_worker((void *)new_pcmChunk, + (void *)&scSet); } #endif @@ -1698,9 +1625,8 @@ static void http_get_task(void *pvParameters) { #if CONFIG_USE_DSP_PROCESSOR if ((pcmData) && (pcmData->fragment->payload)) { - dsp_processor_worker(pcmData->fragment->payload, - pcmData->fragment->size, - scSet.sr); + dsp_processor_worker((void *)pcmData, + (void *)&scSet); } #endif