OpenShot Audio Library | OpenShotAudio  0.6.0
juce_BufferingAudioFormatReader.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  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 
26 namespace juce
27 {
28 
30  TimeSliceThread& timeSliceThread,
31  int samplesToBuffer)
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;
41  usesFloatingPointData = true;
42 
43  timeSliceThread.addTimeSliceClient (this);
44 }
45 
46 BufferingAudioReader::~BufferingAudioReader()
47 {
48  thread.removeTimeSliceClient (this);
49 }
50 
51 void BufferingAudioReader::setReadTimeout (int timeoutMilliseconds) noexcept
52 {
53  timeoutMs = timeoutMilliseconds;
54 }
55 
56 bool BufferingAudioReader::readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer,
57  int64 startSampleInFile, int numSamples)
58 {
59  auto startTime = Time::getMillisecondCounter();
60  clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
61  startSampleInFile, numSamples, lengthInSamples);
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  {
79  dest += startOffsetInDestBuffer;
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 
88  startOffsetInDestBuffer += numToDo;
89  startSampleInFile += numToDo;
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);
108  Thread::yield();
109  }
110  }
111  }
112 
113  return allSamplesRead;
114 }
115 
116 BufferingAudioReader::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 
123 BufferingAudioReader::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 
132 int BufferingAudioReader::useTimeSlice()
133 {
134  return readNextBufferChunk() ? 1 : 100;
135 }
136 
137 bool 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 
179 static 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 
188 struct 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  {
218  dest += startOffsetInDestBuffer;
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 
233 static 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 
244 class BufferingAudioReaderTests final : public UnitTest
245 {
246 public:
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  {
256  struct BlockingReader final : public TestAudioFormatReader
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 
317 private:
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 
337 static BufferingAudioReaderTests bufferingAudioReaderTests;
338 
339 #endif
340 
341 } // namespace juce
static void clearSamplesBeyondAvailableLength(int *const *destChannels, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int &numSamples, int64 fileLengthInSamples)
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
Definition: juce_Time.cpp:241