OpenShot Library | OpenShotAudio  0.2.1
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 
23 namespace juce
24 {
25 
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 
37 
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 
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 
64 {
65  const ScopedLock sl (callbackLock);
66 
67  buffer.clear();
68  bufferPos = 0;
69  sampsInBuffer = 0;
70  subSampleOffset = 0.0;
71  resetFilters();
72 }
73 
75 {
76  input->releaseResources();
77  buffer.setSize (numChannels, 0);
78 }
79 
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 
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 
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 
233 void ResamplingAudioSource::resetFilters()
234 {
235  if (filterStates != nullptr)
236  filterStates.clear ((size_t) numChannels);
237 }
238 
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
void prepareToPlay(int samplesPerBlockExpected, double sampleRate) override
Tells the source to prepare for playing.
int numSamples
The number of samples in the buffer which the callback is expected to fill with data.
void getNextAudioBlock(const AudioSourceChannelInfo &) override
Called repeatedly to fetch subsequent blocks of audio data.
void setSize(int newNumChannels, int newNumSamples, bool keepExistingContent=false, bool clearExtraSpace=false, bool avoidReallocating=false)
Changes the buffer&#39;s size or number of channels.
void releaseResources() override
Allows the source to release anything it no longer needs after playback has stopped.
const Type * getReadPointer(int channelNumber) const noexcept
Returns a pointer to an array of read-only samples in one of the buffer&#39;s channels.
void calloc(SizeType newNumElements, const size_t elementSize=sizeof(ElementType))
Allocates a specified amount of memory and clears it.
void flushBuffers()
Clears any buffers and filters that the resampler is using.
Base class for objects that can produce a continuous stream of audio.
int getNumChannels() const noexcept
Returns the number of channels of audio data that this buffer contains.
~ResamplingAudioSource() override
Destructor.
void clear(SizeType numElements) noexcept
This fills the block with zeros, up to the number of elements specified.
ResamplingAudioSource(AudioSource *inputSource, bool deleteInputWhenDeleted, int numChannels=2)
Creates a ResamplingAudioSource for a given input source.
int startSample
The first sample in the buffer from which the callback is expected to write data. ...
AudioBuffer< float > * buffer
The destination buffer to fill with audio data.
void setResamplingRatio(double samplesInPerOutputSample)
Changes the resampling ratio.
Type * getWritePointer(int channelNumber) noexcept
Returns a writeable pointer to one of the buffer&#39;s channels.
int getNumSamples() const noexcept
Returns the number of samples allocated in each of the buffer&#39;s channels.
Commonly used mathematical constants.
Used by AudioSource::getNextAudioBlock().
Automatically locks and unlocks a mutex object.
void clear() noexcept
Clears all the samples in all channels.