JavaScript Equalizer
- Tutorial

What did you want to get?
Suppose we already have some kind of player. In the simplest case, this is a bare
audioelement.I want us to be able to fasten the equalizer to it
var audio = document.getElementById('audio');
equalize(audio); // как-то так,
so that you don’t have to think, and all this would not affect the work of the player itself. But, let's start from the beginning.
API
Any work with the Web Audio API begins with creating a context:
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();
What is important - such an object should be one. First, in order for all related objects to work together, they must be created in the same context.
( UPD: as of 09/21/15, an error occurs when creating more contexts
Uncaught NotSupportedError: Failed to construct 'AudioContext': The number of hardware contexts provided (6) is greater than or equal to the maximum bound (6). That is, chrome allows you to create up to six contexts at a time. ) The first thing we need is to create a wrapper for
HTMLMediaElement, with which we will work:var source = context.createMediaElementSource(audio);
The createMediaElementSource method also works with
Object elements
source- this is the first link in the chain (in the literal sense) that we are building. In the simplest case, the circuit consists of only two links - the source is immediately connected to the output.source.connect(context.destination);
Here context.destination is, roughly speaking, your columns.
The equalizer itself is built from filters created using createBiquadFilter .
Filter Creation Code:
var createFilter = function (frequency) {
var filter = context.createBiquadFilter();
filter.type = 'peaking'; // тип фильтра
filter.frequency.value = frequency; // частота
filter.Q.value = 1; // Q-factor
filter.gain.value = 0;
return filter;
};
The only parameter in this case is the frequency. The remaining parameters coincide for all filters or change during program operation. It:
- type - type of filter. It can take one of the values:
lowpass, highpass, bandpass, lowshelf, highshelf, peaking, notch, allpass.We only need a peaking filter - it allows you to selectively emphasize or weaken a limited band of the sound spectrum. Read more. - Q - quality factor - changes the bandwidth of the frequencies that the filter affects.
- gain - the force with which the filter affects the frequency band.
It is necessary to create filters for the entire set of frequencies. For a 10-band equalizer, this can be
60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000 и 16000Hz (the values are copied from winamp).var createFilters = function () {
var frequencies = [60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000],
filters;
// создаем фильтры
filters = frequencies.map(createFilter);
// цепляем их последовательно.
// Каждый фильтр, кроме первого, соединяется с предыдущим.
// Удачно, что reduce без начального значения как раз пропускает первый элемент.
filters.reduce(function (prev, curr) {
prev.connect(curr);
return curr;
});
return filters;
};
It is very important to connect the filters in series. When I wrote the first version, my filters were connected in parallel, and the output was nothing but a terrible rumble. The cure was not immediately found (mainly because the answer marked as 'solution' is not correct).
It remains only to tie it all together:
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext(),
audio = document.getElementById('audio');
var createFilter = function (frequency) {
var filter = context.createBiquadFilter();
filter.type = 'peaking'; // тип фильтра
filter.frequency.value = frequency; // частота
filter.Q.value = 1; // Q-factor
filter.gain.value = 0;
return filter;
};
var createFilters = function () {
var frequencies = [60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000],
filters = frequencies.map(createFilter);
filters.reduce(function (prev, curr) {
prev.connect(curr);
return curr;
});
return filters;
};
var equalize = function (audio) {
var source = context.createMediaElementSource(audio),
filters = createFilters();
// источник цепляем к первому фильтру
source.connect(filters[0]);
// а последний фильтр - к выходу
filters[filters.length - 1].connect(context.destination);
};
equalize(audio);
Like this. Equalizer in 30 lines. Then it’s the small business - to tie the controls, but this is an elementary task.
// схематично
var bindEvents = function (inputs) {
inputs.forEach(function (item, i) {
item.addEventListener('change', function (e) {
filters[i].gain.value = e.target.value;
}, false);
});
};
Here, in fact, is a demo where an ogg file is streamed and passed through our equalizer, but only Google Chrome users can enjoy it, users of other browsers will have to bother to open a local file, and not even any . Because…
Moment of frustration
Having collected the first version of the player, I decided to fasten a soundcloud to it. It's great to drive songs from the cloud through the equalizer. In the end, everything started up ... but only in chrome - Mozilla stubbornly refused to reproduce the stream. But at the same time, I ran local files with a bang. And then it turned out scary:
To prevent this [information leakage], a MediaElementAudioSourceNode must output silence instead of the normal output of the HTMLMediaElement if it has been created using an HTMLMediaElement for which the execution of the fetch algorithm labeled the resource as CORS-cross-origin. ( documentation )
That is, CORS and the Web Audio API are incompatible.
upd: as rightly noted in the comments, CORS can be configured using the crossorigin attribute , but for this, the header must be added to the stream
Access-Control-Allow-Origin. And for downloaded files, for example, you can use
ObjectURL:// схематично
fileInput.addEventListener('change', function (e) {
var url = URL.createObjectURL(e.target.files[0]);
audio.src = url;
}, false);
Total
In general, the Web Audio API is already supported pretty well and can be widely used. And most importantly - api allows you to write a very high-level code, and you can write your own equalizer in 30 lines if you do not like this :)
Materials:
References:
PS It is nice to know that the article got into the top habr for 2014. 2nd place in the API category