diff --git a/plugins/Cardinal/src/HostMIDI.cpp b/plugins/Cardinal/src/HostMIDI.cpp index 2af9721..d53ed31 100644 --- a/plugins/Cardinal/src/HostMIDI.cpp +++ b/plugins/Cardinal/src/HostMIDI.cpp @@ -82,6 +82,7 @@ struct HostMIDI : Module { uint32_t midiEventFrame; int64_t lastBlockFrame; bool wasPlaying; + uint8_t channel; // stuff from Rack bool smooth; @@ -138,6 +139,7 @@ struct HostMIDI : Module { midiEventFrame = 0; lastBlockFrame = -1; wasPlaying = false; + channel = 0; smooth = true; channels = 1; polyMode = ROTATE_MODE; @@ -194,7 +196,7 @@ struct HostMIDI : Module { } } - for (uint32_t i=0; i { CardinalPluginContext* const pcontext; + uint8_t channel = 0; dsp::Timer rateLimiterTimer; MidiOutput(CardinalPluginContext* const pc) @@ -516,7 +525,7 @@ struct HostMIDI : Module { void onMessage(const midi::Message& message) override { - pcontext->writeMidiMessage(message); + pcontext->writeMidiMessage(message, channel); } } midiOutput; @@ -555,6 +564,13 @@ struct HostMIDI : Module { configOutput(CONTINUE_OUTPUT, "Continue trigger"); } + void onReset() override + { + midiInput.reset(); + midiOutput.reset(); + midiOutput.channel = 0; + } + void process(const ProcessArgs& args) override { if (midiInput.process(args, outputs)) @@ -622,39 +638,48 @@ struct HostMIDI : Module { json_t* dataToJson() override { - json_t* rootJ = json_object(); + json_t* const rootJ = json_object(); + DISTRHO_SAFE_ASSERT_RETURN(rootJ != nullptr, nullptr); + json_object_set_new(rootJ, "smooth", json_boolean(midiInput.smooth)); json_object_set_new(rootJ, "channels", json_integer(midiInput.channels)); json_object_set_new(rootJ, "polyMode", json_integer(midiInput.polyMode)); + // Saving/restoring pitch and mod doesn't make much sense for MPE. - if (midiInput.polyMode != MidiInput::MPE_MODE) { + if (midiInput.polyMode != MidiInput::MPE_MODE) + { json_object_set_new(rootJ, "lastPitch", json_integer(midiInput.pws[0])); json_object_set_new(rootJ, "lastMod", json_integer(midiInput.mods[0])); } + + json_object_set_new(rootJ, "inputChannel", json_integer(midiInput.channel)); + json_object_set_new(rootJ, "outputChannel", json_integer(midiOutput.channel)); + return rootJ; } void dataFromJson(json_t* rootJ) override { - json_t* smoothJ = json_object_get(rootJ, "smooth"); - if (smoothJ) + if (json_t* const smoothJ = json_object_get(rootJ, "smooth")) midiInput.smooth = json_boolean_value(smoothJ); - json_t* channelsJ = json_object_get(rootJ, "channels"); - if (channelsJ) + if (json_t* const channelsJ = json_object_get(rootJ, "channels")) midiInput.setChannels(json_integer_value(channelsJ)); - json_t* polyModeJ = json_object_get(rootJ, "polyMode"); - if (polyModeJ) + if (json_t* const polyModeJ = json_object_get(rootJ, "polyMode")) midiInput.polyMode = (MidiInput::PolyMode) json_integer_value(polyModeJ); - json_t* lastPitchJ = json_object_get(rootJ, "lastPitch"); - if (lastPitchJ) + if (json_t* const lastPitchJ = json_object_get(rootJ, "lastPitch")) midiInput.pws[0] = json_integer_value(lastPitchJ); - json_t* lastModJ = json_object_get(rootJ, "lastMod"); - if (lastModJ) + if (json_t* const lastModJ = json_object_get(rootJ, "lastMod")) midiInput.mods[0] = json_integer_value(lastModJ); + + if (json_t* const inputChannelJ = json_object_get(rootJ, "inputChannel")) + midiInput.channel = json_integer_value(inputChannelJ); + + if (json_t* const outputChannelJ = json_object_get(rootJ, "outputChannel")) + midiOutput.channel = json_integer_value(outputChannelJ) & 0x0F; } }; @@ -742,12 +767,31 @@ struct HostMIDIWidget : ModuleWidget { void appendContextMenu(Menu* const menu) override { menu->addChild(new MenuSeparator); - menu->addChild(createMenuLabel("MIDI Input")); menu->addChild(createBoolPtrMenuItem("Smooth pitch/mod wheel", "", &module->midiInput.smooth)); - struct ChannelItem : MenuItem { + struct InputChannelItem : MenuItem { + HostMIDI* module; + Menu* createChildMenu() override { + Menu* menu = new Menu; + for (int c = 0; c <= 16; c++) { + menu->addChild(createCheckMenuItem((c == 0) ? "All" : string::f("%d", c), "", + [=]() {return module->midiInput.channel == c;}, + [=]() {module->midiInput.channel = c;} + )); + } + return menu; + } + }; + InputChannelItem* const inputChannelItem = new InputChannelItem; + inputChannelItem->text = "MIDI channel"; + inputChannelItem->rightText = (module->midiInput.channel ? string::f("%d", module->midiInput.channel) : "All") + + " " + RIGHT_ARROW; + inputChannelItem->module = module; + menu->addChild(inputChannelItem); + + struct PolyphonyChannelItem : MenuItem { HostMIDI* module; Menu* createChildMenu() override { Menu* menu = new Menu; @@ -760,11 +804,11 @@ struct HostMIDIWidget : ModuleWidget { return menu; } }; - ChannelItem* channelItem = new ChannelItem; - channelItem->text = "Polyphony channels"; - channelItem->rightText = string::f("%d", module->midiInput.channels) + " " + RIGHT_ARROW; - channelItem->module = module; - menu->addChild(channelItem); + PolyphonyChannelItem* const polyphonyChannelItem = new PolyphonyChannelItem; + polyphonyChannelItem->text = "Polyphony channels"; + polyphonyChannelItem->rightText = string::f("%d", module->midiInput.channels) + " " + RIGHT_ARROW; + polyphonyChannelItem->module = module; + menu->addChild(polyphonyChannelItem); menu->addChild(createIndexPtrSubmenuItem("Polyphony mode", { "Rotate", @@ -774,7 +818,28 @@ struct HostMIDIWidget : ModuleWidget { }, &module->midiInput.polyMode)); menu->addChild(new MenuSeparator); + menu->addChild(createMenuLabel("MIDI Output")); + + struct OutputChannelItem : MenuItem { + HostMIDI* module; + Menu* createChildMenu() override { + Menu* menu = new Menu; + for (uint8_t c = 0; c < 15; c++) { + menu->addChild(createCheckMenuItem(string::f("%d", c+1), "", + [=]() {return module->midiOutput.channel == c;}, + [=]() {module->midiOutput.channel = c;} + )); + } + return menu; + } + }; + OutputChannelItem* const outputChannelItem = new OutputChannelItem; + outputChannelItem->text = "MIDI channel"; + outputChannelItem->rightText = string::f("%d", module->midiOutput.channel+1) + " " + RIGHT_ARROW; + outputChannelItem->module = module; + menu->addChild(outputChannelItem); + menu->addChild(new MenuSeparator); menu->addChild(createMenuLabel("MIDI Input & Output")); menu->addChild(createMenuItem("Panic", "", diff --git a/plugins/Cardinal/src/plugincontext.hpp b/plugins/Cardinal/src/plugincontext.hpp index 712d563..2d0eeba 100644 --- a/plugins/Cardinal/src/plugincontext.hpp +++ b/plugins/Cardinal/src/plugincontext.hpp @@ -70,7 +70,7 @@ struct CardinalPluginContext : rack::Context { UI* ui; #endif CardinalPluginContext(Plugin* const p); - void writeMidiMessage(const rack::midi::Message& message); + void writeMidiMessage(const rack::midi::Message& message, uint8_t channel); #ifndef HEADLESS bool addIdleCallback(IdleCallback* cb) const; void removeIdleCallback(IdleCallback* cb) const; diff --git a/src/CardinalPlugin.cpp b/src/CardinalPlugin.cpp index 821ab58..6ca5a95 100644 --- a/src/CardinalPlugin.cpp +++ b/src/CardinalPlugin.cpp @@ -312,7 +312,7 @@ struct Initializer // ----------------------------------------------------------------------------------------------------------- -void CardinalPluginContext::writeMidiMessage(const rack::midi::Message& message) +void CardinalPluginContext::writeMidiMessage(const rack::midi::Message& message, const uint8_t channel) { const size_t size = message.bytes.size(); DISTRHO_SAFE_ASSERT_RETURN(size > 0,); @@ -367,6 +367,9 @@ void CardinalPluginContext::writeMidiMessage(const rack::midi::Message& message) std::memcpy(event.data, message.bytes.data(), event.size); + if (channel != 0 && event.data[0] < 0xF0) + event.data[0] |= channel & 0x0F; + plugin->writeMidiEvent(event); } @@ -831,34 +834,7 @@ protected: { const TimePosition& timePos(getTimePosition()); - bool reset = false; - - if (timePos.playing) - { - if (timePos.frame == 0 || fPreviousFrame + frames != timePos.frame) - reset = true; - - /* - if (! context->playing) - { - if (timePos.frame == 0) - { - singleTimeMidiEvent.data[0] = 0xFA; // start - sendSingleSimpleMidiMessage(singleTimeMidiEvent); - } - - singleTimeMidiEvent.data[0] = 0xFB; // continue - sendSingleSimpleMidiMessage(singleTimeMidiEvent); - } - */ - } - else if (context->playing) - { - /* - singleTimeMidiEvent.data[0] = 0xFC; // stop - sendSingleSimpleMidiMessage(singleTimeMidiEvent); - */ - } + const bool reset = timePos.playing && (timePos.frame == 0 || fPreviousFrame + frames != timePos.frame); context->playing = timePos.playing; context->bbtValid = timePos.bbt.valid; diff --git a/src/PluginContext.hpp b/src/PluginContext.hpp index d2a738e..6beab05 100644 --- a/src/PluginContext.hpp +++ b/src/PluginContext.hpp @@ -107,7 +107,7 @@ struct CardinalPluginContext : rack::Context { std::memset(parameters, 0, sizeof(parameters)); } - void writeMidiMessage(const rack::midi::Message& message); + void writeMidiMessage(const rack::midi::Message& message, uint8_t channel); #ifndef HEADLESS bool addIdleCallback(IdleCallback* cb) const;