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