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

Dependencies:   ClockControl PowerControl mbed

Committer:
kayekss
Date:
Sun Nov 09 08:15:11 2014 +0000
Revision:
1:5f0c89bffec1
Parent:
0:727737138ac5
Child:
4:b2423ad4b248
Restore temporary settings

Who changed what in which revision?

UserRevisionLine numberNew contents of line
kayekss 0:727737138ac5 1 #include <climits>
kayekss 0:727737138ac5 2 #include "mbed.h"
kayekss 0:727737138ac5 3 #include "defs.h"
kayekss 0:727737138ac5 4 #include "Channels.h"
kayekss 0:727737138ac5 5 #include "GeminiCore.h"
kayekss 0:727737138ac5 6 #include "Wavetable.h"
kayekss 0:727737138ac5 7 #include "debug.h"
kayekss 0:727737138ac5 8 #include "events.h"
kayekss 0:727737138ac5 9 #include "frequency.h"
kayekss 0:727737138ac5 10 #include "note.h"
kayekss 0:727737138ac5 11
kayekss 0:727737138ac5 12 extern Serial console;
kayekss 0:727737138ac5 13 extern volatile uint32_t __countMs;
kayekss 0:727737138ac5 14 extern GeminiCore gemCore;
kayekss 0:727737138ac5 15 extern Channels ch;
kayekss 0:727737138ac5 16 extern note_t noteSent[NUM_INSTRUMENT];
kayekss 0:727737138ac5 17 extern float const frequencyTable[128];
kayekss 0:727737138ac5 18 extern dumpmode_t dumpMode;
kayekss 0:727737138ac5 19
kayekss 0:727737138ac5 20 bool isImplementedDrumNoteNumber(uint8_t noteNumber) {
kayekss 0:727737138ac5 21 switch (noteNumber) {
kayekss 0:727737138ac5 22 case 0x23: // Acoustic Bass Drum
kayekss 0:727737138ac5 23 case 0x24: // Bass Drum 1
kayekss 0:727737138ac5 24 case 0x25: // Side Stick
kayekss 0:727737138ac5 25 case 0x26: // Acoustic Snare
kayekss 0:727737138ac5 26 case 0x28: // Electric Snare
kayekss 0:727737138ac5 27 case 0x29: // Low Floor Tom
kayekss 0:727737138ac5 28 case 0x2a: // Closed High-hat
kayekss 0:727737138ac5 29 case 0x2b: // High Floor Tom
kayekss 0:727737138ac5 30 case 0x2c: // Pedal High-hat
kayekss 0:727737138ac5 31 case 0x2d: // Low Tom
kayekss 0:727737138ac5 32 case 0x2e: // Open High-hat
kayekss 0:727737138ac5 33 case 0x2f: // Low-Mid Tom
kayekss 0:727737138ac5 34 case 0x30: // High-Mid Tom
kayekss 0:727737138ac5 35 case 0x31: // Crash Cymbal 1
kayekss 0:727737138ac5 36 case 0x32: // High Tom
kayekss 0:727737138ac5 37 case 0x39: // Crash Cymbal 2
kayekss 0:727737138ac5 38 return true;
kayekss 0:727737138ac5 39 default:
kayekss 0:727737138ac5 40 return false;
kayekss 0:727737138ac5 41 }
kayekss 0:727737138ac5 42 }
kayekss 0:727737138ac5 43
kayekss 0:727737138ac5 44 void dispatchNoteOff(char messageBytes[3]) {
kayekss 0:727737138ac5 45 uint16_t targetInst;
kayekss 0:727737138ac5 46 uint16_t oldest = 0xffff;
kayekss 0:727737138ac5 47 uint32_t oldestMs = UINT_MAX;
kayekss 0:727737138ac5 48 uint8_t channel = messageBytes[0] & 0x0f;
kayekss 0:727737138ac5 49 uint8_t noteNumber = messageBytes[1] & 0x7f;
kayekss 0:727737138ac5 50
kayekss 0:727737138ac5 51 // Skip if rhythm channel
kayekss 0:727737138ac5 52 if (channel == 0x09) {
kayekss 0:727737138ac5 53 return;
kayekss 0:727737138ac5 54 }
kayekss 0:727737138ac5 55
kayekss 0:727737138ac5 56 // Find an (oldest) Instrument sounding the specified note number
kayekss 0:727737138ac5 57 for (uint16_t i = 0; i < NUM_INSTRUMENT; i++) {
kayekss 0:727737138ac5 58 if (noteSent[i].channel == channel + 1
kayekss 0:727737138ac5 59 && noteSent[i].noteNumber == noteNumber) {
kayekss 0:727737138ac5 60 if (noteSent[i].noteOnMs < oldestMs) {
kayekss 0:727737138ac5 61 oldest = i;
kayekss 0:727737138ac5 62 oldestMs = noteSent[i].noteOnMs;
kayekss 0:727737138ac5 63 }
kayekss 0:727737138ac5 64 }
kayekss 0:727737138ac5 65 }
kayekss 0:727737138ac5 66 if (oldest != 0xffff) {
kayekss 0:727737138ac5 67 noteSent[oldest].noteOnMs = 0;
kayekss 0:727737138ac5 68 noteSent[oldest].channel = 0;
kayekss 0:727737138ac5 69 noteSent[oldest].noteNumber = 0;
kayekss 0:727737138ac5 70 noteSent[oldest].velocity = 0;
kayekss 0:727737138ac5 71
kayekss 0:727737138ac5 72 // Send note off message to Performer
kayekss 0:727737138ac5 73 targetInst = oldest % NUM_INSTRUMENT_IN_PERFORMER;
kayekss 0:727737138ac5 74 gemCore.noteOff(targetInst);
kayekss 0:727737138ac5 75
kayekss 0:727737138ac5 76 if (dumpMode == DUMP_INST || dumpMode == DUMP_INST_DETAIL) {
kayekss 0:727737138ac5 77 console.printf("%-12s", "Note off");
kayekss 0:727737138ac5 78 dumpInstrumentList();
kayekss 0:727737138ac5 79 }
kayekss 0:727737138ac5 80 } else {
kayekss 0:727737138ac5 81 if (dumpMode == DUMP_INST || dumpMode == DUMP_INST_DETAIL) {
kayekss 0:727737138ac5 82 console.printf("* No such corresponding note: [ch=%2d n=%02Xh]\r\n",
kayekss 0:727737138ac5 83 channel + 1, noteNumber);
kayekss 0:727737138ac5 84 }
kayekss 0:727737138ac5 85 }
kayekss 0:727737138ac5 86 }
kayekss 0:727737138ac5 87
kayekss 0:727737138ac5 88 void dispatchNoteOn(char messageBytes[3]) {
kayekss 0:727737138ac5 89 uint32_t currentMs = __countMs;
kayekss 0:727737138ac5 90 uint32_t oldestMs = UINT_MAX;
kayekss 0:727737138ac5 91 uint16_t oldest = 0;
kayekss 0:727737138ac5 92 uint16_t targetInst;
kayekss 0:727737138ac5 93 uint8_t channel = messageBytes[0] & 0x0f;
kayekss 0:727737138ac5 94 uint8_t noteNumber = messageBytes[1] & 0x7f;
kayekss 0:727737138ac5 95 uint8_t velocity = messageBytes[2] & 0x7f;
kayekss 0:727737138ac5 96
kayekss 0:727737138ac5 97 // Skip if unimplemented note on rhythm channel
kayekss 0:727737138ac5 98 if (channel == 0x09 && !isImplementedDrumNoteNumber(noteNumber)) {
kayekss 0:727737138ac5 99 return;
kayekss 0:727737138ac5 100 }
kayekss 0:727737138ac5 101
kayekss 0:727737138ac5 102 // Find an available Instrument or the one with the oldest note
kayekss 0:727737138ac5 103 for (uint16_t i = 0; i < NUM_INSTRUMENT; i++) {
kayekss 0:727737138ac5 104 if (noteSent[i].noteOnMs < oldestMs) {
kayekss 0:727737138ac5 105 oldest = i;
kayekss 0:727737138ac5 106 oldestMs = noteSent[i].noteOnMs;
kayekss 0:727737138ac5 107 }
kayekss 0:727737138ac5 108 }
kayekss 0:727737138ac5 109
kayekss 0:727737138ac5 110 if (noteSent[oldest].channel != 0) {
kayekss 0:727737138ac5 111 if (dumpMode == DUMP_INST || dumpMode == DUMP_INST_DETAIL) {
kayekss 0:727737138ac5 112 console.printf("- Purged: #%2d [ch=%2d, n=%02Xh]\r\n",
kayekss 0:727737138ac5 113 oldest, noteSent[oldest].channel,
kayekss 0:727737138ac5 114 noteSent[oldest].noteNumber);
kayekss 0:727737138ac5 115 }
kayekss 0:727737138ac5 116 }
kayekss 0:727737138ac5 117
kayekss 0:727737138ac5 118 noteSent[oldest].noteOnMs = currentMs;
kayekss 0:727737138ac5 119 noteSent[oldest].channel = channel + 1;
kayekss 0:727737138ac5 120 noteSent[oldest].noteNumber = messageBytes[1];
kayekss 0:727737138ac5 121 noteSent[oldest].velocity = velocity & 0x70;
kayekss 0:727737138ac5 122
kayekss 0:727737138ac5 123 // Send note on message to Performer
kayekss 0:727737138ac5 124 targetInst = oldest % NUM_INSTRUMENT_IN_PERFORMER;
kayekss 0:727737138ac5 125 gemCore.volume(targetInst, ch.getVolume(channel));
kayekss 0:727737138ac5 126 gemCore.expression(targetInst, ch.getExpression(channel));
kayekss 0:727737138ac5 127 if (channel == 0x09) {
kayekss 0:727737138ac5 128 Wavetable::wave_t drumWave = { Wavetable::Noise, 4, 0 };
kayekss 0:727737138ac5 129 float drumFreq = 200;
kayekss 0:727737138ac5 130
kayekss 0:727737138ac5 131 switch (noteNumber) {
kayekss 0:727737138ac5 132 case 0x23: // Acoustic Bass Drum
kayekss 0:727737138ac5 133 case 0x24: // Bass Drum 1
kayekss 0:727737138ac5 134 drumWave.decaySpeed = 4;
kayekss 0:727737138ac5 135 drumFreq = 7;
kayekss 0:727737138ac5 136 break;
kayekss 0:727737138ac5 137 case 0x26: // Acoustic Snare
kayekss 0:727737138ac5 138 case 0x28: // Electric Snare
kayekss 0:727737138ac5 139 drumWave.decaySpeed = 4;
kayekss 0:727737138ac5 140 drumFreq = 35;
kayekss 0:727737138ac5 141 break;
kayekss 0:727737138ac5 142 case 0x2a: // Closed High-hat
kayekss 0:727737138ac5 143 case 0x2c: // Pedal High-hat
kayekss 0:727737138ac5 144 drumWave.decaySpeed = 3;
kayekss 0:727737138ac5 145 drumFreq = 200;
kayekss 0:727737138ac5 146 break;
kayekss 0:727737138ac5 147 case 0x2e: // Open High-hat
kayekss 0:727737138ac5 148 drumWave.decaySpeed = 5;
kayekss 0:727737138ac5 149 drumFreq = 400;
kayekss 0:727737138ac5 150 break;
kayekss 0:727737138ac5 151 case 0x31: // Crash Cymbal 1
kayekss 0:727737138ac5 152 case 0x39: // Crash Cymbal 2
kayekss 0:727737138ac5 153 drumWave.decaySpeed = 7;
kayekss 0:727737138ac5 154 drumFreq = 100;
kayekss 0:727737138ac5 155 break;
kayekss 0:727737138ac5 156 case 0x29: // Low Floor Tom
kayekss 0:727737138ac5 157 drumWave.wavetype = Wavetable::Triangle;
kayekss 0:727737138ac5 158 drumWave.decaySpeed = 5;
kayekss 0:727737138ac5 159 drumFreq = 120;
kayekss 0:727737138ac5 160 break;
kayekss 0:727737138ac5 161 case 0x2b: // High Floor Tom
kayekss 0:727737138ac5 162 drumWave.wavetype = Wavetable::Triangle;
kayekss 0:727737138ac5 163 drumWave.decaySpeed = 5;
kayekss 0:727737138ac5 164 drumFreq = 160;
kayekss 0:727737138ac5 165 break;
kayekss 0:727737138ac5 166 case 0x2d: // Low Tom
kayekss 0:727737138ac5 167 drumWave.wavetype = Wavetable::Triangle;
kayekss 0:727737138ac5 168 drumWave.decaySpeed = 5;
kayekss 0:727737138ac5 169 drumFreq = 190;
kayekss 0:727737138ac5 170 break;
kayekss 0:727737138ac5 171 case 0x2f: // Low-Mid Tom
kayekss 0:727737138ac5 172 drumWave.wavetype = Wavetable::Triangle;
kayekss 0:727737138ac5 173 drumWave.decaySpeed = 5;
kayekss 0:727737138ac5 174 drumFreq = 220;
kayekss 0:727737138ac5 175 break;
kayekss 0:727737138ac5 176 case 0x30: // High-Mid Tom
kayekss 0:727737138ac5 177 drumWave.wavetype = Wavetable::Triangle;
kayekss 0:727737138ac5 178 drumWave.decaySpeed = 5;
kayekss 0:727737138ac5 179 drumFreq = 250;
kayekss 0:727737138ac5 180 break;
kayekss 0:727737138ac5 181 case 0x32: // High Tom
kayekss 0:727737138ac5 182 drumWave.wavetype = Wavetable::Triangle;
kayekss 0:727737138ac5 183 drumWave.decaySpeed = 5;
kayekss 0:727737138ac5 184 drumFreq = 280;
kayekss 0:727737138ac5 185 break;
kayekss 0:727737138ac5 186 case 0x25: // Side Stick
kayekss 0:727737138ac5 187 drumWave.decaySpeed = 3;
kayekss 0:727737138ac5 188 drumFreq = 60;
kayekss 0:727737138ac5 189 break;
kayekss 0:727737138ac5 190 default:
kayekss 0:727737138ac5 191 break;
kayekss 0:727737138ac5 192 }
kayekss 0:727737138ac5 193 gemCore.setWave(targetInst, drumWave);
kayekss 0:727737138ac5 194 gemCore.noteOn(targetInst, drumFreq, velocity);
kayekss 0:727737138ac5 195 } else {
kayekss 0:727737138ac5 196 gemCore.setWave(targetInst, ch.getWave(channel));
kayekss 0:727737138ac5 197 gemCore.noteOn(targetInst, frequencyTable[noteNumber], velocity);
kayekss 0:727737138ac5 198 }
kayekss 0:727737138ac5 199
kayekss 0:727737138ac5 200 if (dumpMode == DUMP_INST || dumpMode == DUMP_INST_DETAIL) {
kayekss 0:727737138ac5 201 console.printf("%-12s", "Note on");
kayekss 0:727737138ac5 202 dumpInstrumentList();
kayekss 0:727737138ac5 203 }
kayekss 0:727737138ac5 204 }
kayekss 0:727737138ac5 205
kayekss 0:727737138ac5 206 void sendControlChange(char messageBytes[3]) {
kayekss 0:727737138ac5 207 uint8_t channel = messageBytes[0] & 0x0f;
kayekss 0:727737138ac5 208 uint8_t controlNumber = messageBytes[1];
kayekss 0:727737138ac5 209 uint8_t value = messageBytes[2];
kayekss 0:727737138ac5 210
kayekss 0:727737138ac5 211 switch (controlNumber) {
kayekss 0:727737138ac5 212 case 0x07:
kayekss 0:727737138ac5 213 ch.setVolume(channel, value);
kayekss 0:727737138ac5 214 for (uint8_t i = 0; i < NUM_INSTRUMENT; i++) {
kayekss 0:727737138ac5 215 if (noteSent[i].channel == channel + 1) {
kayekss 0:727737138ac5 216 gemCore.volume(i, value);
kayekss 0:727737138ac5 217 }
kayekss 0:727737138ac5 218 }
kayekss 0:727737138ac5 219 break;
kayekss 0:727737138ac5 220 case 0x0b:
kayekss 0:727737138ac5 221 ch.setExpression(channel, value);
kayekss 0:727737138ac5 222 for (uint8_t i = 0; i < NUM_INSTRUMENT; i++) {
kayekss 0:727737138ac5 223 if (noteSent[i].channel == channel + 1) {
kayekss 0:727737138ac5 224 gemCore.expression(i, value);
kayekss 0:727737138ac5 225 }
kayekss 0:727737138ac5 226 }
kayekss 0:727737138ac5 227 break;
kayekss 0:727737138ac5 228 default:
kayekss 1:5f0c89bffec1 229 // Other program change process goes here
kayekss 0:727737138ac5 230 break;
kayekss 0:727737138ac5 231 }
kayekss 0:727737138ac5 232 }
kayekss 0:727737138ac5 233
kayekss 0:727737138ac5 234 void sendProgramChange(char messageBytes[2]) {
kayekss 0:727737138ac5 235 uint8_t channel = messageBytes[0] & 0x0f;
kayekss 0:727737138ac5 236 uint8_t value = messageBytes[1] & 0x7f;
kayekss 0:727737138ac5 237 Wavetable::wave_t wave = Wavetable::waveDefList[value];
kayekss 0:727737138ac5 238
kayekss 0:727737138ac5 239 // Change channel wave type
kayekss 0:727737138ac5 240 ch.setWave(channel, wave);
kayekss 0:727737138ac5 241 }
kayekss 0:727737138ac5 242
kayekss 0:727737138ac5 243 void sendPitchBend(char messageBytes[3]) {
kayekss 0:727737138ac5 244 // @TODO
kayekss 0:727737138ac5 245 }
kayekss 0:727737138ac5 246
kayekss 0:727737138ac5 247 void allNoteOff(char messageBytes[3]) {
kayekss 0:727737138ac5 248 for (uint16_t i = 0; i < NUM_INSTRUMENT; i++) {
kayekss 0:727737138ac5 249 if (noteSent[i].channel == (messageBytes[0] & 0x0f) + 1) {
kayekss 0:727737138ac5 250 noteSent[i].noteOnMs = 0;
kayekss 0:727737138ac5 251 noteSent[i].channel = 0;
kayekss 0:727737138ac5 252 noteSent[i].noteNumber = 0;
kayekss 0:727737138ac5 253 noteSent[i].velocity = 0;
kayekss 0:727737138ac5 254 gemCore.noteOff(i);
kayekss 0:727737138ac5 255 }
kayekss 0:727737138ac5 256 }
kayekss 0:727737138ac5 257
kayekss 0:727737138ac5 258 if (dumpMode == DUMP_INST || dumpMode == DUMP_INST_DETAIL) {
kayekss 0:727737138ac5 259 console.printf("%-12s", "All note off");
kayekss 0:727737138ac5 260 dumpInstrumentList();
kayekss 0:727737138ac5 261 }
kayekss 0:727737138ac5 262 }
kayekss 0:727737138ac5 263
kayekss 0:727737138ac5 264 void sendSystemReset() {
kayekss 0:727737138ac5 265 for (uint16_t i = 0; i < NUM_INSTRUMENT; i++) {
kayekss 0:727737138ac5 266 noteSent[i].noteOnMs = 0;
kayekss 0:727737138ac5 267 noteSent[i].channel = 0;
kayekss 0:727737138ac5 268 noteSent[i].noteNumber = 0;
kayekss 0:727737138ac5 269 noteSent[i].velocity = 0;
kayekss 0:727737138ac5 270 }
kayekss 0:727737138ac5 271
kayekss 0:727737138ac5 272 if (dumpMode == DUMP_INST || dumpMode == DUMP_INST_DETAIL) {
kayekss 0:727737138ac5 273 console.printf("%-12s", "System reset");
kayekss 0:727737138ac5 274 dumpInstrumentList();
kayekss 0:727737138ac5 275 }
kayekss 0:727737138ac5 276 for (uint8_t i = 0; i < NUM_INSTRUMENT; i++) {
kayekss 0:727737138ac5 277 gemCore.noteOff(i);
kayekss 0:727737138ac5 278 gemCore.volume(i, 100);
kayekss 0:727737138ac5 279 gemCore.expression(i, 127);
kayekss 0:727737138ac5 280 }
kayekss 0:727737138ac5 281 }