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

Dependencies:   ClockControl PowerControl mbed

Committer:
kayekss
Date:
Sun Nov 09 08:00:33 2014 +0000
Revision:
0:727737138ac5
Child:
1:5f0c89bffec1
12-polyphonic "chiptune" MIDI synthesizer for mbed LPC1768 (Standalone version)

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 0:727737138ac5 98 default:
kayekss 0:727737138ac5 99 sendControlChange(messageBytes);
kayekss 0:727737138ac5 100 break;
kayekss 0:727737138ac5 101 }
kayekss 0:727737138ac5 102 lastStatusByte = c;
kayekss 0:727737138ac5 103 }
kayekss 0:727737138ac5 104 break;
kayekss 0:727737138ac5 105 case 0xc0: // Program change
kayekss 0:727737138ac5 106 if (buffer.getItemCount() >= 2 - runningStatus) {
kayekss 0:727737138ac5 107 messageBytes[0] = runningStatus ? c : *buffer.read();
kayekss 0:727737138ac5 108 for (uint8_t i = 1; i < 2; i++) {
kayekss 0:727737138ac5 109 messageBytes[i] = *buffer.read();
kayekss 0:727737138ac5 110 }
kayekss 0:727737138ac5 111 if (dumpMode == DUMP_EVENTS) {
kayekss 0:727737138ac5 112 console.printf("%-19s %c%02X %02X __h\r\n",
kayekss 0:727737138ac5 113 "Program change", runningStatus ? '*' : ' ',
kayekss 0:727737138ac5 114 messageBytes[0], messageBytes[1]);
kayekss 0:727737138ac5 115 }
kayekss 0:727737138ac5 116 sendProgramChange(messageBytes);
kayekss 0:727737138ac5 117 lastStatusByte = c;
kayekss 0:727737138ac5 118 }
kayekss 0:727737138ac5 119 break;
kayekss 0:727737138ac5 120 case 0xd0: // Channel pressure
kayekss 0:727737138ac5 121 // Not supported
kayekss 0:727737138ac5 122 if (buffer.getItemCount() >= 2 - runningStatus) {
kayekss 0:727737138ac5 123 messageBytes[0] = runningStatus ? c : *buffer.read();
kayekss 0:727737138ac5 124 for (uint8_t i = 1; i < 2; i++) {
kayekss 0:727737138ac5 125 messageBytes[i] = *buffer.read();
kayekss 0:727737138ac5 126 }
kayekss 0:727737138ac5 127 if (dumpMode == DUMP_EVENTS) {
kayekss 0:727737138ac5 128 console.printf("%-19s %c%02X %02X __h\r\n",
kayekss 0:727737138ac5 129 "Ch pressure", runningStatus ? '*' : ' ',
kayekss 0:727737138ac5 130 messageBytes[0], messageBytes[1]);
kayekss 0:727737138ac5 131 }
kayekss 0:727737138ac5 132 lastStatusByte = c;
kayekss 0:727737138ac5 133 }
kayekss 0:727737138ac5 134 break;
kayekss 0:727737138ac5 135 case 0xe0: // Pitch bend
kayekss 0:727737138ac5 136 if (buffer.getItemCount() >= 3 - runningStatus) {
kayekss 0:727737138ac5 137 messageBytes[0] = runningStatus ? c : *buffer.read();
kayekss 0:727737138ac5 138 for (uint8_t i = 1; i < 3; i++) {
kayekss 0:727737138ac5 139 messageBytes[i] = *buffer.read();
kayekss 0:727737138ac5 140 }
kayekss 0:727737138ac5 141 if (dumpMode == DUMP_EVENTS) {
kayekss 0:727737138ac5 142 console.printf("%-19s %c%02X %02X %02Xh\r\n",
kayekss 0:727737138ac5 143 "Pitch bend", runningStatus ? '*' : ' ',
kayekss 0:727737138ac5 144 messageBytes[0], messageBytes[1], messageBytes[2]);
kayekss 0:727737138ac5 145 }
kayekss 0:727737138ac5 146 sendPitchBend(messageBytes);
kayekss 0:727737138ac5 147 lastStatusByte = c;
kayekss 0:727737138ac5 148 }
kayekss 0:727737138ac5 149 break;
kayekss 0:727737138ac5 150 case 0xf0:
kayekss 0:727737138ac5 151 switch (c) {
kayekss 0:727737138ac5 152 case 0xf0: // SysEx message
kayekss 0:727737138ac5 153 if (buffer.find(0xf7) == -1) {
kayekss 0:727737138ac5 154 break;
kayekss 0:727737138ac5 155 }
kayekss 0:727737138ac5 156
kayekss 0:727737138ac5 157 // Extract "F0 ** F7h" message block from buffer
kayekss 0:727737138ac5 158 extractSysExMessage(buffer, sysExBuffer);
kayekss 0:727737138ac5 159 if (strncmp(sysExBuffer, sysExGmSystemOn, 6) == 0) {
kayekss 0:727737138ac5 160 // Matches "GM System On" SysEx message
kayekss 0:727737138ac5 161 if (dumpMode == DUMP_EVENTS) {
kayekss 0:727737138ac5 162 console.printf("SysEx message: GM System On\r\n");
kayekss 0:727737138ac5 163 }
kayekss 0:727737138ac5 164 sendSystemReset();
kayekss 0:727737138ac5 165 } else if (strncmp(sysExBuffer, sysExGsReset, 11) == 0) {
kayekss 0:727737138ac5 166 // Matches "GS Reset" SysEx message
kayekss 0:727737138ac5 167 if (dumpMode == DUMP_EVENTS) {
kayekss 0:727737138ac5 168 console.printf("SysEx message: GS Reset\r\n");
kayekss 0:727737138ac5 169 }
kayekss 0:727737138ac5 170 sendSystemReset();
kayekss 0:727737138ac5 171 } else if (strncmp(sysExBuffer, sysExXgSystemOn, 9) == 0) {
kayekss 0:727737138ac5 172 // Matches "XG System On" SysEx message
kayekss 0:727737138ac5 173 if (dumpMode == DUMP_EVENTS) {
kayekss 0:727737138ac5 174 console.printf("SysEx message: XG System On\r\n");
kayekss 0:727737138ac5 175 }
kayekss 0:727737138ac5 176 sendSystemReset();
kayekss 0:727737138ac5 177 } else {
kayekss 0:727737138ac5 178 if (dumpMode == DUMP_EVENTS) {
kayekss 0:727737138ac5 179 console.printf("Unsupported SysEx message\r\n");
kayekss 0:727737138ac5 180 }
kayekss 0:727737138ac5 181 }
kayekss 0:727737138ac5 182 break;
kayekss 0:727737138ac5 183 case 0xf1: // MTC quarter frame
kayekss 0:727737138ac5 184 case 0xf3: // Song select
kayekss 0:727737138ac5 185 // Not supported
kayekss 0:727737138ac5 186 buffer.read();
kayekss 0:727737138ac5 187 buffer.read();
kayekss 0:727737138ac5 188 break;
kayekss 0:727737138ac5 189 case 0xf2: // Song position
kayekss 0:727737138ac5 190 // Not supported
kayekss 0:727737138ac5 191 buffer.read();
kayekss 0:727737138ac5 192 buffer.read();
kayekss 0:727737138ac5 193 buffer.read();
kayekss 0:727737138ac5 194 break;
kayekss 0:727737138ac5 195 case 0xf4: case 0xf5: case 0xf9: case 0xfd: // Undefined
kayekss 0:727737138ac5 196 case 0xf6: // Tune request
kayekss 0:727737138ac5 197 case 0xfa: // Start
kayekss 0:727737138ac5 198 case 0xfb: // Continue
kayekss 0:727737138ac5 199 case 0xfc: // Stop
kayekss 0:727737138ac5 200 buffer.read();
kayekss 0:727737138ac5 201 break;
kayekss 0:727737138ac5 202 case 0xfe: // Active sensing
kayekss 0:727737138ac5 203 buffer.read();
kayekss 0:727737138ac5 204 break;
kayekss 0:727737138ac5 205 case 0xff: // System reset
kayekss 0:727737138ac5 206 // Discard message (@todo)
kayekss 0:727737138ac5 207 buffer.read();
kayekss 0:727737138ac5 208 sendSystemReset();
kayekss 0:727737138ac5 209 }
kayekss 0:727737138ac5 210 }
kayekss 0:727737138ac5 211 }
kayekss 0:727737138ac5 212
kayekss 0:727737138ac5 213 uint32_t extractSysExMessage(RingBuffer<char>& buffer, char* msg) {
kayekss 0:727737138ac5 214 uint32_t extractedLength;
kayekss 0:727737138ac5 215 char* c = NULL;
kayekss 0:727737138ac5 216
kayekss 0:727737138ac5 217 // Check if the first byte matches SysEx start byte (0xf0)
kayekss 0:727737138ac5 218 c = buffer.read();
kayekss 0:727737138ac5 219 if (!c) return 0;
kayekss 0:727737138ac5 220 if (*c != 0xf0) {
kayekss 0:727737138ac5 221 return 0;
kayekss 0:727737138ac5 222 } else {
kayekss 0:727737138ac5 223 msg[0] = *c;
kayekss 0:727737138ac5 224 }
kayekss 0:727737138ac5 225
kayekss 0:727737138ac5 226 // Read buffer until reaching SysEx end byte (0xf7)
kayekss 0:727737138ac5 227 extractedLength = 1;
kayekss 0:727737138ac5 228 while (extractedLength < SYSEX_BUFFER_LENGTH) {
kayekss 0:727737138ac5 229 c = buffer.read();
kayekss 0:727737138ac5 230 if (!c) break;
kayekss 0:727737138ac5 231
kayekss 0:727737138ac5 232 msg[extractedLength++] = *c;
kayekss 0:727737138ac5 233 if (*c == 0xf7) return extractedLength;
kayekss 0:727737138ac5 234 }
kayekss 0:727737138ac5 235 return 0;
kayekss 0:727737138ac5 236 }