| @@ -21535,45 +21535,13 @@ END_JUCE_NAMESPACE | |||||
| /*** Start of inlined file: juce_AudioThumbnail.cpp ***/ | /*** Start of inlined file: juce_AudioThumbnail.cpp ***/ | ||||
| BEGIN_JUCE_NAMESPACE | 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_, | AudioThumbnail::AudioThumbnail (const int orginalSamplesPerThumbnailSample_, | ||||
| AudioFormatManager& formatManagerToUse_, | AudioFormatManager& formatManagerToUse_, | ||||
| AudioThumbnailCache& cacheToUse) | AudioThumbnailCache& cacheToUse) | ||||
| : formatManagerToUse (formatManagerToUse_), | : formatManagerToUse (formatManagerToUse_), | ||||
| cache (cacheToUse), | cache (cacheToUse), | ||||
| orginalSamplesPerThumbnailSample (orginalSamplesPerThumbnailSample_) | |||||
| orginalSamplesPerThumbnailSample (orginalSamplesPerThumbnailSample_), | |||||
| timeBeforeDeletingReader (2000) | |||||
| { | { | ||||
| clear(); | clear(); | ||||
| } | } | ||||
| @@ -21586,6 +21554,12 @@ AudioThumbnail::~AudioThumbnail() | |||||
| reader = 0; | reader = 0; | ||||
| } | } | ||||
| AudioThumbnail::DataFormat* AudioThumbnail::getData() const throw() | |||||
| { | |||||
| jassert (data.getData() != 0); | |||||
| return static_cast <DataFormat*> (data.getData()); | |||||
| } | |||||
| void AudioThumbnail::setSource (InputSource* const newSource) | void AudioThumbnail::setSource (InputSource* const newSource) | ||||
| { | { | ||||
| cache.removeThumbnail (this); | cache.removeThumbnail (this); | ||||
| @@ -21670,9 +21644,9 @@ void AudioThumbnail::timerCallback() | |||||
| void AudioThumbnail::clear() | 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[0] = 'j'; | ||||
| d->thumbnailMagic[1] = 'a'; | d->thumbnailMagic[1] = 'a'; | ||||
| @@ -21697,8 +21671,8 @@ void AudioThumbnail::loadFrom (InputStream& input) | |||||
| data.setSize (0); | data.setSize (0); | ||||
| input.readIntoMemoryBlock (data); | input.readIntoMemoryBlock (data); | ||||
| AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); | |||||
| d->swapEndiannessIfNeeded(); | |||||
| DataFormat* const d = getData(); | |||||
| d->flipEndiannessIfBigEndian(); | |||||
| if (! (d->thumbnailMagic[0] == 'j' | if (! (d->thumbnailMagic[0] == 'j' | ||||
| && d->thumbnailMagic[1] == 'a' | && d->thumbnailMagic[1] == 'a' | ||||
| @@ -21714,15 +21688,17 @@ void AudioThumbnail::loadFrom (InputStream& input) | |||||
| void AudioThumbnail::saveTo (OutputStream& output) const | 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) | bool AudioThumbnail::initialiseFromAudioFile (AudioFormatReader& fileReader) | ||||
| { | { | ||||
| AudioThumbnailDataFormat* d = (AudioThumbnailDataFormat*) data.getData(); | |||||
| DataFormat* d = getData(); | |||||
| d->totalSamples = fileReader.lengthInSamples; | d->totalSamples = fileReader.lengthInSamples; | ||||
| d->numChannels = jmin ((uint32) 2, fileReader.numChannels); | d->numChannels = jmin ((uint32) 2, fileReader.numChannels); | ||||
| @@ -21730,17 +21706,17 @@ bool AudioThumbnail::initialiseFromAudioFile (AudioFormatReader& fileReader) | |||||
| d->sampleRate = roundToInt (fileReader.sampleRate); | d->sampleRate = roundToInt (fileReader.sampleRate); | ||||
| d->numThumbnailSamples = (int) (d->totalSamples / d->samplesPerThumbSample) + 1; | 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; | return d->totalSamples > 0; | ||||
| } | } | ||||
| bool AudioThumbnail::readNextBlockFromAudioFile (AudioFormatReader& fileReader) | bool AudioThumbnail::readNextBlockFromAudioFile (AudioFormatReader& fileReader) | ||||
| { | { | ||||
| AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); | |||||
| DataFormat* const d = getData(); | |||||
| if (d->numFinishedSamples < d->totalSamples) | if (d->numFinishedSamples < d->totalSamples) | ||||
| { | { | ||||
| @@ -21754,24 +21730,20 @@ bool AudioThumbnail::readNextBlockFromAudioFile (AudioFormatReader& fileReader) | |||||
| } | } | ||||
| cacheNeedsRefilling = true; | cacheNeedsRefilling = true; | ||||
| return (d->numFinishedSamples < d->totalSamples); | |||||
| return d->numFinishedSamples < d->totalSamples; | |||||
| } | } | ||||
| int AudioThumbnail::getNumChannels() const throw() | 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() | 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) | if (d->sampleRate > 0) | ||||
| return d->totalSamples / (double)d->sampleRate; | |||||
| return d->totalSamples / (double) d->sampleRate; | |||||
| else | else | ||||
| return 0.0; | return 0.0; | ||||
| } | } | ||||
| @@ -21780,14 +21752,13 @@ void AudioThumbnail::generateSection (AudioFormatReader& fileReader, | |||||
| int64 startSample, | int64 startSample, | ||||
| int numSamples) | 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) | for (int i = firstDataPos; i < lastDataPos; ++i) | ||||
| { | { | ||||
| @@ -21822,8 +21793,7 @@ void AudioThumbnail::generateSection (AudioFormatReader& fileReader, | |||||
| char* AudioThumbnail::getChannelData (int channel) const | 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) | if (channel >= 0 && channel < d->numChannels) | ||||
| return d->data + (channel * 2 * d->numThumbnailSamples); | return d->data + (channel * 2 * d->numThumbnailSamples); | ||||
| @@ -21833,9 +21803,7 @@ char* AudioThumbnail::getChannelData (int channel) const | |||||
| bool AudioThumbnail::isFullyLoaded() const throw() | 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; | return d->numFinishedSamples >= d->totalSamples; | ||||
| } | } | ||||
| @@ -21843,8 +21811,7 @@ void AudioThumbnail::refillCache (const int numSamples, | |||||
| double startTime, | double startTime, | ||||
| const double timePerPixel) | const double timePerPixel) | ||||
| { | { | ||||
| const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); | |||||
| jassert (d != 0); | |||||
| const DataFormat* const d = getData(); | |||||
| if (numSamples <= 0 | if (numSamples <= 0 | ||||
| || timePerPixel <= 0.0 | || timePerPixel <= 0.0 | ||||
| @@ -22017,17 +21984,33 @@ void AudioThumbnail::drawChannel (Graphics& g, | |||||
| cacheData += numChannelsCached << 1; | cacheData += numChannelsCached << 1; | ||||
| if (mn <= mx) // if the wrong way round, signifies that the sample's not yet known | 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; | 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_JUCE_NAMESPACE | ||||
| /*** End of inlined file: juce_AudioThumbnail.cpp ***/ | /*** End of inlined file: juce_AudioThumbnail.cpp ***/ | ||||
| @@ -64,7 +64,7 @@ | |||||
| */ | */ | ||||
| #define JUCE_MAJOR_VERSION 1 | #define JUCE_MAJOR_VERSION 1 | ||||
| #define JUCE_MINOR_VERSION 52 | #define JUCE_MINOR_VERSION 52 | ||||
| #define JUCE_BUILDNUMBER 56 | |||||
| #define JUCE_BUILDNUMBER 57 | |||||
| /** Current Juce version number. | /** Current Juce version number. | ||||
| @@ -31087,10 +31087,27 @@ public: | |||||
| int channelNum, | int channelNum, | ||||
| float verticalZoomFactor); | 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(); | 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 */ | /** @internal */ | ||||
| bool useTimeSlice(); | bool useTimeSlice(); | ||||
| /** @internal */ | /** @internal */ | ||||
| @@ -31102,24 +31119,23 @@ private: | |||||
| AudioFormatManager& formatManagerToUse; | AudioFormatManager& formatManagerToUse; | ||||
| AudioThumbnailCache& cache; | AudioThumbnailCache& cache; | ||||
| ScopedPointer <InputSource> source; | ScopedPointer <InputSource> source; | ||||
| CriticalSection readerLock; | CriticalSection readerLock; | ||||
| ScopedPointer <AudioFormatReader> reader; | ScopedPointer <AudioFormatReader> reader; | ||||
| MemoryBlock data, cachedLevels; | MemoryBlock data, cachedLevels; | ||||
| int orginalSamplesPerThumbnailSample; | int orginalSamplesPerThumbnailSample; | ||||
| int numChannelsCached, numSamplesCached; | int numChannelsCached, numSamplesCached; | ||||
| double cachedStart, cachedTimePerPixel; | double cachedStart, cachedTimePerPixel; | ||||
| bool cacheNeedsRefilling; | bool cacheNeedsRefilling; | ||||
| const int timeBeforeDeletingReader; | |||||
| friend class AudioThumbnailCache; | |||||
| void clear(); | void clear(); | ||||
| AudioFormatReader* createReader() const; | AudioFormatReader* createReader() const; | ||||
| void generateSection (AudioFormatReader& reader, int64 startSample, int numSamples); | void generateSection (AudioFormatReader& reader, int64 startSample, int numSamples); | ||||
| char* getChannelData (int channel) const; | char* getChannelData (int channel) const; | ||||
| void refillCache (int numSamples, double startTime, double timePerPixel); | 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 | // true if it needs more callbacks from the readNextBlockFromAudioFile() method | ||||
| bool initialiseFromAudioFile (AudioFormatReader& reader); | bool initialiseFromAudioFile (AudioFormatReader& reader); | ||||
| @@ -30,40 +30,6 @@ BEGIN_JUCE_NAMESPACE | |||||
| #include "juce_AudioThumbnail.h" | #include "juce_AudioThumbnail.h" | ||||
| #include "juce_AudioThumbnailCache.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_, | AudioThumbnail::AudioThumbnail (const int orginalSamplesPerThumbnailSample_, | ||||
| @@ -71,7 +37,8 @@ AudioThumbnail::AudioThumbnail (const int orginalSamplesPerThumbnailSample_, | |||||
| AudioThumbnailCache& cacheToUse) | AudioThumbnailCache& cacheToUse) | ||||
| : formatManagerToUse (formatManagerToUse_), | : formatManagerToUse (formatManagerToUse_), | ||||
| cache (cacheToUse), | cache (cacheToUse), | ||||
| orginalSamplesPerThumbnailSample (orginalSamplesPerThumbnailSample_) | |||||
| orginalSamplesPerThumbnailSample (orginalSamplesPerThumbnailSample_), | |||||
| timeBeforeDeletingReader (2000) | |||||
| { | { | ||||
| clear(); | clear(); | ||||
| } | } | ||||
| @@ -84,6 +51,12 @@ AudioThumbnail::~AudioThumbnail() | |||||
| reader = 0; | reader = 0; | ||||
| } | } | ||||
| AudioThumbnail::DataFormat* AudioThumbnail::getData() const throw() | |||||
| { | |||||
| jassert (data.getData() != 0); | |||||
| return static_cast <DataFormat*> (data.getData()); | |||||
| } | |||||
| void AudioThumbnail::setSource (InputSource* const newSource) | void AudioThumbnail::setSource (InputSource* const newSource) | ||||
| { | { | ||||
| cache.removeThumbnail (this); | cache.removeThumbnail (this); | ||||
| @@ -168,9 +141,9 @@ void AudioThumbnail::timerCallback() | |||||
| void AudioThumbnail::clear() | 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[0] = 'j'; | ||||
| d->thumbnailMagic[1] = 'a'; | d->thumbnailMagic[1] = 'a'; | ||||
| @@ -195,8 +168,8 @@ void AudioThumbnail::loadFrom (InputStream& input) | |||||
| data.setSize (0); | data.setSize (0); | ||||
| input.readIntoMemoryBlock (data); | input.readIntoMemoryBlock (data); | ||||
| AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); | |||||
| d->swapEndiannessIfNeeded(); | |||||
| DataFormat* const d = getData(); | |||||
| d->flipEndiannessIfBigEndian(); | |||||
| if (! (d->thumbnailMagic[0] == 'j' | if (! (d->thumbnailMagic[0] == 'j' | ||||
| && d->thumbnailMagic[1] == 'a' | && d->thumbnailMagic[1] == 'a' | ||||
| @@ -212,15 +185,17 @@ void AudioThumbnail::loadFrom (InputStream& input) | |||||
| void AudioThumbnail::saveTo (OutputStream& output) const | 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) | bool AudioThumbnail::initialiseFromAudioFile (AudioFormatReader& fileReader) | ||||
| { | { | ||||
| AudioThumbnailDataFormat* d = (AudioThumbnailDataFormat*) data.getData(); | |||||
| DataFormat* d = getData(); | |||||
| d->totalSamples = fileReader.lengthInSamples; | d->totalSamples = fileReader.lengthInSamples; | ||||
| d->numChannels = jmin ((uint32) 2, fileReader.numChannels); | d->numChannels = jmin ((uint32) 2, fileReader.numChannels); | ||||
| @@ -228,17 +203,17 @@ bool AudioThumbnail::initialiseFromAudioFile (AudioFormatReader& fileReader) | |||||
| d->sampleRate = roundToInt (fileReader.sampleRate); | d->sampleRate = roundToInt (fileReader.sampleRate); | ||||
| d->numThumbnailSamples = (int) (d->totalSamples / d->samplesPerThumbSample) + 1; | 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; | return d->totalSamples > 0; | ||||
| } | } | ||||
| bool AudioThumbnail::readNextBlockFromAudioFile (AudioFormatReader& fileReader) | bool AudioThumbnail::readNextBlockFromAudioFile (AudioFormatReader& fileReader) | ||||
| { | { | ||||
| AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); | |||||
| DataFormat* const d = getData(); | |||||
| if (d->numFinishedSamples < d->totalSamples) | if (d->numFinishedSamples < d->totalSamples) | ||||
| { | { | ||||
| @@ -252,24 +227,20 @@ bool AudioThumbnail::readNextBlockFromAudioFile (AudioFormatReader& fileReader) | |||||
| } | } | ||||
| cacheNeedsRefilling = true; | cacheNeedsRefilling = true; | ||||
| return (d->numFinishedSamples < d->totalSamples); | |||||
| return d->numFinishedSamples < d->totalSamples; | |||||
| } | } | ||||
| int AudioThumbnail::getNumChannels() const throw() | 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() | 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) | if (d->sampleRate > 0) | ||||
| return d->totalSamples / (double)d->sampleRate; | |||||
| return d->totalSamples / (double) d->sampleRate; | |||||
| else | else | ||||
| return 0.0; | return 0.0; | ||||
| } | } | ||||
| @@ -278,14 +249,13 @@ void AudioThumbnail::generateSection (AudioFormatReader& fileReader, | |||||
| int64 startSample, | int64 startSample, | ||||
| int numSamples) | 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) | for (int i = firstDataPos; i < lastDataPos; ++i) | ||||
| { | { | ||||
| @@ -320,8 +290,7 @@ void AudioThumbnail::generateSection (AudioFormatReader& fileReader, | |||||
| char* AudioThumbnail::getChannelData (int channel) const | 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) | if (channel >= 0 && channel < d->numChannels) | ||||
| return d->data + (channel * 2 * d->numThumbnailSamples); | return d->data + (channel * 2 * d->numThumbnailSamples); | ||||
| @@ -331,9 +300,7 @@ char* AudioThumbnail::getChannelData (int channel) const | |||||
| bool AudioThumbnail::isFullyLoaded() const throw() | 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; | return d->numFinishedSamples >= d->totalSamples; | ||||
| } | } | ||||
| @@ -341,8 +308,7 @@ void AudioThumbnail::refillCache (const int numSamples, | |||||
| double startTime, | double startTime, | ||||
| const double timePerPixel) | const double timePerPixel) | ||||
| { | { | ||||
| const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); | |||||
| jassert (d != 0); | |||||
| const DataFormat* const d = getData(); | |||||
| if (numSamples <= 0 | if (numSamples <= 0 | ||||
| || timePerPixel <= 0.0 | || timePerPixel <= 0.0 | ||||
| @@ -515,16 +481,33 @@ void AudioThumbnail::drawChannel (Graphics& g, | |||||
| cacheData += numChannelsCached << 1; | cacheData += numChannelsCached << 1; | ||||
| if (mn <= mx) // if the wrong way round, signifies that the sample's not yet known | 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; | 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_JUCE_NAMESPACE | ||||
| @@ -136,10 +136,28 @@ public: | |||||
| int channelNum, | int channelNum, | ||||
| float verticalZoomFactor); | 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(); | 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 */ | /** @internal */ | ||||
| bool useTimeSlice(); | bool useTimeSlice(); | ||||
| @@ -153,24 +171,23 @@ private: | |||||
| AudioFormatManager& formatManagerToUse; | AudioFormatManager& formatManagerToUse; | ||||
| AudioThumbnailCache& cache; | AudioThumbnailCache& cache; | ||||
| ScopedPointer <InputSource> source; | ScopedPointer <InputSource> source; | ||||
| CriticalSection readerLock; | CriticalSection readerLock; | ||||
| ScopedPointer <AudioFormatReader> reader; | ScopedPointer <AudioFormatReader> reader; | ||||
| MemoryBlock data, cachedLevels; | MemoryBlock data, cachedLevels; | ||||
| int orginalSamplesPerThumbnailSample; | int orginalSamplesPerThumbnailSample; | ||||
| int numChannelsCached, numSamplesCached; | int numChannelsCached, numSamplesCached; | ||||
| double cachedStart, cachedTimePerPixel; | double cachedStart, cachedTimePerPixel; | ||||
| bool cacheNeedsRefilling; | bool cacheNeedsRefilling; | ||||
| const int timeBeforeDeletingReader; | |||||
| friend class AudioThumbnailCache; | |||||
| void clear(); | void clear(); | ||||
| AudioFormatReader* createReader() const; | AudioFormatReader* createReader() const; | ||||
| void generateSection (AudioFormatReader& reader, int64 startSample, int numSamples); | void generateSection (AudioFormatReader& reader, int64 startSample, int numSamples); | ||||
| char* getChannelData (int channel) const; | char* getChannelData (int channel) const; | ||||
| void refillCache (int numSamples, double startTime, double timePerPixel); | 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 | // true if it needs more callbacks from the readNextBlockFromAudioFile() method | ||||
| bool initialiseFromAudioFile (AudioFormatReader& reader); | bool initialiseFromAudioFile (AudioFormatReader& reader); | ||||
| @@ -33,7 +33,7 @@ | |||||
| */ | */ | ||||
| #define JUCE_MAJOR_VERSION 1 | #define JUCE_MAJOR_VERSION 1 | ||||
| #define JUCE_MINOR_VERSION 52 | #define JUCE_MINOR_VERSION 52 | ||||
| #define JUCE_BUILDNUMBER 56 | |||||
| #define JUCE_BUILDNUMBER 57 | |||||
| /** Current Juce version number. | /** Current Juce version number. | ||||