Page 1 of 1

Raw Stream Writer does not play the complete data written to the buffer.

Posted: Tue Dec 31, 2024 4:12 pm
by CyborgZA
I have a pipeline set up as follows: raw_stream_writer->mp3_decoder->I2S_stream_writer.
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)");
        }
    }
}
The above Code is Then Paused When I need to Play a short mp3 File. I use a seperate task for this:

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
}
The above task calls the function below to play the small mp3 file ( 1.5 Second clip)

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;
}  // 
For additional info, here is how my pipeline is set up:

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;
And initialised here:

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);
    
 
    
I set up events and start the pipeline in a seperate task (audio_pipeline_run(pipeline_seat1);)

Re: Raw Stream Writer does not play the complete data written to the buffer.

Posted: Thu Jan 02, 2025 4:46 pm
by CyborgZA
So, in my function "play_seat1_mp3()" I added the following line to finish playing the audio in the rawstreamWriter's buffer

Code: Select all

audio_element_set_ringbuf_done(base_raw_write_seat1); //Where base_raw_write_seat1 is my rawstream element
This now finishes playing the leftover frames in the buffer, But Now, I cannot write to the rawstream element in my pipeline afterwards.
I cheked the element states, of the 3 elements in my pipeline and they are all set to 1 and my pipeline is running,
But as soon as I want to call the raw_stream_write(...) function again I get a return value of <= 0

I have tried the following commands to try and get the rawstreamWriter element to accept new data written to it with no luck:
audio_element_reset_state(base_raw_write_seat1);
and
audio_element_resume(base_raw_write_seat1,0,0);

So, I am totally stuck now. Fixing one issue that just creates another issue :(