4

由于旧的 Webaudio 脚本处理器自 2014 年以来已被弃用,并且 Audioworklets 在 Chrome 64 中出现,我决定尝试一下。但是我在移植我的应用程序时遇到了困难。我将从一篇不错的文章中举两个例子来说明我的观点。

首先是脚本处理器方式:

var node = context.createScriptProcessor(1024, 1, 1);
node.onaudioprocess = function (e) {
  var output = e.outputBuffer.getChannelData(0);
  for (var i = 0; i < output.length; i++) {
    output[i] = Math.random();
  }
};
node.connect(context.destination);

另一个填充缓冲区然后播放它:

var node = context.createBufferSource(), buffer = 
context.createBuffer(1, 4096, context.sampleRate), data = buffer.getChannelData(0);

for (var i = 0; i < 4096; i++) {
  data[i] = Math.random();
}

node.buffer = buffer;
node.loop = true;
node.connect(context.destination);
node.start(0);

两者之间的最大区别是第一个在播放期间用新数据填充缓冲区,而第二个预先生成所有数据。

由于我生成了大量数据,因此我无法事先进行。Audioworklet有很多示例,但它们都使用其他节点,只需在这些节点上运行 .start(),连接它,它就会开始生成音频。当我没有这样的方法时,我无法找到一种方法来做到这一点。

所以我的问题基本上是如何在 Audioworklet 中执行上述示例,当数据在某个数组的主线程中连续生成并且该数据的播放发生在 Webaudio 线程中时。

我一直在阅读有关消息端口的内容,但我也不确定这是否可行。这些例子并没有将我指向我所说的那个方向。我可能需要的是用我自己的数据在 AudioWorkletProcesser 派生类中提供流程函数的正确方法。

我目前基于脚本处理器的代码位于github,特别是 vgmplay-js-glue.js。

我一直在向 VGMPlay_WebAudio 类的构造函数添加一些代码,从示例转到实际结果,但正如我所说,我现在不知道该朝哪个方向移动。

constructor() {
            super();

            this.audioWorkletSupport = false;

            window.AudioContext = window.AudioContext||window.webkitAudioContext;
            this.context = new AudioContext();
            this.destination = this.destination || this.context.destination;
            this.sampleRate = this.context.sampleRate;

            if (this.context.audioWorklet && typeof this.context.audioWorklet.addModule === 'function') {
                    this.audioWorkletSupport = true;
                    console.log("Audioworklet support detected, don't use the old scriptprocessor...");
                    this.context.audioWorklet.addModule('bypass-processor.js').then(() => {
                            this.oscillator = new OscillatorNode(this.context);
                            this.bypasser = new AudioWorkletNode(this.context, 'bypass-processor');
                            this.oscillator.connect(this.bypasser).connect(this.context.destination);
                            this.oscillator.start();
                    });
            } else {
                    this.node = this.context.createScriptProcessor(16384, 2, 2);
            }
    }
4

1 回答 1

2

所以我的问题基本上是如何在Audioworklet中做上面的例子,

对于您的第一个示例,已经有一个 AudioWorklet 版本: https ://github.com/GoogleChromeLabs/web-audio-samples/blob/gh-pages/audio-worklet/basic/js/noise-generator.js

我不推荐第二个示例(也称为缓冲区拼接),因为它会创建大量源节点和缓冲区,因此可能会导致 GC 干扰主线程中的其他任务。如果计划的开始时间不落在样本上,则在两个连续缓冲区的边界处也可能发生不连续性。话虽如此,您将无法在此特定示例中听到故障,因为源材料是噪音。

当数据在某个数组的主线程中连续生成并且该数据的播放发生在 Webaudio 线程中时。

您应该做的第一件事是将音频生成器与主线程分开。音频发生器必须在AudioWorkletGlobalScope. 这就是 AudioWorklet 系统的全部目的——更低的延迟和更好的音频渲染性能。

您的代码中, VGMPlay_WebAudio.generateBuffer()应在AudioWorkletProcessor.process()回调中调用以填充处理器的输出缓冲区。这与您的onaudioprocess回调大致匹配。

我一直在阅读有关消息端口的内容,但我也不确定这是否可行。这些例子并没有将我指向我所说的那个方向。我可能需要的是用我自己的数据在 AudioWorkletProcesser 派生类中提供流程函数的正确方法。

我认为您的用例不需要MessagePort. 我在代码中看到了其他方法,但除了启动和停止节点之外,它们并没有做太多事情。这可以通过在主线程中连接/断开 AudioWorkletNode 来完成。不需要跨线程消息传递。

最后的代码示例可以是 AudioWorklet 的设置。我很清楚设置和实际音频生成之间的分离可能很棘手,但这是值得的。

给你几个问题:

  1. 游戏图形引擎如何向 VGM 生成器发送消息?
  2. 类可以VGMPlay在工作线程上运行而无需与主线程进行任何交互吗?除了启动和停止之外,我在代码中看不到任何交互。
  3. XMLHttpRequest必不可少的VGMPlay类?或者可以在其他地方完成吗?
于 2018-02-21T20:22:21.070 回答