@@ -1,6 +1,6 @@ | |||||
ARCH ?= linux | ARCH ?= linux | ||||
CFLAGS = -MMD -g -Wall -O2 | 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 | -I./ext -I./include | ||||
LDFLAGS = | 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.* | *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 | # Rack | ||||
@@ -3,14 +3,78 @@ | |||||
#include <assert.h> | #include <assert.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <samplerate.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. | /** 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> | template<int ZERO_CROSSINGS> | ||||
struct MinBLEP { | struct MinBLEP { | ||||
float buf[2*ZERO_CROSSINGS] = {}; | float buf[2*ZERO_CROSSINGS] = {}; | ||||
int pos = 0; | int pos = 0; | ||||
/** You must set this to the array generated by generateMinBLEP() */ | |||||
float *minblep = NULL; | |||||
const float *minblep; | |||||
int oversample; | int oversample; | ||||
/** Places a discontinuity with magnitude dx at -1 < p <= 0 relative to the current frame */ | /** 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; | 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) { | 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); | 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 | // 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 | } // namespace rack |
@@ -7,7 +7,7 @@ | |||||
#include <set> | #include <set> | ||||
#include <thread> | #include <thread> | ||||
#include <mutex> | #include <mutex> | ||||
#include "widgets.hpp" | |||||
#include "rackwidgets.hpp" | |||||
namespace rack { | 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 <stdint.h> | ||||
#include <string> | #include <string> | ||||
#include "math.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -6,23 +6,15 @@ | |||||
#include <list> | #include <list> | ||||
#include <map> | #include <map> | ||||
#include <jansson.h> | |||||
#include "../ext/nanovg/src/nanovg.h" | #include "../ext/nanovg/src/nanovg.h" | ||||
#include "../ext/oui/blendish.h" | #include "../ext/oui/blendish.h" | ||||
#include "math.hpp" | |||||
#include "util.hpp" | #include "util.hpp" | ||||
namespace rack { | namespace rack { | ||||
struct Module; | |||||
struct Wire; | |||||
struct RackWidget; | |||||
struct ParamWidget; | |||||
struct InputPort; | |||||
struct OutputPort; | |||||
//////////////////// | //////////////////// | ||||
// base class and traits | // base class and traits | ||||
@@ -227,6 +219,18 @@ struct ChoiceButton : Button { | |||||
void draw(NVGcontext *vg); | 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 { | struct Slider : OpaqueWidget, QuantityWidget { | ||||
BNDwidgetState state = BND_DEFAULT; | BNDwidgetState state = BND_DEFAULT; | ||||
@@ -272,206 +276,5 @@ struct Tooltip : Widget { | |||||
void draw(NVGcontext *vg); | 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 | } // namespace rack |
@@ -4,6 +4,7 @@ | |||||
#include <math.h> | #include <math.h> | ||||
#include <chrono> | #include <chrono> | ||||
#include <condition_variable> | #include <condition_variable> | ||||
#include <xmmintrin.h> | |||||
#include "rack.hpp" | #include "rack.hpp" | ||||
@@ -82,6 +83,10 @@ void Rack::stop() { | |||||
} | } | ||||
void Rack::run() { | 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 | // Every time the rack waits and locks a mutex, it steps this many frames | ||||
const int stepSize = 32; | const int stepSize = 32; | ||||
@@ -9,8 +9,8 @@ | |||||
namespace rack { | 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; | Rack *gRack; | ||||
@@ -7,7 +7,7 @@ namespace rack { | |||||
void Knob::step() { | 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() { | void Knob::onDragStart() { | ||||
@@ -7,12 +7,15 @@ void ModulePanel::draw(NVGcontext *vg) { | |||||
nvgBeginPath(vg); | nvgBeginPath(vg); | ||||
nvgRect(vg, box.pos.x, box.pos.y, box.size.x, box.size.y); | nvgRect(vg, box.pos.x, box.pos.y, box.size.x, box.size.y); | ||||
NVGpaint paint; | NVGpaint paint; | ||||
// Background gradient | // Background gradient | ||||
Vec c = box.pos; | Vec c = box.pos; | ||||
float length = box.size.norm(); | float length = box.size.norm(); | ||||
paint = nvgRadialGradient(vg, c.x, c.y, 0.0, length, highlightColor, backgroundColor); | paint = nvgRadialGradient(vg, c.x, c.y, 0.0, length, highlightColor, backgroundColor); | ||||
nvgFillPaint(vg, paint); | nvgFillPaint(vg, paint); | ||||
// nvgFillColor(vg, backgroundColor); | |||||
nvgFill(vg); | nvgFill(vg); | ||||
// Background image | // Background image | ||||
if (!imageFilename.empty()) { | if (!imageFilename.empty()) { | ||||
int imageId = loadImage(imageFilename); | 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); | bndBevel(vg, box.pos.x, box.pos.y, box.size.x, box.size.y); | ||||
// CPU usage text | // CPU usage text | ||||
if (true) { | |||||
if (gScene->toolbar->cpuUsageButton->value != 0.0) { | |||||
float cpuTime = module ? module->cpuTime : 0.0; | float cpuTime = module ? module->cpuTime : 0.0; | ||||
std::string text = stringf("%.1f%%", cpuTime * 100.0); | std::string text = stringf("%.1f%%", cpuTime * 100.0); | ||||
nvgSave(vg); | 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); | nvgBeginPath(vg); | ||||
cpuTime = clampf(cpuTime, 0.0, 1.0); | 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); | 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); | 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 *label = new Label(); | ||||
label->box.pos = Vec(xPos, margin); | label->box.pos = Vec(xPos, margin); | ||||
label->text = gApplicationName + " " + gApplicationVersion; | |||||
label->text = gApplicationVersion; | |||||
addChild(label); | addChild(label); | ||||
xPos += 175; | |||||
xPos += 100; | |||||
} | } | ||||
xPos += margin; | xPos += margin; | ||||
@@ -150,6 +150,16 @@ Toolbar::Toolbar() { | |||||
addChild(wireTensionSlider); | addChild(wireTensionSlider); | ||||
xPos += wireTensionSlider->box.size.x; | 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) { | void Toolbar::draw(NVGcontext *vg) { | ||||