API Web Audio : pourquoi composer quand on peut coder ?

Publié: 2022-03-11

Le tout premier brouillon d'une API audio Web est apparu dans le W3C en 2011. Bien que l'audio dans les pages Web soit pris en charge depuis longtemps, un moyen approprié de générer de l'audio à partir du navigateur Web n'était pas disponible jusqu'à tout récemment. J'attribue personnellement cela à Google Chrome, car en ce qui concerne l'intérêt de Google, le navigateur a commencé à devenir la partie la plus importante d'un ordinateur. Vous vous souvenez peut-être que le domaine du navigateur Web n'a pas beaucoup changé jusqu'à l'apparition de Google Chrome. Si vous utilisiez le son dans une page Web à cette époque, cela aurait été une mauvaise décision de conception. Mais depuis que l'idée des expériences Web est apparue, l'audio Web a recommencé à avoir un sens. Les navigateurs Web sont aujourd'hui un autre outil d'expression artistique, et la vidéo et l'audio dans le navigateur Web y jouent un rôle essentiel.

API Web Audio : pourquoi composer quand on peut coder ?

API Web Audio : pourquoi composer quand on peut coder ?
Tweeter

L'API Web Audio peut être assez difficile à utiliser à certaines fins, car elle est encore en cours de développement, mais un certain nombre de bibliothèques JavaScript existent déjà pour faciliter les choses. Dans ce cas, je vais vous montrer comment démarrer avec l'API Web Audio en utilisant une bibliothèque appelée Tone.js. Avec cela, vous serez en mesure de couvrir la plupart des besoins sonores de votre navigateur en apprenant uniquement les bases.

Bonjour API Web Audio

Commencer

Nous allons commencer sans utiliser la bibliothèque. Notre première expérience consistera à créer trois ondes sinusoïdales. Comme ce sera un exemple simple, nous allons créer un seul fichier nommé hello.html, un fichier HTML nu avec une petite quantité de balisage.

 <!DOCTYPE html> <html> <head> <meta charset="utf‐8"> <title> Hello web audio </title> </head> <body> </body> <script> </script> </html>

Le cœur de l'API Web Audio est le contexte audio. Le contexte audio est un objet qui contiendra tout ce qui concerne l'audio Web. Il n'est pas considéré comme une bonne pratique d'avoir plus d'un contexte audio dans un même projet. Nous commencerons par instancier un contexte audio en suivant les recommandations données par la documentation de l'API Web Audio de Mozilla.

 var audioCtx = new (window.AudioContext || window.webkitAudioContext);

Fabriquer un oscillateur

Avec un contexte audio instancié, vous avez déjà un composant audio : le audioCtx.destination. C'est comme votre haut-parleur. Pour faire un son, vous devez le connecter à audioCtx.destination. Maintenant, pour produire du son, créons un oscillateur :

 var sine = audioCtx.createOscillator();

Super, mais pas assez. Il doit également être démarré et connecté à notre audioCtx.destination :

 sine.start(); sine.connect(audioCtx.destination);

Avec ces quatre lignes, vous aurez une page Web assez ennuyeuse qui joue un son sinusoïdal, mais vous comprenez maintenant comment les modules peuvent se connecter les uns aux autres. Dans le script suivant, il y aura trois tonalités en forme de sinus, connectées à la sortie, chacune avec une tonalité différente. Le code est très explicite :

 //create the context for the web audio var audioCtx = new (window.AudioContext || window.webkitAudioContext)(); //create, tune, start and connect each oscillator sinea, sineb and sinec var sinea = audioCtx.createOscillator(); sinea.frequency.value = 440; sinea.type = "sine"; sinea.start(); sinea.connect(audioCtx.destination); var sineb = audioCtx.createOscillator(); sineb.frequency.value = 523.25; sineb.type = "sine"; sineb.start(); sineb.connect(audioCtx.destination); var sinec = audioCtx.createOscillator(); sinec.frequency.value = 698.46; sinec.type = "sine"; sinec.start(); sinec.connect(audioCtx.destination);

Les oscillateurs ne se limitent pas aux ondes sinusoïdales, mais peuvent également être des triangles, des dents de scie, des carrés et des formes personnalisées, comme indiqué dans le MDN.

Patcher la logique de Web Audio

Ensuite, nous ajouterons un module de gain à notre orchestre de composants Web Audio. Ce module nous permet de changer l'amplitude de nos sons. Il s'apparente à un bouton de volume. Nous avons déjà utilisé la fonction connect pour connecter un oscillateur à la sortie audio. Nous pouvons utiliser la même fonction de connexion pour connecter n'importe quel composant audio. Si vous utilisez Firefox et que vous jetez un coup d'œil à la console audio Web, vous verrez ce qui suit :

Si nous voulons changer le volume, notre patch devrait ressembler à :

Ce qui signifie que les oscillateurs ne sont plus connectés à la destination audio, mais à un module de gain, et que ce module de gain est connecté à la destination. Il est bon de toujours imaginer que vous faites cela avec des pédales de guitare et des câbles. Le code ressemblera à ceci :

 var audioCtx = new (window.AudioContext || window.webkitAudioContext) // we create the gain module, named as volume, and connect it to our var volume = audioCtx.createGain(); volume.connect(audioCtx.destination); //these sines are the same, exept for the last connect statement. //Now they are connected to the volume gain module and not to the au var sinea = audioCtx.createOscillator(); sinea.frequency.value = 440; sinea.type = "sine"; sinea.start(); sinea.connect(volume); var sineb = audioCtx.createOscillator(); sineb.frequency.value = 523.25; sineb.type = "sine"; sineb.start(); sineb.connect(volume); var sinec = audioCtx.createOscillator(); sinec.frequency.value = 698.46; sinec.type = "sine"; sinec.start(); sinec.connect(volume); volume.gain.value=0.2;

Vous pouvez trouver la solution sur https://github.com/autotel/simple-webaudioapi/.

GainNode est l'unité d'effet la plus basique, mais il y a aussi un delay, un convolver, un filtre biquadratique, un stéréo panoramique, un wave shaper et bien d'autres. Vous pouvez récupérer de nouveaux effets à partir de bibliothèques telles que Tone.js.

Stocker l'un de ces patchs sonores dans des objets qui leur sont propres vous permettra de les réutiliser au besoin et de créer des orchestrations plus complexes avec moins de code. Cela pourrait faire l'objet d'un prochain article.

Simplifier les choses avec Tone.js

Maintenant que nous avons jeté un bref coup d'œil sur le fonctionnement des modules Vanilla Web Audio, jetons un coup d'œil à l'impressionnant framework Web Audio : Tone.js. Avec cela (et NexusUI pour les composants d'interface utilisateur), nous pouvons très facilement créer des synthés et des sons plus intéressants. Pour essayer des choses, créons un échantillonneur et appliquons-lui des effets interactifs utilisateur, puis nous ajouterons quelques commandes simples pour cet échantillon.

Échantillonneur Tone.js

Nous pouvons commencer par créer une structure de projet simple :

 simpleSampler |-- js |-- nexusUI.js |-- Tone.js |-- noisecollector_hit4.wav |-- sampler.html

Nos bibliothèques JavaScript résideront dans le répertoire js . Pour les besoins de cette démo, nous pouvons utiliser le fichier hit4.wav de NoiseCollector qui peut être téléchargé à partir de Freesound.org.

Tone.js fournit ses fonctionnalités via des objets Player. La capacité de base de l'objet est de charger un échantillon et de le lire en boucle ou une fois. Notre première étape ici consiste à créer un objet player dans une variable "sampler", à l'intérieur du fichier sampler.html :

 <!doctype html> <html> <head> <title> Sampler </title> <script type="text/javascript" src="js/nexusUI.js" ></script> <script type="text/javascript" src="js/Tone.js" ></script> <script> var sampler = new Tone.Player("noisecollector_hit4.wav", function() { console.log("samples loaded"); }); </script> </head> <body> </body> </html>

Notez que le premier paramètre du constructeur du lecteur est le nom du fichier WAV, et le second est une fonction de rappel. WAV n'est pas le seul type de fichier pris en charge et la compatibilité dépend davantage du navigateur Web que de la bibliothèque. La fonction de rappel s'exécutera lorsque le lecteur aura fini de charger l'échantillon dans sa mémoire tampon.

Nous devons également connecter notre échantillonneur à la sortie. La façon dont Tone.js procède est la suivante :

 sampler.toMaster();

… où sampler est un objet Tone.Player, après la ligne 10. La fonction toMaster est un raccourci pour connect(Tone.Master).

Si vous ouvrez votre navigateur Web avec la console de développement ouverte, vous devriez voir le message "échantillons chargés", indiquant que le lecteur a été créé correctement. À ce stade, vous voudrez peut-être entendre l'échantillon. Pour ce faire, nous devons ajouter un bouton à la page Web et le programmer pour lire l'échantillon une fois enfoncé. Nous allons utiliser un bouton NexusUI dans le corps :

 <canvas nx="button"></canvas>

Vous devriez maintenant voir un bouton arrondi en cours de rendu dans le document. Pour le programmer pour lire notre échantillon, nous ajoutons un écouteur NexusUI, qui ressemble à ceci :

 button1.on('*',function(data) { console.log("button pressed!"); })

Quelque chose d'exceptionnel à propos de NexusUI est qu'il crée une variable globale pour chaque élément NexusUI. Vous pouvez configurer NexusUI pour qu'il ne le fasse pas, et à la place n'avoir ces variables que dans nx.widgets[] en définissant nx.globalWidgets sur false. Ici, nous allons créer seulement quelques éléments, nous nous en tiendrons donc à ce comportement.

