I have a seperate task that feeds data (mp3 frames) to a ringbuffer and another task that writes the data from the ringbuffer to the raw_stream_writer.
At Times i need to stop the mp3 frames in the ringbuffer and play a 1 second mp3 file and then continue to process and play the mp3 frames in the ringbuffer after a small delay. So when I need to play the mp3 file, I stop feeding data to the ringbuffer and then directly write the file contents to the raw_stream_writer. when complete, I resume the task for writing the ringbuffer data to the raw_stream_writer.
Problem:
When I feed the data from an mp3 file directly to the raw_stream_writer only the first portion is played and the rest is only played after I start receiving new data from the ringbuffer. I have tried to Stop and Start the Pipeline before writing the small mp3 file to the raw_stream_writer. I have also tried using audio_element_finish_state(..) after the mp3 file was written to the raw_stream_writer.
I have increased the length my mp3 files to be 1.5 seconds ( added 500ms silence to the files) but also did not help.
Is there any way to force the pipeline to finish playing the data in its buffers, instead of waiting for more data?
Do I need to Stop and Start the pipeline everytime I want to inject a small audio clip to the raw_stream_writer?
I find very little information and documentation on this topic.
Please see some supporting code to understand what I am trying to achieve (I removed the pipeline stops and starts as it caused all kinds of issues, I have also removed raw_stream_flush(base_raw_write_seat1) from the code below where I flush the remaining data in the buffer before writing the small mp3 clip to the pipeline)
Here I push mp3 frames to the pipeline (raw_stream_writer ) And this works.. no issues with this..
Code: Select all
void audiowrite_task_seat1(void *arg)
{
uint8_t *audiodata;
size_t item_size;
for(;;)
{
audiodata = (uint8_t *)xRingbufferReceive(audio_ringbuffer_handle_s1, &item_size, portMAX_DELAY); // Receive audio data from the ring buffer
if(!system_status.pipeline_s1_running || !seatUnit->seat1.enabled)
{
vRingbufferReturnItem(audio_ringbuffer_handle_s1, (void *)audiodata); //Return the item as the Pipeline is not running
}
if (audiodata != NULL)
{
#ifndef PIPLINE_USE_MP3_FILE
ssize_t bytes_written = raw_stream_write(base_raw_write_seat1, (char *)audiodata, item_size); // Write the data to the raw stream
if (bytes_written < 0) {
ESP_LOGE(TAG, "Failed to write data to raw stream 1");
resume_audio_pipeline_seat1();
} else
{
#ifdef LOG_COMMS_ENABLED
ESP_LOGI(TAG, "[ RBUFF ] Data sent: %d bytes", item_size);
#endif
}
#endif
// Free the received item in the ring buffer
vRingbufferReturnItem(audio_ringbuffer_handle_s1, (void *)audiodata);
}
else
{
ESP_LOGW(TAG, "[ !! ] No data received from ring buffer 1 (timeout or empty)");
}
}
}
Code: Select all
void play_seat1_mp3_task(void *arg) // Pause Data from the mp3 Ringbuffer
{
seatUnit->seat1.enabled = false;
flush_audio_pipeline_seat1(); //Flush any leftover data in the rawstreamwriter's buffer
if (play_seat1_mp3() == ESP_OK)
{
ESP_LOGI(TAG, "Audio clip playback finished successfully.");
}
else
{
ESP_LOGE(TAG, "Audio clip playback failed.");
}
vTaskDelay(1000/portTICK_PERIOD_MS);
seatUnit->seat1.enabled = true; // Re Enable data from the mp3 Ringbuffer
vTaskDelete(NULL); // Delete the task to free resources
}
Code: Select all
esp_err_t play_seat1_mp3()
{
if (seat1_mp3_playing) return ESP_FAIL;
int ch = seatUnit->seat1.channelNo;
ESP_LOGW(TAG, "Play Language for Channel %d",ch);
char mp3path[128] = {0};
char * lname = NULL;
snprintf(mp3path, sizeof(mp3path), "/littlefs/snd/notfound.mp3");
if (!esp_littlefs_mounted("littlefs"))
{
ESP_LOGE(TAG,"FILESYSTEM NOT MOUNTED");
return ESP_FAIL;
}
FILE *file = fopen(mp3path, "r");
if (!file)
{
ESP_LOGW(TAG, "MP3 file %s not found",mp3path);
return ESP_FAIL;
}
char *buffer = heap_caps_malloc(FILECHUNK_SIZE, MALLOC_CAP_SPIRAM);
if (!buffer) {
ESP_LOGE(TAG, "Failed to allocate buffer memory");
fclose(file);
return ESP_FAIL;
}
seat1_mp3_playing = true;
int bytes_read = 0;
while ((bytes_read = fread(buffer, 1, FILECHUNK_SIZE, file)) > 0) {
if (raw_stream_write(base_raw_write_seat1, buffer, bytes_read) <= 0) {
ESP_LOGE(TAG, "Error writing to raw stream");
break;
}
}
heap_caps_free(buffer);
fclose(file);
// Signal end of data
ESP_LOGW(TAG, "WAIT FOR STOP");
audio_element_finish_state(base_raw_write_seat1);
seat1_wait_for_stop();
ESP_LOGI(TAG, "Finished playing MP3 file");
seat1_mp3_playing = false;
return ESP_OK;
}
esp_err_t seat1_wait_for_stop()
{
while (audio_element_get_state(base_raw_write_seat1) != AEL_STATE_FINISHED)
{
vTaskDelay(10 / portTICK_PERIOD_MS); // Poll every 10ms
}
return ESP_OK;
} //
Code: Select all
audio_pipeline_handle_t pipeline_seat1; //SEAT 1 PIPELINE
audio_element_handle_t base_raw_write_seat1 = NULL; //AUDIO ELEMENT - raw_stream_writer
audio_element_handle_t mp3_decoder_seat1;
audio_element_handle_t i2s_stream_writer_seat1;
Code: Select all
raw_stream_cfg_t raw_cfg = RAW_STREAM_CFG_DEFAULT();
raw_cfg.type = AUDIO_STREAM_WRITER;
raw_cfg.out_rb_size = 8190;
base_raw_write_seat1 = raw_stream_init(&raw_cfg);
i2s_stream_cfg_t s1_i2s_cfg = I2S_STREAM_CFG_DEFAULT();
s1_i2s_cfg.chan_cfg.id = S1_I2S_NUM;
s1_i2s_cfg.std_cfg.gpio_cfg.bclk = S1_I2S_BCK_IO;
s1_i2s_cfg.std_cfg.gpio_cfg.dout = S1_I2S_DO_IO;
s1_i2s_cfg.std_cfg.gpio_cfg.ws = S1_I2S_WS_IO;
s1_i2s_cfg.use_alc = true;
i2s_stream_writer_seat1 = i2s_stream_init(&s1_i2s_cfg);
mp3_decoder_cfg_t mp3_cfg = DEFAULT_MP3_DECODER_CONFIG();
mp3_cfg.out_rb_size = (4 * 1024);
mp3_cfg.id3_parse_enable = true;
mp3_decoder_seat1 = mp3_decoder_init(&mp3_cfg);
audio_pipeline_register(pipeline_seat1, base_raw_write_seat1, "raw_s1");
audio_pipeline_register(pipeline_seat1, mp3_decoder_seat1, "mp3_s1");
audio_pipeline_register(pipeline_seat1, i2s_stream_writer_seat1, "i2s_s1");
audio_pipeline_link(pipeline_seat1, &link_tag[0], 3);