playsound.c 18.7 KB
Newer Older
1 2 3
/* -*- tab-width: 8; c-basic-offset: 4 -*- */

/*
4
 * MMSYSTEM functions
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * Copyright 1993      Martin Ayotte
 *           1998-2002 Eric Pouech
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 <stdarg.h>
25 26
#include <string.h>

27
#include "windef.h"
28
#include "winbase.h"
29
#include "mmsystem.h"
30 31 32 33
#include "wingdi.h"
#include "winuser.h"
#include "winreg.h"
#include "winemm.h"
34
#include "winternl.h"
35 36 37 38 39

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(winmm);

40 41 42 43 44 45 46 47 48 49 50
typedef struct tagWINE_PLAYSOUND
{
    unsigned                    bLoop : 1,
                                bAlloc : 1;
    LPCWSTR                     pszSound;
    HMODULE                     hMod;
    DWORD                       fdwSound;
    struct tagWINE_PLAYSOUND*   lpNext;
} WINE_PLAYSOUND;

static WINE_PLAYSOUND *PlaySoundList;
51
static BOOL bPlaySoundStop;
52

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
static HMMIO	get_mmioFromFile(LPCWSTR lpszName)
{
    HMMIO       ret;
    WCHAR       buf[256];
    LPWSTR      dummy;

    ret = mmioOpenW((LPWSTR)lpszName, NULL,
                    MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE);
    if (ret != 0) return ret;
    if (SearchPathW(NULL, lpszName, NULL, sizeof(buf)/sizeof(buf[0]), buf, &dummy))
    {
        return mmioOpenW(buf, NULL,
                         MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE);
    }
    return 0;
}

static HMMIO	get_mmioFromProfile(UINT uFlags, LPCWSTR lpszName)
{
    WCHAR	str[128];
    LPWSTR	ptr;
    HMMIO  	hmmio;
    HKEY        hRegSnd, hRegApp, hScheme, hSnd;
    DWORD       err, type, count;

78 79 80
    static const WCHAR  wszSounds[] = {'S','o','u','n','d','s',0};
    static const WCHAR  wszDefault[] = {'D','e','f','a','u','l','t',0};
    static const WCHAR  wszKey[] = {'A','p','p','E','v','e','n','t','s','\\',
81 82
                                    'S','c','h','e','m','e','s','\\',
                                    'A','p','p','s',0};
83
    static const WCHAR  wszDotDefault[] = {'.','D','e','f','a','u','l','t',0};
Mike McCormack's avatar
Mike McCormack committed
84
    static const WCHAR  wszDotCurrent[] = {'.','C','u','r','r','e','n','t',0};
85
    static const WCHAR  wszNull[] = {0};
86 87

    TRACE("searching in SystemSound list for %s\n", debugstr_w(lpszName));
88
    GetProfileStringW(wszSounds, lpszName, wszNull, str, sizeof(str)/sizeof(str[0]));
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
    if (lstrlenW(str) == 0)
    {
	if (uFlags & SND_NODEFAULT) goto next;
	GetProfileStringW(wszSounds, wszDefault, wszNull, str, sizeof(str)/sizeof(str[0]));
	if (lstrlenW(str) == 0) goto next;
    }
    for (ptr = str; *ptr && *ptr != ','; ptr++);
    if (*ptr) *ptr = 0;
    hmmio = mmioOpenW(str, NULL, MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE);
    if (hmmio != 0) return hmmio;
 next:
    /* we look up the registry under
     *      HKCU\AppEvents\Schemes\Apps\.Default
     *      HKCU\AppEvents\Schemes\Apps\<AppName>
     */
    if (RegOpenKeyW(HKEY_CURRENT_USER, wszKey, &hRegSnd) != 0) goto none;
    if (uFlags & SND_APPLICATION)
    {
107 108
        DWORD len;

109
        err = 1; /* error */
110 111
        len = GetModuleFileNameW(0, str, sizeof(str)/sizeof(str[0]));
        if (len > 0 && len < sizeof(str)/sizeof(str[0]))
112 113 114 115 116 117
        {
            for (ptr = str + lstrlenW(str) - 1; ptr >= str; ptr--)
            {
                if (*ptr == '.') *ptr = 0;
                if (*ptr == '\\')
                {
Mike McCormack's avatar
Mike McCormack committed
118
                    err = RegOpenKeyW(hRegSnd, ptr+1, &hRegApp);
119 120 121 122 123 124 125 126 127 128 129 130 131 132
                    break;
                }
            }
        }
    }
    else
    {
        err = RegOpenKeyW(hRegSnd, wszDotDefault, &hRegApp);
    }
    RegCloseKey(hRegSnd);
    if (err != 0) goto none;
    err = RegOpenKeyW(hRegApp, lpszName, &hScheme);
    RegCloseKey(hRegApp);
    if (err != 0) goto none;
