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.


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow