@@ -1 +1 @@ | |||||
Subproject commit 9a74da4db5ac74083e444010d75114658581b9c7 | |||||
Subproject commit 06c1f0f3bb041d69a73bb74067d063a700215b0e |
@@ -5,18 +5,17 @@ | |||||
namespace rack { | namespace rack { | ||||
// TODO Change driver and port number to something less common | |||||
/** Driver ID in AudioIO and MidiIO */ | /** 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 */ | /** 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 */ | /** 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 | /** All commands are called from the client and served by the server | ||||
@@ -127,6 +127,7 @@ Your plugin needs to have a clear purpose for manipulating other modules and wir | |||||
*/ | */ | ||||
extern std::vector<Module*> gModules; | extern std::vector<Module*> gModules; | ||||
extern std::vector<Wire*> gWires; | extern std::vector<Wire*> gWires; | ||||
extern bool gCpuMeters; | |||||
} // namespace rack | } // namespace rack |
@@ -0,0 +1,13 @@ | |||||
#pragma once | |||||
namespace rack { | |||||
const int GAMEPAD_DRIVER = -10; | |||||
void gamepadStep(); | |||||
} // namespace rack |
@@ -1,17 +1,11 @@ | |||||
#pragma once | #pragma once | ||||
#include "util/common.hpp" | #include "util/common.hpp" | ||||
#include <queue> | |||||
#include <vector> | #include <vector> | ||||
#include <queue> | |||||
#include <set> | |||||
#include <jansson.h> | #include <jansson.h> | ||||
#pragma GCC diagnostic push | |||||
#ifndef __clang__ | |||||
#pragma GCC diagnostic ignored "-Wsuggest-override" | |||||
#endif | |||||
#include "rtmidi/RtMidi.h" | |||||
#pragma GCC diagnostic pop | |||||
namespace rack { | namespace rack { | ||||
@@ -35,6 +29,13 @@ struct MidiMessage { | |||||
} | } | ||||
}; | }; | ||||
//////////////////// | |||||
// MidiIO | |||||
//////////////////// | |||||
struct MidiInputDevice; | |||||
struct MidiOutputDevice; | |||||
struct MidiIO { | struct MidiIO { | ||||
int driver = -1; | int driver = -1; | ||||
@@ -45,18 +46,16 @@ struct MidiIO { | |||||
Zero indexed. | Zero indexed. | ||||
*/ | */ | ||||
int channel = -1; | int channel = -1; | ||||
RtMidi *rtMidi = NULL; | |||||
/** Cached */ | |||||
std::string deviceName; | |||||
virtual ~MidiIO() {} | virtual ~MidiIO() {} | ||||
std::vector<int> getDrivers(); | std::vector<int> getDrivers(); | ||||
std::string getDriverName(int driver); | 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); | std::string getChannelName(int channel); | ||||
json_t *toJson(); | json_t *toJson(); | ||||
@@ -65,17 +64,22 @@ struct MidiIO { | |||||
struct MidiInput : MidiIO { | struct MidiInput : MidiIO { | ||||
RtMidiIn *rtMidiIn = NULL; | |||||
MidiInputDevice *midiInputDevice = NULL; | |||||
MidiInput(); | MidiInput(); | ||||
~MidiInput(); | ~MidiInput(); | ||||
void setDriver(int driver) override; | |||||
int getDeviceCount() override; | |||||
std::string getDeviceName(int device) override; | |||||
void setDevice(int device) override; | void setDevice(int device) override; | ||||
virtual void onMessage(MidiMessage message) {} | virtual void onMessage(MidiMessage message) {} | ||||
}; | }; | ||||
struct MidiInputQueue : MidiInput { | struct MidiInputQueue : MidiInput { | ||||
int queueSize = 8192; | int queueSize = 8192; | ||||
// TODO Switch to RingBuffer | |||||
std::queue<MidiMessage> queue; | std::queue<MidiMessage> queue; | ||||
void onMessage(MidiMessage message) override; | void onMessage(MidiMessage message) override; | ||||
/** If a MidiMessage is available, writes `message` and return true */ | /** If a MidiMessage is available, writes `message` and return true */ | ||||
@@ -84,12 +88,31 @@ struct MidiInputQueue : MidiInput { | |||||
struct MidiOutput : MidiIO { | struct MidiOutput : MidiIO { | ||||
RtMidiOut *rtMidiOut = NULL; | |||||
MidiOutputDevice *midiOutputDevice = NULL; | |||||
MidiOutput(); | MidiOutput(); | ||||
~MidiOutput(); | ~MidiOutput(); | ||||
void setDriver(int driver) override; | |||||
void setDevice(int device) override; | void setDevice(int device) override; | ||||
}; | }; | ||||
//////////////////// | |||||
// MidiIODevice | |||||
//////////////////// | |||||
struct MidiIODevice { | |||||
virtual ~MidiIODevice() {} | |||||
}; | |||||
struct MidiInputDevice : MidiIODevice { | |||||
std::set<MidiInput*> 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 | } // namespace rack |
@@ -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<int> rtmidiGetDrivers(); | |||||
int rtmidiGetDeviceCount(int driver); | |||||
std::string rtmidiGetDeviceName(int driver, int device); | |||||
RtMidiInputDevice *rtmidiGetDevice(int driver, int device); | |||||
} // namespace rack |
@@ -187,6 +187,18 @@ void ModuleWidget::randomize() { | |||||
void ModuleWidget::draw(NVGcontext *vg) { | void ModuleWidget::draw(NVGcontext *vg) { | ||||
nvgScissor(vg, 0, 0, box.size.x, box.size.y); | nvgScissor(vg, 0, 0, box.size.x, box.size.y); | ||||
Widget::draw(vg); | 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); | nvgResetScissor(vg); | ||||
} | } | ||||
@@ -17,6 +17,7 @@ namespace rack { | |||||
bool gPaused = false; | bool gPaused = false; | ||||
std::vector<Module*> gModules; | std::vector<Module*> gModules; | ||||
std::vector<Wire*> gWires; | std::vector<Wire*> gWires; | ||||
bool gCpuMeters = true; | |||||
static bool running = false; | static bool running = false; | ||||
static float sampleRate; | static float sampleRate; | ||||
@@ -86,8 +87,16 @@ static void engineStep() { | |||||
// Step modules | // Step modules | ||||
for (Module *module : gModules) { | for (Module *module : gModules) { | ||||
auto startTime = std::chrono::high_resolution_clock::now(); | |||||
module->step(); | module->step(); | ||||
if (gCpuMeters) { | |||||
auto stopTime = std::chrono::high_resolution_clock::now(); | |||||
float cpuTime = std::chrono::duration<float>(stopTime - startTime).count() * sampleRate; | |||||
module->cpuTime += (cpuTime - module->cpuTime) * sampleTime * 10.f; | |||||
} | |||||
// TODO skip this step when plug lights are disabled | // TODO skip this step when plug lights are disabled | ||||
// Step ports | // Step ports | ||||
for (Input &input : module->inputs) { | for (Input &input : module->inputs) { | ||||
@@ -0,0 +1,23 @@ | |||||
#include "gamepad.hpp" | |||||
#include "util/common.hpp" | |||||
#include <GLFW/glfw3.h> | |||||
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 |
@@ -1,5 +1,7 @@ | |||||
#include "midi.hpp" | #include "midi.hpp" | ||||
#include "rtmidi.hpp" | |||||
#include "bridge.hpp" | #include "bridge.hpp" | ||||
#include "gamepad.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -10,15 +12,10 @@ namespace rack { | |||||
//////////////////// | //////////////////// | ||||
std::vector<int> MidiIO::getDrivers() { | std::vector<int> MidiIO::getDrivers() { | ||||
std::vector<RtMidi::Api> rtApis; | |||||
RtMidi::getCompiledApi(rtApis); | |||||
std::vector<int> drivers; | |||||
for (RtMidi::Api api : rtApis) { | |||||
drivers.push_back((int) api); | |||||
} | |||||
// Add fake Bridge driver | |||||
std::vector<int> drivers = rtmidiGetDrivers(); | |||||
// Add custom drivers | |||||
drivers.push_back(BRIDGE_DRIVER); | drivers.push_back(BRIDGE_DRIVER); | ||||
drivers.push_back(GAMEPAD_DRIVER); | |||||
return drivers; | return drivers; | ||||
} | } | ||||
@@ -31,34 +28,14 @@ std::string MidiIO::getDriverName(int driver) { | |||||
case RtMidi::WINDOWS_MM: return "Windows MIDI"; | case RtMidi::WINDOWS_MM: return "Windows MIDI"; | ||||
case RtMidi::RTMIDI_DUMMY: return "Dummy MIDI"; | case RtMidi::RTMIDI_DUMMY: return "Dummy MIDI"; | ||||
case BRIDGE_DRIVER: return "Bridge"; | case BRIDGE_DRIVER: return "Bridge"; | ||||
case GAMEPAD_DRIVER: return "Gamepad"; | |||||
default: return "Unknown"; | 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) { | std::string MidiIO::getChannelName(int channel) { | ||||
@@ -105,68 +82,55 @@ void MidiIO::fromJson(json_t *rootJ) { | |||||
// MidiInput | // MidiInput | ||||
//////////////////// | //////////////////// | ||||
static void midiInputCallback(double timeStamp, std::vector<unsigned char> *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() { | MidiInput::MidiInput() { | ||||
setDriver(RtMidi::UNSPECIFIED); | |||||
} | } | ||||
MidiInput::~MidiInput() { | MidiInput::~MidiInput() { | ||||
setDriver(-1); | 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) { | 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) { | else if (driver == BRIDGE_DRIVER) { | ||||
this->driver = BRIDGE_DRIVER; | |||||
// TODO | |||||
} | |||||
else if (driver == GAMEPAD_DRIVER) { | |||||
// TODO | |||||
} | } | ||||
return ""; | |||||
} | } | ||||
void MidiInput::setDevice(int device) { | 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) { | 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() { | MidiOutput::MidiOutput() { | ||||
setDriver(RtMidi::UNSPECIFIED); | |||||
} | } | ||||
MidiOutput::~MidiOutput() { | 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); | |||||
} | } | ||||
} | } | ||||
@@ -0,0 +1,69 @@ | |||||
#include "rtmidi.hpp" | |||||
namespace rack { | |||||
static void midiInputCallback(double timeStamp, std::vector<unsigned char> *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<int> rtmidiGetDrivers() { | |||||
std::vector<RtMidi::Api> rtApis; | |||||
RtMidi::getCompiledApi(rtApis); | |||||
std::vector<int> 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 |
@@ -1,6 +1,7 @@ | |||||
#include "window.hpp" | #include "window.hpp" | ||||
#include "app.hpp" | #include "app.hpp" | ||||
#include "asset.hpp" | #include "asset.hpp" | ||||
#include "gamepad.hpp" | |||||
#include "util/color.hpp" | #include "util/color.hpp" | ||||
#include <map> | #include <map> | ||||
@@ -431,6 +432,7 @@ void windowRun() { | |||||
cursorPosCallback(gWindow, xpos, ypos); | cursorPosCallback(gWindow, xpos, ypos); | ||||
} | } | ||||
mouseButtonStickyPop(); | mouseButtonStickyPop(); | ||||
gamepadStep(); | |||||
// Set window title | // Set window title | ||||
std::string windowTitle; | std::string windowTitle; | ||||