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