How to fix “cracking” audio when using Audio Worklets?

1.7k Views Asked by At

I’m trying to understand how AudioWorklet is working and made some tests.

So far, I have a huge “cracking” problem when I let the browser play the sound in the background and do something else (e.g. opening a CPU-heavy application like Photoshop or VSCode and move the window around).

At first I thought it was a hardware problem. I upgraded to Catalina, removed any system audio extension I found, but it’s the same on Android, and some other friends’ computers (Mac, PC).

I’m using Version 1.0.1 Chromium: 78.0.3904.108 (Official Build) (64-bit) myself.

This YouTube video demonstrates the cracking audio issue.

I made two CodePen demos you can test here:

Web Audio Cracks (Vanila + no Worklet):

const ctx = new(window.AudioContext || window.webkitAudioContext)();
const request = new XMLHttpRequest();
const gainNode = ctx.createGain();
const sourceNode = ctx.createBufferSource();

request.open('GET', 'https://poppublic.s3.amazonaws.com/other/2.mp3', true);
request.responseType = 'arraybuffer';
request.onload = () => {
  ctx.decodeAudioData(request.response, buffer => {
    sourceNode.buffer = buffer;
    console.log(sourceNode.buffer.sampleRate);
  });
};
request.onerror = function(e) {
  console.log('HTTP error', e);
};
request.send();
play = () => {
  sourceNode.connect(gainNode);
  gainNode.connect(ctx.destination);
  sourceNode.start(0);
}
stop = () => {
  sourceNode.stop(0);
}
<button onClick="play()">Play</button>
<button onClick="stop()">Stop</button>

Web Audio Cracks (Vanila + Worklet):

const ctx = new(window.AudioContext || window.webkitAudioContext)();
const request = new XMLHttpRequest();
let gainNode = null;
let sourceNode = null;
let buffer = null;
let worklet = null;

try {
  const script = 'https://poppublic.s3.amazonaws.com/other/worklet/processor.js';
  
  ctx.audioWorklet.addModule(script).then(() => {
    worklet = new AudioWorkletNode(ctx, 'popscord-processor')
    request.open('GET', 'https://poppublic.s3.amazonaws.com/other/2.mp3', true);
    request.responseType = 'arraybuffer';
    request.onload = () => {
      ctx.decodeAudioData(request.response, buff => {
        buffer = buff;
        console.log(buff.sampleRate);
      });
    };
    request.onerror = function(e) {
      console.log('HTTP error', e);
    };
    request.send();
  });
} catch (e) {
  this.setState({
    moduleLoaded: false
  });
  console.log('Failed to load module', e);
}
play = () => {
  stop();
  gainNode = ctx.createGain();
  sourceNode = ctx.createBufferSource();
  sourceNode.buffer = buffer;
  sourceNode.connect(gainNode);
  gainNode.connect(ctx.destination);
  sourceNode.start(0);
}
stop = () => {
  try {
    sourceNode.disconnect();
    gainNode.disconnect();
    sourceNode.stop(0);
  } catch (e) {
    console.log(e.message)
  }
}
<button onClick="play()">Play</button>
<button onClick="stop()">Stop</button>

The piano MP3 you’ll hear is a 48000Hz / 32bits / 320kb audio recorded in studio.

Before filing any bugs, I need to make sure my code is correct. Maybe I’m not chaining the things the way it should.

1

There are 1 best solutions below

1
On

When using a worklet, the default priority of the audio thread is normal. This is not good for audio as you've seen by running webaudio and then moving a window around.

What you can do is go to chrome://flags, search for worklet and enable the flag named "Use realtime priority thread for Audio Worklet". This should help on mac and windows. I don't know if it will make a difference on Android.

If you are hearing cracks with WebAudio without a worklet, as you do in your first codepen example, then that's unexpected and you really should file an issue on that.