diff --git a/include/window.hpp b/include/window.hpp index 0186820e..81369606 100644 --- a/include/window.hpp +++ b/include/window.hpp @@ -10,14 +10,24 @@ #include +/** Remaps Ctrl to Cmd on Mac +Use this instead of GLFW_MOD_CONTROL, since Cmd should be used on Mac in place of Ctrl on Linux/Windows. +*/ #ifdef ARCH_MAC - #define WINDOW_MOD GLFW_MOD_SUPER - #define WINDOW_MOD_KEY_NAME "Cmd" + #define WINDOW_MOD_CTRL GLFW_MOD_SUPER + #define WINDOW_MOD_CTRL_NAME "Cmd" #else - #define WINDOW_MOD GLFW_MOD_CONTROL - #define WINDOW_MOD_KEY_NAME "Ctrl" + #define WINDOW_MOD_CTRL GLFW_MOD_CONTROL + #define WINDOW_MOD_CTRL_NAME "Ctrl" #endif +/** Filters actual mod keys from the mod flags. +Use this if you don't care about GLFW_MOD_CAPS_LOCK and GLFW_MOD_NUM_LOCK. +Example usage: + if ((e.mod & WINDOW_MOD_MASK) == (WINDOW_MOD | GLFW_MOD_SHIFT)) ... +*/ +#define WINDOW_MOD_MASK (GLFW_MOD_SHIFT | GLFW_MOD_CONTROL | GLFW_MOD_ALT | GLFW_MOD_SUPER) + namespace rack { @@ -70,8 +80,10 @@ struct Window { void close(); void cursorLock(); void cursorUnlock(); - bool isModPressed(); - bool isShiftPressed(); + /** Gets the current keyboard mod state + Don't call this from a Key event. Simply use `e.mods` instead. + */ + int getMods(); math::Vec getWindowSize(); void setWindowSize(math::Vec size); math::Vec getWindowPos(); diff --git a/res/Core/CV-CC.svg b/res/Core/CV-CC.svg new file mode 100644 index 00000000..e8a061f3 --- /dev/null +++ b/res/Core/CV-CC.svg @@ -0,0 +1,249 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/Core/CV-Gate.svg b/res/Core/CV-Gate.svg new file mode 100644 index 00000000..7e1067a2 --- /dev/null +++ b/res/Core/CV-Gate.svg @@ -0,0 +1,146 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Core/CV_CC.cpp b/src/Core/CV_CC.cpp new file mode 100644 index 00000000..fce7d450 --- /dev/null +++ b/src/Core/CV_CC.cpp @@ -0,0 +1,117 @@ +#include "Core.hpp" + + +template +struct CCMidiOutput : midi::Output { + int ccs[N]; + int lastValues[N]; + + CCMidiOutput() { + reset(); + } + + void reset() { + for (int n = 0; n < N; n++) { + ccs[n] = n; + lastValues[n] = -1; + } + } + + void setCC(int cc, int n) { + ccs[n] = cc; + } + + void setValue(int value, int n) { + if (value == lastValues[n]) + return; + lastValues[n] = value; + // CC + midi::Message m; + m.setStatus(0xb); + m.setNote(ccs[n]); + m.setValue(value); + sendMessage(m); + } +}; + + +struct CV_CC : Module { + enum ParamIds { + NUM_PARAMS + }; + enum InputIds { + ENUMS(CC_INPUTS, 16), + NUM_INPUTS + }; + enum OutputIds { + NUM_OUTPUTS + }; + enum LightIds { + NUM_LIGHTS + }; + + CCMidiOutput<16> midiOutput; + float rateLimiterPhase = 0.f; + + CV_CC() { + config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); + } + + void step() override { + const float rateLimiterPeriod = 0.010f; + rateLimiterPhase += app()->engine->getSampleTime() / rateLimiterPeriod; + if (rateLimiterPhase >= 1.f) { + rateLimiterPhase -= 1.f; + } + else { + return; + } + + for (int n = 0; n < 16; n++) { + int value = (int) std::round(inputs[CC_INPUTS + n].getVoltage() / 10.f * 127); + value = clamp(value, 0, 127); + midiOutput.setValue(value, n); + } + } +}; + + +struct CV_CCWidget : ModuleWidget { + CV_CCWidget(CV_CC *module) { + setModule(module); + setPanel(SVG::load(asset::system("res/Core/CV-CC.svg"))); + + addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + + addInput(createInputCentered(mm2px(Vec(8, 77)), module, CV_CC::CC_INPUTS + 0)); + addInput(createInputCentered(mm2px(Vec(20, 77)), module, CV_CC::CC_INPUTS + 1)); + addInput(createInputCentered(mm2px(Vec(31, 77)), module, CV_CC::CC_INPUTS + 2)); + addInput(createInputCentered(mm2px(Vec(43, 77)), module, CV_CC::CC_INPUTS + 3)); + addInput(createInputCentered(mm2px(Vec(8, 89)), module, CV_CC::CC_INPUTS + 4)); + addInput(createInputCentered(mm2px(Vec(20, 89)), module, CV_CC::CC_INPUTS + 5)); + addInput(createInputCentered(mm2px(Vec(31, 89)), module, CV_CC::CC_INPUTS + 6)); + addInput(createInputCentered(mm2px(Vec(43, 89)), module, CV_CC::CC_INPUTS + 7)); + addInput(createInputCentered(mm2px(Vec(8, 101)), module, CV_CC::CC_INPUTS + 8)); + addInput(createInputCentered(mm2px(Vec(20, 101)), module, CV_CC::CC_INPUTS + 9)); + addInput(createInputCentered(mm2px(Vec(31, 101)), module, CV_CC::CC_INPUTS + 10)); + addInput(createInputCentered(mm2px(Vec(43, 101)), module, CV_CC::CC_INPUTS + 11)); + addInput(createInputCentered(mm2px(Vec(8, 112)), module, CV_CC::CC_INPUTS + 12)); + addInput(createInputCentered(mm2px(Vec(20, 112)), module, CV_CC::CC_INPUTS + 13)); + addInput(createInputCentered(mm2px(Vec(31, 112)), module, CV_CC::CC_INPUTS + 14)); + addInput(createInputCentered(mm2px(Vec(43, 112)), module, CV_CC::CC_INPUTS + 15)); + + MidiWidget *midiWidget = createWidget(mm2px(Vec(3.4, 14.839))); + midiWidget->box.size = mm2px(Vec(44, 54.667)); + if (module) + midiWidget->midiIO = &module->midiOutput; + // midiWidget->createGridChoices(); + addChild(midiWidget); + } +}; + + +Model *modelCV_CC = createModel("CV-CC"); + diff --git a/src/Core/CV_Gate.cpp b/src/Core/CV_Gate.cpp new file mode 100644 index 00000000..21e5d182 --- /dev/null +++ b/src/Core/CV_Gate.cpp @@ -0,0 +1,159 @@ +#include "Core.hpp" + + +template +struct GateMidiOutput : midi::Output { + int vels[N]; + bool lastGates[N]; + int notes[N]; + + GateMidiOutput() { + reset(); + } + + void reset() { + for (int n = 0; n < N; n++) { + vels[n] = 100; + lastGates[n] = false; + notes[n] = 60 + n; + } + } + + void panic() { + reset(); + // Send all note off commands + for (int note = 0; note <= 127; note++) { + // Note off + midi::Message m; + m.setStatus(0x8); + m.setNote(note); + m.setValue(0); + sendMessage(m); + } + } + + void setVelocity(int vel, int n) { + vels[n] = vel; + } + + void setGate(bool gate, int n) { + if (gate && !lastGates[n]) { + // Note on + midi::Message m; + m.setStatus(0x9); + m.setNote(notes[n]); + m.setValue(vels[n]); + sendMessage(m); + } + else if (!gate && lastGates[n]) { + // Note off + midi::Message m; + m.setStatus(0x8); + m.setNote(notes[n]); + m.setValue(vels[n]); + sendMessage(m); + } + lastGates[n] = gate; + } + + void setNote(int note, int n) { + if (note == notes[n]) + return; + if (lastGates[n]) { + // Note off + midi::Message m1; + m1.setStatus(0x8); + m1.setNote(notes[n]); + m1.setValue(vels[n]); + sendMessage(m1); + // Note on + midi::Message m2; + m2.setStatus(0x9); + m2.setNote(note); + m2.setValue(vels[n]); + sendMessage(m2); + } + notes[n] = note; + } +}; + + +struct CV_Gate : Module { + enum ParamIds { + NUM_PARAMS + }; + enum InputIds { + ENUMS(GATE_INPUTS, 16), + NUM_INPUTS + }; + enum OutputIds { + NUM_OUTPUTS + }; + enum LightIds { + NUM_LIGHTS + }; + + GateMidiOutput<16> midiOutput; + bool velocityMode = false; + + CV_Gate() { + config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); + } + + void step() override { + for (int n = 0; n < 16; n++) { + if (velocityMode) { + int vel = (int) std::round(inputs[GATE_INPUTS + n].getVoltage() / 10.f * 127); + vel = clamp(vel, 0, 127); + midiOutput.setVelocity(vel, n); + midiOutput.setGate(vel > 0, n); + } + else { + bool gate = inputs[GATE_INPUTS + n].getVoltage() >= 1.f; + midiOutput.setVelocity(100, n); + midiOutput.setGate(gate, n); + } + } + } +}; + + +struct CV_GateWidget : ModuleWidget { + CV_GateWidget(CV_Gate *module) { + setModule(module); + setPanel(SVG::load(asset::system("res/Core/CV-Gate.svg"))); + + addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + + addInput(createInputCentered(mm2px(Vec(8, 77)), module, CV_Gate::GATE_INPUTS + 0)); + addInput(createInputCentered(mm2px(Vec(20, 77)), module, CV_Gate::GATE_INPUTS + 1)); + addInput(createInputCentered(mm2px(Vec(31, 77)), module, CV_Gate::GATE_INPUTS + 2)); + addInput(createInputCentered(mm2px(Vec(43, 77)), module, CV_Gate::GATE_INPUTS + 3)); + addInput(createInputCentered(mm2px(Vec(8, 89)), module, CV_Gate::GATE_INPUTS + 4)); + addInput(createInputCentered(mm2px(Vec(20, 89)), module, CV_Gate::GATE_INPUTS + 5)); + addInput(createInputCentered(mm2px(Vec(31, 89)), module, CV_Gate::GATE_INPUTS + 6)); + addInput(createInputCentered(mm2px(Vec(43, 89)), module, CV_Gate::GATE_INPUTS + 7)); + addInput(createInputCentered(mm2px(Vec(8, 101)), module, CV_Gate::GATE_INPUTS + 8)); + addInput(createInputCentered(mm2px(Vec(20, 101)), module, CV_Gate::GATE_INPUTS + 9)); + addInput(createInputCentered(mm2px(Vec(31, 101)), module, CV_Gate::GATE_INPUTS + 10)); + addInput(createInputCentered(mm2px(Vec(43, 101)), module, CV_Gate::GATE_INPUTS + 11)); + addInput(createInputCentered(mm2px(Vec(8, 112)), module, CV_Gate::GATE_INPUTS + 12)); + addInput(createInputCentered(mm2px(Vec(20, 112)), module, CV_Gate::GATE_INPUTS + 13)); + addInput(createInputCentered(mm2px(Vec(31, 112)), module, CV_Gate::GATE_INPUTS + 14)); + addInput(createInputCentered(mm2px(Vec(43, 112)), module, CV_Gate::GATE_INPUTS + 15)); + + MidiWidget *midiWidget = createWidget(mm2px(Vec(3.4, 14.839))); + midiWidget->box.size = mm2px(Vec(44, 54.667)); + if (module) + midiWidget->midiIO = &module->midiOutput; + // midiWidget->createGridChoices(); + addChild(midiWidget); + } +}; + + +Model *modelCV_Gate = createModel("CV-Gate"); + diff --git a/src/Core/CV_MIDI.cpp b/src/Core/CV_MIDI.cpp index f4ff89a4..a4f33a7c 100644 --- a/src/Core/CV_MIDI.cpp +++ b/src/Core/CV_MIDI.cpp @@ -52,7 +52,7 @@ struct PolyphonicMidiOutput : midi::Output { } } - void setVel(int vel, int c) { + void setVelocity(int vel, int c) { vels[c] = vel; } @@ -229,16 +229,26 @@ struct CV_MIDI : Module { }; PolyphonicMidiOutput midiOutput; + float rateLimiterPhase = 0.f; CV_MIDI() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); } void step() override { + const float rateLimiterPeriod = 0.005f; + rateLimiterPhase += app()->engine->getSampleTime() / rateLimiterPeriod; + if (rateLimiterPhase >= 1.f) { + rateLimiterPhase -= 1.f; + } + else { + return; + } + for (int c = 0; c < inputs[PITCH_INPUT].getChannels(); c++) { int vel = (int) std::round(inputs[VEL_INPUT].normalize(10.f * 100 / 127, c) / 10.f * 127); vel = clamp(vel, 0, 127); - midiOutput.setVel(vel, c); + midiOutput.setVelocity(vel, c); int note = (int) std::round(inputs[PITCH_INPUT].getVoltage(c) * 12.f + 60.f); note = clamp(note, 0, 127); @@ -262,9 +272,6 @@ struct CV_MIDI : Module { mw = clamp(mw, 0, 127); midiOutput.setModWheel(mw); - bool clk = inputs[CLK_INPUT].value >= 1.f; - midiOutput.setClock(clk); - int vol = (int) std::round(inputs[VOL_INPUT].normalize(10.f) / 10.f * 127); vol = clamp(vol, 0, 127); midiOutput.setVolume(vol); @@ -273,6 +280,9 @@ struct CV_MIDI : Module { pan = clamp(pan, 0, 127); midiOutput.setPan(pan); + bool clk = inputs[CLK_INPUT].value >= 1.f; + midiOutput.setClock(clk); + bool start = inputs[START_INPUT].value >= 1.f; midiOutput.setStart(start); diff --git a/src/Core/Core.cpp b/src/Core/Core.cpp index 740bc894..a86c31f5 100644 --- a/src/Core/Core.cpp +++ b/src/Core/Core.cpp @@ -14,7 +14,7 @@ void init(rack::Plugin *p) { p->sourceUrl = "https://github.com/VCVRack/Rack"; modelAudioInterface->name = "Audio"; - modelAudioInterface->description = ""; + modelAudioInterface->description = "Sends audio and CV to/from an audio device"; modelAudioInterface->tags = {"External"}; p->addModel(modelAudioInterface); @@ -43,6 +43,16 @@ void init(rack::Plugin *p) { modelCV_MIDI->tags = {"External", "MIDI"}; p->addModel(modelCV_MIDI); + modelCV_CC->name = "CV-CC"; + modelCV_CC->description = ""; + modelCV_CC->tags = {"External", "MIDI"}; + p->addModel(modelCV_CC); + + modelCV_Gate->name = "CV-Gate"; + modelCV_Gate->description = ""; + modelCV_Gate->tags = {"External", "MIDI"}; + p->addModel(modelCV_Gate); + modelBlank->name = "Blank"; modelBlank->description = ""; modelBlank->tags = {"Blank"}; diff --git a/src/Core/Core.hpp b/src/Core/Core.hpp index c72e1f05..78bd78a6 100644 --- a/src/Core/Core.hpp +++ b/src/Core/Core.hpp @@ -11,6 +11,8 @@ extern Model *modelQuadMIDIToCVInterface; extern Model *modelMIDICCToCVInterface; extern Model *modelMIDITriggerToCVInterface; extern Model *modelCV_MIDI; +extern Model *modelCV_CC; +extern Model *modelCV_Gate; extern Model *modelBlank; extern Model *modelNotes; diff --git a/src/app/Knob.cpp b/src/app/Knob.cpp index 9a6bce8a..52d02f8f 100644 --- a/src/app/Knob.cpp +++ b/src/app/Knob.cpp @@ -67,7 +67,7 @@ void Knob::onDragMove(const event::DragMove &e) { float delta = KNOB_SENSITIVITY * -e.mouseDelta.y * speed * range; // Drag slower if Mod is held - if (app()->window->isModPressed()) + if ((app()->window->getMods() & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) delta /= 16.f; if (snap) { diff --git a/src/app/ModuleWidget.cpp b/src/app/ModuleWidget.cpp index 8bd5253b..cd6ab35c 100644 --- a/src/app/ModuleWidget.cpp +++ b/src/app/ModuleWidget.cpp @@ -16,12 +16,13 @@ namespace rack { ModuleWidget::~ModuleWidget() { - if (module) { - delete module; - } + setModule(NULL); } void ModuleWidget::setModule(Module *module) { + if (this->module) { + delete this->module; + } this->module = module; } @@ -338,7 +339,7 @@ void ModuleWidget::onHover(const event::Hover &e) { // Instead of checking key-down events, delete the module even if key-repeat hasn't fired yet and the cursor is hovering over the widget. if (glfwGetKey(app()->window->win, GLFW_KEY_DELETE) == GLFW_PRESS || glfwGetKey(app()->window->win, GLFW_KEY_BACKSPACE) == GLFW_PRESS) { - if (!app()->window->isModPressed() && !app()->window->isShiftPressed()) { + if ((app()->window->getMods() & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { ModuleWidget_removeAction(this); e.consume(NULL); return; @@ -348,8 +349,9 @@ void ModuleWidget::onHover(const event::Hover &e) { void ModuleWidget::onButton(const event::Button &e) { OpaqueWidget::onButton(e); + if (e.getConsumed() == this) { - if (e.button == 1) { + if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) { createContextMenu(); } } @@ -359,43 +361,43 @@ void ModuleWidget::onHoverKey(const event::HoverKey &e) { if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { switch (e.key) { case GLFW_KEY_I: { - if (app()->window->isModPressed() && !app()->window->isShiftPressed()) { + if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { reset(); e.consume(this); } } break; case GLFW_KEY_R: { - if (app()->window->isModPressed() && !app()->window->isShiftPressed()) { + if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { randomize(); e.consume(this); } } break; case GLFW_KEY_C: { - if (app()->window->isModPressed() && !app()->window->isShiftPressed()) { + if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { copyClipboard(); e.consume(this); } } break; case GLFW_KEY_V: { - if (app()->window->isModPressed() && !app()->window->isShiftPressed()) { + if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { pasteClipboard(); e.consume(this); } } break; case GLFW_KEY_D: { - if (app()->window->isModPressed() && !app()->window->isShiftPressed()) { + if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { app()->scene->rackWidget->cloneModule(this); e.consume(this); } } break; case GLFW_KEY_U: { - if (app()->window->isModPressed() && !app()->window->isShiftPressed()) { + if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { disconnect(); e.consume(this); } } break; case GLFW_KEY_E: { - if (app()->window->isModPressed() && !app()->window->isShiftPressed()) { + if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { ModuleWidget_bypassAction(this); e.consume(this); } @@ -436,7 +438,7 @@ struct ModuleDisconnectItem : MenuItem { ModuleWidget *moduleWidget; ModuleDisconnectItem() { text = "Disconnect cables"; - rightText = WINDOW_MOD_KEY_NAME "+U"; + rightText = WINDOW_MOD_CTRL_NAME "+U"; } void onAction(const event::Action &e) override { moduleWidget->disconnect(); @@ -447,7 +449,7 @@ struct ModuleResetItem : MenuItem { ModuleWidget *moduleWidget; ModuleResetItem() { text = "Initialize"; - rightText = WINDOW_MOD_KEY_NAME "+I"; + rightText = WINDOW_MOD_CTRL_NAME "+I"; } void onAction(const event::Action &e) override { moduleWidget->reset(); @@ -458,7 +460,7 @@ struct ModuleRandomizeItem : MenuItem { ModuleWidget *moduleWidget; ModuleRandomizeItem() { text = "Randomize"; - rightText = WINDOW_MOD_KEY_NAME "+R"; + rightText = WINDOW_MOD_CTRL_NAME "+R"; } void onAction(const event::Action &e) override { moduleWidget->randomize(); @@ -469,7 +471,7 @@ struct ModuleCopyItem : MenuItem { ModuleWidget *moduleWidget; ModuleCopyItem() { text = "Copy preset"; - rightText = WINDOW_MOD_KEY_NAME "+C"; + rightText = WINDOW_MOD_CTRL_NAME "+C"; } void onAction(const event::Action &e) override { moduleWidget->copyClipboard(); @@ -480,7 +482,7 @@ struct ModulePasteItem : MenuItem { ModuleWidget *moduleWidget; ModulePasteItem() { text = "Paste preset"; - rightText = WINDOW_MOD_KEY_NAME "+V"; + rightText = WINDOW_MOD_CTRL_NAME "+V"; } void onAction(const event::Action &e) override { moduleWidget->pasteClipboard(); @@ -511,7 +513,7 @@ struct ModuleCloneItem : MenuItem { ModuleWidget *moduleWidget; ModuleCloneItem() { text = "Duplicate"; - rightText = WINDOW_MOD_KEY_NAME "+D"; + rightText = WINDOW_MOD_CTRL_NAME "+D"; } void onAction(const event::Action &e) override { app()->scene->rackWidget->cloneModule(moduleWidget); @@ -524,7 +526,7 @@ struct ModuleBypassItem : MenuItem { text = "Bypass"; } void step() override { - rightText = WINDOW_MOD_KEY_NAME "+E"; + rightText = WINDOW_MOD_CTRL_NAME "+E"; if (!moduleWidget->module) return; if (moduleWidget->module->bypass) diff --git a/src/app/ParamWidget.cpp b/src/app/ParamWidget.cpp index 55f872b5..ef645634 100644 --- a/src/app/ParamWidget.cpp +++ b/src/app/ParamWidget.cpp @@ -119,7 +119,7 @@ void ParamWidget::fromJson(json_t *rootJ) { void ParamWidget::onButton(const event::Button &e) { // Right click to reset - if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT && !(e.mods & WINDOW_MOD) && !(e.mods & GLFW_MOD_SHIFT)) { + if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT && (e.mods & WINDOW_MOD_MASK) == 0) { if (paramQuantity && paramQuantity->isBounded()) { float oldValue = paramQuantity->getValue(); paramQuantity->reset(); @@ -141,7 +141,7 @@ void ParamWidget::onButton(const event::Button &e) { } // Shift-click to open value entry - if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT && !(e.mods & WINDOW_MOD) && (e.mods & GLFW_MOD_SHIFT)) { + if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT && (e.mods & WINDOW_MOD_MASK) == GLFW_MOD_SHIFT) { // Create ParamField MenuOverlay *overlay = new MenuOverlay; app()->scene->addChild(overlay); diff --git a/src/app/PortWidget.cpp b/src/app/PortWidget.cpp index 05ccdfd0..9f6190bc 100644 --- a/src/app/PortWidget.cpp +++ b/src/app/PortWidget.cpp @@ -72,7 +72,10 @@ void PortWidget::onButton(const event::Button &e) { void PortWidget::onDragStart(const event::DragStart &e) { // Try to grab cable on top of stack CableWidget *cable = NULL; - if (type == INPUT || !app()->window->isModPressed()) { + if (type == OUTPUT && (app()->window->getMods() & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { + // Keep cable NULL + } + else { cable = app()->scene->rackWidget->cableContainer->getTopCable(this); } diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index 64deab0e..92d415ec 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -591,11 +591,11 @@ void RackWidget::draw(NVGcontext *vg) { void RackWidget::onHover(const event::Hover &e) { // Scroll with arrow keys float arrowSpeed = 30.0; - if (app()->window->isShiftPressed() && app()->window->isModPressed()) + if ((app()->window->getMods() & WINDOW_MOD_MASK) == (WINDOW_MOD_CTRL |GLFW_MOD_SHIFT)) arrowSpeed /= 16.0; - else if (app()->window->isShiftPressed()) + else if ((app()->window->getMods() & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) arrowSpeed *= 4.0; - else if (app()->window->isModPressed()) + else if ((app()->window->getMods() & WINDOW_MOD_MASK) == GLFW_MOD_SHIFT) arrowSpeed /= 4.0; ScrollWidget *scrollWidget = app()->scene->scrollWidget; diff --git a/src/app/Scene.cpp b/src/app/Scene.cpp index 948be49f..edfa2479 100644 --- a/src/app/Scene.cpp +++ b/src/app/Scene.cpp @@ -84,49 +84,49 @@ void Scene::onHoverKey(const event::HoverKey &e) { if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { switch (e.key) { case GLFW_KEY_N: { - if ((e.mods & WINDOW_MOD) && !(e.mods & GLFW_MOD_SHIFT)) { + if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { rackWidget->reset(); e.consume(this); } } break; case GLFW_KEY_Q: { - if ((e.mods & WINDOW_MOD) && !(e.mods & GLFW_MOD_SHIFT)) { + if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { app()->window->close(); e.consume(this); } } break; case GLFW_KEY_O: { - if ((e.mods & WINDOW_MOD) && !(e.mods & GLFW_MOD_SHIFT)) { + if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { rackWidget->loadDialog(); e.consume(this); } - if ((e.mods & WINDOW_MOD) && (e.mods & GLFW_MOD_SHIFT)) { + if ((e.mods & WINDOW_MOD_MASK) == (WINDOW_MOD_CTRL | GLFW_MOD_SHIFT)) { rackWidget->revert(); e.consume(this); } } break; case GLFW_KEY_S: { - if ((e.mods & WINDOW_MOD) && !(e.mods & GLFW_MOD_SHIFT)) { + if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { rackWidget->saveDialog(); e.consume(this); } - if ((e.mods & WINDOW_MOD) && (e.mods & GLFW_MOD_SHIFT)) { + if ((e.mods & WINDOW_MOD_MASK) == (WINDOW_MOD_CTRL | GLFW_MOD_SHIFT)) { rackWidget->saveAsDialog(); e.consume(this); } } break; case GLFW_KEY_V: { - if ((e.mods & WINDOW_MOD) && !(e.mods & GLFW_MOD_SHIFT)) { + if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { rackWidget->pastePresetClipboard(); e.consume(this); } } break; case GLFW_KEY_Z: { - if ((e.mods & WINDOW_MOD) && !(e.mods & GLFW_MOD_SHIFT)) { + if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { app()->history->undo(); e.consume(this); } - if ((e.mods & WINDOW_MOD) && (e.mods & GLFW_MOD_SHIFT)) { + if ((e.mods & WINDOW_MOD_MASK) == (WINDOW_MOD_CTRL | GLFW_MOD_SHIFT)) { app()->history->redo(); e.consume(this); } diff --git a/src/app/Switch.cpp b/src/app/Switch.cpp index 8dba41b4..9769cec8 100644 --- a/src/app/Switch.cpp +++ b/src/app/Switch.cpp @@ -33,12 +33,13 @@ void Switch::onDragStart(const event::DragStart &e) { else { if (paramQuantity) { float oldValue = paramQuantity->getValue(); - // Increment value by 1, or reset back to minimum if (paramQuantity->isMax()) { + // Reset value back to minimum paramQuantity->setMin(); } else { - paramQuantity->setValue(std::floor(paramQuantity->getValue() + 1)); + // Increment value by 1 + paramQuantity->setValue(std::round(paramQuantity->getValue()) + 1.f); } float newValue = paramQuantity->getValue(); diff --git a/src/app/Toolbar.cpp b/src/app/Toolbar.cpp index 88426dac..2a114f22 100644 --- a/src/app/Toolbar.cpp +++ b/src/app/Toolbar.cpp @@ -35,7 +35,7 @@ struct MenuButton : Button { struct NewItem : MenuItem { NewItem() { text = "New"; - rightText = "(" WINDOW_MOD_KEY_NAME "+N)"; + rightText = "(" WINDOW_MOD_CTRL_NAME "+N)"; } void onAction(const event::Action &e) override { app()->scene->rackWidget->reset(); @@ -46,7 +46,7 @@ struct NewItem : MenuItem { struct OpenItem : MenuItem { OpenItem() { text = "Open"; - rightText = "(" WINDOW_MOD_KEY_NAME "+O)"; + rightText = "(" WINDOW_MOD_CTRL_NAME "+O)"; } void onAction(const event::Action &e) override { app()->scene->rackWidget->loadDialog(); @@ -57,7 +57,7 @@ struct OpenItem : MenuItem { struct SaveItem : MenuItem { SaveItem() { text = "Save"; - rightText = "(" WINDOW_MOD_KEY_NAME "+S)"; + rightText = "(" WINDOW_MOD_CTRL_NAME "+S)"; } void onAction(const event::Action &e) override { app()->scene->rackWidget->saveDialog(); @@ -68,7 +68,7 @@ struct SaveItem : MenuItem { struct SaveAsItem : MenuItem { SaveAsItem() { text = "Save as"; - rightText = "(" WINDOW_MOD_KEY_NAME "+Shift+S)"; + rightText = "(" WINDOW_MOD_CTRL_NAME "+Shift+S)"; } void onAction(const event::Action &e) override { app()->scene->rackWidget->saveAsDialog(); @@ -109,7 +109,7 @@ struct DisconnectCablesItem : MenuItem { struct QuitItem : MenuItem { QuitItem() { text = "Quit"; - rightText = "(" WINDOW_MOD_KEY_NAME "+Q)"; + rightText = "(" WINDOW_MOD_CTRL_NAME "+Q)"; } void onAction(const event::Action &e) override { app()->window->close(); diff --git a/src/rtmidi.cpp b/src/rtmidi.cpp index a849f415..8b3dea25 100644 --- a/src/rtmidi.cpp +++ b/src/rtmidi.cpp @@ -63,8 +63,8 @@ struct RtMidiOutputDevice : midi::OutputDevice { void sendMessage(midi::Message message) override { unsigned char msg[3]; msg[0] = message.cmd; - msg[0] = message.data1; - msg[0] = message.data2; + msg[1] = message.data1; + msg[2] = message.data2; rtMidiOut->sendMessage(msg, 3); } }; diff --git a/src/ui/TextField.cpp b/src/ui/TextField.cpp index 38c419b6..7ca99e61 100644 --- a/src/ui/TextField.cpp +++ b/src/ui/TextField.cpp @@ -94,7 +94,7 @@ void TextField::onSelectKey(const event::SelectKey &e) { } } break; case GLFW_KEY_LEFT: { - if (app()->window->isModPressed()) { + if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { while (--cursor > 0) { if (text[cursor] == ' ') break; @@ -103,12 +103,12 @@ void TextField::onSelectKey(const event::SelectKey &e) { else { cursor--; } - if (!app()->window->isShiftPressed()) { + if ((e.mods & WINDOW_MOD_MASK) == 0) { selection = cursor; } } break; case GLFW_KEY_RIGHT: { - if (app()->window->isModPressed()) { + if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { while (++cursor < (int) text.size()) { if (text[cursor] == ' ') break; @@ -117,7 +117,7 @@ void TextField::onSelectKey(const event::SelectKey &e) { else { cursor++; } - if (!app()->window->isShiftPressed()) { + if ((e.mods & WINDOW_MOD_MASK) == 0) { selection = cursor; } } break; @@ -128,14 +128,14 @@ void TextField::onSelectKey(const event::SelectKey &e) { selection = cursor = text.size(); } break; case GLFW_KEY_V: { - if (app()->window->isModPressed()) { + if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { const char *newText = glfwGetClipboardString(app()->window->win); if (newText) insertText(newText); } } break; case GLFW_KEY_X: { - if (app()->window->isModPressed()) { + if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { if (cursor != selection) { int begin = std::min(cursor, selection); std::string selectedText = text.substr(begin, std::abs(selection - cursor)); @@ -145,7 +145,7 @@ void TextField::onSelectKey(const event::SelectKey &e) { } } break; case GLFW_KEY_C: { - if (app()->window->isModPressed()) { + if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { if (cursor != selection) { int begin = std::min(cursor, selection); std::string selectedText = text.substr(begin, std::abs(selection - cursor)); @@ -154,7 +154,7 @@ void TextField::onSelectKey(const event::SelectKey &e) { } } break; case GLFW_KEY_A: { - if (app()->window->isModPressed()) { + if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { selectAll(); } } break; diff --git a/src/window.cpp b/src/window.cpp index ee9461ce..336f5b91 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -66,6 +66,7 @@ static void mouseButtonCallback(GLFWwindow *win, int button, int action, int mod if (button == GLFW_MOUSE_BUTTON_LEFT) { if (mods & GLFW_MOD_CONTROL) { button = GLFW_MOUSE_BUTTON_RIGHT; + mods &= ~GLFW_MOD_CONTROL; } } #endif @@ -123,12 +124,12 @@ static void cursorEnterCallback(GLFWwindow *win, int entered) { static void scrollCallback(GLFWwindow *win, double x, double y) { Window *window = (Window*) glfwGetWindowUserPointer(win); math::Vec scrollDelta = math::Vec(x, y); -#if ARCH_LIN || ARCH_WIN - if (window->isShiftPressed()) - scrollDelta = math::Vec(y, x); -#endif scrollDelta = scrollDelta.mult(50.0); + // Flip coordinates if shift is held + if ((window->getMods() & WINDOW_MOD_MASK) == GLFW_MOD_SHIFT) + scrollDelta = scrollDelta.flip(); + app()->event->handleScroll(window->mousePos, scrollDelta); } @@ -142,7 +143,7 @@ static void keyCallback(GLFWwindow *win, int key, int scancode, int action, int app()->event->handleKey(window->mousePos, key, scancode, action, mods); // Keyboard MIDI driver - if (!(mods & (GLFW_MOD_SHIFT | GLFW_MOD_CONTROL | GLFW_MOD_ALT | GLFW_MOD_SUPER))) { + if ((mods & WINDOW_MOD_MASK) == 0) { if (action == GLFW_PRESS) { keyboard::press(key); } @@ -380,16 +381,17 @@ void Window::cursorUnlock() { } } -bool Window::isModPressed() { -#ifdef ARCH_MAC - return glfwGetKey(win, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS; -#else - return glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS; -#endif -} - -bool Window::isShiftPressed() { - return glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS; +int Window::getMods() { + int mods = 0; + if (glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS) + mods |= GLFW_MOD_SHIFT; + if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) + mods |= GLFW_MOD_CONTROL; + if (glfwGetKey(win, GLFW_KEY_LEFT_ALT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS) + mods |= GLFW_MOD_ALT; + if (glfwGetKey(win, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS) + mods |= GLFW_MOD_SUPER; + return mods; } math::Vec Window::getWindowSize() {