Hey h4yn0nnym0u5e,
i am not sure how fast i can come with an demo code.
I don't think it is that much helpful but hope to get you more involved somehow:_) first half of the video is Nics Library playing from flash. Second half is with your current code state for SD card.
Dont't turn volume up to much since there will be nasty artefacts. No question, it is not better with the default of Nics Library, however the problem might to be our own issues, not the library - im kind of clueluss where to look for the problem, first.
https://youtu.be/yz_a2vfgIeM
You don't have to be fast, I think there are quite a few things I need to do before it's worth doing a proper test with real-life applications!
One thing you could perhaps try fairly easily is to change the buffer sizes and count. In src/ResamplingSdReader.h you'll find these lines:
Code:
#define RESAMPLE_BUFFER_SAMPLE_SIZE 512
#define RESAMPLE_BUFFER_COUNT 7
RESAMPLE_BUFFER_SAMPLE_SIZE is in samples, so this says a buffer is 512 samples long (1024 bytes), hence it'll last for 4 audio updates assuming a mono WAV file - it'll only last for 2 updates if you're using stereo. It will then be reloaded in the foreground / sketch / EventResponder code, and given there are RESAMPLE_BUFFER_COUNT = 7 buffers, that could take up to (7-1)*4 = 24 updates, or about 96ms, before the playback is starved of audio data. That's fine for a single object and a decent SD card, but obviously gets more marginal as the number of playback objects is increased. Also, if your code doesn't yield() quite often, that prevents EventResponder from doing the pending reloads. And, big reads are more efficient than little ones, so using 1024-byte buffers is a bit on the small side.
So, try increasing RESAMPLE_BUFFER_SAMPLE_SIZE from 512 to 2048 or more (must be a power of 2), and possibly increase RESAMPLE_BUFFER_COUNT as well. You should also have a good hard look at your loop(), and instrument it to see how often it exits, assuming you don't already know. yield() is called on loop() exit, and also on delay() calls, but using the latter (except on super-rare occasions) should be an absolute no-no in a real-time application like this. If your loop() has multiple time-consuming operations, either use a state machine to run each one in turn on separate loop() iterations, or put a yield() call between the long operations. "Long" is anything over, say, a quarter of the total buffer duration
divided by the number of playback objects. So if you have 8 objects playing, each with a 240ms buffer, operations can ideally take no more than 7.5ms before yield() needs to be called again.
For testing, please keep it super simple, at least to start with. It's lovely to have a nice tune, but it's pretty much hopeless for early debugging! Can you play a single sample at 1x? Two samples at once? Four? Different playback speeds? Does it help to turn the display or web page off? When it breaks, record that to a WAV file, along with the same thing from flash (presumably not broken), and zip and attach it here. Boring sine waves are excellent samples, or perhaps a sweep, so it's super-easy to track where samples have gone missing or got duplicated.