Browse Source

Add audio activation/deactivation to Bridge

tags/v0.6.0
Andrew Belt 7 years ago
parent
commit
6de4205cb2
5 changed files with 143 additions and 102 deletions
  1. +3
    -2
      include/audio.hpp
  2. +1
    -2
      include/bridge.hpp
  3. +18
    -19
      src/Core/AudioInterface.cpp
  4. +14
    -19
      src/audio.cpp
  5. +107
    -60
      src/bridge.cpp

+ 3
- 2
include/audio.hpp View File

@@ -43,17 +43,18 @@ struct AudioIO {
void setSampleRate(int sampleRate); void setSampleRate(int sampleRate);
void setBlockSize(int blockSize); void setBlockSize(int blockSize);


void setChannels(int numOutputs, int numInputs);

/** Must close the stream before opening */ /** Must close the stream before opening */
void openStream(); void openStream();
void closeStream(); void closeStream();
/** Returns whether the audio stream is open and running */
bool isActive();


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


virtual void processStream(const float *input, float *output, int frames) {} virtual void processStream(const float *input, float *output, int frames) {}
virtual void onCloseStream() {} virtual void onCloseStream() {}
virtual void onOpenStream() {} virtual void onOpenStream() {}
virtual void onChannelsChange() {}
json_t *toJson(); json_t *toJson();
void fromJson(json_t *rootJ); void fromJson(json_t *rootJ);
}; };


+ 1
- 2
include/bridge.hpp View File

@@ -5,14 +5,13 @@
namespace rack { namespace rack {




static const int BRIDGE_CHANNELS = 16;
static const int BRIDGE_NUM_PORTS = 16;




void bridgeInit(); void bridgeInit();
void bridgeDestroy(); void bridgeDestroy();
void bridgeAudioSubscribe(int channel, AudioIO *audio); void bridgeAudioSubscribe(int channel, AudioIO *audio);
void bridgeAudioUnsubscribe(int channel, AudioIO *audio); void bridgeAudioUnsubscribe(int channel, AudioIO *audio);
bool bridgeAudioIsActive(int channel, AudioIO *audio);




} // namespace rack } // namespace rack

+ 18
- 19
src/Core/AudioInterface.cpp View File

@@ -43,10 +43,10 @@ struct AudioInterfaceIO : AudioIO {
for (int i = 0; i < frames; i++) { for (int i = 0; i < frames; i++) {
if (inputBuffer.full()) if (inputBuffer.full())
break; break;
Frame<INPUTS> f;
memset(&f, 0, sizeof(f));
memcpy(&f, &input[numInputs * i], numInputs * sizeof(float));
inputBuffer.push(f);
Frame<INPUTS> 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 // Notify engine when finished processing
engineCv.notify_all();
engineCv.notify_one();
} }


void onCloseStream() override { void onCloseStream() override {
@@ -127,12 +127,6 @@ struct AudioInterface : Module {
} }


void onSampleRateChange() override { 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()); inputSrc.setRates(audioIO.sampleRate, engineGetSampleRate());
outputSrc.setRates(engineGetSampleRate(), audioIO.sampleRate); outputSrc.setRates(engineGetSampleRate(), audioIO.sampleRate);
} }
@@ -154,6 +148,7 @@ void AudioInterface::step() {
} }


