12-polyphonic "chiptune" MIDI synthesizer for LPC1768 (Standalone version)
Dependencies: ClockControl PowerControl mbed
Diff: main.cpp
- Revision:
- 0:727737138ac5
- Child:
- 1:5f0c89bffec1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Sun Nov 09 08:00:33 2014 +0000 @@ -0,0 +1,222 @@ +#include "mbed.h" +#include "ClockControl.h" +#include "EthernetPowerControl.h" +#include "PowerControl.h" +#include "defs.h" +#include "Channels.h" +#include "GeminiCore.h" +#include "RingBuffer.h" +#include "debug.h" +#include "events.h" +#include "note.h" +#include "parser.h" + +Serial pc(/*Tx*/ USBTX, /*Rx*/ USBRX); +Serial midiIn(/*Tx*/ p28, /*Rx*/ p27); // @TODO final: p13, p14 +Serial console(/*Tx*/ p13, /*Rx*/ p14); // @TODO final: p28, p27 +DigitalIn swSerialSelect(); +DigitalIn swPanic(p15); +BusOut led(/*Left*/ LED1, LED2, LED3, LED4 /*Right*/); +BusOut ledBar(p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p29, p30); +SPI spi(/*MOSI*/ p5, /*MISO*/ p6, /*SCK*/ p7); +DigitalOut dacCs(p8); +Ticker t1ms; +Ticker tSample; +volatile uint32_t __countMs; +bool serSource; +GeminiCore gemCore(NUM_INSTRUMENT_IN_PERFORMER); +RingBuffer<char> buffer(BUFFER_LENGTH); +Channels ch; +note_t noteSent[NUM_INSTRUMENT]; +uint32_t receivedBytes; +dumpmode_t dumpMode; + +void sampleOut() { + // soundOut.write(gemCore.makeSample() / 65536.0); + dacCs = 0; + spi.write(0x3000 | gemCore.makeSample() >> 4); + dacCs = 1; +} + +void count1ms() { + __countMs++; +} + +void readMidiIn() { + char c; + + // Put a MIDI input byte into buffer if available + if ((serSource ? midiIn : pc).readable()) { + c = (serSource ? midiIn : pc).getc(); + receivedBytes++; + + // Discard if input byte is an active sensing message + if (c != 0xfe) { + buffer.write(c); + } + } +} + +void setup() { +#ifdef POWER_SAVE + // Power down Ethernet PHY chip + PHY_PowerDown(); + + // Power down unused peripherals + Peripheral_PowerDown(0x40067200); +#endif + +#if defined(POWER_SAVE) && defined(CLOCKUP) + // Change clock speed to 120 MHz + setSystemFrequency(0x03, 0x01, 15, 1); +#endif + + // Enable pull-up at switch inputs + // swSerialSelect.mode(PullUp); + swPanic.mode(PullUp); + wait(0.2); // Wait a moment for stability + + // Read serial selector switch + serSource = 1; // @TODO final: serSource = swSerialSelect; + + // Open selected port + if (serSource) { + // Use MIDI port when high + midiIn.baud(38400); // @TODO final: midiIn.baud(31250); + midiIn.format(8, Serial::None, 1); + midiIn.attach(readMidiIn, Serial::RxIrq); + } else { + // Use serial MIDI when low + pc.baud(38400); + pc.format(8, Serial::None, 1); + pc.attach(readMidiIn, Serial::RxIrq); + } + receivedBytes = 0; + + // Setup console + console.baud(115200); + console.format(8, Serial::None, 1); + dumpMode = DUMP_NOTHING; + + // Setup SPI + spi.format(16, 0); + spi.frequency(12000000); + dacCs = 1; + + // Initialize channels + ch.initializeAll(); + + // Initialize note status + for (uint16_t i = 0; i < NUM_INSTRUMENT; i++) { + noteSent[i].noteOnMs = 0; + noteSent[i].channel = 0; + noteSent[i].noteNumber = 0; + noteSent[i].velocity = 0; + } + + // Start Timer + __countMs = 0; + t1ms.attach_us(&count1ms, 1000); + + // Start playback & attach interrupt +#if defined(POWER_SAVE) && defined(CLOCKUP) + tSample.attach_us(&sampleOut, 1250000 / GeminiCore::samplingRate); +#else + tSample.attach_us(&sampleOut, 1000000 / GeminiCore::samplingRate); +#endif +} + +void consoleOperations(uint8_t c) { + switch (c) { + case 'b': + checkBuffer(); + break; + case 'e': + if (dumpMode != DUMP_EVENTS) { + dumpMode = DUMP_EVENTS; + } else { + dumpMode = DUMP_NOTHING; + } + break; + case 'f': + buffer.flush(); + console.printf("* Buffer flushed.\r\n"); + break; + case 'i': + if (dumpMode == DUMP_INST) { + dumpMode = DUMP_INST_DETAIL; + } else if (dumpMode == DUMP_INST_DETAIL) { + dumpMode = DUMP_NOTHING; + } else { + dumpMode = DUMP_INST; + } + break; + case 'p': + // Panic + for (uint8_t i = 0; i < NUM_INSTRUMENT_IN_PERFORMER; i++) { + gemCore.noteOff(i); + noteSent[i].noteOnMs = 0; + noteSent[i].channel = 0; + noteSent[i].noteNumber = 0; + noteSent[i].velocity = 0; + } + break; + case 'r': + console.printf("* Received MIDI bytes: %u\r\n", receivedBytes); + break; + default: break; + } +} + +void loop() { + static uint32_t lastPollSwitchMs = __countMs; + static bool prevstatePanic = 0; + bool statePanic; + + // Serial console + if (console.readable()) { + consoleOperations(console.getc()); + } + + // Poll switches + if (__countMs > lastPollSwitchMs + 20) { + lastPollSwitchMs = __countMs; + + statePanic = swPanic; + + // Panic (all note off and flush buffer) + if (!statePanic && prevstatePanic) { + buffer.flush(); + for (uint8_t i = 0; i < NUM_INSTRUMENT_IN_PERFORMER; i++) { + gemCore.noteOff(i); + noteSent[i].noteOnMs = 0; + noteSent[i].channel = 0; + noteSent[i].noteNumber = 0; + noteSent[i].velocity = 0; + } + } + + // Preserve this time's switch states + prevstatePanic = statePanic; + } + + // Parse MIDI messages + parseMessage(buffer); + + // LEDs + uint8_t busyCount = 0; + for (uint8_t i = 0; i < NUM_INSTRUMENT_IN_PERFORMER; i++) { + if (noteSent[i].noteOnMs) { + busyCount++; + } + } + ledBar = (1 << busyCount) - 1; +} + +int main() { + setup(); + + while (1) { + loop(); + } +}