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.

364 lines
11KB

  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_BufferingAudioSource.h"
  21. #include "../../../juce_core/threads/juce_ScopedLock.h"
  22. #include "../../../juce_core/basics/juce_Singleton.h"
  23. #include "../../../juce_core/containers/juce_VoidArray.h"
  24. #include "../../application/juce_DeletedAtShutdown.h"
  25. #include "../../events/juce_Timer.h"
  26. //==============================================================================
  27. class SharedBufferingAudioSourceThread : public DeletedAtShutdown,
  28. public Thread,
  29. private Timer
  30. {
  31. public:
  32. SharedBufferingAudioSourceThread()
  33. : Thread ("Audio Buffer"),
  34. sources (8)
  35. {
  36. }
  37. ~SharedBufferingAudioSourceThread()
  38. {
  39. stopThread (10000);
  40. clearSingletonInstance();
  41. }
  42. juce_DeclareSingleton (SharedBufferingAudioSourceThread, false)
  43. void addSource (BufferingAudioSource* source)
  44. {
  45. const ScopedLock sl (lock);
  46. if (! sources.contains ((void*) source))
  47. {
  48. sources.add ((void*) source);
  49. startThread();
  50. stopTimer();
  51. }
  52. notify();
  53. }
  54. void removeSource (BufferingAudioSource* source)
  55. {
  56. const ScopedLock sl (lock);
  57. sources.removeValue ((void*) source);
  58. if (sources.size() == 0)
  59. startTimer (5000);
  60. }
  61. private:
  62. VoidArray sources;
  63. CriticalSection lock;
  64. void run()
  65. {
  66. while (! threadShouldExit())
  67. {
  68. bool busy = false;
  69. for (int i = sources.size(); --i >= 0;)
  70. {
  71. if (threadShouldExit())
  72. return;
  73. const ScopedLock sl (lock);
  74. BufferingAudioSource* const b = (BufferingAudioSource*) sources[i];
  75. if (b != 0 && b->readNextBufferChunk())
  76. busy = true;
  77. }
  78. if (! busy)
  79. wait (500);
  80. }
  81. }
  82. void timerCallback()
  83. {
  84. stopTimer();
  85. if (sources.size() == 0)
  86. deleteInstance();
  87. }
  88. SharedBufferingAudioSourceThread (const SharedBufferingAudioSourceThread&);
  89. const SharedBufferingAudioSourceThread& operator= (const SharedBufferingAudioSourceThread&);
  90. };
  91. juce_ImplementSingleton (SharedBufferingAudioSourceThread)
  92. //==============================================================================
  93. BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* source_,
  94. const bool deleteSourceWhenDeleted_,
  95. int numberOfSamplesToBuffer_)
  96. : source (source_),
  97. deleteSourceWhenDeleted (deleteSourceWhenDeleted_),
  98. numberOfSamplesToBuffer (jmax (1024, numberOfSamplesToBuffer_)),
  99. buffer (2, 0),
  100. bufferValidStart (0),
  101. bufferValidEnd (0),
  102. nextPlayPos (0),
  103. wasSourceLooping (false)
  104. {
  105. jassert (source_ != 0);
  106. jassert (numberOfSamplesToBuffer_ > 1024); // not much point using this class if you're
  107. // not using a larger buffer..
  108. }
  109. BufferingAudioSource::~BufferingAudioSource()
  110. {
  111. SharedBufferingAudioSourceThread* const thread = SharedBufferingAudioSourceThread::getInstanceWithoutCreating();
  112. if (thread != 0)
  113. thread->removeSource (this);
  114. if (deleteSourceWhenDeleted)
  115. delete source;
  116. }
  117. //==============================================================================
  118. void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate_)
  119. {
  120. source->prepareToPlay (samplesPerBlockExpected, sampleRate_);
  121. sampleRate = sampleRate_;
  122. buffer.setSize (2, jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer));
  123. buffer.clear();
  124. bufferValidStart = 0;
  125. bufferValidEnd = 0;
  126. SharedBufferingAudioSourceThread::getInstance()->addSource (this);
  127. while (bufferValidEnd - bufferValidStart < jmin (((int) sampleRate_) / 4,
  128. buffer.getNumSamples() / 2))
  129. {
  130. SharedBufferingAudioSourceThread::getInstance()->notify();
  131. Thread::sleep (5);
  132. }
  133. }
  134. void BufferingAudioSource::releaseResources()
  135. {
  136. SharedBufferingAudioSourceThread* const thread = SharedBufferingAudioSourceThread::getInstanceWithoutCreating();
  137. if (thread != 0)
  138. thread->removeSource (this);
  139. buffer.setSize (2, 0);
  140. source->releaseResources();
  141. }
  142. void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
  143. {
  144. const ScopedLock sl (bufferStartPosLock);
  145. const int validStart = jlimit (bufferValidStart, bufferValidEnd, nextPlayPos) - nextPlayPos;
  146. const int validEnd = jlimit (bufferValidStart, bufferValidEnd, nextPlayPos + info.numSamples) - nextPlayPos;
  147. if (validStart == validEnd)
  148. {
  149. // total cache miss
  150. info.clearActiveBufferRegion();
  151. }
  152. else
  153. {
  154. if (validStart > 0)
  155. info.buffer->clear (info.startSample, validStart); // partial cache miss at start
  156. if (validEnd < info.numSamples)
  157. info.buffer->clear (info.startSample + validEnd,
  158. info.numSamples - validEnd); // partial cache miss at end
  159. if (validStart < validEnd)
  160. {
  161. for (int chan = jmin (2, info.buffer->getNumChannels()); --chan >= 0;)
  162. {
  163. const int startBufferIndex = (validStart + nextPlayPos) % buffer.getNumSamples();
  164. const int endBufferIndex = (validEnd + nextPlayPos) % buffer.getNumSamples();
  165. if (startBufferIndex < endBufferIndex)
  166. {
  167. info.buffer->copyFrom (chan, info.startSample + validStart,
  168. buffer,
  169. chan, startBufferIndex,
  170. validEnd - validStart);
  171. }
  172. else
  173. {
  174. const int initialSize = buffer.getNumSamples() - startBufferIndex;
  175. info.buffer->copyFrom (chan, info.startSample + validStart,
  176. buffer,
  177. chan, startBufferIndex,
  178. initialSize);
  179. info.buffer->copyFrom (chan, info.startSample + validStart + initialSize,
  180. buffer,
  181. chan, 0,
  182. (validEnd - validStart) - initialSize);
  183. }
  184. }
  185. }
  186. nextPlayPos += info.numSamples;
  187. if (source->isLooping() && nextPlayPos > 0)
  188. nextPlayPos %= source->getTotalLength();
  189. }
  190. SharedBufferingAudioSourceThread* const thread = SharedBufferingAudioSourceThread::getInstanceWithoutCreating();
  191. if (thread != 0)
  192. thread->notify();
  193. }
  194. int BufferingAudioSource::getNextReadPosition() const
  195. {
  196. return (source->isLooping() && nextPlayPos > 0)
  197. ? nextPlayPos % source->getTotalLength()
  198. : nextPlayPos;
  199. }
  200. void BufferingAudioSource::setNextReadPosition (int newPosition)
  201. {
  202. const ScopedLock sl (bufferStartPosLock);
  203. nextPlayPos = newPosition;
  204. SharedBufferingAudioSourceThread* const thread = SharedBufferingAudioSourceThread::getInstanceWithoutCreating();
  205. if (thread != 0)
  206. thread->notify();
  207. }
  208. bool BufferingAudioSource::readNextBufferChunk()
  209. {
  210. bufferStartPosLock.enter();
  211. if (wasSourceLooping != isLooping())
  212. {
  213. wasSourceLooping = isLooping();
  214. bufferValidStart = 0;
  215. bufferValidEnd = 0;
  216. }
  217. int newBVS = jmax (0, nextPlayPos);
  218. int newBVE = newBVS + buffer.getNumSamples() - 4;
  219. int sectionToReadStart = 0;
  220. int sectionToReadEnd = 0;
  221. const int maxChunkSize = 2048;
  222. if (newBVS < bufferValidStart || newBVS >= bufferValidEnd)
  223. {
  224. newBVE = jmin (newBVE, newBVS + maxChunkSize);
  225. sectionToReadStart = newBVS;
  226. sectionToReadEnd = newBVE;
  227. bufferValidStart = 0;
  228. bufferValidEnd = 0;
  229. }
  230. else if (abs (newBVS - bufferValidStart) > 512
  231. || abs (newBVE - bufferValidEnd) > 512)
  232. {
  233. newBVE = jmin (newBVE, bufferValidEnd + maxChunkSize);
  234. sectionToReadStart = bufferValidEnd;
  235. sectionToReadEnd = newBVE;
  236. bufferValidStart = newBVS;
  237. bufferValidEnd = jmin (bufferValidEnd, newBVE);
  238. }
  239. bufferStartPosLock.exit();
  240. if (sectionToReadStart != sectionToReadEnd)
  241. {
  242. const int bufferIndexStart = sectionToReadStart % buffer.getNumSamples();
  243. const int bufferIndexEnd = sectionToReadEnd % buffer.getNumSamples();
  244. if (bufferIndexStart < bufferIndexEnd)
  245. {
  246. readBufferSection (sectionToReadStart,
  247. sectionToReadEnd - sectionToReadStart,
  248. bufferIndexStart);
  249. }
  250. else
  251. {
  252. const int initialSize = buffer.getNumSamples() - bufferIndexStart;
  253. readBufferSection (sectionToReadStart,
  254. initialSize,
  255. bufferIndexStart);
  256. readBufferSection (sectionToReadStart + initialSize,
  257. (sectionToReadEnd - sectionToReadStart) - initialSize,
  258. 0);
  259. }
  260. const ScopedLock sl2 (bufferStartPosLock);
  261. bufferValidStart = newBVS;
  262. bufferValidEnd = newBVE;
  263. return true;
  264. }
  265. else
  266. {
  267. return false;
  268. }
  269. }
  270. void BufferingAudioSource::readBufferSection (int start, int length, int bufferOffset)
  271. {
  272. if (source->getNextReadPosition() != start)
  273. source->setNextReadPosition (start);
  274. AudioSourceChannelInfo info;
  275. info.buffer = &buffer;
  276. info.startSample = bufferOffset;
  277. info.numSamples = length;
  278. source->getNextAudioBlock (info);
  279. }
  280. END_JUCE_NAMESPACE