@@ -206,3 +206,32 @@ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR | |||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | ||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
# libsamplerate | |||||
Copyright (c) 2012-2016, Erik de Castro Lopo <erikd@mega-nerd.com> | |||||
All rights reserved. | |||||
Redistribution and use in source and binary forms, with or without | |||||
modification, are permitted provided that the following conditions are | |||||
met: | |||||
1. Redistributions of source code must retain the above copyright | |||||
notice, this list of conditions and the following disclaimer. | |||||
2. Redistributions in binary form must reproduce the above copyright | |||||
notice, this list of conditions and the following disclaimer in the | |||||
documentation and/or other materials provided with the distribution. | |||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS | |||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | |||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | |||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | |||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@@ -15,7 +15,7 @@ CXX = g++ | |||||
SOURCES += lib/noc/noc_file_dialog.c | SOURCES += lib/noc/noc_file_dialog.c | ||||
CFLAGS += -DNOC_FILE_DIALOG_GTK $(shell pkg-config --cflags gtk+-2.0) | CFLAGS += -DNOC_FILE_DIALOG_GTK $(shell pkg-config --cflags gtk+-2.0) | ||||
CXXFLAGS += -DLINUX | CXXFLAGS += -DLINUX | ||||
LDFLAGS += -rdynamic -lpthread -lGL -lGLEW -lglfw -ldl -ljansson -lportaudio -lportmidi \ | |||||
LDFLAGS += -rdynamic -lpthread -lGL -lGLEW -lglfw -ldl -ljansson -lportaudio -lportmidi -lsamplerate \ | |||||
$(shell pkg-config --libs gtk+-2.0) | $(shell pkg-config --libs gtk+-2.0) | ||||
TARGET = Rack | TARGET = Rack | ||||
endif | endif | ||||
@@ -27,7 +27,7 @@ CXX = clang++ | |||||
SOURCES += lib/noc/noc_file_dialog.m | SOURCES += lib/noc/noc_file_dialog.m | ||||
CFLAGS += -DNOC_FILE_DIALOG_OSX | CFLAGS += -DNOC_FILE_DIALOG_OSX | ||||
CXXFLAGS += -DAPPLE -stdlib=libc++ -I$(HOME)/local/include | CXXFLAGS += -DAPPLE -stdlib=libc++ -I$(HOME)/local/include | ||||
LDFLAGS += -stdlib=libc++ -L$(HOME)/local/lib -lpthread -lglew -lglfw3 -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo -ldl -ljansson -lportaudio -lportmidi | |||||
LDFLAGS += -stdlib=libc++ -L$(HOME)/local/lib -lpthread -lglew -lglfw3 -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo -ldl -ljansson -lportaudio -lportmidi -lsamplerate | |||||
TARGET = Rack | TARGET = Rack | ||||
Rack.app: $(TARGET) | Rack.app: $(TARGET) | ||||
@@ -40,12 +40,14 @@ CC = x86_64-w64-mingw32-gcc | |||||
CXX = x86_64-w64-mingw32-g++ | CXX = x86_64-w64-mingw32-g++ | ||||
SOURCES += lib/noc/noc_file_dialog.c | SOURCES += lib/noc/noc_file_dialog.c | ||||
CFLAGS += -DNOC_FILE_DIALOG_WIN32 | CFLAGS += -DNOC_FILE_DIALOG_WIN32 | ||||
CXXFLAGS += -DWINDOWS -D_USE_MATH_DEFINES \ | |||||
CXXFLAGS += -DWINDOWS -D_USE_MATH_DEFINES -DGLEW_STATIC \ | |||||
-I$(HOME)/pkg/portaudio-r1891-build/include | -I$(HOME)/pkg/portaudio-r1891-build/include | ||||
LDFLAGS += -lpthread \ | |||||
-lglfw3 -lgdi32 -lopengl32 -lglew32 \ | |||||
-lcomdlg32 -lole32 \ | |||||
-ljansson -lportmidi \ | |||||
LDFLAGS += \ | |||||
-Wl,-Bstatic,--whole-archive \ | |||||
-lglfw3 -lgdi32 -lglew32 -ljansson -lsamplerate \ | |||||
-Wl,-Bdynamic,--no-whole-archive \ | |||||
-lpthread -lopengl32 -lcomdlg32 -lole32 \ | |||||
-lportmidi \ | |||||
-L$(HOME)/pkg/portaudio-r1891-build/lib/x64/ReleaseMinDependency -lportaudio_x64 \ | -L$(HOME)/pkg/portaudio-r1891-build/lib/x64/ReleaseMinDependency -lportaudio_x64 \ | ||||
-Wl,--export-all-symbols,--out-implib,libRack.a -mwindows | -Wl,--export-all-symbols,--out-implib,libRack.a -mwindows | ||||
TARGET = Rack.exe | TARGET = Rack.exe | ||||
@@ -13,6 +13,7 @@ Install dependencies | |||||
- [jansson](http://www.digip.org/jansson/) | - [jansson](http://www.digip.org/jansson/) | ||||
- [portaudio](http://www.portaudio.com/) | - [portaudio](http://www.portaudio.com/) | ||||
- [portmidi](http://portmedia.sourceforge.net/portmidi/) | - [portmidi](http://portmedia.sourceforge.net/portmidi/) | ||||
- [libsamplerate](http://www.mega-nerd.com/SRC/) | |||||
- GTK+-2.0 if Linux (for file open/save dialog) | - GTK+-2.0 if Linux (for file open/save dialog) | ||||
Run `make ARCH=linux` or `make ARCH=windows` or `make ARCH=apple` | Run `make ARCH=linux` or `make ARCH=windows` or `make ARCH=apple` | ||||
@@ -57,6 +57,7 @@ extern Vec gMousePos; | |||||
extern Widget *gHoveredWidget; | extern Widget *gHoveredWidget; | ||||
extern Widget *gDraggedWidget; | extern Widget *gDraggedWidget; | ||||
extern Widget *gSelectedWidget; | extern Widget *gSelectedWidget; | ||||
extern int gGuiFrame; | |||||
void guiInit(); | void guiInit(); | ||||
void guiDestroy(); | void guiDestroy(); | ||||
@@ -82,10 +83,13 @@ struct Wire; | |||||
struct Module { | struct Module { | ||||
std::vector<float> params; | std::vector<float> params; | ||||
// Pointers to voltage values at each port | |||||
// If value is NULL, the input/output is disconnected | |||||
/** Pointers to voltage values at each port | |||||
If value is NULL, the input/output is disconnected | |||||
*/ | |||||
std::vector<float*> inputs; | std::vector<float*> inputs; | ||||
std::vector<float*> outputs; | std::vector<float*> outputs; | ||||
/** For CPU usage */ | |||||
float cpuTime = 0.0; | |||||
virtual ~Module() {} | virtual ~Module() {} | ||||
@@ -0,0 +1,172 @@ | |||||
#pragma once | |||||
#include <assert.h> | |||||
#include <string.h> | |||||
#include <samplerate.h> | |||||
namespace rack { | |||||
/** S must be a power of 2 */ | |||||
template <typename T, size_t S> | |||||
struct RingBuffer { | |||||
T data[S] = {}; | |||||
size_t start = 0; | |||||
size_t end = 0; | |||||
size_t mask(size_t i) { | |||||
return i & (S - 1); | |||||
} | |||||
void push(T t) { | |||||
size_t i = mask(end++); | |||||
data[i] = t; | |||||
} | |||||
T shift() { | |||||
return data[mask(start++)]; | |||||
} | |||||
bool empty() { | |||||
return start >= end; | |||||
} | |||||
bool full() { | |||||
return end - start >= S; | |||||
} | |||||
size_t size() { | |||||
return end - start; | |||||
} | |||||
}; | |||||
/** S must be a power of 2 */ | |||||
template <typename T, size_t S> | |||||
struct DoubleRingBuffer { | |||||
T data[S*2] = {}; | |||||
size_t start = 0; | |||||
size_t end = 0; | |||||
size_t mask(size_t i) { | |||||
return i & (S - 1); | |||||
} | |||||
void push(T t) { | |||||
size_t i = mask(end++); | |||||
data[i] = t; | |||||
data[i + S] = t; | |||||
} | |||||
T shift() { | |||||
return data[mask(start++)]; | |||||
} | |||||
bool empty() { | |||||
return start >= end; | |||||
} | |||||
bool full() { | |||||
return end - start >= S; | |||||
} | |||||
size_t size() { | |||||
return end - start; | |||||
} | |||||
}; | |||||
template <typename T, size_t S, size_t N> | |||||
struct AppleRingBuffer { | |||||
T data[N] = {}; | |||||
size_t start = 0; | |||||
size_t end = 0; | |||||
void push(T t) { | |||||
data[end++] = t; | |||||
if (end >= N) { | |||||
// move end block to beginning | |||||
memmove(data, &data[N - S], sizeof(T) * S); | |||||
start -= N - S; | |||||
end = S; | |||||
} | |||||
} | |||||
T shift() { | |||||
return data[start++]; | |||||
} | |||||
bool empty() { | |||||
return start >= end; | |||||
} | |||||
bool full() { | |||||
return end - start >= S; | |||||
} | |||||
size_t size() { | |||||
return end - start; | |||||
} | |||||
}; | |||||
template <size_t S> | |||||
struct SampleRateConverter { | |||||
SRC_STATE *state; | |||||
SRC_DATA data; | |||||
SampleRateConverter() { | |||||
int error; | |||||
state = src_new(SRC_SINC_FASTEST, 1, &error); | |||||
assert(!error); | |||||
data.src_ratio = 1.0; | |||||
data.end_of_input = false; | |||||
} | |||||
~SampleRateConverter() { | |||||
src_delete(state); | |||||
} | |||||
void setRatio(float r) { | |||||
data.src_ratio = r; | |||||
} | |||||
void push(const float *in, int length) { | |||||
float out[S]; | |||||
data.data_in = in; | |||||
data.input_frames = length; | |||||
data.data_out = out; | |||||
data.output_frames = S; | |||||
src_process(state, &data); | |||||
} | |||||
void push(float in) { | |||||
push(&in, 1); | |||||
} | |||||
}; | |||||
template <size_t OVERSAMPLE> | |||||
struct Decimator { | |||||
SRC_STATE *state; | |||||
SRC_DATA data; | |||||
Decimator() { | |||||
int error; | |||||
state = src_new(SRC_SINC_FASTEST, 1, &error); | |||||
assert(!error); | |||||
data.data_in = NULL; | |||||
data.data_out = NULL; | |||||
data.input_frames = OVERSAMPLE; | |||||
data.output_frames = 1; | |||||
data.end_of_input = false; | |||||
data.src_ratio = 1.0 / OVERSAMPLE; | |||||
} | |||||
~Decimator() { | |||||
src_delete(state); | |||||
} | |||||
/** input must be length OVERSAMPLE */ | |||||
float process(float *input) { | |||||
float output[1]; | |||||
data.data_in = input; | |||||
data.data_out = output; | |||||
src_process(state, &data); | |||||
if (data.output_frames_gen > 0) { | |||||
return output[0]; | |||||
} | |||||
else { | |||||
return 0.0; | |||||
} | |||||
} | |||||
void reset() { | |||||
src_reset(state); | |||||
} | |||||
}; | |||||
} // namespace rack |
@@ -12,7 +12,7 @@ namespace rack { | |||||
//////////////////// | //////////////////// | ||||
/** Limits a value between a minimum and maximum | /** Limits a value between a minimum and maximum | ||||
If min > max for some reason, returns min; | |||||
If min > max for some reason, returns min | |||||
*/ | */ | ||||
inline float clampf(float x, float min, float max) { | inline float clampf(float x, float min, float max) { | ||||
if (x > max) | if (x > max) | ||||
@@ -22,6 +22,13 @@ inline float clampf(float x, float min, float max) { | |||||
return x; | return x; | ||||
} | } | ||||
/** If the magnitude of x if less than eps, return 0 */ | |||||
inline float chopf(float x, float eps) { | |||||
if (x < eps && x > -eps) | |||||
return 0.0; | |||||
return x; | |||||
} | |||||
inline float mapf(float x, float xMin, float xMax, float yMin, float yMax) { | inline float mapf(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); | ||||
} | } | ||||
@@ -43,12 +50,22 @@ inline float quadraticBipolar(float x) { | |||||
return x >= 0.0 ? x2 : -x2; | return x >= 0.0 ? x2 : -x2; | ||||
} | } | ||||
inline float cubic(float x) { | |||||
// optimal with --fast-math | |||||
return x*x*x; | |||||
} | |||||
inline float quarticBipolar(float x) { | inline float quarticBipolar(float x) { | ||||
float x2 = x*x; | float x2 = x*x; | ||||
float x4 = x2*x2; | float x4 = x2*x2; | ||||
return x >= 0.0 ? x4 : -x4; | return x >= 0.0 ? x4 : -x4; | ||||
} | } | ||||
inline float quintic(float x) { | |||||
// optimal with --fast-math | |||||
return x*x*x*x*x; | |||||
} | |||||
// 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 eucMod(int a, int base) { | ||||
@@ -68,17 +85,21 @@ 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 ceil(x)+1. | ||||
*/ | */ | ||||
inline float interpf(float *p, float x) { | |||||
int i = x; | |||||
x -= i; | |||||
return crossf(p[i], p[i+1], x); | |||||
inline float interpf(const float *p, float x) { | |||||
int xi = x; | |||||
float xf = x - xi; | |||||
return crossf(p[xi], p[xi+1], xf); | |||||
} | } | ||||
//////////////////// | //////////////////// | ||||
// RNG | // RNG | ||||
//////////////////// | //////////////////// | ||||
extern std::mt19937 rng; | |||||
uint32_t randomu32(); | |||||
/** Returns a uniform random float in the interval [0.0, 1.0) */ | |||||
float randomf(); | |||||
/** Returns a normal random number with mean 0 and std dev 1 */ | |||||
float randomNormal(); | |||||
//////////////////// | //////////////////// | ||||
// 2D float vector | // 2D float vector | ||||
@@ -79,12 +79,12 @@ struct Widget { | |||||
/** Called when a widget responds to `onMouseDown` for a left button press */ | /** Called when a widget responds to `onMouseDown` for a left button press */ | ||||
virtual void onDragStart() {} | virtual void onDragStart() {} | ||||
/** Called when the left button is released and this widget is being dragged */ | |||||
virtual void onDragEnd() {} | |||||
/** Called when a widget responds to `onMouseMove` and is being dragged */ | /** Called when a widget responds to `onMouseMove` and is being dragged */ | ||||
virtual void onDragMove(Vec mouseRel) {} | virtual void onDragMove(Vec mouseRel) {} | ||||
/** Called when a widget responds to `onMouseUp` for a left button release and a widget is being dragged */ | /** Called when a widget responds to `onMouseUp` for a left button release and a widget is being dragged */ | ||||
virtual void onDragDrop(Widget *origin) {} | virtual void onDragDrop(Widget *origin) {} | ||||
/** Called when the left button is released and this widget is being dragged */ | |||||
virtual void onDragEnd() {} | |||||
virtual void onAction() {} | virtual void onAction() {} | ||||
virtual void onChange() {} | virtual void onChange() {} | ||||
@@ -217,7 +217,9 @@ struct Button : OpaqueWidget { | |||||
} | } | ||||
void draw(NVGcontext *vg); | void draw(NVGcontext *vg); | ||||
void onMouseEnter(); | void onMouseEnter(); | ||||
void onMouseLeave() ; | |||||
void onMouseLeave(); | |||||
void onDragStart(); | |||||
void onDragEnd(); | |||||
void onDragDrop(Widget *origin); | void onDragDrop(Widget *origin); | ||||
}; | }; | ||||
@@ -339,7 +341,6 @@ struct RackWidget : OpaqueWidget { | |||||
json_t *toJson(); | json_t *toJson(); | ||||
void fromJson(json_t *root); | void fromJson(json_t *root); | ||||
int frame = 0; | |||||
void repositionModule(ModuleWidget *module); | void repositionModule(ModuleWidget *module); | ||||
void step(); | void step(); | ||||
void draw(NVGcontext *vg); | void draw(NVGcontext *vg); | ||||
@@ -27,6 +27,8 @@ Widget *gHoveredWidget = NULL; | |||||
Widget *gDraggedWidget = NULL; | Widget *gDraggedWidget = NULL; | ||||
Widget *gSelectedWidget = NULL; | Widget *gSelectedWidget = NULL; | ||||
int gGuiFrame; | |||||
static GLFWwindow *window = NULL; | static GLFWwindow *window = NULL; | ||||
static NVGcontext *vg = NULL; | static NVGcontext *vg = NULL; | ||||
@@ -225,8 +227,10 @@ void guiRun() { | |||||
glfwGetWindowSize(window, &width, &height); | glfwGetWindowSize(window, &width, &height); | ||||
windowSizeCallback(window, width, height); | windowSizeCallback(window, width, height); | ||||
} | } | ||||
gGuiFrame = 0; | |||||
double lastTime = 0.0; | double lastTime = 0.0; | ||||
while(!glfwWindowShouldClose(window)) { | while(!glfwWindowShouldClose(window)) { | ||||
gGuiFrame++; | |||||
glfwPollEvents(); | glfwPollEvents(); | ||||
{ | { | ||||
double xpos, ypos; | double xpos, ypos; | ||||
@@ -94,6 +94,7 @@ void pluginInit() { | |||||
void pluginDestroy() { | void pluginDestroy() { | ||||
for (Plugin *plugin : gPlugins) { | for (Plugin *plugin : gPlugins) { | ||||
// TODO unload plugin with `dlclose` or `FreeLibrary` | |||||
delete plugin; | delete plugin; | ||||
} | } | ||||
gPlugins.clear(); | gPlugins.clear(); | ||||
@@ -73,8 +73,18 @@ void rackStep() { | |||||
} | } | ||||
} | } | ||||
// Step all modules | // Step all modules | ||||
std::chrono::time_point<std::chrono::high_resolution_clock> start, end; | |||||
for (Module *module : modules) { | for (Module *module : modules) { | ||||
// Start clock for CPU usage | |||||
start = std::chrono::high_resolution_clock::now(); | |||||
// Step module by one frame | |||||
module->step(); | module->step(); | ||||
// Stop clock and smooth step time value | |||||
end = std::chrono::high_resolution_clock::now(); | |||||
std::chrono::duration<float> diff = end - start; | |||||
float elapsed = diff.count() * SAMPLE_RATE; | |||||
const float lambda = 1.0; | |||||
module->cpuTime += (elapsed - module->cpuTime) * lambda / SAMPLE_RATE; | |||||
} | } | ||||
} | } | ||||
@@ -3,7 +3,25 @@ | |||||
namespace rack { | namespace rack { | ||||
// TODO | |||||
// Convert this to xoroshiro128+ and custom normal dist implementation | |||||
static std::random_device rd; | static std::random_device rd; | ||||
std::mt19937 rng(rd()); | |||||
static std::mt19937 rng(rd()); | |||||
static std::uniform_real_distribution<float> uniformDist; | |||||
static std::normal_distribution<float> normalDist; | |||||
uint32_t randomu32() { | |||||
return rng(); | |||||
} | |||||
float randomf() { | |||||
return uniformDist(rng); | |||||
} | |||||
float randomNormal(){ | |||||
return normalDist(rng); | |||||
} | |||||
} // namespace rack | } // namespace rack |
@@ -15,6 +15,14 @@ void Button::onMouseLeave() { | |||||
state = BND_DEFAULT; | state = BND_DEFAULT; | ||||
} | } | ||||
void Button::onDragStart() { | |||||
state = BND_ACTIVE; | |||||
} | |||||
void Button::onDragEnd() { | |||||
state = BND_HOVER; | |||||
} | |||||
void Button::onDragDrop(Widget *origin) { | void Button::onDragDrop(Widget *origin) { | ||||
if (origin == this) { | if (origin == this) { | ||||
onAction(); | onAction(); | ||||
@@ -8,7 +8,7 @@ void ModulePanel::draw(NVGcontext *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.getTopRight(); | |||||
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); | ||||
@@ -98,6 +98,15 @@ void ModuleWidget::cloneParams(ModuleWidget *source) { | |||||
void ModuleWidget::draw(NVGcontext *vg) { | void ModuleWidget::draw(NVGcontext *vg) { | ||||
Widget::draw(vg); | Widget::draw(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 | |||||
if (module) { | |||||
char text[32]; | |||||
snprintf(text, sizeof(text), "%.2f%%", module->cpuTime * 10); | |||||
nvgSave(vg); | |||||
bndSlider(vg, box.pos.x, box.pos.y, box.size.x, BND_WIDGET_HEIGHT, BND_CORNER_ALL, BND_DEFAULT, module->cpuTime, text, NULL); | |||||
nvgRestore(vg); | |||||
} | |||||
} | } | ||||
void ModuleWidget::onDragStart() { | void ModuleWidget::onDragStart() { | ||||
@@ -8,9 +8,8 @@ Port::Port() { | |||||
spriteOffset = Vec(-18, -18); | spriteOffset = Vec(-18, -18); | ||||
spriteSize = Vec(56, 56); | spriteSize = Vec(56, 56); | ||||
spriteFilename = "res/port.png"; | spriteFilename = "res/port.png"; | ||||
std::uniform_int_distribution<> dist(0, 4); | |||||
index = dist(rng); | |||||
index = randomu32() % 5; | |||||
} | } | ||||
Port::~Port() { | Port::~Port() { | ||||
@@ -248,8 +248,7 @@ void RackWidget::step() { | |||||
// Autosave every 15 seconds | // Autosave every 15 seconds | ||||
// (This is alpha software, expect crashes!) | // (This is alpha software, expect crashes!) | ||||
if (++frame >= 60*15) { | |||||
frame = 0; | |||||
if (gGuiFrame % (60*15) == 0) { | |||||
savePatch("autosave.json"); | savePatch("autosave.json"); | ||||
} | } | ||||
@@ -9,8 +9,7 @@ Screw::Screw() { | |||||
spriteSize = Vec(29, 29); | spriteSize = Vec(29, 29); | ||||
spriteFilename = "res/screw.png"; | spriteFilename = "res/screw.png"; | ||||
std::uniform_int_distribution<> dist(0, 4); | |||||
index = dist(rng); | |||||
index = randomu32() % 5; | |||||
} | } | ||||