arduino
MIDI-communicatie
Zoeken…
Invoering
De bedoeling van dit onderwerp om enkele basis MIDI-programma's te demonstreren die laten zien hoe te werken met het protocol en geleidelijk nuttige functies toevoegen die complexere toepassingen vereisen.
MIDI THRU Voorbeeld
De MIDI Thru is eenvoudig en gemakkelijk te testen. Als u goed werkt, kunt u uw Arduino-project tussen twee MIDI-apparaten, MIDI IN naar MIDI OUT, installeren en kunt u controleren of de twee apparaten samen werken. Als u de latentie kunt meten, ziet u een toename als gevolg van het vastleggen van seriële buffers en het opnieuw verzenden van instructies.
// This is a simple MIDI THRU. Everything in, goes right out.
// This has been validate on an Arduino UNO and a Olimex MIDI Shield
boolean byteReady;
unsigned char midiByte;
void setup() {
// put your setup code here, to run once:
// Set MIDI baud rate:
Serial.begin(31250);
byteReady = false;
midiByte = 0;
}
// The Loop that always gets called...
void loop() {
if (byteReady) {
byteReady = false;
Serial.write(midiByte);
}
}
// The little function that gets called each time loop is called.
// This is automated somwhere in the Arduino code.
void serialEvent() {
if (Serial.available()) {
// get the new byte:
midiByte = (unsigned char)Serial.read();
byteReady = true;
}
}
MIDI Thru met wachtrij
// This is a more complex MIDI THRU. This version uses a queue. Queues are important because some
// MIDI messages can be interrupted for real time events. If you are generating your own messages,
// you may need to stop your message to let a "real time" message through and then resume your message.
#define QUEUE_DEPTH 128
// Queue Logic for storing messages
int headQ = 0;
int tailQ = 0;
unsigned char tx_queue[QUEUE_DEPTH];
void setup() {
// put your setup code here, to run once:
// Set MIDI baud rate:
Serial.begin(31250);
}
// getQDepth checks for roll over. Folks have told me this
// is not required. Feel free to experiment.
int getQDepth() {
int depth = 0;
if (headQ < tailQ) {
depth = QUEUE_DEPTH - (tailQ - headQ);
} else {
depth = headQ - tailQ;
}
return depth;
}
void addQueue (unsigned char myByte) {
int depth = 0;
depth = getQDepth();
if (depth < (QUEUE_DEPTH-2)) {
tx_queue[headQ] = myByte;
headQ++;
headQ = headQ % QUEUE_DEPTH; // Always keep the headQ limited between 0 and 127
}
}
unsigned char deQueue() {
unsigned char myByte;
myByte = tx_queue[tailQ];
tailQ++;
tailQ = tailQ % QUEUE_DEPTH; // Keep this tailQ contained within a limit
// Now that we dequeed the byte, it must be sent.
return myByte;
}
void loop() {
if (getQDepth>0) {
Serial.write(deQueue());
}
}
// The little function that gets called each time loop is called.
// This is automated somwhere in the Arduino code.
void serialEvent() {
if (Serial.available()) {
// get the new byte:
addQueue((unsigned char)Serial.read());;
}
}
MIDI Clock Generation
// This is a MiDI clk generator. This takes a #defined BPM and
// makes the appropriate clk rate. The queue is used to let other messages
// through, but allows a clock to go immediately to reduce clock jitter
#define QUEUE_DEPTH 128
#define BPM 121
#define MIDI_SYSRT_CLK 0xF8
// clock tracking and calculation
unsigned long lastClock;
unsigned long captClock;
unsigned long clk_period_us;
// Queue Logic for storing messages
int headQ = 0;
int tailQ = 0;
unsigned char tx_queue[QUEUE_DEPTH];
void setup() {
// Set MIDI baud rate:
Serial.begin(31250);
clk_period_us = 60000000 / (24 * BPM);
lastClock = micros();
}
// getQDepth checks for roll over. Folks have told me this
// is not required. Feel free to experiment.
int getQDepth() {
int depth = 0;
if (headQ < tailQ) {
depth = QUEUE_DEPTH - (tailQ - headQ);
} else {
depth = headQ - tailQ;
}
return depth;
}
void addQueue (unsigned char myByte) {
int depth = 0;
depth = getQDepth();
if (depth < (QUEUE_DEPTH-2)) {
tx_queue[headQ] = myByte;
headQ++;
headQ = headQ % QUEUE_DEPTH; // Always keep the headQ limited between 0 and 127
}
}
unsigned char deQueue() {
unsigned char myByte;
myByte = tx_queue[tailQ];
tailQ++;
tailQ = tailQ % QUEUE_DEPTH; // Keep this tailQ contained within a limit
// Now that we dequeed the byte, it must be sent.
return myByte;
}
void loop() {
captClock = micros();
if (lastClock > captClock) {
// we have a roll over condition - Again, maybe we don't need to do this.
if (clk_period_us <= (4294967295 - (lastClock - captClock))) {
// Add a the ideal clock period for this BPM to the last measurement value
lastClock = lastClock + clk_period_us;
// Send a clock, bypasing the transmit queue
Serial.write(MIDI_SYSRT_CLK);
}
} else if (clk_period_us <= captClock-lastClock) {
// Basically the same two commands above, but not within a roll over check
lastClock = lastClock + clk_period_us;
// Send a clock, bypasing the transmit queue
Serial.write(MIDI_SYSRT_CLK);
}
if (getQDepth>0) {
Serial.write(deQueue());
}
}
// The little function that gets called each time loop is called.
// This is automated somwhere in the Arduino code.
void serialEvent() {
if (Serial.available()) {
// get the new byte:
addQueue((unsigned char)Serial.read());;
}
}
MIDI-berichten gedefinieerd
Over het algemeen is het MIDI-protocol onderverdeeld in "berichten". Er zijn 4 algemene klassen van berichten:
- Kanaalstem
- Kanaalmodus
- Systeem gemeenschappelijk
- Systeem realtime berichten
Berichten beginnen met een bytewaarde boven 0x80. Elke waarde onder 0x7F wordt beschouwd als data. Dit betekent in feite dat 127 de maximale waarde is die kan worden gecodeerd in een enkele MIDI-databyte. Om grotere waarden te coderen, zijn twee of meer MIDI-databytes vereist.
Opgemerkt moet worden dat berichten van begin tot einde zonder onderbreking moeten worden verzonden ... BEHALVE ... Real-Time systeemberichten, die een enkele byte zijn, die in het midden van elk bericht kunnen worden geïnjecteerd.
Kanaal spraakberichten
Status D7..D0 | Gegevensbytes | Beschrijving |
---|---|---|
1000nnnn | 0kkkkkkk 0vvvvvvv | Note Off-gebeurtenis. Dit bericht wordt verzonden wanneer een notitie wordt vrijgegeven (beëindigd). (kkkkkkk) is het nummer van de toets (noot). (vvvvvvv) is de snelheid. |
1001nnnn | 0kkkkkkk 0vvvvvvv | Opmerking over gebeurtenis. Dit bericht wordt verzonden wanneer een notitie is ingedrukt (start). (kkkkkkk) is het nummer van de toets (noot). (vvvvvvv) is de snelheid. |
1010nnnn | 0kkkkkkk 0vvvvvvv | Polyfone toetsdruk (aftertouch). Dit bericht wordt meestal verzonden door op de toets te drukken nadat deze is "ingedrukt". (kkkkkkk) is het nummer van de toets (noot). (vvvvvvv) is de drukwaarde. |
1011nnnn | 0ccccccc 0vvvvvvv | Controle wijzigen. Dit bericht wordt verzonden wanneer een controllerwaarde verandert. Controllers omvatten apparaten zoals pedalen en hendels. Controllernummers 120-127 zijn gereserveerd als "Kanaalmodusberichten" (hieronder). (ccccccc) is het controllernummer (0-119). (vvvvvvv) is de controllerwaarde (0-127). |
1100nnnn | 0ppppppp | Programmawijziging. Dit bericht wordt verzonden wanneer het patchnummer verandert. (ppppppp) is het nieuwe programmanummer. |
1101nnnn | 0vvvvvvv | Kanaaldruk (na aanraking). Dit bericht wordt meestal verzonden door op de toets te drukken nadat deze is "ingedrukt". Dit bericht verschilt van polyfone after-touch. Gebruik dit bericht om de grootste drukwaarde te verzenden (van alle momenteel ingedrukte toetsen). (vvvvvvv) is de drukwaarde. |
1110nnnn | 0lllllll 0mmmmmmm | Pitch Bend Change. Dit bericht wordt verzonden om een verandering in de pitchbuiger aan te geven (typisch wiel of hendel). De pitchbuder wordt gemeten met een waarde van veertien bits. Midden (geen verandering van toonhoogte) is 2000H. Gevoeligheid is een functie van de ontvanger, maar kan worden ingesteld met RPN 0. (lllllll) zijn de minst significante 7 bits. (mmmmmmm) zijn de belangrijkste 7 bits. |
Kanaalmodusberichten
Status D7..D0 | Gegevensbytes | Beschrijving |
---|---|---|
1011nnnn | 0ccccccc 0vvvvvvv | Kanaalmodusberichten. Dit is dezelfde code als de besturingswijziging (hierboven), maar implementeert moduscontrole en speciale melding met behulp van gereserveerde controllernummers 120-127. De commando's zijn: |
Alles klinkt uit. Wanneer All Sound Off wordt ontvangen, worden alle oscillators uitgeschakeld en worden hun volume-enveloppen zo snel mogelijk op nul gezet. c = 120, v = 0: All Sound Off | ||
Reset alle controllers. Wanneer Reset All Controllers wordt ontvangen, worden alle controllerwaarden teruggezet naar hun standaardwaarden. (Zie specifieke aanbevolen werkwijzen voor standaardinstellingen). | ||
c = 121, v = x: waarde mag alleen nul zijn, tenzij anders toegestaan in een specifieke aanbevolen praktijk. | ||
Lokale bediening. Wanneer Local Control is uitgeschakeld, reageren alle apparaten op een bepaald kanaal alleen op gegevens die via MIDI worden ontvangen. Afgespeelde gegevens, etc. worden genegeerd. Local Control On herstelt de functies van de normale controllers. | ||
c = 122, v = 0: Local Control Off | ||
c = 122, v = 127: Local Control On | ||
Alle notities uitgeschakeld. Wanneer een All Notes Off wordt ontvangen, worden alle oscillators uitgeschakeld. | ||
c = 123, v = 0: Alle noten uit (zie tekst voor beschrijving van actuele modusopdrachten.) | ||
c = 124, v = 0: Omni Mode Off | ||
c = 125, v = 0: Omni Mode On | ||
c = 126, v = M: Monomodus Aan (Poly Uit) waarbij M het aantal kanalen is (Omni Uit) of 0 (Omni Aan) | ||
c = 127, v = 0: Poly Mode aan (Mono uit) (Opmerking: deze vier berichten veroorzaken ook alle noten uit) |
Algemene systeemberichten
Status D7..D0 | Gegevensbytes | Beschrijving |
---|---|---|
11110000 | 0iiiiiii [0iiiiiii 0iiiiiii] 0ddddddd --- --- 0ddddddd 11110111 | Exclusief systeem. Met dit berichttype kunnen fabrikanten hun eigen berichten maken (zoals bulkdumps, patchparameters en andere niet-spec-gegevens) en biedt een mechanisme voor het maken van extra MIDI-specificatieberichten. De ID-code van de fabrikant (toegewezen door MMA of AMEI) is 1 byte (0iiiiiii) of 3 bytes (0iiiiiii 0iiiiiii 0iiiiiii). Twee van de 1 Byte ID's zijn gereserveerd voor extensies genaamd Universal Exclusive Messages, die niet fabrikant-specifiek zijn. Als een apparaat de ID-code herkent als zijn eigen (of als een ondersteund universeel bericht), luistert het naar de rest van het bericht (0ddddddd). Anders wordt het bericht genegeerd. (Opmerking: alleen realtime berichten mogen worden afwisselend met een systeemexclusie.) |
11110001 | 0nnndddd | MIDI Time Code Quarter Frame. nnn = Berichttype dddd = Waarden |
11110010 | 0lllllll 0mmmmmmm | Nummerpositie-aanwijzer. Dit is een intern 14-bits register dat het aantal MIDI-beats (1 beat = zes MIDI-klokken) sinds het begin van de song bevat. Ik ben de LSB, m de MSB. |
11110011 | 0sssssss | Nummer selecteren. De Song Select geeft aan welke reeks of song moet worden gespeeld. |
11110100 | Undefined. (Gereserveerd) | |
11110101 | Undefined. (Gereserveerd) | |
11110110 | Verzoek afstemmen. Bij ontvangst van een afstemmingsverzoek moeten alle analoge synthesizers hun oscillators afstemmen. | |
11110111 | Einde van Exclusief. Wordt gebruikt om een System Exclusive dump te beëindigen (zie hierboven). |
Systeem realtime berichten
Status D7..D0 | Gegevensbytes | Beschrijving |
---|---|---|
11111000 | Timing Clock. 24 keer per kwartnoot verzonden wanneer synchronisatie vereist is (zie tekst). | |
11111001 | Undefined. (Gereserveerd) | |
11111010 | Begin. Start het afspelen van de huidige reeks. (Dit bericht wordt gevolgd met timingklokken). | |
11111011 | Doorgaan met. Ga door op het punt waar de reeks is gestopt. | |
11111100 | Hou op. Stop de huidige reeks. | |
11111101 | Undefined. (Gereserveerd) | |
11111110 | Actieve detectie. Dit bericht is bedoeld om herhaaldelijk te worden verzonden om de ontvanger te vertellen dat er een verbinding actief is. Gebruik van dit bericht is optioneel. Wanneer de ontvanger voor het eerst wordt ontvangen, verwacht deze elke 300ms (max) een ander Active Sensing-bericht te ontvangen, en als dit niet het geval is, wordt ervan uitgegaan dat de verbinding is verbroken. Bij beëindiging zal de ontvanger alle stemmen uitschakelen en terugkeren naar de normale (niet-actieve detectie) werking. | |
11111111 | Reset. Reset alle ontvangers in het systeem naar de opstartstatus. Dit moet spaarzaam worden gebruikt, bij voorkeur onder handmatige bediening. Het mag met name niet worden verzonden bij het opstarten. |