12-polyphonic "chiptune" MIDI synthesizer for LPC1768 (Standalone version)
Dependencies: ClockControl PowerControl mbed
main.cpp@6:cda45a5e723e, 2014-12-23 (annotated)
- 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?
User | Revision | Line number | New 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 | } |