| @@ -132,13 +132,13 @@ struct Knob : ParamWidget { | |||||
| void onChange(); | void onChange(); | ||||
| }; | }; | ||||
| struct SpriteKnob : Knob, SpriteWidget { | |||||
| struct SpriteKnob : virtual Knob, SpriteWidget { | |||||
| int minIndex, maxIndex, spriteCount; | int minIndex, maxIndex, spriteCount; | ||||
| void step(); | void step(); | ||||
| }; | }; | ||||
| /** A knob which rotates an SVG and caches it in a framebuffer */ | /** A knob which rotates an SVG and caches it in a framebuffer */ | ||||
| struct SVGKnob : Knob, FramebufferWidget { | |||||
| struct SVGKnob : virtual Knob, FramebufferWidget { | |||||
| /** Angles in radians */ | /** Angles in radians */ | ||||
| float minAngle, maxAngle; | float minAngle, maxAngle; | ||||
| /** Not owned */ | /** Not owned */ | ||||
| @@ -151,6 +151,14 @@ struct SVGKnob : Knob, FramebufferWidget { | |||||
| void onChange(); | void onChange(); | ||||
| }; | }; | ||||
| /** Snaps to the nearest integer value on mouse release */ | |||||
| struct SnapKnob : virtual Knob { | |||||
| void onDragEnd() { | |||||
| setValue(roundf(value)); | |||||
| Knob::onDragEnd(); | |||||
| } | |||||
| }; | |||||
| struct SVGSlider : Knob, FramebufferWidget { | struct SVGSlider : Knob, FramebufferWidget { | ||||
| /** Intermediate positions will be interpolated between these positions */ | /** Intermediate positions will be interpolated between these positions */ | ||||
| Vec minHandlePos, maxHandlePos; | Vec minHandlePos, maxHandlePos; | ||||
| @@ -26,16 +26,16 @@ namespace rack { | |||||
| struct Rogan : SVGKnob { | struct Rogan : SVGKnob { | ||||
| Rogan() { | Rogan() { | ||||
| minAngle = -0.75*M_PI; | |||||
| maxAngle = 0.75*M_PI; | |||||
| minAngle = -0.83*M_PI; | |||||
| maxAngle = 0.83*M_PI; | |||||
| } | } | ||||
| }; | }; | ||||
| struct Rogan6PS : Rogan { | struct Rogan6PS : Rogan { | ||||
| Rogan6PS() { | Rogan6PS() { | ||||
| box.size = Vec(89, 89); | box.size = Vec(89, 89); | ||||
| minAngle = -0.83*M_PI; | |||||
| maxAngle = 0.83*M_PI; | |||||
| // minAngle = -0.83*M_PI; | |||||
| // maxAngle = 0.83*M_PI; | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -263,8 +263,8 @@ struct SynthTechAlco : SVGKnob { | |||||
| struct Davies1900hKnob : SVGKnob { | struct Davies1900hKnob : SVGKnob { | ||||
| Davies1900hKnob() { | Davies1900hKnob() { | ||||
| box.size = Vec(36, 36); | box.size = Vec(36, 36); | ||||
| minAngle = -0.75*M_PI; | |||||
| maxAngle = 0.75*M_PI; | |||||
| minAngle = -0.83*M_PI; | |||||
| maxAngle = 0.83*M_PI; | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -304,6 +304,14 @@ struct Davies1900hLargeRedKnob : Davies1900hKnob { | |||||
| } | } | ||||
| }; | }; | ||||
| struct Davies1900hSmallBlackKnob : Davies1900hKnob { | |||||
| Davies1900hSmallBlackKnob() { | |||||
| setSVG(SVG::load("res/ComponentLibrary/Davies1900hSmallBlack.svg")); | |||||
| } | |||||
| }; | |||||
| struct Davies1900hSmallBlackSnapKnob : Davies1900hSmallBlackKnob, SnapKnob {}; | |||||
| struct Trimpot : SVGKnob { | struct Trimpot : SVGKnob { | ||||
| Trimpot() { | Trimpot() { | ||||
| box.size = Vec(17, 17); | box.size = Vec(17, 17); | ||||
| @@ -322,6 +330,8 @@ struct BefacoBigKnob : SVGKnob { | |||||
| } | } | ||||
| }; | }; | ||||
| struct BefacoBigSnapKnob : BefacoBigKnob, SnapKnob {}; | |||||
| struct BefacoTinyKnob : SVGKnob { | struct BefacoTinyKnob : SVGKnob { | ||||
| BefacoTinyKnob() { | BefacoTinyKnob() { | ||||
| box.size = Vec(26, 26); | box.size = Vec(26, 26); | ||||
| @@ -460,6 +470,13 @@ struct SmallLight : BASE { | |||||
| } | } | ||||
| }; | }; | ||||
| template <typename BASE> | |||||
| struct TinyLight : BASE { | |||||
| TinyLight() { | |||||
| this->box.size = Vec(5, 5); | |||||
| } | |||||
| }; | |||||
| //////////////////// | //////////////////// | ||||
| // Switches and Buttons | // Switches and Buttons | ||||
| //////////////////// | //////////////////// | ||||
| @@ -422,14 +422,28 @@ struct SlewLimiter { | |||||
| }; | }; | ||||
| /** Triggered when input value rises above 0.0 */ | |||||
| struct Trigger { | |||||
| float lastIn = 0.0; | |||||
| /** Returns whether a trigger is detected */ | |||||
| struct SchmittTrigger { | |||||
| // false is low, true is high | |||||
| bool state = false; | |||||
| float low = 0.0; | |||||
| float high = 1.0; | |||||
| void setThresholds(float low, float high) { | |||||
| this->low = low; | |||||
| this->high = high; | |||||
| } | |||||
| /** Returns true if triggered */ | |||||
| bool process(float in) { | bool process(float in) { | ||||
| bool triggered = (lastIn <= 0.0 && in > 0.0); | |||||
| lastIn = in; | |||||
| return triggered; | |||||
| if (state) { | |||||
| if (in < low) | |||||
| state = false; | |||||
| } | |||||
| else { | |||||
| if (in >= high) { | |||||
| state = true; | |||||
| return true; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -28,7 +28,7 @@ inline int absi(int a) { | |||||
| // Euclidean modulus, always returns 0 <= mod < base for positive base | // Euclidean modulus, always returns 0 <= mod < base for positive base | ||||
| // Assumes this architecture's division is non-Euclidean | // Assumes this architecture's division is non-Euclidean | ||||
| inline int eucmod(int a, int base) { | |||||
| inline int eucmodi(int a, int base) { | |||||
| int mod = a % base; | int mod = a % base; | ||||
| return mod < 0 ? mod + base : mod; | return mod < 0 ? mod + base : mod; | ||||
| } | } | ||||
| @@ -41,7 +41,7 @@ inline int log2i(int n) { | |||||
| return i; | return i; | ||||
| } | } | ||||
| inline bool ispow2(int n) { | |||||
| inline bool ispow2i(int n) { | |||||
| return n > 0 && (n & (n - 1)) == 0; | return n > 0 && (n & (n - 1)) == 0; | ||||
| } | } | ||||
| @@ -54,6 +54,11 @@ inline float sgnf(float x) { | |||||
| return copysignf(1.0, x); | return copysignf(1.0, x); | ||||
| } | } | ||||
| inline float eucmodf(float a, float base) { | |||||
| float mod = fmodf(a, base); | |||||
| return mod < 0.0 ? mod + base : mod; | |||||
| } | |||||
| /** Limits a value between a minimum and maximum */ | /** Limits a value between a minimum and maximum */ | ||||
| inline float clampf(float x, float min, float max) { | inline float clampf(float x, float min, float max) { | ||||
| return x > max ? max : x < min ? min : x; | return x > max ? max : x < min ? min : x; | ||||
| @@ -117,7 +122,7 @@ inline void setf(float *p, float v) { | |||||
| } | } | ||||
| /** Linearly interpolate an array `p` with index `x` | /** Linearly interpolate an array `p` with index `x` | ||||
| Assumes that the array at `p` is of length at least ceil(x)+1. | |||||
| Assumes that the array at `p` is of length at least floor(x)+1. | |||||
| */ | */ | ||||
| inline float interpf(const float *p, float x) { | inline float interpf(const float *p, float x) { | ||||
| int xi = x; | int xi = x; | ||||
| @@ -6,7 +6,7 @@ namespace rack { | |||||
| void Light::draw(NVGcontext *vg) { | void Light::draw(NVGcontext *vg) { | ||||
| NVGcolor bgColor = nvgRGBf(0.0, 0.0, 0.0); | NVGcolor bgColor = nvgRGBf(0.0, 0.0, 0.0); | ||||
| float radius = box.size.x / 2.0; | |||||
| float radius = roundf(box.size.x / 2.0); | |||||
| float oradius = radius + 30.0; | float oradius = radius + 30.0; | ||||
| // Solid | // Solid | ||||
| @@ -105,6 +105,8 @@ void ModuleWidget::resetParams() { | |||||
| } | } | ||||
| void ModuleWidget::draw(NVGcontext *vg) { | void ModuleWidget::draw(NVGcontext *vg) { | ||||
| nvgScissor(vg, 0, 0, box.size.x, box.size.y); | |||||
| Widget::draw(vg); | Widget::draw(vg); | ||||
| bndBevel(vg, 0.0, 0.0, box.size.x, box.size.y); | bndBevel(vg, 0.0, 0.0, box.size.x, box.size.y); | ||||
| @@ -128,6 +130,8 @@ void ModuleWidget::draw(NVGcontext *vg) { | |||||
| bndMenuItem(vg, 0.0, 0.0, box.size.x, BND_WIDGET_HEIGHT, BND_DEFAULT, -1, text.c_str()); | bndMenuItem(vg, 0.0, 0.0, box.size.x, BND_WIDGET_HEIGHT, BND_DEFAULT, -1, text.c_str()); | ||||
| nvgRestore(vg); | nvgRestore(vg); | ||||
| } | } | ||||
| nvgResetScissor(vg); | |||||
| } | } | ||||
| void ModuleWidget::onDragStart() { | void ModuleWidget::onDragStart() { | ||||
| @@ -4,7 +4,7 @@ | |||||
| namespace rack { | namespace rack { | ||||
| void SpriteKnob::step() { | void SpriteKnob::step() { | ||||
| index = eucmod((int) roundf(mapf(value, minValue, maxValue, minIndex, maxIndex)), spriteCount); | |||||
| index = eucmodi((int) roundf(mapf(value, minValue, maxValue, minIndex, maxIndex)), spriteCount); | |||||
| } | } | ||||
| } // namespace rack | } // namespace rack | ||||
| @@ -113,8 +113,7 @@ Toolbar::Toolbar() { | |||||
| SampleRateChoice *srChoice = new SampleRateChoice(); | SampleRateChoice *srChoice = new SampleRateChoice(); | ||||
| srChoice->box.pos = Vec(xPos, margin); | srChoice->box.pos = Vec(xPos, margin); | ||||
| srChoice->box.size.x = 100; | srChoice->box.size.x = 100; | ||||
| // TODO Change to actual sample rate, e.g. 44100 Hz | |||||
| srChoice->text = "Sample Rate"; | |||||
| srChoice->text = ""; | |||||
| addChild(srChoice); | addChild(srChoice); | ||||
| xPos += srChoice->box.size.x; | xPos += srChoice->box.size.x; | ||||
| } | } | ||||
| @@ -7,7 +7,7 @@ | |||||
| namespace rack { | namespace rack { | ||||
| // TODO | // TODO | ||||
| // Convert this to xoroshiro128+ and custom normal dist implementation | |||||
| // Convert this to xoroshiro128+ or something, and write custom normal dist implementation | |||||
| static std::random_device rd; | static std::random_device rd; | ||||
| static std::mt19937 rng(rd()); | static std::mt19937 rng(rd()); | ||||
| @@ -8,7 +8,7 @@ QuantityWidget::QuantityWidget() { | |||||
| } | } | ||||
| void QuantityWidget::setValue(float value) { | void QuantityWidget::setValue(float value) { | ||||
| this->value = clampf(value, minValue, maxValue); | |||||
| this->value = clampf(value, fminf(minValue, maxValue), fmaxf(minValue, maxValue)); | |||||
| onChange(); | onChange(); | ||||
| } | } | ||||