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

Dependencies:   ClockControl PowerControl mbed

main.cpp

Committer:
kayekss
Date:
2014-12-23
Revision:
6:cda45a5e723e
Parent:
3:cf57d7031c12

File content as of revision 6:cda45a5e723e:

#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*/ p13, /*Rx*/ p14);
Serial            console(/*Tx*/ p28, /*Rx*/ p27);
DigitalIn         swSerialSelect(p12);
DigitalIn         swPanic(p15);
BusOut            ledBar(p17, p18, p19, p20, p21, p22, p23, p24,
                         p25, p26, p29, p30, LED1, LED2, LED3, LED4);
SPI               spi(/*MOSI*/ p5, /*MISO*/ p6, /*SCK*/ p7);
DigitalOut        dacCs(p8);
Ticker            t1ms;
Ticker            tSample;
volatile uint32_t __countMs;
volatile uint32_t __lastActiveSenseReceivedMs;
bool              serSource;
GeminiCore        gemCore(NUM_INSTRUMENT_IN_PERFORMER, SAMPLING_RATE);
RingBuffer<char>  buffer(BUFFER_LENGTH);
Channels          ch;
note_t            noteSent[NUM_INSTRUMENT];
uint32_t          receivedBytes;
dumpmode_t        dumpMode;

void sampleOut() {
    dacCs = 0;
    spi.write(0x3000 | gemCore.makeSample(noteSent) >> 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);
        } else {
            __lastActiveSenseReceivedMs = __countMs;
        }
    }
}

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 = swSerialSelect;
    
    // Open selected port
    if (serSource) {
        // Use MIDI port when high
        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;
    __lastActiveSenseReceivedMs = __countMs;
    t1ms.attach_us(&count1ms, 1000);
    
    // Start playback & attach interrupt
    float clockUpRatio = SystemCoreClock / 96000000.0;
    tSample.attach_us(&sampleOut, 1000000 * clockUpRatio / SAMPLING_RATE);
}

void 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;
    }
}

void consoleOperations(uint8_t c) {
    switch (c) {
    case 'b':  // Check buffer
        checkBuffer();
        break;
    case 'e':  // Toggle event dump
        if (dumpMode != DUMP_EVENTS) {
            dumpMode = DUMP_EVENTS;
        } else {
            dumpMode = DUMP_NOTHING;
        }
        break;
    case 'f':  // Flush buffer
        buffer.flush();
        console.printf("* Buffer flushed.\r\n");
        break;
    case 'i':  // Toggle Instrument dump
        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
        panic();
        break;
    case 'r':  // Print received MIDI bytes
        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 and flush buffer
        if (!statePanic && prevstatePanic) {
            buffer.flush();
            panic();
        }
        
        // Preserve this time's switch states
        prevstatePanic = statePanic;
    }
    
    // Panic on active sensing failure
    if (serSource && (__countMs > __lastActiveSenseReceivedMs + 400)) {
        __lastActiveSenseReceivedMs = __countMs;
        panic();
    }
    
    // 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();
    }
}