Le Worklet audio est désormais disponible par défaut

Hongchan Choi

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.

<ph type="x-smartling-placeholder">
</ph> Schéma du champ d&#39;application global principal et du champ d&#39;application du workflow audio <ph type="x-smartling-placeholder">
</ph> Fig.1
.

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.

<ph type="x-smartling-placeholder">
</ph> Schéma du nœud de workflow audio et du processeur <ph type="x-smartling-placeholder">
</ph> Fig.2
.

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.

<ph type="x-smartling-placeholder">
</ph> Fig.2 <ph type="x-smartling-placeholder">
</ph> Fig.2
.

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.