Engine directory for ticket #1

This commit is contained in:
DavidWyand-GG 2012-09-19 11:15:01 -04:00
parent 352279af7a
commit 7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions

View file

@ -0,0 +1,27 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
// The various functions we need to grab from DSound.dll
DS_FUNCTION( DirectSoundEnumerateA, HRESULT, (LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext) )
DS_FUNCTION( DirectSoundEnumerateW, HRESULT, (LPDSENUMCALLBACKW pDSEnumCallback, LPVOID pContext) )
DS_FUNCTION( DirectSoundCreate8, HRESULT, (LPCGUID pcGuidDevice, LPDIRECTSOUND8 *ppDS8, LPUNKNOWN pUnkOuter) )

View file

@ -0,0 +1,277 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "sfx/dsound/sfxDSBuffer.h"
#include "sfx/sfxStream.h"
#include "sfx/sfxDescription.h"
#include "sfx/sfxInternal.h"
#include "platform/async/asyncUpdate.h"
#include "core/util/safeRelease.h"
#include "core/util/safeCast.h"
SFXDSBuffer* SFXDSBuffer::create( IDirectSound8 *dsound,
const ThreadSafeRef< SFXStream >& stream,
SFXDescription* description,
bool useHardware )
{
AssertFatal( dsound, "SFXDSBuffer::create() - Got null dsound!" );
AssertFatal( stream, "SFXDSBuffer::create() - Got a null stream!" );
AssertFatal( description, "SFXDSBuffer::create() - Got a null description" );
SFXDSBuffer* buffer = new SFXDSBuffer( dsound,
stream,
description,
useHardware );
if( !buffer->_createBuffer( &buffer->mBuffer ) )
SAFE_DELETE( buffer );
return buffer;
}
SFXDSBuffer::SFXDSBuffer( IDirectSound8* dsound,
const ThreadSafeRef< SFXStream >& stream,
SFXDescription* description,
bool useHardware )
: Parent( stream, description ),
mDSound( dsound ),
mIs3d( description->mIs3D ),
mUseHardware( useHardware ),
mBuffer( NULL ),
mDuplicate( false )
{
AssertFatal( mDSound, "SFXDSBuffer::SFXDSBuffer() - Got null dsound!" );
mDSound->AddRef();
}
SFXDSBuffer::~SFXDSBuffer()
{
SAFE_RELEASE( mBuffer );
SAFE_RELEASE( mDSound );
}
bool SFXDSBuffer::_createBuffer( IDirectSoundBuffer8 **buffer8 )
{
AssertFatal( mAsyncState != NULL,
"SFXDSBuffer::_createBuffer() - Can't create buffer when not connected to stream!" );
const SFXFormat& format = getFormat();
// Set up WAV format structure.
WAVEFORMATEX wfx;
dMemset( &wfx, 0, sizeof( WAVEFORMATEX ) );
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = format.getChannels();
wfx.nSamplesPerSec = format.getSamplesPerSecond();
wfx.wBitsPerSample = format.getBitsPerChannel();
wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
// Set up DSBUFFERDESC structure.
DSBUFFERDESC dsbdesc;
dMemset( &dsbdesc, 0, sizeof( DSBUFFERDESC ) );
dsbdesc.dwSize = sizeof( DSBUFFERDESC );
dsbdesc.dwFlags =
( mIs3d ? DSBCAPS_CTRL3D | DSBCAPS_MUTE3DATMAXDISTANCE : DSBCAPS_CTRLPAN ) |
( isStreaming() ? DSBCAPS_CTRLPOSITIONNOTIFY : 0 ) |
DSBCAPS_CTRLFREQUENCY |
DSBCAPS_CTRLVOLUME |
DSBCAPS_GETCURRENTPOSITION2 |
DSBCAPS_GLOBALFOCUS |
DSBCAPS_STATIC |
( mUseHardware ? DSBCAPS_LOCHARDWARE : DSBCAPS_LOCSOFTWARE );
dsbdesc.dwBufferBytes = mBufferSize;
if ( mIs3d )
dsbdesc.guid3DAlgorithm = DS3DALG_HRTF_FULL;
dsbdesc.lpwfxFormat = &wfx;
// Create the buffer.
IDirectSoundBuffer *buffer = NULL;
HRESULT hr = mDSound->CreateSoundBuffer( &dsbdesc, &buffer, NULL );
if ( FAILED( hr ) || !buffer )
return false;
// Grab the version 8 interface.
IDirectSoundBuffer8* buffer8Ptr;
hr = buffer->QueryInterface( IID_IDirectSoundBuffer8, ( LPVOID* ) &buffer8Ptr );
// Release the original interface.
buffer->Release();
// If we failed to get the 8 interface... exit.
if ( FAILED( hr ) || !buffer8Ptr )
return false;
// Set up notification positions, if this is a streaming buffer.
if( isStreaming() )
{
using namespace SFXInternal;
const U32 maxQueuedPackets = SFXAsyncQueue::DEFAULT_STREAM_QUEUE_LENGTH;
const U32 packetSize = mAsyncState->mStream->getPacketSize();
LPDIRECTSOUNDNOTIFY8 lpDsNotify;
if( FAILED( hr = buffer8Ptr->QueryInterface( IID_IDirectSoundNotify8, ( LPVOID* ) &lpDsNotify ) ) )
{
SAFE_RELEASE( buffer8Ptr );
return false;
}
const U32 numNotifies = maxQueuedPackets + 1; // Add one for end-of-stream notification pos.
DSBPOSITIONNOTIFY* dsbNotifyPos =
( DSBPOSITIONNOTIFY* ) _alloca( numNotifies * sizeof( DSBPOSITIONNOTIFY ) );
// Events seem to be triggered way too early by DS causing the playback queues to
// reject updates, so we nudge the update markers "somewhat" to the right here.
// This value here is based on experimentation. No harm should result if we don't
// hit it other than updates happening in sub-optimal timing.
enum { OFFSET_DELTA = 5000 };
U32 offset = ( packetSize + OFFSET_DELTA ) % mBufferSize;
HANDLE updateEvent = ( HANDLE ) UPDATE_THREAD()->getUpdateEvent();
for( U32 i = 0; i < maxQueuedPackets; ++ i, offset = ( offset + packetSize ) % mBufferSize )
{
dsbNotifyPos[ i ].dwOffset = offset;
dsbNotifyPos[ i ].hEventNotify = updateEvent;
}
// A end-of-stream notification position.
//FIXME: this position will start to be wrong when doing stream seeks
dsbNotifyPos[ numNotifies - 1 ].dwOffset = ( format.getDataLength( getDuration() ) + OFFSET_DELTA ) % mBufferSize;
dsbNotifyPos[ numNotifies - 1 ].hEventNotify = updateEvent;
// Install the notifications.
lpDsNotify->SetNotificationPositions( numNotifies, dsbNotifyPos );
SAFE_RELEASE( lpDsNotify );
// Don't need to notify on stop as when playback is stopped,
// the packet buffers will just fill up and stop updating
// when saturated.
}
*buffer8 = buffer8Ptr;
return true;
}
bool SFXDSBuffer::_copyData( U32 offset,
const U8 *data,
U32 length )
{
AssertFatal( mBuffer, "SFXDSBuffer::_copyData() - no buffer" );
// Fill the buffer with the resource data.
VOID* lpvWrite;
DWORD dwLength;
VOID* lpvWrite2;
DWORD dwLength2;
HRESULT hr = mBuffer->Lock(
offset, // Offset at which to start lock.
length, // Size of lock.
&lpvWrite, // Gets address of first part of lock.
&dwLength, // Gets size of first part of lock.
&lpvWrite2, // Address of wraparound not needed.
&dwLength2, // Size of wraparound not needed.
0 );
if ( FAILED( hr ) )
return false;
// Copy the first part.
dMemcpy( lpvWrite, data, dwLength );
// Do we have a wrap?
if ( lpvWrite2 )
dMemcpy( lpvWrite2, data + dwLength, dwLength2 );
// And finally, unlock.
hr = mBuffer->Unlock(
lpvWrite, // Address of lock start.
dwLength, // Size of lock.
lpvWrite2, // No wraparound portion.
dwLength2 ); // No wraparound size.
// Return success code.
return SUCCEEDED(hr);
}
void SFXDSBuffer::_flush()
{
AssertFatal( isStreaming(), "SFXDSBuffer::_flush() - not a streaming buffer" );
AssertFatal( SFXInternal::isSFXThread(), "SFXDSBuffer::_flush() - not on SFX thread" );
Parent::_flush();
mBuffer->SetCurrentPosition( 0 );
}
bool SFXDSBuffer::_duplicateBuffer( IDirectSoundBuffer8 **buffer8 )
{
AssertFatal( mBuffer, "SFXDSBuffer::_duplicateBuffer() - Duplicate buffer is null!" );
// If this is the first duplicate then
// give the caller our original buffer.
if ( !mDuplicate )
{
mDuplicate = true;
*buffer8 = mBuffer;
(*buffer8)->AddRef();
return true;
}
IDirectSoundBuffer *buffer1 = NULL;
HRESULT hr = mDSound->DuplicateSoundBuffer( mBuffer, &buffer1 );
if ( FAILED( hr ) || !buffer1 )
return false;
// Grab the version 8 interface.
hr = buffer1->QueryInterface( IID_IDirectSoundBuffer8, (LPVOID*)buffer8 );
// Release the original interface.
buffer1->Release();
return SUCCEEDED( hr ) && (*buffer8);
}
bool SFXDSBuffer::createVoice( IDirectSoundBuffer8 **buffer8 )
{
return ( mBuffer && _duplicateBuffer( buffer8 ) && *buffer8 );
}
void SFXDSBuffer::releaseVoice( IDirectSoundBuffer8 **buffer )
{
AssertFatal( *buffer, "SFXDSBuffer::releaseVoice() - Got null buffer!" );
if ( *buffer == mBuffer )
{
mDuplicate = false;
(*buffer)->Stop();
}
SAFE_RELEASE( (*buffer) );
}

