OpenShot Library | OpenShotAudio 0.2.2
juce_BufferingAudioSource.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2017 - ROLI Ltd.
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
45{
47}
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 bufferValidStart = 0;
69 bufferValidEnd = 0;
70
71 backgroundThread.addTimeSliceClient (this);
72
73 do
74 {
75 backgroundThread.moveToFrontOfQueue (this);
76 Thread::sleep (5);
77 }
78 while (prefillBuffer
79 && (bufferValidEnd - bufferValidStart < jmin (((int) newSampleRate) / 4, buffer.getNumSamples() / 2)));
80 }
81}
82
84{
85 isPrepared = false;
86 backgroundThread.removeTimeSliceClient (this);
87
88 buffer.setSize (numberOfChannels, 0);
89
90 // MSVC2015 seems to need this if statement to not generate a warning during linking.
91 // As source is set in the constructor, there is no way that source could
92 // ever equal this, but it seems to make MSVC2015 happy.
93 if (source != this)
94 source->releaseResources();
95}
96
98{
99 const ScopedLock sl (bufferStartPosLock);
100
101 auto start = bufferValidStart.load();
102 auto end = bufferValidEnd.load();
103 auto pos = nextPlayPos.load();
104
105 auto validStart = (int) (jlimit (start, end, pos) - pos);
106 auto validEnd = (int) (jlimit (start, end, pos + info.numSamples) - pos);
107
108 if (validStart == validEnd)
109 {
110 // total cache miss
112 }
113 else
114 {
115 if (validStart > 0)
116 info.buffer->clear (info.startSample, validStart); // partial cache miss at start
117
118 if (validEnd < info.numSamples)
119 info.buffer->clear (info.startSample + validEnd,
120 info.numSamples - validEnd); // partial cache miss at end
121
122 if (validStart < validEnd)
123 {
124 for (int chan = jmin (numberOfChannels, info.buffer->getNumChannels()); --chan >= 0;)
125 {
126 jassert (buffer.getNumSamples() > 0);
127 auto startBufferIndex = (int) ((validStart + nextPlayPos) % buffer.getNumSamples());
128 auto endBufferIndex = (int) ((validEnd + nextPlayPos) % buffer.getNumSamples());
129
130 if (startBufferIndex < endBufferIndex)
131 {
132 info.buffer->copyFrom (chan, info.startSample + validStart,
133 buffer,
134 chan, startBufferIndex,
135 validEnd - validStart);
136 }
137 else
138 {
139 auto initialSize = buffer.getNumSamples() - startBufferIndex;
140
141 info.buffer->copyFrom (chan, info.startSample + validStart,
142 buffer,
143 chan, startBufferIndex,
144 initialSize);
145
146 info.buffer->copyFrom (chan, info.startSample + validStart + initialSize,
147 buffer,
148 chan, 0,
149 (validEnd - validStart) - initialSize);
150 }
151 }
152 }
153
154 nextPlayPos += info.numSamples;
155 }
156}
157
159{
160 if (!source || source->getTotalLength() <= 0)
161 return false;
162
163 if (nextPlayPos + info.numSamples < 0)
164 return true;
165
166 if (! isLooping() && nextPlayPos > getTotalLength())
167 return true;
168
169 auto now = Time::getMillisecondCounter();
170 auto startTime = now;
171
172 auto elapsed = (now >= startTime ? now - startTime
173 : (std::numeric_limits<uint32>::max() - startTime) + now);
174
175 while (elapsed <= timeout)
176 {
177 {
178 const ScopedLock sl (bufferStartPosLock);
179
180 auto start = bufferValidStart.load();
181 auto end = bufferValidEnd.load();
182 auto pos = nextPlayPos.load();
183
184 auto validStart = static_cast<int> (jlimit (start, end, pos) - pos);
185 auto validEnd = static_cast<int> (jlimit (start, end, pos + info.numSamples) - pos);
186
187 if (validStart <= 0 && validStart < validEnd && validEnd >= info.numSamples)
188 return true;
189 }
190
191 if (elapsed < timeout && (! bufferReadyEvent.wait (static_cast<int> (timeout - elapsed))))
192 return false;
193
195 elapsed = (now >= startTime ? now - startTime
196 : (std::numeric_limits<uint32>::max() - startTime) + now);
197 }
198
199 return false;
200}
201
203{
204 jassert (source->getTotalLength() > 0);
205 auto pos = nextPlayPos.load();
206
207 return (source->isLooping() && nextPlayPos > 0)
208 ? pos % source->getTotalLength()
209 : pos;
210}
211
213{
214 const ScopedLock sl (bufferStartPosLock);
215
216 nextPlayPos = newPosition;
217 backgroundThread.moveToFrontOfQueue (this);
218}
219
220bool BufferingAudioSource::readNextBufferChunk()
221{
222 int64 newBVS, newBVE, sectionToReadStart, sectionToReadEnd;
223
224 {
225 const ScopedLock sl (bufferStartPosLock);
226
227 if (wasSourceLooping != isLooping())
228 {
229 wasSourceLooping = isLooping();
230 bufferValidStart = 0;
231 bufferValidEnd = 0;
232 }
233
234 newBVS = jmax ((int64) 0, nextPlayPos.load());
235 newBVE = newBVS + buffer.getNumSamples() - 4;
236 sectionToReadStart = 0;
237 sectionToReadEnd = 0;
238
239 const int maxChunkSize = 2048;
240
241 if (newBVS < bufferValidStart || newBVS >= bufferValidEnd)
242 {
243 newBVE = jmin (newBVE, newBVS + maxChunkSize);
244
245 sectionToReadStart = newBVS;
246 sectionToReadEnd = newBVE;
247
248 bufferValidStart = 0;
249 bufferValidEnd = 0;
250 }
251 else if (std::abs ((int) (newBVS - bufferValidStart)) > 512
252 || std::abs ((int) (newBVE - bufferValidEnd)) > 512)
253 {
254 newBVE = jmin (newBVE, bufferValidEnd + maxChunkSize);
255
256 sectionToReadStart = bufferValidEnd;
257 sectionToReadEnd = newBVE;
258
259 bufferValidStart = newBVS;
260 bufferValidEnd = jmin (bufferValidEnd.load(), newBVE);
261 }
262 }
263
264 if (sectionToReadStart == sectionToReadEnd)
265 return false;
266
267 jassert (buffer.getNumSamples() > 0);
268 auto bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples());
269 auto bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples());
270
271 if (bufferIndexStart < bufferIndexEnd)
272 {
273 readBufferSection (sectionToReadStart,
274 (int) (sectionToReadEnd - sectionToReadStart),
275 bufferIndexStart);
276 }
277 else
278 {
279 auto initialSize = buffer.getNumSamples() - bufferIndexStart;
280
281 readBufferSection (sectionToReadStart,
282 initialSize,
283 bufferIndexStart);
284
285 readBufferSection (sectionToReadStart + initialSize,
286 (int) (sectionToReadEnd - sectionToReadStart) - initialSize,
287 0);
288 }
289
290 {
291 const ScopedLock sl2 (bufferStartPosLock);
292
293 bufferValidStart = newBVS;
294 bufferValidEnd = newBVE;
295 }
296
297 bufferReadyEvent.signal();
298 return true;
299}
300
301void BufferingAudioSource::readBufferSection (int64 start, int length, int bufferOffset)
302{
303 if (source->getNextReadPosition() != start)
304 source->setNextReadPosition (start);
305
306 AudioSourceChannelInfo info (&buffer, bufferOffset, length);
307 source->getNextAudioBlock (info);
308}
309
310int BufferingAudioSource::useTimeSlice()
311{
312 return readNextBufferChunk() ? 1 : 100;
313}
314
315} // 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.
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 isLooping() const override
Implements the PositionableAudioSource method.
void releaseResources() override
Implementation of the AudioSource method.
bool waitForNextAudioBlockReady(const AudioSourceChannelInfo &info, const uint32 timeout)
A useful function to block until the next the buffer info can be filled.
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.
A type of AudioSource which can be repositioned.
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).
Definition: juce_Time.cpp:226
bool wait(int timeOutMilliseconds=-1) const
Suspends the calling thread until the event has been signalled.
void signal() const
Wakes up any threads that are currently waiting on this object.
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.