Search code examples
javascriptweb-audio-api

Web Audio API, setting treble and bass


I'm trying to learn how to use the web audio api properly and I'm running into a bit of confusion.

In my project, I'm trying to replicate the functionality of an old 1982 Harman/Kardon receiver. (click link to see photo)

This receiver has separate dials for the treble and bass control. I'll just deal with treble in this question. I'm sure I can figure out the bass equivalent once I'm pointed in the right direction.

In an initialization function I create the context and filter nodes.

window.AudioContext = window.AudioContext || window.webkitAudioContext;
    context = new AudioContext();
    source = context.createMediaElementSource(document.getElementById('audio'));
    gainNode = context.createGain();

//filter nodes
bassTurnoverFilter = context.createBiquadFilter();
trebleTurnoverFilter = context.createBiquadFilter();
loudnessTrebFilter = context.createBiquadFilter();
loudnessBassFilter = context.createBiquadFilter();
trebleLevelFilter = context.createBiquadFilter();
bassLevelFilter = context.createBiquadFilter();

I am currently using the jogDial plugin to control the dials. The dials do work and I can get a range for the "treble" variable between 0 and 1 when the dials turn from 0% to 100%.

This is the current mousemove function I am using for the treble dial:

.on("mousemove", function(event){

var treble = (event.target.rotation + 140) / 280;

    if(trebleLevelFilter !== undefined){
        trebleLevelFilter.disconnect();
    }
    source.connect(trebleLevelFilter); 
    trebleLevelFilter.type = "highshelf"; 
    trebleLevelFilter.frequency.value = 200; 
    trebleLevelFilter.gain.value = treble; 
    trebleLevelFilter.connect(context.destination);
});

My question, or multi-part question is... which of the 6 types should I use? ("lowpass","highpass", "bandpass", "lowshelf", "highshelf", "peaking", "notch", "allpass") I'm guessing it is the highpass or highself.

What frequency should I set?

Is the gain.value what should be dynamic when the dial turns?

Am I going in completely the wrong direction?

I have the gain.value set to the treble variable value, and it seems like it is increasing the volume a little when turned to 100%... but i don't think that is the correct functionality I am trying to accomplish.


Solution

  • I'm not sure why you're creating six different filters - you should only need two, one for treble and one for bass.

    I think that the HK amp you're looking at has no midrange control- which is a bit odd, but okay. The bass filter is likely a low shelf, and the treble is a high shelf; the buttons control what the cutoff frequency for each is. Remember, a shelving filter is flat response at zero gain - you can play with the filters at http://googlechrome.github.io/web-audio-samples/samples/audio/frequency-response.html see what they'll look like. Select a low shelf, freq= ~200, and then play with the gain.

    So, for example you're close with the treble filter, EXCEPT the "treble" and "bass" gain values shouldn't go [0,1] - it should go between [-maxgain,+maxgain]. "maxgain" is probably 2-3? You'll have to play around with it and pick a good range - I couldn't find that in the HK manual (http://www.manualslib.com/manual/279084/Harman-Kardon-Hk590i.html). It's also quite important to connect the filters in series, not in parallel (otherwise you will have phase problems).

    // if treble=0 and bass=0 you'll have a flat response
    bassFilter = context.createBiquadFilter();
    bassFilter.type = "lowshelf"; 
    bassFilter.frequency.value = 200;  // switches to 400 in UI
    bassFilter.gain.value = bass;  // you'll need to hook this to UI too
    
    trebleFilter = context.createBiquadFilter();
    trebleFilter.type = "highshelf"; 
    trebleFilter.frequency.value = 2000;  // switches to 6000 in UI
    trebleFilter.gain.value = treble;  // you'll need to hook this to UI too
    
    source.connect(bassFilter); 
    bassFilter.connect(trebleFilter); 
    trebleFilter.connect(context.destination);
    

    Also, there's no reason to disconnect the filter and reconnect it - you can make live changes to the .gain.value while it's connected.