Web Audio API Convolver Node

Web Audio API Convolver Node

Web Audio API Convolver Node is a tutorial that shows how to use the convolver node to apply a linear convolution effect with an impulse response. You should understand the basics of the Web Audio API as well as how to buffer and play audio files. If not, please refer to the previous tutorials. Once you’re up to speed, it’ll be even easier for you to follow along as I show how to make a custom reverb effect.

Web Audio API Convolver Node

What is Convolution?

According to the spec,

Convolution is a mathematical process which can be applied to an audio signal to achieve many interesting high-quality linear effects. Very often, the effect is used to simulate an acoustic space such as a concert hall, cathedral, or outdoor amphitheater. It can also be used for complex filter effects, like a muffled sound coming from inside a closet, sound underwater, sound coming through a telephone, or playing through a vintage speaker cabinet. This technique is very commonly used in major motion picture and music production and is considered to be extremely versatile and of high quality.

Each unique effect is defined by an impulse response. An impulse response can be represented as an audio file and can be recorded from a real acoustic space such as a cave, or can be synthetically generated through a great variety of techniques.

Let’s Make Reverb

The Web Audio API Convolver Node is our gateway to many high quality reverb effects. Now that we’re beginning to understand the vast possibilities of the Web Audio API Convolver Node, we can put it to work. Let’s make reverb in the browser! For this experiment, imagine yourself being somehow magically transported to the Falkland Palace Royal tennis court while listening to your music. For the full effect, we’ll need an interface, some variables, some functions, and a little imagination.

Create the User Interface

First, create the User Interface in HTML with two button elements, a range input and a text span. To really hear this effect in action, we must be able to adjust the level of effect in relationship to the overall signal. We want to hear the original source audio first. This becomes the baseline for comparison. Then the convolution level can be manipulated for the desired effect.

The play button’s class is play. The stop button’s class is stop. The input’s class is convolver-gain-control, type is range, min is 0, max is 1, step is 0.05, and value is 0. The span’s class is convolver-gain-value and contains the text 0.

Declare and Define Variables

The rest is javaScript. We’ll start by creating an audio context the smart way. It’s smart because it falls back to the webkit-prefixed version for browsers that still require a vendor prefix.

var context = new (window.AudioContext || window.webkitAudioContext)();

Then we create our other nodes, including an audio buffer source, a convolver, a compressor, and two gain nodes.

var source;
var convolverGain = context.createGain();
var convolver = context.createConvolver();
var masterGain = context.createGain();
var masterCompression = context.createDynamicsCompressor();

Then we declare and define our other variables. The location of our impulse response and source audio are defined by impulseUrl and sourceUrl. Change the path and filename of both to match yours and you are ready to rock!

Bind the play and stop buttons to the play and stop functions that we’ll be creating soon. Also, initially disable the stop button.

We need to hear the source audio with the impulse response and without, so we’ll need a slider to adjust the gain level of the wet signal. Let’s throw in a numerical readout of the current level to make it more informative. Bind the slider to the convolverGainControl variable. Bind the readout to the convolverGainValue variable. Also, initially disable the slider.

var impulseUrl = 'sounds/impulse_response.wav';
var sourceUrl = 'sounds/source_audio.wav';
	
var play = document.querySelector('.play');
var stop = document.querySelector('.stop');
stop.setAttribute('disabled', 'disabled');

var convolverGainControl = document.querySelector('.convolver-gain-control');
var convolverGainValue = document.querySelector('.convolver-gain-value');
convolverGainControl.setAttribute('disabled', 'disabled');

Make it Work

Next we create a function to load the source audio into a buffer and set it to loop. We connect the source to the convolver gain and the master gain. Connect the master gain to the master compressor. Connect the master compressor to the speakers.

Call the getImpulse function to load the impulse response. Reset the effect level, the slider position, and the numerical readout to zero each time the getSource function is called.

function getSource() {
  source = context.createBufferSource();
  request = new XMLHttpRequest();
  request.open('GET', sourceUrl, true);
  request.responseType = 'arraybuffer';

  request.onload = function() {
    var audioData = request.response;

    context.decodeAudioData(audioData, function(buffer) {
        myBuffer = buffer;
        source.buffer = myBuffer;
        source.loop = true;
        source.connect(convolverGain);
        source.connect(masterGain);
		masterGain.connect(masterCompression);
		masterCompression.connect(context.destination);
      
      },

      function(e){"Error with decoding audio data" + e.err});

  }

  request.send();
  
  getImpulse();

  document.querySelector('.convolver-gain-control').value = '0';
  document.querySelector('.convolver-gain-control').addEventListener('change', function() {
	convolverGainControl.value = this.value;
  });
  
}

When the getImpulse function is called, we load the impulse response into an audio buffer. This buffer is passed to the convolver node. The convolver is set to perform a scaled RMS-power analysis of the audio data in buffer to calculate a normalizationScale. The convolver gain is set to zero. The convolver gain is connected to the convolver. The convolver is connected to the master gain.

function getImpulse() {
  convolver = context.createConvolver(); 
  ajaxRequest = new XMLHttpRequest();
  ajaxRequest.open('GET', impulseUrl, true);
  ajaxRequest.responseType = 'arraybuffer';

  ajaxRequest.onload = function() {
    var impulseData = ajaxRequest.response;

    context.decodeAudioData(impulseData, function(buffer) {
        myImpulseBuffer = buffer;
        convolver.buffer = myImpulseBuffer;
        convolver.loop = true;
	convolver.normalize = true;
        convolverGain.gain.value = 0;
        convolverGain.connect(convolver);
        convolver.connect(masterGain);
      },

      function(e){"Error with decoding audio data" + e.err});

  }

  ajaxRequest.send();
}

To make it usable, we need functions to control our user interface. We also need some extra logic to prevent unwanted behavior.

When the page first loads, the stop button and slider are disabled. However, when the play button is clicked, the play button becomes deactivated to prevent multiple occurrences of the source audio from loading, the convolver is disconnected to prevent multiple occurrences of the impulse response, the stop button becomes active, and the slider becomes active to allow manipulation of the convolver gain. The getSource function is called and the source is set to begin immediately.

When the stop button is clicked, the source is set to stop immediately. Also, the stop button and slider are both deactivated while the play button is reactivated.

Finally, we need a function to make our slider and numerical readout on the page react to whatever the convolver gain is set to. When the audio source is stopped and restarted, the convolver gain, slider, and readout are all reset to zero.

play.onclick = function() {
  convolver.disconnect();
  getSource();
  source.start(0);
  play.setAttribute('disabled', 'disabled');
  convolverGainControl.removeAttribute('disabled');
  stop.removeAttribute('disabled');
  convolverGainValue.innerHTML = 0;
}

stop.onclick = function() {
  source.stop(0);
  play.removeAttribute('disabled');
  convolverGainControl.setAttribute('disabled', 'disabled');
  stop.setAttribute('disabled', 'disabled');
}

convolverGainControl.oninput = function() {
  convolverGain.gain.value = convolverGainControl.value;
  convolverGainValue.innerHTML = convolverGainControl.value;
}

Web Audio API Convolver Node

I’ve shown you how to create a reverb effect by loading and playing source audio through a Web Audio API Convolver Node. Maybe rocking beats on the royal tennis court of a distant island isn’t your idea of a good time, but hopefully it got you thinking more about spacial awareness and convolution of sound.

Now your only limitation is your access to quality impulse responses. Can you think of any other ways to use this fantastic audio node? Please share if you do. Be creative! I’d love to hear what you’ve done with it. How will you use the convolver node to make the web more spacial?

Other tutorials in this series

More examples of the Web Audio API in action


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *