JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_BufferingAudioSource.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23namespace juce
24{
25
27 TimeSliceThread& thread,
28 bool deleteSourceWhenDeleted,
30 int numChannels,
32 : source (s, deleteSourceWhenDeleted),
33 backgroundThread (thread),
34 numberOfSamplesToBuffer (jmax (1024, bufferSizeSamples)),
35 numberOfChannels (numChannels),
36 prefillBuffer (prefillBufferOnPrepareToPlay)
37{
38 jassert (source != nullptr);
39
40 jassert (numberOfSamplesToBuffer > 1024); // not much point using this class if you're
41 // not using a larger buffer..
42}
43
48
49//==============================================================================
51{
52 auto bufferSizeNeeded = jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer);
53
54 if (! approximatelyEqual (newSampleRate, sampleRate)
55 || bufferSizeNeeded != buffer.getNumSamples()
56 || ! isPrepared)
57 {
58 backgroundThread.removeTimeSliceClient (this);
59
60 isPrepared = true;
61 sampleRate = newSampleRate;
62
63 source->prepareToPlay (samplesPerBlockExpected, newSampleRate);
64
65 buffer.setSize (numberOfChannels, bufferSizeNeeded);
66 buffer.clear();
67
68 const ScopedLock sl (bufferRangeLock);
69
70 bufferValidStart = 0;
71 bufferValidEnd = 0;
72
73 backgroundThread.addTimeSliceClient (this);
74
75 do
76 {
77 const ScopedUnlock ul (bufferRangeLock);
78
79 backgroundThread.moveToFrontOfQueue (this);
80 Thread::sleep (5);
81 }
82 while (prefillBuffer
83 && (bufferValidEnd - bufferValidStart < jmin (((int) newSampleRate) / 4, buffer.getNumSamples() / 2)));
84 }
85}
86
88{
89 isPrepared = false;
90 backgroundThread.removeTimeSliceClient (this);
91
92 buffer.setSize (numberOfChannels, 0);
93
94 // MSVC2017 seems to need this if statement to not generate a warning during linking.
95 // As source is set in the constructor, there is no way that source could
96 // ever equal this, but it seems to make MSVC2017 happy.
97 if (source != this)
98 source->releaseResources();
99}
100
102{
103 const auto bufferRange = getValidBufferRange (info.numSamples);
104
105 if (bufferRange.isEmpty())
106 {
107 // total cache miss
109 return;
110 }
111
112 const auto validStart = bufferRange.getStart();
113 const auto validEnd = bufferRange.getEnd();
114
115 const ScopedLock sl (callbackLock);
116
117 if (validStart > 0)
118 info.buffer->clear (info.startSample, validStart); // partial cache miss at start
119
120 if (validEnd < info.numSamples)
121 info.buffer->clear (info.startSample + validEnd,
122 info.numSamples - validEnd); // partial cache miss at end
123
124 if (validStart < validEnd)
125 {
126 for (int chan = jmin (numberOfChannels, info.buffer->getNumChannels()); --chan >= 0;)
127 {
128 jassert (buffer.getNumSamples() > 0);
129
130 const auto startBufferIndex = (int) ((validStart + nextPlayPos) % buffer.getNumSamples());
131 const auto endBufferIndex = (int) ((validEnd + nextPlayPos) % buffer.getNumSamples());
132
134 {
135 info.buffer->copyFrom (chan, info.startSample + validStart,
136 buffer,
138 validEnd - validStart);
139 }
140 else
141 {
142 const auto initialSize = buffer.getNumSamples() - startBufferIndex;
143
144 info.buffer->copyFrom (chan, info.startSample + validStart,
145 buffer,
148
149 info.buffer->copyFrom (chan, info.startSample + validStart + initialSize,
150 buffer,
151 chan, 0,
152 (validEnd - validStart) - initialSize);
153 }
154 }
155 }
156
157 nextPlayPos += info.numSamples;
158}
159
161{
162 if (source == nullptr || source->getTotalLength() <= 0)
163 return false;
164
165 if ((nextPlayPos + info.numSamples < 0)
166 || (! isLooping() && nextPlayPos > getTotalLength()))
167 return true;
168
169 const auto startTime = Time::getMillisecondCounter();
170 auto now = startTime;
171
172 auto elapsed = (now >= startTime ? now - startTime
173 : (std::numeric_limits<uint32>::max() - startTime) + now);
174
175 while (elapsed <= timeout)
176 {
177 const auto bufferRange = getValidBufferRange (info.numSamples);
178
179 const auto validStart = bufferRange.getStart();
180 const auto validEnd = bufferRange.getEnd();
181
182 if (validStart <= 0
183 && validStart < validEnd
184 && validEnd >= info.numSamples)
185 {
186 return true;
187 }
188
189 if (elapsed < timeout
190 && ! bufferReadyEvent.wait (static_cast<int> (timeout - elapsed)))
191 {
192 return false;
193 }
194
196 elapsed = (now >= startTime ? now - startTime
197 : (std::numeric_limits<uint32>::max() - startTime) + now);
198 }
199
200 return false;
201}
202
204{
205 jassert (source->getTotalLength() > 0);
206 const auto pos = nextPlayPos.load();
207
208 return (source->isLooping() && nextPlayPos > 0)
209 ? pos % source->getTotalLength()
210 : pos;
211}
212
214{
215 const ScopedLock sl (bufferRangeLock);
216
217 nextPlayPos = newPosition;
218 backgroundThread.moveToFrontOfQueue (this);
219}
220
221Range<int> BufferingAudioSource::getValidBufferRange (int numSamples) const
222{
223 const ScopedLock sl (bufferRangeLock);
224
225 const auto pos = nextPlayPos.load();
226
227 return { (int) (jlimit (bufferValidStart, bufferValidEnd, pos) - pos),
228 (int) (jlimit (bufferValidStart, bufferValidEnd, pos + numSamples) - pos) };
229}
230
231bool BufferingAudioSource::readNextBufferChunk()
232{
234
235 {
236 const ScopedLock sl (bufferRangeLock);
237
238 if (wasSourceLooping != isLooping())
239 {
240 wasSourceLooping = isLooping();
241 bufferValidStart = 0;
242 bufferValidEnd = 0;
243 }
244
245 newBVS = jmax ((int64) 0, nextPlayPos.load());
246 newBVE = newBVS + buffer.getNumSamples() - 4;
249
250 constexpr int maxChunkSize = 2048;
251
252 if (newBVS < bufferValidStart || newBVS >= bufferValidEnd)
253 {
255
258
259 bufferValidStart = 0;
260 bufferValidEnd = 0;
261 }
262 else if (std::abs ((int) (newBVS - bufferValidStart)) > 512
263 || std::abs ((int) (newBVE - bufferValidEnd)) > 512)
264 {
265 newBVE = jmin (newBVE, bufferValidEnd + maxChunkSize);
266
267 sectionToReadStart = bufferValidEnd;
269
270 bufferValidStart = newBVS;
271 bufferValidEnd = jmin (bufferValidEnd, newBVE);
272 }
273 }
274
276 return false;
277
278 jassert (buffer.getNumSamples() > 0);
279
280 const auto bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples());
281 const auto bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples());
282
284 {
285 readBufferSection (sectionToReadStart,
288 }
289 else
290 {
291 const auto initialSize = buffer.getNumSamples() - bufferIndexStart;
292
293 readBufferSection (sectionToReadStart,
296
297 readBufferSection (sectionToReadStart + initialSize,
299 0);
300 }
301
302 {
303 const ScopedLock sl2 (bufferRangeLock);
304
305 bufferValidStart = newBVS;
306 bufferValidEnd = newBVE;
307 }
308
309 bufferReadyEvent.signal();
310 return true;
311}
312
313void BufferingAudioSource::readBufferSection (int64 start, int length, int bufferOffset)
314{
315 if (source->getNextReadPosition() != start)
316 source->setNextReadPosition (start);
317
318 AudioSourceChannelInfo info (&buffer, bufferOffset, length);
319
320 const ScopedLock sl (callbackLock);
321 source->getNextAudioBlock (info);
322}
323
324int BufferingAudioSource::useTimeSlice()
325{
326 return readNextBufferChunk() ? 1 : 100;
327}
328
329} // namespace juce
void setSize(int newNumChannels, int newNumSamples, bool keepExistingContent=false, bool clearExtraSpace=false, bool avoidReallocating=false)
Changes the buffer's size or number of channels.
int getNumChannels() const noexcept
Returns the number of channels of audio data that this buffer contains.
int getNumSamples() const noexcept
Returns the number of samples allocated in each of the buffer's channels.
void clear() noexcept
Clears all the samples in all channels and marks the buffer as cleared.
void copyFrom(int destChannel, int destStartSample, const AudioBuffer &source, int sourceChannel, int sourceStartSample, int numSamples) noexcept
Copies samples from another buffer to this one.
void getNextAudioBlock(const AudioSourceChannelInfo &) override
Implementation of the AudioSource method.
void setNextReadPosition(int64 newPosition) override
Implements the PositionableAudioSource method.
int64 getTotalLength() const override
Implements the PositionableAudioSource method.
~BufferingAudioSource() override
Destructor.
BufferingAudioSource(PositionableAudioSource *source, TimeSliceThread &backgroundThread, bool deleteSourceWhenDeleted, int numberOfSamplesToBuffer, int numberOfChannels=2, bool prefillBufferOnPrepareToPlay=true)
Creates a BufferingAudioSource.
bool waitForNextAudioBlockReady(const AudioSourceChannelInfo &info, uint32 timeout)
A useful function to block until the next the buffer info can be filled.
bool isLooping() const override
Implements the PositionableAudioSource method.
void releaseResources() override
Implementation of the AudioSource method.
int64 getNextReadPosition() const override
Implements the PositionableAudioSource method.
void prepareToPlay(int samplesPerBlockExpected, double sampleRate) override
Implementation of the AudioSource method.
Automatically locks and unlocks a mutex object.
Automatically unlocks and re-locks a mutex object.
A type of AudioSource which can be repositioned.
A general-purpose range object, that simply represents any linear range with a start and end point.
Definition juce_Range.h:40
static void JUCE_CALLTYPE sleep(int milliseconds)
Suspends the execution of the current thread until the specified timeout period has elapsed (note tha...
A thread that keeps a list of clients, and calls each one in turn, giving them all a chance to run so...
void removeTimeSliceClient(TimeSliceClient *clientToRemove)
Removes a client from the list.
void addTimeSliceClient(TimeSliceClient *clientToAdd, int millisecondsBeforeStarting=0)
Adds a client to the list.
void moveToFrontOfQueue(TimeSliceClient *clientToMove)
If the given client is waiting in the queue, it will be moved to the front and given a time-slice as ...
static uint32 getMillisecondCounter() noexcept
Returns the number of millisecs since a fixed event (usually system startup).
void signal() const
Wakes up any threads that are currently waiting on this object.
bool wait(double timeOutMilliseconds=-1.0) const
Suspends the calling thread until the event has been signalled.
#define jassert(expression)
Platform-independent assertion macro.
typedef int
T load(T... args)
T max(T... args)
JUCE Namespace.
CriticalSection::ScopedLockType ScopedLock
Automatically locks and unlocks a CriticalSection object.
constexpr bool approximatelyEqual(Type a, Type b, Tolerance< Type > tolerance=Tolerance< Type >{} .withAbsolute(std::numeric_limits< Type >::min()) .withRelative(std::numeric_limits< Type >::epsilon()))
Returns true if the two floating-point numbers are approximately equal.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Constrains a value to keep it within a given range.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
unsigned int uint32
A platform-independent 32-bit unsigned integer type.
long long int64
A platform-independent 64-bit integer type.
Used by AudioSource::getNextAudioBlock().
int numSamples
The number of samples in the buffer which the callback is expected to fill with data.
void clearActiveBufferRegion() const
Convenient method to clear the buffer if the source is not producing any data.
AudioBuffer< float > * buffer
The destination buffer to fill with audio data.
int startSample
The first sample in the buffer from which the callback is expected to write data.