Browse Source

New class of audio format readers: MemoryMappedAudioFormatReader, which does what it says on the tin.

tags/2021-05-28
jules 12 years ago
parent
commit
27895cb6bd
14 changed files with 457 additions and 86 deletions
  1. +132
    -30
      modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp
  2. +2
    -0
      modules/juce_audio_formats/codecs/juce_AiffAudioFormat.h
  3. +2
    -11
      modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp
  4. +111
    -25
      modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp
  5. +2
    -0
      modules/juce_audio_formats/codecs/juce_WavAudioFormat.h
  6. +5
    -0
      modules/juce_audio_formats/format/juce_AudioFormat.cpp
  7. +8
    -1
      modules/juce_audio_formats/format/juce_AudioFormat.h
  8. +55
    -3
      modules/juce_audio_formats/format/juce_AudioFormatReader.cpp
  9. +25
    -3
      modules/juce_audio_formats/format/juce_AudioFormatReader.h
  10. +2
    -11
      modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp
  11. +91
    -0
      modules/juce_audio_formats/format/juce_MemoryMappedAudioFormatReader.h
  12. +3
    -0
      modules/juce_audio_formats/juce_audio_formats.h
  13. +12
    -1
      modules/juce_audio_utils/gui/juce_AudioThumbnailCache.cpp
  14. +7
    -1
      modules/juce_audio_utils/gui/juce_AudioThumbnailCache.h

+ 132
- 30
modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp View File

@@ -407,16 +407,8 @@ public:
bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
int64 startSampleInFile, int numSamples) 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) if (numSamples <= 0)
return true; return true;
@@ -440,27 +432,14 @@ public:
jassert (! usesFloatingPointData); // (would need to add support for this if it's possible) jassert (! usesFloatingPointData); // (would need to add support for this if it's possible)
if (littleEndian) 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 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; startOffsetInDestBuffer += numThisTime;
numSamples -= numThisTime; numSamples -= numThisTime;
@@ -469,6 +448,21 @@ public:
return true; 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; int bytesPerFrame;
int64 dataChunkStart; int64 dataChunkStart;
bool littleEndian; bool littleEndian;
@@ -661,6 +655,101 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatWriter) 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() AiffAudioFormat::AiffAudioFormat()
: AudioFormat (TRANS (aiffFormatName), StringArray (aiffExtensions)) : AudioFormat (TRANS (aiffFormatName), StringArray (aiffExtensions))
@@ -711,6 +800,19 @@ AudioFormatReader* AiffAudioFormat::createReaderFor (InputStream* sourceStream,
return nullptr; 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, AudioFormatWriter* AiffAudioFormat::createWriterFor (OutputStream* out,
double sampleRate, double sampleRate,
unsigned int numberOfChannels, unsigned int numberOfChannels,


+ 2
- 0
modules/juce_audio_formats/codecs/juce_AiffAudioFormat.h View File

@@ -53,6 +53,8 @@ public:
AudioFormatReader* createReaderFor (InputStream* sourceStream, AudioFormatReader* createReaderFor (InputStream* sourceStream,
bool deleteStreamIfOpeningFails); bool deleteStreamIfOpeningFails);
MemoryMappedAudioFormatReader* createMemoryMappedReader (const File&);
AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo,
double sampleRateToUse, double sampleRateToUse,
unsigned int numberOfChannels, unsigned int numberOfChannels,


+ 2
- 11
modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp View File

@@ -123,17 +123,8 @@ public:
bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
int64 startSampleInFile, int numSamples) 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) if (numSamples <= 0)
return true; return true;


+ 111
- 25
modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp View File

