/********************************************************************* Gadget MIDI pour améliorer la lecture des notes sur une portée. Une note s'affiche sur une portée sur un écran Nokia, et on doit la jouer sur un clavier MIDI.. Pour plus d'infos: https://electroniqueamateur.blogspot.com/2020/08/sentrainer-lire-les-notes-sur-une.html *********************************************************************/ #include <MIDI.h> // https://github.com/FortySevenEffects/arduino_midi_library #include <SPI.h> // Pour l'afficheur #include <Adafruit_GFX.h> // https://github.com/adafruit/Adafruit-GFX-Library #include <Adafruit_PCD8544.h> // https://github.com/adafruit/Adafruit-PCD8544-Nokia-5110-LCD-library MIDI_CREATE_DEFAULT_INSTANCE(); Adafruit_PCD8544 display = Adafruit_PCD8544(5, 4, 3); // D/C broche 5,CSE ou CS broche 4, RST broche 3 // mages bitmap éalisées avec l'outil en ligne http://javl.github.io/image2cpp/ // la portée en clé de sol const unsigned char cleDeSol [] PROGMEM = { // 'cledesol', 45x36px 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x01, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb5, 0x80, 0x00, 0x00, 0x00, 0x01, 0xa4, 0x40, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x01, 0x84, 0x40, 0x00, 0x00, 0x00, 0x00, 0xc4, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // la portée en clé de fa const unsigned char cleDeFa [] PROGMEM = { // 'cledefa', 45x36px 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x06, 0x39, 0x00, 0x00, 0x00, 0x00, 0x07, 0x98, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // symbol bémol const unsigned char bemol [] PROGMEM = { // 'bemol', 4x7px 0x80, 0x80, 0xa0, 0xd0, 0x90, 0xa0, 0xc0 }; // symbole dièse const unsigned char diese [] PROGMEM = { // 'dièse', 7x7px 0x14, 0x14, 0x7e, 0x28, 0xfc, 0x50, 0x50 }; // numéro de note midi associé à chaque position de la portée en clé de fa (numero 0 = MIDI 33, etc.) const int notes_fa[] = {33, 35, 36, 38, 40, 41, 43, 45, 47, 48, 50, 52, 53, 55, 57, 59, 60, 62, 64, 65, 67, 69}; // numéro de note midi associé à chaque position de la portée en clé de sol(numero 0 = MIDI 53, etc.) const int notes_sol[] = {53, 55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72, 74, 76, 77, 79, 81, 83, 84, 86, 88, 89}; // variables globales int numero; //position de la note sur la portée: 0 à 21 int alteration; // 0: rien 1: bémol 2: dièse int clef; // 0: sol 1: fa int erreur = 0; // 1 si notre dernière réponse était fausse int pointage = 0; // nombre de bonnes réponses depuit le début int total = 0; // nombre de questions posées depuis le début int bonneNote; // le numéro MIDI de la note qu'il faut jouer void dessineNote() { const int marge = 0; // nombre de pixels à gauche avant de commencer à dessiner display.clearDisplay(); // on dessine la portée if (clef) { //clef de fa display.drawBitmap(marge, 8, cleDeFa, 45, 36, BLACK); } else { // clef de sol display.drawBitmap(marge, 8, cleDeSol, 45, 36, BLACK); } //on dessine la note display.drawRoundRect(marge + 26, 42 - 2 * numero, 8, 5, 4, BLACK); // s'il y a lieu, on dessine les petits traits supplémentaires // note à l'extérieur de la portée if (numero == 0) { display.drawLine(marge + 24, 44, marge + 34, 44, BLACK); } if (numero < 3) { display.drawLine(marge + 24, 40, marge + 34, 40, BLACK); } if (numero < 5) { display.drawLine(marge + 24, 36, marge + 34, 36, BLACK); } if (numero > 15) { display.drawLine(marge + 24, 12, marge + 34, 12, BLACK); } if (numero > 17) { display.drawLine(marge + 24, 8, marge + 34, 8, BLACK); } if (numero > 19) { display.drawLine(marge + 24, 4, marge + 34, 4, BLACK); } // on dessine l'altération, s'il y a lieu if (alteration == 1) { // bemol display.drawBitmap(marge + 19, 41 - 2 * numero, bemol, 4, 7, BLACK); } if (alteration == 2) { // dièse display.drawBitmap(marge + 18, 42 - 2 * numero, diese, 7, 7, BLACK); } // si notre dernière tentative était mauvaise, on affiche un clavier qui montre // comment jouer la note. if (erreur) { dessineClavier(); } // on affiche le pointage display.setCursor(46, 8); // coordonnées du point de départ du texte display.setTextColor(BLACK); display.setTextSize(1); // taille par défaut display.print(String(pointage)); display.print("/"); display.print(String(total)); display.display(); } void dessineClavier() { const int origx = 48; const int origy = 28; const int largeurBlanche = 5; // les touches blanches for (int i = 0; i <= 6; i++) { display.drawRect(origx + i * largeurBlanche, origy, largeurBlanche , 12, BLACK); } //les touches noires for (int i = 0; i <= 1; i++) { display.fillRect(origx + largeurBlanche / 2 + 1 + i * largeurBlanche, origy, 4 , 7, BLACK); } for (int i = 0; i <= 2; i++) { display.fillRect(origx + 7 * largeurBlanche / 2 + 1 + i * largeurBlanche, origy, 4 , 7, BLACK); } // le repère qui indique la touche à enfoncer: switch (bonneNote % 12) { case 0: // do display.fillCircle(origx + largeurBlanche / 2 , origy + 14, 2, BLACK); break; case 1: // do# réb display.fillCircle(origx + largeurBlanche , origy - 4, 2, BLACK); break; case 2: // ré display.fillCircle(origx + 3 * largeurBlanche / 2 , origy + 14, 2, BLACK); break; case 3: // ré# mib display.fillCircle(origx + 2 * largeurBlanche , origy - 4, 2, BLACK); break; case 4: // mi display.fillCircle(origx + 5 * largeurBlanche / 2 , origy + 14, 2, BLACK); break; case 5: // fa display.fillCircle(origx + 7 * largeurBlanche / 2 , origy + 14, 2, BLACK); break; case 6: // fa# solb display.fillCircle(origx + 4 * largeurBlanche , origy - 4, 2, BLACK); break; case 7: // sol display.fillCircle(origx + 9 * largeurBlanche / 2 , origy + 14, 2, BLACK); break; case 8: // sol# lab display.fillCircle(origx + 5 * largeurBlanche , origy - 4, 2, BLACK); break; case 9: // la display.fillCircle(origx + 11 * largeurBlanche / 2 , origy + 14, 2, BLACK); break; case 10: // la# sib display.fillCircle(origx + 6 * largeurBlanche , origy - 4, 2, BLACK); break; case 11: // si display.fillCircle(origx + 13 * largeurBlanche / 2 , origy + 14, 2, BLACK); break; } display.display(); } // on choisit au hasard la nouvelle note à jouer void nouvelleQuestion() { numero = random(22); // position de la note sur la portée (0 à 21); alteration = random(3); // 0: rien 1: bemol 2: dièse clef = random(2); // 0: clé de sol 1: clé de fa // ajustement: on ne veut pas de mi dièse, de fa bémol, de si dièse et de do bémol if (alteration == 1) { // bémol if (clef) { // clé de fa if ((numero % 7 == 2) || (numero % 7 == 5) ) { // do ou fa alteration = 0; // on enlève le bémol. } } else { // clé de sol if ((numero % 7 == 0) || (numero % 7 == 4) ) { // do ou fa alteration = 0; // on enlève le bémol. } } } if (alteration == 2) { // dièse if (clef) { // clé de fa if ((numero % 7 == 1) || (numero % 7 == 4) ) { // si ou mi alteration = 0; // on enlève la dièse } } else { // clé de sol if ((numero % 7 == 3) || (numero % 7 == 6) ) { // si ou mi alteration = 0; // on enlève la dièse } } } dessineNote(); // affichage de la question } // on compare la note jouée avec celle qu'il fallait jouer int compareNotes(byte pitch) { // on convertit la variable numero (position sur la portée) en numéro de note MIDI if (clef) { // clé de fa bonneNote = notes_fa[numero]; } else { // clé de sol bonneNote = notes_sol[numero]; } if (alteration == 1) { // bémol bonneNote = bonneNote - 1; } if (alteration == 2) { // dièse bonneNote = bonneNote + 1; } // on joue la bonne note delay(200); // petit délai pour séparer la note jouée de la note corrigée MIDI.sendNoteOff(pitch, 0, 1); MIDI.sendNoteOn(bonneNote, 100, 1); delay(100); MIDI.sendNoteOff(bonneNote, 0, 1); return (bonneNote % 12 == pitch % 12); // on ne vérifie pas si c'est le bon octave /* Si vous désirez que la note soit acceptée uniquement si elle est jouée dans le bon octave, utilisez plutôt: return (bonneNote == pitch); */ } // éxécuté lorsque l'utilisateur appuie sur une touche void handleNoteOn(byte channel, byte pitch, byte velocity) { if (erreur == 0) { total = total + 1; } if (compareNotes(pitch)) { // bonne réponse // on additionne 1 au pointage if (erreur == 0) { pointage = pointage + 1; } erreur = 0; // on fait apparaître une nouvelle question nouvelleQuestion(); } else { // mauvaise réponse erreur = 1; dessineNote(); } } void setup() { // initialisation de l'écran display.begin(); // initialisation de l'afficheur display.setContrast(60); // réglage du contraste (40-60) variable selon votre écran display.clearDisplay(); // ça efface à la fois le buffer et l'écran display.display(); // initialisation MIDI MIDI.setHandleNoteOn(handleNoteOn); MIDI.begin(MIDI_CHANNEL_OMNI); // initialisation du générateur de nombres aléatoires randomSeed(analogRead(0)); // affichage de la première question nouvelleQuestion(); } void loop() { MIDI.read(); // on vérifie si une note est jouée sur le clavier. }