mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-14 08:04:40 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
125
Engine/source/sfx/xaudio/sfxXAudioBuffer.cpp
Normal file
125
Engine/source/sfx/xaudio/sfxXAudioBuffer.cpp
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/xaudio/sfxXAudioBuffer.h"
|
||||
#include "sfx/xaudio/sfxXAudioVoice.h"
|
||||
|
||||
|
||||
//#define DEBUG_SPEW
|
||||
|
||||
|
||||
SFXXAudioBuffer* SFXXAudioBuffer::create( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description )
|
||||
{
|
||||
SFXXAudioBuffer *buffer = new SFXXAudioBuffer( stream, description );
|
||||
return buffer;
|
||||
}
|
||||
|
||||
SFXXAudioBuffer::SFXXAudioBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description )
|
||||
: Parent( stream, description )
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION( mBufferQueue );
|
||||
}
|
||||
|
||||
SFXXAudioBuffer::~SFXXAudioBuffer()
|
||||
{
|
||||
_flush();
|
||||
}
|
||||
|
||||
void SFXXAudioBuffer::write( SFXInternal::SFXStreamPacket* const* packets, U32 num )
|
||||
{
|
||||
AssertFatal( SFXInternal::isSFXThread(), "SFXXAudioBuffer::write() - not on SFX thread" );
|
||||
|
||||
using namespace SFXInternal;
|
||||
|
||||
// Unqueue processed packets.
|
||||
|
||||
if( isStreaming() )
|
||||
{
|
||||
EnterCriticalSection( &_getUniqueVoice()->mLock );
|
||||
|
||||
XAUDIO2_VOICE_STATE state;
|
||||
_getUniqueVoice()->mXAudioVoice->GetState( &state );
|
||||
|
||||
U32 numProcessed = mBufferQueue.size() - state.BuffersQueued;
|
||||
for( U32 i = 0; i < numProcessed; ++ i )
|
||||
{
|
||||
destructSingle< SFXStreamPacket* >( mBufferQueue.first().mPacket );
|
||||
mBufferQueue.pop_front();
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[SFXXAudioBuffer] Unqueued packet" );
|
||||
#endif
|
||||
}
|
||||
|
||||
LeaveCriticalSection( &_getUniqueVoice()->mLock );
|
||||
}
|
||||
|
||||
// Queue new packets.
|
||||
|
||||
for( U32 i = 0; i < num; ++ i )
|
||||
{
|
||||
SFXStreamPacket* packet = packets[ i ];
|
||||
Buffer buffer;
|
||||
|
||||
if( packet->mIsLast )
|
||||
buffer.mData.Flags = XAUDIO2_END_OF_STREAM;
|
||||
|
||||
buffer.mPacket = packet;
|
||||
buffer.mData.AudioBytes = packet->mSizeActual;
|
||||
buffer.mData.pAudioData = packet->data;
|
||||
|
||||
mBufferQueue.push_back( buffer );
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[SFXXAudioBuffer] Queued packet" );
|
||||
#endif
|
||||
|
||||
// If this is a streaming buffer, submit the packet to the
|
||||
// voice queue right away.
|
||||
|
||||
if( isStreaming() )
|
||||
{
|
||||
EnterCriticalSection( &_getUniqueVoice()->mLock );
|
||||
|
||||
IXAudio2SourceVoice* voice = _getUniqueVoice()->mXAudioVoice;
|
||||
voice->SubmitSourceBuffer( &buffer.mData );
|
||||
|
||||
LeaveCriticalSection( &_getUniqueVoice()->mLock );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SFXXAudioBuffer::_flush()
|
||||
{
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[SFXXAudioBuffer] Flushing buffer" );
|
||||
#endif
|
||||
|
||||
if( _getUniqueVoice() )
|
||||
_getUniqueVoice()->_stop();
|
||||
|
||||
while( !mBufferQueue.empty() )
|
||||
{
|
||||
destructSingle( mBufferQueue.last().mPacket );
|
||||
mBufferQueue.pop_back();
|
||||
}
|
||||
}
|
||||
83
Engine/source/sfx/xaudio/sfxXAudioBuffer.h
Normal file
83
Engine/source/sfx/xaudio/sfxXAudioBuffer.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _SFXXAUDIOBUFFER_H_
|
||||
#define _SFXXAUDIOBUFFER_H_
|
||||
|
||||
#include <xaudio2.h>
|
||||
|
||||
#ifndef _SFXINTERNAL_H_
|
||||
#include "sfx/sfxInternal.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
|
||||
|
||||
/// Audio data buffer for the XAudio device layer.
|
||||
class SFXXAudioBuffer : public SFXBuffer
|
||||
{
|
||||
public:
|
||||
|
||||
typedef SFXBuffer Parent;
|
||||
|
||||
friend class SFXXAudioDevice;
|
||||
friend class SFXXAudioVoice;
|
||||
|
||||
protected:
|
||||
|
||||
struct Buffer
|
||||
{
|
||||
XAUDIO2_BUFFER mData;
|
||||
SFXInternal::SFXStreamPacket* mPacket;
|
||||
|
||||
Buffer()
|
||||
: mPacket( 0 )
|
||||
{
|
||||
dMemset( &mData, 0, sizeof( mData ) );
|
||||
}
|
||||
};
|
||||
|
||||
typedef Vector< Buffer > QueueType;
|
||||
|
||||
QueueType mBufferQueue;
|
||||
|
||||
/// If this is a streaming buffer, return the unique voice associated
|
||||
/// with the buffer.
|
||||
SFXXAudioVoice* _getUniqueVoice() { return ( SFXXAudioVoice* ) mUniqueVoice.getPointer(); }
|
||||
|
||||
///
|
||||
SFXXAudioBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description );
|
||||
virtual ~SFXXAudioBuffer();
|
||||
|
||||
// SFXBuffer.
|
||||
virtual void write( SFXInternal::SFXStreamPacket* const* packets, U32 num );
|
||||
void _flush();
|
||||
|
||||
public:
|
||||
|
||||
///
|
||||
static SFXXAudioBuffer* create( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description );
|
||||
};
|
||||
|
||||
#endif // _SFXXAUDIOBUFFER_H_
|
||||
245
Engine/source/sfx/xaudio/sfxXAudioDevice.cpp
Normal file
245
Engine/source/sfx/xaudio/sfxXAudioDevice.cpp
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/xaudio/sfxXAudioDevice.h"
|
||||
#include "platform/async/asyncUpdate.h"
|
||||
#include "core/stringTable.h"
|
||||
#include "console/console.h"
|
||||
#include "core/util/safeRelease.h"
|
||||
#include "core/tAlgorithm.h"
|
||||
#include "platform/profiler.h"
|
||||
|
||||
|
||||
SFXXAudioDevice::SFXXAudioDevice( SFXProvider* provider,
|
||||
const String& name,
|
||||
IXAudio2 *xaudio,
|
||||
U32 deviceIndex,
|
||||
U32 speakerChannelMask,
|
||||
U32 maxBuffers )
|
||||
: Parent( name, provider, false, maxBuffers ),
|
||||
mXAudio( xaudio ),
|
||||
mMasterVoice( NULL )
|
||||
{
|
||||
dMemset( &mListener, 0, sizeof( mListener ) );
|
||||
|
||||
// If mMaxBuffers is negative then use some default value.
|
||||
// to decide on a good maximum value... or set 8.
|
||||
//
|
||||
// TODO: We should change the terminology to voices!
|
||||
if ( mMaxBuffers < 0 )
|
||||
mMaxBuffers = 64;
|
||||
|
||||
// Create the mastering voice.
|
||||
HRESULT hr = mXAudio->CreateMasteringVoice( &mMasterVoice,
|
||||
XAUDIO2_DEFAULT_CHANNELS,
|
||||
XAUDIO2_DEFAULT_SAMPLERATE,
|
||||
0,
|
||||
deviceIndex,
|
||||
NULL );
|
||||
if ( FAILED( hr ) || !mMasterVoice )
|
||||
{
|
||||
Con::errorf( "SFXXAudioDevice - Failed creating master voice!" );
|
||||
return;
|
||||
}
|
||||
|
||||
mMasterVoice->GetVoiceDetails( &mMasterVoiceDetails );
|
||||
|
||||
// Init X3DAudio.
|
||||
X3DAudioInitialize( speakerChannelMask,
|
||||
X3DAUDIO_SPEED_OF_SOUND,
|
||||
mX3DAudio );
|
||||
|
||||
// Start the update thread.
|
||||
|
||||
if( !Con::getBoolVariable( "$_forceAllMainThread" ) )
|
||||
{
|
||||
SFXInternal::gUpdateThread = new AsyncUpdateThread
|
||||
( "XAudio Update Thread", SFXInternal::gBufferUpdateList );
|
||||
SFXInternal::gUpdateThread->start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SFXXAudioDevice::~SFXXAudioDevice()
|
||||
{
|
||||
_releaseAllResources();
|
||||
|
||||
if ( mMasterVoice )
|
||||
{
|
||||
mMasterVoice->DestroyVoice();
|
||||
mMasterVoice = NULL;
|
||||
}
|
||||
|
||||
// Kill the engine.
|
||||
SAFE_RELEASE( mXAudio );
|
||||
}
|
||||
|
||||
|
||||
SFXBuffer* SFXXAudioDevice::createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description )
|
||||
{
|
||||
SFXXAudioBuffer* buffer = SFXXAudioBuffer::create( stream, description );
|
||||
if ( !buffer )
|
||||
return NULL;
|
||||
|
||||
_addBuffer( buffer );
|
||||
return buffer;
|
||||
}
|
||||
|
||||
SFXVoice* SFXXAudioDevice::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, "SFXXAudioDevice::createVoice() - Got null buffer!" );
|
||||
|
||||
SFXXAudioBuffer* xaBuffer = dynamic_cast<SFXXAudioBuffer*>( buffer );
|
||||
AssertFatal( xaBuffer, "SFXXAudioDevice::createVoice() - Got bad buffer!" );
|
||||
|
||||
SFXXAudioVoice* voice = SFXXAudioVoice::create( mXAudio, is3D, xaBuffer );
|
||||
if ( !voice )
|
||||
return NULL;
|
||||
|
||||
voice->mXAudioDevice = this;
|
||||
|
||||
_addVoice( voice );
|
||||
return voice;
|
||||
}
|
||||
|
||||
void SFXXAudioDevice::_setOutputMatrix( SFXXAudioVoice *voice )
|
||||
{
|
||||
X3DAUDIO_DSP_SETTINGS dspSettings = {0};
|
||||
FLOAT32 matrix[12] = { 0 };
|
||||
dspSettings.DstChannelCount = mMasterVoiceDetails.InputChannels;
|
||||
dspSettings.pMatrixCoefficients = matrix;
|
||||
|
||||
const X3DAUDIO_EMITTER &emitter = voice->getEmitter();
|
||||
dspSettings.SrcChannelCount = emitter.ChannelCount;
|
||||
|
||||
// Calculate the output volumes and doppler.
|
||||
X3DAudioCalculate( mX3DAudio,
|
||||
&mListener,
|
||||
&emitter,
|
||||
X3DAUDIO_CALCULATE_MATRIX |
|
||||
X3DAUDIO_CALCULATE_DOPPLER,
|
||||
&dspSettings );
|
||||
|
||||
voice->mXAudioVoice->SetOutputMatrix( mMasterVoice,
|
||||
dspSettings.SrcChannelCount,
|
||||
dspSettings.DstChannelCount,
|
||||
dspSettings.pMatrixCoefficients,
|
||||
4321 );
|
||||
|
||||
voice->mXAudioVoice->SetFrequencyRatio( dspSettings.DopplerFactor * voice->mPitch,
|
||||
4321 );
|
||||
|
||||
// Commit the changes.
|
||||
mXAudio->CommitChanges( 4321 );
|
||||
}
|
||||
|
||||
void SFXXAudioDevice::update()
|
||||
{
|
||||
PROFILE_SCOPE( SFXXAudioDevice_Update );
|
||||
|
||||
Parent::update();
|
||||
|
||||
X3DAUDIO_DSP_SETTINGS dspSettings = {0};
|
||||
FLOAT32 matrix[12] = { 0 };
|
||||
dspSettings.DstChannelCount = mMasterVoiceDetails.InputChannels;
|
||||
dspSettings.pMatrixCoefficients = matrix;
|
||||
|
||||
dspSettings.DopplerFactor = mDopplerFactor;
|
||||
|
||||
// Now update the volume and frequency of
|
||||
// all the active 3D voices.
|
||||
VoiceVector::iterator voice = mVoices.begin();
|
||||
for ( ; voice != mVoices.end(); voice++ )
|
||||
{
|
||||
SFXXAudioVoice* xaVoice = ( SFXXAudioVoice* ) *voice;
|
||||
|
||||
// Skip 2D or stopped voices.
|
||||
if ( !xaVoice->is3D() ||
|
||||
xaVoice->getStatus() != SFXStatusPlaying )
|
||||
continue;
|
||||
|
||||
const X3DAUDIO_EMITTER &emitter = xaVoice->getEmitter();
|
||||
dspSettings.SrcChannelCount = emitter.ChannelCount;
|
||||
|
||||
// Calculate the output volumes and doppler.
|
||||
X3DAudioCalculate( mX3DAudio,
|
||||
&mListener,
|
||||
&emitter,
|
||||
X3DAUDIO_CALCULATE_MATRIX |
|
||||
X3DAUDIO_CALCULATE_DOPPLER,
|
||||
&dspSettings );
|
||||
|
||||
xaVoice->mXAudioVoice->SetOutputMatrix( mMasterVoice,
|
||||
dspSettings.SrcChannelCount,
|
||||
dspSettings.DstChannelCount,
|
||||
dspSettings.pMatrixCoefficients,
|
||||
4321 ) ;
|
||||
|
||||
xaVoice->mXAudioVoice->SetFrequencyRatio( dspSettings.DopplerFactor * xaVoice->mPitch,
|
||||
4321 );
|
||||
}
|
||||
|
||||
// Commit the changes.
|
||||
mXAudio->CommitChanges( 4321 );
|
||||
}
|
||||
|
||||
void SFXXAudioDevice::setListener( U32 index, const SFXListenerProperties& listener )
|
||||
{
|
||||
// Get the transform from the listener.
|
||||
const MatrixF& transform = listener.getTransform();
|
||||
transform.getColumn( 3, (Point3F*)&mListener.Position );
|
||||
transform.getColumn( 1, (Point3F*)&mListener.OrientFront );
|
||||
transform.getColumn( 2, (Point3F*)&mListener.OrientTop );
|
||||
|
||||
// And the velocity...
|
||||
const VectorF& velocity = listener.getVelocity();
|
||||
mListener.Velocity.x = velocity.x;
|
||||
mListener.Velocity.y = velocity.y;
|
||||
mListener.Velocity.z = velocity.z;
|
||||
|
||||
// XAudio and Torque use opposite handedness, so
|
||||
// flip the z coord to account for that.
|
||||
mListener.Position.z *= -1.0f;
|
||||
mListener.OrientFront.z *= -1.0f;
|
||||
mListener.OrientTop.z *= -1.0f;
|
||||
mListener.Velocity.z *= -1.0f;
|
||||
}
|
||||
|
||||
void SFXXAudioDevice::setDistanceModel( SFXDistanceModel model )
|
||||
{
|
||||
mDistanceModel = model;
|
||||
}
|
||||
|
||||
void SFXXAudioDevice::setDopplerFactor( F32 factor )
|
||||
{
|
||||
mDopplerFactor = factor;
|
||||
}
|
||||
|
||||
void SFXXAudioDevice::setRolloffFactor( F32 factor )
|
||||
{
|
||||
mRolloffFactor = factor;
|
||||
}
|
||||
101
Engine/source/sfx/xaudio/sfxXAudioDevice.h
Normal file
101
Engine/source/sfx/xaudio/sfxXAudioDevice.h
Normal 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 _SFXXAUDIODEVICE_H_
|
||||
#define _SFXXAUDIODEVICE_H_
|
||||
|
||||
class SFXProvider;
|
||||
|
||||
#ifndef _SFXDEVICE_H_
|
||||
#include "sfx/sfxDevice.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SFXPROVIDER_H_
|
||||
#include "sfx/sfxProvider.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SFXXAUDIOVOICE_H_
|
||||
#include "sfx/xaudio/sfxXAudioVoice.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SFXXAUDIOBUFFER_H_
|
||||
#include "sfx/xaudio/sfxXAudioBuffer.h"
|
||||
#endif
|
||||
|
||||
#include <xaudio2.h>
|
||||
#include <x3daudio.h>
|
||||
|
||||
|
||||
class SFXXAudioDevice : public SFXDevice
|
||||
{
|
||||
public:
|
||||
|
||||
typedef SFXDevice Parent;
|
||||
friend class SFXXAudioVoice; // mXAudio
|
||||
|
||||
protected:
|
||||
|
||||
/// The XAudio engine interface passed
|
||||
/// on creation from the provider.
|
||||
IXAudio2 *mXAudio;
|
||||
|
||||
/// The X3DAudio instance.
|
||||
X3DAUDIO_HANDLE mX3DAudio;
|
||||
|
||||
/// The one and only mastering voice.
|
||||
IXAudio2MasteringVoice* mMasterVoice;
|
||||
|
||||
/// The details of the master voice.
|
||||
XAUDIO2_VOICE_DETAILS mMasterVoiceDetails;
|
||||
|
||||
/// The one listener.
|
||||
X3DAUDIO_LISTENER mListener;
|
||||
|
||||
SFXDistanceModel mDistanceModel;
|
||||
F32 mRolloffFactor;
|
||||
F32 mDopplerFactor;
|
||||
|
||||
public:
|
||||
|
||||
SFXXAudioDevice( SFXProvider* provider,
|
||||
const String& name,
|
||||
IXAudio2 *xaudio,
|
||||
U32 deviceIndex,
|
||||
U32 speakerChannelMask,
|
||||
U32 maxBuffers );
|
||||
|
||||
virtual ~SFXXAudioDevice();
|
||||
|
||||
// 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 model );
|
||||
virtual void setRolloffFactor( F32 factor );
|
||||
virtual void setDopplerFactor( F32 factor );
|
||||
|
||||
/// Called from the voice when its about to start playback.
|
||||
void _setOutputMatrix( SFXXAudioVoice *voice );
|
||||
};
|
||||
|
||||
#endif // _SFXXAUDIODEVICE_H_
|
||||
193
Engine/source/sfx/xaudio/sfxXAudioProvider.cpp
Normal file
193
Engine/source/sfx/xaudio/sfxXAudioProvider.cpp
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Note: This must be defined before platform.h so that
|
||||
// CoInitializeEx is properly included.
|
||||
#define _WIN32_DCOM
|
||||
#include <xaudio2.h>
|
||||
|
||||
#include "sfx/xaudio/sfxXAudioDevice.h"
|
||||
#include "sfx/sfxProvider.h"
|
||||
#include "core/util/safeRelease.h"
|
||||
#include "core/strings/unicode.h"
|
||||
#include "core/strings/stringFunctions.h"
|
||||
#include "console/console.h"
|
||||
#include "core/module.h"
|
||||
|
||||
|
||||
class SFXXAudioProvider : public SFXProvider
|
||||
{
|
||||
public:
|
||||
|
||||
SFXXAudioProvider()
|
||||
: SFXProvider( "XAudio" ) {}
|
||||
virtual ~SFXXAudioProvider();
|
||||
|
||||
protected:
|
||||
|
||||
/// Extended SFXDeviceInfo to also store some
|
||||
/// extra XAudio specific data.
|
||||
struct XADeviceInfo : SFXDeviceInfo
|
||||
{
|
||||
UINT32 deviceIndex;
|
||||
|
||||
XAUDIO2_DEVICE_ROLE role;
|
||||
|
||||
WAVEFORMATEXTENSIBLE format;
|
||||
};
|
||||
|
||||
/// Helper for creating the XAudio engine.
|
||||
static bool _createXAudio( IXAudio2 **xaudio );
|
||||
|
||||
public:
|
||||
|
||||
// SFXProvider
|
||||
void init();
|
||||
SFXDevice* createDevice( const String& deviceName, bool useHardware, S32 maxBuffers );
|
||||
|
||||
};
|
||||
|
||||
MODULE_BEGIN( XAudio )
|
||||
|
||||
MODULE_INIT_BEFORE( SFX )
|
||||
MODULE_SHUTDOWN_AFTER( SFX )
|
||||
|
||||
SFXXAudioProvider* mProvider;
|
||||
|
||||
MODULE_INIT
|
||||
{
|
||||
mProvider = new SFXXAudioProvider;
|
||||
}
|
||||
|
||||
MODULE_SHUTDOWN
|
||||
{
|
||||
delete mProvider;
|
||||
}
|
||||
|
||||
MODULE_END;
|
||||
|
||||
SFXXAudioProvider::~SFXXAudioProvider()
|
||||
{
|
||||
}
|
||||
|
||||
void SFXXAudioProvider::init()
|
||||
{
|
||||
// Create a temp XAudio object for device enumeration.
|
||||
IXAudio2 *xAudio = NULL;
|
||||
if ( !_createXAudio( &xAudio ) )
|
||||
{
|
||||
Con::errorf( "SFXXAudioProvider::init() - XAudio2 failed to load!" );
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the devices to the info list.
|
||||
UINT32 count = 0;
|
||||
xAudio->GetDeviceCount( &count );
|
||||
for ( UINT32 i = 0; i < count; i++ )
|
||||
{
|
||||
XAUDIO2_DEVICE_DETAILS details;
|
||||
HRESULT hr = xAudio->GetDeviceDetails( i, &details );
|
||||
if ( FAILED( hr ) )
|
||||
continue;
|
||||
|
||||
// Add a device to the info list.
|
||||
XADeviceInfo* info = new XADeviceInfo;
|
||||
info->deviceIndex = i;
|
||||
info->driver = String( "XAudio" );
|
||||
info->name = String( details.DisplayName );
|
||||
info->hasHardware = false;
|
||||
info->maxBuffers = 64;
|
||||
info->role = details.Role;
|
||||
info->format = details.OutputFormat;
|
||||
mDeviceInfo.push_back( info );
|
||||
}
|
||||
|
||||
// We're done with XAudio for now.
|
||||
SAFE_RELEASE( xAudio );
|
||||
|
||||
// If we have no devices... we're done.
|
||||
if ( mDeviceInfo.empty() )
|
||||
{
|
||||
Con::errorf( "SFXXAudioProvider::init() - No valid XAudio2 devices found!" );
|
||||
return;
|
||||
}
|
||||
|
||||
// If we got this far then we should be able to
|
||||
// safely create a device for XAudio.
|
||||
regProvider( this );
|
||||
}
|
||||
|
||||
bool SFXXAudioProvider::_createXAudio( IXAudio2 **xaudio )
|
||||
{
|
||||
// In debug builds enable the debug version
|
||||
// of the XAudio engine.
|
||||
#ifdef TORQUE_DEBUG
|
||||
#define XAUDIO_FLAGS XAUDIO2_DEBUG_ENGINE
|
||||
#else
|
||||
#define XAUDIO_FLAGS 0
|
||||
#endif
|
||||
|
||||
#ifndef TORQUE_OS_XENON
|
||||
// This must be called first... it doesn't hurt to
|
||||
// call it more than once.
|
||||
CoInitialize( NULL );
|
||||
#endif
|
||||
|
||||
// Try creating the xaudio engine.
|
||||
HRESULT hr = XAudio2Create( xaudio, XAUDIO_FLAGS, XAUDIO2_DEFAULT_PROCESSOR );
|
||||
|
||||
return SUCCEEDED( hr ) && (*xaudio);
|
||||
}
|
||||
|
||||
SFXDevice* SFXXAudioProvider::createDevice( const String& deviceName, bool useHardware, S32 maxBuffers )
|
||||
{
|
||||
String devName;
|
||||
|
||||
// On the 360, ignore what the prefs say, and create the only audio device
|
||||
#ifndef TORQUE_OS_XENON
|
||||
devName = deviceName;
|
||||
#endif
|
||||
|
||||
XADeviceInfo* info = dynamic_cast< XADeviceInfo* >( _findDeviceInfo( devName ) );
|
||||
|
||||
// Do we find one to create?
|
||||
if ( info )
|
||||
{
|
||||
// Create the XAudio object to pass to the device.
|
||||
IXAudio2 *xAudio = NULL;
|
||||
if ( !_createXAudio( &xAudio ) )
|
||||
{
|
||||
Con::errorf( "SFXXAudioProvider::createDevice() - XAudio2 failed to load!" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return new SFXXAudioDevice( this,
|
||||
devName,
|
||||
xAudio,
|
||||
info->deviceIndex,
|
||||
info->format.dwChannelMask,
|
||||
maxBuffers );
|
||||
}
|
||||
|
||||
// We didn't find a matching valid device.
|
||||
return NULL;
|
||||
}
|
||||
445
Engine/source/sfx/xaudio/sfxXAudioVoice.cpp
Normal file
445
Engine/source/sfx/xaudio/sfxXAudioVoice.cpp
Normal file
|
|
@ -0,0 +1,445 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "platform/platform.h"
|
||||
#include "sfx/xaudio/sfxXAudioVoice.h"
|
||||
#include "sfx/xaudio/sfxXAudioDevice.h"
|
||||
#include "sfx/xaudio/sfxXAudioBuffer.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
#include "math/mMathFn.h"
|
||||
|
||||
|
||||
//#define DEBUG_SPEW
|
||||
|
||||
|
||||
static void sfxFormatToWAVEFORMATEX( const SFXFormat& format, 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;
|
||||
}
|
||||
|
||||
|
||||
SFXXAudioVoice* SFXXAudioVoice::create( IXAudio2 *xaudio,
|
||||
bool is3D,
|
||||
SFXXAudioBuffer *buffer,
|
||||
SFXXAudioVoice* inVoice )
|
||||
{
|
||||
AssertFatal( xaudio, "SFXXAudioVoice::create() - Got null XAudio!" );
|
||||
AssertFatal( buffer, "SFXXAudioVoice::create() - Got null buffer!" );
|
||||
|
||||
// Create the voice object first as it also the callback object.
|
||||
SFXXAudioVoice* voice = inVoice;
|
||||
if( !voice )
|
||||
voice = new SFXXAudioVoice( buffer );
|
||||
|
||||
// Get the buffer format.
|
||||
WAVEFORMATEX wfx;
|
||||
sfxFormatToWAVEFORMATEX( buffer->getFormat(), &wfx );
|
||||
|
||||
// We don't support multi-channel 3d sounds!
|
||||
if ( is3D && wfx.nChannels > 1 )
|
||||
return NULL;
|
||||
|
||||
// Create the voice.
|
||||
IXAudio2SourceVoice *xaVoice;
|
||||
HRESULT hr = xaudio->CreateSourceVoice( &xaVoice,
|
||||
(WAVEFORMATEX*)&wfx,
|
||||
0,
|
||||
XAUDIO2_DEFAULT_FREQ_RATIO,
|
||||
voice,
|
||||
NULL,
|
||||
NULL );
|
||||
|
||||
if( FAILED( hr ) || !voice )
|
||||
{
|
||||
if( !inVoice )
|
||||
delete voice;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
voice->mIs3D = is3D;
|
||||
voice->mEmitter.ChannelCount = wfx.nChannels;
|
||||
voice->mXAudioVoice = xaVoice;
|
||||
|
||||
return voice;
|
||||
}
|
||||
|
||||
SFXXAudioVoice::SFXXAudioVoice( SFXXAudioBuffer* buffer )
|
||||
: Parent( buffer ),
|
||||
mXAudioDevice( NULL ),
|
||||
mXAudioVoice( NULL ),
|
||||
mIs3D( false ),
|
||||
mPitch( 1.0f ),
|
||||
mHasStopped( false ),
|
||||
mHasStarted( false ),
|
||||
mIsLooping( false ),
|
||||
mIsPlaying( false ),
|
||||
mNonStreamSampleStartPos( 0 ),
|
||||
mNonStreamBufferLoaded( false ),
|
||||
mSamplesPlayedOffset( 0 )
|
||||
{
|
||||
dMemset( &mEmitter, 0, sizeof( mEmitter ) );
|
||||
mEmitter.DopplerScaler = 1.0f;
|
||||
|
||||
InitializeCriticalSection( &mLock );
|
||||
}
|
||||
|
||||
SFXXAudioVoice::~SFXXAudioVoice()
|
||||
{
|
||||
if ( mEmitter.pVolumeCurve )
|
||||
{
|
||||
SAFE_DELETE_ARRAY( mEmitter.pVolumeCurve->pPoints );
|
||||
SAFE_DELETE( mEmitter.pVolumeCurve );
|
||||
}
|
||||
|
||||
SAFE_DELETE( mEmitter.pCone );
|
||||
|
||||
if ( mXAudioVoice )
|
||||
mXAudioVoice->DestroyVoice();
|
||||
|
||||
DeleteCriticalSection( &mLock );
|
||||
}
|
||||
|
||||
SFXStatus SFXXAudioVoice::_status() const
|
||||
{
|
||||
if( mHasStopped )
|
||||
return SFXStatusStopped;
|
||||
else if( mHasStarted )
|
||||
{
|
||||
if( !mIsPlaying )
|
||||
return SFXStatusPaused;
|
||||
else
|
||||
return SFXStatusPlaying;
|
||||
}
|
||||
else
|
||||
return SFXStatusStopped;
|
||||
}
|
||||
|
||||
void SFXXAudioVoice::_flush()
|
||||
{
|
||||
AssertFatal( mXAudioVoice != NULL,
|
||||
"SFXXAudioVoice::_flush() - invalid voice" );
|
||||
|
||||
EnterCriticalSection( &mLock );
|
||||
|
||||
mXAudioVoice->Stop( 0 );
|
||||
mXAudioVoice->FlushSourceBuffers();
|
||||
mNonStreamBufferLoaded = false;
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[SFXXAudioVoice] Flushed state" );
|
||||
#endif
|
||||
|
||||
mIsPlaying = false;
|
||||
mHasStarted = false;
|
||||
mHasStopped = true;
|
||||
|
||||
//WORKAROUND: According to the docs, SamplesPlayed reported by the
|
||||
// voice should get reset as soon as we submit a new buffer to the voice.
|
||||
// Alas it won't. So, save the current value here and offset our future
|
||||
// play cursors.
|
||||
|
||||
XAUDIO2_VOICE_STATE state;
|
||||
mXAudioVoice->GetState( &state );
|
||||
mSamplesPlayedOffset = - S32( state.SamplesPlayed );
|
||||
|
||||
LeaveCriticalSection( &mLock );
|
||||
}
|
||||
|
||||
void SFXXAudioVoice::_play()
|
||||
{
|
||||
AssertFatal( mXAudioVoice != NULL,
|
||||
"SFXXAudioVoice::_play() - invalid voice" );
|
||||
|
||||
// For non-streaming voices queue the data if we haven't yet.
|
||||
|
||||
if( !mBuffer->isStreaming() && !mNonStreamBufferLoaded )
|
||||
_loadNonStreamed();
|
||||
|
||||
// Start playback.
|
||||
|
||||
mXAudioVoice->Start( 0, 0 );
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[SFXXAudioVoice] Started playback" );
|
||||
#endif
|
||||
|
||||
mIsPlaying = true;
|
||||
mHasStarted = true;
|
||||
mHasStopped = false;
|
||||
}
|
||||
|
||||
void SFXXAudioVoice::_pause()
|
||||
{
|
||||
AssertFatal( mXAudioVoice != NULL,
|
||||
"SFXXAudioVoice::_pause() - invalid voice" );
|
||||
|
||||
mXAudioVoice->Stop( 0 );
|
||||
mIsPlaying = false;
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[SFXXAudioVoice] Paused playback" );
|
||||
#endif
|
||||
}
|
||||
|
||||
void SFXXAudioVoice::_stop()
|
||||
{
|
||||
AssertFatal( mXAudioVoice != NULL,
|
||||
"SFXXAudioVoice::_stop() - invalid voice" );
|
||||
|
||||
_flush();
|
||||
|
||||
mIsPlaying = false;
|
||||
mHasStarted = false;
|
||||
mHasStopped = true;
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[SFXXAudioVoice] Stopped playback" );
|
||||
#endif
|
||||
}
|
||||
|
||||
void SFXXAudioVoice::_seek( U32 sample )
|
||||
{
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[SFXXAudioVoice] Seeking to %i", sample );
|
||||
#endif
|
||||
|
||||
mNonStreamSampleStartPos = sample;
|
||||
|
||||
bool wasPlaying = mIsPlaying;
|
||||
|
||||
_stop();
|
||||
if( wasPlaying )
|
||||
_play();
|
||||
}
|
||||
|
||||
void SFXXAudioVoice::_loadNonStreamed()
|
||||
{
|
||||
AssertFatal( !mBuffer->isStreaming(), "SFXXAudioVoice::_loadNonStreamed - must not be called on streaming voices" );
|
||||
AssertFatal( mXAudioVoice != NULL, "SFXXAudioVoice::_loadNonStreamed - invalid voice" );
|
||||
AssertWarn( !mNonStreamBufferLoaded, "SFXXAudioVoice::_nonStreamNonstreamed - Data already loaded" );
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[SFXXAudioVoice] Loading non-stream buffer at %i", mNonStreamSampleStartPos );
|
||||
#endif
|
||||
|
||||
EnterCriticalSection( &mLock );
|
||||
|
||||
const XAUDIO2_BUFFER& orgBuffer = _getBuffer()->mBufferQueue.front().mData;
|
||||
|
||||
mNonStreamBuffer = orgBuffer;
|
||||
|
||||
if( mNonStreamSampleStartPos )
|
||||
{
|
||||
mNonStreamBuffer.PlayBegin = mNonStreamSampleStartPos;
|
||||
mNonStreamBuffer.PlayLength = _getBuffer()->getNumSamples() - mNonStreamSampleStartPos;
|
||||
mSamplesPlayedOffset += mNonStreamSampleStartPos; // Add samples that we are skipping.
|
||||
mNonStreamSampleStartPos = 0;
|
||||
}
|
||||
|
||||
if( mIsLooping )
|
||||
{
|
||||
mNonStreamBuffer.LoopCount = XAUDIO2_LOOP_INFINITE;
|
||||
mNonStreamBuffer.LoopLength = _getBuffer()->getNumSamples();
|
||||
}
|
||||
|
||||
// Submit buffer.
|
||||
|
||||
mXAudioVoice->SubmitSourceBuffer( &mNonStreamBuffer );
|
||||
mNonStreamBufferLoaded = true;
|
||||
|
||||
LeaveCriticalSection( &mLock );
|
||||
}
|
||||
|
||||
U32 SFXXAudioVoice::_tell() const
|
||||
{
|
||||
XAUDIO2_VOICE_STATE state;
|
||||
mXAudioVoice->GetState( &state );
|
||||
|
||||
// Workaround SamplesPlayed not getting reset.
|
||||
return ( state.SamplesPlayed + mSamplesPlayedOffset );
|
||||
}
|
||||
|
||||
void SFXXAudioVoice::setMinMaxDistance( F32 min, F32 max )
|
||||
{
|
||||
// Set the overall volume curve scale.
|
||||
mEmitter.CurveDistanceScaler = max;
|
||||
|
||||
// The curve uses normalized distances, so
|
||||
// figure out the normalized min distance.
|
||||
F32 normMin = 0.0f;
|
||||
if ( min > 0.0f )
|
||||
normMin = min / max;
|
||||
|
||||
// See what type of curve we are supposed to generate.
|
||||
const bool linear = ( mXAudioDevice->mDistanceModel == SFXDistanceModelLinear );
|
||||
|
||||
// Have we setup the curve yet?
|
||||
if( !mEmitter.pVolumeCurve
|
||||
|| ( linear && mEmitter.pVolumeCurve->PointCount != 2 )
|
||||
|| ( !linear && mEmitter.pVolumeCurve->PointCount != 6 ) )
|
||||
{
|
||||
if( !mEmitter.pVolumeCurve )
|
||||
mEmitter.pVolumeCurve = new X3DAUDIO_DISTANCE_CURVE;
|
||||
else
|
||||
SAFE_DELETE_ARRAY( mEmitter.pVolumeCurve->pPoints );
|
||||
|
||||
// We use 6 points for logarithmic volume curves and 2 for linear volume curves.
|
||||
if( linear )
|
||||
{
|
||||
mEmitter.pVolumeCurve->pPoints = new X3DAUDIO_DISTANCE_CURVE_POINT[ 2 ];
|
||||
mEmitter.pVolumeCurve->PointCount = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
mEmitter.pVolumeCurve->pPoints = new X3DAUDIO_DISTANCE_CURVE_POINT[ 6 ];
|
||||
mEmitter.pVolumeCurve->PointCount = 6;
|
||||
}
|
||||
|
||||
// The first and last points are known
|
||||
// and will not change.
|
||||
mEmitter.pVolumeCurve->pPoints[ 0 ].Distance = 0.0f;
|
||||
mEmitter.pVolumeCurve->pPoints[ 0 ].DSPSetting = 1.0f;
|
||||
mEmitter.pVolumeCurve->pPoints[ linear ? 1 : 5 ].Distance = 1.0f;
|
||||
mEmitter.pVolumeCurve->pPoints[ linear ? 1 : 5 ].DSPSetting = 0.0f;
|
||||
}
|
||||
|
||||
if( !linear )
|
||||
{
|
||||
// Set the second point of the curve.
|
||||
mEmitter.pVolumeCurve->pPoints[1].Distance = normMin;
|
||||
mEmitter.pVolumeCurve->pPoints[1].DSPSetting = 1.0f;
|
||||
|
||||
// The next three points are calculated to
|
||||
// give the sound a rough logarithmic falloff.
|
||||
F32 distStep = ( 1.0f - normMin ) / 4.0f;
|
||||
for ( U32 i=0; i < 3; i++ )
|
||||
{
|
||||
U32 index = 2 + i;
|
||||
F32 dist = normMin + ( distStep * (F32)( i + 1 ) );
|
||||
|
||||
mEmitter.pVolumeCurve->pPoints[index].Distance = dist;
|
||||
mEmitter.pVolumeCurve->pPoints[index].DSPSetting = 1.0f - log10( dist * 10.0f );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SFXXAudioVoice::OnBufferEnd( void* bufferContext )
|
||||
{
|
||||
if( mBuffer->isStreaming() )
|
||||
SFXInternal::TriggerUpdate();
|
||||
}
|
||||
|
||||
void SFXXAudioVoice::OnStreamEnd()
|
||||
{
|
||||
// Warning: This is being called within the XAudio
|
||||
// thread, so be sure you're thread safe!
|
||||
|
||||
mHasStopped = true;
|
||||
|
||||
if( mBuffer->isStreaming() )
|
||||
SFXInternal::TriggerUpdate();
|
||||
else
|
||||
_stop();
|
||||
}
|
||||
|
||||
void SFXXAudioVoice::play( bool looping )
|
||||
{
|
||||
// Give the device a chance to calculate our positional
|
||||
// audio settings before we start playback... this is
|
||||
// important else we get glitches.
|
||||
if( mIs3D )
|
||||
mXAudioDevice->_setOutputMatrix( this );
|
||||
|
||||
mIsLooping = looping;
|
||||
Parent::play( looping );
|
||||
}
|
||||
|
||||
void SFXXAudioVoice::setVelocity( const VectorF& velocity )
|
||||
{
|
||||
mEmitter.Velocity.x = velocity.x;
|
||||
mEmitter.Velocity.y = velocity.y;
|
||||
|
||||
// XAudio and Torque use opposite handedness, so
|
||||
// flip the z coord to account for that.
|
||||
mEmitter.Velocity.z = -velocity.z;
|
||||
}
|
||||
|
||||
void SFXXAudioVoice::setTransform( const MatrixF& transform )
|
||||
{
|
||||
transform.getColumn( 3, (Point3F*)&mEmitter.Position );
|
||||
transform.getColumn( 1, (Point3F*)&mEmitter.OrientFront );
|
||||
transform.getColumn( 2, (Point3F*)&mEmitter.OrientTop );
|
||||
|
||||
// XAudio and Torque use opposite handedness, so
|
||||
// flip the z coord to account for that.
|
||||
mEmitter.Position.z *= -1.0f;
|
||||
mEmitter.OrientFront.z *= -1.0f;
|
||||
mEmitter.OrientTop.z *= -1.0f;
|
||||
}
|
||||
|
||||
void SFXXAudioVoice::setVolume( F32 volume )
|
||||
{
|
||||
mXAudioVoice->SetVolume( volume );
|
||||
}
|
||||
|
||||
void SFXXAudioVoice::setPitch( F32 pitch )
|
||||
{
|
||||
mPitch = mClampF( pitch, XAUDIO2_MIN_FREQ_RATIO, XAUDIO2_DEFAULT_FREQ_RATIO );
|
||||
mXAudioVoice->SetFrequencyRatio( mPitch );
|
||||
}
|
||||
|
||||
void SFXXAudioVoice::setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume )
|
||||
{
|
||||
// If the cone is set to 360 then the
|
||||
// cone is null and doesn't need to be
|
||||
// set on the voice.
|
||||
if ( mIsEqual( innerAngle, 360 ) )
|
||||
{
|
||||
SAFE_DELETE( mEmitter.pCone );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !mEmitter.pCone )
|
||||
{
|
||||
mEmitter.pCone = new X3DAUDIO_CONE;
|
||||
|
||||
// The inner volume is always 1... the overall
|
||||
// volume is what scales it.
|
||||
mEmitter.pCone->InnerVolume = 1.0f;
|
||||
|
||||
// We don't use these yet.
|
||||
mEmitter.pCone->InnerLPF = 0.0f;
|
||||
mEmitter.pCone->OuterLPF = 0.0f;
|
||||
mEmitter.pCone->InnerReverb = 0.0f;
|
||||
mEmitter.pCone->OuterReverb = 0.0f;
|
||||
}
|
||||
|
||||
mEmitter.pCone->InnerAngle = mDegToRad( innerAngle );
|
||||
mEmitter.pCone->OuterAngle = mDegToRad( outerAngle );
|
||||
mEmitter.pCone->OuterVolume = outerVolume;
|
||||
}
|
||||
154
Engine/source/sfx/xaudio/sfxXAudioVoice.h
Normal file
154
Engine/source/sfx/xaudio/sfxXAudioVoice.h
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _SFXXAUDIOVOICE_H_
|
||||
#define _SFXXAUDIOVOICE_H_
|
||||
|
||||
#include <xaudio2.h>
|
||||
#include <x3daudio.h>
|
||||
|
||||
#include "sfx/sfxVoice.h"
|
||||
|
||||
|
||||
class SFXXAudioBuffer;
|
||||
|
||||
|
||||
class SFXXAudioVoice : public SFXVoice,
|
||||
public IXAudio2VoiceCallback
|
||||
{
|
||||
public:
|
||||
|
||||
typedef SFXVoice Parent;
|
||||
|
||||
friend class SFXXAudioDevice;
|
||||
friend class SFXXAudioBuffer;
|
||||
|
||||
protected:
|
||||
|
||||
/// This constructor does not create a valid voice.
|
||||
/// @see SFXXAudioVoice::create
|
||||
SFXXAudioVoice( SFXXAudioBuffer* buffer );
|
||||
|
||||
/// The device that created us.
|
||||
SFXXAudioDevice *mXAudioDevice;
|
||||
|
||||
/// The XAudio voice.
|
||||
IXAudio2SourceVoice *mXAudioVoice;
|
||||
|
||||
///
|
||||
CRITICAL_SECTION mLock;
|
||||
|
||||
/// Used to know what sounds need positional updates.
|
||||
bool mIs3D;
|
||||
|
||||
/// Whether the voice has stopped playing.
|
||||
mutable bool mHasStopped;
|
||||
|
||||
/// Whether we have started playback.
|
||||
bool mHasStarted;
|
||||
|
||||
/// Whether playback is currently running.
|
||||
bool mIsPlaying;
|
||||
|
||||
/// Whether playback is looping.
|
||||
bool mIsLooping;
|
||||
|
||||
/// Since 3D sounds are pitch shifted for doppler
|
||||
/// effect we need to track the users base pitch.
|
||||
F32 mPitch;
|
||||
|
||||
/// The cached X3DAudio emitter data.
|
||||
X3DAUDIO_EMITTER mEmitter;
|
||||
|
||||
/// XAudio does not reset the SamplesPlayed count as is stated in the docs. To work around
|
||||
/// that, we offset the values reported by XAudio through this field.
|
||||
S32 mSamplesPlayedOffset;
|
||||
|
||||
/// @name Data for Non-Streaming Voices
|
||||
/// @{
|
||||
|
||||
/// Whether we have loaded our non-streaming data. We use an explicit
|
||||
/// flag here as we really can't rely on XAUDIO2_VOICE_STATE.
|
||||
bool mNonStreamBufferLoaded;
|
||||
|
||||
/// Audio buffer for non-streaming voice.
|
||||
XAUDIO2_BUFFER mNonStreamBuffer;
|
||||
|
||||
/// Sample to start playing at when seting up #mNonStreamBuffer.
|
||||
U32 mNonStreamSampleStartPos;
|
||||
|
||||
/// @}
|
||||
|
||||
// IXAudio2VoiceCallback
|
||||
void STDMETHODCALLTYPE OnStreamEnd();
|
||||
void STDMETHODCALLTYPE OnVoiceProcessingPassStart( UINT32 BytesRequired ) {}
|
||||
void STDMETHODCALLTYPE OnVoiceProcessingPassEnd() {}
|
||||
void STDMETHODCALLTYPE OnBufferEnd( void *bufferContext );
|
||||
void STDMETHODCALLTYPE OnBufferStart( void *bufferContext ) {}
|
||||
void STDMETHODCALLTYPE OnLoopEnd( void *bufferContext ) {}
|
||||
void STDMETHODCALLTYPE OnVoiceError( void * bufferContext, HRESULT error ) {}
|
||||
|
||||
/// @deprecated This is only here for compatibility with
|
||||
/// the March 2008 SDK version of IXAudio2VoiceCallback.
|
||||
void STDMETHODCALLTYPE OnVoiceProcessingPassStart() {}
|
||||
|
||||
void _flush();
|
||||
void _loadNonStreamed();
|
||||
|
||||
// SFXVoice.
|
||||
virtual SFXStatus _status() const;
|
||||
virtual void _play();
|
||||
virtual void _pause();
|
||||
virtual void _stop();
|
||||
virtual void _seek( U32 sample );
|
||||
virtual U32 _tell() const;
|
||||
|
||||
SFXXAudioBuffer* _getBuffer() const { return ( SFXXAudioBuffer* ) mBuffer.getPointer(); }
|
||||
|
||||
public:
|
||||
|
||||
///
|
||||
static SFXXAudioVoice* create( IXAudio2 *xaudio,
|
||||
bool is3D,
|
||||
SFXXAudioBuffer *buffer,
|
||||
SFXXAudioVoice* inVoice = NULL );
|
||||
|
||||
///
|
||||
virtual ~SFXXAudioVoice();
|
||||
|
||||
// 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 );
|
||||
|
||||
/// Is this a 3D positional voice.
|
||||
bool is3D() const { return mIs3D; }
|
||||
|
||||
///
|
||||
const X3DAUDIO_EMITTER& getEmitter() const { return mEmitter; }
|
||||
};
|
||||
|
||||
#endif // _SFXXAUDIOVOICE_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue