This commit is contained in:
nephacks
2025-06-04 03:22:50 +02:00
parent f234f23848
commit f12416cffd
14243 changed files with 6446499 additions and 26 deletions

View File

@@ -0,0 +1,46 @@
// ----------------------------------------- //
// File generated by VPC //
// ----------------------------------------- //
Source file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
Debug output file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
Release output file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\soundsystem\lowlevel\device_dsound.cpp
Debug output file: F:\csgo_64\cstrike15_src\soundsystem\lowlevel\device_dsound.cpp
Release output file: F:\csgo_64\cstrike15_src\soundsystem\lowlevel\device_dsound.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\soundsystem\lowlevel\device_null.cpp
Debug output file: F:\csgo_64\cstrike15_src\soundsystem\lowlevel\device_null.cpp
Release output file: F:\csgo_64\cstrike15_src\soundsystem\lowlevel\device_null.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\soundsystem\lowlevel\device_xaudio2.cpp
Debug output file: F:\csgo_64\cstrike15_src\soundsystem\lowlevel\device_xaudio2.cpp
Release output file: F:\csgo_64\cstrike15_src\soundsystem\lowlevel\device_xaudio2.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\soundsystem\lowlevel\mix.cpp
Debug output file: F:\csgo_64\cstrike15_src\soundsystem\lowlevel\mix.cpp
Release output file: F:\csgo_64\cstrike15_src\soundsystem\lowlevel\mix.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\soundsystem\lowlevel\simple_filter.cpp
Debug output file: F:\csgo_64\cstrike15_src\soundsystem\lowlevel\simple_filter.cpp
Release output file: F:\csgo_64\cstrike15_src\soundsystem\lowlevel\simple_filter.cpp
Containing unity file:
PCH file:
Source file: F:\csgo_64\cstrike15_src\soundsystem\lowlevel\windows_audio.cpp
Debug output file: F:\csgo_64\cstrike15_src\soundsystem\lowlevel\windows_audio.cpp
Release output file: F:\csgo_64\cstrike15_src\soundsystem\lowlevel\windows_audio.cpp
Containing unity file:
PCH file:

View File

