Browse Source

Refactor MIDI-4, remove retriggering on GATE output

tags/v0.6.0
Andrew Belt 7 years ago
parent
commit
9b9f2a9f6f
1 changed files with 113 additions and 115 deletions
  1. +113
    -115
      src/Core/QuadMIDIToCVInterface.cpp

+ 113
- 115
src/Core/QuadMIDIToCVInterface.cpp View File

@@ -1,5 +1,6 @@
#include "Core.hpp" #include "Core.hpp"
#include "midi.hpp" #include "midi.hpp"
#include "dsp/digital.hpp"


#include <algorithm> #include <algorithm>


@@ -26,10 +27,10 @@ struct QuadMIDIToCVInterface : Module {


enum PolyMode { enum PolyMode {
ROTATE_MODE, 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, REUSE_MODE,
RESET_MODE,
RESET_MODE,
REASSIGN_MODE, REASSIGN_MODE,
UNISON_MODE, UNISON_MODE,
NUM_MODES NUM_MODES
@@ -42,18 +43,15 @@ struct QuadMIDIToCVInterface : Module {
}; };


NoteData noteData[128]; NoteData noteData[128];
// cachedNotes : UNISON_MODE and REASSIGN_MODE cache all played notes. The other polyModes cache stealed notes (after 4th one).
std::vector<uint8_t> cachedNotes;
// cachedNotes : UNISON_MODE and REASSIGN_MODE cache all played notes. The other polyModes cache stolen notes (after the 4th one).
std::vector<uint8_t> cachedNotes;
uint8_t notes[4]; uint8_t notes[4];
bool gates[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; bool pedal;
int rotateIndex; 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) { QuadMIDIToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS), cachedNotes(128) {
onReset(); onReset();
@@ -80,34 +78,34 @@ struct QuadMIDIToCVInterface : Module {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
notes[i] = 60; notes[i] = 60;
gates[i] = false; gates[i] = false;
pedalgates[i] = false;
pedalgates[i] = false;
} }
pedal = false; pedal = false;
rotateIndex = -1; 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; nowIndex = 0;
if (!(gates[nowIndex] || pedalgates[nowIndex])) {
if (!(gates[nowIndex] || pedalgates[nowIndex])) {
stealIndex = nowIndex; stealIndex = nowIndex;
return nowIndex; return nowIndex;
}
}
} }
// All taken = steal (stealIndex always rotate)
stealIndex ++;
// All taken = steal (stealIndex always rotates)
stealIndex++;
if (stealIndex > 3) if (stealIndex > 3)
stealIndex = 0;
stealIndex = 0;
if ((polyMode < REASSIGN_MODE) && (gates[stealIndex])) if ((polyMode < REASSIGN_MODE) && (gates[stealIndex]))
cachedNotes.push_back(notes[stealIndex]); cachedNotes.push_back(notes[stealIndex]);
return stealIndex; return stealIndex;
} }


void pressNote(uint8_t note) { void pressNote(uint8_t note) {
// Set notes and gates
// Set notes and gates
switch (polyMode) { switch (polyMode) {
case ROTATE_MODE: { case ROTATE_MODE: {
rotateIndex = getPolyIndex(rotateIndex); rotateIndex = getPolyIndex(rotateIndex);
@@ -141,8 +139,7 @@ struct QuadMIDIToCVInterface : Module {
notes[i] = note; notes[i] = note;
gates[i] = true; gates[i] = true;
pedalgates[i] = pedal; pedalgates[i] = pedal;
//...it could be just "legato" for Unison mode without this...
reTrigger[i].trigger(1e-3);
// reTrigger[i].trigger(1e-3);
} }
return; return;
} break; } break;
@@ -150,104 +147,99 @@ struct QuadMIDIToCVInterface : Module {
default: break; default: break;
} }
// Set notes and gates // 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; notes[rotateIndex] = note;
gates[rotateIndex] = true; gates[rotateIndex] = true;
pedalgates[rotateIndex] = pedal; pedalgates[rotateIndex] = pedal;
}
}


void releaseNote(uint8_t note) { 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) { switch (polyMode) {
case REASSIGN_MODE: {
int held = static_cast<int>(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: { 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; } 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() { void pressPedal() {
pedal = true; 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() { 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<int>(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 { void step() override {
@@ -258,7 +250,7 @@ struct QuadMIDIToCVInterface : Module {


for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
uint8_t lastNote = notes[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[CV_OUTPUT + i].value = (lastNote - 60) / 12.f;
outputs[GATE_OUTPUT + i].value = lastGate ? 10.f : 0.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); 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) { void processMessage(MidiMessage msg) {
// filter MIDI channel // filter MIDI channel
if ((midiInput.channel > -1) && (midiInput.channel != msg.channel()))
return;
if ((midiInput.channel > -1) && (midiInput.channel != msg.channel()))
return;
switch (msg.status()) { switch (msg.status()) {
// note off // note off
case 0x8: { case 0x8: {
@@ -358,7 +350,13 @@ struct QuadMIDIToCVInterfaceWidget : ModuleWidget {


menu->addChild(MenuEntry::create()); menu->addChild(MenuEntry::create());
menu->addChild(MenuLabel::create("Polyphony mode")); menu->addChild(MenuLabel::create("Polyphony mode"));
std::vector<std::string> polyModeNames = {"Rotate", "Reset", "Reassign", "Unison"};
std::vector<std::string> polyModeNames = {
"Rotate",
"Reuse",
"Reset",
"Reassign",
"Unison"
};
for (int i = 0; i < QuadMIDIToCVInterface::NUM_MODES; i++) { for (int i = 0; i < QuadMIDIToCVInterface::NUM_MODES; i++) {
PolyphonyItem *item = MenuItem::create<PolyphonyItem>(polyModeNames[i], CHECKMARK(module->polyMode == i)); PolyphonyItem *item = MenuItem::create<PolyphonyItem>(polyModeNames[i], CHECKMARK(module->polyMode == i));
item->module = module; item->module = module;


Loading…
Cancel
Save