sfxDSBuffer.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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/dsound/sfxDSBuffer.h"
  23. #include "sfx/sfxStream.h"
  24. #include "sfx/sfxDescription.h"
  25. #include "sfx/sfxInternal.h"
  26. #include "platform/async/asyncUpdate.h"
  27. #include "core/util/safeRelease.h"
  28. #include "core/util/safeCast.h"
  29. SFXDSBuffer* SFXDSBuffer::create( IDirectSound8 *dsound,
  30. const ThreadSafeRef< SFXStream >& stream,
  31. SFXDescription* description,
  32. bool useHardware )
  33. {
  34. AssertFatal( dsound, "SFXDSBuffer::create() - Got null dsound!" );
  35. AssertFatal( stream, "SFXDSBuffer::create() - Got a null stream!" );
  36. AssertFatal( description, "SFXDSBuffer::create() - Got a null description" );
  37. SFXDSBuffer* buffer = new SFXDSBuffer( dsound,
  38. stream,
  39. description,
  40. useHardware );
  41. if( !buffer->_createBuffer( &buffer->mBuffer ) )
  42. SAFE_DELETE( buffer );
  43. return buffer;
  44. }
  45. SFXDSBuffer::SFXDSBuffer( IDirectSound8* dsound,
  46. const ThreadSafeRef< SFXStream >& stream,
  47. SFXDescription* description,
  48. bool useHardware )
  49. : Parent( stream, description ),
  50. mDSound( dsound ),
  51. mIs3d( description->mIs3D ),
  52. mUseHardware( useHardware ),
  53. mBuffer( NULL ),
  54. mDuplicate( false )
  55. {
  56. AssertFatal( mDSound, "SFXDSBuffer::SFXDSBuffer() - Got null dsound!" );
  57. mDSound->AddRef();
  58. }
  59. SFXDSBuffer::~SFXDSBuffer()
  60. {
  61. SAFE_RELEASE( mBuffer );
  62. SAFE_RELEASE( mDSound );
  63. }
  64. bool SFXDSBuffer::_createBuffer( IDirectSoundBuffer8 **buffer8 )
  65. {
  66. AssertFatal( mAsyncState != NULL,
  67. "SFXDSBuffer::_createBuffer() - Can't create buffer when not connected to stream!" );
  68. const SFXFormat& format = getFormat();
  69. // Set up WAV format structure.
  70. WAVEFORMATEX wfx;
  71. dMemset( &wfx, 0, sizeof( WAVEFORMATEX ) );
  72. wfx.wFormatTag = WAVE_FORMAT_PCM;
  73. wfx.nChannels = format.getChannels();
  74. wfx.nSamplesPerSec = format.getSamplesPerSecond();
  75. wfx.wBitsPerSample = format.getBitsPerChannel();
  76. wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8;
  77. wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
  78. // Set up DSBUFFERDESC structure.
  79. DSBUFFERDESC dsbdesc;
  80. dMemset( &dsbdesc, 0, sizeof( DSBUFFERDESC ) );
  81. dsbdesc.dwSize = sizeof( DSBUFFERDESC );
  82. dsbdesc.dwFlags =
  83. ( mIs3d ? DSBCAPS_CTRL3D | DSBCAPS_MUTE3DATMAXDISTANCE : DSBCAPS_CTRLPAN ) |
  84. ( isStreaming() ? DSBCAPS_CTRLPOSITIONNOTIFY : 0 ) |
  85. DSBCAPS_CTRLFREQUENCY |
  86. DSBCAPS_CTRLVOLUME |
  87. DSBCAPS_GETCURRENTPOSITION2 |
  88. DSBCAPS_GLOBALFOCUS |
  89. DSBCAPS_STATIC |
  90. ( mUseHardware ? DSBCAPS_LOCHARDWARE : DSBCAPS_LOCSOFTWARE );
  91. dsbdesc.dwBufferBytes = mBufferSize;
  92. if ( mIs3d )
  93. dsbdesc.guid3DAlgorithm = DS3DALG_HRTF_FULL;
  94. dsbdesc.lpwfxFormat = &wfx;
  95. // Create the buffer.
  96. IDirectSoundBuffer *buffer = NULL;
  97. HRESULT hr = mDSound->CreateSoundBuffer( &dsbdesc, &buffer, NULL );
  98. if ( FAILED( hr ) || !buffer )
  99. return false;
  100. // Grab the version 8 interface.
  101. IDirectSoundBuffer8* buffer8Ptr;
  102. hr = buffer->QueryInterface( IID_IDirectSoundBuffer8, ( LPVOID* ) &buffer8Ptr );
  103. // Release the original interface.
  104. buffer->Release();
  105. // If we failed to get the 8 interface... exit.
  106. if ( FAILED( hr ) || !buffer8Ptr )
  107. return false;
  108. // Set up notification positions, if this is a streaming buffer.
  109. if( isStreaming() )
  110. {
  111. using namespace SFXInternal;
  112. const U32 maxQueuedPackets = SFXAsyncQueue::DEFAULT_STREAM_QUEUE_LENGTH;
  113. const U32 packetSize = mAsyncState->mStream->getPacketSize();
  114. LPDIRECTSOUNDNOTIFY8 lpDsNotify;
  115. if( FAILED( hr = buffer8Ptr->QueryInterface( IID_IDirectSoundNotify8, ( LPVOID* ) &lpDsNotify ) ) )
  116. {
  117. SAFE_RELEASE( buffer8Ptr );
  118. return false;
  119. }
  120. const U32 numNotifies = maxQueuedPackets + 1; // Add one for end-of-stream notification pos.
  121. DSBPOSITIONNOTIFY* dsbNotifyPos =
  122. ( DSBPOSITIONNOTIFY* ) _alloca( numNotifies * sizeof( DSBPOSITIONNOTIFY ) );
  123. // Events seem to be triggered way too early by DS causing the playback queues to
  124. // reject updates, so we nudge the update markers "somewhat" to the right here.
  125. // This value here is based on experimentation. No harm should result if we don't
  126. // hit it other than updates happening in sub-optimal timing.
  127. enum { OFFSET_DELTA = 5000 };
  128. U32 offset = ( packetSize + OFFSET_DELTA ) % mBufferSize;
  129. HANDLE updateEvent = ( HANDLE ) UPDATE_THREAD()->getUpdateEvent();
  130. for( U32 i = 0; i < maxQueuedPackets; ++ i, offset = ( offset + packetSize ) % mBufferSize )
  131. {
  132. dsbNotifyPos[ i ].dwOffset = offset;
  133. dsbNotifyPos[ i ].hEventNotify = updateEvent;
  134. }
  135. // A end-of-stream notification position.
  136. //FIXME: this position will start to be wrong when doing stream seeks
  137. dsbNotifyPos[ numNotifies - 1 ].dwOffset = ( format.getDataLength( getDuration() ) + OFFSET_DELTA ) % mBufferSize;
  138. dsbNotifyPos[ numNotifies - 1 ].hEventNotify = updateEvent;
  139. // Install the notifications.
  140. lpDsNotify->SetNotificationPositions( numNotifies, dsbNotifyPos );
  141. SAFE_RELEASE( lpDsNotify );
  142. // Don't need to notify on stop as when playback is stopped,
  143. // the packet buffers will just fill up and stop updating
  144. // when saturated.
  145. }
  146. *buffer8 = buffer8Ptr;
  147. return true;
  148. }
  149. bool SFXDSBuffer::_copyData( U32 offset,
  150. const U8 *data,
  151. U32 length )
  152. {
  153. AssertFatal( mBuffer, "SFXDSBuffer::_copyData() - no buffer" );
  154. // Fill the buffer with the resource data.
  155. VOID* lpvWrite;
  156. DWORD dwLength;
  157. VOID* lpvWrite2;
  158. DWORD dwLength2;
  159. HRESULT hr = mBuffer->Lock(
  160. offset, // Offset at which to start lock.
  161. length, // Size of lock.
  162. &lpvWrite, // Gets address of first part of lock.
  163. &dwLength, // Gets size of first part of lock.
  164. &lpvWrite2, // Address of wraparound not needed.
  165. &dwLength2, // Size of wraparound not needed.
  166. 0 );
  167. if ( FAILED( hr ) )
  168. return false;
  169. // Copy the first part.
  170. dMemcpy( lpvWrite, data, dwLength );
  171. // Do we have a wrap?
  172. if ( lpvWrite2 )
  173. dMemcpy( lpvWrite2, data + dwLength, dwLength2 );
  174. // And finally, unlock.
  175. hr = mBuffer->Unlock(
  176. lpvWrite, // Address of lock start.
  177. dwLength, // Size of lock.
  178. lpvWrite2, // No wraparound portion.
  179. dwLength2 ); // No wraparound size.
  180. // Return success code.
  181. return SUCCEEDED(hr);
  182. }
  183. void SFXDSBuffer::_flush()
  184. {
  185. AssertFatal( isStreaming(), "SFXDSBuffer::_flush() - not a streaming buffer" );
  186. AssertFatal( SFXInternal::isSFXThread(), "SFXDSBuffer::_flush() - not on SFX thread" );
  187. Parent::_flush();
  188. mBuffer->SetCurrentPosition( 0 );
  189. }
  190. bool SFXDSBuffer::_duplicateBuffer( IDirectSoundBuffer8 **buffer8 )
  191. {
  192. AssertFatal( mBuffer, "SFXDSBuffer::_duplicateBuffer() - Duplicate buffer is null!" );
  193. // If this is the first duplicate then
  194. // give the caller our original buffer.
  195. if ( !mDuplicate )
  196. {
  197. mDuplicate = true;
  198. *buffer8 = mBuffer;
  199. (*buffer8)->AddRef();
  200. return true;
  201. }
  202. IDirectSoundBuffer *buffer1 = NULL;
  203. HRESULT hr = mDSound->DuplicateSoundBuffer( mBuffer, &buffer1 );
  204. if ( FAILED( hr ) || !buffer1 )
  205. return false;
  206. // Grab the version 8 interface.
  207. hr = buffer1->QueryInterface( IID_IDirectSoundBuffer8, (LPVOID*)buffer8 );
  208. // Release the original interface.
  209. buffer1->Release();
  210. return SUCCEEDED( hr ) && (*buffer8);
  211. }
  212. bool SFXDSBuffer::createVoice( IDirectSoundBuffer8 **buffer8 )
  213. {
  214. return ( mBuffer && _duplicateBuffer( buffer8 ) && *buffer8 );
  215. }
  216. void SFXDSBuffer::releaseVoice( IDirectSoundBuffer8 **buffer )
  217. {
  218. AssertFatal( *buffer, "SFXDSBuffer::releaseVoice() - Got null buffer!" );
  219. if ( *buffer == mBuffer )
  220. {
  221. mDuplicate = false;
  222. (*buffer)->Stop();
  223. }
  224. SAFE_RELEASE( (*buffer) );
  225. }