@@ -0,0 +1,716 @@
#include "basetypes.h"
#include "commonmacros.h"
#include "soundsystem/lowlevel.h"
#include "tier1/uniqueid.h"
#include "mix.h"
#define DIRECTSOUND_VERSION 0x0800
#include "../thirdparty/dxsdk/include/dsound.h"
#pragma warning(disable : 4201) // nameless struct/union
#include <ks.h>
#include <ksmedia.h>
static HRESULT (WINAPI *g_pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
extern void ReleaseSurround(void);
static LPDIRECTSOUND pDS;
static LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
//-----------------------------------------------------------------------------
// Purpose: Implementation of direct sound
//-----------------------------------------------------------------------------
class CAudioDirectSound2 : public IAudioDevice2
{
public:
CAudioDirectSound2()
{
m_pName = "Windows DirectSound";
m_nChannels = 2;
m_nSampleBits = 16;
m_nSampleRate = 44100;
m_bIsActive = true;
m_hWindow = NULL;
m_hInstDS = 0;
m_bIsHeadphone = false;
m_bSupportsBufferStarvationDetection = false;
m_bIsCaptureDevice = false;
m_bPlayEvenWhenNotInFocus = false;
}
~CAudioDirectSound2( void );
bool Init( const audio_device_init_params_t &params );
void Shutdown( void );
void OutputBuffer( int nChannels, CAudioMixBuffer *pChannelArray );
int QueuedBufferCount();
int EmptyBufferCount();
void CancelOutput( void );
void WaitForComplete();
const wchar_t *GetDeviceID() const { return m_deviceID; }
bool SetShouldPlayWhenNotInFocus( bool bPlayEvenWhenNotInFocus ) OVERRIDE
{
if ( bPlayEvenWhenNotInFocus != m_bPlayEvenWhenNotInFocus )
return false;
return true;
}
// directsound handles this itself
void UpdateFocus( bool bWindowHasFocus ) {}
void ClearBuffer();
void OutputDebugInfo() const;
inline int BytesPerSample() { return BitsPerSample()>>3; }
// Singleton object
static CAudioDirectSound2 *m_pSingleton;
private:
// no copies of this class ever
CAudioDirectSound2( const CAudioDirectSound2 & );
CAudioDirectSound2 & operator=( const CAudioDirectSound2 & );
bool LockDSBuffer( LPDIRECTSOUNDBUFFER pBuffer, DWORD **pdwWriteBuffer, DWORD *pdwSizeBuffer, const char *pBufferName, int lockFlags = 0 );
int GetOutputPosition();
bool SNDDMA_InitInterleaved( LPDIRECTSOUND lpDS, WAVEFORMATEX* lpFormat, const audio_device_init_params_t *pParams, int nChannelCount );
int m_nTotalBufferSizeBytes; // size of a single hardware output buffer, in bytes
int m_nOneBufferInBytes;
int m_nBufferCount;
int m_nSubmitPosition;
DWORD m_nOutputBufferStartOffset; // output buffer playback starting byte offset
HINSTANCE m_hInstDS;
HWND m_hWindow;
wchar_t m_deviceID[256];
bool m_bPlayEvenWhenNotInFocus;
};
struct dsound_list_t
{
audio_device_description_t *m_pDeviceListOut;
int m_nListMax;
int m_nListCount;
};
#pragma warning(disable:4996) // suppress: deprecated use strncpy_s instead
static BOOL CALLBACK DSEnumCallback( LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext )
{
dsound_list_t *pList = reinterpret_cast<dsound_list_t *>(lpContext);
audio_device_description_t *pDesc = pList->m_pDeviceListOut + pList->m_nListCount;
if ( pList->m_nListCount < pList->m_nListMax )
{
if ( !lpGuid )
{
V_memset( pDesc->m_deviceName, 0, sizeof(pDesc->m_deviceName) );
pDesc->m_bIsDefault = true;
}
else
{
pDesc->m_bIsDefault = false;
char tempString[256];
UniqueIdToString( *reinterpret_cast<const UniqueId_t *>(lpGuid), tempString, sizeof(tempString) );
for ( int i = 0; i < sizeof(UniqueId_t); i++ )
{
pDesc->m_deviceName[i] = tempString[i];
}
}
pDesc->m_bIsAvailable = true;
V_strncpy( pDesc->m_friendlyName, lpcstrDescription, sizeof(pDesc->m_friendlyName) );
pDesc->m_nChannelCount = 2;
pDesc->m_nSubsystemId = AUDIO_SUBSYSTEM_DSOUND;
pList->m_nListCount++;
}
return 1;
}
extern HRESULT WINAPI DirectSoundEnumerateA(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext);
int Audio_EnumerateDSoundDevices( audio_device_description_t *pDeviceListOut, int listCount )
{
HINSTANCE hInstDS = LoadLibrary("dsound.dll");
HRESULT (WINAPI *pDirectSoundEnumerate)(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID lpContext);
pDirectSoundEnumerate = (long (WINAPI *)(LPDSENUMCALLBACKA ,LPVOID))GetProcAddress(hInstDS,"DirectSoundEnumerateA");
dsound_list_t list;
list.m_nListCount = 0;
list.m_nListMax = listCount;
list.m_pDeviceListOut = pDeviceListOut;
pDirectSoundEnumerate( &DSEnumCallback, (LPVOID)&list );
FreeLibrary( hInstDS );
return list.m_nListCount;
}
//-----------------------------------------------------------------------------
// Class factory
//-----------------------------------------------------------------------------
IAudioDevice2 *Audio_CreateDSoundDevice( const audio_device_init_params_t &params )
{
if ( !CAudioDirectSound2::m_pSingleton )
{
CAudioDirectSound2::m_pSingleton = new CAudioDirectSound2;
}
if ( CAudioDirectSound2::m_pSingleton->Init( params ) )
return CAudioDirectSound2::m_pSingleton;
delete CAudioDirectSound2::m_pSingleton;
CAudioDirectSound2::m_pSingleton = NULL;
Warning("Failed to initialize direct sound!\n");
return NULL;
}
CAudioDirectSound2 *CAudioDirectSound2::m_pSingleton = NULL;
// ----------------------------------------------------------------------------- //
// Helpers.
// ----------------------------------------------------------------------------- //
CAudioDirectSound2::~CAudioDirectSound2( void )
{
Shutdown();
m_pSingleton = NULL;
}
static int GetWindowsSpeakerConfig()
{
DWORD nSpeakerConfig = DSSPEAKER_STEREO;
if (DS_OK == pDS->GetSpeakerConfig( &nSpeakerConfig ))
{
// split out settings
nSpeakerConfig = DSSPEAKER_CONFIG(nSpeakerConfig);
if ( nSpeakerConfig == DSSPEAKER_7POINT1_SURROUND )
nSpeakerConfig = DSSPEAKER_7POINT1;
if ( nSpeakerConfig == DSSPEAKER_5POINT1_SURROUND)
nSpeakerConfig = DSSPEAKER_5POINT1;
switch( nSpeakerConfig )
{
case DSSPEAKER_HEADPHONE:
return 0;
case DSSPEAKER_MONO:
case DSSPEAKER_STEREO:
default:
return 2;
case DSSPEAKER_QUAD:
return 4;
case DSSPEAKER_5POINT1:
return 5;
case DSSPEAKER_7POINT1:
return 7;
}
}
return 2;
}
bool CAudioDirectSound2::Init( const audio_device_init_params_t &params )
{
DSBUFFERDESC dsbuf;
DSBCAPS dsbcaps;
WAVEFORMATEX format;
WAVEFORMATEX pformat;
HRESULT hresult;
bool primary_format_set = false;
// if a specific device was requested use that one, otherwise use the default (NULL means default)
LPGUID pGUID = NULL;
UniqueId_t overrideGUID;
m_bPlayEvenWhenNotInFocus = params.m_bPlayEvenWhenNotInFocus;
if ( params.m_bOverrideDevice )
{
char tempString[ Q_ARRAYSIZE(params.m_overrideDeviceName) ];
int nLen = V_wcslen( params.m_overrideDeviceName );
if ( nLen > 0 )
{
for ( int i = 0; i < nLen+1; i++ )
{
tempString[i] = params.m_overrideDeviceName[i] & 0xFF;
}
UniqueIdFromString( &overrideGUID, tempString );
pGUID = &(( GUID &)overrideGUID);
V_wcscpy_safe( m_deviceID, params.m_overrideDeviceName );
}
}
if ( !pGUID )
{
V_memset( m_deviceID, 0, sizeof(m_deviceID) );
}
if (!m_hInstDS)
{
m_hInstDS = LoadLibrary("dsound.dll");
if (m_hInstDS == NULL)
{
Warning( "Couldn't load dsound.dll\n");
return false;
}
g_pDirectSoundCreate = (long (__stdcall *)(struct _GUID *,struct IDirectSound ** ,struct IUnknown *))GetProcAddress(m_hInstDS,"DirectSoundCreate");
if (!g_pDirectSoundCreate)
{
Warning( "Couldn't get DS proc addr\n");
return false;
}
}
while ((hresult = g_pDirectSoundCreate(pGUID, &pDS, NULL)) != DS_OK)
{
if (hresult == DSERR_ALLOCATED)
{
Warning("DirectSound hardware in use, can't initialize!\n");
return false;
}
Warning("DirectSound Create failed.\n");
return false;
}
int nSpeakerConfig = -1;
if ( params.m_bOverrideSpeakerConfig )
{
nSpeakerConfig = params.m_nOverrideSpeakerConfig;
}
else
{
nSpeakerConfig = GetWindowsSpeakerConfig();
}
if ( nSpeakerConfig == 0 )
{
m_bIsHeadphone = true;
}
// NOTE: This is basically the same as SpeakerConfigToChannelCount() but we
// have to set primary channels correctly and DirectSound maps 7.1 to 5.1
// at the moment (seems like it could support it easily though)
int nPrimaryChannels = 2;
switch( nSpeakerConfig )
{
// stereo
case 0:
case 2:
default:
nPrimaryChannels = 2;
m_nChannels = 2; // secondary buffers should have same # channels as primary
break;
// surround, use mono 3d primary buffer
// quad surround
case 4:
nPrimaryChannels = 1;
m_nChannels = 4;
break;
case 5:
case 7:
nPrimaryChannels = 1;
m_nChannels = 6;
break;
}
V_memset( &format, 0, sizeof(format) );
format.wFormatTag = WAVE_FORMAT_PCM;
format.nChannels = nPrimaryChannels;
format.wBitsPerSample = BitsPerSample();
format.nSamplesPerSec = SampleRate();
format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
format.cbSize = 0;
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
DSCAPS dscaps;
V_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 false;
}
m_hWindow = (HWND)params.m_pWindowHandle;
DWORD dwCooperativeLevel = DSSCL_EXCLUSIVE;
if (DS_OK != pDS->SetCooperativeLevel( m_hWindow, dwCooperativeLevel ) )
{
Warning( "Set coop level failed\n");
Shutdown();
return false;
}
// get access to the primary buffer, if possible, so we can set the
// sound hardware format
V_memset( &dsbuf, 0, sizeof(dsbuf) );
dsbuf.dwSize = sizeof(DSBUFFERDESC);
dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
if ( m_nChannels > 2 )
{
dsbuf.dwFlags |= DSBCAPS_CTRL3D;
Assert( nPrimaryChannels == 1 );
}
dsbuf.dwBufferBytes = 0;
dsbuf.lpwfxFormat = NULL;
V_memset( &dsbcaps, 0, sizeof(dsbcaps) );
dsbcaps.dwSize = sizeof(dsbcaps);
if ( 1 )
{
if (DS_OK == pDS->CreateSoundBuffer(&dsbuf, &pDSPBuf, NULL))
{
pformat = format;
if (DS_OK != pDSPBuf->SetFormat(&pformat))
{
}
else
{
primary_format_set = true;
}
}
}
bool bRet = SNDDMA_InitInterleaved( pDS, &format, &params, m_nChannels );
// number of mono samples output buffer may hold
m_nSubmitPosition = 0;
return bRet;
}
void CAudioDirectSound2::Shutdown( void )
{
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 && m_hWindow )
{
pDS->SetCooperativeLevel( m_hWindow, DSSCL_NORMAL);
pDS->Release();
}
pDS = NULL;
pDSBuf = NULL;
pDSPBuf = NULL;
if ( m_hInstDS )
{
FreeLibrary( m_hInstDS );
m_hInstDS = NULL;
}
if ( this == CAudioDirectSound2::m_pSingleton )
{
CAudioDirectSound2::m_pSingleton = NULL;
}
}
void CAudioDirectSound2::OutputDebugInfo() const
{
Msg( "Direct Sound Device\n" );
Msg( "Channels:\t%d\n", ChannelCount() );
Msg( "Bits/Sample:\t%d\n", BitsPerSample() );
Msg( "Rate:\t\t%d\n", SampleRate() );
}
void CAudioDirectSound2::CancelOutput( void )
{
if (pDSBuf)
{
DWORD dwSize;
DWORD *pData;
int reps;
HRESULT hresult;
reps = 0;
while ((hresult = pDSBuf->Lock(0, m_nTotalBufferSizeBytes, (void**)&pData, &dwSize, NULL, NULL, 0)) != DS_OK)
{
if (hresult != DSERR_BUFFERLOST)
{
Msg("S_ClearBuffer: DS::Lock Sound Buffer Failed\n");
return;
}
if (++reps > 10000)
{
Msg("S_ClearBuffer: DS: couldn't restore buffer\n");
return;
}
}
V_memset(pData, 0, dwSize);
pDSBuf->Unlock(pData, dwSize, NULL, 0);
}
}
bool CAudioDirectSound2::SNDDMA_InitInterleaved( LPDIRECTSOUND lpDS, WAVEFORMATEX* lpFormat, const audio_device_init_params_t *pParams, int nChannelCount )
{
WAVEFORMATEXTENSIBLE wfx = { 0 } ; // DirectSoundBuffer wave format (extensible)
// set the channel mask and number of channels based on the command line parameter
if( nChannelCount == 2 )
{
wfx.Format.nChannels = 2;
wfx.dwChannelMask = KSAUDIO_SPEAKER_STEREO; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
}
else if( nChannelCount == 4 )
{
wfx.Format.nChannels = 4;
wfx.dwChannelMask = KSAUDIO_SPEAKER_QUAD; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
}
else if( nChannelCount == 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
int nBytesPerSample = lpFormat->wBitsPerSample / 8;
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
//wfx.Format.nChannels = SET ABOVE
wfx.Format.nSamplesPerSec = lpFormat->nSamplesPerSec;
wfx.Format.wBitsPerSample = lpFormat->wBitsPerSample;
wfx.Format.nBlockAlign = nBytesPerSample * 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);
DWORD nBaseFlags = pParams->m_bPlayEvenWhenNotInFocus ? DSBCAPS_GLOBALFOCUS : 0;
m_nOneBufferInBytes = ( MIX_BUFFER_SIZE * nBytesPerSample * nChannelCount );
m_nBufferCount = pParams->m_nOutputBufferCount + 1;
m_nTotalBufferSizeBytes = m_nBufferCount * m_nOneBufferInBytes;
dsbdesc.dwBufferBytes = m_nTotalBufferSizeBytes;
dsbdesc.lpwfxFormat = (WAVEFORMATEX*)&wfx;
bool bSuccess = false;
for ( int i = 0; i < 3; i++ )
{
switch(i)
{
case 0:
dsbdesc.dwFlags = nBaseFlags | DSBCAPS_LOCHARDWARE;
break;
case 1:
dsbdesc.dwFlags = nBaseFlags | DSBCAPS_LOCSOFTWARE;
break;
case 2:
dsbdesc.dwFlags = nBaseFlags;
break;
}
if(!FAILED(lpDS->CreateSoundBuffer(&dsbdesc, &pDSBuf, NULL)))
{
bSuccess = true;
break;
}
}
if ( !bSuccess )
{
dsbdesc.dwFlags = nBaseFlags;
if(FAILED(lpDS->CreateSoundBuffer(&dsbdesc, &pDSBuf, NULL)))
{
wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
wfx.Format.cbSize = 0;
dsbdesc.dwFlags = DSBCAPS_LOCSOFTWARE | nBaseFlags;
HRESULT hr = lpDS->CreateSoundBuffer(&dsbdesc, &pDSBuf, NULL);
if(FAILED(hr))
{
printf("Failed %d\n", hr );
return false;
}
}
}
DWORD dwSize = 0, dwWrite;
DWORD *pBuffer = 0;
if ( !LockDSBuffer( pDSBuf, &pBuffer, &dwSize, "DS_INTERLEAVED", DSBLOCK_ENTIREBUFFER ) )
return false;
m_nChannels = wfx.Format.nChannels;
V_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_nOutputBufferStartOffset, &dwWrite);
pDSBuf->Play(0, 0, DSBPLAY_LOOPING);
return true;
}
bool CAudioDirectSound2::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_nTotalBufferSizeBytes, (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;
}
void CAudioDirectSound2::OutputBuffer( int nMixChannelCount, CAudioMixBuffer *pMixChannels )
{
HRESULT hr;
int nDeviceChannelCount = ChannelCount();
int nOutputSize = BytesPerSample() * nDeviceChannelCount * MIX_BUFFER_SIZE;
void *pBuffer0=NULL;
void *pBuffer1=NULL;
DWORD nSize0, nSize1;
int nReps = 0;
while ( (hr = pDSBuf->Lock( m_nSubmitPosition, nOutputSize, &pBuffer0, &nSize0, &pBuffer1, &nSize1, 0 )) != DS_OK )
{
if ( hr == DSERR_BUFFERLOST )
{
if ( ++nReps < 10000 )
continue;
}
Msg ("DS::Lock Sound Buffer Failed\n");
return;
}
int nStart = 0;
if ( pBuffer0 )
{
short *pOut = (short *)pBuffer0;
int nSamplesOut = nSize0 / (nDeviceChannelCount*2);
if ( nMixChannelCount == 2 && nMixChannelCount == nDeviceChannelCount )
{
ConvertFloat32Int16_Clamp_Interleave2( pOut, pMixChannels[0].m_flData, pMixChannels[1].m_flData, nSamplesOut );
}
else
{
ConvertFloat32Int16_Clamp_InterleaveStride( pOut, nDeviceChannelCount, MIX_BUFFER_SIZE, pMixChannels[0].m_flData, nMixChannelCount, nSamplesOut );
}
nStart = nSamplesOut;
}
if ( pBuffer1 )
{
short *pOut = (short *)pBuffer1;
int nSamplesOut = nSize1 / (nDeviceChannelCount*2);
if ( nMixChannelCount == 2 && nMixChannelCount == nDeviceChannelCount )
{
ConvertFloat32Int16_Clamp_Interleave2( pOut, pMixChannels[0].m_flData, pMixChannels[1].m_flData, nSamplesOut );
}
else
{
ConvertFloat32Int16_Clamp_InterleaveStride( pOut, nDeviceChannelCount, MIX_BUFFER_SIZE, pMixChannels[0].m_flData, nMixChannelCount, nSamplesOut );
}
}
pDSBuf->Unlock(pBuffer0, nSize0, pBuffer1, nSize1);
m_nSubmitPosition += nOutputSize;
m_nSubmitPosition %= m_nTotalBufferSizeBytes;
}
int CAudioDirectSound2::QueuedBufferCount()
{
int nStart, nCurrent;
DWORD dwCurrentPlayCursor;
// get size in bytes of output buffer
const int nSizeInBytes = m_nTotalBufferSizeBytes;
// multi-channel interleaved output buffer
// get byte offset of playback cursor in output buffer
HRESULT hr = pDSBuf->GetCurrentPosition(&dwCurrentPlayCursor, NULL);
if ( hr != S_OK )
return m_nBufferCount;
if ( dwCurrentPlayCursor > DWORD(m_nTotalBufferSizeBytes) )
{
// BUGBUG: ??? what do do here?
DebuggerBreakIfDebugging();
dwCurrentPlayCursor %= m_nTotalBufferSizeBytes;
}
nStart = (int) m_nOutputBufferStartOffset;
nCurrent = (int) dwCurrentPlayCursor;
// get 16 bit samples played, relative to buffer starting offset
int delta = m_nSubmitPosition - nCurrent;
if ( delta < 0 )
{
delta += nSizeInBytes;
}
int nSamples = delta / (ChannelCount() * BytesPerSample());
int nBuffers = nSamples / MIX_BUFFER_SIZE;
//int nTotalBuffers = (m_nTotalBufferSizeBytes/ (ChannelCount() * BytesPerSample())) / MIX_BUFFER_SIZE;
//Msg("%d buffers remain %d total\n", nBuffers, nTotalBuffers);
if ( nBuffers == 0 )
{
//Msg("cursor %d, submit %d, relative %d\n", nCurrent, m_nSubmitPosition, delta );
}
return nBuffers;
}
int CAudioDirectSound2::EmptyBufferCount()
{
return (m_nBufferCount-1) - QueuedBufferCount();
}
void CAudioDirectSound2::WaitForComplete()
{
while ( QueuedBufferCount() )
{
ThreadSleep(0);
}
}
void CAudioDirectSound2::ClearBuffer( void )
{
DWORD dwSize = 0;
DWORD *pBuffer = 0;
if ( LockDSBuffer( pDSBuf, &pBuffer, &dwSize, "DS_INTERLEAVED", DSBLOCK_ENTIREBUFFER ) )
{
V_memset( pBuffer, 0, dwSize );
pDSBuf->Unlock(pBuffer, dwSize, NULL, 0);
}
}

View File

