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.

323 lines
12 KiB

  1. /*
  2. sound.c
  3. Level 1 kitchen sink DLL sound driver functions
  4. Copyright (c) Microsoft Corporation 1990. All rights reserved
  5. */
  6. #include <windows.h>
  7. #include "mmsystem.h"
  8. #include "mmddk.h"
  9. #include "mmsysi.h"
  10. #include "playwav.h"
  11. BOOL WINAPI IsTaskLocked(void); // In Kernel
  12. //
  13. // place sndPlaySound in the _TEXT segment so the entire wave segment
  14. // does not come in if no wave devices are loaded.
  15. //
  16. #pragma alloc_text(_TEXT, sndPlaySound)
  17. static SZCODE szNull[] = "";
  18. static SZCODE szSoundSection[] = "sounds"; // WIN.INI section for sounds
  19. SZCODE szSystemDefault[] = "SystemDefault"; // Name of the default sound
  20. #define SOUNDNAMELEN 128
  21. static HGLOBAL hCurrentSound; // handle to current sound.
  22. extern LPWAVEHDR lpWavHdr; // current playing sound PLAYWAV.C
  23. /****************************************************************************/
  24. static void PASCAL NEAR GetSoundName(
  25. LPCSTR lszSoundName,
  26. LPSTR lszBuffer)
  27. {
  28. OFSTRUCT of;
  29. int i;
  30. //
  31. // if the sound is defined in the [sounds] section of WIN.INI
  32. // get it and remove the description, otherwise assume it is a
  33. // file and qualify it.
  34. //
  35. GetProfileString(szSoundSection, lszSoundName, lszSoundName, lszBuffer, SOUNDNAMELEN);
  36. // remove any trailing text first
  37. for (i = 0; lszBuffer[i] && (lszBuffer[i] != ' ') && (lszBuffer[i] != '\t') && (lszBuffer[i] != ','); i++)
  38. ;
  39. lszBuffer[i] = (char)0;
  40. if (OpenFile(lszBuffer, &of, OF_EXIST | OF_READ | OF_SHARE_DENY_NONE) != HFILE_ERROR)
  41. OemToAnsi(of.szPathName, lszBuffer);
  42. }
  43. /*****************************************************************************
  44. * @doc EXTERNAL
  45. *
  46. * @api BOOL | sndPlaySound | This function plays a waveform
  47. * sound specified by a filename or by an entry in the [sounds] section
  48. * of WIN.INI. If the sound can't be found, it plays the
  49. * default sound specified by the SystemDefault entry in the
  50. * [sounds] section of WIN.INI. If there is no SystemDefault
  51. * entry or if the default sound can't be found, the function
  52. * makes no sound and returns FALSE.
  53. *
  54. * @parm LPCSTR | lpszSoundName | Specifies the name of the sound to play.
  55. * The function searches the [sounds] section of WIN.INI for an entry
  56. * with this name and plays the associated waveform file.
  57. * If no entry by this name exists, then it assumes the name is
  58. * the name of a waveform file. If this parameter is NULL, any
  59. * currently playing sound is stopped.
  60. *
  61. * @parm UINT | wFlags | Specifies options for playing the sound using one
  62. * or more of the following flags:
  63. *
  64. * @flag SND_SYNC | The sound is played synchronously and the
  65. * function does not return until the sound ends.
  66. * @flag SND_ASYNC | The sound is played asynchronously and the
  67. * function returns immediately after beginning the sound. To terminate
  68. * an asynchronously-played sound, call <f sndPlaySound> with
  69. * <p lpszSoundName> set to NULL.
  70. * @flag SND_NODEFAULT | If the sound can't be found, the function
  71. * returns silently without playing the default sound.
  72. * @flag SND_MEMORY | The parameter specified by <p lpszSoundName>
  73. * points to an in-memory image of a waveform sound.
  74. * @flag SND_LOOP | The sound will continue to play repeatedly
  75. * until <f sndPlaySound> is called again with the
  76. * <p lpszSoundName> parameter set to NULL. You must also specify the
  77. * SND_ASYNC flag to loop sounds.
  78. * @flag SND_NOSTOP | If a sound is currently playing, the
  79. * function will immediately return FALSE without playing the requested
  80. * sound.
  81. *
  82. * @rdesc Returns TRUE if the sound is played, otherwise
  83. * returns FALSE.
  84. *
  85. * @comm The sound must fit in available physical memory and be playable
  86. * by an installed waveform audio device driver. The directories
  87. * searched for sound files are, in order: the current directory;
  88. * the Windows directory; the Windows system directory; the directories
  89. * listed in the PATH environment variable; the list of directories
  90. * mapped in a network. See the Windows <f OpenFile> function for
  91. * more information about the directory search order.
  92. *
  93. * If you specify the SND_MEMORY flag, <p lpszSoundName> must point
  94. * to an in-memory image of a waveform sound. If the sound is stored
  95. * as a resource, use <f LoadResource> and <f LockResource> to load
  96. * and lock the resource and get a pointer to it. If the sound is not
  97. * a resource, you must use <f GlobalAlloc> with the GMEM_MOVEABLE and
  98. * GMEM_SHARE flags set and then <f GlobalLock> to allocate and lock
  99. * memory for the sound.
  100. *
  101. * @xref MessageBeep
  102. ****************************************************************************/
  103. BOOL WINAPI sndPlaySound(LPCSTR szSoundName, UINT wFlags)
  104. {
  105. //
  106. // !!! quick exit for no wave devices !!!
  107. //
  108. static UINT wTotalWaveOutDevs = (UINT)-1;
  109. if (wTotalWaveOutDevs == -1 ) {
  110. wTotalWaveOutDevs = waveOutGetNumDevs();
  111. }
  112. if (wTotalWaveOutDevs)
  113. return sndPlaySoundI(szSoundName, wFlags);
  114. else
  115. return FALSE;
  116. }
  117. /****************************************************************************/
  118. /*
  119. @doc INTERNAL
  120. @func BOOL | sndPlaySoundI | Internal version of <f>sndPlaySound<d> which
  121. resides in the WAVE segment instead.
  122. If the SND_NOSTOP flag is specifed and a wave file is currently
  123. playing, or if for some reason no mmsystem window is present, the
  124. function returns failure immediately. The first condition ensures
  125. that a current sound is not interrupted if the flag is set. The
  126. second condition is only in case of some start up error in which
  127. the notification window was not created, or mmsystem was not
  128. specified in the [drivers] line, and therefore never loaded.
  129. Next, if the <p>lszSoundName<d> parameter does not represent a memory
  130. file, and it is non-NULL, then it must represent a string. Therefore
  131. the string must be parsed before sending the sound message to the
  132. mmsystem window. This is because the mmsystem window may reside in a
  133. a different task than the task which is calling the function, and
  134. would most likely have a different current directory.
  135. In this case, the parameter is first checked to determine if it
  136. actually contains anything. For some reason a zero length string
  137. was determined to be able to return TRUE from this function, so that
  138. is checked.
  139. Next the string is checked against INI entries, then parsed.
  140. After parsing the sound name, ensure that a task switch only occurs if
  141. the sound is asyncronous (SND_ASYNC), and a previous sound does not
  142. need to be discarded.
  143. If a task switch is needed, first ensure that intertask messages can
  144. be sent by checking to see that this task is not locked, or that the
  145. notification window is in the current task.
  146. @parm LPCSTR | lszSoundName | Specifies the name of the sound to play.
  147. @parm UINT | wFlags | Specifies options for playing the sound.
  148. @rdesc Returns TRUE if the function was successful, else FALSE if an error
  149. occurred.
  150. */
  151. BOOL FAR PASCAL sndPlaySoundI(LPCSTR lszSoundName, UINT wFlags)
  152. {
  153. BOOL fPlayReturn;
  154. PSTR szSoundName;
  155. V_FLAGS(wFlags, SND_VALID, sndPlaySound, NULL);
  156. if ((wFlags & SND_LOOP) && !(wFlags & SND_ASYNC)) {
  157. LogParamError(ERR_BAD_FLAGS, (FARPROC)sndPlaySound, (LPVOID)(DWORD)wFlags);
  158. return FALSE;
  159. }
  160. if (!(wFlags & SND_MEMORY) && lszSoundName)
  161. V_STRING(lszSoundName, 128, FALSE);
  162. #ifdef DEBUG
  163. if (wFlags & SND_MEMORY) {
  164. DPRINTF1("MMSYSTEM: sndPlaySound(%lx)\r\n", lszSoundName);
  165. }
  166. else if (lszSoundName) {
  167. if (wFlags & SND_ASYNC) {
  168. if (wFlags & SND_LOOP) {
  169. DPRINTF1("MMSYSTEM: sndPlaySound(%ls, SND_ASYNC|SND_LOOP)\r\n", lszSoundName);
  170. }
  171. else {
  172. DPRINTF1("MMSYSTEM: sndPlaySound(%ls, SND_ASYNC)\r\n", lszSoundName);
  173. }
  174. }
  175. else
  176. DPRINTF1("MMSYSTEM: sndPlaySound(%ls, SND_SYNC)\r\n", lszSoundName);
  177. }
  178. else
  179. DOUT("MMSYSTEM: sndPlaySound(NULL)\r\n");
  180. #endif //ifdef DEBUG
  181. if (((wFlags & SND_NOSTOP) && lpWavHdr) || !hwndNotify)
  182. return FALSE;
  183. if (!(wFlags & SND_MEMORY) && lszSoundName) {
  184. if (!*lszSoundName)
  185. return TRUE;
  186. if (!(szSoundName = (PSTR)LocalAlloc(LMEM_FIXED, SOUNDNAMELEN)))
  187. return FALSE;
  188. GetSoundName(lszSoundName, szSoundName);
  189. lszSoundName = (LPCSTR)szSoundName;
  190. } else
  191. szSoundName = NULL;
  192. if (!(wFlags & SND_ASYNC) && !lpWavHdr)
  193. fPlayReturn = sndMessage((LPSTR)lszSoundName, wFlags);
  194. else {
  195. if (!IsTaskLocked() || (GetWindowTask(hwndNotify) == GetCurrentTask())) {
  196. fPlayReturn = (BOOL)(LONG)SendMessage(hwndNotify, MM_SND_PLAY, (WPARAM)wFlags, (LPARAM)lszSoundName);
  197. } else
  198. fPlayReturn = FALSE;
  199. }
  200. if (szSoundName)
  201. LocalFree((HLOCAL)szSoundName);
  202. return fPlayReturn;
  203. }
  204. /****************************************************************************/
  205. static BOOL PASCAL NEAR SetCurrentSound(
  206. LPCSTR lszSoundName)
  207. {
  208. HGLOBAL hSound;
  209. BOOL f;
  210. LPSTR lp;
  211. if (hCurrentSound && (lp = GlobalLock(hCurrentSound))) {
  212. f = lstrcmpi(lszSoundName, lp + sizeof(WAVEHDR)) == 0;
  213. GlobalUnlock(hCurrentSound);
  214. if (f)
  215. return TRUE;
  216. }
  217. DPRINTF(("MMSYSTEM: soundLoadFile(%ls)\r\n",lszSoundName));
  218. if (hSound = soundLoadFile(lszSoundName)) {
  219. soundFree(hCurrentSound);
  220. hCurrentSound = hSound;
  221. return TRUE;
  222. }
  223. return FALSE;
  224. }
  225. /****************************************************************************/
  226. /*
  227. @doc INTERNAL
  228. @func BOOL | sndMessage | This function is called in response to an
  229. MM_SND_PLAY message sent to the mmsystem window, and attempts to
  230. play the specified file, or dump current sound caching.
  231. If <p>lszSoundName<d> is NULL, any currently cached sound is
  232. discarded, and the function returns success.
  233. If the SND_MEMORY flag is set, then <p>lszSoundName<d> actually
  234. points to a buffer containing a RIFF format WAVE memory file, and
  235. the function attempts to play it. The load function performs
  236. validation on this memory file. Unlike playing sound names,
  237. memory files are not cached for future use.
  238. Otherwise the <p>lszSoundName<d> parameter is actually an INI entry
  239. or file name. The function initially attempts to load that sound,
  240. and if it fails, attempts to load the system default sound. Note of
  241. course that the SND_NODEFAULT flag is first checked to determine if
  242. the default sound is to be played when the original name cannot be
  243. located. If no default is wanted, or the default cannot be located,
  244. the function returns failure. Note that in calling <f>GetSoundName<d>,
  245. the <p>lszSoundName<d> parameter is modified. This function assumes
  246. that the parameter passed has been previously allocated if a string is
  247. passed to this function, and is not the actual user's parameter passed
  248. to <f>sndPlaySound<d>.
  249. @parm LPSTR | lszSoundName | Specifies the name of the sound to play.
  250. @parm UINT | wFlags | Specifies options for playing the sound.
  251. @rdesc Returns TRUE if the function was successful, else FALSE if an error
  252. occurred.
  253. */
  254. BOOL FAR PASCAL sndMessage(LPSTR lszSoundName, UINT wFlags)
  255. {
  256. if (!lszSoundName) {
  257. soundFree(hCurrentSound);
  258. hCurrentSound = NULL;
  259. return TRUE;
  260. }
  261. if (wFlags & SND_MEMORY) {
  262. soundFree(hCurrentSound);
  263. hCurrentSound = soundLoadMemory(lszSoundName);
  264. } else if (!SetCurrentSound(lszSoundName)) {
  265. if (wFlags & SND_NODEFAULT)
  266. return FALSE;
  267. GetSoundName(szSystemDefault, lszSoundName);
  268. if (!SetCurrentSound(lszSoundName))
  269. return FALSE;
  270. }
  271. return soundPlay(hCurrentSound, wFlags);
  272. }