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