@@ -0,0 +1,319 @@
#include "basetypes.h"
#include "commonmacros.h"
#include "soundsystem/lowlevel.h"
#include "soundsystem/audio_mix.h"
CInterlockedInt g_nDetectedAudioError(0);
CInterlockedInt g_nDetectedBufferStarvation( 0 );
uint g_nDeviceStamp = 0x100;
class CAudioDeviceNull2 : public IAudioDevice2
{
public:
CAudioDeviceNull2()
{
m_pName = "Sound Disabled";
m_nChannels = 2;
m_nSampleBits = 16;
m_nSampleRate = int(MIX_DEFAULT_SAMPLING_RATE);
m_bIsActive = false;
m_bIsHeadphone = false;
m_bSupportsBufferStarvationDetection = false;
m_bIsCaptureDevice = false;
}
virtual ~CAudioDeviceNull2() {}
virtual void OutputBuffer( int nChannels, CAudioMixBuffer *pChannelArray ) {}
virtual void Shutdown() {}
virtual int QueuedBufferCount() { return 0; }
virtual int EmptyBufferCount() { return 0; }
virtual void CancelOutput() {}
virtual void WaitForComplete() {}
virtual void UpdateFocus( bool bWindowHasFocus ) {}
virtual void ClearBuffer() {}
virtual const wchar_t *GetDeviceID() const
{
static wchar_t deviceID[4] = {0};
return deviceID;
}
virtual void OutputDebugInfo() const
{
Msg( "Sound Disabled.\n" );
}
virtual bool SetShouldPlayWhenNotInFocus( bool bPlayEvenWhenNotInFocus ) OVERRIDE
{
return true;
}
};
IAudioDevice2 *Audio_CreateNullDevice()
{
return new CAudioDeviceNull2;
}
int Audio_EnumerateDevices( eSubSystems_t nSubsystem, audio_device_description_t *pDeviceListOut, int nListCount )
{
int nDeviceCount = 0;
switch( nSubsystem )
{
#ifdef IS_WINDOWS_PC
case AUDIO_SUBSYSTEM_XAUDIO:
nDeviceCount = Audio_EnumerateXAudio2Devices( pDeviceListOut, nListCount );
break;
case AUDIO_SUBSYSTEM_DSOUND:
nDeviceCount = Audio_EnumerateDSoundDevices( pDeviceListOut, nListCount );
break;
#endif
#ifdef POSIX
case AUDIO_SUBSYSTEM_SDL:
nDeviceCount = Audio_EnumerateSDLDevices( pDeviceListOut, nListCount );
break;
#endif
case AUDIO_SUBSYSTEM_NULL:
nDeviceCount = 1;
if ( nListCount > 0 )
{
pDeviceListOut[0].InitAsNullDevice();
V_strcpy_safe( pDeviceListOut[0].m_friendlyName, "Sound Disabled" );
}
break;
}
return nDeviceCount;
}
int CAudioDeviceList::FindDeviceById( const wchar_t *pId, finddevice_t nFind )
{
for ( int i = 0; i < m_list.Count(); i++ )
{
if ( nFind == FIND_AVAILABLE_DEVICE_ONLY && !m_list[i].m_bIsAvailable )
continue;
if ( !V_wcscmp( pId, m_list[i].m_deviceName ) )
return i;
}
return -1;
}
audio_device_description_t *CAudioDeviceList::FindDeviceById( const char *pId )
{
wchar_t tempName[256];
V_strtowcs( pId, -1, tempName, Q_ARRAYSIZE(tempName) );
int nDevice = FindDeviceById( tempName, FIND_AVAILABLE_DEVICE_ONLY );
if ( nDevice >= 0 && nDevice < m_list.Count() )
return &m_list[nDevice];
return NULL;
}
void CAudioDeviceList::BuildDeviceList( eSubSystems_t nPreferredSubsystem )
{
m_nDeviceStamp = g_nDeviceStamp;
audio_device_description_t initList[32];
int nCount = Audio_EnumerateDevices( nPreferredSubsystem, initList, Q_ARRAYSIZE(initList) );
// No XAudio2? Fall back to direct sound.
if ( nCount == 0 && nPreferredSubsystem == AUDIO_SUBSYSTEM_XAUDIO )
{
nPreferredSubsystem = AUDIO_SUBSYSTEM_DSOUND;
nCount = Audio_EnumerateDevices( nPreferredSubsystem, initList, Q_ARRAYSIZE(initList) );
}
m_nSubsystem = nPreferredSubsystem;
// No sound devices? Add a NULL device.
if ( nCount == 0 )
{
nCount = Audio_EnumerateDevices( AUDIO_SUBSYSTEM_NULL, initList, Q_ARRAYSIZE(initList) );
}
if ( !m_list.Count() )
{
m_list.CopyArray( initList, nCount );
for ( int i = 0; i < m_list.Count(); i++ )
{
m_list[i].m_bIsAvailable = true;
}
}
else
{
for ( int i = 0; i < m_list.Count(); i++ )
{
m_list[i].m_bIsAvailable = false;
m_list[i].m_bIsDefault = false;
}
for ( int i = 0; i < nCount; i++ )
{
int nIndex = FindDeviceById( initList[i].m_deviceName, FIND_ANY_DEVICE );
if ( nIndex >= 0 )
{
m_list[nIndex] = initList[i];
}
else
{
m_list.AddToTail( initList[i] );
}
}
}
UpdateDefaultDevice();
}
bool CAudioDeviceList::UpdateDeviceList()
{
if ( m_nDeviceStamp == g_nDeviceStamp )
return false;
BuildDeviceList( m_nSubsystem );
return true;
}
void CAudioDeviceList::UpdateDefaultDevice()
{
#if IS_WINDOWS_PC
// BUG: DirectSound devices use a different string format for GUIDs. Fix so this works?
wchar_t deviceName[256];
if ( GetWindowsDefaultAudioDevice( deviceName, sizeof(deviceName ) ) )
{
int nIndex = FindDeviceById( deviceName, FIND_AVAILABLE_DEVICE_ONLY );
if ( nIndex >= 0 )
{
m_nDefaultDevice = nIndex;
return;
}
}
#endif
m_nDefaultDevice = -1;
int nFirst = -1;
for ( int i = 0; i < m_list.Count(); i++ )
{
if ( m_list[i].m_bIsAvailable )
{
if ( nFirst < 0 )
{
nFirst = i;
}
if ( m_list[i].m_bIsDefault )
{
m_nDefaultDevice = i;
break;
}
}
}
if ( m_nDefaultDevice < 0 )
{
m_nDefaultDevice = nFirst;
}
}
IAudioDevice2 *CAudioDeviceList::CreateDevice( audio_device_init_params_t &params )
{
Assert( IsValid() );
int nSubsystem = m_nSubsystem;
#if !defined( _GAMECONSOLE )
if ( params.m_bOverrideDevice )
{
nSubsystem = params.m_nOverrideSubsystem;
}
#endif
#if IS_WINDOWS_PC
// try xaudio2
if ( nSubsystem == AUDIO_SUBSYSTEM_XAUDIO )
{
IAudioDevice2 *pDevice = Audio_CreateXAudio2Device( params );
if ( pDevice )
return pDevice;
Warning("Failed to initialize XAudio2 device!\n");
nSubsystem = AUDIO_SUBSYSTEM_DSOUND;
}
if ( nSubsystem == AUDIO_SUBSYSTEM_DSOUND )
{
// either we were asked for dsound or we failed to create xaudio2, try dsound
IAudioDevice2 *pDevice = Audio_CreateDSoundDevice( params );
if ( pDevice )
return pDevice;
Warning("Failed to initialize DirectSound device!\n");
nSubsystem = AUDIO_SUBSYSTEM_NULL;
}
#endif
#ifdef POSIX
nSubsystem = AUDIO_SUBSYSTEM_SDL;
if ( nSubsystem == AUDIO_SUBSYSTEM_SDL )
{
IAudioDevice2 *pDevice = Audio_CreateSDLDevice( params );
if ( pDevice )
return pDevice;
Warning("Failed to initialize SDL device!\n");
nSubsystem = AUDIO_SUBSYSTEM_NULL;
}
#endif
// failed
return Audio_CreateNullDevice();
}
const wchar_t *CAudioDeviceList::GetDeviceToCreate( audio_device_init_params_t &params )
{
if ( params.m_bOverrideDevice )
{
int nIndex = FindDeviceById( params.m_overrideDeviceName, FIND_AVAILABLE_DEVICE_ONLY );
if ( nIndex >= 0 )
{
return m_list[nIndex].m_deviceName;
}
}
Assert( m_nDefaultDevice >= 0 && m_nDefaultDevice < m_list.Count() );
return m_list[m_nDefaultDevice].m_deviceName;
}
int SpeakerConfigValueToChannelCount( int nSpeakerConfig )
{
// headphone or stereo
switch( nSpeakerConfig )
{
case -1:
return 0;
case 0: // headphone
case 2: // stereo
return 2;
case 4: // quad surround
return 4;
case 5: // 5.1 surround
return 6;
case 7: // 7.1 surround
return 8;
}
// doesn't map to anything, return stereo
AssertMsg( 0, "Bad speaker config requested\n");
return 2;
}
int ChannelCountToSpeakerConfigValue( int nChannelCount, bool bIsHeadphone )
{
switch( nChannelCount )
{
case 2:
return bIsHeadphone ? 0 : 2;
case 4:
return 4;
case 6:
return 5;
case 8:
return 7;
}
AssertMsg(0, "Bad channel count\n");
return 2;
}
bool Audio_PollErrorEvents()
{
int nError = g_nDetectedAudioError.InterlockedExchange(0);
return nError != 0;
}

View File

