initial
This commit is contained in:
70
soundsystem/_vpc_/manifest_soundsystem/win32/manifest.txt
Normal file
70
soundsystem/_vpc_/manifest_soundsystem/win32/manifest.txt
Normal file
@@ -0,0 +1,70 @@
|
||||
// ----------------------------------------- //
|
||||
// File generated by VPC //
|
||||
// ----------------------------------------- //
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\common\debug_dll_check.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\common\debug_dll_check.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\common\debug_dll_check.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\public\tier0\memoverride.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\public\tier0\memoverride.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\public\tier0\memoverride.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\public\phonemeconverter.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\public\phonemeconverter.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\public\phonemeconverter.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\public\sentence.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\public\sentence.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\public\sentence.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\soundsystem\snd_audio_source.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\soundsystem\snd_audio_source.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\soundsystem\snd_audio_source.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\soundsystem\snd_dev_wave.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\soundsystem\snd_dev_wave.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\soundsystem\snd_dev_wave.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\soundsystem\snd_io.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\soundsystem\snd_io.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\soundsystem\snd_io.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\soundsystem\snd_wave_mixer.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\soundsystem\snd_wave_mixer.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\soundsystem\snd_wave_mixer.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\soundsystem\snd_wave_mixer_adpcm.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\soundsystem\snd_wave_mixer_adpcm.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\soundsystem\snd_wave_mixer_adpcm.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\soundsystem\snd_wave_source.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\soundsystem\snd_wave_source.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\soundsystem\snd_wave_source.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\soundsystem\soundsystem.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\soundsystem\soundsystem.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\soundsystem\soundsystem.cpp
|
||||
Containing unity file:
|
||||
PCH 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:
|
||||
|
||||
716
soundsystem/lowlevel/device_dsound.cpp
Normal file
716
soundsystem/lowlevel/device_dsound.cpp
Normal 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 ¶ms );
|
||||
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 ¶ms )
|
||||
{
|
||||
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 ¶ms )
|
||||
{
|
||||
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, ¶ms, 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);
|
||||
}
|
||||
}
|
||||
319
soundsystem/lowlevel/device_null.cpp
Normal file
319
soundsystem/lowlevel/device_null.cpp
Normal 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 ¶ms )
|
||||
{
|
||||
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 ¶ms )
|
||||
{
|
||||
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;
|
||||
}
|
||||
493
soundsystem/lowlevel/device_sdl.cpp
Normal file
493
soundsystem/lowlevel/device_sdl.cpp
Normal 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 ¶ms );
|
||||
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 ¶ms )
|
||||
{
|
||||
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 ¶ms )
|
||||
{
|
||||
if ( !InitSDLAudio() )
|
||||
return NULL;
|
||||
|
||||
CAudioSDL *pDevice = new CAudioSDL;
|
||||
|
||||
if ( pDevice->Init( params ) )
|
||||
{
|
||||
return pDevice;
|
||||
}
|
||||
|
||||
delete pDevice;
|
||||
return NULL;
|
||||
}
|
||||
495
soundsystem/lowlevel/device_xaudio2.cpp
Normal file
495
soundsystem/lowlevel/device_xaudio2.cpp
Normal 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 ¶ms, 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 ¶ms )
|
||||
{
|
||||
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 ¶ms, 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
1121
soundsystem/lowlevel/mix.cpp
Normal file
File diff suppressed because it is too large
Load Diff
12
soundsystem/lowlevel/mix.h
Normal file
12
soundsystem/lowlevel/mix.h
Normal 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 );
|
||||
|
||||
|
||||
335
soundsystem/lowlevel/simple_filter.cpp
Normal file
335
soundsystem/lowlevel/simple_filter.cpp
Normal 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 );
|
||||
}
|
||||
|
||||
|
||||
40
soundsystem/lowlevel/simple_filter.h
Normal file
40
soundsystem/lowlevel/simple_filter.h
Normal 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 );
|
||||
55
soundsystem/lowlevel/soundsystem_lowlevel.vpc
Normal file
55
soundsystem/lowlevel/soundsystem_lowlevel.vpc
Normal 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]
|
||||
}
|
||||
}
|
||||
13
soundsystem/lowlevel/soundsystem_lowlevel.vpc.vpc_cache
Normal file
13
soundsystem/lowlevel/soundsystem_lowlevel.vpc.vpc_cache
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
224
soundsystem/lowlevel/windows_audio.cpp
Normal file
224
soundsystem/lowlevel/windows_audio.cpp
Normal 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()
|
||||
{
|
||||
}
|
||||
|
||||
61
soundsystem/snd_audio_source.cpp
Normal file
61
soundsystem/snd_audio_source.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include <stdio.h>
|
||||
#include "soundsystem/snd_audio_source.h"
|
||||
#include "soundsystem/isoundsystem.h"
|
||||
#include "soundsystem.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
extern CAudioSource *Audio_CreateMemoryWave( const char *pName );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Simple wrapper to crack naming convention and create the proper wave source
|
||||
// Input : *pName - WAVE filename
|
||||
// Output : CAudioSource
|
||||
//-----------------------------------------------------------------------------
|
||||
CAudioSource *AudioSource_Create( const char *pName )
|
||||
{
|
||||
if ( !pName )
|
||||
return NULL;
|
||||
|
||||
// if ( pName[0] == '!' ) // sentence
|
||||
;
|
||||
|
||||
// Names that begin with "*" are streaming.
|
||||
// Skip over the * and create a streamed source
|
||||
if ( pName[0] == '*' )
|
||||
{
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// These are loaded into memory directly
|
||||
return Audio_CreateMemoryWave( pName );
|
||||
}
|
||||
|
||||
CAudioSource::~CAudioSource( void )
|
||||
{
|
||||
CAudioMixer *mixer;
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
mixer = g_pSoundSystem->FindMixer( this );
|
||||
if ( !mixer )
|
||||
break;
|
||||
|
||||
g_pSoundSystem->StopSound( mixer );
|
||||
}
|
||||
}
|
||||
|
||||
CAudioSource::CAudioSource( void )
|
||||
{
|
||||
}
|
||||
903
soundsystem/snd_dev_wave.cpp
Normal file
903
soundsystem/snd_dev_wave.cpp
Normal file
@@ -0,0 +1,903 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "snd_dev_wave.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#pragma warning( disable: 4201 )
|
||||
#include <mmsystem.h>
|
||||
#pragma warning( default: 4201 )
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "soundsystem/snd_audio_source.h"
|
||||
#include "soundsystem.h"
|
||||
#include "soundsystem/snd_device.h"
|
||||
#include "tier1/utlvector.h"
|
||||
#include "FileSystem.h"
|
||||
#include "sentence.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_SoundSystem, "SoundSystem", 0, LS_ERROR );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Forward declarations
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAudioMixer;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Important constants
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// 64K is > 1 second at 16-bit, 22050 Hz
|
||||
// 44k: UNDONE - need to double buffers now that we're playing back at 44100?
|
||||
#define OUTPUT_CHANNEL_COUNT 2
|
||||
#define BYTES_PER_SAMPLE 2
|
||||
#define OUTPUT_SAMPLE_RATE SOUND_DMA_SPEED
|
||||
#define OUTPUT_BUFFER_COUNT 64
|
||||
#define OUTPUT_BUFFER_MASK 0x3F
|
||||
#define OUTPUT_BUFFER_SAMPLE_COUNT (OUTPUT_BUFFER_SIZE_BYTES / BYTES_PER_SAMPLE)
|
||||
#define OUTPUT_BUFFER_SIZE_BYTES 1024
|
||||
#define PAINTBUFFER_SIZE 1024
|
||||
#define MAX_CHANNELS 16
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Implementation of IAudioDevice for WAV files
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAudioDeviceWave : public IAudioDevice
|
||||
{
|
||||
public:
|
||||
// Inherited from IAudioDevice
|
||||
virtual bool Init( void );
|
||||
virtual void Shutdown( void );
|
||||
virtual const char *DeviceName( void ) const;
|
||||
virtual int DeviceChannels( void ) const;
|
||||
virtual int DeviceSampleBits( void ) const;
|
||||
virtual int DeviceSampleBytes( void ) const;
|
||||
virtual int DeviceSampleRate( void ) const;
|
||||
virtual int DeviceSampleCount( void ) const;
|
||||
virtual void Mix8Mono( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward = true );
|
||||
virtual void Mix8Stereo( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward = true );
|
||||
virtual void Mix16Mono( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward = true );
|
||||
virtual void Mix16Stereo( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward = true );
|
||||
virtual int PaintBufferSampleCount( void ) const;
|
||||
virtual void MixBegin( void );
|
||||
|
||||
// mix a buffer up to time (time is absolute)
|
||||
void Update( float time );
|
||||
void Flush( void );
|
||||
void TransferBufferStereo16( short *pOutput, int sampleCount );
|
||||
int GetOutputPosition( void );
|
||||
float GetAmountofTimeAhead( void );
|
||||
int GetNumberofSamplesAhead( void );
|
||||
void AddSource( CAudioMixer *pSource );
|
||||
void StopSounds( void );
|
||||
int FindSourceIndex( CAudioMixer *pSource );
|
||||
CAudioMixer *GetMixerForSource( CAudioSource *source );
|
||||
|
||||
private:
|
||||
class CAudioMixerState
|
||||
{
|
||||
public:
|
||||
CAudioMixer *mixer;
|
||||
};
|
||||
|
||||
class CAudioBuffer
|
||||
{
|
||||
public:
|
||||
WAVEHDR *hdr;
|
||||
bool submitted;
|
||||
int submit_sample_count;
|
||||
|
||||
CUtlVector< CAudioMixerState > m_Referenced;
|
||||
};
|
||||
|
||||
struct portable_samplepair_t
|
||||
{
|
||||
int left;
|
||||
int right;
|
||||
};
|
||||
|
||||
void OpenWaveOut( void );
|
||||
void CloseWaveOut( void );
|
||||
void AllocateOutputBuffers();
|
||||
void FreeOutputBuffers();
|
||||
void* AllocOutputMemory( int nSize, HGLOBAL &hMemory );
|
||||
void FreeOutputMemory( HGLOBAL &hMemory );
|
||||
|
||||
bool ValidWaveOut( void ) const;
|
||||
CAudioBuffer *GetEmptyBuffer( void );
|
||||
void SilenceBuffer( short *pSamples, int sampleCount );
|
||||
|
||||
void SetChannel( int channelIndex, CAudioMixer *pSource );
|
||||
void FreeChannel( int channelIndex );
|
||||
|
||||
void RemoveMixerChannelReferences( CAudioMixer *mixer );
|
||||
void AddToReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer );
|
||||
void RemoveFromReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer );
|
||||
bool IsSourceReferencedByActiveBuffer( CAudioMixer *mixer );
|
||||
bool IsSoundInReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer );
|
||||
|
||||
// Compute how many samples we've mixed since most recent buffer submission
|
||||
void ComputeSampleAheadAmount( void );
|
||||
|
||||
// This is a single allocation for all wave headers (there are OUTPUT_BUFFER_COUNT of them)
|
||||
HGLOBAL m_hWaveHdr;
|
||||
|
||||
// This is a single allocation for all wave data (there are OUTPUT_BUFFER_COUNT of them)
|
||||
HANDLE m_hWaveData;
|
||||
|
||||
HWAVEOUT m_waveOutHandle;
|
||||
float m_mixTime;
|
||||
float m_baseTime;
|
||||
int m_sampleIndex;
|
||||
CAudioBuffer m_buffers[ OUTPUT_BUFFER_COUNT ];
|
||||
CAudioMixer *m_sourceList[MAX_CHANNELS];
|
||||
int m_nEstimatedSamplesAhead;
|
||||
|
||||
portable_samplepair_t m_paintbuffer[ PAINTBUFFER_SIZE ];
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Singleton
|
||||
//-----------------------------------------------------------------------------
|
||||
IAudioDevice *Audio_CreateWaveDevice( void )
|
||||
{
|
||||
return new CAudioDeviceWave;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Init, shutdown
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAudioDeviceWave::Init( void )
|
||||
{
|
||||
m_hWaveData = NULL;
|
||||
m_hWaveHdr = NULL;
|
||||
m_waveOutHandle = NULL;
|
||||
|
||||
for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
|
||||
{
|
||||
CAudioBuffer *buffer = &m_buffers[ i ];
|
||||
Assert( buffer );
|
||||
buffer->hdr = NULL;
|
||||
buffer->submitted = false;
|
||||
buffer->submit_sample_count = false;
|
||||
}
|
||||
|
||||
OpenWaveOut();
|
||||
|
||||
m_mixTime = m_baseTime = -1;
|
||||
m_sampleIndex = 0;
|
||||
memset( m_sourceList, 0, sizeof(m_sourceList) );
|
||||
|
||||
m_nEstimatedSamplesAhead = (int)( ( float ) OUTPUT_SAMPLE_RATE / 10.0f );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CAudioDeviceWave::Shutdown( void )
|
||||
{
|
||||
CloseWaveOut();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// WAV out device
|
||||
//-----------------------------------------------------------------------------
|
||||
inline bool CAudioDeviceWave::ValidWaveOut( void ) const
|
||||
{
|
||||
return m_waveOutHandle != 0;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Opens the windows wave out device
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAudioDeviceWave::OpenWaveOut( void )
|
||||
{
|
||||
WAVEFORMATEX waveFormat;
|
||||
memset( &waveFormat, 0, sizeof(waveFormat) );
|
||||
|
||||
// Select a PCM, 16-bit stereo playback device
|
||||
waveFormat.cbSize = sizeof(waveFormat);
|
||||
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
|
||||
waveFormat.nChannels = DeviceChannels();
|
||||
waveFormat.wBitsPerSample = DeviceSampleBits();
|
||||
waveFormat.nSamplesPerSec = DeviceSampleRate();
|
||||
waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
|
||||
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
|
||||
|
||||
MMRESULT errorCode = waveOutOpen( &m_waveOutHandle, WAVE_MAPPER, &waveFormat, 0, 0L, CALLBACK_NULL );
|
||||
while ( errorCode != MMSYSERR_NOERROR )
|
||||
{
|
||||
if ( errorCode != MMSYSERR_ALLOCATED )
|
||||
{
|
||||
Log_Warning( LOG_SoundSystem, "waveOutOpen failed\n" );
|
||||
m_waveOutHandle = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
int nRetVal = MessageBox( NULL,
|
||||
"The sound hardware is in use by another app.\n\n"
|
||||
"Select Retry to try to start sound again or Cancel to run with no sound.",
|
||||
"Sound not available",
|
||||
MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION);
|
||||
|
||||
if ( nRetVal != IDRETRY )
|
||||
{
|
||||
Log_Warning( LOG_SoundSystem, "waveOutOpen failure--hardware already in use\n" );
|
||||
m_waveOutHandle = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
errorCode = waveOutOpen( &m_waveOutHandle, WAVE_MAPPER, &waveFormat, 0, 0L, CALLBACK_NULL );
|
||||
}
|
||||
|
||||
AllocateOutputBuffers();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Closes the windows wave out device
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAudioDeviceWave::CloseWaveOut( void )
|
||||
{
|
||||
if ( ValidWaveOut() )
|
||||
{
|
||||
waveOutReset( m_waveOutHandle );
|
||||
FreeOutputBuffers();
|
||||
waveOutClose( m_waveOutHandle );
|
||||
m_waveOutHandle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Alloc output memory
|
||||
//-----------------------------------------------------------------------------
|
||||
void* CAudioDeviceWave::AllocOutputMemory( int nSize, HGLOBAL &hMemory )
|
||||
{
|
||||
// Output memory for waveform data+hdrs must be
|
||||
// globally allocated with GMEM_MOVEABLE and GMEM_SHARE flags.
|
||||
hMemory = GlobalAlloc( GMEM_MOVEABLE | GMEM_SHARE, nSize );
|
||||
if ( !hMemory )
|
||||
{
|
||||
Log_Warning( LOG_SoundSystem, "Sound: Out of memory.\n");
|
||||
CloseWaveOut();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HPSTR lpData = (char *)GlobalLock( hMemory );
|
||||
if ( !lpData )
|
||||
{
|
||||
Log_Warning( LOG_SoundSystem, "Sound: Failed to lock.\n");
|
||||
GlobalFree( hMemory );
|
||||
hMemory = NULL;
|
||||
CloseWaveOut();
|
||||
return NULL;
|
||||
}
|
||||
memset( lpData, 0, nSize );
|
||||
return lpData;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Free output memory
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAudioDeviceWave::FreeOutputMemory( HGLOBAL &hMemory )
|
||||
{
|
||||
if ( hMemory )
|
||||
{
|
||||
GlobalUnlock( hMemory );
|
||||
GlobalFree( hMemory );
|
||||
hMemory = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Allocate, free output buffers
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAudioDeviceWave::AllocateOutputBuffers()
|
||||
{
|
||||
// Allocate and lock memory for the waveform data.
|
||||
int nBufferSize = OUTPUT_BUFFER_SIZE_BYTES * OUTPUT_BUFFER_COUNT;
|
||||
HPSTR lpData = (char *)AllocOutputMemory( nBufferSize, m_hWaveData );
|
||||
if ( !lpData )
|
||||
return;
|
||||
|
||||
// Allocate and lock memory for the waveform header
|
||||
int nHdrSize = sizeof( WAVEHDR ) * OUTPUT_BUFFER_COUNT;
|
||||
LPWAVEHDR lpWaveHdr = (LPWAVEHDR)AllocOutputMemory( nHdrSize, m_hWaveHdr );
|
||||
if ( !lpWaveHdr )
|
||||
return;
|
||||
|
||||
// After allocation, set up and prepare headers.
|
||||
for ( int i=0 ; i < OUTPUT_BUFFER_COUNT; i++ )
|
||||
{
|
||||
LPWAVEHDR lpHdr = lpWaveHdr + i;
|
||||
lpHdr->dwBufferLength = OUTPUT_BUFFER_SIZE_BYTES;
|
||||
lpHdr->lpData = lpData + (i * OUTPUT_BUFFER_SIZE_BYTES);
|
||||
|
||||
MMRESULT nResult = waveOutPrepareHeader( m_waveOutHandle, lpHdr, sizeof(WAVEHDR) );
|
||||
if ( nResult != MMSYSERR_NOERROR )
|
||||
{
|
||||
Log_Warning( LOG_SoundSystem, "Sound: failed to prepare wave headers\n" );
|
||||
CloseWaveOut();
|
||||
return;
|
||||
}
|
||||
|
||||
m_buffers[i].hdr = lpHdr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CAudioDeviceWave::FreeOutputBuffers()
|
||||
{
|
||||
// Unprepare headers.
|
||||
for ( int i=0 ; i < OUTPUT_BUFFER_COUNT; i++ )
|
||||
{
|
||||
if ( m_buffers[i].hdr )
|
||||
{
|
||||
waveOutUnprepareHeader( m_waveOutHandle, m_buffers[i].hdr, sizeof(WAVEHDR) );
|
||||
m_buffers[i].hdr = NULL;
|
||||
}
|
||||
|
||||
m_buffers[i].submitted = false;
|
||||
m_buffers[i].submit_sample_count = 0;
|
||||
m_buffers[i].m_Referenced.Purge();
|
||||
}
|
||||
|
||||
FreeOutputMemory( m_hWaveData );
|
||||
FreeOutputMemory( m_hWaveHdr );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Device parameters
|
||||
//-----------------------------------------------------------------------------
|
||||
const char *CAudioDeviceWave::DeviceName( void ) const
|
||||
{
|
||||
return "Windows WAVE";
|
||||
}
|
||||
|
||||
int CAudioDeviceWave::DeviceChannels( void ) const
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
int CAudioDeviceWave::DeviceSampleBits( void ) const
|
||||
{
|
||||
return (BYTES_PER_SAMPLE * 8);
|
||||
}
|
||||
|
||||
int CAudioDeviceWave::DeviceSampleBytes( void ) const
|
||||
{
|
||||
return BYTES_PER_SAMPLE;
|
||||
}
|
||||
|
||||
int CAudioDeviceWave::DeviceSampleRate( void ) const
|
||||
{
|
||||
return OUTPUT_SAMPLE_RATE;
|
||||
}
|
||||
|
||||
int CAudioDeviceWave::DeviceSampleCount( void ) const
|
||||
{
|
||||
return OUTPUT_BUFFER_SAMPLE_COUNT;
|
||||
}
|
||||
|
||||
int CAudioDeviceWave::PaintBufferSampleCount( void ) const
|
||||
{
|
||||
return PAINTBUFFER_SIZE;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Mixing routines
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAudioDeviceWave::Mix8Mono( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward )
|
||||
{
|
||||
int sampleIndex = 0;
|
||||
fixedint sampleFrac = inputOffset;
|
||||
|
||||
int fixup = 0;
|
||||
int fixupstep = 1;
|
||||
|
||||
if ( !forward )
|
||||
{
|
||||
fixup = outCount - 1;
|
||||
fixupstep = -1;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < outCount; i++, fixup += fixupstep )
|
||||
{
|
||||
int dest = max( outputOffset + fixup, 0 );
|
||||
|
||||
m_paintbuffer[ dest ].left += pChannel->leftvol * pData[sampleIndex];
|
||||
m_paintbuffer[ dest ].right += pChannel->rightvol * pData[sampleIndex];
|
||||
sampleFrac += rateScaleFix;
|
||||
sampleIndex += FIX_INTPART(sampleFrac);
|
||||
sampleFrac = FIX_FRACPART(sampleFrac);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CAudioDeviceWave::Mix8Stereo( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward )
|
||||
{
|
||||
int sampleIndex = 0;
|
||||
fixedint sampleFrac = inputOffset;
|
||||
|
||||
int fixup = 0;
|
||||
int fixupstep = 1;
|
||||
|
||||
if ( !forward )
|
||||
{
|
||||
fixup = outCount - 1;
|
||||
fixupstep = -1;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < outCount; i++, fixup += fixupstep )
|
||||
{
|
||||
int dest = max( outputOffset + fixup, 0 );
|
||||
|
||||
m_paintbuffer[ dest ].left += pChannel->leftvol * pData[sampleIndex];
|
||||
m_paintbuffer[ dest ].right += pChannel->rightvol * pData[sampleIndex+1];
|
||||
sampleFrac += rateScaleFix;
|
||||
sampleIndex += FIX_INTPART(sampleFrac)<<1;
|
||||
sampleFrac = FIX_FRACPART(sampleFrac);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CAudioDeviceWave::Mix16Mono( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward )
|
||||
{
|
||||
int sampleIndex = 0;
|
||||
fixedint sampleFrac = inputOffset;
|
||||
|
||||
int fixup = 0;
|
||||
int fixupstep = 1;
|
||||
|
||||
if ( !forward )
|
||||
{
|
||||
fixup = outCount - 1;
|
||||
fixupstep = -1;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < outCount; i++, fixup += fixupstep )
|
||||
{
|
||||
int dest = max( outputOffset + fixup, 0 );
|
||||
|
||||
m_paintbuffer[ dest ].left += (pChannel->leftvol * pData[sampleIndex])>>8;
|
||||
m_paintbuffer[ dest ].right += (pChannel->rightvol * pData[sampleIndex])>>8;
|
||||
sampleFrac += rateScaleFix;
|
||||
sampleIndex += FIX_INTPART(sampleFrac);
|
||||
sampleFrac = FIX_FRACPART(sampleFrac);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CAudioDeviceWave::Mix16Stereo( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward )
|
||||
{
|
||||
int sampleIndex = 0;
|
||||
fixedint sampleFrac = inputOffset;
|
||||
|
||||
int fixup = 0;
|
||||
int fixupstep = 1;
|
||||
|
||||
if ( !forward )
|
||||
{
|
||||
fixup = outCount - 1;
|
||||
fixupstep = -1;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < outCount; i++, fixup += fixupstep )
|
||||
{
|
||||
int dest = max( outputOffset + fixup, 0 );
|
||||
|
||||
m_paintbuffer[ dest ].left += (pChannel->leftvol * pData[sampleIndex])>>8;
|
||||
m_paintbuffer[ dest ].right += (pChannel->rightvol * pData[sampleIndex+1])>>8;
|
||||
|
||||
sampleFrac += rateScaleFix;
|
||||
sampleIndex += FIX_INTPART(sampleFrac)<<1;
|
||||
sampleFrac = FIX_FRACPART(sampleFrac);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CAudioDeviceWave::MixBegin( void )
|
||||
{
|
||||
memset( m_paintbuffer, 0, sizeof(m_paintbuffer) );
|
||||
}
|
||||
|
||||
void CAudioDeviceWave::TransferBufferStereo16( short *pOutput, int sampleCount )
|
||||
{
|
||||
for ( int i = 0; i < sampleCount; i++ )
|
||||
{
|
||||
if ( m_paintbuffer[i].left > 32767 )
|
||||
m_paintbuffer[i].left = 32767;
|
||||
else if ( m_paintbuffer[i].left < -32768 )
|
||||
m_paintbuffer[i].left = -32768;
|
||||
|
||||
if ( m_paintbuffer[i].right > 32767 )
|
||||
m_paintbuffer[i].right = 32767;
|
||||
else if ( m_paintbuffer[i].right < -32768 )
|
||||
m_paintbuffer[i].right = -32768;
|
||||
|
||||
*pOutput++ = (short)m_paintbuffer[i].left;
|
||||
*pOutput++ = (short)m_paintbuffer[i].right;
|
||||
}
|
||||
}
|
||||
|
||||
void CAudioDeviceWave::RemoveMixerChannelReferences( CAudioMixer *mixer )
|
||||
{
|
||||
for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
|
||||
{
|
||||
RemoveFromReferencedList( mixer, &m_buffers[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
void CAudioDeviceWave::AddToReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer )
|
||||
{
|
||||
// Already in list
|
||||
for ( int i = 0; i < buffer->m_Referenced.Count(); i++ )
|
||||
{
|
||||
if ( buffer->m_Referenced[ i ].mixer == mixer )
|
||||
return;
|
||||
}
|
||||
|
||||
// Just remove it
|
||||
int idx = buffer->m_Referenced.AddToTail();
|
||||
|
||||
CAudioMixerState *state = &buffer->m_Referenced[ idx ];
|
||||
state->mixer = mixer;
|
||||
}
|
||||
|
||||
void CAudioDeviceWave::RemoveFromReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer )
|
||||
{
|
||||
for ( int i = 0; i < buffer->m_Referenced.Count(); i++ )
|
||||
{
|
||||
if ( buffer->m_Referenced[ i ].mixer == mixer )
|
||||
{
|
||||
buffer->m_Referenced.Remove( i );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CAudioDeviceWave::IsSoundInReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer )
|
||||
{
|
||||
for ( int i = 0; i < buffer->m_Referenced.Count(); i++ )
|
||||
{
|
||||
if ( buffer->m_Referenced[ i ].mixer == mixer )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CAudioDeviceWave::IsSourceReferencedByActiveBuffer( CAudioMixer *mixer )
|
||||
{
|
||||
if ( !ValidWaveOut() )
|
||||
return false;
|
||||
|
||||
CAudioBuffer *buffer;
|
||||
for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
|
||||
{
|
||||
buffer = &m_buffers[ i ];
|
||||
if ( !buffer->submitted )
|
||||
continue;
|
||||
|
||||
if ( buffer->hdr->dwFlags & WHDR_DONE )
|
||||
continue;
|
||||
|
||||
// See if it's referenced
|
||||
if ( IsSoundInReferencedList( mixer, buffer ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CAudioDeviceWave::CAudioBuffer *CAudioDeviceWave::GetEmptyBuffer( void )
|
||||
{
|
||||
CAudioBuffer *pOutput = NULL;
|
||||
if ( ValidWaveOut() )
|
||||
{
|
||||
for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
|
||||
{
|
||||
if ( !(m_buffers[ i ].submitted ) ||
|
||||
m_buffers[i].hdr->dwFlags & WHDR_DONE )
|
||||
{
|
||||
pOutput = &m_buffers[i];
|
||||
pOutput->submitted = true;
|
||||
pOutput->m_Referenced.Purge();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pOutput;
|
||||
}
|
||||
|
||||
void CAudioDeviceWave::SilenceBuffer( short *pSamples, int sampleCount )
|
||||
{
|
||||
int i;
|
||||
|
||||
for ( i = 0; i < sampleCount; i++ )
|
||||
{
|
||||
// left
|
||||
*pSamples++ = 0;
|
||||
// right
|
||||
*pSamples++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CAudioDeviceWave::Flush( void )
|
||||
{
|
||||
waveOutReset( m_waveOutHandle );
|
||||
}
|
||||
|
||||
// mix a buffer up to time (time is absolute)
|
||||
void CAudioDeviceWave::Update( float time )
|
||||
{
|
||||
if ( !ValidWaveOut() )
|
||||
return;
|
||||
|
||||
// reset the system
|
||||
if ( m_mixTime < 0 || time < m_baseTime )
|
||||
{
|
||||
m_baseTime = time;
|
||||
m_mixTime = 0;
|
||||
}
|
||||
|
||||
// put time in our coordinate frame
|
||||
time -= m_baseTime;
|
||||
|
||||
if ( time > m_mixTime )
|
||||
{
|
||||
CAudioBuffer *pBuffer = GetEmptyBuffer();
|
||||
|
||||
// no free buffers, mixing is ahead of the playback!
|
||||
if ( !pBuffer || !pBuffer->hdr )
|
||||
{
|
||||
//Con_Printf( "out of buffers\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
// UNDONE: These numbers are constants
|
||||
// calc number of samples (2 channels * 2 bytes per sample)
|
||||
int sampleCount = pBuffer->hdr->dwBufferLength >> 2;
|
||||
m_mixTime += sampleCount * (1.0f / OUTPUT_SAMPLE_RATE);
|
||||
|
||||
short *pSamples = reinterpret_cast<short *>(pBuffer->hdr->lpData);
|
||||
|
||||
SilenceBuffer( pSamples, sampleCount );
|
||||
|
||||
int tempCount = sampleCount;
|
||||
|
||||
while ( tempCount > 0 )
|
||||
{
|
||||
if ( tempCount > PaintBufferSampleCount() )
|
||||
{
|
||||
sampleCount = PaintBufferSampleCount();
|
||||
}
|
||||
else
|
||||
{
|
||||
sampleCount = tempCount;
|
||||
}
|
||||
|
||||
MixBegin();
|
||||
for ( int i = 0; i < MAX_CHANNELS; i++ )
|
||||
{
|
||||
CAudioMixer *pSource = m_sourceList[i];
|
||||
if ( !pSource )
|
||||
continue;
|
||||
|
||||
int currentsample = pSource->GetSamplePosition();
|
||||
bool forward = pSource->GetDirection();
|
||||
|
||||
if ( pSource->GetActive() )
|
||||
{
|
||||
if ( !pSource->MixDataToDevice( this, pSource->GetChannel(), currentsample, sampleCount, DeviceSampleRate(), forward ) )
|
||||
{
|
||||
// Source becomes inactive when last submitted sample is finally
|
||||
// submitted. But it lingers until it's no longer referenced
|
||||
pSource->SetActive( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
AddToReferencedList( pSource, pBuffer );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !IsSourceReferencedByActiveBuffer( pSource ) )
|
||||
{
|
||||
if ( !pSource->GetAutoDelete() )
|
||||
{
|
||||
FreeChannel( i );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pSource->IncrementSamples( pSource->GetChannel(), currentsample, sampleCount, DeviceSampleRate(), forward );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TransferBufferStereo16( pSamples, sampleCount );
|
||||
|
||||
m_sampleIndex += sampleCount;
|
||||
tempCount -= sampleCount;
|
||||
pSamples += sampleCount * 2;
|
||||
}
|
||||
// if the buffers aren't aligned on sample boundaries, this will hard-lock the machine!
|
||||
|
||||
pBuffer->submit_sample_count = GetOutputPosition();
|
||||
|
||||
waveOutWrite( m_waveOutHandle, pBuffer->hdr, sizeof(*(pBuffer->hdr)) );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
int CAudioDeviceWave::GetNumberofSamplesAhead( void )
|
||||
{
|
||||
ComputeSampleAheadAmount();
|
||||
return m_nEstimatedSamplesAhead;
|
||||
}
|
||||
|
||||
float CAudioDeviceWave::GetAmountofTimeAhead( void )
|
||||
{
|
||||
ComputeSampleAheadAmount();
|
||||
return ( (float)m_nEstimatedSamplesAhead / (float)OUTPUT_SAMPLE_RATE );
|
||||
}
|
||||
|
||||
// Find the most recent submitted sample that isn't flagged as whdr_done
|
||||
void CAudioDeviceWave::ComputeSampleAheadAmount( void )
|
||||
{
|
||||
m_nEstimatedSamplesAhead = 0;
|
||||
|
||||
int newest_sample_index = -1;
|
||||
int newest_sample_count = 0;
|
||||
|
||||
CAudioBuffer *buffer;
|
||||
|
||||
if ( ValidDevice() )
|
||||
{
|
||||
|
||||
for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
|
||||
{
|
||||
buffer = &m_buffers[ i ];
|
||||
if ( !buffer->submitted )
|
||||
continue;
|
||||
|
||||
if ( buffer->hdr->dwFlags & WHDR_DONE )
|
||||
continue;
|
||||
|
||||
if ( buffer->submit_sample_count > newest_sample_count )
|
||||
{
|
||||
newest_sample_index = i;
|
||||
newest_sample_count = buffer->submit_sample_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( newest_sample_index == -1 )
|
||||
return;
|
||||
|
||||
|
||||
buffer = &m_buffers[ newest_sample_index ];
|
||||
int currentPos = GetOutputPosition() ;
|
||||
m_nEstimatedSamplesAhead = currentPos - buffer->submit_sample_count;
|
||||
}
|
||||
*/
|
||||
|
||||
int CAudioDeviceWave::FindSourceIndex( CAudioMixer *pSource )
|
||||
{
|
||||
for ( int i = 0; i < MAX_CHANNELS; i++ )
|
||||
{
|
||||
if ( pSource == m_sourceList[i] )
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
CAudioMixer *CAudioDeviceWave::GetMixerForSource( CAudioSource *source )
|
||||
{
|
||||
for ( int i = 0; i < MAX_CHANNELS; i++ )
|
||||
{
|
||||
if ( !m_sourceList[i] )
|
||||
continue;
|
||||
|
||||
if ( source == m_sourceList[i]->GetSource() )
|
||||
{
|
||||
return m_sourceList[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CAudioDeviceWave::AddSource( CAudioMixer *pSource )
|
||||
{
|
||||
int slot = 0;
|
||||
for ( int i = 0; i < MAX_CHANNELS; i++ )
|
||||
{
|
||||
if ( !m_sourceList[i] )
|
||||
{
|
||||
slot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_sourceList[slot] )
|
||||
{
|
||||
FreeChannel( slot );
|
||||
}
|
||||
SetChannel( slot, pSource );
|
||||
|
||||
pSource->SetActive( true );
|
||||
}
|
||||
|
||||
|
||||
void CAudioDeviceWave::StopSounds( void )
|
||||
{
|
||||
for ( int i = 0; i < MAX_CHANNELS; i++ )
|
||||
{
|
||||
if ( m_sourceList[i] )
|
||||
{
|
||||
FreeChannel( i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CAudioDeviceWave::SetChannel( int channelIndex, CAudioMixer *pSource )
|
||||
{
|
||||
if ( channelIndex < 0 || channelIndex >= MAX_CHANNELS )
|
||||
return;
|
||||
|
||||
m_sourceList[channelIndex] = pSource;
|
||||
}
|
||||
|
||||
void CAudioDeviceWave::FreeChannel( int channelIndex )
|
||||
{
|
||||
if ( channelIndex < 0 || channelIndex >= MAX_CHANNELS )
|
||||
return;
|
||||
|
||||
if ( m_sourceList[channelIndex] )
|
||||
{
|
||||
RemoveMixerChannelReferences( m_sourceList[channelIndex] );
|
||||
|
||||
delete m_sourceList[channelIndex];
|
||||
m_sourceList[channelIndex] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int CAudioDeviceWave::GetOutputPosition( void )
|
||||
{
|
||||
if ( !m_waveOutHandle )
|
||||
return 0;
|
||||
|
||||
MMTIME mmtime;
|
||||
mmtime.wType = TIME_SAMPLES;
|
||||
waveOutGetPosition( m_waveOutHandle, &mmtime, sizeof( MMTIME ) );
|
||||
|
||||
// Convert time to sample count
|
||||
return ( mmtime.u.sample );
|
||||
}
|
||||
|
||||
27
soundsystem/snd_dev_wave.h
Normal file
27
soundsystem/snd_dev_wave.h
Normal file
@@ -0,0 +1,27 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef SND_DEV_WAVE_H
|
||||
#define SND_DEV_WAVE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Forward declarations
|
||||
//-----------------------------------------------------------------------------
|
||||
class IAudioDevice;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Creates a device that mixes WAVs using windows
|
||||
//-----------------------------------------------------------------------------
|
||||
IAudioDevice *Audio_CreateWaveDevice( void );
|
||||
|
||||
|
||||
#endif // SND_DEV_WAVE_H
|
||||
93
soundsystem/snd_io.cpp
Normal file
93
soundsystem/snd_io.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "soundsystem.h"
|
||||
#include "tier2/riff.h"
|
||||
#include "filesystem.h"
|
||||
#include "tier1/strtools.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Implements Audio IO on the engine's COMMON filesystem
|
||||
//-----------------------------------------------------------------------------
|
||||
class COM_IOReadBinary : public IFileReadBinary
|
||||
{
|
||||
public:
|
||||
FileHandle_t open( const char *pFileName );
|
||||
int read( void *pOutput, int size, FileHandle_t file );
|
||||
void seek( FileHandle_t file, int pos );
|
||||
unsigned int tell( FileHandle_t file );
|
||||
unsigned int size( FileHandle_t file );
|
||||
void close( FileHandle_t file );
|
||||
};
|
||||
|
||||
|
||||
// prepend sound/ to the filename -- all sounds are loaded from the sound/ directory
|
||||
FileHandle_t COM_IOReadBinary::open( const char *pFileName )
|
||||
{
|
||||
char namebuffer[512];
|
||||
FileHandle_t hFile;
|
||||
|
||||
Q_strncpy(namebuffer, "sound", sizeof( namebuffer ) );
|
||||
|
||||
//HACK HACK HACK the server is sending back sound names with slashes in front...
|
||||
if (pFileName[0]!='/')
|
||||
{
|
||||
Q_strncat(namebuffer,"/", sizeof( namebuffer ), COPY_ALL_CHARACTERS );
|
||||
}
|
||||
|
||||
Q_strncat( namebuffer, pFileName, sizeof( namebuffer ), COPY_ALL_CHARACTERS );
|
||||
|
||||
hFile = g_pFullFileSystem->Open( namebuffer, "rb", "GAME" );
|
||||
|
||||
return hFile;
|
||||
}
|
||||
|
||||
int COM_IOReadBinary::read( void *pOutput, int size, FileHandle_t file )
|
||||
{
|
||||
if ( !file )
|
||||
return 0;
|
||||
|
||||
return g_pFullFileSystem->Read( pOutput, size, file );
|
||||
}
|
||||
|
||||
void COM_IOReadBinary::seek( FileHandle_t file, int pos )
|
||||
{
|
||||
if ( !file )
|
||||
return;
|
||||
|
||||
g_pFullFileSystem->Seek( file, pos, FILESYSTEM_SEEK_HEAD );
|
||||
}
|
||||
|
||||
unsigned int COM_IOReadBinary::tell( FileHandle_t file )
|
||||
{
|
||||
if ( !file )
|
||||
return 0;
|
||||
return g_pFullFileSystem->Tell( file );
|
||||
}
|
||||
|
||||
unsigned int COM_IOReadBinary::size( FileHandle_t file )
|
||||
{
|
||||
if (!file)
|
||||
return 0;
|
||||
return g_pFullFileSystem->Size( file );
|
||||
}
|
||||
|
||||
void COM_IOReadBinary::close( FileHandle_t file )
|
||||
{
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
g_pFullFileSystem->Close( file );
|
||||
}
|
||||
|
||||
static COM_IOReadBinary io;
|
||||
IFileReadBinary *g_pSndIO = &io;
|
||||
|
||||
532
soundsystem/snd_wave_mixer.cpp
Normal file
532
soundsystem/snd_wave_mixer.cpp
Normal file
@@ -0,0 +1,532 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "snd_dev_wave.h"
|
||||
#include "snd_wave_source.h"
|
||||
#include "soundsystem/snd_audio_source.h"
|
||||
#include "snd_wave_mixer_private.h"
|
||||
#include "snd_wave_mixer_adpcm.h"
|
||||
#include "tier2/riff.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// These mixers provide an abstraction layer between the audio device and
|
||||
// mixing/decoding code. They allow data to be decoded and mixed using
|
||||
// optimized, format sensitive code by calling back into the device that
|
||||
// controls them.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: maps mixing to 8-bit mono mixer
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAudioMixerWave8Mono : public CAudioMixerWave
|
||||
{
|
||||
public:
|
||||
CAudioMixerWave8Mono( CWaveData *data ) : CAudioMixerWave( data ) {}
|
||||
virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true )
|
||||
{
|
||||
pDevice->Mix8Mono( pChannel, (char *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward );
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: maps mixing to 8-bit stereo mixer
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAudioMixerWave8Stereo : public CAudioMixerWave
|
||||
{
|
||||
public:
|
||||
CAudioMixerWave8Stereo( CWaveData *data ) : CAudioMixerWave( data ) {}
|
||||
virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true )
|
||||
{
|
||||
pDevice->Mix8Stereo( pChannel, (char *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward );
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: maps mixing to 16-bit mono mixer
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAudioMixerWave16Mono : public CAudioMixerWave
|
||||
{
|
||||
public:
|
||||
CAudioMixerWave16Mono( CWaveData *data ) : CAudioMixerWave( data ) {}
|
||||
virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true )
|
||||
{
|
||||
pDevice->Mix16Mono( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: maps mixing to 16-bit stereo mixer
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAudioMixerWave16Stereo : public CAudioMixerWave
|
||||
{
|
||||
public:
|
||||
CAudioMixerWave16Stereo( CWaveData *data ) : CAudioMixerWave( data ) {}
|
||||
virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true )
|
||||
{
|
||||
pDevice->Mix16Stereo( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Create an approprite mixer type given the data format
|
||||
// Input : *data - data access abstraction
|
||||
// format - pcm or adpcm (1 or 2 -- RIFF format)
|
||||
// channels - number of audio channels (1 = mono, 2 = stereo)
|
||||
// bits - bits per sample
|
||||
// Output : CAudioMixer * abstract mixer type that maps mixing to appropriate code
|
||||
//-----------------------------------------------------------------------------
|
||||
CAudioMixer *CreateWaveMixer( CWaveData *data, int format, int channels, int bits )
|
||||
{
|
||||
if ( format == WAVE_FORMAT_PCM )
|
||||
{
|
||||
if ( channels > 1 )
|
||||
{
|
||||
if ( bits == 8 )
|
||||
return new CAudioMixerWave8Stereo( data );
|
||||
else
|
||||
return new CAudioMixerWave16Stereo( data );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( bits == 8 )
|
||||
return new CAudioMixerWave8Mono( data );
|
||||
else
|
||||
return new CAudioMixerWave16Mono( data );
|
||||
}
|
||||
}
|
||||
else if ( format == WAVE_FORMAT_ADPCM )
|
||||
{
|
||||
return CreateADPCMMixer( data );
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Init the base WAVE mixer.
|
||||
// Input : *data - data access object
|
||||
//-----------------------------------------------------------------------------
|
||||
CAudioMixerWave::CAudioMixerWave( CWaveData *data ) : m_pData(data), m_pChannel(NULL)
|
||||
{
|
||||
m_loop = 0;
|
||||
m_sample = 0;
|
||||
m_absoluteSample = 0;
|
||||
m_scubSample = -1;
|
||||
m_fracOffset = 0;
|
||||
m_bActive = false;
|
||||
m_nModelIndex = -1;
|
||||
m_bForward = true;
|
||||
m_bAutoDelete = true;
|
||||
m_pChannel = new channel_t;
|
||||
m_pChannel->leftvol = 127;
|
||||
m_pChannel->rightvol = 127;
|
||||
m_pChannel->pitch = 1.0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Frees the data access object (we own it after construction)
|
||||
//-----------------------------------------------------------------------------
|
||||
CAudioMixerWave::~CAudioMixerWave( void )
|
||||
{
|
||||
delete m_pData;
|
||||
delete m_pChannel;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Decode and read the data
|
||||
// by default we just pass the request on to the data access object
|
||||
// other mixers may need to buffer or decode the data for some reason
|
||||
//
|
||||
// Input : **pData - dest pointer
|
||||
// sampleCount - number of samples needed
|
||||
// Output : number of samples available in this batch
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAudioMixerWave::GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward /*= true*/ )
|
||||
{
|
||||
if ( samplePosition != m_sample )
|
||||
{
|
||||
// Seek
|
||||
m_sample = samplePosition;
|
||||
m_absoluteSample = samplePosition;
|
||||
}
|
||||
|
||||
return m_pData->ReadSourceData( pData, m_sample, sampleCount, forward );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: calls through the wavedata to get the audio source
|
||||
// Output : CAudioSource
|
||||
//-----------------------------------------------------------------------------
|
||||
CAudioSource *CAudioMixerWave::GetSource( void )
|
||||
{
|
||||
if ( m_pData )
|
||||
return &m_pData->Source();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Gets the current sample location in playback
|
||||
// Output : int (samples from start of wave)
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAudioMixerWave::GetSamplePosition( void )
|
||||
{
|
||||
return m_sample;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Gets the current sample location in playback
|
||||
// Output : int (samples from start of wave)
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAudioMixerWave::GetScubPosition( void )
|
||||
{
|
||||
if (m_scubSample != -1)
|
||||
{
|
||||
return m_scubSample;
|
||||
}
|
||||
return m_sample;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : position -
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAudioMixerWave::SetSamplePosition( int position, bool scrubbing )
|
||||
{
|
||||
position = MAX( 0, position );
|
||||
|
||||
m_sample = position;
|
||||
m_absoluteSample = position;
|
||||
m_startpos = m_sample;
|
||||
if (scrubbing)
|
||||
{
|
||||
m_scubSample = position;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_scubSample = -1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : position -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAudioMixerWave::SetLoopPosition( int position )
|
||||
{
|
||||
m_loop = position;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : int
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAudioMixerWave::GetStartPosition( void )
|
||||
{
|
||||
return m_startpos;
|
||||
}
|
||||
|
||||
bool CAudioMixerWave::GetActive( void )
|
||||
{
|
||||
return m_bActive;
|
||||
}
|
||||
|
||||
void CAudioMixerWave::SetActive( bool active )
|
||||
{
|
||||
m_bActive = active;
|
||||
}
|
||||
|
||||
void CAudioMixerWave::SetModelIndex( int index )
|
||||
{
|
||||
m_nModelIndex = index;
|
||||
}
|
||||
|
||||
int CAudioMixerWave::GetModelIndex( void ) const
|
||||
{
|
||||
return m_nModelIndex;
|
||||
}
|
||||
|
||||
void CAudioMixerWave::SetDirection( bool forward )
|
||||
{
|
||||
m_bForward = forward;
|
||||
}
|
||||
|
||||
bool CAudioMixerWave::GetDirection( void ) const
|
||||
{
|
||||
return m_bForward;
|
||||
}
|
||||
|
||||
void CAudioMixerWave::SetAutoDelete( bool autodelete )
|
||||
{
|
||||
m_bAutoDelete = autodelete;
|
||||
}
|
||||
|
||||
bool CAudioMixerWave::GetAutoDelete( void ) const
|
||||
{
|
||||
return m_bAutoDelete;
|
||||
}
|
||||
|
||||
void CAudioMixerWave::SetVolume( float volume )
|
||||
{
|
||||
int ivolume = (int)( clamp( volume, 0.0f, 1.0f ) * 127.0f );
|
||||
|
||||
m_pChannel->leftvol = ivolume;
|
||||
m_pChannel->rightvol = ivolume;
|
||||
}
|
||||
|
||||
channel_t *CAudioMixerWave::GetChannel()
|
||||
{
|
||||
Assert( m_pChannel );
|
||||
return m_pChannel;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pChannel -
|
||||
// sampleCount -
|
||||
// outputRate -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAudioMixerWave::IncrementSamples( channel_t *pChannel, int startSample, int sampleCount,int outputRate, bool forward /*= true*/ )
|
||||
{
|
||||
int inputSampleRate = (int)(pChannel->pitch * m_pData->Source().SampleRate());
|
||||
float rate = (float)inputSampleRate / outputRate;
|
||||
|
||||
int startpos = startSample;
|
||||
|
||||
if ( !forward )
|
||||
{
|
||||
int requestedstart = startSample - (int)( sampleCount * rate );
|
||||
if ( requestedstart < 0 )
|
||||
return;
|
||||
|
||||
startpos = MAX( 0, requestedstart );
|
||||
SetSamplePosition( startpos );
|
||||
}
|
||||
|
||||
while ( sampleCount > 0 )
|
||||
{
|
||||
int inputSampleCount;
|
||||
int outputSampleCount = sampleCount;
|
||||
|
||||
if ( outputRate != inputSampleRate )
|
||||
{
|
||||
inputSampleCount = (int)(sampleCount * rate);
|
||||
}
|
||||
else
|
||||
{
|
||||
inputSampleCount = sampleCount;
|
||||
}
|
||||
|
||||
sampleCount -= outputSampleCount;
|
||||
if ( forward )
|
||||
{
|
||||
m_sample += inputSampleCount;
|
||||
m_absoluteSample += inputSampleCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: The device calls this to request data. The mixer must provide the
|
||||
// full amount of samples or have silence in its output stream.
|
||||
// Input : *pDevice - requesting device
|
||||
// sampleCount - number of samples at the output rate
|
||||
// outputRate - sampling rate of the request
|
||||
// Output : Returns true to keep mixing, false to delete this mixer
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAudioMixerWave::SkipSamples( IAudioDevice *pDevice, channel_t *pChannel,
|
||||
int startSample, int sampleCount, int outputRate, bool forward /*= true*/ )
|
||||
{
|
||||
int offset = 0;
|
||||
|
||||
int inputSampleRate = (int)(pChannel->pitch * m_pData->Source().SampleRate());
|
||||
float rate = (float)inputSampleRate / outputRate;
|
||||
|
||||
sampleCount = MIN( sampleCount, pDevice->PaintBufferSampleCount() );
|
||||
|
||||
int startpos = startSample;
|
||||
|
||||
if ( !forward )
|
||||
{
|
||||
int requestedstart = startSample - (int)( sampleCount * rate );
|
||||
if ( requestedstart < 0 )
|
||||
return false;
|
||||
|
||||
startpos = MAX( 0, requestedstart );
|
||||
SetSamplePosition( startpos );
|
||||
}
|
||||
|
||||
while ( sampleCount > 0 )
|
||||
{
|
||||
int availableSamples;
|
||||
int inputSampleCount;
|
||||
char *pData = NULL;
|
||||
int outputSampleCount = sampleCount;
|
||||
|
||||
if ( outputRate != inputSampleRate )
|
||||
{
|
||||
inputSampleCount = (int)(sampleCount * rate);
|
||||
if ( !forward )
|
||||
{
|
||||
startSample = MAX( 0, startSample - inputSampleCount );
|
||||
}
|
||||
int availableSamples = GetOutputData( (void **)&pData, startSample, inputSampleCount, forward );
|
||||
if ( !availableSamples )
|
||||
break;
|
||||
|
||||
if ( availableSamples < inputSampleCount )
|
||||
{
|
||||
outputSampleCount = (int)(availableSamples / rate);
|
||||
inputSampleCount = availableSamples;
|
||||
}
|
||||
|
||||
// compute new fraction part of sample index
|
||||
float offset = (m_fracOffset / FIX_SCALE) + (rate * outputSampleCount);
|
||||
offset = offset - (float)((int)offset);
|
||||
m_fracOffset = FIX_FLOAT(offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !forward )
|
||||
{
|
||||
startSample = MAX( 0, startSample - sampleCount );
|
||||
}
|
||||
availableSamples = GetOutputData( (void **)&pData, startSample, sampleCount, forward );
|
||||
if ( !availableSamples )
|
||||
break;
|
||||
outputSampleCount = availableSamples;
|
||||
inputSampleCount = availableSamples;
|
||||
|
||||
}
|
||||
offset += outputSampleCount;
|
||||
sampleCount -= outputSampleCount;
|
||||
if ( forward )
|
||||
{
|
||||
m_sample += inputSampleCount;
|
||||
m_absoluteSample += inputSampleCount;
|
||||
}
|
||||
|
||||
if ( m_loop != 0 && m_sample >= m_loop )
|
||||
{
|
||||
SetSamplePosition( m_startpos );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( sampleCount > 0 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: The device calls this to request data. The mixer must provide the
|
||||
// full amount of samples or have silence in its output stream.
|
||||
// Input : *pDevice - requesting device
|
||||
// sampleCount - number of samples at the output rate
|
||||
// outputRate - sampling rate of the request
|
||||
// Output : Returns true to keep mixing, false to delete this mixer
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAudioMixerWave::MixDataToDevice( IAudioDevice *pDevice, channel_t *pChannel, int startSample, int sampleCount, int outputRate, bool forward /*= true*/ )
|
||||
{
|
||||
int offset = 0;
|
||||
|
||||
int inputSampleRate = (int)(pChannel->pitch * m_pData->Source().SampleRate());
|
||||
float rate = (float)inputSampleRate / outputRate;
|
||||
fixedint fracstep = FIX_FLOAT( rate );
|
||||
|
||||
sampleCount = MIN( sampleCount, pDevice->PaintBufferSampleCount() );
|
||||
|
||||
int startpos = startSample;
|
||||
|
||||
if ( !forward )
|
||||
{
|
||||
int requestedstart = startSample - (int)( sampleCount * rate );
|
||||
if ( requestedstart < 0 )
|
||||
return false;
|
||||
|
||||
startpos = MAX( 0, requestedstart );
|
||||
SetSamplePosition( startpos );
|
||||
}
|
||||
|
||||
while ( sampleCount > 0 )
|
||||
{
|
||||
int availableSamples;
|
||||
int inputSampleCount;
|
||||
char *pData = NULL;
|
||||
int outputSampleCount = sampleCount;
|
||||
|
||||
|
||||
if ( outputRate != inputSampleRate )
|
||||
{
|
||||
inputSampleCount = (int)(sampleCount * rate);
|
||||
|
||||
int availableSamples = GetOutputData( (void **)&pData, startpos, inputSampleCount, forward );
|
||||
if ( !availableSamples )
|
||||
break;
|
||||
|
||||
if ( availableSamples < inputSampleCount )
|
||||
{
|
||||
outputSampleCount = (int)(availableSamples / rate);
|
||||
inputSampleCount = availableSamples;
|
||||
}
|
||||
|
||||
Mix( pDevice, pChannel, pData, offset, m_fracOffset, fracstep, outputSampleCount, 0, forward );
|
||||
|
||||
// compute new fraction part of sample index
|
||||
float offset = (m_fracOffset / FIX_SCALE) + (rate * outputSampleCount);
|
||||
offset = offset - (float)((int)offset);
|
||||
m_fracOffset = FIX_FLOAT(offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
availableSamples = GetOutputData( (void **)&pData, startpos, sampleCount, forward );
|
||||
if ( !availableSamples )
|
||||
break;
|
||||
|
||||
outputSampleCount = availableSamples;
|
||||
inputSampleCount = availableSamples;
|
||||
|
||||
Mix( pDevice, pChannel, pData, offset, m_fracOffset, FIX(1), outputSampleCount, 0, forward );
|
||||
}
|
||||
offset += outputSampleCount;
|
||||
sampleCount -= outputSampleCount;
|
||||
|
||||
if ( forward )
|
||||
{
|
||||
m_sample += inputSampleCount;
|
||||
m_absoluteSample += inputSampleCount;
|
||||
}
|
||||
|
||||
if ( m_loop != 0 && m_sample >= m_loop )
|
||||
{
|
||||
SetSamplePosition( m_startpos );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( sampleCount > 0 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
31
soundsystem/snd_wave_mixer.h
Normal file
31
soundsystem/snd_wave_mixer.h
Normal file
@@ -0,0 +1,31 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef SND_WAVE_MIXER_H
|
||||
#define SND_WAVE_MIXER_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Forward declarations
|
||||
//-----------------------------------------------------------------------------
|
||||
class CWaveData;
|
||||
class CAudioMixer;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Wave mixer
|
||||
//-----------------------------------------------------------------------------
|
||||
CAudioMixer *CreateWaveMixer( CWaveData *data, int format, int channels, int bits );
|
||||
|
||||
|
||||
#endif // SND_WAVE_MIXER_H
|
||||
521
soundsystem/snd_wave_mixer_adpcm.cpp
Normal file
521
soundsystem/snd_wave_mixer_adpcm.cpp
Normal file
@@ -0,0 +1,521 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#pragma warning( disable: 4201 )
|
||||
#include <mmsystem.h>
|
||||
#pragma warning( default: 4201 )
|
||||
|
||||
#include <mmreg.h>
|
||||
#include "snd_wave_source.h"
|
||||
#include "snd_wave_mixer_adpcm.h"
|
||||
#include "snd_wave_mixer_private.h"
|
||||
#include "soundsystem.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
// max size of ADPCM block in bytes
|
||||
#define MAX_BLOCK_SIZE 4096
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Mixer for ADPCM encoded audio
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAudioMixerWaveADPCM : public CAudioMixerWave
|
||||
{
|
||||
public:
|
||||
CAudioMixerWaveADPCM( CWaveData *data );
|
||||
~CAudioMixerWaveADPCM( void );
|
||||
|
||||
virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true );
|
||||
virtual int GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward = true );
|
||||
|
||||
virtual bool SetSamplePosition( int position, bool scrubbing = false );
|
||||
|
||||
private:
|
||||
bool DecodeBlock( void );
|
||||
int NumChannels( void );
|
||||
void DecompressBlockMono( short *pOut, const char *pIn, int count );
|
||||
void DecompressBlockStereo( short *pOut, const char *pIn, int count );
|
||||
|
||||
void SetCurrentBlock( int block );
|
||||
int GetCurrentBlock( void ) const;
|
||||
int GetBlockNumberForSample( int samplePosition );
|
||||
bool IsSampleInCurrentBlock( int samplePosition );
|
||||
int GetFirstSampleForBlock( int blocknum ) const;
|
||||
|
||||
const ADPCMWAVEFORMAT *m_pFormat;
|
||||
const ADPCMCOEFSET *m_pCoefficients;
|
||||
|
||||
short *m_pSamples;
|
||||
int m_sampleCount;
|
||||
int m_samplePosition;
|
||||
|
||||
int m_blockSize;
|
||||
int m_offset;
|
||||
|
||||
int m_currentBlock;
|
||||
};
|
||||
|
||||
|
||||
CAudioMixerWaveADPCM::CAudioMixerWaveADPCM( CWaveData *data ) : CAudioMixerWave( data )
|
||||
{
|
||||
m_currentBlock = -1;
|
||||
m_pSamples = NULL;
|
||||
m_sampleCount = 0;
|
||||
m_samplePosition = 0;
|
||||
m_offset = 0;
|
||||
|
||||
m_pFormat = (const ADPCMWAVEFORMAT *)m_pData->Source().GetHeader();
|
||||
if ( m_pFormat )
|
||||
{
|
||||
m_pCoefficients = (ADPCMCOEFSET *)((char *)m_pFormat + sizeof(WAVEFORMATEX) + 4);
|
||||
|
||||
// create the decode buffer
|
||||
m_pSamples = new short[m_pFormat->wSamplesPerBlock * m_pFormat->wfx.nChannels];
|
||||
|
||||
// number of bytes for samples
|
||||
m_blockSize = ((m_pFormat->wSamplesPerBlock - 2) * m_pFormat->wfx.nChannels ) / 2;
|
||||
// size of channel header
|
||||
m_blockSize += 7 * m_pFormat->wfx.nChannels;
|
||||
// Assert(m_blockSize < MAX_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CAudioMixerWaveADPCM::~CAudioMixerWaveADPCM( void )
|
||||
{
|
||||
delete[] m_pSamples;
|
||||
}
|
||||
|
||||
|
||||
int CAudioMixerWaveADPCM::NumChannels( void )
|
||||
{
|
||||
if ( m_pFormat )
|
||||
{
|
||||
return m_pFormat->wfx.nChannels;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CAudioMixerWaveADPCM::Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward /*= true*/ )
|
||||
{
|
||||
if ( NumChannels() == 1 )
|
||||
pDevice->Mix16Mono( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward );
|
||||
else
|
||||
pDevice->Mix16Stereo( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward );
|
||||
}
|
||||
|
||||
|
||||
static int error_sign_lut[] = { 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1 };
|
||||
static int error_coefficients_lut[] = { 230, 230, 230, 230, 307, 409, 512, 614,
|
||||
768, 614, 512, 409, 307, 230, 230, 230 };
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: ADPCM decompress a single block of 1-channel audio
|
||||
// Input : *pOut - output buffer 16-bit
|
||||
// *pIn - input block
|
||||
// count - number of samples to decode (to support partial blocks)
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAudioMixerWaveADPCM::DecompressBlockMono( short *pOut, const char *pIn, int count )
|
||||
{
|
||||
|
||||
int pred = *pIn++;
|
||||
int co1 = m_pCoefficients[pred].iCoef1;
|
||||
int co2 = m_pCoefficients[pred].iCoef2;
|
||||
|
||||
// read initial delta
|
||||
int delta = *((short *)pIn);
|
||||
pIn += 2;
|
||||
|
||||
// read initial samples for prediction
|
||||
int samp1 = *((short *)pIn);
|
||||
pIn += 2;
|
||||
|
||||
int samp2 = *((short *)pIn);
|
||||
pIn += 2;
|
||||
|
||||
// write out the initial samples (stored in reverse order)
|
||||
*pOut++ = (short)samp2;
|
||||
*pOut++ = (short)samp1;
|
||||
|
||||
// subtract the 2 samples in the header
|
||||
count -= 2;
|
||||
|
||||
// this is a toggle to read nibbles, first nibble is high
|
||||
int high = 1;
|
||||
|
||||
int error = 0, sample = 0;
|
||||
|
||||
// now process the block
|
||||
while ( count )
|
||||
{
|
||||
// read the error nibble from the input stream
|
||||
if ( high )
|
||||
{
|
||||
sample = (unsigned char) (*pIn++);
|
||||
// high nibble
|
||||
error = sample >> 4;
|
||||
// cache low nibble for next read
|
||||
sample = sample & 0xf;
|
||||
// Next read is from cache, not stream
|
||||
high = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// stored in previous read (low nibble)
|
||||
error = sample;
|
||||
// next read is from stream
|
||||
high = 1;
|
||||
}
|
||||
// convert to signed with LUT
|
||||
int errorSign = error_sign_lut[error];
|
||||
|
||||
// interpolate the new sample
|
||||
int predSample = (samp1 * co1) + (samp2 * co2);
|
||||
// coefficients are fixed point 8-bit, so shift back to 16-bit integer
|
||||
predSample >>= 8;
|
||||
|
||||
// Add in current error estimate
|
||||
predSample += (errorSign * delta);
|
||||
|
||||
// Correct error estimate
|
||||
delta = (delta * error_coefficients_lut[error]) >> 8;
|
||||
// Clamp error estimate
|
||||
if ( delta < 16 )
|
||||
delta = 16;
|
||||
|
||||
// clamp
|
||||
if ( predSample > 32767L )
|
||||
predSample = 32767L;
|
||||
else if ( predSample < -32768L )
|
||||
predSample = -32768L;
|
||||
|
||||
// output
|
||||
*pOut++ = (short)predSample;
|
||||
// move samples over
|
||||
samp2 = samp1;
|
||||
samp1 = predSample;
|
||||
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Decode a single block of stereo ADPCM audio
|
||||
// Input : *pOut - 16-bit output buffer
|
||||
// *pIn - ADPCM encoded block data
|
||||
// count - number of sample pairs to decode
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAudioMixerWaveADPCM::DecompressBlockStereo( short *pOut, const char *pIn, int count )
|
||||
{
|
||||
int pred[2], co1[2], co2[2];
|
||||
int i;
|
||||
|
||||
for ( i = 0; i < 2; i++ )
|
||||
{
|
||||
pred[i] = *pIn++;
|
||||
co1[i] = m_pCoefficients[pred[i]].iCoef1;
|
||||
co2[i] = m_pCoefficients[pred[i]].iCoef2;
|
||||
}
|
||||
|
||||
int delta[2], samp1[2], samp2[2];
|
||||
|
||||
for ( i = 0; i < 2; i++, pIn += 2 )
|
||||
{
|
||||
// read initial delta
|
||||
delta[i] = *((short *)pIn);
|
||||
}
|
||||
|
||||
// read initial samples for prediction
|
||||
for ( i = 0; i < 2; i++, pIn += 2 )
|
||||
{
|
||||
samp1[i] = *((short *)pIn);
|
||||
}
|
||||
for ( i = 0; i < 2; i++, pIn += 2 )
|
||||
{
|
||||
samp2[i] = *((short *)pIn);
|
||||
}
|
||||
|
||||
// write out the initial samples (stored in reverse order)
|
||||
*pOut++ = (short)samp2[0]; // left
|
||||
*pOut++ = (short)samp2[1]; // right
|
||||
*pOut++ = (short)samp1[0]; // left
|
||||
*pOut++ = (short)samp1[1]; // right
|
||||
|
||||
// subtract the 2 samples in the header
|
||||
count -= 2;
|
||||
|
||||
// this is a toggle to read nibbles, first nibble is high
|
||||
int high = 1;
|
||||
|
||||
int error, sample = 0;
|
||||
|
||||
// now process the block
|
||||
while ( count )
|
||||
{
|
||||
for ( i = 0; i < 2; i++ )
|
||||
{
|
||||
// read the error nibble from the input stream
|
||||
if ( high )
|
||||
{
|
||||
sample = (unsigned char) (*pIn++);
|
||||
// high nibble
|
||||
error = sample >> 4;
|
||||
// cache low nibble for next read
|
||||
sample = sample & 0xf;
|
||||
// Next read is from cache, not stream
|
||||
high = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// stored in previous read (low nibble)
|
||||
error = sample;
|
||||
// next read is from stream
|
||||
high = 1;
|
||||
}
|
||||
// convert to signed with LUT
|
||||
int errorSign = error_sign_lut[error];
|
||||
|
||||
// interpolate the new sample
|
||||
int predSample = (samp1[i] * co1[i]) + (samp2[i] * co2[i]);
|
||||
// coefficients are fixed point 8-bit, so shift back to 16-bit integer
|
||||
predSample >>= 8;
|
||||
|
||||
// Add in current error estimate
|
||||
predSample += (errorSign * delta[i]);
|
||||
|
||||
// Correct error estimate
|
||||
delta[i] = (delta[i] * error_coefficients_lut[error]) >> 8;
|
||||
// Clamp error estimate
|
||||
if ( delta[i] < 16 )
|
||||
delta[i] = 16;
|
||||
|
||||
// clamp
|
||||
if ( predSample > 32767L )
|
||||
predSample = 32767L;
|
||||
else if ( predSample < -32768L )
|
||||
predSample = -32768L;
|
||||
|
||||
// output
|
||||
*pOut++ = (short)predSample;
|
||||
// move samples over
|
||||
samp2[i] = samp1[i];
|
||||
samp1[i] = predSample;
|
||||
}
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CAudioMixerWaveADPCM::DecodeBlock( void )
|
||||
{
|
||||
char tmpBlock[MAX_BLOCK_SIZE];
|
||||
char *pData;
|
||||
|
||||
int available = m_pData->ReadSourceData( (void **) (&pData), m_offset, m_blockSize );
|
||||
if ( available < m_blockSize )
|
||||
{
|
||||
int total = 0;
|
||||
while ( available && total < m_blockSize )
|
||||
{
|
||||
memcpy( tmpBlock + total, pData, available );
|
||||
total += available;
|
||||
available = m_pData->ReadSourceData( (void **) (&pData), m_offset + total, m_blockSize - total );
|
||||
}
|
||||
pData = tmpBlock;
|
||||
available = total;
|
||||
}
|
||||
|
||||
Assert( m_blockSize > 0 );
|
||||
|
||||
// Current block number is based on starting offset
|
||||
int blockNumber = m_offset / m_blockSize;
|
||||
SetCurrentBlock( blockNumber );
|
||||
|
||||
if ( !available )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// advance the file pointer
|
||||
m_offset += available;
|
||||
|
||||
int channelCount = NumChannels();
|
||||
|
||||
// this is sample pairs for stereo, samples for mono
|
||||
m_sampleCount = m_pFormat->wSamplesPerBlock;
|
||||
|
||||
// short block?, fixup sample count (2 samples per byte, divided by number of channels per sample set)
|
||||
m_sampleCount -= ((m_blockSize - available) * 2) / channelCount;
|
||||
|
||||
// new block, start at the first sample
|
||||
m_samplePosition = 0;
|
||||
|
||||
// no need to subclass for different channel counts...
|
||||
if ( channelCount == 1 )
|
||||
{
|
||||
DecompressBlockMono( m_pSamples, pData, m_sampleCount );
|
||||
}
|
||||
else
|
||||
{
|
||||
DecompressBlockStereo( m_pSamples, pData, m_sampleCount );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : block -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAudioMixerWaveADPCM::SetCurrentBlock( int block )
|
||||
{
|
||||
m_currentBlock = block;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : int
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAudioMixerWaveADPCM::GetCurrentBlock( void ) const
|
||||
{
|
||||
return m_currentBlock;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : samplePosition -
|
||||
// Output : int
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAudioMixerWaveADPCM::GetBlockNumberForSample( int samplePosition )
|
||||
{
|
||||
int blockNum = samplePosition / m_pFormat->wSamplesPerBlock;
|
||||
return blockNum;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : samplePosition -
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAudioMixerWaveADPCM::IsSampleInCurrentBlock( int samplePosition )
|
||||
{
|
||||
int currentBlock = GetCurrentBlock();
|
||||
|
||||
int startSample = currentBlock * m_pFormat->wSamplesPerBlock;
|
||||
int endSample = startSample + m_pFormat->wSamplesPerBlock - 1;
|
||||
|
||||
if ( samplePosition >= startSample &&
|
||||
samplePosition <= endSample )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : blocknum -
|
||||
// Output : int
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAudioMixerWaveADPCM::GetFirstSampleForBlock( int blocknum ) const
|
||||
{
|
||||
return m_pFormat->wSamplesPerBlock * blocknum;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Read existing buffer or decompress a new block when necessary
|
||||
// Input : **pData - output data pointer
|
||||
// sampleCount - number of samples (or pairs)
|
||||
// Output : int - available samples (zero to stop decoding)
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAudioMixerWaveADPCM::GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward /*= true*/ )
|
||||
{
|
||||
int requestedBlock = GetBlockNumberForSample( samplePosition );
|
||||
if ( requestedBlock != GetCurrentBlock() )
|
||||
{
|
||||
// Ran out of data!!!
|
||||
if ( !SetSamplePosition( samplePosition ) )
|
||||
return 0;
|
||||
}
|
||||
|
||||
Assert( requestedBlock == GetCurrentBlock() );
|
||||
|
||||
if ( m_samplePosition >= m_sampleCount )
|
||||
{
|
||||
if ( !DecodeBlock() )
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( m_samplePosition < m_sampleCount )
|
||||
{
|
||||
*pData = (void *)(m_pSamples + m_samplePosition * NumChannels());
|
||||
int available = m_sampleCount - m_samplePosition;
|
||||
if ( available > sampleCount )
|
||||
available = sampleCount;
|
||||
|
||||
m_samplePosition += available;
|
||||
return available;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : position -
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAudioMixerWaveADPCM::SetSamplePosition( int position, bool scrubbing )
|
||||
{
|
||||
position = max( 0, position );
|
||||
|
||||
CAudioMixerWave::SetSamplePosition( position, scrubbing );
|
||||
|
||||
int requestedBlock = GetBlockNumberForSample( position );
|
||||
int firstSample = GetFirstSampleForBlock( requestedBlock );
|
||||
|
||||
if ( firstSample >= m_pData->Source().SampleCount() )
|
||||
{
|
||||
// Read past end of file!!!
|
||||
return false;
|
||||
}
|
||||
|
||||
int currentSample = ( position - firstSample );
|
||||
|
||||
if ( requestedBlock != GetCurrentBlock() )
|
||||
{
|
||||
// Rewind file to beginning of block
|
||||
m_offset = requestedBlock * m_blockSize;
|
||||
if ( !DecodeBlock() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_samplePosition = currentSample;
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Abstract factory function for ADPCM mixers
|
||||
// Input : *data - wave data access object
|
||||
// channels -
|
||||
// Output : CAudioMixer
|
||||
//-----------------------------------------------------------------------------
|
||||
CAudioMixer *CreateADPCMMixer( CWaveData *data )
|
||||
{
|
||||
return new CAudioMixerWaveADPCM( data );
|
||||
}
|
||||
24
soundsystem/snd_wave_mixer_adpcm.h
Normal file
24
soundsystem/snd_wave_mixer_adpcm.h
Normal file
@@ -0,0 +1,24 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef SND_WAVE_MIXER_ADPCM_H
|
||||
#define SND_WAVE_MIXER_ADPCM_H
|
||||
#pragma once
|
||||
|
||||
|
||||
class CAudioMixer;
|
||||
class CWaveData;
|
||||
|
||||
CAudioMixer *CreateADPCMMixer( CWaveData *data );
|
||||
|
||||
#endif // SND_WAVE_MIXER_ADPCM_H
|
||||
103
soundsystem/snd_wave_mixer_private.h
Normal file
103
soundsystem/snd_wave_mixer_private.h
Normal file
@@ -0,0 +1,103 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef SND_WAVE_MIXER_PRIVATE_H
|
||||
#define SND_WAVE_MIXER_PRIVATE_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "soundsystem/snd_audio_source.h"
|
||||
#include "soundsystem/snd_device.h"
|
||||
#include "snd_wave_mixer.h"
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Linear iterator over source data.
|
||||
// Keeps track of position in source, and maintains necessary buffers
|
||||
//-----------------------------------------------------------------------------
|
||||
class CWaveData
|
||||
{
|
||||
public:
|
||||
virtual ~CWaveData( void ) {}
|
||||
virtual CAudioSourceWave &Source( void ) = 0;
|
||||
virtual int ReadSourceData( void **pData, int sampleIndex, int sampleCount, bool forward = true ) = 0;
|
||||
};
|
||||
|
||||
class CAudioMixerWave : public CAudioMixer
|
||||
{
|
||||
public:
|
||||
CAudioMixerWave( CWaveData *data );
|
||||
virtual ~CAudioMixerWave( void );
|
||||
|
||||
virtual bool MixDataToDevice( IAudioDevice *pDevice, channel_t *pChannel, int startSample, int sampleCount, int outputRate, bool forward = true );
|
||||
virtual void IncrementSamples( channel_t *pChannel, int startSample, int sampleCount,int outputRate, bool forward = true );
|
||||
virtual bool SkipSamples( IAudioDevice *pDevice, channel_t *pChannel, int startSample, int sampleCount,int outputRate, bool forward = true );
|
||||
virtual void Mix( IAudioDevice *pDevice,
|
||||
channel_t *pChannel,
|
||||
void *pData,
|
||||
int outputOffset,
|
||||
int inputOffset,
|
||||
fixedint fracRate,
|
||||
int outCount,
|
||||
int timecompress,
|
||||
bool forward = true ) = 0;
|
||||
|
||||
virtual int GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward = true );
|
||||
|
||||
virtual CAudioSource *GetSource( void );
|
||||
|
||||
virtual int GetSamplePosition( void );
|
||||
virtual int GetScubPosition( void );
|
||||
|
||||
virtual bool SetSamplePosition( int position, bool scrubbing = false );
|
||||
virtual void SetLoopPosition( int position );
|
||||
virtual int GetStartPosition( void );
|
||||
|
||||
virtual bool GetActive( void );
|
||||
virtual void SetActive( bool active );
|
||||
|
||||
virtual void SetModelIndex( int index );
|
||||
virtual int GetModelIndex( void ) const;
|
||||
|
||||
virtual void SetDirection( bool forward );
|
||||
virtual bool GetDirection( void ) const;
|
||||
|
||||
virtual void SetAutoDelete( bool autodelete );
|
||||
virtual bool GetAutoDelete( void ) const;
|
||||
|
||||
virtual void SetVolume( float volume );
|
||||
virtual channel_t *GetChannel();
|
||||
|
||||
protected:
|
||||
int m_sample;
|
||||
int m_absoluteSample;
|
||||
int m_scubSample;
|
||||
int m_startpos;
|
||||
int m_loop;
|
||||
int m_fracOffset;
|
||||
CWaveData *m_pData;
|
||||
|
||||
int m_absoluteStartPos;
|
||||
|
||||
bool m_bActive;
|
||||
// Associated playback model in faceposer
|
||||
int m_nModelIndex;
|
||||
|
||||
bool m_bForward;
|
||||
|
||||
bool m_bAutoDelete;
|
||||
|
||||
channel_t *m_pChannel;
|
||||
};
|
||||
|
||||
|
||||
#endif // SND_WAVE_MIXER_PRIVATE_H
|
||||
606
soundsystem/snd_wave_source.cpp
Normal file
606
soundsystem/snd_wave_source.cpp
Normal file
@@ -0,0 +1,606 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
#include "tier2/riff.h"
|
||||
#include "snd_wave_source.h"
|
||||
#include "snd_wave_mixer_private.h"
|
||||
#include "soundsystem/snd_audio_source.h"
|
||||
#include <mmsystem.h> // wave format
|
||||
#include <mmreg.h> // adpcm format
|
||||
#include "soundsystem.h"
|
||||
#include "FileSystem.h"
|
||||
#include "tier1/utlbuffer.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Implements the RIFF i/o interface on stdio
|
||||
//-----------------------------------------------------------------------------
|
||||
class StdIOReadBinary : public IFileReadBinary
|
||||
{
|
||||
public:
|
||||
FileHandle_t open( const char *pFileName )
|
||||
{
|
||||
return g_pFullFileSystem->Open( pFileName, "rb", "GAME" );
|
||||
}
|
||||
|
||||
int read( void *pOutput, int size, FileHandle_t file )
|
||||
{
|
||||
if ( !file )
|
||||
return 0;
|
||||
|
||||
return g_pFullFileSystem->Read( pOutput, size, (FileHandle_t)file );
|
||||
}
|
||||
|
||||
void seek( FileHandle_t file, int pos )
|
||||
{
|
||||
if ( !file )
|
||||
return;
|
||||
|
||||
g_pFullFileSystem->Seek( file, pos, FILESYSTEM_SEEK_HEAD );
|
||||
}
|
||||
|
||||
unsigned int tell( FileHandle_t file )
|
||||
{
|
||||
if ( !file )
|
||||
return 0;
|
||||
|
||||
return g_pFullFileSystem->Tell( (FileHandle_t)file );
|
||||
}
|
||||
|
||||
unsigned int size( FileHandle_t file )
|
||||
{
|
||||
if ( !file )
|
||||
return 0;
|
||||
|
||||
return g_pFullFileSystem->Size( (FileHandle_t)file );
|
||||
}
|
||||
|
||||
void close( FileHandle_t file )
|
||||
{
|
||||
if ( !file )
|
||||
return;
|
||||
|
||||
g_pFullFileSystem->Close( (FileHandle_t)file );
|
||||
}
|
||||
};
|
||||
|
||||
static StdIOReadBinary io;
|
||||
|
||||
#define RIFF_WAVE MAKEID('W','A','V','E')
|
||||
#define WAVE_FMT MAKEID('f','m','t',' ')
|
||||
#define WAVE_DATA MAKEID('d','a','t','a')
|
||||
#define WAVE_FACT MAKEID('f','a','c','t')
|
||||
#define WAVE_CUE MAKEID('c','u','e',' ')
|
||||
|
||||
void ChunkError( unsigned int id )
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Init to empty wave
|
||||
//-----------------------------------------------------------------------------
|
||||
CAudioSourceWave::CAudioSourceWave( void )
|
||||
{
|
||||
m_bits = 0;
|
||||
m_rate = 0;
|
||||
m_channels = 0;
|
||||
m_format = 0;
|
||||
m_pHeader = NULL;
|
||||
// no looping
|
||||
m_loopStart = -1;
|
||||
m_sampleSize = 1;
|
||||
m_sampleCount = 0;
|
||||
}
|
||||
|
||||
|
||||
CAudioSourceWave::~CAudioSourceWave( void )
|
||||
{
|
||||
// for non-standard waves, we store a copy of the header in RAM
|
||||
delete[] m_pHeader;
|
||||
// m_pWords points into m_pWordBuffer, no need to delete
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Init the wave data.
|
||||
// Input : *pHeaderBuffer - the RIFF fmt chunk
|
||||
// headerSize - size of that chunk
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAudioSourceWave::Init( const char *pHeaderBuffer, int headerSize )
|
||||
{
|
||||
const WAVEFORMATEX *pHeader = (const WAVEFORMATEX *)pHeaderBuffer;
|
||||
|
||||
// copy the relevant header data
|
||||
m_format = pHeader->wFormatTag;
|
||||
m_bits = pHeader->wBitsPerSample;
|
||||
m_rate = pHeader->nSamplesPerSec;
|
||||
m_channels = pHeader->nChannels;
|
||||
|
||||
m_sampleSize = (m_bits * m_channels) / 8;
|
||||
|
||||
// this can never be zero -- other functions divide by this.
|
||||
// This should never happen, but avoid crashing
|
||||
if ( m_sampleSize <= 0 )
|
||||
m_sampleSize = 1;
|
||||
|
||||
// For non-standard waves (like ADPCM) store the header, it has some useful data
|
||||
if ( m_format != WAVE_FORMAT_PCM )
|
||||
{
|
||||
m_pHeader = new char[headerSize];
|
||||
memcpy( m_pHeader, pHeader, headerSize );
|
||||
if ( m_format == WAVE_FORMAT_ADPCM )
|
||||
{
|
||||
// treat ADPCM sources as a file of bytes. They are decoded by the mixer
|
||||
m_sampleSize = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : float
|
||||
//-----------------------------------------------------------------------------
|
||||
float CAudioSourceWave::TrueSampleSize( void )
|
||||
{
|
||||
if ( m_format == WAVE_FORMAT_ADPCM )
|
||||
{
|
||||
return 0.5f;
|
||||
}
|
||||
return (float)m_sampleSize;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Total number of samples in this source
|
||||
// Output : int
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAudioSourceWave::SampleCount( void )
|
||||
{
|
||||
if ( m_format == WAVE_FORMAT_ADPCM )
|
||||
{
|
||||
ADPCMWAVEFORMAT *pFormat = (ADPCMWAVEFORMAT *)m_pHeader;
|
||||
int blockSize = ((pFormat->wSamplesPerBlock - 2) * pFormat->wfx.nChannels ) / 2;
|
||||
blockSize += 7 * pFormat->wfx.nChannels;
|
||||
|
||||
int blockCount = m_sampleCount / blockSize;
|
||||
int blockRem = m_sampleCount % blockSize;
|
||||
|
||||
// total samples in complete blocks
|
||||
int sampleCount = blockCount * pFormat->wSamplesPerBlock;
|
||||
|
||||
// add remaining in a short block
|
||||
if ( blockRem )
|
||||
{
|
||||
sampleCount += pFormat->wSamplesPerBlock - (((blockSize - blockRem) * 2) / m_channels);
|
||||
}
|
||||
return sampleCount;
|
||||
}
|
||||
return m_sampleCount;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Do any sample conversion
|
||||
// For 8 bit PCM, convert to signed because the mixing routine assumes this
|
||||
// Input : *pData - pointer to sample data
|
||||
// sampleCount - number of samples
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAudioSourceWave::ConvertSamples( char *pData, int sampleCount )
|
||||
{
|
||||
if ( m_format == WAVE_FORMAT_PCM )
|
||||
{
|
||||
if ( m_bits == 8 )
|
||||
{
|
||||
for ( int i = 0; i < sampleCount; i++ )
|
||||
{
|
||||
for ( int j = 0; j < m_channels; j++ )
|
||||
{
|
||||
*pData = (unsigned char)((int)((unsigned)*pData) - 128);
|
||||
pData++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &walk -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAudioSourceWave::ParseSentence( IterateRIFF &walk )
|
||||
{
|
||||
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
|
||||
|
||||
buf.EnsureCapacity( walk.ChunkSize() );
|
||||
walk.ChunkRead( buf.Base() );
|
||||
buf.SeekPut( CUtlBuffer::SEEK_HEAD, walk.ChunkSize() );
|
||||
|
||||
m_Sentence.InitFromDataChunk( buf.Base(), buf.TellPut() );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Parse base chunks
|
||||
// Input : &walk - riff file to parse
|
||||
// : chunkName - name of the chunk to parse
|
||||
//-----------------------------------------------------------------------------
|
||||
// UNDONE: Move parsing loop here and drop each chunk into a virtual function
|
||||
// instead of this being virtual.
|
||||
void CAudioSourceWave::ParseChunk( IterateRIFF &walk, int chunkName )
|
||||
{
|
||||
switch( chunkName )
|
||||
{
|
||||
case WAVE_CUE:
|
||||
{
|
||||
m_loopStart = ParseCueChunk( walk );
|
||||
}
|
||||
break;
|
||||
case WAVE_VALVEDATA:
|
||||
{
|
||||
ParseSentence( walk );
|
||||
}
|
||||
break;
|
||||
// unknown/don't care
|
||||
default:
|
||||
{
|
||||
ChunkError( walk.ChunkName() );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : CSentence
|
||||
//-----------------------------------------------------------------------------
|
||||
CSentence *CAudioSourceWave::GetSentence( void )
|
||||
{
|
||||
return &m_Sentence;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Bastardized construction routine. This is just to avoid complex
|
||||
// constructor functions so code can be shared more easily by sub-classes
|
||||
// Input : *pFormatBuffer - RIFF header
|
||||
// formatSize - header size
|
||||
// &walk - RIFF file
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAudioSourceWave::Setup( const char *pFormatBuffer, int formatSize, IterateRIFF &walk )
|
||||
{
|
||||
Init( pFormatBuffer, formatSize );
|
||||
|
||||
while ( walk.ChunkAvailable() )
|
||||
{
|
||||
ParseChunk( walk, walk.ChunkName() );
|
||||
walk.ChunkNext();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Wave file that is completely in memory
|
||||
// UNDONE: Implement Lock/Unlock and caching
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAudioSourceMemWave : public CAudioSourceWave
|
||||
{
|
||||
public:
|
||||
CAudioSourceMemWave( void );
|
||||
~CAudioSourceMemWave( void );
|
||||
|
||||
// Create an instance (mixer) of this audio source
|
||||
virtual CAudioMixer *CreateMixer( void );
|
||||
|
||||
virtual void ParseChunk( IterateRIFF &walk, int chunkName );
|
||||
void ParseDataChunk( IterateRIFF &walk );
|
||||
|
||||
virtual int GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward = true );
|
||||
virtual float GetRunningLength( void ) { return CAudioSourceWave::GetRunningLength(); };
|
||||
|
||||
virtual int GetNumChannels();
|
||||
|
||||
private:
|
||||
char *m_pData; // wave data
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Iterator for wave data (this is to abstract streaming/buffering)
|
||||
//-----------------------------------------------------------------------------
|
||||
class CWaveDataMemory : public CWaveData
|
||||
{
|
||||
public:
|
||||
CWaveDataMemory( CAudioSourceWave &source ) : m_source(source) {}
|
||||
~CWaveDataMemory( void ) {}
|
||||
CAudioSourceWave &Source( void ) { return m_source; }
|
||||
|
||||
// this file is in memory, simply pass along the data request to the source
|
||||
virtual int ReadSourceData( void **pData, int sampleIndex, int sampleCount, bool forward /*= true*/ )
|
||||
{
|
||||
return m_source.GetOutputData( pData, sampleIndex, sampleCount, forward );
|
||||
}
|
||||
private:
|
||||
CAudioSourceWave &m_source; // pointer to source
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: NULL the wave data pointer (we haven't loaded yet)
|
||||
//-----------------------------------------------------------------------------
|
||||
CAudioSourceMemWave::CAudioSourceMemWave( void )
|
||||
{
|
||||
m_pData = NULL;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Free any wave data we've allocated
|
||||
//-----------------------------------------------------------------------------
|
||||
CAudioSourceMemWave::~CAudioSourceMemWave( void )
|
||||
{
|
||||
delete[] m_pData;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Creates a mixer and initializes it with an appropriate mixer
|
||||
//-----------------------------------------------------------------------------
|
||||
CAudioMixer *CAudioSourceMemWave::CreateMixer( void )
|
||||
{
|
||||
return CreateWaveMixer( new CWaveDataMemory(*this), m_format, m_channels, m_bits );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: parse chunks with unique processing to in-memory waves
|
||||
// Input : &walk - RIFF file
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAudioSourceMemWave::ParseChunk( IterateRIFF &walk, int chunkName )
|
||||
{
|
||||
switch( chunkName )
|
||||
{
|
||||
// this is the audio data
|
||||
case WAVE_DATA:
|
||||
{
|
||||
ParseDataChunk( walk );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
CAudioSourceWave::ParseChunk( walk, chunkName );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: reads the actual sample data and parses it
|
||||
// Input : &walk - RIFF file
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAudioSourceMemWave::ParseDataChunk( IterateRIFF &walk )
|
||||
{
|
||||
int size = walk.ChunkSize();
|
||||
|
||||
// create a buffer for the samples
|
||||
m_pData = new char[size];
|
||||
|
||||
// load them into memory
|
||||
walk.ChunkRead( m_pData );
|
||||
|
||||
if ( m_format == WAVE_FORMAT_PCM )
|
||||
{
|
||||
// number of samples loaded
|
||||
m_sampleCount = size / m_sampleSize;
|
||||
|
||||
// some samples need to be converted
|
||||
ConvertSamples( m_pData, m_sampleCount );
|
||||
}
|
||||
else if ( m_format == WAVE_FORMAT_ADPCM )
|
||||
{
|
||||
// The ADPCM mixers treat the wave source as a flat file of bytes.
|
||||
m_sampleSize = 1;
|
||||
// Since each "sample" is a byte (this is a flat file), the number of samples is the file size
|
||||
m_sampleCount = size;
|
||||
|
||||
// file says 4, output is 16
|
||||
m_bits = 16;
|
||||
}
|
||||
}
|
||||
|
||||
int CAudioSourceMemWave::GetNumChannels()
|
||||
{
|
||||
return m_channels;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: parses loop information from a cue chunk
|
||||
// Input : &walk - RIFF iterator
|
||||
// Output : int loop start position
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAudioSourceWave::ParseCueChunk( IterateRIFF &walk )
|
||||
{
|
||||
// Cue chunk as specified by RIFF format
|
||||
// see $/research/jay/sound/riffnew.htm
|
||||
struct
|
||||
{
|
||||
unsigned int dwName;
|
||||
unsigned int dwPosition;
|
||||
unsigned int fccChunk;
|
||||
unsigned int dwChunkStart;
|
||||
unsigned int dwBlockStart;
|
||||
unsigned int dwSampleOffset;
|
||||
} cue_chunk;
|
||||
|
||||
int cueCount;
|
||||
|
||||
// assume that the cue chunk stored in the wave is the start of the loop
|
||||
// assume only one cue chunk, UNDONE: Test this assumption here?
|
||||
cueCount = walk.ChunkReadInt();
|
||||
|
||||
walk.ChunkReadPartial( &cue_chunk, sizeof(cue_chunk) );
|
||||
return cue_chunk.dwSampleOffset;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: get the wave header
|
||||
//-----------------------------------------------------------------------------
|
||||
void *CAudioSourceWave::GetHeader( void )
|
||||
{
|
||||
return m_pHeader;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: wrap the position wrt looping
|
||||
// Input : samplePosition - absolute position
|
||||
// Output : int - looped position
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAudioSourceWave::ConvertLoopedPosition( int samplePosition )
|
||||
{
|
||||
// if the wave is looping and we're past the end of the sample
|
||||
// convert to a position within the loop
|
||||
// At the end of the loop, we return a short buffer, and subsequent call
|
||||
// will loop back and get the rest of the buffer
|
||||
if ( m_loopStart >= 0 )
|
||||
{
|
||||
if ( samplePosition >= m_sampleCount )
|
||||
{
|
||||
// size of loop
|
||||
int loopSize = m_sampleCount - m_loopStart;
|
||||
// subtract off starting bit of the wave
|
||||
samplePosition -= m_loopStart;
|
||||
|
||||
if ( loopSize )
|
||||
{
|
||||
// "real" position in memory (mod off extra loops)
|
||||
samplePosition = m_loopStart + (samplePosition % loopSize);
|
||||
}
|
||||
// ERROR? if no loopSize
|
||||
}
|
||||
}
|
||||
|
||||
return samplePosition;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : **pData - output pointer to samples
|
||||
// samplePosition - position (in samples not bytes)
|
||||
// sampleCount - number of samples (not bytes)
|
||||
// Output : int - number of samples available
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAudioSourceMemWave::GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward /*= true*/ )
|
||||
{
|
||||
// handle position looping
|
||||
samplePosition = ConvertLoopedPosition( samplePosition );
|
||||
|
||||
// how many samples are available (linearly not counting looping)
|
||||
int availableSampleCount = m_sampleCount - samplePosition;
|
||||
if ( !forward )
|
||||
{
|
||||
if ( samplePosition >= m_sampleCount )
|
||||
{
|
||||
availableSampleCount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
availableSampleCount = samplePosition;
|
||||
}
|
||||
}
|
||||
|
||||
// may be asking for a sample out of range, clip at zero
|
||||
if ( availableSampleCount < 0 )
|
||||
availableSampleCount = 0;
|
||||
|
||||
// clip max output samples to max available
|
||||
if ( sampleCount > availableSampleCount )
|
||||
sampleCount = availableSampleCount;
|
||||
|
||||
// byte offset in sample database
|
||||
samplePosition *= m_sampleSize;
|
||||
|
||||
// if we are returning some samples, store the pointer
|
||||
if ( sampleCount )
|
||||
{
|
||||
*pData = m_pData + samplePosition;
|
||||
}
|
||||
|
||||
return sampleCount;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Create a wave audio source (streaming or in memory)
|
||||
// Input : *pName - file name
|
||||
// streaming - if true, don't load, stream each instance
|
||||
// Output : CAudioSource * - a new source
|
||||
//-----------------------------------------------------------------------------
|
||||
// UNDONE : Pool these and check for duplicates?
|
||||
CAudioSource *CreateWave( const char *pName )
|
||||
{
|
||||
char formatBuffer[1024];
|
||||
InFileRIFF riff( pName, io );
|
||||
|
||||
if ( riff.RIFFName() != RIFF_WAVE )
|
||||
{
|
||||
Warning("Bad RIFF file type %s\n", pName );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// set up the iterator for the whole file (root RIFF is a chunk)
|
||||
IterateRIFF walk( riff, riff.RIFFSize() );
|
||||
|
||||
int format = 0;
|
||||
int formatSize = 0;
|
||||
|
||||
// This chunk must be first as it contains the wave's format
|
||||
// break out when we've parsed it
|
||||
while ( walk.ChunkAvailable() && format == 0 )
|
||||
{
|
||||
switch( walk.ChunkName() )
|
||||
{
|
||||
case WAVE_FMT:
|
||||
{
|
||||
if ( walk.ChunkSize() <= 1024 )
|
||||
{
|
||||
walk.ChunkRead( formatBuffer );
|
||||
formatSize = walk.ChunkSize();
|
||||
format = ((WAVEFORMATEX *)formatBuffer)->wFormatTag;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
ChunkError( walk.ChunkName() );
|
||||
}
|
||||
break;
|
||||
}
|
||||
walk.ChunkNext();
|
||||
}
|
||||
|
||||
// Not really a WAVE file or no format chunk, bail
|
||||
if ( !format )
|
||||
return NULL;
|
||||
|
||||
CAudioSourceWave *pWave;
|
||||
|
||||
// create the source from this file
|
||||
pWave = new CAudioSourceMemWave();
|
||||
|
||||
// init the wave source
|
||||
pWave->Setup( formatBuffer, formatSize, walk );
|
||||
|
||||
return pWave;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Wrapper for CreateWave()
|
||||
//-----------------------------------------------------------------------------
|
||||
CAudioSource *Audio_CreateMemoryWave( const char *pName )
|
||||
{
|
||||
return CreateWave( pName );
|
||||
}
|
||||
76
soundsystem/snd_wave_source.h
Normal file
76
soundsystem/snd_wave_source.h
Normal file
@@ -0,0 +1,76 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef SND_WAVE_SOURCE_H
|
||||
#define SND_WAVE_SOURCE_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "soundsystem/snd_audio_source.h"
|
||||
#include "sentence.h"
|
||||
|
||||
class IterateRIFF;
|
||||
|
||||
class CAudioSourceWave : public CAudioSource
|
||||
{
|
||||
public:
|
||||
CAudioSourceWave( void );
|
||||
~CAudioSourceWave( void );
|
||||
|
||||
void Setup( const char *pFormat, int formatSize, IterateRIFF &walk );
|
||||
|
||||
virtual int SampleRate( void ) { return m_rate; }
|
||||
inline int SampleSize( void ) { return m_sampleSize; }
|
||||
virtual float TrueSampleSize( void );
|
||||
|
||||
void *GetHeader( void );
|
||||
|
||||
// Legacy
|
||||
virtual void ParseChunk( IterateRIFF &walk, int chunkName );
|
||||
|
||||
virtual void ParseSentence( IterateRIFF &walk );
|
||||
|
||||
void ConvertSamples( char *pData, int sampleCount );
|
||||
bool IsLooped( void ) { return (m_loopStart >= 0) ? true : false; }
|
||||
bool IsStreaming( void ) { return false; }
|
||||
int ConvertLoopedPosition( int samplePosition );
|
||||
|
||||
int SampleCount( void );
|
||||
|
||||
virtual float GetRunningLength( void )
|
||||
{
|
||||
if ( m_rate > 0.0 )
|
||||
{
|
||||
return (float)SampleCount() / m_rate;
|
||||
}
|
||||
return 0.0f; }
|
||||
|
||||
CSentence *GetSentence( void );
|
||||
|
||||
protected:
|
||||
// returns the loop start from a cue chunk
|
||||
int ParseCueChunk( IterateRIFF &walk );
|
||||
void Init( const char *pHeaderBuffer, int headerSize );
|
||||
|
||||
int m_bits;
|
||||
int m_rate;
|
||||
int m_channels;
|
||||
int m_format;
|
||||
int m_sampleSize;
|
||||
int m_loopStart;
|
||||
int m_sampleCount;
|
||||
|
||||
private:
|
||||
char *m_pHeader;
|
||||
CSentence m_Sentence;
|
||||
};
|
||||
|
||||
#endif // SND_WAVE_SOURCE_H
|
||||
306
soundsystem/soundsystem.cpp
Normal file
306
soundsystem/soundsystem.cpp
Normal file
@@ -0,0 +1,306 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose: DLL interface for low-level sound utilities
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "soundsystem/isoundsystem.h"
|
||||
#include "filesystem.h"
|
||||
#include "tier1/strtools.h"
|
||||
#include "tier1/convar.h"
|
||||
#include "mathlib/mathlib.h"
|
||||
#include "soundsystem/snd_device.h"
|
||||
#include "datacache/idatacache.h"
|
||||
#include "soundchars.h"
|
||||
#include "tier1/utldict.h"
|
||||
#include "snd_wave_source.h"
|
||||
#include "snd_dev_wave.h"
|
||||
#include "tier2/tier2.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// External interfaces
|
||||
//-----------------------------------------------------------------------------
|
||||
IAudioDevice *g_pAudioDevice = NULL;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Globals
|
||||
//-----------------------------------------------------------------------------
|
||||
int g_nSoundFrameCount = 0;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: DLL interface for low-level sound utilities
|
||||
//-----------------------------------------------------------------------------
|
||||
class CSoundSystem : public CTier2AppSystem< ISoundSystem >
|
||||
{
|
||||
typedef CTier2AppSystem< ISoundSystem > BaseClass;
|
||||
|
||||
public:
|
||||
// Inherited from IAppSystem
|
||||
virtual bool Connect( CreateInterfaceFn factory );
|
||||
virtual void Disconnect();
|
||||
virtual void *QueryInterface( const char *pInterfaceName );
|
||||
virtual InitReturnVal_t Init();
|
||||
virtual void Shutdown();
|
||||
|
||||
void Update( float dt );
|
||||
void Flush( void );
|
||||
|
||||
CAudioSource *FindOrAddSound( const char *filename );
|
||||
CAudioSource *LoadSound( const char *wavfile );
|
||||
void PlaySound( CAudioSource *source, float volume, CAudioMixer **ppMixer );
|
||||
|
||||
bool IsSoundPlaying( CAudioMixer *pMixer );
|
||||
CAudioMixer *FindMixer( CAudioSource *source );
|
||||
|
||||
void StopAll( void );
|
||||
void StopSound( CAudioMixer *mixer );
|
||||
|
||||
void GetAudioDevices(CUtlVector< audio_device_description_t >& deviceListOut) const;
|
||||
|
||||
|
||||
private:
|
||||
struct CSoundFile
|
||||
{
|
||||
char filename[ 512 ];
|
||||
CAudioSource *source;
|
||||
long filetime;
|
||||
};
|
||||
|
||||
IAudioDevice *m_pAudioDevice;
|
||||
float m_flElapsedTime;
|
||||
CUtlVector < CSoundFile > m_ActiveSounds;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Singleton interface
|
||||
//-----------------------------------------------------------------------------
|
||||
static CSoundSystem s_SoundSystem;
|
||||
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CSoundSystem, ISoundSystem, SOUNDSYSTEM_INTERFACE_VERSION, s_SoundSystem );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Connect, disconnect
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CSoundSystem::Connect( CreateInterfaceFn factory )
|
||||
{
|
||||
if ( !BaseClass::Connect( factory ) )
|
||||
return false;
|
||||
|
||||
g_pDataCache = (IDataCache*)factory( DATACACHE_INTERFACE_VERSION, NULL );
|
||||
g_pSoundSystem = this;
|
||||
return (g_pFullFileSystem != NULL) && (g_pDataCache != NULL);
|
||||
}
|
||||
|
||||
void CSoundSystem::Disconnect()
|
||||
{
|
||||
g_pSoundSystem = NULL;
|
||||
g_pDataCache = NULL;
|
||||
BaseClass::Disconnect();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Query interface
|
||||
//-----------------------------------------------------------------------------
|
||||
void *CSoundSystem::QueryInterface( const char *pInterfaceName )
|
||||
{
|
||||
if (!Q_strncmp( pInterfaceName, SOUNDSYSTEM_INTERFACE_VERSION, Q_strlen(SOUNDSYSTEM_INTERFACE_VERSION) + 1))
|
||||
return (ISoundSystem*)this;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Init, shutdown
|
||||
//-----------------------------------------------------------------------------
|
||||
InitReturnVal_t CSoundSystem::Init()
|
||||
{
|
||||
InitReturnVal_t nRetVal = BaseClass::Init();
|
||||
if ( nRetVal != INIT_OK )
|
||||
return nRetVal;
|
||||
|
||||
MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
|
||||
m_flElapsedTime = 0.0f;
|
||||
m_pAudioDevice = Audio_CreateWaveDevice();
|
||||
if ( !m_pAudioDevice->Init() )
|
||||
return INIT_FAILED;
|
||||
|
||||
return INIT_OK;
|
||||
}
|
||||
|
||||
void CSoundSystem::Shutdown()
|
||||
{
|
||||
Msg( "Removing %i sounds\n", m_ActiveSounds.Count() );
|
||||
for ( int i = 0 ; i < m_ActiveSounds.Count(); i++ )
|
||||
{
|
||||
CSoundFile *p = &m_ActiveSounds[ i ];
|
||||
Msg( "Removing sound: %s\n", p->filename );
|
||||
delete p->source;
|
||||
}
|
||||
|
||||
m_ActiveSounds.RemoveAll();
|
||||
|
||||
if ( m_pAudioDevice )
|
||||
{
|
||||
m_pAudioDevice->Shutdown();
|
||||
delete m_pAudioDevice;
|
||||
}
|
||||
|
||||
BaseClass::Shutdown();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CAudioSource *CSoundSystem::FindOrAddSound( const char *filename )
|
||||
{
|
||||
CSoundFile *s;
|
||||
|
||||
int i;
|
||||
for ( i = 0; i < m_ActiveSounds.Count(); i++ )
|
||||
{
|
||||
s = &m_ActiveSounds[ i ];
|
||||
Assert( s );
|
||||
if ( !stricmp( s->filename, filename ) )
|
||||
{
|
||||
long filetime = g_pFullFileSystem->GetFileTime( filename );
|
||||
if ( filetime != s->filetime )
|
||||
{
|
||||
Msg( "Reloading sound %s\n", filename );
|
||||
delete s->source;
|
||||
s->source = LoadSound( filename );
|
||||
s->filetime = filetime;
|
||||
}
|
||||
return s->source;
|
||||
}
|
||||
}
|
||||
|
||||
i = m_ActiveSounds.AddToTail();
|
||||
s = &m_ActiveSounds[ i ];
|
||||
strcpy( s->filename, filename );
|
||||
s->source = LoadSound( filename );
|
||||
s->filetime = g_pFullFileSystem->GetFileTime( filename );
|
||||
|
||||
return s->source;
|
||||
}
|
||||
|
||||
CAudioSource *CSoundSystem::LoadSound( const char *wavfile )
|
||||
{
|
||||
if ( !m_pAudioDevice )
|
||||
return NULL;
|
||||
|
||||
CAudioSource *wave = AudioSource_Create( wavfile );
|
||||
return wave;
|
||||
}
|
||||
|
||||
void CSoundSystem::PlaySound( CAudioSource *source, float volume, CAudioMixer **ppMixer )
|
||||
{
|
||||
if ( ppMixer )
|
||||
{
|
||||
*ppMixer = NULL;
|
||||
}
|
||||
|
||||
if ( m_pAudioDevice )
|
||||
{
|
||||
CAudioMixer *mixer = source->CreateMixer();
|
||||
if ( ppMixer )
|
||||
{
|
||||
*ppMixer = mixer;
|
||||
}
|
||||
mixer->SetVolume( volume );
|
||||
m_pAudioDevice->AddSource( mixer );
|
||||
}
|
||||
}
|
||||
|
||||
void CSoundSystem::Update( float dt )
|
||||
{
|
||||
// closecaptionmanager->PreProcess( g_nSoundFrameCount );
|
||||
|
||||
if ( m_pAudioDevice )
|
||||
{
|
||||
m_pAudioDevice->Update( m_flElapsedTime );
|
||||
}
|
||||
|
||||
// closecaptionmanager->PostProcess( g_nSoundFrameCount, dt );
|
||||
|
||||
m_flElapsedTime += dt;
|
||||
g_nSoundFrameCount++;
|
||||
}
|
||||
|
||||
void CSoundSystem::Flush( void )
|
||||
{
|
||||
if ( m_pAudioDevice )
|
||||
{
|
||||
m_pAudioDevice->Flush();
|
||||
}
|
||||
}
|
||||
|
||||
void CSoundSystem::StopAll( void )
|
||||
{
|
||||
if ( m_pAudioDevice )
|
||||
{
|
||||
m_pAudioDevice->StopSounds();
|
||||
}
|
||||
}
|
||||
|
||||
void CSoundSystem::StopSound( CAudioMixer *mixer )
|
||||
{
|
||||
int idx = m_pAudioDevice->FindSourceIndex( mixer );
|
||||
if ( idx != -1 )
|
||||
{
|
||||
m_pAudioDevice->FreeChannel( idx );
|
||||
}
|
||||
}
|
||||
|
||||
static eSubSystems_t GetDefaultAudioSubsystem()
|
||||
{
|
||||
eSubSystems_t nSubsystem = AUDIO_SUBSYSTEM_XAUDIO;
|
||||
#if IS_WINDOWS_PC
|
||||
if (CommandLine()->CheckParm("-directsound"))
|
||||
{
|
||||
nSubsystem = AUDIO_SUBSYSTEM_DSOUND;
|
||||
}
|
||||
#endif
|
||||
return nSubsystem;
|
||||
}
|
||||
|
||||
|
||||
void CSoundSystem::GetAudioDevices(CUtlVector< audio_device_description_t >& deviceListOut) const
|
||||
{
|
||||
CAudioDeviceList list;
|
||||
eSubSystems_t nSubsystem = GetDefaultAudioSubsystem();
|
||||
|
||||
list.BuildDeviceList(nSubsystem);
|
||||
|
||||
deviceListOut = list.m_list;
|
||||
}
|
||||
|
||||
bool CSoundSystem::IsSoundPlaying( CAudioMixer *pMixer )
|
||||
{
|
||||
if ( !m_pAudioDevice || !pMixer )
|
||||
return false;
|
||||
|
||||
//
|
||||
int index = m_pAudioDevice->FindSourceIndex( pMixer );
|
||||
if ( index != -1 )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CAudioMixer *CSoundSystem::FindMixer( CAudioSource *source )
|
||||
{
|
||||
if ( !m_pAudioDevice )
|
||||
return NULL;
|
||||
|
||||
return m_pAudioDevice->GetMixerForSource( source );
|
||||
}
|
||||
35
soundsystem/soundsystem.h
Normal file
35
soundsystem/soundsystem.h
Normal file
@@ -0,0 +1,35 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose: DLL interface for low-level sound utilities
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef SOUNDSYSTEM_H
|
||||
#define SOUNDSYSTEM_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "tier2/tier2.h"
|
||||
#include "tier3/tier3.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Forward declarations
|
||||
//-----------------------------------------------------------------------------
|
||||
class ISoundSystem;
|
||||
class ISoundSystemServices;
|
||||
class IAudioDevice;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Singleton interface
|
||||
//-----------------------------------------------------------------------------
|
||||
extern ISoundSystem *g_pSoundSystem;
|
||||
extern ISoundSystemServices *g_pSoundServices;
|
||||
extern IAudioDevice *g_pAudioDevice;
|
||||
|
||||
|
||||
#endif // SOUNDSYSTEM_H
|
||||
66
soundsystem/soundsystem.vpc
Normal file
66
soundsystem/soundsystem.vpc
Normal file
@@ -0,0 +1,66 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// SOUNDSYSTEM.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR ".."
|
||||
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_dll_win32_base.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$PreprocessorDefinitions "$BASE;SOUNDSYSTEM_EXPORTS"
|
||||
}
|
||||
|
||||
$Linker
|
||||
{
|
||||
$AdditionalDependencies "$BASE winmm.lib"
|
||||
}
|
||||
}
|
||||
|
||||
$Project "Soundsystem"
|
||||
{
|
||||
$Folder "Source Files"
|
||||
{
|
||||
$File "$SRCDIR\public\sentence.cpp"
|
||||
$File "$SRCDIR\public\phonemeconverter.cpp"
|
||||
$File "snd_audio_source.cpp"
|
||||
$File "snd_dev_wave.cpp"
|
||||
$File "snd_io.cpp"
|
||||
$File "snd_wave_mixer.cpp"
|
||||
$File "snd_wave_mixer_adpcm.cpp"
|
||||
$File "snd_wave_source.cpp"
|
||||
$File "soundsystem.cpp"
|
||||
}
|
||||
|
||||
$Folder "Header Files"
|
||||
{
|
||||
$File "snd_dev_wave.h"
|
||||
$File "snd_wave_mixer.h"
|
||||
$File "snd_wave_mixer_adpcm.h"
|
||||
$File "snd_wave_mixer_private.h"
|
||||
$File "snd_wave_source.h"
|
||||
$File "soundsystem.h"
|
||||
}
|
||||
|
||||
$Folder "Interface"
|
||||
{
|
||||
$File "$SRCDIR\public\soundsystem\isoundsystem.h"
|
||||
$File "$SRCDIR\public\soundsystem\snd_audio_source.h"
|
||||
$File "$SRCDIR\public\soundsystem\snd_device.h"
|
||||
}
|
||||
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib soundsystem_lowlevel [$WIN32]
|
||||
$File "$SRCDIR\dx9sdk\lib\dsound.lib"
|
||||
$File "$SRCDIR\dx9sdk\lib\dxguid.lib"
|
||||
$File "$SRCDIR\lib\public\mathlib.lib"
|
||||
$File "$SRCDIR\lib\common\mss32.lib"
|
||||
$File "$SRCDIR\lib\public\tier2.lib"
|
||||
}
|
||||
}
|
||||
13
soundsystem/soundsystem.vpc.vpc_cache
Normal file
13
soundsystem/soundsystem.vpc.vpc_cache
Normal file
@@ -0,0 +1,13 @@
|
||||
"vpc_cache"
|
||||
{
|
||||
"CacheVersion" "1"
|
||||
"win32"
|
||||
{
|
||||
"CRCFile" "soundsystem.vcxproj.vpc_crc"
|
||||
"OutputFiles"
|
||||
{
|
||||
"0" "soundsystem.vcxproj"
|
||||
"1" "soundsystem.vcxproj.filters"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user