JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_AudioFormatReader.cpp
Go to the documentation of this file.
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
26namespace juce
27{
28
30 : input (in), formatName (name)
31{
32}
33
38
39static void convertFixedToFloat (int* const* channels, int numChannels, int numSamples)
40{
41 constexpr auto scaleFactor = 1.0f / static_cast<float> (0x7fffffff);
42
43 for (int i = 0; i < numChannels; ++i)
44 if (auto d = channels[i])
45 FloatVectorOperations::convertFixedToFloat (reinterpret_cast<float*> (d), d, scaleFactor, numSamples);
46}
47
48bool AudioFormatReader::read (float* const* destChannels, int numDestChannels,
49 int64 startSampleInSource, int numSamplesToRead)
50{
51 auto channelsAsInt = reinterpret_cast<int* const*> (destChannels);
52
53 if (! read (channelsAsInt, numDestChannels, startSampleInSource, numSamplesToRead, false))
54 return false;
55
57 convertFixedToFloat (channelsAsInt, numDestChannels, numSamplesToRead);
58
59 return true;
60}
61
62bool AudioFormatReader::read (int* const* destChannels,
63 int numDestChannels,
64 int64 startSampleInSource,
65 int numSamplesToRead,
66 bool fillLeftoverChannelsWithCopies)
67{
68 jassert (numDestChannels > 0); // you have to actually give this some channels to work with!
69
70 auto originalNumSamplesToRead = (size_t) numSamplesToRead;
71 int startOffsetInDestBuffer = 0;
72
73 if (startSampleInSource < 0)
74 {
75 auto silence = (int) jmin (-startSampleInSource, (int64) numSamplesToRead);
76
77 for (int i = numDestChannels; --i >= 0;)
78 if (auto d = destChannels[i])
79 zeromem (d, (size_t) silence * sizeof (int));
80
81 startOffsetInDestBuffer += silence;
82 numSamplesToRead -= silence;
83 startSampleInSource = 0;
84 }
85
86 if (numSamplesToRead <= 0)
87 return true;
88
89 if (! readSamples (destChannels,
90 jmin ((int) numChannels, numDestChannels), startOffsetInDestBuffer,
91 startSampleInSource, numSamplesToRead))
92 return false;
93
94 if (numDestChannels > (int) numChannels)
95 {
96 if (fillLeftoverChannelsWithCopies)
97 {
98 auto lastFullChannel = destChannels[0];
99
100 for (int i = (int) numChannels; --i > 0;)
101 {
102 if (destChannels[i] != nullptr)
103 {
104 lastFullChannel = destChannels[i];
105 break;
106 }
107 }
108
109 if (lastFullChannel != nullptr)
110 for (int i = (int) numChannels; i < numDestChannels; ++i)
111 if (auto d = destChannels[i])
112 memcpy (d, lastFullChannel, sizeof (int) * originalNumSamplesToRead);
113 }
114 else
115 {
116 for (int i = (int) numChannels; i < numDestChannels; ++i)
117 if (auto d = destChannels[i])
118 zeromem (d, sizeof (int) * originalNumSamplesToRead);
119 }
120 }
121
122 return true;
123}
124
125static bool readChannels (AudioFormatReader& reader, int** chans, AudioBuffer<float>* buffer,
126 int startSample, int numSamples, int64 readerStartSample, int numTargetChannels,
127 bool convertToFloat)
128{
129 for (int j = 0; j < numTargetChannels; ++j)
130 chans[j] = reinterpret_cast<int*> (buffer->getWritePointer (j, startSample));
131
132 chans[numTargetChannels] = nullptr;
133
134 const bool success = reader.read (chans, numTargetChannels, readerStartSample, numSamples, true);
135
136 if (convertToFloat)
137 convertFixedToFloat (chans, numTargetChannels, numSamples);
138
139 return success;
140}
141
143 int startSample,
144 int numSamples,
145 int64 readerStartSample,
146 bool useReaderLeftChan,
147 bool useReaderRightChan)
148{
149 jassert (buffer != nullptr);
150 jassert (startSample >= 0 && startSample + numSamples <= buffer->getNumSamples());
151
152 if (numSamples <= 0)
153 return true;
154
155 auto numTargetChannels = buffer->getNumChannels();
156
157 if (numTargetChannels <= 2)
158 {
159 int* dests[2] = { reinterpret_cast<int*> (buffer->getWritePointer (0, startSample)),
160 reinterpret_cast<int*> (numTargetChannels > 1 ? buffer->getWritePointer (1, startSample) : nullptr) };
161 int* chans[3] = {};
162
163 if (useReaderLeftChan == useReaderRightChan)
164 {
165 chans[0] = dests[0];
166
167 if (numChannels > 1)
168 chans[1] = dests[1];
169 }
170 else if (useReaderLeftChan || (numChannels == 1))
171 {
172 chans[0] = dests[0];
173 }
174 else if (useReaderRightChan)
175 {
176 chans[1] = dests[0];
177 }
178
179 if (! read (chans, 2, readerStartSample, numSamples, true))
180 return false;
181
182 // if the target's stereo and the source is mono, dupe the first channel..
183 if (numTargetChannels > 1
184 && (chans[0] == nullptr || chans[1] == nullptr)
185 && (dests[0] != nullptr && dests[1] != nullptr))
186 {
187 memcpy (dests[1], dests[0], (size_t) numSamples * sizeof (float));
188 }
189
191 convertFixedToFloat (dests, 2, numSamples);
192
193 return true;
194 }
195
196 if (numTargetChannels <= 64)
197 {
198 int* chans[65];
199 return readChannels (*this, chans, buffer, startSample, numSamples,
200 readerStartSample, numTargetChannels, ! usesFloatingPointData);
201 }
202
203 HeapBlock<int*> chans (numTargetChannels + 1);
204
205 return readChannels (*this, chans, buffer, startSample, numSamples,
206 readerStartSample, numTargetChannels, ! usesFloatingPointData);
207}
208
209void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples,
210 Range<float>* const results, const int channelsToRead)
211{
212 jassert (channelsToRead > 0 && channelsToRead <= (int) numChannels);
213
214 if (numSamples <= 0)
215 {
216 for (int i = 0; i < channelsToRead; ++i)
217 results[i] = Range<float>();
218
219 return;
220 }
221
222 auto bufferSize = (int) jmin (numSamples, (int64) 4096);
223 AudioBuffer<float> tempSampleBuffer ((int) channelsToRead, bufferSize);
224
225 auto floatBuffer = tempSampleBuffer.getArrayOfWritePointers();
226 auto intBuffer = reinterpret_cast<int* const*> (floatBuffer);
227 bool isFirstBlock = true;
228
229 while (numSamples > 0)
230 {
231 auto numToDo = (int) jmin (numSamples, (int64) bufferSize);
232
233 if (! read (intBuffer, channelsToRead, startSampleInFile, numToDo, false))
234 break;
235
236 for (int i = 0; i < channelsToRead; ++i)
237 {
238 Range<float> r;
239
241 {
242 r = FloatVectorOperations::findMinAndMax (floatBuffer[i], numToDo);
243 }
244 else
245 {
246 auto intRange = Range<int>::findMinAndMax (intBuffer[i], numToDo);
247
248 r = Range<float> ((float) intRange.getStart() / (float) std::numeric_limits<int>::max(),
249 (float) intRange.getEnd() / (float) std::numeric_limits<int>::max());
250 }
251
252 results[i] = isFirstBlock ? r : results[i].getUnionWith (r);
253 }
254
255 isFirstBlock = false;
256 numSamples -= numToDo;
257 startSampleInFile += numToDo;
258 }
259}
260
261void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples,
262 float& lowestLeft, float& highestLeft,
263 float& lowestRight, float& highestRight)
264{
265 Range<float> levels[2];
266
267 if (numChannels < 2)
268 {
269 readMaxLevels (startSampleInFile, numSamples, levels, (int) numChannels);
270 levels[1] = levels[0];
271 }
272 else
273 {
274 readMaxLevels (startSampleInFile, numSamples, levels, 2);
275 }
276
277 lowestLeft = levels[0].getStart();
278 highestLeft = levels[0].getEnd();
279 lowestRight = levels[1].getStart();
280 highestRight = levels[1].getEnd();
281}
282
284 int64 numSamplesToSearch,
285 double magnitudeRangeMinimum,
286 double magnitudeRangeMaximum,
287 int minimumConsecutiveSamples)
288{
289 if (numSamplesToSearch == 0)
290 return -1;
291
292 const int bufferSize = 4096;
293 HeapBlock<int> tempSpace (bufferSize * 2 + 64);
294
295 int* tempBuffer[3] = { tempSpace.get(),
296 tempSpace.get() + bufferSize,
297 nullptr };
298
299 int consecutive = 0;
300 int64 firstMatchPos = -1;
301
302 jassert (magnitudeRangeMaximum > magnitudeRangeMinimum);
303
304 auto doubleMin = jlimit (0.0, (double) std::numeric_limits<int>::max(), magnitudeRangeMinimum * std::numeric_limits<int>::max());
305 auto doubleMax = jlimit (doubleMin, (double) std::numeric_limits<int>::max(), magnitudeRangeMaximum * std::numeric_limits<int>::max());
306 auto intMagnitudeRangeMinimum = roundToInt (doubleMin);
307 auto intMagnitudeRangeMaximum = roundToInt (doubleMax);
308
309 while (numSamplesToSearch != 0)
310 {
311 auto numThisTime = (int) jmin (std::abs (numSamplesToSearch), (int64) bufferSize);
312 int64 bufferStart = startSample;
313
314 if (numSamplesToSearch < 0)
315 bufferStart -= numThisTime;
316
317 if (bufferStart >= lengthInSamples)
318 break;
319
320 read (tempBuffer, 2, bufferStart, numThisTime, false);
321 auto num = numThisTime;
322
323 while (--num >= 0)
324 {
325 if (numSamplesToSearch < 0)
326 --startSample;
327
328 bool matches = false;
329 auto index = (int) (startSample - bufferStart);
330
332 {
333 const float sample1 = std::abs (((float*) tempBuffer[0]) [index]);
334
335 if (sample1 >= magnitudeRangeMinimum
336 && sample1 <= magnitudeRangeMaximum)
337 {
338 matches = true;
339 }
340 else if (numChannels > 1)
341 {
342 const float sample2 = std::abs (((float*) tempBuffer[1]) [index]);
343
344 matches = (sample2 >= magnitudeRangeMinimum
345 && sample2 <= magnitudeRangeMaximum);
346 }
347 }
348 else
349 {
350 const int sample1 = std::abs (tempBuffer[0] [index]);
351
352 if (sample1 >= intMagnitudeRangeMinimum
353 && sample1 <= intMagnitudeRangeMaximum)
354 {
355 matches = true;
356 }
357 else if (numChannels > 1)
358 {
359 const int sample2 = std::abs (tempBuffer[1][index]);
360
361 matches = (sample2 >= intMagnitudeRangeMinimum
362 && sample2 <= intMagnitudeRangeMaximum);
363 }
364 }
365
366 if (matches)
367 {
368 if (firstMatchPos < 0)
369 firstMatchPos = startSample;
370
371 if (++consecutive >= minimumConsecutiveSamples)
372 {
373 if (firstMatchPos < 0 || firstMatchPos >= lengthInSamples)
374 return -1;
375
376 return firstMatchPos;
377 }
378 }
379 else
380 {
381 consecutive = 0;
382 firstMatchPos = -1;
383 }
384
385 if (numSamplesToSearch > 0)
386 ++startSample;
387 }
388
389 if (numSamplesToSearch > 0)
390 numSamplesToSearch -= numThisTime;
391 else
392 numSamplesToSearch += numThisTime;
393 }
394
395 return -1;
396}
397
402
403//==============================================================================
405 int64 start, int64 length, int frameSize)
406 : AudioFormatReader (nullptr, reader.getFormatName()), file (f),
407 dataChunkStart (start), dataLength (length), bytesPerFrame (frameSize)
408{
409 sampleRate = reader.sampleRate;
412 numChannels = reader.numChannels;
415}
416
421
423{
424 if (map == nullptr || samplesToMap != mappedSection)
425 {
426 map.reset();
427
428 const Range<int64> fileRange (sampleToFilePos (samplesToMap.getStart()),
429 sampleToFilePos (samplesToMap.getEnd()));
430
431 map.reset (new MemoryMappedFile (file, fileRange, MemoryMappedFile::readOnly));
432
433 if (map->getData() == nullptr)
434 map.reset();
435 else
436 mappedSection = Range<int64> (jmax ((int64) 0, filePosToSample (map->getRange().getStart() + (bytesPerFrame - 1))),
437 jmin (lengthInSamples, filePosToSample (map->getRange().getEnd())));
438 }
439
440 return map != nullptr;
441}
442
443static int memoryReadDummyVariable; // used to force the compiler not to optimise-away the read operation
444
446{
447 if (map != nullptr && mappedSection.contains (sample))
448 memoryReadDummyVariable += *(char*) sampleToPointer (sample);
449 else
450 jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
451}
452
453} // 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 *const * 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.
virtual bool readSamples(int *const *destChannels, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples)=0
Subclasses must implement this method to perform the low-level read operation.
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.
unsigned int numChannels
The total number of channels in the audio stream.
Represents a local file or directory.
Definition juce_File.h:45
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:40
constexpr ValueType getStart() const noexcept
Returns the start of the range.
Definition juce_Range.h:80
constexpr ValueType getEnd() const noexcept
Returns the end of the range.
Definition juce_Range.h:86
constexpr Range getUnionWith(Range other) const noexcept
Returns the smallest range that contains both this one and the other one.
Definition juce_Range.h:246
static Range findMinAndMax(const ValueType *values, Integral numValues) noexcept
Scans an array of values for its min and max, and returns these as a Range.
Definition juce_Range.h:279
The JUCE String class!
Definition juce_String.h:53
#define jassert(expression)
Platform-independent assertion macro.
#define jassertfalse
This will always cause an assertion failure.
typedef int
memcpy
JUCE Namespace.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Constrains a value to keep it within a given range.
int roundToInt(const FloatType value) noexcept
Fast floating-point-to-integer conversion.
long long int64
A platform-independent 64-bit integer type.
void zeromem(void *memory, size_t numBytes) noexcept
Fills a block of memory with zeros.
Definition juce_Memory.h:28
typedef size_t