[Conseil] Réalisation d'un analyseur de son en temps réel

Bonjour,

Mise en contexte : Je poste sur ce forum car le confinement m'a poussé a m'interesser un peu plus a l'univers Arduino que j'avais rencontré au lycée en SI, je suis donc plutôt débutant mais je ne demande qu'a apprendre des choses !

J'ai pour projet de réaliser un analyseur de spectre audio, en fait je ne suis pas sur du terme exact mais voilà mon idée : j'aimerais traiter un signal audio provenant d'un microphone, puis le traiter pour le restituer sous forme de diagramme en barre via des LED pour créer une sorte de visualisation du son, je joint une photo de l'effet vers lequel j'aimerais tendre :

Je précise que mon but n'est pas d'avoir un analyseur de spectre précis, je cherche simplement une visualisation du son en temps réel.

Pour le traitement du signal provenant de mon microphone j'avais pensé a utiliser les librairies de transformation de Fourier rapide afin de découper les signal en plusieurs plages fréquences et retrouver les amplitudes qui y sont associées et ainsi commander les LED adressables a ma guise.
C'est ici que j'aurais besoin d'aide car il existe beaucoup de projets similaires sur internet (c'est d'ailleurs pour ça que je commence par là) mais je n'en trouve pas qui explique le fonctionnement des librairies FFT, j'ai simplement réussi pour l'instant a trouver la fréquence du pic d'amplitude sonore en utilisant un code trouvé sur internet.

Alors voilà, j'aimerai connaître l'avis de quelqu'un sur mon projet, si je ne m'attaque pas à trop ambitieux pour mon niveau ? Si l'idée de l'utilisation de FFT est bonne ou non et si oui si il existe un site pour apprendre a les utiliser peut-être ?

Le matériel dont je dispose pour l'instant:

-WS2811 LED Pixels Strip 30/48/60/96/144leds/m Addressable DC12V

-Alimentation 12V-2,5A

-Adafruit 1063 Electret Amplificateur de Microphone – MAX4466 avec Gain réglable

-Arduino UNO

Merci d'avance pour votre réponse :slight_smile:

Bonjour

mais je n'en trouve pas qui explique le fonctionnement des librairies FFT,

L'algorithme utlisé (Cooley-Turkey) demande un bon bagage mathématique pour être compreis dans les détails !!

C'est loin d'être intuitif !!

Souvent on se contente de l'aborder de manière empirique en jouant avec les paramètres de la librairie et en observant le résultat !

Le projet ne parait pas trop ambitieux.... si la fréquence d'échantillonage est compatible avec la carte Arduino (équivalente) ET si la taille de la FFT est compatible avec la RAM de la carte Arduino utilisée.

Voir ici une réalisatipon récente de lesept : https://forum.arduino.cc/index.php?topic=671963.0

Bonjour,
J' ai fait un truc qui tourne avec un MSGEQ7 pour des raisons similaires a celles de ton projet.

Le problème, c'est que ce circuit est souvent défectueux dès l'achat comme beaucoup l'ont malheureusement constaté.

++

Le signal électrique du micro est à moyenne nulle, c-à-d que des fois il est positif, des fois négatif.
Or la Uno ne sait mesurer que des tensions positives (entre 0 et 5V).
Je ne sais pas ce que sort ton module Adafruit, mais la 1ère chose à vérifier avant de faire une FFT, c’est que tu analyses bien tout le signal et pas seulement sa partie positive. Et aussi que le signal n’est pas écrêté.

Ensuite, pour pouvoir utiliser une FFT, il est indispensable :

  • que le signal soit échantillonné à intervalles de temps égaux,
  • de connaître cette fréquence d’échantillonnage (faire des tests…)

Enfin, il y a la puissance de calcul de l’Arduino, selon la finesse des calculs dont tu as besoin.
Pour faire une FFT, tu vas devoir en 1er lieu choisir la taille du buffet d’échantillons à analyser.
Plus il est grand, meilleure est la résolution de la FFT (tu as plus de bandes de fréquence),
mais plus floue est la localisation dans le temps (je pense que tu t’en fous un peu)

Comme tu ne fabriques pas un instrument de mesure, tu peux te permettre de “sauter” des intervalles, c-à-d:

  • acquérir un buffet d’échantillons
  • l’analyser
  • afficher les résultats,
  • recommencer. On a perdu du signal, mais pas grave.

