Browse Source

Support 14-bit MIDI CC via MSB/LSB. (Untested)

tags/v2.0.0
Andrew Belt 5 years ago
parent
commit
a7f5449d0c
1 changed files with 67 additions and 22 deletions
  1. +67
    -22
      src/core/MIDI_CC.cpp

+ 67
- 22
src/core/MIDI_CC.cpp View File

@@ -22,12 +22,17 @@ struct MIDI_CC : Module {

midi::InputQueue midiInput;
/** [cc][channel] */
int8_t values[128][16];
int8_t ccValues[128][16];
/** When LSB is enabled for CC 0-31, the MSB is stored here until the LSB is received.
[cc][channel]
*/
int8_t msbValues[32][16];
int learningId;
int learnedCcs[16];
/** [cell][channel] */
dsp::ExponentialFilter valueFilters[16][16];
bool mpeMode;
bool lsbEnabled;

MIDI_CC() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
@@ -43,17 +48,23 @@ struct MIDI_CC : Module {
}

void onReset() override {
for (int i = 0; i < 128; i++) {
for (int cc = 0; cc < 128; cc++) {
for (int c = 0; c < 16; c++) {
ccValues[cc][c] = 0;
}
}
for (int cc = 0; cc < 32; cc++) {
for (int c = 0; c < 16; c++) {
values[i][c] = 0;
msbValues[cc][c] = 0;
}
}
learningId = -1;
for (int i = 0; i < 16; i++) {
learnedCcs[i] = i;
}
learningId = -1;
midiInput.reset();
mpeMode = false;
lsbEnabled = false;
}

void process(const ProcessArgs& args) override {
@@ -77,7 +88,13 @@ struct MIDI_CC : Module {
int cc = learnedCcs[i];

for (int c = 0; c < channels; c++) {
float value = values[cc][c] / 127.f;
int16_t cellValue = ccValues[cc][c] * 128;
if (lsbEnabled && cc < 32)
cellValue += ccValues[cc + 32][c];
// Maximum value for 14-bit CC should be MSB=127 LSB=0, not MSB=127 LSB=127.
float value = cellValue / float(128 * 127);
// Support negative values because the gamepad MIDI driver generates nonstandard 8-bit CC values.
value = clamp(value, -1.f, 1.f);

// Detect behavior from MIDI buttons.
if (std::fabs(valueFilters[i][c].out - value) >= 1.f) {
@@ -93,7 +110,7 @@ struct MIDI_CC : Module {
}
}

void processMessage(const midi::Message &msg) {
void processMessage(const midi::Message& msg) {
switch (msg.getStatus()) {
// cc
case 0xb: {
@@ -103,22 +120,33 @@ struct MIDI_CC : Module {
}
}

void processCC(const midi::Message &msg) {
void processCC(const midi::Message& msg) {
uint8_t c = mpeMode ? msg.getChannel() : 0;
uint8_t cc = msg.getNote();
if (msg.bytes.size() < 2)
return;
// Allow CC to be negative if the 8th bit is set.
// The gamepad driver abuses this, for example.
// Cast uint8_t to int8_t
if (msg.bytes.size() < 2)
return;
int8_t value = msg.bytes[2];
value = clamp(value, -127, 127);
// Learn
if (learningId >= 0 && values[cc][c] != value) {
if (learningId >= 0 && ccValues[cc][c] != value) {
learnedCcs[learningId] = cc;
learningId = -1;
}
values[cc][c] = value;

if (lsbEnabled && cc < 32) {
// Don't set MSB yet. Wait for LSB to be received.
msbValues[cc][c] = value;
}
else if (lsbEnabled && 32 <= cc && cc < 64) {
// Apply MSB when LSB is received
ccValues[cc - 32][c] = msbValues[cc - 32][c];
ccValues[cc][c] = value;
}
else {
ccValues[cc][c] = value;
}
}

json_t* dataToJson() override {
@@ -134,13 +162,14 @@ struct MIDI_CC : Module {
json_t* valuesJ = json_array();
for (int i = 0; i < 128; i++) {
// Note: Only save channel 0. Since MPE mode won't be commonly used, it's pointless to save all 16 channels.
json_array_append_new(valuesJ, json_integer(values[i][0]));
json_array_append_new(valuesJ, json_integer(ccValues[i][0]));
}
json_object_set_new(rootJ, "values", valuesJ);

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

json_object_set_new(rootJ, "mpeMode", json_boolean(mpeMode));
json_object_set_new(rootJ, "lsbEnabled", json_boolean(lsbEnabled));
return rootJ;
}

@@ -159,7 +188,7 @@ struct MIDI_CC : Module {
for (int i = 0; i < 128; i++) {
json_t* valueJ = json_array_get(valuesJ, i);
if (valueJ) {
values[i][0] = json_integer_value(valueJ);
ccValues[i][0] = json_integer_value(valueJ);
}
}
}
@@ -171,14 +200,10 @@ struct MIDI_CC : Module {
json_t* mpeModeJ = json_object_get(rootJ, "mpeMode");
if (mpeModeJ)
mpeMode = json_boolean_value(mpeModeJ);
}
};


struct MIDI_CCMpeModeItem : MenuItem {
MIDI_CC* module;
void onAction(const event::Action& e) override {
module->mpeMode ^= true;
json_t* lsbEnabledJ = json_object_get(rootJ, "lsbEnabled");
if (lsbEnabledJ)
lsbEnabled = json_boolean_value(lsbEnabledJ);
}
};

@@ -223,11 +248,31 @@ struct MIDI_CCWidget : ModuleWidget {

menu->addChild(new MenuSeparator);

MIDI_CCMpeModeItem* mpeModeItem = new MIDI_CCMpeModeItem;
struct MpeModeItem : MenuItem {
MIDI_CC* module;
void onAction(const event::Action& e) override {
module->mpeMode ^= true;
}
};

MpeModeItem* mpeModeItem = new MpeModeItem;
mpeModeItem->text = "MPE mode";
mpeModeItem->rightText = CHECKMARK(module->mpeMode);
mpeModeItem->module = module;
menu->addChild(mpeModeItem);

struct LSBItem : MenuItem {
MIDI_CC* module;
void onAction(const event::Action& e) override {
module->lsbEnabled ^= true;
}
};

LSBItem* highResolutionItem = new LSBItem;
highResolutionItem->text = "CC 0-31 controls are 14-bit";
highResolutionItem->rightText = CHECKMARK(module->lsbEnabled);
highResolutionItem->module = module;
menu->addChild(highResolutionItem);
}
};



Loading…
Cancel
Save