dimanche 14 avril 2019

Synthèse sonore avec Mozzi et Arduino (2)

Dans le billet précédent, nous avons vu comment produire un son avec Mozzi. Cette fois, nous allons  écrire un sketch qui joue une mélodie. Ensuite, nous améliorerons progressivement ce sketch en y ajoutant de la polyphonie, puis une enveloppe ADSR.

Jouer une mélodie: la classe EventDelay

Nous voulons donc, dans un premier temps, écrire un sketch qui jouera automatiquement une mélodie. L'algorithme sera donc : jouer la première note, attendre un peu, jouer la deuxième note, attendre un peu, etc.

Mais attention: la bonne vieille fonction "delay()" est désactivée dans Mozzi. Il faut plutôt utiliser la classe EventDelay, ce qui implique de démarrer le chronométrage ("start()"), puis de vérifier périodiquement si le délai est écoulé ou non ("ready()").

Vous en avez une illustration dans le sketch ci-dessous, qui joue de façon répétitive une suite de 12 notes au rythme de 4 notes par seconde.

Pour utiliser des délais dans le sketch, il faut d'abord inclure le fichier "EventDelay.h". C'est fait à la ligne 8 du sketch.

On doit ensuite définir un objet de type "EventDelay": c'est ce que j'ai fait à la ligne 14, je l'ai baptisé "attente".

À la ligne 26, je démarre un temps d'attente de 250 millisecondes grâce à la commande "attente.start(duree);".

Ensuite, à l'intérieur d'updateControl(), je vérifie si le délai est écoulé; ça commence à la ligne 31 du sketch ("if (attente.ready())"). Cette condition deviendra vraie 250 millisecondes après le démarrage du délai.

Lorsque les 250 millisecondes sont écoulées, les lignes 32 à 38 sont exécutées: elles consistent à modifier la valeur de la fréquence de l'oscillateur (ligne 32), à incrémenter la variable "compteur" qui indique le rang de la note à jouer (ligne 33), et à redémarrer le chronomètre pour un nouveau délai de 250 millisecondes (ligne 38).


Jouer des accords (polyphonie)

Rien ne nous oblige à nous limiter à jouer une note à la fois. Le sketch ci-dessous est très similaire au précédent, sauf que nous utilisons 3 oscillateurs afin de jouer trois notes simultanément.




Enveloppe ADSR

Nos deux programmes précédents donnent un résultat qui manque un peu d'expression, puisque chaque note (ou accord) est joué avec un volume sonore égal du début à la fin.  Pour améliorer les choses, nous allons maintenant définir une enveloppe ADSR qui nous permettra de modifier le volume pendant l'exécution de la note.

"ADSR" est l'acronyme pour Attack, Decay, Sustain et Release, quatre phases qui se succèdent pendant l'exécution d'une note.

  • L'attaque (attack) est la première phase; il s'agit du temps pendant lequel le volume de la note augmente progressivement d'une valeur nulle jusqu'à la valeur maximale. Pour un son percussif, on utilise une attaque courte (le son atteint instantanément son volume maximal), alors qu'une attaque longue donnera un résultat beaucoup plus doux (le volume augmente lentement au début de la note).
  • La chute (decay) est le temps pendant lequel le volume de la note diminue afin de passer de la valeur maximale (atteinte à la fin de l'attaque) jusqu'à une valeur un peu plus faible.
  • L'entretien (sustain) est le temps pendant lequel le volume de la note demeure constant.
  • L'extinction (release) est l'étape finale, pendant laquelle le volume de la note diminue progressivement jusqu'à devenir nul.

Le sketch ci-dessous joue 5 fois la même note en utilisant chaque fois une enveloppe dont les paramètres sont différents.

Cette fois, il est important d'inclure le fichier ADSR.h; c'est fait à la ligne numéro 10.

À la ligne 31, j'ai créé un objet de type ADSR que j'ai baptisé "enveloppe".

Les caractéristiques de l'enveloppe sont réglées aux lignes 44 et 47.

"setADLevels(niveau_attaque, niveau_chute)" (ligne 44) permet de régler le volume sonore atteint à la fin de l'attaque et le volume qui sera maintenu constant pendant la phase d'entretien. Les deux paramètres peuvent prendre n'importe quelle valeur entre 0 et 255.

À la ligne 47, "setTimes(durée_attaque, durée_chute, durée_entretien, durée_extinction)" permet de définir, en millisecondes, la durée de chacune des 4 phases de l'enveloppe. Il semble nécessaire d'éviter les durées inférieures à 20 ms, qui génèrent parfois des résultats indésirables.

À la ligne 60, "update()" met l'enveloppe à jour.

Finalement, la ligne 66 retourne la multiplication de notre enveloppe et de la note jouée par l'oscillateur principal. Il faut diviser par 256 (">> 8") pour que le résultat demeure à l'intérieur des limites requises.

Vous devriez entendre 5 notes qui ne diffèrent que par les paramètres de leur enveloppe.



Résultat final

Pour terminer, voici un autre sketch qui joue une suite d'accords mais, cette fois, je leur applique une enveloppe ADSL (définie dans setUp()). Sans l'enveloppe, ça sonnait un peu comme un orgue. Maintenant, c'est plus proche d'un accordéon...



Yves Pelletier   (TwitterFacebook)

1 commentaire:

  1. Excellente introduction à Mozzi et ses grands principes. D'un point de vue pédagogique, pour une bonne compréhension des décalages >>2, peut-être aurait-il mieux valu utiliser 4 oscillateurs de polyphonie ? Même si musicalement ce n'est pas idéal...
    En tout cas bravo, tout est dit dans vos articles.

    RépondreSupprimer