From b9d247c4368e53befe09c01e00f1f5aa58b362a7 Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Wed, 21 Jun 2023 16:01:47 +0200 Subject: [PATCH] examples: Update audio example for esp_codec_dev and SPIFFS API --- esp32_s3_korvo_2/idf_component.yml | 2 +- examples/audio/main/bsp_audio_example.c | 183 +++++++++--------------- examples/audio/partitions.csv | 2 +- examples/bsp_ext.py | 6 +- 4 files changed, 73 insertions(+), 120 deletions(-) diff --git a/esp32_s3_korvo_2/idf_component.yml b/esp32_s3_korvo_2/idf_component.yml index 6e3d1231..339bebaf 100644 --- a/esp32_s3_korvo_2/idf_component.yml +++ b/esp32_s3_korvo_2/idf_component.yml @@ -23,7 +23,7 @@ dependencies: public: true button: - version: "^2.4" + version: "^2.5" public: true esp_lvgl_port: diff --git a/examples/audio/main/bsp_audio_example.c b/examples/audio/main/bsp_audio_example.c index a0a5eb6d..f3b63f5e 100644 --- a/examples/audio/main/bsp_audio_example.c +++ b/examples/audio/main/bsp_audio_example.c @@ -1,49 +1,37 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 */ #include -#include -#include -#include -#include #include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" #include "esp_log.h" -#include "esp_spiffs.h" - #include "bsp/esp-bsp.h" -#include "es8311.h" -#include "es7210.h" -#include "lvgl.h" /* Buffer for reading/writing to I2S driver. Same length as SPIFFS buffer and I2S buffer, for optimal read/write performance. Recording audio data path: I2S peripheral -> I2S buffer (DMA) -> App buffer (RAM) -> SPIFFS buffer -> External SPI Flash. Vice versa for playback. */ #define BUFFER_SIZE (1024) -#define SAMPLE_RATE (22050) +#define SAMPLE_RATE (16000) // For recording #define DEFAULT_VOLUME (50) /* The recording will be RECORDING_LENGTH * BUFFER_SIZE long (in bytes) - With sampling frequency 22050 Hz and 16bit mono resolution it equals to ~3.715 seconds */ + With sampling frequency 16000 Hz and 16bit mono resolution it equals to ~5.12 seconds */ #define RECORDING_LENGTH (160) /* Globals */ static const char *TAG = "example"; -static button_handle_t audio_button[BSP_BUTTON_NUM - 1] = {}; static QueueHandle_t audio_button_q = NULL; -static i2s_chan_handle_t i2s_tx_chan; -static i2s_chan_handle_t i2s_rx_chan; -static void btn_handler(void *arg, void *arg2) +static void btn_handler(void *button_handle, void *usr_data) { - for (uint8_t i = 0; i < (BSP_BUTTON_NUM - 1); i++) { - if ((button_handle_t)arg == audio_button[i]) { - xQueueSend(audio_button_q, &i, 0); - break; - } - } + int button_pressed = (int)usr_data; + xQueueSend(audio_button_q, &button_pressed, 0); } // Very simple WAV header, ignores most fields @@ -59,79 +47,28 @@ typedef struct __attribute__((packed)) uint8_t data[]; } dumb_wav_header_t; -static esp_err_t spiffs_init(void) -{ - esp_err_t ret = ESP_OK; - ESP_LOGI(TAG, "Initializing SPIFFS"); - - const esp_vfs_spiffs_conf_t conf = { - .base_path = "/spiffs", - .partition_label = NULL, - .max_files = 5, - .format_if_mount_failed = false - }; - - /*!< Use settings defined above to initialize and mount SPIFFS filesystem. */ - /*!< Note: esp_vfs_spiffs_register is an all-in-one convenience function. */ - ret = esp_vfs_spiffs_register(&conf); - - if (ret != ESP_OK) { - if (ret == ESP_FAIL) { - ESP_LOGE(TAG, "Failed to mount or format filesystem"); - } else if (ret == ESP_ERR_NOT_FOUND) { - ESP_LOGE(TAG, "Failed to find SPIFFS partition"); - } else { - ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret)); - } - - return ret; - } - - size_t total = 0, used = 0; - ret = esp_spiffs_info(NULL, &total, &used); - - if (ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret)); - } else { - ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); - } - - return ret; -} - static void audio_task(void *arg) { - /* Create and configure ES8311 I2C driver */ - es8311_handle_t es8311_dev = es8311_create(BSP_I2C_NUM, ES8311_ADDRRES_0); - const es8311_clock_config_t clk_cfg = BSP_ES8311_SCLK_CONFIG(SAMPLE_RATE); - es8311_init(es8311_dev, &clk_cfg, ES8311_RESOLUTION_16, ES8311_RESOLUTION_16); - es8311_voice_volume_set(es8311_dev, DEFAULT_VOLUME, NULL); - - /* Microphone settings */ - es7210_dev_handle_t es7210_handle = NULL; - const es7210_i2c_config_t es7210_i2c = BSP_ES7210_I2C_CONFIG(BSP_I2C_NUM); - ESP_ERROR_CHECK(es7210_new_codec(&es7210_i2c, &es7210_handle)); - const es7210_codec_config_t codec_conf = BSP_ES7210_CONFIG(22050, I2S_MCLK_MULTIPLE_512); - ESP_ERROR_CHECK(es7210_config_codec(es7210_handle, &codec_conf)); - ESP_ERROR_CHECK(es7210_config_volume(es7210_handle, 32)); - - /* Configure I2S peripheral and Power Amplifier */ - bsp_audio_init(NULL, &i2s_tx_chan, &i2s_rx_chan); - bsp_audio_poweramp_enable(true); + esp_codec_dev_handle_t spk_codec_dev = bsp_audio_codec_speaker_init(); + assert(spk_codec_dev); + esp_codec_dev_set_out_vol(spk_codec_dev, DEFAULT_VOLUME); + esp_codec_dev_handle_t mic_codec_dev = bsp_audio_codec_microphone_init(); /* Pointer to a file that is going to be played */ - const char music_filename[] = "/spiffs/16bit_mono_22_05khz.wav"; - const char recording_filename[] = "/spiffs/recording.wav"; + const char music_filename[] = BSP_SPIFFS_MOUNT_POINT"/16bit_mono_22_05khz.wav"; + const char recording_filename[] = BSP_SPIFFS_MOUNT_POINT"/recording.wav"; const char *play_filename = music_filename; while (1) { - uint8_t btn_index = 0; + int btn_index = 0; if (xQueueReceive(audio_button_q, &btn_index, portMAX_DELAY) == pdTRUE) { - btn_index++; /* The first button is not handled here */ switch (btn_index) { case BSP_BUTTON_REC: { - /* Writing to SPIFFS from internal RAM is ~15% faster than from external SPI RAM */ - int16_t *recording_buffer = heap_caps_malloc(BUFFER_SIZE, MALLOC_CAP_INTERNAL); + if (mic_codec_dev == NULL) { + ESP_LOGW(TAG, "This board does not support microphone recording!"); + break; + } + int16_t *recording_buffer = heap_caps_malloc(BUFFER_SIZE, MALLOC_CAP_DEFAULT); assert(recording_buffer != NULL); /* Open file for recording */ @@ -146,24 +83,32 @@ static void audio_task(void *arg) .sample_rate = SAMPLE_RATE }; if (fwrite((void *)&recording_header, 1, sizeof(dumb_wav_header_t), record_file) != sizeof(dumb_wav_header_t)) { - ESP_LOGW(TAG, "Error in writting to file"); + ESP_LOGW(TAG, "Error in writing to file"); continue; } - ESP_LOGI(TAG, "Recording start"); + setvbuf(record_file, NULL, _IOFBF, BUFFER_SIZE); + esp_codec_dev_sample_info_t fs = { + .sample_rate = SAMPLE_RATE, + .channel = 1, + .bits_per_sample = 16, + }; + esp_codec_dev_set_in_gain(mic_codec_dev, 42.0); + esp_codec_dev_open(mic_codec_dev, &fs); + + ESP_LOGI(TAG, "Recording start"); size_t bytes_written_to_spiffs = 0; while (bytes_written_to_spiffs < RECORDING_LENGTH * BUFFER_SIZE) { - size_t bytes_received_from_i2s; - ESP_ERROR_CHECK(i2s_channel_read(i2s_rx_chan, recording_buffer, BUFFER_SIZE, &bytes_received_from_i2s, pdMS_TO_TICKS(5000))); - - /* Write WAV file data */ - size_t data_written = fwrite(recording_buffer, 1, bytes_received_from_i2s, record_file); + /* Read data from codec and write it to SPIFFS */ + ESP_ERROR_CHECK(esp_codec_dev_read(mic_codec_dev, recording_buffer, BUFFER_SIZE)); + size_t data_written = fwrite(recording_buffer, 1, BUFFER_SIZE, record_file); bytes_written_to_spiffs += data_written; } ESP_LOGI(TAG, "Recording stop, length: %i bytes", bytes_written_to_spiffs); fclose(record_file); free(recording_buffer); + esp_codec_dev_close(mic_codec_dev); break; } case BSP_BUTTON_SET: { @@ -198,38 +143,44 @@ static void audio_task(void *arg) ESP_LOGI(TAG, "Sample rate: %" PRIu32 "", wav_header.sample_rate); ESP_LOGI(TAG, "Data size: %" PRIu32 "", wav_header.data_size); + esp_codec_dev_sample_info_t fs = { + .sample_rate = wav_header.sample_rate, + .channel = wav_header.num_channels, + .bits_per_sample = wav_header.bits_per_sample, + }; + esp_codec_dev_open(spk_codec_dev, &fs); + uint32_t bytes_send_to_i2s = 0; while (bytes_send_to_i2s < wav_header.data_size) { - /* Get data from SPIFFS */ + /* Get data from SPIFFS and send it to codec */ size_t bytes_read_from_spiffs = fread(wav_bytes, 1, BUFFER_SIZE, play_file); - - /* Send it to I2S */ - size_t i2s_bytes_written; - ESP_ERROR_CHECK(i2s_channel_write(i2s_tx_chan, wav_bytes, bytes_read_from_spiffs, &i2s_bytes_written, pdMS_TO_TICKS(500))); - bytes_send_to_i2s += i2s_bytes_written; + esp_codec_dev_write(spk_codec_dev, wav_bytes, bytes_read_from_spiffs); + bytes_send_to_i2s += bytes_read_from_spiffs; } fclose(play_file); free(wav_bytes); + esp_codec_dev_close(spk_codec_dev); break; } case BSP_BUTTON_VOLDOWN: { - int vol, vol_real; - es8311_voice_volume_get(es8311_dev, &vol); - vol -= 5; - es8311_voice_volume_set(es8311_dev, vol, &vol_real); - ESP_LOGI(TAG, "Volume Down: %i", vol_real); + int vol; + esp_codec_dev_get_out_vol(spk_codec_dev, &vol); + vol = (vol - 5 < 0) ? 0 : vol - 5; + esp_codec_dev_set_out_vol(spk_codec_dev, vol); + ESP_LOGI(TAG, "Volume Down: %i", vol); break; } case BSP_BUTTON_VOLUP: { - int vol, vol_real; - es8311_voice_volume_get(es8311_dev, &vol); - vol += 5; - es8311_voice_volume_set(es8311_dev, vol, &vol_real); - ESP_LOGI(TAG, "Volume Up: %i", vol_real); + int vol; + esp_codec_dev_get_out_vol(spk_codec_dev, &vol); + vol = (vol + 5 > 100) ? 100 : vol + 5; + esp_codec_dev_set_out_vol(spk_codec_dev, vol); + ESP_LOGI(TAG, "Volume Up: %i", vol); break; } default: - ESP_LOGW(TAG, "Button index out of range"); + ESP_LOGI(TAG, "No function for this button"); + break; } } } @@ -237,21 +188,19 @@ static void audio_task(void *arg) void app_main(void) { - /* Init board peripherals */ - bsp_i2c_init(); // Used by ES8311 driver - ESP_ERROR_CHECK(spiffs_init()); + ESP_ERROR_CHECK(bsp_spiffs_mount()); /* Create FreeRTOS tasks and queues */ - audio_button_q = xQueueCreate(10, sizeof(uint8_t)); + audio_button_q = xQueueCreate(10, sizeof(int)); assert (audio_button_q != NULL); BaseType_t ret = xTaskCreate(audio_task, "audio_task", 4096, NULL, 6, NULL); assert(ret == pdPASS); /* Init audio buttons */ - for (int i = 0; i < (BSP_BUTTON_NUM - 1); i++) { - audio_button[i] = iot_button_create(&bsp_button_config[i]); - assert(audio_button[i] != NULL); - ESP_ERROR_CHECK(iot_button_register_cb(audio_button[i], BUTTON_PRESS_DOWN, btn_handler, NULL)); + button_handle_t btns[BSP_BUTTON_NUM]; + ESP_ERROR_CHECK(bsp_iot_button_create(btns, NULL, BSP_BUTTON_NUM)); + for (int i = 0; i < BSP_BUTTON_NUM; i++) { + ESP_ERROR_CHECK(iot_button_register_cb(btns[i], BUTTON_PRESS_DOWN, btn_handler, (void *) i)); } } diff --git a/examples/audio/partitions.csv b/examples/audio/partitions.csv index b895d62e..89821582 100644 --- a/examples/audio/partitions.csv +++ b/examples/audio/partitions.csv @@ -3,4 +3,4 @@ nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 1M, -storage, data, spiffs, 0x110000,0x2f0000, +storage, data, spiffs, , 0x2f0000, diff --git a/examples/bsp_ext.py b/examples/bsp_ext.py index fbb4cd87..c9571de4 100644 --- a/examples/bsp_ext.py +++ b/examples/bsp_ext.py @@ -3,12 +3,16 @@ from pathlib import Path from click.core import Context from typing import List -from idf_py_actions.tools import PropertyDict, red_print def action_extensions(base_actions, project_path=os.getcwd()): """ Describes extension for Board Support Packages. """ + try: + from idf_py_actions.tools import PropertyDict, red_print + except ImportError: + print('Unsupported IDF version!') + return {} try: import ruamel.yaml except ImportError: