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