Javascript 带offlineAudioContext的FFT

Javascript 带offlineAudioContext的FFT,javascript,fft,web-audio-api,Javascript,Fft,Web Audio Api,我正在尝试对音频文件进行频率分析,但我希望它在不播放的情况下发生。我发现有一个,这可能是我在这里需要的 在中完成代码 网络音频API对我来说是一个未经探索的领域,我不完全确定我在做什么,很多教程都关注实时音频,这正是我想要阻止的 在context.oncomplete中,我设法获得了一些外观准确的音频缓冲区渲染数据。当从fft中获取数据时,我似乎得到了一组非常小的数据,我猜这只是来自最后一个样本的数据。我更希望我正在加载的音频文件的每x ms都有这些数据。我很想了解一些关于如何获取这种数据格式的

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

在中完成代码

网络音频API对我来说是一个未经探索的领域,我不完全确定我在做什么,很多教程都关注实时音频,这正是我想要阻止的

context.oncomplete
中,我设法获得了一些外观准确的音频缓冲区渲染数据。当从fft中获取数据时,我似乎得到了一组非常小的数据,我猜这只是来自最后一个样本的数据。我更希望我正在加载的音频文件的每x ms都有这些数据。我很想了解一些关于如何获取这种数据格式的想法

基本上我期望的是:

[
  // 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,
  …
]

当您在
分析器节点上设置fftSize时,您将获得
(fftSize/2)
箱子数量。这就是为什么你看到的数据比你预期的要少得多

本质上,发生的是
getByteFrequencyData
只查看渲染缓冲区的前128个样本,而其余样本则被忽略

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

var processor = context.createScriptProcessor(fftSize, 1, 1);
source.connect(processor);

processor.onaudioprocess = function( e ) {
  var input = e.inputBuffer.getChannelData(0),
      data = new Uint8Array(fftSamples);
  fft.getByteFrequencyData(data);
  // do whatever you want with `data`
}

假设缓冲区是一个包含单声道声音的数组,
这是我写的一段很好的离线分析代码

传递给回调函数的函数将在分析的每个步骤中执行。
传递给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();
}

我无法让onaudioprocess实际启动,但从我所能看出,这是因为audioContext没有正确初始化。当我使用
new webkitAudioContext()
时,事件会按预期触发,但
webkitOfflineAudioContext
没有成功。似乎需要一些参数,但即使有一个通道,一个超高的长度和采样44100它似乎没有做任何事情。有什么想法吗?你能把你到目前为止得到的东西发一小部分吗?哦,伙计。你太接近了!只需将
context.startRendering()
添加到
processBuffer
函数的末尾,您的
onaudioprocess
处理程序就会开始工作。它基本上就是
processBuffer.length
——或者您想要渲染的任何
AudioBuffer
的长度。这里的技巧是,您可能需要一个
AudioContext
和一个
OfflineAudioContext
。使用
decodeAudioData
AudioContext
可以获得缓冲区长度,然后可以创建脱机上下文来创建
BufferSourceNode
ScriptProcessorNode
并进行渲染。@Marco你让它工作了吗?我也试着这么做,花了这么多时间试图找到解决方案,但仍然没有任何效果。请检查我的答案,并将其标记为已接受:@Zibri它看起来可能是正确的解决方案,但在问题发布后不久就放弃了这个项目,因此我真的没有办法或精力进行验证,对不起,这个问题可能离题了,但是。。。是否有可能获取给定时间的频率数据/frequencyBinCount?当用户通过音频文件搜索时,我想为给定时间的频率数据绘制一个波形。不播放音频。@frizurd确定。。只需在source.start之前设置时间