@@ -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() { | |||