简体   繁体   English

FFT 与 offlineAudioContext

[英]FFT with offlineAudioContext

I'm trying to do frequency analysis of an audio file, but I want it to happen without playing.我正在尝试对音频文件进行频率分析,但我希望它在不播放的情况下发生。 I found there was a offlineAudioContext , which is probably what I need here.我发现有一个offlineAudioContext ,这可能是我在这里需要的。

Complete code in this jsfiddle这个jsfiddle中的完整代码

The Web Audio API is a bit of unexplored territory for me and I'm not completely sure what I'm doing, a lot of the tutorials are focused on realtime audio and that's exactly what I want to prevent. Web Audio API 对我来说是一个未开发的领域,我不完全确定我在做什么,很多教程都专注于实时音频,而这正是我想要防止的。

Inside context.oncomplete , I managed to get some accurate looking rendered audiobuffer data.context.oncomplete ,我设法获得了一些看起来准确的渲染音频缓冲区数据。 When getting data from the fft, I seem to be getting a very small set of data, which I'm guessing it's just the data from the last sample.当从 fft 获取数据时,我似乎得到了一个非常小的数据集,我猜这只是来自最后一个样本的数据。 I'd rather have this data for every x ms of the audio file that I'm loading.我宁愿为我正在加载的音频文件的每 x ms 获取这些数据。 I would love to get some ideas on how to get this format of the data?我很想知道如何获取这种格式的数据?

Basically what I'm expecting is something like:基本上我期待的是这样的:

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

When you set fftSize on your AnalyserNode , you'll get (fftSize / 2) number of bins.当您在设置fftSize AnalyserNode ,你会得到(fftSize / 2)框的数目。 So that's why you're seeing a lot less data than you expect.所以这就是为什么你看到的数据比你预期的少得多。

Essentially what's happening is that getByteFrequencyData is only looking at the first 128 samples of your rendered buffer, and the rest simply get ignored.基本上发生的事情是getByteFrequencyData只查看渲染缓冲区的前 128 个样本,其余的只是被忽略。

Instead, you might want to try using a ScriptProcessorNode with a bufferSize equal to your fftSize .相反,您可能想尝试使用ScriptProcessorNode ,其bufferSize等于您的fftSize Then, in the ScriptProcessorNode 's onaudioprocess event handler, you can grab the buffer and get its FFT.然后,在ScriptProcessorNodeonaudioprocess事件处理程序中,您可以获取缓冲区并获取其 FFT。 Something like this:像这样的东西:

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`
}

Assuming that buffer is a float32array containing a one channel sound,假设缓冲区是一个包含单声道声音的 float32array,
here is a nice code for offline analysis I wrote.这是我编写的离线分析的好代码。

The function passed to callback will be executed at every step of the analysis.传递给回调的函数将在分析的每一步执行。
The function passed to onend will be called at the end of the process.传递给 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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM