| @@ -21,8 +21,10 @@ struct MIDITriggerToCVInterface : Module { | |||||
| bool gates[16]; | bool gates[16]; | ||||
| float gateTimes[16]; | float gateTimes[16]; | ||||
| uint8_t velocities[16]; | |||||
| int learningId = -1; | int learningId = -1; | ||||
| uint8_t learnedNotes[16] = {}; | uint8_t learnedNotes[16] = {}; | ||||
| bool velocity = false; | |||||
| MIDITriggerToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { | MIDITriggerToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { | ||||
| onReset(); | onReset(); | ||||
| @@ -37,7 +39,7 @@ struct MIDITriggerToCVInterface : Module { | |||||
| learningId = -1; | learningId = -1; | ||||
| } | } | ||||
| void pressNote(uint8_t note) { | |||||
| void pressNote(uint8_t note, uint8_t vel) { | |||||
| // Learn | // Learn | ||||
| if (learningId >= 0) { | if (learningId >= 0) { | ||||
| learnedNotes[learningId] = note; | learnedNotes[learningId] = note; | ||||
| @@ -48,6 +50,7 @@ struct MIDITriggerToCVInterface : Module { | |||||
| if (learnedNotes[i] == note) { | if (learnedNotes[i] == note) { | ||||
| gates[i] = true; | gates[i] = true; | ||||
| gateTimes[i] = 1e-3f; | gateTimes[i] = 1e-3f; | ||||
| velocities[i] = vel; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -70,7 +73,7 @@ struct MIDITriggerToCVInterface : Module { | |||||
| for (int i = 0; i < 16; i++) { | for (int i = 0; i < 16; i++) { | ||||
| if (gateTimes[i] > 0.f) { | if (gateTimes[i] > 0.f) { | ||||
| outputs[TRIG_OUTPUT + i].value = 10.f; | |||||
| outputs[TRIG_OUTPUT + i].value = velocities ? rescale(velocities[i], 0, 127, 0.f, 10.f) : 10.f; | |||||
| // If the gate is off, wait 1 ms before turning the pulse off. | // If the gate is off, wait 1 ms before turning the pulse off. | ||||
| // This avoids drum controllers sending a pulse with 0 ms duration. | // This avoids drum controllers sending a pulse with 0 ms duration. | ||||
| if (!gates[i]) { | if (!gates[i]) { | ||||
| @@ -92,7 +95,7 @@ struct MIDITriggerToCVInterface : Module { | |||||
| // note on | // note on | ||||
| case 0x9: { | case 0x9: { | ||||
| if (msg.value() > 0) { | if (msg.value() > 0) { | ||||
| pressNote(msg.note()); | |||||
| pressNote(msg.note(), msg.value()); | |||||
| } | } | ||||
| else { | else { | ||||
| // Many stupid keyboards send a "note on" command with 0 velocity to mean "note release" | // Many stupid keyboards send a "note on" command with 0 velocity to mean "note release" | ||||
| @@ -114,6 +117,7 @@ struct MIDITriggerToCVInterface : Module { | |||||
| json_object_set_new(rootJ, "notes", notesJ); | json_object_set_new(rootJ, "notes", notesJ); | ||||
| json_object_set_new(rootJ, "midi", midiInput.toJson()); | json_object_set_new(rootJ, "midi", midiInput.toJson()); | ||||
| json_object_set_new(rootJ, "velocity", json_boolean(velocity)); | |||||
| return rootJ; | return rootJ; | ||||
| } | } | ||||
| @@ -123,13 +127,17 @@ struct MIDITriggerToCVInterface : Module { | |||||
| for (int i = 0; i < 16; i++) { | for (int i = 0; i < 16; i++) { | ||||
| json_t *noteJ = json_array_get(notesJ, i); | json_t *noteJ = json_array_get(notesJ, i); | ||||
| if (noteJ) | if (noteJ) | ||||
| learnedNotes[i] = json_integer_value(noteJ) & 0x7f; | |||||
| learnedNotes[i] = json_integer_value(noteJ); | |||||
| } | } | ||||
| } | } | ||||
| json_t *midiJ = json_object_get(rootJ, "midi"); | json_t *midiJ = json_object_get(rootJ, "midi"); | ||||
| if (midiJ) | if (midiJ) | ||||
| midiInput.fromJson(midiJ); | midiInput.fromJson(midiJ); | ||||
| json_t *velocityJ = json_object_get(rootJ, "velocity"); | |||||
| if (velocityJ) | |||||
| velocity = json_boolean_value(velocityJ); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -221,9 +229,24 @@ struct MIDITriggerToCVInterfaceWidget : ModuleWidget { | |||||
| midiWidget->midiIO = &module->midiInput; | midiWidget->midiIO = &module->midiInput; | ||||
| midiWidget->createGridChoices(); | midiWidget->createGridChoices(); | ||||
| addChild(midiWidget); | addChild(midiWidget); | ||||
| } | |||||
| void appendContextMenu(Menu *menu) override { | |||||
| MIDITriggerToCVInterface *module = dynamic_cast<MIDITriggerToCVInterface*>(this->module); | |||||
| struct VelocityItem : MenuItem { | |||||
| MIDITriggerToCVInterface *module; | |||||
| void onAction(EventAction &e) override { | |||||
| module->velocity ^= true; | |||||
| } | |||||
| }; | |||||
| menu->addChild(MenuEntry::create()); | |||||
| VelocityItem *velocityItem = MenuItem::create<VelocityItem>("Velocity", CHECKMARK(module->velocity)); | |||||
| velocityItem->module = module; | |||||
| menu->addChild(velocityItem); | |||||
| } | } | ||||
| }; | }; | ||||
| Model *modelMIDITriggerToCVInterface = Model::create<MIDITriggerToCVInterface, MIDITriggerToCVInterfaceWidget>("Core", "MIDITriggerToCVInterface", "MIDI-TRIG", MIDI_TAG, EXTERNAL_TAG); | |||||
| Model *modelMIDITriggerToCVInterface = Model::create<MIDITriggerToCVInterface, MIDITriggerToCVInterfaceWidget>("Core", "MIDITriggerToCVInterface", "MIDI-Trig", MIDI_TAG, EXTERNAL_TAG); | |||||