From bdee8330c7968943fdf535b7737e09214b6abf79 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 22 Jan 2022 08:34:20 -0500 Subject: [PATCH] Add "Pitch bend range" setting to MIDI to CV module. --- src/core/MIDI_CV.cpp | 59 ++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/src/core/MIDI_CV.cpp b/src/core/MIDI_CV.cpp index e850cc5f..1030ab11 100644 --- a/src/core/MIDI_CV.cpp +++ b/src/core/MIDI_CV.cpp @@ -35,7 +35,10 @@ struct MIDI_CV : Module { midi::InputQueue midiInput; + /** Number of semitones to bend up/down by pitch wheel */ + float pwRange; bool smooth; + int clockDivision; int channels; enum PolyMode { ROTATE_MODE, @@ -47,7 +50,6 @@ struct MIDI_CV : Module { PolyMode polyMode; uint32_t clock = 0; - int clockDivision; bool pedal; // Indexed by channel @@ -102,6 +104,7 @@ struct MIDI_CV : Module { smooth = true; channels = 1; polyMode = ROTATE_MODE; + pwRange = 2; clockDivision = 24; panic(); midiInput.reset(); @@ -130,30 +133,19 @@ struct MIDI_CV : Module { processMessage(msg); } - outputs[PITCH_OUTPUT].setChannels(channels); - outputs[GATE_OUTPUT].setChannels(channels); - outputs[VELOCITY_OUTPUT].setChannels(channels); - outputs[AFTERTOUCH_OUTPUT].setChannels(channels); - outputs[RETRIGGER_OUTPUT].setChannels(channels); - for (int c = 0; c < channels; c++) { - outputs[PITCH_OUTPUT].setVoltage((notes[c] - 60.f) / 12.f, c); - outputs[GATE_OUTPUT].setVoltage(gates[c] ? 10.f : 0.f, c); - outputs[VELOCITY_OUTPUT].setVoltage(rescale(velocities[c], 0, 127, 0.f, 10.f), c); - outputs[AFTERTOUCH_OUTPUT].setVoltage(rescale(aftertouches[c], 0, 127, 0.f, 10.f), c); - outputs[RETRIGGER_OUTPUT].setVoltage(retriggerPulses[c].process(args.sampleTime) ? 10.f : 0.f, c); - } - - // Set pitch and mod wheel + // Set pitch wheel and mod wheel int wheelChannels = (polyMode == MPE_MODE) ? 16 : 1; + float pwValues[16] = {}; outputs[PW_OUTPUT].setChannels(wheelChannels); outputs[MOD_OUTPUT].setChannels(wheelChannels); for (int c = 0; c < wheelChannels; c++) { - float pw = ((int) pws[c] - 8192) / 8191.f; + float pw = (int16_t(pws[c]) - 8192) / 8191.f; pw = clamp(pw, -1.f, 1.f); if (smooth) pw = pwFilters[c].process(args.sampleTime, pw); else pwFilters[c].out = pw; + pwValues[c] = pw; outputs[PW_OUTPUT].setVoltage(pw * 5.f); float mod = mods[c] / 127.f; @@ -165,6 +157,23 @@ struct MIDI_CV : Module { outputs[MOD_OUTPUT].setVoltage(mod * 10.f); } + // Set note outputs + outputs[PITCH_OUTPUT].setChannels(channels); + outputs[GATE_OUTPUT].setChannels(channels); + outputs[VELOCITY_OUTPUT].setChannels(channels); + outputs[AFTERTOUCH_OUTPUT].setChannels(channels); + outputs[RETRIGGER_OUTPUT].setChannels(channels); + for (int c = 0; c < channels; c++) { + float pw = pwValues[(polyMode == MPE_MODE) ? c : 0]; + float pitch = (notes[c] - 60.f + pw * pwRange) / 12.f; + outputs[PITCH_OUTPUT].setVoltage(pitch, c); + outputs[GATE_OUTPUT].setVoltage(gates[c] ? 10.f : 0.f, c); + outputs[VELOCITY_OUTPUT].setVoltage(rescale(velocities[c], 0, 127, 0.f, 10.f), c); + outputs[AFTERTOUCH_OUTPUT].setVoltage(rescale(aftertouches[c], 0, 127, 0.f, 10.f), c); + outputs[RETRIGGER_OUTPUT].setVoltage(retriggerPulses[c].process(args.sampleTime) ? 10.f : 0.f, c); + } + + // Set clock and transport outputs outputs[CLOCK_OUTPUT].setVoltage(clockPulse.process(args.sampleTime) ? 10.f : 0.f); outputs[CLOCK_DIV_OUTPUT].setVoltage(clockDividerPulse.process(args.sampleTime) ? 10.f : 0.f); outputs[START_OUTPUT].setVoltage(startPulse.process(args.sampleTime) ? 10.f : 0.f); @@ -426,6 +435,7 @@ struct MIDI_CV : Module { json_t* dataToJson() override { json_t* rootJ = json_object(); + json_object_set_new(rootJ, "pwRange", json_real(pwRange)); json_object_set_new(rootJ, "smooth", json_boolean(smooth)); json_object_set_new(rootJ, "channels", json_integer(channels)); json_object_set_new(rootJ, "polyMode", json_integer(polyMode)); @@ -440,6 +450,13 @@ struct MIDI_CV : Module { } void dataFromJson(json_t* rootJ) override { + json_t* pwRangeJ = json_object_get(rootJ, "pwRange"); + if (pwRangeJ) + pwRange = json_number_value(pwRangeJ); + // For backwards compatibility, set to 0 if undefined in JSON. + else + pwRange = 0; + json_t* smoothJ = json_object_get(rootJ, "smooth"); if (smoothJ) smooth = json_boolean_value(smoothJ); @@ -510,6 +527,16 @@ struct MIDI_CVWidget : ModuleWidget { menu->addChild(new MenuSeparator); + static const std::vector pwRanges = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 24, 36, 48}; + menu->addChild(createSubmenuItem("Pitch bend range", string::f("%g", module->pwRange), [=](Menu* menu) { + for (size_t i = 0; i < pwRanges.size(); i++) { + menu->addChild(createCheckMenuItem(string::f("%g", pwRanges[i]), "", + [=]() {return module->pwRange == pwRanges[i];}, + [=]() {module->pwRange = pwRanges[i];} + )); + } + })); + menu->addChild(createBoolPtrMenuItem("Smooth pitch/mod wheel", "", &module->smooth)); static const std::vector clockDivisions = {24 * 4, 24 * 2, 24, 24 / 2, 24 / 4, 24 / 8, 2, 1};