diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..c7003d6 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,23 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.3.0 + hooks: + - id: end-of-file-fixer + - id: trailing-whitespace + +- repo: https://github.com/codespell-project/codespell + rev: v1.16.0 + hooks: + - id: codespell + +- repo: https://github.com/pocc/pre-commit-hooks + rev: v1.1.1 + hooks: + - id: clang-format + args: [-i, --style=GNU] +# args: [-i, --style=Google] +# - id: clang-tidy +# args: [-checks=clang-diagnostic-return-type] +# - id: uncrustify +# - id: cppcheck +# args: [--enable=all] diff --git a/components/custom_driver/i2s.c b/components/custom_driver/i2s.c index b11880a..bf0b9dc 100644 --- a/components/custom_driver/i2s.c +++ b/components/custom_driver/i2s.c @@ -12,213 +12,243 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include -#include -#include #include +#include +#include +#include #include "freertos/FreeRTOS.h" #include "freertos/queue.h" #include "freertos/semphr.h" -#include "soc/lldesc.h" #include "driver/gpio.h" -#include "i2s.h" #include "hal/gpio_hal.h" +#include "i2s.h" +#include "soc/lldesc.h" #include "soc/rtc.h" -#include "esp_intr_alloc.h" -#include "esp_err.h" #include "esp_attr.h" +#include "esp_efuse.h" +#include "esp_err.h" +#include "esp_intr_alloc.h" #include "esp_log.h" #include "esp_pm.h" -#include "esp_efuse.h" #include "esp_rom_gpio.h" #include "sdkconfig.h" -static const char* I2S_TAG = "c_I2S"; +static const char *I2S_TAG = "c_I2S"; -#define I2S_CHECK(a, str, ret) if (!(a)) { \ - ESP_LOGE(I2S_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \ - return (ret); \ - } +#define I2S_CHECK(a, str, ret) \ + if (!(a)) \ + { \ + ESP_LOGE (I2S_TAG, "%s(%d): %s", __FUNCTION__, __LINE__, str); \ + return (ret); \ + } -#define I2S_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&i2s_spinlock[i2s_num]) -#define I2S_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&i2s_spinlock[i2s_num]) -#define I2S_ENTER_CRITICAL() portENTER_CRITICAL(&i2s_spinlock[i2s_num]) -#define I2S_EXIT_CRITICAL() portEXIT_CRITICAL(&i2s_spinlock[i2s_num]) -#define I2S_FULL_DUPLEX_SLAVE_MODE_MASK (I2S_MODE_TX | I2S_MODE_RX | I2S_MODE_SLAVE) -#define I2S_FULL_DUPLEX_MASTER_MODE_MASK (I2S_MODE_TX | I2S_MODE_RX | I2S_MODE_MASTER) +#define I2S_ENTER_CRITICAL_ISR() \ + portENTER_CRITICAL_ISR (&i2s_spinlock[i2s_num]) +#define I2S_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR (&i2s_spinlock[i2s_num]) +#define I2S_ENTER_CRITICAL() portENTER_CRITICAL (&i2s_spinlock[i2s_num]) +#define I2S_EXIT_CRITICAL() portEXIT_CRITICAL (&i2s_spinlock[i2s_num]) +#define I2S_FULL_DUPLEX_SLAVE_MODE_MASK \ + (I2S_MODE_TX | I2S_MODE_RX | I2S_MODE_SLAVE) +#define I2S_FULL_DUPLEX_MASTER_MODE_MASK \ + (I2S_MODE_TX | I2S_MODE_RX | I2S_MODE_MASTER) -//TODO: Refactor to put this logic into LL -#define I2S_AD_BCK_FACTOR (2) -#define I2S_PDM_BCK_FACTOR (64) -#define I2S_BASE_CLK (2*APB_CLK_FREQ) +// TODO: Refactor to put this logic into LL +#define I2S_AD_BCK_FACTOR (2) +#define I2S_PDM_BCK_FACTOR (64) +#define I2S_BASE_CLK (2 * APB_CLK_FREQ) /** * @brief DMA buffer object * */ -typedef struct { - char **buf; - int buf_size; - int rw_pos; - void *curr_ptr; - SemaphoreHandle_t mux; - xQueueHandle queue; - lldesc_t **desc; +typedef struct +{ + char **buf; + int buf_size; + int rw_pos; + void *curr_ptr; + SemaphoreHandle_t mux; + xQueueHandle queue; + lldesc_t **desc; } i2s_dma_t; /** * @brief I2S object instance * */ -typedef struct { - i2s_port_t i2s_num; /*!< I2S port number*/ - int queue_size; /*!< I2S event queue size*/ - QueueHandle_t i2s_queue; /*!< I2S queue handler*/ - int dma_buf_count; /*!< DMA buffer count, number of buffer*/ - int dma_buf_len; /*!< DMA buffer length, length of each buffer*/ - i2s_dma_t *rx; /*!< DMA Tx buffer*/ - i2s_dma_t *tx; /*!< DMA Rx buffer*/ - i2s_isr_handle_t i2s_isr_handle; /*!< I2S Interrupt handle*/ - int channel_num; /*!< Number of channels*/ - int bytes_per_sample; /*!< Bytes per sample*/ - int bits_per_sample; /*!< Bits per sample*/ - i2s_mode_t mode; /*!< I2S Working mode*/ - uint32_t sample_rate; /*!< I2S sample rate */ - bool use_apll; /*!< I2S use APLL clock */ - bool tx_desc_auto_clear; /*!< I2S auto clear tx descriptor on underflow */ - int fixed_mclk; /*!< I2S fixed MLCK clock */ - double real_rate; +typedef struct +{ + i2s_port_t i2s_num; /*!< I2S port number*/ + int queue_size; /*!< I2S event queue size*/ + QueueHandle_t i2s_queue; /*!< I2S queue handler*/ + int dma_buf_count; /*!< DMA buffer count, number of buffer*/ + int dma_buf_len; /*!< DMA buffer length, length of each buffer*/ + i2s_dma_t *rx; /*!< DMA Tx buffer*/ + i2s_dma_t *tx; /*!< DMA Rx buffer*/ + i2s_isr_handle_t i2s_isr_handle; /*!< I2S Interrupt handle*/ + int channel_num; /*!< Number of channels*/ + int bytes_per_sample; /*!< Bytes per sample*/ + int bits_per_sample; /*!< Bits per sample*/ + i2s_mode_t mode; /*!< I2S Working mode*/ + uint32_t sample_rate; /*!< I2S sample rate */ + bool use_apll; /*!< I2S use APLL clock */ + bool tx_desc_auto_clear; /*!< I2S auto clear tx descriptor on underflow */ + int fixed_mclk; /*!< I2S fixed MLCK clock */ + double real_rate; #ifdef CONFIG_PM_ENABLE - esp_pm_lock_handle_t pm_lock; + esp_pm_lock_handle_t pm_lock; #endif - i2s_hal_context_t hal; /*!< I2S hal context*/ + i2s_hal_context_t hal; /*!< I2S hal context*/ } i2s_obj_t; -static i2s_obj_t *p_i2s_obj[I2S_NUM_MAX] = {0}; +static i2s_obj_t *p_i2s_obj[I2S_NUM_MAX] = { 0 }; static portMUX_TYPE i2s_spinlock[I2S_NUM_MAX]; -static i2s_dma_t *i2s_create_dma_queue(i2s_port_t i2s_num, int dma_buf_count, int dma_buf_len); -static esp_err_t i2s_destroy_dma_queue(i2s_port_t i2s_num, i2s_dma_t *dma); +static i2s_dma_t *i2s_create_dma_queue (i2s_port_t i2s_num, int dma_buf_count, + int dma_buf_len); +static esp_err_t i2s_destroy_dma_queue (i2s_port_t i2s_num, i2s_dma_t *dma); -static inline void gpio_matrix_out_check(int gpio, uint32_t signal_idx, bool out_inv, bool oen_inv) +static inline void +gpio_matrix_out_check (int gpio, uint32_t signal_idx, bool out_inv, + bool oen_inv) { - //if pin = -1, do not need to configure - if (gpio != -1) { - gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO); - gpio_set_direction(gpio, GPIO_MODE_OUTPUT); - esp_rom_gpio_connect_out_signal(gpio, signal_idx, out_inv, oen_inv); + // if pin = -1, do not need to configure + if (gpio != -1) + { + gpio_hal_iomux_func_sel (GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO); + gpio_set_direction (gpio, GPIO_MODE_OUTPUT); + esp_rom_gpio_connect_out_signal (gpio, signal_idx, out_inv, oen_inv); } } -static inline void gpio_matrix_in_check(int gpio, uint32_t signal_idx, bool inv) +static inline void +gpio_matrix_in_check (int gpio, uint32_t signal_idx, bool inv) { - if (gpio != -1) { - gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO); - //Set direction, for some GPIOs, the input function are not enabled as default. - gpio_set_direction(gpio, GPIO_MODE_INPUT); - esp_rom_gpio_connect_in_signal(gpio, signal_idx, inv); + if (gpio != -1) + { + gpio_hal_iomux_func_sel (GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO); + // Set direction, for some GPIOs, the input function are not enabled as + // default. + gpio_set_direction (gpio, GPIO_MODE_INPUT); + esp_rom_gpio_connect_in_signal (gpio, signal_idx, inv); } } -esp_err_t i2s_custom_clear_intr_status(i2s_port_t i2s_num, uint32_t clr_mask) +esp_err_t +i2s_custom_clear_intr_status (i2s_port_t i2s_num, uint32_t clr_mask) { - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - i2s_hal_clear_intr_status(&(p_i2s_obj[i2s_num]->hal), clr_mask); - return ESP_OK; + I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + i2s_hal_clear_intr_status (&(p_i2s_obj[i2s_num]->hal), clr_mask); + return ESP_OK; } -esp_err_t i2s_custom_enable_rx_intr(i2s_port_t i2s_num) +esp_err_t +i2s_custom_enable_rx_intr (i2s_port_t i2s_num) { - I2S_ENTER_CRITICAL(); - i2s_hal_enable_rx_intr(&(p_i2s_obj[i2s_num]->hal)); - I2S_EXIT_CRITICAL(); - return ESP_OK; + I2S_ENTER_CRITICAL (); + i2s_hal_enable_rx_intr (&(p_i2s_obj[i2s_num]->hal)); + I2S_EXIT_CRITICAL (); + return ESP_OK; } -esp_err_t i2s_custom_disable_rx_intr(i2s_port_t i2s_num) +esp_err_t +i2s_custom_disable_rx_intr (i2s_port_t i2s_num) { - I2S_ENTER_CRITICAL(); - i2s_hal_disable_rx_intr(&(p_i2s_obj[i2s_num]->hal)); - I2S_EXIT_CRITICAL(); - return ESP_OK; + I2S_ENTER_CRITICAL (); + i2s_hal_disable_rx_intr (&(p_i2s_obj[i2s_num]->hal)); + I2S_EXIT_CRITICAL (); + return ESP_OK; } -esp_err_t i2s_custom_disable_tx_intr(i2s_port_t i2s_num) +esp_err_t +i2s_custom_disable_tx_intr (i2s_port_t i2s_num) { - I2S_ENTER_CRITICAL(); - i2s_hal_disable_tx_intr(&(p_i2s_obj[i2s_num]->hal)); - I2S_EXIT_CRITICAL(); - return ESP_OK; + I2S_ENTER_CRITICAL (); + i2s_hal_disable_tx_intr (&(p_i2s_obj[i2s_num]->hal)); + I2S_EXIT_CRITICAL (); + return ESP_OK; } -esp_err_t i2s_custom_enable_tx_intr(i2s_port_t i2s_num) +esp_err_t +i2s_custom_enable_tx_intr (i2s_port_t i2s_num) { - I2S_ENTER_CRITICAL(); - i2s_hal_enable_tx_intr(&(p_i2s_obj[i2s_num]->hal)); - I2S_EXIT_CRITICAL(); - return ESP_OK; + I2S_ENTER_CRITICAL (); + i2s_hal_enable_tx_intr (&(p_i2s_obj[i2s_num]->hal)); + I2S_EXIT_CRITICAL (); + return ESP_OK; } -float i2s_custom_get_clk(i2s_port_t i2s_num) +float +i2s_custom_get_clk (i2s_port_t i2s_num) { - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - return p_i2s_obj[i2s_num]->real_rate; + I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + return p_i2s_obj[i2s_num]->real_rate; } -static esp_err_t i2s_isr_register(i2s_port_t i2s_num, int intr_alloc_flags, void (*fn)(void*), void * arg, i2s_isr_handle_t *handle) +static esp_err_t +i2s_isr_register (i2s_port_t i2s_num, int intr_alloc_flags, + void (*fn) (void *), void *arg, i2s_isr_handle_t *handle) { - return esp_intr_alloc(i2s_periph_signal[i2s_num].irq, intr_alloc_flags, fn, arg, handle); + return esp_intr_alloc (i2s_periph_signal[i2s_num].irq, intr_alloc_flags, fn, + arg, handle); } -static float i2s_apll_get_fi2s(int bits_per_sample, int sdm0, int sdm1, int sdm2, int odir) +static float +i2s_apll_get_fi2s (int bits_per_sample, int sdm0, int sdm1, int sdm2, int odir) { - int f_xtal = (int)rtc_clk_xtal_freq_get() * 1000000; + int f_xtal = (int)rtc_clk_xtal_freq_get () * 1000000; #if CONFIG_IDF_TARGET_ESP32 - /* ESP32 rev0 silicon issue for APLL range/accuracy, please see ESP32 ECO document for more information on this */ - if (esp_efuse_get_chip_ver() == 0) { - sdm0 = 0; - sdm1 = 0; + /* ESP32 rev0 silicon issue for APLL range/accuracy, please see ESP32 ECO + * document for more information on this */ + if (esp_efuse_get_chip_ver () == 0) + { + sdm0 = 0; + sdm1 = 0; } #endif - float fout = f_xtal * (sdm2 + sdm1 / 256.0f + sdm0 / 65536.0f + 4); - if (fout < SOC_I2S_APLL_MIN_FREQ || fout > SOC_I2S_APLL_MAX_FREQ) { - return SOC_I2S_APLL_MAX_FREQ; + float fout = f_xtal * (sdm2 + sdm1 / 256.0f + sdm0 / 65536.0f + 4); + if (fout < SOC_I2S_APLL_MIN_FREQ || fout > SOC_I2S_APLL_MAX_FREQ) + { + return SOC_I2S_APLL_MAX_FREQ; } - float fpll = fout / (2 * (odir+2)); //== fi2s (N=1, b=0, a=1) - return fpll/2; + float fpll = fout / (2 * (odir + 2)); //== fi2s (N=1, b=0, a=1) + return fpll / 2; } /** * @brief APLL calculate function, was described by following: * APLL Output frequency is given by the formula: * - * apll_freq = xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536)/((o_div + 2) * 2) - * apll_freq = fout / ((o_div + 2) * 2) + * apll_freq = xtal_freq * (4 + sdm2 + sdm1/256 + + * sdm0/65536)/((o_div + 2) * 2) apll_freq = fout / ((o_div + 2) * 2) * - * The dividend in this expression should be in the range of 240 - 600 MHz. - * In rev. 0 of ESP32, sdm0 and sdm1 are unused and always set to 0. + * The dividend in this expression should be in the range of 240 - + * 600 MHz. In rev. 0 of ESP32, sdm0 and sdm1 are unused and always set to 0. * * sdm0 frequency adjustment parameter, 0..255 * * sdm1 frequency adjustment parameter, 0..255 * * sdm2 frequency adjustment parameter, 0..63 * * o_div frequency divider, 0..31 * - * The most accurate way to find the sdm0..2 and odir parameters is to loop through them all, - * then apply the above formula, finding the closest frequency to the desired one. - * But 256*256*64*32 = 134.217.728 loops are too slow with ESP32 - * 1. We will choose the parameters with the highest level of change, - * With 350MHztx), "tx NULL", ESP_ERR_INVALID_ARG); + I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + I2S_CHECK ((p_i2s_obj[i2s_num]->tx), "tx NULL", ESP_ERR_INVALID_ARG); - i2s_custom_stop(i2s_num); + i2s_custom_stop (i2s_num); - xSemaphoreTake(p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); + xSemaphoreTake (p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); - i2s_hal_set_out_link_addr(&(p_i2s_obj[i2s_num]->hal), (uint32_t) p_i2s_obj[i2s_num]->tx->desc[0]); + i2s_hal_set_out_link_addr (&(p_i2s_obj[i2s_num]->hal), + (uint32_t)p_i2s_obj[i2s_num]->tx->desc[0]); - p_i2s_obj[i2s_num]->tx->curr_ptr = NULL; - p_i2s_obj[i2s_num]->tx->rw_pos = 0; + p_i2s_obj[i2s_num]->tx->curr_ptr = NULL; + p_i2s_obj[i2s_num]->tx->rw_pos = 0; - // fill DMA buffers - if ((data != NULL) && (written != NULL) && (size != 0)) { - size_t offset = 0; - size_t maxDmaBufBytes = p_i2s_obj[i2s_num]->dma_buf_len * p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; + // fill DMA buffers + if ((data != NULL) && (written != NULL) && (size != 0)) + { + size_t offset = 0; + size_t maxDmaBufBytes = p_i2s_obj[i2s_num]->dma_buf_len + * p_i2s_obj[i2s_num]->bytes_per_sample + * p_i2s_obj[i2s_num]->channel_num; - for (i=0; idma_buf_count; i++) { - char *buf = (char *)p_i2s_obj[i2s_num]->tx->desc[i]->buf; + for (i = 0; i < p_i2s_obj[i2s_num]->dma_buf_count; i++) + { + char *buf = (char *)p_i2s_obj[i2s_num]->tx->desc[i]->buf; - if (tmpSize > maxDmaBufBytes) { + if (tmpSize > maxDmaBufBytes) + { - memcpy(buf, &data[offset], maxDmaBufBytes); - offset += maxDmaBufBytes; + memcpy (buf, &data[offset], maxDmaBufBytes); + offset += maxDmaBufBytes; -// ESP_LOGW(I2S_TAG, "wrote %d", maxDmaBufBytes); + // ESP_LOGW(I2S_TAG, "wrote %d", + // maxDmaBufBytes); - tmpSize -= maxDmaBufBytes; - } - else { - memcpy(buf, &data[offset], tmpSize); - offset += tmpSize; + tmpSize -= maxDmaBufBytes; + } + else + { + memcpy (buf, &data[offset], tmpSize); + offset += tmpSize; -// ESP_LOGW(I2S_TAG, "wrote %d", tmpSize); + // ESP_LOGW(I2S_TAG, "wrote %d", + // tmpSize); - tmpSize = 0; - } + tmpSize = 0; + } - if (tmpSize == 0) { - break; - } - } + if (tmpSize == 0) + { + break; + } + } - *written = offset; - } + *written = offset; + } - // empty queue - xQueueReset(p_i2s_obj[i2s_num]->tx->queue); + // empty queue + xQueueReset (p_i2s_obj[i2s_num]->tx->queue); - xSemaphoreGive(p_i2s_obj[i2s_num]->tx->mux); + xSemaphoreGive (p_i2s_obj[i2s_num]->tx->mux); - return ESP_OK; + return ESP_OK; } -esp_err_t i2s_custom_set_clk(i2s_port_t i2s_num, uint32_t rate, i2s_bits_per_sample_t bits, i2s_channel_t ch) +esp_err_t +i2s_custom_set_clk (i2s_port_t i2s_num, uint32_t rate, + i2s_bits_per_sample_t bits, i2s_channel_t ch) { - int factor = (256%bits)? 384 : 256; // According to hardware codec requirement(supported 256fs or 384fs) - int clkmInteger, clkmDecimals, bck = 0; - double denom = (double)1 / 64; - int channel = 2; - i2s_dma_t *save_tx = NULL, *save_rx = NULL; + int factor + = (256 % bits) ? 384 : 256; // According to hardware codec + // requirement(supported 256fs or 384fs) + int clkmInteger, clkmDecimals, bck = 0; + double denom = (double)1 / 64; + int channel = 2; + i2s_dma_t *save_tx = NULL, *save_rx = NULL; - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - if (bits % 8 != 0 || bits > I2S_BITS_PER_SAMPLE_32BIT || bits < I2S_BITS_PER_SAMPLE_16BIT) { - ESP_LOGE(I2S_TAG, "Invalid bits per sample"); - return ESP_ERR_INVALID_ARG; + if (bits % 8 != 0 || bits > I2S_BITS_PER_SAMPLE_32BIT + || bits < I2S_BITS_PER_SAMPLE_16BIT) + { + ESP_LOGE (I2S_TAG, "Invalid bits per sample"); + return ESP_ERR_INVALID_ARG; } - if (p_i2s_obj[i2s_num] == NULL) { - ESP_LOGE(I2S_TAG, "Not initialized yet"); - return ESP_ERR_INVALID_ARG; + if (p_i2s_obj[i2s_num] == NULL) + { + ESP_LOGE (I2S_TAG, "Not initialized yet"); + return ESP_ERR_INVALID_ARG; } - p_i2s_obj[i2s_num]->sample_rate = rate; - double clkmdiv = (double)I2S_BASE_CLK / (rate * factor); + p_i2s_obj[i2s_num]->sample_rate = rate; + double clkmdiv = (double)I2S_BASE_CLK / (rate * factor); - if (clkmdiv > 256) { - ESP_LOGE(I2S_TAG, "clkmdiv is too large\r\n"); - return ESP_ERR_INVALID_ARG; + if (clkmdiv > 256) + { + ESP_LOGE (I2S_TAG, "clkmdiv is too large\r\n"); + return ESP_ERR_INVALID_ARG; } - // wait all on-going writing finish - if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) && p_i2s_obj[i2s_num]->tx) { - xSemaphoreTake(p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); + // wait all on-going writing finish + if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) && p_i2s_obj[i2s_num]->tx) + { + xSemaphoreTake (p_i2s_obj[i2s_num]->tx->mux, + (portTickType)portMAX_DELAY); } - if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) && p_i2s_obj[i2s_num]->rx) { - xSemaphoreTake(p_i2s_obj[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); + if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) && p_i2s_obj[i2s_num]->rx) + { + xSemaphoreTake (p_i2s_obj[i2s_num]->rx->mux, + (portTickType)portMAX_DELAY); } - i2s_custom_stop(i2s_num); - i2s_hal_set_rx_mode(&(p_i2s_obj[i2s_num]->hal), ch, bits); - i2s_hal_set_tx_mode(&(p_i2s_obj[i2s_num]->hal), ch, bits); + i2s_custom_stop (i2s_num); + i2s_hal_set_rx_mode (&(p_i2s_obj[i2s_num]->hal), ch, bits); + i2s_hal_set_tx_mode (&(p_i2s_obj[i2s_num]->hal), ch, bits); - if (p_i2s_obj[i2s_num]->channel_num != (int)ch) { - p_i2s_obj[i2s_num]->channel_num = (ch == 2) ? 2 : 1; + if (p_i2s_obj[i2s_num]->channel_num != (int)ch) + { + p_i2s_obj[i2s_num]->channel_num = (ch == 2) ? 2 : 1; } - if ((int)bits != p_i2s_obj[i2s_num]->bits_per_sample) { - p_i2s_obj[i2s_num]->bits_per_sample = bits; + if ((int)bits != p_i2s_obj[i2s_num]->bits_per_sample) + { + p_i2s_obj[i2s_num]->bits_per_sample = bits; - // Round bytes_per_sample up to next multiple of 16 bits - int halfwords_per_sample = (bits + 15) / 16; - p_i2s_obj[i2s_num]->bytes_per_sample = halfwords_per_sample * 2; + // Round bytes_per_sample up to next multiple of 16 bits + int halfwords_per_sample = (bits + 15) / 16; + p_i2s_obj[i2s_num]->bytes_per_sample = halfwords_per_sample * 2; - // Because limited of DMA buffer is 4092 bytes - if (p_i2s_obj[i2s_num]->dma_buf_len * p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num > 4092) { - p_i2s_obj[i2s_num]->dma_buf_len = 4092 / p_i2s_obj[i2s_num]->bytes_per_sample / p_i2s_obj[i2s_num]->channel_num; + // Because limited of DMA buffer is 4092 bytes + if (p_i2s_obj[i2s_num]->dma_buf_len + * p_i2s_obj[i2s_num]->bytes_per_sample + * p_i2s_obj[i2s_num]->channel_num + > 4092) + { + p_i2s_obj[i2s_num]->dma_buf_len + = 4092 / p_i2s_obj[i2s_num]->bytes_per_sample + / p_i2s_obj[i2s_num]->channel_num; } - // Re-create TX DMA buffer - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + // Re-create TX DMA buffer + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) + { - save_tx = p_i2s_obj[i2s_num]->tx; + save_tx = p_i2s_obj[i2s_num]->tx; - p_i2s_obj[i2s_num]->tx = i2s_create_dma_queue(i2s_num, p_i2s_obj[i2s_num]->dma_buf_count, p_i2s_obj[i2s_num]->dma_buf_len); - if (p_i2s_obj[i2s_num]->tx == NULL) { - ESP_LOGE(I2S_TAG, "Failed to create tx dma buffer"); - i2s_custom_driver_uninstall(i2s_num); - return ESP_ERR_NO_MEM; + p_i2s_obj[i2s_num]->tx = i2s_create_dma_queue ( + i2s_num, p_i2s_obj[i2s_num]->dma_buf_count, + p_i2s_obj[i2s_num]->dma_buf_len); + if (p_i2s_obj[i2s_num]->tx == NULL) + { + ESP_LOGE (I2S_TAG, "Failed to create tx dma buffer"); + i2s_custom_driver_uninstall (i2s_num); + return ESP_ERR_NO_MEM; } - i2s_hal_set_out_link_addr(&(p_i2s_obj[i2s_num]->hal), (uint32_t) p_i2s_obj[i2s_num]->tx->desc[0]); + i2s_hal_set_out_link_addr ( + &(p_i2s_obj[i2s_num]->hal), + (uint32_t)p_i2s_obj[i2s_num]->tx->desc[0]); - //destroy old tx dma if exist - if (save_tx) { - i2s_destroy_dma_queue(i2s_num, save_tx); + // destroy old tx dma if exist + if (save_tx) + { + i2s_destroy_dma_queue (i2s_num, save_tx); } } - // Re-create RX DMA buffer - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + // Re-create RX DMA buffer + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) + { - save_rx = p_i2s_obj[i2s_num]->rx; + save_rx = p_i2s_obj[i2s_num]->rx; - p_i2s_obj[i2s_num]->rx = i2s_create_dma_queue(i2s_num, p_i2s_obj[i2s_num]->dma_buf_count, p_i2s_obj[i2s_num]->dma_buf_len); - if (p_i2s_obj[i2s_num]->rx == NULL){ - ESP_LOGE(I2S_TAG, "Failed to create rx dma buffer"); - i2s_custom_driver_uninstall(i2s_num); - return ESP_ERR_NO_MEM; + p_i2s_obj[i2s_num]->rx = i2s_create_dma_queue ( + i2s_num, p_i2s_obj[i2s_num]->dma_buf_count, + p_i2s_obj[i2s_num]->dma_buf_len); + if (p_i2s_obj[i2s_num]->rx == NULL) + { + ESP_LOGE (I2S_TAG, "Failed to create rx dma buffer"); + i2s_custom_driver_uninstall (i2s_num); + return ESP_ERR_NO_MEM; } - i2s_hal_set_in_link(&(p_i2s_obj[i2s_num]->hal), p_i2s_obj[i2s_num]->dma_buf_len * p_i2s_obj[i2s_num]->channel_num * p_i2s_obj[i2s_num]->bytes_per_sample, (uint32_t) p_i2s_obj[i2s_num]->rx->desc[0]); - //destroy old rx dma if exist - if (save_rx) { - i2s_destroy_dma_queue(i2s_num, save_rx); + i2s_hal_set_in_link (&(p_i2s_obj[i2s_num]->hal), + p_i2s_obj[i2s_num]->dma_buf_len + * p_i2s_obj[i2s_num]->channel_num + * p_i2s_obj[i2s_num]->bytes_per_sample, + (uint32_t)p_i2s_obj[i2s_num]->rx->desc[0]); + // destroy old rx dma if exist + if (save_rx) + { + i2s_destroy_dma_queue (i2s_num, save_rx); } } - } - double mclk; - int sdm0, sdm1, sdm2, odir, m_scale = 8; - int fi2s_clk = rate*channel*bits*m_scale; + double mclk; + int sdm0, sdm1, sdm2, odir, m_scale = 8; + int fi2s_clk = rate * channel * bits * m_scale; #if SOC_I2S_SUPPORTS_PDM - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_PDM) { - uint32_t b_clk = 0; - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { - uint32_t fp, fs; - i2s_hal_get_tx_pdm(&(p_i2s_obj[i2s_num]->hal), &fp, &fs); - // Recommended set `fp = 960, fs = sample_rate / 100` - fs = rate / 100; - i2s_hal_tx_pdm_cfg(&(p_i2s_obj[i2s_num]->hal), fp, fs); - b_clk = rate * I2S_PDM_BCK_FACTOR * fp / fs; - - } else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { - uint32_t dsr; - i2s_hal_get_rx_pdm(&(p_i2s_obj[i2s_num]->hal), &dsr); - b_clk = rate * I2S_PDM_BCK_FACTOR * (dsr ? 2 : 1); + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_PDM) + { + uint32_t b_clk = 0; + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) + { + uint32_t fp, fs; + i2s_hal_get_tx_pdm (&(p_i2s_obj[i2s_num]->hal), &fp, &fs); + // Recommended set `fp = 960, fs = sample_rate / 100` + fs = rate / 100; + i2s_hal_tx_pdm_cfg (&(p_i2s_obj[i2s_num]->hal), fp, fs); + b_clk = rate * I2S_PDM_BCK_FACTOR * fp / fs; } - fi2s_clk = b_clk * m_scale; - int factor2 = 5 ; - mclk = b_clk * factor2; - clkmdiv = ((double) I2S_BASE_CLK) / mclk; - clkmInteger = clkmdiv; - clkmDecimals = (clkmdiv - clkmInteger) / denom; - bck = mclk / b_clk; - } else + else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) + { + uint32_t dsr; + i2s_hal_get_rx_pdm (&(p_i2s_obj[i2s_num]->hal), &dsr); + b_clk = rate * I2S_PDM_BCK_FACTOR * (dsr ? 2 : 1); + } + fi2s_clk = b_clk * m_scale; + int factor2 = 5; + mclk = b_clk * factor2; + clkmdiv = ((double)I2S_BASE_CLK) / mclk; + clkmInteger = clkmdiv; + clkmDecimals = (clkmdiv - clkmInteger) / denom; + bck = mclk / b_clk; + } + else #endif { - clkmInteger = clkmdiv; - clkmDecimals = (clkmdiv - clkmInteger) / denom; - mclk = clkmInteger + denom * clkmDecimals; - bck = factor/(bits * channel); + clkmInteger = clkmdiv; + clkmDecimals = (clkmdiv - clkmInteger) / denom; + mclk = clkmInteger + denom * clkmDecimals; + bck = factor / (bits * channel); } - if(p_i2s_obj[i2s_num]->use_apll && p_i2s_obj[i2s_num]->fixed_mclk) { - fi2s_clk = p_i2s_obj[i2s_num]->fixed_mclk; - m_scale = fi2s_clk/bits/rate/channel; + if (p_i2s_obj[i2s_num]->use_apll && p_i2s_obj[i2s_num]->fixed_mclk) + { + fi2s_clk = p_i2s_obj[i2s_num]->fixed_mclk; + m_scale = fi2s_clk / bits / rate / channel; } - if(p_i2s_obj[i2s_num]->use_apll && i2s_apll_calculate_fi2s(fi2s_clk, bits, &sdm0, &sdm1, &sdm2, &odir) == ESP_OK) { - ESP_LOGD(I2S_TAG, "sdm0=%d, sdm1=%d, sdm2=%d, odir=%d", sdm0, sdm1, sdm2, odir); - rtc_clk_apll_enable(1, sdm0, sdm1, sdm2, odir); - i2s_hal_set_clk_div(&(p_i2s_obj[i2s_num]->hal), 1, 1, 0, m_scale, m_scale); - i2s_hal_set_clock_sel(&(p_i2s_obj[i2s_num]->hal), I2S_CLK_APLL); - double fi2s_rate = i2s_apll_get_fi2s(bits, sdm0, sdm1, sdm2, odir); - p_i2s_obj[i2s_num]->real_rate = fi2s_rate/bits/channel/m_scale; - ESP_LOGI(I2S_TAG, "APLL: Req RATE: %d, real rate: %0.3f, BITS: %u, CLKM: %u, BCK_M: %u, MCLK: %0.3f, SCLK: %f, diva: %d, divb: %d", - rate, fi2s_rate/bits/channel/m_scale, bits, 1, m_scale, fi2s_rate, fi2s_rate/8, 1, 0); - } else { - i2s_hal_set_clock_sel(&(p_i2s_obj[i2s_num]->hal), I2S_CLK_D2CLK); - i2s_hal_set_clk_div(&(p_i2s_obj[i2s_num]->hal), clkmInteger, 63, clkmDecimals, bck, bck); - double real_rate = (double) (I2S_BASE_CLK / (bck * bits * clkmInteger) / 2); - p_i2s_obj[i2s_num]->real_rate = real_rate; - ESP_LOGI(I2S_TAG, "PLL_D2: Req RATE: %d, real rate: %0.3f, BITS: %u, CLKM: %u, BCK: %u, MCLK: %0.3f, SCLK: %f, diva: %d, divb: %d", - rate, real_rate, bits, clkmInteger, bck, (double)I2S_BASE_CLK / mclk, real_rate*bits*channel, 64, clkmDecimals); + if (p_i2s_obj[i2s_num]->use_apll + && i2s_apll_calculate_fi2s (fi2s_clk, bits, &sdm0, &sdm1, &sdm2, &odir) + == ESP_OK) + { + ESP_LOGD (I2S_TAG, "sdm0=%d, sdm1=%d, sdm2=%d, odir=%d", sdm0, sdm1, + sdm2, odir); + rtc_clk_apll_enable (1, sdm0, sdm1, sdm2, odir); + i2s_hal_set_clk_div (&(p_i2s_obj[i2s_num]->hal), 1, 1, 0, m_scale, + m_scale); + i2s_hal_set_clock_sel (&(p_i2s_obj[i2s_num]->hal), I2S_CLK_APLL); + double fi2s_rate = i2s_apll_get_fi2s (bits, sdm0, sdm1, sdm2, odir); + p_i2s_obj[i2s_num]->real_rate = fi2s_rate / bits / channel / m_scale; + ESP_LOGI (I2S_TAG, + "APLL: Req RATE: %d, real rate: %0.3f, BITS: %u, CLKM: %u, " + "BCK_M: %u, MCLK: %0.3f, SCLK: %f, diva: %d, divb: %d", + rate, fi2s_rate / bits / channel / m_scale, bits, 1, m_scale, + fi2s_rate, fi2s_rate / 8, 1, 0); } - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { - p_i2s_obj[i2s_num]->tx->curr_ptr = NULL; - p_i2s_obj[i2s_num]->tx->rw_pos = 0; + else + { + i2s_hal_set_clock_sel (&(p_i2s_obj[i2s_num]->hal), I2S_CLK_D2CLK); + i2s_hal_set_clk_div (&(p_i2s_obj[i2s_num]->hal), clkmInteger, 63, + clkmDecimals, bck, bck); + double real_rate + = (double)(I2S_BASE_CLK / (bck * bits * clkmInteger) / 2); + p_i2s_obj[i2s_num]->real_rate = real_rate; + ESP_LOGI (I2S_TAG, + "PLL_D2: Req RATE: %d, real rate: %0.3f, BITS: %u, CLKM: %u, " + "BCK: %u, MCLK: %0.3f, SCLK: %f, diva: %d, divb: %d", + rate, real_rate, bits, clkmInteger, bck, + (double)I2S_BASE_CLK / mclk, real_rate * bits * channel, 64, + clkmDecimals); } - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { - p_i2s_obj[i2s_num]->rx->curr_ptr = NULL; - p_i2s_obj[i2s_num]->rx->rw_pos = 0; + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) + { + p_i2s_obj[i2s_num]->tx->curr_ptr = NULL; + p_i2s_obj[i2s_num]->tx->rw_pos = 0; + } + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) + { + p_i2s_obj[i2s_num]->rx->curr_ptr = NULL; + p_i2s_obj[i2s_num]->rx->rw_pos = 0; } - i2s_hal_set_tx_bits_mod(&(p_i2s_obj[i2s_num]->hal), bits); - i2s_hal_set_rx_bits_mod(&(p_i2s_obj[i2s_num]->hal), bits); + i2s_hal_set_tx_bits_mod (&(p_i2s_obj[i2s_num]->hal), bits); + i2s_hal_set_rx_bits_mod (&(p_i2s_obj[i2s_num]->hal), bits); - // wait all writing on-going finish - if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) && p_i2s_obj[i2s_num]->tx) { - xSemaphoreGive(p_i2s_obj[i2s_num]->tx->mux); + // wait all writing on-going finish + if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) && p_i2s_obj[i2s_num]->tx) + { + xSemaphoreGive (p_i2s_obj[i2s_num]->tx->mux); } - if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) && p_i2s_obj[i2s_num]->rx) { - xSemaphoreGive(p_i2s_obj[i2s_num]->rx->mux); + if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) && p_i2s_obj[i2s_num]->rx) + { + xSemaphoreGive (p_i2s_obj[i2s_num]->rx->mux); } -// i2s_custom_start(i2s_num); // don't start just yet, we want to fill dma buffer first -// return ESP_OK; + // i2s_custom_start(i2s_num); // don't start just yet, we want to + // fill dma buffer first return ESP_OK; - // ensure all DMA buffers are available right after initialization - // ENSURE i2s_custom_start() isn't called before i2s_write has filled at least one buffer - return i2s_custom_init_dma_tx_queues(i2s_num, NULL, 0, NULL); + // ensure all DMA buffers are available right after initialization + // ENSURE i2s_custom_start() isn't called before i2s_write has filled at + // least one buffer + return i2s_custom_init_dma_tx_queues (i2s_num, NULL, 0, NULL); } -static void IRAM_ATTR i2s_intr_handler_default(void *arg) +static void IRAM_ATTR +i2s_intr_handler_default (void *arg) { - i2s_obj_t *p_i2s = (i2s_obj_t*) arg; - uint32_t status; - i2s_hal_get_intr_status(&(p_i2s->hal), &status); - if(status == 0) { - //Avoid spurious interrupt - return; + i2s_obj_t *p_i2s = (i2s_obj_t *)arg; + uint32_t status; + i2s_hal_get_intr_status (&(p_i2s->hal), &status); + if (status == 0) + { + // Avoid spurious interrupt + return; } - i2s_event_t i2s_event; - int dummy; + i2s_event_t i2s_event; + int dummy; - portBASE_TYPE high_priority_task_awoken = 0; + portBASE_TYPE high_priority_task_awoken = 0; - lldesc_t *finish_desc = NULL; + lldesc_t *finish_desc = NULL; - if ((status & I2S_INTR_OUT_DSCR_ERR) || (status & I2S_INTR_IN_DSCR_ERR)) { - ESP_EARLY_LOGE(I2S_TAG, "dma error, interrupt status: 0x%08x", status); - if (p_i2s->i2s_queue) { - i2s_event.type = I2S_EVENT_DMA_ERROR; - if (xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { - xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); + if ((status & I2S_INTR_OUT_DSCR_ERR) || (status & I2S_INTR_IN_DSCR_ERR)) + { + ESP_EARLY_LOGE (I2S_TAG, "dma error, interrupt status: 0x%08x", status); + if (p_i2s->i2s_queue) + { + i2s_event.type = I2S_EVENT_DMA_ERROR; + if (xQueueIsQueueFullFromISR (p_i2s->i2s_queue)) + { + xQueueReceiveFromISR (p_i2s->i2s_queue, &dummy, + &high_priority_task_awoken); } - xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); + xQueueSendFromISR (p_i2s->i2s_queue, (void *)&i2s_event, + &high_priority_task_awoken); } } - if ((status & I2S_INTR_OUT_EOF) && p_i2s->tx) { - i2s_hal_get_out_eof_des_addr(&(p_i2s->hal), (uint32_t *)&finish_desc); - // All buffers are empty. This means we have an underflow on our hands. - if (xQueueIsQueueFullFromISR(p_i2s->tx->queue)) { - xQueueReceiveFromISR(p_i2s->tx->queue, &dummy, &high_priority_task_awoken); - // See if tx descriptor needs to be auto cleared: - // This will avoid any kind of noise that may get introduced due to transmission - // of previous data from tx descriptor on I2S line. - if (p_i2s->tx_desc_auto_clear == true) { - memset((void *) dummy, 0, p_i2s->tx->buf_size); + if ((status & I2S_INTR_OUT_EOF) && p_i2s->tx) + { + i2s_hal_get_out_eof_des_addr (&(p_i2s->hal), (uint32_t *)&finish_desc); + // All buffers are empty. This means we have an underflow on our hands. + if (xQueueIsQueueFullFromISR (p_i2s->tx->queue)) + { + xQueueReceiveFromISR (p_i2s->tx->queue, &dummy, + &high_priority_task_awoken); + // See if tx descriptor needs to be auto cleared: + // This will avoid any kind of noise that may get introduced due to + // transmission of previous data from tx descriptor on I2S line. + if (p_i2s->tx_desc_auto_clear == true) + { + memset ((void *)dummy, 0, p_i2s->tx->buf_size); } } - xQueueSendFromISR(p_i2s->tx->queue, (void*)(&finish_desc->buf), &high_priority_task_awoken); - if (p_i2s->i2s_queue) { - i2s_event.type = I2S_EVENT_TX_DONE; - if (xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { - xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); + xQueueSendFromISR (p_i2s->tx->queue, (void *)(&finish_desc->buf), + &high_priority_task_awoken); + if (p_i2s->i2s_queue) + { + i2s_event.type = I2S_EVENT_TX_DONE; + if (xQueueIsQueueFullFromISR (p_i2s->i2s_queue)) + { + xQueueReceiveFromISR (p_i2s->i2s_queue, &dummy, + &high_priority_task_awoken); } - xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); + xQueueSendFromISR (p_i2s->i2s_queue, (void *)&i2s_event, + &high_priority_task_awoken); } } - if ((status & I2S_INTR_IN_SUC_EOF) && p_i2s->rx) { - // All buffers are full. This means we have an overflow. - i2s_hal_get_in_eof_des_addr(&(p_i2s->hal), (uint32_t *)&finish_desc); - if (xQueueIsQueueFullFromISR(p_i2s->rx->queue)) { - xQueueReceiveFromISR(p_i2s->rx->queue, &dummy, &high_priority_task_awoken); + if ((status & I2S_INTR_IN_SUC_EOF) && p_i2s->rx) + { + // All buffers are full. This means we have an overflow. + i2s_hal_get_in_eof_des_addr (&(p_i2s->hal), (uint32_t *)&finish_desc); + if (xQueueIsQueueFullFromISR (p_i2s->rx->queue)) + { + xQueueReceiveFromISR (p_i2s->rx->queue, &dummy, + &high_priority_task_awoken); } - xQueueSendFromISR(p_i2s->rx->queue, (void*)(&finish_desc->buf), &high_priority_task_awoken); - if (p_i2s->i2s_queue) { - i2s_event.type = I2S_EVENT_RX_DONE; - if (p_i2s->i2s_queue && xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { - xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); + xQueueSendFromISR (p_i2s->rx->queue, (void *)(&finish_desc->buf), + &high_priority_task_awoken); + if (p_i2s->i2s_queue) + { + i2s_event.type = I2S_EVENT_RX_DONE; + if (p_i2s->i2s_queue && xQueueIsQueueFullFromISR (p_i2s->i2s_queue)) + { + xQueueReceiveFromISR (p_i2s->i2s_queue, &dummy, + &high_priority_task_awoken); } - xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); + xQueueSendFromISR (p_i2s->i2s_queue, (void *)&i2s_event, + &high_priority_task_awoken); } } - i2s_hal_clear_intr_status(&(p_i2s->hal), status); + i2s_hal_clear_intr_status (&(p_i2s->hal), status); - if (high_priority_task_awoken == pdTRUE) { - portYIELD_FROM_ISR(); + if (high_priority_task_awoken == pdTRUE) + { + portYIELD_FROM_ISR (); } } -static esp_err_t i2s_destroy_dma_queue(i2s_port_t i2s_num, i2s_dma_t *dma) +static esp_err_t +i2s_destroy_dma_queue (i2s_port_t i2s_num, i2s_dma_t *dma) { - int bux_idx; - if (p_i2s_obj[i2s_num] == NULL) { - ESP_LOGE(I2S_TAG, "Not initialized yet"); - return ESP_ERR_INVALID_ARG; + int bux_idx; + if (p_i2s_obj[i2s_num] == NULL) + { + ESP_LOGE (I2S_TAG, "Not initialized yet"); + return ESP_ERR_INVALID_ARG; } - if (dma == NULL) { - ESP_LOGE(I2S_TAG, "dma is NULL"); - return ESP_ERR_INVALID_ARG; + if (dma == NULL) + { + ESP_LOGE (I2S_TAG, "dma is NULL"); + return ESP_ERR_INVALID_ARG; } - for (bux_idx = 0; bux_idx < p_i2s_obj[i2s_num]->dma_buf_count; bux_idx++) { - if (dma->desc && dma->desc[bux_idx]) { - free(dma->desc[bux_idx]); + for (bux_idx = 0; bux_idx < p_i2s_obj[i2s_num]->dma_buf_count; bux_idx++) + { + if (dma->desc && dma->desc[bux_idx]) + { + free (dma->desc[bux_idx]); } - if (dma->buf && dma->buf[bux_idx]) { - free(dma->buf[bux_idx]); + if (dma->buf && dma->buf[bux_idx]) + { + free (dma->buf[bux_idx]); } } - if (dma->buf) { - free(dma->buf); + if (dma->buf) + { + free (dma->buf); } - if (dma->desc) { - free(dma->desc); + if (dma->desc) + { + free (dma->desc); } - vQueueDelete(dma->queue); - vSemaphoreDelete(dma->mux); - free(dma); - return ESP_OK; + vQueueDelete (dma->queue); + vSemaphoreDelete (dma->mux); + free (dma); + return ESP_OK; } -static i2s_dma_t *i2s_create_dma_queue(i2s_port_t i2s_num, int dma_buf_count, int dma_buf_len) +static i2s_dma_t * +i2s_create_dma_queue (i2s_port_t i2s_num, int dma_buf_count, int dma_buf_len) { - int bux_idx; - int sample_size = p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; - i2s_dma_t *dma = (i2s_dma_t*) malloc(sizeof(i2s_dma_t)); - if (dma == NULL) { - ESP_LOGE(I2S_TAG, "Error malloc i2s_dma_t"); - return NULL; + int bux_idx; + int sample_size + = p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; + i2s_dma_t *dma = (i2s_dma_t *)malloc (sizeof (i2s_dma_t)); + if (dma == NULL) + { + ESP_LOGE (I2S_TAG, "Error malloc i2s_dma_t"); + return NULL; } - memset(dma, 0, sizeof(i2s_dma_t)); + memset (dma, 0, sizeof (i2s_dma_t)); - dma->buf = (char **)malloc(sizeof(char*) * dma_buf_count); - if (dma->buf == NULL) { - ESP_LOGE(I2S_TAG, "Error malloc dma buffer pointer"); - free(dma); - return NULL; + dma->buf = (char **)malloc (sizeof (char *) * dma_buf_count); + if (dma->buf == NULL) + { + ESP_LOGE (I2S_TAG, "Error malloc dma buffer pointer"); + free (dma); + return NULL; } - memset(dma->buf, 0, sizeof(char*) * dma_buf_count); + memset (dma->buf, 0, sizeof (char *) * dma_buf_count); - for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { - dma->buf[bux_idx] = (char*) heap_caps_calloc(1, dma_buf_len * sample_size, MALLOC_CAP_DMA); - if (dma->buf[bux_idx] == NULL) { - ESP_LOGE(I2S_TAG, "Error malloc dma buffer"); - i2s_destroy_dma_queue(i2s_num, dma); - return NULL; + for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) + { + dma->buf[bux_idx] = (char *)heap_caps_calloc ( + 1, dma_buf_len * sample_size, MALLOC_CAP_DMA); + if (dma->buf[bux_idx] == NULL) + { + ESP_LOGE (I2S_TAG, "Error malloc dma buffer"); + i2s_destroy_dma_queue (i2s_num, dma); + return NULL; } - ESP_LOGD(I2S_TAG, "Addr[%d] = %d", bux_idx, (int)dma->buf[bux_idx]); + ESP_LOGD (I2S_TAG, "Addr[%d] = %d", bux_idx, (int)dma->buf[bux_idx]); } - dma->desc = (lldesc_t**) malloc(sizeof(lldesc_t*) * dma_buf_count); - if (dma->desc == NULL) { - ESP_LOGE(I2S_TAG, "Error malloc dma description"); - i2s_destroy_dma_queue(i2s_num, dma); - return NULL; + dma->desc = (lldesc_t **)malloc (sizeof (lldesc_t *) * dma_buf_count); + if (dma->desc == NULL) + { + ESP_LOGE (I2S_TAG, "Error malloc dma description"); + i2s_destroy_dma_queue (i2s_num, dma); + return NULL; } - for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { - dma->desc[bux_idx] = (lldesc_t*) heap_caps_malloc(sizeof(lldesc_t), MALLOC_CAP_DMA); - if (dma->desc[bux_idx] == NULL) { - ESP_LOGE(I2S_TAG, "Error malloc dma description entry"); - i2s_destroy_dma_queue(i2s_num, dma); - return NULL; + for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) + { + dma->desc[bux_idx] + = (lldesc_t *)heap_caps_malloc (sizeof (lldesc_t), MALLOC_CAP_DMA); + if (dma->desc[bux_idx] == NULL) + { + ESP_LOGE (I2S_TAG, "Error malloc dma description entry"); + i2s_destroy_dma_queue (i2s_num, dma); + return NULL; } } - for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { - dma->desc[bux_idx]->owner = 1; - dma->desc[bux_idx]->eof = 1; - dma->desc[bux_idx]->sosf = 0; - dma->desc[bux_idx]->length = dma_buf_len * sample_size; - dma->desc[bux_idx]->size = dma_buf_len * sample_size; - dma->desc[bux_idx]->buf = (uint8_t *) dma->buf[bux_idx]; - dma->desc[bux_idx]->offset = 0; - dma->desc[bux_idx]->empty = (uint32_t)((bux_idx < (dma_buf_count - 1)) ? (dma->desc[bux_idx + 1]) : dma->desc[0]); + for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) + { + dma->desc[bux_idx]->owner = 1; + dma->desc[bux_idx]->eof = 1; + dma->desc[bux_idx]->sosf = 0; + dma->desc[bux_idx]->length = dma_buf_len * sample_size; + dma->desc[bux_idx]->size = dma_buf_len * sample_size; + dma->desc[bux_idx]->buf = (uint8_t *)dma->buf[bux_idx]; + dma->desc[bux_idx]->offset = 0; + dma->desc[bux_idx]->empty = (uint32_t) ((bux_idx < (dma_buf_count - 1)) + ? (dma->desc[bux_idx + 1]) + : dma->desc[0]); } - dma->queue = xQueueCreate(dma_buf_count - 1, sizeof(char*)); - dma->mux = xSemaphoreCreateMutex(); - dma->buf_size = dma_buf_len * sample_size; - ESP_LOGI(I2S_TAG, "DMA Malloc info, datalen=blocksize=%d, dma_buf_count=%d", dma_buf_len * sample_size, dma_buf_count); - return dma; + dma->queue = xQueueCreate (dma_buf_count - 1, sizeof (char *)); + dma->mux = xSemaphoreCreateMutex (); + dma->buf_size = dma_buf_len * sample_size; + ESP_LOGI (I2S_TAG, "DMA Malloc info, datalen=blocksize=%d, dma_buf_count=%d", + dma_buf_len * sample_size, dma_buf_count); + return dma; } -esp_err_t i2s_custom_start(i2s_port_t i2s_num) +esp_err_t +i2s_custom_start (i2s_port_t i2s_num) { - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - //start DMA link - I2S_ENTER_CRITICAL(); - i2s_hal_reset(&(p_i2s_obj[i2s_num]->hal)); + I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + // start DMA link + I2S_ENTER_CRITICAL (); + i2s_hal_reset (&(p_i2s_obj[i2s_num]->hal)); - esp_intr_disable(p_i2s_obj[i2s_num]->i2s_isr_handle); - i2s_hal_clear_intr_status(&(p_i2s_obj[i2s_num]->hal), I2S_INTR_MAX); - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { - i2s_custom_enable_tx_intr(i2s_num); - i2s_hal_start_tx(&(p_i2s_obj[i2s_num]->hal)); + esp_intr_disable (p_i2s_obj[i2s_num]->i2s_isr_handle); + i2s_hal_clear_intr_status (&(p_i2s_obj[i2s_num]->hal), I2S_INTR_MAX); + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) + { + i2s_custom_enable_tx_intr (i2s_num); + i2s_hal_start_tx (&(p_i2s_obj[i2s_num]->hal)); } - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { - i2s_custom_enable_rx_intr(i2s_num); - i2s_hal_start_rx(&(p_i2s_obj[i2s_num]->hal)); + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) + { + i2s_custom_enable_rx_intr (i2s_num); + i2s_hal_start_rx (&(p_i2s_obj[i2s_num]->hal)); } - esp_intr_enable(p_i2s_obj[i2s_num]->i2s_isr_handle); - I2S_EXIT_CRITICAL(); - return ESP_OK; + esp_intr_enable (p_i2s_obj[i2s_num]->i2s_isr_handle); + I2S_EXIT_CRITICAL (); + return ESP_OK; } -esp_err_t i2s_custom_stop(i2s_port_t i2s_num) +esp_err_t +i2s_custom_stop (i2s_port_t i2s_num) { - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_ENTER_CRITICAL(); - esp_intr_disable(p_i2s_obj[i2s_num]->i2s_isr_handle); - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { - i2s_hal_stop_tx(&(p_i2s_obj[i2s_num]->hal)); - i2s_custom_disable_tx_intr(i2s_num); + I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + I2S_ENTER_CRITICAL (); + esp_intr_disable (p_i2s_obj[i2s_num]->i2s_isr_handle); + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) + { + i2s_hal_stop_tx (&(p_i2s_obj[i2s_num]->hal)); + i2s_custom_disable_tx_intr (i2s_num); } - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { - i2s_hal_stop_rx(&(p_i2s_obj[i2s_num]->hal)); - i2s_custom_disable_rx_intr(i2s_num); + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) + { + i2s_hal_stop_rx (&(p_i2s_obj[i2s_num]->hal)); + i2s_custom_disable_rx_intr (i2s_num); } - uint32_t mask; - i2s_hal_get_intr_status(&(p_i2s_obj[i2s_num]->hal), &mask); - i2s_hal_clear_intr_status(&(p_i2s_obj[i2s_num]->hal), mask); - I2S_EXIT_CRITICAL(); - return ESP_OK; + uint32_t mask; + i2s_hal_get_intr_status (&(p_i2s_obj[i2s_num]->hal), &mask); + i2s_hal_clear_intr_status (&(p_i2s_obj[i2s_num]->hal), mask); + I2S_EXIT_CRITICAL (); + return ESP_OK; } -esp_err_t i2s_custom_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin) +esp_err_t +i2s_custom_set_pin (i2s_port_t i2s_num, const i2s_pin_config_t *pin) { - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - if (pin == NULL) { - return ESP_ERR_INVALID_ARG; + I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + if (pin == NULL) + { + return ESP_ERR_INVALID_ARG; } - if (pin->bck_io_num != -1 && !GPIO_IS_VALID_GPIO(pin->bck_io_num)) { - ESP_LOGE(I2S_TAG, "bck_io_num error"); - return ESP_FAIL; + if (pin->bck_io_num != -1 && !GPIO_IS_VALID_GPIO (pin->bck_io_num)) + { + ESP_LOGE (I2S_TAG, "bck_io_num error"); + return ESP_FAIL; } - if (pin->ws_io_num != -1 && !GPIO_IS_VALID_GPIO(pin->ws_io_num)) { - ESP_LOGE(I2S_TAG, "ws_io_num error"); - return ESP_FAIL; + if (pin->ws_io_num != -1 && !GPIO_IS_VALID_GPIO (pin->ws_io_num)) + { + ESP_LOGE (I2S_TAG, "ws_io_num error"); + return ESP_FAIL; } - if (pin->data_out_num != -1 && !GPIO_IS_VALID_OUTPUT_GPIO(pin->data_out_num)) { - ESP_LOGE(I2S_TAG, "data_out_num error"); - return ESP_FAIL; + if (pin->data_out_num != -1 + && !GPIO_IS_VALID_OUTPUT_GPIO (pin->data_out_num)) + { + ESP_LOGE (I2S_TAG, "data_out_num error"); + return ESP_FAIL; } - if (pin->data_in_num != -1 && !GPIO_IS_VALID_GPIO(pin->data_in_num)) { - ESP_LOGE(I2S_TAG, "data_in_num error"); - return ESP_FAIL; + if (pin->data_in_num != -1 && !GPIO_IS_VALID_GPIO (pin->data_in_num)) + { + ESP_LOGE (I2S_TAG, "data_in_num error"); + return ESP_FAIL; } - int bck_sig = -1, ws_sig = -1, data_out_sig = -1, data_in_sig = -1; - //Each IIS hw module has a RX and TX unit. - //For TX unit, the output signal index should be I2SnO_xxx_OUT_IDX - //For TX unit, the input signal index should be I2SnO_xxx_IN_IDX - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) { - bck_sig = i2s_periph_signal[i2s_num].o_bck_out_sig; - ws_sig = i2s_periph_signal[i2s_num].o_ws_out_sig; - data_out_sig = i2s_periph_signal[i2s_num].o_data_out_sig; - } else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_SLAVE) { - bck_sig = i2s_periph_signal[i2s_num].o_bck_in_sig; - ws_sig = i2s_periph_signal[i2s_num].o_ws_in_sig; - data_out_sig = i2s_periph_signal[i2s_num].o_data_out_sig; + int bck_sig = -1, ws_sig = -1, data_out_sig = -1, data_in_sig = -1; + // Each IIS hw module has a RX and TX unit. + // For TX unit, the output signal index should be I2SnO_xxx_OUT_IDX + // For TX unit, the input signal index should be I2SnO_xxx_IN_IDX + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) + { + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) + { + bck_sig = i2s_periph_signal[i2s_num].o_bck_out_sig; + ws_sig = i2s_periph_signal[i2s_num].o_ws_out_sig; + data_out_sig = i2s_periph_signal[i2s_num].o_data_out_sig; + } + else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_SLAVE) + { + bck_sig = i2s_periph_signal[i2s_num].o_bck_in_sig; + ws_sig = i2s_periph_signal[i2s_num].o_ws_in_sig; + data_out_sig = i2s_periph_signal[i2s_num].o_data_out_sig; } } - //For RX unit, the output signal index should be I2SnI_xxx_OUT_IDX - //For RX unit, the input signal index shuld be I2SnI_xxx_IN_IDX - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) { - bck_sig = i2s_periph_signal[i2s_num].i_bck_out_sig; - ws_sig = i2s_periph_signal[i2s_num].i_ws_out_sig; - data_in_sig = i2s_periph_signal[i2s_num].i_data_in_sig; - } else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_SLAVE) { - bck_sig = i2s_periph_signal[i2s_num].i_bck_in_sig; - ws_sig = i2s_periph_signal[i2s_num].i_ws_in_sig; - data_in_sig = i2s_periph_signal[i2s_num].i_data_in_sig; + // For RX unit, the output signal index should be I2SnI_xxx_OUT_IDX + // For RX unit, the input signal index should be I2SnI_xxx_IN_IDX + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) + { + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) + { + bck_sig = i2s_periph_signal[i2s_num].i_bck_out_sig; + ws_sig = i2s_periph_signal[i2s_num].i_ws_out_sig; + data_in_sig = i2s_periph_signal[i2s_num].i_data_in_sig; + } + else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_SLAVE) + { + bck_sig = i2s_periph_signal[i2s_num].i_bck_in_sig; + ws_sig = i2s_periph_signal[i2s_num].i_ws_in_sig; + data_in_sig = i2s_periph_signal[i2s_num].i_data_in_sig; } } - //For "full-duplex + slave" mode, we should select RX signal index for ws and bck. - //For "full-duplex + master" mode, we should select TX signal index for ws and bck. - if ((p_i2s_obj[i2s_num]->mode & I2S_FULL_DUPLEX_SLAVE_MODE_MASK) == I2S_FULL_DUPLEX_SLAVE_MODE_MASK) { - bck_sig = i2s_periph_signal[i2s_num].i_bck_in_sig; - ws_sig = i2s_periph_signal[i2s_num].i_ws_in_sig; - } else if ((p_i2s_obj[i2s_num]->mode & I2S_FULL_DUPLEX_MASTER_MODE_MASK) == I2S_FULL_DUPLEX_MASTER_MODE_MASK) { - bck_sig = i2s_periph_signal[i2s_num].o_bck_out_sig; - ws_sig = i2s_periph_signal[i2s_num].o_ws_out_sig; + // For "full-duplex + slave" mode, we should select RX signal index for ws + // and bck. For "full-duplex + master" mode, we should select TX signal index + // for ws and bck. + if ((p_i2s_obj[i2s_num]->mode & I2S_FULL_DUPLEX_SLAVE_MODE_MASK) + == I2S_FULL_DUPLEX_SLAVE_MODE_MASK) + { + bck_sig = i2s_periph_signal[i2s_num].i_bck_in_sig; + ws_sig = i2s_periph_signal[i2s_num].i_ws_in_sig; } - gpio_matrix_out_check(pin->data_out_num, data_out_sig, 0, 0); - gpio_matrix_in_check(pin->data_in_num, data_in_sig, 0); - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) { - gpio_matrix_out_check(pin->ws_io_num, ws_sig, 0, 0); - gpio_matrix_out_check(pin->bck_io_num, bck_sig, 0, 0); - } else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_SLAVE) { - gpio_matrix_in_check(pin->ws_io_num, ws_sig, 0); - gpio_matrix_in_check(pin->bck_io_num, bck_sig, 0); + else if ((p_i2s_obj[i2s_num]->mode & I2S_FULL_DUPLEX_MASTER_MODE_MASK) + == I2S_FULL_DUPLEX_MASTER_MODE_MASK) + { + bck_sig = i2s_periph_signal[i2s_num].o_bck_out_sig; + ws_sig = i2s_periph_signal[i2s_num].o_ws_out_sig; } - ESP_LOGD(I2S_TAG, "data: out %d, in: %d, ws: %d, bck: %d", data_out_sig, data_in_sig, ws_sig, bck_sig); + gpio_matrix_out_check (pin->data_out_num, data_out_sig, 0, 0); + gpio_matrix_in_check (pin->data_in_num, data_in_sig, 0); + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) + { + gpio_matrix_out_check (pin->ws_io_num, ws_sig, 0, 0); + gpio_matrix_out_check (pin->bck_io_num, bck_sig, 0, 0); + } + else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_SLAVE) + { + gpio_matrix_in_check (pin->ws_io_num, ws_sig, 0); + gpio_matrix_in_check (pin->bck_io_num, bck_sig, 0); + } + ESP_LOGD (I2S_TAG, "data: out %d, in: %d, ws: %d, bck: %d", data_out_sig, + data_in_sig, ws_sig, bck_sig); - return ESP_OK; + return ESP_OK; } -esp_err_t i2s_custom_set_sample_rates(i2s_port_t i2s_num, uint32_t rate) +esp_err_t +i2s_custom_set_sample_rates (i2s_port_t i2s_num, uint32_t rate) { - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK((p_i2s_obj[i2s_num]->bytes_per_sample > 0), "bits_per_sample not set", ESP_ERR_INVALID_ARG); - return i2s_custom_set_clk(i2s_num, rate, p_i2s_obj[i2s_num]->bits_per_sample, p_i2s_obj[i2s_num]->channel_num); + I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + I2S_CHECK ((p_i2s_obj[i2s_num]->bytes_per_sample > 0), + "bits_per_sample not set", ESP_ERR_INVALID_ARG); + return i2s_custom_set_clk (i2s_num, rate, + p_i2s_obj[i2s_num]->bits_per_sample, + p_i2s_obj[i2s_num]->channel_num); } #if SOC_I2S_SUPPORTS_PDM -esp_err_t i2s_custom_set_pdm_rx_down_sample(i2s_port_t i2s_num, i2s_pdm_dsr_t dsr) +esp_err_t +i2s_custom_set_pdm_rx_down_sample (i2s_port_t i2s_num, i2s_pdm_dsr_t dsr) { - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - i2s_hal_rx_pdm_cfg(&(p_i2s_obj[i2s_num]->hal), dsr); - return i2s_custom_set_clk(i2s_num, p_i2s_obj[i2s_num]->sample_rate, p_i2s_obj[i2s_num]->bits_per_sample, p_i2s_obj[i2s_num]->channel_num); + I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + i2s_hal_rx_pdm_cfg (&(p_i2s_obj[i2s_num]->hal), dsr); + return i2s_custom_set_clk (i2s_num, p_i2s_obj[i2s_num]->sample_rate, + p_i2s_obj[i2s_num]->bits_per_sample, + p_i2s_obj[i2s_num]->channel_num); } #endif -static esp_err_t i2s_custom_check_cfg_static(i2s_port_t i2s_num, const i2s_config_t *cfg) +static esp_err_t +i2s_custom_check_cfg_static (i2s_port_t i2s_num, const i2s_config_t *cfg) { #if SOC_I2S_SUPPORTS_PDM - //We only check if the I2S number is invalid when set to PDM mode. - I2S_CHECK(!((cfg->mode & I2S_MODE_PDM) && (i2s_num != I2S_NUM_0)), "I2S DAC PDM only support on I2S0", ESP_ERR_INVALID_ARG); - return ESP_OK; + // We only check if the I2S number is invalid when set to PDM mode. + I2S_CHECK (!((cfg->mode & I2S_MODE_PDM) && (i2s_num != I2S_NUM_0)), + "I2S DAC PDM only support on I2S0", ESP_ERR_INVALID_ARG); + return ESP_OK; #endif - I2S_CHECK(cfg->communication_format && (cfg->communication_format < I2S_COMM_FORMAT_STAND_MAX), "invalid communication formats", ESP_ERR_INVALID_ARG); - I2S_CHECK(!((cfg->communication_format & I2S_COMM_FORMAT_STAND_MSB) && (cfg->communication_format & I2S_COMM_FORMAT_STAND_PCM_LONG)), "multiple communication formats specified", ESP_ERR_INVALID_ARG); - return ESP_OK; + I2S_CHECK (cfg->communication_format + && (cfg->communication_format < I2S_COMM_FORMAT_STAND_MAX), + "invalid communication formats", ESP_ERR_INVALID_ARG); + I2S_CHECK ( + !((cfg->communication_format & I2S_COMM_FORMAT_STAND_MSB) + && (cfg->communication_format & I2S_COMM_FORMAT_STAND_PCM_LONG)), + "multiple communication formats specified", ESP_ERR_INVALID_ARG); + return ESP_OK; } -static esp_err_t i2s_param_config(i2s_port_t i2s_num, const i2s_config_t *i2s_config) +static esp_err_t +i2s_param_config (i2s_port_t i2s_num, const i2s_config_t *i2s_config) { - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK((i2s_config), "param null", ESP_ERR_INVALID_ARG); - I2S_CHECK((i2s_custom_check_cfg_static(i2s_num, i2s_config) == ESP_OK), "param check error", ESP_ERR_INVALID_ARG); + I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + I2S_CHECK ((i2s_config), "param null", ESP_ERR_INVALID_ARG); + I2S_CHECK ((i2s_custom_check_cfg_static (i2s_num, i2s_config) == ESP_OK), + "param check error", ESP_ERR_INVALID_ARG); - periph_module_enable(i2s_periph_signal[i2s_num].module); + periph_module_enable (i2s_periph_signal[i2s_num].module); - // configure I2S data port interface. - i2s_hal_config_param(&(p_i2s_obj[i2s_num]->hal), i2s_config); - if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) && (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX)) { - i2s_hal_enable_sig_loopback(&(p_i2s_obj[i2s_num]->hal)); - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) { - i2s_hal_enable_master_mode(&(p_i2s_obj[i2s_num]->hal)); - } else { - i2s_hal_enable_slave_mode(&(p_i2s_obj[i2s_num]->hal)); + // configure I2S data port interface. + i2s_hal_config_param (&(p_i2s_obj[i2s_num]->hal), i2s_config); + if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) + && (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX)) + { + i2s_hal_enable_sig_loopback (&(p_i2s_obj[i2s_num]->hal)); + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) + { + i2s_hal_enable_master_mode (&(p_i2s_obj[i2s_num]->hal)); + } + else + { + i2s_hal_enable_slave_mode (&(p_i2s_obj[i2s_num]->hal)); } } - p_i2s_obj[i2s_num]->use_apll = i2s_config->use_apll; - p_i2s_obj[i2s_num]->tx_desc_auto_clear = i2s_config->tx_desc_auto_clear; - p_i2s_obj[i2s_num]->fixed_mclk = i2s_config->fixed_mclk; - return ESP_OK; + p_i2s_obj[i2s_num]->use_apll = i2s_config->use_apll; + p_i2s_obj[i2s_num]->tx_desc_auto_clear = i2s_config->tx_desc_auto_clear; + p_i2s_obj[i2s_num]->fixed_mclk = i2s_config->fixed_mclk; + return ESP_OK; } -esp_err_t i2s_custom_zero_dma_buffer(i2s_port_t i2s_num) +esp_err_t +i2s_custom_zero_dma_buffer (i2s_port_t i2s_num) { - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - if (p_i2s_obj[i2s_num]->rx && p_i2s_obj[i2s_num]->rx->buf != NULL && p_i2s_obj[i2s_num]->rx->buf_size != 0) { - for (int i = 0; i < p_i2s_obj[i2s_num]->dma_buf_count; i++) { - memset(p_i2s_obj[i2s_num]->rx->buf[i], 0, p_i2s_obj[i2s_num]->rx->buf_size); + I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + if (p_i2s_obj[i2s_num]->rx && p_i2s_obj[i2s_num]->rx->buf != NULL + && p_i2s_obj[i2s_num]->rx->buf_size != 0) + { + for (int i = 0; i < p_i2s_obj[i2s_num]->dma_buf_count; i++) + { + memset (p_i2s_obj[i2s_num]->rx->buf[i], 0, + p_i2s_obj[i2s_num]->rx->buf_size); } } - if (p_i2s_obj[i2s_num]->tx && p_i2s_obj[i2s_num]->tx->buf != NULL && p_i2s_obj[i2s_num]->tx->buf_size != 0) { - int bytes_left = 0; - bytes_left = (p_i2s_obj[i2s_num]->tx->buf_size - p_i2s_obj[i2s_num]->tx->rw_pos) % 4; - if (bytes_left) { - size_t zero_bytes = 0, bytes_written; - i2s_custom_write(i2s_num, (void *)&zero_bytes, bytes_left, &bytes_written, portMAX_DELAY); + if (p_i2s_obj[i2s_num]->tx && p_i2s_obj[i2s_num]->tx->buf != NULL + && p_i2s_obj[i2s_num]->tx->buf_size != 0) + { + int bytes_left = 0; + bytes_left + = (p_i2s_obj[i2s_num]->tx->buf_size - p_i2s_obj[i2s_num]->tx->rw_pos) + % 4; + if (bytes_left) + { + size_t zero_bytes = 0, bytes_written; + i2s_custom_write (i2s_num, (void *)&zero_bytes, bytes_left, + &bytes_written, portMAX_DELAY); } - for (int i = 0; i < p_i2s_obj[i2s_num]->dma_buf_count; i++) { - memset(p_i2s_obj[i2s_num]->tx->buf[i], 0, p_i2s_obj[i2s_num]->tx->buf_size); + for (int i = 0; i < p_i2s_obj[i2s_num]->dma_buf_count; i++) + { + memset (p_i2s_obj[i2s_num]->tx->buf[i], 0, + p_i2s_obj[i2s_num]->tx->buf_size); } } - return ESP_OK; + return ESP_OK; } -esp_err_t i2s_custom_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void* i2s_queue) +esp_err_t +i2s_custom_driver_install (i2s_port_t i2s_num, const i2s_config_t *i2s_config, + int queue_size, void *i2s_queue) { - esp_err_t err; - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK((i2s_config != NULL), "I2S configuration must not NULL", ESP_ERR_INVALID_ARG); - I2S_CHECK((i2s_config->dma_buf_count >= 2 && i2s_config->dma_buf_count <= 128), "I2S buffer count less than 128 and more than 2", ESP_ERR_INVALID_ARG); - I2S_CHECK((i2s_config->dma_buf_len >= 8 && i2s_config->dma_buf_len <= 1024), "I2S buffer length at most 1024 and more than 8", ESP_ERR_INVALID_ARG); - if (p_i2s_obj[i2s_num] == NULL) { - p_i2s_obj[i2s_num] = (i2s_obj_t*) malloc(sizeof(i2s_obj_t)); - if (p_i2s_obj[i2s_num] == NULL) { - ESP_LOGE(I2S_TAG, "Malloc I2S driver error"); - return ESP_ERR_NO_MEM; + esp_err_t err; + I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + I2S_CHECK ((i2s_config != NULL), "I2S configuration must not NULL", + ESP_ERR_INVALID_ARG); + I2S_CHECK ( + (i2s_config->dma_buf_count >= 2 && i2s_config->dma_buf_count <= 128), + "I2S buffer count less than 128 and more than 2", ESP_ERR_INVALID_ARG); + I2S_CHECK ((i2s_config->dma_buf_len >= 8 && i2s_config->dma_buf_len <= 1024), + "I2S buffer length at most 1024 and more than 8", + ESP_ERR_INVALID_ARG); + if (p_i2s_obj[i2s_num] == NULL) + { + p_i2s_obj[i2s_num] = (i2s_obj_t *)malloc (sizeof (i2s_obj_t)); + if (p_i2s_obj[i2s_num] == NULL) + { + ESP_LOGE (I2S_TAG, "Malloc I2S driver error"); + return ESP_ERR_NO_MEM; } - memset(p_i2s_obj[i2s_num], 0, sizeof(i2s_obj_t)); + memset (p_i2s_obj[i2s_num], 0, sizeof (i2s_obj_t)); - portMUX_TYPE i2s_spinlock_unlocked[1] = {portMUX_INITIALIZER_UNLOCKED}; - for (int x = 0; x < I2S_NUM_MAX; x++) { - i2s_spinlock[x] = i2s_spinlock_unlocked[0]; + portMUX_TYPE i2s_spinlock_unlocked[1] = { portMUX_INITIALIZER_UNLOCKED }; + for (int x = 0; x < I2S_NUM_MAX; x++) + { + i2s_spinlock[x] = i2s_spinlock_unlocked[0]; } - //To make sure hardware is enabled before any hardware register operations. - periph_module_enable(i2s_periph_signal[i2s_num].module); - i2s_hal_init(&(p_i2s_obj[i2s_num]->hal), i2s_num); + // To make sure hardware is enabled before any hardware register + // operations. + periph_module_enable (i2s_periph_signal[i2s_num].module); + i2s_hal_init (&(p_i2s_obj[i2s_num]->hal), i2s_num); - p_i2s_obj[i2s_num]->i2s_num = i2s_num; - p_i2s_obj[i2s_num]->dma_buf_count = i2s_config->dma_buf_count; - p_i2s_obj[i2s_num]->dma_buf_len = i2s_config->dma_buf_len; - p_i2s_obj[i2s_num]->i2s_queue = i2s_queue; - p_i2s_obj[i2s_num]->mode = i2s_config->mode; + p_i2s_obj[i2s_num]->i2s_num = i2s_num; + p_i2s_obj[i2s_num]->dma_buf_count = i2s_config->dma_buf_count; + p_i2s_obj[i2s_num]->dma_buf_len = i2s_config->dma_buf_len; + p_i2s_obj[i2s_num]->i2s_queue = i2s_queue; + p_i2s_obj[i2s_num]->mode = i2s_config->mode; - p_i2s_obj[i2s_num]->bits_per_sample = 0; - p_i2s_obj[i2s_num]->bytes_per_sample = 0; // Not initialized yet - p_i2s_obj[i2s_num]->channel_num = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 2 : 1; + p_i2s_obj[i2s_num]->bits_per_sample = 0; + p_i2s_obj[i2s_num]->bytes_per_sample = 0; // Not initialized yet + p_i2s_obj[i2s_num]->channel_num + = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 2 : 1; #ifdef CONFIG_PM_ENABLE - if (i2s_config->use_apll) { - err = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "i2s_driver", &p_i2s_obj[i2s_num]->pm_lock); - } else { - err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "i2s_driver", &p_i2s_obj[i2s_num]->pm_lock); - } - if (err != ESP_OK) { - free(p_i2s_obj[i2s_num]); - p_i2s_obj[i2s_num] = NULL; - ESP_LOGE(I2S_TAG, "I2S pm lock error"); - return err; - } -#endif //CONFIG_PM_ENABLE + if (i2s_config->use_apll) + { + err = esp_pm_lock_create (ESP_PM_NO_LIGHT_SLEEP, 0, "i2s_driver", + &p_i2s_obj[i2s_num]->pm_lock); + } + else + { + err = esp_pm_lock_create (ESP_PM_APB_FREQ_MAX, 0, "i2s_driver", + &p_i2s_obj[i2s_num]->pm_lock); + } + if (err != ESP_OK) + { + free (p_i2s_obj[i2s_num]); + p_i2s_obj[i2s_num] = NULL; + ESP_LOGE (I2S_TAG, "I2S pm lock error"); + return err; + } +#endif // CONFIG_PM_ENABLE - //initial interrupt - err = i2s_isr_register(i2s_num, i2s_config->intr_alloc_flags, i2s_intr_handler_default, p_i2s_obj[i2s_num], &p_i2s_obj[i2s_num]->i2s_isr_handle); - if (err != ESP_OK) { + // initial interrupt + err = i2s_isr_register (i2s_num, i2s_config->intr_alloc_flags, + i2s_intr_handler_default, p_i2s_obj[i2s_num], + &p_i2s_obj[i2s_num]->i2s_isr_handle); + if (err != ESP_OK) + { #ifdef CONFIG_PM_ENABLE - if (p_i2s_obj[i2s_num]->pm_lock) { - esp_pm_lock_delete(p_i2s_obj[i2s_num]->pm_lock); + if (p_i2s_obj[i2s_num]->pm_lock) + { + esp_pm_lock_delete (p_i2s_obj[i2s_num]->pm_lock); } #endif - free(p_i2s_obj[i2s_num]); - p_i2s_obj[i2s_num] = NULL; - ESP_LOGE(I2S_TAG, "Register I2S Interrupt error"); - return err; + free (p_i2s_obj[i2s_num]); + p_i2s_obj[i2s_num] = NULL; + ESP_LOGE (I2S_TAG, "Register I2S Interrupt error"); + return err; } - i2s_custom_stop(i2s_num); - err = i2s_param_config(i2s_num, i2s_config); - if (err != ESP_OK) { - i2s_custom_driver_uninstall(i2s_num); - ESP_LOGE(I2S_TAG, "I2S param configure error"); - return err; + i2s_custom_stop (i2s_num); + err = i2s_param_config (i2s_num, i2s_config); + if (err != ESP_OK) + { + i2s_custom_driver_uninstall (i2s_num); + ESP_LOGE (I2S_TAG, "I2S param configure error"); + return err; } - if (i2s_queue) { - p_i2s_obj[i2s_num]->i2s_queue = xQueueCreate(queue_size, sizeof(i2s_event_t)); - *((QueueHandle_t*) i2s_queue) = p_i2s_obj[i2s_num]->i2s_queue; - ESP_LOGI(I2S_TAG, "queue free spaces: %d", uxQueueSpacesAvailable(p_i2s_obj[i2s_num]->i2s_queue)); - } else { - p_i2s_obj[i2s_num]->i2s_queue = NULL; + if (i2s_queue) + { + p_i2s_obj[i2s_num]->i2s_queue + = xQueueCreate (queue_size, sizeof (i2s_event_t)); + *((QueueHandle_t *)i2s_queue) = p_i2s_obj[i2s_num]->i2s_queue; + ESP_LOGI (I2S_TAG, "queue free spaces: %d", + uxQueueSpacesAvailable (p_i2s_obj[i2s_num]->i2s_queue)); } - //set clock and start - return i2s_custom_set_clk(i2s_num, i2s_config->sample_rate, i2s_config->bits_per_sample, p_i2s_obj[i2s_num]->channel_num); + else + { + p_i2s_obj[i2s_num]->i2s_queue = NULL; + } + // set clock and start + return i2s_custom_set_clk (i2s_num, i2s_config->sample_rate, + i2s_config->bits_per_sample, + p_i2s_obj[i2s_num]->channel_num); } - ESP_LOGW(I2S_TAG, "I2S driver already installed"); - return ESP_OK; + ESP_LOGW (I2S_TAG, "I2S driver already installed"); + return ESP_OK; } -esp_err_t i2s_custom_driver_uninstall(i2s_port_t i2s_num) +esp_err_t +i2s_custom_driver_uninstall (i2s_port_t i2s_num) { - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - if (p_i2s_obj[i2s_num] == NULL) { - ESP_LOGI(I2S_TAG, "already uninstalled"); - return ESP_OK; + I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + if (p_i2s_obj[i2s_num] == NULL) + { + ESP_LOGI (I2S_TAG, "already uninstalled"); + return ESP_OK; } - i2s_custom_stop(i2s_num); - esp_intr_free(p_i2s_obj[i2s_num]->i2s_isr_handle); + i2s_custom_stop (i2s_num); + esp_intr_free (p_i2s_obj[i2s_num]->i2s_isr_handle); - if (p_i2s_obj[i2s_num]->tx != NULL && p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { - i2s_destroy_dma_queue(i2s_num, p_i2s_obj[i2s_num]->tx); - p_i2s_obj[i2s_num]->tx = NULL; + if (p_i2s_obj[i2s_num]->tx != NULL + && (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX)) + { + i2s_destroy_dma_queue (i2s_num, p_i2s_obj[i2s_num]->tx); + p_i2s_obj[i2s_num]->tx = NULL; } - if (p_i2s_obj[i2s_num]->rx != NULL && p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { - i2s_destroy_dma_queue(i2s_num, p_i2s_obj[i2s_num]->rx); - p_i2s_obj[i2s_num]->rx = NULL; + if (p_i2s_obj[i2s_num]->rx != NULL + && (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX)) + { + i2s_destroy_dma_queue (i2s_num, p_i2s_obj[i2s_num]->rx); + p_i2s_obj[i2s_num]->rx = NULL; } - if (p_i2s_obj[i2s_num]->i2s_queue) { - vQueueDelete(p_i2s_obj[i2s_num]->i2s_queue); - p_i2s_obj[i2s_num]->i2s_queue = NULL; + if (p_i2s_obj[i2s_num]->i2s_queue) + { + vQueueDelete (p_i2s_obj[i2s_num]->i2s_queue); + p_i2s_obj[i2s_num]->i2s_queue = NULL; } - if(p_i2s_obj[i2s_num]->use_apll) { - rtc_clk_apll_enable(0, 0, 0, 0, 0); + if (p_i2s_obj[i2s_num]->use_apll) + { + rtc_clk_apll_enable (0, 0, 0, 0, 0); } #ifdef CONFIG_PM_ENABLE - if (p_i2s_obj[i2s_num]->pm_lock) { - esp_pm_lock_delete(p_i2s_obj[i2s_num]->pm_lock); + if (p_i2s_obj[i2s_num]->pm_lock) + { + esp_pm_lock_delete (p_i2s_obj[i2s_num]->pm_lock); } #endif - free(p_i2s_obj[i2s_num]); - p_i2s_obj[i2s_num] = NULL; - periph_module_disable(i2s_periph_signal[i2s_num].module); + free (p_i2s_obj[i2s_num]); + p_i2s_obj[i2s_num] = NULL; + periph_module_disable (i2s_periph_signal[i2s_num].module); - return ESP_OK; + return ESP_OK; } -esp_err_t i2s_custom_write(i2s_port_t i2s_num, const void *src, size_t size, size_t *bytes_written, TickType_t ticks_to_wait) +esp_err_t +i2s_custom_write (i2s_port_t i2s_num, const void *src, size_t size, + size_t *bytes_written, TickType_t ticks_to_wait) { - char *data_ptr, *src_byte; - size_t bytes_can_write; - *bytes_written = 0; - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK((size < SOC_I2S_MAX_BUFFER_SIZE), "size is too large", ESP_ERR_INVALID_ARG); - I2S_CHECK((p_i2s_obj[i2s_num]->tx), "tx NULL", ESP_ERR_INVALID_ARG); - xSemaphoreTake(p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); + char *data_ptr, *src_byte; + size_t bytes_can_write; + *bytes_written = 0; + I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + I2S_CHECK ((size < SOC_I2S_MAX_BUFFER_SIZE), "size is too large", + ESP_ERR_INVALID_ARG); + I2S_CHECK ((p_i2s_obj[i2s_num]->tx), "tx NULL", ESP_ERR_INVALID_ARG); + xSemaphoreTake (p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); #ifdef CONFIG_PM_ENABLE - esp_pm_lock_acquire(p_i2s_obj[i2s_num]->pm_lock); + esp_pm_lock_acquire (p_i2s_obj[i2s_num]->pm_lock); #endif - src_byte = (char *)src; - while (size > 0) { -// for (int i = 0; i < p_i2s_obj[i2s_num]->dma_buf_count; i++) { -// ESP_LOGI(I2S_TAG,"%d: EOF %d", i, p_i2s_obj[i2s_num]->tx->desc[i]->eof); -// } + src_byte = (char *)src; + while (size > 0) + { + // for (int i = 0; i < p_i2s_obj[i2s_num]->dma_buf_count; i++) { + // ESP_LOGI(I2S_TAG,"%d: EOF %d", i, + // p_i2s_obj[i2s_num]->tx->desc[i]->eof); + // } - if (p_i2s_obj[i2s_num]->tx->rw_pos == p_i2s_obj[i2s_num]->tx->buf_size || p_i2s_obj[i2s_num]->tx->curr_ptr == NULL) { - if (xQueueReceive(p_i2s_obj[i2s_num]->tx->queue, &p_i2s_obj[i2s_num]->tx->curr_ptr, ticks_to_wait) == pdFALSE) { - break; + if (p_i2s_obj[i2s_num]->tx->rw_pos == p_i2s_obj[i2s_num]->tx->buf_size + || p_i2s_obj[i2s_num]->tx->curr_ptr == NULL) + { + if (xQueueReceive (p_i2s_obj[i2s_num]->tx->queue, + &p_i2s_obj[i2s_num]->tx->curr_ptr, ticks_to_wait) + == pdFALSE) + { + break; } - p_i2s_obj[i2s_num]->tx->rw_pos = 0; + p_i2s_obj[i2s_num]->tx->rw_pos = 0; } - ESP_LOGD(I2S_TAG, "size: %d, rw_pos: %d, buf_size: %d, curr_ptr: %d", size, p_i2s_obj[i2s_num]->tx->rw_pos, p_i2s_obj[i2s_num]->tx->buf_size, (int)p_i2s_obj[i2s_num]->tx->curr_ptr); - data_ptr = (char*)p_i2s_obj[i2s_num]->tx->curr_ptr; - data_ptr += p_i2s_obj[i2s_num]->tx->rw_pos; - bytes_can_write = p_i2s_obj[i2s_num]->tx->buf_size - p_i2s_obj[i2s_num]->tx->rw_pos; - if (bytes_can_write > size) { - bytes_can_write = size; + ESP_LOGD (I2S_TAG, "size: %d, rw_pos: %d, buf_size: %d, curr_ptr: %d", + size, p_i2s_obj[i2s_num]->tx->rw_pos, + p_i2s_obj[i2s_num]->tx->buf_size, + (int)p_i2s_obj[i2s_num]->tx->curr_ptr); + data_ptr = (char *)p_i2s_obj[i2s_num]->tx->curr_ptr; + data_ptr += p_i2s_obj[i2s_num]->tx->rw_pos; + bytes_can_write + = p_i2s_obj[i2s_num]->tx->buf_size - p_i2s_obj[i2s_num]->tx->rw_pos; + if (bytes_can_write > size) + { + bytes_can_write = size; } - memcpy(data_ptr, src_byte, bytes_can_write); - size -= bytes_can_write; - src_byte += bytes_can_write; - p_i2s_obj[i2s_num]->tx->rw_pos += bytes_can_write; - (*bytes_written) += bytes_can_write; + memcpy (data_ptr, src_byte, bytes_can_write); + size -= bytes_can_write; + src_byte += bytes_can_write; + p_i2s_obj[i2s_num]->tx->rw_pos += bytes_can_write; + (*bytes_written) += bytes_can_write; } #ifdef CONFIG_PM_ENABLE - esp_pm_lock_release(p_i2s_obj[i2s_num]->pm_lock); + esp_pm_lock_release (p_i2s_obj[i2s_num]->pm_lock); #endif - xSemaphoreGive(p_i2s_obj[i2s_num]->tx->mux); - return ESP_OK; + xSemaphoreGive (p_i2s_obj[i2s_num]->tx->mux); + return ESP_OK; } -esp_err_t i2s_custom_write_expand(i2s_port_t i2s_num, const void *src, size_t size, size_t src_bits, size_t aim_bits, size_t *bytes_written, TickType_t ticks_to_wait) +esp_err_t +i2s_custom_write_expand (i2s_port_t i2s_num, const void *src, size_t size, + size_t src_bits, size_t aim_bits, + size_t *bytes_written, TickType_t ticks_to_wait) { - char *data_ptr; - int bytes_can_write, tail; - int src_bytes, aim_bytes, zero_bytes; - *bytes_written = 0; - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK((size > 0), "size must greater than zero", ESP_ERR_INVALID_ARG); - I2S_CHECK((aim_bits * size < SOC_I2S_MAX_BUFFER_SIZE), "size is too large", ESP_ERR_INVALID_ARG); - I2S_CHECK((aim_bits >= src_bits), "aim_bits mustn't be less than src_bits", ESP_ERR_INVALID_ARG); - I2S_CHECK((p_i2s_obj[i2s_num]->tx), "tx NULL", ESP_ERR_INVALID_ARG); - if (src_bits < I2S_BITS_PER_SAMPLE_8BIT || aim_bits < I2S_BITS_PER_SAMPLE_8BIT) { - ESP_LOGE(I2S_TAG,"bits mustn't be less than 8, src_bits %d aim_bits %d", src_bits, aim_bits); - return ESP_ERR_INVALID_ARG; + char *data_ptr; + int bytes_can_write, tail; + int src_bytes, aim_bytes, zero_bytes; + *bytes_written = 0; + I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + I2S_CHECK ((size > 0), "size must greater than zero", ESP_ERR_INVALID_ARG); + I2S_CHECK ((aim_bits * size < SOC_I2S_MAX_BUFFER_SIZE), "size is too large", + ESP_ERR_INVALID_ARG); + I2S_CHECK ((aim_bits >= src_bits), "aim_bits mustn't be less than src_bits", + ESP_ERR_INVALID_ARG); + I2S_CHECK ((p_i2s_obj[i2s_num]->tx), "tx NULL", ESP_ERR_INVALID_ARG); + if (src_bits < I2S_BITS_PER_SAMPLE_8BIT + || aim_bits < I2S_BITS_PER_SAMPLE_8BIT) + { + ESP_LOGE (I2S_TAG, + "bits mustn't be less than 8, src_bits %d aim_bits %d", + src_bits, aim_bits); + return ESP_ERR_INVALID_ARG; } - if (src_bits > I2S_BITS_PER_SAMPLE_32BIT || aim_bits > I2S_BITS_PER_SAMPLE_32BIT) { - ESP_LOGE(I2S_TAG,"bits mustn't be greater than 32, src_bits %d aim_bits %d", src_bits, aim_bits); - return ESP_ERR_INVALID_ARG; + if (src_bits > I2S_BITS_PER_SAMPLE_32BIT + || aim_bits > I2S_BITS_PER_SAMPLE_32BIT) + { + ESP_LOGE (I2S_TAG, + "bits mustn't be greater than 32, src_bits %d aim_bits %d", + src_bits, aim_bits); + return ESP_ERR_INVALID_ARG; } - if ((src_bits == I2S_BITS_PER_SAMPLE_16BIT || src_bits == I2S_BITS_PER_SAMPLE_32BIT) && (size % 2 != 0)) { - ESP_LOGE(I2S_TAG,"size must be a even number while src_bits is even, src_bits %d size %d", src_bits, size); - return ESP_ERR_INVALID_ARG; + if ((src_bits == I2S_BITS_PER_SAMPLE_16BIT + || src_bits == I2S_BITS_PER_SAMPLE_32BIT) + && (size % 2 != 0)) + { + ESP_LOGE (I2S_TAG, + "size must be a even number while src_bits is even, src_bits " + "%d size %d", + src_bits, size); + return ESP_ERR_INVALID_ARG; } - if (src_bits == I2S_BITS_PER_SAMPLE_24BIT && (size % 3 != 0)) { - ESP_LOGE(I2S_TAG,"size must be a multiple of 3 while src_bits is 24, size %d", size); - return ESP_ERR_INVALID_ARG; + if (src_bits == I2S_BITS_PER_SAMPLE_24BIT && (size % 3 != 0)) + { + ESP_LOGE (I2S_TAG, + "size must be a multiple of 3 while src_bits is 24, size %d", + size); + return ESP_ERR_INVALID_ARG; } - src_bytes = src_bits / 8; - aim_bytes = aim_bits / 8; - zero_bytes = aim_bytes - src_bytes; - xSemaphoreTake(p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); - size = size * aim_bytes / src_bytes; - ESP_LOGD(I2S_TAG,"aim_bytes %d src_bytes %d size %d", aim_bytes, src_bytes, size); - while (size > 0) { - if (p_i2s_obj[i2s_num]->tx->rw_pos == p_i2s_obj[i2s_num]->tx->buf_size || p_i2s_obj[i2s_num]->tx->curr_ptr == NULL) { - if (xQueueReceive(p_i2s_obj[i2s_num]->tx->queue, &p_i2s_obj[i2s_num]->tx->curr_ptr, ticks_to_wait) == pdFALSE) { - break; + src_bytes = src_bits / 8; + aim_bytes = aim_bits / 8; + zero_bytes = aim_bytes - src_bytes; + xSemaphoreTake (p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); + size = size * aim_bytes / src_bytes; + ESP_LOGD (I2S_TAG, "aim_bytes %d src_bytes %d size %d", aim_bytes, src_bytes, + size); + while (size > 0) + { + if (p_i2s_obj[i2s_num]->tx->rw_pos == p_i2s_obj[i2s_num]->tx->buf_size + || p_i2s_obj[i2s_num]->tx->curr_ptr == NULL) + { + if (xQueueReceive (p_i2s_obj[i2s_num]->tx->queue, + &p_i2s_obj[i2s_num]->tx->curr_ptr, ticks_to_wait) + == pdFALSE) + { + break; } - p_i2s_obj[i2s_num]->tx->rw_pos = 0; + p_i2s_obj[i2s_num]->tx->rw_pos = 0; } - data_ptr = (char*)p_i2s_obj[i2s_num]->tx->curr_ptr; - data_ptr += p_i2s_obj[i2s_num]->tx->rw_pos; - bytes_can_write = p_i2s_obj[i2s_num]->tx->buf_size - p_i2s_obj[i2s_num]->tx->rw_pos; - if (bytes_can_write > (int)size) { - bytes_can_write = size; + data_ptr = (char *)p_i2s_obj[i2s_num]->tx->curr_ptr; + data_ptr += p_i2s_obj[i2s_num]->tx->rw_pos; + bytes_can_write + = p_i2s_obj[i2s_num]->tx->buf_size - p_i2s_obj[i2s_num]->tx->rw_pos; + if (bytes_can_write > (int)size) + { + bytes_can_write = size; } - tail = bytes_can_write % aim_bytes; - bytes_can_write = bytes_can_write - tail; + tail = bytes_can_write % aim_bytes; + bytes_can_write = bytes_can_write - tail; - memset(data_ptr, 0, bytes_can_write); - for (int j = 0; j < bytes_can_write; j += (aim_bytes - zero_bytes)) { - j += zero_bytes; - memcpy(&data_ptr[j], (const char *)(src + *bytes_written), aim_bytes - zero_bytes); - (*bytes_written) += (aim_bytes - zero_bytes); + memset (data_ptr, 0, bytes_can_write); + for (int j = 0; j < bytes_can_write; j += (aim_bytes - zero_bytes)) + { + j += zero_bytes; + memcpy (&data_ptr[j], (const char *)(src + *bytes_written), + aim_bytes - zero_bytes); + (*bytes_written) += (aim_bytes - zero_bytes); } - size -= bytes_can_write; - p_i2s_obj[i2s_num]->tx->rw_pos += bytes_can_write; + size -= bytes_can_write; + p_i2s_obj[i2s_num]->tx->rw_pos += bytes_can_write; } - xSemaphoreGive(p_i2s_obj[i2s_num]->tx->mux); - return ESP_OK; + xSemaphoreGive (p_i2s_obj[i2s_num]->tx->mux); + return ESP_OK; } -esp_err_t i2s_custom_read(i2s_port_t i2s_num, void *dest, size_t size, size_t *bytes_read, TickType_t ticks_to_wait) +esp_err_t +i2s_custom_read (i2s_port_t i2s_num, void *dest, size_t size, + size_t *bytes_read, TickType_t ticks_to_wait) { - char *data_ptr, *dest_byte; - int bytes_can_read; - *bytes_read = 0; - dest_byte = (char *)dest; - I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK((size < SOC_I2S_MAX_BUFFER_SIZE), "size is too large", ESP_ERR_INVALID_ARG); - I2S_CHECK((p_i2s_obj[i2s_num]->rx), "rx NULL", ESP_ERR_INVALID_ARG); - xSemaphoreTake(p_i2s_obj[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); + char *data_ptr, *dest_byte; + int bytes_can_read; + *bytes_read = 0; + dest_byte = (char *)dest; + I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + I2S_CHECK ((size < SOC_I2S_MAX_BUFFER_SIZE), "size is too large", + ESP_ERR_INVALID_ARG); + I2S_CHECK ((p_i2s_obj[i2s_num]->rx), "rx NULL", ESP_ERR_INVALID_ARG); + xSemaphoreTake (p_i2s_obj[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); #ifdef CONFIG_PM_ENABLE - esp_pm_lock_acquire(p_i2s_obj[i2s_num]->pm_lock); + esp_pm_lock_acquire (p_i2s_obj[i2s_num]->pm_lock); #endif - while (size > 0) { - if (p_i2s_obj[i2s_num]->rx->rw_pos == p_i2s_obj[i2s_num]->rx->buf_size || p_i2s_obj[i2s_num]->rx->curr_ptr == NULL) { - if (xQueueReceive(p_i2s_obj[i2s_num]->rx->queue, &p_i2s_obj[i2s_num]->rx->curr_ptr, ticks_to_wait) == pdFALSE) { - break; + while (size > 0) + { + if (p_i2s_obj[i2s_num]->rx->rw_pos == p_i2s_obj[i2s_num]->rx->buf_size + || p_i2s_obj[i2s_num]->rx->curr_ptr == NULL) + { + if (xQueueReceive (p_i2s_obj[i2s_num]->rx->queue, + &p_i2s_obj[i2s_num]->rx->curr_ptr, ticks_to_wait) + == pdFALSE) + { + break; } - p_i2s_obj[i2s_num]->rx->rw_pos = 0; + p_i2s_obj[i2s_num]->rx->rw_pos = 0; } - data_ptr = (char*)p_i2s_obj[i2s_num]->rx->curr_ptr; - data_ptr += p_i2s_obj[i2s_num]->rx->rw_pos; - bytes_can_read = p_i2s_obj[i2s_num]->rx->buf_size - p_i2s_obj[i2s_num]->rx->rw_pos; - if (bytes_can_read > (int)size) { - bytes_can_read = size; + data_ptr = (char *)p_i2s_obj[i2s_num]->rx->curr_ptr; + data_ptr += p_i2s_obj[i2s_num]->rx->rw_pos; + bytes_can_read + = p_i2s_obj[i2s_num]->rx->buf_size - p_i2s_obj[i2s_num]->rx->rw_pos; + if (bytes_can_read > (int)size) + { + bytes_can_read = size; } - memcpy(dest_byte, data_ptr, bytes_can_read); - size -= bytes_can_read; - dest_byte += bytes_can_read; - p_i2s_obj[i2s_num]->rx->rw_pos += bytes_can_read; - (*bytes_read) += bytes_can_read; + memcpy (dest_byte, data_ptr, bytes_can_read); + size -= bytes_can_read; + dest_byte += bytes_can_read; + p_i2s_obj[i2s_num]->rx->rw_pos += bytes_can_read; + (*bytes_read) += bytes_can_read; } #ifdef CONFIG_PM_ENABLE - esp_pm_lock_release(p_i2s_obj[i2s_num]->pm_lock); + esp_pm_lock_release (p_i2s_obj[i2s_num]->pm_lock); #endif - xSemaphoreGive(p_i2s_obj[i2s_num]->rx->mux); - return ESP_OK; + xSemaphoreGive (p_i2s_obj[i2s_num]->rx->mux); + return ESP_OK; } diff --git a/components/dsp_processor/dsp_processor.c b/components/dsp_processor/dsp_processor.c index 68fba6c..8aa6f51 100644 --- a/components/dsp_processor/dsp_processor.c +++ b/components/dsp_processor/dsp_processor.c @@ -14,13 +14,11 @@ #include "dsps_biquad_gen.h" #include "esp_log.h" -//#include "websocket_if.h" +#include "board_pins_config.h" #include "driver/dac.h" #include "driver/i2s.h" #include "dsp_processor.h" #include "hal/i2s_hal.h" -//#include "adc1_i2s_private.h" -#include "board_pins_config.h" #ifdef CONFIG_USE_BIQUAD_ASM #define BIQUAD dsps_biquad_f32_ae32 @@ -30,226 +28,212 @@ static const char *TAG = "dspProc"; -//static const uint8_t chunkDurationMs = CONFIG_WIRE_CHUNK_DURATION_MS; -//static const uint32_t sampleRate = CONFIG_PCM_SAMPLE_RATE; -//static const uint8_t channels = CONFIG_CHANNELS; -//static const uint8_t bitsPerSample = CONFIG_BITS_PER_SAMPLE; - -// TODO: allocate these buffers dynamically from heap -static float *sbuffer0 = NULL;//[1024]; -//static float sbuffer1[1024]; -//static float sbuffer2[1024]; -static float *sbufout0 = NULL;//[1024]; -//static float sbufout1[1024]; -//static float sbufout2[1024]; -static float *sbuftmp0 = NULL;//[1024]; -//static uint8_t dsp_audio[4 * 1024]; -//static uint8_t dsp_audio1[4 * 1024]; - -extern uint8_t muteCH[4]; +static float *sbuffer0 = NULL; +static float *sbufout0 = NULL; +static float *sbuftmp0 = NULL; static uint32_t currentSamplerate = 0; static uint32_t currentChunkDurationMs = 0; -ptype_t bq[8]; +static ptype_t bq[8]; - -int dsp_processor(char *audio, size_t chunk_size, dspFlows_t dspFlow) { +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; - // ESP_LOGI(TAG, - // "got data %p, %d, %u", audio, chunk_size, dspFlow); + if ((sbuffer0 == NULL) || (sbufout0 == NULL) || (sbuftmp0 == NULL)) + { + ESP_LOGE (TAG, "No Memory allocated for dsp_processor %p %p %p", + sbuffer0, sbufout0, sbuftmp0); - if ((sbuffer0 == NULL) || (sbufout0 == NULL) || (sbuftmp0 == NULL)) { - ESP_LOGE( - TAG, - "No Memory allocated for dsp_processor %p %p %p", sbuffer0, sbufout0, sbuftmp0); + return -1; + } - return -1; - } - -/* - for (uint16_t i = 0; i < len; i++) { - sbuffer0[i] = - dynamic_vol * 0.5 * - ((float)((int16_t)(audio[i * 4 + 1] << 8) + audio[i * 4 + 0])) / 32768; - sbuffer1[i] = - dynamic_vol * 0.5 * - ((float)((int16_t)(audio[i * 4 + 3] << 8) + audio[i * 4 + 2])) / 32768; - sbuffer2[i] = ((sbuffer0[i] / 2) + (sbuffer1[i] / 2)); - } - */ - - switch (dspFlow) { - case dspfStereo: { -// for (i = 0; i < len; i++) { -// audio[i * 4 + 0] = (muteCH[0] == 1) ? 0 : audio[i * 4 + 0]; -// audio[i * 4 + 1] = (muteCH[0] == 1) ? 0 : audio[i * 4 + 1]; -// audio[i * 4 + 2] = (muteCH[1] == 1) ? 0 : audio[i * 4 + 2]; -// audio[i * 4 + 3] = (muteCH[1] == 1) ? 0 : audio[i * 4 + 3]; -// } - - // mute is done through audio_hal_set_mute() - } break; - - case dspfBassBoost: { // CH0 low shelf 6dB @ 400Hz - // channel 0 - for (i = 0; i < len; i++) { - sbuffer0[i] = - dynamic_vol * 0.5 * - ((float)((int16_t)(audio[i * 4 + 1] << 8) + audio[i * 4 + 0])) / 32768; + switch (dspFlow) + { + case dspfStereo: + { } - BIQUAD(sbuffer0, sbufout0, len, bq[6].coeffs, bq[6].w); + break; - for (i = 0; i < len; i++) { - valint = (int16_t)(sbufout0[i] * 32768); + case dspfBassBoost: + { // CH0 low shelf 6dB @ 400Hz + // channel 0 + for (i = 0; i < len; i++) + { + sbuffer0[i] = dynamic_vol * 0.5 + * ((float)((int16_t) (audio[i * 4 + 1] << 8) + + audio[i * 4 + 0])) + / 32768; + } + BIQUAD (sbuffer0, sbufout0, len, bq[6].coeffs, bq[6].w); - audio[i * 4 + 0] = (valint & 0x00ff); - audio[i * 4 + 1] = ((valint & 0xff00) >> 8); + 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); + } + + // 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])) + / 32768; + } + BIQUAD (sbuffer0, sbufout0, len, bq[7].coeffs, bq[7].w); + + 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); + } } + break; - // 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])) / 32768; + case dspfBiamp: + { + // Process audio ch0 LOW PASS FILTER + for (i = 0; i < len; i++) + { + sbuffer0[i] = dynamic_vol * 0.5 + * ((float)((int16_t) (audio[i * 4 + 1] << 8) + + audio[i * 4 + 0])) + / 32768; + } + BIQUAD (sbuffer0, sbuftmp0, len, bq[0].coeffs, bq[0].w); + BIQUAD (sbuftmp0, sbufout0, len, bq[1].coeffs, bq[1].w); + + 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); + } + + // 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])) + / 32768; + } + BIQUAD (sbuffer0, sbuftmp0, len, bq[2].coeffs, bq[2].w); + BIQUAD (sbuftmp0, sbufout0, len, bq[3].coeffs, bq[3].w); + + 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); + } } - BIQUAD(sbuffer0, sbufout0, len, bq[7].coeffs, bq[7].w); + break; - 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); + case dspf2DOT1: + { // Process audio L + R LOW PASS FILTER + /* + BIQUAD(sbuffer2, sbuftmp0, len, bq[0].coeffs, bq[0].w); + BIQUAD(sbuftmp0, sbufout2, len, bq[1].coeffs, bq[1].w); + + // Process audio L HIGH PASS FILTER + BIQUAD(sbuffer0, sbuftmp0, len, bq[2].coeffs, bq[2].w); + BIQUAD(sbuftmp0, sbufout0, len, bq[3].coeffs, bq[3].w); + + // Process audio R HIGH PASS FILTER + BIQUAD(sbuffer1, sbuftmp0, len, bq[4].coeffs, bq[4].w); + BIQUAD(sbuftmp0, sbufout1, len, bq[5].coeffs, bq[5].w); + + int16_t valint[5]; + for (uint16_t i = 0; i < len; i++) { + valint[0] = + (muteCH[0] == 1) ? (int16_t)0 : (int16_t)(sbufout0[i] * + 32768); valint[1] = (muteCH[1] == 1) ? (int16_t)0 : + (int16_t)(sbufout1[i] * 32768); valint[2] = (muteCH[2] == 1) ? + (int16_t)0 : (int16_t)(sbufout2[i] * 32768); dsp_audio[i * 4 + 0] = + (valint[2] & 0xff); dsp_audio[i * 4 + 1] = ((valint[2] & 0xff00) >> + 8); dsp_audio[i * 4 + 2] = 0; dsp_audio[i * 4 + 3] = 0; + + dsp_audio1[i * 4 + 0] = (valint[0] & 0xff); + dsp_audio1[i * 4 + 1] = ((valint[0] & 0xff00) >> 8); + dsp_audio1[i * 4 + 2] = (valint[1] & 0xff); + dsp_audio1[i * 4 + 3] = ((valint[1] & 0xff00) >> 8); + } + + // TODO: this copy could be avoided if dsp_audio buffers are + // allocated dynamically and pointers are exchanged after + // audio was freed + memcpy(audio, dsp_audio, chunk_size); + + ESP_LOGW(TAG, "Don't know what to do with dsp_audio1"); + */ + ESP_LOGW (TAG, "dspf2DOT1, not implemented yet, using stereo instead"); } + break; - } break; + case dspfFunkyHonda: + { // Process audio L + R LOW PASS FILTER + /* + BIQUAD(sbuffer2, sbuftmp0, len, bq[0].coeffs, bq[0].w); + BIQUAD(sbuftmp0, sbufout2, len, bq[1].coeffs, bq[1].w); - case dspfBiamp: { - // Process audio ch0 LOW PASS FILTER - for (i = 0; i < len; i++) { - sbuffer0[i] = - dynamic_vol * 0.5 * - ((float)((int16_t)(audio[i * 4 + 1] << 8) + audio[i * 4 + 0])) / 32768; - } - BIQUAD(sbuffer0, sbuftmp0, len, bq[0].coeffs, bq[0].w); - BIQUAD(sbuftmp0, sbufout0, len, bq[1].coeffs, bq[1].w); + // Process audio L HIGH PASS FILTER + BIQUAD(sbuffer0, sbuftmp0, len, bq[2].coeffs, bq[2].w); + BIQUAD(sbuftmp0, sbufout0, len, bq[3].coeffs, bq[3].w); - 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); + // Process audio R HIGH PASS FILTER + BIQUAD(sbuffer1, sbuftmp0, len, bq[4].coeffs, bq[4].w); + BIQUAD(sbuftmp0, sbufout1, len, bq[5].coeffs, bq[5].w); + + uint16_t scale = 16384; // 32768 + int16_t valint[5]; + for (uint16_t i = 0; i < len; i++) { + valint[0] = + (muteCH[0] == 1) ? (int16_t)0 : (int16_t)(sbufout0[i] * scale); + valint[1] = + (muteCH[1] == 1) ? (int16_t)0 : (int16_t)(sbufout1[i] * scale); + valint[2] = + (muteCH[2] == 1) ? (int16_t)0 : (int16_t)(sbufout2[i] * scale); + valint[3] = valint[0] + valint[2]; + valint[4] = -valint[2]; + valint[5] = -valint[1] - valint[2]; + dsp_audio[i * 4 + 0] = (valint[3] & 0xff); + dsp_audio[i * 4 + 1] = ((valint[3] & 0xff00) >> 8); + dsp_audio[i * 4 + 2] = (valint[2] & 0xff); + dsp_audio[i * 4 + 3] = ((valint[2] & 0xff00) >> 8); + + dsp_audio1[i * 4 + 0] = (valint[4] & 0xff); + dsp_audio1[i * 4 + 1] = ((valint[4] & 0xff00) >> 8); + dsp_audio1[i * 4 + 2] = (valint[5] & 0xff); + dsp_audio1[i * 4 + 3] = ((valint[5] & 0xff00) >> 8); + } + + // TODO: this copy could be avoided if dsp_audio buffers are + // allocated dynamically and pointers are exchanged after + // audio was freed + memcpy(audio, dsp_audio, chunk_size); + + ESP_LOGW(TAG, "Don't know what to do with dsp_audio1"); + */ + ESP_LOGW (TAG, + "dspfFunkyHonda, not implemented yet, using stereo instead"); } + break; - // 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])) / 32768; + default: + { } - BIQUAD(sbuffer0, sbuftmp0, len, bq[2].coeffs, bq[2].w); - BIQUAD(sbuftmp0, sbufout0, len, bq[3].coeffs, bq[3].w); - - 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); - } - - } break; - - case dspf2DOT1: { // Process audio L + R LOW PASS FILTER - /* - BIQUAD(sbuffer2, sbuftmp0, len, bq[0].coeffs, bq[0].w); - BIQUAD(sbuftmp0, sbufout2, len, bq[1].coeffs, bq[1].w); - - // Process audio L HIGH PASS FILTER - BIQUAD(sbuffer0, sbuftmp0, len, bq[2].coeffs, bq[2].w); - BIQUAD(sbuftmp0, sbufout0, len, bq[3].coeffs, bq[3].w); - - // Process audio R HIGH PASS FILTER - BIQUAD(sbuffer1, sbuftmp0, len, bq[4].coeffs, bq[4].w); - BIQUAD(sbuftmp0, sbufout1, len, bq[5].coeffs, bq[5].w); - - int16_t valint[5]; - for (uint16_t i = 0; i < len; i++) { - valint[0] = - (muteCH[0] == 1) ? (int16_t)0 : (int16_t)(sbufout0[i] * 32768); - valint[1] = - (muteCH[1] == 1) ? (int16_t)0 : (int16_t)(sbufout1[i] * 32768); - valint[2] = - (muteCH[2] == 1) ? (int16_t)0 : (int16_t)(sbufout2[i] * 32768); - dsp_audio[i * 4 + 0] = (valint[2] & 0xff); - dsp_audio[i * 4 + 1] = ((valint[2] & 0xff00) >> 8); - dsp_audio[i * 4 + 2] = 0; - dsp_audio[i * 4 + 3] = 0; - - dsp_audio1[i * 4 + 0] = (valint[0] & 0xff); - dsp_audio1[i * 4 + 1] = ((valint[0] & 0xff00) >> 8); - dsp_audio1[i * 4 + 2] = (valint[1] & 0xff); - dsp_audio1[i * 4 + 3] = ((valint[1] & 0xff00) >> 8); - } - - // TODO: this copy could be avoided if dsp_audio buffers are - // allocated dynamically and pointers are exchanged after - // audio was freed - memcpy(audio, dsp_audio, chunk_size); - - ESP_LOGW(TAG, "Don't know what to do with dsp_audio1"); -*/ - ESP_LOGW(TAG, "dspf2DOT1, not implemented yet, using stereo instead"); - } break; - - case dspfFunkyHonda: { // Process audio L + R LOW PASS FILTER - /* - BIQUAD(sbuffer2, sbuftmp0, len, bq[0].coeffs, bq[0].w); - BIQUAD(sbuftmp0, sbufout2, len, bq[1].coeffs, bq[1].w); - - // Process audio L HIGH PASS FILTER - BIQUAD(sbuffer0, sbuftmp0, len, bq[2].coeffs, bq[2].w); - BIQUAD(sbuftmp0, sbufout0, len, bq[3].coeffs, bq[3].w); - - // Process audio R HIGH PASS FILTER - BIQUAD(sbuffer1, sbuftmp0, len, bq[4].coeffs, bq[4].w); - BIQUAD(sbuftmp0, sbufout1, len, bq[5].coeffs, bq[5].w); - - uint16_t scale = 16384; // 32768 - int16_t valint[5]; - for (uint16_t i = 0; i < len; i++) { - valint[0] = - (muteCH[0] == 1) ? (int16_t)0 : (int16_t)(sbufout0[i] * scale); - valint[1] = - (muteCH[1] == 1) ? (int16_t)0 : (int16_t)(sbufout1[i] * scale); - valint[2] = - (muteCH[2] == 1) ? (int16_t)0 : (int16_t)(sbufout2[i] * scale); - valint[3] = valint[0] + valint[2]; - valint[4] = -valint[2]; - valint[5] = -valint[1] - valint[2]; - dsp_audio[i * 4 + 0] = (valint[3] & 0xff); - dsp_audio[i * 4 + 1] = ((valint[3] & 0xff00) >> 8); - dsp_audio[i * 4 + 2] = (valint[2] & 0xff); - dsp_audio[i * 4 + 3] = ((valint[2] & 0xff00) >> 8); - - dsp_audio1[i * 4 + 0] = (valint[4] & 0xff); - dsp_audio1[i * 4 + 1] = ((valint[4] & 0xff00) >> 8); - dsp_audio1[i * 4 + 2] = (valint[5] & 0xff); - dsp_audio1[i * 4 + 3] = ((valint[5] & 0xff00) >> 8); - } - - // TODO: this copy could be avoided if dsp_audio buffers are - // allocated dynamically and pointers are exchanged after - // audio was freed - memcpy(audio, dsp_audio, chunk_size); - - ESP_LOGW(TAG, "Don't know what to do with dsp_audio1"); - */ - ESP_LOGW(TAG, "dspfFunkyHonda, not implemented yet, using stereo instead"); - - } break; - - default: { } break; } + break; + } return 0; } @@ -268,117 +252,134 @@ int dsp_processor(char *audio, size_t chunk_size, dspFlows_t dspFlow) { // ---------------------------------------- // Fixed 2x2 biquad flow Xover for biAmp systems // Interface for cross over frequency and level -void dsp_setup_flow(double freq, uint32_t samplerate, uint32_t chunkDurationMs) { +void +dsp_setup_flow (double freq, uint32_t samplerate, uint32_t chunkDurationMs) +{ float f = freq / samplerate / 2.0; uint16_t len = (samplerate * chunkDurationMs / 1000); - if (((currentSamplerate == samplerate) && (currentChunkDurationMs == chunkDurationMs)) || - (samplerate == 0) || (chunkDurationMs == 0)) - { - return; - } + if (((currentSamplerate == samplerate) + && (currentChunkDurationMs == chunkDurationMs)) + || (samplerate == 0) || (chunkDurationMs == 0)) + { + return; + } currentSamplerate = samplerate; currentChunkDurationMs = chunkDurationMs; - bq[0] = (ptype_t){LPF, f, 0, 0.707, NULL, NULL, {0, 0, 0, 0, 0}, {0, 0}}; - bq[1] = (ptype_t){LPF, f, 0, 0.707, NULL, NULL, {0, 0, 0, 0, 0}, {0, 0}}; - bq[2] = (ptype_t){HPF, f, 0, 0.707, NULL, NULL, {0, 0, 0, 0, 0}, {0, 0}}; - bq[3] = (ptype_t){HPF, f, 0, 0.707, NULL, NULL, {0, 0, 0, 0, 0}, {0, 0}}; - bq[4] = (ptype_t){HPF, f, 0, 0.707, NULL, NULL, {0, 0, 0, 0, 0}, {0, 0}}; - bq[5] = (ptype_t){HPF, f, 0, 0.707, NULL, NULL, {0, 0, 0, 0, 0}, {0, 0}}; - bq[6] = (ptype_t){LOWSHELF, f, 6, 0.707, NULL, NULL, {0, 0, 0, 0, 0}, {0, 0}}; - bq[7] = (ptype_t){LOWSHELF, f, 6, 0.707, NULL, NULL, {0, 0, 0, 0, 0}, {0, 0}}; + bq[0] + = (ptype_t){ LPF, f, 0, 0.707, NULL, NULL, { 0, 0, 0, 0, 0 }, { 0, 0 } }; + bq[1] + = (ptype_t){ LPF, f, 0, 0.707, NULL, NULL, { 0, 0, 0, 0, 0 }, { 0, 0 } }; + bq[2] + = (ptype_t){ HPF, f, 0, 0.707, NULL, NULL, { 0, 0, 0, 0, 0 }, { 0, 0 } }; + bq[3] + = (ptype_t){ HPF, f, 0, 0.707, NULL, NULL, { 0, 0, 0, 0, 0 }, { 0, 0 } }; + bq[4] + = (ptype_t){ HPF, f, 0, 0.707, NULL, NULL, { 0, 0, 0, 0, 0 }, { 0, 0 } }; + bq[5] + = (ptype_t){ HPF, f, 0, 0.707, NULL, NULL, { 0, 0, 0, 0, 0 }, { 0, 0 } }; + bq[6] = (ptype_t){ LOWSHELF, f, 6, 0.707, NULL, NULL, + { 0, 0, 0, 0, 0 }, { 0, 0 } }; + bq[7] = (ptype_t){ LOWSHELF, f, 6, 0.707, NULL, NULL, + { 0, 0, 0, 0, 0 }, { 0, 0 } }; -// pnode_t *aflow = NULL; -// aflow = malloc(sizeof(pnode_t)); -// if (aflow == NULL) { -// printf("Could not create node"); -// } - - for (uint8_t n = 0; n <= 7; n++) { - switch (bq[n].filtertype) { - case LOWSHELF: - dsps_biquad_gen_lowShelf_f32(bq[n].coeffs, bq[n].freq, bq[n].gain, - bq[n].q); - break; - case LPF: - dsps_biquad_gen_lpf_f32(bq[n].coeffs, bq[n].freq, bq[n].q); - break; - case HPF: - dsps_biquad_gen_hpf_f32(bq[n].coeffs, bq[n].freq, bq[n].q); - break; - default: - break; + for (uint8_t n = 0; n <= 7; n++) + { + switch (bq[n].filtertype) + { + case LOWSHELF: + dsps_biquad_gen_lowShelf_f32 (bq[n].coeffs, bq[n].freq, bq[n].gain, + bq[n].q); + break; + case LPF: + dsps_biquad_gen_lpf_f32 (bq[n].coeffs, bq[n].freq, bq[n].q); + break; + case HPF: + dsps_biquad_gen_hpf_f32 (bq[n].coeffs, bq[n].freq, bq[n].q); + break; + default: + break; + } + // for (uint8_t i = 0; i <= 4; i++) { + // printf("%.6f ", bq[n].coeffs[i]); + // } + // printf("\n"); } -// for (uint8_t i = 0; i <= 4; i++) { -// printf("%.6f ", bq[n].coeffs[i]); -// } -// printf("\n"); - } - if (sbuffer0 != NULL) { - free(sbuffer0); - sbuffer0 = NULL; - } - if (sbufout0 != NULL) { - free(sbufout0); - sbufout0 = NULL; - } - if (sbuftmp0 != NULL) { - free(sbuftmp0); - sbuftmp0 = NULL; - } + if (sbuffer0 != NULL) + { + free (sbuffer0); + sbuffer0 = NULL; + } + if (sbufout0 != NULL) + { + free (sbufout0); + sbufout0 = NULL; + } + if (sbuftmp0 != NULL) + { + free (sbuftmp0); + sbuftmp0 = NULL; + } - sbuffer0 = (float *)heap_caps_malloc(sizeof(float) * len, MALLOC_CAP_8BIT); - sbufout0 = (float *)heap_caps_malloc(sizeof(float) * len, MALLOC_CAP_8BIT); - sbuftmp0 = (float *)heap_caps_malloc(sizeof(float) * len, MALLOC_CAP_8BIT); - if ((sbuffer0 == NULL) || (sbufout0 == NULL) || (sbuftmp0 == NULL)) { - ESP_LOGE( - TAG, - "Failed to allocate initial memory for dsp_processor %p %p %p", sbuffer0, sbufout0, sbuftmp0); + sbuffer0 = (float *)heap_caps_malloc (sizeof (float) * len, MALLOC_CAP_8BIT); + sbufout0 = (float *)heap_caps_malloc (sizeof (float) * len, MALLOC_CAP_8BIT); + sbuftmp0 = (float *)heap_caps_malloc (sizeof (float) * len, MALLOC_CAP_8BIT); + if ((sbuffer0 == NULL) || (sbufout0 == NULL) || (sbuftmp0 == NULL)) + { + ESP_LOGE (TAG, + "Failed to allocate initial memory for dsp_processor %p %p %p", + sbuffer0, sbufout0, sbuftmp0); - if (sbuffer0) { - free(sbuffer0); - } - if (sbufout0) { - free(sbufout0); - } - if (sbuftmp0) { - free(sbuftmp0); - } - } - else { - ESP_LOGI( - TAG, - "GOT memory for dsp_processor %p %p", sbuffer0, sbufout0); - } + if (sbuffer0) + { + free (sbuffer0); + } + if (sbufout0) + { + free (sbufout0); + } + if (sbuftmp0) + { + free (sbuftmp0); + } + } + else + { + ESP_LOGI (TAG, "GOT memory for dsp_processor %p %p", sbuffer0, sbufout0); + } } -void dsp_set_xoverfreq(uint8_t freqh, uint8_t freql, uint32_t samplerate) { +void +dsp_set_xoverfreq (uint8_t freqh, uint8_t freql, uint32_t samplerate) +{ float freq = freqh * 256 + freql; -// printf("%f\n", freq); + // printf("%f\n", freq); float f = freq / samplerate / 2.; - for (int8_t n = 0; n <= 5; n++) { - bq[n].freq = f; - switch (bq[n].filtertype) { - case LPF: -// for (uint8_t i = 0; i <= 4; i++) { -// printf("%.6f ", bq[n].coeffs[i]); -// } -// printf("\n"); - dsps_biquad_gen_lpf_f32(bq[n].coeffs, bq[n].freq, bq[n].q); -// for (uint8_t i = 0; i <= 4; i++) { -// printf("%.6f ", bq[n].coeffs[i]); -// } -// printf("%f \n", bq[n].freq); - break; - case HPF: - dsps_biquad_gen_hpf_f32(bq[n].coeffs, bq[n].freq, bq[n].q); - break; - default: - break; + for (int8_t n = 0; n <= 5; n++) + { + bq[n].freq = f; + switch (bq[n].filtertype) + { + case LPF: + // for (uint8_t i = 0; i <= 4; i++) { + // printf("%.6f ", bq[n].coeffs[i]); + // } + // printf("\n"); + dsps_biquad_gen_lpf_f32 (bq[n].coeffs, bq[n].freq, bq[n].q); + // for (uint8_t i = 0; i <= 4; i++) { + // printf("%.6f ", bq[n].coeffs[i]); + // } + // printf("%f \n", bq[n].freq); + break; + case HPF: + dsps_biquad_gen_hpf_f32 (bq[n].coeffs, bq[n].freq, bq[n].q); + break; + default: + break; + } } - } } #endif diff --git a/components/esp-dsp/modules/math/sqrt/float/dsps_sqrt_f32_ansi.c b/components/esp-dsp/modules/math/sqrt/float/dsps_sqrt_f32_ansi.c index 68913a4..dc8b1c7 100644 --- a/components/esp-dsp/modules/math/sqrt/float/dsps_sqrt_f32_ansi.c +++ b/components/esp-dsp/modules/math/sqrt/float/dsps_sqrt_f32_ansi.c @@ -13,37 +13,43 @@ // limitations under the License. #include "dsps_sqrt.h" +#include "esp_err.h" #include - -inline float dsps_sqrtf_f32_ansi(float f) +inline float +dsps_sqrtf_f32_ansi (float f) { - const int result = 0x1fbb4000 + (*(int*)&f >> 1); - return *(float*)&result; + const int result = 0x1fbb4000 + (*(int *)&f >> 1); + return *(float *)&result; } -esp_err_t dsps_sqrt_f32_ansi(const float *input, float *output, int len) +esp_err_t +dsps_sqrt_f32_ansi (const float *input, float *output, int len) { - if (NULL == input) return ESP_ERR_DSP_PARAM_OUTOFRANGE; - if (NULL == output) return ESP_ERR_DSP_PARAM_OUTOFRANGE; + if (NULL == input) + return ESP_ERR_DSP_PARAM_OUTOFRANGE; + if (NULL == output) + return ESP_ERR_DSP_PARAM_OUTOFRANGE; - for (int i = 0 ; i < len ; i++) { - output[i] = dsps_sqrtf_f32_ansi(input[i]); + for (int i = 0; i < len; i++) + { + output[i] = dsps_sqrtf_f32_ansi (input[i]); } - return ESP_OK; + return ESP_OK; } -float dsps_inverted_sqrtf_f32_ansi(float data ) -{ - const float x2 = data * 0.5F; - const float threehalfs = 1.5F; +float +dsps_inverted_sqrtf_f32_ansi (float data) +{ + const float x2 = data * 0.5F; + const float threehalfs = 1.5F; - union { - float f; - uint32_t i; - } conv = {data}; // member 'f' set to value of 'data'. - conv.i = 0x5f3759df - ( conv.i >> 1 ); - conv.f *= ( threehalfs - ( x2 * conv.f * conv.f ) ); - return conv.f; + union + { + float f; + uint32_t i; + } conv = { data }; // member 'f' set to value of 'data'. + conv.i = 0x5f3759df - (conv.i >> 1); + conv.f *= (threehalfs - (x2 * conv.f * conv.f)); + return conv.f; } - diff --git a/components/lightsnapcast/include/player.h b/components/lightsnapcast/include/player.h index 2f030c3..65a5391 100644 --- a/components/lightsnapcast/include/player.h +++ b/components/lightsnapcast/include/player.h @@ -1,60 +1,69 @@ #ifndef __PLAYER_H__ #define __PLAYER_H__ -#include "freertos/FreeRTOS.h" #include "esp_types.h" +#include "freertos/FreeRTOS.h" +#include "i2s.h" #include "sdkconfig.h" #include "snapcast.h" -#include "i2s.h" #define I2S_PORT I2S_NUM_0 -extern volatile int64_t clientDacLatency; - #define LATENCY_MEDIAN_FILTER_LEN 99 #define SHORT_BUFFER_LEN 29 typedef struct pcm_chunk_fragment pcm_chunk_fragment_t; -struct pcm_chunk_fragment { +struct pcm_chunk_fragment +{ size_t size; char *payload; pcm_chunk_fragment_t *nextFragment; }; -typedef struct pcm_chunk_message { +typedef struct pcm_chunk_message +{ tv_t timestamp; pcm_chunk_fragment_t *fragment; } pcm_chunk_message_t; -typedef enum codec_type_e { NONE, PCM, FLAC, OGG, OPUS } codec_type_t; +typedef enum codec_type_e +{ + NONE, + PCM, + FLAC, + OGG, + OPUS +} codec_type_t; -typedef struct snapcastSetting_s { - uint32_t buffer_ms; - uint32_t chunkDuration_ms; +typedef struct snapcastSetting_s +{ + uint32_t buffer_ms; + uint32_t chunkDuration_ms; + int32_t clientDacLatency_ms; - codec_type_t codec; - int32_t sampleRate; - uint8_t channels; - i2s_bits_per_sample_t bits; + codec_type_t codec; + int32_t sampleRate; + uint8_t channels; + i2s_bits_per_sample_t bits; - bool muted; - uint32_t volume; + bool muted; + uint32_t volume; } snapcastSetting_t; -QueueHandle_t init_player(void); -int deinit_player(void); +QueueHandle_t init_player (void); +int deinit_player (void); -int8_t insert_pcm_chunk(wire_chunk_message_t *decodedWireChunk); -int8_t free_pcm_chunk(pcm_chunk_message_t *pcmChunk); +int8_t insert_pcm_chunk (wire_chunk_message_t *decodedWireChunk); +int8_t free_pcm_chunk (pcm_chunk_message_t *pcmChunk); -int8_t player_latency_insert(int64_t newValue); -int8_t player_notify_buffer_ms(uint32_t ms); -int8_t player_send_snapcast_setting(snapcastSetting_t setting); +int8_t player_latency_insert (int64_t newValue); +int8_t player_notify_buffer_ms (uint32_t ms); +int8_t player_send_snapcast_setting (snapcastSetting_t setting); -int8_t reset_latency_buffer(void); -int8_t latency_buffer_full(void); -int8_t get_diff_to_server(int64_t *tDiff); -int8_t server_now(int64_t *sNow); +int8_t reset_latency_buffer (void); +int8_t latency_buffer_full (void); +int8_t get_diff_to_server (int64_t *tDiff); +int8_t server_now (int64_t *sNow); -#endif // __PLAYER_H__ +#endif // __PLAYER_H__ diff --git a/components/lightsnapcast/player.c b/components/lightsnapcast/player.c index 27f073a..fb9bcad 100644 --- a/components/lightsnapcast/player.c +++ b/components/lightsnapcast/player.c @@ -2,9 +2,9 @@ * */ -#include -#include #include +#include +#include #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" @@ -14,7 +14,6 @@ #include "soc/rtc.h" - #include "driver/timer.h" #include "MedianFilter.h" @@ -22,7 +21,7 @@ #include "player.h" #include "snapcast.h" -#include "i2s.h" // use custom i2s driver instead of IDF version +#include "i2s.h" // use custom i2s driver instead of IDF version #define SYNC_TASK_PRIORITY 20 #define SYNC_TASK_CORE_ID tskNO_AFFINITY @@ -30,14 +29,15 @@ static const char *TAG = "PLAYER"; /** - * @brief Pre define APLL parameters, save compute time. They are calculated in player_setup_i2s() - * | bits_per_sample | rate | sdm0 | sdm1 | sdm2 | odir + * @brief Pre define APLL parameters, save compute time. They are calculated in + * player_setup_i2s() | bits_per_sample | rate | sdm0 | sdm1 | sdm2 | odir * * apll_freq = xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536)/((o_div + * 2) * 2) I2S bit clock is (apll_freq / 16) */ -static int apll_normal_predefine[6] = {0, 0, 0, 0, 0, 0}; -static int apll_corr_predefine[][6] = {{0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}}; +static int apll_normal_predefine[6] = { 0, 0, 0, 0, 0, 0 }; +static int apll_corr_predefine[][6] + = { { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 } }; static SemaphoreHandle_t latencyBufSemaphoreHandle = NULL; @@ -51,15 +51,15 @@ static int64_t latencyToServer = 0; static sMedianFilter_t shortMedianFilter; static sMedianNode_t shortMedianBuffer[SHORT_BUFFER_LEN]; -static int8_t currentDir = 0; //!< current apll direction, see apll_adjust() +static int8_t currentDir = 0; //!< current apll direction, see apll_adjust() static QueueHandle_t pcmChunkQueueHandle = NULL; -#define PCM_CHNK_QUEUE_LENGTH \ - 50 // TODO: one chunk is hardcoded to 20ms, change it to be dynamically - // adjustable. +#define PCM_CHNK_QUEUE_LENGTH \ + 50 // TODO: one chunk is hardcoded to 20ms, change it to be dynamically + // adjustable. static StaticQueue_t pcmChunkQueue; -static uint8_t pcmChunkQueueStorageArea[PCM_CHNK_QUEUE_LENGTH * - sizeof(pcm_chunk_message_t *)]; +static uint8_t pcmChunkQueueStorageArea[PCM_CHNK_QUEUE_LENGTH + * sizeof (pcm_chunk_message_t *)]; static TaskHandle_t syncTaskHandle = NULL; @@ -72,9 +72,9 @@ static uint32_t i2sDmaBufMaxLen; snapcastSetting_t currentSnapcastSetting; -static void tg0_timer_init(void); -static void tg0_timer_deinit(void); -static void snapcast_sync_task(void *pvParameters); +static void tg0_timer_init (void); +static void tg0_timer_deinit (void); +static void snapcast_sync_task (void *pvParameters); /* #define CONFIG_MASTER_I2S_BCK_PIN 5 @@ -85,29 +85,38 @@ static void snapcast_sync_task(void *pvParameters); #define CONFIG_SLAVE_I2S_DATAOUT_PIN 5 */ -static esp_err_t player_setup_i2s(i2s_port_t i2sNum, snapcastSetting_t *setting) { +static esp_err_t +player_setup_i2s (i2s_port_t i2sNum, snapcastSetting_t *setting) +{ int chunkInFrames; int __dmaBufCnt; int __dmaBufLen; const int __dmaBufMaxLen = 1024; int m_scale = 8, fi2s_clk; - chunkInBytes = (setting->chunkDuration_ms * setting->sampleRate * setting->channels * (setting->bits / 8)) / 1000; + chunkInBytes = (setting->chunkDuration_ms * setting->sampleRate + * setting->channels * (setting->bits / 8)) + / 1000; chunkInFrames = chunkInBytes / (setting->channels * (setting->bits / 8)); __dmaBufCnt = 1; __dmaBufLen = chunkInFrames; - while ((__dmaBufLen >= __dmaBufMaxLen) || (__dmaBufCnt <= 1)) { - if ((__dmaBufLen % 2) == 0) { - __dmaBufCnt *= 2; - __dmaBufLen /= 2; - } else { - ESP_LOGE(TAG, - "player_setup_i2s: Can't setup i2s with this configuration"); + while ((__dmaBufLen >= __dmaBufMaxLen) || (__dmaBufCnt <= 1)) + { + if ((__dmaBufLen % 2) == 0) + { + __dmaBufCnt *= 2; + __dmaBufLen /= 2; + } + else + { + ESP_LOGE ( + TAG, + "player_setup_i2s: Can't setup i2s with this configuration"); - return -1; + return -1; + } } - } i2sDmaBufCnt = __dmaBufCnt; i2sDmaBufMaxLen = __dmaBufLen; @@ -116,89 +125,119 @@ static esp_err_t player_setup_i2s(i2s_port_t i2sNum, snapcastSetting_t *setting) apll_normal_predefine[0] = setting->bits; apll_normal_predefine[1] = setting->sampleRate; - if (i2s_apll_calculate_fi2s(fi2s_clk, setting->bits, &apll_normal_predefine[2], &apll_normal_predefine[3], &apll_normal_predefine[4], &apll_normal_predefine[5]) != ESP_OK) { - ESP_LOGE(TAG, "ERROR, fi2s_clk"); - } + if (i2s_apll_calculate_fi2s ( + fi2s_clk, setting->bits, &apll_normal_predefine[2], + &apll_normal_predefine[3], &apll_normal_predefine[4], + &apll_normal_predefine[5]) + != ESP_OK) + { + ESP_LOGE (TAG, "ERROR, fi2s_clk"); + } apll_corr_predefine[0][0] = setting->bits; apll_corr_predefine[0][1] = setting->sampleRate * 1.001; - if (i2s_apll_calculate_fi2s(fi2s_clk * 1.001, setting->bits, &apll_corr_predefine[0][2], &apll_corr_predefine[0][3], &apll_corr_predefine[0][4], &apll_corr_predefine[0][5]) != ESP_OK) { - ESP_LOGE(TAG, "ERROR, fi2s_clk * 1.001"); - } + if (i2s_apll_calculate_fi2s ( + fi2s_clk * 1.001, setting->bits, &apll_corr_predefine[0][2], + &apll_corr_predefine[0][3], &apll_corr_predefine[0][4], + &apll_corr_predefine[0][5]) + != ESP_OK) + { + ESP_LOGE (TAG, "ERROR, fi2s_clk * 1.001"); + } apll_corr_predefine[1][0] = setting->bits; apll_corr_predefine[1][1] = setting->sampleRate * 0.999; - if (i2s_apll_calculate_fi2s(fi2s_clk * 0.999, setting->bits, &apll_corr_predefine[1][2], &apll_corr_predefine[1][3], &apll_corr_predefine[1][4], &apll_corr_predefine[1][5]) != ESP_OK) { - ESP_LOGE(TAG, "ERROR, fi2s_clk * 0.999"); - } + if (i2s_apll_calculate_fi2s ( + fi2s_clk * 0.999, setting->bits, &apll_corr_predefine[1][2], + &apll_corr_predefine[1][3], &apll_corr_predefine[1][4], + &apll_corr_predefine[1][5]) + != ESP_OK) + { + ESP_LOGE (TAG, "ERROR, fi2s_clk * 0.999"); + } - ESP_LOGI(TAG, "player_setup_i2s: dma_buf_len is %d, dma_buf_count is %d", - i2sDmaBufMaxLen, i2sDmaBufCnt); + ESP_LOGI (TAG, "player_setup_i2s: dma_buf_len is %d, dma_buf_count is %d", + i2sDmaBufMaxLen, i2sDmaBufCnt); i2s_config_t i2s_config0 = { - .mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX - .sample_rate = setting->sampleRate, - .bits_per_sample = setting->bits, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // 2-channels - .communication_format = I2S_COMM_FORMAT_STAND_I2S, - .dma_buf_count = i2sDmaBufCnt, - .dma_buf_len = i2sDmaBufMaxLen, - .intr_alloc_flags = 1, // Default interrupt priority - .use_apll = true, - .fixed_mclk = 0, - .tx_desc_auto_clear = true // Auto clear tx descriptor on underflow + .mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX + .sample_rate = setting->sampleRate, + .bits_per_sample = setting->bits, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // 2-channels + .communication_format = I2S_COMM_FORMAT_STAND_I2S, + .dma_buf_count = i2sDmaBufCnt, + .dma_buf_len = i2sDmaBufMaxLen, + .intr_alloc_flags = 1, // Default interrupt priority + .use_apll = true, + .fixed_mclk = 0, + .tx_desc_auto_clear = true // Auto clear tx descriptor on underflow }; i2s_pin_config_t pin_config0; - get_i2s_pins(i2sNum, &pin_config0); + get_i2s_pins (i2sNum, &pin_config0); - i2s_custom_driver_uninstall(i2sNum); - i2s_custom_driver_install(i2sNum, &i2s_config0, 0, NULL); - i2s_custom_set_pin(i2sNum, &pin_config0); + i2s_custom_driver_uninstall (i2sNum); + i2s_custom_driver_install (i2sNum, &i2s_config0, 0, NULL); + i2s_custom_set_pin (i2sNum, &pin_config0); return 0; } // ensure this is called after http_task was killed! -int deinit_player(void) { +int +deinit_player (void) +{ int ret = 0; pcm_chunk_message_t *chnk = NULL; // stop the task - if (syncTaskHandle == NULL) { - ESP_LOGW(TAG, "no sync task created?"); - } else { - vTaskDelete(syncTaskHandle); - } - - if (pcmChunkQueueHandle == NULL) { - ESP_LOGW(TAG, "no pcm chunk queue created?"); - } else { - // free all allocated memory - while (uxQueueMessagesWaiting(pcmChunkQueueHandle)) { - ret = xQueueReceive(pcmChunkQueueHandle, &chnk, pdMS_TO_TICKS(2000)); - if (ret != pdFAIL) { - if (chnk != NULL) { - free_pcm_chunk(chnk); - } - } + if (syncTaskHandle == NULL) + { + ESP_LOGW (TAG, "no sync task created?"); + } + else + { + vTaskDelete (syncTaskHandle); } - // delete the queue - vQueueDelete(pcmChunkQueueHandle); - pcmChunkQueueHandle = NULL; - } + if (pcmChunkQueueHandle == NULL) + { + ESP_LOGW (TAG, "no pcm chunk queue created?"); + } + else + { + // free all allocated memory + while (uxQueueMessagesWaiting (pcmChunkQueueHandle)) + { + ret = xQueueReceive (pcmChunkQueueHandle, &chnk, + pdMS_TO_TICKS (2000)); + if (ret != pdFAIL) + { + if (chnk != NULL) + { + free_pcm_chunk (chnk); + } + } + } - if (latencyBufSemaphoreHandle == NULL) { - ESP_LOGW(TAG, "no latency buffer semaphore created?"); - } else { - vSemaphoreDelete(latencyBufSemaphoreHandle); - latencyBufSemaphoreHandle = NULL; - } + // delete the queue + vQueueDelete (pcmChunkQueueHandle); + pcmChunkQueueHandle = NULL; + } - tg0_timer_deinit(); + if (latencyBufSemaphoreHandle == NULL) + { + ESP_LOGW (TAG, "no latency buffer semaphore created?"); + } + else + { + vSemaphoreDelete (latencyBufSemaphoreHandle); + latencyBufSemaphoreHandle = NULL; + } - ESP_LOGI(TAG, "deinit player done"); + tg0_timer_deinit (); + + ESP_LOGI (TAG, "deinit player done"); return ret; } @@ -206,7 +245,9 @@ int deinit_player(void) { /** * call before http task creation! */ -QueueHandle_t init_player(void) { +QueueHandle_t +init_player (void) +{ int ret; currentSnapcastSetting.buffer_ms = 1000; @@ -218,74 +259,87 @@ QueueHandle_t init_player(void) { currentSnapcastSetting.muted = false; currentSnapcastSetting.volume = 70; - ret = player_setup_i2s(I2S_NUM_0, ¤tSnapcastSetting); - if (ret < 0) { - ESP_LOGE(TAG, "player_setup_i2s failed: %d", ret); + ret = player_setup_i2s (I2S_NUM_0, ¤tSnapcastSetting); + if (ret < 0) + { + ESP_LOGE (TAG, "player_setup_i2s failed: %d", ret); - return NULL; - } + return NULL; + } // create semaphore for time diff buffer to server - if (latencyBufSemaphoreHandle == NULL) { - latencyBufSemaphoreHandle = xSemaphoreCreateMutex(); - } + if (latencyBufSemaphoreHandle == NULL) + { + latencyBufSemaphoreHandle = xSemaphoreCreateMutex (); + } // create snapcast receive buffer - if (pcmChunkQueueHandle == NULL) { - pcmChunkQueueHandle = xQueueCreateStatic( - PCM_CHNK_QUEUE_LENGTH, sizeof(pcm_chunk_message_t *), - pcmChunkQueueStorageArea, &pcmChunkQueue); - } + if (pcmChunkQueueHandle == NULL) + { + pcmChunkQueueHandle = xQueueCreateStatic ( + PCM_CHNK_QUEUE_LENGTH, sizeof (pcm_chunk_message_t *), + pcmChunkQueueStorageArea, &pcmChunkQueue); + } // init diff buff median filter latencyMedianFilterLong.numNodes = LATENCY_MEDIAN_FILTER_LEN; latencyMedianFilterLong.medianBuffer = latencyMedianLongBuffer; - reset_latency_buffer(); + reset_latency_buffer (); - tg0_timer_init(); + tg0_timer_init (); - if (syncTaskHandle == NULL) { - ESP_LOGI(TAG, "Start snapcast_sync_task"); + if (syncTaskHandle == NULL) + { + ESP_LOGI (TAG, "Start snapcast_sync_task"); - xTaskCreatePinnedToCore(snapcast_sync_task, "snapcast_sync_task", 8 * 1024, - NULL, SYNC_TASK_PRIORITY, &syncTaskHandle, - SYNC_TASK_CORE_ID); - } + xTaskCreatePinnedToCore (snapcast_sync_task, "snapcast_sync_task", + 8 * 1024, NULL, SYNC_TASK_PRIORITY, + &syncTaskHandle, SYNC_TASK_CORE_ID); + } - ESP_LOGI(TAG, "init player done"); + ESP_LOGI (TAG, "init player done"); return pcmChunkQueueHandle; } -int8_t player_latency_insert(int64_t newValue) { +int8_t +player_latency_insert (int64_t newValue) +{ int64_t medianValue; - medianValue = MEDIANFILTER_Insert(&latencyMedianFilterLong, newValue); - if (xSemaphoreTake(latencyBufSemaphoreHandle, pdMS_TO_TICKS(1)) == pdTRUE) { - if (MEDIANFILTER_isFull(&latencyMedianFilterLong)) { - latencyBuffFull = true; + medianValue = MEDIANFILTER_Insert (&latencyMedianFilterLong, newValue); + if (xSemaphoreTake (latencyBufSemaphoreHandle, pdMS_TO_TICKS (1)) == pdTRUE) + { + if (MEDIANFILTER_isFull (&latencyMedianFilterLong)) + { + latencyBuffFull = true; + } + + latencyToServer = medianValue; + + xSemaphoreGive (latencyBufSemaphoreHandle); + } + else + { + ESP_LOGW (TAG, "couldn't set latencyToServer = medianValue"); } - latencyToServer = medianValue; - - xSemaphoreGive(latencyBufSemaphoreHandle); - } else { - ESP_LOGW(TAG, "couldn't set latencyToServer = medianValue"); - } - return 0; } /** * */ -int8_t player_notify_buffer_ms(uint32_t ms) { - if (syncTaskHandle == NULL) { - return -1; - } +int8_t +player_notify_buffer_ms (uint32_t ms) +{ + if (syncTaskHandle == NULL) + { + return -1; + } // notify task of changed parameters - xTaskNotify(syncTaskHandle, ms, eSetBits); + xTaskNotify (syncTaskHandle, ms, eSetBits); return 0; } @@ -293,29 +347,36 @@ int8_t player_notify_buffer_ms(uint32_t ms) { /** * */ -int8_t player_send_snapcast_setting(snapcastSetting_t setting) { +int8_t +player_send_snapcast_setting (snapcastSetting_t setting) +{ int ret; - if ((syncTaskHandle == NULL) || (snapcastSettingQueueHandle == NULL)) { - return -1; - } + if ((syncTaskHandle == NULL) || (snapcastSettingQueueHandle == NULL)) + { + return -1; + } - if ((currentSnapcastSetting.bits != setting.bits) || - (currentSnapcastSetting.buffer_ms != setting.buffer_ms) || - (currentSnapcastSetting.channels != setting.channels) || - (currentSnapcastSetting.chunkDuration_ms != setting.chunkDuration_ms) || - (currentSnapcastSetting.codec != setting.codec) || - (currentSnapcastSetting.muted != setting.muted) || - (currentSnapcastSetting.sampleRate != setting.sampleRate) || - (currentSnapcastSetting.volume != setting.volume)) - { - // notify task of changed parameters - memcpy(¤tSnapcastSetting, &setting, sizeof(snapcastSetting_t)); - ret = xQueueSend(snapcastSettingQueueHandle, ¤tSnapcastSetting, pdMS_TO_TICKS(10)); - if (ret != pdPASS) { - ESP_LOGE(TAG, "player_send_snapcast_setting: couldn't send snapcast setting"); - } - } + if ((currentSnapcastSetting.bits != setting.bits) + || (currentSnapcastSetting.buffer_ms != setting.buffer_ms) + || (currentSnapcastSetting.channels != setting.channels) + || (currentSnapcastSetting.chunkDuration_ms != setting.chunkDuration_ms) + || (currentSnapcastSetting.codec != setting.codec) + || (currentSnapcastSetting.muted != setting.muted) + || (currentSnapcastSetting.sampleRate != setting.sampleRate) + || (currentSnapcastSetting.volume != setting.volume)) + { + // notify task of changed parameters + memcpy (¤tSnapcastSetting, &setting, sizeof (snapcastSetting_t)); + ret = xQueueSend (snapcastSettingQueueHandle, ¤tSnapcastSetting, + pdMS_TO_TICKS (10)); + if (ret != pdPASS) + { + ESP_LOGE ( + TAG, + "player_send_snapcast_setting: couldn't send snapcast setting"); + } + } return 0; } @@ -323,30 +384,38 @@ int8_t player_send_snapcast_setting(snapcastSetting_t setting) { /** * */ -int8_t reset_latency_buffer(void) { +int8_t +reset_latency_buffer (void) +{ // init diff buff median filter - if (MEDIANFILTER_Init(&latencyMedianFilterLong) < 0) { - ESP_LOGE(TAG, "reset_diff_buffer: couldn't init median filter long. STOP"); + if (MEDIANFILTER_Init (&latencyMedianFilterLong) < 0) + { + ESP_LOGE (TAG, + "reset_diff_buffer: couldn't init median filter long. STOP"); - return -2; - } + return -2; + } - if (latencyBufSemaphoreHandle == NULL) { - ESP_LOGE(TAG, "reset_diff_buffer: latencyBufSemaphoreHandle == NULL"); + if (latencyBufSemaphoreHandle == NULL) + { + ESP_LOGE (TAG, "reset_diff_buffer: latencyBufSemaphoreHandle == NULL"); - return -2; - } + return -2; + } - if (xSemaphoreTake(latencyBufSemaphoreHandle, portMAX_DELAY) == pdTRUE) { - latencyBuffFull = false; - latencyToServer = 0; + if (xSemaphoreTake (latencyBufSemaphoreHandle, portMAX_DELAY) == pdTRUE) + { + latencyBuffFull = false; + latencyToServer = 0; - xSemaphoreGive(latencyBufSemaphoreHandle); - } else { - ESP_LOGW(TAG, "reset_diff_buffer: can't take semaphore"); + xSemaphoreGive (latencyBufSemaphoreHandle); + } + else + { + ESP_LOGW (TAG, "reset_diff_buffer: can't take semaphore"); - return -1; - } + return -1; + } return 0; } @@ -354,24 +423,28 @@ int8_t reset_latency_buffer(void) { /** * */ -int8_t latency_buffer_full(void) { +int8_t +latency_buffer_full (void) +{ int8_t tmp; - if (latencyBufSemaphoreHandle == NULL) { - ESP_LOGE(TAG, "latency_buffer_full: latencyBufSemaphoreHandle == NULL"); + if (latencyBufSemaphoreHandle == NULL) + { + ESP_LOGE (TAG, "latency_buffer_full: latencyBufSemaphoreHandle == NULL"); - return -2; - } + return -2; + } - if (xSemaphoreTake(latencyBufSemaphoreHandle, 0) == pdFALSE) { - ESP_LOGW(TAG, "latency_buffer_full: can't take semaphore"); + if (xSemaphoreTake (latencyBufSemaphoreHandle, 0) == pdFALSE) + { + ESP_LOGW (TAG, "latency_buffer_full: can't take semaphore"); - return -1; - } + return -1; + } tmp = latencyBuffFull; - xSemaphoreGive(latencyBufSemaphoreHandle); + xSemaphoreGive (latencyBufSemaphoreHandle); return tmp; } @@ -379,29 +452,33 @@ int8_t latency_buffer_full(void) { /** * */ -int8_t get_diff_to_server(int64_t *tDiff) { +int8_t +get_diff_to_server (int64_t *tDiff) +{ static int64_t lastDiff = 0; - if (latencyBufSemaphoreHandle == NULL) { - ESP_LOGE(TAG, "get_diff_to_server: latencyBufSemaphoreHandle == NULL"); + if (latencyBufSemaphoreHandle == NULL) + { + ESP_LOGE (TAG, "get_diff_to_server: latencyBufSemaphoreHandle == NULL"); - return -2; - } + return -2; + } - if (xSemaphoreTake(latencyBufSemaphoreHandle, 0) == pdFALSE) { - *tDiff = lastDiff; + if (xSemaphoreTake (latencyBufSemaphoreHandle, 0) == pdFALSE) + { + *tDiff = lastDiff; - // ESP_LOGW(TAG, "get_diff_to_server: can't take semaphore. Old diff - // retrieved"); + // ESP_LOGW(TAG, "get_diff_to_server: can't take semaphore. Old diff + // retrieved"); - return -1; - } + return -1; + } *tDiff = latencyToServer; - lastDiff = latencyToServer; // store value, so we can return a value if - // semaphore couldn't be taken + lastDiff = latencyToServer; // store value, so we can return a value if + // semaphore couldn't be taken - xSemaphoreGive(latencyBufSemaphoreHandle); + xSemaphoreGive (latencyBufSemaphoreHandle); return 0; } @@ -409,27 +486,33 @@ int8_t get_diff_to_server(int64_t *tDiff) { /** * */ -int8_t server_now(int64_t *sNow) { +int8_t +server_now (int64_t *sNow) +{ struct timeval now; int64_t diff; // get current time - if (gettimeofday(&now, NULL)) { - ESP_LOGE(TAG, "server_now: Failed to get time of day"); + if (gettimeofday (&now, NULL)) + { + ESP_LOGE (TAG, "server_now: Failed to get time of day"); - return -1; - } + return -1; + } - if (get_diff_to_server(&diff) == -1) { - ESP_LOGW(TAG, - "server_now: can't get current diff to server. Retrieved old one"); - } + if (get_diff_to_server (&diff) == -1) + { + ESP_LOGW ( + TAG, + "server_now: can't get current diff to server. Retrieved old one"); + } - if (diff == 0) { - // ESP_LOGW(TAG, "server_now: diff to server not initialized yet"); + if (diff == 0) + { + // ESP_LOGW(TAG, "server_now: diff to server not initialized yet"); - return -1; - } + return -1; + } *sNow = ((int64_t)now.tv_sec * 1000000LL + (int64_t)now.tv_usec) + diff; @@ -449,67 +532,81 @@ int8_t server_now(int64_t *sNow) { * flash cache is disabled, we can allocate this interrupt without the * ESP_INTR_FLAG_IRAM flag and use the normal API. */ -void IRAM_ATTR timer_group0_isr(void *para) { - timer_spinlock_take(TIMER_GROUP_0); +void IRAM_ATTR +timer_group0_isr (void *para) +{ + timer_spinlock_take (TIMER_GROUP_0); BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Retrieve the interrupt status and the counter value // from the timer that reported the interrupt - uint32_t timer_intr = timer_group_get_intr_status_in_isr(TIMER_GROUP_0); + uint32_t timer_intr = timer_group_get_intr_status_in_isr (TIMER_GROUP_0); // Clear the interrupt // and update the alarm time for the timer with without reload - if (timer_intr & TIMER_INTR_T1) { - timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_1); + if (timer_intr & TIMER_INTR_T1) + { + timer_group_clr_intr_status_in_isr (TIMER_GROUP_0, TIMER_1); - // Notify the task in the task's notification value. - xTaskNotifyFromISR(syncTaskHandle, 0, eNoAction, &xHigherPriorityTaskWoken); - } + // Notify the task in the task's notification value. + xTaskNotifyFromISR (syncTaskHandle, 0, eNoAction, + &xHigherPriorityTaskWoken); + } - timer_spinlock_give(TIMER_GROUP_0); + timer_spinlock_give (TIMER_GROUP_0); - if (xHigherPriorityTaskWoken) { - portYIELD_FROM_ISR(); - } + if (xHigherPriorityTaskWoken) + { + portYIELD_FROM_ISR (); + } } -static void tg0_timer_deinit(void) { timer_deinit(TIMER_GROUP_0, TIMER_1); } +static void +tg0_timer_deinit (void) +{ + timer_deinit (TIMER_GROUP_0, TIMER_1); +} /* * */ -static void tg0_timer_init(void) { +static void +tg0_timer_init (void) +{ // Select and initialize basic parameters of the timer timer_config_t config = { - //.divider = 8, // 100ns ticks - .divider = 80, // 1µs ticks - .counter_dir = TIMER_COUNT_UP, - .counter_en = TIMER_PAUSE, - .alarm_en = TIMER_ALARM_EN, - .auto_reload = TIMER_AUTORELOAD_DIS, - }; // default clock source is APB - timer_init(TIMER_GROUP_0, TIMER_1, &config); + //.divider = 8, // 100ns ticks + .divider = 80, // 1µs ticks + .counter_dir = TIMER_COUNT_UP, + .counter_en = TIMER_PAUSE, + .alarm_en = TIMER_ALARM_EN, + .auto_reload = TIMER_AUTORELOAD_DIS, + }; // default clock source is APB + timer_init (TIMER_GROUP_0, TIMER_1, &config); // Configure the alarm value and the interrupt on alarm. - timer_set_alarm_value(TIMER_GROUP_0, TIMER_1, 0); - timer_enable_intr(TIMER_GROUP_0, TIMER_1); - if (timer_isr_register(TIMER_GROUP_0, TIMER_1, timer_group0_isr, NULL, - ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL3, - NULL) != ESP_OK) { - ESP_LOGE(TAG, "unable to register timer 1 callback"); - } + timer_set_alarm_value (TIMER_GROUP_0, TIMER_1, 0); + timer_enable_intr (TIMER_GROUP_0, TIMER_1); + if (timer_isr_register (TIMER_GROUP_0, TIMER_1, timer_group0_isr, NULL, + ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL3, NULL) + != ESP_OK) + { + ESP_LOGE (TAG, "unable to register timer 1 callback"); + } } /** * */ -static void tg0_timer1_start(uint64_t alarm_value) { - timer_pause(TIMER_GROUP_0, TIMER_1); - timer_set_counter_value(TIMER_GROUP_0, TIMER_1, 0); - timer_set_alarm_value(TIMER_GROUP_0, TIMER_1, alarm_value); - timer_set_alarm(TIMER_GROUP_0, TIMER_1, TIMER_ALARM_EN); - timer_start(TIMER_GROUP_0, TIMER_1); +static void +tg0_timer1_start (uint64_t alarm_value) +{ + timer_pause (TIMER_GROUP_0, TIMER_1); + timer_set_counter_value (TIMER_GROUP_0, TIMER_1, 0); + timer_set_alarm_value (TIMER_GROUP_0, TIMER_1, alarm_value); + timer_set_alarm (TIMER_GROUP_0, TIMER_1, TIMER_ALARM_EN); + timer_start (TIMER_GROUP_0, TIMER_1); // ESP_LOGI(TAG, "started age timer"); } @@ -518,60 +615,68 @@ static void tg0_timer1_start(uint64_t alarm_value) { // sdm2, uint32_t o_div); apll_freq = xtal_freq * (4 + sdm2 + sdm1/256 + // sdm0/65536)/((o_div + 2) * 2) xtal == 40MHz on lyrat v4.3 I2S bit_clock = // rate * (number of channels) * bits_per_sample -void adjust_apll(int8_t direction) { +void +adjust_apll (int8_t direction) +{ int sdm0, sdm1, sdm2, o_div; -// int index = 2; // 2 for slow adjustment, 0 for fast adjustment + // int index = 2; // 2 for slow adjustment, 0 for fast adjustment // only change if necessary - if (currentDir == direction) { - return; - } + if (currentDir == direction) + { + return; + } -// if (direction == 1) { -// // speed up -// sdm0 = apll_predefine_48k_corr[index][2]; -// sdm1 = apll_predefine_48k_corr[index][3]; -// sdm2 = apll_predefine_48k_corr[index][4]; -// o_div = apll_predefine_48k_corr[index][5]; -// } else if (direction == -1) { -// // slow down -// sdm0 = apll_predefine_48k_corr[index + 1][2]; -// sdm1 = apll_predefine_48k_corr[index + 1][3]; -// sdm2 = apll_predefine_48k_corr[index + 1][4]; -// o_div = apll_predefine_48k_corr[index + 1][5]; -// } else { -// // reset to normal playback speed -// sdm0 = apll_predefine[5][2]; -// sdm1 = apll_predefine[5][3]; -// sdm2 = apll_predefine[5][4]; -// o_div = apll_predefine[5][5]; -// -// direction = 0; -// } + // if (direction == 1) { + // // speed up + // sdm0 = apll_predefine_48k_corr[index][2]; + // sdm1 = apll_predefine_48k_corr[index][3]; + // sdm2 = apll_predefine_48k_corr[index][4]; + // o_div = apll_predefine_48k_corr[index][5]; + // } else if (direction == -1) { + // // slow down + // sdm0 = apll_predefine_48k_corr[index + 1][2]; + // sdm1 = apll_predefine_48k_corr[index + 1][3]; + // sdm2 = apll_predefine_48k_corr[index + 1][4]; + // o_div = apll_predefine_48k_corr[index + 1][5]; + // } else { + // // reset to normal playback speed + // sdm0 = apll_predefine[5][2]; + // sdm1 = apll_predefine[5][3]; + // sdm2 = apll_predefine[5][4]; + // o_div = apll_predefine[5][5]; + // + // direction = 0; + // } - if (direction == 1) { - // speed up - sdm0 = apll_corr_predefine[0][2]; - sdm1 = apll_corr_predefine[0][3]; - sdm2 = apll_corr_predefine[0][4]; - o_div = apll_corr_predefine[0][5]; - } else if (direction == -1) { - // slow down - sdm0 = apll_corr_predefine[1][2]; - sdm1 = apll_corr_predefine[1][3]; - sdm2 = apll_corr_predefine[1][4]; - o_div = apll_corr_predefine[1][5]; - } else { - // reset to normal playback speed - sdm0 = apll_normal_predefine[2]; - sdm1 = apll_normal_predefine[3]; - sdm2 = apll_normal_predefine[4]; - o_div = apll_normal_predefine[5]; + if (direction == 1) + { + // speed up + sdm0 = apll_corr_predefine[0][2]; + sdm1 = apll_corr_predefine[0][3]; + sdm2 = apll_corr_predefine[0][4]; + o_div = apll_corr_predefine[0][5]; + } + else if (direction == -1) + { + // slow down + sdm0 = apll_corr_predefine[1][2]; + sdm1 = apll_corr_predefine[1][3]; + sdm2 = apll_corr_predefine[1][4]; + o_div = apll_corr_predefine[1][5]; + } + else + { + // reset to normal playback speed + sdm0 = apll_normal_predefine[2]; + sdm1 = apll_normal_predefine[3]; + sdm2 = apll_normal_predefine[4]; + o_div = apll_normal_predefine[5]; - direction = 0; - } + direction = 0; + } - rtc_clk_apll_enable(1, sdm0, sdm1, sdm2, o_div); + rtc_clk_apll_enable (1, sdm0, sdm1, sdm2, o_div); currentDir = direction; } @@ -579,357 +684,428 @@ void adjust_apll(int8_t direction) { /** * */ -int8_t free_pcm_chunk_fragments(pcm_chunk_fragment_t *fragment) { - if (fragment == NULL) { - ESP_LOGE( TAG, - "free_pcm_chunk_fragments() parameter Error"); +int8_t +free_pcm_chunk_fragments (pcm_chunk_fragment_t *fragment) +{ + if (fragment == NULL) + { + ESP_LOGE (TAG, "free_pcm_chunk_fragments() parameter Error"); - return -1; - } + return -1; + } - // free all fragments recursive - if (fragment->nextFragment == NULL) { - if (fragment->payload != NULL) { - free(fragment->payload); - fragment->payload = NULL; - } + // free all fragments recursive + if (fragment->nextFragment == NULL) + { + if (fragment->payload != NULL) + { + free (fragment->payload); + fragment->payload = NULL; + } - free(fragment); - fragment = NULL; - } - else { - free_pcm_chunk_fragments(fragment->nextFragment); - } + free (fragment); + fragment = NULL; + } + else + { + free_pcm_chunk_fragments (fragment->nextFragment); + } - return 0; + return 0; } /** * */ -int8_t free_pcm_chunk(pcm_chunk_message_t *pcmChunk) { - if (pcmChunk == NULL) { - ESP_LOGE( TAG, - "free_pcm_chunk() parameter Error"); +int8_t +free_pcm_chunk (pcm_chunk_message_t *pcmChunk) +{ + if (pcmChunk == NULL) + { + ESP_LOGE (TAG, "free_pcm_chunk() parameter Error"); - return -1; - } + return -1; + } - free_pcm_chunk_fragments(pcmChunk->fragment); - pcmChunk->fragment = NULL; // was freed in free_pcm_chunk_fragments() + free_pcm_chunk_fragments (pcmChunk->fragment); + pcmChunk->fragment = NULL; // was freed in free_pcm_chunk_fragments() - free(pcmChunk); - pcmChunk = NULL; + free (pcmChunk); + pcmChunk = NULL; - return 0; + return 0; } - /** * */ -int8_t insert_pcm_chunk(wire_chunk_message_t *decodedWireChunk) { - pcm_chunk_message_t *pcmChunk; - size_t tmpSize; - size_t largestFreeBlock, freeMem; - int ret = -3; +int8_t +insert_pcm_chunk (wire_chunk_message_t *decodedWireChunk) +{ + pcm_chunk_message_t *pcmChunk; + size_t tmpSize; + size_t largestFreeBlock, freeMem; + int ret = -3; -// heap_caps_get_free_size(MALLOC_CAP_8BIT); -// heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); -// heap_caps_get_free_size(MALLOC_CAP_32BIT); -// heap_caps_get_largest_free_block(MALLOC_CAP_32BIT); + // heap_caps_get_free_size(MALLOC_CAP_8BIT); + // heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); + // heap_caps_get_free_size(MALLOC_CAP_32BIT); + // heap_caps_get_largest_free_block(MALLOC_CAP_32BIT); - if (decodedWireChunk == NULL) { - ESP_LOGE( - TAG, - "Parameter Error"); + if (decodedWireChunk == NULL) + { + ESP_LOGE (TAG, "Parameter Error"); - return -1; - } + return -1; + } - pcmChunk = (pcm_chunk_message_t *)calloc(1, sizeof(pcm_chunk_message_t)); - if (pcmChunk == NULL) { - ESP_LOGE( - TAG, - "Failed to allocate memory for pcm chunk message"); + pcmChunk = (pcm_chunk_message_t *)calloc (1, sizeof (pcm_chunk_message_t)); + if (pcmChunk == NULL) + { + ESP_LOGE (TAG, "Failed to allocate memory for pcm chunk message"); - return -2; - } + return -2; + } - pcmChunk->fragment = (pcm_chunk_fragment_t *)calloc(1, sizeof(pcm_chunk_fragment_t)); - if (pcmChunk->fragment == NULL) { - ESP_LOGE( - TAG, - "Failed to allocate memory for pcm chunk fragment"); + pcmChunk->fragment + = (pcm_chunk_fragment_t *)calloc (1, sizeof (pcm_chunk_fragment_t)); + if (pcmChunk->fragment == NULL) + { + ESP_LOGE (TAG, "Failed to allocate memory for pcm chunk fragment"); - free_pcm_chunk(pcmChunk); + free_pcm_chunk (pcmChunk); - return -2; - } + return -2; + } - // store the timestamp - pcmChunk->timestamp = decodedWireChunk->timestamp; + // store the timestamp + pcmChunk->timestamp = decodedWireChunk->timestamp; #if CONFIG_USE_PSRAM - pcmChunk->fragment->payload = (char *)heap_caps_malloc(decodedWireChunk->size, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); - if (pcmChunk->fragment->payload == NULL) { - ESP_LOGE( - TAG, - "Failed to allocate memory for pcm chunk fragment payload"); + pcmChunk->fragment->payload = (char *)heap_caps_malloc ( + decodedWireChunk->size, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); + if (pcmChunk->fragment->payload == NULL) + { + ESP_LOGE (TAG, + "Failed to allocate memory for pcm chunk fragment payload"); - free_pcm_chunk(pcmChunk); + free_pcm_chunk (pcmChunk); - freeMem = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); + freeMem = heap_caps_get_free_size (MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); - ret = -2; - } - else { - // copy the whole payload to our fragment - memcpy(pcmChunk->fragment->payload, decodedWireChunk->payload, decodedWireChunk->size); - pcmChunk->fragment->nextFragment = NULL; - pcmChunk->fragment->size = decodedWireChunk->size; + ret = -2; + } + else + { + // copy the whole payload to our fragment + memcpy (pcmChunk->fragment->payload, decodedWireChunk->payload, + decodedWireChunk->size); + pcmChunk->fragment->nextFragment = NULL; + pcmChunk->fragment->size = decodedWireChunk->size; - ret = 0; - } + ret = 0; + } #else - // we got valid memory for pcm_chunk_message_t - // first we try to allocated 32 bit aligned memory for payload - // check available memory first so we can decide if we need to fragment the data - freeMem = heap_caps_get_free_size(MALLOC_CAP_32BIT); - if (freeMem >= decodedWireChunk->size) { - largestFreeBlock = heap_caps_get_largest_free_block(MALLOC_CAP_32BIT); -// ESP_LOGI( -// TAG, -// "32b f %d b %d", freeMem, largestFreeBlock); - if (largestFreeBlock >= decodedWireChunk->size) { - pcmChunk->fragment->payload = (char *)heap_caps_malloc(decodedWireChunk->size, MALLOC_CAP_32BIT); - if (pcmChunk->fragment->payload == NULL) { - ESP_LOGE( - TAG, - "Failed to allocate memory for pcm chunk fragment payload"); + // we got valid memory for pcm_chunk_message_t + // first we try to allocated 32 bit aligned memory for payload + // check available memory first so we can decide if we need to fragment the + // data + freeMem = heap_caps_get_free_size (MALLOC_CAP_32BIT); + if (freeMem >= decodedWireChunk->size) + { + largestFreeBlock = heap_caps_get_largest_free_block (MALLOC_CAP_32BIT); + // ESP_LOGI( + // TAG, + // "32b f %d b %d", freeMem, + // largestFreeBlock); + if (largestFreeBlock >= decodedWireChunk->size) + { + pcmChunk->fragment->payload = (char *)heap_caps_malloc ( + decodedWireChunk->size, MALLOC_CAP_32BIT); + if (pcmChunk->fragment->payload == NULL) + { + ESP_LOGE ( + TAG, + "Failed to allocate memory for pcm chunk fragment payload"); - free_pcm_chunk(pcmChunk); + free_pcm_chunk (pcmChunk); - ret = -2; - } - else { - // copy the whole payload to our fragment - memcpy(pcmChunk->fragment->payload, decodedWireChunk->payload, decodedWireChunk->size); - pcmChunk->fragment->nextFragment = NULL; - pcmChunk->fragment->size = decodedWireChunk->size; + ret = -2; + } + else + { + // copy the whole payload to our fragment + memcpy (pcmChunk->fragment->payload, decodedWireChunk->payload, + decodedWireChunk->size); + pcmChunk->fragment->nextFragment = NULL; + pcmChunk->fragment->size = decodedWireChunk->size; - ret = 0; - } - } - else { - pcm_chunk_fragment_t *next = NULL; - size_t s; + ret = 0; + } + } + else + { + pcm_chunk_fragment_t *next = NULL; + size_t s; - tmpSize = decodedWireChunk->size; - //heap_caps_aligned_alloc(sizeof(uint32_t), decodedWireChunk->size, MALLOC_CAP_32BIT); - pcmChunk->fragment->payload = (char *)heap_caps_malloc(largestFreeBlock, MALLOC_CAP_32BIT); - if (pcmChunk->fragment->payload == NULL) { - ESP_LOGE( - TAG, - "Failed to allocate memory for pcm chunk fragmented payload"); + tmpSize = decodedWireChunk->size; + // heap_caps_aligned_alloc(sizeof(uint32_t), decodedWireChunk->size, + // MALLOC_CAP_32BIT); + pcmChunk->fragment->payload + = (char *)heap_caps_malloc (largestFreeBlock, MALLOC_CAP_32BIT); + if (pcmChunk->fragment->payload == NULL) + { + ESP_LOGE (TAG, "Failed to allocate memory for pcm chunk " + "fragmented payload"); - free_pcm_chunk(pcmChunk); + free_pcm_chunk (pcmChunk); - ret = -2; - } - else { - next = pcmChunk->fragment; - s = largestFreeBlock; + ret = -2; + } + else + { + next = pcmChunk->fragment; + s = largestFreeBlock; - // loop until we have all data stored to a fragment - do { - // copy the whole payload to our fragment - memcpy(next->payload, decodedWireChunk->payload, s); - next->size = s; - tmpSize -= s; - decodedWireChunk->payload += s; + // loop until we have all data stored to a fragment + do + { + // copy the whole payload to our fragment + memcpy (next->payload, decodedWireChunk->payload, s); + next->size = s; + tmpSize -= s; + decodedWireChunk->payload += s; - if (tmpSize > 0) { - next->nextFragment = (pcm_chunk_fragment_t *)calloc(1, sizeof(pcm_chunk_fragment_t)); - if (next->nextFragment == NULL) { - ESP_LOGE( - TAG, - "Failed to allocate memory for next pcm chunk fragment %d %d", heap_caps_get_free_size(MALLOC_CAP_8BIT), heap_caps_get_largest_free_block(MALLOC_CAP_8BIT)); + if (tmpSize > 0) + { + next->nextFragment = (pcm_chunk_fragment_t *)calloc ( + 1, sizeof (pcm_chunk_fragment_t)); + if (next->nextFragment == NULL) + { + ESP_LOGE (TAG, + "Failed to allocate memory for next pcm " + "chunk fragment %d %d", + heap_caps_get_free_size (MALLOC_CAP_8BIT), + heap_caps_get_largest_free_block ( + MALLOC_CAP_8BIT)); - free_pcm_chunk(pcmChunk); + free_pcm_chunk (pcmChunk); - ret = -3; + ret = -3; - break; - } - else { - largestFreeBlock = heap_caps_get_largest_free_block(MALLOC_CAP_32BIT); - if (largestFreeBlock <= tmpSize) { - s = largestFreeBlock; - // TODO: DMA buffer needs to be changed also to one chunk size if sample rate changes - } - else { - s = tmpSize; - } + break; + } + else + { + largestFreeBlock = heap_caps_get_largest_free_block ( + MALLOC_CAP_32BIT); + if (largestFreeBlock <= tmpSize) + { + s = largestFreeBlock; + // TODO: DMA buffer needs to be changed also to + // one chunk size if sample rate changes + } + else + { + s = tmpSize; + } - next->nextFragment->payload = (char *)heap_caps_malloc(s, MALLOC_CAP_32BIT); - if (next->nextFragment->payload == NULL) { - ESP_LOGE( - TAG, - "Failed to allocate memory for pcm chunk next fragmented payload"); + next->nextFragment->payload + = (char *)heap_caps_malloc (s, MALLOC_CAP_32BIT); + if (next->nextFragment->payload == NULL) + { + ESP_LOGE (TAG, + "Failed to allocate memory for pcm " + "chunk next fragmented payload"); - free_pcm_chunk(pcmChunk); + free_pcm_chunk (pcmChunk); - ret = -3; + ret = -3; - break; - } - else { - next = next->nextFragment; - } - } - } - } while(tmpSize); - } - } + break; + } + else + { + next = next->nextFragment; + } + } + } + } + while (tmpSize); + } + } - ret = 0; - } - else { - // we got valid memory for pcm_chunk_message_t - // no 32 bit aligned memory available, try to allocate 8 bit aligned memory for payload - // check available memory first so we can decide if we need to fragment the data - freeMem = heap_caps_get_free_size(MALLOC_CAP_8BIT); - if (freeMem >= decodedWireChunk->size) { - largestFreeBlock = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); -// ESP_LOGI( -// TAG, -// "8b f %d b %d", freeMem, largestFreeBlock); - if (largestFreeBlock >= decodedWireChunk->size) { - pcmChunk->fragment->payload = (char *)heap_caps_malloc(decodedWireChunk->size, MALLOC_CAP_8BIT); - if (pcmChunk->fragment->payload == NULL) { - ESP_LOGE( - TAG, - "Failed to allocate memory for pcm chunk fragment payload"); + ret = 0; + } + else + { + // we got valid memory for pcm_chunk_message_t + // no 32 bit aligned memory available, try to allocate 8 bit aligned + // memory for payload check available memory first so we can decide if we + // need to fragment the data + freeMem = heap_caps_get_free_size (MALLOC_CAP_8BIT); + if (freeMem >= decodedWireChunk->size) + { + largestFreeBlock + = heap_caps_get_largest_free_block (MALLOC_CAP_8BIT); + // ESP_LOGI( + // TAG, + // "8b f %d b %d", freeMem, + // largestFreeBlock); + if (largestFreeBlock >= decodedWireChunk->size) + { + pcmChunk->fragment->payload = (char *)heap_caps_malloc ( + decodedWireChunk->size, MALLOC_CAP_8BIT); + if (pcmChunk->fragment->payload == NULL) + { + ESP_LOGE (TAG, "Failed to allocate memory for pcm chunk " + "fragment payload"); - free_pcm_chunk(pcmChunk); + free_pcm_chunk (pcmChunk); - ret = -2; - } - else { - // copy the whole payload to our fragment - memcpy(pcmChunk->fragment->payload, decodedWireChunk->payload, decodedWireChunk->size); - pcmChunk->fragment->nextFragment = NULL; - pcmChunk->fragment->size = decodedWireChunk->size; + ret = -2; + } + else + { + // copy the whole payload to our fragment + memcpy (pcmChunk->fragment->payload, + decodedWireChunk->payload, decodedWireChunk->size); + pcmChunk->fragment->nextFragment = NULL; + pcmChunk->fragment->size = decodedWireChunk->size; - ret = 0; - } - } - else { - pcm_chunk_fragment_t *next = NULL; - size_t s; + ret = 0; + } + } + else + { + pcm_chunk_fragment_t *next = NULL; + size_t s; - tmpSize = decodedWireChunk->size; - //heap_caps_aligned_alloc(sizeof(uint32_t), decodedWireChunk->size, MALLOC_CAP_32BIT); - pcmChunk->fragment->payload = (char *)heap_caps_malloc(largestFreeBlock, MALLOC_CAP_8BIT); - if (pcmChunk->fragment->payload == NULL) { - ESP_LOGE( - TAG, - "Failed to allocate memory for pcm chunk fragmented payload"); + tmpSize = decodedWireChunk->size; + // heap_caps_aligned_alloc(sizeof(uint32_t), + // decodedWireChunk->size, MALLOC_CAP_32BIT); + pcmChunk->fragment->payload = (char *)heap_caps_malloc ( + largestFreeBlock, MALLOC_CAP_8BIT); + if (pcmChunk->fragment->payload == NULL) + { + ESP_LOGE (TAG, "Failed to allocate memory for pcm chunk " + "fragmented payload"); - free_pcm_chunk(pcmChunk); + free_pcm_chunk (pcmChunk); - ret = -2; - } - else { - next = pcmChunk->fragment; - s = largestFreeBlock; + ret = -2; + } + else + { + next = pcmChunk->fragment; + s = largestFreeBlock; - // loop until we have all data stored to a fragment - do { - // copy the whole payload to our fragment - memcpy(next->payload, decodedWireChunk->payload, s); - next->size = s; - tmpSize -= s; - decodedWireChunk->payload += s; + // loop until we have all data stored to a fragment + do + { + // copy the whole payload to our fragment + memcpy (next->payload, decodedWireChunk->payload, s); + next->size = s; + tmpSize -= s; + decodedWireChunk->payload += s; - if (tmpSize > 0) { - next->nextFragment = (pcm_chunk_fragment_t *)calloc(1, sizeof(pcm_chunk_fragment_t)); - if (next->nextFragment == NULL) { - ESP_LOGE( - TAG, - "Failed to allocate memory for next pcm chunk fragment %d %d", heap_caps_get_free_size(MALLOC_CAP_8BIT), heap_caps_get_largest_free_block(MALLOC_CAP_8BIT)); + if (tmpSize > 0) + { + next->nextFragment = (pcm_chunk_fragment_t *)calloc ( + 1, sizeof (pcm_chunk_fragment_t)); + if (next->nextFragment == NULL) + { + ESP_LOGE ( + TAG, + "Failed to allocate memory for next pcm " + "chunk fragment %d %d", + heap_caps_get_free_size (MALLOC_CAP_8BIT), + heap_caps_get_largest_free_block ( + MALLOC_CAP_8BIT)); - free_pcm_chunk(pcmChunk); + free_pcm_chunk (pcmChunk); - ret = -3; + ret = -3; - break; - } - else { - largestFreeBlock = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); - if (largestFreeBlock <= tmpSize) { - s = largestFreeBlock; - } - else { - s = tmpSize; - } + break; + } + else + { + largestFreeBlock + = heap_caps_get_largest_free_block ( + MALLOC_CAP_8BIT); + if (largestFreeBlock <= tmpSize) + { + s = largestFreeBlock; + } + else + { + s = tmpSize; + } - next->nextFragment->payload = (char *)heap_caps_malloc(s, MALLOC_CAP_8BIT); - if (next->nextFragment->payload == NULL) { - ESP_LOGE( - TAG, - "Failed to allocate memory for pcm chunk next fragmented payload"); + next->nextFragment->payload + = (char *)heap_caps_malloc (s, + MALLOC_CAP_8BIT); + if (next->nextFragment->payload == NULL) + { + ESP_LOGE ( + TAG, "Failed to allocate memory for pcm " + "chunk next fragmented payload"); - free_pcm_chunk(pcmChunk); + free_pcm_chunk (pcmChunk); - ret = -3; + ret = -3; - break; - } - else { - next = next->nextFragment; - } - } - } - } while(tmpSize); - } - } + break; + } + else + { + next = next->nextFragment; + } + } + } + } + while (tmpSize); + } + } - ret = 0; - } - } + ret = 0; + } + } #endif - if (ret == 0) { - if (xQueueSendToBack(pcmChunkQueueHandle, - &pcmChunk, - pdMS_TO_TICKS(1000)) != pdTRUE) - { - ESP_LOGW( - TAG, - "send: pcmChunkQueue full, messages waiting %d", - uxQueueMessagesWaiting(pcmChunkQueueHandle)); + if (ret == 0) + { + if (xQueueSendToBack (pcmChunkQueueHandle, &pcmChunk, + pdMS_TO_TICKS (1000)) + != pdTRUE) + { + ESP_LOGW (TAG, "send: pcmChunkQueue full, messages waiting %d", + uxQueueMessagesWaiting (pcmChunkQueueHandle)); - free_pcm_chunk(pcmChunk); - } - } - else { - ESP_LOGE(TAG, "couldn't get memory to insert fragmented chunk, %d", freeMem); - } + free_pcm_chunk (pcmChunk); + } + } + else + { + ESP_LOGE (TAG, "couldn't get memory to insert fragmented chunk, %d", + freeMem); + } - return ret; + return ret; } /** * */ -static void snapcast_sync_task(void *pvParameters) { +static void +snapcast_sync_task (void *pvParameters) +{ pcm_chunk_message_t *chnk = NULL; int64_t serverNow = 0; int64_t age; BaseType_t ret; - int64_t chunkDuration_us;// = WIRE_CHUNK_DURATION_MS * 1000; + int64_t chunkDuration_us; // = WIRE_CHUNK_DURATION_MS * 1000; char *p_payload = NULL; size_t size = 0; uint32_t notifiedValue; @@ -943,318 +1119,406 @@ static void snapcast_sync_task(void *pvParameters) { pcm_chunk_fragment_t *fragment = NULL; size_t written; bool gotSnapserverConfig = false; + int64_t clientDacLatency_us = 0; - ESP_LOGI(TAG, "started sync task"); + ESP_LOGI (TAG, "started sync task"); - snapcastSettingQueueHandle = xQueueCreate(1, sizeof(snapcastSetting_t)); + snapcastSettingQueueHandle = xQueueCreate (1, sizeof (snapcastSetting_t)); initialSync = 0; shortMedianFilter.numNodes = SHORT_BUFFER_LEN; shortMedianFilter.medianBuffer = shortMedianBuffer; - if (MEDIANFILTER_Init(&shortMedianFilter)) { - ESP_LOGE(TAG, "snapcast_sync_task: couldn't init shortMedianFilter. STOP"); - - return; - } - - while (1) { - // check if we got changed setting available, if so we need to reinitialize - ret = xQueueReceive(snapcastSettingQueueHandle, &snapcastSetting, 0); - if (ret == pdTRUE) { - buffer_us_local = (int64_t)(snapcastSetting.buffer_ms) * 1000LL; - - chunkDuration_us = (int64_t)(snapcastSetting.chunkDuration_ms) * 1000LL; - chunkInBytes = (snapcastSetting.chunkDuration_ms * snapcastSetting.sampleRate * snapcastSetting.channels * (snapcastSetting.bits / 8)) / 1000; - - if ((snapcastSetting.sampleRate > 0) && (snapcastSetting.bits) > 0 && (snapcastSetting.channels > 0)) { - i2s_custom_stop(I2S_NUM_0); - - ret = player_setup_i2s(I2S_NUM_0, ¤tSnapcastSetting); - if (ret < 0) { - ESP_LOGE(TAG, "player_setup_i2s failed: %d", ret); - - return; - } - - currentDir = 1; // force adjust_apll to set correct playback speed - adjust_apll(0); - - i2s_custom_set_clk(I2S_NUM_0, snapcastSetting.sampleRate, snapcastSetting.bits, snapcastSetting.channels); - initialSync = 0; - } - - ESP_LOGI(TAG, "snapserver config changed, buffer %dms, chunk %dms, sample rate %d, ch %d, bits %d", snapcastSetting.buffer_ms, snapcastSetting.chunkDuration_ms, snapcastSetting.sampleRate, snapcastSetting.channels, snapcastSetting.bits); - - gotSnapserverConfig = true; - } - else if (gotSnapserverConfig == false) { - vTaskDelay(pdMS_TO_TICKS(10)); - - continue; - } - - if (chnk == NULL) { - ret = xQueueReceive(pcmChunkQueueHandle, &chnk, pdMS_TO_TICKS(2000)); - if (ret != pdFAIL) { - // ESP_LOGW(TAG, "got pcm chunk"); - } - } else { - // ESP_LOGW(TAG, "already retrieved chunk needs - // service"); - ret = pdPASS; - } - - if (ret != pdFAIL) { - if (server_now(&serverNow) >= 0) { - age = serverNow - - ((int64_t)chnk->timestamp.sec * 1000000LL + - (int64_t)chnk->timestamp.usec) - - (int64_t)buffer_us_local + - (int64_t)clientDacLatency; - } else { - // ESP_LOGW(TAG, "couldn't get server now"); - - if (chnk != NULL) { - free_pcm_chunk(chnk); - chnk = NULL; - } - - vTaskDelay(pdMS_TO_TICKS(1)); - - continue; - } - - // wait for early time syncs to be ready - int tmp = latency_buffer_full(); - if ( tmp <= 0 ) { - if (tmp < 0) { - vTaskDelay(1); - - continue; - } - - if (age >= 0) { - if (chnk != NULL) { - free_pcm_chunk(chnk); - chnk = NULL; - } - } - - // ESP_LOGW(TAG, "diff buffer not full"); - - vTaskDelay( pdMS_TO_TICKS(10) ); - - continue; - } - - if (age < 0) { // get initial sync using hardware timer - if (initialSync == 0) { - tg0_timer1_start(-age - - alarmValSub); // alarm a little earlier to account - // for context switch duration from - // freeRTOS, timer with 1µs ticks - -// tg0_timer1_start((-age * 10) - alarmValSub)); // alarm a - // little earlier to account for context switch duration from - // freeRTOS, timer with 100ns ticks - - i2s_custom_stop(I2S_NUM_0); - - if (MEDIANFILTER_Init(&shortMedianFilter)) { - ESP_LOGE( - TAG, + if (MEDIANFILTER_Init (&shortMedianFilter)) + { + ESP_LOGE (TAG, "snapcast_sync_task: couldn't init shortMedianFilter. STOP"); - return; - } + return; + } - adjust_apll(0); // reset to normal playback speed + while (1) + { + // check if we got changed setting available, if so we need to + // reinitialize + ret = xQueueReceive (snapcastSettingQueueHandle, &snapcastSetting, 0); + if (ret == pdTRUE) + { + buffer_us_local = (int64_t) (snapcastSetting.buffer_ms) * 1000LL; - fragment = chnk->fragment; - p_payload = fragment->payload; - size = fragment->size; + chunkDuration_us + = (int64_t) (snapcastSetting.chunkDuration_ms) * 1000LL; + chunkInBytes + = (snapcastSetting.chunkDuration_ms * snapcastSetting.sampleRate + * snapcastSetting.channels * (snapcastSetting.bits / 8)) + / 1000; + clientDacLatency_us + = (int64_t)snapcastSetting.clientDacLatency_ms * 1000LL; - i2s_custom_init_dma_tx_queues(I2S_NUM_0, (uint8_t *)p_payload, size, &written); - size -= written; - p_payload += written; + if ((snapcastSetting.sampleRate > 0) && (snapcastSetting.bits) > 0 + && (snapcastSetting.channels > 0)) + { + i2s_custom_stop (I2S_NUM_0); -// ESP_LOGE(TAG, "wrote %d", written); + ret = player_setup_i2s (I2S_NUM_0, ¤tSnapcastSetting); + if (ret < 0) + { + ESP_LOGE (TAG, "player_setup_i2s failed: %d", ret); - if (size == 0) { - if (fragment->nextFragment != NULL) { - fragment = fragment->nextFragment; - p_payload = fragment->payload; - size = fragment->size; - } - else { - free_pcm_chunk(chnk); - chnk = NULL; - } - } + return; + } - // Wait to be notified of a timer interrupt. - xTaskNotifyWait(pdFALSE, // Don't clear bits on entry. - pdFALSE, // Don't clear bits on exit. - ¬ifiedValue, // Stores the notified value. - portMAX_DELAY); - // or use simple task delay for this - // vTaskDelay( pdMS_TO_TICKS(-age / 1000) ); + currentDir + = 1; // force adjust_apll to set correct playback speed + adjust_apll (0); - i2s_custom_start(I2S_NUM_0); + i2s_custom_set_clk (I2S_NUM_0, snapcastSetting.sampleRate, + snapcastSetting.bits, + snapcastSetting.channels); + initialSync = 0; + } - // get timer value so we can get the real age - timer_get_counter_value(TIMER_GROUP_0, TIMER_1, &timer_val); - timer_pause(TIMER_GROUP_0, TIMER_1); + ESP_LOGI (TAG, + "snapserver config changed, buffer %dms, chunk %dms, " + "sample rate %d, ch %d, bits %d", + snapcastSetting.buffer_ms, + snapcastSetting.chunkDuration_ms, + snapcastSetting.sampleRate, snapcastSetting.channels, + snapcastSetting.bits); - // get actual age after alarm -// age = ((int64_t)timer_val - (-age) * 10) / 10; // timer with 100ns ticks - age = (int64_t)timer_val - (-age); // timer with 1µs ticks - - // check if we need to write remaining data - if (size != 0) { - do { - written = 0; - if (i2s_custom_write(I2S_NUM_0, p_payload, (size_t)size, &written, - portMAX_DELAY) != ESP_OK) { - ESP_LOGE(TAG, "i2s_playback_task: I2S write error"); - } - if (written < size) { - ESP_LOGE(TAG, "i2s_playback_task: I2S didn't write all data"); - } - size -= written; - p_payload += written; - - if (size == 0) { - if (fragment->nextFragment != NULL) { - fragment = fragment->nextFragment; - p_payload = fragment->payload; - size = fragment->size; - } - else { - free_pcm_chunk(chnk); - chnk = NULL; - - break; - } - } - } while(1); - } - - initialSync = 1; - - ESP_LOGI(TAG, "initial sync %lldus", age); + gotSnapserverConfig = true; + } + else if (gotSnapserverConfig == false) + { + vTaskDelay (pdMS_TO_TICKS (10)); continue; } - } else if ((age > 0) && (initialSync == 0)) { - if (chnk != NULL) { - free_pcm_chunk(chnk); - chnk = NULL; + + if (chnk == NULL) + { + ret = xQueueReceive (pcmChunkQueueHandle, &chnk, + pdMS_TO_TICKS (2000)); + if (ret != pdFAIL) + { + // ESP_LOGW(TAG, "got pcm chunk"); + } + } + else + { + // ESP_LOGW(TAG, "already retrieved chunk needs + // service"); + ret = pdPASS; } - int64_t t; - get_diff_to_server(&t); - ESP_LOGW(TAG, "RESYNCING HARD 1 %lldus, %lldus", age, t); + if (ret != pdFAIL) + { + if (server_now (&serverNow) >= 0) + { + age = serverNow + - ((int64_t)chnk->timestamp.sec * 1000000LL + + (int64_t)chnk->timestamp.usec) + - (int64_t)buffer_us_local + (int64_t)clientDacLatency_us; + } + else + { + // ESP_LOGW(TAG, "couldn't get server now"); - dir = 0; + if (chnk != NULL) + { + free_pcm_chunk (chnk); + chnk = NULL; + } - initialSync = 0; + vTaskDelay (pdMS_TO_TICKS (1)); - continue; - } - - if (initialSync == 1) { - const uint8_t enableControlLoop = 1; - const int64_t age_expect = -chunkDuration_us * 1; // this value is highly coupled with I2S DMA buffer size. - // DMA buffer has a size of 1 chunk (e.g. 20ms) so next chunk - // we get from queue will be -20ms - const int64_t maxOffset = 50; //µs, softsync 1 - const int64_t hardResyncThreshold = 3000; //µs, hard sync - - avg = MEDIANFILTER_Insert(&shortMedianFilter, age + (-age_expect)); - if (MEDIANFILTER_isFull(&shortMedianFilter) == 0) { - avg = age + (-age_expect); - } else { - // resync hard if we are off too far - if ((avg < -hardResyncThreshold) || (avg > hardResyncThreshold) || - (initialSync == 0)) { - if (chnk != NULL) { - free_pcm_chunk(chnk); - chnk = NULL; + continue; } - int64_t t; - get_diff_to_server(&t); - ESP_LOGW(TAG, "RESYNCING HARD 2 %lldus, %lldus, %lldus", age, avg, - t); + // wait for early time syncs to be ready + int tmp = latency_buffer_full (); + if (tmp <= 0) + { + if (tmp < 0) + { + vTaskDelay (1); - initialSync = 0; + continue; + } - continue; - } + if (age >= 0) + { + if (chnk != NULL) + { + free_pcm_chunk (chnk); + chnk = NULL; + } + } + + // ESP_LOGW(TAG, "diff buffer not full"); + + vTaskDelay (pdMS_TO_TICKS (10)); + + continue; + } + + if (age < 0) + { // get initial sync using hardware timer + if (initialSync == 0) + { + tg0_timer1_start ( + -age - alarmValSub); // alarm a little earlier to account + // for context switch duration from + // freeRTOS, timer with 1µs ticks + + // tg0_timer1_start((-age * 10) - alarmValSub)); + // // alarm a + // little earlier to account for context switch duration from + // freeRTOS, timer with 100ns ticks + + i2s_custom_stop (I2S_NUM_0); + + if (MEDIANFILTER_Init (&shortMedianFilter)) + { + ESP_LOGE (TAG, "snapcast_sync_task: couldn't init " + "shortMedianFilter. STOP"); + + return; + } + + adjust_apll (0); // reset to normal playback speed + + fragment = chnk->fragment; + p_payload = fragment->payload; + size = fragment->size; + + i2s_custom_init_dma_tx_queues ( + I2S_NUM_0, (uint8_t *)p_payload, size, &written); + size -= written; + p_payload += written; + + // ESP_LOGE(TAG, "wrote %d", written); + + if (size == 0) + { + if (fragment->nextFragment != NULL) + { + fragment = fragment->nextFragment; + p_payload = fragment->payload; + size = fragment->size; + } + else + { + free_pcm_chunk (chnk); + chnk = NULL; + } + } + + // Wait to be notified of a timer interrupt. + xTaskNotifyWait ( + pdFALSE, // Don't clear bits on entry. + pdFALSE, // Don't clear bits on exit. + ¬ifiedValue, // Stores the notified value. + portMAX_DELAY); + // or use simple task delay for this + // vTaskDelay( pdMS_TO_TICKS(-age / 1000) ); + + i2s_custom_start (I2S_NUM_0); + + // get timer value so we can get the real age + timer_get_counter_value (TIMER_GROUP_0, TIMER_1, &timer_val); + timer_pause (TIMER_GROUP_0, TIMER_1); + + // get actual age after alarm + // age = ((int64_t)timer_val - (-age) * 10) / 10; + // // timer with 100ns ticks + age = (int64_t)timer_val - (-age); // timer with 1µs ticks + + // check if we need to write remaining data + if (size != 0) + { + do + { + written = 0; + if (i2s_custom_write (I2S_NUM_0, p_payload, + (size_t)size, &written, + portMAX_DELAY) + != ESP_OK) + { + ESP_LOGE (TAG, + "i2s_playback_task: I2S write error"); + } + if (written < size) + { + ESP_LOGE (TAG, "i2s_playback_task: I2S didn't " + "write all data"); + } + size -= written; + p_payload += written; + + if (size == 0) + { + if (fragment->nextFragment != NULL) + { + fragment = fragment->nextFragment; + p_payload = fragment->payload; + size = fragment->size; + } + else + { + free_pcm_chunk (chnk); + chnk = NULL; + + break; + } + } + } + while (1); + } + + initialSync = 1; + + ESP_LOGI (TAG, "initial sync %lldus", age); + + continue; + } + } + else if ((age > 0) && (initialSync == 0)) + { + if (chnk != NULL) + { + free_pcm_chunk (chnk); + chnk = NULL; + } + + int64_t t; + get_diff_to_server (&t); + ESP_LOGW (TAG, "RESYNCING HARD 1 %lldus, %lldus", age, t); + + dir = 0; + + initialSync = 0; + + continue; + } + + if (initialSync == 1) + { + const uint8_t enableControlLoop = 1; + const int64_t age_expect + = -chunkDuration_us + * 1; // this value is highly coupled with I2S DMA buffer + // size. DMA buffer has a size of 1 chunk (e.g. 20ms) + // so next chunk we get from queue will be -20ms + const int64_t maxOffset = 50; //µs, softsync 1 + const int64_t hardResyncThreshold = 3000; //µs, hard sync + + avg = MEDIANFILTER_Insert (&shortMedianFilter, + age + (-age_expect)); + if (MEDIANFILTER_isFull (&shortMedianFilter) == 0) + { + avg = age + (-age_expect); + } + else + { + // resync hard if we are off too far + if ((avg < -hardResyncThreshold) + || (avg > hardResyncThreshold) || (initialSync == 0)) + { + if (chnk != NULL) + { + free_pcm_chunk (chnk); + chnk = NULL; + } + + int64_t t; + get_diff_to_server (&t); + ESP_LOGW (TAG, "RESYNCING HARD 2 %lldus, %lldus, %lldus", + age, avg, t); + + initialSync = 0; + + continue; + } + } + + if (enableControlLoop == 1) + { + if (avg < -maxOffset) + { // we are early + dir = -1; + } + else if ((avg >= -maxOffset) && (avg <= maxOffset)) + { + dir = 0; + } + else if (avg > maxOffset) + { // we are late + dir = 1; + } + + adjust_apll (dir); + } + + fragment = chnk->fragment; + p_payload = fragment->payload; + size = fragment->size; + + do + { + written = 0; + if (i2s_custom_write (I2S_NUM_0, p_payload, (size_t)size, + &written, portMAX_DELAY) + != ESP_OK) + { + ESP_LOGE (TAG, "i2s_playback_task: I2S write error"); + } + if (written < size) + { + ESP_LOGE ( + TAG, "i2s_playback_task: I2S didn't write all data"); + } + size -= written; + p_payload += written; + + if (size == 0) + { + if (fragment->nextFragment != NULL) + { + fragment = fragment->nextFragment; + p_payload = fragment->payload; + size = fragment->size; + + ESP_LOGI (TAG, "i2s_playback_task: fragmented"); + } + else + { + free_pcm_chunk (chnk); + chnk = NULL; + + break; + } + } + } + while (1); + } + + int64_t t; + get_diff_to_server (&t); + ESP_LOGI (TAG, "%d: %lldus, %lldus %lldus", dir, age, avg, t); } + else + { + int64_t t; + get_diff_to_server (&t); + ESP_LOGE (TAG, + "Couldn't get PCM chunk, recv: messages waiting %d, " + "latency %lldus", + uxQueueMessagesWaiting (pcmChunkQueueHandle), t); - if (enableControlLoop == 1) { - if (avg < -maxOffset) { // we are early - dir = -1; - } else if ((avg >= -maxOffset) && (avg <= maxOffset)) { - dir = 0; - } else if (avg > maxOffset) { // we are late - dir = 1; - } + dir = 0; - adjust_apll(dir); + initialSync = 0; } - - fragment = chnk->fragment; - p_payload = fragment->payload; - size = fragment->size; - - do { - written = 0; - if (i2s_custom_write(I2S_NUM_0, p_payload, (size_t)size, &written, - portMAX_DELAY) != ESP_OK) { - ESP_LOGE(TAG, "i2s_playback_task: I2S write error"); - } - if (written < size) { - ESP_LOGE(TAG, "i2s_playback_task: I2S didn't write all data"); - } - size -= written; - p_payload += written; - - if (size == 0) { - if (fragment->nextFragment != NULL) { - fragment = fragment->nextFragment; - p_payload = fragment->payload; - size = fragment->size; - - ESP_LOGI(TAG, "i2s_playback_task: fragmented"); - } - else { - free_pcm_chunk(chnk); - chnk = NULL; - - break; - } - } - } while(1); - } - - int64_t t; - get_diff_to_server(&t); - ESP_LOGI(TAG, "%d: %lldus, %lldus %lldus", dir, age, avg, t); - } else { - int64_t t; - get_diff_to_server(&t); - ESP_LOGE( - TAG, - "Couldn't get PCM chunk, recv: messages waiting %d, latency %lldus", - uxQueueMessagesWaiting(pcmChunkQueueHandle), t); - - dir = 0; - - initialSync = 0; } - } } diff --git a/components/net_functions/net_functions.c b/components/net_functions/net_functions.c index b3e66ef..f1209b2 100644 --- a/components/net_functions/net_functions.c +++ b/components/net_functions/net_functions.c @@ -24,128 +24,136 @@ 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"}; +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) { - ESP_LOGI(TAG, "Setup mdns"); - ESP_ERROR_CHECK(mdns_init()); - ESP_ERROR_CHECK(mdns_hostname_set(clientname)); - ESP_ERROR_CHECK(mdns_instance_name_set("ESP32 SNAPcast client OTA")); - ESP_ERROR_CHECK(mdns_service_add(NULL, "_http", "_tcp", 8032, NULL, 0)); +void +net_mdns_register (const char *clientname) +{ + ESP_LOGI (TAG, "Setup mdns"); + ESP_ERROR_CHECK (mdns_init ()); + ESP_ERROR_CHECK (mdns_hostname_set (clientname)); + ESP_ERROR_CHECK (mdns_instance_name_set ("ESP32 SNAPcast client OTA")); + ESP_ERROR_CHECK (mdns_service_add (NULL, "_http", "_tcp", 8032, NULL, 0)); } -void mdns_print_results(mdns_result_t *results) { +void +mdns_print_results (mdns_result_t *results) +{ mdns_result_t *r = results; mdns_ip_addr_t *a = NULL; int i = 1, t; - while (r) { - printf("%d: Interface: %s, Type: %s\n", i++, if_str[r->tcpip_if], - ip_protocol_str[r->ip_protocol]); - if (r->instance_name) { - printf(" PTR : %s\n", r->instance_name); + while (r) + { + printf ("%d: Interface: %s, Type: %s\n", i++, if_str[r->tcpip_if], + ip_protocol_str[r->ip_protocol]); + if (r->instance_name) + { + printf (" PTR : %s\n", r->instance_name); + } + if (r->hostname) + { + printf (" SRV : %s.local:%u\n", r->hostname, r->port); + } + if (r->txt_count) + { + printf (" TXT : [%u] ", r->txt_count); + for (t = 0; t < r->txt_count; t++) + { + printf ("%s=%s; ", r->txt[t].key, r->txt[t].value); + } + printf ("\n"); + } + a = r->addr; + while (a) + { + if (a->addr.type == IPADDR_TYPE_V6) + { + printf (" AAAA: " IPV6STR "\n", IPV62STR (a->addr.u_addr.ip6)); + } + else + { + printf (" A : " IPSTR "\n", IP2STR (&(a->addr.u_addr.ip4))); + } + a = a->next; + } + r = r->next; } - if (r->hostname) { - printf(" SRV : %s.local:%u\n", r->hostname, r->port); - } - if (r->txt_count) { - printf(" TXT : [%u] ", r->txt_count); - for (t = 0; t < r->txt_count; t++) { - printf("%s=%s; ", r->txt[t].key, r->txt[t].value); - } - printf("\n"); - } - a = r->addr; - while (a) { - if (a->addr.type == IPADDR_TYPE_V6) { - printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6)); - } else { - printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4))); - } - a = a->next; - } - r = r->next; - } } -uint32_t find_mdns_service(const char *service_name, const char *proto) { - ESP_LOGI(TAG, "Query PTR: %s.%s.local", service_name, proto); +uint32_t +find_mdns_service (const char *service_name, const char *proto) +{ + ESP_LOGI (TAG, "Query PTR: %s.%s.local", service_name, proto); mdns_result_t *r = NULL; - esp_err_t err = mdns_query_ptr(service_name, proto, 3000, 20, &r); - if (err) { - ESP_LOGE(TAG, "Query Failed"); - return -1; - } - if (!r) { - ESP_LOGW(TAG, "No results found!"); - return -1; - } + esp_err_t err = mdns_query_ptr (service_name, proto, 3000, 20, &r); + if (err) + { + ESP_LOGE (TAG, "Query Failed"); + return -1; + } + if (!r) + { + ESP_LOGW (TAG, "No results found!"); + return -1; + } - if (r->instance_name) { - printf(" PTR : %s\n", r->instance_name); - } - if (r->hostname) { - printf(" SRV : %s.local:%u\n", r->hostname, r->port); - return r->port; - } - mdns_query_results_free(r); + if (r->instance_name) + { + printf (" PTR : %s\n", r->instance_name); + } + if (r->hostname) + { + printf (" SRV : %s.local:%u\n", r->hostname, r->port); + return r->port; + } + mdns_query_results_free (r); return 0; } -static int sntp_synced = 0; -/* -void sntp_sync_time(struct timeval *tv_ntp) { - if ((sntp_synced%10) == 0) { - settimeofday(tv_ntp,NULL); - sntp_synced++; - ESP_LOGI(TAG,"SNTP time set from server number :%d",sntp_synced); - return; - } - sntp_synced++; - struct timeval tv_esp; - gettimeofday(&tv_esp, NULL); - //ESP_LOGI(TAG,"SNTP diff s: %ld , %ld ", tv_esp.tv_sec , tv_ntp->tv_sec); - ESP_LOGI(TAG,"SNTP diff us: %ld , %ld ", tv_esp.tv_usec , tv_ntp->tv_usec); - ESP_LOGI(TAG,"SNTP diff us: %.2f", (double)((tv_esp.tv_usec - -tv_ntp->tv_usec)/1000.0)); - -}*/ - -void sntp_cb(struct timeval *tv) { - struct tm timeinfo = {0}; +/** + * + */ +void +sntp_cb (struct timeval *tv) +{ + struct tm timeinfo = { 0 }; time_t now = tv->tv_sec; - localtime_r(&now, &timeinfo); + localtime_r (&now, &timeinfo); char strftime_buf[64]; - strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo); - ESP_LOGI(TAG, "sntp_cb called :%s", strftime_buf); + strftime (strftime_buf, sizeof (strftime_buf), "%c", &timeinfo); + ESP_LOGI (TAG, "sntp_cb called :%s", strftime_buf); } -void set_time_from_sntp() { - xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT, false, true, - portMAX_DELAY); +void +set_time_from_sntp () +{ + xEventGroupWaitBits (s_wifi_event_group, WIFI_CONNECTED_BIT, false, true, + portMAX_DELAY); // ESP_LOGI(TAG, "clock %"); - ESP_LOGI(TAG, "Initializing SNTP"); - sntp_setoperatingmode(SNTP_OPMODE_POLL); - sntp_setservername(0, CONFIG_SNTP_SERVER); - sntp_init(); + ESP_LOGI (TAG, "Initializing SNTP"); + sntp_setoperatingmode (SNTP_OPMODE_POLL); + sntp_setservername (0, CONFIG_SNTP_SERVER); + sntp_init (); // sntp_set_time_sync_notification_cb(sntp_cb); - setenv("TZ", SNTP_TIMEZONE, 1); - tzset(); + setenv ("TZ", SNTP_TIMEZONE, 1); + tzset (); time_t now = 0; - struct tm timeinfo = {0}; + struct tm timeinfo = { 0 }; int retry = 0; const int retry_count = 10; - while (timeinfo.tm_year < (2016 - 1900) && ++retry < retry_count) { - ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, - retry_count); - vTaskDelay(2000 / portTICK_PERIOD_MS); - time(&now); - localtime_r(&now, &timeinfo); - } + while (timeinfo.tm_year < (2016 - 1900) && ++retry < retry_count) + { + ESP_LOGI (TAG, "Waiting for system time to be set... (%d/%d)", retry, + retry_count); + vTaskDelay (2000 / portTICK_PERIOD_MS); + time (&now); + localtime_r (&now, &timeinfo); + } char strftime_buf[64]; - strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo); - ESP_LOGI(TAG, "The current date/time in UTC is: %s", strftime_buf); + strftime (strftime_buf, sizeof (strftime_buf), "%c", &timeinfo); + ESP_LOGI (TAG, "The current date/time in UTC is: %s", strftime_buf); } diff --git a/components/rtprx/rtprx.c b/components/rtprx/rtprx.c index 850c44a..0022584 100644 --- a/components/rtprx/rtprx.c +++ b/components/rtprx/rtprx.c @@ -6,21 +6,22 @@ * * \brief RTP audio stream receiver * - * \warning This software is a PROTOTYPE version and is not designed or intended - * for use in production, especially not for safety-critical applications! The - * user represents and warrants that it will NOT use or redistribute the - * Software for such purposes. This prototype is for research purposes only. - * This software is provided "AS IS," without a warranty of any kind. + * \warning This software is a PROTOTYPE version and is not designed or + * intended for use in production, especially not for safety-critical + * applications! The user represents and warrants that it will NOT use or + * redistribute the Software for such purposes. This prototype is for research + * purposes only. This software is provided "AS IS," without a warranty of any + * kind. */ #include "rtprx.h" #include -#include "i2s.h" #include "esp_log.h" #include "esp_netif.h" #include "esp_wifi.h" #include "freertos/FreeRTOS.h" +#include "i2s.h" #include "lwip/api.h" #include "lwip/err.h" #include "lwip/sockets.h" @@ -31,34 +32,42 @@ static bool rtpRxState = 0; -void rtp_rx_start() { - if (rtpRxState == 0) { - setup_rtp_i2s(); - xTaskCreate(rtp_rx_task, "RTPRx", 12 * 1024, NULL, 0, NULL); - rtpRxState = 1; - } +void +rtp_rx_start () +{ + if (rtpRxState == 0) + { + setup_rtp_i2s (); + xTaskCreate (rtp_rx_task, "RTPRx", 12 * 1024, NULL, 0, NULL); + rtpRxState = 1; + } } -void rtp_rx_stop() { - if (rtpRxState == 1) { - i2s_custom_driver_uninstall(0); - // vTaskDelete(xTaskSignal); xxx Fix me - rtpRxState = 0; - } +void +rtp_rx_stop () +{ + if (rtpRxState == 1) + { + i2s_custom_driver_uninstall (0); + // vTaskDelete(xTaskSignal); xxx Fix me + rtpRxState = 0; + } } -void rtp_rx_task(void *pvParameters) { +void +rtp_rx_task (void *pvParameters) +{ OpusDecoder *decoder; // int size = opus_decoder_get_size(2); int oe = 0; - decoder = opus_decoder_create(48000, 2, &oe); + decoder = opus_decoder_create (48000, 2, &oe); // int error = opus_decoder_init(decoder, 48000, 2); - printf("Initialized Decoder: %d", oe); + printf ("Initialized Decoder: %d", oe); // int32_t *audio32 = (int32_t*)malloc(960*sizeof(int32_t)); - int16_t *audio = (int16_t *)malloc(960 * 2 * sizeof(int16_t)); - i2s_custom_zero_dma_buffer(0); + int16_t *audio = (int16_t *)malloc (960 * 2 * sizeof (int16_t)); + i2s_custom_zero_dma_buffer (0); static struct netconn *conn; static struct netbuf *buf; static uint32_t pkg = 0; @@ -66,115 +75,129 @@ void rtp_rx_task(void *pvParameters) { err_t err; uint16_t oldseq = 1; uint16_t first = 1; - conn = netconn_new(NETCONN_UDP); - if (conn != NULL) { - printf("Net RTP RX\n"); - netconn_bind(conn, IP_ADDR_ANY, 1350); - netconn_listen(conn); - printf("Net RTP will enter loopn\n"); - while (1) { - netconn_recv(conn, &buf); - if (buf == NULL) { - printf("NETCONN RX error \n"); - } - pkg++; + conn = netconn_new (NETCONN_UDP); + if (conn != NULL) + { + printf ("Net RTP RX\n"); + netconn_bind (conn, IP_ADDR_ANY, 1350); + netconn_listen (conn); + printf ("Net RTP will enter loopn\n"); + while (1) + { + netconn_recv (conn, &buf); + if (buf == NULL) + { + printf ("NETCONN RX error \n"); + } + pkg++; - uint8_t *p = (buf->p->payload); - uint16_t seq = (p[2] << 8) + p[3]; - if ((seq != oldseq + 1) & (first != 1)) { - printf("seq : %d, oldseq : %d \n", seq, oldseq); - uint16_t errors = seq - oldseq - 1; - pkgerror = pkgerror + errors; - printf("ERROR --- Package drop : %d %d \n", errors, pkgerror); - size_t bWritten; - // for (int i = 0; i;i++ ) - int ret = i2s_custom_write_expand(0, (char *)audio, 960 * 2 * sizeof(int16_t), - 16, 32, &bWritten, 100); - printf("bWritten : %d ret : %d \n ", bWritten, ret); + uint8_t *p = (buf->p->payload); + uint16_t seq = (p[2] << 8) + p[3]; + if ((seq != oldseq + 1) & (first != 1)) + { + printf ("seq : %d, oldseq : %d \n", seq, oldseq); + uint16_t errors = seq - oldseq - 1; + pkgerror = pkgerror + errors; + printf ("ERROR --- Package drop : %d %d \n", errors, pkgerror); + size_t bWritten; + // for (int i = 0; i;i++ ) + int ret = i2s_custom_write_expand (0, (char *)audio, + 960 * 2 * sizeof (int16_t), + 16, 32, &bWritten, 100); + printf ("bWritten : %d ret : %d \n ", bWritten, ret); - // opus_pkg = NULL; - } + // opus_pkg = NULL; + } - if (seq < oldseq) { - printf("ERROR --- Package order:"); - } - oldseq = seq; - first = 0; - // printf("UDP package len : %d -> \n", buf->p->len); - // printf("UDP package : %02x %02x %02x %02x\n",p[0],p[1],p[2],p[3]); - // printf("Timestamp : %02x %02x %02x %02x\n",p[4],p[5],p[6],p[7]); - // printf("Sync source : %02x %02x %02x - // %02x\n",p[8],p[9],p[10],p[11]); printf("R1 : %d - // \n",(p[12]&0xf8)>>3) ; int size = 240; - unsigned char *opus_pkg = buf->p->payload + 12; - int size = opus_decode(decoder, (unsigned char *)opus_pkg, + if (seq < oldseq) + { + printf ("ERROR --- Package order:"); + } + oldseq = seq; + first = 0; + // printf("UDP package len : %d -> \n", buf->p->len); + // printf("UDP package : %02x %02x %02x + // %02x\n",p[0],p[1],p[2],p[3]); printf("Timestamp : %02x %02x + // %02x %02x\n",p[4],p[5],p[6],p[7]); printf("Sync source : %02x + // %02x %02x %02x\n",p[8],p[9],p[10],p[11]); printf("R1 : %d + // \n",(p[12]&0xf8)>>3) ; int size = 240; + unsigned char *opus_pkg = buf->p->payload + 12; + int size + = opus_decode (decoder, (unsigned char *)opus_pkg, buf->p->len - 12, (opus_int16 *)audio, 960, 0); - if (size < 0) { - printf("Decode error : %d \n", size); - } + if (size < 0) + { + printf ("Decode error : %d \n", size); + } - // for (int i = 0; i < size*2; i++) { - // audio[i*2] = 0x0000; - // audio[i*2+1] = 0x0000; - //} + // for (int i = 0; i < size*2; i++) { + // audio[i*2] = 0x0000; + // audio[i*2+1] = 0x0000; + //} - size_t bWritten; - int ret = i2s_custom_write_expand(0, (char *)audio, size * 2 * sizeof(int16_t), - 16, 32, &bWritten, 100); - if (ret != 0) - printf("Error I2S written: %d %d %d \n", ret, - size * 2 * sizeof(int16_t), bWritten); + size_t bWritten; + int ret = i2s_custom_write_expand (0, (char *)audio, + size * 2 * sizeof (int16_t), 16, + 32, &bWritten, 100); + if (ret != 0) + printf ("Error I2S written: %d %d %d \n", ret, + size * 2 * sizeof (int16_t), bWritten); - if ((pkg % 1000) == 1) { - // printf("I2S written: %d %d \n", size*sizeof(int32_t) ,bWritten); - printf("%d > %d %d %d\n", pkg, size, buf->p->len, buf->p->tot_len); - printf("UDP package len : %d -> \n", buf->p->len); - printf("UDP package : %02x %02x %02x %02x\n", p[0], p[1], p[2], - p[3]); - printf("Timestamp : %02x %02x %02x %02x\n", p[4], p[5], p[6], - p[7]); - printf("Sync source : %02x %02x %02x %02x\n", p[8], p[9], p[10], - p[11]); - printf("R1 : %d \n", (p[12] & 0xf8) >> 3); + if ((pkg % 1000) == 1) + { + // printf("I2S written: %d %d \n", size*sizeof(int32_t) + // ,bWritten); + printf ("%d > %d %d %d\n", pkg, size, buf->p->len, + buf->p->tot_len); + printf ("UDP package len : %d -> \n", buf->p->len); + printf ("UDP package : %02x %02x %02x %02x\n", p[0], p[1], + p[2], p[3]); + printf ("Timestamp : %02x %02x %02x %02x\n", p[4], p[5], + p[6], p[7]); + printf ("Sync source : %02x %02x %02x %02x\n", p[8], p[9], + p[10], p[11]); + printf ("R1 : %d \n", (p[12] & 0xf8) >> 3); - for (int i = 0; i < 8; i++) - printf("%02d %04x %04x\n", i, audio[2 * i], audio[2 * i + 1]); - } + for (int i = 0; i < 8; i++) + printf ("%02d %04x %04x\n", i, audio[2 * i], audio[2 * i + 1]); + } - // netbuf_free(buf); - netbuf_delete(buf); + // netbuf_free(buf); + netbuf_delete (buf); + } } - } - netconn_close(conn); - netconn_delete(conn); + netconn_close (conn); + netconn_delete (conn); } -void setup_rtp_i2s() { +void +setup_rtp_i2s () +{ i2s_config_t i2s_config = { - .mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX - .sample_rate = 48000, - .bits_per_sample = 32, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // 2-channels - .communication_format = I2S_COMM_FORMAT_I2S, - .dma_buf_count = 8, - .dma_buf_len = 480, - //.intr_alloc_flags = 1, //Default interrupt priority - .use_apll = true, - .fixed_mclk = 0, - .tx_desc_auto_clear = true // Auto clear tx descriptor on underflow + .mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX + .sample_rate = 48000, + .bits_per_sample = 32, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // 2-channels + .communication_format = I2S_COMM_FORMAT_STAND_I2S, + .dma_buf_count = 8, + .dma_buf_len = 480, + //.intr_alloc_flags = 1, //Default interrupt priority + .use_apll = true, + .fixed_mclk = 0, + .tx_desc_auto_clear = true // Auto clear tx descriptor on underflow }; - i2s_custom_driver_install(0, &i2s_config, 0, NULL); - i2s_custom_start(0); - i2s_custom_zero_dma_buffer(0); + i2s_custom_driver_install (0, &i2s_config, 0, NULL); + i2s_custom_start (0); + i2s_custom_zero_dma_buffer (0); i2s_pin_config_t pin_config = { - .bck_io_num = 12, // CONFIG_EXAMPLE_I2S_BCK_PIN, - .ws_io_num = 13, // CONFIG_EXAMPLE_I2S_LRCK_PIN, - .data_out_num = 14, // CONFIG_EXAMPLE_I2S_DATA_PIN, - .data_in_num = -1 // Not used + .bck_io_num = 12, // CONFIG_EXAMPLE_I2S_BCK_PIN, + .ws_io_num = 13, // CONFIG_EXAMPLE_I2S_LRCK_PIN, + .data_out_num = 14, // CONFIG_EXAMPLE_I2S_DATA_PIN, + .data_in_num = -1 // Not used }; - i2s_custom_set_pin(0, &pin_config); - printf("Here... set pin\n"); + i2s_custom_set_pin (0, &pin_config); + printf ("Here... set pin\n"); } diff --git a/components/websocket/websocket_server.c b/components/websocket/websocket_server.c index d419700..dc7d12a 100644 --- a/components/websocket/websocket_server.c +++ b/components/websocket/websocket_server.c @@ -19,413 +19,504 @@ This program is free software: you can redistribute it and/or modify #include "websocket_server.h" #include "freertos/FreeRTOS.h" +#include "freertos/queue.h" #include "freertos/semphr.h" #include "freertos/task.h" -#include "freertos/queue.h" #include static SemaphoreHandle_t xwebsocket_mutex; // to lock the client array -static QueueHandle_t xwebsocket_queue; // to hold the clients that send messages -static ws_client_t clients[WEBSOCKET_SERVER_MAX_CLIENTS]; // holds list of clients -static TaskHandle_t xtask; // the task itself +static QueueHandle_t + xwebsocket_queue; // to hold the clients that send messages +static ws_client_t + clients[WEBSOCKET_SERVER_MAX_CLIENTS]; // holds list of clients +static TaskHandle_t xtask; // the task itself -static void background_callback(struct netconn* conn, enum netconn_evt evt,u16_t len) { - switch(evt) { +static void +background_callback (struct netconn *conn, enum netconn_evt evt, u16_t len) +{ + switch (evt) + { case NETCONN_EVT_RCVPLUS: - xQueueSendToBack(xwebsocket_queue,&conn,WEBSOCKET_SERVER_QUEUE_TIMEOUT); + xQueueSendToBack (xwebsocket_queue, &conn, + WEBSOCKET_SERVER_QUEUE_TIMEOUT); break; default: break; - } + } } -static void handle_read(uint8_t num) { +static void +handle_read (uint8_t num) +{ ws_header_t header; - char* msg; + char *msg; header.received = 0; - msg = ws_read(&clients[num],&header); + msg = ws_read (&clients[num], &header); - if(!header.received) { - if(msg) free(msg); - return NULL; - } + if (!header.received) + { + if (msg) + free (msg); + return; + } - switch(clients[num].last_opcode) { + switch (clients[num].last_opcode) + { case WEBSOCKET_OPCODE_CONT: break; case WEBSOCKET_OPCODE_BIN: - clients[num].scallback(num,WEBSOCKET_BIN,msg,header.length); + clients[num].scallback (num, WEBSOCKET_BIN, msg, header.length); break; case WEBSOCKET_OPCODE_TEXT: - clients[num].scallback(num,WEBSOCKET_TEXT,msg,header.length); + clients[num].scallback (num, WEBSOCKET_TEXT, msg, header.length); break; case WEBSOCKET_OPCODE_PING: - ws_send(&clients[num],WEBSOCKET_OPCODE_PONG,msg,header.length,0); - clients[num].scallback(num,WEBSOCKET_PING,msg,header.length); + ws_send (&clients[num], WEBSOCKET_OPCODE_PONG, msg, header.length, 0); + clients[num].scallback (num, WEBSOCKET_PING, msg, header.length); break; case WEBSOCKET_OPCODE_PONG: - if(clients[num].ping) { - clients[num].scallback(num,WEBSOCKET_PONG,NULL,0); - clients[num].ping = 0; - } + if (clients[num].ping) + { + clients[num].scallback (num, WEBSOCKET_PONG, NULL, 0); + clients[num].ping = 0; + } break; case WEBSOCKET_OPCODE_CLOSE: - clients[num].scallback(num,WEBSOCKET_DISCONNECT_EXTERNAL,NULL,0); - ws_disconnect_client(&clients[num], 0); + clients[num].scallback (num, WEBSOCKET_DISCONNECT_EXTERNAL, NULL, 0); + ws_disconnect_client (&clients[num], 0); break; default: break; - } - if(msg) free(msg); + } + if (msg) + free (msg); } -static void ws_server_task(void* pvParameters) { - struct netconn* conn; +static void +ws_server_task (void *pvParameters) +{ + struct netconn *conn; - xwebsocket_mutex = xSemaphoreCreateMutex(); - xwebsocket_queue = xQueueCreate(WEBSOCKET_SERVER_QUEUE_SIZE, sizeof(struct netconn*)); + xwebsocket_mutex = xSemaphoreCreateMutex (); + xwebsocket_queue + = xQueueCreate (WEBSOCKET_SERVER_QUEUE_SIZE, sizeof (struct netconn *)); // initialize all clients - for(int i=0;icallback = background_callback; - netconn_write(conn,handshake,strlen(handshake),NETCONN_COPY); + netconn_write (conn, handshake, strlen (handshake), NETCONN_COPY); - for(int i=0;i #include +#include #include "esp_event.h" #include "esp_log.h" @@ -46,7 +46,6 @@ #include "player.h" #include "snapcast.h" - //#include "ma120.h" const char *VERSION_STRING = "0.0.2"; @@ -64,21 +63,18 @@ xQueueHandle prot_queue; static snapcastSetting_t snapcastSetting; -volatile int64_t clientDacLatency = 0; -uint32_t buffer_ms = 400; -uint8_t muteCH[4] = {0}; +// static int64_t clientDacLatency = 0; +// uint32_t buffer_ms = 400; +// uint8_t muteCH[4] = {0}; struct timeval tdif, tavg; audio_board_handle_t board_handle = NULL; -int timeval_subtract(struct timeval *result, struct timeval *x, - struct timeval *y); - /* snapast parameters; configurable in menuconfig */ #define SNAPCAST_SERVER_USE_MDNS CONFIG_SNAPSERVER_USE_MDNS #if !SNAPCAST_SERVER_USE_MDNS - #define SNAPCAST_SERVER_HOST CONFIG_SNAPSERVER_HOST - #define SNAPCAST_SERVER_PORT CONFIG_SNAPSERVER_PORT +#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 @@ -87,35 +83,40 @@ int timeval_subtract(struct timeval *result, struct timeval *x, static const char *TAG = "SNAPCAST"; extern char mac_address[18]; -extern EventGroupHandle_t s_wifi_event_group; static QueueHandle_t playerChunkQueueHandle; SemaphoreHandle_t timeSyncSemaphoreHandle = NULL; #if CONFIG_USE_DSP_PROCESSOR -uint8_t dspFlow = dspfStereo; // dspfBiamp; // dspfStereo; // dspfBassBoost; +uint8_t dspFlow = dspfStereo; // dspfBiamp; // dspfStereo; // dspfBassBoost; #endif /** * */ -void time_sync_msg_cb(void *args) { +void +time_sync_msg_cb (void *args) +{ static BaseType_t xHigherPriorityTaskWoken; // causes kernel panic, which shouldn't happen though? // Isn't it called from timer task instead of ISR? // xSemaphoreGive(timeSyncSemaphoreHandle); - xSemaphoreGiveFromISR(timeSyncSemaphoreHandle, &xHigherPriorityTaskWoken); + xSemaphoreGiveFromISR (timeSyncSemaphoreHandle, &xHigherPriorityTaskWoken); } /** * */ -static void http_get_task(void *pvParameters) { +static void +http_get_task (void *pvParameters) +{ struct sockaddr_in servaddr; char *start; int sock = -1; + base_message_t base_message; + hello_message_t hello_message; char base_message_serialized[BASE_MESSAGE_SIZE]; char time_message_serialized[TIME_MESSAGE_SIZE]; char *hello_message_serialized = NULL; @@ -123,12 +124,11 @@ static void http_get_task(void *pvParameters) { struct timeval now, trx, tdif, ttx; time_message_t time_message; struct timeval tmpDiffToServer; - struct timeval lastTimeSync = {0, 0}; - tv_t wire_chunk_last_timestamp = {0, 0}; + struct timeval lastTimeSync = { 0, 0 }; esp_timer_handle_t timeSyncMessageTimer = NULL; - const esp_timer_create_args_t tSyncArgs = {.callback = &time_sync_msg_cb, - .name = "tSyncMsg"}; - int16_t frameSize = 960; // 960*2: 20ms, 960*1: 10ms + const esp_timer_create_args_t tSyncArgs + = { .callback = &time_sync_msg_cb, .name = "tSyncMsg" }; + int16_t frameSize = 960; // 960*2: 20ms, 960*1: 10ms int16_t *audio = NULL; int16_t pcm_size = 120; uint16_t channels; @@ -136,814 +136,983 @@ static void http_get_task(void *pvParameters) { codec_header_message_t codec_header_message; server_settings_message_t server_settings_message; bool received_header = false; - base_message_t base_message; - hello_message_t hello_message; mdns_result_t *r; OpusDecoder *opusDecoder = NULL; codec_type_t codec = NONE; - bool chunkDurationDetected; // create a timer to send time sync messages every x µs - esp_timer_create(&tSyncArgs, &timeSyncMessageTimer); - timeSyncSemaphoreHandle = xSemaphoreCreateMutex(); - xSemaphoreGive(timeSyncSemaphoreHandle); + esp_timer_create (&tSyncArgs, &timeSyncMessageTimer); + timeSyncSemaphoreHandle = xSemaphoreCreateMutex (); + xSemaphoreGive (timeSyncSemaphoreHandle); id_counter = 0; #if CONFIG_SNAPCLIENT_USE_MDNS - ESP_LOGI(TAG, "Enable mdns"); - mdns_init(); + ESP_LOGI (TAG, "Enable mdns"); + mdns_init (); #endif - while (1) { - if (reset_latency_buffer() < 0) { - ESP_LOGE(TAG, - "reset_diff_buffer: couldn't reset median filter long. STOP"); + while (1) + { + if (reset_latency_buffer () < 0) + { + ESP_LOGE ( + TAG, + "reset_diff_buffer: couldn't reset median filter long. STOP"); - return; - } + return; + } - esp_timer_stop(timeSyncMessageTimer); - xSemaphoreGive(timeSyncSemaphoreHandle); + esp_timer_stop (timeSyncMessageTimer); + xSemaphoreGive (timeSyncSemaphoreHandle); - if (opusDecoder != NULL) { - opus_decoder_destroy(opusDecoder); - opusDecoder = NULL; - } + if (opusDecoder != NULL) + { + opus_decoder_destroy (opusDecoder); + opusDecoder = NULL; + } #if SNAPCAST_SERVER_USE_MDNS - // Find snapcast server - // Connect to first snapcast server found - r = NULL; - err = 0; - while (!r || err) { - ESP_LOGI(TAG, "Lookup snapcast service on network"); - esp_err_t err = mdns_query_ptr("_snapcast", "_tcp", 3000, 20, &r); - if (err) { - ESP_LOGE(TAG, "Query Failed"); - } - - if (!r) { - ESP_LOGW(TAG, "No results found!"); - } - - vTaskDelay(1000 / portTICK_PERIOD_MS); - } - - char serverAddr[] = "255.255.255.255"; - ESP_LOGI(TAG, "Found %s:%d", - inet_ntop(AF_INET, &(r->addr->addr.u_addr.ip4.addr), serverAddr, - sizeof(serverAddr)), - r->port); - - servaddr.sin_family = AF_INET; - servaddr.sin_addr.s_addr = r->addr->addr.u_addr.ip4.addr; - servaddr.sin_port = htons(r->port); - mdns_query_results_free(r); -#else - // configure a failsafe snapserver according to CONFIG values - servaddr.sin_family = AF_INET; - inet_pton(AF_INET, SNAPCAST_SERVER_HOST, &(servaddr.sin_addr.s_addr)); - servaddr.sin_port = htons(SNAPCAST_SERVER_PORT); -#endif - ESP_LOGI(TAG, "allocate socket"); - sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) { - ESP_LOGE(TAG, "... Failed to allocate socket."); - vTaskDelay(1000 / portTICK_PERIOD_MS); - continue; - } - ESP_LOGI(TAG, "... allocated socket %d", sock); - - ESP_LOGI(TAG, "connect to socket"); - err = - connect(sock, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in)); - if (err < 0) { - ESP_LOGE(TAG, "%s, %d", strerror(errno), errno); - - shutdown(sock, 2); - closesocket(sock); - - vTaskDelay(4000 / portTICK_PERIOD_MS); - - continue; - } - - ESP_LOGI(TAG, "... connected"); - - result = gettimeofday(&now, NULL); - if (result) { - ESP_LOGI(TAG, "Failed to gettimeofday\r\n"); - return; - } - - received_header = false; - chunkDurationDetected = false; - - // init base message - base_message.type = SNAPCAST_MESSAGE_HELLO; - base_message.id = 0x0000; - base_message.refersTo = 0x0000; - base_message.sent.sec = now.tv_sec; - base_message.sent.usec = now.tv_usec; - base_message.received.sec = 0; - base_message.received.usec = 0; - base_message.size = 0x00000000; - - // init hello message - hello_message.mac = mac_address; - hello_message.hostname = "ESP32-Caster"; - hello_message.version = (char *)VERSION_STRING; - hello_message.client_name = "libsnapcast"; - hello_message.os = "esp32"; - hello_message.arch = "xtensa"; - hello_message.instance = 1; - hello_message.id = mac_address; - hello_message.protocol_version = 2; - - if (hello_message_serialized == NULL) { - hello_message_serialized = hello_message_serialize( - &hello_message, (size_t *)&(base_message.size)); - if (!hello_message_serialized) { - ESP_LOGE(TAG, "Failed to serialize hello message\r\b"); - return; - } - } - - result = base_message_serialize(&base_message, base_message_serialized, - BASE_MESSAGE_SIZE); - if (result) { - ESP_LOGE(TAG, "Failed to serialize base message\r\n"); - return; - } - - result = send(sock, base_message_serialized, BASE_MESSAGE_SIZE, 0); - if (result < 0) { - ESP_LOGW(TAG, "error writing base msg to socket: %s", strerror(errno)); - - free(hello_message_serialized); - hello_message_serialized = NULL; - - shutdown(sock, 2); - closesocket(sock); - - continue; - } - - result = send(sock, hello_message_serialized, base_message.size, 0); - if (result < 0) { - ESP_LOGW(TAG, "error writing hello msg to socket: %s", strerror(errno)); - - free(hello_message_serialized); - hello_message_serialized = NULL; - - shutdown(sock, 2); - closesocket(sock); - - continue; - } - - free(hello_message_serialized); - hello_message_serialized = NULL; - - // init default setting - snapcastSetting.buffer_ms = 0; - snapcastSetting.codec = NONE; - snapcastSetting.bits = 0; - snapcastSetting.channels = 0; - snapcastSetting.sampleRate = 0; - snapcastSetting.chunkDuration_ms = 0; - snapcastSetting.volume = 0; - snapcastSetting.muted = false; - - for (;;) { - size = 0; - result = 0; - while (size < BASE_MESSAGE_SIZE) { - result = recv(sock, &(base_message_serialized[size]), BASE_MESSAGE_SIZE - size, 0); - if (result < 0) { - break; - } - size += result; - } - - if (result < 0) { - if (errno != 0) { - ESP_LOGW(TAG, "1: %s, %d", strerror(errno), (int)errno); - } - - shutdown(sock, 2); - closesocket(sock); - - break; // stop for(;;) will try to reconnect then - } - - if (result > 0) { - result = gettimeofday(&now, NULL); - // ESP_LOGI(TAG, "time of day: %ld %ld", now.tv_sec, now.tv_usec); - if (result) { - ESP_LOGW(TAG, "Failed to gettimeofday"); - continue; - } - - result = base_message_deserialize(&base_message, base_message_serialized, size); - if (result) { - ESP_LOGW(TAG, "Failed to read base message: %d", result); - continue; - } - - base_message.received.usec = now.tv_usec; - // ESP_LOGI(TAG,"%d %d : %d %d : %d %d",base_message.size, - // base_message.refersTo, - // base_message.sent.sec, - // base_message.sent.usec, - // base_message.received.sec, - // base_message.received.usec ); - - // TODO: ensure this buffer is freed before task gets deleted - size = 0; - char *typedMsg = malloc(sizeof(char) * base_message.size); - if (typedMsg == NULL) { - ESP_LOGE(TAG, "Couldn't get memory for typed message"); - - return; - } - start = typedMsg; - - while (size < base_message.size) { - result = recv(sock, &(start[size]), base_message.size - size, 0); - if (result < 0) { - ESP_LOGW(TAG, "Failed to read from server: %d", result); - - break; - } - - size += result; - } - - if (result < 0) { - if (errno != 0) { - ESP_LOGI(TAG, "2: %s, %d", strerror(errno), (int)errno); - } - - shutdown(sock, 2); - closesocket(sock); - - break; // stop for(;;) will try to reconnect then - } - - switch (base_message.type) { - case SNAPCAST_MESSAGE_CODEC_HEADER: - result = codec_header_message_deserialize(&codec_header_message, - start, size); - if (result) { - ESP_LOGI(TAG, "Failed to read codec header: %d", result); - return; - } - - size = codec_header_message.size; - start = codec_header_message.payload; - - // ESP_LOGI(TAG, "Received codec header message with size %d", - // codec_header_message.size); - - if (strcmp(codec_header_message.codec, "opus") == 0) { - uint32_t rate; - memcpy(&rate, start + 4, sizeof(rate)); - uint16_t bits; - memcpy(&bits, start + 8, sizeof(bits)); - memcpy(&channels, start + 10, sizeof(channels)); - ESP_LOGI(TAG, "%s sampleformat: %d:%d:%d", - codec_header_message.codec, rate, bits, channels); - - int error = 0; - if (opusDecoder != NULL) { - opus_decoder_destroy(opusDecoder); - opusDecoder = NULL; - } - opusDecoder = opus_decoder_create(rate, channels, &error); - if (error != 0) { - ESP_LOGI(TAG, "Failed to init %s decoder", - codec_header_message.codec); - } - ESP_LOGI(TAG, "Initialized %s decoder", - codec_header_message.codec); - - codec = OPUS; - - snapcastSetting.codec = codec; - snapcastSetting.bits = bits; - snapcastSetting.channels = channels; - snapcastSetting.sampleRate = rate; - } else if (strcmp(codec_header_message.codec, "pcm") == 0) { - codec = PCM; - - memcpy(&channels, start + 22, sizeof(channels)); - uint32_t rate; - memcpy(&rate, start + 24, sizeof(rate)); - uint16_t bits; - memcpy(&bits, start + 34, sizeof(bits)); - - ESP_LOGI(TAG, "%s sampleformat: %d:%d:%d", - codec_header_message.codec, rate, bits, channels); - - snapcastSetting.codec = codec; - snapcastSetting.bits = bits; - snapcastSetting.channels = channels; - snapcastSetting.sampleRate = rate; - - } else { - codec = NONE; - - ESP_LOGI(TAG, "Codec : %s not supported", - codec_header_message.codec); - ESP_LOGI(TAG, - "Change encoder codec to opus in /etc/snapserver.conf " - "on server"); - return; - } - - trx.tv_sec = base_message.sent.sec; - trx.tv_usec = base_message.sent.usec; - // we do this, so uint32_t timvals won't overflow - // if e.g. raspberry server is off to far - settimeofday(&trx, NULL); - ESP_LOGI(TAG, "syncing clock to server %ld.%06ld", trx.tv_sec, - trx.tv_usec); - - codec_header_message_free(&codec_header_message); - - received_header = true; - - break; - - case SNAPCAST_MESSAGE_WIRE_CHUNK: { - if (!received_header) { - if (typedMsg != NULL) { - free(typedMsg); - } - - continue; - } - - wire_chunk_message_t wire_chunk_message; - - result = wire_chunk_message_deserialize(&wire_chunk_message, start, - size); - if (result) { - ESP_LOGI(TAG, "Failed to read wire chunk: %d\r\n", result); - - wire_chunk_message_free(&wire_chunk_message); - break; - } - - // ESP_LOGI(TAG, "wire chnk with size: %d, timestamp %d.%d", - // wire_chunk_message.size, - // wire_chunk_message.timestamp.sec, - // wire_chunk_message.timestamp.usec); - - // store chunk's timestamp, decoder callback - // will need it later - tv_t timestamp; - timestamp = wire_chunk_message.timestamp; - - switch (codec) { - case OPUS: { - int frame_size = 0; - - if (audio == NULL) { - #if CONFIG_USE_PSRAM - audio = (int16_t *)heap_caps_malloc( - frameSize * snapcastSetting.channels * (snapcastSetting.bits / 8), - MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); // 960*2: 20ms, 960*1: 10ms - #else - audio = (int16_t *)malloc( - frameSize * snapcastSetting.channels * (snapcastSetting.bits / 8)); // 960*2: 20ms, 960*1: 10ms - #endif - } - - if (audio == NULL) { - ESP_LOGE(TAG, - "Failed to allocate memory for opus audio decoder"); - } else { - size = wire_chunk_message.size; - start = wire_chunk_message.payload; - - while ((frame_size = opus_decode( - opusDecoder, (unsigned char *)start, size, - (opus_int16 *)audio, pcm_size / channels, 0)) == - OPUS_BUFFER_TOO_SMALL) { - pcm_size = pcm_size * 2; - - // 960*2: 20ms, 960*1: 10ms - #if CONFIG_USE_PSRAM - audio = (int16_t *)heap_caps_realloc( - audio, pcm_size * snapcastSetting.channels * (snapcastSetting.bits / 8), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); // 2 channels + 2 Byte per sample == int32_t - #else - audio = (int16_t *)realloc( - audio, pcm_size * snapcastSetting.channels * (snapcastSetting.bits / 8)); -// audio = (int16_t *)heap_caps_realloc( -// (int32_t *)audio, frameSize * CHANNELS * (BITS_PER_SAMPLE / 8), MALLOC_CAP_32BIT); - #endif - - - - ESP_LOGI(TAG, - "OPUS encoding buffer too small, resizing to %d " - "samples per channel", - pcm_size / channels); - } - - if (frame_size < 0) { - ESP_LOGE(TAG, "Decode error : %d, %d, %s, %s, %d\n", - frame_size, size, start, (char *)audio, - pcm_size / channels); - - free(audio); - audio = NULL; - } else { - wire_chunk_message_t pcm_chunk_message; - - pcm_chunk_message.size = frame_size * snapcastSetting.channels * (snapcastSetting.bits / 8); - pcm_chunk_message.payload = audio; - pcm_chunk_message.timestamp = timestamp; - - snapcastSetting.chunkDuration_ms = (1000UL * pcm_chunk_message.size) / (uint32_t)(snapcastSetting.channels * (snapcastSetting.bits / 8)) / snapcastSetting.sampleRate; - if (player_send_snapcast_setting(snapcastSetting) < 0) { - ESP_LOGE(TAG, "Failed to notify sync task about codec. Did you init player?"); - - return; - } - -// if (snapcastSetting.chunkDuration_ms > 30) { -// ESP_LOGE(TAG, "We can't get that big chunks on this platform. RAM is a scarce good!"); -// -// return; -// } - - #if CONFIG_USE_DSP_PROCESSOR - dsp_setup_flow(500, snapcastSetting.sampleRate, snapcastSetting.chunkDuration_ms); - dsp_processor(pcm_chunk_message.payload, - pcm_chunk_message.size, dspFlow); - #endif - - insert_pcm_chunk(&pcm_chunk_message); - } - } - - break; - } - - case PCM: { - wire_chunk_message_t pcm_chunk_message; - - if (audio == NULL) { - #if CONFIG_USE_PSRAM - audio = (int16_t *)heap_caps_malloc( - pcm_chunk_message.size * sizeof(char), - MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); // 960*2: 20ms, 960*1: 10ms - #else - audio = (int16_t *)malloc(pcm_chunk_message.size * sizeof(char)); - #endif - } - - if (audio == NULL) { - ESP_LOGE(TAG, - "Failed to allocate memory for opus audio decoder"); - } else { - size = wire_chunk_message.size; - start = wire_chunk_message.payload; - - pcm_chunk_message.size = size; - pcm_chunk_message.timestamp = timestamp; - pcm_chunk_message.payload = (char *)audio; - // TODO: if wire_chunk_message_free is done - // differently this copy can be avoided - memcpy(pcm_chunk_message.payload, start, - pcm_chunk_message.size); - - snapcastSetting.chunkDuration_ms = (1000UL * pcm_chunk_message.size) / (uint32_t)(snapcastSetting.channels * (snapcastSetting.bits / 8)) / snapcastSetting.sampleRate; - if (player_send_snapcast_setting(snapcastSetting) < 0) { - ESP_LOGE(TAG, "Failed to notify sync task about codec. Did you init player?"); - - return; - } - - #if CONFIG_USE_DSP_PROCESSOR - dsp_setup_flow(500, snapcastSetting.sampleRate, snapcastSetting.chunkDuration_ms); - dsp_processor(pcm_chunk_message.payload, - pcm_chunk_message.size, dspFlow); - #endif - - insert_pcm_chunk(&pcm_chunk_message); - } - - break; - } - - default: { - ESP_LOGE(TAG, "Decoder not supported"); - - return; - - break; - } - } - - wire_chunk_message_free(&wire_chunk_message); - - break; - } - - case SNAPCAST_MESSAGE_SERVER_SETTINGS: - // The first 4 bytes in the buffer are the size of the string. - // We don't need this, so we'll shift the entire buffer over 4 bytes - // and use the extra room to add a null character so cJSON can pares - // it. - memmove(start, start + 4, size - 4); - start[size - 3] = '\0'; - result = server_settings_message_deserialize( - &server_settings_message, start); - if (result) { - ESP_LOGI(TAG, "Failed to read server settings: %d", result); - return; - } - // log mute state, buffer, latency - buffer_ms = server_settings_message.buffer_ms; - ESP_LOGI(TAG, "Buffer length: %d", - server_settings_message.buffer_ms); - clientDacLatency = server_settings_message.latency; - ESP_LOGI(TAG, "Latency: %d", - server_settings_message.latency); - ESP_LOGI(TAG, "Mute: %d", server_settings_message.muted); - ESP_LOGI(TAG, "Setting volume: %d", server_settings_message.volume); - muteCH[0] = server_settings_message.muted; - muteCH[1] = server_settings_message.muted; - muteCH[2] = server_settings_message.muted; - muteCH[3] = server_settings_message.muted; - - snapcastSetting.buffer_ms = server_settings_message.buffer_ms; - snapcastSetting.muted = server_settings_message.muted; - snapcastSetting.volume = server_settings_message.volume; - - // Volume setting using ADF HAL abstraction - audio_hal_set_mute(board_handle->audio_hal, - server_settings_message.muted); - audio_hal_set_volume(board_handle->audio_hal, - server_settings_message.volume); - - //if (player_notify_buffer_ms(buffer_ms) < 0) { - if (player_send_snapcast_setting(snapcastSetting) < 0) { - ESP_LOGE(TAG, "Failed to notify sync task. Did you init player?"); - - return; - } - - break; - - case SNAPCAST_MESSAGE_TIME: - result = time_message_deserialize(&time_message, start, size); - if (result) { - ESP_LOGI(TAG, "Failed to deserialize time message"); - - return; - } - -// ESP_LOGI(TAG, "BaseTX : %d %d ", -// base_message.sent.sec , -// base_message.sent.usec); ESP_LOGI(TAG, "BaseRX -// : %d %d ", base_message.received.sec , -// base_message.received.usec); ESP_LOGI(TAG, -// "baseTX->RX : %d s ", -// (base_message.received.sec - -// base_message.sent.sec)); ESP_LOGI(TAG, -// "baseTX->RX : %d ms ", -// (base_message.received.usec - -// base_message.sent.usec)/1000); ESP_LOGI(TAG, -// "Latency : %d.%d ", time_message.latency.sec, -// time_message.latency.usec/1000); - - // tv == server to client latency (s2c) - // time_message.latency == client to server latency(c2s) - // TODO the fact that I have to do this simple conversion means - // I should probably use the timeval struct instead of my own - trx.tv_sec = base_message.received.sec; - trx.tv_usec = base_message.received.usec; - ttx.tv_sec = base_message.sent.sec; - ttx.tv_usec = base_message.sent.usec; - timersub(&trx, &ttx, &tdif); - - trx.tv_sec = time_message.latency.sec; - trx.tv_usec = time_message.latency.usec; - - // trx == c2s: client to server - // tdif == s2c: server to client - // ESP_LOGI(TAG, "c2s: %ld %ld", trx.tv_sec, - // trx.tv_usec); ESP_LOGI(TAG, "s2c: %ld %ld", - // tdif.tv_sec, tdif.tv_usec); - - timersub(&trx, &tdif, &tmpDiffToServer); - if ((tmpDiffToServer.tv_sec / 2) == 0) { - tmpDiffToServer.tv_sec = 0; - tmpDiffToServer.tv_usec = - (suseconds_t)((int64_t)tmpDiffToServer.tv_sec * 1000000LL / - 2) + - (int64_t)tmpDiffToServer.tv_usec / 2; - } else { - tmpDiffToServer.tv_sec /= 2; - tmpDiffToServer.tv_usec /= 2; - } - - // ESP_LOGI(TAG, "Current - // latency: %ld.%06ld", tmpDiffToServer.tv_sec, - // tmpDiffToServer.tv_usec); - - // TODO: Move the time message sending to an own thread maybe - // following code is storing / initializing / resetting diff to - // server algorithm we collect a number of latencies and apply a - // median filter. Based on these we can get server now + // Find snapcast server + // Connect to first snapcast server found + r = NULL; + err = 0; + while (!r || err) + { + ESP_LOGI (TAG, "Lookup snapcast service on network"); + esp_err_t err = mdns_query_ptr ("_snapcast", "_tcp", 3000, 20, &r); + if (err) { - struct timeval diff; - int64_t newValue; - - // clear diffBuffer if last update is older than a minute - timersub(&now, &lastTimeSync, &diff); - - if (diff.tv_sec > 60) { - ESP_LOGW( - TAG, - "Last time sync older than a minute. Clearing time buffer"); - - reset_latency_buffer(); - } - - newValue = ((int64_t)tmpDiffToServer.tv_sec * 1000000LL + - (int64_t)tmpDiffToServer.tv_usec); - player_latency_insert(newValue); - -// ESP_LOGE(TAG, "latency %lld", newValue); - - // store current time - lastTimeSync.tv_sec = now.tv_sec; - lastTimeSync.tv_usec = now.tv_usec; - - // we don't care if it was already taken, just make sure it is - // taken at this point - xSemaphoreTake(timeSyncSemaphoreHandle, 0); - - uint64_t timeout; - if (latency_buffer_full() > 0) { - // we give timeSyncSemaphoreHandle after x µs through timer - // TODO: maybe start a periodic timer here, but we need to remember if it is already running then. - // also we need to stop it if reset_latency_buffer() was called - timeout = 1000000; - } else { - // Do a initial time sync with the server at boot - // we need to fill diffBuff fast so we get a good estimate of - // latency - timeout = 50000; - } - - esp_timer_start_once(timeSyncMessageTimer, timeout); + ESP_LOGE (TAG, "Query Failed"); } - break; + if (!r) + { + ESP_LOGW (TAG, "No results found!"); + } + + vTaskDelay (1000 / portTICK_PERIOD_MS); } - if (typedMsg != NULL) { - free(typedMsg); - } - } + char serverAddr[] = "255.255.255.255"; + ESP_LOGI (TAG, "Found %s:%d", + inet_ntop (AF_INET, &(r->addr->addr.u_addr.ip4.addr), + serverAddr, sizeof (serverAddr)), + r->port); - if (received_header == true) { - if (xSemaphoreTake(timeSyncSemaphoreHandle, 0) == pdTRUE) { - result = gettimeofday(&now, NULL); - // ESP_LOGI(TAG, "time of day: %ld %ld", now.tv_sec, now.tv_usec); - if (result) { - ESP_LOGI(TAG, "Failed to gettimeofday"); - continue; - } - - base_message.type = SNAPCAST_MESSAGE_TIME; - base_message.id = id_counter++; - base_message.refersTo = 0; - base_message.received.sec = 0; - base_message.received.usec = 0; - base_message.sent.sec = now.tv_sec; - base_message.sent.usec = now.tv_usec; - base_message.size = TIME_MESSAGE_SIZE; - - result = base_message_serialize( - &base_message, base_message_serialized, BASE_MESSAGE_SIZE); - if (result) { - ESP_LOGE(TAG, "Failed to serialize base message for time\r\n"); - continue; - } - - memset(&time_message, 0, sizeof(time_message)); - - result = - time_message_serialize(&time_message, time_message_serialized, TIME_MESSAGE_SIZE); - if (result) { - ESP_LOGI(TAG, "Failed to serialize time message\r\b"); - continue; - } - - result = send(sock, base_message_serialized, BASE_MESSAGE_SIZE, 0); - if (result < 0) { - ESP_LOGW(TAG, "error writing timesync base msg to socket: %s", - strerror(errno)); - - shutdown(sock, 2); - closesocket(sock); - - break; // stop for(;;) will try to reconnect then - } - - result = send(sock, time_message_serialized, TIME_MESSAGE_SIZE, 0); - if (result < 0) { - ESP_LOGW(TAG, "error writing timesync msg to socket: %s", - strerror(errno)); - - shutdown(sock, 2); - closesocket(sock); - - break; // stop for(;;) will try to reconnect then - } - - // ESP_LOGI(TAG, "sent time sync message %ld.%06ld", now.tv_sec, - // now.tv_usec); + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = r->addr->addr.u_addr.ip4.addr; + servaddr.sin_port = htons (r->port); + mdns_query_results_free (r); +#else + // configure a failsafe snapserver according to CONFIG values + servaddr.sin_family = AF_INET; + inet_pton (AF_INET, SNAPCAST_SERVER_HOST, &(servaddr.sin_addr.s_addr)); + servaddr.sin_port = htons (SNAPCAST_SERVER_PORT); +#endif + ESP_LOGI (TAG, "allocate socket"); + sock = socket (AF_INET, SOCK_STREAM, 0); + if (sock < 0) + { + ESP_LOGE (TAG, "... Failed to allocate socket."); + vTaskDelay (1000 / portTICK_PERIOD_MS); + continue; + } + ESP_LOGI (TAG, "... allocated socket %d", sock); + + ESP_LOGI (TAG, "connect to socket"); + err = connect (sock, (struct sockaddr *)&servaddr, + sizeof (struct sockaddr_in)); + if (err < 0) + { + ESP_LOGE (TAG, "%s, %d", strerror (errno), errno); + + shutdown (sock, 2); + closesocket (sock); + + vTaskDelay (4000 / portTICK_PERIOD_MS); + + continue; + } + + ESP_LOGI (TAG, "... connected"); + + result = gettimeofday (&now, NULL); + if (result) + { + ESP_LOGI (TAG, "Failed to gettimeofday\r\n"); + return; + } + + received_header = false; + + // init base message + base_message.type = SNAPCAST_MESSAGE_HELLO; + base_message.id = 0x0000; + base_message.refersTo = 0x0000; + base_message.sent.sec = now.tv_sec; + base_message.sent.usec = now.tv_usec; + base_message.received.sec = 0; + base_message.received.usec = 0; + base_message.size = 0x00000000; + + // init hello message + hello_message.mac = mac_address; + hello_message.hostname = "ESP32-Caster"; + hello_message.version = (char *)VERSION_STRING; + hello_message.client_name = "libsnapcast"; + hello_message.os = "esp32"; + hello_message.arch = "xtensa"; + hello_message.instance = 1; + hello_message.id = mac_address; + hello_message.protocol_version = 2; + + if (hello_message_serialized == NULL) + { + hello_message_serialized = hello_message_serialize ( + &hello_message, (size_t *)&(base_message.size)); + if (!hello_message_serialized) + { + ESP_LOGE (TAG, "Failed to serialize hello message\r\b"); + return; + } + } + + result = base_message_serialize (&base_message, base_message_serialized, + BASE_MESSAGE_SIZE); + if (result) + { + ESP_LOGE (TAG, "Failed to serialize base message\r\n"); + return; + } + + result = send (sock, base_message_serialized, BASE_MESSAGE_SIZE, 0); + if (result < 0) + { + ESP_LOGW (TAG, "error writing base msg to socket: %s", + strerror (errno)); + + free (hello_message_serialized); + hello_message_serialized = NULL; + + shutdown (sock, 2); + closesocket (sock); + + continue; + } + + result = send (sock, hello_message_serialized, base_message.size, 0); + if (result < 0) + { + ESP_LOGW (TAG, "error writing hello msg to socket: %s", + strerror (errno)); + + free (hello_message_serialized); + hello_message_serialized = NULL; + + shutdown (sock, 2); + closesocket (sock); + + continue; + } + + free (hello_message_serialized); + hello_message_serialized = NULL; + + // init default setting + snapcastSetting.buffer_ms = 0; + snapcastSetting.codec = NONE; + snapcastSetting.bits = 0; + snapcastSetting.channels = 0; + snapcastSetting.sampleRate = 0; + snapcastSetting.chunkDuration_ms = 0; + snapcastSetting.volume = 0; + snapcastSetting.muted = false; + + for (;;) + { + size = 0; + result = 0; + while (size < BASE_MESSAGE_SIZE) + { + result = recv (sock, &(base_message_serialized[size]), + BASE_MESSAGE_SIZE - size, 0); + if (result < 0) + { + break; + } + size += result; + } + + if (result < 0) + { + if (errno != 0) + { + ESP_LOGW (TAG, "1: %s, %d", strerror (errno), (int)errno); + } + + shutdown (sock, 2); + closesocket (sock); + + break; // stop for(;;) will try to reconnect then + } + + if (result > 0) + { + result = gettimeofday (&now, NULL); + // ESP_LOGI(TAG, "time of day: %ld %ld", now.tv_sec, + // now.tv_usec); + if (result) + { + ESP_LOGW (TAG, "Failed to gettimeofday"); + continue; + } + + result = base_message_deserialize ( + &base_message, base_message_serialized, size); + if (result) + { + ESP_LOGW (TAG, "Failed to read base message: %d", result); + continue; + } + + base_message.received.usec = now.tv_usec; + // ESP_LOGI(TAG,"%d %d : %d %d : %d %d",base_message.size, + // base_message.refersTo, + // base_message.sent.sec, + // base_message.sent.usec, + // base_message.received.sec, + // base_message.received.usec + //); + + // TODO: ensure this buffer is freed before task gets deleted + size = 0; + char *typedMsg = malloc (sizeof (char) * base_message.size); + if (typedMsg == NULL) + { + ESP_LOGE (TAG, "Couldn't get memory for typed message"); + + return; + } + start = typedMsg; + + while (size < base_message.size) + { + result = recv (sock, &(start[size]), + base_message.size - size, 0); + if (result < 0) + { + ESP_LOGW (TAG, "Failed to read from server: %d", result); + + break; + } + + size += result; + } + + if (result < 0) + { + if (errno != 0) + { + ESP_LOGI (TAG, "2: %s, %d", strerror (errno), + (int)errno); + } + + shutdown (sock, 2); + closesocket (sock); + + break; // stop for(;;) will try to reconnect then + } + + switch (base_message.type) + { + case SNAPCAST_MESSAGE_CODEC_HEADER: + result = codec_header_message_deserialize ( + &codec_header_message, start, size); + if (result) + { + ESP_LOGI (TAG, "Failed to read codec header: %d", + result); + return; + } + + size = codec_header_message.size; + start = codec_header_message.payload; + + // ESP_LOGI(TAG, "Received codec header message with size + // %d", codec_header_message.size); + + if (strcmp (codec_header_message.codec, "opus") == 0) + { + uint32_t rate; + memcpy (&rate, start + 4, sizeof (rate)); + uint16_t bits; + memcpy (&bits, start + 8, sizeof (bits)); + memcpy (&channels, start + 10, sizeof (channels)); + ESP_LOGI (TAG, "%s sampleformat: %d:%d:%d", + codec_header_message.codec, rate, bits, + channels); + + int error = 0; + if (opusDecoder != NULL) + { + opus_decoder_destroy (opusDecoder); + opusDecoder = NULL; + } + opusDecoder + = opus_decoder_create (rate, channels, &error); + if (error != 0) + { + ESP_LOGI (TAG, "Failed to init %s decoder", + codec_header_message.codec); + } + ESP_LOGI (TAG, "Initialized %s decoder", + codec_header_message.codec); + + codec = OPUS; + + snapcastSetting.codec = codec; + snapcastSetting.bits = bits; + snapcastSetting.channels = channels; + snapcastSetting.sampleRate = rate; + } + else if (strcmp (codec_header_message.codec, "pcm") == 0) + { + codec = PCM; + + memcpy (&channels, start + 22, sizeof (channels)); + uint32_t rate; + memcpy (&rate, start + 24, sizeof (rate)); + uint16_t bits; + memcpy (&bits, start + 34, sizeof (bits)); + + ESP_LOGI (TAG, "%s sampleformat: %d:%d:%d", + codec_header_message.codec, rate, bits, + channels); + + snapcastSetting.codec = codec; + snapcastSetting.bits = bits; + snapcastSetting.channels = channels; + snapcastSetting.sampleRate = rate; + } + else + { + codec = NONE; + + ESP_LOGI (TAG, "Codec : %s not supported", + codec_header_message.codec); + ESP_LOGI (TAG, "Change encoder codec to opus in " + "/etc/snapserver.conf " + "on server"); + return; + } + + trx.tv_sec = base_message.sent.sec; + trx.tv_usec = base_message.sent.usec; + // we do this, so uint32_t timvals won't overflow + // if e.g. raspberry server is off to far + settimeofday (&trx, NULL); + ESP_LOGI (TAG, "syncing clock to server %ld.%06ld", + trx.tv_sec, trx.tv_usec); + + codec_header_message_free (&codec_header_message); + + received_header = true; + + break; + + case SNAPCAST_MESSAGE_WIRE_CHUNK: + { + if (!received_header) + { + if (typedMsg != NULL) + { + free (typedMsg); + } + + continue; + } + + wire_chunk_message_t wire_chunk_message; + + result = wire_chunk_message_deserialize ( + &wire_chunk_message, start, size); + if (result) + { + ESP_LOGI (TAG, "Failed to read wire chunk: %d\r\n", + result); + + wire_chunk_message_free (&wire_chunk_message); + break; + } + + // ESP_LOGI(TAG, "wire chnk with size: %d, timestamp + // %d.%d", wire_chunk_message.size, + // wire_chunk_message.timestamp.sec, + // wire_chunk_message.timestamp.usec); + + // store chunk's timestamp, decoder callback + // will need it later + tv_t timestamp; + timestamp = wire_chunk_message.timestamp; + + switch (codec) + { + case OPUS: + { + int frame_size = 0; + + if (audio == NULL) + { +#if CONFIG_USE_PSRAM + audio = (int16_t *)heap_caps_malloc ( + frameSize * snapcastSetting.channels + * (snapcastSetting.bits / 8), + MALLOC_CAP_8BIT + | MALLOC_CAP_SPIRAM); // 960*2: 20ms, + // 960*1: 10ms +#else + audio = (int16_t *)malloc ( + frameSize * snapcastSetting.channels + * (snapcastSetting.bits + / 8)); // 960*2: 20ms, 960*1: 10ms +#endif + } + + if (audio == NULL) + { + ESP_LOGE (TAG, "Failed to allocate memory for " + "opus audio decoder"); + } + else + { + size = wire_chunk_message.size; + start = wire_chunk_message.payload; + + while ((frame_size = opus_decode ( + opusDecoder, (unsigned char *)start, + size, (opus_int16 *)audio, + pcm_size / channels, 0)) + == OPUS_BUFFER_TOO_SMALL) + { + pcm_size = pcm_size * 2; + + // 960*2: 20ms, 960*1: 10ms +#if CONFIG_USE_PSRAM + audio = (int16_t *)heap_caps_realloc ( + audio, + pcm_size * snapcastSetting.channels + * (snapcastSetting.bits / 8), + MALLOC_CAP_8BIT + | MALLOC_CAP_SPIRAM); // 2 channels + + // 2 Byte per + // sample == + // int32_t +#else + audio = (int16_t *)realloc ( + audio, pcm_size + * snapcastSetting.channels + * (snapcastSetting.bits / 8)); + // audio = (int16_t + // *)heap_caps_realloc( + // (int32_t + // *)audio, frameSize * + // CHANNELS * + // (BITS_PER_SAMPLE / 8), + // MALLOC_CAP_32BIT); +#endif + + ESP_LOGI (TAG, + "OPUS encoding buffer too small, " + "resizing to %d " + "samples per channel", + pcm_size / channels); + } + + if (frame_size < 0) + { + ESP_LOGE ( + TAG, + "Decode error : %d, %d, %s, %s, %d\n", + frame_size, size, start, (char *)audio, + pcm_size / channels); + + free (audio); + audio = NULL; + } + else + { + wire_chunk_message_t pcm_chunk_message; + + pcm_chunk_message.size + = frame_size * snapcastSetting.channels + * (snapcastSetting.bits / 8); + pcm_chunk_message.payload = (char *)audio; + pcm_chunk_message.timestamp = timestamp; + + snapcastSetting.chunkDuration_ms + = (1000UL * pcm_chunk_message.size) + / (uint32_t) ( + snapcastSetting.channels + * (snapcastSetting.bits / 8)) + / snapcastSetting.sampleRate; + if (player_send_snapcast_setting ( + snapcastSetting) + < 0) + { + ESP_LOGE ( + TAG, + "Failed to notify sync task about " + "codec. Did you init player?"); + + return; + } + + // if + //(snapcastSetting.chunkDuration_ms > 30) { + // ESP_LOGE(TAG, + //"We can't get that big chunks on this + // platform. RAM + // is a scarce good!"); + // + // return; + // } + +#if CONFIG_USE_DSP_PROCESSOR + dsp_setup_flow ( + 500, snapcastSetting.sampleRate, + snapcastSetting.chunkDuration_ms); + dsp_processor (pcm_chunk_message.payload, + pcm_chunk_message.size, + dspFlow); +#endif + + insert_pcm_chunk (&pcm_chunk_message); + } + } + + break; + } + + case PCM: + { + wire_chunk_message_t pcm_chunk_message; + + if (audio == NULL) + { +#if CONFIG_USE_PSRAM + audio = (int16_t *)heap_caps_malloc ( + pcm_chunk_message.size * sizeof (char), + MALLOC_CAP_8BIT + | MALLOC_CAP_SPIRAM); // 960*2: 20ms, + // 960*1: 10ms +#else + audio = (int16_t *)malloc (pcm_chunk_message.size + * sizeof (char)); +#endif + } + + if (audio == NULL) + { + ESP_LOGE (TAG, "Failed to allocate memory for " + "opus audio decoder"); + } + else + { + size = wire_chunk_message.size; + start = wire_chunk_message.payload; + + pcm_chunk_message.size = size; + pcm_chunk_message.timestamp = timestamp; + pcm_chunk_message.payload = (char *)audio; + // TODO: if wire_chunk_message_free is done + // differently this copy can be avoided + memcpy (pcm_chunk_message.payload, start, + pcm_chunk_message.size); + + snapcastSetting.chunkDuration_ms + = (1000UL * pcm_chunk_message.size) + / (uint32_t) (snapcastSetting.channels + * (snapcastSetting.bits / 8)) + / snapcastSetting.sampleRate; + if (player_send_snapcast_setting ( + snapcastSetting) + < 0) + { + ESP_LOGE (TAG, + "Failed to notify sync task about " + "codec. Did you init player?"); + + return; + } + +#if CONFIG_USE_DSP_PROCESSOR + dsp_setup_flow ( + 500, snapcastSetting.sampleRate, + snapcastSetting.chunkDuration_ms); + dsp_processor (pcm_chunk_message.payload, + pcm_chunk_message.size, dspFlow); +#endif + + insert_pcm_chunk (&pcm_chunk_message); + } + + break; + } + + default: + { + ESP_LOGE (TAG, "Decoder not supported"); + + return; + + break; + } + } + + wire_chunk_message_free (&wire_chunk_message); + + break; + } + + case SNAPCAST_MESSAGE_SERVER_SETTINGS: + // The first 4 bytes in the buffer are the size of the + // string. We don't need this, so we'll shift the entire + // buffer over 4 bytes and use the extra room to add a null + // character so cJSON can pares it. + memmove (start, start + 4, size - 4); + start[size - 3] = '\0'; + result = server_settings_message_deserialize ( + &server_settings_message, start); + if (result) + { + ESP_LOGI (TAG, "Failed to read server settings: %d", + result); + return; + } + // log mute state, buffer, latency + ESP_LOGI (TAG, "Buffer length: %d", + server_settings_message.buffer_ms); + ESP_LOGI (TAG, "Latency: %d", + server_settings_message.latency); + ESP_LOGI (TAG, "Mute: %d", + server_settings_message.muted); + ESP_LOGI (TAG, "Setting volume: %d", + server_settings_message.volume); + + // Volume setting using ADF HAL abstraction + if (snapcastSetting.muted != server_settings_message.muted) + { + audio_hal_set_mute (board_handle->audio_hal, + server_settings_message.muted); + } + if (snapcastSetting.volume != server_settings_message.volume) + { + audio_hal_set_mute (board_handle->audio_hal, + server_settings_message.volume); + } + + snapcastSetting.clientDacLatency_ms + = server_settings_message.latency; + snapcastSetting.buffer_ms + = server_settings_message.buffer_ms; + snapcastSetting.muted = server_settings_message.muted; + snapcastSetting.volume = server_settings_message.volume; + + if (player_send_snapcast_setting (snapcastSetting) < 0) + { + ESP_LOGE ( + TAG, + "Failed to notify sync task. Did you init player?"); + + return; + } + + break; + + case SNAPCAST_MESSAGE_TIME: + result + = time_message_deserialize (&time_message, start, size); + if (result) + { + ESP_LOGI (TAG, "Failed to deserialize time message"); + + return; + } + + // ESP_LOGI(TAG, "BaseTX : %d %d ", + // base_message.sent.sec , + // base_message.sent.usec); ESP_LOGI(TAG, + //"BaseRX : %d %d ", + // base_message.received.sec , + // base_message.received.usec); ESP_LOGI(TAG, + // "baseTX->RX : %d s ", + // (base_message.received.sec + // - + // base_message.sent.sec)); ESP_LOGI(TAG, + // "baseTX->RX : %d ms ", + // (base_message.received.usec - + // base_message.sent.usec)/1000); + // ESP_LOGI(TAG, "Latency : %d.%d ", + // time_message.latency.sec, + // time_message.latency.usec/1000); + + // tv == server to client latency (s2c) + // time_message.latency == client to server latency(c2s) + // TODO the fact that I have to do this simple conversion + // means I should probably use the timeval struct instead of + // my own + trx.tv_sec = base_message.received.sec; + trx.tv_usec = base_message.received.usec; + ttx.tv_sec = base_message.sent.sec; + ttx.tv_usec = base_message.sent.usec; + timersub (&trx, &ttx, &tdif); + + trx.tv_sec = time_message.latency.sec; + trx.tv_usec = time_message.latency.usec; + + // trx == c2s: client to server + // tdif == s2c: server to client + // ESP_LOGI(TAG, "c2s: %ld %ld", + // trx.tv_sec, trx.tv_usec); ESP_LOGI(TAG, + // "s2c: %ld %ld", tdif.tv_sec, + // tdif.tv_usec); + + timersub (&trx, &tdif, &tmpDiffToServer); + if ((tmpDiffToServer.tv_sec / 2) == 0) + { + tmpDiffToServer.tv_sec = 0; + tmpDiffToServer.tv_usec + = (suseconds_t) ((int64_t)tmpDiffToServer.tv_sec + * 1000000LL / 2) + + (int64_t)tmpDiffToServer.tv_usec / 2; + } + else + { + tmpDiffToServer.tv_sec /= 2; + tmpDiffToServer.tv_usec /= 2; + } + + // ESP_LOGI(TAG, + //"Current + // latency: %ld.%06ld", tmpDiffToServer.tv_sec, + // tmpDiffToServer.tv_usec); + + // TODO: Move the time message sending to an own thread maybe + // following code is storing / initializing / resetting diff + // to server algorithm we collect a number of latencies and + // apply a median filter. Based on these we can get server + // now + { + struct timeval diff; + int64_t newValue; + + // clear diffBuffer if last update is older than a minute + timersub (&now, &lastTimeSync, &diff); + + if (diff.tv_sec > 60) + { + ESP_LOGW (TAG, "Last time sync older than a minute. " + "Clearing time buffer"); + + reset_latency_buffer (); + } + + newValue = ((int64_t)tmpDiffToServer.tv_sec * 1000000LL + + (int64_t)tmpDiffToServer.tv_usec); + player_latency_insert (newValue); + + // ESP_LOGE(TAG, "latency %lld", newValue); + + // store current time + lastTimeSync.tv_sec = now.tv_sec; + lastTimeSync.tv_usec = now.tv_usec; + + // we don't care if it was already taken, just make sure it + // is taken at this point + xSemaphoreTake (timeSyncSemaphoreHandle, 0); + + uint64_t timeout; + if (latency_buffer_full () > 0) + { + // we give timeSyncSemaphoreHandle after x µs through + // timer + // TODO: maybe start a periodic timer here, but we need + // to remember if it is already running then. also we + // need to stop it if reset_latency_buffer() was called + timeout = 1000000; + } + else + { + // Do a initial time sync with the server at boot + // we need to fill diffBuff fast so we get a good + // estimate of latency + timeout = 50000; + } + + esp_timer_start_once (timeSyncMessageTimer, timeout); + } + + break; + } + + if (typedMsg != NULL) + { + free (typedMsg); + } + } + + if (received_header == true) + { + if (xSemaphoreTake (timeSyncSemaphoreHandle, 0) == pdTRUE) + { + result = gettimeofday (&now, NULL); + // ESP_LOGI(TAG, "time of day: %ld %ld", now.tv_sec, + // now.tv_usec); + if (result) + { + ESP_LOGI (TAG, "Failed to gettimeofday"); + continue; + } + + base_message.type = SNAPCAST_MESSAGE_TIME; + base_message.id = id_counter++; + base_message.refersTo = 0; + base_message.received.sec = 0; + base_message.received.usec = 0; + base_message.sent.sec = now.tv_sec; + base_message.sent.usec = now.tv_usec; + base_message.size = TIME_MESSAGE_SIZE; + + result = base_message_serialize (&base_message, + base_message_serialized, + BASE_MESSAGE_SIZE); + if (result) + { + ESP_LOGE ( + TAG, + "Failed to serialize base message for time\r\n"); + continue; + } + + memset (&time_message, 0, sizeof (time_message)); + + result = time_message_serialize (&time_message, + time_message_serialized, + TIME_MESSAGE_SIZE); + if (result) + { + ESP_LOGI (TAG, "Failed to serialize time message\r\b"); + continue; + } + + result = send (sock, base_message_serialized, + BASE_MESSAGE_SIZE, 0); + if (result < 0) + { + ESP_LOGW ( + TAG, "error writing timesync base msg to socket: %s", + strerror (errno)); + + shutdown (sock, 2); + closesocket (sock); + + break; // stop for(;;) will try to reconnect then + } + + result = send (sock, time_message_serialized, + TIME_MESSAGE_SIZE, 0); + if (result < 0) + { + ESP_LOGW (TAG, + "error writing timesync msg to socket: %s", + strerror (errno)); + + shutdown (sock, 2); + closesocket (sock); + + break; // stop for(;;) will try to reconnect then + } + + // ESP_LOGI(TAG, "sent time sync message %ld.%06ld", + // now.tv_sec, now.tv_usec); + } + } } - } } - } } -void app_main(void) { - esp_err_t ret = nvs_flash_init(); - if (ret == ESP_ERR_NVS_NO_FREE_PAGES || - ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { - ESP_ERROR_CHECK(nvs_flash_erase()); - ret = nvs_flash_init(); - } - ESP_ERROR_CHECK(ret); +void +app_main (void) +{ + esp_err_t ret = nvs_flash_init (); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) + { + ESP_ERROR_CHECK (nvs_flash_erase ()); + ret = nvs_flash_init (); + } + ESP_ERROR_CHECK (ret); - esp_log_level_set("*", ESP_LOG_INFO); -// esp_log_level_set("c_I2S", ESP_LOG_NONE); // - esp_log_level_set("HEADPHONE", ESP_LOG_NONE); // if enabled these cause a timer srv stack overflow - esp_log_level_set("gpio", ESP_LOG_NONE); // + esp_log_level_set ("*", ESP_LOG_INFO); + // esp_log_level_set("c_I2S", ESP_LOG_NONE); // + esp_log_level_set ( + "HEADPHONE", + ESP_LOG_NONE); // if enabled these cause a timer srv stack overflow + esp_log_level_set ("gpio", ESP_LOG_NONE); // + esp_timer_init (); - esp_timer_init(); + ESP_LOGI (TAG, "Start codec chip"); + board_handle = audio_board_init (); + ESP_LOGI (TAG, "Audio board_init done"); - ESP_LOGI(TAG, "Start codec chip"); - board_handle = audio_board_init(); - ESP_LOGI(TAG, "Audio board_init done"); - - audio_hal_ctrl_codec(board_handle->audio_hal, AUDIO_HAL_CODEC_MODE_BOTH, - AUDIO_HAL_CTRL_START); - i2s_mclk_gpio_select(0, 0); + audio_hal_ctrl_codec (board_handle->audio_hal, AUDIO_HAL_CODEC_MODE_BOTH, + AUDIO_HAL_CTRL_START); + i2s_mclk_gpio_select (0, 0); // setup_ma120(); - #if CONFIG_USE_DSP_PROCESSOR - dsp_setup_flow(500, 44100, 20); // init with default value - #endif +#if CONFIG_USE_DSP_PROCESSOR + dsp_setup_flow (500, 44100, 20); // init with default value +#endif - ESP_LOGI(TAG, "init player"); - playerChunkQueueHandle = init_player(); - if (playerChunkQueueHandle == NULL) { - return; - } + ESP_LOGI (TAG, "init player"); + playerChunkQueueHandle = init_player (); + if (playerChunkQueueHandle == NULL) + { + return; + } // Enable and setup WIFI in station mode and connect to Access point setup in // menu config or set up provisioning mode settable in menuconfig - wifi_init(); + wifi_init (); // Enable websocket server - ESP_LOGI(TAG, "Connected to AP"); -// ESP_LOGI(TAG, "Setup ws server"); -// websocket_if_start(); + ESP_LOGI (TAG, "Connected to AP"); + // ESP_LOGI(TAG, "Setup ws server"); + // websocket_if_start(); - net_mdns_register("snapclient"); + net_mdns_register ("snapclient"); #ifdef CONFIG_SNAPCLIENT_SNTP_ENABLE - set_time_from_sntp(); + set_time_from_sntp (); #endif - xTaskCreatePinnedToCore(&ota_server_task, "ota_server_task", 4096, NULL, - OTA_TASK_PRIORITY, t_ota_task, OTA_TASK_CORE_ID); + xTaskCreatePinnedToCore (&ota_server_task, "ota_server_task", 4096, NULL, + OTA_TASK_PRIORITY, t_ota_task, OTA_TASK_CORE_ID); - xTaskCreatePinnedToCore(&http_get_task, "http_get_task", 4 * 4096, NULL, - HTTP_TASK_PRIORITY, &t_http_get_task, - HTTP_TASK_CORE_ID); - while (1) { - // audio_event_iface_msg_t msg; - vTaskDelay(1000 / portTICK_PERIOD_MS); + xTaskCreatePinnedToCore (&http_get_task, "http_get_task", 4 * 4096, NULL, + HTTP_TASK_PRIORITY, &t_http_get_task, + HTTP_TASK_CORE_ID); + while (1) + { + // audio_event_iface_msg_t msg; + vTaskDelay (1000 / portTICK_PERIOD_MS); - // ma120_read_error(0x20); + // ma120_read_error(0x20); - esp_err_t ret = 0; // audio_event_iface_listen(evt, &msg, portMAX_DELAY); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "[ * ] Event interface error : %d", ret); - continue; + esp_err_t ret = 0; // audio_event_iface_listen(evt, &msg, portMAX_DELAY); + if (ret != ESP_OK) + { + ESP_LOGE (TAG, "[ * ] Event interface error : %d", ret); + continue; + } } - } }