From 6de4205cb247673f0973866856ced371fd9538d0 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Thu, 15 Mar 2018 04:53:26 -0400 Subject: [PATCH] Add audio activation/deactivation to Bridge --- include/audio.hpp | 5 +- include/bridge.hpp | 3 +- src/Core/AudioInterface.cpp | 37 ++++---- src/audio.cpp | 33 +++---- src/bridge.cpp | 167 +++++++++++++++++++++++------------- 5 files changed, 143 insertions(+), 102 deletions(-) diff --git a/include/audio.hpp b/include/audio.hpp index 5a59cd2e..38da910c 100644 --- a/include/audio.hpp +++ b/include/audio.hpp @@ -43,17 +43,18 @@ struct AudioIO { void setSampleRate(int sampleRate); void setBlockSize(int blockSize); + void setChannels(int numOutputs, int numInputs); + /** Must close the stream before opening */ void openStream(); void closeStream(); - /** Returns whether the audio stream is open and running */ - bool isActive(); std::vector getSampleRates(); virtual void processStream(const float *input, float *output, int frames) {} virtual void onCloseStream() {} virtual void onOpenStream() {} + virtual void onChannelsChange() {} json_t *toJson(); void fromJson(json_t *rootJ); }; diff --git a/include/bridge.hpp b/include/bridge.hpp index eedca7ab..62980056 100644 --- a/include/bridge.hpp +++ b/include/bridge.hpp @@ -5,14 +5,13 @@ namespace rack { -static const int BRIDGE_CHANNELS = 16; +static const int BRIDGE_NUM_PORTS = 16; void bridgeInit(); void bridgeDestroy(); void bridgeAudioSubscribe(int channel, AudioIO *audio); void bridgeAudioUnsubscribe(int channel, AudioIO *audio); -bool bridgeAudioIsActive(int channel, AudioIO *audio); } // namespace rack diff --git a/src/Core/AudioInterface.cpp b/src/Core/AudioInterface.cpp index 8e6350bd..6edf373f 100644 --- a/src/Core/AudioInterface.cpp +++ b/src/Core/AudioInterface.cpp @@ -43,10 +43,10 @@ struct AudioInterfaceIO : AudioIO { for (int i = 0; i < frames; i++) { if (inputBuffer.full()) break; - Frame f; - memset(&f, 0, sizeof(f)); - memcpy(&f, &input[numInputs * i], numInputs * sizeof(float)); - inputBuffer.push(f); + Frame inputFrame; + memset(&inputFrame, 0, sizeof(inputFrame)); + memcpy(&inputFrame, &input[numInputs * i], numInputs * sizeof(float)); + inputBuffer.push(inputFrame); } } @@ -71,7 +71,7 @@ struct AudioInterfaceIO : AudioIO { } // Notify engine when finished processing - engineCv.notify_all(); + engineCv.notify_one(); } void onCloseStream() override { @@ -127,12 +127,6 @@ struct AudioInterface : Module { } void onSampleRateChange() override { - // for (int i = 0; i < INPUTS; i++) { - // inputSrc[i].setRates(audioIO.sampleRate, engineGetSampleRate()); - // } - // for (int i = 0; i < OUTPUTS; i++) { - // outputSrc[i].setRates(engineGetSampleRate(), audioIO.sampleRate); - // } inputSrc.setRates(audioIO.sampleRate, engineGetSampleRate()); outputSrc.setRates(engineGetSampleRate(), audioIO.sampleRate); } @@ -154,6 +148,7 @@ void AudioInterface::step() { } if (audioIO.numInputs > 0) { + // Convert inputs if needed if (inputBuffer.empty()) { int inLen = audioIO.inputBuffer.size(); int outLen = inputBuffer.capacity(); @@ -163,25 +158,27 @@ void AudioInterface::step() { } } + // Take input from buffer if (!inputBuffer.empty()) { inputFrame = inputBuffer.shift(); } for (int i = 0; i < INPUTS; i++) { - outputs[AUDIO_OUTPUT + i].value = 10.0 * inputFrame.samples[i]; + outputs[AUDIO_OUTPUT + i].value = 10.f * inputFrame.samples[i]; } if (audioIO.numOutputs > 0) { // Get and push output SRC frame if (!outputBuffer.full()) { - Frame f; - for (int i = 0; i < audioIO.numOutputs; i++) { - f.samples[i] = inputs[AUDIO_INPUT + i].value / 10.0; + Frame outputFrame; + for (int i = 0; i < OUTPUTS; i++) { + outputFrame.samples[i] = inputs[AUDIO_INPUT + i].value / 10.f; } - outputBuffer.push(f); + outputBuffer.push(outputFrame); } if (outputBuffer.full()) { - // Wait until outputs are needed + // Wait until outputs are needed. + // 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); @@ -200,14 +197,16 @@ void AudioInterface::step() { debug("Audio Interface underflow"); } } + + // Notify audio thread that an output is potentially ready + audioIO.audioCv.notify_one(); } + // 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); for (int i = 0; i < OUTPUTS / 2; i++) lights[OUTPUT_LIGHT + i].value = (audioIO.numInputs >= 2*i+1); - - audioIO.audioCv.notify_all(); } diff --git a/src/audio.cpp b/src/audio.cpp index c68b0e8f..c7df42d9 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -46,8 +46,10 @@ std::string AudioIO::getDriverName(int driver) { } void AudioIO::setDriver(int driver) { + // Close device setDevice(-1, 0); + // Close driver if (rtAudio) { delete rtAudio; rtAudio = NULL; @@ -69,7 +71,7 @@ int AudioIO::getDeviceCount() { return rtAudio->getDeviceCount(); } if (driver == BRIDGE_DRIVER) { - return BRIDGE_CHANNELS; + return BRIDGE_NUM_PORTS; } return 0; } @@ -90,13 +92,11 @@ bool AudioIO::getDeviceInfo(int device, RtAudio::DeviceInfo *deviceInfo) { } catch (RtAudioError &e) { warn("Failed to query RtAudio device: %s", e.what()); - return false; } } } - else { - return false; - } + + return false; } int AudioIO::getDeviceChannels(int device) { @@ -148,7 +148,7 @@ std::string AudioIO::getDeviceDetail(int device, int offset) { } } if (driver == BRIDGE_DRIVER) { - return stringf("Channel %d", device + 1); + return stringf("Port %d", device + 1); } return ""; } @@ -172,6 +172,12 @@ void AudioIO::setBlockSize(int blockSize) { openStream(); } +void AudioIO::setChannels(int numOutputs, int numInputs) { + this->numOutputs = numOutputs; + this->numInputs = numInputs; + onChannelsChange(); +} + static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData) { AudioIO *audioIO = (AudioIO*) userData; @@ -252,8 +258,8 @@ void AudioIO::openStream() { onOpenStream(); } if (driver == BRIDGE_DRIVER) { - numOutputs = 2; - numInputs = 2; + numOutputs = 0; + numInputs = 0; // TEMP sampleRate = 44100; blockSize = 256; @@ -293,17 +299,6 @@ void AudioIO::closeStream() { onCloseStream(); } -bool AudioIO::isActive() { - if (rtAudio) { - return rtAudio->isStreamRunning(); - } - if (driver == BRIDGE_DRIVER) { - bridgeAudioIsActive(device, this); - } - return false; -} - - std::vector AudioIO::getSampleRates() { if (rtAudio) { try { diff --git a/src/bridge.cpp b/src/bridge.cpp index 4846d70b..d899f962 100644 --- a/src/bridge.cpp +++ b/src/bridge.cpp @@ -25,11 +25,13 @@ enum BridgeCommand { NO_COMMAND = 0, START_COMMAND, QUIT_COMMAND, - CHANNEL_SET_COMMAND, + PORT_SET_COMMAND, + MIDI_MESSAGE_SEND_COMMAND, AUDIO_SAMPLE_RATE_SET_COMMAND, AUDIO_CHANNELS_SET_COMMAND, AUDIO_BUFFER_SEND_COMMAND, - MIDI_MESSAGE_SEND_COMMAND, + AUDIO_ACTIVATE, + AUDIO_DEACTIVATE, NUM_COMMANDS }; @@ -37,7 +39,9 @@ enum BridgeCommand { static const int RECV_BUFFER_SIZE = (1<<13); static const int RECV_QUEUE_SIZE = (1<<17); -static AudioIO *audioListeners[BRIDGE_CHANNELS]; +struct BridgeClientConnection; +static BridgeClientConnection *connections[BRIDGE_NUM_PORTS] = {}; +static AudioIO *audioListeners[BRIDGE_NUM_PORTS] = {}; static std::thread serverThread; static bool serverQuit; @@ -47,10 +51,11 @@ struct BridgeClientConnection { RingBuffer recvQueue; BridgeCommand currentCommand = START_COMMAND; bool closeRequested = false; - int channel = -1; + int port = -1; int sampleRate = -1; int audioChannels = 0; int audioBufferLength = -1; + bool audioActive = false; void send(const uint8_t *buffer, int length) { if (length <= 0) @@ -80,6 +85,35 @@ struct BridgeClientConnection { return x; } + void run() { + info("Bridge client connected"); + + while (!closeRequested) { + uint8_t buffer[RECV_BUFFER_SIZE]; +#ifdef ARCH_LIN + int recvFlags = MSG_NOSIGNAL; +#else + int recvFlags = 0; +#endif + ssize_t received = ::recv(client, (char*) buffer, sizeof(buffer), recvFlags); + if (received <= 0) + break; + + // Make sure we can fill the buffer + if (recvQueue.capacity() < (size_t) received) { + // If we can't accept it, future messages will be incomplete + break; + } + + recvQueue.pushBuffer(buffer, received); + + // Loop the state machine until it returns false + while (step()) {} + } + + info("Bridge client closed"); + } + /** Steps the state machine Returns true if step() should be called again */ @@ -115,10 +149,21 @@ struct BridgeClientConnection { debug("Quitting!"); } break; - case CHANNEL_SET_COMMAND: { + case PORT_SET_COMMAND: { if (recvQueue.size() >= 1) { - channel = shift(); - debug("Set channel %d", channel); + int port = shift(); + setPort(port); + debug("Set port %d", port); + currentCommand = NO_COMMAND; + return true; + } + } break; + + case MIDI_MESSAGE_SEND_COMMAND: { + if (recvQueue.size() >= 3) { + uint8_t midiBuffer[3]; + recvQueue.shiftBuffer(midiBuffer, 3); + debug("MIDI: %02x %02x %02x", midiBuffer[0], midiBuffer[1], midiBuffer[2]); currentCommand = NO_COMMAND; return true; } @@ -136,7 +181,7 @@ struct BridgeClientConnection { case AUDIO_CHANNELS_SET_COMMAND: { if (recvQueue.size() >= 1) { audioChannels = shift(); - debug("Set audio channels %d", channel); + debug("Set audio channels %d", audioChannels); currentCommand = NO_COMMAND; return true; } @@ -177,14 +222,16 @@ struct BridgeClientConnection { } } break; - case MIDI_MESSAGE_SEND_COMMAND: { - if (recvQueue.size() >= 3) { - uint8_t midiBuffer[3]; - recvQueue.shiftBuffer(midiBuffer, 3); - debug("MIDI: %02x %02x %02x", midiBuffer[0], midiBuffer[1], midiBuffer[2]); - currentCommand = NO_COMMAND; - return true; - } + case AUDIO_ACTIVATE: { + audioActive = true; + refreshAudioActive(); + return true; + } break; + + case AUDIO_DEACTIVATE: { + audioActive = false; + refreshAudioActive(); + return true; } break; default: { @@ -192,44 +239,47 @@ struct BridgeClientConnection { closeRequested = true; } break; } + + // Stop looping the state machine return false; } + void setPort(int newPort) { + if (!(0 <= newPort && newPort < BRIDGE_NUM_PORTS)) + return; + // Unbind from existing port + if (connections[port] == this) { + if (audioListeners[port]) + audioListeners[port]->setChannels(0, 0); + connections[port] = NULL; + } + + port = newPort; + // Bind to new port + if (!connections[port]) { + connections[port] = this; + refreshAudioActive(); + } + else { + port = -1; + } + } + void processStream(const float *input, float *output, int frames) { - if (!(0 <= channel && channel < BRIDGE_CHANNELS)) + if (!(0 <= port && port < BRIDGE_NUM_PORTS)) return; - if (!audioListeners[channel]) + if (!audioListeners[port]) return; - audioListeners[channel]->processStream(input, output, frames); + audioListeners[port]->processStream(input, output, frames); } - void run() { - info("Bridge client connected"); - - while (!closeRequested) { - uint8_t buffer[RECV_BUFFER_SIZE]; -#ifdef ARCH_LIN - int recvFlags = MSG_NOSIGNAL; -#else - int recvFlags = 0; -#endif - ssize_t received = ::recv(client, (char*) buffer, sizeof(buffer), recvFlags); - if (received <= 0) - break; - - // Make sure we can fill the buffer - if (recvQueue.capacity() < (size_t) received) { - // If we can't accept it, future messages will be incomplete - break; - } - - recvQueue.pushBuffer(buffer, received); - - // Loop the state machine until it returns false - while (step()) {} - } - - info("Bridge client closed"); + void refreshAudioActive() { + if (!audioListeners[port]) + return; + if (audioActive) + audioListeners[port]->setChannels(2, 2); + else + audioListeners[port]->setChannels(0, 0); } }; @@ -373,26 +423,23 @@ void bridgeDestroy() { serverThread.join(); } -void bridgeAudioSubscribe(int channel, AudioIO *audio) { - if (!(0 <= channel && channel < BRIDGE_CHANNELS)) +void bridgeAudioSubscribe(int port, AudioIO *audio) { + if (!(0 <= port && port < BRIDGE_NUM_PORTS)) return; - if (audioListeners[channel]) + if (audioListeners[port]) return; - audioListeners[channel] = audio; + audioListeners[port] = audio; + if (connections[port]) + connections[port]->refreshAudioActive(); } -void bridgeAudioUnsubscribe(int channel, AudioIO *audio) { - if (!(0 <= channel && channel < BRIDGE_CHANNELS)) +void bridgeAudioUnsubscribe(int port, AudioIO *audio) { + if (!(0 <= port && port < BRIDGE_NUM_PORTS)) return; - if (audioListeners[channel] != audio) + if (audioListeners[port] != audio) return; - audioListeners[channel] = NULL; -} - -bool bridgeAudioIsActive(int channel, AudioIO *audio) { - if (!(0 <= channel && channel < BRIDGE_CHANNELS)) - return false; - return (audioListeners[channel] == audio); + audioListeners[port] = NULL; + audio->setChannels(0, 0); }