C'est quoi, un séquenceur MIDI?
Un séquenceur musical est un dispositif qui permet de programmer un motif musical (une suite de notes), puis de jouer ce motif en boucle à une vitesse choisie par l'utilisateur.
Le séquenceur que j'ai construit utilise la norme MIDI pour communiquer avec un clavier musical. Lorsqu'il est en mode "record", il mémorise les notes jouées sur le clavier. Lorsqu'il est en mode "play", il envoie au clavier musical les instructions nécessaires pour que les notes soient jouées en boucle.
L'intérêt principal de ce séquenceur si on le compare à celui que j'avais déjà fabriqué auparavant, c'est que vous pouvez le brancher à un instrument de musique de bonne qualité et obtenir des résultats professionnels, plutôt que produire des sonorités assez primitives au moyen de la fonction "tone" de l'Arduino. Par contre ce séquenceur ne vous sera d'aucune utilité si vous ne disposez pas d'un instrument adhérant à la norme MIDI.
Le circuit
Tout d'abord vous avez besoin d'un Arduino (j'ai utilisé un vieux Duemilanove, qui est l'équivalent du Uno).
Pour la communication MIDI, j'ai utilisé ce module MIDI que j'avais fabriqué il y a quelques mois. Vous avez besoin du circuit complet, avec l'optocoupleur et tout, car les messages MIDI circuleront dans les deux sens.
Si vous êtes riche et paresseux (ou riche et impatient), vous pouvez également vous procurer le MIDI shield de Sparkfun, mais ça me semble cher pour un circuit pas très compliqué.
Ensuite, il y a les boutons-poussoirs: un premier qui permet d'entrer et sortir du mode "play", et un deuxième qui permet d'entrer et sortir du mode "record". Au départ, j'avais utilisé des boutons-poussoirs momentanés: chaque fois que le bouton est abaissé, le mode associé au bouton change d'état. Mais j'avais d'irritants problèmes de rebonds qui persistaient malgré l'ajout de procédures anti-rebond dans le sketch. Je les ai donc remplacé par des interrupteurs à deux position: lorsqu'on appuie dessus, ils ferment le circuit, et lorsqu'on appuie dessus à nouveau, ils ouvrent le circuit. Si vous utilisez des boutons-poussoirs momentanés, vous devrez modifier mon sketch en conséquence (ou garder le doigt sur le bouton, ce qui n'est pas très pratique).
Maintenant que j'y pense, l'idéal serait un seul commutateur à trois positions: record - off - play. Ça éviterait que les deux modes "record" et "play" soient actifs en même temps, ce qui n'est pas souhaitable et n'a pas été prévu dans le sketch.
Donc l'interrupteur qui contrôle le mode "record" relie l'entrée 6 à la masse (GND), alors que celui qui contrôle le mode "play" relie l'entrée 7 à la masse. Le sketch utilise les pull-ups internes, donc pas la peine d'ajouter des résistances au circuit.
Nous avons aussi deux LEDs indicatrices: une LED rouge (branchée à la sortie 8) qui s'allume pour indiquer qu'on est en mode "record", et une LED verte (branchée à la sortie 9 ) qui s'allume pour indiquer qu'on est en mode "play". Prévoir une résistance protectrice de 220 Ω pour chaque LED.
Finalement, deux potentiomètre (j'ai utilisé 10kΩ, mais ça n'a pas une très grande importance). Un premier potentiomètre (branché à A0) contrôle le tempo, en d'autres mots la rapidité à laquelle les notes sont jouées. Un deuxième potentiomètre (branché à A1) permet de sélectionner le programme, c'est à dire le timbre utilisé par le clavier MIDI pour jouer la mélodie (piano, trompette, guitare, etc.).
Le sketch
Au départ, j'espérais utiliser tel quel ce sketch de Vance Gloster, conçu pour être utilisé avec le shield MIDI de Sparkfun: Old-School Arduino MIDI Sequencer. Malheureusement, je ne parvenais pas à enregistrer mes motifs à partir de son sketch (j'ignore s'il s'agit d'une erreur de ma part ou d'une particularité de mon clavier MIDI). J'ai vainement tenté de modifier son sketch, mais je ne m'y retrouvais pas du tout. Alors je me suis résigné à écrire mon propre sketch à partir de zéro, ce qui s'est avéré beaucoup plus simple que ce que j'avais prévu.
Ce sketch nécessite l'installation préalable de la bibliothèque MIDI.
Attention: chaque fois que vous téléversez le sketch dans l'Arduino, vous devrez probablement débrancher le fil branché dans la pin 0 (Rx), sinon le téléversement échoue. (C'est la vie!)
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
/**************************************************************************************** | |
* Séquenceur MIDI à base d'Arduino | |
* Yves Pelletier | |
* http://electroniqueamateur.blogspot.com/2014/01/sequenceur-midi-base-darduino.html | |
****************************************************************************************/ | |
#include <MIDI.h> // MIDI library | |
// définition des pins | |
#define recordButton 6 | |
#define playButton 7 | |
#define recordLED 8 | |
#define playLED 9 | |
#define tempoPot A0 | |
#define programPot A1 | |
#define tailleMaxPattern 128 // on peut enregistrer une mélodie de 128 notes (ou moins) | |
int recordMode = 0; // sera 1 en mode "record" | |
int previousRecordMode = 0; // 1 si on était déjà en mode "record" | |
int playMode = 0; // sera 1 en mode "play" | |
int previousPlayMode = 0; // 1 si on était déjà en mode "play" | |
int program; // numéro de l'instrument utilisé pour jouer les notes | |
int delaiEntreLesNotes; // en millisecondes | |
unsigned long previousNoteTime = 0; // moment où la dernière note a été jouée (en millisecondes) | |
byte pattern [tailleMaxPattern] = { 72,76,72,79,72,84,72,76,72,79,72,84,77,81,77,84,77,89,77,81,77,84,77,89,72,76,72,79,72,84,72,76,72,79,72,84,71,74,71,79,71,83,71,74,71,79,71,83 }; // motif par défaut | |
int playIndex = 0; // numéro de la note à jouer dans pattern | |
int recordIndex = 0; // numéro de la note à enregistrer dans pattern | |
int maxRecordIndex = 48; //le nombre de notes enregistrées dans le pattern | |
void setup() { | |
// initialisation des LEDs | |
pinMode(recordLED, OUTPUT); | |
pinMode(playLED, OUTPUT); | |
// initialisation des boutons | |
pinMode(recordButton, INPUT); | |
pinMode(playButton, INPUT); | |
digitalWrite(recordButton, HIGH); // on active la résistance pullup interne | |
digitalWrite(playButton, HIGH); | |
recordMode = 0; | |
playMode = 0; | |
// Initialisation de la librairie MIDI | |
MIDI.begin(MIDI_CHANNEL_OMNI); | |
// On indique à la librairie MIDI que la routine "HandleNoteOn" devra s'exécuter | |
// lorsqu'on joue une note au clavier | |
MIDI.setHandleNoteOn(HandleNoteOn); // Put only the name of the function | |
} | |
void loop() { | |
// lecture des deux boutons | |
recordMode = !digitalRead(recordButton); | |
playMode = !digitalRead(playButton); | |
// les LEDs indiquent le mode dans lequel on se trouve | |
digitalWrite(recordLED, recordMode); | |
digitalWrite(playLED, playMode); | |
// lecture du potentiomètre de tempo | |
delaiEntreLesNotes = 30 + analogRead(tempoPot); | |
// lecture du potentiomètre de Programme | |
// on peut diviser par un nombre plus grand que 5 pour avoir moins de programmes possibles, | |
// mais meilleure stabilité | |
if (program != analogRead(programPot)/5){ // le programme doit changer | |
program = analogRead(programPot)/5; | |
MIDI.sendProgramChange(program,1); // numéro de programme, canal | |
} | |
if (!recordMode && previousRecordMode){ // on vient de sortir du mode record | |
maxRecordIndex = recordIndex; | |
previousRecordMode = 0; | |
} | |
if (recordMode){ | |
if (!previousRecordMode){ // on vient tout juste de passer au mode record | |
recordIndex = 0; | |
previousRecordMode = 1; | |
} | |
MIDI.read(); | |
} | |
if (!playMode && previousPlayMode){ // on vient de sortir du mode play | |
// on envoie un note off pour la dernière note jouée | |
MIDI.sendNoteOn(pattern[playIndex-1],0,1); | |
previousPlayMode = 0; | |
} | |
if (playMode){ | |
if (!previousPlayMode){ // on vient tout juste de passer au mode play | |
playIndex = 0; | |
previousPlayMode = 1; | |
} | |
if ((millis() - previousNoteTime) >= delaiEntreLesNotes){ // c'est le temps de jouer une note | |
previousNoteTime = millis(); | |
if (playIndex!=0){ // on arrête la note précédente | |
MIDI.sendNoteOn(pattern[playIndex-1],0,1); | |
} | |
if (playIndex >= maxRecordIndex){ // on retourne au début de la mélodie | |
playIndex = 0; | |
} | |
MIDI.sendNoteOn(pattern[playIndex],127,1); | |
playIndex = playIndex + 1; | |
} // if ((millis() - previousNoteTime) >= delaiEntreLesNotes) | |
} // if playMode | |
} | |
// cette routine s'exécute chaque fois qu'une note est jouée au clavier | |
void HandleNoteOn(byte channel, byte pitch, byte velocity) { | |
if ((recordMode) && (velocity > 0) && (recordIndex < tailleMaxPattern)){ | |
pattern[recordIndex] = pitch; | |
recordIndex = recordIndex + 1; | |
} | |
} | |
Utilisation
Vous appuyez sur le bouton "play": la LED verte s'allume et, si tout va bien, le clavier MIDI va jouer une suite de note. Tournez les deux potentiomètres pour modifier le tempo et le timbre de l'instrument. Appuyez de nouveau sur le bouton "play": la LED verte s'éteint, et la musique cesse de jouer.
Vous appuyez sur le bouton "record": la LED rouge s'allume. Vous jouez une suite de notes sur les touches du clavier. Lorsque vous avez terminé, vous appuyez sur le bouton "record" à nouveau, et la LED rouge s'éteint pour indiquer que vous êtes sortis du mode "record". Vous appuyez sur play: c'est la mélodie que vous venez d'enregistrer qui est jouée.
Améliorations possibles
Pour une utilisation "sérieuse" du dispositif, il serait essentiel de pouvoir enregistrer sur un eeprom ou sur une carte SD les motifs musicaux qu'on désire conserver, et de faire jouer ces motifs à volonté. Un contrôle du volume sonore serait également souhaitable. Ce sera pour la version 2.0.
Yves Pelletier (Twitter: @ElectroAmateur)
Bonjour. Merci pour cet article. Ce petit montage a l'air trés sympa. J'ai une question à vous poser avant de m'y mettre : les notes sont-elles rejouées comme on les a jouées, avec la même temporalité ? Merci
RépondreSupprimer