Browse Source

Add automatic audio activation/deactivation when device pauses for >200ms

tags/v0.6.0
Andrew Belt 7 years ago
parent
commit
5c8cadbbf9
4 changed files with 37 additions and 34 deletions
  1. +0
    -4
      include/bridgeprotocol.hpp
  2. +36
    -11
      src/Core/AudioInterface.cpp
  3. +1
    -1
      src/audio.cpp
  4. +0
    -18
      src/bridge.cpp

+ 0
- 4
include/bridgeprotocol.hpp View File

@@ -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
};



+ 36
- 11
src/Core/AudioInterface.cpp View File

@@ -31,6 +31,7 @@ struct AudioInterfaceIO : AudioIO {
DoubleRingBuffer<Frame<INPUTS>, (1<<15)> inputBuffer;
// Audio thread consumes, engine thread produces
DoubleRingBuffer<Frame<OUTPUTS>, (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<INPUTS> 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<std::mutex> 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<INPUTS> 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<OUTPUTS> 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<std::mutex> 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);
}




+ 1
- 1
src/audio.cpp View File

@@ -279,7 +279,7 @@ void AudioIO::openStream() {
onOpenStream();
}
else if (driver == BRIDGE_DRIVER) {
setChannels(0, 0);
setChannels(BRIDGE_OUTPUTS, BRIDGE_INPUTS);
bridgeAudioSubscribe(device, this);
}
}


+ 0
- 18
src/bridge.cpp View File

@@ -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);
}




Loading…
Cancel
Save