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,
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,


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

@@ -53,6 +53,8 @@ public:
AudioFormatReader* createReaderFor (InputStream* sourceStream,
bool deleteStreamIfOpeningFails);
MemoryMappedAudioFormatReader* createMemoryMappedReader (const File&);
AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo,
double sampleRateToUse,
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,
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;


+ 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,
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*/)


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

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


+ 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; }
bool AudioFormat::isCompressed() { return false; }
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_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


+ 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),
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.
}
}

+ 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 <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;


+ 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,
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);


+ 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__
#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"


+ 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 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;
}

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

@@ -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…
Cancel
Save