Help with streaming BLE audio with device as source

Hi there!

I am trying to make an application that plays an audio sample to a BLE speaker (another nRF5340 running as headset) where the audio sample is in memory. I have based my code on the nRF5340 BLE audio example. During development, I am simply including the audio buffer from a header file (generated from a hex dump using xxd), and have my raw PCM data (encoded as 16-bit 48kHz) ready.

My first approach which was giving promising results, was to "hijack" the BLE gateway example in the audio_system.c encoder thread next to where the test tone gets added. I simply gave myself a callback there which gave me a pointer to the buffer that was about to be encoded, and then I overwrote that data to the data from my sample instead with a memcpy() call. This very simple approach seemed to be working, but the processor could not keep up. I was getting very choppy audio and frequently getting "I2S RX overrun" messages. The audio was discernibly my sample though.

I investigated ways of being less intrusive to the example, so I rewrote the code to instead have my callback right at the I2S driver buffer switch (in the audio_datapath_i2s_blk_complete() function). Even though I feel I am shoving in my data right where the normal I2S driver is putting its PCM data, I got the same choppy audio. I did make sure that I kept passing the I2S driver a fresh "dummy" buffer to keep it satisfied while I passed the real rx_buf (from the FIFO) to my callback to put my data in.

In both of these approaches I did my best to minimize the code changes to the drivers (a handful of lines at most), but I continue to be unsuccessful.

I am starting to think my next approach is to completely get rid of the I2S side of things (the I2S chip is not even on my custom PCB for the gateway). Basically get rid of the entire data path and audio system setup, and just make calls to sw_codec_encode() and streamctrl_encoded_data_send() directly using a timer (knowing my sample rate and chunk size this should be pretty doable I imagine). But before I go down this path, I want to see if there is any advice on alternative approaches or getting my earlier attempts to work?

I know this application is not really what the BLE audio example was built for, but I feel this is a commonly desired use for it so hopefully I can get some help. Thanks!

Parents
  • Hi Ben

    I think you might have an easier time if set up the example for USB rather than I2S, using the audio_usb.c module as shown here.

    In many ways the USB interface is easier to manage than the I2S one, and it is possible that the choppy audio you experience is caused by the synchronization of the I2S interface.  

    Could you give this a go and see if you get better results? 

    If you are still having issues please let me know what you tried, and I will take a look at it. 

    Best regards
    Torbjørn

  • Hi Ben,

    In general, I2S (PCM as well) requires very precise clock, especially if using an external codec to produce I2S signals. To process  incoming streams (either PCM or I2S) in the processor, a very quick and precise processing is required, as loosing frames may result in choppy or noisy audio. You may need to use PPI and potentially bypass Zephyr OS scheduler to process these signals properly.

    Usually to properly process audio (with some filters) you would need a dedicated core, unless you just pass it through.



    Thanks,
    Reuven

Reply
  • Hi Ben,

    In general, I2S (PCM as well) requires very precise clock, especially if using an external codec to produce I2S signals. To process  incoming streams (either PCM or I2S) in the processor, a very quick and precise processing is required, as loosing frames may result in choppy or noisy audio. You may need to use PPI and potentially bypass Zephyr OS scheduler to process these signals properly.

    Usually to properly process audio (with some filters) you would need a dedicated core, unless you just pass it through.



    Thanks,
    Reuven

Children
No Data
Related