vendredi 15 janvier 2016

Mesurer la fréquence d'un son avec Arduino


Maintenant que je suis en mesure de capturer un signal audio avec mon Arduino, j'aimerais bien que l'Arduino détermine la fréquence fondamentale de ce signal. De cette façon, je pourrais utiliser l'Arduino pour accorder ma guitare.  Ou peut-être même que l'Arduino serait en mesure de reconnaître les notes jouées par la guitare et transmettre à un synthétiseur les messages MIDI correspondants?

Matériel

Aduino Uno, guitare électrique, et interface audio. L'interface audio joue deux rôles très importants: amplifier le minuscule signal électrique émis par la guitare électrique, et faire en sorte que ce signal amplifié demeure dans les limites de tension pouvant être supportées par une entrée de l'Arduino, c'est à dire entre 0 et 5 V. Une fois amplifié et additionné à une tension continue de 2,5 volts, le signal audio est acheminé à l'entrée analogique A0 de la carte Arduino.

Quelques notions de théorie concernant les sons

La fréquence d'un son émis par un instrument de musique détermine la hauteur de la note jouée.  Par exemple, un son dont la fréquence est de 262 Hz est un do, alors qu'un son dont la fréquence est de 440 Hz est un la (pour connaître la note associée à d'autres fréquences, voir ici).

Alors que la fréquence représente le nombre d'oscillations par seconde, la période est le temps nécessaire pour effectuer une oscillation complète.

Pour le signal audio ci-dessous, la période est d'environ 6 ms (ce qui correspond à une fréquence de 167 Hz).
Mesurer la fréquence d'un signal périodique simple comme un sinus ou une dent de scie n'a rien de bien compliqué:  la période est simplement égale au temps écoulé entre deux moments où le signal atteint sa valeur maximale, par exemple.  À la condition que la fréquence du signal soit significativement plus basse que la fréquence à laquelle l'Arduino effectue ses mesures, nous obtenons une mesure fiable au moyen d'un algorithme simple.

Mais les choses se compliquent sensiblement lorsqu'un désire déterminer la fréquence fondamentale d'une note produite par un instrument de musique.  Le signal se répète,  bien sûr, mais sa forme peut être très complexe:  il peut atteindre plusieurs fois la même valeur maximale à l'intérieure d'une même oscillation, traverser plusieurs fois la valeur médiane (qui est ici de 2,5 V) à l'intérieur d'une même oscillation, etc.  De plus, les oscillations ne sont pas toutes rigoureusement identique (par exemple, le signal émis par une corde de guitare devient de moins en moins intense avec le temps).

Le sketch d'Amanda Ghassaei

J'ai trouvé un sketch assez efficace proposé par la jeune physicienne Amanda Ghassaei sur le site Instructables.  Son sketch se trouve à la toute fin de l'article (4e page).

En gros, son algorithme consiste à mesurer la pente du signal chaque fois ce dernier passe par la valeur 2,5 V (qui est la valeur moyenne de notre signal audio), de chronométrer le temps écoulé entre chaque passage par cette valeur, et de vérifier s'il y a une répétition des paires pente/délai.

En général, le convertisseur analogique-numérique de l'Arduino n'est pas d'une très grande rapidité, ce qui peut devenir problématique lorsqu'on cherche à prendre des mesures sur un signal qui varie rapidement, comme c'est le cas ici.   Pour rendre l'Arduino plus rapide, Amanda Ghassaei manipule directement les ports de l'Atmega plutôt que d'utiliser des instructions typiques de la programmation Arduino comme "analogRead".

De plus, grâce à quelques lignes de code placés dans la partie "setUp" de son sketch, elle a modifié les paramètres du convertisseur analogique numérique. De cette façon, le taux d'échantillonnage de l'Arduino devient  38,5 kHz (c'est 4 fois plus rapide que la normale).  En contrepartie, sa résolution est diminuée ( les 5 volts sont réparties sur 255 valeurs possibles, alors que c'est généralement 1024) et les autres entrées analogiques de l'Arduino sont désactivée.

Tests du sketch d'Amanda Ghassaei avec ma guitare électrique

Voici ce que m'a présenté le moniteur série lorsque j'ai mis à l'essai le sketch d'Amanda Ghassei pour chacune des 6 cordes de ma guitare électrique:


Comme vous pouvez le constater, le sketch donne généralement une valeur plausible, mais de temps à autre (une ou deux fois sur dix), une fréquence incorrecte apparaît dans la liste.

Pour améliorer les performances de son programme, Amanda nous invite à modifier deux variables:  slopeTol et timerTol, qui contrôlent respectivement la tolérance lors de la comparaison des pentes et des durées chronométrées.  Je n'ai pas constaté d'améliorations notables en modifiant ces deux paramètres.

Le sktech modifié

J'ai plutôt décidé de filtrer les résultats, puisqu'ils ne sont mauvais qu'occasionnellement.

Avant d'afficher la fréquence au moniteur série, je m'assure d'abord qu'elle se trouve à l'intérieur des valeurs plausibles pour une guitare.   Ma guitare ne devrait pas produire des notes dont la fréquence est inférieure à 50 Hz ou supérieure à 1000 Hz, donc tous les résultats qui ne se situent pas entre 50 Hz et 1000 Hz sont nécessairement des anomalies, qui ne seront pas retenues.

De plus, chaque fois que la fréquence calculée est significativement différente de la fréquence précédente, j'attends une deuxième mesure similaire avant de l'afficher.

Voici ce que ça donne, suite à mes modifications:

(En fait, il arrive encore occasionnellement qu'une mauvaise fréquence soit affichée, mais c'est vraiment rare).

Et voici le sketch d'Amanda Ghassaei, avec une toute petite modification de ma part:


Oui, mais...

Les résultats sont corrects pour ma guitare électrique, mais ça ne fonctionne pas pour n'importe quel instrument.  Par exemple, pour le son de ma guitare acoustique capté par un micro, les faux résultats demeurent très nombreux malgré les modifications que j'ai apportées au sketch.  Pour les sons produits par un clavier musical, les résultats sont très variables dépendant du timbre choisi:  c'est parfois excellent, parfois catastrophiques.

On peut probablement obtenir un résultat intéressant pour un instrument donné en changeant la valeur des variables slopeTol et timerTol, mais il ne semble pas possible de rendre l'algorithme infaillible pour tous les sons possibles.

D'autres solutions

Il existe des algorithme plus robustes, mis au point par des mathématiciens dans le but spécifique de déterminer la fréquence fondamentale d'un signal sonore.  En gros, l'autocorrélation consiste à superposer deux segments sonores et déplacer progressivement l'un des deux segments jusqu'à ce qu'on trouve la position pour laquelle l'écart entre les deux signaux est minimal.   Puisque le sketch proposé plus haut semble répondre à mes besoins immédiats, je n'ai pas poussé très loin mon exploration du sujet, mais je vous indique rapidement quelques pistes intéressantes.

Voici d'abord un article du site Instructables (écrit par David Dein). Malheureusement, puisque ce sketch a été conçu pour un violon, il ne détecte pas les fréquences situées en-dessous de 196 Hz (qui correspond à la note la plus grave pouvant être émise par un violon).  C'est assez problématique pour une guitare, puisque la note la plus grave est de 82 Hz.  On peut certainement éliminer cet irritant en modifiant la valeur de la variable "delay_index_high_limit".  Je n'ai pas pris cette peine, ayant constaté que, même pour des notes au-dessus de 200 Hz, cet algorithme donnait des résultats pitoyables pour ma guitare électrique (il semblait beaucoup plus performant avec mon clavier, toutefois).

Frédéric Hamel a aussi utilisé un algorithme d'autocorrélation lors de la conception de son accordeur de guitare.  Il a été forcé de faire plusieurs compromis afin de rester à l'intérieur des limites de l'Arduino original, basé sur l'Atmega 168 (c'est un projet qui date de 2008).  Je n'ai pas pu tester les résultats car la portion de sketch concernant la détermination de la fréquence se trouve à l'intérieur du sketch complet  (incluant la gestion de l'afficheur LCD, l'envoi de messages MIDI, etc).

J'ai aussi trouvé ce projet très récent, apparemment mené par un garçon de 11 ans avec un peu d'aide de son père (mouais...).  C'est un projet assez complexe, mais très bien documenté,  et il semble que tout le nécessaire se trouve à l'intérieur du fichier "frequency.cpp".

Voilà!  J'espère que vous avez trouvé ici quelques informations utiles pour la réalisation de vos projet...

Yves Pelletier   (TwitterFacebook)

3 commentaires:

  1. Super intéressant, je viens de découvrir ton blog grâce a framboise 314

    Par Renoor974 : www.rennor974-2.blogspot.com

    RépondreSupprimer
  2. Bonjour,
    J'ai l'ambition qui dépasse mes capacités...
    J'aimerai capter le "chant de la reine vierge" :
    c'est un signal émis par une reine qui vient de naître afin que les autres reines à naitre lui répondent et ... elle les tue pour rester la seule nouvelle reine. Ce signal précède l'essaimage de la ruche de 12 à 24h,ce qui permettrait à un apiculteur d'agir.
    Le son émis est très caractéristique et très différent du bourdonnement habituel de la ruche, par exemple : https://www.youtube.com/watch?v=sCCX62l1iTg
    Pensez-vous cela possible avec un Arduino, un Due peut-être ?

    RépondreSupprimer
    Réponses
    1. Votre question est plus instructive pour moi que ma réponse ne le sera pour vous... Un Rasbperry Pi, peut-être, mais je n'ai aucune idée comment...

      Supprimer