From 5c8cadbbf9e1b9655394d521b6af7f0aa13ab74a Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 19 Mar 2018 06:54:40 -0400 Subject: [PATCH] Add automatic audio activation/deactivation when device pauses for >200ms --- include/bridgeprotocol.hpp | 4 ---- src/Core/AudioInterface.cpp | 47 ++++++++++++++++++++++++++++--------- src/audio.cpp | 2 +- src/bridge.cpp | 18 -------------- 4 files changed, 37 insertions(+), 34 deletions(-) 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); }