| @@ -20,9 +20,9 @@ ifdef ARCH_LIN | |||
| SOURCES += dep/osdialog/osdialog_gtk2.c | |||
| build/dep/osdialog/osdialog_gtk2.c.o: FLAGS += $(shell pkg-config --cflags gtk+-2.0) | |||
| LDFLAGS += -rdynamic \ | |||
| LDFLAGS += -rdynamic -Wl,--whole-archive \ | |||
| dep/lib/libGLEW.a dep/lib/libglfw3.a dep/lib/libjansson.a dep/lib/libcurl.a dep/lib/libssl.a dep/lib/libcrypto.a dep/lib/libzip.a dep/lib/libz.a dep/lib/libspeexdsp.a dep/lib/libsamplerate.a dep/lib/librtmidi.a dep/lib/librtaudio.a \ | |||
| -lpthread -lGL -ldl -lX11 -lasound -ljack \ | |||
| -Wl,--no-whole-archive -lpthread -lGL -ldl -lX11 -lasound -ljack \ | |||
| $(shell pkg-config --libs gtk+-2.0) | |||
| TARGET := Rack | |||
| endif | |||
| @@ -12,21 +12,24 @@ namespace app { | |||
| struct Knob : ParamWidget { | |||
| /** Multiplier for mouse movement to adjust knob value */ | |||
| float speed = 1.0; | |||
| float oldValue = 0.f; | |||
| /** Drag horizontally instead of vertically. */ | |||
| bool horizontal = false; | |||
| /** Enables per-sample value smoothing while dragging. */ | |||
| bool smooth = true; | |||
| /** Enable snapping at integer values */ | |||
| /** DEPRECATED. Use `ParamQuantity::snapEnabled`. */ | |||
| bool snap = false; | |||
| float snapValue = NAN; | |||
| /** Drag horizontally instead of vertically */ | |||
| bool horizontal = false; | |||
| /** Value of the knob before dragging. */ | |||
| float oldValue = 0.f; | |||
| /** Fractional value between the param's value and the dragged knob position. */ | |||
| float snapDelta = 0.f; | |||
| void init() override; | |||
| void onHover(const event::Hover& e) override; | |||
| void onButton(const event::Button& e) override; | |||
| void onDragStart(const event::DragStart& e) override; | |||
| void onDragEnd(const event::DragEnd& e) override; | |||
| void onDragMove(const event::DragMove& e) override; | |||
| void reset() override; | |||
| void randomize() override; | |||
| }; | |||
| @@ -13,9 +13,11 @@ namespace app { | |||
| /** Manages an engine::Param on a ModuleWidget. */ | |||
| struct ParamWidget : widget::OpaqueWidget { | |||
| engine::ParamQuantity* paramQuantity = NULL; | |||
| float dirtyValue = NAN; | |||
| ui::Tooltip* tooltip = NULL; | |||
| /** For triggering the Change event. `*/ | |||
| float lastValue = NAN; | |||
| virtual void init() {} | |||
| void step() override; | |||
| void draw(const DrawArgs& args) override; | |||
| @@ -26,8 +28,6 @@ struct ParamWidget : widget::OpaqueWidget { | |||
| void createContextMenu(); | |||
| void resetAction(); | |||
| virtual void reset() {} | |||
| virtual void randomize() {} | |||
| }; | |||
| @@ -19,12 +19,11 @@ struct Switch : ParamWidget { | |||
| bool momentaryPressed = false; | |||
| bool momentaryReleased = false; | |||
| void init() override; | |||
| void step() override; | |||
| void onDoubleClick(const event::DoubleClick& e) override; | |||
| void onDragStart(const event::DragStart& e) override; | |||
| void onDragEnd(const event::DragEnd& e) override; | |||
| void reset() override; | |||
| void randomize() override; | |||
| }; | |||
| @@ -65,7 +65,9 @@ struct Engine { | |||
| // Params | |||
| void setParam(Module* module, int paramId, float value); | |||
| float getParam(Module* module, int paramId); | |||
| /** Requests the parameter to smoothly change toward `value`. */ | |||
| void setSmoothParam(Module* module, int paramId, float value); | |||
| /** Returns the target value before smoothing. */ | |||
| float getSmoothParam(Module* module, int paramId); | |||
| // ParamHandles | |||
| @@ -38,13 +38,16 @@ struct ParamQuantity : Quantity { | |||
| int displayPrecision = 5; | |||
| /** An optional one-sentence description of the parameter. */ | |||
| std::string description; | |||
| /** Enables parameter resetting when the module is reset. | |||
| /** Enables parameter resetting when the module or parameter itself is reset. | |||
| */ | |||
| bool resetEnabled = true; | |||
| /** Enables parameter randomization when the module is randomized. | |||
| Unbounded (infinite) parameters are not randomizable, regardless of this setting. | |||
| */ | |||
| bool randomizeEnabled = true; | |||
| /** Rounds values to the nearest integer. */ | |||
| bool snapEnabled = false; | |||
| Param* getParam(); | |||
| /** Request to the engine to smoothly set the value */ | |||
| @@ -62,6 +62,7 @@ TParamWidget* createParam(math::Vec pos, engine::Module* module, int paramId) { | |||
| if (module) { | |||
| o->paramQuantity = module->paramQuantities[paramId]; | |||
| } | |||
| o->init(); | |||
| return o; | |||
| } | |||
| @@ -56,10 +56,12 @@ void CableWidget::setCable(engine::Cable* cable) { | |||
| app::ModuleWidget* outputModule = APP->scene->rack->getModule(cable->outputModule->id); | |||
| assert(outputModule); | |||
| outputPort = outputModule->getOutput(cable->outputId); | |||
| assert(outputPort); | |||
| app::ModuleWidget* inputModule = APP->scene->rack->getModule(cable->inputModule->id); | |||
| assert(inputModule); | |||
| inputPort = inputModule->getInput(cable->inputId); | |||
| assert(inputPort); | |||
| } | |||
| else { | |||
| outputPort = NULL; | |||
| @@ -12,6 +12,14 @@ namespace app { | |||
| static const float KNOB_SENSITIVITY = 0.0015f; | |||
| void Knob::init() { | |||
| ParamWidget::init(); | |||
| if (paramQuantity) { | |||
| if (snap) | |||
| paramQuantity->snapEnabled = true; | |||
| } | |||
| } | |||
| void Knob::onHover(const event::Hover& e) { | |||
| math::Vec c = box.size.div(2); | |||
| float dist = e.pos.minus(c).norm(); | |||
| @@ -34,9 +42,7 @@ void Knob::onDragStart(const event::DragStart& e) { | |||
| if (paramQuantity) { | |||
| oldValue = paramQuantity->getSmoothValue(); | |||
| if (snap) { | |||
| snapValue = paramQuantity->getValue(); | |||
| } | |||
| snapDelta = 0.f; | |||
| } | |||
| APP->window->cursorLock(); | |||
| @@ -81,22 +87,25 @@ void Knob::onDragMove(const event::DragMove& e) { | |||
| delta *= speed; | |||
| delta *= range; | |||
| // Drag slower if mod is held | |||
| // Drag slower if Mod is held | |||
| int mods = APP->window->getMods(); | |||
| if ((mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||
| delta /= 16.f; | |||
| } | |||
| // Drag even slower if mod+shift is held | |||
| // Drag even slower if Mod-Shift is held | |||
| if ((mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) { | |||
| delta /= 256.f; | |||
| } | |||
| if (snap) { | |||
| snapValue += delta; | |||
| snapValue = math::clamp(snapValue, paramQuantity->getMinValue(), paramQuantity->getMaxValue()); | |||
| paramQuantity->setValue(std::round(snapValue)); | |||
| if (paramQuantity->snapEnabled) { | |||
| // Replace delta with an accumulated delta since the last integer knob. | |||
| snapDelta += delta; | |||
| delta = std::trunc(snapDelta); | |||
| snapDelta -= delta; | |||
| } | |||
| else if (smooth) { | |||
| // Set value | |||
| if (smooth) { | |||
| paramQuantity->setSmoothValue(paramQuantity->getSmoothValue() + delta); | |||
| } | |||
| else { | |||
| @@ -107,23 +116,6 @@ void Knob::onDragMove(const event::DragMove& e) { | |||
| ParamWidget::onDragMove(e); | |||
| } | |||
| void Knob::reset() { | |||
| if (paramQuantity && paramQuantity->isBounded()) { | |||
| paramQuantity->reset(); | |||
| oldValue = snapValue = paramQuantity->getValue(); | |||
| } | |||
| } | |||
| void Knob::randomize() { | |||
| if (paramQuantity && paramQuantity->isBounded()) { | |||
| float value = math::rescale(random::uniform(), 0.f, 1.f, paramQuantity->getMinValue(), paramQuantity->getMaxValue()); | |||
| if (snap) | |||
| value = std::round(value); | |||
| paramQuantity->setValue(value); | |||
| oldValue = snapValue = paramQuantity->getValue(); | |||
| } | |||
| } | |||
| } // namespace app | |||
| } // namespace rack | |||
| @@ -668,9 +668,6 @@ void ModuleWidget::resetAction() { | |||
| h->moduleId = module->id; | |||
| h->oldModuleJ = toJson(); | |||
| for (ParamWidget* param : params) { | |||
| param->reset(); | |||
| } | |||
| APP->engine->resetModule(module); | |||
| h->newModuleJ = toJson(); | |||
| @@ -686,9 +683,6 @@ void ModuleWidget::randomizeAction() { | |||
| h->moduleId = module->id; | |||
| h->oldModuleJ = toJson(); | |||
| for (ParamWidget* param : params) { | |||
| param->randomize(); | |||
| } | |||
| APP->engine->randomizeModule(module); | |||
| h->newModuleJ = toJson(); | |||
| @@ -754,12 +748,13 @@ void ModuleWidget::cloneAction() { | |||
| void ModuleWidget::disableAction() { | |||
| assert(module); | |||
| APP->engine->disableModule(module, !module->disabled); | |||
| // history::ModuleDisable | |||
| history::ModuleDisable* h = new history::ModuleDisable; | |||
| h->moduleId = module->id; | |||
| h->disabled = !module->disabled; | |||
| h->disabled = module->disabled; | |||
| APP->history->push(h); | |||
| h->redo(); | |||
| } | |||
| void ModuleWidget::removeAction() { | |||
| @@ -113,12 +113,12 @@ struct ParamUnmapItem : ui::MenuItem { | |||
| void ParamWidget::step() { | |||
| if (paramQuantity) { | |||
| float value = paramQuantity->getValue(); | |||
| float value = paramQuantity->getSmoothValue(); | |||
| // Trigger change event when paramQuantity value changes | |||
| if (value != dirtyValue) { | |||
| dirtyValue = value; | |||
| if (value != lastValue) { | |||
| event::Change eChange; | |||
| onChange(eChange); | |||
| lastValue = value; | |||
| } | |||
| } | |||
| @@ -218,11 +218,9 @@ void ParamWidget::createContextMenu() { | |||
| } | |||
| void ParamWidget::resetAction() { | |||
| if (paramQuantity && paramQuantity->isBounded()) { | |||
| if (paramQuantity && paramQuantity->resetEnabled) { | |||
| float oldValue = paramQuantity->getValue(); | |||
| reset(); | |||
| // Here's another way of doing it, but either works. | |||
| // paramQuantity->getParam()->reset(); | |||
| paramQuantity->reset(); | |||
| float newValue = paramQuantity->getValue(); | |||
| if (oldValue != newValue) { | |||
| @@ -573,7 +573,10 @@ void RackWidget::clearCablesAction() { | |||
| complexAction->push(h); | |||
| } | |||
| APP->history->push(complexAction); | |||
| if (!complexAction->isEmpty()) | |||
| APP->history->push(complexAction); | |||
| else | |||
| delete complexAction; | |||
| clearCables(); | |||
| } | |||
| @@ -34,12 +34,14 @@ void SvgKnob::setSvg(std::shared_ptr<Svg> svg) { | |||
| void SvgKnob::onChange(const event::Change& e) { | |||
| // Re-transform the widget::TransformWidget | |||
| if (paramQuantity) { | |||
| float value = paramQuantity->getScaledValue(); | |||
| float angle; | |||
| if (paramQuantity->isBounded()) { | |||
| angle = math::rescale(paramQuantity->getScaledValue(), 0.f, 1.f, minAngle, maxAngle); | |||
| angle = math::rescale(value, 0.f, 1.f, minAngle, maxAngle); | |||
| } | |||
| else { | |||
| angle = math::rescale(paramQuantity->getValue(), -1.f, 1.f, minAngle, maxAngle); | |||
| // Center unbounded knobs | |||
| angle = math::rescale(value, -1.f, 1.f, minAngle, maxAngle); | |||
| } | |||
| angle = std::fmod(angle, 2 * M_PI); | |||
| tw->identity(); | |||
| @@ -9,6 +9,17 @@ namespace rack { | |||
| namespace app { | |||
| void Switch::init() { | |||
| ParamWidget::init(); | |||
| if (paramQuantity) { | |||
| paramQuantity->snapEnabled = true; | |||
| if (momentary) { | |||
| paramQuantity->resetEnabled = false; | |||
| paramQuantity->randomizeEnabled = false; | |||
| } | |||
| } | |||
| } | |||
| void Switch::step() { | |||
| if (momentaryPressed) { | |||
| momentaryPressed = false; | |||
| @@ -75,19 +86,6 @@ void Switch::onDragEnd(const event::DragEnd& e) { | |||
| } | |||
| } | |||
| void Switch::reset() { | |||
| if (paramQuantity && !momentary) { | |||
| paramQuantity->reset(); | |||
| } | |||
| } | |||
| void Switch::randomize() { | |||
| if (paramQuantity && !momentary) { | |||
| float value = paramQuantity->getMinValue() + std::floor(random::uniform() * (paramQuantity->getRange() + 1)); | |||
| paramQuantity->setValue(value); | |||
| } | |||
| } | |||
| } // namespace app | |||
| } // namespace rack | |||
| @@ -539,7 +539,7 @@ void Engine::addModule(Module* module) { | |||
| if (paramHandle->moduleId == module->id) | |||
| paramHandle->module = module; | |||
| } | |||
| DEBUG("Added module %d to engine", module->id); | |||
| // DEBUG("Added module %d to engine", module->id); | |||
| } | |||
| @@ -582,7 +582,7 @@ void Engine::removeModule(Module* module) { | |||
| internal->primaryModule = NULL; | |||
| // Remove module | |||
| internal->modules.erase(it); | |||
| DEBUG("Removed module %d to engine", module->id); | |||
| // DEBUG("Removed module %d to engine", module->id); | |||
| } | |||
| @@ -736,7 +736,7 @@ void Engine::addCable(Cable* cable) { | |||
| e.portId = cable->outputId; | |||
| cable->outputModule->onPortChange(e); | |||
| } | |||
| DEBUG("Added cable %d to engine", cable->id); | |||
| // DEBUG("Added cable %d to engine", cable->id); | |||
| } | |||
| @@ -772,7 +772,7 @@ void Engine::removeCable(Cable* cable) { | |||
| e.portId = cable->outputId; | |||
| cable->outputModule->onPortChange(e); | |||
| } | |||
| DEBUG("Removed cable %d to engine", cable->id); | |||
| // DEBUG("Removed cable %d to engine", cable->id); | |||
| } | |||
| @@ -979,7 +979,6 @@ void Engine::fromJson(json_t* rootJ) { | |||
| Cable* cable = new Cable; | |||
| try { | |||
| cable->fromJson(cableJ); | |||
| // DEBUG("%p %d %p %d", cable->inputModule, cable->inputId, cable->) | |||
| addCable(cable); | |||
| } | |||
| catch (Exception& e) { | |||
| @@ -180,11 +180,10 @@ void Module::fromJson(json_t* rootJ) { | |||
| void Module::onReset(const ResetEvent& e) { | |||
| // Reset all parameters | |||
| assert(params.size() == paramQuantities.size()); | |||
| for (int i = 0; i < (int) params.size(); i++) { | |||
| if (!paramQuantities[i]->resetEnabled) | |||
| for (ParamQuantity* pq : paramQuantities) { | |||
| if (!pq->resetEnabled) | |||
| continue; | |||
| paramQuantities[i]->reset(); | |||
| pq->reset(); | |||
| } | |||
| // Call deprecated event | |||
| onReset(); | |||
| @@ -193,11 +192,10 @@ void Module::onReset(const ResetEvent& e) { | |||
| void Module::onRandomize(const RandomizeEvent& e) { | |||
| // Randomize all parameters | |||
| assert(params.size() == paramQuantities.size()); | |||
| for (int i = 0; i < (int) params.size(); i++) { | |||
| if (!paramQuantities[i]->randomizeEnabled) | |||
| for (ParamQuantity* pq : paramQuantities) { | |||
| if (!pq->randomizeEnabled) | |||
| continue; | |||
| paramQuantities[i]->randomize(); | |||
| pq->randomize(); | |||
| } | |||
| // Call deprecated event | |||
| onRandomize(); | |||
| @@ -17,6 +17,8 @@ void ParamQuantity::setSmoothValue(float smoothValue) { | |||
| if (!module) | |||
| return; | |||
| smoothValue = math::clampSafe(smoothValue, getMinValue(), getMaxValue()); | |||
| if (snapEnabled) | |||
| smoothValue = std::round(smoothValue); | |||
| APP->engine->setSmoothParam(module, paramId, smoothValue); | |||
| } | |||
| @@ -30,6 +32,8 @@ void ParamQuantity::setValue(float value) { | |||
| if (!module) | |||
| return; | |||
| value = math::clampSafe(value, getMinValue(), getMaxValue()); | |||
| if (snapEnabled) | |||
| value = std::round(value); | |||
| APP->engine->setParam(module, paramId, value); | |||
| } | |||
| @@ -54,11 +54,8 @@ void ModuleAdd::setModule(app::ModuleWidget* mw) { | |||
| void ModuleAdd::undo() { | |||
| app::ModuleWidget* mw = APP->scene->rack->getModule(moduleId); | |||
| assert(mw); | |||
| engine::Module* module = mw->module; | |||
| APP->scene->rack->removeModule(mw); | |||
| delete mw; | |||
| APP->engine->removeModule(module); | |||
| delete module; | |||
| } | |||
| void ModuleAdd::redo() { | |||
| @@ -66,6 +63,7 @@ void ModuleAdd::redo() { | |||
| module->id = moduleId; | |||
| module->fromJson(moduleJ); | |||
| APP->engine->addModule(module); | |||
| app::ModuleWidget* mw = model->createModuleWidget(module); | |||
| mw->box.pos = pos; | |||
| APP->scene->rack->addModule(mw); | |||
| @@ -130,6 +128,7 @@ void ParamChange::redo() { | |||
| void CableAdd::setCable(app::CableWidget* cw) { | |||
| assert(cw); | |||
| assert(cw->cable); | |||
| assert(cw->cable->id >= 0); | |||
| cableId = cw->cable->id; | |||
| @@ -147,11 +146,6 @@ void CableAdd::undo() { | |||
| assert(cw); | |||
| APP->scene->rack->removeCable(cw); | |||
| delete cw; | |||
| engine::Cable* cable = APP->engine->getCable(cableId); | |||
| assert(cable); | |||
| APP->engine->removeCable(cable); | |||
| delete cable; | |||
| } | |||
| void CableAdd::redo() { | |||