xref: /reactos/dll/win32/winmm/playsound.c (revision 92231340)
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