Windows NT 4.0 source code leak
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.

2072 lines
56 KiB

4 years ago
  1. //--------------------------------------------------------------------------;
  2. //
  3. // File: DSound.c
  4. //
  5. // Copyright (c) 1995 Microsoft Corporation. All Rights Reserved.
  6. //
  7. // Abstract:
  8. //
  9. //
  10. // Contents:
  11. // IDSWECreateSoundBuffer()
  12. // DirectSoundCreate()
  13. // DirectSoundEnumerateA()
  14. // CreateNewDirectSoundObject()
  15. //
  16. // History:
  17. // Date By Reason
  18. // ==== == ======
  19. // 3/5/96 angusm Added use of fInitialized
  20. //
  21. //--------------------------------------------------------------------------;
  22. #define INITGUID
  23. #include "dsoundpr.h"
  24. #include <mmddk.h>
  25. #include <initguid.h>
  26. #include "dsdriver.h"
  27. #include "grace.h"
  28. #include "resource.h"
  29. #include "flocks.h"
  30. #define TID_INIT_VALUE 0xffffffff // This is a dummy value that can not be a valid TID
  31. #if !defined NUMELMS
  32. #define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0]))
  33. #endif
  34. // internal Kernel32 API
  35. extern DWORD WINAPI OpenVxDHandle(HANDLE hSource);
  36. LPDSOUNDINFO gpdsinfo = NULL;
  37. //--------------------------------------------------------------------------;
  38. //
  39. // BOOL wavIsMappable
  40. //
  41. // Description:
  42. // This function determines whether a specified wave device is
  43. // a mappable device
  44. //
  45. // Arguments:
  46. // UINT uWaveId :
  47. //
  48. // Return (BOOL): TRUE if and only if it the wave device is a mappable device
  49. //
  50. // History:
  51. // 09/09/95 FrankYe Created
  52. //
  53. //--------------------------------------------------------------------------;
  54. BOOL wavIsMappable(UINT uWaveId)
  55. {
  56. MMRESULT mmr;
  57. mmr = waveOutMessage((HWAVEOUT)uWaveId, DRV_QUERYMAPPABLE, 0, 0);
  58. return (MMSYSERR_NOERROR == mmr);
  59. }
  60. //--------------------------------------------------------------------------;
  61. //
  62. // void wavGetPreferredId
  63. //
  64. // Description:
  65. // This function accesses the registry settings maintained by the
  66. // wave mapper and multimedia control panel to determine the wave id
  67. // of the preferred sound device.
  68. //
  69. // Arguments:
  70. // LPUINT puWaveId :
  71. //
  72. // LPBOOL pfPreferredOnly :
  73. //
  74. // Return (void):
  75. //
  76. // History:
  77. // 09/09/95 FrankYe Created
  78. //
  79. //--------------------------------------------------------------------------;
  80. const TCHAR gszRegKeynameWaveMapper[] =
  81. TEXT("Software\\Microsoft\\Multimedia\\Sound Mapper");
  82. const TCHAR gszRegValuenamePreferredPlayback[] =
  83. TEXT("Playback");
  84. const TCHAR gszRegValuenamePreferredOnly[] =
  85. TEXT("PreferredOnly");
  86. BOOL
  87. wavGetPreferredId(LPUINT puWaveId, LPBOOL pfPreferredOnly)
  88. {
  89. UINT u;
  90. WAVEOUTCAPS woc;
  91. HKEY hkeyWaveMapper;
  92. TCHAR szPreferred[MAXPNAMELEN];
  93. DWORD dwPreferredOnly;
  94. DWORD dwType;
  95. DWORD cbData;
  96. LONG lr;
  97. *puWaveId = 0;
  98. *pfPreferredOnly = 0;
  99. lr = RegOpenKeyEx(HKEY_CURRENT_USER, gszRegKeynameWaveMapper, 0,
  100. KEY_QUERY_VALUE, &hkeyWaveMapper);
  101. if (ERROR_SUCCESS == lr) {
  102. szPreferred[0] = '\0';
  103. dwType = REG_SZ;
  104. cbData = sizeof(szPreferred);
  105. lr = RegQueryValueEx(hkeyWaveMapper, gszRegValuenamePreferredPlayback,
  106. NULL, &dwType, szPreferred, &cbData);
  107. if (ERROR_SUCCESS == lr) {
  108. dwType = REG_DWORD;
  109. cbData = sizeof(dwPreferredOnly);
  110. lr = RegQueryValueEx(hkeyWaveMapper, gszRegValuenamePreferredOnly,
  111. NULL, &dwType, (LPBYTE)&dwPreferredOnly, &cbData);
  112. if (ERROR_SUCCESS != lr) {
  113. dwPreferredOnly = 0;
  114. }
  115. for (u = 0; u < waveOutGetNumDevs(); u++) {
  116. if (!waveOutGetDevCaps(u, &woc, sizeof(woc))) {
  117. woc.szPname[SIZEOF(woc.szPname)-1] = '\0';
  118. if (!lstrcmp(woc.szPname, szPreferred)) {
  119. *puWaveId = u;
  120. *pfPreferredOnly = (0 != dwPreferredOnly);
  121. break;
  122. }
  123. }
  124. }
  125. }
  126. RegCloseKey(hkeyWaveMapper);
  127. }
  128. return TRUE;
  129. }
  130. //--------------------------------------------------------------------------;
  131. //
  132. // HRESULT wavGetIdFromDrvGuid
  133. //
  134. // Description:
  135. // This function finds the mmsystem wave ID of the device that
  136. // corresponds the the specified ds driver
  137. //
  138. // Arguments:
  139. // LPGUID refGuid : guid of a ds driver
  140. //
  141. // LPUINT puWaveId : pointer to UINT to receive the mmsystem wave id
  142. //
  143. // Return (HRESULT):
  144. //
  145. // History:
  146. //
  147. //--------------------------------------------------------------------------;
  148. HRESULT wavGetIdFromDrvGuid(REFGUID refGuid, LPUINT puWaveId)
  149. {
  150. DSDRIVERDESC dsDrvDesc;
  151. DWORD dnDevNode;
  152. UINT uDeviceNum;
  153. UINT uWaveId;
  154. UINT cWaveDevices;
  155. BOOL fFound;
  156. UINT i;
  157. MMRESULT mmr;
  158. DSVAL dsv;
  159. FillMemory(&dsDrvDesc, sizeof(dsDrvDesc), 0);
  160. dsv = vxdDrvGetDesc(refGuid, &dsDrvDesc);
  161. if (DS_OK != dsv) return dsv;
  162. uDeviceNum = dsDrvDesc.ulDeviceNum+1;
  163. cWaveDevices = waveOutGetNumDevs();
  164. DPF(3, "Looking for corresponding mmsystem driver...");
  165. for (uWaveId = 0; uWaveId < cWaveDevices; uWaveId++) {
  166. WORD awVxdIds[50]; // we won't handle more than 50 vxds on the devnode
  167. DWORD cReturnedIds;
  168. mmr = waveOutMessage((HWAVEOUT)uWaveId,
  169. DRV_QUERYDEVNODE,
  170. (DWORD)&dnDevNode, 0);
  171. if (MMSYSERR_NOERROR != mmr) break;
  172. if (dnDevNode != dsDrvDesc.dnDevNode) continue;
  173. cReturnedIds = sizeof(awVxdIds) / sizeof(awVxdIds[0]);
  174. mmr = waveOutMessage((HWAVEOUT)uWaveId,
  175. DRV_QUERYDRIVERIDS,
  176. (DWORD)awVxdIds, (DWORD)&cReturnedIds);
  177. if (MMSYSERR_INVALPARAM == mmr) continue;
  178. if (MMSYSERR_NOERROR != mmr) break;
  179. fFound = FALSE;
  180. for (i=0; i < cReturnedIds; i++) {
  181. if (awVxdIds[i] == dsDrvDesc.wVxdId) {
  182. fFound = TRUE;
  183. break;
  184. }
  185. }
  186. if ( (fFound) && (0 == --uDeviceNum) ) break;
  187. }
  188. if (0 == uDeviceNum) {
  189. *puWaveId = uWaveId;
  190. dsv = DS_OK;
  191. } else {
  192. dsv = DSERR_NODRIVER;
  193. }
  194. return dsv;
  195. }
  196. //--------------------------------------------------------------------------;
  197. //
  198. // HRESULT wavGetDrvGuidFromId
  199. //
  200. // Description:
  201. //
  202. // Arguments:
  203. // UINT uWaveId : wave id
  204. //
  205. // LPGUID pGuid : ptr to guid to receive ds driver guid
  206. //
  207. // Return (HRESULT):
  208. //
  209. // History:
  210. //
  211. //--------------------------------------------------------------------------;
  212. HRESULT wavGetDrvGuidFromId(UINT uWaveIdCaller, LPGUID pGuidCaller)
  213. {
  214. DSDRIVERDESC dsDrvDesc;
  215. GUID guid, guidLast;
  216. UINT uWaveId;
  217. HRESULT hr;
  218. FillMemory(&dsDrvDesc, sizeof(dsDrvDesc), 0);
  219. hr = vxdDrvGetNextDriverDesc(NULL, &guid, &dsDrvDesc);
  220. while (DS_OK == hr) {
  221. hr = wavGetIdFromDrvGuid(&guid, &uWaveId);
  222. if ((DS_OK == hr) && (uWaveId == uWaveIdCaller)) {
  223. *pGuidCaller = guid;
  224. return DS_OK;
  225. }
  226. guidLast = guid;
  227. FillMemory(&dsDrvDesc, sizeof(dsDrvDesc), 0);
  228. hr = vxdDrvGetNextDriverDesc(&guidLast, &guid, &dsDrvDesc);
  229. }
  230. return hr;
  231. }
  232. //--------------------------------------------------------------------------;
  233. //
  234. // LPDSBUFFER ICreateSoundBuffer
  235. //
  236. // Description:
  237. // This function is the member function for CreateSoundBuffer.
  238. //
  239. // Arguments:
  240. // LPDIRECTSOUND pids: Pointer to Direct Sound Object.
  241. //
  242. // LPDSBUFFERCREATE pdsbc: Pointer to a DSBufferCreate structure.
  243. //
  244. // Return (LPDSBUFFER):
  245. // Pointer to a DSBUFFER structure.
  246. //
  247. // History:
  248. //
  249. //--------------------------------------------------------------------------;
  250. HRESULT FAR PASCAL WaveEmulateCreateSoundBuffer
  251. (
  252. LPDSOUND pds,
  253. LPDSBUFFER pdsb,
  254. LPDSBUFFERDESC pdsbd
  255. )
  256. {
  257. MMRESULT mmr;
  258. DPF(3,"Wave Emulate CreateSoundBuffer");
  259. // All validation done in other case
  260. //If not primary, allocate the actual buffer
  261. if (!((pdsbd->dwFlags & DSBCAPS_PRIMARYBUFFER))) {
  262. pdsb->pDSBuffer = MemAlloc(pdsbd->dwBufferBytes);
  263. if (!pdsb->pDSBuffer) {
  264. DPF(1,"WE CreateSoundBuffer cannot allocate buffer");
  265. return DSERR_OUTOFMEMORY;
  266. }
  267. } else {
  268. pdsb->pDSBuffer = NULL;
  269. }
  270. pdsb->cbBufferSize = pdsbd->dwBufferBytes;
  271. pdsb->fdwDsbI |= DSB_INTERNALF_WAVEEMULATED;
  272. pdsb->fdwDsbI |= DSB_INTERNALF_EMULATED;
  273. DPF(3,"--------Alloc data for WAVE Emulated obj %X buff %X len %X",
  274. pdsb, pdsb->pDSBuffer, pdsb->cbBufferSize );
  275. if (pdsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
  276. mmr = DSInitializeEmulator(pds);
  277. if (mmr) {
  278. DPF(0,"DSInitEmu returns %u", (UINT)mmr);
  279. if (MMSYSERR_ALLOCATED == mmr) return DSERR_ALLOCATED;
  280. if (MMSYSERR_NOMEM == mmr) return DSERR_OUTOFMEMORY;
  281. return DSERR_GENERIC;
  282. }
  283. }
  284. DPF(3,"Wave Emulate CreateSoundBuffer Exit");
  285. return DS_OK;
  286. } // IDSCreateSoundBuffer()
  287. //--------------------------------------------------------------------------;
  288. //
  289. // DsGetDriverVersion
  290. //
  291. // Description: Figure out driver version, and sets the pds version fields.
  292. //
  293. //--------------------------------------------------------------------------;
  294. BOOL DsGetDriverVersion
  295. (
  296. LPDSOUND pds,
  297. LPTSTR szDrvname
  298. )
  299. {
  300. TCHAR szDriverPath[MAX_PATH];
  301. int iStrLen;
  302. DWORD dwHandle;
  303. DWORD dwVerLen;
  304. LPVOID pvVerInfo;
  305. DWORD dwVsffiLen;
  306. VS_FIXEDFILEINFO* pvsffi;
  307. BOOL fSucceeded;
  308. if( !GetSystemDirectory( szDriverPath, MAX_PATH ) ) {
  309. DPF(0,"DsGetDriverVersion - GetSystemDirectory failed!");
  310. return FALSE;
  311. }
  312. iStrLen = lstrlen( szDriverPath );
  313. if( iStrLen + 1 + lstrlen(szDrvname) >= MAX_PATH ) {
  314. DPF(0,"DsGetDriverVersion - Ran out of path room!");
  315. return FALSE;
  316. }
  317. // We have enough room.
  318. if( szDriverPath[iStrLen-1] != TEXT('\\') )
  319. {
  320. szDriverPath[iStrLen++] = TEXT('\\');
  321. szDriverPath[iStrLen] = TEXT('\0');
  322. }
  323. lstrcat( szDriverPath, szDrvname );
  324. dwVerLen = GetFileVersionInfoSize( szDriverPath, &dwHandle );
  325. if( 0 == dwVerLen ) {
  326. DPF(0,"DsGetDriverVersion - GetFileVersionInfoSize failed!");
  327. return FALSE;
  328. }
  329. pvVerInfo = LocalAlloc ( LPTR, dwVerLen );
  330. if( NULL == pvVerInfo ) {
  331. DPF(0,"DsGetDriverVersion - Couldn't allocate memory for pvVerInfo!");
  332. return FALSE;
  333. }
  334. // Side effects begin... no more returns.
  335. fSucceeded = FALSE;
  336. if( !GetFileVersionInfo( szDriverPath, dwHandle, dwVerLen, pvVerInfo ) ) {
  337. DPF(0,"DsGetDriverVersion - GetFileVersionInfo failed!");
  338. }
  339. else
  340. {
  341. dwVsffiLen = sizeof( VS_FIXEDFILEINFO );
  342. if( !VerQueryValue( pvVerInfo, TEXT("\\"), (LPVOID *)&pvsffi, &dwVsffiLen ) ) {
  343. DPF(0,"DsGetDriverVersion - VerQueryValue failed!");
  344. } else {
  345. pds->dwDriverVersionMajor = pvsffi->dwFileVersionMS;
  346. pds->dwDriverVersionMinor = pvsffi->dwFileVersionLS;
  347. DPF(3,"DsGetDriverVersion - succeeded, major=0x%08x, minor=0x%08x",pds->dwDriverVersionMajor,pds->dwDriverVersionMinor);
  348. fSucceeded = TRUE;
  349. }
  350. }
  351. LocalFree( pvVerInfo );
  352. return fSucceeded;
  353. }
  354. //--------------------------------------------------------------------------;
  355. //
  356. // CreateDsNative
  357. //
  358. // This function creates a DSOUND object (a pds) using a native mode driver
  359. // identified by a guid.
  360. //
  361. // Parameters:
  362. // REFGUID rguid : identifies the guid of the requested driver
  363. //
  364. // LPDSOUND *ppds : receives a pointer to the new DSOUND object
  365. //
  366. // Return value (HRESULT):
  367. // DS_OK : if and only if a DSOUND object has been created successfully
  368. //
  369. // DSERR_NODRIVER : if there no native driver having the specified guid
  370. //
  371. // other : some other failure
  372. //
  373. // Notes:
  374. // For return values other than DS_OK, there should be no side effects
  375. //
  376. //--------------------------------------------------------------------------;
  377. HRESULT WINAPI CreateDsNative(REFGUID rguid, LPDSOUND *ppds)
  378. {
  379. LPDSOUND pds;
  380. DSDRIVERDESC dsDrvDesc;
  381. UINT uWaveDeviceID;
  382. BOOL fHaveWaveId;
  383. int cb;
  384. HRESULT hr;
  385. MMRESULT mmr;
  386. *ppds = NULL;
  387. pds = (LPDSOUND)MemAlloc(sizeof(*pds));
  388. if (NULL == pds) return DSERR_OUTOFMEMORY;
  389. // We have a device that is for a Direct Sound MINIDRIVER
  390. DPF(3,"Create DS pds = %X", pds );
  391. ZeroMemory(&dsDrvDesc, sizeof(dsDrvDesc));
  392. hr = vxdDrvGetDesc(rguid, &dsDrvDesc);
  393. if (DS_OK == hr) {
  394. pds->fdwDriverDesc = dsDrvDesc.dwFlags;
  395. pds->dwHeapType = dsDrvDesc.dwHeapType;
  396. pds->dwMemAllocExtra= dsDrvDesc.dwMemAllocExtra;
  397. // Figure out if we're running on a certified driver.
  398. // Sum of driver filename chars + DSCAPS_FILENAMECOOKIE
  399. // mod DSCAPS_FILENAMEMODVALUE must equal dsDrvDesc.wReserved.
  400. {
  401. DWORD dwCertifiedCookie = (DWORD)dsDrvDesc.wReserved;
  402. DWORD dwSum = DSCAPS_FILENAMECOOKIE;
  403. PTCHAR p = dsDrvDesc.szDrvname;
  404. for ( ; *p != TEXT('\0'); dwSum += (DWORD)(*p++));
  405. if( dwSum%DSCAPS_FILENAMEMODVALUE == dwCertifiedCookie ) {
  406. DPF(1,"Running on certified driver.");
  407. pds->fdwInternal |= DS_INTERNALF_CERTIFIED;
  408. }
  409. }
  410. // Set driver version numbers.
  411. if( !DsGetDriverVersion( pds, dsDrvDesc.szDrvname ) ) {
  412. DPF(0,"DirectSoundCreateFromGuid - DsGetDriverVersion failed!");
  413. ASSERT( pds->dwDriverVersionMajor == 0 );
  414. ASSERT( pds->dwDriverVersionMinor == 0 );
  415. }
  416. hr = wavGetIdFromDrvGuid(rguid, &uWaveDeviceID);
  417. fHaveWaveId = (DS_OK == hr);
  418. if (!(DSDDESC_DOMMSYSTEMOPEN & pds->fdwDriverDesc)) hr = DS_OK;
  419. if ((DS_OK == hr) && fHaveWaveId) {
  420. WAVEFORMATEX wfxJustToOpen = {
  421. WAVE_FORMAT_PCM, // wFormatTag
  422. 1, // nChannels
  423. 11025, // nSamplesPerSec
  424. 11025, // nAvgBytesPerSec
  425. 1, // nBlockAlign
  426. 8, // wBitsPerSample
  427. 0 // cbSize
  428. };
  429. // The wfx is used only to open the mmsystem wave device, it
  430. // has nothing to do with the format of any DirectSound
  431. // buffers.
  432. // We have to copy the wfx to shared memory since the ddhelp
  433. // process will do the actuall open on the wave driver. The
  434. // wfxDefault member of the ds object is a convenient place
  435. // to put it since it has not yet been initialized with the
  436. // default format for this ds object.
  437. ASSERT(0 == pds->wfxDefault.wFormatTag);
  438. CopyMemory(&pds->wfxDefault, &wfxJustToOpen, sizeof(wfxJustToOpen));
  439. mmr = (DWORD)HelperWaveOpen(&pds->hwo,
  440. uWaveDeviceID,
  441. &pds->wfxDefault);
  442. pds->wfxDefault.wFormatTag = 0;
  443. if (MMSYSERR_NOERROR != mmr) {
  444. DPF(0," Wave Open Failed ");
  445. pds->hwo = NULL;
  446. // We'll consider this an error only if the driver
  447. // specified that we must do mmsystem open
  448. if (DSDDESC_DOMMSYSTEMOPEN & pds->fdwDriverDesc) {
  449. if (MMSYSERR_ALLOCATED == mmr) {
  450. hr = DSERR_ALLOCATED;
  451. } else {
  452. hr = DSERR_NODRIVER;
  453. }
  454. }
  455. }
  456. // If driver did not specify that we must do mmsystem open,
  457. // then immediately close the device after opening it.
  458. if ( !(DSDDESC_DOMMSYSTEMOPEN & pds->fdwDriverDesc) && (NULL != pds->hwo) ) {
  459. HelperWaveClose((DWORD)pds->hwo);
  460. pds->hwo = NULL;
  461. }
  462. }
  463. //
  464. // Try to open driver
  465. //
  466. if (DS_OK == hr) {
  467. hr = vxdDrvOpen(rguid, &(pds->hHal));
  468. if (DS_OK == hr) {
  469. DPF(3,"Finished Open HAL layer %X", pds->hHal );
  470. // Handle on-card memory management if necessary
  471. //
  472. pds->pDriverHeap = NULL;
  473. if (DSDHEAP_USEDIRECTDRAWHEAP == pds->dwHeapType) {
  474. pds->pDriverHeap = dsDrvDesc.pvDirectDrawHeap;
  475. } else if (DSDHEAP_CREATEHEAP == pds->dwHeapType) {
  476. pds->pDriverHeap = VidMemInit( VMEMHEAP_LINEAR,
  477. dsDrvDesc.dwMemStartAddress,
  478. dsDrvDesc.dwMemEndAddress,
  479. 0, 0 );
  480. if (NULL == pds->pDriverHeap) {
  481. RPF("DirectSoundCreate: Couldn't create driver heap for on-card memory" );
  482. hr = DSERR_OUTOFMEMORY;
  483. }
  484. }
  485. if (DS_OK == hr) {
  486. mxInitialize(pds);
  487. cb = sizeof(DSBWAVEBLTI) + (sizeof(DSBWAVEBLTSRCI)*LIMIT_BLT_SOURCES);
  488. pds->pdswb = (LPDSBWAVEBLTI)MemAlloc(cb);
  489. if (pds->pdswb) {
  490. pds->pdswb->padswbs = (LPDSBWAVEBLTSRCI)(((LPSTR)pds->pdswb) + sizeof(*pds->pdswb));
  491. pds->pdswb->dwSize = sizeof(DSBWAVEBLT);
  492. pds->pdswb->dwCount = 0;
  493. pds->guid = *rguid;
  494. pds->uDeviceID = uWaveDeviceID;
  495. pds->pds3d = NULL;
  496. pds->uRefCount = 1;
  497. pds->fdwInternal |= DS_INTERNALF_ALLOCATED;
  498. pds->dwSig = DSOUNDSIG;
  499. pds->pNext = gpdsinfo->pDSoundObj;
  500. gpdsinfo->pDSoundObj = pds;
  501. } else {
  502. hr = DSERR_OUTOFMEMORY;
  503. }
  504. if (hr) mxTerminate(pds);
  505. }
  506. if (hr) {
  507. if (pds->pDriverHeap && (pds->dwHeapType & DSDHEAP_CREATEHEAP)) {
  508. VidMemFini(pds->pDriverHeap);
  509. pds->pDriverHeap = NULL;
  510. }
  511. if (pds->hwo) {
  512. HelperWaveClose((ULONG)pds->hwo);
  513. pds->hwo = NULL;
  514. }
  515. vxdDrvClose(pds->hHal);
  516. pds->hHal = NULL;
  517. }
  518. }
  519. }
  520. }
  521. if (hr) {
  522. MemFree(pds);
  523. pds = NULL;
  524. }
  525. if (DS_OK == hr) *ppds = pds;
  526. return hr;
  527. }
  528. //--------------------------------------------------------------------------;
  529. //
  530. // CreateDsEmulated
  531. //
  532. // This function creates a DSOUND object (a pds) using an mmsystem wave driver
  533. // identified by a guid.
  534. //
  535. // Parameters:
  536. // REFGUID rguid : identifies the guid of the requested wave driver
  537. //
  538. // LPDSOUND *ppds : receives a pointer to the new DSOUND object
  539. //
  540. // Return value (HRESULT):
  541. // DS_OK : if and only if a DSOUND object has been created successfully
  542. //
  543. // DSERR_NODRIVER : if there is no wave driver having the specified guid
  544. //
  545. // other : some other failure
  546. //
  547. // Notes:
  548. // For return values other than DS_OK, there should be no side effects
  549. //
  550. //--------------------------------------------------------------------------;
  551. HRESULT WINAPI CreateDsEmulated(REFGUID rguid, LPDSOUND *ppds)
  552. {
  553. LPDSOUND pds;
  554. LPDSOUND pdsT;
  555. UINT cWaveDev;
  556. UINT iWaveDev;
  557. HRESULT hr;
  558. int cb;
  559. *ppds = NULL;
  560. pds = NULL;
  561. cWaveDev = waveOutGetNumDevs();
  562. cWaveDev = min(cWaveDev, LIMIT_WAVE_DEVICES-1);
  563. hr = DS_OK;
  564. for (iWaveDev = 0; iWaveDev < cWaveDev; iWaveDev++) {
  565. if (IsEqualGUID(rguid, &gpdsinfo->aguidWave[iWaveDev])) break;
  566. }
  567. if (iWaveDev >= cWaveDev) hr = DSERR_NODRIVER;
  568. if (DS_OK == hr) {
  569. // REMIND here is a temporary block to prevent us from
  570. // creating a second waveem ds object on a different
  571. // wave device. This would result in a deadlock. We
  572. // look for another waveem ds object with a different
  573. // guid.
  574. for (pdsT = gpdsinfo->pDSoundObj; pdsT; pdsT = pdsT->pNext) {
  575. if ( (pdsT->fdwInternal & DS_INTERNALF_WAVEEMULATED) &&
  576. (!IsEqualGUID(rguid, &pdsT->guid)) )
  577. {
  578. break;
  579. }
  580. }
  581. if (pdsT) hr = DSERR_ALLOCATED;
  582. if (DS_OK == hr) {
  583. WAVEOUTCAPS woc;
  584. MMRESULT mmr;
  585. mmr = waveOutGetDevCaps(iWaveDev, &woc, sizeof(woc));
  586. switch (mmr) {
  587. case MMSYSERR_NOERROR:
  588. if (woc.dwSupport & WAVECAPS_SYNC) {
  589. hr = DSERR_NODRIVER;
  590. } else {
  591. hr = DS_OK;
  592. }
  593. break;
  594. case MMSYSERR_NOMEM:
  595. hr = DSERR_OUTOFMEMORY;
  596. break;
  597. case MMSYSERR_NODRIVER:
  598. hr = DSERR_NODRIVER;
  599. break;
  600. default:
  601. hr = DSERR_GENERIC;
  602. }
  603. }
  604. if (DS_OK == hr) {
  605. pds = (LPDSOUND)MemAlloc(sizeof(*pds));
  606. if (pds) {
  607. pds->guid = *rguid;
  608. cb = sizeof(DSBWAVEBLTI) + (sizeof(DSBWAVEBLTSRCI)*LIMIT_BLT_SOURCES);
  609. pds->pdswb = (LPDSBWAVEBLTI)MemAlloc(cb);
  610. if (pds->pdswb) {
  611. pds->pdswb->padswbs = (LPDSBWAVEBLTSRCI)(((LPSTR)pds->pdswb) + sizeof(*pds->pdswb));
  612. pds->pdswb->dwSize = sizeof(DSBWAVEBLT);
  613. pds->pdswb->dwCount = 0;
  614. //
  615. pds->uDeviceID = iWaveDev;
  616. pds->pds3d = NULL;
  617. pds->uRefCount = 1;
  618. pds->fdwInternal |= DS_INTERNALF_ALLOCATED;
  619. pds->fdwInternal |= DS_INTERNALF_WAVEEMULATED;
  620. pds->dwSig = DSOUNDSIG;
  621. //
  622. pds->pNext = gpdsinfo->pDSoundObj;
  623. gpdsinfo->pDSoundObj = pds;
  624. } else {
  625. hr = DSERR_OUTOFMEMORY;
  626. }
  627. if (hr) {
  628. MemFree(pds);
  629. pds = NULL;
  630. }
  631. } else {
  632. hr = DSERR_OUTOFMEMORY;
  633. }
  634. }
  635. }
  636. if (DS_OK == hr) *ppds = pds;
  637. return hr;
  638. }
  639. //--------------------------------------------------------------------------;
  640. //
  641. // DseInitializeFromGuid
  642. //
  643. // This function attempts to initialize a DSOUNDEXTERNAL object to use
  644. // the direct sound driver identified by rguid.
  645. //
  646. // Parameters:
  647. // REFGUID rguid : identifies the guid of the requested driver
  648. //
  649. // Return value (HRESULT):
  650. // DS_OK : if and only if a DSOUND object has been created successfully
  651. //
  652. // DSERR_NODRIVER : if there is no driver having the specified guid
  653. //
  654. // other : some other failure
  655. //
  656. // Notes:
  657. // For return values other than DS_OK, there should be no side effects
  658. //
  659. //--------------------------------------------------------------------------;
  660. HRESULT WINAPI DseInitializeFromGuid(LPDSOUNDEXTERNAL pdse, REFGUID rguid)
  661. {
  662. LPDSOUND pds;
  663. DSBUFFERDESC dsbd;
  664. HANDLE hFocusLock;
  665. HRESULT hr;
  666. DPF(0, "DseInitializeFromGuid()");
  667. // First look for an existing ds object with same guid
  668. hr = DSERR_NODRIVER;
  669. for (pds = gpdsinfo->pDSoundObj; pds; pds = pds->pNext) {
  670. if (IsEqualGUID(rguid, &pds->guid)) {
  671. // We've found one! AddRef it since we're going to use it.
  672. DsAddRef(pds);
  673. pdse->pds = pds;
  674. pdse->pNext = NULL;
  675. pdse->dwPID = GetCurrentProcessId();
  676. pdse->pdsbe = NULL;
  677. pdse->tidSound = TID_INIT_VALUE;
  678. pdse->dwPriority = DSSCL_NORMAL;
  679. pdse->dwSpeakerConfig = DSSPEAKER_STEREO;
  680. pdse->pwfxApp = NULL;
  681. if (!GetFocusLock(&hFocusLock)) {
  682. DPF(2, "DirectSoundCreateFromGuid: note: error getting focus lock");
  683. }
  684. pdse->pNext = gpdsinfo->pDSoundExternalObj;
  685. gpdsinfo->pDSoundExternalObj = pdse;
  686. if (!ReleaseFocusLock(hFocusLock)) {
  687. DPF(2, "DirectSoundCreateFromGuid: note: error releasing focus lock");
  688. }
  689. return DS_OK;
  690. }
  691. }
  692. // Try native ds
  693. if (DSERR_NODRIVER == hr) hr = CreateDsNative(rguid, &pds);
  694. // Try emulated ds
  695. if (DSERR_NODRIVER == hr) hr = CreateDsEmulated(rguid, &pds);
  696. // If none of the above worked, return
  697. if (DS_OK != hr) return hr;
  698. // At this point, we have a pds that has been addrefed
  699. hr = DsInitializeDefaultFormat(pds);
  700. if (DS_OK != hr) {
  701. // We don't really care about this failure
  702. DPF(0, "DirectSoundCreateFromGuid: note: DsInitializeDefaultFormat returned %08Xh", hr);
  703. hr = DS_OK;
  704. }
  705. //
  706. pdse->pds = pds;
  707. pdse->pNext = NULL;
  708. pdse->dwPID = GetCurrentProcessId();
  709. pdse->pdsbe = NULL;
  710. pdse->tidSound = TID_INIT_VALUE;
  711. pdse->dwPriority = DSSCL_NORMAL;
  712. pdse->dwSpeakerConfig = DSSPEAKER_STEREO;
  713. pdse->pwfxApp = NULL;
  714. //
  715. ZeroMemory(&dsbd, sizeof(dsbd));
  716. dsbd.dwSize = sizeof(dsbd);
  717. dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
  718. dsbd.dwBufferBytes = 0;
  719. hr = DseCreateDsbe(pdse, &dsbd, &pds->pdsbePrimary);
  720. if (DS_OK == hr) {
  721. pds->pdsbePrimary->dwPriority = DSSCL_WRITEPRIMARY;
  722. pds->pdsbePrimary->dwPID = DWPRIMARY_INTERNAL_PID;
  723. pds->pdsbePrimary->pNext = NULL;
  724. pds->pdsbePrimary->pdse = NULL;
  725. pds->pdsbPrimary->dsbe.pdse = NULL;
  726. pds->pdsbPrimary->plProcess->dwPID = DWPRIMARY_INTERNAL_PID;
  727. pdse->pdsbe = NULL;
  728. }
  729. if (DS_OK == hr) {
  730. //
  731. if (!GetFocusLock(&hFocusLock)) {
  732. DPF(2, "DirectSoundCreateFromGuid: note: error getting focus lock");
  733. }
  734. pdse->pNext = gpdsinfo->pDSoundExternalObj;
  735. gpdsinfo->pDSoundExternalObj = pdse;
  736. if (!ReleaseFocusLock(hFocusLock)) {
  737. DPF(2, "DirectSoundCreateFromGuid: note: error releasing focus lock");
  738. }
  739. }
  740. if (DS_OK != hr) {
  741. DsRelease(pds);
  742. pds = NULL;
  743. }
  744. return hr;
  745. }
  746. HRESULT WINAPI DirectSoundCreate
  747. (
  748. LPGUID lpGUID,
  749. LPDIRECTSOUND *ppIDs,
  750. IUnknown FAR *pUnkOuter
  751. )
  752. {
  753. LPDIRECTSOUND pIDs;
  754. HRESULT hr;
  755. if( !VALID_DWORD_PTR(ppIDs) ) {
  756. RPF("DirectSoundCreate: Invalid pointer to DS object pointer");
  757. return DSERR_INVALIDPARAM;
  758. }
  759. *ppIDs = NULL;
  760. DPF(3, "DirectSoundCreate pGUID %X",(DWORD)lpGUID );
  761. if( lpGUID != NULL ) {
  762. DPF(3, "DirectSoundCreate GUID %X %X %X %X",
  763. *(((LPDWORD)lpGUID) + 0 ),
  764. *(((LPDWORD)lpGUID) + 1 ),
  765. *(((LPDWORD)lpGUID) + 2 ),
  766. *(((LPDWORD)lpGUID) + 3 ) );
  767. }
  768. hr = CreateNewDirectSoundObject (&pIDs, pUnkOuter);
  769. if (S_OK != hr) return hr;
  770. hr = IDirectSound_Initialize(pIDs, lpGUID);
  771. if (S_OK == hr) {
  772. *ppIDs = pIDs;
  773. } else {
  774. IDirectSound_Release(pIDs);
  775. }
  776. return hr;
  777. }
  778. //--------------------------------------------------------------------------
  779. //
  780. // Enumerate
  781. //
  782. // This is a helper function for DirectSoundEnumerate. This function will
  783. // execute the callback to the client. The caller of this function provides
  784. // a pointer to a BOOL which this function uses to determine whether to
  785. // enumerate the "Primary Sound Device".
  786. //
  787. // Arguments:
  788. // LPDSENUMCALLBACK lpCallback:
  789. // LPVOID lpContext:
  790. // REFGUID rguid:
  791. // LPTSTR pszDescription:
  792. // LPTSTR pszDriver:
  793. // These are required to execute the callback to the client
  794. // PBOOL pfDidPrimary:
  795. // Indicates whether the primary driver has already been enumerated.
  796. // If it hasn't, this function will enumerate it and set this BOOL
  797. // to TRUE.
  798. //
  799. // Return (BOOL):
  800. // The return value from the client's callback.
  801. //
  802. //--------------------------------------------------------------------------
  803. BOOL EnumerateA(LPDSENUMCALLBACKA lpCallback, LPVOID lpContext, LPGUID pguid,
  804. char *pszDescription, char *pszDriver, PBOOL pfDidPrimary)
  805. {
  806. BOOL fReturn;
  807. char szDriver[MAX_PATH];
  808. char szDescription[MAX_PATH];
  809. if (!*pfDidPrimary) {
  810. *pfDidPrimary = TRUE;
  811. szDescription[0] = '\0';
  812. szDriver[0] = '\0';
  813. if( !LoadStringA( hModule, IDS_PRIMARYDRIVER, szDescription, sizeof(szDescription) ) ||
  814. !LoadStringA( hModule, IDS_SOUND, szDriver, sizeof(szDriver) ) ) {
  815. DPF(0,"EnumerateA : error: LoadString failed.");
  816. }
  817. DPF(3,"EnumerateA : note: enumerating primary driver: Desc=\"%s\", Filename=\"%s\".",szDescription,szDriver);
  818. fReturn = lpCallback( NULL, szDescription, szDriver, lpContext );
  819. if (!fReturn) return fReturn;
  820. }
  821. fReturn = lpCallback(pguid, pszDescription, pszDriver, lpContext);
  822. return fReturn;
  823. }
  824. BOOL EnumerateW(LPDSENUMCALLBACKW lpCallback, LPVOID lpContext, LPGUID pguid,
  825. WCHAR *pszDescription, WCHAR *pszDriver, PBOOL pfDidPrimary)
  826. {
  827. BOOL fReturn;
  828. WCHAR szDriver[MAX_PATH];
  829. WCHAR szDescription[MAX_PATH];
  830. if (!*pfDidPrimary) {
  831. *pfDidPrimary = TRUE;
  832. szDescription[0] = '\0';
  833. szDriver[0] = '\0';
  834. if( !LoadStringW( hModule, IDS_PRIMARYDRIVER, szDescription, SIZEOF(szDescription) ) ||
  835. !LoadStringW( hModule, IDS_SOUND, szDriver, SIZEOF(szDriver) ) ) {
  836. DPF(0,"EnumerateW : error: LoadString failed.");
  837. }
  838. DPF(3,"EnumerateW : note: enumerating primary driver: Desc=\"%ls\", Filename=\"%ls\".",szDescription,szDriver);
  839. fReturn = lpCallback( NULL, szDescription, szDriver, lpContext );
  840. if (!fReturn) return fReturn;
  841. }
  842. fReturn = lpCallback(pguid, pszDescription, pszDriver, lpContext);
  843. return fReturn;
  844. }
  845. /*
  846. * DirectSoundEnumerateA
  847. */
  848. HRESULT WINAPI DirectSoundEnumerateA(
  849. LPDSENUMCALLBACKA lpCallback,
  850. LPVOID lpContext )
  851. {
  852. DWORD rc;
  853. DSDRIVERDESC dsDrvDesc;
  854. GUID guid;
  855. DSVAL dsv;
  856. DWORD dw;
  857. DWORD dwNumDev;
  858. BOOL fDidPrimary;
  859. char szDriver[MAX_PATH];
  860. char szDescription[MAX_PATH];
  861. struct _wave_em_info {
  862. DWORD dnDevNode;
  863. UINT ulDeviceNum;
  864. UINT nVxdIds;
  865. BOOL bEnumerated;
  866. WORD awVxdIds[50];
  867. } * pWaveEm = NULL;
  868. if( !VALID_CODE_PTR( lpCallback ) )
  869. {
  870. RPF("DirectSoundEnumerate: Invalid callback routine" );
  871. return DSERR_INVALIDPARAM;
  872. }
  873. fDidPrimary = FALSE;
  874. // gather up devnode & vxd id information from mmsystem
  875. // and build a table devices in pWaveEm. we will check off
  876. // devices from this table as we enumerate so that we wont
  877. // end up enumerating the same device twice
  878. //
  879. DPF(3, "enum: getting devnodes for wave devices");
  880. dwNumDev = waveOutGetNumDevs();
  881. dwNumDev = min(dwNumDev, LIMIT_WAVE_DEVICES-1);
  882. pWaveEm = NULL;
  883. if (dwNumDev) {
  884. // if we fail to allocate memory, go ahead anyway.
  885. // we will just end up enumerating some devices more than
  886. // once in this case.
  887. //
  888. pWaveEm = MemAlloc (sizeof(*pWaveEm) * dwNumDev);
  889. if (pWaveEm) {
  890. for (dw = 0; dw < dwNumDev; ++dw) {
  891. DWORD ii;
  892. if (waveOutMessage ((HWAVEOUT)dw, DRV_QUERYDEVNODE,
  893. (DWORD)&pWaveEm[dw].dnDevNode, 0))
  894. {
  895. pWaveEm[dw].dnDevNode = 0;
  896. }
  897. pWaveEm[dw].nVxdIds = NUMELMS(pWaveEm[dw].awVxdIds);
  898. if (waveOutMessage ((HWAVEOUT)dw, DRV_QUERYDRIVERIDS,
  899. (DWORD)pWaveEm[dw].awVxdIds,
  900. (DWORD)&pWaveEm[dw].nVxdIds))
  901. {
  902. pWaveEm[dw].nVxdIds = 0;
  903. }
  904. pWaveEm[dw].ulDeviceNum = 0;
  905. for (ii = 0; ii < dw; ++ii)
  906. {
  907. if (pWaveEm[ii].dnDevNode == pWaveEm[dw].dnDevNode)
  908. {
  909. ++pWaveEm[dw].ulDeviceNum;
  910. }
  911. }
  912. DPF(3, "Wave[%d,%d] dn=%08lX awVxd=%d:{%x,%x,%x,%x}", dw,
  913. pWaveEm[dw].ulDeviceNum,
  914. pWaveEm[dw].dnDevNode,
  915. pWaveEm[dw].nVxdIds,
  916. pWaveEm[dw].awVxdIds[0],
  917. pWaveEm[dw].awVxdIds[1],
  918. pWaveEm[dw].awVxdIds[2],
  919. pWaveEm[dw].awVxdIds[3]
  920. );
  921. pWaveEm[dw].bEnumerated = FALSE;
  922. }
  923. } else {
  924. DPF(0, "Direct Sound: Failed to allocate pWaveEm, may enumerate some devices more than once!");
  925. }
  926. }
  927. //
  928. // REMIND handle error conditions better, also enumerate emulated devices
  929. //
  930. #if defined(RDEBUG) || defined(DEBUG)
  931. if (!(gpdsinfo->fEnumOnlyWaveDevs))
  932. #endif
  933. {
  934. ZeroMemory(&dsDrvDesc, sizeof(dsDrvDesc));
  935. dsv = vxdDrvGetNextDriverDesc(NULL, &guid, &dsDrvDesc);
  936. while (DS_OK == dsv) {
  937. GUID guidLast;
  938. DPF(3, "DirectSoundEnum GUID %X %X %X %X",
  939. *(((LPDWORD)&guid) + 0 ),
  940. *(((LPDWORD)&guid) + 1 ),
  941. *(((LPDWORD)&guid) + 2 ),
  942. *(((LPDWORD)&guid) + 3 ) );
  943. // if there is a wave device corresponding to this device,
  944. // check it off as having been enumerated already.
  945. //
  946. if (pWaveEm) {
  947. for (dw = 0; dw < dwNumDev; ++dw) {
  948. DPF(3, "\tdn=%08lX num=%d vxd=%X",
  949. dsDrvDesc.dnDevNode, dsDrvDesc.ulDeviceNum, dsDrvDesc.wVxdId);
  950. if ((pWaveEm[dw].dnDevNode == dsDrvDesc.dnDevNode) &&
  951. (pWaveEm[dw].ulDeviceNum == dsDrvDesc.ulDeviceNum)) {
  952. UINT ii;
  953. for (ii = 0; ii < pWaveEm[dw].nVxdIds; ++ii) {
  954. if ((pWaveEm[dw].awVxdIds[ii] == dsDrvDesc.wVxdId)) {
  955. pWaveEm[dw].bEnumerated = TRUE;
  956. DPF(3, "Enum: marking wave as already enum'd");
  957. break;
  958. }
  959. }
  960. }
  961. }
  962. }
  963. DPF(3,"DsEnum - enumerating driver: Desc=\"%s\", Filename=\"%s\".",dsDrvDesc.szDesc,dsDrvDesc.szDrvname);
  964. rc = EnumerateA(lpCallback, lpContext, &guid, dsDrvDesc.szDesc, dsDrvDesc.szDrvname, &fDidPrimary);
  965. if ( !rc ) goto bailout;
  966. guidLast = guid;
  967. ZeroMemory(&dsDrvDesc, sizeof(dsDrvDesc));
  968. dsv = vxdDrvGetNextDriverDesc(&guidLast, &guid, &dsDrvDesc);
  969. }
  970. }
  971. // loop though the wave devices, enumererating as emulated,
  972. // those devices that have not already been enumerated as native.
  973. //
  974. for (dw = 0; dw < dwNumDev; ++dw) {
  975. struct {
  976. WAVEOUTCAPSA woc;
  977. char szFill[MAX_PATH]; // gurantee room for appended string
  978. } uu;
  979. guid = gpdsinfo->aguidWave[dw];
  980. //#define ENUM_DSDEVS_AS_WAVE_ALSO
  981. // if we already enumerated this device as non-emulated
  982. // skip it here.
  983. //
  984. if (pWaveEm && pWaveEm[dw].bEnumerated) {
  985. if (!(gpdsinfo->fDupEnumWaveDevs)) {
  986. DPF(3, "Direct Sound Enum Wave%d already enumerated", dw);
  987. continue;
  988. }
  989. }
  990. DPF(3, "DirectSoundEnum Wave%d GUID %X %X %X %X", dw,
  991. *(((LPDWORD)&guid) + 0 ),
  992. *(((LPDWORD)&guid) + 1 ),
  993. *(((LPDWORD)&guid) + 2 ),
  994. *(((LPDWORD)&guid) + 3 ) );
  995. waveOutGetDevCapsA (dw, &uu.woc, sizeof(uu.woc));
  996. szDescription[0] = '\0';
  997. if( !LoadStringA( hModule, IDS_EMULATED, szDescription, sizeof(szDescription) ) ) {
  998. DPF(0,"DirectSoundEnumerateA - LoadString2 failed.");
  999. }
  1000. lstrcatA (uu.woc.szPname, szDescription);
  1001. szDescription[0] = TEXT('\0');
  1002. szDriver[0] = TEXT('\0');
  1003. if( !LoadStringA( hModule, IDS_DRIVERLD, szDescription, sizeof(szDescription) ) ) {
  1004. DPF(0,"DirectSoundEnumerateA - LoadString3 failed.");
  1005. } else {
  1006. wsprintfA (szDriver, szDescription, dw);
  1007. }
  1008. if (0 == (uu.woc.dwSupport & WAVECAPS_SYNC)) {
  1009. DPF(3,"DsEnum - enumerating driver: Desc=\"%s\", Filename=\"%s\".",uu.woc.szPname,szDriver);
  1010. rc = EnumerateA(lpCallback, lpContext, &guid, uu.woc.szPname, szDriver, &fDidPrimary);
  1011. if ( !rc ) goto bailout;
  1012. } else {
  1013. DPF(2, "DsEnum: note: driver \"%s\" reported WAVECAPS_SYNC, not emumerating", uu.woc.szPname);
  1014. }
  1015. }
  1016. bailout:
  1017. if (pWaveEm) MemFree (pWaveEm);
  1018. return DS_OK;
  1019. } /* DirectSoundEnumerateA */
  1020. HRESULT WINAPI DirectSoundEnumerateW(
  1021. LPDSENUMCALLBACKW lpCallback,
  1022. LPVOID lpContext )
  1023. {
  1024. #ifndef WINNT
  1025. if( !VALID_CODE_PTR( lpCallback ) )
  1026. {
  1027. RPF("DirectSoundEnumerate: Invalid callback routine" );
  1028. return DSERR_INVALIDPARAM;
  1029. }
  1030. return DSERR_UNSUPPORTED;
  1031. #else
  1032. #ifndef DSBLD_EMULONLY
  1033. // this is implemented for only emulation mode
  1034. #error
  1035. #endif
  1036. DWORD rc;
  1037. GUID guid;
  1038. DWORD dw;
  1039. DWORD dwNumDev;
  1040. BOOL fDidPrimary;
  1041. WCHAR szDriver[MAX_PATH];
  1042. WCHAR szDescription[MAX_PATH];
  1043. if( !VALID_CODE_PTR( lpCallback ) )
  1044. {
  1045. RPF("DirectSoundEnumerate: Invalid callback routine" );
  1046. return DSERR_INVALIDPARAM;
  1047. }
  1048. fDidPrimary = FALSE;
  1049. // loop though the wave devices, enumererating as emulated,
  1050. // those devices that have not already been enumerated as native.
  1051. dwNumDev = waveOutGetNumDevs();
  1052. for (dw = 0; dw < dwNumDev; ++dw) {
  1053. struct {
  1054. WAVEOUTCAPSW woc;
  1055. WCHAR szFill[MAX_PATH]; // gurantee room for appended string
  1056. } uu;
  1057. guid = gpdsinfo->aguidWave[dw];
  1058. DPF(3, "DirectSoundEnum Wave%d GUID %X %X %X %X", dw,
  1059. *(((LPDWORD)&guid) + 0 ),
  1060. *(((LPDWORD)&guid) + 1 ),
  1061. *(((LPDWORD)&guid) + 2 ),
  1062. *(((LPDWORD)&guid) + 3 ) );
  1063. waveOutGetDevCapsW (dw, &uu.woc, sizeof(uu.woc));
  1064. szDescription[0] = L'\0';
  1065. if( !LoadStringW( hModule, IDS_EMULATED, szDescription, SIZEOF(szDescription) ) ) {
  1066. DPF(0,"DirectSoundEnumerateW - LoadString2 failed.");
  1067. }
  1068. lstrcatW (uu.woc.szPname, szDescription);
  1069. szDescription[0] = L'\0';
  1070. szDriver[0] = L'\0';
  1071. if( !LoadStringW( hModule, IDS_DRIVERLD, szDescription, SIZEOF(szDescription) ) ) {
  1072. DPF(0,"DirectSoundEnumerateW - LoadString3 failed.");
  1073. } else {
  1074. wsprintfW (szDriver, szDescription, dw);
  1075. }
  1076. if (0 == (uu.woc.dwSupport & WAVECAPS_SYNC)) {
  1077. DPF(3,"DsEnum - enumerating driver: Desc=\"%ls\", Filename=\"%ls\".",uu.woc.szPname,szDriver);
  1078. rc = EnumerateW(lpCallback, lpContext, &guid, uu.woc.szPname, szDriver, &fDidPrimary);
  1079. if ( !rc ) goto bailout;
  1080. } else {
  1081. DPF(2, "DsEnum: note: driver \"%ls\" reported WAVECAPS_SYNC, not emumerating", uu.woc.szPname);
  1082. }
  1083. }
  1084. bailout:
  1085. return DS_OK;
  1086. #endif
  1087. } /* DirectSoundEnumerateW */
  1088. //---------------------------------------------------------------------------
  1089. //
  1090. // DSDetermineDMASize
  1091. //
  1092. // Determine DMA buffer size on the given emulated direct sound device and
  1093. // munge it to figure out the desired size for the emulator to allocate
  1094. // per wave header.
  1095. //
  1096. //---------------------------------------------------------------------------
  1097. #define TIMEOUT_PERIOD 5000
  1098. MMRESULT DSDetermineDMASize
  1099. (
  1100. LPDSOUND pds,
  1101. LPWAVEFORMATEX pwfx
  1102. )
  1103. {
  1104. UINT idx;
  1105. WAVEHDR whdr;
  1106. UINT mmResult;
  1107. BYTE aWaveData[4];
  1108. DWORD dwTotalTime;
  1109. DWORD dwBeginTime;
  1110. DWORD ulDmaSizeBytes;
  1111. DPF(3, "DSDetermineDMASize");
  1112. // we'll send a packet of 4 bytes
  1113. // (that's at least 1 sample in every format)
  1114. for (idx = 0; idx < sizeof(aWaveData); ++idx)
  1115. aWaveData[idx] = (pwfx->wBitsPerSample == 16) ? 0 : 0x80;
  1116. // prepare header
  1117. //
  1118. whdr.lpData = (LPBYTE)aWaveData;
  1119. whdr.dwBufferLength = sizeof(aWaveData);
  1120. whdr.dwFlags = 0;
  1121. whdr.dwLoops = 0;
  1122. whdr.dwUser = 0;
  1123. mmResult = waveOutPrepareHeader (pds->hwo, &whdr, sizeof(whdr));
  1124. if (mmResult)
  1125. return mmResult;
  1126. dwTotalTime = 0;
  1127. dwBeginTime = timeGetTime();
  1128. // play our buffer
  1129. mmResult = waveOutWrite(pds->hwo, &whdr, sizeof(whdr));
  1130. if (!mmResult)
  1131. {
  1132. // spin until the done bit is set, or 5 seconds
  1133. while (!(whdr.dwFlags & WHDR_DONE))
  1134. {
  1135. if (dwTotalTime >= TIMEOUT_PERIOD)
  1136. {
  1137. DPF (0, "TIMEOUT getting dma buffer size");
  1138. mmResult = MMSYSERR_ERROR;
  1139. //
  1140. // Without the reset we were freeing the header before the
  1141. // driver could process it, thus causing faults in the driver.
  1142. //
  1143. waveOutReset(pds->hwo);
  1144. break;
  1145. }
  1146. // This thread is THREAD_PRIORITY_TIME_CRITICAL so it would be
  1147. // very dangerous to busy wait without explicitly giving up the
  1148. // CPU for a while.
  1149. Sleep(10);
  1150. dwTotalTime = timeGetTime() - dwBeginTime;
  1151. }
  1152. } else {
  1153. DPF(0, "waveOutWrite (determine DMA size) returned %u", mmResult);
  1154. }
  1155. waveOutUnprepareHeader(pds->hwo, &whdr, sizeof(whdr));
  1156. if (!mmResult)
  1157. {
  1158. // if it's smaller than 62ms, it probably isn't
  1159. // a dma based card.
  1160. dwTotalTime = max(dwTotalTime, 62);
  1161. DPF(3, "DSDetermineDMASize dwTotalTime %lu", dwTotalTime);
  1162. ulDmaSizeBytes = dwTotalTime * pwfx->nSamplesPerSec;
  1163. ulDmaSizeBytes *= pwfx->nBlockAlign;
  1164. ulDmaSizeBytes /= 1000;
  1165. DPF(3, "ulDmaSizeBytes %lu", (DWORD)ulDmaSizeBytes);
  1166. // add in 10% for slop
  1167. // and to account for drivers that deal with dma wrapping
  1168. ulDmaSizeBytes += (ulDmaSizeBytes * 10) / 100;
  1169. // make sure it's a mod 4 (no samples spanning buffers);
  1170. ulDmaSizeBytes &= 0xfffffffc;
  1171. pds->cbDMASize = ulDmaSizeBytes;
  1172. }
  1173. else
  1174. {
  1175. DPF(3, "DSDetermineDMASize: waveOutWRite returned %u",
  1176. (UINT)mmResult);
  1177. }
  1178. DPF(3, "DSDetermineDMASize done");
  1179. return mmResult;
  1180. }
  1181. //---------------------------------------------------------------------------
  1182. //
  1183. // DSGetCurrentSample
  1184. //
  1185. // Basically sample-accurate waveOutGetPosition
  1186. //
  1187. // If we're sample accurate, just call waveOutGetPosition
  1188. //
  1189. // Otherwise, call QueryPerformanceCounter and calculate what sample
  1190. // should be playing.
  1191. //
  1192. // NOTE: What happens if the wave device was ever starved? We will
  1193. // be out of sync if that happens
  1194. //
  1195. //---------------------------------------------------------------------------
  1196. DWORD FNGLOBAL DSGetCurrentSample
  1197. (
  1198. LPDSOUND pds
  1199. )
  1200. {
  1201. LARGE_INTEGER qwTicks;
  1202. if (pds->fdwInternal & DS_INTERNALF_SAMPLEACCURATE)
  1203. {
  1204. MMTIME mmtTime;
  1205. mmtTime.wType = TIME_SAMPLES;
  1206. waveOutGetPosition(pds->hwo, &mmtTime, sizeof(mmtTime));
  1207. return mmtTime.u.sample;
  1208. } // implied "ELSE"
  1209. // have we re-set the device?
  1210. if (pds->qwTicks.LowPart == 0)
  1211. {
  1212. // yep
  1213. return 0;
  1214. }
  1215. QueryPerformanceCounter (&qwTicks);
  1216. // assert(qwTicks.LowPart - pMixData->qwTicks.LowPart);
  1217. // assert(pMixData->ulTickRate);
  1218. // this deals with the wrap case cuz everyting is 32 bits
  1219. //
  1220. return UMulDivRDClip(qwTicks.LowPart - pds->qwTicks.LowPart,
  1221. pds->pdsbPrimary->pwfx->nSamplesPerSec,
  1222. pds->ulTickRate);
  1223. }
  1224. //---------------------------------------------------------------------------
  1225. //
  1226. // DSSetCurrentSample
  1227. //
  1228. // Find a synchronization point between waveOutGetPosition and
  1229. // QueryPerformanceCounter
  1230. //
  1231. // If we have a sample accurate device, don't bother - we'll just
  1232. // trust the driver
  1233. //
  1234. // Otherwise, wait for the instant waveOutGetPosition changes
  1235. // and save off the QueryPerformanceCounter at that time. Then
  1236. // calculate the QPC time when sample 0 played. This is the
  1237. // timebase we save.
  1238. //
  1239. // !!! Failure cases !!!
  1240. //
  1241. //---------------------------------------------------------------------------
  1242. BOOL DSSetCurrentSample
  1243. (
  1244. LPDSOUND pds
  1245. )
  1246. {
  1247. LARGE_INTEGER qwFreq;
  1248. MMTIME mmtTime;
  1249. DWORD ulCardTime;
  1250. DWORD tmTimeout;
  1251. MMRESULT mmr;
  1252. DPF(3, "DSSetCurrentSample");
  1253. QueryPerformanceFrequency(&qwFreq);
  1254. mmtTime.wType = TIME_SAMPLES;
  1255. mmr = waveOutGetPosition(pds->hwo, &mmtTime, sizeof(mmtTime));
  1256. if ((!mmr) && mmtTime.wType != TIME_SAMPLES)
  1257. {
  1258. DPF(0, "This driver sucks! (Doesn't support TIME_SAMPLES)");
  1259. mmtTime.wType = TIME_MS;
  1260. mmr = waveOutGetPosition(pds->hwo, &mmtTime, sizeof(mmtTime));
  1261. if ((!mmr) && mmtTime.wType != TIME_MS)
  1262. {
  1263. DPF(0, "This driver REALLY sucks! (Doesn't support TIME_MS either)");
  1264. mmtTime.wType = TIME_BYTES;
  1265. mmr = waveOutGetPosition(pds->hwo, &mmtTime, sizeof(mmtTime));
  1266. if ((!mmr) && mmtTime.wType != TIME_BYTES)
  1267. {
  1268. DPF(0, "Last chance failed! Card does not even support TIME_BYTES!");
  1269. DPF(0, "Bob, I'm not fixing any card that exhibits this behavior!!!");
  1270. DPF(0, "Given time format was %u", (UINT)mmtTime.wType);
  1271. return FALSE;
  1272. }
  1273. }
  1274. }
  1275. tmTimeout = timeGetTime();
  1276. ulCardTime = (mmtTime.wType == TIME_MS ?
  1277. mmtTime.u.ms :
  1278. (mmtTime.wType == TIME_SAMPLES ? mmtTime.u.sample :
  1279. mmtTime.u.cb));
  1280. do
  1281. {
  1282. mmr = waveOutGetPosition (pds->hwo, &mmtTime, sizeof(mmtTime));
  1283. if (mmr)
  1284. {
  1285. DPF(0, "waveOutGetPosition returned %u. BAD driver!!!w", mmr);
  1286. return FALSE;
  1287. }
  1288. if (timeGetTime() - tmTimeout >= TIMEOUT_PERIOD*2)
  1289. {
  1290. DPF(0, "Timeout in DSSetCurrentSample!");
  1291. return FALSE;
  1292. }
  1293. } while (ulCardTime == (mmtTime.wType == TIME_MS ?
  1294. mmtTime.u.ms :
  1295. (mmtTime.wType == TIME_SAMPLES ? mmtTime.u.sample :
  1296. mmtTime.u.cb)));
  1297. ASSERT(pds->pdsbPrimary->pwfx->wFormatTag == WAVE_FORMAT_PCM);
  1298. if (mmtTime.wType == TIME_BYTES)
  1299. {
  1300. mmtTime.wType = TIME_SAMPLES;
  1301. mmtTime.u.sample = mmtTime.u.cb / pds->pdsbPrimary->pwfx->nBlockAlign;
  1302. }
  1303. DPF(3, "DSSetCurrentSample: End profile");
  1304. QueryPerformanceCounter(&pds->qwTicks);
  1305. if (qwFreq.HighPart != 0)
  1306. {
  1307. DPF(0, "Clock > 4Ghz in SetCurrentSample ???");
  1308. DPF(0, "Please send this machine to [email protected]");
  1309. return FALSE;
  1310. }
  1311. pds->ulTickRate = qwFreq.LowPart;
  1312. // assert( pds->ulTickRate );
  1313. // assert( mmtTime.u.sample );
  1314. if (mmtTime.wType == TIME_SAMPLES)
  1315. {
  1316. pds->qwTicks.LowPart -= UMulDivRDClip(mmtTime.u.sample,
  1317. pds->ulTickRate,
  1318. pds->pdsbPrimary->pwfx->nSamplesPerSec);
  1319. }
  1320. else
  1321. {
  1322. ASSERT(mmtTime.wType == TIME_MS);
  1323. pds->qwTicks.LowPart -= UMulDivRDClip(mmtTime.u.ms,
  1324. pds->ulTickRate,
  1325. 1000);
  1326. }
  1327. DPF(3, "DSSetCurrentSample done");
  1328. return TRUE;
  1329. }
  1330. //---------------------------------------------------------------------------
  1331. //
  1332. // waveUnprepareLoopingBuffers
  1333. //
  1334. //---------------------------------------------------------------------------
  1335. MMRESULT waveUnprepareLoopingBuffers(LPDSOUND pds)
  1336. {
  1337. int i;
  1338. MMRESULT mmr;
  1339. for (i=0; i<NUMELMS(pds->aWaveHeader); i++) {
  1340. mmr = waveOutUnprepareHeader(pds->hwo, &pds->aWaveHeader[i], sizeof(pds->aWaveHeader[i]));
  1341. ASSERT(!mmr);
  1342. }
  1343. return MMSYSERR_NOERROR;
  1344. }
  1345. //---------------------------------------------------------------------------
  1346. //
  1347. // waveAllocLoopingBuffers
  1348. //
  1349. // Allocate the array of wavehdr's to point at the primary buffer.
  1350. // Prepare them and start them looping.
  1351. //
  1352. // !!! Add error checking to the waveOut calls !!!
  1353. //
  1354. //---------------------------------------------------------------------------
  1355. MMRESULT waveAllocAndPrepareLoopingBuffers
  1356. (
  1357. LPDSOUND pds
  1358. )
  1359. {
  1360. int iawh;
  1361. MMRESULT mmr;
  1362. WAVEOUTCAPS woc;
  1363. DPF(3, "waveAllocLoopingBuffers");
  1364. pds->pLoopingBuffer = MemAlloc(N_EMU_WAVE_HDRS * pds->cbDMASize);
  1365. if (!pds->pLoopingBuffer) {
  1366. RPF("No memory for looping buffer!");
  1367. return MMSYSERR_NOMEM;
  1368. }
  1369. // Initialize to 8 or 16-bit silence
  1370. FillMemory(pds->pLoopingBuffer, N_EMU_WAVE_HDRS * pds->cbDMASize,
  1371. (BYTE)((pds->pdsbPrimary->pwfx->wBitsPerSample == 8) ? 0x80 : 0x00));
  1372. // build buffers and headers
  1373. for (iawh = 0; iawh < NUMELMS(pds->aWaveHeader); ++iawh)
  1374. {
  1375. pds->aWaveHeader[iawh].lpData =
  1376. (LPBYTE)(pds->pLoopingBuffer + (iawh * (pds->cbDMASize)));
  1377. pds->aWaveHeader[iawh].dwBufferLength = pds->cbDMASize;
  1378. pds->aWaveHeader[iawh].dwUser = iawh;
  1379. pds->aWaveHeader[iawh].dwFlags = 0;
  1380. }
  1381. pds->iawhPlaying = 0;
  1382. DPF(5, "waveAllocLoopingBuffers: note: first waveOutPrepare and waveOutWrite");
  1383. mmr = waveOutPrepareHeader(pds->hwo, &pds->aWaveHeader[0], sizeof(pds->aWaveHeader[0]));
  1384. ASSERT(!mmr);
  1385. mmr = waveOutWrite(pds->hwo, &pds->aWaveHeader[0], sizeof(pds->aWaveHeader[0]));
  1386. if (!mmr) {
  1387. mmr = waveOutPause(pds->hwo);
  1388. ASSERT(!mmr);
  1389. // send down the rest
  1390. for (iawh = 1; iawh < NUMELMS(pds->aWaveHeader); ++iawh)
  1391. {
  1392. mmr = waveOutPrepareHeader(pds->hwo, &pds->aWaveHeader[iawh],
  1393. sizeof(pds->aWaveHeader[iawh]));
  1394. if (mmr) break;
  1395. mmr = waveOutWrite (pds->hwo, &pds->aWaveHeader[iawh],
  1396. sizeof(pds->aWaveHeader[iawh]));
  1397. if (mmr) break;
  1398. }
  1399. // BUGBUG We should have better error recovery here to reset the wave
  1400. // device and unprepare any headers that were successfully prepared
  1401. if (!mmr) {
  1402. DPF(5, "waveOutRestart");
  1403. // start the device
  1404. mmr = waveOutRestart (pds->hwo);
  1405. ASSERT(!mmr);
  1406. DPF(5, "waveOutGetDevCaps");
  1407. // Determine if we're sample accurate
  1408. //
  1409. mmr = waveOutGetDevCaps((UINT)pds->hwo, &woc, sizeof(woc));
  1410. pds->fdwInternal &= ~DS_INTERNALF_SAMPLEACCURATE;
  1411. if (MMSYSERR_NOERROR == mmr && (woc.dwSupport & WAVECAPS_SAMPLEACCURATE)) {
  1412. DPF(1, "Emulate: Running on sample accurate driver");
  1413. pds->fdwInternal |= DS_INTERNALF_SAMPLEACCURATE;
  1414. } else {
  1415. mmr = MMSYSERR_NOERROR;
  1416. DPF(1, "Emulate: Driver is NOT sample accurate!!!");
  1417. // Set up our own timing
  1418. if (!DSSetCurrentSample(pds))
  1419. mmr = MMSYSERR_ERROR;
  1420. }
  1421. pds->pdsbPrimary->pDSBuffer = pds->pLoopingBuffer;
  1422. pds->pdsbPrimary->cbBufferSize = N_EMU_WAVE_HDRS * pds->cbDMASize;
  1423. }
  1424. }
  1425. if (mmr) MemFree(pds->pLoopingBuffer);
  1426. DPF(3, "DSSetupLoopingBuffers done, mmr=%08lX", mmr);
  1427. return mmr;
  1428. }
  1429. //---------------------------------------------------------------------------
  1430. //
  1431. // waveThreadCallback
  1432. //
  1433. // This is a waveOutProc callback function. Its sole purpose is to
  1434. // increment a count of done headers and signal and event to waveThreadLoop
  1435. // that another header is done.
  1436. //
  1437. //---------------------------------------------------------------------------
  1438. VOID CALLBACK _loadds waveThreadCallback
  1439. (
  1440. HWAVE hwo,
  1441. UINT uMsg,
  1442. DWORD dwUser,
  1443. DWORD dwParam1,
  1444. DWORD dwParam2
  1445. )
  1446. {
  1447. LPDSOUND pds = (LPDSOUND)dwUser; // get our context
  1448. if ((MM_WOM_DONE == uMsg) && (pds->hEventWaveHeaderDone)) {
  1449. InterlockedIncrement(&pds->cwhDone);
  1450. SetEvent(pds->hEventWaveHeaderDone);
  1451. }
  1452. }
  1453. //---------------------------------------------------------------------------
  1454. //
  1455. // waveThreadLoop
  1456. //
  1457. // This function is responsible for continuously writing our wave headers
  1458. // to the wave device. It also calls the MixThreadCallback routine to
  1459. // mix more data into the wave headers.
  1460. //
  1461. // This function waits for a WaveHeaderDone event signalled by
  1462. // waveThreadCallback, which is a waveOutProc callback function. Upon
  1463. // receving the signal this function will write all done headers back to
  1464. // the wave device. Normally one header will be done on each signal. But
  1465. // there may be more in cases where more than one header finishex before this
  1466. // thread is scheduled.
  1467. //
  1468. // Once all done headers are rewritten to the wave device, the header
  1469. // following the last one written is considered to be the one currently
  1470. // playing. This header is called the "committed" header and an index to
  1471. // it is saved in pds->iawhPlaying.
  1472. //
  1473. // The count of done headers is maintained using the Interlocked APIs. The
  1474. // waveThreadCallback function will increment the count and this function will
  1475. // decrement it.
  1476. //
  1477. // This function will also react to a terminate event. This event is
  1478. // signalled during release of the DirectSound object. This loop will
  1479. // terminate and return to the waveThread function which will clean up
  1480. // and terminate.
  1481. //
  1482. //---------------------------------------------------------------------------
  1483. void waveThreadLoop(LPDSOUND pds, HANDLE hEventTerminate)
  1484. {
  1485. MMRESULT mmr;
  1486. while(TRUE) {
  1487. int cIterations;
  1488. BOOL fPaused;
  1489. DWORD dwResult;
  1490. LONG l;
  1491. LPWAVEHDR pwh;
  1492. HANDLE ah[2] = { hEventTerminate,
  1493. pds->hEventWaveHeaderDone };
  1494. // The first wait is for either a terminate or headerdone event.
  1495. // The second wait is for either a terminate or the DLL mutex.
  1496. dwResult = WaitForMultipleObjectsEx(2, ah, FALSE, INFINITE, FALSE);
  1497. if (WAIT_OBJECT_0 == dwResult) break;
  1498. ASSERT((WAIT_OBJECT_0 + 1) == dwResult);
  1499. cIterations = 0;
  1500. fPaused = FALSE;
  1501. l = InterlockedDecrement(&pds->cwhDone);
  1502. while (l >= 0) {
  1503. dwResult = ENTER_DLL_CSECT_OR_EVENT(hEventTerminate);
  1504. if (WAIT_OBJECT_0 == dwResult) break;
  1505. pwh = &pds->aWaveHeader[pds->iawhPlaying];
  1506. pds->iawhPlaying = (++pds->iawhPlaying) % NUMELMS(pds->aWaveHeader);
  1507. MixThreadCallback(pds);
  1508. LEAVE_DLL_CSECT();
  1509. // If it looks like we're spending all our time mixing, then
  1510. // let's pause the wave device until we catch up. It is very
  1511. // important that we eventually catch up. If we are always
  1512. // behind then this high priority thread might starve every
  1513. // other thread in the system.
  1514. cIterations++;
  1515. if (cIterations > NUMELMS(pds->aWaveHeader)) {
  1516. mmr = waveOutPause(pds->hwo);
  1517. ASSERT(!mmr);
  1518. fPaused = TRUE;
  1519. cIterations = 0;
  1520. }
  1521. mmr = waveOutWrite(pds->hwo, pwh, sizeof(*pwh));
  1522. ASSERT(!mmr);
  1523. l = InterlockedDecrement(&pds->cwhDone);
  1524. }
  1525. InterlockedIncrement(&pds->cwhDone);
  1526. if (fPaused) {
  1527. mmr = waveOutRestart(pds->hwo);
  1528. ASSERT(!mmr);
  1529. }
  1530. if (WAIT_OBJECT_0 == dwResult) break;
  1531. }
  1532. DPF(0, "waveThreadLoop: note: exiting");
  1533. return;
  1534. }
  1535. //---------------------------------------------------------------------------
  1536. //
  1537. // waveThread
  1538. //
  1539. // This thread proc initializes the wave device for ds emulation and then
  1540. // calls waveThreadLoop. See the waveThreadLoop comment header. Upon
  1541. // return from waveThreadLoop, this function will clean up and terminate.
  1542. //
  1543. //---------------------------------------------------------------------------
  1544. DWORD __stdcall waveThread
  1545. (
  1546. PVOID pThreadParms
  1547. )
  1548. {
  1549. LPDSOUND pds = (LPDSOUND)pThreadParms;
  1550. HANDLE hEventInitDone;
  1551. HANDLE hEventTerminate;
  1552. DWORD dwPriority;
  1553. DWORD dwVolume;
  1554. MMRESULT mmrInit;
  1555. MMRESULT mmr;
  1556. //
  1557. // mmrInit - holds the result code to be passed back to the creator
  1558. // via pds->mmrWaveThreadInit.
  1559. //
  1560. // mmr - a temp result code
  1561. //
  1562. DPF(0, "waveThread startup for pds=%08lX", pds);
  1563. ASSERT(NULL == pds->hwo);
  1564. hEventInitDone = CreateEvent(NULL, FALSE, FALSE, pds->szEventWaveThreadInitDone);
  1565. if (!hEventInitDone) {
  1566. DPF(0, "waveThread: error: couldn't create hEventInitDone");
  1567. return 0;
  1568. }
  1569. dwPriority = GetPriorityClass(GetCurrentProcess());
  1570. SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
  1571. mmrInit = waveOutOpen(&pds->hwo, pds->uDeviceID, pds->pdsbPrimary->pwfx,
  1572. (DWORD)waveThreadCallback, (DWORD)pds, CALLBACK_FUNCTION);
  1573. SetPriorityClass(GetCurrentProcess(), dwPriority);
  1574. if (!mmrInit) {
  1575. // Some mmsystem wave drivers will program their wave mixer
  1576. // hardware only while the device is open. By doing the
  1577. // following, we can get such drivers to program the hardware
  1578. if (MMSYSERR_NOERROR == waveOutGetVolume(pds->hwo, &dwVolume)) {
  1579. waveOutSetVolume(pds->hwo, dwVolume);
  1580. }
  1581. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
  1582. mmrInit = DSDetermineDMASize(pds, pds->pdsbPrimary->pwfx);
  1583. if (!mmrInit) {
  1584. ASSERT(NULL == pds->hEventWaveHeaderDone);
  1585. pds->cwhDone = 0;
  1586. pds->hEventWaveHeaderDone = CreateEvent(NULL, FALSE, FALSE, pds->szEventWaveHeaderDone);
  1587. if (!pds->hEventWaveHeaderDone) mmrInit = MMSYSERR_NOMEM;
  1588. if (!mmrInit) {
  1589. mmrInit = waveAllocAndPrepareLoopingBuffers(pds);
  1590. if (!mmrInit) {
  1591. hEventTerminate = CreateEvent(NULL, FALSE, FALSE, pds->szEventTerminateWaveThread);
  1592. if (!hEventTerminate) mmrInit = MMSYSERR_NOMEM;
  1593. if (!mmrInit) {
  1594. // Signal that we're finished with initialization.
  1595. // mmrInit should not be modified below this point.
  1596. pds->mmrWaveThreadInit = mmrInit;
  1597. SetEvent(hEventInitDone);
  1598. waveThreadLoop(pds, hEventTerminate);
  1599. mmr = waveOutReset(pds->hwo);
  1600. ASSERT(!mmr);
  1601. CloseHandle(hEventTerminate);
  1602. }
  1603. waveUnprepareLoopingBuffers(pds);
  1604. }
  1605. CloseHandle(pds->hEventWaveHeaderDone);
  1606. pds->hEventWaveHeaderDone = NULL;
  1607. }
  1608. }
  1609. mmr = waveOutClose(pds->hwo);
  1610. pds->hwo = NULL;
  1611. ASSERT(!mmr);
  1612. }
  1613. // If init failed, set the result code and signal init done.
  1614. if (mmrInit) {
  1615. pds->mmrWaveThreadInit = mmrInit;
  1616. SetEvent(hEventInitDone);
  1617. }
  1618. CloseHandle(hEventInitDone);
  1619. return 0;
  1620. }
  1621. MMRESULT FNGLOBAL DSInitializeEmulator
  1622. (
  1623. LPDSOUND pds
  1624. )
  1625. {
  1626. DWORD dwResult;
  1627. HANDLE hEventInitDone;
  1628. MMRESULT mmr;
  1629. DPF(3, "DSInitializeEmulator");
  1630. ASSERT(pds->pdsbPrimary);
  1631. ASSERT(SIZEOF(pds->szEventWaveHeaderDone) >= 7+8+8+1);
  1632. wsprintf(pds->szEventWaveHeaderDone, "DS-EWHD%08lX%08lX", GetCurrentProcessId(), pds);
  1633. ASSERT(SIZEOF(pds->szEventWaveThreadInitDone) >= 8+8+8+1);
  1634. wsprintf(pds->szEventWaveThreadInitDone, "DS-EWTID%08lX%08lX", GetCurrentProcessId(), pds);
  1635. ASSERT(SIZEOF(pds->szEventTerminateWaveThread) >= 7+8+8+1);
  1636. wsprintf(pds->szEventTerminateWaveThread, "DS-ETWT%08lX%08lX", GetCurrentProcessId(), pds);
  1637. hEventInitDone = CreateEvent(NULL, FALSE, FALSE, pds->szEventWaveThreadInitDone);
  1638. if (!hEventInitDone) return MMSYSERR_NOMEM;
  1639. // side effects begin (hEventInitDone created)
  1640. // hWaveThread is the thread which recycles wave buffers
  1641. pds->hWaveThread = HelperCreateDSMixerThread(waveThread, pds, 0, NULL);
  1642. mmr = (pds->hWaveThread) ? MMSYSERR_NOERROR : MMSYSERR_NOMEM;
  1643. if (!mmr) {
  1644. DPF(0, "waveThread handle is %08lX", (LONG)pds->hWaveThread);
  1645. dwResult = WaitForSingleObjectEx(hEventInitDone, INFINITE, FALSE);
  1646. ASSERT(WAIT_OBJECT_0 == dwResult);
  1647. mmr = pds->mmrWaveThreadInit;
  1648. if (mmr) {
  1649. HANDLE hHelper;
  1650. HANDLE hWaveThreadOurs;
  1651. // Something went wrong. Clean up.
  1652. // Note that hWaveThread is relative to the helper process.
  1653. hHelper = OpenProcess(PROCESS_DUP_HANDLE, FALSE, gpdsinfo->pidHelper);
  1654. if (hHelper) {
  1655. if (DuplicateHandle(hHelper, pds->hWaveThread,
  1656. GetCurrentProcess(), &hWaveThreadOurs,
  1657. SYNCHRONIZE | THREAD_TERMINATE,
  1658. FALSE, DUPLICATE_CLOSE_SOURCE))
  1659. {
  1660. dwResult = WaitForSingleObjectEx(hWaveThreadOurs, INFINITE, FALSE);
  1661. ASSERT(WAIT_OBJECT_0 == dwResult);
  1662. dwResult = CloseHandle(hWaveThreadOurs);
  1663. ASSERT(dwResult);
  1664. }
  1665. dwResult = CloseHandle(hHelper);
  1666. ASSERT(dwResult);
  1667. }
  1668. pds->hWaveThread = NULL;
  1669. }
  1670. }
  1671. dwResult = CloseHandle(hEventInitDone);
  1672. ASSERT(dwResult);
  1673. return mmr;
  1674. }
  1675. MMRESULT FNGLOBAL DSShutdownEmulator
  1676. (
  1677. LPDSOUND pds
  1678. )
  1679. {
  1680. HANDLE hEventTerminate;
  1681. HANDLE hHelper;
  1682. HANDLE hWaveThreadOurs;
  1683. DWORD dwResult;
  1684. DPF(0, " !!! About to shutdown emulator !!!");
  1685. ASSERT(pds->hWaveThread);
  1686. // Signal wave thread to go away.
  1687. hEventTerminate = CreateEvent(NULL, FALSE, FALSE, pds->szEventTerminateWaveThread);
  1688. if (hEventTerminate) {
  1689. SetEvent(hEventTerminate);
  1690. CloseHandle(hEventTerminate);
  1691. hEventTerminate = NULL;
  1692. }
  1693. DPF(0, "Emulator: Wait for callback thread to die");
  1694. hHelper = OpenProcess(PROCESS_DUP_HANDLE, FALSE, gpdsinfo->pidHelper);
  1695. if (hHelper) {
  1696. if (DuplicateHandle(hHelper, pds->hWaveThread, GetCurrentProcess(),
  1697. &hWaveThreadOurs, SYNCHRONIZE | THREAD_TERMINATE,
  1698. FALSE, DUPLICATE_CLOSE_SOURCE))
  1699. {
  1700. dwResult = WaitForSingleObjectEx(hWaveThreadOurs, INFINITE, FALSE);
  1701. ASSERT(dwResult == WAIT_OBJECT_0);
  1702. dwResult = CloseHandle(hWaveThreadOurs);
  1703. ASSERT(dwResult);
  1704. }
  1705. dwResult = CloseHandle(hHelper);
  1706. ASSERT(dwResult);
  1707. } else {
  1708. DPF(0, "Emulator: couldn't open handle on helper");
  1709. }
  1710. ASSERT(NULL == pds->hwo); // waveThread should do this if
  1711. pds->hwo = NULL; // it terminates normally
  1712. pds->hWaveThread = NULL;
  1713. return MMSYSERR_NOERROR;
  1714. }
  1715. HRESULT CreateNewDirectSoundObject
  1716. (LPDIRECTSOUND *ppDS,
  1717. IUnknown *pUnkOuter)
  1718. {
  1719. LPDSOUNDEXTERNAL pdse;
  1720. DPF(2, "DSound: CreateNewDirectSoundObject: Function Enter");
  1721. /* Argument Validation */
  1722. *ppDS = NULL;
  1723. if( pUnkOuter != NULL ) {
  1724. RPF("DSound: Direct Sound does not support aggregation.");
  1725. return CLASS_E_NOAGGREGATION;
  1726. }
  1727. /* Object Allocation */
  1728. pdse = (LPDSOUNDEXTERNAL)MemAlloc(sizeof(DSOUNDEXTERNAL));
  1729. if (NULL == pdse) {
  1730. RPF("DSound: Direct Sound Object memory allocation failed.");
  1731. return E_OUTOFMEMORY;
  1732. }
  1733. /* Initialize Data Members */
  1734. pdse->lpVtbl = gpdsinfo->lpVtblDS;
  1735. /* set to NULL to signal no */
  1736. pdse->fInitialized = IDSHWINITIALIZEF_UNINITIALIZED;
  1737. /* initialization */
  1738. pdse->uRefCount = 1;
  1739. *ppDS = (LPDIRECTSOUND)pdse;
  1740. DPF(2, "DSound: CreateNewDirectSoundObject: Successful Function Exit");
  1741. return S_OK;
  1742. }