You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2096 lines
56 KiB
2096 lines
56 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//=====================================================================================// |
|
|
|
#include "audio_pch.h" |
|
#include <dsound.h> |
|
#pragma warning(disable : 4201) // nameless struct/union |
|
#include <ks.h> |
|
// Fix for VS 2010 build errors copied from Dota |
|
#if !defined( NEW_DXSDK ) && ( _MSC_VER >= 1600 ) |
|
#undef KSDATAFORMAT_SUBTYPE_WAVEFORMATEX |
|
//#undef KSDATAFORMAT_SUBTYPE_PCM |
|
#undef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT |
|
#endif |
|
#include <ksmedia.h> |
|
#include "iprediction.h" |
|
#include "eax.h" |
|
#include "tier0/icommandline.h" |
|
#include "video//ivideoservices.h" |
|
#include "sys_dll.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
extern bool snd_firsttime; |
|
|
|
extern void DEBUG_StartSoundMeasure(int type, int samplecount ); |
|
extern void DEBUG_StopSoundMeasure(int type, int samplecount ); |
|
|
|
// legacy support |
|
extern ConVar sxroom_off; |
|
extern ConVar sxroom_type; |
|
extern ConVar sxroomwater_type; |
|
extern float sxroom_typeprev; |
|
|
|
extern HWND* pmainwindow; |
|
|
|
typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat; |
|
|
|
#define SECONDARY_BUFFER_SIZE 0x10000 // output buffer size in bytes |
|
#define SECONDARY_BUFFER_SIZE_SURROUND 0x04000 // output buffer size in bytes, one per channel |
|
|
|
#if !defined( NEW_DXSDK ) |
|
// hack - need to include latest dsound.h |
|
#undef DSSPEAKER_5POINT1 |
|
#undef DSSPEAKER_7POINT1 |
|
#undef DSSPEAKER_7POINT1_SURROUND |
|
#undef DSSPEAKER_5POINT1_SURROUND |
|
#define DSSPEAKER_5POINT1 6 |
|
#define DSSPEAKER_7POINT1 7 |
|
#define DSSPEAKER_7POINT1_SURROUND 8 |
|
#define DSSPEAKER_5POINT1_SURROUND 9 |
|
#endif |
|
|
|
HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter); |
|
|
|
extern void ReleaseSurround(void); |
|
extern bool MIX_ScaleChannelVolume( paintbuffer_t *ppaint, channel_t *pChannel, int volume[CCHANVOLUMES], int mixchans ); |
|
void OnSndSurroundCvarChanged( IConVar *var, const char *pOldString, float flOldValue ); |
|
void OnSndSurroundLegacyChanged( IConVar *var, const char *pOldString, float flOldValue ); |
|
void OnSndVarChanged( IConVar *var, const char *pOldString, float flOldValue ); |
|
|
|
static LPDIRECTSOUND pDS = NULL; |
|
static LPDIRECTSOUNDBUFFER pDSBuf = NULL, pDSPBuf = NULL; |
|
|
|
static GUID IID_IDirectSound3DBufferDef = {0x279AFA86, 0x4981, 0x11CE, {0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60}}; |
|
static ConVar windows_speaker_config("windows_speaker_config", "-1", FCVAR_ARCHIVE); |
|
static DWORD g_ForcedSpeakerConfig = 0; |
|
|
|
extern ConVar snd_mute_losefocus; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Implementation of direct sound |
|
//----------------------------------------------------------------------------- |
|
class CAudioDirectSound : public CAudioDeviceBase |
|
{ |
|
public: |
|
~CAudioDirectSound( void ); |
|
bool IsActive( void ) { return true; } |
|
bool Init( void ); |
|
void Shutdown( void ); |
|
void Pause( void ); |
|
void UnPause( void ); |
|
float MixDryVolume( void ); |
|
bool Should3DMix( void ); |
|
void StopAllSounds( void ); |
|
|
|
int PaintBegin( float mixAheadTime, int soundtime, int paintedtime ); |
|
void PaintEnd( void ); |
|
|
|
int GetOutputPosition( void ); |
|
void ClearBuffer( void ); |
|
void UpdateListener( const Vector& position, const Vector& forward, const Vector& right, const Vector& up ); |
|
|
|
void ChannelReset( int entnum, int channelIndex, float distanceMod ); |
|
void TransferSamples( int end ); |
|
|
|
const char *DeviceName( void ); |
|
int DeviceChannels( void ) { return m_deviceChannels; } |
|
int DeviceSampleBits( void ) { return m_deviceSampleBits; } |
|
int DeviceSampleBytes( void ) { return m_deviceSampleBits/8; } |
|
int DeviceDmaSpeed( void ) { return m_deviceDmaSpeed; } |
|
int DeviceSampleCount( void ) { return m_deviceSampleCount; } |
|
|
|
bool IsInterleaved() { return m_isInterleaved; } |
|
|
|
// Singleton object |
|
static CAudioDirectSound *m_pSingleton; |
|
|
|
private: |
|
void DetectWindowsSpeakerSetup(); |
|
bool LockDSBuffer( LPDIRECTSOUNDBUFFER pBuffer, DWORD **pdwWriteBuffer, DWORD *pdwSizeBuffer, const char *pBufferName, int lockFlags = 0 ); |
|
bool IsUsingBufferPerSpeaker(); |
|
|
|
sndinitstat SNDDMA_InitDirect( void ); |
|
bool SNDDMA_InitInterleaved( LPDIRECTSOUND lpDS, WAVEFORMATEX* lpFormat, int channelCount ); |
|
bool SNDDMA_InitSurround(LPDIRECTSOUND lpDS, WAVEFORMATEX* lpFormat, DSBCAPS* lpdsbc, int cchan); |
|
void S_TransferSurround16( portable_samplepair_t *pfront, portable_samplepair_t *prear, portable_samplepair_t *pcenter, int lpaintedtime, int endtime, int cchan); |
|
void S_TransferSurround16Interleaved( const portable_samplepair_t *pfront, const portable_samplepair_t *prear, const portable_samplepair_t *pcenter, int lpaintedtime, int endtime); |
|
void S_TransferSurround16Interleaved_FullLock( const portable_samplepair_t *pfront, const portable_samplepair_t *prear, const portable_samplepair_t *pcenter, int lpaintedtime, int endtime); |
|
|
|
int m_deviceChannels; // channels per hardware output buffer (1 for quad/5.1, 2 for stereo) |
|
int m_deviceSampleBits; // bits per sample (16) |
|
int m_deviceSampleCount; // count of mono samples in output buffer |
|
int m_deviceDmaSpeed; // samples per second per output buffer |
|
int m_bufferSizeBytes; // size of a single hardware output buffer, in bytes |
|
|
|
DWORD m_outputBufferStartOffset; // output buffer playback starting byte offset |
|
HINSTANCE m_hInstDS; |
|
bool m_isInterleaved; |
|
}; |
|
|
|
CAudioDirectSound *CAudioDirectSound::m_pSingleton = NULL; |
|
|
|
LPDIRECTSOUNDBUFFER pDSBufFL = NULL; |
|
LPDIRECTSOUNDBUFFER pDSBufFR = NULL; |
|
LPDIRECTSOUNDBUFFER pDSBufRL = NULL; |
|
LPDIRECTSOUNDBUFFER pDSBufRR = NULL; |
|
LPDIRECTSOUNDBUFFER pDSBufFC = NULL; |
|
LPDIRECTSOUND3DBUFFER pDSBuf3DFL = NULL; |
|
LPDIRECTSOUND3DBUFFER pDSBuf3DFR = NULL; |
|
LPDIRECTSOUND3DBUFFER pDSBuf3DRL = NULL; |
|
LPDIRECTSOUND3DBUFFER pDSBuf3DRR = NULL; |
|
LPDIRECTSOUND3DBUFFER pDSBuf3DFC = NULL; |
|
|
|
// ----------------------------------------------------------------------------- // |
|
// Helpers. |
|
// ----------------------------------------------------------------------------- // |
|
|
|
|
|
CAudioDirectSound::~CAudioDirectSound( void ) |
|
{ |
|
m_pSingleton = NULL; |
|
} |
|
|
|
bool CAudioDirectSound::Init( void ) |
|
{ |
|
m_hInstDS = NULL; |
|
|
|
static bool first = true; |
|
if ( first ) |
|
{ |
|
snd_surround.InstallChangeCallback( &OnSndSurroundCvarChanged ); |
|
snd_legacy_surround.InstallChangeCallback( &OnSndSurroundLegacyChanged ); |
|
snd_mute_losefocus.InstallChangeCallback( &OnSndVarChanged ); |
|
first = false; |
|
} |
|
|
|
if ( SNDDMA_InitDirect() == SIS_SUCCESS) |
|
{ |
|
if ( g_pVideo != NULL ) |
|
{ |
|
g_pVideo->SoundDeviceCommand( VideoSoundDeviceOperation::SET_DIRECT_SOUND_DEVICE, pDS ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
void CAudioDirectSound::Shutdown( void ) |
|
{ |
|
ReleaseSurround(); |
|
|
|
if (pDSBuf) |
|
{ |
|
pDSBuf->Stop(); |
|
pDSBuf->Release(); |
|
} |
|
|
|
// only release primary buffer if it's not also the mixing buffer we just released |
|
if (pDSPBuf && (pDSBuf != pDSPBuf)) |
|
{ |
|
pDSPBuf->Release(); |
|
} |
|
|
|
if (pDS) |
|
{ |
|
pDS->SetCooperativeLevel(*pmainwindow, DSSCL_NORMAL); |
|
pDS->Release(); |
|
} |
|
|
|
pDS = NULL; |
|
pDSBuf = NULL; |
|
pDSPBuf = NULL; |
|
|
|
if ( m_hInstDS ) |
|
{ |
|
FreeLibrary( m_hInstDS ); |
|
m_hInstDS = NULL; |
|
} |
|
|
|
if ( this == CAudioDirectSound::m_pSingleton ) |
|
{ |
|
CAudioDirectSound::m_pSingleton = NULL; |
|
} |
|
} |
|
|
|
// Total number of samples that have played out to hardware |
|
// for current output buffer (ie: from buffer offset start). |
|
// return playback position within output playback buffer: |
|
// the output units are dependant on the device channels |
|
// so the ouput units for a 2 channel device are as 16 bit LR pairs |
|
// and the output unit for a 1 channel device are as 16 bit mono samples. |
|
// take into account the original start position within the buffer, and |
|
// calculate difference between current position (with buffer wrap) and |
|
// start position. |
|
int CAudioDirectSound::GetOutputPosition( void ) |
|
{ |
|
int samp16; |
|
int start, current; |
|
DWORD dwCurrent; |
|
|
|
// get size in bytes of output buffer |
|
const int size_bytes = m_bufferSizeBytes; |
|
if ( IsUsingBufferPerSpeaker() ) |
|
{ |
|
// mono output buffers |
|
// get byte offset of playback cursor in Front Left output buffer |
|
pDSBufFL->GetCurrentPosition(&dwCurrent, NULL); |
|
|
|
start = (int) m_outputBufferStartOffset; |
|
current = (int) dwCurrent; |
|
} |
|
else |
|
{ |
|
// multi-channel interleavd output buffer |
|
// get byte offset of playback cursor in output buffer |
|
pDSBuf->GetCurrentPosition(&dwCurrent, NULL); |
|
|
|
start = (int) m_outputBufferStartOffset; |
|
current = (int) dwCurrent; |
|
} |
|
|
|
// get 16 bit samples played, relative to buffer starting offset |
|
if (current > start) |
|
{ |
|
// get difference & convert to 16 bit mono samples |
|
samp16 = (current - start) >> SAMPLE_16BIT_SHIFT; |
|
} |
|
else |
|
{ |
|
// get difference (with buffer wrap) convert to 16 bit mono samples |
|
samp16 = ((size_bytes - start) + current) >> SAMPLE_16BIT_SHIFT; |
|
} |
|
|
|
int outputPosition = samp16 / DeviceChannels(); |
|
|
|
return outputPosition; |
|
} |
|
|
|
void CAudioDirectSound::Pause( void ) |
|
{ |
|
if (pDSBuf) |
|
{ |
|
pDSBuf->Stop(); |
|
} |
|
|
|
if ( pDSBufFL ) pDSBufFL->Stop(); |
|
if ( pDSBufFR ) pDSBufFR->Stop(); |
|
if ( pDSBufRL ) pDSBufRL->Stop(); |
|
if ( pDSBufRR ) pDSBufRR->Stop(); |
|
if ( pDSBufFC ) pDSBufFC->Stop(); |
|
} |
|
|
|
|
|
void CAudioDirectSound::UnPause( void ) |
|
{ |
|
if (pDSBuf) |
|
pDSBuf->Play(0, 0, DSBPLAY_LOOPING); |
|
|
|
if (pDSBufFL) pDSBufFL->Play(0, 0, DSBPLAY_LOOPING); |
|
if (pDSBufFR) pDSBufFR->Play(0, 0, DSBPLAY_LOOPING); |
|
if (pDSBufRL) pDSBufRL->Play(0, 0, DSBPLAY_LOOPING); |
|
if (pDSBufRR) pDSBufRR->Play( 0, 0, DSBPLAY_LOOPING); |
|
if (pDSBufFC) pDSBufFC->Play( 0, 0, DSBPLAY_LOOPING); |
|
} |
|
|
|
|
|
float CAudioDirectSound::MixDryVolume( void ) |
|
{ |
|
return 0; |
|
} |
|
|
|
|
|
bool CAudioDirectSound::Should3DMix( void ) |
|
{ |
|
if ( m_bSurround ) |
|
return true; |
|
return false; |
|
} |
|
|
|
|
|
IAudioDevice *Audio_CreateDirectSoundDevice( void ) |
|
{ |
|
if ( !CAudioDirectSound::m_pSingleton ) |
|
CAudioDirectSound::m_pSingleton = new CAudioDirectSound; |
|
|
|
if ( CAudioDirectSound::m_pSingleton->Init() ) |
|
{ |
|
if (snd_firsttime) |
|
DevMsg ("DirectSound initialized\n"); |
|
|
|
return CAudioDirectSound::m_pSingleton; |
|
} |
|
|
|
DevMsg ("DirectSound failed to init\n"); |
|
|
|
delete CAudioDirectSound::m_pSingleton; |
|
CAudioDirectSound::m_pSingleton = NULL; |
|
|
|
return NULL; |
|
} |
|
|
|
int CAudioDirectSound::PaintBegin( float mixAheadTime, int soundtime, int lpaintedtime ) |
|
{ |
|
// soundtime - total full samples that have been played out to hardware at dmaspeed |
|
// paintedtime - total full samples that have been mixed at speed |
|
// endtime - target for full samples in mixahead buffer at speed |
|
// samps - size of output buffer in full samples |
|
|
|
int mixaheadtime = mixAheadTime * DeviceDmaSpeed(); |
|
int endtime = soundtime + mixaheadtime; |
|
|
|
if ( endtime <= lpaintedtime ) |
|
return endtime; |
|
|
|
uint nSamples = endtime - lpaintedtime; |
|
if ( nSamples & 0x3 ) |
|
{ |
|
// The difference between endtime and painted time should align on |
|
// boundaries of 4 samples. This is important when upsampling from 11khz -> 44khz. |
|
nSamples += (4 - (nSamples & 3)); |
|
} |
|
// clamp to min 512 samples per mix |
|
if ( nSamples > 0 && nSamples < 512 ) |
|
{ |
|
nSamples = 512; |
|
} |
|
endtime = lpaintedtime + nSamples; |
|
|
|
int fullsamps = DeviceSampleCount() / DeviceChannels(); |
|
if ( (endtime - soundtime) > fullsamps) |
|
{ |
|
endtime = soundtime + fullsamps; |
|
endtime += (4 - (endtime & 3)); |
|
} |
|
|
|
DWORD dwStatus; |
|
|
|
// If using surround, there are 4 or 5 different buffers being used and the pDSBuf is NULL. |
|
if ( IsUsingBufferPerSpeaker() ) |
|
{ |
|
if (pDSBufFL->GetStatus(&dwStatus) != DS_OK) |
|
Msg ("Couldn't get SURROUND FL sound buffer status\n"); |
|
|
|
if (dwStatus & DSBSTATUS_BUFFERLOST) |
|
pDSBufFL->Restore(); |
|
|
|
if (!(dwStatus & DSBSTATUS_PLAYING)) |
|
pDSBufFL->Play(0, 0, DSBPLAY_LOOPING); |
|
|
|
if (pDSBufFR->GetStatus(&dwStatus) != DS_OK) |
|
Msg ("Couldn't get SURROUND FR sound buffer status\n"); |
|
|
|
if (dwStatus & DSBSTATUS_BUFFERLOST) |
|
pDSBufFR->Restore(); |
|
|
|
if (!(dwStatus & DSBSTATUS_PLAYING)) |
|
pDSBufFR->Play(0, 0, DSBPLAY_LOOPING); |
|
|
|
if (pDSBufRL->GetStatus(&dwStatus) != DS_OK) |
|
Msg ("Couldn't get SURROUND RL sound buffer status\n"); |
|
|
|
if (dwStatus & DSBSTATUS_BUFFERLOST) |
|
pDSBufRL->Restore(); |
|
|
|
if (!(dwStatus & DSBSTATUS_PLAYING)) |
|
pDSBufRL->Play(0, 0, DSBPLAY_LOOPING); |
|
|
|
if (pDSBufRR->GetStatus(&dwStatus) != DS_OK) |
|
Msg ("Couldn't get SURROUND RR sound buffer status\n"); |
|
|
|
if (dwStatus & DSBSTATUS_BUFFERLOST) |
|
pDSBufRR->Restore(); |
|
|
|
if (!(dwStatus & DSBSTATUS_PLAYING)) |
|
pDSBufRR->Play(0, 0, DSBPLAY_LOOPING); |
|
|
|
if ( m_bSurroundCenter ) |
|
{ |
|
if (pDSBufFC->GetStatus(&dwStatus) != DS_OK) |
|
Msg ("Couldn't get SURROUND FC sound buffer status\n"); |
|
|
|
if (dwStatus & DSBSTATUS_BUFFERLOST) |
|
pDSBufFC->Restore(); |
|
|
|
if (!(dwStatus & DSBSTATUS_PLAYING)) |
|
pDSBufFC->Play(0, 0, DSBPLAY_LOOPING); |
|
} |
|
} |
|
else if (pDSBuf) |
|
{ |
|
if ( pDSBuf->GetStatus (&dwStatus) != DS_OK ) |
|
Msg("Couldn't get sound buffer status\n"); |
|
|
|
if ( dwStatus & DSBSTATUS_BUFFERLOST ) |
|
pDSBuf->Restore(); |
|
|
|
if ( !(dwStatus & DSBSTATUS_PLAYING) ) |
|
pDSBuf->Play(0, 0, DSBPLAY_LOOPING); |
|
} |
|
|
|
return endtime; |
|
} |
|
|
|
|
|
void CAudioDirectSound::PaintEnd( void ) |
|
{ |
|
} |
|
|
|
|
|
void CAudioDirectSound::ClearBuffer( void ) |
|
{ |
|
int clear; |
|
|
|
DWORD dwSizeFL, dwSizeFR, dwSizeRL, dwSizeRR, dwSizeFC; |
|
char *pDataFL, *pDataFR, *pDataRL, *pDataRR, *pDataFC; |
|
|
|
dwSizeFC = 0; // compiler warning |
|
pDataFC = NULL; |
|
|
|
if ( IsUsingBufferPerSpeaker() ) |
|
{ |
|
int SURROUNDreps; |
|
HRESULT SURROUNDhresult; |
|
SURROUNDreps = 0; |
|
|
|
if ( !pDSBufFL && !pDSBufFR && !pDSBufRL && !pDSBufRR && !pDSBufFC ) |
|
return; |
|
|
|
while ((SURROUNDhresult = pDSBufFL->Lock(0, m_bufferSizeBytes, (void**)&pDataFL, &dwSizeFL, NULL, NULL, 0)) != DS_OK) |
|
{ |
|
if (SURROUNDhresult != DSERR_BUFFERLOST) |
|
{ |
|
Msg ("S_ClearBuffer: DS::Lock FL Sound Buffer Failed\n"); |
|
S_Shutdown (); |
|
return; |
|
} |
|
|
|
if (++SURROUNDreps > 10000) |
|
{ |
|
Msg ("S_ClearBuffer: DS: couldn't restore FL buffer\n"); |
|
S_Shutdown (); |
|
return; |
|
} |
|
} |
|
|
|
SURROUNDreps = 0; |
|
while ((SURROUNDhresult = pDSBufFR->Lock(0, m_bufferSizeBytes, (void**)&pDataFR, &dwSizeFR, NULL, NULL, 0)) != DS_OK) |
|
{ |
|
if (SURROUNDhresult != DSERR_BUFFERLOST) |
|
{ |
|
Msg ("S_ClearBuffer: DS::Lock FR Sound Buffer Failed\n"); |
|
S_Shutdown (); |
|
return; |
|
} |
|
|
|
if (++SURROUNDreps > 10000) |
|
{ |
|
Msg ("S_ClearBuffer: DS: couldn't restore FR buffer\n"); |
|
S_Shutdown (); |
|
return; |
|
} |
|
} |
|
|
|
SURROUNDreps = 0; |
|
while ((SURROUNDhresult = pDSBufRL->Lock(0, m_bufferSizeBytes, (void**)&pDataRL, &dwSizeRL, NULL, NULL, 0)) != DS_OK) |
|
{ |
|
if (SURROUNDhresult != DSERR_BUFFERLOST) |
|
{ |
|
Msg ("S_ClearBuffer: DS::Lock RL Sound Buffer Failed\n"); |
|
S_Shutdown (); |
|
return; |
|
} |
|
|
|
if (++SURROUNDreps > 10000) |
|
{ |
|
Msg ("S_ClearBuffer: DS: couldn't restore RL buffer\n"); |
|
S_Shutdown (); |
|
return; |
|
} |
|
} |
|
|
|
SURROUNDreps = 0; |
|
while ((SURROUNDhresult = pDSBufRR->Lock(0, m_bufferSizeBytes, (void**)&pDataRR, &dwSizeRR, NULL, NULL, 0)) != DS_OK) |
|
{ |
|
if (SURROUNDhresult != DSERR_BUFFERLOST) |
|
{ |
|
Msg ("S_ClearBuffer: DS::Lock RR Sound Buffer Failed\n"); |
|
S_Shutdown (); |
|
return; |
|
} |
|
|
|
if (++SURROUNDreps > 10000) |
|
{ |
|
Msg ("S_ClearBuffer: DS: couldn't restore RR buffer\n"); |
|
S_Shutdown (); |
|
return; |
|
} |
|
} |
|
|
|
if (m_bSurroundCenter) |
|
{ |
|
SURROUNDreps = 0; |
|
while ((SURROUNDhresult = pDSBufFC->Lock(0, m_bufferSizeBytes, (void**)&pDataFC, &dwSizeFC, NULL, NULL, 0)) != DS_OK) |
|
{ |
|
if (SURROUNDhresult != DSERR_BUFFERLOST) |
|
{ |
|
Msg ("S_ClearBuffer: DS::Lock FC Sound Buffer Failed\n"); |
|
S_Shutdown (); |
|
return; |
|
} |
|
|
|
if (++SURROUNDreps > 10000) |
|
{ |
|
Msg ("S_ClearBuffer: DS: couldn't restore FC buffer\n"); |
|
S_Shutdown (); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
Q_memset(pDataFL, 0, m_bufferSizeBytes); |
|
Q_memset(pDataFR, 0, m_bufferSizeBytes); |
|
Q_memset(pDataRL, 0, m_bufferSizeBytes); |
|
Q_memset(pDataRR, 0, m_bufferSizeBytes); |
|
|
|
if (m_bSurroundCenter) |
|
Q_memset(pDataFC, 0, m_bufferSizeBytes); |
|
|
|
pDSBufFL->Unlock(pDataFL, dwSizeFL, NULL, 0); |
|
pDSBufFR->Unlock(pDataFR, dwSizeFR, NULL, 0); |
|
pDSBufRL->Unlock(pDataRL, dwSizeRL, NULL, 0); |
|
pDSBufRR->Unlock(pDataRR, dwSizeRR, NULL, 0); |
|
|
|
if (m_bSurroundCenter) |
|
pDSBufFC->Unlock(pDataFC, dwSizeFC, NULL, 0); |
|
|
|
return; |
|
} |
|
|
|
if ( !pDSBuf ) |
|
return; |
|
|
|
if ( DeviceSampleBits() == 8 ) |
|
clear = 0x80; |
|
else |
|
clear = 0; |
|
|
|
if (pDSBuf) |
|
{ |
|
DWORD dwSize; |
|
DWORD *pData; |
|
int reps; |
|
HRESULT hresult; |
|
|
|
reps = 0; |
|
while ((hresult = pDSBuf->Lock(0, m_bufferSizeBytes, (void**)&pData, &dwSize, NULL, NULL, 0)) != DS_OK) |
|
{ |
|
if (hresult != DSERR_BUFFERLOST) |
|
{ |
|
Msg("S_ClearBuffer: DS::Lock Sound Buffer Failed\n"); |
|
S_Shutdown(); |
|
return; |
|
} |
|
|
|
if (++reps > 10000) |
|
{ |
|
Msg("S_ClearBuffer: DS: couldn't restore buffer\n"); |
|
S_Shutdown(); |
|
return; |
|
} |
|
} |
|
|
|
Q_memset(pData, clear, dwSize); |
|
|
|
pDSBuf->Unlock(pData, dwSize, NULL, 0); |
|
} |
|
} |
|
|
|
void CAudioDirectSound::StopAllSounds( void ) |
|
{ |
|
} |
|
|
|
bool CAudioDirectSound::SNDDMA_InitInterleaved( LPDIRECTSOUND lpDS, WAVEFORMATEX* lpFormat, int channelCount ) |
|
{ |
|
WAVEFORMATEXTENSIBLE wfx = { 0 } ; // DirectSoundBuffer wave format (extensible) |
|
|
|
// set the channel mask and number of channels based on the command line parameter |
|
if(channelCount == 2) |
|
{ |
|
wfx.Format.nChannels = 2; |
|
wfx.dwChannelMask = KSAUDIO_SPEAKER_STEREO; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; |
|
} |
|
else if(channelCount == 4) |
|
{ |
|
wfx.Format.nChannels = 4; |
|
wfx.dwChannelMask = KSAUDIO_SPEAKER_QUAD; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; |
|
} |
|
else if(channelCount == 6) |
|
{ |
|
wfx.Format.nChannels = 6; |
|
wfx.dwChannelMask = KSAUDIO_SPEAKER_5POINT1; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; |
|
} |
|
else |
|
{ |
|
return false; |
|
} |
|
|
|
// setup the extensible structure |
|
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; |
|
//wfx.Format.nChannels = SET ABOVE |
|
wfx.Format.nSamplesPerSec = lpFormat->nSamplesPerSec; |
|
wfx.Format.wBitsPerSample = lpFormat->wBitsPerSample; |
|
wfx.Format.nBlockAlign = wfx.Format.wBitsPerSample / 8 * wfx.Format.nChannels; |
|
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign; |
|
wfx.Format.cbSize = 22; // size from after this to end of extensible struct. sizeof(WORD + DWORD + GUID) |
|
wfx.Samples.wValidBitsPerSample = lpFormat->wBitsPerSample; |
|
//wfx.dwChannelMask = SET ABOVE BASED ON COMMAND LINE PARAMETERS |
|
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; |
|
|
|
// setup the DirectSound |
|
DSBUFFERDESC dsbdesc = { 0 }; // DirectSoundBuffer descriptor |
|
dsbdesc.dwSize = sizeof(DSBUFFERDESC); |
|
dsbdesc.dwFlags = 0; |
|
|
|
dsbdesc.dwBufferBytes = SECONDARY_BUFFER_SIZE_SURROUND * channelCount; |
|
|
|
dsbdesc.lpwfxFormat = (WAVEFORMATEX*)&wfx; |
|
bool bSuccess = false; |
|
for ( int i = 0; i < 3; i++ ) |
|
{ |
|
switch(i) |
|
{ |
|
case 0: |
|
dsbdesc.dwFlags = DSBCAPS_LOCHARDWARE; |
|
break; |
|
case 1: |
|
dsbdesc.dwFlags = DSBCAPS_LOCSOFTWARE; |
|
break; |
|
case 2: |
|
dsbdesc.dwFlags = 0; |
|
break; |
|
} |
|
if ( !snd_mute_losefocus.GetBool() ) |
|
{ |
|
dsbdesc.dwFlags |= DSBCAPS_GLOBALFOCUS; |
|
} |
|
|
|
if(!FAILED(lpDS->CreateSoundBuffer(&dsbdesc, &pDSBuf, NULL))) |
|
{ |
|
bSuccess = true; |
|
break; |
|
} |
|
} |
|
if ( !bSuccess ) |
|
return false; |
|
|
|
DWORD dwSize = 0, dwWrite; |
|
DWORD *pBuffer = 0; |
|
if ( !LockDSBuffer( pDSBuf, &pBuffer, &dwSize, "DS_INTERLEAVED", DSBLOCK_ENTIREBUFFER ) ) |
|
return false; |
|
|
|
m_deviceChannels = wfx.Format.nChannels; |
|
m_deviceSampleBits = wfx.Format.wBitsPerSample; |
|
m_deviceDmaSpeed = wfx.Format.nSamplesPerSec; |
|
m_bufferSizeBytes = dsbdesc.dwBufferBytes; |
|
m_isInterleaved = true; |
|
|
|
Q_memset( pBuffer, 0, dwSize ); |
|
|
|
pDSBuf->Unlock(pBuffer, dwSize, NULL, 0); |
|
|
|
// Make sure mixer is active (this was moved after the zeroing to avoid popping on startup -- at least when using the dx9.0b debug .dlls) |
|
pDSBuf->Play(0, 0, DSBPLAY_LOOPING); |
|
|
|
pDSBuf->Stop(); |
|
pDSBuf->GetCurrentPosition(&m_outputBufferStartOffset, &dwWrite); |
|
|
|
pDSBuf->Play(0, 0, DSBPLAY_LOOPING); |
|
|
|
return true; |
|
} |
|
|
|
/* |
|
================== |
|
SNDDMA_InitDirect |
|
|
|
Direct-Sound support |
|
================== |
|
*/ |
|
sndinitstat CAudioDirectSound::SNDDMA_InitDirect( void ) |
|
{ |
|
DSBUFFERDESC dsbuf; |
|
DSBCAPS dsbcaps; |
|
DWORD dwSize, dwWrite; |
|
WAVEFORMATEX format; |
|
WAVEFORMATEX pformat; |
|
HRESULT hresult; |
|
void *lpData = NULL; |
|
bool primary_format_set = false; |
|
int pri_channels = 2; |
|
|
|
if (!m_hInstDS) |
|
{ |
|
m_hInstDS = LoadLibrary("dsound.dll"); |
|
if (m_hInstDS == NULL) |
|
{ |
|
Warning( "Couldn't load dsound.dll\n"); |
|
return SIS_FAILURE; |
|
} |
|
|
|
pDirectSoundCreate = (long (__stdcall *)(struct _GUID *,struct IDirectSound ** ,struct IUnknown *))GetProcAddress(m_hInstDS,"DirectSoundCreate"); |
|
if (!pDirectSoundCreate) |
|
{ |
|
Warning( "Couldn't get DS proc addr\n"); |
|
return SIS_FAILURE; |
|
} |
|
} |
|
|
|
while ((hresult = pDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK) |
|
{ |
|
if (hresult != DSERR_ALLOCATED) |
|
{ |
|
DevMsg ("DirectSound create failed\n"); |
|
return SIS_FAILURE; |
|
} |
|
|
|
return SIS_NOTAVAIL; |
|
} |
|
|
|
// get snd_surround value from window settings |
|
DetectWindowsSpeakerSetup(); |
|
|
|
m_bSurround = false; |
|
m_bSurroundCenter = false; |
|
m_bHeadphone = false; |
|
m_isInterleaved = false; |
|
|
|
switch ( snd_surround.GetInt() ) |
|
{ |
|
case 0: |
|
m_bHeadphone = true; // stereo headphone |
|
pri_channels = 2; // primary buffer mixes stereo input data |
|
break; |
|
default: |
|
case 2: |
|
pri_channels = 2; // primary buffer mixes stereo input data |
|
break; // no surround |
|
case 4: |
|
m_bSurround = true; // quad surround |
|
pri_channels = 1; // primary buffer mixes 3d mono input data |
|
break; |
|
case 5: |
|
case 7: |
|
m_bSurround = true; // 5.1 surround |
|
m_bSurroundCenter = true; |
|
pri_channels = 1; // primary buffer mixes 3d mono input data |
|
break; |
|
} |
|
|
|
m_deviceChannels = pri_channels; // secondary buffers should have same # channels as primary |
|
m_deviceSampleBits = 16; // hardware bits per sample |
|
m_deviceDmaSpeed = SOUND_DMA_SPEED; // hardware playback rate |
|
|
|
Q_memset( &format, 0, sizeof(format) ); |
|
format.wFormatTag = WAVE_FORMAT_PCM; |
|
format.nChannels = pri_channels; |
|
format.wBitsPerSample = m_deviceSampleBits; |
|
format.nSamplesPerSec = m_deviceDmaSpeed; |
|
format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; |
|
format.cbSize = 0; |
|
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; |
|
|
|
DSCAPS dscaps; |
|
Q_memset( &dscaps, 0, sizeof(dscaps) ); |
|
dscaps.dwSize = sizeof(dscaps); |
|
if (DS_OK != pDS->GetCaps(&dscaps)) |
|
{ |
|
Warning( "Couldn't get DS caps\n"); |
|
} |
|
|
|
if (dscaps.dwFlags & DSCAPS_EMULDRIVER) |
|
{ |
|
Warning( "No DirectSound driver installed\n"); |
|
Shutdown(); |
|
return SIS_FAILURE; |
|
} |
|
|
|
if (DS_OK != pDS->SetCooperativeLevel(*pmainwindow, DSSCL_EXCLUSIVE)) |
|
{ |
|
Warning( "Set coop level failed\n"); |
|
Shutdown(); |
|
return SIS_FAILURE; |
|
} |
|
|
|
// get access to the primary buffer, if possible, so we can set the |
|
// sound hardware format |
|
Q_memset( &dsbuf, 0, sizeof(dsbuf) ); |
|
dsbuf.dwSize = sizeof(DSBUFFERDESC); |
|
dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER; |
|
if ( snd_legacy_surround.GetBool() || m_bSurround ) |
|
{ |
|
dsbuf.dwFlags |= DSBCAPS_CTRL3D; |
|
} |
|
dsbuf.dwBufferBytes = 0; |
|
dsbuf.lpwfxFormat = NULL; |
|
|
|
Q_memset( &dsbcaps, 0, sizeof(dsbcaps) ); |
|
dsbcaps.dwSize = sizeof(dsbcaps); |
|
|
|
if ( !CommandLine()->CheckParm("-snoforceformat")) |
|
{ |
|
if (DS_OK == pDS->CreateSoundBuffer(&dsbuf, &pDSPBuf, NULL)) |
|
{ |
|
pformat = format; |
|
|
|
if (DS_OK != pDSPBuf->SetFormat(&pformat)) |
|
{ |
|
if (snd_firsttime) |
|
DevMsg ("Set primary sound buffer format: no\n"); |
|
} |
|
else |
|
{ |
|
if (snd_firsttime) |
|
DevMsg ("Set primary sound buffer format: yes\n"); |
|
|
|
primary_format_set = true; |
|
} |
|
} |
|
} |
|
|
|
if ( m_bSurround ) |
|
{ |
|
// try to init surround |
|
m_bSurround = false; |
|
if ( snd_legacy_surround.GetBool() ) |
|
{ |
|
if (snd_surround.GetInt() == 4) |
|
{ |
|
// attempt to init 4 channel surround |
|
m_bSurround = SNDDMA_InitSurround(pDS, &format, &dsbcaps, 4); |
|
} |
|
else if (snd_surround.GetInt() == 5 || snd_surround.GetInt() == 7) |
|
{ |
|
// attempt to init 5 channel surround |
|
m_bSurroundCenter = SNDDMA_InitSurround(pDS, &format, &dsbcaps, 5); |
|
m_bSurround = m_bSurroundCenter; |
|
} |
|
} |
|
if ( !m_bSurround ) |
|
{ |
|
pri_channels = 6; |
|
if ( snd_surround.GetInt() < 5 ) |
|
{ |
|
pri_channels = 4; |
|
} |
|
|
|
m_bSurround = SNDDMA_InitInterleaved( pDS, &format, pri_channels ); |
|
} |
|
} |
|
|
|
if ( !m_bSurround ) |
|
{ |
|
// snd_surround.SetValue( 0 ); |
|
if ( !primary_format_set || !CommandLine()->CheckParm ("-primarysound") ) |
|
{ |
|
// create the secondary buffer we'll actually work with |
|
Q_memset( &dsbuf, 0, sizeof(dsbuf) ); |
|
dsbuf.dwSize = sizeof(DSBUFFERDESC); |
|
dsbuf.dwFlags = DSBCAPS_LOCSOFTWARE; // NOTE: don't use CTRLFREQUENCY (slow) |
|
dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE; |
|
dsbuf.lpwfxFormat = &format; |
|
if ( !snd_mute_losefocus.GetBool() ) |
|
{ |
|
dsbuf.dwFlags |= DSBCAPS_GLOBALFOCUS; |
|
} |
|
|
|
if (DS_OK != pDS->CreateSoundBuffer(&dsbuf, &pDSBuf, NULL)) |
|
{ |
|
Warning( "DS:CreateSoundBuffer Failed"); |
|
Shutdown(); |
|
return SIS_FAILURE; |
|
} |
|
|
|
m_deviceChannels = format.nChannels; |
|
m_deviceSampleBits = format.wBitsPerSample; |
|
m_deviceDmaSpeed = format.nSamplesPerSec; |
|
|
|
Q_memset(&dsbcaps, 0, sizeof(dsbcaps)); |
|
dsbcaps.dwSize = sizeof(dsbcaps); |
|
|
|
if (DS_OK != pDSBuf->GetCaps( &dsbcaps )) |
|
{ |
|
Warning( "DS:GetCaps failed\n"); |
|
Shutdown(); |
|
return SIS_FAILURE; |
|
} |
|
|
|
if ( snd_firsttime ) |
|
DevMsg ("Using secondary sound buffer\n"); |
|
} |
|
else |
|
{ |
|
if (DS_OK != pDS->SetCooperativeLevel(*pmainwindow, DSSCL_WRITEPRIMARY)) |
|
{ |
|
Warning( "Set coop level failed\n"); |
|
Shutdown(); |
|
return SIS_FAILURE; |
|
} |
|
|
|
Q_memset(&dsbcaps, 0, sizeof(dsbcaps)); |
|
dsbcaps.dwSize = sizeof(dsbcaps); |
|
if (DS_OK != pDSPBuf->GetCaps(&dsbcaps)) |
|
{ |
|
Msg ("DS:GetCaps failed\n"); |
|
return SIS_FAILURE; |
|
} |
|
|
|
pDSBuf = pDSPBuf; |
|
DevMsg ("Using primary sound buffer\n"); |
|
} |
|
|
|
if ( snd_firsttime ) |
|
{ |
|
DevMsg(" %d channel(s)\n" |
|
" %d bits/sample\n" |
|
" %d samples/sec\n", |
|
DeviceChannels(), DeviceSampleBits(), DeviceDmaSpeed()); |
|
} |
|
|
|
// initialize the buffer |
|
m_bufferSizeBytes = dsbcaps.dwBufferBytes; |
|
int reps = 0; |
|
while ((hresult = pDSBuf->Lock(0, m_bufferSizeBytes, (void**)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK) |
|
{ |
|
if (hresult != DSERR_BUFFERLOST) |
|
{ |
|
Warning( "SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n"); |
|
Shutdown(); |
|
return SIS_FAILURE; |
|
} |
|
|
|
if (++reps > 10000) |
|
{ |
|
Warning( "SNDDMA_InitDirect: DS: couldn't restore buffer\n"); |
|
Shutdown(); |
|
return SIS_FAILURE; |
|
} |
|
} |
|
|
|
Q_memset( lpData, 0, dwSize ); |
|
pDSBuf->Unlock(lpData, dwSize, NULL, 0); |
|
|
|
// Make sure mixer is active (this was moved after the zeroing to avoid popping on startup -- at least when using the dx9.0b debug .dlls) |
|
pDSBuf->Play(0, 0, DSBPLAY_LOOPING); |
|
|
|
// we don't want anyone to access the buffer directly w/o locking it first. |
|
lpData = NULL; |
|
|
|
pDSBuf->Stop(); |
|
|
|
pDSBuf->GetCurrentPosition(&m_outputBufferStartOffset, &dwWrite); |
|
|
|
pDSBuf->Play(0, 0, DSBPLAY_LOOPING); |
|
} |
|
|
|
// number of mono samples output buffer may hold |
|
m_deviceSampleCount = m_bufferSizeBytes/(DeviceSampleBytes()); |
|
|
|
return SIS_SUCCESS; |
|
|
|
} |
|
|
|
static DWORD GetSpeakerConfigForSurroundMode( int surroundMode, const char **pConfigDesc ) |
|
{ |
|
DWORD newSpeakerConfig = DSSPEAKER_STEREO; |
|
const char *speakerConfigDesc = ""; |
|
|
|
switch ( surroundMode ) |
|
{ |
|
case 0: |
|
newSpeakerConfig = DSSPEAKER_HEADPHONE; |
|
speakerConfigDesc = "headphone"; |
|
break; |
|
|
|
case 2: |
|
default: |
|
newSpeakerConfig = DSSPEAKER_STEREO; |
|
speakerConfigDesc = "stereo speaker"; |
|
break; |
|
|
|
case 4: |
|
newSpeakerConfig = DSSPEAKER_QUAD; |
|
speakerConfigDesc = "quad speaker"; |
|
break; |
|
|
|
case 5: |
|
newSpeakerConfig = DSSPEAKER_5POINT1; |
|
speakerConfigDesc = "5.1 speaker"; |
|
break; |
|
|
|
case 7: |
|
newSpeakerConfig = DSSPEAKER_7POINT1; |
|
speakerConfigDesc = "7.1 speaker"; |
|
break; |
|
} |
|
if ( pConfigDesc ) |
|
{ |
|
*pConfigDesc = speakerConfigDesc; |
|
} |
|
return newSpeakerConfig; |
|
} |
|
|
|
// Read the speaker config from windows |
|
static DWORD GetWindowsSpeakerConfig() |
|
{ |
|
DWORD speaker_config = windows_speaker_config.GetInt(); |
|
if ( windows_speaker_config.GetInt() < 0 ) |
|
{ |
|
speaker_config = DSSPEAKER_STEREO; |
|
if (DS_OK == pDS->GetSpeakerConfig( &speaker_config )) |
|
{ |
|
// split out settings |
|
speaker_config = DSSPEAKER_CONFIG(speaker_config); |
|
if ( speaker_config == DSSPEAKER_7POINT1_SURROUND ) |
|
speaker_config = DSSPEAKER_7POINT1; |
|
if ( speaker_config == DSSPEAKER_5POINT1_SURROUND) |
|
speaker_config = DSSPEAKER_5POINT1; |
|
} |
|
windows_speaker_config.SetValue((int)speaker_config); |
|
} |
|
|
|
return speaker_config; |
|
} |
|
|
|
// Writes snd_surround convar given a directsound speaker config |
|
static void SetSurroundModeFromSpeakerConfig( DWORD speakerConfig ) |
|
{ |
|
// set the cvar to be the windows setting |
|
switch (speakerConfig) |
|
{ |
|
case DSSPEAKER_HEADPHONE: |
|
snd_surround.SetValue(0); |
|
break; |
|
|
|
case DSSPEAKER_MONO: |
|
case DSSPEAKER_STEREO: |
|
default: |
|
snd_surround.SetValue( 2 ); |
|
break; |
|
|
|
case DSSPEAKER_QUAD: |
|
snd_surround.SetValue(4); |
|
break; |
|
|
|
case DSSPEAKER_5POINT1: |
|
snd_surround.SetValue(5); |
|
break; |
|
|
|
case DSSPEAKER_7POINT1: |
|
snd_surround.SetValue(7); |
|
break; |
|
} |
|
} |
|
/* |
|
Sets the snd_surround_speakers cvar based on the windows setting |
|
*/ |
|
|
|
void CAudioDirectSound::DetectWindowsSpeakerSetup() |
|
{ |
|
// detect speaker settings from windows |
|
DWORD speaker_config = GetWindowsSpeakerConfig(); |
|
SetSurroundModeFromSpeakerConfig(speaker_config); |
|
|
|
// DEBUG |
|
if (speaker_config == DSSPEAKER_MONO) |
|
DevMsg( "DS:mono configuration detected\n"); |
|
|
|
if (speaker_config == DSSPEAKER_HEADPHONE) |
|
DevMsg( "DS:headphone configuration detected\n"); |
|
|
|
if (speaker_config == DSSPEAKER_STEREO) |
|
DevMsg( "DS:stereo speaker configuration detected\n"); |
|
|
|
if (speaker_config == DSSPEAKER_QUAD) |
|
DevMsg( "DS:quad speaker configuration detected\n"); |
|
|
|
if (speaker_config == DSSPEAKER_SURROUND) |
|
DevMsg( "DS:surround speaker configuration detected\n"); |
|
|
|
if (speaker_config == DSSPEAKER_5POINT1) |
|
DevMsg( "DS:5.1 speaker configuration detected\n"); |
|
|
|
if (speaker_config == DSSPEAKER_7POINT1) |
|
DevMsg( "DS:7.1 speaker configuration detected\n"); |
|
} |
|
|
|
/* |
|
Updates windows settings based on snd_surround_speakers cvar changing |
|
This should only happen if the user has changed it via the console or the UI |
|
Changes won't take effect until the engine has restarted |
|
*/ |
|
void OnSndSurroundCvarChanged( IConVar *pVar, const char *pOldString, float flOldValue ) |
|
{ |
|
// if the old value is -1, we're setting this from the detect routine for the first time |
|
// no need to reset the device |
|
if (!pDS || flOldValue == -1 ) |
|
return; |
|
|
|
// get the user's previous speaker config |
|
DWORD speaker_config = GetWindowsSpeakerConfig(); |
|
|
|
// get the new config |
|
DWORD newSpeakerConfig = 0; |
|
const char *speakerConfigDesc = ""; |
|
|
|
ConVarRef var( pVar ); |
|
newSpeakerConfig = GetSpeakerConfigForSurroundMode( var.GetInt(), &speakerConfigDesc ); |
|
// make sure the config has changed |
|
if (newSpeakerConfig == speaker_config) |
|
return; |
|
|
|
// set new configuration |
|
windows_speaker_config.SetValue( (int)newSpeakerConfig ); |
|
|
|
Msg("Speaker configuration has been changed to %s.\n", speakerConfigDesc); |
|
|
|
// restart sound system so it takes effect |
|
g_pSoundServices->RestartSoundSystem(); |
|
} |
|
|
|
void OnSndSurroundLegacyChanged( IConVar *pVar, const char *pOldString, float flOldValue ) |
|
{ |
|
if ( pDS && CAudioDirectSound::m_pSingleton ) |
|
{ |
|
ConVarRef var( pVar ); |
|
// should either be interleaved or have legacy surround set, not both |
|
if ( CAudioDirectSound::m_pSingleton->IsInterleaved() == var.GetBool() ) |
|
{ |
|
Msg( "Legacy Surround %s.\n", var.GetBool() ? "enabled" : "disabled" ); |
|
// restart sound system so it takes effect |
|
g_pSoundServices->RestartSoundSystem(); |
|
} |
|
} |
|
} |
|
|
|
void OnSndVarChanged( IConVar *pVar, const char *pOldString, float flOldValue ) |
|
{ |
|
ConVarRef var(pVar); |
|
// restart sound system so the change takes effect |
|
if ( var.GetInt() != int(flOldValue) ) |
|
{ |
|
g_pSoundServices->RestartSoundSystem(); |
|
} |
|
} |
|
|
|
/* |
|
Release all Surround buffer pointers |
|
*/ |
|
void ReleaseSurround(void) |
|
{ |
|
if ( pDSBuf3DFL != NULL ) |
|
{ |
|
pDSBuf3DFL->Release(); |
|
pDSBuf3DFL = NULL; |
|
} |
|
|
|
if ( pDSBuf3DFR != NULL) |
|
{ |
|
pDSBuf3DFR->Release(); |
|
pDSBuf3DFR = NULL; |
|
} |
|
|
|
if ( pDSBuf3DRL != NULL ) |
|
{ |
|
pDSBuf3DRL->Release(); |
|
pDSBuf3DRL = NULL; |
|
} |
|
|
|
if ( pDSBuf3DRR != NULL ) |
|
{ |
|
pDSBuf3DRR->Release(); |
|
pDSBuf3DRR = NULL; |
|
} |
|
|
|
if ( pDSBufFL != NULL ) |
|
{ |
|
pDSBufFL->Release(); |
|
pDSBufFL = NULL; |
|
} |
|
|
|
if ( pDSBufFR != NULL ) |
|
{ |
|
pDSBufFR->Release(); |
|
pDSBufFR = NULL; |
|
} |
|
|
|
if ( pDSBufRL != NULL ) |
|
{ |
|
pDSBufRL->Release(); |
|
pDSBufRL = NULL; |
|
} |
|
|
|
if ( pDSBufRR != NULL ) |
|
{ |
|
pDSBufRR->Release(); |
|
pDSBufRR = NULL; |
|
} |
|
|
|
if ( pDSBufFC != NULL ) |
|
{ |
|
pDSBufFC->Release(); |
|
pDSBufFC = NULL; |
|
} |
|
} |
|
|
|
void DEBUG_DS_FillSquare( void *lpData, DWORD dwSize ) |
|
{ |
|
short *lpshort = (short *)lpData; |
|
DWORD j = min((DWORD)10000, dwSize/2); |
|
|
|
for (DWORD i = 0; i < j; i++) |
|
lpshort[i] = 8000; |
|
} |
|
|
|
void DEBUG_DS_FillSquare2( void *lpData, DWORD dwSize ) |
|
{ |
|
short *lpshort = (short *)lpData; |
|
DWORD j = min((DWORD)1000, dwSize/2); |
|
|
|
for (DWORD i = 0; i < j; i++) |
|
lpshort[i] = 16000; |
|
} |
|
|
|
// helper to set default buffer params |
|
void DS3D_SetBufferParams( LPDIRECTSOUND3DBUFFER pDSBuf3D, D3DVECTOR *pbpos, D3DVECTOR *pbdir ) |
|
{ |
|
DS3DBUFFER bparm; |
|
D3DVECTOR bvel; |
|
D3DVECTOR bpos, bdir; |
|
HRESULT hr; |
|
|
|
bvel.x = 0.0f; bvel.y = 0.0f; bvel.z = 0.0f; |
|
bpos = *pbpos; |
|
bdir = *pbdir; |
|
|
|
bparm.dwSize = sizeof(DS3DBUFFER); |
|
|
|
hr = pDSBuf3D->GetAllParameters( &bparm ); |
|
|
|
bparm.vPosition = bpos; |
|
bparm.vVelocity = bvel; |
|
bparm.dwInsideConeAngle = 5.0; // narrow cones for each speaker |
|
bparm.dwOutsideConeAngle = 10.0; |
|
bparm.vConeOrientation = bdir; |
|
bparm.lConeOutsideVolume = DSBVOLUME_MIN; |
|
bparm.flMinDistance = 100.0; // no rolloff (until > 2.0 meter distance) |
|
bparm.flMaxDistance = DS3D_DEFAULTMAXDISTANCE; |
|
bparm.dwMode = DS3DMODE_NORMAL; |
|
|
|
hr = pDSBuf3D->SetAllParameters( &bparm, DS3D_DEFERRED ); |
|
} |
|
|
|
// Initialization for Surround sound support (4 channel or 5 channel). |
|
// Creates 4 or 5 mono 3D buffers to be used as Front Left, (Front Center), Front Right, Rear Left, Rear Right |
|
bool CAudioDirectSound::SNDDMA_InitSurround(LPDIRECTSOUND lpDS, WAVEFORMATEX* lpFormat, DSBCAPS* lpdsbc, int cchan) |
|
{ |
|
DSBUFFERDESC dsbuf; |
|
WAVEFORMATEX wvex; |
|
DWORD dwSize, dwWrite; |
|
int reps; |
|
HRESULT hresult; |
|
void *lpData = NULL; |
|
|
|
if ( lpDS == NULL ) return FALSE; |
|
|
|
// Force format to mono channel |
|
|
|
memcpy(&wvex, lpFormat, sizeof(WAVEFORMATEX)); |
|
wvex.nChannels = 1; |
|
wvex.nBlockAlign = wvex.nChannels * wvex.wBitsPerSample / 8; |
|
wvex.nAvgBytesPerSec = wvex.nSamplesPerSec * wvex.nBlockAlign; |
|
|
|
memset (&dsbuf, 0, sizeof(dsbuf)); |
|
dsbuf.dwSize = sizeof(DSBUFFERDESC); |
|
// NOTE: LOCHARDWARE causes SB AWE64 to crash in it's DSOUND driver |
|
dsbuf.dwFlags = DSBCAPS_CTRL3D; // don't use CTRLFREQUENCY (slow) |
|
if ( !snd_mute_losefocus.GetBool() ) |
|
{ |
|
dsbuf.dwFlags |= DSBCAPS_GLOBALFOCUS; |
|
} |
|
|
|
// reserve space for each buffer |
|
|
|
dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE_SURROUND; |
|
|
|
dsbuf.lpwfxFormat = &wvex; |
|
|
|
// create 4 mono buffers FL, FR, RL, RR |
|
|
|
if (DS_OK != lpDS->CreateSoundBuffer(&dsbuf, &pDSBufFL, NULL)) |
|
{ |
|
Warning( "DS:CreateSoundBuffer for 3d front left failed"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
|
|
if (DS_OK != lpDS->CreateSoundBuffer(&dsbuf, &pDSBufFR, NULL)) |
|
{ |
|
Warning( "DS:CreateSoundBuffer for 3d front right failed"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
|
|
if (DS_OK != lpDS->CreateSoundBuffer(&dsbuf, &pDSBufRL, NULL)) |
|
{ |
|
Warning( "DS:CreateSoundBuffer for 3d rear left failed"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
|
|
if (DS_OK != lpDS->CreateSoundBuffer(&dsbuf, &pDSBufRR, NULL)) |
|
{ |
|
Warning( "DS:CreateSoundBuffer for 3d rear right failed"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
|
|
// create center channel |
|
|
|
if (cchan == 5) |
|
{ |
|
if (DS_OK != lpDS->CreateSoundBuffer(&dsbuf, &pDSBufFC, NULL)) |
|
{ |
|
Warning( "DS:CreateSoundBuffer for 3d front center failed"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
} |
|
|
|
// Try to get 4 or 5 3D buffers from the mono DS buffers |
|
|
|
if (DS_OK != pDSBufFL->QueryInterface(IID_IDirectSound3DBufferDef, (void**)&pDSBuf3DFL)) |
|
{ |
|
Warning( "DS:Query 3DBuffer for 3d front left failed"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
|
|
if (DS_OK != pDSBufFR->QueryInterface(IID_IDirectSound3DBufferDef, (void**)&pDSBuf3DFR)) |
|
{ |
|
Warning( "DS:Query 3DBuffer for 3d front right failed"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
|
|
if (DS_OK != pDSBufRL->QueryInterface(IID_IDirectSound3DBufferDef, (void**)&pDSBuf3DRL)) |
|
{ |
|
Warning( "DS:Query 3DBuffer for 3d rear left failed"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
|
|
if (DS_OK != pDSBufRR->QueryInterface(IID_IDirectSound3DBufferDef, (void**)&pDSBuf3DRR)) |
|
{ |
|
Warning( "DS:Query 3DBuffer for 3d rear right failed"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
|
|
if (cchan == 5) |
|
{ |
|
if (DS_OK != pDSBufFC->QueryInterface(IID_IDirectSound3DBufferDef, (void**)&pDSBuf3DFC)) |
|
{ |
|
Warning( "DS:Query 3DBuffer for 3d front center failed"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
} |
|
|
|
// set listener position & orientation. |
|
// DS uses left handed coord system: +x is right, +y is up, +z is forward |
|
|
|
HRESULT hr; |
|
|
|
IDirectSound3DListener *plistener = NULL; |
|
|
|
hr = pDSPBuf->QueryInterface(IID_IDirectSound3DListener, (void**)&plistener); |
|
if (plistener) |
|
{ |
|
DS3DLISTENER lparm; |
|
lparm.dwSize = sizeof(DS3DLISTENER); |
|
|
|
hr = plistener->GetAllParameters( &lparm ); |
|
|
|
hr = plistener->SetOrientation( 0.0f,0.0f,1.0f, 0.0f,1.0f,0.0f, DS3D_IMMEDIATE); // frontx,y,z topx,y,z |
|
hr = plistener->SetPosition(0.0f, 0.0f, 0.0f, DS3D_IMMEDIATE); |
|
} |
|
else |
|
{ |
|
Warning( "DS: failed to get 3D listener interface."); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
|
|
// set 3d buffer position and orientation params |
|
|
|
D3DVECTOR bpos, bdir; |
|
|
|
bpos.x = -1.0; bpos.y = 0.0; bpos.z = 1.0; // FL |
|
bdir.x = 1.0; bdir.y = 0.0; bdir.z = -1.0; |
|
|
|
DS3D_SetBufferParams( pDSBuf3DFL, &bpos, &bdir ); |
|
|
|
bpos.x = 1.0; bpos.y = 0.0; bpos.z = 1.0; // FR |
|
bdir.x = -1.0; bdir.y = 0.0; bdir.z = -1.0; |
|
|
|
DS3D_SetBufferParams( pDSBuf3DFR, &bpos, &bdir ); |
|
|
|
bpos.x = -1.0; bpos.y = 0.0; bpos.z = -1.0; // RL |
|
bdir.x = 1.0; bdir.y = 0.0; bdir.z = 1.0; |
|
|
|
DS3D_SetBufferParams( pDSBuf3DRL, &bpos, &bdir ); |
|
|
|
bpos.x = 1.0; bpos.y = 0.0; bpos.z = -1.0; // RR |
|
bdir.x = -1.0; bdir.y = 0.0; bdir.z = 1.0; |
|
|
|
DS3D_SetBufferParams( pDSBuf3DRR, &bpos, &bdir ); |
|
|
|
if (cchan == 5) |
|
{ |
|
bpos.x = 0.0; bpos.y = 0.0; bpos.z = 1.0; // FC |
|
bdir.x = 0.0; bdir.y = 0.0; bdir.z = -1.0; |
|
|
|
DS3D_SetBufferParams( pDSBuf3DFC, &bpos, &bdir ); |
|
} |
|
|
|
// commit all buffer param settings |
|
|
|
hr = plistener->CommitDeferredSettings(); |
|
|
|
m_deviceChannels = 1; // 1 mono 3d output buffer |
|
m_deviceSampleBits = lpFormat->wBitsPerSample; |
|
m_deviceDmaSpeed = lpFormat->nSamplesPerSec; |
|
|
|
memset(lpdsbc, 0, sizeof(DSBCAPS)); |
|
lpdsbc->dwSize = sizeof(DSBCAPS); |
|
|
|
if (DS_OK != pDSBufFL->GetCaps (lpdsbc)) |
|
{ |
|
Warning( "DS:GetCaps failed for 3d sound buffer\n"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
|
|
pDSBufFL->Play(0, 0, DSBPLAY_LOOPING); |
|
pDSBufFR->Play(0, 0, DSBPLAY_LOOPING); |
|
pDSBufRL->Play(0, 0, DSBPLAY_LOOPING); |
|
pDSBufRR->Play(0, 0, DSBPLAY_LOOPING); |
|
|
|
if (cchan == 5) |
|
pDSBufFC->Play(0, 0, DSBPLAY_LOOPING); |
|
|
|
if (snd_firsttime) |
|
DevMsg(" %d channel(s)\n" |
|
" %d bits/sample\n" |
|
" %d samples/sec\n", |
|
cchan, DeviceSampleBits(), DeviceDmaSpeed()); |
|
|
|
m_bufferSizeBytes = lpdsbc->dwBufferBytes; |
|
|
|
// Test everything just like in the normal initialization. |
|
if (cchan == 5) |
|
{ |
|
reps = 0; |
|
while ((hresult = pDSBufFC->Lock(0, lpdsbc->dwBufferBytes, (void**)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK) |
|
{ |
|
if (hresult != DSERR_BUFFERLOST) |
|
{ |
|
Warning( "SNDDMA_InitDirect: DS::Lock Sound Buffer Failed for FC\n"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
|
|
if (++reps > 10000) |
|
{ |
|
Warning( "SNDDMA_InitDirect: DS: couldn't restore buffer for FC\n"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
} |
|
memset(lpData, 0, dwSize); |
|
// DEBUG_DS_FillSquare( lpData, dwSize ); |
|
pDSBufFC->Unlock(lpData, dwSize, NULL, 0); |
|
} |
|
|
|
reps = 0; |
|
while ((hresult = pDSBufFL->Lock(0, lpdsbc->dwBufferBytes, (void**)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK) |
|
{ |
|
if (hresult != DSERR_BUFFERLOST) |
|
{ |
|
Warning( "SNDDMA_InitSurround: DS::Lock Sound Buffer Failed for 3d FL\n"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
|
|
if (++reps > 10000) |
|
{ |
|
Warning( "SNDDMA_InitSurround: DS: couldn't restore buffer for 3d FL\n"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
} |
|
memset(lpData, 0, dwSize); |
|
// DEBUG_DS_FillSquare( lpData, dwSize ); |
|
pDSBufFL->Unlock(lpData, dwSize, NULL, 0); |
|
|
|
reps = 0; |
|
while ((hresult = pDSBufFR->Lock(0, lpdsbc->dwBufferBytes, (void**)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK) |
|
{ |
|
if (hresult != DSERR_BUFFERLOST) |
|
{ |
|
Warning( "SNDDMA_InitSurround: DS::Lock Sound Buffer Failed for 3d FR\n"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
|
|
if (++reps > 10000) |
|
{ |
|
Warning( "SNDDMA_InitSurround: DS: couldn't restore buffer for FR\n"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
} |
|
memset(lpData, 0, dwSize); |
|
// DEBUG_DS_FillSquare( lpData, dwSize ); |
|
pDSBufFR->Unlock(lpData, dwSize, NULL, 0); |
|
|
|
reps = 0; |
|
while ((hresult = pDSBufRL->Lock(0, lpdsbc->dwBufferBytes, (void**)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK) |
|
{ |
|
if (hresult != DSERR_BUFFERLOST) |
|
{ |
|
Warning( "SNDDMA_InitDirect: DS::Lock Sound Buffer Failed for RL\n"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
|
|
if (++reps > 10000) |
|
{ |
|
Warning( "SNDDMA_InitDirect: DS: couldn't restore buffer for RL\n"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
} |
|
memset(lpData, 0, dwSize); |
|
// DEBUG_DS_FillSquare( lpData, dwSize ); |
|
pDSBufRL->Unlock(lpData, dwSize, NULL, 0); |
|
|
|
reps = 0; |
|
while ((hresult = pDSBufRR->Lock(0, lpdsbc->dwBufferBytes, (void**)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK) |
|
{ |
|
if (hresult != DSERR_BUFFERLOST) |
|
{ |
|
Warning( "SNDDMA_InitDirect: DS::Lock Sound Buffer Failed for RR\n"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
|
|
if (++reps > 10000) |
|
{ |
|
Warning( "SNDDMA_InitDirect: DS: couldn't restore buffer for RR\n"); |
|
ReleaseSurround(); |
|
return FALSE; |
|
} |
|
} |
|
memset(lpData, 0, dwSize); |
|
// DEBUG_DS_FillSquare( lpData, dwSize ); |
|
pDSBufRR->Unlock(lpData, dwSize, NULL, 0); |
|
|
|
lpData = NULL; // this is invalid now |
|
|
|
// OK Stop and get our positions and were good to go. |
|
pDSBufFL->Stop(); |
|
pDSBufFR->Stop(); |
|
pDSBufRL->Stop(); |
|
pDSBufRR->Stop(); |
|
if (cchan == 5) |
|
pDSBufFC->Stop(); |
|
|
|
// get hardware playback position, store it, syncronize all buffers to FL |
|
|
|
pDSBufFL->GetCurrentPosition(&m_outputBufferStartOffset, &dwWrite); |
|
pDSBufFR->SetCurrentPosition(m_outputBufferStartOffset); |
|
pDSBufRL->SetCurrentPosition(m_outputBufferStartOffset); |
|
pDSBufRR->SetCurrentPosition(m_outputBufferStartOffset); |
|
if (cchan == 5) |
|
pDSBufFC->SetCurrentPosition(m_outputBufferStartOffset); |
|
|
|
pDSBufFL->Play(0, 0, DSBPLAY_LOOPING); |
|
pDSBufFR->Play(0, 0, DSBPLAY_LOOPING); |
|
pDSBufRL->Play(0, 0, DSBPLAY_LOOPING); |
|
pDSBufRR->Play(0, 0, DSBPLAY_LOOPING); |
|
if (cchan == 5) |
|
pDSBufFC->Play(0, 0, DSBPLAY_LOOPING); |
|
|
|
if (snd_firsttime) |
|
Warning( "3d surround sound initialization successful\n"); |
|
|
|
return TRUE; |
|
} |
|
|
|
void CAudioDirectSound::UpdateListener( const Vector& position, const Vector& forward, const Vector& right, const Vector& up ) |
|
{ |
|
} |
|
|
|
void CAudioDirectSound::ChannelReset( int entnum, int channelIndex, float distanceMod ) |
|
{ |
|
} |
|
|
|
const char *CAudioDirectSound::DeviceName( void ) |
|
{ |
|
if ( m_bSurroundCenter ) |
|
return "5 Channel Surround"; |
|
|
|
if ( m_bSurround ) |
|
return "4 Channel Surround"; |
|
|
|
return "Direct Sound"; |
|
} |
|
|
|
// use the partial buffer locking code in stereo as well - not available when recording a movie |
|
ConVar snd_lockpartial("snd_lockpartial","1"); |
|
|
|
// Transfer up to a full paintbuffer (PAINTBUFFER_SIZE) of stereo samples |
|
// out to the directsound secondary buffer(s). |
|
// For 4 or 5 ch surround, there are 4 or 5 mono 16 bit secondary DS streaming buffers. |
|
// For stereo speakers, there is one stereo 16 bit secondary DS streaming buffer. |
|
|
|
void CAudioDirectSound::TransferSamples( int end ) |
|
{ |
|
int lpaintedtime = g_paintedtime; |
|
int endtime = end; |
|
|
|
// When Surround is enabled, divert to 4 or 5 chan xfer scheme. |
|
if ( m_bSurround ) |
|
{ |
|
if ( m_isInterleaved ) |
|
{ |
|
S_TransferSurround16Interleaved( PAINTBUFFER, REARPAINTBUFFER, CENTERPAINTBUFFER, lpaintedtime, endtime); |
|
} |
|
else |
|
{ |
|
int cchan = ( m_bSurroundCenter ? 5 : 4); |
|
|
|
S_TransferSurround16( PAINTBUFFER, REARPAINTBUFFER, CENTERPAINTBUFFER, lpaintedtime, endtime, cchan); |
|
} |
|
return; |
|
} |
|
else if ( snd_lockpartial.GetBool() && DeviceChannels() == 2 && DeviceSampleBits() == 16 && !SND_IsRecording() ) |
|
{ |
|
S_TransferSurround16Interleaved( PAINTBUFFER, NULL, NULL, lpaintedtime, endtime ); |
|
} |
|
else |
|
{ |
|
DWORD *pBuffer = NULL; |
|
DWORD dwSize = 0; |
|
if ( !LockDSBuffer( pDSBuf, &pBuffer, &dwSize, "DS_STEREO" ) ) |
|
{ |
|
S_Shutdown(); |
|
S_Startup(); |
|
return; |
|
} |
|
if ( pBuffer ) |
|
{ |
|
if ( DeviceChannels() == 2 && DeviceSampleBits() == 16 ) |
|
{ |
|
S_TransferStereo16( pBuffer, PAINTBUFFER, lpaintedtime, endtime ); |
|
} |
|
else |
|
{ |
|
// UNDONE: obsolete - no 8 bit mono output supported |
|
S_TransferPaintBuffer( pBuffer, PAINTBUFFER, lpaintedtime, endtime ); |
|
} |
|
pDSBuf->Unlock( pBuffer, dwSize, NULL, 0 ); |
|
} |
|
} |
|
} |
|
|
|
bool CAudioDirectSound::IsUsingBufferPerSpeaker() |
|
{ |
|
return m_bSurround && !m_isInterleaved; |
|
} |
|
|
|
bool CAudioDirectSound::LockDSBuffer( LPDIRECTSOUNDBUFFER pBuffer, DWORD **pdwWriteBuffer, DWORD *pdwSizeBuffer, const char *pBufferName, int lockFlags ) |
|
{ |
|
if ( !pBuffer ) |
|
return false; |
|
HRESULT hr; |
|
int reps = 0; |
|
while ((hr = pBuffer->Lock(0, m_bufferSizeBytes, (void**)pdwWriteBuffer, pdwSizeBuffer, |
|
NULL, NULL, lockFlags)) != DS_OK) |
|
{ |
|
if (hr != DSERR_BUFFERLOST) |
|
{ |
|
Msg ("DS::Lock Sound Buffer Failed %s\n", pBufferName); |
|
return false; |
|
} |
|
|
|
if (++reps > 10000) |
|
{ |
|
Msg ("DS:: couldn't restore buffer %s\n", pBufferName); |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////// |
|
// Given front, rear and center stereo paintbuffers, split samples into 4 or 5 mono directsound buffers (FL, FC, FR, RL, RR) |
|
void CAudioDirectSound::S_TransferSurround16( portable_samplepair_t *pfront, portable_samplepair_t *prear, portable_samplepair_t *pcenter, int lpaintedtime, int endtime, int cchan) |
|
{ |
|
int lpos; |
|
DWORD *pdwWriteFL=NULL, *pdwWriteFR=NULL, *pdwWriteRL=NULL, *pdwWriteRR=NULL, *pdwWriteFC=NULL; |
|
DWORD dwSizeFL=0, dwSizeFR=0, dwSizeRL=0, dwSizeRR=0, dwSizeFC=0; |
|
int i, j, *snd_p, *snd_rp, *snd_cp, volumeFactor; |
|
short *snd_out_fleft, *snd_out_fright, *snd_out_rleft, *snd_out_rright, *snd_out_fcenter; |
|
|
|
pdwWriteFC = NULL; // compiler warning |
|
dwSizeFC = 0; |
|
snd_out_fcenter = NULL; |
|
|
|
volumeFactor = S_GetMasterVolume() * 256; |
|
|
|
// lock all 4 or 5 mono directsound buffers FL, FR, RL, RR, FC |
|
if ( !LockDSBuffer( pDSBufFL, &pdwWriteFL, &dwSizeFL, "FL" ) || |
|
!LockDSBuffer( pDSBufFR, &pdwWriteFR, &dwSizeFR, "FR" ) || |
|
!LockDSBuffer( pDSBufRL, &pdwWriteRL, &dwSizeRL, "RL" ) || |
|
!LockDSBuffer( pDSBufRR, &pdwWriteRR, &dwSizeRR, "RR" ) ) |
|
{ |
|
S_Shutdown(); |
|
S_Startup(); |
|
return; |
|
} |
|
|
|
if (cchan == 5 && !LockDSBuffer( pDSBufFC, &pdwWriteFC, &dwSizeFC, "FC" )) |
|
{ |
|
S_Shutdown (); |
|
S_Startup (); |
|
return; |
|
} |
|
|
|
// take stereo front and rear paintbuffers, and center paintbuffer if provided, |
|
// and copy samples into the 4 or 5 mono directsound buffers |
|
|
|
snd_rp = (int *)prear; |
|
snd_cp = (int *)pcenter; |
|
snd_p = (int *)pfront; |
|
|
|
int linearCount; // space in output buffer for linearCount mono samples |
|
int sampleMonoCount = DeviceSampleCount(); // number of mono samples per output buffer (was;(DeviceSampleCount()>>1)) |
|
int sampleMask = sampleMonoCount - 1; |
|
|
|
// paintedtime - number of full samples that have played since start |
|
// endtime - number of full samples to play to - endtime is g_soundtime + mixahead samples |
|
|
|
while (lpaintedtime < endtime) |
|
{ |
|
lpos = lpaintedtime & sampleMask; // lpos is next output position in output buffer |
|
|
|
linearCount = sampleMonoCount - lpos; |
|
|
|
// limit output count to requested number of samples |
|
|
|
if (linearCount > endtime - lpaintedtime) |
|
linearCount = endtime - lpaintedtime; |
|
|
|
snd_out_fleft = (short *)pdwWriteFL + lpos; |
|
snd_out_fright = (short *)pdwWriteFR + lpos; |
|
snd_out_rleft = (short *)pdwWriteRL + lpos; |
|
snd_out_rright = (short *)pdwWriteRR + lpos; |
|
|
|
if (cchan == 5) |
|
snd_out_fcenter = (short *)pdwWriteFC + lpos; |
|
|
|
// for 16 bit sample in the front and rear stereo paintbuffers, copy |
|
// into the 4 or 5 FR, FL, RL, RR, FC directsound paintbuffers |
|
|
|
for (i=0, j= 0 ; i<linearCount ; i++, j+=2) |
|
{ |
|
snd_out_fleft[i] = (snd_p[j]*volumeFactor)>>8; |
|
snd_out_fright[i] = (snd_p[j + 1]*volumeFactor)>>8; |
|
snd_out_rleft[i] = (snd_rp[j]*volumeFactor)>>8; |
|
snd_out_rright[i] = (snd_rp[j + 1]*volumeFactor)>>8; |
|
} |
|
|
|
// copy front center buffer (mono) data to center chan directsound paintbuffer |
|
|
|
if (cchan == 5) |
|
{ |
|
for (i=0, j=0 ; i<linearCount ; i++, j+=2) |
|
{ |
|
snd_out_fcenter[i] = (snd_cp[j]*volumeFactor)>>8; |
|
|
|
} |
|
} |
|
|
|
snd_p += linearCount << 1; |
|
snd_rp += linearCount << 1; |
|
snd_cp += linearCount << 1; |
|
|
|
lpaintedtime += linearCount; |
|
} |
|
|
|
pDSBufFL->Unlock(pdwWriteFL, dwSizeFL, NULL, 0); |
|
pDSBufFR->Unlock(pdwWriteFR, dwSizeFR, NULL, 0); |
|
pDSBufRL->Unlock(pdwWriteRL, dwSizeRL, NULL, 0); |
|
pDSBufRR->Unlock(pdwWriteRR, dwSizeRR, NULL, 0); |
|
|
|
if (cchan == 5) |
|
pDSBufFC->Unlock(pdwWriteFC, dwSizeFC, NULL, 0); |
|
} |
|
|
|
struct surround_transfer_t |
|
{ |
|
int paintedtime; |
|
int linearCount; |
|
int sampleMask; |
|
int channelCount; |
|
int *snd_p; |
|
int *snd_rp; |
|
int *snd_cp; |
|
short *pOutput; |
|
}; |
|
|
|
static void TransferSamplesToSurroundBuffer( int outputCount, surround_transfer_t &transfer ) |
|
{ |
|
int i, j; |
|
int volumeFactor = S_GetMasterVolume() * 256; |
|
|
|
if ( transfer.channelCount == 2 ) |
|
{ |
|
for (i=0, j=0; i<outputCount ; i++, j+=2) |
|
{ |
|
transfer.pOutput[0] = (transfer.snd_p[j]*volumeFactor)>>8; // FL |
|
transfer.pOutput[1] = (transfer.snd_p[j + 1]*volumeFactor)>>8; // FR |
|
transfer.pOutput += 2; |
|
} |
|
} |
|
// no center channel, 4 channel surround |
|
else if ( transfer.channelCount == 4 ) |
|
{ |
|
for (i=0, j=0; i<outputCount ; i++, j+=2) |
|
{ |
|
transfer.pOutput[0] = (transfer.snd_p[j]*volumeFactor)>>8; // FL |
|
transfer.pOutput[1] = (transfer.snd_p[j + 1]*volumeFactor)>>8; // FR |
|
transfer.pOutput[2] = (transfer.snd_rp[j]*volumeFactor)>>8; // RL |
|
transfer.pOutput[3] = (transfer.snd_rp[j + 1]*volumeFactor)>>8; // RR |
|
transfer.pOutput += 4; |
|
//Assert( baseOffset <= (DeviceSampleCount()) ); |
|
} |
|
} |
|
else |
|
{ |
|
Assert(transfer.snd_cp); |
|
// 6 channel / 5.1 |
|
for (i=0, j=0 ; i<outputCount ; i++, j+=2) |
|
{ |
|
transfer.pOutput[0] = (transfer.snd_p[j]*volumeFactor)>>8; // FL |
|
transfer.pOutput[1] = (transfer.snd_p[j + 1]*volumeFactor)>>8; // FR |
|
|
|
transfer.pOutput[2] = (transfer.snd_cp[j]*volumeFactor)>>8; // Center |
|
|
|
transfer.pOutput[3] = 0; |
|
|
|
transfer.pOutput[4] = (transfer.snd_rp[j]*volumeFactor)>>8; // RL |
|
transfer.pOutput[5] = (transfer.snd_rp[j + 1]*volumeFactor)>>8; // RR |
|
|
|
#if 0 |
|
// average channels into the subwoofer, let the sub filter the output |
|
// NOTE: avg l/r rear to do 2 shifts instead of divide by 5 |
|
int sumFront = (int)transfer.pOutput[0] + (int)transfer.pOutput[1] + (int)transfer.pOutput[2]; |
|
int sumRear = (int)transfer.pOutput[4] + (int)transfer.pOutput[5]; |
|
transfer.pOutput[3] = (sumFront + (sumRear>>1)) >> 2; |
|
#endif |
|
|
|
transfer.pOutput += 6; |
|
//Assert( baseOffset <= (DeviceSampleCount()) ); |
|
} |
|
} |
|
|
|
transfer.snd_p += outputCount << 1; |
|
if ( transfer.snd_rp ) |
|
{ |
|
transfer.snd_rp += outputCount << 1; |
|
} |
|
if ( transfer.snd_cp ) |
|
{ |
|
transfer.snd_cp += outputCount << 1; |
|
} |
|
|
|
transfer.paintedtime += outputCount; |
|
transfer.linearCount -= outputCount; |
|
|
|
} |
|
|
|
void CAudioDirectSound::S_TransferSurround16Interleaved_FullLock( const portable_samplepair_t *pfront, const portable_samplepair_t *prear, const portable_samplepair_t *pcenter, int lpaintedtime, int endtime ) |
|
{ |
|
int lpos; |
|
DWORD *pdwWrite = NULL; |
|
DWORD dwSize = 0; |
|
int i, j, *snd_p, *snd_rp, *snd_cp, volumeFactor; |
|
|
|
volumeFactor = S_GetMasterVolume() * 256; |
|
int channelCount = m_bSurroundCenter ? 5 : 4; |
|
if ( DeviceChannels() == 2 ) |
|
{ |
|
channelCount = 2; |
|
} |
|
|
|
// lock single interleaved buffer |
|
if ( !LockDSBuffer( pDSBuf, &pdwWrite, &dwSize, "DS_INTERLEAVED" ) ) |
|
{ |
|
S_Shutdown (); |
|
S_Startup (); |
|
return; |
|
} |
|
|
|
// take stereo front and rear paintbuffers, and center paintbuffer if provided, |
|
// and copy samples into the 4 or 5 mono directsound buffers |
|
|
|
snd_rp = (int *)prear; |
|
snd_cp = (int *)pcenter; |
|
snd_p = (int *)pfront; |
|
|
|
int linearCount; // space in output buffer for linearCount mono samples |
|
int sampleMonoCount = m_bufferSizeBytes/(DeviceSampleBytes()*DeviceChannels()); // number of mono samples per output buffer (was;(DeviceSampleCount()>>1)) |
|
int sampleMask = sampleMonoCount - 1; |
|
|
|
// paintedtime - number of full samples that have played since start |
|
// endtime - number of full samples to play to - endtime is g_soundtime + mixahead samples |
|
|
|
short *pOutput = (short *)pdwWrite; |
|
while (lpaintedtime < endtime) |
|
{ |
|
lpos = lpaintedtime & sampleMask; // lpos is next output position in output buffer |
|
|
|
linearCount = sampleMonoCount - lpos; |
|
|
|
// limit output count to requested number of samples |
|
|
|
if (linearCount > endtime - lpaintedtime) |
|
linearCount = endtime - lpaintedtime; |
|
|
|
if ( channelCount == 4 ) |
|
{ |
|
int baseOffset = lpos * channelCount; |
|
for (i=0, j= 0 ; i<linearCount ; i++, j+=2) |
|
{ |
|
pOutput[baseOffset+0] = (snd_p[j]*volumeFactor)>>8; // FL |
|
pOutput[baseOffset+1] = (snd_p[j + 1]*volumeFactor)>>8; // FR |
|
pOutput[baseOffset+2] = (snd_rp[j]*volumeFactor)>>8; // RL |
|
pOutput[baseOffset+3] = (snd_rp[j + 1]*volumeFactor)>>8; // RR |
|
baseOffset += 4; |
|
} |
|
} |
|
else |
|
{ |
|
Assert(channelCount==5); // 6 channel / 5.1 |
|
int baseOffset = lpos * 6; |
|
for (i=0, j= 0 ; i<linearCount ; i++, j+=2) |
|
{ |
|
pOutput[baseOffset+0] = (snd_p[j]*volumeFactor)>>8; // FL |
|
pOutput[baseOffset+1] = (snd_p[j + 1]*volumeFactor)>>8; // FR |
|
|
|
pOutput[baseOffset+2] = (snd_cp[j]*volumeFactor)>>8; // Center |
|
// NOTE: Let the hardware mix the sub from the main channels since |
|
// we don't have any sub-specific sounds, or direct sub-addressing |
|
pOutput[baseOffset+3] = 0; |
|
|
|
pOutput[baseOffset+4] = (snd_rp[j]*volumeFactor)>>8; // RL |
|
pOutput[baseOffset+5] = (snd_rp[j + 1]*volumeFactor)>>8; // RR |
|
|
|
|
|
baseOffset += 6; |
|
} |
|
} |
|
|
|
snd_p += linearCount << 1; |
|
snd_rp += linearCount << 1; |
|
snd_cp += linearCount << 1; |
|
|
|
lpaintedtime += linearCount; |
|
} |
|
|
|
pDSBuf->Unlock(pdwWrite, dwSize, NULL, 0); |
|
} |
|
|
|
void CAudioDirectSound::S_TransferSurround16Interleaved( const portable_samplepair_t *pfront, const portable_samplepair_t *prear, const portable_samplepair_t *pcenter, int lpaintedtime, int endtime ) |
|
{ |
|
if ( !pDSBuf ) |
|
return; |
|
if ( !snd_lockpartial.GetBool() ) |
|
{ |
|
S_TransferSurround16Interleaved_FullLock( pfront, prear, pcenter, lpaintedtime, endtime ); |
|
return; |
|
} |
|
// take stereo front and rear paintbuffers, and center paintbuffer if provided, |
|
// and copy samples into the 4 or 5 mono directsound buffers |
|
|
|
surround_transfer_t transfer; |
|
transfer.snd_rp = (int *)prear; |
|
transfer.snd_cp = (int *)pcenter; |
|
transfer.snd_p = (int *)pfront; |
|
|
|
int sampleMonoCount = DeviceSampleCount()/DeviceChannels(); // number of full samples per output buffer |
|
Assert(IsPowerOfTwo(sampleMonoCount)); |
|
transfer.sampleMask = sampleMonoCount - 1; |
|
transfer.paintedtime = lpaintedtime; |
|
transfer.linearCount = endtime - lpaintedtime; |
|
// paintedtime - number of full samples that have played since start |
|
// endtime - number of full samples to play to - endtime is g_soundtime + mixahead samples |
|
int channelCount = m_bSurroundCenter ? 6 : 4; |
|
if ( DeviceChannels() == 2 ) |
|
{ |
|
channelCount = 2; |
|
} |
|
transfer.channelCount = channelCount; |
|
void *pBuffer0=NULL; |
|
void *pBuffer1=NULL; |
|
DWORD size0, size1; |
|
int lpos = transfer.paintedtime & transfer.sampleMask; // lpos is next output position in output buffer |
|
|
|
int offset = lpos*2*channelCount; |
|
int lockSize = transfer.linearCount*2*channelCount; |
|
int reps = 0; |
|
HRESULT hr; |
|
while ( (hr = pDSBuf->Lock( offset, lockSize, &pBuffer0, &size0, &pBuffer1, &size1, 0 )) != DS_OK ) |
|
{ |
|
if ( hr == DSERR_BUFFERLOST ) |
|
{ |
|
if ( ++reps < 10000 ) |
|
continue; |
|
} |
|
Msg ("DS::Lock Sound Buffer Failed\n"); |
|
return; |
|
} |
|
|
|
if ( pBuffer0 ) |
|
{ |
|
transfer.pOutput = (short *)pBuffer0; |
|
TransferSamplesToSurroundBuffer( size0 / (channelCount*2), transfer ); |
|
} |
|
if ( pBuffer1 ) |
|
{ |
|
transfer.pOutput = (short *)pBuffer1; |
|
TransferSamplesToSurroundBuffer( size1 / (channelCount*2), transfer ); |
|
} |
|
pDSBuf->Unlock(pBuffer0, size0, pBuffer1, size1); |
|
} |
|
|
|
|