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 <karl_osterseher@gmx.at>
This commit is contained in:
Karl Osterseher
2025-02-22 20:58:38 +01:00
committed by Karl Osterseher
Unverified
parent 3cc8572caf
commit 4eb01805fe
13 changed files with 123 additions and 123 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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;

View File

@@ -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);

View File

@@ -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")

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -1,6 +1,10 @@
#ifndef _WIFI_INTERFACE_H_
#define _WIFI_INTERFACE_H_
#include <stdbool.h>
#include "esp_netif.h"
// use wifi provisioning
#define ENABLE_WIFI_PROVISIONING CONFIG_ENABLE_WIFI_PROVISIONING

View File

@@ -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 || \

View File

@@ -4,9 +4,10 @@
Must be taken over/merge with wifi provision
*/
#include "wifi_interface.h"
#include <string.h> // 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 <string.h> // for memcpy
#include "wifi_provisioning.h"
#endif

View File

@@ -8,20 +8,19 @@
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "ui_http_server.h"
#include <string.h>
#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";

View File

@@ -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