Autres articles / Other articles

Synthèse sonore avec ESP32Forth

publication: 20 juillet 2022 / mis à jour 10 août 2022

Read this page in english

 

Appel à collaboration

Vous développez des montages, simples ou complexes avec ESP32 et ESP32forth.

Partagez-les ici sur ce site.

ESP32forth ne pourra se développer qu'avec la collaboration active de toutes les bonnes volontés.

Vos montages peuvent aider d'autres développeurs.

Les montages des autres développeurs peuvent vous aider.

Pour proposer un article ou un montage, cliquez ici



Pré-requis

Pour vos premières expérimentations sonores, il faut disposer d'un haut-parleur que vous connectez à une sortie GPIO. Mais l'impédance des haut-parleurs étant très basse, il faudra passer par un transistor. Voici le schéma conseillé pour un petit haut-parleur:

Sur ce schéma, il est mentionné le pin GPIO4. En fait, ce montage est utilisable sur n'importe quelle sortie GPIO de la carte ESP32. Les deux sorties qui vont plus particulièrement nous intéresser sont GPIO25 et GPIO26 qui sont réservées aux sorties DAC (Digital to Analog Conversion).

Synthèse sonore simple

Pour ce premier article, nous allons utiliser la génération de signaux PWM, mais sur les sorties DAC.

Notre buzzer est connecté à la sortie GPIO25, via le transistor PN2222A qui sert d'adapteur d'impédance.

 0 constant CHANNEL0     \ define PWM channel 0 
25 constant BUZZER      \ buzzer connected to GPI25 
 
ledc    \ select ledc vocabulary 
: initTones ( -- ) 
    BUZZER CHANNEL0 ledcAttachPin 
  ; 

Le mot initTones connecte la sortie GPIO25 au canal PWM 0. La génération d'un son s'effectue selon:

  CHANNEL0 freq ledcWriteTone drop

freq est la fréquence souhaitée, multipliée par 1000. Ainsi, pour générer la note LA (A en notation anglaise), dont la fréquence est de 440 Hz, il faudra utiliser la valeur 440*1000:

  CHANNEL0 440000 ledcWriteTone drop

Définition du tableau des fréquences sonores

Pour retrouver les fréquences sonores des notes de musique, nous sommes allés sur Wikipedia. On construit un tableau des fréquences, où chaque fréqence sera enregistrée sous sa forme utilisable par ledcWriteTone:

\ frequency notes 
\ source: https://fr.wikipedia.org/wiki/Note_de_musique 
\ frequency is multiplied by 1000 
create NOTES 
\ C          C#        D         D#        E         F         F#        G         G#        A         A#        B 
\ octave -1 
 15350 ,    17330 ,   18360 ,   19450 ,   20600 ,   21830 ,   23130 ,   24500 ,   25960 ,   27500 ,   29140 ,   30870 , 
\ octave 0 
 32700 ,    34650 ,   36710 ,   38890 ,   41200 ,   43650 ,   46250 ,   49000 ,   51910 ,   55000 ,   58270 ,   61740 , 
\ octave 1 
 65410 ,    69300 ,   73420 ,   77780 ,   82410 ,   87310 ,   92500 ,   98000 ,  103830 ,  110000 ,  116540 ,  123470 , 
\ octave 2 
130810 ,   138590 ,  146830 ,  155560 ,  164810 ,  174610 ,  185000 ,  196000 ,  207650 ,  220000 ,  233080 ,  246940 , 
\ octave 3 
261630 ,   277180 ,  293660 ,  311130 ,  329630 ,  349230 ,  369990 ,  392000 ,  415300 ,  440000 ,  466160 ,  493880 , 
\ octave 4 
523250 ,   554370 ,  587330 ,  622250 ,  659260 ,  698460 ,  739990 ,  783990 ,  830610 ,  880000 ,  932330 ,  987770 , 
\ octave 5 
1046500 , 1108730 , 1174660 , 1244510 , 1318510 , 1396910 , 1479980 , 1567980 , 1661220 , 1760000 , 1864660 , 1975530 , 
\ octave 6 
2093000 , 2217460 , 2349320 , 2489020 , 2637020 , 2793830 , 2959960 , 3135960 , 3322440 , 3520000 , 3729310 , 3951070 , 
\ octave 7 
4186010 , 4434920 , 4698640 , 4978030 , 5274040 , 5587650 , 5919910 , 6271930 , 6644880 , 7040000 , 7458620 , 7902130 , 
\ octave 8 
8372020 , 8869840 , 9397280 , 9956060 , 10548080 , 11175300 , 11839820 , 12543860 , 13289760 , 14080000 , 14917240 , 15804260 , 

