Browse Source

Add audio activation/deactivation to Bridge

tags/v0.6.0
Andrew Belt 6 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 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<int> 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);
};


+ 1
- 2
include/bridge.hpp View File

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

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

@@ -43,10 +43,10 @@ struct AudioInterfaceIO : AudioIO {
for (int i = 0; i < frames; i++) {
if (inputBuffer.full())
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
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<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()) {
// 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);
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();
}




+ 14
- 19
src/audio.cpp View File

@@ -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<int> AudioIO::getSampleRates() {
if (rtAudio) {
try {


+ 107
- 60
src/bridge.cpp View File

@@ -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<uint8_t, RECV_QUEUE_SIZE> 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<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;
return true;
}
@@ -136,7 +181,7 @@ struct BridgeClientConnection {
case AUDIO_CHANNELS_SET_COMMAND: {
if (recvQueue.size() >= 1) {
audioChannels = shift<uint8_t>();
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);
}




Loading…
Cancel
Save