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

Dependencies:   ClockControl PowerControl mbed

Committer:
kayekss
Date:
Tue Dec 23 21:50:53 2014 +0000
Revision:
6:cda45a5e723e
Parent:
5:7bc917d03bd6
Supports "Panic on offline" feature when using MIDI-port input

Who changed what in which revision?

UserRevisionLine numberNew contents of line
kayekss 0:727737138ac5 1 #include <stdarg.h>
kayekss 0:727737138ac5 2 #include "mbed.h"
kayekss 0:727737138ac5 3 #include "defs.h"
kayekss 0:727737138ac5 4 #include "RingBuffer.h"
kayekss 0:727737138ac5 5 #include "debug.h"
kayekss 0:727737138ac5 6 #include "events.h"
kayekss 0:727737138ac5 7 #include "parser.h"
kayekss 0:727737138ac5 8
kayekss 0:727737138ac5 9 extern Serial console;
kayekss 0:727737138ac5 10 extern bool serSource;
kayekss 0:727737138ac5 11 extern dumpmode_t dumpMode;
kayekss 0:727737138ac5 12 char const sysExGmSystemOn[6] = { 0xf0, 0x7e, 0x7f, 0x09, 0x01, 0xf7 };
kayekss 0:727737138ac5 13 char const sysExGsReset[11] = { 0xf0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7f, 0x00, 0x41, 0xf7 };
kayekss 0:727737138ac5 14 char const sysExXgSystemOn[9] = { 0xf0, 0x43, 0x10, 0x4c, 0x00, 0x00, 0x7e, 0x00, 0xf7 };
kayekss 0:727737138ac5 15
kayekss 0:727737138ac5 16 void parseMessage(RingBuffer<char>& buffer) {
kayekss 0:727737138ac5 17 static char lastStatusByte;
kayekss 0:727737138ac5 18 char c;
kayekss 0:727737138ac5 19 char messageBytes[3];
kayekss 0:727737138ac5 20 char sysExBuffer[SYSEX_BUFFER_LENGTH];
kayekss 0:727737138ac5 21 bool runningStatus = false;
kayekss 0:727737138ac5 22
kayekss 0:727737138ac5 23 c = *buffer.peek();
kayekss 0:727737138ac5 24
kayekss 0:727737138ac5 25 // Running status
kayekss 0:727737138ac5 26 if (!(c & 0x80)) {
kayekss 0:727737138ac5 27 runningStatus = true;
kayekss 0:727737138ac5 28 // Restore previous status byte
kayekss 0:727737138ac5 29 c = lastStatusByte;
kayekss 0:727737138ac5 30 }
kayekss 0:727737138ac5 31
kayekss 0:727737138ac5 32 switch (c & 0xf0) {
kayekss 0:727737138ac5 33 case 0x80: // Note off
kayekss 0:727737138ac5 34 if (buffer.getItemCount() >= 3 - runningStatus) {
kayekss 0:727737138ac5 35 messageBytes[0] = runningStatus ? c : *buffer.read();
kayekss 0:727737138ac5 36 for (uint8_t i = 1; i < 3; i++) {
kayekss 0:727737138ac5 37 messageBytes[i] = *buffer.read();
kayekss 0:727737138ac5 38 }
kayekss 0:727737138ac5 39 if (dumpMode == DUMP_EVENTS) {
kayekss 0:727737138ac5 40 console.printf("%-19s %c%02X %02X %02Xh\r\n",
kayekss 0:727737138ac5 41 "Note off", runningStatus ? '*' : ' ',
kayekss 0:727737138ac5 42 messageBytes[0], messageBytes[1], messageBytes[2]);
kayekss 0:727737138ac5 43 }
kayekss 0:727737138ac5 44 dispatchNoteOff(messageBytes);
kayekss 0:727737138ac5 45 lastStatusByte = c;
kayekss 0:727737138ac5 46 }
kayekss 0:727737138ac5 47 break;
kayekss 0:727737138ac5 48 case 0x90: // Note on
kayekss 0:727737138ac5 49 if (buffer.getItemCount() >= 3 - runningStatus) {
kayekss 0:727737138ac5 50 messageBytes[0] = runningStatus ? c : *buffer.read();
kayekss 0:727737138ac5 51 for (uint8_t i = 1; i < 3; i++) {
kayekss 0:727737138ac5 52 messageBytes[i] = *buffer.read();
kayekss 0:727737138ac5 53 }
kayekss 0:727737138ac5 54 if (dumpMode == DUMP_EVENTS) {
kayekss 0:727737138ac5 55 console.printf("%-19s %c%02X %02X %02Xh\r\n",
kayekss 0:727737138ac5 56 "Note on", runningStatus ? '*' : ' ',
kayekss 0:727737138ac5 57 messageBytes[0], messageBytes[1], messageBytes[2]);
kayekss 0:727737138ac5 58 }
kayekss 0:727737138ac5 59 if (messageBytes[2] == 0x00) {
kayekss 0:727737138ac5 60 dispatchNoteOff(messageBytes);
kayekss 0:727737138ac5 61 } else {
kayekss 0:727737138ac5 62 dispatchNoteOn(messageBytes);
kayekss 0:727737138ac5 63 }
kayekss 0:727737138ac5 64 lastStatusByte = c;
kayekss 0:727737138ac5 65 }
kayekss 0:727737138ac5 66 break;
kayekss 0:727737138ac5 67 case 0xa0: // Polyphonic pressure
kayekss 0:727737138ac5 68 // Not supported
kayekss 0:727737138ac5 69 if (buffer.getItemCount() >= 3 - runningStatus) {
kayekss 0:727737138ac5 70 messageBytes[0] = runningStatus ? c : *buffer.read();
kayekss 0:727737138ac5 71 for (uint8_t i = 1; i < 3; i++) {
kayekss 0:727737138ac5 72 messageBytes[i] = *buffer.read();
kayekss 0:727737138ac5 73 }
kayekss 0:727737138ac5 74 if (dumpMode == DUMP_EVENTS) {
kayekss 0:727737138ac5 75 console.printf("%-19s %c%02X %02X %02Xh\r\n",
kayekss 0:727737138ac5 76 "Poly pressure", runningStatus ? '*' : ' ',
kayekss 0:727737138ac5 77 messageBytes[0], messageBytes[1], messageBytes[2]);
kayekss 0:727737138ac5 78 }
kayekss 0:727737138ac5 79 lastStatusByte = c;
kayekss 0:727737138ac5 80 }
kayekss 0:727737138ac5 81 break;
kayekss 0:727737138ac5 82 case 0xb0: // Control change
kayekss 0:727737138ac5 83 if (buffer.getItemCount() >= 3 - runningStatus) {
kayekss 0:727737138ac5 84 messageBytes[0] = runningStatus ? c : *buffer.read();
kayekss 0:727737138ac5 85 for (uint8_t i = 1; i < 3; i++) {
kayekss 0:727737138ac5 86 messageBytes[i] = *buffer.read();
kayekss 0:727737138ac5 87 }
kayekss 0:727737138ac5 88 if (dumpMode == DUMP_EVENTS) {
kayekss 0:727737138ac5 89 console.printf("%-19s %c%02X %02X %02Xh\r\n",
kayekss 0:727737138ac5 90 "Control change", runningStatus ? '*' : ' ',
kayekss 0:727737138ac5 91 messageBytes[0], messageBytes[1], messageBytes[2]);
kayekss 0:727737138ac5 92 }
kayekss 0:727737138ac5 93 switch (messageBytes[1]) {
kayekss 0:727737138ac5 94 case 0x78: // All sound off
kayekss 0:727737138ac5 95 case 0x7b: // All note off
kayekss 0:727737138ac5 96 allNoteOff(messageBytes);
kayekss 0:727737138ac5 97 break;
kayekss 5:7bc917d03bd6 98 case 0x79: // Reset all controllers
kayekss 5:7bc917d03bd6 99 resetAllControllers(messageBytes);
kayekss 5:7bc917d03bd6 100 break;
kayekss 0:727737138ac5 101 default:
kayekss 0:727737138ac5 102 sendControlChange(messageBytes);
kayekss 0:727737138ac5 103 break;
kayekss 0:727737138ac5 104 }
kayekss 0:727737138ac5 105 lastStatusByte = c;
kayekss 0:727737138ac5 106 }
kayekss 0:727737138ac5 107 break;
kayekss 0:727737138ac5 108 case 0xc0: // Program change
kayekss 0:727737138ac5 109 if (buffer.getItemCount() >= 2 - runningStatus) {
kayekss 0:727737138ac5 110 messageBytes[0] = runningStatus ? c : *buffer.read();
kayekss 0:727737138ac5 111 for (uint8_t i = 1; i < 2; i++) {
kayekss 0:727737138ac5 112 messageBytes[i] = *buffer.read();
kayekss 0:727737138ac5 113 }
kayekss 0:727737138ac5 114 if (dumpMode == DUMP_EVENTS) {
kayekss 0:727737138ac5 115 console.printf("%-19s %c%02X %02X __h\r\n",
kayekss 0:727737138ac5 116 "Program change", runningStatus ? '*' : ' ',
kayekss 0:727737138ac5 117 messageBytes[0], messageBytes[1]);
kayekss 0:727737138ac5 118 }
kayekss 0:727737138ac5 119 sendProgramChange(messageBytes);
kayekss 0:727737138ac5 120 lastStatusByte = c;
kayekss 0:727737138ac5 121 }
kayekss 0:727737138ac5 122 break;
kayekss 0:727737138ac5 123 case 0xd0: // Channel pressure
kayekss 0:727737138ac5 124 // Not supported
kayekss 0:727737138ac5 125 if (buffer.getItemCount() >= 2 - runningStatus) {
kayekss 0:727737138ac5 126 messageBytes[0] = runningStatus ? c : *buffer.read();
kayekss 0:727737138ac5 127 for (uint8_t i = 1; i < 2; i++) {
kayekss 0:727737138ac5 128 messageBytes[i] = *buffer.read();
kayekss 0:727737138ac5 129 }
kayekss 0:727737138ac5 130 if (dumpMode == DUMP_EVENTS) {
kayekss 0:727737138ac5 131 console.printf("%-19s %c%02X %02X __h\r\n",
kayekss 0:727737138ac5 132 "Ch pressure", runningStatus ? '*' : ' ',
kayekss 0:727737138ac5 133 messageBytes[0], messageBytes[1]);
kayekss 0:727737138ac5 134 }
kayekss 0:727737138ac5 135 lastStatusByte = c;
kayekss 0:727737138ac5 136 }
kayekss 0:727737138ac5 137 break;
kayekss 0:727737138ac5 138 case 0xe0: // Pitch bend
kayekss 0:727737138ac5 139 if (buffer.getItemCount() >= 3 - runningStatus) {
kayekss 0:727737138ac5 140 messageBytes[0] = runningStatus ? c : *buffer.read();
kayekss 0:727737138ac5 141 for (uint8_t i = 1; i < 3; i++) {
kayekss 0:727737138ac5 142 messageBytes[i] = *buffer.read();
kayekss 0:727737138ac5 143 }
kayekss 0:727737138ac5 144 if (dumpMode == DUMP_EVENTS) {
kayekss 0:727737138ac5 145 console.printf("%-19s %c%02X %02X %02Xh\r\n",
kayekss 0:727737138ac5 146 "Pitch bend", runningStatus ? '*' : ' ',
kayekss 0:727737138ac5 147 messageBytes[0], messageBytes[1], messageBytes[2]);
kayekss 0:727737138ac5 148 }
kayekss 0:727737138ac5 149 sendPitchBend(messageBytes);
kayekss 0:727737138ac5 150 lastStatusByte = c;
kayekss 0:727737138ac5 151 }
kayekss 0:727737138ac5 152 break;
kayekss 0:727737138ac5 153 case 0xf0:
kayekss 0:727737138ac5 154 switch (c) {
kayekss 0:727737138ac5 155 case 0xf0: // SysEx message
kayekss 0:727737138ac5 156 if (buffer.find(0xf7) == -1) {
kayekss 0:727737138ac5 157 break;
kayekss 0:727737138ac5 158 }
kayekss 0:727737138ac5 159
kayekss 0:727737138ac5 160 // Extract "F0 ** F7h" message block from buffer
kayekss 0:727737138ac5 161 extractSysExMessage(buffer, sysExBuffer);
kayekss 0:727737138ac5 162 if (strncmp(sysExBuffer, sysExGmSystemOn, 6) == 0) {
kayekss 0:727737138ac5 163 // Matches "GM System On" SysEx message
kayekss 0:727737138ac5 164 if (dumpMode == DUMP_EVENTS) {
kayekss 0:727737138ac5 165 console.printf("SysEx message: GM System On\r\n");
kayekss 0:727737138ac5 166 }
kayekss 0:727737138ac5 167 sendSystemReset();
kayekss 0:727737138ac5 168 } else if (strncmp(sysExBuffer, sysExGsReset, 11) == 0) {
kayekss 0:727737138ac5 169 // Matches "GS Reset" SysEx message
kayekss 0:727737138ac5 170 if (dumpMode == DUMP_EVENTS) {
kayekss 0:727737138ac5 171 console.printf("SysEx message: GS Reset\r\n");
kayekss 0:727737138ac5 172 }
kayekss 0:727737138ac5 173 sendSystemReset();
kayekss 0:727737138ac5 174 } else if (strncmp(sysExBuffer, sysExXgSystemOn, 9) == 0) {
kayekss 0:727737138ac5 175 // Matches "XG System On" SysEx message
kayekss 0:727737138ac5 176 if (dumpMode == DUMP_EVENTS) {
kayekss 0:727737138ac5 177 console.printf("SysEx message: XG System On\r\n");
kayekss 0:727737138ac5 178 }
kayekss 0:727737138ac5 179 sendSystemReset();
kayekss 0:727737138ac5 180 } else {
kayekss 0:727737138ac5 181 if (dumpMode == DUMP_EVENTS) {
kayekss 0:727737138ac5 182 console.printf("Unsupported SysEx message\r\n");
kayekss 0:727737138ac5 183 }
kayekss 0:727737138ac5 184 }
kayekss 0:727737138ac5 185 break;
kayekss 0:727737138ac5 186 case 0xf1: // MTC quarter frame
kayekss 0:727737138ac5 187 case 0xf3: // Song select
kayekss 0:727737138ac5 188 // Not supported
kayekss 0:727737138ac5 189 buffer.read();
kayekss 0:727737138ac5 190 buffer.read();
kayekss 0:727737138ac5 191 break;
kayekss 0:727737138ac5 192 case 0xf2: // Song position
kayekss 0:727737138ac5 193 // Not supported
kayekss 0:727737138ac5 194 buffer.read();
kayekss 0:727737138ac5 195 buffer.read();
kayekss 0:727737138ac5 196 buffer.read();
kayekss 0:727737138ac5 197 break;
kayekss 0:727737138ac5 198 case 0xf4: case 0xf5: case 0xf9: case 0xfd: // Undefined
kayekss 0:727737138ac5 199 case 0xf6: // Tune request
kayekss 0:727737138ac5 200 case 0xfa: // Start
kayekss 0:727737138ac5 201 case 0xfb: // Continue
kayekss 0:727737138ac5 202 case 0xfc: // Stop
kayekss 0:727737138ac5 203 buffer.read();
kayekss 0:727737138ac5 204 break;
kayekss 0:727737138ac5 205 case 0xfe: // Active sensing
kayekss 0:727737138ac5 206 buffer.read();
kayekss 0:727737138ac5 207 break;
kayekss 0:727737138ac5 208 case 0xff: // System reset
kayekss 1:5f0c89bffec1 209 // Discard message
kayekss 0:727737138ac5 210 buffer.read();
kayekss 0:727737138ac5 211 sendSystemReset();
kayekss 0:727737138ac5 212 }
kayekss 0:727737138ac5 213 }
kayekss 0:727737138ac5 214 }
kayekss 0:727737138ac5 215
kayekss 0:727737138ac5 216 uint32_t extractSysExMessage(RingBuffer<char>& buffer, char* msg) {
kayekss 0:727737138ac5 217 uint32_t extractedLength;
kayekss 0:727737138ac5 218 char* c = NULL;
kayekss 0:727737138ac5 219
kayekss 0:727737138ac5 220 // Check if the first byte matches SysEx start byte (0xf0)
kayekss 0:727737138ac5 221 c = buffer.read();
kayekss 0:727737138ac5 222 if (!c) return 0;
kayekss 0:727737138ac5 223 if (*c != 0xf0) {
kayekss 0:727737138ac5 224 return 0;
kayekss 0:727737138ac5 225 } else {
kayekss 0:727737138ac5 226 msg[0] = *c;
kayekss 0:727737138ac5 227 }
kayekss 0:727737138ac5 228
kayekss 0:727737138ac5 229 // Read buffer until reaching SysEx end byte (0xf7)
kayekss 0:727737138ac5 230 extractedLength = 1;
kayekss 0:727737138ac5 231 while (extractedLength < SYSEX_BUFFER_LENGTH) {
kayekss 0:727737138ac5 232 c = buffer.read();
kayekss 0:727737138ac5 233 if (!c) break;
kayekss 0:727737138ac5 234
kayekss 0:727737138ac5 235 msg[extractedLength++] = *c;
kayekss 0:727737138ac5 236 if (*c == 0xf7) return extractedLength;
kayekss 0:727737138ac5 237 }
kayekss 0:727737138ac5 238 return 0;
kayekss 0:727737138ac5 239 }