@@ -0,0 +1,493 @@
#include "basetypes.h"
#include "commonmacros.h"
#include "SDL.h"
#include "mix.h"
#include "soundsystem/lowlevel.h"
#define DEFAULT_DEVICE_NAME "SDLDefaultDevice"
#define DEFAULT_DEVICE_NAME_WIDE L"SDLDefaultDevice"
class CAudioSDL : public IAudioDevice2
{
public:
CAudioSDL()
{
m_nDeviceID = 0;
m_nDeviceIndex = -1;
m_nBufferSizeBytes = 0;
m_nBufferCount = 0;
m_bIsActive = true;
m_bIsHeadphone = false;
m_bSupportsBufferStarvationDetection = false;
m_bIsCaptureDevice = false;
m_nReadBuffer = m_nWriteBuffer = 0;
m_nPartialRead = 0;
m_bAudioStarted = false;
m_bSilenced = false;
m_fSilencedVol = 1.0f;
V_memset( m_pBuffer, 0, sizeof( m_pBuffer ) );
};
~CAudioSDL();
bool Init( const audio_device_init_params_t &params );
void OutputBuffer( int nChannels, CAudioMixBuffer *pChannelArray );
void Shutdown();
int QueuedBufferCount();
int EmptyBufferCount();
void CancelOutput();
void WaitForComplete();
void UpdateFocus( bool bWindowHasFocus );
void ClearBuffer();
const wchar_t *GetDeviceID() const;
void OutputDebugInfo() const;
virtual bool SetShouldPlayWhenNotInFocus( bool bPlayEvenWhenNotInFocus )
{
m_savedParams.m_bPlayEvenWhenNotInFocus = bPlayEvenWhenNotInFocus;
return true;
}
// inline methods
inline int BytesPerSample() { return BitsPerSample()>>3; }
void FillAudioBuffer( Uint8 *buf, int len );
private:
// no copies of this class ever
CAudioSDL( const CAudioSDL & );
CAudioSDL & operator=( const CAudioSDL & );
int SamplesPerBuffer() { return MIX_BUFFER_SIZE; }
int BytesPerBuffer() { return m_nBufferSizeBytes; }
SDL_AudioDeviceID m_nDeviceID;
SDL_AudioSpec m_deviceSpec;
audio_device_init_params_t m_savedParams;
int m_nDeviceIndex;
uint m_nBufferSizeBytes;
uint m_nBufferCount;
wchar_t m_deviceID[256];
enum { kNumBuffers = 32 };
short *m_pBuffer[ kNumBuffers ];
int m_nReadBuffer, m_nWriteBuffer;
int m_nPartialRead;
bool m_bAudioStarted;
CThreadMutex m_mutexBuffer;
bool m_bSilenced;
float m_fSilencedVol;
};
CAudioSDL::~CAudioSDL()
{
Shutdown();
for ( int i = 0; i != kNumBuffers; ++i )
{
if ( m_pBuffer[ i ] )
{
MemAlloc_FreeAligned( m_pBuffer[ i ] );
}
}
}
static void AudioCallback( void *userdata, Uint8 *stream, int len )
{
CAudioSDL *dev = reinterpret_cast<CAudioSDL*>( userdata );
dev->FillAudioBuffer( stream, len );
}
bool CAudioSDL::Init( const audio_device_init_params_t &params )
{
m_savedParams = params;
int nDeviceCount = SDL_GetNumAudioDevices( 0 );
if ( !nDeviceCount )
return false;
m_nDeviceIndex = -1;
if ( params.m_bOverrideDevice )
{
if ( wcscmp( params.m_overrideDeviceName, DEFAULT_DEVICE_NAME_WIDE ) == 0 )
{
m_nDeviceIndex = -1;
}
else
{
for( int i = 0; i < nDeviceCount; ++i )
{
const char *devName = SDL_GetAudioDeviceName( i, 0 );
if ( devName == NULL )
{
continue;
}
wchar_t devNameWide[AUDIO_DEVICE_NAME_MAX];
Q_UTF8ToWString( devName, devNameWide, sizeof( devNameWide ) );
if ( wcscmp( devNameWide, params.m_overrideDeviceName ) == 0 )
{
m_nDeviceIndex = i;
break;
}
}
}
}
#if 0
// setup the format structure
{
int nChannels = details.InputChannels;
if ( params.m_bOverrideSpeakerConfig )
{
nChannels = SpeakerConfigValueToChannelCount( params.m_nOverrideSpeakerConfig );
if ( params.m_nOverrideSpeakerConfig == 0 )
{
m_bIsHeadphone = true;
}
}
m_nChannels = nChannels;
}
#endif
SDL_AudioSpec desired;
desired.freq = int(MIX_DEFAULT_SAMPLING_RATE);
desired.format = AUDIO_S16SYS;
desired.channels = 2;
desired.samples = 1024;
desired.callback = AudioCallback;
desired.userdata = this;
m_nDeviceID = SDL_OpenAudioDevice( m_nDeviceIndex == -1 ? NULL : SDL_GetAudioDeviceName( m_nDeviceIndex, 0 ), 0, &desired, &m_deviceSpec, SDL_AUDIO_ALLOW_ANY_CHANGE );
const char *pDeviceNameUTF8 = m_nDeviceIndex == -1 ? DEFAULT_DEVICE_NAME : SDL_GetAudioDeviceName( m_nDeviceIndex, 0 );
Q_UTF8ToWString( pDeviceNameUTF8, m_deviceID, sizeof( m_deviceID ) );
// UNDONE: Have the device report MIX_DEFAULT_SAMPLING_RATE to the outside world
// and build an SDL_AudioCVT to convert from the engine's mix to the SDL device's audio output?
// BUGBUG: Assert this for now
Assert( m_deviceSpec.channels == 2 );
Assert( m_deviceSpec.freq == int(MIX_DEFAULT_SAMPLING_RATE) );
m_nChannels = m_deviceSpec.channels;
m_nSampleBits = SDL_AUDIO_BITSIZE( m_deviceSpec.format );
m_nSampleRate = m_deviceSpec.freq;
m_bIsActive = true;
m_bIsHeadphone = false;
m_pName = "SDL Audio";
//m_nBufferCount = params.m_nOutputBufferCount;
m_nBufferCount = 1;
int nBufferSize = MIX_BUFFER_SIZE * m_nChannels * BytesPerSample();
m_nBufferSizeBytes = nBufferSize;
for ( int i = 0; i != kNumBuffers; ++i )
{
m_pBuffer[ i ] = (short *)MemAlloc_AllocAligned( nBufferSize * m_nBufferCount, 16 );
}
m_nReadBuffer = m_nWriteBuffer = 0;
m_nPartialRead = 0;
m_bAudioStarted = false;
// start audio playback
SDL_PauseAudioDevice( m_nDeviceID, 0 );
#if defined( LINUX ) && defined( INCLUDE_SCALEFORM )
// Send the obtained audio device details to scaleform
if ( g_pScaleformUI )
{
g_pScaleformUI->SDLSetAudioSpec( sizeof(m_deviceSpec), &m_deviceSpec );
}
#endif
return true;
}
void CAudioSDL::OutputBuffer( int nChannels, CAudioMixBuffer *pChannelArray )
{
AUTO_LOCK( m_mutexBuffer );
m_bAudioStarted = true;
if ( ( (m_nWriteBuffer+1) % kNumBuffers ) == m_nReadBuffer )
{
// Filled up with data, can't take anymore.
// This shouldn't really happen unless SDL stops consuming data for us or the
// game pushes data at us at an unreasonable rate.
return;
}
short *pWaveData = m_pBuffer[ m_nWriteBuffer ];
m_nWriteBuffer = (m_nWriteBuffer+1) % kNumBuffers;
if ( nChannels == 2 && nChannels == m_nChannels )
{
ConvertFloat32Int16_Clamp_Interleave2( pWaveData, pChannelArray[0].m_flData, pChannelArray[1].m_flData, MIX_BUFFER_SIZE );
}
else
{
ConvertFloat32Int16_Clamp_InterleaveStride( pWaveData, m_nChannels, MIX_BUFFER_SIZE, pChannelArray[0].m_flData, nChannels, MIX_BUFFER_SIZE );
}
#if defined( LINUX ) && defined( INCLUDE_SCALEFORM )
if ( g_pScaleformUI )
{
g_pScaleformUI->SDLMixAudio( nChannels, pWaveData, m_nBufferSizeBytes );
}
#endif
// Old way of sending data, by queueing it. Now we do it by providing it in the callback.
// SDL_QueueAudio( m_nDeviceID, m_pBuffer, m_nBufferSizeBytes );
}
void CAudioSDL::Shutdown()
{
if ( m_nDeviceID > 0 )
{
SDL_CloseAudioDevice( m_nDeviceID );
m_nDeviceID = 0;
}
}
int CAudioSDL::QueuedBufferCount()
{
AUTO_LOCK( m_mutexBuffer );
if ( m_nWriteBuffer >= m_nReadBuffer )
{
return m_nWriteBuffer - m_nReadBuffer;
}
else
{
return (kNumBuffers - m_nReadBuffer) + m_nWriteBuffer;
}
}
int CAudioSDL::EmptyBufferCount()
{
return (kNumBuffers - QueuedBufferCount()) - 1;
}
void CAudioSDL::CancelOutput()
{
// SDL_ClearQueuedAudio( m_nDeviceID );
}
void CAudioSDL::WaitForComplete()
{
while( QueuedBufferCount() )
{
ThreadSleep(0);
}
}
void CAudioSDL::UpdateFocus( bool bWindowHasFocus )
{
m_bSilenced = !bWindowHasFocus && !m_savedParams.m_bPlayEvenWhenNotInFocus;
}
void CAudioSDL::ClearBuffer()
{
}
const wchar_t* CAudioSDL::GetDeviceID() const
{
return L"SDL Device";
}
void CAudioSDL::OutputDebugInfo() const
{
fprintf(stderr, "SDL Audio Device\n" );
fprintf(stderr, "Channels:\t%d\n", ChannelCount() );
fprintf(stderr, "Bits/Sample:\t%d\n", BitsPerSample() );
fprintf(stderr, "Rate:\t\t%d\n", SampleRate() );
}
void CAudioSDL::FillAudioBuffer( Uint8 *buf, int len )
{
m_mutexBuffer.Lock();
bool bFailedToGetMore = false;
while ( m_bAudioStarted && len > 0 && !bFailedToGetMore )
{
if ( m_nReadBuffer == m_nWriteBuffer )
{
m_mutexBuffer.Unlock();
/*
if ( m_savedParams.m_pfnMixAudio != NULL )
{
//We are going to be starved, so ask the mixer to mix
//some more audio for us right now. We expect this
//call to fill our buffers up.
m_savedParams.m_pfnMixAudio( 0.01 );
}
*/
m_mutexBuffer.Lock();
if ( m_nReadBuffer == m_nWriteBuffer )
{
//The mixer couldn't get us more data for some reason.
//We are starved.
bFailedToGetMore = true;
break;
}
}
while ( len > 0 && m_nReadBuffer != m_nWriteBuffer )
{
int bufsize = m_nBufferSizeBytes - m_nPartialRead;
int nbytes = len < bufsize ? len : bufsize;
if(m_bSilenced && m_fSilencedVol <= 0.0f)
{
memset( buf, 0, nbytes );
}
else
{
memcpy( buf, ((unsigned char*)m_pBuffer[ m_nReadBuffer ]) + m_nPartialRead, nbytes );
// If we are silencing or unsilencing, make the volume fade rather than
// changing abruptly.
static const float FadeTime = 0.5f;
static const int FadeTick = 16;
static const float FadeDelta = FadeTick / ( m_nChannels * m_nSampleRate * FadeTime );
if ( m_bSilenced )
{
short* sbuf = reinterpret_cast<short*>(buf);
int i = 0;
while ( i < nbytes/2 )
{
sbuf[i] *= m_fSilencedVol;
++i;
if ( i%FadeTick == 0 && m_fSilencedVol > 0.0f )
{
m_fSilencedVol -= FadeDelta;
if ( m_fSilencedVol < 0.0f )
{
m_fSilencedVol = 0.0f;
}
}
}
}
else if ( m_fSilencedVol < 1.0f )
{
short* sbuf = reinterpret_cast<short*>(buf);
int i = 0;
while ( i < nbytes/2 && m_fSilencedVol < 1.0f )
{
sbuf[i] *= m_fSilencedVol;
++i;
if ( i%FadeTick == 0 && m_fSilencedVol < 1.0f )
{
m_fSilencedVol += FadeDelta;
}
}
}
}
if ( nbytes == bufsize )
{
m_nReadBuffer = (m_nReadBuffer+1) % kNumBuffers;
m_nPartialRead = 0;
}
else
{
m_nPartialRead += nbytes;
}
buf += nbytes;
len -= nbytes;
}
}
m_mutexBuffer.Unlock();
if ( len > 0 )
{
// We have been starved of data and have to fill with silence.
memset( buf, 0, len );
}
}
static bool g_bInitSDLAudio = false;
static bool InitSDLAudio()
{
if ( !g_bInitSDLAudio )
{
int nRet = SDL_InitSubSystem( SDL_INIT_AUDIO );
if ( nRet < 0 )
{
return false;
}
g_bInitSDLAudio = true;
}
return true;
}
// enumerate the available devices so the app can select one
// fills out app-supplied list & returns count of available devices. If the list is too small, the count
// will signal the app to call again with a larger list
int Audio_EnumerateSDLDevices( audio_device_description_t *pDeviceListOut, int nListCount )
{
if ( !InitSDLAudio() )
return 0;
if ( nListCount > 0 )
{
audio_device_description_t& description = pDeviceListOut[0];
Q_UTF8ToWString( DEFAULT_DEVICE_NAME, description.m_deviceName, sizeof(description.m_deviceName) );
V_strcpy_safe( description.m_friendlyName, "#OS_Default_Device" );
description.m_nChannelCount = 6;
description.m_bIsDefault = true;
description.m_bIsAvailable = true;
description.m_nSubsystemId = AUDIO_SUBSYSTEM_SDL;
}
int nOutputDeviceCount = SDL_GetNumAudioDevices( 0 );
int nIterateCount = MIN(nListCount-1, nOutputDeviceCount);
for ( int i = 0; i < nIterateCount; i++ )
{
const char *pNameUTF8 = SDL_GetAudioDeviceName( i, 0 );
audio_device_description_t& description = pDeviceListOut[i+1];
Q_UTF8ToWString( pNameUTF8, description.m_deviceName, sizeof(description.m_deviceName) );
Q_WStringToUTF8( description.m_deviceName, description.m_friendlyName, sizeof(description.m_friendlyName) );
description.m_nChannelCount = 6;
description.m_bIsDefault = false;
description.m_bIsAvailable = true;
description.m_nSubsystemId = AUDIO_SUBSYSTEM_SDL;
}
return nIterateCount + 1;
}
//-----------------------------------------------------------------------------
// Class factory
//-----------------------------------------------------------------------------
IAudioDevice2 *Audio_CreateSDLDevice( const audio_device_init_params_t &params )
{
if ( !InitSDLAudio() )
return NULL;
CAudioSDL *pDevice = new CAudioSDL;
if ( pDevice->Init( params ) )
{
return pDevice;
}
delete pDevice;
return NULL;
}

