diff --git a/README.md b/README.md index 6fc01789..5d5c5375 100644 --- a/README.md +++ b/README.md @@ -87,10 +87,10 @@ Build plugin. All **source code** in this repository is licensed under [BSD-3-Clause](LICENSE.txt) by [Andrew Belt](https://andrewbelt.name/). -**Component Library graphics** in `res/ComponentLibrary` are licensed under [CC BY-NC 4.0](https://creativecommons.org/licenses/by-nc/4.0/) by [Grayscale](http://grayscale.info/). Commercial plugins must request a commercial license to use Component Library graphics. +**Component Library graphics** in `res/ComponentLibrary` are licensed under [CC BY-NC 4.0](https://creativecommons.org/licenses/by-nc/4.0/) by [Grayscale](http://grayscale.info/). Commercial plugins must request a commercial license to use Component Library graphics by emailing contact@vcvrack.com. -**Core** panel graphics in `res/Core` are copyright © 2017 by Grayscale. You may not create derivative works of Core panels. +**Core** panel graphics in `res/Core` are copyright © 2017 Grayscale. You may not create derivative works of Core panels. -The **VCV logo and icon** are copyright © 2017 by Grayscale and may not be used in derivative works. +The **VCV logo and icon** are copyright © 2017 Andrew Belt and may not be used in derivative works. -The **"VCV" brand name** is trademarked and may not be used for unofficial products. However, it is acceptable to use the phrase "for VCV Rack" for promotion of your plugin. +The **"VCV" name** is trademarked and may not be used for unofficial products. However, it is acceptable to use the phrase "for VCV Rack" for promotion of your plugin. For all other purposes, email contact@vcvrack.com. diff --git a/include/app.hpp b/include/app.hpp index 64db4f36..ef0ba852 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -36,7 +36,7 @@ struct Module; struct Wire; struct RackWidget; -struct ParamWidget; +struct Parameter; struct Port; struct SVGPanel; @@ -58,14 +58,14 @@ struct ModuleWidget : OpaqueWidget { SVGPanel *panel = NULL; std::vector inputs; std::vector outputs; - std::vector params; + std::vector params; ModuleWidget(Module *module); ~ModuleWidget(); /** Convenience functions for adding special widgets (calls addChild()) */ void addInput(Port *input); void addOutput(Port *output); - void addParam(ParamWidget *param); + void addParam(Parameter *param); void setPanel(std::shared_ptr svg); virtual json_t *toJson(); @@ -200,16 +200,31 @@ struct SVGPanel : FramebufferWidget { }; //////////////////// -// params +// ParamWidgets and other components //////////////////// +/** A Widget that exists on a Panel and interacts with a Module */ +struct Component : OpaqueWidget { + Module *module = NULL; + + template + static T *create(Vec pos, Module *module) { + T *o = new T(); + o->box.pos = pos; + o->module = module; + return o; + } +}; + struct CircularShadow : TransparentWidget { - float blur = 0.0; + float blurRadius; + float opacity; + CircularShadow(); void draw(NVGcontext *vg) override; }; -struct ParamWidget : OpaqueWidget, QuantityWidget { - Module *module = NULL; +/** A Component which has control over a Param (defined in engine.hpp) */ +struct Parameter : Component, QuantityWidget { int paramId; /** Used to momentarily disable value randomization To permanently disable or change randomization behavior, override the randomize() method instead of changing this. @@ -225,10 +240,9 @@ struct ParamWidget : OpaqueWidget, QuantityWidget { void onMouseDown(EventMouseDown &e) override; void onChange(EventChange &e) override; - template + template static T *create(Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { - T *o = Widget::create(pos); - o->module = module; + T *o = Component::create(pos, module); o->paramId = paramId; o->setLimits(minValue, maxValue); o->setDefaultValue(defaultValue); @@ -236,8 +250,11 @@ struct ParamWidget : OpaqueWidget, QuantityWidget { } }; +/** Deprecated name of Parameter */ +typedef Parameter ParamWidget; + /** Implements vertical dragging behavior for ParamWidgets */ -struct Knob : ParamWidget { +struct Knob : Parameter { /** Snap to nearest integer while dragging */ bool snap = false; /** Multiplier for mouse movement to adjust knob value */ @@ -249,18 +266,19 @@ struct Knob : ParamWidget { void onDragEnd(EventDragEnd &e) override; }; -struct SpriteKnob : virtual Knob, SpriteWidget { +/** Deprecated */ +struct SpriteKnob : Knob, SpriteWidget { int minIndex, maxIndex, spriteCount; void step() override; }; /** A knob which rotates an SVG and caches it in a framebuffer */ -struct SVGKnob : virtual Knob, FramebufferWidget { - /** Angles in radians */ - float minAngle, maxAngle; - /** Not owned */ +struct SVGKnob : Knob, FramebufferWidget { TransformWidget *tw; SVGWidget *sw; + CircularShadow *shadow; + /** Angles in radians */ + float minAngle, maxAngle; SVGKnob(); void setSVG(std::shared_ptr svg); @@ -268,26 +286,28 @@ struct SVGKnob : virtual Knob, FramebufferWidget { void onChange(EventChange &e) override; }; -struct SVGFader : Knob, FramebufferWidget { - /** Intermediate positions will be interpolated between these positions */ - Vec minHandlePos, maxHandlePos; - /** Not owned */ +/** Behaves like a knob but linearly moves an SVGWidget between two points. +Can be used for horizontal or vertical linear faders. +*/ +struct SVGSlider : Knob, FramebufferWidget { SVGWidget *background; SVGWidget *handle; + /** Intermediate positions will be interpolated between these positions */ + Vec minHandlePos, maxHandlePos; - SVGFader(); + SVGSlider(); + void setSVGs(std::shared_ptr backgroundSVG, std::shared_ptr handleSVG); void step() override; void onChange(EventChange &e) override; }; -struct Switch : ParamWidget { -}; +/** Deprecated name for SVGSlider */ +typedef SVGSlider SVGFader; -struct SVGSwitch : virtual Switch, FramebufferWidget { +/** A Parameter with multiple frames corresponding to its value */ +struct SVGSwitch : virtual Parameter, FramebufferWidget { std::vector> frames; - /** Not owned */ SVGWidget *sw; - SVGSwitch(); /** Adds an SVG file to represent the next switch position */ void addFrame(std::shared_ptr svg); @@ -295,29 +315,33 @@ struct SVGSwitch : virtual Switch, FramebufferWidget { }; /** A switch that cycles through each mechanical position */ -struct ToggleSwitch : virtual Switch { - void onDragStart(EventDragStart &e) override { - // Cycle through values - // e.g. a range of [0.0, 3.0] would have modes 0, 1, 2, and 3. - if (value >= maxValue) - setValue(minValue); - else - setValue(value + 1.0); - } +struct ToggleSwitch : virtual Parameter { + void onDragStart(EventDragStart &e) override; }; -/** A switch that is turned on when held */ -struct MomentarySwitch : virtual Switch { +/** A switch that is turned on when held and turned off when released. +Consider using SVGButton if the switch simply changes the state of your Module when clicked. +*/ +struct MomentarySwitch : virtual Parameter { /** Don't randomize state */ void randomize() override {} - void onDragStart(EventDragStart &e) override { - setValue(maxValue); - EventAction eAction; - onAction(eAction); - } - void onDragEnd(EventDragEnd &e) override { - setValue(minValue); - } + void onDragStart(EventDragStart &e) override; + void onDragEnd(EventDragEnd &e) override; +}; + +/** A Component with a default (up) and active (down) state when clicked. +Does not modify a Param, simply calls onAction() of a subclass. +*/ +struct SVGButton : Component, FramebufferWidget { + Module *module = NULL; + std::shared_ptr defaultSVG; + std::shared_ptr activeSVG; + SVGWidget *sw; + SVGButton(); + /** If `activeSVG` is NULL, `defaultSVG` is used as the active state instead. */ + void setSVGs(std::shared_ptr defaultSVG, std::shared_ptr activeSVG); + void onDragStart(EventDragStart &e) override; + void onDragEnd(EventDragEnd &e) override; }; //////////////////// @@ -426,13 +450,11 @@ struct ModuleLightWidget : MultiLightWidget { // ports //////////////////// -struct Port : OpaqueWidget { +struct Port : Component { enum PortType { INPUT, OUTPUT }; - - Module *module = NULL; PortType type = INPUT; int portId; MultiLightWidget *plugLight; @@ -450,9 +472,8 @@ struct Port : OpaqueWidget { template static T *create(Vec pos, PortType type, Module *module, int portId) { - T *o = Widget::create(pos); + T *o = Component::create(pos, module); o->type = type; - o->module = module; o->portId = portId; return o; } @@ -460,8 +481,10 @@ struct Port : OpaqueWidget { struct SVGPort : Port, FramebufferWidget { SVGWidget *background; + CircularShadow *shadow; SVGPort(); + void setSVG(std::shared_ptr svg); void draw(NVGcontext *vg) override; }; diff --git a/include/audio.hpp b/include/audio.hpp index 5a59cd2e..38da910c 100644 --- a/include/audio.hpp +++ b/include/audio.hpp @@ -43,17 +43,18 @@ struct AudioIO { void setSampleRate(int sampleRate); void setBlockSize(int blockSize); + void setChannels(int numOutputs, int numInputs); + /** Must close the stream before opening */ void openStream(); void closeStream(); - /** Returns whether the audio stream is open and running */ - bool isActive(); std::vector getSampleRates(); virtual void processStream(const float *input, float *output, int frames) {} virtual void onCloseStream() {} virtual void onOpenStream() {} + virtual void onChannelsChange() {} json_t *toJson(); void fromJson(json_t *rootJ); }; diff --git a/include/bridge.hpp b/include/bridge.hpp index d1c9e45c..62980056 100644 --- a/include/bridge.hpp +++ b/include/bridge.hpp @@ -5,14 +5,13 @@ namespace rack { -static const int BRIDGE_CHANNELS = 16; +static const int BRIDGE_NUM_PORTS = 16; void bridgeInit(); void bridgeDestroy(); void bridgeAudioSubscribe(int channel, AudioIO *audio); void bridgeAudioUnsubscribe(int channel, AudioIO *audio); -bool bridgeAudioIsSubscribed(int channel, AudioIO *audio); } // namespace rack diff --git a/include/componentlibrary.hpp b/include/componentlibrary.hpp index fb151c6a..ab51d576 100644 --- a/include/componentlibrary.hpp +++ b/include/componentlibrary.hpp @@ -36,31 +36,30 @@ struct RoundKnob : SVGKnob { struct RoundBlackKnob : RoundKnob { RoundBlackKnob() { - setSVG(SVG::load(assetGlobal("res/ComponentLibrary/RoundBlack.svg"))); - box.size = Vec(38, 38); + setSVG(SVG::load(assetGlobal("res/ComponentLibrary/RoundBlackKnob.svg"))); } }; -struct RoundSmallBlackKnob : RoundBlackKnob { +struct RoundSmallBlackKnob : RoundKnob { RoundSmallBlackKnob() { - box.size = Vec(28, 28); + setSVG(SVG::load(assetGlobal("res/ComponentLibrary/RoundSmallBlackKnob.svg"))); } }; -struct RoundLargeBlackKnob : RoundBlackKnob { +struct RoundLargeBlackKnob : RoundKnob { RoundLargeBlackKnob() { - box.size = Vec(46, 46); + setSVG(SVG::load(assetGlobal("res/ComponentLibrary/RoundLargeBlackKnob.svg"))); } }; -struct RoundHugeBlackKnob : RoundBlackKnob { +struct RoundHugeBlackKnob : RoundKnob { RoundHugeBlackKnob() { - box.size = Vec(56, 56); + setSVG(SVG::load(assetGlobal("res/ComponentLibrary/RoundHugeBlackKnob.svg"))); } }; -struct RoundSmallBlackSnapKnob : RoundSmallBlackKnob { - RoundSmallBlackSnapKnob() { +struct RoundBlackSnapKnob : RoundBlackKnob { + RoundBlackSnapKnob() { snap = true; smooth = false; } @@ -294,7 +293,6 @@ struct SynthTechAlco : SVGKnob { struct Trimpot : SVGKnob { Trimpot() { - box.size = Vec(17, 17); minAngle = -0.75*M_PI; maxAngle = 0.75*M_PI; setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Trimpot.svg"))); @@ -303,7 +301,6 @@ struct Trimpot : SVGKnob { struct BefacoBigKnob : SVGKnob { BefacoBigKnob() { - box.size = Vec(75, 75); minAngle = -0.75*M_PI; maxAngle = 0.75*M_PI; setSVG(SVG::load(assetGlobal("res/ComponentLibrary/BefacoBigKnob.svg"))); @@ -319,52 +316,81 @@ struct BefacoBigSnapKnob : BefacoBigKnob { struct BefacoTinyKnob : SVGKnob { BefacoTinyKnob() { - box.size = Vec(26, 26); minAngle = -0.75*M_PI; maxAngle = 0.75*M_PI; setSVG(SVG::load(assetGlobal("res/ComponentLibrary/BefacoTinyKnob.svg"))); } }; -struct BefacoSlidePot : SVGFader { +struct BefacoSlidePot : SVGSlider { BefacoSlidePot() { Vec margin = Vec(3.5, 3.5); maxHandlePos = Vec(-1, -2).plus(margin); minHandlePos = Vec(-1, 87).plus(margin); - background->svg = SVG::load(assetGlobal("res/ComponentLibrary/BefacoSlidePot.svg")); - background->wrap(); + setSVGs(SVG::load(assetGlobal("res/ComponentLibrary/BefacoSlidePot.svg")), SVG::load(assetGlobal("res/ComponentLibrary/BefacoSlidePotHandle.svg"))); background->box.pos = margin; box.size = background->box.size.plus(margin.mult(2)); - handle->svg = SVG::load(assetGlobal("res/ComponentLibrary/BefacoSlidePotHandle.svg")); - handle->wrap(); + } +}; + +struct LEDSlider : SVGSlider { + LEDSlider() { + maxHandlePos = mm2px(Vec(0.738, 0.738).plus(Vec(2, 0))); + minHandlePos = mm2px(Vec(0.738, 22.078).plus(Vec(2, 0))); + setSVGs(SVG::load(assetGlobal("res/ComponentLibrary/LEDSlider.svg")), NULL); + } +}; + +/** API is unstable for LEDSlider. Will add a LightWidget later. */ +struct LEDSliderGreen : LEDSlider { + LEDSliderGreen() { + handle->setSVG(SVG::load(assetGlobal("res/ComponentLibrary/LEDSliderGreenHandle.svg"))); + } +}; + +struct LEDSliderRed : LEDSlider { + LEDSliderRed() { + handle->setSVG(SVG::load(assetGlobal("res/ComponentLibrary/LEDSliderRedHandle.svg"))); + } +}; + +struct LEDSliderYellow : LEDSlider { + LEDSliderYellow() { + handle->setSVG(SVG::load(assetGlobal("res/ComponentLibrary/LEDSliderYellowHandle.svg"))); + } +}; + +struct LEDSliderBlue : LEDSlider { + LEDSliderBlue() { + handle->setSVG(SVG::load(assetGlobal("res/ComponentLibrary/LEDSliderBlueHandle.svg"))); + } +}; + +struct LEDSliderWhite : LEDSlider { + LEDSliderWhite() { + handle->setSVG(SVG::load(assetGlobal("res/ComponentLibrary/LEDSliderWhiteHandle.svg"))); } }; //////////////////// -// Jacks +// Ports //////////////////// struct PJ301MPort : SVGPort { PJ301MPort() { - background->svg = SVG::load(assetGlobal("res/ComponentLibrary/PJ301M.svg")); - background->wrap(); - box.size = background->box.size; + setSVG(SVG::load(assetGlobal("res/ComponentLibrary/PJ301M.svg"))); } }; struct PJ3410Port : SVGPort { PJ3410Port() { - background->svg = SVG::load(assetGlobal("res/ComponentLibrary/PJ3410.svg")); - background->wrap(); - box.size = background->box.size; + setSVG(SVG::load(assetGlobal("res/ComponentLibrary/PJ3410.svg"))); } }; struct CL1362Port : SVGPort { CL1362Port() { - background->svg = SVG::load(assetGlobal("res/ComponentLibrary/CL1362.svg")); - background->wrap(); - box.size = background->box.size; + setSVG(SVG::load(assetGlobal("res/ComponentLibrary/CL1362.svg"))); } }; @@ -452,6 +478,26 @@ struct TinyLight : BASE { } }; +/** A light to displayed over PB61303. Must add a color by subclassing or templating. */ +template +struct LEDBezelLight : BASE { + LEDBezelLight() { + this->bgColor = COLOR_BLACK_TRANSPARENT; + this->box.size = mm2px(Vec(6.0, 6.0)); + } +}; + +/** A light to displayed over PB61303. Must add a color by subclassing or templating. +Don't add this as a child of the PB61303 itself. Instead, just place it over it as a sibling in the scene graph, offset by mm2px(Vec(0.5, 0.5)). +*/ +template +struct PB61303Light : BASE { + PB61303Light() { + this->bgColor = COLOR_BLACK_TRANSPARENT; + this->box.size = mm2px(Vec(9.0, 9.0)); + } +}; + //////////////////// // Switches and Buttons @@ -515,18 +561,29 @@ struct BefacoPush : SVGSwitch, MomentarySwitch { } }; +struct LEDBezel : SVGSwitch, MomentarySwitch { + LEDBezel() { + addFrame(SVG::load(assetGlobal("res/ComponentLibrary/LEDBezel.svg"))); + } +}; + struct PB61303 : SVGSwitch, MomentarySwitch { PB61303() { addFrame(SVG::load(assetGlobal("res/ComponentLibrary/PB61303.svg"))); } }; -struct LEDBezel : SVGSwitch, MomentarySwitch { - LEDBezel() { - addFrame(SVG::load(assetGlobal("res/ComponentLibrary/LEDBezel.svg"))); +struct PB61303Button : SVGButton { + PB61303Button() { + setSVGs(SVG::load(assetGlobal("res/ComponentLibrary/PB61303.svg")), NULL); } }; +struct LEDBezelButton : SVGButton { + LEDBezelButton() { + setSVGs(SVG::load(assetGlobal("res/ComponentLibrary/LEDBezel.svg")), NULL); + } +}; //////////////////// // Misc diff --git a/include/util/math.hpp b/include/util/math.hpp index 9eb2e441..2742c1cd 100644 --- a/include/util/math.hpp +++ b/include/util/math.hpp @@ -257,6 +257,12 @@ struct Rect { r.size = size; return r; } + Rect grow(Vec delta) { + Rect r; + r.pos = pos.minus(delta); + r.size = size.plus(delta.mult(2.f)); + return r; + } }; diff --git a/res/ComponentLibrary/BefacoBigKnob.svg b/res/ComponentLibrary/BefacoBigKnob.svg index e4967eb0..912ba00b 100644 --- a/res/ComponentLibrary/BefacoBigKnob.svg +++ b/res/ComponentLibrary/BefacoBigKnob.svg @@ -9,376 +9,15 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="25.664583mm" - height="25.66571mm" - viewBox="0 0 25.664584 25.665714" + width="26.000004mm" + height="26.001146mm" + viewBox="0 0 26.000004 26.001146" version="1.1" - id="svg15246" - sodipodi:docname="BefacoBigKnob.svg" - inkscape:version="0.92.2 5c3e80d, 2017-08-06"> + id="svg113936" + inkscape:version="0.92.2 5c3e80d, 2017-08-06" + sodipodi:docname="BefacoBigKnob.svg"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + id="defs113930" /> + fit-margin-bottom="0" + inkscape:window-width="2560" + inkscape:window-height="1422" + inkscape:window-x="0" + inkscape:window-y="18" + inkscape:window-maximized="0" /> + id="metadata113933"> @@ -418,26 +56,26 @@ inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" - transform="translate(-35.482976,-90.654687)"> + transform="translate(-80.833177,-85.089003)"> + style="fill:#d4d4d4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2879144" + d="m 106.83318,98.089007 c 0,7.179863 -5.82014,13.001143 -13.000007,13.001143 -7.179864,0 -12.999996,-5.82128 -12.999996,-13.001143 0,-7.178735 5.820132,-13.000004 12.999996,-13.000004 7.179867,0 13.000007,5.821269 13.000007,13.000004" + id="path109718" /> + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2879144" + d="m 103.36809,92.943667 c -0.6377,-1.189896 -1.49355,-2.244833 -2.51139,-3.115323 -1.962536,0.275543 -3.99481,-0.701791 -5.009247,-2.403411 -0.652299,-0.123713 -1.32599,-0.190069 -2.01428,-0.190069 -0.688287,0 -1.360855,0.06636 -2.014277,0.190069 -1.014438,1.70162 -3.045587,2.678954 -5.00925,2.403411 -1.017835,0.87049 -1.873689,1.925427 -2.51139,3.115323 0.726523,1.878194 0.208076,4.120774 -1.270883,5.489499 0.04838,1.363087 0.345295,2.662074 0.851391,3.850844 1.968154,0.59045 3.433609,2.43604 3.561817,4.48854 1.042568,0.76591 2.227968,1.34736 3.507834,1.7005 1.694867,-1.19327 4.074651,-1.19327 5.769516,0 1.279864,-0.35314 2.465268,-0.93459 3.508959,-1.7005 0.12709,-2.0525 1.59254,-3.89809 3.56182,-4.48854 0.50609,-1.18877 0.80302,-2.487757 0.85024,-3.850844 -1.47894,-1.368725 -1.99626,-3.611305 -1.27086,-5.489499" + id="path109720" /> + style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2879144" + d="m 94.2909,86.161932 c 0,0.253052 -0.205801,0.456617 -0.457727,0.456617 -0.251924,0 -0.456603,-0.203565 -0.456603,-0.456617 0,-0.253046 0.204679,-0.456614 0.456603,-0.456614 0.251926,0 0.457727,0.203568 0.457727,0.456614" + id="path109722" /> + style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2879144" + d="m 94.2909,98.090134 h -0.91433 v -9.861065 h 0.91433 z m 0,0" + id="path109724" /> diff --git a/res/ComponentLibrary/BefacoTinyKnob.svg b/res/ComponentLibrary/BefacoTinyKnob.svg index 3d38e4c8..b3db71a3 100644 --- a/res/ComponentLibrary/BefacoTinyKnob.svg +++ b/res/ComponentLibrary/BefacoTinyKnob.svg @@ -9,376 +9,15 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="8.6999998mm" - height="8.7000618mm" - viewBox="0 0 8.7000002 8.7000631" + width="9.0000019mm" + height="9.0000801mm" + viewBox="0 0 9.0000016 9.00008" version="1.1" - id="svg15246" - sodipodi:docname="BefacoTinyKnob.svg" - inkscape:version="0.92.2 5c3e80d, 2017-08-06"> + id="svg113936" + inkscape:version="0.92.2 5c3e80d, 2017-08-06" + sodipodi:docname="BefacoTinyKnob.svg"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + id="defs113930" /> + inkscape:window-maximized="0" /> + id="metadata113933"> @@ -418,26 +56,31 @@ inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" - transform="translate(-44.329061,-94.497224)"> + transform="translate(-111.86932,-85.795053)"> + + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.28209424" + d="m 120.02026,91.143448 c -0.46281,2.022043 -2.47716,3.285957 -4.49919,2.823148 -2.02205,-0.462813 -3.28598,-2.47714 -2.82314,-4.499182 0.46281,-2.020944 2.47713,-3.285958 4.49918,-2.82315 2.02093,0.462812 3.28594,2.477141 2.82315,4.499184" + id="path109732" /> + style="fill:none;stroke:#7f7878;stroke-width:0.11481237;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" + d="m 120.02026,91.143463 c -0.46281,2.022042 -2.47715,3.285956 -4.49919,2.823145 -2.02204,-0.462811 -3.28595,-2.47714 -2.82314,-4.499183 0.46281,-2.020941 2.47714,-3.285957 4.49918,-2.823145 2.02094,0.46281 3.28596,2.47714 2.82315,4.499183 z m 0,0" + id="path109734" /> + style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.28209424" + d="m 116.20648,88.009558 c -0.11239,-0.07603 -0.14328,-0.229201 -0.0672,-0.342701 0.0761,-0.113495 0.23029,-0.143251 0.34379,-0.06722 0.11356,0.07603 0.14325,0.229201 0.0672,0.342701 -0.076,0.113495 -0.23031,0.14325 -0.34379,0.06722" + id="path109736" /> + style="fill:none;stroke:#000000;stroke-width:0.46912277;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" + d="m 116.20647,88.009571 c -0.11239,-0.07603 -0.14326,-0.229202 -0.0672,-0.3427 0.076,-0.113495 0.23032,-0.14325 0.34381,-0.06722 0.11356,0.07602 0.14325,0.229201 0.0672,0.3427 -0.076,0.113495 -0.2303,0.143251 -0.3438,0.06722 z m 0,0" + id="path109738" /> diff --git a/res/ComponentLibrary/LEDSlider.svg b/res/ComponentLibrary/LEDSlider.svg new file mode 100644 index 00000000..272a9f19 --- /dev/null +++ b/res/ComponentLibrary/LEDSlider.svg @@ -0,0 +1,66 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/res/ComponentLibrary/LEDSliderBlueHandle.svg b/res/ComponentLibrary/LEDSliderBlueHandle.svg new file mode 100644 index 00000000..eabe2cbd --- /dev/null +++ b/res/ComponentLibrary/LEDSliderBlueHandle.svg @@ -0,0 +1,71 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/res/ComponentLibrary/LEDSliderGreenHandle.svg b/res/ComponentLibrary/LEDSliderGreenHandle.svg new file mode 100644 index 00000000..add9739f --- /dev/null +++ b/res/ComponentLibrary/LEDSliderGreenHandle.svg @@ -0,0 +1,71 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/res/ComponentLibrary/LEDSliderRedHandle.svg b/res/ComponentLibrary/LEDSliderRedHandle.svg new file mode 100644 index 00000000..b4b0baf9 --- /dev/null +++ b/res/ComponentLibrary/LEDSliderRedHandle.svg @@ -0,0 +1,71 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/res/ComponentLibrary/LEDSliderWhiteHandle.svg b/res/ComponentLibrary/LEDSliderWhiteHandle.svg new file mode 100644 index 00000000..e9933563 --- /dev/null +++ b/res/ComponentLibrary/LEDSliderWhiteHandle.svg @@ -0,0 +1,71 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/res/ComponentLibrary/LEDSliderYellowHandle.svg b/res/ComponentLibrary/LEDSliderYellowHandle.svg new file mode 100644 index 00000000..c6dab093 --- /dev/null +++ b/res/ComponentLibrary/LEDSliderYellowHandle.svg @@ -0,0 +1,71 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/res/ComponentLibrary/PB61303.svg b/res/ComponentLibrary/PB61303.svg index 42ac4a99..e1efe7df 100644 --- a/res/ComponentLibrary/PB61303.svg +++ b/res/ComponentLibrary/PB61303.svg @@ -7,209 +7,17 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="10.000438mm" height="10.000403mm" viewBox="0 0 10.000438 10.000403" version="1.1" - id="svg81475" + id="svg27765" inkscape:version="0.92.2 5c3e80d, 2017-08-06" sodipodi:docname="PB61303.svg"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + id="defs27759" /> + id="metadata27762"> @@ -248,16 +56,11 @@ inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" - transform="translate(-57.743829,-79.577179)"> + transform="translate(-52.452162,-82.600989)"> - + style="fill:#211e1e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" + d="m 62.4526,87.601896 c 0,2.760204 -2.239327,4.999496 -4.999531,4.999496 -2.76158,0 -5.000907,-2.239292 -5.000907,-4.999496 0,-2.761615 2.239327,-5.000907 5.000907,-5.000907 2.760204,0 4.999531,2.239292 4.999531,5.000907" + id="path26168" /> diff --git a/res/ComponentLibrary/Rogan1PGreen.svg b/res/ComponentLibrary/Rogan1PGreen.svg index 2c08b7ec..6121583d 100644 --- a/res/ComponentLibrary/Rogan1PGreen.svg +++ b/res/ComponentLibrary/Rogan1PGreen.svg @@ -392,9 +392,9 @@ inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" - inkscape:window-width="1274" - inkscape:window-height="1434" - inkscape:window-x="1280" + inkscape:window-width="2560" + inkscape:window-height="1440" + inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="0" units="px" @@ -410,7 +410,7 @@ image/svg+xml - + @@ -428,11 +428,11 @@ inkscape:connector-curvature="0" id="path7717" d="m 60.430463,91.091863 c 0,2.182813 -1.768016,3.950836 -3.950828,3.950836 -2.181437,0 -3.950829,-1.768023 -3.950829,-3.950836 0,-2.181433 1.769392,-3.949456 3.950829,-3.949456 2.182812,0 3.950828,1.768023 3.950828,3.949456" - style="fill:#42c5bd;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#00a1a7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#00a1a7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> diff --git a/res/ComponentLibrary/Rogan1PRed.svg b/res/ComponentLibrary/Rogan1PRed.svg index 668d6f70..6527f8f7 100644 --- a/res/ComponentLibrary/Rogan1PRed.svg +++ b/res/ComponentLibrary/Rogan1PRed.svg @@ -387,15 +387,15 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="5.6" - inkscape:cx="35.570219" + inkscape:cx="4.6773619" inkscape:cy="11.017728" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" - inkscape:window-width="1274" - inkscape:window-height="1434" - inkscape:window-x="1280" - inkscape:window-y="0" + inkscape:window-width="2560" + inkscape:window-height="1422" + inkscape:window-x="0" + inkscape:window-y="18" inkscape:window-maximized="0" units="px" fit-margin-top="0" @@ -410,7 +410,7 @@ image/svg+xml - + @@ -428,11 +428,11 @@ inkscape:connector-curvature="0" id="path7725" d="m 58.530155,91.196795 c 0,2.182812 -1.768016,3.950836 -3.950828,3.950836 -2.181437,0 -3.950829,-1.768024 -3.950829,-3.950836 0,-2.181433 1.769392,-3.949457 3.950829,-3.949457 2.182812,0 3.950828,1.768024 3.950828,3.949457" - style="fill:#e147c5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#da4061;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#da4061;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> diff --git a/res/ComponentLibrary/Rogan1PSGreen.svg b/res/ComponentLibrary/Rogan1PSGreen.svg index 2fd583f3..8d0772b0 100644 --- a/res/ComponentLibrary/Rogan1PSGreen.svg +++ b/res/ComponentLibrary/Rogan1PSGreen.svg @@ -387,15 +387,15 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="5.6" - inkscape:cx="45.219096" + inkscape:cx="14.326239" inkscape:cy="27.987957" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" - inkscape:window-width="1274" - inkscape:window-height="1434" - inkscape:window-x="1280" - inkscape:window-y="0" + inkscape:window-width="2560" + inkscape:window-height="1422" + inkscape:window-x="0" + inkscape:window-y="18" inkscape:window-maximized="0" units="px" fit-margin-top="0" @@ -410,7 +410,7 @@ image/svg+xml - + @@ -428,7 +428,7 @@ inkscape:connector-curvature="0" id="path7691" d="m 57.439346,94.226115 c 0,2.181437 -1.769392,3.950836 -3.950829,3.950836 -2.182812,0 -3.952204,-1.769399 -3.952204,-3.950836 0,-2.182812 1.769392,-3.950832 3.952204,-3.950832 2.181437,0 3.950829,1.76802 3.950829,3.950832" - style="fill:#42c5bd;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#00a1a7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#00a1a7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> diff --git a/res/ComponentLibrary/Rogan1PSRed.svg b/res/ComponentLibrary/Rogan1PSRed.svg index 787a11c8..3d3ff086 100644 --- a/res/ComponentLibrary/Rogan1PSRed.svg +++ b/res/ComponentLibrary/Rogan1PSRed.svg @@ -387,15 +387,15 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="5.6" - inkscape:cx="50.246257" + inkscape:cx="19.3534" inkscape:cy="20.515109" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" - inkscape:window-width="1274" - inkscape:window-height="1434" - inkscape:window-x="1280" - inkscape:window-y="0" + inkscape:window-width="2560" + inkscape:window-height="1422" + inkscape:window-x="0" + inkscape:window-y="18" inkscape:window-maximized="0" units="px" fit-margin-top="0" @@ -410,7 +410,7 @@ image/svg+xml - + @@ -428,7 +428,7 @@ inkscape:connector-curvature="0" id="path7699" d="m 56.109243,92.248924 c 0,2.181437 -1.769392,3.950836 -3.950829,3.950836 -2.182812,0 -3.952204,-1.769399 -3.952204,-3.950836 0,-2.182812 1.769392,-3.950832 3.952204,-3.950832 2.181437,0 3.950829,1.76802 3.950829,3.950832" - style="fill:#e147c5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#da4061;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#da4061;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> diff --git a/res/ComponentLibrary/Rogan2PGreen.svg b/res/ComponentLibrary/Rogan2PGreen.svg index cf860118..95ddfe7a 100644 --- a/res/ComponentLibrary/Rogan2PGreen.svg +++ b/res/ComponentLibrary/Rogan2PGreen.svg @@ -387,15 +387,15 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="5.6" - inkscape:cx="19.905014" + inkscape:cx="-10.987843" inkscape:cy="37.67402" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" - inkscape:window-width="1274" - inkscape:window-height="1434" - inkscape:window-x="1280" - inkscape:window-y="0" + inkscape:window-width="2560" + inkscape:window-height="1422" + inkscape:window-x="0" + inkscape:window-y="18" inkscape:window-maximized="0" units="px" fit-margin-top="0" @@ -410,7 +410,7 @@ image/svg+xml - + @@ -428,11 +428,11 @@ inkscape:connector-curvature="0" id="path7673" d="m 63.549962,97.739725 c 0,2.382635 -1.931977,4.314635 -4.31462,4.314635 -2.382629,0 -4.316017,-1.932 -4.316017,-4.314635 0,-2.384005 1.933388,-4.314638 4.316017,-4.314638 2.382643,0 4.31462,1.930633 4.31462,4.314638" - style="fill:#42c5bd;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#00a1a7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#00a1a7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> diff --git a/res/ComponentLibrary/Rogan2PRed.svg b/res/ComponentLibrary/Rogan2PRed.svg index 370a8c46..7ee556ca 100644 --- a/res/ComponentLibrary/Rogan2PRed.svg +++ b/res/ComponentLibrary/Rogan2PRed.svg @@ -387,15 +387,15 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="5.6" - inkscape:cx="51.516994" + inkscape:cx="20.624137" inkscape:cy="37.321731" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" - inkscape:window-width="1274" - inkscape:window-height="1434" - inkscape:window-x="1280" - inkscape:window-y="0" + inkscape:window-width="2560" + inkscape:window-height="1422" + inkscape:window-x="0" + inkscape:window-y="18" inkscape:window-maximized="0" units="px" fit-margin-top="0" @@ -410,7 +410,7 @@ image/svg+xml - + @@ -428,11 +428,11 @@ inkscape:connector-curvature="0" id="path7679" d="m 55.185959,97.646518 c 0,2.382632 -1.931991,4.314642 -4.314616,4.314642 -2.38263,0 -4.316018,-1.93201 -4.316018,-4.314642 0,-2.384005 1.933388,-4.314638 4.316018,-4.314638 2.382625,0 4.314616,1.930633 4.314616,4.314638" - style="fill:#e147c5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#da4061;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#da4061;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> diff --git a/res/ComponentLibrary/Rogan2PSGreen.svg b/res/ComponentLibrary/Rogan2PSGreen.svg index e84b1ff5..7ce58c54 100644 --- a/res/ComponentLibrary/Rogan2PSGreen.svg +++ b/res/ComponentLibrary/Rogan2PSGreen.svg @@ -387,15 +387,15 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="5.6" - inkscape:cx="17.70635" + inkscape:cx="-13.186507" inkscape:cy="40.123343" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" - inkscape:window-width="1274" - inkscape:window-height="1434" - inkscape:window-x="1280" - inkscape:window-y="0" + inkscape:window-width="2560" + inkscape:window-height="1422" + inkscape:window-x="0" + inkscape:window-y="18" inkscape:window-maximized="0" units="px" fit-margin-top="0" @@ -410,7 +410,7 @@ image/svg+xml - + @@ -433,11 +433,11 @@ inkscape:connector-curvature="0" id="path7651" d="m 65.728163,96.79133 c 0,2.382629 -1.932012,4.31464 -4.316017,4.31464 -2.382626,0 -4.314638,-1.932011 -4.314638,-4.31464 0,-2.384005 1.932012,-4.314638 4.314638,-4.314638 2.384005,0 4.316017,1.930633 4.316017,4.314638" - style="fill:#42c5bd;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#00a1a7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#00a1a7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> diff --git a/res/ComponentLibrary/Rogan2PSRed.svg b/res/ComponentLibrary/Rogan2PSRed.svg index 17be8303..4f05e98d 100644 --- a/res/ComponentLibrary/Rogan2PSRed.svg +++ b/res/ComponentLibrary/Rogan2PSRed.svg @@ -387,15 +387,15 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="5.6" - inkscape:cx="28.209324" + inkscape:cx="-2.6835331" inkscape:cy="41.164604" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" - inkscape:window-width="1274" - inkscape:window-height="1434" - inkscape:window-x="1280" - inkscape:window-y="0" + inkscape:window-width="2560" + inkscape:window-height="1422" + inkscape:window-x="0" + inkscape:window-y="18" inkscape:window-maximized="0" units="px" fit-margin-top="0" @@ -410,7 +410,7 @@ image/svg+xml - + @@ -433,11 +433,11 @@ inkscape:connector-curvature="0" id="path7659" d="m 62.949251,97.066827 c 0,2.382629 -1.932012,4.314643 -4.316017,4.314643 -2.382626,0 -4.314638,-1.932014 -4.314638,-4.314643 0,-2.384005 1.932012,-4.314638 4.314638,-4.314638 2.384005,0 4.316017,1.930633 4.316017,4.314638" - style="fill:#e147c5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#da4061;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#da4061;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> diff --git a/res/ComponentLibrary/Rogan3PGreen.svg b/res/ComponentLibrary/Rogan3PGreen.svg index b97092d5..16143e37 100644 --- a/res/ComponentLibrary/Rogan3PGreen.svg +++ b/res/ComponentLibrary/Rogan3PGreen.svg @@ -387,15 +387,15 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="5.6" - inkscape:cx="32.218991" + inkscape:cx="1.3261339" inkscape:cy="50.179458" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" - inkscape:window-width="1274" - inkscape:window-height="1434" - inkscape:window-x="1280" - inkscape:window-y="0" + inkscape:window-width="2560" + inkscape:window-height="1422" + inkscape:window-x="0" + inkscape:window-y="18" inkscape:window-maximized="0" units="px" fit-margin-top="0" @@ -410,7 +410,7 @@ image/svg+xml - + @@ -428,11 +428,11 @@ inkscape:connector-curvature="0" id="path7631" d="m 62.261116,99.730359 c 0,2.743681 -2.222775,4.967831 -4.967827,4.967831 -2.742293,0 -4.966448,-2.22415 -4.966448,-4.967831 0,-2.742292 2.224155,-4.966443 4.966448,-4.966443 2.745052,0 4.967827,2.224151 4.967827,4.966443" - style="fill:#42c5bd;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#00a1a7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#00a1a7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> diff --git a/res/ComponentLibrary/Rogan3PRed.svg b/res/ComponentLibrary/Rogan3PRed.svg index d27f1cc2..c97785eb 100644 --- a/res/ComponentLibrary/Rogan3PRed.svg +++ b/res/ComponentLibrary/Rogan3PRed.svg @@ -387,15 +387,15 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="5.6" - inkscape:cx="38.452993" + inkscape:cx="7.5601359" inkscape:cy="47.306337" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" - inkscape:window-width="1274" - inkscape:window-height="1434" - inkscape:window-x="1280" - inkscape:window-y="0" + inkscape:window-width="2560" + inkscape:window-height="1422" + inkscape:window-x="0" + inkscape:window-y="18" inkscape:window-maximized="0" units="px" fit-margin-top="0" @@ -410,7 +410,7 @@ image/svg+xml - + @@ -428,11 +428,11 @@ inkscape:connector-curvature="0" id="path7637" d="m 60.611703,98.97018 c 0,2.74368 -2.222775,4.96783 -4.967827,4.96783 -2.742293,0 -4.966448,-2.22415 -4.966448,-4.96783 0,-2.742297 2.224155,-4.966448 4.966448,-4.966448 2.745052,0 4.967827,2.224151 4.967827,4.966448" - style="fill:#e147c5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#da4061;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#da4061;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> diff --git a/res/ComponentLibrary/Rogan3PSGreen.svg b/res/ComponentLibrary/Rogan3PSGreen.svg index eead1ffa..9a1d2a7b 100644 --- a/res/ComponentLibrary/Rogan3PSGreen.svg +++ b/res/ComponentLibrary/Rogan3PSGreen.svg @@ -387,15 +387,15 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="5.6" - inkscape:cx="39.52628" + inkscape:cx="8.6334229" inkscape:cy="35.160107" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" - inkscape:window-width="1274" - inkscape:window-height="1434" - inkscape:window-x="1280" - inkscape:window-y="0" + inkscape:window-width="2560" + inkscape:window-height="1422" + inkscape:window-x="0" + inkscape:window-y="18" inkscape:window-maximized="0" units="px" fit-margin-top="0" @@ -410,7 +410,7 @@ image/svg+xml - + @@ -428,7 +428,7 @@ inkscape:connector-curvature="0" id="path7607" d="m 62.688998,93.978827 c 0,3.064757 -2.484604,5.550739 -5.55074,5.550739 -3.064757,0 -5.549357,-2.485982 -5.549357,-5.550739 0,-3.064757 2.4846,-5.54936 5.549357,-5.54936 3.066136,0 5.55074,2.484603 5.55074,5.54936" - style="fill:#42c5bd;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#00a1a7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#00a1a7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> diff --git a/res/ComponentLibrary/Rogan3PSRed.svg b/res/ComponentLibrary/Rogan3PSRed.svg index 6ab94ed1..7e5e431d 100644 --- a/res/ComponentLibrary/Rogan3PSRed.svg +++ b/res/ComponentLibrary/Rogan3PSRed.svg @@ -392,9 +392,9 @@ inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" - inkscape:window-width="1274" - inkscape:window-height="1434" - inkscape:window-x="1280" + inkscape:window-width="2560" + inkscape:window-height="1440" + inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="0" units="px" @@ -410,7 +410,7 @@ image/svg+xml - + @@ -428,7 +428,7 @@ inkscape:connector-curvature="0" id="path7615" d="m 62.71026,94.193113 c 0,3.064757 -2.484604,5.550739 -5.55074,5.550739 -3.064757,0 -5.549357,-2.485982 -5.549357,-5.550739 0,-3.064757 2.4846,-5.54936 5.549357,-5.54936 3.066136,0 5.55074,2.484603 5.55074,5.54936" - style="fill:#e147c5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#da4061;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> + style="fill:#da4061;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> diff --git a/res/ComponentLibrary/RoundBlack.svg b/res/ComponentLibrary/RoundBlack.svg deleted file mode 100644 index 92969944..00000000 --- a/res/ComponentLibrary/RoundBlack.svg +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - diff --git a/res/ComponentLibrary/RoundBlackKnob.svg b/res/ComponentLibrary/RoundBlackKnob.svg new file mode 100644 index 00000000..0fa91ad9 --- /dev/null +++ b/res/ComponentLibrary/RoundBlackKnob.svg @@ -0,0 +1,71 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/res/ComponentLibrary/RoundHugeBlackKnob.svg b/res/ComponentLibrary/RoundHugeBlackKnob.svg new file mode 100644 index 00000000..cc3c2a7f --- /dev/null +++ b/res/ComponentLibrary/RoundHugeBlackKnob.svg @@ -0,0 +1,71 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/res/ComponentLibrary/RoundLargeBlackKnob.svg b/res/ComponentLibrary/RoundLargeBlackKnob.svg new file mode 100644 index 00000000..82e4ae7a --- /dev/null +++ b/res/ComponentLibrary/RoundLargeBlackKnob.svg @@ -0,0 +1,71 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/res/ComponentLibrary/RoundSmallBlackKnob.svg b/res/ComponentLibrary/RoundSmallBlackKnob.svg new file mode 100644 index 00000000..609d3a87 --- /dev/null +++ b/res/ComponentLibrary/RoundSmallBlackKnob.svg @@ -0,0 +1,71 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/res/ComponentLibrary/Trimpot.svg b/res/ComponentLibrary/Trimpot.svg index 6191a6d0..f8ff0922 100644 --- a/res/ComponentLibrary/Trimpot.svg +++ b/res/ComponentLibrary/Trimpot.svg @@ -9,376 +9,15 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="6.2990241mm" - height="6.3003879mm" - viewBox="0 0 6.2990242 6.3003887" + width="6.2990298mm" + height="6.3003922mm" + viewBox="0 0 6.2990294 6.3003921" version="1.1" - id="svg15246" - sodipodi:docname="Trimpot.svg" - inkscape:version="0.92.2 5c3e80d, 2017-08-06"> + id="svg111794" + inkscape:version="0.92.2 5c3e80d, 2017-08-06" + sodipodi:docname="Trimpot.svg"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + id="defs111788" /> + fit-margin-bottom="0" + inkscape:window-width="2560" + inkscape:window-height="1422" + inkscape:window-x="0" + inkscape:window-y="18" + inkscape:window-maximized="0" /> + id="metadata111791"> @@ -418,16 +56,16 @@ inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" - transform="translate(-46.318588,-97.647662)"> + transform="translate(-183.93691,-77.348595)"> + style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" + d="m 183.93691,80.498791 c 0,1.740461 1.40974,3.150196 3.1502,3.150196 1.73909,0 3.14883,-1.409735 3.14883,-3.150196 0,-1.739085 -1.40974,-3.150196 -3.14883,-3.150196 -1.74046,0 -3.1502,1.411111 -3.1502,3.150196" + id="path108214" /> + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" + d="m 187.29109,77.359619 c -0.0675,-0.0055 -0.13508,-0.01101 -0.20398,-0.01101 -0.0689,0 -0.13779,0.0055 -0.20531,0.01101 v 3.139172 h 0.40929 z m 0,0" + id="path108216" /> diff --git a/src/Core/AudioInterface.cpp b/src/Core/AudioInterface.cpp index 8e6350bd..e81f6f49 100644 --- a/src/Core/AudioInterface.cpp +++ b/src/Core/AudioInterface.cpp @@ -43,10 +43,10 @@ struct AudioInterfaceIO : AudioIO { for (int i = 0; i < frames; i++) { if (inputBuffer.full()) break; - Frame f; - memset(&f, 0, sizeof(f)); - memcpy(&f, &input[numInputs * i], numInputs * sizeof(float)); - inputBuffer.push(f); + Frame inputFrame; + memset(&inputFrame, 0, sizeof(inputFrame)); + memcpy(&inputFrame, &input[numInputs * i], numInputs * sizeof(float)); + inputBuffer.push(inputFrame); } } @@ -71,13 +71,17 @@ struct AudioInterfaceIO : AudioIO { } // Notify engine when finished processing - engineCv.notify_all(); + engineCv.notify_one(); } void onCloseStream() override { inputBuffer.clear(); outputBuffer.clear(); } + + void onChannelsChange() override { + debug("Channels changed %d %d", numOutputs, numInputs); + } }; @@ -127,12 +131,6 @@ struct AudioInterface : Module { } void onSampleRateChange() override { - // for (int i = 0; i < INPUTS; i++) { - // inputSrc[i].setRates(audioIO.sampleRate, engineGetSampleRate()); - // } - // for (int i = 0; i < OUTPUTS; i++) { - // outputSrc[i].setRates(engineGetSampleRate(), audioIO.sampleRate); - // } inputSrc.setRates(audioIO.sampleRate, engineGetSampleRate()); outputSrc.setRates(engineGetSampleRate(), audioIO.sampleRate); } @@ -154,6 +152,7 @@ void AudioInterface::step() { } if (audioIO.numInputs > 0) { + // Convert inputs if needed if (inputBuffer.empty()) { int inLen = audioIO.inputBuffer.size(); int outLen = inputBuffer.capacity(); @@ -163,25 +162,27 @@ void AudioInterface::step() { } } + // Take input from buffer if (!inputBuffer.empty()) { inputFrame = inputBuffer.shift(); } for (int i = 0; i < INPUTS; i++) { - outputs[AUDIO_OUTPUT + i].value = 10.0 * inputFrame.samples[i]; + outputs[AUDIO_OUTPUT + i].value = 10.f * inputFrame.samples[i]; } if (audioIO.numOutputs > 0) { // Get and push output SRC frame if (!outputBuffer.full()) { - Frame f; - for (int i = 0; i < audioIO.numOutputs; i++) { - f.samples[i] = inputs[AUDIO_INPUT + i].value / 10.0; + Frame outputFrame; + for (int i = 0; i < OUTPUTS; i++) { + outputFrame.samples[i] = inputs[AUDIO_INPUT + i].value / 10.f; } - outputBuffer.push(f); + outputBuffer.push(outputFrame); } if (outputBuffer.full()) { - // Wait until outputs are needed + // Wait until outputs are needed. + // Give up after a timeout in case the audio device is being unresponsive. std::unique_lock lock(audioIO.engineMutex); auto cond = [&] { return (audioIO.outputBuffer.size() < (size_t) audioIO.blockSize); @@ -200,14 +201,16 @@ void AudioInterface::step() { debug("Audio Interface underflow"); } } + + // Notify audio thread that an output is potentially ready + audioIO.audioCv.notify_one(); } + // Turn on light if at least one port is enabled in the nearby pair for (int i = 0; i < INPUTS / 2; i++) lights[INPUT_LIGHT + i].value = (audioIO.numOutputs >= 2*i+1); for (int i = 0; i < OUTPUTS / 2; i++) lights[OUTPUT_LIGHT + i].value = (audioIO.numInputs >= 2*i+1); - - audioIO.audioCv.notify_all(); } diff --git a/src/Core/QuadMIDIToCVInterface.cpp b/src/Core/QuadMIDIToCVInterface.cpp index 99e0f210..48e13f09 100644 --- a/src/Core/QuadMIDIToCVInterface.cpp +++ b/src/Core/QuadMIDIToCVInterface.cpp @@ -43,6 +43,7 @@ struct QuadMIDIToCVInterface : Module { uint8_t notes[4]; bool gates[4]; bool pedal; + int rotateIndex; QuadMIDIToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS), heldNotes(128) { onReset(); @@ -71,6 +72,7 @@ struct QuadMIDIToCVInterface : Module { gates[i] = false; } pedal = false; + rotateIndex = 0; } void pressNote(uint8_t note) { @@ -84,17 +86,23 @@ struct QuadMIDIToCVInterface : Module { // Set notes and gates switch (polyMode) { case ROTATE_MODE: { - } break; + case RESET_MODE: { } break; + case REASSIGN_MODE: { } break; - case UNISON_MODE: { + case UNISON_MODE: { + for (int i = 0; i < 4; i++) { + notes[i] = note; + gates[i] = true; + } } break; + default: break; } } @@ -105,18 +113,41 @@ struct QuadMIDIToCVInterface : Module { 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; - // } + if (pedal) + return; + // Set last note + switch (polyMode) { + case ROTATE_MODE: { + + } break; + + case RESET_MODE: { + + } break; + + case REASSIGN_MODE: { + + } break; + + case UNISON_MODE: { + if (!heldNotes.empty()) { + auto it2 = heldNotes.end(); + it2--; + for (int i = 0; i < 4; i++) { + notes[i] = *it2; + gates[i] = true; + } + } + else { + for (int i = 0; i < 4; i++) { + gates[i] = false; + } + } + } break; + + default: break; + } + } void pressPedal() { diff --git a/src/app/CircularShadow.cpp b/src/app/CircularShadow.cpp index 3d3ec3fe..3df2b8a3 100644 --- a/src/app/CircularShadow.cpp +++ b/src/app/CircularShadow.cpp @@ -4,15 +4,22 @@ namespace rack { +CircularShadow::CircularShadow() { + blurRadius = 0; + opacity = 0.15; +} + void CircularShadow::draw(NVGcontext *vg) { + if (opacity < 0.0) + return; + nvgBeginPath(vg); - nvgRect(vg, -blur, -blur, box.size.x + 2*blur, box.size.y + 2*blur); - nvgFillColor(vg, nvgRGBAf(0.0, 0.0, 0.0, 0.25)); - Vec c = box.size.div(2.0); - float radius = c.x; - NVGcolor icol = nvgRGBAf(0.0, 0.0, 0.0, 0.25); + nvgRect(vg, -blurRadius, -blurRadius, box.size.x + 2*blurRadius, box.size.y + 2*blurRadius); + Vec center = box.size.div(2.0); + float radius = center.x; + NVGcolor icol = nvgRGBAf(0.0, 0.0, 0.0, opacity); NVGcolor ocol = nvgRGBAf(0.0, 0.0, 0.0, 0.0); - NVGpaint paint = nvgRadialGradient(vg, c.x, c.y, radius - blur/2, radius + blur/2, icol, ocol); + NVGpaint paint = nvgRadialGradient(vg, center.x, center.y, radius - blurRadius, radius, icol, ocol); nvgFillPaint(vg, paint); nvgFill(vg); } diff --git a/src/app/Knob.cpp b/src/app/Knob.cpp index 51d5288f..4eb0a522 100644 --- a/src/app/Knob.cpp +++ b/src/app/Knob.cpp @@ -21,15 +21,20 @@ void Knob::onDragStart(EventDragStart &e) { } void Knob::onDragMove(EventDragMove &e) { - float range = maxValue - minValue; - float delta = KNOB_SENSITIVITY * -e.mouseRel.y * speed; - if (isfinite(range)) - delta *= range; + float range; + if (isfinite(minValue) && isfinite(maxValue)) { + range = maxValue - minValue; + } + else { + range = 1.0 - (-1.0); + } + float delta = KNOB_SENSITIVITY * -e.mouseRel.y * speed * range; // Drag slower if Mod is held if (windowIsModPressed()) delta /= 16.0; dragValue += delta; + dragValue = clamp2(dragValue, minValue, maxValue); if (snap) setValue(roundf(dragValue)); else diff --git a/src/app/LightWidget.cpp b/src/app/LightWidget.cpp index 7b4443c3..1300b432 100644 --- a/src/app/LightWidget.cpp +++ b/src/app/LightWidget.cpp @@ -34,7 +34,7 @@ void LightWidget::drawHalo(NVGcontext *vg) { nvgRect(vg, radius - oradius, radius - oradius, 2*oradius, 2*oradius); NVGpaint paint; - NVGcolor icol = colorMult(color, 0.15); + NVGcolor icol = colorMult(color, 0.25); NVGcolor ocol = nvgRGB(0, 0, 0); paint = nvgRadialGradient(vg, radius, radius, radius, oradius, icol, ocol); nvgFillPaint(vg, paint); diff --git a/src/app/MomentarySwitch.cpp b/src/app/MomentarySwitch.cpp new file mode 100644 index 00000000..7eeed43c --- /dev/null +++ b/src/app/MomentarySwitch.cpp @@ -0,0 +1,18 @@ +#include "app.hpp" + + +namespace rack { + + +void MomentarySwitch::onDragStart(EventDragStart &e) { + setValue(maxValue); + EventAction eAction; + onAction(eAction); +} + +void MomentarySwitch::onDragEnd(EventDragEnd &e) { + setValue(minValue); +} + + +} // namespace rack diff --git a/src/app/ParamWidget.cpp b/src/app/Parameter.cpp similarity index 70% rename from src/app/ParamWidget.cpp rename to src/app/Parameter.cpp index 4ddf02eb..7e588d3b 100644 --- a/src/app/ParamWidget.cpp +++ b/src/app/Parameter.cpp @@ -5,29 +5,29 @@ namespace rack { -json_t *ParamWidget::toJson() { +json_t *Parameter::toJson() { json_t *rootJ = json_object(); json_object_set_new(rootJ, "paramId", json_integer(paramId)); json_object_set_new(rootJ, "value", json_real(value)); return rootJ; } -void ParamWidget::fromJson(json_t *rootJ) { +void Parameter::fromJson(json_t *rootJ) { json_t *valueJ = json_object_get(rootJ, "value"); if (valueJ) setValue(json_number_value(valueJ)); } -void ParamWidget::reset() { +void Parameter::reset() { setValue(defaultValue); } -void ParamWidget::randomize() { - if (randomizable) +void Parameter::randomize() { + if (randomizable && isfinite(minValue) && isfinite(maxValue)) setValue(rescale(randomUniform(), 0.0, 1.0, minValue, maxValue)); } -void ParamWidget::onMouseDown(EventMouseDown &e) { +void Parameter::onMouseDown(EventMouseDown &e) { if (e.button == 1) { setValue(defaultValue); } @@ -35,7 +35,7 @@ void ParamWidget::onMouseDown(EventMouseDown &e) { e.target = this; } -void ParamWidget::onChange(EventChange &e) { +void Parameter::onChange(EventChange &e) { if (!module) return; diff --git a/src/app/SVGButton.cpp b/src/app/SVGButton.cpp new file mode 100644 index 00000000..68e4bb23 --- /dev/null +++ b/src/app/SVGButton.cpp @@ -0,0 +1,32 @@ +#include "app.hpp" + + +namespace rack { + + +SVGButton::SVGButton() { + sw = new SVGWidget(); + addChild(sw); +} + +void SVGButton::setSVGs(std::shared_ptr defaultSVG, std::shared_ptr activeSVG) { + sw->setSVG(defaultSVG); + box.size = sw->box.size; + this->defaultSVG = defaultSVG; + this->activeSVG = activeSVG ? activeSVG : defaultSVG; +} + +void SVGButton::onDragStart(EventDragStart &e) { + EventAction eAction; + onAction(eAction); + sw->setSVG(activeSVG); + dirty = true; +} + +void SVGButton::onDragEnd(EventDragEnd &e) { + sw->setSVG(defaultSVG); + dirty = true; +} + + +} // namespace rack diff --git a/src/app/SVGFader.cpp b/src/app/SVGFader.cpp deleted file mode 100644 index 088c401d..00000000 --- a/src/app/SVGFader.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "app.hpp" - - -namespace rack { - - -SVGFader::SVGFader() { - background = new SVGWidget(); - addChild(background); - - handle = new SVGWidget(); - addChild(handle); -} - -void SVGFader::step() { - if (dirty) { - // Update handle position - Vec handlePos = Vec(rescale(value, minValue, maxValue, minHandlePos.x, maxHandlePos.x), rescale(value, minValue, maxValue, minHandlePos.y, maxHandlePos.y)); - handle->box.pos = handlePos; - } - FramebufferWidget::step(); -} - -void SVGFader::onChange(EventChange &e) { - dirty = true; - Knob::onChange(e); -} - - -} // namespace rack diff --git a/src/app/SVGKnob.cpp b/src/app/SVGKnob.cpp index 0d0f56f9..6517e7f6 100644 --- a/src/app/SVGKnob.cpp +++ b/src/app/SVGKnob.cpp @@ -5,6 +5,10 @@ namespace rack { SVGKnob::SVGKnob() { + shadow = new CircularShadow(); + addChild(shadow); + shadow->box.size = Vec(); + tw = new TransformWidget(); addChild(tw); @@ -13,22 +17,26 @@ SVGKnob::SVGKnob() { } void SVGKnob::setSVG(std::shared_ptr svg) { - sw->svg = svg; - sw->wrap(); + sw->setSVG(svg); tw->box.size = sw->box.size; box.size = sw->box.size; + shadow->box.size = sw->box.size; + shadow->box.pos = Vec(0, sw->box.size.y * 0.1); + // shadow->box = shadow->box.grow(Vec(2, 2)); } void SVGKnob::step() { // Re-transform TransformWidget if dirty if (dirty) { - tw->box.size = box.size; - float angle = 0.0; - if (isfinite(minValue) && isfinite(maxValue)) + float angle; + if (isfinite(minValue) && isfinite(maxValue)) { angle = rescale(value, minValue, maxValue, minAngle, maxAngle); + } + else { + angle = rescale(value, -1.0, 1.0, minAngle, maxAngle); + angle = fmodf(angle, 2*M_PI); + } tw->identity(); - // Scale SVG to box - tw->scale(box.size.div(sw->box.size)); // Rotate SVG Vec center = sw->box.getCenter(); tw->translate(center); @@ -44,5 +52,4 @@ void SVGKnob::onChange(EventChange &e) { } - } // namespace rack diff --git a/src/app/SVGPort.cpp b/src/app/SVGPort.cpp index 06343097..4b9651c1 100644 --- a/src/app/SVGPort.cpp +++ b/src/app/SVGPort.cpp @@ -5,10 +5,24 @@ namespace rack { SVGPort::SVGPort() { + shadow = new CircularShadow(); + addChild(shadow); + // Avoid breakage if plugins fail to call setSVG() + // In that case, just disable the shadow. + shadow->box.size = Vec(); + background = new SVGWidget(); addChild(background); } +void SVGPort::setSVG(std::shared_ptr svg) { + background->setSVG(svg); + box.size = background->box.size; + shadow->box.size = background->box.size; + shadow->box.pos = Vec(0, background->box.size.y * 0.1); + // shadow->box = shadow->box.grow(Vec(2, 2)); +} + void SVGPort::draw(NVGcontext *vg) { Port::draw(vg); FramebufferWidget::draw(vg); diff --git a/src/app/SVGSlider.cpp b/src/app/SVGSlider.cpp new file mode 100644 index 00000000..8931fa4b --- /dev/null +++ b/src/app/SVGSlider.cpp @@ -0,0 +1,37 @@ +#include "app.hpp" + + +namespace rack { + + +SVGSlider::SVGSlider() { + background = new SVGWidget(); + addChild(background); + + handle = new SVGWidget(); + addChild(handle); +} + +void SVGSlider::setSVGs(std::shared_ptr backgroundSVG, std::shared_ptr handleSVG) { + background->setSVG(backgroundSVG); + box.size = background->box.size; + if (handleSVG) { + handle->setSVG(handleSVG); + } +} + +void SVGSlider::step() { + if (dirty) { + // Interpolate handle position + handle->box.pos = Vec(rescale(value, minValue, maxValue, minHandlePos.x, maxHandlePos.x), rescale(value, minValue, maxValue, minHandlePos.y, maxHandlePos.y)); + } + FramebufferWidget::step(); +} + +void SVGSlider::onChange(EventChange &e) { + dirty = true; + Knob::onChange(e); +} + + +} // namespace rack diff --git a/src/app/SVGSwitch.cpp b/src/app/SVGSwitch.cpp index 3d163604..d399cd06 100644 --- a/src/app/SVGSwitch.cpp +++ b/src/app/SVGSwitch.cpp @@ -24,7 +24,7 @@ void SVGSwitch::onChange(EventChange &e) { int index = clamp((int) roundf(valueScaled), 0, frames.size() - 1); sw->setSVG(frames[index]); dirty = true; - Switch::onChange(e); + ParamWidget::onChange(e); } diff --git a/src/app/ToggleSwitch.cpp b/src/app/ToggleSwitch.cpp new file mode 100644 index 00000000..cb3c9404 --- /dev/null +++ b/src/app/ToggleSwitch.cpp @@ -0,0 +1,17 @@ +#include "app.hpp" + + +namespace rack { + + +void ToggleSwitch::onDragStart(EventDragStart &e) { + // Cycle through values + // e.g. a range of [0.0, 3.0] would have modes 0, 1, 2, and 3. + if (value >= maxValue) + setValue(minValue); + else + setValue(value + 1.0); +} + + +} // namespace rack diff --git a/src/audio.cpp b/src/audio.cpp index 6cdb1112..1ac78196 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -46,8 +46,10 @@ std::string AudioIO::getDriverName(int driver) { } void AudioIO::setDriver(int driver) { + // Close device setDevice(-1, 0); + // Close driver if (rtAudio) { delete rtAudio; rtAudio = NULL; @@ -69,7 +71,7 @@ int AudioIO::getDeviceCount() { return rtAudio->getDeviceCount(); } if (driver == BRIDGE_DRIVER) { - return BRIDGE_CHANNELS; + return BRIDGE_NUM_PORTS; } return 0; } @@ -90,13 +92,11 @@ bool AudioIO::getDeviceInfo(int device, RtAudio::DeviceInfo *deviceInfo) { } catch (RtAudioError &e) { warn("Failed to query RtAudio device: %s", e.what()); - return false; } } } - else { - return false; - } + + return false; } int AudioIO::getDeviceChannels(int device) { @@ -148,7 +148,7 @@ std::string AudioIO::getDeviceDetail(int device, int offset) { } } if (driver == BRIDGE_DRIVER) { - return stringf("Channel %d", device + 1); + return stringf("Port %d", device + 1); } return ""; } @@ -172,6 +172,12 @@ void AudioIO::setBlockSize(int blockSize) { openStream(); } +void AudioIO::setChannels(int numOutputs, int numInputs) { + this->numOutputs = numOutputs; + this->numInputs = numInputs; + onChannelsChange(); +} + static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData) { AudioIO *audioIO = (AudioIO*) userData; @@ -197,8 +203,7 @@ void AudioIO::openStream() { if (rtAudio->isStreamOpen()) return; - numOutputs = clamp((int) deviceInfo.outputChannels - offset, 0, maxChannels); - numInputs = clamp((int) deviceInfo.inputChannels - offset, 0, maxChannels); + setChannels(clamp((int) deviceInfo.outputChannels - offset, 0, maxChannels), clamp((int) deviceInfo.inputChannels - offset, 0, maxChannels)); if (numOutputs == 0 && numInputs == 0) { warn("RtAudio device %d has 0 inputs and 0 outputs"); @@ -252,8 +257,7 @@ void AudioIO::openStream() { onOpenStream(); } if (driver == BRIDGE_DRIVER) { - numOutputs = 2; - numInputs = 2; + setChannels(0, 0); // TEMP sampleRate = 44100; blockSize = 256; @@ -262,8 +266,7 @@ void AudioIO::openStream() { } void AudioIO::closeStream() { - numOutputs = 0; - numInputs = 0; + setChannels(0, 0); if (rtAudio) { if (rtAudio->isStreamRunning()) { @@ -293,17 +296,6 @@ void AudioIO::closeStream() { onCloseStream(); } -bool AudioIO::isActive() { - if (rtAudio) { - return rtAudio->isStreamRunning(); - } - if (driver == BRIDGE_DRIVER) { - bridgeAudioIsSubscribed(device, this); - } - return false; -} - - std::vector AudioIO::getSampleRates() { if (rtAudio) { try { diff --git a/src/bridge.cpp b/src/bridge.cpp index 2c220cf1..47409dfa 100644 --- a/src/bridge.cpp +++ b/src/bridge.cpp @@ -25,11 +25,13 @@ enum BridgeCommand { NO_COMMAND = 0, START_COMMAND, QUIT_COMMAND, - CHANNEL_SET_COMMAND, + PORT_SET_COMMAND, + MIDI_MESSAGE_SEND_COMMAND, AUDIO_SAMPLE_RATE_SET_COMMAND, AUDIO_CHANNELS_SET_COMMAND, AUDIO_BUFFER_SEND_COMMAND, - MIDI_MESSAGE_SEND_COMMAND, + AUDIO_ACTIVATE, + AUDIO_DEACTIVATE, NUM_COMMANDS }; @@ -37,19 +39,46 @@ enum BridgeCommand { static const int RECV_BUFFER_SIZE = (1<<13); static const int RECV_QUEUE_SIZE = (1<<17); -static AudioIO *audioListeners[BRIDGE_CHANNELS]; +struct BridgeClientConnection; +static BridgeClientConnection *connections[BRIDGE_NUM_PORTS] = {}; +static AudioIO *audioListeners[BRIDGE_NUM_PORTS] = {}; static std::thread serverThread; static bool serverQuit; struct BridgeClientConnection { + int client; RingBuffer recvQueue; BridgeCommand currentCommand = START_COMMAND; bool closeRequested = false; - int channel = -1; + int port = -1; int sampleRate = -1; int audioChannels = 0; int audioBufferLength = -1; + bool audioActive = false; + + ~BridgeClientConnection() { + setPort(-1); + } + + void send(const uint8_t *buffer, int length) { + if (length <= 0) + return; +#ifdef ARCH_LIN + int sendFlags = MSG_NOSIGNAL; +#else + int sendFlags = 0; +#endif + ssize_t written = ::send(client, (const char*) buffer, length, sendFlags); + // We must write the entire buffer + if (written < length) + closeRequested = true; + } + + template + void send(T x) { + send((uint8_t*) &x, sizeof(x)); + } /** Does not check if the queue has enough data. You must do that yourself before calling this method. @@ -61,6 +90,35 @@ struct BridgeClientConnection { return x; } + void run() { + info("Bridge client connected"); + + while (!closeRequested) { + uint8_t buffer[RECV_BUFFER_SIZE]; +#ifdef ARCH_LIN + int recvFlags = MSG_NOSIGNAL; +#else + int recvFlags = 0; +#endif + ssize_t received = ::recv(client, (char*) buffer, sizeof(buffer), recvFlags); + if (received <= 0) + break; + + // Make sure we can fill the buffer + if (recvQueue.capacity() < (size_t) received) { + // If we can't accept it, future messages will be incomplete + break; + } + + recvQueue.pushBuffer(buffer, received); + + // Loop the state machine until it returns false + while (step()) {} + } + + info("Bridge client closed"); + } + /** Steps the state machine Returns true if step() should be called again */ @@ -96,10 +154,21 @@ struct BridgeClientConnection { debug("Quitting!"); } break; - case CHANNEL_SET_COMMAND: { + case PORT_SET_COMMAND: { if (recvQueue.size() >= 1) { - channel = shift(); - debug("Set channel %d", channel); + int port = shift(); + setPort(port); + debug("Set port %d", port); + currentCommand = NO_COMMAND; + return true; + } + } break; + + case MIDI_MESSAGE_SEND_COMMAND: { + if (recvQueue.size() >= 3) { + uint8_t midiBuffer[3]; + recvQueue.shiftBuffer(midiBuffer, 3); + // debug("MIDI: %02x %02x %02x", midiBuffer[0], midiBuffer[1], midiBuffer[2]); currentCommand = NO_COMMAND; return true; } @@ -117,7 +186,7 @@ struct BridgeClientConnection { case AUDIO_CHANNELS_SET_COMMAND: { if (recvQueue.size() >= 1) { audioChannels = shift(); - debug("Set audio channels %d", channel); + debug("Set audio channels %d", audioChannels); currentCommand = NO_COMMAND; return true; } @@ -139,12 +208,18 @@ struct BridgeClientConnection { } else { if (recvQueue.size() >= (size_t) (sizeof(float) * audioBufferLength)) { + // Get input buffer + int frames = audioBufferLength / 2; float input[audioBufferLength]; - float output[audioBufferLength]; - memset(output, 0, sizeof(output)); recvQueue.shiftBuffer((uint8_t*) input, sizeof(float) * audioBufferLength); - int frames = audioBufferLength / 2; + // Process stream + float output[audioBufferLength]; processStream(input, output, frames); + // Send output buffer + send(AUDIO_BUFFER_SEND_COMMAND); + send(audioBufferLength); + send((uint8_t*) output, audioBufferLength * sizeof(float)); + audioBufferLength = -1; currentCommand = NO_COMMAND; return true; @@ -152,14 +227,18 @@ struct BridgeClientConnection { } } break; - case MIDI_MESSAGE_SEND_COMMAND: { - if (recvQueue.size() >= 3) { - uint8_t midiBuffer[3]; - recvQueue.shiftBuffer(midiBuffer, 3); - debug("MIDI: %02x %02x %02x", midiBuffer[0], midiBuffer[1], midiBuffer[2]); - currentCommand = NO_COMMAND; - return true; - } + case AUDIO_ACTIVATE: { + audioActive = true; + refreshAudioActive(); + currentCommand = NO_COMMAND; + return true; + } break; + + case AUDIO_DEACTIVATE: { + audioActive = false; + refreshAudioActive(); + currentCommand = NO_COMMAND; + return true; } break; default: { @@ -167,51 +246,58 @@ struct BridgeClientConnection { closeRequested = true; } break; } + + // Stop looping the state machine return false; } - void recv(uint8_t *buffer, int length) { - // Make sure we can fill the buffer - if (recvQueue.capacity() < (size_t) length) { - // If we can't accept it, future messages will be incomplete - closeRequested = true; - return; + void setPort(int newPort) { + // Unbind from existing port + if (port >= 0 && connections[port] == this) { + if (audioListeners[port]) + audioListeners[port]->setChannels(0, 0); + connections[port] = NULL; } - recvQueue.pushBuffer(buffer, length); - - // Loop the state machine until it returns false - while (step()) {} + // Bind to new port + if (newPort >= 0 && !connections[newPort]) { + connections[newPort] = this; + refreshAudioActive(); + port = newPort; + } + else { + port = -1; + } } void processStream(const float *input, float *output, int frames) { - if (!(0 <= channel && channel < BRIDGE_CHANNELS)) + if (!(0 <= port && port < BRIDGE_NUM_PORTS)) return; - if (!audioListeners[channel]) + if (!audioListeners[port]) return; - audioListeners[channel]->processStream(input, output, frames); + audioListeners[port]->processStream(input, output, frames); + debug("%d frames", frames); } -}; + void refreshAudioActive() { + if (!(0 <= port && port < BRIDGE_NUM_PORTS)) + return; + if (!audioListeners[port]) + return; + if (audioActive) + audioListeners[port]->setChannels(2, 2); + else + audioListeners[port]->setChannels(0, 0); + } +}; static void clientRun(int client) { + defer({ + close(client); + }); int err; - BridgeClientConnection connection; - - // // Get client address - // struct sockaddr_in addr; - // socklen_t clientAddrLen = sizeof(addr); - // err = getpeername(client, (struct sockaddr*) &addr, &clientAddrLen); - // assert(!err); - - // // Get client IP address - // struct in_addr ipAddr = addr.sin_addr; - // char ipBuffer[INET_ADDRSTRLEN]; - // inet_ntop(AF_INET, &ipAddr, ipBuffer, INET_ADDRSTRLEN); - - // info("Bridge client %s connected", ipBuffer); - info("Bridge client connected"); + (void) err; #ifdef ARCH_MAC // Avoid SIGPIPE @@ -219,29 +305,17 @@ static void clientRun(int client) { setsockopt(client, SOL_SOCKET, SO_NOSIGPIPE, &flag, sizeof(int)); #endif + // Disable non-blocking #ifdef ARCH_WIN - unsigned long blockingMode = 1; + unsigned long blockingMode = 0; ioctlsocket(client, FIONBIO, &blockingMode); #else err = fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) & ~O_NONBLOCK); #endif - while (!connection.closeRequested) { - uint8_t buffer[RECV_BUFFER_SIZE]; -#ifdef ARCH_LIN - ssize_t received = recv(client, (char*) buffer, sizeof(buffer), MSG_NOSIGNAL); -#else - ssize_t received = recv(client, (char*) buffer, sizeof(buffer), 0); -#endif - if (received <= 0) - break; - - connection.recv(buffer, received); - } - - err = close(client); - (void) err; - info("Bridge client closed"); + BridgeClientConnection connection; + connection.client = client; + connection.run(); } @@ -270,7 +344,7 @@ static void serverRun() { hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; - err = getaddrinfo(NULL, "5000", &hints, &result); + err = getaddrinfo("127.0.0.1", "5000", &hints, &result); if (err) { warn("Could not get Bridge server address"); return; @@ -301,7 +375,6 @@ static void serverRun() { close(server); }); - // Bind socket to address #ifdef ARCH_WIN err = bind(server, result->ai_addr, (int)result->ai_addrlen); @@ -321,12 +394,13 @@ static void serverRun() { } info("Bridge server started"); - // Make server non-blocking + // Enable non-blocking #ifdef ARCH_WIN unsigned long blockingMode = 1; ioctlsocket(server, FIONBIO, &blockingMode); #else - err = fcntl(server, F_SETFL, fcntl(server, F_GETFL, 0) | O_NONBLOCK); + int flags = fcntl(server, F_GETFL, 0); + err = fcntl(server, F_SETFL, flags | O_NONBLOCK); #endif // Accept clients @@ -357,26 +431,24 @@ void bridgeDestroy() { serverThread.join(); } -void bridgeAudioSubscribe(int channel, AudioIO *audio) { - if (!(0 <= channel && channel < BRIDGE_CHANNELS)) +void bridgeAudioSubscribe(int port, AudioIO *audio) { + if (!(0 <= port && port < BRIDGE_NUM_PORTS)) return; - if (audioListeners[channel]) + // Check if an Audio is already subscribed on the port + if (audioListeners[port]) return; - audioListeners[channel] = audio; + audioListeners[port] = audio; + if (connections[port]) + connections[port]->refreshAudioActive(); } -void bridgeAudioUnsubscribe(int channel, AudioIO *audio) { - if (!(0 <= channel && channel < BRIDGE_CHANNELS)) +void bridgeAudioUnsubscribe(int port, AudioIO *audio) { + if (!(0 <= port && port < BRIDGE_NUM_PORTS)) return; - if (audioListeners[channel] != audio) + if (audioListeners[port] != audio) return; - audioListeners[channel] = NULL; -} - -bool bridgeAudioIsSubscribed(int channel, AudioIO *audio) { - if (!(0 <= channel && channel < BRIDGE_CHANNELS)) - return false; - return (audioListeners[channel] == audio); + audioListeners[port] = NULL; + audio->setChannels(0, 0); } diff --git a/src/main.cpp b/src/main.cpp index 1758f2c6..9443ebeb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,7 +20,7 @@ int main(int argc, char* argv[]) { gLogFile = fopen(logFilename.c_str(), "w"); #endif - info("Rack v%s", gApplicationVersion.c_str()); + info("Rack %s", gApplicationVersion.c_str()); { char *cwd = getcwd(NULL, 0);