mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
New class of audio format readers: MemoryMappedAudioFormatReader, which does what it says on the tin.
This commit is contained in:
parent
ceb556eb30
commit
27895cb6bd
14 changed files with 457 additions and 86 deletions
|
|
@ -407,16 +407,8 @@ public:
|
|||
bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
|
||||
int64 startSampleInFile, int numSamples)
|
||||
{
|
||||
const int64 samplesAvailable = lengthInSamples - startSampleInFile;
|
||||
|
||||
if (samplesAvailable < numSamples)
|
||||
{
|
||||
for (int i = numDestChannels; --i >= 0;)
|
||||
if (destSamples[i] != nullptr)
|
||||
zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples);
|
||||
|
||||
numSamples = (int) samplesAvailable;
|
||||
}
|
||||
clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
|
||||
startSampleInFile, numSamples, lengthInSamples);
|
||||
|
||||
if (numSamples <= 0)
|
||||
return true;
|
||||
|
|
@ -440,27 +432,14 @@ public:
|
|||
jassert (! usesFloatingPointData); // (would need to add support for this if it's possible)
|
||||
|
||||
if (littleEndian)
|
||||
{
|
||||
switch (bitsPerSample)
|
||||
{
|
||||
case 8: ReadHelper<AudioData::Int32, AudioData::Int8, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
|
||||
case 16: ReadHelper<AudioData::Int32, AudioData::Int16, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
|
||||
case 24: ReadHelper<AudioData::Int32, AudioData::Int24, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
|
||||
case 32: ReadHelper<AudioData::Int32, AudioData::Int32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
|
||||
default: jassertfalse; break;
|
||||
}
|
||||
}
|
||||
copySampleData<AudioData::LittleEndian> (bitsPerSample, usesFloatingPointData,
|
||||
destSamples, startOffsetInDestBuffer, numDestChannels,
|
||||
tempBuffer, (int) numChannels, numThisTime);
|
||||
else
|
||||
{
|
||||
switch (bitsPerSample)
|
||||
{
|
||||
case 8: ReadHelper<AudioData::Int32, AudioData::Int8, AudioData::BigEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
|
||||
case 16: ReadHelper<AudioData::Int32, AudioData::Int16, AudioData::BigEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
|
||||
case 24: ReadHelper<AudioData::Int32, AudioData::Int24, AudioData::BigEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
|
||||
case 32: ReadHelper<AudioData::Int32, AudioData::Int32, AudioData::BigEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
|
||||
default: jassertfalse; break;
|
||||
}
|
||||
}
|
||||
copySampleData<AudioData::BigEndian> (bitsPerSample, usesFloatingPointData,
|
||||
destSamples, startOffsetInDestBuffer, numDestChannels,
|
||||
tempBuffer, (int) numChannels, numThisTime);
|
||||
|
||||
|
||||
startOffsetInDestBuffer += numThisTime;
|
||||
numSamples -= numThisTime;
|
||||
|
|
@ -469,6 +448,21 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
template <typename Endianness>
|
||||
static void copySampleData (unsigned int bitsPerSample, const bool usesFloatingPointData,
|
||||
int* const* destSamples, int startOffsetInDestBuffer, int numDestChannels,
|
||||
const void* sourceData, int numChannels, int numSamples) noexcept
|
||||
{
|
||||
switch (bitsPerSample)
|
||||
{
|
||||
case 8: ReadHelper<AudioData::Int32, AudioData::Int8, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
|
||||
case 16: ReadHelper<AudioData::Int32, AudioData::Int16, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
|
||||
case 24: ReadHelper<AudioData::Int32, AudioData::Int24, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
|
||||
case 32: ReadHelper<AudioData::Int32, AudioData::Int32, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
|
||||
default: jassertfalse; break;
|
||||
}
|
||||
}
|
||||
|
||||
int bytesPerFrame;
|
||||
int64 dataChunkStart;
|
||||
bool littleEndian;
|
||||
|
|
@ -661,6 +655,101 @@ private:
|
|||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatWriter)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class MemoryMappedAiffReader : public MemoryMappedAudioFormatReader
|
||||
{
|
||||
public:
|
||||
MemoryMappedAiffReader (const File& file, const AiffAudioFormatReader& reader)
|
||||
: MemoryMappedAudioFormatReader (file, reader, reader.dataChunkStart,
|
||||
reader.bytesPerFrame * reader.lengthInSamples, reader.bytesPerFrame),
|
||||
littleEndian (reader.littleEndian)
|
||||
{
|
||||
}
|
||||
|
||||
bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
|
||||
int64 startSampleInFile, int numSamples)
|
||||
{
|
||||
clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
|
||||
startSampleInFile, numSamples, lengthInSamples);
|
||||
|
||||
if (map == nullptr || ! mappedSection.contains (Range<int64> (startSampleInFile, startSampleInFile + numSamples)))
|
||||
{
|
||||
jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (littleEndian)
|
||||
AiffAudioFormatReader::copySampleData<AudioData::LittleEndian>
|
||||
(bitsPerSample, usesFloatingPointData, destSamples, startOffsetInDestBuffer,
|
||||
numDestChannels, sampleToPointer (startSampleInFile), (int) numChannels, numSamples);
|
||||
else
|
||||
AiffAudioFormatReader::copySampleData<AudioData::BigEndian>
|
||||
(bitsPerSample, usesFloatingPointData, destSamples, startOffsetInDestBuffer,
|
||||
numDestChannels, sampleToPointer (startSampleInFile), (int) numChannels, numSamples);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void readMaxLevels (int64 startSampleInFile, int64 numSamples,
|
||||
float& min0, float& max0, float& min1, float& max1)
|
||||
{
|
||||
if (numSamples <= 0)
|
||||
{
|
||||
min0 = max0 = min1 = max1 = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (map == nullptr || ! mappedSection.contains (Range<int64> (startSampleInFile, startSampleInFile + numSamples)))
|
||||
{
|
||||
jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
|
||||
|
||||
min0 = max0 = min1 = max1 = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (bitsPerSample)
|
||||
{
|
||||
case 8: scanMinAndMax<AudioData::UInt8> (startSampleInFile, numSamples, min0, max0, min1, max1); break;
|
||||
case 16: scanMinAndMax<AudioData::Int16> (startSampleInFile, numSamples, min0, max0, min1, max1); break;
|
||||
case 24: scanMinAndMax<AudioData::Int24> (startSampleInFile, numSamples, min0, max0, min1, max1); break;
|
||||
case 32: if (usesFloatingPointData) scanMinAndMax<AudioData::Float32> (startSampleInFile, numSamples, min0, max0, min1, max1);
|
||||
else scanMinAndMax<AudioData::Int32> (startSampleInFile, numSamples, min0, max0, min1, max1); break;
|
||||
default: jassertfalse; break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const bool littleEndian;
|
||||
|
||||
template <typename SampleType>
|
||||
void scanMinAndMax (int64 startSampleInFile, int64 numSamples,
|
||||
float& min0, float& max0, float& min1, float& max1) const noexcept
|
||||
{
|
||||
if (littleEndian)
|
||||
scanMinAndMax2<SampleType, AudioData::LittleEndian> (startSampleInFile, numSamples, min0, max0, min1, max1);
|
||||
else
|
||||
scanMinAndMax2<SampleType, AudioData::BigEndian> (startSampleInFile, numSamples, min0, max0, min1, max1);
|
||||
}
|
||||
|
||||
template <typename SampleType, typename Endianness>
|
||||
void scanMinAndMax2 (int64 startSampleInFile, int64 numSamples,
|
||||
float& min0, float& max0, float& min1, float& max1) const noexcept
|
||||
{
|
||||
typedef AudioData::Pointer <SampleType, Endianness, AudioData::Interleaved, AudioData::Const> SourceType;
|
||||
|
||||
SourceType (sampleToPointer (startSampleInFile), (int) numChannels)
|
||||
.findMinAndMax ((size_t) numSamples, min0, max0);
|
||||
|
||||
if (numChannels > 1)
|
||||
SourceType (addBytesToPointer (sampleToPointer (startSampleInFile), bitsPerSample / 8), (int) numChannels)
|
||||
.findMinAndMax ((size_t) numSamples, min1, max1);
|
||||
else
|
||||
min1 = max1 = 0;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedAiffReader)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
AiffAudioFormat::AiffAudioFormat()
|
||||
: AudioFormat (TRANS (aiffFormatName), StringArray (aiffExtensions))
|
||||
|
|
@ -711,6 +800,19 @@ AudioFormatReader* AiffAudioFormat::createReaderFor (InputStream* sourceStream,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
MemoryMappedAudioFormatReader* AiffAudioFormat::createMemoryMappedReader (const File& file)
|
||||
{
|
||||
if (FileInputStream* fin = file.createInputStream())
|
||||
{
|
||||
AiffAudioFormatReader reader (fin);
|
||||
|
||||
if (reader.lengthInSamples > 0)
|
||||
return new MemoryMappedAiffReader (file, reader);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AudioFormatWriter* AiffAudioFormat::createWriterFor (OutputStream* out,
|
||||
double sampleRate,
|
||||
unsigned int numberOfChannels,
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ public:
|
|||
AudioFormatReader* createReaderFor (InputStream* sourceStream,
|
||||
bool deleteStreamIfOpeningFails);
|
||||
|
||||
MemoryMappedAudioFormatReader* createMemoryMappedReader (const File&);
|
||||
|
||||
AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo,
|
||||
double sampleRateToUse,
|
||||
unsigned int numberOfChannels,
|
||||
|
|
|
|||
|
|
@ -123,17 +123,8 @@ public:
|
|||
bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
|
||||
int64 startSampleInFile, int numSamples)
|
||||
{
|
||||
jassert (destSamples != nullptr);
|
||||
const int64 samplesAvailable = lengthInSamples - startSampleInFile;
|
||||
|
||||
if (samplesAvailable < numSamples)
|
||||
{
|
||||
for (int i = numDestChannels; --i >= 0;)
|
||||
if (destSamples[i] != nullptr)
|
||||
zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples);
|
||||
|
||||
numSamples = (int) samplesAvailable;
|
||||
}
|
||||
clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
|
||||
startSampleInFile, numSamples, lengthInSamples);
|
||||
|
||||
if (numSamples <= 0)
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -712,17 +712,8 @@ public:
|
|||
bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
|
||||
int64 startSampleInFile, int numSamples)
|
||||
{
|
||||
jassert (destSamples != nullptr);
|
||||
const int64 samplesAvailable = lengthInSamples - startSampleInFile;
|
||||
|
||||
if (samplesAvailable < numSamples)
|
||||
{
|
||||
for (int i = numDestChannels; --i >= 0;)
|
||||
if (destSamples[i] != nullptr)
|
||||
zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples);
|
||||
|
||||
numSamples = (int) samplesAvailable;
|
||||
}
|
||||
clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
|
||||
startSampleInFile, numSamples, lengthInSamples);
|
||||
|
||||
if (numSamples <= 0)
|
||||
return true;
|
||||
|
|
@ -743,15 +734,9 @@ public:
|
|||
zeromem (tempBuffer + bytesRead, (size_t) (numThisTime * bytesPerFrame - bytesRead));
|
||||
}
|
||||
|
||||
switch (bitsPerSample)
|
||||
{
|
||||
case 8: ReadHelper<AudioData::Int32, AudioData::UInt8, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
|
||||
case 16: ReadHelper<AudioData::Int32, AudioData::Int16, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
|
||||
case 24: ReadHelper<AudioData::Int32, AudioData::Int24, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
|
||||
case 32: if (usesFloatingPointData) ReadHelper<AudioData::Float32, AudioData::Float32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime);
|
||||
else ReadHelper<AudioData::Int32, AudioData::Int32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break;
|
||||
default: jassertfalse; break;
|
||||
}
|
||||
copySampleData (bitsPerSample, usesFloatingPointData,
|
||||
destSamples, startOffsetInDestBuffer, numDestChannels,
|
||||
tempBuffer, (int) numChannels, numThisTime);
|
||||
|
||||
startOffsetInDestBuffer += numThisTime;
|
||||
numSamples -= numThisTime;
|
||||
|
|
@ -760,14 +745,27 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
int64 bwavChunkStart, bwavSize;
|
||||
static void copySampleData (unsigned int bitsPerSample, const bool usesFloatingPointData,
|
||||
int* const* destSamples, int startOffsetInDestBuffer, int numDestChannels,
|
||||
const void* sourceData, int numChannels, int numSamples) noexcept
|
||||
{
|
||||
switch (bitsPerSample)
|
||||
{
|
||||
case 8: ReadHelper<AudioData::Int32, AudioData::UInt8, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
|
||||
case 16: ReadHelper<AudioData::Int32, AudioData::Int16, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
|
||||
case 24: ReadHelper<AudioData::Int32, AudioData::Int24, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
|
||||
case 32: if (usesFloatingPointData) ReadHelper<AudioData::Float32, AudioData::Float32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples);
|
||||
else ReadHelper<AudioData::Int32, AudioData::Int32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break;
|
||||
default: jassertfalse; break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedPointer<AudioData::Converter> converter;
|
||||
int bytesPerFrame;
|
||||
int64 bwavChunkStart, bwavSize;
|
||||
int64 dataChunkStart, dataLength;
|
||||
int bytesPerFrame;
|
||||
bool isRF64;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatReader)
|
||||
};
|
||||
|
||||
|
|
@ -853,7 +851,6 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
ScopedPointer<AudioData::Converter> converter;
|
||||
MemoryBlock tempBlock, bwavChunk, smplChunk, instChunk, cueChunk, listChunk;
|
||||
uint64 lengthInSamples, bytesWritten;
|
||||
int64 headerPosition;
|
||||
|
|
@ -1001,6 +998,82 @@ private:
|
|||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatWriter)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class MemoryMappedWavReader : public MemoryMappedAudioFormatReader
|
||||
{
|
||||
public:
|
||||
MemoryMappedWavReader (const File& file, const WavAudioFormatReader& reader)
|
||||
: MemoryMappedAudioFormatReader (file, reader, reader.dataChunkStart,
|
||||
reader.dataLength, reader.bytesPerFrame)
|
||||
{
|
||||
}
|
||||
|
||||
bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
|
||||
int64 startSampleInFile, int numSamples)
|
||||
{
|
||||
clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
|
||||
startSampleInFile, numSamples, lengthInSamples);
|
||||
|
||||
if (map == nullptr || ! mappedSection.contains (Range<int64> (startSampleInFile, startSampleInFile + numSamples)))
|
||||
{
|
||||
jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
|
||||
return false;
|
||||
}
|
||||
|
||||
WavAudioFormatReader::copySampleData (bitsPerSample, usesFloatingPointData,
|
||||
destSamples, startOffsetInDestBuffer, numDestChannels,
|
||||
sampleToPointer (startSampleInFile), (int) numChannels, numSamples);
|
||||
return true;
|
||||
}
|
||||
|
||||
void readMaxLevels (int64 startSampleInFile, int64 numSamples,
|
||||
float& min0, float& max0, float& min1, float& max1)
|
||||
{
|
||||
if (numSamples <= 0)
|
||||
{
|
||||
min0 = max0 = min1 = max1 = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (map == nullptr || ! mappedSection.contains (Range<int64> (startSampleInFile, startSampleInFile + numSamples)))
|
||||
{
|
||||
jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
|
||||
|
||||
min0 = max0 = min1 = max1 = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (bitsPerSample)
|
||||
{
|
||||
case 8: scanMinAndMax<AudioData::UInt8> (startSampleInFile, numSamples, min0, max0, min1, max1); break;
|
||||
case 16: scanMinAndMax<AudioData::Int16> (startSampleInFile, numSamples, min0, max0, min1, max1); break;
|
||||
case 24: scanMinAndMax<AudioData::Int24> (startSampleInFile, numSamples, min0, max0, min1, max1); break;
|
||||
case 32: if (usesFloatingPointData) scanMinAndMax<AudioData::Float32> (startSampleInFile, numSamples, min0, max0, min1, max1);
|
||||
else scanMinAndMax<AudioData::Int32> (startSampleInFile, numSamples, min0, max0, min1, max1); break;
|
||||
default: jassertfalse; break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename SampleType>
|
||||
void scanMinAndMax (int64 startSampleInFile, int64 numSamples,
|
||||
float& min0, float& max0, float& min1, float& max1) const
|
||||
{
|
||||
typedef AudioData::Pointer <SampleType, AudioData::LittleEndian, AudioData::Interleaved, AudioData::Const> SourceType;
|
||||
|
||||
SourceType (sampleToPointer (startSampleInFile), (int) numChannels)
|
||||
.findMinAndMax ((size_t) numSamples, min0, max0);
|
||||
|
||||
if (numChannels > 1)
|
||||
SourceType (addBytesToPointer (sampleToPointer (startSampleInFile), bitsPerSample / 8), (int) numChannels)
|
||||
.findMinAndMax ((size_t) numSamples, min1, max1);
|
||||
else
|
||||
min1 = max1 = 0;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedWavReader)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
WavAudioFormat::WavAudioFormat()
|
||||
: AudioFormat (TRANS (wavFormatName), StringArray (wavExtensions))
|
||||
|
|
@ -1040,6 +1113,19 @@ AudioFormatReader* WavAudioFormat::createReaderFor (InputStream* sourceStream,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
MemoryMappedAudioFormatReader* WavAudioFormat::createMemoryMappedReader (const File& file)
|
||||
{
|
||||
if (FileInputStream* fin = file.createInputStream())
|
||||
{
|
||||
WavAudioFormatReader reader (fin);
|
||||
|
||||
if (reader.lengthInSamples > 0)
|
||||
return new MemoryMappedWavReader (file, reader);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AudioFormatWriter* WavAudioFormat::createWriterFor (OutputStream* out, double sampleRate,
|
||||
unsigned int numChannels, int bitsPerSample,
|
||||
const StringPairArray& metadataValues, int /*qualityOptionIndex*/)
|
||||
|
|
|
|||
|
|
@ -120,6 +120,8 @@ public:
|
|||
AudioFormatReader* createReaderFor (InputStream* sourceStream,
|
||||
bool deleteStreamIfOpeningFails);
|
||||
|
||||
MemoryMappedAudioFormatReader* createMemoryMappedReader (const File& file);
|
||||
|
||||
AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo,
|
||||
double sampleRateToUse,
|
||||
unsigned int numberOfChannels,
|
||||
|
|
|
|||
|
|
@ -47,3 +47,8 @@ const String& AudioFormat::getFormatName() const { return formatN
|
|||
const StringArray& AudioFormat::getFileExtensions() const { return fileExtensions; }
|
||||
bool AudioFormat::isCompressed() { return false; }
|
||||
StringArray AudioFormat::getQualityOptions() { return StringArray(); }
|
||||
|
||||
MemoryMappedAudioFormatReader* AudioFormat::createMemoryMappedReader (const File&)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
#include "juce_AudioFormatReader.h"
|
||||
#include "juce_AudioFormatWriter.h"
|
||||
|
||||
#include "juce_MemoryMappedAudioFormatReader.h"
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
|
|
@ -113,6 +113,13 @@ public:
|
|||
virtual AudioFormatReader* createReaderFor (InputStream* sourceStream,
|
||||
bool deleteStreamIfOpeningFails) = 0;
|
||||
|
||||
/** Attempts to create a MemoryMappedAudioFormatReader, if possible for this
|
||||
format.
|
||||
|
||||
If the format does not support this, the method will return nullptr;
|
||||
*/
|
||||
virtual MemoryMappedAudioFormatReader* createMemoryMappedReader (const File& file);
|
||||
|
||||
/** Tries to create an object that can write to a stream with this audio format.
|
||||
|
||||
The writer object that is returned can be used to write to the stream, and
|
||||
|
|
|
|||
|
|
@ -23,15 +23,14 @@
|
|||
==============================================================================
|
||||
*/
|
||||
|
||||
AudioFormatReader::AudioFormatReader (InputStream* const in,
|
||||
const String& formatName_)
|
||||
AudioFormatReader::AudioFormatReader (InputStream* const in, const String& name)
|
||||
: sampleRate (0),
|
||||
bitsPerSample (0),
|
||||
lengthInSamples (0),
|
||||
numChannels (0),
|
||||
usesFloatingPointData (false),
|
||||
input (in),
|
||||
formatName (formatName_)
|
||||
formatName (name)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -365,3 +364,56 @@ int64 AudioFormatReader::searchForLevel (int64 startSample,
|
|||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
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 (const Range<int64>& samplesToMap)
|
||||
{
|
||||
if (map == nullptr || samplesToMap != mappedSection)
|
||||
{
|
||||
map = nullptr;
|
||||
|
||||
const Range<int64> fileRange (sampleToFilePos (samplesToMap.getStart()),
|
||||
sampleToFilePos (samplesToMap.getEnd()));
|
||||
|
||||
map = new MemoryMappedFile (file, fileRange, MemoryMappedFile::readOnly);
|
||||
|
||||
if (map->getData() == nullptr)
|
||||
map = nullptr;
|
||||
else
|
||||
mappedSection = Range<int64> (jmax ((int64) 0, filePosToSample (map->getRange().getStart())),
|
||||
jmin (lengthInSamples, filePosToSample (map->getRange().getEnd())));
|
||||
}
|
||||
|
||||
return map != nullptr;
|
||||
}
|
||||
|
||||
void MemoryMappedAudioFormatReader::touchSample (int64 sample) const noexcept
|
||||
{
|
||||
if (map != nullptr && mappedSection.contains (sample))
|
||||
{
|
||||
static int dummy = 0; // to force the compiler not to optimise this stuff away
|
||||
dummy += *(int*) sampleToPointer (sample);
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -237,13 +237,15 @@ protected:
|
|||
typedef AudioData::Pointer <DestSampleType, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> DestType;
|
||||
typedef AudioData::Pointer <SourceSampleType, SourceEndianness, AudioData::Interleaved, AudioData::Const> SourceType;
|
||||
|
||||
static void read (int** destData, int destOffset, int numDestChannels, const void* sourceData, int numSourceChannels, int numSamples) noexcept
|
||||
template <typename TargetType>
|
||||
static void read (TargetType* const* destData, int destOffset, int numDestChannels,
|
||||
const void* sourceData, int numSourceChannels, int numSamples) noexcept
|
||||
{
|
||||
for (int i = 0; i < numDestChannels; ++i)
|
||||
{
|
||||
if (destData[i] != nullptr)
|
||||
if (void* targetChan = destData[i])
|
||||
{
|
||||
DestType dest (destData[i]);
|
||||
DestType dest (targetChan);
|
||||
dest += destOffset;
|
||||
|
||||
if (i < numSourceChannels)
|
||||
|
|
@ -255,6 +257,26 @@ protected:
|
|||
}
|
||||
};
|
||||
|
||||
/** Used by AudioFormatReader subclasses to clear any parts of the data blocks that lie
|
||||
beyond the end of their available length.
|
||||
*/
|
||||
static void clearSamplesBeyondAvailableLength (int** destSamples, int numDestChannels,
|
||||
int startOffsetInDestBuffer, int64 startSampleInFile,
|
||||
int& numSamples, int64 fileLengthInSamples)
|
||||
{
|
||||
jassert (destSamples != nullptr);
|
||||
const int64 samplesAvailable = fileLengthInSamples - startSampleInFile;
|
||||
|
||||
if (samplesAvailable < numSamples)
|
||||
{
|
||||
for (int i = numDestChannels; --i >= 0;)
|
||||
if (destSamples[i] != nullptr)
|
||||
zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples);
|
||||
|
||||
numSamples = (int) samplesAvailable;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
String formatName;
|
||||
|
||||
|
|
|
|||
|
|
@ -51,17 +51,8 @@ AudioSubsectionReader::~AudioSubsectionReader()
|
|||
bool AudioSubsectionReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
|
||||
int64 startSampleInFile, int numSamples)
|
||||
{
|
||||
if (startSampleInFile + numSamples > length)
|
||||
{
|
||||
for (int i = numDestChannels; --i >= 0;)
|
||||
if (destSamples[i] != nullptr)
|
||||
zeromem (destSamples[i], sizeof (int) * (size_t) numSamples);
|
||||
|
||||
numSamples = jmin (numSamples, (int) (length - startSampleInFile));
|
||||
|
||||
if (numSamples <= 0)
|
||||
return true;
|
||||
}
|
||||
clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
|
||||
startSampleInFile, numSamples, length);
|
||||
|
||||
return source->readSamples (destSamples, numDestChannels, startOffsetInDestBuffer,
|
||||
startSampleInFile + startSample, numSamples);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_MEMORYMAPPEDAUDIOFORMATREADER_JUCEHEADER__
|
||||
#define __JUCE_MEMORYMAPPEDAUDIOFORMATREADER_JUCEHEADER__
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A specialised type of AudioFormatReader that uses a MemoryMappedFile to read
|
||||
directly from an audio file.
|
||||
|
||||
This allows for incredibly fast random-access to sample data in the mapped
|
||||
region of the file, but not all audio formats support it - see
|
||||
AudioFormat::createMemoryMappedReader().
|
||||
|
||||
Note that before reading samples from a MemoryMappedAudioFormatReader, you must first
|
||||
call mapEntireFile() or mapSectionOfFile() to ensure that the region you want to
|
||||
read has been mapped.
|
||||
|
||||
@see AudioFormat::createMemoryMappedReader, AudioFormatReader
|
||||
*/
|
||||
class JUCE_API MemoryMappedAudioFormatReader : public AudioFormatReader
|
||||
{
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Creates an MemoryMappedAudioFormatReader object.
|
||||
|
||||
Note that before attempting to read any data, you must call mapEntireFile()
|
||||
or mapSectionOfFile() to ensure that the region you want to read has
|
||||
been mapped.
|
||||
*/
|
||||
MemoryMappedAudioFormatReader (const File& file, const AudioFormatReader& details,
|
||||
int64 dataChunkStart, int64 dataChunkLength, int bytesPerFrame);
|
||||
|
||||
public:
|
||||
/** Returns the file that is being mapped */
|
||||
const File& getFile() const noexcept { return file; }
|
||||
|
||||
/** Attempts to map the entire file into memory. */
|
||||
bool mapEntireFile();
|
||||
|
||||
/** Attempts to map a section of the file into memory. */
|
||||
bool mapSectionOfFile (const Range<int64>& samplesToMap);
|
||||
|
||||
/** Returns the sample range that's currently memory-mapped and available for reading. */
|
||||
const Range<int64>& getMappedSection() const noexcept { return mappedSection; }
|
||||
|
||||
/** Touches the memory for the given sample, to force it to be loaded into active memory. */
|
||||
void touchSample (int64 sample) const noexcept;
|
||||
|
||||
/** Returns the number of bytes currently being mapped */
|
||||
size_t getNumBytesUsed() const { return map != nullptr ? map->getSize() : 0; }
|
||||
|
||||
protected:
|
||||
File file;
|
||||
Range<int64> mappedSection;
|
||||
ScopedPointer<MemoryMappedFile> map;
|
||||
int64 dataChunkStart, dataLength;
|
||||
int bytesPerFrame;
|
||||
|
||||
inline int64 sampleToFilePos (int64 sample) const noexcept { return dataChunkStart + sample * bytesPerFrame; }
|
||||
inline int64 filePosToSample (int64 filePos) const noexcept { return (filePos - dataChunkStart) / bytesPerFrame; }
|
||||
inline const void* sampleToPointer (int64 sample) const noexcept { return addBytesToPointer (map->getData(), sampleToFilePos (sample) - map->getRange().getStart()); }
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedAudioFormatReader)
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_MEMORYMAPPEDAUDIOFORMATREADER_JUCEHEADER__
|
||||
|
|
@ -105,6 +105,9 @@ namespace juce
|
|||
#ifndef __JUCE_AUDIOSUBSECTIONREADER_JUCEHEADER__
|
||||
#include "format/juce_AudioSubsectionReader.h"
|
||||
#endif
|
||||
#ifndef __JUCE_MEMORYMAPPEDAUDIOFORMATREADER_JUCEHEADER__
|
||||
#include "format/juce_MemoryMappedAudioFormatReader.h"
|
||||
#endif
|
||||
#include "codecs/juce_AiffAudioFormat.h"
|
||||
#include "codecs/juce_CoreAudioFormat.h"
|
||||
#include "codecs/juce_FlacAudioFormat.h"
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ bool AudioThumbnailCache::loadThumb (AudioThumbnailBase& thumb, const int64 hash
|
|||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return loadNewThumb (thumb, hashCode);
|
||||
}
|
||||
|
||||
void AudioThumbnailCache::storeThumb (const AudioThumbnailBase& thumb,
|
||||
|
|
@ -130,6 +130,8 @@ void AudioThumbnailCache::storeThumb (const AudioThumbnailBase& thumb,
|
|||
|
||||
MemoryOutputStream out (te->data, false);
|
||||
thumb.saveTo (out);
|
||||
|
||||
saveNewlyFinishedThumbnail (thumb, hashCode);
|
||||
}
|
||||
|
||||
void AudioThumbnailCache::clear()
|
||||
|
|
@ -168,3 +170,12 @@ void AudioThumbnailCache::writeToStream (OutputStream& out)
|
|||
for (int i = 0; i < thumbs.size(); ++i)
|
||||
thumbs.getUnchecked(i)->write (out);
|
||||
}
|
||||
|
||||
void AudioThumbnailCache::saveNewlyFinishedThumbnail (const AudioThumbnailBase&, int64)
|
||||
{
|
||||
}
|
||||
|
||||
bool AudioThumbnailCache::loadNewThumb (AudioThumbnailBase&, int64)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ public:
|
|||
explicit AudioThumbnailCache (int maxNumThumbsToStore);
|
||||
|
||||
/** Destructor. */
|
||||
~AudioThumbnailCache();
|
||||
virtual ~AudioThumbnailCache();
|
||||
|
||||
//==============================================================================
|
||||
/** Clears out any stored thumbnails.
|
||||
|
|
@ -88,6 +88,12 @@ public:
|
|||
/** Returns the thread that client thumbnails can use. */
|
||||
TimeSliceThread& getTimeSliceThread() noexcept { return thread; }
|
||||
|
||||
protected:
|
||||
/** */
|
||||
virtual void saveNewlyFinishedThumbnail (const AudioThumbnailBase&, int64 hashCode);
|
||||
/** */
|
||||
virtual bool loadNewThumb (AudioThumbnailBase&, int64 hashCode);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
TimeSliceThread thread;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue