Pesquisa de site

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.