View file

@ -0,0 +1,101 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _SFXDSBUFFER_H_
#define _SFXDSBUFFER_H_
#include <dsound.h>
#ifndef _SFXINTERNAL_H_
# include "sfx/sfxInternal.h"
#endif
/// DirectSound SFXBuffer implementation.
///
/// Note that the actual sound buffer held by the buffer may
/// get duplicated around for individual voices. This is kind
/// of ugly as the resulting buffers aren't tied to a SFXDSBuffer
/// anymore.
class SFXDSBuffer : public SFXInternal::SFXWrapAroundBuffer
{
typedef SFXInternal::SFXWrapAroundBuffer Parent;
friend class SFXDSDevice;
friend class SFXDSVoice;
protected:
///
bool mIs3d;
///
bool mUseHardware;
IDirectSound8 *mDSound;
/// The buffer used when duplication is allowed.
IDirectSoundBuffer8 *mBuffer;
/// We set this to true when the original buffer has
/// been handed out and duplicates need to be made.
bool mDuplicate;
///
SFXDSBuffer( IDirectSound8 *dsound,
const ThreadSafeRef< SFXStream >& stream,
SFXDescription* description,
bool useHardware );
virtual ~SFXDSBuffer();
/// Set up a DirectSound buffer.
/// @note This method will not fill the buffer with data.
/// @note If this is a streaming buffer, the resulting buffer
/// will have its notification positions set up and already
/// be registered with SFXDSStreamThread.
bool _createBuffer( IDirectSoundBuffer8 **buffer8 );
///
bool _duplicateBuffer( IDirectSoundBuffer8 **buffer8 );
// SFXWrapAroundBuffer.
virtual bool _copyData( U32 offset, const U8* data, U32 length );
virtual void _flush();
public:
///
static SFXDSBuffer* create( IDirectSound8* dsound,
const ThreadSafeRef< SFXStream >& stream,
SFXDescription* description,
bool useHardware );
//
bool createVoice( IDirectSoundBuffer8 **buffer );
//
void releaseVoice( IDirectSoundBuffer8 **buffer );
};
#endif // _SFXDSBUFFER_H_