if (audioIO.numInputs > 0) { if (audioIO.numInputs > 0) {
// Convert inputs if needed
if (inputBuffer.empty()) { if (inputBuffer.empty()) {
int inLen = audioIO.inputBuffer.size(); int inLen = audioIO.inputBuffer.size();
int outLen = inputBuffer.capacity(); int outLen = inputBuffer.capacity();
@@ -163,25 +158,27 @@ void AudioInterface::step() {
} }
} }


// Take input from buffer
if (!inputBuffer.empty()) { if (!inputBuffer.empty()) {
inputFrame = inputBuffer.shift(); inputFrame = inputBuffer.shift();
} }
for (int i = 0; i < INPUTS; i++) { 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) { if (audioIO.numOutputs > 0) {
// Get and push output SRC frame // Get and push output SRC frame
if (!outputBuffer.full()) { if (!outputBuffer.full()) {
Frame<OUTPUTS> f;
for (int i = 0; i < audioIO.numOutputs; i++) {
f.samples[i] = inputs[AUDIO_INPUT + i].value / 10.0;
Frame<OUTPUTS> 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()) { 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<std::mutex> lock(audioIO.engineMutex); std::unique_lock<std::mutex> lock(audioIO.engineMutex);
auto cond = [&] { auto cond = [&] {
return (audioIO.outputBuffer.size() < (size_t) audioIO.blockSize); return (audioIO.outputBuffer.size() < (size_t) audioIO.blockSize);
@@ -200,14 +197,16 @@ void AudioInterface::step() {
debug("Audio Interface underflow"); 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++) for (int i = 0; i < INPUTS / 2; i++)
lights[INPUT_LIGHT + i].value = (audioIO.numOutputs >= 2*i+1); lights[INPUT_LIGHT + i].value = (audioIO.numOutputs >= 2*i+1);
for (int i = 0; i < OUTPUTS / 2; i++) for (int i = 0; i < OUTPUTS / 2; i++)
lights[OUTPUT_LIGHT + i].value = (audioIO.numInputs >= 2*i+1); lights[OUTPUT_LIGHT + i].value = (audioIO.numInputs >= 2*i+1);

audioIO.audioCv.notify_all();
} }






+ 14
- 19
src/audio.cpp View File

@@ -46,8 +46,10 @@ std::string AudioIO::getDriverName(int driver) {
} }


void AudioIO::setDriver(int driver) { void AudioIO::setDriver(int driver) {
// Close device
setDevice(-1, 0); setDevice(-1, 0);


// Close driver
if (rtAudio) { if (rtAudio) {
delete rtAudio; delete rtAudio;
rtAudio = NULL; rtAudio = NULL;
@@ -69,7 +71,7 @@ int AudioIO::getDeviceCount() {
return rtAudio->getDeviceCount(); return rtAudio->getDeviceCount();
} }
if (driver == BRIDGE_DRIVER) { if (driver == BRIDGE_DRIVER) {
return BRIDGE_CHANNELS;
return BRIDGE_NUM_PORTS;
} }
return 0; return 0;
} }
@@ -90,13 +92,11 @@ bool AudioIO::getDeviceInfo(int device, RtAudio::DeviceInfo *deviceInfo) {
} }
catch (RtAudioError &e) { catch (RtAudioError &e) {
warn("Failed to query RtAudio device: %s", e.what()); warn("Failed to query RtAudio device: %s", e.what());
return false;
} }
} }
} }
else {
return false;
}

return false;
} }


int AudioIO::getDeviceChannels(int device) { int AudioIO::getDeviceChannels(int device) {
@@ -148,7 +148,7 @@ std::string AudioIO::getDeviceDetail(int device, int offset) {
} }
} }
if (driver == BRIDGE_DRIVER) { if (driver == BRIDGE_DRIVER) {
return stringf("Channel %d", device + 1);
return stringf("Port %d", device + 1);
} }
return ""; return "";
} }
@@ -172,6 +172,12 @@ void AudioIO::setBlockSize(int blockSize) {
openStream(); 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) { static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData) {
AudioIO *audioIO = (AudioIO*) userData; AudioIO *audioIO = (AudioIO*) userData;
@@ -252,8 +258,8 @@ void AudioIO::openStream() {
onOpenStream(); onOpenStream();
} }
if (driver == BRIDGE_DRIVER) { if (driver == BRIDGE_DRIVER) {
numOutputs = 2;
numInputs = 2;
numOutputs = 0;
numInputs = 0;
// TEMP // TEMP
sampleRate = 44100; sampleRate = 44100;
blockSize = 256; blockSize = 256;
@@ -293,17 +299,6 @@ void AudioIO::closeStream() {
onCloseStream(); onCloseStream();
} }


bool AudioIO::isActive() {
if (rtAudio) {
return rtAudio->isStreamRunning();
}
if (driver == BRIDGE_DRIVER) {
bridgeAudioIsActive(device, this);
}
return false;
}


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


+ 107
- 60
src/bridge.cpp View File

@@ -25,11 +25,13 @@ enum BridgeCommand {
NO_COMMAND = 0, NO_COMMAND = 0,
START_COMMAND, START_COMMAND,
QUIT_COMMAND, QUIT_COMMAND,
CHANNEL_SET_COMMAND,
PORT_SET_COMMAND,
MIDI_MESSAGE_SEND_COMMAND,
AUDIO_SAMPLE_RATE_SET_COMMAND, AUDIO_SAMPLE_RATE_SET_COMMAND,
AUDIO_CHANNELS_SET_COMMAND, AUDIO_CHANNELS_SET_COMMAND,
AUDIO_BUFFER_SEND_COMMAND, AUDIO_BUFFER_SEND_COMMAND,
MIDI_MESSAGE_SEND_COMMAND,
AUDIO_ACTIVATE,
AUDIO_DEACTIVATE,
NUM_COMMANDS NUM_COMMANDS
}; };


