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.

449 lines
12 KiB

  1. #include <windows.h>
  2. #define MMNOTIMER
  3. #define MMNOSEQ
  4. #define MMNOJOY
  5. #define MMNOMIDI
  6. #define MMNOMCI
  7. #include "mmsystem.h"
  8. #include "mmsysi.h" // to get DOUT() and _hread()
  9. #include "playwav.h"
  10. //
  11. // These globals are used to keep track of the currently playing sound, and
  12. // the handle to the wave device. only 1 sound can be playing at a time.
  13. //
  14. static HWAVEOUT hWaveOut; // handle to open wave device
  15. LPWAVEHDR lpWavHdr; // current wave file playing
  16. /* flags for _lseek */
  17. #define SEEK_CUR 1
  18. #define SEEK_END 2
  19. #define SEEK_SET 0
  20. #define FMEM (GMEM_MOVEABLE|GMEM_SHARE)
  21. BOOL NEAR PASCAL soundInitWavHdr(LPWAVEHDR lpwh, LPCSTR lpMem, DWORD dwLen);
  22. BOOL NEAR PASCAL soundOpen(HGLOBAL hSound, UINT wFlags);
  23. BOOL NEAR PASCAL soundClose(void);
  24. void NEAR PASCAL soundWait(void);
  25. /*****************************************************************************
  26. * @doc INTERNAL
  27. *
  28. * @api void | WaveOutNotify | called by mmWndProc when it recives a
  29. * MM_WOM_DONE message
  30. * @rdesc None.
  31. *
  32. ****************************************************************************/
  33. void FAR PASCAL WaveOutNotify(WPARAM wParam, LPARAM lParam)
  34. {
  35. if (hWaveOut && !(lpWavHdr->dwFlags & WHDR_DONE))
  36. return; // wave is not done! get out
  37. //
  38. // wave file is done! release the device
  39. //
  40. DOUT("MMSYSTEM: ASYNC sound done, closing wave device\r\n");
  41. soundClose();
  42. }
  43. /*****************************************************************************
  44. * @doc INTERNAL
  45. *
  46. * @api BOOL | soundPlay | Pretty much speaks for itself!
  47. *
  48. * @parm HGLOBAL | hSound | The sound resource to play.
  49. *
  50. * @parm wFlags | UINT | flags controlling sync/async etc.
  51. *
  52. * @flag SND_SYNC | play synchronously (default)
  53. * @flag SND_ASYNC | play asynchronously
  54. *
  55. * @rdesc Returns TRUE if successful and FALSE on failure.
  56. ****************************************************************************/
  57. BOOL NEAR PASCAL soundPlay(HGLOBAL hSound, UINT wFlags)
  58. {
  59. //
  60. // Before playing a sound release it
  61. //
  62. soundClose();
  63. //
  64. // open the sound device and write the sound to it.
  65. //
  66. if (!soundOpen(hSound, wFlags))
  67. return FALSE;
  68. if (!(wFlags & SND_ASYNC))
  69. {
  70. soundWait();
  71. soundClose();
  72. }
  73. return TRUE;
  74. }
  75. /*****************************************************************************
  76. * @doc INTERNAL
  77. *
  78. * @api BOOL | soundOpen | Open the wave device and write a sound to it.
  79. *
  80. * @parm HGLOBAL | hSound | The sound resource to play.
  81. *
  82. * @rdesc Returns TRUE if successful and FALSE on failure.
  83. ****************************************************************************/
  84. BOOL NEAR PASCAL soundOpen(HGLOBAL hSound, UINT wFlags)
  85. {
  86. UINT wErr;
  87. if (!hSound || !hwndNotify)
  88. return FALSE;
  89. if (hWaveOut)
  90. {
  91. DOUT("MMSYSTEM: soundOpen() wave device is currently open.\r\n");
  92. return FALSE;
  93. }
  94. lpWavHdr = (LPWAVEHDR)GlobalLock(hSound);
  95. if (!lpWavHdr)
  96. {
  97. #ifdef DEBUG
  98. if ((GlobalFlags(hSound) & GMEM_DISCARDED))
  99. DOUT("MMSYSTEM: sound was discarded before play could begin.\r\n");
  100. #endif
  101. return FALSE;
  102. }
  103. //
  104. // open the wave device, open any wave device that supports the
  105. // format
  106. //
  107. wErr = waveOutOpen(&hWaveOut, // returns handle to device
  108. (UINT)WAVE_MAPPER, // device id (any device)
  109. (LPWAVEFORMAT)lpWavHdr->dwUser, // wave format
  110. (DWORD)(UINT)hwndNotify, // callback function
  111. 0L, // callback instance data
  112. WAVE_ALLOWSYNC | CALLBACK_WINDOW); // flags
  113. if (wErr != 0)
  114. {
  115. DOUT("MMSYSTEM: soundOpen() unable to open wave device\r\n");
  116. GlobalUnlock(hSound);
  117. lpWavHdr = NULL;
  118. hWaveOut = NULL;
  119. return FALSE;
  120. }
  121. wErr = waveOutPrepareHeader(hWaveOut, lpWavHdr, sizeof(WAVEHDR));
  122. if (wErr != 0)
  123. {
  124. DOUT("MMSYSTEM: soundOpen() waveOutPrepare failed\r\n");
  125. soundClose();
  126. return FALSE;
  127. }
  128. //
  129. // Only allow sound looping if playing ASYNC sounds
  130. //
  131. if ((wFlags & SND_ASYNC) && (wFlags & SND_LOOP))
  132. {
  133. lpWavHdr->dwLoops = 0xFFFFFFFF; // infinite loop
  134. lpWavHdr->dwFlags |= WHDR_BEGINLOOP|WHDR_ENDLOOP;
  135. }
  136. else
  137. {
  138. lpWavHdr->dwLoops = 0;
  139. lpWavHdr->dwFlags &=~(WHDR_BEGINLOOP|WHDR_ENDLOOP);
  140. }
  141. lpWavHdr->dwFlags &= ~WHDR_DONE; // mark as not done!
  142. wErr = waveOutWrite(hWaveOut, lpWavHdr, sizeof(WAVEHDR));
  143. if (wErr != 0)
  144. {
  145. DOUT("MMSYSTEM: soundOpen() waveOutWrite failed\r\n");
  146. soundClose();
  147. return FALSE;
  148. }
  149. return TRUE;
  150. }
  151. /*****************************************************************************
  152. * @doc INTERNAL
  153. *
  154. * @func BOOL | soundClose | This function closes the sound device
  155. *
  156. * @rdesc Returns TRUE if successful and FALSE on failure.
  157. ****************************************************************************/
  158. BOOL NEAR PASCAL soundClose(void)
  159. {
  160. UINT wErr;
  161. //
  162. // Do we have the sound device open?
  163. //
  164. if (!lpWavHdr || !hWaveOut)
  165. return TRUE;
  166. //
  167. // if the block is still playing, stop it!
  168. //
  169. if (!(lpWavHdr->dwFlags & WHDR_DONE))
  170. waveOutReset(hWaveOut);
  171. #ifdef DEBUG
  172. if (!(lpWavHdr->dwFlags & WHDR_DONE))
  173. {
  174. DOUT("MMSYSTEM: soundClose() data is not DONE!???\r\n");
  175. lpWavHdr->dwFlags |= WHDR_DONE;
  176. }
  177. if (!(lpWavHdr->dwFlags & WHDR_PREPARED))
  178. {
  179. DOUT("MMSYSTEM: soundClose() data not prepared???\r\n");
  180. }
  181. #endif
  182. //
  183. // unprepare the data anyway!
  184. //
  185. wErr = waveOutUnprepareHeader(hWaveOut, lpWavHdr, sizeof(WAVEHDR));
  186. if (wErr != 0)
  187. {
  188. DOUT("MMSYSTEM: soundClose() waveOutUnprepare failed?\r\n");
  189. }
  190. //
  191. // finaly actually close the device, and unlock the data
  192. //
  193. waveOutClose(hWaveOut);
  194. GlobalUnlock((HGLOBAL)HIWORD(lpWavHdr));
  195. //
  196. // update globals, claiming the device is closed.
  197. //
  198. hWaveOut = NULL;
  199. lpWavHdr = NULL;
  200. return TRUE;
  201. }
  202. /*****************************************************************************
  203. * @doc INTERNAL
  204. *
  205. * @api void | soundWait | wait for the sound device to complete
  206. *
  207. * @rdesc none
  208. ****************************************************************************/
  209. void NEAR PASCAL soundWait(void)
  210. {
  211. if (lpWavHdr)
  212. while (!(lpWavHdr->dwFlags & WHDR_DONE))
  213. ;
  214. }
  215. /*****************************************************************************
  216. * @doc INTERNAL
  217. *
  218. * @api void | soundFree | This function frees a sound resource created
  219. * with soundLoadFile or soundLoadMemory
  220. *
  221. * @rdesc Returns TRUE if successful and FALSE on failure.
  222. ****************************************************************************/
  223. void NEAR PASCAL soundFree(HGLOBAL hSound)
  224. {
  225. if (!hSound)
  226. return;
  227. // !!! we should only close the sound device iff this hSound is playing!
  228. //
  229. soundClose();
  230. GlobalFree(hSound);
  231. }
  232. /*****************************************************************************
  233. * @doc INTERNAL
  234. *
  235. * @api HGLOBAL | soundLoadFile | Loads a specified sound resource from a
  236. * file into a global, discardable object.
  237. *
  238. * @parm LPCSTR | lpszFile | The file from which to load the sound resource.
  239. *
  240. * @rdesc Returns NULL on failure, GLOBAL HANDLE to a WAVEHDR iff success
  241. ****************************************************************************/
  242. HGLOBAL NEAR PASCAL soundLoadFile(LPCSTR szFileName)
  243. {
  244. HFILE fh;
  245. OFSTRUCT of;
  246. DWORD dwSize;
  247. LPSTR lpData;
  248. HGLOBAL h;
  249. UINT wNameLen;
  250. // open the file
  251. fh = OpenFile(szFileName, &of, OF_READ | OF_SHARE_DENY_NONE);
  252. if (fh == HFILE_ERROR)
  253. return NULL;
  254. wNameLen = lstrlen(szFileName) + 1;
  255. dwSize = _llseek(fh, 0l, SEEK_END); // get the size of file
  256. _llseek(fh, 0l, SEEK_SET); // seek back to the start
  257. // allocate some discardable memory for a wave hdr, name and the file data.
  258. h = GlobalAlloc(FMEM+GMEM_DISCARDABLE, sizeof(WAVEHDR) + wNameLen + dwSize);
  259. if (!h)
  260. goto error1;
  261. // lock it down
  262. lpData = GlobalLock(h);
  263. // read the file into the memory block
  264. if (_hread(fh,lpData+sizeof(WAVEHDR)+wNameLen,(LONG)dwSize) != (LONG)dwSize)
  265. goto error3;
  266. // do the rest of it from the memory image
  267. if (!soundInitWavHdr((LPWAVEHDR)lpData, lpData+sizeof(WAVEHDR)+wNameLen, dwSize))
  268. goto error3;
  269. _lclose(fh);
  270. lstrcpy(lpData+sizeof(WAVEHDR), szFileName);
  271. GlobalUnlock(h);
  272. return h;
  273. error3:
  274. GlobalUnlock(h);
  275. GlobalFree(h);
  276. error1:
  277. _lclose(fh);
  278. return NULL;
  279. }
  280. /*****************************************************************************
  281. * @doc INTERNAL
  282. *
  283. * @api HGLOBAL | soundLoadMemory | Loads a user specified sound resource from a
  284. * a memory block supplied by the caller.
  285. *
  286. * @parm LPCSTR | lpMem | Pointer to a memory image of the file
  287. *
  288. * @rdesc Returns NULL on failure, GLOBAL HANDLE to a WAVEHDR iff success
  289. ****************************************************************************/
  290. HGLOBAL NEAR PASCAL soundLoadMemory(LPCSTR lpMem)
  291. {
  292. HGLOBAL h;
  293. LPSTR lp;
  294. // allocate some memory, for a wave hdr
  295. h = GlobalAlloc(FMEM, (LONG)sizeof(WAVEHDR)+1);
  296. if (!h)
  297. goto error1;
  298. // lock it down
  299. lp = GlobalLock(h);
  300. //
  301. // we must assume the memory pointer is correct! (hence the -1l)
  302. //
  303. if (!soundInitWavHdr((LPWAVEHDR)lp, lpMem, (DWORD)-1l))
  304. goto error3;
  305. lp[sizeof(WAVEHDR)] = (char)0; // No file name for memory file
  306. GlobalUnlock(h);
  307. return h;
  308. error3:
  309. GlobalUnlock(h);
  310. GlobalFree(h);
  311. error1:
  312. return NULL;
  313. }
  314. /*****************************************************************************
  315. * @doc INTERNAL
  316. *
  317. * @api BOOL | soundInitWavHdr | Initializes a WAVEHDR data structure from a
  318. * pointer to a memory image of a RIFF WAV file.
  319. *
  320. * @parm LPWAVHDR | lpwh | Pointer to a WAVEHDR
  321. *
  322. * @parm LPCSTR | lpMem | Pointer to a memory image of a RIFF WAV file
  323. *
  324. * @rdesc Returns FALSE on failure, TRUE on success.
  325. *
  326. * @comm the dwUser field of the WAVEHDR structure is initialized to point
  327. * to the WAVEFORMAT structure that is inside the RIFF data
  328. *
  329. ****************************************************************************/
  330. BOOL NEAR PASCAL soundInitWavHdr(LPWAVEHDR lpwh, LPCSTR lpMem, DWORD dwLen)
  331. {
  332. FPFileHeader fpHead;
  333. LPWAVEFORMAT lpFmt;
  334. LPCSTR lpData;
  335. DWORD dwFileSize,dwCurPos;
  336. DWORD dwSize;
  337. if (dwLen < sizeof(FileHeader))
  338. return FALSE;
  339. // assume the first few bytes are the file header
  340. fpHead = (FPFileHeader) lpMem;
  341. // check that it's a valid RIFF file and a valid WAVE form.
  342. if (fpHead->dwRiff != RIFF_FILE || fpHead->dwWave != RIFF_WAVE ) {
  343. return FALSE;
  344. }
  345. dwFileSize = fpHead->dwSize;
  346. dwCurPos = sizeof(FileHeader);
  347. lpData = lpMem + sizeof(FileHeader);
  348. if (dwLen < dwFileSize) // RIFF header
  349. return FALSE;
  350. // scan until we find the 'fmt' chunk
  351. while( 1 ) {
  352. if( ((FPChunkHeader)lpData)->dwCKID == RIFF_FORMAT )
  353. break; // from the while loop that's looking for it
  354. dwCurPos += ((FPChunkHeader)lpData)->dwSize + sizeof(ChunkHeader);
  355. if( dwCurPos >= dwFileSize )
  356. return FALSE;
  357. lpData += ((FPChunkHeader)lpData)->dwSize + sizeof(ChunkHeader);
  358. }
  359. // now we're at the beginning of the 'fmt' chunk data
  360. lpFmt = (LPWAVEFORMAT) (lpData + sizeof(ChunkHeader));
  361. // scan until we find the 'data' chunk
  362. lpData = lpData + ((FPChunkHeader)lpData)->dwSize + sizeof(ChunkHeader);
  363. while( 1 ) {
  364. if( ((FPChunkHeader)lpData)->dwCKID == RIFF_CHANNEL)
  365. break; // from the while loop that's looking for it
  366. dwCurPos += ((FPChunkHeader)lpData)->dwSize + sizeof(ChunkHeader);
  367. if( dwCurPos >= dwFileSize )
  368. return NULL;
  369. lpData += ((FPChunkHeader)lpData)->dwSize + sizeof(ChunkHeader);
  370. }
  371. // now we're at the beginning of the 'data' chunk data
  372. dwSize = ((FPChunkHeader)lpData)->dwSize;
  373. lpData = lpData + sizeof(ChunkHeader);
  374. // initialize the WAVEHDR
  375. lpwh->lpData = (LPSTR)lpData; // pointer to locked data buffer
  376. lpwh->dwBufferLength = dwSize; // length of data buffer
  377. lpwh->dwUser = (DWORD)lpFmt; // for client's use
  378. lpwh->dwFlags = WHDR_DONE; // assorted flags (see defines)
  379. lpwh->dwLoops = 0;
  380. return TRUE;
  381. }