Reconnaissance de note de musique (guitare)

Comment reconnait/valide-t-on une note ?

Le problème exposé dans ce sujet a été résolu.

Bonjour,

Je suis entrain de programmer une petite application pour reconnaitre ou vérifier qu'une note de guitare soit jouer correctement. Pour streamer l'audio, je n'ai aucun soucis tout se passe bien avec AudioAnalyzer. J'ai aucun soucis quand j'essaye un son avec une fréquence fixe, mais lorsque j'essaye avec une des notes suivantes, je ne sais pas comment faire.

Voici des MP3 pré-enregistrés :

Corde Vide 1 2 3 4
1ère vide E4 1 F4 2 F#4 3 G4
2ème vide B3
3ème
4ème
5ème vide A2 4 C#3
6ème

J'utilise ces images pour trouver la correspondance entre note et case : http://imgur.com/a/rermN.

J'ai aussi ce tableau pour la correspondance des notes :

En principe, d'après ce tableau, voici l'équivalence note <=> fréquence :

Note Hz
E4 329.63
F4 349.23
F#4 369.99
G4 392
- -
B3 246.94
- -
A2 110
C#3 138.59

Je voulais savoir si vous avez des éléments pour m'aider (documentation/site) ou m'expliquer comment je dois m'y prendre pour reconnaitre une note ?

Car je ne sais pas du tout comment je dois m'y prendre, et j'aimerai bien comprendre comment ça fonctionne.

Quand j'ai une fréquence fixe, j'utilise ce script qui me renvoie la fréquence sous la variable "pitch" :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
using UnityEngine;

public class AudioMeasureCS : MonoBehaviour {
  public float RmsValue;
  public float DbValue;
  public float PitchValue;
  public static float sPitchValue;

  public AudioSource AudioSource;
  public UnityEngine.UI.Text display; // drag a GUIText here to show results

  private const int QSamples = 1024;
  private const float RefValue = 0.1f;
  private const float Threshold = 0.02f;

  float[] _samples;
  private float[] _spectrum;
  private float _fSample;

  void Start() {
      if (!AudioSource) {
          Debug.Log("Default AudioSource loaded.");
          AudioSource = GetComponent<AudioSource>();
      }

      _samples = new float[QSamples];
      _spectrum = new float[QSamples];
      _fSample = AudioSettings.outputSampleRate;
  }


  void Update() {
      AnalyzeSound();
      if (display) {
          display.text = "RMS: " + RmsValue.ToString("F2") +
              " (" + DbValue.ToString("F1") + " dB)\n" +
              "Pitch: " + PitchValue.ToString("F0") + " Hz";
      }
      sPitchValue = PitchValue;
  }

  void AnalyzeSound() {
      AudioSource.GetOutputData(_samples, 0); // fill array with samples
      int i;
      float sum = 0;
      for (i = 0; i < QSamples; i++) {
          sum += _samples[i] * _samples[i]; // sum squared samples
      }
      RmsValue = Mathf.Sqrt(sum / QSamples); // rms = square root of average
      DbValue = 20 * Mathf.Log10(RmsValue / RefValue); // calculate dB
      if (DbValue < -160) DbValue = -160; // clamp it to -160dB min
                                          // get sound spectrum
      AudioSource.GetSpectrumData(_spectrum, 0, FFTWindow.BlackmanHarris);
      float maxV = 0;
      var maxN = 0;
      for (i = 0; i < QSamples; i++) { // find max 
          if (!(_spectrum[i] > maxV) || !(_spectrum[i] > Threshold))
              continue;

          maxV = _spectrum[i];
          maxN = i; // maxN is the index of max
      }
      float freqN = maxN; // pass the index to a float variable
      if (maxN > 0 && maxN < QSamples - 1) { // interpolate index using neighbours
          var dL = _spectrum[maxN - 1] / _spectrum[maxN];
          var dR = _spectrum[maxN + 1] / _spectrum[maxN];
          freqN += 0.5f * (dR * dR - dL * dL);
      }
      PitchValue = freqN * (_fSample / 2) / QSamples; // convert index to frequency
  }
}

Je vous remercie d'avance ! :)

+2 -0

J'essaye de lire tout ça et essayer de comprendre le concept. Mais ne connaissant par les termes techniques, il me faut un certain temps d'analyse.

