How to play back synthesized 22kHz audio in a glitch-free manner?

Hi,

I authored a port of ScummVM over to Emscripten, and am currently
implementing Web Audio API backend support to the Emscripten SDL audio API,
to allow other browsers than Firefox to be able to play back audio as well.

Existing SDL C/C++ applications utilize SDL audio in various sampling
rates, and for example, ScummVM synthesizes 22kHz audio, whereas Chrome and
Firefox both use 48kHz natively in the Web Audio graph.

The way SDL works is that the C/C++ code code synthesizes the audio in
small buffers of 1024-8192 samples (per sound channel) at a time.

I hit an issue trying to manage the playback of these generated audio data.

Currently I create an AudioBufferSourceNode for each buffer, fill it, and
schedule it to be played back in appropriate time with
AudioBufferSourceNode.start(when) call. This is problematic and produces
glitching audio in both Firefox nightly and Chrome:

http://clb.demon.fi/html5scummvm/tests2/monkey_webaudio_only.html (don't
navigate to other pages from this page, they are _not_ test cases)
https://dl.dropboxusercontent.com/u/40949268/emcc/bugs/webaudio_only_sdl_beep.html

As a reference, try this in Firefox, which uses the Mozilla Audio Data API
on Firefox (and Web Audio on Chrome):
https://dl.dropboxusercontent.com/u/40949268/emcc/bugs/mozaudiodata_webaudio_sdl_beep.html

In the above code, you can read the relevant source code by looking for a
text string 'function _SDL_OpenAudio', which is a hand-written function -
you can safely ignore the emscripten code.

Alternatively, I tried using ScriptProcessorNode to schedule audio playback:

http://clb.demon.fi/html5scummvm/tests2/monkey_webaudio_only_scriptprocessornode.html


This produces glitch-free audio, but plays back with the wrong sampling
rate. I have tried to find how to change the device playback rate, but it
looks like this is not supported, am I correct?

The question is: Since AudioBufferSourceNode.start(when) cannot guarantee
sample-precise stitching of back-to-back buffers, and with the
ScriptProcessorNode the playback rates don't match, how should I resolve
this issue? I would prefer _not_ to have to write my own resampler just to
go around an API limitation. Someone suggested using a secondary offline
graph to resample, but that sounds like a complicated approach.

I was more than slightly surprised to see that there is no 'buffer
queueing' functionality in the Web Audio API. Other common sound APIs have
this (see etc. OpenAL, XAudio2, DirectSound), and it allows applications to
easily play back audio buffers while guaranteeing glitch-free stitching,
and also allows filling with variable-sized buffers, e.g. in case of
adaptive rate decoding audio. ScriptProcessorNode forces to a callback
approach and limits to fixed buffer sizes. In addition to Emscripten SDL
API (and other audio APIs on that platform), VOIP and multimedia playback
applications commonly utilize this functionality. Is there a specific
rationale why buffer queueing functionality, e.g.

   void AudioBufferSourceNode.startImmediatelyAfter(AudioBufferSourceNode
predecessor);

was left out from the spec?

Thanks!
   Jukka

Received on Monday, 17 June 2013 12:42:10 UTC