Add note to docs regarding limitations of IMA ADPCM (WAV) format for seamless audio looping

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.

4 Likes

Interested if you have different results with the adpcm-xq encoder.

Related:

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).

It does seem to vary heavily based on the input audio file though, so perhaps most people won't run into this issue.

XQ probably does provide the best quality though, at least from the readme it sounds like a great conversion tool.

1 Like

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!

1 Like

Also, I wonder if the audio engine on Playdate removes the silence on looping/playback?

That could be the best way to negate all 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. :slight_smile:

1 Like

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.

1 Like

Good tip! Can I confirm you mean ocenaudio

Ah yes indeed. That’s the one. I guess working on a submarine game affects me more than I thought :slight_smile:

1 Like

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:

  1. Create and mix and do all the cross fading shenanigans in Audacity. Export as wav float 32 bit.
  2. Import the sample in Ocenaudio and export the ADPCM.
  3. 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.

1 Like

@StephanRewind I'd be interested in your results working only with wav pcm for exact loops, and converting to adpcm using adpcm-xq

this is what I do and I've not noticed any pops or clicks, but I no longer have perfect hearing.

Yes that’s a good idea. I did convert a few samples with your script but it was before I was working on loops. I’ll try and report back here soon.

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.

2 Likes

I learned of adpcm-xq from the Direct Drive team. So props to them!

We're lucky that it will generate perfect looping adpcm.

It would be great to get these loop findings in the docs.

2 Likes

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.

I'll file an issue to enhance our documentation with this information. Thank you for the investigation!

(@StephanRewind your other points are valid too, thanks for the feedback!)

3 Likes

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.

I'll summarize for people finding this now:

  • 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.
  • the best chance to get an adpcm output that aligns to the correct length seems to be GitHub - dbry/adpcm-xq: Xtreme Quality IMA-ADPCM Encoder / Decoder . It has to be compiled from source
  • For the convenience of Mac users that tool was wrapped into a Desktop app: ADPCM encoder tool (Mac-only)
2 Likes

Pre-built adpcm-xq binaries are on its release tab. No need to build from source!

https://github.com/dbry/adpcm-xq/releases

My process:

  1. convert audio to WAV
    (don't work on MP3 or ADPCM directly)
  2. make loop, still as WAV
    (make sure it's seamless in multiple editors/players; you might also like to try PyMusicLooper to extract loops)
  3. generate ADPCM version using adpcm-xq
    (I use high lookahead so they take a few seconds to process)
  4. you're done
    (audio will be a perfect loop on Playdate)
4 Likes