3498 lines
97 KiB
C
3498 lines
97 KiB
C
//--------------------------------------------------------------------------;
|
|
//
|
|
// File: DSoundHW.c
|
|
//
|
|
// Copyright (c) 1995 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
// Abstract:
|
|
//
|
|
//
|
|
// Contents:
|
|
// IDSHWQueryInterface()
|
|
// IDSHWAddRef()
|
|
// IDSHWRelease()
|
|
// IDSHWCreateSoundBuffer()
|
|
// IDSHWEnumSoundBuffers()
|
|
// IDSHWGetCaps()
|
|
// IDSHWDuplicateSoundBuffer()
|
|
// IDSHWSetCooperativeLevel()
|
|
// IDSHWCompact();
|
|
// IDSHWGetSpeakerConfig()
|
|
// IDSHWSetSpeakerConfig()
|
|
// IDSHWInitialize()
|
|
// DSHWCreateTable()
|
|
// GetTopUnownedWindow() added by angusm on 11/27/95
|
|
// CreateFocusThread() added by angusm on 12/1/95
|
|
// cSoundObjects() added by angusm on 11/30/95
|
|
//
|
|
// History:
|
|
// Date By Reason
|
|
// ==== == ======
|
|
// 3/5/96 angusm Added use of fInitialized
|
|
// 03/20/06 angusm Added support for Sticky Focus
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
#include "dsoundpr.h"
|
|
#ifndef DSBLD_EMULONLY
|
|
#include <pbt.h>
|
|
#endif
|
|
#include "grace.h"
|
|
#include "flocks.h"
|
|
|
|
/* Global Constansts */
|
|
#define POLL_INTERVAL 250 /* number of miliseconds between polls
|
|
* for active window */
|
|
#define WAKEFOCUSTHREAD "DSWakeFocusThread" /* event name for Focus Thread
|
|
* event. */
|
|
#define FOCUSSTARTUPEVENT "DSFocusStartupEvent" /* event name for handshake
|
|
* after Focus Thread
|
|
* startup code */
|
|
|
|
/* Function Prototypes */
|
|
|
|
HWND GetTopUnownedWindow (HWND hWindow);
|
|
BOOL IsValidDSApp (DWORD dwTid);
|
|
void EndFocusThread();
|
|
DWORD WINAPI FocusTracker (LPVOID lpvPollInterval);
|
|
__inline void DseUpdateActivationState (LPDSOUNDEXTERNAL pdse);
|
|
__inline void DsbeDeactivateIfNecessary (LPDSBUFFEREXTERNAL);
|
|
__inline void ActivateFocusWindow (void);
|
|
|
|
// From DirectDraw - for subclassing
|
|
extern HRESULT _stdcall DSoundHelp( HWND hWnd,WNDPROC lpWndProc,DWORD dwPID );
|
|
|
|
LRESULT CALLBACK SubclassWndProc( HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam );
|
|
|
|
|
|
|
|
LPDSOUNDINFO gpdsinfo;
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// HRESULT DseVerifyValidFormat
|
|
//
|
|
// Description:
|
|
// This function returns DS_OK if the specified format is valid for
|
|
// this sound device.
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
HRESULT DseVerifyValidFormat
|
|
(
|
|
LPDSOUNDEXTERNAL pdse,
|
|
LPWAVEFORMATEX pwfx
|
|
)
|
|
{
|
|
DSCAPS dsc;
|
|
HRESULT hr;
|
|
|
|
ZeroMemory(&dsc, sizeof(dsc));
|
|
dsc.dwSize = sizeof(dsc);
|
|
|
|
hr = DsGetCaps (pdse->pds, &dsc);
|
|
if (DS_OK != hr) {
|
|
return hr;
|
|
}
|
|
|
|
if (WAVE_FORMAT_PCM != pwfx->wFormatTag) return DSERR_BADFORMAT;
|
|
|
|
if( ( (pwfx->nChannels == 1) &&
|
|
!(dsc.dwFlags & DSCAPS_PRIMARYMONO) ) ||
|
|
( (pwfx->nChannels == 2) &&
|
|
!(dsc.dwFlags & DSCAPS_PRIMARYSTEREO) ) ||
|
|
( (pwfx->wBitsPerSample == 8) &&
|
|
!(dsc.dwFlags & DSCAPS_PRIMARY8BIT) ) ||
|
|
( (pwfx->wBitsPerSample == 16) &&
|
|
!(dsc.dwFlags & DSCAPS_PRIMARY16BIT) ) ) {
|
|
return DSERR_BADFORMAT;
|
|
}
|
|
|
|
return DS_OK;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// HRESULT DseSaveAppFormat
|
|
//
|
|
// Description:
|
|
// This function saves the specified format as the app format in the
|
|
// specified dse object.
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
HRESULT DseSaveAppFormat(LPDSOUNDEXTERNAL pdse, LPWAVEFORMATEX pwfxAppCaller)
|
|
{
|
|
LPWAVEFORMATEX pwfxApp;
|
|
int cbFormat;
|
|
|
|
cbFormat = SIZEOF_WAVEFORMATEX(pwfxAppCaller);
|
|
pwfxApp = (LPWAVEFORMATEX)MemAlloc(cbFormat);
|
|
|
|
if (NULL == pwfxApp) {
|
|
DPF(0, "DseSaveAppFormat: error: Out of memory saving app's format");
|
|
return DSERR_OUTOFMEMORY;
|
|
}
|
|
|
|
CopyMemory(pwfxApp, pwfxAppCaller, cbFormat);
|
|
|
|
if (NULL != pdse->pwfxApp) {
|
|
DPF(1, "Freeing previous app format");
|
|
MemFree(pdse->pwfxApp);
|
|
}
|
|
|
|
pdse->pwfxApp = pwfxApp;
|
|
|
|
return DS_OK;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// HRESULT IDSHWQueryInterface
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
HRESULT FAR PASCAL IDSHWQueryInterface
|
|
(
|
|
LPDIRECTSOUND pids,
|
|
REFIID riid,
|
|
LPVOID FAR* ppvObj
|
|
)
|
|
{
|
|
LPDSOUNDEXTERNAL pdse;
|
|
|
|
if( !VALID_DSOUNDE_PTR(pids) ) {
|
|
RPF("IDirectSound::QueryInterface - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
pdse = (LPDSOUNDEXTERNAL)pids;
|
|
|
|
if( 0 == pdse->uRefCount ) {
|
|
RPF("IDirectSound::QueryInterface - Invalid ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
if (IDSHWINITIALIZEF_INITIALIZED == pdse->fInitialized) {
|
|
ASSERT(VALID_DSOUND_PTR(pdse->pds));
|
|
}
|
|
|
|
if( riid == NULL ) {
|
|
RPF("IDirectSound::QueryInterface - NULL riid");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
if( ppvObj == NULL ) {
|
|
RPF("IDirectSound::QueryInterface - NULL ppvObj");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
ENTER_DLL_CSECT();
|
|
|
|
if( IsEqualGUID(riid,&IID_IDirectSound) ||
|
|
IsEqualGUID(riid,&IID_IUnknown) ) {
|
|
*ppvObj = pdse;
|
|
|
|
pdse->lpVtbl->AddRef((LPDIRECTSOUND)pdse);
|
|
|
|
LEAVE_DLL_CSECT();
|
|
return DS_OK;
|
|
}
|
|
else
|
|
{
|
|
LEAVE_DLL_CSECT();
|
|
return DSERR_NOINTERFACE;
|
|
}
|
|
|
|
LEAVE_DLL_CSECT();
|
|
return DSERR_GENERIC;
|
|
} // IDSHWQueryInterface()
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// IDSHWAddRef
|
|
//
|
|
// Description:
|
|
// This function implements the standard IUnknown::AddRef()
|
|
//
|
|
// Arguments:
|
|
// pids "this" pointer
|
|
//
|
|
// Return (ULONG):
|
|
// Estimate of the value of the reference count.
|
|
//
|
|
// History:
|
|
// 02/11/96 angusm Removed need to have (uRefCount != 0)
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
LONG DsAddRef(LPDSOUND pds)
|
|
{
|
|
pds->uRefCount++;
|
|
return (int)pds->uRefCount;
|
|
}
|
|
|
|
ULONG FAR PASCAL IDSHWAddRef
|
|
(
|
|
LPDIRECTSOUND pids
|
|
)
|
|
{
|
|
LPDSOUNDEXTERNAL pdse;
|
|
|
|
if( !VALID_DSOUNDE_PTR(pids) ) {
|
|
RPF("IDirectSound::AddRef - Invalid Object");
|
|
return 0;
|
|
}
|
|
pdse = (LPDSOUNDEXTERNAL)pids;
|
|
if( 0 == pdse->uRefCount) {
|
|
RPF("IDirectSound::AddRef - Invalid Object or ref count");
|
|
return 0;
|
|
}
|
|
|
|
ENTER_DLL_CSECT();
|
|
pdse->uRefCount++;
|
|
LEAVE_DLL_CSECT();
|
|
|
|
return pdse->uRefCount;
|
|
} // IDSHWAddRef()
|
|
|
|
LONG DsRelease(LPDSOUND pds)
|
|
{
|
|
LPDSOUND pdsList;
|
|
LPDSOUND pdsPrev;
|
|
HANDLE hMixThread;
|
|
|
|
if (0 != --pds->uRefCount) {
|
|
DPF(3,"DsRelease done, ref count now %X", pds->uRefCount );
|
|
ASSERT((LONG)pds->uRefCount > 0);
|
|
return pds->uRefCount;
|
|
}
|
|
|
|
|
|
DPF(2,"Destroying DirectSound object");
|
|
|
|
// Free the primary we created
|
|
if( pds->pdsbePrimary != NULL ) {
|
|
IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)(pds->pdsbePrimary));
|
|
}
|
|
pds->pdsbePrimary = NULL;
|
|
|
|
//
|
|
if (!(DS_INTERNALF_WAVEEMULATED & pds->fdwInternal)) {
|
|
hMixThread = mxTerminate(pds);
|
|
} else {
|
|
hMixThread = NULL;
|
|
}
|
|
|
|
// Remove it from the list
|
|
pdsList = gpdsinfo->pDSoundObj;
|
|
pdsPrev = NULL;
|
|
while( pdsList != NULL ) {
|
|
if( pds == pdsList ) {
|
|
if( pdsPrev ) {
|
|
// Previous element in list remove it
|
|
pdsPrev->pNext = pdsList->pNext;
|
|
} else {
|
|
// Head of list - remove
|
|
gpdsinfo->pDSoundObj = pdsList->pNext;
|
|
}
|
|
pdsList = NULL;
|
|
} else {
|
|
pdsPrev = pdsList;
|
|
pdsList = pdsList->pNext;
|
|
}
|
|
}
|
|
pds->pNext = NULL;
|
|
|
|
|
|
// If we allocated a heap manager for the driver
|
|
if (DSDHEAP_CREATEHEAP == pds->dwHeapType) {
|
|
VidMemFini(pds->pDriverHeap);
|
|
pds->pDriverHeap = NULL;
|
|
}
|
|
|
|
// Free the Wave Blit structure we use on eumlation
|
|
MemFree( pds->pdswb );
|
|
|
|
|
|
|
|
// FINI layer to VxD
|
|
if( pds->hHal) {
|
|
DPF(3,"Close HAL layer %X", pds->hHal );
|
|
vxdDrvClose( pds->hHal );
|
|
DPF(3,"Finished Close HAL layer" );
|
|
|
|
}
|
|
pds->hHal = INVALID_HANDLE_VALUE;
|
|
|
|
// Close mmsystem wave handle if we opened one for this driver
|
|
if (pds->hwo) {
|
|
// ASSERT(DSDDESC_DOMMSYSTEMOPEN & pds->dsDrvDesc.dwFlags);
|
|
HelperWaveClose( (DWORD)(pds->hwo) );
|
|
pds->hwo = NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
// Validity checking
|
|
if(!(pds->fdwInternal & DS_INTERNALF_ALLOCATED)) {
|
|
DPF(0,"******* Already Freed dsound HW object ********");
|
|
}
|
|
if( pds->pdsb != NULL ) {
|
|
DPF(0,"******* PDSB != NULL ********");
|
|
}
|
|
if( pds->pdsbPrimary != NULL ) {
|
|
DPF(0,"******* PDSBPrimary != NULL ********");
|
|
}
|
|
if( pds->hPlaybackThread != NULL ) {
|
|
DPF(0,"******* Playback thread != NULL ********");
|
|
}
|
|
if( pds->hwo != NULL ) {
|
|
DPF(0,"******* HWO != NULL ********");
|
|
}
|
|
if( pds->dwBuffersPlaying != 0 ) {
|
|
DPF(0,"******* BuffersPlaying != NULL ********");
|
|
}
|
|
#endif
|
|
|
|
|
|
pds->dwSig = 0xdeaddead;
|
|
MemFree( pds );
|
|
|
|
|
|
DPF(3,"Release done %X ref count now 0", pds);
|
|
|
|
//
|
|
// Wait for mixer thread to die
|
|
//
|
|
if (NULL != hMixThread) {
|
|
DWORD dwResult;
|
|
HANDLE hHelper;
|
|
HANDLE hMixThreadOurs;
|
|
|
|
DPF(3, "IDSHWRelease: note: waiting for mixer thread to terminate");
|
|
hHelper = OpenProcess(PROCESS_DUP_HANDLE, FALSE, gpdsinfo->pidHelper);
|
|
if (hHelper)
|
|
{
|
|
if (DuplicateHandle(hHelper, hMixThread, GetCurrentProcess(),
|
|
&hMixThreadOurs, SYNCHRONIZE | THREAD_TERMINATE,
|
|
FALSE, DUPLICATE_CLOSE_SOURCE))
|
|
{
|
|
dwResult = WaitForSingleObjectEx(hMixThreadOurs, INFINITE, FALSE);
|
|
ASSERT(WAIT_OBJECT_0 == dwResult);
|
|
dwResult = CloseHandle(hMixThreadOurs);
|
|
ASSERT(dwResult);
|
|
}
|
|
dwResult = CloseHandle(hHelper);
|
|
ASSERT(dwResult);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
} // DsRelease()
|
|
|
|
void DseTerminate(LPDSOUNDEXTERNAL pdse)
|
|
{
|
|
LPDSOUNDEXTERNAL pdseList;
|
|
LPDSOUNDEXTERNAL pdsePrev;
|
|
HANDLE hFocusLock;
|
|
|
|
// Now release any buffers this process may have accessed.
|
|
// Release all buffers for this process for this object
|
|
FreeBuffersForProcess( pdse );
|
|
|
|
// We use DSoundHelp to give us APM window notifications. If we are
|
|
// building only for emulation mode then we don't need APM notifications
|
|
// since APM will be handled entirely by the WAVE drivers that we are
|
|
// using for emulation.
|
|
#ifndef DSBLD_EMULONLY
|
|
// Remove the window from the subclass list
|
|
// Window is stored by PID
|
|
DSoundHelp( NULL, SubclassWndProc, HackGetCurrentProcessId() );
|
|
#endif
|
|
|
|
// Remove it from the list
|
|
if (FALSE == GetFocusLock(&hFocusLock)) {
|
|
DPF (3, "Could not get Focus Lock");
|
|
}
|
|
pdseList = gpdsinfo->pDSoundExternalObj;
|
|
pdsePrev = NULL;
|
|
while( pdseList != NULL ) {
|
|
if( pdse == pdseList ) {
|
|
if( pdsePrev ) {
|
|
// Previous element in list remove it
|
|
pdsePrev->pNext = pdseList->pNext;
|
|
} else {
|
|
// Head of list - remove
|
|
gpdsinfo->pDSoundExternalObj = pdseList->pNext;
|
|
}
|
|
pdseList = NULL;
|
|
} else {
|
|
pdsePrev = pdseList;
|
|
pdseList = pdseList->pNext;
|
|
}
|
|
}
|
|
pdse->pNext = NULL;
|
|
if (FALSE == ReleaseFocusLock(hFocusLock)) {
|
|
DPF (3, "Could not release Focus Lock.");
|
|
}
|
|
|
|
// If an app wave format was allocated for this, then free it.
|
|
if (pdse->pwfxApp) MemFree(pdse->pwfxApp);
|
|
pdse->pwfxApp = NULL;
|
|
|
|
//
|
|
DsRelease(pdse->pds);
|
|
}
|
|
|
|
ULONG FAR PASCAL IDSHWRelease(LPDIRECTSOUND pIDs)
|
|
{
|
|
LPDSOUNDEXTERNAL pdse;
|
|
ULONG cRef;
|
|
|
|
pdse = (LPDSOUNDEXTERNAL)pIDs;
|
|
|
|
ENTER_DLL_CSECT();
|
|
|
|
cRef = --pdse->uRefCount;
|
|
if (0 == cRef) {
|
|
|
|
// Check to see if the object is initialized
|
|
if (IDSHWINITIALIZEF_UNINITIALIZED != pdse->fInitialized) {
|
|
DPF(1, "DseRelease: deleting object %08Xh", pdse);
|
|
|
|
/* End Focus Thread */
|
|
if (1 == cSoundObjects()) EndFocusThread();
|
|
|
|
DseTerminate(pdse);
|
|
|
|
pdse->fInitialized = IDSHWINITIALIZEF_UNINITIALIZED;
|
|
}
|
|
|
|
DPF(0, "Freeing pdse %08Xh", pdse);
|
|
MemFree( pdse );
|
|
|
|
}
|
|
|
|
LEAVE_DLL_CSECT();
|
|
|
|
return cRef;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// HRESULT DseCreateDsbe
|
|
//
|
|
// Description:
|
|
// This function operates on a Dse object to create a Dsb object
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Return (HRESULT):
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
HRESULT DseCreateDsbe
|
|
(
|
|
LPDSOUNDEXTERNAL pdse,
|
|
LPDSBUFFERDESC pdsbd,
|
|
LPDSBUFFEREXTERNAL *ppdsbe
|
|
)
|
|
{
|
|
LPDSBUFFER pdsb;
|
|
LPDSBUFFEREXTERNAL pdsbe;
|
|
LPDSBUFFER pdsb1;
|
|
LPDSBUFFEREXTERNAL pdsbe1;
|
|
UINT uDevID;
|
|
LPDSOUND pds;
|
|
DWORD cbMixBufferSize;
|
|
DWORD cbFormatSize;
|
|
DWORD dw;
|
|
DWORD dwForce;
|
|
LPDSPROCESS pDSPID;
|
|
DSCAPS dsc;
|
|
BOOL fTryHardware, fTrySoftware;
|
|
HRESULT hr;
|
|
HRESULT hrReturn;
|
|
DSBUFFERDESC dsbdTemp;
|
|
HRESULT hrTemp;
|
|
|
|
DPF(3,"DseCreateDsbe()");
|
|
|
|
pds = pdse->pds;
|
|
if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) {
|
|
RPF("IDirectSound::CreateSoundBuffer - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
hrReturn = DS_OK;
|
|
*ppdsbe = NULL;
|
|
dwForce = 0;
|
|
uDevID = pds->uDeviceID;
|
|
|
|
_fmemcpy( &dsbdTemp, pdsbd, sizeof(DSBUFFERDESC));
|
|
|
|
// If the user wants a primary then it must be Hardware
|
|
// If they're trying to force a software primary, fail them
|
|
// Set the hardware flag otherwise
|
|
// Also set that this buffer can be blt to
|
|
// If it's not a request for a primary, then set our internal force flag
|
|
if( (dsbdTemp.dwFlags & DSBCAPS_PRIMARYBUFFER) ) {
|
|
if( (dsbdTemp.dwFlags & DSBCAPS_LOCSOFTWARE) ) {
|
|
RPF("IDirectSound::CreateSoundBuffer - Invalid attempt to force a software primary buffer!");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
dsbdTemp.dwFlags |= DSBCAPS_LOCHARDWARE;
|
|
dsbdTemp.dwFlags |= DSBCAPS_CTRLWAVEBLTDST;
|
|
|
|
// Caller mustn't specify primary specify buffer size.
|
|
if ( (0 != dsbdTemp.dwBufferBytes) ) {
|
|
RPF("IDirectSound::CreateSoundBuffer - Primary buffers must be created with dwBufferBytes = 0");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
else {
|
|
dsbdTemp.dwBufferBytes = DEFAULT_PRIMARY_SIZE;
|
|
}
|
|
|
|
// We don't allow any apps to specify a format for primary buffers.
|
|
if (NULL != dsbdTemp.lpwfxFormat) {
|
|
RPF("IDirectSound::CreateSoundBuffer - error: app must not specify primary buffer format");
|
|
return DSERR_INVALIDCALL;
|
|
}
|
|
|
|
|
|
} else {
|
|
// NOTE: There is a conflict of motives with these flags: the user is
|
|
// telling us what they want to do, but we use them internally to tell
|
|
// ourselves what to do, so save off the user's intentions into dwForce
|
|
// and then tell them what they actually got at the end of the create
|
|
if( (dsbdTemp.dwFlags & DSBCAPS_LOCSOFTWARE) ) {
|
|
dsbdTemp.dwFlags &= (~DSBCAPS_LOCSOFTWARE);
|
|
dwForce = DSBCAPS_LOCSOFTWARE;
|
|
}
|
|
else if( (dsbdTemp.dwFlags & DSBCAPS_LOCHARDWARE) ) {
|
|
dsbdTemp.dwFlags &= (~DSBCAPS_LOCHARDWARE);
|
|
dwForce = DSBCAPS_LOCHARDWARE;
|
|
}
|
|
}
|
|
|
|
|
|
// Check that buffer has a real size...
|
|
|
|
if( dsbdTemp.dwBufferBytes <= 4 ) {
|
|
RPF("IDirectSound::CreateSoundBuffer - Buffer size too small");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
if( dsbdTemp.dwBufferBytes >= 0x10000000 ) {
|
|
RPF("IDirectSound::CreateSoundBuffer - Buffer size too large");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
//
|
|
// If the app is creating a primary, and a primary already exists for
|
|
// the app, then return the app's existing primary buffer object.
|
|
//
|
|
if (DSBCAPS_PRIMARYBUFFER & dsbdTemp.dwFlags) {
|
|
|
|
pdsbe = pdse->pdsbe;
|
|
while (NULL != pdsbe) {
|
|
if (DSB_INTERNALF_PRIMARY & pdsbe->pdsb->fdwDsbI) break;
|
|
pdsbe = pdsbe->pNext;
|
|
}
|
|
|
|
if (NULL != pdsbe) {
|
|
|
|
//
|
|
// If they requested vol control, specify that this pdsbe is allowed.
|
|
//
|
|
if( dsbdTemp.dwFlags & DSBCAPS_CTRLVOLUME ) {
|
|
pdsbe->fdwDsbeI |= DSBE_INTERNALF_CTRLVOLUMEPRIMARY;
|
|
}
|
|
|
|
//
|
|
// If they requested pan control, specify that this pdsbe is allowed.
|
|
//
|
|
if( dsbdTemp.dwFlags & DSBCAPS_CTRLPAN ) {
|
|
pdsbe->fdwDsbeI |= DSBE_INTERNALF_CTRLPANPRIMARY;
|
|
}
|
|
|
|
// Addref and return pointer to existing buffer
|
|
pdsbe->lpVtbl->AddRef((LPDIRECTSOUNDBUFFER)pdsbe);
|
|
*ppdsbe = pdsbe;
|
|
|
|
return DS_OK;
|
|
}
|
|
}
|
|
|
|
// Allocate the dsbe object
|
|
pdsbe = (LPDSBUFFEREXTERNAL)MemAlloc(sizeof(DSBUFFEREXTERNAL));
|
|
if(NULL == pdsbe) {
|
|
DPF(0,"CreateSoundBuffer object (External) alloc fail");
|
|
goto CREATE_ERROR_LAST;
|
|
}
|
|
|
|
pdsbe->lpVtbl = gpdsinfo->lpVtblDSb;
|
|
pdsbe->pdse = pdse;
|
|
pdsbe->uRefCount = 1;
|
|
pdsbe->dwPID = GetCurrentProcessId();
|
|
pdsbe->dwPriority = pdse->dwPriority;
|
|
|
|
pdsbe->pNext = pdse->pdsbe;
|
|
pdse->pdsbe = pdsbe;
|
|
|
|
// HACK HACK Multiple Primaries
|
|
// Check to see if primary buffer is already allocated.
|
|
if( (dsbdTemp.dwFlags & DSBCAPS_PRIMARYBUFFER ) && (pds->pdsbPrimary) ) {
|
|
// We are asking for another primary buffer...
|
|
DPF(3,"Primary Object already allocated" );
|
|
ASSERT( VALID_DSBUFFER_PTR(pds->pdsbPrimary) );
|
|
ASSERT( DSBUFFSIG == pds->pdsbPrimary->dwSig );
|
|
|
|
// Point external object to this primary
|
|
pdsb = pds->pdsbPrimary;
|
|
pdsbe->pdsb = pds->pdsbPrimary;
|
|
|
|
// If this is not waveemulated, create an alias pointer to the data
|
|
// buffer. Note that this only reserves linear address space.
|
|
// Physical memory is committed on Lock.
|
|
if (!(pdsb->pds->fdwInternal & DS_INTERNALF_WAVEEMULATED)) {
|
|
pdsbe->pDSBufferAlias = vxdMemReserveAlias(pdsb->pDSBuffer, pdsb->cbBufferSize);
|
|
} else {
|
|
pdsbe->pDSBufferAlias = NULL;
|
|
}
|
|
|
|
//
|
|
// If they requested vol control, specify that this pdsbe is allowed.
|
|
//
|
|
if( dsbdTemp.dwFlags & DSBCAPS_CTRLVOLUME ) {
|
|
pdsbe->fdwDsbeI |= DSBE_INTERNALF_CTRLVOLUMEPRIMARY;
|
|
}
|
|
|
|
//
|
|
// If they requested pan control, specify that this pdsbe is allowed.
|
|
//
|
|
if( dsbdTemp.dwFlags & DSBCAPS_CTRLPAN ) {
|
|
pdsbe->fdwDsbeI |= DSBE_INTERNALF_CTRLPANPRIMARY;
|
|
}
|
|
|
|
// Addref existing object and return pointer to it
|
|
pdsbe->lpVtbl->AddRef((LPDIRECTSOUNDBUFFER)pdsbe);
|
|
|
|
// The refcount on this external primary should be 1 for the create
|
|
// But it is now 2 since addref needs a + refcount to work
|
|
// So reset it to 1
|
|
pdsbe->uRefCount = 1;
|
|
|
|
|
|
DPF(2, "Return buffer ext %X, core %X mem ptr %X size %X",
|
|
pdsbe, pdsb, pdsb->pDSBuffer ,pdsb->cbBufferSize );
|
|
|
|
|
|
// If the pdse is in focus and it is WRITEPRIMARY, then the
|
|
// primary dsbe for this app should be stopped and so must be
|
|
// the internal primary dsb.
|
|
//
|
|
// We also need to set the internal primary format, even when
|
|
// not WRITEPRIMARY.
|
|
//
|
|
if (pdse->tidSound == gpdsinfo->tidSoundFocus || pdse->tidSound == gpdsinfo->tidStuckFocus) {
|
|
HRESULT hr;
|
|
|
|
if (pdse->dwPriority >= DSSCL_WRITEPRIMARY) {
|
|
IDsbStopI(pds->pdsbPrimary, FALSE);
|
|
}
|
|
|
|
if (NULL != pdse->pwfxApp) {
|
|
hr = IDsbSetFormatI( pds->pdsbPrimary, pdse->pwfxApp, 0 );
|
|
} else {
|
|
hr = IDsbSetFormatI( pds->pdsbPrimary, &pds->wfxDefault, 0 );
|
|
}
|
|
|
|
if( DS_OK != hr )
|
|
{
|
|
RPF("IDirectSound::CreateSoundBuffer - Couldn't set primary format when creating primary buffer.");
|
|
}
|
|
}
|
|
|
|
DsbeDeactivateIfNecessary (pdsbe);
|
|
|
|
// Return pointer to new buffer
|
|
*ppdsbe = pdsbe;
|
|
|
|
return DS_OK;
|
|
}
|
|
|
|
// Allocate the dsb object
|
|
pdsb = (LPDSBUFFER)MemAlloc(sizeof(DSBUFFER));
|
|
if(NULL == pdsb) {
|
|
DPF(0,"CreateSoundBuffer object alloc fail");
|
|
goto CREATE_ERROR_BUFFEREXTERNAL;
|
|
}
|
|
|
|
pdsb->dwSig = DSBUFFSIG;
|
|
DPF(1, "Allocating DSBUFFER obj 0x%8x",pdsb);
|
|
|
|
// Point external object to this
|
|
pdsbe->pdsb = pdsb;
|
|
|
|
|
|
pdsb->fdwBufferDesc = dsbdTemp.dwFlags;
|
|
pdsb->dwPrimaryNumber = dsbdTemp.dwReserved; // The reserved field is dwPrimaryNumber
|
|
pdsb->cbBufferSize = dsbdTemp.dwBufferBytes;
|
|
pdsb->pdsb3d = NULL;
|
|
pdsb->pds = pds;
|
|
pdsb->fdwDsbI = DSB_INTERNALF_STOP;
|
|
pdsb->uRefCount = 1;
|
|
|
|
pdsb->pNext = pds->pdsb;
|
|
pds->pdsb = pdsb;
|
|
|
|
pdsb->pdsbDuplicateNext = pdsb;
|
|
pdsb->pdsbDuplicatePrev = pdsb;
|
|
|
|
|
|
// Set up hack internal use external objct
|
|
// This will be used by the mixer for calling
|
|
// methods like stop
|
|
// It will never be used for addref or release
|
|
pdsb->dsbe.lpVtbl = gpdsinfo->lpVtblDSb;
|
|
pdsb->dsbe.pdse = pdse;
|
|
pdsb->dsbe.uRefCount = 1;
|
|
pdsb->dsbe.dwPID = DWBUFFER_INTERNAL_PID;
|
|
pdsb->dsbe.pNext = NULL;
|
|
pdsb->dsbe.pdsb = pdsb;
|
|
pdsb->dsbe.dwPriority = pdse->dwPriority;
|
|
|
|
|
|
// If we are allocating the primary and this is the first primary
|
|
// then initialize the ds obj to show the primary
|
|
// The DSEPrimary will only be used internally
|
|
if( dsbdTemp.dwFlags & DSBCAPS_PRIMARYBUFFER ) {
|
|
pds->pdsbPrimary = pdsb;
|
|
pdsb->fdwDsbI |= DSB_INTERNALF_PRIMARY;
|
|
|
|
}
|
|
|
|
// Alloc Process ID list
|
|
pDSPID = (LPDSPROCESS)MemAlloc(sizeof(DSPROCESS));
|
|
if(NULL == pDSPID) {
|
|
DPF(1,"IDSCreateSoundBuffer process alloc fail");
|
|
goto CREATE_ERROR_BUFFER;
|
|
}
|
|
pDSPID->dwPID = HackGetCurrentProcessId();
|
|
pDSPID->dwProcessRefCount = 1;
|
|
pDSPID->pNext = NULL;
|
|
pdsb->plProcess = pDSPID;
|
|
|
|
|
|
// Get the ds object's caps
|
|
dsc.dwSize = sizeof( DSCAPS );
|
|
hrTemp = DsGetCaps(pdse->pds, &dsc);
|
|
ASSERT (DS_OK == hrTemp);
|
|
|
|
DPF(3,"Caps Flags from Driver %X free buffers %X",
|
|
dsc.dwFlags, dsc.dwFreeHwMixingAllBuffers );
|
|
|
|
// Allocate a buffer to hold the format
|
|
|
|
// First get max format size
|
|
// HACK HACK - NO Need to bring in ACM just for this...
|
|
// Assume size will be reasonable
|
|
// If we have a format use that size
|
|
// Add on 16 bytes of slop space
|
|
// Format may be changed to a larger one
|
|
if( dsbdTemp.lpwfxFormat != NULL ) {
|
|
dw = SIZEOF_WAVEFORMATEX( dsbdTemp.lpwfxFormat );
|
|
cbFormatSize = dw + 16;
|
|
} else {
|
|
cbFormatSize = SIZEOF_WAVEFORMATEX(&pds->wfxDefault);
|
|
}
|
|
|
|
|
|
// Now allocate memory
|
|
pdsb->pwfx = (LPWAVEFORMATEX)MemAlloc(cbFormatSize);
|
|
if(NULL == pdsb->pwfx) {
|
|
DPF(1,"IDSHWCreateSoundBuffer object alloc fail");
|
|
goto CREATE_ERROR_PROCESS;
|
|
}
|
|
// Now copy the format
|
|
if( dsbdTemp.lpwfxFormat != NULL ) {
|
|
// We have a real format given
|
|
DPF(3,"Format Given %X", dsbdTemp.lpwfxFormat );
|
|
|
|
if( !ValidPCMFormat( dsbdTemp.lpwfxFormat ) ) {
|
|
RPF("IDirectSound::CreateSoundBuffer - Not a valid PCM format");
|
|
// Note that later on we should handle non pcm formats
|
|
hrReturn = DSERR_BADFORMAT;
|
|
goto CREATE_ERROR_FORMAT;
|
|
}
|
|
|
|
dw = SIZEOF_WAVEFORMATEX( dsbdTemp.lpwfxFormat );
|
|
CopyMemory( pdsb->pwfx, dsbdTemp.lpwfxFormat, dw );
|
|
} else {
|
|
// Not a real format - set to default for now
|
|
// Note this is only possible in the primary case.
|
|
DPF(3,"No format create default." );
|
|
CopyMemory(pdsb->pwfx, &pds->wfxDefault, SIZEOF_WAVEFORMATEX(&pds->wfxDefault));
|
|
}
|
|
|
|
|
|
//Set the helInfo stuff
|
|
pdsb->helInfo.dwSampleRate = 0;
|
|
pdsb->helInfo.hfFormat = 0;
|
|
pdsb->helInfo.lVolume = 0;
|
|
pdsb->helInfo.lPan = 0;
|
|
pdsb->helInfo.dwLVolume = 0xffff;
|
|
pdsb->helInfo.dwRVolume = 0xffff;
|
|
pdsb->helInfo.dwMVolume = 0xffff;
|
|
|
|
|
|
// IF there is a format to set
|
|
if( pdsb->pwfx->wFormatTag == WAVE_FORMAT_PCM ) {
|
|
pdsb->helInfo.dwSampleRate = pdsb->pwfx->nSamplesPerSec;
|
|
pdsb->helInfo.hfFormat = 0;
|
|
if( pdsb->pwfx->wBitsPerSample == 8 ) {
|
|
pdsb->helInfo.hfFormat |= (H_8_BITS | H_UNSIGNED);
|
|
} else {
|
|
pdsb->helInfo.hfFormat |= (H_16_BITS | H_SIGNED);
|
|
}
|
|
if( pdsb->pwfx->nChannels == 2 ) {
|
|
pdsb->helInfo.hfFormat |= (H_STEREO | H_ORDER_LR);
|
|
} else {
|
|
pdsb->helInfo.hfFormat |= H_MONO;
|
|
}
|
|
} else {
|
|
DPF(0, "****************** set NON PCM format ******************" );
|
|
|
|
// Note that later on we should handle non pcm formats
|
|
hrReturn = DSERR_BADFORMAT;
|
|
goto CREATE_ERROR_FORMAT;
|
|
}
|
|
|
|
pdsb->helInfo.hfFormat |= H_LOOP;
|
|
pdsb->fdwDsbI |= DSB_INTERNALF_LOOPING;
|
|
|
|
|
|
|
|
|
|
|
|
// If this buffer is emulated on the WAVE APIs
|
|
// then create and use that buffer
|
|
if( pds->fdwInternal & DS_INTERNALF_WAVEEMULATED ) {
|
|
if( dwForce & DSBCAPS_LOCHARDWARE )
|
|
{
|
|
RPF("IDirectSound::CreateSoundBuffer - Can't create a hardware buffer when using wave emulation.");
|
|
hrReturn = DSERR_INVALIDCALL;
|
|
goto CREATE_ERROR_FORMAT;
|
|
}
|
|
|
|
// If the WAVEBLTDST flag is not set, you are not allowed
|
|
// to WaveBlt into this buffer!
|
|
if(!( dsbdTemp.dwFlags & DSBCAPS_CTRLWAVEBLTDST )) {
|
|
// We will not need a mix buffer
|
|
pdsb->pMixBuffer = NULL;
|
|
} else {
|
|
// Buffer will have stuff mixed into it....
|
|
// Default mix buffer size of 32 K.
|
|
cbMixBufferSize = 0x00008000;
|
|
|
|
pdsb->cbMixBufferSize = cbMixBufferSize;
|
|
pdsb->pMixBuffer = (LPBYTE)MemAlloc(cbMixBufferSize);
|
|
if(NULL == pdsb->pMixBuffer) {
|
|
DPF(1,"IDSHWCreateSoundBuffer mix buffer alloc fail");
|
|
goto CREATE_ERROR_FORMAT;
|
|
}
|
|
}
|
|
|
|
hr = WaveEmulateCreateSoundBuffer( pds, pdsb, &dsbdTemp );
|
|
|
|
// Set the grace mixer info
|
|
pdsb->cSamples = pdsb->cbBufferSize / pdsb->pwfx->nBlockAlign;
|
|
|
|
switch (pdsb->pwfx->nBlockAlign) {
|
|
case 1:
|
|
pdsb->uBlockAlignShift = 0;
|
|
break;
|
|
case 2:
|
|
pdsb->uBlockAlignShift = 1;
|
|
break;
|
|
case 4:
|
|
pdsb->uBlockAlignShift = 2;
|
|
break;
|
|
default:
|
|
// Unsupported block align
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
// For emulated primary buffers, we don't use an alias,
|
|
// we use the real thing
|
|
pdsbe->pDSBufferAlias = NULL;
|
|
|
|
DsbeDeactivateIfNecessary (pdsbe);
|
|
|
|
*ppdsbe = pdsbe;
|
|
|
|
if( hr == DS_OK ) {
|
|
return DS_OK;
|
|
} else {
|
|
hrReturn = hr;
|
|
goto CREATE_ERROR_MIX;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
DPF(3," Check for try HW buffers %ld ", dsc.dwFreeHwMixingAllBuffers );
|
|
|
|
if (!(DSBCAPS_LOCSOFTWARE & dwForce)) {
|
|
|
|
// if it's static, see if there are _any_ free HwMixingBuffers,
|
|
// otherwise see if there are any free _streaming_ buffers
|
|
if ( ((DSBCAPS_STATIC & dsbdTemp.dwFlags) && (dsc.dwFreeHwMixingAllBuffers > 0)) ||
|
|
(dsc.dwFreeHwMixingStreamingBuffers > 0) )
|
|
{
|
|
|
|
DPF(3," Check for try HW FLAGS %X ", dsc.dwFlags );
|
|
DPF(3," Check for try HW format chann %d ", pdsb->pwfx->nChannels);
|
|
DPF(3," Check for try HW format bits %d ",pdsb->pwfx->wBitsPerSample);
|
|
|
|
// Card has secondary buffers in HW
|
|
if( ( ( (pdsb->pwfx->nChannels == 1) &&
|
|
(dsc.dwFlags & DSCAPS_SECONDARYMONO) ) ||
|
|
( (pdsb->pwfx->nChannels == 2) &&
|
|
(dsc.dwFlags & DSCAPS_SECONDARYSTEREO) ) ) &&
|
|
( ( (pdsb->pwfx->wBitsPerSample == 8) &&
|
|
(dsc.dwFlags & DSCAPS_SECONDARY8BIT) ) ||
|
|
( (pdsb->pwfx->wBitsPerSample == 16) &&
|
|
(dsc.dwFlags & DSCAPS_SECONDARY16BIT) ) ) )
|
|
{
|
|
// Card can support mono/stereo format requested
|
|
// Card can support 8/16 bit format requested
|
|
DPF(3," Try a hardware buffer" );
|
|
|
|
// If it was a primary this flag was already set
|
|
dsbdTemp.dwFlags |= DSBCAPS_LOCHARDWARE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check whether this is a primary or HW buffer. If
|
|
// so then try to use the HAL code
|
|
//
|
|
fTryHardware = (0 != (dsbdTemp.dwFlags & DSBCAPS_LOCHARDWARE));
|
|
|
|
fTrySoftware = !(DSBCAPS_PRIMARYBUFFER & dsbdTemp.dwFlags) &&
|
|
!(DSBCAPS_LOCHARDWARE & dwForce);
|
|
|
|
if (!fTryHardware && !fTrySoftware) {
|
|
hrReturn = DSERR_INVALIDCALL;
|
|
goto CREATE_ERROR_FORMAT;
|
|
}
|
|
|
|
|
|
if (fTryHardware) {
|
|
// in order to implement sound focus with secondary hardware
|
|
// buffers, we need CTRLVOLUME on them.
|
|
if (0 == (DSBCAPS_PRIMARYBUFFER & dsbdTemp.dwFlags)) {
|
|
dsbdTemp.dwFlags |= DSBCAPS_CTRLVOLUME;
|
|
}
|
|
|
|
hrReturn = DsCreateHardwareBuffer(pds, pdsb, &dsbdTemp, &fTrySoftware);
|
|
if (DS_OK == hrReturn) fTrySoftware = FALSE;
|
|
}
|
|
|
|
if (fTrySoftware) hrReturn = DsCreateSoftwareBuffer(pds, pdsb, &dsbdTemp);
|
|
|
|
if (DS_OK != hrReturn) goto CREATE_ERROR_FORMAT;
|
|
|
|
//
|
|
// If the creating app doesn't have sound focus then we need to
|
|
// immediately deactivate the new buffer
|
|
//
|
|
DsbeDeactivateIfNecessary (pdsbe);
|
|
|
|
//
|
|
//
|
|
//
|
|
DsbFillSilence( pdsb );
|
|
|
|
// If the WAVEBLTDST flag is not set, you are not allowed
|
|
// to WaveBlt into this buffer!
|
|
if(!( dsbdTemp.dwFlags & DSBCAPS_CTRLWAVEBLTDST )) {
|
|
// We will not need a mix buffer
|
|
pdsb->pMixBuffer = NULL;
|
|
} else {
|
|
// Buffer will have stuff mixed into it....
|
|
|
|
// If we've just created the hardware primary buffer, then let's
|
|
// allocate a mixer buffer that is certainly large enough
|
|
if (DSB_INTERNALF_PRIMARY & pdsb->fdwDsbI) {
|
|
// May need up to 4X the buffer size
|
|
cbMixBufferSize = pdsb->cbBufferSize * 4;
|
|
} else {
|
|
// Default mix buffer size of 32 K.
|
|
cbMixBufferSize = 0x00008000;
|
|
}
|
|
pdsb->cbMixBufferSize = cbMixBufferSize;
|
|
pdsb->pMixBuffer = (LPBYTE)MemAlloc(cbMixBufferSize);
|
|
if(NULL == pdsb->pMixBuffer) {
|
|
DPF(1,"IDSHWCreateSoundBuffer mix buffer alloc fail");
|
|
goto CREATE_ERROR_MIX;
|
|
}
|
|
}
|
|
|
|
// Set the grace mixer info
|
|
pdsb->cSamples = pdsb->cbBufferSize / pdsb->pwfx->nBlockAlign;
|
|
|
|
switch (pdsb->pwfx->nBlockAlign) {
|
|
case 1:
|
|
pdsb->uBlockAlignShift = 0;
|
|
break;
|
|
case 2:
|
|
pdsb->uBlockAlignShift = 1;
|
|
break;
|
|
case 4:
|
|
pdsb->uBlockAlignShift = 2;
|
|
break;
|
|
default:
|
|
// Unsupported block align
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
// If this is the first buffer for this app, and it wasn't a
|
|
// primary buffer, and this app has focus, then we need to set
|
|
// the format of the primary buffer.
|
|
if ((NULL == pdsbe->pNext) && (!(DSB_INTERNALF_PRIMARY & pdsbe->pdsb->fdwDsbI)))
|
|
{
|
|
if( pdse->tidSound == gpdsinfo->tidSoundFocus || pdse->tidSound == gpdsinfo->tidStuckFocus ) {
|
|
HRESULT hr;
|
|
if (NULL != pdse->pwfxApp) {
|
|
hr = IDsbSetFormatI( pds->pdsbPrimary, pdse->pwfxApp, 0 );
|
|
} else {
|
|
hr = IDsbSetFormatI( pds->pdsbPrimary, &pds->wfxDefault, 0 );
|
|
}
|
|
|
|
if( DS_OK != hr )
|
|
{
|
|
DPF( 0, "Couldn't set primary format when creating primary buffer.");
|
|
}
|
|
}
|
|
}
|
|
|
|
// If this is a primary buffer, then create an alias pointer to the data
|
|
// buffer. Note that this only reserves linear address space. Physical
|
|
// memory is not commited. For secondary buffers, we use the internal
|
|
// buffer ptr.
|
|
ASSERT(!(pds->fdwInternal & DS_INTERNALF_WAVEEMULATED));
|
|
if (DSB_INTERNALF_PRIMARY & pdsb->fdwDsbI) {
|
|
pdsbe->pDSBufferAlias = vxdMemReserveAlias(pdsb->pDSBuffer, pdsb->cbBufferSize);
|
|
} else {
|
|
pdsbe->pDSBufferAlias = NULL;
|
|
}
|
|
|
|
DPF(2, "Return buffer ext %X, core %X mem ptr %X size %X",
|
|
pdsbe, pdsb, pdsb->pDSBuffer ,pdsb->cbBufferSize );
|
|
|
|
// Return pointer to new buffer
|
|
*ppdsbe = pdsbe;
|
|
|
|
DPF(3,"IDSHWCreateSoundBuffer Exit");
|
|
return DS_OK;
|
|
|
|
|
|
|
|
|
|
|
|
CREATE_ERROR_MIX:
|
|
if( pdsb->pMixBuffer != NULL ) {
|
|
MemFree(pdsb->pMixBuffer);
|
|
}
|
|
|
|
CREATE_ERROR_FORMAT:
|
|
MemFree(pdsb->pwfx);
|
|
|
|
CREATE_ERROR_PROCESS:
|
|
MemFree(pdsb->plProcess);
|
|
|
|
CREATE_ERROR_BUFFER:
|
|
// if this buffer was added to the pDS obj list, remove it
|
|
if(pds->pdsb == pdsb)
|
|
{
|
|
pds->pdsb = pdsb->pNext;
|
|
} else {
|
|
for(pdsb1 = pds->pdsb; pdsb1 != NULL; pdsb1 = pdsb1->pNext)
|
|
{
|
|
if(pdsb1->pNext == pdsb) {
|
|
pdsb1->pNext = pdsb->pNext;
|
|
pdsb->pNext = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
DPF(1, "Freeing DSBUFFER obj 0x%8x",pdsb);
|
|
pdsb->dwSig = 0xdeaddead;
|
|
MemFree(pdsb);
|
|
|
|
CREATE_ERROR_BUFFEREXTERNAL:
|
|
if( pdsbe == pds->pdsbePrimary ) {
|
|
// This is the original alloc of the primary
|
|
// clean up DS obj
|
|
pds->pdsbPrimary = NULL;
|
|
pds->pdsbePrimary = NULL;
|
|
}
|
|
|
|
// If this buffer was added to the pDSE list, remove it
|
|
if(pdse->pdsbe == pdsbe)
|
|
{
|
|
pdse->pdsbe = pdsbe->pNext;
|
|
} else {
|
|
for(pdsbe1 = pdse->pdsbe; pdsbe1 != NULL; pdsbe1 = pdsbe1->pNext)
|
|
{
|
|
if(pdsbe1->pNext == pdsbe) {
|
|
pdsbe1->pNext = pdsbe->pNext;
|
|
pdsbe->pNext = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we allocated a wave format, free it.
|
|
if (NULL != pdse->pwfxApp) MemFree(pdse->pwfxApp);
|
|
|
|
//
|
|
MemFree(pdsbe);
|
|
|
|
CREATE_ERROR_LAST:
|
|
|
|
if( hrReturn != DS_OK ) {
|
|
return hrReturn;
|
|
} else {
|
|
return DSERR_OUTOFMEMORY;
|
|
}
|
|
|
|
}
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// LPDIRECTSOUNDBUFFER IDSHWCreateSoundBuffer
|
|
//
|
|
// Description:
|
|
// This function is the member function for CreateSoundBuffer.
|
|
//
|
|
// Arguments:
|
|
// LPDIRECTSOUND pids: Pointer to Direct Sound Object.
|
|
//
|
|
// LPDSBUFFERCREATE pdsbc: Pointer to a DSBufferCreate structure.
|
|
//
|
|
// Return (LPDSBUFFER):
|
|
// Pointer to a DSBUFFER structure.
|
|
// Return (HRESULT):
|
|
// DSERR_UNINITIALIZED if object has not be initialized
|
|
//
|
|
// History:
|
|
// 02/01/95 Fwong Making sense out of non-sense.
|
|
// 11/30/95 angusm Added creation of Focus Thread.
|
|
// 02/11/96 angusm Added check for initialization
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
HRESULT FAR PASCAL IDSHWCreateSoundBuffer
|
|
(
|
|
LPDIRECTSOUND pids,
|
|
LPDSBUFFERDESC pdsbd,
|
|
LPLPDIRECTSOUNDBUFFER lplpDirectSoundBuffer,
|
|
IUnknown FAR *pUnkOuter
|
|
)
|
|
{
|
|
LPDSBUFFEREXTERNAL pdsbe;
|
|
LPDSOUNDEXTERNAL pdse;
|
|
HRESULT hrReturn;
|
|
|
|
DPF(3,"IDSHWCreateSoundBuffer");
|
|
if( !VALID_DWORD_PTR(lplpDirectSoundBuffer) ) {
|
|
RPF("IDirectSound::CreateSoundBuffer - Invalid lplpDirectSoundBuffer");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
*lplpDirectSoundBuffer = NULL;
|
|
|
|
if( !VALID_DSOUNDE_PTR(pids) ) {
|
|
RPF("IDirectSound::CreateSoundBuffer - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
pdse = (LPDSOUNDEXTERNAL)pids;
|
|
|
|
/* Check to see if the object is initialized */
|
|
if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) {
|
|
RPF("Direct Sound Object is uninitialized.");
|
|
return DSERR_UNINITIALIZED;
|
|
}
|
|
|
|
if( !VALID_DSOUND_PTR(pdse->pds) || (0 == pdse->uRefCount)) {
|
|
RPF("IDirectSound::CreateSoundBuffer - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
|
|
if( !VALID_DSBUFFERDESC_PTR(pdsbd) ) {
|
|
RPF("IDirectSound::CreateSoundBuffer - Invalid Buffer Description or dwSize member.");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
if( 0 != pdsbd->dwReserved ) {
|
|
RPF("IDirectSound::CreateSoundBuffer - DSBUFFERDESC.dwReserved must be zero.");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
if( pdsbd->dwFlags & (~DSBCAPS_VALIDFLAGS)) {
|
|
RPF("IDirectSound::CreateSoundBuffer - Invalid CAPS flags sent to CreateSoundBuffer");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
if( (DSBCAPS_LOCHARDWARE | DSBCAPS_LOCSOFTWARE) ==
|
|
(pdsbd->dwFlags & (DSBCAPS_LOCHARDWARE | DSBCAPS_LOCSOFTWARE)) ) {
|
|
RPF("IDirectSound::CreateSoundBuffer - Both DSBCAPS_LOCHARDWARE and DSBCAPS_LOCSOFTWARE flags were specified: failing");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
if( pUnkOuter != NULL ) {
|
|
RPF("IDirectSound::CreateSoundBuffer - pUnkOuter must be NULL for this rev!");
|
|
return DSERR_NOAGGREGATION;
|
|
}
|
|
|
|
|
|
// If this is not a primary then format must be set
|
|
if( !(pdsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) ) {
|
|
if( !VALID_WAVEFORMATEX_PTR((pdsbd->lpwfxFormat)) ) {
|
|
RPF("IDirectSound::CreateSoundBuffer - Invalid Format pointer");
|
|
return DSERR_BADFORMAT;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If this is a primary, then we only allow DSBCAPS_CTRLVOLUME and
|
|
// DSBCAPS_CTRLPAN controls on it. In fact, we should really mask off
|
|
// this flag before we call down to the driver to create the buffer
|
|
// (just in case the call were to fail), but in fact the primary will be
|
|
// created by DirectSoundCreate without that control, and subsequent
|
|
// creations will simply create a new external object, so we're OK for
|
|
// now. REMIND HACKHACK BUGBUG fix this.
|
|
//
|
|
if( pdsbd->dwFlags & DSBCAPS_PRIMARYBUFFER )
|
|
{
|
|
if( pdsbd->dwFlags & DSBCAPS_CTRLFREQUENCY ) {
|
|
RPF("IDirectSound::CreateSoundBuffer - Primary buffers don't support frequency control.");
|
|
return DSERR_CONTROLUNAVAIL;
|
|
}
|
|
if( pdsbd->dwFlags & DSBCAPS_STATIC ) {
|
|
RPF("IDirectSound::CreateSoundBuffer - Primary buffers can't be static!");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
}
|
|
|
|
|
|
ENTER_DLL_CSECT();
|
|
hrReturn = DseCreateDsbe(pdse, pdsbd, &pdsbe);
|
|
LEAVE_DLL_CSECT();
|
|
|
|
*lplpDirectSoundBuffer = (LPDIRECTSOUNDBUFFER)pdsbe;
|
|
return hrReturn;
|
|
|
|
} // IDSHWCreateSoundBuffer()
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// LPDSBUFFER IDuplicateSoundBuffer
|
|
//
|
|
// Description:
|
|
// This function is the member function for DuplicateSoundBuffer.
|
|
//
|
|
// Arguments:
|
|
// LPDIRECTSOUND pids: Pointer to Direct Sound Object.
|
|
//
|
|
// LPDSBUFFERCREATE pdsbc: Pointer to a DSBufferCreate structure.
|
|
//
|
|
// Return (LPDSBUFFER):
|
|
// Pointer to a DSBUFFER structure.
|
|
// Return (HRESULT):
|
|
// DSERR_UNINITIALIZED if object has not be initialized
|
|
//
|
|
// History:
|
|
// 02/01/95 Fwong Making sense out of non-sense.
|
|
// 02/11/96 angusm Added check for initialization
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
HRESULT FAR PASCAL IDSHWDuplicateSoundBuffer
|
|
(
|
|
LPDIRECTSOUND pids,
|
|
LPDIRECTSOUNDBUFFER pidsbCurrent,
|
|
LPLPDIRECTSOUNDBUFFER ppidsbD
|
|
)
|
|
{
|
|
LPDSBUFFER pdsbCurrent;
|
|
LPDSBUFFEREXTERNAL pdsbeCurrent;
|
|
LPDSBUFFER pdsbNew;
|
|
LPDSBUFFEREXTERNAL pdsbeNew;
|
|
LPDSOUND pds;
|
|
LPDSOUNDEXTERNAL pdse;
|
|
|
|
DWORD cbMixBufferSize;
|
|
DWORD cbFormatSize;
|
|
DWORD dw;
|
|
LPDSPROCESS pDSPID;
|
|
DSCAPS dsc;
|
|
HRESULT hrReturn;
|
|
HRESULT hr;
|
|
|
|
DPF(3,"IDSHWDuplicateSoundBuffer");
|
|
if( !VALID_DSOUNDE_PTR(pids) ) {
|
|
RPF("IDirectSound::DuplicateSoundBuffer - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
pdse = (LPDSOUNDEXTERNAL)pids;
|
|
|
|
/* Check to see if the object is initialized */
|
|
if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) {
|
|
RPF("Direct Sound Object is uninitialized.");
|
|
return DSERR_UNINITIALIZED;
|
|
}
|
|
|
|
pds = pdse->pds;
|
|
if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) {
|
|
RPF("IDirectSound::DuplicateSoundBuffer - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
if( !VALID_DSBUFFERE_PTR(pidsbCurrent) ) {
|
|
RPF("IDirectSound::DuplicateSoundBuffer - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
pdsbeCurrent = (LPDSBUFFEREXTERNAL)pidsbCurrent;
|
|
pdsbCurrent = pdsbeCurrent->pdsb;
|
|
if( !VALID_DSBUFFER_PTR(pdsbCurrent) || (0 == pdsbeCurrent->uRefCount)) {
|
|
RPF("IDirectSound::DuplicateSoundBuffer - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
ASSERT( DSBUFFSIG == pdsbCurrent->dwSig );
|
|
|
|
|
|
// If this is a primary then return failure
|
|
if( pdsbCurrent == pds->pdsbPrimary ) {
|
|
RPF("IDirectSound::DuplicateSoundBuffer - Can not duplicate primary");
|
|
return DSERR_INVALIDCALL;
|
|
}
|
|
|
|
|
|
hrReturn = DS_OK;
|
|
*ppidsbD = NULL;
|
|
|
|
|
|
|
|
ENTER_DLL_CSECT();
|
|
|
|
|
|
cbMixBufferSize = pdsbCurrent->cbMixBufferSize;
|
|
|
|
|
|
|
|
// Allocate the external dsb object
|
|
pdsbeNew = (LPDSBUFFEREXTERNAL)MemAlloc(sizeof(DSBUFFEREXTERNAL));
|
|
if(NULL == pdsbeNew) {
|
|
DPF(0,"DuplicateSoundBuffer object (External) alloc fail");
|
|
goto DUPLICATE_ERROR_LAST;
|
|
}
|
|
|
|
pdsbeNew->lpVtbl = gpdsinfo->lpVtblDSb;
|
|
pdsbeNew->pdse = pdse;
|
|
pdsbeNew->uRefCount = 1;
|
|
pdsbeNew->dwPID = GetCurrentProcessId();
|
|
pdsbeNew->dwPriority = pdsbeCurrent->dwPriority;
|
|
|
|
pdsbeNew->pNext = pdse->pdsbe;
|
|
pdse->pdsbe = pdsbeNew;
|
|
|
|
|
|
|
|
// We are not going to dup primaryies no need to mess
|
|
|
|
// Allocate the dsb object
|
|
pdsbNew = (LPDSBUFFER)MemAlloc(sizeof(DSBUFFER));
|
|
if(NULL == pdsbNew) {
|
|
DPF(0,"DuplicateSoundBuffer object alloc fail");
|
|
goto DUPLICATE_ERROR_BUFFEREXTERNAL;
|
|
}
|
|
|
|
pdsbNew->dwSig = DSBUFFSIG;
|
|
DPF(1, "Allocating DSBUFFER obj 0x%8x",pdsbNew);
|
|
|
|
// Point external object to this
|
|
pdsbeNew->pdsb = pdsbNew;
|
|
|
|
|
|
pdsbNew->fdwBufferDesc = pdsbCurrent->fdwBufferDesc;
|
|
pdsbNew->dwPrimaryNumber = pdsbCurrent->dwPrimaryNumber;
|
|
pdsbNew->pDSBuffer = pdsbCurrent->pDSBuffer;
|
|
pdsbNew->cbBufferSize = pdsbCurrent->cbBufferSize;
|
|
pdsbNew->dwCardAddress = pdsbCurrent->dwCardAddress;
|
|
pdsbNew->pdsb3d = NULL;
|
|
pdsbNew->pds = pds;
|
|
|
|
pdsbNew->fdwDsbI = pdsbCurrent->fdwDsbI;
|
|
pdsbNew->fdwDsbI |= DSB_INTERNALF_STOP;
|
|
pdsbNew->fdwDsbI &= ~DSB_INTERNALF_LOOPING;
|
|
|
|
pdsbNew->uRefCount = 1;
|
|
|
|
pdsbNew->pNext = pds->pdsb;
|
|
pds->pdsb = pdsbNew;
|
|
pdsbNew->pdsbDuplicateNext = pdsbCurrent;
|
|
pdsbNew->pdsbDuplicatePrev = pdsbCurrent->pdsbDuplicatePrev;
|
|
pdsbCurrent->pdsbDuplicatePrev->pdsbDuplicateNext = pdsbNew;
|
|
pdsbCurrent->pdsbDuplicatePrev = pdsbNew;
|
|
|
|
|
|
|
|
// Set up hack internal use external objct
|
|
// This will be used by the mixer for calling
|
|
// methods like stop
|
|
// It will never be used for addref or release
|
|
pdsbNew->dsbe.lpVtbl = gpdsinfo->lpVtblDSb;
|
|
pdsbNew->dsbe.pdse = pdse;
|
|
pdsbNew->dsbe.uRefCount = 1;
|
|
pdsbNew->dsbe.dwPID = DWBUFFER_INTERNAL_PID;
|
|
pdsbNew->dsbe.pNext = NULL;
|
|
pdsbNew->dsbe.pdsb = pdsbNew;
|
|
pdsbNew->dsbe.dwPriority = pdsbeCurrent->dwPriority;
|
|
|
|
|
|
|
|
//Allocate the mix buffer
|
|
pdsbNew->cbMixBufferSize = cbMixBufferSize;
|
|
pdsbNew->pMixBuffer = NULL;
|
|
if( cbMixBufferSize != 0 ) {
|
|
pdsbNew->pMixBuffer = (LPBYTE)MemAlloc(cbMixBufferSize);
|
|
if(NULL == pdsbNew->pMixBuffer) {
|
|
DPF(1,"IDSHWDuplicateSoundBuffer mix buffer alloc fail");
|
|
goto DUPLICATE_ERROR_BUFFER;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Alloc Process ID list
|
|
pDSPID = (LPDSPROCESS)MemAlloc(sizeof(DSPROCESS));
|
|
if(NULL == pDSPID) {
|
|
DPF(1,"IDSDuplicateSoundBuffer process alloc fail");
|
|
goto DUPLICATE_ERROR_MIX;
|
|
}
|
|
pDSPID->dwPID = HackGetCurrentProcessId();
|
|
pDSPID->dwProcessRefCount = 1;
|
|
pDSPID->pNext = NULL;
|
|
pdsbNew->plProcess = pDSPID;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Allocate a buffer to hold the format
|
|
dw = SIZEOF_WAVEFORMATEX( pdsbCurrent->pwfx );
|
|
cbFormatSize = dw + 16;
|
|
|
|
|
|
// Now allocate memory
|
|
pdsbNew->pwfx = NULL;
|
|
pdsbNew->pwfx = (LPWAVEFORMATEX)MemAlloc(cbFormatSize);
|
|
if(NULL == pdsbNew->pwfx) {
|
|
DPF(1,"IDSHWDuplicateSoundBuffer object alloc fail");
|
|
goto DUPLICATE_ERROR_PROCESS;
|
|
}
|
|
|
|
dw = SIZEOF_WAVEFORMATEX( pdsbCurrent->pwfx );
|
|
CopyMemory( pdsbNew->pwfx, pdsbCurrent->pwfx, dw );
|
|
|
|
//Set the helInfo stuff
|
|
pdsbNew->helInfo.dwSampleRate = pdsbNew->pwfx->nSamplesPerSec;
|
|
pdsbNew->helInfo.hfFormat = pdsbCurrent->helInfo.hfFormat;
|
|
pdsbNew->helInfo.lVolume = pdsbCurrent->helInfo.lVolume;
|
|
pdsbNew->helInfo.lPan = pdsbCurrent->helInfo.lPan;
|
|
pdsbNew->helInfo.dwLVolume = pdsbCurrent->helInfo.dwLVolume;
|
|
pdsbNew->helInfo.dwRVolume = pdsbCurrent->helInfo.dwRVolume;
|
|
pdsbNew->helInfo.dwMVolume = pdsbCurrent->helInfo.dwMVolume;
|
|
|
|
|
|
// No need to handle Wave Emulated separatly since primary
|
|
// is the same and emulated are the same as other emulated
|
|
|
|
|
|
|
|
// If the other buffer is HW then see if we can make this one.
|
|
if (DSB_INTERNALF_HARDWARE & pdsbCurrent->fdwDsbI) {
|
|
// Check to see if we can make this a HW buffer
|
|
// Get the caps and see if we have it free....
|
|
dsc.dwSize = sizeof( DSCAPS );
|
|
hr = DsGetCaps(pdse->pds, &dsc);
|
|
ASSERT (DS_OK == hr);
|
|
if( (dsc.dwFreeHwMixingAllBuffers > 0 ) ) {
|
|
DPF(3," Check for try HW FLAGS %X ", dsc.dwFlags );
|
|
DPF(3," Check for try HW format chann %d ",
|
|
pdsbNew->pwfx->nChannels);
|
|
DPF(3," Check for try HW format bits %d ",
|
|
pdsbNew->pwfx->wBitsPerSample);
|
|
// Card has secondary buffers in HW
|
|
// We are OK
|
|
} else {
|
|
// Other buffer is in HW and we can not make this one in HW
|
|
// Fail call.
|
|
RPF("IDirectSound::DuplicateSoundBuffer - HW does not have resources to duplicate");
|
|
hrReturn = DSERR_ALLOCATED;
|
|
goto DUPLICATE_ERROR_FORMAT;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check to see if this is a primary or HW buffer
|
|
// If so then use the HAL code
|
|
if (DSB_INTERNALF_HARDWARE & pdsbCurrent->fdwDsbI) {
|
|
|
|
// We are asking for a HW buffer...
|
|
|
|
DPF(3,"HW Duplicate HW buffer");
|
|
DPF(3,"HW Duplicate - sample rate %d", pdsbNew->helInfo.dwSampleRate );
|
|
DPF(3,"HW Duplicate - hfForamt %X", pdsbNew->helInfo.hfFormat );
|
|
|
|
hrReturn = vxdDrvDuplicateSoundBuffer( pds->hHal,
|
|
pdsbCurrent->hBuffer,
|
|
&pdsbNew->hBuffer );
|
|
|
|
if (DS_OK != hrReturn) {
|
|
DPF(1,"IDSHWDuplicateSoundBuffer buffer alloc fail");
|
|
goto DUPLICATE_ERROR_FORMAT;
|
|
}
|
|
|
|
ASSERT(NULL != pdsbNew->hBuffer);
|
|
DPF(3,"IDSHWDuplicateSoundBuffer buffer alloc OK");
|
|
|
|
} else {
|
|
|
|
DPF(3,"HW Duplicate Emulated secondary buffer");
|
|
}
|
|
|
|
// Should be using same snd data buffer
|
|
ASSERT(pdsbNew->pDSBuffer == pdsbCurrent->pDSBuffer);
|
|
ASSERT(pdsbNew->cbBufferSize == pdsbCurrent->cbBufferSize);
|
|
|
|
DPF(3,"--------Dup data for Emulated obj %X buff %X len %X",
|
|
pdsbNew, pdsbNew->pDSBuffer, pdsbNew->cbBufferSize );
|
|
|
|
//
|
|
// If the creating app doesn't have sound focus then we need to
|
|
// immediately deactivate the new buffer
|
|
//
|
|
DsbeDeactivateIfNecessary (pdsbeNew);
|
|
|
|
// Set the grace info
|
|
pdsbNew->cSamples = pdsbNew->cbBufferSize / pdsbNew->pwfx->nBlockAlign;
|
|
|
|
switch (pdsbNew->pwfx->nBlockAlign) {
|
|
case 1:
|
|
pdsbNew->uBlockAlignShift = 0;
|
|
break;
|
|
case 2:
|
|
pdsbNew->uBlockAlignShift = 1;
|
|
break;
|
|
case 4:
|
|
pdsbNew->uBlockAlignShift = 2;
|
|
break;
|
|
default:
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
*ppidsbD = (LPDIRECTSOUNDBUFFER)pdsbeNew;
|
|
|
|
DPF(3,"IDSHWDuplicateSoundBuffer Exit");
|
|
LEAVE_DLL_CSECT( );
|
|
return DS_OK;
|
|
|
|
|
|
|
|
|
|
|
|
//DUPLICATE_ERROR_DATA:
|
|
// Do not free since we are duplicating
|
|
DUPLICATE_ERROR_FORMAT:
|
|
MemFree(pdsbNew->pwfx);
|
|
|
|
DUPLICATE_ERROR_PROCESS:
|
|
MemFree(pdsbNew->plProcess);
|
|
|
|
DUPLICATE_ERROR_MIX:
|
|
if( pdsbNew->pMixBuffer != NULL ) {
|
|
MemFree(pdsbNew->pMixBuffer);
|
|
}
|
|
|
|
DUPLICATE_ERROR_BUFFER:
|
|
// Remove it from duplicate list
|
|
pdsbNew->pdsbDuplicateNext->pdsbDuplicatePrev = pdsbNew->pdsbDuplicatePrev;
|
|
pdsbNew->pdsbDuplicatePrev->pdsbDuplicateNext = pdsbNew->pdsbDuplicateNext;
|
|
|
|
// Remove from buffer list (inserted at head)
|
|
//
|
|
ASSERT(pds->pdsb == pdsbNew);
|
|
pds->pdsb = pds->pdsb->pNext;
|
|
|
|
DPF(1, "Freeing DSBUFFER obj 0x%8x",pdsbNew);
|
|
pdsbNew->dwSig = 0xdeaddead;
|
|
MemFree(pdsbNew);
|
|
|
|
DUPLICATE_ERROR_BUFFEREXTERNAL:
|
|
// Unlink pdsbNew from external list (inserted at head)
|
|
//
|
|
ASSERT(pdse->pdsbe == pdsbeNew);
|
|
pdse->pdsbe = pdse->pdsbe->pNext;
|
|
|
|
MemFree(pdsbeNew);
|
|
|
|
DUPLICATE_ERROR_LAST:
|
|
|
|
LEAVE_DLL_CSECT( );
|
|
if( hrReturn != DS_OK ) {
|
|
return hrReturn;
|
|
} else {
|
|
return DSERR_OUTOFMEMORY;
|
|
}
|
|
|
|
} // IDSHWDuplicateSoundBuffer()
|
|
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// IDSHWEnumSoundBuffers
|
|
//
|
|
// Description:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Return (HRESULT):
|
|
// DSERR_UNINITIALIZED if object has not be initialized
|
|
//
|
|
// History:
|
|
// 02/11/96 angusm Added check for initialization
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
HRESULT FAR PASCAL IDSHWEnumSoundBuffers
|
|
(
|
|
LPDIRECTSOUND pids,
|
|
LPDSENUMBUFFERSCALLBACK pfnEnumCB,
|
|
LPVOID lpContext
|
|
)
|
|
{
|
|
LPDSOUND pds;
|
|
LPDSOUNDEXTERNAL pdse;
|
|
LPDSBUFFEREXTERNAL pdsbeSearch;
|
|
LPDSBUFFERDESC lpdsbd;
|
|
LPWAVEFORMATEX pwfx;
|
|
DWORD dw;
|
|
BOOL fRet;
|
|
|
|
if( !VALID_DSOUNDE_PTR(pids) ) {
|
|
RPF("IDirectSound::EnumSoundBuffers - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
pdse = (LPDSOUNDEXTERNAL)pids;
|
|
|
|
/* Check to see if the object is initialized */
|
|
if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) {
|
|
RPF("Direct Sound Object is uninitialized.");
|
|
return DSERR_UNINITIALIZED;
|
|
}
|
|
|
|
pds = pdse->pds;
|
|
if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) {
|
|
RPF("IDirectSound::EnumSoundBuffers - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
DPF(0,"Entering EnumSoundBuffers method");
|
|
|
|
ENTER_DLL_CSECT();
|
|
|
|
if(( lpdsbd = MemAlloc( sizeof(DSBUFFERDESC) )) == NULL ) {
|
|
RPF("IDirectSound::EnumSoundBuffers - Couldn't allocate DSBUFFERDESC structure!");
|
|
LEAVE_DLL_CSECT();
|
|
DPF(0,"Leaving EnumSoundBuffers method");
|
|
|
|
return DSERR_OUTOFMEMORY;
|
|
}
|
|
|
|
pdsbeSearch = pdse->pdsbe;
|
|
while( pdsbeSearch != NULL ) {
|
|
_fmemset( lpdsbd, 0, sizeof(DSBUFFERDESC));
|
|
|
|
// In case we want to use non-PCM format structures, we can do so,
|
|
// because we're allowing for
|
|
dw = SIZEOF_WAVEFORMATEX( pdsbeSearch->pdsb->pwfx ) + 16;
|
|
if(( pwfx = MemAlloc( dw )) == NULL ) {
|
|
RPF("IDirectSound::EnumSoundBuffers - Couldn't allocate WAVEFORMATEX structure!");
|
|
MemFree( lpdsbd );
|
|
LEAVE_DLL_CSECT();
|
|
DPF(0,"Leaving EnumSoundBuffers method");
|
|
|
|
return DSERR_OUTOFMEMORY;
|
|
}
|
|
|
|
dw = SIZEOF_WAVEFORMATEX( pdsbeSearch->pdsb->pwfx );
|
|
CopyMemory( pwfx, pdsbeSearch->pdsb->pwfx, dw );
|
|
|
|
lpdsbd->dwSize = sizeof(DSBUFFERDESC);
|
|
lpdsbd->cbBuffer = pdsbeSearch->pdsb->cbBufferSize;
|
|
lpdsbd->lpwfxFormat = pwfx;
|
|
lpdsbd->dwFlags = pdsbeSearch->pdsb->fdwBufferDesc;
|
|
lpdsbd->dwReserved = pdsbeSearch->pdsb->dwPrimaryNumber;
|
|
lpdsbd->lpDirectSoundBuffer = (LPDIRECTSOUNDBUFFER)pdsbeSearch;
|
|
|
|
pdsbeSearch->lpVtbl->AddRef((LPDIRECTSOUNDBUFFER)pdsbeSearch );
|
|
|
|
DPF(1,"EnumSoundBuffers: Calling enumeration callback");
|
|
fRet = pfnEnumCB( lpdsbd, lpContext );
|
|
|
|
MemFree( pwfx );
|
|
|
|
// Break out early if they told us not to continue...
|
|
if( fRet != TRUE ) {
|
|
DPF(1,"Ending enumeration prematurely");
|
|
break;
|
|
}
|
|
|
|
pdsbeSearch = pdsbeSearch->pNext;
|
|
}
|
|
|
|
MemFree( lpdsbd );
|
|
|
|
LEAVE_DLL_CSECT();
|
|
|
|
DPF(0,"Leaving EnumSoundBuffers method");
|
|
|
|
return DS_OK;
|
|
}
|
|
|
|
#endif // 0
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// DsGetCapsNative
|
|
//
|
|
// Description: This sets capabilty flags for supported hardware
|
|
//
|
|
// Arguments: pDSCaps pointer to capabilities structure to be filled
|
|
// pds pointer to direct sound object
|
|
//
|
|
// Concurrency: sequential
|
|
//
|
|
// Assumptions: pds->hHal, pds->dwHeapType, pds->DriverHeap
|
|
// are initialized
|
|
//
|
|
// Return (HRESULT): DS_OK Success
|
|
// DS_xxxxx on failure
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
HRESULT DsGetCapsNative
|
|
(
|
|
LPDSCAPS pDSCaps,
|
|
LPDSOUND pds
|
|
)
|
|
{
|
|
DSDRIVERCAPS drvCaps;
|
|
HRESULT hr;
|
|
|
|
DPF (2, "DsGetCapsNative: Enter Function");
|
|
DPF (2, "DsGetCapsNative: pDSCaps = %x", pDSCaps);
|
|
DPF (2, "DsGetCapsNative: pds = %x", pds);
|
|
|
|
ZeroMemory(&drvCaps, sizeof(drvCaps));
|
|
|
|
hr = vxdDrvGetCaps(pds->hHal, &drvCaps);
|
|
if (DS_OK != hr) {
|
|
DPF(0, "DsGetCapsNative: vxdDrvGetCaps failure");
|
|
return hr;
|
|
}
|
|
|
|
// Check flags returned from driver to see if they are all
|
|
// valid flags for a driver
|
|
if (drvCaps.dwFlags & (~DSCAPS_VALIDDRIVERFLAGS)) {
|
|
RPF ("Sound Device Driver may be broken. Returned non-valid"
|
|
"flags to Direct Sound: %x", drvCaps.dwFlags);
|
|
ASSERT (0);
|
|
}
|
|
|
|
pDSCaps->dwFlags = drvCaps.dwFlags & DSCAPS_VALIDDRIVERFLAGS;
|
|
pDSCaps->dwMinSecondarySampleRate = drvCaps.dwMinSecondarySampleRate;
|
|
pDSCaps->dwMaxSecondarySampleRate = drvCaps.dwMaxSecondarySampleRate;
|
|
pDSCaps->dwPrimaryBuffers = drvCaps.dwPrimaryBuffers;
|
|
pDSCaps->dwMaxHwMixingAllBuffers = drvCaps.dwMaxHwMixingAllBuffers;
|
|
pDSCaps->dwMaxHwMixingStaticBuffers = drvCaps.dwMaxHwMixingStaticBuffers;
|
|
pDSCaps->dwMaxHwMixingStreamingBuffers = drvCaps.dwMaxHwMixingStreamingBuffers;
|
|
pDSCaps->dwFreeHwMixingAllBuffers = drvCaps.dwFreeHwMixingAllBuffers;
|
|
pDSCaps->dwFreeHwMixingStaticBuffers = drvCaps.dwFreeHwMixingStaticBuffers;
|
|
pDSCaps->dwFreeHwMixingStreamingBuffers = drvCaps.dwFreeHwMixingStreamingBuffers;
|
|
pDSCaps->dwMaxHw3DAllBuffers = drvCaps.dwMaxHw3DAllBuffers;
|
|
pDSCaps->dwMaxHw3DStaticBuffers = drvCaps.dwMaxHw3DStaticBuffers;
|
|
pDSCaps->dwMaxHw3DStreamingBuffers = drvCaps.dwMaxHw3DStreamingBuffers;
|
|
pDSCaps->dwFreeHw3DAllBuffers = drvCaps.dwFreeHw3DAllBuffers;
|
|
pDSCaps->dwFreeHw3DStaticBuffers = drvCaps.dwFreeHw3DStaticBuffers;
|
|
pDSCaps->dwFreeHw3DStreamingBuffers = drvCaps.dwFreeHw3DStreamingBuffers;
|
|
pDSCaps->dwTotalHwMemBytes = drvCaps.dwTotalHwMemBytes;
|
|
|
|
pDSCaps->dwFreeHwMemBytes = drvCaps.dwFreeHwMemBytes;
|
|
pDSCaps->dwMaxContigFreeHwMemBytes = drvCaps.dwMaxContigFreeHwMemBytes;
|
|
|
|
//
|
|
// If we're using vmemmgr to manage the hw memory...
|
|
//
|
|
if ((DSDHEAP_CREATEHEAP == pds->dwHeapType) ||
|
|
(DSDHEAP_USEDIRECTDRAWHEAP == pds->dwHeapType))
|
|
{
|
|
ASSERT(pds->pDriverHeap);
|
|
pDSCaps->dwFreeHwMemBytes = VidMemAmountFree(pds->pDriverHeap);
|
|
pDSCaps->dwMaxContigFreeHwMemBytes = VidMemLargestFree(pds->pDriverHeap);
|
|
}
|
|
|
|
// REMIND what about these?
|
|
pDSCaps->dwUnlockTransferRateHwBuffers = 0;
|
|
pDSCaps->dwPlayCpuOverheadSwBuffers = 0;
|
|
|
|
DPF (2, "DsGetCapsNative: Leave Function");
|
|
return DS_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// DsGetCapsEmulated
|
|
//
|
|
// Description: This sets capability flags for emulated hardware
|
|
//
|
|
// Arguments: pDSCaps pointer to capabilities structure to be filled
|
|
// pds pointer to Direct Sound Object
|
|
//
|
|
// Concurrency: sequential
|
|
//
|
|
// Assumptions: pds->uDeviceID is initialized
|
|
//
|
|
// Return (HRESULT): DS_OK Success
|
|
// DSERR_GENERIC Error getting capabilities wave
|
|
// out device
|
|
// DS_xxxxx on failure
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
HRESULT DsGetCapsEmulated
|
|
(
|
|
LPDSCAPS pDSCaps,
|
|
LPDSOUND pds
|
|
)
|
|
{
|
|
WAVEOUTCAPS woc;
|
|
MMRESULT mmr;
|
|
|
|
DPF (2, "DsGetCapsEmulated: Enter Function");
|
|
DPF (2, "DsGetCapsEmulated: pDSCaps = %x", pDSCaps);
|
|
DPF (2, "DsGetCapsEmulated: pds = %x", pds);
|
|
|
|
mmr = waveOutGetDevCaps( pds->uDeviceID, &woc, sizeof(woc) );
|
|
if( mmr != MMSYSERR_NOERROR ) {
|
|
DPF (0, "DsGetCapsEmulated: WaveOutGetCaps failed");
|
|
return DSERR_GENERIC;
|
|
}
|
|
|
|
pDSCaps->dwFlags = 0;
|
|
|
|
if( woc.dwFormats & WAVE_FORMAT_1M08 ) {
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARYMONO;
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARY8BIT;
|
|
}
|
|
if( woc.dwFormats & WAVE_FORMAT_2M08 ) {
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARYMONO;
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARY8BIT;
|
|
}
|
|
if( woc.dwFormats & WAVE_FORMAT_4M08 ) {
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARYMONO;
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARY8BIT;
|
|
}
|
|
if( woc.dwFormats & WAVE_FORMAT_1S08 ) {
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARYSTEREO;
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARY8BIT;
|
|
}
|
|
if( woc.dwFormats & WAVE_FORMAT_2S08 ) {
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARYSTEREO;
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARY8BIT;
|
|
}
|
|
if( woc.dwFormats & WAVE_FORMAT_4S08 ) {
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARYSTEREO;
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARY8BIT;
|
|
}
|
|
if( woc.dwFormats & WAVE_FORMAT_1M16 ) {
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARYMONO;
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARY16BIT;
|
|
}
|
|
if( woc.dwFormats & WAVE_FORMAT_2M16 ) {
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARYMONO;
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARY16BIT;
|
|
}
|
|
if( woc.dwFormats & WAVE_FORMAT_4M16 ) {
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARYMONO;
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARY16BIT;
|
|
}
|
|
if( woc.dwFormats & WAVE_FORMAT_1S16 ) {
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARYSTEREO;
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARY16BIT;
|
|
}
|
|
if( woc.dwFormats & WAVE_FORMAT_2S16 ) {
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARYSTEREO;
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARY16BIT;
|
|
}
|
|
if( woc.dwFormats & WAVE_FORMAT_4S16 ) {
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARYSTEREO;
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARY16BIT;
|
|
}
|
|
if( woc.dwFormats & WAVE_FORMAT_1S16 ) {
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARYSTEREO;
|
|
pDSCaps->dwFlags |= DSCAPS_PRIMARY16BIT;
|
|
}
|
|
|
|
pDSCaps->dwFlags |= DSCAPS_EMULDRIVER;
|
|
pDSCaps->dwMinSecondarySampleRate = 100;
|
|
pDSCaps->dwMaxSecondarySampleRate = 100000;
|
|
pDSCaps->dwPrimaryBuffers = 1;
|
|
pDSCaps->dwMaxHwMixingAllBuffers = 0;
|
|
pDSCaps->dwMaxHwMixingStaticBuffers = 0;
|
|
pDSCaps->dwMaxHwMixingStreamingBuffers = 0;
|
|
pDSCaps->dwFreeHwMixingAllBuffers = 0;
|
|
pDSCaps->dwFreeHwMixingStaticBuffers = 0;
|
|
pDSCaps->dwFreeHwMixingStreamingBuffers = 0;
|
|
pDSCaps->dwMaxHw3DAllBuffers = 0;
|
|
pDSCaps->dwMaxHw3DStaticBuffers = 0;
|
|
pDSCaps->dwMaxHw3DStreamingBuffers = 0;
|
|
pDSCaps->dwFreeHw3DAllBuffers = 0;
|
|
pDSCaps->dwFreeHw3DStaticBuffers = 0;
|
|
pDSCaps->dwFreeHw3DStreamingBuffers = 0;
|
|
pDSCaps->dwTotalHwMemBytes = 0;
|
|
pDSCaps->dwFreeHwMemBytes = 0;
|
|
pDSCaps->dwMaxContigFreeHwMemBytes = 0;
|
|
|
|
// REMIND what about these?
|
|
pDSCaps->dwUnlockTransferRateHwBuffers = 0;
|
|
pDSCaps->dwPlayCpuOverheadSwBuffers = 0;
|
|
|
|
DPF (2, "DsGetCapsEmulated: Leave Function");
|
|
return DS_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// DsGetCaps
|
|
//
|
|
// Description: This function gets the capabilty flags for the given
|
|
// device.
|
|
//
|
|
// Arguments: pds pointer to direct sound object to retrieve
|
|
// capabilities on.
|
|
// pDSCaps pointer to capabilities structure to be filled
|
|
//
|
|
// Concurrency: synchronous
|
|
//
|
|
// Return (HRESULT): DS_OK on Success
|
|
// refer to DsGetCapsNative or DsGetCapsEmulated
|
|
// error codes
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
HRESULT DsGetCaps
|
|
(
|
|
LPDSOUND pds,
|
|
LPDSCAPS pDSCaps
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
DPF (2, "DsGetCaps: Enter Function");
|
|
DPF (2, "DsGetCaps: pds = %x", pds);
|
|
DPF (2, "DsGetCaps: pDSCaps = %x", pDSCaps);
|
|
|
|
ENTER_DLL_CSECT();
|
|
|
|
if( pds->fdwInternal & DS_INTERNALF_WAVEEMULATED ) {
|
|
DPF (3, "DsGetCaps: getting emulated capabilities");
|
|
hr = DsGetCapsEmulated (pDSCaps, pds);
|
|
} else {
|
|
DPF (3, "DsGetCaps: getting native capabilities");
|
|
hr = DsGetCapsNative (pDSCaps, pds);
|
|
}
|
|
|
|
if (DS_OK == hr) {
|
|
// Set driver version, as calculated on pds object creation.
|
|
pDSCaps->dwReserved1 = pds->dwDriverVersionMinor;
|
|
pDSCaps->dwReserved2 = pds->dwDriverVersionMajor;
|
|
|
|
// Set certified bit.
|
|
if( pds->fdwInternal & DS_INTERNALF_CERTIFIED ) {
|
|
pDSCaps->dwFlags |= DSCAPS_CERTIFIED;
|
|
}
|
|
DPF (3, "DsGetCaps: version information:");
|
|
DPF (3, "DsGetCaps: dwDriverVersionMinor = %d", pDSCaps->dwReserved1);
|
|
DPF (3, "DsGetCaps: dwDriverVersionMajor = %d", pDSCaps->dwReserved2);
|
|
DPF (3, "DsGetCaps: Certified? = %d",
|
|
pDSCaps->dwFlags & DSCAPS_CERTIFIED);
|
|
}
|
|
else {
|
|
DPF (1, "DsGetCaps: error getting capabilities (%d)", hr);
|
|
}
|
|
|
|
LEAVE_DLL_CSECT();
|
|
|
|
DPF (2, "DsGetCaps: Leave Function");
|
|
return hr;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// IDSHWGetCaps
|
|
//
|
|
// Description: Direct Sound Object member to determine hardware
|
|
// capabilities.
|
|
//
|
|
// Arguments: pids pointer to Direct Sound Object
|
|
// pDSCaps pointer to DS Capabilities structures
|
|
//
|
|
// Return (HRESULT):
|
|
// DS_OK Success
|
|
// DSERR_UNINITIALIZED if object has not be initialized
|
|
// DSERR_INVALIDPARAM a parameter could not be validated
|
|
// DSERR_GENERIC any error getting Capabilities
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
HRESULT FAR PASCAL IDSHWGetCaps
|
|
(
|
|
LPDIRECTSOUND pids,
|
|
LPDSCAPS pDSCaps
|
|
)
|
|
{
|
|
LPDSOUND pds;
|
|
LPDSOUNDEXTERNAL pdse;
|
|
HRESULT hr;
|
|
|
|
// Validation Code
|
|
if( !VALID_DSOUNDE_PTR(pids) ) {
|
|
RPF("IDirectSound::GetCaps - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
pdse = (LPDSOUNDEXTERNAL)pids;
|
|
/* Check to see if object has been initialized */
|
|
if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) {
|
|
RPF("Direct Sound Object is uninitialized.");
|
|
return DSERR_UNINITIALIZED;
|
|
}
|
|
pds = pdse->pds;
|
|
if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) {
|
|
RPF("IDirectSound::GetCaps - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
if( !VALID_DSCAPS_PTR(pDSCaps) ) {
|
|
RPF("IDirectSound::GetCaps - Invalid DSCAPS structure or dwSize member.");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
hr = DsGetCaps (pdse->pds, pDSCaps);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// DSDeactivateApp
|
|
//
|
|
// In: hWnd Handle of application to deactivate
|
|
// bSticky DSDEACTIVATEAPPF_NONSTICKY will deactivate only
|
|
// non-sticky buffers
|
|
// DSDEACTIVATEAPPF_ALL will deactivate all buffers
|
|
//--------------------------------------------------------------------------;
|
|
void DSDeactivateApp(DWORD tid, enum DSDeactivateAppF bSticky)
|
|
{
|
|
LPDSOUNDEXTERNAL pdse;
|
|
LPDSBUFFEREXTERNAL pdsbe;
|
|
|
|
DPF(0,"###### Deactivate tid %X ######", tid);
|
|
|
|
for (pdse = gpdsinfo->pDSoundExternalObj; NULL != pdse; pdse = pdse->pNext)
|
|
{
|
|
if (tid == pdse->tidSound) {
|
|
for (pdsbe = pdse->pdsbe; NULL != pdsbe; pdsbe = pdsbe->pNext)
|
|
{
|
|
if ((DSDEACTIVATEAPPF_ALL == bSticky) ||
|
|
(FALSE == (DSBCAPS_STICKYFOCUS & pdsbe->pdsb->fdwBufferDesc)))
|
|
{
|
|
DSBufferDeactivate(pdsbe);
|
|
}
|
|
}
|
|
if (!(pdse->pds->fdwInternal & DS_INTERNALF_WAVEEMULATED)) {
|
|
mxSignalRemix(pdse->pds, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// DSActivateApp
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
void DSActivateApp(DWORD tid)
|
|
{
|
|
LPDSOUND pds;
|
|
LPDSBUFFER pdsb;
|
|
LPDSOUNDEXTERNAL pdse;
|
|
LPDSBUFFEREXTERNAL pdsbe;
|
|
DWORD dwActivePrio;
|
|
BOOL fWritePrimary;
|
|
|
|
//
|
|
// Find the highest priority setting of all pdse under this hwnd
|
|
//
|
|
dwActivePrio = DSSCL_NORMAL;
|
|
for (pdse = gpdsinfo->pDSoundExternalObj; pdse; pdse = pdse->pNext) {
|
|
if (tid == pdse->tidSound && pdse->dwPriority > dwActivePrio) {
|
|
dwActivePrio = pdse->dwPriority;
|
|
}
|
|
}
|
|
|
|
DPF(0,"##### Activate tid %X Active Prio %X #####", tid, dwActivePrio);
|
|
|
|
fWritePrimary = (dwActivePrio >= DSSCL_WRITEPRIMARY);
|
|
|
|
//
|
|
// For all of the app's ds objects, set the format of the internal
|
|
// primary buffer to the app's format. Also play or stop the internal
|
|
// primary buffer as appropriate for the app we are activating.
|
|
//
|
|
// If we're WRITEPRIMARY, mark everyone else's external buffers as
|
|
// lost
|
|
//
|
|
for (pdse = gpdsinfo->pDSoundExternalObj; pdse; pdse = pdse->pNext) {
|
|
DPF(2, "Activating pdse %08Xh", pdse);
|
|
if (tid == pdse->tidSound) {
|
|
|
|
ASSERT(NULL != pdse->pds->pdsbPrimary);
|
|
if (NULL == pdse->pwfxApp) {
|
|
ASSERT(0 != pdse->pds->wfxDefault.wFormatTag);
|
|
IDsbSetFormatI(pdse->pds->pdsbPrimary, &pdse->pds->wfxDefault, 0);
|
|
} else {
|
|
IDsbSetFormatI(pdse->pds->pdsbPrimary, pdse->pwfxApp, 0);
|
|
}
|
|
|
|
//
|
|
// If we're not WRITEPRIMARY, then Play/Stop the primary per the
|
|
// the count of playing buffers
|
|
//
|
|
// We must be sure to call Play/Stop on the internal primary dsb's
|
|
// contained dsbe object so that Play/Stop doesn't operate on the
|
|
// app's primary dsbe object.
|
|
//
|
|
for (pdsbe = pdse->pdsbe; pdsbe; pdsbe = pdsbe->pNext) {
|
|
|
|
pdsb = pdsbe->pdsb;
|
|
pds = pdsb->pds;
|
|
|
|
if (pdsb->fdwDsbI & DSB_INTERNALF_PRIMARY) {
|
|
ASSERT(pdsb == pds->pdsbPrimary);
|
|
|
|
// If the external primary has been played, then cPlayPrimary must be >0.
|
|
ASSERT( !( (pdsbe->fdwDsbeI&DSBE_INTERNALF_PLAYING) && (0==pds->cPlayPrimary) ) );
|
|
|
|
if( (!fWritePrimary) &&
|
|
( (pds->cPlayPrimary > 0) ||
|
|
(pds->dwBuffersPlaying > 0) ) )
|
|
{
|
|
DPF(3, "DSActivateApp: playing primary dsb %08Xh, pdsb");
|
|
IDirectSoundBuffer_Play((LPDIRECTSOUNDBUFFER)pds->pdsbePrimary,
|
|
0, 0, DSBPLAY_LOOPING);
|
|
} else {
|
|
DPF(3, "DSActivateApp: stopping primary dsb %08Xh, pdsb");
|
|
IDsbStopI(pdsb, FALSE);
|
|
}
|
|
}
|
|
}
|
|
} else if (fWritePrimary) {
|
|
for (pdsbe = pdse->pdsbe; pdsbe; pdsbe = pdsbe->pNext) {
|
|
//
|
|
// Stop all playing buffers
|
|
//
|
|
if( !(pdsbe->pdsb->fdwDsbI & DSB_INTERNALF_STOP) ) {
|
|
IDsbStopI(pdsbe->pdsb, FALSE);
|
|
}
|
|
|
|
//
|
|
// Lose all secondary buffers.
|
|
//
|
|
if( !(pdsbe->pdsb->fdwDsbI & DSB_INTERNALF_PRIMARY) ) {
|
|
pdsbe->fdwDsbeI |= DSBE_INTERNALF_LOST;
|
|
}
|
|
|
|
//
|
|
// WRITEPRIMARY primaries should already be lost.
|
|
//
|
|
ASSERT( !(pdsbe->pdsb->fdwDsbI & DSB_INTERNALF_PRIMARY ) ||
|
|
( pdsbe->dwPriority < DSSCL_WRITEPRIMARY ) ||
|
|
( pdsbe->fdwDsbeI & DSBE_INTERNALF_LOST ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// For every pdse that are in focus, we activate all its pdsbe.
|
|
// We also signal the mixer to remix for the pdse's pds.
|
|
//
|
|
for (pdse = gpdsinfo->pDSoundExternalObj; pdse; pdse = pdse->pNext) {
|
|
if (tid == pdse->tidSound) {
|
|
for (pdsbe = pdse->pdsbe; pdsbe; pdsbe = pdsbe->pNext) {
|
|
DSBufferActivate(pdsbe);
|
|
}
|
|
if (!(pdse->pds->fdwInternal & DS_INTERNALF_WAVEEMULATED)) {
|
|
mxSignalRemix(pdse->pds, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// void apmResume
|
|
//
|
|
// Description:
|
|
//
|
|
// Arguments:
|
|
// none
|
|
//
|
|
// Return (void):
|
|
//
|
|
// History:
|
|
// 09/11/95 FrankYe Created
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
void apmResume(void)
|
|
{
|
|
LPDSOUND pds;
|
|
|
|
ENTER_DLL_CSECT();
|
|
|
|
if (gpdsinfo->fApmSuspended) {
|
|
gpdsinfo->fApmSuspended = FALSE;
|
|
for (pds = gpdsinfo->pDSoundObj; pds; pds = pds->pNext) {
|
|
IDsbSetFormatI( pds->pdsbPrimary, &pds->wfxDefault, IDSBSETFORMATIF_ALWAYS );
|
|
}
|
|
}
|
|
|
|
/* Update Focus and Stuck handles */
|
|
ActivateFocusWindow();
|
|
|
|
LEAVE_DLL_CSECT();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// void apmSuspend
|
|
//
|
|
// Description:
|
|
//
|
|
// Arguments:
|
|
// none
|
|
//
|
|
// Return (void):
|
|
//
|
|
// History:
|
|
// 09/11/95 FrankYe Created
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
void apmSuspend(void)
|
|
{
|
|
LPDSOUNDEXTERNAL pdse;
|
|
LPDSBUFFEREXTERNAL pdsbe;
|
|
|
|
ENTER_DLL_CSECT();
|
|
|
|
DSDeactivateApp(gpdsinfo->tidSoundFocus, DSDEACTIVATEAPPF_ALL);
|
|
|
|
for (pdse = gpdsinfo->pDSoundExternalObj; pdse; pdse = pdse->pNext) {
|
|
for (pdsbe = pdse->pdsbe; pdsbe; pdsbe = pdsbe->pNext) {
|
|
|
|
|
|
if( !(pdsbe->pdsb->fdwDsbI & DSB_INTERNALF_PRIMARY) ) {
|
|
//
|
|
// Stop all playing buffers
|
|
//
|
|
if( !(pdsbe->pdsb->fdwDsbI & DSB_INTERNALF_STOP) ) {
|
|
IDsbStopI(pdsbe->pdsb, FALSE);
|
|
}
|
|
|
|
//
|
|
// Lose all secondary buffers.
|
|
//
|
|
pdsbe->fdwDsbeI |= DSBE_INTERNALF_LOST;
|
|
}
|
|
|
|
}
|
|
for (pdsbe = pdse->pdsbe; pdsbe; pdsbe = pdsbe->pNext) {
|
|
|
|
|
|
if (pdsbe->pdsb->fdwDsbI & DSB_INTERNALF_PRIMARY) {
|
|
|
|
if( !(pdsbe->pdsb->fdwDsbI & DSB_INTERNALF_STOP) ) {
|
|
IDsbStopI(pdsbe->pdsb, FALSE);
|
|
}
|
|
|
|
//
|
|
// WRITEPRIMARY primaries should already be lost.
|
|
//
|
|
ASSERT( ( pdsbe->dwPriority < DSSCL_WRITEPRIMARY ) ||
|
|
( pdsbe->fdwDsbeI & DSBE_INTERNALF_LOST ) );
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
gpdsinfo->fApmSuspended = TRUE;
|
|
|
|
LEAVE_DLL_CSECT();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// SubclassWndProc
|
|
//
|
|
// We use APM notifications to help use get the native DS drivers
|
|
// into a state proper for suspending. If we are compiling for
|
|
// emulation mode only, then we don't need to catch these APM
|
|
// notifications.
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
#ifndef DSBLD_EMULONLY
|
|
LRESULT CALLBACK SubclassWndProc
|
|
(
|
|
HWND hWnd,
|
|
UINT uMessage,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
DPF(9,"Sublcass Window %X Message %X wParam %X, lParam %X ",
|
|
(DWORD)hWnd, (DWORD)uMessage, (DWORD)wParam, (DWORD)lParam );
|
|
switch(uMessage)
|
|
{
|
|
case WM_POWERBROADCAST:
|
|
switch (wParam) {
|
|
case PBT_APMSUSPEND:
|
|
apmSuspend();
|
|
return TRUE;
|
|
case PBT_APMRESUMESUSPEND:
|
|
apmResume();
|
|
return TRUE;
|
|
}
|
|
}
|
|
return 0;
|
|
} // SubclassWndProc()
|
|
#endif
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// IDSHWSetCooperativeLevel
|
|
//
|
|
// Description:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Return (HRESULT):
|
|
// DSERR_UNINITIALIZED if object has not be initialized
|
|
//
|
|
// History:
|
|
// 02/11/96 angusm Added check for initialization
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
HRESULT FAR PASCAL IDSHWSetCooperativeLevel
|
|
(
|
|
LPDIRECTSOUND pids,
|
|
HWND hwnd,
|
|
DWORD dwPriority
|
|
)
|
|
{
|
|
LPDSOUND pds;
|
|
LPDSOUNDEXTERNAL pdse;
|
|
LPDSBUFFEREXTERNAL pdsbeSearch;
|
|
DWORD dwProcessIdCaller;
|
|
DWORD tid;
|
|
|
|
if( !VALID_DSOUNDE_PTR(pids) ) {
|
|
RPF("IDirectSound::SetCooperativeLevel - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
pdse = (LPDSOUNDEXTERNAL)pids;
|
|
|
|
/* Check to see if the object is initialized */
|
|
if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) {
|
|
RPF("Direct Sound Object is uninitialized.");
|
|
return DSERR_UNINITIALIZED;
|
|
}
|
|
|
|
pds = pdse->pds;
|
|
if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) {
|
|
RPF("IDirectSound::SetCooperativeLevel - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
if( !VALID_HWND(hwnd) ) {
|
|
RPF("IDirectSound::SetCooperativeLevel - Invalid hwnd");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
if((dwPriority < DSSCL_FIRST) || (dwPriority > DSSCL_LAST)) {
|
|
RPF("IDirectSound::SetCooperativeLevel - Invalid DSSCL_* passed to SetCooperativeLevel");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
//
|
|
// Due to bugs in the wave emulation code, we don't allow writeprimary
|
|
// level on unsupported cards. This should be fixed. BUGBUG REMIND
|
|
//
|
|
if( ( pds->fdwInternal & DS_INTERNALF_WAVEEMULATED ) &&
|
|
( dwPriority >= DSSCL_WRITEPRIMARY ) )
|
|
{
|
|
RPF("IDirectSound::SetCooperativeLevel - DSSCL_WRITEPRIMARY level not available on emulated drivers.");
|
|
return DSERR_INVALIDCALL;
|
|
}
|
|
|
|
/* set the actual window handle that sound */
|
|
/* focus is tracked on to be the top unowned */
|
|
/* window. */
|
|
hwnd = GetTopUnownedWindow (hwnd);
|
|
tid = GetWindowThreadProcessId(hwnd, NULL);
|
|
|
|
|
|
DPF(3,"=== Enter SetCooperativeLevel ===");
|
|
|
|
ENTER_DLL_CSECT();
|
|
|
|
dwProcessIdCaller = GetCurrentProcessId();
|
|
|
|
// We use DSoundHelp to give us APM window notifications. If we are
|
|
// building only for emulation mode then we don't need APM notifications
|
|
// since APM will be handled entirely by the WAVE drivers that we are
|
|
// using for emulation.
|
|
#ifndef DSBLD_EMULONLY
|
|
// The first time this is called, register the window
|
|
if( pdse->hwndCooperative == 0 ) {
|
|
DSoundHelp( hwnd, SubclassWndProc, dwProcessIdCaller );
|
|
} else {
|
|
// If we are given a new window, then release old window and add new
|
|
if( pdse->hwndCooperative != hwnd ) {
|
|
DSoundHelp( NULL, SubclassWndProc, dwProcessIdCaller );
|
|
DSoundHelp( hwnd, SubclassWndProc, dwProcessIdCaller );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
pdse->tidSound = tid;
|
|
pdse->hwndCooperative = hwnd;
|
|
pdse->dwPriority = dwPriority;
|
|
|
|
// Update all buffers under this object to reflect the new priority level
|
|
pdsbeSearch = pdse->pdsbe;
|
|
while( pdsbeSearch != NULL ) {
|
|
pdsbeSearch->dwPriority = dwPriority;
|
|
pdsbeSearch = pdsbeSearch->pNext;
|
|
}
|
|
|
|
DseUpdateActivationState(pdse);
|
|
|
|
LEAVE_DLL_CSECT();
|
|
DPF(3,"=== Exit SetCooperativeLevel ===");
|
|
return DS_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// IDSHWCompact
|
|
//
|
|
// Description:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Return (HRESULT):
|
|
// DSERR_UNINITIALIZED if object has not be initialized
|
|
//
|
|
// History:
|
|
// 02/11/96 angusm Added check for initialization
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
HRESULT FAR PASCAL IDSHWCompact
|
|
(
|
|
LPDIRECTSOUND pids
|
|
)
|
|
{
|
|
LPDSOUND pds;
|
|
LPDSOUNDEXTERNAL pdse;
|
|
|
|
if( !VALID_DSOUNDE_PTR(pids) ) {
|
|
RPF("IDirectSound::Compact - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
pdse = (LPDSOUNDEXTERNAL)pids;
|
|
|
|
/* Check to see if the object is initialized */
|
|
if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) {
|
|
RPF("Direct Sound Object is uninitialized.");
|
|
return DSERR_UNINITIALIZED;
|
|
}
|
|
|
|
pds = pdse->pds;
|
|
if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) {
|
|
RPF("IDirectSound::Compact - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
if (DSSCL_NORMAL == pdse->dwPriority) {
|
|
RPF("IDirectSound::Compact - Compact with priority DSSCL_NORMAL");
|
|
return DSERR_PRIOLEVELNEEDED;
|
|
}
|
|
|
|
|
|
|
|
return DS_OK;
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// IDSHWGetSpeakerConfig
|
|
//
|
|
// Description:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Return (HRESULT):
|
|
// DSERR_UNINITIALIZED if object has not be initialized
|
|
//
|
|
// History:
|
|
// 02/11/96 angusm Added check for initialization
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
HRESULT FAR PASCAL IDSHWGetSpeakerConfig
|
|
(
|
|
LPDIRECTSOUND pids,
|
|
LPDWORD pdwConfig
|
|
)
|
|
{
|
|
LPDSOUND pds;
|
|
LPDSOUNDEXTERNAL pdse;
|
|
|
|
if( !VALID_DSOUNDE_PTR(pids) ) {
|
|
RPF("IDirectSound::GetSpeakerConfig - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
pdse = (LPDSOUNDEXTERNAL)pids;
|
|
|
|
/* Check to see if the object is initialized */
|
|
if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) {
|
|
RPF("Direct Sound Object is uninitialized.");
|
|
return DSERR_UNINITIALIZED;
|
|
}
|
|
|
|
pds = pdse->pds;
|
|
if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) {
|
|
RPF("IDirectSound::GetSpeakerConfig - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
if( !VALID_DWORD_PTR(pdwConfig)) {
|
|
RPF("IDirectSound::GetSpeakerConfig - Invalid pointer passed to GetSpeakerConfig()");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
ENTER_DLL_CSECT();
|
|
|
|
*pdwConfig = pdse->dwSpeakerConfig;
|
|
|
|
LEAVE_DLL_CSECT();
|
|
|
|
return DS_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// IDSHWSetSpeakerConfig
|
|
//
|
|
// Description:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Return (HRESULT):
|
|
// DSERR_UNINITIALIZED if object has not be initialized
|
|
//
|
|
// History:
|
|
// 02/11/96 angusm Added check for initialization
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
HRESULT FAR PASCAL IDSHWSetSpeakerConfig
|
|
(
|
|
LPDIRECTSOUND pids,
|
|
DWORD dwConfig
|
|
)
|
|
{
|
|
LPDSOUND pds;
|
|
LPDSOUNDEXTERNAL pdse;
|
|
|
|
if( !VALID_DSOUNDE_PTR(pids) ) {
|
|
RPF("IDirectSound::SetSpeakerConfig - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
pdse = (LPDSOUNDEXTERNAL)pids;
|
|
|
|
/* Check to see if the object is initialized */
|
|
if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) {
|
|
RPF("Direct Sound Object is uninitialized.");
|
|
return DSERR_UNINITIALIZED;
|
|
}
|
|
|
|
pds = pdse->pds;
|
|
if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) {
|
|
RPF("IDirectSound::SetSpeakerConfig - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
if(( dwConfig < DSSPEAKER_FIRST ) || ( dwConfig > DSSPEAKER_LAST )) {
|
|
RPF("IDirectSound::SetSpeakerConfig - Invalid Config value");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
|
|
ENTER_DLL_CSECT();
|
|
|
|
// We're not doing a darn thing with this other than saving away the
|
|
// value until we get 3D sound...
|
|
|
|
pdse->dwSpeakerConfig = dwConfig;
|
|
|
|
LEAVE_DLL_CSECT();
|
|
return DS_OK;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// IDSHWInitialize
|
|
//
|
|
// Description:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Return ():
|
|
//
|
|
// History:
|
|
// 02/11/96 angusm Added init code from DirectSoundCreate
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
HRESULT FAR PASCAL IDSHWInitialize
|
|
(
|
|
LPDIRECTSOUND pids,
|
|
GUID FAR *lpGUID
|
|
)
|
|
{
|
|
LPDSOUNDEXTERNAL pdse;
|
|
GUID guid, guidLast;
|
|
DSDRIVERDESC dsDrvDesc;
|
|
UINT cWaves;
|
|
UINT uPreferredWaveId;
|
|
UINT uWaveId;
|
|
BOOL fPreferredOnly;
|
|
HRESULT hr;
|
|
HRESULT hrClient;
|
|
|
|
if( !VALID_DSOUNDE_PTR(pids) ) {
|
|
RPF("IDirectSound::Initialize - Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
pdse = (LPDSOUNDEXTERNAL)pids;
|
|
|
|
/* Check to see if the object is initialized */
|
|
if (IDSHWINITIALIZEF_INITIALIZED == pdse->fInitialized) {
|
|
return DSERR_ALREADYINITIALIZED;
|
|
}
|
|
|
|
cWaves = waveOutGetNumDevs();
|
|
cWaves = min(cWaves, LIMIT_WAVE_DEVICES-1);
|
|
if (0 == cWaves) return DSERR_NODRIVER;
|
|
|
|
if (!wavGetPreferredId(&uPreferredWaveId, &fPreferredOnly)) {
|
|
return DSERR_NODRIVER;
|
|
}
|
|
|
|
ENTER_DLL_CSECT();
|
|
|
|
if (NULL == lpGUID) {
|
|
|
|
hrClient = DSERR_NODRIVER;
|
|
|
|
#if defined(RDEBUG) || defined(DEBUG)
|
|
if (!gpdsinfo->fEnumOnlyWaveDevs)
|
|
#endif
|
|
{
|
|
hr = wavGetDrvGuidFromId(uPreferredWaveId, &guid);
|
|
if (DS_OK == hr) {
|
|
hr = hrClient = DseInitializeFromGuid(pdse, &guid);
|
|
}
|
|
|
|
if ((DS_OK != hr) && !fPreferredOnly) {
|
|
|
|
ZeroMemory(&dsDrvDesc, sizeof(dsDrvDesc));
|
|
hr = vxdDrvGetNextDriverDesc(NULL, &guid, &dsDrvDesc);
|
|
while (DS_OK == hr) {
|
|
if ((DS_OK == wavGetIdFromDrvGuid(&guid, &uWaveId)) &&
|
|
wavIsMappable(uWaveId))
|
|
{
|
|
hr = hrClient = DseInitializeFromGuid(pdse, &guid);
|
|
if (DS_OK == hr) break;
|
|
}
|
|
guidLast = guid;
|
|
ZeroMemory(&dsDrvDesc, sizeof(dsDrvDesc));
|
|
hr = vxdDrvGetNextDriverDesc(&guidLast, &guid, &dsDrvDesc);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((DS_OK != hrClient) && (uPreferredWaveId < cWaves)) {
|
|
guid = gpdsinfo->aguidWave[uPreferredWaveId];
|
|
hr = hrClient = DseInitializeFromGuid(pdse, &guid);
|
|
}
|
|
|
|
if ((DS_OK != hrClient) && !fPreferredOnly) {
|
|
uWaveId = 0;
|
|
while (uWaveId < cWaves) {
|
|
guid = gpdsinfo->aguidWave[uWaveId];
|
|
if (wavIsMappable(uWaveId)) {
|
|
hr = hrClient = DseInitializeFromGuid(pdse, &guid);
|
|
if (DS_OK == hr) break;
|
|
}
|
|
uWaveId++;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
// NULL != lpGUID
|
|
hrClient = DseInitializeFromGuid(pdse, lpGUID);
|
|
}
|
|
|
|
/* Check to see if this is the first buffer */
|
|
/* created, and start Focus Thread. */
|
|
if ((DS_OK == hrClient) && (1 == cSoundObjects())) {
|
|
if (!CreateFocusThread()) {
|
|
hrClient = DSERR_GENERIC;
|
|
DseTerminate(pdse);
|
|
}
|
|
}
|
|
|
|
if (DS_OK == hrClient) {
|
|
pdse->fInitialized = IDSHWINITIALIZEF_INITIALIZED;
|
|
}
|
|
|
|
LEAVE_DLL_CSECT();
|
|
return hrClient;
|
|
} // IDSHWInitialize()
|
|
|
|
|
|
|
|
// This code is disabled for now...
|
|
|
|
#ifdef ENABLE_EXCLUSIVEMODE_STUFF
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// IDSHWGetExclusiveModeOwner
|
|
//
|
|
// Description:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Return (HRESULT):
|
|
// DSERR_UNINITIALIZED if object has not be initialized
|
|
//
|
|
// History:
|
|
// 02/11/96 angusm Added check for initialization
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
HRESULT FAR PASCAL IDSHWGetExclusiveModeOwner
|
|
(
|
|
LPDIRECTSOUND pids,
|
|
LPHANDLE phProcess
|
|
)
|
|
{
|
|
LPDSOUND pds;
|
|
LPDSOUNDEXTERNAL pdse;
|
|
|
|
if( !VALID_DSOUNDE_PTR(pids) ) {
|
|
DPF(0,"Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
pdse = (LPDSOUNDEXTERNAL)pids;
|
|
|
|
/* Check to see if the object is initialized */
|
|
if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) {
|
|
RPF("Direct Sound Object is uninitialized.");
|
|
return DSERR_UNINITIALIZED;
|
|
}
|
|
|
|
pds = pdse->pds;
|
|
if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) {
|
|
DPF(0,"Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
|
|
ENTER_DLL_CSECT();
|
|
|
|
*phProcess = pds->hExclusiveOwner;
|
|
|
|
|
|
LEAVE_DLL_CSECT();
|
|
return DS_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// IDSHWSetExclusiveModeOwner
|
|
//
|
|
// Description:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Return (HRESULT):
|
|
// DSERR_UNINITIALIZED if object has not be initialized
|
|
//
|
|
// History:
|
|
// 02/11/96 angusm Added check for initialization
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
HRESULT FAR PASCAL IDSHWSetExclusiveModeOwner
|
|
(
|
|
LPDIRECTSOUND pids,
|
|
DWORD fdwFlags
|
|
)
|
|
{
|
|
LPDSOUND pds;
|
|
LPDSOUNDEXTERNAL pdse;
|
|
|
|
if( !VALID_DSOUNDE_PTR(pids) ) {
|
|
DPF(0,"Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
pdse = (LPDSOUNDEXTERNAL)pids;
|
|
|
|
/* Check to see if the object is initialized */
|
|
if (IDSHWINITIALIZEF_UNINITIALIZED == pdse->fInitialized) {
|
|
RPF("Direct Sound Object is uninitialized.");
|
|
return DSERR_UNINITIALIZED;
|
|
}
|
|
|
|
pds = pdse->pds;
|
|
if( !VALID_DSOUND_PTR(pds) || (0 == pdse->uRefCount)) {
|
|
DPF(0,"Invalid Object or ref count");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
|
|
ENTER_DLL_CSECT();
|
|
|
|
if( (pds->hExclusiveOwner != NULL) &&
|
|
(pds->hExclusiveOwner != (HANDLE)GetCurrentProcessId()) ) {
|
|
DPF(0,"Exclusive mode already set");
|
|
LEAVE_DLL_CSECT();
|
|
return DSERR_GENERIC;
|
|
}
|
|
|
|
if( fdwFlags & DS_EXCLUSIVEF_NONEXCLUSIVE ) {
|
|
pds->fdwExclusive = 0;
|
|
pds->hExclusiveOwner = 0;
|
|
} else {
|
|
pds->fdwExclusive = fdwFlags;
|
|
pds->hExclusiveOwner = (HANDLE)GetCurrentProcessId();
|
|
}
|
|
|
|
LEAVE_DLL_CSECT();
|
|
return DSERR_OK;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
void FNGLOBAL DSHWCreateTable
|
|
(
|
|
LPDSOUNDCALLBACKS lpVtbl
|
|
)
|
|
{
|
|
lpVtbl->QueryInterface = IDSHWQueryInterface;
|
|
lpVtbl->AddRef = IDSHWAddRef;
|
|
lpVtbl->Release = IDSHWRelease;
|
|
lpVtbl->CreateSoundBuffer = IDSHWCreateSoundBuffer;
|
|
lpVtbl->GetCaps = IDSHWGetCaps;
|
|
lpVtbl->DuplicateSoundBuffer
|
|
= IDSHWDuplicateSoundBuffer;
|
|
lpVtbl->SetCooperativeLevel = IDSHWSetCooperativeLevel;
|
|
lpVtbl->Compact = IDSHWCompact;
|
|
lpVtbl->GetSpeakerConfig = IDSHWGetSpeakerConfig;
|
|
lpVtbl->SetSpeakerConfig = IDSHWSetSpeakerConfig;
|
|
lpVtbl->Initialize = IDSHWInitialize;
|
|
} // DSHWCreateTable()
|
|
|
|
/* GetTopUnownedWindow
|
|
* This function finds the top-most unowned window ancesting for the
|
|
* given window handle.
|
|
*
|
|
* IN: a valid Window handle
|
|
* OUT: the top-most unowned window ancesting from hWindow
|
|
* SIDE EFFECTS: none
|
|
* NOTES: This function must be called in a Dll Critical Section
|
|
*
|
|
* REVISION HISTORY:
|
|
* 11/27/95 angusm Initial Version
|
|
* 12/06/95 angusm Removed supurpholous call to GetWindow( , GW_OWNER)
|
|
* 12/10/95 angusm Code style cleanup
|
|
*/
|
|
|
|
HWND GetTopUnownedWindow (HWND hWindow)
|
|
{
|
|
HWND hTempWindow = hWindow;
|
|
|
|
while (NULL != hTempWindow)
|
|
{
|
|
hWindow = hTempWindow;
|
|
ASSERT (hWindow);
|
|
hTempWindow = GetParent (hWindow); /* This call will return the owner,
|
|
if on exists, for top level
|
|
windows. */
|
|
}
|
|
|
|
return hWindow;
|
|
}
|
|
|
|
/* FocusTracker
|
|
* This function is a thread that runs in the process space of DDHelp,
|
|
* and polls to see which window is the top unowned window. If the window
|
|
* has changed the sound focus changes.
|
|
*
|
|
* IN: the number of miliseconds to wait between polls
|
|
* OUT: none
|
|
* SIDE EFFECTS: The sound focus will change
|
|
* NOTES: 1. The hwndStuckSoundFocus, and validity of the current Focus handle may
|
|
* change outside of this thread. (In apmResume and SetCooperativeL.
|
|
*
|
|
* REVISION HISTORY:
|
|
* 11/27/95 angusm Initial Version
|
|
* 12/10/95 angusm added FocusLock creation
|
|
* 12/10/95 angusm Code style cleanup
|
|
* 03/20/96 angusm Added Sticky focus logic
|
|
*/
|
|
|
|
DWORD WINAPI FocusTracker (LPVOID lpvPollInterval)
|
|
{
|
|
HWND hNewWindowFocus;
|
|
BOOL fIsNewWindowValid;
|
|
BOOL fIsNewWindowMinimized;
|
|
HWND hOldWindowFocus = NULL;
|
|
DWORD tidOldWindowFocus = 0;
|
|
BOOL fIsOldWindowValid = FALSE;
|
|
BOOL fIsOldWindowMinimized = FALSE;
|
|
|
|
HANDLE hWaitEvent;
|
|
HANDLE hFocusLock;
|
|
HANDLE hStartupEvent;
|
|
DWORD nRetVal;
|
|
WINDOWPLACEMENT wndplPlacement;
|
|
DWORD tid;
|
|
|
|
DPF (2, "FocusTracker: Enter Function (lpvPollInterval = %x)",
|
|
lpvPollInterval);
|
|
|
|
/* Create "Wait" Event */
|
|
hWaitEvent = CreateEvent (NULL, /* No security attribs */
|
|
TRUE, /* manual-reset */
|
|
FALSE, /* nonsignaled */
|
|
WAKEFOCUSTHREAD); /* event name */
|
|
if (NULL == hWaitEvent) { /* On Error ... */
|
|
DPF(0, "FocusTracker: Could not create DSWakeFocusThread "
|
|
"event; exiting" );
|
|
ExitThread (0);
|
|
}
|
|
|
|
/* Grab Focus Lock */
|
|
if (FALSE == CreateFocusLock (&hFocusLock)) {
|
|
DPF(1, "FocusTracker: Could not create Focus Lock" );
|
|
}
|
|
/* Open Focus Thread startup
|
|
startup verification event */
|
|
hStartupEvent = OpenEvent (EVENT_MODIFY_STATE, /* only allowed to pulse */
|
|
FALSE, /* non inheritable */
|
|
FOCUSSTARTUPEVENT);
|
|
if (NULL == hStartupEvent) { /* On Error ... */
|
|
DPF(0, "FocusTracker: Could not open Startup Event; exiting" );
|
|
if (FALSE == DestroyFocusLock (hFocusLock)) {
|
|
DPF (1, "FocusTracker: Could not destroy focus lock.");
|
|
}
|
|
if (FALSE == CloseHandle (hWaitEvent)) ASSERT (0);
|
|
ExitThread (0);
|
|
}
|
|
|
|
/* Handle Cleanup */
|
|
if (FALSE == SetEvent (hStartupEvent)) ASSERT (0);
|
|
if (FALSE == CloseHandle (hStartupEvent)) ASSERT (0);
|
|
|
|
wndplPlacement.length = sizeof(WINDOWPLACEMENT);
|
|
|
|
while (1) /* Focus Tracking Loop */
|
|
{
|
|
DWORD dwWaitResult;
|
|
|
|
hNewWindowFocus = GetTopUnownedWindow (GetForegroundWindow());
|
|
|
|
if (hOldWindowFocus != hNewWindowFocus)
|
|
{
|
|
DWORD tidNewFocus;
|
|
|
|
DPF(3, "FocusTracker: New Focus = %x Old Focus = %x",
|
|
hNewWindowFocus, hOldWindowFocus);
|
|
|
|
tidNewFocus = hNewWindowFocus ? GetWindowThreadProcessId(hNewWindowFocus, NULL) : 0;
|
|
fIsNewWindowValid = IsValidDSApp(tidNewFocus);
|
|
|
|
nRetVal = ENTER_DLL_CSECT_OR_EVENT (hWaitEvent);
|
|
if (WAIT_OBJECT_0 == nRetVal) { /* If Wait Event is signaled */
|
|
DPF (3, "FocusTracker: breaking on Wait Event");
|
|
break;
|
|
} /* Else we have taken the DLL_CSECT */
|
|
|
|
if (hNewWindowFocus) {
|
|
GetWindowPlacement (hNewWindowFocus, &wndplPlacement);
|
|
fIsNewWindowMinimized = (SW_SHOWMINIMIZED == wndplPlacement.showCmd);
|
|
} else {
|
|
fIsNewWindowMinimized = FALSE;
|
|
}
|
|
|
|
if (fIsNewWindowValid && !fIsNewWindowMinimized)
|
|
/* New window uses Direct Sound and is not minimized */
|
|
{
|
|
DPF (3, "FocusTracker: switching to DS app"
|
|
" gpdsinfo->tidStuckFocus = %x, "
|
|
"tidNewFocus = %x",
|
|
gpdsinfo->tidStuckFocus, tidNewFocus);
|
|
|
|
// If focus is switching back to the hwnd that was stuck
|
|
// then we don't need to deactivate the stuck hwnd.
|
|
if (gpdsinfo->tidStuckFocus != tidNewFocus) {
|
|
DSDeactivateApp (gpdsinfo->tidStuckFocus,
|
|
DSDEACTIVATEAPPF_ALL);
|
|
gpdsinfo->tidStuckFocus = tidNewFocus;
|
|
}
|
|
DSActivateApp (tidNewFocus);
|
|
gpdsinfo->tidSoundFocus = tidNewFocus;
|
|
gpdsinfo->hwndSoundFocus = hNewWindowFocus;
|
|
}
|
|
else /* New window is not associated with a Direct Sound object,
|
|
or the application is minimized */
|
|
{
|
|
DPF (3, "FocusTracker: switching away from DS app"
|
|
" hOldWindowFocus = %x", hOldWindowFocus);
|
|
|
|
fIsOldWindowValid = IsValidDSApp (tidOldWindowFocus);
|
|
if (fIsOldWindowValid && gpdsinfo->hwndSoundFocus) {
|
|
DSDeactivateApp (gpdsinfo->tidSoundFocus,
|
|
DSDEACTIVATEAPPF_NONSTICKY);
|
|
}
|
|
|
|
if (fIsNewWindowMinimized && fIsNewWindowValid)
|
|
/* there is a minimized ds app */
|
|
{
|
|
gpdsinfo->tidSoundFocus = 0;
|
|
gpdsinfo->hwndSoundFocus = NULL;
|
|
}
|
|
else
|
|
{
|
|
gpdsinfo->tidSoundFocus = tidNewFocus;
|
|
gpdsinfo->hwndSoundFocus = hNewWindowFocus;
|
|
}
|
|
}
|
|
|
|
hOldWindowFocus = hNewWindowFocus;
|
|
tidOldWindowFocus = tidNewFocus;
|
|
fIsOldWindowValid = fIsNewWindowValid;
|
|
fIsOldWindowMinimized = fIsNewWindowMinimized;
|
|
LEAVE_DLL_CSECT();
|
|
|
|
} else if (fIsOldWindowValid && fIsOldWindowMinimized)
|
|
{
|
|
DPF (3, "FocusTracker: switching DS from minimization"
|
|
" hOldWindowFocus = %x", hOldWindowFocus);
|
|
ASSERT (hOldWindowFocus);
|
|
GetWindowPlacement (hOldWindowFocus, &wndplPlacement);
|
|
fIsNewWindowMinimized = (SW_SHOWMINIMIZED == wndplPlacement.showCmd);
|
|
/* Old window is a DS app but has been maximized */
|
|
if (!fIsNewWindowMinimized)
|
|
{
|
|
nRetVal = ENTER_DLL_CSECT_OR_EVENT (hWaitEvent);
|
|
if (WAIT_OBJECT_0 == nRetVal) { /* If Wait Event is signaled */
|
|
DPF (3, "FocusTracker: breaking on Wait Event");
|
|
break;
|
|
} /* Else we have taken the DLL_CSECT */
|
|
|
|
tid = GetWindowThreadProcessId(hOldWindowFocus, NULL);
|
|
if (gpdsinfo->tidStuckFocus != tid) {
|
|
DSDeactivateApp (gpdsinfo->tidStuckFocus, DSDEACTIVATEAPPF_ALL);
|
|
gpdsinfo->tidStuckFocus = tid;
|
|
}
|
|
DSActivateApp(tid);
|
|
|
|
gpdsinfo->tidSoundFocus = tid;
|
|
gpdsinfo->hwndSoundFocus = hOldWindowFocus;
|
|
fIsOldWindowMinimized = fIsNewWindowMinimized;
|
|
LEAVE_DLL_CSECT();
|
|
}
|
|
}
|
|
|
|
dwWaitResult = WaitForSingleObjectEx(hWaitEvent, (DWORD)lpvPollInterval, FALSE);
|
|
if (WAIT_OBJECT_0 == dwWaitResult) break;
|
|
ASSERT(WAIT_TIMEOUT == dwWaitResult);
|
|
} /* while (1) */
|
|
|
|
/* Free Focus Lock */
|
|
if (FALSE == DestroyFocusLock (hFocusLock)) {
|
|
DPF (1, "Could not destroy focus lock.");
|
|
}
|
|
|
|
/* Cleanup Handles */
|
|
if (FALSE == CloseHandle (hWaitEvent)) ASSERT (0);
|
|
if (FALSE == CloseHandle (gpdsinfo->hFocusTracker)) ASSERT (0);
|
|
|
|
DPF (2, "FocusTracker: Exit Function");
|
|
ExitThread (0);
|
|
return 0; /* This should never be reached. */
|
|
}
|
|
|
|
/* IsValidDSApp
|
|
* This function walks the external Direct Sound structures to see if the
|
|
* given tid is a valid direct sound thread.
|
|
*
|
|
* IN: any tid
|
|
* OUT: TRUE if it is a Direct Sound thread, FALSE otherwise
|
|
* SIDE EFFECTS: none
|
|
* NOTES: This function must be called in a Dll Critical Section if you
|
|
* need the returned value to be correct. Otherwise, the returned value
|
|
* may be a false answer.
|
|
*
|
|
* REVISION HISTORY:
|
|
* 11/27/95 angusm Initial Version
|
|
* 12/06/95 angusm appended to NOTES:
|
|
* 12/10/95 angusm Added FocusLock calls
|
|
* 12/10/95 angusm Code style cleanup
|
|
*/
|
|
|
|
BOOL IsValidDSApp (DWORD dwTid)
|
|
{
|
|
LPDSOUNDEXTERNAL pdse = gpdsinfo->pDSoundExternalObj;
|
|
BOOL fReturnValue = FALSE;
|
|
HANDLE hFocusLock;
|
|
|
|
if (FALSE == GetFocusLock (&hFocusLock)) {
|
|
DPF(2, "Dsound: IsValidDSApp: Error getting Focus Lock.");
|
|
return FALSE;
|
|
}
|
|
|
|
while (NULL != pdse) {
|
|
if (dwTid == (pdse->tidSound)) {
|
|
ASSERT (0 != pdse->tidSound);
|
|
fReturnValue = TRUE;
|
|
break;
|
|
}
|
|
pdse = pdse->pNext;
|
|
}
|
|
|
|
if (FALSE == ReleaseFocusLock(hFocusLock)) {
|
|
DPF (2, "Dsound: IsValidDSApp: Could not release Focus Lock.");
|
|
ASSERT (0);
|
|
}
|
|
|
|
return fReturnValue;
|
|
}
|
|
|
|
|
|
/* cSoundObjects
|
|
* This function walks the external Direct Sound structures to count them.
|
|
*
|
|
* IN: none
|
|
* OUT: The number of external structures if 0 or 1, or 2 for 2 or more
|
|
* structures.
|
|
* SIDE EFFECTS: none
|
|
* NOTES: This function must be called in a Dll Critical Section
|
|
*
|
|
* REVISION HISTORY:
|
|
* 12/1/95 angusm Initial Version
|
|
* 12/10/95 angusm Code style cleanup
|
|
*/
|
|
|
|
int cSoundObjects ()
|
|
{
|
|
LPDSOUNDEXTERNAL pdse;
|
|
int cNumObjects = 0;
|
|
|
|
pdse = gpdsinfo->pDSoundExternalObj;
|
|
|
|
while ((NULL != pdse) && (2 >= cNumObjects))
|
|
{
|
|
pdse = pdse->pNext;
|
|
cNumObjects++;
|
|
}
|
|
|
|
return cNumObjects;
|
|
}
|
|
|
|
|
|
/* CreateFocusThread
|
|
* This function creates the Focus Thread.
|
|
*
|
|
* IN: none
|
|
* OUT: TRUE if Thread was created, or FALSE otherwise
|
|
* SIDE EFFECTS: a Focus Thread is created, and hWakeFocusThread, and
|
|
* hFocusThread handles are created.
|
|
* NOTES: This function must be called in a Dll Critical Section
|
|
*
|
|
* REVISION HISTORY:
|
|
* 12/1/95 angusm Initial Version
|
|
* 12/10/95 angusm Code style cleanup
|
|
*/
|
|
|
|
BOOL CreateFocusThread ()
|
|
{
|
|
HANDLE hFocusStartupEvent;
|
|
DWORD dwResult;
|
|
|
|
hFocusStartupEvent = CreateEvent (NULL, /* no security attribs */
|
|
FALSE, /* auto-reset */
|
|
FALSE, /* non signaled */
|
|
FOCUSSTARTUPEVENT);
|
|
if (NULL == hFocusStartupEvent) {
|
|
DPF (1, "Dsound: CreateFocusThread: Could not create "
|
|
"Focus Startup Event");
|
|
return FALSE;
|
|
}
|
|
|
|
gpdsinfo->hFocusTracker = HelperCreateDSFocusThread
|
|
((LPTHREAD_START_ROUTINE) FocusTracker, /* thread function */
|
|
(LPVOID) POLL_INTERVAL, /* parameter */
|
|
0, /* normal creation flags */
|
|
NULL); /* unwanted threadid */
|
|
|
|
if (NULL == gpdsinfo->hFocusTracker) {
|
|
DPF(1, "Could not start Focus Tracker thread in helper; exiting" );
|
|
if (FALSE == CloseHandle (hFocusStartupEvent)) ASSERT (0);
|
|
return FALSE;
|
|
}
|
|
|
|
dwResult = WaitForSingleObjectEx(hFocusStartupEvent, INFINITE, FALSE);
|
|
ASSERT(WAIT_OBJECT_0 == dwResult);
|
|
|
|
if (FALSE == CloseHandle (hFocusStartupEvent)) ASSERT (0);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
/* EndFocusThread
|
|
* This function will send an event to the Focus Thread running in DDHelp,
|
|
* and wait to see if it terminates. If it does not, the thread will be
|
|
* terminated.
|
|
*
|
|
* IN: none
|
|
* OUT: none
|
|
* SIDE EFFECTS: 1. Focus Thread will be killed.
|
|
* 2. hWakeFocusThread handle will be closed.
|
|
* 3. hFocusTracker hadnle will be closed.
|
|
* NOTES: This function must be called in a Dll Critical Section
|
|
*
|
|
* REVISION HISTORY:
|
|
* 11/30/95 angusm Initial Version
|
|
* 12/10/95 angusm Code style cleanup
|
|
*/
|
|
|
|
void EndFocusThread ()
|
|
{
|
|
HANDLE hWaitEvent, hHelper, hOurFocusTracker;
|
|
DWORD dwResult;
|
|
|
|
hWaitEvent = CreateEvent (NULL,
|
|
FALSE, /* auto-reset */
|
|
TRUE, /* if event object does not
|
|
already exists, create one
|
|
signaled */
|
|
WAKEFOCUSTHREAD); /* event name */
|
|
if (NULL == hWaitEvent)
|
|
{
|
|
DPF (1, "Dsound: EndFocusThread: Could not create "
|
|
"wait event");
|
|
return;
|
|
}
|
|
|
|
hHelper = OpenProcess (PROCESS_DUP_HANDLE,
|
|
FALSE, /* no inheritance */
|
|
gpdsinfo->pidHelper); /* Helper PID */
|
|
if (NULL == hHelper)
|
|
{
|
|
DPF (1, "Dsound: EndFocusThread: Could not open process");
|
|
if (FALSE == CloseHandle (hWaitEvent)) ASSERT (0);
|
|
return;
|
|
}
|
|
|
|
if (FALSE ==
|
|
DuplicateHandle (hHelper, /* source process */
|
|
gpdsinfo->hFocusTracker, /* source handle */
|
|
GetCurrentProcess(), /* our process */
|
|
&hOurFocusTracker, /* our new handle */
|
|
SYNCHRONIZE | THREAD_TERMINATE, /* access flags */
|
|
FALSE, /* no inheritance */
|
|
0)) /* no options */
|
|
{ /* On Error ... */
|
|
DPF (1, "Dsound: EndFocusThread: Could not duplicate handle");
|
|
if (FALSE == CloseHandle (hHelper)) ASSERT (0);
|
|
if (FALSE == CloseHandle (hWaitEvent)) ASSERT (0);
|
|
return;
|
|
}
|
|
|
|
if (FALSE == SetEvent (hWaitEvent)) ASSERT (0);
|
|
if (FALSE == CloseHandle (hWaitEvent)) ASSERT (0);
|
|
|
|
dwResult = WaitForSingleObjectEx(hOurFocusTracker, INFINITE, FALSE);
|
|
ASSERT(WAIT_OBJECT_0 == dwResult);
|
|
|
|
if (FALSE == CloseHandle (hHelper)) ASSERT (0);
|
|
if (FALSE == CloseHandle (hOurFocusTracker)) ASSERT (0);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* DseUpdateActivationState
|
|
*
|
|
* Given the current tidSoundFocus and tidSticky, this function activates
|
|
* or deactivates all dsbe for a dse
|
|
*/
|
|
__inline void DseUpdateActivationState(LPDSOUNDEXTERNAL pdse)
|
|
{
|
|
LPDSBUFFEREXTERNAL pdsbe;
|
|
WINDOWPLACEMENT wndplPlacement;
|
|
BOOL fIsMinimized;
|
|
|
|
wndplPlacement.length = sizeof(WINDOWPLACEMENT);
|
|
|
|
ASSERT (pdse->hwndCooperative);
|
|
|
|
if (NULL != gpdsinfo->hwndSoundFocus) {
|
|
GetWindowPlacement (gpdsinfo->hwndSoundFocus, &wndplPlacement);
|
|
fIsMinimized = (SW_SHOWMINIMIZED == wndplPlacement.showCmd);
|
|
}
|
|
else fIsMinimized = FALSE;
|
|
|
|
if ((gpdsinfo->tidSoundFocus == pdse->tidSound) && !fIsMinimized) {
|
|
if (gpdsinfo->tidStuckFocus != pdse->tidSound) {
|
|
DSDeactivateApp (gpdsinfo->tidStuckFocus, DSDEACTIVATEAPPF_ALL);
|
|
}
|
|
gpdsinfo->tidStuckFocus = gpdsinfo->tidSoundFocus;
|
|
}
|
|
|
|
for (pdsbe = pdse->pdsbe; pdsbe; pdsbe = pdsbe->pNext)
|
|
{
|
|
if ((gpdsinfo->tidSoundFocus != pdsbe->pdse->tidSound) &&
|
|
!((gpdsinfo->tidStuckFocus == pdsbe->pdse->tidSound) &&
|
|
(DSBCAPS_STICKYFOCUS & pdsbe->pdsb->fdwBufferDesc)))
|
|
{
|
|
DSBufferDeactivate (pdsbe);
|
|
}
|
|
else
|
|
{
|
|
DSBufferActivate (pdsbe);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* DsbeDeactivateIfNecessary
|
|
*
|
|
* This function deactivates a buffer only if the buffer is current in focus.
|
|
*/
|
|
__inline void DsbeDeactivateIfNecessary (LPDSBUFFEREXTERNAL pdsbe)
|
|
{
|
|
DPF (2, "DsbeDeactivateIfNecessary: entered pdsbe = %x", pdsbe);
|
|
if ((gpdsinfo->tidSoundFocus != pdsbe->pdse->tidSound) &&
|
|
!((gpdsinfo->tidStuckFocus == pdsbe->pdse->tidSound) &&
|
|
(DSBCAPS_STICKYFOCUS & pdsbe->pdsb->fdwBufferDesc)))
|
|
{
|
|
DSBufferDeactivate (pdsbe);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* ActivateFocusWindow
|
|
*
|
|
* This function will set the gpdsinfo->hwndSticky handle properly. In addition
|
|
* it will Deactivate the current sticky hwnd and activate the the in focus
|
|
* hwnd.
|
|
*
|
|
* NOTE: This function must be called within a CSECT
|
|
*/
|
|
__inline void ActivateFocusWindow (void) {
|
|
if (gpdsinfo->tidSoundFocus != gpdsinfo->tidStuckFocus) {
|
|
DSDeactivateApp (gpdsinfo->tidStuckFocus, DSDEACTIVATEAPPF_ALL);
|
|
gpdsinfo->tidStuckFocus = gpdsinfo->tidSoundFocus;
|
|
}
|
|
DSActivateApp (gpdsinfo->tidSoundFocus);
|
|
}
|