LMMS
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,
29 int bufferSizeSamples,
30 int numChannels,
31 bool prefillBufferOnPrepareToPlay)
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//==============================================================================
50void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double newSampleRate)
51{
52 auto bufferSizeNeeded = jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer);
53
54 if (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
72
73 backgroundThread.addTimeSliceClient (this);
74
75 do
76 {
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
108 info.clearActiveBufferRegion();
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
133 if (startBufferIndex < endBufferIndex)
134 {
135 info.buffer->copyFrom (chan, info.startSample + validStart,
136 buffer,
137 chan, startBufferIndex,
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,
146 chan, startBufferIndex,
147 initialSize);
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
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
232{
233 int64 newBVS, newBVE, sectionToReadStart, sectionToReadEnd;
234
235 {
236 const ScopedLock sl (bufferRangeLock);
237
239 {
242 bufferValidEnd = 0;
243 }
244
245 newBVS = jmax ((int64) 0, nextPlayPos.load());
246 newBVE = newBVS + buffer.getNumSamples() - 4;
247 sectionToReadStart = 0;
248 sectionToReadEnd = 0;
249
250 constexpr int maxChunkSize = 2048;
251
252 if (newBVS < bufferValidStart || newBVS >= bufferValidEnd)
253 {
254 newBVE = jmin (newBVE, newBVS + maxChunkSize);
255
256 sectionToReadStart = newBVS;
257 sectionToReadEnd = newBVE;
258
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;
268 sectionToReadEnd = newBVE;
269
270 bufferValidStart = newBVS;
272 }
273 }
274
275 if (sectionToReadStart == sectionToReadEnd)
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
283 if (bufferIndexStart < bufferIndexEnd)
284 {
285 readBufferSection (sectionToReadStart,
286 (int) (sectionToReadEnd - sectionToReadStart),
287 bufferIndexStart);
288 }
289 else
290 {
291 const auto initialSize = buffer.getNumSamples() - bufferIndexStart;
292
293 readBufferSection (sectionToReadStart,
294 initialSize,
295 bufferIndexStart);
296
297 readBufferSection (sectionToReadStart + initialSize,
298 (int) (sectionToReadEnd - 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
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
325{
326 return readNextBufferChunk() ? 1 : 100;
327}
328
329} // namespace juce
Type jmin(const Type a, const Type b)
Definition MathsFunctions.h:60
Type jmax(const Type a, const Type b)
Definition MathsFunctions.h:48
void getNextAudioBlock(const AudioSourceChannelInfo &) override
Definition juce_BufferingAudioSource.cpp:101
OptionalScopedPointer< PositionableAudioSource > source
Definition juce_BufferingAudioSource.h:108
CriticalSection callbackLock
Definition juce_BufferingAudioSource.h:112
void setNextReadPosition(int64 newPosition) override
Definition juce_BufferingAudioSource.cpp:213
WaitableEvent bufferReadyEvent
Definition juce_BufferingAudioSource.h:113
CriticalSection bufferRangeLock
Definition juce_BufferingAudioSource.h:112
int useTimeSlice() override
Definition juce_BufferingAudioSource.cpp:324
int numberOfChannels
Definition juce_BufferingAudioSource.h:110
int64 getTotalLength() const override
Definition juce_BufferingAudioSource.h:89
~BufferingAudioSource() override
Definition juce_BufferingAudioSource.cpp:44
bool isPrepared
Definition juce_BufferingAudioSource.h:117
bool readNextBufferChunk()
Definition juce_BufferingAudioSource.cpp:231
BufferingAudioSource(PositionableAudioSource *source, TimeSliceThread &backgroundThread, bool deleteSourceWhenDeleted, int numberOfSamplesToBuffer, int numberOfChannels=2, bool prefillBufferOnPrepareToPlay=true)
Definition juce_BufferingAudioSource.cpp:26
int numberOfSamplesToBuffer
Definition juce_BufferingAudioSource.h:110
bool isLooping() const override
Definition juce_BufferingAudioSource.h:92
AudioBuffer< float > buffer
Definition juce_BufferingAudioSource.h:111
double sampleRate
Definition juce_BufferingAudioSource.h:116
TimeSliceThread & backgroundThread
Definition juce_BufferingAudioSource.h:109
Range< int > getValidBufferRange(int numSamples) const
Definition juce_BufferingAudioSource.cpp:221
int64 bufferValidStart
Definition juce_BufferingAudioSource.h:114
bool wasSourceLooping
Definition juce_BufferingAudioSource.h:117
void readBufferSection(int64 start, int length, int bufferOffset)
Definition juce_BufferingAudioSource.cpp:313
int64 bufferValidEnd
Definition juce_BufferingAudioSource.h:114
const bool prefillBuffer
Definition juce_BufferingAudioSource.h:118
void releaseResources() override
Definition juce_BufferingAudioSource.cpp:87
bool waitForNextAudioBlockReady(const AudioSourceChannelInfo &info, const uint32 timeout)
Definition juce_BufferingAudioSource.cpp:160
int64 getNextReadPosition() const override
Definition juce_BufferingAudioSource.cpp:203
std::atomic< int64 > nextPlayPos
Definition juce_BufferingAudioSource.h:115
void prepareToPlay(int samplesPerBlockExpected, double sampleRate) override
Definition juce_BufferingAudioSource.cpp:50
Definition juce_Range.h:40
static void JUCE_CALLTYPE sleep(int milliseconds)
Definition juce_posix_SharedCode.h:44
static uint32 getMillisecondCounter() noexcept
Definition juce_Time.cpp:241
friend class TimeSliceThread
Definition juce_TimeSliceThread.h:68
unsigned s
Definition inflate.c:1555
static double timeout
Definition pugl.h:1799
virtual ASIOError start()=0
struct backing_store_struct * info
Definition jmemsys.h:183
#define jassert(expression)
Definition carla_juce.cpp:31
CriticalSection::ScopedLockType ScopedLock
Definition juce_CriticalSection.h:186
constexpr Type jmin(Type a, Type b)
Definition juce_MathsFunctions.h:106
unsigned int uint32
Definition juce_MathsFunctions.h:45
constexpr Type jmax(Type a, Type b)
Definition juce_MathsFunctions.h:94
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Definition juce_MathsFunctions.h:262
long long int64
Definition juce_MathsFunctions.h:54
CriticalSection::ScopedUnlockType ScopedUnlock
Definition juce_CriticalSection.h:226
png_uint_32 length
Definition png.c:2247
Definition juce_AudioSource.h:33
typedef int(UZ_EXP MsgFn)()