| 
							- /*
 -   ==============================================================================
 - 
 -    This file is part of the JUCE 6 technical preview.
 -    Copyright (c) 2020 - Raw Material Software Limited
 - 
 -    You may use this code under the terms of the GPL v3
 -    (see www.gnu.org/licenses).
 - 
 -    For this technical preview, this file is not subject to commercial licensing.
 - 
 -    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 -    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 -    DISCLAIMED.
 - 
 -   ==============================================================================
 - */
 - 
 - namespace juce
 - {
 - 
 - struct AudioThumbnail::MinMaxValue
 - {
 -     MinMaxValue() noexcept
 -     {
 -         values[0] = 0;
 -         values[1] = 0;
 -     }
 - 
 -     inline void set (const int8 newMin, const int8 newMax) noexcept
 -     {
 -         values[0] = newMin;
 -         values[1] = newMax;
 -     }
 - 
 -     inline int8 getMinValue() const noexcept        { return values[0]; }
 -     inline int8 getMaxValue() const noexcept        { return values[1]; }
 - 
 -     inline void setFloat (Range<float> newRange) noexcept
 -     {
 -         // Workaround for an ndk armeabi compiler bug which crashes on signed saturation
 -        #if JUCE_ANDROID
 -         Range<float> limitedRange (jlimit (-1.0f, 1.0f, newRange.getStart()),
 -                                    jlimit (-1.0f, 1.0f, newRange.getEnd()));
 -         values[0] = (int8) (limitedRange.getStart() * 127.0f);
 -         values[1] = (int8) (limitedRange.getEnd()   * 127.0f);
 -        #else
 -         values[0] = (int8) jlimit (-128, 127, roundToInt (newRange.getStart() * 127.0f));
 -         values[1] = (int8) jlimit (-128, 127, roundToInt (newRange.getEnd()   * 127.0f));
 -        #endif
 - 
 -         if (values[0] == values[1])
 -         {
 -             if (values[1] == 127)
 -                 values[0]--;
 -             else
 -                 values[1]++;
 -         }
 -     }
 - 
 -     inline bool isNonZero() const noexcept
 -     {
 -         return values[1] > values[0];
 -     }
 - 
 -     inline int getPeak() const noexcept
 -     {
 -         return jmax (std::abs ((int) values[0]),
 -                      std::abs ((int) values[1]));
 -     }
 - 
 -     inline void read (InputStream& input)      { input.read (values, 2); }
 -     inline void write (OutputStream& output)   { output.write (values, 2); }
 - 
 - private:
 -     int8 values[2];
 - };
 - 
 - //==============================================================================
 - class AudioThumbnail::LevelDataSource   : public TimeSliceClient
 - {
 - public:
 -     LevelDataSource (AudioThumbnail& thumb, AudioFormatReader* newReader, int64 hash)
 -         : hashCode (hash), owner (thumb), reader (newReader)
 -     {
 -     }
 - 
 -     LevelDataSource (AudioThumbnail& thumb, InputSource* src)
 -         : hashCode (src->hashCode()), owner (thumb), source (src)
 -     {
 -     }
 - 
 -     ~LevelDataSource() override
 -     {
 -         owner.cache.getTimeSliceThread().removeTimeSliceClient (this);
 -     }
 - 
 -     enum { timeBeforeDeletingReader = 3000 };
 - 
 -     void initialise (int64 samplesFinished)
 -     {
 -         const ScopedLock sl (readerLock);
 - 
 -         numSamplesFinished = samplesFinished;
 - 
 -         createReader();
 - 
 -         if (reader != nullptr)
 -         {
 -             lengthInSamples = reader->lengthInSamples;
 -             numChannels = reader->numChannels;
 -             sampleRate = reader->sampleRate;
 - 
 -             if (lengthInSamples <= 0 || isFullyLoaded())
 -                 reader.reset();
 -             else
 -                 owner.cache.getTimeSliceThread().addTimeSliceClient (this);
 -         }
 -     }
 - 
 -     void getLevels (int64 startSample, int numSamples, Array<Range<float>>& levels)
 -     {
 -         const ScopedLock sl (readerLock);
 - 
 -         if (reader == nullptr)
 -         {
 -             createReader();
 - 
 -             if (reader != nullptr)
 -             {
 -                 lastReaderUseTime = Time::getMillisecondCounter();
 -                 owner.cache.getTimeSliceThread().addTimeSliceClient (this);
 -             }
 -         }
 - 
 -         if (reader != nullptr)
 -         {
 -             if (levels.size() < (int) reader->numChannels)
 -                 levels.insertMultiple (0, {}, (int) reader->numChannels - levels.size());
 - 
 -             reader->readMaxLevels (startSample, numSamples, levels.getRawDataPointer(), (int) reader->numChannels);
 - 
 -             lastReaderUseTime = Time::getMillisecondCounter();
 -         }
 -     }
 - 
 -     void releaseResources()
 -     {
 -         const ScopedLock sl (readerLock);
 -         reader.reset();
 -     }
 - 
 -     int useTimeSlice() override
 -     {
 -         if (isFullyLoaded())
 -         {
 -             if (reader != nullptr && source != nullptr)
 -             {
 -                 if (Time::getMillisecondCounter() > lastReaderUseTime + timeBeforeDeletingReader)
 -                     releaseResources();
 -                 else
 -                     return 200;
 -             }
 - 
 -             return -1;
 -         }
 - 
 -         bool justFinished = false;
 - 
 -         {
 -             const ScopedLock sl (readerLock);
 -             createReader();
 - 
 -             if (reader != nullptr)
 -             {
 -                 if (! readNextBlock())
 -                     return 0;
 - 
 -                 justFinished = true;
 -             }
 -         }
 - 
 -         if (justFinished)
 -             owner.cache.storeThumb (owner, hashCode);
 - 
 -         return 200;
 -     }
 - 
 -     bool isFullyLoaded() const noexcept
 -     {
 -         return numSamplesFinished >= lengthInSamples;
 -     }
 - 
 -     inline int sampleToThumbSample (const int64 originalSample) const noexcept
 -     {
 -         return (int) (originalSample / owner.samplesPerThumbSample);
 -     }
 - 
 -     int64 lengthInSamples = 0, numSamplesFinished = 0;
 -     double sampleRate = 0;
 -     unsigned int numChannels = 0;
 -     int64 hashCode = 0;
 - 
 - private:
 -     AudioThumbnail& owner;
 -     std::unique_ptr<InputSource> source;
 -     std::unique_ptr<AudioFormatReader> reader;
 -     CriticalSection readerLock;
 -     std::atomic<uint32> lastReaderUseTime { 0 };
 - 
 -     void createReader()
 -     {
 -         if (reader == nullptr && source != nullptr)
 -             if (auto* audioFileStream = source->createInputStream())
 -                 reader.reset (owner.formatManagerToUse.createReaderFor (std::unique_ptr<InputStream> (audioFileStream)));
 -     }
 - 
 -     bool readNextBlock()
 -     {
 -         jassert (reader != nullptr);
 - 
 -         if (! isFullyLoaded())
 -         {
 -             auto numToDo = (int) jmin (256 * (int64) owner.samplesPerThumbSample, lengthInSamples - numSamplesFinished);
 - 
 -             if (numToDo > 0)
 -             {
 -                 auto startSample = numSamplesFinished;
 - 
 -                 auto firstThumbIndex = sampleToThumbSample (startSample);
 -                 auto lastThumbIndex  = sampleToThumbSample (startSample + numToDo);
 -                 auto numThumbSamps = lastThumbIndex - firstThumbIndex;
 - 
 -                 HeapBlock<MinMaxValue> levelData ((unsigned int) numThumbSamps * numChannels);
 -                 HeapBlock<MinMaxValue*> levels (numChannels);
 - 
 -                 for (int i = 0; i < (int) numChannels; ++i)
 -                     levels[i] = levelData + i * numThumbSamps;
 - 
 -                 HeapBlock<Range<float>> levelsRead (numChannels);
 - 
 -                 for (int i = 0; i < numThumbSamps; ++i)
 -                 {
 -                     reader->readMaxLevels ((firstThumbIndex + i) * owner.samplesPerThumbSample,
 -                                            owner.samplesPerThumbSample, levelsRead, (int) numChannels);
 - 
 -                     for (int j = 0; j < (int) numChannels; ++j)
 -                         levels[j][i].setFloat (levelsRead[j]);
 -                 }
 - 
 -                 {
 -                     const ScopedUnlock su (readerLock);
 -                     owner.setLevels (levels, firstThumbIndex, (int) numChannels, numThumbSamps);
 -                 }
 - 
 -                 numSamplesFinished += numToDo;
 -                 lastReaderUseTime = Time::getMillisecondCounter();
 -             }
 -         }
 - 
 -         return isFullyLoaded();
 -     }
 - };
 - 
 - //==============================================================================
 - class AudioThumbnail::ThumbData
 - {
 - public:
 -     ThumbData (int numThumbSamples)
 -     {
 -         ensureSize (numThumbSamples);
 -     }
 - 
 -     inline MinMaxValue* getData (int thumbSampleIndex) noexcept
 -     {
 -         jassert (thumbSampleIndex < data.size());
 -         return data.getRawDataPointer() + thumbSampleIndex;
 -     }
 - 
 -     int getSize() const noexcept
 -     {
 -         return data.size();
 -     }
 - 
 -     void getMinMax (int startSample, int endSample, MinMaxValue& result) const noexcept
 -     {
 -         if (startSample >= 0)
 -         {
 -             endSample = jmin (endSample, data.size() - 1);
 - 
 -             int8 mx = -128;
 -             int8 mn = 127;
 - 
 -             while (startSample <= endSample)
 -             {
 -                 auto& v = data.getReference (startSample);
 - 
 -                 if (v.getMinValue() < mn)  mn = v.getMinValue();
 -                 if (v.getMaxValue() > mx)  mx = v.getMaxValue();
 - 
 -                 ++startSample;
 -             }
 - 
 -             if (mn <= mx)
 -             {
 -                 result.set (mn, mx);
 -                 return;
 -             }
 -         }
 - 
 -         result.set (1, 0);
 -     }
 - 
 -     void write (const MinMaxValue* values, int startIndex, int numValues)
 -     {
 -         resetPeak();
 - 
 -         if (startIndex + numValues > data.size())
 -             ensureSize (startIndex + numValues);
 - 
 -         auto* dest = getData (startIndex);
 - 
 -         for (int i = 0; i < numValues; ++i)
 -             dest[i] = values[i];
 -     }
 - 
 -     void resetPeak() noexcept
 -     {
 -         peakLevel = -1;
 -     }
 - 
 -     int getPeak() noexcept
 -     {
 -         if (peakLevel < 0)
 -         {
 -             for (auto& s : data)
 -             {
 -                 auto peak = s.getPeak();
 - 
 -                 if (peak > peakLevel)
 -                     peakLevel = peak;
 -             }
 -         }
 - 
 -         return peakLevel;
 -     }
 - 
 - private:
 -     Array<MinMaxValue> data;
 -     int peakLevel = -1;
 - 
 -     void ensureSize (int thumbSamples)
 -     {
 -         auto extraNeeded = thumbSamples - data.size();
 - 
 -         if (extraNeeded > 0)
 -             data.insertMultiple (-1, MinMaxValue(), extraNeeded);
 -     }
 - };
 - 
 - //==============================================================================
 - class AudioThumbnail::CachedWindow
 - {
 - public:
 -     CachedWindow() {}
 - 
 -     void invalidate()
 -     {
 -         cacheNeedsRefilling = true;
 -     }
 - 
 -     void drawChannel (Graphics& g, const Rectangle<int>& area,
 -                       const double startTime, const double endTime,
 -                       const int channelNum, const float verticalZoomFactor,
 -                       const double rate, const int numChans, const int sampsPerThumbSample,
 -                       LevelDataSource* levelData, const OwnedArray<ThumbData>& chans)
 -     {
 -         if (refillCache (area.getWidth(), startTime, endTime, rate,
 -                          numChans, sampsPerThumbSample, levelData, chans)
 -              && isPositiveAndBelow (channelNum, numChannelsCached))
 -         {
 -             auto clip = g.getClipBounds().getIntersection (area.withWidth (jmin (numSamplesCached, area.getWidth())));
 - 
 -             if (! clip.isEmpty())
 -             {
 -                 auto topY = (float) area.getY();
 -                 auto bottomY = (float) area.getBottom();
 -                 auto midY = (topY + bottomY) * 0.5f;
 -                 auto vscale = verticalZoomFactor * (bottomY - topY) / 256.0f;
 - 
 -                 auto* cacheData = getData (channelNum, clip.getX() - area.getX());
 - 
 -                 RectangleList<float> waveform;
 -                 waveform.ensureStorageAllocated (clip.getWidth());
 - 
 -                 auto x = (float) clip.getX();
 - 
 -                 for (int w = clip.getWidth(); --w >= 0;)
 -                 {
 -                     if (cacheData->isNonZero())
 -                     {
 -                         auto top    = jmax (midY - cacheData->getMaxValue() * vscale - 0.3f, topY);
 -                         auto bottom = jmin (midY - cacheData->getMinValue() * vscale + 0.3f, bottomY);
 - 
 -                         waveform.addWithoutMerging (Rectangle<float> (x, top, 1.0f, bottom - top));
 -                     }
 - 
 -                     x += 1.0f;
 -                     ++cacheData;
 -                 }
 - 
 -                 g.fillRectList (waveform);
 -             }
 -         }
 -     }
 - 
 - private:
 -     Array<MinMaxValue> data;
 -     double cachedStart = 0, cachedTimePerPixel = 0;
 -     int numChannelsCached = 0, numSamplesCached = 0;
 -     bool cacheNeedsRefilling = true;
 - 
 -     bool refillCache (int numSamples, double startTime, double endTime,
 -                       double rate, int numChans, int sampsPerThumbSample,
 -                       LevelDataSource* levelData, const OwnedArray<ThumbData>& chans)
 -     {
 -         auto timePerPixel = (endTime - startTime) / numSamples;
 - 
 -         if (numSamples <= 0 || timePerPixel <= 0.0 || rate <= 0)
 -         {
 -             invalidate();
 -             return false;
 -         }
 - 
 -         if (numSamples == numSamplesCached
 -              && numChannelsCached == numChans
 -              && startTime == cachedStart
 -              && timePerPixel == cachedTimePerPixel
 -              && ! cacheNeedsRefilling)
 -         {
 -             return ! cacheNeedsRefilling;
 -         }
 - 
 -         numSamplesCached = numSamples;
 -         numChannelsCached = numChans;
 -         cachedStart = startTime;
 -         cachedTimePerPixel = timePerPixel;
 -         cacheNeedsRefilling = false;
 - 
 -         ensureSize (numSamples);
 - 
 -         if (timePerPixel * rate <= sampsPerThumbSample && levelData != nullptr)
 -         {
 -             auto sample = roundToInt (startTime * rate);
 -             Array<Range<float>> levels;
 - 
 -             int i;
 -             for (i = 0; i < numSamples; ++i)
 -             {
 -                 auto nextSample = roundToInt ((startTime + timePerPixel) * rate);
 - 
 -                 if (sample >= 0)
 -                 {
 -                     if (sample >= levelData->lengthInSamples)
 -                     {
 -                         for (int chan = 0; chan < numChannelsCached; ++chan)
 -                             *getData (chan, i) = MinMaxValue();
 -                     }
 -                     else
 -                     {
 -                         levelData->getLevels (sample, jmax (1, nextSample - sample), levels);
 - 
 -                         auto totalChans = jmin (levels.size(), numChannelsCached);
 - 
 -                         for (int chan = 0; chan < totalChans; ++chan)
 -                             getData (chan, i)->setFloat (levels.getReference (chan));
 -                     }
 -                 }
 - 
 -                 startTime += timePerPixel;
 -                 sample = nextSample;
 -             }
 - 
 -             numSamplesCached = i;
 -         }
 -         else
 -         {
 -             jassert (chans.size() == numChannelsCached);
 - 
 -             for (int channelNum = 0; channelNum < numChannelsCached; ++channelNum)
 -             {
 -                 ThumbData* channelData = chans.getUnchecked (channelNum);
 -                 MinMaxValue* cacheData = getData (channelNum, 0);
 - 
 -                 auto timeToThumbSampleFactor = rate / (double) sampsPerThumbSample;
 - 
 -                 startTime = cachedStart;
 -                 auto sample = roundToInt (startTime * timeToThumbSampleFactor);
 - 
 -                 for (int i = numSamples; --i >= 0;)
 -                 {
 -                     auto nextSample = roundToInt ((startTime + timePerPixel) * timeToThumbSampleFactor);
 - 
 -                     channelData->getMinMax (sample, nextSample, *cacheData);
 - 
 -                     ++cacheData;
 -                     startTime += timePerPixel;
 -                     sample = nextSample;
 -                 }
 -             }
 -         }
 - 
 -         return true;
 -     }
 - 
 -     MinMaxValue* getData (const int channelNum, const int cacheIndex) noexcept
 -     {
 -         jassert (isPositiveAndBelow (channelNum, numChannelsCached) && isPositiveAndBelow (cacheIndex, data.size()));
 - 
 -         return data.getRawDataPointer() + channelNum * numSamplesCached
 -                                         + cacheIndex;
 -     }
 - 
 -     void ensureSize (const int numSamples)
 -     {
 -         auto itemsRequired = numSamples * numChannelsCached;
 - 
 -         if (data.size() < itemsRequired)
 -             data.insertMultiple (-1, MinMaxValue(), itemsRequired - data.size());
 -     }
 - };
 - 
 - //==============================================================================
 - AudioThumbnail::AudioThumbnail (const int originalSamplesPerThumbnailSample,
 -                                 AudioFormatManager& formatManager,
 -                                 AudioThumbnailCache& cacheToUse)
 -     : formatManagerToUse (formatManager),
 -       cache (cacheToUse),
 -       window (new CachedWindow()),
 -       samplesPerThumbSample (originalSamplesPerThumbnailSample)
 - {
 - }
 - 
 - AudioThumbnail::~AudioThumbnail()
 - {
 -     clear();
 - }
 - 
 - void AudioThumbnail::clear()
 - {
 -     source.reset();
 -     const ScopedLock sl (lock);
 -     clearChannelData();
 - }
 - 
 - void AudioThumbnail::clearChannelData()
 - {
 -     window->invalidate();
 -     channels.clear();
 -     totalSamples = numSamplesFinished = 0;
 -     numChannels = 0;
 -     sampleRate = 0;
 - 
 -     sendChangeMessage();
 - }
 - 
 - void AudioThumbnail::reset (int newNumChannels, double newSampleRate, int64 totalSamplesInSource)
 - {
 -     clear();
 - 
 -     const ScopedLock sl (lock);
 -     numChannels = newNumChannels;
 -     sampleRate = newSampleRate;
 -     totalSamples = totalSamplesInSource;
 - 
 -     createChannels (1 + (int) (totalSamplesInSource / samplesPerThumbSample));
 - }
 - 
 - void AudioThumbnail::createChannels (const int length)
 - {
 -     while (channels.size() < numChannels)
 -         channels.add (new ThumbData (length));
 - }
 - 
 - //==============================================================================
 - bool AudioThumbnail::loadFrom (InputStream& rawInput)
 - {
 -     BufferedInputStream input (rawInput, 4096);
 - 
 -     if (input.readByte() != 'j' || input.readByte() != 'a' || input.readByte() != 't' || input.readByte() != 'm')
 -         return false;
 - 
 -     const ScopedLock sl (lock);
 -     clearChannelData();
 - 
 -     samplesPerThumbSample = input.readInt();
 -     totalSamples = input.readInt64();             // Total number of source samples.
 -     numSamplesFinished = input.readInt64();       // Number of valid source samples that have been read into the thumbnail.
 -     int32 numThumbnailSamples = input.readInt();  // Number of samples in the thumbnail data.
 -     numChannels = input.readInt();                // Number of audio channels.
 -     sampleRate = input.readInt();                 // Source sample rate.
 -     input.skipNextBytes (16);                     // (reserved)
 - 
 -     createChannels (numThumbnailSamples);
 - 
 -     for (int i = 0; i < numThumbnailSamples; ++i)
 -         for (int chan = 0; chan < numChannels; ++chan)
 -             channels.getUnchecked(chan)->getData(i)->read (input);
 - 
 -     return true;
 - }
 - 
 - void AudioThumbnail::saveTo (OutputStream& output) const
 - {
 -     const ScopedLock sl (lock);
 - 
 -     const int numThumbnailSamples = channels.size() == 0 ? 0 : channels.getUnchecked(0)->getSize();
 - 
 -     output.write ("jatm", 4);
 -     output.writeInt (samplesPerThumbSample);
 -     output.writeInt64 (totalSamples);
 -     output.writeInt64 (numSamplesFinished);
 -     output.writeInt (numThumbnailSamples);
 -     output.writeInt (numChannels);
 -     output.writeInt ((int) sampleRate);
 -     output.writeInt64 (0);
 -     output.writeInt64 (0);
 - 
 -     for (int i = 0; i < numThumbnailSamples; ++i)
 -         for (int chan = 0; chan < numChannels; ++chan)
 -             channels.getUnchecked(chan)->getData(i)->write (output);
 - }
 - 
 - //==============================================================================
 - bool AudioThumbnail::setDataSource (LevelDataSource* newSource)
 - {
 -     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 - 
 -     numSamplesFinished = 0;
 -     auto wasSuccessful = [&] { return sampleRate > 0 && totalSamples > 0; };
 - 
 -     if (cache.loadThumb (*this, newSource->hashCode) && isFullyLoaded())
 -     {
 -         source.reset (newSource); // (make sure this isn't done before loadThumb is called)
 - 
 -         source->lengthInSamples = totalSamples;
 -         source->sampleRate = sampleRate;
 -         source->numChannels = (unsigned int) numChannels;
 -         source->numSamplesFinished = numSamplesFinished;
 - 
 -         return wasSuccessful();
 -     }
 - 
 -     source.reset (newSource);
 - 
 -     const ScopedLock sl (lock);
 -     source->initialise (numSamplesFinished);
 - 
 -     totalSamples = source->lengthInSamples;
 -     sampleRate = source->sampleRate;
 -     numChannels = (int32) source->numChannels;
 - 
 -     createChannels (1 + (int) (totalSamples / samplesPerThumbSample));
 - 
 -     return wasSuccessful();
 - }
 - 
 - bool AudioThumbnail::setSource (InputSource* const newSource)
 - {
 -     clear();
 - 
 -     return newSource != nullptr && setDataSource (new LevelDataSource (*this, newSource));
 - }
 - 
 - void AudioThumbnail::setReader (AudioFormatReader* newReader, int64 hash)
 - {
 -     clear();
 - 
 -     if (newReader != nullptr)
 -         setDataSource (new LevelDataSource (*this, newReader, hash));
 - }
 - 
 - int64 AudioThumbnail::getHashCode() const
 - {
 -     return source == nullptr ? 0 : source->hashCode;
 - }
 - 
 - void AudioThumbnail::addBlock (int64 startSample, const AudioBuffer<float>& incoming,
 -                                int startOffsetInBuffer, int numSamples)
 - {
 -     jassert (startSample >= 0
 -               && startOffsetInBuffer >= 0
 -               && startOffsetInBuffer + numSamples <= incoming.getNumSamples());
 - 
 -     auto firstThumbIndex = (int) (startSample / samplesPerThumbSample);
 -     auto lastThumbIndex  = (int) ((startSample + numSamples + (samplesPerThumbSample - 1)) / samplesPerThumbSample);
 -     auto numToDo = lastThumbIndex - firstThumbIndex;
 - 
 -     if (numToDo > 0)
 -     {
 -         auto numChans = jmin (channels.size(), incoming.getNumChannels());
 - 
 -         const HeapBlock<MinMaxValue> thumbData (numToDo * numChans);
 -         const HeapBlock<MinMaxValue*> thumbChannels (numChans);
 - 
 -         for (int chan = 0; chan < numChans; ++chan)
 -         {
 -             auto* sourceData = incoming.getReadPointer (chan, startOffsetInBuffer);
 -             auto* dest = thumbData + numToDo * chan;
 -             thumbChannels [chan] = dest;
 - 
 -             for (int i = 0; i < numToDo; ++i)
 -             {
 -                 auto start = i * samplesPerThumbSample;
 -                 dest[i].setFloat (FloatVectorOperations::findMinAndMax (sourceData + start, jmin (samplesPerThumbSample, numSamples - start)));
 -             }
 -         }
 - 
 -         setLevels (thumbChannels, firstThumbIndex, numChans, numToDo);
 -     }
 - }
 - 
 - void AudioThumbnail::setLevels (const MinMaxValue* const* values, int thumbIndex, int numChans, int numValues)
 - {
 -     const ScopedLock sl (lock);
 - 
 -     for (int i = jmin (numChans, channels.size()); --i >= 0;)
 -         channels.getUnchecked(i)->write (values[i], thumbIndex, numValues);
 - 
 -     auto start = thumbIndex * (int64) samplesPerThumbSample;
 -     auto end   = (thumbIndex + numValues) * (int64) samplesPerThumbSample;
 - 
 -     if (numSamplesFinished >= start && end > numSamplesFinished)
 -         numSamplesFinished = end;
 - 
 -     totalSamples = jmax (numSamplesFinished, totalSamples.load());
 -     window->invalidate();
 -     sendChangeMessage();
 - }
 - 
 - //==============================================================================
 - int AudioThumbnail::getNumChannels() const noexcept
 - {
 -     return numChannels;
 - }
 - 
 - double AudioThumbnail::getTotalLength() const noexcept
 - {
 -     return sampleRate > 0 ? (totalSamples / sampleRate) : 0;
 - }
 - 
 - bool AudioThumbnail::isFullyLoaded() const noexcept
 - {
 -     return numSamplesFinished >= totalSamples - samplesPerThumbSample;
 - }
 - 
 - double AudioThumbnail::getProportionComplete() const noexcept
 - {
 -     return jlimit (0.0, 1.0, numSamplesFinished / (double) jmax ((int64) 1, totalSamples.load()));
 - }
 - 
 - int64 AudioThumbnail::getNumSamplesFinished() const noexcept
 - {
 -     return numSamplesFinished;
 - }
 - 
 - float AudioThumbnail::getApproximatePeak() const
 - {
 -     const ScopedLock sl (lock);
 -     int peak = 0;
 - 
 -     for (auto* c : channels)
 -         peak = jmax (peak, c->getPeak());
 - 
 -     return jlimit (0, 127, peak) / 127.0f;
 - }
 - 
 - void AudioThumbnail::getApproximateMinMax (double startTime, double endTime, int channelIndex,
 -                                            float& minValue, float& maxValue) const noexcept
 - {
 -     const ScopedLock sl (lock);
 -     MinMaxValue result;
 -     auto* data = channels [channelIndex];
 - 
 -     if (data != nullptr && sampleRate > 0)
 -     {
 -         auto firstThumbIndex = (int) ((startTime * sampleRate) / samplesPerThumbSample);
 -         auto lastThumbIndex  = (int) (((endTime * sampleRate) + samplesPerThumbSample - 1) / samplesPerThumbSample);
 - 
 -         data->getMinMax (jmax (0, firstThumbIndex), lastThumbIndex, result);
 -     }
 - 
 -     minValue = result.getMinValue() / 128.0f;
 -     maxValue = result.getMaxValue() / 128.0f;
 - }
 - 
 - void AudioThumbnail::drawChannel (Graphics& g, const Rectangle<int>& area, double startTime,
 -                                   double endTime, int channelNum, float verticalZoomFactor)
 - {
 -     const ScopedLock sl (lock);
 - 
 -     window->drawChannel (g, area, startTime, endTime, channelNum, verticalZoomFactor,
 -                          sampleRate, numChannels, samplesPerThumbSample, source.get(), channels);
 - }
 - 
 - void AudioThumbnail::drawChannels (Graphics& g, const Rectangle<int>& area, double startTimeSeconds,
 -                                    double endTimeSeconds, float verticalZoomFactor)
 - {
 -     for (int i = 0; i < numChannels; ++i)
 -     {
 -         auto y1 = roundToInt ((i * area.getHeight()) / numChannels);
 -         auto y2 = roundToInt (((i + 1) * area.getHeight()) / numChannels);
 - 
 -         drawChannel (g, { area.getX(), area.getY() + y1, area.getWidth(), y2 - y1 },
 -                      startTimeSeconds, endTimeSeconds, i, verticalZoomFactor);
 -     }
 - }
 - 
 - } // namespace juce
 
 
  |