12-polyphonic "chiptune" MIDI synthesizer for LPC1768 (Standalone version)
Dependencies: ClockControl PowerControl mbed
Diff: events.cpp
- Revision:
- 0:727737138ac5
- Child:
- 1:5f0c89bffec1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/events.cpp Sun Nov 09 08:00:33 2014 +0000 @@ -0,0 +1,281 @@ +#include <climits> +#include "mbed.h" +#include "defs.h" +#include "Channels.h" +#include "GeminiCore.h" +#include "Wavetable.h" +#include "debug.h" +#include "events.h" +#include "frequency.h" +#include "note.h" + +extern Serial console; +extern volatile uint32_t __countMs; +extern GeminiCore gemCore; +extern Channels ch; +extern note_t noteSent[NUM_INSTRUMENT]; +extern float const frequencyTable[128]; +extern dumpmode_t dumpMode; + +bool isImplementedDrumNoteNumber(uint8_t noteNumber) { + switch (noteNumber) { + case 0x23: // Acoustic Bass Drum + case 0x24: // Bass Drum 1 + case 0x25: // Side Stick + case 0x26: // Acoustic Snare + case 0x28: // Electric Snare + case 0x29: // Low Floor Tom + case 0x2a: // Closed High-hat + case 0x2b: // High Floor Tom + case 0x2c: // Pedal High-hat + case 0x2d: // Low Tom + case 0x2e: // Open High-hat + case 0x2f: // Low-Mid Tom + case 0x30: // High-Mid Tom + case 0x31: // Crash Cymbal 1 + case 0x32: // High Tom + case 0x39: // Crash Cymbal 2 + return true; + default: + return false; + } +} + +void dispatchNoteOff(char messageBytes[3]) { + uint16_t targetInst; + uint16_t oldest = 0xffff; + uint32_t oldestMs = UINT_MAX; + uint8_t channel = messageBytes[0] & 0x0f; + uint8_t noteNumber = messageBytes[1] & 0x7f; + + // Skip if rhythm channel + if (channel == 0x09) { + return; + } + + // Find an (oldest) Instrument sounding the specified note number + for (uint16_t i = 0; i < NUM_INSTRUMENT; i++) { + if (noteSent[i].channel == channel + 1 + && noteSent[i].noteNumber == noteNumber) { + if (noteSent[i].noteOnMs < oldestMs) { + oldest = i; + oldestMs = noteSent[i].noteOnMs; + } + } + } + if (oldest != 0xffff) { + noteSent[oldest].noteOnMs = 0; + noteSent[oldest].channel = 0; + noteSent[oldest].noteNumber = 0; + noteSent[oldest].velocity = 0; + + // Send note off message to Performer + targetInst = oldest % NUM_INSTRUMENT_IN_PERFORMER; + gemCore.noteOff(targetInst); + + if (dumpMode == DUMP_INST || dumpMode == DUMP_INST_DETAIL) { + console.printf("%-12s", "Note off"); + dumpInstrumentList(); + } + } else { + if (dumpMode == DUMP_INST || dumpMode == DUMP_INST_DETAIL) { + console.printf("* No such corresponding note: [ch=%2d n=%02Xh]\r\n", + channel + 1, noteNumber); + } + } +} + +void dispatchNoteOn(char messageBytes[3]) { + uint32_t currentMs = __countMs; + uint32_t oldestMs = UINT_MAX; + uint16_t oldest = 0; + uint16_t targetInst; + uint8_t channel = messageBytes[0] & 0x0f; + uint8_t noteNumber = messageBytes[1] & 0x7f; + uint8_t velocity = messageBytes[2] & 0x7f; + + // Skip if unimplemented note on rhythm channel + if (channel == 0x09 && !isImplementedDrumNoteNumber(noteNumber)) { + return; + } + + // Find an available Instrument or the one with the oldest note + for (uint16_t i = 0; i < NUM_INSTRUMENT; i++) { + if (noteSent[i].noteOnMs < oldestMs) { + oldest = i; + oldestMs = noteSent[i].noteOnMs; + } + } + + if (noteSent[oldest].channel != 0) { + if (dumpMode == DUMP_INST || dumpMode == DUMP_INST_DETAIL) { + console.printf("- Purged: #%2d [ch=%2d, n=%02Xh]\r\n", + oldest, noteSent[oldest].channel, + noteSent[oldest].noteNumber); + } + } + + noteSent[oldest].noteOnMs = currentMs; + noteSent[oldest].channel = channel + 1; + noteSent[oldest].noteNumber = messageBytes[1]; + noteSent[oldest].velocity = velocity & 0x70; + + // Send note on message to Performer + targetInst = oldest % NUM_INSTRUMENT_IN_PERFORMER; + gemCore.volume(targetInst, ch.getVolume(channel)); + gemCore.expression(targetInst, ch.getExpression(channel)); + if (channel == 0x09) { + Wavetable::wave_t drumWave = { Wavetable::Noise, 4, 0 }; + float drumFreq = 200; + + switch (noteNumber) { + case 0x23: // Acoustic Bass Drum + case 0x24: // Bass Drum 1 + drumWave.decaySpeed = 4; + drumFreq = 7; + break; + case 0x26: // Acoustic Snare + case 0x28: // Electric Snare + drumWave.decaySpeed = 4; + drumFreq = 35; + break; + case 0x2a: // Closed High-hat + case 0x2c: // Pedal High-hat + drumWave.decaySpeed = 3; + drumFreq = 200; + break; + case 0x2e: // Open High-hat + drumWave.decaySpeed = 5; + drumFreq = 400; + break; + case 0x31: // Crash Cymbal 1 + case 0x39: // Crash Cymbal 2 + drumWave.decaySpeed = 7; + drumFreq = 100; + break; + case 0x29: // Low Floor Tom + drumWave.wavetype = Wavetable::Triangle; + drumWave.decaySpeed = 5; + drumFreq = 120; + break; + case 0x2b: // High Floor Tom + drumWave.wavetype = Wavetable::Triangle; + drumWave.decaySpeed = 5; + drumFreq = 160; + break; + case 0x2d: // Low Tom + drumWave.wavetype = Wavetable::Triangle; + drumWave.decaySpeed = 5; + drumFreq = 190; + break; + case 0x2f: // Low-Mid Tom + drumWave.wavetype = Wavetable::Triangle; + drumWave.decaySpeed = 5; + drumFreq = 220; + break; + case 0x30: // High-Mid Tom + drumWave.wavetype = Wavetable::Triangle; + drumWave.decaySpeed = 5; + drumFreq = 250; + break; + case 0x32: // High Tom + drumWave.wavetype = Wavetable::Triangle; + drumWave.decaySpeed = 5; + drumFreq = 280; + break; + case 0x25: // Side Stick + drumWave.decaySpeed = 3; + drumFreq = 60; + break; + default: + break; + } + gemCore.setWave(targetInst, drumWave); + gemCore.noteOn(targetInst, drumFreq, velocity); + } else { + gemCore.setWave(targetInst, ch.getWave(channel)); + gemCore.noteOn(targetInst, frequencyTable[noteNumber], velocity); + } + + if (dumpMode == DUMP_INST || dumpMode == DUMP_INST_DETAIL) { + console.printf("%-12s", "Note on"); + dumpInstrumentList(); + } +} + +void sendControlChange(char messageBytes[3]) { + uint8_t channel = messageBytes[0] & 0x0f; + uint8_t controlNumber = messageBytes[1]; + uint8_t value = messageBytes[2]; + + switch (controlNumber) { + case 0x07: + ch.setVolume(channel, value); + for (uint8_t i = 0; i < NUM_INSTRUMENT; i++) { + if (noteSent[i].channel == channel + 1) { + gemCore.volume(i, value); + } + } + break; + case 0x0b: + ch.setExpression(channel, value); + for (uint8_t i = 0; i < NUM_INSTRUMENT; i++) { + if (noteSent[i].channel == channel + 1) { + gemCore.expression(i, value); + } + } + break; + default: + // @TODO Other program change process goes here + break; + } +} + +void sendProgramChange(char messageBytes[2]) { + uint8_t channel = messageBytes[0] & 0x0f; + uint8_t value = messageBytes[1] & 0x7f; + Wavetable::wave_t wave = Wavetable::waveDefList[value]; + + // Change channel wave type + ch.setWave(channel, wave); +} + +void sendPitchBend(char messageBytes[3]) { + // @TODO +} + +void allNoteOff(char messageBytes[3]) { + for (uint16_t i = 0; i < NUM_INSTRUMENT; i++) { + if (noteSent[i].channel == (messageBytes[0] & 0x0f) + 1) { + noteSent[i].noteOnMs = 0; + noteSent[i].channel = 0; + noteSent[i].noteNumber = 0; + noteSent[i].velocity = 0; + gemCore.noteOff(i); + } + } + + if (dumpMode == DUMP_INST || dumpMode == DUMP_INST_DETAIL) { + console.printf("%-12s", "All note off"); + dumpInstrumentList(); + } +} + +void sendSystemReset() { + 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; + } + + if (dumpMode == DUMP_INST || dumpMode == DUMP_INST_DETAIL) { + console.printf("%-12s", "System reset"); + dumpInstrumentList(); + } + for (uint8_t i = 0; i < NUM_INSTRUMENT; i++) { + gemCore.noteOff(i); + gemCore.volume(i, 100); + gemCore.expression(i, 127); + } +}