Leaked source code of windows server 2003
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.

345 lines
12 KiB

  1. /************************************************************************/
  2. /*
  3. ** Copyright (c) 1985-1998 Microsoft Corporation
  4. **
  5. ** Title: mwplay.c - Multimedia Systems Media Control Interface
  6. ** waveform digital audio driver for RIFF wave files.
  7. ** Routines for playing wave files
  8. **
  9. ** Version: 1.00
  10. **
  11. ** Date: 18-Apr-1990
  12. **
  13. ** Author: ROBWI
  14. */
  15. /************************************************************************/
  16. /*
  17. ** Change log:
  18. **
  19. ** DATE REV DESCRIPTION
  20. ** ----------- ----- ------------------------------------------
  21. ** 18-APR-1990 ROBWI Original
  22. ** 19-JUN-1990 ROBWI Added wave in
  23. ** 10-Jan-1992 MikeTri Ported to NT
  24. ** @@@ Need to change comments from slash slash to slash star
  25. ** 4-Mar-1992 SteveDav Continue the port. Update to current Win 3.1
  26. */
  27. /************************************************************************/
  28. #define UNICODE
  29. #define NOGDICAPMASKS
  30. #define NOVIRTUALKEYCODES
  31. #define NOWINSTYLES
  32. #define NOSYSMETRICS
  33. #define NOMENUS
  34. #define NOICONS
  35. #define NOKEYSTATES
  36. #define NOSYSCOMMANDS
  37. #define NORASTEROPS
  38. #define NOSHOWWINDOW
  39. #define OEMRESOURCE
  40. #define NOATOM
  41. #define NOCLIPBOARD
  42. #define NOCOLOR
  43. #define NOCTLMGR
  44. #define NODRAWTEXT
  45. #define NOGDI
  46. #define NOKERNEL
  47. #define NONLS
  48. #define NOMB
  49. #define NOMEMMGR
  50. #define NOMETAFILE
  51. #define NOOPENFILE
  52. #define NOSCROLL
  53. #define NOTEXTMETRIC
  54. #define NOWH
  55. #define NOWINOFFSETS
  56. #define NOCOMM
  57. #define NOKANJI
  58. #define NOHELP
  59. #define NOPROFILER
  60. #define NODEFERWINDOWPOS
  61. #include <windows.h>
  62. #include "mciwave.h"
  63. #include <mmddk.h>
  64. /************************************************************************/
  65. /*
  66. @doc INTERNAL MCIWAVE
  67. @func DWORD | mwRead |
  68. This function reads a buffer of wave data from either the input file,
  69. or the temporary data file. The position is taken from the
  70. <e>WAVEDESC.dCur<d> pointer, which is updated with the number of bytes
  71. actually read.
  72. The data needed may come from several consecutively linked nodes, so
  73. first the virtual data ending position for the current wave data node
  74. is checked against the the current position. This is to determine if
  75. the next node needs to be accessed. The function then reads the data
  76. from the appropriate source, either the temporary data file, or the
  77. original file.
  78. @parm <t>PWAVEDESC<d> | pwd |
  79. Pointer to the wave device descriptor.
  80. @parm LPBYTE | lpbBuffer |
  81. Points to a buffer to contain the data read.
  82. @parm DWORD | dBufferLength |
  83. Indicates the maximum number of bytes to read into the buffer.
  84. @rdesc Returns number of bytes read, else 0 on an error wherein no bytes
  85. could be read. This means that there is not distinction between a
  86. read of zero bytes, or an error, but the function is never called
  87. if no bytes are to be read.
  88. */
  89. PRIVATE DWORD PASCAL NEAR mwRead(
  90. PWAVEDESC pwd,
  91. LPBYTE lpbBuffer,
  92. DWORD dBufferLength)
  93. {
  94. DWORD dTotalRead;
  95. LPWAVEDATANODE lpwdn;
  96. lpwdn = LPWDN(pwd, pwd->dWaveDataCurrentNode);
  97. for (dTotalRead = 0; dBufferLength;) {
  98. DWORD dStartRead;
  99. DWORD dReadSize;
  100. DWORD dBytesRead;
  101. if (pwd->dVirtualWaveDataStart + lpwdn->dDataLength <= (DWORD)pwd->dCur) {
  102. pwd->dWaveDataCurrentNode = lpwdn->dNextWaveDataNode;
  103. pwd->dVirtualWaveDataStart += lpwdn->dDataLength;
  104. lpwdn = LPWDN(pwd, lpwdn->dNextWaveDataNode);
  105. }
  106. dStartRead = pwd->dCur - pwd->dVirtualWaveDataStart;
  107. dReadSize = min(dBufferLength, lpwdn->dDataLength - dStartRead);
  108. if (ISTEMPDATA(lpwdn)) {
  109. if (MySeekFile(pwd->hTempBuffers, UNMASKDATASTART(lpwdn) + dStartRead))
  110. MyReadFile(pwd->hTempBuffers, lpbBuffer, dReadSize, &dBytesRead);
  111. else
  112. dBytesRead = (DWORD)-1;
  113. } else {
  114. if (mmioSeek(pwd->hmmio, pwd->dRiffData + lpwdn->dDataStart + dStartRead, SEEK_SET) != -1)
  115. dBytesRead = (DWORD)mmioRead(pwd->hmmio, lpbBuffer, (LONG)dReadSize);
  116. else
  117. dBytesRead = (DWORD)-1;
  118. }
  119. if (dBytesRead != -1) {
  120. dTotalRead += dBytesRead;
  121. dBufferLength -= dBytesRead;
  122. lpbBuffer += dBytesRead;
  123. pwd->dCur += dBytesRead;
  124. }
  125. if (dBytesRead != dReadSize) {
  126. pwd->wTaskError = MCIERR_FILE_READ;
  127. break;
  128. }
  129. }
  130. return dTotalRead;
  131. }
  132. /************************************************************************/
  133. /*
  134. @doc INTERNAL MCIWAVE
  135. @func BOOL | CheckNewCommand |
  136. This function is called when a New command flag is found during the
  137. playback loop. It determines if the new commands affect current
  138. playback enough that it must be terminated. This can happen if either
  139. a Stop command is received, or a Cue command is received and an error
  140. occurs while pausing the output wave device.
  141. Any other playback change does not need to stop current playback, as
  142. they should just release all the buffers from the wave device before
  143. setting the command.
  144. @parm <t>PWAVEDESC<d> | pwd |
  145. Pointer to the wave device descriptor.
  146. @rdesc Returns TRUE if the new commands do not affect playback and it should
  147. continue, else FALSE if the new commands affect the playback, and it
  148. should be aborted.
  149. */
  150. REALLYPRIVATE BOOL PASCAL NEAR CheckNewCommand(
  151. PWAVEDESC pwd)
  152. {
  153. if (ISMODE(pwd, COMMAND_STOP))
  154. return FALSE;
  155. if (ISMODE(pwd, COMMAND_CUE)
  156. && (0 != (pwd->wTaskError = waveOutPause(pwd->hWaveOut))))
  157. return FALSE;
  158. REMOVEMODE(pwd, COMMAND_NEW);
  159. return TRUE;
  160. }
  161. /************************************************************************/
  162. /*
  163. @doc INTERNAL MCIWAVE
  164. @func VOID | HoldPlayback |
  165. This function blocks the task, waiting to be signalled that it can
  166. continue from the Hold command. Since the Play Hold command is
  167. considered to be "finished" when playback is done, but before any
  168. buffers are freed, the optional notification is performed here. When
  169. the task is signalled, it can then check for new commands, which may
  170. continue playback, or exit the playback loop.
  171. @parm <t>PWAVEDESC<d> | pwd |
  172. Pointer to the wave device descriptor.
  173. @rdesc Nothing.
  174. */
  175. PRIVATE VOID PASCAL NEAR HoldPlayback(
  176. PWAVEDESC pwd)
  177. {
  178. ADDMODE(pwd, MODE_HOLDING);
  179. mwDelayedNotify(pwd, MCI_NOTIFY_SUCCESSFUL);
  180. while (TaskBlock() != WTM_STATECHANGE);
  181. }
  182. /************************************************************************/
  183. /*
  184. @doc INTERNAL MCIWAVE
  185. @func UINT | PlayFile |
  186. This function is used to Cue or Play a wave file. The function
  187. basically reads buffers from the wave file, and sends them out to the
  188. wave device, blocking for each buffer sent. It also makes sure to
  189. call <f>mmTaskYield<d> while both reading in new buffers, and waiting
  190. for buffers to be released.
  191. Within the playback loop, the function first checks for the new command
  192. flag, which can possibly interrupt or change the current playback.
  193. The only thing that can really make a difference is setting the stop
  194. flag. Changing the playback TO and FROM positions should not affect
  195. the loop, and setting the Cue command only pauses the output of the
  196. wave device.
  197. When the playback loop is first entered, the New command flag is
  198. set, and this condition is entered. This allows the Cue command to
  199. be sent with the Play command, and initially pause the wave output
  200. device. Calling <f>waveOutPause<d> stops any data from going out the
  201. DACs but, but still allows all the buffers to be queued up.
  202. After checking for a new command, the loop checks to see if there is
  203. any more data to play from the wave file, and if there are any empty
  204. buffers to read it in to. If so, that data is read and written to the
  205. wave device, with the appropriate error checking, the in-use buffer
  206. count in incremented, and a pointer to the next data buffer to use is
  207. retrieved.
  208. After checking for more data to play, there is a check to see if any
  209. more buffers are outstanding. If so, the task blocks until a buffer
  210. is released by the wave device. Normally during the end of playback,
  211. this condition is performed for each outstanding buffer until all
  212. buffers have been released, then the function would enter the default
  213. condition and fall out of the loop. It just blocks the task, waiting
  214. for the wave device to signal this task after releasing the buffer,
  215. and the in-use buffer count is decremented. Note that since the task
  216. blocked itself, a new command could have been sent, so the playback
  217. loop starts again after doing a task yield, as it does after each of
  218. conditional parts of the playback loop.
  219. Before blocking the Cue command must be checked for in order to
  220. determine if the optional notification should be sent. This is
  221. because a Cue Output command is considered "finished" when the buffers
  222. have been filled.
  223. After all playback buffers have been released by the wave device, if
  224. the hold command was given with the current play command, the task is
  225. blocked (and thus does not release the memory used by the playback
  226. buffers, nor leave the playback loop), waiting for a signal, which
  227. may stop or continue playback with a new set of parameters.
  228. The final default condition occurs when all the data has been read,
  229. all the buffers have been released, and the hold flag was not set.
  230. In this case, playback is done, and the playback loop is exited.
  231. @parm <t>PWAVEDESC<d> | pwd |
  232. Pointer to the wave device descriptor.
  233. @rdesc Returns the number of outstanding buffers written to the wave device.
  234. This can be used when removing task signal from the message queue.
  235. In cases of error, the <e>WAVEDESC.wTaskError<d> flag is set. This
  236. specific error is not currently returned, as the calling task may not
  237. have waited for the command to complete. But it is at least used for
  238. notification in order to determine if Failure status should be sent.
  239. @xref RecordFile.
  240. */
  241. PUBLIC UINT PASCAL FAR PlayFile(
  242. register PWAVEDESC pwd)
  243. {
  244. LPWAVEHDR *lplpWaveHdr;
  245. register UINT wBuffersOutstanding;
  246. ADDMODE(pwd, MODE_PLAYING);
  247. for (wBuffersOutstanding = 0, lplpWaveHdr = pwd->rglpWaveHdr;;) {
  248. if (ISMODE(pwd, COMMAND_NEW) && !CheckNewCommand(pwd))
  249. break;
  250. if ((wBuffersOutstanding < pwd->wAudioBuffers) && (pwd->dCur < pwd->dTo)) {
  251. if (!((*lplpWaveHdr)->dwFlags & WHDR_DONE)) {
  252. #if DBG
  253. dprintf1(("\nMCIWAVE Buffer not complete ! %8X", *lplpWaveHdr));
  254. DebugBreak();
  255. #endif
  256. }
  257. if (!((*lplpWaveHdr)->dwBufferLength = mwRead(pwd, (LPBYTE)(*lplpWaveHdr)->lpData, min(pwd->dAudioBufferLen, pwd->dTo - pwd->dCur))))
  258. break;
  259. (*lplpWaveHdr)->dwFlags &= ~(WHDR_DONE | WHDR_BEGINLOOP | WHDR_ENDLOOP);
  260. if (0 != (pwd->wTaskError = waveOutWrite(pwd->hWaveOut, *lplpWaveHdr, sizeof(WAVEHDR))))
  261. break;
  262. wBuffersOutstanding++;
  263. lplpWaveHdr = NextWaveHdr(pwd, lplpWaveHdr);
  264. } else if (wBuffersOutstanding) {
  265. if (ISMODE(pwd, COMMAND_CUE)) {
  266. ADDMODE(pwd, MODE_CUED);
  267. mwDelayedNotify(pwd, MCI_NOTIFY_SUCCESSFUL);
  268. }
  269. if (TaskBlock() == WM_USER)
  270. wBuffersOutstanding--;
  271. } else if (ISMODE(pwd, COMMAND_HOLD)) {
  272. HoldPlayback(pwd);
  273. }
  274. else
  275. break;
  276. //@@ mmTaskYield();
  277. mmYield(pwd);
  278. }
  279. REMOVEMODE(pwd, MODE_PLAYING);
  280. return wBuffersOutstanding;
  281. }
  282. /************************************************************************/