簡體   English   中英

將基於腳本處理器的應用程序移植到 audioworklet

[英]Porting scriptprocessor based application to audioworklet

由於舊的 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線程中發生時。

我一直在閱讀有關 messageport 的內容,但我不確定這是否可行。 這些例子並沒有指向我要說的那個方向。 我可能需要的是使用我自己的數據在 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 系統的全部目的——更低的延遲和更好的音頻渲染性能。

你的代碼VGMPlay_WebAudio.generateBuffer()應該被稱為AudioWorkletProcessor.process()回調填補處理器的輸出緩沖器。 這大致匹配您的onaudioprocess回調所做的事情。

我一直在閱讀有關 messageport 的內容,但我不確定這是否可行。 這些例子並沒有指向我要說的那個方向。 我可能需要的是使用我自己的數據在 AudioWorkletProcesser 派生類中提供處理函數的正確方法。

我認為您的用例不需要MessagePort 我在代碼中看到了其他方法,但除了啟動和停止節點之外,它們實際上並沒有做太多事情。 這可以通過在主線程中連接/斷開 AudioWorkletNode 來完成。 不需要跨線程消息傳遞。

最后的代碼示例可以是 AudioWorklet 的設置。 我很清楚設置和實際音頻生成之間的分離可能很棘手,但這是值得的。

幾個問題問你:

  1. 游戲圖形引擎如何向 VGM 生成器發送消息?
  2. VGMPlay類可以在工作線程上運行而不與主線程進行任何交互嗎? 除了啟動和停止之外,我在代碼中沒有看到任何交互。
  3. XMLHttpRequestVGMPlay類是否必不可少? 或者可以在其他地方完成?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM