Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Isolate frequencies of audio context using the Web Audio API

I'm experimenting with the WebAudio API and am attempting to build an analyser that a user can interact with and ultimately turn on and off different frequencies within the music to isolate different beats within the track, i.e bass, kick etc.

I'm visualising the frequency data using the Canvas, and would like the user to be able to highlight parts of the visualisation, and in-turn muting frequencies.

By default the visualisation would look like this and the user would hear all frequencies.

enter image description here

But when the user selects several bars, the greyed out ones would mute the related frequencies:

enter image description here

My thinking is can I reverse-engineer the frequencyData array and essentially mute the associated frequencies?

** Update **

So I've been playing around by adding several biquadFilters with a type of notch in series, and then adjusting their frequency and Q values. This does sort of help isolate pats of the music, but not exactly how I want. This is the code I'm using so far...

const audioContext = new window.AudioContext();
const source = audioContext.createMediaElementSource(element);
const biquadFilter1 = audioContext.createBiquadFilter();
const biquadFilter2 = audioContext.createBiquadFilter();
const biquadFilter3 = audioContext.createBiquadFilter();
const analyser = audioContext.createAnalyser();

biquadFilter1.connect(analyser);
biquadFilter2.connect(analyser);
biquadFilter3.connect(analyser);
source
    .connect(biquadFilter1)
    .connect(biquadFilter2)
    .connect(biquadFilter3);
analyser.connect(audioContext.destination);

I'm not sure if I've set this up correctly, but it does allow me to very roughly manipulate the frequencies, but it feels like there is no accurate science to doing it this way.

Is what I'm attempting possible, and if so, any suggestions are really appreciated :)

like image 515
DanV Avatar asked Jan 22 '18 22:01

DanV


2 Answers

What you are looking for is a band pass filter.

A band-pass filter (also bandpass filter, BPF) is a device that passes frequencies within a certain range and rejects (attenuates) frequencies outside that range. https://en.wikipedia.org/wiki/Band-pass_filter

And we are lucky, the web audio api provide it via the BiquadFilterNode.

https://developer.mozilla.org/en-US/docs/Web/API/BiquadFilterNode

So, based on the demo code provided, to apply a band pass filter at the 400Hz frequency range to an html audio player.

The html audio element must be created by js then inserted into the DOM, or it won't works with the web audio api.

 <div id="hello"></div>

 <script>
    var audioCtx = new (window.AudioContext || window.webkitAudioContext)();

    // Create new player 
    var a = document.createElement("audio");
    // Give a source song
    a.src = "vorbis.ogg";
    // Show the controls
    a.setAttribute("controls", "");
    // Append to the DOM
    hello.appendChild(a);

    // Player now ready for the web audio api
    var mediaElement = a;

    //set up the different audio nodes we will use for the app

    var gainNode = audioCtx.createGain();
    var biquadFilter = audioCtx.createBiquadFilter();

    // connect the nodes together

    source = audioCtx.createMediaElementSource(mediaElement);
    source.connect(biquadFilter);
    biquadFilter.connect(gainNode);
    gainNode.connect(audioCtx.destination);

    // Manipulate the Biquad filter

    biquadFilter.type = "bandpass";
    biquadFilter.frequency.value = 400;
    biquadFilter.gain.value = 25;
</script>

The biquadFilter.frequency.value represent the center of the range frequencies. The range expands with the gain value.

This filters can be chained just like this example with some other GainNode, see https://developer.mozilla.org/en-US/docs/Web/API/GainNode

In a similar way, using two filters, a lowpass and a highpass, for the range 1000hz to 1500hz.

<div id="hello"></div>
<script>
var audioCtx = new (window.AudioContext)()
var a = document.createElement("audio")
a.src = "vorbis.ogg"
a.setAttribute("controls", "")
hello.appendChild(a)

var mediaElement = a

var gainNode = audioCtx.createGain()
var lowpass = audioCtx.createBiquadFilter()
var highpass = audioCtx.createBiquadFilter()

source = audioCtx.createMediaElementSource(mediaElement)
source.connect(lowpass)
lowpass.connect(highpass)
highpass.connect(gainNode)
gainNode.connect(audioCtx.destination)

lowpass.type = "lowpass"
lowpass.frequency.value = 1000
lowpass.gain.value = -1
highpass.type = "highpass"
highpass.frequency.value = 1500
highpass.gain.value = -1 
</script>
like image 87
NVRM Avatar answered Oct 24 '22 15:10

NVRM


You’re working with audio, so what you need is a band-pass filter. That will allow you to play only certain frequencies.

I can see you’re already using FFT to get the frequencies. From there, you have two options. One is to remove the frequencies you want from that array obtained after you do the FFT. Then, in order to get the sound you need the signal in the time domain, so you will need an Inverse FFT. The other option is to make your filter in the time domain, and then make convolution with the original signal.

I’m not posting code since I’ve just made this in C, but I’m pretty sure there are many libraries to do these operations easily (Convolution/IFFT).
:)

like image 20
Luis Gonzalez Avatar answered Oct 24 '22 16:10

Luis Gonzalez