|
|
@@ -1,6 +1,8 @@ |
|
|
|
#include "Core.hpp" |
|
|
|
#include "midi.hpp" |
|
|
|
|
|
|
|
#include <algorithm> |
|
|
|
|
|
|
|
|
|
|
|
struct QuadMIDIToCVInterface : Module { |
|
|
|
enum ParamIds { |
|
|
@@ -10,22 +12,10 @@ struct QuadMIDIToCVInterface : Module { |
|
|
|
NUM_INPUTS |
|
|
|
}; |
|
|
|
enum OutputIds { |
|
|
|
RECT17025_OUTPUT, |
|
|
|
RECT17027_OUTPUT, |
|
|
|
RECT17029_OUTPUT, |
|
|
|
RECT17031_OUTPUT, |
|
|
|
RECT17033_OUTPUT, |
|
|
|
RECT17035_OUTPUT, |
|
|
|
RECT17037_OUTPUT, |
|
|
|
RECT17039_OUTPUT, |
|
|
|
RECT17041_OUTPUT, |
|
|
|
RECT17043_OUTPUT, |
|
|
|
RECT17045_OUTPUT, |
|
|
|
RECT17047_OUTPUT, |
|
|
|
RECT17049_OUTPUT, |
|
|
|
RECT17051_OUTPUT, |
|
|
|
RECT17053_OUTPUT, |
|
|
|
RECT17055_OUTPUT, |
|
|
|
ENUMS(CV_OUTPUT, 4), |
|
|
|
ENUMS(GATE_OUTPUT, 4), |
|
|
|
ENUMS(VELOCITY_OUTPUT, 4), |
|
|
|
ENUMS(AFTERTOUCH_OUTPUT, 4), |
|
|
|
NUM_OUTPUTS |
|
|
|
}; |
|
|
|
enum LightIds { |
|
|
@@ -34,9 +24,168 @@ struct QuadMIDIToCVInterface : Module { |
|
|
|
|
|
|
|
MidiInputQueue midiInput; |
|
|
|
|
|
|
|
QuadMIDIToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} |
|
|
|
enum PolyMode { |
|
|
|
ROTATE_MODE, |
|
|
|
RESET_MODE, |
|
|
|
REASSIGN_MODE, |
|
|
|
UNISON_MODE, |
|
|
|
NUM_MODES |
|
|
|
}; |
|
|
|
PolyMode polyMode = ROTATE_MODE; |
|
|
|
|
|
|
|
struct NoteData { |
|
|
|
uint8_t velocity = 0; |
|
|
|
uint8_t aftertouch = 0; |
|
|
|
}; |
|
|
|
|
|
|
|
NoteData noteData[128]; |
|
|
|
std::vector<uint8_t> heldNotes; |
|
|
|
uint8_t notes[4]; |
|
|
|
bool gates[4]; |
|
|
|
bool pedal; |
|
|
|
|
|
|
|
QuadMIDIToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS), heldNotes(128) { |
|
|
|
onReset(); |
|
|
|
} |
|
|
|
|
|
|
|
json_t *toJson() override { |
|
|
|
json_t *rootJ = json_object(); |
|
|
|
json_object_set_new(rootJ, "midi", midiInput.toJson()); |
|
|
|
json_object_set_new(rootJ, "polyMode", json_integer(polyMode)); |
|
|
|
return rootJ; |
|
|
|
} |
|
|
|
|
|
|
|
void fromJson(json_t *rootJ) override { |
|
|
|
json_t *midiJ = json_object_get(rootJ, "midi"); |
|
|
|
if (midiJ) |
|
|
|
midiInput.fromJson(midiJ); |
|
|
|
|
|
|
|
json_t *polyModeJ = json_object_get(rootJ, "polyMode"); |
|
|
|
if (polyModeJ) |
|
|
|
polyMode = (PolyMode) json_integer_value(polyModeJ); |
|
|
|
} |
|
|
|
|
|
|
|
void onReset() override { |
|
|
|
for (int i = 0; i < 4; i++) { |
|
|
|
notes[i] = 60; |
|
|
|
gates[i] = false; |
|
|
|
} |
|
|
|
pedal = false; |
|
|
|
} |
|
|
|
|
|
|
|
void pressNote(uint8_t note) { |
|
|
|
// Remove existing similar note |
|
|
|
auto it = std::find(heldNotes.begin(), heldNotes.end(), note); |
|
|
|
if (it != heldNotes.end()) |
|
|
|
heldNotes.erase(it); |
|
|
|
// Push note |
|
|
|
heldNotes.push_back(note); |
|
|
|
|
|
|
|
// Set notes and gates |
|
|
|
switch (polyMode) { |
|
|
|
case ROTATE_MODE: { |
|
|
|
|
|
|
|
} break; |
|
|
|
case RESET_MODE: { |
|
|
|
|
|
|
|
void step() override {} |
|
|
|
} break; |
|
|
|
case REASSIGN_MODE: { |
|
|
|
|
|
|
|
} break; |
|
|
|
case UNISON_MODE: { |
|
|
|
|
|
|
|
} break; |
|
|
|
default: break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void releaseNote(uint8_t note) { |
|
|
|
// Remove the note |
|
|
|
auto it = std::find(heldNotes.begin(), heldNotes.end(), note); |
|
|
|
if (it != heldNotes.end()) |
|
|
|
heldNotes.erase(it); |
|
|
|
// Hold note if pedal is pressed |
|
|
|
// if (pedal) |
|
|
|
// return; |
|
|
|
// // Set last note |
|
|
|
// if (!heldNotes.empty()) { |
|
|
|
// auto it2 = heldNotes.end(); |
|
|
|
// it2--; |
|
|
|
// lastNote = *it2; |
|
|
|
// gate = true; |
|
|
|
// } |
|
|
|
// else { |
|
|
|
// gate = false; |
|
|
|
// } |
|
|
|
} |
|
|
|
|
|
|
|
void pressPedal() { |
|
|
|
pedal = true; |
|
|
|
} |
|
|
|
|
|
|
|
void releasePedal() { |
|
|
|
pedal = false; |
|
|
|
releaseNote(255); |
|
|
|
} |
|
|
|
|
|
|
|
void step() override { |
|
|
|
MidiMessage msg; |
|
|
|
while (midiInput.shift(&msg)) { |
|
|
|
processMessage(msg); |
|
|
|
} |
|
|
|
|
|
|
|
for (int i = 0; i < 4; i++) { |
|
|
|
uint8_t lastNote = notes[i]; |
|
|
|
outputs[CV_OUTPUT + i].value = (lastNote - 60) / 12.f; |
|
|
|
outputs[GATE_OUTPUT + i].value = gates[i] ? 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].aftertouch, 0, 127, 0.f, 10.f); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void processMessage(MidiMessage msg) { |
|
|
|
switch (msg.status()) { |
|
|
|
// note off |
|
|
|
case 0x8: { |
|
|
|
// releaseNote(msg.data1); |
|
|
|
} break; |
|
|
|
// note on |
|
|
|
case 0x9: { |
|
|
|
if (msg.data2 > 0) { |
|
|
|
uint8_t note = msg.data1 & 0x7f; |
|
|
|
noteData[note].velocity = msg.data2; |
|
|
|
// pressNote(msg.data1); |
|
|
|
} |
|
|
|
else { |
|
|
|
// For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released. |
|
|
|
// releaseNote(msg.data1); |
|
|
|
} |
|
|
|
} break; |
|
|
|
// channel aftertouch |
|
|
|
case 0xa: { |
|
|
|
uint8_t note = msg.data1 & 0x7f; |
|
|
|
noteData[note].aftertouch = msg.data2; |
|
|
|
} break; |
|
|
|
// cc |
|
|
|
case 0xb: { |
|
|
|
processCC(msg); |
|
|
|
} break; |
|
|
|
default: break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void processCC(MidiMessage msg) { |
|
|
|
switch (msg.data1) { |
|
|
|
// sustain |
|
|
|
case 0x40: { |
|
|
|
if (msg.data2 >= 64) |
|
|
|
pressPedal(); |
|
|
|
else |
|
|
|
releasePedal(); |
|
|
|
} break; |
|
|
|
default: break; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@@ -49,22 +198,22 @@ struct QuadMIDIToCVInterfaceWidget : ModuleWidget { |
|
|
|
addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); |
|
|
|
addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); |
|
|
|
|
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::RECT17025_OUTPUT)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::RECT17027_OUTPUT)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::RECT17029_OUTPUT)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::RECT17031_OUTPUT)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::RECT17033_OUTPUT)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::RECT17035_OUTPUT)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::RECT17037_OUTPUT)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::RECT17039_OUTPUT)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::RECT17041_OUTPUT)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::RECT17043_OUTPUT)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::RECT17045_OUTPUT)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::RECT17047_OUTPUT)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::RECT17049_OUTPUT)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::RECT17051_OUTPUT)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::RECT17053_OUTPUT)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::RECT17055_OUTPUT)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 0)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 0)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 0)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 0)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 1)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 1)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 1)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 1)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 2)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 2)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 2)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 2)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 3)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 3)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 3)); |
|
|
|
addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 3)); |
|
|
|
|
|
|
|
MidiWidget *midiWidget = Widget::create<MidiWidget>(mm2px(Vec(3.4009969, 14.837336))); |
|
|
|
midiWidget->box.size = mm2px(Vec(44, 28)); |
|
|
|