Je ne peux pas te conseiller sur la taille optimale du buffet, ça dépend de ta fréquence d"échantillonnage.

Au final tu regroupes (tu sommes) les résultats de la FFT par tranches de fréquences, selon les besoin de ton affichage (8 tranches par ex.)

Le signal électrique du micro est à moyenne nulle, c-à-d que des fois il est positif, des fois négatif.
Or la Uno ne sait mesurer que des tensions positives (entre 0 et 5V).
Je ne sais pas ce que sort ton module Adafruit, mais la 1ère chose à vérifier avant de faire une FFT, c'est que tu analyses bien tout le signal et pas seulement sa partie positive. Et aussi que le signal n'est pas écrêté.

Le micro amplifié Adafruit cité délivre une tension centrée sur Vcc/2, il reste bien entendu à s'assurer de l'absence d'écrếtage.
La librairie arduinoFFT d'Enrique Condes, proposée dans le gestionnaire de librairie gère correctement l'acquisition , le remplissage du buffer et la FFT proprement dite.

Sur Leonardo, j'ai pu implanter une FFT en temps réel de taille 256 , ça occupe presque toute la RAM et ça sort une FFT avec 128 raies. Une FFT de taille 128 , n'occupe de la moitié de la RAM et il en reste sans doute assez pour le buffer de l'écran. Sur ESP8266 ou ESP32 une FFT de taille 1024 ne pose pas de problème et donnea ccès à une bien meilleure résolution.

Vu qu'il est question içi de regrouper les raies, pas besoin de pousser la taille FFT.
Une FFT de taille 128 (64 raies) suffit sans doute (la taille est nécessairement une puissance de 2 et le nombre de raies est la moitiée de la taille de la FFT)

la librarie arduinoFFT avec son exemple sortant les données dans le terminal permet de trouver le bon compromis de paramètres FFT avant d'ajouter ensuite la visualisation sur écran.

Pour cette bibliothèque "arduinoFFT d'Enrique Condes", je sais la télécharger, mais je ne sais pas ou trouver la documentation. C'est aussi le cas pour pas mal de bibliothèques. Comment trouve-t-on cette doc?

Bonsoir

on trouve un peu doc (API) sur ce dépot : GitHub - kosme/arduinoFFT: Fast Fourier Transform for Arduino

C’est sommaire mais ensuite en étudiant les exemples et en jouant avec on finit par se faire une idée (exploitable) du fonctionnement !

Je n’avais pas compris que “traduire les commentaires en anglais” cela s"signifiait “supprimer les commentaires”. Maintenant, je me dis que je serai capable d’écrire du code en anglais.

Je n’avais pas compris non plus que documenter une fonction c’était fournir le .h

Si j’ai bien compris, il est plus rapide de tout réécrire que de comprendre ce qui a déjà été fait. J’avais compris cela pour mon TFT sensitif, pour la gestion de mes moteurs pas à pas…

Voici le début d’un exemple. Au vu de ça a-t-on les moyens de savoir ce que va faire cet ino?

/*
	Example of use of the FFT libray to compute FFT for a signal sampled through the ADC.
        Copyright (C) 2018 Enrique Condés and Ragnar Ranøyen Homb
	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "arduinoFFT.h"

arduinoFFT FFT = arduinoFFT(); /* Create FFT object */
/*
These values can be changed in order to evaluate the functions
*/
#define CHANNEL A0
const uint16_t samples = 64; //This value MUST ALWAYS be a power of 2
const double samplingFrequency = 100; //Hz, must be less than 10000 due to ADC

Bonjour

Je prend cette librairie ‘comme elle est’ et pas ‘comme elle devrait être’ :slight_smile:

Disons que quand on a un peu pratiqué la FFT sur d’autres plateformes on arrive à utliser cette librairie malgré la faiblesse de la documentation.

Si j’ai bien compris, il est plus rapide de tout réécrire que de comprendre ce qui a déjà été fait.

Tout le monde n’a pas les compétences pour voir les choses ainsi.

Tout le monde n'a pas les compétences pour voir les choses ainsi.

Je ne parlerai pas de compétence lorsque je n'arrive pas à comprendre les bibliothèques...

Je mentionnait les compétences pour ce qui est de la création d'une librairie nouvelle, bien construite et bien documentée. Ne les possédant pas et n'ayant pas prévu à court terme de les acquérir, j'utilise l'existant, c'est plus rapide.

