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