arduino
MIDI-kommunikation
Sök…
Introduktion
Syftet med detta ämne att demonstrera några grundläggande MIDI-program som visar hur man arbetar med protokollet och gradvis lägger till användbara funktioner som mer komplexa applikationer kräver.
MIDI THRU Exempel
MIDI Thru är enkel och enkel att testa. När du arbetar korrekt kommer du att kunna installera ditt Arduino-projekt mellan två MIDI-enheter, MIDI IN till MIDI OUT och du kommer att kunna verifiera att de två enheterna fungerar tillsammans. Om du har möjlighet att mäta latens ser du en ökning på grund av seriell buffertinsamling och omöverföringsinstruktioner.
// 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 genom med kö
// 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-meddelanden definierade
Generellt är MIDI-protokollet uppdelat i "meddelanden". Det finns fyra allmänna klasser av meddelanden:
- Kanalröst
- Kanalläge
- System vanligt
- Systemmeddelanden i realtid
Meddelanden börjar med ett bytevärde över 0x80. Alla värden under 0x7F betraktas som data. Det betyder effektivt att 127 är det maximala värdet som kan kodas till en enda MIDI-databyte. För att koda större värden krävs två eller flera MIDI-databyte.
Det bör påpekas att meddelanden måste skickas från början till slut utan avbrott ... UNDANTAG ... System i realtid meddelanden, som är en enda byte, som kan injiceras i mitten av alla meddelanden.
Kanals röstmeddelanden
Status D7..D0 | Databytes | Beskrivning |
---|---|---|
1000nnnn | 0kkkkkkk 0vvvvvvv | Obs Off-händelse. Detta meddelande skickas när en anteckning släpps (avslutas). (kkkkkkk) är nyckelnumret (anteckning). (vvvvvvv) är hastigheten. |
1001nnnn | 0kkkkkkk 0vvvvvvv | Obs om händelse. Det här meddelandet skickas när en anteckning är nedtryckt (start). (kkkkkkk) är nyckelnumret (anteckning). (vvvvvvv) är hastigheten. |
1010nnnn | 0kkkkkkk 0vvvvvvv | Polyfoniskt tangenttryck (Aftertouch). Det här meddelandet skickas oftast genom att trycka på tangenten efter att den "bottnar ut". (kkkkkkk) är nyckelnumret (anteckning). (vvvvvvv) är tryckvärdet. |
1011nnnn | 0ccccccc 0vvvvvvv | Kontrolländring. Det här meddelandet skickas när en styrenhetsvärde ändras. Regulatorer inkluderar enheter som pedaler och spakar. Styrenhetsnummer 120-127 är reserverade som "Meddelande om kanalläge" (nedan). (ccccccc) är regulatorns nummer (0-119). (vvvvvvv) är regulatorns värde (0-127). |
1100nnnn | 0ppppppp | Programbyte. Det här meddelandet skickas när korrigeringsnumret ändras. (ppppppp) är det nya programnumret. |
1101nnnn | 0vvvvvvv | Kanaltryck (After-touch). Det här meddelandet skickas oftast genom att trycka på tangenten efter att den "bottnar ut". Det här meddelandet skiljer sig från polyfonisk efter-beröring. Använd det här meddelandet för att skicka det enskilt största tryckvärdet (av alla aktuella nedtryckta tangenter). (vvvvvvv) är tryckvärdet. |
1110nnnn | 0lllllll 0mmmmmmm | Pitch Bend Change. Det här meddelandet skickas för att indikera en förändring i tonhöjdbockaren (hjul eller spak, vanligtvis). Stigböjaren mäts med ett fjortonbitsvärde. Center (ingen tonhöjd förändring) är 2000H. Känslighet är en mottagares funktion, men kan ställas in med RPN 0. (lllllll) är de minst betydande 7 bitarna. (mmmmmmm) är de viktigaste 7 bitarna. |
Meddelande om kanalläge
Status D7..D0 | Databytes | Beskrivning |
---|---|---|
1011nnnn | 0ccccccc 0vvvvvvv | Meddelande om kanalläge. Detta är samma kod som kontrolländringen (ovan), men implementerar lägesstyrning och specialmeddelande med hjälp av reserverade styrenhetsnummer 120-127. Kommandona är: |
Allt ljud av. När All Sound Off tas emot kommer alla oscillatorer att stängas av och deras volym kuvert ställs in på noll så snart som möjligt. c = 120, v = 0: Allt ljud av | ||
Återställ alla kontroller. När Återställ alla styrenheter tas emot återställs alla kontrollvärden till sina standardvärden. (Se specifika rekommenderade metoder för standardvärden). | ||
c = 121, v = x: Värdet får endast vara noll om inte annat tillåts i en specifik rekommenderad praxis. | ||
Lokal kontroll. När lokal kontroll är av, svarar alla enheter på en given kanal endast på data som mottas via MIDI. Uppspelade data etc. ignoreras. Local Control On återställer funktionerna för de normala kontrollerna. | ||
c = 122, v = 0: Lokal kontroll av | ||
c = 122, v = 127: Lokal kontroll på | ||
Alla anteckningar av. När en Alla anteckningar är mottagna kommer alla oscillatorer att stängas av. | ||
c = 123, v = 0: Alla anteckningar av (Se text för beskrivning av verkliga lägeskommandon.) | ||
c = 124, v = 0: Omni-läget är av | ||
c = 125, v = 0: Omni-läge på | ||
c = 126, v = M: Mono Mode On (Poly Off) där M är antalet kanaler (Omni Off) eller 0 (Omni On) | ||
c = 127, v = 0: Poly Mode On (Mono Off) (Obs: Dessa fyra meddelanden orsakar också alla anteckningar av) |
System vanliga meddelanden
Status D7..D0 | Databytes | Beskrivning |
---|---|---|
11110000 | 0iiiiiii [0iiiiiii 0iiiiiii] 0ddddddd --- --- 0ddddddd 11110111 | Exklusivt system. Den här meddelandetypen gör det möjligt för tillverkare att skapa sina egna meddelanden (som bulkdumpar, patchparametrar och andra icke-spec-data) och tillhandahåller en mekanism för att skapa ytterligare MIDI-specifikationsmeddelanden. Tillverkarens ID-kod (tilldelad av MMA eller AMEI) är antingen 1 byte (0iiiiiii) eller 3 byte (0iiiiiii 0iiiiiii 0iiiiiii). Två av de 1 byte-ID: erna är reserverade för tillägg som kallas Universal Exclusive Messages, som inte är tillverkarens specifika. Om en enhet känner igen ID-koden som sin egen (eller som ett universellt meddelande som stöds) lyssnar det på resten av meddelandet (0ddddddd). Annars ignoreras meddelandet. (Obs! Endast realtidsmeddelanden får interfolieras med ett system exklusivt.) |
11110001 | 0nnndddd | MIDI Time Code Quarter Frame. nnn = Meddelandetyp dddd = Värden |
11110010 | 0lllllll 0mmmmmmm | Song Position Pointer. Detta är ett internt 14-bitarsregister som har antalet MIDI-beats (1 takt = sex MIDI-klockor) sedan låtens början. Jag är LSB, m MSB. |
11110011 | 0sssssss | Song Select. Song Select anger vilken sekvens eller låt som ska spelas. |
11110100 | Odefinierad. (Reserverad) | |
11110101 | Odefinierad. (Reserverad) | |
11110110 | Ställ in begäran. Vid mottagning av en Tune Request bör alla analoga synthesizers ställa in sina oscillatorer. | |
11110111 | Slutet av exklusivt. Används för att avsluta en system exklusiv dumpning (se ovan). |
Systemmeddelanden i realtid
Status D7..D0 | Databytes | Beskrivning |
---|---|---|
11111000 | Tidsklocka. Skickas 24 gånger per kvartals anteckning när synkronisering krävs (se text). | |
11111001 | Odefinierad. (Reserverad) | |
11111010 | Start. Starta den aktuella sekvensen som spelas. (Detta meddelande följs med Timing Clocks). | |
11111011 | Fortsätta. Fortsätt vid den tidpunkt då sekvensen stoppades. | |
11111100 | Sluta. Stoppa den aktuella sekvensen. | |
11111101 | Odefinierad. (Reserverad) | |
11111110 | Aktiv avkänning. Det här meddelandet är avsett att skickas upprepade gånger för att berätta för mottagaren att en anslutning lever. Användning av detta meddelande är valfritt. När mottagaren ursprungligen mottas förväntar sig att få ett nytt Active Sensing-meddelande varje 300 ms (max), och om det inte gör det kommer den att anta att anslutningen har avslutats. Vid avslutningen stänger mottagaren av alla röster och återgår till normal (icke-aktiv avkänning). | |
11111111 | Återställa. Återställ alla mottagare i systemet till startstatus. Detta bör användas sparsamt, helst under manuell kontroll. I synnerhet bör det inte skickas vid uppstart. |