MIDI interpreter using mbed

Dependencies:   MIDI TextLCD mbed

Revision:
0:93868ff6d1b1
Child:
1:af9dd50ffbc2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/parser.cpp	Fri Jun 14 09:25:58 2013 +0000
@@ -0,0 +1,233 @@
+#include "mbed.h"
+
+#include "define.h"
+#include "RingBuffer.h"
+#include "events.h"
+#include "parser.h"
+
+extern BusOut obLeds;
+#ifdef USE_PC_SERIAL
+extern Serial pc;
+#endif
+
+char const sysExGmSystemOn[6] = { 0xf0, 0x7e, 0x7f, 0x09, 0x01, 0xf7 };
+char const sysExGsReset[11]   = { 0xf0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7f, 0x00, 0x41, 0xf7 };
+char const sysExXgSystemOn[9] = { 0xf0, 0x43, 0x10, 0x4c, 0x00, 0x00, 0x7e, 0x00, 0xf7 };
+
+
+void parseMessage(RingBuffer<char>& buffer) {
+    static char lastStatusByte;
+    char c;
+    char dataBytes[2];
+    char sysExBuffer[SYSEX_BUFFER_LENGTH];
+    bool runningStatus = false;
+    
+    c = *buffer.peek();
+    
+    // Running status
+    if (!(c & 0x80)) {
+        runningStatus = true;
+        // Restore previous status byte
+        c = lastStatusByte;
+    }
+    
+    switch (c & 0xf0) {
+    case 0x80:  /* Note off */
+        if (buffer.items() >= 3 - runningStatus) {
+            if (!runningStatus) buffer.read();
+            for (uint8_t i = 0; i < 2; i++) {
+                dataBytes[i] = *buffer.read();
+            }
+            #ifdef USE_PC_SERIAL
+            pc.printf("%-19s %c%02X %02X %02Xh\r\n",
+                      "Note off", runningStatus ? '*' : ' ',
+                      c, dataBytes[0], dataBytes[1]);
+            #endif
+            
+            dispatchNoteOff(c, dataBytes[0]);
+            lastStatusByte = c;
+        }
+        break;
+    case 0x90:  /* Note on */
+        if (buffer.items() >= 3 - runningStatus) {
+            if (!runningStatus) buffer.read();
+            for (uint8_t i = 0; i < 2; i++) {
+                dataBytes[i] = *buffer.read();
+            }
+            #ifdef USE_PC_SERIAL
+            pc.printf("%-19s %c%02X %02X %02Xh\r\n",
+                      "Note on", runningStatus ? '*' : ' ',
+                      c, dataBytes[0], dataBytes[1]);
+            #endif
+            
+            if (dataBytes[1] == 0x00) {
+                dispatchNoteOff(c, dataBytes[0]);
+            } else {
+                dispatchNoteOn(c, dataBytes[0], dataBytes[1]);
+            }
+            lastStatusByte = c;
+        }
+        break;
+    case 0xa0:  /* Polyphonic pressure */
+        // Not supported
+        if (buffer.items() >= 3 - runningStatus) {
+            if (!runningStatus) buffer.read();
+            for (uint8_t i = 0; i < 2; i++) {
+                dataBytes[i] = *buffer.read();
+            }
+            #ifdef USE_PC_SERIAL
+            pc.printf("%-19s %c%02X %02X %02Xh\r\n",
+                      "Poly pressure", runningStatus ? '*' : ' ',
+                      c, dataBytes[0], dataBytes[1]);
+            #endif
+            lastStatusByte = c;
+        }
+        break;
+    case 0xb0:  /* Control change */
+        if (buffer.items() >= 3 - runningStatus) {
+            if (!runningStatus) buffer.read();
+            for (uint8_t i = 0; i < 2; i++) {
+                dataBytes[i] = *buffer.read();
+            }
+            #ifdef USE_PC_SERIAL
+            pc.printf("%-19s %c%02X %02X %02Xh\r\n",
+                      "Control change", runningStatus ? '*' : ' ',
+                      c, dataBytes[0], dataBytes[1]);
+            #endif
+            setControlChange(c, dataBytes[0], dataBytes[1]);
+            lastStatusByte = c;
+        }
+        break;
+    case 0xc0:  /* Program change */
+        if (buffer.items() >= 3 - runningStatus) {
+            if (!runningStatus) buffer.read();
+            for (uint8_t i = 0; i < 2; i++) {
+                dataBytes[i] = *buffer.read();
+            }
+            #ifdef USE_PC_SERIAL
+            pc.printf("%-19s %c%02X %02X __h\r\n",
+                      "Program change", runningStatus ? '*' : ' ',
+                      c, dataBytes[0]);
+            #endif
+            setProgramChange(c, dataBytes[0]);
+            lastStatusByte = c;
+        }
+        break;
+    case 0xd0:  /* Channel pressure */
+        // Not supported
+        if (buffer.items() >= 3 - runningStatus) {
+            if (!runningStatus) buffer.read();
+            for (uint8_t i = 0; i < 2; i++) {
+                dataBytes[i] = *buffer.read();
+            }
+            #ifdef USE_PC_SERIAL
+            pc.printf("%-19s %c%02X %02X __h\r\n",
+                      "Ch pressure", runningStatus ? '*' : ' ',
+                      c, dataBytes[0]);
+            #endif
+            lastStatusByte = c;
+        }
+        break;
+    case 0xe0:  /* Pitch bend */
+        if (buffer.items() >= 3 - runningStatus) {
+            if (!runningStatus) buffer.read();
+            for (uint8_t i = 0; i < 2; i++) {
+                dataBytes[i] = *buffer.read();
+            }
+            #ifdef USE_PC_SERIAL
+            pc.printf("%-19s %c%02X %02X %02Xh\r\n",
+                      "Pitch bend", runningStatus ? '*' : ' ',
+                      c, dataBytes[0], dataBytes[1]);
+            #endif
+            setPitchBend(c, dataBytes[0] << 7 | dataBytes[1]);
+            lastStatusByte = c;
+        }
+        break;
+    case 0xf0:
+        switch (c) {
+        case 0xf0:  /* SysEx message */
+            if (buffer.find(0xf7) == -1) {
+                break;
+            }
+            
+            extractSysExMessage(buffer, sysExBuffer);
+            if (strncmp(sysExBuffer, sysExGmSystemOn, 6) == 0) {
+                // Matches "GM System On" SysEx message
+                #ifdef USE_PC_SERIAL
+                pc.printf("SysEx message: GM System On\r\n");
+                #endif
+                obLeds = 0x1;
+            } else if (strncmp(sysExBuffer, sysExGsReset, 11) == 0) {
+                // Matches "GS Reset" SysEx message
+                #ifdef USE_PC_SERIAL
+                pc.printf("SysEx message: GS Reset\r\n");
+                #endif
+                obLeds = 0x2;
+            } else if (strncmp(sysExBuffer, sysExXgSystemOn, 9) == 0) {
+                // Matches "XG System On" SysEx message
+                #ifdef USE_PC_SERIAL
+                pc.printf("SysEx message: XG System On\r\n");
+                #endif
+                obLeds = 0x2;
+            } else {
+                #ifdef USE_PC_SERIAL
+                pc.printf("Unsupported SysEx message\r\n");
+                #endif
+            }
+            break;
+        case 0xf1:  /* MTC quarter frame */
+        case 0xf3:  /* Song select */
+            // Not supported
+            buffer.read();
+            buffer.read();
+            obLeds = 0x0;
+            break;
+        case 0xf2:  /* Song position */
+            // Not supported
+            buffer.read();
+            buffer.read();
+            buffer.read();
+            obLeds = 0x0;
+            break;
+        case 0xf4: case 0xf5: case 0xf9: case 0xfd:  /* Undefined */
+        case 0xf6:  /* Tune request */
+        case 0xfa:  /* Start */
+        case 0xfb:  /* Continue */
+        case 0xfc:  /* Stop */
+            buffer.read();
+            obLeds = 0x0;
+            break;
+        case 0xfe:  /* Active sensing */
+            break;
+        case 0xff:  /* System reset */
+            // Discard message (@todo)
+            buffer.read();
+            obLeds = 0x0;
+        }
+    }
+}
+
+uint32_t extractSysExMessage(RingBuffer<char>& buffer, char* msg) {
+    uint32_t extractedLength;
+    char* c = NULL;
+    
+    // Check if the first byte matches SysEx start byte (0xf0)
+    c = buffer.read();
+    if (!c) return 0;
+    if (*c != 0xf0) {
+        return 0;
+    } else {
+        msg[0] = *c;
+    }
+    
+    // Read buffer until reaching SysEx end byte (0xf7)
+    extractedLength = 1;
+    while (extractedLength < SYSEX_BUFFER_LENGTH) {
+        c = buffer.read();
+        if (!c) break;
+        
+        msg[extractedLength++] = *c;
+        if (*c == 0xf7) return extractedLength;
+    }
+    return 0;
+}