MIDI interpreter using mbed

Dependencies:   MIDI TextLCD mbed

parser.cpp

Committer:
kayekss
Date:
2013-06-14
Revision:
1:af9dd50ffbc2
Parent:
0:93868ff6d1b1

File content as of revision 1:af9dd50ffbc2:

#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[1] << 7 | dataBytes[0]);
            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;
}