My SNES SPC player is built on top of WebAssembly + AudioWorklet. It works well with Chrome/Firefox on my desktop/laptop but produces audio glitches with Chrome on my phone (Pixel 4a). An interesting thing is that the player doesn’t produce audio glitches when it runs with Firefox on my phone.
I had a guess. I didn’t think the cause of glitches was the player was too slow to generate samples. The player’s CPU usage was quite low on desktop/laptop (<1% according to Windows Task Manager). I thought that there might be a task scheduling problem in Chrome for Android.
I used chrome://inspect/#devices to see if my guess was right.
First, let’s take a look at how much time it took to generate audio samples.
It took <1ms for each AudioWorkletProcessor#process() call. AudioWorkletProcessor#process() asks 128 samples per call (As of Feb 2021). Since the S-SMP generates a sample at 32kHz, the AudioContext’s sampleRate was set up 32000 for the player. Generating 128 samples within 1ms seems to be fast enough (128 / 32000 = 0.04 = 4ms is the hard deadline).
Zoom out to get an overview of what’s going on.
“Idle” is dominant. I couldn’t think of my player’s bad behavior to be blocked, as it doesn’t allocate any memory and doesn’t perform any IO. These findings support my guess – there seems to be a problem in the audio scheduler of Chrome for Android.
The next step is to find a solution. I searched on crbug.com to see if anyone else had the same issue. I found https://crbug.com/1014614. It looks like a similar issue but it’s the opposite.
My interpretation of the discussion is: On a low-latency path, the audio task scheduler posts tasks to generate samples more frequently and each task is asked to generate less samples. For my player the behavior doesn’t seem to work well as intended.
Then how do I get my problem fixed? The crbug.com issue mentioned latencyHint. My player doesn’t require interaction so increasing latency seems a good compromise. Setting a large
latencyHint like 0.05 solves the problem. I don’t hear audio glitches anymore, yay!
I hope that I can get rid of the workaround in the future.