Szukaj…


Wprowadzenie

Celem tego tematu jest pokazanie podstawowych programów MIDI, które pokazują, jak operować protokołem i stopniowo dodają użyteczne funkcje, których wymagają bardziej złożone aplikacje.

Przykład MIDI THRU

MIDI Thru jest prosty i łatwy do przetestowania. Przy prawidłowym działaniu będziesz mógł zainstalować swój projekt Arduino między dwoma urządzeniami MIDI, MIDI IN na MIDI OUT i będziesz mógł sprawdzić, czy oba urządzenia działają razem. Jeśli masz możliwość pomiaru opóźnienia, zauważysz wzrost z powodu instrukcji przechwytywania i ponownego przesyłania bufora szeregowego.

// 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 z kolejką

// 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());;
  }
}

Generowanie zegara MIDI

// 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());;
  }
}

Zdefiniowane komunikaty MIDI

Ogólnie protokół MIDI jest podzielony na „wiadomości”. Istnieją 4 ogólne klasy wiadomości:

  • Głos kanału
  • Tryb kanału
  • Wspólny system
  • Systemowe komunikaty w czasie rzeczywistym

Wiadomości zaczynają się od wartości bajtu powyżej 0x80. Każda wartość poniżej 0x7F jest uważana za dane. Skutecznie oznacza, że 127 jest wartością maksymalną, którą można zakodować w jednym bajcie danych MIDI. Aby zakodować większe wartości, wymagane są dwa lub więcej bajtów danych MIDI.

Należy zauważyć, że wiadomości muszą być wysyłane od początku do końca bez przerwy ... Z WYJĄTKIEM ... Wiadomości systemowe w czasie rzeczywistym, które są pojedynczymi bajtami, które można wstawić w środku każdej wiadomości.

Wiadomości głosowe kanału

Status D7..D0 Bajty danych Opis
1000nnnn 0kkkkkkk 0vvvvvvv Notatka - zdarzenie wyłączone. Ta wiadomość jest wysyłana po zwolnieniu (zakończeniu) notatki. (kkkkkkk) to numer klucza (uwagi). (vvvvvvv) to prędkość.
1001nnnn 0kkkkkkk 0vvvvvvv Uwaga W przypadku zdarzenia. Ta wiadomość jest wysyłana, gdy notatka jest wciśnięta (start). (kkkkkkk) to numer klucza (uwagi). (vvvvvvv) to prędkość.
1010nnnn 0kkkkkkk 0vvvvvvv Nacisk klawisza polifonicznego (Aftertouch). Ta wiadomość jest najczęściej wysyłana przez naciśnięcie klawisza po „dnie”. (kkkkkkk) to numer klucza (uwagi). (vvvvvvv) to wartość ciśnienia.
1011nnnn 0ccccccc 0vvvvvvv Kontrola zmian. Ten komunikat jest wysyłany, gdy zmienia się wartość kontrolera. Kontrolery obejmują takie urządzenia, jak pedały i dźwignie. Numery kontrolerów 120-127 są zarezerwowane jako „Komunikaty trybu kanału” (poniżej). (ccccccc) to numer kontrolera (0-119). (vvvvvvv) to wartość kontrolera (0-127).
1100nnnn 0ppppppp Zmiana programu. Ten komunikat jest wysyłany, gdy zmienia się numer poprawki. (ppppppp) to nowy numer programu.
1101nnnn 0vvvvvvv Ciśnienie kanału (po dotknięciu). Ta wiadomość jest najczęściej wysyłana przez naciśnięcie klawisza po „dnie”. Ta wiadomość różni się od polifonicznego after-touch. Użyj tego komunikatu, aby wysłać największą pojedynczą wartość ciśnienia (spośród wszystkich aktualnie wciśniętych klawiszy). (vvvvvvv) to wartość ciśnienia.
1110nnnn 0lllllll 0mmmmmmm Zmiana skoku skoku. Ten komunikat jest wysyłany w celu wskazania zmiany gięcia pochylenia (zazwyczaj koło lub dźwignia). Gięcie skoku mierzone jest za pomocą czternastobitowej wartości. Centrum (bez zmiany wysokości dźwięku) to 2000H. Czułość jest funkcją odbiornika, ale można ją ustawić za pomocą RPN 0. (lllllll) to najmniej znaczące 7 bitów. (mmmmmmm) to najbardziej znaczące 7 bitów.

Komunikaty trybu kanału

