#include "Core.hpp" #include "midi.hpp" #include struct QuadMIDIToCVInterface : Module { enum ParamIds { NUM_PARAMS }; enum InputIds { NUM_INPUTS }; enum OutputIds { ENUMS(CV_OUTPUT, 4), ENUMS(GATE_OUTPUT, 4), ENUMS(VELOCITY_OUTPUT, 4), ENUMS(AFTERTOUCH_OUTPUT, 4), NUM_OUTPUTS }; enum LightIds { NUM_LIGHTS }; MidiInputQueue midiInput; 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 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: { } 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; } } }; struct QuadMIDIToCVInterfaceWidget : ModuleWidget { QuadMIDIToCVInterfaceWidget(QuadMIDIToCVInterface *module) : ModuleWidget(module) { setPanel(SVG::load(assetGlobal("res/Core/QuadMIDIToCVInterface.svg"))); addChild(Widget::create(Vec(RACK_GRID_WIDTH, 0))); addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); addChild(Widget::create(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); addOutput(Port::create(mm2px(Vec(3.894335, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 0)); addOutput(Port::create(mm2px(Vec(15.494659, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 0)); addOutput(Port::create(mm2px(Vec(27.094986, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 0)); addOutput(Port::create(mm2px(Vec(38.693935, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 0)); addOutput(Port::create(mm2px(Vec(3.894335, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 1)); addOutput(Port::create(mm2px(Vec(15.494659, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 1)); addOutput(Port::create(mm2px(Vec(27.094986, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 1)); addOutput(Port::create(mm2px(Vec(38.693935, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 1)); addOutput(Port::create(mm2px(Vec(3.894335, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 2)); addOutput(Port::create(mm2px(Vec(15.494659, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 2)); addOutput(Port::create(mm2px(Vec(27.094986, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 2)); addOutput(Port::create(mm2px(Vec(38.693935, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 2)); addOutput(Port::create(mm2px(Vec(3.894335, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 3)); addOutput(Port::create(mm2px(Vec(15.494659, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 3)); addOutput(Port::create(mm2px(Vec(27.094986, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 3)); addOutput(Port::create(mm2px(Vec(38.693935, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 3)); MidiWidget *midiWidget = Widget::create(mm2px(Vec(3.4009969, 14.837336))); midiWidget->box.size = mm2px(Vec(44, 28)); midiWidget->midiIO = &module->midiInput; addChild(midiWidget); } }; Model *modelQuadMIDIToCVInterface = Model::create("Core", "QuadMIDIToCVInterface", "MIDI-4", MIDI_TAG, EXTERNAL_TAG, QUAD_TAG);