//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=====================================================================================// #include "audio_pch.h" #include #pragma warning(disable : 4201) // nameless struct/union #include // 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 #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>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>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>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>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>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>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>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); }