I have a typescript class that can playback 8-bit, 11025 Hz, mono PCM streams. When one sound is playing and the second one is started, it garbles up all playback until only one sound is being played again. There is one AudioContext
and every Sound-class creates their own buffers and source nodes when playback starts.
Simple pseudo example of my set-up:
const context = new AudioContext();
const data = new Float32Array(); // <- Imagine this being filled with data.
function play() {
const buffer = context.createBuffer(1, data.length, 11025);
buffer.copyToChannel(buffer, 0);
const srcNode = context.createBufferSource();
srcNode.buffer = buffer;
srcNode.connect(context.destination);
srcNode.start();
}
Imagine the audio being played is 2 seconds long. When invoking play()
for the first time, it just plays fine. If you wait until it finished playing and invoke it a second time, it plays just fine.
The issues start when invoking play()
while a previously started AudioSourceNode is still playing. It almost looks like both sources are fighting for the same resources.
I've tried putting gain and panning nodes in between the source nodes and context, but nothing seems to help. Creating a multitude of AudioContext
objects shouldn't be a solution either due to performance/resource limitations.
Why can't two (different) sounds being played back at the same time?
I'm completely clueless why this is a problem. Thanks in advance!
#edit1: It seems like when using a gainNode
and setting its value to 0.5
, I can play two sounds simultaneously without playing becoming garbled. It seems that audio is being added on top of currently playing audio data. How do we mix this properly?
I figured it out. For anyone having similar issues: The answer is using a DynamicsCompressorNode
and use that to route all audio source nodes to.
const context = new AudioContext();
const compressorNode = context.createDynamicsCompressor();
compressorNode.connect(context.destination); // <- Connect to AudioContext
function play() {
const buffer = context.createBuffer(1, data.length, 11025);
buffer.copyToChannel(buffer, 0);
const srcNode = context.createBufferSource();
srcNode.buffer = buffer;
srcNode.connect(compressor); // <- Connect to DynamicsCompressorNode
srcNode.start();
}