@@ -712,17 +712,8 @@ public:
bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
int64 startSampleInFile, int numSamples) 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) if (numSamples <= 0)
return true; return true;
@@ -743,15 +734,9 @@ public:
zeromem (tempBuffer + bytesRead, (size_t) (numThisTime * bytesPerFrame - bytesRead)); 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; startOffsetInDestBuffer += numThisTime;
numSamples -= numThisTime; numSamples -= numThisTime;
@@ -760,14 +745,27 @@ public:
return true; 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; int64 dataChunkStart, dataLength;
int bytesPerFrame;
bool isRF64; bool isRF64;
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatReader) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatReader)
}; };
@@ -853,7 +851,6 @@ public:
} }
private: private:
ScopedPointer<AudioData::Converter> converter;
MemoryBlock tempBlock, bwavChunk, smplChunk, instChunk, cueChunk, listChunk; MemoryBlock tempBlock, bwavChunk, smplChunk, instChunk, cueChunk, listChunk;
uint64 lengthInSamples, bytesWritten; uint64 lengthInSamples, bytesWritten;
int64 headerPosition; int64 headerPosition;
@@ -1001,6 +998,82 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatWriter) 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() WavAudioFormat::WavAudioFormat()
: AudioFormat (TRANS (wavFormatName), StringArray (wavExtensions)) : AudioFormat (TRANS (wavFormatName), StringArray (wavExtensions))
@@ -1040,6 +1113,19 @@ AudioFormatReader* WavAudioFormat::createReaderFor (InputStream* sourceStream,
return nullptr; 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, AudioFormatWriter* WavAudioFormat::createWriterFor (OutputStream* out, double sampleRate,
unsigned int numChannels, int bitsPerSample, unsigned int numChannels, int bitsPerSample,
const StringPairArray& metadataValues, int /*qualityOptionIndex*/) const StringPairArray& metadataValues, int /*qualityOptionIndex*/)


+ 2
- 0
modules/juce_audio_formats/codecs/juce_WavAudioFormat.h View File

@@ -120,6 +120,8 @@ public:
AudioFormatReader* createReaderFor (InputStream* sourceStream, AudioFormatReader* createReaderFor (InputStream* sourceStream,
bool deleteStreamIfOpeningFails); bool deleteStreamIfOpeningFails);
MemoryMappedAudioFormatReader* createMemoryMappedReader (const File& file);
AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo,
double sampleRateToUse, double sampleRateToUse,
unsigned int numberOfChannels, unsigned int numberOfChannels,


+ 5
- 0
modules/juce_audio_formats/format/juce_AudioFormat.cpp View File

@@ -47,3 +47,8 @@ const String& AudioFormat::getFormatName() const { return formatN
const StringArray& AudioFormat::getFileExtensions() const { return fileExtensions; } const StringArray& AudioFormat::getFileExtensions() const { return fileExtensions; }
bool AudioFormat::isCompressed() { return false; } bool AudioFormat::isCompressed() { return false; }
StringArray AudioFormat::getQualityOptions() { return StringArray(); } StringArray AudioFormat::getQualityOptions() { return StringArray(); }
MemoryMappedAudioFormatReader* AudioFormat::createMemoryMappedReader (const File&)
{
return nullptr;
}

+ 8
- 1
modules/juce_audio_formats/format/juce_AudioFormat.h View File

@@ -28,7 +28,7 @@
#include "juce_AudioFormatReader.h" #include "juce_AudioFormatReader.h"
#include "juce_AudioFormatWriter.h" #include "juce_AudioFormatWriter.h"
#include "juce_MemoryMappedAudioFormatReader.h"
//============================================================================== //==============================================================================
/** /**
@@ -113,6 +113,13 @@ public:
virtual AudioFormatReader* createReaderFor (InputStream* sourceStream, virtual AudioFormatReader* createReaderFor (InputStream* sourceStream,
bool deleteStreamIfOpeningFails) = 0; 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. /** 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 The writer object that is returned can be used to write to the stream, and


+ 55
- 3
modules/juce_audio_formats/format/juce_AudioFormatReader.cpp View File

@@ -23,15 +23,14 @@
============================================================================== ==============================================================================
*/ */
AudioFormatReader::AudioFormatReader (InputStream* const in,
const String& formatName_)
AudioFormatReader::AudioFormatReader (InputStream* const in, const String& name)
: sampleRate (0), : sampleRate (0),
bitsPerSample (0), bitsPerSample (0),
lengthInSamples (0), lengthInSamples (0),
numChannels (0), numChannels (0),
usesFloatingPointData (false), usesFloatingPointData (false),
input (in), input (in),
formatName (formatName_)
formatName (name)
{ {
} }
@@ -365,3 +364,56 @@ int64 AudioFormatReader::searchForLevel (int64 startSample,
return -1; 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.
}
}

+ 25
- 3
modules/juce_audio_formats/format/juce_AudioFormatReader.h View File

@@ -237,13 +237,15 @@ protected:
typedef AudioData::Pointer <DestSampleType, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> DestType; typedef AudioData::Pointer <DestSampleType, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> DestType;
typedef AudioData::Pointer <SourceSampleType, SourceEndianness, AudioData::Interleaved, AudioData::Const> SourceType; 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) 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; dest += destOffset;
if (i < numSourceChannels) 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: private:
String formatName; String formatName;