View File

@@ -0,0 +1,495 @@
#include "basetypes.h"
#include "commonmacros.h"
#if !defined( _X360 ) && defined( WIN32 )
#define _WIN32_DCOM
#include <windows.h>
#endif
#include <xaudio2.h>
#include "mix.h"
#include "soundsystem/lowlevel.h"
static IXAudio2 *g_pXAudio2 = NULL;
static int g_XAudio2Refcount = 0;
extern CInterlockedInt g_nDetectedAudioError;
extern CInterlockedInt g_nDetectedBufferStarvation;
//-----------------------------------------------------------------------------
// Purpose: Implementation of XAudio2 device for source2
//-----------------------------------------------------------------------------
class CAudioXAudio2 : public IAudioDevice2, public IXAudio2VoiceCallback
{
public:
CAudioXAudio2()
{
g_XAudio2Refcount++;
m_pName = "XAudio2 Device";
m_nChannels = 2;
m_bIsHeadphone = false;
m_bSupportsBufferStarvationDetection = true;
m_bIsCaptureDevice = false;
m_nSampleBits = 16;
m_nSampleRate = 44100;
m_bIsActive = true;
m_pMasterVoice = NULL;
m_pSourceVoice = NULL;
m_pBuffer = NULL;
m_nBufferSizeBytes = 0;
m_nBufferCount = 0;
m_nSubmitIndex = 0;
m_nActiveBuffers = 0;
m_nBufferErrors = 0;
m_bHasFocus = true;
m_bVoiceStarted = false;
}
~CAudioXAudio2( void );
bool Init( const audio_device_init_params_t &params, int nDeviceIndex );
void Shutdown( void ) OVERRIDE;
void OutputBuffer( int nChannels, CAudioMixBuffer *pChannelArray ) OVERRIDE;
const wchar_t *GetDeviceID() const OVERRIDE { return m_deviceID; }
int QueuedBufferCount() OVERRIDE;
int EmptyBufferCount() OVERRIDE;
void CancelOutput() OVERRIDE;
void WaitForComplete() OVERRIDE;
void UpdateFocus( bool bWindowHasFocus ) OVERRIDE;
void ClearBuffer() OVERRIDE {}
void OutputDebugInfo() const OVERRIDE;
bool SetShouldPlayWhenNotInFocus( bool bPlayEvenWhenNotInFocus ) OVERRIDE
{
m_savedParams.m_bPlayEvenWhenNotInFocus = bPlayEvenWhenNotInFocus;
return true;
}
inline int BytesPerSample() { return BitsPerSample()>>3; }
// Singleton object
// IXAudio2VoiceCallback
// Called just before this voice's processing pass begins.
virtual void __stdcall OnVoiceProcessingPassStart( UINT32 nBytesRequired ) OVERRIDE {}
virtual void __stdcall OnVoiceProcessingPassEnd() OVERRIDE {}
virtual void __stdcall OnStreamEnd() OVERRIDE {}
virtual void __stdcall OnBufferStart( void* pBufferContext ) OVERRIDE
{
}
virtual void __stdcall OnBufferEnd( void* pBufferContext ) OVERRIDE
{
Assert( m_nActiveBuffers > 0 );
m_nActiveBuffers--;
if ( m_nActiveBuffers == 0 )
{
m_nBufferErrors++;
if ( m_nBufferErrors > 10 )
{
g_nDetectedBufferStarvation++;
}
}
}
virtual void __stdcall OnLoopEnd( void* pBufferContext ) OVERRIDE {}
virtual void __stdcall OnVoiceError( void* pBufferContext, HRESULT nError ) OVERRIDE
{
g_nDetectedAudioError = 1;
Warning("Xaudio2 Voice Error %x\n", uint(nError) );
}
private:
// no copies of this class ever
CAudioXAudio2( const CAudioXAudio2 & );
CAudioXAudio2 & operator=( const CAudioXAudio2 & );
int SamplesPerBuffer() { return MIX_BUFFER_SIZE; }
int BytesPerBuffer() { return m_nBufferSizeBytes; }
IXAudio2MasteringVoice *m_pMasterVoice;
IXAudio2SourceVoice *m_pSourceVoice;
short *m_pBuffer;
int m_nBufferSizeBytes; // size of a single hardware output buffer, in bytes
int m_nBufferCount;
int m_nSubmitIndex;
int m_nBufferErrors;
CInterlockedInt m_nActiveBuffers;
// for error recovery
audio_device_init_params_t m_savedParams;
int m_nSavedPreferred;
wchar_t m_deviceID[256];
char m_displayName[256];
bool m_bHasFocus;
bool m_bVoiceStarted;
};
class CXAudioCallbacks : public IXAudio2EngineCallback
{
public:
CXAudioCallbacks() : m_bRegistered(false) {}
// IXAudio2EngineCallback
// ------------------------------------
STDMETHOD_(void, OnProcessingPassStart) (THIS);
STDMETHOD_(void, OnProcessingPassEnd) (THIS);
// Called in the event of a critical system error which requires XAudio2
// to be closed down and restarted. The error code is given in Error.
STDMETHOD_(void, OnCriticalError) (THIS_ HRESULT Error);
// ------------------------------------
void Init( IXAudio2 *pInterface )
{
pInterface->RegisterForCallbacks( this );
m_bRegistered = true;
}
void Shutdown( IXAudio2 *pInterface )
{
if ( m_bRegistered )
{
pInterface->UnregisterForCallbacks( this );
m_bRegistered = false;
}
}
bool m_bRegistered;
};
void CXAudioCallbacks::OnProcessingPassStart() {}
void CXAudioCallbacks::OnProcessingPassEnd() {}
void CXAudioCallbacks::OnCriticalError( HRESULT nError )
{
g_nDetectedAudioError = 1;
Warning("Xaudio2 Error %x\n", uint(nError) );
}
static CXAudioCallbacks g_XAudioErrors;
static bool InitXAudio()
{
if ( !g_pXAudio2 )
{
HRESULT hr;
InitCOM();
#ifdef _DEBUG
// UNDONE: Figure out why this fails - I believe it requires a separate install
if ( FAILED(hr = XAudio2Create( &g_pXAudio2, XAUDIO2_DEBUG_ENGINE, XAUDIO2_DEFAULT_PROCESSOR ) ) )
#endif
if ( FAILED(hr = XAudio2Create( &g_pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR ) ) )
return false;
g_XAudioErrors.Init( g_pXAudio2 );
// if someone calls CoFreeUnusedLibrariesEx (some shell extensions do this), then XAudio2 will get freed, go ahead an load the library explicitly
// to prevent this unloading
#if defined(PLATFORM_WINDOWS_PC)
#ifdef _DEBUG
const char *pDLLName = "XAudioD2_7.dll";
#else
const char *pDLLName = "XAudio2_7.dll";
#endif
// The DLL is already loaded, check the name to make sure we aren't loading an additional DLL
if ( GetModuleHandle( pDLLName ) )
{
LoadLibrary( pDLLName );
}
#endif
}
return true;
}
static void ShutdownXAudio()
{
if ( g_pXAudio2 )
{
g_XAudioErrors.Shutdown( g_pXAudio2 );
g_pXAudio2->Release();
g_pXAudio2 = NULL;
ShutdownCOM();
}
}
// enumerate the available devices so the app can select one
// fills out app-supplied list & returns count of available devices. If the list is too small, the count
// will signal the app to call again with a larger list
int Audio_EnumerateXAudio2Devices( audio_device_description_t *pDeviceListOut, int nListCount )
{
if ( !InitXAudio() )
return 0;
UINT32 nDeviceCountWindows = 0;
HRESULT hr = g_pXAudio2->GetDeviceCount(&nDeviceCountWindows);
Assert( hr == S_OK );
if ( hr != S_OK )
return 0;
int nDeviceCount = (int)nDeviceCountWindows;
XAUDIO2_DEVICE_DETAILS deviceDetails;
bool bWroteDefault = false;
int nMaxChannelDevice = -1;
// now get each device's details that will fit into the supplied list
int nIterateCount = min(nListCount, nDeviceCount);
for ( int i = 0; i < nIterateCount; i++ )
{
g_pXAudio2->GetDeviceDetails(i,&deviceDetails);
V_wcscpy_safe( pDeviceListOut[i].m_deviceName, deviceDetails.DeviceID );
V_wcstostr( deviceDetails.DisplayName, -1, pDeviceListOut[i].m_friendlyName, sizeof(pDeviceListOut[i].m_friendlyName) );
pDeviceListOut[i].m_nChannelCount = deviceDetails.OutputFormat.Format.nChannels;
pDeviceListOut[i].m_bIsDefault = false;
pDeviceListOut[i].m_bIsAvailable = true;
pDeviceListOut[i].m_nSubsystemId = AUDIO_SUBSYSTEM_XAUDIO;
// anything marked as default game device will be selected by default
if ( deviceDetails.Role & DefaultGameDevice )
{
pDeviceListOut[i].m_bIsDefault = true;
bWroteDefault = true;
}
if ( nMaxChannelDevice < 0 || deviceDetails.OutputFormat.Format.nChannels > pDeviceListOut[nMaxChannelDevice].m_nChannelCount )
{
nMaxChannelDevice = i;
}
}
// no default? Select first device with max # of channels
// If there are no channels then nMaxChannelDevice will be negative so we need to
// check before using it as an index.
if ( !bWroteDefault && nMaxChannelDevice >= 0 && nMaxChannelDevice < nListCount )
{
pDeviceListOut[nMaxChannelDevice].m_bIsDefault = true;
}
return nIterateCount;
}
//-----------------------------------------------------------------------------
// Class factory
//-----------------------------------------------------------------------------
IAudioDevice2 *Audio_CreateXAudio2Device( const audio_device_init_params_t &params )
{
if ( !InitXAudio() )
return NULL;
int nPreferredDevice = 0;
UINT32 nDeviceCountWindows = 0;
int nCount = 0;
HRESULT hr = g_pXAudio2->GetDeviceCount(&nDeviceCountWindows);
CUtlVector<audio_device_description_t> desc;
if ( hr == S_OK )
{
desc.SetCount( nDeviceCountWindows );
nCount = Audio_EnumerateXAudio2Devices( desc.Base(), desc.Count() );
}
// If there are no devices then device Init will fail. We might as well
// fail early.
// This was happening when running test_source2.bat in a loop and
// disconnecting partway through the past -- as soon as the machine was
// running headless the enumeration would return zero devices.
if ( !nCount )
return NULL;
for ( int i = 0; i < nCount; i++ )
{
if ( desc[i].m_bIsDefault )
{
nPreferredDevice = i;
}
}
if ( params.m_bOverrideDevice )
{
for ( int i = 0; i < nCount; i++ )
{
if ( !V_wcscmp( desc[i].m_deviceName, params.m_overrideDeviceName ) )
{
nPreferredDevice = i;
break;
}
}
}
CAudioXAudio2 *pDevice = new CAudioXAudio2;
if ( pDevice->Init( params, nPreferredDevice ) )
return pDevice;
delete pDevice;
return NULL;
}
CAudioXAudio2::~CAudioXAudio2()
{
Shutdown();
if ( m_pBuffer )
{
MemAlloc_FreeAligned( m_pBuffer );
}
g_XAudio2Refcount--;
if ( !g_XAudio2Refcount )
{
ShutdownXAudio();
}
}
bool CAudioXAudio2::Init( const audio_device_init_params_t &params, int nDeviceIndex )
{
HRESULT hr;
// NOTE: sample rate was XAUDIO2_DEFAULT_SAMPLERATE
if ( FAILED(hr = g_pXAudio2->CreateMasteringVoice( &m_pMasterVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, nDeviceIndex, NULL ) ) )
return false;
XAUDIO2_DEVICE_DETAILS deviceDetails;
g_pXAudio2->GetDeviceDetails( nDeviceIndex, &deviceDetails );
V_wcscpy_safe( m_deviceID, deviceDetails.DeviceID );
V_wcstostr( deviceDetails.DisplayName, -1, m_displayName, sizeof(m_displayName) );
// save for error recovery
m_nSavedPreferred = nDeviceIndex;
m_savedParams = params;
XAUDIO2_VOICE_DETAILS details;
m_pMasterVoice->GetVoiceDetails( &details );
WAVEFORMATEX wfx = { 0 };
// setup the format structure
{
int nChannels = details.InputChannels;
if ( params.m_bOverrideSpeakerConfig )
{
nChannels = SpeakerConfigValueToChannelCount( params.m_nOverrideSpeakerConfig );
if ( params.m_nOverrideSpeakerConfig == 0 )
{
m_bIsHeadphone = true;
}
}
m_nChannels = nChannels;
}
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = m_nChannels;
wfx.nSamplesPerSec = SampleRate();
wfx.wBitsPerSample = BitsPerSample();
wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
wfx.cbSize = 0;
if( FAILED( hr = g_pXAudio2->CreateSourceVoice( &m_pSourceVoice, &wfx, 0, XAUDIO2_DEFAULT_FREQ_RATIO, this ) ) )
{
return false;
}
m_nBufferCount = params.m_nOutputBufferCount;
int nBufferSize = MIX_BUFFER_SIZE * m_nChannels * BytesPerSample();
m_nBufferSizeBytes = nBufferSize;
m_nChannels = wfx.nChannels;
Assert( m_nChannels <= SOUND_DEVICE_MAX_CHANNELS );
m_pBuffer = (short *)MemAlloc_AllocAligned( nBufferSize * m_nBufferCount, 16 );
m_nActiveBuffers = 0;
m_nSubmitIndex = 0;
m_bVoiceStarted = false;
return true;
}
void CAudioXAudio2::Shutdown()
{
if ( m_pMasterVoice )
{
if ( m_pSourceVoice )
{
m_pSourceVoice->Stop();
m_pSourceVoice->DestroyVoice();
m_pSourceVoice = nullptr;
m_bVoiceStarted = false;
}
m_pMasterVoice->DestroyVoice();
m_pMasterVoice = nullptr;
}
}
void CAudioXAudio2::OutputDebugInfo() const
{
Msg( "XAudio2 Sound Device: %s\n", m_displayName );
Msg( "Channels:\t%d\n", ChannelCount() );
Msg( "Bits/Sample:\t%d\n", BitsPerSample() );
Msg( "Rate:\t\t%d\n", SampleRate() );
}
void CAudioXAudio2::OutputBuffer( int nChannels, CAudioMixBuffer *pChannelArray )
{
// start the voice as soon as we have data to output
if ( !m_bVoiceStarted )
{
m_pSourceVoice->Start();
m_bVoiceStarted = true;
}
int nBufferSize = BytesPerBuffer();
short *pWaveData = m_pBuffer + ( m_nSubmitIndex * (nBufferSize>>1) );
XAUDIO2_BUFFER buffer = {0};
buffer.pAudioData = (BYTE *)pWaveData;
buffer.Flags = 0;
buffer.AudioBytes = nBufferSize;
if ( nChannels == 2 && nChannels == m_nChannels )
{
ConvertFloat32Int16_Clamp_Interleave2( pWaveData, pChannelArray[0].m_flData, pChannelArray[1].m_flData, MIX_BUFFER_SIZE );
}
else
{
ConvertFloat32Int16_Clamp_InterleaveStride( pWaveData, m_nChannels, MIX_BUFFER_SIZE, pChannelArray[0].m_flData, nChannels, MIX_BUFFER_SIZE );
}
m_nActiveBuffers++;
m_pSourceVoice->SubmitSourceBuffer( &buffer );
m_nSubmitIndex = ( m_nSubmitIndex + 1 ) % m_nBufferCount;
}
int CAudioXAudio2::QueuedBufferCount()
{
// NOTE: If callbacks work on all clients then we do not need to do the potentially expensive GetState() call
// we already know if buffers are retired
// UNDONE: If this is causing problems, just change to #if 0 - the other code in this changelist will not interact with anything
#if 1
return m_nActiveBuffers;
#else
XAUDIO2_VOICE_STATE state;
m_pSourceVoice->GetState( &state );
return state.BuffersQueued;
#endif
}
int CAudioXAudio2::EmptyBufferCount()
{
return m_nBufferCount - QueuedBufferCount();
}
void CAudioXAudio2::CancelOutput()
{
m_pSourceVoice->FlushSourceBuffers();
}
void CAudioXAudio2::WaitForComplete()
{
m_pSourceVoice->Discontinuity();
while( QueuedBufferCount() )
{
ThreadSleep(0);
}
}
void CAudioXAudio2::UpdateFocus( bool bWindowHasFocus )
{
if ( m_pMasterVoice && !m_savedParams.m_bPlayEvenWhenNotInFocus )
{
if ( bWindowHasFocus != m_bHasFocus )
{
m_bHasFocus = bWindowHasFocus;
float flVolume = 1.0f;
m_pMasterVoice->GetVolume( &flVolume );
m_pMasterVoice->SetVolume( bWindowHasFocus ? 1.0f : 0.0f );
}
}
}

