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 * Modified for use with ReactOS by Thamatip Chitpong, 2023 10 * 11 * This library is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU Lesser General Public 13 * License as published by the Free Software Foundation; either 14 * version 2.1 of the License, or (at your option) any later version. 15 * 16 * This library is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * Lesser General Public License for more details. 20 * 21 * You should have received a copy of the GNU Lesser General Public 22 * License along with this library; if not, write to the Free Software 23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 24 */ 25 26 #include "winemm.h" 27 28 #include <winternl.h> 29 30 WINE_DEFAULT_DEBUG_CHANNEL(winmm); 31 32 typedef struct tagWINE_PLAYSOUND 33 { 34 unsigned bLoop : 1; 35 HMMIO hmmio; 36 HWAVEOUT hWave; 37 } WINE_PLAYSOUND; 38 39 static WINE_PLAYSOUND *PlaySoundCurrent; 40 static BOOL bPlaySoundStop; 41 42 static HMMIO get_mmioFromFile(LPCWSTR lpszName) 43 { 44 HMMIO ret; 45 WCHAR buf[256]; 46 LPWSTR dummy; 47 48 ret = mmioOpenW((LPWSTR)lpszName, NULL, 49 MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE); 50 if (ret != 0) return ret; 51 if (SearchPathW(NULL, lpszName, L".wav", ARRAY_SIZE(buf), buf, &dummy)) 52 { 53 return mmioOpenW(buf, NULL, 54 MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE); 55 } 56 return 0; 57 } 58 59 static HMMIO get_mmioFromProfile(UINT uFlags, LPCWSTR lpszName) 60 { 61 WCHAR str[128]; 62 LPWSTR ptr, pszSnd; 63 HMMIO hmmio; 64 HKEY hUserKey, hRegSnd, hRegApp, hScheme, hSnd; 65 DWORD err, type, count; 66 BOOL bIsDefault; 67 68 TRACE("searching in SystemSound list for %s\n", debugstr_w(lpszName)); 69 70 bIsDefault = (_wcsicmp(lpszName, L"SystemDefault") == 0); 71 72 GetProfileStringW(L"Sounds", 73 bIsDefault ? L"Default" : lpszName, 74 L"", 75 str, 76 ARRAY_SIZE(str)); 77 if (!*str) 78 goto Next; 79 80 for (ptr = str; *ptr && *ptr != L','; ptr++); 81 82 if (*ptr) 83 *ptr = UNICODE_NULL; 84 85 hmmio = mmioOpenW(str, NULL, MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE); 86 if (hmmio) 87 return hmmio; 88 89 Next: 90 /* we look up the registry under 91 * HKCU\AppEvents\Schemes\Apps\.Default 92 * HKCU\AppEvents\Schemes\Apps\<AppName> 93 */ 94 err = RegOpenCurrentUser(KEY_READ, &hUserKey); 95 if (err == ERROR_SUCCESS) 96 { 97 err = RegOpenKeyW(hUserKey, L"AppEvents\\Schemes\\Apps", &hRegSnd); 98 RegCloseKey(hUserKey); 99 } 100 101 if (err != ERROR_SUCCESS) 102 goto None; 103 104 if (uFlags & SND_APPLICATION) 105 { 106 DWORD len; 107 108 err = ERROR_FILE_NOT_FOUND; /* error */ 109 len = GetModuleFileNameW(NULL, str, ARRAY_SIZE(str)); 110 if (len > 0 && len < ARRAY_SIZE(str)) 111 { 112 for (ptr = str + lstrlenW(str) - 1; ptr >= str; ptr--) 113 { 114 if (*ptr == L'.') 115 *ptr = UNICODE_NULL; 116 117 if (*ptr == L'\\') 118 { 119 err = RegOpenKeyW(hRegSnd, ptr + 1, &hRegApp); 120 break; 121 } 122 } 123 } 124 } 125 else 126 { 127 err = RegOpenKeyW(hRegSnd, L".Default", &hRegApp); 128 } 129 130 RegCloseKey(hRegSnd); 131 132 if (err != ERROR_SUCCESS) 133 goto None; 134 135 err = RegOpenKeyW(hRegApp, 136 bIsDefault ? L".Default" : lpszName, 137 &hScheme); 138 139 RegCloseKey(hRegApp); 140 141 if (err != ERROR_SUCCESS) 142 goto None; 143 144 err = RegOpenKeyW(hScheme, L".Current", &hSnd); 145 146 RegCloseKey(hScheme); 147 148 if (err != ERROR_SUCCESS) 149 goto None; 150 151 count = sizeof(str); 152 err = RegQueryValueExW(hSnd, NULL, 0, &type, (LPBYTE)str, &count); 153 154 RegCloseKey(hSnd); 155 156 if (err != ERROR_SUCCESS || !*str) 157 goto None; 158 159 if (type == REG_EXPAND_SZ) 160 { 161 count = ExpandEnvironmentStringsW(str, NULL, 0); 162 if (count == 0) 163 goto None; 164 165 pszSnd = HeapAlloc(GetProcessHeap(), 0, count * sizeof(WCHAR)); 166 if (!pszSnd) 167 goto None; 168 169 if (ExpandEnvironmentStringsW(str, pszSnd, count) == 0) 170 { 171 HeapFree(GetProcessHeap(), 0, pszSnd); 172 goto None; 173 } 174 } 175 else if (type == REG_SZ) 176 { 177 /* The type is REG_SZ, no need to expand */ 178 pszSnd = str; 179 } 180 else 181 { 182 /* Invalid type */ 183 goto None; 184 } 185 186 hmmio = mmioOpenW(pszSnd, NULL, MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE); 187 188 if (type == REG_EXPAND_SZ) 189 HeapFree(GetProcessHeap(), 0, pszSnd); 190 191 if (hmmio) 192 return hmmio; 193 194 None: 195 WARN("can't find SystemSound=%s !\n", debugstr_w(lpszName)); 196 return NULL; 197 } 198 199 static HMMIO PlaySound_GetMMIO(LPCWSTR pszSound, HMODULE hMod, DWORD fdwSound) 200 { 201 BOOL bIsDefault = FALSE; 202 HMMIO hmmio = NULL; 203 204 TRACE("SoundName=%s !\n", debugstr_w(pszSound)); 205 206 if (fdwSound & SND_MEMORY) 207 { 208 PVOID data; 209 MMIOINFO mminfo; 210 211 /* NOTE: SND_RESOURCE has the SND_MEMORY bit set */ 212 if ((fdwSound & SND_RESOURCE) == SND_RESOURCE) 213 { 214 HRSRC hRes; 215 HGLOBAL hGlob; 216 217 hRes = FindResourceW(hMod, pszSound, L"WAVE"); 218 hGlob = LoadResource(hMod, hRes); 219 if (!hRes || !hGlob) 220 goto Quit; 221 222 data = LockResource(hGlob); 223 FreeResource(hGlob); 224 if (!data) 225 goto Quit; 226 } 227 else 228 { 229 data = (PVOID)pszSound; 230 } 231 232 ZeroMemory(&mminfo, sizeof(mminfo)); 233 mminfo.fccIOProc = FOURCC_MEM; 234 mminfo.pchBuffer = data; 235 mminfo.cchBuffer = -1; /* FIXME: when a resource, could grab real size */ 236 237 TRACE("Memory sound %p\n", data); 238 239 hmmio = mmioOpenW(NULL, &mminfo, MMIO_READ); 240 } 241 else if (fdwSound & SND_ALIAS) 242 { 243 LPCWSTR pszName; 244 245 /* NOTE: SND_ALIAS_ID has the SND_ALIAS bit set */ 246 if ((fdwSound & SND_ALIAS_ID) == SND_ALIAS_ID) 247 { 248 if (pszSound == (LPCWSTR)SND_ALIAS_SYSTEMASTERISK) 249 pszName = L"SystemAsterisk"; 250 else if (pszSound == (LPCWSTR)SND_ALIAS_SYSTEMDEFAULT) 251 pszName = L"SystemDefault"; 252 else if (pszSound == (LPCWSTR)SND_ALIAS_SYSTEMEXCLAMATION) 253 pszName = L"SystemExclamation"; 254 else if (pszSound == (LPCWSTR)SND_ALIAS_SYSTEMEXIT) 255 pszName = L"SystemExit"; 256 else if (pszSound == (LPCWSTR)SND_ALIAS_SYSTEMHAND) 257 pszName = L"SystemHand"; 258 else if (pszSound == (LPCWSTR)SND_ALIAS_SYSTEMQUESTION) 259 pszName = L"SystemQuestion"; 260 else if (pszSound == (LPCWSTR)SND_ALIAS_SYSTEMSTART) 261 pszName = L"SystemStart"; 262 else if (pszSound == (LPCWSTR)SND_ALIAS_SYSTEMWELCOME) 263 pszName = L"SystemWelcome"; 264 else 265 goto Quit; 266 } 267 else 268 { 269 pszName = pszSound; 270 } 271 272 bIsDefault = (_wcsicmp(pszName, L"SystemDefault") == 0); 273 hmmio = get_mmioFromProfile(fdwSound, pszName); 274 } 275 else if (fdwSound & SND_FILENAME) 276 { 277 hmmio = get_mmioFromFile(pszSound); 278 } 279 else 280 { 281 hmmio = get_mmioFromProfile(fdwSound, pszSound); 282 if (!hmmio) 283 hmmio = get_mmioFromFile(pszSound); 284 } 285 286 Quit: 287 if (!hmmio && !(fdwSound & SND_NODEFAULT)) 288 { 289 if (fdwSound & SND_APPLICATION) 290 { 291 if (!bIsDefault) 292 { 293 /* Find application-defined default sound */ 294 hmmio = get_mmioFromProfile(fdwSound, L"SystemDefault"); 295 if (hmmio) 296 return hmmio; 297 } 298 299 /* Find system default sound */ 300 hmmio = get_mmioFromProfile(fdwSound & ~SND_APPLICATION, L"SystemDefault"); 301 } 302 else if (!bIsDefault) 303 { 304 hmmio = get_mmioFromProfile(fdwSound, L"SystemDefault"); 305 } 306 } 307 308 return hmmio; 309 } 310 311 struct playsound_data 312 { 313 HANDLE hEvent; 314 LONG dwEventCount; 315 }; 316 317 static void CALLBACK PlaySound_Callback(HWAVEOUT hwo, UINT uMsg, 318 DWORD_PTR dwInstance, 319 DWORD_PTR dwParam1, DWORD_PTR dwParam2) 320 { 321 struct playsound_data* s = (struct playsound_data*)dwInstance; 322 323 switch (uMsg) { 324 case WOM_OPEN: 325 case WOM_CLOSE: 326 break; 327 case WOM_DONE: 328 InterlockedIncrement(&s->dwEventCount); 329 TRACE("Returning waveHdr=%lx\n", dwParam1); 330 SetEvent(s->hEvent); 331 break; 332 default: 333 ERR("Unknown uMsg=%d\n", uMsg); 334 } 335 } 336 337 static void PlaySound_WaitDone(struct playsound_data* s) 338 { 339 for (;;) { 340 if (InterlockedDecrement(&s->dwEventCount) >= 0) break; 341 InterlockedIncrement(&s->dwEventCount); 342 343 WaitForSingleObject(s->hEvent, INFINITE); 344 } 345 } 346 347 static BOOL PlaySound_IsString(DWORD fdwSound, const void* psz) 348 { 349 /* SND_RESOURCE is 0x40004 while 350 * SND_MEMORY is 0x00004 351 */ 352 switch (fdwSound & (SND_RESOURCE | SND_ALIAS_ID | SND_FILENAME)) 353 { 354 case SND_RESOURCE: 355 return HIWORD(psz) != 0; /* by name or by ID ? */ 356 357 case SND_ALIAS_ID: 358 case SND_MEMORY: 359 return FALSE; 360 361 case SND_ALIAS: 362 case SND_FILENAME: 363 case 0: 364 return TRUE; 365 366 default: 367 FIXME("WTF\n"); 368 return FALSE; 369 } 370 } 371 372 static void PlaySound_Free(WINE_PLAYSOUND* wps) 373 { 374 EnterCriticalSection(&WINMM_cs); 375 PlaySoundCurrent = NULL; 376 SetEvent(psLastEvent); 377 LeaveCriticalSection(&WINMM_cs); 378 if (wps->hmmio) mmioClose(wps->hmmio, 0); 379 HeapFree(GetProcessHeap(), 0, wps); 380 } 381 382 static WINE_PLAYSOUND* PlaySound_AllocAndGetMMIO(const void* pszSound, HMODULE hmod, 383 DWORD fdwSound, BOOL bUnicode) 384 { 385 BOOL bIsString; 386 LPCWSTR pszSoundW; 387 UNICODE_STRING usBuffer; 388 WINE_PLAYSOUND* wps; 389 390 bIsString = PlaySound_IsString(fdwSound, pszSound); 391 392 if (bIsString && !bUnicode) 393 { 394 RtlCreateUnicodeStringFromAsciiz(&usBuffer, pszSound); 395 if (!usBuffer.Buffer) 396 return NULL; 397 398 pszSoundW = usBuffer.Buffer; 399 } 400 else 401 { 402 pszSoundW = pszSound; 403 } 404 405 wps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wps)); 406 if (wps) 407 { 408 /* construct an MMIO stream (either in memory, or from a file) */ 409 wps->hmmio = PlaySound_GetMMIO(pszSoundW, hmod, fdwSound); 410 if (!wps->hmmio) 411 { 412 PlaySound_Free(wps); 413 wps = NULL; 414 } 415 } 416 417 if (bIsString && !bUnicode) 418 RtlFreeUnicodeString(&usBuffer); 419 420 return wps; 421 } 422 423 static BOOL proc_PlaySound(WINE_PLAYSOUND *wps) 424 { 425 BOOL bRet = FALSE; 426 MMCKINFO ckMainRIFF; 427 MMCKINFO mmckInfo; 428 LPWAVEFORMATEX lpWaveFormat = NULL; 429 HWAVEOUT hWave = 0; 430 LPWAVEHDR waveHdr = NULL; 431 INT count, bufsize, left, index; 432 struct playsound_data s; 433 LONG r; 434 435 s.hEvent = 0; 436 437 if (mmioDescend(wps->hmmio, &ckMainRIFF, NULL, 0)) 438 goto errCleanUp; 439 440 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n", 441 (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize); 442 443 if ((ckMainRIFF.ckid != FOURCC_RIFF) || 444 (ckMainRIFF.fccType != mmioFOURCC('W', 'A', 'V', 'E'))) 445 goto errCleanUp; 446 447 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' '); 448 if (mmioDescend(wps->hmmio, &mmckInfo, &ckMainRIFF, MMIO_FINDCHUNK)) 449 goto errCleanUp; 450 451 TRACE("Chunk Found ckid=%.4s fccType=%08x cksize=%08X\n", 452 (LPSTR)&mmckInfo.ckid, mmckInfo.fccType, mmckInfo.cksize); 453 454 lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize); 455 if (!lpWaveFormat) 456 goto errCleanUp; 457 r = mmioRead(wps->hmmio, (HPSTR)lpWaveFormat, mmckInfo.cksize); 458 if (r < 0 || r < sizeof(PCMWAVEFORMAT)) 459 goto errCleanUp; 460 461 TRACE("wFormatTag=%04X !\n", lpWaveFormat->wFormatTag); 462 TRACE("nChannels=%d\n", lpWaveFormat->nChannels); 463 TRACE("nSamplesPerSec=%d\n", lpWaveFormat->nSamplesPerSec); 464 TRACE("nAvgBytesPerSec=%d\n", lpWaveFormat->nAvgBytesPerSec); 465 TRACE("nBlockAlign=%d\n", lpWaveFormat->nBlockAlign); 466 TRACE("wBitsPerSample=%u !\n", lpWaveFormat->wBitsPerSample); 467 468 /* move to end of 'fmt ' chunk */ 469 mmioAscend(wps->hmmio, &mmckInfo, 0); 470 471 mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a'); 472 if (mmioDescend(wps->hmmio, &mmckInfo, &ckMainRIFF, MMIO_FINDCHUNK)) 473 goto errCleanUp; 474 475 TRACE("Chunk Found ckid=%.4s fccType=%08x cksize=%08X\n", 476 (LPSTR)&mmckInfo.ckid, mmckInfo.fccType, mmckInfo.cksize); 477 478 s.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); 479 if (!s.hEvent || bPlaySoundStop) 480 goto errCleanUp; 481 482 if (waveOutOpen(&hWave, WAVE_MAPPER, lpWaveFormat, (DWORD_PTR)PlaySound_Callback, 483 (DWORD_PTR)&s, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) 484 goto errCleanUp; 485 486 /* make it so that 3 buffers per second are needed */ 487 bufsize = (((lpWaveFormat->nAvgBytesPerSec / 3) - 1) / lpWaveFormat->nBlockAlign + 1) * 488 lpWaveFormat->nBlockAlign; 489 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize); 490 if (!waveHdr) 491 goto errCleanUp; 492 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR); 493 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize; 494 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L; 495 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L; 496 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L; 497 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize; 498 if (waveOutPrepareHeader(hWave, &waveHdr[0], sizeof(WAVEHDR)) || 499 waveOutPrepareHeader(hWave, &waveHdr[1], sizeof(WAVEHDR))) { 500 goto errCleanUp; 501 } 502 503 wps->hWave = hWave; 504 s.dwEventCount = 1L; /* for first buffer */ 505 index = 0; 506 507 do { 508 left = mmckInfo.cksize; 509 510 mmioSeek(wps->hmmio, mmckInfo.dwDataOffset, SEEK_SET); 511 while (left) 512 { 513 if (bPlaySoundStop) 514 { 515 wps->bLoop = FALSE; 516 break; 517 } 518 count = mmioRead(wps->hmmio, waveHdr[index].lpData, min(bufsize, left)); 519 if (count < 1) break; 520 left -= count; 521 waveHdr[index].dwBufferLength = count; 522 if (waveOutWrite(hWave, &waveHdr[index], sizeof(WAVEHDR)) == MMSYSERR_NOERROR) { 523 index ^= 1; 524 PlaySound_WaitDone(&s); 525 } 526 else { 527 ERR("Aborting play loop, waveOutWrite error\n"); 528 wps->bLoop = FALSE; 529 break; 530 } 531 } 532 bRet = TRUE; 533 } while (wps->bLoop); 534 535 PlaySound_WaitDone(&s); /* to balance first buffer */ 536 waveOutReset(hWave); 537 538 waveOutUnprepareHeader(hWave, &waveHdr[0], sizeof(WAVEHDR)); 539 waveOutUnprepareHeader(hWave, &waveHdr[1], sizeof(WAVEHDR)); 540 541 errCleanUp: 542 TRACE("Done playing sound => %s!\n", bRet ? "ok" : "ko"); 543 HeapFree(GetProcessHeap(), 0, lpWaveFormat); 544 if (hWave) 545 { 546 EnterCriticalSection(&WINMM_cs); 547 /* the CS prevents a concurrent waveOutReset */ 548 wps->hWave = 0; 549 LeaveCriticalSection(&WINMM_cs); 550 while (waveOutClose(hWave) == WAVERR_STILLPLAYING) 551 Sleep(100); 552 } 553 CloseHandle(s.hEvent); 554 HeapFree(GetProcessHeap(), 0, waveHdr); 555 556 PlaySound_Free(wps); 557 558 return bRet; 559 } 560 561 static DWORD WINAPI PlaySoundAsyncThreadProc(LPVOID lpParameter) 562 { 563 WINE_PLAYSOUND *wps = (WINE_PLAYSOUND*)lpParameter; 564 565 /* Play the sound */ 566 proc_PlaySound(wps); 567 568 return 0; 569 } 570 571 static BOOL proc_PlaySoundAsync(WINE_PLAYSOUND *wps) 572 { 573 HANDLE hThread; 574 575 /* Create a thread to play the sound asynchronously */ 576 hThread = CreateThread(NULL, 0, PlaySoundAsyncThreadProc, wps, 0, NULL); 577 if (hThread) 578 { 579 SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL); 580 CloseHandle(hThread); 581 return TRUE; 582 } 583 584 /* Error cases */ 585 PlaySound_Free(wps); 586 return FALSE; 587 } 588 589 static BOOL MULTIMEDIA_PlaySound(const void* pszSound, HMODULE hmod, DWORD fdwSound, BOOL bUnicode) 590 { 591 WINE_PLAYSOUND* wps = NULL; 592 593 TRACE("pszSound='%p' hmod=%p fdwSound=%08X\n", 594 pszSound, hmod, fdwSound); 595 596 /* SND_NOWAIT is ignored in w95/2k/xp. */ 597 if ((fdwSound & SND_NOSTOP) && PlaySoundCurrent != NULL) 598 return FALSE; 599 600 /* alloc internal structure, if we need to play something */ 601 if (pszSound && !(fdwSound & SND_PURGE)) 602 { 603 if (!(wps = PlaySound_AllocAndGetMMIO(pszSound, hmod, fdwSound, bUnicode))) 604 return FALSE; 605 } 606 607 EnterCriticalSection(&WINMM_cs); 608 /* since several threads can enter PlaySound in parallel, we're not 609 * sure, at this point, that another thread didn't start a new playsound 610 */ 611 while (PlaySoundCurrent != NULL) 612 { 613 ResetEvent(psLastEvent); 614 /* FIXME: doc says we have to stop all instances of pszSound if it's non 615 * NULL... as of today, we stop all playing instances */ 616 bPlaySoundStop = TRUE; 617 if (PlaySoundCurrent->hWave) 618 waveOutReset(PlaySoundCurrent->hWave); 619 620 LeaveCriticalSection(&WINMM_cs); 621 WaitForSingleObject(psLastEvent, INFINITE); 622 EnterCriticalSection(&WINMM_cs); 623 624 bPlaySoundStop = FALSE; 625 } 626 627 PlaySoundCurrent = wps; 628 LeaveCriticalSection(&WINMM_cs); 629 630 if (!wps) return TRUE; 631 632 if (fdwSound & SND_ASYNC) 633 { 634 wps->bLoop = (fdwSound & SND_LOOP) ? TRUE : FALSE; 635 636 return proc_PlaySoundAsync(wps); 637 } 638 639 return proc_PlaySound(wps); 640 } 641 642 /************************************************************************** 643 * PlaySoundA [WINMM.@] 644 */ 645 BOOL WINAPI PlaySoundA(LPCSTR pszSoundA, HMODULE hmod, DWORD fdwSound) 646 { 647 return MULTIMEDIA_PlaySound(pszSoundA, hmod, fdwSound, FALSE); 648 } 649 650 /************************************************************************** 651 * PlaySoundW [WINMM.@] 652 */ 653 BOOL WINAPI PlaySoundW(LPCWSTR pszSoundW, HMODULE hmod, DWORD fdwSound) 654 { 655 return MULTIMEDIA_PlaySound(pszSoundW, hmod, fdwSound, TRUE); 656 } 657 658 /************************************************************************** 659 * sndPlaySoundA [WINMM.@] 660 */ 661 BOOL WINAPI sndPlaySoundA(LPCSTR pszSoundA, UINT uFlags) 662 { 663 uFlags &= SND_RESOURCE|SND_ALIAS_ID|SND_FILENAME|SND_ASYNC|SND_LOOP|SND_MEMORY|SND_NODEFAULT|SND_NOSTOP|SND_SYNC; 664 return MULTIMEDIA_PlaySound(pszSoundA, 0, uFlags, FALSE); 665 } 666 667 /************************************************************************** 668 * sndPlaySoundW [WINMM.@] 669 */ 670 BOOL WINAPI sndPlaySoundW(LPCWSTR pszSound, UINT uFlags) 671 { 672 uFlags &= SND_RESOURCE|SND_ALIAS_ID|SND_FILENAME|SND_ASYNC|SND_LOOP|SND_MEMORY|SND_NODEFAULT|SND_NOSTOP|SND_SYNC; 673 return MULTIMEDIA_PlaySound(pszSound, 0, uFlags, TRUE); 674 } 675 676 /************************************************************************** 677 * mmsystemGetVersion [WINMM.@] 678 */ 679 UINT WINAPI mmsystemGetVersion(void) 680 { 681 TRACE("3.10 (Win95?)\n"); 682 return 0x030a; 683 } 684