Catégories
Liens
:
Dans le cadre de la création de son spectacle Rien n'arrive ! Et ça arrive souvent !, la compagnie l'Art de Vivre nous a confié la fabrication d'un contrôleur MIDI utilisant des leviers pour déclencher des sons. L'association Laisser Passer nous a permis d'extraire de leur matériaux portuaires à recycler huit leviers articulés en aluminium qui seront la base du contrôleur. Ces huit levier ON/OFF actionnerons des sons, un neuvième levier gradué permettra de régler un volume ou des effets et deux entrées pour pédale d'expression ou switch en complétera le jeu. Les pièces nécessaires à ce fonctionnement (butées avant et arrières et le ressort de rappel, maintien des boutons poussoirs…) ont été conçues autour de ces leviers et découpé au LFO. Enfin, un cadre maintenant solidement le tout à été fabriqué en acier soudé.
Afin d'éviter les redondances dans le code, les entrées sont déclarées comme des structs contenant toutes les variables qui se réfèrent à l'entrée : numéro de pin, durée d'anti-rebond, note midi émise, valeur actuelle, valeur précédente… Ces structs sont ajoutés dans deux tableaux, l'un contenant les capteurs analogiques, l'autre les capteurs numériques. L'initialisation de toutes les entrées ainsi que leur traitement peut ainsi se faire sous forme de boucle for. Le midi est géré par la lib MIDIUSB de Gary Grewal. Des événements note-on et note-off sont envoyés quand un interrupteur est actionné et des control changes dès qu'une entrée analogique est bougée (pédale ou levier gradué). Chaque bouton est assigné à une note midi de C0 à B0 et les contrôles continus vont du numéro 20 au 22. Ce code implémente une zone morte autour de la dernière valeur analogique lue pour éviter l'envoi massif de messages non-significatifs. Toutefois comme l'Arduino offre une acquisition de 10bits (0~1023) et les valeurs MIDI restent bornées à 7bits (0~127 soit un demi-octet), la zone morte n'est pas nécessaire ici.
Pour lire les valeurs brutes et calibrer les entrées analogiques, un define en première ligne peut être dé-commenté. Il transforme alors le port USB de l'arduino Leonardo non plus en périphérique MIDI mais en port série, permettant de lire sur le moniteur série d'Arduino les valeurs brutes et mise à l'échelle MIDI des capteurs telles qu'elles sont envoyées en mode normal.
Les #define sont parfois ignorés pour les cartes Leonardo, Due et Micro par le pré-processeur des versions récentes d'arduino. le code téléchargeable en en-tête n'en possède pas pour rester compatible avec toutes les versions d'IDE d'Arduino.
Le code du projet peut être consulté ci-dessous :
//#define CALIBRATION #ifndef CALIBRATION #include "MIDIUSB.h" #endif #define CHANNEL 0x1 #define DEBOUNCE 2 // in millis #define DEADBAND // 0~1023 static const unsigned int maxRate = 0; // in millis struct analog { String name; byte controllerNumber; unsigned int pin; unsigned int calibMin; unsigned int calibMax; unsigned int lastValue; unsigned int currentValue; unsigned int deadBand; }; analog analogs[] = { {"pot", 20, A0, 290, 930, 0, 0, 0}, {"pedal1", 21, A1, 0, 530, 0, 0, 0}, {"pedal2", 22, A2, 0, 530, 0, 0, 0} }; unsigned int analogsCount = sizeof(analogs)/sizeof(analog); struct digital { String name; byte midiNote; unsigned int pin; bool lastState; unsigned int debounce; long lastTriggered; }; digital digitals[] = { {"A0", 33, 2, true, DEBOUNCE, 0}, {"F0", 29, 3, true, DEBOUNCE, 0}, {"D0", 26, 4, true, DEBOUNCE, 0}, {"G0", 31, 5, true, DEBOUNCE, 0}, {"E0", 28, 6, true, DEBOUNCE, 0}, {"B0", 35, 7, true, DEBOUNCE, 0}, {"C0", 24, 8, true, DEBOUNCE, 0}, {"Bb0", 34, 9, true, DEBOUNCE, 0},}; unsigned int digitalsCount = sizeof(digitals)/sizeof(digital); void setup() { // setting up the digital inputs for (unsigned int i=0; i<digitalsCount; i++){ pinMode(digitals[i].pin, INPUT_PULLUP); digitals[i].lastState = digitalRead(digitals[i].pin); } // setting up the analog inputs for (unsigned int i=0; i<analogsCount; i++) { analogs[i].currentValue = map(analogRead(analogs[i].pin), analogs[i].calibMin, analogs[i].calibMax, 0, 127); } #ifdef CALIBRATION Serial.begin(115200); delay(100); Serial.println("connected, "+String(analogsCount)+" analog inputs and "+String(digitalsCount)+" digital inputs configured"); #endif } void loop() { // Reading analog inputs for (unsigned int i=0; i<analogsCount; i++) { analogs[i].currentValue = constrain(analogRead(analogs[i].pin), analogs[i].calibMin, analogs[i].calibMax); analogs[i].currentValue = map(analogs[i].currentValue, analogs[i].calibMin, analogs[i].calibMax, 0, 127); //if (analogs[i].lastValue != analogs[i].currentValue) { if (analogs[i].lastValue < analogs[i].currentValue - analogs[i].deadBand || analogs[i].lastValue > analogs[i].currentValue + analogs[i].deadBand) { #ifdef CALIBRATION Serial.println(analogs[i].name + ": RAW"+String(analogRead(analogs[i].pin))+", SCALED : "+ String(analogs[i].currentValue)); //analogs[i].lastValue = analogs[i].currentValue; #else byte ccValue = analogs[i].currentValue; controlChange(analogs[i].controllerNumber, ccValue); #endif delay(maxRate); } //} analogs[i].lastValue = analogs[i].currentValue; } // Reading digital inputs for (unsigned int i=0; i<digitalsCount; i++) { bool currentState = digitalRead(digitals[i].pin); // input has been pulled LOW if (currentState == LOW && digitals[i].lastState == HIGH && millis() - digitals[i].lastTriggered > digitals[i].debounce) { #ifdef CALIBRATION Serial.println(digitals[i].name+":ON"); #else noteOn(digitals[i].midiNote, (byte) 127); #endif digitals[i].lastTriggered = millis(); } // input has been pulled HIGH if (currentState == HIGH && digitals[i].lastState == LOW && millis() - digitals[i].lastTriggered > digitals[i].debounce) { #ifdef CALIBRATION Serial.println(digitals[i].name+":OFF"); #else noteOff(digitals[i].midiNote); #endif digitals[i].lastTriggered = millis(); } digitals[i].lastState = currentState; } #ifndef CALIBRATION MidiUSB.flush(); #endif delay(5); } #ifndef CALIBRATION void noteOn(byte pitch, byte velocity) { midiEventPacket_t noteOn = {0x09, 0x90 | CHANNEL, pitch, velocity}; MidiUSB.sendMIDI(noteOn); } void noteOff(byte pitch) { midiEventPacket_t noteOff = {0x08, 0x80 | CHANNEL, pitch, 0x0}; MidiUSB.sendMIDI(noteOff); } void controlChange(byte control, byte value) { midiEventPacket_t event = {0x0B, 0xB0 | CHANNEL, control, value}; MidiUSB.sendMIDI(event); } #endif