#include #include "audio.h" #include #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/queue.h" #include "esp_log.h" #include "bsp/esp-bsp.h" /* Buffer for reading/writing to I2S driver. Same length as SPIFFS buffer and I2S buffer, for optimal read/write performance. External SPI Flash -> SPIFFS buffer -> App buffer (RAM) -> I2S buffer (DMA) -> I2S peripheral. */ #define BUFFER_SIZE (1024) #define DEFAULT_VOLUME (50) /* Globals */ static const char *TAG = "audio"; TaskHandle_t audio_task_handle; // Very simple WAV header, ignores most fields typedef struct __attribute__((packed)) { uint8_t ignore_0[22]; uint16_t num_channels; uint32_t sample_rate; uint8_t ignore_1[6]; uint16_t bits_per_sample; uint8_t ignore_2[4]; uint32_t data_size; uint8_t data[]; } dumb_wav_header_t; typedef struct { FILE *f; uint32_t data_offset; uint32_t data_size; } wav_ctx_t; #define AUDIO_CHUNK_SIZE 4096 bool wav_open(wav_ctx_t *ctx, const char *path) { ctx->f = fopen(path, "rb"); if (!ctx->f) return false; /* Skip RIFF header */ fseek(ctx->f, 12, SEEK_SET); uint32_t chunk_id, chunk_size; while (fread(&chunk_id, 4, 1, ctx->f)) { fread(&chunk_size, 4, 1, ctx->f); if (chunk_id == 0x61746164) { // "data" ctx->data_offset = ftell(ctx->f); ctx->data_size = chunk_size; return true; } fseek(ctx->f, chunk_size, SEEK_CUR); } fclose(ctx->f); return false; } void rewind_wav(wav_ctx_t *ctx) { fseek(ctx->f, ctx->data_offset, SEEK_SET); } #define AUDIO_CHUNK_SIZE 4096 static void audio_task(void *arg) { ESP_LOGI(TAG,"Audio task"); 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_set_out_mute(spk_codec_dev, false); // 🔑 CRITIQUE const char *play_filename = "/littlefs/sounds/mixkit-clear-announce-tones-2861.wav"; uint8_t *wav_bytes = malloc(BUFFER_SIZE); assert(wav_bytes != NULL); for (;;) { /* ⏸️ Attend PLAY */ ulTaskNotifyTake(pdTRUE, portMAX_DELAY); bool stop_requested = false; while (!stop_requested) { ESP_LOGI(TAG,"On joue !"); FILE *play_file = fopen(play_filename, "rb"); if (!play_file) { ESP_LOGE(TAG,"File not found"); break; } dumb_wav_header_t wav_header; if (fread(&wav_header, 1, sizeof(wav_header), play_file) != sizeof(wav_header)) { ESP_LOGE(TAG,"Header read failed"); fclose(play_file); break; } 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_ERROR_CHECK(esp_codec_dev_open(spk_codec_dev, &fs)); uint32_t bytes_sent = 0; while (bytes_sent < wav_header.data_size) { size_t bytes_read = fread(wav_bytes, 1, BUFFER_SIZE, play_file); if (bytes_read == 0) break; esp_codec_dev_write(spk_codec_dev, wav_bytes, bytes_read); bytes_sent += bytes_read; vTaskDelay(1); // watchdog + scheduler } esp_codec_dev_close(spk_codec_dev); fclose(play_file); ESP_LOGI(TAG,"Fin du fichier"); if (ulTaskNotifyTake(pdTRUE, 0) > 0) { stop_requested = true; } } } free(wav_bytes); } static void audio_task__(void *arg) { ESP_LOGE(TAG,"Audio task"); 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); /* Pointer to a file that is going to be played */ const char music_filename[] = "/littlefs/sounds/mixkit-clear-announce-tones-2861.wav"; const char *play_filename = music_filename; int16_t *wav_bytes = malloc(BUFFER_SIZE); assert(wav_bytes != NULL); bool stop_requested = false; while (!stop_requested){ ESP_LOGE(TAG,"On joue !"); /* Open WAV file */ ESP_LOGI(TAG, "Playing file %s", play_filename); FILE *play_file = fopen(play_filename, "rb"); if (play_file == NULL) { ESP_LOGW(TAG, "%s file does not exist!", play_filename); break; } /* Read WAV header file */ dumb_wav_header_t wav_header; if (fread((void *)&wav_header, 1, sizeof(wav_header), play_file) != sizeof(wav_header)) { ESP_LOGW(TAG, "Error in reading file"); break; } //ESP_LOGI(TAG, "Number of channels: %" PRIu16 "", wav_header.num_channels); //ESP_LOGI(TAG, "Bits per sample: %" PRIu16 "", wav_header.bits_per_sample); //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 and send it to codec */ size_t bytes_read_from_spiffs = fread(wav_bytes, 1, BUFFER_SIZE, play_file); esp_codec_dev_write(spk_codec_dev, wav_bytes, bytes_read_from_spiffs); bytes_send_to_i2s += bytes_read_from_spiffs; /* ⭐ LIGNE CRITIQUE ⭐ */ vTaskDelay(1); // laisse respirer IDLE + watchdog } ESP_LOGE(TAG,"On a fini le fichier"); fclose(play_file); esp_codec_dev_close(spk_codec_dev); if(ulTaskNotifyTake(pdTRUE, 0)>0){ stop_requested=true; break; }; } free(wav_bytes); vTaskSuspend(NULL); } void playSound(void) { if(audio_task_handle==NULL){ BaseType_t ret = xTaskCreate(audio_task, "audio_task", 4096, NULL, 6, &audio_task_handle); assert(ret == pdPASS); } xTaskNotifyGive(audio_task_handle); } void stopSound(void){ if (audio_task_handle) { xTaskNotifyGive(audio_task_handle); } }