1121
soundsystem/lowlevel/mix.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
#include "soundsystem/isoundsystem.h"
#include "soundsystem/audio_mix.h"
// full depth/join/resample/convert
extern int ConvertSourceToFloat( const audio_source_input_t &channel, float flPitch, float flOutput[MIX_BUFFER_SIZE], audio_source_indexstate_t *pOut );
// just update the output state as if we mixed
extern int AdvanceSource( const audio_source_input_t &source, float flPitch, audio_source_indexstate_t *pOut );
extern uint AdvanceSourceIndex( audio_source_indexstate_t *pOut, const audio_source_input_t &source, uint nAdvance );

View File

@@ -0,0 +1,335 @@
#include <math.h>
#include "mathlib/ssemath.h"
#include "mix.h"
#include "simple_filter.h"
#define V_powf(x, y) powf(x, y)
#define V_sinf(x) sinf(x)
#define V_cosf(x) cosf(x)
#define V_sinhf(x) sinhf(x)
#define V_sqrtf(x) sqrtf(x)
// natural log of 2
#ifndef M_LN2
#define M_LN2 0.69314718055994530942
#endif
// slow, just for test
static inline fltx4 LoadSIMD( float flR0, float flR1, float flR2, float flR3 )
{
fltx4 t;
SubFloat( t, 0 ) = flR0;
SubFloat( t, 1 ) = flR1;
SubFloat( t, 2 ) = flR2;
SubFloat( t, 3 ) = flR3;
return t;
}
// scalar implemetation
#if 0
void SimpleFilter_ProcessBuffer( float flSamples[MIX_BUFFER_SIZE], float flOutput[MIX_BUFFER_SIZE], filterstate_t *pFilter )
{
float x1 = pFilter->m_flFIRState[0];
float x2 = pFilter->m_flFIRState[1];
float y1 = pFilter->m_flIIRState[0];
float y2 = pFilter->m_flIIRState[1];
float sample, out;
float fir0 = pFilter->m_flFIRCoeff[0];
float fir1 = pFilter->m_flFIRCoeff[1];
float fir2 = pFilter->m_flFIRCoeff[2];
float iir0 = -pFilter->m_flIIRCoeff[0];
float iir1 = -pFilter->m_flIIRCoeff[1];
for ( int i = 0; i < MIX_BUFFER_SIZE; i++ )
{
sample = flSamples[i];
// FIR part of the filter
out = fir0 * sample + fir1 * x1 + fir2 * x2;
// IIR part of the filter
out += iir0 * y1 + iir1 * y2;
// write FIR delay line of input
x2 = x1;
x1 = sample;
// write IIR delay line of output
y2 = y1;
y1 = out;
// write filtered sample
flOutput[i] = out;
}
// write state back to the filter
pFilter->m_flFIRState[0] = x1;
pFilter->m_flFIRState[1] = x2;
pFilter->m_flIIRState[0] = y1;
pFilter->m_flIIRState[1] = y2;
}
#endif
void SimpleFilter_ProcessFIR4( const float flSamples[MIX_BUFFER_SIZE], float flOutput[MIX_BUFFER_SIZE], filterstate_t *pFilter )
{
fltx4 fl4FIR0 = ReplicateX4( pFilter->m_flFIRCoeff[0] );
fltx4 fl4FIR1 = ReplicateX4( pFilter->m_flFIRCoeff[1] );
fltx4 fl4FIR2 = ReplicateX4( pFilter->m_flFIRCoeff[2] );
fltx4 fl4FIR3 = ReplicateX4( pFilter->m_flFIRCoeff[3] );
fltx4 fl4PrevSample = pFilter->m_fl4prevInputSamples;
const fltx4 *RESTRICT pInput = (const fltx4 *)&flSamples[0];
fltx4 *RESTRICT pOutput = (fltx4 *)&flOutput[0];
for ( int i = 0; i < MIX_BUFFER_SIZE/4; i++ )
{
fltx4 fl4Sample = LoadAlignedSIMD( pInput );
pInput++;
fltx4 fl4fx2 = _mm_shuffle_ps( fl4PrevSample, fl4Sample, MM_SHUFFLE_REV(2,3,0,1) );
fltx4 fl4fx1 = _mm_shuffle_ps( fl4fx2, fl4Sample, MM_SHUFFLE_REV(1,2,1,2) );
fltx4 fl4fx3 = _mm_shuffle_ps( fl4PrevSample, fl4fx2, MM_SHUFFLE_REV(1,2,1,2) );
// FIR part of the filter
//out = fir0 * sample + fir1 * x1 + fir2 * x2; + fir3 * x3
fltx4 fl4t0 = MulSIMD( fl4FIR0, fl4Sample );
fltx4 fl4t1 = MulSIMD( fl4FIR1, fl4fx1 );
fltx4 fl4t2 = MaddSIMD( fl4FIR3, fl4fx3, fl4t0 );
fltx4 fl4out = AddSIMD( MaddSIMD( fl4FIR2, fl4fx2, fl4t1 ), fl4t2 );
// write FIR delay line of input
fl4PrevSample = fl4Sample;
StoreAlignedSIMD( (float *)pOutput, fl4out );
pOutput++;
}
pFilter->m_fl4prevInputSamples = fl4PrevSample;
}
void SimpleFilter_ProcessBuffer( const float flSamples[MIX_BUFFER_SIZE], float flOutput[MIX_BUFFER_SIZE], filterstate_t *pFilter )
{
if ( pFilter->m_nFilterType == 1 )
{
SimpleFilter_ProcessFIR4( flSamples, flOutput, pFilter );
return;
}
fltx4 fl4FIR0 = ReplicateX4( pFilter->m_flFIRCoeff[0] );
fltx4 fl4FIR1 = ReplicateX4( pFilter->m_flFIRCoeff[1] );
fltx4 fl4FIR2 = ReplicateX4( pFilter->m_flFIRCoeff[2] );
// UNDONE: Store in filterstate this way
fltx4 fl4PrevSample = pFilter->m_fl4prevInputSamples;
const fltx4 *RESTRICT pInput = (const fltx4 *)&flSamples[0];
fltx4 *RESTRICT pOutput = (fltx4 *)&flOutput[0];
// iir exapansion from intel paper
// [y3, y2, y1, 0] * [b3, b3, b3, 0]
// [y2, y1, 0, v2] * [b2, b2, 0, b1] row2
// + [y1, 0, v1, v1] * [b1, 0, b1, (b1*b1)+b2] row1
// + [0, v0, v0, v0] * [ 0, b1, (b1*b1)+b2, b1*b1*b1 + 2*b1*b2 + b3] row0
// NOTE: b3/y3 are always zero in our case because we only have two taps (drop the first row and b3 term in the fourth row)
fltx4 iirRow2 = pFilter->m_fl4iirRow2;
fltx4 iirRow1 = pFilter->m_fl4iirRow1;
fltx4 iirRow0 = pFilter->m_fl4iirRow0;
fltx4 fl4PrevOutput = pFilter->m_fl4prevOutputSamples;
for ( int i = 0; i < MIX_BUFFER_SIZE/4; i++ )
{
fltx4 fl4Sample = LoadAlignedSIMD( pInput );
pInput++;
fltx4 fx2 = _mm_shuffle_ps( fl4PrevSample, fl4Sample, MM_SHUFFLE_REV(2,3,0,1) );
fltx4 fx1 = _mm_shuffle_ps( fx2, fl4Sample, MM_SHUFFLE_REV(1,2,1,2) );
// FIR part of the filter
//out = fl4FIR0 * fl4Sample + fl4FIR1 * x1 + fl4FIR2 * x2;
fltx4 t0 = MulSIMD( fl4FIR0, fl4Sample );
fltx4 t1 = MulSIMD( fl4FIR1, fx1 );
fltx4 out = AddSIMD( MaddSIMD( fl4FIR2, fx2, t0 ), t1 );
// write FIR delay line of input
fl4PrevSample = fl4Sample;
// IIR part of the filter
fltx4 fl4OutRow = _mm_shuffle_ps( fl4PrevOutput, out, MM_SHUFFLE_REV(2,3,0,2) );
fltx4 v = MaddSIMD( fl4OutRow, iirRow2, out );
fl4OutRow = _mm_shuffle_ps( fl4PrevOutput, v, MM_SHUFFLE_REV(3,0,1,1) );
v = MaddSIMD( fl4OutRow, iirRow1, v );
fl4OutRow = SplatXSIMD(v);
out = MaddSIMD( fl4OutRow, iirRow0, v );
// write IIR delay line of output
fl4PrevOutput = out;
StoreAlignedSIMD( (float *)pOutput, out );
pOutput++;
}
pFilter->m_fl4prevInputSamples = fl4PrevSample;
pFilter->m_fl4prevOutputSamples = fl4PrevOutput;
}
void SimpleFilter_Coefficients( biquad_filter_coefficients_t *pCoeffs, int nFilterType, float fldbGain, float flCenterFrequency, float flBandwidth, float flSamplingRate )
{
float flA0, flA1, flA2, flB0, flB1, flB2;
/* setup variables */
float flGain = V_powf( 10, fldbGain / 40 );
float flOmega = float( 2 * M_PI * flCenterFrequency / flSamplingRate );
float flSinOmega = V_sinf( flOmega );
float flCosOmega = V_cosf( flOmega );
float flAlpha = flSinOmega * (float)V_sinhf( M_LN2 / 2 * flBandwidth * flOmega / flSinOmega );
float flBeta = V_sqrtf( flGain + flGain );
switch ( nFilterType )
{
default:
case FILTER_LOWPASS:
flB0 = ( 1 - flCosOmega ) / 2;
flB1 = 1 - flCosOmega;
flB2 = ( 1 - flCosOmega ) / 2;
flA0 = 1 + flAlpha;
flA1 = -2 * flCosOmega;
flA2 = 1 - flAlpha;
break;
case FILTER_HIGHPASS:
flB0 = ( 1 + flCosOmega ) / 2;
flB1 = -( 1 + flCosOmega );
flB2 = ( 1 + flCosOmega ) / 2;
flA0 = 1 + flAlpha;
flA1 = -2 * flCosOmega;
flA2 = 1 - flAlpha;
break;
case FILTER_BANDPASS:
flB0 = flAlpha;
flB1 = 0;
flB2 = -flAlpha;
flA0 = 1 + flAlpha;
flA1 = -2 * flCosOmega;
flA2 = 1 - flAlpha;
break;
case FILTER_NOTCH:
flB0 = 1;
flB1 = -2 * flCosOmega;
flB2 = 1;
flA0 = 1 + flAlpha;
flA1 = -2 * flCosOmega;
flA2 = 1 - flAlpha;
break;
case FILTER_PEAKING_EQ:
flB0 = 1 + ( flAlpha * flGain );
flB1 = -2 * flCosOmega;
flB2 = 1 - ( flAlpha * flGain );
flA0 = 1 + ( flAlpha / flGain );
flA1 = -2 * flCosOmega;
flA2 = 1 - ( flAlpha / flGain );
break;
case FILTER_LOW_SHELF:
flB0 = flGain * ( ( flGain + 1 ) - ( flGain - 1 ) * flCosOmega + flBeta * flSinOmega );
flB1 = 2 * flGain * ( ( flGain - 1 ) - ( flGain + 1 ) * flCosOmega );
flB2 = flGain * ( ( flGain + 1 ) - ( flGain - 1 ) * flCosOmega - flBeta * flSinOmega );
flA0 = ( flGain + 1 ) + ( flGain - 1 ) * flCosOmega + flBeta * flSinOmega;
flA1 = -2 * ( ( flGain - 1 ) + ( flGain + 1 ) * flCosOmega );
flA2 = ( flGain + 1 ) + ( flGain - 1 ) * flCosOmega - flBeta * flSinOmega;
break;
case FILTER_HIGH_SHELF:
flB0 = flGain * ( ( flGain + 1 ) + ( flGain - 1 ) * flCosOmega + flBeta * flSinOmega );
flB1 = -2 * flGain * ( ( flGain - 1 ) + ( flGain + 1 ) * flCosOmega );
flB2 = flGain * ( ( flGain + 1 ) + ( flGain - 1 ) * flCosOmega - flBeta * flSinOmega );
flA0 = ( flGain + 1 ) - ( flGain - 1 ) * flCosOmega + flBeta * flSinOmega;
flA1 = 2 * ( ( flGain - 1 ) - ( flGain + 1 ) * flCosOmega );
flA2 = ( flGain + 1 ) - ( flGain - 1 ) * flCosOmega - flBeta * flSinOmega;
break;
}
pCoeffs->m_flA[0] = flA0;
pCoeffs->m_flA[1] = flA1;
pCoeffs->m_flA[2] = flA2;
pCoeffs->m_flB[0] = flB0;
pCoeffs->m_flB[1] = flB1;
pCoeffs->m_flB[2] = flB2;
}
void SimpleFilter_Init( filterstate_t *pFilter, int nFilterType, float fldbGain, float flCenterFrequency, float flBandwidth, float flSamplingRate )
{
biquad_filter_coefficients_t coeffs;
SimpleFilter_Coefficients( &coeffs, nFilterType, fldbGain, flCenterFrequency, flBandwidth, flSamplingRate );
// compute biquad coefficients
pFilter->m_flFIRCoeff[0] = coeffs.m_flB[0] / coeffs.m_flA[0];
pFilter->m_flFIRCoeff[1] = coeffs.m_flB[1] / coeffs.m_flA[0];
pFilter->m_flFIRCoeff[2] = coeffs.m_flB[2] / coeffs.m_flA[0];
pFilter->m_flIIRCoeff[0] = coeffs.m_flA[1] / coeffs.m_flA[0];
pFilter->m_flIIRCoeff[1] = coeffs.m_flA[2] / coeffs.m_flA[0];
// zero out initial state
pFilter->m_flFIRState[0] = 0;
pFilter->m_flFIRState[1] = 0;
pFilter->m_flIIRState[0] = 0;
pFilter->m_flIIRState[1] = 0;
pFilter->m_fl4prevInputSamples = Four_Zeros;
float iir0 = -pFilter->m_flIIRCoeff[0];
float iir1 = -pFilter->m_flIIRCoeff[1];
float iir0_sqr = iir0*iir0;
pFilter->m_fl4iirRow2 = LoadSIMD( iir1, iir1, 0, iir0 );
pFilter->m_fl4iirRow1 = LoadSIMD( iir0, 0, iir0, iir0_sqr+iir1 );
pFilter->m_fl4iirRow0 = LoadSIMD( 0, iir0, iir0_sqr+iir1, iir0_sqr*iir0 + (2*iir0*iir1) );
pFilter->m_fl4prevOutputSamples = Four_Zeros;
pFilter->m_nFilterType = 0;
}
void SimpleFilter_Update( filterstate_t *pFilter, int nFilterType, float fldbGain, float flCenterFrequency, float flBandwidth, float flSamplingRate )
{
biquad_filter_coefficients_t coeffs;
SimpleFilter_Coefficients( &coeffs, nFilterType, fldbGain, flCenterFrequency, flBandwidth, flSamplingRate );
// compute biquad coefficients
pFilter->m_flFIRCoeff[0] = coeffs.m_flB[0] / coeffs.m_flA[0];
pFilter->m_flFIRCoeff[1] = coeffs.m_flB[1] / coeffs.m_flA[0];
pFilter->m_flFIRCoeff[2] = coeffs.m_flB[2] / coeffs.m_flA[0];
pFilter->m_flIIRCoeff[0] = coeffs.m_flA[1] / coeffs.m_flA[0];
pFilter->m_flIIRCoeff[1] = coeffs.m_flA[2] / coeffs.m_flA[0];
float iir0 = -pFilter->m_flIIRCoeff[0];
float iir1 = -pFilter->m_flIIRCoeff[1];
float iir0_sqr = iir0*iir0;
pFilter->m_fl4iirRow2 = LoadSIMD( iir1, iir1, 0, iir0 );
pFilter->m_fl4iirRow1 = LoadSIMD( iir0, 0, iir0, iir0_sqr + iir1 );
pFilter->m_fl4iirRow0 = LoadSIMD( 0, iir0, iir0_sqr + iir1, iir0_sqr*iir0 + ( 2 * iir0*iir1 ) );
pFilter->m_nFilterType = 0;
}
void SimpleFilter_InitLowPass( filterstate_t *pFilter, float dbGain, float flCenterFrequency, float flBandWidth, float flSamplingRate )
{
SimpleFilter_Init( pFilter, FILTER_LOWPASS, dbGain, flCenterFrequency, flBandWidth, flSamplingRate );
}
void SimpleFilter_InitHighPass( filterstate_t *pFilter, float dbGain, float flCenterFrequency, float flBandWidth, float flSamplingRate )
{
SimpleFilter_Init( pFilter, FILTER_HIGHPASS, dbGain, flCenterFrequency, flBandWidth, flSamplingRate );
}
void SimpleFilter_InitBandPass( filterstate_t *pFilter, float dbGain, float flCenterFrequency, float flBandWidth, float flSamplingRate )
{
SimpleFilter_Init( pFilter, FILTER_BANDPASS, dbGain, flCenterFrequency, flBandWidth, flSamplingRate );
}
void SimpleFilter_InitNotch( filterstate_t *pFilter, float dbGain, float flCenterFrequency, float flBandWidth, float flSamplingRate )
{
SimpleFilter_Init( pFilter, FILTER_NOTCH, dbGain, flCenterFrequency, flBandWidth, flSamplingRate );
}
void SimpleFilter_InitPeakingEQ( filterstate_t *pFilter, float dbGain, float flCenterFrequency, float flBandWidth, float flSamplingRate )
{
SimpleFilter_Init( pFilter, FILTER_PEAKING_EQ, dbGain, flCenterFrequency, flBandWidth, flSamplingRate );
}
void SimpleFilter_InitLowShelf( filterstate_t *pFilter, float dbGain, float flCenterFrequency, float flBandWidth, float flSamplingRate )
{
SimpleFilter_Init( pFilter, FILTER_LOW_SHELF, dbGain, flCenterFrequency, flBandWidth, flSamplingRate );
}
void SimpleFilter_InitHighShelf( filterstate_t *pFilter, float dbGain, float flCenterFrequency, float flBandWidth, float flSamplingRate )
{
SimpleFilter_Init( pFilter, FILTER_HIGH_SHELF, dbGain, flCenterFrequency, flBandWidth, flSamplingRate );
}

