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.


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow