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