Mike McCormack's avatar
Mike McCormack committed
133
    /* what's the difference between .Current and .Default ? */
134
    err = RegOpenKeyW(hScheme, wszDotDefault, &hSnd);
Mike McCormack's avatar
Mike McCormack committed
135 136 137 138 139 140 141
    if (err != 0)
    {
        err = RegOpenKeyW(hScheme, wszDotCurrent, &hSnd);
        RegCloseKey(hScheme);
        if (err != 0)
            goto none;
    }
142 143 144 145 146 147 148
    count = sizeof(str)/sizeof(str[0]);
    err = RegQueryValueExW(hSnd, NULL, 0, &type, (LPBYTE)str, &count);
    RegCloseKey(hSnd);
    if (err != 0 || !*str) goto none;
    hmmio = mmioOpenW(str, NULL, MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE);
    if (hmmio) return hmmio;
 none:
149
    WARN("can't find SystemSound=%s !\n", debugstr_w(lpszName));
150 151 152 153 154 155
    return 0;
}

struct playsound_data
{
    HANDLE	hEvent;
156
    LONG	dwEventCount;
157 158 159
};

static void CALLBACK PlaySound_Callback(HWAVEOUT hwo, UINT uMsg,
160 161
					DWORD_PTR dwInstance,
					DWORD_PTR dwParam1, DWORD_PTR dwParam2)
162 163 164 165 166 167 168 169 170
{
    struct playsound_data*	s = (struct playsound_data*)dwInstance;

    switch (uMsg) {
    case WOM_OPEN:
    case WOM_CLOSE:
	break;
    case WOM_DONE:
	InterlockedIncrement(&s->dwEventCount);
171
	TRACE("Returning waveHdr=%lx\n", dwParam1);
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
	SetEvent(s->hEvent);
	break;
    default:
	ERR("Unknown uMsg=%d\n", uMsg);
    }
}

static void PlaySound_WaitDone(struct playsound_data* s)
{
    for (;;) {
	if (InterlockedDecrement(&s->dwEventCount) >= 0) break;
	InterlockedIncrement(&s->dwEventCount);

	WaitForSingleObject(s->hEvent, INFINITE);
    }
}

static BOOL PlaySound_IsString(DWORD fdwSound, const void* psz)
{
    /* SND_RESOURCE is 0x40004 while
     * SND_MEMORY is 0x00004
     */
194
    switch (fdwSound & (SND_RESOURCE|SND_ALIAS_ID|SND_FILENAME))
195 196
    {
    case SND_RESOURCE:  return HIWORD(psz) != 0; /* by name or by ID ? */
197
    case SND_ALIAS_ID:
198
    case SND_MEMORY:    return FALSE;
199
    case SND_ALIAS:
200
    case SND_FILENAME:
201
    case SND_ALIAS|SND_FILENAME:
202 203 204 205 206 207 208 209 210
    case 0:             return TRUE;
    default:            FIXME("WTF\n"); return FALSE;
    }
}

static void     PlaySound_Free(WINE_PLAYSOUND* wps)
{
    WINE_PLAYSOUND**    p;

211 212
    EnterCriticalSection(&WINMM_cs);
    for (p = &PlaySoundList; *p && *p != wps; p = &((*p)->lpNext));
213
    if (*p) *p = (*p)->lpNext;
214 215
    if (PlaySoundList == NULL) SetEvent(psLastEvent);
    LeaveCriticalSection(&WINMM_cs);
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
    if (wps->bAlloc) HeapFree(GetProcessHeap(), 0, (void*)wps->pszSound);
    HeapFree(GetProcessHeap(), 0, wps);
}

static WINE_PLAYSOUND*  PlaySound_Alloc(const void* pszSound, HMODULE hmod,
                                        DWORD fdwSound, BOOL bUnicode)
{
    WINE_PLAYSOUND* wps;

    wps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wps));
    if (!wps) return NULL;

    wps->hMod = hmod;
    wps->fdwSound = fdwSound;
    if (PlaySound_IsString(fdwSound, pszSound))
    {
        if (bUnicode)
        {
            if (fdwSound & SND_ASYNC)
            {
236 237 238 239
                LPWSTR sound = HeapAlloc(GetProcessHeap(), 0,
                                         (lstrlenW(pszSound)+1) * sizeof(WCHAR));
                if (!sound) goto oom_error;
                wps->pszSound = lstrcpyW(sound, pszSound);
240 241 242 243 244 245 246
                wps->bAlloc = TRUE;
            }
            else
                wps->pszSound = pszSound;
        }
        else
        {
247 248 249
            UNICODE_STRING usBuffer;
            RtlCreateUnicodeStringFromAsciiz(&usBuffer, pszSound);
            wps->pszSound = usBuffer.Buffer;
250 251 252 253 254 255 256 257 258
            if (!wps->pszSound) goto oom_error;
            wps->bAlloc = TRUE;
        }
    }
    else
        wps->pszSound = pszSound;

    return wps;
 oom_error:
259 260
    if (wps->bAlloc) HeapFree(GetProcessHeap(), 0, (void*)wps->pszSound);
    HeapFree(GetProcessHeap(), 0, wps);
261 262 263 264 265
    return NULL;
}

