由于自2014年起就不再使用旧的Webaudio脚本处理器,并且Audioworklets出现在Chrome 64中,因此我决定尝试一下。但是,我在移植应用程序时遇到困难。我将通过一个很好的article给出2个示例来说明我的观点。

首先是脚本处理器方式:

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有很多examples,但是它们都使用其他节点,可以在其上运行.start()并将其连接起来,它将开始生成音频。当我没有这样的方法时,我无法解决这个问题。

因此,我的问题基本上是在音频工作集中执行上面的示例时,当数据在某个数组的主线程中连续生成并且该数据的回放在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);
            }
    }

最佳答案

所以我的问题主要是如何在Audioworklet中执行上述示例,


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

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


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


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

your code中,
应该在VGMPlay_WebAudio.generateBuffer()回调中调用AudioWorkletProcessor.process()来填充处理器的输出缓冲区。这与您的onaudioprocess回调函数大致匹配。


  我一直在阅读有关消息端口的内容,但我不确定这也不是必经之路。这些例子并没有指出我要说的那个方向。我可能需要的是在AudioWorkletProcesser派生类中为处理功能提供我自己的数据的正确方法。


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

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

几个问题给您:


游戏图形引擎如何将消息发送到VGM生成器?
VGMPlay类可以在不与主线程进行任何交互的情况下驻留在工作线程上吗?除了启动和停止之外,我在代码中看不到任何交互。
XMLHttpRequestVGMPlay类所必需的吗?还是可以在其他地方完成?

关于javascript - 将基于脚本处理器的应用程序移植到audioworklet,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48874118/

10-12 13:00
查看更多