|
|
@@ -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<float> 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<int> clockDivisions = {24 * 4, 24 * 2, 24, 24 / 2, 24 / 4, 24 / 8, 2, 1}; |
|
|
|