Loading...
Searching...
No Matches
juce_AudioFormatReader.cpp
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
AudioFormatReader::AudioFormatReader (InputStream* in, const String& name)
: input (in), formatName (name)
{
}
AudioFormatReader::~AudioFormatReader()
{
delete input;
}
static void convertFixedToFloat (int* const* channels, int numChannels, int numSamples)
{
constexpr auto scaleFactor = 1.0f / static_cast<float> (0x7fffffff);
for (int i = 0; i < numChannels; ++i)
if (auto d = channels[i])
FloatVectorOperations::convertFixedToFloat (reinterpret_cast<float*> (d), d, scaleFactor, numSamples);
}
bool AudioFormatReader::read (float* const* destChannels, int numDestChannels,
int64 startSampleInSource, int numSamplesToRead)
{
auto channelsAsInt = reinterpret_cast<int* const*> (destChannels);
if (! read (channelsAsInt, numDestChannels, startSampleInSource, numSamplesToRead, false))
return false;
if (! usesFloatingPointData)
convertFixedToFloat (channelsAsInt, numDestChannels, numSamplesToRead);
return true;
}
bool AudioFormatReader::read (int* const* destChannels,
int numDestChannels,
int64 startSampleInSource,
int numSamplesToRead,
bool fillLeftoverChannelsWithCopies)
{
jassert (numDestChannels > 0); // you have to actually give this some channels to work with!
auto originalNumSamplesToRead = (size_t) numSamplesToRead;
int startOffsetInDestBuffer = 0;
if (startSampleInSource < 0)
{
auto silence = (int) jmin (-startSampleInSource, (int64) numSamplesToRead);
for (int i = numDestChannels; --i >= 0;)
if (auto d = destChannels[i])
zeromem (d, (size_t) silence * sizeof (int));
startOffsetInDestBuffer += silence;
numSamplesToRead -= silence;
startSampleInSource = 0;
}
if (numSamplesToRead <= 0)
return true;
if (! readSamples (destChannels,
jmin ((int) numChannels, numDestChannels), startOffsetInDestBuffer,
startSampleInSource, numSamplesToRead))
return false;
if (numDestChannels > (int) numChannels)
{
if (fillLeftoverChannelsWithCopies)
{
auto lastFullChannel = destChannels[0];
for (int i = (int) numChannels; --i > 0;)
{
if (destChannels[i] != nullptr)
{
lastFullChannel = destChannels[i];
break;
}
}
if (lastFullChannel != nullptr)
for (int i = (int) numChannels; i < numDestChannels; ++i)
if (auto d = destChannels[i])
memcpy (d, lastFullChannel, sizeof (int) * originalNumSamplesToRead);
}
else
{
for (int i = (int) numChannels; i < numDestChannels; ++i)
if (auto d = destChannels[i])
zeromem (d, sizeof (int) * originalNumSamplesToRead);
}
}
return true;
}
static bool readChannels (AudioFormatReader& reader, int** chans, AudioBuffer<float>* buffer,
int startSample, int numSamples, int64 readerStartSample, int numTargetChannels,
bool convertToFloat)
{
for (int j = 0; j < numTargetChannels; ++j)
chans[j] = reinterpret_cast<int*> (buffer->getWritePointer (j, startSample));
chans[numTargetChannels] = nullptr;
const bool success = reader.read (chans, numTargetChannels, readerStartSample, numSamples, true);
if (convertToFloat)
convertFixedToFloat (chans, numTargetChannels, numSamples);
return success;
}
bool AudioFormatReader::read (AudioBuffer<float>* buffer,
int startSample,
int numSamples,
int64 readerStartSample,
bool useReaderLeftChan,
bool useReaderRightChan)
{
jassert (buffer != nullptr);
jassert (startSample >= 0 && startSample + numSamples <= buffer->getNumSamples());
if (numSamples <= 0)
return true;
auto numTargetChannels = buffer->getNumChannels();
if (numTargetChannels <= 2)
{
int* dests[2] = { reinterpret_cast<int*> (buffer->getWritePointer (0, startSample)),
reinterpret_cast<int*> (numTargetChannels > 1 ? buffer->getWritePointer (1, startSample) : nullptr) };
int* chans[3] = {};
if (useReaderLeftChan == useReaderRightChan)
{
chans[0] = dests[0];
if (numChannels > 1)
chans[1] = dests[1];
}
else if (useReaderLeftChan || (numChannels == 1))
{
chans[0] = dests[0];
}
else if (useReaderRightChan)
{
chans[1] = dests[0];
}
if (! read (chans, 2, readerStartSample, numSamples, true))
return false;
// if the target's stereo and the source is mono, dupe the first channel..
if (numTargetChannels > 1
&& (chans[0] == nullptr || chans[1] == nullptr)
&& (dests[0] != nullptr && dests[1] != nullptr))
{
memcpy (dests[1], dests[0], (size_t) numSamples * sizeof (float));
}
if (! usesFloatingPointData)
convertFixedToFloat (dests, 2, numSamples);
return true;
}
if (numTargetChannels <= 64)
{
int* chans[65];
return readChannels (*this, chans, buffer, startSample, numSamples,
readerStartSample, numTargetChannels, ! usesFloatingPointData);
}
HeapBlock<int*> chans (numTargetChannels + 1);
return readChannels (*this, chans, buffer, startSample, numSamples,
readerStartSample, numTargetChannels, ! usesFloatingPointData);
}
void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples,
Range<float>* const results, const int channelsToRead)
{
jassert (channelsToRead > 0 && channelsToRead <= (int) numChannels);
if (numSamples <= 0)
{
for (int i = 0; i < channelsToRead; ++i)
results[i] = Range<float>();
return;
}
auto bufferSize = (int) jmin (numSamples, (int64) 4096);
AudioBuffer<float> tempSampleBuffer ((int) channelsToRead, bufferSize);
auto floatBuffer = tempSampleBuffer.getArrayOfWritePointers();
auto intBuffer = reinterpret_cast<int* const*> (floatBuffer);
bool isFirstBlock = true;
while (numSamples > 0)
{
auto numToDo = (int) jmin (numSamples, (int64) bufferSize);
if (! read (intBuffer, channelsToRead, startSampleInFile, numToDo, false))
break;
for (int i = 0; i < channelsToRead; ++i)
{
Range<float> r;
if (usesFloatingPointData)
{
r = FloatVectorOperations::findMinAndMax (floatBuffer[i], numToDo);
}
else
{
auto intRange = Range<int>::findMinAndMax (intBuffer[i], numToDo);
r = Range<float> ((float) intRange.getStart() / (float) std::numeric_limits<int>::max(),
(float) intRange.getEnd() / (float) std::numeric_limits<int>::max());
}
results[i] = isFirstBlock ? r : results[i].getUnionWith (r);
}
isFirstBlock = false;
numSamples -= numToDo;
startSampleInFile += numToDo;
}
}
void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples,
float& lowestLeft, float& highestLeft,
float& lowestRight, float& highestRight)
{
Range<float> levels[2];
if (numChannels < 2)
{
readMaxLevels (startSampleInFile, numSamples, levels, (int) numChannels);
levels[1] = levels[0];
}
else
{
readMaxLevels (startSampleInFile, numSamples, levels, 2);
}
lowestLeft = levels[0].getStart();
highestLeft = levels[0].getEnd();
lowestRight = levels[1].getStart();
highestRight = levels[1].getEnd();
}
int64 AudioFormatReader::searchForLevel (int64 startSample,
int64 numSamplesToSearch,
double magnitudeRangeMinimum,
double magnitudeRangeMaximum,
int minimumConsecutiveSamples)
{
if (numSamplesToSearch == 0)
return -1;
const int bufferSize = 4096;
HeapBlock<int> tempSpace (bufferSize * 2 + 64);
int* tempBuffer[3] = { tempSpace.get(),
tempSpace.get() + bufferSize,
nullptr };
int consecutive = 0;
int64 firstMatchPos = -1;
jassert (magnitudeRangeMaximum > magnitudeRangeMinimum);
auto doubleMin = jlimit (0.0, (double) std::numeric_limits<int>::max(), magnitudeRangeMinimum * std::numeric_limits<int>::max());
auto doubleMax = jlimit (doubleMin, (double) std::numeric_limits<int>::max(), magnitudeRangeMaximum * std::numeric_limits<int>::max());
auto intMagnitudeRangeMinimum = roundToInt (doubleMin);
auto intMagnitudeRangeMaximum = roundToInt (doubleMax);
while (numSamplesToSearch != 0)
{
auto numThisTime = (int) jmin (std::abs (numSamplesToSearch), (int64) bufferSize);
int64 bufferStart = startSample;
if (numSamplesToSearch < 0)
bufferStart -= numThisTime;
if (bufferStart >= lengthInSamples)
break;
read (tempBuffer, 2, bufferStart, numThisTime, false);
auto num = numThisTime;
while (--num >= 0)
{
if (numSamplesToSearch < 0)
--startSample;
bool matches = false;
auto index = (int) (startSample - bufferStart);
if (usesFloatingPointData)
{
const float sample1 = std::abs (((float*) tempBuffer[0]) [index]);
if (sample1 >= magnitudeRangeMinimum
&& sample1 <= magnitudeRangeMaximum)
{
matches = true;
}
else if (numChannels > 1)
{
const float sample2 = std::abs (((float*) tempBuffer[1]) [index]);
matches = (sample2 >= magnitudeRangeMinimum
&& sample2 <= magnitudeRangeMaximum);
}
}
else
{
const int sample1 = std::abs (tempBuffer[0] [index]);
if (sample1 >= intMagnitudeRangeMinimum
&& sample1 <= intMagnitudeRangeMaximum)
{
matches = true;
}
else if (numChannels > 1)
{
const int sample2 = std::abs (tempBuffer[1][index]);
matches = (sample2 >= intMagnitudeRangeMinimum
&& sample2 <= intMagnitudeRangeMaximum);
}
}
if (matches)
{
if (firstMatchPos < 0)
firstMatchPos = startSample;
if (++consecutive >= minimumConsecutiveSamples)
{
if (firstMatchPos < 0 || firstMatchPos >= lengthInSamples)
return -1;
return firstMatchPos;
}
}
else
{
consecutive = 0;
firstMatchPos = -1;
}
if (numSamplesToSearch > 0)
++startSample;
}
if (numSamplesToSearch > 0)
numSamplesToSearch -= numThisTime;
else
numSamplesToSearch += numThisTime;
}
return -1;
}
AudioChannelSet AudioFormatReader::getChannelLayout()
{
return AudioChannelSet::canonicalChannelSet (static_cast<int> (numChannels));
}
//==============================================================================
MemoryMappedAudioFormatReader::MemoryMappedAudioFormatReader (const File& f, const AudioFormatReader& reader,
int64 start, int64 length, int frameSize)
: AudioFormatReader (nullptr, reader.getFormatName()), file (f),
dataChunkStart (start), dataLength (length), bytesPerFrame (frameSize)
{
sampleRate = reader.sampleRate;
bitsPerSample = reader.bitsPerSample;
lengthInSamples = reader.lengthInSamples;
numChannels = reader.numChannels;
metadataValues = reader.metadataValues;
usesFloatingPointData = reader.usesFloatingPointData;
}
bool MemoryMappedAudioFormatReader::mapEntireFile()
{
return mapSectionOfFile (Range<int64> (0, lengthInSamples));
}
bool MemoryMappedAudioFormatReader::mapSectionOfFile (Range<int64> samplesToMap)
{
if (map == nullptr || samplesToMap != mappedSection)
{
map.reset();
const Range<int64> fileRange (sampleToFilePos (samplesToMap.getStart()),
sampleToFilePos (samplesToMap.getEnd()));
map.reset (new MemoryMappedFile (file, fileRange, MemoryMappedFile::readOnly));
if (map->getData() == nullptr)
map.reset();
else
mappedSection = Range<int64> (jmax ((int64) 0, filePosToSample (map->getRange().getStart() + (bytesPerFrame - 1))),
jmin (lengthInSamples, filePosToSample (map->getRange().getEnd())));
}
return map != nullptr;
}
static int memoryReadDummyVariable; // used to force the compiler not to optimise-away the read operation
void MemoryMappedAudioFormatReader::touchSample (int64 sample) const noexcept
{
if (map != nullptr && mappedSection.contains (sample))
memoryReadDummyVariable += *(char*) sampleToPointer (sample);
else
jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
}
} // namespace juce
juce::AudioBuffer
Definition juce_AudioSampleBuffer.h:34
juce::AudioBuffer::getWritePointer
Type * getWritePointer(int channelNumber) noexcept
Definition juce_AudioSampleBuffer.h:291
juce::AudioBuffer::getNumChannels
int getNumChannels() const noexcept
Definition juce_AudioSampleBuffer.h:236
juce::AudioBuffer::getArrayOfWritePointers
Type *const * getArrayOfWritePointers() noexcept
Definition juce_AudioSampleBuffer.h:342
juce::AudioChannelSet
Definition juce_AudioChannelSet.h:47
juce::AudioChannelSet::canonicalChannelSet
static AudioChannelSet JUCE_CALLTYPE canonicalChannelSet(int numChannels)
Definition juce_AudioChannelSet.cpp:616
juce::AudioFormatReader
Definition juce_AudioFormatReader.h:44
juce::AudioFormatReader::input
InputStream * input
Definition juce_AudioFormatReader.h:248
juce::AudioFormatReader::usesFloatingPointData
bool usesFloatingPointData
Definition juce_AudioFormatReader.h:237
juce::AudioFormatReader::read
bool read(float *const *destChannels, int numDestChannels, int64 startSampleInSource, int numSamplesToRead)
Definition juce_AudioFormatReader.cpp:48
juce::AudioFormatReader::searchForLevel
int64 searchForLevel(int64 startSample, int64 numSamplesToSearch, double magnitudeRangeMinimum, double magnitudeRangeMaximum, int minimumConsecutiveSamples)
Definition juce_AudioFormatReader.cpp:283
juce::AudioFormatReader::metadataValues
StringPairArray metadataValues
Definition juce_AudioFormatReader.h:245
juce::AudioFormatReader::getFormatName
const String & getFormatName() const noexcept
Definition juce_AudioFormatReader.h:68
juce::AudioFormatReader::~AudioFormatReader
virtual ~AudioFormatReader()
Definition juce_AudioFormatReader.cpp:34
juce::AudioFormatReader::lengthInSamples
int64 lengthInSamples
Definition juce_AudioFormatReader.h:231
juce::AudioFormatReader::sampleRate
double sampleRate
Definition juce_AudioFormatReader.h:225
juce::AudioFormatReader::getChannelLayout
virtual AudioChannelSet getChannelLayout()
Definition juce_AudioFormatReader.cpp:398
juce::AudioFormatReader::AudioFormatReader
AudioFormatReader(InputStream *sourceStream, const String &formatName)
Definition juce_AudioFormatReader.cpp:29
juce::AudioFormatReader::readSamples
virtual bool readSamples(int *const *destChannels, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples)=0
juce::AudioFormatReader::bitsPerSample
unsigned int bitsPerSample
Definition juce_AudioFormatReader.h:228
juce::AudioFormatReader::readMaxLevels
virtual void readMaxLevels(int64 startSample, int64 numSamples, Range< float > *results, int numChannelsToRead)
Definition juce_AudioFormatReader.cpp:209
juce::AudioFormatReader::numChannels
unsigned int numChannels
Definition juce_AudioFormatReader.h:234
juce::File
Definition juce_File.h:45
juce::HeapBlock
Definition juce_HeapBlock.h:87
juce::HeapBlock::get
ElementType * get() const noexcept
Definition juce_HeapBlock.h:185
juce::InputStream
Definition juce_InputStream.h:37
juce::MemoryMappedAudioFormatReader::MemoryMappedAudioFormatReader
MemoryMappedAudioFormatReader(const File &file, const AudioFormatReader &details, int64 dataChunkStart, int64 dataChunkLength, int bytesPerFrame)
Definition juce_AudioFormatReader.cpp:404
juce::MemoryMappedAudioFormatReader::touchSample
void touchSample(int64 sample) const noexcept
Definition juce_AudioFormatReader.cpp:445
juce::MemoryMappedAudioFormatReader::mapEntireFile
bool mapEntireFile()
Definition juce_AudioFormatReader.cpp:417
juce::MemoryMappedAudioFormatReader::sampleToPointer
const void * sampleToPointer(int64 sample) const noexcept
Definition juce_MemoryMappedAudioFormatReader.h:98
juce::MemoryMappedAudioFormatReader::sampleToFilePos
int64 sampleToFilePos(int64 sample) const noexcept
Definition juce_MemoryMappedAudioFormatReader.h:92
juce::MemoryMappedAudioFormatReader::mapSectionOfFile
virtual bool mapSectionOfFile(Range< int64 > samplesToMap)
Definition juce_AudioFormatReader.cpp:422
juce::MemoryMappedAudioFormatReader::filePosToSample
int64 filePosToSample(int64 filePos) const noexcept
Definition juce_MemoryMappedAudioFormatReader.h:95
juce::MemoryMappedFile
Definition juce_MemoryMappedFile.h:33
juce::MemoryMappedFile::readOnly
@ readOnly
Definition juce_MemoryMappedFile.h:38
juce::Range
Definition juce_Range.h:40
juce::Range::getStart
constexpr ValueType getStart() const noexcept
Definition juce_Range.h:80
juce::Range::getEnd
constexpr ValueType getEnd() const noexcept
Definition juce_Range.h:86
juce::Range::getUnionWith
constexpr Range getUnionWith(Range other) const noexcept
Definition juce_Range.h:246
juce::Range::findMinAndMax
static Range findMinAndMax(const ValueType *values, Integral numValues) noexcept
Definition juce_Range.h:279
juce::String
Definition juce_String.h:53