Créer un bouton pour changer d'octave midi

Bonjour,

Je suis nouveau sur le forum, je vous explique en quelques mots ce qui m'amène à demander de l'aide : Je rame depuis plusieurs jours pour résoudre un problème probablement simple pour les initiés.

Je souhaite faire un instrument de musique de type harpe laser avec 12 notes (12 lasers et 12 LDR). Le programme de base fonctionne parfaitement avec FL Studio et une carte Léonardo, j’obtiens bien les 12 notes.


#include "MIDIUSB.h"
#define LED 13 //Associe 13 au mot "LED" dans le programme
const byte TOTAL_LDR = 12;
// Toutes les broches sont utilisées. Chaque photo résistance (LDR) est reliée à chaque broche.
const byte LDR_PIN[TOTAL_LDR] = {1,2,3,4,5,6,7,8,9,10,11,12};
// Chaque hauteur de note correspond à chaque broche. Chaque note est associée à une valeur numérique (échelle de fréquence).
// Correspondance hauteur de note (pitch) : C1=24 ... B1=35 C2=36 ... B2=47 C3=48 ... B3=59 C4=60 ... B4=71 C5=72 ... B5=83 C6=84 ... B6=95 C7=96 ... B7=107
const byte LDR_PITCH[TOTAL_LDR] = {48,49,50,51,52,53,54,55,56,57,58,59};
// Etat actuel des LDR.
byte currentRead[TOTAL_LDR];
// Lecture des entrées temporaires pour vérifier l'état actuel.
byte tempRead;

// La fonction setup s'exécute une seule fois en pressant setup ou en alimentant la carte
void setup() {
  pinMode(LED, OUTPUT);
  // Initialisez toutes les broches LDR en tant qu'entrée pull-up.
  for (byte i = 0; i < TOTAL_LDR; i++) {
    pinMode(LDR_PIN[i], INPUT_PULLUP);
  }
}

// La fonction loop s'exécute en boucle, indéfiniment.
void loop() {
  for (byte i = 0; i < TOTAL_LDR; i++) {
    // Lire l'état numérique des broches LDR.
    // Dans les entrées pull-up, la logique est inversée (HIGH : reçoit le faisceau laser, LOW : faisceau interrompu).
    byte LDRState = digitalRead(LDR_PIN[i]);
    // Stocker temporairement l'état numérique.
    tempRead = LDRState;
    // Continuer uniquement si le dernier état est différent de l'état actuel.
    if (currentRead[i] != tempRead) {
      delay(2);
      // Obtenir la hauteur qui correspond au faisceau interrompu.
      byte pitch = LDR_PITCH[i];
      // Enregistrez le nouvel état.
      currentRead[i] = tempRead;
      // Joue ou coupe la note en fonction de l'état (coupure du faisceau: Low ou pas de coupure: Hight).
      if (LDRState == LOW) {
        noteOn(pitch);
        digitalWrite(LED, HIGH);
      } else {
        noteOff(pitch);
        digitalWrite(LED, LOW);
      }
    }
  }
}
//Définit la fonction noteOn pour envoyer à l'ordinateur le signal midi correspondant à la note jouée. 
void noteOn(byte pitch) {
  MidiUSB.sendMIDI({0x09, 0x90, pitch, 127});
  MidiUSB.flush();
}

void noteOff(byte pitch) {
  MidiUSB.sendMIDI({0x08, 0x80, pitch, 0});
  MidiUSB.flush();
}

Je voudrais maintenant rajouter deux boutons qui me permettent de passer à l'octave supérieure ou inférieure et c'est là que ça se complique.

J'ai utilisé ce programme pour intégrer la lecture d'état du bouton

boolean buttonWasUp = true;
boolean ledEnabled = false;

void setup() {
  pinMode(10, OUTPUT);
  pinMode(A0, INPUT_PULLUP);
}
 
