sfxSound.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  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/sfxSound.h"
  23. #include "sfx/sfxDevice.h"
  24. #include "sfx/sfxVoice.h"
  25. #include "sfx/sfxSystem.h"
  26. #include "sfx/sfxBuffer.h"
  27. #include "sfx/sfxStream.h"
  28. #include "sfx/sfxDescription.h"
  29. #include "core/util/safeDelete.h"
  30. #include "console/engineAPI.h"
  31. //#define DEBUG_SPEW
  32. IMPLEMENT_CONOBJECT( SFXSound );
  33. ConsoleDocClass( SFXSound,
  34. "@brief A sound controller that directly plays a single sound file.\n\n"
  35. "When playing individual audio files, SFXSounds are implicitly created by the sound system.\n\n"
  36. "Each sound source has an associated play cursor that can be queried and explicitly positioned "
  37. "by the user. The cursor is a floating-point value measured in seconds.\n\n"
  38. "For streamed sources, playback may not be continuous in case the streaming queue is interrupted.\n\n"
  39. "@note This class cannot be instantiated directly by the user but rather is implicitly created by the sound "
  40. "system when sfxCreateSource() or sfxPlayOnce() is called on a SFXProfile instance.\n\n"
  41. "@section SFXSound_virtualization Sounds and Voices\n\n"
  42. "To actually emit an audible signal, a sound must allocate a resource on the sound device through "
  43. "which the sound data is being played back. This resource is called 'voice'.\n\n"
  44. "As with other types of resources, the availability of these resources may be restricted, i.e. a given "
  45. "sound device will usually only support a fixed number of voices that are playing at the same time. Since, "
  46. "however, there may be arbitrary many SFXSounds instantiated and playing at the same time, this needs to be "
  47. "solved. \n\n"
  48. "@see SFXDescription::priority\n"
  49. "@ingroup SFX"
  50. );
  51. //-----------------------------------------------------------------------------
  52. SFXSound::SFXSound()
  53. : mVoice( NULL )
  54. {
  55. // NOTE: This should never be used directly
  56. // and is only here to satisfy satisfy the
  57. // construction needs of IMPLEMENT_CONOBJECT.
  58. }
  59. //-----------------------------------------------------------------------------
  60. SFXSound::SFXSound( SFXProfile *profile, SFXDescription* desc )
  61. : Parent( profile, desc ),
  62. mVoice( NULL )
  63. {
  64. }
  65. //-----------------------------------------------------------------------------
  66. SFXSound* SFXSound::_create( SFXDevice *device, SFXProfile *profile )
  67. {
  68. AssertFatal( profile, "SFXSound::_create() - Got a null profile!" );
  69. SFXDescription* desc = profile->getDescription();
  70. if ( !desc )
  71. {
  72. Con::errorf( "SFXSound::_create() - Profile has null description!" );
  73. return NULL;
  74. }
  75. // Create the sound and register it.
  76. SFXSound* sound = new SFXSound( profile, desc );
  77. sound->registerObject();
  78. // Initialize the buffer.
  79. SFXBuffer* buffer = profile->getBuffer();
  80. if( !buffer )
  81. {
  82. sound->deleteObject();
  83. Con::errorf( "SFXSound::_create() - Could not create device buffer!" );
  84. return NULL;
  85. }
  86. sound->_setBuffer( buffer );
  87. // The sound is a console object... register it.
  88. #ifdef DEBUG_SPEW
  89. Platform::outputDebugString( "[SFXSound] new sound '%i' with profile '%i' (\"%s\")",
  90. sound->getId(), profile->getId(), profile->getName() );
  91. #endif
  92. // Hook up reloading.
  93. profile->getChangedSignal().notify( sound, &SFXSound::_onProfileChanged );
  94. return sound;
  95. }
  96. //-----------------------------------------------------------------------------
  97. SFXSound* SFXSound::_create( SFXDevice* device,
  98. const ThreadSafeRef< SFXStream >& stream,
  99. SFXDescription* description )
  100. {
  101. AssertFatal( stream.ptr() != NULL, "SFXSound::_create() - Got a null stream!" );
  102. AssertFatal( description, "SFXSound::_create() - Got a null description!" );
  103. // Create the source and register it.
  104. SFXSound* source = new SFXSound( NULL, description );
  105. source->registerObject();
  106. // Create the buffer.
  107. SFXBuffer* buffer = SFX->_createBuffer( stream, description );
  108. if( !buffer )
  109. {
  110. source->deleteObject();
  111. Con::errorf( "SFXSound::_create() - Could not create device buffer!" );
  112. return NULL;
  113. }
  114. source->_setBuffer( buffer );
  115. #ifdef DEBUG_SPEW
  116. Platform::outputDebugString( "[SFXSound] new source '%i' for stream", source->getId() );
  117. #endif
  118. return source;
  119. }
  120. //-----------------------------------------------------------------------------
  121. void SFXSound::_reloadBuffer()
  122. {
  123. SFXProfile* profile = getProfile();
  124. if( profile != NULL && _releaseVoice() )
  125. {
  126. SFXBuffer* buffer = profile->getBuffer();
  127. if( !buffer )
  128. {
  129. Con::errorf( "SFXSound::_reloadBuffer() - Could not create device buffer!" );
  130. return;
  131. }
  132. _setBuffer( buffer );
  133. if( getLastStatus() == SFXStatusPlaying )
  134. SFX->_assignVoice( this );
  135. }
  136. }
  137. //-----------------------------------------------------------------------------
  138. void SFXSound::_setBuffer( SFXBuffer* buffer )
  139. {
  140. mBuffer = buffer;
  141. // There is no telling when the device will be
  142. // destroyed and the buffers deleted.
  143. //
  144. // By caching the duration now we can allow sources
  145. // to continue virtual playback until the device
  146. // is restored.
  147. mDuration = mBuffer->getDuration();
  148. }
  149. //-----------------------------------------------------------------------------
  150. bool SFXSound::_allocVoice( SFXDevice* device )
  151. {
  152. // We shouldn't have any existing voice!
  153. AssertFatal( !mVoice, "SFXSound::_allocVoice() - Already had a voice!" );
  154. // Must not assign voice to source that isn't playing.
  155. AssertFatal( getLastStatus() == SFXStatusPlaying,
  156. "SFXSound::_allocVoice() - Source is not playing!" );
  157. // The buffer can be lost when the device is reset
  158. // or changed, so initialize it if we have to. If
  159. // that fails then we cannot create the voice.
  160. if( mBuffer.isNull() )
  161. {
  162. SFXProfile* profile = getProfile();
  163. if( profile != NULL )
  164. {
  165. SFXBuffer* buffer = profile->getBuffer();
  166. if( buffer )
  167. _setBuffer( buffer );
  168. }
  169. if( mBuffer.isNull() )
  170. return false;
  171. }
  172. // Ask the device for a voice based on this buffer.
  173. mVoice = device->createVoice( is3d(), mBuffer );
  174. if( !mVoice )
  175. return false;
  176. // Set initial properties.
  177. mVoice->setVolume( mPreAttenuatedVolume );
  178. mVoice->setPitch( mEffectivePitch );
  179. mVoice->setPriority( mEffectivePriority );
  180. if( mDescription->mRolloffFactor != -1.f )
  181. mVoice->setRolloffFactor( mDescription->mRolloffFactor );
  182. // Set 3D parameters.
  183. if( is3d() )
  184. {
  185. // Scatter the position, if requested. Do this only once so
  186. // we don't change position when resuming from virtualized
  187. // playback.
  188. if( !mTransformScattered )
  189. _scatterTransform();
  190. // Set the 3D attributes.
  191. setTransform( mTransform );
  192. setVelocity( mVelocity );
  193. _setMinMaxDistance( mMinDistance, mMaxDistance );
  194. _setCone( mConeInsideAngle, mConeOutsideAngle, mConeOutsideVolume );
  195. }
  196. // Set reverb, if enabled.
  197. if( mDescription->mUseReverb )
  198. mVoice->setReverb( mDescription->mReverb );
  199. // Update the duration... it shouldn't have changed, but
  200. // its probably better that we're accurate if it did.
  201. mDuration = mBuffer->getDuration();
  202. // If virtualized playback has been started, we transfer its position to the
  203. // voice and stop virtualization.
  204. const U32 playTime = mPlayTimer.getPosition();
  205. if( playTime > 0 )
  206. {
  207. const U32 pos = mBuffer->getFormat().getSampleCount( playTime );
  208. mVoice->setPosition( pos);
  209. }
  210. mVoice->play( isLooping() );
  211. #ifdef DEBUG_SPEW
  212. Platform::outputDebugString( "[SFXSound] allocated voice for source '%i' (pos=%i, 3d=%i, vol=%f)",
  213. getId(), playTime, is3d(), mPreAttenuatedVolume );
  214. #endif
  215. return true;
  216. }
  217. //-----------------------------------------------------------------------------
  218. void SFXSound::_onParameterEvent( SFXParameter* parameter, SFXParameterEvent event )
  219. {
  220. Parent::_onParameterEvent( parameter, event );
  221. switch( event )
  222. {
  223. case SFXParameterEvent_ValueChanged:
  224. switch( parameter->getChannel() )
  225. {
  226. case SFXChannelCursor:
  227. setPosition( parameter->getValue() * 1000.f );
  228. break;
  229. default:
  230. break;
  231. }
  232. break;
  233. default:
  234. break;
  235. }
  236. }
  237. //-----------------------------------------------------------------------------
  238. void SFXSound::onRemove()
  239. {
  240. SFXProfile* profile = getProfile();
  241. if( profile != NULL )
  242. profile->getChangedSignal().remove( this, &SFXSound::_onProfileChanged );
  243. Parent::onRemove();
  244. }
  245. //-----------------------------------------------------------------------------
  246. void SFXSound::onDeleteNotify( SimObject* object )
  247. {
  248. if( object == mDescription )
  249. {
  250. deleteObject();
  251. return;
  252. }
  253. Parent::onDeleteNotify( object );
  254. }
  255. //-----------------------------------------------------------------------------
  256. bool SFXSound::_releaseVoice()
  257. {
  258. if( !mVoice )
  259. return true;
  260. // Refuse to release a voice for a streaming buffer that
  261. // is not coming from a profile. For streaming buffers, we will
  262. // have to release the buffer, too, and without a profile we don't
  263. // know how to recreate the stream.
  264. if( isStreaming() && !mTrack )
  265. return false;
  266. // If we're currently playing, transfer our playback position
  267. // to the playtimer so we can virtualize playback while not
  268. // having a voice.
  269. SFXStatus status = getLastStatus();
  270. if( status == SFXStatusPlaying || status == SFXStatusBlocked )
  271. {
  272. // Sync up the play timer with the voice's current position to make
  273. // sure we handle any lag that's cropped up.
  274. mPlayTimer.setPosition( mVoice->getPosition() );
  275. if( status == SFXStatusBlocked )
  276. status = SFXStatusPlaying;
  277. }
  278. mVoice = NULL;
  279. // If this is a streaming source, release our buffer, too.
  280. // Otherwise the voice will stick around as it is uniquely assigned to
  281. // the buffer. When we get reassigned a voice, we will have to do
  282. // a full stream seek anyway, so it's no real loss here.
  283. if( isStreaming() )
  284. mBuffer = NULL;
  285. #ifdef DEBUG_SPEW
  286. Platform::outputDebugString( "[SFXSound] release voice for source '%i' (status: %s)",
  287. getId(), SFXStatusToString( status ) );
  288. #endif
  289. return true;
  290. }
  291. //-----------------------------------------------------------------------------
  292. void SFXSound::_play()
  293. {
  294. Parent::_play();
  295. if( mVoice )
  296. mVoice->play( isLooping() );
  297. else
  298. {
  299. // To ensure the fastest possible reaction
  300. // to this playback let the system reassign
  301. // voices immediately.
  302. SFX->_assignVoice( this );
  303. // If we did not get assigned a voice, we'll be
  304. // running virtualized.
  305. #ifdef DEBUG_SPEW
  306. if( !mVoice )
  307. Platform::outputDebugString( "[SFXSound] virtualizing playback of source '%i'", getId() );
  308. #endif
  309. }
  310. }
  311. //-----------------------------------------------------------------------------
  312. void SFXSound::_stop()
  313. {
  314. Parent::_stop();
  315. if( mVoice )
  316. mVoice->stop();
  317. }
  318. //-----------------------------------------------------------------------------
  319. void SFXSound::_pause()
  320. {
  321. Parent::_pause();
  322. if( mVoice )
  323. mVoice->pause();
  324. }
  325. //-----------------------------------------------------------------------------
  326. void SFXSound::_updateStatus()
  327. {
  328. // If we have a voice, use its status.
  329. if( mVoice )
  330. {
  331. SFXStatus voiceStatus = mVoice->getStatus();
  332. // Filter out SFXStatusBlocked.
  333. if( voiceStatus == SFXStatusBlocked )
  334. _setStatus( SFXStatusPlaying );
  335. else
  336. _setStatus( voiceStatus );
  337. return;
  338. }
  339. // If we're not in a playing state or we're a looping
  340. // sound then we don't need to calculate the status.
  341. if( isLooping() || mStatus != SFXStatusPlaying )
  342. return;
  343. // If we're playing and don't have a voice we
  344. // need to decide if the sound is done playing
  345. // to ensure proper virtualization of the sound.
  346. if( mPlayTimer.getPosition() > mDuration )
  347. {
  348. _stop();
  349. _setStatus( SFXStatusStopped );
  350. }
  351. }
  352. //-----------------------------------------------------------------------------
  353. void SFXSound::_updateVolume( const MatrixF& listener )
  354. {
  355. F32 oldPreAttenuatedVolume = mPreAttenuatedVolume;
  356. Parent::_updateVolume( listener );
  357. // If we have a voice and the pre-attenuated volume has
  358. // changed, pass it on to the voice. Attenuation itself will
  359. // happen on the device.
  360. if( mVoice != NULL && oldPreAttenuatedVolume != mPreAttenuatedVolume )
  361. mVoice->setVolume( mPreAttenuatedVolume );
  362. }
  363. //-----------------------------------------------------------------------------
  364. void SFXSound::_updatePitch()
  365. {
  366. F32 oldEffectivePitch = mEffectivePitch;
  367. Parent::_updatePitch();
  368. if( mVoice != NULL && oldEffectivePitch != mEffectivePitch )
  369. mVoice->setPitch( mEffectivePitch );
  370. }
  371. //-----------------------------------------------------------------------------
  372. void SFXSound::_updatePriority()
  373. {
  374. F32 oldEffectivePriority = mEffectivePriority;
  375. Parent::_updatePriority();
  376. if( mVoice != NULL && oldEffectivePriority != mEffectivePriority )
  377. mVoice->setPriority( mEffectivePriority );
  378. }
  379. //-----------------------------------------------------------------------------
  380. U32 SFXSound::getPosition() const
  381. {
  382. if( mVoice )
  383. return mVoice->getFormat().getDuration( mVoice->getPosition() );
  384. else
  385. return ( mPlayTimer.getPosition() % mDuration ); // Clamp for looped sounds.
  386. }
  387. //-----------------------------------------------------------------------------
  388. void SFXSound::setPosition( U32 ms )
  389. {
  390. AssertFatal( ms < getDuration(), "SFXSound::setPosition() - position out of range" );
  391. if( mVoice )
  392. mVoice->setPosition( mVoice->getFormat().getSampleCount( ms ) );
  393. else
  394. mPlayTimer.setPosition( ms );
  395. }
  396. //-----------------------------------------------------------------------------
  397. void SFXSound::setVelocity( const VectorF& velocity )
  398. {
  399. Parent::setVelocity( velocity );
  400. if( mVoice && is3d() )
  401. mVoice->setVelocity( velocity );
  402. }
  403. //-----------------------------------------------------------------------------
  404. void SFXSound::setTransform( const MatrixF& transform )
  405. {
  406. Parent::setTransform( transform );
  407. if( mVoice && is3d() )
  408. mVoice->setTransform( mTransform );
  409. }
  410. //-----------------------------------------------------------------------------
  411. void SFXSound::_setMinMaxDistance( F32 min, F32 max )
  412. {
  413. Parent::_setMinMaxDistance( min, max );
  414. if( mVoice && is3d() )
  415. mVoice->setMinMaxDistance( mMinDistance, mMaxDistance );
  416. }
  417. //-----------------------------------------------------------------------------
  418. void SFXSound::_setCone( F32 innerAngle,
  419. F32 outerAngle,
  420. F32 outerVolume )
  421. {
  422. Parent::_setCone( innerAngle, outerAngle, outerVolume );
  423. if( mVoice && is3d() )
  424. mVoice->setCone( mConeInsideAngle,
  425. mConeOutsideAngle,
  426. mConeOutsideVolume );
  427. }
  428. //-----------------------------------------------------------------------------
  429. bool SFXSound::isReady() const
  430. {
  431. return ( mBuffer != NULL && mBuffer->isReady() );
  432. }
  433. //-----------------------------------------------------------------------------
  434. bool SFXSound::isVirtualized() const
  435. {
  436. return ( ( mVoice == NULL && isPlaying() ) ||
  437. ( mVoice != NULL && mVoice->isVirtual() ) );
  438. }
  439. //-----------------------------------------------------------------------------
  440. SFXProfile* SFXSound::getProfile() const
  441. {
  442. return dynamic_cast< SFXProfile* >( mTrack.getPointer() );
  443. }
  444. //-----------------------------------------------------------------------------
  445. F32 SFXSound::getElapsedPlayTimeCurrentCycle() const
  446. {
  447. return F32( getPosition() ) / 1000.f;
  448. }
  449. //-----------------------------------------------------------------------------
  450. F32 SFXSound::getTotalPlayTime() const
  451. {
  452. return F32( mDuration ) / 1000.f;
  453. }
  454. //-----------------------------------------------------------------------------
  455. // Let the user define a priority value for each channel
  456. // in script. We assign it in the system init and use
  457. // it when doleing out hardware handles.
  458. S32 QSORT_CALLBACK SFXSound::qsortCompare( const void* item1, const void* item2 )
  459. {
  460. const SFXSound* source1 = *( ( SFXSound** ) item1 );
  461. const SFXSound* source2 = *( ( SFXSound** ) item2 );
  462. // Sounds that are playing are always sorted
  463. // closer than non-playing sounds.
  464. const bool source1IsPlaying = source1->isPlaying();
  465. const bool source2IsPlaying = source2->isPlaying();
  466. if( !source1IsPlaying && !source2IsPlaying )
  467. return 0;
  468. else if( !source1IsPlaying && source2IsPlaying )
  469. return 1;
  470. else if( source1IsPlaying && !source2IsPlaying )
  471. return -1;
  472. // Louder attenuated volumes take precedence but adjust them
  473. // by priority so that less audible sounds with higher priority
  474. // become more important.
  475. F32 volume1 = source1->getAttenuatedVolume();
  476. F32 volume2 = source2->getAttenuatedVolume();
  477. volume1 += volume1 * source1->mEffectivePriority;
  478. volume2 += volume2 * source2->mEffectivePriority;
  479. if( volume1 < volume2 )
  480. return 1;
  481. if( volume1 > volume2 )
  482. return -1;
  483. // If we got this far then the source that was
  484. // played last has the higher priority.
  485. if( source1->mPlayStartTick > source2->mPlayStartTick )
  486. return -1;
  487. if( source1->mPlayStartTick < source2->mPlayStartTick )
  488. return 1;
  489. // These are sorted the same!
  490. return 0;
  491. }
  492. //=============================================================================
  493. // Console Methods.
  494. //=============================================================================
  495. // MARK: ---- Console Methods ----
  496. //-----------------------------------------------------------------------------
  497. DefineEngineMethod( SFXSound, isReady, bool, (),,
  498. "Test whether the sound data associated with the sound has been fully loaded and is ready for playback.\n"
  499. "For streamed sounds, this will be false during playback when the stream queue for the sound is starved and "
  500. "waiting for data. For buffered sounds, only an initial loading phase will potentially cause isReady to "
  501. "return false.\n\n"
  502. "@return True if the sound is ready for playback." )
  503. {
  504. return object->isReady();
  505. }
  506. //-----------------------------------------------------------------------------
  507. DefineEngineMethod( SFXSound, getPosition, F32, (),,
  508. "Get the current playback position in seconds.\n"
  509. "@return The current play cursor offset." )
  510. {
  511. return F32( object->getPosition() ) * 0.001f;
  512. }
  513. //-----------------------------------------------------------------------------
  514. DefineEngineMethod( SFXSound, setPosition, void, ( F32 position ),,
  515. "Set the current playback position in seconds.\n"
  516. "If the source is currently playing, playback will jump to the new position. If playback is stopped or paused, "
  517. "playback will resume at the given position when play() is called.\n\n"
  518. "@param position The new position of the play cursor (in seconds).\n" )
  519. {
  520. if( position >= 0 && position <= object->getDuration() )
  521. object->setPosition( position * 1000.0f );
  522. }
  523. //-----------------------------------------------------------------------------
  524. DefineEngineMethod( SFXSound, getDuration, F32, (),,
  525. "Get the total play time (in seconds) of the sound data attached to the sound.\n"
  526. "@return \n\n"
  527. "@note Be aware that for looped sounds, this will not return the total playback time of the sound.\n" )
  528. {
  529. return F32( object->getDuration() ) * 0.001f;
  530. }