windows-nt/Source/XPSP1/NT/enduser/netmeeting/ui/conf/dstest.cpp
2020-09-26 16:20:57 +08:00

560 lines
12 KiB
C++

#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 = LoadLibrary(TEXT("DSOUND.DLL"));
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;
}
*/