Reputation: 500
I am creating an audio bar visualizer with the Web API and I want the bars to only show ~ 40Hz to ~10kHz. The only thing I found was the frequency domain but that doesn't provide me with what I'm looking for (AnalyserNode.fftSize). Is there a way to only visualize that frequency? Here's my code:
.controller('PlayerCtrl', function(PlayerService, $scope){
$scope.title = PlayerService.songName;
$scope.art = PlayerService.songArt;
$scope.url = PlayerService.songUrl + '?client_id=54970813fe2081a104a874f0f870bcfe';
if (! window.AudioContext) {
if (! window.webkitAudioContext) {
alert('no audiocontext found, update your browser yo');
}
window.AudioContext = window.webkitAudioContext;
}
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var analyser = audioCtx.createAnalyser();
analyser.minDecibels = -60;
analyser.maxDecibels = 0;
analyser.smoothingTimeConstant = 0.85;
var audioBuffer;
var sourceNode;
var javascriptNode;
var canvas = document.querySelector('.visualizer');
var canvasCtx = canvas.getContext("2d");
var intendedWidth = document.querySelector('.now-playing').clientWidth;
canvas.setAttribute('width',intendedWidth);
var visualSelect = document.getElementById("visual");
var drawVisual;
setupAudioNodes();
loadSound($scope.url); //music file
function loadSound(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = function() {
audioCtx.decodeAudioData(request.response, function(buffer) {
playSound(buffer);
}, function(error){
console.log(error)
});
};
request.send();
}
function playSound(buffer) {
sourceNode.buffer = buffer;
sourceNode.start(0);
$(".content").show();
$("#hue").hide();
}
function setupAudioNodes() {
console.log('audio nodes')
javascriptNode = audioCtx.createScriptProcessor(2048, 1, 1);
javascriptNode.connect(audioCtx.destination);
sourceNode = audioCtx.createBufferSource();
sourceNode.connect(analyser);
analyser.connect(javascriptNode);
sourceNode.connect(audioCtx.destination);
visualize();
}
function visualize() {
console.log('viz');
WIDTH = canvas.width;
HEIGHT = canvas.height;
analyser.fftSize = 64;
var bufferLength = analyser.frequencyBinCount;
console.log(bufferLength);
var dataArray = new Uint8Array(bufferLength);
canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
function draw() {
drawVisual = requestAnimationFrame(draw);
analyser.getByteFrequencyData(dataArray);
canvasCtx.fillStyle = 'rgb(0, 0, 0)';
canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
var barWidth = (WIDTH / bufferLength) * 2.5;
var barHeight;
var x = 0;
for (var i = 0; i < bufferLength; i++) {
barHeight = dataArray[i];
canvasCtx.fillStyle = 'rgb(' + (barHeight + 100) + ',50,50)';
canvasCtx.fillRect(i*17, HEIGHT - barHeight / 2, 10, barHeight);
x += barWidth + 1;
}
}
draw()
}
})
Upvotes: 2
Views: 650
Reputation: 5915
I interpreted @mehmet's answer regarding
frequency resolution: sampleRate/fftSize
To mean that the total is divided across the eq bands displayed.
calcFreqs(sampleRate, fftSize) {
const bands = fftSize/2; // bands are half the fftSize
const fqRange = sampleRate / bands;
let allocated = [];
for ( let i = 0, j = bands; i < j; i++ ) {
sampleRate = Math.round(sampleRate - fqRange);
allocated.push(sampleRate);
}
// console.log(allocated.slice().reverse());
return allocated.slice().reverse();
}
So for 16 bands over 48,000Hz sample:
[0, 3000, 6000, 9000, 12000, 15000, 18000, 21000, 24000, 27000, 30000, 33000, 36000, 39000, 42000, 45000]
I'd like some clarification by an expert but here is the method which does that. My project is at Github
Upvotes: 1
Reputation: 8144
You need to calculate your frequency resolution: sampleRate/fftSize. That will give you the frequency range of each number given to you by the getByteFrequencyData
. Of course that ratio is hard to know if you are relying on the default sampleRate. Override it as such:
let audio_context = new AudioContext({
sampleRate: 44000,
});
Upvotes: 1
Reputation: 6748
Just don't use the higher frequencies that the analyser calculates. The easy way to do this is to set bufferLength to a smaller value than analyser.frequencyBinCount. The analyser will give you as much data as it can fit in the array, and drop the rest.
The bins are evenly spaced from zero to half the sampling rate, so at typical sampling rates (44kHz) you would want about half of the bins. More generally, Math.ceil(analyser.frequencyBinCount * 10000 / (audioCtx.sampleRate / 2))
should give you the number you want.
Upvotes: 1