【问题标题】:Porting scriptprocessor based application to audioworklet将基于脚本处理器的应用程序移植到 audioworklet
【发布时间】:2018-02-22 04:22:21
【问题描述】:

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

首先是脚本处理器方式:

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(),连接它,它就会开始生成音频。当我没有这样的方法时,我无法想办法做到这一点。

所以我的问题基本上是如何在 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);
            }
    }

【问题讨论】:

  • 你能发布你尝试过的例子吗?从文档中添加代码很棒,但更重要的是添加您尝试过的代码。请参阅How to Ask,阅读Tour,尤其是阅读如何创建Minimal, Complete, and Verifiable example
  • 感谢您的回复泰勒。我已经添加了一些信息,我无法添加更多信息,因为我不知道接下来要尝试什么...

标签: javascript web-audio-api scriptprocessor


【解决方案1】:

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

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

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

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

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

your codeVGMPlay_WebAudio.generateBuffer() 应该在AudioWorkletProcessor.process() 回调中调用以填充处理器的输出缓冲区。这与您的 onaudioprocess 回调的功能大致相符。

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

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

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

问你几个问题:

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

【讨论】:

  • 感谢宏灿的回复!!噪声发生器对我来说似乎不同,因为它使用主线程中振荡器节点的启动方法,我的部分问题是我不知道如何在没有这样的源节点的情况下启动整个工作集,可以你请告诉我我会怎么做?这就是为什么我这样问的原因;只是在没有任何节点的主线程中的某个数组中创建随机噪声;我将如何将其发送到工作集?我猜振荡器的作用类似;在主线程中创建声音然后将其发送到工作集,对吗?
  • 回调的东西很有意思,但是要怎么给worklet中的process函数提供generate函数呢?
  • 我猜你的观点是正确的;整个音频生成可能存在于工作线程中,只向它发出 vgm 文件本身,其中包含将模拟芯片插入模拟声音芯片的实际命令。结果将是音乐。 :) 我通过 xmlhttprequest 加载它们并将它们放在 Emscripten 文件系统模拟器中。当 c 代码包含 fopen 时,转译后的 javascript 也将只打开文件,无需修改原始 C 代码。所以我不确定我是否可以在 Worklet 线程中做到这一点?
  • 所以,文件系统的事情很困难,主线程中数据生成的速度不是问题(甚至在我的一部不太快的 Android 手机上的 Chrome 上也能正常工作);所以假设我想保持这种状态,我只想使用工作集而不是脚本处理器。除了不使用已弃用的技术之外,这是否会为我提供您认为更低的 CPU 使用率或任何其他优势?
  • 问题2和3已经回答了,1的答案是没有图形引擎。 VGM 格式基本上只是发送到带有标题的声音芯片的所有指令的日志。主要挑战是从游戏中提取这些命令。完整的计算机模拟器是一个很好的方法,只需保存发送到模拟芯片的数据。
猜你喜欢
  • 2012-12-12
  • 1970-01-01
  • 2017-01-22
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
  • 2011-02-19
相关资源
最近更新 更多