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.

263 lines
9.1KB

  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. }
  121. }
  122. int64 BufferingAudioSource::getNextReadPosition() const
  123. {
  124. jassert (source->getTotalLength() > 0);
  125. return (source->isLooping() && nextPlayPos > 0)
  126. ? nextPlayPos % source->getTotalLength()
  127. : nextPlayPos;
  128. }
  129. void BufferingAudioSource::setNextReadPosition (int64 newPosition)
  130. {
  131. const ScopedLock sl (bufferStartPosLock);
  132. nextPlayPos = newPosition;
  133. backgroundThread.moveToFrontOfQueue (this);
  134. }
  135. bool BufferingAudioSource::readNextBufferChunk()
  136. {
  137. int64 newBVS, newBVE, sectionToReadStart, sectionToReadEnd;
  138. {
  139. const ScopedLock sl (bufferStartPosLock);
  140. if (wasSourceLooping != isLooping())
  141. {
  142. wasSourceLooping = isLooping();
  143. bufferValidStart = 0;
  144. bufferValidEnd = 0;
  145. }
  146. newBVS = jmax ((int64) 0, nextPlayPos);
  147. newBVE = newBVS + buffer.getNumSamples() - 4;
  148. sectionToReadStart = 0;
  149. sectionToReadEnd = 0;
  150. const int maxChunkSize = 2048;
  151. if (newBVS < bufferValidStart || newBVS >= bufferValidEnd)
  152. {
  153. newBVE = jmin (newBVE, newBVS + maxChunkSize);
  154. sectionToReadStart = newBVS;
  155. sectionToReadEnd = newBVE;
  156. bufferValidStart = 0;
  157. bufferValidEnd = 0;
  158. }
  159. else if (std::abs ((int) (newBVS - bufferValidStart)) > 512
  160. || std::abs ((int) (newBVE - bufferValidEnd)) > 512)
  161. {
  162. newBVE = jmin (newBVE, bufferValidEnd + maxChunkSize);
  163. sectionToReadStart = bufferValidEnd;
  164. sectionToReadEnd = newBVE;
  165. bufferValidStart = newBVS;
  166. bufferValidEnd = jmin (bufferValidEnd, newBVE);
  167. }
  168. }
  169. if (sectionToReadStart != sectionToReadEnd)
  170. {
  171. jassert (buffer.getNumSamples() > 0);
  172. const int bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples());
  173. const int bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples());
  174. if (bufferIndexStart < bufferIndexEnd)
  175. {
  176. readBufferSection (sectionToReadStart,
  177. (int) (sectionToReadEnd - sectionToReadStart),
  178. bufferIndexStart);
  179. }
  180. else
  181. {
  182. const int initialSize = buffer.getNumSamples() - bufferIndexStart;
  183. readBufferSection (sectionToReadStart,
  184. initialSize,
  185. bufferIndexStart);
  186. readBufferSection (sectionToReadStart + initialSize,
  187. (int) (sectionToReadEnd - sectionToReadStart) - initialSize,
  188. 0);
  189. }
  190. const ScopedLock sl2 (bufferStartPosLock);
  191. bufferValidStart = newBVS;
  192. bufferValidEnd = newBVE;
  193. return true;
  194. }
  195. else
  196. {
  197. return false;
  198. }
  199. }
  200. void BufferingAudioSource::readBufferSection (const int64 start, const int length, const int bufferOffset)
  201. {
  202. if (source->getNextReadPosition() != start)
  203. source->setNextReadPosition (start);
  204. AudioSourceChannelInfo info (&buffer, bufferOffset, length);
  205. source->getNextAudioBlock (info);
  206. }
  207. int BufferingAudioSource::useTimeSlice()
  208. {
  209. return readNextBufferChunk() ? 1 : 100;
  210. }