diff --git a/src/Core/QuadMIDIToCVInterface.cpp b/src/Core/QuadMIDIToCVInterface.cpp index b7e27c05..ff04101d 100644 --- a/src/Core/QuadMIDIToCVInterface.cpp +++ b/src/Core/QuadMIDIToCVInterface.cpp @@ -1,5 +1,6 @@ #include "Core.hpp" #include "midi.hpp" +#include "dsp/digital.hpp" #include @@ -26,10 +27,10 @@ struct QuadMIDIToCVInterface : Module { enum PolyMode { ROTATE_MODE, - /* Added REUSE option that reuses a channel when receiving the same note. - Good when using sustain pedal so it doesn't "stack" unisons ... not sure this is the best name but it is descriptive...*/ + // Added REUSE option that reuses a channel when receiving the same note. + // Good when using sustain pedal so it doesn't "stack" unisons ... not sure this is the best name but it is descriptive... REUSE_MODE, - RESET_MODE, + RESET_MODE, REASSIGN_MODE, UNISON_MODE, NUM_MODES @@ -42,18 +43,15 @@ struct QuadMIDIToCVInterface : Module { }; NoteData noteData[128]; - // cachedNotes : UNISON_MODE and REASSIGN_MODE cache all played notes. The other polyModes cache stealed notes (after 4th one). - std::vector cachedNotes; + // cachedNotes : UNISON_MODE and REASSIGN_MODE cache all played notes. The other polyModes cache stolen notes (after the 4th one). + std::vector cachedNotes; uint8_t notes[4]; bool gates[4]; - // gates set to TRUE by pedal and current gate. FALSE by pedal. - bool pedalgates[4]; + // gates set to TRUE by pedal and current gate. FALSE by pedal. + bool pedalgates[4]; bool pedal; int rotateIndex; - int stealIndex; - - // retrigger for stolen notes (when gates already open) - PulseGenerator reTrigger[4]; + int stealIndex; QuadMIDIToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS), cachedNotes(128) { onReset(); @@ -80,34 +78,34 @@ struct QuadMIDIToCVInterface : Module { for (int i = 0; i < 4; i++) { notes[i] = 60; gates[i] = false; - pedalgates[i] = false; + pedalgates[i] = false; } pedal = false; rotateIndex = -1; - cachedNotes.clear(); + cachedNotes.clear(); } - - int getPolyIndex (int nowIndex) { - for (int i = 0; i < 4; i++) { - nowIndex ++; - if (nowIndex > 3) + + int getPolyIndex(int nowIndex) { + for (int i = 0; i < 4; i++) { + nowIndex++; + if (nowIndex > 3) nowIndex = 0; - if (!(gates[nowIndex] || pedalgates[nowIndex])) { + if (!(gates[nowIndex] || pedalgates[nowIndex])) { stealIndex = nowIndex; return nowIndex; - } + } } - // All taken = steal (stealIndex always rotate) - stealIndex ++; + // All taken = steal (stealIndex always rotates) + stealIndex++; if (stealIndex > 3) - stealIndex = 0; + stealIndex = 0; if ((polyMode < REASSIGN_MODE) && (gates[stealIndex])) cachedNotes.push_back(notes[stealIndex]); return stealIndex; } void pressNote(uint8_t note) { - // Set notes and gates + // Set notes and gates switch (polyMode) { case ROTATE_MODE: { rotateIndex = getPolyIndex(rotateIndex); @@ -141,8 +139,7 @@ struct QuadMIDIToCVInterface : Module { notes[i] = note; gates[i] = true; pedalgates[i] = pedal; - //...it could be just "legato" for Unison mode without this... - reTrigger[i].trigger(1e-3); + // reTrigger[i].trigger(1e-3); } return; } break; @@ -150,104 +147,99 @@ struct QuadMIDIToCVInterface : Module { default: break; } // Set notes and gates - if (gates[rotateIndex] || pedalgates[rotateIndex]) - reTrigger[rotateIndex].trigger(1e-3); + // if (gates[rotateIndex] || pedalgates[rotateIndex]) + // reTrigger[rotateIndex].trigger(1e-3); notes[rotateIndex] = note; gates[rotateIndex] = true; pedalgates[rotateIndex] = pedal; - } + } void releaseNote(uint8_t note) { - // Remove the note - auto it = std::find(cachedNotes.begin(), cachedNotes.end(), note); - if (it != cachedNotes.end()) - cachedNotes.erase(it); - + // Remove the note + auto it = std::find(cachedNotes.begin(), cachedNotes.end(), note); + if (it != cachedNotes.end()) + cachedNotes.erase(it); + switch (polyMode) { - case REASSIGN_MODE: { - int held = static_cast(cachedNotes.size()); - if (held > 4) - held = 4; - for (int i = 0; i < held; i++) { - if (!pedalgates[i]) - notes[i] = cachedNotes.at(i); - pedalgates[i] = pedal; - } - for (int i = held; i < 4; i++) { - gates[i] = false; - } - } break; - + case REASSIGN_MODE: { + for (int i = 0; i < 4; i++) { + if (i < (int) cachedNotes.size()) { + if (!pedalgates[i]) + notes[i] = cachedNotes[i]; + pedalgates[i] = pedal; + } + else { + gates[i] = false; + } + } + } break; + case UNISON_MODE: { - if (!cachedNotes.empty()) { - uint8_t backnote = cachedNotes.back(); - for (int i = 0; i < 4; i++) { - notes[i] = backnote; - gates[i] = true; - } - } - else { - for (int i = 0; i < 4; i++) { - gates[i] = false; - } - } + if (!cachedNotes.empty()) { + uint8_t backnote = cachedNotes.back(); + for (int i = 0; i < 4; i++) { + notes[i] = backnote; + gates[i] = true; + } + } + else { + for (int i = 0; i < 4; i++) { + gates[i] = false; + } + } + } break; + + // default ROTATE_MODE REUSE_MODE RESET_MODE + default: { + for (int i = 0; i < 4; i++) { + if (notes[i] == note) { + if (pedalgates[i]) { + gates[i] = false; + } + else if (!cachedNotes.empty()) { + notes[i] = cachedNotes.back(); + cachedNotes.pop_back(); + } + else { + gates[i] = false; + } + } + } } break; - - // default ROTATE_MODE REUSE_MODE RESET_MODE - default: { - for (int i = 0; i < 4; i++) { - if (notes[i] == note) { - if (pedalgates[i]){ - gates[i] = false; - } - else if (!cachedNotes.empty()) { - notes[i] = cachedNotes.back(); - cachedNotes.pop_back(); - } - else { - gates[i] = false; - } - } - } - } break; } - } - + } + void pressPedal() { pedal = true; - for (int i = 0; i < 4; i++) { - pedalgates[i] = gates[i]; - } + for (int i = 0; i < 4; i++) { + pedalgates[i] = gates[i]; + } } void releasePedal() { - pedal = false; - /* When pedal is off: Recover notes for still-pressed keys (if any), - ...after they were already being "cycled" out by pedal-sustained notes - */ - - for (int i = 0; i < 4; i++) { - pedalgates[i] = false; - if (!cachedNotes.empty()) { - if (polyMode < REASSIGN_MODE) { - notes[i] = cachedNotes.back(); - cachedNotes.pop_back(); - gates[i] = true; - } - } - } - if (polyMode == REASSIGN_MODE) { - int held = static_cast(cachedNotes.size()); - if (held > 4) - held = 4; - for (int i = 0; i < held; i++) { - notes[i] = cachedNotes.at(i); - gates[i] = true; - } - for (int i = held; i < 4; i++) { - gates[i] = false; - } - } + pedal = false; + // When pedal is off, recover notes for pressed keys (if any) after they were already being "cycled" out by pedal-sustained notes. + for (int i = 0; i < 4; i++) { + pedalgates[i] = false; + if (!cachedNotes.empty()) { + if (polyMode < REASSIGN_MODE) { + notes[i] = cachedNotes.back(); + cachedNotes.pop_back(); + gates[i] = true; + } + } + } + if (polyMode == REASSIGN_MODE) { + for (int i = 0; i < 4; i++) { + if (i < (int) cachedNotes.size()) { + notes[i] = cachedNotes[i]; + gates[i] = true; + } + else { + gates[i] = false; + } + } + } } void step() override { @@ -258,7 +250,7 @@ struct QuadMIDIToCVInterface : Module { for (int i = 0; i < 4; i++) { uint8_t lastNote = notes[i]; - uint8_t lastGate = ((gates[i] || pedalgates[i]) && (!(reTrigger[i].process(engineGetSampleTime())))); + uint8_t lastGate = (gates[i] || pedalgates[i]); outputs[CV_OUTPUT + i].value = (lastNote - 60) / 12.f; outputs[GATE_OUTPUT + i].value = lastGate ? 10.f : 0.f; outputs[VELOCITY_OUTPUT + i].value = rescale(noteData[lastNote].velocity, 0, 127, 0.f, 10.f); @@ -268,9 +260,9 @@ struct QuadMIDIToCVInterface : Module { void processMessage(MidiMessage msg) { // filter MIDI channel - if ((midiInput.channel > -1) && (midiInput.channel != msg.channel())) - return; - + if ((midiInput.channel > -1) && (midiInput.channel != msg.channel())) + return; + switch (msg.status()) { // note off case 0x8: { @@ -358,7 +350,13 @@ struct QuadMIDIToCVInterfaceWidget : ModuleWidget { menu->addChild(MenuEntry::create()); menu->addChild(MenuLabel::create("Polyphony mode")); - std::vector polyModeNames = {"Rotate", "Reset", "Reassign", "Unison"}; + std::vector polyModeNames = { + "Rotate", + "Reuse", + "Reset", + "Reassign", + "Unison" + }; for (int i = 0; i < QuadMIDIToCVInterface::NUM_MODES; i++) { PolyphonyItem *item = MenuItem::create(polyModeNames[i], CHECKMARK(module->polyMode == i)); item->module = module;