Fixed flags handling in sndPlaySound.
[wine] / dlls / winmm / mmsystem.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  * MMSYTEM functions
5  *
6  * Copyright 1993 Martin Ayotte
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 /*
24  * Eric POUECH :
25  *      98/9    added Win32 MCI support
26  *      99/4    added mmTask and mmThread functions support
27  *              added midiStream support
28  *      99/9    added support for loadable low level drivers
29  */
30
31 /* FIXME: I think there are some segmented vs. linear pointer weirdnesses
32  *        and long term pointers to 16 bit space in here
33  */
34
35 #include <string.h>
36
37 #include "mmsystem.h"
38 #include "winbase.h"
39 #include "wingdi.h"
40
41 #include "wine/mmsystem16.h"
42 #include "wine/winuser16.h"
43 #include "heap.h"
44 #include "ntddk.h"
45 #include "winemm.h"
46
47 #include "wine/debug.h"
48
49 WINE_DEFAULT_DEBUG_CHANNEL(mmsys);
50
51 static LPWINE_MM_IDATA          lpFirstIData = NULL;
52
53 static  LPWINE_MM_IDATA MULTIMEDIA_GetIDataNoCheck(void)
54 {
55     DWORD               pid = GetCurrentProcessId();
56     LPWINE_MM_IDATA     iData;
57
58     for (iData = lpFirstIData; iData; iData = iData->lpNextIData) {
59         if (iData->dwThisProcess == pid)
60             break;
61     }
62     return iData;
63 }
64
65 /**************************************************************************
66  *                      MULTIMEDIA_GetIData                     [internal]
67  */
68 LPWINE_MM_IDATA MULTIMEDIA_GetIData(void)
69 {
70     LPWINE_MM_IDATA     iData = MULTIMEDIA_GetIDataNoCheck();
71
72     if (!iData) {
73         ERR("IData not found for pid=%08lx. Suicide !!!\n", GetCurrentProcessId());
74         DbgBreakPoint();
75         ExitProcess(0);
76     }
77     return iData;
78 }
79
80 /**************************************************************************
81  *                      MULTIMEDIA_CreateIData                  [internal]
82  */
83 static  BOOL    MULTIMEDIA_CreateIData(HINSTANCE hInstDLL)
84 {
85     LPWINE_MM_IDATA     iData;
86
87     iData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MM_IDATA));
88
89     if (!iData)
90         return FALSE;
91     iData->hWinMM32Instance = hInstDLL;
92     iData->dwThisProcess = GetCurrentProcessId();
93     iData->lpNextIData = lpFirstIData;
94     lpFirstIData = iData;
95     InitializeCriticalSection(&iData->cs);
96     iData->cs.DebugInfo = (void*)__FILE__ ": WinMM";
97     iData->psStopEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
98     iData->psLastEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
99     TRACE("Created IData (%p) for pid %08lx\n", iData, iData->dwThisProcess);
100     return TRUE;
101 }
102
103 /**************************************************************************
104  *                      MULTIMEDIA_DeleteIData                  [internal]
105  */
106 static  void MULTIMEDIA_DeleteIData(void)
107 {
108     LPWINE_MM_IDATA     iData = MULTIMEDIA_GetIDataNoCheck();
109     LPWINE_MM_IDATA*    ppid;
110
111     if (iData) {
112         TIME_MMTimeStop();
113
114         for (ppid = &lpFirstIData; *ppid; ppid = &(*ppid)->lpNextIData) {
115             if (*ppid == iData) {
116                 *ppid = iData->lpNextIData;
117                 break;
118             }
119         }
120         /* FIXME: should also free content and resources allocated
121          * inside iData */
122         CloseHandle(iData->psStopEvent);
123         CloseHandle(iData->psLastEvent);
124         DeleteCriticalSection(&iData->cs);
125         HeapFree(GetProcessHeap(), 0, iData);
126     }
127 }
128
129 /**************************************************************************
130  *              DllEntryPoint (WINMM.init)
131  *
132  * WINMM DLL entry point
133  *
134  */
135 BOOL WINAPI WINMM_LibMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID fImpLoad)
136 {
137     TRACE("0x%x 0x%lx %p\n", hInstDLL, fdwReason, fImpLoad);
138
139     switch (fdwReason) {
140     case DLL_PROCESS_ATTACH:
141         if (!MULTIMEDIA_CreateIData(hInstDLL))
142             return FALSE;
143         if (!MULTIMEDIA_MciInit() || !MMDRV_Init()) {
144             MULTIMEDIA_DeleteIData();
145             return FALSE;
146         }
147         break;
148     case DLL_PROCESS_DETACH:
149         MULTIMEDIA_DeleteIData();
150         break;
151     case DLL_THREAD_ATTACH:
152     case DLL_THREAD_DETACH:
153         break;
154     }
155     return TRUE;
156 }
157
158 /**************************************************************************
159  *                      DllEntryPoint (MMSYSTEM.2046)
160  *
161  * MMSYSTEM DLL entry point
162  *
163  */
164 BOOL WINAPI MMSYSTEM_LibMain(DWORD fdwReason, HINSTANCE hinstDLL, WORD ds,
165                              WORD wHeapSize, DWORD dwReserved1, WORD wReserved2)
166 {
167     HANDLE                      hndl;
168     LPWINE_MM_IDATA             iData;
169
170     TRACE("0x%x 0x%lx\n", hinstDLL, fdwReason);
171
172     switch (fdwReason) {
173     case DLL_PROCESS_ATTACH:
174         /* need to load WinMM in order to:
175          * - initiate correctly shared variables (MULTIMEDIA_Init())
176          * - create correctly the per process WINE_MM_IDATA chunk
177          */
178         hndl = LoadLibraryA("WINMM.DLL");
179
180         if (!hndl) {
181             ERR("Could not load sibling WinMM.dll\n");
182             return FALSE;
183         }
184         iData = MULTIMEDIA_GetIData();
185         iData->hWinMM16Instance = hinstDLL;
186         iData->h16Module32 = hndl;
187         break;
188     case DLL_PROCESS_DETACH:
189         iData = MULTIMEDIA_GetIData();
190         FreeLibrary(iData->h16Module32);
191         break;
192     case DLL_THREAD_ATTACH:
193     case DLL_THREAD_DETACH:
194         break;
195     }
196     return TRUE;
197 }
198
199 /**************************************************************************
200  *                              MMSYSTEM_WEP                    [MMSYSTEM.1]
201  */
202 int WINAPI MMSYSTEM_WEP(HINSTANCE16 hInstance, WORD wDataSeg,
203                         WORD cbHeapSize, LPSTR lpCmdLine)
204 {
205     FIXME("STUB: Unloading MMSystem DLL ... hInst=%04X \n", hInstance);
206     return TRUE;
207 }
208
209 void MMSYSTEM_MMTIME32to16(LPMMTIME16 mmt16, const MMTIME* mmt32)
210 {
211     mmt16->wType = mmt32->wType;
212     /* layout of rest is the same for 32/16,
213      * Note: mmt16->u is 2 bytes smaller than mmt32->u, which has padding
214      */
215     memcpy(&(mmt16->u), &(mmt32->u), sizeof(mmt16->u));
216 }
217
218 void MMSYSTEM_MMTIME16to32(LPMMTIME mmt32, const MMTIME16* mmt16)
219 {
220     mmt32->wType = mmt16->wType;
221     /* layout of rest is the same for 32/16,
222      * Note: mmt16->u is 2 bytes smaller than mmt32->u, which has padding
223      */
224     memcpy(&(mmt32->u), &(mmt16->u), sizeof(mmt16->u));
225 }
226
227 static HMMIO    get_mmioFromFile(LPCWSTR lpszName)
228 {
229     HMMIO       ret;
230     WCHAR       buf[256];
231     LPWSTR      dummy;
232
233     ret = mmioOpenW((LPWSTR)lpszName, NULL,
234                     MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE);
235     if (ret != 0) return ret;
236     if (SearchPathW(NULL, lpszName, NULL, sizeof(buf)/sizeof(buf[0]), buf, &dummy))
237     {
238         return mmioOpenW(buf, NULL,
239                          MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE);
240     }
241     return 0;
242 }
243
244 static HMMIO    get_mmioFromProfile(UINT uFlags, LPCWSTR lpszName)
245 {
246     WCHAR       str[128];
247     LPWSTR      ptr;
248     HMMIO       hmmio;
249     HKEY        hRegSnd, hRegApp, hScheme, hSnd;
250     DWORD       err, type, count;
251
252     static  WCHAR       wszSounds[] = {'S','o','u','n','d','s',0};
253     static  WCHAR       wszDefault[] = {'D','e','f','a','u','l','t',0};
254     static  WCHAR       wszKey[] = {'A','p','p','E','v','e','n','t','s','\\',
255                                     'S','c','h','e','m','e','s','\\',
256                                     'A','p','p','s',0};
257     static  WCHAR       wszDotDefault[] = {'.','D','e','f','a','u','l','t',0};
258     static  WCHAR       wszNull[] = {0};
259
260     TRACE("searching in SystemSound list for %s\n", debugstr_w(lpszName));
261     GetProfileStringW(wszSounds, (LPWSTR)lpszName, wszNull, str, sizeof(str)/sizeof(str[0]));
262     if (lstrlenW(str) == 0)
263     {
264         if (uFlags & SND_NODEFAULT) goto next;
265         GetProfileStringW(wszSounds, wszDefault, wszNull, str, sizeof(str)/sizeof(str[0]));
266         if (lstrlenW(str) == 0) goto next;
267     }
268     for (ptr = str; *ptr && *ptr != ','; ptr++);
269     if (*ptr) *ptr = 0;
270     hmmio = mmioOpenW(str, NULL, MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE);
271     if (hmmio != 0) return hmmio;
272  next:
273     /* we look up the registry under
274      *      HKCU\AppEvents\Schemes\Apps\.Default
275      *      HKCU\AppEvents\Schemes\Apps\<AppName>
276      */
277     if (RegOpenKeyW(HKEY_CURRENT_USER, wszKey, &hRegSnd) != 0) goto none;
278     if (uFlags & SND_APPLICATION)
279     {
280         err = 1; /* error */
281         if (GetModuleFileNameW(0, str, sizeof(str)/sizeof(str[0])))
282         {
283             for (ptr = str + lstrlenW(str) - 1; ptr >= str; ptr--)
284             {
285                 if (*ptr == '.') *ptr = 0;
286                 if (*ptr == '\\')
287                 {
288                     err = RegOpenKeyW(hRegSnd, str, &hRegApp);
289                     break;
290                 }
291             }
292         }
293     }
294     else
295     {
296         err = RegOpenKeyW(hRegSnd, wszDotDefault, &hRegApp);
297     }
298     RegCloseKey(hRegSnd);
299     if (err != 0) goto none;
300     err = RegOpenKeyW(hRegApp, lpszName, &hScheme);
301     RegCloseKey(hRegApp);
302     if (err != 0) goto none;
303     err = RegOpenKeyW(hScheme, wszDotDefault, &hSnd);
304     RegCloseKey(hScheme);
305     if (err != 0) goto none;
306     count = sizeof(str)/sizeof(str[0]);
307     err = RegQueryValueExW(hSnd, NULL, 0, &type, (LPBYTE)str, &count);
308     RegCloseKey(hSnd);
309     if (err != 0 || !*str) goto none;
310     hmmio = mmioOpenW(str, NULL, MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE);
311     if (hmmio) return hmmio;
312  none:
313     WARN("can't find SystemSound='%s' !\n", debugstr_w(lpszName));
314     return 0;
315 }
316
317 struct playsound_data
318 {
319     HANDLE      hEvent;
320     DWORD       dwEventCount;
321 };
322
323 static void CALLBACK PlaySound_Callback(HWAVEOUT hwo, UINT uMsg,
324                                         DWORD dwInstance,
325                                         DWORD dwParam1, DWORD dwParam2)
326 {
327     struct playsound_data*      s = (struct playsound_data*)dwInstance;
328
329     switch (uMsg) {
330     case WOM_OPEN:
331     case WOM_CLOSE:
332         break;
333     case WOM_DONE:
334         InterlockedIncrement(&s->dwEventCount);
335         TRACE("Returning waveHdr=%lx\n", dwParam1);
336         SetEvent(s->hEvent);
337         break;
338     default:
339         ERR("Unknown uMsg=%d\n", uMsg);
340     }
341 }
342
343 static void PlaySound_WaitDone(struct playsound_data* s)
344 {
345     for (;;) {
346         ResetEvent(s->hEvent);
347         if (InterlockedDecrement(&s->dwEventCount) >= 0) break;
348         InterlockedIncrement(&s->dwEventCount);
349
350         WaitForSingleObject(s->hEvent, INFINITE);
351     }
352 }
353
354 static BOOL PlaySound_IsString(DWORD fdwSound, const void* psz)
355 {
356     /* SND_RESOURCE is 0x40004 while
357      * SND_MEMORY is 0x00004
358      */
359     switch (fdwSound & (SND_RESOURCE|SND_ALIAS|SND_FILENAME))
360     {
361     case SND_RESOURCE:  return HIWORD(psz) != 0; /* by name or by ID ? */
362     case SND_MEMORY:    return FALSE;
363     case SND_ALIAS:     /* what about ALIAS_ID ??? */
364     case SND_FILENAME:
365     case 0:             return TRUE;
366     default:            FIXME("WTF\n"); return FALSE;
367     }
368 }
369
370 static void     PlaySound_Free(WINE_PLAYSOUND* wps)
371 {
372     LPWINE_MM_IDATA     iData = MULTIMEDIA_GetIData();
373     WINE_PLAYSOUND**    p;
374
375     EnterCriticalSection(&iData->cs);
376     for (p = &iData->lpPlaySound; *p && *p != wps; p = &((*p)->lpNext));
377     if (*p) *p = (*p)->lpNext;
378     if (iData->lpPlaySound == NULL) SetEvent(iData->psLastEvent);
379     LeaveCriticalSection(&iData->cs);
380     if (wps->bAlloc) HeapFree(GetProcessHeap(), 0, (void*)wps->pszSound);
381     HeapFree(GetProcessHeap(), 0, wps);
382 }
383
384 static WINE_PLAYSOUND*  PlaySound_Alloc(const void* pszSound, HMODULE hmod,
385                                         DWORD fdwSound, BOOL bUnicode)
386 {
387     WINE_PLAYSOUND* wps;
388
389     wps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wps));
390     if (!wps) return NULL;
391
392     wps->hMod = hmod;
393     wps->fdwSound = fdwSound;
394     if (PlaySound_IsString(fdwSound, pszSound))
395     {
396         if (bUnicode)
397         {
398             if (fdwSound & SND_ASYNC)
399             {
400                 wps->pszSound = HeapAlloc(GetProcessHeap(), 0,
401                                           (lstrlenW(pszSound)+1) * sizeof(WCHAR));
402                 if (!wps->pszSound) goto oom_error;
403                 lstrcpyW((LPWSTR)wps->pszSound, pszSound);
404                 wps->bAlloc = TRUE;
405             }
406             else
407                 wps->pszSound = pszSound;
408         }
409         else
410         {
411             wps->pszSound = HEAP_strdupAtoW(GetProcessHeap(), 0, pszSound);
412             if (!wps->pszSound) goto oom_error;
413             wps->bAlloc = TRUE;
414         }
415     }
416     else
417         wps->pszSound = pszSound;
418
419     return wps;
420  oom_error:
421     PlaySound_Free(wps);
422     return NULL;
423 }
424
425 static DWORD WINAPI proc_PlaySound(LPVOID arg)
426 {
427     WINE_PLAYSOUND*     wps = (WINE_PLAYSOUND*)arg;
428     LPWINE_MM_IDATA     iData = MULTIMEDIA_GetIData();
429     BOOL                bRet = FALSE;
430     HMMIO               hmmio = 0;
431     MMCKINFO            ckMainRIFF;
432     MMCKINFO            mmckInfo;
433     LPWAVEFORMATEX      lpWaveFormat = NULL;
434     HWAVE               hWave = 0;
435     LPWAVEHDR           waveHdr = NULL;
436     INT                 count, bufsize, left, index;
437     struct playsound_data       s;
438     void*               data;
439
440     s.hEvent = 0;
441
442     TRACE("SoundName='%s' !\n", debugstr_w(wps->pszSound));
443
444     /* if resource, grab it */
445     if ((wps->fdwSound & SND_RESOURCE) == SND_RESOURCE) {
446         static WCHAR wszWave[] = {'W','A','V','E',0};
447         HRSRC   hRes;
448         HGLOBAL hGlob;
449
450         if ((hRes = FindResourceW(wps->hMod, wps->pszSound, wszWave)) == 0 ||
451             (hGlob = LoadResource(wps->hMod, hRes)) == 0)
452             goto errCleanUp;
453         if ((data = LockResource(hGlob)) == NULL) {
454             FreeResource(hGlob);
455             goto errCleanUp;
456         }
457         FreeResource(hGlob);
458     } else
459         data = (void*)wps->pszSound;
460
461     /* construct an MMIO stream (either in memory, or from a file */
462     if (wps->fdwSound & SND_MEMORY)
463     { /* NOTE: SND_RESOURCE has the SND_MEMORY bit set */
464         MMIOINFO        mminfo;
465
466         memset(&mminfo, 0, sizeof(mminfo));
467         mminfo.fccIOProc = FOURCC_MEM;
468         mminfo.pchBuffer = (LPSTR)data;
469         mminfo.cchBuffer = -1; /* FIXME: when a resource, could grab real size */
470         TRACE("Memory sound %p\n", data);
471         hmmio = mmioOpenW(NULL, &mminfo, MMIO_READ);
472     }
473     else if (wps->fdwSound & SND_ALIAS)
474     {
475         hmmio = get_mmioFromProfile(wps->fdwSound, wps->pszSound);
476     }
477     else if (wps->fdwSound & SND_FILENAME)
478     {
479         hmmio = get_mmioFromFile(wps->pszSound);
480     }
481     else
482     {
483         if ((hmmio = get_mmioFromProfile(wps->fdwSound | SND_NODEFAULT, wps->pszSound)) == 0)
484         {
485             if ((hmmio = get_mmioFromFile(wps->pszSound)) == 0)
486             {
487                 hmmio = get_mmioFromProfile(wps->fdwSound, wps->pszSound);
488             }
489         }
490     }
491     if (hmmio == 0) goto errCleanUp;
492
493     if (mmioDescend(hmmio, &ckMainRIFF, NULL, 0))
494         goto errCleanUp;
495
496     TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \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(hmmio, &mmckInfo, &ckMainRIFF, MMIO_FINDCHUNK))
505         goto errCleanUp;
506
507     TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
508           (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
509
510     lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
511     if (mmioRead(hmmio, (HPSTR)lpWaveFormat, mmckInfo.cksize) < sizeof(WAVEFORMAT))
512         goto errCleanUp;
513
514     TRACE("wFormatTag=%04X !\n",        lpWaveFormat->wFormatTag);
515     TRACE("nChannels=%d \n",            lpWaveFormat->nChannels);
516     TRACE("nSamplesPerSec=%ld\n",       lpWaveFormat->nSamplesPerSec);
517     TRACE("nAvgBytesPerSec=%ld\n",      lpWaveFormat->nAvgBytesPerSec);
518     TRACE("nBlockAlign=%d \n",          lpWaveFormat->nBlockAlign);
519     TRACE("wBitsPerSample=%u !\n",      lpWaveFormat->wBitsPerSample);
520
521     /* move to end of 'fmt ' chunk */
522     mmioAscend(hmmio, &mmckInfo, 0);
523
524     mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a');
525     if (mmioDescend(hmmio, &mmckInfo, &ckMainRIFF, MMIO_FINDCHUNK))
526         goto errCleanUp;
527
528     TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX\n",
529           (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
530
531     s.hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
532
533     if (waveOutOpen(&hWave, WAVE_MAPPER, lpWaveFormat, (DWORD)PlaySound_Callback,
534                     (DWORD)&s, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
535         goto errCleanUp;
536
537     /* make it so that 3 buffers per second are needed */
538     bufsize = (((lpWaveFormat->nAvgBytesPerSec / 3) - 1) / lpWaveFormat->nBlockAlign + 1) *
539         lpWaveFormat->nBlockAlign;
540     waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
541     waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
542     waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
543     waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
544     waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
545     waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
546     waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
547     if (waveOutPrepareHeader(hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
548         waveOutPrepareHeader(hWave, &waveHdr[1], sizeof(WAVEHDR))) {
549         goto errCleanUp;
550     }
551
552     do {
553         index = 0;
554         left = mmckInfo.cksize;
555         s.dwEventCount = 1L; /* for first buffer */
556
557         mmioSeek(hmmio, mmckInfo.dwDataOffset, SEEK_SET);
558         while (left)
559         {
560             if (WaitForSingleObject(iData->psStopEvent, 0) == WAIT_OBJECT_0)
561             {
562                 wps->bLoop = FALSE;
563                 break;
564             }
565             count = mmioRead(hmmio, waveHdr[index].lpData, min(bufsize, left));
566             if (count < 1) break;
567             left -= count;
568             waveHdr[index].dwBufferLength = count;
569             waveHdr[index].dwFlags &= ~WHDR_DONE;
570             if (waveOutWrite(hWave, &waveHdr[index], sizeof(WAVEHDR)) == MMSYSERR_NOERROR) {
571                 index ^= 1;
572                 PlaySound_WaitDone(&s);
573             }
574             else FIXME("Couldn't play header\n");
575         }
576         bRet = TRUE;
577     } while (wps->bLoop);
578
579     PlaySound_WaitDone(&s); /* for last buffer */
580     waveOutReset(hWave);
581
582     waveOutUnprepareHeader(hWave, &waveHdr[0], sizeof(WAVEHDR));
583     waveOutUnprepareHeader(hWave, &waveHdr[1], sizeof(WAVEHDR));
584
585 errCleanUp:
586     TRACE("Done playing='%s' => %s!\n", debugstr_w(wps->pszSound), bRet ? "ok" : "ko");
587     CloseHandle(s.hEvent);
588     if (waveHdr)        HeapFree(GetProcessHeap(), 0, waveHdr);
589     if (lpWaveFormat)   HeapFree(GetProcessHeap(), 0, lpWaveFormat);
590     if (hWave)          while (waveOutClose(hWave) == WAVERR_STILLPLAYING) Sleep(100);
591     if (hmmio)          mmioClose(hmmio, 0);
592
593     PlaySound_Free(wps);
594
595     return bRet;
596 }
597
598 static BOOL MULTIMEDIA_PlaySound(const void* pszSound, HMODULE hmod, DWORD fdwSound, BOOL bUnicode)
599 {
600     WINE_PLAYSOUND*     wps = NULL;
601     LPWINE_MM_IDATA     iData = MULTIMEDIA_GetIData();
602
603     TRACE("pszSound='%p' hmod=%04X fdwSound=%08lX\n",
604           pszSound, hmod, fdwSound);
605
606     /* FIXME? I see no difference between SND_NOWAIT and SND_NOSTOP !
607      * there could be one if several sounds can be played at once...
608      */
609     if ((fdwSound & (SND_NOWAIT | SND_NOSTOP)) && iData->lpPlaySound != NULL)
610         return FALSE;
611
612     /* alloc internal structure, if we need to play something */
613     if (pszSound && !(fdwSound & SND_PURGE))
614     {
615         if (!(wps = PlaySound_Alloc(pszSound, hmod, fdwSound, bUnicode)))
616             return FALSE;
617     }
618
619     EnterCriticalSection(&iData->cs);
620     /* since several threads can enter PlaySound in parallel, we're not
621      * sure, at this point, that another thread didn't start a new playsound
622      */
623     while (iData->lpPlaySound != NULL)
624     {
625         ResetEvent(iData->psLastEvent);
626         /* FIXME: doc says we have to stop all instances of pszSound if it's non
627          * NULL... as of today, we stop all playing instances */
628         SetEvent(iData->psStopEvent);
629
630         LeaveCriticalSection(&iData->cs);
631         WaitForSingleObject(iData->psLastEvent, INFINITE);
632         EnterCriticalSection(&iData->cs);
633
634         ResetEvent(iData->psStopEvent);
635     }
636
637     if (wps) wps->lpNext = iData->lpPlaySound;
638     iData->lpPlaySound = wps;
639     LeaveCriticalSection(&iData->cs);
640
641     if (!pszSound || (fdwSound & SND_PURGE)) return TRUE;
642
643     if (fdwSound & SND_ASYNC)
644     {
645         DWORD       id;
646         wps->bLoop = (fdwSound & SND_LOOP) ? TRUE : FALSE;
647         if (CreateThread(NULL, 0, proc_PlaySound, wps, 0, &id) != 0)
648             return TRUE;
649     }
650     else return proc_PlaySound(wps);
651
652     /* error cases */
653     PlaySound_Free(wps);
654     return FALSE;
655 }
656
657 /**************************************************************************
658  *                              PlaySoundA              [WINMM.@]
659  */
660 BOOL WINAPI PlaySoundA(LPCSTR pszSoundA, HMODULE hmod, DWORD fdwSound)
661 {
662     return MULTIMEDIA_PlaySound(pszSoundA, hmod, fdwSound, FALSE);
663 }
664
665 /**************************************************************************
666  *                              PlaySoundW              [WINMM.@]
667  */
668 BOOL WINAPI PlaySoundW(LPCWSTR pszSoundW, HMODULE hmod, DWORD fdwSound)
669 {
670     return MULTIMEDIA_PlaySound(pszSoundW, hmod, fdwSound, TRUE);
671 }
672
673 /**************************************************************************
674  *                              PlaySound               [MMSYSTEM.3]
675  */
676 BOOL16 WINAPI PlaySound16(LPCSTR pszSound, HMODULE16 hmod, DWORD fdwSound)
677 {
678     BOOL16      retv;
679     DWORD       lc;
680
681     ReleaseThunkLock(&lc);
682     retv = PlaySoundA(pszSound, hmod, fdwSound);
683     RestoreThunkLock(lc);
684
685     return retv;
686 }
687
688 /**************************************************************************
689  *                              sndPlaySoundA           [WINMM.@]
690  */
691 BOOL WINAPI sndPlaySoundA(LPCSTR pszSoundA, UINT uFlags)
692 {
693     uFlags &= SND_ASYNC|SND_LOOP|SND_MEMORY|SND_NODEFAULT|SND_NOSTOP|SND_SYNC;
694     return MULTIMEDIA_PlaySound(pszSoundA, 0, uFlags, FALSE);
695 }
696
697 /**************************************************************************
698  *                              sndPlaySoundW           [WINMM.@]
699  */
700 BOOL WINAPI sndPlaySoundW(LPCWSTR pszSound, UINT uFlags)
701 {
702     uFlags &= SND_ASYNC|SND_LOOP|SND_MEMORY|SND_NODEFAULT|SND_NOSTOP|SND_SYNC;
703     return MULTIMEDIA_PlaySound(pszSound, 0, uFlags, TRUE);
704 }
705
706 /**************************************************************************
707  *                              sndPlaySound            [MMSYSTEM.2]
708  */
709 BOOL16 WINAPI sndPlaySound16(LPCSTR lpszSoundName, UINT16 uFlags)
710 {
711     BOOL16      retv;
712     DWORD       lc;
713
714     ReleaseThunkLock(&lc);
715     retv = sndPlaySoundA(lpszSoundName, uFlags);
716     RestoreThunkLock(lc);
717
718     return retv;
719 }
720
721 /**************************************************************************
722  *                              mmsystemGetVersion      [MMSYSTEM.5]
723  * return value borrowed from Win95 winmm.dll ;)
724  */
725 UINT16 WINAPI mmsystemGetVersion16(void)
726 {
727     return mmsystemGetVersion();
728 }
729
730 /**************************************************************************
731  *                              mmsystemGetVersion      [WINMM.@]
732  */
733 UINT WINAPI mmsystemGetVersion(void)
734 {
735     TRACE("3.10 (Win95?)\n");
736     return 0x030a;
737 }
738
739 /**************************************************************************
740  *                              DriverCallback                  [WINMM.@]
741  */
742 BOOL WINAPI DriverCallback(DWORD dwCallBack, UINT uFlags, HDRVR hDev,
743                            UINT wMsg, DWORD dwUser, DWORD dwParam1,
744                            DWORD dwParam2)
745 {
746     TRACE("(%08lX, %04X, %04X, %04X, %08lX, %08lX, %08lX); !\n",
747           dwCallBack, uFlags, hDev, wMsg, dwUser, dwParam1, dwParam2);
748
749     switch (uFlags & DCB_TYPEMASK) {
750     case DCB_NULL:
751         TRACE("Null !\n");
752         if (dwCallBack)
753             WARN("uFlags=%04X has null DCB value, but dwCallBack=%08lX is not null !\n", uFlags, dwCallBack);
754         break;
755     case DCB_WINDOW:
756         TRACE("Window(%04lX) handle=%04X!\n", dwCallBack, hDev);
757         PostMessageA((HWND)dwCallBack, wMsg, hDev, dwParam1);
758         break;
759     case DCB_TASK: /* aka DCB_THREAD */
760         TRACE("Task(%04lx) !\n", dwCallBack);
761         PostThreadMessageA(dwCallBack, wMsg, hDev, dwParam1);
762         break;
763     case DCB_FUNCTION:
764         TRACE("Function (32 bit) !\n");
765         ((LPDRVCALLBACK)dwCallBack)(hDev, wMsg, dwUser, dwParam1, dwParam2);
766         break;
767     case DCB_EVENT:
768         TRACE("Event(%08lx) !\n", dwCallBack);
769         SetEvent((HANDLE)dwCallBack);
770         break;
771     case 6: /* I would dub it DCB_MMTHREADSIGNAL */
772         /* this is an undocumented DCB_ value used for mmThreads
773          * loword of dwCallBack contains the handle of the lpMMThd block
774          * which dwSignalCount has to be incremented
775          */
776         {
777             WINE_MMTHREAD*      lpMMThd = MapSL( MAKESEGPTR(LOWORD(dwCallBack), 0) );
778
779             TRACE("mmThread (%04x, %p) !\n", LOWORD(dwCallBack), lpMMThd);
780             /* same as mmThreadSignal16 */
781             InterlockedIncrement(&lpMMThd->dwSignalCount);
782             SetEvent(lpMMThd->hEvent);
783             /* some other stuff on lpMMThd->hVxD */
784         }
785         break;
786 #if 0
787     case 4:
788         /* this is an undocumented DCB_ value for... I don't know */
789         break;
790 #endif
791     default:
792         WARN("Unknown callback type %d\n", uFlags & DCB_TYPEMASK);
793         return FALSE;
794     }
795     TRACE("Done\n");
796     return TRUE;
797 }
798
799 /**************************************************************************
800  *                              DriverCallback                  [MMSYSTEM.31]
801  */
802 BOOL16 WINAPI DriverCallback16(DWORD dwCallBack, UINT16 uFlags, HDRVR16 hDev,
803                                WORD wMsg, DWORD dwUser, DWORD dwParam1,
804                                DWORD dwParam2)
805 {
806     return DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwUser, dwParam1, dwParam2);
807 }
808
809 /**************************************************************************
810  *      Mixer devices. New to Win95
811  */
812
813 /**************************************************************************
814  * find out the real mixer ID depending on hmix (depends on dwFlags)
815  */
816 static LPWINE_MIXER MIXER_GetDev(HMIXEROBJ hmix, DWORD dwFlags)
817 {
818     LPWINE_MIXER        lpwm = NULL;
819
820     switch (dwFlags & 0xF0000000ul) {
821     case MIXER_OBJECTF_MIXER:
822         lpwm = (LPWINE_MIXER)MMDRV_Get(hmix, MMDRV_MIXER, TRUE);
823         break;
824     case MIXER_OBJECTF_HMIXER:
825         lpwm = (LPWINE_MIXER)MMDRV_Get(hmix, MMDRV_MIXER, FALSE);
826         break;
827     case MIXER_OBJECTF_WAVEOUT:
828         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEOUT, TRUE,  MMDRV_MIXER);
829         break;
830     case MIXER_OBJECTF_HWAVEOUT:
831         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEOUT, FALSE, MMDRV_MIXER);
832         break;
833     case MIXER_OBJECTF_WAVEIN:
834         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEIN,  TRUE,  MMDRV_MIXER);
835         break;
836     case MIXER_OBJECTF_HWAVEIN:
837         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEIN,  FALSE, MMDRV_MIXER);
838         break;
839     case MIXER_OBJECTF_MIDIOUT:
840         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIOUT, TRUE,  MMDRV_MIXER);
841         break;
842     case MIXER_OBJECTF_HMIDIOUT:
843         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIOUT, FALSE, MMDRV_MIXER);
844         break;
845     case MIXER_OBJECTF_MIDIIN:
846         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIIN,  TRUE,  MMDRV_MIXER);
847         break;
848     case MIXER_OBJECTF_HMIDIIN:
849         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIIN,  FALSE, MMDRV_MIXER);
850         break;
851     case MIXER_OBJECTF_AUX:
852         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_AUX,     TRUE,  MMDRV_MIXER);
853         break;
854     default:
855         FIXME("Unsupported flag (%08lx)\n", dwFlags & 0xF0000000ul);
856         break;
857     }
858     return lpwm;
859 }
860
861 /**************************************************************************
862  *                              mixerGetNumDevs                 [WINMM.@]
863  */
864 UINT WINAPI mixerGetNumDevs(void)
865 {
866     return MMDRV_GetNum(MMDRV_MIXER);
867 }
868
869 /**************************************************************************
870  *                              mixerGetNumDevs                 [MMSYSTEM.800]
871  */
872 UINT16 WINAPI mixerGetNumDevs16(void)
873 {
874     return MMDRV_GetNum(MMDRV_MIXER);
875 }
876
877 /**************************************************************************
878  *                              mixerGetDevCapsA                [WINMM.@]
879  */
880 UINT WINAPI mixerGetDevCapsA(UINT devid, LPMIXERCAPSA mixcaps, UINT size)
881 {
882     LPWINE_MLD  wmld;
883
884     if ((wmld = MMDRV_Get(devid, MMDRV_MIXER, TRUE)) == NULL)
885         return MMSYSERR_BADDEVICEID;
886
887     return MMDRV_Message(wmld, MXDM_GETDEVCAPS, (DWORD)mixcaps, size, TRUE);
888 }
889
890 /**************************************************************************
891  *                              mixerGetDevCapsW                [WINMM.@]
892  */
893 UINT WINAPI mixerGetDevCapsW(UINT devid, LPMIXERCAPSW mixcaps, UINT size)
894 {
895     MIXERCAPSA  micA;
896     UINT        ret = mixerGetDevCapsA(devid, &micA, sizeof(micA));
897
898     if (ret == MMSYSERR_NOERROR) {
899         mixcaps->wMid           = micA.wMid;
900         mixcaps->wPid           = micA.wPid;
901         mixcaps->vDriverVersion = micA.vDriverVersion;
902         MultiByteToWideChar( CP_ACP, 0, micA.szPname, -1, mixcaps->szPname,
903                              sizeof(mixcaps->szPname)/sizeof(WCHAR) );
904         mixcaps->fdwSupport     = micA.fdwSupport;
905         mixcaps->cDestinations  = micA.cDestinations;
906     }
907     return ret;
908 }
909
910 /**************************************************************************
911  *                              mixerGetDevCaps                 [MMSYSTEM.801]
912  */
913 UINT16 WINAPI mixerGetDevCaps16(UINT16 devid, LPMIXERCAPS16 mixcaps,
914                                 UINT16 size)
915 {
916     MIXERCAPSA  micA;
917     UINT        ret = mixerGetDevCapsA(devid, &micA, sizeof(micA));
918
919     if (ret == MMSYSERR_NOERROR) {
920         mixcaps->wMid           = micA.wMid;
921         mixcaps->wPid           = micA.wPid;
922         mixcaps->vDriverVersion = micA.vDriverVersion;
923         strcpy(mixcaps->szPname, micA.szPname);
924         mixcaps->fdwSupport     = micA.fdwSupport;
925         mixcaps->cDestinations  = micA.cDestinations;
926     }
927     return ret;
928 }
929
930 static  UINT  MMSYSTEM_mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD dwCallback,
931                                  DWORD dwInstance, DWORD fdwOpen, BOOL bFrom32)
932 {
933     HMIXER              hMix;
934     LPWINE_MLD          wmld;
935     DWORD               dwRet = 0;
936     MIXEROPENDESC       mod;
937
938     TRACE("(%p, %d, %08lx, %08lx, %08lx)\n",
939           lphMix, uDeviceID, dwCallback, dwInstance, fdwOpen);
940
941     wmld = MMDRV_Alloc(sizeof(WINE_MIXER), MMDRV_MIXER, &hMix, &fdwOpen,
942                        &dwCallback, &dwInstance, bFrom32);
943
944     wmld->uDeviceID = uDeviceID;
945     mod.hmx = hMix;
946     mod.dwCallback = dwCallback;
947     mod.dwInstance = dwInstance;
948
949     dwRet = MMDRV_Open(wmld, MXDM_OPEN, (DWORD)&mod, fdwOpen);
950
951     if (dwRet != MMSYSERR_NOERROR) {
952         MMDRV_Free(hMix, wmld);
953         hMix = 0;
954     }
955     if (lphMix) *lphMix = hMix;
956     TRACE("=> %ld hMixer=%04x\n", dwRet, hMix);
957
958     return dwRet;
959 }
960
961 /**************************************************************************
962  *                              mixerOpen                       [WINMM.@]
963  */
964 UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD dwCallback,
965                       DWORD dwInstance, DWORD fdwOpen)
966 {
967     return MMSYSTEM_mixerOpen(lphMix, uDeviceID,
968                               dwCallback, dwInstance, fdwOpen, TRUE);
969 }
970
971 /**************************************************************************
972  *                              mixerOpen                       [MMSYSTEM.802]
973  */
974 UINT16 WINAPI mixerOpen16(LPHMIXER16 lphmix, UINT16 uDeviceID, DWORD dwCallback,
975                           DWORD dwInstance, DWORD fdwOpen)
976 {
977     HMIXER      hmix;
978     UINT        ret;
979
980     ret = MMSYSTEM_mixerOpen(&hmix, uDeviceID,
981                              dwCallback, dwInstance, fdwOpen, FALSE);
982     if (lphmix) *lphmix = hmix;
983     return ret;
984 }
985
986 /**************************************************************************
987  *                              mixerClose                      [WINMM.@]
988  */
989 UINT WINAPI mixerClose(HMIXER hMix)
990 {
991     LPWINE_MLD          wmld;
992     DWORD               dwRet;
993
994     TRACE("(%04x)\n", hMix);
995
996     if ((wmld = MMDRV_Get(hMix, MMDRV_MIXER, FALSE)) == NULL) return MMSYSERR_INVALHANDLE;
997
998     dwRet = MMDRV_Close(wmld, MXDM_CLOSE);
999     MMDRV_Free(hMix, wmld);
1000
1001     return dwRet;
1002 }
1003
1004 /**************************************************************************
1005  *                              mixerClose                      [MMSYSTEM.803]
1006  */
1007 UINT16 WINAPI mixerClose16(HMIXER16 hMix)
1008 {
1009     return mixerClose(hMix);
1010 }
1011
1012 /**************************************************************************
1013  *                              mixerGetID                      [WINMM.@]
1014  */
1015 UINT WINAPI mixerGetID(HMIXEROBJ hmix, LPUINT lpid, DWORD fdwID)
1016 {
1017     LPWINE_MIXER        lpwm;
1018
1019     TRACE("(%04x %p %08lx)\n", hmix, lpid, fdwID);
1020
1021     if ((lpwm = MIXER_GetDev(hmix, fdwID)) == NULL) {
1022         return MMSYSERR_INVALHANDLE;
1023     }
1024
1025     if (lpid)
1026       *lpid = lpwm->mld.uDeviceID;
1027
1028     return MMSYSERR_NOERROR;
1029 }
1030
1031 /**************************************************************************
1032  *                              mixerGetID (MMSYSTEM.806)
1033  */
1034 UINT16 WINAPI mixerGetID16(HMIXEROBJ16 hmix, LPUINT16 lpid, DWORD fdwID)
1035 {
1036     UINT        xid;
1037     UINT        ret = mixerGetID(hmix, &xid, fdwID);
1038
1039     if (lpid)
1040         *lpid = xid;
1041     return ret;
1042 }
1043
1044 /**************************************************************************
1045  *                              mixerGetControlDetailsA         [WINMM.@]
1046  */
1047 UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
1048                                     DWORD fdwDetails)
1049 {
1050     LPWINE_MIXER        lpwm;
1051
1052     TRACE("(%04x, %p, %08lx)\n", hmix, lpmcdA, fdwDetails);
1053
1054     if ((lpwm = MIXER_GetDev(hmix, fdwDetails)) == NULL)
1055         return MMSYSERR_INVALHANDLE;
1056
1057     if (lpmcdA == NULL || lpmcdA->cbStruct != sizeof(*lpmcdA))
1058         return MMSYSERR_INVALPARAM;
1059
1060     return MMDRV_Message(&lpwm->mld, MXDM_GETCONTROLDETAILS, (DWORD)lpmcdA,
1061                          fdwDetails, TRUE);
1062 }
1063
1064 /**************************************************************************
1065  *                              mixerGetControlDetailsW [WINMM.@]
1066  */
1067 UINT WINAPI mixerGetControlDetailsW(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
1068 {
1069     DWORD                       ret = MMSYSERR_NOTENABLED;
1070
1071     TRACE("(%04x, %p, %08lx)\n", hmix, lpmcd, fdwDetails);
1072
1073     if (lpmcd == NULL || lpmcd->cbStruct != sizeof(*lpmcd))
1074         return MMSYSERR_INVALPARAM;
1075
1076     switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
1077     case MIXER_GETCONTROLDETAILSF_VALUE:
1078         /* can savely use W structure as it is, no string inside */
1079         ret = mixerGetControlDetailsA(hmix, lpmcd, fdwDetails);
1080         break;
1081     case MIXER_GETCONTROLDETAILSF_LISTTEXT:
1082         {
1083             MIXERCONTROLDETAILS_LISTTEXTW *pDetailsW = (MIXERCONTROLDETAILS_LISTTEXTW *)lpmcd->paDetails;
1084             MIXERCONTROLDETAILS_LISTTEXTA *pDetailsA;
1085             int size = max(1, lpmcd->cChannels) * sizeof(MIXERCONTROLDETAILS_LISTTEXTA);
1086             int i;
1087
1088             if (lpmcd->u.cMultipleItems != 0) {
1089                 size *= lpmcd->u.cMultipleItems;
1090             }
1091             pDetailsA = (MIXERCONTROLDETAILS_LISTTEXTA *)HeapAlloc(GetProcessHeap(), 0, size);
1092             lpmcd->paDetails = pDetailsA;
1093             lpmcd->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTA);
1094             /* set up lpmcd->paDetails */
1095             ret = mixerGetControlDetailsA(hmix, lpmcd, fdwDetails);
1096             /* copy from lpmcd->paDetails back to paDetailsW; */
1097             if(ret == MMSYSERR_NOERROR) {
1098                 for(i=0;i<lpmcd->u.cMultipleItems*lpmcd->cChannels;i++) {
1099                     pDetailsW->dwParam1 = pDetailsA->dwParam1;
1100                     pDetailsW->dwParam2 = pDetailsA->dwParam2;
1101                     MultiByteToWideChar( CP_ACP, 0, pDetailsA->szName, -1,
1102                                          pDetailsW->szName,
1103                                          sizeof(pDetailsW->szName)/sizeof(WCHAR) );
1104                     pDetailsA++;
1105                     pDetailsW++;
1106                 }
1107                 pDetailsA -= lpmcd->u.cMultipleItems*lpmcd->cChannels;
1108                 pDetailsW -= lpmcd->u.cMultipleItems*lpmcd->cChannels;
1109             }
1110             HeapFree(GetProcessHeap(), 0, pDetailsA);
1111             lpmcd->paDetails = pDetailsW;
1112             lpmcd->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
1113         }
1114         break;
1115     default:
1116         ERR("Unsupported fdwDetails=0x%08lx\n", fdwDetails);
1117     }
1118
1119     return ret;
1120 }
1121
1122 /**************************************************************************
1123  *                              mixerGetControlDetails  [MMSYSTEM.808]
1124  */
1125 UINT16 WINAPI mixerGetControlDetails16(HMIXEROBJ16 hmix,
1126                                        LPMIXERCONTROLDETAILS16 lpmcd,
1127                                        DWORD fdwDetails)
1128 {
1129     DWORD       ret = MMSYSERR_NOTENABLED;
1130     SEGPTR      sppaDetails;
1131
1132     TRACE("(%04x, %p, %08lx)\n", hmix, lpmcd, fdwDetails);
1133
1134     if (lpmcd == NULL || lpmcd->cbStruct != sizeof(*lpmcd))
1135         return MMSYSERR_INVALPARAM;
1136
1137     sppaDetails = (SEGPTR)lpmcd->paDetails;
1138     lpmcd->paDetails = MapSL(sppaDetails);
1139     ret = mixerGetControlDetailsA(hmix, (LPMIXERCONTROLDETAILS)lpmcd, fdwDetails);
1140     lpmcd->paDetails = (LPVOID)sppaDetails;
1141
1142     return ret;
1143 }
1144
1145 /**************************************************************************
1146  *                              mixerGetLineControlsA   [WINMM.@]
1147  */
1148 UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,
1149                                   DWORD fdwControls)
1150 {
1151     LPWINE_MIXER        lpwm;
1152
1153     TRACE("(%04x, %p, %08lx)\n", hmix, lpmlcA, fdwControls);
1154
1155     if ((lpwm = MIXER_GetDev(hmix, fdwControls)) == NULL)
1156         return MMSYSERR_INVALHANDLE;
1157
1158     if (lpmlcA == NULL || lpmlcA->cbStruct != sizeof(*lpmlcA))
1159         return MMSYSERR_INVALPARAM;
1160
1161     return MMDRV_Message(&lpwm->mld, MXDM_GETLINECONTROLS, (DWORD)lpmlcA,
1162                          fdwControls, TRUE);
1163 }
1164
1165 /**************************************************************************
1166  *                              mixerGetLineControlsW           [WINMM.@]
1167  */
1168 UINT WINAPI mixerGetLineControlsW(HMIXEROBJ hmix, LPMIXERLINECONTROLSW lpmlcW,
1169                                   DWORD fdwControls)
1170 {
1171     MIXERLINECONTROLSA  mlcA;
1172     DWORD               ret;
1173     int                 i;
1174
1175     TRACE("(%04x, %p, %08lx)\n", hmix, lpmlcW, fdwControls);
1176
1177     if (lpmlcW == NULL || lpmlcW->cbStruct != sizeof(*lpmlcW) ||
1178         lpmlcW->cbmxctrl != sizeof(MIXERCONTROLW))
1179         return MMSYSERR_INVALPARAM;
1180
1181     mlcA.cbStruct = sizeof(mlcA);
1182     mlcA.dwLineID = lpmlcW->dwLineID;
1183     mlcA.u.dwControlID = lpmlcW->u.dwControlID;
1184     mlcA.u.dwControlType = lpmlcW->u.dwControlType;
1185     mlcA.cControls = lpmlcW->cControls;
1186     mlcA.cbmxctrl = sizeof(MIXERCONTROLA);
1187     mlcA.pamxctrl = HeapAlloc(GetProcessHeap(), 0,
1188                               mlcA.cControls * mlcA.cbmxctrl);
1189
1190     ret = mixerGetLineControlsA(hmix, &mlcA, fdwControls);
1191
1192     if (ret == MMSYSERR_NOERROR) {
1193         lpmlcW->dwLineID = mlcA.dwLineID;
1194         lpmlcW->u.dwControlID = mlcA.u.dwControlID;
1195         lpmlcW->u.dwControlType = mlcA.u.dwControlType;
1196         lpmlcW->cControls = mlcA.cControls;
1197
1198         for (i = 0; i < mlcA.cControls; i++) {
1199             lpmlcW->pamxctrl[i].cbStruct = sizeof(MIXERCONTROLW);
1200             lpmlcW->pamxctrl[i].dwControlID = mlcA.pamxctrl[i].dwControlID;
1201             lpmlcW->pamxctrl[i].dwControlType = mlcA.pamxctrl[i].dwControlType;
1202             lpmlcW->pamxctrl[i].fdwControl = mlcA.pamxctrl[i].fdwControl;
1203             lpmlcW->pamxctrl[i].cMultipleItems = mlcA.pamxctrl[i].cMultipleItems;
1204             MultiByteToWideChar( CP_ACP, 0, mlcA.pamxctrl[i].szShortName, -1,
1205                                  lpmlcW->pamxctrl[i].szShortName,
1206                                  sizeof(lpmlcW->pamxctrl[i].szShortName)/sizeof(WCHAR) );
1207             MultiByteToWideChar( CP_ACP, 0, mlcA.pamxctrl[i].szName, -1,
1208                                  lpmlcW->pamxctrl[i].szName,
1209                                  sizeof(lpmlcW->pamxctrl[i].szName)/sizeof(WCHAR) );
1210             /* sizeof(lpmlcW->pamxctrl[i].Bounds) ==
1211              * sizeof(mlcA.pamxctrl[i].Bounds) */
1212             memcpy(&lpmlcW->pamxctrl[i].Bounds, &mlcA.pamxctrl[i].Bounds,
1213                    sizeof(mlcA.pamxctrl[i].Bounds));
1214             /* sizeof(lpmlcW->pamxctrl[i].Metrics) ==
1215              * sizeof(mlcA.pamxctrl[i].Metrics) */
1216             memcpy(&lpmlcW->pamxctrl[i].Metrics, &mlcA.pamxctrl[i].Metrics,
1217                    sizeof(mlcA.pamxctrl[i].Metrics));
1218         }
1219     }
1220
1221     HeapFree(GetProcessHeap(), 0, mlcA.pamxctrl);
1222
1223     return ret;
1224 }
1225
1226 /**************************************************************************
1227  *                              mixerGetLineControls            [MMSYSTEM.807]
1228  */
1229 UINT16 WINAPI mixerGetLineControls16(HMIXEROBJ16 hmix,
1230                                      LPMIXERLINECONTROLS16 lpmlc16,
1231                                      DWORD fdwControls)
1232 {
1233     MIXERLINECONTROLSA  mlcA;
1234     DWORD               ret;
1235     int                 i;
1236     LPMIXERCONTROL16    lpmc16;
1237
1238     TRACE("(%04x, %p, %08lx)\n", hmix, lpmlc16, fdwControls);
1239
1240     if (lpmlc16 == NULL || lpmlc16->cbStruct != sizeof(*lpmlc16) ||
1241         lpmlc16->cbmxctrl != sizeof(MIXERCONTROL16))
1242         return MMSYSERR_INVALPARAM;
1243
1244     mlcA.cbStruct = sizeof(mlcA);
1245     mlcA.dwLineID = lpmlc16->dwLineID;
1246     mlcA.u.dwControlID = lpmlc16->u.dwControlID;
1247     mlcA.u.dwControlType = lpmlc16->u.dwControlType;
1248     mlcA.cControls = lpmlc16->cControls;
1249     mlcA.cbmxctrl = sizeof(MIXERCONTROLA);
1250     mlcA.pamxctrl = HeapAlloc(GetProcessHeap(), 0,
1251                               mlcA.cControls * mlcA.cbmxctrl);
1252
1253     ret = mixerGetLineControlsA(hmix, &mlcA, fdwControls);
1254
1255     if (ret == MMSYSERR_NOERROR) {
1256         lpmlc16->dwLineID = mlcA.dwLineID;
1257         lpmlc16->u.dwControlID = mlcA.u.dwControlID;
1258         lpmlc16->u.dwControlType = mlcA.u.dwControlType;
1259         lpmlc16->cControls = mlcA.cControls;
1260
1261         lpmc16 = MapSL(lpmlc16->pamxctrl);
1262
1263         for (i = 0; i < mlcA.cControls; i++) {
1264             lpmc16[i].cbStruct = sizeof(MIXERCONTROL16);
1265             lpmc16[i].dwControlID = mlcA.pamxctrl[i].dwControlID;
1266             lpmc16[i].dwControlType = mlcA.pamxctrl[i].dwControlType;
1267             lpmc16[i].fdwControl = mlcA.pamxctrl[i].fdwControl;
1268             lpmc16[i].cMultipleItems = mlcA.pamxctrl[i].cMultipleItems;
1269             strcpy(lpmc16[i].szShortName, mlcA.pamxctrl[i].szShortName);
1270             strcpy(lpmc16[i].szName, mlcA.pamxctrl[i].szName);
1271             /* sizeof(lpmc16[i].Bounds) == sizeof(mlcA.pamxctrl[i].Bounds) */
1272             memcpy(&lpmc16[i].Bounds, &mlcA.pamxctrl[i].Bounds,
1273                    sizeof(mlcA.pamxctrl[i].Bounds));
1274             /* sizeof(lpmc16[i].Metrics) == sizeof(mlcA.pamxctrl[i].Metrics) */
1275             memcpy(&lpmc16[i].Metrics, &mlcA.pamxctrl[i].Metrics,
1276                    sizeof(mlcA.pamxctrl[i].Metrics));
1277         }
1278     }
1279
1280     HeapFree(GetProcessHeap(), 0, mlcA.pamxctrl);
1281
1282     return ret;
1283 }
1284
1285 /**************************************************************************
1286  *                              mixerGetLineInfoA               [WINMM.@]
1287  */
1288 UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliW, DWORD fdwInfo)
1289 {
1290     LPWINE_MIXER        lpwm;
1291
1292     TRACE("(%04x, %p, %08lx)\n", hmix, lpmliW, fdwInfo);
1293
1294     if ((lpwm = MIXER_GetDev(hmix, fdwInfo)) == NULL)
1295         return MMSYSERR_INVALHANDLE;
1296
1297     return MMDRV_Message(&lpwm->mld, MXDM_GETLINEINFO, (DWORD)lpmliW,
1298                          fdwInfo, TRUE);
1299 }
1300
1301 /**************************************************************************
1302  *                              mixerGetLineInfoW               [WINMM.@]
1303  */
1304 UINT WINAPI mixerGetLineInfoW(HMIXEROBJ hmix, LPMIXERLINEW lpmliW,
1305                               DWORD fdwInfo)
1306 {
1307     MIXERLINEA          mliA;
1308     UINT                ret;
1309
1310     TRACE("(%04x, %p, %08lx)\n", hmix, lpmliW, fdwInfo);
1311
1312     if (lpmliW == NULL || lpmliW->cbStruct != sizeof(*lpmliW))
1313         return MMSYSERR_INVALPARAM;
1314
1315     mliA.cbStruct = sizeof(mliA);
1316     switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {
1317     case MIXER_GETLINEINFOF_COMPONENTTYPE:
1318         mliA.dwComponentType = lpmliW->dwComponentType;
1319         break;
1320     case MIXER_GETLINEINFOF_DESTINATION:
1321         mliA.dwDestination = lpmliW->dwDestination;
1322         break;
1323     case MIXER_GETLINEINFOF_LINEID:
1324         mliA.dwLineID = lpmliW->dwLineID;
1325         break;
1326     case MIXER_GETLINEINFOF_SOURCE:
1327         mliA.dwDestination = lpmliW->dwDestination;
1328         mliA.dwSource = lpmliW->dwSource;
1329         break;
1330     case MIXER_GETLINEINFOF_TARGETTYPE:
1331         mliA.Target.dwType = lpmliW->Target.dwType;
1332         mliA.Target.wMid = lpmliW->Target.wMid;
1333         mliA.Target.wPid = lpmliW->Target.wPid;
1334         mliA.Target.vDriverVersion = lpmliW->Target.vDriverVersion;
1335         WideCharToMultiByte( CP_ACP, 0, lpmliW->Target.szPname, -1, mliA.Target.szPname, sizeof(mliA.Target.szPname), NULL, NULL);
1336         break;
1337     default:
1338         FIXME("Unsupported fdwControls=0x%08lx\n", fdwInfo);
1339     }
1340
1341     ret = mixerGetLineInfoA(hmix, &mliA, fdwInfo);
1342
1343     lpmliW->dwDestination = mliA.dwDestination;
1344     lpmliW->dwSource = mliA.dwSource;
1345     lpmliW->dwLineID = mliA.dwLineID;
1346     lpmliW->fdwLine = mliA.fdwLine;
1347     lpmliW->dwUser = mliA.dwUser;
1348     lpmliW->dwComponentType = mliA.dwComponentType;
1349     lpmliW->cChannels = mliA.cChannels;
1350     lpmliW->cConnections = mliA.cConnections;
1351     lpmliW->cControls = mliA.cControls;
1352     MultiByteToWideChar( CP_ACP, 0, mliA.szShortName, -1, lpmliW->szShortName,
1353                          sizeof(lpmliW->szShortName)/sizeof(WCHAR) );
1354     MultiByteToWideChar( CP_ACP, 0, mliA.szName, -1, lpmliW->szName,
1355                          sizeof(lpmliW->szName)/sizeof(WCHAR) );
1356     lpmliW->Target.dwType = mliA.Target.dwType;
1357     lpmliW->Target.dwDeviceID = mliA.Target.dwDeviceID;
1358     lpmliW->Target.wMid = mliA.Target.wMid;
1359     lpmliW->Target.wPid = mliA.Target.wPid;
1360     lpmliW->Target.vDriverVersion = mliA.Target.vDriverVersion;
1361     MultiByteToWideChar( CP_ACP, 0, mliA.Target.szPname, -1, lpmliW->Target.szPname,
1362                          sizeof(lpmliW->Target.szPname)/sizeof(WCHAR) );
1363
1364     return ret;
1365 }
1366
1367 /**************************************************************************
1368  *                              mixerGetLineInfo        [MMSYSTEM.805]
1369  */
1370 UINT16 WINAPI mixerGetLineInfo16(HMIXEROBJ16 hmix, LPMIXERLINE16 lpmli16,
1371                                  DWORD fdwInfo)
1372 {
1373     MIXERLINEA          mliA;
1374     UINT                ret;
1375
1376     TRACE("(%04x, %p, %08lx)\n", hmix, lpmli16, fdwInfo);
1377
1378     if (lpmli16 == NULL || lpmli16->cbStruct != sizeof(*lpmli16))
1379         return MMSYSERR_INVALPARAM;
1380
1381     mliA.cbStruct = sizeof(mliA);
1382     switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {
1383     case MIXER_GETLINEINFOF_COMPONENTTYPE:
1384         mliA.dwComponentType = lpmli16->dwComponentType;
1385         break;
1386     case MIXER_GETLINEINFOF_DESTINATION:
1387         mliA.dwDestination = lpmli16->dwDestination;
1388         break;
1389     case MIXER_GETLINEINFOF_LINEID:
1390         mliA.dwLineID = lpmli16->dwLineID;
1391         break;
1392     case MIXER_GETLINEINFOF_SOURCE:
1393         mliA.dwDestination = lpmli16->dwDestination;
1394         mliA.dwSource = lpmli16->dwSource;
1395         break;
1396     case MIXER_GETLINEINFOF_TARGETTYPE:
1397         mliA.Target.dwType = lpmli16->Target.dwType;
1398         mliA.Target.wMid = lpmli16->Target.wMid;
1399         mliA.Target.wPid = lpmli16->Target.wPid;
1400         mliA.Target.vDriverVersion = lpmli16->Target.vDriverVersion;
1401         strcpy(mliA.Target.szPname, lpmli16->Target.szPname);
1402         break;
1403     default:
1404         FIXME("Unsupported fdwControls=0x%08lx\n", fdwInfo);
1405     }
1406
1407     ret = mixerGetLineInfoA(hmix, &mliA, fdwInfo);
1408
1409     lpmli16->dwDestination      = mliA.dwDestination;
1410     lpmli16->dwSource           = mliA.dwSource;
1411     lpmli16->dwLineID           = mliA.dwLineID;
1412     lpmli16->fdwLine            = mliA.fdwLine;
1413     lpmli16->dwUser             = mliA.dwUser;
1414     lpmli16->dwComponentType    = mliA.dwComponentType;
1415     lpmli16->cChannels          = mliA.cChannels;
1416     lpmli16->cConnections       = mliA.cConnections;
1417     lpmli16->cControls          = mliA.cControls;
1418     strcpy(lpmli16->szShortName, mliA.szShortName);
1419     strcpy(lpmli16->szName, mliA.szName);
1420     lpmli16->Target.dwType      = mliA.Target.dwType;
1421     lpmli16->Target.dwDeviceID  = mliA.Target.dwDeviceID;
1422     lpmli16->Target.wMid        = mliA.Target.wMid;
1423     lpmli16->Target.wPid        = mliA.Target.wPid;
1424     lpmli16->Target.vDriverVersion = mliA.Target.vDriverVersion;
1425     strcpy(lpmli16->Target.szPname, mliA.Target.szPname);
1426
1427     return ret;
1428 }
1429
1430 /**************************************************************************
1431  *                              mixerSetControlDetails  [WINMM.@]
1432  */
1433 UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
1434                                    DWORD fdwDetails)
1435 {
1436     LPWINE_MIXER        lpwm;
1437
1438     TRACE("(%04x, %p, %08lx)\n", hmix, lpmcdA, fdwDetails);
1439
1440     if ((lpwm = MIXER_GetDev(hmix, fdwDetails)) == NULL)
1441         return MMSYSERR_INVALHANDLE;
1442
1443     return MMDRV_Message(&lpwm->mld, MXDM_SETCONTROLDETAILS, (DWORD)lpmcdA,
1444                          fdwDetails, TRUE);
1445 }
1446
1447 /**************************************************************************
1448  *                              mixerSetControlDetails  [MMSYSTEM.809]
1449  */
1450 UINT16 WINAPI mixerSetControlDetails16(HMIXEROBJ16 hmix,
1451                                        LPMIXERCONTROLDETAILS16 lpmcd,
1452                                        DWORD fdwDetails)
1453 {
1454     TRACE("(%04x, %p, %08lx)\n", hmix, lpmcd, fdwDetails);
1455     return MMSYSERR_NOTENABLED;
1456 }
1457
1458 /**************************************************************************
1459  *                              mixerMessage            [WINMM.@]
1460  */
1461 UINT WINAPI mixerMessage(HMIXER hmix, UINT uMsg, DWORD dwParam1, DWORD dwParam2)
1462 {
1463     LPWINE_MLD          wmld;
1464
1465     TRACE("(%04lx, %d, %08lx, %08lx): semi-stub?\n",
1466           (DWORD)hmix, uMsg, dwParam1, dwParam2);
1467
1468     if ((wmld = MMDRV_Get(hmix, MMDRV_MIXER, FALSE)) == NULL)
1469         return MMSYSERR_INVALHANDLE;
1470
1471     return MMDRV_Message(wmld, uMsg, dwParam1, dwParam2, TRUE);
1472 }
1473
1474 /**************************************************************************
1475  *                              mixerMessage            [MMSYSTEM.804]
1476  */
1477 DWORD WINAPI mixerMessage16(HMIXER16 hmix, UINT16 uMsg, DWORD dwParam1,
1478                              DWORD dwParam2)
1479 {
1480     return mixerMessage(hmix, uMsg, dwParam1, dwParam2);
1481 }
1482
1483 /**************************************************************************
1484  *                              auxGetNumDevs           [WINMM.@]
1485  */
1486 UINT WINAPI auxGetNumDevs(void)
1487 {
1488     return MMDRV_GetNum(MMDRV_AUX);
1489 }
1490
1491 /**************************************************************************
1492  *                              auxGetNumDevs           [MMSYSTEM.350]
1493  */
1494 UINT16 WINAPI auxGetNumDevs16(void)
1495 {
1496     return MMDRV_GetNum(MMDRV_AUX);
1497 }
1498
1499 /**************************************************************************
1500  *                              auxGetDevCapsW          [WINMM.@]
1501  */
1502 UINT WINAPI auxGetDevCapsW(UINT uDeviceID, LPAUXCAPSW lpCaps, UINT uSize)
1503 {
1504     AUXCAPSA    acA;
1505     UINT        ret = auxGetDevCapsA(uDeviceID, &acA, sizeof(acA));
1506
1507     lpCaps->wMid = acA.wMid;
1508     lpCaps->wPid = acA.wPid;
1509     lpCaps->vDriverVersion = acA.vDriverVersion;
1510     MultiByteToWideChar( CP_ACP, 0, acA.szPname, -1, lpCaps->szPname,
1511                          sizeof(lpCaps->szPname)/sizeof(WCHAR) );
1512     lpCaps->wTechnology = acA.wTechnology;
1513     lpCaps->dwSupport = acA.dwSupport;
1514     return ret;
1515 }
1516
1517 /**************************************************************************
1518  *                              auxGetDevCapsA          [WINMM.@]
1519  */
1520 UINT WINAPI auxGetDevCapsA(UINT uDeviceID, LPAUXCAPSA lpCaps, UINT uSize)
1521 {
1522     LPWINE_MLD          wmld;
1523
1524     TRACE("(%04X, %p, %d) !\n", uDeviceID, lpCaps, uSize);
1525
1526     if ((wmld = MMDRV_Get(uDeviceID, MMDRV_AUX, TRUE)) == NULL)
1527         return MMSYSERR_INVALHANDLE;
1528     return MMDRV_Message(wmld, AUXDM_GETDEVCAPS, (DWORD)lpCaps, uSize, TRUE);
1529 }
1530
1531 /**************************************************************************
1532  *                              auxGetDevCaps           [MMSYSTEM.351]
1533  */
1534 UINT16 WINAPI auxGetDevCaps16(UINT16 uDeviceID, LPAUXCAPS16 lpCaps, UINT16 uSize)
1535 {
1536     LPWINE_MLD          wmld;
1537
1538     TRACE("(%04X, %p, %d) !\n", uDeviceID, lpCaps, uSize);
1539
1540     if ((wmld = MMDRV_Get(uDeviceID, MMDRV_AUX, TRUE)) == NULL)
1541         return MMSYSERR_INVALHANDLE;
1542     return MMDRV_Message(wmld, AUXDM_GETDEVCAPS, (DWORD)lpCaps, uSize, TRUE);
1543 }
1544
1545 /**************************************************************************
1546  *                              auxGetVolume            [WINMM.@]
1547  */
1548 UINT WINAPI auxGetVolume(UINT uDeviceID, DWORD* lpdwVolume)
1549 {
1550     LPWINE_MLD          wmld;
1551
1552     TRACE("(%04X, %p) !\n", uDeviceID, lpdwVolume);
1553
1554     if ((wmld = MMDRV_Get(uDeviceID, MMDRV_AUX, TRUE)) == NULL)
1555         return MMSYSERR_INVALHANDLE;
1556     return MMDRV_Message(wmld, AUXDM_GETVOLUME, (DWORD)lpdwVolume, 0L, TRUE);
1557 }
1558
1559 /**************************************************************************
1560  *                              auxGetVolume            [MMSYSTEM.352]
1561  */
1562 UINT16 WINAPI auxGetVolume16(UINT16 uDeviceID, LPDWORD lpdwVolume)
1563 {
1564     LPWINE_MLD          wmld;
1565
1566     TRACE("(%04X, %p) !\n", uDeviceID, lpdwVolume);
1567
1568     if ((wmld = MMDRV_Get(uDeviceID, MMDRV_AUX, TRUE)) == NULL)
1569         return MMSYSERR_INVALHANDLE;
1570     return MMDRV_Message(wmld, AUXDM_GETVOLUME, (DWORD)lpdwVolume, 0L, TRUE);
1571 }
1572
1573 /**************************************************************************
1574  *                              auxSetVolume            [WINMM.@]
1575  */
1576 UINT WINAPI auxSetVolume(UINT uDeviceID, DWORD dwVolume)
1577 {
1578     LPWINE_MLD          wmld;
1579
1580     TRACE("(%04X, %lu) !\n", uDeviceID, dwVolume);
1581
1582     if ((wmld = MMDRV_Get(uDeviceID, MMDRV_AUX, TRUE)) == NULL)
1583         return MMSYSERR_INVALHANDLE;
1584     return MMDRV_Message(wmld, AUXDM_SETVOLUME, dwVolume, 0L, TRUE);
1585 }
1586
1587 /**************************************************************************
1588  *                              auxSetVolume            [MMSYSTEM.353]
1589  */
1590 UINT16 WINAPI auxSetVolume16(UINT16 uDeviceID, DWORD dwVolume)
1591 {
1592     LPWINE_MLD          wmld;
1593
1594     TRACE("(%04X, %lu) !\n", uDeviceID, dwVolume);
1595
1596     if ((wmld = MMDRV_Get(uDeviceID, MMDRV_AUX, TRUE)) == NULL)
1597         return MMSYSERR_INVALHANDLE;
1598     return MMDRV_Message(wmld, AUXDM_SETVOLUME, dwVolume, 0L, TRUE);
1599 }
1600
1601 /**************************************************************************
1602  *                              auxOutMessage           [WINMM.@]
1603  */
1604 DWORD WINAPI auxOutMessage(UINT uDeviceID, UINT uMessage, DWORD dw1, DWORD dw2)
1605 {
1606     LPWINE_MLD          wmld;
1607
1608     if ((wmld = MMDRV_Get(uDeviceID, MMDRV_AUX, TRUE)) == NULL)
1609         return MMSYSERR_INVALHANDLE;
1610
1611     return MMDRV_Message(wmld, uMessage, dw1, dw2, TRUE);
1612 }
1613
1614 /**************************************************************************
1615  *                              auxOutMessage           [MMSYSTEM.354]
1616  */
1617 DWORD WINAPI auxOutMessage16(UINT16 uDeviceID, UINT16 uMessage, DWORD dw1, DWORD dw2)
1618 {
1619     LPWINE_MLD          wmld;
1620
1621     TRACE("(%04X, %04X, %08lX, %08lX)\n", uDeviceID, uMessage, dw1, dw2);
1622
1623     switch (uMessage) {
1624     case AUXDM_GETNUMDEVS:
1625     case AUXDM_SETVOLUME:
1626         /* no argument conversion needed */
1627         break;
1628     case AUXDM_GETVOLUME:
1629         return auxGetVolume16(uDeviceID, MapSL(dw1));
1630     case AUXDM_GETDEVCAPS:
1631         return auxGetDevCaps16(uDeviceID, MapSL(dw1), dw2);
1632     default:
1633         TRACE("(%04x, %04x, %08lx, %08lx): unhandled message\n",
1634               uDeviceID, uMessage, dw1, dw2);
1635         break;
1636     }
1637     if ((wmld = MMDRV_Get(uDeviceID, MMDRV_AUX, TRUE)) == NULL)
1638         return MMSYSERR_INVALHANDLE;
1639
1640     return MMDRV_Message(wmld, uMessage, dw1, dw2, TRUE);
1641 }
1642
1643 /**************************************************************************
1644  *                              mciGetErrorStringW              [WINMM.@]
1645  */
1646 BOOL WINAPI mciGetErrorStringW(DWORD wError, LPWSTR lpstrBuffer, UINT uLength)
1647 {
1648     LPSTR       bufstr = HeapAlloc(GetProcessHeap(), 0, uLength);
1649     BOOL        ret = mciGetErrorStringA(wError, bufstr, uLength);
1650
1651     MultiByteToWideChar( CP_ACP, 0, bufstr, -1, lpstrBuffer, uLength );
1652     HeapFree(GetProcessHeap(), 0, bufstr);
1653     return ret;
1654 }
1655
1656 /**************************************************************************
1657  *                              mciGetErrorString               [MMSYSTEM.706]
1658  */
1659 BOOL16 WINAPI mciGetErrorString16(DWORD wError, LPSTR lpstrBuffer, UINT16 uLength)
1660 {
1661     return mciGetErrorStringA(wError, lpstrBuffer, uLength);
1662 }
1663
1664 /**************************************************************************
1665  *                              mciGetErrorStringA              [WINMM.@]
1666  */
1667 BOOL WINAPI mciGetErrorStringA(DWORD dwError, LPSTR lpstrBuffer, UINT uLength)
1668 {
1669     BOOL16              ret = FALSE;
1670
1671     if (lpstrBuffer != NULL && uLength > 0 &&
1672         dwError >= MCIERR_BASE && dwError <= MCIERR_CUSTOM_DRIVER_BASE) {
1673
1674         if (LoadStringA(MULTIMEDIA_GetIData()->hWinMM32Instance,
1675                         dwError, lpstrBuffer, uLength) > 0) {
1676             ret = TRUE;
1677         }
1678     }
1679     return ret;
1680 }
1681
1682 /**************************************************************************
1683  *                              mciDriverNotify                 [MMSYSTEM.711]
1684  */
1685 BOOL16 WINAPI mciDriverNotify16(HWND16 hWndCallBack, UINT16 wDevID, UINT16 wStatus)
1686 {
1687     TRACE("(%04X, %04x, %04X)\n", hWndCallBack, wDevID, wStatus);
1688
1689     return PostMessageA(hWndCallBack, MM_MCINOTIFY, wStatus, wDevID);
1690 }
1691
1692 /**************************************************************************
1693  *                      mciDriverNotify                         [WINMM.@]
1694  */
1695 BOOL WINAPI mciDriverNotify(HWND hWndCallBack, UINT wDevID, UINT wStatus)
1696 {
1697
1698     TRACE("(%08X, %04x, %04X)\n", hWndCallBack, wDevID, wStatus);
1699
1700     return PostMessageA(hWndCallBack, MM_MCINOTIFY, wStatus, wDevID);
1701 }
1702
1703 /**************************************************************************
1704  *                      mciGetDriverData                        [MMSYSTEM.708]
1705  */
1706 DWORD WINAPI mciGetDriverData16(UINT16 uDeviceID)
1707 {
1708     return mciGetDriverData(uDeviceID);
1709 }
1710
1711 /**************************************************************************
1712  *                      mciGetDriverData                        [WINMM.@]
1713  */
1714 DWORD WINAPI mciGetDriverData(UINT uDeviceID)
1715 {
1716     LPWINE_MCIDRIVER    wmd;
1717
1718     TRACE("(%04x)\n", uDeviceID);
1719
1720     wmd = MCI_GetDriver(uDeviceID);
1721
1722     if (!wmd) {
1723         WARN("Bad uDeviceID\n");
1724         return 0L;
1725     }
1726
1727     return wmd->dwPrivate;
1728 }
1729
1730 /**************************************************************************
1731  *                      mciSetDriverData                        [MMSYSTEM.707]
1732  */
1733 BOOL16 WINAPI mciSetDriverData16(UINT16 uDeviceID, DWORD data)
1734 {
1735     return mciSetDriverData(uDeviceID, data);
1736 }
1737
1738 /**************************************************************************
1739  *                      mciSetDriverData                        [WINMM.@]
1740  */
1741 BOOL WINAPI mciSetDriverData(UINT uDeviceID, DWORD data)
1742 {
1743     LPWINE_MCIDRIVER    wmd;
1744
1745     TRACE("(%04x, %08lx)\n", uDeviceID, data);
1746
1747     wmd = MCI_GetDriver(uDeviceID);
1748
1749     if (!wmd) {
1750         WARN("Bad uDeviceID\n");
1751         return FALSE;
1752     }
1753
1754     wmd->dwPrivate = data;
1755     return TRUE;
1756 }
1757
1758 /**************************************************************************
1759  *                              mciSendCommandA                 [WINMM.@]
1760  */
1761 DWORD WINAPI mciSendCommandA(UINT wDevID, UINT wMsg, DWORD dwParam1, DWORD dwParam2)
1762 {
1763     DWORD       dwRet;
1764
1765     TRACE("(%08x, %s, %08lx, %08lx)\n",
1766           wDevID, MCI_MessageToString(wMsg), dwParam1, dwParam2);
1767
1768     dwRet = MCI_SendCommand(wDevID, wMsg, dwParam1, dwParam2, TRUE);
1769     dwRet = MCI_CleanUp(dwRet, wMsg, dwParam2, TRUE);
1770     TRACE("=> %08lx\n", dwRet);
1771     return dwRet;
1772 }
1773
1774 /**************************************************************************
1775  *                              mciSendCommandW                 [WINMM.@]
1776  */
1777 DWORD WINAPI mciSendCommandW(UINT wDevID, UINT wMsg, DWORD dwParam1, DWORD dwParam2)
1778 {
1779     FIXME("(%08x, %s, %08lx, %08lx): stub\n",
1780           wDevID, MCI_MessageToString(wMsg), dwParam1, dwParam2);
1781     return MCIERR_UNSUPPORTED_FUNCTION;
1782 }
1783
1784 /**************************************************************************
1785  *                              mciSendCommand                  [MMSYSTEM.701]
1786  */
1787 DWORD WINAPI mciSendCommand16(UINT16 wDevID, UINT16 wMsg, DWORD dwParam1, DWORD dwParam2)
1788 {
1789     DWORD               dwRet;
1790
1791     TRACE("(%04X, %s, %08lX, %08lX)\n",
1792           wDevID, MCI_MessageToString(wMsg), dwParam1, dwParam2);
1793
1794     dwRet = MCI_SendCommand(wDevID, wMsg, dwParam1, dwParam2, FALSE);
1795     dwRet = MCI_CleanUp(dwRet, wMsg, dwParam2, FALSE);
1796     TRACE("=> %ld\n", dwRet);
1797     return dwRet;
1798 }
1799
1800 /**************************************************************************
1801  *                              mciGetDeviceID                  [MMSYSTEM.703]
1802  */
1803 UINT16 WINAPI mciGetDeviceID16(LPCSTR lpstrName)
1804 {
1805     TRACE("(\"%s\")\n", lpstrName);
1806
1807     return MCI_GetDriverFromString(lpstrName);
1808 }
1809
1810 /**************************************************************************
1811  *                              mciGetDeviceIDA                 [WINMM.@]
1812  */
1813 UINT WINAPI mciGetDeviceIDA(LPCSTR lpstrName)
1814 {
1815     return MCI_GetDriverFromString(lpstrName);
1816 }
1817
1818 /**************************************************************************
1819  *                              mciGetDeviceIDW                 [WINMM.@]
1820  */
1821 UINT WINAPI mciGetDeviceIDW(LPCWSTR lpwstrName)
1822 {
1823     LPSTR       lpstrName;
1824     UINT        ret;
1825
1826     lpstrName = HEAP_strdupWtoA(GetProcessHeap(), 0, lpwstrName);
1827     ret = MCI_GetDriverFromString(lpstrName);
1828     HeapFree(GetProcessHeap(), 0, lpstrName);
1829     return ret;
1830 }
1831
1832 /**************************************************************************
1833  *                              MCI_DefYieldProc                [internal]
1834  */
1835 UINT WINAPI MCI_DefYieldProc(MCIDEVICEID wDevID, DWORD data)
1836 {
1837     INT16       ret;
1838
1839     TRACE("(0x%04x, 0x%08lx)\n", wDevID, data);
1840
1841     if ((HIWORD(data) != 0 && GetActiveWindow() != HIWORD(data)) ||
1842         (GetAsyncKeyState(LOWORD(data)) & 1) == 0) {
1843         UserYield16();
1844         ret = 0;
1845     } else {
1846         MSG             msg;
1847
1848         msg.hwnd = HIWORD(data);
1849         while (!PeekMessageA(&msg, HIWORD(data), WM_KEYFIRST, WM_KEYLAST, PM_REMOVE));
1850         ret = -1;
1851     }
1852     return ret;
1853 }
1854
1855 /**************************************************************************
1856  *                              mciSetYieldProc                 [MMSYSTEM.714]
1857  */
1858 BOOL16 WINAPI mciSetYieldProc16(UINT16 uDeviceID, YIELDPROC16 fpYieldProc, DWORD dwYieldData)
1859 {
1860     LPWINE_MCIDRIVER    wmd;
1861
1862     TRACE("(%u, %p, %08lx)\n", uDeviceID, fpYieldProc, dwYieldData);
1863
1864     if (!(wmd = MCI_GetDriver(uDeviceID))) {
1865         WARN("Bad uDeviceID\n");
1866         return FALSE;
1867     }
1868
1869     wmd->lpfnYieldProc = (YIELDPROC)fpYieldProc;
1870     wmd->dwYieldData   = dwYieldData;
1871     wmd->bIs32         = FALSE;
1872
1873     return TRUE;
1874 }
1875
1876 /**************************************************************************
1877  *                              mciSetYieldProc                 [WINMM.@]
1878  */
1879 BOOL WINAPI mciSetYieldProc(UINT uDeviceID, YIELDPROC fpYieldProc, DWORD dwYieldData)
1880 {
1881     LPWINE_MCIDRIVER    wmd;
1882
1883     TRACE("(%u, %p, %08lx)\n", uDeviceID, fpYieldProc, dwYieldData);
1884
1885     if (!(wmd = MCI_GetDriver(uDeviceID))) {
1886         WARN("Bad uDeviceID\n");
1887         return FALSE;
1888     }
1889
1890     wmd->lpfnYieldProc = fpYieldProc;
1891     wmd->dwYieldData   = dwYieldData;
1892     wmd->bIs32         = TRUE;
1893
1894     return TRUE;
1895 }
1896
1897 /**************************************************************************
1898  *                              mciGetDeviceIDFromElementID     [MMSYSTEM.715]
1899  */
1900 UINT16 WINAPI mciGetDeviceIDFromElementID16(DWORD dwElementID, LPCSTR lpstrType)
1901 {
1902     FIXME("(%lu, %s) stub\n", dwElementID, lpstrType);
1903     return 0;
1904 }
1905
1906 /**************************************************************************
1907  *                              mciGetDeviceIDFromElementIDW    [WINMM.@]
1908  */
1909 UINT WINAPI mciGetDeviceIDFromElementIDW(DWORD dwElementID, LPCWSTR lpstrType)
1910 {
1911     /* FIXME: that's rather strange, there is no
1912      * mciGetDeviceIDFromElementID32A in winmm.spec
1913      */
1914     FIXME("(%lu, %p) stub\n", dwElementID, lpstrType);
1915     return 0;
1916 }
1917
1918 /**************************************************************************
1919  *                              mciGetYieldProc                 [MMSYSTEM.716]
1920  */
1921 YIELDPROC16 WINAPI mciGetYieldProc16(UINT16 uDeviceID, DWORD* lpdwYieldData)
1922 {
1923     LPWINE_MCIDRIVER    wmd;
1924
1925     TRACE("(%u, %p)\n", uDeviceID, lpdwYieldData);
1926
1927     if (!(wmd = MCI_GetDriver(uDeviceID))) {
1928         WARN("Bad uDeviceID\n");
1929         return NULL;
1930     }
1931     if (!wmd->lpfnYieldProc) {
1932         WARN("No proc set\n");
1933         return NULL;
1934     }
1935     if (wmd->bIs32) {
1936         WARN("Proc is 32 bit\n");
1937         return NULL;
1938     }
1939     return (YIELDPROC16)wmd->lpfnYieldProc;
1940 }
1941
1942 /**************************************************************************
1943  *                              mciGetYieldProc                 [WINMM.@]
1944  */
1945 YIELDPROC WINAPI mciGetYieldProc(UINT uDeviceID, DWORD* lpdwYieldData)
1946 {
1947     LPWINE_MCIDRIVER    wmd;
1948
1949     TRACE("(%u, %p)\n", uDeviceID, lpdwYieldData);
1950
1951     if (!(wmd = MCI_GetDriver(uDeviceID))) {
1952         WARN("Bad uDeviceID\n");
1953         return NULL;
1954     }
1955     if (!wmd->lpfnYieldProc) {
1956         WARN("No proc set\n");
1957         return NULL;
1958     }
1959     if (!wmd->bIs32) {
1960         WARN("Proc is 32 bit\n");
1961         return NULL;
1962     }
1963     return wmd->lpfnYieldProc;
1964 }
1965
1966 /**************************************************************************
1967  *                              mciGetCreatorTask               [MMSYSTEM.717]
1968  */
1969 HTASK16 WINAPI mciGetCreatorTask16(UINT16 uDeviceID)
1970 {
1971     return mciGetCreatorTask(uDeviceID);
1972 }
1973
1974 /**************************************************************************
1975  *                              mciGetCreatorTask               [WINMM.@]
1976  */
1977 HTASK WINAPI mciGetCreatorTask(UINT uDeviceID)
1978 {
1979     LPWINE_MCIDRIVER    wmd;
1980     HTASK               ret;
1981
1982     TRACE("(%u)\n", uDeviceID);
1983
1984     ret = (!(wmd = MCI_GetDriver(uDeviceID))) ? 0 : wmd->hCreatorTask;
1985
1986     TRACE("=> %04x\n", ret);
1987     return ret;
1988 }
1989
1990 /**************************************************************************
1991  *                              mciDriverYield                  [MMSYSTEM.710]
1992  */
1993 UINT16 WINAPI mciDriverYield16(UINT16 uDeviceID)
1994 {
1995     LPWINE_MCIDRIVER    wmd;
1996     UINT16              ret = 0;
1997
1998     /*    TRACE("(%04x)\n", uDeviceID); */
1999
2000     if (!(wmd = MCI_GetDriver(uDeviceID)) || !wmd->lpfnYieldProc || wmd->bIs32) {
2001         UserYield16();
2002     } else {
2003         ret = wmd->lpfnYieldProc(uDeviceID, wmd->dwYieldData);
2004     }
2005
2006     return ret;
2007 }
2008
2009 /**************************************************************************
2010  *                      mciDriverYield                          [WINMM.@]
2011  */
2012 UINT WINAPI mciDriverYield(UINT uDeviceID)
2013 {
2014     LPWINE_MCIDRIVER    wmd;
2015     UINT                ret = 0;
2016
2017     TRACE("(%04x)\n", uDeviceID);
2018
2019     if (!(wmd = MCI_GetDriver(uDeviceID)) || !wmd->lpfnYieldProc || !wmd->bIs32) {
2020         UserYield16();
2021     } else {
2022         ret = wmd->lpfnYieldProc(uDeviceID, wmd->dwYieldData);
2023     }
2024
2025     return ret;
2026 }
2027
2028 /**************************************************************************
2029  *                              midiOutGetNumDevs       [WINMM.@]
2030  */
2031 UINT WINAPI midiOutGetNumDevs(void)
2032 {
2033     return MMDRV_GetNum(MMDRV_MIDIOUT);
2034 }
2035
2036 /**************************************************************************
2037  *                              midiOutGetNumDevs       [MMSYSTEM.201]
2038  */
2039 UINT16 WINAPI midiOutGetNumDevs16(void)
2040 {
2041     return MMDRV_GetNum(MMDRV_MIDIOUT);
2042 }
2043
2044 /**************************************************************************
2045  *                              midiOutGetDevCapsW      [WINMM.@]
2046  */
2047 UINT WINAPI midiOutGetDevCapsW(UINT uDeviceID, LPMIDIOUTCAPSW lpCaps,
2048                                UINT uSize)
2049 {
2050     MIDIOUTCAPSA        mocA;
2051     UINT                ret;
2052
2053     ret = midiOutGetDevCapsA(uDeviceID, &mocA, sizeof(mocA));
2054     lpCaps->wMid                = mocA.wMid;
2055     lpCaps->wPid                = mocA.wPid;
2056     lpCaps->vDriverVersion      = mocA.vDriverVersion;
2057     MultiByteToWideChar( CP_ACP, 0, mocA.szPname, -1, lpCaps->szPname,
2058                          sizeof(lpCaps->szPname)/sizeof(WCHAR) );
2059     lpCaps->wTechnology         = mocA.wTechnology;
2060     lpCaps->wVoices             = mocA.wVoices;
2061     lpCaps->wNotes              = mocA.wNotes;
2062     lpCaps->wChannelMask        = mocA.wChannelMask;
2063     lpCaps->dwSupport           = mocA.dwSupport;
2064     return ret;
2065 }
2066
2067 /**************************************************************************
2068  *                              midiOutGetDevCapsA      [WINMM.@]
2069  */
2070 UINT WINAPI midiOutGetDevCapsA(UINT uDeviceID, LPMIDIOUTCAPSA lpCaps,
2071                                UINT uSize)
2072 {
2073     LPWINE_MLD  wmld;
2074
2075     TRACE("(%u, %p, %u);\n", uDeviceID, lpCaps, uSize);
2076
2077     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2078
2079     if ((wmld = MMDRV_Get(uDeviceID, MMDRV_MIDIOUT, TRUE)) == NULL)
2080         return MMSYSERR_INVALHANDLE;
2081
2082     return MMDRV_Message(wmld, MODM_GETDEVCAPS, (DWORD)lpCaps, uSize, TRUE);
2083 }
2084
2085 /**************************************************************************
2086  *                              midiOutGetDevCaps       [MMSYSTEM.202]
2087  */
2088 UINT16 WINAPI midiOutGetDevCaps16(UINT16 uDeviceID, LPMIDIOUTCAPS16 lpCaps,
2089                                   UINT16 uSize)
2090 {
2091     MIDIOUTCAPSA        capsA;
2092     UINT                dwRet;
2093
2094     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2095
2096     dwRet = midiOutGetDevCapsA(uDeviceID, &capsA, sizeof(capsA));
2097     if (dwRet == MMSYSERR_NOERROR) {
2098         lpCaps->wMid            = capsA.wMid;
2099         lpCaps->wPid            = capsA.wPid;
2100         lpCaps->vDriverVersion  = capsA.vDriverVersion;
2101         strcpy(lpCaps->szPname, capsA.szPname);
2102         lpCaps->wTechnology     = capsA.wTechnology;
2103         lpCaps->wVoices         = capsA.wVoices;
2104         lpCaps->wNotes          = capsA.wNotes;
2105         lpCaps->wChannelMask    = capsA.wChannelMask;
2106         lpCaps->dwSupport       = capsA.dwSupport;
2107     }
2108     return dwRet;
2109  }
2110
2111 /**************************************************************************
2112  *                              MIDI_GetErrorText               [internal]
2113  */
2114 static  UINT16  MIDI_GetErrorText(UINT16 uError, LPSTR lpText, UINT16 uSize)
2115 {
2116     UINT16              ret = MMSYSERR_BADERRNUM;
2117
2118     if (lpText == NULL) {
2119         ret = MMSYSERR_INVALPARAM;
2120     } else if (uSize == 0) {
2121         ret = MMSYSERR_NOERROR;
2122     } else if (
2123                /* test has been removed 'coz MMSYSERR_BASE is 0, and gcc did emit
2124                 * a warning for the test was always true */
2125                (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||
2126                (uError >= MIDIERR_BASE  && uError <= MIDIERR_LASTERROR)) {
2127
2128         if (LoadStringA(MULTIMEDIA_GetIData()->hWinMM32Instance,
2129                         uError, lpText, uSize) > 0) {
2130             ret = MMSYSERR_NOERROR;
2131         }
2132     }
2133     return ret;
2134 }
2135
2136 /**************************************************************************
2137  *                              midiOutGetErrorTextA    [WINMM.@]
2138  */
2139 UINT WINAPI midiOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
2140 {
2141     return MIDI_GetErrorText(uError, lpText, uSize);
2142 }
2143
2144 /**************************************************************************
2145  *                              midiOutGetErrorTextW    [WINMM.@]
2146  */
2147 UINT WINAPI midiOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
2148 {
2149     LPSTR       xstr = HeapAlloc(GetProcessHeap(), 0, uSize);
2150     UINT        ret;
2151
2152     ret = MIDI_GetErrorText(uError, xstr, uSize);
2153     MultiByteToWideChar( CP_ACP, 0, xstr, -1, lpText, uSize );
2154     HeapFree(GetProcessHeap(), 0, xstr);
2155     return ret;
2156 }
2157
2158 /**************************************************************************
2159  *                              midiOutGetErrorText     [MMSYSTEM.203]
2160  */
2161 UINT16 WINAPI midiOutGetErrorText16(UINT16 uError, LPSTR lpText, UINT16 uSize)
2162 {
2163     return MIDI_GetErrorText(uError, lpText, uSize);
2164 }
2165
2166 /**************************************************************************
2167  *                              MIDI_OutAlloc                   [internal]
2168  */
2169 static  LPWINE_MIDI     MIDI_OutAlloc(HMIDIOUT* lphMidiOut, LPDWORD lpdwCallback,
2170                                       LPDWORD lpdwInstance, LPDWORD lpdwFlags,
2171                                       DWORD cIDs, MIDIOPENSTRMID* lpIDs, BOOL bFrom32)
2172 {
2173     HMIDIOUT            hMidiOut;
2174     LPWINE_MIDI         lpwm;
2175     UINT                size;
2176
2177     size = sizeof(WINE_MIDI) + (cIDs ? (cIDs-1) : 0) * sizeof(MIDIOPENSTRMID);
2178
2179     lpwm = (LPWINE_MIDI)MMDRV_Alloc(size, MMDRV_MIDIOUT, &hMidiOut, lpdwFlags,
2180                                     lpdwCallback, lpdwInstance, bFrom32);
2181
2182     if (lphMidiOut != NULL)
2183         *lphMidiOut = hMidiOut;
2184
2185     if (lpwm) {
2186         lpwm->mod.hMidi = hMidiOut;
2187         lpwm->mod.dwCallback = *lpdwCallback;
2188         lpwm->mod.dwInstance = *lpdwInstance;
2189         lpwm->mod.dnDevNode = 0;
2190         lpwm->mod.cIds = cIDs;
2191         if (cIDs)
2192             memcpy(&(lpwm->mod.rgIds), lpIDs, cIDs * sizeof(MIDIOPENSTRMID));
2193     }
2194     return lpwm;
2195 }
2196
2197 UINT MMSYSTEM_midiOutOpen(HMIDIOUT* lphMidiOut, UINT uDeviceID, DWORD dwCallback,
2198                           DWORD dwInstance, DWORD dwFlags, BOOL bFrom32)
2199 {
2200     HMIDIOUT            hMidiOut;
2201     LPWINE_MIDI         lpwm;
2202     UINT                dwRet = 0;
2203
2204     TRACE("(%p, %d, %08lX, %08lX, %08lX);\n",
2205           lphMidiOut, uDeviceID, dwCallback, dwInstance, dwFlags);
2206
2207     if (lphMidiOut != NULL) *lphMidiOut = 0;
2208
2209     lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &dwFlags,
2210                          0, NULL, bFrom32);
2211
2212     if (lpwm == NULL)
2213         return MMSYSERR_NOMEM;
2214
2215     lpwm->mld.uDeviceID = uDeviceID;
2216
2217     dwRet = MMDRV_Open((LPWINE_MLD)lpwm, MODM_OPEN, (DWORD)&lpwm->mod,
2218                        dwFlags);
2219
2220     if (dwRet != MMSYSERR_NOERROR) {
2221         MMDRV_Free(hMidiOut, (LPWINE_MLD)lpwm);
2222         hMidiOut = 0;
2223     }
2224
2225     if (lphMidiOut) *lphMidiOut = hMidiOut;
2226     TRACE("=> %d hMidi=%04x\n", dwRet, hMidiOut);
2227
2228     return dwRet;
2229 }
2230
2231 /**************************************************************************
2232  *                              midiOutOpen             [WINMM.@]
2233  */
2234 UINT WINAPI midiOutOpen(HMIDIOUT* lphMidiOut, UINT uDeviceID,
2235                         DWORD dwCallback, DWORD dwInstance, DWORD dwFlags)
2236 {
2237     return MMSYSTEM_midiOutOpen(lphMidiOut, uDeviceID, dwCallback,
2238                                 dwInstance, dwFlags, TRUE);
2239 }
2240
2241 /**************************************************************************
2242  *                              midiOutOpen             [MMSYSTEM.204]
2243  */
2244 UINT16 WINAPI midiOutOpen16(HMIDIOUT16* lphMidiOut, UINT16 uDeviceID,
2245                             DWORD dwCallback, DWORD dwInstance, DWORD dwFlags)
2246 {
2247     HMIDIOUT    hmo;
2248     UINT        ret;
2249
2250     ret = MMSYSTEM_midiOutOpen(&hmo, uDeviceID, dwCallback, dwInstance,
2251                                dwFlags, FALSE);
2252
2253     if (lphMidiOut != NULL) *lphMidiOut = hmo;
2254     return ret;
2255 }
2256
2257 /**************************************************************************
2258  *                              midiOutClose            [WINMM.@]
2259  */
2260 UINT WINAPI midiOutClose(HMIDIOUT hMidiOut)
2261 {
2262     LPWINE_MLD          wmld;
2263     DWORD               dwRet;
2264
2265     TRACE("(%04X)\n", hMidiOut);
2266
2267     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
2268         return MMSYSERR_INVALHANDLE;
2269
2270     dwRet = MMDRV_Close(wmld, MODM_CLOSE);
2271     MMDRV_Free(hMidiOut, wmld);
2272
2273     return dwRet;
2274 }
2275
2276 /**************************************************************************
2277  *                              midiOutClose            [MMSYSTEM.205]
2278  */
2279 UINT16 WINAPI midiOutClose16(HMIDIOUT16 hMidiOut)
2280 {
2281     return midiOutClose(hMidiOut);
2282 }
2283
2284 /**************************************************************************
2285  *                              midiOutPrepareHeader    [WINMM.@]
2286  */
2287 UINT WINAPI midiOutPrepareHeader(HMIDIOUT hMidiOut,
2288                                  MIDIHDR* lpMidiOutHdr, UINT uSize)
2289 {
2290     LPWINE_MLD          wmld;
2291
2292     TRACE("(%04X, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
2293
2294     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
2295         return MMSYSERR_INVALHANDLE;
2296
2297     return MMDRV_Message(wmld, MODM_PREPARE, (DWORD)lpMidiOutHdr, uSize, TRUE);
2298 }
2299
2300 /**************************************************************************
2301  *                              midiOutPrepareHeader    [MMSYSTEM.206]
2302  */
2303 UINT16 WINAPI midiOutPrepareHeader16(HMIDIOUT16 hMidiOut,         /* [in] */
2304                                      SEGPTR lpsegMidiOutHdr,      /* [???] */
2305                                      UINT16 uSize)                /* [in] */
2306 {
2307     LPWINE_MLD          wmld;
2308
2309     TRACE("(%04X, %08lx, %d)\n", hMidiOut, lpsegMidiOutHdr, uSize);
2310
2311     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
2312         return MMSYSERR_INVALHANDLE;
2313
2314     return MMDRV_Message(wmld, MODM_PREPARE, lpsegMidiOutHdr, uSize, FALSE);
2315 }
2316
2317 /**************************************************************************
2318  *                              midiOutUnprepareHeader  [WINMM.@]
2319  */
2320 UINT WINAPI midiOutUnprepareHeader(HMIDIOUT hMidiOut,
2321                                    MIDIHDR* lpMidiOutHdr, UINT uSize)
2322 {
2323     LPWINE_MLD          wmld;
2324
2325     TRACE("(%04X, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
2326
2327     if (!(lpMidiOutHdr->dwFlags & MHDR_PREPARED)) {
2328         return MMSYSERR_NOERROR;
2329     }
2330
2331     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
2332         return MMSYSERR_INVALHANDLE;
2333
2334     return MMDRV_Message(wmld, MODM_UNPREPARE, (DWORD)lpMidiOutHdr, uSize, TRUE);
2335 }
2336
2337 /**************************************************************************
2338  *                              midiOutUnprepareHeader  [MMSYSTEM.207]
2339  */
2340 UINT16 WINAPI midiOutUnprepareHeader16(HMIDIOUT16 hMidiOut,         /* [in] */
2341                                        SEGPTR lpsegMidiOutHdr,      /* [???] */
2342                                        UINT16 uSize)                /* [in] */
2343 {
2344     LPWINE_MLD          wmld;
2345     LPMIDIHDR16         lpMidiOutHdr = MapSL(lpsegMidiOutHdr);
2346
2347     TRACE("(%04X, %08lx, %d)\n", hMidiOut, lpsegMidiOutHdr, uSize);
2348
2349     if (!(lpMidiOutHdr->dwFlags & MHDR_PREPARED)) {
2350         return MMSYSERR_NOERROR;
2351     }
2352
2353     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
2354         return MMSYSERR_INVALHANDLE;
2355
2356     return MMDRV_Message(wmld, MODM_UNPREPARE, (DWORD)lpsegMidiOutHdr, uSize, FALSE);
2357 }
2358
2359 /**************************************************************************
2360  *                              midiOutShortMsg         [WINMM.@]
2361  */
2362 UINT WINAPI midiOutShortMsg(HMIDIOUT hMidiOut, DWORD dwMsg)
2363 {
2364     LPWINE_MLD          wmld;
2365
2366     TRACE("(%04X, %08lX)\n", hMidiOut, dwMsg);
2367
2368     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
2369         return MMSYSERR_INVALHANDLE;
2370
2371     return MMDRV_Message(wmld, MODM_DATA, dwMsg, 0L, FALSE);
2372 }
2373
2374 /**************************************************************************
2375  *                              midiOutShortMsg         [MMSYSTEM.208]
2376  */
2377 UINT16 WINAPI midiOutShortMsg16(HMIDIOUT16 hMidiOut, DWORD dwMsg)
2378 {
2379     return midiOutShortMsg(hMidiOut, dwMsg);
2380 }
2381
2382 /**************************************************************************
2383  *                              midiOutLongMsg          [WINMM.@]
2384  */
2385 UINT WINAPI midiOutLongMsg(HMIDIOUT hMidiOut,
2386                            MIDIHDR* lpMidiOutHdr, UINT uSize)
2387 {
2388     LPWINE_MLD          wmld;
2389
2390     TRACE("(%04X, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
2391
2392     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
2393         return MMSYSERR_INVALHANDLE;
2394
2395     return MMDRV_Message(wmld, MODM_LONGDATA, (DWORD)lpMidiOutHdr, uSize, TRUE);
2396 }
2397
2398 /**************************************************************************
2399  *                              midiOutLongMsg          [MMSYSTEM.209]
2400  */
2401 UINT16 WINAPI midiOutLongMsg16(HMIDIOUT16 hMidiOut,          /* [in] */
2402                                LPMIDIHDR16 lpsegMidiOutHdr,  /* [???] NOTE: SEGPTR */
2403                                UINT16 uSize)                 /* [in] */
2404 {
2405     LPWINE_MLD          wmld;
2406
2407     TRACE("(%04X, %p, %d)\n", hMidiOut, lpsegMidiOutHdr, uSize);
2408
2409     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
2410         return MMSYSERR_INVALHANDLE;
2411
2412     return MMDRV_Message(wmld, MODM_LONGDATA, (DWORD)lpsegMidiOutHdr, uSize, FALSE);
2413 }
2414
2415 /**************************************************************************
2416  *                              midiOutReset            [WINMM.@]
2417  */
2418 UINT WINAPI midiOutReset(HMIDIOUT hMidiOut)
2419 {
2420     LPWINE_MLD          wmld;
2421
2422     TRACE("(%04X)\n", hMidiOut);
2423
2424     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
2425         return MMSYSERR_INVALHANDLE;
2426
2427     return MMDRV_Message(wmld, MODM_RESET, 0L, 0L, TRUE);
2428 }
2429
2430 /**************************************************************************
2431  *                              midiOutReset            [MMSYSTEM.210]
2432  */
2433 UINT16 WINAPI midiOutReset16(HMIDIOUT16 hMidiOut)
2434 {
2435     return midiOutReset(hMidiOut);
2436 }
2437
2438 /**************************************************************************
2439  *                              midiOutGetVolume        [WINMM.@]
2440  */
2441 UINT WINAPI midiOutGetVolume(UINT uDeviceID, DWORD* lpdwVolume)
2442 {
2443     LPWINE_MLD          wmld;
2444
2445     TRACE("(%04X, %p);\n", uDeviceID, lpdwVolume);
2446
2447     if ((wmld = MMDRV_Get(uDeviceID, MMDRV_MIDIOUT, TRUE)) == NULL)
2448         return MMSYSERR_INVALHANDLE;
2449
2450     return MMDRV_Message(wmld, MODM_GETVOLUME, (DWORD)lpdwVolume, 0L, TRUE);
2451 }
2452
2453 /**************************************************************************
2454  *                              midiOutGetVolume        [MMSYSTEM.211]
2455  */
2456 UINT16 WINAPI midiOutGetVolume16(UINT16 uDeviceID, DWORD* lpdwVolume)
2457 {
2458     return midiOutGetVolume(uDeviceID, lpdwVolume);
2459 }
2460
2461 /**************************************************************************
2462  *                              midiOutSetVolume        [WINMM.@]
2463  */
2464 UINT WINAPI midiOutSetVolume(UINT uDeviceID, DWORD dwVolume)
2465 {
2466     LPWINE_MLD          wmld;
2467
2468     TRACE("(%04X, %ld);\n", uDeviceID, dwVolume);
2469
2470     if ((wmld = MMDRV_Get(uDeviceID, MMDRV_MIDIOUT, TRUE)) == NULL)
2471         return MMSYSERR_INVALHANDLE;
2472
2473     return MMDRV_Message(wmld, MODM_SETVOLUME, dwVolume, 0L, TRUE);
2474 }
2475
2476 /**************************************************************************
2477  *                              midiOutSetVolume        [MMSYSTEM.212]
2478  */
2479 UINT16 WINAPI midiOutSetVolume16(UINT16 uDeviceID, DWORD dwVolume)
2480 {
2481     return midiOutSetVolume(uDeviceID, dwVolume);
2482 }
2483
2484 /**************************************************************************
2485  *                              midiOutCachePatches             [WINMM.@]
2486  */
2487 UINT WINAPI midiOutCachePatches(HMIDIOUT hMidiOut, UINT uBank,
2488                                 WORD* lpwPatchArray, UINT uFlags)
2489 {
2490     /* not really necessary to support this */
2491     FIXME("not supported yet\n");
2492     return MMSYSERR_NOTSUPPORTED;
2493 }
2494
2495 /**************************************************************************
2496  *                              midiOutCachePatches             [MMSYSTEM.213]
2497  */
2498 UINT16 WINAPI midiOutCachePatches16(HMIDIOUT16 hMidiOut, UINT16 uBank,
2499                                     WORD* lpwPatchArray, UINT16 uFlags)
2500 {
2501     return midiOutCachePatches(hMidiOut, uBank, lpwPatchArray, uFlags);
2502 }
2503
2504 /**************************************************************************
2505  *                              midiOutCacheDrumPatches [WINMM.@]
2506  */
2507 UINT WINAPI midiOutCacheDrumPatches(HMIDIOUT hMidiOut, UINT uPatch,
2508                                     WORD* lpwKeyArray, UINT uFlags)
2509 {
2510     FIXME("not supported yet\n");
2511     return MMSYSERR_NOTSUPPORTED;
2512 }
2513
2514 /**************************************************************************
2515  *                              midiOutCacheDrumPatches [MMSYSTEM.214]
2516  */
2517 UINT16 WINAPI midiOutCacheDrumPatches16(HMIDIOUT16 hMidiOut, UINT16 uPatch,
2518                                         WORD* lpwKeyArray, UINT16 uFlags)
2519 {
2520     return midiOutCacheDrumPatches16(hMidiOut, uPatch, lpwKeyArray, uFlags);
2521 }
2522
2523 /**************************************************************************
2524  *                              midiOutGetID            [WINMM.@]
2525  */
2526 UINT WINAPI midiOutGetID(HMIDIOUT hMidiOut, UINT* lpuDeviceID)
2527 {
2528     LPWINE_MLD          wmld;
2529
2530     TRACE("(%04X, %p)\n", hMidiOut, lpuDeviceID);
2531
2532     if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
2533     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
2534         return MMSYSERR_INVALHANDLE;
2535
2536     *lpuDeviceID = wmld->uDeviceID;
2537     return MMSYSERR_NOERROR;
2538 }
2539
2540 /**************************************************************************
2541  *                              midiOutGetID            [MMSYSTEM.215]
2542  */
2543 UINT16 WINAPI midiOutGetID16(HMIDIOUT16 hMidiOut, UINT16* lpuDeviceID)
2544 {
2545     LPWINE_MLD          wmld;
2546
2547     TRACE("(%04X, %p)\n", hMidiOut, lpuDeviceID);
2548
2549     if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
2550     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
2551         return MMSYSERR_INVALHANDLE;
2552
2553     *lpuDeviceID = wmld->uDeviceID;
2554     return MMSYSERR_NOERROR;
2555 }
2556
2557 /**************************************************************************
2558  *                              midiOutMessage          [WINMM.@]
2559  */
2560 DWORD WINAPI midiOutMessage(HMIDIOUT hMidiOut, UINT uMessage,
2561                             DWORD dwParam1, DWORD dwParam2)
2562 {
2563     LPWINE_MLD          wmld;
2564
2565     TRACE("(%04X, %04X, %08lX, %08lX)\n", hMidiOut, uMessage, dwParam1, dwParam2);
2566
2567     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL) {
2568         /* HACK... */
2569         if (uMessage == 0x0001) {
2570             *(LPDWORD)dwParam1 = 1;
2571             return 0;
2572         }
2573         if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) != NULL) {
2574             return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
2575         }
2576         return MMSYSERR_INVALHANDLE;
2577     }
2578
2579     switch (uMessage) {
2580     case MODM_OPEN:
2581     case MODM_CLOSE:
2582         FIXME("can't handle OPEN or CLOSE message!\n");
2583         return MMSYSERR_NOTSUPPORTED;
2584     }
2585     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
2586 }
2587
2588 /**************************************************************************
2589  *                              midiOutMessage          [MMSYSTEM.216]
2590  */
2591 DWORD WINAPI midiOutMessage16(HMIDIOUT16 hMidiOut, UINT16 uMessage,
2592                               DWORD dwParam1, DWORD dwParam2)
2593 {
2594     LPWINE_MLD          wmld;
2595
2596     TRACE("(%04X, %04X, %08lX, %08lX)\n", hMidiOut, uMessage, dwParam1, dwParam2);
2597
2598     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
2599         return MMSYSERR_INVALHANDLE;
2600
2601     switch (uMessage) {
2602     case MODM_OPEN:
2603     case MODM_CLOSE:
2604         FIXME("can't handle OPEN or CLOSE message!\n");
2605         return MMSYSERR_NOTSUPPORTED;
2606
2607     case MODM_GETVOLUME:
2608         return midiOutGetVolume16(hMidiOut, MapSL(dwParam1));
2609     case MODM_LONGDATA:
2610         return midiOutLongMsg16(hMidiOut, MapSL(dwParam1), dwParam2);
2611     case MODM_PREPARE:
2612         /* lpMidiOutHdr is still a segmented pointer for this function */
2613         return midiOutPrepareHeader16(hMidiOut, dwParam1, dwParam2);
2614     case MODM_UNPREPARE:
2615         return midiOutUnprepareHeader16(hMidiOut, dwParam1, dwParam2);
2616     }
2617     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
2618 }
2619
2620 /**************************************************************************
2621  *                              midiInGetNumDevs        [WINMM.@]
2622  */
2623 UINT WINAPI midiInGetNumDevs(void)
2624 {
2625     return MMDRV_GetNum(MMDRV_MIDIIN);
2626 }
2627
2628 /**************************************************************************
2629  *                              midiInGetNumDevs        [MMSYSTEM.301]
2630  */
2631 UINT16 WINAPI midiInGetNumDevs16(void)
2632 {
2633     return MMDRV_GetNum(MMDRV_MIDIIN);
2634 }
2635
2636 /**************************************************************************
2637  *                              midiInGetDevCapsW       [WINMM.@]
2638  */
2639 UINT WINAPI midiInGetDevCapsW(UINT uDeviceID, LPMIDIINCAPSW lpCaps, UINT uSize)
2640 {
2641     MIDIINCAPSA         micA;
2642     UINT                ret = midiInGetDevCapsA(uDeviceID, &micA, uSize);
2643
2644     if (ret == MMSYSERR_NOERROR) {
2645         lpCaps->wMid = micA.wMid;
2646         lpCaps->wPid = micA.wPid;
2647         lpCaps->vDriverVersion = micA.vDriverVersion;
2648         MultiByteToWideChar( CP_ACP, 0, micA.szPname, -1, lpCaps->szPname,
2649                              sizeof(lpCaps->szPname)/sizeof(WCHAR) );
2650         lpCaps->dwSupport = micA.dwSupport;
2651     }
2652     return ret;
2653 }
2654
2655 /**************************************************************************
2656  *                              midiInGetDevCapsA       [WINMM.@]
2657  */
2658 UINT WINAPI midiInGetDevCapsA(UINT uDeviceID, LPMIDIINCAPSA lpCaps, UINT uSize)
2659 {
2660     LPWINE_MLD  wmld;
2661
2662     TRACE("(%d, %p, %d);\n", uDeviceID, lpCaps, uSize);
2663
2664     if ((wmld = MMDRV_Get(uDeviceID, MMDRV_MIDIIN, TRUE)) == NULL)
2665         return MMSYSERR_INVALHANDLE;
2666
2667    return MMDRV_Message(wmld, MIDM_GETDEVCAPS, (DWORD)lpCaps, uSize, TRUE);
2668 }
2669
2670 /**************************************************************************
2671  *                              midiInGetDevCaps        [MMSYSTEM.302]
2672  */
2673 UINT16 WINAPI midiInGetDevCaps16(UINT16 uDeviceID, LPMIDIINCAPS16 lpCaps,
2674                                  UINT16 uSize)
2675 {
2676     MIDIINCAPSA         micA;
2677     UINT                ret = midiInGetDevCapsA(uDeviceID, &micA, uSize);
2678
2679     if (ret == MMSYSERR_NOERROR) {
2680         lpCaps->wMid = micA.wMid;
2681         lpCaps->wPid = micA.wPid;
2682         lpCaps->vDriverVersion = micA.vDriverVersion;
2683         strcpy(lpCaps->szPname, micA.szPname);
2684         lpCaps->dwSupport = micA.dwSupport;
2685     }
2686
2687     return ret;
2688 }
2689
2690 /**************************************************************************
2691  *                              midiInGetErrorTextW             [WINMM.@]
2692  */
2693 UINT WINAPI midiInGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
2694 {
2695     LPSTR       xstr = HeapAlloc(GetProcessHeap(), 0, uSize);
2696     UINT        ret = MIDI_GetErrorText(uError, xstr, uSize);
2697
2698     MultiByteToWideChar( CP_ACP, 0, xstr, -1, lpText, uSize );
2699     HeapFree(GetProcessHeap(), 0, xstr);
2700     return ret;
2701 }
2702
2703 /**************************************************************************
2704  *                              midiInGetErrorTextA             [WINMM.@]
2705  */
2706 UINT WINAPI midiInGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
2707 {
2708     return MIDI_GetErrorText(uError, lpText, uSize);
2709 }
2710
2711 /**************************************************************************
2712  *                              midiInGetErrorText              [MMSYSTEM.303]
2713  */
2714 UINT16 WINAPI midiInGetErrorText16(UINT16 uError, LPSTR lpText, UINT16 uSize)
2715 {
2716     return MIDI_GetErrorText(uError, lpText, uSize);
2717 }
2718
2719 static  UINT MMSYSTEM_midiInOpen(HMIDIIN* lphMidiIn, UINT uDeviceID, DWORD dwCallback,
2720                                  DWORD dwInstance, DWORD dwFlags, BOOL bFrom32)
2721 {
2722     HMIDIIN             hMidiIn;
2723     LPWINE_MIDI         lpwm;
2724     DWORD               dwRet = 0;
2725
2726     TRACE("(%p, %d, %08lX, %08lX, %08lX);\n",
2727           lphMidiIn, uDeviceID, dwCallback, dwInstance, dwFlags);
2728
2729     if (lphMidiIn != NULL) *lphMidiIn = 0;
2730
2731     lpwm = (LPWINE_MIDI)MMDRV_Alloc(sizeof(WINE_MIDI), MMDRV_MIDIIN, &hMidiIn,
2732                                     &dwFlags, &dwCallback, &dwInstance, bFrom32);
2733
2734     if (lpwm == NULL)
2735         return MMSYSERR_NOMEM;
2736
2737     lpwm->mod.hMidi = hMidiIn;
2738     lpwm->mod.dwCallback = dwCallback;
2739     lpwm->mod.dwInstance = dwInstance;
2740
2741     lpwm->mld.uDeviceID = uDeviceID;
2742     dwRet = MMDRV_Open(&lpwm->mld, MIDM_OPEN, (DWORD)&lpwm->mod, dwFlags);
2743
2744     if (dwRet != MMSYSERR_NOERROR) {
2745         MMDRV_Free(hMidiIn, &lpwm->mld);
2746         hMidiIn = 0;
2747     }
2748     if (lphMidiIn != NULL) *lphMidiIn = hMidiIn;
2749     TRACE("=> %ld hMidi=%04x\n", dwRet, hMidiIn);
2750
2751     return dwRet;
2752 }
2753
2754 /**************************************************************************
2755  *                              midiInOpen              [WINMM.@]
2756  */
2757 UINT WINAPI midiInOpen(HMIDIIN* lphMidiIn, UINT uDeviceID,
2758                        DWORD dwCallback, DWORD dwInstance, DWORD dwFlags)
2759 {
2760     return MMSYSTEM_midiInOpen(lphMidiIn, uDeviceID, dwCallback,
2761                                dwInstance, dwFlags, TRUE);
2762 }
2763
2764 /**************************************************************************
2765  *                              midiInOpen              [MMSYSTEM.304]
2766  */
2767 UINT16 WINAPI midiInOpen16(HMIDIIN16* lphMidiIn, UINT16 uDeviceID,
2768                            DWORD dwCallback, DWORD dwInstance, DWORD dwFlags)
2769 {
2770     HMIDIIN     xhmid;
2771     UINT        ret;
2772
2773     ret = MMSYSTEM_midiInOpen(&xhmid, uDeviceID, dwCallback, dwInstance,
2774                               dwFlags, FALSE);
2775
2776     if (lphMidiIn) *lphMidiIn = xhmid;
2777     return ret;
2778 }
2779
2780 /**************************************************************************
2781  *                              midiInClose             [WINMM.@]
2782  */
2783 UINT WINAPI midiInClose(HMIDIIN hMidiIn)
2784 {
2785     LPWINE_MLD          wmld;
2786     DWORD               dwRet;
2787
2788     TRACE("(%04X)\n", hMidiIn);
2789
2790     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
2791         return MMSYSERR_INVALHANDLE;
2792
2793     dwRet = MMDRV_Close(wmld, MIDM_CLOSE);
2794     MMDRV_Free(hMidiIn, wmld);
2795     return dwRet;
2796 }
2797
2798 /**************************************************************************
2799  *                              midiInClose             [MMSYSTEM.305]
2800  */
2801 UINT16 WINAPI midiInClose16(HMIDIIN16 hMidiIn)
2802 {
2803     return midiInClose(hMidiIn);
2804 }
2805
2806 /**************************************************************************
2807  *                              midiInPrepareHeader     [WINMM.@]
2808  */
2809 UINT WINAPI midiInPrepareHeader(HMIDIIN hMidiIn,
2810                                 MIDIHDR* lpMidiInHdr, UINT uSize)
2811 {
2812     LPWINE_MLD          wmld;
2813
2814     TRACE("(%04X, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
2815
2816     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
2817         return MMSYSERR_INVALHANDLE;
2818
2819     return MMDRV_Message(wmld, MIDM_PREPARE, (DWORD)lpMidiInHdr, uSize, TRUE);
2820 }
2821
2822 /**************************************************************************
2823  *                              midiInPrepareHeader     [MMSYSTEM.306]
2824  */
2825 UINT16 WINAPI midiInPrepareHeader16(HMIDIIN16 hMidiIn,         /* [in] */
2826                                     SEGPTR lpsegMidiInHdr,     /* [???] */
2827                                     UINT16 uSize)              /* [in] */
2828 {
2829     LPWINE_MLD          wmld;
2830
2831     TRACE("(%04X, %08lx, %d)\n", hMidiIn, lpsegMidiInHdr, uSize);
2832
2833     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
2834         return MMSYSERR_INVALHANDLE;
2835
2836     return MMDRV_Message(wmld, MIDM_PREPARE, (DWORD)lpsegMidiInHdr, uSize, FALSE);
2837 }
2838
2839 /**************************************************************************
2840  *                              midiInUnprepareHeader   [WINMM.@]
2841  */
2842 UINT WINAPI midiInUnprepareHeader(HMIDIIN hMidiIn,
2843                                   MIDIHDR* lpMidiInHdr, UINT uSize)
2844 {
2845     LPWINE_MLD          wmld;
2846
2847     TRACE("(%04X, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
2848
2849     if (!(lpMidiInHdr->dwFlags & MHDR_PREPARED)) {
2850         return MMSYSERR_NOERROR;
2851     }
2852
2853     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
2854         return MMSYSERR_INVALHANDLE;
2855
2856     return MMDRV_Message(wmld, MIDM_UNPREPARE, (DWORD)lpMidiInHdr, uSize, TRUE);
2857 }
2858
2859 /**************************************************************************
2860  *                              midiInUnprepareHeader   [MMSYSTEM.307]
2861  */
2862 UINT16 WINAPI midiInUnprepareHeader16(HMIDIIN16 hMidiIn,         /* [in] */
2863                                       SEGPTR lpsegMidiInHdr,     /* [???] */
2864                                       UINT16 uSize)              /* [in] */
2865 {
2866     LPWINE_MLD          wmld;
2867     LPMIDIHDR16         lpMidiInHdr = MapSL(lpsegMidiInHdr);
2868
2869     TRACE("(%04X, %08lx, %d)\n", hMidiIn, lpsegMidiInHdr, uSize);
2870
2871     if (!(lpMidiInHdr->dwFlags & MHDR_PREPARED)) {
2872         return MMSYSERR_NOERROR;
2873     }
2874
2875     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
2876         return MMSYSERR_INVALHANDLE;
2877
2878     return MMDRV_Message(wmld, MIDM_UNPREPARE, (DWORD)lpsegMidiInHdr, uSize, FALSE);
2879 }
2880
2881 /**************************************************************************
2882  *                              midiInAddBuffer         [WINMM.@]
2883  */
2884 UINT WINAPI midiInAddBuffer(HMIDIIN hMidiIn,
2885                             MIDIHDR* lpMidiInHdr, UINT uSize)
2886 {
2887     LPWINE_MLD          wmld;
2888
2889     TRACE("(%04X, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
2890
2891     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
2892         return MMSYSERR_INVALHANDLE;
2893
2894     return MMDRV_Message(wmld, MIDM_ADDBUFFER, (DWORD)lpMidiInHdr, uSize, TRUE);
2895 }
2896
2897 /**************************************************************************
2898  *                              midiInAddBuffer         [MMSYSTEM.308]
2899  */
2900 UINT16 WINAPI midiInAddBuffer16(HMIDIIN16 hMidiIn,         /* [in] */
2901                                 MIDIHDR16* lpsegMidiInHdr, /* [???] NOTE: SEGPTR */
2902                                 UINT16 uSize)              /* [in] */
2903 {
2904     LPWINE_MLD          wmld;
2905
2906     TRACE("(%04X, %p, %d)\n", hMidiIn, lpsegMidiInHdr, uSize);
2907
2908     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
2909         return MMSYSERR_INVALHANDLE;
2910
2911     return MMDRV_Message(wmld, MIDM_ADDBUFFER, (DWORD)lpsegMidiInHdr, uSize, FALSE);
2912 }
2913
2914 /**************************************************************************
2915  *                              midiInStart                     [WINMM.@]
2916  */
2917 UINT WINAPI midiInStart(HMIDIIN hMidiIn)
2918 {
2919     LPWINE_MLD          wmld;
2920
2921     TRACE("(%04X)\n", hMidiIn);
2922
2923     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
2924         return MMSYSERR_INVALHANDLE;
2925
2926     return MMDRV_Message(wmld, MIDM_START, 0L, 0L, TRUE);
2927 }
2928
2929 /**************************************************************************
2930  *                              midiInStart                     [MMSYSTEM.309]
2931  */
2932 UINT16 WINAPI midiInStart16(HMIDIIN16 hMidiIn)
2933 {
2934     return midiInStart(hMidiIn);
2935 }
2936
2937 /**************************************************************************
2938  *                              midiInStop                      [WINMM.@]
2939  */
2940 UINT WINAPI midiInStop(HMIDIIN hMidiIn)
2941 {
2942     LPWINE_MLD          wmld;
2943
2944     TRACE("(%04X)\n", hMidiIn);
2945
2946     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
2947         return MMSYSERR_INVALHANDLE;
2948
2949     return MMDRV_Message(wmld, MIDM_STOP, 0L, 0L, TRUE);
2950 }
2951
2952 /**************************************************************************
2953  *                              midiInStop                      [MMSYSTEM.310]
2954  */
2955 UINT16 WINAPI midiInStop16(HMIDIIN16 hMidiIn)
2956 {
2957     return midiInStop(hMidiIn);
2958 }
2959
2960 /**************************************************************************
2961  *                              midiInReset                     [WINMM.@]
2962  */
2963 UINT WINAPI midiInReset(HMIDIIN hMidiIn)
2964 {
2965     LPWINE_MLD          wmld;
2966
2967     TRACE("(%04X)\n", hMidiIn);
2968
2969     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
2970         return MMSYSERR_INVALHANDLE;
2971
2972     return MMDRV_Message(wmld, MIDM_RESET, 0L, 0L, TRUE);
2973 }
2974
2975 /**************************************************************************
2976  *                              midiInReset                     [MMSYSTEM.311]
2977  */
2978 UINT16 WINAPI midiInReset16(HMIDIIN16 hMidiIn)
2979 {
2980     return midiInReset(hMidiIn);
2981 }
2982
2983 /**************************************************************************
2984  *                              midiInGetID                     [WINMM.@]
2985  */
2986 UINT WINAPI midiInGetID(HMIDIIN hMidiIn, UINT* lpuDeviceID)
2987 {
2988     LPWINE_MLD          wmld;
2989
2990     TRACE("(%04X, %p)\n", hMidiIn, lpuDeviceID);
2991
2992     if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
2993
2994     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, TRUE)) == NULL)
2995         return MMSYSERR_INVALHANDLE;
2996
2997     *lpuDeviceID = wmld->uDeviceID;
2998
2999     return MMSYSERR_NOERROR;
3000 }
3001
3002 /**************************************************************************
3003  *                              midiInGetID                     [MMSYSTEM.312]
3004  */
3005 UINT16 WINAPI midiInGetID16(HMIDIIN16 hMidiIn, UINT16* lpuDeviceID)
3006 {
3007     LPWINE_MLD          wmld;
3008
3009     TRACE("(%04X, %p)\n", hMidiIn, lpuDeviceID);
3010
3011     if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
3012
3013     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, TRUE)) == NULL)
3014         return MMSYSERR_INVALHANDLE;
3015
3016     *lpuDeviceID = wmld->uDeviceID;
3017
3018     return MMSYSERR_NOERROR;
3019 }
3020
3021 /**************************************************************************
3022  *                              midiInMessage           [WINMM.@]
3023  */
3024 DWORD WINAPI midiInMessage(HMIDIIN hMidiIn, UINT uMessage,
3025                            DWORD dwParam1, DWORD dwParam2)
3026 {
3027     LPWINE_MLD          wmld;
3028
3029     TRACE("(%04X, %04X, %08lX, %08lX)\n", hMidiIn, uMessage, dwParam1, dwParam2);
3030
3031     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
3032         return MMSYSERR_INVALHANDLE;
3033
3034     switch (uMessage) {
3035     case MIDM_OPEN:
3036     case MIDM_CLOSE:
3037         FIXME("can't handle OPEN or CLOSE message!\n");
3038         return MMSYSERR_NOTSUPPORTED;
3039     }
3040     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
3041 }
3042
3043 /**************************************************************************
3044  *                              midiInMessage           [MMSYSTEM.313]
3045  */
3046 DWORD WINAPI midiInMessage16(HMIDIIN16 hMidiIn, UINT16 uMessage,
3047                              DWORD dwParam1, DWORD dwParam2)
3048 {
3049     LPWINE_MLD          wmld;
3050
3051     TRACE("(%04X, %04X, %08lX, %08lX)\n", hMidiIn, uMessage, dwParam1, dwParam2);
3052
3053     switch (uMessage) {
3054     case MIDM_OPEN:
3055     case MIDM_CLOSE:
3056         FIXME("can't handle OPEN or CLOSE message!\n");
3057         return MMSYSERR_NOTSUPPORTED;
3058
3059     case MIDM_GETDEVCAPS:
3060         return midiInGetDevCaps16(hMidiIn, MapSL(dwParam1), dwParam2);
3061     case MIDM_PREPARE:
3062         return midiInPrepareHeader16(hMidiIn, dwParam1, dwParam2);
3063     case MIDM_UNPREPARE:
3064         return midiInUnprepareHeader16(hMidiIn, dwParam1, dwParam2);
3065     case MIDM_ADDBUFFER:
3066         return midiInAddBuffer16(hMidiIn, MapSL(dwParam1), dwParam2);
3067     }
3068
3069     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
3070         return MMSYSERR_INVALHANDLE;
3071
3072     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, FALSE);
3073 }
3074
3075 typedef struct WINE_MIDIStream {
3076     HMIDIOUT                    hDevice;
3077     HANDLE                      hThread;
3078     DWORD                       dwThreadID;
3079     DWORD                       dwTempo;
3080     DWORD                       dwTimeDiv;
3081     DWORD                       dwPositionMS;
3082     DWORD                       dwPulses;
3083     DWORD                       dwStartTicks;
3084     WORD                        wFlags;
3085     HANDLE                      hEvent;
3086     LPMIDIHDR                   lpMidiHdr;
3087 } WINE_MIDIStream;
3088
3089 #define WINE_MSM_HEADER         (WM_USER+0)
3090 #define WINE_MSM_STOP           (WM_USER+1)
3091
3092 /**************************************************************************
3093  *                              MMSYSTEM_GetMidiStream          [internal]
3094  */
3095 static  BOOL    MMSYSTEM_GetMidiStream(HMIDISTRM hMidiStrm, WINE_MIDIStream** lpMidiStrm, WINE_MIDI** lplpwm)
3096 {
3097     WINE_MIDI* lpwm = (LPWINE_MIDI)MMDRV_Get(hMidiStrm, MMDRV_MIDIOUT, FALSE);
3098
3099     if (lplpwm)
3100         *lplpwm = lpwm;
3101
3102     if (lpwm == NULL) {
3103         return FALSE;
3104     }
3105
3106     *lpMidiStrm = (WINE_MIDIStream*)lpwm->mod.rgIds.dwStreamID;
3107
3108     return *lpMidiStrm != NULL;
3109 }
3110
3111 /**************************************************************************
3112  *                              MMSYSTEM_MidiStream_Convert     [internal]
3113  */
3114 static  DWORD   MMSYSTEM_MidiStream_Convert(WINE_MIDIStream* lpMidiStrm, DWORD pulse)
3115 {
3116     DWORD       ret = 0;
3117
3118     if (lpMidiStrm->dwTimeDiv == 0) {
3119         FIXME("Shouldn't happen. lpMidiStrm->dwTimeDiv = 0\n");
3120     } else if (lpMidiStrm->dwTimeDiv > 0x8000) { /* SMPTE, unchecked FIXME? */
3121         int     nf = -(char)HIBYTE(lpMidiStrm->dwTimeDiv);      /* number of frames     */
3122         int     nsf = LOBYTE(lpMidiStrm->dwTimeDiv);            /* number of sub-frames */
3123         ret = (pulse * 1000) / (nf * nsf);
3124     } else {
3125         ret = (DWORD)((double)pulse * ((double)lpMidiStrm->dwTempo / 1000) /
3126                       (double)lpMidiStrm->dwTimeDiv);
3127     }
3128
3129     return ret;
3130 }
3131
3132 /**************************************************************************
3133  *                      MMSYSTEM_MidiStream_MessageHandler      [internal]
3134  */
3135 static  BOOL    MMSYSTEM_MidiStream_MessageHandler(WINE_MIDIStream* lpMidiStrm, LPWINE_MIDI lpwm, LPMSG msg)
3136 {
3137     LPMIDIHDR   lpMidiHdr;
3138     LPMIDIHDR*  lpmh;
3139     LPBYTE      lpData;
3140
3141     switch (msg->message) {
3142     case WM_QUIT:
3143         SetEvent(lpMidiStrm->hEvent);
3144         return FALSE;
3145     case WINE_MSM_STOP:
3146         TRACE("STOP\n");
3147         /* this is not quite what MS doc says... */
3148         midiOutReset(lpMidiStrm->hDevice);
3149         /* empty list of already submitted buffers */
3150         for (lpMidiHdr = lpMidiStrm->lpMidiHdr; lpMidiHdr; lpMidiHdr = (LPMIDIHDR)lpMidiHdr->lpNext) {
3151             lpMidiHdr->dwFlags |= MHDR_DONE;
3152             lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
3153
3154             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags, lpMidiStrm->hDevice,
3155                            MM_MOM_DONE, lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
3156         }
3157         lpMidiStrm->lpMidiHdr = 0;
3158         SetEvent(lpMidiStrm->hEvent);
3159         break;
3160     case WINE_MSM_HEADER:
3161         /* sets initial tick count for first MIDIHDR */
3162         if (!lpMidiStrm->dwStartTicks)
3163             lpMidiStrm->dwStartTicks = GetTickCount();
3164
3165         /* FIXME(EPP): "I don't understand the content of the first MIDIHDR sent
3166          * by native mcimidi, it doesn't look like a correct one".
3167          * this trick allows to throw it away... but I don't like it.
3168          * It looks like part of the file I'm trying to play and definitively looks
3169          * like raw midi content
3170          * I'd really like to understand why native mcimidi sends it. Perhaps a bad
3171          * synchronization issue where native mcimidi is still processing raw MIDI
3172          * content before generating MIDIEVENTs ?
3173          *
3174          * 4c 04 89 3b 00 81 7c 99 3b 43 00 99 23 5e 04 89 L..;..|.;C..#^..
3175          * 3b 00 00 89 23 00 7c 99 3b 45 00 99 28 62 04 89 ;...#.|.;E..(b..
3176          * 3b 00 00 89 28 00 81 7c 99 3b 4e 00 99 23 5e 04 ;...(..|.;N..#^.
3177          * 89 3b 00 00 89 23 00 7c 99 3b 45 00 99 23 78 04 .;...#.|.;E..#x.
3178          * 89 3b 00 00 89 23 00 81 7c 99 3b 48 00 99 23 5e .;...#..|.;H..#^
3179          * 04 89 3b 00 00 89 23 00 7c 99 3b 4e 00 99 28 62 ..;...#.|.;N..(b
3180          * 04 89 3b 00 00 89 28 00 81 7c 99 39 4c 00 99 23 ..;...(..|.9L..#
3181          * 5e 04 89 39 00 00 89 23 00 82 7c 99 3b 4c 00 99 ^..9...#..|.;L..
3182          * 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 00 99 #^..;...#.|.;H..
3183          * 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b 3f 04 (b..;...(..|.;?.
3184          * 89 3b 00 1c 99 23 5e 04 89 23 00 5c 99 3b 45 00 .;...#^..#.\.;E.
3185          * 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 3b 46 .#x..;...#..|.;F
3186          * 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 ..#^..;...#.|.;H
3187          * 00 99 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b ..(b..;...(..|.;
3188          * 46 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b F..#^..;...#.|.;
3189          * 48 00 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 H..#x..;...#..|.
3190          * 3b 4c 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 ;L..#^..;...#.|.
3191          */
3192         lpMidiHdr = (LPMIDIHDR)msg->lParam;
3193         lpData = lpMidiHdr->lpData;
3194         TRACE("Adding %s lpMidiHdr=%p [lpData=0x%08lx dwBufferLength=%lu/%lu dwFlags=0x%08lx size=%u]\n",
3195               (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular", lpMidiHdr,
3196               (DWORD)lpMidiHdr, lpMidiHdr->dwBufferLength, lpMidiHdr->dwBytesRecorded,
3197               lpMidiHdr->dwFlags, msg->wParam);
3198 #if 0
3199         /* dumps content of lpMidiHdr->lpData
3200          * FIXME: there should be a debug routine somewhere that already does this
3201          * I hate spreading this type of shit all around the code
3202          */
3203         for (dwToGo = 0; dwToGo < lpMidiHdr->dwBufferLength; dwToGo += 16) {
3204             DWORD       i;
3205             BYTE        ch;
3206
3207             for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++)
3208                 printf("%02x ", lpData[dwToGo + i]);
3209             for (; i < 16; i++)
3210                 printf("   ");
3211             for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++) {
3212                 ch = lpData[dwToGo + i];
3213                 printf("%c", (ch >= 0x20 && ch <= 0x7F) ? ch : '.');
3214             }
3215             printf("\n");
3216         }
3217 #endif
3218         if (((LPMIDIEVENT)lpData)->dwStreamID != 0 &&
3219             ((LPMIDIEVENT)lpData)->dwStreamID != 0xFFFFFFFF &&
3220             ((LPMIDIEVENT)lpData)->dwStreamID != (DWORD)lpMidiStrm) {
3221             FIXME("Dropping bad %s lpMidiHdr (streamID=%08lx)\n",
3222                   (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular",
3223                   ((LPMIDIEVENT)lpData)->dwStreamID);
3224             lpMidiHdr->dwFlags |= MHDR_DONE;
3225             lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
3226
3227             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags, lpMidiStrm->hDevice,
3228                            MM_MOM_DONE, lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
3229             break;
3230         }
3231
3232         for (lpmh = &lpMidiStrm->lpMidiHdr; *lpmh; lpmh = (LPMIDIHDR*)&((*lpmh)->lpNext));
3233         *lpmh = lpMidiHdr;
3234         lpMidiHdr = (LPMIDIHDR)msg->lParam;
3235         lpMidiHdr->lpNext = 0;
3236         lpMidiHdr->dwFlags |= MHDR_INQUEUE;
3237         lpMidiHdr->dwFlags &= MHDR_DONE;
3238         lpMidiHdr->dwOffset = 0;
3239
3240         break;
3241     default:
3242         FIXME("Unknown message %d\n", msg->message);
3243         break;
3244     }
3245     return TRUE;
3246 }
3247
3248 /**************************************************************************
3249  *                              MMSYSTEM_MidiStream_Player      [internal]
3250  */
3251 static  DWORD   CALLBACK        MMSYSTEM_MidiStream_Player(LPVOID pmt)
3252 {
3253     WINE_MIDIStream*    lpMidiStrm = pmt;
3254     WINE_MIDI*          lpwm;
3255     MSG                 msg;
3256     DWORD               dwToGo;
3257     DWORD               dwCurrTC;
3258     LPMIDIHDR           lpMidiHdr;
3259     LPMIDIEVENT         me;
3260     LPBYTE              lpData = 0;
3261
3262     TRACE("(%p)!\n", lpMidiStrm);
3263
3264     if (!lpMidiStrm ||
3265         (lpwm = (LPWINE_MIDI)MMDRV_Get(lpMidiStrm->hDevice, MMDRV_MIDIOUT, FALSE)) == NULL)
3266         goto the_end;
3267
3268     /* force thread's queue creation */
3269     /* Used to be InitThreadInput16(0, 5); */
3270     /* but following works also with hack in midiStreamOpen */
3271     PeekMessageA(&msg, 0, 0, 0, 0);
3272
3273     /* FIXME: this next line must be called before midiStreamOut or midiStreamRestart are called */
3274     SetEvent(lpMidiStrm->hEvent);
3275     TRACE("Ready to go 1\n");
3276     /* thread is started in paused mode */
3277     SuspendThread(lpMidiStrm->hThread);
3278     TRACE("Ready to go 2\n");
3279
3280     lpMidiStrm->dwStartTicks = 0;
3281     lpMidiStrm->dwPulses = 0;
3282
3283     lpMidiStrm->lpMidiHdr = 0;
3284
3285     for (;;) {
3286         lpMidiHdr = lpMidiStrm->lpMidiHdr;
3287         if (!lpMidiHdr) {
3288             /* for first message, block until one arrives, then process all that are available */
3289             GetMessageA(&msg, 0, 0, 0);
3290             do {
3291                 if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
3292                     goto the_end;
3293             } while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE));
3294             lpData = 0;
3295             continue;
3296         }
3297
3298         if (!lpData)
3299             lpData = lpMidiHdr->lpData;
3300
3301         me = (LPMIDIEVENT)(lpData + lpMidiHdr->dwOffset);
3302
3303         /* do we have to wait ? */
3304         if (me->dwDeltaTime) {
3305             lpMidiStrm->dwPositionMS += MMSYSTEM_MidiStream_Convert(lpMidiStrm, me->dwDeltaTime);
3306             lpMidiStrm->dwPulses += me->dwDeltaTime;
3307
3308             dwToGo = lpMidiStrm->dwStartTicks + lpMidiStrm->dwPositionMS;
3309
3310             TRACE("%ld/%ld/%ld\n", dwToGo, GetTickCount(), me->dwDeltaTime);
3311             while ((dwCurrTC = GetTickCount()) < dwToGo) {
3312                 if (MsgWaitForMultipleObjects(0, NULL, FALSE, dwToGo - dwCurrTC, QS_ALLINPUT) == WAIT_OBJECT_0) {
3313                     /* got a message, handle it */
3314                     while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
3315                         if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
3316                             goto the_end;
3317                     }
3318                     lpData = 0;
3319                 } else {
3320                     /* timeout, so me->dwDeltaTime is elapsed, can break the while loop */
3321                     break;
3322                 }
3323             }
3324         }
3325         switch (MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK)) {
3326         case MEVT_COMMENT:
3327             FIXME("NIY: MEVT_COMMENT\n");
3328             /* do nothing, skip bytes */
3329             break;
3330         case MEVT_LONGMSG:
3331             FIXME("NIY: MEVT_LONGMSG, aka sending Sysex event\n");
3332             break;
3333         case MEVT_NOP:
3334             break;
3335         case MEVT_SHORTMSG:
3336             midiOutShortMsg(lpMidiStrm->hDevice, MEVT_EVENTPARM(me->dwEvent));
3337             break;
3338         case MEVT_TEMPO:
3339             lpMidiStrm->dwTempo = MEVT_EVENTPARM(me->dwEvent);
3340             break;
3341         case MEVT_VERSION:
3342             break;
3343         default:
3344             FIXME("Unknown MEVT (0x%02x)\n", MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK));
3345             break;
3346         }
3347         if (me->dwEvent & MEVT_F_CALLBACK) {
3348             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags, lpMidiStrm->hDevice,
3349                            MM_MOM_POSITIONCB, lpwm->mod.dwInstance, (LPARAM)lpMidiHdr, 0L);
3350         }
3351         lpMidiHdr->dwOffset += sizeof(MIDIEVENT) - sizeof(me->dwParms);
3352         if (me->dwEvent & MEVT_F_LONG)
3353             lpMidiHdr->dwOffset += (MEVT_EVENTPARM(me->dwEvent) + 3) & ~3;
3354         if (lpMidiHdr->dwOffset >= lpMidiHdr->dwBufferLength) {
3355             /* done with this header */
3356             lpMidiHdr->dwFlags |= MHDR_DONE;
3357             lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
3358
3359             lpMidiStrm->lpMidiHdr = (LPMIDIHDR)lpMidiHdr->lpNext;
3360             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags, lpMidiStrm->hDevice,
3361                            MM_MOM_DONE, lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
3362             lpData = 0;
3363         }
3364     }
3365 the_end:
3366     TRACE("End of thread\n");
3367     ExitThread(0);
3368     return 0;   /* for removing the warning, never executed */
3369 }
3370
3371 /**************************************************************************
3372  *                              MMSYSTEM_MidiStream_PostMessage [internal]
3373  */
3374 static  BOOL MMSYSTEM_MidiStream_PostMessage(WINE_MIDIStream* lpMidiStrm, WORD msg, DWORD pmt1, DWORD pmt2)
3375 {
3376     if (PostThreadMessageA(lpMidiStrm->dwThreadID, msg, pmt1, pmt2)) {
3377         DWORD   count;
3378
3379         ReleaseThunkLock(&count);
3380         WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);
3381         RestoreThunkLock(count);
3382     } else {
3383         WARN("bad PostThreadMessageA\n");
3384         return FALSE;
3385     }
3386     return TRUE;
3387 }
3388
3389 /**************************************************************************
3390  *                              midiStreamClose                 [WINMM.@]
3391  */
3392 MMRESULT WINAPI midiStreamClose(HMIDISTRM hMidiStrm)
3393 {
3394     WINE_MIDIStream*    lpMidiStrm;
3395
3396     TRACE("(%08x)!\n", hMidiStrm);
3397
3398     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL))
3399         return MMSYSERR_INVALHANDLE;
3400
3401     midiStreamStop(hMidiStrm);
3402     MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WM_QUIT, 0, 0);
3403     HeapFree(GetProcessHeap(), 0, lpMidiStrm);
3404     CloseHandle(lpMidiStrm->hEvent);
3405
3406     return midiOutClose(hMidiStrm);
3407 }
3408
3409 /**************************************************************************
3410  *                              MMSYSTEM_MidiStream_Open        [internal]
3411  */
3412 static  MMRESULT WINAPI MMSYSTEM_MidiStream_Open(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID,
3413                                                  DWORD cMidi, DWORD dwCallback,
3414                                                  DWORD dwInstance, DWORD fdwOpen, BOOL bFrom32)
3415 {
3416     WINE_MIDIStream*    lpMidiStrm;
3417     MMRESULT            ret;
3418     MIDIOPENSTRMID      mosm;
3419     LPWINE_MIDI         lpwm;
3420     HMIDIOUT            hMidiOut;
3421
3422     TRACE("(%p, %p, %ld, 0x%08lx, 0x%08lx, 0x%08lx)!\n",
3423           lphMidiStrm, lpuDeviceID, cMidi, dwCallback, dwInstance, fdwOpen);
3424
3425     if (cMidi != 1 || lphMidiStrm == NULL || lpuDeviceID == NULL)
3426         return MMSYSERR_INVALPARAM;
3427
3428     lpMidiStrm = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_MIDIStream));
3429     if (!lpMidiStrm)
3430         return MMSYSERR_NOMEM;
3431
3432     lpMidiStrm->dwTempo = 500000;
3433     lpMidiStrm->dwTimeDiv = 480;        /* 480 is 120 quater notes per minute *//* FIXME ??*/
3434     lpMidiStrm->dwPositionMS = 0;
3435
3436     mosm.dwStreamID = (DWORD)lpMidiStrm;
3437     /* FIXME: the correct value is not allocated yet for MAPPER */
3438     mosm.wDeviceID  = *lpuDeviceID;
3439     lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &fdwOpen, 1, &mosm, bFrom32);
3440     lpMidiStrm->hDevice = hMidiOut;
3441     if (lphMidiStrm)
3442         *lphMidiStrm = hMidiOut;
3443
3444     /* FIXME: is lpuDevice initialized upon entering midiStreamOpen ? */
3445     FIXME("*lpuDeviceID=%x\n", *lpuDeviceID);
3446     lpwm->mld.uDeviceID = *lpuDeviceID = 0;
3447
3448     ret = MMDRV_Open(&lpwm->mld, MODM_OPEN, (DWORD)&lpwm->mod, fdwOpen);
3449     lpMidiStrm->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
3450     lpMidiStrm->wFlags = HIWORD(fdwOpen);
3451
3452     lpMidiStrm->hThread = CreateThread(NULL, 0, MMSYSTEM_MidiStream_Player,
3453                                        lpMidiStrm, 0, &(lpMidiStrm->dwThreadID));
3454
3455     if (!lpMidiStrm->hThread) {
3456         midiStreamClose((HMIDISTRM)hMidiOut);
3457         return MMSYSERR_NOMEM;
3458     }
3459
3460     /* wait for thread to have started, and for its queue to be created */
3461     {
3462         DWORD   count;
3463
3464         /* (Release|Restore)ThunkLock() is needed when this method is called from 16 bit code,
3465          * (meaning the Win16Lock is set), so that it's released and the 32 bit thread running
3466          * MMSYSTEM_MidiStreamPlayer can acquire Win16Lock to create its queue.
3467          */
3468         ReleaseThunkLock(&count);
3469         WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);
3470         RestoreThunkLock(count);
3471     }
3472
3473     TRACE("=> (%u/%d) hMidi=0x%04x ret=%d lpMidiStrm=%p\n",
3474           *lpuDeviceID, lpwm->mld.uDeviceID, *lphMidiStrm, ret, lpMidiStrm);
3475     return ret;
3476 }
3477
3478 /**************************************************************************
3479  *                              midiStreamOpen                  [WINMM.@]
3480  */
3481 MMRESULT WINAPI midiStreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID,
3482                                DWORD cMidi, DWORD dwCallback,
3483                                DWORD dwInstance, DWORD fdwOpen)
3484 {
3485     return MMSYSTEM_MidiStream_Open(lphMidiStrm, lpuDeviceID, cMidi, dwCallback,
3486                                     dwInstance, fdwOpen, TRUE);
3487 }
3488
3489 /**************************************************************************
3490  *                              midiStreamOut                   [WINMM.@]
3491  */
3492 MMRESULT WINAPI midiStreamOut(HMIDISTRM hMidiStrm, LPMIDIHDR lpMidiHdr,
3493                               UINT cbMidiHdr)
3494 {
3495     WINE_MIDIStream*    lpMidiStrm;
3496     DWORD               ret = MMSYSERR_NOERROR;
3497
3498     TRACE("(%08x, %p, %u)!\n", hMidiStrm, lpMidiHdr, cbMidiHdr);
3499
3500     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
3501         ret = MMSYSERR_INVALHANDLE;
3502     } else if (!lpMidiHdr) {
3503         ret = MMSYSERR_INVALPARAM;
3504     } else {
3505         if (!PostThreadMessageA(lpMidiStrm->dwThreadID,
3506                                 WINE_MSM_HEADER, cbMidiHdr,
3507                                 (DWORD)lpMidiHdr)) {
3508             WARN("bad PostThreadMessageA\n");
3509             ret = MMSYSERR_ERROR;
3510         }
3511     }
3512     return ret;
3513 }
3514
3515 /**************************************************************************
3516  *                              midiStreamPause                 [WINMM.@]
3517  */
3518 MMRESULT WINAPI midiStreamPause(HMIDISTRM hMidiStrm)
3519 {
3520     WINE_MIDIStream*    lpMidiStrm;
3521     DWORD               ret = MMSYSERR_NOERROR;
3522
3523     TRACE("(%08x)!\n", hMidiStrm);
3524
3525     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
3526         ret = MMSYSERR_INVALHANDLE;
3527     } else {
3528         if (SuspendThread(lpMidiStrm->hThread) == 0xFFFFFFFF) {
3529             WARN("bad Suspend (%ld)\n", GetLastError());
3530             ret = MMSYSERR_ERROR;
3531         }
3532     }
3533     return ret;
3534 }
3535
3536 /**************************************************************************
3537  *                              midiStreamPosition              [WINMM.@]
3538  */
3539 MMRESULT WINAPI midiStreamPosition(HMIDISTRM hMidiStrm, LPMMTIME lpMMT, UINT cbmmt)
3540 {
3541     WINE_MIDIStream*    lpMidiStrm;
3542     DWORD               ret = MMSYSERR_NOERROR;
3543
3544     TRACE("(%08x, %p, %u)!\n", hMidiStrm, lpMMT, cbmmt);
3545
3546     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
3547         ret = MMSYSERR_INVALHANDLE;
3548     } else if (lpMMT == NULL || cbmmt != sizeof(MMTIME)) {
3549         ret = MMSYSERR_INVALPARAM;
3550     } else {
3551         switch (lpMMT->wType) {
3552         case TIME_MS:
3553             lpMMT->u.ms = lpMidiStrm->dwPositionMS;
3554             TRACE("=> %ld ms\n", lpMMT->u.ms);
3555             break;
3556         case TIME_TICKS:
3557             lpMMT->u.ticks = lpMidiStrm->dwPulses;
3558             TRACE("=> %ld ticks\n", lpMMT->u.ticks);
3559             break;
3560         default:
3561             WARN("Unsupported time type %d\n", lpMMT->wType);
3562             lpMMT->wType = TIME_MS;
3563             ret = MMSYSERR_INVALPARAM;
3564             break;
3565         }
3566     }
3567     return ret;
3568 }
3569
3570 /**************************************************************************
3571  *                              midiStreamProperty              [WINMM.@]
3572  */
3573 MMRESULT WINAPI midiStreamProperty(HMIDISTRM hMidiStrm, LPBYTE lpPropData, DWORD dwProperty)
3574 {
3575     WINE_MIDIStream*    lpMidiStrm;
3576     MMRESULT            ret = MMSYSERR_NOERROR;
3577
3578     TRACE("(%08x, %p, %lx)\n", hMidiStrm, lpPropData, dwProperty);
3579
3580     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
3581         ret = MMSYSERR_INVALHANDLE;
3582     } else if ((dwProperty & (MIDIPROP_GET|MIDIPROP_SET)) == 0) {
3583         ret = MMSYSERR_INVALPARAM;
3584     } else if (dwProperty & MIDIPROP_TEMPO) {
3585         MIDIPROPTEMPO*  mpt = (MIDIPROPTEMPO*)lpPropData;
3586
3587         if (sizeof(MIDIPROPTEMPO) != mpt->cbStruct) {
3588             ret = MMSYSERR_INVALPARAM;
3589         } else if (dwProperty & MIDIPROP_SET) {
3590             lpMidiStrm->dwTempo = mpt->dwTempo;
3591             TRACE("Setting tempo to %ld\n", mpt->dwTempo);
3592         } else if (dwProperty & MIDIPROP_GET) {
3593             mpt->dwTempo = lpMidiStrm->dwTempo;
3594             TRACE("Getting tempo <= %ld\n", mpt->dwTempo);
3595         }
3596     } else if (dwProperty & MIDIPROP_TIMEDIV) {
3597         MIDIPROPTIMEDIV*        mptd = (MIDIPROPTIMEDIV*)lpPropData;
3598
3599         if (sizeof(MIDIPROPTIMEDIV) != mptd->cbStruct) {
3600             ret = MMSYSERR_INVALPARAM;
3601         } else if (dwProperty & MIDIPROP_SET) {
3602             lpMidiStrm->dwTimeDiv = mptd->dwTimeDiv;
3603             TRACE("Setting time div to %ld\n", mptd->dwTimeDiv);
3604         } else if (dwProperty & MIDIPROP_GET) {
3605             mptd->dwTimeDiv = lpMidiStrm->dwTimeDiv;
3606             TRACE("Getting time div <= %ld\n", mptd->dwTimeDiv);
3607         }
3608     } else {
3609         ret = MMSYSERR_INVALPARAM;
3610     }
3611
3612     return ret;
3613 }
3614
3615 /**************************************************************************
3616  *                              midiStreamRestart               [WINMM.@]
3617  */
3618 MMRESULT WINAPI midiStreamRestart(HMIDISTRM hMidiStrm)
3619 {
3620     WINE_MIDIStream*    lpMidiStrm;
3621     MMRESULT            ret = MMSYSERR_NOERROR;
3622
3623     TRACE("(%08x)!\n", hMidiStrm);
3624
3625     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
3626         ret = MMSYSERR_INVALHANDLE;
3627     } else {
3628         DWORD   ret;
3629
3630         /* since we increase the thread suspend count on each midiStreamPause
3631          * there may be a need for several midiStreamResume
3632          */
3633         do {
3634             ret = ResumeThread(lpMidiStrm->hThread);
3635         } while (ret != 0xFFFFFFFF && ret != 0);
3636         if (ret == 0xFFFFFFFF) {
3637             WARN("bad Resume (%ld)\n", GetLastError());
3638             ret = MMSYSERR_ERROR;
3639         } else {
3640             lpMidiStrm->dwStartTicks = GetTickCount() - lpMidiStrm->dwPositionMS;
3641         }
3642     }
3643     return ret;
3644 }
3645
3646 /**************************************************************************
3647  *                              midiStreamStop                  [WINMM.@]
3648  */
3649 MMRESULT WINAPI midiStreamStop(HMIDISTRM hMidiStrm)
3650 {
3651     WINE_MIDIStream*    lpMidiStrm;
3652     MMRESULT            ret = MMSYSERR_NOERROR;
3653
3654     TRACE("(%08x)!\n", hMidiStrm);
3655
3656     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
3657         ret = MMSYSERR_INVALHANDLE;
3658     } else {
3659         /* in case stream has been paused... FIXME is the current state correct ? */
3660         midiStreamRestart(hMidiStrm);
3661         MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WINE_MSM_STOP, 0, 0);
3662     }
3663     return ret;
3664 }
3665
3666 /**************************************************************************
3667  *                              midiStreamClose                 [MMSYSTEM.252]
3668  */
3669 MMRESULT16 WINAPI midiStreamClose16(HMIDISTRM16 hMidiStrm)
3670 {
3671     return midiStreamClose(hMidiStrm);
3672 }
3673
3674 /**************************************************************************
3675  *                              midiStreamOpen                  [MMSYSTEM.251]
3676  */
3677 MMRESULT16 WINAPI midiStreamOpen16(HMIDISTRM16* phMidiStrm, LPUINT16 devid,
3678                                    DWORD cMidi, DWORD dwCallback,
3679                                    DWORD dwInstance, DWORD fdwOpen)
3680 {
3681     HMIDISTRM   hMidiStrm32;
3682     MMRESULT    ret;
3683     UINT        devid32;
3684
3685     if (!phMidiStrm || !devid)
3686         return MMSYSERR_INVALPARAM;
3687     devid32 = *devid;
3688     ret = MMSYSTEM_MidiStream_Open(&hMidiStrm32, &devid32, cMidi, dwCallback,
3689                                    dwInstance, fdwOpen, FALSE);
3690     *phMidiStrm = hMidiStrm32;
3691     *devid = devid32;
3692     return ret;
3693 }
3694
3695 /**************************************************************************
3696  *                              midiStreamOut                   [MMSYSTEM.254]
3697  */
3698 MMRESULT16 WINAPI midiStreamOut16(HMIDISTRM16 hMidiStrm, LPMIDIHDR16 lpMidiHdr, UINT16 cbMidiHdr)
3699 {
3700     return midiStreamOut(hMidiStrm, (LPMIDIHDR)lpMidiHdr, cbMidiHdr);
3701 }
3702
3703 /**************************************************************************
3704  *                              midiStreamPause                 [MMSYSTEM.255]
3705  */
3706 MMRESULT16 WINAPI midiStreamPause16(HMIDISTRM16 hMidiStrm)
3707 {
3708     return midiStreamPause(hMidiStrm);
3709 }
3710
3711 /**************************************************************************
3712  *                              midiStreamPosition              [MMSYSTEM.253]
3713  */
3714 MMRESULT16 WINAPI midiStreamPosition16(HMIDISTRM16 hMidiStrm, LPMMTIME16 lpmmt16, UINT16 cbmmt)
3715 {
3716     MMTIME      mmt32;
3717     MMRESULT    ret;
3718
3719     if (!lpmmt16)
3720         return MMSYSERR_INVALPARAM;
3721     MMSYSTEM_MMTIME16to32(&mmt32, lpmmt16);
3722     ret = midiStreamPosition(hMidiStrm, &mmt32, sizeof(MMTIME));
3723     MMSYSTEM_MMTIME32to16(lpmmt16, &mmt32);
3724     return ret;
3725 }
3726
3727 /**************************************************************************
3728  *                              midiStreamProperty              [MMSYSTEM.250]
3729  */
3730 MMRESULT16 WINAPI midiStreamProperty16(HMIDISTRM16 hMidiStrm, LPBYTE lpPropData, DWORD dwProperty)
3731 {
3732     return midiStreamProperty(hMidiStrm, lpPropData, dwProperty);
3733 }
3734
3735 /**************************************************************************
3736  *                              midiStreamRestart               [MMSYSTEM.256]
3737  */
3738 MMRESULT16 WINAPI midiStreamRestart16(HMIDISTRM16 hMidiStrm)
3739 {
3740     return midiStreamRestart(hMidiStrm);
3741 }
3742
3743 /**************************************************************************
3744  *                              midiStreamStop                  [MMSYSTEM.257]
3745  */
3746 MMRESULT16 WINAPI midiStreamStop16(HMIDISTRM16 hMidiStrm)
3747 {
3748     return midiStreamStop(hMidiStrm);
3749 }
3750
3751 static  UINT WINAPI MMSYSTEM_waveOpen(HANDLE* lphndl, UINT uDeviceID, UINT uType,
3752                                       const LPWAVEFORMATEX lpFormat,
3753                                       DWORD dwCallback, DWORD dwInstance,
3754                                       DWORD dwFlags, BOOL bFrom32)
3755 {
3756     HANDLE              handle;
3757     LPWINE_MLD          wmld;
3758     DWORD               dwRet = MMSYSERR_NOERROR;
3759     WAVEOPENDESC        wod;
3760
3761     TRACE("(%p, %d, %s, %p, %08lX, %08lX, %08lX, %d);\n",
3762           lphndl, (int)uDeviceID, (uType==MMDRV_WAVEOUT)?"Out":"In", lpFormat, dwCallback,
3763           dwInstance, dwFlags, bFrom32?32:16);
3764
3765     if (dwFlags & WAVE_FORMAT_QUERY)    TRACE("WAVE_FORMAT_QUERY requested !\n");
3766
3767     if (lpFormat == NULL) return WAVERR_BADFORMAT;
3768     if ((dwFlags & WAVE_MAPPED) && (uDeviceID == (UINT)-1))
3769         return MMSYSERR_INVALPARAM;
3770
3771     TRACE("wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u\n",
3772           lpFormat->wFormatTag, lpFormat->nChannels, lpFormat->nSamplesPerSec,
3773           lpFormat->nAvgBytesPerSec, lpFormat->nBlockAlign, lpFormat->wBitsPerSample, lpFormat->cbSize);
3774
3775     if ((wmld = MMDRV_Alloc(sizeof(WINE_WAVE), uType, &handle,
3776                             &dwFlags, &dwCallback, &dwInstance, bFrom32)) == NULL)
3777         return MMSYSERR_NOMEM;
3778
3779     wod.hWave = handle;
3780     wod.lpFormat = lpFormat;  /* should the struct be copied iso pointer? */
3781     wod.dwCallback = dwCallback;
3782     wod.dwInstance = dwInstance;
3783     wod.dnDevNode = 0L;
3784
3785     if (dwFlags & WAVE_MAPPED) {
3786         wod.uMappedDeviceID = uDeviceID;
3787         uDeviceID = WAVE_MAPPER;
3788     } else {
3789         wod.uMappedDeviceID = -1;
3790     }
3791     wmld->uDeviceID = uDeviceID;
3792
3793     dwRet = MMDRV_Open(wmld, (uType == MMDRV_WAVEOUT) ? WODM_OPEN : WIDM_OPEN, (DWORD)&wod, dwFlags);
3794
3795     if ((dwFlags & WAVE_FORMAT_QUERY) || dwRet != MMSYSERR_NOERROR) {
3796         MMDRV_Free(handle, wmld);
3797         handle = 0;
3798     }
3799
3800     if (lphndl != NULL) *lphndl = handle;
3801     TRACE("=> %ld hWave=%04x\n", dwRet, handle);
3802
3803     return dwRet;
3804 }
3805
3806 /**************************************************************************
3807  *                              waveOutGetNumDevs               [WINMM.@]
3808  */
3809 UINT WINAPI waveOutGetNumDevs(void)
3810 {
3811     return MMDRV_GetNum(MMDRV_WAVEOUT);
3812 }
3813
3814 /**************************************************************************
3815  *                              waveOutGetNumDevs               [MMSYSTEM.401]
3816  */
3817 UINT16 WINAPI waveOutGetNumDevs16(void)
3818 {
3819     return MMDRV_GetNum(MMDRV_WAVEOUT);
3820 }
3821
3822 /**************************************************************************
3823  *                              waveOutGetDevCaps               [MMSYSTEM.402]
3824  */
3825 UINT16 WINAPI waveOutGetDevCaps16(UINT16 uDeviceID,
3826                                   LPWAVEOUTCAPS16 lpCaps, UINT16 uSize)
3827 {
3828     WAVEOUTCAPSA        wocA;
3829     UINT                ret;
3830
3831     TRACE("(%u %p %u)!\n", uDeviceID, lpCaps, uSize);
3832     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
3833
3834     ret = waveOutGetDevCapsA(uDeviceID, &wocA, sizeof(wocA));
3835
3836     if (ret == MMSYSERR_NOERROR) {
3837         lpCaps->wMid = wocA.wMid;
3838         lpCaps->wPid = wocA.wPid;
3839         lpCaps->vDriverVersion = wocA.vDriverVersion;
3840         strcpy(lpCaps->szPname, wocA.szPname);
3841         lpCaps->dwFormats = wocA.dwFormats;
3842         lpCaps->wChannels = wocA.wChannels;
3843         lpCaps->dwSupport = wocA.dwSupport;
3844     }
3845     return ret;
3846 }
3847
3848 /**************************************************************************
3849  *                              waveOutGetDevCapsA              [WINMM.@]
3850  */
3851 UINT WINAPI waveOutGetDevCapsA(UINT uDeviceID, LPWAVEOUTCAPSA lpCaps,
3852                                UINT uSize)
3853 {
3854     LPWINE_MLD          wmld;
3855
3856     TRACE("(%u %p %u)!\n", uDeviceID, lpCaps, uSize);
3857
3858     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
3859
3860     if ((wmld = MMDRV_Get(uDeviceID, MMDRV_WAVEOUT, TRUE)) == NULL)
3861         return MMSYSERR_INVALHANDLE;
3862
3863     return MMDRV_Message(wmld, WODM_GETDEVCAPS, (DWORD)lpCaps, uSize, TRUE);
3864
3865 }
3866
3867 /**************************************************************************
3868  *                              waveOutGetDevCapsW              [WINMM.@]
3869  */
3870 UINT WINAPI waveOutGetDevCapsW(UINT uDeviceID, LPWAVEOUTCAPSW lpCaps,
3871                                UINT uSize)
3872 {
3873     WAVEOUTCAPSA        wocA;
3874     UINT                ret;
3875
3876     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
3877
3878     ret = waveOutGetDevCapsA(uDeviceID, &wocA, sizeof(wocA));
3879
3880     if (ret == MMSYSERR_NOERROR) {
3881         lpCaps->wMid = wocA.wMid;
3882         lpCaps->wPid = wocA.wPid;
3883         lpCaps->vDriverVersion = wocA.vDriverVersion;
3884         MultiByteToWideChar( CP_ACP, 0, wocA.szPname, -1, lpCaps->szPname,
3885                              sizeof(lpCaps->szPname)/sizeof(WCHAR) );
3886         lpCaps->dwFormats = wocA.dwFormats;
3887         lpCaps->wChannels = wocA.wChannels;
3888         lpCaps->dwSupport = wocA.dwSupport;
3889     }
3890     return ret;
3891 }
3892
3893 /**************************************************************************
3894  *                              WAVE_GetErrorText               [internal]
3895  */
3896 static  UINT16  WAVE_GetErrorText(UINT16 uError, LPSTR lpText, UINT16 uSize)
3897 {
3898     UINT16              ret = MMSYSERR_BADERRNUM;
3899
3900     if (lpText == NULL) {
3901         ret = MMSYSERR_INVALPARAM;
3902     } else if (uSize == 0) {
3903         ret = MMSYSERR_NOERROR;
3904     } else if (
3905                /* test has been removed 'coz MMSYSERR_BASE is 0, and gcc did emit
3906                 * a warning for the test was always true */
3907                (/*uError >= MMSYSERR_BASE && */uError <= MMSYSERR_LASTERROR) ||
3908                (uError >= WAVERR_BASE  && uError <= WAVERR_LASTERROR)) {
3909
3910         if (LoadStringA(MULTIMEDIA_GetIData()->hWinMM32Instance,
3911                         uError, lpText, uSize) > 0) {
3912             ret = MMSYSERR_NOERROR;
3913         }
3914     }
3915     return ret;
3916 }
3917
3918 /**************************************************************************
3919  *                              waveOutGetErrorText     [MMSYSTEM.403]
3920  */
3921 UINT16 WINAPI waveOutGetErrorText16(UINT16 uError, LPSTR lpText, UINT16 uSize)
3922 {
3923     return WAVE_GetErrorText(uError, lpText, uSize);
3924 }
3925
3926 /**************************************************************************
3927  *                              waveOutGetErrorTextA    [WINMM.@]
3928  */
3929 UINT WINAPI waveOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
3930 {
3931     return WAVE_GetErrorText(uError, lpText, uSize);
3932 }
3933
3934 /**************************************************************************
3935  *                              waveOutGetErrorTextW    [WINMM.@]
3936  */
3937 UINT WINAPI waveOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
3938 {
3939     LPSTR       xstr = HeapAlloc(GetProcessHeap(), 0, uSize);
3940     UINT        ret = WAVE_GetErrorText(uError, xstr, uSize);
3941
3942     MultiByteToWideChar( CP_ACP, 0, xstr, -1, lpText, uSize );
3943     HeapFree(GetProcessHeap(), 0, xstr);
3944     return ret;
3945 }
3946
3947 /**************************************************************************
3948  *                      waveOutOpen                     [WINMM.@]
3949  * All the args/structs have the same layout as the win16 equivalents
3950  */
3951 UINT WINAPI waveOutOpen(HWAVEOUT* lphWaveOut, UINT uDeviceID,
3952                         const LPWAVEFORMATEX lpFormat, DWORD dwCallback,
3953                         DWORD dwInstance, DWORD dwFlags)
3954 {
3955     return MMSYSTEM_waveOpen(lphWaveOut, uDeviceID, MMDRV_WAVEOUT, lpFormat,
3956                              dwCallback, dwInstance, dwFlags, TRUE);
3957 }
3958
3959 /**************************************************************************
3960  *                      waveOutOpen                     [MMSYSTEM.404]
3961  */
3962 UINT16 WINAPI waveOutOpen16(HWAVEOUT16* lphWaveOut, UINT16 uDeviceID,
3963                             const LPWAVEFORMATEX lpFormat, DWORD dwCallback,
3964                             DWORD dwInstance, DWORD dwFlags)
3965 {
3966     HWAVEOUT            hWaveOut;
3967     UINT                ret;
3968
3969     /* since layout of WAVEFORMATEX is the same for 16/32 bits, we directly
3970      * call the 32 bit version
3971      * however, we need to promote correctly the wave mapper id
3972      * (0xFFFFFFFF and not 0x0000FFFF)
3973      */
3974     ret = MMSYSTEM_waveOpen(&hWaveOut, (uDeviceID == (UINT16)-1) ? (UINT)-1 : uDeviceID,
3975                             MMDRV_WAVEOUT, lpFormat, dwCallback, dwInstance, dwFlags, FALSE);
3976
3977     if (lphWaveOut != NULL) *lphWaveOut = hWaveOut;
3978     return ret;
3979 }
3980
3981 /**************************************************************************
3982  *                              waveOutClose            [WINMM.@]
3983  */
3984 UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
3985 {
3986     LPWINE_MLD          wmld;
3987     DWORD               dwRet;
3988
3989     TRACE("(%04X)\n", hWaveOut);
3990
3991     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
3992         return MMSYSERR_INVALHANDLE;
3993
3994     dwRet = MMDRV_Close(wmld, WODM_CLOSE);
3995     MMDRV_Free(hWaveOut, wmld);
3996
3997     return dwRet;
3998 }
3999
4000 /**************************************************************************
4001  *                              waveOutClose            [MMSYSTEM.405]
4002  */
4003 UINT16 WINAPI waveOutClose16(HWAVEOUT16 hWaveOut)
4004 {
4005     DWORD       level;
4006     UINT16      ret;
4007
4008     ReleaseThunkLock(&level);
4009     ret = waveOutClose(hWaveOut);
4010     RestoreThunkLock(level);
4011     return ret;
4012 }
4013
4014 /**************************************************************************
4015  *                              waveOutPrepareHeader    [WINMM.@]
4016  */
4017 UINT WINAPI waveOutPrepareHeader(HWAVEOUT hWaveOut,
4018                                  WAVEHDR* lpWaveOutHdr, UINT uSize)
4019 {
4020     LPWINE_MLD          wmld;
4021
4022     TRACE("(%04X, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
4023
4024     if (lpWaveOutHdr == NULL) return MMSYSERR_INVALPARAM;
4025
4026     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4027         return MMSYSERR_INVALHANDLE;
4028
4029     return MMDRV_Message(wmld, WODM_PREPARE, (DWORD)lpWaveOutHdr, uSize, TRUE);
4030 }
4031
4032 /**************************************************************************
4033  *                              waveOutPrepareHeader    [MMSYSTEM.406]
4034  */
4035 UINT16 WINAPI waveOutPrepareHeader16(HWAVEOUT16 hWaveOut,      /* [in] */
4036                                      SEGPTR lpsegWaveOutHdr,   /* [???] */
4037                                      UINT16 uSize)             /* [in] */
4038 {
4039     LPWINE_MLD          wmld;
4040     LPWAVEHDR           lpWaveOutHdr = MapSL(lpsegWaveOutHdr);
4041
4042     TRACE("(%04X, %08lx, %u);\n", hWaveOut, lpsegWaveOutHdr, uSize);
4043
4044     if (lpWaveOutHdr == NULL) return MMSYSERR_INVALPARAM;
4045
4046     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4047         return MMSYSERR_INVALHANDLE;
4048
4049     return MMDRV_Message(wmld, WODM_PREPARE, (DWORD)lpsegWaveOutHdr, uSize, FALSE);
4050 }
4051
4052 /**************************************************************************
4053  *                              waveOutUnprepareHeader  [WINMM.@]
4054  */
4055 UINT WINAPI waveOutUnprepareHeader(HWAVEOUT hWaveOut,
4056                                    LPWAVEHDR lpWaveOutHdr, UINT uSize)
4057 {
4058     LPWINE_MLD          wmld;
4059
4060     TRACE("(%04X, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
4061
4062     if (!(lpWaveOutHdr->dwFlags & WHDR_PREPARED)) {
4063         return MMSYSERR_NOERROR;
4064     }
4065
4066     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4067         return MMSYSERR_INVALHANDLE;
4068
4069     return MMDRV_Message(wmld, WODM_UNPREPARE, (DWORD)lpWaveOutHdr, uSize, TRUE);
4070 }
4071
4072 /**************************************************************************
4073  *                              waveOutUnprepareHeader  [MMSYSTEM.407]
4074  */
4075 UINT16 WINAPI waveOutUnprepareHeader16(HWAVEOUT16 hWaveOut,       /* [in] */
4076                                        SEGPTR lpsegWaveOutHdr,    /* [???] */
4077                                        UINT16 uSize)              /* [in] */
4078 {
4079     LPWINE_MLD          wmld;
4080     LPWAVEHDR           lpWaveOutHdr = MapSL(lpsegWaveOutHdr);
4081
4082     TRACE("(%04X, %08lx, %u);\n", hWaveOut, lpsegWaveOutHdr, uSize);
4083
4084     if (!(lpWaveOutHdr->dwFlags & WHDR_PREPARED)) {
4085         return MMSYSERR_NOERROR;
4086     }
4087
4088     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4089         return MMSYSERR_INVALHANDLE;
4090
4091     return MMDRV_Message(wmld, WODM_UNPREPARE, (DWORD)lpsegWaveOutHdr, uSize, FALSE);
4092 }
4093
4094 /**************************************************************************
4095  *                              waveOutWrite            [WINMM.@]
4096  */
4097 UINT WINAPI waveOutWrite(HWAVEOUT hWaveOut, LPWAVEHDR lpWaveOutHdr,
4098                          UINT uSize)
4099 {
4100     LPWINE_MLD          wmld;
4101
4102     TRACE("(%04X, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
4103
4104     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4105         return MMSYSERR_INVALHANDLE;
4106
4107     return MMDRV_Message(wmld, WODM_WRITE, (DWORD)lpWaveOutHdr, uSize, TRUE);
4108 }
4109
4110 /**************************************************************************
4111  *                              waveOutWrite            [MMSYSTEM.408]
4112  */
4113 UINT16 WINAPI waveOutWrite16(HWAVEOUT16 hWaveOut,       /* [in] */
4114                              LPWAVEHDR lpsegWaveOutHdr, /* [???] NOTE: SEGPTR */
4115                              UINT16 uSize)              /* [in] */
4116 {
4117     LPWINE_MLD          wmld;
4118
4119     TRACE("(%04X, %p, %u);\n", hWaveOut, lpsegWaveOutHdr, uSize);
4120
4121     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4122         return MMSYSERR_INVALHANDLE;
4123
4124     return MMDRV_Message(wmld, WODM_WRITE, (DWORD)lpsegWaveOutHdr, uSize, FALSE);
4125 }
4126
4127 /**************************************************************************
4128  *                              waveOutBreakLoop        [WINMM.@]
4129  */
4130 UINT WINAPI waveOutBreakLoop(HWAVEOUT hWaveOut)
4131 {
4132     LPWINE_MLD          wmld;
4133
4134     TRACE("(%04X);\n", hWaveOut);
4135
4136     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4137         return MMSYSERR_INVALHANDLE;
4138     return MMDRV_Message(wmld, WODM_BREAKLOOP, 0L, 0L, TRUE);
4139 }
4140
4141 /**************************************************************************
4142  *                              waveOutBreakLoop        [MMSYSTEM.419]
4143  */
4144 UINT16 WINAPI waveOutBreakLoop16(HWAVEOUT16 hWaveOut16)
4145 {
4146     DWORD       level;
4147     UINT16      ret;
4148
4149     ReleaseThunkLock(&level);
4150     ret = waveOutBreakLoop(hWaveOut16);
4151     RestoreThunkLock(level);
4152     return ret;
4153 }
4154
4155 /**************************************************************************
4156  *                              waveOutPause            [WINMM.@]
4157  */
4158 UINT WINAPI waveOutPause(HWAVEOUT hWaveOut)
4159 {
4160     LPWINE_MLD          wmld;
4161
4162     TRACE("(%04X);\n", hWaveOut);
4163
4164     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4165         return MMSYSERR_INVALHANDLE;
4166     return MMDRV_Message(wmld, WODM_PAUSE, 0L, 0L, TRUE);
4167 }
4168
4169 /**************************************************************************
4170  *                              waveOutPause            [MMSYSTEM.409]
4171  */
4172 UINT16 WINAPI waveOutPause16(HWAVEOUT16 hWaveOut16)
4173 {
4174     DWORD       level;
4175     UINT16      ret;
4176
4177     ReleaseThunkLock(&level);
4178     ret = waveOutPause(hWaveOut16);
4179     RestoreThunkLock(level);
4180     return ret;
4181 }
4182
4183 /**************************************************************************
4184  *                              waveOutReset            [WINMM.@]
4185  */
4186 UINT WINAPI waveOutReset(HWAVEOUT hWaveOut)
4187 {
4188     LPWINE_MLD          wmld;
4189
4190     TRACE("(%04X);\n", hWaveOut);
4191
4192     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4193         return MMSYSERR_INVALHANDLE;
4194     return MMDRV_Message(wmld, WODM_RESET, 0L, 0L, TRUE);
4195 }
4196
4197 /**************************************************************************
4198  *                              waveOutReset            [MMSYSTEM.411]
4199  */
4200 UINT16 WINAPI waveOutReset16(HWAVEOUT16 hWaveOut16)
4201 {
4202     DWORD       level;
4203     UINT16      ret;
4204
4205     ReleaseThunkLock(&level);
4206     ret = waveOutReset(hWaveOut16);
4207     RestoreThunkLock(level);
4208     return ret;
4209 }
4210
4211 /**************************************************************************
4212  *                              waveOutRestart          [WINMM.@]
4213  */
4214 UINT WINAPI waveOutRestart(HWAVEOUT hWaveOut)
4215 {
4216     LPWINE_MLD          wmld;
4217
4218     TRACE("(%04X);\n", hWaveOut);
4219
4220     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4221         return MMSYSERR_INVALHANDLE;
4222     return MMDRV_Message(wmld, WODM_RESTART, 0L, 0L, TRUE);
4223 }
4224
4225 /**************************************************************************
4226  *                              waveOutRestart  [MMSYSTEM.410]
4227  */
4228 UINT16 WINAPI waveOutRestart16(HWAVEOUT16 hWaveOut16)
4229 {
4230     DWORD       level;
4231     UINT16      ret;
4232
4233     ReleaseThunkLock(&level);
4234     ret = waveOutRestart(hWaveOut16);
4235     RestoreThunkLock(level);
4236     return ret;
4237 }
4238
4239 /**************************************************************************
4240  *                              waveOutGetPosition      [WINMM.@]
4241  */
4242 UINT WINAPI waveOutGetPosition(HWAVEOUT hWaveOut, LPMMTIME lpTime,
4243                                UINT uSize)
4244 {
4245     LPWINE_MLD          wmld;
4246
4247     TRACE("(%04X, %p, %u);\n", hWaveOut, lpTime, uSize);
4248
4249     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4250         return MMSYSERR_INVALHANDLE;
4251
4252     return MMDRV_Message(wmld, WODM_GETPOS, (DWORD)lpTime, uSize, TRUE);
4253 }
4254
4255 /**************************************************************************
4256  *                              waveOutGetPosition      [MMSYSTEM.412]
4257  */
4258 UINT16 WINAPI waveOutGetPosition16(HWAVEOUT16 hWaveOut, LPMMTIME16 lpTime,
4259                                    UINT16 uSize)
4260 {
4261     UINT        ret;
4262     MMTIME      mmt;
4263
4264     mmt.wType = lpTime->wType;
4265     ret = waveOutGetPosition(hWaveOut, &mmt, sizeof(mmt));
4266     MMSYSTEM_MMTIME32to16(lpTime, &mmt);
4267     return ret;
4268 }
4269
4270 /**************************************************************************
4271  *                              waveOutGetPitch         [WINMM.@]
4272  */
4273 UINT WINAPI waveOutGetPitch(HWAVEOUT hWaveOut, LPDWORD lpdw)
4274 {
4275     LPWINE_MLD          wmld;
4276
4277     TRACE("(%04X, %08lx);\n", hWaveOut, (DWORD)lpdw);
4278
4279     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4280         return MMSYSERR_INVALHANDLE;
4281     return MMDRV_Message(wmld, WODM_GETPITCH, (DWORD)lpdw, 0L, TRUE);
4282 }
4283
4284 /**************************************************************************
4285  *                              waveOutGetPitch         [MMSYSTEM.413]
4286  */
4287 UINT16 WINAPI waveOutGetPitch16(HWAVEOUT16 hWaveOut16, LPDWORD lpdw)
4288 {
4289     return waveOutGetPitch(hWaveOut16, lpdw);
4290 }
4291
4292 /**************************************************************************
4293  *                              waveOutSetPitch         [WINMM.@]
4294  */
4295 UINT WINAPI waveOutSetPitch(HWAVEOUT hWaveOut, DWORD dw)
4296 {
4297     LPWINE_MLD          wmld;
4298
4299     TRACE("(%04X, %08lx);\n", hWaveOut, (DWORD)dw);
4300
4301     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4302         return MMSYSERR_INVALHANDLE;
4303     return MMDRV_Message(wmld, WODM_SETPITCH, dw, 0L, TRUE);
4304 }
4305
4306 /**************************************************************************
4307  *                              waveOutSetPitch         [MMSYSTEM.414]
4308  */
4309 UINT16 WINAPI waveOutSetPitch16(HWAVEOUT16 hWaveOut16, DWORD dw)
4310 {
4311     return waveOutSetPitch(hWaveOut16, dw);
4312 }
4313
4314 /**************************************************************************
4315  *                              waveOutGetPlaybackRate  [WINMM.@]
4316  */
4317 UINT WINAPI waveOutGetPlaybackRate(HWAVEOUT hWaveOut, LPDWORD lpdw)
4318 {
4319     LPWINE_MLD          wmld;
4320
4321     TRACE("(%04X, %08lx);\n", hWaveOut, (DWORD)lpdw);
4322
4323     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4324         return MMSYSERR_INVALHANDLE;
4325     return MMDRV_Message(wmld, WODM_GETPLAYBACKRATE, (DWORD)lpdw, 0L, TRUE);
4326 }
4327
4328 /**************************************************************************
4329  *                              waveOutGetPlaybackRate  [MMSYSTEM.417]
4330  */
4331 UINT16 WINAPI waveOutGetPlaybackRate16(HWAVEOUT16 hWaveOut16, LPDWORD lpdw)
4332 {
4333     return waveOutGetPlaybackRate(hWaveOut16, lpdw);
4334 }
4335
4336 /**************************************************************************
4337  *                              waveOutSetPlaybackRate  [WINMM.@]
4338  */
4339 UINT WINAPI waveOutSetPlaybackRate(HWAVEOUT hWaveOut, DWORD dw)
4340 {
4341     LPWINE_MLD          wmld;
4342
4343     TRACE("(%04X, %08lx);\n", hWaveOut, (DWORD)dw);
4344
4345     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4346         return MMSYSERR_INVALHANDLE;
4347     return MMDRV_Message(wmld, WODM_SETPLAYBACKRATE, dw, 0L, TRUE);
4348 }
4349
4350 /**************************************************************************
4351  *                              waveOutSetPlaybackRate  [MMSYSTEM.418]
4352  */
4353 UINT16 WINAPI waveOutSetPlaybackRate16(HWAVEOUT16 hWaveOut16, DWORD dw)
4354 {
4355     return waveOutSetPlaybackRate(hWaveOut16, dw);
4356 }
4357
4358 /**************************************************************************
4359  *                              waveOutGetVolume        [WINMM.@]
4360  */
4361 UINT WINAPI waveOutGetVolume(UINT devid, LPDWORD lpdw)
4362 {
4363     LPWINE_MLD          wmld;
4364
4365     TRACE("(%04X, %08lx);\n", devid, (DWORD)lpdw);
4366
4367      if ((wmld = MMDRV_Get(devid, MMDRV_WAVEOUT, TRUE)) == NULL)
4368         return MMSYSERR_INVALHANDLE;
4369
4370     return MMDRV_Message(wmld, WODM_GETVOLUME, (DWORD)lpdw, 0L, TRUE);
4371 }
4372
4373 /**************************************************************************
4374  *                              waveOutGetVolume        [MMSYSTEM.415]
4375  */
4376 UINT16 WINAPI waveOutGetVolume16(UINT16 devid, LPDWORD lpdw)
4377 {
4378     return waveOutGetVolume(devid, lpdw);
4379 }
4380
4381 /**************************************************************************
4382  *                              waveOutSetVolume        [WINMM.@]
4383  */
4384 UINT WINAPI waveOutSetVolume(UINT devid, DWORD dw)
4385 {
4386     LPWINE_MLD          wmld;
4387
4388     TRACE("(%04X, %08lx);\n", devid, dw);
4389
4390      if ((wmld = MMDRV_Get(devid, MMDRV_WAVEOUT, TRUE)) == NULL)
4391         return MMSYSERR_INVALHANDLE;
4392
4393     return MMDRV_Message(wmld, WODM_SETVOLUME, dw, 0L, TRUE);
4394 }
4395
4396 /**************************************************************************
4397  *                              waveOutSetVolume        [MMSYSTEM.416]
4398  */
4399 UINT16 WINAPI waveOutSetVolume16(UINT16 devid, DWORD dw)
4400 {
4401     return waveOutSetVolume(devid, dw);
4402 }
4403
4404 /**************************************************************************
4405  *                              waveOutGetID            [WINMM.@]
4406  */
4407 UINT WINAPI waveOutGetID(HWAVEOUT hWaveOut, UINT* lpuDeviceID)
4408 {
4409     LPWINE_MLD          wmld;
4410
4411     TRACE("(%04X, %p);\n", hWaveOut, lpuDeviceID);
4412
4413     if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
4414
4415     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4416         return MMSYSERR_INVALHANDLE;
4417
4418     *lpuDeviceID = wmld->uDeviceID;
4419     return 0;
4420 }
4421
4422 /**************************************************************************
4423  *                              waveOutGetID            [MMSYSTEM.420]
4424  */
4425 UINT16 WINAPI waveOutGetID16(HWAVEOUT16 hWaveOut, UINT16* lpuDeviceID)
4426 {
4427     LPWINE_MLD          wmld;
4428
4429     TRACE("(%04X, %p);\n", hWaveOut, lpuDeviceID);
4430
4431     if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
4432
4433     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4434         return MMSYSERR_INVALHANDLE;
4435
4436     *lpuDeviceID = wmld->uDeviceID;
4437     return 0;
4438 }
4439
4440 /**************************************************************************
4441  *                              waveOutMessage          [WINMM.@]
4442  */
4443 DWORD WINAPI waveOutMessage(HWAVEOUT hWaveOut, UINT uMessage,
4444                             DWORD dwParam1, DWORD dwParam2)
4445 {
4446     LPWINE_MLD          wmld;
4447
4448     TRACE("(%04x, %u, %ld, %ld)\n", hWaveOut, uMessage, dwParam1, dwParam2);
4449
4450     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) {
4451         if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) != NULL) {
4452             return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
4453         }
4454         return MMSYSERR_INVALHANDLE;
4455     }
4456
4457     /* from M$ KB */
4458     if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER))
4459         return MMSYSERR_INVALPARAM;
4460
4461     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
4462 }
4463
4464 /**************************************************************************
4465  *                              waveOutMessage          [MMSYSTEM.421]
4466  */
4467 DWORD WINAPI waveOutMessage16(HWAVEOUT16 hWaveOut, UINT16 uMessage,
4468                               DWORD dwParam1, DWORD dwParam2)
4469 {
4470     LPWINE_MLD          wmld;
4471
4472     TRACE("(%04x, %u, %ld, %ld)\n", hWaveOut, uMessage, dwParam1, dwParam2);
4473
4474     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) {
4475         if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) != NULL) {
4476             return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
4477         }
4478         return MMSYSERR_INVALHANDLE;
4479     }
4480
4481     /* from M$ KB */
4482     if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER))
4483         return MMSYSERR_INVALPARAM;
4484
4485     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, FALSE);
4486 }
4487
4488 /**************************************************************************
4489  *                              waveInGetNumDevs                [WINMM.@]
4490  */
4491 UINT WINAPI waveInGetNumDevs(void)
4492 {
4493     return MMDRV_GetNum(MMDRV_WAVEIN);
4494 }
4495
4496 /**************************************************************************
4497  *                              waveInGetNumDevs                [MMSYSTEM.501]
4498  */
4499 UINT16 WINAPI waveInGetNumDevs16(void)
4500 {
4501     return MMDRV_GetNum(MMDRV_WAVEIN);
4502 }
4503
4504 /**************************************************************************
4505  *                              waveInGetDevCapsW               [WINMM.@]
4506  */
4507 UINT WINAPI waveInGetDevCapsW(UINT uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)
4508 {
4509     WAVEINCAPSA         wicA;
4510     UINT                ret = waveInGetDevCapsA(uDeviceID, &wicA, uSize);
4511
4512     if (ret == MMSYSERR_NOERROR) {
4513         lpCaps->wMid = wicA.wMid;
4514         lpCaps->wPid = wicA.wPid;
4515         lpCaps->vDriverVersion = wicA.vDriverVersion;
4516         MultiByteToWideChar( CP_ACP, 0, wicA.szPname, -1, lpCaps->szPname,
4517                              sizeof(lpCaps->szPname)/sizeof(WCHAR) );
4518         lpCaps->dwFormats = wicA.dwFormats;
4519         lpCaps->wChannels = wicA.wChannels;
4520     }
4521
4522     return ret;
4523 }
4524
4525 /**************************************************************************
4526  *                              waveInGetDevCapsA               [WINMM.@]
4527  */
4528 UINT WINAPI waveInGetDevCapsA(UINT uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)
4529 {
4530     LPWINE_MLD          wmld;
4531
4532     TRACE("(%u %p %u)!\n", uDeviceID, lpCaps, uSize);
4533
4534     if ((wmld = MMDRV_Get(uDeviceID, MMDRV_WAVEIN, TRUE)) == NULL)
4535         return MMSYSERR_INVALHANDLE;
4536
4537     return MMDRV_Message(wmld, WIDM_GETDEVCAPS, (DWORD)lpCaps, uSize, TRUE);
4538 }
4539
4540 /**************************************************************************
4541  *                              waveInGetDevCaps                [MMSYSTEM.502]
4542  */
4543 UINT16 WINAPI waveInGetDevCaps16(UINT16 uDeviceID, LPWAVEINCAPS16 lpCaps,
4544                                  UINT16 uSize)
4545 {
4546     WAVEINCAPSA wicA;
4547     UINT        ret = waveInGetDevCapsA(uDeviceID, &wicA, sizeof(wicA));
4548
4549     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
4550
4551     if (ret == MMSYSERR_NOERROR) {
4552         lpCaps->wMid = wicA.wMid;
4553         lpCaps->wPid = wicA.wPid;
4554         lpCaps->vDriverVersion = wicA.vDriverVersion;
4555         strcpy(lpCaps->szPname, wicA.szPname);
4556         lpCaps->dwFormats = wicA.dwFormats;
4557         lpCaps->wChannels = wicA.wChannels;
4558     }
4559     return ret;
4560 }
4561
4562 /**************************************************************************
4563  *                              waveInGetErrorTextA     [WINMM.@]
4564  */
4565 UINT WINAPI waveInGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
4566 {
4567     return WAVE_GetErrorText(uError, lpText, uSize);
4568 }
4569
4570 /**************************************************************************
4571  *                              waveInGetErrorTextW     [WINMM.@]
4572  */
4573 UINT WINAPI waveInGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
4574 {
4575     LPSTR txt = HeapAlloc(GetProcessHeap(), 0, uSize);
4576     UINT        ret = WAVE_GetErrorText(uError, txt, uSize);
4577
4578     MultiByteToWideChar( CP_ACP, 0, txt, -1, lpText, uSize );
4579     HeapFree(GetProcessHeap(), 0, txt);
4580     return ret;
4581 }
4582
4583 /**************************************************************************
4584  *                              waveInGetErrorText      [MMSYSTEM.503]
4585  */
4586 UINT16 WINAPI waveInGetErrorText16(UINT16 uError, LPSTR lpText, UINT16 uSize)
4587 {
4588     return WAVE_GetErrorText(uError, lpText, uSize);
4589 }
4590
4591 /**************************************************************************
4592  *                              waveInOpen                      [WINMM.@]
4593  */
4594 UINT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
4595                        const LPWAVEFORMATEX lpFormat, DWORD dwCallback,
4596                        DWORD dwInstance, DWORD dwFlags)
4597 {
4598     return MMSYSTEM_waveOpen(lphWaveIn, uDeviceID, MMDRV_WAVEIN, lpFormat,
4599                              dwCallback, dwInstance, dwFlags, TRUE);
4600 }
4601
4602 /**************************************************************************
4603  *                              waveInOpen                      [MMSYSTEM.504]
4604  */
4605 UINT16 WINAPI waveInOpen16(HWAVEIN16* lphWaveIn, UINT16 uDeviceID,
4606                            const LPWAVEFORMATEX lpFormat, DWORD dwCallback,
4607                            DWORD dwInstance, DWORD dwFlags)
4608 {
4609     HWAVEIN             hWaveIn;
4610     UINT                ret;
4611
4612     /* since layout of WAVEFORMATEX is the same for 16/32 bits, we directly
4613      * call the 32 bit version
4614      * however, we need to promote correctly the wave mapper id
4615      * (0xFFFFFFFF and not 0x0000FFFF)
4616      */
4617     ret = MMSYSTEM_waveOpen(&hWaveIn, (uDeviceID == (UINT16)-1) ? (UINT)-1 : uDeviceID,
4618                             MMDRV_WAVEIN, lpFormat, dwCallback, dwInstance, dwFlags, FALSE);
4619
4620     if (lphWaveIn != NULL) *lphWaveIn = hWaveIn;
4621     return ret;
4622 }
4623
4624 /**************************************************************************
4625  *                              waveInClose                     [WINMM.@]
4626  */
4627 UINT WINAPI waveInClose(HWAVEIN hWaveIn)
4628 {
4629     LPWINE_MLD          wmld;
4630     DWORD               dwRet;
4631
4632     TRACE("(%04X)\n", hWaveIn);
4633
4634     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
4635         return MMSYSERR_INVALHANDLE;
4636
4637     dwRet = MMDRV_Message(wmld, WIDM_CLOSE, 0L, 0L, TRUE);
4638     MMDRV_Free(hWaveIn, wmld);
4639     return dwRet;
4640 }
4641
4642 /**************************************************************************
4643  *                              waveInClose                     [MMSYSTEM.505]
4644  */
4645 UINT16 WINAPI waveInClose16(HWAVEIN16 hWaveIn)
4646 {
4647     DWORD       level;
4648     UINT16      ret;
4649
4650     ReleaseThunkLock(&level);
4651     ret = waveInClose(hWaveIn);
4652     RestoreThunkLock(level);
4653     return ret;
4654 }
4655
4656 /**************************************************************************
4657  *                              waveInPrepareHeader             [WINMM.@]
4658  */
4659 UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
4660                                 UINT uSize)
4661 {
4662     LPWINE_MLD          wmld;
4663
4664     TRACE("(%04X, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
4665
4666     if (lpWaveInHdr == NULL) return MMSYSERR_INVALPARAM;
4667     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
4668         return MMSYSERR_INVALHANDLE;
4669
4670     lpWaveInHdr->dwBytesRecorded = 0;
4671
4672     return MMDRV_Message(wmld, WIDM_PREPARE, (DWORD)lpWaveInHdr, uSize, TRUE);
4673 }
4674
4675 /**************************************************************************
4676  *                              waveInPrepareHeader             [MMSYSTEM.506]
4677  */
4678 UINT16 WINAPI waveInPrepareHeader16(HWAVEIN16 hWaveIn,       /* [in] */
4679                                     SEGPTR lpsegWaveInHdr,   /* [???] */
4680                                     UINT16 uSize)            /* [in] */
4681 {
4682     LPWINE_MLD          wmld;
4683     LPWAVEHDR           lpWaveInHdr = MapSL(lpsegWaveInHdr);
4684     UINT16              ret;
4685
4686     TRACE("(%04X, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
4687
4688     if (lpWaveInHdr == NULL) return MMSYSERR_INVALHANDLE;
4689     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
4690         return MMSYSERR_INVALHANDLE;
4691
4692     lpWaveInHdr->dwBytesRecorded = 0;
4693
4694     ret = MMDRV_Message(wmld, WIDM_PREPARE, (DWORD)lpsegWaveInHdr, uSize, FALSE);
4695     return ret;
4696 }
4697
4698 /**************************************************************************
4699  *                              waveInUnprepareHeader   [WINMM.@]
4700  */
4701 UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
4702                                   UINT uSize)
4703 {
4704     LPWINE_MLD          wmld;
4705
4706     TRACE("(%04X, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
4707
4708     if (lpWaveInHdr == NULL) return MMSYSERR_INVALPARAM;
4709     if (!(lpWaveInHdr->dwFlags & WHDR_PREPARED)) {
4710         return MMSYSERR_NOERROR;
4711     }
4712
4713     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
4714         return MMSYSERR_INVALHANDLE;
4715
4716     return MMDRV_Message(wmld, WIDM_UNPREPARE, (DWORD)lpWaveInHdr, uSize, TRUE);
4717 }
4718
4719 /**************************************************************************
4720  *                              waveInUnprepareHeader   [MMSYSTEM.507]
4721  */
4722 UINT16 WINAPI waveInUnprepareHeader16(HWAVEIN16 hWaveIn,       /* [in] */
4723                                       SEGPTR lpsegWaveInHdr,   /* [???] */
4724                                       UINT16 uSize)            /* [in] */
4725 {
4726     LPWINE_MLD          wmld;
4727     LPWAVEHDR           lpWaveInHdr = MapSL(lpsegWaveInHdr);
4728
4729     TRACE("(%04X, %08lx, %u);\n", hWaveIn, lpsegWaveInHdr, uSize);
4730
4731     if (lpWaveInHdr == NULL) return MMSYSERR_INVALPARAM;
4732
4733     if (!(lpWaveInHdr->dwFlags & WHDR_PREPARED)) {
4734         return MMSYSERR_NOERROR;
4735     }
4736
4737     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
4738         return MMSYSERR_INVALHANDLE;
4739
4740     return MMDRV_Message(wmld, WIDM_UNPREPARE, (DWORD)lpsegWaveInHdr, uSize, FALSE);
4741 }
4742
4743 /**************************************************************************
4744  *                              waveInAddBuffer         [WINMM.@]
4745  */
4746 UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn,
4747                             WAVEHDR* lpWaveInHdr, UINT uSize)
4748 {
4749     LPWINE_MLD          wmld;
4750
4751     TRACE("(%04X, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
4752
4753     if (lpWaveInHdr == NULL) return MMSYSERR_INVALPARAM;
4754     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
4755         return MMSYSERR_INVALHANDLE;
4756
4757     return MMDRV_Message(wmld, WIDM_ADDBUFFER, (DWORD)lpWaveInHdr, uSize, TRUE);
4758 }
4759
4760 /**************************************************************************
4761  *                              waveInAddBuffer         [MMSYSTEM.508]
4762  */
4763 UINT16 WINAPI waveInAddBuffer16(HWAVEIN16 hWaveIn,       /* [in] */
4764                                 WAVEHDR* lpsegWaveInHdr, /* [???] NOTE: SEGPTR */
4765                                 UINT16 uSize)            /* [in] */
4766 {
4767     LPWINE_MLD          wmld;
4768
4769     TRACE("(%04X, %p, %u);\n", hWaveIn, lpsegWaveInHdr, uSize);
4770
4771     if (lpsegWaveInHdr == NULL) return MMSYSERR_INVALPARAM;
4772     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
4773         return MMSYSERR_INVALHANDLE;
4774
4775     return MMDRV_Message(wmld, WIDM_ADDBUFFER, (DWORD)lpsegWaveInHdr, uSize, FALSE);
4776 }
4777
4778 /**************************************************************************
4779  *                              waveInReset             [WINMM.@]
4780  */
4781 UINT WINAPI waveInReset(HWAVEIN hWaveIn)
4782 {
4783     LPWINE_MLD          wmld;
4784
4785     TRACE("(%04X);\n", hWaveIn);
4786
4787     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
4788         return MMSYSERR_INVALHANDLE;
4789
4790     return MMDRV_Message(wmld, WIDM_RESET, 0L, 0L, TRUE);
4791 }
4792
4793 /**************************************************************************
4794  *                              waveInReset             [MMSYSTEM.511]
4795  */
4796 UINT16 WINAPI waveInReset16(HWAVEIN16 hWaveIn16)
4797 {
4798     DWORD       level;
4799     UINT16      ret;
4800
4801     ReleaseThunkLock(&level);
4802     ret = waveInReset(hWaveIn16);
4803     RestoreThunkLock(level);
4804     return ret;
4805 }
4806
4807 /**************************************************************************
4808  *                              waveInStart             [WINMM.@]
4809  */
4810 UINT WINAPI waveInStart(HWAVEIN hWaveIn)
4811 {
4812     LPWINE_MLD          wmld;
4813
4814     TRACE("(%04X);\n", hWaveIn);
4815
4816     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
4817         return MMSYSERR_INVALHANDLE;
4818
4819     return MMDRV_Message(wmld, WIDM_START, 0L, 0L, TRUE);
4820 }
4821
4822 /**************************************************************************
4823  *                              waveInStart             [MMSYSTEM.509]
4824  */
4825 UINT16 WINAPI waveInStart16(HWAVEIN16 hWaveIn16)
4826 {
4827     DWORD       level;
4828     UINT16      ret;
4829
4830     ReleaseThunkLock(&level);
4831     ret = waveInStart(hWaveIn16);
4832     RestoreThunkLock(level);
4833     return ret;
4834 }
4835
4836 /**************************************************************************
4837  *                              waveInStop              [WINMM.@]
4838  */
4839 UINT WINAPI waveInStop(HWAVEIN hWaveIn)
4840 {
4841     LPWINE_MLD          wmld;
4842
4843     TRACE("(%04X);\n", hWaveIn);
4844
4845     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
4846         return MMSYSERR_INVALHANDLE;
4847
4848     return MMDRV_Message(wmld,WIDM_STOP, 0L, 0L, TRUE);
4849 }
4850
4851 /**************************************************************************
4852  *                              waveInStop              [MMSYSTEM.510]
4853  */
4854 UINT16 WINAPI waveInStop16(HWAVEIN16 hWaveIn16)
4855 {
4856     DWORD       level;
4857     UINT16      ret;
4858
4859     ReleaseThunkLock(&level);
4860     ret = waveInStop(hWaveIn16);
4861     RestoreThunkLock(level);
4862     return ret;
4863 }
4864
4865 /**************************************************************************
4866  *                              waveInGetPosition       [WINMM.@]
4867  */
4868 UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,
4869                               UINT uSize)
4870 {
4871     LPWINE_MLD          wmld;
4872
4873     TRACE("(%04X, %p, %u);\n", hWaveIn, lpTime, uSize);
4874
4875     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
4876         return MMSYSERR_INVALHANDLE;
4877
4878     return MMDRV_Message(wmld, WIDM_GETPOS, (DWORD)lpTime, uSize, TRUE);
4879 }
4880
4881 /**************************************************************************
4882  *                              waveInGetPosition       [MMSYSTEM.512]
4883  */
4884 UINT16 WINAPI waveInGetPosition16(HWAVEIN16 hWaveIn, LPMMTIME16 lpTime,
4885                                   UINT16 uSize)
4886 {
4887     UINT        ret;
4888     MMTIME      mmt;
4889
4890     mmt.wType = lpTime->wType;
4891     ret = waveInGetPosition(hWaveIn, &mmt, sizeof(mmt));
4892     MMSYSTEM_MMTIME32to16(lpTime, &mmt);
4893     return ret;
4894 }
4895
4896 /**************************************************************************
4897  *                              waveInGetID                     [WINMM.@]
4898  */
4899 UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)
4900 {
4901     LPWINE_MLD          wmld;
4902
4903     TRACE("(%04X, %p);\n", hWaveIn, lpuDeviceID);
4904
4905     if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
4906
4907     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
4908         return MMSYSERR_INVALHANDLE;
4909
4910     *lpuDeviceID = wmld->uDeviceID;
4911     return MMSYSERR_NOERROR;
4912 }
4913
4914 /**************************************************************************
4915  *                              waveInGetID                     [MMSYSTEM.513]
4916  */
4917 UINT16 WINAPI waveInGetID16(HWAVEIN16 hWaveIn, UINT16* lpuDeviceID)
4918 {
4919     LPWINE_MLD          wmld;
4920
4921     TRACE("(%04X, %p);\n", hWaveIn, lpuDeviceID);
4922
4923     if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
4924
4925     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
4926         return MMSYSERR_INVALHANDLE;
4927
4928     *lpuDeviceID = wmld->uDeviceID;
4929     return MMSYSERR_NOERROR;
4930 }
4931
4932 /**************************************************************************
4933  *                              waveInMessage           [WINMM.@]
4934  */
4935 DWORD WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
4936                            DWORD dwParam1, DWORD dwParam2)
4937 {
4938     LPWINE_MLD          wmld;
4939
4940     TRACE("(%04x, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
4941
4942     /* from M$ KB */
4943     if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER))
4944         return MMSYSERR_INVALPARAM;
4945
4946     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
4947         return MMSYSERR_INVALHANDLE;
4948
4949     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
4950 }
4951
4952 /**************************************************************************
4953  *                              waveInMessage           [MMSYSTEM.514]
4954  */
4955 DWORD WINAPI waveInMessage16(HWAVEIN16 hWaveIn, UINT16 uMessage,
4956                              DWORD dwParam1, DWORD dwParam2)
4957 {
4958     LPWINE_MLD          wmld;
4959
4960     TRACE("(%04x, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
4961
4962     /* from M$ KB */
4963     if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER))
4964         return MMSYSERR_INVALPARAM;
4965
4966     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
4967         return MMSYSERR_INVALHANDLE;
4968
4969     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
4970 }
4971
4972 /*#define USE_MM_TSK_WINE*/
4973
4974 /**************************************************************************
4975  *                              mmTaskCreate            [MMSYSTEM.900]
4976  *
4977  * Creates a 16 bit MM task. It's entry point is lpFunc, and it should be
4978  * called upon creation with dwPmt as parameter.
4979  */
4980 HINSTANCE16 WINAPI mmTaskCreate16(SEGPTR spProc, HINSTANCE16 *lphMmTask, DWORD dwPmt)
4981 {
4982     HINSTANCE16         ret;
4983     HINSTANCE16         handle;
4984     char cmdline[16];
4985     DWORD showCmd = 0x40002;
4986     LOADPARAMS16 lp;
4987
4988     TRACE("(%08lx, %p, %08lx);\n", spProc, lphMmTask, dwPmt);
4989     /* This to work requires NE modules to be started with a binary command line
4990      * which is not currently the case. A patch exists but has never been committed.
4991      * A workaround would be to integrate code for mmtask.tsk into Wine, but
4992      * this requires tremendous work (starting with patching tools/build to
4993      * create NE executables (and not only DLLs) for builtins modules.
4994      * EP 99/04/25
4995      */
4996     FIXME("This is currently broken. It will fail\n");
4997
4998     cmdline[0] = 0x0d;
4999     *(LPDWORD)(cmdline + 1) = (DWORD)spProc;
5000     *(LPDWORD)(cmdline + 5) = dwPmt;
5001     *(LPDWORD)(cmdline + 9) = 0;
5002
5003     lp.hEnvironment = 0;
5004     lp.cmdLine = MapLS(cmdline);
5005     lp.showCmd = MapLS(&showCmd);
5006     lp.reserved = 0;
5007
5008 #ifndef USE_MM_TSK_WINE
5009     handle = LoadModule16("c:\\windows\\system\\mmtask.tsk", &lp);
5010 #else
5011     handle = LoadModule16("mmtask.tsk", &lp);
5012 #endif
5013     if (handle < 32) {
5014         ret = (handle) ? 1 : 2;
5015         handle = 0;
5016     } else {
5017         ret = 0;
5018     }
5019     if (lphMmTask)
5020         *lphMmTask = handle;
5021
5022     UnMapLS( lp.cmdLine );
5023     UnMapLS( lp.showCmd );
5024     TRACE("=> 0x%04x/%d\n", handle, ret);
5025     return ret;
5026 }
5027
5028 #ifdef USE_MM_TSK_WINE
5029 /* C equivalent to mmtask.tsk binary content */
5030 void    mmTaskEntryPoint16(LPSTR cmdLine, WORD di, WORD si)
5031 {
5032     int len = cmdLine[0x80];
5033
5034     if (len / 2 == 6) {
5035         void    (*fpProc)(DWORD) = MapSL(*((DWORD*)(cmdLine + 1)));
5036         DWORD   dwPmt  = *((DWORD*)(cmdLine + 5));
5037
5038 #if 0
5039         InitTask16(); /* FIXME: pmts / from context ? */
5040         InitApp(di);
5041 #endif
5042         if (SetMessageQueue16(0x40)) {
5043             WaitEvent16(0);
5044             if (HIWORD(fpProc)) {
5045                 OldYield16();
5046 /* EPP          StackEnter16(); */
5047                 (fpProc)(dwPmt);
5048             }
5049         }
5050     }
5051     OldYield16();
5052     OldYield16();
5053     OldYield16();
5054     ExitProcess(0);
5055 }
5056 #endif
5057
5058 /**************************************************************************
5059  *                              mmTaskBlock             [MMSYSTEM.902]
5060  */
5061 void    WINAPI  mmTaskBlock16(HINSTANCE16 WINE_UNUSED hInst)
5062 {
5063     MSG         msg;
5064
5065     do {
5066         GetMessageA(&msg, 0, 0, 0);
5067         if (msg.hwnd) {
5068             TranslateMessage(&msg);
5069             DispatchMessageA(&msg);
5070         }
5071     } while (msg.message < 0x3A0);
5072 }
5073
5074 /**************************************************************************
5075  *                              mmTaskSignal            [MMSYSTEM.903]
5076  */
5077 LRESULT WINAPI mmTaskSignal16(HTASK16 ht)
5078 {
5079     TRACE("(%04x);\n", ht);
5080     return PostAppMessage16(ht, WM_USER, 0, 0);
5081 }
5082
5083 /**************************************************************************
5084  *                              mmGetCurrentTask        [MMSYSTEM.904]
5085  */
5086 HTASK16 WINAPI mmGetCurrentTask16(void)
5087 {
5088     return GetCurrentTask();
5089 }
5090
5091 /**************************************************************************
5092  *                              mmTaskYield             [MMSYSTEM.905]
5093  */
5094 void    WINAPI  mmTaskYield16(void)
5095 {
5096     MSG         msg;
5097
5098     if (PeekMessageA(&msg, 0, 0, 0, 0)) {
5099         K32WOWYield16();
5100     }
5101 }
5102
5103 DWORD   WINAPI  GetProcessFlags(DWORD);
5104
5105 /**************************************************************************
5106  *                              mmThreadCreate          [MMSYSTEM.1120]
5107  *
5108  * undocumented
5109  * Creates a MM thread, calling fpThreadAddr(dwPmt).
5110  * dwFlags:
5111  *      bit.0 set means create a 16 bit task instead of thread calling a 16 bit proc
5112  *      bit.1 set means to open a VxD for this thread (unsupported)
5113  */
5114 LRESULT WINAPI mmThreadCreate16(FARPROC16 fpThreadAddr, LPHANDLE lpHndl, DWORD dwPmt, DWORD dwFlags)
5115 {
5116     HANDLE16            hndl;
5117     LRESULT             ret;
5118
5119     TRACE("(%p, %p, %08lx, %08lx)!\n", fpThreadAddr, lpHndl, dwPmt, dwFlags);
5120
5121     hndl = GlobalAlloc16(sizeof(WINE_MMTHREAD), GMEM_SHARE|GMEM_ZEROINIT);
5122
5123     if (hndl == 0) {
5124         ret = 2;
5125     } else {
5126         WINE_MMTHREAD*  lpMMThd = MapSL( MAKESEGPTR(hndl, 0) );
5127
5128 #if 0
5129         /* force mmtask routines even if mmthread is required */
5130         /* this will work only if the patch about binary cmd line and NE tasks
5131          * is committed
5132          */
5133         dwFlags |= 1;
5134 #endif
5135
5136         lpMMThd->dwSignature    = WINE_MMTHREAD_CREATED;
5137         lpMMThd->dwCounter      = 0;
5138         lpMMThd->hThread        = 0;
5139         lpMMThd->dwThreadID     = 0;
5140         lpMMThd->fpThread       = fpThreadAddr;
5141         lpMMThd->dwThreadPmt    = dwPmt;
5142         lpMMThd->dwSignalCount  = 0;
5143         lpMMThd->hEvent         = 0;
5144         lpMMThd->hVxD           = 0;
5145         lpMMThd->dwStatus       = 0;
5146         lpMMThd->dwFlags        = dwFlags;
5147         lpMMThd->hTask          = 0;
5148
5149         if ((dwFlags & 1) == 0 && (GetProcessFlags(GetCurrentThreadId()) & 8) == 0) {
5150             lpMMThd->hEvent = CreateEventA(0, 0, 1, 0);
5151
5152             TRACE("Let's go crazy... trying new MM thread. lpMMThd=%p\n", lpMMThd);
5153             if (lpMMThd->dwFlags & 2) {
5154                 /* as long as we don't support MM VxD in wine, we don't need
5155                  * to care about this flag
5156                  */
5157                 /* FIXME("Don't know how to properly open VxD handles\n"); */
5158                 /* lpMMThd->hVxD = OpenVxDHandle(lpMMThd->hEvent); */
5159             }
5160
5161             lpMMThd->hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)WINE_mmThreadEntryPoint,
5162                                             (LPVOID)(DWORD)hndl, CREATE_SUSPENDED, &lpMMThd->dwThreadID);
5163             if (lpMMThd->hThread == 0) {
5164                 WARN("Couldn't create thread\n");
5165                 /* clean-up(VxDhandle...); devicedirectio... */
5166                 if (lpMMThd->hEvent != 0)
5167                     CloseHandle(lpMMThd->hEvent);
5168                 ret = 2;
5169             } else {
5170                 TRACE("Got a nice thread hndl=0x%04x id=0x%08lx\n", lpMMThd->hThread, lpMMThd->dwThreadID);
5171                 ret = 0;
5172             }
5173         } else {
5174             /* get WINE_mmThreadEntryPoint()
5175              * 2047 is its ordinal in mmsystem.spec
5176              */
5177             FARPROC16   fp = GetProcAddress16(GetModuleHandle16("MMSYSTEM"), (LPCSTR)2047);
5178
5179             TRACE("farproc seg=0x%08lx lin=%p\n", (DWORD)fp, MapSL((SEGPTR)fp));
5180
5181             ret = (fp == 0) ? 2 : mmTaskCreate16((DWORD)fp, 0, hndl);
5182         }
5183
5184         if (ret == 0) {
5185             if (lpMMThd->hThread && !ResumeThread(lpMMThd->hThread))
5186                 WARN("Couldn't resume thread\n");
5187
5188             while (lpMMThd->dwStatus != 0x10) { /* test also HIWORD of dwStatus */
5189                 UserYield16();
5190             }
5191         }
5192     }
5193
5194     if (ret != 0) {
5195         GlobalFree16(hndl);
5196         hndl = 0;
5197     }
5198
5199     if (lpHndl)
5200         *lpHndl = hndl;
5201
5202     TRACE("ok => %ld\n", ret);
5203     return ret;
5204 }
5205
5206 /**************************************************************************
5207  *                              mmThreadSignal          [MMSYSTEM.1121]
5208  */
5209 void WINAPI mmThreadSignal16(HANDLE16 hndl)
5210 {
5211     TRACE("(%04x)!\n", hndl);
5212
5213     if (hndl) {
5214         WINE_MMTHREAD*  lpMMThd = MapSL( MAKESEGPTR(hndl, 0) );
5215
5216         lpMMThd->dwCounter++;
5217         if (lpMMThd->hThread != 0) {
5218             InterlockedIncrement(&lpMMThd->dwSignalCount);
5219             SetEvent(lpMMThd->hEvent);
5220         } else {
5221             mmTaskSignal16(lpMMThd->hTask);
5222         }
5223         lpMMThd->dwCounter--;
5224     }
5225 }
5226
5227 /**************************************************************************
5228  *                              MMSYSTEM_ThreadBlock            [internal]
5229  */
5230 static  void    MMSYSTEM_ThreadBlock(WINE_MMTHREAD* lpMMThd)
5231 {
5232     MSG         msg;
5233     DWORD       ret;
5234
5235     if (lpMMThd->dwThreadID != GetCurrentThreadId())
5236         ERR("Not called by thread itself\n");
5237
5238     for (;;) {
5239         ResetEvent(lpMMThd->hEvent);
5240         if (InterlockedDecrement(&lpMMThd->dwSignalCount) >= 0)
5241             break;
5242         InterlockedIncrement(&lpMMThd->dwSignalCount);
5243
5244         TRACE("S1\n");
5245
5246         ret = MsgWaitForMultipleObjects(1, &lpMMThd->hEvent, FALSE, INFINITE, QS_ALLINPUT);
5247         switch (ret) {
5248         case WAIT_OBJECT_0:     /* Event */
5249             TRACE("S2.1\n");
5250             break;
5251         case WAIT_OBJECT_0 + 1: /* Msg */
5252             TRACE("S2.2\n");
5253             if (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
5254                 TranslateMessage(&msg);
5255                 DispatchMessageA(&msg);
5256             }
5257             break;
5258         default:
5259             WARN("S2.x unsupported ret val 0x%08lx\n", ret);
5260         }
5261         TRACE("S3\n");
5262     }
5263 }
5264
5265 /**************************************************************************
5266  *                              mmThreadBlock           [MMSYSTEM.1122]
5267  */
5268 void    WINAPI mmThreadBlock16(HANDLE16 hndl)
5269 {
5270     TRACE("(%04x)!\n", hndl);
5271
5272     if (hndl) {
5273         WINE_MMTHREAD*  lpMMThd = MapSL( MAKESEGPTR(hndl, 0) );
5274
5275         if (lpMMThd->hThread != 0) {
5276             DWORD       lc;
5277
5278             ReleaseThunkLock(&lc);
5279             MMSYSTEM_ThreadBlock(lpMMThd);
5280             RestoreThunkLock(lc);
5281         } else {
5282             mmTaskBlock16(lpMMThd->hTask);
5283         }
5284     }
5285     TRACE("done\n");
5286 }
5287
5288 /**************************************************************************
5289  *                              mmThreadIsCurrent       [MMSYSTEM.1123]
5290  */
5291 BOOL16  WINAPI mmThreadIsCurrent16(HANDLE16 hndl)
5292 {
5293     BOOL16              ret = FALSE;
5294
5295     TRACE("(%04x)!\n", hndl);
5296
5297     if (hndl && mmThreadIsValid16(hndl)) {
5298         WINE_MMTHREAD*  lpMMThd = MapSL( MAKESEGPTR(hndl, 0) );
5299         ret = (GetCurrentThreadId() == lpMMThd->dwThreadID);
5300     }
5301     TRACE("=> %d\n", ret);
5302     return ret;
5303 }
5304
5305 /**************************************************************************
5306  *                              mmThreadIsValid         [MMSYSTEM.1124]
5307  */
5308 BOOL16  WINAPI  mmThreadIsValid16(HANDLE16 hndl)
5309 {
5310     BOOL16              ret = FALSE;
5311
5312     TRACE("(%04x)!\n", hndl);
5313
5314     if (hndl) {
5315         WINE_MMTHREAD*  lpMMThd = MapSL( MAKESEGPTR(hndl, 0) );
5316
5317         if (!IsBadWritePtr(lpMMThd, sizeof(WINE_MMTHREAD)) &&
5318             lpMMThd->dwSignature == WINE_MMTHREAD_CREATED &&
5319             IsTask16(lpMMThd->hTask)) {
5320             lpMMThd->dwCounter++;
5321             if (lpMMThd->hThread != 0) {
5322                 DWORD   dwThreadRet;
5323                 if (GetExitCodeThread(lpMMThd->hThread, &dwThreadRet) &&
5324                     dwThreadRet == STATUS_PENDING) {
5325                     ret = TRUE;
5326                 }
5327             } else {
5328                 ret = TRUE;
5329             }
5330             lpMMThd->dwCounter--;
5331         }
5332     }
5333     TRACE("=> %d\n", ret);
5334     return ret;
5335 }
5336
5337 /**************************************************************************
5338  *                              mmThreadGetTask         [MMSYSTEM.1125]
5339  */
5340 HANDLE16 WINAPI mmThreadGetTask16(HANDLE16 hndl)
5341 {
5342     HANDLE16    ret = 0;
5343
5344     TRACE("(%04x)\n", hndl);
5345
5346     if (mmThreadIsValid16(hndl)) {
5347         WINE_MMTHREAD*  lpMMThd = MapSL( MAKESEGPTR(hndl, 0) );
5348         ret = lpMMThd->hTask;
5349     }
5350     return ret;
5351 }
5352
5353 /* ### start build ### */
5354 extern LONG CALLBACK MMSYSTEM_CallTo16_long_l    (FARPROC16,LONG);
5355 /* ### stop build ### */
5356
5357 /**************************************************************************
5358  *                              __wine_mmThreadEntryPoint (MMSYSTEM.2047)
5359  */
5360 void WINAPI WINE_mmThreadEntryPoint(DWORD _pmt)
5361 {
5362     HANDLE16            hndl = (HANDLE16)_pmt;
5363     WINE_MMTHREAD*      lpMMThd = MapSL( MAKESEGPTR(hndl, 0) );
5364
5365     TRACE("(%04x %p)\n", hndl, lpMMThd);
5366
5367     lpMMThd->hTask = LOWORD(GetCurrentTask());
5368     TRACE("[10-%08x] setting hTask to 0x%08x\n", lpMMThd->hThread, lpMMThd->hTask);
5369     lpMMThd->dwStatus = 0x10;
5370     MMSYSTEM_ThreadBlock(lpMMThd);
5371     TRACE("[20-%08x]\n", lpMMThd->hThread);
5372     lpMMThd->dwStatus = 0x20;
5373     if (lpMMThd->fpThread) {
5374         MMSYSTEM_CallTo16_long_l(lpMMThd->fpThread, lpMMThd->dwThreadPmt);
5375     }
5376     lpMMThd->dwStatus = 0x30;
5377     TRACE("[30-%08x]\n", lpMMThd->hThread);
5378     while (lpMMThd->dwCounter) {
5379         Sleep(1);
5380         /* K32WOWYield16();*/
5381     }
5382     TRACE("[XX-%08x]\n", lpMMThd->hThread);
5383     /* paranoia */
5384     lpMMThd->dwSignature = WINE_MMTHREAD_DELETED;
5385     /* close lpMMThread->hVxD directIO */
5386     if (lpMMThd->hEvent)
5387         CloseHandle(lpMMThd->hEvent);
5388     GlobalFree16(hndl);
5389     TRACE("done\n");
5390 }
5391
5392 typedef BOOL16 (WINAPI *MMCPLCALLBACK)(HWND, LPCSTR, LPCSTR, LPCSTR);
5393
5394 /**************************************************************************
5395  *                      mmShowMMCPLPropertySheet        [MMSYSTEM.1150]
5396  */
5397 BOOL16  WINAPI  mmShowMMCPLPropertySheet16(HWND hWnd, LPCSTR lpStrDevice,
5398                                            LPCSTR lpStrTab, LPCSTR lpStrTitle)
5399 {
5400     HANDLE      hndl;
5401     BOOL16      ret = FALSE;
5402
5403     TRACE("(%04x \"%s\" \"%s\" \"%s\")\n", hWnd, lpStrDevice, lpStrTab, lpStrTitle);
5404
5405     hndl = LoadLibraryA("MMSYS.CPL");
5406     if (hndl != 0) {
5407         MMCPLCALLBACK   fp = (MMCPLCALLBACK)GetProcAddress(hndl, "ShowMMCPLPropertySheet");
5408         if (fp != NULL) {
5409             DWORD       lc;
5410             ReleaseThunkLock(&lc);
5411             ret = (fp)(hWnd, lpStrDevice, lpStrTab, lpStrTitle);
5412             RestoreThunkLock(lc);
5413         }
5414         FreeLibrary(hndl);
5415     }
5416
5417     return ret;
5418 }
5419
5420 /**************************************************************************
5421  *                      StackEnter              [MMSYSTEM.32]
5422  */
5423 void WINAPI StackEnter16(void)
5424 {
5425 #ifdef __i386__
5426     /* mmsystem.dll from Win 95 does only this: so does Wine */
5427     __asm__("stc");
5428 #endif
5429 }
5430
5431 /**************************************************************************
5432  *                      StackLeave              [MMSYSTEM.33]
5433  */
5434 void WINAPI StackLeave16(void)
5435 {
5436 #ifdef __i386__
5437     /* mmsystem.dll from Win 95 does only this: so does Wine */
5438     __asm__("stc");
5439 #endif
5440 }
5441
5442 /**************************************************************************
5443  *                      WMMMidiRunOnce          [MMSYSTEM.8]
5444  */
5445 void WINAPI WMMMidiRunOnce16(void)
5446 {
5447         FIXME("(), stub!\n");
5448 }
5449
5450 /**************************************************************************
5451  *                      OutputDebugStr          [MMSYSTEM.30]
5452  */
5453 void WINAPI OutputDebugStr16(
5454     LPCSTR str) /* [in] The message to be logged and given to the debugger. */
5455 {
5456     OutputDebugStringA( str );
5457 }