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

Dependencies:   ClockControl PowerControl mbed

parser.cpp

Committer:
kayekss
Date:
2014-12-23
Revision:
6:cda45a5e723e
Parent:
5:7bc917d03bd6

File content as of revision 6:cda45a5e723e:

#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;
            case 0x79:  // Reset all controllers
                resetAllControllers(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
            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;
}