Javascript 从音频中获取对数byteFrequencyData

Javascript 从音频中获取对数byteFrequencyData,javascript,web-audio-api,Javascript,Web Audio Api,早些时候我问了一个类似的问题,但没有解决我的问题,而且解释得很糟糕。 这次我做了一些插图,希望能更好地解释 我有一个简单的频谱分析仪为我的音频播放器。频率存储在一个数组中,该数组在每个requestAnimationFrame上更新,该数组如下所示: fbc_array = new Uint8Array(analyser.frequencyBinCount); analyser.getByteFrequencyData(fbc_array); // These variables are dy

早些时候我问了一个类似的问题,但没有解决我的问题,而且解释得很糟糕。 这次我做了一些插图,希望能更好地解释

我有一个简单的频谱分析仪为我的音频播放器。频率存储在一个数组中,该数组在每个
requestAnimationFrame
上更新,该数组如下所示:

fbc_array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(fbc_array);
// 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;
    }
}

所以这很好,但是我希望频率在整个频谱中均匀分布。现在它正在显示线性频率:

如你所见,这里的主要频率范围是高音(高端),而最主要的频率范围是低音(低端)。我希望我的分析仪呈现均匀分布的频率范围,如下所示:

fbc_array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(fbc_array);
// 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;
    }
}

这里您可以看到在分析仪上均匀分布的频率。这可能吗

我用于生成分析仪的代码如下所示:

fbc_array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(fbc_array);
// 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;
    }
}
//这些变量是动态更改的,请忽略它们。
var Canbar=737
var CAN乘数=8
var canspace=1
//分析仪
var画布、ctx、源、上下文、分析器、fbc_数组、条、条,
钢筋宽度、钢筋高度;
函数AudioAnalyzerFrame(){
"严格使用",;
var i;
canvas.width=$('analyzer-').width();
canvas.height=$('analyzer-').height();
ctx.imageSmoothingEnabled=假;
fbc_阵列=新UINT8阵列(分析仪频率b计数);
分析仪。GetByTefFrequencyData(fbc_阵列);
ctx.clearRect(0,0,canvas.width,canvas.height);//清除画布
ctx.fillStyle=“white”//条的颜色
钢筋=钢筋;
对于(i=0;i

请注意,我跳过了for循环中的8个条(请参见顶部的canmultiplier)(如果我没有跳过,分析器的另一半会在画布外渲染,因为它太大)。我不知道这是否也是导致频率范围不一致的原因。

您必须手动平均这些值(或类似的)将其转换为对数数组;这正是FFT算法的工作方式。

另一种可能有效也可能无效的方法。将信号分成,比如说5个频带。应用低通和高通滤波器以及覆盖整个频率范围的3个带通滤波器。调制所有滤波器的输出(低通除外)降低0频率。为5个不同信号中的每一个添加一个分析仪。考虑到您已将滤波器输出频率降低,绘制每个信号的响应

单个分析仪的输出仍然是一致的,但结果可能足够接近


(可以使用一个或两个增益节点(其增益为振荡器节点的正弦波或余弦波)将频率调低到0。)

如果我理解正确,我认为这将适用于您,尽管还远远不够完美

在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.
}
//给定一个范围,将值从线性比例转换为对数比例。
var toLog=函数(值、最小值、最大值){
var exp=(最小值)/(最大-最小值);
返回最小*数学功率(最大/最小,经验);
}
//这将是线性范围内的频率阵列
var arr=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
//在本例中,我使用的范围是从1到20,您可以使用数组的大小。我每次将“i”递增一,但您也可以更改它
对于(变量i=1;i<20;i+=1){
//我从1开始,因为0和对数不协调
var logindex=toLog(i,1,19);//我们要采样的索引
//由于logindex可能是十进制的,我们需要插值(在本例中为线性插值)
var低=数学地板(logindex);
var high=数学单元(logindex);
var lv=arr[低];
var hv=arr[高];
var w=(逻辑指数低)/(高-低);
var v=lv+(hv-lv)*w;//logindex索引中原始数组的插值。
document.write(v+“
”;//在本例中,您应该在此处绘制条形图或将其保存在数组中以备以后使用。 }

我希望我能很好地解释我自己。这里你有一个有一些边界错误,但它可以按照我认为你需要的方式工作。

类似的东西应该可以工作:

// 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;
    //Find the center
    var center = Math.round(bars / 2) - 1;
    for (i = 0; i < fbc_array.length; i ++) {
        // Update the spectrum bars, spread evenly.
        bar_x = (center + (i % 2 == 0 ? -1 : 1) * Math.round(i / 2));
        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;
    }
}
//这些变量是动态更改的,请忽略它们。
var Canbar=737
var CAN乘数=8
var canspace=1
//分析仪
var画布、ctx、源、上下文、分析器、fbc_数组、条、条,
钢筋宽度、钢筋高度;
函数AudioAnalyzerFrame(){
"严格使用",;
var i;
canvas.width=$('analyzer-').width();
canvas.height=$('analyzer-').height();
ctx.imageSmoothingEnabled=假;
fbc_阵列=新UINT8阵列(分析仪频率b计数);
分析仪。GetByTefFrequencyData(fbc_阵列);
ctx.clearRect(0,0,canvas.width,canvas.height);//清除画布
ctx.fillStyle=“白色”;