Chrome 64 include una nuova funzionalità molto attesa nell'API Web Audio: AudioWorklet. Imparerai i concetti di base e l'utilizzo per creare con un processore audio personalizzato con codice JavaScript. Dai un'occhiata dimostrazioni dal vivo. L'articolo successivo della serie, Audio Worklet Design Pattern, potrebbe essere una lettura interessante per sviluppare un'app audio avanzata.
Spiegazione: ScriptProcessorNode
L'elaborazione dell'audio nell'API Web Audio viene eseguita in un thread separato da quello principale Thread UI, per un funzionamento ottimale. Per attivare l'elaborazione audio personalizzata in JavaScript, l'API Web Audio ha proposto uno ScriptProcessorNode che utilizzava per richiamare lo script utente nel thread principale dell'interfaccia utente.
Questa progettazione presenta due problemi: la gestione degli eventi è asincrona
e l'esecuzione del codice avviene sul thread principale. Il primo
causa la latenza e quest'ultima fa pressione sul thread principale che è
spesso affollate di varie attività relative all'interfaccia utente e al DOM che causano
a "jank" o audio in "glitch". A causa di questo fondamentale difetto di progettazione,
ScriptProcessorNode
è deprecato dalla specifica e
sostituito con AudioWorklet.
Concetti
Audio Worklet conserva il codice JavaScript fornito dall'utente all'interno
thread di elaborazione audio. Ciò significa che non deve passare direttamente alla
thread per elaborare l'audio. Ciò significa che il codice dello script fornito dall'utente viene eseguito
sul thread del rendering audio (AudioWorkletGlobalScope
) insieme ad altri
AudioNodes
integrato, che garantisce latenza sincrona e zero latenza aggiuntiva
per il rendering delle immagini.
Registrazione e creazione di istanze
L'utilizzo del worklet audio è costituito da due parti: AudioWorkletProcessor
e
AudioWorkletNode
. Questa operazione è più complessa dell'utilizzo di ScriptProcessorNode,
ma è necessario per offrire agli sviluppatori la capacità di basso livello per
e l'elaborazione dei dati. AudioWorkletProcessor
rappresenta l'effettivo processore audio
è scritto nel codice JavaScript e si trova nel file AudioWorkletGlobalScope
.
AudioWorkletNode
è la controparte di AudioWorkletProcessor
e richiede
della connessione da e verso altri AudioNodes
nel thread principale. it
è esposto nell'ambito globale principale e funziona come un normale AudioNode
.
Ecco una coppia di snippet di codice che dimostrano la registrazione e e creazione di un'istanza.
// The code in the main global scope.
class MyWorkletNode extends AudioWorkletNode {
constructor(context) {
super(context, 'my-worklet-processor');
}
}
let context = new AudioContext();
context.audioWorklet.addModule('processors.js').then(() => {
let node = new MyWorkletNode(context);
});
Per creare un AudioWorkletNode
, devi aggiungere un AudioContext
e il nome del processore come stringa. Una definizione di processore può essere
caricati e registrati dalla chiamata addModule()
del nuovo oggetto Audio Worklet.
Le API Worklet, incluso il Worklet audio, sono disponibili solo in un
contesto sicuro,
la pagina che le utilizza deve essere pubblicata tramite HTTPS, sebbene http://localhost
sia
considerata sicura per i test locali.
Puoi definire la sottoclasse AudioWorkletNode
un nodo personalizzato supportato dal processore in esecuzione sul worklet.
// This is the "processors.js" file, evaluated in AudioWorkletGlobalScope
// upon audioWorklet.addModule() call in the main global scope.
class MyWorkletProcessor extends AudioWorkletProcessor {
constructor() {
super();
}
process(inputs, outputs, parameters) {
// audio processing code here.
}
}
registerProcessor('my-worklet-processor', MyWorkletProcessor);
Il metodo registerProcessor()
in AudioWorkletGlobalScope
utilizza un
stringa per il nome del processore da registrare e la definizione della classe.
Dopo il completamento della valutazione del codice dello script in ambito globale,
la promessa di AudioWorklet.addModule()
verrà risolta quando invii una notifica agli utenti
che la definizione della classe sia pronta per essere usata nell'ambito globale principale.
Parametri audio personalizzati
Uno degli aspetti utili di AudioNodes è il parametro pianificabile
automatica con AudioParam
. AudioWorkletNodes può usarli per ottenere
parametri esposti che possono essere controllati automaticamente alla velocità audio.
I parametri audio definiti dall'utente possono essere dichiarati in un AudioWorkletProcessor
definizione della classe mediante un insieme di AudioParamDescriptor
. La
il motore WebAudio sottostante raccoglie queste informazioni durante
di un AudioWorkletNode, quindi crea e collega
AudioParam
di oggetti nel nodo di conseguenza.
/* A separate script file, like "my-worklet-processor.js" */
class MyWorkletProcessor extends AudioWorkletProcessor {
// Static getter to define AudioParam objects in this custom processor.
static get parameterDescriptors() {
return [{
name: 'myParam',
defaultValue: 0.707
}];
}
constructor() { super(); }
process(inputs, outputs, parameters) {
// |myParamValues| is a Float32Array of either 1 or 128 audio samples
// calculated by WebAudio engine from regular AudioParam operations.
// (automation methods, setter) Without any AudioParam change, this array
// would be a single value of 0.707.
const myParamValues = parameters.myParam;
if (myParamValues.length === 1) {
// |myParam| has been a constant value for the current render quantum,
// which can be accessed by |myParamValues[0]|.
} else {
// |myParam| has been changed and |myParamValues| has 128 values.
}
}
}
Metodo AudioWorkletProcessor.process()
L'elaborazione effettiva dell'audio avviene nel metodo di callback process()
nella sezione
AudioWorkletProcessor
. Deve essere implementato da un utente del corso
definizione di Kubernetes. Il motore WebAudio richiama questa funzione in modo sincronizzato
per alimentare input e parametri e recuperare gli output.
/* AudioWorkletProcessor.process() method */
process(inputs, outputs, parameters) {
// The processor may have multiple inputs and outputs. Get the first input and
// output.
const input = inputs[0];
const output = outputs[0];
// Each input or output may have multiple channels. Get the first channel.
const inputChannel0 = input[0];
const outputChannel0 = output[0];
// Get the parameter value array.
const myParamValues = parameters.myParam;
// if |myParam| has been a constant value during this render quantum, the
// length of the array would be 1.
if (myParamValues.length === 1) {
// Simple gain (multiplication) processing over a render quantum
// (128 samples). This processor only supports the mono channel.
for (let i = 0; i < inputChannel0.length; ++i) {
outputChannel0[i] = inputChannel0[i] * myParamValues[0];
}
} else {
for (let i = 0; i < inputChannel0.length; ++i) {
outputChannel0[i] = inputChannel0[i] * myParamValues[i];
}
}
// To keep this processor alive.
return true;
}
Inoltre, il valore restituito del metodo process()
può essere utilizzato per
controllare l'intera durata di AudioWorkletNode
per consentire agli sviluppatori
l'impronta di memoria. Restituzione di false
da process()
segni di metodo
processore non attivo e il motore WebAudio
non richiama più
. Per mantenere attivo il processore, il metodo deve restituire true
.
In caso contrario, la coppia di nodo e processore viene garbage collection dal sistema.
e alla fine.
Comunicazione bidirezionale con MessagePort
A volte, un AudioWorkletNode
personalizzato vuole esporre controlli che non
viene mappato a AudioParam
, ad esempio un attributo type
basato su stringa
utilizzata per controllare un filtro personalizzato. A questo scopo e non solo,
AudioWorkletNode
e AudioWorkletProcessor
dispongono di un
MessagePort
per la comunicazione bidirezionale. Qualsiasi tipo di dati personalizzati
possono essere scambiati
tramite questo canale.
È possibile accedere a MessagePort con l'attributo .port
sia sul nodo che
per il processore. Il metodo port.postMessage()
del nodo invia un messaggio a
al gestore port.onmessage
del processore associato e viceversa.
/* The code in the main global scope. */
context.audioWorklet.addModule('processors.js').then(() => {
let node = new AudioWorkletNode(context, 'port-processor');
node.port.onmessage = (event) => {
// Handling data from the processor.
console.log(event.data);
};
node.port.postMessage('Hello!');
});
/* "processors.js" file. */
class PortProcessor extends AudioWorkletProcessor {
constructor() {
super();
this.port.onmessage = (event) => {
// Handling data from the node.
console.log(event.data);
};
this.port.postMessage('Hi!');
}
process(inputs, outputs, parameters) {
// Do nothing, producing silent output.
return true;
}
}
registerProcessor('port-processor', PortProcessor);
MessagePort
supporta il trasferimento, che ti consente
dell'archiviazione dei dati o di un modulo WASM al limite del thread. Si apre
e offre innumerevoli possibilità di utilizzo del sistema Audio Worklet.
Procedura dettagliata: creazione di un GainNode
Ecco un esempio completo di GainNode basato su
AudioWorkletNode
e AudioWorkletProcessor
.
Il file index.html
:
<!doctype html>
<html>
<script>
const context = new AudioContext();
// Loads module script with AudioWorklet.
context.audioWorklet.addModule('gain-processor.js').then(() => {
let oscillator = new OscillatorNode(context);
// After the resolution of module loading, an AudioWorkletNode can be
// constructed.
let gainWorkletNode = new AudioWorkletNode(context, 'gain-processor');
// AudioWorkletNode can be interoperable with other native AudioNodes.
oscillator.connect(gainWorkletNode).connect(context.destination);
oscillator.start();
});
</script>
</html>
Il file gain-processor.js
:
class GainProcessor extends AudioWorkletProcessor {
// Custom AudioParams can be defined with this static getter.
static get parameterDescriptors() {
return [{ name: 'gain', defaultValue: 1 }];
}
constructor() {
// The super constructor call is required.
super();
}
process(inputs, outputs, parameters) {
const input = inputs[0];
const output = outputs[0];
const gain = parameters.gain;
for (let channel = 0; channel < input.length; ++channel) {
const inputChannel = input[channel];
const outputChannel = output[channel];
if (gain.length === 1) {
for (let i = 0; i < inputChannel.length; ++i)
outputChannel[i] = inputChannel[i] * gain[0];
} else {
for (let i = 0; i < inputChannel.length; ++i)
outputChannel[i] = inputChannel[i] * gain[i];
}
}
return true;
}
}
registerProcessor('gain-processor', GainProcessor);
Questa lezione tratterà le nozioni fondamentali del sistema Audio Worklet. Sono disponibili demo dal vivo nel repository GitHub del team Chrome WebAudio.
Transizione delle funzionalità: da sperimentale a stabile
Audio Worklet è abilitato per impostazione predefinita per Chrome 66 o versioni successive. Nelle versioni 64 e 65 di Chrome, la funzionalità era dietro il flag sperimentale.