| @@ -35,7 +35,12 @@ struct MIDI_Gate : Module { | |||||
| // Settings | // Settings | ||||
| bool velocityMode; | |||||
| enum VelocityMode { | |||||
| FIXED_MODE, | |||||
| VELOCITY_MODE, | |||||
| AFTERTOUCH_MODE, | |||||
| }; | |||||
| VelocityMode velocityMode; | |||||
| bool mpeMode; | bool mpeMode; | ||||
| bool trigMode; | bool trigMode; | ||||
| @@ -55,7 +60,7 @@ struct MIDI_Gate : Module { | |||||
| } | } | ||||
| learningId = -1; | learningId = -1; | ||||
| midiInput.reset(); | midiInput.reset(); | ||||
| velocityMode = false; | |||||
| velocityMode = FIXED_MODE; | |||||
| mpeMode = false; | mpeMode = false; | ||||
| trigMode = false; | trigMode = false; | ||||
| panic(); | panic(); | ||||
| @@ -65,7 +70,7 @@ struct MIDI_Gate : Module { | |||||
| for (int i = 0; i < 16; i++) { | for (int i = 0; i < 16; i++) { | ||||
| for (int c = 0; c < 16; c++) { | for (int c = 0; c < 16; c++) { | ||||
| gates[i][c] = false; | gates[i][c] = false; | ||||
| velocities[i][c] = 0; | |||||
| velocities[i][c] = 127; | |||||
| trigPulses[i][c].reset(); | trigPulses[i][c].reset(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -83,7 +88,9 @@ struct MIDI_Gate : Module { | |||||
| for (int c = 0; c < channels; c++) { | for (int c = 0; c < channels; c++) { | ||||
| bool pulse = trigPulses[i][c].process(args.sampleTime); | bool pulse = trigPulses[i][c].process(args.sampleTime); | ||||
| bool gate = pulse || (!trigMode && gates[i][c]); | bool gate = pulse || (!trigMode && gates[i][c]); | ||||
| float velocity = gate ? velocityMode ? (velocities[i][c] / 127.f) : 1.f : 0.f; | |||||
| float velocity = gate ? 1.f : 0.f; | |||||
| if (velocityMode != FIXED_MODE) | |||||
| velocity *= velocities[i][c] / 127.f; | |||||
| outputs[GATE_OUTPUTS + i].setVoltage(velocity * 10.f, c); | outputs[GATE_OUTPUTS + i].setVoltage(velocity * 10.f, c); | ||||
| } | } | ||||
| outputs[GATE_OUTPUTS + i].setChannels(channels); | outputs[GATE_OUTPUTS + i].setChannels(channels); | ||||
| @@ -98,14 +105,38 @@ struct MIDI_Gate : Module { | |||||
| } break; | } break; | ||||
| // note on | // note on | ||||
| case 0x9: { | case 0x9: { | ||||
| if (msg.getValue() > 0) { | |||||
| pressNote(msg.getChannel(), msg.getNote(), msg.getValue()); | |||||
| uint8_t velocity = msg.getValue(); | |||||
| if (velocity > 0) { | |||||
| pressNote(msg.getChannel(), msg.getNote(), velocity); | |||||
| } | } | ||||
| else { | else { | ||||
| // Note-on event with velocity 0 is an alternative for note-off event. | // Note-on event with velocity 0 is an alternative for note-off event. | ||||
| releaseNote(msg.getChannel(), msg.getNote()); | releaseNote(msg.getChannel(), msg.getNote()); | ||||
| } | } | ||||
| } break; | } break; | ||||
| // polyphonic pressure/aftertouch | |||||
| case 0xa: { | |||||
| if (velocityMode == AFTERTOUCH_MODE) { | |||||
| uint8_t c = mpeMode ? msg.getChannel() : 0; | |||||
| uint8_t note = msg.getNote(); | |||||
| uint8_t velocity = msg.getValue(); | |||||
| for (int i = 0; i < 16; i++) { | |||||
| if (learnedNotes[i] == note) { | |||||
| velocities[i][c] = velocity; | |||||
| } | |||||
| } | |||||
| } | |||||
| } break; | |||||
| // channel pressure/aftertouch | |||||
| case 0xd: { | |||||
| if (velocityMode == AFTERTOUCH_MODE) { | |||||
| uint8_t c = mpeMode ? msg.getChannel() : 0; | |||||
| uint8_t velocity = msg.getNote(); | |||||
| for (int i = 0; i < 16; i++) { | |||||
| velocities[i][c] = velocity; | |||||
| } | |||||
| } | |||||
| } break; | |||||
| default: break; | default: break; | ||||
| } | } | ||||
| } | } | ||||
| @@ -121,7 +152,8 @@ struct MIDI_Gate : Module { | |||||
| for (int i = 0; i < 16; i++) { | for (int i = 0; i < 16; i++) { | ||||
| if (learnedNotes[i] == note) { | if (learnedNotes[i] == note) { | ||||
| gates[i][c] = true; | gates[i][c] = true; | ||||
| velocities[i][c] = vel; | |||||
| if (velocityMode == VELOCITY_MODE) | |||||
| velocities[i][c] = vel; | |||||
| trigPulses[i][c].trigger(1e-3f); | trigPulses[i][c].trigger(1e-3f); | ||||
| } | } | ||||
| } | } | ||||
| @@ -153,12 +185,11 @@ struct MIDI_Gate : Module { | |||||
| json_t* notesJ = json_array(); | json_t* notesJ = json_array(); | ||||
| for (int i = 0; i < 16; i++) { | for (int i = 0; i < 16; i++) { | ||||
| json_t* noteJ = json_integer(learnedNotes[i]); | |||||
| json_array_append_new(notesJ, noteJ); | |||||
| json_array_append_new(notesJ, json_integer(learnedNotes[i])); | |||||
| } | } | ||||
| json_object_set_new(rootJ, "notes", notesJ); | json_object_set_new(rootJ, "notes", notesJ); | ||||
| json_object_set_new(rootJ, "velocity", json_boolean(velocityMode)); | |||||
| json_object_set_new(rootJ, "velocity", json_integer(velocityMode)); | |||||
| json_object_set_new(rootJ, "mpeMode", json_boolean(mpeMode)); | json_object_set_new(rootJ, "mpeMode", json_boolean(mpeMode)); | ||||
| @@ -180,8 +211,11 @@ struct MIDI_Gate : Module { | |||||
| } | } | ||||
| json_t* velocityJ = json_object_get(rootJ, "velocity"); | json_t* velocityJ = json_object_get(rootJ, "velocity"); | ||||
| if (velocityJ) | |||||
| velocityMode = json_boolean_value(velocityJ); | |||||
| // Boolean in Rack <=v2.6.4 | |||||
| if (json_is_true(velocityJ)) | |||||
| velocityMode = VELOCITY_MODE; | |||||
| else if (json_is_integer(velocityJ)) | |||||
| velocityMode = VelocityMode(json_integer_value(velocityJ)); | |||||
| json_t* mpeModeJ = json_object_get(rootJ, "mpeMode"); | json_t* mpeModeJ = json_object_get(rootJ, "mpeMode"); | ||||
| if (mpeModeJ) | if (mpeModeJ) | ||||
| @@ -238,7 +272,11 @@ struct MIDI_GateWidget : ModuleWidget { | |||||
| menu->addChild(new MenuSeparator); | menu->addChild(new MenuSeparator); | ||||
| menu->addChild(createBoolPtrMenuItem("Velocity mode", "", &module->velocityMode)); | |||||
| menu->addChild(createIndexPtrSubmenuItem("Gate amplitude", { | |||||
| "10V", | |||||
| "Velocity", | |||||
| "Aftertouch", | |||||
| }, &module->velocityMode)); | |||||
| menu->addChild(createBoolPtrMenuItem("MPE mode", "", &module->mpeMode)); | menu->addChild(createBoolPtrMenuItem("MPE mode", "", &module->mpeMode)); | ||||