Implement midi channel filtering in host midi
Signed-off-by: falkTX <falktx@falktx.com>
This commit is contained in:
parent
ecc8b00a9f
commit
e3b9858994
4 changed files with 93 additions and 52 deletions
|
|
@ -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", "",
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue