在 JavaScript 中预处理音频数据(fft 频谱、峰值等)

Pre-process audio data (fft spectrum, peak, etc.) in JavaScript

我目前可以使用 JavaScript 网络音频 API 播放音轨。在播放此曲目时,我可以提取 FFT 频谱数据、峰值、RMS 值等。

但是,出于我的应用程序的目的,我需要能够在开始播放曲目之前从曲目中提取所有这些数据。

有没有办法使用网络音频来做到这一点 API。如果没有,还可以怎么做?

我试图通过使用以下代码来实现这一点,但它为每个 'frame':

返回了完全相同的值

在用户选择文件时加载音频:

var fileChooser = document.getElementById("chooseAudio");
var audio = null;

var file = fileChooser.files[0];

var reader = new FileReader();

reader.onload = function(e) {
    audio = new Audio(reader.result);
}

reader.readAsDataURL(file);

音频加载完成后:

var FPS = 60;
var INCREMENT = 1 / FPS;
var FFT_SIZE = 256;
var SMOOTHING = 0.7;

var duration = null;
var length = null;
var width = null;
var time = null;

var analyser = null;
var data = null;
var index = null;

function analyse() {

    duration = audio.duration / APR;
    length = Math.ceil(duration * FPS);
    width = 4;
    time = 0.0;

    data = array(length, width);
    index = 0;

    var context = new AudioContext();

    analyser = context.createAnalyser();
    analyser.fftSize = FFT_SIZE;
    analyser.smoothingTimeConstant = SMOOTHING;

    var source = context.createMediaElementSource(audio);

    source.connect(analyser);
    analyser.connect(context.destination);

    audio.play();

    while (index < length) {

        audio.currentTime = time;

        frame = getFrame();
        data[index] = frame;

        time += INCREMENT;
        index++;

    }

    audio.pause();
    audio.currentTime = 0;

}

function getFrame() {

    var rawFreq = new Uint8Array(analyser.frequencyBinCount);
    analyser.getByteFrequencyData(rawFreq);

    var rawTimeDom = new Uint8Array(analyser.fftSize);
    analyser.getByteTimeDomainData(rawTimeDom);

    var frame = [];
    frame.push(peak(rawTimeDom), rms(rawTimeDom), low(rawFreq), high(rawFreq));

    return frame;

}

data 的输出:

[
  [128, 128, 100.2, 68.3],
  [128, 128, 100.2, 68.3],
  ...
  [128, 128, 100.2, 68.3]
]

此外,我应该澄清一下,我尝试以每秒 60 次的频率对音频进行采样的原因是我需要稍后以相同的速率显示音频数据。

您可以使用 OfflineAudioContext 而不是 AudioContext 来预处理数据而不是 运行 实时音频管道。

From the MDN page:

An OfflineAudioContext doesn't render the audio to the device hardware; instead, it generates it, as fast as it can, and outputs the result to an AudioBuffer.

您可以在使用普通上下文播放音频之前在离线上下文中进行处理!

如果您确实有一组文件并且需要预先处理它们,请在此处使用 decodeAudioData to get the audio data from each file. Use an OfflineAudioContext instead of an AudioContext to get the analyser data. However, since the offline context can run faster than real time, you'll need to be careful on when you call getByteTimeDomainData. It's probably best to use suspend and resume,在 suspend 中调用 getFrame

但是,根据 getFrame 的作用(获取音频样本),您最好使用 ScriptProcessorNode or AudioWorklet 来获取您正在寻找的时域数据。这些适用于 AudioContextOfflineAudioContext.