OpenShot Library | OpenShotAudio  0.2.2
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
Represents a set of audio channel types.
virtual bool mapSectionOfFile(Range< int64 > samplesToMap)
Attempts to map a section of the file into memory.
AudioFormatReader(InputStream *sourceStream, const String &formatName)
Creates an AudioFormatReader object.
Indicates that the memory can only be read.
JUCE_CONSTEXPR Range getUnionWith(Range other) const noexcept
Returns the smallest range that contains both this one and the other one.
Definition: juce_Range.h:245
const void * sampleToPointer(int64 sample) const noexcept
Converts a sample index to a pointer to the mapped file memory.
bool usesFloatingPointData
Indicates whether the data is floating-point or fixed.
The base class for streams that read data.
Very simple container class to hold a pointer to some data on the heap.
JUCE_CONSTEXPR bool contains(const ValueType position) const noexcept
Returns true if the given position lies inside this range.
Definition: juce_Range.h:213
int64 filePosToSample(int64 filePos) const noexcept
Converts a byte position in the file to a sample index.
InputStream * input
The input stream, for use by subclasses.
Type ** getArrayOfWritePointers() noexcept
Returns an array of pointers to the channels in the buffer.
const String & getFormatName() const noexcept
Returns a description of what type of format this is.
int64 lengthInSamples
The total number of samples in the audio stream.
The JUCE String class!
Definition: juce_String.h:42
static void JUCE_CALLTYPE convertFixedToFloat(float *dest, const int *src, float multiplier, int numValues) noexcept
Converts a stream of integers to floats, multiplying each one by the given multiplier.
unsigned int bitsPerSample
The number of bits per sample, e.g.
ElementType * get() const noexcept
Returns a raw pointer to the allocated data.
virtual void readMaxLevels(int64 startSample, int64 numSamples, Range< float > *results, int numChannelsToRead)
Finds the highest and lowest sample levels from a section of the audio stream.
StringPairArray metadataValues
A set of metadata values that the reader has pulled out of the stream.
Maps a file into virtual memory for easy reading and/or writing.
JUCE_CONSTEXPR ValueType getEnd() const noexcept
Returns the end of the range.
Definition: juce_Range.h:90
MemoryMappedAudioFormatReader(const File &file, const AudioFormatReader &details, int64 dataChunkStart, int64 dataChunkLength, int bytesPerFrame)
Creates an MemoryMappedAudioFormatReader object.
int64 searchForLevel(int64 startSample, int64 numSamplesToSearch, double magnitudeRangeMinimum, double magnitudeRangeMaximum, int minimumConsecutiveSamples)
Scans the source looking for a sample whose magnitude is in a specified range.
static Range< float > JUCE_CALLTYPE findMinAndMax(const float *src, int numValues) noexcept
Finds the minimum and maximum values in the given array.
int getNumChannels() const noexcept
Returns the number of channels of audio data that this buffer contains.
Represents a local file or directory.
Definition: juce_File.h:44
static Range findMinAndMax(const ValueType *values, int numValues) noexcept
Scans an array of values for its min and max, and returns these as a Range.
Definition: juce_Range.h:277
static AudioChannelSet JUCE_CALLTYPE canonicalChannelSet(int numChannels)
Create a canonical channel set for a given number of channels.
void touchSample(int64 sample) const noexcept
Touches the memory for the given sample, to force it to be loaded into active memory.
bool read(float *const *destChannels, int numDestChannels, int64 startSampleInSource, int numSamplesToRead)
Reads samples from the stream.
virtual ~AudioFormatReader()
Destructor.
double sampleRate
The sample-rate of the stream.
Type * getWritePointer(int channelNumber) noexcept
Returns a writeable pointer to one of the buffer&#39;s channels.
virtual bool readSamples(int **destChannels, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples)=0
Subclasses must implement this method to perform the low-level read operation.
JUCE_CONSTEXPR ValueType getStart() const noexcept
Returns the start of the range.
Definition: juce_Range.h:84
Reads samples from an audio file stream.
int64 sampleToFilePos(int64 sample) const noexcept
Converts a sample index to a byte position in the file.
virtual AudioChannelSet getChannelLayout()
Get the channel layout of the audio stream.
bool mapEntireFile()
Attempts to map the entire file into memory.
unsigned int numChannels
The total number of channels in the audio stream.
A general-purpose range object, that simply represents any linear range with a start and end point...
Definition: juce_Range.h:43