OpenShot Audio Library | OpenShotAudio  0.3.1
juce_AudioFormatReader.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  By using JUCE, you agree to the terms of both the JUCE 5 End-User License
11  Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
12  27th April 2017).
13 
14  End User License Agreement: www.juce.com/juce-5-licence
15  Privacy Policy: www.juce.com/juce-5-privacy-policy
16 
17  Or: You may also use this code under the terms of the GPL v3 (see
18  www.gnu.org/licenses).
19 
20  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
21  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
22  DISCLAIMED.
23 
24  ==============================================================================
25 */
26 
27 namespace juce
28 {
29 
31  : input (in), formatName (name)
32 {
33 }
34 
36 {
37  delete input;
38 }
39 
40 static void convertFixedToFloat (int* const* channels, int numChannels, int numSamples)
41 {
42  for (int i = 0; i < numChannels; ++i)
43  if (auto d = channels[i])
44  FloatVectorOperations::convertFixedToFloat (reinterpret_cast<float*> (d), d, 1.0f / 0x7fffffff, numSamples);
45 }
46 
47 bool AudioFormatReader::read (float* const* destChannels, int numDestChannels,
48  int64 startSampleInSource, int numSamplesToRead)
49 {
50  auto channelsAsInt = reinterpret_cast<int* const*> (destChannels);
51 
52  if (! read (channelsAsInt, numDestChannels, startSampleInSource, numSamplesToRead, false))
53  return false;
54 
56  convertFixedToFloat (channelsAsInt, numDestChannels, numSamplesToRead);
57 
58  return true;
59 }
60 
61 bool AudioFormatReader::read (int* const* destChannels,
62  int numDestChannels,
63  int64 startSampleInSource,
64  int numSamplesToRead,
65  bool fillLeftoverChannelsWithCopies)
66 {
67  jassert (numDestChannels > 0); // you have to actually give this some channels to work with!
68 
69  auto originalNumSamplesToRead = (size_t) numSamplesToRead;
70  int startOffsetInDestBuffer = 0;
71 
72  if (startSampleInSource < 0)
73  {
74  auto silence = (int) jmin (-startSampleInSource, (int64) numSamplesToRead);
75 
76  for (int i = numDestChannels; --i >= 0;)
77  if (auto d = destChannels[i])
78  zeromem (d, (size_t) silence * sizeof (int));
79 
80  startOffsetInDestBuffer += silence;
81  numSamplesToRead -= silence;
82  startSampleInSource = 0;
83  }
84 
85  if (numSamplesToRead <= 0)
86  return true;
87 
88  if (! readSamples (const_cast<int**> (destChannels),
89  jmin ((int) numChannels, numDestChannels), startOffsetInDestBuffer,
90  startSampleInSource, numSamplesToRead))
91  return false;
92 
93  if (numDestChannels > (int) numChannels)
94  {
95  if (fillLeftoverChannelsWithCopies)
96  {
97  auto lastFullChannel = destChannels[0];
98 
99  for (int i = (int) numChannels; --i > 0;)
100  {
101  if (destChannels[i] != nullptr)
102  {
103  lastFullChannel = destChannels[i];
104  break;
105  }
106  }
107 
108  if (lastFullChannel != nullptr)
109  for (int i = (int) numChannels; i < numDestChannels; ++i)
110  if (auto d = destChannels[i])
111  memcpy (d, lastFullChannel, sizeof (int) * originalNumSamplesToRead);
112  }
113  else
114  {
115  for (int i = (int) numChannels; i < numDestChannels; ++i)
116  if (auto d = destChannels[i])
117  zeromem (d, sizeof (int) * originalNumSamplesToRead);
118  }
119  }
120 
121  return true;
122 }
123 
124 static void readChannels (AudioFormatReader& reader, int** chans, AudioBuffer<float>* buffer,
125  int startSample, int numSamples, int64 readerStartSample, int numTargetChannels,
126  bool convertToFloat)
127 {
128  for (int j = 0; j < numTargetChannels; ++j)
129  chans[j] = reinterpret_cast<int*> (buffer->getWritePointer (j, startSample));
130 
131  chans[numTargetChannels] = nullptr;
132  reader.read (chans, numTargetChannels, readerStartSample, numSamples, true);
133 
134  if (convertToFloat)
135  convertFixedToFloat (chans, numTargetChannels, numSamples);
136 }
137 
139  int startSample,
140  int numSamples,
141  int64 readerStartSample,
142  bool useReaderLeftChan,
143  bool useReaderRightChan)
144 {
145  jassert (buffer != nullptr);
146  jassert (startSample >= 0 && startSample + numSamples <= buffer->getNumSamples());
147 
148  if (numSamples > 0)
149  {
150  auto numTargetChannels = buffer->getNumChannels();
151 
152  if (numTargetChannels <= 2)
153  {
154  int* dests[2] = { reinterpret_cast<int*> (buffer->getWritePointer (0, startSample)),
155  reinterpret_cast<int*> (numTargetChannels > 1 ? buffer->getWritePointer (1, startSample) : nullptr) };
156  int* chans[3] = {};
157 
158  if (useReaderLeftChan == useReaderRightChan)
159  {
160  chans[0] = dests[0];
161 
162  if (numChannels > 1)
163  chans[1] = dests[1];
164  }
165  else if (useReaderLeftChan || (numChannels == 1))
166  {
167  chans[0] = dests[0];
168  }
169  else if (useReaderRightChan)
170  {
171  chans[1] = dests[0];
172  }
173 
174  read (chans, 2, readerStartSample, numSamples, true);
175 
176  // if the target's stereo and the source is mono, dupe the first channel..
177  if (numTargetChannels > 1 && (chans[0] == nullptr || chans[1] == nullptr))
178  memcpy (dests[1], dests[0], (size_t) numSamples * sizeof (float));
179 
180  if (! usesFloatingPointData)
181  convertFixedToFloat (dests, 2, numSamples);
182  }
183  else if (numTargetChannels <= 64)
184  {
185  int* chans[65];
186  readChannels (*this, chans, buffer, startSample, numSamples,
187  readerStartSample, numTargetChannels, ! usesFloatingPointData);
188  }
189  else
190  {
191  HeapBlock<int*> chans (numTargetChannels + 1);
192  readChannels (*this, chans, buffer, startSample, numSamples,
193  readerStartSample, numTargetChannels, ! usesFloatingPointData);
194  }
195  }
196 }
197 
198 void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples,
199  Range<float>* const results, const int channelsToRead)
200 {
201  jassert (channelsToRead > 0 && channelsToRead <= (int) numChannels);
202 
203  if (numSamples <= 0)
204  {
205  for (int i = 0; i < channelsToRead; ++i)
206  results[i] = Range<float>();
207 
208  return;
209  }
210 
211  auto bufferSize = (int) jmin (numSamples, (int64) 4096);
212  AudioBuffer<float> tempSampleBuffer ((int) channelsToRead, bufferSize);
213 
214  auto floatBuffer = tempSampleBuffer.getArrayOfWritePointers();
215  auto intBuffer = reinterpret_cast<int* const*> (floatBuffer);
216  bool isFirstBlock = true;
217 
218  while (numSamples > 0)
219  {
220  auto numToDo = (int) jmin (numSamples, (int64) bufferSize);
221 
222  if (! read (intBuffer, channelsToRead, startSampleInFile, numToDo, false))
223  break;
224 
225  for (int i = 0; i < channelsToRead; ++i)
226  {
227  Range<float> r;
228 
230  {
231  r = FloatVectorOperations::findMinAndMax (floatBuffer[i], numToDo);
232  }
233  else
234  {
235  auto intRange = Range<int>::findMinAndMax (intBuffer[i], numToDo);
236 
237  r = Range<float> (intRange.getStart() / (float) std::numeric_limits<int>::max(),
238  intRange.getEnd() / (float) std::numeric_limits<int>::max());
239  }
240 
241  results[i] = isFirstBlock ? r : results[i].getUnionWith (r);
242  }
243 
244  isFirstBlock = false;
245  numSamples -= numToDo;
246  startSampleInFile += numToDo;
247  }
248 }
249 
250 void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples,
251  float& lowestLeft, float& highestLeft,
252  float& lowestRight, float& highestRight)
253 {
254  Range<float> levels[2];
255 
256  if (numChannels < 2)
257  {
258  readMaxLevels (startSampleInFile, numSamples, levels, (int) numChannels);
259  levels[1] = levels[0];
260  }
261  else
262  {
263  readMaxLevels (startSampleInFile, numSamples, levels, 2);
264  }
265 
266  lowestLeft = levels[0].getStart();
267  highestLeft = levels[0].getEnd();
268  lowestRight = levels[1].getStart();
269  highestRight = levels[1].getEnd();
270 }
271 
272 int64 AudioFormatReader::searchForLevel (int64 startSample,
273  int64 numSamplesToSearch,
274  double magnitudeRangeMinimum,
275  double magnitudeRangeMaximum,
276  int minimumConsecutiveSamples)
277 {
278  if (numSamplesToSearch == 0)
279  return -1;
280 
281  const int bufferSize = 4096;
282  HeapBlock<int> tempSpace (bufferSize * 2 + 64);
283 
284  int* tempBuffer[3] = { tempSpace.get(),
285  tempSpace.get() + bufferSize,
286  nullptr };
287 
288  int consecutive = 0;
289  int64 firstMatchPos = -1;
290 
291  jassert (magnitudeRangeMaximum > magnitudeRangeMinimum);
292 
293  auto doubleMin = jlimit (0.0, (double) std::numeric_limits<int>::max(), magnitudeRangeMinimum * std::numeric_limits<int>::max());
294  auto doubleMax = jlimit (doubleMin, (double) std::numeric_limits<int>::max(), magnitudeRangeMaximum * std::numeric_limits<int>::max());
295  auto intMagnitudeRangeMinimum = roundToInt (doubleMin);
296  auto intMagnitudeRangeMaximum = roundToInt (doubleMax);
297 
298  while (numSamplesToSearch != 0)
299  {
300  auto numThisTime = (int) jmin (std::abs (numSamplesToSearch), (int64) bufferSize);
301  int64 bufferStart = startSample;
302 
303  if (numSamplesToSearch < 0)
304  bufferStart -= numThisTime;
305 
306  if (bufferStart >= lengthInSamples)
307  break;
308 
309  read (tempBuffer, 2, bufferStart, numThisTime, false);
310  auto num = numThisTime;
311 
312  while (--num >= 0)
313  {
314  if (numSamplesToSearch < 0)
315  --startSample;
316 
317  bool matches = false;
318  auto index = (int) (startSample - bufferStart);
319 
321  {
322  const float sample1 = std::abs (((float*) tempBuffer[0]) [index]);
323 
324  if (sample1 >= magnitudeRangeMinimum
325  && sample1 <= magnitudeRangeMaximum)
326  {
327  matches = true;
328  }
329  else if (numChannels > 1)
330  {
331  const float sample2 = std::abs (((float*) tempBuffer[1]) [index]);
332 
333  matches = (sample2 >= magnitudeRangeMinimum
334  && sample2 <= magnitudeRangeMaximum);
335  }
336  }
337  else
338  {
339  const int sample1 = std::abs (tempBuffer[0] [index]);
340 
341  if (sample1 >= intMagnitudeRangeMinimum
342  && sample1 <= intMagnitudeRangeMaximum)
343  {
344  matches = true;
345  }
346  else if (numChannels > 1)
347  {
348  const int sample2 = std::abs (tempBuffer[1][index]);
349 
350  matches = (sample2 >= intMagnitudeRangeMinimum
351  && sample2 <= intMagnitudeRangeMaximum);
352  }
353  }
354 
355  if (matches)
356  {
357  if (firstMatchPos < 0)
358  firstMatchPos = startSample;
359 
360  if (++consecutive >= minimumConsecutiveSamples)
361  {
362  if (firstMatchPos < 0 || firstMatchPos >= lengthInSamples)
363  return -1;
364 
365  return firstMatchPos;
366  }
367  }
368  else
369  {
370  consecutive = 0;
371  firstMatchPos = -1;
372  }
373 
374  if (numSamplesToSearch > 0)
375  ++startSample;
376  }
377 
378  if (numSamplesToSearch > 0)
379  numSamplesToSearch -= numThisTime;
380  else
381  numSamplesToSearch += numThisTime;
382  }
383 
384  return -1;
385 }
386 
388 {
389  return AudioChannelSet::canonicalChannelSet (static_cast<int> (numChannels));
390 }
391 
392 //==============================================================================
394  int64 start, int64 length, int frameSize)
395  : AudioFormatReader (nullptr, reader.getFormatName()), file (f),
396  dataChunkStart (start), dataLength (length), bytesPerFrame (frameSize)
397 {
398  sampleRate = reader.sampleRate;
399  bitsPerSample = reader.bitsPerSample;
401  numChannels = reader.numChannels;
404 }
405 
407 {
409 }
410 
412 {
413  if (map == nullptr || samplesToMap != mappedSection)
414  {
415  map.reset();
416 
417  const Range<int64> fileRange (sampleToFilePos (samplesToMap.getStart()),
418  sampleToFilePos (samplesToMap.getEnd()));
419 
420  map.reset (new MemoryMappedFile (file, fileRange, MemoryMappedFile::readOnly));
421 
422  if (map->getData() == nullptr)
423  map.reset();
424  else
425  mappedSection = Range<int64> (jmax ((int64) 0, filePosToSample (map->getRange().getStart() + (bytesPerFrame - 1))),
426  jmin (lengthInSamples, filePosToSample (map->getRange().getEnd())));
427  }
428 
429  return map != nullptr;
430 }
431 
432 static int memoryReadDummyVariable; // used to force the compiler not to optimise-away the read operation
433 
434 void MemoryMappedAudioFormatReader::touchSample (int64 sample) const noexcept
435 {
436  if (map != nullptr && mappedSection.contains (sample))
437  memoryReadDummyVariable += *(char*) sampleToPointer (sample);
438  else
439  jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
440 }
441 
442 } // namespace juce
Type * getWritePointer(int channelNumber) noexcept
Type ** getArrayOfWritePointers() noexcept
int getNumChannels() const noexcept
static AudioChannelSet JUCE_CALLTYPE canonicalChannelSet(int numChannels)
bool read(float *const *destChannels, int numDestChannels, int64 startSampleInSource, int numSamplesToRead)
int64 searchForLevel(int64 startSample, int64 numSamplesToSearch, double magnitudeRangeMinimum, double magnitudeRangeMaximum, int minimumConsecutiveSamples)
virtual AudioChannelSet getChannelLayout()
AudioFormatReader(InputStream *sourceStream, const String &formatName)
virtual void readMaxLevels(int64 startSample, int64 numSamples, Range< float > *results, int numChannelsToRead)
virtual bool readSamples(int **destChannels, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples)=0
static void JUCE_CALLTYPE convertFixedToFloat(float *dest, const int *src, float multiplier, int numValues) noexcept
static Range< float > JUCE_CALLTYPE findMinAndMax(const float *src, int numValues) noexcept
ElementType * get() const noexcept
MemoryMappedAudioFormatReader(const File &file, const AudioFormatReader &details, int64 dataChunkStart, int64 dataChunkLength, int bytesPerFrame)
void touchSample(int64 sample) const noexcept
int64 sampleToFilePos(int64 sample) const noexcept
virtual bool mapSectionOfFile(Range< int64 > samplesToMap)
int64 filePosToSample(int64 filePos) const noexcept
JUCE_CONSTEXPR ValueType getStart() const noexcept
Definition: juce_Range.h:80
static Range findMinAndMax(const ValueType *values, int numValues) noexcept
Definition: juce_Range.h:273
JUCE_CONSTEXPR Range getUnionWith(Range other) const noexcept
Definition: juce_Range.h:241
JUCE_CONSTEXPR ValueType getEnd() const noexcept
Definition: juce_Range.h:86