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.

336 lines
8.3 KiB

  1. /*--------------------------------------------------------------+
  2. | audplay.c Simple routines to play audio using an AVIStream to |
  3. | get data. Uses global variables, so only one instance at a |
  4. | time. (Usually, there's only one sound card, so this isn't |
  5. | so bad. |
  6. +--------------------------------------------------------------*/
  7. #include <windows.h>
  8. #include <windowsx.h>
  9. #include <win32.h>
  10. #include <mmsystem.h>
  11. #include <vfw.h>
  12. #include "audplay.h"
  13. /*--------------------------------------------------------------+
  14. | ****************** AUDIO PLAYING SUPPORT ******************** |
  15. +--------------------------------------------------------------*/
  16. static HWAVEOUT shWaveOut = 0; /* Current MCI device ID */
  17. static LONG slBegin;
  18. static LONG slCurrent;
  19. static LONG slEnd;
  20. static BOOL sfLooping;
  21. static BOOL sfPlaying = FALSE;
  22. #define MAX_AUDIO_BUFFERS 16
  23. #define MIN_AUDIO_BUFFERS 2
  24. #define AUDIO_BUFFER_SIZE 16384
  25. static UINT swBuffers; // total # buffers
  26. static UINT swBuffersOut; // buffers device has
  27. static UINT swNextBuffer; // next buffer to fill
  28. static LPWAVEHDR salpAudioBuf[MAX_AUDIO_BUFFERS];
  29. static PAVISTREAM spavi; // stream we're playing
  30. static LONG slSampleSize; // size of an audio sample
  31. static LONG sdwBytesPerSec;
  32. static LONG sdwSamplesPerSec;
  33. #ifndef WIN32
  34. extern LONG FAR PASCAL muldiv32(LONG, LONG, LONG);
  35. #endif
  36. /*---------------------------------------------------------------+
  37. | aviaudioCloseDevice -- close the open audio device, if any. |
  38. +---------------------------------------------------------------*/
  39. void NEAR aviaudioCloseDevice(void)
  40. {
  41. UINT w;
  42. if (shWaveOut) {
  43. while (swBuffers > 0) {
  44. --swBuffers;
  45. waveOutUnprepareHeader(shWaveOut, salpAudioBuf[swBuffers],
  46. sizeof(WAVEHDR));
  47. GlobalFreePtr((LPBYTE) salpAudioBuf[swBuffers]);
  48. }
  49. w = waveOutClose(shWaveOut);
  50. // DPF("AudioCloseDevice: waveOutClose returns %u\n", w);
  51. shWaveOut = NULL;
  52. }
  53. }
  54. /*--------------------------------------------------------------+
  55. | aviaudioOpenDevice -- get ready to play waveform data. |
  56. +--------------------------------------------------------------*/
  57. BOOL FAR aviaudioOpenDevice(HWND hwnd, PAVISTREAM pavi)
  58. {
  59. UINT w;
  60. LPVOID lpFormat;
  61. LONG cbFormat;
  62. AVISTREAMINFO strhdr;
  63. if (!pavi) // no wave data to play
  64. return FALSE;
  65. if (shWaveOut) // already something playing
  66. return TRUE;
  67. spavi = pavi;
  68. AVIStreamInfo(pavi, &strhdr, sizeof(strhdr));
  69. slSampleSize = (LONG) strhdr.dwSampleSize;
  70. if (slSampleSize <= 0 || slSampleSize > AUDIO_BUFFER_SIZE)
  71. return FALSE;
  72. AVIStreamFormatSize(pavi, 0, &cbFormat);
  73. lpFormat = GlobalAllocPtr(GHND, cbFormat);
  74. if (!lpFormat)
  75. return FALSE;
  76. AVIStreamReadFormat(pavi, 0, lpFormat, &cbFormat);
  77. sdwSamplesPerSec = ((LPWAVEFORMAT) lpFormat)->nSamplesPerSec;
  78. sdwBytesPerSec = ((LPWAVEFORMAT) lpFormat)->nAvgBytesPerSec;
  79. w = waveOutOpen(&shWaveOut, WAVE_MAPPER, lpFormat,
  80. (DWORD) (UINT) hwnd, 0L, CALLBACK_WINDOW);
  81. //
  82. // Maybe we failed because someone is playing sound already.
  83. // Shut any sound off, and try once more before giving up.
  84. //
  85. if (w) {
  86. sndPlaySound(NULL, 0);
  87. w = waveOutOpen(&shWaveOut, WAVE_MAPPER, lpFormat,
  88. (DWORD) (UINT) hwnd, 0L, CALLBACK_WINDOW);
  89. }
  90. // DPF("waveOutOpen returns %u, shWaveOut = %u\n", w, shWaveOut);
  91. if (w != 0) {
  92. /* Show error message here? */
  93. return FALSE;
  94. }
  95. for (swBuffers = 0; swBuffers < MAX_AUDIO_BUFFERS; swBuffers++) {
  96. if (!(salpAudioBuf[swBuffers] =
  97. (LPWAVEHDR)GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
  98. (DWORD)(sizeof(WAVEHDR) + AUDIO_BUFFER_SIZE))))
  99. break;
  100. salpAudioBuf[swBuffers]->dwFlags = WHDR_DONE;
  101. salpAudioBuf[swBuffers]->lpData = (LPBYTE) salpAudioBuf[swBuffers]
  102. + sizeof(WAVEHDR);
  103. salpAudioBuf[swBuffers]->dwBufferLength = AUDIO_BUFFER_SIZE;
  104. if (!waveOutPrepareHeader(shWaveOut, salpAudioBuf[swBuffers],
  105. sizeof(WAVEHDR)))
  106. continue;
  107. GlobalFreePtr((LPBYTE) salpAudioBuf[swBuffers]);
  108. break;
  109. }
  110. // DPF("Allocated %u %lu-byte buffers.\n", swBuffers, (DWORD) AUDIO_BUFFER_SIZE);
  111. if (swBuffers < MIN_AUDIO_BUFFERS) {
  112. aviaudioCloseDevice();
  113. return FALSE;
  114. }
  115. swBuffersOut = 0;
  116. swNextBuffer = 0;
  117. sfPlaying = FALSE;
  118. return TRUE;
  119. }
  120. //
  121. // Return the time in milliseconds corresponding to the currently playing
  122. // audio sample, or -1 if no audio is playing.
  123. // WARNING: Some sound cards are pretty inaccurate!
  124. //
  125. LONG FAR aviaudioTime(void)
  126. {
  127. MMTIME mmtime;
  128. if (!sfPlaying)
  129. return -1;
  130. mmtime.wType = TIME_SAMPLES;
  131. waveOutGetPosition(shWaveOut, &mmtime, sizeof(mmtime));
  132. if (mmtime.wType == TIME_SAMPLES)
  133. return AVIStreamSampleToTime(spavi, slBegin)
  134. + muldiv32(mmtime.u.sample, 1000, sdwSamplesPerSec);
  135. else if (mmtime.wType == TIME_BYTES)
  136. return AVIStreamSampleToTime(spavi, slBegin)
  137. + muldiv32(mmtime.u.cb, 1000, sdwBytesPerSec);
  138. else
  139. return -1;
  140. }
  141. //
  142. // Fill up any empty audio buffers and ship them out to the device.
  143. //
  144. BOOL NEAR aviaudioiFillBuffers(void)
  145. {
  146. LONG lRead;
  147. UINT w;
  148. LONG lSamplesToPlay;
  149. /* We're not playing, so do nothing. */
  150. if (!sfPlaying)
  151. return TRUE;
  152. // DPF3("%u/%u (%lu-%lu)\n", swBuffersOut, swBuffers, slCurrent, slEnd);
  153. while (swBuffersOut < swBuffers) {
  154. if (slCurrent >= slEnd) {
  155. if (sfLooping) {
  156. /* Looping, so go to the beginning. */
  157. slCurrent = slBegin;
  158. } else
  159. break;
  160. }
  161. /* Figure out how much data should go in this buffer */
  162. lSamplesToPlay = slEnd - slCurrent;
  163. if (lSamplesToPlay > AUDIO_BUFFER_SIZE / slSampleSize)
  164. lSamplesToPlay = AUDIO_BUFFER_SIZE / slSampleSize;
  165. AVIStreamRead(spavi, slCurrent, lSamplesToPlay,
  166. salpAudioBuf[swNextBuffer]->lpData,
  167. AUDIO_BUFFER_SIZE,
  168. &salpAudioBuf[swNextBuffer]->dwBufferLength,
  169. &lRead);
  170. if (lRead != lSamplesToPlay) {
  171. // DPF("Error from WAVE_READ\n");
  172. return FALSE;
  173. }
  174. slCurrent += lRead;
  175. w = waveOutWrite(shWaveOut, salpAudioBuf[swNextBuffer],sizeof(WAVEHDR));
  176. if (w != 0) {
  177. // DPF("Error from waveOutWrite\n");
  178. return FALSE;
  179. }
  180. ++swBuffersOut;
  181. ++swNextBuffer;
  182. if (swNextBuffer >= swBuffers)
  183. swNextBuffer = 0;
  184. }
  185. if (swBuffersOut == 0 && slCurrent >= slEnd)
  186. aviaudioStop();
  187. /* We've filled all of the buffers we can or want to. */
  188. return TRUE;
  189. }
  190. /*--------------------------------------------------------------+
  191. | aviaudioPlay -- Play audio, starting at a given frame |
  192. | |
  193. +--------------------------------------------------------------*/
  194. BOOL FAR aviaudioPlay(HWND hwnd, PAVISTREAM pavi, LONG lStart, LONG lEnd, BOOL fWait)
  195. {
  196. if (!aviaudioOpenDevice(hwnd, pavi))
  197. return FALSE;
  198. if (lStart < 0)
  199. lStart = AVIStreamStart(pavi);
  200. if (lEnd < 0)
  201. lEnd = AVIStreamEnd(pavi);
  202. // DPF2("Audio play%s from %ld to %ld (samples)\n", ((LPSTR) (fWait ? " wait" : "")), lStart, lEnd);
  203. if (lStart >= lEnd)
  204. return TRUE;
  205. if (!sfPlaying) {
  206. //
  207. // We're beginning play, so pause until we've filled the buffers
  208. // for a seamless start
  209. //
  210. waveOutPause(shWaveOut);
  211. slBegin = lStart;
  212. slCurrent = lStart;
  213. slEnd = lEnd;
  214. sfPlaying = TRUE;
  215. } else {
  216. if (lStart > slEnd) {
  217. // DPF("Gap in wave that is supposed to be played!\n");
  218. }
  219. slEnd = lEnd;
  220. }
  221. // sfLooping = fLoop;
  222. aviaudioiFillBuffers();
  223. //
  224. // Now unpause the audio and away it goes!
  225. //
  226. waveOutRestart(shWaveOut);
  227. //
  228. // Caller wants us not to return until play is finished
  229. //
  230. if (fWait) {
  231. while (swBuffersOut > 0)
  232. Yield();
  233. }
  234. return TRUE;
  235. }
  236. /*--------------------------------------------------------------+
  237. | aviaudioMessage -- handle wave messages received by |
  238. | window controlling audio playback. When audio buffers are |
  239. | done, this routine calls aviaudioiFillBuffers to fill them |
  240. | up again. |
  241. +--------------------------------------------------------------*/
  242. void FAR aviaudioMessage(HWND hwnd, unsigned msg, WPARAM wParam, LPARAM lParam)
  243. {
  244. if (msg == MM_WOM_DONE) {
  245. --swBuffersOut;
  246. aviaudioiFillBuffers();
  247. }
  248. }
  249. /*--------------------------------------------------------------+
  250. | aviaudioStop -- stop playing, close the device. |
  251. +--------------------------------------------------------------*/
  252. void FAR aviaudioStop(void)
  253. {
  254. UINT w;
  255. if (shWaveOut != 0) {
  256. w = waveOutReset(shWaveOut);
  257. sfPlaying = FALSE;
  258. // DPF("AudioStop: waveOutReset() returns %u \n", w);
  259. aviaudioCloseDevice();
  260. }
  261. }