Skip to content

Commit

Permalink
basic 14-bit CC support
Browse files Browse the repository at this point in the history
  • Loading branch information
Dewb committed Mar 8, 2024
1 parent 16827b0 commit 7be2482
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 44 deletions.
152 changes: 115 additions & 37 deletions src/faderbank/FaderbankModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,7 @@ FaderbankModule::~FaderbankModule()

void FaderbankModule::process(const ProcessArgs& args)
{
rack::midi::Message msg;
while (midiInput.tryPop(&msg, args.frame))
{
processMIDIMessage(msg);
}
processMIDIMessages(args);

for (unsigned i = 0; i < NUM_FADERS; i++)
{
Expand All @@ -44,51 +40,120 @@ void FaderbankModule::process(const ProcessArgs& args)
}
}

void FaderbankModule::processMIDIMessage(const rack::midi::Message& msg)
void FaderbankModule::processMIDIMessages(const ProcessArgs& args)
{
DEBUG("MIDI: %lld %s", msg.getFrame(), msg.toString().c_str());
int min14bitInterval = 16;

switch (msg.getStatus())
rack::midi::Message msg;
while (midiInput.tryPop(&msg, args.frame))
{
case 0xb: // Continuous Controller
{
// Combine channel and CC number into a lookup key
uint16_t key = (msg.getChannel() << 8) | msg.getNote();
auto iter = inputMap.find(key);
if (iter != inputMap.end())
DEBUG("MIDI: %lld %s", msg.getFrame(), msg.toString().c_str());

switch (msg.getStatus())
{
case 0xb: // Continuous Controller
{
uint8_t index = iter->second;
if (index < NUM_FADERS)
// Combine channel and CC number into a lookup key
uint8_t ccNum = msg.getNote();
uint16_t key = (msg.getChannel() << 8) | ccNum;
auto iter = inputMap.find(key);
if (iter != inputMap.end())
{
auto param = getParamQuantity(index);
if (param)
uint8_t index = iter->second;
if (index < NUM_FADERS)
{
param->setScaledValue((msg.getValue() * 1.0) / 127.0);
records[index].highValue = msg.getValue();
records[index].lastHighValue = msg.getValue();
records[index].lastHighValueFrame = args.frame;
}
}
else if (use14bitCCs && ccNum >= 32 && ccNum < 64)
{
// look for potential LSB CC of 14-bit CC 0-31
key = (msg.getChannel() << 8) | (ccNum - 32);
iter = inputMap.find(key);
if (iter != inputMap.end())
{
uint8_t index = iter->second;
if (index < NUM_FADERS)
{
records[index].lowValue = msg.getValue();
records[index].lastLowValueFrame = args.frame;
}
}
}
}
}
break;
case 0xF: // System Exclusive
{
if (msg.bytes[1] == 0x7d && // 16n manufacturer ID
msg.bytes[2] == 0x00 &&
msg.bytes[3] == 0x00 &&
msg.bytes[4] == 0x0F && // sysex config response ID
msg.bytes.size() > (9 + 48 + NUM_FADERS))
break;
case 0xF: // System Exclusive
{
inputMap.clear();
for (int i = 0; i < NUM_FADERS; i++)
if (msg.bytes[1] == 0x7d && // 16n manufacturer ID
msg.bytes[2] == 0x00 &&
msg.bytes[3] == 0x00 &&
msg.bytes[4] == 0x0F && // sysex config response ID
msg.bytes.size() > (9 + 48 + NUM_FADERS))
{
uint8_t channel = msg.bytes[9 + 16 + i] - 1;
uint8_t ccNum = msg.bytes[9 + 48 + i];
inputMap[(channel << 8) | ccNum] = i;
inputMap.clear();
for (int i = 0; i < NUM_FADERS; i++)
{
uint8_t channel = msg.bytes[9 + 16 + i] - 1;
uint8_t ccNum = msg.bytes[9 + 48 + i];
inputMap[(channel << 8) | ccNum] = i;
records[i].ccNum = ccNum;
}
}
}
break;
default:
break;
}
}

for (int i = 0; i < NUM_FADERS; i++)
{
uint16_t value;
bool updateable = false;
bool expect14bit = use14bitCCs && records[i].ccNum < 32;

if (records[i].highValue != 0xFF)
{
if (expect14bit)
{
if (records[i].lowValue != 0xFF)
{
value = ((records[i].highValue & 0x7F) << 7) + (records[i].lowValue & 0x7F);
updateable = true;
}
else if ((args.frame - records[i].lastHighValueFrame) > min14bitInterval)
{
// give up waiting for a low value
value = (records[i].highValue & 0x7F) << 7;
updateable = true;
}
}
break;
default:
break;
else
{
value = records[i].highValue & 0x7F;
updateable = true;
}
}
else if (expect14bit && records[i].lowValue != 0xFF && (args.frame - records[i].lastLowValueFrame) > min14bitInterval)
{
// give up waiting for a high value
value = ((records[i].lastHighValue & 0x7F) << 7) + (records[i].lowValue & 0x7F);
updateable = true;
}

if (updateable)
{
auto param = getParamQuantity(i);
if (param)
{
param->setScaledValue((value * 1.0f) / ((expect14bit ? 0x3FFF : 0x7F) * 1.0f));
}

records[i].highValue = 0xFF;
records[i].lowValue = 0xFF;
}
}
}

Expand All @@ -99,6 +164,7 @@ void FaderbankModule::resetConfig()
{
// by default, assign CC faders starting with 32, all on channel 1
inputMap[32 + i] = i;
records[i].ccNum = 32 + i;
}
}

