12-polyphonic "chiptune" MIDI synthesizer for LPC1768 (Standalone version)
Dependencies: ClockControl PowerControl mbed
events.cpp
- Committer:
- kayekss
- Date:
- 2014-12-09
- Revision:
- 4:b2423ad4b248
- Parent:
- 1:5f0c89bffec1
- Child:
- 5:7bc917d03bd6
File content as of revision 4:b2423ad4b248:
#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 { // @TODO Pitch bend sensitivity is fixed to 2 float pitchModifier = pow(2.0, 2.0 * ch.getPitchBend(channel) / (12 * 8192)); gemCore.setWave(targetInst, ch.getWave(channel)); gemCore.noteOn(targetInst, frequencyTable[noteNumber] * pitchModifier, 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: // 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]) { uint8_t channel = messageBytes[0] & 0x0f; int16_t value = (((messageBytes[2] & 0x7f) << 7) | (messageBytes[1] & 0x7f)) - 8192; // @TODO Pitch bend sensitivity is fixed to 2 float pitchModifier = pow(2.0, 2.0 * value / (12 * 8192)); // Register pitch bend value for succeeding notes ch.setPitchBend(channel, value); // Modify frequency of all existing notes in selected channel Instrument* instList = gemCore.getInstrumentList(); for (uint16_t i = 0; i < NUM_INSTRUMENT; i++) { if (noteSent[i].channel == channel + 1) { instList[i].setFrequency(frequencyTable[noteSent[i].noteNumber] * pitchModifier); } } } 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); } }