问题描述
我之前问了一个类似的问题,但它没有解决我的问题,而且解释得很糟糕。 这一次,我制作了插图,希望能更好地解释。
我的音频播放器有一个简单的频谱分析仪。频率存储在一个数组中,该数组在每隔requestAnimationFrame进行更新,该数组如下所示:
fbc_array = new Uint8Array(analyser.frequencyBinCount); analyser.getByteFrequencyData(fbc_array);
Read more about getByteFrequencyData here.
所以这很好用,但是我希望频率在整个频谱中均匀分布。目前它显示的是线性频率:如您所见,这里的主要频率范围是高音(高端),而最主要的频率范围是低音范围(低端)。我希望我的分析器具有如下均匀分布的频率范围:
在这里,您可以看到频率在分析仪上均匀分布。这可能吗?
我用来生成分析器的代码如下所示:
// These variables are dynamically changed, ignore them. var canbars = 737 var canmultiplier = 8 var canspace = 1 // The analyser var canvas, ctx, source, context, analyser, fbc_array, bars, bar_x, bar_width, bar_height; function audioAnalyserFrame() { 'use strict'; var i; canvas.width = $('analyser-').width(); canvas.height = $('analyser-').height(); ctx.imageSmoothingEnabled = false; fbc_array = new Uint8Array(analyser.frequencyBinCount); analyser.getByteFrequencyData(fbc_array); ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas ctx.fillStyle = "white"; // Color of the bars bars = canbars; for (i = 0; i < bars; i += canmultiplier) { bar_x = i * canspace; bar_width = 2; bar_height = -3 - (fbc_array[i] / 2); ctx.fillRect(bar_x, canvas.height, bar_width, bar_height); } window.requestAnimationFrame(audioAnalyserFrame); } function audioAnalyserInitialize() { 'use strict'; var analyserElement = document.getElementById('analyzer'); if (analyserElement !== null && audioViewIsCurrent() === true) { if (analyserInitialized === false) { context = new AudioContext(); source = context.createMediaElementSource(audioSource); } else { analyser.disconnect(); } analyser = context.createAnalyser(); canvas = analyserElement; ctx = canvas.getContext('2d'); source.connect(analyser); analyser.connect(context.destination); if (analyserInitialized === false) { audioAnalyserFrame(); } analyserInitialized = true; analyser.smoothingTimeConstant = 0.7; } }
请注意,我在for循环中跳过了8个栏(请参见顶部的canmultiplier)(如果我不这样做,分析器的另一半将呈现在画布之外,因为它太大了。)我不知道这是否也是导致频率范围不一致的原因。
推荐答案
如果我理解正确的话,我认为这对您是有效的,尽管还远远不够完美。
您在for循环中所做的是每8个元素对数组进行一次采样。我会做的是以对数的方式进行采样。
示例:
//Given a range, transforms a value from linear scale to log scale. var toLog = function(value, min, max){ var exp = (value-min) / (max-min); return min * Math.pow(max/min, exp); } //This would be the frequency array in a linear scale var arr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]; //In this case i'm using a range from 1 to 20, you would use the size of your array. I'm incrementing 'i' by one each time, but you could also change that for (var i = 1; i < 20; i += 1) { //I'm starting at 1 because 0 and logarithms dont get along var logindex = toLog(i,1,19); //the index we want to sample //As the logindex will probably be decimal, we need to interpolate (in this case linear interpolation) var low = Math.floor(logindex); var high = Math.ceil(logindex); var lv = arr[low]; var hv = arr[high]; var w = (logindex-low)/(high-low); var v = lv + (hv-lv)*w; //the interpolated value of the original array in the logindex index. document.write(v + "<br/>"); //In your case you should draw the bar here or save it in an array for later. }
我希望我已经解释清楚了。这里有一个working demo,它有一些边界错误,但它的工作方式与我认为您需要的一样。