|
- /*
- ==============================================================================
-
- This file is part of the JUCE library - "Jules' Utility Class Extensions"
- Copyright 2004-9 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.
-
- ==============================================================================
- */
-
- #include "../../../juce_core/basics/juce_StandardHeader.h"
-
- BEGIN_JUCE_NAMESPACE
-
- #include "juce_BufferingAudioSource.h"
- #include "../../../juce_core/threads/juce_ScopedLock.h"
- #include "../../../juce_core/basics/juce_Singleton.h"
- #include "../../../juce_core/containers/juce_VoidArray.h"
- #include "../../application/juce_DeletedAtShutdown.h"
- #include "../../events/juce_Timer.h"
-
-
- //==============================================================================
- class SharedBufferingAudioSourceThread : public DeletedAtShutdown,
- public Thread,
- private Timer
- {
- public:
- SharedBufferingAudioSourceThread()
- : Thread ("Audio Buffer"),
- sources (8)
- {
- }
-
- ~SharedBufferingAudioSourceThread()
- {
- stopThread (10000);
- clearSingletonInstance();
- }
-
- juce_DeclareSingleton (SharedBufferingAudioSourceThread, false)
-
- void addSource (BufferingAudioSource* source)
- {
- const ScopedLock sl (lock);
-
- if (! sources.contains ((void*) source))
- {
- sources.add ((void*) source);
- startThread();
-
- stopTimer();
- }
-
- notify();
- }
-
- void removeSource (BufferingAudioSource* source)
- {
- const ScopedLock sl (lock);
- sources.removeValue ((void*) source);
-
- if (sources.size() == 0)
- startTimer (5000);
- }
-
- private:
- VoidArray sources;
- CriticalSection lock;
-
- void run()
- {
- while (! threadShouldExit())
- {
- bool busy = false;
-
- for (int i = sources.size(); --i >= 0;)
- {
- if (threadShouldExit())
- return;
-
- const ScopedLock sl (lock);
-
- BufferingAudioSource* const b = (BufferingAudioSource*) sources[i];
-
- if (b != 0 && b->readNextBufferChunk())
- busy = true;
- }
-
- if (! busy)
- wait (500);
- }
- }
-
- void timerCallback()
- {
- stopTimer();
-
- if (sources.size() == 0)
- deleteInstance();
- }
-
- SharedBufferingAudioSourceThread (const SharedBufferingAudioSourceThread&);
- const SharedBufferingAudioSourceThread& operator= (const SharedBufferingAudioSourceThread&);
- };
-
- juce_ImplementSingleton (SharedBufferingAudioSourceThread)
-
- //==============================================================================
- BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* source_,
- const bool deleteSourceWhenDeleted_,
- int numberOfSamplesToBuffer_)
- : source (source_),
- deleteSourceWhenDeleted (deleteSourceWhenDeleted_),
- numberOfSamplesToBuffer (jmax (1024, numberOfSamplesToBuffer_)),
- buffer (2, 0),
- bufferValidStart (0),
- bufferValidEnd (0),
- nextPlayPos (0),
- wasSourceLooping (false)
- {
- jassert (source_ != 0);
-
- jassert (numberOfSamplesToBuffer_ > 1024); // not much point using this class if you're
- // not using a larger buffer..
- }
-
- BufferingAudioSource::~BufferingAudioSource()
- {
- SharedBufferingAudioSourceThread* const thread = SharedBufferingAudioSourceThread::getInstanceWithoutCreating();
-
- if (thread != 0)
- thread->removeSource (this);
-
- if (deleteSourceWhenDeleted)
- delete source;
- }
-
- //==============================================================================
- void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate_)
- {
- source->prepareToPlay (samplesPerBlockExpected, sampleRate_);
-
- sampleRate = sampleRate_;
-
- buffer.setSize (2, jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer));
- buffer.clear();
-
- bufferValidStart = 0;
- bufferValidEnd = 0;
-
- SharedBufferingAudioSourceThread::getInstance()->addSource (this);
-
- while (bufferValidEnd - bufferValidStart < jmin (((int) sampleRate_) / 4,
- buffer.getNumSamples() / 2))
- {
- SharedBufferingAudioSourceThread::getInstance()->notify();
- Thread::sleep (5);
- }
- }
-
- void BufferingAudioSource::releaseResources()
- {
- SharedBufferingAudioSourceThread* const thread = SharedBufferingAudioSourceThread::getInstanceWithoutCreating();
-
- if (thread != 0)
- thread->removeSource (this);
-
- buffer.setSize (2, 0);
- source->releaseResources();
- }
-
- void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
- {
- const ScopedLock sl (bufferStartPosLock);
-
- const int validStart = jlimit (bufferValidStart, bufferValidEnd, nextPlayPos) - nextPlayPos;
- const int validEnd = jlimit (bufferValidStart, bufferValidEnd, nextPlayPos + info.numSamples) - nextPlayPos;
-
- if (validStart == validEnd)
- {
- // total cache miss
- info.clearActiveBufferRegion();
- }
- else
- {
- if (validStart > 0)
- info.buffer->clear (info.startSample, validStart); // partial cache miss at start
-
- if (validEnd < info.numSamples)
- info.buffer->clear (info.startSample + validEnd,
- info.numSamples - validEnd); // partial cache miss at end
-
- if (validStart < validEnd)
- {
- for (int chan = jmin (2, info.buffer->getNumChannels()); --chan >= 0;)
- {
- const int startBufferIndex = (validStart + nextPlayPos) % buffer.getNumSamples();
- const int endBufferIndex = (validEnd + nextPlayPos) % buffer.getNumSamples();
-
- if (startBufferIndex < endBufferIndex)
- {
- info.buffer->copyFrom (chan, info.startSample + validStart,
- buffer,
- chan, startBufferIndex,
- validEnd - validStart);
- }
- else
- {
- const int initialSize = buffer.getNumSamples() - startBufferIndex;
-
- info.buffer->copyFrom (chan, info.startSample + validStart,
- buffer,
- chan, startBufferIndex,
- initialSize);
-
- info.buffer->copyFrom (chan, info.startSample + validStart + initialSize,
- buffer,
- chan, 0,
- (validEnd - validStart) - initialSize);
- }
- }
- }
-
- nextPlayPos += info.numSamples;
-
- if (source->isLooping() && nextPlayPos > 0)
- nextPlayPos %= source->getTotalLength();
- }
-
- SharedBufferingAudioSourceThread* const thread = SharedBufferingAudioSourceThread::getInstanceWithoutCreating();
-
- if (thread != 0)
- thread->notify();
- }
-
- int BufferingAudioSource::getNextReadPosition() const
- {
- return (source->isLooping() && nextPlayPos > 0)
- ? nextPlayPos % source->getTotalLength()
- : nextPlayPos;
- }
-
- void BufferingAudioSource::setNextReadPosition (int newPosition)
- {
- const ScopedLock sl (bufferStartPosLock);
-
- nextPlayPos = newPosition;
-
- SharedBufferingAudioSourceThread* const thread = SharedBufferingAudioSourceThread::getInstanceWithoutCreating();
-
- if (thread != 0)
- thread->notify();
- }
-
- bool BufferingAudioSource::readNextBufferChunk()
- {
- bufferStartPosLock.enter();
-
- if (wasSourceLooping != isLooping())
- {
- wasSourceLooping = isLooping();
- bufferValidStart = 0;
- bufferValidEnd = 0;
- }
-
- int newBVS = jmax (0, nextPlayPos);
- int newBVE = newBVS + buffer.getNumSamples() - 4;
- int sectionToReadStart = 0;
- int sectionToReadEnd = 0;
-
- const int maxChunkSize = 2048;
-
- if (newBVS < bufferValidStart || newBVS >= bufferValidEnd)
- {
- newBVE = jmin (newBVE, newBVS + maxChunkSize);
-
- sectionToReadStart = newBVS;
- sectionToReadEnd = newBVE;
-
- bufferValidStart = 0;
- bufferValidEnd = 0;
- }
- else if (abs (newBVS - bufferValidStart) > 512
- || abs (newBVE - bufferValidEnd) > 512)
- {
- newBVE = jmin (newBVE, bufferValidEnd + maxChunkSize);
-
- sectionToReadStart = bufferValidEnd;
- sectionToReadEnd = newBVE;
-
- bufferValidStart = newBVS;
- bufferValidEnd = jmin (bufferValidEnd, newBVE);
- }
-
- bufferStartPosLock.exit();
-
- if (sectionToReadStart != sectionToReadEnd)
- {
- const int bufferIndexStart = sectionToReadStart % buffer.getNumSamples();
- const int bufferIndexEnd = sectionToReadEnd % buffer.getNumSamples();
-
- if (bufferIndexStart < bufferIndexEnd)
- {
- readBufferSection (sectionToReadStart,
- sectionToReadEnd - sectionToReadStart,
- bufferIndexStart);
- }
- else
- {
- const int initialSize = buffer.getNumSamples() - bufferIndexStart;
-
- readBufferSection (sectionToReadStart,
- initialSize,
- bufferIndexStart);
-
- readBufferSection (sectionToReadStart + initialSize,
- (sectionToReadEnd - sectionToReadStart) - initialSize,
- 0);
- }
-
- const ScopedLock sl2 (bufferStartPosLock);
-
- bufferValidStart = newBVS;
- bufferValidEnd = newBVE;
-
- return true;
- }
- else
- {
- return false;
- }
- }
-
- void BufferingAudioSource::readBufferSection (int start, int length, int bufferOffset)
- {
- if (source->getNextReadPosition() != start)
- source->setNextReadPosition (start);
-
- AudioSourceChannelInfo info;
- info.buffer = &buffer;
- info.startSample = bufferOffset;
- info.numSamples = length;
-
- source->getNextAudioBlock (info);
- }
-
- END_JUCE_NAMESPACE
|