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.

262 lines
8.9KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* source_,
  18. TimeSliceThread& backgroundThread_,
  19. const bool deleteSourceWhenDeleted,
  20. const int numberOfSamplesToBuffer_,
  21. const int numberOfChannels_)
  22. : source (source_, deleteSourceWhenDeleted),
  23. backgroundThread (backgroundThread_),
  24. numberOfSamplesToBuffer (jmax (1024, numberOfSamplesToBuffer_)),
  25. numberOfChannels (numberOfChannels_),
  26. buffer (numberOfChannels_, 0),
  27. bufferValidStart (0),
  28. bufferValidEnd (0),
  29. nextPlayPos (0),
  30. wasSourceLooping (false),
  31. isPrepared (false)
  32. {
  33. jassert (source_ != nullptr);
  34. jassert (numberOfSamplesToBuffer_ > 1024); // not much point using this class if you're
  35. // not using a larger buffer..
  36. }
  37. BufferingAudioSource::~BufferingAudioSource()
  38. {
  39. releaseResources();
  40. }
  41. //==============================================================================
  42. void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate_)
  43. {
  44. const int bufferSizeNeeded = jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer);
  45. if (sampleRate_ != sampleRate
  46. || bufferSizeNeeded != buffer.getNumSamples()
  47. || ! isPrepared)
  48. {
  49. backgroundThread.removeTimeSliceClient (this);
  50. isPrepared = true;
  51. sampleRate = sampleRate_;
  52. source->prepareToPlay (samplesPerBlockExpected, sampleRate_);
  53. buffer.setSize (numberOfChannels, bufferSizeNeeded);
  54. buffer.clear();
  55. bufferValidStart = 0;
  56. bufferValidEnd = 0;
  57. backgroundThread.addTimeSliceClient (this);
  58. while (bufferValidEnd - bufferValidStart < jmin (((int) sampleRate_) / 4,
  59. buffer.getNumSamples() / 2))
  60. {
  61. backgroundThread.moveToFrontOfQueue (this);
  62. Thread::sleep (5);
  63. }
  64. }
  65. }
  66. void BufferingAudioSource::releaseResources()
  67. {
  68. isPrepared = false;
  69. backgroundThread.removeTimeSliceClient (this);
  70. buffer.setSize (numberOfChannels, 0);
  71. source->releaseResources();
  72. }
  73. void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
  74. {
  75. const ScopedLock sl (bufferStartPosLock);
  76. const int validStart = (int) (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos) - nextPlayPos);
  77. const int validEnd = (int) (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos + info.numSamples) - nextPlayPos);
  78. if (validStart == validEnd)
  79. {
  80. // total cache miss
  81. info.clearActiveBufferRegion();
  82. }
  83. else
  84. {
  85. if (validStart > 0)
  86. info.buffer->clear (info.startSample, validStart); // partial cache miss at start
  87. if (validEnd < info.numSamples)
  88. info.buffer->clear (info.startSample + validEnd,
  89. info.numSamples - validEnd); // partial cache miss at end
  90. if (validStart < validEnd)
  91. {
  92. for (int chan = jmin (numberOfChannels, info.buffer->getNumChannels()); --chan >= 0;)
  93. {
  94. jassert (buffer.getNumSamples() > 0);
  95. const int startBufferIndex = (int) ((validStart + nextPlayPos) % buffer.getNumSamples());
  96. const int endBufferIndex = (int) ((validEnd + nextPlayPos) % buffer.getNumSamples());
  97. if (startBufferIndex < endBufferIndex)
  98. {
  99. info.buffer->copyFrom (chan, info.startSample + validStart,
  100. buffer,
  101. chan, startBufferIndex,
  102. validEnd - validStart);
  103. }
  104. else
  105. {
  106. const int initialSize = buffer.getNumSamples() - startBufferIndex;
  107. info.buffer->copyFrom (chan, info.startSample + validStart,
  108. buffer,
  109. chan, startBufferIndex,
  110. initialSize);
  111. info.buffer->copyFrom (chan, info.startSample + validStart + initialSize,
  112. buffer,
  113. chan, 0,
  114. (validEnd - validStart) - initialSize);
  115. }
  116. }
  117. }
  118. nextPlayPos += info.numSamples;
  119. }
  120. }
  121. int64 BufferingAudioSource::getNextReadPosition() const
  122. {
  123. jassert (source->getTotalLength() > 0);
  124. return (source->isLooping() && nextPlayPos > 0)
  125. ? nextPlayPos % source->getTotalLength()
  126. : nextPlayPos;
  127. }
  128. void BufferingAudioSource::setNextReadPosition (int64 newPosition)
  129. {
  130. const ScopedLock sl (bufferStartPosLock);
  131. nextPlayPos = newPosition;
  132. backgroundThread.moveToFrontOfQueue (this);
  133. }
  134. bool BufferingAudioSource::readNextBufferChunk()
  135. {
  136. int64 newBVS, newBVE, sectionToReadStart, sectionToReadEnd;
  137. {
  138. const ScopedLock sl (bufferStartPosLock);
  139. if (wasSourceLooping != isLooping())
  140. {
  141. wasSourceLooping = isLooping();
  142. bufferValidStart = 0;
  143. bufferValidEnd = 0;
  144. }
  145. newBVS = jmax ((int64) 0, nextPlayPos);
  146. newBVE = newBVS + buffer.getNumSamples() - 4;
  147. sectionToReadStart = 0;
  148. sectionToReadEnd = 0;
  149. const int maxChunkSize = 2048;
  150. if (newBVS < bufferValidStart || newBVS >= bufferValidEnd)
  151. {
  152. newBVE = jmin (newBVE, newBVS + maxChunkSize);
  153. sectionToReadStart = newBVS;
  154. sectionToReadEnd = newBVE;
  155. bufferValidStart = 0;
  156. bufferValidEnd = 0;
  157. }
  158. else if (std::abs ((int) (newBVS - bufferValidStart)) > 512
  159. || std::abs ((int) (newBVE - bufferValidEnd)) > 512)
  160. {
  161. newBVE = jmin (newBVE, bufferValidEnd + maxChunkSize);
  162. sectionToReadStart = bufferValidEnd;
  163. sectionToReadEnd = newBVE;
  164. bufferValidStart = newBVS;
  165. bufferValidEnd = jmin (bufferValidEnd, newBVE);
  166. }
  167. }
  168. if (sectionToReadStart != sectionToReadEnd)
  169. {
  170. jassert (buffer.getNumSamples() > 0);
  171. const int bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples());
  172. const int bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples());
  173. if (bufferIndexStart < bufferIndexEnd)
  174. {
  175. readBufferSection (sectionToReadStart,
  176. (int) (sectionToReadEnd - sectionToReadStart),
  177. bufferIndexStart);
  178. }
  179. else
  180. {
  181. const int initialSize = buffer.getNumSamples() - bufferIndexStart;
  182. readBufferSection (sectionToReadStart,
  183. initialSize,
  184. bufferIndexStart);
  185. readBufferSection (sectionToReadStart + initialSize,
  186. (int) (sectionToReadEnd - sectionToReadStart) - initialSize,
  187. 0);
  188. }
  189. const ScopedLock sl2 (bufferStartPosLock);
  190. bufferValidStart = newBVS;
  191. bufferValidEnd = newBVE;
  192. return true;
  193. }
  194. else
  195. {
  196. return false;
  197. }
  198. }
  199. void BufferingAudioSource::readBufferSection (const int64 start, const int length, const int bufferOffset)
  200. {
  201. if (source->getNextReadPosition() != start)
  202. source->setNextReadPosition (start);
  203. AudioSourceChannelInfo info (&buffer, bufferOffset, length);
  204. source->getNextAudioBlock (info);
  205. }
  206. int BufferingAudioSource::useTimeSlice()
  207. {
  208. return readNextBufferChunk() ? 1 : 100;
  209. }