View file

@ -0,0 +1,229 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "sfx/dsound/sfxDSDevice.h"
#include "sfx/dsound/sfxDSBuffer.h"
#include "sfx/dsound/sfxDSVoice.h"
#include "platformWin32/platformWin32.h"
#include "core/util/safeRelease.h"
#include "platform/async/asyncUpdate.h"
#include "console/console.h"
SFXDSDevice::SFXDSDevice( SFXProvider* provider,
DSoundFNTable *dsFnTbl,
GUID* guid,
String name,
bool useHardware,
S32 maxBuffers )
: SFXDevice( name, provider, useHardware, maxBuffers ),
mDSound( NULL ),
mPrimaryBuffer( NULL ),
mListener( NULL ),
mDSoundTbl( dsFnTbl ),
mGUID( guid )
{
}
bool SFXDSDevice::_init()
{
HRESULT hr = mDSoundTbl->DirectSoundCreate8( mGUID, &mDSound, NULL );
if ( FAILED( hr ) || !mDSound )
{
Con::errorf( "SFXDSDevice::SFXDSDevice() - DirectSoundCreate8 failed" );
return false;
}
hr = mDSound->SetCooperativeLevel( getWin32WindowHandle(), DSSCL_PRIORITY );
if ( FAILED( hr ) )
{
Con::errorf( "SFXDSDevice::SFXDSDevice() - SetCooperativeLevel failed" );
return false;
}
// Get the primary buffer.
DSBUFFERDESC dsbd;
dMemset( &dsbd, 0, sizeof( DSBUFFERDESC ) );
dsbd.dwSize = sizeof( DSBUFFERDESC );
dsbd.dwFlags = DSBCAPS_CTRL3D | DSBCAPS_PRIMARYBUFFER;
hr = mDSound->CreateSoundBuffer( &dsbd, &mPrimaryBuffer, NULL );
if ( FAILED( hr ) )
{
Con::errorf( "SFXDSDevice::SFXDSDevice - Creating primary sound buffer failed" );
return false;
}
// Set the format and bitrate on the primary buffer.
S32 frequency = Con::getIntVariable( "$pref::SFX::frequency", 44100 );
S32 bitrate = Con::getIntVariable( "$pref::SFX::bitrate", 32 );
WAVEFORMATEX wfx;
dMemset( &wfx, 0, sizeof( WAVEFORMATEX ) );
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 2;
wfx.nSamplesPerSec = frequency;
wfx.wBitsPerSample = bitrate;
wfx.nBlockAlign = ( wfx.nChannels * wfx.wBitsPerSample ) / 8;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
hr = mPrimaryBuffer->SetFormat( &wfx );
if( FAILED( hr ) )
{
Con::errorf( "SFXDSDevice::SFXDSDevice() - Setting format of primary buffer failed" );
return false;
}
// Grab the 3d listener.
hr = mPrimaryBuffer->QueryInterface( IID_IDirectSound3DListener8, (LPVOID*)&mListener );
if ( FAILED( hr ) )
{
Con::errorf( "SFXDSDevice::SFXDevice() - Querying the listener interface failed!" );
mListener = NULL;
}
mCaps.dwSize = sizeof( DSCAPS );
mDSound->GetCaps( &mCaps );
// If the device reports no hardware buffers then
// we have no choice but to disable hardware.
if ( mCaps.dwMaxHw3DAllBuffers == 0 )
mUseHardware = false;
// If mMaxBuffers is negative then use the caps
// to decide on a good maximum value... or set 8.
if ( mMaxBuffers < 0 )
mMaxBuffers = getMax( mCaps.dwMaxHw3DAllBuffers, 8 );
// Start the stream thread.
if( !Con::getBoolVariable( "$_forceAllMainThread" ) )
{
SFXInternal::gUpdateThread =
new AsyncUpdateThread( "DirectSound Update Thread", SFXInternal::gBufferUpdateList );
SFXInternal::gUpdateThread->start();
}
return true;
}
SFXDSDevice::~SFXDSDevice()
{
// And release our resources.
SAFE_RELEASE( mListener );
SAFE_RELEASE( mPrimaryBuffer );
SAFE_RELEASE( mDSound );
}
SFXBuffer* SFXDSDevice::createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description )
{
AssertFatal( stream, "SFXDSDevice::createBuffer() - Got null stream!" );
AssertFatal( description, "SFXDSDevice::createBuffer() - Got null description!" );
SFXDSBuffer* buffer = SFXDSBuffer::create( mDSound,
stream,
description,
mUseHardware );
if( buffer )
_addBuffer( buffer );
return buffer;
}
SFXVoice* SFXDSDevice::createVoice( bool is3D, SFXBuffer *buffer )
{
// Don't bother going any further if we've
// exceeded the maximum voices.
if ( mVoices.size() >= mMaxBuffers )
return NULL;
AssertFatal( buffer, "SFXDSDevice::createVoice() - Got null buffer!" );
SFXDSBuffer* dsBuffer = dynamic_cast<SFXDSBuffer*>( buffer );
AssertFatal( dsBuffer, "SFXDSDevice::createVoice() - Got bad buffer!" );
SFXDSVoice* voice = SFXDSVoice::create( this, dsBuffer );
if ( !voice )
return NULL;
_addVoice( voice );
return voice;
}
void SFXDSDevice::_commitDeferred()
{
if( mListener )
mListener->CommitDeferredSettings();
}
void SFXDSDevice::update()
{
Parent::update();
// Apply the deferred settings that changed between updates.
mListener->CommitDeferredSettings();
}
void SFXDSDevice::setListener( U32 index, const SFXListenerProperties& listener )
{
// Get the transform from the listener.
const MatrixF& transform = listener.getTransform();
Point3F pos, dir, up;
transform.getColumn( 3, &pos );
transform.getColumn( 1, &dir );
transform.getColumn( 2, &up );
// And the velocity...
const VectorF& velocity = listener.getVelocity();
// Finally, set it all to DSound!
mListener->SetPosition( pos.x, pos.z, pos.y, DS3D_DEFERRED );
mListener->SetOrientation( dir.x, dir.z, dir.y, up.x, up.z, up.y, DS3D_DEFERRED );
mListener->SetVelocity( velocity.x, velocity.z, velocity.y, DS3D_DEFERRED );
}
void SFXDSDevice::setDistanceModel( SFXDistanceModel model )
{
switch( model )
{
case SFXDistanceModelLinear:
Con::errorf( "SFXDSDevice::setDistanceModel - 'linear' distance attenuation not supported by DirectSound" );
break;
case SFXDistanceModelLogarithmic:
break; // Nothing to do.
default:
AssertWarn( false, "SFXDSDevice::setDistanceModel() - model not implemented" );
}
}
void SFXDSDevice::setDopplerFactor( F32 factor )
{
if( mListener )
mListener->SetDopplerFactor( factor, DS3D_DEFERRED ); // Committed in update.
}
void SFXDSDevice::setRolloffFactor( F32 factor )
{
if( mListener )
mListener->SetRolloffFactor( factor, DS3D_DEFERRED ); // Committed in update.
}

