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.

276 lines
9.4KB

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