+ 2
- 11
modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp View File

@@ -51,17 +51,8 @@ AudioSubsectionReader::~AudioSubsectionReader()
bool AudioSubsectionReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, bool AudioSubsectionReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
int64 startSampleInFile, int numSamples) 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, return source->readSamples (destSamples, numDestChannels, startOffsetInDestBuffer,
startSampleInFile + startSample, numSamples); startSampleInFile + startSample, numSamples);


+ 91
- 0
modules/juce_audio_formats/format/juce_MemoryMappedAudioFormatReader.h View File

@@ -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__

+ 3
- 0
modules/juce_audio_formats/juce_audio_formats.h View File

@@ -105,6 +105,9 @@ namespace juce
#ifndef __JUCE_AUDIOSUBSECTIONREADER_JUCEHEADER__ #ifndef __JUCE_AUDIOSUBSECTIONREADER_JUCEHEADER__
#include "format/juce_AudioSubsectionReader.h" #include "format/juce_AudioSubsectionReader.h"
#endif #endif
#ifndef __JUCE_MEMORYMAPPEDAUDIOFORMATREADER_JUCEHEADER__
#include "format/juce_MemoryMappedAudioFormatReader.h"
#endif
#include "codecs/juce_AiffAudioFormat.h" #include "codecs/juce_AiffAudioFormat.h"
#include "codecs/juce_CoreAudioFormat.h" #include "codecs/juce_CoreAudioFormat.h"
#include "codecs/juce_FlacAudioFormat.h" #include "codecs/juce_FlacAudioFormat.h"


+ 12
- 1
modules/juce_audio_utils/gui/juce_AudioThumbnailCache.cpp View File

@@ -109,7 +109,7 @@ bool AudioThumbnailCache::loadThumb (AudioThumbnailBase& thumb, const int64 hash
return true; return true;
} }
return false;
return loadNewThumb (thumb, hashCode);
} }
void AudioThumbnailCache::storeThumb (const AudioThumbnailBase& thumb, void AudioThumbnailCache::storeThumb (const AudioThumbnailBase& thumb,
@@ -130,6 +130,8 @@ void AudioThumbnailCache::storeThumb (const AudioThumbnailBase& thumb,
MemoryOutputStream out (te->data, false); MemoryOutputStream out (te->data, false);
thumb.saveTo (out); thumb.saveTo (out);
saveNewlyFinishedThumbnail (thumb, hashCode);
} }
void AudioThumbnailCache::clear() void AudioThumbnailCache::clear()
@@ -168,3 +170,12 @@ void AudioThumbnailCache::writeToStream (OutputStream& out)
for (int i = 0; i < thumbs.size(); ++i) for (int i = 0; i < thumbs.size(); ++i)
thumbs.getUnchecked(i)->write (out); thumbs.getUnchecked(i)->write (out);
} }
void AudioThumbnailCache::saveNewlyFinishedThumbnail (const AudioThumbnailBase&, int64)
{
}
bool AudioThumbnailCache::loadNewThumb (AudioThumbnailBase&, int64)
{
return false;
}

+ 7
- 1
modules/juce_audio_utils/gui/juce_AudioThumbnailCache.h View File

@@ -51,7 +51,7 @@ public:
explicit AudioThumbnailCache (int maxNumThumbsToStore); explicit AudioThumbnailCache (int maxNumThumbsToStore);
/** Destructor. */ /** Destructor. */
~AudioThumbnailCache();
virtual ~AudioThumbnailCache();
//============================================================================== //==============================================================================
/** Clears out any stored thumbnails. /** Clears out any stored thumbnails.
@@ -88,6 +88,12 @@ public:
/** Returns the thread that client thumbnails can use. */ /** Returns the thread that client thumbnails can use. */
TimeSliceThread& getTimeSliceThread() noexcept { return thread; } TimeSliceThread& getTimeSliceThread() noexcept { return thread; }
protected:
/** */
virtual void saveNewlyFinishedThumbnail (const AudioThumbnailBase&, int64 hashCode);
/** */
virtual bool loadNewThumb (AudioThumbnailBase&, int64 hashCode);
private: private:
//============================================================================== //==============================================================================
TimeSliceThread thread; TimeSliceThread thread;


Loading…
Cancel
Save