1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

BufferingAudioSource: Fix AudioBuffer data race

This commit is contained in:
ed 2021-08-13 14:21:50 +01:00
parent 832deb9ad7
commit 80602649a2
2 changed files with 101 additions and 82 deletions

View file

@ -65,6 +65,8 @@ void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double ne
buffer.setSize (numberOfChannels, bufferSizeNeeded);
buffer.clear();
const ScopedLock sl (bufferRangeLock);
bufferValidStart = 0;
bufferValidEnd = 0;
@ -72,6 +74,8 @@ void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double ne
do
{
const ScopedUnlock ul (bufferRangeLock);
backgroundThread.moveToFrontOfQueue (this);
Thread::sleep (5);
}
@ -96,100 +100,97 @@ void BufferingAudioSource::releaseResources()
void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
{
const ScopedLock sl (bufferStartPosLock);
const auto bufferRange = getValidBufferRange (info.numSamples);
auto start = bufferValidStart.load();
auto end = bufferValidEnd.load();
auto pos = nextPlayPos.load();
auto validStart = (int) (jlimit (start, end, pos) - pos);
auto validEnd = (int) (jlimit (start, end, pos + info.numSamples) - pos);
if (validStart == validEnd)
if (bufferRange.isEmpty())
{
// total cache miss
info.clearActiveBufferRegion();
return;
}
else
const auto validStart = bufferRange.getStart();
const auto validEnd = bufferRange.getEnd();
const ScopedLock sl (callbackLock);
if (validStart > 0)
info.buffer->clear (info.startSample, validStart); // partial cache miss at start
if (validEnd < info.numSamples)
info.buffer->clear (info.startSample + validEnd,
info.numSamples - validEnd); // partial cache miss at end
if (validStart < validEnd)
{
if (validStart > 0)
info.buffer->clear (info.startSample, validStart); // partial cache miss at start
if (validEnd < info.numSamples)
info.buffer->clear (info.startSample + validEnd,
info.numSamples - validEnd); // partial cache miss at end
if (validStart < validEnd)
for (int chan = jmin (numberOfChannels, info.buffer->getNumChannels()); --chan >= 0;)
{
for (int chan = jmin (numberOfChannels, info.buffer->getNumChannels()); --chan >= 0;)
jassert (buffer.getNumSamples() > 0);
const auto startBufferIndex = (int) ((validStart + nextPlayPos) % buffer.getNumSamples());
const auto endBufferIndex = (int) ((validEnd + nextPlayPos) % buffer.getNumSamples());
if (startBufferIndex < endBufferIndex)
{
jassert (buffer.getNumSamples() > 0);
auto startBufferIndex = (int) ((validStart + nextPlayPos) % buffer.getNumSamples());
auto endBufferIndex = (int) ((validEnd + nextPlayPos) % buffer.getNumSamples());
info.buffer->copyFrom (chan, info.startSample + validStart,
buffer,
chan, startBufferIndex,
validEnd - validStart);
}
else
{
const auto initialSize = buffer.getNumSamples() - startBufferIndex;
if (startBufferIndex < endBufferIndex)
{
info.buffer->copyFrom (chan, info.startSample + validStart,
buffer,
chan, startBufferIndex,
validEnd - validStart);
}
else
{
auto initialSize = buffer.getNumSamples() - startBufferIndex;
info.buffer->copyFrom (chan, info.startSample + validStart,
buffer,
chan, startBufferIndex,
initialSize);
info.buffer->copyFrom (chan, info.startSample + validStart,
buffer,
chan, startBufferIndex,
initialSize);
info.buffer->copyFrom (chan, info.startSample + validStart + initialSize,
buffer,
chan, 0,
(validEnd - validStart) - initialSize);
}
info.buffer->copyFrom (chan, info.startSample + validStart + initialSize,
buffer,
chan, 0,
(validEnd - validStart) - initialSize);
}
}
nextPlayPos += info.numSamples;
}
nextPlayPos += info.numSamples;
}
bool BufferingAudioSource::waitForNextAudioBlockReady (const AudioSourceChannelInfo& info, uint32 timeout)
{
if (!source || source->getTotalLength() <= 0)
if (source == nullptr || source->getTotalLength() <= 0)
return false;
if (nextPlayPos + info.numSamples < 0)
if ((nextPlayPos + info.numSamples < 0)
|| (! isLooping() && nextPlayPos > getTotalLength()))
return true;
if (! isLooping() && nextPlayPos > getTotalLength())
return true;
auto now = Time::getMillisecondCounter();
auto startTime = now;
const auto startTime = Time::getMillisecondCounter();
auto now = startTime;
auto elapsed = (now >= startTime ? now - startTime
: (std::numeric_limits<uint32>::max() - startTime) + now);
while (elapsed <= timeout)
{
const auto bufferRange = getValidBufferRange (info.numSamples);
const auto validStart = bufferRange.getStart();
const auto validEnd = bufferRange.getEnd();
if (validStart <= 0
&& validStart < validEnd
&& validEnd >= info.numSamples)
{
const ScopedLock sl (bufferStartPosLock);
auto start = bufferValidStart.load();
auto end = bufferValidEnd.load();
auto pos = nextPlayPos.load();
auto validStart = static_cast<int> (jlimit (start, end, pos) - pos);
auto validEnd = static_cast<int> (jlimit (start, end, pos + info.numSamples) - pos);
if (validStart <= 0 && validStart < validEnd && validEnd >= info.numSamples)
return true;
return true;
}
if (elapsed < timeout && (! bufferReadyEvent.wait (static_cast<int> (timeout - elapsed))))
if (elapsed < timeout
&& ! bufferReadyEvent.wait (static_cast<int> (timeout - elapsed)))
{
return false;
}
now = Time::getMillisecondCounter();
elapsed = (now >= startTime ? now - startTime
@ -202,7 +203,7 @@ bool BufferingAudioSource::waitForNextAudioBlockReady (const AudioSourceChannelI
int64 BufferingAudioSource::getNextReadPosition() const
{
jassert (source->getTotalLength() > 0);
auto pos = nextPlayPos.load();
const auto pos = nextPlayPos.load();
return (source->isLooping() && nextPlayPos > 0)
? pos % source->getTotalLength()
@ -211,18 +212,28 @@ int64 BufferingAudioSource::getNextReadPosition() const
void BufferingAudioSource::setNextReadPosition (int64 newPosition)
{
const ScopedLock sl (bufferStartPosLock);
const ScopedLock sl (bufferRangeLock);
nextPlayPos = newPosition;
backgroundThread.moveToFrontOfQueue (this);
}
Range<int> BufferingAudioSource::getValidBufferRange (int numSamples) const
{
const ScopedLock sl (bufferRangeLock);
const auto pos = nextPlayPos.load();
return { (int) (jlimit (bufferValidStart, bufferValidEnd, pos) - pos),
(int) (jlimit (bufferValidStart, bufferValidEnd, pos + numSamples) - pos) };
}
bool BufferingAudioSource::readNextBufferChunk()
{
int64 newBVS, newBVE, sectionToReadStart, sectionToReadEnd;
{
const ScopedLock sl (bufferStartPosLock);
const ScopedLock sl (bufferRangeLock);
if (wasSourceLooping != isLooping())
{
@ -236,7 +247,7 @@ bool BufferingAudioSource::readNextBufferChunk()
sectionToReadStart = 0;
sectionToReadEnd = 0;
const int maxChunkSize = 2048;
constexpr int maxChunkSize = 2048;
if (newBVS < bufferValidStart || newBVS >= bufferValidEnd)
{
@ -257,7 +268,7 @@ bool BufferingAudioSource::readNextBufferChunk()
sectionToReadEnd = newBVE;
bufferValidStart = newBVS;
bufferValidEnd = jmin (bufferValidEnd.load(), newBVE);
bufferValidEnd = jmin (bufferValidEnd, newBVE);
}
}
@ -265,8 +276,9 @@ bool BufferingAudioSource::readNextBufferChunk()
return false;
jassert (buffer.getNumSamples() > 0);
auto bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples());
auto bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples());
const auto bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples());
const auto bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples());
if (bufferIndexStart < bufferIndexEnd)
{
@ -276,7 +288,7 @@ bool BufferingAudioSource::readNextBufferChunk()
}
else
{
auto initialSize = buffer.getNumSamples() - bufferIndexStart;
const auto initialSize = buffer.getNumSamples() - bufferIndexStart;
readBufferSection (sectionToReadStart,
initialSize,
@ -288,7 +300,7 @@ bool BufferingAudioSource::readNextBufferChunk()
}
{
const ScopedLock sl2 (bufferStartPosLock);
const ScopedLock sl2 (bufferRangeLock);
bufferValidStart = newBVS;
bufferValidEnd = newBVE;
@ -304,6 +316,8 @@ void BufferingAudioSource::readBufferSection (int64 start, int length, int buffe
source->setNextReadPosition (start);
AudioSourceChannelInfo info (&buffer, bufferOffset, length);
const ScopedLock sl (callbackLock);
source->getNextAudioBlock (info);
}