Friday, April 8, 2016

HTML5-Audio- The most useful filter nodes

The most useful filter nodes

GAIN NODE

Useful for setting volume....
Definition: "The GainNode interface represents a change in volume. It is an AudioNode audio-processing module that causes a given gain to be applied to the input data before its propagation to the output. A GainNode always has exactly one input and one output, both with the same number of channels."
Example at JSBin, or try it in your browser:
 
 
Source code extract:
  1. /* Gain Node */
  2.  
  3. var gainExample = document.querySelector('#gainExample');
  4. var gainSlider = document.querySelector('#gainSlider');
  5.  
  6. var gainMediaElementSource =audioContext.createMediaElementSource(gainExample);
  7. var gainNode = audioContext.createGain();
  8.  
  9. gainMediaElementSource.connect(gainNode);
  10. gainNode.connect(audioContext.destination);
  11.  
  12. gainSlider.oninput = function(evt){
  13.    gainNode.gain.value = evt.target.value;
  14. };
The gain property (line 13 in the above code) corresponds to the multiplication we apply to the input signal volume. A value of 1 will keep the volume unchanged. A value < 1 will lower the volume (0 will mute the signal), and a value > 1 will increase the global volume, with a risk of clipping. With gain values > 1, we usually add a compressor node to the signal chain to prevent clipping. You will see an example of this when we discuss the compressor node.

STEREO PANNER

Definition: "The StereoPannerNode interface of the Web Audio API represents a simple stereo panner node that can be used to pan an audio stream left or right. The pan property takes a value between -1 (full left pan) and 1 (full right pan)."
Example at JSBin, or try it in your browser:
 
 
Source code extract:
  1. // the audio element
  2. playerPanner = document.querySelector('#pannerPlayer');
  3. pannerSlider = document.querySelector('#pannerSlider');
  4.  
  5. // create nodes
  6. var source = audioContext.createMediaElementSource(playerPanner);
  7. pannerNode = audioContext.createStereoPanner();
  8. // connect nodes together
  9. source.connect(pannerNode);
  10. pannerNode.connect(audioContext.destination);
  11.  
  12. // input listener on the gain slider
  13. pannerSlider.oninput = function(evt){
  14.   pannerNode.pan.value = evt.target.value;
  15. };

BIQUAD FILTER

Definition: "The BiquadFilterNode interface represents a simple low-order filter, and is created using theAudioContext.createBiquadFilter() method. It is an AudioNode that can represent different kinds of filters, tone control devices, and graphic equalizers. A BiquadFilterNode always has exactly one input and one output."
Example at JSBin, or try it in your browser, with a lowpass filter, only the frequency slider will have a noticeable effect:
 
      
The most useful slider is the frequency slider (that changes the frequency value property of the node). The meaning of the different properties (frequencydetune and Q) differs depending on the type of the filter you use (click on the dropdown menu to see the different types available). Look at this documentation for details on the different filters and how the frequencydetune and Q properties are used with each of these filter types.
Here is a nice graphic application that shows the frequency responses to the various filters, you can choose the type of filters and play with the different property values using sliders:
Frequency responses for various filters. Screenshot of a nice application that visualizes that
Multiple filters are often used together. We will make a multi band equalizer in a next lesson, and use six filters with type=peaking
Source code extract:
  1. var ctx = window.AudioContext || window.webkitAudioContext;
  2. var audioContext = new ctx();
  3.  
  4. /* BiquadFilterNode */
  5.  
  6. var biquadExample = document.querySelector('#biquadExample');
  7. var biquadFilterFrequencySlider =
  8.       document.querySelector('#biquadFilterFrequencySlider');
  9. var biquadFilterDetuneSlider =
  10.       document.querySelector('#biquadFilterDetuneSlider');
  11. var biquadFilterQSlider =
  12.       document.querySelector('#biquadFilterQSlider');
  13. var biquadFilterTypeSelector =
  14.       document.querySelector('#biquadFilterTypeSelector');
  15.  
  16. var biquadExampleMediaElementSource =
  17.       audioContext.createMediaElementSource(biquadExample);
  18.  
  19. var filterNode = audioContext.createBiquadFilter();
  20.  
  21. biquadExampleMediaElementSource.connect(filterNode);
  22.  
  23. filterNode.connect(audioContext.destination);
  24.  
  25. biquadFilterFrequencySlider.oninput = function(evt){
  26.    filterNode.frequency.value = parseFloat(evt.target.value);
  27. };
  28.  
  29. biquadFilterDetuneSlider.oninput = function(evt){
  30.    filterNode.detune.value = parseFloat(evt.target.value);
  31. };
  32.  
  33. biquadFilterQSlider.oninput = function(evt){
  34.    filterNode.Q.value = parseFloat(evt.target.value);
  35. };
  36.  
  37.  
  38. biquadFilterTypeSelector.onchange = function(evt){
  39.     filterNode.type = evt.target.value;
  40. };