al1fch:
Bonjour
L'algorithme utlisé (Cooley-Turkey) demande un bon bagage mathématique pour être compreis dans les détails !!
Transformation de Fourier rapide — Wikipédia
C'est loin d'être intuitif !!

Souvent on se contente de l'aborder de manière empirique en jouant avec les paramètres de la librairie et en observant le résultat !

Le projet ne parait pas trop ambitieux.... si la fréquence d'échantillonage est compatible avec la carte Arduino (équivalente) ET si la taille de la FFT est compatible avec la RAM de la carte Arduino utilisée.

Voir ici une réalisatipon récente de lesept : https://forum.arduino.cc/index.php?topic=671963.0

Bonjour
Il y a çà qui est assez documenté qui tourne une base nano
j'ai l'impression que çà utilise la meme lib ou un fork que celle evoquée

la réalisation que tu montres, Artouste, utilise une autre libairie proposée dans le Gestionnaire de LIbrairies par la même personne (il n'est pas l'auteur de fix-FFT)

Voci la présentation des deux dans le gestionnaire de librairie :

J’ai examiné l’exemple sur ESP32 et l’ai réadapté pour mon utilisation, j’ai supprimé toutes les commandes liées à l’utilisation de l’écran car je n’en ai pas et ai tenté d’obtenir une simple variable correspondant a l’amplitude du signal a certaines fréquences données, pour ensuite pouvoir exploiter cette variable pour mes LEDs.

Cependant mon arduino UNO ne peut pas dépasser les 128 SAMPLES car 256 sature sa RAM, j’ai donc mon signal découpé en 64 plages de fréquences (si j’ai bien compris on ne peut récupérer que SAMPLES/2 plages), comment savoir à quelles fréquences correspondent les plages que j’obtiens ? Dois-je utiliser des plages de tailles égales ?

D’autre part le code que j’ai récupéré établis une fréquence d’échantillonnage de 40 000 Hz, a t’elle un impact ? Comment déterminer sa taille optimale pour mon utilisation ?

Je vous partage le code que j’ai adapté, je ne prétends pas avoir le niveau pour comprendre réellement ce que j’ai fait c’est pourquoi je demande un avis :slight_smile: Je sais juste que ce code me renvoie un nombre mais j’aimerais savoir s’il correspond réellement a quelque chose, j’ai gardé les écritures “125Hz :” , “250Hz” de l’ancien code mais je ne connais pas la valeur réelle de la fréquence correspondante

/* ESP8266/32 Audio Spectrum Analyser on an SSD1306/SH1106 Display
 * The MIT License (MIT) Copyright (c) 2017 by David Bird. 
 * The formulation and display of an AUdio Spectrum using an ESp8266 or ESP32 and SSD1306 or SH1106 OLED Display using a Fast Fourier Transform
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 
 * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 
 * publish, distribute, but not to use it commercially for profit making or to sub-license and/or to sell copies of the Software or to 
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:  
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
 * See more at http://dsbird.org.uk 
*/

#include <arduinoFFT.h> // Standard Arduino FFT library 
// https://github.com/kosme/arduinoFFT, in IDE, Sketch, Include Library, Manage Library, then search for FFT
arduinoFFT FFT = arduinoFFT();
/////////////////////////////////////////////////////////////////////////
// Comment out the display your nNOT using e.g. if you have a 1.3" display comment out the SSD1306 library and object
//#include "SH1106.h"     // https://github.com/squix78/esp8266-oled-ssd1306
//SH1106 display(0x3c, 21,22); // 1.3" OLED display object definition (address, SDA, SCL) Connect OLED SDA , SCL pins to ESP SDA, SCL pins

//#include "SSD1306.h"  // https://github.com/squix78/esp8266-oled-ssd1306
/////////////////////////////////////////////////////////////////////////
#define SAMPLES 128// Must be a power of 2
#define SAMPLING_FREQUENCY 40000 // Hz, must be 40000 or less due to ADC conversion time. Determines maximum frequency that can be analysed by the FFT Fmax=sampleF/2.
#define amplitude 20000            // Depending on your audio source level, you may need to increase this value
unsigned int sampling_period_us;
unsigned long microseconds;
double vReal[SAMPLES];
double vImag[SAMPLES];
unsigned long newTime, oldTime;
int Somme = 0;
int Moyenne = 0;
/////////////////////////////////////////////////////////////////////////
void setup() {
  Serial.begin(115200);
  sampling_period_us = round(1000000 * (1.0 / SAMPLING_FREQUENCY));
}

