Browse Source

Reorganize midi.hpp and audio.hpp, add prompt before clearing patch after crash, add Bridge scaffolding to AudioIO

tags/v0.6.0
Andrew Belt 6 years ago
parent
commit
5e6463f5af
12 changed files with 260 additions and 201 deletions
  1. +6
    -2
      include/audio.hpp
  2. +25
    -4
      include/midi.hpp
  3. +1
    -1
      src/app/AudioWidget.cpp
  4. +1
    -1
      src/app/RackWidget.cpp
  5. +126
    -63
      src/audio.cpp
  6. +5
    -32
      src/core/AudioInterface.cpp
  7. +0
    -44
      src/core/Bridge.cpp
  8. +17
    -42
      src/core/MidiToCV.cpp
  9. +1
    -2
      src/core/core.cpp
  10. +6
    -1
      src/main.cpp
  11. +71
    -8
      src/midi.cpp
  12. +1
    -1
      src/settings.cpp

+ 6
- 2
include/audio.hpp View File

@@ -1,5 +1,7 @@
#pragma once #pragma once


#include <jansson.h>

#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsuggest-override" #pragma GCC diagnostic ignored "-Wsuggest-override"
#include <RtAudio.h> #include <RtAudio.h>
@@ -13,20 +15,20 @@ struct AudioIO {
int maxOutputs = 8; int maxOutputs = 8;
int maxInputs = 8; int maxInputs = 8;


RtAudio *stream = NULL;
// Stream properties // Stream properties
int driver = 0;
int device = -1; int device = -1;
int sampleRate = 44100; int sampleRate = 44100;
int blockSize = 256; int blockSize = 256;
int numOutputs = 0; int numOutputs = 0;
int numInputs = 0; int numInputs = 0;
RtAudio *rtAudio = NULL;


AudioIO(); AudioIO();
virtual ~AudioIO(); virtual ~AudioIO();


std::vector<int> listDrivers(); std::vector<int> listDrivers();
std::string getDriverName(int driver); std::string getDriverName(int driver);
int getDriver();
void setDriver(int driver); void setDriver(int driver);


int getDeviceCount(); int getDeviceCount();
@@ -40,6 +42,8 @@ struct AudioIO {
virtual void processStream(const float *input, float *output, int length) {} virtual void processStream(const float *input, float *output, int length) {}
virtual void onCloseStream() {} virtual void onCloseStream() {}
virtual void onOpenStream() {} virtual void onOpenStream() {}
json_t *toJson();
void fromJson(json_t *rootJ);
}; };






+ 25
- 4
include/midi.hpp View File

@@ -1,5 +1,11 @@
#pragma once #pragma once


#include "util.hpp"
#include <queue>
#include <vector>
#include <jansson.h>


#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsuggest-override" #pragma GCC diagnostic ignored "-Wsuggest-override"
#include "rtmidi/RtMidi.h" #include "rtmidi/RtMidi.h"
@@ -9,8 +15,13 @@
namespace rack { namespace rack {




struct MidiMessage {
double time;
std::vector<uint8_t> data;
};


struct MidiIO { struct MidiIO {
RtMidi *midi;
int port = -1; int port = -1;
/* For MIDI output, the channel to output messages. /* For MIDI output, the channel to output messages.
For MIDI input, the channel to filter. For MIDI input, the channel to filter.
@@ -18,17 +29,27 @@ struct MidiIO {
Zero indexed. Zero indexed.
*/ */
int channel = -1; int channel = -1;
RtMidi *rtMidi = NULL;


virtual ~MidiIO() {} virtual ~MidiIO() {}
virtual int getPortCount();
virtual std::string getPortName(int port);
virtual void openPort(int port);
int getPortCount();
std::string getPortName(int port);
void openPort(int port);
json_t *toJson();
void fromJson(json_t *rootJ);
}; };




struct MidiInput : MidiIO { struct MidiInput : MidiIO {
MidiInput(); MidiInput();
~MidiInput(); ~MidiInput();
virtual void onMessage(const MidiMessage &message) {}
};


struct MidiInputQueue : MidiInput {
std::queue<MidiMessage> messageQueue;
void onMessage(const MidiMessage &message) override;
}; };






+ 1
- 1
src/app/AudioWidget.cpp View File

@@ -59,7 +59,7 @@ void AudioWidget::onMouseDown(EventMouseDown &e) {
item->audioIO = audioIO; item->audioIO = audioIO;
item->driver = driver; item->driver = driver;
item->text = audioIO->getDriverName(driver); item->text = audioIO->getDriverName(driver);
item->rightText = CHECKMARK(item->driver == audioIO->getDriver());
item->rightText = CHECKMARK(item->driver == audioIO->driver);
menu->addChild(item); menu->addChild(item);
} }
menu->addChild(construct<MenuEntry>()); menu->addChild(construct<MenuEntry>());


+ 1
- 1
src/app/RackWidget.cpp View File

@@ -110,7 +110,7 @@ void RackWidget::savePatch(std::string path) {


FILE *file = fopen(path.c_str(), "w"); FILE *file = fopen(path.c_str(), "w");
if (file) { if (file) {
json_dumpf(rootJ, file, JSON_INDENT(2));
json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9));
fclose(file); fclose(file);
} }




+ 126
- 63
src/audio.cpp View File

@@ -3,6 +3,9 @@
#include "audio.hpp" #include "audio.hpp"




#define DRIVER_BRIDGE -1


namespace rack { namespace rack {




@@ -20,6 +23,8 @@ std::vector<int> AudioIO::listDrivers() {
std::vector<int> drivers; std::vector<int> drivers;
for (RtAudio::Api api : apis) for (RtAudio::Api api : apis)
drivers.push_back((int) api); drivers.push_back((int) api);
// Add Bridge fake driver
// drivers.push_back(DRIVER_BRIDGE);
return drivers; return drivers;
} }


@@ -35,55 +40,71 @@ std::string AudioIO::getDriverName(int driver) {
case RtAudio::WINDOWS_ASIO: return "ASIO"; case RtAudio::WINDOWS_ASIO: return "ASIO";
case RtAudio::WINDOWS_DS: return "DirectSound"; case RtAudio::WINDOWS_DS: return "DirectSound";
case RtAudio::RTAUDIO_DUMMY: return "Dummy"; case RtAudio::RTAUDIO_DUMMY: return "Dummy";
case DRIVER_BRIDGE: return "VCV Bridge";
default: return "Unknown"; default: return "Unknown";
} }
} }


int AudioIO::getDriver() {
if (!stream)
return RtAudio::UNSPECIFIED;
return stream->getCurrentApi();
}

void AudioIO::setDriver(int driver) { void AudioIO::setDriver(int driver) {
// Close driver
closeStream(); closeStream();
if (stream)
delete stream;
stream = new RtAudio((RtAudio::Api) driver);
if (rtAudio) {
delete rtAudio;
rtAudio = NULL;
}
this->driver = 0;

// Open driver
if (driver >= 0) {
rtAudio = new RtAudio((RtAudio::Api) driver);
this->driver = (int) rtAudio->getCurrentApi();
}
else if (driver == DRIVER_BRIDGE) {
// TODO Connect to Bridge
this->driver = DRIVER_BRIDGE;
}
} }


int AudioIO::getDeviceCount() { int AudioIO::getDeviceCount() {
if (!stream)
return 0;
return stream->getDeviceCount();
if (rtAudio) {
return rtAudio->getDeviceCount();
}
if (driver == DRIVER_BRIDGE) {
return 16;
}
return 0;
} }


std::string AudioIO::getDeviceName(int device) { std::string AudioIO::getDeviceName(int device) {
if (!stream || device < 0)
return "";

try {
RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device);
return deviceInfo.name;
if (rtAudio) {
try {
RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(device);
return deviceInfo.name;
}
catch (RtAudioError &e) {
warn("Failed to query RtAudio device: %s", e.what());
}
} }
catch (RtAudioError &e) {
warn("Failed to query audio device: %s", e.what());
return "";
if (driver == DRIVER_BRIDGE) {
return stringf("%d", device + 1);
} }
return "";
} }


