MIDI ist ein Industriestandard für den Austausch musikalischer Steuerinformationen zwischen elektronischen Instrumenten, z.B. Keyboards oder Synthesizern. Mittlerweile ist man nicht mehr auf die 5-poligen MIDI-DIN-Stecker angewiesen sondern kann USB als alternativen Übertragungsweg verwenden. Dies macht den Arduino interessant als MIDI-Controller oder sogar MIDI-fähiges Gerät.
Der Arduino kann über die seriellen Schnittstelle (USB) Daten an den angeschlossenen PC schicken. Diese können dann als MIDI-Befehle interpretiert werden. Dafür benötigt man eine spezielle Software, z.B. Hairless MIDI. Diese freie/kostenlose Software lädt man sich einfach herunter, packt das Archiv aus und startet die "hairless-midiserial.exe". In "File -> Preferences" muss noch die Baud-Rate auf dieselbe Übertragungsrate des Arduino-Sketches (hier: 9600) eingestellt werden. Nun wird in der Hauptoberfläche des Programms der serielle Port ein verfügbares "MIDI-Out"-Gerät.
Der folgende Sketch sendet der MIDI-Befehl zum spielen einer einzigen Note.
Wichtig beim Hochladen des Sketches ist, dass in Hairless MIDI der serielle Port deaktiviert ist. Nach dem
Hochladen kann dieser dann wieder aktiviert werden.
byte noteON = 144; // note on command
void setup()
{
// set "Hairless MIDI" to the same baud rate in the preferences
Serial.begin(9600);
}
void loop()
{
MIDImessage(noteON, 60, 100); // turn note on
delay(300);
MIDImessage(noteON, 60, 0); // turn note off (note on with velocity 0)
delay(200);
}
void MIDImessage(byte command, byte data1, byte data2)
{
Serial.write(command);
Serial.write(data1);
Serial.write(data2);
}
Der folgende Sketch lässt über den Arduino MIDI-Befehle für einen Akkord und eine Notensequenz abspielen.
byte noteON = B10010000;
byte noteOFF = B10000000;
void setup()
{
// set "Hairless MIDI" to the same baud rate in the preferences
Serial.begin(9600);
}
void loop()
{
MIDImessage(noteON, 60, 65);
MIDImessage(noteON, 63, 65);
MIDImessage(noteON, 67, 65);
delay(500);
MIDImessage(noteOFF, 60, 0);
MIDImessage(noteON, 61, 65);
delay(500);
MIDImessage(noteOFF, 61, 0);
MIDImessage(noteON, 60, 65);
delay(500);
MIDImessage(noteOFF, 60, 0);
MIDImessage(noteON, 61, 65);
delay(500);
MIDImessage(noteOFF, 61, 0);
MIDImessage(noteON, 60, 65);
MIDImessage(noteOFF, 63, 0);
MIDImessage(noteOFF, 67, 0);
delay(1500);
}
void MIDImessage(byte command, byte data1, byte data2)
{
Serial.write(command);
Serial.write(data1);
Serial.write(data2);
}
So (oder ähnlich) sollte sich das Resultat des obigen Sketches anhören:
(253kB)
In der MIDI-Spezifikation wird die Note mit der Nummer 60 (dezimal) als "mittleres C" (C4) angegeben, wobei alle anderen Noten dann relativ dazu eingegliedert werden. Die folgende Tabelle gibt die Notenwerten im Einzelnen an. Bei einigen MIDI-Geräten könnte es sein, dass die tiefste Oktave 0 ist, dann wäre das mittlere C dann ein C5. Die tiefste Note auf einem MIDI-Gerät ist jedoch immer ein C.
Oktave | C | C# | D | D# | E | F | F# | G | G# | A | A# | H |
---|---|---|---|---|---|---|---|---|---|---|---|---|
-1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
0 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
1 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
2 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
3 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
4 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 |
5 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 |
6 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
7 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 |
8 | 108 | 108 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 |
9 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | - | - | - | - |
Der folgende Sketch steuert durch externe Bauteile (Mikrotaster und Potentiometer) das Verhalten des MIDI-Gerätes auf dem PC. In unserem Falle können wir mit dem Mikrotaster die Instrumente durchwählen, das eine Potentiometer steuert die Tonhöhe und das andere Potentiometer den Pitch der Note.
#define PIN_BUTTON A5
#define PIN_POTENTIOMETER A4
#define PIN_LINEAR_POTENTIOMETER A3
#define PHASE_PAUSE 0
#define PHASE_PLAY 1
const byte noteON = B10010000;
const byte noteOFF = B10000000;
const byte pitchBend = B11100000;
const byte programChange = B11000000; // changing instrument
const byte noteVelocity = 65;
const int delayNote = 1000;
const int delayPause = 100;
const int potentiometerMaxValue = 675;
int value = 0, oldPitchValue = 0, oldNoteValue = 0;
bool buttonPressed = false;
byte currentNote = 0, currentInstrument = 1, currentPitch = 0, currentPhase = PHASE_PAUSE;
unsigned long lastPhaseChange = 0;
void setup()
{
// set "Hairless MIDI" to the same baud rate in the preferences
Serial.begin(9600);
lastPhaseChange = millis();
}
void loop()
{
value = analogRead(PIN_BUTTON);
if (!buttonPressed && value < 10) {
buttonPressed = true;
currentInstrument++;
if (currentInstrument>128) {
currentInstrument = 1;
}
MIDImessage(programChange, currentInstrument, 255);
}
if (buttonPressed && value > 500) {
buttonPressed = false;
}
value = analogRead(PIN_POTENTIOMETER);
if (oldNoteValue!=value) {
oldNoteValue = value;
currentNote = map(value, 0, potentiometerMaxValue, 0, 127);
}
value = analogRead(PIN_LINEAR_POTENTIOMETER);
if (oldPitchValue-2 > value || oldPitchValue+2 < value) {
oldPitchValue = value;
currentPitch = map(value, 0, potentiometerMaxValue, 0, 127);
MIDImessage(pitchBend, 0, currentPitch);
}
switch(currentPhase) {
case PHASE_PLAY:
if (millis() > lastPhaseChange) {
MIDImessage(noteON, currentNote, noteVelocity);
lastPhaseChange = millis() + delayNote;
currentPhase = PHASE_PAUSE;
}
break;
case PHASE_PAUSE:
if (millis() > lastPhaseChange) {
MIDImessage(noteOFF, currentNote, 0);
currentPhase = PHASE_PLAY;
lastPhaseChange = millis() + delayPause;
}
break;
}
}
void MIDImessage(byte command, byte data1, byte data2)
{
Serial.write(command);
if (data1 != 255) {
Serial.write(data1);
}
if (data2 != 255) {
Serial.write(data2);
}
}