Mises à jour audio sur le Web dans Chrome 49

Chris Wilson
Chris Wilson

Chrome n'a cessé d'améliorer la compatibilité avec l'API Web Audio. Dans Chrome 49 (bêta à partir de février 2016 et qui devrait être stable en mars 2016), nous avons mis à jour plusieurs fonctionnalités pour suivre la spécification et ajouté un nouveau nœud.

decodeAudioData() renvoie désormais une promesse

La méthode decodeAudioData() sur AudioContext renvoie désormais un Promise, ce qui permet la gestion des modèles asynchrones basée sur la promesse. La méthode decodeAudioData() a toujours utilisé les fonctions de rappel de réussite et d'erreur comme paramètres:

context.decodeAudioData( arraybufferData, onSuccess, onError);

Mais maintenant, vous pouvez utiliser la méthode Promise standard pour gérer la nature asynchrone du décodage des données audio:

context.decodeAudioData( arraybufferData ).then(
        (buffer) => { /* store the buffer */ },
        (reason) => { console.log("decode failed! " + reason) });

Bien que, dans un seul exemple, cela semble plus détaillé, les promesses rendent la programmation asynchrone plus facile et plus cohérente. Pour des raisons de compatibilité, les fonctions de rappel de réussite et d'erreur sont toujours acceptées, conformément à la spécification.

Mise à jour hors connexion de l'audioContext, désormais compatible avec la fonction "suspend()" et "Resume()".

À première vue, il peut sembler étrange d'avoir suspend() sur un OfflineAudioContext. Après tout, suspend() a été ajouté à AudioContext pour permettre de mettre le matériel audio en mode veille, ce qui semble inutile dans les cas où vous effectuez un rendu dans un tampon (ce à quoi sert bien évidemment OfflineAudioContext). L'objectif de cette fonctionnalité est de ne pouvoir construire qu'une partie d'un "score" à la fois, afin de minimiser l'utilisation de la mémoire. Vous pouvez créer des nœuds pendant la suspension au milieu d'un rendu.

Par exemple, la sonate au clair de lune de Beethoven contient environ 6 500 notes. Chaque "note" se décompose probablement en au moins deux nœuds de graphe audio (par exemple, un AudioBuffer et un nœud Gain). Si vous souhaitez afficher l'intégralité des sept minutes et demie dans un tampon avec OfflineAudioContext, vous ne souhaitez probablement pas créer tous ces nœuds en même temps. Au lieu de cela, vous pouvez les créer par petits morceaux:

var context = new OfflineAudioContext(2, length, sampleRate);
scheduleNextBlock();
context.startRendering().then( (buffer) => { /* store the buffer */ } );

function scheduleNextBlock() {
    // create any notes for the next blockSize number of seconds here
    // ...

    // make sure to tell the context to suspend again after this block;
    context.suspend(context.currentTime + blockSize).then( scheduleNextBlock );

    context.resume();
}

Vous pourrez ainsi réduire le nombre de nœuds à précréer au début du rendu et diminuer les besoins en mémoire.

IIRFilterNode

La spécification a ajouté un nœud pour les audiophiles qui souhaitent créer leur propre infinite-impulse-response spécifiée avec précision : IIRFilterNode. Ce filtre complète le BiquadFilterNode, mais permet de spécifier complètement les paramètres de réponse du filtre (plutôt que le AudioParams facile à utiliser de BiquadFilterNode pour le type, la fréquence, le Q, etc.). IIRFilterNode permet de spécifier avec précision des filtres qui ne pouvaient pas être créés auparavant, comme les filtres à ordre unique. Toutefois, son utilisation nécessite une connaissance approfondie du fonctionnement des filtres IIR. De plus, ils ne sont pas programmables, contrairement à BiquadFilterNode.

Modifications précédentes

Je souhaite également mentionner quelques améliorations qui ont déjà été apportées: dans Chrome 48, l'automatisation du nœud BiquadFilter a commencé à s'exécuter à un débit audio. L'API n'a pas du tout changé pour ce faire, mais cela signifie que les balayages de vos filtres seront encore plus fluides. Toujours dans Chrome 48, nous avons ajouté un chaînage à la méthode AudioNode.connect() en renvoyant le nœud auquel nous nous connectons. Cela facilite la création de chaînes de nœuds, comme dans cet exemple:

sourceNode.connect(gainNode).connect(filterNode).connect(context.destination);

C'est tout pour aujourd'hui, et continue de bouger !