@@ -1,6 +1,6 @@ | |||
ARCH ?= linux | |||
CFLAGS = -MMD -g -Wall -O2 | |||
CXXFLAGS = -MMD -g -Wall -std=c++11 -O3 -ffast-math -fno-exceptions \ | |||
CXXFLAGS = -MMD -g -Wall -std=c++11 -O3 -msse -mfpmath=sse -ffast-math -fno-exceptions \ | |||
-I./ext -I./include | |||
LDFLAGS = | |||
@@ -1,6 +1,6 @@ | |||
*Note: This software is in semi-public alpha. If you have stumbled upon this project, feel free to try it out and report bugs to the GitHub issue tracker. However, with more users it becomes difficult to make breaking changes, so please don't spread this to your friends and the Internet just yet, until the official announcement has been made.* | |||
*Feel free to post **bugs**, **enhancements**, and **questions** on the [Issue Tracker](https://github.com/AndrewBelt/Rack/issues). To vote for a feature, give a thumbs-up on the first post.* | |||
*Feel free to post bugs, enhancements, and questions on the [Issue Tracker](https://github.com/AndrewBelt/Rack/issues). To vote for a feature, give a thumbs-up on the first post.* | |||
# Rack | |||
@@ -3,14 +3,78 @@ | |||
#include <assert.h> | |||
#include <string.h> | |||
#include <samplerate.h> | |||
#include "util.hpp" | |||
#include <complex.h> | |||
#include "math.hpp" | |||
// in minBLEP.cpp | |||
float *generateMinBLEP(int zeroCrossings, int overSampling); | |||
namespace rack { | |||
namespace rack { | |||
/** Construct a C-style complex float | |||
With -O3 this is as fast as the 1.0 + 1.0*I syntax but it stops the compiler from complaining | |||
*/ | |||
inline float _Complex complexf(float r, float i) { | |||
union { | |||
float x[2]; | |||
float _Complex c; | |||
} v; | |||
v.x[0] = r; | |||
v.x[1] = i; | |||
return v.c; | |||
} | |||
/** Simple FFT implementation | |||
If you need something fast, use pffft, KissFFT, etc instead. | |||
The size N must be a power of 2 | |||
*/ | |||
struct SimpleFFT { | |||
int N; | |||
/** Twiddle factors e^(2pi k/N), interleaved complex numbers */ | |||
float _Complex *tw; | |||
SimpleFFT(int N, bool inverse) : N(N) { | |||
tw = new float _Complex[N]; | |||
for (int i = 0; i < N; i++) { | |||
float phase = 2*M_PI * (float)i / N; | |||
if (inverse) | |||
phase *= -1.0; | |||
tw[i] = cexpf(phase * complexf(0.0, 1.0)); | |||
} | |||
} | |||
~SimpleFFT() { | |||
delete[] tw; | |||
} | |||
/** Reference naive implementation | |||
x and y are arrays of interleaved complex numbers | |||
y must be size N/s | |||
s is the stride factor for the x array which divides the size N | |||
*/ | |||
void dft(const float _Complex *x, float _Complex *y, int s=1) { | |||
for (int k = 0; k < N/s; k++) { | |||
float _Complex yk = 0.0; | |||
for (int n = 0; n < N; n += s) { | |||
int m = (n*k) % N; | |||
yk += x[n] * tw[m]; | |||
} | |||
y[k] = yk; | |||
} | |||
} | |||
void fft(const float _Complex *x, float _Complex *y, int s=1) { | |||
if (N/s <= 2) { | |||
// Naive DFT is faster than further FFT recursions at this point | |||
dft(x, y, s); | |||
return; | |||
} | |||
float _Complex e[N/(2*s)]; // Even inputs | |||
float _Complex o[N/(2*s)]; // Odd inputs | |||
fft(x, e, 2*s); | |||
fft(x + s, o, 2*s); | |||
for (int k = 0; k < N/(2*s); k++) { | |||
int m = (k*s) % N; | |||
y[k] = e[k] + tw[m] * o[k]; | |||
y[k + N/(2*s)] = e[k] - tw[m] * o[k]; | |||
} | |||
} | |||
}; | |||
/** A simple cyclic buffer. | |||
@@ -196,12 +260,15 @@ struct SampleRateConverter { | |||
}; | |||
// Pre-made minBLEP samples in minBLEP.cpp | |||
extern const float minblep_16_32[]; | |||
template<int ZERO_CROSSINGS> | |||
struct MinBLEP { | |||
float buf[2*ZERO_CROSSINGS] = {}; | |||
int pos = 0; | |||
/** You must set this to the array generated by generateMinBLEP() */ | |||
float *minblep = NULL; | |||
const float *minblep; | |||
int oversample; | |||
/** Places a discontinuity with magnitude dx at -1 < p <= 0 relative to the current frame */ | |||
@@ -60,11 +60,11 @@ inline float quintic(float x) { | |||
return x*x*x*x*x; | |||
} | |||
// Euclidean modulus, always returns 0 <= mod < base for positive base | |||
// Assumes this architecture's division is non-Euclidean | |||
inline int eucMod(int a, int base) { | |||
int mod = a % base; | |||
return mod < 0 ? mod + base : mod; | |||
inline float sincf(float x) { | |||
if (x == 0.0) | |||
return 1.0; | |||
x *= M_PI; | |||
return sinf(x) / x; | |||
} | |||
inline float getf(const float *p, float v = 0.0) { | |||
@@ -85,6 +85,25 @@ inline float interpf(const float *p, float x) { | |||
return crossf(p[xi], p[xi+1], xf); | |||
} | |||
// Euclidean modulus, always returns 0 <= mod < base for positive base | |||
// Assumes this architecture's division is non-Euclidean | |||
inline int eucmod(int a, int base) { | |||
int mod = a % base; | |||
return mod < 0 ? mod + base : mod; | |||
} | |||
inline int log2i(int n) { | |||
int i = 0; | |||
while (n >>= 1) { | |||
i++; | |||
} | |||
return i; | |||
} | |||
inline bool ispow2(int n) { | |||
return n > 0 && (n & (n - 1)) == 0; | |||
} | |||
//////////////////// | |||
// 2D float vector | |||
//////////////////// | |||
@@ -167,79 +186,5 @@ struct Rect { | |||
} | |||
}; | |||
//////////////////// | |||
// Simple FFT implementation | |||
//////////////////// | |||
// Derived from the Italian Wikipedia article for FFT | |||
// https://it.wikipedia.org/wiki/Trasformata_di_Fourier_veloce | |||
// If you need speed, use KissFFT, pffft, etc instead. | |||
inline int log2i(int n) { | |||
int i = 0; | |||
while (n >>= 1) { | |||
i++; | |||
} | |||
return i; | |||
} | |||
inline bool isPowerOf2(int n) { | |||
return n > 0 && (n & (n-1)) == 0; | |||
} | |||
/* | |||
inline int reverse(int N, int n) //calculating revers number | |||
{ | |||
int j, p = 0; | |||
for(j = 1; j <= log2i(N); j++) { | |||
if(n & (1 << (log2i(N) - j))) | |||
p |= 1 << (j - 1); | |||
} | |||
return p; | |||
} | |||
inline void ordina(complex<double>* f1, int N) //using the reverse order in the array | |||
{ | |||
complex<double> f2[MAX]; | |||
for(int i = 0; i < N; i++) | |||
f2[i] = f1[reverse(N, i)]; | |||
for(int j = 0; j < N; j++) | |||
f1[j] = f2[j]; | |||
} | |||
inline void transform(complex<double>* f, int N) | |||
{ | |||
ordina(f, N); //first: reverse order | |||
complex<double> *W; | |||
W = (complex<double> *)malloc(N / 2 * sizeof(complex<double>)); | |||
W[1] = polar(1., -2. * M_PI / N); | |||
W[0] = 1; | |||
for(int i = 2; i < N / 2; i++) | |||
W[i] = pow(W[1], i); | |||
int n = 1; | |||
int a = N / 2; | |||
for(int j = 0; j < log2i(N); j++) { | |||
for(int i = 0; i < N; i++) { | |||
if(!(i & n)) { | |||
complex<double> temp = f[i]; | |||
complex<double> Temp = W[(i * a) % (n * a)] * f[i + n]; | |||
f[i] = temp + Temp; | |||
f[i + n] = temp - Temp; | |||
} | |||
} | |||
n *= 2; | |||
a = a / 2; | |||
} | |||
} | |||
inline void FFT(complex<double>* f, int N, double d) | |||
{ | |||
transform(f, N); | |||
for(int i = 0; i < N; i++) | |||
f[i] *= d; //multiplying by step | |||
} | |||
*/ | |||
} // namespace rack |
@@ -7,7 +7,7 @@ | |||
#include <set> | |||
#include <thread> | |||
#include <mutex> | |||
#include "widgets.hpp" | |||
#include "rackwidgets.hpp" | |||
namespace rack { | |||
@@ -0,0 +1,224 @@ | |||
#pragma once | |||
#include "widgets.hpp" | |||
#include <jansson.h> | |||
namespace rack { | |||
struct Module; | |||
struct Wire; | |||
struct RackWidget; | |||
struct ParamWidget; | |||
struct InputPort; | |||
struct OutputPort; | |||
//////////////////// | |||
// module | |||
//////////////////// | |||
// A 1U module should be 15x380. Thus the width of a module should be a factor of 15. | |||
struct Model; | |||
struct ModuleWidget : OpaqueWidget { | |||
Model *model = NULL; | |||
// Eventually this should be replaced with a `moduleId` which will be used for inter-process communication between the gui world and the audio world. | |||
Module *module = NULL; | |||
// int moduleId; | |||
std::vector<InputPort*> inputs; | |||
std::vector<OutputPort*> outputs; | |||
std::vector<ParamWidget*> params; | |||
ModuleWidget(Module *module); | |||
~ModuleWidget(); | |||
// Convenience functions for adding special widgets (calls addChild()) | |||
void addInput(InputPort *input); | |||
void addOutput(OutputPort *output); | |||
void addParam(ParamWidget *param); | |||
json_t *toJson(); | |||
void fromJson(json_t *root); | |||
void disconnectPorts(); | |||
void resetParams(); | |||
void cloneParams(ModuleWidget *source); | |||
void draw(NVGcontext *vg); | |||
bool requested = false; | |||
Vec requestedPos; | |||
Vec dragPos; | |||
void onDragStart(); | |||
void onDragMove(Vec mouseRel); | |||
void onDragEnd(); | |||
void onMouseDown(int button); | |||
}; | |||
struct WireWidget : OpaqueWidget { | |||
OutputPort *outputPort = NULL; | |||
InputPort *inputPort = NULL; | |||
Wire *wire = NULL; | |||
NVGcolor color; | |||
WireWidget(); | |||
~WireWidget(); | |||
void updateWire(); | |||
void draw(NVGcontext *vg); | |||
void drawOutputPlug(NVGcontext *vg); | |||
void drawInputPlug(NVGcontext *vg); | |||
}; | |||
struct RackWidget : OpaqueWidget { | |||
// Only put ModuleWidgets in here | |||
Widget *moduleContainer; | |||
// Only put WireWidgets in here | |||
Widget *wireContainer; | |||
WireWidget *activeWire = NULL; | |||
RackWidget(); | |||
~RackWidget(); | |||
void clear(); | |||
void savePatch(std::string filename); | |||
void loadPatch(std::string filename); | |||
json_t *toJson(); | |||
void fromJson(json_t *root); | |||
void repositionModule(ModuleWidget *module); | |||
void step(); | |||
void draw(NVGcontext *vg); | |||
void onMouseDown(int button); | |||
}; | |||
struct ModulePanel : TransparentWidget { | |||
NVGcolor backgroundColor; | |||
NVGcolor highlightColor; | |||
std::string imageFilename; | |||
void draw(NVGcontext *vg); | |||
}; | |||
//////////////////// | |||
// params | |||
//////////////////// | |||
struct Light : TransparentWidget, SpriteWidget { | |||
NVGcolor color; | |||
void draw(NVGcontext *vg); | |||
}; | |||
// If you don't add these to your ModuleWidget, it will fall out of the RackWidget | |||
struct Screw : TransparentWidget, SpriteWidget { | |||
Screw(); | |||
}; | |||
struct ParamWidget : OpaqueWidget, QuantityWidget { | |||
Module *module = NULL; | |||
int paramId; | |||
json_t *toJson(); | |||
void fromJson(json_t *root); | |||
void onMouseDown(int button); | |||
void onChange(); | |||
}; | |||
struct Knob : ParamWidget, SpriteWidget { | |||
int minIndex, maxIndex, spriteCount; | |||
void step(); | |||
void onDragStart(); | |||
void onDragMove(Vec mouseRel); | |||
void onDragEnd(); | |||
}; | |||
struct Switch : ParamWidget, SpriteWidget { | |||
}; | |||
struct ToggleSwitch : virtual Switch { | |||
void onDragStart() { | |||
index = 1; | |||
} | |||
void onDragEnd() { | |||
index = 0; | |||
} | |||
void onDragDrop(Widget *origin) { | |||
if (origin != this) | |||
return; | |||
// Cycle through modes | |||
// e.g. a range of [0.0, 3.0] would have modes 0, 1, 2, and 3. | |||
float v = value + 1.0; | |||
setValue(v > maxValue ? minValue : v); | |||
} | |||
}; | |||
struct MomentarySwitch : virtual Switch { | |||
void onDragStart() { | |||
setValue(maxValue); | |||
index = 1; | |||
} | |||
void onDragEnd() { | |||
setValue(minValue); | |||
index = 0; | |||
} | |||
}; | |||
//////////////////// | |||
// ports | |||
//////////////////// | |||
struct Port : OpaqueWidget, SpriteWidget { | |||
Module *module = NULL; | |||
WireWidget *connectedWire = NULL; | |||
Port(); | |||
~Port(); | |||
void disconnect(); | |||
int type; | |||
void drawGlow(NVGcontext *vg); | |||
void onMouseDown(int button); | |||
void onDragEnd(); | |||
}; | |||
struct InputPort : Port { | |||
int inputId; | |||
void draw(NVGcontext *vg); | |||
void onDragStart(); | |||
void onDragDrop(Widget *origin); | |||
}; | |||
struct OutputPort : Port { | |||
int outputId; | |||
void draw(NVGcontext *vg); | |||
void onDragStart(); | |||
void onDragDrop(Widget *origin); | |||
}; | |||
//////////////////// | |||
// scene | |||
//////////////////// | |||
struct Toolbar : OpaqueWidget { | |||
Slider *wireOpacitySlider; | |||
Slider *wireTensionSlider; | |||
RadioButton *cpuUsageButton; | |||
Toolbar(); | |||
void draw(NVGcontext *vg); | |||
}; | |||
struct Scene : OpaqueWidget { | |||
Toolbar *toolbar; | |||
ScrollWidget *scrollWidget; | |||
Widget *overlay = NULL; | |||
Scene(); | |||
void setOverlay(Widget *w); | |||
void step(); | |||
}; | |||
} // namespace rack |
@@ -2,7 +2,6 @@ | |||
#include <stdint.h> | |||
#include <string> | |||
#include "math.hpp" | |||
namespace rack { | |||
@@ -6,23 +6,15 @@ | |||
#include <list> | |||
#include <map> | |||
#include <jansson.h> | |||
#include "../ext/nanovg/src/nanovg.h" | |||
#include "../ext/oui/blendish.h" | |||
#include "math.hpp" | |||
#include "util.hpp" | |||
namespace rack { | |||
struct Module; | |||
struct Wire; | |||
struct RackWidget; | |||
struct ParamWidget; | |||
struct InputPort; | |||
struct OutputPort; | |||
//////////////////// | |||
// base class and traits | |||
@@ -227,6 +219,18 @@ struct ChoiceButton : Button { | |||
void draw(NVGcontext *vg); | |||
}; | |||
struct RadioButton : OpaqueWidget, QuantityWidget { | |||
BNDwidgetState state = BND_DEFAULT; | |||
RadioButton() { | |||
box.size.y = BND_WIDGET_HEIGHT; | |||
} | |||
void draw(NVGcontext *vg); | |||
void onMouseEnter(); | |||
void onMouseLeave(); | |||
void onDragDrop(Widget *origin); | |||
}; | |||
struct Slider : OpaqueWidget, QuantityWidget { | |||
BNDwidgetState state = BND_DEFAULT; | |||
@@ -272,206 +276,5 @@ struct Tooltip : Widget { | |||
void draw(NVGcontext *vg); | |||
}; | |||
//////////////////// | |||
// module | |||
//////////////////// | |||
// A 1U module should be 15x380. Thus the width of a module should be a factor of 15. | |||
struct Model; | |||
struct ModuleWidget : OpaqueWidget { | |||
Model *model = NULL; | |||
// Eventually this should be replaced with a `moduleId` which will be used for inter-process communication between the gui world and the audio world. | |||
Module *module = NULL; | |||
// int moduleId; | |||
std::vector<InputPort*> inputs; | |||
std::vector<OutputPort*> outputs; | |||
std::vector<ParamWidget*> params; | |||
ModuleWidget(Module *module); | |||
~ModuleWidget(); | |||
// Convenience functions for adding special widgets (calls addChild()) | |||
void addInput(InputPort *input); | |||
void addOutput(OutputPort *output); | |||
void addParam(ParamWidget *param); | |||
json_t *toJson(); | |||
void fromJson(json_t *root); | |||
void disconnectPorts(); | |||
void resetParams(); | |||
void cloneParams(ModuleWidget *source); | |||
void draw(NVGcontext *vg); | |||
bool requested = false; | |||
Vec requestedPos; | |||
Vec dragPos; | |||
void onDragStart(); | |||
void onDragMove(Vec mouseRel); | |||
void onDragEnd(); | |||
void onMouseDown(int button); | |||
}; | |||
struct WireWidget : OpaqueWidget { | |||
OutputPort *outputPort = NULL; | |||
InputPort *inputPort = NULL; | |||
Wire *wire = NULL; | |||
NVGcolor color; | |||
WireWidget(); | |||
~WireWidget(); | |||
void updateWire(); | |||
void draw(NVGcontext *vg); | |||
void drawOutputPlug(NVGcontext *vg); | |||
void drawInputPlug(NVGcontext *vg); | |||
}; | |||
struct RackWidget : OpaqueWidget { | |||
// Only put ModuleWidgets in here | |||
Widget *moduleContainer; | |||
// Only put WireWidgets in here | |||
Widget *wireContainer; | |||
WireWidget *activeWire = NULL; | |||
RackWidget(); | |||
~RackWidget(); | |||
void clear(); | |||
void savePatch(std::string filename); | |||
void loadPatch(std::string filename); | |||
json_t *toJson(); | |||
void fromJson(json_t *root); | |||
void repositionModule(ModuleWidget *module); | |||
void step(); | |||
void draw(NVGcontext *vg); | |||
void onMouseDown(int button); | |||
}; | |||
struct ModulePanel : TransparentWidget { | |||
NVGcolor backgroundColor; | |||
NVGcolor highlightColor; | |||
std::string imageFilename; | |||
void draw(NVGcontext *vg); | |||
}; | |||
//////////////////// | |||
// params | |||
//////////////////// | |||
struct Light : TransparentWidget, SpriteWidget { | |||
NVGcolor color; | |||
void draw(NVGcontext *vg); | |||
}; | |||
// If you don't add these to your ModuleWidget, it will fall out of the RackWidget | |||
struct Screw : TransparentWidget, SpriteWidget { | |||
Screw(); | |||
}; | |||
struct ParamWidget : OpaqueWidget, QuantityWidget { | |||
Module *module = NULL; | |||
int paramId; | |||
json_t *toJson(); | |||
void fromJson(json_t *root); | |||
void onMouseDown(int button); | |||
void onChange(); | |||
}; | |||
struct Knob : ParamWidget, SpriteWidget { | |||
int minIndex, maxIndex, spriteCount; | |||
void step(); | |||
void onDragStart(); | |||
void onDragMove(Vec mouseRel); | |||
void onDragEnd(); | |||
}; | |||
struct Switch : ParamWidget, SpriteWidget { | |||
}; | |||
struct ToggleSwitch : virtual Switch { | |||
void onDragStart() { | |||
index = 1; | |||
} | |||
void onDragEnd() { | |||
index = 0; | |||
} | |||
void onDragDrop(Widget *origin) { | |||
if (origin != this) | |||
return; | |||
// Cycle through modes | |||
// e.g. a range of [0.0, 3.0] would have modes 0, 1, 2, and 3. | |||
float v = value + 1.0; | |||
setValue(v > maxValue ? minValue : v); | |||
} | |||
}; | |||
struct MomentarySwitch : virtual Switch { | |||
void onDragStart() { | |||
setValue(maxValue); | |||
index = 1; | |||
} | |||
void onDragEnd() { | |||
setValue(minValue); | |||
index = 0; | |||
} | |||
}; | |||
//////////////////// | |||
// ports | |||
//////////////////// | |||
struct Port : OpaqueWidget, SpriteWidget { | |||
Module *module = NULL; | |||
WireWidget *connectedWire = NULL; | |||
Port(); | |||
~Port(); | |||
void disconnect(); | |||
int type; | |||
void drawGlow(NVGcontext *vg); | |||
void onMouseDown(int button); | |||
void onDragEnd(); | |||
}; | |||
struct InputPort : Port { | |||
int inputId; | |||
void draw(NVGcontext *vg); | |||
void onDragStart(); | |||
void onDragDrop(Widget *origin); | |||
}; | |||
struct OutputPort : Port { | |||
int outputId; | |||
void draw(NVGcontext *vg); | |||
void onDragStart(); | |||
void onDragDrop(Widget *origin); | |||
}; | |||
//////////////////// | |||
// scene | |||
//////////////////// | |||
struct Toolbar : OpaqueWidget { | |||
Slider *wireOpacitySlider; | |||
Slider *wireTensionSlider; | |||
Toolbar(); | |||
void draw(NVGcontext *vg); | |||
}; | |||
struct Scene : OpaqueWidget { | |||
Toolbar *toolbar; | |||
ScrollWidget *scrollWidget; | |||
Widget *overlay = NULL; | |||
Scene(); | |||
void setOverlay(Widget *w); | |||
void step(); | |||
}; | |||
} // namespace rack |
@@ -4,6 +4,7 @@ | |||
#include <math.h> | |||
#include <chrono> | |||
#include <condition_variable> | |||
#include <xmmintrin.h> | |||
#include "rack.hpp" | |||
@@ -82,6 +83,10 @@ void Rack::stop() { | |||
} | |||
void Rack::run() { | |||
// Set CPU to denormals-are-zero mode | |||
// http://carlh.net/plugins/denormals.php | |||
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); | |||
// Every time the rack waits and locks a mutex, it steps this many frames | |||
const int stepSize = 32; | |||
@@ -9,8 +9,8 @@ | |||
namespace rack { | |||
std::string gApplicationName = "Virtuoso Rack"; | |||
std::string gApplicationVersion = "v0.0.0 alpha"; | |||
std::string gApplicationName = "VCV Rack"; | |||
std::string gApplicationVersion = "v0.1.0 alpha"; | |||
Rack *gRack; | |||
@@ -7,7 +7,7 @@ namespace rack { | |||
void Knob::step() { | |||
index = eucMod((int) roundf(mapf(value, minValue, maxValue, minIndex, maxIndex)), spriteCount); | |||
index = eucmod((int) roundf(mapf(value, minValue, maxValue, minIndex, maxIndex)), spriteCount); | |||
} | |||
void Knob::onDragStart() { | |||
@@ -7,12 +7,15 @@ void ModulePanel::draw(NVGcontext *vg) { | |||
nvgBeginPath(vg); | |||
nvgRect(vg, box.pos.x, box.pos.y, box.size.x, box.size.y); | |||
NVGpaint paint; | |||
// Background gradient | |||
Vec c = box.pos; | |||
float length = box.size.norm(); | |||
paint = nvgRadialGradient(vg, c.x, c.y, 0.0, length, highlightColor, backgroundColor); | |||
nvgFillPaint(vg, paint); | |||
// nvgFillColor(vg, backgroundColor); | |||
nvgFill(vg); | |||
// Background image | |||
if (!imageFilename.empty()) { | |||
int imageId = loadImage(imageFilename); | |||
@@ -100,19 +100,23 @@ void ModuleWidget::draw(NVGcontext *vg) { | |||
bndBevel(vg, box.pos.x, box.pos.y, box.size.x, box.size.y); | |||
// CPU usage text | |||
if (true) { | |||
if (gScene->toolbar->cpuUsageButton->value != 0.0) { | |||
float cpuTime = module ? module->cpuTime : 0.0; | |||
std::string text = stringf("%.1f%%", cpuTime * 100.0); | |||
nvgSave(vg); | |||
nvgBeginPath(vg); | |||
nvgRect(vg, box.pos.x, box.pos.y, box.size.x, BND_WIDGET_HEIGHT); | |||
nvgFillColor(vg, nvgRGBf(0.0, 0.0, 0.0)); | |||
nvgFill(vg); | |||
nvgBeginPath(vg); | |||
cpuTime = clampf(cpuTime, 0.0, 1.0); | |||
const float barWidth = 15.0; | |||
nvgRect(vg, box.pos.x, box.pos.y + (1.0 - cpuTime) * box.size.y, barWidth, cpuTime * box.size.y); | |||
nvgFillColor(vg, nvgHSLA(0.33 * cubic(1.0 - cpuTime), 1.0, 0.5, 128)); | |||
nvgRect(vg, box.pos.x, box.pos.y, box.size.x * cpuTime, BND_WIDGET_HEIGHT); | |||
nvgFillColor(vg, nvgHSL(0.33 * cubic(1.0 - cpuTime), 1.0, 0.4)); | |||
nvgFill(vg); | |||
bndLabel(vg, box.pos.x, box.pos.y + box.size.y - BND_WIDGET_HEIGHT, box.size.x, BND_WIDGET_HEIGHT, -1, text.c_str()); | |||
bndMenuItem(vg, box.pos.x, box.pos.y, box.size.x, BND_WIDGET_HEIGHT, BND_DEFAULT, -1, text.c_str()); | |||
nvgRestore(vg); | |||
} | |||
} | |||
@@ -0,0 +1,28 @@ | |||
#include "rack.hpp" | |||
namespace rack { | |||
void RadioButton::draw(NVGcontext *vg) { | |||
bndRadioButton(vg, box.pos.x, box.pos.y, box.size.x, box.size.y, BND_CORNER_NONE, value == 0.0 ? state : BND_ACTIVE, -1, label.c_str()); | |||
} | |||
void RadioButton::onMouseEnter() { | |||
state = BND_HOVER; | |||
} | |||
void RadioButton::onMouseLeave() { | |||
state = BND_DEFAULT; | |||
} | |||
void RadioButton::onDragDrop(Widget *origin) { | |||
if (origin == this) { | |||
if (value == 0.0) | |||
value = 1.0; | |||
else | |||
value = 0.0; | |||
} | |||
} | |||
} // namespace rack |
@@ -98,9 +98,9 @@ Toolbar::Toolbar() { | |||
{ | |||
Label *label = new Label(); | |||
label->box.pos = Vec(xPos, margin); | |||
label->text = gApplicationName + " " + gApplicationVersion; | |||
label->text = gApplicationVersion; | |||
addChild(label); | |||
xPos += 175; | |||
xPos += 100; | |||
} | |||
xPos += margin; | |||
@@ -150,6 +150,16 @@ Toolbar::Toolbar() { | |||
addChild(wireTensionSlider); | |||
xPos += wireTensionSlider->box.size.x; | |||
} | |||
xPos += margin; | |||
{ | |||
cpuUsageButton = new RadioButton(); | |||
cpuUsageButton->box.pos = Vec(xPos, margin); | |||
cpuUsageButton->box.size.x = 100; | |||
cpuUsageButton->label = "CPU usage"; | |||
addChild(cpuUsageButton); | |||
xPos += cpuUsageButton->box.size.x; | |||
} | |||
} | |||
void Toolbar::draw(NVGcontext *vg) { | |||