From 13fe861ddd63e221d5fefdb91a057f6e0b51aa9e Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 10 Mar 2018 02:23:19 -0500 Subject: [PATCH] Add CV functionality to MIDI to CV interface --- include/util/common.hpp | 2 +- src/Core/MIDIToCVInterface.cpp | 155 +++++++++++++++------------------ src/audio.cpp | 2 +- src/midi.cpp | 13 ++- 4 files changed, 82 insertions(+), 90 deletions(-) diff --git a/include/util/common.hpp b/include/util/common.hpp index d061f416..4f392459 100644 --- a/include/util/common.hpp +++ b/include/util/common.hpp @@ -74,7 +74,7 @@ T *construct(F f, V v, Args... args) { return o; } -/** Delays code until the scope is destructed +/** Defers code until the scope is destructed From http://www.gingerbill.org/article/defer-in-cpp.html Example: diff --git a/src/Core/MIDIToCVInterface.cpp b/src/Core/MIDIToCVInterface.cpp index cbd5405e..822b0637 100644 --- a/src/Core/MIDIToCVInterface.cpp +++ b/src/Core/MIDIToCVInterface.cpp @@ -2,6 +2,8 @@ #include "midi.hpp" #include "dsp/filter.hpp" +#include + 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 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 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"))); diff --git a/src/audio.cpp b/src/audio.cpp index 4d8aeebb..6cdb1112 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -23,7 +23,7 @@ std::vector AudioIO::getDrivers() { std::vector drivers; for (RtAudio::Api api : apis) drivers.push_back((int) api); - // Add Bridge fake driver + // Add fake Bridge driver drivers.push_back(BRIDGE_DRIVER); return drivers; } diff --git a/src/midi.cpp b/src/midi.cpp index 15eff369..1f32fe51 100644 --- a/src/midi.cpp +++ b/src/midi.cpp @@ -1,4 +1,8 @@ #include "midi.hpp" +#include "bridge.hpp" + + +#define BRIDGE_DRIVER -5000 namespace rack { @@ -20,7 +24,8 @@ std::vector MidiIO::getDrivers() { for (RtMidi::Api api : rtApis) { drivers.push_back((int) api); } - // drivers.push_back(BRIDGE_DRIVER) + // Add fake Bridge driver + drivers.push_back(BRIDGE_DRIVER); return drivers; } @@ -32,7 +37,7 @@ std::string MidiIO::getDriverName(int driver) { case RtMidi::UNIX_JACK: return "JACK"; case RtMidi::WINDOWS_MM: return "Windows MIDI"; case RtMidi::RTMIDI_DUMMY: return "Dummy MIDI"; - // case BRIDGE_DRIVER: return "Bridge"; + case BRIDGE_DRIVER: return "Bridge"; default: return "Unknown"; } } @@ -131,6 +136,10 @@ static void midiInputCallback(double timeStamp, std::vector *mess midiMessage.data1 = (*message)[1]; if (message->size() >= 3) midiMessage.data2 = (*message)[2]; + + // Filter channel + if (midiInput->channel >= 0 && (midiMessage.channel() != midiInput->channel)) + return; midiInput->onMessage(midiMessage); }