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 */
PlaySound_ExpandEnvironmentStrings(LPCWSTR lpSrc,LPWSTR lpDst,DWORD nSize)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
get_mmioFromFile(LPCWSTR lpszName)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
get_mmioFromProfile(UINT uFlags,LPCWSTR lpszName)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
PlaySound_GetMMIO(LPCWSTR pszSound,HMODULE hMod,DWORD fdwSound)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
PlaySound_Callback(HWAVEOUT hwo,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)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
PlaySound_WaitDone(struct playsound_data * s)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
PlaySound_IsString(DWORD fdwSound,const void * psz)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
PlaySound_Free(WINE_PLAYSOUND * wps)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
PlaySound_AllocAndGetMMIO(const void * pszSound,HMODULE hmod,DWORD fdwSound,BOOL bUnicode)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
proc_PlaySound(WINE_PLAYSOUND * wps)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
PlaySoundAsyncThreadProc(LPVOID lpParameter)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
proc_PlaySoundAsync(WINE_PLAYSOUND * wps)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
MULTIMEDIA_PlaySound(const void * pszSound,HMODULE hmod,DWORD fdwSound,BOOL bUnicode)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 */
PlaySoundA(LPCSTR pszSoundA,HMODULE hmod,DWORD fdwSound)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 */
PlaySoundW(LPCWSTR pszSoundW,HMODULE hmod,DWORD fdwSound)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 */
sndPlaySoundA(LPCSTR pszSoundA,UINT uFlags)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 */
sndPlaySoundW(LPCWSTR pszSound,UINT uFlags)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 */
mmsystemGetVersion(void)735 UINT WINAPI mmsystemGetVersion(void)
736 {
737 TRACE("3.10 (Win95?)\n");
738 return 0x030a;
739 }
740