Create audio pipeline with output to file and Bluetooth

jeeper1974
Posts: 7
Joined: Thu Aug 13, 2020 5:47 am

Create audio pipeline with output to file and Bluetooth

Postby jeeper1974 » Wed Sep 09, 2020 6:02 am

I am trying to setup a audio pipeline that will take input from the microphone(i2s) on a LyraT board and send it to some Bluetooth headphones along with converting it to a WAV and saving it to a file on the SD card.
I can make a i2s-WAV-sdcard pipeline work successfully.
And I can make a i2s-Bluetooth headphones pipeline work successfully.
But when I create a i2s-Bluetooth-WAV-sdcard pipeline only the Bluetooth works some of the times and the file never saves.
Any suggestions on why the pipeline does not allow two writers (Bluetooth and sdcard) to be linked to one audio source?

Code: Select all

audio_pipeline_cfg_t pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG();
pipeline = audio_pipeline_init(&pipeline_cfg);
mem_assert(pipeline);

i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT();
i2s_cfg.type = AUDIO_STREAM_READER;
i2s_stream_reader = i2s_stream_init(&i2s_cfg);

wav_encoder_cfg_t wav_cfg = DEFAULT_WAV_ENCODER_CONFIG();
wav_encoder = wav_encoder_init(&wav_cfg);

fatfs_stream_cfg_t fatfs_cfg = FATFS_STREAM_CFG_DEFAULT();
fatfs_cfg.type = AUDIO_STREAM_WRITER;
fatfs_stream_writer = fatfs_stream_init(&fatfs_cfg);

a2dp_stream_config_t a2dp_config = {
		   .type = AUDIO_STREAM_WRITER,
		   .user_callback = {0},
	   };
bt_stream_writer = a2dp_stream_init(&a2dp_config);

audio_pipeline_register(pipeline, i2s_stream_reader, "i2s");
audio_pipeline_register(pipeline, wav_encoder, "wav");
audio_pipeline_register(pipeline, fatfs_stream_writer, "file");
audio_pipeline_register(pipeline, bt_stream_writer, "bt");

const char *link_tag[4] = {"i2s", "bt", "wav", "file"  };
audio_pipeline_link(pipeline, &link_tag[0], 4);

audio_element_set_uri(fatfs_stream_writer, "/sdcard/rec1.wav");

audio_pipeline_run(pipeline);

...


jeeper1974
Posts: 7
Joined: Thu Aug 13, 2020 5:47 am

Re: Create audio pipeline with output to file and Bluetooth

Postby jeeper1974 » Thu Sep 10, 2020 2:35 pm

For others that may have a similar issue and want to output audio to multiple elements, I found a solution that works. You cannot use the ESP-ADF pipeline. You need to go to the layer below the pipeline function and connect the elements with ringbuffers. This allows one of the elements to use "audio_element_set_multi_output_ringbuf(audio_element_handle_t el, ringbuf_handle_t rb, int index)" which will use the input ringbuffer for multiple other elements.

