sfxVoice.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "sfx/sfxVoice.h"
  23. #include "sfx/sfxBuffer.h"
  24. #include "sfx/sfxInternal.h"
  25. #include "console/console.h"
  26. // [rene, 07-May-11] The interplay between SFXBuffer and SFXVoice here isn't good.
  27. // Too complex, and while it works reliably in most cases, when doing seeks
  28. // on streaming sources, it is prone to subtle timing dependencies.
  29. //#define DEBUG_SPEW
  30. Signal< void( SFXVoice* voice ) > SFXVoice::smVoiceCreatedSignal;
  31. Signal< void( SFXVoice* voice ) > SFXVoice::smVoiceDestroyedSignal;
  32. //-----------------------------------------------------------------------------
  33. SFXVoice::SFXVoice( SFXBuffer* buffer )
  34. : mStatus( SFXStatusNull ),
  35. mBuffer( buffer ),
  36. mOffset( 0 )
  37. {
  38. }
  39. //-----------------------------------------------------------------------------
  40. SFXVoice::~SFXVoice()
  41. {
  42. smVoiceDestroyedSignal.trigger( this );
  43. if( mBuffer )
  44. mBuffer->mOnStatusChange.remove( this, &SFXVoice::_onBufferStatusChange );
  45. }
  46. //-----------------------------------------------------------------------------
  47. void SFXVoice::_attachToBuffer()
  48. {
  49. using namespace SFXInternal;
  50. // If the buffer is unique, attach us as its unique voice.
  51. if( mBuffer->isUnique() )
  52. {
  53. AssertFatal( !mBuffer->mUniqueVoice,
  54. "SFXVoice::SFXVoice - streaming buffer already is assigned a voice" );
  55. mBuffer->mUniqueVoice = this;
  56. // The buffer can start its queuing now so give it a chance
  57. // to run an update.
  58. SFXInternal::TriggerUpdate();
  59. }
  60. mBuffer->mOnStatusChange.notify( this, &SFXVoice::_onBufferStatusChange );
  61. smVoiceCreatedSignal.trigger( this );
  62. }
  63. //-----------------------------------------------------------------------------
  64. void SFXVoice::_onBufferStatusChange( SFXBuffer* buffer, SFXBuffer::Status newStatus )
  65. {
  66. AssertFatal( buffer == mBuffer, "SFXVoice::_onBufferStatusChange() - got an invalid buffer" );
  67. #ifdef DEBUG_SPEW
  68. Platform::outputDebugString( "[SFXVoice] Buffer changes status to: %s",
  69. newStatus == SFXBuffer::STATUS_Null ? "STATUS_Null" :
  70. newStatus == SFXBuffer::STATUS_Loading ? "STATUS_Loading" :
  71. newStatus == SFXBuffer::STATUS_Ready ? "STATUS_Ready" :
  72. newStatus == SFXBuffer::STATUS_Blocked ? "STATUS_Blocked" :
  73. newStatus == SFXBuffer::STATUS_AtEnd ? "STATUS_AtEnd" : "unknown" );
  74. #endif
  75. // This is called concurrently!
  76. switch( newStatus )
  77. {
  78. case SFXBuffer::STATUS_Loading:
  79. // Can ignore this. Buffer simply lets us know it has started
  80. // its initial stream load.
  81. break;
  82. case SFXBuffer::STATUS_AtEnd:
  83. // Streaming voice has played to end of stream.
  84. if( dCompareAndSwap( ( U32& ) mStatus, SFXStatusPlaying, SFXStatusTransition ) )
  85. {
  86. _stop();
  87. mOffset = 0;
  88. dCompareAndSwap( ( U32& ) mStatus, SFXStatusTransition, SFXStatusStopped );
  89. }
  90. #ifdef DEBUG_SPEW
  91. Platform::outputDebugString( "[SFXVoice] Voice stopped as end of stream reached" );
  92. #endif
  93. break;
  94. case SFXBuffer::STATUS_Blocked:
  95. // Streaming has fallen behind.
  96. if( dCompareAndSwap( ( U32& ) mStatus, SFXStatusPlaying, SFXStatusTransition ) )
  97. {
  98. _pause();
  99. dCompareAndSwap( ( U32& ) mStatus, SFXStatusTransition, SFXStatusBlocked );
  100. }
  101. #ifdef DEBUG_SPEW
  102. Platform::outputDebugString( "[SFXVoice] Voice waiting for buffer to catch up" );
  103. #endif
  104. break;
  105. case SFXBuffer::STATUS_Ready:
  106. if( dCompareAndSwap( ( U32& ) mStatus, SFXStatusBlocked, SFXStatusTransition ) )
  107. {
  108. // Get the playback going again.
  109. _play();
  110. dCompareAndSwap( ( U32& ) mStatus, SFXStatusTransition, SFXStatusPlaying );
  111. #ifdef DEBUG_SPEW
  112. Platform::outputDebugString( "[SFXVoice] Buffer caught up with voice" );
  113. #endif
  114. }
  115. break;
  116. case SFXBuffer::STATUS_Null:
  117. AssertFatal( false, "SFXVoice::_onBufferStatusChange - Buffer changed to invalid NULL status" );
  118. break;
  119. }
  120. }
  121. //-----------------------------------------------------------------------------
  122. SFXStatus SFXVoice::getStatus() const
  123. {
  124. // Detect when the device has finished playback. Only for
  125. // non-streaming voices. For streaming voices, we rely on
  126. // the buffer to send us a STATUS_AtEnd signal when it is
  127. // done playing.
  128. if( mStatus == SFXStatusPlaying &&
  129. !mBuffer->isStreaming() &&
  130. _status() == SFXStatusStopped )
  131. mStatus = SFXStatusStopped;
  132. return mStatus;
  133. }
  134. //-----------------------------------------------------------------------------
  135. void SFXVoice::play( bool looping )
  136. {
  137. AssertFatal( mBuffer != NULL, "SFXVoice::play() - no buffer" );
  138. using namespace SFXInternal;
  139. // For streaming, check whether we have played previously.
  140. // If so, reset the buffer's stream.
  141. if( mBuffer->isStreaming() &&
  142. mStatus == SFXStatusStopped )
  143. _resetStream( 0 );
  144. // Now switch state.
  145. while( mStatus != SFXStatusPlaying &&
  146. mStatus != SFXStatusBlocked )
  147. {
  148. if( !mBuffer->isReady() &&
  149. ( dCompareAndSwap( ( U32& ) mStatus, SFXStatusNull, SFXStatusBlocked ) ||
  150. dCompareAndSwap( ( U32& ) mStatus, SFXStatusStopped, SFXStatusBlocked ) ||
  151. dCompareAndSwap( ( U32& ) mStatus, SFXStatusPaused, SFXStatusBlocked ) ) )
  152. {
  153. #ifdef DEBUG_SPEW
  154. Platform::outputDebugString( "[SFXVoice] Wanted to start playback but buffer isn't ready" );
  155. #endif
  156. break;
  157. }
  158. else if( dCompareAndSwap( ( U32& ) mStatus, SFXStatusNull, SFXStatusTransition ) ||
  159. dCompareAndSwap( ( U32& ) mStatus, SFXStatusStopped, SFXStatusTransition ) ||
  160. dCompareAndSwap( ( U32& ) mStatus, SFXStatusPaused, SFXStatusTransition ) )
  161. {
  162. _play();
  163. dCompareAndSwap( ( U32& ) mStatus, SFXStatusTransition, SFXStatusPlaying );
  164. #ifdef DEBUG_SPEW
  165. Platform::outputDebugString( "[SFXVoice] Started playback" );
  166. #endif
  167. break;
  168. }
  169. }
  170. }
  171. //-----------------------------------------------------------------------------
  172. void SFXVoice::pause()
  173. {
  174. while( mStatus != SFXStatusPaused &&
  175. mStatus != SFXStatusNull &&
  176. mStatus != SFXStatusStopped )
  177. {
  178. if( dCompareAndSwap( ( U32& ) mStatus, SFXStatusPlaying, SFXStatusTransition ) ||
  179. dCompareAndSwap( ( U32& ) mStatus, SFXStatusBlocked, SFXStatusTransition ) )
  180. {
  181. _pause();
  182. dCompareAndSwap( ( U32& ) mStatus, SFXStatusTransition, SFXStatusPaused );
  183. break;
  184. }
  185. }
  186. }
  187. //-----------------------------------------------------------------------------
  188. void SFXVoice::stop()
  189. {
  190. while( mStatus != SFXStatusStopped &&
  191. mStatus != SFXStatusNull )
  192. {
  193. if( dCompareAndSwap( ( U32& ) mStatus, ( U32 ) SFXStatusPlaying, ( U32 ) SFXStatusTransition ) ||
  194. dCompareAndSwap( ( U32& ) mStatus, SFXStatusPaused, SFXStatusTransition ) ||
  195. dCompareAndSwap( ( U32& ) mStatus, SFXStatusBlocked, SFXStatusTransition ) )
  196. {
  197. _stop();
  198. dCompareAndSwap( ( U32& ) mStatus, SFXStatusTransition, SFXStatusStopped );
  199. break;
  200. }
  201. }
  202. }
  203. //-----------------------------------------------------------------------------
  204. U32 SFXVoice::getPosition() const
  205. {
  206. // When stopped, always return 0.
  207. if( getStatus() == SFXStatusStopped )
  208. return 0;
  209. // It depends on the device if and when it will return a count of the total samples
  210. // played so far. With streaming buffers, all devices will do that. With non-streaming
  211. // buffers, some may do for looping voices thus returning a number that exceeds the actual
  212. // source stream size. So, clamp things into range here and also take care of any offsetting
  213. // resulting from a setPosition() call.
  214. U32 pos = _tell() + mOffset;
  215. const U32 numStreamSamples = mBuffer->getFormat().getSampleCount( mBuffer->getDuration() );
  216. if( mBuffer->mIsLooping )
  217. pos %= numStreamSamples;
  218. else if( pos > numStreamSamples )
  219. {
  220. // Ensure we never report out-of-range positions even if the device does.
  221. pos = numStreamSamples;
  222. }
  223. return pos;
  224. }
  225. //-----------------------------------------------------------------------------
  226. void SFXVoice::setPosition( U32 inSample )
  227. {
  228. // Clamp to sample range.
  229. const U32 sample = inSample % ( mBuffer->getFormat().getSampleCount( mBuffer->getDuration() ) - 1 );
  230. // Don't perform a seek when we already are at the
  231. // given position. Especially avoids a costly stream
  232. // clone when seeking on a streamed voice.
  233. if( getPosition() == sample )
  234. return;
  235. if( !mBuffer->isStreaming() )
  236. {
  237. // Non-streaming sound. Just seek in the device buffer.
  238. _seek( sample );
  239. }
  240. else
  241. {
  242. // Streaming sound. Reset the stream and playback.
  243. //
  244. // Unfortunately, the logic here is still prone to subtle timing dependencies
  245. // in relation to the buffer updates. In retrospect, I feel that solving all issues
  246. // of asynchronous operation on a per-voice/buffer level has greatly complicated
  247. // the system. It seems now that it would have been a lot simpler to have a single
  248. // asynchronous buffer/voice manager that manages the updates of all voices and buffers
  249. // currently in the system in one spot. Packet reads could still be pushed out to
  250. // the thread pool but queue updates would all be handled centrally in one spot. This
  251. // would do away with problems like those (mostly) solved by the multi-step procedure
  252. // here.
  253. // Go into transition.
  254. SFXStatus oldStatus;
  255. while( true )
  256. {
  257. oldStatus = mStatus;
  258. if( oldStatus != SFXStatusTransition &&
  259. dCompareAndSwap( ( U32& ) mStatus, oldStatus, SFXStatusTransition ) )
  260. break;
  261. }
  262. // Switch the stream.
  263. _resetStream( sample, false );
  264. // Come out of transition.
  265. SFXStatus newStatus = oldStatus;
  266. if( oldStatus == SFXStatusPlaying )
  267. newStatus = SFXStatusBlocked;
  268. dCompareAndSwap( ( U32& ) mStatus, SFXStatusTransition, newStatus );
  269. // Trigger an update.
  270. SFXInternal::TriggerUpdate();
  271. }
  272. }
  273. //-----------------------------------------------------------------------------
  274. void SFXVoice::_resetStream( U32 sampleStartPos, bool triggerUpdate )
  275. {
  276. AssertFatal( mBuffer->isStreaming(), "SFXVoice::_resetStream - Not a streaming voice!" );
  277. ThreadSafeRef< SFXBuffer::AsyncState > oldState = mBuffer->mAsyncState;
  278. AssertFatal( oldState != NULL,
  279. "SFXVoice::_resetStream() - streaming buffer must have valid async state" );
  280. #ifdef DEBUG_SPEW
  281. Platform::outputDebugString( "[SFXVoice] Resetting stream to %i", sampleStartPos );
  282. #endif
  283. // Rather than messing up the async code by adding repositioning (which
  284. // further complicates synchronizing the various parts), just construct
  285. // a complete new AsyncState and discard the old one. The only problem
  286. // here is the stateful sound streams. We can't issue a new packet as long
  287. // as we aren't sure there's no request pending, so we just clone the stream
  288. // and leave the old one to the old AsyncState.
  289. ThreadSafeRef< SFXStream > sfxStream = oldState->mStream->getSourceStream()->clone();
  290. if( sfxStream == NULL )
  291. {
  292. Con::errorf( "SFXVoice::_resetStream - could not clone SFXStream" );
  293. return;
  294. }
  295. IPositionable< U32 >* sfxPositionable = dynamic_cast< IPositionable< U32 >* >( sfxStream.ptr() );
  296. if( !sfxPositionable )
  297. {
  298. Con::errorf( "SFXVoice::_resetStream - could not seek in SFXStream" );
  299. return;
  300. }
  301. sfxPositionable->setPosition( sampleStartPos * sfxStream->getFormat().getBytesPerSample() );
  302. ThreadSafeRef< SFXInternal::SFXAsyncStream > newStream =
  303. new SFXInternal::SFXAsyncStream
  304. ( sfxStream,
  305. true,
  306. oldState->mStream->getPacketDuration() / 1000,
  307. oldState->mStream->getReadAhead(),
  308. oldState->mStream->isLooping() );
  309. newStream->setReadSilenceAtEnd( oldState->mStream->getReadSilenceAtEnd() );
  310. AssertFatal( newStream->getPacketSize() == oldState->mStream->getPacketSize(),
  311. "SFXVoice::setPosition() - packet size mismatch with new stream" );
  312. ThreadSafeRef< SFXBuffer::AsyncState > newState =
  313. new SFXBuffer::AsyncState( newStream );
  314. newStream->start();
  315. // Switch the states.
  316. mOffset = sampleStartPos;
  317. mBuffer->mAsyncState = newState;
  318. // Stop the old state from reading more data.
  319. oldState->mStream->stop();
  320. // Trigger update.
  321. if( triggerUpdate )
  322. SFXInternal::TriggerUpdate();
  323. }