@Christopher: Je ne sais pas comment m'y prendre pour avoir le résultat, à part plusieurs essais différents, pour l'instant je fais beaucoup de chose pour rien faire.

Le script que tu montres calcules le spectre du signal, c'est-à-dire la répartition de sa puissance en fonction de la fréquence. Il va ensuite chercher le maximum sur le spectre, autrement dit la fréquence la plus forte et il considère qu'il s'agit de la fréquence fondamentale du signal (ce qui est en général vrai, mais pas dans certains cas particuliers). Il effectue quelques manipulations supplémentaires entre deux, mais d'importance secondaire.

Avec ce script, tu obtiens donc la fréquence la plus significative dans le signal.

Pour savoir si une note est jouée correctement (tu veux faire un accordeur ?), tu peux comparer la note attendue. Si c'est trop loin (trop grave ou trop aigu), tu peux l'indiquer, etc.

En fait, je comprend pas ton problème, parce que tu as exactement ce qu'il te faut : le script.

+0 -0

Si la guitare est mal accordée, tu ne peux pas retrouver la correspondance facilement.

Ensuite, le pic du spectre n'est pas forcément la fréquence fondamentale, comme je le mentionnais un peu avant : ça peut être une harmonique. Par exemple, si tu fais un « la » à 440 Hz, tu auras aussi les harmoniques (880 Hz, 1320 Hz, etc.). Si une harmonique est plus forte, tu peux te retrouver avec un maximum à 880 Hz par exemple, qui n'est PAS la fréquence fondamentale. La fondamentale sera donnée par l'écart entre les principaux pic. Par exemple ici il y aurait peut-être trois pics à 440 Hz, 880 Hz et 1320 Hz et la note correspond à l'écart entre deux pics (1320 - 880 = 440 Hz).

Ça t'avance ? Est-ce qu tu as les moyens de visualiser le spectre ou le signal pour voir à quoi ressemble vraiment ton signal ?

Pour analyser ton signal sans passer par Unity ou autre, tu peux utiliser Audacity (libre & gratuit). Tu y trouveras tous les outils et d'analyse dont tu as besoin pour comprendre les tenants et aboutissants des représentations spectrales du son.

