OpenShot Library | libopenshot-audio 0.2.0
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
27namespace juce
28{
29
31 : input (in), formatName (name)
32{
33}
34
36{
37 delete input;
38}
39
40static 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
47bool 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
61bool 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, sizeof (int) * (size_t) silence);
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
124static 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], sizeof (float) * (size_t) numSamples);
179
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
198void 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
250void 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
272int64 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;
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
432static int memoryReadDummyVariable; // used to force the compiler not to optimise-away the read operation
433
434void 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
A multi-channel buffer containing floating point audio samples.
Type * getWritePointer(int channelNumber) noexcept
Returns a writeable pointer to one of the buffer's channels.
int getNumChannels() const noexcept
Returns the number of channels of audio data that this buffer contains.
Type ** getArrayOfWritePointers() noexcept
Returns an array of pointers to the channels in the buffer.
Represents a set of audio channel types.
static AudioChannelSet JUCE_CALLTYPE canonicalChannelSet(int numChannels)
Create a canonical channel set for a given number of channels.
Reads samples from an audio file stream.
InputStream * input
The input stream, for use by subclasses.
bool usesFloatingPointData
Indicates whether the data is floating-point or fixed.
bool read(float *const *destChannels, int numDestChannels, int64 startSampleInSource, int numSamplesToRead)
Reads samples from the stream.
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.
StringPairArray metadataValues
A set of metadata values that the reader has pulled out of the stream.
virtual ~AudioFormatReader()
Destructor.
int64 lengthInSamples
The total number of samples in the audio stream.
double sampleRate
The sample-rate of the stream.
virtual AudioChannelSet getChannelLayout()
Get the channel layout of the audio stream.
AudioFormatReader(InputStream *sourceStream, const String &formatName)
Creates an AudioFormatReader object.
unsigned int bitsPerSample
The number of bits per sample, e.g.
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.
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.
unsigned int numChannels
The total number of channels in the audio stream.
Represents a local file or directory.
Definition juce_File.h:45
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.
static Range< float > JUCE_CALLTYPE findMinAndMax(const float *src, int numValues) noexcept
Finds the minimum and maximum values in the given array.
Very simple container class to hold a pointer to some data on the heap.
ElementType * get() const noexcept
Returns a raw pointer to the allocated data.
The base class for streams that read data.
MemoryMappedAudioFormatReader(const File &file, const AudioFormatReader &details, int64 dataChunkStart, int64 dataChunkLength, int bytesPerFrame)
Creates an MemoryMappedAudioFormatReader object.
void touchSample(int64 sample) const noexcept
Touches the memory for the given sample, to force it to be loaded into active memory.
bool mapEntireFile()
Attempts to map the entire file into memory.
int64 sampleToFilePos(int64 sample) const noexcept
Converts a sample index to a byte position in the file.
virtual bool mapSectionOfFile(Range< int64 > samplesToMap)
Attempts to map a section of the file into memory.
int64 filePosToSample(int64 filePos) const noexcept
Converts a byte position in the file to a sample index.
Maps a file into virtual memory for easy reading and/or writing.
@ readOnly
Indicates that the memory can only be read.
A general-purpose range object, that simply represents any linear range with a start and end point.
Definition juce_Range.h:44
JUCE_CONSTEXPR ValueType getStart() const noexcept
Returns the start of the range.
Definition juce_Range.h:84
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
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
JUCE_CONSTEXPR ValueType getEnd() const noexcept
Returns the end of the range.
Definition juce_Range.h:90
The JUCE String class!
Definition juce_String.h:43