void loop() {
  for (int i = 0; i < SAMPLES; i++) {
    newTime = micros()-oldTime;
    oldTime = newTime;
    vReal[i] = analogRead(A0); // A conversion takes about 1uS on an ESP32
    vImag[i] = 0;
    while (micros() < (newTime + sampling_period_us)) { /* do nothing to wait */ }
  }
  FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
  FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
  for (int i = 2; i < (SAMPLES/2); i++) { // Don't use sample 0 and only first SAMPLES/2 are usable. Each array eleement represents a frequency and its value the amplitude
     
      if (i<=3 )  {
        Serial.print("125Hz :");
        Serial.println((int)vReal[i]);
      }
      
      if (i >4   && i<=8  ) {
        Serial.print("250Hz :");
        Serial.println((int)vReal[i]);
      }
        
      if (i >8   && i<=14 ) { 
      Serial.print("500Hz :");
        Serial.println((int)vReal[i]);
      }
 
      if (i >14   && i<=24 ) {
        Serial.print("1000Hz :");
        Serial.println((int)vReal[i]); 
      }  
        
      if (i >24  && i<=35 ) {
        Serial.print("2000Hz :");
        Serial.println((int)vReal[i]);
      }
       
      if (i >35  && i<=54 ){
        Serial.print("4000Hz :");
        Serial.println((int)vReal[i]); 
      }
        
      if (i >54  && i<=60 ){
        Serial.print("8000Hz :");
        Serial.println((int)vReal[i]); 
      }

      if (i >60          ){
        Serial.print("16000Hz :");
        Serial.println((int)vReal[i]); 
      }
       
}
      //Serial.println(i);
 
    

}

Et voilà le genre de réponse que j’obtiens sur la liaison série avec une musique en jouée devant le micro :

125Hz :1912
125Hz :5
250Hz :186
250Hz :27
250Hz :21
250Hz :31
500Hz :51
500Hz :42
500Hz :21
500Hz :50
500Hz :25
500Hz :18
1000Hz :5
1000Hz :15
1000Hz :20
1000Hz :12
1000Hz :15
1000Hz :4
1000Hz :13
1000Hz :25
1000Hz :13
1000Hz :12
2000Hz :5
2000Hz :30
2000Hz :4
2000Hz :6
2000Hz :10
2000Hz :12
2000Hz :6
2000Hz :8
2000Hz :5
2000Hz :5
2000Hz :5
4000Hz :14
4000Hz :9
4000Hz :12
4000Hz :4
4000Hz :3
4000Hz :8
4000Hz :8
4000Hz :5
4000Hz :5
4000Hz :9
4000Hz :2
4000Hz :5
4000Hz :8
4000Hz :5
4000Hz :1
4000Hz :6
4000Hz :5
4000Hz :3
4000Hz :6
8000Hz :2
8000Hz :11
8000Hz :9
8000Hz :8
8000Hz :6
8000Hz :6
16000Hz :8
16000Hz :4
16000Hz :5
125Hz :331
125Hz :124
250Hz :31
250Hz :21
250Hz :18
250Hz :36
500Hz :31
500Hz :19
500Hz :27
500Hz :28
500Hz :21
500Hz :16
1000Hz :10
1000Hz :3
1000Hz :7
1000Hz :5
1000Hz :9
1000Hz :7
1000Hz :9
1000Hz :7
1000Hz :6
1000Hz :4
2000Hz :4
2000Hz :4
2000Hz :5
2000Hz :1
2000Hz :4
2000Hz :4
2000Hz :5
2000Hz :7
2000Hz :5
2000Hz :8
2000Hz :4
4000Hz :0
4000Hz :5
4000Hz :5
4000Hz :7
4000Hz :7
4000Hz :3
4000Hz :2
4000Hz :1
4000Hz :2
4000Hz :2
4000Hz :1
4000Hz :4
4000Hz :5
4000Hz :5
4000Hz :2
4000Hz :2
4000Hz :4
4000Hz :6
4000Hz :9
8000Hz :6
8000Hz :3
8000Hz :1
8000Hz :1
8000Hz :1
8000Hz :2
16000Hz :2
16000Hz :6
16000Hz :4

j’aimerais savoir comment trouver la fréquence correspondant a la cariable retournée par le programme si cela est possible!

Et merci beaucoup pour toutes vos réponses !