View file

@ -0,0 +1,142 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _SFXDSDEVICE_H_
#define _SFXDSDEVICE_H_
#ifndef _STRINGFUNCTIONS_H_
# include "core/strings/stringFunctions.h"
#endif
#ifndef _SFXDEVICE_H_
#include "sfx/sfxDevice.h"
#endif
#ifndef _SFXDSVOICE_H_
#include "sfx/dsound/sfxDSVoice.h"
#endif
#ifndef OS_DLIBRARY_H
#include "platform/platformDlibrary.h"
#endif
#include <dsound.h>
// Typedefs
#define DS_FUNCTION(fn_name, fn_return, fn_args) \
typedef fn_return (WINAPI *DSFNPTR##fn_name##)##fn_args##;
#include "sfx/dsound/dsFunctions.h"
#undef DS_FUNCTION
// Function table
struct DSoundFNTable
{
DSoundFNTable() : isLoaded( false ) {};
bool isLoaded;
DLibraryRef dllRef;
#define DS_FUNCTION(fn_name, fn_return, fn_args) \
DSFNPTR##fn_name fn_name;
#include "sfx/dsound/dsFunctions.h"
#undef DS_FUNCTION
};
/// Helper for asserting on dsound HRESULTS.
inline void DSAssert( HRESULT hr, const char *info )
{
#ifdef TORQUE_DEBUG
if( FAILED( hr ) )
{
char buf[256];
dSprintf( buf, 256, "Error code: %x\n%s", hr, info );
AssertFatal( false, buf );
}
#endif
}
/// The DirectSound device implementation exposes a couple
/// of settings to script that you should be aware of:
///
/// $DirectSound::dopplerFactor - This controls the scale of
/// the doppler effect. Valid factors are 0.0 to 10.0 and it
/// defaults to 0.75.
///
/// $DirectSound::distanceFactor - This sets the unit conversion
/// for
///
/// $DirectSound::rolloffFactor - ;
///
///
class SFXDSDevice : public SFXDevice
{
typedef SFXDevice Parent;
friend class SFXDSVoice;
friend class SFXDSProvider; // _init
public:
//explicit SFXDSDevice();
SFXDSDevice( SFXProvider* provider,
DSoundFNTable *dsFnTbl,
GUID* guid,
String name,
bool useHardware,
S32 maxBuffers );
virtual ~SFXDSDevice();
protected:
IDirectSound8 *mDSound;
IDirectSound3DListener8 *mListener;
IDirectSoundBuffer *mPrimaryBuffer;
DSoundFNTable *mDSoundTbl;
DSCAPS mCaps;
GUID* mGUID;
bool _init();
/// Called from SFXDSVoice to commit any deferred
/// settings before playback begins.
void _commitDeferred();
public:
// SFXDevice
virtual SFXBuffer* createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description );
virtual SFXVoice* createVoice( bool is3D, SFXBuffer *buffer );
virtual void update();
virtual void setListener( U32 index, const SFXListenerProperties& listener );
virtual void setDistanceModel( SFXDistanceModel mode );
virtual void setDopplerFactor( F32 factor );
virtual void setRolloffFactor( F32 factor );
};
#endif // _SFXDSDEVICE_H_