Plusieurs features vont t'aider:

  • Quand tu as ouvert une piste dans le coin haut gauche, il y a un menu déroulant. Les options Spectrogram et Spectrogram(logf) te permettent d'afficher le spectrogramme en échelle linéaire et logarithmique respectivement. Les options se trouvent dans Edit > Preferences > Spectrograms

  • En sélectionnant à l'aide du Selection Tool, et grâce à Analyse > Plot Spectrum, tu vas pouvoir afficher le spectre (ou l'autocorrélation, ou le cepstre) en jouant sur plusieurs paramètres (Algorithme, Fenêtre d'analyse, taille de la fênetre d'analyse, type d'axes. Si tu pointes avec ta souris sur le spectre, tu te rendras que ce curseur donnes des infos sur son positionnement (fréquence ou temps en abscisse avec la correspondance avec la note de musique rattaché, "puissance" en ordonnée) et utilise même un algorithme de recherche de peak pour donner la position du plus proche pic.

Amuses toi avec ça et tes propres fichiers et tu auras une meilleur appréhension de tout ça !

Je ne suis pas très satisfait d'Audacy, mais je pense que ça vient de moi.

@Aabu: Au départ je pensais que c'était une coïncidence mais pour A2, j'ai bien un pique qui retombe à 110Hz.

Pour A2 :

Pour G4 :


Après quelques recherches supplémentaire, j'ai trouvé la définition de "Fréquence fondamentale", ce qui me permet de comprendre le pique qui retombe à 110Hz…

Fréquence fondamentale : fréquence la plus basse fournie par un système vibrant.

Source : http://www.utc.fr/~mecagom4/MECAWEB/EXEMPLE/EX13/WEB/piano.htm#fonda

Je vais donc chercher à obtenir la fréquence la plus basse, cependant, je pense que ça ne va pas me suffire. Je vais devoir faire ma propre emprunte des sons et intégrer la longueur d'onde pour détecter correctement les notes, sur une écoute continue du micro.

Ton tableau de fréquences te donne déjà les fréquences fondamentales (la plus basse harmonique = harmonique de premier rang != fréquence la plus basse)

Je sais pas ce qu'on est supposé voir sur les captures d'écran, mais je ne vois aucun signal qui ressemble de près ou de loin à une note de guitare.

Je vais devoir faire ma propre emprunte des sons et intégrer la longueur d'onde pour détecter correctement les notes, sur une écoute continue du micro.

J'ai pas compris.

Le passage qui explique les screens à disparu, j'ai du l'écraser lors de la rédaction de ma réponse.

Les notes sont représentées en rose, le graphique représente la fréquence atteinte. La ligne rouge plus basse c'est la fréquence 110 Hz et sur le deuxième screen la deuxième ligne c'est la fréquence 392 Hz.

Pour faire ce graph, j'ajoute à chaque itération (toutes les 20-30 ms) la valeur de la fréquence dans un tableau.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using UnityEngine;
using System.Collections;

public class GraphSpectrum : MonoBehaviour {
  AudioSource audio;
  public float frequency = 0;
  float[] spectrum = new float[256];
  ArrayList graph = new ArrayList();

  void Start() {
      audio = GetComponent<AudioSource>();
  }

  void Update() {
      audio.GetSpectrumData(spectrum, 0, FFTWindow.BlackmanHarris);
      for (int i = 1; i < spectrum.Length - 1; i++) {
          Debug.DrawLine(new Vector3(i - 1, Mathf.Log(spectrum[i - 1]) + 200, 2), new Vector3(i, Mathf.Log(spectrum[i]) + 200, 2), Color.cyan);
          Debug.DrawLine(new Vector3(Mathf.Log(i - 1), spectrum[i - 1] + 180, 1), new Vector3(Mathf.Log(i), spectrum[i] + 180, 1), Color.green);
          Debug.DrawLine(new Vector3(Mathf.Log(i - 1) * 10, Mathf.Log(spectrum[i - 1]) * 10 + 300, 3), new Vector3(Mathf.Log(i) * 10, Mathf.Log(spectrum[i]) * 10 + 300, 3), Color.yellow);
      }
      
      graph.Add(AudioMeasureCS.sPitchValue);

      if (graph.Count >= 400)
          graph.RemoveAt(0);

      for (int i = 1, d = 10; i < graph.Count - 1; i++) {
          Debug.DrawLine(new Vector3(i, (float)graph[i] / 40 + 200, 1), new Vector3(i, -(float)graph[i] / 30 + 200), Color.grey);
          Debug.DrawLine(new Vector3(i - 1, (float)graph[i] / d + 200, 0), new Vector3(i, (float)graph[i + 1] / d + 200, 0), Color.magenta);
          Debug.DrawLine(new Vector3(i - 1, 200 + 110 / d, 0), new Vector3(graph.Count, 200 + 110 / d, 0), Color.red);
            Debug.DrawLine(new Vector3(i - 1, 200 + 392 / d, 0), new Vector3(graph.Count, 200 + 392 / d, 0), Color.red);
        }

  }
}

Au final j’ai jamais réussi à faire mon application qui détecte les notes.

Ca pourrait être intéressant de savoir où tu es resté bloqué.

A priori ça ne me parait pas être parti si faux pourtant. Il y a juste un truc qui me turlupine un petit peu, tu dis relever le pic toutes les 20–30ms; donc j’en déduis que le FFT que tu utilise a une fenêtre de 1024 (je suppose un son échantillonné à 44100Hz). Ca veut dire que tu es précis à 22050/1024=21.5Hz près et ensuite tu fais de l’interpolation linéaire ? Ca me parait assez faible en fait… 21.5Hz c’est ce qui sépare à peu près le la 440 du la#. Donc pour détecter des notes à 110Hz ça me semble carrément insuffisant. Passer à une fenêtre de taille 2048 ou 4096 changerait peut-être des choses.

+0 -0

Ah. C’est peut-être pour ça que j’avais du mal à distinguer certaine note. :/

Je vais essayer de faire ça en JavaScript ou Python sans Unity3D (je doute que j’ai mes PC de voyage soit suffisamment performant), je vais chercher une lib python.

Mon plus gros problème c’est que je n’ai pas vraiment de base théorique sur le son pour identifier et mesurer ces courbes.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte