JellyTots
JellyTots

Reputation: 123

Filtering and Web Audio

I'm trying to implement some filters using Web Audio.

I have the below set-up and only the lowpass filter works, but even then I can't seem to get it to stop.

          //Creating filters, setting their types and setting up booleans for later use.
      //Lowpass Filter Setup
      lowpass = context.createBiquadFilter();
      lp_bool=false;
      lowpass.type="lowpass";
      lowpass.type=LOWPASS;

      //Highpass Filter Setup
      highpass = context.createBiquadFilter();
      highpass.type="highpass";
      highpass.type=HIGHPASS;
      hp_bool=false;

      //Bandpass Filter Setup
      bandpass = context.createBiquadFilter();
      bandpass.type="bandpass";
      bandpass.type=BANDPASS;
      bp_bool=false;

      //Lowshelf Filter Setup
      lowshelf = context.createBiquadFilter();
      lowshelf.type="lowshelf";
      lowshelf.type=LOWSHELF;
      ls_bool=false;
      /*
      lowshelf.frequency.value = 440;
      lowshelf.gain.value = 0;*/

      //Highshelf Filter Setup
      highshelf = context.createBiquadFilter();
      highshelf.type="highshelf";
      highshelf.type=HIGHSHELF;
      hs_bool=false;/*
      highshelf.frequency.value = 440;
      highshelf.gain.value = 0;*/

      //Peaking Filter Setup
      peaking = context.createBiquadFilter();
      peaking.type="peaking";
      peaking.type=PEAKING;
      pk_bool=false;/*
      peaking.frequency.value = 440;
      peaking.Q.value = 0;
      peaking.gain.value = 0;*/

      //Notch Filter Setup
      notch = context.createBiquadFilter();
      notch.type="notch";
      notch.type=NOTCH;
      nh_bool=false;/*
      notch.frequency.value = 440;
      notch.Q.value = 0;*/

      //Allpass Filter Setup
      allpass = context.createBiquadFilter();
      allpass.type="allpass";
      allpass.type=ALLPASS;
      ap_bool=false;/*
      allpass.frequency.value = 440;
      allpass.Q.value = 0;*/
    };

Toggle Function:

function toggle_filter(filter_name,filter_bool) {
      masterVolume.disconnect(0);
      // Check if we want to enable the filter.
      if (filter_bool==false) {
        // Connect through the filter.
        masterVolume.connect(filter_name);
        filter_name.connect(context.destination);
        filter_bool=true;
      } else if(filter_bool==true){
        filter_name.disconnect(0);
        // Otherwise, connect directly.
        masterVolume.connect(context.destination);
        filter_bool=false;
      }
    };

Called from buttons:

<div id="filter-container">
<a onclick="toggle_filter(lowpass,lp_bool);" id="filter-button">Lowpass</a>
<a onclick="toggle_filter(highpass,hp_bool);" id="filter-button">Highpass</a>
<a onclick="toggle_filter(bandpass,bp_bool);" id="filter-button">Bandpass</a>
<a onclick="toggle_filter(lowshelf,ls_bool);" id="filter-button">Lowshelf</a>
<a onclick="toggle_filter(highshelf,hs_bool);" id="filter-button">Highshelf</a>
<a onclick="toggle_filter(peaking,pk_bool);" id="filter-button">Peaking</a>
<a onclick="toggle_filter(notch,nh_bool);" id="filter-button">Notch</a>
<a onclick="toggle_filter(allpass,ap_bool);" id="filter-button">Allpass</a>
<div>

Can anyone point me in the direction of where I'm going wrong?

Upvotes: 1

Views: 607

Answers (3)

ggorlen
ggorlen

Reputation: 56945

Firstly, you can't have more than one unique id in the DOM. Use class="" if you want multiple nodes to share an identifier.

Secondly, immediate re-assignments generally don't make sense.

allpass.type="allpass";
allpass.type=ALLPASS;

immediately overwrites the first value set with something else. It's unclear how the variable ALLPASS was defined in your code, but remove one of these lines -- it's either redundant at best or incorrect at worst (probably the latter).

Thirdly, there's a lot of repeated code. Use an array of filter types, along with knobs for Q, frequency, gain, etc (if desired). The sort of coding that copy-pastes the code over and over is error-prone, unnecessarily verbose, hard to maintain, and if there were hundreds of filter types, virtually impossible to write by hand.

Here's a basic proof of concept to work from:

const makeOsc = ({type, freq}) => {
  const oscillator = audioCtx.createOscillator();
  oscillator.type = type;
  oscillator.frequency.setValueAtTime(freq, audioCtx.currentTime);
  return oscillator;
};

const makeFilter = ({type, freq, q, gain}) => {
  const filter = audioCtx.createBiquadFilter();
  filter.type = type;
  filter.frequency.value = freq;
  filter.Q.value = q;
  filter.gain.value = gain;
  return filter;
};

const audioCtx = new (window.AudioContext || window.webkitAudioContext)();

const filters = [
  "lowpass", "highpass", "bandpass", "lowshelf",
  "highshelf", "peaking", "notch", "allpass",
];

const filterTest = (() => {
  let osc;
  let filter;
  
  const start = filterType => {
    if (osc) {
      stop();
    }
    
    osc = makeOsc({type: "sawtooth", freq: 220});
    filter = makeFilter({
      type: filterType,
      freq: 200,
      q: 3,
      gain: 0
    });
    osc.connect(filter);
    filter.connect(audioCtx.destination);
    osc.start();
  };
  
  const stop = () => {
    if (osc) {
      osc.stop();
      osc.disconnect();
      filter.disconnect();
      osc = null;
    }
  };
  
  const playing = () => !!osc;
  
  return {start, stop, playing};
})();

let filterType = filters[0];
document.querySelector(".filter-type")
  .innerHTML = filters.map(e => `<option>${e}</option>`)
;
document.querySelector(".start")
  .addEventListener("click", e => filterTest.start(filterType))
;
document.querySelector(".stop")
  .addEventListener("click", e => filterTest.stop())
;
document.querySelector(".filter-type")
  .addEventListener("change", e => {
    const {target: sel} = e;
    filterType = sel.options[sel.selectedIndex].value;
    
    if (filterTest.playing()) {
      filterTest.start(filterType);
    }
  });
;
<h1>warning: loud noise</h1>
<select class="filter-type"></select>
<button class="start">start</button>
<button class="stop">stop</button>

Upvotes: 0

asdf
asdf

Reputation: 1032

you have to set the filter type like this:

filter.type = "highpass";

Upvotes: 0

cwilso
cwilso

Reputation: 13908

You're setting the type to an unknown value after properly setting it to a strung every time. That ends up resetting it to the default - lowpass. Cut the lines that set .type=HIPASS, etc. (Or move them before the corresponding string set.)

Upvotes: 4

Related Questions