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