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