1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2017 - ROLI Ltd.
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 
ResamplingAudioSource(AudioSource * const inputSource,const bool deleteInputWhenDeleted,const int channels)26 ResamplingAudioSource::ResamplingAudioSource (AudioSource* const inputSource,
27                                               const bool deleteInputWhenDeleted,
28                                               const int channels)
29     : input (inputSource, deleteInputWhenDeleted),
30       numChannels (channels)
31 {
32     jassert (input != nullptr);
33     zeromem (coefficients, sizeof (coefficients));
34 }
35 
~ResamplingAudioSource()36 ResamplingAudioSource::~ResamplingAudioSource() {}
37 
setResamplingRatio(const double samplesInPerOutputSample)38 void ResamplingAudioSource::setResamplingRatio (const double samplesInPerOutputSample)
39 {
40     jassert (samplesInPerOutputSample > 0);
41 
42     const SpinLock::ScopedLockType sl (ratioLock);
43     ratio = jmax (0.0, samplesInPerOutputSample);
44 }
45 
prepareToPlay(int samplesPerBlockExpected,double sampleRate)46 void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
47 {
48     const SpinLock::ScopedLockType sl (ratioLock);
49 
50     auto scaledBlockSize = roundToInt (samplesPerBlockExpected * ratio);
51     input->prepareToPlay (scaledBlockSize, sampleRate * ratio);
52 
53     buffer.setSize (numChannels, scaledBlockSize + 32);
54 
55     filterStates.calloc (numChannels);
56     srcBuffers.calloc (numChannels);
57     destBuffers.calloc (numChannels);
58     createLowPass (ratio);
59 
60     flushBuffers();
61 }
62 
flushBuffers()63 void ResamplingAudioSource::flushBuffers()
64 {
65     const ScopedLock sl (callbackLock);
66 
67     buffer.clear();
68     bufferPos = 0;
69     sampsInBuffer = 0;
70     subSampleOffset = 0.0;
71     resetFilters();
72 }
73 
releaseResources()74 void ResamplingAudioSource::releaseResources()
75 {
76     input->releaseResources();
77     buffer.setSize (numChannels, 0);
78 }
79 
getNextAudioBlock(const AudioSourceChannelInfo & info)80 void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
81 {
82     const ScopedLock sl (callbackLock);
83 
84     double localRatio;
85 
86     {
87         const SpinLock::ScopedLockType ratioSl (ratioLock);
88         localRatio = ratio;
89     }
90 
91     if (lastRatio != localRatio)
92     {
93         createLowPass (localRatio);
94         lastRatio = localRatio;
95     }
96 
97     const int sampsNeeded = roundToInt (info.numSamples * localRatio) + 3;
98 
99     int bufferSize = buffer.getNumSamples();
100 
101     if (bufferSize < sampsNeeded + 8)
102     {
103         bufferPos %= bufferSize;
104         bufferSize = sampsNeeded + 32;
105         buffer.setSize (buffer.getNumChannels(), bufferSize, true, true);
106     }
107 
108     bufferPos %= bufferSize;
109 
110     int endOfBufferPos = bufferPos + sampsInBuffer;
111     const int channelsToProcess = jmin (numChannels, info.buffer->getNumChannels());
112 
113     while (sampsNeeded > sampsInBuffer)
114     {
115         endOfBufferPos %= bufferSize;
116 
117         int numToDo = jmin (sampsNeeded - sampsInBuffer,
118                             bufferSize - endOfBufferPos);
119 
120         AudioSourceChannelInfo readInfo (&buffer, endOfBufferPos, numToDo);
121         input->getNextAudioBlock (readInfo);
122 
123         if (localRatio > 1.0001)
124         {
125             // for down-sampling, pre-apply the filter..
126 
127             for (int i = channelsToProcess; --i >= 0;)
128                 applyFilter (buffer.getWritePointer (i, endOfBufferPos), numToDo, filterStates[i]);
129         }
130 
131         sampsInBuffer += numToDo;
132         endOfBufferPos += numToDo;
133     }
134 
135     for (int channel = 0; channel < channelsToProcess; ++channel)
136     {
137         destBuffers[channel] = info.buffer->getWritePointer (channel, info.startSample);
138         srcBuffers[channel] = buffer.getReadPointer (channel);
139     }
140 
141     int nextPos = (bufferPos + 1) % bufferSize;
142 
143     for (int m = info.numSamples; --m >= 0;)
144     {
145         jassert (sampsInBuffer > 0 && nextPos != endOfBufferPos);
146 
147         const float alpha = (float) subSampleOffset;
148 
149         for (int channel = 0; channel < channelsToProcess; ++channel)
150             *destBuffers[channel]++ = srcBuffers[channel][bufferPos]
151                                         + alpha * (srcBuffers[channel][nextPos] - srcBuffers[channel][bufferPos]);
152 
153         subSampleOffset += localRatio;
154 
155         while (subSampleOffset >= 1.0)
156         {
157             if (++bufferPos >= bufferSize)
158                 bufferPos = 0;
159 
160             --sampsInBuffer;
161 
162             nextPos = (bufferPos + 1) % bufferSize;
163             subSampleOffset -= 1.0;
164         }
165     }
166 
167     if (localRatio < 0.9999)
168     {
169         // for up-sampling, apply the filter after transposing..
170         for (int i = channelsToProcess; --i >= 0;)
171             applyFilter (info.buffer->getWritePointer (i, info.startSample), info.numSamples, filterStates[i]);
172     }
173     else if (localRatio <= 1.0001 && info.numSamples > 0)
174     {
175         // if the filter's not currently being applied, keep it stoked with the last couple of samples to avoid discontinuities
176         for (int i = channelsToProcess; --i >= 0;)
177         {
178             const float* const endOfBuffer = info.buffer->getReadPointer (i, info.startSample + info.numSamples - 1);
179             FilterState& fs = filterStates[i];
180 
181             if (info.numSamples > 1)
182             {
183                 fs.y2 = fs.x2 = *(endOfBuffer - 1);
184             }
185             else
186             {
187                 fs.y2 = fs.y1;
188                 fs.x2 = fs.x1;
189             }
190 
191             fs.y1 = fs.x1 = *endOfBuffer;
192         }
193     }
194 
195     jassert (sampsInBuffer >= 0);
196 }
197 
createLowPass(const double frequencyRatio)198 void ResamplingAudioSource::createLowPass (const double frequencyRatio)
199 {
200     const double proportionalRate = (frequencyRatio > 1.0) ? 0.5 / frequencyRatio
201                                                            : 0.5 * frequencyRatio;
202 
203     const double n = 1.0 / std::tan (MathConstants<double>::pi * jmax (0.001, proportionalRate));
204     const double nSquared = n * n;
205     const double c1 = 1.0 / (1.0 + MathConstants<double>::sqrt2 * n + nSquared);
206 
207     setFilterCoefficients (c1,
208                            c1 * 2.0f,
209                            c1,
210                            1.0,
211                            c1 * 2.0 * (1.0 - nSquared),
212                            c1 * (1.0 - MathConstants<double>::sqrt2 * n + nSquared));
213 }
214 
setFilterCoefficients(double c1,double c2,double c3,double c4,double c5,double c6)215 void ResamplingAudioSource::setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6)
216 {
217     const double a = 1.0 / c4;
218 
219     c1 *= a;
220     c2 *= a;
221     c3 *= a;
222     c5 *= a;
223     c6 *= a;
224 
225     coefficients[0] = c1;
226     coefficients[1] = c2;
227     coefficients[2] = c3;
228     coefficients[3] = c4;
229     coefficients[4] = c5;
230     coefficients[5] = c6;
231 }
232 
resetFilters()233 void ResamplingAudioSource::resetFilters()
234 {
235     if (filterStates != nullptr)
236         filterStates.clear ((size_t) numChannels);
237 }
238 
applyFilter(float * samples,int num,FilterState & fs)239 void ResamplingAudioSource::applyFilter (float* samples, int num, FilterState& fs)
240 {
241     while (--num >= 0)
242     {
243         const double in = *samples;
244 
245         double out = coefficients[0] * in
246                      + coefficients[1] * fs.x1
247                      + coefficients[2] * fs.x2
248                      - coefficients[4] * fs.y1
249                      - coefficients[5] * fs.y2;
250 
251        #if JUCE_INTEL
252         if (! (out < -1.0e-8 || out > 1.0e-8))
253             out = 0;
254        #endif
255 
256         fs.x2 = fs.x1;
257         fs.x1 = in;
258         fs.y2 = fs.y1;
259         fs.y1 = out;
260 
261         *samples++ = (float) out;
262     }
263 }
264 
265 } // namespace juce
266