@@ -18,8 +18,8 @@ struct MIDI_CC : Module { | |||
midi::InputQueue midiInput; | |||
int8_t values[128]; | |||
int learningId = -1; | |||
int learnedCcs[16] = {}; | |||
int learningId; | |||
int learnedCcs[16]; | |||
dsp::ExponentialFilter valueFilters[16]; | |||
int8_t lastValues[16] = {}; | |||
@@ -76,20 +76,24 @@ struct MIDI_CC : Module { | |||
switch (msg.getStatus()) { | |||
// cc | |||
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; | |||
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 *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 { | |||
midi::Message 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 *rootJ = json_object(); | |||
@@ -17,8 +17,9 @@ struct MIDI_Map : Module { | |||
midi::InputQueue midiInput; | |||
int8_t values[128]; | |||
int learningId = -1; | |||
int learnedCcs[16] = {}; | |||
int learningId; | |||
int lastLearnedCc; | |||
int learnedCcs[8]; | |||
dsp::ExponentialFilter valueFilters[8]; | |||
MIDI_Map() { | |||
@@ -27,10 +28,71 @@ struct MIDI_Map : Module { | |||
} | |||
void onReset() override { | |||
learningId = -1; | |||
lastLearnedCc = -1; | |||
for (int i = 0; i < 8; i++) { | |||
learnedCcs[i] = -1; | |||
} | |||
midiInput.reset(); | |||
} | |||
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; | |||
bgColor = color; | |||
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 { | |||
text = "Unmapped"; | |||
color.a = 0.5; | |||
bgColor = nvgRGBA(0, 0, 0, 0); | |||
// HACK | |||
if (APP->event->selectedWidget == this) | |||
APP->event->setSelected(NULL); | |||
} | |||
} | |||
}; | |||
@@ -87,6 +87,8 @@ struct CcChoice : LedDisplayChoice { | |||
else { | |||
text = string::f("%d", module->learnedCcs[id]); | |||
color.a = 1.0; | |||
// HACK | |||
if (APP->event->selectedWidget == this) | |||
APP->event->setSelected(NULL); | |||
} | |||
@@ -170,6 +172,7 @@ struct NoteChoice : LedDisplayChoice { | |||
text = string::f("%s%d", noteNames[semi], oct); | |||
color.a = 1.0; | |||
// HACK | |||
if (APP->event->selectedWidget == this) | |||
APP->event->setSelected(NULL); | |||
} | |||