Browse Source

Restructure midi for multiplexing, add gamepad midi driver, add CPU

meter
tags/v0.6.1
Andrew Belt 6 years ago
parent
commit
531f348dc2
12 changed files with 271 additions and 125 deletions
  1. +1
    -1
      dep/nanosvg
  2. +8
    -9
      include/bridgeprotocol.hpp
  3. +1
    -0
      include/engine.hpp
  4. +13
    -0
      include/gamepad.hpp
  5. +42
    -19
      include/midi.hpp
  6. +33
    -0
      include/rtmidi.hpp
  7. +12
    -0
      src/app/ModuleWidget.cpp
  8. +9
    -0
      src/engine.cpp
  9. +23
    -0
      src/gamepad.cpp
  10. +58
    -96
      src/midi.cpp
  11. +69
    -0
      src/rtmidi.cpp
  12. +2
    -0
      src/window.cpp

+ 1
- 1
dep/nanosvg

@@ -1 +1 @@
Subproject commit 9a74da4db5ac74083e444010d75114658581b9c7
Subproject commit 06c1f0f3bb041d69a73bb74067d063a700215b0e

+ 8
- 9
include/bridgeprotocol.hpp View File

@@ -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


+ 1
- 0
include/engine.hpp View File

@@ -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

+ 13
- 0
include/gamepad.hpp View File

@@ -0,0 +1,13 @@
#pragma once



namespace rack {


const int GAMEPAD_DRIVER = -10;

void gamepadStep();


} // namespace rack

+ 42
- 19
include/midi.hpp View File

@@ -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

+ 33
- 0
include/rtmidi.hpp View File

@@ -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

+ 12
- 0
src/app/ModuleWidget.cpp View File

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



+ 9
- 0
src/engine.cpp View File

@@ -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) {


+ 23
- 0
src/gamepad.cpp View File

@@ -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

+ 58
- 96
src/midi.cpp View File

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



+ 69
- 0
src/rtmidi.cpp View File

@@ -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

+ 2
- 0
src/window.cpp View File

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


Loading…
Cancel
Save