296 lines
8.3 KiB
C++
296 lines
8.3 KiB
C++
#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;
|
|
|
|
/// 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_audio_clip_t> 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");
|
|
if (fh == NULL) {
|
|
ESP_LOGE(TAG, "Failed to open file");
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
|
|
// create a writer buffer
|
|
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;
|
|
|
|
esp_err_t channel_enable_result = i2s_channel_enable(tx_chan);
|
|
ESP_ERROR_CHECK_WITHOUT_ABORT(channel_enable_result);
|
|
if (channel_enable_result != ESP_OK) {
|
|
return channel_enable_result;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// i2s_channel_enable(tx_handle);
|
|
|
|
while (bytes_read > 0) {
|
|
// write the buffer to the i2s
|
|
// ESP_LOGI(TAG, "Writing: ");
|
|
// for (int i = 0; i < words_read; i++) {
|
|
// int16_t val = buf[i];
|
|
// 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_BLOCK_SIZE, fh);
|
|
for (int i = 0; i < bytes_read; i++) {
|
|
write_buf[i] = read_buf[i] << 4;
|
|
}
|
|
uint8_t recv = 0;
|
|
if (xQueueReceive(stop_handle, &recv, 0)) {
|
|
break;
|
|
}
|
|
vTaskDelay(pdMS_TO_TICKS(10));
|
|
}
|
|
|
|
i2s_channel_disable(tx_chan);
|
|
free(read_buf);
|
|
free(write_buf);
|
|
fclose(fh);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t play_wav(const char *fp) {
|
|
FILE *fh = fopen(fp, "rb");
|
|
if (fh == NULL) {
|
|
ESP_LOGE(TAG, "Failed to open file");
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
// skip the header...
|
|
fseek(fh, 44, SEEK_SET);
|
|
|
|
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_BLOCK_SIZE, fh);
|
|
for (int i = 0; i < bytes_read; i++) {
|
|
buf[i] = buf[i] >> 2;
|
|
}
|
|
|
|
i2s_channel_preload_data(tx_chan, buf, bytes_read * sizeof(int16_t), &bytes_written);
|
|
i2s_channel_enable(tx_chan);
|
|
|
|
while (bytes_read > 0)
|
|
{
|
|
bytes_read = fread(buf, sizeof(int16_t), AUDIO_BLOCK_SIZE, fh);
|
|
for (int i = 0; i < bytes_read; i++) {
|
|
buf[i] = buf[i] >> 2;
|
|
}
|
|
// write the buffer to the i2s
|
|
i2s_channel_write(tx_chan, buf, bytes_read * sizeof(int16_t), &bytes_written, portMAX_DELAY);
|
|
// ESP_LOGI(TAG, "Bytes read: %d", bytes_read);
|
|
}
|
|
|
|
i2s_channel_disable(tx_chan);
|
|
free(buf);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t play_raw(const char *fp) {
|
|
FILE *fh = fopen(fp, "rb");
|
|
if (fh == NULL) {
|
|
ESP_LOGE(TAG, "Failed to open file");
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
// create a writer buffer
|
|
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_BLOCK_SIZE, fh);
|
|
for (int i = 0; i < bytes_read; i++) {
|
|
write_buf[i] = read_buf[i] << 4;
|
|
}
|
|
|
|
i2s_channel_preload_data(tx_chan, write_buf, bytes_read * sizeof(int16_t), &bytes_written);
|
|
esp_err_t channel_enable_result = i2s_channel_enable(tx_chan);
|
|
ESP_ERROR_CHECK_WITHOUT_ABORT(channel_enable_result);
|
|
if (channel_enable_result != ESP_OK) {
|
|
return channel_enable_result;
|
|
}
|
|
|
|
while (bytes_read > 0) {
|
|
// write the buffer to the i2s
|
|
// ESP_LOGI(TAG, "Writing: ");
|
|
// for (int i = 0; i < words_read; i++) {
|
|
// int16_t val = buf[i];
|
|
// printf("%s0x%04X ", (val < 0 ? "-" : "+"), (val < 0 ? -val : val));
|
|
// }>
|
|
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;
|
|
}
|
|
vTaskDelay(pdMS_TO_TICKS(10));
|
|
i2s_channel_write(tx_chan, write_buf, bytes_read * sizeof(int16_t), &bytes_written, portMAX_DELAY);
|
|
}
|
|
|
|
i2s_channel_disable(tx_chan);
|
|
free(read_buf);
|
|
free(write_buf);
|
|
fclose(fh);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
void init_speaker(void) {
|
|
ESP_LOGI(TAG, "Initializing speaker...");
|
|
|
|
i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
|
ESP_ERROR_CHECK(i2s_new_channel(&tx_chan_cfg, &tx_chan, NULL));
|
|
|
|
i2s_std_config_t tx_std_cfg = {
|
|
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
|
|
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
|
|
.gpio_cfg = {
|
|
.mclk = I2S_GPIO_UNUSED,
|
|
.bclk = SPEAKER_PIN_BCLK,
|
|
.ws = SPEAKER_PIN_WS,
|
|
.dout = SPEAKER_PIN_DOUT,
|
|
.din = GPIO_NUM_NC,
|
|
.invert_flags = {
|
|
.mclk_inv = false,
|
|
.bclk_inv = false,
|
|
.ws_inv = false,
|
|
},
|
|
},
|
|
};
|
|
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &tx_std_cfg));
|
|
|
|
// 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!");
|
|
}
|
|
|
|
void play_example() {
|
|
ESP_ERROR_CHECK_WITHOUT_ABORT(play_raw("/sdcard/o.pcm"));
|
|
}
|