arduino
Comunicazione MIDI
Ricerca…
introduzione
Lo scopo di questo argomento è di dimostrare alcuni programmi MIDI di base che mostrano come operare con il protocollo e aggiungere progressivamente funzionalità utili richieste da applicazioni più complesse.
MIDI THRU Esempio
MIDI Thru è semplice e facile da testare. Quando si lavora correttamente, sarà possibile installare il progetto Arduino tra due dispositivi MIDI, MIDI IN su MIDI OUT e sarà possibile verificare che i due dispositivi funzionino insieme. Se hai la capacità di misurare la latenza, vedrai un aumento dovuto alla cattura del buffer seriale e alle istruzioni di ritrasmissione.
// 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 con coda
// 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());;
}
}
Messaggi MIDI definiti
In generale, il protocollo MIDI è suddiviso in "messaggi". Esistono 4 classi generali di messaggi:
- Channel Voice
- Modalità Canale
- Sistema comune
- Messaggi in tempo reale del sistema
I messaggi iniziano con un valore di byte sopra 0x80. Qualsiasi valore inferiore a 0x7F è considerato dati. Significato efficace del fatto che 127 è il valore massimo che può essere codificato in un singolo byte di dati MIDI. Per codificare valori più grandi, sono necessari due o più byte di dati MIDI.
Va sottolineato che i messaggi devono essere inviati dall'inizio alla fine senza interruzioni ... TRANNE ... messaggi in tempo reale del sistema, che sono un singolo byte, che può essere iniettato nel mezzo di qualsiasi messaggio.
Messaggi vocali del canale
Stato D7..D0 | Byte di dati | Descrizione |
---|---|---|
1000nnnn | 0kkkkkkk 0vvvvvvv | Evento Nota Off. Questo messaggio viene inviato quando una nota viene rilasciata (terminata). (kkkkkkk) è il numero della chiave (nota). (vvvvvvv) è la velocità. |
1001nnnn | 0kkkkkkk 0vvvvvvv | Nota sull'evento. Questo messaggio viene inviato quando una nota è depressa (inizio). (kkkkkkk) è il numero della chiave (nota). (vvvvvvv) è la velocità. |
1010nnnn | 0kkkkkkk 0vvvvvvv | Pressione polifonica (Aftertouch). Questo messaggio viene inviato più spesso premendo sul tasto dopo che "esce dal fondo". (kkkkkkk) è il numero della chiave (nota). (vvvvvvv) è il valore di pressione. |
1011nnnn | 0ccccccc 0vvvvvvv | Control Change. Questo messaggio viene inviato quando cambia un valore del controller. I controller includono dispositivi come pedali e leve. I numeri di controller 120-127 sono riservati come "Messaggi in modalità canale" (sotto). (ccccccc) è il numero del controller (0-119). (vvvvvvv) è il valore del controller (0-127). |
1100nnnn | 0ppppppp | Cambio di programma. Questo messaggio viene inviato quando il numero di patch cambia. (ppppppp) è il nuovo numero di programma. |
1101nnnn | 0vvvvvvv | Pressione del canale (dopo il tocco). Questo messaggio viene inviato più spesso premendo sul tasto dopo che "esce dal fondo". Questo messaggio è diverso dal post-tocco polifonico. Utilizzare questo messaggio per inviare il singolo valore di pressione più grande (di tutti i tasti attualmente premuti). (vvvvvvv) è il valore di pressione. |
1110nnnn | 0lllllll 0mmmmmmm | Pitch Bend Change. Questo messaggio viene inviato per indicare un cambiamento nella curvatura (ruota o leva, in genere). La pitch bender è misurata con un valore di quattordici bit. Centro (nessun cambiamento di intonazione) è 2000H. La sensibilità è una funzione del ricevitore, ma può essere impostata usando RPN 0. (lllllll) sono i 7 bit meno significativi. (mmmmmmm) sono i 7 bit più significativi. |
Messaggi in modalità canale
Stato D7..D0 | Byte di dati | Descrizione |
---|---|---|
1011nnnn | 0ccccccc 0vvvvvvv | Messaggi in modalità canale. Questo è lo stesso codice di Control Change (sopra), ma implementa il controllo della modalità e il messaggio speciale usando i numeri di controller riservati 120-127. I comandi sono: |
All Sound Off. Quando viene ricevuto All Sound Off, tutti gli oscillatori si spengono e gli inviluppi di volume vengono impostati su zero il prima possibile. c = 120, v = 0: All Sound Off | ||
Ripristina tutti i controller. Quando viene ricevuto il messaggio Ripristina tutti i controller, tutti i valori del controller vengono ripristinati ai valori predefiniti. (Vedi Pratiche raccomandate specifiche per i valori predefiniti). | ||
c = 121, v = x: il valore deve essere zero solo se diversamente consentito in una specifica pratica raccomandata. | ||
Controllo locale. Quando Local Control è Off, tutti i dispositivi su un determinato canale risponderanno solo ai dati ricevuti via MIDI. I dati riprodotti, ecc. Saranno ignorati. Local Control On ripristina le funzioni dei normali controller. | ||
c = 122, v = 0: controllo locale disattivato | ||
c = 122, v = 127: Local Control On | ||
Tutte le note disattivate. Quando viene ricevuto un All Notes Off, tutti gli oscillatori si spengono. | ||
c = 123, v = 0: Tutte le note disattivate (vedere il testo per la descrizione dei comandi della modalità effettiva.) | ||
c = 124, v = 0: Omni Mode Off | ||
c = 125, v = 0: modalità Omni attivata | ||
c = 126, v = M: Mono Mode On (Poly Off) dove M è il numero di canali (Omni Off) o 0 (Omni On) | ||
c = 127, v = 0: Poly Mode On (Mono Off) (Nota: questi quattro messaggi causano anche l'annullamento di tutte le note) |
Messaggi comuni di sistema
Stato D7..D0 | Byte di dati | Descrizione |
---|---|---|
11110000 | 0iiiiiii [0iiiiiii 0iiiiiii] 0ddddddd --- --- 0ddddddd 11110111 | Sistema esclusivo. Questo tipo di messaggio consente ai produttori di creare i propri messaggi (come dump di massa, parametri di patch e altri dati non specifici) e fornisce un meccanismo per la creazione di ulteriori messaggi di specifica MIDI. Il codice ID del produttore (assegnato da MMA o AMEI) è 1 byte (0iiiiiii) o 3 byte (0iiiiiii 0iiiiiii 0iiiiiii). Due degli ID 1 byte sono riservati per le estensioni chiamate Universal Exclusive Messages, che non sono specifiche del produttore. Se un dispositivo riconosce il proprio codice ID come proprio (o come messaggio Universal supportato) ascolterà il resto del messaggio (0ddddddd). Altrimenti, il messaggio verrà ignorato. (Nota: solo i messaggi in tempo reale possono essere intercalati con un esclusivo sistema.) |
11110001 | 0nnndddd | Frame quarto del time code MIDI. nnn = Tipo di messaggio dddd = Valori |
11110010 | 0lllllll 0mmmmmmm | Song Position Pointer. Questo è un registro interno a 14 bit che contiene il numero di beat MIDI (1 beat = sei clock MIDI) dall'inizio della song. l è il LSB, il MSB. |
11110011 | 0sssssss | Song Select. Song Select specifica quale sequenza o brano deve essere riprodotto. |
11110100 | Non definito. (Riservato) | |
11110101 | Non definito. (Riservato) | |
11110110 | Tune Request. Dopo aver ricevuto una richiesta di sintonia, tutti i sintetizzatori analogici dovrebbero accordare i loro oscillatori. | |
11110111 | Fine di esclusiva. Utilizzato per terminare un dump esclusivo di System (vedere sopra). |
Messaggi in tempo reale del sistema
Stato D7..D0 | Byte di dati | Descrizione |
---|---|---|
11111000 | Orologio a tempo. Inviato 24 volte al trimestre quando è richiesta la sincronizzazione (vedi testo). | |
11111001 | Non definito. (Riservato) | |
11111010 | Inizio. Inizia la riproduzione della sequenza corrente. (Questo messaggio verrà seguito con Timing Clocks). | |
11111011 | Continua. Continua nel punto in cui la sequenza è stata interrotta. | |
11111100 | Stop. Interrompe la sequenza corrente. | |
11111101 | Non definito. (Riservato) | |
11111110 | Sensing attivo. Questo messaggio è destinato ad essere inviato più volte per comunicare al destinatario che una connessione è attiva. L'uso di questo messaggio è facoltativo. Quando viene ricevuto inizialmente, il ricevitore si aspetta di ricevere un altro messaggio di rilevamento attivo ogni 300 ms (max) e, in caso contrario, supporrà che la connessione sia stata interrotta. Al termine, il ricevitore spegne tutte le voci e ritorna al normale funzionamento (rilevamento non attivo). | |
11111111 | Reset. Ripristina tutti i ricevitori nel sistema per lo stato di accensione. Questo dovrebbe essere usato con parsimonia, preferibilmente sotto controllo manuale. In particolare, non dovrebbe essere inviato all'accensione. |