|
|
//--------------------------------------------------------------------------;
//
// File: DSound.c
//
// Copyright (c) 1995 Microsoft Corporation. All Rights Reserved.
//
// Abstract:
//
//
// Contents:
// IDSWECreateSoundBuffer()
// DirectSoundCreate()
// DirectSoundEnumerateA()
// CreateNewDirectSoundObject()
//
// History:
// Date By Reason
// ==== == ======
// 3/5/96 angusm Added use of fInitialized
//
//--------------------------------------------------------------------------;
#define INITGUID
#include "dsoundpr.h"
#include <mmddk.h>
#include <initguid.h>
#include "dsdriver.h"
#include "grace.h"
#include "resource.h"
#include "flocks.h"
#define TID_INIT_VALUE 0xffffffff // This is a dummy value that can not be a valid TID
#if !defined NUMELMS
#define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0]))
#endif
// internal Kernel32 API
extern DWORD WINAPI OpenVxDHandle(HANDLE hSource);
LPDSOUNDINFO gpdsinfo = NULL;
//--------------------------------------------------------------------------;
//
// BOOL wavIsMappable
//
// Description:
// This function determines whether a specified wave device is
// a mappable device
//
// Arguments:
// UINT uWaveId :
//
// Return (BOOL): TRUE if and only if it the wave device is a mappable device
//
// History:
// 09/09/95 FrankYe Created
//
//--------------------------------------------------------------------------;
BOOL wavIsMappable(UINT uWaveId) { MMRESULT mmr;
mmr = waveOutMessage((HWAVEOUT)uWaveId, DRV_QUERYMAPPABLE, 0, 0); return (MMSYSERR_NOERROR == mmr); }
//--------------------------------------------------------------------------;
//
// void wavGetPreferredId
//
// Description:
// This function accesses the registry settings maintained by the
// wave mapper and multimedia control panel to determine the wave id
// of the preferred sound device.
//
// Arguments:
// LPUINT puWaveId :
//
// LPBOOL pfPreferredOnly :
//
// Return (void):
//
// History:
// 09/09/95 FrankYe Created
//
//--------------------------------------------------------------------------;
const TCHAR gszRegKeynameWaveMapper[] = TEXT("Software\\Microsoft\\Multimedia\\Sound Mapper"); const TCHAR gszRegValuenamePreferredPlayback[] = TEXT("Playback"); const TCHAR gszRegValuenamePreferredOnly[] = TEXT("PreferredOnly");
BOOL wavGetPreferredId(LPUINT puWaveId, LPBOOL pfPreferredOnly) { UINT u; WAVEOUTCAPS woc; HKEY hkeyWaveMapper; TCHAR szPreferred[MAXPNAMELEN]; DWORD dwPreferredOnly; DWORD dwType; DWORD cbData; LONG lr;
*puWaveId = 0; *pfPreferredOnly = 0;
lr = RegOpenKeyEx(HKEY_CURRENT_USER, gszRegKeynameWaveMapper, 0, KEY_QUERY_VALUE, &hkeyWaveMapper); if (ERROR_SUCCESS == lr) {
szPreferred[0] = '\0'; dwType = REG_SZ; cbData = sizeof(szPreferred); lr = RegQueryValueEx(hkeyWaveMapper, gszRegValuenamePreferredPlayback, NULL, &dwType, szPreferred, &cbData);
if (ERROR_SUCCESS == lr) {
dwType = REG_DWORD; cbData = sizeof(dwPreferredOnly); lr = RegQueryValueEx(hkeyWaveMapper, gszRegValuenamePreferredOnly, NULL, &dwType, (LPBYTE)&dwPreferredOnly, &cbData);
if (ERROR_SUCCESS != lr) { dwPreferredOnly = 0; }
for (u = 0; u < waveOutGetNumDevs(); u++) { if (!waveOutGetDevCaps(u, &woc, sizeof(woc))) {
woc.szPname[SIZEOF(woc.szPname)-1] = '\0'; if (!lstrcmp(woc.szPname, szPreferred)) { *puWaveId = u; *pfPreferredOnly = (0 != dwPreferredOnly); break; } } } }
RegCloseKey(hkeyWaveMapper); } return TRUE; } //--------------------------------------------------------------------------;
//
// HRESULT wavGetIdFromDrvGuid
//
// Description:
// This function finds the mmsystem wave ID of the device that
// corresponds the the specified ds driver
//
// Arguments:
// LPGUID refGuid : guid of a ds driver
//
// LPUINT puWaveId : pointer to UINT to receive the mmsystem wave id
//
// Return (HRESULT):
//
// History:
//
//--------------------------------------------------------------------------;
HRESULT wavGetIdFromDrvGuid(REFGUID refGuid, LPUINT puWaveId) { DSDRIVERDESC dsDrvDesc; DWORD dnDevNode; UINT uDeviceNum; UINT uWaveId; UINT cWaveDevices; BOOL fFound; UINT i; MMRESULT mmr; DSVAL dsv;
FillMemory(&dsDrvDesc, sizeof(dsDrvDesc), 0); dsv = vxdDrvGetDesc(refGuid, &dsDrvDesc); if (DS_OK != dsv) return dsv;
uDeviceNum = dsDrvDesc.ulDeviceNum+1;
cWaveDevices = waveOutGetNumDevs();
DPF(3, "Looking for corresponding mmsystem driver...");
for (uWaveId = 0; uWaveId < cWaveDevices; uWaveId++) { WORD awVxdIds[50]; // we won't handle more than 50 vxds on the devnode
DWORD cReturnedIds;
mmr = waveOutMessage((HWAVEOUT)uWaveId, DRV_QUERYDEVNODE, (DWORD)&dnDevNode, 0);
if (MMSYSERR_NOERROR != mmr) break; if (dnDevNode != dsDrvDesc.dnDevNode) continue;
cReturnedIds = sizeof(awVxdIds) / sizeof(awVxdIds[0]); mmr = waveOutMessage((HWAVEOUT)uWaveId, DRV_QUERYDRIVERIDS, (DWORD)awVxdIds, (DWORD)&cReturnedIds);
if (MMSYSERR_INVALPARAM == mmr) continue; if (MMSYSERR_NOERROR != mmr) break;
fFound = FALSE; for (i=0; i < cReturnedIds; i++) { if (awVxdIds[i] == dsDrvDesc.wVxdId) { fFound = TRUE; break; } }
if ( (fFound) && (0 == --uDeviceNum) ) break; }
if (0 == uDeviceNum) { *puWaveId = uWaveId; dsv = DS_OK; } else { dsv = DSERR_NODRIVER; }
return dsv; }
//--------------------------------------------------------------------------;
//
// HRESULT wavGetDrvGuidFromId
//
// Description:
//
// Arguments:
// UINT uWaveId : wave id
//
// LPGUID pGuid : ptr to guid to receive ds driver guid
//
// Return (HRESULT):
//
// History:
//
//--------------------------------------------------------------------------;
HRESULT wavGetDrvGuidFromId(UINT uWaveIdCaller, LPGUID pGuidCaller) { DSDRIVERDESC dsDrvDesc; GUID guid, guidLast; UINT uWaveId; HRESULT hr; FillMemory(&dsDrvDesc, sizeof(dsDrvDesc), 0); hr = vxdDrvGetNextDriverDesc(NULL, &guid, &dsDrvDesc); while (DS_OK == hr) { hr = wavGetIdFromDrvGuid(&guid, &uWaveId); if ((DS_OK == hr) && (uWaveId == uWaveIdCaller)) { *pGuidCaller = guid; return DS_OK; } guidLast = guid; FillMemory(&dsDrvDesc, sizeof(dsDrvDesc), 0); hr = vxdDrvGetNextDriverDesc(&guidLast, &guid, &dsDrvDesc); }
return hr; }
//--------------------------------------------------------------------------;
//
// LPDSBUFFER ICreateSoundBuffer
//
// 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.
//
// History:
//
//--------------------------------------------------------------------------;
HRESULT FAR PASCAL WaveEmulateCreateSoundBuffer ( LPDSOUND pds, LPDSBUFFER pdsb, LPDSBUFFERDESC pdsbd ) { MMRESULT mmr;
DPF(3,"Wave Emulate CreateSoundBuffer");
// All validation done in other case
//If not primary, allocate the actual buffer
if (!((pdsbd->dwFlags & DSBCAPS_PRIMARYBUFFER))) { pdsb->pDSBuffer = MemAlloc(pdsbd->dwBufferBytes); if (!pdsb->pDSBuffer) { DPF(1,"WE CreateSoundBuffer cannot allocate buffer"); return DSERR_OUTOFMEMORY; } } else { pdsb->pDSBuffer = NULL; }
pdsb->cbBufferSize = pdsbd->dwBufferBytes;
pdsb->fdwDsbI |= DSB_INTERNALF_WAVEEMULATED; pdsb->fdwDsbI |= DSB_INTERNALF_EMULATED;
DPF(3,"--------Alloc data for WAVE Emulated obj %X buff %X len %X", pdsb, pdsb->pDSBuffer, pdsb->cbBufferSize );
if (pdsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) { mmr = DSInitializeEmulator(pds); if (mmr) { DPF(0,"DSInitEmu returns %u", (UINT)mmr); if (MMSYSERR_ALLOCATED == mmr) return DSERR_ALLOCATED; if (MMSYSERR_NOMEM == mmr) return DSERR_OUTOFMEMORY; return DSERR_GENERIC; } }
DPF(3,"Wave Emulate CreateSoundBuffer Exit"); return DS_OK; } // IDSCreateSoundBuffer()
//--------------------------------------------------------------------------;
//
// DsGetDriverVersion
//
// Description: Figure out driver version, and sets the pds version fields.
//
//--------------------------------------------------------------------------;
BOOL DsGetDriverVersion ( LPDSOUND pds, LPTSTR szDrvname ) { TCHAR szDriverPath[MAX_PATH]; int iStrLen; DWORD dwHandle; DWORD dwVerLen; LPVOID pvVerInfo; DWORD dwVsffiLen; VS_FIXEDFILEINFO* pvsffi; BOOL fSucceeded;
if( !GetSystemDirectory( szDriverPath, MAX_PATH ) ) { DPF(0,"DsGetDriverVersion - GetSystemDirectory failed!"); return FALSE; }
iStrLen = lstrlen( szDriverPath ); if( iStrLen + 1 + lstrlen(szDrvname) >= MAX_PATH ) { DPF(0,"DsGetDriverVersion - Ran out of path room!"); return FALSE; }
// We have enough room.
if( szDriverPath[iStrLen-1] != TEXT('\\') ) { szDriverPath[iStrLen++] = TEXT('\\'); szDriverPath[iStrLen] = TEXT('\0'); }
lstrcat( szDriverPath, szDrvname ); dwVerLen = GetFileVersionInfoSize( szDriverPath, &dwHandle ); if( 0 == dwVerLen ) { DPF(0,"DsGetDriverVersion - GetFileVersionInfoSize failed!"); return FALSE; }
pvVerInfo = LocalAlloc ( LPTR, dwVerLen ); if( NULL == pvVerInfo ) { DPF(0,"DsGetDriverVersion - Couldn't allocate memory for pvVerInfo!"); return FALSE; }
// Side effects begin... no more returns.
fSucceeded = FALSE;
if( !GetFileVersionInfo( szDriverPath, dwHandle, dwVerLen, pvVerInfo ) ) { DPF(0,"DsGetDriverVersion - GetFileVersionInfo failed!"); } else { dwVsffiLen = sizeof( VS_FIXEDFILEINFO ); if( !VerQueryValue( pvVerInfo, TEXT("\\"), (LPVOID *)&pvsffi, &dwVsffiLen ) ) { DPF(0,"DsGetDriverVersion - VerQueryValue failed!"); } else { pds->dwDriverVersionMajor = pvsffi->dwFileVersionMS; pds->dwDriverVersionMinor = pvsffi->dwFileVersionLS; DPF(3,"DsGetDriverVersion - succeeded, major=0x%08x, minor=0x%08x",pds->dwDriverVersionMajor,pds->dwDriverVersionMinor); fSucceeded = TRUE; } }
LocalFree( pvVerInfo ); return fSucceeded; }
//--------------------------------------------------------------------------;
//
// CreateDsNative
//
// This function creates a DSOUND object (a pds) using a native mode driver
// identified by a guid.
//
// Parameters:
// REFGUID rguid : identifies the guid of the requested driver
//
// LPDSOUND *ppds : receives a pointer to the new DSOUND object
//
// Return value (HRESULT):
// DS_OK : if and only if a DSOUND object has been created successfully
//
// DSERR_NODRIVER : if there no native driver having the specified guid
//
// other : some other failure
//
// Notes:
// For return values other than DS_OK, there should be no side effects
//
//--------------------------------------------------------------------------;
HRESULT WINAPI CreateDsNative(REFGUID rguid, LPDSOUND *ppds) { LPDSOUND pds; DSDRIVERDESC dsDrvDesc; UINT uWaveDeviceID; BOOL fHaveWaveId; int cb; HRESULT hr; MMRESULT mmr;
*ppds = NULL; pds = (LPDSOUND)MemAlloc(sizeof(*pds)); if (NULL == pds) return DSERR_OUTOFMEMORY; // We have a device that is for a Direct Sound MINIDRIVER
DPF(3,"Create DS pds = %X", pds );
ZeroMemory(&dsDrvDesc, sizeof(dsDrvDesc)); hr = vxdDrvGetDesc(rguid, &dsDrvDesc); if (DS_OK == hr) {
pds->fdwDriverDesc = dsDrvDesc.dwFlags; pds->dwHeapType = dsDrvDesc.dwHeapType; pds->dwMemAllocExtra= dsDrvDesc.dwMemAllocExtra;
// Figure out if we're running on a certified driver.
// Sum of driver filename chars + DSCAPS_FILENAMECOOKIE
// mod DSCAPS_FILENAMEMODVALUE must equal dsDrvDesc.wReserved.
{ DWORD dwCertifiedCookie = (DWORD)dsDrvDesc.wReserved; DWORD dwSum = DSCAPS_FILENAMECOOKIE; PTCHAR p = dsDrvDesc.szDrvname;
for ( ; *p != TEXT('\0'); dwSum += (DWORD)(*p++));
if( dwSum%DSCAPS_FILENAMEMODVALUE == dwCertifiedCookie ) { DPF(1,"Running on certified driver."); pds->fdwInternal |= DS_INTERNALF_CERTIFIED; } }
// Set driver version numbers.
if( !DsGetDriverVersion( pds, dsDrvDesc.szDrvname ) ) { DPF(0,"DirectSoundCreateFromGuid - DsGetDriverVersion failed!"); ASSERT( pds->dwDriverVersionMajor == 0 ); ASSERT( pds->dwDriverVersionMinor == 0 ); }
hr = wavGetIdFromDrvGuid(rguid, &uWaveDeviceID); fHaveWaveId = (DS_OK == hr); if (!(DSDDESC_DOMMSYSTEMOPEN & pds->fdwDriverDesc)) hr = DS_OK;
if ((DS_OK == hr) && fHaveWaveId) {
WAVEFORMATEX wfxJustToOpen = { WAVE_FORMAT_PCM, // wFormatTag
1, // nChannels
11025, // nSamplesPerSec
11025, // nAvgBytesPerSec
1, // nBlockAlign
8, // wBitsPerSample
0 // cbSize
};
// The wfx is used only to open the mmsystem wave device, it
// has nothing to do with the format of any DirectSound
// buffers.
// We have to copy the wfx to shared memory since the ddhelp
// process will do the actuall open on the wave driver. The
// wfxDefault member of the ds object is a convenient place
// to put it since it has not yet been initialized with the
// default format for this ds object.
ASSERT(0 == pds->wfxDefault.wFormatTag); CopyMemory(&pds->wfxDefault, &wfxJustToOpen, sizeof(wfxJustToOpen));
mmr = (DWORD)HelperWaveOpen(&pds->hwo, uWaveDeviceID, &pds->wfxDefault);
pds->wfxDefault.wFormatTag = 0;
if (MMSYSERR_NOERROR != mmr) { DPF(0," Wave Open Failed "); pds->hwo = NULL; // We'll consider this an error only if the driver
// specified that we must do mmsystem open
if (DSDDESC_DOMMSYSTEMOPEN & pds->fdwDriverDesc) { if (MMSYSERR_ALLOCATED == mmr) { hr = DSERR_ALLOCATED; } else { hr = DSERR_NODRIVER; } } }
// If driver did not specify that we must do mmsystem open,
// then immediately close the device after opening it.
if ( !(DSDDESC_DOMMSYSTEMOPEN & pds->fdwDriverDesc) && (NULL != pds->hwo) ) { HelperWaveClose((DWORD)pds->hwo); pds->hwo = NULL; } }
//
// Try to open driver
//
if (DS_OK == hr) { hr = vxdDrvOpen(rguid, &(pds->hHal)); if (DS_OK == hr) { DPF(3,"Finished Open HAL layer %X", pds->hHal );
// Handle on-card memory management if necessary
//
pds->pDriverHeap = NULL; if (DSDHEAP_USEDIRECTDRAWHEAP == pds->dwHeapType) { pds->pDriverHeap = dsDrvDesc.pvDirectDrawHeap; } else if (DSDHEAP_CREATEHEAP == pds->dwHeapType) { pds->pDriverHeap = VidMemInit( VMEMHEAP_LINEAR, dsDrvDesc.dwMemStartAddress, dsDrvDesc.dwMemEndAddress, 0, 0 ); if (NULL == pds->pDriverHeap) { RPF("DirectSoundCreate: Couldn't create driver heap for on-card memory" ); hr = DSERR_OUTOFMEMORY; } }
if (DS_OK == hr) { mxInitialize(pds);
cb = sizeof(DSBWAVEBLTI) + (sizeof(DSBWAVEBLTSRCI)*LIMIT_BLT_SOURCES); pds->pdswb = (LPDSBWAVEBLTI)MemAlloc(cb); if (pds->pdswb) { pds->pdswb->padswbs = (LPDSBWAVEBLTSRCI)(((LPSTR)pds->pdswb) + sizeof(*pds->pdswb)); pds->pdswb->dwSize = sizeof(DSBWAVEBLT); pds->pdswb->dwCount = 0;
pds->guid = *rguid; pds->uDeviceID = uWaveDeviceID; pds->pds3d = NULL; pds->uRefCount = 1; pds->fdwInternal |= DS_INTERNALF_ALLOCATED; pds->dwSig = DSOUNDSIG;
pds->pNext = gpdsinfo->pDSoundObj; gpdsinfo->pDSoundObj = pds; } else { hr = DSERR_OUTOFMEMORY; }
if (hr) mxTerminate(pds); }
if (hr) { if (pds->pDriverHeap && (pds->dwHeapType & DSDHEAP_CREATEHEAP)) { VidMemFini(pds->pDriverHeap); pds->pDriverHeap = NULL; } if (pds->hwo) { HelperWaveClose((ULONG)pds->hwo); pds->hwo = NULL; } vxdDrvClose(pds->hHal); pds->hHal = NULL; } } } }
if (hr) { MemFree(pds); pds = NULL; }
if (DS_OK == hr) *ppds = pds;
return hr; }
//--------------------------------------------------------------------------;
//
// CreateDsEmulated
//
// This function creates a DSOUND object (a pds) using an mmsystem wave driver
// identified by a guid.
//
// Parameters:
// REFGUID rguid : identifies the guid of the requested wave driver
//
// LPDSOUND *ppds : receives a pointer to the new DSOUND object
//
// Return value (HRESULT):
// DS_OK : if and only if a DSOUND object has been created successfully
//
// DSERR_NODRIVER : if there is no wave driver having the specified guid
//
// other : some other failure
//
// Notes:
// For return values other than DS_OK, there should be no side effects
//
//--------------------------------------------------------------------------;
HRESULT WINAPI CreateDsEmulated(REFGUID rguid, LPDSOUND *ppds) { LPDSOUND pds; LPDSOUND pdsT; UINT cWaveDev; UINT iWaveDev; HRESULT hr; int cb;
*ppds = NULL; pds = NULL;
cWaveDev = waveOutGetNumDevs(); cWaveDev = min(cWaveDev, LIMIT_WAVE_DEVICES-1);
hr = DS_OK; for (iWaveDev = 0; iWaveDev < cWaveDev; iWaveDev++) { if (IsEqualGUID(rguid, &gpdsinfo->aguidWave[iWaveDev])) break; } if (iWaveDev >= cWaveDev) hr = DSERR_NODRIVER;
if (DS_OK == hr) { // REMIND here is a temporary block to prevent us from
// creating a second waveem ds object on a different
// wave device. This would result in a deadlock. We
// look for another waveem ds object with a different
// guid.
for (pdsT = gpdsinfo->pDSoundObj; pdsT; pdsT = pdsT->pNext) { if ( (pdsT->fdwInternal & DS_INTERNALF_WAVEEMULATED) && (!IsEqualGUID(rguid, &pdsT->guid)) ) { break; } } if (pdsT) hr = DSERR_ALLOCATED;
if (DS_OK == hr) { WAVEOUTCAPS woc; MMRESULT mmr;
mmr = waveOutGetDevCaps(iWaveDev, &woc, sizeof(woc)); switch (mmr) { case MMSYSERR_NOERROR: if (woc.dwSupport & WAVECAPS_SYNC) { hr = DSERR_NODRIVER; } else { hr = DS_OK; } break; case MMSYSERR_NOMEM: hr = DSERR_OUTOFMEMORY; break; case MMSYSERR_NODRIVER: hr = DSERR_NODRIVER; break; default: hr = DSERR_GENERIC; } }
if (DS_OK == hr) {
pds = (LPDSOUND)MemAlloc(sizeof(*pds)); if (pds) { pds->guid = *rguid;
cb = sizeof(DSBWAVEBLTI) + (sizeof(DSBWAVEBLTSRCI)*LIMIT_BLT_SOURCES); pds->pdswb = (LPDSBWAVEBLTI)MemAlloc(cb); if (pds->pdswb) { pds->pdswb->padswbs = (LPDSBWAVEBLTSRCI)(((LPSTR)pds->pdswb) + sizeof(*pds->pdswb)); pds->pdswb->dwSize = sizeof(DSBWAVEBLT); pds->pdswb->dwCount = 0;
//
pds->uDeviceID = iWaveDev; pds->pds3d = NULL; pds->uRefCount = 1; pds->fdwInternal |= DS_INTERNALF_ALLOCATED; pds->fdwInternal |= DS_INTERNALF_WAVEEMULATED; pds->dwSig = DSOUNDSIG;
//
pds->pNext = gpdsinfo->pDSoundObj; gpdsinfo->pDSoundObj = pds; } else { hr = DSERR_OUTOFMEMORY; }
if (hr) { MemFree(pds); pds = NULL; }
} else { hr = DSERR_OUTOFMEMORY; } } }
if (DS_OK == hr) *ppds = pds; return hr; }
//--------------------------------------------------------------------------;
//
// DseInitializeFromGuid
//
// This function attempts to initialize a DSOUNDEXTERNAL object to use
// the direct sound driver identified by rguid.
//
// Parameters:
// REFGUID rguid : identifies the guid of the requested driver
//
// Return value (HRESULT):
// DS_OK : if and only if a DSOUND object has been created successfully
//
// DSERR_NODRIVER : if there is no driver having the specified guid
//
// other : some other failure
//
// Notes:
// For return values other than DS_OK, there should be no side effects
//
//--------------------------------------------------------------------------;
HRESULT WINAPI DseInitializeFromGuid(LPDSOUNDEXTERNAL pdse, REFGUID rguid) { LPDSOUND pds; DSBUFFERDESC dsbd; HANDLE hFocusLock; HRESULT hr;
DPF(0, "DseInitializeFromGuid()");
// First look for an existing ds object with same guid
hr = DSERR_NODRIVER; for (pds = gpdsinfo->pDSoundObj; pds; pds = pds->pNext) { if (IsEqualGUID(rguid, &pds->guid)) { // We've found one! AddRef it since we're going to use it.
DsAddRef(pds); pdse->pds = pds; pdse->pNext = NULL; pdse->dwPID = GetCurrentProcessId(); pdse->pdsbe = NULL; pdse->tidSound = TID_INIT_VALUE; pdse->dwPriority = DSSCL_NORMAL; pdse->dwSpeakerConfig = DSSPEAKER_STEREO; pdse->pwfxApp = NULL;
if (!GetFocusLock(&hFocusLock)) { DPF(2, "DirectSoundCreateFromGuid: note: error getting focus lock"); } pdse->pNext = gpdsinfo->pDSoundExternalObj; gpdsinfo->pDSoundExternalObj = pdse; if (!ReleaseFocusLock(hFocusLock)) { DPF(2, "DirectSoundCreateFromGuid: note: error releasing focus lock"); } return DS_OK; } }
// Try native ds
if (DSERR_NODRIVER == hr) hr = CreateDsNative(rguid, &pds);
// Try emulated ds
if (DSERR_NODRIVER == hr) hr = CreateDsEmulated(rguid, &pds);
// If none of the above worked, return
if (DS_OK != hr) return hr;
// At this point, we have a pds that has been addrefed
hr = DsInitializeDefaultFormat(pds); if (DS_OK != hr) { // We don't really care about this failure
DPF(0, "DirectSoundCreateFromGuid: note: DsInitializeDefaultFormat returned %08Xh", hr); hr = DS_OK; }
//
pdse->pds = pds; pdse->pNext = NULL; pdse->dwPID = GetCurrentProcessId(); pdse->pdsbe = NULL; pdse->tidSound = TID_INIT_VALUE; pdse->dwPriority = DSSCL_NORMAL; pdse->dwSpeakerConfig = DSSPEAKER_STEREO; pdse->pwfxApp = NULL;
//
ZeroMemory(&dsbd, sizeof(dsbd)); dsbd.dwSize = sizeof(dsbd); dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; dsbd.dwBufferBytes = 0; hr = DseCreateDsbe(pdse, &dsbd, &pds->pdsbePrimary); if (DS_OK == hr) { pds->pdsbePrimary->dwPriority = DSSCL_WRITEPRIMARY; pds->pdsbePrimary->dwPID = DWPRIMARY_INTERNAL_PID; pds->pdsbePrimary->pNext = NULL; pds->pdsbePrimary->pdse = NULL; pds->pdsbPrimary->dsbe.pdse = NULL; pds->pdsbPrimary->plProcess->dwPID = DWPRIMARY_INTERNAL_PID;
pdse->pdsbe = NULL; }
if (DS_OK == hr) { //
if (!GetFocusLock(&hFocusLock)) { DPF(2, "DirectSoundCreateFromGuid: note: error getting focus lock"); } pdse->pNext = gpdsinfo->pDSoundExternalObj; gpdsinfo->pDSoundExternalObj = pdse; if (!ReleaseFocusLock(hFocusLock)) { DPF(2, "DirectSoundCreateFromGuid: note: error releasing focus lock"); } }
if (DS_OK != hr) { DsRelease(pds); pds = NULL; }
return hr; } HRESULT WINAPI DirectSoundCreate ( LPGUID lpGUID, LPDIRECTSOUND *ppIDs, IUnknown FAR *pUnkOuter ) { LPDIRECTSOUND pIDs; HRESULT hr;
if( !VALID_DWORD_PTR(ppIDs) ) { RPF("DirectSoundCreate: Invalid pointer to DS object pointer"); return DSERR_INVALIDPARAM; } *ppIDs = NULL;
DPF(3, "DirectSoundCreate pGUID %X",(DWORD)lpGUID ); if( lpGUID != NULL ) { DPF(3, "DirectSoundCreate GUID %X %X %X %X", *(((LPDWORD)lpGUID) + 0 ), *(((LPDWORD)lpGUID) + 1 ), *(((LPDWORD)lpGUID) + 2 ), *(((LPDWORD)lpGUID) + 3 ) ); }
hr = CreateNewDirectSoundObject (&pIDs, pUnkOuter); if (S_OK != hr) return hr; hr = IDirectSound_Initialize(pIDs, lpGUID); if (S_OK == hr) { *ppIDs = pIDs; } else { IDirectSound_Release(pIDs); }
return hr; }
//--------------------------------------------------------------------------
//
// Enumerate
//
// This is a helper function for DirectSoundEnumerate. This function will
// execute the callback to the client. The caller of this function provides
// a pointer to a BOOL which this function uses to determine whether to
// enumerate the "Primary Sound Device".
//
// Arguments:
// LPDSENUMCALLBACK lpCallback:
// LPVOID lpContext:
// REFGUID rguid:
// LPTSTR pszDescription:
// LPTSTR pszDriver:
// These are required to execute the callback to the client
// PBOOL pfDidPrimary:
// Indicates whether the primary driver has already been enumerated.
// If it hasn't, this function will enumerate it and set this BOOL
// to TRUE.
//
// Return (BOOL):
// The return value from the client's callback.
//
//--------------------------------------------------------------------------
BOOL EnumerateA(LPDSENUMCALLBACKA lpCallback, LPVOID lpContext, LPGUID pguid, char *pszDescription, char *pszDriver, PBOOL pfDidPrimary) { BOOL fReturn; char szDriver[MAX_PATH]; char szDescription[MAX_PATH]; if (!*pfDidPrimary) { *pfDidPrimary = TRUE; szDescription[0] = '\0'; szDriver[0] = '\0'; if( !LoadStringA( hModule, IDS_PRIMARYDRIVER, szDescription, sizeof(szDescription) ) || !LoadStringA( hModule, IDS_SOUND, szDriver, sizeof(szDriver) ) ) { DPF(0,"EnumerateA : error: LoadString failed."); }
DPF(3,"EnumerateA : note: enumerating primary driver: Desc=\"%s\", Filename=\"%s\".",szDescription,szDriver); fReturn = lpCallback( NULL, szDescription, szDriver, lpContext ); if (!fReturn) return fReturn; } fReturn = lpCallback(pguid, pszDescription, pszDriver, lpContext);
return fReturn; }
BOOL EnumerateW(LPDSENUMCALLBACKW lpCallback, LPVOID lpContext, LPGUID pguid, WCHAR *pszDescription, WCHAR *pszDriver, PBOOL pfDidPrimary) { BOOL fReturn; WCHAR szDriver[MAX_PATH]; WCHAR szDescription[MAX_PATH]; if (!*pfDidPrimary) { *pfDidPrimary = TRUE; szDescription[0] = '\0'; szDriver[0] = '\0'; if( !LoadStringW( hModule, IDS_PRIMARYDRIVER, szDescription, SIZEOF(szDescription) ) || !LoadStringW( hModule, IDS_SOUND, szDriver, SIZEOF(szDriver) ) ) { DPF(0,"EnumerateW : error: LoadString failed."); }
DPF(3,"EnumerateW : note: enumerating primary driver: Desc=\"%ls\", Filename=\"%ls\".",szDescription,szDriver); fReturn = lpCallback( NULL, szDescription, szDriver, lpContext ); if (!fReturn) return fReturn; } fReturn = lpCallback(pguid, pszDescription, pszDriver, lpContext);
return fReturn; }
/*
* DirectSoundEnumerateA */ HRESULT WINAPI DirectSoundEnumerateA( LPDSENUMCALLBACKA lpCallback, LPVOID lpContext ) { DWORD rc; DSDRIVERDESC dsDrvDesc; GUID guid; DSVAL dsv; DWORD dw; DWORD dwNumDev; BOOL fDidPrimary; char szDriver[MAX_PATH]; char szDescription[MAX_PATH];
struct _wave_em_info { DWORD dnDevNode; UINT ulDeviceNum; UINT nVxdIds; BOOL bEnumerated; WORD awVxdIds[50]; } * pWaveEm = NULL;
if( !VALID_CODE_PTR( lpCallback ) ) { RPF("DirectSoundEnumerate: Invalid callback routine" ); return DSERR_INVALIDPARAM; }
fDidPrimary = FALSE;
// gather up devnode & vxd id information from mmsystem
// and build a table devices in pWaveEm. we will check off
// devices from this table as we enumerate so that we wont
// end up enumerating the same device twice
//
DPF(3, "enum: getting devnodes for wave devices"); dwNumDev = waveOutGetNumDevs(); dwNumDev = min(dwNumDev, LIMIT_WAVE_DEVICES-1); pWaveEm = NULL; if (dwNumDev) { // if we fail to allocate memory, go ahead anyway.
// we will just end up enumerating some devices more than
// once in this case.
//
pWaveEm = MemAlloc (sizeof(*pWaveEm) * dwNumDev); if (pWaveEm) { for (dw = 0; dw < dwNumDev; ++dw) { DWORD ii;
if (waveOutMessage ((HWAVEOUT)dw, DRV_QUERYDEVNODE, (DWORD)&pWaveEm[dw].dnDevNode, 0)) { pWaveEm[dw].dnDevNode = 0; }
pWaveEm[dw].nVxdIds = NUMELMS(pWaveEm[dw].awVxdIds); if (waveOutMessage ((HWAVEOUT)dw, DRV_QUERYDRIVERIDS, (DWORD)pWaveEm[dw].awVxdIds, (DWORD)&pWaveEm[dw].nVxdIds)) { pWaveEm[dw].nVxdIds = 0; }
pWaveEm[dw].ulDeviceNum = 0; for (ii = 0; ii < dw; ++ii) { if (pWaveEm[ii].dnDevNode == pWaveEm[dw].dnDevNode) { ++pWaveEm[dw].ulDeviceNum; } }
DPF(3, "Wave[%d,%d] dn=%08lX awVxd=%d:{%x,%x,%x,%x}", dw, pWaveEm[dw].ulDeviceNum, pWaveEm[dw].dnDevNode, pWaveEm[dw].nVxdIds, pWaveEm[dw].awVxdIds[0], pWaveEm[dw].awVxdIds[1], pWaveEm[dw].awVxdIds[2], pWaveEm[dw].awVxdIds[3] );
pWaveEm[dw].bEnumerated = FALSE; } } else { DPF(0, "Direct Sound: Failed to allocate pWaveEm, may enumerate some devices more than once!"); } }
//
// REMIND handle error conditions better, also enumerate emulated devices
//
#if defined(RDEBUG) || defined(DEBUG)
if (!(gpdsinfo->fEnumOnlyWaveDevs)) #endif
{ ZeroMemory(&dsDrvDesc, sizeof(dsDrvDesc)); dsv = vxdDrvGetNextDriverDesc(NULL, &guid, &dsDrvDesc); while (DS_OK == dsv) { GUID guidLast;
DPF(3, "DirectSoundEnum GUID %X %X %X %X", *(((LPDWORD)&guid) + 0 ), *(((LPDWORD)&guid) + 1 ), *(((LPDWORD)&guid) + 2 ), *(((LPDWORD)&guid) + 3 ) );
// if there is a wave device corresponding to this device,
// check it off as having been enumerated already.
//
if (pWaveEm) { for (dw = 0; dw < dwNumDev; ++dw) { DPF(3, "\tdn=%08lX num=%d vxd=%X", dsDrvDesc.dnDevNode, dsDrvDesc.ulDeviceNum, dsDrvDesc.wVxdId); if ((pWaveEm[dw].dnDevNode == dsDrvDesc.dnDevNode) && (pWaveEm[dw].ulDeviceNum == dsDrvDesc.ulDeviceNum)) { UINT ii; for (ii = 0; ii < pWaveEm[dw].nVxdIds; ++ii) { if ((pWaveEm[dw].awVxdIds[ii] == dsDrvDesc.wVxdId)) { pWaveEm[dw].bEnumerated = TRUE; DPF(3, "Enum: marking wave as already enum'd"); break; } } } } }
DPF(3,"DsEnum - enumerating driver: Desc=\"%s\", Filename=\"%s\".",dsDrvDesc.szDesc,dsDrvDesc.szDrvname); rc = EnumerateA(lpCallback, lpContext, &guid, dsDrvDesc.szDesc, dsDrvDesc.szDrvname, &fDidPrimary); if ( !rc ) goto bailout; guidLast = guid; ZeroMemory(&dsDrvDesc, sizeof(dsDrvDesc)); dsv = vxdDrvGetNextDriverDesc(&guidLast, &guid, &dsDrvDesc); } }
// loop though the wave devices, enumererating as emulated,
// those devices that have not already been enumerated as native.
//
for (dw = 0; dw < dwNumDev; ++dw) { struct { WAVEOUTCAPSA woc; char szFill[MAX_PATH]; // gurantee room for appended string
} uu;
guid = gpdsinfo->aguidWave[dw];
//#define ENUM_DSDEVS_AS_WAVE_ALSO
// if we already enumerated this device as non-emulated
// skip it here.
//
if (pWaveEm && pWaveEm[dw].bEnumerated) { if (!(gpdsinfo->fDupEnumWaveDevs)) { DPF(3, "Direct Sound Enum Wave%d already enumerated", dw); continue; } }
DPF(3, "DirectSoundEnum Wave%d GUID %X %X %X %X", dw, *(((LPDWORD)&guid) + 0 ), *(((LPDWORD)&guid) + 1 ), *(((LPDWORD)&guid) + 2 ), *(((LPDWORD)&guid) + 3 ) );
waveOutGetDevCapsA (dw, &uu.woc, sizeof(uu.woc));
szDescription[0] = '\0'; if( !LoadStringA( hModule, IDS_EMULATED, szDescription, sizeof(szDescription) ) ) { DPF(0,"DirectSoundEnumerateA - LoadString2 failed."); } lstrcatA (uu.woc.szPname, szDescription);
szDescription[0] = TEXT('\0'); szDriver[0] = TEXT('\0'); if( !LoadStringA( hModule, IDS_DRIVERLD, szDescription, sizeof(szDescription) ) ) { DPF(0,"DirectSoundEnumerateA - LoadString3 failed."); } else { wsprintfA (szDriver, szDescription, dw); } if (0 == (uu.woc.dwSupport & WAVECAPS_SYNC)) { DPF(3,"DsEnum - enumerating driver: Desc=\"%s\", Filename=\"%s\".",uu.woc.szPname,szDriver); rc = EnumerateA(lpCallback, lpContext, &guid, uu.woc.szPname, szDriver, &fDidPrimary); if ( !rc ) goto bailout; } else { DPF(2, "DsEnum: note: driver \"%s\" reported WAVECAPS_SYNC, not emumerating", uu.woc.szPname); } }
bailout: if (pWaveEm) MemFree (pWaveEm); return DS_OK; } /* DirectSoundEnumerateA */
HRESULT WINAPI DirectSoundEnumerateW( LPDSENUMCALLBACKW lpCallback, LPVOID lpContext ) { #ifndef WINNT
if( !VALID_CODE_PTR( lpCallback ) ) { RPF("DirectSoundEnumerate: Invalid callback routine" ); return DSERR_INVALIDPARAM; }
return DSERR_UNSUPPORTED;
#else
#ifndef DSBLD_EMULONLY
// this is implemented for only emulation mode
#error
#endif
DWORD rc; GUID guid; DWORD dw; DWORD dwNumDev; BOOL fDidPrimary; WCHAR szDriver[MAX_PATH]; WCHAR szDescription[MAX_PATH];
if( !VALID_CODE_PTR( lpCallback ) ) { RPF("DirectSoundEnumerate: Invalid callback routine" ); return DSERR_INVALIDPARAM; }
fDidPrimary = FALSE; // loop though the wave devices, enumererating as emulated,
// those devices that have not already been enumerated as native.
dwNumDev = waveOutGetNumDevs(); for (dw = 0; dw < dwNumDev; ++dw) { struct { WAVEOUTCAPSW woc; WCHAR szFill[MAX_PATH]; // gurantee room for appended string
} uu;
guid = gpdsinfo->aguidWave[dw];
DPF(3, "DirectSoundEnum Wave%d GUID %X %X %X %X", dw, *(((LPDWORD)&guid) + 0 ), *(((LPDWORD)&guid) + 1 ), *(((LPDWORD)&guid) + 2 ), *(((LPDWORD)&guid) + 3 ) );
waveOutGetDevCapsW (dw, &uu.woc, sizeof(uu.woc));
szDescription[0] = L'\0'; if( !LoadStringW( hModule, IDS_EMULATED, szDescription, SIZEOF(szDescription) ) ) { DPF(0,"DirectSoundEnumerateW - LoadString2 failed."); } lstrcatW (uu.woc.szPname, szDescription);
szDescription[0] = L'\0'; szDriver[0] = L'\0'; if( !LoadStringW( hModule, IDS_DRIVERLD, szDescription, SIZEOF(szDescription) ) ) { DPF(0,"DirectSoundEnumerateW - LoadString3 failed."); } else { wsprintfW (szDriver, szDescription, dw); } if (0 == (uu.woc.dwSupport & WAVECAPS_SYNC)) { DPF(3,"DsEnum - enumerating driver: Desc=\"%ls\", Filename=\"%ls\".",uu.woc.szPname,szDriver); rc = EnumerateW(lpCallback, lpContext, &guid, uu.woc.szPname, szDriver, &fDidPrimary); if ( !rc ) goto bailout; } else { DPF(2, "DsEnum: note: driver \"%ls\" reported WAVECAPS_SYNC, not emumerating", uu.woc.szPname); } }
bailout: return DS_OK; #endif
} /* DirectSoundEnumerateW */
//---------------------------------------------------------------------------
//
// DSDetermineDMASize
//
// Determine DMA buffer size on the given emulated direct sound device and
// munge it to figure out the desired size for the emulator to allocate
// per wave header.
//
//---------------------------------------------------------------------------
#define TIMEOUT_PERIOD 5000
MMRESULT DSDetermineDMASize ( LPDSOUND pds, LPWAVEFORMATEX pwfx ) { UINT idx; WAVEHDR whdr; UINT mmResult; BYTE aWaveData[4]; DWORD dwTotalTime; DWORD dwBeginTime; DWORD ulDmaSizeBytes;
DPF(3, "DSDetermineDMASize");
// we'll send a packet of 4 bytes
// (that's at least 1 sample in every format)
for (idx = 0; idx < sizeof(aWaveData); ++idx) aWaveData[idx] = (pwfx->wBitsPerSample == 16) ? 0 : 0x80;
// prepare header
//
whdr.lpData = (LPBYTE)aWaveData; whdr.dwBufferLength = sizeof(aWaveData); whdr.dwFlags = 0; whdr.dwLoops = 0; whdr.dwUser = 0; mmResult = waveOutPrepareHeader (pds->hwo, &whdr, sizeof(whdr)); if (mmResult) return mmResult;
dwTotalTime = 0; dwBeginTime = timeGetTime();
// play our buffer
mmResult = waveOutWrite(pds->hwo, &whdr, sizeof(whdr)); if (!mmResult) { // spin until the done bit is set, or 5 seconds
while (!(whdr.dwFlags & WHDR_DONE)) { if (dwTotalTime >= TIMEOUT_PERIOD) { DPF (0, "TIMEOUT getting dma buffer size"); mmResult = MMSYSERR_ERROR; //
// Without the reset we were freeing the header before the
// driver could process it, thus causing faults in the driver.
//
waveOutReset(pds->hwo); break; }
// This thread is THREAD_PRIORITY_TIME_CRITICAL so it would be
// very dangerous to busy wait without explicitly giving up the
// CPU for a while.
Sleep(10);
dwTotalTime = timeGetTime() - dwBeginTime; } } else { DPF(0, "waveOutWrite (determine DMA size) returned %u", mmResult); }
waveOutUnprepareHeader(pds->hwo, &whdr, sizeof(whdr));
if (!mmResult) { // if it's smaller than 62ms, it probably isn't
// a dma based card.
dwTotalTime = max(dwTotalTime, 62);
DPF(3, "DSDetermineDMASize dwTotalTime %lu", dwTotalTime);
ulDmaSizeBytes = dwTotalTime * pwfx->nSamplesPerSec; ulDmaSizeBytes *= pwfx->nBlockAlign; ulDmaSizeBytes /= 1000;
DPF(3, "ulDmaSizeBytes %lu", (DWORD)ulDmaSizeBytes); // add in 10% for slop
// and to account for drivers that deal with dma wrapping
ulDmaSizeBytes += (ulDmaSizeBytes * 10) / 100; // make sure it's a mod 4 (no samples spanning buffers);
ulDmaSizeBytes &= 0xfffffffc;
pds->cbDMASize = ulDmaSizeBytes;
} else { DPF(3, "DSDetermineDMASize: waveOutWRite returned %u", (UINT)mmResult); }
DPF(3, "DSDetermineDMASize done"); return mmResult; }
//---------------------------------------------------------------------------
//
// DSGetCurrentSample
//
// Basically sample-accurate waveOutGetPosition
//
// If we're sample accurate, just call waveOutGetPosition
//
// Otherwise, call QueryPerformanceCounter and calculate what sample
// should be playing.
//
// NOTE: What happens if the wave device was ever starved? We will
// be out of sync if that happens
//
//---------------------------------------------------------------------------
DWORD FNGLOBAL DSGetCurrentSample ( LPDSOUND pds ) { LARGE_INTEGER qwTicks;
if (pds->fdwInternal & DS_INTERNALF_SAMPLEACCURATE) { MMTIME mmtTime; mmtTime.wType = TIME_SAMPLES; waveOutGetPosition(pds->hwo, &mmtTime, sizeof(mmtTime)); return mmtTime.u.sample; } // implied "ELSE"
// have we re-set the device?
if (pds->qwTicks.LowPart == 0) { // yep
return 0; }
QueryPerformanceCounter (&qwTicks);
// assert(qwTicks.LowPart - pMixData->qwTicks.LowPart);
// assert(pMixData->ulTickRate);
// this deals with the wrap case cuz everyting is 32 bits
//
return UMulDivRDClip(qwTicks.LowPart - pds->qwTicks.LowPart, pds->pdsbPrimary->pwfx->nSamplesPerSec, pds->ulTickRate); }
//---------------------------------------------------------------------------
//
// DSSetCurrentSample
//
// Find a synchronization point between waveOutGetPosition and
// QueryPerformanceCounter
//
// If we have a sample accurate device, don't bother - we'll just
// trust the driver
//
// Otherwise, wait for the instant waveOutGetPosition changes
// and save off the QueryPerformanceCounter at that time. Then
// calculate the QPC time when sample 0 played. This is the
// timebase we save.
//
// !!! Failure cases !!!
//
//---------------------------------------------------------------------------
BOOL DSSetCurrentSample ( LPDSOUND pds ) { LARGE_INTEGER qwFreq; MMTIME mmtTime; DWORD ulCardTime; DWORD tmTimeout; MMRESULT mmr;
DPF(3, "DSSetCurrentSample");
QueryPerformanceFrequency(&qwFreq);
mmtTime.wType = TIME_SAMPLES; mmr = waveOutGetPosition(pds->hwo, &mmtTime, sizeof(mmtTime));
if ((!mmr) && mmtTime.wType != TIME_SAMPLES) { DPF(0, "This driver sucks! (Doesn't support TIME_SAMPLES)"); mmtTime.wType = TIME_MS; mmr = waveOutGetPosition(pds->hwo, &mmtTime, sizeof(mmtTime)); if ((!mmr) && mmtTime.wType != TIME_MS) { DPF(0, "This driver REALLY sucks! (Doesn't support TIME_MS either)"); mmtTime.wType = TIME_BYTES; mmr = waveOutGetPosition(pds->hwo, &mmtTime, sizeof(mmtTime)); if ((!mmr) && mmtTime.wType != TIME_BYTES) { DPF(0, "Last chance failed! Card does not even support TIME_BYTES!"); DPF(0, "Bob, I'm not fixing any card that exhibits this behavior!!!"); DPF(0, "Given time format was %u", (UINT)mmtTime.wType); return FALSE; } } }
tmTimeout = timeGetTime(); ulCardTime = (mmtTime.wType == TIME_MS ? mmtTime.u.ms : (mmtTime.wType == TIME_SAMPLES ? mmtTime.u.sample : mmtTime.u.cb)); do { mmr = waveOutGetPosition (pds->hwo, &mmtTime, sizeof(mmtTime)); if (mmr) { DPF(0, "waveOutGetPosition returned %u. BAD driver!!!w", mmr); return FALSE; } if (timeGetTime() - tmTimeout >= TIMEOUT_PERIOD*2) { DPF(0, "Timeout in DSSetCurrentSample!"); return FALSE; } } while (ulCardTime == (mmtTime.wType == TIME_MS ? mmtTime.u.ms : (mmtTime.wType == TIME_SAMPLES ? mmtTime.u.sample : mmtTime.u.cb)));
ASSERT(pds->pdsbPrimary->pwfx->wFormatTag == WAVE_FORMAT_PCM); if (mmtTime.wType == TIME_BYTES) { mmtTime.wType = TIME_SAMPLES; mmtTime.u.sample = mmtTime.u.cb / pds->pdsbPrimary->pwfx->nBlockAlign; }
DPF(3, "DSSetCurrentSample: End profile");
QueryPerformanceCounter(&pds->qwTicks);
if (qwFreq.HighPart != 0) { DPF(0, "Clock > 4Ghz in SetCurrentSample ???"); DPF(0, "Please send this machine to [email protected]");
return FALSE; } pds->ulTickRate = qwFreq.LowPart;
// assert( pds->ulTickRate );
// assert( mmtTime.u.sample );
if (mmtTime.wType == TIME_SAMPLES) { pds->qwTicks.LowPart -= UMulDivRDClip(mmtTime.u.sample, pds->ulTickRate, pds->pdsbPrimary->pwfx->nSamplesPerSec); } else { ASSERT(mmtTime.wType == TIME_MS); pds->qwTicks.LowPart -= UMulDivRDClip(mmtTime.u.ms, pds->ulTickRate, 1000); }
DPF(3, "DSSetCurrentSample done");
return TRUE; }
//---------------------------------------------------------------------------
//
// waveUnprepareLoopingBuffers
//
//---------------------------------------------------------------------------
MMRESULT waveUnprepareLoopingBuffers(LPDSOUND pds) { int i; MMRESULT mmr; for (i=0; i<NUMELMS(pds->aWaveHeader); i++) { mmr = waveOutUnprepareHeader(pds->hwo, &pds->aWaveHeader[i], sizeof(pds->aWaveHeader[i])); ASSERT(!mmr); }
return MMSYSERR_NOERROR; }
//---------------------------------------------------------------------------
//
// waveAllocLoopingBuffers
//
// Allocate the array of wavehdr's to point at the primary buffer.
// Prepare them and start them looping.
//
// !!! Add error checking to the waveOut calls !!!
//
//---------------------------------------------------------------------------
MMRESULT waveAllocAndPrepareLoopingBuffers ( LPDSOUND pds ) { int iawh; MMRESULT mmr; WAVEOUTCAPS woc;
DPF(3, "waveAllocLoopingBuffers");
pds->pLoopingBuffer = MemAlloc(N_EMU_WAVE_HDRS * pds->cbDMASize); if (!pds->pLoopingBuffer) { RPF("No memory for looping buffer!"); return MMSYSERR_NOMEM; }
// Initialize to 8 or 16-bit silence
FillMemory(pds->pLoopingBuffer, N_EMU_WAVE_HDRS * pds->cbDMASize, (BYTE)((pds->pdsbPrimary->pwfx->wBitsPerSample == 8) ? 0x80 : 0x00));
// build buffers and headers
for (iawh = 0; iawh < NUMELMS(pds->aWaveHeader); ++iawh) { pds->aWaveHeader[iawh].lpData = (LPBYTE)(pds->pLoopingBuffer + (iawh * (pds->cbDMASize))); pds->aWaveHeader[iawh].dwBufferLength = pds->cbDMASize; pds->aWaveHeader[iawh].dwUser = iawh; pds->aWaveHeader[iawh].dwFlags = 0; }
pds->iawhPlaying = 0;
DPF(5, "waveAllocLoopingBuffers: note: first waveOutPrepare and waveOutWrite"); mmr = waveOutPrepareHeader(pds->hwo, &pds->aWaveHeader[0], sizeof(pds->aWaveHeader[0])); ASSERT(!mmr); mmr = waveOutWrite(pds->hwo, &pds->aWaveHeader[0], sizeof(pds->aWaveHeader[0])); if (!mmr) { mmr = waveOutPause(pds->hwo); ASSERT(!mmr);
// send down the rest
for (iawh = 1; iawh < NUMELMS(pds->aWaveHeader); ++iawh) { mmr = waveOutPrepareHeader(pds->hwo, &pds->aWaveHeader[iawh], sizeof(pds->aWaveHeader[iawh])); if (mmr) break; mmr = waveOutWrite (pds->hwo, &pds->aWaveHeader[iawh], sizeof(pds->aWaveHeader[iawh])); if (mmr) break; }
// BUGBUG We should have better error recovery here to reset the wave
// device and unprepare any headers that were successfully prepared
if (!mmr) { DPF(5, "waveOutRestart"); // start the device
mmr = waveOutRestart (pds->hwo); ASSERT(!mmr);
DPF(5, "waveOutGetDevCaps");
// Determine if we're sample accurate
//
mmr = waveOutGetDevCaps((UINT)pds->hwo, &woc, sizeof(woc)); pds->fdwInternal &= ~DS_INTERNALF_SAMPLEACCURATE; if (MMSYSERR_NOERROR == mmr && (woc.dwSupport & WAVECAPS_SAMPLEACCURATE)) { DPF(1, "Emulate: Running on sample accurate driver"); pds->fdwInternal |= DS_INTERNALF_SAMPLEACCURATE; } else { mmr = MMSYSERR_NOERROR; DPF(1, "Emulate: Driver is NOT sample accurate!!!"); // Set up our own timing
if (!DSSetCurrentSample(pds)) mmr = MMSYSERR_ERROR; }
pds->pdsbPrimary->pDSBuffer = pds->pLoopingBuffer; pds->pdsbPrimary->cbBufferSize = N_EMU_WAVE_HDRS * pds->cbDMASize; } }
if (mmr) MemFree(pds->pLoopingBuffer);
DPF(3, "DSSetupLoopingBuffers done, mmr=%08lX", mmr); return mmr; }
//---------------------------------------------------------------------------
//
// waveThreadCallback
//
// This is a waveOutProc callback function. Its sole purpose is to
// increment a count of done headers and signal and event to waveThreadLoop
// that another header is done.
//
//---------------------------------------------------------------------------
VOID CALLBACK _loadds waveThreadCallback ( HWAVE hwo, UINT uMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2 ) { LPDSOUND pds = (LPDSOUND)dwUser; // get our context
if ((MM_WOM_DONE == uMsg) && (pds->hEventWaveHeaderDone)) { InterlockedIncrement(&pds->cwhDone); SetEvent(pds->hEventWaveHeaderDone); } }
//---------------------------------------------------------------------------
//
// waveThreadLoop
//
// This function is responsible for continuously writing our wave headers
// to the wave device. It also calls the MixThreadCallback routine to
// mix more data into the wave headers.
//
// This function waits for a WaveHeaderDone event signalled by
// waveThreadCallback, which is a waveOutProc callback function. Upon
// receving the signal this function will write all done headers back to
// the wave device. Normally one header will be done on each signal. But
// there may be more in cases where more than one header finishex before this
// thread is scheduled.
//
// Once all done headers are rewritten to the wave device, the header
// following the last one written is considered to be the one currently
// playing. This header is called the "committed" header and an index to
// it is saved in pds->iawhPlaying.
//
// The count of done headers is maintained using the Interlocked APIs. The
// waveThreadCallback function will increment the count and this function will
// decrement it.
//
// This function will also react to a terminate event. This event is
// signalled during release of the DirectSound object. This loop will
// terminate and return to the waveThread function which will clean up
// and terminate.
//
//---------------------------------------------------------------------------
void waveThreadLoop(LPDSOUND pds, HANDLE hEventTerminate) { MMRESULT mmr;
while(TRUE) { int cIterations; BOOL fPaused; DWORD dwResult; LONG l; LPWAVEHDR pwh; HANDLE ah[2] = { hEventTerminate, pds->hEventWaveHeaderDone };
// The first wait is for either a terminate or headerdone event.
// The second wait is for either a terminate or the DLL mutex.
dwResult = WaitForMultipleObjectsEx(2, ah, FALSE, INFINITE, FALSE); if (WAIT_OBJECT_0 == dwResult) break; ASSERT((WAIT_OBJECT_0 + 1) == dwResult);
cIterations = 0; fPaused = FALSE; l = InterlockedDecrement(&pds->cwhDone); while (l >= 0) { dwResult = ENTER_DLL_CSECT_OR_EVENT(hEventTerminate); if (WAIT_OBJECT_0 == dwResult) break;
pwh = &pds->aWaveHeader[pds->iawhPlaying]; pds->iawhPlaying = (++pds->iawhPlaying) % NUMELMS(pds->aWaveHeader); MixThreadCallback(pds); LEAVE_DLL_CSECT();
// If it looks like we're spending all our time mixing, then
// let's pause the wave device until we catch up. It is very
// important that we eventually catch up. If we are always
// behind then this high priority thread might starve every
// other thread in the system.
cIterations++; if (cIterations > NUMELMS(pds->aWaveHeader)) { mmr = waveOutPause(pds->hwo); ASSERT(!mmr); fPaused = TRUE; cIterations = 0; }
mmr = waveOutWrite(pds->hwo, pwh, sizeof(*pwh)); ASSERT(!mmr); l = InterlockedDecrement(&pds->cwhDone); } InterlockedIncrement(&pds->cwhDone); if (fPaused) { mmr = waveOutRestart(pds->hwo); ASSERT(!mmr); }
if (WAIT_OBJECT_0 == dwResult) break; } DPF(0, "waveThreadLoop: note: exiting"); return; }
//---------------------------------------------------------------------------
//
// waveThread
//
// This thread proc initializes the wave device for ds emulation and then
// calls waveThreadLoop. See the waveThreadLoop comment header. Upon
// return from waveThreadLoop, this function will clean up and terminate.
//
//---------------------------------------------------------------------------
DWORD __stdcall waveThread ( PVOID pThreadParms ) { LPDSOUND pds = (LPDSOUND)pThreadParms; HANDLE hEventInitDone; HANDLE hEventTerminate; DWORD dwPriority; DWORD dwVolume; MMRESULT mmrInit; MMRESULT mmr;
//
// mmrInit - holds the result code to be passed back to the creator
// via pds->mmrWaveThreadInit.
//
// mmr - a temp result code
//
DPF(0, "waveThread startup for pds=%08lX", pds);
ASSERT(NULL == pds->hwo);
hEventInitDone = CreateEvent(NULL, FALSE, FALSE, pds->szEventWaveThreadInitDone); if (!hEventInitDone) { DPF(0, "waveThread: error: couldn't create hEventInitDone"); return 0; } dwPriority = GetPriorityClass(GetCurrentProcess()); SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); mmrInit = waveOutOpen(&pds->hwo, pds->uDeviceID, pds->pdsbPrimary->pwfx, (DWORD)waveThreadCallback, (DWORD)pds, CALLBACK_FUNCTION); SetPriorityClass(GetCurrentProcess(), dwPriority);
if (!mmrInit) { // Some mmsystem wave drivers will program their wave mixer
// hardware only while the device is open. By doing the
// following, we can get such drivers to program the hardware
if (MMSYSERR_NOERROR == waveOutGetVolume(pds->hwo, &dwVolume)) { waveOutSetVolume(pds->hwo, dwVolume); }
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); mmrInit = DSDetermineDMASize(pds, pds->pdsbPrimary->pwfx); if (!mmrInit) {
ASSERT(NULL == pds->hEventWaveHeaderDone); pds->cwhDone = 0; pds->hEventWaveHeaderDone = CreateEvent(NULL, FALSE, FALSE, pds->szEventWaveHeaderDone); if (!pds->hEventWaveHeaderDone) mmrInit = MMSYSERR_NOMEM; if (!mmrInit) {
mmrInit = waveAllocAndPrepareLoopingBuffers(pds); if (!mmrInit) {
hEventTerminate = CreateEvent(NULL, FALSE, FALSE, pds->szEventTerminateWaveThread); if (!hEventTerminate) mmrInit = MMSYSERR_NOMEM; if (!mmrInit) {
// Signal that we're finished with initialization.
// mmrInit should not be modified below this point.
pds->mmrWaveThreadInit = mmrInit; SetEvent(hEventInitDone);
waveThreadLoop(pds, hEventTerminate);
mmr = waveOutReset(pds->hwo); ASSERT(!mmr);
CloseHandle(hEventTerminate); }
waveUnprepareLoopingBuffers(pds); }
CloseHandle(pds->hEventWaveHeaderDone); pds->hEventWaveHeaderDone = NULL; } }
mmr = waveOutClose(pds->hwo); pds->hwo = NULL; ASSERT(!mmr); }
// If init failed, set the result code and signal init done.
if (mmrInit) { pds->mmrWaveThreadInit = mmrInit; SetEvent(hEventInitDone); } CloseHandle(hEventInitDone); return 0; }
MMRESULT FNGLOBAL DSInitializeEmulator ( LPDSOUND pds ) { DWORD dwResult; HANDLE hEventInitDone; MMRESULT mmr;
DPF(3, "DSInitializeEmulator"); ASSERT(pds->pdsbPrimary);
ASSERT(SIZEOF(pds->szEventWaveHeaderDone) >= 7+8+8+1); wsprintf(pds->szEventWaveHeaderDone, "DS-EWHD%08lX%08lX", GetCurrentProcessId(), pds);
ASSERT(SIZEOF(pds->szEventWaveThreadInitDone) >= 8+8+8+1); wsprintf(pds->szEventWaveThreadInitDone, "DS-EWTID%08lX%08lX", GetCurrentProcessId(), pds);
ASSERT(SIZEOF(pds->szEventTerminateWaveThread) >= 7+8+8+1); wsprintf(pds->szEventTerminateWaveThread, "DS-ETWT%08lX%08lX", GetCurrentProcessId(), pds);
hEventInitDone = CreateEvent(NULL, FALSE, FALSE, pds->szEventWaveThreadInitDone); if (!hEventInitDone) return MMSYSERR_NOMEM;
// side effects begin (hEventInitDone created)
// hWaveThread is the thread which recycles wave buffers
pds->hWaveThread = HelperCreateDSMixerThread(waveThread, pds, 0, NULL); mmr = (pds->hWaveThread) ? MMSYSERR_NOERROR : MMSYSERR_NOMEM;
if (!mmr) { DPF(0, "waveThread handle is %08lX", (LONG)pds->hWaveThread);
dwResult = WaitForSingleObjectEx(hEventInitDone, INFINITE, FALSE); ASSERT(WAIT_OBJECT_0 == dwResult); mmr = pds->mmrWaveThreadInit;
if (mmr) { HANDLE hHelper; HANDLE hWaveThreadOurs;
// Something went wrong. Clean up.
// Note that hWaveThread is relative to the helper process.
hHelper = OpenProcess(PROCESS_DUP_HANDLE, FALSE, gpdsinfo->pidHelper); if (hHelper) { if (DuplicateHandle(hHelper, pds->hWaveThread, GetCurrentProcess(), &hWaveThreadOurs, SYNCHRONIZE | THREAD_TERMINATE, FALSE, DUPLICATE_CLOSE_SOURCE)) { dwResult = WaitForSingleObjectEx(hWaveThreadOurs, INFINITE, FALSE); ASSERT(WAIT_OBJECT_0 == dwResult); dwResult = CloseHandle(hWaveThreadOurs); ASSERT(dwResult); } dwResult = CloseHandle(hHelper); ASSERT(dwResult); } pds->hWaveThread = NULL; } }
dwResult = CloseHandle(hEventInitDone); ASSERT(dwResult); return mmr; }
MMRESULT FNGLOBAL DSShutdownEmulator ( LPDSOUND pds ) { HANDLE hEventTerminate; HANDLE hHelper; HANDLE hWaveThreadOurs; DWORD dwResult;
DPF(0, " !!! About to shutdown emulator !!!");
ASSERT(pds->hWaveThread); // Signal wave thread to go away.
hEventTerminate = CreateEvent(NULL, FALSE, FALSE, pds->szEventTerminateWaveThread); if (hEventTerminate) { SetEvent(hEventTerminate); CloseHandle(hEventTerminate); hEventTerminate = NULL; }
DPF(0, "Emulator: Wait for callback thread to die");
hHelper = OpenProcess(PROCESS_DUP_HANDLE, FALSE, gpdsinfo->pidHelper); if (hHelper) { if (DuplicateHandle(hHelper, pds->hWaveThread, GetCurrentProcess(), &hWaveThreadOurs, SYNCHRONIZE | THREAD_TERMINATE, FALSE, DUPLICATE_CLOSE_SOURCE)) { dwResult = WaitForSingleObjectEx(hWaveThreadOurs, INFINITE, FALSE); ASSERT(dwResult == WAIT_OBJECT_0);
dwResult = CloseHandle(hWaveThreadOurs); ASSERT(dwResult); } dwResult = CloseHandle(hHelper); ASSERT(dwResult); } else { DPF(0, "Emulator: couldn't open handle on helper"); }
ASSERT(NULL == pds->hwo); // waveThread should do this if
pds->hwo = NULL; // it terminates normally
pds->hWaveThread = NULL; return MMSYSERR_NOERROR; }
HRESULT CreateNewDirectSoundObject (LPDIRECTSOUND *ppDS, IUnknown *pUnkOuter) { LPDSOUNDEXTERNAL pdse;
DPF(2, "DSound: CreateNewDirectSoundObject: Function Enter");
/* Argument Validation */ *ppDS = NULL; if( pUnkOuter != NULL ) { RPF("DSound: Direct Sound does not support aggregation."); return CLASS_E_NOAGGREGATION; }
/* Object Allocation */ pdse = (LPDSOUNDEXTERNAL)MemAlloc(sizeof(DSOUNDEXTERNAL)); if (NULL == pdse) { RPF("DSound: Direct Sound Object memory allocation failed."); return E_OUTOFMEMORY; }
/* Initialize Data Members */ pdse->lpVtbl = gpdsinfo->lpVtblDS; /* set to NULL to signal no */ pdse->fInitialized = IDSHWINITIALIZEF_UNINITIALIZED;
/* initialization */ pdse->uRefCount = 1;
*ppDS = (LPDIRECTSOUND)pdse;
DPF(2, "DSound: CreateNewDirectSoundObject: Successful Function Exit");
return S_OK; }
|