@@ -1,5 +1,5 @@ | |||||
ARCH ?= lin | ARCH ?= lin | ||||
FLAGS = -g -Wall -O3 -msse -mfpmath=sse -ffast-math -fno-finite-math-only \ | |||||
FLAGS = -g -Wall -O2 -msse -mfpmath=sse -ffast-math -fno-finite-math-only \ | |||||
-I./ext -I./include | -I./ext -I./include | ||||
CXXFLAGS = -fno-exceptions | CXXFLAGS = -fno-exceptions | ||||
@@ -41,6 +41,7 @@ struct ModuleWidget : OpaqueWidget { | |||||
void fromJson(json_t *root); | void fromJson(json_t *root); | ||||
void disconnectPorts(); | void disconnectPorts(); | ||||
void resetParams(); | void resetParams(); | ||||
void randomizeParams(); | |||||
void draw(NVGcontext *vg); | void draw(NVGcontext *vg); | ||||
@@ -518,6 +518,14 @@ struct TL1105 : SVGSwitch, MomentarySwitch { | |||||
} | } | ||||
}; | }; | ||||
struct LEDButton : SVGSwitch, MomentarySwitch { | |||||
LEDButton() { | |||||
addFrame(SVG::load("res/ComponentLibrary/LEDButton.svg")); | |||||
sw->wrap(); | |||||
box.size = sw->box.size; | |||||
} | |||||
}; | |||||
struct BefacoSwitch : SVGSwitch, ToggleSwitch { | struct BefacoSwitch : SVGSwitch, ToggleSwitch { | ||||
BefacoSwitch() { | BefacoSwitch() { | ||||
addFrame(SVG::load("res/ComponentLibrary/BefacoSwitch_0.svg")); | addFrame(SVG::load("res/ComponentLibrary/BefacoSwitch_0.svg")); | ||||
@@ -560,8 +568,8 @@ struct ScrewBlack : SVGScrew { | |||||
struct LightPanel : Panel { | struct LightPanel : Panel { | ||||
LightPanel() { | LightPanel() { | ||||
// backgroundColor = nvgRGB(0xe8, 0xe8, 0xe8); | |||||
backgroundColor = nvgRGB(0xf4, 0xf4, 0xf4); | |||||
// backgroundColor = nvgRGB(0xe6, 0xe6, 0xe6); | |||||
backgroundColor = nvgRGB(0xf0, 0xf0, 0xf0); | |||||
borderColor = nvgRGB(0xac, 0xac, 0xac); | borderColor = nvgRGB(0xac, 0xac, 0xac); | ||||
} | } | ||||
}; | }; | ||||
@@ -76,9 +76,43 @@ struct SimpleFFT { | |||||
typedef void (*stepCallback)(float x, const float y[], float dydt[]); | typedef void (*stepCallback)(float x, const float y[], float dydt[]); | ||||
/** Solve an ODE system using the 1st order Euler method */ | /** Solve an ODE system using the 1st order Euler method */ | ||||
void stepEuler(stepCallback f, float x, float dx, float y[], int len); | |||||
inline void stepEuler(stepCallback f, float x, float dx, float y[], int len) { | |||||
float k[len]; | |||||
f(x, y, k); | |||||
for (int i = 0; i < len; i++) { | |||||
y[i] += dx * k[i]; | |||||
} | |||||
} | |||||
/** Solve an ODE system using the 4th order Runge-Kutta method */ | /** Solve an ODE system using the 4th order Runge-Kutta method */ | ||||
void stepRK4(stepCallback f, float x, float dx, float y[], int len); | |||||
inline void stepRK4(stepCallback f, float x, float dx, float y[], int len) { | |||||
float k1[len]; | |||||
float k2[len]; | |||||
float k3[len]; | |||||
float k4[len]; | |||||
float yi[len]; | |||||
f(x, y, k1); | |||||
for (int i = 0; i < len; i++) { | |||||
yi[i] = y[i] + k1[i] * dx / 2.0; | |||||
} | |||||
f(x + dx / 2.0, yi, k2); | |||||
for (int i = 0; i < len; i++) { | |||||
yi[i] = y[i] + k2[i] * dx / 2.0; | |||||
} | |||||
f(x + dx / 2.0, yi, k3); | |||||
for (int i = 0; i < len; i++) { | |||||
yi[i] = y[i] + k3[i] * dx; | |||||
} | |||||
f(x + dx, yi, k4); | |||||
for (int i = 0; i < len; i++) { | |||||
y[i] += dx * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]) / 6.0; | |||||
} | |||||
} | |||||
/** A simple cyclic buffer. | /** A simple cyclic buffer. | ||||
@@ -423,8 +457,8 @@ struct SlewLimiter { | |||||
struct SchmittTrigger { | struct SchmittTrigger { | ||||
// false is low, true is high | |||||
bool state = false; | |||||
/** 0 unknown, 1 low, 2 high */ | |||||
int state = 0; | |||||
float low = 0.0; | float low = 0.0; | ||||
float high = 1.0; | float high = 1.0; | ||||
void setThresholds(float low, float high) { | void setThresholds(float low, float high) { | ||||
@@ -433,17 +467,19 @@ struct SchmittTrigger { | |||||
} | } | ||||
/** Returns true if triggered */ | /** Returns true if triggered */ | ||||
bool process(float in) { | bool process(float in) { | ||||
if (state) { | |||||
if (in < low) | |||||
state = false; | |||||
bool triggered = false; | |||||
if (in >= high) { | |||||
if (state == 1) | |||||
triggered = true; | |||||
state = 2; | |||||
} | } | ||||
else { | |||||
if (in >= high) { | |||||
state = true; | |||||
return true; | |||||
} | |||||
else if (in <= low) { | |||||
state = 1; | |||||
} | } | ||||
return false; | |||||
return triggered; | |||||
} | |||||
void reset() { | |||||
state = 0; | |||||
} | } | ||||
}; | }; | ||||
@@ -15,5 +15,6 @@ const char *guiSaveDialog(const char *filters, const char *filename); | |||||
const char *guiOpenDialog(const char *filters, const char *filename); | const char *guiOpenDialog(const char *filters, const char *filename); | ||||
extern NVGcontext *gVg; | extern NVGcontext *gVg; | ||||
extern std::shared_ptr<Font> gGuiFont; | |||||
} // namespace rack | } // namespace rack |
@@ -69,7 +69,7 @@ inline float chopf(float x, float eps) { | |||||
return -eps < x && x < eps ? 0.0 : x; | return -eps < x && x < eps ? 0.0 : x; | ||||
} | } | ||||
inline float mapf(float x, float xMin, float xMax, float yMin, float yMax) { | |||||
inline float rescalef(float x, float xMin, float xMax, float yMin, float yMax) { | |||||
return yMin + (x - xMin) / (xMax - xMin) * (yMax - yMin); | return yMin + (x - xMin) / (xMax - xMin) * (yMax - yMin); | ||||
} | } | ||||
@@ -130,6 +130,15 @@ inline float interpf(const float *p, float x) { | |||||
return crossf(p[xi], p[xi+1], xf); | return crossf(p[xi], p[xi+1], xf); | ||||
} | } | ||||
/** Complex multiply c = a * b | |||||
It is of course acceptable to reuse arguments | |||||
i.e. cmultf(&ar, &ai, ar, ai, br, bi) | |||||
*/ | |||||
inline void cmultf(float *cr, float *ci, float ar, float ai, float br, float bi) { | |||||
*cr = ar * br - ai * bi; | |||||
*ci = ar * bi + ai * br; | |||||
} | |||||
//////////////////// | //////////////////// | ||||
// 2D float vector | // 2D float vector | ||||
//////////////////// | //////////////////// | ||||
@@ -1,7 +1,7 @@ | |||||
#pragma once | #pragma once | ||||
#include "math.hpp" | |||||
#include "util.hpp" | #include "util.hpp" | ||||
#include "math.hpp" | |||||
#include "plugin.hpp" | #include "plugin.hpp" | ||||
#include "engine.hpp" | #include "engine.hpp" | ||||
#include "gui.hpp" | #include "gui.hpp" | ||||
@@ -11,6 +11,24 @@ | |||||
#include <mutex> | #include <mutex> | ||||
/** Surrounds raw text with quotes | |||||
Example: | |||||
printf("Hello " STRINGIFY(world)) | |||||
will expand to | |||||
printf("Hello " "world") | |||||
and of course the C++ lexer/parser will then concatenate the string literals | |||||
*/ | |||||
#define STRINGIFY(x) #x | |||||
/** Converts a macro to a string literal | |||||
Example: | |||||
#define NAME "world" | |||||
printf("Hello " TOSTRING(NAME)) | |||||
will expand to | |||||
printf("Hello " "world") | |||||
*/ | |||||
#define TOSTRING(x) STRINGIFY(x) | |||||
namespace rack { | namespace rack { | ||||
@@ -104,6 +104,12 @@ void ModuleWidget::resetParams() { | |||||
} | } | ||||
} | } | ||||
void ModuleWidget::randomizeParams() { | |||||
for (ParamWidget *param : params) { | |||||
param->setValue(rescalef(randomf(), 0.0, 1.0, param->minValue, param->maxValue)); | |||||
} | |||||
} | |||||
void ModuleWidget::draw(NVGcontext *vg) { | void ModuleWidget::draw(NVGcontext *vg) { | ||||
nvgScissor(vg, 0, 0, box.size.x, box.size.y); | nvgScissor(vg, 0, 0, box.size.x, box.size.y); | ||||
@@ -160,6 +166,13 @@ struct ResetParamsMenuItem : MenuItem { | |||||
} | } | ||||
}; | }; | ||||
struct RandomizeParamsMenuItem : MenuItem { | |||||
ModuleWidget *moduleWidget; | |||||
void onAction() { | |||||
moduleWidget->randomizeParams(); | |||||
} | |||||
}; | |||||
struct CloneModuleMenuItem : MenuItem { | struct CloneModuleMenuItem : MenuItem { | ||||
ModuleWidget *moduleWidget; | ModuleWidget *moduleWidget; | ||||
void onAction() { | void onAction() { | ||||
@@ -197,6 +210,11 @@ void ModuleWidget::onMouseDown(int button) { | |||||
resetItem->moduleWidget = this; | resetItem->moduleWidget = this; | ||||
menu->pushChild(resetItem); | menu->pushChild(resetItem); | ||||
RandomizeParamsMenuItem *randomizeParams = new RandomizeParamsMenuItem(); | |||||
randomizeParams->text = "Randomize"; | |||||
randomizeParams->moduleWidget = this; | |||||
menu->pushChild(randomizeParams); | |||||
DisconnectPortsMenuItem *disconnectItem = new DisconnectPortsMenuItem(); | DisconnectPortsMenuItem *disconnectItem = new DisconnectPortsMenuItem(); | ||||
disconnectItem->text = "Disconnect cables"; | disconnectItem->text = "Disconnect cables"; | ||||
disconnectItem->moduleWidget = this; | disconnectItem->moduleWidget = this; | ||||
@@ -24,7 +24,7 @@ void SVGKnob::setSVG(std::shared_ptr<SVG> svg) { | |||||
void SVGKnob::step() { | void SVGKnob::step() { | ||||
// Re-transform TransformWidget if dirty | // Re-transform TransformWidget if dirty | ||||
if (dirty) { | if (dirty) { | ||||
float angle = mapf(value, minValue, maxValue, minAngle, maxAngle); | |||||
float angle = rescalef(value, minValue, maxValue, minAngle, maxAngle); | |||||
tw->identity(); | tw->identity(); | ||||
// Rotate SVG | // Rotate SVG | ||||
Vec center = sw->box.getCenter(); | Vec center = sw->box.getCenter(); | ||||
@@ -15,7 +15,7 @@ SVGSlider::SVGSlider() { | |||||
void SVGSlider::step() { | void SVGSlider::step() { | ||||
if (dirty) { | if (dirty) { | ||||
// Update handle position | // Update handle position | ||||
Vec handlePos = Vec(mapf(value, minValue, maxValue, minHandlePos.x, maxHandlePos.x), mapf(value, minValue, maxValue, minHandlePos.y, maxHandlePos.y)); | |||||
Vec handlePos = Vec(rescalef(value, minValue, maxValue, minHandlePos.x, maxHandlePos.x), rescalef(value, minValue, maxValue, minHandlePos.y, maxHandlePos.y)); | |||||
handle->box.pos = handlePos; | handle->box.pos = handlePos; | ||||
} | } | ||||
FramebufferWidget::step(); | FramebufferWidget::step(); | ||||
@@ -24,7 +24,7 @@ void SVGSwitch::step() { | |||||
} | } | ||||
void SVGSwitch::onChange() { | void SVGSwitch::onChange() { | ||||
int index = roundf(mapf(value, minValue, maxValue, 0, frames.size() - 1)); | |||||
int index = roundf(rescalef(value, minValue, maxValue, 0, frames.size() - 1)); | |||||
if (0 <= index && index < (int)frames.size()) | if (0 <= index && index < (int)frames.size()) | ||||
sw->svg = frames[index]; | sw->svg = frames[index]; | ||||
dirty = true; | dirty = true; | ||||
@@ -4,7 +4,7 @@ | |||||
namespace rack { | namespace rack { | ||||
void SpriteKnob::step() { | void SpriteKnob::step() { | ||||
index = eucmodi((int) roundf(mapf(value, minValue, maxValue, minIndex, maxIndex)), spriteCount); | |||||
index = eucmodi((int) roundf(rescalef(value, minValue, maxValue, minIndex, maxIndex)), spriteCount); | |||||
} | } | ||||
} // namespace rack | } // namespace rack |
@@ -14,44 +14,4 @@ const float minblep_16_32[] = { | |||||
}; | }; | ||||
void stepEuler(stepCallback f, float x, float dx, float y[], int len) { | |||||
float k[len]; | |||||
f(x, y, k); | |||||
for (int i = 0; i < len; i++) { | |||||
y[i] += dx * k[i]; | |||||
} | |||||
} | |||||
void stepRK4(stepCallback f, float x, float dx, float y[], int len) { | |||||
float k1[len]; | |||||
float k2[len]; | |||||
float k3[len]; | |||||
float k4[len]; | |||||
float yi[len]; | |||||
f(x, y, k1); | |||||
for (int i = 0; i < len; i++) { | |||||
yi[i] = y[i] + k1[i] * dx / 2.0; | |||||
} | |||||
f(x + dx / 2.0, yi, k2); | |||||
for (int i = 0; i < len; i++) { | |||||
yi[i] = y[i] + k2[i] * dx / 2.0; | |||||
} | |||||
f(x + dx / 2.0, yi, k3); | |||||
for (int i = 0; i < len; i++) { | |||||
yi[i] = y[i] + k3[i] * dx; | |||||
} | |||||
f(x + dx, yi, k4); | |||||
for (int i = 0; i < len; i++) { | |||||
y[i] += dx * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]) / 6.0; | |||||
} | |||||
} | |||||
} // namespace rack | } // namespace rack |
@@ -25,7 +25,7 @@ extern "C" { | |||||
namespace rack { | namespace rack { | ||||
static GLFWwindow *window = NULL; | static GLFWwindow *window = NULL; | ||||
static std::shared_ptr<Font> defaultFont; | |||||
std::shared_ptr<Font> gGuiFont; | |||||
NVGcontext *gVg = NULL; | NVGcontext *gVg = NULL; | ||||
@@ -232,13 +232,13 @@ void guiInit() { | |||||
assert(gVg); | assert(gVg); | ||||
// Set up Blendish | // Set up Blendish | ||||
defaultFont = Font::load("res/DejaVuSans.ttf"); | |||||
bndSetFont(defaultFont->handle); | |||||
gGuiFont = Font::load("res/DejaVuSans.ttf"); | |||||
bndSetFont(gGuiFont->handle); | |||||
// bndSetIconImage(loadImage("res/icons.png")); | // bndSetIconImage(loadImage("res/icons.png")); | ||||
} | } | ||||
void guiDestroy() { | void guiDestroy() { | ||||
defaultFont.reset(); | |||||
gGuiFont.reset(); | |||||
nvgDeleteGL2(gVg); | nvgDeleteGL2(gVg); | ||||
// nvgDeleteGL3(gVg); | // nvgDeleteGL3(gVg); | ||||
glfwDestroyWindow(window); | glfwDestroyWindow(window); | ||||
@@ -4,7 +4,7 @@ | |||||
namespace rack { | namespace rack { | ||||
void ProgressBar::draw(NVGcontext *vg) { | void ProgressBar::draw(NVGcontext *vg) { | ||||
float progress = mapf(value, minValue, maxValue, 0.0, 1.0); | |||||
float progress = rescalef(value, minValue, maxValue, 0.0, 1.0); | |||||
bndSlider(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_ALL, BND_DEFAULT, progress, getText().c_str(), NULL); | bndSlider(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_ALL, BND_DEFAULT, progress, getText().c_str(), NULL); | ||||
} | } | ||||
@@ -7,7 +7,7 @@ namespace rack { | |||||
#define SLIDER_SENSITIVITY 0.001 | #define SLIDER_SENSITIVITY 0.001 | ||||
void Slider::draw(NVGcontext *vg) { | void Slider::draw(NVGcontext *vg) { | ||||
float progress = mapf(value, minValue, maxValue, 0.0, 1.0); | |||||
float progress = rescalef(value, minValue, maxValue, 0.0, 1.0); | |||||
bndSlider(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE, state, progress, getText().c_str(), NULL); | bndSlider(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE, state, progress, getText().c_str(), NULL); | ||||
} | } | ||||