Un accordeur chromatique (encore !) pour cornemuse

Bonjour tout le monde,
voilà je suis nouveau sur le forum, j'ai lu ce qu'il fallait lire au niveau conditions d'utilisation j'espère ne pas m'être planté sur certains points (!).
Bon concrètement comme l'indique le titre du topic mon projet est de réaliser un accordeur un peu particulier pour cornemuse, cornemuseS devrais-je dire !

Petit extrait du cahier des charges de l'accordeur nommé Bbraztuner 1.0 (Biniou braz = grande cornemuse en breton) doit :

  • pouvoir mesurer une fréquence variant de environ 80Hz/100Hz jusqu'autour de 1300Hz/1400Hz
  • informer le joueur de cornemuse (sonneur) de son accord et de ce qu'il doit faire pour etre accordé en fonction du diapason souhaité
  • se fixer sur l'instrument en question sans pour autant gêner le sonneur
  • permettre une visualisation optimale des résultats

etc... bon en gros la dedans il y a les principaux points concernant la phase codage qui consistera à faire tout le travail de mesure, et comparaison de fréquence et l'affichage sur l'écran TFT 2.8 1651 de adafruit.

Aussi, l'accordeur possède 2 voies de mesures de fréquence que l'on peut changer en appuyant sur un bouton poussoir.
J'ai 3 boutons en tout : 1 pour le changement de voie de mesure, et 2 pour augmenter le diapason souhaité ou le diminuer.

Donc par conséquent j'ai quelques ISR à gérer ( une pour les boutons, l'autre pour le CAN).
Comme un projet est toujours plus sympa à présenter en mots accompagnés d'images, voici quelques images issues de la conception 3D (l'image sur l'écran c'est juste pour avoir une idée de la zone d'affichage) =>

Pour ne pas perdre ceux qui ne connaissent pas "l'anatomie" de la bestiole :wink:


