The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

275 lines
8.2KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-10 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "../../core/juce_StandardHeader.h"
  19. BEGIN_JUCE_NAMESPACE
  20. #include "juce_ResamplingAudioSource.h"
  21. #include "../../threads/juce_ScopedLock.h"
  22. //==============================================================================
  23. ResamplingAudioSource::ResamplingAudioSource (AudioSource* const inputSource,
  24. const bool deleteInputWhenDeleted_,
  25. const int numChannels_)
  26. : input (inputSource),
  27. deleteInputWhenDeleted (deleteInputWhenDeleted_),
  28. ratio (1.0),
  29. lastRatio (1.0),
  30. buffer (numChannels_, 0),
  31. sampsInBuffer (0),
  32. numChannels (numChannels_)
  33. {
  34. jassert (input != 0);
  35. }
  36. ResamplingAudioSource::~ResamplingAudioSource()
  37. {
  38. if (deleteInputWhenDeleted)
  39. delete input;
  40. }
  41. void ResamplingAudioSource::setResamplingRatio (const double samplesInPerOutputSample)
  42. {
  43. jassert (samplesInPerOutputSample > 0);
  44. const ScopedLock sl (ratioLock);
  45. ratio = jmax (0.0, samplesInPerOutputSample);
  46. }
  47. void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected,
  48. double sampleRate)
  49. {
  50. const ScopedLock sl (ratioLock);
  51. input->prepareToPlay (samplesPerBlockExpected, sampleRate);
  52. buffer.setSize (numChannels, roundToInt (samplesPerBlockExpected * ratio) + 32);
  53. buffer.clear();
  54. sampsInBuffer = 0;
  55. bufferPos = 0;
  56. subSampleOffset = 0.0;
  57. filterStates.calloc (numChannels);
  58. srcBuffers.calloc (numChannels);
  59. destBuffers.calloc (numChannels);
  60. createLowPass (ratio);
  61. resetFilters();
  62. }
  63. void ResamplingAudioSource::releaseResources()
  64. {
  65. input->releaseResources();
  66. buffer.setSize (numChannels, 0);
  67. }
  68. void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
  69. {
  70. float localRatio;
  71. {
  72. const ScopedLock sl (ratioLock);
  73. localRatio = ratio;
  74. }
  75. if (lastRatio != localRatio)
  76. {
  77. createLowPass (localRatio);
  78. lastRatio = localRatio;
  79. }
  80. const int sampsNeeded = roundToInt (info.numSamples * localRatio) + 2;
  81. int bufferSize = buffer.getNumSamples();
  82. if (bufferSize < sampsNeeded + 8)
  83. {
  84. bufferPos %= bufferSize;
  85. bufferSize = sampsNeeded + 32;
  86. buffer.setSize (buffer.getNumChannels(), bufferSize, true, true);
  87. }
  88. bufferPos %= bufferSize;
  89. int endOfBufferPos = bufferPos + sampsInBuffer;
  90. const int channelsToProcess = jmin (numChannels, info.buffer->getNumChannels());
  91. while (sampsNeeded > sampsInBuffer)
  92. {
  93. endOfBufferPos %= bufferSize;
  94. int numToDo = jmin (sampsNeeded - sampsInBuffer,
  95. bufferSize - endOfBufferPos);
  96. AudioSourceChannelInfo readInfo;
  97. readInfo.buffer = &buffer;
  98. readInfo.numSamples = numToDo;
  99. readInfo.startSample = endOfBufferPos;
  100. input->getNextAudioBlock (readInfo);
  101. if (localRatio > 1.0001)
  102. {
  103. // for down-sampling, pre-apply the filter..
  104. for (int i = channelsToProcess; --i >= 0;)
  105. applyFilter (buffer.getSampleData (i, endOfBufferPos), numToDo, filterStates[i]);
  106. }
  107. sampsInBuffer += numToDo;
  108. endOfBufferPos += numToDo;
  109. }
  110. for (int channel = 0; channel < channelsToProcess; ++channel)
  111. {
  112. destBuffers[channel] = info.buffer->getSampleData (channel, info.startSample);
  113. srcBuffers[channel] = buffer.getSampleData (channel, 0);
  114. }
  115. int nextPos = (bufferPos + 1) % bufferSize;
  116. for (int m = info.numSamples; --m >= 0;)
  117. {
  118. const float alpha = (float) subSampleOffset;
  119. const float invAlpha = 1.0f - alpha;
  120. for (int channel = 0; channel < channelsToProcess; ++channel)
  121. *destBuffers[channel]++ = srcBuffers[channel][bufferPos] * invAlpha + srcBuffers[channel][nextPos] * alpha;
  122. subSampleOffset += localRatio;
  123. jassert (sampsInBuffer > 0);
  124. while (subSampleOffset >= 1.0)
  125. {
  126. if (++bufferPos >= bufferSize)
  127. bufferPos = 0;
  128. --sampsInBuffer;
  129. nextPos = (bufferPos + 1) % bufferSize;
  130. subSampleOffset -= 1.0;
  131. }
  132. }
  133. if (localRatio < 0.9999)
  134. {
  135. // for up-sampling, apply the filter after transposing..
  136. for (int i = channelsToProcess; --i >= 0;)
  137. applyFilter (info.buffer->getSampleData (i, info.startSample), info.numSamples, filterStates[i]);
  138. }
  139. else if (localRatio <= 1.0001)
  140. {
  141. // if the filter's not currently being applied, keep it stoked with the last couple of samples to avoid discontinuities
  142. for (int i = channelsToProcess; --i >= 0;)
  143. {
  144. const float* const endOfBuffer = info.buffer->getSampleData (i, info.startSample + info.numSamples - 1);
  145. FilterState& fs = filterStates[i];
  146. if (info.numSamples > 1)
  147. {
  148. fs.y2 = fs.x2 = *(endOfBuffer - 1);
  149. }
  150. else
  151. {
  152. fs.y2 = fs.y1;
  153. fs.x2 = fs.x1;
  154. }
  155. fs.y1 = fs.x1 = *endOfBuffer;
  156. }
  157. }
  158. jassert (sampsInBuffer >= 0);
  159. }
  160. void ResamplingAudioSource::createLowPass (const double frequencyRatio)
  161. {
  162. const double proportionalRate = (frequencyRatio > 1.0) ? 0.5 / frequencyRatio
  163. : 0.5 * frequencyRatio;
  164. const double n = 1.0 / std::tan (double_Pi * jmax (0.001, proportionalRate));
  165. const double nSquared = n * n;
  166. const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared);
  167. setFilterCoefficients (c1,
  168. c1 * 2.0f,
  169. c1,
  170. 1.0,
  171. c1 * 2.0 * (1.0 - nSquared),
  172. c1 * (1.0 - std::sqrt (2.0) * n + nSquared));
  173. }
  174. void ResamplingAudioSource::setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6)
  175. {
  176. const double a = 1.0 / c4;
  177. c1 *= a;
  178. c2 *= a;
  179. c3 *= a;
  180. c5 *= a;
  181. c6 *= a;
  182. coefficients[0] = c1;
  183. coefficients[1] = c2;
  184. coefficients[2] = c3;
  185. coefficients[3] = c4;
  186. coefficients[4] = c5;
  187. coefficients[5] = c6;
  188. }
  189. void ResamplingAudioSource::resetFilters()
  190. {
  191. zeromem (filterStates, sizeof (FilterState) * numChannels);
  192. }
  193. void ResamplingAudioSource::applyFilter (float* samples, int num, FilterState& fs)
  194. {
  195. while (--num >= 0)
  196. {
  197. const double in = *samples;
  198. double out = coefficients[0] * in
  199. + coefficients[1] * fs.x1
  200. + coefficients[2] * fs.x2
  201. - coefficients[4] * fs.y1
  202. - coefficients[5] * fs.y2;
  203. #if JUCE_INTEL
  204. if (! (out < -1.0e-8 || out > 1.0e-8))
  205. out = 0;
  206. #endif
  207. fs.x2 = fs.x1;
  208. fs.x1 = in;
  209. fs.y2 = fs.y1;
  210. fs.y1 = out;
  211. *samples++ = (float) out;
  212. }
  213. }
  214. END_JUCE_NAMESPACE