std::string AudioIO::getDeviceDetail(int device) { std::string AudioIO::getDeviceDetail(int device) {
if (!stream || device < 0)
return "";

try {
RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device);
return stringf("%s (%d in, %d out)", deviceInfo.name.c_str(), deviceInfo.inputChannels, deviceInfo.outputChannels);
if (rtAudio) {
try {
RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(device);
return stringf("%s (%d in, %d out)", deviceInfo.name.c_str(), deviceInfo.inputChannels, deviceInfo.outputChannels);
}
catch (RtAudioError &e) {
warn("Failed to query RtAudio device: %s", e.what());
}
} }
catch (RtAudioError &e) {
warn("Failed to query audio device: %s", e.what());
return "";
if (driver == DRIVER_BRIDGE) {
return stringf("Channel %d", device + 1);
} }
return "";
} }


static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData) { static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData) {
@@ -97,17 +118,18 @@ void AudioIO::openStream() {
// Close device but remember the current device number // Close device but remember the current device number
int device = this->device; int device = this->device;
closeStream(); closeStream();
if (!stream)

if (device < 0)
return; return;


// Open new device
if (device >= 0) {
if (rtAudio) {
// Open new device
RtAudio::DeviceInfo deviceInfo; RtAudio::DeviceInfo deviceInfo;
try { try {
deviceInfo = stream->getDeviceInfo(device);
deviceInfo = rtAudio->getDeviceInfo(device);
} }
catch (RtAudioError &e) { catch (RtAudioError &e) {
warn("Failed to query audio device: %s", e.what());
warn("Failed to query RtAudio device: %s", e.what());
return; return;
} }


@@ -115,7 +137,7 @@ void AudioIO::openStream() {
numInputs = mini(deviceInfo.inputChannels, maxInputs); numInputs = mini(deviceInfo.inputChannels, maxInputs);


if (numOutputs == 0 && numInputs == 0) { if (numOutputs == 0 && numInputs == 0) {
warn("Audio device %d has 0 inputs and 0 outputs");
warn("RtAudio device %d has 0 inputs and 0 outputs");
return; return;
} }


@@ -138,56 +160,56 @@ void AudioIO::openStream() {
} }


try { try {
debug("Opening audio stream %d", device);
stream->openStream(
debug("Opening audio RtAudio device %d", device);
rtAudio->openStream(
numOutputs == 0 ? NULL : &outParameters, numOutputs == 0 ? NULL : &outParameters,
numInputs == 0 ? NULL : &inParameters, numInputs == 0 ? NULL : &inParameters,
RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL); RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL);
} }
catch (RtAudioError &e) { catch (RtAudioError &e) {
warn("Failed to open audio stream: %s", e.what());
warn("Failed to open RtAudio stream: %s", e.what());
return; return;
} }


try { try {
debug("Starting audio stream %d", device);
stream->startStream();
debug("Starting RtAudio stream %d", device);
rtAudio->startStream();
} }
catch (RtAudioError &e) { catch (RtAudioError &e) {
warn("Failed to start audio stream: %s", e.what());
warn("Failed to start RtAudio stream: %s", e.what());
return; return;
} }


// Update sample rate because this may have changed // Update sample rate because this may have changed
this->sampleRate = stream->getStreamSampleRate();
this->sampleRate = rtAudio->getStreamSampleRate();
this->device = device; this->device = device;
onOpenStream(); onOpenStream();
} }
} }


