Torque3D Documentation / _generateds / sfxDSBuffer.cpp

sfxDSBuffer.cpp

Engine/source/sfx/dsound/sfxDSBuffer.cpp

More...

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