ESP-IDF Firmware
Firmware architecture and call graph
Loading...
Searching...
No Matches
applications/lyrat_board_app/main/audio_amp_main.c
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: CC0-1.0
5 */
6
7#include <stdio.h>
8#include <inttypes.h>
9#include <sdkconfig.h>
10#include "freertos/FreeRTOS.h"
11#include "freertos/task.h"
12#include "freertos/queue.h"
13#include "esp_log.h"
14#include "esp_dsp.h"
15#include "bsp/esp-bsp.h"
16#include <stdint.h>
17#include <math.h>
18
19// Buffer for reading/writing to I2S driver. Same length as SPIFFS buffer and I2S buffer, for optimal read/write performance.
20// Recording audio data path:
21// I2S peripheral -> I2S buffer (DMA) -> App buffer (RAM) -> SPIFFS buffer -> External SPI Flash.
22// Vice versa for playback.
23#define BUFFER_SIZE (64)
24#define SAMPLE_RATE (16000) // For recording
25#define DEFAULT_VOLUME (100)
26
27// Globals
28static const char *TAG = "example";
29static QueueHandle_t audio_button_q = NULL;
30
31static void btn_handler(void *button_handle, void *usr_data)
32{
33 int button_pressed = (int)usr_data;
34 xQueueSend(audio_button_q, &button_pressed, 0);
35}
36
37// Very simple WAV header, ignores most fields
38typedef struct __attribute__((packed))
39{
40 uint8_t ignore_0[22];
41 uint16_t num_channels;
42 uint32_t sample_rate;
43 uint8_t ignore_1[6];
44 uint16_t bits_per_sample;
45 uint8_t ignore_2[4];
46 uint32_t data_size;
47 uint8_t data[];
49
55
56static esp_codec_dev_handle_t spk_codec_dev = NULL;
57static FILE *play_file = NULL;
58// Pointer to a file that is going to be played
59static const char play_filename[] = BSP_SPIFFS_MOUNT_POINT"/16bit_mono_44_1_khz.wav";
60
61// Definition for all tasks
62static void buttons_process_task(void *arg);
63static void audio_read_task(void *arg);
64static void audio_process_task(void *arg);
65
66
67// Wave file header
70
71// Data for IIR filters
73float iir_w_lpf[5] = {0, 0};
74
76float iir_w_hpf[5] = {0, 0};
77
78// IIR filters parameters
79// Parameters for low-pass filter (LPF)
80float lpf_gain = 0;
81float lpf_qFactor = 0.5;
82float lpf_freq = 0.01;
83
84// Parameters for high-pass filter (HPF)
85float hpf_gain = 0;
86float hpf_qFactor = 1;
87float hpf_freq = 0.15;
88
89// Volume control definitions
90float full_volume = 1;
91// Volume in dB
93// Digital limiter envelope value
94float full_envelope = 0;
95
96// processing audio buffer
98
99// The triple_audio_buffer contains tree data arrays sizeof BUFFER_SIZE, for writing audio data to the codec
101// The write index shows the audio buffer that will be used to write data to the codec
103// The read index shows the audio buffer that will be used to fill data from the data source to the processing buffer
105
106// Semaphore to synchronize the read and write
107static SemaphoreHandle_t sync_read_task;
108
109// Convert input int16_t Q15 values array to the float values array
110static void convert_short2float(int16_t *int16_data, float *float_data, int len)
111{
112 float multiplier = 1.0 / (float)(INT16_MAX + 1);
113 for (int i = 0 ; i < len ; i++) {
114 float_data[i] = (float)int16_data[i] * multiplier;
115 }
116}
117
118// Convert input float values to the int16_t Q15 values array
119static void convert_float2short(float *float_data, int16_t *int16_data, int len)
120{
121 float multiplier = (float)(INT16_MAX + 1);
122 for (int i = 0 ; i < len ; i++) {
123 int16_data[i] = (int16_t)((float)multiplier * (float)float_data[i]);
124 }
125}
126
127// In the audio processing the valid output audio values for the floating point format should be in range +/- 1.0,
128// because these values will be converted to the 0x8000 and 0x7fff int16_t values, and will be accepted by the codec.
129// With additional amplification, for example bass, treble, and for other processing, it is possible that the audio
130// signal will reach the maximum values and will make an overflow at the DAC.
131// To avoid this situation the digital limiter analyze the output values and control the full amplification gain.
132//
133void digitalLimiter(float *input_signal, float *output_signal, int signal_length, float threshold, float attack_value, float release_value, float *in_envelope)
134{
135 float envelope = *in_envelope;
136 for (int i = 0; i < signal_length; i++) {
137 // Calculate envelope
138 float abs_input = fabsf(input_signal[i]);
139 if (abs_input > envelope) {
140 envelope = envelope * (1 - attack_value) + attack_value * abs_input;
141 } else {
142 envelope = envelope * (1 - release_value) + release_value * abs_input;
143 }
144
145 // Apply compression
146 if (envelope > threshold) {
147 output_signal[i] = input_signal[i] * (threshold / envelope);
148 } else {
149 output_signal[i] = input_signal[i];
150 }
151 }
152 *in_envelope = envelope;
153}
154
155static void audio_process_task(void *arg)
156{
157 // Init codeac and apply the initial volume to maximum
158 spk_codec_dev = bsp_audio_codec_speaker_init();
159 assert(spk_codec_dev);
160 esp_codec_dev_set_out_vol(spk_codec_dev, DEFAULT_VOLUME);
161
162 // Open file and het the WAV header to set up the sample frequency, amount of channels and resolution
163 play_file = fopen(play_filename, "rb");
164 if (play_file == NULL) {
165 ESP_LOGW(TAG, "%s file does not exist!", play_filename);
166 }
167 // Read WAV header
168 if (fread((void *)&wav_header, 1, sizeof(wav_header), play_file) != sizeof(wav_header)) {
169 ESP_LOGW(TAG, "Error in reading file");
170 return;
171 }
172
173 ESP_LOGI(TAG, "Number of channels: %" PRIu16 "", wav_header.num_channels);
174 ESP_LOGI(TAG, "Bits per sample: %" PRIu16 "", wav_header.bits_per_sample);
175 ESP_LOGI(TAG, "Sample rate: %" PRIu32 "", wav_header.sample_rate);
176 ESP_LOGI(TAG, "Data size: %" PRIu32 "", wav_header.data_size);
177 esp_codec_dev_sample_info_t fs = {
178 .sample_rate = wav_header.sample_rate,
179 .channel = wav_header.num_channels,
180 .bits_per_sample = wav_header.bits_per_sample,
181 };
182 if (spk_codec_dev != NULL) {
183 int result = esp_codec_dev_open(spk_codec_dev, &fs);
184 }
185 // Calculate initial volume value
186 full_volume = exp10f((float)full_volume_db / 20);
187 // Calculate initial state for LPF
189 // Calculate initial state for HPF
191
192 BaseType_t ret = xTaskCreate(buttons_process_task, "buttons_process_task", 4096, NULL, 4, NULL);
193 assert(ret == pdPASS);
194
195 sync_read_task = xSemaphoreCreateCounting(1, 0);
196
197 ret = xTaskCreate(audio_read_task, "audio_read_task", 4096, NULL, 7, NULL);
198 assert(ret == pdPASS);
199 vTaskDelay(1);
200
201 ESP_LOGW(TAG, "To select volume/bass/treble please use the 'Set' button. And adjust the value with +/- buttons.");
202
203 for (;;) {
204 /* Get data from SPIFFS and send it to codec */
206 // Write samples to audio codec
207 esp_codec_dev_write(spk_codec_dev, wav_bytes, BUFFER_SIZE * sizeof(int16_t));
209 if (audio_buffer_write_index >= 3) {
211 }
212 // Check the triple buffer overflow
214 // Call delay to switch the task to fill the buffer
215 vTaskDelay(1);
216 // Check and indicate overflow status
218 ESP_LOGW(TAG, "Audio buffer overflow!");
219 }
220 }
221 // Generate synt event to read task:
222 xSemaphoreGive(sync_read_task);
223 }
224}
225
226// The audio_read_task is responsible to read data from the file and fill it to the audio buffer.
227// The audio buffer is places inside of triple buffer, to avoid overflow situation in case if
228// the processing task busy for a while.
229static void audio_read_task(void *arg)
230{
231 while (1) {
232 // Wait the sync semaphore
233 if (xSemaphoreTake(sync_read_task, 100)) {
234 // Get the pointer to the current audio buffer
236 // Read the data from the file
237 uint32_t bytes_read_from_spiffs = fread(wav_buffer, sizeof(int16_t), BUFFER_SIZE, play_file);
238 // Convert input samples from int16 to float for processing
240 // Apply bass
242 // Apply treble
244 // Apply voluve
246 // Apply limiter
248 // Convert from float to int16 for audio codec
250
251 if (bytes_read_from_spiffs != BUFFER_SIZE) {
252 // Rewind the file and read the WAV header
253 rewind(play_file);
254 fread((void *)&wav_header, 1, sizeof(wav_header), play_file);
255 // Read data to the audio buffer
256 bytes_read_from_spiffs = fread(wav_buffer, sizeof(int16_t), BUFFER_SIZE, play_file);
257 }
259 if (audio_buffer_read_index >= 3) {
261 }
262 } else {
263 // Error in case of timeout
264 ESP_LOGE(TAG, "Audio timeout!");
265 }
266 }
267}
268
269static void buttons_process_task(void *arg)
270{
271 while (1) {
272 int btn_index = 0;
273 if (xQueueReceive(audio_button_q, &btn_index, portMAX_DELAY) == pdTRUE) {
274 switch (btn_index) {
275 case BSP_BUTTON_SET: {
276 current_set += 1;
277 if (current_set > 2) {
279 }
280 switch (current_set) {
281 case AUDIO_VOLUME:
282 ESP_LOGW(TAG, "Select volume");
283 break;
284 case AUDIO_BASS:
285 ESP_LOGW(TAG, "Select bass");
286 break;
287 case AUDIO_TREBLE:
288 ESP_LOGW(TAG, "Select treble");
289 break;
290 default:
291 break;
292 }
293 break;
294 }
295 case BSP_BUTTON_VOLDOWN: {
296 switch (current_set) {
297 case AUDIO_VOLUME:
298 full_volume_db -= 3;
299 if (full_volume_db < -36) {
300 full_volume_db = -36;
301 }
302 full_volume = exp10f((float)full_volume_db / 20);
303 ESP_LOGI(TAG, "Volume Down: %i dB", full_volume_db);
304 break;
305 case AUDIO_BASS:
306 lpf_gain -= 1;
307 if (lpf_gain < -12) {
308 lpf_gain = -12;
309 }
310 ESP_LOGI(TAG, "Bass Down: %i", (int)lpf_gain);
312 break;
313 case AUDIO_TREBLE:
314 hpf_gain -= 1;
315 if (hpf_gain < -12) {
316 hpf_gain = -12;
317 }
318 ESP_LOGI(TAG, "Treble Down: %i", (int)hpf_gain);
320 break;
321 default:
322 break;
323 }
324 break;
325 }
326 case BSP_BUTTON_VOLUP: {
327 switch (current_set) {
328 case AUDIO_VOLUME:
329 full_volume_db += 3;
330 if (full_volume_db > 0) {
331 full_volume_db = 0;
332 }
333 full_volume = exp10f((float)full_volume_db / 20);
334 ESP_LOGI(TAG, "Volume Up: %i dB", full_volume_db);
335 break;
336 case AUDIO_BASS:
337 lpf_gain += 1;
338 if (lpf_gain > 12) {
339 lpf_gain = 12;
340 }
341 ESP_LOGI(TAG, "Bass Up: %i", (int)lpf_gain);
343 break;
344 case AUDIO_TREBLE:
345 hpf_gain += 1;
346 if (hpf_gain > 12) {
347 hpf_gain = 12;
348 }
349 ESP_LOGI(TAG, "Treble Up: %i", (int)hpf_gain);
351 break;
352 default:
353 break;
354 }
355 break;
356 }
357 default:
358 ESP_LOGI(TAG, "No function for this button");
359 break;
360 }
361 }
362 }
363}
364
365void app_main(void)
366{
367 ESP_ERROR_CHECK(bsp_spiffs_mount());
368
369 // Create FreeRTOS tasks and queues
370 audio_button_q = xQueueCreate(10, sizeof(int));
371 assert (audio_button_q != NULL);
372
373 BaseType_t ret = xTaskCreate(audio_process_task, "audio_process_task", 4096, NULL, 6, NULL);
374 assert(ret == pdPASS);
375
376 // Init audio buttons
377 button_handle_t btns[BSP_BUTTON_NUM];
378 ESP_ERROR_CHECK(bsp_iot_button_create(btns, NULL, BSP_BUTTON_NUM));
379 for (int i = 0; i < BSP_BUTTON_NUM; i++) {
380 ESP_ERROR_CHECK(iot_button_register_cb(btns[i], BUTTON_PRESS_DOWN, btn_handler, (void *) i));
381 }
382}
void digitalLimiter(float *input_signal, float *output_signal, int signal_length, float threshold, float attack_value, float release_value, float *in_envelope)
static SemaphoreHandle_t sync_read_task
static void audio_read_task(void *arg)
static QueueHandle_t audio_button_q
static void btn_handler(void *button_handle, void *usr_data)
static const char play_filename[]
static void buttons_process_task(void *arg)
enum audio_set audio_set_t
static dumb_wav_header_t wav_header
static void audio_process_task(void *arg)
static esp_codec_dev_handle_t spk_codec_dev
static void convert_float2short(float *float_data, int16_t *int16_data, int len)
static void convert_short2float(int16_t *int16_data, float *float_data, int len)
#define dsps_biquad_f32
Definition dsps_biquad.h:99
esp_err_t dsps_biquad_gen_lowShelf_f32(float *coeffs, float f, float gain, float qFactor)
low shelf IIR filter coefficients
esp_err_t dsps_biquad_gen_highShelf_f32(float *coeffs, float f, float gain, float qFactor)
high shelf IIR filter coefficients
esp_err_t dsps_mulc_f32_ansi(const float *input, float *output, int len, float C, int step_in, int step_out)
multiply constant
static const char * TAG
Definition main/main.c:31
struct __attribute__((packed))
Definition main/main.c:65
static float data[128 *2]
Definition test_fft2r.c:34