Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

559 lines
12 KiB

  1. #include "precomp.h"
  2. #include <confreg.h>
  3. #include "audiowiz.h"
  4. #include <dsound.h>
  5. #include <mmsystem.h>
  6. #include "wavedev.h"
  7. #include <nmdsprv.h>
  8. #include "dstest.h"
  9. // assume 10 direct sound devices as a max
  10. #define MAX_DS_DEVS 10
  11. // directsound functions
  12. typedef HRESULT (WINAPI *LPFNDSCREATE)(const GUID *, LPDIRECTSOUND *, IUnknown FAR *);
  13. typedef HRESULT (WINAPI *LPFNDSENUM)(LPDSENUMCALLBACKA , LPVOID);
  14. // directsound capture functions
  15. typedef HRESULT (WINAPI *DS_CAP_CREATE)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN);
  16. typedef HRESULT (WINAPI *DS_CAP_ENUM)(LPDSENUMCALLBACKA, LPVOID);
  17. static HRESULT MapWaveOutIdToGuid(UINT waveOutId, GUID *pGuid, LPFNDSCREATE dsCreate, LPFNDSENUM dsEnum);
  18. static HRESULT MapWaveInIdToGuid(UINT waveInId, GUID *pGuid, DS_CAP_CREATE dscCreate, DS_CAP_ENUM dscEnum);
  19. struct GuidDescription
  20. {
  21. GUID guid;
  22. BOOL fAllocated;
  23. };
  24. static GuidDescription guidList_DS[MAX_DS_DEVS];
  25. static int nGList_DS = 0;
  26. static GuidDescription guidList_DSC[MAX_DS_DEVS];
  27. static int nGList_DSC = 0;
  28. static BOOL CALLBACK DSEnumCallback(GUID FAR * lpGuid, LPTSTR lpstrDescription,
  29. LPTSTR lpstrModule, LPVOID lpContext)
  30. {
  31. GuidDescription *pList;
  32. int *pListSize;
  33. if (lpContext)
  34. {
  35. pList = guidList_DS;
  36. pListSize = &nGList_DS;
  37. }
  38. else
  39. {
  40. pList = guidList_DSC;
  41. pListSize = &nGList_DSC;
  42. }
  43. if (lpGuid)
  44. {
  45. pList[*pListSize].guid = *lpGuid;
  46. }
  47. else
  48. {
  49. pList[*pListSize].guid = GUID_NULL;
  50. }
  51. pList->fAllocated = FALSE;
  52. // pList->szDescription = new TCHAR[lstrlen(lpstrDescription) + 1];
  53. // if (pList->szDescription)
  54. // {
  55. // lstrcpy(pList->szDescription, lpstrDescription);
  56. // }
  57. *pListSize = *pListSize + 1;
  58. if ((*pListSize) < MAX_DS_DEVS)
  59. return TRUE;
  60. return FALSE;
  61. }
  62. // returns a set of flags (see dstest.h) indicating full duplex
  63. // capabilities
  64. UINT DirectSoundCheck(UINT waveInID, UINT waveOutID, HWND hwnd)
  65. {
  66. BOOL bRet;
  67. HRESULT hr;
  68. HINSTANCE hDSI;
  69. LPFNDSCREATE dsCreate;
  70. LPFNDSENUM dsEnum;
  71. GUID dsguid, dscguid;
  72. LPDIRECTSOUND pDirectSound = NULL;
  73. MMRESULT mmr;
  74. int nRetVal = 0;
  75. DSBUFFERDESC dsBufDesc;
  76. LPDIRECTSOUNDBUFFER pDirectSoundBuffer;
  77. WAVEFORMATEX waveFormat = {WAVE_FORMAT_PCM,1,8000,16000,2,16,0};
  78. DS_CAP_CREATE dsCapCreate = NULL;
  79. DS_CAP_ENUM dsCapEnum = NULL;
  80. DSCBUFFERDESC dscBufDesc;
  81. LPDIRECTSOUNDCAPTURE pDirectSoundCapture=NULL;
  82. LPDIRECTSOUNDCAPTUREBUFFER pDSCBuffer = NULL;
  83. //
  84. // If changing DirectSound is prevented by policy and the current
  85. // setting is off, skip the test. If the setting is on, we always
  86. // want to perform the test in case the system is no longer DS capable.
  87. //
  88. RegEntry rePol(POLICIES_KEY, HKEY_CURRENT_USER);
  89. if (rePol.GetNumber(REGVAL_POL_NOCHANGE_DIRECTSOUND, DEFAULT_POL_NOCHANGE_DIRECTSOUND))
  90. {
  91. // changing DS is prevented by policy.
  92. RegEntry re(AUDIO_KEY, HKEY_CURRENT_USER);
  93. if (re.GetNumber(REGVAL_DIRECTSOUND, DSOUND_USER_DISABLED) != DSOUND_USER_ENABLED)
  94. {
  95. return 0;
  96. }
  97. }
  98. PlaySound(NULL, NULL, NULL); // cancel any running playsound
  99. hDSI = LoadLibrary(TEXT("DSOUND.DLL"));
  100. if (hDSI == NULL)
  101. {
  102. return 0; // direct sound is not available!
  103. }
  104. // check for Direct Sound 5 or higher
  105. // Existance of DirectSoundCapture functions implies DSound v.5
  106. dsEnum = (LPFNDSENUM)GetProcAddress(hDSI, "DirectSoundEnumerateA");
  107. dsCreate = (LPFNDSCREATE)GetProcAddress(hDSI, "DirectSoundCreate");
  108. dsCapEnum = (DS_CAP_ENUM)GetProcAddress(hDSI, "DirectSoundCaptureEnumerateA");
  109. dsCapCreate = (DS_CAP_CREATE)GetProcAddress(hDSI, TEXT("DirectSoundCaptureCreate"));
  110. if ((dsCapCreate == NULL) || (dsCreate == NULL) || (dsEnum == NULL) || (dsCapEnum==NULL))
  111. {
  112. FreeLibrary(hDSI);
  113. return 0;
  114. }
  115. hr = MapWaveOutIdToGuid(waveOutID, &dsguid, dsCreate, dsEnum);
  116. if (FAILED(hr))
  117. {
  118. WARNING_OUT(("Unable to map waveOutID to DirectSound guid!"));
  119. FreeLibrary(hDSI);
  120. return 0;
  121. }
  122. hr = MapWaveInIdToGuid(waveInID, &dscguid, dsCapCreate, dsCapEnum);
  123. if (FAILED(hr))
  124. {
  125. WARNING_OUT(("Unable to map waveOutID to DirectSound guid!"));
  126. FreeLibrary(hDSI);
  127. return 0;
  128. }
  129. nRetVal = DS_AVAILABLE;
  130. // Open DirectSound First
  131. hr = dsCreate((dsguid==GUID_NULL)?NULL:&dsguid, &pDirectSound, NULL);
  132. if (FAILED(hr))
  133. {
  134. WARNING_OUT(("Direct Sound failed to open by itself!"));
  135. FreeLibrary(hDSI);
  136. return 0;
  137. }
  138. // set cooperative level
  139. hr = pDirectSound->SetCooperativeLevel(hwnd, DSSCL_PRIORITY);
  140. if (hr != DS_OK)
  141. {
  142. WARNING_OUT(("Direct Sound: failed to set cooperative level"));
  143. pDirectSound->Release();
  144. FreeLibrary(hDSI);
  145. return 0;
  146. }
  147. ZeroMemory(&dsBufDesc,sizeof(dsBufDesc));
  148. dsBufDesc.dwSize = sizeof(dsBufDesc);
  149. dsBufDesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
  150. hr = pDirectSound->CreateSoundBuffer(&dsBufDesc,&pDirectSoundBuffer,NULL);
  151. if (hr == S_OK)
  152. {
  153. pDirectSoundBuffer->SetFormat(&waveFormat);
  154. }
  155. else
  156. {
  157. WARNING_OUT(("Direct Sound: failed to set format"));
  158. pDirectSound->Release();
  159. FreeLibrary(hDSI);
  160. return 0;
  161. }
  162. // now attempt to open DirectSoundCapture
  163. hr = dsCapCreate((dscguid==GUID_NULL)?NULL:&dscguid, &pDirectSoundCapture, NULL);
  164. if (SUCCEEDED(hr))
  165. {
  166. dscBufDesc.dwSize = sizeof(dscBufDesc);
  167. dscBufDesc.dwFlags = 0;
  168. dscBufDesc.dwBufferBytes = 1000;
  169. dscBufDesc.dwReserved = 0;
  170. dscBufDesc.lpwfxFormat = &waveFormat;
  171. hr = pDirectSoundCapture->CreateCaptureBuffer(&dscBufDesc, &pDSCBuffer, NULL);
  172. if (SUCCEEDED(hr))
  173. {
  174. // full duplex is avaiable;
  175. nRetVal |= DS_FULLDUPLEX;
  176. }
  177. }
  178. if (pDSCBuffer)
  179. {
  180. pDSCBuffer->Release();
  181. }
  182. if (pDirectSoundCapture)
  183. {
  184. pDirectSoundCapture->Release();
  185. }
  186. pDirectSoundBuffer->Release();
  187. pDirectSound->Release();
  188. FreeLibrary(hDSI);
  189. return nRetVal;
  190. }
  191. HRESULT MapWaveOutIdToGuid(UINT waveOutID, GUID *pGuid, LPFNDSCREATE dsCreate, LPFNDSENUM dsEnum)
  192. {
  193. waveOutDev waveOut(waveOutID);
  194. MMRESULT mmr;
  195. HRESULT hr;
  196. LPDIRECTSOUND pDS;
  197. DSCAPS dscaps;
  198. BOOL fEmulFound, bRet;
  199. int index;
  200. GUID *pIID;
  201. WAVEOUTCAPS waveOutCaps;
  202. if (waveOutID == WAVE_MAPPER || waveOutGetNumDevs()==1)
  203. {
  204. // we want the default or there is only one DS device, take the easy way out
  205. *pGuid = GUID_NULL;
  206. return S_OK;
  207. }
  208. // The New way. DirectX on Win98/NT 5 gives an IKsProperty interface
  209. // to generate the mapping correctly
  210. ZeroMemory(&waveOutCaps, sizeof(WAVEOUTCAPS));
  211. mmr = waveOutGetDevCaps(waveOutID, &waveOutCaps, sizeof(WAVEOUTCAPS));
  212. if (mmr == MMSYSERR_NOERROR)
  213. {
  214. hr = DsprvGetWaveDeviceMapping(waveOutCaps.szPname, FALSE, pGuid);
  215. if (SUCCEEDED(hr))
  216. {
  217. TRACE_OUT(("dstest.cpp: Succeeded in mapping Wave ID to DS guid through IKsProperty interface\r\n"));
  218. return hr;
  219. }
  220. // if we failed to make a mapping, fall through to the old code path
  221. WARNING_OUT(("dstest.cpp: Failed to map Wave ID to DS guid through IKsProperty interface\r\n"));
  222. }
  223. // the old way!
  224. // try to figure out which Guid maps to a wave id
  225. // Do this by opening the wave device corresponding to the wave id and then
  226. // all the DS devices in sequence and see which one fails.
  227. // Yes, this is a monstrous hack and clearly unreliable
  228. ZeroMemory(guidList_DS, sizeof(guidList_DS));
  229. nGList_DS = 0;
  230. hr = dsEnum((LPDSENUMCALLBACK)DSEnumCallback, (VOID*)TRUE);
  231. if (hr != DS_OK)
  232. {
  233. WARNING_OUT(("DirectSoundEnumerate failed\n"));
  234. return hr;
  235. }
  236. mmr = waveOut.Open(8000, 16);
  237. if (mmr != MMSYSERR_NOERROR)
  238. {
  239. return DSERR_INVALIDPARAM;
  240. }
  241. // now open all the DS devices in turn
  242. for (index = 0; index < nGList_DS; index++)
  243. {
  244. if (guidList_DS[index].guid==GUID_NULL)
  245. pIID = NULL;
  246. else
  247. pIID = &(guidList_DS[index].guid);
  248. hr = dsCreate(pIID, &pDS, NULL);
  249. if (hr != DS_OK)
  250. {
  251. guidList_DS[index].fAllocated = TRUE;
  252. }
  253. else
  254. {
  255. pDS->Release();
  256. }
  257. }
  258. waveOut.Close();
  259. hr = DSERR_ALLOCATED;
  260. dscaps.dwSize = sizeof(dscaps);
  261. fEmulFound = FALSE;
  262. // try opening the DS devices that failed the first time
  263. for (index = 0; index < nGList_DS; index++)
  264. {
  265. if (guidList_DS[index].fAllocated == TRUE)
  266. {
  267. if (guidList_DS[index].guid==GUID_NULL)
  268. pIID = NULL;
  269. else
  270. pIID = &(guidList_DS[index].guid);
  271. hr = dsCreate(pIID, &pDS, NULL);
  272. if (hr == DS_OK)
  273. {
  274. *pGuid = guidList_DS[index].guid;
  275. // get dsound capabilities.
  276. pDS->GetCaps(&dscaps);
  277. pDS->Release();
  278. if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
  279. fEmulFound = TRUE; // keep looking in case there's also a native driver
  280. else
  281. break; // native DS driver. Look no further
  282. }
  283. }
  284. }
  285. if (fEmulFound)
  286. hr = DS_OK;
  287. if (hr != DS_OK)
  288. {
  289. WARNING_OUT(("Can't map id %d to DSound guid!\n", waveOutID));
  290. hr = DSERR_ALLOCATED;
  291. }
  292. return hr;
  293. }
  294. HRESULT MapWaveInIdToGuid(UINT waveInId, GUID *pGuid, DS_CAP_CREATE dscCreate, DS_CAP_ENUM dscEnum)
  295. {
  296. HRESULT hr;
  297. waveInDev WaveIn(waveInId);
  298. WAVEINCAPS waveInCaps;
  299. UINT uNumWaveDevs;
  300. GUID guid = GUID_NULL;
  301. int nIndex;
  302. MMRESULT mmr;
  303. WAVEFORMATEX waveFormat = {WAVE_FORMAT_PCM, 1, 8000, 16000, 2, 16, 0};
  304. IDirectSoundCapture *pDSC=NULL;
  305. *pGuid = GUID_NULL;
  306. // only one wave device, take the easy way out
  307. uNumWaveDevs = waveInGetNumDevs();
  308. if ((uNumWaveDevs <= 1) || (waveInId == WAVE_MAPPER))
  309. {
  310. return S_OK;
  311. }
  312. // more than one wavein device
  313. // try to use the IKSProperty interface to map a WaveIN ID to
  314. // DirectSoundCaptureGuid
  315. // Win98 and Windows 2000 only. (Probably will fail on Win95)
  316. mmr = waveInGetDevCaps(waveInId, &waveInCaps, sizeof(WAVEINCAPS));
  317. if (mmr == MMSYSERR_NOERROR)
  318. {
  319. hr = DsprvGetWaveDeviceMapping(waveInCaps.szPname, TRUE, &guid);
  320. if (SUCCEEDED(hr))
  321. {
  322. *pGuid = guid;
  323. return S_OK;
  324. }
  325. }
  326. // Use the old way to map devices
  327. ZeroMemory(guidList_DSC, sizeof(guidList_DSC));
  328. nGList_DSC = 0;
  329. hr = dscEnum((LPDSENUMCALLBACK)DSEnumCallback, NULL);
  330. if (hr != DS_OK)
  331. {
  332. WARNING_OUT(("DirectSoundCaptureEnumerate failed\n"));
  333. return hr;
  334. }
  335. // hack approach to mapping the device to a guid
  336. mmr = WaveIn.Open(waveFormat.nSamplesPerSec, waveFormat.wBitsPerSample);
  337. if (mmr != MMSYSERR_NOERROR)
  338. {
  339. return S_FALSE;
  340. }
  341. // find all the DSC devices that fail to open
  342. for (nIndex = 0; nIndex < nGList_DSC; nIndex++)
  343. {
  344. guidList_DSC[nIndex].fAllocated = FALSE;
  345. if (guidList_DSC[nIndex].guid == GUID_NULL)
  346. {
  347. hr = dscCreate(NULL, &pDSC, NULL);
  348. }
  349. else
  350. {
  351. hr = dscCreate(&(guidList_DSC[nIndex].guid), &pDSC, NULL);
  352. }
  353. if (FAILED(hr))
  354. {
  355. guidList_DSC[nIndex].fAllocated = TRUE;
  356. }
  357. else
  358. {
  359. pDSC->Release();
  360. pDSC=NULL;
  361. }
  362. }
  363. WaveIn.Close();
  364. // scan through the list of allocated devices and
  365. // see which one opens
  366. for (nIndex = 0; nIndex < nGList_DSC; nIndex++)
  367. {
  368. if (guidList_DSC[nIndex].fAllocated)
  369. {
  370. if (guidList_DSC[nIndex].guid == GUID_NULL)
  371. {
  372. hr = dscCreate(NULL, &pDSC, NULL);
  373. }
  374. else
  375. {
  376. hr = dscCreate(&(guidList_DSC[nIndex].guid), &pDSC, NULL);
  377. }
  378. if (SUCCEEDED(hr))
  379. {
  380. // we have a winner
  381. pDSC->Release();
  382. pDSC = NULL;
  383. *pGuid = guidList_DSC[nIndex].guid;
  384. return S_OK;
  385. }
  386. }
  387. }
  388. // if we got to this point, it means we failed to map a device
  389. // just use GUID_NULL and return an error
  390. return S_FALSE;
  391. }
  392. // This function answers the question:
  393. // we have full duplex and DirectSound, but do we really
  394. // trust it to work well in FD-DS Mode ? Returns TRUE if so,
  395. // FALSE otherwise.
  396. /*BOOL IsFDDSRecommended(UINT waveInId, UINT waveOutId)
  397. {
  398. WAVEINCAPS waveInCaps;
  399. WAVEOUTCAPS waveOutCaps;
  400. MMRESULT mmr;
  401. TCHAR szRegKey[30];
  402. RegEntry re(AUDIODEVCAPS_KEY, HKEY_LOCAL_MACHINE, FALSE);
  403. LONG lCaps;
  404. mmr = waveInGetDevCaps(waveInId, &waveInCaps, sizeof(waveInCaps));
  405. if (mmr != MMSYSERR_NOERROR)
  406. {
  407. return FALSE;
  408. }
  409. mmr = waveOutGetDevCaps(waveOutId, &waveOutCaps, sizeof(waveOutCaps));
  410. if (mmr != MMSYSERR_NOERROR)
  411. {
  412. return FALSE;
  413. }
  414. // assume that if the two devices are made by different manufacturers
  415. // then DirectSound can always be enabled (because it's two serpate devices)
  416. if (waveInCaps.wMid != waveOutCaps.wMid)
  417. {
  418. return TRUE;
  419. }
  420. // does a key for this specific product exist
  421. wsprintf(szRegKey, "Dev-%d-%d", waveInCaps.wMid, waveInCaps.wPid);
  422. lCaps = re.GetNumber(szRegKey, -1);
  423. if (lCaps == -1)
  424. {
  425. // maybe we have a string for all of the products
  426. // by this manufacturer
  427. wsprintf(szRegKey, "Dev-%d", waveInCaps.wMid);
  428. lCaps = re.GetNumber(szRegKey, -1);
  429. }
  430. if (lCaps == -1)
  431. {
  432. // it's an unknown device, we can't trust it to be
  433. // full duplex - direct sound
  434. return FALSE;
  435. }
  436. // examine this devices caps
  437. if (lCaps & DEVCAPS_AUDIO_FDDS)
  438. {
  439. return TRUE;
  440. }
  441. return FALSE;
  442. }
  443. */