static DWORD WINAPI proc_PlaySound(LPVOID arg)
{
266
    WINE_PLAYSOUND*     wps = arg;
267 268 269 270 271
    BOOL		bRet = FALSE;
    HMMIO		hmmio = 0;
    MMCKINFO		ckMainRIFF;
    MMCKINFO        	mmckInfo;
    LPWAVEFORMATEX      lpWaveFormat = NULL;
272
    HWAVEOUT		hWave = 0;
273 274 275 276 277 278 279
    LPWAVEHDR		waveHdr = NULL;
    INT			count, bufsize, left, index;
    struct playsound_data	s;
    void*               data;

    s.hEvent = 0;

280
    TRACE("SoundName=%s !\n", debugstr_w(wps->pszSound));
281 282 283

    /* if resource, grab it */
    if ((wps->fdwSound & SND_RESOURCE) == SND_RESOURCE) {
284
        static const WCHAR wszWave[] = {'W','A','V','E',0};
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
        HRSRC	hRes;
        HGLOBAL	hGlob;

        if ((hRes = FindResourceW(wps->hMod, wps->pszSound, wszWave)) == 0 ||
            (hGlob = LoadResource(wps->hMod, hRes)) == 0)
            goto errCleanUp;
        if ((data = LockResource(hGlob)) == NULL) {
            FreeResource(hGlob);
            goto errCleanUp;
        }
        FreeResource(hGlob);
    } else
        data = (void*)wps->pszSound;

    /* construct an MMIO stream (either in memory, or from a file */
    if (wps->fdwSound & SND_MEMORY)
    { /* NOTE: SND_RESOURCE has the SND_MEMORY bit set */
	MMIOINFO	mminfo;

	memset(&mminfo, 0, sizeof(mminfo));
	mminfo.fccIOProc = FOURCC_MEM;
306
	mminfo.pchBuffer = data;
307 308 309 310
	mminfo.cchBuffer = -1; /* FIXME: when a resource, could grab real size */
	TRACE("Memory sound %p\n", data);
	hmmio = mmioOpenW(NULL, &mminfo, MMIO_READ);
    }
311
    if (!hmmio && wps->fdwSound & SND_ALIAS)
312
    {
313
        if ((wps->fdwSound & SND_ALIAS_ID) == SND_ALIAS_ID)
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
        {
            static const WCHAR  wszSystemAsterisk[] = {'S','y','s','t','e','m','A','s','t','e','r','i','s','k',0};
            static const WCHAR  wszSystemDefault[] = {'S','y','s','t','e','m','D','e','f','a','u','l','t',0};
            static const WCHAR  wszSystemExclamation[] = {'S','y','s','t','e','m','E','x','c','l','a','m','a','t','i','o','n',0};
            static const WCHAR  wszSystemExit[] = {'S','y','s','t','e','m','E','x','i','t',0};
            static const WCHAR  wszSystemHand[] = {'S','y','s','t','e','m','H','a','n','d',0};
            static const WCHAR  wszSystemQuestion[] = {'S','y','s','t','e','m','Q','u','e','s','t','i','o','n',0};
            static const WCHAR  wszSystemStart[] = {'S','y','s','t','e','m','S','t','a','r','t',0};
            static const WCHAR  wszSystemWelcome[] = {'S','y','s','t','e','m','W','e','l','c','o','m','e',0};

            wps->fdwSound &= ~(SND_ALIAS_ID ^ SND_ALIAS);
            if (wps->pszSound == (LPCWSTR)SND_ALIAS_SYSTEMASTERISK)
                wps->pszSound = wszSystemAsterisk;
            else if (wps->pszSound == (LPCWSTR)SND_ALIAS_SYSTEMDEFAULT)
                wps->pszSound = wszSystemDefault;
            else if (wps->pszSound == (LPCWSTR)SND_ALIAS_SYSTEMEXCLAMATION)
                wps->pszSound = wszSystemExclamation;
            else if (wps->pszSound == (LPCWSTR)SND_ALIAS_SYSTEMEXIT)
                wps->pszSound = wszSystemExit;
            else if (wps->pszSound == (LPCWSTR)SND_ALIAS_SYSTEMHAND)
                wps->pszSound = wszSystemHand;
            else if (wps->pszSound == (LPCWSTR)SND_ALIAS_SYSTEMQUESTION)
                wps->pszSound = wszSystemQuestion;
            else if (wps->pszSound == (LPCWSTR)SND_ALIAS_SYSTEMSTART)
                wps->pszSound = wszSystemStart;
            else if (wps->pszSound == (LPCWSTR)SND_ALIAS_SYSTEMWELCOME)
                wps->pszSound = wszSystemWelcome;
341
            else goto errCleanUp;
342
        }
343 344
        hmmio = get_mmioFromProfile(wps->fdwSound, wps->pszSound);
    }
345
    if (!hmmio && wps->fdwSound & SND_FILENAME)
346 347 348
    {
        hmmio = get_mmioFromFile(wps->pszSound);
    }
349
    if (!(wps->fdwSound & (SND_FILENAME|SND_ALIAS|SND_MEMORY)))
350 351 352 353 354 355 356 357 358 359 360 361 362 363
    {
        if ((hmmio = get_mmioFromProfile(wps->fdwSound | SND_NODEFAULT, wps->pszSound)) == 0)
        {
            if ((hmmio = get_mmioFromFile(wps->pszSound)) == 0)
            {
                hmmio = get_mmioFromProfile(wps->fdwSound, wps->pszSound);
            }
        }
    }
    if (hmmio == 0) goto errCleanUp;

    if (mmioDescend(hmmio, &ckMainRIFF, NULL, 0))
	goto errCleanUp;

364
    TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
365 366 367 368 369 370 371 372 373 374
	  (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize);

    if ((ckMainRIFF.ckid != FOURCC_RIFF) ||
	(ckMainRIFF.fccType != mmioFOURCC('W', 'A', 'V', 'E')))
	goto errCleanUp;

    mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
    if (mmioDescend(hmmio, &mmckInfo, &ckMainRIFF, MMIO_FINDCHUNK))
	goto errCleanUp;

375
    TRACE("Chunk Found ckid=%.4s fccType=%08x cksize=%08X\n",
376
	  (LPSTR)&mmckInfo.ckid, mmckInfo.fccType, mmckInfo.cksize);
377 378

    lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
379 380
    if (!lpWaveFormat)
	goto errCleanUp;
381
    if (mmioRead(hmmio, (HPSTR)lpWaveFormat, mmckInfo.cksize) < sizeof(PCMWAVEFORMAT))
382 383 384
	goto errCleanUp;

    TRACE("wFormatTag=%04X !\n", 	lpWaveFormat->wFormatTag);
385
    TRACE("nChannels=%d\n", 		lpWaveFormat->nChannels);
386 387
    TRACE("nSamplesPerSec=%d\n", 	lpWaveFormat->nSamplesPerSec);
    TRACE("nAvgBytesPerSec=%d\n", 	lpWaveFormat->nAvgBytesPerSec);
388
    TRACE("nBlockAlign=%d\n", 		lpWaveFormat->nBlockAlign);
389 390 391 392 393 394 395 396 397
    TRACE("wBitsPerSample=%u !\n", 	lpWaveFormat->wBitsPerSample);

    /* move to end of 'fmt ' chunk */
    mmioAscend(hmmio, &mmckInfo, 0);

    mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a');
    if (mmioDescend(hmmio, &mmckInfo, &ckMainRIFF, MMIO_FINDCHUNK))
	goto errCleanUp;

398
    TRACE("Chunk Found ckid=%.4s fccType=%08x cksize=%08X\n",
399
	  (LPSTR)&mmckInfo.ckid, mmckInfo.fccType, mmckInfo.cksize);
400

401
    s.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
402 403
    if (!s.hEvent)
	goto errCleanUp;
404

405
    if (waveOutOpen(&hWave, WAVE_MAPPER, lpWaveFormat, (DWORD_PTR)PlaySound_Callback,
406
		    (DWORD_PTR)&s, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
407 408 409 410 411 412
	goto errCleanUp;

    /* make it so that 3 buffers per second are needed */
    bufsize = (((lpWaveFormat->nAvgBytesPerSec / 3) - 1) / lpWaveFormat->nBlockAlign + 1) *
	lpWaveFormat->nBlockAlign;
    waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
413 414
    if (!waveHdr)
	goto errCleanUp;
415 416 417 418 419 420
    waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
    waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
    waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
    waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
    waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
    waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
421 422
    if (waveOutPrepareHeader(hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
	waveOutPrepareHeader(hWave, &waveHdr[1], sizeof(WAVEHDR))) {
423 424 425 426
	goto errCleanUp;
    }

    s.dwEventCount = 1L; /* for first buffer */
427
    index = 0;
428 429 430 431 432 433 434

    do {
	left = mmckInfo.cksize;

	mmioSeek(hmmio, mmckInfo.dwDataOffset, SEEK_SET);
	while (left)
        {
435
	    if (bPlaySoundStop)
436
            {
437
		waveOutReset(hWave);
438 439 440 441 442 443 444
		wps->bLoop = FALSE;
		break;
	    }
	    count = mmioRead(hmmio, waveHdr[index].lpData, min(bufsize, left));
	    if (count < 1) break;
	    left -= count;
	    waveHdr[index].dwBufferLength = count;
445
	    if (waveOutWrite(hWave, &waveHdr[index], sizeof(WAVEHDR)) == MMSYSERR_NOERROR) {
446 447 448
                index ^= 1;
                PlaySound_WaitDone(&s);
            }
449 450 451 452 453
            else {
		ERR("Aborting play loop, waveOutWrite error\n");
		wps->bLoop = FALSE;
		break;
	    }
454 455 456 457
	}
	bRet = TRUE;
    } while (wps->bLoop);

458
    PlaySound_WaitDone(&s); /* to balance first buffer */
459
    waveOutReset(hWave);
460

461 462
    waveOutUnprepareHeader(hWave, &waveHdr[0], sizeof(WAVEHDR));
    waveOutUnprepareHeader(hWave, &waveHdr[1], sizeof(WAVEHDR));
463 464

errCleanUp:
465
    TRACE("Done playing=%s => %s!\n", debugstr_w(wps->pszSound), bRet ? "ok" : "ko");
466
    HeapFree(GetProcessHeap(), 0, lpWaveFormat);
467
    if (hWave)		while (waveOutClose(hWave) == WAVERR_STILLPLAYING) Sleep(100);
468 469
    CloseHandle(s.hEvent);
    HeapFree(GetProcessHeap(), 0, waveHdr);
470 471 472 473 474 475 476
    if (hmmio) 		mmioClose(hmmio, 0);

    PlaySound_Free(wps);

    return bRet;
}

477
static BOOL MULTIMEDIA_PlaySound(const void* pszSound, HMODULE hmod, DWORD fdwSound, BOOL bUnicode)
478 479 480
{
    WINE_PLAYSOUND*     wps = NULL;

481
    TRACE("pszSound='%p' hmod=%p fdwSound=%08X\n",
482 483
	  pszSound, hmod, fdwSound);

484 485
    /* SND_NOWAIT is ignored in w95/2k/xp. */
    if ((fdwSound & SND_NOSTOP) && PlaySoundList != NULL)
486 487 488 489 490 491 492 493 494
	return FALSE;

    /* alloc internal structure, if we need to play something */
    if (pszSound && !(fdwSound & SND_PURGE))
    {
        if (!(wps = PlaySound_Alloc(pszSound, hmod, fdwSound, bUnicode)))
            return FALSE;
    }

495
    EnterCriticalSection(&WINMM_cs);
496 497 498
    /* since several threads can enter PlaySound in parallel, we're not
     * sure, at this point, that another thread didn't start a new playsound
     */
499
    while (PlaySoundList != NULL)
500
    {
501
        ResetEvent(psLastEvent);
502 503
        /* FIXME: doc says we have to stop all instances of pszSound if it's non
         * NULL... as of today, we stop all playing instances */
504
        bPlaySoundStop = TRUE;
505

506 507 508
        LeaveCriticalSection(&WINMM_cs);
        WaitForSingleObject(psLastEvent, INFINITE);
        EnterCriticalSection(&WINMM_cs);
509

510
        bPlaySoundStop = FALSE;
511 512
    }

513 514 515
    if (wps) wps->lpNext = PlaySoundList;
    PlaySoundList = wps;
    LeaveCriticalSection(&WINMM_cs);
516

517
    if (!wps) return TRUE;
518 519 520

    if (fdwSound & SND_ASYNC)
    {
Robert Reif's avatar
Robert Reif committed
521
        HANDLE      handle;
522
        wps->bLoop = (fdwSound & SND_LOOP) ? TRUE : FALSE;
523
        if ((handle = CreateThread(NULL, 0, proc_PlaySound, wps, 0, NULL)) != 0) {
524
            SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL);
525
            CloseHandle(handle);
526
            return TRUE;
Robert Reif's avatar
Robert Reif committed
527
        }
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
    }
    else return proc_PlaySound(wps);

    /* error cases */
    PlaySound_Free(wps);
    return FALSE;
}

/**************************************************************************
 * 				PlaySoundA		[WINMM.@]
 */
BOOL WINAPI PlaySoundA(LPCSTR pszSoundA, HMODULE hmod, DWORD fdwSound)
{
    return MULTIMEDIA_PlaySound(pszSoundA, hmod, fdwSound, FALSE);
}

/**************************************************************************
 * 				PlaySoundW		[WINMM.@]
 */
BOOL WINAPI PlaySoundW(LPCWSTR pszSoundW, HMODULE hmod, DWORD fdwSound)
{
    return MULTIMEDIA_PlaySound(pszSoundW, hmod, fdwSound, TRUE);
}

/**************************************************************************
 * 				sndPlaySoundA		[WINMM.@]
 */
BOOL WINAPI sndPlaySoundA(LPCSTR pszSoundA, UINT uFlags)
{
    uFlags &= SND_ASYNC|SND_LOOP|SND_MEMORY|SND_NODEFAULT|SND_NOSTOP|SND_SYNC;
    return MULTIMEDIA_PlaySound(pszSoundA, 0, uFlags, FALSE);
}

/**************************************************************************
 * 				sndPlaySoundW		[WINMM.@]
 */
BOOL WINAPI sndPlaySoundW(LPCWSTR pszSound, UINT uFlags)
{
    uFlags &= SND_ASYNC|SND_LOOP|SND_MEMORY|SND_NODEFAULT|SND_NOSTOP|SND_SYNC;
    return MULTIMEDIA_PlaySound(pszSound, 0, uFlags, TRUE);
}

/**************************************************************************
 * 				mmsystemGetVersion	[WINMM.@]
 */
UINT WINAPI mmsystemGetVersion(void)
{
    TRACE("3.10 (Win95?)\n");
    return 0x030a;
}