(source : http://www.sonerezh.net/pipe.jpg)

les micros (2) :
micros à contact à fixer sur les bourdons et/ou le chanter de la cornemuse

(source : http://www.saitenmarkt.com/shop/)

et en pièces jointes les photos de l'accordeur, en 3D puis à son stade actuel puis monté sur son support, sur ma cornemuse.

En recap, le but de ce projet (de bac j'ai oublié de préciser) est de pouvoir simplifier l'accordage d'une cornemuse, avec efficacité et rapidité. Parce que accorder son instrument tout seul à l'oreille c'est facile (encore faut-il avoir l'oreille musicale), mais quand on doit jouer ensemble à un diapason bien donné on est obligatoirement 2 pour accorder une cornemuse. 1 qui joue, l'autre qui accorde...Si vous jouez seul, mais avec un orchestre, eh bien la c'est la galère voila. Donc ce problème doit être totalement éliminé.

Maintenant j'ai pleiiin de questions arduinesques concernant mon programme qui je croyais serait simple à faire !

Tout d'abord la mesure de fréquence :

j'ai mon signal entrant amplifié, avec une composante continue de 2.5v et filtré si nécessaire mais au niveau soft comment mesurer la fréquence de mon signal entrant ?
Ca fait globalement depuis le début de l'année scolaire que je cherche une méthode fiable. Mais pour le moment mes tests sous proteus ne sont pas vraiment concluants. J'explique :

1 - avec le code d'Amanda Ghassaei (http://www.instructables.com/id/Arduino-Frequency-Detection/ )

Technique de calcule de pente max + recherche de pente très similaire dans la suite du signal entrant pour déterminer la période. Sur papier c'est Ok, en vrai c'est assez dépendant de la composition de on signal....Sinon la précision quand ça marche est de 'ordre du Hertz mais bon le son de la cornemuse est très complexe et même avec un filtrage il reste...complexe
Donc ça ne m'inspire pas confiance pour l'instant.

2 - avec le code d'akellyirl (technique de DSP, autocorrelation) (http://www.instructables.com/id/Reliable-Frequency-Detection-Using-DSP-Techniques/)

La c'est déjà beaucoup moins dépendant de a forme de mon signal donc ça parait plus fiable mais même avec une fréquence d'échantillonnage de 38.46kHz ( on respecte largement le théorème de Shannon vu que 1300Hz <= fmax <= 1400Hz ) on constate une erreur qui grandit en même temps que la fréquence augmente.... L'erreur n'est même pas de 3 voir 4Hz pour des fréquance d'ordre 102 jusqu'à 1300Hz. Je n'ai pas encore testé sur le réel et je devrais peut être bien m'y mettre mais au Lycée on a pas encore reçu tout le matériel !!!!! :roll_eyes:

Première question : connaissez vous de meilleures techniques de mesure de fréquences fondamentales utilisable sur une carte de type Uno, ou y'a-t-il moyen d'améliorer un de ces 2 programmes ? (j'ai mis pas mal de profs sur le coup, du coté de mon prof d'électronique..ben ça ne donne rien et les autres profs de sciences essayent de m'aider surtout au niveau maths mais bon pour l'instant rien de concluant)

deuxième question : sur le morceau de code suivant, celui de la méthode utilisant l'autocorrelation, je remplis un tableau de 800 valeurs de tension consécutives (copie d'au moins 1 période de mon signal) via l'ISR(ADC_Vect). Je souhaite qu'aucune interruption n puisse perturber l'execution de ma fonction d'autocorrelation mais le problème c'est qu'en simulation, une fois les 800 valeurs acquises, le tableau ne se rafraîchit plus ou alors les ISR restent inactives ou je ne sais pas...et malgré une étude approfondie des registres liés aux ISR ça ne marche toujours paaaaas ! alors est-ce-dû à mon logiciel de simulation ? Pour vous dire, ma variable "index" modifiée dans l'ISR(ADC_vect) est du coup censée être déclarée en "volatile" mais en simple "int" ça marche aussi sous proteus...Help please :frowning: :frowning: :frowning:

//Algo d'autocorrelation tiré d'un instructable : http://www.instructables.com/id/Reliable-Frequency-Detection-Using-DSP-Techniques/

#include <LiquidCrystal.h>;
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

/************Variables*****************/

////Variables relatives à la copie du signal
const int nbData = 800; // nombres d'échantillons du signal
char rawData[nbData] = {}; //tableau de stockage de la copie du signal
volatile unsigned int index = 0;

float frequency = 0;//variable pour stockage de la frequence mesurée

/*******Pour l'autocorrelation (admf)*************/
float sample_freq = 38461.54;//fréquence d'échantillonnage en Hz
 
void setup() {
  //Serial.begin(9600);
  lcd.begin(16,2);

  cli();
  
  ADCSRA = 0;//initialisation des registres ADCSRA et ADCSRB pour le CAN
  ADCSRB = 0;
  //mettre le voltage de référence (set reference voltage)
  //Alignement à gauche des bits -> resultat de la conversion dans registre ADCH
  ADMUX |= 0x60;
  //parametrage des prédiviseurs d'horloge pour le CAN à un prediviseur de 32 - 16mHz/32=500kHz = f_horlogeCAN
  //active le déclenchement automatique en gros dès que la CAN précédente est finie, on en recommnce diret une autre (enable auto trigger)
  //active l'interruption quand toutes les mesures sont faites (enable interrupts when measurement complete)
  //autorise la conversion (enable ADC)
  ADCSRA |= 0xad; 
  ADCSRA |= (1<<ADSC);//démarre la conversion (starts the ADC measurment)
  sei();
}

ISR(ADC_vect){//éxecute le code à chaque fin de conversion A/N
  if(index < nbData){//Si on a pas encore rempli un taleau de 800 échantillons
    rawData[index] = ADCH;//mettre la tension mesurée dans le tableau
    ++ index;
  }
}
void loop(){
  
  if(index == nbData){// si j'ai mes 800 valeurs
    //uint8_t saveSREG = SREG; //copie du registre pour pouvoir le restaurer après avoir traité l'admf
    //cli(); //désactiver toute interruption
    frequency = runAdmf();
    index = 0;
    //sei();
    //lcd.println(index);

    //SREG = saveSREG; //restaurer le SREG et re autoriser les interruptions  
  }
lcd.setCursor(0,0);
lcd.print(frequency);
//Serial.println(frequency); 
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////// Autocorrelation /////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////

float runAdmf(){

  int len = nbData; // copie du nombred d'échantillons pour l'admf
  int i,k;//compteurs
  long sum, sum_old;//résultats de correlation
  int thresh = 0;
  int period = 0;
  byte pd_state = 0;
  float freq_per = 0;
  sum = 0;
  pd_state = 0;
  
  // Autocorrelation
  for(i=0; i < len; i++)
  {
    sum_old = sum;
    sum = 0;
    for(k=0; k < len-i; k++) sum += (rawData[k])*(rawData[k+i])/256;

    //sum /= 256;
    
    // Peak Detect State Machine
    if (pd_state == 2 && (sum-sum_old) <=0) 
    {
      period = i-1;//à l'origine c'etait juste period = i mais c'est plus precis comme ça...
      pd_state = 3;
    }
    if (pd_state == 1 && (sum > thresh) && (sum-sum_old) > 0) pd_state = 2;
    if (!i) {
      thresh = sum * 0.5;
      pd_state = 1;
    }
  }
  // Frequence mesurée en Hz
  freq_per = sample_freq/period;
  return freq_per;
}

Voilà pour le début j'espère que le projet vous plaît et que vous voudrez bien m'aider (mon groupe et moi) :-[

Hugo :wink:

Toujours personne ? j'aurais trop écrit ? :frowning:

Hugo_Scott:
Toujours personne ? j'aurais trop écrit ? :frowning:

bonjour
nan ! , nan ! :grin:
C'est juste que l'arduino de base n'est pas specialement adapté à l"acquisition des sons
neanmoins regarde cette page

Bonjour

connaissez vous de meilleures techniques de mesure de fréquences fondamentales utilisable sur une carte de type Uno ... ?

meilleure ou pas .... à voir... en voilà une , autocorrelation aussi mais avec une approche algorithmique différente, qui d'après sa description parait plus adaptée aux contraintes d'un Mega328 :

Ha merci pour vos réponses !
Malheureusement la librairie FFT, et la FHT je les ai étudiées et ce n'est pas du tout ce qu'il me faut puisque je dois avoir une détection précise au Hz près...
et l'instructable a été testé et cela m'affiche...bouh de sales résultats assez aléatoires de plus il y a certaine choses dont je ne vois pas l'utilité dans le code pour réaliser une ADMF aussi "simple"...

j'ai étudié de près l'algo YIN et en le simplifiant je devrais arriver à quelque-chose de pas trop mal je pense. Après si quelqu'un l'a déjà adapté et qu'il n'y a plus qu'a étudier le code pour savoir comment l'utiliser ...:smiley:

En tous cas j'arrive au bout de l'exploration des techniques d'estimation de fréquence, mais je n'ai troujours pas tapé d'algo "précis" qui fonctionne !

Et sinon concernant mon souci avec l'ISR ?

Hugo_Scott:
...
En tous cas j'arrive au bout de l'exploration des techniques d'estimation de fréquence, mais je n'ai troujours pas tapé d'algo "précis" qui fonctionne !

C'est peut etre aussi comme déjà evoqué , parce que que l'arduino de base n'est pas vraiment le candidat ideal pour "jouer à ça" :grin:
Sous toutes reserves , jette quand meme un oeil sur les lib de PJRC
en particulier

Pas mal cette librairie, par contre je doute que ca fonctionne bien pour un signal issu du son de ma cornemuse... ! car cette librairie impose d'être en présence d'un signal "carré"...Le souci c'est que même après filtrage intensif le son de tous les instruments de musique et en particulier celui d'une cornemuse n'est pas transformable en signal carré (à mon niveau de connaissance...)
Aussi je me doute bien que l'arduino, UNO en plus, ne soit pas le meilleur concurrent pour ce genre d'application mais après tout je ne suis même pas sur que dans les accordeurs classiques le processeur soit plus "puissant"...ils y mettent quand même pas des i5 donc ça doit se faire non ?
Je vais encore étudier tout ça et je vous tiens au courant...

Sinon toujours rien pour mon souci avec l'ISR ? le problème viendrait-t-il de proteus ?

Bonjour,

Ce n'est qu'un avis mais je pense que mesurer à 1 Hz près des fréquences aussi basses ne fonctionnera pas.
Il faudrait peut-être mesurer la période ? (en us et moyenner sur qq valeurs ?)

D'autres avis ?

ben c'est ce qu'on fait la, on mesure bien la période, je ne vois pas comment faire autrement pour mesurer une fréquence :slight_smile: