Note...possible TLDR situation below... but it is not a one-paragraph discussion wrt I2s and uasynciopythoncoder wrote: ↑Sat Dec 08, 2018 4:51 pmI have doubts about uasyncio coping with i2s. It depends on how the i2s code is implemented. uasyncio can handle data streams on UARTs because the UART interface uses interrupts and buffering. So the buffer is filled below the radar of uasyncio, and so long as the latter is emptied quicker than the ISR fills it, all is well, even with multiple competing coros. The buffer size needs to be big enough to cope with the worst-case latency imposed by the other coros.
A few years ago someone tried to implement I2S on the Pyboard using DMA but never managed to get it working without glitches. How does the Loboris version work? Is the buffer size configurable?
In many cases it will be possible to have I2S work with uasyncio on the ESP32. I'll refer to the Machine.I2S implementation I recently finished on a Loboris fork Lobo fork with I2S, C file here: Machine.I2S class (Note that the Loboris port does not yet have I2S support)
I2S in the ESP-IDF works with DMA buffering. During instantiation in Machine.I2S, the size and number of DMA buffers is defined. Once started, the ESP32 I2S implementation continually reads samples from an I2S device and fills DMA buffers even when something like uasyncio is taking cpu time. There are two design constraints to making "gapless sample processing work". Best, shown by example:
Let's say you want to process audio samples from the Adafruit MEMS Microphone at a CD quality sampling rate of 44.1 kHz. You create an uasyncio task to call the Machine.I2S.readinto() method. In this example, the readinto() method will request 256 audio samples each call. The I2S microphone Adafruit uses in their breakout board encodes each audio sample as 8 bytes (L+R channels - although only one channel is used, 32 bit samples). Say you request 2048 bytes (256 samples x 8 bytes/sample). On my Lobo build, each call to the readinto() method takes on average 410 us to read 2048 bytes into a bytearray() buffer. The time for the physical I2S port to read 256 samples from the microphone is 5.8 ms (256/44.1kHz). The 5.8 ms and 410us define Design Constraint #1 - all other Coroutines must yield to the I2S task, on average, within 5.8 ms - 410us. Otherwise, in the long run, samples will get dropped and the processing of audio samples will not be gapless.
Design Constraint #2 focuses on DMA buffering and is the maximum amount of time that all other Coroutines can run before samples get dropped. Let's say we allocate 128 DMA buffers @ 128 bytes-per-buffer in the Machine.I2S() instantiation call (pretty much the maximum allowed). That is 128 x 128 = 16,384 bytes of sample buffering. As mentioned, there are 8 bytes per sample, so the DMA buffers can hold 2048 samples. At 44.1kHz, that works out to 46.4 ms of buffer time (2048/44.1kHz).
This means that other Coroutines can run for 46.4 ms before yielding to the I2S coroutine. If they run longer the DMA buffer will get overrun and audio samples will be lost.
In summary, usasyncio can work with I2S if the system is designed taking into account the two design constraints described above. I think the 44.1 kHz example will be difficult to realize with the existing usasyncio implementation. Having all other coros complete in an average of 5.4 ms appears difficult to realize. However, many applications don't need CD sample rates. For example, my application is analysis of traffic noise which only needs a max freq of 5000 Hz (10k sample rate). With a 10k sample rate, design constraint #1 relaxes to 25.6 ms -- in my early tests this seems doable with usasyncio. But... still some coros to add, so this optimism might be a bit early.
My repo includes an example uPy file showing how audio samples are read from an I2S microphone, then written to a WAV file on an external SD Card. In this example 32 DMA buffers are used at 128 bytes/buffer: I2S example
What do you think? Based on your expert knowledge of usasyncio is there any hope for me to keep going with usayncio?