CONVOLVER NODE: USEFUL FOR CONVOLUTION EFFECTS SUCH AS REVERBERATION

Definition: "The ConvolverNode interface is an AudioNode that performs a Linear Convolution on a given AudioBuffer, often used to achieve a reverb effect. A ConvolverNode always has exactly one input and one output."
Example at JSBin, THIS EXAMPLE DOES NOT WORK IN YOUR BROWSER as the edX platforms disables Ajax loading in its HTML pages. Try it at JSBin!
 
 
From Wikipediaa 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 wide variety of techniques. We can find many high quality impulses on the Web (for example here). The impulse used in the example is the one recorded at the opera: La Scala Opera of Milan, in Italy. It's a .wav file.
Try this demo to see the difference between different impulse files!
screenshot of a webapp that enable to switch between different impulse files
So before building the audio graph, we need to download the impulse. For this, we use an Ajax request (this will be detailed during Week 3), but for the moment, just take this function as it is... The Web Audio API requires that impulse files should be decoded in memory before use. For this reason, once the requested file has arrived, we call the decodeAudioData method. Once the impulse is decoded, we can build the graph. So the typical use is as follows:
  1. var impulseURL = "http://mainline.i3s.unice.fr/mooc/Scala-Milan-Opera-Hall.wav";
  2. var decodedImpulse;
  3. ...
  4. loadImpulse(impulseURL, function() {
  5.    // we only get here once the impulse has finished
  6.    // loading and is decoded
  7.    buildAudioGraphConvolver();
  8. });
  9.  
  10. ...
  11. function loadImpulse(url, callback) {
  12.    ajaxRequest = new XMLHttpRequest();
  13.    ajaxRequest.open('GET', url, true);
  14.    ajaxRequest.responseType = 'arraybuffer'; // for binary transfer
  15.  
  16.    ajaxRequest.onload = function() {
  17.       // The impulse has been loaded
  18.       var impulseData = ajaxRequest.response;
  19.       // let's decode it.
  20.       audioContext.decodeAudioData(impulseData, function(buffer) {
  21.          // The impulse has been decoded
  22.          decodedImpulse = buffer;
  23.          // Let's call the callback function, we're done!
  24.          callback();
  25.      });
  26.    };
  27.    ajaxRequest.onerror = function(e) {
  28.       console.log("Error with loading audio data" + e.err);
  29.    };
  30.    ajaxRequest.send();
  31. }
Here is the function that builds the graph. In order to set the quantity of reverb we would like to apply to the signal, we need to have two different routes for the signal:
    1. One "dry" route where we directly connect the audio source to the destination,
    2. One "wet" route where we connect the audio source to the convolver node (that will add a reverb effect), then to the destination,
    3. At the end of both routes, before the destination, we add a gain node, so that we can specify the quantity of dry and wet signal we're going to send to the speakers.
The audio graph will look like this:
audio graph of the previous example
And here is the function that builds the graph:
  1. function buildAudioGraphConvolver() {
  2.   // create the nodes
  3.   var source = audioContext.createMediaElementSource(playerConvolver);
  4.   convolverNode = audioContext.createConvolver();
  5.   // Set the buffer property of the convolver node with the decoded impulse
  6.   convolverNode.buffer = decodedImpulse;
  7.   convolverGain = audioContext.createGain();
  8.   convolverGain.gain.value = 0;
  9.   directGain = audioContext.createGain();
  10.   directGain.gain.value = 1;
  11.   // direct/dry route source -> directGain -> destination
  12.   source.connect(directGain);
  13.   directGain.connect(audioContext.destination);
  14.   // wet route with convolver: source -> convolver
  15.   // -> convolverGain -> destination
  16.   source.connect(convolverNode);
  17.   convolverNode.connect(convolverGain);
  18.   convolverGain.connect(audioContext.destination);
  19. }
