samedi 19 mai 2012

Jolie trouvaille

La saison des ventes de garage est commencée au Québec (en Europe, vous appelez ça un vide-grenier, ce qui me semble beaucoup plus approprié puisque je n'ai jamais vu un seul garage à vendre dans une vente de garage...).

Ce matin, j'ai eu le bonheur de tomber sur tout un lot de matériel électronique gratuit.  De haut en bas sur la photo:  un tourne-disque, un synthonisateur AM-FM, un lecteur de cassettes, et un lecteur de CDs (tous de marque Sony), en plus d'une robuste imprimante laser.

L'ancien propriétaire semblait très heureux de se départir de ces appareils désuets et encombrants;  de mon côté,  je gagne des heures de plaisir à démonter ces appareils à la recherche de moteurs et d'autres composants qui pourront m'être utiles dans la réalisation de mes projets.

Pendant ce temps, une énorme quantité d'appareils de ce genre finissent à la poubelle...vraiment dommage!

Yves Pelletier (Twitter: @ElectroAmateur)

samedi 12 mai 2012

Clavier MIDI à base d'Arduino

Résumé du projet:  un vieux clavier récupéré d'un vieil orgue Yamaha est transformé en clavier midi grâce à un Arduino.

Cet article fait suite aux articles suivants:

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:

/**************************************************************
* 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)

mardi 8 mai 2012

Décodage d'un clavier d'orgue par un Arduino

Résumé du projet:  un vieux clavier d'orgue électronique est branché à un Arduino par l'entremise de deux registres à décalage HC595.  Lorsqu'on appuie sur une ou plusieurs touches du clavier, le nom des notes activées s'affiche à l'écran de l'ordinateur.

Le but ultime (qui sera atteint, je l'espère, dans un prochain article) sera de construire moi-même un instrument à clavier complet:  un clavier MIDI, un synthétiseur, une Keytar, peut-être?

J'ai déjà décrit le clavier et sa matrice dans un article précédent:  il a été récupéré d'un vieil orgue électronique Yamaha Electone qui avait été fabriqué au début des années '80.  Le clavier comporte 37 touches (3 octaves) et j'ai utilisé sans modification sa matrice de diodes.

Le schéma ci-dessous montre la matrice du clavier (dont je n'ai représenté que 4 des 37 touches).  Il y a 4 entrées d'octaves.  La première est reliée aux 8 premières touches du clavier (de FA à DO), la deuxième est reliée aux 12 touches suivantes (de DO# à DO), etc.  Il y a 12 entrées de notes:  une même entrée pour tous les "sol" , une autre entrée pour tous les "la", etc.

Pour identifier les touches qui sont enfoncées, j'ai opté pour le principe suivant (voir le circuit ci-dessous):  si je veux vérifier l'état de la touche "sol 1" je place l'entrée "octave 1" à 5 volts, les autres entrées d'octave à 0 volts, l'entrée "sol" à 0 V et les autres entrées de notes (la, si, do, etc.) à 5 V.

De cette façon, la sortie "octave 1" sera à 5 V si la touche "sol 1" est enfoncée puisqu'aucun courant ne circule dans la résistance.  Le voltage de la sortie "octave 1" deviendra (idéalement) 0 V lorsque la touche "sol 1" est enfoncée.  L'état des autres touches du même octave ne change rien, puisque ces touches ne sont soumises à aucune différence de potentiel (5 volts de part et d'autre).

Il s'agit ensuite de tester l'état de chacune des 37 touches du clavier, l'une après l'autre, ce que l'Arduino pourra faire très rapidement.


Mais pas question d'utiliser 20 entrées/sorties de l'Arduino (4 entrées d'octave, 12 entrées de notes et 4 sorties d'octave), surtout que mon Duemilanove en comporte beaucoup moins...  J'ai donc utilisé deux registres à décalage 74HC595 en cascade.  Puisque chaque registre à décalage comporte 8 sorties, ils contrôleront les 4 entrées d'octave et les 12 entrées de notes.


Si vous n'êtes pas familiers avec les registres à décalage, je mentionne rapidement qu'il s'agit d'un circuit intégré qui transforme un signal numérique 8 bits reçu de façon séquentielle à son entrée en un signal équivalent étalé en parallèle sur 8 sorties.  Par exemple, l'Arduino peut envoyer le message "11111111" à l'entrée du registre à décalage; sur réception de cette instruction, le régistre mettra ses 8 sorties  à 5 volts.  Si l'Arduino envoie le message "01010101", 4 sorties seront à 0 volt, les autres seront à 5 volts, etc.

Le schéma ci-dessous montre les branchements des deux registres à décalage.

Il restait ensuite à coder un sketch pour que l'Arduino vérifie les 37 touches du clavier l'une après l'autre.  J'ai d'abord tenté de produire  un programme élégant avec des boucles et des opérateurs "bitwise"...et je me suis lamentablement cassé la gueule:  rien ne fonctionnait!    Voici donc une version peu élégante, très répétitive,  mais qui a l'avantage de fonctionner (et qui est facile à comprendre, je crois), dans laquelle je vérifie l'état de chaque touche une par une.