Thanks for the feedback, everyone!
I’ve profiled processing times and didn’t see any major spikes in processBlock()
, but since I’m offloading work to a background thread now, I wonder if some thread contention is causing delays in the data handoff.
Currently, I’m not explicitly reporting plugin latency to the DAW, but given my background processing approach, it seems like I should be.
I’ve implemented a FIFO-based solution to offload processing to a background thread, which has greatly improved playback stability and reduced CPU usage. However, I’m now facing an issue that I suspect is a race condition.
Here’s the setup:
- The audio thread writes input data into a lock-free FIFO and reads the processed output.
- A background thread pulls from the FIFO, applies time-stretching with Elastique, and pushes the processed data back.
- I process 1024-sample blocks, while the DAW buffer size is 32 samples (or user-defined).
The FIFO approach has resolved previous glitches, but now I’m seeing assertion failures in Elastique. Since Elastique doesn’t include debugging symbols, I can’t debug it directly. The strange thing is that when I comment out the fifo.read()
call in getNextAudioBlock()
, the assertion failure stops, which makes me think that there’s a race condition between the audio and background threads.
I’ve posted my relavent code below. If anyone has insights or has worked with similar issues, I’d really appreciate the advice. Thanks again!
void run()
{
while (!threadShouldExit())
{
if (fifo.getFreeSpace() >= 1024)
{
AudioSampleBuffer inputBuffer;
const int numOutputSamplesThisBlock = 1024;
const int numInputSamplesThisBlock = elastique->GetFramesNeeded(numOutputSamplesThisBlock);
if (numInputSamplesThisBlock != -1)
{
fillInputBuffer(inputBuffer);
auto write = fifo.write(numOutputSamplesThisBlock);
const float* inputs[2] = { inputBuffer.getReadPointer(0, 0),
inputBuffer.getReadPointer(1, 0) };
float* outputs[2] = { fifoBuffer.getWritePointer(0, write.startIndex1),
fifoBuffer.getWritePointer(1, write.startIndex1) };
elastique->ProcessData((float**)inputs, numInputSamplesThisBlock, (float**)outputs);
}
}
wait(1);
}
}
void getNextAudioBlock(const AudioSourceChannelInfo& bufferToFill)
{
if (fifo.getNumReady() < bufferToFill.buffer->getNumSamples())
{
return;
}
int numSamplesToRead = jmin(bufferToFill.buffer->getNumSamples(), fifo.getNumReady());
auto read = fifo.read(numSamplesToRead);
for (int ch = 0; ch < bufferToFill.buffer->getNumChannels(); ++ch)
{
bufferToFill.buffer->copyFrom(ch, 0, fifoBuffer, ch, read.startIndex1, read.blockSize1);
if (read.startIndex2 > 0)
bufferToFill.buffer->copyFrom(ch, read.startIndex1, fifoBuffer, ch, read.startIndex2, read.blockSize2);
}
}