From 4bdf07f8384854ff432b1c08fcb60745880f8068 Mon Sep 17 00:00:00 2001 From: ben Date: Mon, 23 Oct 2017 23:01:48 +0200 Subject: [PATCH 1/3] Basic implementation of quad midi to cv --- src/core/MidiIO.hpp | 5 + src/core/QuadMidiToCV.cpp | 415 ++++++++++++++++++++++++++++++++++++++ src/core/core.cpp | 3 +- 3 files changed, 422 insertions(+), 1 deletion(-) create mode 100644 src/core/QuadMidiToCV.cpp diff --git a/src/core/MidiIO.hpp b/src/core/MidiIO.hpp index c88287e4..65408c6b 100644 --- a/src/core/MidiIO.hpp +++ b/src/core/MidiIO.hpp @@ -152,3 +152,8 @@ struct MIDITriggerToCVWidget : ModuleWidget { void step(); }; +struct QuadMidiToCVWidget : ModuleWidget { + QuadMidiToCVWidget(); + + void step(); +}; diff --git a/src/core/QuadMidiToCV.cpp b/src/core/QuadMidiToCV.cpp new file mode 100644 index 00000000..9d78c06e --- /dev/null +++ b/src/core/QuadMidiToCV.cpp @@ -0,0 +1,415 @@ +#include +#include +#include "rtmidi/RtMidi.h" +#include "core.hpp" +#include "MidiIO.hpp" +#include "dsp/digital.hpp" + +struct MidiKey { + int pitch = 60; + int at = 0; // aftertouch + int vel = 0; // velocity + int retriggerC = 0; + bool gate = false; +}; + +struct QuadMIDIToCVInterface : MidiIO, Module { + enum ParamIds { + RESET_PARAM, + NUM_PARAMS + }; + enum InputIds { + NUM_INPUTS + }; + enum OutputIds { + PITCH_OUTPUT = 0, + GATE_OUTPUT = 4, + VELOCITY_OUTPUT = 8, + AT_OUTPUT = 12, + NUM_OUTPUTS = 16 + }; + + enum Modes { + ROTATE, + RESET, + REASIGN + }; + + bool pedal = false; + + int mode = REASIGN; + + int getMode() const; + + void setMode(int mode); + + MidiKey activeKeys[4]; + std::list open; + + SchmittTrigger resetTrigger; + float resetLight = 0.0; + + QuadMIDIToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { + + } + + ~QuadMIDIToCVInterface() { + }; + + void step(); + + void processMidi(std::vector msg); + + json_t *toJson() { + json_t *rootJ = json_object(); + addBaseJson(rootJ); + return rootJ; + } + + void fromJson(json_t *rootJ) { + baseFromJson(rootJ); + } + + void reset() { + resetMidi(); + } + + void resetMidi(); + +}; + +void QuadMIDIToCVInterface::resetMidi() { + + for (int i = 0; i < 4; i++) { + outputs[GATE_OUTPUT + i].value = 0.0; + activeKeys[i].gate = false; + activeKeys[i].vel = 0; + activeKeys[i].at = 0; + } + + open.clear(); + + pedal = false; + resetLight = 1.0; +} + +void QuadMIDIToCVInterface::step() { + static float sampleRate = engineGetSampleRate(); + static int msgsProcessed = 0; + + if (isPortOpen()) { + std::vector message; + + // midiIn->getMessage returns empty vector if there are no messages in the queue + // NOTE: For the quadmidi we will process max 4 midi messages per step to avoid + // problems with parallel input. + getMessage(&message); + while (msgsProcessed < 4 && message.size() > 0) { + processMidi(message); + getMessage(&message); + msgsProcessed++; + } + msgsProcessed = 0; + } + + + for (int i = 0; i < 4; i++) { + outputs[GATE_OUTPUT + i].value = activeKeys[i].gate ? 10.0 : 0; + outputs[PITCH_OUTPUT + i].value = (activeKeys[i].pitch - 60) / 12.0; + outputs[VELOCITY_OUTPUT + i].value = activeKeys[i].vel / 127.0 * 10.0; + outputs[AT_OUTPUT + i].value = activeKeys[i].at / 127.0 * 10.0; + } + + if (resetTrigger.process(params[RESET_PARAM].value)) { + resetMidi(); + return; + } + + if (resetLight > 0) { + resetLight -= resetLight / 0.55 / sampleRate; // fade out light + } + +} + + +void QuadMIDIToCVInterface::processMidi(std::vector msg) { + int channel = msg[0] & 0xf; + int status = (msg[0] >> 4) & 0xf; + int data1 = msg[1]; + int data2 = msg[2]; + + static int gate; + + // Filter channels + if (this->channel >= 0 && this->channel != channel) + return; + + switch (status) { + // note off + case 0x8: { + gate = false; + } + break; + case 0x9: // note on + if (data2 > 0) { + gate = true; + } else { + // For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released. + gate = false; + } + break; + case 0xa: // channel aftertouch + for (int i = 0; i < 4; i++) { + if (activeKeys[i].pitch == data1) { + activeKeys[i].at = data2; + } + } + return; + case 0xb: // cc + if (data1 == 0x40) { // pedal + pedal = (data2 >= 64); + if (!pedal) { + for (int i = 0; i < 4; i++) { + activeKeys[i].gate = false; + open.push_back(i); + } + } + } + return; + default: + return; + } + + if (!pedal && !gate) { + for (int i = 0; i < 4; i++) { + if (activeKeys[i].pitch == data1) { + activeKeys[i].gate = false; + activeKeys[i].vel = data2; + open.push_front(i); + } + } + return; + } + + switch (mode) { + case RESET: + case REASIGN: + for (int i = 0; i < 4; i++) { + if (activeKeys[i].gate == false && std::find(open.begin(), open.end(), i) == open.end()) { + open.push_back(i); + } + } + + if (open.size() == 4) { + open.sort(); + } + + open.push_back(open.front()); + + break; + case ROTATE: + if (open.empty()) { + for (int i = 0; i < 4; i++) { + open.push_back(i); + } + } else { + open.sort(); + } + break; + default: + fprintf(stderr, "No mode selected?!\n"); + } + + activeKeys[open. + + front() + + ]. + gate = true; + activeKeys[open. + + front() + + ]. + pitch = data1; + activeKeys[open. + + front() + + ]. + vel = data2; + fprintf(stderr, + "Using No: %d\n", open. + + front() + + ); + open. + + pop_front(); + + return; + + +} + +int QuadMIDIToCVInterface::getMode() const { + return mode; +} + +void QuadMIDIToCVInterface::setMode(int mode) { + resetMidi(); + QuadMIDIToCVInterface::mode = mode; +} + +struct ModeItem : MenuItem { + int mode; + QuadMIDIToCVInterface *module; + + void onAction() { + module->setMode(mode); + } +}; + +struct ModeChoice : ChoiceButton { + QuadMIDIToCVInterface *module; + const std::vector modeNames = {"ROTATE", "RESET", "REASSIGN"}; + + + void onAction() { + Menu *menu = gScene->createMenu(); + menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); + menu->box.size.x = box.size.x; + + for (unsigned long i = 0; i < modeNames.size(); i++) { + ModeItem *modeItem = new ModeItem(); + modeItem->mode = i; + modeItem->module = module; + modeItem->text = modeNames[i]; + menu->pushChild(modeItem); + } + } + + void step() { + text = modeNames[module->getMode()]; + } +}; + + +QuadMidiToCVWidget::QuadMidiToCVWidget() { + QuadMIDIToCVInterface *module = new QuadMIDIToCVInterface(); + setModule(module); + box.size = Vec(15 * 16, 380); + + { + Panel *panel = new LightPanel(); + panel->box.size = box.size; + addChild(panel); + } + + float margin = 5; + float labelHeight = 15; + float yPos = margin; + + addChild(createScrew(Vec(15, 0))); + addChild(createScrew(Vec(box.size.x - 30, 0))); + addChild(createScrew(Vec(15, 365))); + addChild(createScrew(Vec(box.size.x - 30, 365))); + + { + Label *label = new Label(); + label->box.pos = Vec(box.size.x - margin - 12 * 15, margin); + label->text = "Quad MIDI to CV"; + addChild(label); + yPos = labelHeight * 2; + } + + addParam(createParam(Vec(12 * 15, labelHeight), module, QuadMIDIToCVInterface::RESET_PARAM, 0.0, 1.0, + 0.0)); + addChild(createValueLight>(Vec(12 * 15 + 5, labelHeight + 5), &module->resetLight)); + { + Label *label = new Label(); + label->box.pos = Vec(margin, yPos); + label->text = "MIDI Interface"; + addChild(label); + yPos += labelHeight + margin; + + MidiChoice *midiChoice = new MidiChoice(); + midiChoice->midiModule = dynamic_cast(module); + midiChoice->box.pos = Vec(margin, yPos); + midiChoice->box.size.x = box.size.x - 10; + addChild(midiChoice); + yPos += midiChoice->box.size.y + margin; + } + + { + Label *label = new Label(); + label->box.pos = Vec(margin, yPos); + label->text = "Channel"; + addChild(label); + yPos += labelHeight + margin; + + ChannelChoice *channelChoice = new ChannelChoice(); + channelChoice->midiModule = dynamic_cast(module); + channelChoice->box.pos = Vec(margin, yPos); + channelChoice->box.size.x = box.size.x - 10; + addChild(channelChoice); + yPos += channelChoice->box.size.y + margin; + } + + { + Label *label = new Label(); + label->box.pos = Vec(margin, yPos); + label->text = "Mode"; + addChild(label); + yPos += labelHeight + margin; + + ModeChoice *modeChoice = new ModeChoice(); + modeChoice->module = module; + modeChoice->box.pos = Vec(margin, yPos); + modeChoice->box.size.x = box.size.x - 10; + addChild(modeChoice); + yPos += modeChoice->box.size.y + margin + 15; + } + + { + Label *label = new Label(); + label->box.pos = Vec(margin, yPos); + label->text = "1V/Oct"; + addChild(label); + } + { + Label *label = new Label(); + label->box.pos = Vec(67, yPos); + label->text = "Gate"; + addChild(label); + } + { + Label *label = new Label(); + label->box.pos = Vec(133, yPos); + label->text = "Vel"; + addChild(label); + } + { + Label *label = new Label(); + label->box.pos = Vec(195, yPos); + label->text = "At"; + addChild(label); + } + + yPos += labelHeight + margin; + for (int i = 0; i < 4; i++) { + addOutput(createOutput(Vec(0 * (63) + 15, yPos + 5), module, i)); + addOutput(createOutput(Vec(1 * (63) + 10, yPos + 5), module, i + 4)); + addOutput(createOutput(Vec(2 * (63) + 10, yPos + 5), module, i + 8)); + addOutput(createOutput(Vec(3 * (63) + 5, yPos + 5), module, i + 12)); + yPos += 40; + } + + +} + +void QuadMidiToCVWidget::step() { + + ModuleWidget::step(); +} diff --git a/src/core/core.cpp b/src/core/core.cpp index a8192064..86f958fd 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -1,7 +1,6 @@ #include "core.hpp" #include "MidiIO.hpp" - void init(rack::Plugin *p) { p->slug = "Core"; p->addModel(createModel("Core", "Core", "AudioInterface", "Audio Interface")); @@ -9,6 +8,8 @@ void init(rack::Plugin *p) { p->addModel(createModel("Core", "Core", "MIDICCToCVInterface", "MIDI CC-to-CV Interface")); p->addModel(createModel("Core", "Core", "MIDIClockToCVInterface", "MIDI Clock-to-CV Interface")); p->addModel(createModel("Core", "Core", "MIDITriggerToCVInterface", "MIDI Trigger-to-CV Interface")); + p->addModel(createModel("Core", "Core", "QuadMIDIToCVInterface", "Quad MIDI-to-CV Interface")); + // p->addModel(createModel("Core", "Core", "Bridge", "Bridge")); p->addModel(createModel("Core", "Core", "Blank", "Blank")); p->addModel(createModel("Core", "Core", "Notes", "Notes")); From bdc0479b4c24cbbac15c795bd9fef57652bf1755 Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 24 Oct 2017 00:32:30 +0200 Subject: [PATCH 2/3] fix/implement modes --- src/core/QuadMidiToCV.cpp | 106 +++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 60 deletions(-) diff --git a/src/core/QuadMidiToCV.cpp b/src/core/QuadMidiToCV.cpp index 9d78c06e..5e488923 100644 --- a/src/core/QuadMidiToCV.cpp +++ b/src/core/QuadMidiToCV.cpp @@ -32,12 +32,12 @@ struct QuadMIDIToCVInterface : MidiIO, Module { enum Modes { ROTATE, RESET, - REASIGN + REASSIGN }; bool pedal = false; - int mode = REASIGN; + int mode = REASSIGN; int getMode() const; @@ -185,69 +185,50 @@ void QuadMIDIToCVInterface::processMidi(std::vector msg) { if (activeKeys[i].pitch == data1) { activeKeys[i].gate = false; activeKeys[i].vel = data2; + if (std::find(open.begin(), open.end(), i) != open.end()) { + open.remove(i); + } open.push_front(i); } } return; } + if (open.empty()) { + open.clear(); + for (int i = 0; i < 4; i++) { + open.push_back(i); + } + } + + + if (!activeKeys[0].gate && !activeKeys[1].gate && + !activeKeys[2].gate && !activeKeys[3].gate) { + open.sort(); + } + switch (mode) { case RESET: - case REASIGN: - for (int i = 0; i < 4; i++) { - if (activeKeys[i].gate == false && std::find(open.begin(), open.end(), i) == open.end()) { + if (open.size() == 4 ) { + for (int i = 0; i < 4; i++) { + activeKeys[i].gate = false; open.push_back(i); } } - - if (open.size() == 4) { - open.sort(); - } - + break; + case REASSIGN: open.push_back(open.front()); - break; case ROTATE: - if (open.empty()) { - for (int i = 0; i < 4; i++) { - open.push_back(i); - } - } else { - open.sort(); - } break; default: fprintf(stderr, "No mode selected?!\n"); } - activeKeys[open. - - front() - - ]. - gate = true; - activeKeys[open. - - front() - - ]. - pitch = data1; - activeKeys[open. - - front() - - ]. - vel = data2; - fprintf(stderr, - "Using No: %d\n", open. - - front() - - ); - open. - - pop_front(); - + activeKeys[open.front()].gate = true; + activeKeys[open.front()].pitch = data1; + activeKeys[open.front()].vel = data2; + open.pop_front(); return; @@ -273,7 +254,7 @@ struct ModeItem : MenuItem { struct ModeChoice : ChoiceButton { QuadMIDIToCVInterface *module; - const std::vector modeNames = {"ROTATE", "RESET", "REASSIGN"}; + const std::vector modeNames = {"ROTATE", "RESET", "REASSIGN"}; void onAction() { @@ -374,35 +355,40 @@ QuadMidiToCVWidget::QuadMidiToCVWidget() { { Label *label = new Label(); - label->box.pos = Vec(margin, yPos); - label->text = "1V/Oct"; + label->box.pos = Vec(84, yPos); + label->text = "1"; addChild(label); } { Label *label = new Label(); - label->box.pos = Vec(67, yPos); - label->text = "Gate"; + label->box.pos = Vec(125, yPos); + label->text = "2"; addChild(label); } { Label *label = new Label(); - label->box.pos = Vec(133, yPos); - label->text = "Vel"; + label->box.pos = Vec(164, yPos); + label->text = "3"; addChild(label); } { Label *label = new Label(); - label->box.pos = Vec(195, yPos); - label->text = "At"; + label->box.pos = Vec(203, yPos); + label->text = "4"; addChild(label); } + std::string labels[4] = {"1V/Oct", "Gate", "Vel", "At"}; - yPos += labelHeight + margin; + yPos += labelHeight + margin * 2; for (int i = 0; i < 4; i++) { - addOutput(createOutput(Vec(0 * (63) + 15, yPos + 5), module, i)); - addOutput(createOutput(Vec(1 * (63) + 10, yPos + 5), module, i + 4)); - addOutput(createOutput(Vec(2 * (63) + 10, yPos + 5), module, i + 8)); - addOutput(createOutput(Vec(3 * (63) + 5, yPos + 5), module, i + 12)); + Label *label = new Label(); + label->box.pos = Vec(margin, yPos); + label->text = labels[i]; + addChild(label); + addOutput(createOutput(Vec(2 * (40), yPos - 5), module, i * 4)); + addOutput(createOutput(Vec(3 * (40), yPos - 5), module, i * 4 + 1)); + addOutput(createOutput(Vec(4 * (40), yPos - 5), module, i * 4 + 2)); + addOutput(createOutput(Vec(5 * (40), yPos - 5), module, i * 4 + 3)); yPos += 40; } From df94ca7ac22b09e0bb7120be5de78268bddf3de4 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 23 Oct 2017 18:45:19 -0400 Subject: [PATCH 3/3] Fixed labels for Quad MIDI interface --- src/core/QuadMidiToCV.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/QuadMidiToCV.cpp b/src/core/QuadMidiToCV.cpp index 5e488923..5f27f7b3 100644 --- a/src/core/QuadMidiToCV.cpp +++ b/src/core/QuadMidiToCV.cpp @@ -377,7 +377,7 @@ QuadMidiToCVWidget::QuadMidiToCVWidget() { label->text = "4"; addChild(label); } - std::string labels[4] = {"1V/Oct", "Gate", "Vel", "At"}; + std::string labels[4] = {"1V/oct", "Gate", "Velocity", "Aftertouch"}; yPos += labelHeight + margin * 2; for (int i = 0; i < 4; i++) {