sfxDSBuffer.cpp
Engine/source/sfx/dsound/sfxDSBuffer.cpp
Detailed Description
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "sfx/dsound/sfxDSBuffer.h" 25#include "sfx/sfxStream.h" 26#include "sfx/sfxDescription.h" 27#include "sfx/sfxInternal.h" 28#include "platform/async/asyncUpdate.h" 29#include "core/util/safeRelease.h" 30#include "core/util/safeCast.h" 31 32 33SFXDSBuffer* SFXDSBuffer::create( IDirectSound8 *dsound, 34 const ThreadSafeRef< SFXStream>& stream, 35 SFXDescription* description, 36 bool useHardware ) 37{ 38 AssertFatal( dsound, "SFXDSBuffer::create() - Got null dsound!" ); 39 AssertFatal( stream, "SFXDSBuffer::create() - Got a null stream!" ); 40 AssertFatal( description, "SFXDSBuffer::create() - Got a null description" ); 41 42 SFXDSBuffer* buffer = new SFXDSBuffer( dsound, 43 stream, 44 description, 45 useHardware ); 46 47 48 if( !buffer->_createBuffer( &buffer->mBuffer ) ) 49 SAFE_DELETE( buffer ); 50 51 return buffer; 52} 53 54SFXDSBuffer::SFXDSBuffer( IDirectSound8* dsound, 55 const ThreadSafeRef< SFXStream>& stream, 56 SFXDescription* description, 57 bool useHardware ) 58 : Parent( stream, description ), 59 mDSound( dsound ), 60 mIs3d( description->mIs3D ), 61 mUseHardware( useHardware ), 62 mBuffer( NULL ), 63 mDuplicate( false ) 64{ 65 AssertFatal( mDSound, "SFXDSBuffer::SFXDSBuffer() - Got null dsound!" ); 66 67 mDSound->AddRef(); 68} 69 70SFXDSBuffer::~SFXDSBuffer() 71{ 72 SAFE_RELEASE( mBuffer ); 73 SAFE_RELEASE( mDSound ); 74} 75 76bool SFXDSBuffer::_createBuffer( IDirectSoundBuffer8 **buffer8 ) 77{ 78 AssertFatal( mAsyncState != NULL, 79 "SFXDSBuffer::_createBuffer() - Can't create buffer when not connected to stream!" ); 80 81 const SFXFormat& format = getFormat(); 82 83 // Set up WAV format structure. 84 WAVEFORMATEX wfx; 85 dMemset( &wfx, 0, sizeof( WAVEFORMATEX ) ); 86 wfx.wFormatTag = WAVE_FORMAT_PCM; 87 wfx.nChannels = format.getChannels(); 88 wfx.nSamplesPerSec = format.getSamplesPerSecond(); 89 wfx.wBitsPerSample = format.getBitsPerChannel(); 90 wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8; 91 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; 92 93 // Set up DSBUFFERDESC structure. 94 DSBUFFERDESC dsbdesc; 95 dMemset( &dsbdesc, 0, sizeof( DSBUFFERDESC ) ); 96 dsbdesc.dwSize = sizeof( DSBUFFERDESC ); 97 dsbdesc.dwFlags = 98 ( mIs3d ? DSBCAPS_CTRL3D | DSBCAPS_MUTE3DATMAXDISTANCE : DSBCAPS_CTRLPAN ) | 99 ( isStreaming() ? DSBCAPS_CTRLPOSITIONNOTIFY : 0 ) | 100 DSBCAPS_CTRLFREQUENCY | 101 DSBCAPS_CTRLVOLUME | 102 DSBCAPS_GETCURRENTPOSITION2 | 103 DSBCAPS_GLOBALFOCUS | 104 DSBCAPS_STATIC | 105 ( mUseHardware ? DSBCAPS_LOCHARDWARE : DSBCAPS_LOCSOFTWARE ); 106 dsbdesc.dwBufferBytes = mBufferSize; 107 if ( mIs3d ) 108 dsbdesc.guid3DAlgorithm = DS3DALG_HRTF_FULL; 109 dsbdesc.lpwfxFormat = &wfx; 110 111 // Create the buffer. 112 IDirectSoundBuffer *buffer = NULL; 113 HRESULT hr = mDSound->CreateSoundBuffer( &dsbdesc, &buffer, NULL ); 114 if ( FAILED( hr ) || !buffer ) 115 return false; 116 117 // Grab the version 8 interface. 118 IDirectSoundBuffer8* buffer8Ptr; 119 hr = buffer->QueryInterface( IID_IDirectSoundBuffer8, ( LPVOID* ) &buffer8Ptr ); 120 121 // Release the original interface. 122 buffer->Release(); 123 124 // If we failed to get the 8 interface... exit. 125 if ( FAILED( hr ) || !buffer8Ptr ) 126 return false; 127 128 // Set up notification positions, if this is a streaming buffer. 129 130 if( isStreaming() ) 131 { 132 using namespace SFXInternal; 133 134 const U32 maxQueuedPackets = SFXAsyncQueue::DEFAULT_STREAM_QUEUE_LENGTH; 135 const U32 packetSize = mAsyncState->mStream->getPacketSize(); 136 137 LPDIRECTSOUNDNOTIFY8 lpDsNotify; 138 if( FAILED( hr = buffer8Ptr->QueryInterface( IID_IDirectSoundNotify8, ( LPVOID* ) &lpDsNotify ) ) ) 139 { 140 SAFE_RELEASE( buffer8Ptr ); 141 return false; 142 } 143 144 const U32 numNotifies = maxQueuedPackets + 1; // Add one for end-of-stream notification pos. 145 DSBPOSITIONNOTIFY* dsbNotifyPos = 146 ( DSBPOSITIONNOTIFY* ) _alloca( numNotifies * sizeof( DSBPOSITIONNOTIFY ) ); 147 148 // Events seem to be triggered way too early by DS causing the playback queues to 149 // reject updates, so we nudge the update markers "somewhat" to the right here. 150 // This value here is based on experimentation. No harm should result if we don't 151 // hit it other than updates happening in sub-optimal timing. 152 enum { OFFSET_DELTA = 5000 }; 153 154 U32 offset = ( packetSize + OFFSET_DELTA ) % mBufferSize; 155 HANDLE updateEvent = ( HANDLE ) UPDATE_THREAD()->getUpdateEvent(); 156 for( U32 i = 0; i < maxQueuedPackets; ++ i, offset = ( offset + packetSize ) % mBufferSize ) 157 { 158 dsbNotifyPos[ i ].dwOffset = offset; 159 dsbNotifyPos[ i ].hEventNotify = updateEvent; 160 } 161 162 // A end-of-stream notification position. 163 164 //FIXME: this position will start to be wrong when doing stream seeks 165 166 dsbNotifyPos[ numNotifies - 1 ].dwOffset = ( format.getDataLength( getDuration() ) + OFFSET_DELTA ) % mBufferSize; 167 dsbNotifyPos[ numNotifies - 1 ].hEventNotify = updateEvent; 168 169 // Install the notifications. 170 171 lpDsNotify->SetNotificationPositions( numNotifies, dsbNotifyPos ); 172 SAFE_RELEASE( lpDsNotify ); 173 174 // Don't need to notify on stop as when playback is stopped, 175 // the packet buffers will just fill up and stop updating 176 // when saturated. 177 } 178 179 *buffer8 = buffer8Ptr; 180 return true; 181} 182 183bool SFXDSBuffer::_copyData( U32 offset, 184 const U8 *data, 185 U32 length ) 186{ 187 AssertFatal( mBuffer, "SFXDSBuffer::_copyData() - no buffer" ); 188 189 // Fill the buffer with the resource data. 190 VOID* lpvWrite; 191 DWORD dwLength; 192 VOID* lpvWrite2; 193 DWORD dwLength2; 194 HRESULT hr = mBuffer->Lock( 195 offset, // Offset at which to start lock. 196 length, // Size of lock. 197 &lpvWrite, // Gets address of first part of lock. 198 &dwLength, // Gets size of first part of lock. 199 &lpvWrite2, // Address of wraparound not needed. 200 &dwLength2, // Size of wraparound not needed. 201 0 ); 202 if ( FAILED( hr ) ) 203 return false; 204 205 // Copy the first part. 206 dMemcpy( lpvWrite, data, dwLength ); 207 208 // Do we have a wrap? 209 if ( lpvWrite2 ) 210 dMemcpy( lpvWrite2, data + dwLength, dwLength2 ); 211 212 // And finally, unlock. 213 hr = mBuffer->Unlock( 214 lpvWrite, // Address of lock start. 215 dwLength, // Size of lock. 216 lpvWrite2, // No wraparound portion. 217 dwLength2 ); // No wraparound size. 218 219 // Return success code. 220 return SUCCEEDED(hr); 221} 222 223void SFXDSBuffer::_flush() 224{ 225 AssertFatal( isStreaming(), "SFXDSBuffer::_flush() - not a streaming buffer" ); 226 AssertFatal( SFXInternal::isSFXThread(), "SFXDSBuffer::_flush() - not on SFX thread" ); 227 228 Parent::_flush(); 229 mBuffer->SetCurrentPosition( 0 ); 230} 231 232bool SFXDSBuffer::_duplicateBuffer( IDirectSoundBuffer8 **buffer8 ) 233{ 234 AssertFatal( mBuffer, "SFXDSBuffer::_duplicateBuffer() - Duplicate buffer is null!" ); 235 236 // If this is the first duplicate then 237 // give the caller our original buffer. 238 if ( !mDuplicate ) 239 { 240 mDuplicate = true; 241 242 *buffer8 = mBuffer; 243 (*buffer8)->AddRef(); 244 245 return true; 246 } 247 248 IDirectSoundBuffer *buffer1 = NULL; 249 HRESULT hr = mDSound->DuplicateSoundBuffer( mBuffer, &buffer1 ); 250 if ( FAILED( hr ) || !buffer1 ) 251 return false; 252 253 // Grab the version 8 interface. 254 hr = buffer1->QueryInterface( IID_IDirectSoundBuffer8, (LPVOID*)buffer8 ); 255 256 // Release the original interface. 257 buffer1->Release(); 258 259 return SUCCEEDED( hr ) && (*buffer8); 260} 261 262bool SFXDSBuffer::createVoice( IDirectSoundBuffer8 **buffer8 ) 263{ 264 return ( mBuffer && _duplicateBuffer( buffer8 ) && *buffer8 ); 265} 266 267void SFXDSBuffer::releaseVoice( IDirectSoundBuffer8 **buffer ) 268{ 269 AssertFatal( *buffer, "SFXDSBuffer::releaseVoice() - Got null buffer!" ); 270 271 if ( *buffer == mBuffer ) 272 { 273 mDuplicate = false; 274 (*buffer)->Stop(); 275 } 276 277 SAFE_RELEASE( (*buffer) ); 278} 279