OpenShot Library | libopenshot-audio 0.2.0
juce_ResamplingAudioSource.cpp
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
23namespace juce
24{
25
27 const bool deleteInputWhenDeleted,
28 const int channels)
29 : input (inputSource, deleteInputWhenDeleted),
30 ratio (1.0),
31 lastRatio (1.0),
32 bufferPos (0),
33 sampsInBuffer (0),
34 subSampleOffset (0),
35 numChannels (channels)
36{
37 jassert (input != nullptr);
38 zeromem (coefficients, sizeof (coefficients));
39}
40
42
43void ResamplingAudioSource::setResamplingRatio (const double samplesInPerOutputSample)
44{
45 jassert (samplesInPerOutputSample > 0);
46
47 const SpinLock::ScopedLockType sl (ratioLock);
48 ratio = jmax (0.0, samplesInPerOutputSample);
49}
50
51void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
52{
53 const SpinLock::ScopedLockType sl (ratioLock);
54
55 auto scaledBlockSize = roundToInt (samplesPerBlockExpected * ratio);
56 input->prepareToPlay (scaledBlockSize, sampleRate * ratio);
57
58 buffer.setSize (numChannels, scaledBlockSize + 32);
59
60 filterStates.calloc (numChannels);
61 srcBuffers.calloc (numChannels);
62 destBuffers.calloc (numChannels);
63 createLowPass (ratio);
64
66}
67
69{
70 buffer.clear();
71 bufferPos = 0;
72 sampsInBuffer = 0;
73 subSampleOffset = 0.0;
74 resetFilters();
75}
76
78{
79 input->releaseResources();
80 buffer.setSize (numChannels, 0);
81}
82
84{
85 double localRatio;
86
87 {
88 const SpinLock::ScopedLockType sl (ratioLock);
89 localRatio = ratio;
90 }
91
92 if (lastRatio != localRatio)
93 {
94 createLowPass (localRatio);
95 lastRatio = localRatio;
96 }
97
98 const int sampsNeeded = roundToInt (info.numSamples * localRatio) + 3;
99
100 int bufferSize = buffer.getNumSamples();
101
102 if (bufferSize < sampsNeeded + 8)
103 {
104 bufferPos %= bufferSize;
105 bufferSize = sampsNeeded + 32;
106 buffer.setSize (buffer.getNumChannels(), bufferSize, true, true);
107 }
108
109 bufferPos %= bufferSize;
110
111 int endOfBufferPos = bufferPos + sampsInBuffer;
112 const int channelsToProcess = jmin (numChannels, info.buffer->getNumChannels());
113
114 while (sampsNeeded > sampsInBuffer)
115 {
116 endOfBufferPos %= bufferSize;
117
118 int numToDo = jmin (sampsNeeded - sampsInBuffer,
119 bufferSize - endOfBufferPos);
120
121 AudioSourceChannelInfo readInfo (&buffer, endOfBufferPos, numToDo);
122 input->getNextAudioBlock (readInfo);
123
124 if (localRatio > 1.0001)
125 {
126 // for down-sampling, pre-apply the filter..
127
128 for (int i = channelsToProcess; --i >= 0;)
129 applyFilter (buffer.getWritePointer (i, endOfBufferPos), numToDo, filterStates[i]);
130 }
131
132 sampsInBuffer += numToDo;
133 endOfBufferPos += numToDo;
134 }
135
136 for (int channel = 0; channel < channelsToProcess; ++channel)
137 {
138 destBuffers[channel] = info.buffer->getWritePointer (channel, info.startSample);
139 srcBuffers[channel] = buffer.getReadPointer (channel);
140 }
141
142 int nextPos = (bufferPos + 1) % bufferSize;
143
144 for (int m = info.numSamples; --m >= 0;)
145 {
146 jassert (sampsInBuffer > 0 && nextPos != endOfBufferPos);
147
148 const float alpha = (float) subSampleOffset;
149
150 for (int channel = 0; channel < channelsToProcess; ++channel)
151 *destBuffers[channel]++ = srcBuffers[channel][bufferPos]
152 + alpha * (srcBuffers[channel][nextPos] - srcBuffers[channel][bufferPos]);
153
154 subSampleOffset += localRatio;
155
156 while (subSampleOffset >= 1.0)
157 {
158 if (++bufferPos >= bufferSize)
159 bufferPos = 0;
160
161 --sampsInBuffer;
162
163 nextPos = (bufferPos + 1) % bufferSize;
164 subSampleOffset -= 1.0;
165 }
166 }
167
168 if (localRatio < 0.9999)
169 {
170 // for up-sampling, apply the filter after transposing..
171 for (int i = channelsToProcess; --i >= 0;)
172 applyFilter (info.buffer->getWritePointer (i, info.startSample), info.numSamples, filterStates[i]);
173 }
174 else if (localRatio <= 1.0001 && info.numSamples > 0)
175 {
176 // if the filter's not currently being applied, keep it stoked with the last couple of samples to avoid discontinuities
177 for (int i = channelsToProcess; --i >= 0;)
178 {
179 const float* const endOfBuffer = info.buffer->getReadPointer (i, info.startSample + info.numSamples - 1);
180 FilterState& fs = filterStates[i];
181
182 if (info.numSamples > 1)
183 {
184 fs.y2 = fs.x2 = *(endOfBuffer - 1);
185 }
186 else
187 {
188 fs.y2 = fs.y1;
189 fs.x2 = fs.x1;
190 }
191
192 fs.y1 = fs.x1 = *endOfBuffer;
193 }
194 }
195
196 jassert (sampsInBuffer >= 0);
197}
198
199void ResamplingAudioSource::createLowPass (const double frequencyRatio)
200{
201 const double proportionalRate = (frequencyRatio > 1.0) ? 0.5 / frequencyRatio
202 : 0.5 * frequencyRatio;
203
204 const double n = 1.0 / std::tan (MathConstants<double>::pi * jmax (0.001, proportionalRate));
205 const double nSquared = n * n;
206 const double c1 = 1.0 / (1.0 + MathConstants<double>::sqrt2 * n + nSquared);
207
208 setFilterCoefficients (c1,
209 c1 * 2.0f,
210 c1,
211 1.0,
212 c1 * 2.0 * (1.0 - nSquared),
213 c1 * (1.0 - MathConstants<double>::sqrt2 * n + nSquared));
214}
215
216void ResamplingAudioSource::setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6)
217{
218 const double a = 1.0 / c4;
219
220 c1 *= a;
221 c2 *= a;
222 c3 *= a;
223 c5 *= a;
224 c6 *= a;
225
226 coefficients[0] = c1;
227 coefficients[1] = c2;
228 coefficients[2] = c3;
229 coefficients[3] = c4;
230 coefficients[4] = c5;
231 coefficients[5] = c6;
232}
233
234void ResamplingAudioSource::resetFilters()
235{
236 if (filterStates != nullptr)
237 filterStates.clear ((size_t) numChannels);
238}
239
240void ResamplingAudioSource::applyFilter (float* samples, int num, FilterState& fs)
241{
242 while (--num >= 0)
243 {
244 const double in = *samples;
245
246 double out = coefficients[0] * in
247 + coefficients[1] * fs.x1
248 + coefficients[2] * fs.x2
249 - coefficients[4] * fs.y1
250 - coefficients[5] * fs.y2;
251
252 #if JUCE_INTEL
253 if (! (out < -1.0e-8 || out > 1.0e-8))
254 out = 0;
255 #endif
256
257 fs.x2 = fs.x1;
258 fs.x1 = in;
259 fs.y2 = fs.y1;
260 fs.y1 = out;
261
262 *samples++ = (float) out;
263 }
264}
265
266} // namespace juce
void setSize(int newNumChannels, int newNumSamples, bool keepExistingContent=false, bool clearExtraSpace=false, bool avoidReallocating=false)
Changes the buffer's size or number of channels.
Type * getWritePointer(int channelNumber) noexcept
Returns a writeable pointer to one of the buffer's channels.
int getNumChannels() const noexcept
Returns the number of channels of audio data that this buffer contains.
int getNumSamples() const noexcept
Returns the number of samples allocated in each of the buffer's channels.
void clear() noexcept
Clears all the samples in all channels.
const Type * getReadPointer(int channelNumber) const noexcept
Returns a pointer to an array of read-only samples in one of the buffer's channels.
Base class for objects that can produce a continuous stream of audio.
Automatically locks and unlocks a mutex object.
void clear(SizeType numElements) noexcept
This fills the block with zeros, up to the number of elements specified.
void calloc(SizeType newNumElements, const size_t elementSize=sizeof(ElementType))
Allocates a specified amount of memory and clears it.
void releaseResources() override
Allows the source to release anything it no longer needs after playback has stopped.
void getNextAudioBlock(const AudioSourceChannelInfo &) override
Called repeatedly to fetch subsequent blocks of audio data.
void prepareToPlay(int samplesPerBlockExpected, double sampleRate) override
Tells the source to prepare for playing.
void flushBuffers()
Clears any buffers and filters that the resampler is using.
void setResamplingRatio(double samplesInPerOutputSample)
Changes the resampling ratio.
ResamplingAudioSource(AudioSource *inputSource, bool deleteInputWhenDeleted, int numChannels=2)
Creates a ResamplingAudioSource for a given input source.
Used by AudioSource::getNextAudioBlock().
int numSamples
The number of samples in the buffer which the callback is expected to fill with data.
AudioBuffer< float > * buffer
The destination buffer to fill with audio data.
int startSample
The first sample in the buffer from which the callback is expected to write data.
Commonly used mathematical constants.