javascript - Eventos da Web Audio API são executados em uma thread separada?
Estou particularmente interessado no onaudioprocess do ScriptProcessorNode (até recentemente chamado de JavaScriptNode). É um listener de eventos que é chamado periodicamente para processamento de áudio. Ele é executado em uma thread separada?
Eu gostaria de alimentar os dados em um buffer circular e processá-los fora deste callback para não sobrecarregar a CPU. Posso usar web workers para o processamento assíncrono, mas pelo que eu sei, eu precisaria de uma implementação diferente do buffer circular no caso de diferentes threads.
Existe uma maneira de testar isso?
Resposta #1
Todo JavaScript é de thread única, executado de forma síncrona. Qualquer coisa assíncrona é feita via eventos, que adicionam seus manipuladores à fila de tarefas - para serem executados quando a tarefa atual for concluída.
Para usar threads separadas, você precisaria de um ambiente como WebWorkers - cada thread tem seu próprio contexto de execução (escopo global) e fila de tarefas; a comunicação entre eles é feita via eventos.
Como o manipulador onaudioprocess parece viver no mesmo escopo que o DOM, é bastante improvável que ele seja executado em sua própria thread. Se você realmente tiver uma tarefa computacionalmente intensiva que torna sua página não responsiva, você deve usar um WebWorker para o qual você envia os eventos de áudio:
myScriptProcessorNode.onaudioprocess = myWebWorker.postMessage;
Resposta #2
Com a solução de Bergi, você vai enfrentar problemas com o algoritmo de clone estruturado não sendo capaz de copiar parâmetros somente leitura no audioProcessingEvent. O que você precisa fazer é separar as partes do evento que podem ser clonadas e passá-las para o seu worker em uma estrutura de dados diferente, assim:
_onAudioProcess(audioProcessingEvent) {
const {inputBuffer, outputBuffer} = audioProcessingEvent;
// The output buffer contains the samples that will be modified and
// eventually played, so we need to keep a reference to it.
this._outputBuffer = outputBuffer;
const numChannels = inputBuffer.numberOfChannels;
const inputChannels =
Array.from({length: numChannels}, (i) => {
return inputBuffer.getChannelData(i);
});
this._worker.postMessage({
command: 'DO_STUFF',
inputChannels: inputChannels,
});
}
Você também precisará acessar a referência ao seu outputBuffer em setMessageHandler, a fim de copiar os dados processados de volta para serem eventualmente reproduzidos pelo usuário.
