|
|
|
/*
|
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
This file is part of the JUCE library.
|
|
|
|
Copyright (c) 2013 - Raw Material Software Ltd.
|
|
|
|
|
|
|
|
Permission is granted to use this software under the terms of either:
|
|
|
|
a) the GPL v2 (or any later version)
|
|
|
|
b) the Affero GPL v3
|
|
|
|
|
|
|
|
Details of these licenses can be found 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.juce.com for more information.
|
|
|
|
|
|
|
|
==============================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
AudioTransportSource::AudioTransportSource()
|
|
|
|
: source (nullptr),
|
|
|
|
resamplerSource (nullptr),
|
|
|
|
bufferingSource (nullptr),
|
|
|
|
positionableSource (nullptr),
|
|
|
|
masterSource (nullptr),
|
|
|
|
gain (1.0f),
|
|
|
|
lastGain (1.0f),
|
|
|
|
playing (false),
|
|
|
|
stopped (true),
|
|
|
|
sampleRate (44100.0),
|
|
|
|
sourceSampleRate (0.0),
|
|
|
|
blockSize (128),
|
|
|
|
readAheadBufferSize (0),
|
|
|
|
isPrepared (false),
|
|
|
|
inputStreamEOF (false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
AudioTransportSource::~AudioTransportSource()
|
|
|
|
{
|
|
|
|
setSource (nullptr);
|
|
|
|
releaseMasterResources();
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioTransportSource::setSource (PositionableAudioSource* const newSource,
|
|
|
|
int readAheadSize, TimeSliceThread* readAheadThread,
|
|
|
|
double sourceSampleRateToCorrectFor, int maxNumChannels)
|
|
|
|
{
|
|
|
|
if (source == newSource)
|
|
|
|
{
|
|
|
|
if (source == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
setSource (nullptr, 0, nullptr); // deselect and reselect to avoid releasing resources wrongly
|
|
|
|
}
|
|
|
|
|
|
|
|
readAheadBufferSize = readAheadSize;
|
|
|
|
sourceSampleRate = sourceSampleRateToCorrectFor;
|
|
|
|
|
|
|
|
ResamplingAudioSource* newResamplerSource = nullptr;
|
|
|
|
BufferingAudioSource* newBufferingSource = nullptr;
|
|
|
|
PositionableAudioSource* newPositionableSource = nullptr;
|
|
|
|
AudioSource* newMasterSource = nullptr;
|
|
|
|
|
|
|
|
ScopedPointer<ResamplingAudioSource> oldResamplerSource (resamplerSource);
|
|
|
|
ScopedPointer<BufferingAudioSource> oldBufferingSource (bufferingSource);
|
|
|
|
AudioSource* oldMasterSource = masterSource;
|
|
|
|
|
|
|
|
if (newSource != nullptr)
|
|
|
|
{
|
|
|
|
newPositionableSource = newSource;
|
|
|
|
|
|
|
|
if (readAheadSize > 0)
|
|
|
|
{
|
|
|
|
// If you want to use a read-ahead buffer, you must also provide a TimeSliceThread
|
|
|
|
// for it to use!
|
|
|
|
jassert (readAheadThread != nullptr);
|
|
|
|
|
|
|
|
newPositionableSource = newBufferingSource
|
|
|
|
= new BufferingAudioSource (newPositionableSource, *readAheadThread,
|
|
|
|
false, readAheadSize, maxNumChannels);
|
|
|
|
}
|
|
|
|
|
|
|
|
newPositionableSource->setNextReadPosition (0);
|
|
|
|
|
|
|
|
if (sourceSampleRateToCorrectFor > 0)
|
|
|
|
newMasterSource = newResamplerSource
|
|
|
|
= new ResamplingAudioSource (newPositionableSource, false, maxNumChannels);
|
|
|
|
else
|
|
|
|
newMasterSource = newPositionableSource;
|
|
|
|
|
|
|
|
if (isPrepared)
|
|
|
|
{
|
|
|
|
if (newResamplerSource != nullptr && sourceSampleRate > 0 && sampleRate > 0)
|
|
|
|
newResamplerSource->setResamplingRatio (sourceSampleRate / sampleRate);
|
|
|
|
|
|
|
|
newMasterSource->prepareToPlay (blockSize, sampleRate);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const ScopedLock sl (callbackLock);
|
|
|
|
|
|
|
|
source = newSource;
|
|
|
|
resamplerSource = newResamplerSource;
|
|
|
|
bufferingSource = newBufferingSource;
|
|
|
|
masterSource = newMasterSource;
|
|
|
|
positionableSource = newPositionableSource;
|
|
|
|
|
|
|
|
inputStreamEOF = false;
|
|
|
|
playing = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (oldMasterSource != nullptr)
|
|
|
|
oldMasterSource->releaseResources();
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioTransportSource::start()
|
|
|
|
{
|
|
|
|
if ((! playing) && masterSource != nullptr)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const ScopedLock sl (callbackLock);
|
|
|
|
playing = true;
|
|
|
|
stopped = false;
|
|
|
|
inputStreamEOF = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
sendChangeMessage();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioTransportSource::stop()
|
|
|
|
{
|
|
|
|
if (playing)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const ScopedLock sl (callbackLock);
|
|
|
|
playing = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int n = 500;
|
|
|
|
while (--n >= 0 && ! stopped)
|
|
|
|
Thread::sleep (2);
|
|
|
|
|
|
|
|
sendChangeMessage();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioTransportSource::setPosition (double newPosition)
|
|
|
|
{
|
|
|
|
if (sampleRate > 0.0)
|
|
|
|
setNextReadPosition ((int64) (newPosition * sampleRate));
|
|
|
|
}
|
|
|
|
|
|
|
|
double AudioTransportSource::getCurrentPosition() const
|
|
|
|
{
|
|
|
|
if (sampleRate > 0.0)
|
|
|
|
return getNextReadPosition() / sampleRate;
|
|
|
|
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
double AudioTransportSource::getLengthInSeconds() const
|
|
|
|
{
|
|
|
|
if (sampleRate > 0.0)
|
|
|
|
return getTotalLength() / sampleRate;
|
|
|
|
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioTransportSource::setNextReadPosition (int64 newPosition)
|
|
|
|
{
|
|
|
|
if (positionableSource != nullptr)
|
|
|
|
{
|
|
|
|
if (sampleRate > 0 && sourceSampleRate > 0)
|
|
|
|
newPosition = (int64) (newPosition * sourceSampleRate / sampleRate);
|
|
|
|
|
|
|
|
positionableSource->setNextReadPosition (newPosition);
|
|
|
|
inputStreamEOF = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int64 AudioTransportSource::getNextReadPosition() const
|
|
|
|
{
|
|
|
|
if (positionableSource != nullptr)
|
|
|
|
{
|
|
|
|
const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0;
|
|
|
|
return (int64) (positionableSource->getNextReadPosition() * ratio);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int64 AudioTransportSource::getTotalLength() const
|
|
|
|
{
|
|
|
|
const ScopedLock sl (callbackLock);
|
|
|
|
|
|
|
|
if (positionableSource != nullptr)
|
|
|
|
{
|
|
|
|
const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0;
|
|
|
|
return (int64) (positionableSource->getTotalLength() * ratio);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AudioTransportSource::isLooping() const
|
|
|
|
{
|
|
|
|
const ScopedLock sl (callbackLock);
|
|
|
|
return positionableSource != nullptr && positionableSource->isLooping();
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioTransportSource::setGain (const float newGain) noexcept
|
|
|
|
{
|
|
|
|
gain = newGain;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioTransportSource::prepareToPlay (int samplesPerBlockExpected, double newSampleRate)
|
|
|
|
{
|
|
|
|
const ScopedLock sl (callbackLock);
|
|
|
|
|
|
|
|
sampleRate = newSampleRate;
|
|
|
|
blockSize = samplesPerBlockExpected;
|
|
|
|
|
|
|
|
if (masterSource != nullptr)
|
|
|
|
masterSource->prepareToPlay (samplesPerBlockExpected, sampleRate);
|
|
|
|
|
|
|
|
if (resamplerSource != nullptr && sourceSampleRate > 0)
|
|
|
|
resamplerSource->setResamplingRatio (sourceSampleRate / sampleRate);
|
|
|
|
|
|
|
|
inputStreamEOF = false;
|
|
|
|
isPrepared = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioTransportSource::releaseMasterResources()
|
|
|
|
{
|
|
|
|
const ScopedLock sl (callbackLock);
|
|
|
|
|
|
|
|
if (masterSource != nullptr)
|
|
|
|
masterSource->releaseResources();
|
|
|
|
|
|
|
|
isPrepared = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioTransportSource::releaseResources()
|
|
|
|
{
|
|
|
|
releaseMasterResources();
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioTransportSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
|
|
|
|
{
|
|
|
|
const ScopedLock sl (callbackLock);
|
|
|
|
|
|
|
|
if (masterSource != nullptr && ! stopped)
|
|
|
|
{
|
|
|
|
masterSource->getNextAudioBlock (info);
|
|
|
|
|
|
|
|
if (! playing)
|
|
|
|
{
|
|
|
|
// just stopped playing, so fade out the last block..
|
|
|
|
for (int i = info.buffer->getNumChannels(); --i >= 0;)
|
|
|
|
info.buffer->applyGainRamp (i, info.startSample, jmin (256, info.numSamples), 1.0f, 0.0f);
|
|
|
|
|
|
|
|
if (info.numSamples > 256)
|
|
|
|
info.buffer->clear (info.startSample + 256, info.numSamples - 256);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (positionableSource->getNextReadPosition() > positionableSource->getTotalLength() + 1
|
|
|
|
&& ! positionableSource->isLooping())
|
|
|
|
{
|
|
|
|
playing = false;
|
|
|
|
inputStreamEOF = true;
|
|
|
|
sendChangeMessage();
|
|
|
|
}
|
|
|
|
|
|
|
|
stopped = ! playing;
|
|
|
|
|
|
|
|
for (int i = info.buffer->getNumChannels(); --i >= 0;)
|
|
|
|
info.buffer->applyGainRamp (i, info.startSample, info.numSamples, lastGain, gain);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
info.clearActiveBufferRegion();
|
|
|
|
stopped = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
lastGain = gain;
|
|
|
|
}
|