Il y a douze notes par octave, d'où la définition de 12 valeurs par ligne. Ici, on enregistre seulement 10 lignes, soit 10 octaves. Car passé 15Khz, les sons ne seraient plus audibles.

Pour retrouver une note, il suffit de connaitre sa position dans une octave. Par exemple, notre note LA en octave 3 sera: ((octave+1)*12)+position. LA étant en 10ème position en octave 3, l'adresse à déterminer sera NOTES+4*((OCTAVE+1*12)+position)

Récupération de la fréquence d'une note de musique

On crée d'abord un mot set.octave qui nous permettra de sélectionner l'octave souhaitée. Ensuite, on définit get.note qui récupère la fréquence de la note souhaitée:

3 value OCTAVE 
\ select octave in interval -1..8 
: set.octave ( n[-1..8] ) 
    to OCTAVE 
  ; 
 
\ select note in interval 1..12 
: get.note ( n[1..12] -- ) 
    1- OCTAVE 1+ 12 * +  cell *     \ calc. offset in NOTES array 
    NOTES + @                       \ fetch frequency of selected note 
  ; 
 
3 value OCTAVE 
\ select octave in interval -1..8 
: set.octave ( n[-1..8] ) 
    to OCTAVE 
  ; 
 
: OCT6 ( -- )    6 set.octave ; 
: OCT5 ( -- )    5 set.octave ; 
: OCT4 ( -- )    4 set.octave ; 
: OCT3 ( -- )    3 set.octave ; 
: OCT2 ( -- )    2 set.octave ; 
: OCT1 ( -- )    1 set.octave ; 

Nous verrons plus loin comment gérer les notes en les appelant depuis leur notation.

Gestion de la durée des notes

La durée d'une note, c'est l'intervalle de temps qui sépare le déclenchement de deux notes consécutives.

Un délai de base est défini par la constante WHOLE-NOTE-DURATION.

Les durées sont définies dans un nouveau vocabulaire music:

1600 constant WHOLE-NOTE-DURATION 
 
WHOLE-NOTE-DURATION value duration 
 
vocabulary music 
music definitions 
music also 
 
\ set duration of a whole note 
: o  ( -- ) 
    WHOLE-NOTE-DURATION to duration 
  ; 
 
\ set duration of a white note 
: o|  ( -- ) 
    WHOLE-NOTE-DURATION 2/ to duration 
  ; 
 
\ set duration of a black note 
: .|  ( -- ) 
    WHOLE-NOTE-DURATION 2/ 2/ to duration 
  ; 
 
\ set duration of a half black note 
: .|'  ( -- ) 
    WHOLE-NOTE-DURATION 2/ 2/ 2/ to duration 
  ; 
 
\ set duration of a quarter black note 
: .|"  ( -- ) 
    WHOLE-NOTE-DURATION 2/ 2/ 2/ 2/ to duration 
  ; 

On définit des mots qui symbolisent les durées souhaitées: o pour une pleine note, \o pour une note blanche, \. pour une note noire, etc...

Soutien d'une note

Le soutien d'une note correspond au délai pendant lequel la note est audible pendant son temps d'exécution. On définit une valeur sustain qui exprime le pourcentage de soutien d'émission de la note pendant sa durée totale. Si cette valeur est à 100, les notes s'enchaineraient sans aucun silence entre les notes.

\ sustain of note, in interval [0..100] 
90 value SUSTAIN 
 
ledc 
\ sustain note in interval [0..100] 
: sustain.note ( -- ) 
    duration SUSTAIN 100 */ ms 
    CHANNEL0 0 ledcWriteTone drop 
    duration 100 SUSTAIN - 100 */ ms 
  ; 

Sur un synthétiseur, l'enveloppe d'un son est déterminé par quatre parties A D S R:

Dans cet article, les sons que nous générons n'ont qu'une partie sustain.

Le mot sustain.note génère deux délais. Le premier délai correspond à la durée du maintien de la note. Le second délai correspond à un délai de maintien du silence. La somme de ces deux délais correspond toujours au délai défini dans duration.

