You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
716 lines
19 KiB
716 lines
19 KiB
#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);
|
|
}
|
|
}
|