sfxXAudioVoice.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  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 "platform/platform.h"
  23. #include "sfx/xaudio/sfxXAudioVoice.h"
  24. #include "sfx/xaudio/sfxXAudioDevice.h"
  25. #include "sfx/xaudio/sfxXAudioBuffer.h"
  26. #include "core/util/safeDelete.h"
  27. #include "math/mMathFn.h"
  28. //#define DEBUG_SPEW
  29. static void sfxFormatToWAVEFORMATEX( const SFXFormat& format, WAVEFORMATEX *wfx )
  30. {
  31. dMemset( wfx, 0, sizeof( WAVEFORMATEX ) );
  32. wfx->wFormatTag = WAVE_FORMAT_PCM;
  33. wfx->nChannels = format.getChannels();
  34. wfx->nSamplesPerSec = format.getSamplesPerSecond();
  35. wfx->wBitsPerSample = format.getBitsPerChannel();
  36. wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8;
  37. wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
  38. }
  39. SFXXAudioVoice* SFXXAudioVoice::create( IXAudio2 *xaudio,
  40. bool is3D,
  41. SFXXAudioBuffer *buffer,
  42. SFXXAudioVoice* inVoice )
  43. {
  44. AssertFatal( xaudio, "SFXXAudioVoice::create() - Got null XAudio!" );
  45. AssertFatal( buffer, "SFXXAudioVoice::create() - Got null buffer!" );
  46. // Create the voice object first as it also the callback object.
  47. SFXXAudioVoice* voice = inVoice;
  48. if( !voice )
  49. voice = new SFXXAudioVoice( buffer );
  50. // Get the buffer format.
  51. WAVEFORMATEX wfx;
  52. sfxFormatToWAVEFORMATEX( buffer->getFormat(), &wfx );
  53. // We don't support multi-channel 3d sounds!
  54. if ( is3D && wfx.nChannels > 1 )
  55. return NULL;
  56. // Create the voice.
  57. IXAudio2SourceVoice *xaVoice;
  58. HRESULT hr = xaudio->CreateSourceVoice( &xaVoice,
  59. (WAVEFORMATEX*)&wfx,
  60. 0,
  61. XAUDIO2_DEFAULT_FREQ_RATIO,
  62. voice,
  63. NULL,
  64. NULL );
  65. if( FAILED( hr ) || !voice )
  66. {
  67. if( !inVoice )
  68. delete voice;
  69. return NULL;
  70. }
  71. voice->mIs3D = is3D;
  72. voice->mEmitter.ChannelCount = wfx.nChannels;
  73. voice->mXAudioVoice = xaVoice;
  74. return voice;
  75. }
  76. SFXXAudioVoice::SFXXAudioVoice( SFXXAudioBuffer* buffer )
  77. : Parent( buffer ),
  78. mXAudioDevice( NULL ),
  79. mXAudioVoice( NULL ),
  80. mIs3D( false ),
  81. mPitch( 1.0f ),
  82. mHasStopped( false ),
  83. mHasStarted( false ),
  84. mIsLooping( false ),
  85. mIsPlaying( false ),
  86. mNonStreamSampleStartPos( 0 ),
  87. mNonStreamBufferLoaded( false ),
  88. mSamplesPlayedOffset( 0 )
  89. {
  90. dMemset( &mEmitter, 0, sizeof( mEmitter ) );
  91. mEmitter.DopplerScaler = 1.0f;
  92. InitializeCriticalSection( &mLock );
  93. }
  94. SFXXAudioVoice::~SFXXAudioVoice()
  95. {
  96. if ( mEmitter.pVolumeCurve )
  97. {
  98. SAFE_DELETE_ARRAY( mEmitter.pVolumeCurve->pPoints );
  99. SAFE_DELETE( mEmitter.pVolumeCurve );
  100. }
  101. SAFE_DELETE( mEmitter.pCone );
  102. if ( mXAudioVoice )
  103. mXAudioVoice->DestroyVoice();
  104. DeleteCriticalSection( &mLock );
  105. }
  106. SFXStatus SFXXAudioVoice::_status() const
  107. {
  108. if( mHasStopped )
  109. return SFXStatusStopped;
  110. else if( mHasStarted )
  111. {
  112. if( !mIsPlaying )
  113. return SFXStatusPaused;
  114. else
  115. return SFXStatusPlaying;
  116. }
  117. else
  118. return SFXStatusStopped;
  119. }
  120. void SFXXAudioVoice::_flush()
  121. {
  122. AssertFatal( mXAudioVoice != NULL,
  123. "SFXXAudioVoice::_flush() - invalid voice" );
  124. EnterCriticalSection( &mLock );
  125. mXAudioVoice->Stop( 0 );
  126. mXAudioVoice->FlushSourceBuffers();
  127. mNonStreamBufferLoaded = false;
  128. #ifdef DEBUG_SPEW
  129. Platform::outputDebugString( "[SFXXAudioVoice] Flushed state" );
  130. #endif
  131. mIsPlaying = false;
  132. mHasStarted = false;
  133. mHasStopped = true;
  134. //WORKAROUND: According to the docs, SamplesPlayed reported by the
  135. // voice should get reset as soon as we submit a new buffer to the voice.
  136. // Alas it won't. So, save the current value here and offset our future
  137. // play cursors.
  138. XAUDIO2_VOICE_STATE state;
  139. mXAudioVoice->GetState( &state );
  140. mSamplesPlayedOffset = - S32( state.SamplesPlayed );
  141. LeaveCriticalSection( &mLock );
  142. }
  143. void SFXXAudioVoice::_play()
  144. {
  145. AssertFatal( mXAudioVoice != NULL,
  146. "SFXXAudioVoice::_play() - invalid voice" );
  147. // For non-streaming voices queue the data if we haven't yet.
  148. if( !mBuffer->isStreaming() && !mNonStreamBufferLoaded )
  149. _loadNonStreamed();
  150. // Start playback.
  151. mXAudioVoice->Start( 0, 0 );
  152. #ifdef DEBUG_SPEW
  153. Platform::outputDebugString( "[SFXXAudioVoice] Started playback" );
  154. #endif
  155. mIsPlaying = true;
  156. mHasStarted = true;
  157. mHasStopped = false;
  158. }
  159. void SFXXAudioVoice::_pause()
  160. {
  161. AssertFatal( mXAudioVoice != NULL,
  162. "SFXXAudioVoice::_pause() - invalid voice" );
  163. mXAudioVoice->Stop( 0 );
  164. mIsPlaying = false;
  165. #ifdef DEBUG_SPEW
  166. Platform::outputDebugString( "[SFXXAudioVoice] Paused playback" );
  167. #endif
  168. }
  169. void SFXXAudioVoice::_stop()
  170. {
  171. AssertFatal( mXAudioVoice != NULL,
  172. "SFXXAudioVoice::_stop() - invalid voice" );
  173. _flush();
  174. mIsPlaying = false;
  175. mHasStarted = false;
  176. mHasStopped = true;
  177. #ifdef DEBUG_SPEW
  178. Platform::outputDebugString( "[SFXXAudioVoice] Stopped playback" );
  179. #endif
  180. }
  181. void SFXXAudioVoice::_seek( U32 sample )
  182. {
  183. #ifdef DEBUG_SPEW
  184. Platform::outputDebugString( "[SFXXAudioVoice] Seeking to %i", sample );
  185. #endif
  186. mNonStreamSampleStartPos = sample;
  187. bool wasPlaying = mIsPlaying;
  188. _stop();
  189. if( wasPlaying )
  190. _play();
  191. }
  192. void SFXXAudioVoice::_loadNonStreamed()
  193. {
  194. AssertFatal( !mBuffer->isStreaming(), "SFXXAudioVoice::_loadNonStreamed - must not be called on streaming voices" );
  195. AssertFatal( mXAudioVoice != NULL, "SFXXAudioVoice::_loadNonStreamed - invalid voice" );
  196. AssertWarn( !mNonStreamBufferLoaded, "SFXXAudioVoice::_nonStreamNonstreamed - Data already loaded" );
  197. #ifdef DEBUG_SPEW
  198. Platform::outputDebugString( "[SFXXAudioVoice] Loading non-stream buffer at %i", mNonStreamSampleStartPos );
  199. #endif
  200. EnterCriticalSection( &mLock );
  201. const XAUDIO2_BUFFER& orgBuffer = _getBuffer()->mBufferQueue.front().mData;
  202. mNonStreamBuffer = orgBuffer;
  203. if( mNonStreamSampleStartPos )
  204. {
  205. mNonStreamBuffer.PlayBegin = mNonStreamSampleStartPos;
  206. mNonStreamBuffer.PlayLength = _getBuffer()->getNumSamples() - mNonStreamSampleStartPos;
  207. mSamplesPlayedOffset += mNonStreamSampleStartPos; // Add samples that we are skipping.
  208. mNonStreamSampleStartPos = 0;
  209. }
  210. if( mIsLooping )
  211. {
  212. mNonStreamBuffer.LoopCount = XAUDIO2_LOOP_INFINITE;
  213. mNonStreamBuffer.LoopLength = _getBuffer()->getNumSamples();
  214. }
  215. // Submit buffer.
  216. mXAudioVoice->SubmitSourceBuffer( &mNonStreamBuffer );
  217. mNonStreamBufferLoaded = true;
  218. LeaveCriticalSection( &mLock );
  219. }
  220. U32 SFXXAudioVoice::_tell() const
  221. {
  222. XAUDIO2_VOICE_STATE state;
  223. mXAudioVoice->GetState( &state );
  224. // Workaround SamplesPlayed not getting reset.
  225. return ( state.SamplesPlayed + mSamplesPlayedOffset );
  226. }
  227. void SFXXAudioVoice::setMinMaxDistance( F32 min, F32 max )
  228. {
  229. // Set the overall volume curve scale.
  230. mEmitter.CurveDistanceScaler = max;
  231. // The curve uses normalized distances, so
  232. // figure out the normalized min distance.
  233. F32 normMin = 0.0f;
  234. if ( min > 0.0f )
  235. normMin = min / max;
  236. // See what type of curve we are supposed to generate.
  237. const bool linear = ( mXAudioDevice->mDistanceModel == SFXDistanceModelLinear );
  238. // Have we setup the curve yet?
  239. if( !mEmitter.pVolumeCurve
  240. || ( linear && mEmitter.pVolumeCurve->PointCount != 2 )
  241. || ( !linear && mEmitter.pVolumeCurve->PointCount != 6 ) )
  242. {
  243. if( !mEmitter.pVolumeCurve )
  244. mEmitter.pVolumeCurve = new X3DAUDIO_DISTANCE_CURVE;
  245. else
  246. SAFE_DELETE_ARRAY( mEmitter.pVolumeCurve->pPoints );
  247. // We use 6 points for logarithmic volume curves and 2 for linear volume curves.
  248. if( linear )
  249. {
  250. mEmitter.pVolumeCurve->pPoints = new X3DAUDIO_DISTANCE_CURVE_POINT[ 2 ];
  251. mEmitter.pVolumeCurve->PointCount = 2;
  252. }
  253. else
  254. {
  255. mEmitter.pVolumeCurve->pPoints = new X3DAUDIO_DISTANCE_CURVE_POINT[ 6 ];
  256. mEmitter.pVolumeCurve->PointCount = 6;
  257. }
  258. // The first and last points are known
  259. // and will not change.
  260. mEmitter.pVolumeCurve->pPoints[ 0 ].Distance = 0.0f;
  261. mEmitter.pVolumeCurve->pPoints[ 0 ].DSPSetting = 1.0f;
  262. mEmitter.pVolumeCurve->pPoints[ linear ? 1 : 5 ].Distance = 1.0f;
  263. mEmitter.pVolumeCurve->pPoints[ linear ? 1 : 5 ].DSPSetting = 0.0f;
  264. }
  265. if( !linear )
  266. {
  267. // Set the second point of the curve.
  268. mEmitter.pVolumeCurve->pPoints[1].Distance = normMin;
  269. mEmitter.pVolumeCurve->pPoints[1].DSPSetting = 1.0f;
  270. // The next three points are calculated to
  271. // give the sound a rough logarithmic falloff.
  272. F32 distStep = ( 1.0f - normMin ) / 4.0f;
  273. for ( U32 i=0; i < 3; i++ )
  274. {
  275. U32 index = 2 + i;
  276. F32 dist = normMin + ( distStep * (F32)( i + 1 ) );
  277. mEmitter.pVolumeCurve->pPoints[index].Distance = dist;
  278. mEmitter.pVolumeCurve->pPoints[index].DSPSetting = 1.0f - log10( dist * 10.0f );
  279. }
  280. }
  281. }
  282. void SFXXAudioVoice::OnBufferEnd( void* bufferContext )
  283. {
  284. if( mBuffer->isStreaming() )
  285. SFXInternal::TriggerUpdate();
  286. }
  287. void SFXXAudioVoice::OnStreamEnd()
  288. {
  289. // Warning: This is being called within the XAudio
  290. // thread, so be sure you're thread safe!
  291. mHasStopped = true;
  292. if( mBuffer->isStreaming() )
  293. SFXInternal::TriggerUpdate();
  294. else
  295. _stop();
  296. }
  297. void SFXXAudioVoice::play( bool looping )
  298. {
  299. // Give the device a chance to calculate our positional
  300. // audio settings before we start playback... this is
  301. // important else we get glitches.
  302. if( mIs3D )
  303. mXAudioDevice->_setOutputMatrix( this );
  304. mIsLooping = looping;
  305. Parent::play( looping );
  306. }
  307. void SFXXAudioVoice::setVelocity( const VectorF& velocity )
  308. {
  309. mEmitter.Velocity.x = velocity.x;
  310. mEmitter.Velocity.y = velocity.y;
  311. // XAudio and Torque use opposite handedness, so
  312. // flip the z coord to account for that.
  313. mEmitter.Velocity.z = -velocity.z;
  314. }
  315. void SFXXAudioVoice::setTransform( const MatrixF& transform )
  316. {
  317. transform.getColumn( 3, (Point3F*)&mEmitter.Position );
  318. transform.getColumn( 1, (Point3F*)&mEmitter.OrientFront );
  319. transform.getColumn( 2, (Point3F*)&mEmitter.OrientTop );
  320. // XAudio and Torque use opposite handedness, so
  321. // flip the z coord to account for that.
  322. mEmitter.Position.z *= -1.0f;
  323. mEmitter.OrientFront.z *= -1.0f;
  324. mEmitter.OrientTop.z *= -1.0f;
  325. }
  326. void SFXXAudioVoice::setVolume( F32 volume )
  327. {
  328. mXAudioVoice->SetVolume( volume );
  329. }
  330. void SFXXAudioVoice::setPitch( F32 pitch )
  331. {
  332. mPitch = mClampF( pitch, XAUDIO2_MIN_FREQ_RATIO, XAUDIO2_DEFAULT_FREQ_RATIO );
  333. mXAudioVoice->SetFrequencyRatio( mPitch );
  334. }
  335. void SFXXAudioVoice::setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume )
  336. {
  337. // If the cone is set to 360 then the
  338. // cone is null and doesn't need to be
  339. // set on the voice.
  340. if ( mIsEqual( innerAngle, 360 ) )
  341. {
  342. SAFE_DELETE( mEmitter.pCone );
  343. return;
  344. }
  345. if ( !mEmitter.pCone )
  346. {
  347. mEmitter.pCone = new X3DAUDIO_CONE;
  348. // The inner volume is always 1... the overall
  349. // volume is what scales it.
  350. mEmitter.pCone->InnerVolume = 1.0f;
  351. // We don't use these yet.
  352. mEmitter.pCone->InnerLPF = 0.0f;
  353. mEmitter.pCone->OuterLPF = 0.0f;
  354. mEmitter.pCone->InnerReverb = 0.0f;
  355. mEmitter.pCone->OuterReverb = 0.0f;
  356. }
  357. mEmitter.pCone->InnerAngle = mDegToRad( innerAngle );
  358. mEmitter.pCone->OuterAngle = mDegToRad( outerAngle );
  359. mEmitter.pCone->OuterVolume = outerVolume;
  360. }