有没有一种方法可以使用Web Audio API来比实时更快地采样音频?

19

我在尝试使用Web Audio API并寻找一种导入mp3文件的方法(因此仅适用于Chrome),并在画布上生成其波形。我可以实时完成这个过程,但我的目标是要比实时更快地完成。

我能找到的所有示例都涉及从分析器对象中读取频率数据,在附加到onaudioprocess事件的函数内部进行:

processor = context.createJavascriptNode(2048,1,1);
processor.onaudioprocess = processAudio;
...
function processAudio{
    var freqByteData = new Uint8Array(analyser.frequencyBinCount);
    analyser.getByteFrequencyData(freqByteData);
    //calculate magnitude & render to canvas
}

看起来,analyser.frequencyBinCount仅在声音播放时才会填充(大概是缓冲区被填满了的原因)。

我想要的是能够手动/编程快速地遍历文件,以生成画布图像。

到目前为止,我已经得到了以下代码:

$("#files").on('change',function(e){
    var FileList = e.target.files,
        Reader = new FileReader();

    var File = FileList[0];

    Reader.onload = (function(theFile){
        return function(e){
            context.decodeAudioData(e.target.result,function(buffer){
                source.buffer = buffer;
                source.connect(analyser);
                analyser.connect(jsNode);

                var freqData = new Uint8Array(buffer.getChannelData(0));

                console.dir(analyser);
                console.dir(jsNode);

                jsNode.connect(context.destination);
                //source.noteOn(0);
            });
        };
    })(File);

    Reader.readAsArrayBuffer(File);
});

但是getChannelData()始终返回一个空的类型数组。

非常感谢任何建议 - 即使最终发现这无法完成。 我想我是互联网上唯一一个不想实时处理数据的人了。

谢谢。


1
ya-超越实时。也就是说,如果曲目的持续时间为5分钟,我不想等待5分钟来生成波形。我希望尽可能快地处理它(希望几秒钟即可)。 - Quasipickle
1
@Pickle,你填充Uint8Array的方法不正确。可以在这里查看一个可行的解决方案:https://github.com/katspaugh/wavesurfer.js/blob/master/src/webaudio.js。 - katspaugh
2个回答

26

Web Audio API有一个非常惊人的离线模式,允许您通过音频上下文预处理整个文件,然后对结果进行操作:

var context = new webkitOfflineAudioContext();

var source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
source.noteOn(0);

context.oncomplete = function(e) {
  var audioBuffer = e.renderedBuffer;
};

context.startRendering();

因此,该设置与实时处理模式完全相同,只是您设置了oncomplete回调和对startRendering()的调用。在e.redneredBuffer中返回的是一个AudioBuffer


现在已经很晚了,而且我正在忙其他的事情,但这个答案听起来非常有前途(基本上因为这是我收到的唯一一个答案)。我一定会让你知道进展如何。 - Quasipickle
1
好的,我已经玩了一个小时了,但是我迷失了。你的代码如何与我的代码配合?当你设置source.buffer = buffer时,buffer从哪里来?我唯一能看到创建AudioBuffer的地方是作为context.decodeAudioData()成功函数的参数。这是我得到的jsfiddle。它很混乱,但这就是我所在的位置 - 基本上是瞎猜。http://jsfiddle.net/NW7E3/请确保控制台处于活动状态。 - Quasipickle
在你的oncomplete回调中添加一个本地的AudioContext:var context = new webkitAudioContext();。基本上,我认为你需要创建一个用于播放的上下文和一个用于处理的上下文。 - ebidel
我认为你的示例缺少了一些东西。在实时处理时,只有当 source.noteOn(0) 被调用时,数据才会被馈送到处理器。当使用 startRendering() 时,是什么触发了处理过程?你有关于此的任何文档吗?除了补丁说明之外,我什么都找不到。实时处理的 JSFiddle:http://jsfiddle.net/QpzQm/10/ 我最好的猜测是如何使用 startRendering() 的 JSFiddle(但实际上什么也没做)http://jsfiddle.net/9TEKG/ - Quasipickle
1
@Pickle,你有没有在这方面取得任何进展? - lakenen
显示剩余4条评论

6

我使用OfflineAudioContext使其工作,下面是相关代码。这里的完整示例展示了如何使用它来计算线性扫频的FFT幅度。一旦您掌握了连接节点的概念,您就可以在离线状态下做任何事情。

function fsin(freq, phase, t) {
  return Math.sin(2 * Math.PI * freq * t + phase)
}

function linearChirp(startFreq, endFreq, duration, sampleRate) {
  if (duration === undefined) {
    duration = 1; // seconds
  }
  if (sampleRate === undefined) {
    sampleRate = 44100; // per second
  }
  var numSamples = Math.floor(duration * sampleRate);
  var chirp = new Array(numSamples);
  var df = (endFreq - startFreq) / numSamples;
  for (var i = 0; i < numSamples; i++) {
    chirp[i] = fsin(startFreq + df * i, 0, i / sampleRate);
  }
  return chirp;
}

function AnalyzeWithFFT() {
  var numChannels = 1; // mono
  var duration = 1; // seconds
  var sampleRate = 44100; // Any value in [22050, 96000] is allowed
  var chirp = linearChirp(10000, 20000, duration, sampleRate);
  var numSamples = chirp.length;

  // Now we create the offline context to render this with.
  var ctx = new OfflineAudioContext(numChannels, numSamples, sampleRate);
  
  // Our example wires up an analyzer node in between source and destination.
  // You may or may not want to do that, but if you can follow how things are
  // connected, it will at least give you an idea of what is possible.
  //
  // This is what computes the spectrum (FFT) information for us.
  var analyser = ctx.createAnalyser();

  // There are abundant examples of how to get audio from a URL or the
  // microphone. This one shows you how to create it programmatically (we'll
  // use the chirp array above).
  var source = ctx.createBufferSource();
  var chirpBuffer = ctx.createBuffer(numChannels, numSamples, sampleRate);
  var data = chirpBuffer.getChannelData(0); // first and only channel
  for (var i = 0; i < numSamples; i++) {
    data[i] = 128 + Math.floor(chirp[i] * 127); // quantize to [0,256)
  }
  source.buffer = chirpBuffer;

  // Now we wire things up: source (data) -> analyser -> offline destination.
  source.connect(analyser);
  analyser.connect(ctx.destination);

  // When the audio buffer has been processed, this will be called.
  ctx.oncomplete = function(event) {
    console.log("audio processed");
    // To get the spectrum data (e.g., if you want to plot it), you use this.
    var frequencyBins = new Uint8Array(analyser.frequencyBinCount);
    analyser.getByteFrequencyData(frequencyBins);
    console.log(frequencyBins);
    // You can also get the result of any filtering or any other stage here:
    console.log(event.renderedBuffer);
  };

  // Everything is now wired up - start the source so that it produces a
  // signal, and tell the context to start rendering.
  //
  // oncomplete above will be called when it is done.
  source.start();
  ctx.startRendering();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接