@@ -1 +1 @@ | |||
Subproject commit 9a74da4db5ac74083e444010d75114658581b9c7 | |||
Subproject commit 06c1f0f3bb041d69a73bb74067d063a700215b0e |
@@ -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 | |||
@@ -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<Wire*> gWires; | |||
extern bool gCpuMeters; | |||
} // 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 | |||
#include "util/common.hpp" | |||
#include <queue> | |||
#include <vector> | |||
#include <queue> | |||
#include <set> | |||
#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 { | |||
@@ -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<int> 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<MidiMessage> 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<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 |
@@ -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) { | |||
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); | |||
} | |||
@@ -17,6 +17,7 @@ namespace rack { | |||
bool gPaused = false; | |||
std::vector<Module*> gModules; | |||
std::vector<Wire*> 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<float>(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) { | |||
@@ -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 "rtmidi.hpp" | |||
#include "bridge.hpp" | |||
#include "gamepad.hpp" | |||
namespace rack { | |||
@@ -10,15 +12,10 @@ namespace rack { | |||
//////////////////// | |||
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(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<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() { | |||
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); | |||
} | |||
} | |||
@@ -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 "app.hpp" | |||
#include "asset.hpp" | |||
#include "gamepad.hpp" | |||
#include "util/color.hpp" | |||
#include <map> | |||
@@ -431,6 +432,7 @@ void windowRun() { | |||
cursorPosCallback(gWindow, xpos, ypos); | |||
} | |||
mouseButtonStickyPop(); | |||
gamepadStep(); | |||
// Set window title | |||
std::string windowTitle; | |||