Портирование скриптового приложения на аудиоворклет
Поскольку старый скрипт-процессор Webaudio устарел с 2014 года, а Audioworklets появились в Chrome 64, я решил попробовать их. Однако у меня возникают трудности с переносом приложения. Я приведу 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, но все они используют другие узлы, на которых можно просто запустить.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);
}
}
1 ответ
Итак, мой вопрос в основном заключается в том, как сделать приведенный выше пример в 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. Я хорошо знаю, что разделение между настройкой и фактическим поколением аудио может быть сложным, но оно того стоит.
Несколько вопросов к вам:
- Как графический движок игры отправляет сообщения в генератор VGM?
- Может ли
VGMPlay
класс жить на рабочем потоке без какого-либо взаимодействия с основным потоком? Я не вижу никакого взаимодействия в коде, за исключением запуска и остановки. - Является
XMLHttpRequest
важно дляVGMPlay
учебный класс? Или это можно сделать где-то еще?