12-polyphonic "chiptune" MIDI synthesizer for LPC1768 (Standalone version)

Dependencies:   ClockControl PowerControl mbed

Revision:
0:727737138ac5
Child:
1:5f0c89bffec1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/parser.cpp	Sun Nov 09 08:00:33 2014 +0000
@@ -0,0 +1,236 @@
+#include <stdarg.h>
+#include "mbed.h"
+#include "defs.h"
+#include "RingBuffer.h"
+#include "debug.h"
+#include "events.h"
+#include "parser.h"
+
+extern Serial     console;
+extern bool       serSource;
+extern dumpmode_t dumpMode;
+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 messageBytes[3];
+    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.getItemCount() >= 3 - runningStatus) {
+            messageBytes[0] = runningStatus ? c : *buffer.read();
+            for (uint8_t i = 1; i < 3; i++) {
+                messageBytes[i] = *buffer.read();
+            }
+            if (dumpMode == DUMP_EVENTS) {
+                console.printf("%-19s %c%02X %02X %02Xh\r\n",
+                               "Note off", runningStatus ? '*' : ' ',
+                               messageBytes[0], messageBytes[1], messageBytes[2]);
+            }
+            dispatchNoteOff(messageBytes);
+            lastStatusByte = c;
+        }
+        break;
+    case 0x90:  // Note on
+        if (buffer.getItemCount() >= 3 - runningStatus) {
+            messageBytes[0] = runningStatus ? c : *buffer.read();
+            for (uint8_t i = 1; i < 3; i++) {
+                messageBytes[i] = *buffer.read();
+            }
+            if (dumpMode == DUMP_EVENTS) {
+                console.printf("%-19s %c%02X %02X %02Xh\r\n",
+                               "Note on", runningStatus ? '*' : ' ',
+                               messageBytes[0], messageBytes[1], messageBytes[2]);
+            }
+            if (messageBytes[2] == 0x00) {
+                dispatchNoteOff(messageBytes);
+            } else {
+                dispatchNoteOn(messageBytes);
+            }
+            lastStatusByte = c;
+        }
+        break;
+    case 0xa0:  // Polyphonic pressure
+        // Not supported
+        if (buffer.getItemCount() >= 3 - runningStatus) {
+            messageBytes[0] = runningStatus ? c : *buffer.read();
+            for (uint8_t i = 1; i < 3; i++) {
+                messageBytes[i] = *buffer.read();
+            }
+            if (dumpMode == DUMP_EVENTS) {
+                console.printf("%-19s %c%02X %02X %02Xh\r\n",
+                               "Poly pressure", runningStatus ? '*' : ' ',
+                               messageBytes[0], messageBytes[1], messageBytes[2]);
+            }
+            lastStatusByte = c;
+        }
+        break;
+    case 0xb0:  // Control change
+        if (buffer.getItemCount() >= 3 - runningStatus) {
+            messageBytes[0] = runningStatus ? c : *buffer.read();
+            for (uint8_t i = 1; i < 3; i++) {
+                messageBytes[i] = *buffer.read();
+            }
+            if (dumpMode == DUMP_EVENTS) {
+                console.printf("%-19s %c%02X %02X %02Xh\r\n",
+                               "Control change", runningStatus ? '*' : ' ',
+                               messageBytes[0], messageBytes[1], messageBytes[2]);
+            }
+            switch (messageBytes[1]) {
+            case 0x78:  // All sound off
+            case 0x7b:  // All note off
+                allNoteOff(messageBytes);
+                break;
+            default:
+                sendControlChange(messageBytes);
+                break;
+            }            
+            lastStatusByte = c;
+        }
+        break;
+    case 0xc0:  // Program change
+        if (buffer.getItemCount() >= 2 - runningStatus) {
+            messageBytes[0] = runningStatus ? c : *buffer.read();
+            for (uint8_t i = 1; i < 2; i++) {
+                messageBytes[i] = *buffer.read();
+            }
+            if (dumpMode == DUMP_EVENTS) {
+                console.printf("%-19s %c%02X %02X __h\r\n",
+                               "Program change", runningStatus ? '*' : ' ',
+                               messageBytes[0], messageBytes[1]);
+            }
+            sendProgramChange(messageBytes);
+            lastStatusByte = c;
+        }
+        break;
+    case 0xd0:  // Channel pressure
+        // Not supported
+        if (buffer.getItemCount() >= 2 - runningStatus) {
+            messageBytes[0] = runningStatus ? c : *buffer.read();
+            for (uint8_t i = 1; i < 2; i++) {
+                messageBytes[i] = *buffer.read();
+            }
+            if (dumpMode == DUMP_EVENTS) {
+                console.printf("%-19s %c%02X %02X __h\r\n",
+                               "Ch pressure", runningStatus ? '*' : ' ',
+                               messageBytes[0], messageBytes[1]);
+            }
+            lastStatusByte = c;
+        }
+        break;
+    case 0xe0:  // Pitch bend
+        if (buffer.getItemCount() >= 3 - runningStatus) {
+            messageBytes[0] = runningStatus ? c : *buffer.read();
+            for (uint8_t i = 1; i < 3; i++) {
+                messageBytes[i] = *buffer.read();
+            }
+            if (dumpMode == DUMP_EVENTS) {
+                console.printf("%-19s %c%02X %02X %02Xh\r\n",
+                          "Pitch bend", runningStatus ? '*' : ' ',
+                          messageBytes[0], messageBytes[1], messageBytes[2]);
+            }
+            sendPitchBend(messageBytes);
+            lastStatusByte = c;
+        }
+        break;
+    case 0xf0:
+        switch (c) {
+        case 0xf0:  // SysEx message
+            if (buffer.find(0xf7) == -1) {
+                break;
+            }
+            
+            // Extract "F0 ** F7h" message block from buffer
+            extractSysExMessage(buffer, sysExBuffer);
+            if (strncmp(sysExBuffer, sysExGmSystemOn, 6) == 0) {
+                // Matches "GM System On" SysEx message
+                if (dumpMode == DUMP_EVENTS) {
+                    console.printf("SysEx message: GM System On\r\n");
+                }
+                sendSystemReset();
+            } else if (strncmp(sysExBuffer, sysExGsReset, 11) == 0) {
+                // Matches "GS Reset" SysEx message
+                if (dumpMode == DUMP_EVENTS) {
+                    console.printf("SysEx message: GS Reset\r\n");
+                }
+                sendSystemReset();
+            } else if (strncmp(sysExBuffer, sysExXgSystemOn, 9) == 0) {
+                // Matches "XG System On" SysEx message
+                if (dumpMode == DUMP_EVENTS) {
+                    console.printf("SysEx message: XG System On\r\n");
+                }
+                sendSystemReset();
+            } else {
+                if (dumpMode == DUMP_EVENTS) {
+                    console.printf("Unsupported SysEx message\r\n");
+                }
+            }
+            break;
+        case 0xf1:  // MTC quarter frame
+        case 0xf3:  // Song select
+            // Not supported
+            buffer.read();
+            buffer.read();
+            break;
+        case 0xf2:  // Song position
+            // Not supported
+            buffer.read();
+            buffer.read();
+            buffer.read();
+            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();
+            break;
+        case 0xfe:  // Active sensing
+            buffer.read();
+            break;
+        case 0xff:  // System reset
+            // Discard message (@todo)
+            buffer.read();
+            sendSystemReset();
+        }
+    }
+}
+
+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;
+}