Implement midi channel filtering in host midi

Signed-off-by: falkTX <falktx@falktx.com>
This commit is contained in:
falkTX 2022-01-23 22:28:15 +00:00
parent ecc8b00a9f
commit e3b9858994
No known key found for this signature in database
GPG key ID: CDBAA37ABC74FBA0
4 changed files with 93 additions and 52 deletions

View file

@ -82,6 +82,7 @@ struct HostMIDI : Module {
uint32_t midiEventFrame; uint32_t midiEventFrame;
int64_t lastBlockFrame; int64_t lastBlockFrame;
bool wasPlaying; bool wasPlaying;
uint8_t channel;
// stuff from Rack // stuff from Rack
bool smooth; bool smooth;
@ -138,6 +139,7 @@ struct HostMIDI : Module {
midiEventFrame = 0; midiEventFrame = 0;
lastBlockFrame = -1; lastBlockFrame = -1;
wasPlaying = false; wasPlaying = false;
channel = 0;
smooth = true; smooth = true;
channels = 1; channels = 1;
polyMode = ROTATE_MODE; polyMode = ROTATE_MODE;
@ -194,7 +196,7 @@ struct HostMIDI : Module {
} }
} }
for (uint32_t i=0; i<midiEventsLeft; ++i) while (midiEventsLeft != 0)
{ {
const MidiEvent& midiEvent(*midiEvents); const MidiEvent& midiEvent(*midiEvents);
@ -216,6 +218,12 @@ struct HostMIDI : Module {
data = midiEvent.data; data = midiEvent.data;
} }
if (channel != 0 && data[0] < 0xF0)
{
if ((data[0] & 0x0F) != (channel - 1))
continue;
}
converterMsg.frame = midiEventFrame; converterMsg.frame = midiEventFrame;
std::memcpy(converterMsg.bytes.data(), data, midiEvent.size); std::memcpy(converterMsg.bytes.data(), data, midiEvent.size);
@ -509,6 +517,7 @@ struct HostMIDI : Module {
struct MidiOutput : dsp::MidiGenerator<PORT_MAX_CHANNELS> { struct MidiOutput : dsp::MidiGenerator<PORT_MAX_CHANNELS> {
CardinalPluginContext* const pcontext; CardinalPluginContext* const pcontext;
uint8_t channel = 0;
dsp::Timer rateLimiterTimer; dsp::Timer rateLimiterTimer;
MidiOutput(CardinalPluginContext* const pc) MidiOutput(CardinalPluginContext* const pc)
@ -516,7 +525,7 @@ struct HostMIDI : Module {
void onMessage(const midi::Message& message) override void onMessage(const midi::Message& message) override
{ {
pcontext->writeMidiMessage(message); pcontext->writeMidiMessage(message, channel);
} }
} midiOutput; } midiOutput;
@ -555,6 +564,13 @@ struct HostMIDI : Module {
configOutput(CONTINUE_OUTPUT, "Continue trigger"); configOutput(CONTINUE_OUTPUT, "Continue trigger");
} }
void onReset() override
{
midiInput.reset();
midiOutput.reset();
midiOutput.channel = 0;
}
void process(const ProcessArgs& args) override void process(const ProcessArgs& args) override
{ {
if (midiInput.process(args, outputs)) if (midiInput.process(args, outputs))
@ -622,39 +638,48 @@ struct HostMIDI : Module {
json_t* dataToJson() override 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, "smooth", json_boolean(midiInput.smooth));
json_object_set_new(rootJ, "channels", json_integer(midiInput.channels)); json_object_set_new(rootJ, "channels", json_integer(midiInput.channels));
json_object_set_new(rootJ, "polyMode", json_integer(midiInput.polyMode)); json_object_set_new(rootJ, "polyMode", json_integer(midiInput.polyMode));
// Saving/restoring pitch and mod doesn't make much sense for MPE. // 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, "lastPitch", json_integer(midiInput.pws[0]));
json_object_set_new(rootJ, "lastMod", json_integer(midiInput.mods[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; return rootJ;
} }
void dataFromJson(json_t* rootJ) override void dataFromJson(json_t* rootJ) override
{ {
json_t* smoothJ = json_object_get(rootJ, "smooth"); if (json_t* const smoothJ = json_object_get(rootJ, "smooth"))
if (smoothJ)
midiInput.smooth = json_boolean_value(smoothJ); midiInput.smooth = json_boolean_value(smoothJ);
json_t* channelsJ = json_object_get(rootJ, "channels"); if (json_t* const channelsJ = json_object_get(rootJ, "channels"))
if (channelsJ)
midiInput.setChannels(json_integer_value(channelsJ)); midiInput.setChannels(json_integer_value(channelsJ));
json_t* polyModeJ = json_object_get(rootJ, "polyMode"); if (json_t* const polyModeJ = json_object_get(rootJ, "polyMode"))
if (polyModeJ)
midiInput.polyMode = (MidiInput::PolyMode) json_integer_value(polyModeJ); midiInput.polyMode = (MidiInput::PolyMode) json_integer_value(polyModeJ);
json_t* lastPitchJ = json_object_get(rootJ, "lastPitch"); if (json_t* const lastPitchJ = json_object_get(rootJ, "lastPitch"))
if (lastPitchJ)
midiInput.pws[0] = json_integer_value(lastPitchJ); midiInput.pws[0] = json_integer_value(lastPitchJ);
json_t* lastModJ = json_object_get(rootJ, "lastMod"); if (json_t* const lastModJ = json_object_get(rootJ, "lastMod"))
if (lastModJ)
midiInput.mods[0] = json_integer_value(lastModJ); 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 void appendContextMenu(Menu* const menu) override
{ {
menu->addChild(new MenuSeparator); menu->addChild(new MenuSeparator);
menu->addChild(createMenuLabel("MIDI Input")); menu->addChild(createMenuLabel("MIDI Input"));
menu->addChild(createBoolPtrMenuItem("Smooth pitch/mod wheel", "", &module->midiInput.smooth)); 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; HostMIDI* module;
Menu* createChildMenu() override { Menu* createChildMenu() override {
Menu* menu = new Menu; Menu* menu = new Menu;
@ -760,11 +804,11 @@ struct HostMIDIWidget : ModuleWidget {
return menu; return menu;
} }
}; };
ChannelItem* channelItem = new ChannelItem; PolyphonyChannelItem* const polyphonyChannelItem = new PolyphonyChannelItem;
channelItem->text = "Polyphony channels"; polyphonyChannelItem->text = "Polyphony channels";
channelItem->rightText = string::f("%d", module->midiInput.channels) + " " + RIGHT_ARROW; polyphonyChannelItem->rightText = string::f("%d", module->midiInput.channels) + " " + RIGHT_ARROW;
channelItem->module = module; polyphonyChannelItem->module = module;
menu->addChild(channelItem); menu->addChild(polyphonyChannelItem);
menu->addChild(createIndexPtrSubmenuItem("Polyphony mode", { menu->addChild(createIndexPtrSubmenuItem("Polyphony mode", {
"Rotate", "Rotate",
@ -774,7 +818,28 @@ struct HostMIDIWidget : ModuleWidget {
}, &module->midiInput.polyMode)); }, &module->midiInput.polyMode));
menu->addChild(new MenuSeparator); 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(createMenuLabel("MIDI Input & Output"));
menu->addChild(createMenuItem("Panic", "", menu->addChild(createMenuItem("Panic", "",

View file

@ -70,7 +70,7 @@ struct CardinalPluginContext : rack::Context {
UI* ui; UI* ui;
#endif #endif
CardinalPluginContext(Plugin* const p); CardinalPluginContext(Plugin* const p);
void writeMidiMessage(const rack::midi::Message& message); void writeMidiMessage(const rack::midi::Message& message, uint8_t channel);
#ifndef HEADLESS #ifndef HEADLESS
bool addIdleCallback(IdleCallback* cb) const; bool addIdleCallback(IdleCallback* cb) const;
void removeIdleCallback(IdleCallback* cb) const; void removeIdleCallback(IdleCallback* cb) const;

View file

@ -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(); const size_t size = message.bytes.size();
DISTRHO_SAFE_ASSERT_RETURN(size > 0,); 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); std::memcpy(event.data, message.bytes.data(), event.size);
if (channel != 0 && event.data[0] < 0xF0)
event.data[0] |= channel & 0x0F;
plugin->writeMidiEvent(event); plugin->writeMidiEvent(event);
} }
@ -831,34 +834,7 @@ protected:
{ {
const TimePosition& timePos(getTimePosition()); const TimePosition& timePos(getTimePosition());
bool reset = false; const bool reset = timePos.playing && (timePos.frame == 0 || fPreviousFrame + frames != timePos.frame);
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);
*/
}
context->playing = timePos.playing; context->playing = timePos.playing;
context->bbtValid = timePos.bbt.valid; context->bbtValid = timePos.bbt.valid;

View file

@ -107,7 +107,7 @@ struct CardinalPluginContext : rack::Context {
std::memset(parameters, 0, sizeof(parameters)); 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 #ifndef HEADLESS
bool addIdleCallback(IdleCallback* cb) const; bool addIdleCallback(IdleCallback* cb) const;