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:
3:cf57d7031c12
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 "mbed.h"
kayekss 0:727737138ac5 2 #include "ClockControl.h"
kayekss 0:727737138ac5 3 #include "EthernetPowerControl.h"
kayekss 0:727737138ac5 4 #include "PowerControl.h"
kayekss 0:727737138ac5 5 #include "defs.h"
kayekss 0:727737138ac5 6 #include "Channels.h"
kayekss 0:727737138ac5 7 #include "GeminiCore.h"
kayekss 0:727737138ac5 8 #include "RingBuffer.h"
kayekss 0:727737138ac5 9 #include "debug.h"
kayekss 0:727737138ac5 10 #include "events.h"
kayekss 0:727737138ac5 11 #include "note.h"
kayekss 0:727737138ac5 12 #include "parser.h"
kayekss 0:727737138ac5 13
kayekss 0:727737138ac5 14 Serial pc(/*Tx*/ USBTX, /*Rx*/ USBRX);
kayekss 1:5f0c89bffec1 15 Serial midiIn(/*Tx*/ p13, /*Rx*/ p14);
kayekss 1:5f0c89bffec1 16 Serial console(/*Tx*/ p28, /*Rx*/ p27);
kayekss 2:ca10e33bde0a 17 DigitalIn swSerialSelect(p12);
kayekss 0:727737138ac5 18 DigitalIn swPanic(p15);
kayekss 3:cf57d7031c12 19 BusOut ledBar(p17, p18, p19, p20, p21, p22, p23, p24,
kayekss 3:cf57d7031c12 20 p25, p26, p29, p30, LED1, LED2, LED3, LED4);
kayekss 0:727737138ac5 21 SPI spi(/*MOSI*/ p5, /*MISO*/ p6, /*SCK*/ p7);
kayekss 0:727737138ac5 22 DigitalOut dacCs(p8);
kayekss 0:727737138ac5 23 Ticker t1ms;
kayekss 0:727737138ac5 24 Ticker tSample;
kayekss 0:727737138ac5 25 volatile uint32_t __countMs;
kayekss 6:cda45a5e723e 26 volatile uint32_t __lastActiveSenseReceivedMs;
kayekss 0:727737138ac5 27 bool serSource;
kayekss 3:cf57d7031c12 28 GeminiCore gemCore(NUM_INSTRUMENT_IN_PERFORMER, SAMPLING_RATE);
kayekss 0:727737138ac5 29 RingBuffer<char> buffer(BUFFER_LENGTH);
kayekss 0:727737138ac5 30 Channels ch;
kayekss 0:727737138ac5 31 note_t noteSent[NUM_INSTRUMENT];
kayekss 0:727737138ac5 32 uint32_t receivedBytes;
kayekss 0:727737138ac5 33 dumpmode_t dumpMode;
kayekss 0:727737138ac5 34
kayekss 0:727737138ac5 35 void sampleOut() {
kayekss 0:727737138ac5 36 dacCs = 0;
kayekss 2:ca10e33bde0a 37 spi.write(0x3000 | gemCore.makeSample(noteSent) >> 4);
kayekss 0:727737138ac5 38 dacCs = 1;
kayekss 0:727737138ac5 39 }
kayekss 0:727737138ac5 40
kayekss 0:727737138ac5 41 void count1ms() {
kayekss 0:727737138ac5 42 __countMs++;
kayekss 0:727737138ac5 43 }
kayekss 0:727737138ac5 44
kayekss 0:727737138ac5 45 void readMidiIn() {
kayekss 0:727737138ac5 46 char c;
kayekss 0:727737138ac5 47
kayekss 0:727737138ac5 48 // Put a MIDI input byte into buffer if available
kayekss 0:727737138ac5 49 if ((serSource ? midiIn : pc).readable()) {
kayekss 0:727737138ac5 50 c = (serSource ? midiIn : pc).getc();
kayekss 0:727737138ac5 51 receivedBytes++;
kayekss 0:727737138ac5 52
kayekss 0:727737138ac5 53 // Discard if input byte is an active sensing message
kayekss 0:727737138ac5 54 if (c != 0xfe) {
kayekss 0:727737138ac5 55 buffer.write(c);
kayekss 6:cda45a5e723e 56 } else {
kayekss 6:cda45a5e723e 57 __lastActiveSenseReceivedMs = __countMs;
kayekss 0:727737138ac5 58 }
kayekss 0:727737138ac5 59 }
kayekss 0:727737138ac5 60 }
kayekss 0:727737138ac5 61
kayekss 0:727737138ac5 62 void setup() {
kayekss 0:727737138ac5 63 #ifdef POWER_SAVE
kayekss 0:727737138ac5 64 // Power down Ethernet PHY chip
kayekss 0:727737138ac5 65 PHY_PowerDown();
kayekss 0:727737138ac5 66
kayekss 0:727737138ac5 67 // Power down unused peripherals
kayekss 0:727737138ac5 68 Peripheral_PowerDown(0x40067200);
kayekss 0:727737138ac5 69 #endif
kayekss 0:727737138ac5 70
kayekss 0:727737138ac5 71 #if defined(POWER_SAVE) && defined(CLOCKUP)
kayekss 0:727737138ac5 72 // Change clock speed to 120 MHz
kayekss 0:727737138ac5 73 setSystemFrequency(0x03, 0x01, 15, 1);
kayekss 0:727737138ac5 74 #endif
kayekss 0:727737138ac5 75
kayekss 0:727737138ac5 76 // Enable pull-up at switch inputs
kayekss 3:cf57d7031c12 77 swSerialSelect.mode(PullUp);
kayekss 0:727737138ac5 78 swPanic.mode(PullUp);
kayekss 0:727737138ac5 79 wait(0.2); // Wait a moment for stability
kayekss 0:727737138ac5 80
kayekss 0:727737138ac5 81 // Read serial selector switch
kayekss 1:5f0c89bffec1 82 serSource = swSerialSelect;
kayekss 0:727737138ac5 83
kayekss 0:727737138ac5 84 // Open selected port
kayekss 0:727737138ac5 85 if (serSource) {
kayekss 0:727737138ac5 86 // Use MIDI port when high
kayekss 1:5f0c89bffec1 87 midiIn.baud(31250);
kayekss 0:727737138ac5 88 midiIn.format(8, Serial::None, 1);
kayekss 0:727737138ac5 89 midiIn.attach(readMidiIn, Serial::RxIrq);
kayekss 0:727737138ac5 90 } else {
kayekss 0:727737138ac5 91 // Use serial MIDI when low
kayekss 0:727737138ac5 92 pc.baud(38400);
kayekss 0:727737138ac5 93 pc.format(8, Serial::None, 1);
kayekss 0:727737138ac5 94 pc.attach(readMidiIn, Serial::RxIrq);
kayekss 0:727737138ac5 95 }
kayekss 0:727737138ac5 96 receivedBytes = 0;
kayekss 0:727737138ac5 97
kayekss 0:727737138ac5 98 // Setup console
kayekss 0:727737138ac5 99 console.baud(115200);
kayekss 0:727737138ac5 100 console.format(8, Serial::None, 1);
kayekss 0:727737138ac5 101 dumpMode = DUMP_NOTHING;
kayekss 0:727737138ac5 102
kayekss 0:727737138ac5 103 // Setup SPI
kayekss 0:727737138ac5 104 spi.format(16, 0);
kayekss 0:727737138ac5 105 spi.frequency(12000000);
kayekss 0:727737138ac5 106 dacCs = 1;
kayekss 0:727737138ac5 107
kayekss 0:727737138ac5 108 // Initialize channels
kayekss 0:727737138ac5 109 ch.initializeAll();
kayekss 0:727737138ac5 110
kayekss 0:727737138ac5 111 // Initialize note status
kayekss 0:727737138ac5 112 for (uint16_t i = 0; i < NUM_INSTRUMENT; i++) {
kayekss 0:727737138ac5 113 noteSent[i].noteOnMs = 0;
kayekss 0:727737138ac5 114 noteSent[i].channel = 0;
kayekss 0:727737138ac5 115 noteSent[i].noteNumber = 0;
kayekss 0:727737138ac5 116 noteSent[i].velocity = 0;
kayekss 0:727737138ac5 117 }
kayekss 0:727737138ac5 118
kayekss 0:727737138ac5 119 // Start Timer
kayekss 0:727737138ac5 120 __countMs = 0;
kayekss 6:cda45a5e723e 121 __lastActiveSenseReceivedMs = __countMs;
kayekss 0:727737138ac5 122 t1ms.attach_us(&count1ms, 1000);
kayekss 0:727737138ac5 123
kayekss 0:727737138ac5 124 // Start playback & attach interrupt
kayekss 2:ca10e33bde0a 125 float clockUpRatio = SystemCoreClock / 96000000.0;
kayekss 3:cf57d7031c12 126 tSample.attach_us(&sampleOut, 1000000 * clockUpRatio / SAMPLING_RATE);
kayekss 0:727737138ac5 127 }
kayekss 0:727737138ac5 128
kayekss 6:cda45a5e723e 129 void panic() {
kayekss 6:cda45a5e723e 130 for (uint8_t i = 0; i < NUM_INSTRUMENT_IN_PERFORMER; i++) {
kayekss 6:cda45a5e723e 131 gemCore.noteOff(i);
kayekss 6:cda45a5e723e 132 noteSent[i].noteOnMs = 0;
kayekss 6:cda45a5e723e 133 noteSent[i].channel = 0;
kayekss 6:cda45a5e723e 134 noteSent[i].noteNumber = 0;
kayekss 6:cda45a5e723e 135 noteSent[i].velocity = 0;
kayekss 6:cda45a5e723e 136 }
kayekss 6:cda45a5e723e 137 }
kayekss 6:cda45a5e723e 138
kayekss 0:727737138ac5 139 void consoleOperations(uint8_t c) {
kayekss 0:727737138ac5 140 switch (c) {
kayekss 6:cda45a5e723e 141 case 'b': // Check buffer
kayekss 0:727737138ac5 142 checkBuffer();
kayekss 0:727737138ac5 143 break;
kayekss 6:cda45a5e723e 144 case 'e': // Toggle event dump
kayekss 0:727737138ac5 145 if (dumpMode != DUMP_EVENTS) {
kayekss 0:727737138ac5 146 dumpMode = DUMP_EVENTS;
kayekss 0:727737138ac5 147 } else {
kayekss 0:727737138ac5 148 dumpMode = DUMP_NOTHING;
kayekss 0:727737138ac5 149 }
kayekss 0:727737138ac5 150 break;
kayekss 6:cda45a5e723e 151 case 'f': // Flush buffer
kayekss 0:727737138ac5 152 buffer.flush();
kayekss 0:727737138ac5 153 console.printf("* Buffer flushed.\r\n");
kayekss 0:727737138ac5 154 break;
kayekss 6:cda45a5e723e 155 case 'i': // Toggle Instrument dump
kayekss 0:727737138ac5 156 if (dumpMode == DUMP_INST) {
kayekss 0:727737138ac5 157 dumpMode = DUMP_INST_DETAIL;
kayekss 0:727737138ac5 158 } else if (dumpMode == DUMP_INST_DETAIL) {
kayekss 0:727737138ac5 159 dumpMode = DUMP_NOTHING;
kayekss 0:727737138ac5 160 } else {
kayekss 0:727737138ac5 161 dumpMode = DUMP_INST;
kayekss 0:727737138ac5 162 }
kayekss 0:727737138ac5 163 break;
kayekss 6:cda45a5e723e 164 case 'p': // Panic
kayekss 6:cda45a5e723e 165 panic();
kayekss 0:727737138ac5 166 break;
kayekss 6:cda45a5e723e 167 case 'r': // Print received MIDI bytes
kayekss 0:727737138ac5 168 console.printf("* Received MIDI bytes: %u\r\n", receivedBytes);
kayekss 0:727737138ac5 169 break;
kayekss 0:727737138ac5 170 default: break;
kayekss 0:727737138ac5 171 }
kayekss 0:727737138ac5 172 }
kayekss 0:727737138ac5 173
kayekss 0:727737138ac5 174 void loop() {
kayekss 0:727737138ac5 175 static uint32_t lastPollSwitchMs = __countMs;
kayekss 0:727737138ac5 176 static bool prevstatePanic = 0;
kayekss 0:727737138ac5 177 bool statePanic;
kayekss 0:727737138ac5 178
kayekss 0:727737138ac5 179 // Serial console
kayekss 0:727737138ac5 180 if (console.readable()) {
kayekss 0:727737138ac5 181 consoleOperations(console.getc());
kayekss 0:727737138ac5 182 }
kayekss 0:727737138ac5 183
kayekss 0:727737138ac5 184 // Poll switches
kayekss 0:727737138ac5 185 if (__countMs > lastPollSwitchMs + 20) {
kayekss 0:727737138ac5 186 lastPollSwitchMs = __countMs;
kayekss 0:727737138ac5 187
kayekss 0:727737138ac5 188 statePanic = swPanic;
kayekss 0:727737138ac5 189
kayekss 6:cda45a5e723e 190 // Panic and flush buffer
kayekss 0:727737138ac5 191 if (!statePanic && prevstatePanic) {
kayekss 0:727737138ac5 192 buffer.flush();
kayekss 6:cda45a5e723e 193 panic();
kayekss 0:727737138ac5 194 }
kayekss 0:727737138ac5 195
kayekss 0:727737138ac5 196 // Preserve this time's switch states
kayekss 0:727737138ac5 197 prevstatePanic = statePanic;
kayekss 0:727737138ac5 198 }
kayekss 0:727737138ac5 199
kayekss 6:cda45a5e723e 200 // Panic on active sensing failure
kayekss 6:cda45a5e723e 201 if (serSource && (__countMs > __lastActiveSenseReceivedMs + 400)) {
kayekss 6:cda45a5e723e 202 __lastActiveSenseReceivedMs = __countMs;
kayekss 6:cda45a5e723e 203 panic();
kayekss 6:cda45a5e723e 204 }
kayekss 6:cda45a5e723e 205
kayekss 0:727737138ac5 206 // Parse MIDI messages
kayekss 0:727737138ac5 207 parseMessage(buffer);
kayekss 0:727737138ac5 208
kayekss 0:727737138ac5 209 // LEDs
kayekss 0:727737138ac5 210 uint8_t busyCount = 0;
kayekss 0:727737138ac5 211 for (uint8_t i = 0; i < NUM_INSTRUMENT_IN_PERFORMER; i++) {
kayekss 0:727737138ac5 212 if (noteSent[i].noteOnMs) {
kayekss 0:727737138ac5 213 busyCount++;
kayekss 0:727737138ac5 214 }
kayekss 0:727737138ac5 215 }
kayekss 0:727737138ac5 216 ledBar = (1 << busyCount) - 1;
kayekss 0:727737138ac5 217 }
kayekss 0:727737138ac5 218
kayekss 0:727737138ac5 219 int main() {
kayekss 0:727737138ac5 220 setup();
kayekss 0:727737138ac5 221
kayekss 0:727737138ac5 222 while (1) {
kayekss 0:727737138ac5 223 loop();
kayekss 0:727737138ac5 224 }
kayekss 0:727737138ac5 225 }