Skip to content

Commit

Permalink
examples: Update audio example for esp_codec_dev and SPIFFS API
Browse files Browse the repository at this point in the history
  • Loading branch information
tore-espressif committed Jun 22, 2023
1 parent 873aab2 commit b9d247c
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 120 deletions.
2 changes: 1 addition & 1 deletion esp32_s3_korvo_2/idf_component.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ dependencies:
public: true

button:
version: "^2.4"
version: "^2.5"
public: true

esp_lvgl_port:
Expand Down
183 changes: 66 additions & 117 deletions examples/audio/main/bsp_audio_example.c
Original file line number Diff line number Diff line change
@@ -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 <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include <inttypes.h>
#include <sdkconfig.h>
#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
Expand All @@ -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 */
Expand All @@ -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: {
Expand Down Expand Up @@ -198,60 +143,64 @@ 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;
}
}
}
}

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));
}
}
2 changes: 1 addition & 1 deletion examples/audio/partitions.csv
Original file line number Diff line number Diff line change
Expand Up @@ -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,
6 changes: 5 additions & 1 deletion examples/bsp_ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit b9d247c

Please sign in to comment.