From 8ef30afad2c9b0b85126cf739544b4e875baa77d Mon Sep 17 00:00:00 2001 From: jules Date: Fri, 14 Oct 2011 19:34:52 +0100 Subject: [PATCH] Added storage methods to AudioThumbnailCache. --- .../codecs/juce_WavAudioFormat.cpp | 18 ++- .../gui/juce_AudioThumbnailCache.cpp | 103 ++++++++++++++---- .../gui/juce_AudioThumbnailCache.h | 18 ++- .../juce_LowLevelGraphicsSoftwareRenderer.cpp | 20 ++-- 4 files changed, 113 insertions(+), 46 deletions(-) diff --git a/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp b/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp index ae70f36637..e8e2bb4b95 100644 --- a/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp +++ b/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp @@ -453,7 +453,6 @@ namespace WavFileHelpers class WavAudioFormatReader : public AudioFormatReader { public: - //============================================================================== WavAudioFormatReader (InputStream* const in) : AudioFormatReader (in, TRANS (wavFormatName)), bwavChunkStart (0), @@ -650,14 +649,14 @@ public: MemoryBlock textBlock; input->readIntoMemoryBlock (textBlock, (int) stringLength); - metadataValues.set (prefix + "Identifier", String (identifier)); - metadataValues.set (prefix + "SampleLength", String (sampleLength)); - metadataValues.set (prefix + "Purpose", String (purpose)); - metadataValues.set (prefix + "Country", String (country)); - metadataValues.set (prefix + "Language", String (language)); - metadataValues.set (prefix + "Dialect", String (dialect)); - metadataValues.set (prefix + "CodePage", String (codePage)); - metadataValues.set (prefix + "Text", textBlock.toString()); + metadataValues.set (prefix + "Identifier", String (identifier)); + metadataValues.set (prefix + "SampleLength", String (sampleLength)); + metadataValues.set (prefix + "Purpose", String (purpose)); + metadataValues.set (prefix + "Country", String (country)); + metadataValues.set (prefix + "Language", String (language)); + metadataValues.set (prefix + "Dialect", String (dialect)); + metadataValues.set (prefix + "CodePage", String (codePage)); + metadataValues.set (prefix + "Text", textBlock.toString()); } input->setPosition (adtlChunkEnd); @@ -746,7 +745,6 @@ private: class WavAudioFormatWriter : public AudioFormatWriter { public: - //============================================================================== WavAudioFormatWriter (OutputStream* const out, const double sampleRate_, const unsigned int numChannels_, const unsigned int bits, const StringPairArray& metadataValues) diff --git a/modules/juce_audio_utils/gui/juce_AudioThumbnailCache.cpp b/modules/juce_audio_utils/gui/juce_AudioThumbnailCache.cpp index 5b8e3f3346..ff3889ce64 100644 --- a/modules/juce_audio_utils/gui/juce_AudioThumbnailCache.cpp +++ b/modules/juce_audio_utils/gui/juce_AudioThumbnailCache.cpp @@ -26,12 +26,35 @@ BEGIN_JUCE_NAMESPACE //============================================================================== -struct ThumbnailCacheEntry +class AudioThumbnailCache::ThumbnailCacheEntry { +public: + ThumbnailCacheEntry (const int64 hash_) + : hash (hash_), + lastUsed (Time::getMillisecondCounter()) + { + } + + ThumbnailCacheEntry (InputStream& in) + : lastUsed (0) + { + hash = in.readInt64(); + const int64 len = in.readInt64(); + in.readIntoMemoryBlock (data, len); + } + + void write (OutputStream& out) + { + out.writeInt64 (hash); + out.writeInt64 ((int64) data.getSize()); + out << data; + } + int64 hash; uint32 lastUsed; MemoryBlock data; +private: JUCE_LEAK_DETECTOR (ThumbnailCacheEntry); }; @@ -40,6 +63,7 @@ AudioThumbnailCache::AudioThumbnailCache (const int maxNumThumbsToStore_) : TimeSliceThread ("thumb cache"), maxNumThumbsToStore (maxNumThumbsToStore_) { + jassert (maxNumThumbsToStore > 0); startThread (2); } @@ -47,7 +71,7 @@ AudioThumbnailCache::~AudioThumbnailCache() { } -ThumbnailCacheEntry* AudioThumbnailCache::findThumbFor (const int64 hash) const +AudioThumbnailCache::ThumbnailCacheEntry* AudioThumbnailCache::findThumbFor (const int64 hash) const { for (int i = thumbs.size(); --i >= 0;) if (thumbs.getUnchecked(i)->hash == hash) @@ -56,8 +80,28 @@ ThumbnailCacheEntry* AudioThumbnailCache::findThumbFor (const int64 hash) const return nullptr; } +int AudioThumbnailCache::findOldestThumb() const +{ + int oldest = 0; + uint32 oldestTime = Time::getMillisecondCounter() + 1; + + for (int i = thumbs.size(); --i >= 0;) + { + const ThumbnailCacheEntry* const te = thumbs.getUnchecked(i); + + if (te->lastUsed < oldestTime) + { + oldest = i; + oldestTime = te->lastUsed; + } + } + + return oldest; +} + bool AudioThumbnailCache::loadThumb (AudioThumbnail& thumb, const int64 hashCode) { + const ScopedLock sl (lock); ThumbnailCacheEntry* te = findThumbFor (hashCode); if (te != nullptr) @@ -75,45 +119,58 @@ bool AudioThumbnailCache::loadThumb (AudioThumbnail& thumb, const int64 hashCode void AudioThumbnailCache::storeThumb (const AudioThumbnail& thumb, const int64 hashCode) { + const ScopedLock sl (lock); ThumbnailCacheEntry* te = findThumbFor (hashCode); if (te == nullptr) { - te = new ThumbnailCacheEntry(); - te->hash = hashCode; + te = new ThumbnailCacheEntry (hashCode); if (thumbs.size() < maxNumThumbsToStore) - { thumbs.add (te); - } else - { - int oldest = 0; - uint32 oldestTime = Time::getMillisecondCounter() + 1; - - for (int i = thumbs.size(); --i >= 0;) - { - if (thumbs.getUnchecked(i)->lastUsed < oldestTime) - { - oldest = i; - oldestTime = thumbs.getUnchecked(i)->lastUsed; - } - } - - thumbs.set (oldest, te); - } + thumbs.set (findOldestThumb(), te); } - te->lastUsed = Time::getMillisecondCounter(); - MemoryOutputStream out (te->data, false); thumb.saveTo (out); } void AudioThumbnailCache::clear() { + const ScopedLock sl (lock); thumbs.clear(); } +static inline int getThumbnailCacheFileMagicHeader() noexcept +{ + return (int) ByteOrder::littleEndianInt ("ThmC"); +} + +bool AudioThumbnailCache::readFromStream (InputStream& source) +{ + if (source.readInt() != getThumbnailCacheFileMagicHeader()) + return false; + + const ScopedLock sl (lock); + clear(); + int numThumbnails = jmin (maxNumThumbsToStore, source.readInt()); + + while (--numThumbnails >= 0 && ! source.isExhausted()) + thumbs.add (new ThumbnailCacheEntry (source)); + + return true; +} + +void AudioThumbnailCache::writeToStream (OutputStream& out) +{ + const ScopedLock sl (lock); + + out.writeInt (getThumbnailCacheFileMagicHeader()); + out.writeInt (thumbs.size()); + + for (int i = 0; i < thumbs.size(); ++i) + thumbs.getUnchecked(i)->write (out); +} END_JUCE_NAMESPACE diff --git a/modules/juce_audio_utils/gui/juce_AudioThumbnailCache.h b/modules/juce_audio_utils/gui/juce_AudioThumbnailCache.h index 4d00a1cb75..e44faf2685 100644 --- a/modules/juce_audio_utils/gui/juce_AudioThumbnailCache.h +++ b/modules/juce_audio_utils/gui/juce_AudioThumbnailCache.h @@ -27,7 +27,6 @@ #define __JUCE_AUDIOTHUMBNAILCACHE_JUCEHEADER__ #include "juce_AudioThumbnail.h" -struct ThumbnailCacheEntry; //============================================================================== @@ -74,13 +73,28 @@ public: */ void storeThumb (const AudioThumbnail& thumb, int64 hashCode); + //============================================================================== + /** Attempts to re-load a saved cache of thumbnails from a stream. + The cache data must have been written by the writeToStream() method. + This will replace all currently-loaded thumbnails with the new data. + */ + bool readFromStream (InputStream& source); + + /** Writes all currently-loaded cache data to a stream. + The resulting data can be re-loaded with readFromStream(). + */ + void writeToStream (OutputStream& stream); private: //============================================================================== - OwnedArray thumbs; + class ThumbnailCacheEntry; + friend class OwnedArray; + OwnedArray thumbs; + CriticalSection lock; int maxNumThumbsToStore; ThumbnailCacheEntry* findThumbFor (int64 hash) const; + int findOldestThumb() const; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioThumbnailCache); }; diff --git a/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp b/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp index 832aa0bcac..541113c904 100644 --- a/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp +++ b/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp @@ -1929,21 +1929,20 @@ public: if (clip != nullptr) { SoftwareRendererClasses::ClipRegion_EdgeTable* edgeTableClip = new SoftwareRendererClasses::ClipRegion_EdgeTable (edgeTable); - SoftwareRendererClasses::ClipRegionBase::Ptr shapeToFill (edgeTableClip); - edgeTableClip->edgeTable.translate (x + transform.xOffset, y + transform.yOffset); - fillShape (shapeToFill, false); + edgeTableClip->edgeTable.translate (x + transform.xOffset, + y + transform.yOffset); + fillShape (edgeTableClip, false); } } void drawGlyph (const Font& f, int glyphNumber, const AffineTransform& t) { - const ScopedPointer et (f.getTypeface()->getEdgeTableForGlyph (glyphNumber, transform.getTransformWith (t))); - - if (et != nullptr && clip != nullptr) + if (clip != nullptr) { - SoftwareRendererClasses::ClipRegion_EdgeTable* edgeTableClip = new SoftwareRendererClasses::ClipRegion_EdgeTable (*et); - SoftwareRendererClasses::ClipRegionBase::Ptr shapeToFill (edgeTableClip); - fillShape (shapeToFill, false); + const ScopedPointer et (f.getTypeface()->getEdgeTableForGlyph (glyphNumber, transform.getTransformWith (t))); + + if (et != nullptr) + fillShape (new SoftwareRendererClasses::ClipRegion_EdgeTable (*et), false); } } @@ -2021,8 +2020,7 @@ public: if (! area.isEmpty()) { - SoftwareRendererClasses::ClipRegionBase::Ptr c (new SoftwareRendererClasses::ClipRegion_EdgeTable (area)); - c = clip->applyClipTo (c); + SoftwareRendererClasses::ClipRegionBase::Ptr c (clip->applyClipTo (new SoftwareRendererClasses::ClipRegion_EdgeTable (area))); if (c != nullptr) c->renderImageUntransformed (destData, srcData, alpha, tx, ty, false);