@@ -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(); | ||||
} | } | ||||