Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# RNBO Runner Changes

* *current*
* added optional device midi input filtering
* OSCQuery endpoint: `/rnbo/inst/<INSTANCE ID>/config/midi_input_channel`
* set to "all" (default) for no filtering
* set to "1" to only accept messages on channel 1
* any realtime or non channel based messages should always get thru
* fixed bug where `set_preset` config for instance wasn't persisting with graph
* expanded ability to read from / write to various runner OSC endpoints with patcher exports via inport, outport, param
* added the concept of `Param Views`
* these are sorted collections of parameters that can span multiple devices
Expand Down
17 changes: 5 additions & 12 deletions src/Controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2110,22 +2110,15 @@ void Controller::doLoadSet(SetInfo& setInfo, boost::optional<PendingPresetMap>&

if (entry.config.size()) {
auto instConfig = RNBO::Json::parse(entry.config);
//override datarefs
if (instConfig.contains("datarefs"))
config["datarefs"] = instConfig["datarefs"];
//load the last preset as the initial one
if (instConfig["preset_last"].is_string()) {
config["preset_initial"] = instConfig["preset_last"];
}
//override meta
if (instConfig["metaoverride"].is_object()) {
config["metaoverride"] = instConfig["metaoverride"];
}
if (instConfig.contains("setpreset")) {
config["setpreset"] = instConfig["setpreset"];
}
if (instConfig["namealias"].is_string()) {
config["namealias"] = instConfig["namealias"];
//overrides
for (const auto& key: { "namealias", "setpreset", "insetpreset", "midi_input_channel", "metaoverride", "datarefs" }) {
if (instConfig.contains(key)) {
config[key] = instConfig[key];
}
}
}

Expand Down
41 changes: 41 additions & 0 deletions src/Instance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ Instance::Instance(
mInSetPreset = conf["insetpreset"];
}


//setup initial preset channel mapping
try {
std::string chanName = "omni";
Expand Down Expand Up @@ -215,6 +216,10 @@ Instance::Instance(
mCore->setExternalDataHandler(mDataHandler.get());
}

if (conf.contains("midi_input_channel") && conf["midi_input_channel"].is_number()) {
mAudio->setMidiInputChannel(conf["midi_input_channel"].get<int>());
}

mPresetSaveQueue = RNBO::make_unique<moodycamel::ReaderWriterQueue<std::tuple<std::string, RNBO::ConstPresetPtr, std::string, int>, 32>>(32);

mDB->presets(mName, [this](const std::string& name, bool initial, int /*presetindex*/) {
Expand Down Expand Up @@ -252,6 +257,7 @@ Instance::Instance(
if (v.get_type() == ossia::val_type::BOOL) {
mSetPresetPatcherNamed = v.get<bool>();
}
queueConfigChangeSignal();
});
}

Expand All @@ -264,6 +270,40 @@ Instance::Instance(
if (v.get_type() == ossia::val_type::BOOL) {
mInSetPreset = v.get<bool>();
}
queueConfigChangeSignal();
});
}

{
auto n = config->create_child("midi_input_channel");
auto p = n->create_parameter(ossia::val_type::STRING);

n->set(ossia::net::description_attribute{}, "Optionally select a MIDI channel that this device should accept for input, omni for all channels");

std::vector<ossia::value> values = {
"all",
"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"
};

auto dom = ossia::init_domain(ossia::val_type::STRING);
ossia::set_values(dom, values);
n->set(ossia::net::domain_attribute{}, dom);
n->set(ossia::net::bounding_mode_attribute{}, ossia::bounding_mode::CLIP);

p->push_value(values[mAudio->midiInputChannel() + 1]);
p->add_callback([this, values](const ossia::value& v) {
if (v.get_type() == ossia::val_type::STRING) {
auto s = v.get<std::string>();
if (s == "all") {
mAudio->setMidiInputChannel(-1);
} else {
int i = std::stoi(s);
if (i > 0 && i <= 16) {
mAudio->setMidiInputChannel(i - 1);
}
}
}
queueConfigChangeSignal();
});
}

Expand Down Expand Up @@ -1428,6 +1468,7 @@ RNBO::Json Instance::currentConfig() {
config["datarefs"] = datarefs;
config["setpreset"] = mSetPresetPatcherNamed ? "patchernamed" : "values";
config["insetpreset"] = mInSetPreset;
config["midi_input_channel"] = (int)mAudio->midiInputChannel();

//mAudio->addConfig(config);

Expand Down
9 changes: 9 additions & 0 deletions src/InstanceAudio.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ class InstanceAudio {
virtual void registerConfigChangeCallback(std::function<void()> cb) { };

virtual size_t bufferSize() = 0;

int8_t midiInputChannel() {
return mMIDIInputChannel;
}

void setMidiInputChannel(int c) {
mMIDIInputChannel = static_cast<int8_t>(std::clamp(c, -1, 15));
}
protected:
std::atomic<AudioState> mAudioState = AudioState::Idle;
int8_t mMIDIInputChannel = -1; //0 based, negative means omni
};
24 changes: 24 additions & 0 deletions src/JackAudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2560,6 +2560,8 @@ void InstanceAudioJack::process(jack_nframes_t nframes) {
std::unique_lock<std::mutex> guard(mMIDIMapMutex, std::try_to_lock);

uint16_t lastKey = 0;

int8_t acceptchan = mMIDIInputChannel;
for (auto i = 0; i < count; i++) {
jack_midi_event_get(&evt, midi_buf, i);

Expand Down Expand Up @@ -2607,6 +2609,28 @@ void InstanceAudioJack::process(jack_nframes_t nframes) {
}
}

//if we're filtering midi and the byte has status
uint8_t status = bytes[0] & 0xF0;
if (acceptchan >= 0 && (status & 0x80) != 0) {
switch (status) {
case midimap::NOTE_ON:
case midimap::NOTE_OFF:
case midimap::CONTROL_CHANGE:
case midimap::KEY_PRESSURE:
case midimap::CHANNEL_PRESSURE:
case midimap::PITCH_BEND_CHANGE:
case midimap::PROGRAM_CHANGE: {
int8_t chan = static_cast<int8_t>(bytes[0] & 0x0F);
if (chan != acceptchan) {
continue;
}
}
break;
default:
break;
};
}

//look for program change to change preset
if (mProgramChangeQueue && evt.size == 2 && (evt.buffer[0] & 0xF0) == 0xC0) {
mProgramChangeQueue->enqueue(ProgramChange { .chan = static_cast<uint8_t>(evt.buffer[0] & 0x0F), .prog = static_cast<uint8_t>(evt.buffer[1]) });
Expand Down
22 changes: 0 additions & 22 deletions src/MIDIMap.cpp
Original file line number Diff line number Diff line change
@@ -1,27 +1,5 @@
#include "MIDIMap.h"

namespace {
static constexpr uint8_t NOTE_OFF = 0x80;
static constexpr uint8_t NOTE_ON = 0x90;
static constexpr uint8_t KEY_PRESSURE = 0xA0;
static constexpr uint8_t CONTROL_CHANGE = 0xB0;
static constexpr uint8_t PITCH_BEND_CHANGE = 0xE0;
static constexpr uint8_t SONG_POSITION_POINTER = 0xF2;
static constexpr uint8_t PROGRAM_CHANGE = 0xC0;
static constexpr uint8_t CHANNEL_PRESSURE = 0xD0;
static constexpr uint8_t QUARTER_FRAME = 0xF1;
static constexpr uint8_t SONG_SELECT = 0xF3;
static constexpr uint8_t TUNE_REQUEST = 0xF6;
static constexpr uint8_t TIMING_CLOCK = 0xF8;
static constexpr uint8_t START = 0xFA;
static constexpr uint8_t CONTINUE = 0xFB;
static constexpr uint8_t STOP = 0xFC;
static constexpr uint8_t ACTIVE_SENSING = 0xFE;
static constexpr uint8_t RESET = 0xFF;
static constexpr uint8_t SYSEX_START = 0xF0;
static constexpr uint8_t SYSEX_END = 0xF7;
}

namespace midimap {
double value(uint8_t status, uint8_t data0, uint8_t data1, bool normalize) {
switch (status & 0xF0) {
Expand Down
20 changes: 20 additions & 0 deletions src/MIDIMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,26 @@

//always mapping to/from normalized values
namespace midimap {
inline constexpr uint8_t NOTE_OFF = 0x80;
inline constexpr uint8_t NOTE_ON = 0x90;
inline constexpr uint8_t KEY_PRESSURE = 0xA0;
inline constexpr uint8_t CONTROL_CHANGE = 0xB0;
inline constexpr uint8_t PITCH_BEND_CHANGE = 0xE0;
inline constexpr uint8_t SONG_POSITION_POINTER = 0xF2;
inline constexpr uint8_t PROGRAM_CHANGE = 0xC0;
inline constexpr uint8_t CHANNEL_PRESSURE = 0xD0;
inline constexpr uint8_t QUARTER_FRAME = 0xF1;
inline constexpr uint8_t SONG_SELECT = 0xF3;
inline constexpr uint8_t TUNE_REQUEST = 0xF6;
inline constexpr uint8_t TIMING_CLOCK = 0xF8;
inline constexpr uint8_t START = 0xFA;
inline constexpr uint8_t CONTINUE = 0xFB;
inline constexpr uint8_t STOP = 0xFC;
inline constexpr uint8_t ACTIVE_SENSING = 0xFE;
inline constexpr uint8_t RESET = 0xFF;
inline constexpr uint8_t SYSEX_START = 0xF0;
inline constexpr uint8_t SYSEX_END = 0xF7;

double value(uint8_t status, uint8_t data0, uint8_t data1, bool normalize = true);
//0 means we don't map this type
uint16_t key(uint8_t status, uint8_t data0 = 0);
Expand Down
Loading