void AudioIO::closeStream() { void AudioIO::closeStream() {
if (stream) {
if (stream->isStreamRunning()) {
debug("Stopping audio stream %d", device);
if (rtAudio) {
if (rtAudio->isStreamRunning()) {
debug("Stopping RtAudio stream %d", device);
try { try {
stream->stopStream();
rtAudio->stopStream();
} }
catch (RtAudioError &e) { catch (RtAudioError &e) {
warn("Failed to stop stream %s", e.what());
warn("Failed to stop RtAudio stream %s", e.what());
} }
} }
if (stream->isStreamOpen()) {
debug("Closing audio stream %d", device);
if (rtAudio->isStreamOpen()) {
debug("Closing RtAudio stream %d", device);
try { try {
stream->closeStream();
rtAudio->closeStream();
} }
catch (RtAudioError &e) { catch (RtAudioError &e) {
warn("Failed to close stream %s", e.what());
warn("Failed to close RtAudio stream %s", e.what());
} }
} }
} }


// Reset stream settings
// Reset rtAudio settings
device = -1; device = -1;
numOutputs = 0; numOutputs = 0;
numInputs = 0; numInputs = 0;
@@ -195,18 +217,59 @@ void AudioIO::closeStream() {
} }


std::vector<int> AudioIO::listSampleRates() { std::vector<int> AudioIO::listSampleRates() {
if (!stream || device < 0)
return {};

try {
RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device);
std::vector<int> sampleRates(deviceInfo.sampleRates.begin(), deviceInfo.sampleRates.end());
return sampleRates;
if (rtAudio) {
try {
RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(device);
std::vector<int> sampleRates(deviceInfo.sampleRates.begin(), deviceInfo.sampleRates.end());
return sampleRates;
}
catch (RtAudioError &e) {
warn("Failed to query RtAudio device: %s", e.what());
}
} }
catch (RtAudioError &e) {
warn("Failed to query audio device: %s", e.what());
return {};
if (driver == DRIVER_BRIDGE) {
return {44100, 48000, 88200, 96000, 176400, 192000};
} }

return {};
}

json_t *AudioIO::toJson() {
json_t *rootJ = json_object();
json_object_set_new(rootJ, "driver", json_integer(driver));
std::string deviceName = getDeviceName(device);
json_object_set_new(rootJ, "deviceName", json_string(deviceName.c_str()));
json_object_set_new(rootJ, "sampleRate", json_integer(sampleRate));
json_object_set_new(rootJ, "blockSize", json_integer(blockSize));
return rootJ;
}

void AudioIO::fromJson(json_t *rootJ) {
json_t *driverJ = json_object_get(rootJ, "driver");
if (driverJ)
setDriver(json_number_value(driverJ));

json_t *deviceNameJ = json_object_get(rootJ, "deviceName");
if (deviceNameJ) {
std::string deviceName = json_string_value(deviceNameJ);
// Search for device ID with equal name
for (int device = 0; device < getDeviceCount(); device++) {
if (getDeviceName(device) == deviceName) {
this->device = device;
break;
}
}
}

json_t *sampleRateJ = json_object_get(rootJ, "sampleRate");
if (sampleRateJ)
sampleRate = json_integer_value(sampleRateJ);

json_t *blockSizeJ = json_object_get(rootJ, "blockSize");
if (blockSizeJ)
blockSize = json_integer_value(blockSizeJ);

openStream();
} }






+ 5
- 32
src/core/AudioInterface.cpp View File

