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_BufferingAudioFormatReader.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 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
30 TimeSliceThread& timeSliceThread,
32 : AudioFormatReader (nullptr, sourceReader->getFormatName()),
33 source (sourceReader), thread (timeSliceThread),
34 numBlocks (1 + (samplesToBuffer / samplesPerBlock))
35{
36 sampleRate = source->sampleRate;
37 lengthInSamples = source->lengthInSamples;
38 numChannels = source->numChannels;
39 metadataValues = source->metadataValues;
40 bitsPerSample = 32;
42
43 timeSliceThread.addTimeSliceClient (this);
44}
45
46BufferingAudioReader::~BufferingAudioReader()
47{
48 thread.removeTimeSliceClient (this);
49}
50
55
57 int64 startSampleInFile, int numSamples)
58{
59 auto startTime = Time::getMillisecondCounter();
62
63 const ScopedLock sl (lock);
64 nextReadPosition = startSampleInFile;
65
66 bool allSamplesRead = true;
67
68 while (numSamples > 0)
69 {
70 if (auto block = getBlockContaining (startSampleInFile))
71 {
72 auto offset = (int) (startSampleInFile - block->range.getStart());
73 auto numToDo = jmin (numSamples, (int) (block->range.getEnd() - startSampleInFile));
74
75 for (int j = 0; j < numDestChannels; ++j)
76 {
77 if (auto* dest = (float*) destSamples[j])
78 {
80
81 if (j < (int) numChannels)
82 FloatVectorOperations::copy (dest, block->buffer.getReadPointer (j, offset), numToDo);
83 else
84 FloatVectorOperations::clear (dest, numToDo);
85 }
86 }
87
90 numSamples -= numToDo;
91
92 allSamplesRead = allSamplesRead && block->allSamplesRead;
93 }
94 else
95 {
96 if (timeoutMs >= 0 && Time::getMillisecondCounter() >= startTime + (uint32) timeoutMs)
97 {
98 for (int j = 0; j < numDestChannels; ++j)
99 if (auto* dest = (float*) destSamples[j])
100 FloatVectorOperations::clear (dest + startOffsetInDestBuffer, numSamples);
101
102 allSamplesRead = false;
103 break;
104 }
105 else
106 {
107 ScopedUnlock ul (lock);
109 }
110 }
111 }
112
113 return allSamplesRead;
114}
115
116BufferingAudioReader::BufferedBlock::BufferedBlock (AudioFormatReader& reader, int64 pos, int numSamples)
117 : range (pos, pos + numSamples),
118 buffer ((int) reader.numChannels, numSamples),
119 allSamplesRead (reader.read (&buffer, 0, numSamples, pos, true, true))
120{
121}
122
123BufferingAudioReader::BufferedBlock* BufferingAudioReader::getBlockContaining (int64 pos) const noexcept
124{
125 for (auto* b : blocks)
126 if (b->range.contains (pos))
127 return b;
128
129 return nullptr;
130}
131
132int BufferingAudioReader::useTimeSlice()
133{
134 return readNextBufferChunk() ? 1 : 100;
135}
136
137bool BufferingAudioReader::readNextBufferChunk()
138{
139 auto pos = (nextReadPosition.load() / samplesPerBlock) * samplesPerBlock;
140 auto endPos = jmin (lengthInSamples, pos + numBlocks * samplesPerBlock);
141
142 OwnedArray<BufferedBlock> newBlocks;
143
144 for (int i = blocks.size(); --i >= 0;)
145 if (blocks.getUnchecked (i)->range.intersects (Range<int64> (pos, endPos)))
146 newBlocks.add (blocks.getUnchecked (i));
147
148 if (newBlocks.size() == numBlocks)
149 {
150 newBlocks.clear (false);
151 return false;
152 }
153
154 for (auto p = pos; p < endPos; p += samplesPerBlock)
155 {
156 if (getBlockContaining (p) == nullptr)
157 {
158 newBlocks.add (new BufferedBlock (*source, p, samplesPerBlock));
159 break; // just do one block
160 }
161 }
162
163 {
164 const ScopedLock sl (lock);
165 newBlocks.swapWith (blocks);
166 }
167
168 for (int i = blocks.size(); --i >= 0;)
169 newBlocks.removeObject (blocks.getUnchecked (i), false);
170
171 return true;
172}
173
174
175//==============================================================================
176//==============================================================================
177#if JUCE_UNIT_TESTS
178
179static bool isSilent (const AudioBuffer<float>& b)
180{
181 for (int channel = 0; channel < b.getNumChannels(); ++channel)
182 if (b.findMinMax (channel, 0, b.getNumSamples()) != Range<float>{})
183 return false;
184
185 return true;
186}
187
188struct TestAudioFormatReader : public AudioFormatReader
189{
190 explicit TestAudioFormatReader (const AudioBuffer<float>* b)
191 : AudioFormatReader (nullptr, {}),
192 buffer (b)
193 {
194 jassert (buffer != nullptr);
195 sampleRate = 44100.0f;
196 bitsPerSample = 32;
197 usesFloatingPointData = true;
198 lengthInSamples = buffer->getNumSamples();
199 numChannels = (unsigned int) buffer->getNumChannels();
200 }
201
202 bool readSamples (int* const* destChannels, int numDestChannels, int startOffsetInDestBuffer,
203 int64 startSampleInFile, int numSamples) override
204 {
205 clearSamplesBeyondAvailableLength (destChannels, numDestChannels, startOffsetInDestBuffer,
206 startSampleInFile, numSamples, lengthInSamples);
207
208 if (numSamples <= 0)
209 return true;
210
211 for (int j = 0; j < numDestChannels; ++j)
212 {
213 static_assert (sizeof (int) == sizeof (float),
214 "Int and float size must match in order for pointer arithmetic to work correctly");
215
216 if (auto* dest = reinterpret_cast<float*> (destChannels[j]))
217 {
219
220 if (j < (int) numChannels)
221 FloatVectorOperations::copy (dest, buffer->getReadPointer (j, (int) startSampleInFile), numSamples);
222 else
223 FloatVectorOperations::clear (dest, numSamples);
224 }
225 }
226
227 return true;
228 }
229
230 const AudioBuffer<float>* buffer;
231};
232
233static AudioBuffer<float> generateTestBuffer (Random& random, int bufferSize)
234{
235 AudioBuffer<float> buffer { 2, bufferSize };
236
237 for (int channel = 0; channel < buffer.getNumChannels(); ++channel)
238 for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
239 buffer.setSample (channel, sample, random.nextFloat());
240
241 return buffer;
242}
243
244class BufferingAudioReaderTests final : public UnitTest
245{
246public:
247 BufferingAudioReaderTests() : UnitTest ("BufferingAudioReader", UnitTestCategories::audio) {}
248
249 void runTest() override
250 {
251 TimeSliceThread thread ("TestBackgroundThread");
252 thread.startThread (Thread::Priority::normal);
253
254 beginTest ("Reading samples from a blocked reader should produce silence");
255 {
257 {
258 explicit BlockingReader (const AudioBuffer<float>* b)
259 : TestAudioFormatReader (b)
260 {
261 }
262
263 bool readSamples (int* const* destChannels,
264 int numDestChannels,
265 int startOffsetInDestBuffer,
266 int64 startSampleInFile,
267 int numSamples) override
268 {
269 unblock.wait();
270 return TestAudioFormatReader::readSamples (destChannels, numDestChannels, startOffsetInDestBuffer, startSampleInFile, numSamples);
271 }
272
273 WaitableEvent unblock;
274 };
275
276 Random random { getRandom() };
277 constexpr auto bufferSize = 1024;
278
279 const auto source = generateTestBuffer (random, bufferSize);
280 expect (! isSilent (source));
281
282 auto* blockingReader = new BlockingReader (&source);
283 BufferingAudioReader reader (blockingReader, thread, bufferSize);
284
285 auto destination = generateTestBuffer (random, bufferSize);
286 expect (! isSilent (destination));
287
288 read (reader, destination);
289 expect (isSilent (destination));
290
291 blockingReader->unblock.signal();
292 }
293
294 beginTest ("Reading samples from a reader should produce the same samples as its source");
295 {
296 Random random { getRandom() };
297
298 for (auto i = 4; i < 18; ++i)
299 {
300 const auto bufferSize = 1 << i;
301 const auto source = generateTestBuffer (random, bufferSize);
302 expect (! isSilent (source));
303
304 BufferingAudioReader reader (new TestAudioFormatReader (&source), thread, bufferSize);
305 reader.setReadTimeout (-1);
306
307 auto destination = generateTestBuffer (random, bufferSize);
308 expect (! isSilent (destination));
309 expect (source != destination);
310
311 read (reader, destination);
312 expect (source == destination);
313 }
314 }
315 }
316
317private:
318 void read (BufferingAudioReader& reader, AudioBuffer<float>& readBuffer)
319 {
320 constexpr int blockSize = 1024;
321
322 const auto numSamples = readBuffer.getNumSamples();
323 int readPos = 0;
324
325 for (;;)
326 {
327 reader.read (&readBuffer, readPos, jmin (blockSize, numSamples - readPos), readPos, true, true);
328
329 readPos += blockSize;
330
331 if (readPos >= numSamples)
332 break;
333 }
334 }
335};
336
338
339#endif
340
341} // namespace juce
Reads samples from an audio file stream.
bool usesFloatingPointData
Indicates whether the data is floating-point or fixed.
StringPairArray metadataValues
A set of metadata values that the reader has pulled out of the stream.
static void clearSamplesBeyondAvailableLength(int *const *destChannels, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int &numSamples, int64 fileLengthInSamples)
Used by AudioFormatReader subclasses to clear any parts of the data blocks that lie beyond the end of...
int64 lengthInSamples
The total number of samples in the audio stream.
double sampleRate
The sample-rate of the stream.
unsigned int bitsPerSample
The number of bits per sample, e.g.
unsigned int numChannels
The total number of channels in the audio stream.
BufferingAudioReader(AudioFormatReader *sourceReader, TimeSliceThread &timeSliceThread, int samplesToBuffer)
Creates a reader.
void setReadTimeout(int timeoutMilliseconds) noexcept
Sets a number of milliseconds that the reader can block for in its readSamples() method before giving...
bool readSamples(int *const *destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override
Subclasses must implement this method to perform the low-level read operation.
Automatically locks and unlocks a mutex object.
Automatically unlocks and re-locks a mutex object.
int size() const noexcept
Returns the number of items currently in the array.
ObjectClass * getUnchecked(int index) const noexcept
Returns a pointer to the object at this index in the array, without checking whether the index is in-...
static void JUCE_CALLTYPE yield()
Yields the current thread's CPU time-slot and allows a new thread to run.
@ normal
The OS default.
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.
static uint32 getMillisecondCounter() noexcept
Returns the number of millisecs since a fixed event (usually system startup).
random
#define jassert(expression)
Platform-independent assertion macro.
typedef int
T load(T... args)
JUCE Namespace.
CriticalSection::ScopedLockType ScopedLock
Automatically locks and unlocks a CriticalSection object.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
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.
read
T sample(T... args)