I noticed a few people (myself included) were running into issues trying to create seamless audio loops when exporting to the recommended IMA ADPCM (WAV) format.
"Because ADPCM uses sample blocks that are aligned one after the other, a wave compressed with ADPCM may have an unfinished, partial block at its end. The ADPCM decoder generates silence for the remainder of this partial block, which keeps the wave from looping seamlessly.”
I was noticing an unexpected pop when my audio track looped and had mistakenly assumed it was a hardware limitation of the Playdate, but it turns out it's part of the ADPCM spec.
I noticed this with both Audacity (3.1.3) and ffmpeg exports on Windows. Apparently this doesn't happen using WavePad to export but I didn't confirm.
An ugly workaround is something like this:
local music = playdate.sound.fileplayer.new("some_music_track")
music:setLoopRange(0, music:getLength() - 0.05) -- adjust offset per track
But I think the docs would benefit from a short note about this in the section where Audacity and ffmpeg are mentioned.
Interesting, I'll give this a shot. So far I've noticed the results to be different per audio file I'm encoding (which makes sense but just makes troubleshooting slightly trickier).
Alright, I gave ADPCM-XQ a quick test and it does fare better, but I still notice some silence at the end compared to the source file.
The top row is the original (mp3 in this case), middle row is what Audacity exports using IMA ADPCM, and the bottom row is the XQ conversion from Github (with default flags).
Yeah, they will be totally different. This does an optimised encoding by looking ahead.
We've used it for music in Sparrow Solitaire, and even when we loop we don't notice the small silence.
Thanks for doing this comparison.
I use Fission audio editor on macOS and one of the great things about that is that it edits by block. But, it's still not possible to remove all silence as it's unlikely the music/loop will align exactly on block boundaries. Would love to find out a way to do this!
It might be tricky to remove at the SDK level, eg if someone intentionally wanted silence at the end of a track (I guess could pass a flag?), but at least raising awareness that ADPCM works on a block-based system and can lead to unexpected silence would be nice. Even though it's not Panic's responsibility since it's not like they invented ADPCM, but still.
So I’m right in the middle of all this as I ran into the same issues trying to make a loop for Atlantic ‘41.
One thing I have discovered so far is that using Audacity I always get a gap at the end at export, and can never get a seamless loop.
However using Oceanaudio, I was able to export perfect loops. In fact I even did one experiment and took the same 16 bit PCM wav file that was looping seamlessly, exported it to ADPCM from Audacity and from Ocean. Audacity didn’t loop, but Ocean did.
So I’m not sure if this is bulletproof but so far it seems that OceanAudio is the solution to this issue.
Quick note as I dug a little deeper. There’s 2 separate issues I think.
On one hand, the limitations of ADPCM file format, which, due to reasons already mentioned, is not loop friendly.
On the other hand, Audacity is just bad at exporting ADPCM. For some reason, that software always exports a significant added silence at the end of these files. I couldn’t find a way around this. So my conclusion is to avoid it altogether for export.
For the first issue, Ocenaudio seems to do the best job at exporting as closely a perfect file as possible as the limitations allow. But it’s not a sure shot. Basically it comes down to luck. You could get an almost perfect loop or a big pop, or anything in between. The only way I could find is to make tiny adjustments to the length of the file and re export. It’s far from ideal but that’s my solution so far. If someone can make sense of it, I’d love to hear it.
Finally there’s one aspect that makes looping these files difficult. On Mac, softwares like Audacity and Ocenaudio seem to have this real time cross fading loop, that makes loops always perfect when playing them inside the software, even if the sample doesn’t loop right.
However WavePad doesn’t do this, so what you hear is exactly what you’ll get in the Playdate.
So my workflow is this:
Create and mix and do all the cross fading shenanigans in Audacity. Export as wav float 32 bit.
Import the sample in Ocenaudio and export the ADPCM.
Listen to it in WavePad. If not right, go back to 1 or 2, create a slightly shorter or longer loop (a few milliseconds could be enough). Rince and repeat.
I feel like this is a very lame workflow, and there has to be a better way to create perfect loops for the Playdate. Please share if you found it.
So I’m happy to report that I’ve tried 2 different loops and both came out perfect using adpcm-xq.
Both my samples loop seamlessly and I can’t even hear a difference in quality with my working files wav float 32 bits. I listen with decent audio production headphones but I’m not a sound professional, so expert ears may be less forgiving.
Note that the script exports at 22050 sample rate, which sounds so close to 44 kHz on the Playdate that I don’t think it’s worth doubling the file size, at least for sound effects. But I must say in all fairness that I was making ocean sound loops, which are noise heavy by nature, so they may be more forgiving than, let’s say, ambient music. I’ll keep reporting here when I get to music files.
For anyone interested to hear the result, I’ll post sound clips in my next devlog. Thanks Matt for finding this life saver.
Yes the documentation could benefit from information about this. It’s the kind of frustrating issue that can be hard to figure with an internet search, and that few people seem to know about.
In fact I think that the documentation in general could do with more examples and also practical advice and tips to avoid known pitfalls. For instance I wish someone had told me about the utility of drawing modes, or pushcontext for pre caching, or explain better how sprites and bitmaps can co exist.
Thank you. I’m sure that many beginners will enjoy the experience of developing on the Playdate even more with simple, practical tips. It’s such a unique platform in its accessibility that it would be a shame not to empathize that fact.
All this information is most likely already nicely written out in some devlog or another that the author would probably let you quote for free.
adpcm is not great for gapless looping playback, as it's files have a duration that is a multiple of a block size. By micro-adjusting the duration of the source wav file, you might get closer to that multiple.