From 55053d594124cb6f431179e6859407c59ee683c3 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Wed, 30 Aug 2023 22:00:05 -0700 Subject: [PATCH] fix spdif for s3 and remove one override SPDIF on esp32 was partly incorrect due to word ordering and required i2s_hal override. This is not needed anymore as the "mistery" of SPDIF hack has been properly sorted out --- components/_override/CMakeLists.txt | 4 +- components/_override/esp32/i2s_hal.c | 275 --------------------------- components/squeezelite/equalizer.c | 18 +- components/squeezelite/output_i2s.c | 34 ++-- 4 files changed, 35 insertions(+), 296 deletions(-) delete mode 100644 components/_override/esp32/i2s_hal.c diff --git a/components/_override/CMakeLists.txt b/components/_override/CMakeLists.txt index 6c7f388e..06841099 100644 --- a/components/_override/CMakeLists.txt +++ b/components/_override/CMakeLists.txt @@ -1,6 +1,6 @@ if(IDF_TARGET STREQUAL esp32 AND IDF_VERSION_MAJOR EQUAL 4 AND IDF_VERSION_MINOR LESS 4) set(lib_dir ${build_dir}/esp-idf) - set(driver esp32/i2s.c esp32/i2s_hal.c) + set(driver esp32/i2s.c) string(REPLACE ".c" ".c.obj" driver_obj "${driver}") idf_component_register( SRCS ${driver} @@ -20,5 +20,5 @@ if(IDF_TARGET STREQUAL esp32 AND IDF_VERSION_MAJOR EQUAL 4 AND IDF_VERSION_MINOR VERBATIM ) else() - message(STATUS "==> NO I2S LIBRARY OVERRIDE, SPDIF MIGHT NOT WORK <==") + message(STATUS "==> NO OVERRIDE <==") endif() diff --git a/components/_override/esp32/i2s_hal.c b/components/_override/esp32/i2s_hal.c deleted file mode 100644 index 2cb35b0c..00000000 --- a/components/_override/esp32/i2s_hal.c +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// The HAL layer for I2S (common part) - -#include "soc/soc.h" -#include "soc/soc_caps.h" -#include "hal/i2s_hal.h" - -#define I2S_TX_PDM_FP_DEF 960 // Set to the recommended value(960) in TRM -#define I2S_RX_PDM_DSR_DEF 0 - -void i2s_hal_set_tx_mode(i2s_hal_context_t *hal, i2s_channel_t ch, i2s_bits_per_sample_t bits) -{ - if (bits <= I2S_BITS_PER_SAMPLE_16BIT) { - i2s_ll_set_tx_fifo_mod(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 0 : 1); - } else { - i2s_ll_set_tx_fifo_mod(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 2 : 3); - } - i2s_ll_set_tx_chan_mod(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 0 : 1); -#if SOC_I2S_SUPPORTS_DMA_EQUAL - i2s_ll_set_tx_dma_equal(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 0 : 1); -#endif -} - -void i2s_hal_set_rx_mode(i2s_hal_context_t *hal, i2s_channel_t ch, i2s_bits_per_sample_t bits) -{ - if (bits <= I2S_BITS_PER_SAMPLE_16BIT) { - i2s_ll_set_rx_fifo_mod(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 0 : 1); - } else { - i2s_ll_set_rx_fifo_mod(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 2 : 3); - } - i2s_ll_set_rx_chan_mod(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 0 : 1); -#if SOC_I2S_SUPPORTS_DMA_EQUAL - i2s_ll_set_rx_dma_equal(hal->dev, (ch == I2S_CHANNEL_STEREO) ? 0 : 1); -#endif -} - -void i2s_hal_set_in_link(i2s_hal_context_t *hal, uint32_t bytes_num, uint32_t addr) -{ - i2s_ll_set_in_link_addr(hal->dev, addr); - i2s_ll_set_rx_eof_num(hal->dev, bytes_num); -} - -#if SOC_I2S_SUPPORTS_PDM -void i2s_hal_tx_pdm_cfg(i2s_hal_context_t *hal, uint32_t fp, uint32_t fs) -{ - i2s_ll_tx_pdm_cfg(hal->dev, fp, fs); -} - -void i2s_hal_get_tx_pdm(i2s_hal_context_t *hal, uint32_t *fp, uint32_t *fs) -{ - i2s_ll_get_tx_pdm(hal->dev, fp, fs); -} - -void i2s_hal_rx_pdm_cfg(i2s_hal_context_t *hal, uint32_t dsr) -{ - i2s_ll_rx_pdm_cfg(hal->dev, dsr); -} - -void i2s_hal_get_rx_pdm(i2s_hal_context_t *hal, uint32_t *dsr) -{ - i2s_ll_get_rx_pdm(hal->dev, dsr); -} -#endif - -void i2s_hal_set_clk_div(i2s_hal_context_t *hal, int div_num, int div_a, int div_b, int tx_bck_div, int rx_bck_div) -{ - i2s_ll_set_clkm_div_num(hal->dev, div_num); - i2s_ll_set_clkm_div_a(hal->dev, div_a); - i2s_ll_set_clkm_div_b(hal->dev, div_b); - i2s_ll_set_tx_bck_div_num(hal->dev, tx_bck_div); - i2s_ll_set_rx_bck_div_num(hal->dev, rx_bck_div); -} - -void i2s_hal_set_tx_bits_mod(i2s_hal_context_t *hal, i2s_bits_per_sample_t bits) -{ - i2s_ll_set_tx_bits_mod(hal->dev, bits); -} - -void i2s_hal_set_rx_bits_mod(i2s_hal_context_t *hal, i2s_bits_per_sample_t bits) -{ - i2s_ll_set_rx_bits_mod(hal->dev, bits); -} - -void i2s_hal_reset(i2s_hal_context_t *hal) -{ - // Reset I2S TX/RX module first, and then, reset DMA and FIFO. - i2s_ll_reset_tx(hal->dev); - i2s_ll_reset_rx(hal->dev); - i2s_ll_reset_dma_in(hal->dev); - i2s_ll_reset_dma_out(hal->dev); - i2s_ll_reset_rx_fifo(hal->dev); - i2s_ll_reset_tx_fifo(hal->dev); -} - -void i2s_hal_start_tx(i2s_hal_context_t *hal) -{ - i2s_ll_start_out_link(hal->dev); - i2s_ll_start_tx(hal->dev); -} - -void i2s_hal_start_rx(i2s_hal_context_t *hal) -{ - i2s_ll_start_in_link(hal->dev); - i2s_ll_start_rx(hal->dev); -} - -void i2s_hal_stop_tx(i2s_hal_context_t *hal) -{ - i2s_ll_stop_out_link(hal->dev); - i2s_ll_stop_tx(hal->dev); -} - -void i2s_hal_stop_rx(i2s_hal_context_t *hal) -{ - i2s_ll_stop_in_link(hal->dev); - i2s_ll_stop_rx(hal->dev); -} - -void i2s_hal_format_config(i2s_hal_context_t *hal, const i2s_config_t *i2s_config) -{ - switch (i2s_config->communication_format) { - case I2S_COMM_FORMAT_STAND_MSB: - if (i2s_config->mode & I2S_MODE_TX) { - i2s_ll_set_tx_format_msb_align(hal->dev); - } - if (i2s_config->mode & I2S_MODE_RX) { - i2s_ll_set_rx_format_msb_align(hal->dev); - } - break; - case I2S_COMM_FORMAT_STAND_PCM_SHORT: - if (i2s_config->mode & I2S_MODE_TX) { - i2s_ll_set_tx_pcm_long(hal->dev); - } - if (i2s_config->mode & I2S_MODE_RX) { - i2s_ll_set_rx_pcm_long(hal->dev); - } - break; - case I2S_COMM_FORMAT_STAND_PCM_LONG: - if (i2s_config->mode & I2S_MODE_TX) { - i2s_ll_set_tx_pcm_short(hal->dev); - } - if (i2s_config->mode & I2S_MODE_RX) { - i2s_ll_set_rx_pcm_short(hal->dev); - } - break; - default: //I2S_COMM_FORMAT_STAND_I2S - if (i2s_config->mode & I2S_MODE_TX) { - i2s_ll_set_tx_format_philip(hal->dev); - } - if (i2s_config->mode & I2S_MODE_RX) { - i2s_ll_set_rx_format_philip(hal->dev); - } - break; - } -} - -void i2s_hal_config_param(i2s_hal_context_t *hal, const i2s_config_t *i2s_config) -{ - //reset i2s - i2s_ll_reset_tx(hal->dev); - i2s_ll_reset_rx(hal->dev); - - //reset dma - i2s_ll_reset_dma_in(hal->dev); - i2s_ll_reset_dma_out(hal->dev); - - i2s_ll_enable_dma(hal->dev); - - i2s_ll_set_lcd_en(hal->dev, 0); - i2s_ll_set_camera_en(hal->dev, 0); - - i2s_ll_set_dscr_en(hal->dev, 0); - - i2s_ll_set_tx_chan_mod(hal->dev, i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? i2s_config->channel_format : (i2s_config->channel_format >> 1)); // 0-two channel;1-right;2-left;3-righ;4-left - i2s_ll_set_tx_fifo_mod(hal->dev, i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1); // 0-right&left channel;1-one channel - i2s_ll_set_tx_mono(hal->dev, 0); - - i2s_ll_set_rx_chan_mod(hal->dev, i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? i2s_config->channel_format : (i2s_config->channel_format >> 1)); // 0-two channel;1-right;2-left;3-righ;4-left - i2s_ll_set_rx_fifo_mod(hal->dev, i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1); // 0-right&left channel;1-one channel - i2s_ll_set_rx_mono(hal->dev, 0); - - i2s_ll_set_dscr_en(hal->dev, 1); //connect dma to fifo - - i2s_ll_stop_tx(hal->dev); - i2s_ll_stop_rx(hal->dev); - - if (i2s_config->mode & I2S_MODE_TX) { - int order = i2s_config->bits_per_sample == 32 ? 0 : 1; - i2s_ll_set_tx_msb_right(hal->dev, order); - i2s_ll_set_tx_right_first(hal->dev, ~order); - - i2s_ll_set_tx_slave_mod(hal->dev, 0); // Master - i2s_ll_set_tx_fifo_mod_force_en(hal->dev, 1); - - if (i2s_config->mode & I2S_MODE_SLAVE) { - i2s_ll_set_tx_slave_mod(hal->dev, 1); //TX Slave - } - } - - if (i2s_config->mode & I2S_MODE_RX) { - i2s_ll_set_rx_msb_right(hal->dev, 0); - i2s_ll_set_rx_right_first(hal->dev, 0); - i2s_ll_set_rx_slave_mod(hal->dev, 0); // Master - i2s_ll_set_rx_fifo_mod_force_en(hal->dev, 1); - - if (i2s_config->mode & I2S_MODE_SLAVE) { - i2s_ll_set_rx_slave_mod(hal->dev, 1); //RX Slave - } - } - -#if SOC_I2S_SUPPORTS_PDM - if (!(i2s_config->mode & I2S_MODE_PDM)) { - i2s_ll_set_rx_pdm_en(hal->dev, 0); - i2s_ll_set_tx_pdm_en(hal->dev, 0); - } else { - if (i2s_config->mode & I2S_MODE_TX) { - i2s_ll_tx_pdm_cfg(hal->dev, I2S_TX_PDM_FP_DEF, i2s_config->sample_rate/100); - } - if(i2s_config->mode & I2S_MODE_RX) { - i2s_ll_rx_pdm_cfg(hal->dev, I2S_RX_PDM_DSR_DEF); - } - // PDM mode have nothing to do with communication format configuration. - return; - } -#endif - -#if SOC_I2S_SUPPORTS_ADC_DAC - if (i2s_config->mode & (I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN)) { - if (i2s_config->mode & I2S_MODE_DAC_BUILT_IN) { - i2s_ll_build_in_dac_ena(hal->dev); - } - if (i2s_config->mode & I2S_MODE_ADC_BUILT_IN) { - i2s_ll_build_in_adc_ena(hal->dev); - i2s_ll_set_rx_chan_mod(hal->dev, 1); - i2s_ll_set_rx_fifo_mod(hal->dev, 1); - i2s_ll_set_rx_mono(hal->dev, 0); - } - // Buildin ADC and DAC have nothing to do with communication format configuration. - return; - } -#endif - - i2s_hal_format_config(hal, i2s_config); -} - -void i2s_hal_enable_master_mode(i2s_hal_context_t *hal) -{ - i2s_ll_set_tx_slave_mod(hal->dev, 0); //MASTER Slave - i2s_ll_set_rx_slave_mod(hal->dev, 1); //RX Slave -} - -void i2s_hal_enable_slave_mode(i2s_hal_context_t *hal) -{ - i2s_ll_set_tx_slave_mod(hal->dev, 1); //TX Slave - i2s_ll_set_rx_slave_mod(hal->dev, 1); //RX Slave -} - -void i2s_hal_init(i2s_hal_context_t *hal, int i2s_num) -{ - //Get hardware instance. - hal->dev = I2S_LL_GET_HW(i2s_num); -} diff --git a/components/squeezelite/equalizer.c b/components/squeezelite/equalizer.c index d5e9d311..04d48fb4 100644 --- a/components/squeezelite/equalizer.c +++ b/components/squeezelite/equalizer.c @@ -63,7 +63,7 @@ static const float loudness_envelope_coefficients[EQ_BANDS][POLYNOME_COUNT] = { /**************************************************************************************** * calculate loudness gains */ -static void calculate_loudness(void) { +static void calculate_loudness(void) { for (int i = 0; i < EQ_BANDS; i++) { for (int j = 0; j < POLYNOME_COUNT && equalizer.loudness != 0; j++) { equalizer.loudness_gain[i] += @@ -109,29 +109,36 @@ void equalizer_close(void) { * change sample rate */ void equalizer_set_samplerate(uint32_t samplerate) { +#if BYTES_PER_FRAME == 4 if (equalizer.samplerate != samplerate) equalizer_close(); equalizer.samplerate = samplerate; equalizer.update = true; LOG_INFO("equalizer sample rate %u", samplerate); +#else + LOG_INFO("no equalizer with 32 bits samples"); +#endif } /**************************************************************************************** * get volume update and recalculate loudness according to */ void equalizer_set_volume(unsigned left, unsigned right) { +#if BYTES_PER_FRAME == 4 equalizer.volume = (left + right) / 2; // do classic dB conversion and scale it 0..100 if (equalizer.volume) equalizer.volume = log2(equalizer.volume); equalizer.volume = equalizer.volume / 16.0 * 100.0; calculate_loudness(); equalizer.update = true; +#endif } /**************************************************************************************** * change gains from LMS */ void equalizer_set_gain(int8_t *gain) { +#if BYTES_PER_FRAME == 4 char config[EQ_BANDS * 4 + 1] = { }; int n = 0; @@ -145,12 +152,16 @@ void equalizer_set_gain(int8_t *gain) { equalizer.update = true; LOG_INFO("equalizer gain %s", config); +#else + LOG_INFO("no equalizer with 32 bits samples"); +#endif } /**************************************************************************************** * change loudness from LMS */ void equalizer_set_loudness(uint8_t loudness) { +#if BYTES_PER_FRAME == 4 // update loudness gains as a factor of loudness and volume equalizer.loudness = loudness / 100.0; calculate_loudness(); @@ -161,12 +172,16 @@ void equalizer_set_loudness(uint8_t loudness) { equalizer.update = true; LOG_INFO("loudness %u", (unsigned) loudness); +#else + LOG_INFO("no equalizer with 32 bits samples"); +#endif } /**************************************************************************************** * process equalizer */ void equalizer_process(uint8_t *buf, uint32_t bytes) { +#if BYTES_PER_FRAME == 4 // don't want to process with output locked, so take the small risk to miss one parametric update if (equalizer.update) { equalizer.update = false; @@ -198,4 +213,5 @@ void equalizer_process(uint8_t *buf, uint32_t bytes) { if (equalizer.handle) { esp_equalizer_process(equalizer.handle, buf, bytes, equalizer.samplerate, 2); } +#endif } \ No newline at end of file diff --git a/components/squeezelite/output_i2s.c b/components/squeezelite/output_i2s.c index 83b3af4a..7e583338 100644 --- a/components/squeezelite/output_i2s.c +++ b/components/squeezelite/output_i2s.c @@ -583,15 +583,11 @@ static void output_thread_i2s(void *arg) { i2s_set_sample_rates(CONFIG_I2S_NUM, spdif.enabled ? i2s_config.sample_rate * 2 : i2s_config.sample_rate); i2s_zero_dma_buffer(CONFIG_I2S_NUM); -#if BYTES_PER_FRAME == 4 equalizer_set_samplerate(output.current_sample_rate); -#endif } -#if BYTES_PER_FRAME == 4 // run equalizer equalizer_process(obuf, oframes * BYTES_PER_FRAME); -#endif // we assume that here we have been able to entirely fill the DMA buffers if (spdif.enabled) { @@ -600,7 +596,7 @@ static void output_thread_i2s(void *arg) { // need IRAM for speed but can't allocate a FRAME_BLOCK * 16, so process by smaller chunks while (count < oframes) { size_t chunk = min(SPDIF_BLOCK, oframes - count); - spdif_convert((ISAMPLE_T*) obuf + count * 2, chunk, (u32_t*) spdif.buf, &spdif.count); + spdif_convert((ISAMPLE_T*) obuf + count * 2, chunk, (u32_t*) spdif.buf, &spdif.count); i2s_write(CONFIG_I2S_NUM, spdif.buf, chunk * 16, &obytes, portMAX_DELAY); bytes += obytes / (16 / BYTES_PER_FRAME); count += chunk; @@ -709,12 +705,16 @@ static const u16_t spdif_bmclookup[256] = { //biphase mark encoded values (least /* SPDIF is supposed to be (before BMC encoding, from LSB to MSB) - PPPP AAAA SSSS SSSS SSSS SSSS SSSS VUCP - after BMC encoding, each bits becomes 2 hence this becomes a 64 bits word. The - the trick is to start not with a PPPP sequence but with an VUCP sequence to that - the 16 bits samples are aligned with a BMC word boundary. Note that the LSB of the - audio is transmitted first (not the MSB) and that ESP32 libray sends R then L, - contrary to what seems to be usually done, so (dst) order had to be changed + 0.... 1... 191.. 0 + BLFMRF MLFWRF MLFWRF BLFMRF (B,M,W=preamble-4, L/R=left/Right-24, F=Flags-4) + each xLF pattern is 32 bits + PPPP AAAA SSSS SSSS SSSS SSSS SSSS VUCP (P=preamble, A=auxiliary, S=sample-20bits, V=valid, U=user data, C=channel status, P=parity) + After BMC encoding, each bit becomes 2 hence this becomes a 64 bits word. The parity + is fixed by changing AAAA bits so that VUPC does not change. Then then trick is to + start not with a PPPP sequence but with an VUCP sequence to that the 16 bits samples + are aligned with a BMC word boundary. Input buffer is left first => LRLR... + The I2S interface must output first the B/M/W preamble which means that second + 32 bits words must be first and so must be marked right channel. */ static void IRAM_ATTR spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst, size_t *count) { register u16_t hi, lo, aux; @@ -729,7 +729,6 @@ static void IRAM_ATTR spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst, s // invert if last preceeding bit is 1 lo ^= ~((s16_t)hi) >> 16; // first 16 bits - *dst++ = ((u32_t)lo << 16) | hi; aux = 0xb333 ^ (((u32_t)((s16_t)lo)) >> 17); #else hi = spdif_bmclookup[(u8_t)(*src >> 24)]; @@ -738,39 +737,38 @@ static void IRAM_ATTR spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst, s // invert if last preceeding bit is 1 lo ^= ~((s16_t)hi) >> 16; // first 16 bits - *dst++ = ((u32_t)lo << 16) | hi; // we use 20 bits samples as we need to force parity aux = spdif_bmclookup[(u8_t)(*src++ >> 12)]; aux = (u8_t) (aux ^ (~((s16_t)lo) >> 16)); aux |= (0xb3 ^ (((u16_t)((s8_t)aux)) >> 9)) << 8; #endif - // VUCP-Bits: Valid, Subcode, Channelstatus, Parity = 0 - // As parity is always 0, we can use fixed preambles + // set special preamble every 192 iteration if (++cnt > 191) { *dst++ = VUCP | (PREAMBLE_B << 16 ) | aux; //special preamble for one of 192 frames cnt = 0; } else { *dst++ = VUCP | (PREAMBLE_M << 16) | aux; - } + } + // now write sample's 16 low bits + *dst++ = ((u32_t)lo << 16) | hi; // then do right channel, no need to check PREAMBLE_B #if BYTES_PER_FRAME == 4 hi = spdif_bmclookup[(u8_t)(*src >> 8)]; lo = spdif_bmclookup[(u8_t) *src++]; lo ^= ~((s16_t)hi) >> 16; - *dst++ = ((u32_t)lo << 16) | hi; aux = 0xb333 ^ (((u32_t)((s16_t)lo)) >> 17); #else hi = spdif_bmclookup[(u8_t)(*src >> 24)]; lo = spdif_bmclookup[(u8_t)(*src >> 16)]; lo ^= ~((s16_t)hi) >> 16; - *dst++ = ((u32_t)lo << 16) | hi; aux = spdif_bmclookup[(u8_t)(*src++ >> 12)]; aux = (u8_t) (aux ^ (~((s16_t)lo) >> 16)); aux |= (0xb3 ^ (((u16_t)((s8_t)aux)) >> 9)) << 8; #endif *dst++ = VUCP | (PREAMBLE_W << 16) | aux; + *dst++ = ((u32_t)lo << 16) | hi; } *count = cnt;