1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    The code included in this file is provided under the terms of the ISC license
11    http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12    To use, copy, modify, and/or distribute this software for any purpose with or
13    without fee is hereby granted provided that the above copyright notice and
14    this permission notice appear in all copies.
15 
16    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18    DISCLAIMED.
19 
20   ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
AudioTransportSource()26 AudioTransportSource::AudioTransportSource()
27 {
28 }
29 
~AudioTransportSource()30 AudioTransportSource::~AudioTransportSource()
31 {
32     setSource (nullptr);
33     releaseMasterResources();
34 }
35 
setSource(PositionableAudioSource * const newSource,int readAheadSize,TimeSliceThread * readAheadThread,double sourceSampleRateToCorrectFor,int maxNumChannels)36 void AudioTransportSource::setSource (PositionableAudioSource* const newSource,
37                                       int readAheadSize, TimeSliceThread* readAheadThread,
38                                       double sourceSampleRateToCorrectFor, int maxNumChannels)
39 {
40     if (source == newSource)
41     {
42         if (source == nullptr)
43             return;
44 
45         setSource (nullptr, 0, nullptr); // deselect and reselect to avoid releasing resources wrongly
46     }
47 
48     readAheadBufferSize = readAheadSize;
49     sourceSampleRate = sourceSampleRateToCorrectFor;
50 
51     ResamplingAudioSource* newResamplerSource = nullptr;
52     BufferingAudioSource* newBufferingSource = nullptr;
53     PositionableAudioSource* newPositionableSource = nullptr;
54     AudioSource* newMasterSource = nullptr;
55 
56     std::unique_ptr<ResamplingAudioSource> oldResamplerSource (resamplerSource);
57     std::unique_ptr<BufferingAudioSource> oldBufferingSource (bufferingSource);
58     AudioSource* oldMasterSource = masterSource;
59 
60     if (newSource != nullptr)
61     {
62         newPositionableSource = newSource;
63 
64         if (readAheadSize > 0)
65         {
66             // If you want to use a read-ahead buffer, you must also provide a TimeSliceThread
67             // for it to use!
68             jassert (readAheadThread != nullptr);
69 
70             newPositionableSource = newBufferingSource
71                 = new BufferingAudioSource (newPositionableSource, *readAheadThread,
72                                             false, readAheadSize, maxNumChannels);
73         }
74 
75         newPositionableSource->setNextReadPosition (0);
76 
77         if (sourceSampleRateToCorrectFor > 0)
78             newMasterSource = newResamplerSource
79                 = new ResamplingAudioSource (newPositionableSource, false, maxNumChannels);
80         else
81             newMasterSource = newPositionableSource;
82 
83         if (isPrepared)
84         {
85             if (newResamplerSource != nullptr && sourceSampleRate > 0 && sampleRate > 0)
86                 newResamplerSource->setResamplingRatio (sourceSampleRate / sampleRate);
87 
88             newMasterSource->prepareToPlay (blockSize, sampleRate);
89         }
90     }
91 
92     {
93         const ScopedLock sl (callbackLock);
94 
95         source = newSource;
96         resamplerSource = newResamplerSource;
97         bufferingSource = newBufferingSource;
98         masterSource = newMasterSource;
99         positionableSource = newPositionableSource;
100 
101         inputStreamEOF = false;
102         playing = false;
103     }
104 
105     if (oldMasterSource != nullptr)
106         oldMasterSource->releaseResources();
107 }
108 
start()109 void AudioTransportSource::start()
110 {
111     if ((! playing) && masterSource != nullptr)
112     {
113         {
114             const ScopedLock sl (callbackLock);
115             playing = true;
116             stopped = false;
117             inputStreamEOF = false;
118         }
119 
120         sendChangeMessage();
121     }
122 }
123 
stop()124 void AudioTransportSource::stop()
125 {
126     if (playing)
127     {
128         playing = false;
129 
130         int n = 500;
131         while (--n >= 0 && ! stopped)
132             Thread::sleep (2);
133 
134         sendChangeMessage();
135     }
136 }
137 
setPosition(double newPosition)138 void AudioTransportSource::setPosition (double newPosition)
139 {
140     if (sampleRate > 0.0)
141         setNextReadPosition ((int64) (newPosition * sampleRate));
142 }
143 
getCurrentPosition() const144 double AudioTransportSource::getCurrentPosition() const
145 {
146     if (sampleRate > 0.0)
147         return (double) getNextReadPosition() / sampleRate;
148 
149     return 0.0;
150 }
151 
getLengthInSeconds() const152 double AudioTransportSource::getLengthInSeconds() const
153 {
154     if (sampleRate > 0.0)
155         return (double) getTotalLength() / sampleRate;
156 
157     return 0.0;
158 }
159 
setNextReadPosition(int64 newPosition)160 void AudioTransportSource::setNextReadPosition (int64 newPosition)
161 {
162     if (positionableSource != nullptr)
163     {
164         if (sampleRate > 0 && sourceSampleRate > 0)
165             newPosition = (int64) ((double) newPosition * sourceSampleRate / sampleRate);
166 
167         positionableSource->setNextReadPosition (newPosition);
168 
169         if (resamplerSource != nullptr)
170             resamplerSource->flushBuffers();
171 
172         inputStreamEOF = false;
173     }
174 }
175 
getNextReadPosition() const176 int64 AudioTransportSource::getNextReadPosition() const
177 {
178     if (positionableSource != nullptr)
179     {
180         const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0;
181         return (int64) ((double) positionableSource->getNextReadPosition() * ratio);
182     }
183 
184     return 0;
185 }
186 
getTotalLength() const187 int64 AudioTransportSource::getTotalLength() const
188 {
189     const ScopedLock sl (callbackLock);
190 
191     if (positionableSource != nullptr)
192     {
193         const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0;
194         return (int64) ((double) positionableSource->getTotalLength() * ratio);
195     }
196 
197     return 0;
198 }
199 
isLooping() const200 bool AudioTransportSource::isLooping() const
201 {
202     const ScopedLock sl (callbackLock);
203     return positionableSource != nullptr && positionableSource->isLooping();
204 }
205 
setGain(const float newGain)206 void AudioTransportSource::setGain (const float newGain) noexcept
207 {
208     gain = newGain;
209 }
210 
prepareToPlay(int samplesPerBlockExpected,double newSampleRate)211 void AudioTransportSource::prepareToPlay (int samplesPerBlockExpected, double newSampleRate)
212 {
213     const ScopedLock sl (callbackLock);
214 
215     sampleRate = newSampleRate;
216     blockSize = samplesPerBlockExpected;
217 
218     if (masterSource != nullptr)
219         masterSource->prepareToPlay (samplesPerBlockExpected, sampleRate);
220 
221     if (resamplerSource != nullptr && sourceSampleRate > 0)
222         resamplerSource->setResamplingRatio (sourceSampleRate / sampleRate);
223 
224     inputStreamEOF = false;
225     isPrepared = true;
226 }
227 
releaseMasterResources()228 void AudioTransportSource::releaseMasterResources()
229 {
230     const ScopedLock sl (callbackLock);
231 
232     if (masterSource != nullptr)
233         masterSource->releaseResources();
234 
235     isPrepared = false;
236 }
237 
releaseResources()238 void AudioTransportSource::releaseResources()
239 {
240     releaseMasterResources();
241 }
242 
getNextAudioBlock(const AudioSourceChannelInfo & info)243 void AudioTransportSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
244 {
245     const ScopedLock sl (callbackLock);
246 
247     if (masterSource != nullptr && ! stopped)
248     {
249         masterSource->getNextAudioBlock (info);
250 
251         if (! playing)
252         {
253             // just stopped playing, so fade out the last block..
254             for (int i = info.buffer->getNumChannels(); --i >= 0;)
255                 info.buffer->applyGainRamp (i, info.startSample, jmin (256, info.numSamples), 1.0f, 0.0f);
256 
257             if (info.numSamples > 256)
258                 info.buffer->clear (info.startSample + 256, info.numSamples - 256);
259         }
260 
261         if (positionableSource->getNextReadPosition() > positionableSource->getTotalLength() + 1
262               && ! positionableSource->isLooping())
263         {
264             playing = false;
265             inputStreamEOF = true;
266             sendChangeMessage();
267         }
268 
269         stopped = ! playing;
270 
271         for (int i = info.buffer->getNumChannels(); --i >= 0;)
272             info.buffer->applyGainRamp (i, info.startSample, info.numSamples, lastGain, gain);
273     }
274     else
275     {
276         info.clearActiveBufferRegion();
277         stopped = true;
278     }
279 
280     lastGain = gain;
281 }
282 
283 } // namespace juce
284