| @@ -21,8 +21,10 @@ struct MIDITriggerToCVInterface : Module { | |||
| bool gates[16]; | |||
| float gateTimes[16]; | |||
| uint8_t velocities[16]; | |||
| int learningId = -1; | |||
| uint8_t learnedNotes[16] = {}; | |||
| bool velocity = false; | |||
| MIDITriggerToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { | |||
| onReset(); | |||
| @@ -37,7 +39,7 @@ struct MIDITriggerToCVInterface : Module { | |||
| learningId = -1; | |||
| } | |||
| void pressNote(uint8_t note) { | |||
| void pressNote(uint8_t note, uint8_t vel) { | |||
| // Learn | |||
| if (learningId >= 0) { | |||
| learnedNotes[learningId] = note; | |||
| @@ -48,6 +50,7 @@ struct MIDITriggerToCVInterface : Module { | |||
| if (learnedNotes[i] == note) { | |||
| gates[i] = true; | |||
| gateTimes[i] = 1e-3f; | |||
| velocities[i] = vel; | |||
| } | |||
| } | |||
| } | |||
| @@ -70,7 +73,7 @@ struct MIDITriggerToCVInterface : Module { | |||
| for (int i = 0; i < 16; i++) { | |||
| 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. | |||
| // This avoids drum controllers sending a pulse with 0 ms duration. | |||
| if (!gates[i]) { | |||
| @@ -92,7 +95,7 @@ struct MIDITriggerToCVInterface : Module { | |||
| // note on | |||
| case 0x9: { | |||
| if (msg.value() > 0) { | |||
| pressNote(msg.note()); | |||
| pressNote(msg.note(), msg.value()); | |||
| } | |||
| else { | |||
| // 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, "midi", midiInput.toJson()); | |||
| json_object_set_new(rootJ, "velocity", json_boolean(velocity)); | |||
| return rootJ; | |||
| } | |||
| @@ -123,13 +127,17 @@ struct MIDITriggerToCVInterface : Module { | |||
| for (int i = 0; i < 16; i++) { | |||
| json_t *noteJ = json_array_get(notesJ, i); | |||
| if (noteJ) | |||
| learnedNotes[i] = json_integer_value(noteJ) & 0x7f; | |||
| learnedNotes[i] = json_integer_value(noteJ); | |||
| } | |||
| } | |||
| json_t *midiJ = json_object_get(rootJ, "midi"); | |||
| if (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->createGridChoices(); | |||
| 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); | |||