Browse Source

Added active light indicators to AudioInterface and MIDItoCVInterface

tags/v0.6.0
Andrew Belt 6 years ago
parent
commit
63cd488674
8 changed files with 114 additions and 44 deletions
  1. +2
    -0
      include/audio.hpp
  2. +2
    -1
      include/engine.hpp
  3. +6
    -4
      include/midi.hpp
  4. +17
    -9
      src/app/MidiWidget.cpp
  5. +8
    -0
      src/audio.cpp
  6. +38
    -10
      src/core/AudioInterface.cpp
  7. +8
    -0
      src/core/MidiToCV.cpp
  8. +33
    -20
      src/midi.cpp

+ 2
- 0
include/audio.hpp View File

@@ -36,6 +36,8 @@ struct AudioIO {
std::string getDeviceDetail(int device); std::string getDeviceDetail(int device);
void openStream(); void openStream();
void closeStream(); void closeStream();
/** Returns whether the audio stream is open and running */
bool isActive();


std::vector<int> listSampleRates(); std::vector<int> listSampleRates();




+ 2
- 1
include/engine.hpp View File

@@ -63,8 +63,9 @@ struct Module {


/** Advances the module by 1 audio frame with duration 1.0 / gSampleRate */ /** Advances the module by 1 audio frame with duration 1.0 / gSampleRate */
virtual void step() {} virtual void step() {}
virtual void onSampleRateChange() {}


/** Called when the engine sample rate is changed */
virtual void onSampleRateChange() {}
/** Called when module is created by the Add Module popup, cloning, or when loading a patch or autosave */ /** Called when module is created by the Add Module popup, cloning, or when loading a patch or autosave */
virtual void onCreate() {} virtual void onCreate() {}
/** Called when user explicitly deletes the module, not when Rack is closed or a new patch is loaded */ /** Called when user explicitly deletes the module, not when Rack is closed or a new patch is loaded */


+ 6
- 4
include/midi.hpp View File

@@ -22,7 +22,7 @@ struct MidiMessage {




struct MidiIO { struct MidiIO {
int port = -1;
int device = -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.
Set to -1 to allow all MIDI channels (for input). Set to -1 to allow all MIDI channels (for input).
@@ -32,9 +32,11 @@ struct MidiIO {
RtMidi *rtMidi = NULL; RtMidi *rtMidi = NULL;


virtual ~MidiIO() {} virtual ~MidiIO() {}
int getPortCount();
std::string getPortName(int port);
void openPort(int port);
int getDeviceCount();
std::string getDeviceName(int device);
void openDevice(int device);
/** Returns whether the audio stream is open and running */
bool isActive();
json_t *toJson(); json_t *toJson();
void fromJson(json_t *rootJ); void fromJson(json_t *rootJ);
}; };


+ 17
- 9
src/app/MidiWidget.cpp View File

@@ -5,11 +5,11 @@
namespace rack { namespace rack {




struct MidiPortItem : MenuItem {
struct MidiDeviceItem : MenuItem {
MidiIO *midiIO; MidiIO *midiIO;
int port;
int device;
void onAction(EventAction &e) override { void onAction(EventAction &e) override {
midiIO->openPort(port);
midiIO->openDevice(device);
} }
}; };


@@ -31,13 +31,21 @@ void MidiWidget::onMouseDown(EventMouseDown &e) {


Menu *menu = gScene->createMenu(); Menu *menu = gScene->createMenu();


menu->addChild(construct<MenuLabel>(&MenuLabel::text, "MIDI port"));
for (int port = 0; port < midiIO->getPortCount(); port++) {
MidiPortItem *item = new MidiPortItem();
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "MIDI device"));
{
MidiDeviceItem *item = new MidiDeviceItem();
item->midiIO = midiIO; item->midiIO = midiIO;
item->port = port;
item->text = midiIO->getPortName(port);
item->rightText = CHECKMARK(item->port == midiIO->port);
item->device = -1;
item->text = "No device";
item->rightText = CHECKMARK(item->device == midiIO->device);
menu->addChild(item);
}
for (int device = 0; device < midiIO->getDeviceCount(); device++) {
MidiDeviceItem *item = new MidiDeviceItem();
item->midiIO = midiIO;
item->device = device;
item->text = midiIO->getDeviceName(device);
item->rightText = CHECKMARK(item->device == midiIO->device);
menu->addChild(item); menu->addChild(item);
} }
menu->addChild(construct<MenuEntry>()); menu->addChild(construct<MenuEntry>());


+ 8
- 0
src/audio.cpp View File

@@ -216,6 +216,14 @@ void AudioIO::closeStream() {
onCloseStream(); onCloseStream();
} }


bool AudioIO::isActive() {
if (rtAudio)
return rtAudio->isStreamRunning();
// TODO Bridge
return false;
}


std::vector<int> AudioIO::listSampleRates() { std::vector<int> AudioIO::listSampleRates() {
if (rtAudio) { if (rtAudio) {
try { try {


+ 38
- 10
src/core/AudioInterface.cpp View File

@@ -18,7 +18,7 @@
#define MAX_OUTPUTS 8 #define MAX_OUTPUTS 8
#define MAX_INPUTS 8 #define MAX_INPUTS 8


static auto audioTimeout = std::chrono::milliseconds(100);
static const auto audioTimeout = std::chrono::milliseconds(100);




using namespace rack; using namespace rack;
@@ -45,7 +45,7 @@ struct AudioInterfaceIO : AudioIO {


void processStream(const float *input, float *output, int length) override { void processStream(const float *input, float *output, int length) override {
if (numInputs > 0) { if (numInputs > 0) {
// TODO Do we need to wait on the input to be consumed here?
// TODO Do we need to wait on the input to be consumed here? Experimentally, it works fine if we don't.
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
if (inputBuffer.full()) if (inputBuffer.full())
break; break;
@@ -97,8 +97,13 @@ struct AudioInterface : Module {
ENUMS(AUDIO_OUTPUT, MAX_OUTPUTS), ENUMS(AUDIO_OUTPUT, MAX_OUTPUTS),
NUM_OUTPUTS NUM_OUTPUTS
}; };
enum LightIds {
ACTIVE_LIGHT,
NUM_LIGHTS
};


AudioInterfaceIO audioIO; AudioInterfaceIO audioIO;
int lastSampleRate = 0;


SampleRateConverter<MAX_INPUTS> inputSrc; SampleRateConverter<MAX_INPUTS> inputSrc;
SampleRateConverter<MAX_OUTPUTS> outputSrc; SampleRateConverter<MAX_OUTPUTS> outputSrc;
@@ -107,7 +112,8 @@ struct AudioInterface : Module {
DoubleRingBuffer<Frame<MAX_INPUTS>, 16> inputBuffer; DoubleRingBuffer<Frame<MAX_INPUTS>, 16> inputBuffer;
DoubleRingBuffer<Frame<MAX_OUTPUTS>, 16> outputBuffer; DoubleRingBuffer<Frame<MAX_OUTPUTS>, 16> outputBuffer;


AudioInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {
AudioInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
onSampleRateChange();
} }


void step() override; void step() override;
@@ -123,6 +129,17 @@ struct AudioInterface : Module {
audioIO.fromJson(audioJ); audioIO.fromJson(audioJ);
} }


void onSampleRateChange() override {
// for (int i = 0; i < MAX_INPUTS; i++) {
// inputSrc[i].setRates(audioIO.sampleRate, engineGetSampleRate());
// }
// for (int i = 0; i < MAX_OUTPUTS; i++) {
// outputSrc[i].setRates(engineGetSampleRate(), audioIO.sampleRate);
// }
inputSrc.setRates(audioIO.sampleRate, engineGetSampleRate());
outputSrc.setRates(engineGetSampleRate(), audioIO.sampleRate);
}

void onReset() override { void onReset() override {
audioIO.closeStream(); audioIO.closeStream();
} }
@@ -133,9 +150,14 @@ void AudioInterface::step() {
Frame<MAX_INPUTS> inputFrame; Frame<MAX_INPUTS> inputFrame;
memset(&inputFrame, 0, sizeof(inputFrame)); memset(&inputFrame, 0, sizeof(inputFrame));


// Update sample rate if changed by audio driver
if (audioIO.sampleRate != lastSampleRate) {
onSampleRateChange();
lastSampleRate = audioIO.sampleRate;
}

if (audioIO.numInputs > 0) { if (audioIO.numInputs > 0) {
if (inputBuffer.empty()) { if (inputBuffer.empty()) {
inputSrc.setRates(audioIO.sampleRate, engineGetSampleRate());
int inLen = audioIO.inputBuffer.size(); int inLen = audioIO.inputBuffer.size();
int outLen = inputBuffer.capacity(); int outLen = inputBuffer.capacity();
inputSrc.process(audioIO.inputBuffer.startData(), &inLen, inputBuffer.endData(), &outLen); inputSrc.process(audioIO.inputBuffer.startData(), &inLen, inputBuffer.endData(), &outLen);
@@ -169,7 +191,6 @@ void AudioInterface::step() {
}; };
if (audioIO.engineCv.wait_for(lock, audioTimeout, cond)) { if (audioIO.engineCv.wait_for(lock, audioTimeout, cond)) {
// Push converted output // Push converted output
outputSrc.setRates(engineGetSampleRate(), audioIO.sampleRate);
int inLen = outputBuffer.size(); int inLen = outputBuffer.size();
int outLen = audioIO.outputBuffer.capacity(); int outLen = audioIO.outputBuffer.capacity();
outputSrc.process(outputBuffer.startData(), &inLen, audioIO.outputBuffer.endData(), &outLen); outputSrc.process(outputBuffer.startData(), &inLen, audioIO.outputBuffer.endData(), &outLen);
@@ -183,6 +204,9 @@ void AudioInterface::step() {
} }


audioIO.audioCv.notify_all(); audioIO.audioCv.notify_all();

// Lights
lights[ACTIVE_LIGHT].value = audioIO.isActive() ? 1.0 : 0.0;
} }




@@ -196,10 +220,10 @@ AudioInterfaceWidget::AudioInterfaceWidget() {
addChild(panel); 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)));
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)));


Vec margin = Vec(5, 2); Vec margin = Vec(5, 2);
float labelHeight = 15; float labelHeight = 15;
@@ -251,7 +275,8 @@ AudioInterfaceWidget::AudioInterfaceWidget() {
yPos += 5; yPos += 5;
xPos = 10; xPos = 10;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
addOutput(createOutput<PJ3410Port>(Vec(xPos, yPos), module, AudioInterface::AUDIO_OUTPUT + i));
Port *port = createOutput<PJ3410Port>(Vec(xPos, yPos), module, AudioInterface::AUDIO_OUTPUT + i);
addOutput(port);
Label *label = new Label(); Label *label = new Label();
label->box.pos = Vec(xPos + 4, yPos + 28); label->box.pos = Vec(xPos + 4, yPos + 28);
label->text = stringf("%d", i + 1); label->text = stringf("%d", i + 1);
@@ -277,4 +302,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() {
AudioWidget *audioWidget = construct<USB_B_AudioWidget>(); AudioWidget *audioWidget = construct<USB_B_AudioWidget>();
audioWidget->audioIO = &module->audioIO; audioWidget->audioIO = &module->audioIO;
addChild(audioWidget); addChild(audioWidget);

// Lights
addChild(createLight<SmallLight<GreenLight>>(Vec(40, 20), module, AudioInterface::ACTIVE_LIGHT));
} }

+ 8
- 0
src/core/MidiToCV.cpp View File

@@ -15,6 +15,7 @@ struct MidiValue {
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!)
}; };



struct MIDIToCVInterface : Module { struct MIDIToCVInterface : Module {
enum ParamIds { enum ParamIds {
RESET_PARAM, RESET_PARAM,
@@ -33,6 +34,7 @@ struct MIDIToCVInterface : Module {
NUM_OUTPUTS NUM_OUTPUTS
}; };
enum LightIds { enum LightIds {
ACTIVE_LIGHT,
RESET_LIGHT, RESET_LIGHT,
NUM_LIGHTS NUM_LIGHTS
}; };
@@ -136,6 +138,9 @@ void MIDIToCVInterface::step() {


outputs[CHANNEL_AFTERTOUCH_OUTPUT].value = afterTouch.val / 127.0 * 10.0; outputs[CHANNEL_AFTERTOUCH_OUTPUT].value = afterTouch.val / 127.0 * 10.0;
*/ */

// Lights
lights[ACTIVE_LIGHT].value = midiInput.isActive() ? 1.0 : 0.0;
} }


void MIDIToCVInterface::pressNote(int note) { void MIDIToCVInterface::pressNote(int note) {
@@ -272,4 +277,7 @@ MidiToCVWidget::MidiToCVWidget() {
MidiWidget *midiWidget = construct<MIDI_DIN_MidiWidget>(); MidiWidget *midiWidget = construct<MIDI_DIN_MidiWidget>();
midiWidget->midiIO = &module->midiInput; midiWidget->midiIO = &module->midiInput;
addChild(midiWidget); addChild(midiWidget);

// Lights
addChild(createLight<SmallLight<GreenLight>>(Vec(40, 20), module, MIDIToCVInterface::ACTIVE_LIGHT));
} }

+ 33
- 20
src/midi.cpp View File

@@ -8,41 +8,54 @@ namespace rack {
// MidiIO // MidiIO
//////////////////// ////////////////////


int MidiIO::getPortCount() {
return rtMidi->getPortCount();
int MidiIO::getDeviceCount() {
if (rtMidi)
return rtMidi->getPortCount();
return 0;
} }


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


void MidiIO::openPort(int port) {
rtMidi->closePort();
void MidiIO::openDevice(int device) {
if (rtMidi) {
rtMidi->closePort();


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

bool MidiIO::isActive() {
if (rtMidi)
return rtMidi->isPortOpen();
return false;
} }


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


void MidiIO::fromJson(json_t *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);
json_t *deviceNameJ = json_object_get(rootJ, "device");
if (deviceNameJ) {
std::string deviceName = json_string_value(deviceNameJ);
// Search for device with equal name
for (int device = 0; device < getDeviceCount(); device++) {
if (getDeviceName(device) == deviceName) {
openDevice(device);
break; break;
} }
} }


Loading…
Cancel
Save