@@ -0,0 +1,51 @@ | |||
#pragma once | |||
#include <vector> | |||
namespace rack { | |||
struct Module { | |||
std::vector<float> params; | |||
/** Pointers to voltage values at each port | |||
If value is NULL, the input/output is disconnected | |||
*/ | |||
std::vector<float*> inputs; | |||
std::vector<float*> outputs; | |||
/** For CPU usage */ | |||
float cpuTime = 0.0; | |||
virtual ~Module() {} | |||
/** Advances the module by 1 audio frame with duration 1.0 / gSampleRate */ | |||
virtual void step() {} | |||
}; | |||
struct Wire { | |||
Module *outputModule = NULL; | |||
int outputId; | |||
Module *inputModule = NULL; | |||
int inputId; | |||
/** The voltage connected to input ports */ | |||
float inputValue = 0.0; | |||
/** The voltage connected to output ports */ | |||
float outputValue = 0.0; | |||
}; | |||
void engineInit(); | |||
void engineDestroy(); | |||
/** Launches engine thread */ | |||
void engineStart(); | |||
void engineStop(); | |||
/** Does not transfer pointer ownership */ | |||
void engineAddModule(Module *module); | |||
void engineRemoveModule(Module *module); | |||
/** Does not transfer pointer ownership */ | |||
void engineAddWire(Wire *wire); | |||
void engineRemoveWire(Wire *wire); | |||
void engineSetParamSmooth(Module *module, int paramId, float value); | |||
extern float gSampleRate; | |||
} // namespace rack |
@@ -0,0 +1,17 @@ | |||
#pragma once | |||
#include "scene.hpp" | |||
namespace rack { | |||
void guiInit(); | |||
void guiDestroy(); | |||
void guiRun(); | |||
void guiCursorLock(); | |||
void guiCursorUnlock(); | |||
const char *guiSaveDialog(const char *filters, const char *filename); | |||
const char *guiOpenDialog(const char *filters, const char *filename); | |||
} // namespace rack |
@@ -0,0 +1,51 @@ | |||
#pragma once | |||
#include <string> | |||
#include <list> | |||
namespace rack { | |||
struct ModuleWidget; | |||
struct Model; | |||
// Subclass this and return a pointer to a new one when init() is called | |||
struct Plugin { | |||
virtual ~Plugin(); | |||
// A unique identifier for your plugin, e.g. "foo" | |||
std::string slug; | |||
// Human readable name for your plugin, e.g. "Foo Modular" | |||
std::string name; | |||
/** A list of the models made available by this plugin */ | |||
std::list<Model*> models; | |||
}; | |||
struct Model { | |||
virtual ~Model() {} | |||
Plugin *plugin; | |||
// A unique identifier for the model in this plugin, e.g. "vco" | |||
std::string slug; | |||
// Human readable name for your model, e.g. "VCO" | |||
std::string name; | |||
virtual ModuleWidget *createModuleWidget() { return NULL; } | |||
}; | |||
extern std::list<Plugin*> gPlugins; | |||
void pluginInit(); | |||
void pluginDestroy(); | |||
} // namespace rack | |||
//////////////////// | |||
// Implemented by plugin | |||
//////////////////// | |||
/** Called once to initialize and return Plugin. | |||
Plugin is destructed when Rack closes | |||
*/ | |||
extern "C" | |||
rack::Plugin *init(); |
@@ -1,130 +1,14 @@ | |||
#pragma once | |||
#include <string> | |||
#include <list> | |||
#include <vector> | |||
#include <memory> | |||
#include <set> | |||
#include <thread> | |||
#include <mutex> | |||
#include "rackwidgets.hpp" | |||
#include "plugin.hpp" | |||
#include "engine.hpp" | |||
#include "gui.hpp" | |||
namespace rack { | |||
//////////////////// | |||
// Plugin manager | |||
//////////////////// | |||
struct Model; | |||
// Subclass this and return a pointer to a new one when init() is called | |||
struct Plugin { | |||
virtual ~Plugin(); | |||
// A unique identifier for your plugin, e.g. "foo" | |||
std::string slug; | |||
// Human readable name for your plugin, e.g. "Foo Modular" | |||
std::string name; | |||
// A list of the models made available by this plugin | |||
std::list<Model*> models; | |||
}; | |||
struct Model { | |||
virtual ~Model() {} | |||
Plugin *plugin; | |||
// A unique identifier for the model in this plugin, e.g. "vco" | |||
std::string slug; | |||
// Human readable name for your model, e.g. "VCO" | |||
std::string name; | |||
virtual ModuleWidget *createModuleWidget() { return NULL; } | |||
}; | |||
extern std::list<Plugin*> gPlugins; | |||
void pluginInit(); | |||
void pluginDestroy(); | |||
//////////////////// | |||
// gui.cpp | |||
//////////////////// | |||
extern Vec gMousePos; | |||
extern Widget *gHoveredWidget; | |||
extern Widget *gDraggedWidget; | |||
extern Widget *gSelectedWidget; | |||
extern int gGuiFrame; | |||
void guiInit(); | |||
void guiDestroy(); | |||
void guiRun(); | |||
void guiCursorLock(); | |||
void guiCursorUnlock(); | |||
const char *guiSaveDialog(const char *filters, const char *filename); | |||
const char *guiOpenDialog(const char *filters, const char *filename); | |||
int loadFont(std::string filename); | |||
int loadImage(std::string filename); | |||
void drawImage(NVGcontext *vg, Vec pos, int imageId); | |||
//////////////////// | |||
// rack.cpp | |||
//////////////////// | |||
struct Wire; | |||
struct Module { | |||
std::vector<float> params; | |||
/** Pointers to voltage values at each port | |||
If value is NULL, the input/output is disconnected | |||
*/ | |||
std::vector<float*> inputs; | |||
std::vector<float*> outputs; | |||
/** For CPU usage */ | |||
float cpuTime = 0.0; | |||
virtual ~Module() {} | |||
// Always called on each sample frame before calling getOutput() | |||
virtual void step() {} | |||
}; | |||
struct Wire { | |||
Module *outputModule = NULL; | |||
int outputId; | |||
Module *inputModule = NULL; | |||
int inputId; | |||
/** The voltage connected to input ports */ | |||
float inputValue = 0.0; | |||
/** The voltage connected to output ports */ | |||
float outputValue = 0.0; | |||
}; | |||
struct Rack { | |||
Rack(); | |||
~Rack(); | |||
/** Launches rack thread */ | |||
void start(); | |||
void stop(); | |||
void run(); | |||
void step(); | |||
/** Does not transfer pointer ownership */ | |||
void addModule(Module *module); | |||
void removeModule(Module *module); | |||
/** Does not transfer pointer ownership */ | |||
void addWire(Wire *wire); | |||
void removeWire(Wire *wire); | |||
void setParamSmooth(Module *module, int paramId, float value); | |||
float sampleRate; | |||
struct Impl; | |||
Impl *impl; | |||
}; | |||
//////////////////// | |||
// Optional helpers for plugins | |||
// helpers | |||
//////////////////// | |||
inline | |||
@@ -191,26 +75,5 @@ Screw *createScrew(Vec pos) { | |||
return screw; | |||
} | |||
//////////////////// | |||
// Globals | |||
//////////////////// | |||
extern std::string gApplicationName; | |||
extern std::string gApplicationVersion; | |||
extern Scene *gScene; | |||
extern RackWidget *gRackWidget; | |||
extern Rack *gRack; | |||
} // namespace rack | |||
//////////////////// | |||
// Implemented by plugin | |||
//////////////////// | |||
// Called once to initialize and return Plugin. | |||
// Plugin is destructed when Rack closes | |||
extern "C" | |||
rack::Plugin *init(); |
@@ -1,7 +1,7 @@ | |||
#pragma once | |||
#include "widgets.hpp" | |||
#include <vector> | |||
#include <jansson.h> | |||
#include "widgets.hpp" | |||
namespace rack { | |||
@@ -14,7 +14,7 @@ struct RackWidget; | |||
struct ParamWidget; | |||
struct InputPort; | |||
struct OutputPort; | |||
struct Scene; | |||
//////////////////// | |||
// module | |||
@@ -76,6 +76,7 @@ struct RackWidget : OpaqueWidget { | |||
// Only put WireWidgets in here | |||
Widget *wireContainer; | |||
WireWidget *activeWire = NULL; | |||
std::shared_ptr<Image> railsImage; | |||
RackWidget(); | |||
~RackWidget(); | |||
@@ -94,7 +95,7 @@ struct RackWidget : OpaqueWidget { | |||
struct ModulePanel : TransparentWidget { | |||
NVGcolor backgroundColor; | |||
std::string imageFilename; | |||
std::shared_ptr<Image> backgroundImage; | |||
void draw(NVGcontext *vg); | |||
}; | |||
@@ -205,18 +206,17 @@ struct Toolbar : OpaqueWidget { | |||
void draw(NVGcontext *vg); | |||
}; | |||
struct Scene : OpaqueWidget { | |||
struct RackScene : Scene { | |||
Toolbar *toolbar; | |||
ScrollWidget *scrollWidget; | |||
Widget *overlay = NULL; | |||
Scene(); | |||
void setOverlay(Widget *w); | |||
RackScene(); | |||
void step(); | |||
}; | |||
//////////////////// | |||
// component library | |||
// Component Library | |||
//////////////////// | |||
struct PJ301M : SpriteWidget { | |||
@@ -224,7 +224,7 @@ struct PJ301M : SpriteWidget { | |||
box.size = Vec(24, 24); | |||
spriteOffset = Vec(-10, -10); | |||
spriteSize = Vec(48, 48); | |||
spriteFilename = "res/ComponentLibrary/PJ301M.png"; | |||
spriteImage = Image::load("res/ComponentLibrary/PJ301M.png"); | |||
} | |||
}; | |||
struct InputPortPJ301M : InputPort, PJ301M {}; | |||
@@ -235,7 +235,7 @@ struct PJ3410 : SpriteWidget { | |||
box.size = Vec(31, 31); | |||
spriteOffset = Vec(-9, -9); | |||
spriteSize = Vec(54, 54); | |||
spriteFilename = "res/ComponentLibrary/PJ3410.png"; | |||
spriteImage = Image::load("res/ComponentLibrary/PJ3410.png"); | |||
} | |||
}; | |||
struct InputPortPJ3410 : InputPort, PJ3410 {}; | |||
@@ -246,10 +246,24 @@ struct CL1362 : SpriteWidget { | |||
box.size = Vec(33, 29); | |||
spriteOffset = Vec(-10, -10); | |||
spriteSize = Vec(57, 54); | |||
spriteFilename = "res/ComponentLibrary/CL1362.png"; | |||
spriteImage = Image::load("res/ComponentLibrary/CL1362.png"); | |||
} | |||
}; | |||
struct InputPortCL1362 : InputPort, CL1362 {}; | |||
struct OutputPortCL1362 : OutputPort, CL1362 {}; | |||
//////////////////// | |||
// globals | |||
//////////////////// | |||
extern std::string gApplicationName; | |||
extern std::string gApplicationVersion; | |||
extern Scene *gScene; | |||
extern RackWidget *gRackWidget; | |||
void sceneInit(); | |||
void sceneDestroy(); | |||
} // namespace rack |
@@ -1,6 +1,12 @@ | |||
#pragma once | |||
// Include most of the C standard library for convenience | |||
// (C++ programmers will hate me) | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#include <stdint.h> | |||
#include <assert.h> | |||
#include <string> | |||
@@ -1,13 +1,10 @@ | |||
#pragma once | |||
#include <assert.h> | |||
#include <stdio.h> | |||
#include <math.h> | |||
#include <list> | |||
#include <map> | |||
#include <memory> | |||
#include "../ext/nanovg/src/nanovg.h" | |||
#include "../ext/oui/blendish.h" | |||
#include "../ext/nanosvg/src/nanosvg.h" | |||
#include "math.hpp" | |||
#include "util.hpp" | |||
@@ -16,6 +13,35 @@ | |||
namespace rack { | |||
//////////////////// | |||
// resources | |||
//////////////////// | |||
// Constructing these directly will load from the disk each time. Use the load() functions to load from disk and cache them as long as the shared_ptr is held. | |||
// Implemented in gui.cpp | |||
struct Font { | |||
int handle; | |||
Font(const std::string &filename); | |||
~Font(); | |||
static std::shared_ptr<Font> load(const std::string &filename); | |||
}; | |||
struct Image { | |||
int handle; | |||
Image(const std::string &filename); | |||
~Image(); | |||
static std::shared_ptr<Image> load(const std::string &filename); | |||
}; | |||
struct SVG { | |||
NSVGimage *image; | |||
SVG(const std::string &filename); | |||
~SVG(); | |||
static std::shared_ptr<SVG> load(const std::string &filename); | |||
}; | |||
//////////////////// | |||
// base class and traits | |||
//////////////////// | |||
@@ -122,7 +148,7 @@ struct OpaqueWidget : virtual Widget { | |||
struct SpriteWidget : virtual Widget { | |||
Vec spriteOffset; | |||
Vec spriteSize; | |||
std::string spriteFilename; | |||
std::shared_ptr<Image> spriteImage; | |||
int index = 0; | |||
void draw(NVGcontext *vg); | |||
}; | |||
@@ -276,5 +302,24 @@ struct Tooltip : Widget { | |||
void draw(NVGcontext *vg); | |||
}; | |||
struct Scene : OpaqueWidget { | |||
Widget *overlay = NULL; | |||
void setOverlay(Widget *w); | |||
void step(); | |||
}; | |||
//////////////////// | |||
// globals | |||
//////////////////// | |||
extern Vec gMousePos; | |||
extern Widget *gHoveredWidget; | |||
extern Widget *gDraggedWidget; | |||
extern Widget *gSelectedWidget; | |||
extern int gGuiFrame; | |||
extern Scene *gScene; | |||
} // namespace rack |
@@ -99,7 +99,7 @@ void AudioInterface::step() { | |||
// Once full, sample rate convert the input | |||
if (inputBuffer.full()) { | |||
inputSrc.setRatio(sampleRate / gRack->sampleRate); | |||
inputSrc.setRatio(sampleRate / gSampleRate); | |||
int inLen = inputBuffer.size(); | |||
int outLen = inputSrcBuffer.capacity(); | |||
inputSrc.process((const float*) inputBuffer.startData(), &inLen, (float*) inputSrcBuffer.endData(), &outLen); | |||
@@ -127,7 +127,7 @@ void AudioInterface::step() { | |||
} | |||
// Pass output through sample rate converter | |||
outputSrc.setRatio(gRack->sampleRate / sampleRate); | |||
outputSrc.setRatio(gSampleRate / sampleRate); | |||
int inLen = blockSize; | |||
int outLen = outputBuffer.capacity(); | |||
outputSrc.process((float*) buf, &inLen, (float*) outputBuffer.endData(), &outLen); | |||
@@ -3,12 +3,20 @@ | |||
using namespace rack; | |||
Plugin *coreInit() { | |||
struct CorePlugin : Plugin { | |||
CorePlugin() { | |||
slug = "Core"; | |||
name = "Core"; | |||
createModel<AudioInterfaceWidget>(this, "AudioInterface", "Audio Interface"); | |||
createModel<MidiInterfaceWidget>(this, "MidiInterface", "MIDI Interface"); | |||
} | |||
}; | |||
Plugin *init() { | |||
audioInit(); | |||
midiInit(); | |||
Plugin *plugin = createPlugin("Core", "Core"); | |||
createModel<AudioInterfaceWidget>(plugin, "AudioInterface", "Audio Interface"); | |||
createModel<MidiInterfaceWidget>(plugin, "MidiInterface", "MIDI Interface"); | |||
return plugin; | |||
return new CorePlugin(); | |||
} |
@@ -3,8 +3,6 @@ | |||
using namespace rack; | |||
Plugin *coreInit(); | |||
void audioInit(); | |||
void midiInit(); | |||
@@ -2,15 +2,20 @@ | |||
#include <stdlib.h> | |||
#include <assert.h> | |||
#include <math.h> | |||
#include <set> | |||
#include <chrono> | |||
#include <thread> | |||
#include <mutex> | |||
#include <condition_variable> | |||
#include <xmmintrin.h> | |||
#include "rack.hpp" | |||
#include "engine.hpp" | |||
namespace rack { | |||
float gSampleRate; | |||
/** Threads which obtain a VIPLock will cause wait() to block for other less important threads. | |||
This does not provide the VIPs with an exclusive lock. That should be left up to another mutex shared between the less important thread. | |||
*/ | |||
@@ -42,90 +47,51 @@ struct VIPLock { | |||
}; | |||
struct Rack::Impl { | |||
bool running = false; | |||
static bool running = false; | |||
std::mutex mutex; | |||
std::thread audioThread; | |||
VIPMutex vipMutex; | |||
static std::mutex mutex; | |||
static std::thread thread; | |||
static VIPMutex vipMutex; | |||
std::set<Module*> modules; | |||
// Merely used for keeping track of which module inputs point to which module outputs, to prevent pointer mistakes and make the rack API more rigorous | |||
std::set<Wire*> wires; | |||
static std::set<Module*> modules; | |||
// Merely used for keeping track of which module inputs point to which module outputs, to prevent pointer mistakes and make the rack API more rigorous | |||
static std::set<Wire*> wires; | |||
// Parameter interpolation | |||
Module *smoothModule = NULL; | |||
int smoothParamId; | |||
float smoothValue; | |||
}; | |||
// Parameter interpolation | |||
static Module *smoothModule = NULL; | |||
static int smoothParamId; | |||
static float smoothValue; | |||
Rack::Rack() { | |||
impl = new Rack::Impl(); | |||
sampleRate = 44100.0; | |||
void engineInit() { | |||
gSampleRate = 44100.0; | |||
} | |||
Rack::~Rack() { | |||
void engineDestroy() { | |||
// Make sure there are no wires or modules in the rack on destruction. This suggests that a module failed to remove itself before the GUI was destroyed. | |||
assert(impl->wires.empty()); | |||
assert(impl->modules.empty()); | |||
delete impl; | |||
} | |||
void Rack::start() { | |||
impl->running = true; | |||
impl->audioThread = std::thread(&Rack::run, this); | |||
} | |||
void Rack::stop() { | |||
impl->running = false; | |||
impl->audioThread.join(); | |||
} | |||
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; | |||
while (impl->running) { | |||
impl->vipMutex.wait(); | |||
auto start = std::chrono::high_resolution_clock::now(); | |||
{ | |||
std::lock_guard<std::mutex> lock(impl->mutex); | |||
for (int i = 0; i < stepSize; i++) { | |||
step(); | |||
} | |||
} | |||
auto end = std::chrono::high_resolution_clock::now(); | |||
auto duration = std::chrono::nanoseconds((long) (0.9 * 1e9 * stepSize / sampleRate)) - (end - start); | |||
// Avoid pegging the CPU at 100% when there are no "blocking" modules like AudioInterface, but still step audio at a reasonable rate | |||
std::this_thread::sleep_for(duration); | |||
} | |||
assert(wires.empty()); | |||
assert(modules.empty()); | |||
} | |||
void Rack::step() { | |||
static void engineStep() { | |||
// Param interpolation | |||
if (impl->smoothModule) { | |||
float value = impl->smoothModule->params[impl->smoothParamId]; | |||
if (smoothModule) { | |||
float value = smoothModule->params[smoothParamId]; | |||
const float lambda = 60.0; // decay rate is 1 graphics frame | |||
const float snap = 0.0001; | |||
float delta = impl->smoothValue - value; | |||
float delta = smoothValue - value; | |||
if (fabsf(delta) < snap) { | |||
impl->smoothModule->params[impl->smoothParamId] = impl->smoothValue; | |||
impl->smoothModule = NULL; | |||
smoothModule->params[smoothParamId] = smoothValue; | |||
smoothModule = NULL; | |||
} | |||
else { | |||
value += delta * lambda / sampleRate; | |||
impl->smoothModule->params[impl->smoothParamId] = value; | |||
value += delta * lambda / gSampleRate; | |||
smoothModule->params[smoothParamId] = value; | |||
} | |||
} | |||
// Step all modules | |||
std::chrono::time_point<std::chrono::high_resolution_clock> start, end; | |||
for (Module *module : impl->modules) { | |||
for (Module *module : modules) { | |||
// Start clock for CPU usage | |||
start = std::chrono::high_resolution_clock::now(); | |||
// Step module by one frame | |||
@@ -133,91 +99,126 @@ void Rack::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() * sampleRate; | |||
float elapsed = diff.count() * gSampleRate; | |||
const float lambda = 1.0; | |||
module->cpuTime += (elapsed - module->cpuTime) * lambda / sampleRate; | |||
module->cpuTime += (elapsed - module->cpuTime) * lambda / gSampleRate; | |||
} | |||
// Step cables by moving their output values to inputs | |||
for (Wire *wire : impl->wires) { | |||
for (Wire *wire : wires) { | |||
wire->inputValue = wire->outputValue; | |||
wire->outputValue = 0.0; | |||
} | |||
} | |||
void Rack::addModule(Module *module) { | |||
static void engineRun() { | |||
// 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 engine waits and locks a mutex, it steps this many frames | |||
const int stepSize = 32; | |||
while (running) { | |||
vipMutex.wait(); | |||
auto start = std::chrono::high_resolution_clock::now(); | |||
{ | |||
std::lock_guard<std::mutex> lock(mutex); | |||
for (int i = 0; i < stepSize; i++) { | |||
engineStep(); | |||
} | |||
} | |||
auto end = std::chrono::high_resolution_clock::now(); | |||
auto duration = std::chrono::nanoseconds((long) (0.9 * 1e9 * stepSize / gSampleRate)) - (end - start); | |||
// Avoid pegging the CPU at 100% when there are no "blocking" modules like AudioInterface, but still step audio at a reasonable rate | |||
std::this_thread::sleep_for(duration); | |||
} | |||
} | |||
void engineStart() { | |||
running = true; | |||
thread = std::thread(engineRun); | |||
} | |||
void engineStop() { | |||
running = false; | |||
thread.join(); | |||
} | |||
void engineAddModule(Module *module) { | |||
assert(module); | |||
VIPLock vipLock(impl->vipMutex); | |||
std::lock_guard<std::mutex> lock(impl->mutex); | |||
VIPLock vipLock(vipMutex); | |||
std::lock_guard<std::mutex> lock(mutex); | |||
// Check that the module is not already added | |||
assert(impl->modules.find(module) == impl->modules.end()); | |||
impl->modules.insert(module); | |||
assert(modules.find(module) == modules.end()); | |||
modules.insert(module); | |||
} | |||
void Rack::removeModule(Module *module) { | |||
void engineRemoveModule(Module *module) { | |||
assert(module); | |||
VIPLock vipLock(impl->vipMutex); | |||
std::lock_guard<std::mutex> lock(impl->mutex); | |||
VIPLock vipLock(vipMutex); | |||
std::lock_guard<std::mutex> lock(mutex); | |||
// Remove parameter interpolation which point to this module | |||
if (module == impl->smoothModule) { | |||
impl->smoothModule = NULL; | |||
if (module == smoothModule) { | |||
smoothModule = NULL; | |||
} | |||
// Check that all wires are disconnected | |||
for (Wire *wire : impl->wires) { | |||
for (Wire *wire : wires) { | |||
assert(wire->outputModule != module); | |||
assert(wire->inputModule != module); | |||
} | |||
auto it = impl->modules.find(module); | |||
if (it != impl->modules.end()) { | |||
impl->modules.erase(it); | |||
auto it = modules.find(module); | |||
if (it != modules.end()) { | |||
modules.erase(it); | |||
} | |||
} | |||
void Rack::addWire(Wire *wire) { | |||
void engineAddWire(Wire *wire) { | |||
assert(wire); | |||
VIPLock vipLock(impl->vipMutex); | |||
std::lock_guard<std::mutex> lock(impl->mutex); | |||
VIPLock vipLock(vipMutex); | |||
std::lock_guard<std::mutex> lock(mutex); | |||
// Check that the wire is not already added | |||
assert(impl->wires.find(wire) == impl->wires.end()); | |||
assert(wires.find(wire) == wires.end()); | |||
assert(wire->outputModule); | |||
assert(wire->inputModule); | |||
// Check that the inputs/outputs are not already used by another cable | |||
for (Wire *wire2 : impl->wires) { | |||
for (Wire *wire2 : wires) { | |||
assert(wire2 != wire); | |||
assert(!(wire2->outputModule == wire->outputModule && wire2->outputId == wire->outputId)); | |||
assert(!(wire2->inputModule == wire->inputModule && wire2->inputId == wire->inputId)); | |||
} | |||
// Connect the wire to inputModule | |||
impl->wires.insert(wire); | |||
wires.insert(wire); | |||
wire->inputModule->inputs[wire->inputId] = &wire->inputValue; | |||
wire->outputModule->outputs[wire->outputId] = &wire->outputValue; | |||
} | |||
void Rack::removeWire(Wire *wire) { | |||
void engineRemoveWire(Wire *wire) { | |||
assert(wire); | |||
VIPLock vipLock(impl->vipMutex); | |||
std::lock_guard<std::mutex> lock(impl->mutex); | |||
VIPLock vipLock(vipMutex); | |||
std::lock_guard<std::mutex> lock(mutex); | |||
// Disconnect wire from inputModule | |||
wire->inputModule->inputs[wire->inputId] = NULL; | |||
wire->outputModule->outputs[wire->outputId] = NULL; | |||
auto it = impl->wires.find(wire); | |||
assert(it != impl->wires.end()); | |||
impl->wires.erase(it); | |||
auto it = wires.find(wire); | |||
assert(it != wires.end()); | |||
wires.erase(it); | |||
} | |||
void Rack::setParamSmooth(Module *module, int paramId, float value) { | |||
VIPLock vipLock(impl->vipMutex); | |||
std::lock_guard<std::mutex> lock(impl->mutex); | |||
void engineSetParamSmooth(Module *module, int paramId, float value) { | |||
VIPLock vipLock(vipMutex); | |||
std::lock_guard<std::mutex> lock(mutex); | |||
// Check existing parameter interpolation | |||
if (impl->smoothModule) { | |||
if (!(impl->smoothModule == module && impl->smoothParamId == paramId)) { | |||
if (smoothModule) { | |||
if (!(smoothModule == module && smoothParamId == paramId)) { | |||
// Jump param value to smooth value | |||
impl->smoothModule->params[impl->smoothParamId] = impl->smoothValue; | |||
smoothModule->params[smoothParamId] = smoothValue; | |||
} | |||
} | |||
impl->smoothModule = module; | |||
impl->smoothParamId = paramId; | |||
impl->smoothValue = value; | |||
smoothModule = module; | |||
smoothParamId = paramId; | |||
smoothValue = value; | |||
} | |||
@@ -1,16 +1,19 @@ | |||
#include <unistd.h> | |||
#include "rack.hpp" | |||
#include <map> | |||
#include <GL/glew.h> | |||
#include <GLFW/glfw3.h> | |||
// #define NANOVG_GLEW | |||
#define NANOVG_IMPLEMENTATION | |||
#include "../ext/nanovg/src/nanovg.h" | |||
#include "gui.hpp" | |||
#include "scene.hpp" | |||
// Include implementations here | |||
// By the way, please stop packaging your libraries like this. It's easiest to use a single source file (e.g. foo.c) and a single header (e.g. foo.h) | |||
#define NANOVG_GL2_IMPLEMENTATION | |||
#include "../ext/nanovg/src/nanovg_gl.h" | |||
#define BLENDISH_IMPLEMENTATION | |||
#include "../ext/oui/blendish.h" | |||
#define NANOSVG_IMPLEMENTATION | |||
#include "../ext/nanosvg/src/nanosvg.h" | |||
extern "C" { | |||
#include "../ext/noc/noc_file_dialog.h" | |||
@@ -19,18 +22,9 @@ extern "C" { | |||
namespace rack { | |||
Scene *gScene = NULL; | |||
RackWidget *gRackWidget = NULL; | |||
Vec gMousePos; | |||
Widget *gHoveredWidget = NULL; | |||
Widget *gDraggedWidget = NULL; | |||
Widget *gSelectedWidget = NULL; | |||
int gGuiFrame; | |||
static GLFWwindow *window = NULL; | |||
static NVGcontext *vg = NULL; | |||
static std::shared_ptr<Font> defaultFont; | |||
void windowSizeCallback(GLFWwindow* window, int width, int height) { | |||
@@ -178,7 +172,7 @@ void guiInit() { | |||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); | |||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); | |||
window = glfwCreateWindow(1020, 700, gApplicationName.c_str(), NULL, NULL); | |||
window = glfwCreateWindow(1000, 750, gApplicationName.c_str(), NULL, NULL); | |||
assert(window); | |||
glfwMakeContextCurrent(window); | |||
@@ -206,15 +200,13 @@ void guiInit() { | |||
assert(vg); | |||
// Set up Blendish | |||
bndSetFont(loadFont("res/DejaVuSans.ttf")); | |||
defaultFont = Font::load("res/DejaVuSans.ttf"); | |||
bndSetFont(defaultFont->handle); | |||
// bndSetIconImage(loadImage("res/icons.png")); | |||
gScene = new Scene(); | |||
} | |||
void guiDestroy() { | |||
delete gScene; | |||
defaultFont.reset(); | |||
nvgDeleteGL2(vg); | |||
glfwDestroyWindow(window); | |||
glfwTerminate(); | |||
@@ -264,61 +256,83 @@ const char *guiOpenDialog(const char *filters, const char *filename) { | |||
} | |||
//////////////////// | |||
// resources | |||
//////////////////// | |||
Font::Font(const std::string &filename) { | |||
handle = nvgCreateFont(vg, filename.c_str(), filename.c_str()); | |||
if (handle >= 0) { | |||
fprintf(stderr, "Loaded font %s\n", filename.c_str()); | |||
} | |||
else { | |||
fprintf(stderr, "Failed to load font %s\n", filename.c_str()); | |||
} | |||
} | |||
Font::~Font() { | |||
// There is no NanoVG deleteFont() function, so do nothing | |||
} | |||
std::map<std::string, int> images; | |||
std::map<std::string, int> fonts; | |||
std::shared_ptr<Font> Font::load(const std::string &filename) { | |||
static std::map<std::string, std::weak_ptr<Font>> cache; | |||
auto sp = cache[filename].lock(); | |||
if (!sp) | |||
cache[filename] = sp = std::make_shared<Font>(filename); | |||
return sp; | |||
} | |||
int loadImage(std::string filename) { | |||
assert(vg); | |||
int imageId; | |||
auto it = images.find(filename); | |||
if (it == images.end()) { | |||
// Load image | |||
imageId = nvgCreateImage(vg, filename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY | NVG_IMAGE_NEAREST); | |||
if (imageId == 0) { | |||
printf("Failed to load image %s\n", filename.c_str()); | |||
} | |||
else { | |||
printf("Loaded image %s\n", filename.c_str()); | |||
} | |||
images[filename] = imageId; | |||
//////////////////// | |||
// Image | |||
//////////////////// | |||
Image::Image(const std::string &filename) { | |||
handle = nvgCreateImage(vg, filename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); | |||
if (handle > 0) { | |||
fprintf(stderr, "Loaded image %s\n", filename.c_str()); | |||
} | |||
else { | |||
imageId = it->second; | |||
fprintf(stderr, "Failed to load image %s\n", filename.c_str()); | |||
} | |||
return imageId; | |||
} | |||
int loadFont(std::string filename) { | |||
assert(vg); | |||
int fontId; | |||
auto it = fonts.find(filename); | |||
if (it == fonts.end()) { | |||
fontId = nvgCreateFont(vg, filename.c_str(), filename.c_str()); | |||
if (fontId < 0) { | |||
printf("Failed to load font %s\n", filename.c_str()); | |||
} | |||
else { | |||
printf("Loaded font %s\n", filename.c_str()); | |||
} | |||
fonts[filename] = fontId; | |||
Image::~Image() { | |||
// TODO What if handle is invalid? | |||
nvgDeleteImage(vg, handle); | |||
} | |||
std::shared_ptr<Image> Image::load(const std::string &filename) { | |||
static std::map<std::string, std::weak_ptr<Image>> cache; | |||
auto sp = cache[filename].lock(); | |||
if (!sp) | |||
cache[filename] = sp = std::make_shared<Image>(filename); | |||
return sp; | |||
} | |||
//////////////////// | |||
// SVG | |||
//////////////////// | |||
SVG::SVG(const std::string &filename) { | |||
image = nsvgParseFromFile(filename.c_str(), "px", 96.0); | |||
if (image) { | |||
fprintf(stderr, "Loaded SVG %s\n", filename.c_str()); | |||
} | |||
else { | |||
fontId = it->second; | |||
fprintf(stderr, "Failed to load SVG %s\n", filename.c_str()); | |||
} | |||
return fontId; | |||
} | |||
SVG::~SVG() { | |||
nsvgDelete(image); | |||
} | |||
void drawImage(NVGcontext *vg, Vec pos, int imageId) { | |||
int width, height; | |||
nvgImageSize(vg, imageId, &width, &height); | |||
NVGpaint paint = nvgImagePattern(vg, pos.x, pos.y, width, height, 0, imageId, 1.0); | |||
nvgFillPaint(vg, paint); | |||
nvgRect(vg, pos.x, pos.y, width, height); | |||
nvgFill(vg); | |||
std::shared_ptr<SVG> SVG::load(const std::string &filename) { | |||
static std::map<std::string, std::weak_ptr<SVG>> cache; | |||
auto sp = cache[filename].lock(); | |||
if (!sp) | |||
cache[filename] = sp = std::make_shared<SVG>(filename); | |||
return sp; | |||
} | |||
@@ -7,16 +7,6 @@ | |||
#include "rack.hpp" | |||
namespace rack { | |||
std::string gApplicationName = "VCV Rack"; | |||
std::string gApplicationVersion = "v0.1.0 alpha"; | |||
Rack *gRack; | |||
} // namespace rack | |||
using namespace rack; | |||
int main() { | |||
@@ -34,18 +24,20 @@ int main() { | |||
} | |||
#endif | |||
gRack = new Rack(); | |||
engineInit(); | |||
guiInit(); | |||
sceneInit(); | |||
pluginInit(); | |||
gRackWidget->loadPatch("autosave.json"); | |||
gRack->start(); | |||
engineStart(); | |||
guiRun(); | |||
gRack->stop(); | |||
engineStop(); | |||
gRackWidget->savePatch("autosave.json"); | |||
sceneDestroy(); | |||
guiDestroy(); | |||
delete gRack; | |||
engineDestroy(); | |||
pluginDestroy(); | |||
return 0; | |||
} | |||
@@ -7,8 +7,7 @@ | |||
#include <glob.h> | |||
#endif | |||
#include "rack.hpp" | |||
#include "core/core.hpp" | |||
#include "plugin.hpp" | |||
namespace rack { | |||
@@ -60,8 +59,10 @@ int loadPlugin(const char *path) { | |||
void pluginInit() { | |||
// Load core | |||
Plugin *corePlugin = coreInit(); | |||
// This function is defined in core.cpp | |||
Plugin *corePlugin = init(); | |||
gPlugins.push_back(corePlugin); | |||
// Search for plugin libraries | |||
#if defined(WINDOWS) | |||
WIN32_FIND_DATA ffd; | |||
@@ -0,0 +1,22 @@ | |||
#include "scene.hpp" | |||
namespace rack { | |||
std::string gApplicationName = "VCV Rack"; | |||
std::string gApplicationVersion = "v0.1.0 alpha"; | |||
RackWidget *gRackWidget = NULL; | |||
void sceneInit() { | |||
gScene = new RackScene(); | |||
} | |||
void sceneDestroy() { | |||
delete gScene; | |||
gScene = NULL; | |||
} | |||
} // namespace rack |
@@ -0,0 +1,13 @@ | |||
#include "widgets.hpp" | |||
namespace rack { | |||
Vec gMousePos; | |||
Widget *gHoveredWidget = NULL; | |||
Widget *gDraggedWidget = NULL; | |||
Widget *gSelectedWidget = NULL; | |||
int gGuiFrame; | |||
Scene *gScene = NULL; | |||
} // namespace rack |
@@ -1,4 +1,4 @@ | |||
#include "rack.hpp" | |||
#include "widgets.hpp" | |||
namespace rack { | |||
@@ -1,4 +1,4 @@ | |||
#include "rack.hpp" | |||
#include "widgets.hpp" | |||
namespace rack { | |||
@@ -1,4 +1,4 @@ | |||
#include "rack.hpp" | |||
#include "scene.hpp" | |||
namespace rack { | |||
@@ -1,4 +1,5 @@ | |||
#include "rack.hpp" | |||
#include "scene.hpp" | |||
#include "gui.hpp" | |||
namespace rack { | |||
@@ -1,4 +1,4 @@ | |||
#include "rack.hpp" | |||
#include "widgets.hpp" | |||
namespace rack { | |||
@@ -1,4 +1,4 @@ | |||
#include "rack.hpp" | |||
#include "scene.hpp" | |||
namespace rack { | |||
@@ -1,4 +1,4 @@ | |||
#include "rack.hpp" | |||
#include "widgets.hpp" | |||
namespace rack { | |||
@@ -1,4 +1,4 @@ | |||
#include "rack.hpp" | |||
#include "widgets.hpp" | |||
namespace rack { | |||
@@ -1,4 +1,4 @@ | |||
#include "rack.hpp" | |||
#include "widgets.hpp" | |||
namespace rack { | |||
@@ -1,4 +1,4 @@ | |||
#include "rack.hpp" | |||
#include "widgets.hpp" | |||
namespace rack { | |||
@@ -1,5 +1,4 @@ | |||
#include "rack.hpp" | |||
#include "widgets.hpp" | |||
namespace rack { | |||
@@ -1,4 +1,4 @@ | |||
#include "rack.hpp" | |||
#include "scene.hpp" | |||
namespace rack { | |||
@@ -13,11 +13,10 @@ void ModulePanel::draw(NVGcontext *vg) { | |||
nvgFill(vg); | |||
// Background image | |||
if (!imageFilename.empty()) { | |||
int imageId = loadImage(imageFilename); | |||
if (backgroundImage) { | |||
int width, height; | |||
nvgImageSize(vg, imageId, &width, &height); | |||
paint = nvgImagePattern(vg, box.pos.x, box.pos.y, width, height, 0.0, imageId, 1.0); | |||
nvgImageSize(vg, backgroundImage->handle, &width, &height); | |||
paint = nvgImagePattern(vg, box.pos.x, box.pos.y, width, height, 0.0, backgroundImage->handle, 1.0); | |||
nvgFillPaint(vg, paint); | |||
nvgFill(vg); | |||
} | |||
@@ -1,4 +1,6 @@ | |||
#include "rack.hpp" | |||
#include "scene.hpp" | |||
#include "engine.hpp" | |||
#include "plugin.hpp" | |||
namespace rack { | |||
@@ -6,7 +8,7 @@ namespace rack { | |||
ModuleWidget::ModuleWidget(Module *module) { | |||
this->module = module; | |||
if (module) { | |||
gRack->addModule(module); | |||
engineAddModule(module); | |||
} | |||
} | |||
@@ -14,7 +16,7 @@ ModuleWidget::~ModuleWidget() { | |||
// Make sure WireWidget destructors are called *before* removing `module` from the rack. | |||
disconnectPorts(); | |||
if (module) { | |||
gRack->removeModule(module); | |||
engineRemoveModule(module); | |||
delete module; | |||
} | |||
} | |||
@@ -100,7 +102,7 @@ void ModuleWidget::draw(NVGcontext *vg) { | |||
bndBevel(vg, box.pos.x, box.pos.y, box.size.x, box.size.y); | |||
// CPU usage text | |||
if (gScene->toolbar->cpuUsageButton->value != 0.0) { | |||
if (dynamic_cast<RackScene*>(gScene)->toolbar->cpuUsageButton->value != 0.0) { | |||
float cpuTime = module ? module->cpuTime : 0.0; | |||
std::string text = stringf("%.1f%%", cpuTime * 100.0); | |||
@@ -1,4 +1,4 @@ | |||
#include "rack.hpp" | |||
#include "scene.hpp" | |||
namespace rack { | |||
@@ -1,4 +1,5 @@ | |||
#include "rack.hpp" | |||
#include "scene.hpp" | |||
#include "engine.hpp" | |||
namespace rack { | |||
@@ -23,7 +24,7 @@ void ParamWidget::onChange() { | |||
return; | |||
// moduleWidget->module->params[paramId] = value; | |||
gRack->setParamSmooth(module, paramId, value); | |||
engineSetParamSmooth(module, paramId, value); | |||
} | |||
@@ -1,4 +1,4 @@ | |||
#include "rack.hpp" | |||
#include "scene.hpp" | |||
namespace rack { | |||
@@ -1,4 +1,4 @@ | |||
#include "rack.hpp" | |||
#include "widgets.hpp" | |||
namespace rack { | |||
@@ -0,0 +1,28 @@ | |||
#include "scene.hpp" | |||
namespace rack { | |||
RackScene::RackScene() { | |||
scrollWidget = new ScrollWidget(); | |||
{ | |||
assert(!gRackWidget); | |||
gRackWidget = new RackWidget(); | |||
scrollWidget->container->addChild(gRackWidget); | |||
} | |||
addChild(scrollWidget); | |||
toolbar = new Toolbar(); | |||
addChild(toolbar); | |||
scrollWidget->box.pos.y = toolbar->box.size.y; | |||
} | |||
void RackScene::step() { | |||
toolbar->box.size.x = box.size.x; | |||
scrollWidget->box.size = box.size.minus(scrollWidget->box.pos); | |||
Scene::step(); | |||
} | |||
} // namespace rack |
@@ -1,5 +1,9 @@ | |||
#include "rack.hpp" | |||
#include <map> | |||
#include <algorithm> | |||
#include "scene.hpp" | |||
#include "engine.hpp" | |||
#include "plugin.hpp" | |||
#include "gui.hpp" | |||
namespace rack { | |||
@@ -10,6 +14,8 @@ RackWidget::RackWidget() { | |||
wireContainer = new TransparentWidget(); | |||
addChild(wireContainer); | |||
railsImage = Image::load("res/rails.png"); | |||
} | |||
RackWidget::~RackWidget() { | |||
@@ -65,11 +71,11 @@ json_t *RackWidget::toJson() { | |||
json_object_set_new(root, "version", versionJ); | |||
// wireOpacity | |||
json_t *wireOpacityJ = json_real(gScene->toolbar->wireOpacitySlider->value); | |||
json_t *wireOpacityJ = json_real(dynamic_cast<RackScene*>(gScene)->toolbar->wireOpacitySlider->value); | |||
json_object_set_new(root, "wireOpacity", wireOpacityJ); | |||
// wireTension | |||
json_t *wireTensionJ = json_real(gScene->toolbar->wireTensionSlider->value); | |||
json_t *wireTensionJ = json_real(dynamic_cast<RackScene*>(gScene)->toolbar->wireTensionSlider->value); | |||
json_object_set_new(root, "wireTension", wireTensionJ); | |||
// modules | |||
@@ -140,12 +146,12 @@ void RackWidget::fromJson(json_t *root) { | |||
// wireOpacity | |||
json_t *wireOpacityJ = json_object_get(root, "wireOpacity"); | |||
if (wireOpacityJ) | |||
gScene->toolbar->wireOpacitySlider->value = json_number_value(wireOpacityJ); | |||
dynamic_cast<RackScene*>(gScene)->toolbar->wireOpacitySlider->value = json_number_value(wireOpacityJ); | |||
// wireTension | |||
json_t *wireTensionJ = json_object_get(root, "wireTension"); | |||
if (wireTensionJ) | |||
gScene->toolbar->wireTensionSlider->value = json_number_value(wireTensionJ); | |||
dynamic_cast<RackScene*>(gScene)->toolbar->wireTensionSlider->value = json_number_value(wireTensionJ); | |||
// modules | |||
std::map<int, ModuleWidget*> moduleWidgets; | |||
@@ -296,10 +302,9 @@ void RackWidget::draw(NVGcontext *vg) { | |||
nvgFill(vg); | |||
} | |||
{ | |||
int imageId = loadImage("res/rails.png"); | |||
int imageWidth, imageHeight; | |||
nvgImageSize(vg, imageId, &imageWidth, &imageHeight); | |||
paint = nvgImagePattern(vg, box.pos.x, box.pos.y, imageWidth, imageHeight, 0.0, imageId, 1.0); | |||
nvgImageSize(vg, railsImage->handle, &imageWidth, &imageHeight); | |||
paint = nvgImagePattern(vg, box.pos.x, box.pos.y, imageWidth, imageHeight, 0.0, railsImage->handle, 1.0); | |||
nvgFillPaint(vg, paint); | |||
nvgFill(vg); | |||
} | |||
@@ -1,4 +1,4 @@ | |||
#include "rack.hpp" | |||
#include "widgets.hpp" | |||
namespace rack { | |||
@@ -1,22 +1,8 @@ | |||
#include "rack.hpp" | |||
#include "widgets.hpp" | |||
namespace rack { | |||
Scene::Scene() { | |||
scrollWidget = new ScrollWidget(); | |||
{ | |||
assert(!gRackWidget); | |||
gRackWidget = new RackWidget(); | |||
scrollWidget->container->addChild(gRackWidget); | |||
} | |||
addChild(scrollWidget); | |||
toolbar = new Toolbar(); | |||
addChild(toolbar); | |||
scrollWidget->box.pos.y = toolbar->box.size.y; | |||
} | |||
void Scene::setOverlay(Widget *w) { | |||
if (overlay) { | |||
removeChild(overlay); | |||
@@ -31,8 +17,6 @@ void Scene::setOverlay(Widget *w) { | |||
} | |||
void Scene::step() { | |||
toolbar->box.size.x = box.size.x; | |||
scrollWidget->box.size = box.size.minus(scrollWidget->box.pos); | |||
if (overlay) { | |||
overlay->box.size = box.size; | |||
} | |||
@@ -1,4 +1,4 @@ | |||
#include "rack.hpp" | |||
#include "scene.hpp" | |||
namespace rack { | |||
@@ -7,7 +7,7 @@ Screw::Screw() { | |||
box.size = Vec(15, 15); | |||
spriteOffset = Vec(-7, -7); | |||
spriteSize = Vec(29, 29); | |||
spriteFilename = "res/screw.png"; | |||
spriteImage = Image::load("res/screw.png"); | |||
index = randomu32() % 5; | |||
} | |||
@@ -1,4 +1,5 @@ | |||
#include "rack.hpp" | |||
#include "widgets.hpp" | |||
#include "gui.hpp" | |||
namespace rack { | |||
@@ -1,4 +1,4 @@ | |||
#include "rack.hpp" | |||
#include "widgets.hpp" | |||
namespace rack { | |||
@@ -1,4 +1,5 @@ | |||
#include "rack.hpp" | |||
#include "widgets.hpp" | |||
#include "gui.hpp" | |||
namespace rack { | |||
@@ -1,26 +1,20 @@ | |||
#include "rack.hpp" | |||
#include "widgets.hpp" | |||
namespace rack { | |||
void SpriteWidget::draw(NVGcontext *vg) { | |||
int imageId = loadImage(spriteFilename); | |||
if (imageId < 0) { | |||
// printf("Could not load image %s for SpriteWidget\n", spriteFilename.c_str()); | |||
return; | |||
} | |||
Vec pos = box.pos.plus(spriteOffset); | |||
int width, height; | |||
nvgImageSize(vg, imageId, &width, &height); | |||
nvgImageSize(vg, spriteImage->handle, &width, &height); | |||
int stride = width / spriteSize.x; | |||
if (stride == 0) { | |||
printf("Size of SpriteWidget is %d, %d but spriteSize is %f, %f\n", width, height, spriteSize.x, spriteSize.y); | |||
return; | |||
} | |||
Vec offset = Vec((index % stride) * spriteSize.x, (index / stride) * spriteSize.y); | |||
NVGpaint paint = nvgImagePattern(vg, pos.x - offset.x, pos.y - offset.y, width, height, 0.0, imageId, 1.0); | |||
NVGpaint paint = nvgImagePattern(vg, pos.x - offset.x, pos.y - offset.y, width, height, 0.0, spriteImage->handle, 1.0); | |||
nvgFillPaint(vg, paint); | |||
nvgBeginPath(vg); | |||
nvgRect(vg, pos.x, pos.y, spriteSize.x, spriteSize.y); | |||
@@ -1,4 +1,6 @@ | |||
#include "rack.hpp" | |||
#include "scene.hpp" | |||
#include "gui.hpp" | |||
#include "engine.hpp" | |||
namespace rack { | |||
@@ -62,7 +64,7 @@ struct FileChoice : ChoiceButton { | |||
struct SampleRateItem : MenuItem { | |||
float sampleRate; | |||
void onAction() { | |||
gRack->sampleRate = sampleRate; | |||
gSampleRate = sampleRate; | |||
} | |||
}; | |||
@@ -85,7 +87,7 @@ struct SampleRateChoice : ChoiceButton { | |||
gScene->setOverlay(overlay); | |||
} | |||
void step() { | |||
text = stringf("%.0f Hz", gRack->sampleRate); | |||
text = stringf("%.0f Hz", gSampleRate); | |||
} | |||
}; | |||
@@ -1,4 +1,4 @@ | |||
#include "rack.hpp" | |||
#include "widgets.hpp" | |||
namespace rack { | |||
@@ -1,4 +1,4 @@ | |||
#include "rack.hpp" | |||
#include "widgets.hpp" | |||
#include <algorithm> | |||
@@ -1,4 +1,5 @@ | |||
#include "rack.hpp" | |||
#include "scene.hpp" | |||
#include "engine.hpp" | |||
namespace rack { | |||
@@ -112,7 +113,7 @@ WireWidget::~WireWidget() { | |||
void WireWidget::updateWire() { | |||
if (wire) { | |||
gRack->removeWire(wire); | |||
engineRemoveWire(wire); | |||
delete wire; | |||
wire = NULL; | |||
} | |||
@@ -122,14 +123,15 @@ void WireWidget::updateWire() { | |||
wire->outputId = outputPort->outputId; | |||
wire->inputModule = inputPort->module; | |||
wire->inputId = inputPort->inputId; | |||
gRack->addWire(wire); | |||
engineAddWire(wire); | |||
} | |||
} | |||
void WireWidget::draw(NVGcontext *vg) { | |||
Vec outputPos, inputPos; | |||
Vec absolutePos = getAbsolutePos(); | |||
float opacity = gScene->toolbar->wireOpacitySlider->value / 100.0; | |||
float opacity = dynamic_cast<RackScene*>(gScene)->toolbar->wireOpacitySlider->value / 100.0; | |||
float tension = dynamic_cast<RackScene*>(gScene)->toolbar->wireTensionSlider->value; | |||
// Compute location of pos1 and pos2 | |||
if (outputPort) { | |||
@@ -150,7 +152,6 @@ void WireWidget::draw(NVGcontext *vg) { | |||
outputPos = outputPos.minus(absolutePos); | |||
inputPos = inputPos.minus(absolutePos); | |||
float tension = gScene->toolbar->wireTensionSlider->value; | |||
drawWire(vg, outputPos, inputPos, tension, color, opacity); | |||
} | |||