/* ============================================================================== 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 readAheadBufferSize_, 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 = readAheadBufferSize_; sourceSampleRate = sourceSampleRateToCorrectFor; ResamplingAudioSource* newResamplerSource = nullptr; BufferingAudioSource* newBufferingSource = nullptr; PositionableAudioSource* newPositionableSource = nullptr; AudioSource* newMasterSource = nullptr; ScopedPointer oldResamplerSource (resamplerSource); ScopedPointer oldBufferingSource (bufferingSource); AudioSource* oldMasterSource = masterSource; if (newSource != nullptr) { newPositionableSource = newSource; if (readAheadBufferSize_ > 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, readAheadBufferSize_, 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; 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 { return getTotalLength() / sampleRate; } void AudioTransportSource::setNextReadPosition (int64 newPosition) { if (positionableSource != nullptr) { if (sampleRate > 0 && sourceSampleRate > 0) newPosition = (int64) (newPosition * sourceSampleRate / sampleRate); positionableSource->setNextReadPosition (newPosition); } } 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); 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); inputStreamEOF = false; 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; }