Audio artifact when locking the device while a game is paused

I noticed that a game’s audio resumes to run for probably a single tick when locking the device while the game is paused. This leads to a tiny audio artifact and can cause an unpleasant audio pop depending on volume and currently played music or sfx.

To reproduce: Pause a game with active audio output, lock device in pause screen. Loud volume obviously emphasizes the effect.

I've been banging my head on the same problem for a while, and my first workaround was to stop all synths (I'm using synths to generate sound effects) from playing when the console got into pause/lock and let them get triggered by the game again later:

function playdate.deviceWillLock()
   for k, v in pairs(playdate.sound.playingSources()) do
      v:stop()
   end
end

However, once I started using instruments for music, this stopped working because instruments don't have a stop method.

My next idea was to set all volumes to 0, because synths, sampleplayers and instruments all have a setVolume method.
This kinda worked but was finicky because it was then needed to manually reset those volumes back to 1 again, but not immediately at playdate.gameWillResume as it would re-introduce the artefacts. And thus it wasn't possible to rely on playdate.sound.playingSources() for restarting them as sounds could have gone past their release phase in the meanwhile, so you'd have to keep track yourself of all potential playing sources. And then simply setting volumes up and down could generate artefacts…

So now I've got this workaround, which works much better even though it's not very pretty:

function playdate.deviceWillLock()
   for k, v in pairs(playdate.sound.playingSources()) do
      if string.find(tostring(v), "synth", 1, true) or string.find(tostring(v), "sampleplayer", 1, true) then v:stop() end
      if string.find(tostring(v), "instrument", 1, true) then v:setVolume(0) end
   end
   music.sequence:stop()
end

All synths and sampleplayers get stopped and get triggered again by the game as needed after resuming.

The music instruments get muted, before pausing the music sequence with stop (if the music got stopped before muting you'd still get the release phase of those instruments and as they would not be considered playing anymore they wouldn't get muted).
The music has to be restarted and unmuted manually but you can then chose exactly when to do so.

music.sequence:play()
for k, v in pairs(playdate.sound.playingSources()) do
   v:setVolume(1)
end

So… yeah it's a bit hackish, and you'd probably have to tweak it some more for fileplayers as even though they have a stop method it'd be preferable to call the pause one, but at least it works on getting rid of the artifacts.

It's a bit of a pain to have to deal with this manually, but the trade off is that you then have full control over how you want sound to resume.
For example in RollerBlade I have a ticking timer giving players a second or two before actually resuming gameplay after a pause/lock, and the music only gets started back when actual gameplay resumes.
You could also have the volume gradually ramp up to 1, a lowpass sweep up, or anything that softens resuming sound.