| @@ -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); | |||
| } | |||