View file

@ -0,0 +1,198 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "sfx/sfxProvider.h"
#include "sfx/dsound/sfxDSDevice.h"
#include "core/util/safeRelease.h"
#include "console/console.h"
#include "core/strings/unicode.h"
#include "core/util/safeDelete.h"
#include "core/module.h"
class SFXDSProvider : public SFXProvider
{
public:
SFXDSProvider()
: SFXProvider( "DirectSound" ) {}
virtual ~SFXDSProvider();
void init();
protected:
DSoundFNTable mDSound;
struct DSDeviceInfo : SFXDeviceInfo
{
GUID* guid;
DSCAPS caps;
};
static BOOL CALLBACK dsEnumProc(
LPGUID lpGUID,
LPCTSTR lpszDesc,
LPCTSTR lpszDrvName,
LPVOID lpContext );
void addDeviceDesc( GUID* guid, const String& name, const String& desc );
public:
SFXDevice* createDevice( const String& deviceName, bool useHardware, S32 maxBuffers );
};
MODULE_BEGIN( DirectSound )
MODULE_INIT_BEFORE( SFX )
MODULE_SHUTDOWN_AFTER( SFX )
SFXDSProvider* mProvider;
MODULE_INIT
{
mProvider = new SFXDSProvider;
}
MODULE_SHUTDOWN
{
delete mProvider;
}
MODULE_END;
//------------------------------------------------------------------------------
// Helper
bool dsBindFunction( DLibrary *dll, void *&fnAddress, const char *name )
{
fnAddress = dll->bind( name );
if (!fnAddress)
Con::warnf( "DSound Loader: DLL bind failed for %s", name );
return fnAddress != 0;
}
//------------------------------------------------------------------------------
SFXDSProvider::~SFXDSProvider()
{
}
void SFXDSProvider::init()
{
// Grab the functions we'll want from the dsound DLL.
mDSound.dllRef = OsLoadLibrary( "dsound.dll" );
mDSound.isLoaded = true;
#define DS_FUNCTION(fn_name, fn_return, fn_args) \
mDSound.isLoaded &= dsBindFunction(mDSound.dllRef, *(void**)&mDSound.fn_name, #fn_name);
#include "sfx/dsound/dsFunctions.h"
#undef DS_FUNCTION
AssertISV( mDSound.isLoaded, "DirectSound failed to load." );
// All we need to do to init is enumerate the
// devices... if this fails then don't register
// the provider as it's broken in some way.
if ( FAILED( mDSound.DirectSoundEnumerate( dsEnumProc, (VOID*)this ) ) )
{
Con::errorf( "SFXDSProvider - Device enumeration failed!" );
return;
}
// Did we get any devices?
if ( mDeviceInfo.empty() )
{
Con::errorf( "SFXDSProvider - No valid devices found!" );
return;
}
// Wow, we made it - register the provider.
regProvider( this );
}
BOOL CALLBACK SFXDSProvider::dsEnumProc(
LPGUID lpGUID,
LPCTSTR lpszDesc,
LPCTSTR lpszDrvName,
LPVOID lpContext )
{
SFXDSProvider* provider = (SFXDSProvider*)lpContext;
provider->addDeviceDesc( lpGUID, lpszDrvName, lpszDesc );
return TRUE;
}
void SFXDSProvider::addDeviceDesc( GUID* guid, const String& name, const String& desc )
{
// Create a dummy device to get the caps.
IDirectSound8* dsound;
HRESULT hr = mDSound.DirectSoundCreate8( guid, &dsound, NULL );
if ( FAILED( hr ) || !dsound )
return;
// Init the caps structure and have the device fill it out.
DSCAPS caps;
dMemset( &caps, 0, sizeof( caps ) );
caps.dwSize = sizeof( caps );
hr = dsound->GetCaps( &caps );
// Clean up and handle errors.
SAFE_RELEASE( dsound );
if ( FAILED( hr ) )
return;
// Now, record the desc info into our own internal list.
DSDeviceInfo* info = new DSDeviceInfo;
info->name = desc;
info->driver = name;
info->hasHardware = caps.dwMaxHw3DAllBuffers > 0;
info->maxBuffers = caps.dwMaxHw3DAllBuffers;
info->guid = guid;
info->caps = caps;
mDeviceInfo.push_back( info );
}
SFXDevice* SFXDSProvider::createDevice( const String& deviceName, bool useHardware, S32 maxBuffers )
{
DSDeviceInfo* info = dynamic_cast< DSDeviceInfo* >
( _findDeviceInfo( deviceName ) );
if( !info )
return NULL;
SFXDSDevice* device = new SFXDSDevice( this,
&mDSound,
info->guid,
info->name,
useHardware,
maxBuffers );
if( !device->_init() )
SAFE_DELETE( device );
return device;
}

View file

@ -0,0 +1,229 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "sfx/dsound/sfxDSVoice.h"
#include "sfx/dsound/sfxDSDevice.h"
#include "core/util/safeRelease.h"
SFXDSVoice* SFXDSVoice::create( SFXDSDevice *device, SFXDSBuffer *buffer )
{
AssertFatal( buffer, "SFXDSVoice::create() - Got null buffer!" );
IDirectSoundBuffer8 *dsBuffer8 = NULL;
if ( !buffer->createVoice( &dsBuffer8 ) || !dsBuffer8 )
return NULL;
// Now try to grab a 3D interface... if we don't
// get one its probably because its not a 3d sound.
IDirectSound3DBuffer8* dsBuffer3d8 = NULL;
dsBuffer8->QueryInterface( IID_IDirectSound3DBuffer8, (LPVOID*)&dsBuffer3d8 );
// Create the voice and return!
SFXDSVoice* voice = new SFXDSVoice( device,
buffer,
dsBuffer8,
dsBuffer3d8 );
// Now set the voice to a default state.
// The buffer from which we have duplicated may have been assigned different
// properties and we don't want to inherit these.
voice->setVolume( 1.0 );
voice->setPitch( 1.0 );
return voice;
}
SFXDSVoice::SFXDSVoice( SFXDSDevice *device,
SFXDSBuffer *buffer,
IDirectSoundBuffer8 *dsBuffer,
IDirectSound3DBuffer8 *dsBuffer3d )
: Parent( buffer ),
mDevice( device ),
mDSBuffer( dsBuffer ),
mDSBuffer3D( dsBuffer3d ),
mIsLooping( false )
{
AssertFatal( mDevice, "SFXDSVoice::SFXDSVoice() - SFXDSDevice is null!" );
AssertFatal( mBuffer, "SFXDSVoice::SFXDSVoice() - SFXDSBuffer is null!" );
AssertFatal( mDSBuffer, "SFXDSVoice::SFXDSVoice() - Dsound buffer is null!" );
}
SFXDSVoice::~SFXDSVoice()
{
SAFE_RELEASE( mDSBuffer3D );
SFXDSBuffer* dsBuffer = _getBuffer();
if( dsBuffer )
dsBuffer->releaseVoice( &mDSBuffer );
mBuffer = NULL;
}
SFXStatus SFXDSVoice::_status() const
{
DWORD status = 0;
mDSBuffer->GetStatus( &status );
if ( status & DSBSTATUS_PLAYING )
return SFXStatusPlaying;
else
return SFXStatusStopped;
}
void SFXDSVoice::_play()
{
DSAssert( mDSBuffer->Play( 0, 0, mIsLooping ? DSBPLAY_LOOPING : 0 ),
"SFXDSVoice::_play() - Playback failed!" );
}
void SFXDSVoice::_stop()
{
DSAssert( mDSBuffer->Stop(), "SFXDSVoice::pause - stop failed!" );
mDSBuffer->SetCurrentPosition( 0 );
}
void SFXDSVoice::_pause()
{
DSAssert( mDSBuffer->Stop(), "SFXDSVoice::pause - stop failed!" );
}
void SFXDSVoice::_seek( U32 sample )
{
U32 pos = mBuffer->getFormat().getBytesPerSample() * sample;
mDSBuffer->SetCurrentPosition( pos );
}
U32 SFXDSVoice::_tell() const
{
DWORD position = 0;
mDSBuffer->GetCurrentPosition( &position, NULL );
U32 samplePos = _getBuffer()->getSamplePos( position );
return samplePos;
}
void SFXDSVoice::setMinMaxDistance( F32 min, F32 max )
{
if ( !mDSBuffer3D )
return;
mDSBuffer3D->SetMinDistance( min, DS3D_DEFERRED );
mDSBuffer3D->SetMaxDistance( max, DS3D_DEFERRED );
}
void SFXDSVoice::play( bool looping )
{
// If this is a 3d sound then we need
// to commit any deferred settings before
// we start playback else we can get some
// glitches.
if ( mDSBuffer3D )
mDevice->_commitDeferred();
// If this is a streaming buffer,
// force looping.
const bool isStreaming = mBuffer->isStreaming();
if( isStreaming )
looping = true;
mIsLooping = looping;
Parent::play( looping );
}
void SFXDSVoice::setVelocity( const VectorF& velocity )
{
if ( !mDSBuffer3D )
return;
DSAssert( mDSBuffer3D->SetVelocity( velocity.x, velocity.z, velocity.y, DS3D_DEFERRED ),
"SFXDSVoice::setVelocity - couldn't update buffer!" );
}
void SFXDSVoice::setTransform( const MatrixF& transform )
{
if ( !mDSBuffer3D )
return;
Point3F pos, dir;
transform.getColumn( 3, &pos );
transform.getColumn( 1, &dir );
DSAssert( mDSBuffer3D->SetPosition( pos.x, pos.z, pos.y, DS3D_DEFERRED ),
"SFXDSVoice::setTransform - couldn't set position of the buffer." );
DSAssert( mDSBuffer3D->SetConeOrientation( dir.x, dir.z, dir.y, DS3D_DEFERRED ),
"SFXDSVoice::setTransform - couldn't set cone orientation of the buffer." );
}
/// Helper for converting floating point linear volume
/// to a logrithmic integer volume for dsound.
LONG SFXDSVoice::_linearToLogVolume( F32 linVolume )
{
LONG logVolume;
if ( linVolume <= 0.0f )
logVolume = DSBVOLUME_MIN;
else
{
logVolume = -2000.0 * mLog( 1.0f / linVolume );
logVolume = mClamp( logVolume, DSBVOLUME_MIN, DSBVOLUME_MAX );
}
return logVolume;
}
void SFXDSVoice::setVolume( F32 volume )
{
LONG logVolume = _linearToLogVolume( volume );
HRESULT hr = mDSBuffer->SetVolume( logVolume );
DSAssert( hr, "SFXDSVoice::setVolume - couldn't set volume!" );
}
void SFXDSVoice::setPitch( F32 pitch )
{
F32 sampleRate = _getBuffer()->getFormat().getSamplesPerSecond();
F32 frequency = mFloor( mClampF( sampleRate * pitch, DSBFREQUENCY_MIN, DSBFREQUENCY_MAX ) );
DSAssert( mDSBuffer->SetFrequency( ( U32 )frequency ),
"SFXDSVoice::setPitch - couldn't set playback frequency.");
}
void SFXDSVoice::setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume )
{
if ( !mDSBuffer3D )
return;
DSAssert( mDSBuffer3D->SetConeAngles( innerAngle,
outerAngle,
DS3D_DEFERRED ),
"SFXDSVoice::setCone - couldn't set cone angles!" );
LONG logVolume = _linearToLogVolume( outerVolume );
DSAssert( mDSBuffer3D->SetConeOutsideVolume( logVolume,
DS3D_DEFERRED ),
"SFXDSVoice::setCone - couldn't set cone outside volume!" );
}