Note that at line 6 we use the decoded impulse. We could not have done this before the impulse was loaded and decoded.

THE DYNAMICS COMPRESSOR NODE

Definition: "The DynamicsCompressorNode interface provides a compression effect, which lowers the volume of the loudest parts of the signal in order to help prevent clipping and distortion that can occur when multiple sounds are played and multiplexed together at once. This is often used in musical production and game audio."
It's usually a good idea to insert a compressor in your audio graph to give a louder, richer and fuller sound, and prevent clipping. 
Example you can try on JSBin or try it here in your browser:
 
  
In this example we set the gain to a very high value that will make a saturated sound. To prevent clipping, it suffices to add a compressor right at the end of the graph! Here we use the compressor with all default settings. This course does not go into detail about the different properties of the compressor node, as they are largely for musicians with the purpose of enabling the user to set subtle effects such as release, attack, etc.
Audio graph with the compressor activated:
Audio graph of the previous example
Extract of the HTML code:
  1. <audio src="http://mainline.i3s.unice.fr/mooc/guitarRiff1.mp3"
  2.         id="compressorExample" controls loop
  3.         crossorigin="anonymous"></audio>
  4. <br>
  5. <label for="gainSlider1">Gain</label>
  6. <input type="range" min="0" max="10" step="0.01"
  7.         value="8" id="gainSlider1" />
  8. <button id="compressorButton">Turn compressor On</button>
JavaScript source code:
  1. // This line is a trick to initialize the AudioContext
  2. // that will work on all recent browsers
  3. var ctx = window.AudioContext || window.webkitAudioContext;
  4. var audioContext;
  5. var compressorExemple, gainSlider1, gainNode1, compressorNode;
  6. var compressorButton;
  7. var compressorOn = false;
  8.  
  9. window.onload = function() {
  10.   // get the AudioContext
  11.   audioContext = new ctx();
  12.  
  13.   // the audio element
  14.   compressorExemple = document.querySelector('#compressorExample');
  15.   gainSlider1 = document.querySelector('#gainSlider1');
  16.   // button for turning on/off the compressor
  17.   compressorButton = document.querySelector('#compressorButton');
  18.   buildAudioGraph();
  19.   // input listener on the gain slider
  20.   gainSlider1.oninput = function(evt) {
  21.     gainNode1.gain.value = evt.target.value;
  22.   };
  23.   compressorButton.onclick = function(evt) {
  24.      if(compressorOn) {
  25.         // disconnect the compressor and make a
  26.         // direct route from gain to destination
  27.         compressorNode.disconnect(audioContext.destination);
  28.         gainNode1.disconnect(compressorNode);
  29.         gainNode1.connect(audioContext.destination);
  30.         compressorButton.innerHTML="Turn compressor: On";
  31.      } else {
  32.         // compressor was off, we connect the gain to the compressor
  33.         // and the compressor to the destination
  34.         gainNode1.disconnect(audioContext.destination);
  35.         gainNode1.connect(compressorNode);
  36.         compressorNode.connect(audioContext.destination);
  37.         compressorButton.innerHTML="Turn compressor: Off";
  38.      }
  39.      compressorOn = !compressorOn;
  40.    };
  41. };
  42.  
  43. function buildAudioGraph() {
  44.    // create source and gain node
  45.    var gainMediaElementSource =
  46.       audioContext.createMediaElementSource(compressorExemple);
  47.    gainNode1 = audioContext.createGain();
  48.    gainNode1.gain.value = parseFloat(gainSlider1.value);
  49.    // do not connect it yet
  50.    compressorNode = audioContext.createDynamicsCompressor(); // connect nodes together
  51.    gainMediaElementSource.connect(gainNode1);
  52.    gainNode1.connect(audioContext.destination);
  53. }
Explanations: There is nothing special here compared to the other examples in this section, except that we have used a new method disconnect (line 32 and line 38), which is available on all types of nodes (exceptctx.destination)  to modify the graph on the fly. When the button is clicked, we remove  or add a compressor in the audio graph (lines 28-42) and to achieve this, we disconnect and reconnect some of the nodes.

No comments:

Post a Comment