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.

271 lines
9.3KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 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. BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* source_,
  19. TimeSliceThread& backgroundThread_,
  20. const bool deleteSourceWhenDeleted,
  21. const int numberOfSamplesToBuffer_,
  22. const int numberOfChannels_)
  23. : source (source_, deleteSourceWhenDeleted),
  24. backgroundThread (backgroundThread_),
  25. numberOfSamplesToBuffer (jmax (1024, numberOfSamplesToBuffer_)),
  26. numberOfChannels (numberOfChannels_),
  27. buffer (numberOfChannels_, 0),
  28. bufferValidStart (0),
  29. bufferValidEnd (0),
  30. nextPlayPos (0),
  31. wasSourceLooping (false),
  32. isPrepared (false)
  33. {
  34. jassert (source_ != nullptr);
  35. jassert (numberOfSamplesToBuffer_ > 1024); // not much point using this class if you're
  36. // not using a larger buffer..
  37. }
  38. BufferingAudioSource::~BufferingAudioSource()
  39. {
  40. releaseResources();
  41. }
  42. //==============================================================================
  43. void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate_)
  44. {
  45. const int bufferSizeNeeded = jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer);
  46. if (sampleRate_ != sampleRate
  47. || bufferSizeNeeded != buffer.getNumSamples()
  48. || ! isPrepared)
  49. {
  50. backgroundThread.removeTimeSliceClient (this);
  51. isPrepared = true;
  52. sampleRate = sampleRate_;
  53. source->prepareToPlay (samplesPerBlockExpected, sampleRate_);
  54. buffer.setSize (numberOfChannels, bufferSizeNeeded);
  55. buffer.clear();
  56. bufferValidStart = 0;
  57. bufferValidEnd = 0;
  58. backgroundThread.addTimeSliceClient (this);
  59. while (bufferValidEnd - bufferValidStart < jmin (((int) sampleRate_) / 4,
  60. buffer.getNumSamples() / 2))
  61. {
  62. backgroundThread.moveToFrontOfQueue (this);
  63. Thread::sleep (5);
  64. }
  65. }
  66. }
  67. void BufferingAudioSource::releaseResources()
  68. {
  69. isPrepared = false;
  70. backgroundThread.removeTimeSliceClient (this);
  71. buffer.setSize (numberOfChannels, 0);
  72. source->releaseResources();
  73. }
  74. void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
  75. {
  76. const ScopedLock sl (bufferStartPosLock);
  77. const int validStart = (int) (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos) - nextPlayPos);
  78. const int validEnd = (int) (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos + info.numSamples) - nextPlayPos);
  79. if (validStart == validEnd)
  80. {
  81. // total cache miss
  82. info.clearActiveBufferRegion();
  83. }
  84. else
  85. {
  86. if (validStart > 0)
  87. info.buffer->clear (info.startSample, validStart); // partial cache miss at start
  88. if (validEnd < info.numSamples)
  89. info.buffer->clear (info.startSample + validEnd,
  90. info.numSamples - validEnd); // partial cache miss at end
  91. if (validStart < validEnd)
  92. {
  93. for (int chan = jmin (numberOfChannels, info.buffer->getNumChannels()); --chan >= 0;)
  94. {
  95. jassert (buffer.getNumSamples() > 0);
  96. const int startBufferIndex = (int) ((validStart + nextPlayPos) % buffer.getNumSamples());
  97. const int endBufferIndex = (int) ((validEnd + nextPlayPos) % buffer.getNumSamples());
  98. if (startBufferIndex < endBufferIndex)
  99. {
  100. info.buffer->copyFrom (chan, info.startSample + validStart,
  101. buffer,
  102. chan, startBufferIndex,
  103. validEnd - validStart);
  104. }
  105. else
  106. {
  107. const int initialSize = buffer.getNumSamples() - startBufferIndex;
  108. info.buffer->copyFrom (chan, info.startSample + validStart,
  109. buffer,
  110. chan, startBufferIndex,
  111. initialSize);
  112. info.buffer->copyFrom (chan, info.startSample + validStart + initialSize,
  113. buffer,
  114. chan, 0,
  115. (validEnd - validStart) - initialSize);
  116. }
  117. }
  118. }
  119. nextPlayPos += info.numSamples;
  120. if (source->isLooping() && nextPlayPos > 0)
  121. nextPlayPos %= source->getTotalLength();
  122. }
  123. }
  124. int64 BufferingAudioSource::getNextReadPosition() const
  125. {
  126. jassert (source->getTotalLength() > 0);
  127. return (source->isLooping() && nextPlayPos > 0)
  128. ? nextPlayPos % source->getTotalLength()
  129. : nextPlayPos;
  130. }
  131. void BufferingAudioSource::setNextReadPosition (int64 newPosition)
  132. {
  133. const ScopedLock sl (bufferStartPosLock);
  134. nextPlayPos = newPosition;
  135. backgroundThread.moveToFrontOfQueue (this);
  136. }
  137. bool BufferingAudioSource::readNextBufferChunk()
  138. {
  139. int64 newBVS, newBVE, sectionToReadStart, sectionToReadEnd;
  140. {
  141. const ScopedLock sl (bufferStartPosLock);
  142. if (wasSourceLooping != isLooping())
  143. {
  144. wasSourceLooping = isLooping();
  145. bufferValidStart = 0;
  146. bufferValidEnd = 0;
  147. }
  148. newBVS = jmax ((int64) 0, nextPlayPos);
  149. newBVE = newBVS + buffer.getNumSamples() - 4;
  150. sectionToReadStart = 0;
  151. sectionToReadEnd = 0;
  152. const int maxChunkSize = 2048;
  153. if (newBVS < bufferValidStart || newBVS >= bufferValidEnd)
  154. {
  155. newBVE = jmin (newBVE, newBVS + maxChunkSize);
  156. sectionToReadStart = newBVS;
  157. sectionToReadEnd = newBVE;
  158. bufferValidStart = 0;
  159. bufferValidEnd = 0;
  160. }
  161. else if (std::abs ((int) (newBVS - bufferValidStart)) > 512
  162. || std::abs ((int) (newBVE - bufferValidEnd)) > 512)
  163. {
  164. newBVE = jmin (newBVE, bufferValidEnd + maxChunkSize);
  165. sectionToReadStart = bufferValidEnd;
  166. sectionToReadEnd = newBVE;
  167. bufferValidStart = newBVS;
  168. bufferValidEnd = jmin (bufferValidEnd, newBVE);
  169. }
  170. }
  171. if (sectionToReadStart != sectionToReadEnd)
  172. {
  173. jassert (buffer.getNumSamples() > 0);
  174. const int bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples());
  175. const int bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples());
  176. if (bufferIndexStart < bufferIndexEnd)
  177. {
  178. readBufferSection (sectionToReadStart,
  179. (int) (sectionToReadEnd - sectionToReadStart),
  180. bufferIndexStart);
  181. }
  182. else
  183. {
  184. const int initialSize = buffer.getNumSamples() - bufferIndexStart;
  185. readBufferSection (sectionToReadStart,
  186. initialSize,
  187. bufferIndexStart);
  188. readBufferSection (sectionToReadStart + initialSize,
  189. (int) (sectionToReadEnd - sectionToReadStart) - initialSize,
  190. 0);
  191. }
  192. const ScopedLock sl2 (bufferStartPosLock);
  193. bufferValidStart = newBVS;
  194. bufferValidEnd = newBVE;
  195. return true;
  196. }
  197. else
  198. {
  199. return false;
  200. }
  201. }
  202. void BufferingAudioSource::readBufferSection (const int64 start, const int length, const int bufferOffset)
  203. {
  204. if (source->getNextReadPosition() != start)
  205. source->setNextReadPosition (start);
  206. AudioSourceChannelInfo info;
  207. info.buffer = &buffer;
  208. info.startSample = bufferOffset;
  209. info.numSamples = length;
  210. source->getNextAudioBlock (info);
  211. }
  212. int BufferingAudioSource::useTimeSlice()
  213. {
  214. return readNextBufferChunk() ? 1 : 100;
  215. }