mirror of
https://github.com/thug1src/thug.git
synced 2024-12-03 13:26:47 +00:00
1364 lines
41 KiB
C++
1364 lines
41 KiB
C++
/*****************************************************************************
|
|
** **
|
|
** Neversoft Entertainment **
|
|
** **
|
|
** Copyright (C) 1999 - All Rights Reserved **
|
|
** **
|
|
******************************************************************************
|
|
** **
|
|
** Project: GEL Library **
|
|
** **
|
|
** Module: Sound effects (Sfx) **
|
|
** **
|
|
** File name: xbox/p_sfx.cpp **
|
|
** **
|
|
** Created by: 01/10/01 - dc **
|
|
** **
|
|
** Description: XBox Sound effects **
|
|
** **
|
|
*****************************************************************************/
|
|
|
|
|
|
/*****************************************************************************
|
|
** Includes **
|
|
*****************************************************************************/
|
|
|
|
#include <xtl.h>
|
|
#include <dsound.h>
|
|
//#include <dsstdfx.h>
|
|
|
|
#include <core/macros.h>
|
|
#include <sys/file/filesys.h>
|
|
#include <gel/soundfx/soundfx.h>
|
|
#include <gel/soundfx/xbox/p_sfx.h>
|
|
#include <gel/soundfx/xbox/skate5fx.h>
|
|
#include <gel/music/music.h>
|
|
|
|
/*****************************************************************************
|
|
** DBG Information **
|
|
*****************************************************************************/
|
|
|
|
int num_buffers = 0;
|
|
|
|
namespace Sfx
|
|
{
|
|
|
|
/*****************************************************************************
|
|
** Externals **
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
** Defines **
|
|
*****************************************************************************/
|
|
|
|
#define RIFFCHUNK_FLAGS_VALID 0x00000001
|
|
|
|
// FourCC definitions
|
|
const DWORD FOURCC_RIFF = 'FFIR';
|
|
const DWORD FOURCC_WAVE = 'EVAW';
|
|
const DWORD FOURCC_FORMAT = ' tmf';
|
|
const DWORD FOURCC_DATA = 'atad';
|
|
const DWORD FOURCC_SAMPLER = 'lpms';
|
|
|
|
/*****************************************************************************
|
|
** Private Types **
|
|
*****************************************************************************/
|
|
|
|
struct VoiceEntry
|
|
{
|
|
IDirectSoundBuffer* p_buffer;
|
|
DWORD default_frequency;
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
WCHAR * szName;
|
|
DSI3DL2LISTENER ds3dl;
|
|
} I3DL2ENVIRONMENT;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Name: RIFFHEADER
|
|
* Desc: For parsing WAV files
|
|
*
|
|
****************************************************************************/
|
|
struct RIFFHEADER
|
|
{
|
|
FOURCC fccChunkId;
|
|
DWORD dwDataSize;
|
|
};
|
|
|
|
|
|
struct SAMPLERCHUNK
|
|
{
|
|
DWORD manufacturer;
|
|
DWORD product;
|
|
DWORD sample_period;
|
|
DWORD MIDI_unity_note;
|
|
DWORD MIDI_pitch_fraction;
|
|
DWORD SMPTE_format;
|
|
DWORD SMPTE_offset;
|
|
DWORD num_sample_loops;
|
|
DWORD sampler_data;
|
|
};
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Name: class CRiffChunk
|
|
* Desc: RIFF chunk utility class
|
|
*
|
|
****************************************************************************/
|
|
class CRiffChunk
|
|
{
|
|
FOURCC m_fccChunkId; // Chunk identifier
|
|
const CRiffChunk* m_pParentChunk; // Parent chunk
|
|
void* m_hFile;
|
|
DWORD m_dwDataOffset; // Chunk data offset
|
|
DWORD m_dwDataSize; // Chunk data size
|
|
DWORD m_dwFlags; // Chunk flags
|
|
|
|
public:
|
|
|
|
CRiffChunk();
|
|
|
|
// Initialization.
|
|
void Initialize( FOURCC fccChunkId, const CRiffChunk* pParentChunk, void* hFile );
|
|
bool Open();
|
|
bool IsValid( void ) { return !!( m_dwFlags & RIFFCHUNK_FLAGS_VALID ); }
|
|
|
|
// Data
|
|
bool ReadData( LONG lOffset, VOID* pData, DWORD dwDataSize );
|
|
|
|
// Chunk information
|
|
FOURCC GetChunkId() { return m_fccChunkId; }
|
|
DWORD GetDataSize() { return m_dwDataSize; }
|
|
};
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Name: class CWaveFile
|
|
* Desc: Wave file utility class
|
|
*
|
|
****************************************************************************/
|
|
class CWaveFile
|
|
{
|
|
void* m_hFile; // File handle
|
|
CRiffChunk m_RiffChunk; // RIFF chunk
|
|
CRiffChunk m_FormatChunk; // Format chunk
|
|
CRiffChunk m_DataChunk; // Data chunk
|
|
CRiffChunk m_SamplerChunk; // Sampler chunk, may or may not be present
|
|
|
|
bool m_ContainsSamplerChunk;
|
|
|
|
public:
|
|
|
|
CWaveFile( void );
|
|
~CWaveFile( void );
|
|
|
|
// Initialization
|
|
bool Open( const char* strFileName );
|
|
void Close( void );
|
|
|
|
// File format
|
|
bool GetFormat( WAVEFORMATEX* pwfxFormat, DWORD dwFormatSize );
|
|
|
|
// File data
|
|
bool ReadSample( DWORD dwPosition, VOID* pBuffer, DWORD dwBufferSize, DWORD* pdwRead );
|
|
|
|
// File properties
|
|
void GetDuration( DWORD* pdwDuration ) { *pdwDuration = m_DataChunk.GetDataSize(); }
|
|
|
|
bool ContainsLoop( void );
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Name: class CSound
|
|
* Desc: Encapsulates functionality of a DirectSound buffer.
|
|
*
|
|
****************************************************************************/
|
|
class CXBSound
|
|
{
|
|
public:
|
|
XBOXADPCMWAVEFORMAT m_WaveFormat; // This encapsulates WAVEFORMATEX.
|
|
DSBUFFERDESC m_dsbd;
|
|
DWORD m_dwBufferSize;
|
|
void* m_pRawData;
|
|
bool m_Loops;
|
|
|
|
bool Create( const CHAR* strFileName, DWORD dwFlags = 0L );
|
|
bool Create( const XBOXADPCMWAVEFORMAT* pwfxFormat, DWORD dwFlags, VOID* pBuffer, DWORD dwBytes );
|
|
void Destroy();
|
|
|
|
DWORD GetSampleRate( void ) const;
|
|
|
|
CXBSound();
|
|
~CXBSound();
|
|
};
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
** Private Data **
|
|
*****************************************************************************/
|
|
|
|
// Because we don't use voices in the same way as on PS2, this array simulates
|
|
// the system. An entry with a NULL pointer means no sound is playing on that
|
|
// 'voice', a valid pointer indicates a sound is playing on that 'voice', and
|
|
// provides details of the buffer.
|
|
static VoiceEntry VoiceSimulator[NUM_VOICES];
|
|
static IDirectSound8* pDirectSound = NULL;
|
|
|
|
static I3DL2ENVIRONMENT reverbEnvironments[] =
|
|
{
|
|
{ L"Default", { DSI3DL2_ENVIRONMENT_PRESET_DEFAULT } },
|
|
{ L"Generic", { DSI3DL2_ENVIRONMENT_PRESET_GENERIC } },
|
|
{ L"Padded Cell", { DSI3DL2_ENVIRONMENT_PRESET_PADDEDCELL } },
|
|
{ L"Room", { DSI3DL2_ENVIRONMENT_PRESET_ROOM } },
|
|
{ L"Bathroom", { DSI3DL2_ENVIRONMENT_PRESET_BATHROOM } },
|
|
{ L"Living Room", { DSI3DL2_ENVIRONMENT_PRESET_LIVINGROOM } },
|
|
{ L"Stone Room", { DSI3DL2_ENVIRONMENT_PRESET_STONEROOM } },
|
|
{ L"Auditorium", { DSI3DL2_ENVIRONMENT_PRESET_AUDITORIUM } },
|
|
{ L"Concert Hall", { DSI3DL2_ENVIRONMENT_PRESET_CONCERTHALL } },
|
|
{ L"Cave", { DSI3DL2_ENVIRONMENT_PRESET_CAVE } },
|
|
{ L"Arena", { DSI3DL2_ENVIRONMENT_PRESET_ARENA } },
|
|
{ L"Hangar", { DSI3DL2_ENVIRONMENT_PRESET_HANGAR } },
|
|
{ L"Carpeted Hallway", { DSI3DL2_ENVIRONMENT_PRESET_CARPETEDHALLWAY }},
|
|
{ L"Hallway", { DSI3DL2_ENVIRONMENT_PRESET_HALLWAY } },
|
|
{ L"Stone Corridor", { DSI3DL2_ENVIRONMENT_PRESET_STONECORRIDOR } },
|
|
{ L"Alley", { DSI3DL2_ENVIRONMENT_PRESET_ALLEY } },
|
|
{ L"Forest", { DSI3DL2_ENVIRONMENT_PRESET_FOREST } },
|
|
{ L"City", { DSI3DL2_ENVIRONMENT_PRESET_CITY } },
|
|
{ L"Mountains", { DSI3DL2_ENVIRONMENT_PRESET_MOUNTAINS } },
|
|
{ L"Quarry", { DSI3DL2_ENVIRONMENT_PRESET_QUARRY } },
|
|
{ L"Plain", { DSI3DL2_ENVIRONMENT_PRESET_PLAIN } },
|
|
{ L"Parking Lot", { DSI3DL2_ENVIRONMENT_PRESET_PARKINGLOT } },
|
|
{ L"Sewer Pipe", { DSI3DL2_ENVIRONMENT_PRESET_SEWERPIPE } },
|
|
{ L"Underwater", { DSI3DL2_ENVIRONMENT_PRESET_UNDERWATER } },
|
|
};
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
** Public Data **
|
|
*****************************************************************************/
|
|
|
|
// Global sfx multiplier, percentage.
|
|
float gSfxVolume = 100.0f;
|
|
|
|
/*****************************************************************************
|
|
** Private Prototypes **
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
** Private Functions **
|
|
*****************************************************************************/
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: CRiffChunk()
|
|
// Desc: Object constructor.
|
|
//-----------------------------------------------------------------------------
|
|
CRiffChunk::CRiffChunk()
|
|
{
|
|
// Initialize defaults
|
|
m_fccChunkId = 0;
|
|
m_pParentChunk = NULL;
|
|
m_hFile = NULL;
|
|
m_dwDataOffset = 0;
|
|
m_dwDataSize = 0;
|
|
m_dwFlags = 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: Initialize()
|
|
// Desc: Initializes the object
|
|
//-----------------------------------------------------------------------------
|
|
VOID CRiffChunk::Initialize( FOURCC fccChunkId, const CRiffChunk* pParentChunk, void* hFile )
|
|
{
|
|
m_fccChunkId = fccChunkId;
|
|
m_pParentChunk = pParentChunk;
|
|
m_hFile = hFile;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: Open()
|
|
// Desc: Opens an existing chunk.
|
|
//-----------------------------------------------------------------------------
|
|
bool CRiffChunk::Open()
|
|
{
|
|
RIFFHEADER rhRiffHeader;
|
|
LONG lOffset = 0;
|
|
|
|
// Seek to the first byte of the parent chunk's data section
|
|
if( m_pParentChunk )
|
|
{
|
|
lOffset = m_pParentChunk->m_dwDataOffset;
|
|
|
|
// Special case the RIFF chunk
|
|
if( FOURCC_RIFF == m_pParentChunk->m_fccChunkId )
|
|
{
|
|
lOffset += sizeof(FOURCC);
|
|
}
|
|
}
|
|
|
|
// Read each child chunk header until we find the one we're looking for
|
|
// RwFileFunctions* fs = RwOsGetFileInterface();
|
|
for( ;; )
|
|
{
|
|
// if( fs->rwfseek( m_hFile, lOffset, SEEK_SET ) != 0 )
|
|
File::Seek( m_hFile, lOffset, SEEK_SET );
|
|
// {
|
|
// return false;
|
|
// }
|
|
|
|
DWORD dwRead;
|
|
// dwRead = fs->rwfread( &rhRiffHeader, sizeof( rhRiffHeader ), 1, m_hFile );
|
|
dwRead = File::Read( &rhRiffHeader, sizeof( rhRiffHeader ), 1, m_hFile );
|
|
if( dwRead != sizeof( rhRiffHeader ))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check if we found the one we're looking for.
|
|
if( m_fccChunkId == rhRiffHeader.fccChunkId )
|
|
{
|
|
// Save the chunk size and data offset.
|
|
m_dwDataOffset = lOffset + sizeof( rhRiffHeader );
|
|
m_dwDataSize = rhRiffHeader.dwDataSize;
|
|
|
|
// Success.
|
|
m_dwFlags |= RIFFCHUNK_FLAGS_VALID;
|
|
|
|
return true;
|
|
}
|
|
lOffset += sizeof( rhRiffHeader ) + rhRiffHeader.dwDataSize;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: Read()
|
|
// Desc: Reads from the file
|
|
//-----------------------------------------------------------------------------
|
|
bool CRiffChunk::ReadData( LONG lOffset, VOID* pData, DWORD dwDataSize )
|
|
{
|
|
// Seek to the offset
|
|
// RwFileFunctions* fs = RwOsGetFileInterface();
|
|
// if( fs->rwfseek( m_hFile, m_dwDataOffset+lOffset, SEEK_SET ) != 0 )
|
|
// if( File::Seek( m_hFile, m_dwDataOffset+lOffset, SEEK_SET ) != 0 )
|
|
File::Seek( m_hFile, m_dwDataOffset + lOffset, SEEK_SET );
|
|
// {
|
|
// return false;
|
|
// }
|
|
|
|
// Read from the file
|
|
DWORD dwRead;
|
|
// dwRead = fs->rwfread( pData, dwDataSize, 1, m_hFile );
|
|
dwRead = File::Read( pData, dwDataSize, 1, m_hFile );
|
|
if( dwRead != dwDataSize )
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: CWaveFile()
|
|
// Desc: Object constructor.
|
|
//-----------------------------------------------------------------------------
|
|
CWaveFile::CWaveFile()
|
|
{
|
|
m_hFile = NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: ~CWaveFile()
|
|
// Desc: Object destructor.
|
|
//-----------------------------------------------------------------------------
|
|
CWaveFile::~CWaveFile()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: Open()
|
|
// Desc: Initializes the object.
|
|
//-----------------------------------------------------------------------------
|
|
bool CWaveFile::Open( const char* strFileName )
|
|
{
|
|
// If we're already open, close.
|
|
Close();
|
|
|
|
// Open the file
|
|
m_hFile = File::Open( strFileName, "rb" );
|
|
|
|
// RwFileFunctions* fs = RwOsGetFileInterface();
|
|
// m_hFile = fs->rwfopen( strFileName, "rb" );
|
|
|
|
if( m_hFile == NULL )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Initialize the chunk objects.
|
|
m_RiffChunk.Initialize( FOURCC_RIFF, NULL, m_hFile );
|
|
m_FormatChunk.Initialize( FOURCC_FORMAT, &m_RiffChunk, m_hFile );
|
|
m_DataChunk.Initialize( FOURCC_DATA, &m_RiffChunk, m_hFile );
|
|
m_SamplerChunk.Initialize( FOURCC_SAMPLER, &m_RiffChunk, m_hFile );
|
|
|
|
bool hr = m_RiffChunk.Open();
|
|
if( !hr )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = m_FormatChunk.Open();
|
|
if( !hr )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = m_DataChunk.Open();
|
|
if( !hr )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
// Not a problem if it doesn't, not every file will contain the sampler chunk.
|
|
m_ContainsSamplerChunk = m_SamplerChunk.Open();
|
|
|
|
// Validate the file type.
|
|
FOURCC fccType;
|
|
hr = m_RiffChunk.ReadData( 0, &fccType, sizeof( fccType ));
|
|
if( !hr )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if( FOURCC_WAVE != fccType )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: GetFormat()
|
|
// Desc: Gets the wave file format
|
|
//-----------------------------------------------------------------------------
|
|
bool CWaveFile::GetFormat( WAVEFORMATEX* pwfxFormat, DWORD dwFormatSize )
|
|
{
|
|
DWORD dwValidSize = m_FormatChunk.GetDataSize();
|
|
|
|
if( NULL == pwfxFormat || 0 == dwFormatSize )
|
|
return false;
|
|
|
|
// Read the format chunk into the buffer
|
|
bool hr = m_FormatChunk.ReadData( 0, pwfxFormat, min( dwFormatSize, dwValidSize ));
|
|
|
|
if( hr )
|
|
{
|
|
// Zero out remaining bytes, in case enough bytes were not read
|
|
if( dwFormatSize > dwValidSize )
|
|
{
|
|
ZeroMemory( (BYTE*)pwfxFormat + dwValidSize, dwFormatSize - dwValidSize );
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: ReadSample()
|
|
// Desc: Reads data from the audio file.
|
|
//-----------------------------------------------------------------------------
|
|
bool CWaveFile::ReadSample( DWORD dwPosition, VOID* pBuffer, DWORD dwBufferSize, DWORD* pdwRead )
|
|
{
|
|
// Don't read past the end of the data chunk
|
|
DWORD dwDuration;
|
|
GetDuration( &dwDuration );
|
|
|
|
if( dwPosition + dwBufferSize > dwDuration )
|
|
dwBufferSize = dwDuration - dwPosition;
|
|
|
|
bool hr = true;
|
|
if( dwBufferSize )
|
|
hr = m_DataChunk.ReadData( (LONG)dwPosition, pBuffer, dwBufferSize );
|
|
|
|
if( pdwRead )
|
|
*pdwRead = dwBufferSize;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name:
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
bool CWaveFile::ContainsLoop( void )
|
|
{
|
|
// Figure out if it is a looping sound.
|
|
if( m_ContainsSamplerChunk )
|
|
{
|
|
SAMPLERCHUNK sc;
|
|
m_SamplerChunk.ReadData( 0, &sc, sizeof( sc ));
|
|
if( sc.num_sample_loops > 0 )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: Close()
|
|
// Desc: Closes the object
|
|
//-----------------------------------------------------------------------------
|
|
void CWaveFile::Close( void )
|
|
{
|
|
if( m_hFile != NULL )
|
|
{
|
|
// RwFileFunctions* fs = RwOsGetFileInterface();
|
|
// fs->rwfclose( m_hFile );
|
|
// m_hFile = NULL;
|
|
File::Close( m_hFile );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: CXBSound()
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
CXBSound::CXBSound( void )
|
|
{
|
|
m_dwBufferSize = 0L;
|
|
m_pRawData = NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: ~CXBSound()
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
CXBSound::~CXBSound( void )
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: Create()
|
|
// Desc: Creates the sound. Sound is buffered to memory allocated internally
|
|
// by DirectSound.
|
|
//-----------------------------------------------------------------------------
|
|
bool CXBSound::Create( const CHAR* strFileName, DWORD dwFlags )
|
|
{
|
|
bool br;
|
|
|
|
// Open the .wav file
|
|
CWaveFile waveFile;
|
|
br = waveFile.Open( strFileName );
|
|
if( !br )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Get the WAVEFORMAT structure for the .wav file
|
|
br = waveFile.GetFormat( &m_WaveFormat.wfx, sizeof( WAVEFORMATEX ));
|
|
if( !br )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Required if the sound is Xbox ADPCM, will be ignored otherwise.
|
|
m_WaveFormat.wSamplesPerBlock = 64;
|
|
|
|
// Get the size of the .wav file
|
|
waveFile.GetDuration( &m_dwBufferSize );
|
|
|
|
// See if the sound is looping.
|
|
m_Loops = waveFile.ContainsLoop();
|
|
|
|
// Create the sound buffer
|
|
br = Create( &m_WaveFormat, dwFlags, NULL, m_dwBufferSize );
|
|
if( !br )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Lock the buffer so it can be filled
|
|
VOID* pLock1 = NULL;
|
|
VOID* pLock2 = NULL;
|
|
DWORD dwLockSize1 = 0L;
|
|
DWORD dwLockSize2 = 0L;
|
|
|
|
pLock1 = new char[m_dsbd.dwBufferBytes];
|
|
|
|
// Read the wave file data into the buffer
|
|
br = waveFile.ReadSample( 0L, pLock1, m_dsbd.dwBufferBytes, NULL );
|
|
if( !br )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Store pointer to raw sound data.
|
|
m_pRawData = pLock1;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: Create()
|
|
// Desc: Creates the sound and tells DirectSound where the sound data will be
|
|
// stored. If pBuffer is NULL, DirectSound handles buffer creation.
|
|
//-----------------------------------------------------------------------------
|
|
bool CXBSound::Create( const XBOXADPCMWAVEFORMAT* pwfxFormat, DWORD dwFlags, VOID* pBuffer, DWORD dwBytes )
|
|
{
|
|
// Setup the sound buffer description.
|
|
ZeroMemory( &m_dsbd, sizeof(DSBUFFERDESC) );
|
|
m_dsbd.dwSize = sizeof(DSBUFFERDESC);
|
|
m_dsbd.dwFlags = dwFlags;
|
|
|
|
if( pwfxFormat->wfx.wFormatTag == WAVE_FORMAT_XBOX_ADPCM )
|
|
{
|
|
m_dsbd.lpwfxFormat = (LPWAVEFORMATEX)&m_WaveFormat;
|
|
}
|
|
else
|
|
{
|
|
m_dsbd.lpwfxFormat = (LPWAVEFORMATEX)&pwfxFormat->wfx;
|
|
}
|
|
|
|
// If pBuffer is non-NULL, dwBufferBytes will be zero, which informs
|
|
// DirectSoundCreateBuffer that we will presently be using SetBufferData().
|
|
// Otherwise, we set dwBufferBytes to the size of the WAV data, potentially
|
|
// including alignment bytes.
|
|
if( pBuffer == NULL )
|
|
{
|
|
m_dsbd.dwBufferBytes = ( 0 == m_WaveFormat.wfx.nBlockAlign ) ? dwBytes :
|
|
dwBytes - ( dwBytes % m_WaveFormat.wfx.nBlockAlign );
|
|
}
|
|
|
|
// If buffer specified, tell DirectSound to use it
|
|
if( pBuffer != NULL )
|
|
{
|
|
// Store pointer to raw sound data.
|
|
m_pRawData = pBuffer;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: Destroy()
|
|
// Desc: Destroys the resources used by the sound
|
|
//-----------------------------------------------------------------------------
|
|
VOID CXBSound::Destroy()
|
|
{
|
|
if( m_pRawData )
|
|
{
|
|
delete [] m_pRawData;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: GetSampleRate
|
|
// Desc: Provides the sample rate
|
|
//-----------------------------------------------------------------------------
|
|
DWORD CXBSound::GetSampleRate( void ) const
|
|
{
|
|
return m_WaveFormat.wfx.nSamplesPerSec;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
static int getFreeVoice( void )
|
|
{
|
|
for( int i = 0; i < NUM_VOICES; ++i )
|
|
{
|
|
if( VoiceSimulator[i].p_buffer == NULL )
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
** Public Functions **
|
|
*****************************************************************************/
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void InitSoundFX( CSfxManager *p_sfx_manager )
|
|
{
|
|
HRESULT hr;
|
|
LPDSEFFECTIMAGEDESC pDesc;
|
|
DSEFFECTIMAGELOC EffectLoc;
|
|
|
|
Dbg_Assert( pDirectSound == NULL );
|
|
|
|
// Set default volume type.
|
|
if( p_sfx_manager )
|
|
{
|
|
p_sfx_manager->SetDefaultVolumeType( VOLUME_TYPE_5_CHANNEL_DOLBY5_1 );
|
|
}
|
|
|
|
// Initialize the 'voice' array.
|
|
for( int i = 0; i < NUM_VOICES; ++i )
|
|
{
|
|
VoiceSimulator[i].p_buffer = NULL;
|
|
}
|
|
|
|
// Create the DirectSound interface.
|
|
hr = DirectSoundCreate( NULL, &pDirectSound, NULL );
|
|
|
|
// HANDLE hFile = CreateFile( "d:\\data\\sounds\\bin\\dsstdfx.bin", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL );
|
|
HANDLE hFile = CreateFile( "d:\\data\\sounds\\bin\\skate5fx.bin", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL );
|
|
if( hFile == INVALID_HANDLE_VALUE )
|
|
{
|
|
Dbg_Assert( 0 );
|
|
}
|
|
else
|
|
{
|
|
// Determine the size of the scratch image by seeking to the end of the file.
|
|
int size = SetFilePointer( hFile, 0, NULL, FILE_END );
|
|
SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
|
|
|
|
// Allocate memory to read the scratch image from disk
|
|
BYTE* p_buffer = new BYTE[size];
|
|
|
|
// Read the image in
|
|
DWORD dwBytesRead;
|
|
BOOL bResult = ReadFile( hFile, p_buffer, size, &dwBytesRead, 0 );
|
|
if( !bResult )
|
|
{
|
|
Dbg_Assert( 0 );
|
|
}
|
|
else
|
|
{
|
|
// Call DSound API to download the image.
|
|
// EffectLoc.dwI3DL2ReverbIndex = I3DL2_CHAIN_I3DL2_REVERB;
|
|
// EffectLoc.dwCrosstalkIndex = I3DL2_CHAIN_XTALK;
|
|
EffectLoc.dwI3DL2ReverbIndex = GraphI3DL2_I3DL2Reverb;
|
|
EffectLoc.dwCrosstalkIndex = GraphXTalk_XTalk;
|
|
hr = pDirectSound->DownloadEffectsImage( p_buffer, size, &EffectLoc, &pDesc );
|
|
|
|
delete[] p_buffer;
|
|
}
|
|
}
|
|
|
|
if( hFile != INVALID_HANDLE_VALUE )
|
|
{
|
|
CloseHandle( hFile );
|
|
}
|
|
}
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void CleanUpSoundFX( void )
|
|
{
|
|
// This just resets the SPU RAM pointer on the PS2. However, on Xbox it needs to explicitly
|
|
// delete any sounds that were not marked as permanent at load time.
|
|
StopAllSoundFX();
|
|
SetReverbPlease( 0 );
|
|
|
|
for( int i = 0; i < NumWavesInTable; ++i )
|
|
{
|
|
PlatformWaveInfo* p_info = &( WaveTable[PERM_WAVE_TABLE_MAX_ENTRIES + i].platformWaveInfo );
|
|
Dbg_Assert( p_info->p_sound_data );
|
|
delete p_info->p_sound_data;
|
|
p_info->p_sound_data = NULL;
|
|
}
|
|
NumWavesInTable = 0;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void StopAllSoundFX( void )
|
|
{
|
|
Pcm::StopMusic();
|
|
Pcm::StopStreams();
|
|
|
|
for( int i = 0; i < NUM_VOICES; ++i )
|
|
{
|
|
StopSoundPlease( i );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
int GetMemAvailable( void )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
bool LoadSoundPlease( const char *sfxName, uint32 checksum, PlatformWaveInfo *pInfo, bool loadPerm )
|
|
{
|
|
Dbg_Assert( pInfo );
|
|
|
|
// Need to append file type. Copy string into a buffer.
|
|
static char name_buffer[256] = "sounds\\pcm\\";
|
|
const int SOUND_PREPEND_START_POS = 11;
|
|
|
|
// Check there is room to add the extension.
|
|
int length = strlen( sfxName );
|
|
Dbg_Assert( strlen( sfxName ) <= ( 256 - ( SOUND_PREPEND_START_POS + 4 )));
|
|
strcpy( name_buffer + SOUND_PREPEND_START_POS, sfxName );
|
|
strcpy( name_buffer + SOUND_PREPEND_START_POS + length, ".pcm" );
|
|
|
|
CXBSound* p_sound = new CXBSound();
|
|
|
|
// Pass the locdefer flag along to prevent this buffer taking up a voice because it will not be played directly.
|
|
bool result = p_sound->Create( name_buffer, DSBCAPS_LOCDEFER );
|
|
|
|
if( result )
|
|
{
|
|
pInfo->p_sound_data = p_sound;
|
|
pInfo->looping = p_sound->m_Loops;
|
|
pInfo->permanent = loadPerm;
|
|
}
|
|
else
|
|
{
|
|
OutputDebugString( name_buffer );
|
|
OutputDebugString( "\n" );
|
|
pInfo->p_sound_data = NULL;
|
|
pInfo->looping = false;
|
|
pInfo->permanent = false;
|
|
delete p_sound;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
//int PlaySoundPlease( PlatformWaveInfo *pInfo, float volL, float volR, float pitch )
|
|
int PlaySoundPlease( PlatformWaveInfo *pInfo, sVolume *p_vol, float pitch )
|
|
{
|
|
Dbg_Assert( pInfo );
|
|
|
|
if( pInfo->p_sound_data )
|
|
{
|
|
// Obtain a free voice.
|
|
int voice = getFreeVoice();
|
|
if( voice >= 0 )
|
|
{
|
|
// Create the dynamic buffer. Need to set buffer bytes to 0, since we will be setting our own data below.
|
|
DWORD temp_flags = pInfo->p_sound_data->m_dsbd.dwFlags;
|
|
DWORD buffer_bytes = pInfo->p_sound_data->m_dsbd.dwBufferBytes;
|
|
|
|
// pInfo->p_sound_data->m_dsbd.dwFlags |= DSBCAPS_CTRLPOSITIONNOTIFY;
|
|
pInfo->p_sound_data->m_dsbd.dwBufferBytes = 0;
|
|
|
|
// Add in the MixBin setup data.
|
|
DSMIXBINS dsmixbins;
|
|
DSMIXBINVOLUMEPAIR dsmbvp[7];
|
|
pInfo->p_sound_data->m_dsbd.lpMixBins = &dsmixbins;
|
|
dsmixbins.dwMixBinCount = 7;
|
|
dsmixbins.lpMixBinVolumePairs = dsmbvp;
|
|
dsmbvp[0].dwMixBin = DSMIXBIN_3D_FRONT_LEFT;
|
|
dsmbvp[0].lVolume = DSBVOLUME_EFFECTIVE_MIN;
|
|
dsmbvp[1].dwMixBin = DSMIXBIN_3D_FRONT_RIGHT;
|
|
dsmbvp[1].lVolume = DSBVOLUME_EFFECTIVE_MIN;
|
|
dsmbvp[2].dwMixBin = DSMIXBIN_FRONT_CENTER;
|
|
dsmbvp[2].lVolume = DSBVOLUME_EFFECTIVE_MIN;
|
|
dsmbvp[3].dwMixBin = DSMIXBIN_LOW_FREQUENCY;
|
|
dsmbvp[3].lVolume = DSBVOLUME_EFFECTIVE_MIN;
|
|
dsmbvp[4].dwMixBin = DSMIXBIN_3D_BACK_LEFT;
|
|
dsmbvp[4].lVolume = DSBVOLUME_EFFECTIVE_MIN;
|
|
dsmbvp[5].dwMixBin = DSMIXBIN_3D_BACK_RIGHT;
|
|
dsmbvp[5].lVolume = DSBVOLUME_EFFECTIVE_MIN;
|
|
dsmbvp[6].dwMixBin = DSMIXBIN_I3DL2;
|
|
dsmbvp[6].lVolume = DSBVOLUME_EFFECTIVE_MIN;
|
|
|
|
HRESULT hr = DirectSoundCreateBuffer( &pInfo->p_sound_data->m_dsbd, &VoiceSimulator[voice].p_buffer );
|
|
|
|
// Reset various buffer description values.
|
|
pInfo->p_sound_data->m_dsbd.dwFlags = temp_flags;
|
|
pInfo->p_sound_data->m_dsbd.dwBufferBytes = buffer_bytes;
|
|
pInfo->p_sound_data->m_dsbd.lpMixBins = NULL;
|
|
|
|
++num_buffers;
|
|
|
|
if( hr == DS_OK )
|
|
{
|
|
// Set the buffer to point to the raw sound data.
|
|
hr = VoiceSimulator[voice].p_buffer->SetBufferData( pInfo->p_sound_data->m_pRawData, pInfo->p_sound_data->m_dsbd.dwBufferBytes );
|
|
if( hr == DS_OK )
|
|
{
|
|
// Set the default sample rate.
|
|
VoiceSimulator[voice].default_frequency = pInfo->p_sound_data->GetSampleRate();
|
|
|
|
// Play the sound.
|
|
hr = VoiceSimulator[voice].p_buffer->Play( 0, 0, ( pInfo->looping ) ? DSBPLAY_LOOPING : 0 );
|
|
if( hr == DS_OK )
|
|
{
|
|
// SetVoiceParameters( voice, volL, volR, pitch );
|
|
SetVoiceParameters( voice, p_vol, pitch );
|
|
return voice;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void StopSoundPlease( int voice )
|
|
{
|
|
if( VoiceSimulator[voice].p_buffer )
|
|
{
|
|
HRESULT hr = VoiceSimulator[voice].p_buffer->Stop();
|
|
Dbg_Assert( hr == DS_OK )
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void SetVolumePlease( float volumeLevel )
|
|
{
|
|
gSfxVolume = volumeLevel;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void PauseSoundsPlease( void )
|
|
{
|
|
sVolume vol;
|
|
vol.SetSilent();
|
|
|
|
// Just turn the volume down on all playing voices...
|
|
for( int i = 0; i < NUM_VOICES; i++ )
|
|
{
|
|
if( VoiceIsOn( i ))
|
|
{
|
|
// SetVoiceParameters( i, 0.0f, 0.0f );
|
|
SetVoiceParameters( i, &vol );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void SetReverbPlease( float reverbLevel, int reverbMode, bool instant )
|
|
{
|
|
if( reverbMode == 0 )
|
|
{
|
|
// Default to plain.
|
|
reverbMode = 20;
|
|
}
|
|
reverbMode = 20;
|
|
|
|
HRESULT hr = pDirectSound->SetI3DL2Listener( &reverbEnvironments[reverbMode].ds3dl, DS3D_IMMEDIATE );
|
|
Dbg_Assert( hr == DS_OK );
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
bool VoiceIsOn( int voice )
|
|
{
|
|
return( VoiceSimulator[voice].p_buffer != NULL );
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
//void SetVoiceParameters( int voice, float volL, float volR, float pitch )
|
|
void SetVoiceParameters( int voice, sVolume *p_vol, float pitch )
|
|
{
|
|
if( VoiceSimulator[voice].p_buffer )
|
|
{
|
|
// This array will hold individual volumes for the five speakers.
|
|
// In order, they are: front left, center, front right, rear right, rear left.
|
|
float volumes[5];
|
|
int i_volumes[5], max_i_volume;
|
|
|
|
memset( volumes, 0, sizeof( float ) * 5 );
|
|
|
|
if( p_vol->IsSilent())
|
|
{
|
|
// Pointless doing any more work.
|
|
}
|
|
else
|
|
{
|
|
switch( p_vol->GetVolumeType())
|
|
{
|
|
case VOLUME_TYPE_5_CHANNEL_DOLBY5_1:
|
|
{
|
|
// All the work has been done already - just copy volumes over.
|
|
volumes[0] = p_vol->GetChannelVolume( 0 );
|
|
volumes[1] = p_vol->GetChannelVolume( 4 );
|
|
volumes[2] = p_vol->GetChannelVolume( 1 );
|
|
volumes[3] = p_vol->GetChannelVolume( 2 );
|
|
volumes[4] = p_vol->GetChannelVolume( 3 );
|
|
break;
|
|
}
|
|
|
|
case VOLUME_TYPE_BASIC_2_CHANNEL:
|
|
{
|
|
// Simple two channel sound, so just setup as such.
|
|
volumes[0] = p_vol->GetChannelVolume( 0 );
|
|
volumes[1] = ( p_vol->GetChannelVolume( 0 ) + p_vol->GetChannelVolume( 1 )) * 0.5f;
|
|
volumes[2] = p_vol->GetChannelVolume( 1 );
|
|
volumes[3] = p_vol->GetChannelVolume( 0 ) * 0.5f;
|
|
volumes[4] = p_vol->GetChannelVolume( 1 ) * 0.5f;
|
|
break;
|
|
}
|
|
|
|
case VOLUME_TYPE_2_CHANNEL_DOLBYII:
|
|
default:
|
|
{
|
|
// Wes should not get any of these.
|
|
Dbg_Assert( 0 );
|
|
|
|
# if 0
|
|
// Get the length of the vector here which will be used to multiply out the normalised speaker volumes.
|
|
Mth::Vector test( fabsf( p_vol->GetChannelVolume( 0 )), fabsf( p_vol->GetChannelVolume( 1 )), 0.0f, 0.0f );
|
|
float amplitude = test.Length();
|
|
|
|
// Look just at the normalized right component to figure the sound angle from Matt's calculations.
|
|
test.Normalize();
|
|
|
|
float angle;
|
|
angle = asinf( test[Y] );
|
|
angle = ( angle * 2.0f ) - ( Mth::PI * 0.5f );
|
|
angle = ( p_vol->GetChannelVolume( 0 ) < 0.0f ) ? ( Mth::PI - angle ) : angle;
|
|
|
|
|
|
// Now figure volumes based on speaker coverage.
|
|
angle = Mth::RadToDeg( angle );
|
|
|
|
// Left front channel.
|
|
if(( angle >= 225.0f ) || ( angle <= 45.0f ))
|
|
{
|
|
// Because of the discontinuity, shift this angle round to the [0,180] range.
|
|
float shift_angle = angle + 135;
|
|
shift_angle = ( shift_angle >= 360.0f ) ? ( shift_angle - 360.0f ) : shift_angle;
|
|
volumes[0] = ( shift_angle / 180.0f ) * Mth::PI;
|
|
volumes[0] = sqrtf( sinf( volumes[0] ));
|
|
}
|
|
|
|
// Center channel.
|
|
if(( angle >= -60.0f ) && ( angle <= 60.0f ))
|
|
{
|
|
// Scale this into [0,PI] space so we can get smooth fadeout.
|
|
volumes[1] = (( angle + 60.0f ) / 120.0f ) * Mth::PI;
|
|
volumes[1] = sqrtf( sinf( volumes[1] ));
|
|
}
|
|
|
|
// Right front channel.
|
|
if(( angle >= -45.0f ) && ( angle <= 135.0f ))
|
|
{
|
|
// Scale this into [0,PI] space so we can get smooth fadeout.
|
|
volumes[2] = (( angle + 45.0f ) / 180.0f ) * Mth::PI;
|
|
volumes[2] = sqrtf( sinf( volumes[2] ));
|
|
}
|
|
|
|
// Right rear channel.
|
|
if(( angle >= 45.0f ) && ( angle <= 225.0f ))
|
|
{
|
|
// Scale this into [0,PI] space so we can get smooth fadeout.
|
|
volumes[3] = (( angle - 45.0f ) / 180.0f ) * Mth::PI;
|
|
volumes[3] = sqrtf( sinf( volumes[3] ));
|
|
}
|
|
|
|
// Left rear channel.
|
|
if(( angle >= 135.0f ) || ( angle <= -45.0f ))
|
|
{
|
|
// Because of the discontinuity, shift this angle round to the [0,180] range.
|
|
float shift_angle = angle + 225;
|
|
shift_angle = ( shift_angle >= 360.0f ) ? ( shift_angle - 360.0f ) : shift_angle;
|
|
volumes[4] = ( shift_angle / 180.0f ) * Mth::PI;
|
|
volumes[4] = sqrtf( sinf( volumes[4] ));
|
|
}
|
|
|
|
// Scale back up to original amplitude.
|
|
for( int v = 0; v < 5; ++v )
|
|
{
|
|
volumes[v] *= amplitude;
|
|
}
|
|
# endif
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now readjust the relative values...
|
|
for( int v = 0; v < 5; ++v )
|
|
{
|
|
// Adjust for SFX volume level, and scale into limits.
|
|
volumes[v] = fabsf( PERCENT( gSfxVolume, volumes[v] ));
|
|
|
|
if( volumes[v] > 100.0f )
|
|
volumes[v] = 100.0f;
|
|
}
|
|
|
|
// Now figure the attenuation of the sound. To convert to a decibel value, figure the ratio of requested
|
|
// volume versus max volume, then calculate the log10 and multiply by (10 * 2). (The 2 is because sound
|
|
// power varies as square of pressure, and squaring doubles the log value).
|
|
max_i_volume = DSBVOLUME_EFFECTIVE_MIN;
|
|
for( int v = 0; v < 5; ++v )
|
|
{
|
|
if( volumes[v] > 0.0f )
|
|
{
|
|
float attenuation = 20.0f * log10f( volumes[v] * 0.01f );
|
|
i_volumes[v] = DSBVOLUME_MAX + (int)( attenuation * 100.0f );
|
|
if( i_volumes[v] < DSBVOLUME_EFFECTIVE_MIN )
|
|
i_volumes[v] = DSBVOLUME_EFFECTIVE_MIN;
|
|
|
|
if( i_volumes[v] > max_i_volume )
|
|
max_i_volume = i_volumes[v];
|
|
}
|
|
else
|
|
{
|
|
i_volumes[v] = DSBVOLUME_EFFECTIVE_MIN;
|
|
}
|
|
}
|
|
|
|
// Set individual mixbins for panning.
|
|
DSMIXBINS dsmixbins;
|
|
DSMIXBINVOLUMEPAIR dsmbvp[DSMIXBIN_ASSIGNMENT_MAX];
|
|
|
|
dsmixbins.dwMixBinCount = 0;
|
|
dsmixbins.lpMixBinVolumePairs = dsmbvp;
|
|
|
|
if( i_volumes[0] > DSBVOLUME_EFFECTIVE_MIN )
|
|
{
|
|
dsmbvp[dsmixbins.dwMixBinCount].dwMixBin = DSMIXBIN_3D_FRONT_LEFT;
|
|
dsmbvp[dsmixbins.dwMixBinCount].lVolume = i_volumes[0];
|
|
dsmixbins.dwMixBinCount++;
|
|
}
|
|
|
|
if( i_volumes[1] > DSBVOLUME_EFFECTIVE_MIN )
|
|
{
|
|
dsmbvp[dsmixbins.dwMixBinCount].dwMixBin = DSMIXBIN_FRONT_CENTER;
|
|
dsmbvp[dsmixbins.dwMixBinCount].lVolume = i_volumes[1];
|
|
dsmixbins.dwMixBinCount++;
|
|
}
|
|
|
|
if( i_volumes[2] > DSBVOLUME_EFFECTIVE_MIN )
|
|
{
|
|
dsmbvp[dsmixbins.dwMixBinCount].dwMixBin = DSMIXBIN_3D_FRONT_RIGHT;
|
|
dsmbvp[dsmixbins.dwMixBinCount].lVolume = i_volumes[2];
|
|
dsmixbins.dwMixBinCount++;
|
|
}
|
|
|
|
if( i_volumes[3] > DSBVOLUME_EFFECTIVE_MIN )
|
|
{
|
|
dsmbvp[dsmixbins.dwMixBinCount].dwMixBin = DSMIXBIN_3D_BACK_RIGHT;
|
|
dsmbvp[dsmixbins.dwMixBinCount].lVolume = i_volumes[3];
|
|
dsmixbins.dwMixBinCount++;
|
|
}
|
|
|
|
if( i_volumes[4] > DSBVOLUME_EFFECTIVE_MIN )
|
|
{
|
|
dsmbvp[dsmixbins.dwMixBinCount].dwMixBin = DSMIXBIN_3D_BACK_LEFT;
|
|
dsmbvp[dsmixbins.dwMixBinCount].lVolume = i_volumes[4];
|
|
dsmixbins.dwMixBinCount++;
|
|
}
|
|
|
|
if( dsmixbins.dwMixBinCount > 0 )
|
|
{
|
|
dsmbvp[dsmixbins.dwMixBinCount].dwMixBin = DSMIXBIN_LOW_FREQUENCY;
|
|
dsmbvp[dsmixbins.dwMixBinCount].lVolume = max_i_volume;
|
|
dsmixbins.dwMixBinCount++;
|
|
|
|
dsmbvp[dsmixbins.dwMixBinCount].dwMixBin = DSMIXBIN_I3DL2;
|
|
dsmbvp[dsmixbins.dwMixBinCount].lVolume = DSBVOLUME_MAX;
|
|
dsmixbins.dwMixBinCount++;
|
|
}
|
|
|
|
// Set all speaker volumes.
|
|
HRESULT hr = VoiceSimulator[voice].p_buffer->SetMixBinVolumes( &dsmixbins );
|
|
|
|
// Set overall buffer volume.
|
|
if( dsmixbins.dwMixBinCount > 0 )
|
|
{
|
|
hr = VoiceSimulator[voice].p_buffer->SetVolume( DSBVOLUME_MAX );
|
|
}
|
|
else
|
|
{
|
|
hr = VoiceSimulator[voice].p_buffer->SetVolume( DSBVOLUME_EFFECTIVE_MIN );
|
|
|
|
// Pointless doing pitch calculation if volume is zero.
|
|
return;
|
|
}
|
|
|
|
// Same kind of deal for pitch. Each consecutive doubling (or halving) of the pitch percentage
|
|
// raises (or lowers) the pitch by 1 octave. So we need to figure x, where 2 ^ x = percentage / 100.
|
|
// Can do this with logs...
|
|
int ipitch;
|
|
|
|
// Matt appears to be doing an extra adjustment to the pitch on the Ps2 to account for sample rates
|
|
// not at 48,000Hz. Whilst this seems an unnecessary step, we need to do the same adjustment here.
|
|
// Adjust pitch to account for lower sampling rates.
|
|
// pitch = PERCENT( pitch, ((float)VoiceSimulator[voice].default_frequency / 48000.0f ) * 100.0f );
|
|
|
|
if( pitch != 100.0f )
|
|
{
|
|
ipitch = (int)(( VoiceSimulator[voice].default_frequency * pitch ) * 0.01f );
|
|
if( ipitch > DSBFREQUENCY_MAX )
|
|
{
|
|
ipitch = DSBFREQUENCY_MAX;
|
|
}
|
|
else if( ipitch < DSBFREQUENCY_MIN )
|
|
{
|
|
ipitch = DSBFREQUENCY_MIN;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ipitch = VoiceSimulator[voice].default_frequency;
|
|
}
|
|
hr = VoiceSimulator[voice].p_buffer->SetFrequency( ipitch );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void PerFrameUpdate( void )
|
|
{
|
|
DirectSoundDoWork();
|
|
|
|
int num_used_voices = 0;
|
|
|
|
for( int i = 0; i < NUM_VOICES; ++i )
|
|
{
|
|
if( VoiceSimulator[i].p_buffer )
|
|
{
|
|
DWORD status;
|
|
HRESULT hr = VoiceSimulator[i].p_buffer->GetStatus( &status );
|
|
if(( hr == DS_OK ) && ( status == 0 ))
|
|
{
|
|
// This sound has stopped playing, so we can release the buffer.
|
|
hr = VoiceSimulator[i].p_buffer->Release();
|
|
|
|
--num_buffers;
|
|
|
|
Dbg_Assert( hr == 0 );
|
|
|
|
VoiceSimulator[i].p_buffer = NULL;
|
|
}
|
|
else
|
|
{
|
|
++num_used_voices;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
} // namespace Sfx
|