OpenShot Audio Library | OpenShotAudio: juce_BufferingAudioSource.cpp Source File

OpenShot Audio Library | OpenShotAudio 0.4.0

Loading...
Searching...
No Matches
juce_BufferingAudioSource.cpp
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
26BufferingAudioSource::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
44BufferingAudioSource::~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 (! 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
87 void BufferingAudioSource::releaseResources()
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
101 void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
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
160 bool BufferingAudioSource::waitForNextAudioBlockReady (const AudioSourceChannelInfo& info, uint32 timeout)
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
195 now = Time::getMillisecondCounter();
196 elapsed = (now >= startTime ? now - startTime
197 : (std::numeric_limits<uint32>::max() - startTime) + now);
198 }
199
200 return false;
201}
202
203int64 BufferingAudioSource::getNextReadPosition() const
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
213 void BufferingAudioSource::setNextReadPosition (int64 newPosition)
214{
215 const ScopedLock sl (bufferRangeLock);
216
217 nextPlayPos = newPosition;
218 backgroundThread.moveToFrontOfQueue (this);
219}
220
221 Range<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{
233 int64 newBVS, newBVE, sectionToReadStart, sectionToReadEnd;
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;
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
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;
268 sectionToReadEnd = newBVE;
269
270 bufferValidStart = newBVS;
271 bufferValidEnd = jmin (bufferValidEnd, newBVE);
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
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
juce::AudioBuffer::setSize
void setSize(int newNumChannels, int newNumSamples, bool keepExistingContent=false, bool clearExtraSpace=false, bool avoidReallocating=false)
Definition juce_AudioSampleBuffer.h:367
juce::AudioBuffer::getNumChannels
int getNumChannels() const noexcept
Definition juce_AudioSampleBuffer.h:236
juce::AudioBuffer::getNumSamples
int getNumSamples() const noexcept
Definition juce_AudioSampleBuffer.h:242
juce::AudioBuffer::clear
void clear() noexcept
Definition juce_AudioSampleBuffer.h:556
juce::AudioBuffer::copyFrom
void copyFrom(int destChannel, int destStartSample, const AudioBuffer &source, int sourceChannel, int sourceStartSample, int numSamples) noexcept
Definition juce_AudioSampleBuffer.h:938
juce::BufferingAudioSource::getNextAudioBlock
void getNextAudioBlock(const AudioSourceChannelInfo &) override
Definition juce_BufferingAudioSource.cpp:101
juce::BufferingAudioSource::setNextReadPosition
void setNextReadPosition(int64 newPosition) override
Definition juce_BufferingAudioSource.cpp:213
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::waitForNextAudioBlockReady
bool waitForNextAudioBlockReady(const AudioSourceChannelInfo &info, uint32 timeout)
Definition juce_BufferingAudioSource.cpp:160
juce::BufferingAudioSource::isLooping
bool isLooping() const override
Definition juce_BufferingAudioSource.h:92
juce::BufferingAudioSource::releaseResources
void releaseResources() override
Definition juce_BufferingAudioSource.cpp:87
juce::BufferingAudioSource::getNextReadPosition
int64 getNextReadPosition() const override
Definition juce_BufferingAudioSource.cpp:203
juce::BufferingAudioSource::prepareToPlay
void prepareToPlay(int samplesPerBlockExpected, double sampleRate) override
Definition juce_BufferingAudioSource.cpp:50
juce::GenericScopedLock
Definition juce_ScopedLock.h:55
juce::GenericScopedUnlock
Definition juce_ScopedLock.h:126
juce::PositionableAudioSource
Definition juce_PositionableAudioSource.h:39
juce::Range
Definition juce_Range.h:40
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:241
juce::WaitableEvent::signal
void signal() const
Definition juce_WaitableEvent.cpp:57
juce::WaitableEvent::wait
bool wait(double timeOutMilliseconds=-1.0) const
Definition juce_WaitableEvent.cpp:31
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