Bonsoir

Les valeurs de fréqence listées dans le terminal sont justes si la fréquence d'échantillonage effective est égale à celle indiquée dans le paramètre SAMPLING_FREQUENCY.....ce qui suppose que dans ce dernier on n'a pas demandé une fréquence impossible à atteindre avec le matériel.

40000 Hz est excessif pour une carte UNO, la fréquence d'échantillonage plafonne à une valeur bien inférieure de l'ordre de 10kHz Le code , ne connaissant pas la véritable fréquence d'échantillonnage , donnera des valeurs de fréquence décalées.

Faire le test avec 8000 et non 40000 dans le cas de la carte UNO

Regrouper (additionner) ensuite les 'raies' en fonction des plages de fréquence que vous voulez visualiser

Merci beaucoup pour votre réponse je crois que c'est exactement ce que je n'avais pas compris !

Si je comprend bien, avec une fréquence d'échantillonnage de 8000 Hz, pour i = 2, j'obtiens l'amplitude correspondant a 8000 * (2/128) = 125 Hz dans mon cas ?
Etant donné que j'ai 128 samples, je ne peut exploiter que les 64 premières sorties et donc je n’accède qu'au maximum aux fréquences de 4000 Hz ?

Je me permet de rajouter quelque questions :

Comment savez-vous que la fréquence d’échantillonnage maximale de l'arduino est de l'ordre de 10 kHz, Ou trouve-t-on ces infos ?

D'autre part si je comprend bien :
-Plus on a de RAM plus le nombre de sample possible est important
-Plus la fréquence d’échantillonnage est haute plus l'analyse est effective sur les fréquence plus hautes

D’où viens ma question, est-il possible d'augmenter la RAM d'un arduino ? Y a t'il des cartes spécialement conçues pour ce type d'utilisation avec une fréquence d'échantillonnage plus haute ?

Comment savez-vous que la fréquence d'échantillonnage maximale de l'arduino est de l'ordre de 10 kHz, Ou trouve-t-on ces infos ?

Il y Arduino et Arduino....
-Il me semble avoir vu cette valeur dans un exemple pour la carte UNO, à l'occasion je vérifierai.
-injecter une fréqeunce connue puis tester des valeurs croissantes pour SAMPLING_FREQUENCY, on découvre alors le plafond, valeur à partir de laquelle les fréquences apparaisant dans le terminal deviennent fausses.

-Plus on a de RAM plus le nombre de sample possible est important
-Plus la fréquence d'échantillonnage est haute plus l'analyse est effective sur les fréquence plus hautes

La RAM conditionne la taille de la FFT et le nombre de samples
La fréquence d'échantillonnage conditionne la montée en fréqence de la FFT (avec Fech=10kHz on peut mesurer des raies jusqu'a 5kHz), la fréquence d'échantillonage doit être au moins le double de la fréquence maximale à mesurer.

(remarque : pour les essais de FFT audio Audacity peut servir de générateur basse fréquence)

Merci pour votre réponse je saisi mieux le fonctionnement désormais.

En ce qui concerne la valeur retournée par le " (int)vReal " y a t'il des bornes entre lesquelles la valeur est comprise, sur laquelle je pourrais appliquer un map() pour l'adapter ensuite ?

Comment savez-vous que la fréquence d’échantillonnage maximale de l’arduino est de l’ordre de 10 kHz, Ou trouve-t-on ces infos ?

Pour la uno, voici ce que dit la doc ATmega328:

24.1 Features
• 10-bit Resolution

• 13 - 260μs Conversion Time
• Up to 76.9kSPS (Up to 15kSPS at Maximum Resolution)

Pour une fréquence de 200khz il faut 13 cycles minimum pour faire la conversion. Au delà de cette fréquence, la résolution chute. Si on utilise l’horloge à 1Mhz, on peut pousser à 76kech/s mais on tombe en 8 bits. Je n’ai pas vu dans la doc la limite de fréquence pour l’ADC, mais je suppose que si on pouvait aller au delà des 1MHz, ils n’aurait as mis 76SPS ni 13µs
Mais la première conversion prend le double de temps.

Je ne sais pas comment travaille la librairie, passe-t-elle en 8 bits si on dépasse les 15000ech/s?

40000 Hz est excessif pour une carte UNO

Du coup non, c’est peut être excessif pour la bibliothèque, mais pas pour la Uno si on peut se contenter de 8 bits.