mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-16 00:34:19 +00:00
CoreAudio: Avoid data race on fifo storage
Previously, whenever the output device sample time changed from 'invalid' to 'valid', the AudioBuffer fifo in the AudioIODeviceCombiner was cleared. This caused a data race, since the clear operation was not mutually exclusive with writes from the input device. This change causes the AudioIODeviceCombiner to keep track of the timestamp of the first input device callback after the output device is invalidated. The output device is unable to read from the fifo until its timestamp exceeds the stored input device callback timestamp.
This commit is contained in:
parent
6cd2ed022d
commit
93063de28d
1 changed files with 25 additions and 5 deletions
|
|
@ -1695,6 +1695,7 @@ private:
|
|||
CriticalSection closeLock;
|
||||
int targetLatency = 0;
|
||||
std::atomic<int> xruns { -1 };
|
||||
std::atomic<uint64_t> lastValidReadPosition { invalidSampleTime };
|
||||
|
||||
BigInteger inputChannelsRequested, outputChannelsRequested;
|
||||
double sampleRateRequested = 44100;
|
||||
|
|
@ -1792,8 +1793,9 @@ private:
|
|||
}
|
||||
|
||||
auto currentWritePos = writePos.load();
|
||||
const auto nextWritePos = currentWritePos + static_cast<std::uint64_t> (n);
|
||||
|
||||
writePos.compare_exchange_strong (currentWritePos, currentWritePos + static_cast<std::uint64_t> (n));
|
||||
writePos.compare_exchange_strong (currentWritePos, nextWritePos);
|
||||
|
||||
if (currentWritePos == invalidSampleTime)
|
||||
return;
|
||||
|
|
@ -1817,6 +1819,11 @@ private:
|
|||
scratchBuffer.getReadPointer (args.channel, args.inputPos),
|
||||
args.nItems);
|
||||
});
|
||||
|
||||
{
|
||||
auto invalid = invalidSampleTime;
|
||||
lastValidReadPosition.compare_exchange_strong (invalid, nextWritePos);
|
||||
}
|
||||
}
|
||||
|
||||
void outputAudioCallback (float* const* channels, int numChannels, int n) noexcept
|
||||
|
|
@ -1839,16 +1846,29 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
accessFifo (currentReadPos, numChannels, n, [&] (const auto& args)
|
||||
// If there was an xrun, we want to output zeros until we're sure that there's some valid
|
||||
// input for us to read.
|
||||
const auto longN = static_cast<uint64_t> (n);
|
||||
const auto nextReadPos = currentReadPos + longN;
|
||||
const auto validReadPos = lastValidReadPosition.load();
|
||||
const auto sanitisedValidReadPos = validReadPos != invalidSampleTime ? validReadPos : nextReadPos;
|
||||
const auto numZerosToWrite = sanitisedValidReadPos <= currentReadPos
|
||||
? 0
|
||||
: jmin (longN, sanitisedValidReadPos - currentReadPos);
|
||||
|
||||
for (auto i = 0; i < numChannels; ++i)
|
||||
std::fill (channels[i], channels[i] + numZerosToWrite, 0.0f);
|
||||
|
||||
accessFifo (currentReadPos + numZerosToWrite, numChannels, static_cast<int> (longN - numZerosToWrite), [&] (const auto& args)
|
||||
{
|
||||
FloatVectorOperations::copy (channels[args.channel] + args.inputPos,
|
||||
FloatVectorOperations::copy (channels[args.channel] + args.inputPos + numZerosToWrite,
|
||||
fifo.getReadPointer (args.channel, args.fifoPos),
|
||||
args.nItems);
|
||||
});
|
||||
|
||||
// use compare exchange here as we need to avoid the case
|
||||
// where we overwrite readPos being equal to invalidSampleTime
|
||||
readPos.compare_exchange_strong (currentReadPos, currentReadPos + static_cast<std::uint64_t> (n));
|
||||
readPos.compare_exchange_strong (currentReadPos, nextReadPos);
|
||||
}
|
||||
|
||||
void xrun() noexcept
|
||||
|
|
@ -1987,7 +2007,7 @@ private:
|
|||
auto copy = invalidSampleTime;
|
||||
|
||||
if (sampleTime.compare_exchange_strong (copy, callbackSampleTime) && (! input))
|
||||
owner.fifo.clear();
|
||||
owner.lastValidReadPosition = invalidSampleTime;
|
||||
}
|
||||
|
||||
bool isInput() const { return input; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue