#include #include #include #include #include #include #include #include #include "rack.hpp" static std::thread thread; static std::mutex mutex; static std::condition_variable cv; static long frame; static long frameLimit; static bool running; static std::set modules; // Merely used for keeping track of which module inputs point to which module outputs, to prevent pointer mistakes and make the rack API rigorous static std::set wires; static Module *smoothModule = NULL; static int smoothParamId; static float smoothValue; void rackInit() { } void rackDestroy() { // Make sure there are no wires or modules in the rack on destruction. This suggests that a module failed to remove itself when the GUI was destroyed. assert(wires.empty()); assert(modules.empty()); } void rackStep() { // Param interpolation if (smoothModule) { float value = smoothModule->params[smoothParamId]; const float minSpeed = 0.01 * 60.0 / SAMPLE_RATE; // Roughly 0.01 every graphics frame const float lpCoeff = 60.0 / SAMPLE_RATE / 1.0; // decay rate is 1 graphics frame float delta = smoothValue - value; float speed = fmaxf(fabsf(delta) * lpCoeff, minSpeed); if (delta < 0) { value -= speed; if (value < smoothValue) value = smoothValue; } else if (delta > 0) { value += speed; if (value > smoothValue) value = smoothValue; } smoothModule->params[smoothParamId] = value; if (value == smoothValue) { smoothModule = NULL; } } // Step all modules for (Module *module : modules) { module->step(); } } void rackRun() { while (1) { std::unique_lock lock(mutex); if (!running) break; if (frame >= frameLimit) { // Delay for at most 1ms if there are no needed frames cv.wait_for(lock, std::chrono::milliseconds(1)); } frame++; lock.unlock(); // Speed up // for (int i = 0; i < 16; i++) rackStep(); } } void rackStart() { frame = 0; frameLimit = 0; running = true; thread = std::thread(rackRun); } void rackStop() { { std::unique_lock lock(mutex); running = false; } cv.notify_all(); thread.join(); } void rackAddModule(Module *module) { assert(module); // Check that the module is not already added assert(modules.find(module) == modules.end()); modules.insert(module); } void rackRemoveModule(Module *module) { assert(module); // Remove parameter interpolation which point to this module if (module == smoothModule) { smoothModule = NULL; } // FIXME use a mutex here // Check that all wires are disconnected for (Wire *wire : wires) { assert(wire->outputModule != module); assert(wire->inputModule != module); } auto it = modules.find(module); if (it != modules.end()) { modules.erase(it); } } void rackConnectWire(Wire *wire) { assert(wire); // It would probably be good to reset the wire voltage wire->value = 0.0; // Check that the wire is not already added 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 : 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 wires.insert(wire); wire->inputModule->inputs[wire->inputId] = &wire->value; wire->outputModule->outputs[wire->outputId] = &wire->value; } void rackDisconnectWire(Wire *wire) { assert(wire); // Disconnect wire from inputModule wire->inputModule->inputs[wire->inputId] = NULL; wire->outputModule->outputs[wire->outputId] = NULL; auto it = wires.find(wire); assert(it != wires.end()); wires.erase(it); } long rackGetFrame() { return frame; } void rackRequestFrame(long f) { std::unique_lock lock(mutex); if (f > frameLimit) { frameLimit = f; lock.unlock(); cv.notify_all(); } } void rackSetParamSmooth(Module *module, int paramId, float value) { // Check existing parameter interpolation if (smoothModule) { if (!(smoothModule == module && smoothParamId == paramId)) { // Jump param value to smooth value smoothModule->params[smoothParamId] = smoothValue; } } smoothModule = module; smoothParamId = paramId; smoothValue = value; }