|
|
@@ -2,6 +2,8 @@ |
|
|
|
#include "midi.hpp" |
|
|
|
#include "dsp/filter.hpp" |
|
|
|
|
|
|
|
#include <algorithm> |
|
|
|
|
|
|
|
|
|
|
|
struct MidiNoteData { |
|
|
|
uint8_t velocity; |
|
|
@@ -41,62 +43,78 @@ struct MIDIToCVInterface : Module { |
|
|
|
ExponentialFilter modFilter; |
|
|
|
uint16_t pitch = 0; |
|
|
|
ExponentialFilter pitchFilter; |
|
|
|
bool pedal = false; |
|
|
|
|
|
|
|
MidiNoteData noteData[128]; |
|
|
|
std::list<uint8_t> heldNotes; |
|
|
|
uint8_t lastNote = 60; |
|
|
|
bool gate = false; |
|
|
|
|
|
|
|
MIDIToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} |
|
|
|
|
|
|
|
void onReset() override { |
|
|
|
heldNotes.clear(); |
|
|
|
pedal = false; |
|
|
|
} |
|
|
|
|
|
|
|
void pressNote(uint8_t note) { |
|
|
|
// Remove existing similar note |
|
|
|
auto it = std::find(heldNotes.begin(), heldNotes.end(), note); |
|
|
|
if (it != heldNotes.end()) |
|
|
|
heldNotes.erase(it); |
|
|
|
// Push note |
|
|
|
heldNotes.push_back(note); |
|
|
|
lastNote = note; |
|
|
|
gate = true; |
|
|
|
} |
|
|
|
|
|
|
|
void releaseNote(uint8_t note) { |
|
|
|
// Remove the note |
|
|
|
if (note < 128) { |
|
|
|
auto it = std::find(heldNotes.begin(), heldNotes.end(), note); |
|
|
|
if (it != heldNotes.end()) |
|
|
|
heldNotes.erase(it); |
|
|
|
} |
|
|
|
// Hold note if pedal is pressed |
|
|
|
if (pedal) |
|
|
|
return; |
|
|
|
// Set last note |
|
|
|
if (!heldNotes.empty()) { |
|
|
|
auto it2 = heldNotes.end(); |
|
|
|
it2--; |
|
|
|
lastNote = *it2; |
|
|
|
gate = true; |
|
|
|
} |
|
|
|
else { |
|
|
|
gate = false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void pressPedal() { |
|
|
|
pedal = true; |
|
|
|
} |
|
|
|
|
|
|
|
void releasePedal() { |
|
|
|
pedal = false; |
|
|
|
releaseNote(255); |
|
|
|
} |
|
|
|
|
|
|
|
void step() override { |
|
|
|
MidiMessage msg; |
|
|
|
while (midiInput.shift(&msg)) { |
|
|
|
processMessage(msg); |
|
|
|
} |
|
|
|
|
|
|
|
outputs[CV_OUTPUT].value = (lastNote - 60) / 12.f; |
|
|
|
outputs[GATE_OUTPUT].value = gate ? 10.f : 0.f; |
|
|
|
outputs[VELOCITY_OUTPUT].value = rescale(noteData[lastNote].velocity, 0, 127, 0.f, 10.f); |
|
|
|
modFilter.lambda = 100.f * engineGetSampleTime(); |
|
|
|
outputs[MOD_OUTPUT].value = modFilter.process(rescale(mod, 0, 127, 0.f, 10.f)); |
|
|
|
|
|
|
|
pitchFilter.lambda = 100.f * engineGetSampleTime(); |
|
|
|
outputs[PITCH_OUTPUT].value = pitchFilter.process(rescale(pitch, 0, 16384, -5.f, 5.f)); |
|
|
|
|
|
|
|
/* |
|
|
|
if (isPortOpen()) { |
|
|
|
std::vector<unsigned char> message; |
|
|
|
|
|
|
|
// midiIn->getMessage returns empty vector if there are no messages in the queue |
|
|
|
getMessage(&message); |
|
|
|
if (message.size() > 0) { |
|
|
|
processMidi(message); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
outputs[PITCH_OUTPUT].value = ((note - 60)) / 12.0; |
|
|
|
|
|
|
|
if (resetTrigger.process(params[RESET_PARAM].value)) { |
|
|
|
resetMidi(); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
lights[RESET_LIGHT].value -= lights[RESET_LIGHT].value / 0.55 / engineGetSampleRate(); // fade out light |
|
|
|
|
|
|
|
outputs[GATE_OUTPUT].value = gate ? 10.0 : 0.0; |
|
|
|
outputs[VELOCITY_OUTPUT].value = vel / 127.0 * 10.0; |
|
|
|
|
|
|
|
int steps = int(engineGetSampleRate() / 32); |
|
|
|
|
|
|
|
if (mod.changed) { |
|
|
|
mod.tSmooth.set(outputs[MOD_OUTPUT].value, (mod.val / 127.0 * 10.0), steps); |
|
|
|
mod.changed = false; |
|
|
|
} |
|
|
|
outputs[MOD_OUTPUT].value = mod.tSmooth.next(); |
|
|
|
|
|
|
|
if (pitchWheel.changed) { |
|
|
|
pitchWheel.tSmooth.set(outputs[PITCHWHEEL_OUTPUT].value, (pitchWheel.val - 64) / 64.0 * 10.0, steps); |
|
|
|
pitchWheel.changed = false; |
|
|
|
} |
|
|
|
outputs[PITCHWHEEL_OUTPUT].value = pitchWheel.tSmooth.next(); |
|
|
|
|
|
|
|
outputs[CHANNEL_AFTERTOUCH_OUTPUT].value = afterTouch.val / 127.0 * 10.0; |
|
|
|
*/ |
|
|
|
outputs[AFTERTOUCH_OUTPUT].value = rescale(noteData[lastNote].aftertouch, 0, 127, 0.f, 10.f); |
|
|
|
} |
|
|
|
|
|
|
|
void processMessage(MidiMessage msg) { |
|
|
@@ -105,21 +123,25 @@ struct MIDIToCVInterface : Module { |
|
|
|
switch (msg.status()) { |
|
|
|
// note off |
|
|
|
case 0x8: { |
|
|
|
// releaseNote(msg.data1); |
|
|
|
releaseNote(msg.data1); |
|
|
|
} break; |
|
|
|
// note on |
|
|
|
case 0x9: { |
|
|
|
if (msg.data2 > 0) { |
|
|
|
uint8_t note = msg.data1 & 0x7f; |
|
|
|
noteData[note].velocity = msg.data2; |
|
|
|
noteData[note].aftertouch = 0; |
|
|
|
// pressNote(msg.data1); |
|
|
|
pressNote(msg.data1); |
|
|
|
} |
|
|
|
else { |
|
|
|
// For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released. |
|
|
|
// releaseNote(msg.data1); |
|
|
|
releaseNote(msg.data1); |
|
|
|
} |
|
|
|
} break; |
|
|
|
// channel aftertouch |
|
|
|
case 0xa: { |
|
|
|
uint8_t note = msg.data1 & 0x7f; |
|
|
|
noteData[note].aftertouch = msg.data2; |
|
|
|
} break; |
|
|
|
// cc |
|
|
|
case 0xb: { |
|
|
|
processCC(msg); |
|
|
@@ -128,11 +150,6 @@ struct MIDIToCVInterface : Module { |
|
|
|
case 0xe: { |
|
|
|
pitch = msg.data2 * 128 + msg.data1; |
|
|
|
} break; |
|
|
|
// channel aftertouch |
|
|
|
case 0xd: { |
|
|
|
// TODO This is pressure, not aftertouch. |
|
|
|
// aftertouch = rescale(msg.data1, 0.f, 128.f, 0.f, 10.f); |
|
|
|
} break; |
|
|
|
case 0xf: { |
|
|
|
processSystem(msg); |
|
|
|
} break; |
|
|
@@ -148,10 +165,10 @@ struct MIDIToCVInterface : Module { |
|
|
|
} break; |
|
|
|
// sustain |
|
|
|
case 0x40: { |
|
|
|
// pedal = (msg.data2 >= 64); |
|
|
|
// if (!pedal) { |
|
|
|
// releaseNote(-1); |
|
|
|
// } |
|
|
|
if (msg.data2 >= 64) |
|
|
|
pressPedal(); |
|
|
|
else |
|
|
|
releasePedal(); |
|
|
|
} break; |
|
|
|
default: break; |
|
|
|
} |
|
|
@@ -188,40 +205,6 @@ struct MIDIToCVInterface : Module { |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
void MIDIToCVInterface::pressNote(int note) { |
|
|
|
// Remove existing similar note |
|
|
|
auto it = std::find(notes.begin(), notes.end(), note); |
|
|
|
if (it != notes.end()) |
|
|
|
notes.erase(it); |
|
|
|
// Push note |
|
|
|
notes.push_back(note); |
|
|
|
this->note = note; |
|
|
|
gate = true; |
|
|
|
} |
|
|
|
|
|
|
|
void MIDIToCVInterface::releaseNote(int note) { |
|
|
|
// Remove the note |
|
|
|
auto it = std::find(notes.begin(), notes.end(), note); |
|
|
|
if (it != notes.end()) |
|
|
|
notes.erase(it); |
|
|
|
|
|
|
|
if (pedal) { |
|
|
|
// Don't release if pedal is held |
|
|
|
gate = true; |
|
|
|
} else if (!notes.empty()) { |
|
|
|
// Play previous note |
|
|
|
auto it2 = notes.end(); |
|
|
|
it2--; |
|
|
|
this->note = *it2; |
|
|
|
gate = true; |
|
|
|
} else { |
|
|
|
gate = false; |
|
|
|
} |
|
|
|
} |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
struct MIDIToCVInterfaceWidget : ModuleWidget { |
|
|
|
MIDIToCVInterfaceWidget(MIDIToCVInterface *module) : ModuleWidget(module) { |
|
|
|
setPanel(SVG::load(assetGlobal("res/Core/MIDIToCVInterface.svg"))); |
|
|
|