使用offlineAudioContext进行FFT

时间:2013-09-10 11:55:26

标签: javascript fft web-audio

我正在尝试对音频文件进行频率分析,但我想让它在没有播放的情况下发生。我发现有一个offlineAudioContext,这可能就是我在这里所需要的。

this jsfiddle

中的完整代码

Web Audio API对我来说是一个未开发的领域,我不完全确定我在做什么,很多教程都专注于实时音频,而这正是我想要阻止的。

context.oncomplete内,我设法获得了一些准确的渲染音频缓冲数据。从fft获取数据时,我似乎得到了一小组数据,我猜它只是上一个样本的数据。我宁愿为我正在加载的音频文件的每x毫秒都有这些数据。我想知道如何获得这种格式的数据?

基本上我期待的是:

[
  // array with frequency data for every (nth) frequency band for 1st sample,
  // array with frequency data for every (nth) frequency band for 2nd sample,
  // array with frequency data for every (nth) frequency band for 3rd sample,
  …
]

2 个答案:

答案 0 :(得分:1)

当您在AnalyserNode上设置fftSize时,您将获得(fftSize / 2)个分区数。这就是为什么你看到的数据少于预期的原因。

基本上发生的事情是getByteFrequencyData只查看渲染缓冲区的前128个样本,其余的只是被忽略。

相反,您可能希望尝试使用ScriptProcessorNode bufferSize等于您的fftSize。然后,在ScriptProcessorNode的{​​{1}}事件处理程序中,您可以获取缓冲区并获取其FFT。像这样:

onaudioprocess

答案 1 :(得分:1)

假设缓冲区是一个float32array,其中包含一个声道的声音,
这是我编写的用于离线分析的好代码。

传递给回调的函数将在分析的每个步骤中执行。
传递给onend的函数将在流程结束时调用。

function callback(data) {
    callback.count = callback.count || 0;
    console.log(callback.count++, data);
}
fftAnalyser = function(buff, sampleRate, fftSize, callback, onend) {

    // by Zibri (2019)
    var frequencies = [1000 1200,1400,1600]; //for example
    var frequencyBinValue = (f)=>{
        const hzPerBin = (ctx.sampleRate) / (2 * analyser.frequencyBinCount);
        const index = parseInt((f + hzPerBin / 2) / hzPerBin);
        return data[index]+1;
    }
    ctx = new OfflineAudioContext(1,buff.length,sampleRate);
    ctx.sampleRate = sampleRate;
    ctx.destination.channelCount = 1;

    processor = ctx.createScriptProcessor(1024, 1, 1);
    processor.connect(ctx.destination);

    analyser = ctx.createAnalyser();
    analyser.fftSize = fftSize;
    analyser.smoothingTimeConstant = 0.0;

    //analyser.minDecibels = -60;
    //analyser.maxDecibels = -20;

    source = ctx.createBufferSource();
    source.connect(analyser);

    processor.onaudioprocess = function(e) {
        data = new Uint8Array(analyser.frequencyBinCount);
        analyser.getByteFrequencyData(data);
        state = frequencies.map(frequencyBinValue.bind(this))
        if ("function" == typeof callback)
            onend();
        callback(state);
    }

    analyser.connect(processor);

    var sbuffer = ctx.createBuffer(1, buff.length, ctx.sampleRate);
    sbuffer.getChannelData(0).set(buff);
    source.buffer = sbuffer;

    source.onended = function(e) {
        console.log("Analysys ended.");
        if ("function" == typeof onend)
            onend();
    }

    console.log("Start Analysis.");

    source.start(0);
    ctx.startRendering();
}