View File

@@ -0,0 +1,40 @@
#include "mathlib/ssemath.h"
struct biquad_filter_coefficients_t
{
float m_flA[3];
float m_flB[3];
};
struct filterstate_t
{
float m_flFIRCoeff[4];
float m_flIIRCoeff[2];
int m_nFilterType;
float m_flUnused1[1];
float m_flFIRState[2];
float m_flIIRState[2];
fltx4 m_fl4iirRow0;
fltx4 m_fl4iirRow1;
fltx4 m_fl4iirRow2;
fltx4 m_fl4prevInputSamples;
fltx4 m_fl4prevOutputSamples;
};
extern void SimpleFilter_ProcessBuffer( const float flSamples[MIX_BUFFER_SIZE], float flOutput[MIX_BUFFER_SIZE], filterstate_t *pFilter );
extern void SimpleFilter_InitLowPass( filterstate_t *pFilter, float fldbGain, float flCenterFrequency, float flBandWidth, float flSamplingRate = MIX_DEFAULT_SAMPLING_RATE );
extern void SimpleFilter_InitHighPass( filterstate_t *pFilter, float fldbGain, float flCenterFrequency, float flBandWidth, float flSamplingRate = MIX_DEFAULT_SAMPLING_RATE );
extern void SimpleFilter_InitBandPass( filterstate_t *pFilter, float fldbGain, float flCenterFrequency, float flBandWidth, float flSamplingRate = MIX_DEFAULT_SAMPLING_RATE );
extern void SimpleFilter_InitNotch( filterstate_t *pFilter, float fldbGain, float flCenterFrequency, float flBandWidth, float flSamplingRate = MIX_DEFAULT_SAMPLING_RATE );
extern void SimpleFilter_InitPeakingEQ( filterstate_t *pFilter, float fldbGain, float flCenterFrequency, float flBandWidth, float flSamplingRate = MIX_DEFAULT_SAMPLING_RATE );
extern void SimpleFilter_InitLowShelf( filterstate_t *pFilter, float fldbGain, float flCenterFrequency, float flBandWidth, float flSamplingRate = MIX_DEFAULT_SAMPLING_RATE );
extern void SimpleFilter_InitHighShelf( filterstate_t *pFilter, float fldbGain, float flCenterFrequency, float flBandWidth, float flSamplingRate = MIX_DEFAULT_SAMPLING_RATE );
extern void SimpleFilter_Init( filterstate_t *pFilter, int nFilterType, float fldbGain, float flCenterFrequency, float flBandwidth, float flSamplingRate );
extern void SimpleFilter_Coefficients( biquad_filter_coefficients_t *pCoeffs, int nFilterType, float fldbGain, float flCenterFrequency, float flBandwidth, float flSamplingRate );
// update parameterization but don't change prev input state
extern void SimpleFilter_Update( filterstate_t *pFilter, int nFilterType, float fldbGain, float flCenterFrequency, float flBandwidth, float flSamplingRate );