View file

@ -0,0 +1,92 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _SFXDSVOICE_H_
#define _SFXDSVOICE_H_
#ifndef _SFXVOICE_H_
#include "sfx/sfxVoice.h"
#endif
#ifndef _SFXDSBUFFER_H_
#include "sfx/dsound/sfxDSBuffer.h"
#endif
#include <dsound.h>
class SFXDSDevice;
class SFXDSVoice : public SFXVoice
{
typedef SFXVoice Parent;
protected:
SFXDSVoice( SFXDSDevice *device,
SFXDSBuffer *buffer,
IDirectSoundBuffer8 *dsBuffer,
IDirectSound3DBuffer8 *dsBuffer3d );
/// The device used to commit deferred settings.
SFXDSDevice *mDevice;
IDirectSoundBuffer8 *mDSBuffer;
IDirectSound3DBuffer8 *mDSBuffer3D;
bool mIsLooping;
SFXDSBuffer* _getBuffer() const { return ( SFXDSBuffer* ) mBuffer.getPointer(); }
/// Helper for converting floating point linear volume
/// to a logrithmic integer volume for dsound.
static LONG _linearToLogVolume( F32 linVolume );
// SFXVoice
virtual SFXStatus _status() const;
virtual void _play();
virtual void _pause();
virtual void _stop();
virtual void _seek( U32 sample );
virtual U32 _tell() const;
public:
///
static SFXDSVoice* create( SFXDSDevice *device,
SFXDSBuffer *buffer );
///
virtual ~SFXDSVoice();
// SFXVoice
void setMinMaxDistance( F32 min, F32 max );
void play( bool looping );
void setVelocity( const VectorF& velocity );
void setTransform( const MatrixF& transform );
void setVolume( F32 volume );
void setPitch( F32 pitch );
void setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume );
};
#endif // _SFXDSBUFFER_H_