Cet article fait suite aux articles suivants:
- Décodage d'un clavier d'orgue par un Arduino
- Clavier d'orgue électronique et sa matrice
- Communication MIDI avec un Arduino
Après avoir réussi à faire en sorte que mon Arduino interprète correctement l'état (enfoncé ou non) des touches de mon clavier d'orgue ( voir l'article précédent ) , il ne restait plus qu'à modifier le sketch pour que l'information soit envoyée par l'Arduino sous forme de messages MIDI. Ça n'a pas été très compliqué.
Mon sketch précédent envoyait un message "touche enfoncée" de façon répétitive aussi longtemps que la touche demeurait enfoncée. Dans cette nouvelle version, il faut envoyer un message MIDI seulement lors d'un changement d'état: un seul message "Note On" lorsqu'on enfonce la touche, et un message "Note Off" lorsqu'on la relâche (en fait, il s'agit d'un "Note On" avec une vélocité nulle). Chaque fois que l'état d'une touche est déterminé, on le compare avec l'array "etat_des_touches" pour vérifier s'il y a eu un changement.
Le circuit contenant deux registres à décalage a déjà été décrit dans l'article précédent. Il faut bien entendu ajouter un jack DIN5 femelle (pour y brancher un câble MIDI). Ce jack doit être branché à la sortie numérique 1 (TX) de l'Arduino de la façon indiquée ici.
J'en ai profité pour ajouter deux fonctionnalités supplémentaires à mon clavier MIDI: deux boutons permettant de changer le programme (c'est à dire le timbre de l'instrument: piano, violon, trompette, etc.): le bouton "program up" relié à l'entrée numérique 10 de l'Arduino permet d'avancer, alors que le bouton "program down" relié à l'entrée numérique 13 de l'Arduino permet de reculer dans les programmes. Le résultat n'est pas idéal, puisqu'il faut souvent appuyer sur le bouton plusieurs dizaines de fois pour atteindre le programme désiré (et l'ajout d'un afficheur LCD serait bien utile pour qu'on sache où on en est...).
Une autre paire de bouton permet de monter ou descendre d'un ou plusieurs octaves, ce qui est bien pratique pour un petit clavier ne couvrant que 3 octaves (ce sont les boutons "octave up" relié à l'entrée numérique 7, et "octave down" relié à l'entrée numérique 9).
Bien entendu, chacun de ces quatre boutons doit être branché de la façon habituelle, avec un "pull up resistor". Si vous faites le calcul, vous constaterez que toutes les entrées/sorties numérique de mon Duemilanove sont maintenant occupées (sauf le numéro 0). Si je veux ajouter d'autres fonctionnalités à mon clavier (ce qui est le cas!), il ne me reste plus que les 6 entrées analogiques...je crois bien que l'occasion d'acquérir un Arduino Mega est finalement arrivée!
Voici le sketch:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/************************************************************** | |
* Clavier MIDI | |
* Ce sketch permet de transformer un vieux clavier | |
* d'orgue électronique en un clavier MIDI. | |
* http://electroniqueamateur.blogspot.ca/ | |
* Mai 2012, Yves Pelletier | |
* https://electroniqueamateur.blogspot.com/2012/05/clavier-midi-base-darduino.html | |
**************************************************************/ | |
int latchPin = 8; //Pin de l'Arduino branchée à la Pin 12 du 74HC595 (Latch) | |
int clockPin = 12; //Pin de l'Arduino branchée à la Pin 11 du 74HC595 (Clock) | |
int dataPin = 11; //Pin de l'Arduino branchée à la Pin 14 du 74HC595 (Data) | |
int octave1In= 2; // pin de l'Arduino qui reçoit le voltage pour le 1er octave | |
int octave2In = 3; // pin de l'Arduino qui reçoit le voltage pour le 2e octave | |
int octave3In = 4; // pin de l'Arduino qui reçoit le voltage pour le 3e octave | |
int octave4In = 5; // pin de l'Arduino qui reçoit le voltage pour le 4e octave | |
int progDown = 13; //bouton pour reculer dans les programmes | |
int progUp = 10; //bouton pour avancer dans les programmes | |
int octaveDown = 9; //bouton pour monter d'un octave | |
int octaveUp = 7; //bouton pour descendre d'un octave | |
int octave = 0; //valeur initiale par défaut de l'octave | |
int programme = 0; //valeur initiale par défaut du programme | |
int progDownReady = 1; // mis à vrai quand le bouton n'est pas enfoncé | |
int progUpReady = 1; // idem | |
int octaveDownReady = 1; //idem | |
int octaveUpReady = 1; // idem | |
int etat_des_touches[37]; // 1 quand la touche est enfoncée, 0 sinon | |
void setup() { | |
pinMode(latchPin, OUTPUT); // registre à décalage | |
pinMode(clockPin, OUTPUT); | |
pinMode(dataPin, OUTPUT); | |
pinMode(octave1In, INPUT); | |
pinMode(octave2In, INPUT); | |
pinMode(octave3In, INPUT); | |
pinMode(octave4In, INPUT); | |
pinMode(progDown,INPUT); //boutons pour changer | |
pinMode(progUp,INPUT); // de programme | |
pinMode(octaveDown,INPUT); //boutons pour monter | |
pinMode(octaveUp,INPUT); // ou descendre d'un octave | |
Serial.begin(31250); //pour transmission MIDI | |
// on commence: aucune touche n'est enfoncée au départ | |
for (int i=0; i<=36; i++) | |
{ | |
etat_des_touches[i]=0; | |
} | |
} | |
void loop() { | |
int laNote,octaveIn; | |
byte byte1, byte2; | |
//on vérifie s'il faut changer de programme: | |
if (!digitalRead(progDown)){ | |
progDownReady=1; | |
} | |
if (!digitalRead(progUp)){ | |
progUpReady=1; | |
} | |
if (digitalRead(progDown) && (programme > 0)){ | |
if (progDownReady){ | |
programme = programme-1; | |
ProgChange(0,programme); | |
} | |
progDownReady=0; | |
} | |
if (digitalRead(progUp) && (programme < 127)){ | |
if (progUpReady){ | |
programme=programme+1; | |
ProgChange(0,programme); | |
} | |
progUpReady=0; | |
} | |
// on vérifie s'il faut changer d'octave: | |
if (!digitalRead(octaveDown)){ | |
octaveDownReady=1; | |
} | |
if (!digitalRead(octaveUp)){ | |
octaveUpReady=1; | |
} | |
if (digitalRead(octaveDown) && (octave > -2)){ | |
if (octaveDownReady){ | |
octave = octave-1; | |
} | |
octaveDownReady=0; | |
} | |
if (digitalRead(octaveUp) && (octave < 5)){ | |
if (octaveUpReady){ | |
octave=octave+1; | |
} | |
octaveUpReady=0; | |
} | |
// on scan les notes du clavier | |
for (laNote=0; laNote<=36; laNote++){ | |
// définition de l'état des broches des registres à décalage | |
if (laNote <= 7){ | |
octaveIn = octave1In; | |
byte1 = B11110001; | |
} //FA1 à DO1 | |
if (laNote == 0){ | |
byte2 = B11111110; | |
} //FA1 | |
if (laNote == 1){ | |
byte2 = B11111101; | |
} //FA#1 | |
if (laNote == 2){ | |
byte2 = B11111011; | |
} //SOL1 | |
if (laNote == 3){ | |
byte2 = B11110111; | |
} //SOL#1 | |
if (laNote == 4){ | |
byte2 = B11101111; | |
} //LA1 | |
if (laNote == 5){ | |
byte2 = B11011111; | |
} //SIb1 | |
if (laNote == 6){ | |
byte2 = B10111111; | |
} //SI1 | |
if (laNote == 7){ | |
byte2 = B01111111; | |
} //DO1 | |
if ((laNote >= 8) &&(laNote <=19) ){ | |
octaveIn = octave2In; | |
} //DO#1 à DO2 | |
if ((laNote >= 8) &&(laNote <=11) ){ | |
byte2 = B11111111; | |
} //DO#1 à MI1 | |
if (laNote == 8){ | |
byte1 = B11100010; | |
} //DO#1 | |
if (laNote == 9){ | |
byte1 = B11010010; | |
} //RE1 | |
if (laNote == 10){ | |
byte1 = B10110010; | |
} //RE#1 | |
if (laNote == 11){ | |
byte1 = B01110010; | |
} //MI1 | |
if ((laNote >= 12) &&(laNote <=19) ){ | |
byte1 = B11110010; | |
} //FA2 à DO2 | |
if (laNote == 12){ | |
byte2 = B11111110; | |
} //FA2 | |
if (laNote == 13){ | |
byte2 = B11111101; | |
} //FA#2 | |
if (laNote == 14){ | |
byte2 = B11111011; | |
} //SOL2 | |
if (laNote == 15){ | |
byte2 = B11110111; | |
} //SOL#2 | |
if (laNote == 16){ | |
byte2 = B11101111; | |
} //LA2 | |
if (laNote == 17){ | |
byte2 = B11011111; | |
} //SIb2 | |
if (laNote == 18){ | |
byte2 = B10111111; | |
} //SI2 | |
if (laNote == 19){ | |
byte2 = B01111111; | |
} //DO2 | |
if ((laNote >= 20) &&(laNote <=31) ){ | |
octaveIn = octave3In; | |
} //DO#2 à DO3 | |
if ((laNote >= 20) &&(laNote <=23) ){ | |
byte2 = B11111111; | |
} //DO#2 à MI2 | |
if (laNote == 20){ | |
byte1 = B11100100; | |
} //DO#2 | |
if (laNote == 21){ | |
byte1 = B11010100; | |
} //RE2 | |
if (laNote == 22){ | |
byte1 = B10110100; | |
} //RE#2 | |
if (laNote == 23){ | |
byte1 = B01110100; | |
} //MI2 | |
if ((laNote >= 24) &&(laNote <=31) ){ | |
byte1 = B11110100; | |
} //FA3 à DO3 | |
if (laNote == 24){ | |
byte2 = B11111110; | |
} //FA3 | |
if (laNote == 25){ | |
byte2 = B11111101; | |
} //FA#3 | |
if (laNote == 26){ | |
byte2 = B11111011; | |
} //SOL3 | |
if (laNote == 27){ | |
byte2 = B11110111; | |
} //SOL#3 | |
if (laNote == 28){ | |
byte2 = B11101111; | |
} //LA3 | |
if (laNote == 29){ | |
byte2 = B11011111; | |
} //SIb3 | |
if (laNote == 30){ | |
byte2 = B10111111; | |
} //SI3 | |
if (laNote == 31){ | |
byte2 = B01111111; | |
} //DO3 | |
if ((laNote >= 32) &&(laNote <=36) ){ | |
octaveIn = octave4In; | |
} //DO#3 à FA4 | |
if ((laNote >= 32) &&(laNote <=35) ){ | |
byte2 = B11111111; | |
} //DO#3 à MI3 | |
if (laNote == 32){ | |
byte1 = B11101000; | |
} //DO#3 | |
if (laNote == 33){ | |
byte1 = B11011000; | |
} //RE3 | |
if (laNote == 34){ | |
byte1 = B10111000; | |
} //RE#3 | |
if (laNote == 35){ | |
byte1 = B01111000; | |
} //MI3 | |
if (laNote == 36){ | |
byte1 = B11111000; | |
byte2 = B11111110; | |
} //FA4 | |
shiftOut(byte1,byte2); | |
delay(1); | |
if (!digitalRead(octaveIn)) // touche enfoncée | |
{ | |
if (!etat_des_touches[laNote]) { //elle ne l'était pas auparavant | |
etat_des_touches[laNote]=1; | |
noteOn(0x90, 0x22+laNote+12*octave, 0x60); | |
} | |
} | |
else // touche non enfoncée | |
{ | |
if (etat_des_touches[laNote]) { //elle ne l'était pas auparavant | |
etat_des_touches[laNote]=0; | |
noteOn(0x90, 0x22+laNote+12*octave, 0x00); | |
} | |
} | |
} //rof | |
} | |
void noteOn(int cmd, int pitch, int velocity) { | |
Serial.print(cmd, BYTE); | |
Serial.print(pitch, BYTE); | |
Serial.print(velocity, BYTE); | |
} | |
void ProgChange(int channel, int prognumber) { | |
Serial.print(0xC0+channel, BYTE); | |
Serial.print(prognumber, BYTE); | |
} | |
// la routine suivante n'est pas de moi...piqué quelque part... | |
void shiftOut(byte dataOut1,byte dataOut2) { | |
boolean pinState; | |
digitalWrite(latchPin, LOW); | |
digitalWrite(dataPin, LOW); | |
digitalWrite(clockPin, LOW); | |
for (int i=0; i<=7; i++) { | |
digitalWrite(clockPin, LOW); | |
if ( dataOut1 & (1<<i) ) { | |
pinState = HIGH; | |
} | |
else { | |
pinState = LOW; | |
} | |
digitalWrite(dataPin, pinState); | |
digitalWrite(clockPin, HIGH); | |
digitalWrite(dataPin, LOW); | |
} | |
digitalWrite(clockPin, LOW); | |
digitalWrite(dataPin, LOW); | |
digitalWrite(clockPin, LOW); | |
for (int i=0; i<=7; i++) { | |
digitalWrite(clockPin, LOW); | |
if ( dataOut2 & (1<<i) ) { | |
pinState = HIGH; | |
} | |
else { | |
pinState = LOW; | |
} | |
digitalWrite(dataPin, pinState); | |
digitalWrite(clockPin, HIGH); | |
digitalWrite(dataPin, LOW); | |
} | |
digitalWrite(clockPin, LOW); | |
digitalWrite(latchPin, HIGH); | |
} | |
À venir: l'ajout d'un "pitch bend" (possiblement contrôlé par un joystick), d'un potentiomètre contrôlant le volume (la vélocité, en fait), d'un arpégiateur, peut-être... De plus, ce serait amusant d'y ajouter un module de synthèse de son pour que l'instrument puisse produire ses propres sons sans obligatoirement être relié à un autre clavier MIDI... Et si j'en fait une keytar, ce serait bien plus pratique d'envoyer les messages MIDI au moyen d'ondes radio plutôt que par l'entremise d'un encombrant câble MIDI!
On n'a pas fini de s'amuser!
Yves Pelletier (Twitter: @ElectroAmateur)
Aucun commentaire:
Enregistrer un commentaire