@@ -18,8 +18,8 @@ struct MIDI_CC : Module { | |||||
midi::InputQueue midiInput; | midi::InputQueue midiInput; | ||||
int8_t values[128]; | int8_t values[128]; | ||||
int learningId = -1; | |||||
int learnedCcs[16] = {}; | |||||
int learningId; | |||||
int learnedCcs[16]; | |||||
dsp::ExponentialFilter valueFilters[16]; | dsp::ExponentialFilter valueFilters[16]; | ||||
int8_t lastValues[16] = {}; | int8_t lastValues[16] = {}; | ||||
@@ -76,20 +76,24 @@ struct MIDI_CC : Module { | |||||
switch (msg.getStatus()) { | switch (msg.getStatus()) { | ||||
// cc | // cc | ||||
case 0xb: { | case 0xb: { | ||||
uint8_t cc = msg.getNote(); | |||||
// Learn | |||||
if (learningId >= 0 && values[cc] != msg.data2) { | |||||
learnedCcs[learningId] = cc; | |||||
learningId = -1; | |||||
} | |||||
// Allow CC to be negative if the 8th bit is set. | |||||
// The gamepad driver abuses this, for example. | |||||
values[cc] = msg.data2; | |||||
processCC(msg); | |||||
} break; | } break; | ||||
default: break; | default: break; | ||||
} | } | ||||
} | } | ||||
void processCC(midi::Message msg) { | |||||
uint8_t cc = msg.getNote(); | |||||
// Learn | |||||
if (learningId >= 0 && values[cc] != msg.data2) { | |||||
learnedCcs[learningId] = cc; | |||||
learningId = -1; | |||||
} | |||||
// Allow CC to be negative if the 8th bit is set. | |||||
// The gamepad driver abuses this, for example. | |||||
values[cc] = msg.data2; | |||||
} | |||||
json_t *dataToJson() override { | json_t *dataToJson() override { | ||||
json_t *rootJ = json_object(); | json_t *rootJ = json_object(); | ||||
@@ -46,31 +46,6 @@ struct MIDI_Gate : Module { | |||||
} | } | ||||
} | } | ||||
void pressNote(uint8_t note, uint8_t vel) { | |||||
// Learn | |||||
if (learningId >= 0) { | |||||
learnedNotes[learningId] = note; | |||||
learningId = -1; | |||||
} | |||||
// Find id | |||||
for (int i = 0; i < 16; i++) { | |||||
if (learnedNotes[i] == note) { | |||||
gates[i] = true; | |||||
gateTimes[i] = 1e-3f; | |||||
velocities[i] = vel; | |||||
} | |||||
} | |||||
} | |||||
void releaseNote(uint8_t note) { | |||||
// Find id | |||||
for (int i = 0; i < 16; i++) { | |||||
if (learnedNotes[i] == note) { | |||||
gates[i] = false; | |||||
} | |||||
} | |||||
} | |||||
void step() override { | void step() override { | ||||
midi::Message msg; | midi::Message msg; | ||||
while (midiInput.shift(&msg)) { | while (midiInput.shift(&msg)) { | ||||
@@ -113,6 +88,31 @@ struct MIDI_Gate : Module { | |||||
} | } | ||||
} | } | ||||
void pressNote(uint8_t note, uint8_t vel) { | |||||
// Learn | |||||
if (learningId >= 0) { | |||||
learnedNotes[learningId] = note; | |||||
learningId = -1; | |||||
} | |||||
// Find id | |||||
for (int i = 0; i < 16; i++) { | |||||
if (learnedNotes[i] == note) { | |||||
gates[i] = true; | |||||
gateTimes[i] = 1e-3f; | |||||
velocities[i] = vel; | |||||
} | |||||
} | |||||
} | |||||
void releaseNote(uint8_t note) { | |||||
// Find id | |||||
for (int i = 0; i < 16; i++) { | |||||
if (learnedNotes[i] == note) { | |||||
gates[i] = false; | |||||
} | |||||
} | |||||
} | |||||
json_t *dataToJson() override { | json_t *dataToJson() override { | ||||
json_t *rootJ = json_object(); | json_t *rootJ = json_object(); | ||||
@@ -17,8 +17,9 @@ struct MIDI_Map : Module { | |||||
midi::InputQueue midiInput; | midi::InputQueue midiInput; | ||||
int8_t values[128]; | int8_t values[128]; | ||||
int learningId = -1; | |||||
int learnedCcs[16] = {}; | |||||
int learningId; | |||||
int lastLearnedCc; | |||||
int learnedCcs[8]; | |||||
dsp::ExponentialFilter valueFilters[8]; | dsp::ExponentialFilter valueFilters[8]; | ||||
MIDI_Map() { | MIDI_Map() { | ||||
@@ -27,10 +28,71 @@ struct MIDI_Map : Module { | |||||
} | } | ||||
void onReset() override { | void onReset() override { | ||||
learningId = -1; | |||||
lastLearnedCc = -1; | |||||
for (int i = 0; i < 8; i++) { | |||||
learnedCcs[i] = -1; | |||||
} | |||||
midiInput.reset(); | midiInput.reset(); | ||||
} | } | ||||
void step() override { | void step() override { | ||||
midi::Message msg; | |||||
while (midiInput.shift(&msg)) { | |||||
processMessage(msg); | |||||
} | |||||
} | |||||
void processMessage(midi::Message msg) { | |||||
switch (msg.getStatus()) { | |||||
// cc | |||||
case 0xb: { | |||||
processCC(msg); | |||||
} break; | |||||
default: break; | |||||
} | |||||
} | |||||
void processCC(midi::Message msg) { | |||||
uint8_t cc = msg.getNote(); | |||||
// Learn | |||||
if (learningId >= 0 && values[cc] != msg.data2) { | |||||
if (lastLearnedCc != cc) { | |||||
learnedCcs[learningId] = cc; | |||||
lastLearnedCc = cc; | |||||
if (++learningId >= 8) | |||||
learningId = -1; | |||||
} | |||||
} | |||||
values[cc] = msg.getValue(); | |||||
} | |||||
json_t *dataToJson() override { | |||||
json_t *rootJ = json_object(); | |||||
json_t *ccsJ = json_array(); | |||||
for (int i = 0; i < 8; i++) { | |||||
json_array_append_new(ccsJ, json_integer(learnedCcs[i])); | |||||
} | |||||
json_object_set_new(rootJ, "ccs", ccsJ); | |||||
json_object_set_new(rootJ, "midi", midiInput.toJson()); | |||||
return rootJ; | |||||
} | |||||
void dataFromJson(json_t *rootJ) override { | |||||
json_t *ccsJ = json_object_get(rootJ, "ccs"); | |||||
if (ccsJ) { | |||||
for (int i = 0; i < 8; i++) { | |||||
json_t *ccJ = json_array_get(ccsJ, i); | |||||
if (ccJ) | |||||
learnedCcs[i] = json_integer_value(ccJ); | |||||
} | |||||
} | |||||
json_t *midiJ = json_object_get(rootJ, "midi"); | |||||
if (midiJ) | |||||
midiInput.fromJson(midiJ); | |||||
} | } | ||||
}; | }; | ||||
@@ -66,11 +128,24 @@ struct MIDI_MapChoice : LedDisplayChoice { | |||||
color.a = 1.0; | color.a = 1.0; | ||||
bgColor = color; | bgColor = color; | ||||
bgColor.a = 0.15; | bgColor.a = 0.15; | ||||
// HACK | |||||
if (APP->event->selectedWidget != this) | |||||
APP->event->setSelected(this); | |||||
} | |||||
else if (module->learnedCcs[id] >= 0) { | |||||
text = string::f("CC%d", module->learnedCcs[id]); | |||||
color.a = 1.0; | |||||
bgColor = nvgRGBA(0, 0, 0, 0); | |||||
} | } | ||||
else { | else { | ||||
text = "Unmapped"; | text = "Unmapped"; | ||||
color.a = 0.5; | color.a = 0.5; | ||||
bgColor = nvgRGBA(0, 0, 0, 0); | bgColor = nvgRGBA(0, 0, 0, 0); | ||||
// HACK | |||||
if (APP->event->selectedWidget == this) | |||||
APP->event->setSelected(NULL); | |||||
} | } | ||||
} | } | ||||
}; | }; | ||||
@@ -87,6 +87,8 @@ struct CcChoice : LedDisplayChoice { | |||||
else { | else { | ||||
text = string::f("%d", module->learnedCcs[id]); | text = string::f("%d", module->learnedCcs[id]); | ||||
color.a = 1.0; | color.a = 1.0; | ||||
// HACK | |||||
if (APP->event->selectedWidget == this) | if (APP->event->selectedWidget == this) | ||||
APP->event->setSelected(NULL); | APP->event->setSelected(NULL); | ||||
} | } | ||||
@@ -170,6 +172,7 @@ struct NoteChoice : LedDisplayChoice { | |||||
text = string::f("%s%d", noteNames[semi], oct); | text = string::f("%s%d", noteNames[semi], oct); | ||||
color.a = 1.0; | color.a = 1.0; | ||||
// HACK | |||||
if (APP->event->selectedWidget == this) | if (APP->event->selectedWidget == this) | ||||
APP->event->setSelected(NULL); | APP->event->setSelected(NULL); | ||||
} | } | ||||