arduino
Komunikacja MIDI
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. |