Just gonna vent here a bit XD
Took me Waaaay too long yesterday to get the filestreaming from fat32 completely working (was wise enough to write a proper test so i could find any and all errors, and boy, i found them all XD). Somewhere in the future i want to come back to this as i finally have a good view on what's required to stream multiple audio channels from a sd card and be able to mix them
. (my first attempt was way simpler: under interrupt fetch sample per sample from the buffer and try to mix them, but it's sloooooow....)
For those interested, my current system (let me know if you know of better ways):
Per file i stream, i've got the following buffers:
the reasons for these buffers:
- buffer 1 & 2 are just standard double buffering, nothing special
- the filestart buffer, is for supporting looping the audio. The last buffer i fill can literally contain 1 sample, so if i don't have the start data ready, i'm screwed.
-Buffer1StartRepeated: The only way i can get good performance, is if i can just take a pointer to a block of samples, and can just use them, not having to switch to another buffer midway or so. Since i sometimes get offsets (file header, file not repeating in exact multiples of the blocksize i'm streaming in, ...), i can want the data at the start of buffer 1 to be behind buffer2 so i can just give a pointer near the end of buffer2 and know the start of buffer 1 is also after it in memory.
Currently the above buffers are all 4K in size (this is how much data i request per call to the sd card), except for the repeated part which is as large as the blocks i request from this streaming system(this is half the size of the I2S buffer, currently 256bytes).
How it works:
Upon loading a file, you load the startbuffer & buffer 1 (and technically should fill buffer1start repeat to, but i won't have such short files and i'm being lazy :p ).
The I2S port is in looping dma with half transfer & tranfer complete interrupts on.
On each of the above interrupts, you read a block from the files, and can then know it is contiguous sound data, no catches, so just add the data together according to volume of each channel, and copy it to the I2S buffer.
As you move from one sd card block to the next, as in standard double buffering, the other buffer gets loaded. If buffer 1 gets loaded, after its load it also triggers a memory 2 memory dma to fill in the repeated buffer part.
As the last block of a file is loaded, after its end a part of the startbuffer is copied behind it, so you can loop the file seamlessly (if you don't want to loop the file, and empty buffer could also be copied after it to make sure it only returns 0).
That's the basic operation.
Everything is running on DMA (I2S is in a DMA loop, for SDIO i created a queue of DMA calls so whenever a new chunk if data is needed, the system adds a call to be done to the queue, the memory copies for the repeat of buffer 1 & the copying of start data is a memory2memory dma call (should also make a queue for that)).
besides the above high level stuff, there are also nasty things such as offsets compared to the buffer you develop and have to take into account, which really gave me some frustrations to get it completely running without errors ^^'.
Currently the code is a bit atrocious ^^', but at least it's working XD. It evolved trough a lot of getting stuck at points, noticing what tanks the performance (basically anything you have to do per sample. If i work in blocks of 128 samples, any extra instruction there is just killing the performance. I now went for a solution where i could just take the data, know it's consecutive audio data, and not have to check anything at all, just read the buffer and be done with it XD.)
I can now successfully stream four 16 bit stereo 48khz audio files simultenously without issues
. goal accomplished ^^.