Web audio API equalizer
Asked Answered
B

2

10

I have been looking around for creating an audio equalizer using the Web audio API: http://webaudio.github.io/web-audio-api/

I found a lot of threads about creating a visualizer, but that is of course not what I want to do. I simply want to be able to alter the sound using frequency sliders. I found that the biquadFilter should do the work, but I can't get a good result. The sound is altered consistently when I change any frequency value, but it just lowers the quality of the sound while it should alter the frequencies.

I first load a sound:

Audio.prototype.init = function(callback){
    var $this = this;
    this.gainScale = d3.scale.linear().domain([0,1]).range([-40,40]);
    this.context = new AudioContext();
    this.loadSounds(function(){
        $this.loadSound(0);
        $this.play();
        callback.call();
    });
};

Everything works well, the sound plays when ready.

I have 10 sliders for frequencies [32,64,125,250,500,1000,2000,4000,8000,16000]. For each slider I create a filter and I connect it to the source, as is described here: Creating a 10-Band Equalizer Using Web Audio API :

Audio.prototype.createFilter = function(index,frequency){
    if(this.filters == undefined) this.filters = [];
    var filter = this.context.createBiquadFilter();
    filter = this.context.createBiquadFilter();
    filter.type = 2;
    filter.frequency.value = frequency;
    // Connect source to filter, filter to destination.
    this.source.connect(filter);
    filter.connect(this.context.destination);
    this.filters[index] = filter;
};

Finally, when I change the value of a slider I update the filter:

Audio.prototype.updateFilter = function(index,newVal){
    this.filters[index].frequency.gain = this.gainScale(newVal);
};

NB: my this.gainScale function takes as input a value in [0,1] and returns a value in [-40,40] to set the gain between -40 and 40 for each frequency.

Would appreciate any help !

Batts answered 5/5, 2015 at 23:45 Comment(0)
F
17

Multiple things here.

1) You shouldn't use bandpass filters in parallel to implement an equalizer. Among other issues, biquad filtering changes the phase of different parts of the signal, and therefore the different bands will end up in different phases, and you'll have some potentially quite bad effects on your sound when it recombines.

2) The approach that you want is have a low shelf filter on the bottom end, a high shelf filter on the top end, and any number of peaking filters in the middle. These should be connected in series (i.e. the input signal connects to one filter, which connects to another filter, which connects to another filter, et al, and only the final filter should get connected to the audiocontext.destination. The Q values should be tuned (see below), and the gain on the filter determines the boost/cut. (For flat response, all filter gains should be set to zero.)

3) filter.type is an enumerated type that you should set as a string, not as a number. "lowshelf", "highshelf" and "peaking" are the ones you're looking for here.

You can see an example of a simple three-band equalizer in my DJ app - https://github.com/cwilso/wubwubwub/blob/MixTrack/js/tracks.js#L189-L207 sets it up. To modify this into a multiband equalizer, you'll need to tweak the Q value of each filter to get the bands to not overlap too much (it's not bad if they do overlap, but your bands will be more precise if you tune them). You can use http://googlechrome.github.io/web-audio-samples/samples/audio/frequency-response.html to examine the frequency response for a given Q and filter type.

Furl answered 6/5, 2015 at 7:0 Comment(4)
Hi, thanks a lot, it fixed most of my issues and start to sound like something not too bad. Basically, I now just create the filters without type, and without connecting them. Then, I give a type according to the filter index (0: lowshelf, 0<i<n: peaking, n: highshelf). Finally, I connect filters in chain: source.connect(filter[0]), filter[i].connect(filter[i+1]), filter[n].connect(context.destination) For now I did not play with the Q yet, and kept the default values. I guess this is the next step to have something better. Thanks for the references, will have a look at them right nowBatts
What about combining lowshelf and highshelf filter for each frequency?Cavuoto
For example if you want to modify the 250 hertz gain add lowshelf at 350 to cut everything above 350 and second highshelf at 150 to cut everything below 150. connect the two gains and modify their values at once ?Do you think this will work ?Cavuoto
This works to create a single bandpass filter, but to recombine the outputs of each band you would need to connect them in parallel (i.e. connect all custom bandpass filters to a single output node). This does NOT work well - each filter changes the phase in different amounts, and you can't just sum the outputs together because waveforms fall apart. You can use this to create a bandpass in isolation, but not to make an EQ. It's the same reason I used peaking, not bandpass to create the EQ above - you need a single serial signal chain, without recombining different frequency components.Furl
B
0

One issue is that you want your sliders to be controlling the gain of the filter at a given frequency, not the filter frequency itself. According to the spec the gain of a bandpass filter is not controllable which is a bit limiting. Fortunately you can put a gain node at the end of each filter.

var filter = this.context.createBiquadFilter();
filter = this.context.createBiquadFilter();
filter.type = 2;
filter.frequency.value = frequency;

var gain = this.context.createGainNode();

// Connect source to filter, filter to the gain, gain to destination.
this.source.connect(filter);
filter.connect(gain);
gain.connect(this.context.destination);
this.filters[index] = filter;
this.gains[index] = gain;

Next you'll need to connect your slider up to the gain parameter of the gain control. I don't really know web audio so I'll leave that to you. The last thing is that you need to to specify the Q of the filter. I get the impression from your list of frequencies that you're trying to create octave wide filters so the Q factor is probably going to be around 1.414. You're really going to need to do a bit of research if you want to get this right.

Bedclothes answered 5/5, 2015 at 23:53 Comment(2)
Thanks for the reply. You're correct, there was a mistake. But no improvement on my sideBatts
Actually it seems to work as it is. i just made the modifications suggested by @Furl and it works quite well, without using an additional gainNodeBatts

© 2022 - 2024 — McMap. All rights reserved.