@@ -1,5 +1,7 @@ | |||
#pragma once | |||
#include <jansson.h> | |||
#pragma GCC diagnostic push | |||
#pragma GCC diagnostic ignored "-Wsuggest-override" | |||
#include <RtAudio.h> | |||
@@ -13,20 +15,20 @@ struct AudioIO { | |||
int maxOutputs = 8; | |||
int maxInputs = 8; | |||
RtAudio *stream = NULL; | |||
// Stream properties | |||
int driver = 0; | |||
int device = -1; | |||
int sampleRate = 44100; | |||
int blockSize = 256; | |||
int numOutputs = 0; | |||
int numInputs = 0; | |||
RtAudio *rtAudio = NULL; | |||
AudioIO(); | |||
virtual ~AudioIO(); | |||
std::vector<int> listDrivers(); | |||
std::string getDriverName(int driver); | |||
int getDriver(); | |||
void setDriver(int driver); | |||
int getDeviceCount(); | |||
@@ -40,6 +42,8 @@ struct AudioIO { | |||
virtual void processStream(const float *input, float *output, int length) {} | |||
virtual void onCloseStream() {} | |||
virtual void onOpenStream() {} | |||
json_t *toJson(); | |||
void fromJson(json_t *rootJ); | |||
}; | |||
@@ -1,5 +1,11 @@ | |||
#pragma once | |||
#include "util.hpp" | |||
#include <queue> | |||
#include <vector> | |||
#include <jansson.h> | |||
#pragma GCC diagnostic push | |||
#pragma GCC diagnostic ignored "-Wsuggest-override" | |||
#include "rtmidi/RtMidi.h" | |||
@@ -9,8 +15,13 @@ | |||
namespace rack { | |||
struct MidiMessage { | |||
double time; | |||
std::vector<uint8_t> data; | |||
}; | |||
struct MidiIO { | |||
RtMidi *midi; | |||
int port = -1; | |||
/* For MIDI output, the channel to output messages. | |||
For MIDI input, the channel to filter. | |||
@@ -18,17 +29,27 @@ struct MidiIO { | |||
Zero indexed. | |||
*/ | |||
int channel = -1; | |||
RtMidi *rtMidi = NULL; | |||
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 { | |||
MidiInput(); | |||
~MidiInput(); | |||
virtual void onMessage(const MidiMessage &message) {} | |||
}; | |||
struct MidiInputQueue : MidiInput { | |||
std::queue<MidiMessage> messageQueue; | |||
void onMessage(const MidiMessage &message) override; | |||
}; | |||
@@ -59,7 +59,7 @@ void AudioWidget::onMouseDown(EventMouseDown &e) { | |||
item->audioIO = audioIO; | |||
item->driver = 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(construct<MenuEntry>()); | |||
@@ -110,7 +110,7 @@ void RackWidget::savePatch(std::string path) { | |||
FILE *file = fopen(path.c_str(), "w"); | |||
if (file) { | |||
json_dumpf(rootJ, file, JSON_INDENT(2)); | |||
json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9)); | |||
fclose(file); | |||
} | |||
@@ -3,6 +3,9 @@ | |||
#include "audio.hpp" | |||
#define DRIVER_BRIDGE -1 | |||
namespace rack { | |||
@@ -20,6 +23,8 @@ std::vector<int> AudioIO::listDrivers() { | |||
std::vector<int> drivers; | |||
for (RtAudio::Api api : apis) | |||
drivers.push_back((int) api); | |||
// Add Bridge fake driver | |||
// drivers.push_back(DRIVER_BRIDGE); | |||
return drivers; | |||
} | |||
@@ -35,55 +40,71 @@ std::string AudioIO::getDriverName(int driver) { | |||
case RtAudio::WINDOWS_ASIO: return "ASIO"; | |||
case RtAudio::WINDOWS_DS: return "DirectSound"; | |||
case RtAudio::RTAUDIO_DUMMY: return "Dummy"; | |||
case DRIVER_BRIDGE: return "VCV Bridge"; | |||
default: return "Unknown"; | |||
} | |||
} | |||
int AudioIO::getDriver() { | |||
if (!stream) | |||
return RtAudio::UNSPECIFIED; | |||
return stream->getCurrentApi(); | |||
} | |||
void AudioIO::setDriver(int driver) { | |||
// Close driver | |||
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() { | |||
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) { | |||
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) { | |||
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) { | |||
@@ -97,17 +118,18 @@ void AudioIO::openStream() { | |||
// Close device but remember the current device number | |||
int device = this->device; | |||
closeStream(); | |||
if (!stream) | |||
if (device < 0) | |||
return; | |||
// Open new device | |||
if (device >= 0) { | |||
if (rtAudio) { | |||
// Open new device | |||
RtAudio::DeviceInfo deviceInfo; | |||
try { | |||
deviceInfo = stream->getDeviceInfo(device); | |||
deviceInfo = rtAudio->getDeviceInfo(device); | |||
} | |||
catch (RtAudioError &e) { | |||
warn("Failed to query audio device: %s", e.what()); | |||
warn("Failed to query RtAudio device: %s", e.what()); | |||
return; | |||
} | |||
@@ -115,7 +137,7 @@ void AudioIO::openStream() { | |||
numInputs = mini(deviceInfo.inputChannels, maxInputs); | |||
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; | |||
} | |||
@@ -138,56 +160,56 @@ void AudioIO::openStream() { | |||
} | |||
try { | |||
debug("Opening audio stream %d", device); | |||
stream->openStream( | |||
debug("Opening audio RtAudio device %d", device); | |||
rtAudio->openStream( | |||
numOutputs == 0 ? NULL : &outParameters, | |||
numInputs == 0 ? NULL : &inParameters, | |||
RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL); | |||
} | |||
catch (RtAudioError &e) { | |||
warn("Failed to open audio stream: %s", e.what()); | |||
warn("Failed to open RtAudio stream: %s", e.what()); | |||
return; | |||
} | |||
try { | |||
debug("Starting audio stream %d", device); | |||
stream->startStream(); | |||
debug("Starting RtAudio stream %d", device); | |||
rtAudio->startStream(); | |||
} | |||
catch (RtAudioError &e) { | |||
warn("Failed to start audio stream: %s", e.what()); | |||
warn("Failed to start RtAudio stream: %s", e.what()); | |||
return; | |||
} | |||
// Update sample rate because this may have changed | |||
this->sampleRate = stream->getStreamSampleRate(); | |||
this->sampleRate = rtAudio->getStreamSampleRate(); | |||
this->device = device; | |||
onOpenStream(); | |||
} | |||
} | |||
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 { | |||
stream->stopStream(); | |||
rtAudio->stopStream(); | |||
} | |||
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 { | |||
stream->closeStream(); | |||
rtAudio->closeStream(); | |||
} | |||
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; | |||
numOutputs = 0; | |||
numInputs = 0; | |||
@@ -195,18 +217,59 @@ void AudioIO::closeStream() { | |||
} | |||
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(); | |||
} | |||
@@ -114,40 +114,13 @@ struct AudioInterface : Module { | |||
json_t *toJson() override { | |||
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; | |||
} | |||
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 { | |||
@@ -236,7 +209,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() { | |||
{ | |||
Label *label = new Label(); | |||
label->box.pos = Vec(margin.x, yPos); | |||
label->text = "Outputs"; | |||
label->text = "Outputs (DACs)"; | |||
addChild(label); | |||
yPos += labelHeight + margin.y; | |||
} | |||
@@ -270,7 +243,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() { | |||
{ | |||
Label *label = new Label(); | |||
label->box.pos = Vec(margin.x, yPos); | |||
label->text = "Inputs"; | |||
label->text = "Inputs (ADCs)"; | |||
addChild(label); | |||
yPos += labelHeight + margin.y; | |||
} | |||
@@ -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))); | |||
} |
@@ -1,4 +1,3 @@ | |||
#if 0 | |||
#include <list> | |||
#include <algorithm> | |||
#include "core.hpp" | |||
@@ -12,7 +11,7 @@ | |||
*/ | |||
struct MidiValue { | |||
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!) | |||
}; | |||
@@ -38,6 +37,7 @@ struct MIDIToCVInterface : Module { | |||
NUM_LIGHTS | |||
}; | |||
MidiInputQueue midiInput; | |||
std::list<int> notes; | |||
bool pedal = false; | |||
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) { | |||
pitchWheel.val = 64; | |||
pitchWheel.tSmooth.set(0, 0); | |||
// pitchWheel.tSmooth.set(0, 0); | |||
} | |||
~MIDIToCVInterface() { | |||
@@ -67,22 +67,22 @@ struct MIDIToCVInterface : Module { | |||
json_t *toJson() override { | |||
json_t *rootJ = json_object(); | |||
addBaseJson(rootJ); | |||
// addBaseJson(rootJ); | |||
return rootJ; | |||
} | |||
void fromJson(json_t *rootJ) override { | |||
baseFromJson(rootJ); | |||
// baseFromJson(rootJ); | |||
} | |||
void onReset() override { | |||
resetMidi(); | |||
// resetMidi(); | |||
} | |||
void resetMidi() override; | |||
// void resetMidi() override; | |||
}; | |||
/* | |||
void MIDIToCVInterface::resetMidi() { | |||
mod.val = 0; | |||
mod.tSmooth.set(0, 0); | |||
@@ -94,8 +94,10 @@ void MIDIToCVInterface::resetMidi() { | |||
gate = false; | |||
notes.clear(); | |||
} | |||
*/ | |||
void MIDIToCVInterface::step() { | |||
/* | |||
if (isPortOpen()) { | |||
std::vector<unsigned char> message; | |||
@@ -132,11 +134,8 @@ void MIDIToCVInterface::step() { | |||
} | |||
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; | |||
*/ | |||
} | |||
void MIDIToCVInterface::pressNote(int note) { | |||
@@ -171,6 +170,7 @@ void MIDIToCVInterface::releaseNote(int note) { | |||
} | |||
void MIDIToCVInterface::processMidi(std::vector<unsigned char> msg) { | |||
/* | |||
int channel = msg[0] & 0xf; | |||
int status = (msg[0] >> 4) & 0xf; | |||
int data1 = msg[1]; | |||
@@ -220,6 +220,7 @@ void MIDIToCVInterface::processMidi(std::vector<unsigned char> msg) { | |||
afterTouch.changed = true; | |||
break; | |||
} | |||
*/ | |||
} | |||
@@ -254,35 +255,6 @@ MidiToCVWidget::MidiToCVWidget() { | |||
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)); | |||
{ | |||
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"}; | |||
@@ -296,5 +268,8 @@ MidiToCVWidget::MidiToCVWidget() { | |||
yPos += yGap + margin; | |||
} | |||
MidiWidget *midiWidget = construct<MIDI_DIN_MidiWidget>(); | |||
midiWidget->midiIO = &module->midiInput; | |||
addChild(midiWidget); | |||
} | |||
#endif |
@@ -9,13 +9,12 @@ void init(rack::Plugin *p) { | |||
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<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<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<NotesWidget>("Core", "Notes", "Notes", BLANK_TAG)); | |||
} |
@@ -5,6 +5,7 @@ | |||
#include "settings.hpp" | |||
#include "asset.hpp" | |||
#include <unistd.h> | |||
#include "../ext/osdialog/osdialog.h" | |||
using namespace rack; | |||
@@ -40,8 +41,12 @@ int main(int argc, char* argv[]) { | |||
skipAutosaveOnLaunch = true; | |||
settingsSave(assetLocal("settings.json")); | |||
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")); | |||
} | |||
engineStart(); | |||
guiRun(); | |||
@@ -4,38 +4,101 @@ | |||
namespace rack { | |||
//////////////////// | |||
// MidiIO | |||
//////////////////// | |||
int MidiIO::getPortCount() { | |||
return midi->getPortCount(); | |||
return rtMidi->getPortCount(); | |||
} | |||
std::string MidiIO::getPortName(int port) { | |||
return midi->getPortName(port); | |||
if (port < 0) | |||
return ""; | |||
return rtMidi->getPortName(port); | |||
} | |||
void MidiIO::openPort(int port) { | |||
midi->closePort(); | |||
rtMidi->closePort(); | |||
if (port >= 0) { | |||
midi->openPort(port); | |||
rtMidi->openPort(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() { | |||
midi = new RtMidiIn(); | |||
RtMidiIn *rtMidiIn = new RtMidiIn(); | |||
rtMidi = rtMidiIn; | |||
rtMidiIn->setCallback(midiInputCallback, this); | |||
} | |||
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() { | |||
midi = new RtMidiOut(); | |||
rtMidi = new RtMidiOut(); | |||
} | |||
MidiOutput::~MidiOutput() { | |||
delete dynamic_cast<RtMidiOut*>(midi); | |||
delete dynamic_cast<RtMidiOut*>(rtMidi); | |||
} | |||
@@ -137,7 +137,7 @@ void settingsSave(std::string filename) { | |||
if (!file) | |||
return; | |||
json_dumpf(rootJ, file, JSON_INDENT(2)); | |||
json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9)); | |||
json_decref(rootJ); | |||
fclose(file); | |||
} | |||