Status D7..D0 Bajty danych Opis
1011nnnn 0ccccccc 0vvvvvvv Komunikaty trybu kanału. Jest to ten sam kod, co zmiana sterowania (powyżej), ale implementuje kontrolę trybu i komunikat specjalny za pomocą zarezerwowanych numerów kontrolerów 120-127. Polecenia to:
Wszystkie dźwięki wyłączone. Po odebraniu All Sound Off wszystkie oscylatory zostaną wyłączone, a ich obwiednie głośności zostaną jak najszybciej wyzerowane. c = 120, v = 0: Wszystkie dźwięki wyłączone
Zresetuj wszystkie kontrolery. Po odebraniu Resetuj wszystkie kontrolery wszystkie wartości kontrolerów są resetowane do wartości domyślnych. (Zobacz szczegółowe Zalecane praktyki dotyczące wartości domyślnych).
c = 121, v = x: Wartość musi wynosić tylko zero, chyba że jest to dozwolone w konkretnej zalecanej praktyce.
Kontrola lokalna. Gdy sterowanie lokalne jest wyłączone, wszystkie urządzenia na danym kanale będą reagować tylko na dane odbierane przez MIDI. Odtwarzane dane itp. Zostaną zignorowane. Local Control On przywraca funkcje normalnych sterowników.
c = 122, v = 0: Wyłączone sterowanie lokalne
c = 122, v = 127: Sterowanie lokalne włączone
Wszystkie notatki wyłączone. Po otrzymaniu All Notes Off wszystkie oscylatory zostaną wyłączone.
c = 123, v = 0: Wszystkie notatki wyłączone (zobacz tekst dla opisu poleceń trybu rzeczywistego.)
c = 124, v = 0: Wyłączony tryb Omni
c = 125, v = 0: Włączony tryb Omni
c = 126, v = M: Tryb mono włączony (Poly Off), gdzie M to liczba kanałów (Omni Off) lub 0 (Omni On)
c = 127, v = 0: Tryb Poly włączony (mono wyłączony) (Uwaga: te cztery komunikaty powodują również wyłączenie wszystkich notatek)

Wspólne komunikaty systemowe

Status D7..D0 Bajty danych Opis
11110000 0iiiiiii [0iiiiiii 0iiiiiii] 0ddddddd --- --- 0ddddddd 11110111 Wyłącznie z systemu. Ten typ komunikatu umożliwia producentom tworzenie własnych komunikatów (takich jak zrzuty zbiorcze, parametry łaty i inne niespecyficzne dane) oraz zapewnia mechanizm tworzenia dodatkowych komunikatów specyfikacji MIDI. Kod identyfikacyjny producenta (przypisany przez MMA lub AMEI) to 1 bajt (0iiiiiii) lub 3 bajty (0iiiiiii 0iiiiiii 0iiiiiii). Dwa z 1 bajtów identyfikatorów są zarezerwowane dla rozszerzeń o nazwie Universal Exclusive Messages, które nie są specyficzne dla producenta. Jeśli urządzenie rozpozna kod ID jako swój własny (lub jako obsługiwany komunikat uniwersalny), nasłuchuje reszty wiadomości (0ddddddd). W przeciwnym razie wiadomość zostanie zignorowana. (Uwaga: tylko wiadomości w czasie rzeczywistym mogą być przeplatane z wyłącznym systemem).
11110001 0nnndddd Kwadratowa ramka kodu czasu MIDI. nnn = Typ komunikatu dddd = Wartości
11110010 0lllllll 0mmmmmmm Wskaźnik pozycji utworu. Jest to wewnętrzny 14-bitowy rejestr, który przechowuje liczbę uderzeń MIDI (1 takt = sześć zegarów MIDI) od początku utworu. l to LSB, m MSB.
11110011 0sssssss Song Select. Wybór utworu określa, która sekwencja lub utwór ma być odtwarzany.
11110100 Nieokreślony. (Zarezerwowany)
11110101 Nieokreślony. (Zarezerwowany)
11110110 Żądanie dostrojenia. Po otrzymaniu żądania dostrojenia wszystkie syntezatory analogowe powinny nastroić swoje oscylatory.
11110111 Koniec wyłączności. Służy do zakończenia zrzutu System Exclusive (patrz wyżej).

Systemowe komunikaty w czasie rzeczywistym

Status D7..D0 Bajty danych Opis
11111000 Zegar rozrządu. Wysyłane 24 razy na kwartał, gdy wymagana jest synchronizacja (patrz tekst).
11111001 Nieokreślony. (Zarezerwowany)
11111010 Początek. Rozpocznij odtwarzanie bieżącej sekwencji. (Po tym komunikacie pojawią się Zegary rozrządu).
11111011 Kontyntynuj. Kontynuuj od momentu zatrzymania sekwencji.
11111100 Zatrzymać. Zatrzymaj bieżącą sekwencję.
11111101 Nieokreślony. (Zarezerwowany)
11111110 Aktywne wykrywanie. Ten komunikat ma być wysyłany wielokrotnie w celu poinformowania odbiorcy, że połączenie jest aktywne. Korzystanie z tej wiadomości jest opcjonalne. Po początkowym odebraniu odbiornik spodziewa się otrzymywać kolejną wiadomość Active Sensing co 300 ms (maks.), A jeśli nie, zakłada, że połączenie zostało zakończone. Po zakończeniu odbiornik wyłączy wszystkie głosy i powróci do normalnej pracy (nieaktywne wykrywanie).
11111111 Resetowanie. Zresetuj wszystkie odbiorniki w systemie do stanu włączenia. Należy tego używać oszczędnie, najlepiej pod kontrolą ręczną. W szczególności nie należy go wysyłać po włączeniu zasilania.


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow