diff --git a/main/drivers/speaker.cpp b/main/drivers/speaker.cpp index 312394a..bceae1d 100644 --- a/main/drivers/speaker.cpp +++ b/main/drivers/speaker.cpp @@ -1,8 +1,103 @@ #include "speaker.h" +static const char *TAG = "speaker"; + +typedef struct { + /// The path to the file being played. + char* file_name; + /// The handle to the file being played. + FILE* file_handle; + /// A number that all samples will be shifted right by. + uint8_t prescaler; + /// A flag for repeating. + bool repeat; +} playing_audio_clip_t; + i2s_chan_handle_t tx_chan; -static const char *TAG = "speaker"; +/// A queue of `audio_clip_t`s to be played once there is nothing being played +QueueHandle_t sequential_clip_queue; + +/// The clips that are currently playing +std::vector playing_clips; + +static void speaker_task(void* arg) { + audio_clip_t next_clip; + playing_audio_clip_t playing_next_clip; + + int16_t* audio_buf = (int16_t*) malloc(AUDIO_BLOCK_SIZE * sizeof(int16_t)); + int16_t* file_buf = (int16_t*) malloc(AUDIO_BLOCK_SIZE * sizeof(int16_t)); + + // wait for a clip request. + while (!xQueueReceive(sequential_clip_queue, &next_clip, portMAX_DELAY)); + ESP_LOGI(TAG, "opening %s", next_clip.file_name); + FILE* fh = fopen(next_clip.file_name, "rb"); + if (fh != NULL) { + ESP_LOGI(TAG, "fh was not null!"); + // skip the .wav header + fseek(fh, 44, SEEK_SET); + + playing_next_clip = { + .file_name = next_clip.file_name, + .file_handle = fh, + .prescaler = next_clip.prescaler, + .repeat = next_clip.repeat, + }; + playing_clips.push_back(playing_next_clip); + } else { + ESP_LOGI(TAG, "null :("); + } + + i2s_channel_enable(tx_chan); + while (1) { + std::memset(audio_buf, 0, AUDIO_BLOCK_SIZE * sizeof(int16_t)); + + ESP_LOGI(TAG, "size: %d", playing_clips.size()); + for (int clip_idx = playing_clips.size()-1; clip_idx >= 0; clip_idx--) { + ESP_LOGI(TAG, "i: %d", clip_idx); + playing_audio_clip_t& clip = playing_clips.at(clip_idx); + ESP_LOGI(TAG, "r %d", (size_t) clip.file_handle); + // ESP_LOGI(TAG, "reading from %s", clip.file_name); + size_t samples_read = fread(file_buf, sizeof(uint16_t), AUDIO_BLOCK_SIZE, clip.file_handle); + ESP_LOGI(TAG, "read! %d", samples_read); + if (samples_read == 0) { + if (clip.repeat) { + ESP_LOGI(TAG, "repeating..."); + fseek(fh, 44, SEEK_SET); + } else { + ESP_LOGI(TAG, "deleting..."); + fclose(clip.file_handle); + free(clip.file_name); + playing_clips.erase(playing_clips.begin() + clip_idx); + } + continue; + } + + for (int i = 0; i < samples_read; i++) { + audio_buf[i] += (file_buf[i] >> clip.prescaler); + } + } + + size_t bytes_written; + i2s_channel_write(tx_chan, audio_buf, AUDIO_BLOCK_SIZE * sizeof(int16_t), &bytes_written, portMAX_DELAY); + } + + + + // xQueuePeek(sequential_clip_queue, &next_clip, 0); + + // QueueHandle_t stop_music = xQueueCreate(2, sizeof (uint8_t)); + // if (stop_music == 0) { + // ESP_LOGW(TAG, "Could not create stop_music queue!"); + // vTaskDelete(NULL); + // } + + // while (playing_music) { + // play_raw_stop_queue(MOUNT_POINT "/tetris.pcm", stop_music); + // } + + vTaskDelete(NULL); +} esp_err_t play_raw_stop_queue(const char *fp, QueueHandle_t stop_handle) { FILE *fh = fopen(fp, "rb"); @@ -10,10 +105,11 @@ esp_err_t play_raw_stop_queue(const char *fp, QueueHandle_t stop_handle) { ESP_LOGE(TAG, "Failed to open file"); return ESP_ERR_INVALID_ARG; } + // create a writer buffer - uint8_t *read_buf = (uint8_t*) calloc(AUDIO_BUFFER, sizeof(uint8_t)); - uint16_t *write_buf = (uint16_t*) calloc(AUDIO_BUFFER, sizeof(uint16_t)); + uint8_t *read_buf = (uint8_t*) calloc(AUDIO_BLOCK_SIZE, sizeof(uint8_t)); + uint16_t *write_buf = (uint16_t*) calloc(AUDIO_BLOCK_SIZE, sizeof(uint16_t)); size_t bytes_read = 0; size_t bytes_written = 0; @@ -23,7 +119,7 @@ esp_err_t play_raw_stop_queue(const char *fp, QueueHandle_t stop_handle) { return channel_enable_result; } - bytes_read = fread(read_buf, sizeof(uint8_t), AUDIO_BUFFER, fh); + bytes_read = fread(read_buf, sizeof(uint8_t), AUDIO_BLOCK_SIZE, fh); for (int i = 0; i < bytes_read; i++) { write_buf[i] = read_buf[i] << 4; } @@ -38,7 +134,7 @@ esp_err_t play_raw_stop_queue(const char *fp, QueueHandle_t stop_handle) { // printf("%s0x%04X ", (val < 0 ? "-" : "+"), (val < 0 ? -val : val)); // }> i2s_channel_write(tx_chan, write_buf, bytes_read * sizeof(int16_t), &bytes_written, portMAX_DELAY); - bytes_read = fread(read_buf, sizeof(uint8_t), AUDIO_BUFFER, fh); + bytes_read = fread(read_buf, sizeof(uint8_t), AUDIO_BLOCK_SIZE, fh); for (int i = 0; i < bytes_read; i++) { write_buf[i] = read_buf[i] << 4; } @@ -67,11 +163,11 @@ esp_err_t play_wav(const char *fp) { // skip the header... fseek(fh, 44, SEEK_SET); - int16_t *buf = (int16_t*) calloc(AUDIO_BUFFER, sizeof(int16_t)); + int16_t *buf = (int16_t*) calloc(AUDIO_BLOCK_SIZE, sizeof(int16_t)); size_t bytes_read = 0; size_t bytes_written = 0; - bytes_read = fread(buf, sizeof(int16_t), AUDIO_BUFFER, fh); + bytes_read = fread(buf, sizeof(int16_t), AUDIO_BLOCK_SIZE, fh); for (int i = 0; i < bytes_read; i++) { buf[i] = buf[i] >> 2; } @@ -81,7 +177,7 @@ esp_err_t play_wav(const char *fp) { while (bytes_read > 0) { - bytes_read = fread(buf, sizeof(int16_t), AUDIO_BUFFER, fh); + bytes_read = fread(buf, sizeof(int16_t), AUDIO_BLOCK_SIZE, fh); for (int i = 0; i < bytes_read; i++) { buf[i] = buf[i] >> 2; } @@ -104,12 +200,12 @@ esp_err_t play_raw(const char *fp) { } // create a writer buffer - uint8_t *read_buf = (uint8_t*) calloc(AUDIO_BUFFER, sizeof(uint8_t)); - uint16_t *write_buf = (uint16_t*) calloc(AUDIO_BUFFER, sizeof(uint16_t)); + uint8_t *read_buf = (uint8_t*) calloc(AUDIO_BLOCK_SIZE, sizeof(uint8_t)); + uint16_t *write_buf = (uint16_t*) calloc(AUDIO_BLOCK_SIZE, sizeof(uint16_t)); size_t bytes_read = 0; size_t bytes_written = 0; - bytes_read = fread(read_buf, sizeof(uint8_t), AUDIO_BUFFER, fh); + bytes_read = fread(read_buf, sizeof(uint8_t), AUDIO_BLOCK_SIZE, fh); for (int i = 0; i < bytes_read; i++) { write_buf[i] = read_buf[i] << 4; } @@ -128,7 +224,7 @@ esp_err_t play_raw(const char *fp) { // int16_t val = buf[i]; // printf("%s0x%04X ", (val < 0 ? "-" : "+"), (val < 0 ? -val : val)); // }> - bytes_read = fread(read_buf, sizeof(uint8_t), AUDIO_BUFFER, fh); + bytes_read = fread(read_buf, sizeof(uint8_t), AUDIO_BLOCK_SIZE, fh); for (int i = 0; i < bytes_read; i++) { write_buf[i] = read_buf[i] << 4; } @@ -168,8 +264,28 @@ void init_speaker(void) { }; ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &tx_std_cfg)); - ESP_ERROR_CHECK_WITHOUT_ABORT(play_wav("/sdcard/startup.wav")); - ESP_ERROR_CHECK_WITHOUT_ABORT(play_raw("/sdcard/boot.pcm")); + // init queues and list + sequential_clip_queue = xQueueCreate(8, sizeof(audio_clip_t)); + + // start task + // TODO: make stack 4096 again :( + xTaskCreate(speaker_task, "play_audio", 16384, NULL, 5, NULL); + + // ESP_ERROR_CHECK_WITHOUT_ABORT(play_wav(MOUNT_POINT "/startup.wav")); + // ESP_ERROR_CHECK_WITHOUT_ABORT(play_raw(MOUNT_POINT "/boot.pcm")); + + char* file_name = (char*) malloc(sizeof(MOUNT_POINT "/startup.wav")); + memcpy(file_name, MOUNT_POINT "/startup.wav", sizeof(MOUNT_POINT "/startup.wav")); + + audio_clip_t startup_clip = { + .file_name = file_name, + .prescaler = 1, + .repeat = false, + .play_immediatly = false, + }; + + xQueueSend(sequential_clip_queue, &startup_clip, 0); + vTaskDelay(pdMS_TO_TICKS(5000)); ESP_LOGI(TAG, "Speaker initialized!"); } diff --git a/main/drivers/speaker.h b/main/drivers/speaker.h index f020675..c8f99de 100644 --- a/main/drivers/speaker.h +++ b/main/drivers/speaker.h @@ -3,6 +3,9 @@ #include #include +#include +#include +#include #include "freertos/FreeRTOS.h" #include "freertos/queue.h" #include "freertos/task.h" @@ -16,10 +19,28 @@ #define SPEAKER_PIN_WS GPIO_NUM_9 #define SPEAKER_PIN_DOUT GPIO_NUM_3 #define SAMPLE_RATE 44100 -#define AUDIO_BUFFER 2048 +#define PLAY_QUEUE_SIZE 8 +// The maximum number of clips that can be playing at one time. +#define PLAYING_CLIP_SIZE 8 +// Audio will be processed in blocks of this many samples +#define AUDIO_BLOCK_SIZE 2048 extern i2s_chan_handle_t tx_chan; +typedef struct { + /// A dynamically allocated string specifying the name of the + /// file to play. The speaker system will free this once it + /// is done. + char* file_name; + /// A number that all samples will be shifted right by. + uint8_t prescaler; + /// A flag for repeating. + bool repeat; + /// A flag for starting the clip immediatly, + /// even if there is another clip playing. + bool play_immediatly; +} audio_clip_t; + /// Plays a audio file stopping when something is received on `stop_handle` /// /// `stop_handle` should be of size `uint8_t`.