source-engine/engine/audio/snd_dev_direct.cpp

2097 lines
56 KiB
C++

//========= 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);
}