diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 0ebda9d6df..3322f16a42 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -21535,45 +21535,13 @@ END_JUCE_NAMESPACE /*** Start of inlined file: juce_AudioThumbnail.cpp ***/ BEGIN_JUCE_NAMESPACE -const int timeBeforeDeletingReader = 2000; - -struct AudioThumbnailDataFormat -{ - char thumbnailMagic[4]; - int samplesPerThumbSample; - int64 totalSamples; // source samples - int64 numFinishedSamples; // source samples - int numThumbnailSamples; - int numChannels; - int sampleRate; - char future[16]; - char data[1]; - - void swapEndiannessIfNeeded() throw() - { - #if JUCE_BIG_ENDIAN - flip (samplesPerThumbSample); - flip (totalSamples); - flip (numFinishedSamples); - flip (numThumbnailSamples); - flip (numChannels); - flip (sampleRate); - #endif - } - -private: - #if JUCE_BIG_ENDIAN - static void flip (int& n) { n = (int) ByteOrder::swap ((uint32) n); } - static void flip (int64& n) { n = (int64) ByteOrder::swap ((uint64) n); } - #endif -}; - AudioThumbnail::AudioThumbnail (const int orginalSamplesPerThumbnailSample_, AudioFormatManager& formatManagerToUse_, AudioThumbnailCache& cacheToUse) : formatManagerToUse (formatManagerToUse_), cache (cacheToUse), - orginalSamplesPerThumbnailSample (orginalSamplesPerThumbnailSample_) + orginalSamplesPerThumbnailSample (orginalSamplesPerThumbnailSample_), + timeBeforeDeletingReader (2000) { clear(); } @@ -21586,6 +21554,12 @@ AudioThumbnail::~AudioThumbnail() reader = 0; } +AudioThumbnail::DataFormat* AudioThumbnail::getData() const throw() +{ + jassert (data.getData() != 0); + return static_cast (data.getData()); +} + void AudioThumbnail::setSource (InputSource* const newSource) { cache.removeThumbnail (this); @@ -21670,9 +21644,9 @@ void AudioThumbnail::timerCallback() void AudioThumbnail::clear() { - data.setSize (sizeof (AudioThumbnailDataFormat) + 3); + data.setSize (sizeof (DataFormat) + 3); - AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); + DataFormat* const d = getData(); d->thumbnailMagic[0] = 'j'; d->thumbnailMagic[1] = 'a'; @@ -21697,8 +21671,8 @@ void AudioThumbnail::loadFrom (InputStream& input) data.setSize (0); input.readIntoMemoryBlock (data); - AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); - d->swapEndiannessIfNeeded(); + DataFormat* const d = getData(); + d->flipEndiannessIfBigEndian(); if (! (d->thumbnailMagic[0] == 'j' && d->thumbnailMagic[1] == 'a' @@ -21714,15 +21688,17 @@ void AudioThumbnail::loadFrom (InputStream& input) void AudioThumbnail::saveTo (OutputStream& output) const { - AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); - d->swapEndiannessIfNeeded(); - output.write (data.getData(), (int) data.getSize()); - d->swapEndiannessIfNeeded(); + const ScopedLock sl (readerLock); + + DataFormat* const d = getData(); + d->flipEndiannessIfBigEndian(); + output.write (d, (int) data.getSize()); + d->flipEndiannessIfBigEndian(); } bool AudioThumbnail::initialiseFromAudioFile (AudioFormatReader& fileReader) { - AudioThumbnailDataFormat* d = (AudioThumbnailDataFormat*) data.getData(); + DataFormat* d = getData(); d->totalSamples = fileReader.lengthInSamples; d->numChannels = jmin ((uint32) 2, fileReader.numChannels); @@ -21730,17 +21706,17 @@ bool AudioThumbnail::initialiseFromAudioFile (AudioFormatReader& fileReader) d->sampleRate = roundToInt (fileReader.sampleRate); d->numThumbnailSamples = (int) (d->totalSamples / d->samplesPerThumbSample) + 1; - data.setSize (sizeof (AudioThumbnailDataFormat) + 3 + d->numThumbnailSamples * d->numChannels * 2); + data.setSize (sizeof (DataFormat) + 3 + d->numThumbnailSamples * d->numChannels * 2); - d = (AudioThumbnailDataFormat*) data.getData(); - zeromem (&(d->data[0]), d->numThumbnailSamples * d->numChannels * 2); + d = getData(); + zeromem (d->data, d->numThumbnailSamples * d->numChannels * 2); return d->totalSamples > 0; } bool AudioThumbnail::readNextBlockFromAudioFile (AudioFormatReader& fileReader) { - AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); + DataFormat* const d = getData(); if (d->numFinishedSamples < d->totalSamples) { @@ -21754,24 +21730,20 @@ bool AudioThumbnail::readNextBlockFromAudioFile (AudioFormatReader& fileReader) } cacheNeedsRefilling = true; - return (d->numFinishedSamples < d->totalSamples); + return d->numFinishedSamples < d->totalSamples; } int AudioThumbnail::getNumChannels() const throw() { - const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); - jassert (d != 0); - - return d->numChannels; + return getData()->numChannels; } double AudioThumbnail::getTotalLength() const throw() { - const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); - jassert (d != 0); + const DataFormat* const d = getData(); if (d->sampleRate > 0) - return d->totalSamples / (double)d->sampleRate; + return d->totalSamples / (double) d->sampleRate; else return 0.0; } @@ -21780,14 +21752,13 @@ void AudioThumbnail::generateSection (AudioFormatReader& fileReader, int64 startSample, int numSamples) { - AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); - jassert (d != 0); + DataFormat* const d = getData(); - int firstDataPos = (int) (startSample / d->samplesPerThumbSample); - int lastDataPos = (int) ((startSample + numSamples) / d->samplesPerThumbSample); + const int firstDataPos = (int) (startSample / d->samplesPerThumbSample); + const int lastDataPos = (int) ((startSample + numSamples) / d->samplesPerThumbSample); - char* l = getChannelData (0); - char* r = getChannelData (1); + char* const l = getChannelData (0); + char* const r = getChannelData (1); for (int i = firstDataPos; i < lastDataPos; ++i) { @@ -21822,8 +21793,7 @@ void AudioThumbnail::generateSection (AudioFormatReader& fileReader, char* AudioThumbnail::getChannelData (int channel) const { - AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); - jassert (d != 0); + DataFormat* const d = getData(); if (channel >= 0 && channel < d->numChannels) return d->data + (channel * 2 * d->numThumbnailSamples); @@ -21833,9 +21803,7 @@ char* AudioThumbnail::getChannelData (int channel) const bool AudioThumbnail::isFullyLoaded() const throw() { - const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); - jassert (d != 0); - + const DataFormat* const d = getData(); return d->numFinishedSamples >= d->totalSamples; } @@ -21843,8 +21811,7 @@ void AudioThumbnail::refillCache (const int numSamples, double startTime, const double timePerPixel) { - const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); - jassert (d != 0); + const DataFormat* const d = getData(); if (numSamples <= 0 || timePerPixel <= 0.0 @@ -22017,17 +21984,33 @@ void AudioThumbnail::drawChannel (Graphics& g, cacheData += numChannelsCached << 1; if (mn <= mx) // if the wrong way round, signifies that the sample's not yet known - g.drawLine ((float) x, jmax (midY - mx * vscale - 0.3f, topY), - (float) x, jmin (midY - mn * vscale + 0.3f, bottomY)); - - ++x; + g.drawVerticalLine (x, jmax (midY - mx * vscale - 0.3f, topY), + jmin (midY - mn * vscale + 0.3f, bottomY)); - if (x >= clip.getRight()) + if (++x >= clip.getRight()) break; } } } +void AudioThumbnail::DataFormat::flipEndiannessIfBigEndian() throw() +{ + #if JUCE_BIG_ENDIAN + struct Flipper + { + static void flip (int32& n) { n = (int32) ByteOrder::swap ((uint32) n); } + static void flip (int64& n) { n = (int64) ByteOrder::swap ((uint64) n); } + }; + + Flipper::flip (samplesPerThumbSample); + Flipper::flip (totalSamples); + Flipper::flip (numFinishedSamples); + Flipper::flip (numThumbnailSamples); + Flipper::flip (numChannels); + Flipper::flip (sampleRate); + #endif +} + END_JUCE_NAMESPACE /*** End of inlined file: juce_AudioThumbnail.cpp ***/ diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 25e0f8fc7d..3c925772d0 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -64,7 +64,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 52 -#define JUCE_BUILDNUMBER 56 +#define JUCE_BUILDNUMBER 57 /** Current Juce version number. @@ -31087,10 +31087,27 @@ public: int channelNum, float verticalZoomFactor); - /** Returns true if the low res preview is fully generated. - */ + /** Returns true if the low res preview is fully generated. */ bool isFullyLoaded() const throw(); + /** The binary data format that is stored in the thumbnail file. */ + struct DataFormat + { + char thumbnailMagic[4]; /**< Should be 'jatm'. */ + int32 samplesPerThumbSample; /**< Number of source samples per thumbnail sample. */ + int64 totalSamples; /**< Total number of source samples. */ + int64 numFinishedSamples; /**< Number of valid source samples that have been read into the thumbnail. */ + int32 numThumbnailSamples; /**< Number of samples in the thumbnail data. */ + int32 numChannels; /**< Number of audio channels. */ + int32 sampleRate; /**< Source sample rate. */ + char future[16]; /**< Reserved for future use. */ + char data[1]; /**< The raw thumbnail samples, two signed bytes per + sample (min and max values). Channels are interleaved. */ + + /** Flips the endianness of the values in this data structure if the CPU is big-endian. */ + void flipEndiannessIfBigEndian() throw(); + }; + /** @internal */ bool useTimeSlice(); /** @internal */ @@ -31102,24 +31119,23 @@ private: AudioFormatManager& formatManagerToUse; AudioThumbnailCache& cache; ScopedPointer source; - CriticalSection readerLock; ScopedPointer reader; - MemoryBlock data, cachedLevels; int orginalSamplesPerThumbnailSample; - int numChannelsCached, numSamplesCached; double cachedStart, cachedTimePerPixel; bool cacheNeedsRefilling; + const int timeBeforeDeletingReader; + + friend class AudioThumbnailCache; void clear(); AudioFormatReader* createReader() const; void generateSection (AudioFormatReader& reader, int64 startSample, int numSamples); char* getChannelData (int channel) const; void refillCache (int numSamples, double startTime, double timePerPixel); - - friend class AudioThumbnailCache; + DataFormat* getData() const throw(); // true if it needs more callbacks from the readNextBlockFromAudioFile() method bool initialiseFromAudioFile (AudioFormatReader& reader); diff --git a/src/audio/audio_file_formats/juce_AudioThumbnail.cpp b/src/audio/audio_file_formats/juce_AudioThumbnail.cpp index b2b6697a24..618e727790 100644 --- a/src/audio/audio_file_formats/juce_AudioThumbnail.cpp +++ b/src/audio/audio_file_formats/juce_AudioThumbnail.cpp @@ -30,40 +30,6 @@ BEGIN_JUCE_NAMESPACE #include "juce_AudioThumbnail.h" #include "juce_AudioThumbnailCache.h" -const int timeBeforeDeletingReader = 2000; - - -//============================================================================== -struct AudioThumbnailDataFormat -{ - char thumbnailMagic[4]; - int samplesPerThumbSample; - int64 totalSamples; // source samples - int64 numFinishedSamples; // source samples - int numThumbnailSamples; - int numChannels; - int sampleRate; - char future[16]; - char data[1]; - - void swapEndiannessIfNeeded() throw() - { - #if JUCE_BIG_ENDIAN - flip (samplesPerThumbSample); - flip (totalSamples); - flip (numFinishedSamples); - flip (numThumbnailSamples); - flip (numChannels); - flip (sampleRate); - #endif - } - -private: - #if JUCE_BIG_ENDIAN - static void flip (int& n) { n = (int) ByteOrder::swap ((uint32) n); } - static void flip (int64& n) { n = (int64) ByteOrder::swap ((uint64) n); } - #endif -}; //============================================================================== AudioThumbnail::AudioThumbnail (const int orginalSamplesPerThumbnailSample_, @@ -71,7 +37,8 @@ AudioThumbnail::AudioThumbnail (const int orginalSamplesPerThumbnailSample_, AudioThumbnailCache& cacheToUse) : formatManagerToUse (formatManagerToUse_), cache (cacheToUse), - orginalSamplesPerThumbnailSample (orginalSamplesPerThumbnailSample_) + orginalSamplesPerThumbnailSample (orginalSamplesPerThumbnailSample_), + timeBeforeDeletingReader (2000) { clear(); } @@ -84,6 +51,12 @@ AudioThumbnail::~AudioThumbnail() reader = 0; } +AudioThumbnail::DataFormat* AudioThumbnail::getData() const throw() +{ + jassert (data.getData() != 0); + return static_cast (data.getData()); +} + void AudioThumbnail::setSource (InputSource* const newSource) { cache.removeThumbnail (this); @@ -168,9 +141,9 @@ void AudioThumbnail::timerCallback() void AudioThumbnail::clear() { - data.setSize (sizeof (AudioThumbnailDataFormat) + 3); + data.setSize (sizeof (DataFormat) + 3); - AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); + DataFormat* const d = getData(); d->thumbnailMagic[0] = 'j'; d->thumbnailMagic[1] = 'a'; @@ -195,8 +168,8 @@ void AudioThumbnail::loadFrom (InputStream& input) data.setSize (0); input.readIntoMemoryBlock (data); - AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); - d->swapEndiannessIfNeeded(); + DataFormat* const d = getData(); + d->flipEndiannessIfBigEndian(); if (! (d->thumbnailMagic[0] == 'j' && d->thumbnailMagic[1] == 'a' @@ -212,15 +185,17 @@ void AudioThumbnail::loadFrom (InputStream& input) void AudioThumbnail::saveTo (OutputStream& output) const { - AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); - d->swapEndiannessIfNeeded(); - output.write (data.getData(), (int) data.getSize()); - d->swapEndiannessIfNeeded(); + const ScopedLock sl (readerLock); + + DataFormat* const d = getData(); + d->flipEndiannessIfBigEndian(); + output.write (d, (int) data.getSize()); + d->flipEndiannessIfBigEndian(); } bool AudioThumbnail::initialiseFromAudioFile (AudioFormatReader& fileReader) { - AudioThumbnailDataFormat* d = (AudioThumbnailDataFormat*) data.getData(); + DataFormat* d = getData(); d->totalSamples = fileReader.lengthInSamples; d->numChannels = jmin ((uint32) 2, fileReader.numChannels); @@ -228,17 +203,17 @@ bool AudioThumbnail::initialiseFromAudioFile (AudioFormatReader& fileReader) d->sampleRate = roundToInt (fileReader.sampleRate); d->numThumbnailSamples = (int) (d->totalSamples / d->samplesPerThumbSample) + 1; - data.setSize (sizeof (AudioThumbnailDataFormat) + 3 + d->numThumbnailSamples * d->numChannels * 2); + data.setSize (sizeof (DataFormat) + 3 + d->numThumbnailSamples * d->numChannels * 2); - d = (AudioThumbnailDataFormat*) data.getData(); - zeromem (&(d->data[0]), d->numThumbnailSamples * d->numChannels * 2); + d = getData(); + zeromem (d->data, d->numThumbnailSamples * d->numChannels * 2); return d->totalSamples > 0; } bool AudioThumbnail::readNextBlockFromAudioFile (AudioFormatReader& fileReader) { - AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); + DataFormat* const d = getData(); if (d->numFinishedSamples < d->totalSamples) { @@ -252,24 +227,20 @@ bool AudioThumbnail::readNextBlockFromAudioFile (AudioFormatReader& fileReader) } cacheNeedsRefilling = true; - return (d->numFinishedSamples < d->totalSamples); + return d->numFinishedSamples < d->totalSamples; } int AudioThumbnail::getNumChannels() const throw() { - const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); - jassert (d != 0); - - return d->numChannels; + return getData()->numChannels; } double AudioThumbnail::getTotalLength() const throw() { - const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); - jassert (d != 0); + const DataFormat* const d = getData(); if (d->sampleRate > 0) - return d->totalSamples / (double)d->sampleRate; + return d->totalSamples / (double) d->sampleRate; else return 0.0; } @@ -278,14 +249,13 @@ void AudioThumbnail::generateSection (AudioFormatReader& fileReader, int64 startSample, int numSamples) { - AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); - jassert (d != 0); + DataFormat* const d = getData(); - int firstDataPos = (int) (startSample / d->samplesPerThumbSample); - int lastDataPos = (int) ((startSample + numSamples) / d->samplesPerThumbSample); + const int firstDataPos = (int) (startSample / d->samplesPerThumbSample); + const int lastDataPos = (int) ((startSample + numSamples) / d->samplesPerThumbSample); - char* l = getChannelData (0); - char* r = getChannelData (1); + char* const l = getChannelData (0); + char* const r = getChannelData (1); for (int i = firstDataPos; i < lastDataPos; ++i) { @@ -320,8 +290,7 @@ void AudioThumbnail::generateSection (AudioFormatReader& fileReader, char* AudioThumbnail::getChannelData (int channel) const { - AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); - jassert (d != 0); + DataFormat* const d = getData(); if (channel >= 0 && channel < d->numChannels) return d->data + (channel * 2 * d->numThumbnailSamples); @@ -331,9 +300,7 @@ char* AudioThumbnail::getChannelData (int channel) const bool AudioThumbnail::isFullyLoaded() const throw() { - const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); - jassert (d != 0); - + const DataFormat* const d = getData(); return d->numFinishedSamples >= d->totalSamples; } @@ -341,8 +308,7 @@ void AudioThumbnail::refillCache (const int numSamples, double startTime, const double timePerPixel) { - const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); - jassert (d != 0); + const DataFormat* const d = getData(); if (numSamples <= 0 || timePerPixel <= 0.0 @@ -515,16 +481,33 @@ void AudioThumbnail::drawChannel (Graphics& g, cacheData += numChannelsCached << 1; if (mn <= mx) // if the wrong way round, signifies that the sample's not yet known - g.drawLine ((float) x, jmax (midY - mx * vscale - 0.3f, topY), - (float) x, jmin (midY - mn * vscale + 0.3f, bottomY)); - - ++x; + g.drawVerticalLine (x, jmax (midY - mx * vscale - 0.3f, topY), + jmin (midY - mn * vscale + 0.3f, bottomY)); - if (x >= clip.getRight()) + if (++x >= clip.getRight()) break; } } } +//============================================================================== +void AudioThumbnail::DataFormat::flipEndiannessIfBigEndian() throw() +{ + #if JUCE_BIG_ENDIAN + struct Flipper + { + static void flip (int32& n) { n = (int32) ByteOrder::swap ((uint32) n); } + static void flip (int64& n) { n = (int64) ByteOrder::swap ((uint64) n); } + }; + + Flipper::flip (samplesPerThumbSample); + Flipper::flip (totalSamples); + Flipper::flip (numFinishedSamples); + Flipper::flip (numThumbnailSamples); + Flipper::flip (numChannels); + Flipper::flip (sampleRate); + #endif +} + END_JUCE_NAMESPACE diff --git a/src/audio/audio_file_formats/juce_AudioThumbnail.h b/src/audio/audio_file_formats/juce_AudioThumbnail.h index f536c8d29e..7a4458ecef 100644 --- a/src/audio/audio_file_formats/juce_AudioThumbnail.h +++ b/src/audio/audio_file_formats/juce_AudioThumbnail.h @@ -136,10 +136,28 @@ public: int channelNum, float verticalZoomFactor); - /** Returns true if the low res preview is fully generated. - */ + /** Returns true if the low res preview is fully generated. */ bool isFullyLoaded() const throw(); + //============================================================================== + /** The binary data format that is stored in the thumbnail file. */ + struct DataFormat + { + char thumbnailMagic[4]; /**< Should be 'jatm'. */ + int32 samplesPerThumbSample; /**< Number of source samples per thumbnail sample. */ + int64 totalSamples; /**< Total number of source samples. */ + int64 numFinishedSamples; /**< Number of valid source samples that have been read into the thumbnail. */ + int32 numThumbnailSamples; /**< Number of samples in the thumbnail data. */ + int32 numChannels; /**< Number of audio channels. */ + int32 sampleRate; /**< Source sample rate. */ + char future[16]; /**< Reserved for future use. */ + char data[1]; /**< The raw thumbnail samples, two signed bytes per + sample (min and max values). Channels are interleaved. */ + + /** Flips the endianness of the values in this data structure if the CPU is big-endian. */ + void flipEndiannessIfBigEndian() throw(); + }; + //============================================================================== /** @internal */ bool useTimeSlice(); @@ -153,24 +171,23 @@ private: AudioFormatManager& formatManagerToUse; AudioThumbnailCache& cache; ScopedPointer source; - CriticalSection readerLock; ScopedPointer reader; - MemoryBlock data, cachedLevels; int orginalSamplesPerThumbnailSample; - int numChannelsCached, numSamplesCached; double cachedStart, cachedTimePerPixel; bool cacheNeedsRefilling; + const int timeBeforeDeletingReader; + + friend class AudioThumbnailCache; void clear(); AudioFormatReader* createReader() const; void generateSection (AudioFormatReader& reader, int64 startSample, int numSamples); char* getChannelData (int channel) const; void refillCache (int numSamples, double startTime, double timePerPixel); - - friend class AudioThumbnailCache; + DataFormat* getData() const throw(); // true if it needs more callbacks from the readNextBlockFromAudioFile() method bool initialiseFromAudioFile (AudioFormatReader& reader); diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index b7cc2f66b2..13ea3553c2 100644 --- a/src/core/juce_StandardHeader.h +++ b/src/core/juce_StandardHeader.h @@ -33,7 +33,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 52 -#define JUCE_BUILDNUMBER 56 +#define JUCE_BUILDNUMBER 57 /** Current Juce version number.