diff --git a/components/dsp_processor/dsp_processor.c b/components/dsp_processor/dsp_processor.c index 8aa6f51..052e38a 100644 --- a/components/dsp_processor/dsp_processor.c +++ b/components/dsp_processor/dsp_processor.c @@ -37,13 +37,15 @@ static uint32_t currentChunkDurationMs = 0; static ptype_t bq[8]; +static double dynamic_vol = 1.0; + int dsp_processor (char *audio, size_t chunk_size, dspFlows_t dspFlow) { - double dynamic_vol = 1.0; int16_t len = chunk_size / 4; int16_t valint; uint16_t i; + volatile uint32_t *audio_tmp = (uint32_t *)audio; //volatile needed to ensure 32 bit access if ((sbuffer0 == NULL) || (sbufout0 == NULL) || (sbuftmp0 == NULL)) { @@ -57,6 +59,17 @@ dsp_processor (char *audio, size_t chunk_size, dspFlows_t dspFlow) { case dspfStereo: { + //set volume + if (dynamic_vol != 1.0) + { + for (i = 0; i < len; i++) + { + audio_tmp[i] = ((uint32_t) (dynamic_vol + * ((float)((int16_t)((audio_tmp[i] & 0xFFFF0000) >> 16)))) << 16) + + (uint32_t) (dynamic_vol + * ((float)((int16_t)(audio_tmp[i] & 0xFFFF)))); + } + } } break; @@ -66,8 +79,7 @@ dsp_processor (char *audio, size_t chunk_size, dspFlows_t dspFlow) for (i = 0; i < len; i++) { sbuffer0[i] = dynamic_vol * 0.5 - * ((float)((int16_t) (audio[i * 4 + 1] << 8) - + audio[i * 4 + 0])) + * ((float)((int16_t)(audio_tmp[i] & 0xFFFF))) / 32768; } BIQUAD (sbuffer0, sbufout0, len, bq[6].coeffs, bq[6].w); @@ -75,17 +87,14 @@ dsp_processor (char *audio, size_t chunk_size, dspFlows_t dspFlow) for (i = 0; i < len; i++) { valint = (int16_t) (sbufout0[i] * 32768); - - audio[i * 4 + 0] = (valint & 0x00ff); - audio[i * 4 + 1] = ((valint & 0xff00) >> 8); + audio_tmp[i] = (audio_tmp[i]&0xFFFF0000) + (uint32_t)valint; } // channel 1 for (i = 0; i < len; i++) { sbuffer0[i] = dynamic_vol * 0.5 - * ((float)((int16_t) (audio[i * 4 + 3] << 8) - + audio[i * 4 + 2])) + * ((float)((int16_t)((audio_tmp[i] & 0xFFFF0000) >> 16))) / 32768; } BIQUAD (sbuffer0, sbufout0, len, bq[7].coeffs, bq[7].w); @@ -93,8 +102,7 @@ dsp_processor (char *audio, size_t chunk_size, dspFlows_t dspFlow) for (i = 0; i < len; i++) { valint = (int16_t) (sbufout0[i] * 32768); - audio[i * 4 + 2] = (valint & 0x00ff); - audio[i * 4 + 3] = ((valint & 0xff00) >> 8); + audio_tmp[i] = (audio_tmp[i]&0xFFFF) + ((uint32_t)valint << 16); } } break; @@ -105,8 +113,7 @@ dsp_processor (char *audio, size_t chunk_size, dspFlows_t dspFlow) for (i = 0; i < len; i++) { sbuffer0[i] = dynamic_vol * 0.5 - * ((float)((int16_t) (audio[i * 4 + 1] << 8) - + audio[i * 4 + 0])) + * ((float)((int16_t)(audio_tmp[i] & 0xFFFF))) / 32768; } BIQUAD (sbuffer0, sbuftmp0, len, bq[0].coeffs, bq[0].w); @@ -115,16 +122,14 @@ dsp_processor (char *audio, size_t chunk_size, dspFlows_t dspFlow) for (i = 0; i < len; i++) { valint = (int16_t) (sbufout0[i] * 32768); - audio[i * 4 + 0] = (valint & 0x00ff); - audio[i * 4 + 1] = ((valint & 0xff00) >> 8); + audio_tmp[i] = (audio_tmp[i]&0xFFFF0000) + (uint32_t)valint; } // Process audio ch1 HIGH PASS FILTER for (i = 0; i < len; i++) { sbuffer0[i] = dynamic_vol * 0.5 - * ((float)((int16_t) (audio[i * 4 + 3] << 8) - + audio[i * 4 + 2])) + * ((float)((int16_t)((audio_tmp[i] & 0xFFFF0000) >> 16))) / 32768; } BIQUAD (sbuffer0, sbuftmp0, len, bq[2].coeffs, bq[2].w); @@ -133,8 +138,7 @@ dsp_processor (char *audio, size_t chunk_size, dspFlows_t dspFlow) for (i = 0; i < len; i++) { valint = (int16_t) (sbufout0[i] * 32768); - audio[i * 4 + 2] = (valint & 0x00ff); - audio[i * 4 + 3] = ((valint & 0xff00) >> 8); + audio_tmp[i] = (audio_tmp[i]&0xFFFF) + ((uint32_t)valint << 16); } } break; @@ -382,4 +386,14 @@ dsp_set_xoverfreq (uint8_t freqh, uint8_t freql, uint32_t samplerate) } } } + +void +dsp_set_vol (double volume) +{ + if (volume >= 0 && volume <= 1.0) + { + ESP_LOGI (TAG, "Set volume to %f", volume); + dynamic_vol = volume; + } +} #endif diff --git a/components/dsp_processor/include/dsp_processor.h b/components/dsp_processor/include/dsp_processor.h index 5d8ee95..5865a0c 100644 --- a/components/dsp_processor/include/dsp_processor.h +++ b/components/dsp_processor/include/dsp_processor.h @@ -42,5 +42,6 @@ typedef struct pnode { void dsp_setup_flow(double freq, uint32_t samplerate, uint32_t chunkDurationMs); int dsp_processor(char *audio, size_t chunk_size, dspFlows_t dspFlow); void dsp_set_xoverfreq(uint8_t, uint8_t, uint32_t); +void dsp_set_vol(double volume); #endif /* _DSP_PROCESSOR_H_ */ diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 344c76a..e8a67e3 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -19,18 +19,36 @@ menu "Snapcast Configuration" depends on !SNAPSERVER_USE_MDNS help Port of the snapserver to connect to. - - config SNAPCLIENT_BUFF_LEN - int "snapcast protocol buffer len" - default 4000 - help - Size of the snapclient protocol messages buffer (in bytes). config SNAPCLIENT_NAME string "snapclient name" - default "esp-snapclient" + default "ESP32-Caster" help Name of the client to register the snapserver. + + config SNAPCLIENT_USE_SOFT_VOL + bool "Use software volume" + default false + depends on USE_DSP_PROCESSOR + help + Use software volume mixer instead of hardware mixer. + + choice SNAPCLIENT_DSP_FLOW + prompt "DSP flow" + default SNAPCLIENT_DSP_FLOW_STEREO + help + Select the DSP flow to use. + + config SNAPCLIENT_DSP_FLOW_STEREO + bool "Stereo flow" + + config SNAPCLIENT_DSP_FLOW_BASSBOOST + bool "Bassboost flow" + + config SNAPCLIENT_DSP_FLOW_BIAMP + bool "Bi-Amp flow" + endchoice + endmenu menu "SNTP Configuration" diff --git a/main/main.c b/main/main.c index 10cf31b..40657d6 100644 --- a/main/main.c +++ b/main/main.c @@ -101,8 +101,8 @@ static audio_board_handle_t board_handle = NULL; #define SNAPCAST_SERVER_HOST CONFIG_SNAPSERVER_HOST #define SNAPCAST_SERVER_PORT CONFIG_SNAPSERVER_PORT #endif -#define SNAPCAST_BUFF_LEN CONFIG_SNAPCLIENT_BUFF_LEN #define SNAPCAST_CLIENT_NAME CONFIG_SNAPCLIENT_NAME +#define SNAPCAST_USE_SOFT_VOL CONFIG_SNAPCLIENT_USE_SOFT_VOL /* Logging tag */ static const char *TAG = "SC"; @@ -113,7 +113,15 @@ extern char mac_address[18]; SemaphoreHandle_t timeSyncSemaphoreHandle = NULL; #if CONFIG_USE_DSP_PROCESSOR -uint8_t dspFlow = dspfStereo; // dspfBiamp; // dspfStereo; // dspfBassBoost; +#if CONFIG_SNAPCLIENT_DSP_FLOW_STEREO +dspFlows_t dspFlow = dspfStereo; // dspfBiamp; // dspfStereo; // dspfBassBoost; +#endif +#if CONFIG_SNAPCLIENT_DSP_FLOW_BASSBOOST +dspFlows_t dspFlow = dspfBassBoost; +#endif +#if CONFIG_SNAPCLIENT_DSP_FLOW_BIAMP +dspFlows_t dspFlow = dspfBiamp; +#endif #endif typedef struct flacData_s { @@ -722,7 +730,7 @@ static void http_get_task(void *pvParameters) { // init hello message hello_message.mac = mac_address; - hello_message.hostname = "ESP32-Caster"; + hello_message.hostname = SNAPCAST_CLIENT_NAME; hello_message.version = (char *)VERSION_STRING; hello_message.client_name = "libsnapcast"; hello_message.os = "esp32"; @@ -785,6 +793,7 @@ static void http_get_task(void *pvParameters) { uint32_t typedMsgLen = 0; uint32_t offset = 0; uint32_t tmpData = 0; + int flow_drain_counter = 0; #define BASE_MESSAGE_STATE 0 #define TYPED_MESSAGE_STATE 1 @@ -1405,6 +1414,20 @@ static void http_get_task(void *pvParameters) { endTime = esp_timer_get_time(); #if CONFIG_USE_DSP_PROCESSOR + if (flow_drain_counter > 0) { + flow_drain_counter--; + double dynamic_vol = ((double)scSet.volume/100 / (20 - flow_drain_counter)); + if (flow_drain_counter == 0) { +#if SNAPCAST_USE_SOFT_VOL + dynamic_vol = 0; +#else + dynamic_vol = 1; +#endif + audio_hal_set_mute(board_handle->audio_hal, + server_settings_message.muted); + } + dsp_set_vol(dynamic_vol); + } dsp_setup_flow(500, scSet.sr, scSet.chkDur_ms); dsp_processor(pcmData->fragment->payload, pcmData->fragment->size, dspFlow); @@ -1443,6 +1466,20 @@ static void http_get_task(void *pvParameters) { } #if CONFIG_USE_DSP_PROCESSOR + if (flow_drain_counter > 0) { + flow_drain_counter--; + double dynamic_vol = ((double)scSet.volume/100 / (20 - flow_drain_counter)); + if (flow_drain_counter == 0) { +#if SNAPCAST_USE_SOFT_VOL + dynamic_vol = 0; +#else + dynamic_vol = 1; +#endif + audio_hal_set_mute(board_handle->audio_hal, + server_settings_message.muted); + } + dsp_set_vol(dynamic_vol); + } dsp_setup_flow(500, scSet.sr, scSet.chkDur_ms); dsp_processor(pcmData->fragment->payload, pcmData->fragment->size, dspFlow); @@ -1982,12 +2019,32 @@ static void http_get_task(void *pvParameters) { // Volume setting using ADF HAL // abstraction if (scSet.muted != server_settings_message.muted) { +#if CONFIG_USE_DSP_PROCESSOR + if (server_settings_message.muted) { + flow_drain_counter = 20; + } + else { + flow_drain_counter = 0; + audio_hal_set_mute(board_handle->audio_hal, + server_settings_message.muted); +#if SNAPCAST_USE_SOFT_VOL + dsp_set_vol((double)server_settings_message.volume/100); +#else + dsp_set_vol(1.0); +#endif + } +#else audio_hal_set_mute(board_handle->audio_hal, server_settings_message.muted); +#endif } if (scSet.volume != server_settings_message.volume) { +#if SNAPCAST_USE_SOFT_VOL + dsp_set_vol((double)server_settings_message.volume/100); +#else audio_hal_set_volume(board_handle->audio_hal, server_settings_message.volume); +#endif } scSet.cDacLat_ms = server_settings_message.latency;