33 source (sourceReader), thread (timeSliceThread),
34 numBlocks (1 + (samplesToBuffer / samplesPerBlock))
46BufferingAudioReader::~BufferingAudioReader()
53 timeoutMs = timeoutMilliseconds;
57 int64 startSampleInFile,
int numSamples)
64 nextReadPosition = startSampleInFile;
66 bool allSamplesRead =
true;
68 while (numSamples > 0)
70 if (
auto block = getBlockContaining (startSampleInFile))
72 auto offset = (int) (startSampleInFile - block->range.getStart());
73 auto numToDo = jmin (numSamples, (
int) (block->range.getEnd() - startSampleInFile));
75 for (
int j = 0; j < numDestChannels; ++j)
77 if (
auto* dest = (
float*) destSamples[j])
79 dest += startOffsetInDestBuffer;
82 FloatVectorOperations::copy (dest, block->buffer.getReadPointer (j, offset), numToDo);
84 FloatVectorOperations::clear (dest, numToDo);
88 startOffsetInDestBuffer += numToDo;
89 startSampleInFile += numToDo;
90 numSamples -= numToDo;
92 allSamplesRead = allSamplesRead && block->allSamplesRead;
98 for (
int j = 0; j < numDestChannels; ++j)
99 if (
auto* dest = (
float*) destSamples[j])
100 FloatVectorOperations::clear (dest + startOffsetInDestBuffer, numSamples);
102 allSamplesRead =
false;
113 return allSamplesRead;
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))
123BufferingAudioReader::BufferedBlock* BufferingAudioReader::getBlockContaining (int64 pos)
const noexcept
125 for (
auto* b : blocks)
126 if (b->range.contains (pos))
132int BufferingAudioReader::useTimeSlice()
134 return readNextBufferChunk() ? 1 : 100;
137bool BufferingAudioReader::readNextBufferChunk()
139 auto pos = (nextReadPosition.load() / samplesPerBlock) * samplesPerBlock;
140 auto endPos = jmin (
lengthInSamples, pos + numBlocks * samplesPerBlock);
142 OwnedArray<BufferedBlock> newBlocks;
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));
148 if (newBlocks.size() == numBlocks)
150 newBlocks.clear (
false);
154 for (
auto p = pos; p < endPos; p += samplesPerBlock)
156 if (getBlockContaining (p) ==
nullptr)
158 newBlocks.add (
new BufferedBlock (*source, p, samplesPerBlock));
164 const ScopedLock sl (lock);
165 newBlocks.swapWith (blocks);
168 for (
int i = blocks.size(); --i >= 0;)
169 newBlocks.removeObject (blocks.getUnchecked (i),
false);
179static bool isSilent (
const AudioBuffer<float>& b)
181 for (
int channel = 0; channel < b.getNumChannels(); ++channel)
182 if (b.findMinMax (channel, 0, b.getNumSamples()) != Range<float>{})
188struct TestAudioFormatReader :
public AudioFormatReader
190 explicit TestAudioFormatReader (
const AudioBuffer<float>* b)
191 : AudioFormatReader (nullptr, {}),
194 jassert (buffer !=
nullptr);
195 sampleRate = 44100.0f;
197 usesFloatingPointData =
true;
198 lengthInSamples = buffer->getNumSamples();
199 numChannels = (
unsigned int) buffer->getNumChannels();
202 bool readSamples (
int*
const* destChannels,
int numDestChannels,
int startOffsetInDestBuffer,
203 int64 startSampleInFile,
int numSamples)
override
205 clearSamplesBeyondAvailableLength (destChannels, numDestChannels, startOffsetInDestBuffer,
206 startSampleInFile, numSamples, lengthInSamples);
211 for (
int j = 0; j < numDestChannels; ++j)
213 static_assert (
sizeof (int) ==
sizeof (
float),
214 "Int and float size must match in order for pointer arithmetic to work correctly");
216 if (
auto* dest =
reinterpret_cast<float*
> (destChannels[j]))
218 dest += startOffsetInDestBuffer;
220 if (j < (
int) numChannels)
221 FloatVectorOperations::copy (dest, buffer->getReadPointer (j, (
int) startSampleInFile), numSamples);
223 FloatVectorOperations::clear (dest, numSamples);
230 const AudioBuffer<float>* buffer;
233static AudioBuffer<float> generateTestBuffer (Random& random,
int bufferSize)
235 AudioBuffer<float> buffer { 2, bufferSize };
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());
244class BufferingAudioReaderTests final :
public UnitTest
247 BufferingAudioReaderTests() : UnitTest (
"BufferingAudioReader", UnitTestCategories::audio) {}
249 void runTest()
override
251 TimeSliceThread thread (
"TestBackgroundThread");
254 beginTest (
"Reading samples from a blocked reader should produce silence");
256 struct BlockingReader final :
public TestAudioFormatReader
258 explicit BlockingReader (
const AudioBuffer<float>* b)
259 : TestAudioFormatReader (b)
263 bool readSamples (
int*
const* destChannels,
265 int startOffsetInDestBuffer,
266 int64 startSampleInFile,
267 int numSamples)
override
270 return TestAudioFormatReader::readSamples (destChannels, numDestChannels, startOffsetInDestBuffer, startSampleInFile, numSamples);
273 WaitableEvent unblock;
276 Random random { getRandom() };
277 constexpr auto bufferSize = 1024;
279 const auto source = generateTestBuffer (random, bufferSize);
280 expect (! isSilent (source));
282 auto* blockingReader =
new BlockingReader (&source);
283 BufferingAudioReader reader (blockingReader, thread, bufferSize);
285 auto destination = generateTestBuffer (random, bufferSize);
286 expect (! isSilent (destination));
288 read (reader, destination);
289 expect (isSilent (destination));
291 blockingReader->unblock.signal();
294 beginTest (
"Reading samples from a reader should produce the same samples as its source");
296 Random random { getRandom() };
298 for (
auto i = 4; i < 18; ++i)
300 const auto bufferSize = 1 << i;
301 const auto source = generateTestBuffer (random, bufferSize);
302 expect (! isSilent (source));
304 BufferingAudioReader reader (
new TestAudioFormatReader (&source), thread, bufferSize);
305 reader.setReadTimeout (-1);
307 auto destination = generateTestBuffer (random, bufferSize);
308 expect (! isSilent (destination));
309 expect (source != destination);
311 read (reader, destination);
312 expect (source == destination);
318 void read (BufferingAudioReader& reader, AudioBuffer<float>& readBuffer)
320 constexpr int blockSize = 1024;
322 const auto numSamples = readBuffer.getNumSamples();
327 reader.read (&readBuffer, readPos, jmin (blockSize, numSamples - readPos), readPos,
true,
true);
329 readPos += blockSize;
331 if (readPos >= numSamples)
337static BufferingAudioReaderTests bufferingAudioReaderTests;
BufferingAudioReader(AudioFormatReader *sourceReader, TimeSliceThread &timeSliceThread, int samplesToBuffer)
void setReadTimeout(int timeoutMilliseconds) noexcept
bool readSamples(int *const *destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override
static void JUCE_CALLTYPE yield()
void removeTimeSliceClient(TimeSliceClient *clientToRemove)
void addTimeSliceClient(TimeSliceClient *clientToAdd, int millisecondsBeforeStarting=0)
static uint32 getMillisecondCounter() noexcept