View File

@@ -0,0 +1,55 @@
//-----------------------------------------------------------------------------
// SOUNDSYSTEM_LOWLEVEL.VPC
//
// Project Script
//-----------------------------------------------------------------------------
$Macro SRCDIR "..\.."
$include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
$Include "$SRCDIR\vpc_scripts\dxsdk_macros.vpc"
$Configuration
{
$Compiler
{
$AdditionalIncludeDirectories "$BASE;$SRCDIR\thirdparty\dxsdk\include;$DXSDKINCLUDE" [$WINDOWS]
}
}
$Project "soundsystem_lowlevel"
{
$Folder "Source Files"
{
$File "device_dsound.cpp" [$WINDOWS]
$File "device_xaudio2.cpp" [$WINDOWS]
$File "device_sdl.cpp" [$POSIX]
$File "device_null.cpp"
$File "mix.cpp"
$File "simple_filter.cpp"
$File "windows_audio.cpp" [$WINDOWS]
{
$Configuration
{
$Compiler
{
$AdditionalIncludeDirectories "$SRCDIR\dxs9dk\include" [$WINDOWS]
}
}
}
}
$Folder "Header Files"
{
$File "$SRCDIR/public/soundsystem/lowlevel.h"
$File "$SRCDIR/public/soundsystem/audio_mix.h"
$File "mix.h"
$File "simple_filter.h"
}
$Folder "Link Libraries" [$WINDOWS]
{
$Lib "$LIBDXSDK\dsound" [$WINDOWS]
$Lib "$LIBDXSDK\dxguid" [$WINDOWS]
}
}

View File

@@ -0,0 +1,13 @@
"vpc_cache"
{
"CacheVersion" "1"
"win32"
{
"CRCFile" "soundsystem_lowlevel.vcxproj.vpc_crc"
"OutputFiles"
{
"0" "soundsystem_lowlevel.vcxproj"
"1" "soundsystem_lowlevel.vcxproj.filters"
}
}
}

View File

@@ -0,0 +1,224 @@
//
// NOTE: Source has a bunch of windows SDK headers checked in to the dx9sdk directory.
// This file is here to isolate dependencies on the newer version of the windows SDK
// NOTE: This code only applies to VISTA and greater
#include <windows.h>
#include <mmdeviceapi.h>
#include <audiopolicy.h>
#include <endpointvolume.h>
#define SAFE_RELEASE(punk) \
if ((punk) != NULL) \
{ (punk)->Release(); (punk) = NULL; }
extern unsigned int g_nDeviceStamp;
class CMMNotificationClient : public IMMNotificationClient
{
LONG _cRef;
IMMDeviceEnumerator *_pEnumerator;
public:
CMMNotificationClient() :
_cRef(1),
_pEnumerator(NULL)
{
}
~CMMNotificationClient()
{
SAFE_RELEASE(_pEnumerator)
}
// IUnknown methods -- AddRef, Release, and QueryInterface
ULONG STDMETHODCALLTYPE AddRef()
{
return InterlockedIncrement(&_cRef);
}
ULONG STDMETHODCALLTYPE Release()
{
ULONG ulRef = InterlockedDecrement(&_cRef);
if (0 == ulRef)
{
delete this;
}
return ulRef;
}
HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, VOID **ppvInterface)
{
if (IID_IUnknown == riid)
{
AddRef();
*ppvInterface = (IUnknown*)this;
}
else if (__uuidof(IMMNotificationClient) == riid)
{
AddRef();
*ppvInterface = (IMMNotificationClient*)this;
}
else
{
*ppvInterface = NULL;
return E_NOINTERFACE;
}
return S_OK;
}
// Callback methods for device-event notifications.
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged( EDataFlow flow, ERole /*role*/, LPCWSTR /*pwstrDeviceId*/ )
{
if ( flow == eRender )
{
g_nDeviceStamp++;
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR /*pwstrDeviceId*/) { return S_OK; };
HRESULT STDMETHODCALLTYPE OnDeviceRemoved( LPCWSTR /*pwstrDeviceId*/ ) { return S_OK; }
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged( LPCWSTR /*pwstrDeviceId*/, DWORD /*dwNewState*/ ) { return S_OK; }
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged( LPCWSTR /*pwstrDeviceId*/, const PROPERTYKEY /*key*/ ) { return S_OK; }
};
CMMNotificationClient *g_pNotify = NULL;
HRESULT SetupWindowsMixerPreferences( bool bDuckingOptOut = true )
{
HRESULT hr = S_OK;
IMMDeviceEnumerator* pDeviceEnumerator = NULL;
IMMDevice* pEndpoint = NULL;
IAudioSessionManager2* pSessionManager2 = NULL;
IAudioSessionControl* pSessionControl = NULL;
IAudioSessionControl2* pSessionControl2 = NULL;
// Start with the default endpoint.
hr = CoCreateInstance( __uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDeviceEnumerator) );
if (SUCCEEDED(hr))
{
hr = pDeviceEnumerator->GetDefaultAudioEndpoint( eRender, eConsole, &pEndpoint);
g_pNotify = new CMMNotificationClient;
pDeviceEnumerator->RegisterEndpointNotificationCallback( g_pNotify );
pDeviceEnumerator->Release();
pDeviceEnumerator = NULL;
}
// Activate session manager.
if (SUCCEEDED(hr))
{
hr = pEndpoint->Activate(__uuidof(IAudioSessionManager2), CLSCTX_INPROC_SERVER, NULL, reinterpret_cast<void **>(&pSessionManager2) );
pEndpoint->Release();
pEndpoint = NULL;
}
if (SUCCEEDED(hr))
{
hr = pSessionManager2->GetAudioSessionControl(NULL, 0, &pSessionControl);
// enable this code to force some default master volume for this game.
// NOTE: This will have the side effect of not remembering any setting the user made in the wiundows mixer
#if 0
ISimpleAudioVolume *pSimpleVolume = NULL;
hr = pSessionManager2->GetSimpleAudioVolume( NULL, FALSE, &pSimpleVolume );
if ( SUCCEEDED(hr) )
{
pSimpleVolume->SetMasterVolume( flMasterVolume, NULL );
pSimpleVolume->Release();
}
#endif
pSessionManager2->Release();
pSessionManager2 = NULL;
}
if (SUCCEEDED(hr))
{
hr = pSessionControl->QueryInterface( __uuidof(IAudioSessionControl2), (void**)&pSessionControl2 );
pSessionControl->Release();
pSessionControl = NULL;
}
// Sync the ducking state with the specified preference.
if (SUCCEEDED(hr))
{
if (bDuckingOptOut)
{
hr = pSessionControl2->SetDuckingPreference(TRUE);
}
else
{
hr = pSessionControl2->SetDuckingPreference(FALSE);
}
pSessionControl2->Release();
pSessionControl2 = NULL;
}
return hr;
}
bool GetWindowsDefaultAudioDevice( wchar_t *pDeviceNameOut, size_t nDeviceBufSize )
{
IMMDeviceEnumerator* pDeviceEnumerator = NULL;
IMMDevice* pEndpoint = NULL;
bool bRet = false;
// Get the default audio endpoint from the multimedia API (more reliable than XAudio2 for example because it updates with dynamic changes to the default device)
HRESULT hr = CoCreateInstance( __uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDeviceEnumerator) );
if (SUCCEEDED(hr))
{
HRESULT hrEndPointDevice = pDeviceEnumerator->GetDefaultAudioEndpoint( eRender, eConsole, &pEndpoint );
if (SUCCEEDED(hrEndPointDevice))
{
// now query the endpoint device for its Id
LPWSTR deviceOut;
if ( S_OK == pEndpoint->GetId( &deviceOut ) )
{
// got a valid Id, return true to the caller and save it
wcsncpy_s( pDeviceNameOut, nDeviceBufSize/sizeof(wchar_t), deviceOut, _TRUNCATE );
bRet = true;
// GetId allocates memory when successful, we have to free it
CoTaskMemFree( deviceOut );
}
pEndpoint->Release();
}
pDeviceEnumerator->Release();
}
return bRet;
}
class CInitializeCOM
{
public:
CInitializeCOM()
{
CoInitializeEx(0, COINIT_MULTITHREADED);
}
~CInitializeCOM()
{
CoUninitialize();
}
};
void InitCOM()
{
static CInitializeCOM initializer;
}
void ShutdownCOM()
{
}