Skip to content

Commit

Permalink
Using a more flexible wait-free queue for monitor signal
Browse files Browse the repository at this point in the history
  • Loading branch information
mikedickey committed Jan 9, 2024
1 parent 2c19a05 commit 52fd51e
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 64 deletions.
111 changes: 53 additions & 58 deletions src/AudioInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ AudioInterface::AudioInterface(QVarLengthArray<int> InputChans,
, mBitResolutionMode(AudioBitResolution)
, mSampleRate(gDefaultSampleRate)
, mBufferSizeInSamples(gDefaultBufferSizeInSamples)
, mMonProcessBufferIndex(0)
, mMonitorQueuePtr(NULL)
, mAudioInputPacket(NULL)
, mAudioOutputPacket(NULL)
, mLoopBack(false)
Expand All @@ -88,11 +88,7 @@ AudioInterface::~AudioInterface()
for (int i = 0; i < mOutProcessBuffer.size(); i++) {
delete[] mOutProcessBuffer[i];
}
for (int n = 0; n < 2; n++) {
for (int i = 0; i < mMonProcessBuffers[n].size(); i++) {
delete[] mMonProcessBuffers[n][i];
}
}
delete mMonitorQueuePtr;
#ifdef WAIR // NOT WAIR:
for (int i = 0; i < mAPInBuffer.size(); i++) {
delete[] mAPInBuffer[i];
Expand Down Expand Up @@ -130,16 +126,18 @@ void AudioInterface::setup(bool /*verbose*/)
size_audio_output = mSizeInBytesPerChannel * mNumNetRevChans;
}
#endif // endwhere
mAudioInputPacket = new int8_t[size_audio_input];
mAudioOutputPacket = new int8_t[size_audio_output];
const size_t audioInputPacketSize = std::max<size_t>(size_audio_input,
mInputChans.size() * sizeof(sample_t) * nframes);
const size_t audioOutputPacketSize = std::max<size_t>(size_audio_output,
mOutputChans.size() * sizeof(sample_t) * nframes);
mAudioInputPacket = new int8_t[audioInputPacketSize];
mAudioOutputPacket = new int8_t[audioOutputPacketSize];

// Initialize and assign memory for ProcessPlugins Buffers
#ifdef WAIR // WAIR
if (mNumNetRevChans) {
mInProcessBuffer.resize(mNumNetRevChans);
mOutProcessBuffer.resize(mNumNetRevChans);
mMonProcessBuffers[0].resize(mNumNetRevChans);
mMonProcessBuffers[1].resize(mNumNetRevChans);
mAPInBuffer.resize(mInputChans.size());
mNetInBuffer.resize(mNumNetRevChans);
for (int i = 0; i < mAPInBuffer.size(); i++) {
Expand All @@ -157,8 +155,7 @@ void AudioInterface::setup(bool /*verbose*/)
{
mInProcessBuffer.resize(mInputChans.size());
mOutProcessBuffer.resize(mOutputChans.size());
mMonProcessBuffers[0].resize(mOutputChans.size());
mMonProcessBuffers[1].resize(mOutputChans.size());
mMonitorQueuePtr = new WaitFreeFrameBuffer<64>(audioInputPacketSize);
}

for (int i = 0; i < mInputChans.size(); i++) {
Expand All @@ -171,14 +168,6 @@ void AudioInterface::setup(bool /*verbose*/)
// set memory to 0
std::memset(mOutProcessBuffer[i], 0, sizeof(sample_t) * nframes);
}
for (int i = 0; i < mMonProcessBuffers[0].size(); i++) {
mMonProcessBuffers[0][i] = new sample_t[nframes];
mMonProcessBuffers[1][i] = new sample_t[nframes];
// set memory to 0
std::memset(mMonProcessBuffers[0][i], 0, sizeof(sample_t) * nframes);
std::memset(mMonProcessBuffers[1][i], 0, sizeof(sample_t) * nframes);
}

mInBufCopy.resize(mInputChans.size());
for (int i = 0; i < mInputChans.size(); i++) {
mInBufCopy[i] =
Expand Down Expand Up @@ -218,6 +207,19 @@ void AudioInterface::audioInputCallback(QVarLengthArray<sample_t*>& in_buffer,
return;
}

#ifndef WAIR
if (mMonitorQueuePtr != nullptr && mProcessPluginsToMonitor.size() > 0) {
// copy audio input to monitor queue
for (int i = 0; i < mInputChans.size(); i++) {
int8_t* sample_ptr = mAudioInputPacket + (i * sizeof(sample_t) * n_frames);
std::memcpy(sample_ptr, in_buffer[i], sizeof(sample_t) * n_frames);
}
mMonitorQueuePtr->push(mAudioInputPacket);
}
#endif // not WAIR

// TODO: is this really necessary??

// cannot modify in_buffer, so make a copy
// in_buffer is "in" from local audio hardware
if (mInBufCopy.size() < mInputChans.size()) { // created in constructor above
Expand All @@ -241,30 +243,6 @@ void AudioInterface::audioInputCallback(QVarLengthArray<sample_t*>& in_buffer,
}
}

// copy audio input to monitor buffers
// alternate between writing to the first and second monitor process buffers
// we use two buffers to help ensure safety across threads when audio input
// and output are being processed separately (non-duplex mode)
int monIndex = mMonProcessBufferIndex.load(std::memory_order_relaxed) == 0 ? 1 : 0;
for (int i = 0; i < mMonProcessBuffers[0].size(); i++) {
if ((mInputChans.size() == 2 && mInputMixMode == AudioInterface::MIXTOMONO)
|| (mInputChans.size() == AudioInterface::MONO)) {
// if using mix-to-mono, in_buffer[0] should already contain the mixed
// audio, so copy it to the monitor buffer. See RtAudioInterface.cpp

// likewise if using mono, we simply copy the input to every monitor
// channel
std::memcpy(mMonProcessBuffers[monIndex][i], in_buffer[0],
sizeof(sample_t) * n_frames);
} else {
// otherwise, copy each channel individually
std::memcpy(mMonProcessBuffers[monIndex][i], in_buffer[i],
sizeof(sample_t) * n_frames);
}
}
// update index for next buffer ready to read
mMonProcessBufferIndex.store(monIndex, std::memory_order_release);

// add audio testing impulse, if enabled
if (audioTesting) {
mAudioTesterP->writeImpulse(
Expand Down Expand Up @@ -319,6 +297,35 @@ void AudioInterface::audioOutputCallback(QVarLengthArray<sample_t*>& out_buffer,
p->compute(n_frames, out_buffer.data(), out_buffer.data());
}
}

if (mMonitorQueuePtr != nullptr && mProcessPluginsToMonitor.size() > 0) {
// mix in the monitor signal
// note that using memory_order_acquire ensures all data written to the buffers
// will be also available be available to this thread before read
std::memset(mAudioOutputPacket, 0, sizeof(sample_t) * n_frames * getNumInputChannels());
mMonitorQueuePtr->pop(mAudioOutputPacket);
for (int i = 0; i < getNumOutputChannels(); i++) {
// if using mix-to-mono, in_buffer[0] should already contain the mixed
// audio, so copy it to the monitor buffer. See RtAudioInterface.cpp

// likewise if using mono, we simply copy the input to every monitor
// channel
int8_t* sample_ptr = mAudioOutputPacket;
if (i > 0 && getNumInputChannels() > i && mInputMixMode == AudioInterface::STEREO) {
// otherwise, copy each channel individually
sample_ptr += (i * sizeof(sample_t) * n_frames);
}
std::memcpy(mOutProcessBuffer[i], sample_ptr, sizeof(sample_t) * n_frames);
}
for (int i = 0; i < mProcessPluginsToMonitor.size(); i++) {
ProcessPlugin* p = mProcessPluginsToMonitor[i];
if (p->getInited()) {
// note: for monitor plugins, the output is out_buffer (to the speakers)
p->compute(n_frames, mOutProcessBuffer.data(), out_buffer.data());
}
}
}

#else // WAIR:
// nib16 result now in mNetInBuffer
int nChansIn = mInputChans.size();
Expand Down Expand Up @@ -398,18 +405,6 @@ void AudioInterface::audioOutputCallback(QVarLengthArray<sample_t*>& out_buffer,
#endif // AP
}
#endif // endwhere

// mix in the monitor signal
// note that using memory_order_acquire ensures all data written to the buffers
// will be also available be available to this thread before read
int monIndex = mMonProcessBufferIndex.load(std::memory_order_acquire);
for (int i = 0; i < mProcessPluginsToMonitor.size(); i++) {
ProcessPlugin* p = mProcessPluginsToMonitor[i];
if (p->getInited()) {
// note: for monitor plugins, the output is out_buffer (to the speakers)
p->compute(n_frames, mMonProcessBuffers[monIndex].data(), out_buffer.data());
}
}
}

//*******************************************************************************
Expand Down Expand Up @@ -711,7 +706,7 @@ void AudioInterface::appendProcessPluginToMonitor(ProcessPlugin* plugin)
return;
}

const int nChansMon = mMonProcessBuffers[0].size();
const int nChansMon = getNumMonChannels();

if (plugin->getNumInputs() > nChansMon) {
std::cerr
Expand Down Expand Up @@ -740,7 +735,7 @@ void AudioInterface::initPlugins(bool verbose)
{
const int nChansIn = (MIXTOMONO == mInputMixMode) ? 1 : mInputChans.size();
const int nChansOut = mOutputChans.size();
const int nChansMon = mMonProcessBuffers[0].size();
const int nChansMon = getNumMonChannels();
int nPlugins = mProcessPluginsFromNetwork.size() + mProcessPluginsToNetwork.size()
+ mProcessPluginsToMonitor.size();
if (nPlugins > 0) {
Expand Down
11 changes: 5 additions & 6 deletions src/AudioInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,11 @@

#include <QVarLengthArray>
#include <QVector>
#include <atomic>

#include "AudioTester.h"
#include "ProcessPlugin.h"
#include "WaitFreeFrameBuffer.h"
#include "jacktrip_types.h"
//#include "jacktrip_globals.h"

// Forward declarations
class JackTrip;
Expand Down Expand Up @@ -273,6 +272,8 @@ class AudioInterface
virtual int getNumInputChannels() const { return mInputChans.size(); }
/// \brief Get Number of Output Channels
virtual int getNumOutputChannels() const { return mOutputChans.size(); }
/// \brief Get Number of Monitor Channels
virtual int getNumMonChannels() const { return mOutputChans.size(); }
virtual QVarLengthArray<int> getInputChannels() const { return mInputChans; }
virtual QVarLengthArray<int> getOutputChannels() const { return mOutputChans; }
virtual inputMixModeT getInputMixMode() const { return mInputMixMode; }
Expand Down Expand Up @@ -342,10 +343,8 @@ class AudioInterface
mInProcessBuffer; ///< Vector of Input buffers/channel for ProcessPlugin
QVarLengthArray<sample_t*>
mOutProcessBuffer; ///< Vector of Output buffers/channel for ProcessPlugin
QVarLengthArray<sample_t*>
mMonProcessBuffers[2]; ///< Vector of Monitor buffers/channel for ProcessPlugin
std::atomic<int>
mMonProcessBufferIndex; ///< Monitor process buffer index for next read
WaitFreeFrameBuffer<64>*
mMonitorQueuePtr; //< Queue of audio frames from monitor signal
int8_t* mAudioInputPacket; ///< Packet containing all the channels to read from the
///< RingBuffer
int8_t* mAudioOutputPacket; ///< Packet containing all the channels to send to the
Expand Down

0 comments on commit 52fd51e

Please sign in to comment.