diff --git a/dep/nanosvg b/dep/nanosvg index 9a74da4d..06c1f0f3 160000 --- a/dep/nanosvg +++ b/dep/nanosvg @@ -1 +1 @@ -Subproject commit 9a74da4db5ac74083e444010d75114658581b9c7 +Subproject commit 06c1f0f3bb041d69a73bb74067d063a700215b0e diff --git a/include/bridgeprotocol.hpp b/include/bridgeprotocol.hpp index 001cd097..d8992196 100644 --- a/include/bridgeprotocol.hpp +++ b/include/bridgeprotocol.hpp @@ -5,18 +5,17 @@ namespace rack { -// TODO Change driver and port number to something less common /** Driver ID in AudioIO and MidiIO */ -#define BRIDGE_DRIVER -12512 -#define BRIDGE_HOST "127.0.0.1" -#define BRIDGE_PORT 12512 -#define BRIDGE_NUM_PORTS 16 +const int BRIDGE_DRIVER = -12512; +const char* const BRIDGE_HOST = "127.0.0.1"; +const int BRIDGE_PORT = 12512; +const int BRIDGE_NUM_PORTS = 16; /** Number of VST/AU automation parameters */ -#define BRIDGE_NUM_PARAMS 16 +const int BRIDGE_NUM_PARAMS = 16; /** An arbitrary number which prevents connection from other protocols (like WebSockets) and old Bridge versions */ -#define BRIDGE_HELLO 0xff00fefd -#define BRIDGE_INPUTS 8 -#define BRIDGE_OUTPUTS 8 +const uint32_t BRIDGE_HELLO = 0xff00fefd; +const int BRIDGE_INPUTS = 8; +const int BRIDGE_OUTPUTS = 8; /** All commands are called from the client and served by the server diff --git a/include/engine.hpp b/include/engine.hpp index 36c4c813..52031466 100644 --- a/include/engine.hpp +++ b/include/engine.hpp @@ -127,6 +127,7 @@ Your plugin needs to have a clear purpose for manipulating other modules and wir */ extern std::vector gModules; extern std::vector gWires; +extern bool gCpuMeters; } // namespace rack diff --git a/include/gamepad.hpp b/include/gamepad.hpp new file mode 100644 index 00000000..cf1e06e6 --- /dev/null +++ b/include/gamepad.hpp @@ -0,0 +1,13 @@ +#pragma once + + + +namespace rack { + + +const int GAMEPAD_DRIVER = -10; + +void gamepadStep(); + + +} // namespace rack diff --git a/include/midi.hpp b/include/midi.hpp index eef7846f..72e46a57 100644 --- a/include/midi.hpp +++ b/include/midi.hpp @@ -1,17 +1,11 @@ #pragma once #include "util/common.hpp" -#include #include +#include +#include #include -#pragma GCC diagnostic push -#ifndef __clang__ -#pragma GCC diagnostic ignored "-Wsuggest-override" -#endif -#include "rtmidi/RtMidi.h" -#pragma GCC diagnostic pop - namespace rack { @@ -35,6 +29,13 @@ struct MidiMessage { } }; +//////////////////// +// MidiIO +//////////////////// + +struct MidiInputDevice; +struct MidiOutputDevice; + struct MidiIO { int driver = -1; @@ -45,18 +46,16 @@ struct MidiIO { Zero indexed. */ int channel = -1; - RtMidi *rtMidi = NULL; - /** Cached */ - std::string deviceName; virtual ~MidiIO() {} + std::vector getDrivers(); std::string getDriverName(int driver); - virtual void setDriver(int driver) {} + void setDriver(int driver); - int getDeviceCount(); - std::string getDeviceName(int device); - virtual void setDevice(int device) {} + virtual int getDeviceCount() = 0; + virtual std::string getDeviceName(int device) = 0; + virtual void setDevice(int device) = 0; std::string getChannelName(int channel); json_t *toJson(); @@ -65,17 +64,22 @@ struct MidiIO { struct MidiInput : MidiIO { - RtMidiIn *rtMidiIn = NULL; + MidiInputDevice *midiInputDevice = NULL; + MidiInput(); ~MidiInput(); - void setDriver(int driver) override; + + int getDeviceCount() override; + std::string getDeviceName(int device) override; void setDevice(int device) override; + virtual void onMessage(MidiMessage message) {} }; struct MidiInputQueue : MidiInput { int queueSize = 8192; + // TODO Switch to RingBuffer std::queue queue; void onMessage(MidiMessage message) override; /** If a MidiMessage is available, writes `message` and return true */ @@ -84,12 +88,31 @@ struct MidiInputQueue : MidiInput { struct MidiOutput : MidiIO { - RtMidiOut *rtMidiOut = NULL; + MidiOutputDevice *midiOutputDevice = NULL; MidiOutput(); ~MidiOutput(); - void setDriver(int driver) override; void setDevice(int device) override; }; +//////////////////// +// MidiIODevice +//////////////////// + +struct MidiIODevice { + virtual ~MidiIODevice() {} +}; + +struct MidiInputDevice : MidiIODevice { + std::set subscribed; + void subscribe(MidiInput *midiInput); + /** Deletes itself if nothing is subscribed */ + void unsubscribe(MidiInput *midiInput); + void onMessage(MidiMessage message); +}; + +struct MidiOutputDevice : MidiIODevice { + // TODO +}; + } // namespace rack diff --git a/include/rtmidi.hpp b/include/rtmidi.hpp new file mode 100644 index 00000000..f25789b6 --- /dev/null +++ b/include/rtmidi.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "util/common.hpp" +#include "midi.hpp" + +#pragma GCC diagnostic push +#ifndef __clang__ +#pragma GCC diagnostic ignored "-Wsuggest-override" +#endif +#include "rtmidi/RtMidi.h" +#pragma GCC diagnostic pop + + +namespace rack { + + +struct RtMidiInputDevice : MidiInputDevice { + RtMidiIn *rtMidiIn; + /** Cached */ + std::string deviceName; + + RtMidiInputDevice(int driver, int device); + ~RtMidiInputDevice(); +}; + + +std::vector rtmidiGetDrivers(); +int rtmidiGetDeviceCount(int driver); +std::string rtmidiGetDeviceName(int driver, int device); +RtMidiInputDevice *rtmidiGetDevice(int driver, int device); + + +} // namespace rack diff --git a/src/app/ModuleWidget.cpp b/src/app/ModuleWidget.cpp index b0ee1853..19b2f24a 100644 --- a/src/app/ModuleWidget.cpp +++ b/src/app/ModuleWidget.cpp @@ -187,6 +187,18 @@ void ModuleWidget::randomize() { void ModuleWidget::draw(NVGcontext *vg) { nvgScissor(vg, 0, 0, box.size.x, box.size.y); Widget::draw(vg); + + // CPU meter + if (gCpuMeters && module) { + float p = clamp(module->cpuTime, 0.f, 1.f); + nvgBeginPath(vg); + nvgRect(vg, + 0, (1.f - p) * box.size.y, + 5, p * box.size.y); + nvgFillColor(vg, nvgRGBAf(1, 0, 0, 1.0)); + nvgFill(vg); + } + nvgResetScissor(vg); } diff --git a/src/engine.cpp b/src/engine.cpp index 49b5c067..e2da89ea 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -17,6 +17,7 @@ namespace rack { bool gPaused = false; std::vector gModules; std::vector gWires; +bool gCpuMeters = true; static bool running = false; static float sampleRate; @@ -86,8 +87,16 @@ static void engineStep() { // Step modules for (Module *module : gModules) { + auto startTime = std::chrono::high_resolution_clock::now(); + module->step(); + if (gCpuMeters) { + auto stopTime = std::chrono::high_resolution_clock::now(); + float cpuTime = std::chrono::duration(stopTime - startTime).count() * sampleRate; + module->cpuTime += (cpuTime - module->cpuTime) * sampleTime * 10.f; + } + // TODO skip this step when plug lights are disabled // Step ports for (Input &input : module->inputs) { diff --git a/src/gamepad.cpp b/src/gamepad.cpp new file mode 100644 index 00000000..0c1ea1c6 --- /dev/null +++ b/src/gamepad.cpp @@ -0,0 +1,23 @@ +#include "gamepad.hpp" +#include "util/common.hpp" +#include + + +namespace rack { + + +void gamepadStep() { + for (int i = 0; i < 16; i++) { + if (glfwJoystickPresent(i)) { + const char *name = glfwGetJoystickName(i); + int numButtons; + const unsigned char *buttons = glfwGetJoystickButtons(i, &numButtons); + int numAxes; + const float *axes = glfwGetJoystickAxes(i, &numAxes); + debug("%d %s", i, name); + } + } +} + + +} // namespace rack diff --git a/src/midi.cpp b/src/midi.cpp index 18b62f0f..c68405e6 100644 --- a/src/midi.cpp +++ b/src/midi.cpp @@ -1,5 +1,7 @@ #include "midi.hpp" +#include "rtmidi.hpp" #include "bridge.hpp" +#include "gamepad.hpp" namespace rack { @@ -10,15 +12,10 @@ namespace rack { //////////////////// std::vector MidiIO::getDrivers() { - std::vector rtApis; - RtMidi::getCompiledApi(rtApis); - - std::vector drivers; - for (RtMidi::Api api : rtApis) { - drivers.push_back((int) api); - } - // Add fake Bridge driver + std::vector drivers = rtmidiGetDrivers(); + // Add custom drivers drivers.push_back(BRIDGE_DRIVER); + drivers.push_back(GAMEPAD_DRIVER); return drivers; } @@ -31,34 +28,14 @@ std::string MidiIO::getDriverName(int driver) { case RtMidi::WINDOWS_MM: return "Windows MIDI"; case RtMidi::RTMIDI_DUMMY: return "Dummy MIDI"; case BRIDGE_DRIVER: return "Bridge"; + case GAMEPAD_DRIVER: return "Gamepad"; default: return "Unknown"; } } -int MidiIO::getDeviceCount() { - if (rtMidi) { - return rtMidi->getPortCount(); - } - else if (driver == BRIDGE_DRIVER) { - return BRIDGE_NUM_PORTS; - } - return 0; -} - -std::string MidiIO::getDeviceName(int device) { - if (device < 0) - return ""; - - if (rtMidi) { - if (device == this->device) - return deviceName; - else - return rtMidi->getPortName(device); - } - else if (driver == BRIDGE_DRIVER) { - return stringf("Port %d", device + 1); - } - return ""; +void MidiIO::setDriver(int driver) { + setDevice(-1); + this->driver = driver; } std::string MidiIO::getChannelName(int channel) { @@ -105,68 +82,55 @@ void MidiIO::fromJson(json_t *rootJ) { // MidiInput //////////////////// -static void midiInputCallback(double timeStamp, std::vector *message, void *userData) { - if (!message) return; - if (!userData) return; - - MidiInput *midiInput = (MidiInput*) userData; - if (!midiInput) return; - MidiMessage msg; - if (message->size() >= 1) - msg.cmd = (*message)[0]; - if (message->size() >= 2) - msg.data1 = (*message)[1]; - if (message->size() >= 3) - msg.data2 = (*message)[2]; - - midiInput->onMessage(msg); -} MidiInput::MidiInput() { - setDriver(RtMidi::UNSPECIFIED); } MidiInput::~MidiInput() { setDriver(-1); } -void MidiInput::setDriver(int driver) { - setDevice(-1); - if (rtMidiIn) { - delete rtMidiIn; - rtMidi = rtMidiIn = NULL; +int MidiInput::getDeviceCount() { + if (driver >= 0) { + return rtmidiGetDeviceCount(driver); + } + else if (driver == BRIDGE_DRIVER) { + // TODO } + else if (driver == GAMEPAD_DRIVER) { + // TODO + } + return 0; +} +std::string MidiInput::getDeviceName(int device) { if (driver >= 0) { - rtMidiIn = new RtMidiIn((RtMidi::Api) driver, "VCV Rack"); - rtMidiIn->setCallback(midiInputCallback, this); - rtMidiIn->ignoreTypes(false, false, false); - rtMidi = rtMidiIn; - this->driver = rtMidiIn->getCurrentApi(); + return rtmidiGetDeviceName(driver, device); } else if (driver == BRIDGE_DRIVER) { - this->driver = BRIDGE_DRIVER; + // TODO + } + else if (driver == GAMEPAD_DRIVER) { + // TODO } + return ""; } void MidiInput::setDevice(int device) { - if (rtMidi) { - rtMidi->closePort(); + if (midiInputDevice) { + midiInputDevice->unsubscribe(this); + midiInputDevice = NULL; + } - if (device >= 0) { - rtMidi->openPort(device); - deviceName = rtMidi->getPortName(device); - } - this->device = device; + if (driver >= 0) { + midiInputDevice = rtmidiGetDevice(driver, device); + midiInputDevice->subscribe(this); } else if (driver == BRIDGE_DRIVER) { - if (device >= 0) { - bridgeMidiSubscribe(device, this); - } - else { - bridgeMidiUnsubscribe(device, this); - } - this->device = device; + // TODO + } + else if (driver == GAMEPAD_DRIVER) { + // TODO } } @@ -197,38 +161,36 @@ bool MidiInputQueue::shift(MidiMessage *message) { //////////////////// MidiOutput::MidiOutput() { - setDriver(RtMidi::UNSPECIFIED); } MidiOutput::~MidiOutput() { - setDriver(-1); + // TODO } -void MidiOutput::setDriver(int driver) { - setDevice(-1); - if (rtMidiOut) { - delete rtMidiOut; - rtMidi = rtMidiOut = NULL; - } +void MidiOutput::setDevice(int device) { + // TODO +} - if (driver >= 0) { - rtMidiOut = new RtMidiOut((RtMidi::Api) driver, "VCV Rack"); - rtMidi = rtMidiOut; - this->driver = rtMidiOut->getCurrentApi(); - } +// MidiIODevice + +void MidiInputDevice::subscribe(MidiInput *midiInput) { + subscribed.insert(midiInput); } -void MidiOutput::setDevice(int device) { - if (rtMidi) { - rtMidi->closePort(); +void MidiInputDevice::unsubscribe(MidiInput *midiInput) { + auto it = subscribed.find(midiInput); + if (it != subscribed.end()) + subscribed.erase(it); - if (device >= 0) { - rtMidi->openPort(device); - deviceName = rtMidi->getPortName(device); - } - this->device = device; + // Delete self if nothing is subscribed + if (subscribed.size() == 0) { + delete this; } - else if (driver == BRIDGE_DRIVER) { +} + +void MidiInputDevice::onMessage(MidiMessage message) { + for (MidiInput *midiInput : subscribed) { + midiInput->onMessage(message); } } diff --git a/src/rtmidi.cpp b/src/rtmidi.cpp new file mode 100644 index 00000000..cf067877 --- /dev/null +++ b/src/rtmidi.cpp @@ -0,0 +1,69 @@ +#include "rtmidi.hpp" + + +namespace rack { + + +static void midiInputCallback(double timeStamp, std::vector *message, void *userData) { + if (!message) return; + if (!userData) return; + + RtMidiInputDevice *midiInputDevice = (RtMidiInputDevice*) userData; + if (!midiInputDevice) return; + MidiMessage msg; + if (message->size() >= 1) + msg.cmd = (*message)[0]; + if (message->size() >= 2) + msg.data1 = (*message)[1]; + if (message->size() >= 3) + msg.data2 = (*message)[2]; + + midiInputDevice->onMessage(msg); +} + + +RtMidiInputDevice::RtMidiInputDevice(int driver, int device) { + rtMidiIn = new RtMidiIn((RtMidi::Api) driver, "VCV Rack"); + rtMidiIn->ignoreTypes(false, false, false); + rtMidiIn->setCallback(midiInputCallback, this); +} + + +RtMidiInputDevice::~RtMidiInputDevice() { + rtMidiIn->closePort(); + delete rtMidiIn; +} + + +std::vector rtmidiGetDrivers() { + std::vector rtApis; + RtMidi::getCompiledApi(rtApis); + + std::vector drivers; + for (RtMidi::Api api : rtApis) { + drivers.push_back((int) api); + } + return drivers; +} + +int rtmidiGetDeviceCount(int driver) { + RtMidiIn *rtMidiIn = new RtMidiIn((RtMidi::Api) driver); + int count = rtMidiIn->getPortCount(); + delete rtMidiIn; + return count; +} + +std::string rtmidiGetDeviceName(int driver, int device) { + RtMidiIn *rtMidiIn = new RtMidiIn((RtMidi::Api) driver); + std::string name = rtMidiIn->getPortName(device); + delete rtMidiIn; + return name; +} + +RtMidiInputDevice *rtmidiGetDevice(int driver, int device) { + // TODO Pull from cache + return new RtMidiInputDevice(driver, device); +} + + +} // namespace rack diff --git a/src/window.cpp b/src/window.cpp index bb72888f..0b75c430 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -1,6 +1,7 @@ #include "window.hpp" #include "app.hpp" #include "asset.hpp" +#include "gamepad.hpp" #include "util/color.hpp" #include @@ -431,6 +432,7 @@ void windowRun() { cursorPosCallback(gWindow, xpos, ypos); } mouseButtonStickyPop(); + gamepadStep(); // Set window title std::string windowTitle;