From d0224fce14cbf76128f8fa9a20f93c1102b46ce5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 6 Feb 2022 15:30:58 +0000 Subject: [PATCH] Cleanup input midi expander, add channel option --- .../{Expanders.cpp => ExpanderInputMIDI.cpp} | 216 +++++++++++++----- plugins/Cardinal/src/HostMIDI.cpp | 2 +- plugins/Makefile | 2 +- 3 files changed, 167 insertions(+), 53 deletions(-) rename plugins/Cardinal/src/{Expanders.cpp => ExpanderInputMIDI.cpp} (58%) diff --git a/plugins/Cardinal/src/Expanders.cpp b/plugins/Cardinal/src/ExpanderInputMIDI.cpp similarity index 58% rename from plugins/Cardinal/src/Expanders.cpp rename to plugins/Cardinal/src/ExpanderInputMIDI.cpp index dc416a2..a1753a4 100644 --- a/plugins/Cardinal/src/Expanders.cpp +++ b/plugins/Cardinal/src/ExpanderInputMIDI.cpp @@ -20,6 +20,16 @@ // -------------------------------------------------------------------------------------------------------------------- +/** + * This class contains a substantial amount of code from VCVRack's dsp/midi.hpp + * Copyright (C) 2016-2021 VCV. + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of + * the License, or (at your option) any later version. + */ + struct CardinalExpanderForInputMIDI : CardinalExpanderFromCVToCarlaMIDI { enum ParamIds { NUM_PARAMS @@ -27,10 +37,10 @@ struct CardinalExpanderForInputMIDI : CardinalExpanderFromCVToCarlaMIDI { enum InputIds { PITCH_INPUT, GATE_INPUT, - VEL_INPUT, - AFT_INPUT, - PW_INPUT, - MW_INPUT, + VELOCITY_INPUT, + AFTERTOUCH_INPUT, + PITCHBEND_INPUT, + MODWHEEL_INPUT, NUM_INPUTS }; enum OutputIds { @@ -45,112 +55,170 @@ struct CardinalExpanderForInputMIDI : CardinalExpanderFromCVToCarlaMIDI { int8_t notes[CHANNELS]; bool gates[CHANNELS]; int8_t keyPressures[CHANNELS]; - int8_t mw; - int16_t pw; + int8_t modwheel; + int16_t pitchbend; + uint8_t channel = 0; Module* lastConnectedModule = nullptr; - CardinalExpanderForInputMIDI() { + CardinalExpanderForInputMIDI() + { static_assert(NUM_INPUTS == kNumInputs, "Invalid input configuration"); config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); configInput(PITCH_INPUT, "1V/octave pitch"); configInput(GATE_INPUT, "Gate"); - configInput(VEL_INPUT, "Velocity"); - configInput(AFT_INPUT, "Aftertouch"); - configInput(PW_INPUT, "Pitchbend"); - configInput(MW_INPUT, "Mod wheel"); + configInput(VELOCITY_INPUT, "Velocity"); + configInput(AFTERTOUCH_INPUT, "Aftertouch"); + configInput(PITCHBEND_INPUT, "Pitchbend"); + configInput(MODWHEEL_INPUT, "Mod wheel"); onReset(); } /** Must be called before setNoteGate(). */ - void setVelocity(int8_t vel, int c) { + void setVelocity(int8_t vel, int c) + { vels[c] = vel; } - void setNoteGate(int8_t note, bool gate, int c) { - if (midiEventCount == MAX_MIDI_EVENTS) + void setNoteGate(int8_t note, bool gate, int c) + { + if (midiEventCount == MAX_MIDI_EVENTS || frame == UINT_MAX) return; - bool changedNote = gate && gates[c] && (note != notes[c]); - bool enabledGate = gate && !gates[c]; - bool disabledGate = !gate && gates[c]; - if (changedNote || disabledGate) { + + const bool changedNote = gate && gates[c] && (note != notes[c]); + const bool enabledGate = gate && !gates[c]; + const bool disabledGate = !gate && gates[c]; + + if (changedNote || disabledGate) + { // Note off NativeMidiEvent& m(midiEvents[midiEventCount++]); m.time = frame; m.port = 0; m.size = 3; - m.data[0] = 0x80; + m.data[0] = 0x80 | channel; m.data[1] = notes[c]; m.data[2] = vels[c]; } - if (changedNote || enabledGate) { + + if (changedNote || enabledGate) + { // Note on NativeMidiEvent& m(midiEvents[midiEventCount++]); m.time = frame; m.port = 0; m.size = 3; - m.data[0] = 0x90; + m.data[0] = 0x90 | channel; m.data[1] = note; m.data[2] = vels[c]; } + notes[c] = note; gates[c] = gate; } - void setKeyPressure(int8_t val, int c) { - if (keyPressures[c] == val || midiEventCount == MAX_MIDI_EVENTS) + void setKeyPressure(int8_t val, int c) + { + if (keyPressures[c] == val) return; + keyPressures[c] = val; + + if (midiEventCount == MAX_MIDI_EVENTS || frame == UINT_MAX) + return; + // Polyphonic key pressure NativeMidiEvent& m(midiEvents[midiEventCount++]); m.time = frame; m.port = 0; m.size = 3; - m.data[0] = 0xa0; + m.data[0] = 0xa0 | channel; m.data[1] = notes[c]; m.data[2] = val; } - void setModWheel(int8_t mw) { - if (this->mw == mw || midiEventCount == MAX_MIDI_EVENTS) + void setModWheel(int8_t modwheel) + { + if (this->modwheel == modwheel) return; - this->mw = mw; + + this->modwheel = modwheel; + + if (midiEventCount == MAX_MIDI_EVENTS || frame == UINT_MAX) + return; + // Modulation Wheel (CC1) NativeMidiEvent& m(midiEvents[midiEventCount++]); m.time = frame; m.port = 0; m.size = 3; - m.data[0] = 0xb0; + m.data[0] = 0xb0 | channel; m.data[1] = 1; - m.data[2] = mw; + m.data[2] = modwheel; } - void setPitchWheel(int16_t pw) { - if (this->pw == pw || midiEventCount == MAX_MIDI_EVENTS) + void setPitchbend(int16_t pitchbend) + { + if (this->pitchbend == pitchbend) + return; + + this->pitchbend = pitchbend; + + if (midiEventCount == MAX_MIDI_EVENTS || frame == UINT_MAX) return; - this->pw = pw; + // Pitch Wheel NativeMidiEvent& m(midiEvents[midiEventCount++]); m.time = frame; m.port = 0; m.size = 3; - m.data[0] = 0xe0; - m.data[1] = pw & 0x7f; - m.data[2] = (pw >> 7) & 0x7f; + m.data[0] = 0xe0 | channel; + m.data[1] = pitchbend & 0x7f; + m.data[2] = (pitchbend >> 7) & 0x7f; } - void onReset() override + void panic() { - for (uint c = 0; c < CHANNELS; c++) { + if (frame != UINT_MAX) + { + // Send all note off commands + for (int note = 0; note <= 127; ++note) + { + if (midiEventCount == MAX_MIDI_EVENTS) + break; + // Note off + NativeMidiEvent& m(midiEvents[midiEventCount++]); + m.time = frame; + m.port = 0; + m.size = 3; + m.data[0] = 0x80 | channel; + m.data[1] = note; + m.data[2] = 0; + } + } + + reset(); + } + + void reset() + { + for (uint c = 0; c < CHANNELS; ++c) + { vels[c] = 100; notes[c] = 60; gates[c] = false; keyPressures[c] = -1; } - mw = -1; - pw = 0x2000; + modwheel = -1; + pitchbend = 0x2000; midiEventCount = 0; frame = UINT_MAX; + } + + void onReset() override + { + reset(); + channel = 0; lastConnectedModule = nullptr; } @@ -178,7 +246,7 @@ struct CardinalExpanderForInputMIDI : CardinalExpanderFromCVToCarlaMIDI { return; for (int c = 0; c < inputs[PITCH_INPUT].getChannels(); c++) { - int vel = (int) std::round(inputs[VEL_INPUT].getNormalPolyVoltage(10.f * 100 / 127, c) / 10.f * 127); + int vel = (int) std::round(inputs[VELOCITY_INPUT].getNormalPolyVoltage(10.f * 100 / 127, c) / 10.f * 127); vel = clamp(vel, 0, 127); setVelocity(vel, c); @@ -187,21 +255,36 @@ struct CardinalExpanderForInputMIDI : CardinalExpanderFromCVToCarlaMIDI { bool gate = inputs[GATE_INPUT].getPolyVoltage(c) >= 1.f; setNoteGate(note, gate, c); - int aft = (int) std::round(inputs[AFT_INPUT].getPolyVoltage(c) / 10.f * 127); + int aft = (int) std::round(inputs[AFTERTOUCH_INPUT].getPolyVoltage(c) / 10.f * 127); aft = clamp(aft, 0, 127); setKeyPressure(aft, c); } - int pw = (int) std::round((inputs[PW_INPUT].getVoltage() + 5.f) / 10.f * 0x4000); - pw = clamp(pw, 0, 0x3fff); - setPitchWheel(pw); + int pitchbend = (int) std::round((inputs[PITCHBEND_INPUT].getVoltage() + 5.f) / 10.f * 16383); + pitchbend = clamp(pitchbend, 0, 16383); + setPitchbend(pitchbend); - int mw = (int) std::round(inputs[MW_INPUT].getVoltage() / 10.f * 127); - mw = clamp(mw, 0, 127); - setModWheel(mw); + int modwheel = (int) std::round(inputs[MODWHEEL_INPUT].getVoltage() / 10.f * 127); + modwheel = clamp(modwheel, 0, 127); + setModWheel(modwheel); ++frame; } + + json_t* dataToJson() override + { + json_t* const rootJ = json_object(); + DISTRHO_SAFE_ASSERT_RETURN(rootJ != nullptr, nullptr); + + json_object_set_new(rootJ, "channel", json_integer(channel)); + return rootJ; + } + + void dataFromJson(json_t* const rootJ) override + { + if (json_t* const channelJ = json_object_get(rootJ, "channel")) + channel = json_integer_value(channelJ) & 0x0F; + } }; // -------------------------------------------------------------------------------------------------------------------- @@ -211,16 +294,19 @@ struct CardinalExpanderForInputMIDIWidget : ModuleWidgetWith3HP { static constexpr const float startY = 90.0f; static constexpr const float padding = 49.0f; - CardinalExpanderForInputMIDIWidget(CardinalExpanderForInputMIDI* const module) + CardinalExpanderForInputMIDI* const module; + + CardinalExpanderForInputMIDIWidget(CardinalExpanderForInputMIDI* const m) + : module(m) { - setModule(module); + setModule(m); setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/ExpanderMIDI.svg"))); addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); for (int i=0; i(Vec(startX + 4.0f, startY + padding * i), module, i)); + addInput(createInput(Vec(startX + 4.0f, startY + padding * i), m, i)); } void draw(const DrawArgs& args) override @@ -261,7 +347,35 @@ struct CardinalExpanderForInputMIDIWidget : ModuleWidgetWith3HP { nvgText(args.vg, box.size.x * 0.666f, startY + padding * 4 - 4.0f, "Pb", nullptr); nvgText(args.vg, box.size.x * 0.666f, startY + padding * 5 - 4.0f, "MW", nullptr); - ModuleWidgetWithSideScrews::draw(args); + ModuleWidgetWith3HP::draw(args); + } + + void appendContextMenu(Menu* const menu) override + { + menu->addChild(new MenuSeparator); + + struct ChannelItem : MenuItem { + CardinalExpanderForInputMIDI* module; + Menu* createChildMenu() override { + Menu* menu = new Menu; + for (uint8_t c = 0; c < 16; c++) { + menu->addChild(createCheckMenuItem(string::f("%d", c+1), "", + [=]() {return module->channel == c;}, + [=]() {module->channel = c;} + )); + } + return menu; + } + }; + ChannelItem* const channelItem = new ChannelItem; + channelItem->text = "MIDI channel"; + channelItem->rightText = string::f("%d", module->channel+1) + " " + RIGHT_ARROW; + channelItem->module = module; + menu->addChild(channelItem); + + menu->addChild(createMenuItem("Panic", "", + [=]() { module->panic(); } + )); } }; diff --git a/plugins/Cardinal/src/HostMIDI.cpp b/plugins/Cardinal/src/HostMIDI.cpp index 067ea8f..62ad2bf 100644 --- a/plugins/Cardinal/src/HostMIDI.cpp +++ b/plugins/Cardinal/src/HostMIDI.cpp @@ -635,7 +635,7 @@ struct HostMIDI : Module { return rootJ; } - void dataFromJson(json_t* rootJ) override + void dataFromJson(json_t* const rootJ) override { if (json_t* const smoothJ = json_object_get(rootJ, "smooth")) midiInput.smooth = json_boolean_value(smoothJ); diff --git a/plugins/Makefile b/plugins/Makefile index 9f4312e..78f15f6 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -187,7 +187,7 @@ PLUGIN_FILES = plugins.cpp # Cardinal (built-in) PLUGIN_FILES += Cardinal/src/Blank.cpp -PLUGIN_FILES += Cardinal/src/Expanders.cpp +PLUGIN_FILES += Cardinal/src/ExpanderInputMIDI.cpp PLUGIN_FILES += Cardinal/src/glBars.cpp PLUGIN_FILES += Cardinal/src/HostAudio.cpp PLUGIN_FILES += Cardinal/src/HostCV.cpp