OpenShot Audio Library | OpenShotAudio 0.3.2

Loading...
Searching...
No Matches
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
23 namespace juce
24{
25
26 BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* s,
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
44 BufferingAudioSource::~BufferingAudioSource()
45{
46 releaseResources();
47}
48
49//==============================================================================
50 void 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
83 void BufferingAudioSource::releaseResources()
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
97 void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
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
111 info.clearActiveBufferRegion();
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
158 bool BufferingAudioSource::waitForNextAudioBlockReady (const AudioSourceChannelInfo& info, uint32 timeout)
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
194 now = Time::getMillisecondCounter();
195 elapsed = (now >= startTime ? now - startTime
196 : (std::numeric_limits<uint32>::max() - startTime) + now);
197 }
198
199 return false;
200}
201
202int64 BufferingAudioSource::getNextReadPosition() const
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
212 void BufferingAudioSource::setNextReadPosition (int64 newPosition)
213{
214 const ScopedLock sl (bufferStartPosLock);
215
216 nextPlayPos = newPosition;
217 backgroundThread.moveToFrontOfQueue (this);
218}
219
220 bool 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
301 void 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
310 int BufferingAudioSource::useTimeSlice()
311{
312 return readNextBufferChunk() ? 1 : 100;
313}
314
315} // namespace juce
juce::AudioBuffer::setSize
void setSize(int newNumChannels, int newNumSamples, bool keepExistingContent=false, bool clearExtraSpace=false, bool avoidReallocating=false)
Definition: juce_AudioSampleBuffer.h:331
juce::AudioBuffer::getNumChannels
int getNumChannels() const noexcept
Definition: juce_AudioSampleBuffer.h:233
juce::AudioBuffer::getNumSamples
int getNumSamples() const noexcept
Definition: juce_AudioSampleBuffer.h:238
juce::AudioBuffer::clear
void clear() noexcept
Definition: juce_AudioSampleBuffer.h:506
juce::AudioBuffer::copyFrom
void copyFrom(int destChannel, int destStartSample, const AudioBuffer &source, int sourceChannel, int sourceStartSample, int numSamples) noexcept
Definition: juce_AudioSampleBuffer.h:836
juce::BufferingAudioSource::getNextAudioBlock
void getNextAudioBlock(const AudioSourceChannelInfo &) override
Definition: juce_BufferingAudioSource.cpp:97
juce::BufferingAudioSource::setNextReadPosition
void setNextReadPosition(int64 newPosition) override
Definition: juce_BufferingAudioSource.cpp:212
juce::BufferingAudioSource::getTotalLength
int64 getTotalLength() const override
Definition: juce_BufferingAudioSource.h:89
juce::BufferingAudioSource::~BufferingAudioSource
~BufferingAudioSource() override
Definition: juce_BufferingAudioSource.cpp:44
juce::BufferingAudioSource::BufferingAudioSource
BufferingAudioSource(PositionableAudioSource *source, TimeSliceThread &backgroundThread, bool deleteSourceWhenDeleted, int numberOfSamplesToBuffer, int numberOfChannels=2, bool prefillBufferOnPrepareToPlay=true)
Definition: juce_BufferingAudioSource.cpp:26
juce::BufferingAudioSource::isLooping
bool isLooping() const override
Definition: juce_BufferingAudioSource.h:92
juce::BufferingAudioSource::releaseResources
void releaseResources() override
Definition: juce_BufferingAudioSource.cpp:83
juce::BufferingAudioSource::waitForNextAudioBlockReady
bool waitForNextAudioBlockReady(const AudioSourceChannelInfo &info, const uint32 timeout)
Definition: juce_BufferingAudioSource.cpp:158
juce::BufferingAudioSource::getNextReadPosition
int64 getNextReadPosition() const override
Definition: juce_BufferingAudioSource.cpp:202
juce::BufferingAudioSource::prepareToPlay
void prepareToPlay(int samplesPerBlockExpected, double sampleRate) override
Definition: juce_BufferingAudioSource.cpp:50
juce::GenericScopedLock
Definition: juce_ScopedLock.h:55
juce::PositionableAudioSource
Definition: juce_PositionableAudioSource.h:39
juce::Thread::sleep
static void JUCE_CALLTYPE sleep(int milliseconds)
juce::TimeSliceThread
Definition: juce_TimeSliceThread.h:83
juce::TimeSliceThread::removeTimeSliceClient
void removeTimeSliceClient(TimeSliceClient *clientToRemove)
Definition: juce_TimeSliceThread.cpp:47
juce::TimeSliceThread::addTimeSliceClient
void addTimeSliceClient(TimeSliceClient *clientToAdd, int millisecondsBeforeStarting=0)
Definition: juce_TimeSliceThread.cpp:36
juce::TimeSliceThread::moveToFrontOfQueue
void moveToFrontOfQueue(TimeSliceClient *clientToMove)
Definition: juce_TimeSliceThread.cpp:79
juce::Time::getMillisecondCounter
static uint32 getMillisecondCounter() noexcept
Definition: juce_Time.cpp:226
juce::WaitableEvent::wait
bool wait(int timeOutMilliseconds=-1) const
Definition: juce_WaitableEvent.cpp:31
juce::WaitableEvent::signal
void signal() const
Definition: juce_WaitableEvent.cpp:57
juce::AudioSourceChannelInfo
Definition: juce_AudioSource.h:33
juce::AudioSourceChannelInfo::numSamples
int numSamples
Definition: juce_AudioSource.h:81
juce::AudioSourceChannelInfo::clearActiveBufferRegion
void clearActiveBufferRegion() const
Definition: juce_AudioSource.h:84
juce::AudioSourceChannelInfo::buffer
AudioBuffer< float > * buffer
Definition: juce_AudioSource.h:73
juce::AudioSourceChannelInfo::startSample
int startSample
Definition: juce_AudioSource.h:77