Expand Down Expand Up @@ -145,8 +211,10 @@ json_t* FaderbankModule::dataToJson()
json_object_set_new(rootJ, "faderRange", json_integer(faderRange));
json_object_set_new(rootJ, "faderSize", json_integer(faderSize));
json_object_set_new(rootJ, "polyphonicMode", json_boolean(polyphonicMode));
json_object_set_new(rootJ, "use14bitCCs", json_boolean(use14bitCCs));

json_object_set_new(rootJ, "midi", midiInput.toJson());
json_object_set_new(rootJ, "midiOutput", midiOutput.toJson());

json_t* configJ = json_object();
for (auto& entry : inputMap)
Expand Down Expand Up @@ -174,10 +242,18 @@ void FaderbankModule::dataFromJson(json_t* rootJ)
if (polyphonicModeJ)
polyphonicMode = json_boolean_value(polyphonicModeJ);

json_t* use14bitCCsJ = json_object_get(rootJ, "use14bitCCs");
if (use14bitCCsJ)
use14bitCCs = json_boolean_value(use14bitCCsJ);

json_t* midiJ = json_object_get(rootJ, "midi");
if (midiJ)
midiInput.fromJson(midiJ);

json_t* midiOutputJ = json_object_get(rootJ, "midiOutput");
if (midiOutputJ)
midiOutput.fromJson(midiOutputJ);

json_t* configJ = json_object_get(rootJ, "16n_config");
if (configJ)
{
Expand All @@ -187,7 +263,9 @@ void FaderbankModule::dataFromJson(json_t* rootJ)
json_object_foreach(configJ, key, dataJ)
{
int16_t val = std::stoi(key);
inputMap[val] = json_integer_value(dataJ);
int8_t fader = json_integer_value(dataJ);
inputMap[val] = fader;
records[fader].ccNum = val & 0xFF;
}
}
}
Expand Down
26 changes: 24 additions & 2 deletions src/faderbank/FaderbankModule.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,31 @@

#define NUM_FADERS 16

struct ControllerRecord
{
uint8_t highValue;
uint8_t lowValue;
uint8_t lastHighValue;
int64_t lastHighValueFrame;
int64_t lastLowValueFrame;
uint8_t ccNum;

ControllerRecord()
{
highValue = 0xFF;
lowValue = 0xFF;
lastHighValue = 0;
}
};

struct FaderbankModule : rack::Module
{
FaderbankModule();
~FaderbankModule();

void process(const ProcessArgs& args) override;

void processMIDIMessage(const rack::midi::Message& msg);
void processMIDIMessages(const ProcessArgs& args);
void resetConfig();
void updateFaderRanges();

Expand All @@ -21,9 +38,10 @@ struct FaderbankModule : rack::Module
// override fromJson to deserialize data before params
void fromJson(json_t* rootJ) override;

std::map<uint16_t, uint8_t> inputMap;

rack::midi::InputQueue midiInput;
std::map<uint16_t, uint8_t> inputMap;
rack::midi::Output midiOutput;

typedef enum
{
Expand All @@ -41,4 +59,8 @@ struct FaderbankModule : rack::Module
FaderSize faderSize = FaderSize90mm;
FaderRange faderRange = FaderRange10V;
bool polyphonicMode = false;
bool use14bitCCs = false;

protected:
ControllerRecord records[NUM_FADERS];
};
28 changes: 23 additions & 5 deletions src/faderbank/FaderbankWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ void FaderbankWidget::appendContextMenu(Menu* menu)

menu->addChild(new MenuSeparator());

menu->addChild(createSubmenuItem("MIDI connection", fb->midiInput.getDeviceName(fb->midiInput.getDeviceId()),
menu->addChild(createSubmenuItem("MIDI input", fb->midiInput.getDeviceName(fb->midiInput.getDeviceId()),
[=](Menu* childMenu)
{
appendMidiMenu(childMenu, &fb->midiInput);
Expand All @@ -210,6 +210,27 @@ void FaderbankWidget::appendContextMenu(Menu* menu)
delete last;
}));

menu->addChild(createSubmenuItem("MIDI output", fb->midiOutput.getDeviceName(fb->midiOutput.getDeviceId()),
[=](Menu* childMenu)
{
appendMidiMenu(childMenu, &fb->midiOutput);
// remove channel selection
auto last = childMenu->children.back();
childMenu->removeChild(last);
delete last;
}));

menu->addChild(createCheckMenuItem(
"Use 14-bit MIDI CCs", "",
[=]()
{
return fb->use14bitCCs;
},
[=]()
{
fb->use14bitCCs = !fb->use14bitCCs;
}));

menu->addChild(createMenuItem("Autodetect 16n configuration", "",
[=]()
{
Expand All @@ -220,9 +241,6 @@ void FaderbankWidget::appendContextMenu(Menu* menu)
msg.setSize(6);
msg.bytes = { 0xF0, 0x7d, 0x00, 0x00, 0x1F, 0xF7 };

midi::Output output;
output.setDriverId(fb->midiInput.getDriverId());
output.setDeviceId(fb->midiInput.getDeviceId());
output.sendMessage(msg);
fb->midiOutput.sendMessage(msg);
}));
}

0 comments on commit 7be2482

Please sign in to comment.