void loop() {
   boolean buttonIsUp = digitalRead(A0);
   if (buttonWasUp && !buttonIsUp) {
      delay(10);
    buttonIsUp = digitalRead(A0);
      if (!buttonIsUp) {
         ledEnabled = !ledEnabled;
         digitalWrite(13, ledEnabled);
      }
   }
   buttonWasUp = buttonIsUp;
}

Je l'ai intégré à cet endroit dans le programme


void loop() {
  for (byte i = 0; i < TOTAL_LDR; i++) {
  
    // Lire l'état numérique des broches LDR.
    // Dans les entrées pull-up, la logique est inversée (HIGH : reçoit le faisceau laser, LOW : faisceau interrompu).
    byte LDRState = digitalRead(LDR_PIN[i]);
    // Stocker temporairement l'état numérique.
    tempRead = LDRState;
    // Continuer uniquement si le dernier état est différent de l'état actuel.
    if (currentRead[i] != tempRead) {
      // See https://www.arduino.cc/en/pmwiki.php?n=Tutorial/Debounce
      delay(2);
      // Obtenir la hauteur qui correspond au faisceau interrompu.
      byte pitch = LDR_PITCH[i];
      // Enregistrez le nouvel état.
      currentRead[i] = tempRead;
      // Joue ou coupe la note en fonction de l'état (coupure du faisceau: Low ou pas de coupure: Hight).
      if (LDRState == LOW) {
        noteOn(pitch);
        digitalWrite(LED, HIGH);
      } else {
        noteOff(pitch);
        digitalWrite(LED, LOW);
      }
        boolean buttonIsUp = digitalRead(A0);
   if (buttonWasUp && !buttonIsUp) {
      delay(10);
    buttonIsUp = digitalRead(A0);
      if (!buttonIsUp) {
         ledEnabled = !ledEnabled;
         digitalWrite(13, ledEnabled);
      }
   }
   buttonWasUp = buttonIsUp;
    }
  }
}

Mais quand je fais cela, l'état du bouton n'est plus reconnu (la led 13 ne s'allume/s'éteint pas).

J'espère avoir exposé le problème à peu près clairement car je suis novice. Quelqu'un aurait-il une solution ? Merci d'avance.

Olivier

postez tout le sketch final

On ne voit qu'un morceau de code mais j'ai l'impression que le bloc de code n'est pas inséré au bon endroit.
Il se trouve inclus dans le

