Chrome 64 dispose d'une nouvelle fonctionnalité très attendue de l'API Web Audio : AudioWorklet Vous y découvrirez les concepts et leur utilisation pour créer sur mesure avec du code JavaScript. Consultez le démonstrations en direct. L'article suivant de la série, Modèle de conception de workflow audio, peut être intéressant pour créer une application audio avancée.
Arrière-plan: ScriptProcessorNode
Le traitement audio dans l'API Web Audio s'exécute dans un thread distinct du thread principal thread UI pour qu'il fonctionne correctement. Pour activer le traitement audio personnalisé dans JavaScript, l'API Web Audio, a proposé un ScriptProcessorNode qui utilisait des gestionnaires d'événements pour appeler un script utilisateur dans le thread UI principal.
Cette conception pose deux problèmes: la gestion des événements est asynchrone.
par défaut, et l'exécution du code a lieu sur le thread principal. L'ancienne
induit la latence, qui exerce une pression sur le thread principal
de nombreuses tâches liées à l'UI et au DOM, ce qui entraîne
à "à-coups" ou audio en "glitch". En raison de ce défaut
de conception fondamental,
ScriptProcessorNode
est obsolète dans la spécification et
remplacé par AudioWorklet.
Concepts
Le Worklet audio conserve le code JavaScript fourni par l'utilisateur dans
le thread de traitement audio. Ainsi, il n'est pas nécessaire de passer
pour traiter l'audio. Cela signifie que le code de script fourni par l'utilisateur
sur le thread de rendu audio (AudioWorkletGlobalScope
) ainsi que d'autres
AudioNodes
intégré, ce qui garantit aucune latence supplémentaire, et l'exécution
le rendu.
Enregistrement et instanciation
L'utilisation du Worklet audio comprend deux parties: AudioWorkletProcessor
et
AudioWorkletNode
Cette opération est plus complexe que
l'utilisation de ScriptProcessorNode.
mais elle est nécessaire pour donner aux développeurs les fonctionnalités de bas niveau nécessaires à l'audio personnalisé.
en cours de traitement. AudioWorkletProcessor
représente le processeur audio réel.
écrite en code JavaScript et se trouve dans AudioWorkletGlobalScope
.
AudioWorkletNode
est l'équivalent de AudioWorkletProcessor
et prend
s'occupe de la connexion vers et depuis les autres AudioNodes
du thread principal. Il
est exposé dans le champ d'application global principal et fonctionne comme un AudioNode
standard.
Voici une paire d'extraits de code illustrant l'inscription et le une instanciation.
// 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);
});
Pour créer un AudioWorkletNode
, vous devez ajouter un AudioContext.
et le nom du processeur sous forme de chaîne. Une définition de processeur peut être
chargé et enregistré par l'appel addModule()
du nouveau workflow audio.
Les API de Worklet, y compris Audio Worklet, ne sont disponibles que dans un
contexte sécurisé. Ainsi,
la page qui les utilise doit être diffusée via HTTPS, bien que http://localhost
soit
considéré comme sécurisé
pour les tests en local.
Vous pouvez sous-classer AudioWorkletNode
pour définir
un nœud personnalisé secondé par le processeur
qui s'exécute sur le 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);
La méthode registerProcessor()
dans AudioWorkletGlobalScope
prend une
chaîne pour le nom du processeur à enregistrer et la définition de classe.
Une fois l'évaluation du code du script terminée dans le champ d'application global,
la promesse de AudioWorklet.addModule()
sera résolue en notifiant les utilisateurs
que la définition de classe est prête à être utilisée dans le champ d'application global principal.
Paramètres audio personnalisés
Les paramètres programmables sont l'un des principaux avantages des AudioNodes.
avec AudioParam
. AudioWorkletNodes peut les utiliser pour obtenir
qui peuvent être contrôlés automatiquement
au débit audio.
Les paramètres audio définis par l'utilisateur peuvent être déclarés dans un AudioWorkletProcessor
définition de classe en configurant un ensemble de AudioParamDescriptor
. La
WebAudio sous-jacent récupère ces informations
d'un AudioWorkletNode, puis crée et associe
AudioParam
sur le nœud en conséquence.
/* 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.
}
}
}
Méthode AudioWorkletProcessor.process()
Le traitement audio réel a lieu dans la méthode de rappel process()
dans le
AudioWorkletProcessor
Elle doit être implémentée par un utilisateur de la classe.
définition. Le moteur WebAudio appelle cette fonction dans un environnement
pour alimenter les entrées et les paramètres, et extraire les sorties.
/* 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;
}
De plus, la valeur renvoyée par la méthode process()
peut être utilisée pour
contrôler la durée de vie de AudioWorkletNode
afin que les développeurs puissent gérer
l'espace mémoire utilisé. Renvoyez false
à partir de marques de méthode process()
.
le processeur est inactif, et le moteur WebAudio
n'appelle plus
. Pour que le processeur reste actif, la méthode doit renvoyer true
.
Sinon, la paire nœud/processeur est récupérée par le système.
à terme.
Communication bidirectionnelle avec MessagePort
Parfois, un élément AudioWorkletNode
personnalisé souhaite exposer des commandes qui ne
correspondre à AudioParam
, comme un attribut type
basé sur une chaîne
utilisées pour contrôler un filtre personnalisé. À cette fin et au-delà,
AudioWorkletNode
et AudioWorkletProcessor
sont équipés d'un
MessagePort
pour la communication bidirectionnelle. Tout type de données personnalisées
peuvent être échangées par le biais de ce canal.
MessagePort est accessible avec l'attribut .port
sur le nœud et
le processeur. La méthode port.postMessage()
du nœud envoie un message à
le gestionnaire port.onmessage
du processeur associé, et inversement.
/* 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
accepte les transferts, ce qui vous permet
ou un module WASM au-dessus de la limite d'un thread. Cela permet d'ouvrir
offre d'innombrables possibilités d'utilisation
du système Audio Worklet.
Tutoriel: Créer un GainNode
Voici un exemple complet de GainNode basé sur
AudioWorkletNode
et AudioWorkletProcessor
.
Le fichier 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>
Le fichier 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);
Il aborde les principes de base du système de Worklet audio. Des démonstrations en direct sont disponibles dans le dépôt GitHub de l'équipe Chrome WebAudio.
Transition de fonctionnalité: du test vers la version stable
Le Worklet audio est activé par défaut à partir de la version 66 de Chrome. Dans Chrome 64 et 65, cette fonctionnalité se trouvait derrière le drapeau expérimental.