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.

268 lines
7.8KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 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 "../../../juce_core/basics/juce_StandardHeader.h"
  19. BEGIN_JUCE_NAMESPACE
  20. #include "juce_ResamplingAudioSource.h"
  21. #include "../../../juce_core/threads/juce_ScopedLock.h"
  22. //==============================================================================
  23. ResamplingAudioSource::ResamplingAudioSource (AudioSource* const inputSource,
  24. const bool deleteInputWhenDeleted_)
  25. : input (inputSource),
  26. deleteInputWhenDeleted (deleteInputWhenDeleted_),
  27. ratio (1.0),
  28. lastRatio (1.0),
  29. buffer (2, 0),
  30. sampsInBuffer (0)
  31. {
  32. jassert (input != 0);
  33. }
  34. ResamplingAudioSource::~ResamplingAudioSource()
  35. {
  36. if (deleteInputWhenDeleted)
  37. delete input;
  38. }
  39. void ResamplingAudioSource::setResamplingRatio (const double samplesInPerOutputSample)
  40. {
  41. jassert (samplesInPerOutputSample > 0);
  42. const ScopedLock sl (ratioLock);
  43. ratio = jmax (0.0, samplesInPerOutputSample);
  44. }
  45. void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected,
  46. double sampleRate)
  47. {
  48. const ScopedLock sl (ratioLock);
  49. input->prepareToPlay (samplesPerBlockExpected, sampleRate);
  50. buffer.setSize (2, roundDoubleToInt (samplesPerBlockExpected * ratio) + 32);
  51. buffer.clear();
  52. sampsInBuffer = 0;
  53. bufferPos = 0;
  54. subSampleOffset = 0.0;
  55. createLowPass (ratio);
  56. resetFilters();
  57. }
  58. void ResamplingAudioSource::releaseResources()
  59. {
  60. input->releaseResources();
  61. buffer.setSize (2, 0);
  62. }
  63. void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
  64. {
  65. const ScopedLock sl (ratioLock);
  66. if (lastRatio != ratio)
  67. {
  68. createLowPass (ratio);
  69. lastRatio = ratio;
  70. }
  71. const int sampsNeeded = roundDoubleToInt (info.numSamples * ratio) + 2;
  72. int bufferSize = buffer.getNumSamples();
  73. if (bufferSize < sampsNeeded + 8)
  74. {
  75. bufferPos %= bufferSize;
  76. bufferSize = sampsNeeded + 32;
  77. buffer.setSize (buffer.getNumChannels(), bufferSize, true, true);
  78. }
  79. bufferPos %= bufferSize;
  80. int endOfBufferPos = bufferPos + sampsInBuffer;
  81. while (sampsNeeded > sampsInBuffer)
  82. {
  83. endOfBufferPos %= bufferSize;
  84. int numToDo = jmin (sampsNeeded - sampsInBuffer,
  85. bufferSize - endOfBufferPos);
  86. AudioSourceChannelInfo readInfo;
  87. readInfo.buffer = &buffer;
  88. readInfo.numSamples = numToDo;
  89. readInfo.startSample = endOfBufferPos;
  90. input->getNextAudioBlock (readInfo);
  91. if (ratio > 1.0001)
  92. {
  93. // for down-sampling, pre-apply the filter..
  94. for (int i = jmin (2, info.buffer->getNumChannels()); --i >= 0;)
  95. applyFilter (buffer.getSampleData (i, endOfBufferPos), numToDo, filterStates[i]);
  96. }
  97. sampsInBuffer += numToDo;
  98. endOfBufferPos += numToDo;
  99. }
  100. float* dl = info.buffer->getSampleData (0, info.startSample);
  101. float* dr = (info.buffer->getNumChannels() > 1) ? info.buffer->getSampleData (1, info.startSample) : 0;
  102. const float* const bl = buffer.getSampleData (0, 0);
  103. const float* const br = buffer.getSampleData (1, 0);
  104. int nextPos = (bufferPos + 1) % bufferSize;
  105. for (int m = info.numSamples; --m >= 0;)
  106. {
  107. const float alpha = (float) subSampleOffset;
  108. const float invAlpha = 1.0f - alpha;
  109. *dl++ = bl [bufferPos] * invAlpha + bl [nextPos] * alpha;
  110. if (dr != 0)
  111. *dr++ = br [bufferPos] * invAlpha + br [nextPos] * alpha;
  112. subSampleOffset += ratio;
  113. jassert (sampsInBuffer > 0);
  114. while (subSampleOffset >= 1.0)
  115. {
  116. if (++bufferPos >= bufferSize)
  117. bufferPos = 0;
  118. --sampsInBuffer;
  119. nextPos = (bufferPos + 1) % bufferSize;
  120. subSampleOffset -= 1.0;
  121. }
  122. }
  123. if (ratio < 0.9999)
  124. {
  125. // for up-sampling, apply the filter after transposing..
  126. for (int i = jmin (2, info.buffer->getNumChannels()); --i >= 0;)
  127. applyFilter (info.buffer->getSampleData (i, info.startSample), info.numSamples, filterStates[i]);
  128. }
  129. else if (ratio <= 1.0001)
  130. {
  131. // if the filter's not currently being applied, keep it stoked with the last couple of samples to avoid discontinuities
  132. for (int i = jmin (2, info.buffer->getNumChannels()); --i >= 0;)
  133. {
  134. const float* const endOfBuffer = info.buffer->getSampleData (i, info.startSample + info.numSamples - 1);
  135. FilterState& fs = filterStates[i];
  136. if (info.numSamples > 1)
  137. {
  138. fs.y2 = fs.x2 = *(endOfBuffer - 1);
  139. }
  140. else
  141. {
  142. fs.y2 = fs.y1;
  143. fs.x2 = fs.x1;
  144. }
  145. fs.y1 = fs.x1 = *endOfBuffer;
  146. }
  147. }
  148. jassert (sampsInBuffer >= 0);
  149. }
  150. void ResamplingAudioSource::createLowPass (const double ratio)
  151. {
  152. const double proportionalRate = (ratio > 1.0) ? 0.5 / ratio
  153. : 0.5 * ratio;
  154. const double n = 1.0 / tan (double_Pi * jmax (0.001, proportionalRate));
  155. const double nSquared = n * n;
  156. const double c1 = 1.0 / (1.0 + sqrt (2.0) * n + nSquared);
  157. setFilterCoefficients (c1,
  158. c1 * 2.0f,
  159. c1,
  160. 1.0,
  161. c1 * 2.0 * (1.0 - nSquared),
  162. c1 * (1.0 - sqrt (2.0) * n + nSquared));
  163. }
  164. void ResamplingAudioSource::setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6)
  165. {
  166. const double a = 1.0 / c4;
  167. c1 *= a;
  168. c2 *= a;
  169. c3 *= a;
  170. c5 *= a;
  171. c6 *= a;
  172. coefficients[0] = c1;
  173. coefficients[1] = c2;
  174. coefficients[2] = c3;
  175. coefficients[3] = c4;
  176. coefficients[4] = c5;
  177. coefficients[5] = c6;
  178. }
  179. void ResamplingAudioSource::resetFilters()
  180. {
  181. zeromem (filterStates, sizeof (filterStates));
  182. }
  183. void ResamplingAudioSource::applyFilter (float* samples, int num, FilterState& fs)
  184. {
  185. while (--num >= 0)
  186. {
  187. const double in = *samples;
  188. double out = coefficients[0] * in
  189. + coefficients[1] * fs.x1
  190. + coefficients[2] * fs.x2
  191. - coefficients[4] * fs.y1
  192. - coefficients[5] * fs.y2;
  193. #if JUCE_INTEL
  194. if (! (out < -1.0e-8 || out > 1.0e-8))
  195. out = 0;
  196. #endif
  197. fs.x2 = fs.x1;
  198. fs.x1 = in;
  199. fs.y2 = fs.y1;
  200. fs.y1 = out;
  201. *samples++ = (float) out;
  202. }
  203. }
  204. END_JUCE_NAMESPACE