Code: Select all

    audio_element_handle_t fatfs_stream_writer, i2s_stream_reader, wav_encoder, bt_stream_writer;
    ringbuf_handle_t ringbuf01, ringbuf02, ringbuf11;

    esp_log_level_set("*", ESP_LOG_WARN);
    esp_log_level_set(TAG, ESP_LOG_INFO);

    ESP_LOGI(TAG, "[ 1 ] Mount sdcard");
    // Initialize peripherals management
    esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
    esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg);

    // Initialize SD Card peripheral
    audio_board_sdcard_init(set);

    ESP_LOGI(TAG, "[ 2 ] Start codec chip");

    audio_board_handle_t board_handle = audio_board_init();
    codec_begin(); //This will start up the codec
    int player_volume;
    audio_hal_get_volume(board_handle->audio_hal, &player_volume);

    ESP_LOGI(TAG, "[3.0] Create audio pipeline for recording");

    ESP_LOGI(TAG, "[3.2] Create i2s stream to read audio data from codec chip");
    i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT();
    i2s_cfg.type = AUDIO_STREAM_READER;
    i2s_cfg.multi_out_num = 1;
    i2s_cfg.task_core = 1;
	#if defined CONFIG_ESP_LYRAT_MINI_V1_1_BOARD
		i2s_cfg.i2s_port = (i2s_port_t)1;
	#endif
    i2s_stream_reader = i2s_stream_init(&i2s_cfg);

    ESP_LOGI(TAG, "[3.3] Create wav encoder to encode wav format");
    wav_encoder_cfg_t wav_cfg = DEFAULT_WAV_ENCODER_CONFIG();
    wav_encoder = wav_encoder_init(&wav_cfg);

    ESP_LOGI(TAG, "[3.1] Create fatfs stream to write data to sdcard");
	fatfs_stream_cfg_t fatfs_cfg = FATFS_STREAM_CFG_DEFAULT();
	fatfs_cfg.type = AUDIO_STREAM_WRITER;
	fatfs_stream_writer = fatfs_stream_init(&fatfs_cfg);

	audio_element_info_t info = AUDIO_ELEMENT_INFO_DEFAULT();
	audio_element_getinfo(i2s_stream_reader, &info);
	audio_element_setinfo(fatfs_stream_writer, &info);

	ESP_LOGI(TAG, "[3.3] Create a ringbuffer and insert it between i2s_stream_reader and wav_encoder");
	ringbuf01 = rb_create(RING_BUFFER_SIZE, 1);
	audio_element_set_output_ringbuf(i2s_stream_reader, ringbuf01);
	audio_element_set_input_ringbuf(wav_encoder, ringbuf01);

	ESP_LOGI(TAG, "[3.4] Create a ringbuffer and insert it between wav_encoder and wav_fatfs_stream_writer");
	ringbuf02 = rb_create(RING_BUFFER_SIZE, 1);
	audio_element_set_output_ringbuf(wav_encoder, ringbuf02);
	audio_element_set_input_ringbuf(fatfs_stream_writer, ringbuf02);

	ESP_LOGI(TAG, "[3.4.1] Set up  uri (file as fatfs_stream, wav as wav encoder)");
	audio_element_set_uri(fatfs_stream_writer, "/sdcard/rec1.wav");

	ESP_LOGI(TAG, "[3.5] Create Bluetooth with i2s element input ");
	a2dp_stream_config_t a2dp_config = {
		   .type = AUDIO_STREAM_WRITER,
		   .user_callback = {0},
	   };
	bt_stream_writer = a2dp_stream_init(&a2dp_config);
	// bt_stream_writer = bluetooth_service_create_stream();

	ringbuf11 = rb_create(RING_BUFFER_SIZE, 1);
	audio_element_set_multi_output_ringbuf(i2s_stream_reader,ringbuf11,0);
	audio_element_set_input_ringbuf(bt_stream_writer,ringbuf11);

	ESP_LOGI(TAG, "[5.0] Set callback function for audio_elements");
	/**
	* Event handler used here is quite generic and simple one.
	* It just reports state changes of different audio_elements
	* Note that, it is not mandatory to set event callbacks.
	* One may remove entire step [5.0]. This example could still run.
	*/
	audio_element_set_event_callback(wav_encoder, audio_element_event_handler, NULL);
	audio_element_set_event_callback(i2s_stream_reader, audio_element_event_handler, NULL);
	audio_element_set_event_callback(fatfs_stream_writer, audio_element_event_handler, NULL);
	audio_element_set_event_callback(bt_stream_writer, audio_element_event_handler, NULL);


	ESP_LOGI(TAG, "[ 6.0 ] Set up  event listener");
	audio_event_iface_cfg_t evt_cfg = AUDIO_EVENT_IFACE_DEFAULT_CFG();
	audio_event_iface_handle_t evt = audio_event_iface_init(&evt_cfg);
	esp_periph_handle_t bt_periph;

	ESP_LOGI(TAG, "[6.1] Create bt peripheral");
	bt_periph = bt_create_periph();

	ESP_LOGI(TAG, "[6.2] Start bt peripheral");
	esp_periph_start(set, bt_periph);

	ESP_LOGI(TAG, "[7.0] Listening event from peripherals");
	audio_event_iface_set_listener(esp_periph_set_get_event_iface(set), evt);

	ESP_LOGI(TAG, "[8.0] Start audio elements");
	audio_element_run(i2s_stream_reader);
	audio_element_run(wav_encoder);
	audio_element_run(fatfs_stream_writer);
	audio_element_run(bt_stream_writer);

	//RUN starts in a paused state. RESUME starts the element going
	audio_element_resume(i2s_stream_reader, 0, 0);
	audio_element_resume(wav_encoder, 0, 0);
	audio_element_resume(fatfs_stream_writer, 0, 0);
	audio_element_resume(bt_stream_writer, 0, 0);

    ESP_LOGI(TAG, "[ 6 ] Listen for all pipeline events, record for %d Seconds", RECORD_TIME_SECONDS);
    int second_recorded = 0;
    while (1) {
        audio_event_iface_msg_t msg;
        int button_debounce = 3; //Seconds before the button can be pressed again to stop the recording
        int button_time = 0;
        if (audio_event_iface_listen(evt, &msg, 1000 / portTICK_RATE_MS) != ESP_OK) {
            second_recorded ++;
            ESP_LOGI(TAG, "[ * ] Recording ... %d", second_recorded);
            /*Button pressed again to stop recording*/

//            if ((get_button(8) || get_button(9)) && button_time >= button_debounce){
//            	ESP_LOGI(TAG, "Button Pressed to Stop");
//            	break;
//		   }
            //button_time++;
            if (second_recorded >= RECORD_TIME_SECONDS) {
                break;
            }
            continue;
        }

//        if ((msg.source_type == PERIPH_ID_TOUCH || msg.source_type == PERIPH_ID_BUTTON || msg.source_type == PERIPH_ID_ADC_BTN)
//                    && (msg.cmd == PERIPH_TOUCH_TAP || msg.cmd == PERIPH_BUTTON_PRESSED || msg.cmd == PERIPH_ADC_BUTTON_PRESSED)) {

		
			if (msg.source_type == PERIPH_ID_BLUETOOTH && msg.source == (void *)bt_periph) {
				if ((msg.cmd == PERIPH_BLUETOOTH_DISCONNECTED) || (msg.cmd == PERIPH_BLUETOOTH_AUDIO_SUSPENDED)) {
					ESP_LOGW(TAG, "[ * ] Bluetooth disconnected or suspended");
				   // periph_bt_stop(bt_periph);
					break;
				}
			}
		
        /* Stop when the last pipeline element (i2s_stream_reader in this case) receives stop event */
        if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT && msg.source == (void *) i2s_stream_reader
            && msg.cmd == AEL_MSG_CMD_REPORT_STATUS
            && (((int)msg.data == AEL_STATUS_STATE_STOPPED) || ((int)msg.data == AEL_STATUS_STATE_FINISHED))) {
            ESP_LOGW(TAG, "[ * ] Stop event received");
            break;
        }
    }
    ESP_LOGI(TAG, "[ 7 ] Stop audio_pipeline");

    /* Stop all periph before removing the listener */
    esp_periph_set_stop_all(set);

    /* Release all resources */
       audio_element_deinit(i2s_stream_reader);
       audio_element_deinit(wav_encoder);
       audio_element_deinit(fatfs_stream_writer);
       audio_element_deinit(bt_stream_writer);
       esp_periph_set_destroy(set);
       rb_destroy(ringbuf01);
       rb_destroy(ringbuf02);
       rb_destroy(ringbuf11);

}
    
    


bosleymusic
Posts: 3
Joined: Mon Aug 24, 2020 9:45 am

Re: Create audio pipeline with output to file and Bluetooth

Postby bosleymusic » Thu Sep 10, 2020 6:13 pm

You replied earlier about this problem I was having so thank you for the sharing the post. I was also going to tell you there is an example for a raw split - they take an http stream and play it back while writing to a fatfs - you might want to try that example project out to help debug your issues here.

Who is online

Users browsing this forum: No registered users and 1 guest