@@ -114,40 +114,13 @@ struct AudioInterface : Module {


json_t *toJson() override { json_t *toJson() override {
json_t *rootJ = json_object(); json_t *rootJ = json_object();
json_object_set_new(rootJ, "driver", json_integer(audioIO.getDriver()));
std::string deviceName = audioIO.getDeviceName(audioIO.device);
json_object_set_new(rootJ, "deviceName", json_string(deviceName.c_str()));
json_object_set_new(rootJ, "sampleRate", json_integer(audioIO.sampleRate));
json_object_set_new(rootJ, "blockSize", json_integer(audioIO.blockSize));
json_object_set_new(rootJ, "audio", audioIO.toJson());
return rootJ; return rootJ;
} }


void fromJson(json_t *rootJ) override { void fromJson(json_t *rootJ) override {
json_t *driverJ = json_object_get(rootJ, "driver");
if (driverJ)
audioIO.setDriver(json_number_value(driverJ));

json_t *deviceNameJ = json_object_get(rootJ, "deviceName");
if (deviceNameJ) {
std::string deviceName = json_string_value(deviceNameJ);
// Search for device ID with equal name
for (int device = 0; device < audioIO.getDeviceCount(); device++) {
if (audioIO.getDeviceName(device) == deviceName) {
audioIO.device = device;
break;
}
}
}

json_t *sampleRateJ = json_object_get(rootJ, "sampleRate");
if (sampleRateJ)
audioIO.sampleRate = json_integer_value(sampleRateJ);

json_t *blockSizeJ = json_object_get(rootJ, "blockSize");
if (blockSizeJ)
audioIO.blockSize = json_integer_value(blockSizeJ);

audioIO.openStream();
json_t *audioJ = json_object_get(rootJ, "audio");
audioIO.fromJson(audioJ);
} }


void onReset() override { void onReset() override {
@@ -236,7 +209,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() {
{ {
Label *label = new Label(); Label *label = new Label();
label->box.pos = Vec(margin.x, yPos); label->box.pos = Vec(margin.x, yPos);
label->text = "Outputs";
label->text = "Outputs (DACs)";
addChild(label); addChild(label);
yPos += labelHeight + margin.y; yPos += labelHeight + margin.y;
} }
@@ -270,7 +243,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() {
{ {
Label *label = new Label(); Label *label = new Label();
label->box.pos = Vec(margin.x, yPos); label->box.pos = Vec(margin.x, yPos);
label->text = "Inputs";
label->text = "Inputs (ADCs)";
addChild(label); addChild(label);
yPos += labelHeight + margin.y; yPos += labelHeight + margin.y;
} }


+ 0
- 44
src/core/Bridge.cpp View File

@@ -1,44 +0,0 @@
#include "core.hpp"

using namespace rack;


struct Bridge : Module {
enum ParamIds {
NUM_PARAMS
};
enum InputIds {
NUM_INPUTS
};
enum OutputIds {
NUM_OUTPUTS
};

Bridge() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {
}
~Bridge() {
}
void step() override;
};


void Bridge::step() {
}


BridgeWidget::BridgeWidget() {
Bridge *module = new Bridge();
setModule(module);
box.size = Vec(15*8, 380);

{
Panel *panel = new LightPanel();
panel->box.size = box.size;
addChild(panel);
}

addChild(createScrew<ScrewSilver>(Vec(15, 0)));
addChild(createScrew<ScrewSilver>(Vec(box.size.x-30, 0)));
addChild(createScrew<ScrewSilver>(Vec(15, 365)));
addChild(createScrew<ScrewSilver>(Vec(box.size.x-30, 365)));
}

+ 17
- 42
src/core/MidiToCV.cpp View File

@@ -1,4 +1,3 @@
#if 0
#include <list> #include <list>
#include <algorithm> #include <algorithm>
#include "core.hpp" #include "core.hpp"
@@ -12,7 +11,7 @@
*/ */
struct MidiValue { struct MidiValue {
int val = 0; // Controller value int val = 0; // Controller value
TransitionSmoother tSmooth;
// TransitionSmoother tSmooth;
bool changed = false; // Value has been changed by midi message (only if it is in sync!) bool changed = false; // Value has been changed by midi message (only if it is in sync!)
}; };


@@ -38,6 +37,7 @@ struct MIDIToCVInterface : Module {
NUM_LIGHTS NUM_LIGHTS
}; };


MidiInputQueue midiInput;
std::list<int> notes; std::list<int> notes;
bool pedal = false; bool pedal = false;
int note = 60; // C4, most modules should use 261.626 Hz int note = 60; // C4, most modules should use 261.626 Hz
@@ -51,7 +51,7 @@ struct MIDIToCVInterface : Module {


MIDIToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { MIDIToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
pitchWheel.val = 64; pitchWheel.val = 64;
pitchWheel.tSmooth.set(0, 0);
// pitchWheel.tSmooth.set(0, 0);
} }


~MIDIToCVInterface() { ~MIDIToCVInterface() {
@@ -67,22 +67,22 @@ struct MIDIToCVInterface : Module {


json_t *toJson() override { json_t *toJson() override {
json_t *rootJ = json_object(); json_t *rootJ = json_object();
addBaseJson(rootJ);
// addBaseJson(rootJ);
return rootJ; return rootJ;
} }


void fromJson(json_t *rootJ) override { void fromJson(json_t *rootJ) override {
baseFromJson(rootJ);
// baseFromJson(rootJ);
} }


void onReset() override { void onReset() override {
resetMidi();
// resetMidi();
} }


void resetMidi() override;

// void resetMidi() override;
}; };


/*
void MIDIToCVInterface::resetMidi() { void MIDIToCVInterface::resetMidi() {
mod.val = 0; mod.val = 0;
mod.tSmooth.set(0, 0); mod.tSmooth.set(0, 0);
@@ -94,8 +94,10 @@ void MIDIToCVInterface::resetMidi() {
gate = false; gate = false;
notes.clear(); notes.clear();
} }
*/


void MIDIToCVInterface::step() { void MIDIToCVInterface::step() {
/*
if (isPortOpen()) { if (isPortOpen()) {
std::vector<unsigned char> message; std::vector<unsigned char> message;


@@ -132,11 +134,8 @@ void MIDIToCVInterface::step() {
} }
outputs[PITCHWHEEL_OUTPUT].value = pitchWheel.tSmooth.next(); outputs[PITCHWHEEL_OUTPUT].value = pitchWheel.tSmooth.next();



/* NOTE: I'll leave out value smoothing for after touch for now. I currently don't
* have an after touch capable device around and I assume it would require different
* smoothing*/
outputs[CHANNEL_AFTERTOUCH_OUTPUT].value = afterTouch.val / 127.0 * 10.0; outputs[CHANNEL_AFTERTOUCH_OUTPUT].value = afterTouch.val / 127.0 * 10.0;
*/
} }


void MIDIToCVInterface::pressNote(int note) { void MIDIToCVInterface::pressNote(int note) {
@@ -171,6 +170,7 @@ void MIDIToCVInterface::releaseNote(int note) {
} }


void MIDIToCVInterface::processMidi(std::vector<unsigned char> msg) { void MIDIToCVInterface::processMidi(std::vector<unsigned char> msg) {
/*
int channel = msg[0] & 0xf; int channel = msg[0] & 0xf;
int status = (msg[0] >> 4) & 0xf; int status = (msg[0] >> 4) & 0xf;
int data1 = msg[1]; int data1 = msg[1];
@@ -220,6 +220,7 @@ void MIDIToCVInterface::processMidi(std::vector<unsigned char> msg) {
afterTouch.changed = true; afterTouch.changed = true;
break; break;
} }
*/
} }




@@ -254,35 +255,6 @@ MidiToCVWidget::MidiToCVWidget() {


addParam(createParam<LEDButton>(Vec(7 * 15, labelHeight), module, MIDIToCVInterface::RESET_PARAM, 0.0, 1.0, 0.0)); addParam(createParam<LEDButton>(Vec(7 * 15, labelHeight), module, MIDIToCVInterface::RESET_PARAM, 0.0, 1.0, 0.0));
addChild(createLight<SmallLight<RedLight>>(Vec(7 * 15 + 5, labelHeight + 5), module, MIDIToCVInterface::RESET_LIGHT)); addChild(createLight<SmallLight<RedLight>>(Vec(7 * 15 + 5, labelHeight + 5), module, MIDIToCVInterface::RESET_LIGHT));
{
Label *label = new Label();
label->box.pos = Vec(margin, yPos);
label->text = "MIDI Interface";
addChild(label);
yPos += labelHeight + margin;

MidiChoice *midiChoice = new MidiChoice();
midiChoice->midiModule = dynamic_cast<MidiIO *>(module);
midiChoice->box.pos = Vec(margin, yPos);
midiChoice->box.size.x = box.size.x - 10;
addChild(midiChoice);
yPos += midiChoice->box.size.y + margin;
}

{
Label *label = new Label();
label->box.pos = Vec(margin, yPos);
label->text = "Channel";
addChild(label);
yPos += labelHeight + margin;

ChannelChoice *channelChoice = new ChannelChoice();
channelChoice->midiModule = dynamic_cast<MidiIO *>(module);
channelChoice->box.pos = Vec(margin, yPos);
channelChoice->box.size.x = box.size.x - 10;
addChild(channelChoice);
yPos += channelChoice->box.size.y + margin + 15;
}


std::string labels[MIDIToCVInterface::NUM_OUTPUTS] = {"1V/oct", "Gate", "Velocity", "Mod Wheel", "Pitch Wheel", "Aftertouch"}; std::string labels[MIDIToCVInterface::NUM_OUTPUTS] = {"1V/oct", "Gate", "Velocity", "Mod Wheel", "Pitch Wheel", "Aftertouch"};


@@ -296,5 +268,8 @@ MidiToCVWidget::MidiToCVWidget() {


yPos += yGap + margin; yPos += yGap + margin;
} }

MidiWidget *midiWidget = construct<MIDI_DIN_MidiWidget>();
midiWidget->midiIO = &module->midiInput;
addChild(midiWidget);
} }
#endif

+ 1
- 2
src/core/core.cpp View File

@@ -9,13 +9,12 @@ void init(rack::Plugin *p) {


p->addModel(createModel<AudioInterfaceWidget>("Core", "AudioInterface", "Audio Interface", EXTERNAL_TAG)); p->addModel(createModel<AudioInterfaceWidget>("Core", "AudioInterface", "Audio Interface", EXTERNAL_TAG));


// p->addModel(createModel<MidiToCVWidget>("Core", "MIDIToCVInterface", "MIDI-to-CV Interface", MIDI_TAG, EXTERNAL_TAG));
p->addModel(createModel<MidiToCVWidget>("Core", "MIDIToCVInterface", "MIDI-to-CV Interface", MIDI_TAG, EXTERNAL_TAG));
// p->addModel(createModel<MIDICCToCVWidget>("Core", "MIDICCToCVInterface", "MIDI CC-to-CV Interface", MIDI_TAG, EXTERNAL_TAG)); // p->addModel(createModel<MIDICCToCVWidget>("Core", "MIDICCToCVInterface", "MIDI CC-to-CV Interface", MIDI_TAG, EXTERNAL_TAG));
// p->addModel(createModel<MIDIClockToCVWidget>("Core", "MIDIClockToCVInterface", "MIDI Clock-to-CV Interface", MIDI_TAG, EXTERNAL_TAG, CLOCK_TAG)); // p->addModel(createModel<MIDIClockToCVWidget>("Core", "MIDIClockToCVInterface", "MIDI Clock-to-CV Interface", MIDI_TAG, EXTERNAL_TAG, CLOCK_TAG));
// p->addModel(createModel<MIDITriggerToCVWidget>("Core", "MIDITriggerToCVInterface", "MIDI Trigger-to-CV Interface", MIDI_TAG, EXTERNAL_TAG)); // p->addModel(createModel<MIDITriggerToCVWidget>("Core", "MIDITriggerToCVInterface", "MIDI Trigger-to-CV Interface", MIDI_TAG, EXTERNAL_TAG));
// p->addModel(createModel<QuadMidiToCVWidget>("Core", "QuadMIDIToCVInterface", "Quad MIDI-to-CV Interface", MIDI_TAG, EXTERNAL_TAG, QUAD_TAG)); // p->addModel(createModel<QuadMidiToCVWidget>("Core", "QuadMIDIToCVInterface", "Quad MIDI-to-CV Interface", MIDI_TAG, EXTERNAL_TAG, QUAD_TAG));


// p->addModel(createModel<BridgeWidget>("Core", "Bridge", "Bridge"));
p->addModel(createModel<BlankWidget>("Core", "Blank", "Blank", BLANK_TAG)); p->addModel(createModel<BlankWidget>("Core", "Blank", "Blank", BLANK_TAG));
p->addModel(createModel<NotesWidget>("Core", "Notes", "Notes", BLANK_TAG)); p->addModel(createModel<NotesWidget>("Core", "Notes", "Notes", BLANK_TAG));
} }

+ 6
- 1
src/main.cpp View File

@@ -5,6 +5,7 @@
#include "settings.hpp" #include "settings.hpp"
#include "asset.hpp" #include "asset.hpp"
#include <unistd.h> #include <unistd.h>
#include "../ext/osdialog/osdialog.h"




using namespace rack; using namespace rack;
@@ -40,8 +41,12 @@ int main(int argc, char* argv[]) {
skipAutosaveOnLaunch = true; skipAutosaveOnLaunch = true;
settingsSave(assetLocal("settings.json")); settingsSave(assetLocal("settings.json"));
skipAutosaveOnLaunch = false; skipAutosaveOnLaunch = false;
if (!oldSkipAutosaveOnLaunch)
if (oldSkipAutosaveOnLaunch && osdialog_message(OSDIALOG_INFO, OSDIALOG_YES_NO, "Rack has recovered from a crash, likely caused by a faulty module in your patch. Would you like to clear your patch and start over?")) {
// Do nothing. Empty patch is already loaded.
}
else {
gRackWidget->loadPatch(assetLocal("autosave.vcv")); gRackWidget->loadPatch(assetLocal("autosave.vcv"));
}


engineStart(); engineStart();
guiRun(); guiRun();


+ 71
- 8
src/midi.cpp View File

@@ -4,38 +4,101 @@
namespace rack { namespace rack {




////////////////////
// MidiIO
////////////////////

int MidiIO::getPortCount() { int MidiIO::getPortCount() {
return midi->getPortCount();
return rtMidi->getPortCount();
} }


std::string MidiIO::getPortName(int port) { std::string MidiIO::getPortName(int port) {
return midi->getPortName(port);
if (port < 0)
return "";
return rtMidi->getPortName(port);
} }


void MidiIO::openPort(int port) { void MidiIO::openPort(int port) {
midi->closePort();
rtMidi->closePort();


if (port >= 0) { if (port >= 0) {
midi->openPort(port);
rtMidi->openPort(port);
} }
this->port = port; this->port = port;
} }


json_t *MidiIO::toJson() {
json_t *rootJ = json_object();
std::string portName = getPortName(port);
json_object_set_new(rootJ, "port", json_string(portName.c_str()));
json_object_set_new(rootJ, "channel", json_integer(channel));
return rootJ;
}

void MidiIO::fromJson(json_t *rootJ) {
json_t *portNameJ = json_object_get(rootJ, "port");
if (portNameJ) {
std::string portName = json_string_value(portNameJ);
// Search for port with equal name
for (int port = 0; port < getPortCount(); port++) {
if (getPortName(port) == portName) {
openPort(port);
break;
}
}
}

json_t *channelJ = json_object_get(rootJ, "channel");
if (channelJ)
channel = json_integer_value(channelJ);
}

////////////////////
// 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 midiMessage;
midiMessage.time = timeStamp;
midiMessage.data = *message;
midiInput->onMessage(midiMessage);
}

MidiInput::MidiInput() { MidiInput::MidiInput() {
midi = new RtMidiIn();
RtMidiIn *rtMidiIn = new RtMidiIn();
rtMidi = rtMidiIn;
rtMidiIn->setCallback(midiInputCallback, this);
} }


MidiInput::~MidiInput() { MidiInput::~MidiInput() {
delete dynamic_cast<RtMidiIn*>(midi);
delete dynamic_cast<RtMidiIn*>(rtMidi);
}

void MidiInputQueue::onMessage(const MidiMessage &message) {
for (uint8_t d : message.data) {
debug("MIDI message: %02x", d);
}

const int messageQueueSize = 8192;
if (messageQueue.size() < messageQueueSize)
messageQueue.push(message);
} }


////////////////////
// MidiOutput
////////////////////


MidiOutput::MidiOutput() { MidiOutput::MidiOutput() {
midi = new RtMidiOut();
rtMidi = new RtMidiOut();
} }


MidiOutput::~MidiOutput() { MidiOutput::~MidiOutput() {
delete dynamic_cast<RtMidiOut*>(midi);
delete dynamic_cast<RtMidiOut*>(rtMidi);
} }






+ 1
- 1
src/settings.cpp View File

@@ -137,7 +137,7 @@ void settingsSave(std::string filename) {
if (!file) if (!file)
return; return;


json_dumpf(rootJ, file, JSON_INDENT(2));
json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9));
json_decref(rootJ); json_decref(rootJ);
fclose(file); fclose(file);
} }


Loading…
Cancel
Save