Création des notes musicales

On arrive à la partie la plus intéressante, définir les notes par leur nom:

: create-note 
    \ compile position in octave 
    create      ( position -- ) 
        , 
    \ get note frequency in current octave 
    does> 
        @ 1- get.note 
        CHANNEL0 swap ledcWriteTone drop 
        sustain.note 
  ; 
 
\ notes in english notation 
 1 create-note C 
 2 create-note C# 
 3 create-note D 
 4 create-note D# 
 5 create-note E 
 6 create-note F 
 7 create-note F# 
 8 create-note G 
 9 create-note G# 
10 create-note A 
11 create-note A# 
12 create-note B 
 
\ notes in french notation 
 1 create-note DO 
 2 create-note DO# 
 3 create-note RE 
 4 create-note RE# 
 5 create-note MI 
 6 create-note FA 
 7 create-note FA# 
 8 create-note SOL 
 9 create-note SOL# 
10 create-note LA 
11 create-note LA# 
12 create-note SI 
 
: SIL ( -- ) 
    CHANNEL0 0 ledcWriteTone drop 
    duration ms 
  ; 
 
forth definitions 

En plus des douzes notes, de DO à SI, on définit une notre SIL qui est un silence.

Test des notes

On teste toutes les notes, gammme par gamme:

forth definitions 
 
: music-scale ( -- ) 
    C C# D D# E F F# G G# A A# B 
  ; 
 
initTones 
forth also music also 
.| 
80 to SUSTAIN 
OCT1 music-scale 
OCT2 music-scale 
OCT3 music-scale 
OCT4 music-scale  
OCT5 music-scale 
OCT6 music-scale 

Si tout se passe bien, on doit dérouler toutes les notes de musique, par demi-tons, de l'octave 1 à l'octave la plus élevée, ici 6. On ne définit pas d'octave supplémentaire. C'est faisable. Mais les sons émis entrent dans une zone limite pour être audibles.

Rimski KORSAKOV: le vol du bourdon

ceci est un premier test de transposition d'une partition musicale. Pour ce faire, on récupère un morceau musical particulièrement difficile, le VOL DU BOURDON de Rimski KORSAKOV:

Voici la première mesure de la première ligne:

Voici comment on va coder cette première mesure, en notation française:

OCT5 MI RE# RE DO#     RE DO# DO  OCT4 SI 

Ou en notation anglaise:

OCT5 E D# D C#     D C# C OCT4 B 

Voici comment on code la première ligne de cette partition:

: 1stLine ( -- ) 
    .|"  ( duration of a quarter black note ) 
    OCT5 MI RE# RE DO#     RE DO# DO  OCT4 SI 
    OCT5 DO  OCT4 SI LA# LA     SOL# SOL FA# FA 
    MI RE# RE DO#    RE DO# DO OCT3 SI  
  ; 

Toutes mes excuses si j'ai fait des erreurs de traduction de la partition.

A ce stade, il est facile de tester cette ligne musicale:

: flightBumbleBee ( -- ) 
    initTones 
    1stLine 
  ; 
flightBumbleBee 

On code deux autres lignes:

: 2ndLine ( -- ) 
    .|"  ( duration of a quarter black note ) 
    OCT4 DO OCT3 SI FA# FA    SOL# SOL FA# FA 
    MI RE# RE DO#    RE DO DO# OCT2 SI OCT3 
    MI RE# RE DO#    RE DO DO OCT2 SI OCT3 
    MI RE# RE DO#    DO FA FA RE# 
  ; 
 
: 3rdLine ( -- ) 
    .|"  ( duration of a quarter black note ) 
    MI RE# RE DO#   DO DO# RE RE# 
    MI RE# RE DO#    DO FA FA RE# 
    MI RE# RE DO#    DO DO# RE RE# 
    MI RE# RE DO#    RE DO DO# OCT2 SI OCT3 
  ; 
 
: flightBumbleBee ( -- ) 
    initTones 
    1stLine 
    2ndLine 
    3rdLine 
  ; 
flightBumbleBee 

On vous laisse coder les trois autres lignes de la partition.

Pour notre part, nous verrons comment maitriser la forme des sons, leur enveloppe...


Legal: site web personnel sans commerce / personal site without seling