@@ -37,7 +39,9 @@ enum BridgeCommand {
static const int RECV_BUFFER_SIZE = (1<<13); static const int RECV_BUFFER_SIZE = (1<<13);
static const int RECV_QUEUE_SIZE = (1<<17); 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 std::thread serverThread;
static bool serverQuit; static bool serverQuit;


@@ -47,10 +51,11 @@ struct BridgeClientConnection {
RingBuffer<uint8_t, RECV_QUEUE_SIZE> recvQueue; RingBuffer<uint8_t, RECV_QUEUE_SIZE> recvQueue;
BridgeCommand currentCommand = START_COMMAND; BridgeCommand currentCommand = START_COMMAND;
bool closeRequested = false; bool closeRequested = false;
int channel = -1;
int port = -1;
int sampleRate = -1; int sampleRate = -1;
int audioChannels = 0; int audioChannels = 0;
int audioBufferLength = -1; int audioBufferLength = -1;
bool audioActive = false;


void send(const uint8_t *buffer, int length) { void send(const uint8_t *buffer, int length) {
if (length <= 0) if (length <= 0)
@@ -80,6 +85,35 @@ struct BridgeClientConnection {
return x; 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 /** Steps the state machine
Returns true if step() should be called again Returns true if step() should be called again
*/ */
@@ -115,10 +149,21 @@ struct BridgeClientConnection {
debug("Quitting!"); debug("Quitting!");
} break; } break;


case CHANNEL_SET_COMMAND: {
case PORT_SET_COMMAND: {
if (recvQueue.size() >= 1) { if (recvQueue.size() >= 1) {
channel = shift<uint8_t>();
debug("Set channel %d", channel);
int port = shift<uint8_t>();
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; currentCommand = NO_COMMAND;
return true; return true;
} }
@@ -136,7 +181,7 @@ struct BridgeClientConnection {
case AUDIO_CHANNELS_SET_COMMAND: { case AUDIO_CHANNELS_SET_COMMAND: {
if (recvQueue.size() >= 1) { if (recvQueue.size() >= 1) {
audioChannels = shift<uint8_t>(); audioChannels = shift<uint8_t>();
debug("Set audio channels %d", channel);
debug("Set audio channels %d", audioChannels);
currentCommand = NO_COMMAND; currentCommand = NO_COMMAND;
return true; return true;
} }
@@ -177,14 +222,16 @@ struct BridgeClientConnection {
} }
} break; } 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; } break;


default: { default: {
@@ -192,44 +239,47 @@ struct BridgeClientConnection {
closeRequested = true; closeRequested = true;
} break; } break;
} }

// Stop looping the state machine
return false; 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) { void processStream(const float *input, float *output, int frames) {
if (!(0 <= channel && channel < BRIDGE_CHANNELS))
if (!(0 <= port && port < BRIDGE_NUM_PORTS))
return; return;
if (!audioListeners[channel])
if (!audioListeners[port])
return; 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(); 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; return;
if (audioListeners[channel])
if (audioListeners[port])
return; 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; return;
if (audioListeners[channel] != audio)
if (audioListeners[port] != audio)
return; 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);
} }






Loading…
Cancel
Save