if (currentRead[i] != tempRead) {

est-ce voulu?

Tout d'abord merci d'avoir répondu. A dire vrai, je ne sais pas trop ou le placer car quand je le place plus bas, j'ai plein de messages d'erreur qui me disent que "noteOne n'est pas dans le scope"
J'ai essayé plusieurs programmes trouvés sur internet sans succès. Je suis donc revenu à la méthode ancienne : j'ai ouvert un livre ! Et là, j'ai trouvé un code très simple qui fonctionne :

#include "MIDIUSB.h"
#define LED 13 //Associe 13 au mot "LED" dans le programme
const int BOUTON_MOINS = A1;
const int BOUTON_PLUS = A2;
const byte TOTAL_LDR = 12;
// Toutes les broches sont utilisées. Chaque photo résistance (LDR) est reliée à chaque broche.
const byte LDR_PIN[TOTAL_LDR] = {1,2,3,4,5,6,7,8,9,10,11,12};
// Chaque hauteur de note correspond à chaque broche. Chaque note est associée à une valeur numérique (échelle de fréquence).
// Correspondance hauteur de note (pitch) : C1=24 ... B1=35 C2=36 ... B2=47 C3=48 ... B3=59 C4=60 ... B4=71 C5=72 ... B5=83 C6=84 ... B6=95 C7=96 ... B7=107
const byte LDR_PITCH[TOTAL_LDR] = {48,49,50,51,52,53,54,55,56,57,58,59};
// Etat actuel des LDR.
byte currentRead[TOTAL_LDR];
// Lecture des entrées temporaires pour vérifier l'état actuel.
byte tempRead;

// La fonction setup s'exécute une seule fois en pressant setup ou en alimentant la carte
void setup() {
  pinMode (BOUTON_MOINS, INPUT_PULLUP);
  pinMode (BOUTON_PLUS, INPUT_PULLUP);
  pinMode(LED, OUTPUT);
  // Initialisez toutes les broches LDR en tant qu'entrée pull-up.
  for (byte i = 0; i < TOTAL_LDR; i++) {
    pinMode(LDR_PIN[i], INPUT_PULLUP);
  }
}

// La fonction loop s'exécute en boucle, indéfiniment.
void loop() {
  for (byte i = 0; i < TOTAL_LDR; i++) {
    // Lire l'état numérique des broches LDR.
    // Dans les entrées pull-up, la logique est inversée (HIGH : reçoit le faisceau laser, LOW : faisceau interrompu).
    byte LDRState = digitalRead(LDR_PIN[i]);
    // Stocker temporairement l'état numérique.
    tempRead = LDRState;
    // Continuer uniquement si le dernier état est différent de l'état actuel.
    if (currentRead[i] != tempRead) {
      delay(2);
      // Obtenir la hauteur qui correspond au faisceau interrompu.
      byte pitch = LDR_PITCH[i];
      // Enregistrez le nouvel état.
      currentRead[i] = tempRead;
      // Joue ou coupe la note en fonction de l'état (coupure du faisceau: Low ou pas de coupure: Hight).
      if ((LDRState == LOW) && (digitalRead(A1) == HIGH) && (digitalRead(A2) == HIGH)) {
        noteOn(pitch);
        digitalWrite(LED, HIGH);
      } else {
        noteOff(pitch);
        digitalWrite(LED, LOW);
      }
      if ((LDRState == LOW) && (digitalRead(A1) == HIGH) && (digitalRead(A2) == LOW)) {
        noteOn(pitch - 12);
        digitalWrite(LED, HIGH);
      } else {
        noteOff(pitch - 12);
        digitalWrite(LED, LOW);
      }
      if ((LDRState == LOW) && (digitalRead(A1) == LOW) && (digitalRead(A2) == HIGH)) {
        noteOn(pitch + 12);
        digitalWrite(LED, HIGH);
      } else {
        noteOff(pitch + 12);
        digitalWrite(LED, LOW);
      }
    }
  }
}
//Définit la fonction noteOn pour envoyer à l'ordinateur le signal midi correspondant à la note jouée. 
void noteOn(byte pitch) {
  MidiUSB.sendMIDI({0x09, 0x90, pitch, 127});
  MidiUSB.flush();
}

void noteOff(byte pitch) {
  MidiUSB.sendMIDI({0x08, 0x80, pitch, 0});
  MidiUSB.flush();
}

Ce n'est peut-être pas dans les règles de l'art mais au moins ça fonctionne. Les boutons 1 et 2 me permettent de passer à l'octave précédente ou à l'octave suivante.
L'inconvénient est qu'avec un poussoir je dois rester appuyé pour changer d'octave alors que le programme précédent gardait l'état en mémoire.

Il n'y a pas de variable noteOne dans les bouts de code que tu montres :frowning:
On ne peut pas utiliser une variable, si celle-ci n'a pas été déclarée.
De plus suivant là ou tu la déclare cela change sa visibilité, par exemple, si tu déclare un variable dans une fonction, elle ne sera pas connu dans tes autres fonctions.

Si tu voulais aller plus loin et t'affranchir de devoir trouver un programme tout fait sur internet, tu peux regarder les tutoriels de eskimon

D'après ce que j'ai compris noteOn est une fonction qui est définit en fin de code. Cette fonction est appelée dans la partie voidloop() pour envoyer la note en midi vers le PC.

J'ai essayé d'intégrer ce code pour gérer le bouton poussoir. Lorsque l'on relâche le bouton l'état reste en mémoire. Je n'aurai donc pas besoin de rester appuyé. La LED 13 répond bien, donc le code fonctionne.

boolean buttonWasUp = true;
boolean ledEnabled = false;

void setup() {
  pinMode(10, OUTPUT);
  pinMode(A0, INPUT_PULLUP);
}
 
void loop() {
   boolean buttonIsUp = digitalRead(A0);
   if (buttonWasUp && !buttonIsUp) {
      delay(10);
    buttonIsUp = digitalRead(A0);
      if (!buttonIsUp) {
         ledEnabled = !ledEnabled;
         digitalWrite(13, ledEnabled);
      }
   }
   buttonWasUp = buttonIsUp;
}

Le code complet


#include "MIDIUSB.h"

boolean buttonWasUp = true;
boolean ledEnabled = false;

const byte TOTAL_LDR = 12;
// Toutes les broches sont utilisées. Chaque photo résistance (LDR) est reliée à chaque broche.
const byte LDR_PIN[TOTAL_LDR] = {1,2,3,4,5,6,7,8,9,10,11,12};
// Chaque hauteur de note correspond à chaque broche. Chaque note est associée à une valeur numérique (échelle de fréquence).
// Correspondance hauteur de note (pitch) : C1=24 ... B1=35 C2=36 ... B2=47 C3=48 ... B3=59 C4=60 ... B4=71 C5=72 ... B5=83 C6=84 ... B6=95 C7=96 ... B7=107
const byte LDR_PITCH[TOTAL_LDR] = {48,49,50,51,52,53,54,55,56,57,58,59};
// Etat actuel des LDR.
byte currentRead[TOTAL_LDR];
// Lecture des entrées temporaires pour vérifier l'état actuel.
byte tempRead;

// La fonction setup s'exécute une seule fois en pressant setup ou en alimentant la carte
void setup() {
  
 pinMode(13, OUTPUT);
 pinMode(A0, INPUT_PULLUP);

  // Initialisez toutes les broches LDR en tant qu'entrée pull-up.
  for (byte i = 0; i < TOTAL_LDR; i++) {
    pinMode(LDR_PIN[i], INPUT_PULLUP);
  }
}

// La fonction loop s'exécute en boucle, indéfiniment.
void loop() {
  boolean buttonIsUp = digitalRead(A0);
   if (buttonWasUp && !buttonIsUp) {
      delay(10);
    buttonIsUp = digitalRead(A0);
      if (!buttonIsUp) {
         ledEnabled = !ledEnabled;
         digitalWrite(13, ledEnabled);
      }
   }
   buttonWasUp = buttonIsUp;

  for (byte i = 0; i < TOTAL_LDR; i++) {
    // Lire l'état numérique des broches LDR.
    // Dans les entrées pull-up, la logique est inversée (HIGH : reçoit le faisceau laser, LOW : faisceau interrompu).
    byte LDRState = digitalRead(LDR_PIN[i]);
    // Stocker temporairement l'état numérique.
    tempRead = LDRState;
    // Continuer uniquement si le dernier état est différent de l'état actuel.
    if (currentRead[i] != tempRead) {
      delay(2);
      // Obtenir la hauteur qui correspond au faisceau interrompu.
      byte pitch = LDR_PITCH[i];
      // Enregistrez le nouvel état.
      currentRead[i] = tempRead;
      // Joue ou coupe la note en fonction de l'état (coupure du faisceau: Low ou pas de coupure: Hight).
      if (LDRState == LOW) {
        noteOn(pitch);
      } else {
        noteOff(pitch);
      }
      
  
    }
  }
}
//Définit la fonction noteOn pour envoyer à l'ordinateur le signal midi correspondant à la note jouée. 
void noteOn(byte pitch) {
  MidiUSB.sendMIDI({0x09, 0x90, pitch, 127});
  MidiUSB.flush();
}

void noteOff(byte pitch) {
  MidiUSB.sendMIDI({0x08, 0x80, pitch, 0});
  MidiUSB.flush();
}

Le code fonctionne, la pression est bien détectée (la LED s'allume). J'ajoute le changement d'octave pitch == pitch+12


#include "MIDIUSB.h"

boolean buttonWasUp = true;
boolean ledEnabled = false;

const byte TOTAL_LDR = 12;
// Toutes les broches sont utilisées. Chaque photo résistance (LDR) est reliée à chaque broche.
const byte LDR_PIN[TOTAL_LDR] = {1,2,3,4,5,6,7,8,9,10,11,12};
// Chaque hauteur de note correspond à chaque broche. Chaque note est associée à une valeur numérique (échelle de fréquence).
// Correspondance hauteur de note (pitch) : C1=24 ... B1=35 C2=36 ... B2=47 C3=48 ... B3=59 C4=60 ... B4=71 C5=72 ... B5=83 C6=84 ... B6=95 C7=96 ... B7=107
const byte LDR_PITCH[TOTAL_LDR] = {48,49,50,51,52,53,54,55,56,57,58,59};
// Etat actuel des LDR.
byte currentRead[TOTAL_LDR];
// Lecture des entrées temporaires pour vérifier l'état actuel.
byte tempRead;

// La fonction setup s'exécute une seule fois en pressant setup ou en alimentant la carte
void setup() {
  
 pinMode(13, OUTPUT);
 pinMode(A0, INPUT_PULLUP);

  // Initialisez toutes les broches LDR en tant qu'entrée pull-up.
  for (byte i = 0; i < TOTAL_LDR; i++) {
    pinMode(LDR_PIN[i], INPUT_PULLUP);
  }
}

// La fonction loop s'exécute en boucle, indéfiniment.
void loop() {
  boolean buttonIsUp = digitalRead(A0);
   if (buttonWasUp && !buttonIsUp) {
      delay(10);
    buttonIsUp = digitalRead(A0);
      if (!buttonIsUp) {
         ledEnabled = !ledEnabled;
         digitalWrite(13, ledEnabled);
         pitch == pitch + 12
      }
   }
   buttonWasUp = buttonIsUp;

  for (byte i = 0; i < TOTAL_LDR; i++) {
    // Lire l'état numérique des broches LDR.
    // Dans les entrées pull-up, la logique est inversée (HIGH : reçoit le faisceau laser, LOW : faisceau interrompu).
    byte LDRState = digitalRead(LDR_PIN[i]);
    // Stocker temporairement l'état numérique.
    tempRead = LDRState;
    // Continuer uniquement si le dernier état est différent de l'état actuel.
    if (currentRead[i] != tempRead) {
      delay(2);
      // Obtenir la hauteur qui correspond au faisceau interrompu.
      byte pitch = LDR_PITCH[i];
      // Enregistrez le nouvel état.
      currentRead[i] = tempRead;
      // Joue ou coupe la note en fonction de l'état (coupure du faisceau: Low ou pas de coupure: Hight).
      if (LDRState == LOW) {
        noteOn(pitch);
      } else {
        noteOff(pitch);
      }
      
  
    }
  }
}
//Définit la fonction noteOn pour envoyer à l'ordinateur le signal midi correspondant à la note jouée. 
void noteOn(byte pitch) {
  MidiUSB.sendMIDI({0x09, 0x90, pitch, 127});
  MidiUSB.flush();
}

void noteOff(byte pitch) {
  MidiUSB.sendMIDI({0x08, 0x80, pitch, 0});
  MidiUSB.flush();
}

Et là j'ai ces message d'erreur que je ne comprends pas : pitch semble être déclaré dans le void noteOn (byte pitch) ...

D:\Arduino\Harpe_laser_2\Harpe_laser_2.ino: In function 'void loop()':
D:\Arduino\Harpe_laser_2\Harpe_laser_2.ino:39:10: error: 'pitch' was not declared in this scope
pitch == pitch + 12
^~~~~
D:\Arduino\Harpe_laser_2\Harpe_laser_2.ino:39:10: note: suggested alternative: 'putc'
pitch == pitch + 12
^~~~~
putc

exit status 1

Compilation error: 'pitch' was not declared in this scope

Dans ton message d'origine tu parle de noteOne, pas de noteOn, peut être avait tu fais une faute de frappe?

Excuse moi d'être aussi directe mais tu fais n'importe quoi :frowning:
Premièrement le compilateur t'indique que la variable pitch est inconnue, car elle est déclaré plus tard dans la fonction.
Deuxièmement "==" est un opérateur d'égalité, ce n'est pas un opérateur d'affectation.
pour une affectation c'est "=".
Troisièmes même si tu déclare correctement ta variable, celle-ci ne peut contenir à ce moment la valeur que tu veux augmenter.
D'ailleurs quel valeur veux tu augmenter, puisque tu as un tableau de pitch(LDR_PITCH)

Peut tu redécrire ce que ton programme doit faire ?

un exemple

ça ne fait qu'imprimer dans la console série

il faudra affiner ce que vous voulez faire si vous basculez de low pitch à high pitch et qu'il y a un laser "coupé" etc...

une approche par classe serait plus adaptée que d'avoir plein de tableaux séparés

Pas de souci, j'apprends en tâtonnant alors je veux bien croire que ce que je fais ne ressemble à rien pour des experts du code, cela ne me vexe pas du tout. C'est d'ailleurs parce que je suis un peu perdu que je viens chercher du secours sur le forum :wink:
J'avais lu que le placement d'une fonction n'avait pas d'importance. J'ai toutefois déplacé la fonction noteOn pour que pitch soit déclaré avant voidloop() mais j'ai exactement le même message d'erreur.
Mon programme doit envoyer une note en midi au PC. J'ai douze laser donc douze notes (une octave) mais je voudrais avoir une gamme plus importante en en décalant mes douze notes soit vers les aigus (pitch+12) soit vers les graves (pitch-12) en appuyant sur deux poussoirs dédiés.
Pour le moment, mon programme fonctionne en restant appuyé sur les poussoirs ce qui monopolise une main (donc pas idéal pour jouer) et je souhaiterai que le poussoir permette de changer d'octave par une pression brève, puis de revenir à l’octave de base par une seconde pression. Il y aurait deux boutons poussoirs, l'un pour "plus grave" et l'autre pour "plus aigu".

Programme qui fonctionne mais en appuyant constamment :



#include "MIDIUSB.h"
#define LED 13 //Associe 13 au mot "LED" dans le programme
const int BOUTON_MOINS = A1;
const int BOUTON_PLUS = A2;
const byte TOTAL_LDR = 12;
// Toutes les broches sont utilisées. Chaque photo résistance (LDR) est reliée à chaque broche.
const byte LDR_PIN[TOTAL_LDR] = {1,2,3,4,5,6,7,8,9,10,11,12};
// Chaque hauteur de note correspond à chaque broche. Chaque note est associée à une valeur numérique (échelle de fréquence).
// Correspondance hauteur de note (pitch) : C1=24 ... B1=35 C2=36 ... B2=47 C3=48 ... B3=59 C4=60 ... B4=71 C5=72 ... B5=83 C6=84 ... B6=95 C7=96 ... B7=107
const byte LDR_PITCH[TOTAL_LDR] = {48,49,50,51,52,53,54,55,56,57,58,59};
// Etat actuel des LDR.
byte currentRead[TOTAL_LDR];
// Lecture des entrées temporaires pour vérifier l'état actuel.
byte tempRead;

// La fonction setup s'exécute une seule fois en pressant setup ou en alimentant la carte
void setup() {
  pinMode (BOUTON_MOINS, INPUT_PULLUP);
  pinMode (BOUTON_PLUS, INPUT_PULLUP);
  pinMode(LED, OUTPUT);
  // Initialisez toutes les broches LDR en tant qu'entrée pull-up.
  for (byte i = 0; i < TOTAL_LDR; i++) {
    pinMode(LDR_PIN[i], INPUT_PULLUP);
  }
}

// La fonction loop s'exécute en boucle, indéfiniment.
void loop() {
  for (byte i = 0; i < TOTAL_LDR; i++) {
    // Lire l'état numérique des broches LDR.
    // Dans les entrées pull-up, la logique est inversée (HIGH : reçoit le faisceau laser, LOW : faisceau interrompu).
    byte LDRState = digitalRead(LDR_PIN[i]);
    // Stocker temporairement l'état numérique.
    tempRead = LDRState;
    // Continuer uniquement si le dernier état est différent de l'état actuel.
    if (currentRead[i] != tempRead) {
      delay(2);
      // Obtenir la hauteur qui correspond au faisceau interrompu.
      byte pitch = LDR_PITCH[i];
      // Enregistrez le nouvel état.
      currentRead[i] = tempRead;
      // Joue ou coupe la note en fonction de l'état (coupure du faisceau: Low ou pas de coupure: High) + changement d'octave.
      if ((LDRState == LOW) && (digitalRead(A1) == HIGH) && (digitalRead(A2) == HIGH)) {
        noteOn(pitch);
        digitalWrite(LED, HIGH);
      } else {
        noteOff(pitch);
        digitalWrite(LED, LOW);
      }
      if ((LDRState == LOW) && (digitalRead(A1) == LOW) && (digitalRead(A2) == HIGH)) {
        noteOn(pitch - 12);
        digitalWrite(LED, HIGH);
      } else {
        noteOff(pitch - 12);
        digitalWrite(LED, LOW);
      }
      if ((LDRState == LOW) && (digitalRead(A1) == HIGH) && (digitalRead(A2) == LOW)) {
      noteOn(pitch + 12);
      digitalWrite(LED, HIGH);
      } else {
      noteOff(pitch + 12);
      digitalWrite(LED, LOW);
      }
    }
  }
}
//Définit la fonction noteOn pour envoyer à l'ordinateur le signal midi correspondant à la note jouée. 
void noteOn(byte pitch) {
  MidiUSB.sendMIDI({0x09, 0x90, pitch, 127});
  MidiUSB.flush();
}

void noteOff(byte pitch) {
  MidiUSB.sendMIDI({0x08, 0x80, pitch, 0});
  MidiUSB.flush();
}

Merci pour votre message mais là vous m'avez complètement perdu (approche par classe ??). J'ai un programme qui marche à peu près donc je vais essayer de garder cette base et de la modifier pour obtenir ce que je veux (pression brève pour changer d'octave). Ce doit être possible !

regardez la demo sur wokwi, c'est très proche de votre code je n'ai pas mis de classe.
j'ai juste utilisé la bibliothèque Toggle pour gérer les switch

wokwi ne simule pas d'arduino capable d'utiliser MIDIUSB c'est pour cela que je n'ai que des print, mais dans votre code il suffit de le rajouter et de rajouter les noteOn() et noteOff()

En regardant le schéma je vois un interrupteur et non un poussoir. Finalement, c'est peut-ce que je devrais faire pour éviter de rester appuyé ... Et plutôt que d'avoir deux poussoirs, un interrupteur à trois positions supprimerait le risque d'envoyer deux ordres contradictoires (+12 et -12 en même temps) !
Merci pour vos commentaires avisés qui me font avancer dans mon projet !

ah oui je me disais que c'était mieux que d'avoir à tenir un bouton enfoncé, mais la bibliothèque Toggle gère les deux, en fait elle considère le bouton à gauche et le bouton à droite comme deux boutons séparés tels que je l'ai codé.

voilà avec un seul bouton qu'on tient appuyé

dans la simulation il faut faire commandclick sur Mac ou ctrlclick sur PC pour que vous puissiez utiliser la souris et aller ailleurs modifier le seuil de détection des capteurs (en cliquant dessus). Vous désactivez le bouton en re-clickant simplement dessus.

Justement, je ne voudrais pas rester appuyé en permanence (mon programme le fait déjà). J'aurai voulu appuyer juste une fois brièvement pour que l'octave change et rappuyer une seconde fois brièvement pour revenir à l'état initial.
Je pense que je vais opter pour un interrupteur à bascule à trois positions, cela me permettra d'utiliser mon code tel qu'il est.

faites les dans le code, c'est trivial...

un booléen qui s'inverse à chaque appui

Une variable déclarée dans une fonction n'a pas d'existence dans une autre fonction.
Donc tu peux mettre la fonction ou tu veux tu aura toujours le même message d'erreur.

Si ce que tu veux faire c'est que tout les notes joué donc issue de ton tableau LDR_PITCH soit plus ou moins augmenté en fonction du bouton que tu as appuyer.
Il faut déclarer une variable global portant un autre nom, que tu augmente ou diminue suivant les boutons sur lequels tu appuis.
Puis lorsque tu appel ta fonction noteOn noteOff, tu concatène les deux variables.
Enfin je suppose car je ne connais pas l'utilisation de cette paire de fonction noteOn, noteOff.

Je n'ai pas regardé le wokwi que tu propose @J-M-L , mais je pense qu'il serait judicieux de partir de là, pour essayer de construire ton programme.
Car effectivement tu es au début de la programmation et donc ne maitrise pas les principes fondamentaux.
L'autre solution, serait de suivre un cours de C++

Je suis reparti du programme de J-M-L et ça fonctionne bien en poussoir (il faut rester appuyer).

#include <Toggle.h>
#include "MIDIUSB.h"

const byte ldrPins[] = {1,2,3,4,5,6,7,8,9,10,11,12};
const byte nbLdr = sizeof ldrPins / sizeof * ldrPins;
const byte ldrLowPitches[nbLdr] = {48,49,50,51,52,53,54,55,56,57,58,59};
const byte ldrHighPitches[nbLdr] = {60,61,62,63,64,65,66,67,68,69,70,71};
bool lastLdrState[nbLdr];
Toggle HighSwitch;
bool highPitchActif = false;

void noteOn(byte pitch) {
  MidiUSB.sendMIDI({0x09, 0x90, pitch, 127});
  MidiUSB.flush();
}

void noteOff(byte pitch) {
  MidiUSB.sendMIDI({0x08, 0x80, pitch, 0});
  MidiUSB.flush();
}

void setup() {
  HighSwitch.begin(A0);

  Serial.begin(115200);
  for (byte i = 0; i < nbLdr; i++) {
    pinMode(ldrPins[i], INPUT_PULLUP);
    lastLdrState[i] = (digitalRead(ldrPins[i]) == LOW);
  }
}

void loop() {
  HighSwitch.poll();
  if (HighSwitch.onPress()) {
    highPitchActif = !highPitchActif;
    Serial.println("flipping Pitch");
  }

  for (byte i = 0; i < nbLdr; i++) {
    byte ldrState = (digitalRead(ldrPins[i]) == LOW);
    if (lastLdrState[i] != ldrState) {
      lastLdrState[i] = ldrState;
      if (ldrState) {
        noteOn(highPitchActif ? ldrHighPitches[i] : ldrLowPitches[i]);
      } else {
        noteOff(highPitchActif ? ldrHighPitches[i] : ldrLowPitches[i]);
      }
    }
  }
}

Ce programme est sans aucun doute "plus propre" que le précédent mais je n'ai que 2 octaves au lieu de 3. Je vois que J-M-L a utilisé une variable booléenne, il n'y a que deux valeurs possibles alors comment vais-je pouvoir intégrer une troisième option pour avoir un mediumPitch ?
Je dois aussi trouver un moyen de transformer la pression constante en pression brève, je vais explorer la piste de la variable booléenne proposé par J-M-L.

Comment voulez vous gérer vos octaves

Vous pourriez avoir par exemple 2 boutons un pour augmenter et un pour diminuer.

Vous ne seriez alors plus limité à 3 octaves

Effectivement, dans l'idéal, il y aurait deux poussoirs : un qui augmente d'une ou plusieurs octaves (en appuyant plusieurs fois) et un qui diminue d'une ou plusieurs octaves (comme sur les claviers midi) mais ça me semblait plus compliqué que d'avoir un bouton qui diminue d'une seule octave et un autre bouton qui augmente d'une seule octave. C'est donc pour cela que je suis parti sur cette voie ... mais peut-être que je me trompe et qu'il y a une solution pas trop compliquée (pour moi !) pour réaliser la première configuration.

Ce n’est pas compliqué de faire un bouton plus et un bouton moins