|
|
#include "precomp.h"
#include <confreg.h>
#include "audiowiz.h"
#include <dsound.h>
#include <mmsystem.h>
#include "wavedev.h"
#include <nmdsprv.h>
#include "dstest.h"
// assume 10 direct sound devices as a max
#define MAX_DS_DEVS 10
// directsound functions
typedef HRESULT (WINAPI *LPFNDSCREATE)(const GUID *, LPDIRECTSOUND *, IUnknown FAR *); typedef HRESULT (WINAPI *LPFNDSENUM)(LPDSENUMCALLBACKA , LPVOID);
// directsound capture functions
typedef HRESULT (WINAPI *DS_CAP_CREATE)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN); typedef HRESULT (WINAPI *DS_CAP_ENUM)(LPDSENUMCALLBACKA, LPVOID);
static HRESULT MapWaveOutIdToGuid(UINT waveOutId, GUID *pGuid, LPFNDSCREATE dsCreate, LPFNDSENUM dsEnum); static HRESULT MapWaveInIdToGuid(UINT waveInId, GUID *pGuid, DS_CAP_CREATE dscCreate, DS_CAP_ENUM dscEnum);
struct GuidDescription { GUID guid; BOOL fAllocated; };
static GuidDescription guidList_DS[MAX_DS_DEVS]; static int nGList_DS = 0;
static GuidDescription guidList_DSC[MAX_DS_DEVS]; static int nGList_DSC = 0;
static BOOL CALLBACK DSEnumCallback(GUID FAR * lpGuid, LPTSTR lpstrDescription, LPTSTR lpstrModule, LPVOID lpContext) { GuidDescription *pList; int *pListSize;
if (lpContext) { pList = guidList_DS; pListSize = &nGList_DS; } else { pList = guidList_DSC; pListSize = &nGList_DSC; }
if (lpGuid) { pList[*pListSize].guid = *lpGuid; } else { pList[*pListSize].guid = GUID_NULL; }
pList->fAllocated = FALSE; // pList->szDescription = new TCHAR[lstrlen(lpstrDescription) + 1];
// if (pList->szDescription)
// {
// lstrcpy(pList->szDescription, lpstrDescription);
// }
*pListSize = *pListSize + 1;
if ((*pListSize) < MAX_DS_DEVS) return TRUE; return FALSE; }
// returns a set of flags (see dstest.h) indicating full duplex
// capabilities
UINT DirectSoundCheck(UINT waveInID, UINT waveOutID, HWND hwnd) { BOOL bRet; HRESULT hr; HINSTANCE hDSI; LPFNDSCREATE dsCreate; LPFNDSENUM dsEnum; GUID dsguid, dscguid; LPDIRECTSOUND pDirectSound = NULL; MMRESULT mmr; int nRetVal = 0; DSBUFFERDESC dsBufDesc; LPDIRECTSOUNDBUFFER pDirectSoundBuffer; WAVEFORMATEX waveFormat = {WAVE_FORMAT_PCM,1,8000,16000,2,16,0}; DS_CAP_CREATE dsCapCreate = NULL; DS_CAP_ENUM dsCapEnum = NULL; DSCBUFFERDESC dscBufDesc; LPDIRECTSOUNDCAPTURE pDirectSoundCapture=NULL; LPDIRECTSOUNDCAPTUREBUFFER pDSCBuffer = NULL;
//
// If changing DirectSound is prevented by policy and the current
// setting is off, skip the test. If the setting is on, we always
// want to perform the test in case the system is no longer DS capable.
//
RegEntry rePol(POLICIES_KEY, HKEY_CURRENT_USER); if (rePol.GetNumber(REGVAL_POL_NOCHANGE_DIRECTSOUND, DEFAULT_POL_NOCHANGE_DIRECTSOUND)) { // changing DS is prevented by policy.
RegEntry re(AUDIO_KEY, HKEY_CURRENT_USER); if (re.GetNumber(REGVAL_DIRECTSOUND, DSOUND_USER_DISABLED) != DSOUND_USER_ENABLED) { return 0; } }
PlaySound(NULL, NULL, NULL); // cancel any running playsound
hDSI = NmLoadLibrary(TEXT("DSOUND.DLL"),TRUE);
if (hDSI == NULL) { return 0; // direct sound is not available!
}
// check for Direct Sound 5 or higher
// Existance of DirectSoundCapture functions implies DSound v.5
dsEnum = (LPFNDSENUM)GetProcAddress(hDSI, "DirectSoundEnumerateA"); dsCreate = (LPFNDSCREATE)GetProcAddress(hDSI, "DirectSoundCreate"); dsCapEnum = (DS_CAP_ENUM)GetProcAddress(hDSI, "DirectSoundCaptureEnumerateA"); dsCapCreate = (DS_CAP_CREATE)GetProcAddress(hDSI, TEXT("DirectSoundCaptureCreate"));
if ((dsCapCreate == NULL) || (dsCreate == NULL) || (dsEnum == NULL) || (dsCapEnum==NULL)) { FreeLibrary(hDSI); return 0; }
hr = MapWaveOutIdToGuid(waveOutID, &dsguid, dsCreate, dsEnum); if (FAILED(hr)) { WARNING_OUT(("Unable to map waveOutID to DirectSound guid!")); FreeLibrary(hDSI); return 0; }
hr = MapWaveInIdToGuid(waveInID, &dscguid, dsCapCreate, dsCapEnum); if (FAILED(hr)) { WARNING_OUT(("Unable to map waveOutID to DirectSound guid!")); FreeLibrary(hDSI); return 0; }
nRetVal = DS_AVAILABLE;
// Open DirectSound First
hr = dsCreate((dsguid==GUID_NULL)?NULL:&dsguid, &pDirectSound, NULL); if (FAILED(hr)) { WARNING_OUT(("Direct Sound failed to open by itself!")); FreeLibrary(hDSI); return 0; }
// set cooperative level
hr = pDirectSound->SetCooperativeLevel(hwnd, DSSCL_PRIORITY); if (hr != DS_OK) { WARNING_OUT(("Direct Sound: failed to set cooperative level")); pDirectSound->Release(); FreeLibrary(hDSI); return 0; }
ZeroMemory(&dsBufDesc,sizeof(dsBufDesc)); dsBufDesc.dwSize = sizeof(dsBufDesc); dsBufDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; hr = pDirectSound->CreateSoundBuffer(&dsBufDesc,&pDirectSoundBuffer,NULL); if (hr == S_OK) { pDirectSoundBuffer->SetFormat(&waveFormat); } else { WARNING_OUT(("Direct Sound: failed to set format")); pDirectSound->Release(); FreeLibrary(hDSI); return 0; }
// now attempt to open DirectSoundCapture
hr = dsCapCreate((dscguid==GUID_NULL)?NULL:&dscguid, &pDirectSoundCapture, NULL); if (SUCCEEDED(hr)) { dscBufDesc.dwSize = sizeof(dscBufDesc); dscBufDesc.dwFlags = 0; dscBufDesc.dwBufferBytes = 1000; dscBufDesc.dwReserved = 0; dscBufDesc.lpwfxFormat = &waveFormat; hr = pDirectSoundCapture->CreateCaptureBuffer(&dscBufDesc, &pDSCBuffer, NULL); if (SUCCEEDED(hr)) { // full duplex is avaiable;
nRetVal |= DS_FULLDUPLEX; } }
if (pDSCBuffer) { pDSCBuffer->Release(); } if (pDirectSoundCapture) { pDirectSoundCapture->Release(); }
pDirectSoundBuffer->Release(); pDirectSound->Release();
FreeLibrary(hDSI);
return nRetVal;
}
HRESULT MapWaveOutIdToGuid(UINT waveOutID, GUID *pGuid, LPFNDSCREATE dsCreate, LPFNDSENUM dsEnum) { waveOutDev waveOut(waveOutID); MMRESULT mmr; HRESULT hr; LPDIRECTSOUND pDS; DSCAPS dscaps; BOOL fEmulFound, bRet; int index; GUID *pIID; WAVEOUTCAPS waveOutCaps;
if (waveOutID == WAVE_MAPPER || waveOutGetNumDevs()==1) { // we want the default or there is only one DS device, take the easy way out
*pGuid = GUID_NULL; return S_OK; }
// The New way. DirectX on Win98/NT 5 gives an IKsProperty interface
// to generate the mapping correctly
ZeroMemory(&waveOutCaps, sizeof(WAVEOUTCAPS)); mmr = waveOutGetDevCaps(waveOutID, &waveOutCaps, sizeof(WAVEOUTCAPS)); if (mmr == MMSYSERR_NOERROR) { hr = DsprvGetWaveDeviceMapping(waveOutCaps.szPname, FALSE, pGuid); if (SUCCEEDED(hr)) { TRACE_OUT(("dstest.cpp: Succeeded in mapping Wave ID to DS guid through IKsProperty interface\r\n")); return hr; } // if we failed to make a mapping, fall through to the old code path
WARNING_OUT(("dstest.cpp: Failed to map Wave ID to DS guid through IKsProperty interface\r\n")); }
// the old way!
// try to figure out which Guid maps to a wave id
// Do this by opening the wave device corresponding to the wave id and then
// all the DS devices in sequence and see which one fails.
// Yes, this is a monstrous hack and clearly unreliable
ZeroMemory(guidList_DS, sizeof(guidList_DS)); nGList_DS = 0;
hr = dsEnum((LPDSENUMCALLBACK)DSEnumCallback, (VOID*)TRUE); if (hr != DS_OK) { WARNING_OUT(("DirectSoundEnumerate failed\n")); return hr; }
mmr = waveOut.Open(8000, 16);
if (mmr != MMSYSERR_NOERROR) { return DSERR_INVALIDPARAM; }
// now open all the DS devices in turn
for (index = 0; index < nGList_DS; index++) { if (guidList_DS[index].guid==GUID_NULL) pIID = NULL; else pIID = &(guidList_DS[index].guid); hr = dsCreate(pIID, &pDS, NULL); if (hr != DS_OK) { guidList_DS[index].fAllocated = TRUE; } else { pDS->Release(); } }
waveOut.Close();
hr = DSERR_ALLOCATED;
dscaps.dwSize = sizeof(dscaps); fEmulFound = FALSE; // try opening the DS devices that failed the first time
for (index = 0; index < nGList_DS; index++) { if (guidList_DS[index].fAllocated == TRUE) { if (guidList_DS[index].guid==GUID_NULL) pIID = NULL; else pIID = &(guidList_DS[index].guid);
hr = dsCreate(pIID, &pDS, NULL); if (hr == DS_OK) { *pGuid = guidList_DS[index].guid; // get dsound capabilities.
pDS->GetCaps(&dscaps); pDS->Release(); if (dscaps.dwFlags & DSCAPS_EMULDRIVER) fEmulFound = TRUE; // keep looking in case there's also a native driver
else break; // native DS driver. Look no further
} } }
if (fEmulFound) hr = DS_OK; if (hr != DS_OK) { WARNING_OUT(("Can't map id %d to DSound guid!\n", waveOutID)); hr = DSERR_ALLOCATED; }
return hr; }
HRESULT MapWaveInIdToGuid(UINT waveInId, GUID *pGuid, DS_CAP_CREATE dscCreate, DS_CAP_ENUM dscEnum) {
HRESULT hr; waveInDev WaveIn(waveInId); WAVEINCAPS waveInCaps; UINT uNumWaveDevs; GUID guid = GUID_NULL; int nIndex; MMRESULT mmr; WAVEFORMATEX waveFormat = {WAVE_FORMAT_PCM, 1, 8000, 16000, 2, 16, 0}; IDirectSoundCapture *pDSC=NULL;
*pGuid = GUID_NULL;
// only one wave device, take the easy way out
uNumWaveDevs = waveInGetNumDevs();
if ((uNumWaveDevs <= 1) || (waveInId == WAVE_MAPPER)) { return S_OK; }
// more than one wavein device
// try to use the IKSProperty interface to map a WaveIN ID to
// DirectSoundCaptureGuid
// Win98 and Windows 2000 only. (Probably will fail on Win95)
mmr = waveInGetDevCaps(waveInId, &waveInCaps, sizeof(WAVEINCAPS)); if (mmr == MMSYSERR_NOERROR) { hr = DsprvGetWaveDeviceMapping(waveInCaps.szPname, TRUE, &guid); if (SUCCEEDED(hr)) { *pGuid = guid; return S_OK; } }
// Use the old way to map devices
ZeroMemory(guidList_DSC, sizeof(guidList_DSC)); nGList_DSC = 0;
hr = dscEnum((LPDSENUMCALLBACK)DSEnumCallback, NULL); if (hr != DS_OK) { WARNING_OUT(("DirectSoundCaptureEnumerate failed\n")); return hr; }
// hack approach to mapping the device to a guid
mmr = WaveIn.Open(waveFormat.nSamplesPerSec, waveFormat.wBitsPerSample); if (mmr != MMSYSERR_NOERROR) { return S_FALSE; }
// find all the DSC devices that fail to open
for (nIndex = 0; nIndex < nGList_DSC; nIndex++) { guidList_DSC[nIndex].fAllocated = FALSE;
if (guidList_DSC[nIndex].guid == GUID_NULL) { hr = dscCreate(NULL, &pDSC, NULL); } else { hr = dscCreate(&(guidList_DSC[nIndex].guid), &pDSC, NULL); }
if (FAILED(hr)) { guidList_DSC[nIndex].fAllocated = TRUE; } else { pDSC->Release(); pDSC=NULL; } }
WaveIn.Close();
// scan through the list of allocated devices and
// see which one opens
for (nIndex = 0; nIndex < nGList_DSC; nIndex++) { if (guidList_DSC[nIndex].fAllocated) { if (guidList_DSC[nIndex].guid == GUID_NULL) { hr = dscCreate(NULL, &pDSC, NULL); } else { hr = dscCreate(&(guidList_DSC[nIndex].guid), &pDSC, NULL); } if (SUCCEEDED(hr)) { // we have a winner
pDSC->Release(); pDSC = NULL; *pGuid = guidList_DSC[nIndex].guid; return S_OK; } } }
// if we got to this point, it means we failed to map a device
// just use GUID_NULL and return an error
return S_FALSE; }
// This function answers the question:
// we have full duplex and DirectSound, but do we really
// trust it to work well in FD-DS Mode ? Returns TRUE if so,
// FALSE otherwise.
/*BOOL IsFDDSRecommended(UINT waveInId, UINT waveOutId)
{ WAVEINCAPS waveInCaps; WAVEOUTCAPS waveOutCaps; MMRESULT mmr; TCHAR szRegKey[30]; RegEntry re(AUDIODEVCAPS_KEY, HKEY_LOCAL_MACHINE, FALSE); LONG lCaps;
mmr = waveInGetDevCaps(waveInId, &waveInCaps, sizeof(waveInCaps)); if (mmr != MMSYSERR_NOERROR) { return FALSE; }
mmr = waveOutGetDevCaps(waveOutId, &waveOutCaps, sizeof(waveOutCaps)); if (mmr != MMSYSERR_NOERROR) { return FALSE; }
// assume that if the two devices are made by different manufacturers
// then DirectSound can always be enabled (because it's two serpate devices)
if (waveInCaps.wMid != waveOutCaps.wMid) { return TRUE; }
// does a key for this specific product exist
wsprintf(szRegKey, "Dev-%d-%d", waveInCaps.wMid, waveInCaps.wPid); lCaps = re.GetNumber(szRegKey, -1);
if (lCaps == -1) { // maybe we have a string for all of the products
// by this manufacturer
wsprintf(szRegKey, "Dev-%d", waveInCaps.wMid); lCaps = re.GetNumber(szRegKey, -1); }
if (lCaps == -1) { // it's an unknown device, we can't trust it to be
// full duplex - direct sound
return FALSE; }
// examine this devices caps
if (lCaps & DEVCAPS_AUDIO_FDDS) { return TRUE; }
return FALSE; }
*/
|