【问题标题】:FFT with offlineAudioContext带离线音频上下文的 FFT
【发布时间】:2013-09-14 03:05:27
【问题描述】:

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

this jsfiddle中的完整代码

Web Audio 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,
  …
]

【问题讨论】:

  • 请检查我的答案并将其标记为已接受:stackoverflow.com/a/57744228/236062
  • @Zibri 看起来它可能是正确的解决方案,但是在问题发布后不久就放弃了这个项目,所以我真的没有办法或精力去验证,对不起。

标签: javascript fft web-audio-api


【解决方案1】:

当您在 AnalyserNode 上设置 fftSize 时,您将获得 (fftSize / 2) 的 bin 数量。这就是为什么您看到的数据比您预期的要少。

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

相反,您可能想尝试使用ScriptProcessorNodebufferSize 等于您的fftSize。然后,在ScriptProcessorNodeonaudioprocess 事件处理程序中,您可以抓取缓冲区并获取其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`
}

【讨论】:

  • 我无法让 onaudioprocess 真正触发,但据我所知,这是因为 audioContext 未正确初始化。当我使用new webkitAudioContext() 时,事件按预期触发,但webkitOfflineAudioContext 没有运气。似乎需要一些参数,但即使有 1 个通道、超高长度和 44100 的采样率,它似乎也无济于事。有什么想法吗?
  • 你能发布一个 jsFiddle 你到目前为止所拥有的吗?
  • 哦,伙计。你太棒了接近了!只需将context.startRendering() 添加到processBuffer 函数的末尾,您的onaudioprocess 处理程序就会开始工作。
  • 基本上就是processBuffer.length——或者你想渲染的AudioBuffer的长度。这里的诀窍是您可能需要AudioContextOfflineAudioContext。将AudioContext 用于decodeAudioData,这样您就可以获得缓冲区长度,然后然后您可以创建离线上下文来创建您的BufferSourceNodeScriptProcessorNode 并进行渲染。
  • @Marco 你成功了吗?我正在尝试做同样的事情并且已经花了很多时间试图找到解决方案但仍然没有任何工作..
【解决方案2】:

假设缓冲区是一个包含单声道声音的 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();
}

【讨论】:

  • 这个问题可能跑题了,但是......是否有可能获得给定时间的频率数据/frequencyBinCount?当用户搜索音频文件时,我想为给定时间的频率数据绘制波形。没有正在播放的音频。
  • @frizurd 确定.. 只需在 source.start 之前设置时间
猜你喜欢
  • 1970-01-01
  • 2017-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多