Comme dans jQuery, nous pouvons mettre ces événements .on, et le premier argument sera le nom de l'événement. Ici, nous attribuons simplement une fonction à tout ce qui est fait sur le bouton. Ce tout ce qui est écrit comme "*". Vous pouvez en savoir plus sur les événements de chaque élément de l'API NexusUI. Pour lire l'échantillon au lieu d'enregistrer des messages lorsque nous appuyons sur le bouton, nous devons exécuter la fonction de démarrage de notre échantillonneur.

 nx.onload = function() { button1.on('*',function(data) { console.log("button pressed!"); sampler.start(); }); }

Notez également que l'écouteur va à l'intérieur d'un rappel onload. Les éléments NexusUI sont dessinés dans le canevas et vous ne pouvez pas vous y référer tant que nx n'a pas appelé la fonction onload. Tout comme vous le feriez avec des éléments DOM dans jQuery.

L'événement est déclenché au clic de la souris et au relâchement. Si vous voulez qu'il soit déclenché uniquement à la presse, vous devez évaluer si event.press est égal à un.

Avec cela, vous devriez avoir un bouton qui lit l'échantillon à chaque pression. Si vous définissez sampler.retrigger sur true, cela vous permettra de lire l'échantillon, qu'il soit en cours de lecture ou non. Sinon, vous devez attendre la fin de l'échantillon pour le redéclencher.

Appliquer des effets

Avec Tone.js, on peut facilement créer un délai :

 var delay= new Tone.FeedbackDelay("16n",0.5).toMaster();

Le premier argument est le temps de retard, qui peut être écrit en notation musicale comme indiqué ici. Le second est le niveau wet, c'est-à-dire le mélange entre le son d'origine et le son qui a un effet sur lui. Pour les retards, vous ne voulez généralement pas un 100% humide, car les retards sont intéressants par rapport au son d'origine, et le humide seul n'est pas très attrayant car les deux ensemble.

L'étape suivante consiste à débrancher notre échantillonneur du maître et à le brancher à la place au délai. Ajustez la ligne où l'échantillonneur est connecté au maître :

 sampler.connect(delay);

Maintenant, essayez à nouveau le bouton et voyez la différence.

Ensuite, nous ajouterons deux cadrans au corps de notre document :

 <canvas nx="dial"></canvas> <canvas nx="dial"></canvas>

Et nous appliquons les valeurs des cadrans à l'effet de retard en utilisant le NexusUIlistener :

 dial1.on('*',function(data) { delay.delayTime.value=data.value; }) dial2.on('*',function(data) { delay.feedback.value=data.value; })

Les paramètres que vous pouvez modifier sur chaque événement se trouvent dans les documentations de Tone.js. Pour le retard, c'est ici. Vous êtes maintenant prêt à essayer l'exemple et à modifier les paramètres de délai avec les cadrans NexusUI. Ce processus peut être facilement effectué avec chaque élément NexusUI, sans se limiter aux seuls effets. Par exemple, essayez également d'ajouter un autre cadran et d'ajouter son écouteur comme suit :

 dial3.on('*',function(data) { sampler.playbackRate=data.value; })

Vous pouvez trouver ces fichiers sur github.com/autotel/simpleSampler

Conclusion

Lorsque j'ai parcouru ces API, j'ai commencé à me sentir submergé par toutes les possibilités et les idées qui ont commencé à me venir à l'esprit. La grande différence entre cette implémentation de l'audio et les implémentations traditionnelles de l'audio numérique ne réside pas dans l'audio lui-même, mais dans le contexte. Il n'y a pas de nouvelles méthodes de synthèse ici. L'innovation réside plutôt dans le fait que la création audio et musicale rencontre désormais les technologies Web.

Je suis personnellement impliqué dans la création de musique électronique, et ce domaine a toujours eu ce paradoxe de l'ambiguïté entre jouer réellement de la musique et simplement appuyer sur jouer sur une piste enregistrée. Si vous voulez vraiment faire de la musique électronique en direct, vous devez être capable de créer vos propres outils performatifs ou "robots créateurs de musique" pour l'improvisation en direct. Mais si la performance de la musique électronique devient simplement un réglage des paramètres dans des algorithmes de création musicale pré-préparés, alors le public peut également être impliqué dans ce processus. J'ai travaillé sur de petites expériences concernant cette intégration du web et de l'audio pour la musique crowdsourcée, et peut-être que bientôt nous assisterons à des soirées où la musique vient du public via leurs smartphones. Après tout, ce n'est pas si différent des jams rythmiques que nous aurions pu apprécier à l'époque des cavernes.


Lectures complémentaires sur le blog Toptal Engineering :

  • Tutoriel WebAssembly/Rust : traitement audio parfait
  • Didacticiel MIDI : Création d'applications audio basées sur un navigateur et contrôlées par du matériel MIDI