#include "audio_prompt.h" #include "audio_prompts_data.h" #include "audio_output.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "dac_njw1195.h" static const char *TAG = "AudioPrompt"; void audio_prompt_init(void) { ESP_LOGI(TAG, "Audio prompt module initialized"); } void audio_prompt_play(prompt_id_t id) { const int16_t *prompt_data = NULL; size_t prompt_len = 0; switch (id) { case PROMPT_BT_CONNECTED: prompt_data = prompt_bt_connected; prompt_len = prompt_bt_connected_len; break; case PROMPT_BT_DISCONNECTED: prompt_data = prompt_bt_disconnected; prompt_len = prompt_bt_disconnected_len; break; case PROMPT_LOW_BATTERY: prompt_data = prompt_low_battery; prompt_len = prompt_low_battery_len; break; default: return; } if (prompt_data == NULL || prompt_len == 0) { return; } ESP_LOGI(TAG, "Playing prompt tone ID: %d", id); // Lock audio output so we don't interleave with BT/AirPlay audio_output_lock(); // Switch DAC to prompt volume dac_njw1195_set_prompt_mode(true); // Prompts are generated at 44100Hz 16-bit Mono. // audio_output_write expects 16-bit Stereo. We duplicate channels. // We send in chunks to avoid large memory allocations. const size_t CHUNK_SAMPLES = 256; int16_t stereo_buf[CHUNK_SAMPLES * 2]; for (size_t i = 0; i < prompt_len; i += CHUNK_SAMPLES) { size_t samples_to_process = prompt_len - i; if (samples_to_process > CHUNK_SAMPLES) { samples_to_process = CHUNK_SAMPLES; } for (size_t j = 0; j < samples_to_process; j++) { int16_t sample = prompt_data[i + j]; stereo_buf[j * 2] = sample; // Left stereo_buf[j * 2 + 1] = sample; // Right } // Write stereo PCM data. audio_output_write internally calls i2s_channel_write. audio_output_write(stereo_buf, samples_to_process * 4, portMAX_DELAY); } // Write silence to flush the DMA buffers. // I2S_DMA_DESC_NUM(8) * I2S_DMA_FRAME_NUM(352) = 2816 frames. // Writing roughly 3000 frames of silence ensures the prompt is pushed out. memset(stereo_buf, 0, sizeof(stereo_buf)); for (int i = 0; i < 12; i++) { // 12 * 256 = 3072 frames audio_output_write(stereo_buf, CHUNK_SAMPLES * 4, portMAX_DELAY); } // Restore original volume dac_njw1195_set_prompt_mode(false); audio_output_unlock(); }