1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */ 2 3 /* 4 * MMSYSTEM functions 5 * 6 * Copyright 1993 Martin Ayotte 7 * 1998-2002 Eric Pouech 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2.1 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 22 */ 23 24 #include "winemm.h" 25 26 #include <winternl.h> 27 28 WINE_DEFAULT_DEBUG_CHANNEL(winmm); 29 30 typedef struct tagWINE_PLAYSOUND 31 { 32 unsigned bLoop : 1, 33 bAlloc : 1; 34 LPCWSTR pszSound; 35 HMODULE hMod; 36 DWORD fdwSound; 37 HANDLE hThread; 38 HWAVEOUT hWave; 39 struct tagWINE_PLAYSOUND* lpNext; 40 } WINE_PLAYSOUND; 41 42 static WINE_PLAYSOUND *PlaySoundList; 43 44 static HMMIO get_mmioFromFile(LPCWSTR lpszName) 45 { 46 HMMIO ret; 47 WCHAR buf[256]; 48 LPWSTR dummy; 49 50 ret = mmioOpenW((LPWSTR)lpszName, NULL, 51 MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE); 52 if (ret != 0) return ret; 53 if (SearchPathW(NULL, lpszName, NULL, sizeof(buf)/sizeof(buf[0]), buf, &dummy)) 54 { 55 return mmioOpenW(buf, NULL, 56 MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE); 57 } 58 return 0; 59 } 60 61 static HMMIO get_mmioFromProfile(UINT uFlags, LPCWSTR lpszName) 62 { 63 WCHAR str[128]; 64 LPWSTR ptr; 65 HMMIO hmmio; 66 HKEY hRegSnd, hRegApp, hScheme, hSnd; 67 DWORD err, type, count; 68 69 static const WCHAR wszSounds[] = {'S','o','u','n','d','s',0}; 70 static const WCHAR wszDefault[] = {'D','e','f','a','u','l','t',0}; 71 static const WCHAR wszKey[] = {'A','p','p','E','v','e','n','t','s','\\', 72 'S','c','h','e','m','e','s','\\', 73 'A','p','p','s',0}; 74 static const WCHAR wszDotDefault[] = {'.','D','e','f','a','u','l','t',0}; 75 static const WCHAR wszDotCurrent[] = {'.','C','u','r','r','e','n','t',0}; 76 static const WCHAR wszNull[] = {0}; 77 78 TRACE("searching in SystemSound list for %s\n", debugstr_w(lpszName)); 79 GetProfileStringW(wszSounds, lpszName, wszNull, str, sizeof(str)/sizeof(str[0])); 80 if (lstrlenW(str) == 0) 81 { 82 if (uFlags & SND_NODEFAULT) goto next; 83 GetProfileStringW(wszSounds, wszDefault, wszNull, str, sizeof(str)/sizeof(str[0])); 84 if (lstrlenW(str) == 0) goto next; 85 } 86 for (ptr = str; *ptr && *ptr != ','; ptr++); 87 if (*ptr) *ptr = 0; 88 hmmio = mmioOpenW(str, NULL, MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE); 89 if (hmmio != 0) return hmmio; 90 next: 91 /* we look up the registry under 92 * HKCU\AppEvents\Schemes\Apps\.Default 93 * HKCU\AppEvents\Schemes\Apps\<AppName> 94 */ 95 if (RegOpenKeyW(HKEY_CURRENT_USER, wszKey, &hRegSnd) != 0) goto none; 96 if (uFlags & SND_APPLICATION) 97 { 98 DWORD len; 99 100 err = 1; /* error */ 101 len = GetModuleFileNameW(0, str, sizeof(str)/sizeof(str[0])); 102 if (len > 0 && len < sizeof(str)/sizeof(str[0])) 103 { 104 for (ptr = str + lstrlenW(str) - 1; ptr >= str; ptr--) 105 { 106 if (*ptr == '.') *ptr = 0; 107 if (*ptr == '\\') 108 { 109 err = RegOpenKeyW(hRegSnd, ptr+1, &hRegApp); 110 break; 111 } 112 } 113 } 114 } 115 else 116 { 117 err = RegOpenKeyW(hRegSnd, wszDotDefault, &hRegApp); 118 } 119 RegCloseKey(hRegSnd); 120 if (err != 0) goto none; 121 err = RegOpenKeyW(hRegApp, lpszName, &hScheme); 122 RegCloseKey(hRegApp); 123 if (err != 0) goto none; 124 /* what's the difference between .Current and .Default ? */ 125 err = RegOpenKeyW(hScheme, wszDotDefault, &hSnd); 126 if (err != 0) 127 { 128 err = RegOpenKeyW(hScheme, wszDotCurrent, &hSnd); 129 RegCloseKey(hScheme); 130 if (err != 0) 131 goto none; 132 } 133 count = sizeof(str); 134 err = RegQueryValueExW(hSnd, NULL, 0, &type, (LPBYTE)str, &count); 135 RegCloseKey(hSnd); 136 if (err != 0 || !*str) goto none; 137 hmmio = mmioOpenW(str, NULL, MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE); 138 if (hmmio) return hmmio; 139 none: 140 WARN("can't find SystemSound=%s !\n", debugstr_w(lpszName)); 141 return 0; 142 } 143 144 struct playsound_data 145 { 146 HANDLE hEvent; 147 LONG dwEventCount; 148 }; 149 150 static void CALLBACK PlaySound_Callback(HWAVEOUT hwo, UINT uMsg, 151 DWORD_PTR dwInstance, 152 DWORD_PTR dwParam1, DWORD_PTR dwParam2) 153 { 154 struct playsound_data* s = (struct playsound_data*)dwInstance; 155 156 switch (uMsg) { 157 case WOM_OPEN: 158 case WOM_CLOSE: 159 break; 160 case WOM_DONE: 161 InterlockedIncrement(&s->dwEventCount); 162 TRACE("Returning waveHdr=%lx\n", dwParam1); 163 SetEvent(s->hEvent); 164 break; 165 default: 166 ERR("Unknown uMsg=%d\n", uMsg); 167 } 168 } 169 170 static void PlaySound_WaitDone(struct playsound_data* s) 171 { 172 for (;;) { 173 ResetEvent(s->hEvent); 174 if (InterlockedDecrement(&s->dwEventCount) >= 0) break; 175 InterlockedIncrement(&s->dwEventCount); 176 177 WaitForSingleObject(s->hEvent, INFINITE); 178 } 179 } 180 181 static BOOL PlaySound_IsString(DWORD fdwSound, const void* psz) 182 { 183 /* SND_RESOURCE is 0x40004 while 184 * SND_MEMORY is 0x00004 185 */ 186 switch (fdwSound & (SND_RESOURCE|SND_ALIAS_ID|SND_FILENAME)) 187 { 188 case SND_RESOURCE: return HIWORD(psz) != 0; /* by name or by ID ? */ 189 case SND_ALIAS_ID: 190 case SND_MEMORY: return FALSE; 191 case SND_ALIAS: 192 case SND_FILENAME: 193 case 0: return TRUE; 194 default: FIXME("WTF\n"); return FALSE; 195 } 196 } 197 198 static void PlaySound_Free(WINE_PLAYSOUND* wps) 199 { 200 WINE_PLAYSOUND** p; 201 202 EnterCriticalSection(&WINMM_cs); 203 for (p = &PlaySoundList; *p && *p != wps; p = &((*p)->lpNext)); 204 if (*p) *p = (*p)->lpNext; 205 if (PlaySoundList == NULL) SetEvent(psLastEvent); 206 LeaveCriticalSection(&WINMM_cs); 207 if (wps->bAlloc) HeapFree(GetProcessHeap(), 0, (void*)wps->pszSound); 208 if (wps->hThread) CloseHandle(wps->hThread); 209 HeapFree(GetProcessHeap(), 0, wps); 210 } 211 212 static WINE_PLAYSOUND* PlaySound_Alloc(const void* pszSound, HMODULE hmod, 213 DWORD fdwSound, BOOL bUnicode) 214 { 215 WINE_PLAYSOUND* wps; 216 217 wps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wps)); 218 if (!wps) return NULL; 219 220 wps->hMod = hmod; 221 wps->fdwSound = fdwSound; 222 if (PlaySound_IsString(fdwSound, pszSound)) 223 { 224 if (bUnicode) 225 { 226 if (fdwSound & SND_ASYNC) 227 { 228 LPWSTR sound = HeapAlloc(GetProcessHeap(), 0, 229 (lstrlenW(pszSound)+1) * sizeof(WCHAR)); 230 if (!sound) goto oom_error; 231 wps->pszSound = lstrcpyW(sound, pszSound); 232 wps->bAlloc = TRUE; 233 } 234 else 235 wps->pszSound = pszSound; 236 } 237 else 238 { 239 UNICODE_STRING usBuffer; 240 RtlCreateUnicodeStringFromAsciiz(&usBuffer, pszSound); 241 wps->pszSound = usBuffer.Buffer; 242 if (!wps->pszSound) goto oom_error; 243 wps->bAlloc = TRUE; 244 } 245 } 246 else 247 wps->pszSound = pszSound; 248 249 return wps; 250 oom_error: 251 PlaySound_Free(wps); 252 return NULL; 253 } 254 255 static DWORD WINAPI proc_PlaySound(LPVOID arg) 256 { 257 WINE_PLAYSOUND* wps = arg; 258 BOOL bRet = FALSE; 259 HMMIO hmmio = 0; 260 MMCKINFO ckMainRIFF; 261 MMCKINFO mmckInfo; 262 LPWAVEFORMATEX lpWaveFormat = NULL; 263 LPWAVEHDR waveHdr = NULL; 264 INT count, bufsize, left, index; 265 struct playsound_data s; 266 void* data; 267 268 s.hEvent = 0; 269 270 TRACE("SoundName=%s !\n", debugstr_w(wps->pszSound)); 271 272 /* if resource, grab it */ 273 if ((wps->fdwSound & SND_RESOURCE) == SND_RESOURCE) { 274 static const WCHAR wszWave[] = {'W','A','V','E',0}; 275 HRSRC hRes; 276 HGLOBAL hGlob; 277 278 if ((hRes = FindResourceW(wps->hMod, wps->pszSound, wszWave)) == 0 || 279 (hGlob = LoadResource(wps->hMod, hRes)) == 0) 280 goto errCleanUp; 281 if ((data = LockResource(hGlob)) == NULL) { 282 FreeResource(hGlob); 283 goto errCleanUp; 284 } 285 FreeResource(hGlob); 286 } else 287 data = (void*)wps->pszSound; 288 289 /* construct an MMIO stream (either in memory, or from a file */ 290 if (wps->fdwSound & SND_MEMORY) 291 { /* NOTE: SND_RESOURCE has the SND_MEMORY bit set */ 292 MMIOINFO mminfo; 293 294 memset(&mminfo, 0, sizeof(mminfo)); 295 mminfo.fccIOProc = FOURCC_MEM; 296 mminfo.pchBuffer = data; 297 mminfo.cchBuffer = -1; /* FIXME: when a resource, could grab real size */ 298 TRACE("Memory sound %p\n", data); 299 hmmio = mmioOpenW(NULL, &mminfo, MMIO_READ); 300 } 301 else if (wps->fdwSound & SND_ALIAS) 302 { 303 if ((wps->fdwSound & SND_ALIAS_ID) == SND_ALIAS_ID) 304 { 305 static const WCHAR wszSystemAsterisk[] = {'S','y','s','t','e','m','A','s','t','e','r','i','s','k',0}; 306 static const WCHAR wszSystemDefault[] = {'S','y','s','t','e','m','D','e','f','a','u','l','t',0}; 307 static const WCHAR wszSystemExclamation[] = {'S','y','s','t','e','m','E','x','c','l','a','m','a','t','i','o','n',0}; 308 static const WCHAR wszSystemExit[] = {'S','y','s','t','e','m','E','x','i','t',0}; 309 static const WCHAR wszSystemHand[] = {'S','y','s','t','e','m','H','a','n','d',0}; 310 static const WCHAR wszSystemQuestion[] = {'S','y','s','t','e','m','Q','u','e','s','t','i','o','n',0}; 311 static const WCHAR wszSystemStart[] = {'S','y','s','t','e','m','S','t','a','r','t',0}; 312 static const WCHAR wszSystemWelcome[] = {'S','y','s','t','e','m','W','e','l','c','o','m','e',0}; 313 314 wps->fdwSound &= ~(SND_ALIAS_ID ^ SND_ALIAS); 315 if (wps->pszSound == (LPCWSTR)SND_ALIAS_SYSTEMASTERISK) 316 wps->pszSound = wszSystemAsterisk; 317 else if (wps->pszSound == (LPCWSTR)SND_ALIAS_SYSTEMDEFAULT) 318 wps->pszSound = wszSystemDefault; 319 else if (wps->pszSound == (LPCWSTR)SND_ALIAS_SYSTEMEXCLAMATION) 320 wps->pszSound = wszSystemExclamation; 321 else if (wps->pszSound == (LPCWSTR)SND_ALIAS_SYSTEMEXIT) 322 wps->pszSound = wszSystemExit; 323 else if (wps->pszSound == (LPCWSTR)SND_ALIAS_SYSTEMHAND) 324 wps->pszSound = wszSystemHand; 325 else if (wps->pszSound == (LPCWSTR)SND_ALIAS_SYSTEMQUESTION) 326 wps->pszSound = wszSystemQuestion; 327 else if (wps->pszSound == (LPCWSTR)SND_ALIAS_SYSTEMSTART) 328 wps->pszSound = wszSystemStart; 329 else if (wps->pszSound == (LPCWSTR)SND_ALIAS_SYSTEMWELCOME) 330 wps->pszSound = wszSystemWelcome; 331 else goto errCleanUp; 332 } 333 hmmio = get_mmioFromProfile(wps->fdwSound, wps->pszSound); 334 } 335 else if (wps->fdwSound & SND_FILENAME) 336 { 337 hmmio = get_mmioFromFile(wps->pszSound); 338 } 339 else 340 { 341 if ((hmmio = get_mmioFromProfile(wps->fdwSound | SND_NODEFAULT, wps->pszSound)) == 0) 342 { 343 if ((hmmio = get_mmioFromFile(wps->pszSound)) == 0) 344 { 345 hmmio = get_mmioFromProfile(wps->fdwSound, wps->pszSound); 346 } 347 } 348 } 349 if (hmmio == 0) goto errCleanUp; 350 351 if (mmioDescend(hmmio, &ckMainRIFF, NULL, 0)) 352 goto errCleanUp; 353 354 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n", 355 (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize); 356 357 if ((ckMainRIFF.ckid != FOURCC_RIFF) || 358 (ckMainRIFF.fccType != mmioFOURCC('W', 'A', 'V', 'E'))) 359 goto errCleanUp; 360 361 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' '); 362 if (mmioDescend(hmmio, &mmckInfo, &ckMainRIFF, MMIO_FINDCHUNK)) 363 goto errCleanUp; 364 365 TRACE("Chunk Found ckid=%.4s fccType=%08x cksize=%08X\n", 366 (LPSTR)&mmckInfo.ckid, mmckInfo.fccType, mmckInfo.cksize); 367 368 lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize); 369 if (mmioRead(hmmio, (HPSTR)lpWaveFormat, mmckInfo.cksize) < sizeof(PCMWAVEFORMAT)) 370 goto errCleanUp; 371 372 TRACE("wFormatTag=%04X !\n", lpWaveFormat->wFormatTag); 373 TRACE("nChannels=%d\n", lpWaveFormat->nChannels); 374 TRACE("nSamplesPerSec=%d\n", lpWaveFormat->nSamplesPerSec); 375 TRACE("nAvgBytesPerSec=%d\n", lpWaveFormat->nAvgBytesPerSec); 376 TRACE("nBlockAlign=%d\n", lpWaveFormat->nBlockAlign); 377 TRACE("wBitsPerSample=%u !\n", lpWaveFormat->wBitsPerSample); 378 379 /* move to end of 'fmt ' chunk */ 380 mmioAscend(hmmio, &mmckInfo, 0); 381 382 mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a'); 383 if (mmioDescend(hmmio, &mmckInfo, &ckMainRIFF, MMIO_FINDCHUNK)) 384 goto errCleanUp; 385 386 TRACE("Chunk Found ckid=%.4s fccType=%08x cksize=%08X\n", 387 (LPSTR)&mmckInfo.ckid, mmckInfo.fccType, mmckInfo.cksize); 388 389 s.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); 390 391 if (waveOutOpen(&wps->hWave, WAVE_MAPPER, lpWaveFormat, (DWORD_PTR)PlaySound_Callback, 392 (DWORD_PTR)&s, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) 393 goto errCleanUp; 394 395 /* make it so that 3 buffers per second are needed */ 396 bufsize = (((lpWaveFormat->nAvgBytesPerSec / 3) - 1) / lpWaveFormat->nBlockAlign + 1) * 397 lpWaveFormat->nBlockAlign; 398 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize); 399 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR); 400 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize; 401 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L; 402 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L; 403 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L; 404 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize; 405 if (waveOutPrepareHeader(wps->hWave, &waveHdr[0], sizeof(WAVEHDR)) || 406 waveOutPrepareHeader(wps->hWave, &waveHdr[1], sizeof(WAVEHDR))) { 407 goto errCleanUp; 408 } 409 410 s.dwEventCount = 1L; /* for first buffer */ 411 index = 0; 412 413 do { 414 left = mmckInfo.cksize; 415 416 mmioSeek(hmmio, mmckInfo.dwDataOffset, SEEK_SET); 417 while (left) 418 { 419 if (WaitForSingleObject(psStopEvent, 0) == WAIT_OBJECT_0) 420 { 421 wps->bLoop = FALSE; 422 break; 423 } 424 count = mmioRead(hmmio, waveHdr[index].lpData, min(bufsize, left)); 425 if (count < 1) break; 426 left -= count; 427 waveHdr[index].dwBufferLength = count; 428 waveHdr[index].dwFlags &= ~WHDR_DONE; 429 if (waveOutWrite(wps->hWave, &waveHdr[index], sizeof(WAVEHDR)) == MMSYSERR_NOERROR) { 430 index ^= 1; 431 PlaySound_WaitDone(&s); 432 } 433 else FIXME("Couldn't play header\n"); 434 } 435 bRet = TRUE; 436 } while (wps->bLoop); 437 438 PlaySound_WaitDone(&s); /* for last buffer */ 439 waveOutReset(wps->hWave); 440 441 waveOutUnprepareHeader(wps->hWave, &waveHdr[0], sizeof(WAVEHDR)); 442 waveOutUnprepareHeader(wps->hWave, &waveHdr[1], sizeof(WAVEHDR)); 443 444 errCleanUp: 445 TRACE("Done playing=%s => %s!\n", debugstr_w(wps->pszSound), bRet ? "ok" : "ko"); 446 CloseHandle(s.hEvent); 447 HeapFree(GetProcessHeap(), 0, waveHdr); 448 HeapFree(GetProcessHeap(), 0, lpWaveFormat); 449 if (wps->hWave) while (waveOutClose(wps->hWave) == WAVERR_STILLPLAYING) Sleep(100); 450 if (hmmio) mmioClose(hmmio, 0); 451 452 PlaySound_Free(wps); 453 454 return bRet; 455 } 456 457 static BOOL MULTIMEDIA_PlaySound(const void* pszSound, HMODULE hmod, DWORD fdwSound, BOOL bUnicode) 458 { 459 WINE_PLAYSOUND* wps = NULL; 460 461 TRACE("pszSound='%p' hmod=%p fdwSound=%08X\n", 462 pszSound, hmod, fdwSound); 463 464 /* FIXME? I see no difference between SND_NOWAIT and SND_NOSTOP ! 465 * there could be one if several sounds can be played at once... 466 */ 467 if ((fdwSound & (SND_NOWAIT | SND_NOSTOP)) && PlaySoundList != NULL) 468 return FALSE; 469 470 /* alloc internal structure, if we need to play something */ 471 if (pszSound && !(fdwSound & SND_PURGE)) 472 { 473 if (!(wps = PlaySound_Alloc(pszSound, hmod, fdwSound, bUnicode))) 474 return FALSE; 475 } 476 477 EnterCriticalSection(&WINMM_cs); 478 /* since several threads can enter PlaySound in parallel, we're not 479 * sure, at this point, that another thread didn't start a new playsound 480 */ 481 while (PlaySoundList != NULL) 482 { 483 ResetEvent(psLastEvent); 484 /* FIXME: doc says we have to stop all instances of pszSound if it's non 485 * NULL... as of today, we stop all playing instances */ 486 SetEvent(psStopEvent); 487 488 waveOutReset(PlaySoundList->hWave); 489 LeaveCriticalSection(&WINMM_cs); 490 WaitForSingleObject(psLastEvent, INFINITE); 491 EnterCriticalSection(&WINMM_cs); 492 493 ResetEvent(psStopEvent); 494 } 495 496 if (wps) wps->lpNext = PlaySoundList; 497 PlaySoundList = wps; 498 LeaveCriticalSection(&WINMM_cs); 499 500 if (!pszSound || (fdwSound & SND_PURGE)) return TRUE; 501 502 if (fdwSound & SND_ASYNC) 503 { 504 DWORD id; 505 HANDLE handle; 506 wps->bLoop = (fdwSound & SND_LOOP) ? TRUE : FALSE; 507 if ((handle = CreateThread(NULL, 0, proc_PlaySound, wps, 0, &id)) != 0) { 508 wps->hThread = handle; 509 SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL); 510 return TRUE; 511 } 512 } 513 else return proc_PlaySound(wps); 514 515 /* error cases */ 516 PlaySound_Free(wps); 517 return FALSE; 518 } 519 520 /************************************************************************** 521 * PlaySoundA [WINMM.@] 522 */ 523 BOOL WINAPI PlaySoundA(LPCSTR pszSoundA, HMODULE hmod, DWORD fdwSound) 524 { 525 return MULTIMEDIA_PlaySound(pszSoundA, hmod, fdwSound, FALSE); 526 } 527 528 /************************************************************************** 529 * PlaySoundW [WINMM.@] 530 */ 531 BOOL WINAPI PlaySoundW(LPCWSTR pszSoundW, HMODULE hmod, DWORD fdwSound) 532 { 533 return MULTIMEDIA_PlaySound(pszSoundW, hmod, fdwSound, TRUE); 534 } 535 536 /************************************************************************** 537 * sndPlaySoundA [WINMM.@] 538 */ 539 BOOL WINAPI sndPlaySoundA(LPCSTR pszSoundA, UINT uFlags) 540 { 541 uFlags &= SND_ALIAS_ID|SND_FILENAME|SND_ASYNC|SND_LOOP|SND_MEMORY|SND_NODEFAULT|SND_NOSTOP|SND_SYNC; 542 return MULTIMEDIA_PlaySound(pszSoundA, 0, uFlags, FALSE); 543 } 544 545 /************************************************************************** 546 * sndPlaySoundW [WINMM.@] 547 */ 548 BOOL WINAPI sndPlaySoundW(LPCWSTR pszSound, UINT uFlags) 549 { 550 uFlags &= SND_ALIAS_ID|SND_FILENAME|SND_ASYNC|SND_LOOP|SND_MEMORY|SND_NODEFAULT|SND_NOSTOP|SND_SYNC; 551 return MULTIMEDIA_PlaySound(pszSound, 0, uFlags, TRUE); 552 } 553 554 /************************************************************************** 555 * mmsystemGetVersion [WINMM.@] 556 */ 557 UINT WINAPI mmsystemGetVersion(void) 558 { 559 TRACE("3.10 (Win95?)\n"); 560 return 0x030a; 561 } 562