27 TimeSliceThread& thread,
28 bool deleteSourceWhenDeleted,
29 int bufferSizeSamples,
31 bool prefillBufferOnPrepareToPlay)
32 : source (s, deleteSourceWhenDeleted),
33 backgroundThread (thread),
34 numberOfSamplesToBuffer (jmax (1024, bufferSizeSamples)),
35 numberOfChannels (numChannels),
36 prefillBuffer (prefillBufferOnPrepareToPlay)
38 jassert (source !=
nullptr);
40 jassert (numberOfSamplesToBuffer > 1024);
52 auto bufferSizeNeeded = jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer);
54 if (! approximatelyEqual (newSampleRate, sampleRate)
55 || bufferSizeNeeded != buffer.getNumSamples()
58 backgroundThread.removeTimeSliceClient (
this);
61 sampleRate = newSampleRate;
63 source->prepareToPlay (samplesPerBlockExpected, newSampleRate);
65 buffer.setSize (numberOfChannels, bufferSizeNeeded);
68 const ScopedLock sl (bufferRangeLock);
73 backgroundThread.addTimeSliceClient (
this);
77 const ScopedUnlock ul (bufferRangeLock);
79 backgroundThread.moveToFrontOfQueue (
this);
83 && (bufferValidEnd - bufferValidStart < jmin (((
int) newSampleRate) / 4, buffer.getNumSamples() / 2)));
90 backgroundThread.removeTimeSliceClient (
this);
92 buffer.setSize (numberOfChannels, 0);
98 source->releaseResources();
103 const auto bufferRange = getValidBufferRange (info.
numSamples);
105 if (bufferRange.isEmpty())
112 const auto validStart = bufferRange.getStart();
113 const auto validEnd = bufferRange.getEnd();
115 const ScopedLock sl (callbackLock);
124 if (validStart < validEnd)
128 jassert (buffer.getNumSamples() > 0);
130 const auto startBufferIndex = (int) ((validStart + nextPlayPos) % buffer.getNumSamples());
131 const auto endBufferIndex = (int) ((validEnd + nextPlayPos) % buffer.getNumSamples());
133 if (startBufferIndex < endBufferIndex)
137 chan, startBufferIndex,
138 validEnd - validStart);
142 const auto initialSize = buffer.getNumSamples() - startBufferIndex;
146 chan, startBufferIndex,
152 (validEnd - validStart) - initialSize);
162 if (source ==
nullptr || source->getTotalLength() <= 0)
170 auto now = startTime;
172 auto elapsed = (now >= startTime ? now - startTime
173 : (std::numeric_limits<uint32>::max() - startTime) + now);
175 while (elapsed <= timeout)
177 const auto bufferRange = getValidBufferRange (info.
numSamples);
179 const auto validStart = bufferRange.getStart();
180 const auto validEnd = bufferRange.getEnd();
183 && validStart < validEnd
189 if (elapsed < timeout
190 && ! bufferReadyEvent.wait (
static_cast<int> (timeout - elapsed)))
196 elapsed = (now >= startTime ? now - startTime
197 : (std::numeric_limits<uint32>::max() - startTime) + now);
205 jassert (source->getTotalLength() > 0);
206 const auto pos = nextPlayPos.load();
208 return (source->isLooping() && nextPlayPos > 0)
209 ? pos % source->getTotalLength()
215 const ScopedLock sl (bufferRangeLock);
217 nextPlayPos = newPosition;
218 backgroundThread.moveToFrontOfQueue (
this);
221Range<int> BufferingAudioSource::getValidBufferRange (
int numSamples)
const
223 const ScopedLock sl (bufferRangeLock);
225 const auto pos = nextPlayPos.load();
227 return { (int) (jlimit (bufferValidStart, bufferValidEnd, pos) - pos),
228 (int) (jlimit (bufferValidStart, bufferValidEnd, pos + numSamples) - pos) };
231bool BufferingAudioSource::readNextBufferChunk()
233 int64 newBVS, newBVE, sectionToReadStart, sectionToReadEnd;
236 const ScopedLock sl (bufferRangeLock);
241 bufferValidStart = 0;
245 newBVS = jmax ((int64) 0, nextPlayPos.load());
246 newBVE = newBVS + buffer.getNumSamples() - 4;
247 sectionToReadStart = 0;
248 sectionToReadEnd = 0;
250 constexpr int maxChunkSize = 2048;
252 if (newBVS < bufferValidStart || newBVS >= bufferValidEnd)
254 newBVE = jmin (newBVE, newBVS + maxChunkSize);
256 sectionToReadStart = newBVS;
257 sectionToReadEnd = newBVE;
259 bufferValidStart = 0;
262 else if (std::abs ((
int) (newBVS - bufferValidStart)) > 512
263 || std::abs ((
int) (newBVE - bufferValidEnd)) > 512)
265 newBVE = jmin (newBVE, bufferValidEnd + maxChunkSize);
267 sectionToReadStart = bufferValidEnd;
268 sectionToReadEnd = newBVE;
270 bufferValidStart = newBVS;
271 bufferValidEnd = jmin (bufferValidEnd, newBVE);
275 if (sectionToReadStart == sectionToReadEnd)
278 jassert (buffer.getNumSamples() > 0);
280 const auto bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples());
281 const auto bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples());
283 if (bufferIndexStart < bufferIndexEnd)
285 readBufferSection (sectionToReadStart,
286 (
int) (sectionToReadEnd - sectionToReadStart),
291 const auto initialSize = buffer.getNumSamples() - bufferIndexStart;
293 readBufferSection (sectionToReadStart,
297 readBufferSection (sectionToReadStart + initialSize,
298 (
int) (sectionToReadEnd - sectionToReadStart) - initialSize,
303 const ScopedLock sl2 (bufferRangeLock);
305 bufferValidStart = newBVS;
306 bufferValidEnd = newBVE;
309 bufferReadyEvent.signal();
313void BufferingAudioSource::readBufferSection (int64 start,
int length,
int bufferOffset)
315 if (source->getNextReadPosition() != start)
316 source->setNextReadPosition (start);
318 AudioSourceChannelInfo info (&buffer, bufferOffset, length);
320 const ScopedLock sl (callbackLock);
321 source->getNextAudioBlock (info);
326 return readNextBufferChunk() ? 1 : 100;
int getNumChannels() const noexcept
void copyFrom(int destChannel, int destStartSample, const AudioBuffer &source, int sourceChannel, int sourceStartSample, int numSamples) noexcept
void getNextAudioBlock(const AudioSourceChannelInfo &) override
void setNextReadPosition(int64 newPosition) override
int64 getTotalLength() const override
~BufferingAudioSource() override
BufferingAudioSource(PositionableAudioSource *source, TimeSliceThread &backgroundThread, bool deleteSourceWhenDeleted, int numberOfSamplesToBuffer, int numberOfChannels=2, bool prefillBufferOnPrepareToPlay=true)
bool waitForNextAudioBlockReady(const AudioSourceChannelInfo &info, uint32 timeout)
bool isLooping() const override
void releaseResources() override
int64 getNextReadPosition() const override
void prepareToPlay(int samplesPerBlockExpected, double sampleRate) override
PositionableAudioSource()=default
static void JUCE_CALLTYPE sleep(int milliseconds)
virtual int useTimeSlice()=0
static uint32 getMillisecondCounter() noexcept
void clearActiveBufferRegion() const
AudioBuffer< float > * buffer