diff --git a/include/bridgeprotocol.hpp b/include/bridgeprotocol.hpp
index 1b5e3c46..894e0988 100644
--- a/include/bridgeprotocol.hpp
+++ b/include/bridgeprotocol.hpp
@@ -50,10 +50,6 @@ enum BridgeCommand {
- float output[BRIDGE_OUTPUTS * frames]
*/
AUDIO_PROCESS_COMMAND,
- /** Resumes the audio buffer, forcing Rack to wait on an audio buffer */
- AUDIO_ACTIVATE,
- /** Pauses the audio buffer, allowing Rack to not wait on an audio buffer */
- AUDIO_DEACTIVATE,
NUM_COMMANDS
};
diff --git a/src/Core/AudioInterface.cpp b/src/Core/AudioInterface.cpp
index c45cfc97..539da76f 100644
--- a/src/Core/AudioInterface.cpp
+++ b/src/Core/AudioInterface.cpp
@@ -31,6 +31,7 @@ struct AudioInterfaceIO : AudioIO {
DoubleRingBuffer, (1<<15)> inputBuffer;
// Audio thread consumes, engine thread produces
DoubleRingBuffer, (1<<15)> outputBuffer;
+ bool active = false;
~AudioInterfaceIO() {
// Close stream here before destructing AudioInterfaceIO, so the mutexes are still valid when waiting to close.
@@ -38,6 +39,13 @@ struct AudioInterfaceIO : AudioIO {
}
void processStream(const float *input, float *output, int frames) override {
+ // Reactivate idle stream
+ if (!active) {
+ active = true;
+ inputBuffer.clear();
+ outputBuffer.clear();
+ }
+
if (numInputs > 0) {
// 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 < frames; i++) {
@@ -144,35 +152,50 @@ struct AudioInterface : Module {
void AudioInterface::step() {
- Frame 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) {
- // Convert inputs if needed
- if (inputBuffer.empty()) {
+ // Inputs: audio engine -> rack engine
+ if (audioIO.active && audioIO.numInputs > 0) {
+ // Wait until inputs are present
+ // Give up after a timeout in case the audio device is being unresponsive.
+ std::unique_lock lock(audioIO.engineMutex);
+ auto cond = [&] {
+ return (!audioIO.inputBuffer.empty());
+ };
+ auto timeout = std::chrono::milliseconds(200);
+ if (audioIO.engineCv.wait_for(lock, timeout, cond)) {
+ // Convert inputs
int inLen = audioIO.inputBuffer.size();
int outLen = inputBuffer.capacity();
inputSrc.process(audioIO.inputBuffer.startData(), &inLen, inputBuffer.endData(), &outLen);
audioIO.inputBuffer.startIncr(inLen);
inputBuffer.endIncr(outLen);
}
+ else {
+ // Give up on pulling input
+ audioIO.active = false;
+ debug("Audio Interface underflow");
+ }
}
// Take input from buffer
+ Frame inputFrame;
if (!inputBuffer.empty()) {
inputFrame = inputBuffer.shift();
}
+ else {
+ memset(&inputFrame, 0, sizeof(inputFrame));
+ }
for (int i = 0; i < INPUTS; i++) {
outputs[AUDIO_OUTPUT + i].value = 10.f * inputFrame.samples[i];
}
- if (audioIO.numOutputs > 0) {
+ // Outputs: rack engine -> audio engine
+ if (audioIO.active && audioIO.numOutputs > 0) {
// Get and push output SRC frame
if (!outputBuffer.full()) {
Frame outputFrame;
@@ -183,13 +206,13 @@ void AudioInterface::step() {
}
if (outputBuffer.full()) {
- // Wait until outputs are needed.
+ // Wait until enough outputs are consumed
// Give up after a timeout in case the audio device is being unresponsive.
std::unique_lock lock(audioIO.engineMutex);
auto cond = [&] {
return (audioIO.outputBuffer.size() < (size_t) audioIO.blockSize);
};
- auto timeout = std::chrono::milliseconds(100);
+ auto timeout = std::chrono::milliseconds(200);
if (audioIO.engineCv.wait_for(lock, timeout, cond)) {
// Push converted output
int inLen = outputBuffer.size();
@@ -200,6 +223,8 @@ void AudioInterface::step() {
}
else {
// Give up on pushing output
+ audioIO.active = false;
+ outputBuffer.clear();
debug("Audio Interface underflow");
}
}
@@ -210,9 +235,9 @@ void AudioInterface::step() {
// Turn on light if at least one port is enabled in the nearby pair
for (int i = 0; i < INPUTS / 2; i++)
- lights[INPUT_LIGHT + i].value = (audioIO.numOutputs >= 2*i+1);
+ lights[INPUT_LIGHT + i].value = (audioIO.active && audioIO.numOutputs >= 2*i+1);
for (int i = 0; i < OUTPUTS / 2; i++)
- lights[OUTPUT_LIGHT + i].value = (audioIO.numInputs >= 2*i+1);
+ lights[OUTPUT_LIGHT + i].value = (audioIO.active && audioIO.numInputs >= 2*i+1);
}
diff --git a/src/audio.cpp b/src/audio.cpp
index 8fc81250..e26e6287 100644
--- a/src/audio.cpp
+++ b/src/audio.cpp
@@ -279,7 +279,7 @@ void AudioIO::openStream() {
onOpenStream();
}
else if (driver == BRIDGE_DRIVER) {
- setChannels(0, 0);
+ setChannels(BRIDGE_OUTPUTS, BRIDGE_INPUTS);
bridgeAudioSubscribe(device, this);
}
}
diff --git a/src/bridge.cpp b/src/bridge.cpp
index 4fe700af..3f5420e3 100644
--- a/src/bridge.cpp
+++ b/src/bridge.cpp
@@ -34,7 +34,6 @@ struct BridgeClientConnection {
int port = -1;
int sampleRate = 0;
- bool audioActive = false;
~BridgeClientConnection() {
setPort(-1);
@@ -177,24 +176,12 @@ struct BridgeClientConnection {
send(&output, BRIDGE_OUTPUTS * frames * sizeof(float));
// flush();
} break;
-
- case AUDIO_ACTIVATE: {
- audioActive = true;
- refreshAudio();
- } break;
-
- case AUDIO_DEACTIVATE: {
- audioActive = false;
- refreshAudio();
- } break;
}
}
void setPort(int port) {
// Unbind from existing port
if (this->port >= 0 && connections[this->port] == this) {
- if (audioListeners[this->port])
- audioListeners[this->port]->setChannels(0, 0);
connections[this->port] = NULL;
}
@@ -238,10 +225,6 @@ struct BridgeClientConnection {
return;
if (!audioListeners[port])
return;
- if (audioActive)
- audioListeners[port]->setChannels(BRIDGE_OUTPUTS, BRIDGE_INPUTS);
- else
- audioListeners[port]->setChannels(0, 0);
audioListeners[port]->setSampleRate(sampleRate);
}
};
@@ -405,7 +388,6 @@ void bridgeAudioUnsubscribe(int port, AudioIO *audio) {
if (audioListeners[port] != audio)
return;
audioListeners[port] = NULL;
- audio->setChannels(0, 0);
}