A few days ago I have written a small post about Reading Web Audio API with JavaScript. But no matter how silent I am acting on my working place, my computer is always producing the following noise.
I will try to create a small lightweight noise spectrum estimation and elimination filter in JavaScript that is based on the research of Quantile based noise estimation for spectral subtraction and Wiener filtering.
The following approach assumes that there are pure noise frames or speech plus noise frames and that the first frames are only noise. The unfilled or empty bars in the example below are representing the calculated noise.
Here is the JavaScript code behind the noise spectrum estimation based on frame wise speech / non-speech classifcation.
function snsNoiseFilter(alphaValue, betaValue) {
this.alpha = alphaValue;
if (this.alpha === undefined) {
this.alpha = 1.8;
}
this.beta = betaValue;
if (this.beta === undefined) {
this.beta = 0.03;
}
this.noise;
this.noiseSum = 0;
var sumFunction = function(a, b) {
return a + b;
};
this.getNoise = function(input) {
if (this.noiseSum == 0) {
this.noise = input;
this.noiseSum = this.noise.reduce(sumFunction, 0);
return this.noise;
}
var inputSum = input.reduce(sumFunction, 0);
var xnr = inputSum / this.noiseSum;
if (xnr > this.alpha) {
return this.noise;
}
var oneMinusBetaFactor = 1 - this.beta;
for (var i = 0; i < input.length; i++) {
this.noise[i] = oneMinusBetaFactor * this.noise[i] + this.beta * input[i];
}
this.noiseSum = oneMinusBetaFactor * inputSum + this.beta * this.noiseSum;
return this.noise;
};
}
You just have to create a instance of snsNoiseFilter
and can call getNoise
with the given
audio data from getByteFrequencyData
function for example.
// Create analyser and check for navigator.getUserMedia
navigator.getUserMedia = (
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia
);
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var source;
var stream;
var analyser = audioCtx.createAnalyser();
analyser.minDecibels = -90;
analyser.maxDecibels = -10;
analyser.smoothingTimeConstant = 0.85;
analyser.fftSize = 128;
var bufferLength = analyser.frequencyBinCount;
var distortion = audioCtx.createWaveShaper();
var gainNode = audioCtx.createGain();
var biquadFilter = audioCtx.createBiquadFilter();
var convolver = audioCtx.createConvolver();
var noiseFilter = new snsNoiseFilter();
// Bind function readAudioData to navigator.getUserMedia
if (navigator.getUserMedia) {
navigator.getUserMedia(
{audio: true},
function(stream) {
source = audioCtx.createMediaStreamSource(stream);
source.connect(analyser);
analyser.connect(distortion);
distortion.connect(biquadFilter);
biquadFilter.connect(convolver);
convolver.connect(gainNode);
gainNode.connect(audioCtx.destination);
readAudioData();
},
function(err) {
console.log(err);
}
);
} else {
console.log('UserMedia not supported on your browser');
}
var readAudioData = function() {
requestAnimationFrame(readAudioData);
var dataArray = new Uint8Array(bufferLength);
analyser.getByteFrequencyData(dataArray);
var noise = noiseFilter.getNoise(dataArray);
console.log(noise);
}