Fix compilation errors on FreeBSD.
[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      [WINMM.@]
623  */
624 UINT WINAPI mmsystemGetVersion(void)
625 {
626     return mmsystemGetVersion16();
627 }
628
629 /**************************************************************************
630  *                              mmsystemGetVersion      [MMSYSTEM.5]
631  * return value borrowed from Win95 winmm.dll ;)
632  */
633 UINT16 WINAPI mmsystemGetVersion16(void)
634 {
635     TRACE("3.10 (Win95?)\n");
636     return 0x030a;
637 }
638
639 /**************************************************************************
640  *                              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  *                              mciGetErrorStringA              [WINMM.@]
1542  */
1543 BOOL WINAPI mciGetErrorStringA(DWORD wError, LPSTR lpstrBuffer, UINT uLength)
1544 {
1545     return mciGetErrorString16(wError, lpstrBuffer, uLength);
1546 }
1547
1548 /**************************************************************************
1549  *                              mciGetErrorString               [MMSYSTEM.706]
1550  */
1551 BOOL16 WINAPI mciGetErrorString16(DWORD dwError, LPSTR lpstrBuffer, UINT16 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
3654     TRACE("wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u\n", 
3655           lpFormat->wFormatTag, lpFormat->nChannels, lpFormat->nSamplesPerSec, 
3656           lpFormat->nAvgBytesPerSec, lpFormat->nBlockAlign, lpFormat->wBitsPerSample, lpFormat->cbSize);
3657     
3658     if ((wmld = MMDRV_Alloc(sizeof(WINE_WAVE), uType, &handle, 
3659                             &dwFlags, &dwCallback, &dwInstance, bFrom32)) == NULL) 
3660         return MMSYSERR_NOMEM;
3661
3662     wod.hWave = handle;
3663     wod.lpFormat = lpFormat;  /* should the struct be copied iso pointer? */
3664     wod.dwCallback = dwCallback;
3665     wod.dwInstance = dwInstance;
3666     wod.uMappedDeviceID = 0;
3667     wod.dnDevNode = 0L;
3668
3669     /* when called from 16 bit code, mapper will be 0x0000FFFF instead of 0xFFFFFFFF */
3670     /* this should fix it */
3671     wmld->uDeviceID = (uDeviceID == (UINT16)-1 && !bFrom32) ? (UINT)-1 : uDeviceID;
3672
3673     dwRet = MMDRV_Open(wmld, (uType==MMDRV_WAVEOUT)?WODM_OPEN:WIDM_OPEN, (DWORD)&wod, dwFlags);
3674
3675     if ((dwFlags & WAVE_FORMAT_QUERY) || dwRet != MMSYSERR_NOERROR) {
3676         MMDRV_Free(handle, wmld);
3677         handle = 0;
3678     } 
3679
3680     if (lphndl != NULL) *lphndl = handle;
3681     TRACE("=> %ld hWave=%04x\n", dwRet, handle);
3682
3683     return dwRet;
3684 }
3685
3686 /**************************************************************************
3687  *                              waveOutGetNumDevs               [WINMM.@]
3688  */
3689 UINT WINAPI waveOutGetNumDevs(void) 
3690 {
3691     return MMDRV_GetNum(MMDRV_WAVEOUT);
3692 }
3693
3694 /**************************************************************************
3695  *                              waveOutGetNumDevs               [MMSYSTEM.401]
3696  */
3697 UINT16 WINAPI waveOutGetNumDevs16(void)
3698 {
3699     return MMDRV_GetNum(MMDRV_WAVEOUT);
3700 }
3701
3702 /**************************************************************************
3703  *                              waveOutGetDevCaps               [MMSYSTEM.402]
3704  */
3705 UINT16 WINAPI waveOutGetDevCaps16(UINT16 uDeviceID, 
3706                                   LPWAVEOUTCAPS16 lpCaps, UINT16 uSize)
3707 {
3708     WAVEOUTCAPSA        wocA;
3709     UINT                ret;
3710
3711     TRACE("(%u %p %u)!\n", uDeviceID, lpCaps, uSize);
3712     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
3713
3714     ret = waveOutGetDevCapsA(uDeviceID, &wocA, sizeof(wocA));
3715
3716     if (ret == MMSYSERR_NOERROR) {
3717         lpCaps->wMid = wocA.wMid;
3718         lpCaps->wPid = wocA.wPid;
3719         lpCaps->vDriverVersion = wocA.vDriverVersion;
3720         strcpy(lpCaps->szPname, wocA.szPname);
3721         lpCaps->dwFormats = wocA.dwFormats;
3722         lpCaps->wChannels = wocA.wChannels;
3723         lpCaps->dwSupport = wocA.dwSupport;
3724     }
3725     return ret;
3726 }
3727
3728 /**************************************************************************
3729  *                              waveOutGetDevCapsA              [WINMM.@]
3730  */
3731 UINT WINAPI waveOutGetDevCapsA(UINT uDeviceID, LPWAVEOUTCAPSA lpCaps,
3732                                UINT uSize)
3733 {
3734     LPWINE_MLD          wmld;
3735
3736     TRACE("(%u %p %u)!\n", uDeviceID, lpCaps, uSize);
3737
3738     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
3739     
3740     if ((wmld = MMDRV_Get(uDeviceID, MMDRV_WAVEOUT, TRUE)) == NULL) 
3741         return MMSYSERR_INVALHANDLE;
3742
3743     return MMDRV_Message(wmld, WODM_GETDEVCAPS, (DWORD)lpCaps, uSize, TRUE);
3744
3745 }
3746
3747 /**************************************************************************
3748  *                              waveOutGetDevCapsW              [WINMM.@]
3749  */
3750 UINT WINAPI waveOutGetDevCapsW(UINT uDeviceID, LPWAVEOUTCAPSW lpCaps,
3751                                UINT uSize)
3752 {
3753     WAVEOUTCAPSA        wocA;
3754     UINT                ret;
3755
3756     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
3757     
3758     ret = waveOutGetDevCapsA(uDeviceID, &wocA, sizeof(wocA));
3759
3760     if (ret == MMSYSERR_NOERROR) {
3761         lpCaps->wMid = wocA.wMid;
3762         lpCaps->wPid = wocA.wPid;
3763         lpCaps->vDriverVersion = wocA.vDriverVersion;
3764         MultiByteToWideChar( CP_ACP, 0, wocA.szPname, -1, lpCaps->szPname,
3765                              sizeof(lpCaps->szPname)/sizeof(WCHAR) );
3766         lpCaps->dwFormats = wocA.dwFormats;
3767         lpCaps->wChannels = wocA.wChannels;
3768         lpCaps->dwSupport = wocA.dwSupport;
3769     }
3770     return ret;
3771 }
3772
3773 /**************************************************************************
3774  *                              WAVE_GetErrorText               [internal]
3775  */
3776 static  UINT16  WAVE_GetErrorText(UINT16 uError, LPSTR lpText, UINT16 uSize)
3777 {
3778     UINT16              ret = MMSYSERR_BADERRNUM;
3779
3780     if (lpText == NULL) {
3781         ret = MMSYSERR_INVALPARAM;
3782     } else if (uSize == 0) {
3783         ret = MMSYSERR_NOERROR;
3784     } else if (
3785                /* test has been removed 'coz MMSYSERR_BASE is 0, and gcc did emit
3786                 * a warning for the test was always true */
3787                (/*uError >= MMSYSERR_BASE && */uError <= MMSYSERR_LASTERROR) ||
3788                (uError >= WAVERR_BASE  && uError <= WAVERR_LASTERROR)) {
3789         
3790         if (LoadStringA(MULTIMEDIA_GetIData()->hWinMM32Instance, 
3791                         uError, lpText, uSize) > 0) {
3792             ret = MMSYSERR_NOERROR;
3793         }
3794     }
3795     return ret;
3796 }
3797
3798 /**************************************************************************
3799  *                              waveOutGetErrorText     [MMSYSTEM.403]
3800  */
3801 UINT16 WINAPI waveOutGetErrorText16(UINT16 uError, LPSTR lpText, UINT16 uSize)
3802 {
3803     return WAVE_GetErrorText(uError, lpText, uSize);
3804 }
3805
3806 /**************************************************************************
3807  *                              waveOutGetErrorTextA    [WINMM.@]
3808  */
3809 UINT WINAPI waveOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
3810 {
3811     return WAVE_GetErrorText(uError, lpText, uSize);
3812 }
3813
3814 /**************************************************************************
3815  *                              waveOutGetErrorTextW    [WINMM.@]
3816  */
3817 UINT WINAPI waveOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
3818 {
3819     LPSTR       xstr = HeapAlloc(GetProcessHeap(), 0, uSize);
3820     UINT        ret = WAVE_GetErrorText(uError, xstr, uSize);
3821     
3822     MultiByteToWideChar( CP_ACP, 0, xstr, -1, lpText, uSize );
3823     HeapFree(GetProcessHeap(), 0, xstr);
3824     return ret;
3825 }
3826
3827 /**************************************************************************
3828  *                      waveOutOpen                     [WINMM.@]
3829  * All the args/structs have the same layout as the win16 equivalents
3830  */
3831 UINT WINAPI waveOutOpen(HWAVEOUT* lphWaveOut, UINT uDeviceID,
3832                         const LPWAVEFORMATEX lpFormat, DWORD dwCallback,
3833                         DWORD dwInstance, DWORD dwFlags)
3834 {
3835     return MMSYSTEM_waveOpen(lphWaveOut, uDeviceID, MMDRV_WAVEOUT, lpFormat, 
3836                              dwCallback, dwInstance, dwFlags, TRUE);
3837 }
3838
3839 /**************************************************************************
3840  *                      waveOutOpen                     [MMSYSTEM.404]
3841  */
3842 UINT16 WINAPI waveOutOpen16(HWAVEOUT16* lphWaveOut, UINT16 uDeviceID,
3843                             const LPWAVEFORMATEX lpFormat, DWORD dwCallback,
3844                             DWORD dwInstance, DWORD dwFlags)
3845 {
3846     HWAVEOUT            hWaveOut;
3847     UINT                ret;
3848
3849     /* since layout of WAVEFORMATEX is the same for 16/32 bits, we directly
3850      * call the 32 bit version
3851      */
3852     ret = MMSYSTEM_waveOpen(&hWaveOut, uDeviceID, MMDRV_WAVEOUT, lpFormat, 
3853                             dwCallback, dwInstance, dwFlags, FALSE);
3854
3855     if (lphWaveOut != NULL) *lphWaveOut = hWaveOut;
3856     return ret;
3857 }
3858
3859 /**************************************************************************
3860  *                              waveOutClose            [WINMM.@]
3861  */
3862 UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
3863 {
3864     LPWINE_MLD          wmld;
3865     DWORD               dwRet;
3866     
3867     TRACE("(%04X)\n", hWaveOut);
3868
3869     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) 
3870         return MMSYSERR_INVALHANDLE;
3871
3872     dwRet = MMDRV_Close(wmld, WODM_CLOSE);
3873     MMDRV_Free(hWaveOut, wmld);
3874
3875     return dwRet;
3876 }
3877
3878 /**************************************************************************
3879  *                              waveOutClose            [MMSYSTEM.405]
3880  */
3881 UINT16 WINAPI waveOutClose16(HWAVEOUT16 hWaveOut)
3882 {
3883     DWORD       level;
3884     UINT16      ret;
3885
3886     ReleaseThunkLock(&level);
3887     ret = waveOutClose(hWaveOut);
3888     RestoreThunkLock(level);
3889     return ret;
3890 }
3891
3892 /**************************************************************************
3893  *                              waveOutPrepareHeader    [WINMM.@]
3894  */
3895 UINT WINAPI waveOutPrepareHeader(HWAVEOUT hWaveOut,
3896                                  WAVEHDR* lpWaveOutHdr, UINT uSize)
3897 {
3898     LPWINE_MLD          wmld;
3899     
3900     TRACE("(%04X, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
3901
3902     if (lpWaveOutHdr == NULL) return MMSYSERR_INVALPARAM;
3903
3904     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) 
3905         return MMSYSERR_INVALHANDLE;
3906
3907     return MMDRV_Message(wmld, WODM_PREPARE, (DWORD)lpWaveOutHdr, uSize, TRUE);
3908 }
3909
3910 /**************************************************************************
3911  *                              waveOutPrepareHeader    [MMSYSTEM.406]
3912  */
3913 UINT16 WINAPI waveOutPrepareHeader16(HWAVEOUT16 hWaveOut,      /* [in] */
3914                                      SEGPTR lpsegWaveOutHdr,   /* [???] */
3915                                      UINT16 uSize)             /* [in] */
3916 {
3917     LPWINE_MLD          wmld;
3918     LPWAVEHDR           lpWaveOutHdr = MapSL(lpsegWaveOutHdr);
3919     
3920     TRACE("(%04X, %08lx, %u);\n", hWaveOut, lpsegWaveOutHdr, uSize);
3921
3922     if (lpWaveOutHdr == NULL) return MMSYSERR_INVALPARAM;
3923
3924     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) 
3925         return MMSYSERR_INVALHANDLE;
3926
3927     return MMDRV_Message(wmld, WODM_PREPARE, (DWORD)lpsegWaveOutHdr, uSize, FALSE);
3928 }
3929
3930 /**************************************************************************
3931  *                              waveOutUnprepareHeader  [WINMM.@]
3932  */
3933 UINT WINAPI waveOutUnprepareHeader(HWAVEOUT hWaveOut,
3934                                    LPWAVEHDR lpWaveOutHdr, UINT uSize)
3935 {
3936     LPWINE_MLD          wmld;
3937     
3938     TRACE("(%04X, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
3939
3940     if (!(lpWaveOutHdr->dwFlags & WHDR_PREPARED)) {
3941         return MMSYSERR_NOERROR;
3942     }
3943
3944     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) 
3945         return MMSYSERR_INVALHANDLE;
3946
3947     return MMDRV_Message(wmld, WODM_UNPREPARE, (DWORD)lpWaveOutHdr, uSize, TRUE);
3948 }
3949
3950 /**************************************************************************
3951  *                              waveOutUnprepareHeader  [MMSYSTEM.407]
3952  */
3953 UINT16 WINAPI waveOutUnprepareHeader16(HWAVEOUT16 hWaveOut,       /* [in] */
3954                                        SEGPTR lpsegWaveOutHdr,    /* [???] */
3955                                        UINT16 uSize)              /* [in] */
3956 {
3957     LPWINE_MLD          wmld;
3958     LPWAVEHDR           lpWaveOutHdr = MapSL(lpsegWaveOutHdr);
3959     
3960     TRACE("(%04X, %08lx, %u);\n", hWaveOut, lpsegWaveOutHdr, uSize);
3961
3962     if (!(lpWaveOutHdr->dwFlags & WHDR_PREPARED)) {
3963         return MMSYSERR_NOERROR;
3964     }
3965
3966     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) 
3967         return MMSYSERR_INVALHANDLE;
3968
3969     return MMDRV_Message(wmld, WODM_UNPREPARE, (DWORD)lpsegWaveOutHdr, uSize, FALSE);
3970 }
3971
3972 /**************************************************************************
3973  *                              waveOutWrite            [WINMM.@]
3974  */
3975 UINT WINAPI waveOutWrite(HWAVEOUT hWaveOut, LPWAVEHDR lpWaveOutHdr,
3976                          UINT uSize)
3977 {
3978     LPWINE_MLD          wmld;
3979     
3980     TRACE("(%04X, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
3981
3982     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) 
3983         return MMSYSERR_INVALHANDLE;
3984
3985     return MMDRV_Message(wmld, WODM_WRITE, (DWORD)lpWaveOutHdr, uSize, TRUE);
3986 }
3987
3988 /**************************************************************************
3989  *                              waveOutWrite            [MMSYSTEM.408]
3990  */
3991 UINT16 WINAPI waveOutWrite16(HWAVEOUT16 hWaveOut,       /* [in] */
3992                              LPWAVEHDR lpsegWaveOutHdr, /* [???] NOTE: SEGPTR */
3993                              UINT16 uSize)              /* [in] */
3994 {
3995     LPWINE_MLD          wmld;
3996     
3997     TRACE("(%04X, %p, %u);\n", hWaveOut, lpsegWaveOutHdr, uSize);
3998
3999     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) 
4000         return MMSYSERR_INVALHANDLE;
4001
4002     return MMDRV_Message(wmld, WODM_WRITE, (DWORD)lpsegWaveOutHdr, uSize, FALSE);
4003 }
4004
4005 /**************************************************************************
4006  *                              waveOutBreakLoop        [WINMM.@]
4007  */
4008 UINT WINAPI waveOutBreakLoop(HWAVEOUT hWaveOut)
4009 {
4010     LPWINE_MLD          wmld;
4011
4012     TRACE("(%04X);\n", hWaveOut);
4013
4014     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4015         return MMSYSERR_INVALHANDLE;
4016     return MMDRV_Message(wmld, WODM_BREAKLOOP, 0L, 0L, TRUE);
4017 }
4018
4019 /**************************************************************************
4020  *                              waveOutBreakLoop        [MMSYSTEM.419]
4021  */
4022 UINT16 WINAPI waveOutBreakLoop16(HWAVEOUT16 hWaveOut16)
4023 {
4024     DWORD       level;
4025     UINT16      ret;
4026
4027     ReleaseThunkLock(&level);
4028     ret = waveOutBreakLoop(hWaveOut16);
4029     RestoreThunkLock(level);
4030     return ret;
4031 }
4032
4033 /**************************************************************************
4034  *                              waveOutPause            [WINMM.@]
4035  */
4036 UINT WINAPI waveOutPause(HWAVEOUT hWaveOut)
4037 {
4038     LPWINE_MLD          wmld;
4039
4040     TRACE("(%04X);\n", hWaveOut);
4041
4042     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4043         return MMSYSERR_INVALHANDLE;
4044     return MMDRV_Message(wmld, WODM_PAUSE, 0L, 0L, TRUE);
4045 }
4046
4047 /**************************************************************************
4048  *                              waveOutPause            [MMSYSTEM.409]
4049  */
4050 UINT16 WINAPI waveOutPause16(HWAVEOUT16 hWaveOut16)
4051 {
4052     DWORD       level;
4053     UINT16      ret;
4054
4055     ReleaseThunkLock(&level);
4056     ret = waveOutPause(hWaveOut16);
4057     RestoreThunkLock(level);
4058     return ret;
4059 }
4060
4061 /**************************************************************************
4062  *                              waveOutReset            [WINMM.@]
4063  */
4064 UINT WINAPI waveOutReset(HWAVEOUT hWaveOut)
4065 {
4066     LPWINE_MLD          wmld;
4067
4068     TRACE("(%04X);\n", hWaveOut);
4069
4070     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4071         return MMSYSERR_INVALHANDLE;
4072     return MMDRV_Message(wmld, WODM_RESET, 0L, 0L, TRUE);
4073 }
4074
4075 /**************************************************************************
4076  *                              waveOutReset            [MMSYSTEM.411]
4077  */
4078 UINT16 WINAPI waveOutReset16(HWAVEOUT16 hWaveOut16)
4079 {
4080     DWORD       level;
4081     UINT16      ret;
4082
4083     ReleaseThunkLock(&level);
4084     ret = waveOutReset(hWaveOut16);
4085     RestoreThunkLock(level);
4086     return ret;
4087 }
4088
4089 /**************************************************************************
4090  *                              waveOutRestart          [WINMM.@]
4091  */
4092 UINT WINAPI waveOutRestart(HWAVEOUT hWaveOut)
4093 {
4094     LPWINE_MLD          wmld;
4095
4096     TRACE("(%04X);\n", hWaveOut);
4097
4098     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4099         return MMSYSERR_INVALHANDLE;
4100     return MMDRV_Message(wmld, WODM_RESTART, 0L, 0L, TRUE);
4101 }
4102
4103 /**************************************************************************
4104  *                              waveOutRestart  [MMSYSTEM.410]
4105  */
4106 UINT16 WINAPI waveOutRestart16(HWAVEOUT16 hWaveOut16)
4107 {
4108     DWORD       level;
4109     UINT16      ret;
4110
4111     ReleaseThunkLock(&level);
4112     ret = waveOutRestart(hWaveOut16);
4113     RestoreThunkLock(level);
4114     return ret;
4115 }
4116
4117 /**************************************************************************
4118  *                              waveOutGetPosition      [WINMM.@]
4119  */
4120 UINT WINAPI waveOutGetPosition(HWAVEOUT hWaveOut, LPMMTIME lpTime,
4121                                UINT uSize)
4122 {
4123     LPWINE_MLD          wmld;
4124
4125     TRACE("(%04X, %p, %u);\n", hWaveOut, lpTime, uSize);
4126
4127     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) 
4128         return MMSYSERR_INVALHANDLE;
4129
4130     return MMDRV_Message(wmld, WODM_GETPOS, (DWORD)lpTime, uSize, TRUE);
4131 }
4132
4133 /**************************************************************************
4134  *                              waveOutGetPosition      [MMSYSTEM.412]
4135  */
4136 UINT16 WINAPI waveOutGetPosition16(HWAVEOUT16 hWaveOut, LPMMTIME16 lpTime,
4137                                    UINT16 uSize)
4138 {
4139     UINT        ret;
4140     MMTIME      mmt;
4141
4142     mmt.wType = lpTime->wType;
4143     ret = waveOutGetPosition(hWaveOut, &mmt, sizeof(mmt));
4144     MMSYSTEM_MMTIME32to16(lpTime, &mmt);
4145     return ret;
4146 }
4147
4148 /**************************************************************************
4149  *                              waveOutGetPitch         [WINMM.@]
4150  */
4151 UINT WINAPI waveOutGetPitch(HWAVEOUT hWaveOut, LPDWORD lpdw)
4152 {
4153     LPWINE_MLD          wmld;
4154
4155     TRACE("(%04X, %08lx);\n", hWaveOut, (DWORD)lpdw);
4156
4157     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4158         return MMSYSERR_INVALHANDLE;
4159     return MMDRV_Message(wmld, WODM_GETPITCH, (DWORD)lpdw, 0L, TRUE);
4160 }
4161
4162 /**************************************************************************
4163  *                              waveOutGetPitch         [MMSYSTEM.413]
4164  */
4165 UINT16 WINAPI waveOutGetPitch16(HWAVEOUT16 hWaveOut16, LPDWORD lpdw)
4166 {
4167     return waveOutGetPitch(hWaveOut16, lpdw);
4168 }
4169
4170 /**************************************************************************
4171  *                              waveOutSetPitch         [WINMM.@]
4172  */
4173 UINT WINAPI waveOutSetPitch(HWAVEOUT hWaveOut, DWORD dw)
4174 {
4175     LPWINE_MLD          wmld;
4176
4177     TRACE("(%04X, %08lx);\n", hWaveOut, (DWORD)dw);
4178
4179     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4180         return MMSYSERR_INVALHANDLE;
4181     return MMDRV_Message(wmld, WODM_SETPITCH, dw, 0L, TRUE);
4182 }
4183
4184 /**************************************************************************
4185  *                              waveOutSetPitch         [MMSYSTEM.414]
4186  */
4187 UINT16 WINAPI waveOutSetPitch16(HWAVEOUT16 hWaveOut16, DWORD dw)
4188 {
4189     return waveOutSetPitch(hWaveOut16, dw);
4190 }
4191
4192 /**************************************************************************
4193  *                              waveOutGetPlaybackRate  [WINMM.@]
4194  */
4195 UINT WINAPI waveOutGetPlaybackRate(HWAVEOUT hWaveOut, LPDWORD lpdw)
4196 {
4197     LPWINE_MLD          wmld;
4198
4199     TRACE("(%04X, %08lx);\n", hWaveOut, (DWORD)lpdw);
4200
4201     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4202         return MMSYSERR_INVALHANDLE;
4203     return MMDRV_Message(wmld, WODM_GETPLAYBACKRATE, (DWORD)lpdw, 0L, TRUE);
4204 }
4205
4206 /**************************************************************************
4207  *                              waveOutGetPlaybackRate  [MMSYSTEM.417]
4208  */
4209 UINT16 WINAPI waveOutGetPlaybackRate16(HWAVEOUT16 hWaveOut16, LPDWORD lpdw)
4210 {
4211     return waveOutGetPlaybackRate(hWaveOut16, lpdw);
4212 }
4213
4214 /**************************************************************************
4215  *                              waveOutSetPlaybackRate  [WINMM.@]
4216  */
4217 UINT WINAPI waveOutSetPlaybackRate(HWAVEOUT hWaveOut, DWORD dw)
4218 {
4219     LPWINE_MLD          wmld;
4220
4221     TRACE("(%04X, %08lx);\n", hWaveOut, (DWORD)dw);
4222
4223     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
4224         return MMSYSERR_INVALHANDLE;
4225     return MMDRV_Message(wmld, WODM_SETPLAYBACKRATE, dw, 0L, TRUE);
4226 }
4227
4228 /**************************************************************************
4229  *                              waveOutSetPlaybackRate  [MMSYSTEM.418]
4230  */
4231 UINT16 WINAPI waveOutSetPlaybackRate16(HWAVEOUT16 hWaveOut16, DWORD dw)
4232 {
4233     return waveOutSetPlaybackRate(hWaveOut16, dw);
4234 }
4235
4236 /**************************************************************************
4237  *                              waveOutGetVolume        [WINMM.@]
4238  */
4239 UINT WINAPI waveOutGetVolume(UINT devid, LPDWORD lpdw)
4240 {
4241     LPWINE_MLD          wmld;
4242
4243     TRACE("(%04X, %08lx);\n", devid, (DWORD)lpdw);
4244
4245      if ((wmld = MMDRV_Get(devid, MMDRV_WAVEOUT, TRUE)) == NULL)
4246         return MMSYSERR_INVALHANDLE;
4247
4248     return MMDRV_Message(wmld, WODM_GETVOLUME, (DWORD)lpdw, 0L, TRUE);
4249 }
4250
4251 /**************************************************************************
4252  *                              waveOutGetVolume        [MMSYSTEM.415]
4253  */
4254 UINT16 WINAPI waveOutGetVolume16(UINT16 devid, LPDWORD lpdw)
4255 {
4256     return waveOutGetVolume(devid, lpdw);
4257 }
4258     
4259 /**************************************************************************
4260  *                              waveOutSetVolume        [WINMM.@]
4261  */
4262 UINT WINAPI waveOutSetVolume(UINT devid, DWORD dw)
4263 {
4264     LPWINE_MLD          wmld;
4265
4266     TRACE("(%04X, %08lx);\n", devid, dw);
4267
4268      if ((wmld = MMDRV_Get(devid, MMDRV_WAVEOUT, TRUE)) == NULL)
4269         return MMSYSERR_INVALHANDLE;
4270
4271     return MMDRV_Message(wmld, WODM_SETVOLUME, dw, 0L, TRUE);
4272 }
4273
4274 /**************************************************************************
4275  *                              waveOutSetVolume        [MMSYSTEM.416]
4276  */
4277 UINT16 WINAPI waveOutSetVolume16(UINT16 devid, DWORD dw)
4278 {
4279     return waveOutSetVolume(devid, dw);
4280 }
4281     
4282 /**************************************************************************
4283  *                              waveOutGetID            [WINMM.@]
4284  */
4285 UINT WINAPI waveOutGetID(HWAVEOUT hWaveOut, UINT* lpuDeviceID)
4286 {
4287     LPWINE_MLD          wmld;
4288
4289     TRACE("(%04X, %p);\n", hWaveOut, lpuDeviceID);
4290
4291     if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
4292
4293     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) 
4294         return MMSYSERR_INVALHANDLE;
4295
4296     *lpuDeviceID = wmld->uDeviceID;
4297     return 0;
4298 }
4299
4300 /**************************************************************************
4301  *                              waveOutGetID            [MMSYSTEM.420]
4302  */
4303 UINT16 WINAPI waveOutGetID16(HWAVEOUT16 hWaveOut, UINT16* lpuDeviceID)
4304 {
4305     LPWINE_MLD          wmld;
4306
4307     TRACE("(%04X, %p);\n", hWaveOut, lpuDeviceID);
4308
4309     if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
4310
4311     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) 
4312         return MMSYSERR_INVALHANDLE;
4313
4314     *lpuDeviceID = wmld->uDeviceID;
4315     return 0;
4316 }
4317
4318 /**************************************************************************
4319  *                              waveOutMessage          [WINMM.@]
4320  */
4321 DWORD WINAPI waveOutMessage(HWAVEOUT hWaveOut, UINT uMessage, 
4322                             DWORD dwParam1, DWORD dwParam2)
4323 {
4324     LPWINE_MLD          wmld;
4325     
4326     TRACE("(%04x, %u, %ld, %ld)\n", hWaveOut, uMessage, dwParam1, dwParam2);
4327
4328     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) {
4329         if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) != NULL) {
4330             return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
4331         }
4332         return MMSYSERR_INVALHANDLE;
4333     }
4334
4335     /* from M$ KB */
4336     if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER))
4337         return MMSYSERR_INVALPARAM;
4338
4339     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
4340 }
4341
4342 /**************************************************************************
4343  *                              waveOutMessage          [MMSYSTEM.421]
4344  */
4345 DWORD WINAPI waveOutMessage16(HWAVEOUT16 hWaveOut, UINT16 uMessage, 
4346                               DWORD dwParam1, DWORD dwParam2)
4347 {
4348     LPWINE_MLD          wmld;
4349     
4350     TRACE("(%04x, %u, %ld, %ld)\n", hWaveOut, uMessage, dwParam1, dwParam2);
4351
4352     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) {
4353         if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) != NULL) {
4354             return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
4355         }
4356         return MMSYSERR_INVALHANDLE;
4357     }
4358
4359     /* from M$ KB */
4360     if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER))
4361         return MMSYSERR_INVALPARAM;
4362
4363     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, FALSE);
4364 }
4365
4366 /**************************************************************************
4367  *                              waveInGetNumDevs                [WINMM.@]
4368  */
4369 UINT WINAPI waveInGetNumDevs(void)
4370 {
4371     return MMDRV_GetNum(MMDRV_WAVEIN);
4372 }
4373
4374 /**************************************************************************
4375  *                              waveInGetNumDevs                [MMSYSTEM.501]
4376  */
4377 UINT16 WINAPI waveInGetNumDevs16(void)
4378 {
4379     return MMDRV_GetNum(MMDRV_WAVEIN);
4380 }
4381
4382 /**************************************************************************
4383  *                              waveInGetDevCapsW               [WINMM.@]
4384  */
4385 UINT WINAPI waveInGetDevCapsW(UINT uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)
4386 {
4387     WAVEINCAPSA         wicA;
4388     UINT                ret = waveInGetDevCapsA(uDeviceID, &wicA, uSize);
4389
4390     if (ret == MMSYSERR_NOERROR) {
4391         lpCaps->wMid = wicA.wMid;
4392         lpCaps->wPid = wicA.wPid;
4393         lpCaps->vDriverVersion = wicA.vDriverVersion;
4394         MultiByteToWideChar( CP_ACP, 0, wicA.szPname, -1, lpCaps->szPname,
4395                              sizeof(lpCaps->szPname)/sizeof(WCHAR) );
4396         lpCaps->dwFormats = wicA.dwFormats;
4397         lpCaps->wChannels = wicA.wChannels;
4398     }
4399     
4400     return ret;
4401 }
4402
4403 /**************************************************************************
4404  *                              waveInGetDevCapsA               [WINMM.@]
4405  */
4406 UINT WINAPI waveInGetDevCapsA(UINT uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)
4407 {
4408     LPWINE_MLD          wmld;
4409
4410     TRACE("(%u %p %u)!\n", uDeviceID, lpCaps, uSize);
4411
4412     if ((wmld = MMDRV_Get(uDeviceID, MMDRV_WAVEIN, TRUE)) == NULL) 
4413         return MMSYSERR_INVALHANDLE;
4414
4415     return MMDRV_Message(wmld, WIDM_GETDEVCAPS, (DWORD)lpCaps, uSize, TRUE);
4416 }
4417
4418 /**************************************************************************
4419  *                              waveInGetDevCaps                [MMSYSTEM.502]
4420  */
4421 UINT16 WINAPI waveInGetDevCaps16(UINT16 uDeviceID, LPWAVEINCAPS16 lpCaps, 
4422                                  UINT16 uSize)
4423 {
4424     WAVEINCAPSA wicA;
4425     UINT        ret = waveInGetDevCapsA(uDeviceID, &wicA, sizeof(wicA));
4426
4427     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
4428
4429     if (ret == MMSYSERR_NOERROR) {
4430         lpCaps->wMid = wicA.wMid;
4431         lpCaps->wPid = wicA.wPid;
4432         lpCaps->vDriverVersion = wicA.vDriverVersion;
4433         strcpy(lpCaps->szPname, wicA.szPname);
4434         lpCaps->dwFormats = wicA.dwFormats;
4435         lpCaps->wChannels = wicA.wChannels;
4436     }
4437     return ret;
4438 }
4439
4440 /**************************************************************************
4441  *                              waveInGetErrorTextA     [WINMM.@]
4442  */
4443 UINT WINAPI waveInGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
4444 {
4445     return WAVE_GetErrorText(uError, lpText, uSize);
4446 }
4447
4448 /**************************************************************************
4449  *                              waveInGetErrorTextW     [WINMM.@]
4450  */
4451 UINT WINAPI waveInGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
4452 {
4453     LPSTR txt = HeapAlloc(GetProcessHeap(), 0, uSize);
4454     UINT        ret = WAVE_GetErrorText(uError, txt, uSize);
4455     
4456     MultiByteToWideChar( CP_ACP, 0, txt, -1, lpText, uSize );
4457     HeapFree(GetProcessHeap(), 0, txt);
4458     return ret;
4459 }
4460
4461 /**************************************************************************
4462  *                              waveInGetErrorText      [MMSYSTEM.503]
4463  */
4464 UINT16 WINAPI waveInGetErrorText16(UINT16 uError, LPSTR lpText, UINT16 uSize)
4465 {
4466     return WAVE_GetErrorText(uError, lpText, uSize);
4467 }
4468
4469 /**************************************************************************
4470  *                              waveInOpen                      [WINMM.@]
4471  */
4472 UINT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
4473                        const LPWAVEFORMATEX lpFormat, DWORD dwCallback,
4474                        DWORD dwInstance, DWORD dwFlags)
4475 {
4476     return MMSYSTEM_waveOpen(lphWaveIn, uDeviceID, MMDRV_WAVEIN, lpFormat, 
4477                              dwCallback, dwInstance, dwFlags, TRUE);
4478 }
4479
4480 /**************************************************************************
4481  *                              waveInOpen                      [MMSYSTEM.504]
4482  */
4483 UINT16 WINAPI waveInOpen16(HWAVEIN16* lphWaveIn, UINT16 uDeviceID,
4484                            const LPWAVEFORMATEX lpFormat, DWORD dwCallback,
4485                            DWORD dwInstance, DWORD dwFlags)
4486 {
4487     HWAVEIN             hWaveIn;
4488     UINT                ret;
4489
4490     /* since layout of WAVEFORMATEX is the same for 16/32 bits, we directly
4491      * call the 32 bit version
4492      */
4493     ret = MMSYSTEM_waveOpen(&hWaveIn, uDeviceID, MMDRV_WAVEIN, lpFormat, 
4494                             dwCallback, dwInstance, dwFlags, FALSE);
4495
4496     if (lphWaveIn != NULL) *lphWaveIn = hWaveIn;
4497     return ret;
4498 }
4499
4500 /**************************************************************************
4501  *                              waveInClose                     [WINMM.@]
4502  */
4503 UINT WINAPI waveInClose(HWAVEIN hWaveIn)
4504 {
4505     LPWINE_MLD          wmld;
4506     DWORD               dwRet;
4507     
4508     TRACE("(%04X)\n", hWaveIn);
4509
4510     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) 
4511         return MMSYSERR_INVALHANDLE;
4512
4513     dwRet = MMDRV_Message(wmld, WIDM_CLOSE, 0L, 0L, TRUE);
4514     MMDRV_Free(hWaveIn, wmld);
4515     return dwRet;
4516 }
4517
4518 /**************************************************************************
4519  *                              waveInClose                     [MMSYSTEM.505]
4520  */
4521 UINT16 WINAPI waveInClose16(HWAVEIN16 hWaveIn)
4522 {
4523     DWORD       level;
4524     UINT16      ret;
4525
4526     ReleaseThunkLock(&level);
4527     ret = waveInClose(hWaveIn);
4528     RestoreThunkLock(level);
4529     return ret;
4530 }
4531
4532 /**************************************************************************
4533  *                              waveInPrepareHeader             [WINMM.@]
4534  */
4535 UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr, 
4536                                 UINT uSize)
4537 {
4538     LPWINE_MLD          wmld;
4539     
4540     TRACE("(%04X, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
4541
4542     if (lpWaveInHdr == NULL) return MMSYSERR_INVALPARAM;
4543     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) 
4544         return MMSYSERR_INVALHANDLE;
4545
4546     lpWaveInHdr->dwBytesRecorded = 0;
4547
4548     return MMDRV_Message(wmld, WIDM_PREPARE, (DWORD)lpWaveInHdr, uSize, TRUE);
4549 }
4550
4551 /**************************************************************************
4552  *                              waveInPrepareHeader             [MMSYSTEM.506]
4553  */
4554 UINT16 WINAPI waveInPrepareHeader16(HWAVEIN16 hWaveIn,       /* [in] */
4555                                     SEGPTR lpsegWaveInHdr,   /* [???] */
4556                                     UINT16 uSize)            /* [in] */
4557 {
4558     LPWINE_MLD          wmld;
4559     LPWAVEHDR           lpWaveInHdr = MapSL(lpsegWaveInHdr);
4560     UINT16              ret;
4561     
4562     TRACE("(%04X, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
4563
4564     if (lpWaveInHdr == NULL) return MMSYSERR_INVALHANDLE;
4565     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) 
4566         return MMSYSERR_INVALHANDLE;
4567
4568     lpWaveInHdr->dwBytesRecorded = 0;
4569
4570     ret = MMDRV_Message(wmld, WIDM_PREPARE, (DWORD)lpsegWaveInHdr, uSize, FALSE);
4571     return ret;
4572 }
4573
4574 /**************************************************************************
4575  *                              waveInUnprepareHeader   [WINMM.@]
4576  */
4577 UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr, 
4578                                   UINT uSize)
4579 {
4580     LPWINE_MLD          wmld;
4581     
4582     TRACE("(%04X, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
4583
4584     if (lpWaveInHdr == NULL) return MMSYSERR_INVALPARAM;
4585     if (!(lpWaveInHdr->dwFlags & WHDR_PREPARED)) {
4586         return MMSYSERR_NOERROR;
4587     }
4588
4589     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) 
4590         return MMSYSERR_INVALHANDLE;
4591
4592     return MMDRV_Message(wmld, WIDM_UNPREPARE, (DWORD)lpWaveInHdr, uSize, TRUE);
4593 }
4594
4595 /**************************************************************************
4596  *                              waveInUnprepareHeader   [MMSYSTEM.507]
4597  */
4598 UINT16 WINAPI waveInUnprepareHeader16(HWAVEIN16 hWaveIn,       /* [in] */  
4599                                       SEGPTR lpsegWaveInHdr,   /* [???] */
4600                                       UINT16 uSize)            /* [in] */
4601 {
4602     LPWINE_MLD          wmld;
4603     LPWAVEHDR           lpWaveInHdr = MapSL(lpsegWaveInHdr);
4604     
4605     TRACE("(%04X, %08lx, %u);\n", hWaveIn, lpsegWaveInHdr, uSize);
4606
4607     if (lpWaveInHdr == NULL) return MMSYSERR_INVALPARAM;
4608
4609     if (!(lpWaveInHdr->dwFlags & WHDR_PREPARED)) {
4610         return MMSYSERR_NOERROR;
4611     }
4612
4613     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) 
4614         return MMSYSERR_INVALHANDLE;
4615
4616     return MMDRV_Message(wmld, WIDM_UNPREPARE, (DWORD)lpsegWaveInHdr, uSize, FALSE);
4617 }
4618
4619 /**************************************************************************
4620  *                              waveInAddBuffer         [WINMM.@]
4621  */
4622 UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn,
4623                             WAVEHDR* lpWaveInHdr, UINT uSize)
4624 {
4625     LPWINE_MLD          wmld;
4626     
4627     TRACE("(%04X, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
4628
4629     if (lpWaveInHdr == NULL) return MMSYSERR_INVALPARAM;
4630     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) 
4631         return MMSYSERR_INVALHANDLE;
4632
4633     return MMDRV_Message(wmld, WIDM_ADDBUFFER, (DWORD)lpWaveInHdr, uSize, TRUE);
4634 }
4635
4636 /**************************************************************************
4637  *                              waveInAddBuffer         [MMSYSTEM.508]
4638  */
4639 UINT16 WINAPI waveInAddBuffer16(HWAVEIN16 hWaveIn,       /* [in] */
4640                                 WAVEHDR* lpsegWaveInHdr, /* [???] NOTE: SEGPTR */
4641                                 UINT16 uSize)            /* [in] */
4642 {
4643     LPWINE_MLD          wmld;
4644     
4645     TRACE("(%04X, %p, %u);\n", hWaveIn, lpsegWaveInHdr, uSize);
4646
4647     if (lpsegWaveInHdr == NULL) return MMSYSERR_INVALPARAM;
4648     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) 
4649         return MMSYSERR_INVALHANDLE;
4650
4651     return MMDRV_Message(wmld, WIDM_ADDBUFFER, (DWORD)lpsegWaveInHdr, uSize, FALSE);
4652 }
4653
4654 /**************************************************************************
4655  *                              waveInReset             [WINMM.@]
4656  */
4657 UINT WINAPI waveInReset(HWAVEIN hWaveIn)
4658 {
4659     LPWINE_MLD          wmld;
4660
4661     TRACE("(%04X);\n", hWaveIn);
4662
4663     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
4664         return MMSYSERR_INVALHANDLE;
4665
4666     return MMDRV_Message(wmld, WIDM_RESET, 0L, 0L, TRUE);
4667 }
4668
4669 /**************************************************************************
4670  *                              waveInReset             [MMSYSTEM.511]
4671  */
4672 UINT16 WINAPI waveInReset16(HWAVEIN16 hWaveIn16)
4673 {
4674     DWORD       level;
4675     UINT16      ret;
4676
4677     ReleaseThunkLock(&level);
4678     ret = waveInReset(hWaveIn16);
4679     RestoreThunkLock(level);
4680     return ret;
4681 }
4682
4683 /**************************************************************************
4684  *                              waveInStart             [WINMM.@]
4685  */
4686 UINT WINAPI waveInStart(HWAVEIN hWaveIn)
4687 {
4688     LPWINE_MLD          wmld;
4689
4690     TRACE("(%04X);\n", hWaveIn);
4691
4692     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
4693         return MMSYSERR_INVALHANDLE;
4694
4695     return MMDRV_Message(wmld, WIDM_START, 0L, 0L, TRUE);
4696 }
4697
4698 /**************************************************************************
4699  *                              waveInStart             [MMSYSTEM.509]
4700  */
4701 UINT16 WINAPI waveInStart16(HWAVEIN16 hWaveIn16)
4702 {
4703     DWORD       level;
4704     UINT16      ret;
4705
4706     ReleaseThunkLock(&level);
4707     ret = waveInStart(hWaveIn16);
4708     RestoreThunkLock(level);
4709     return ret;
4710 }
4711
4712 /**************************************************************************
4713  *                              waveInStop              [WINMM.@]
4714  */
4715 UINT WINAPI waveInStop(HWAVEIN hWaveIn)
4716 {
4717     LPWINE_MLD          wmld;
4718
4719     TRACE("(%04X);\n", hWaveIn);
4720
4721     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
4722         return MMSYSERR_INVALHANDLE;
4723
4724     return MMDRV_Message(wmld,WIDM_STOP, 0L, 0L, TRUE);
4725 }
4726
4727 /**************************************************************************
4728  *                              waveInStop              [MMSYSTEM.510]
4729  */
4730 UINT16 WINAPI waveInStop16(HWAVEIN16 hWaveIn16)
4731 {
4732     DWORD       level;
4733     UINT16      ret;
4734
4735     ReleaseThunkLock(&level);
4736     ret = waveInStop(hWaveIn16);
4737     RestoreThunkLock(level);
4738     return ret;
4739 }
4740
4741 /**************************************************************************
4742  *                              waveInGetPosition       [WINMM.@]
4743  */
4744 UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,
4745                               UINT uSize)
4746 {
4747     LPWINE_MLD          wmld;
4748
4749     TRACE("(%04X, %p, %u);\n", hWaveIn, lpTime, uSize);
4750
4751     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) 
4752         return MMSYSERR_INVALHANDLE;
4753
4754     return MMDRV_Message(wmld, WIDM_GETPOS, (DWORD)lpTime, uSize, TRUE);
4755 }
4756
4757 /**************************************************************************
4758  *                              waveInGetPosition       [MMSYSTEM.512]
4759  */
4760 UINT16 WINAPI waveInGetPosition16(HWAVEIN16 hWaveIn, LPMMTIME16 lpTime,
4761                                   UINT16 uSize)
4762 {
4763     UINT        ret;
4764     MMTIME      mmt;
4765
4766     mmt.wType = lpTime->wType;
4767     ret = waveInGetPosition(hWaveIn, &mmt, sizeof(mmt));
4768     MMSYSTEM_MMTIME32to16(lpTime, &mmt);
4769     return ret;
4770 }
4771
4772 /**************************************************************************
4773  *                              waveInGetID                     [WINMM.@]
4774  */
4775 UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)
4776 {
4777     LPWINE_MLD          wmld;
4778
4779     TRACE("(%04X, %p);\n", hWaveIn, lpuDeviceID);
4780
4781     if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
4782
4783     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) 
4784         return MMSYSERR_INVALHANDLE;
4785
4786     *lpuDeviceID = wmld->uDeviceID;
4787     return MMSYSERR_NOERROR;
4788 }
4789
4790 /**************************************************************************
4791  *                              waveInGetID                     [MMSYSTEM.513]
4792  */
4793 UINT16 WINAPI waveInGetID16(HWAVEIN16 hWaveIn, UINT16* lpuDeviceID)
4794 {
4795     LPWINE_MLD          wmld;
4796
4797     TRACE("(%04X, %p);\n", hWaveIn, lpuDeviceID);
4798
4799     if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
4800
4801     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) 
4802         return MMSYSERR_INVALHANDLE;
4803
4804     *lpuDeviceID = wmld->uDeviceID;
4805     return MMSYSERR_NOERROR;
4806 }
4807
4808 /**************************************************************************
4809  *                              waveInMessage           [WINMM.@]
4810  */
4811 DWORD WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
4812                            DWORD dwParam1, DWORD dwParam2)
4813 {
4814     LPWINE_MLD          wmld;
4815     
4816     TRACE("(%04x, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
4817
4818     /* from M$ KB */
4819     if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER))
4820         return MMSYSERR_INVALPARAM;
4821
4822     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) 
4823         return MMSYSERR_INVALHANDLE;
4824
4825     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
4826 }
4827
4828 /**************************************************************************
4829  *                              waveInMessage           [MMSYSTEM.514]
4830  */
4831 DWORD WINAPI waveInMessage16(HWAVEIN16 hWaveIn, UINT16 uMessage,
4832                              DWORD dwParam1, DWORD dwParam2)
4833 {
4834     LPWINE_MLD          wmld;
4835
4836     TRACE("(%04x, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
4837
4838     /* from M$ KB */
4839     if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER))
4840         return MMSYSERR_INVALPARAM;
4841
4842     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) 
4843         return MMSYSERR_INVALHANDLE;
4844
4845     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
4846 }
4847
4848 /*#define USE_MM_TSK_WINE*/
4849
4850 /**************************************************************************
4851  *                              mmTaskCreate            [MMSYSTEM.900]
4852  *
4853  * Creates a 16 bit MM task. It's entry point is lpFunc, and it should be
4854  * called upon creation with dwPmt as parameter.
4855  */
4856 HINSTANCE16 WINAPI mmTaskCreate16(SEGPTR spProc, HINSTANCE16 *lphMmTask, DWORD dwPmt)
4857 {
4858     DWORD               *pShowCmd;
4859     LPSTR               cmdline;
4860     LOADPARAMS16*       lp;
4861     HINSTANCE16         ret;
4862     HINSTANCE16         handle;
4863     
4864     TRACE("(%08lx, %p, %08lx);\n", spProc, lphMmTask, dwPmt);
4865     /* This to work requires NE modules to be started with a binary command line
4866      * which is not currently the case. A patch exists but has never been committed.
4867      * A workaround would be to integrate code for mmtask.tsk into Wine, but
4868      * this requires tremendous work (starting with patching tools/build to
4869      * create NE executables (and not only DLLs) for builtins modules.
4870      * EP 99/04/25
4871      */
4872     FIXME("This is currently broken. It will fail\n");
4873
4874     cmdline = SEGPTR_ALLOC(0x0d);
4875     cmdline[0] = 0x0d;
4876     *(LPDWORD)(cmdline + 1) = (DWORD)spProc;
4877     *(LPDWORD)(cmdline + 5) = dwPmt;
4878     *(LPDWORD)(cmdline + 9) = 0;
4879
4880     pShowCmd = SEGPTR_ALLOC(sizeof(DWORD));
4881     *pShowCmd = 0x40002;
4882
4883     lp = (LOADPARAMS16*)HeapAlloc(GetProcessHeap(), 0, sizeof(LOADPARAMS16));
4884     lp->hEnvironment = 0;
4885     lp->cmdLine = SEGPTR_GET(cmdline);
4886     lp->showCmd = SEGPTR_GET(pShowCmd);
4887     lp->reserved = 0;
4888     
4889 #ifndef USE_MM_TSK_WINE
4890     handle = LoadModule16("c:\\windows\\system\\mmtask.tsk", lp);
4891 #else
4892     handle = LoadModule16("mmtask.tsk", lp);
4893 #endif
4894     if (handle < 32) {
4895         ret = (handle) ? 1 : 2;
4896         handle = 0;
4897     } else {
4898         ret = 0;
4899     }
4900     if (lphMmTask)
4901         *lphMmTask = handle;
4902
4903     HeapFree(GetProcessHeap(), 0, lp);
4904     SEGPTR_FREE(pShowCmd);
4905     SEGPTR_FREE(cmdline);
4906
4907     TRACE("=> 0x%04x/%d\n", handle, ret);
4908     return ret;
4909 }
4910
4911 #ifdef USE_MM_TSK_WINE
4912 /* C equivalent to mmtask.tsk binary content */
4913 void    mmTaskEntryPoint16(LPSTR cmdLine, WORD di, WORD si)
4914 {
4915     int len = cmdLine[0x80];
4916
4917     if (len / 2 == 6) {
4918         void    (*fpProc)(DWORD) = MapSL(*((DWORD*)(cmdLine + 1)));
4919         DWORD   dwPmt  = *((DWORD*)(cmdLine + 5));
4920
4921 #if 0
4922         InitTask16(); /* fixme: pmts / from context ? */
4923         InitApp(di);
4924 #endif
4925         if (SetMessageQueue16(0x40)) {
4926             WaitEvent16(0);
4927             if (HIWORD(fpProc)) {
4928                 OldYield16();
4929 /* EPP          StackEnter16(); */
4930                 (fpProc)(dwPmt);
4931             }
4932         }
4933     }
4934     OldYield16();
4935     OldYield16();
4936     OldYield16();
4937     ExitProcess(0);
4938 }
4939 #endif
4940
4941 /**************************************************************************
4942  *                              mmTaskBlock             [MMSYSTEM.902]
4943  */
4944 void    WINAPI  mmTaskBlock16(HINSTANCE16 WINE_UNUSED hInst)
4945 {
4946     MSG         msg;
4947
4948     do {
4949         GetMessageA(&msg, 0, 0, 0);
4950         if (msg.hwnd) {
4951             TranslateMessage(&msg);
4952             DispatchMessageA(&msg);
4953         }
4954     } while (msg.message < 0x3A0);
4955 }
4956
4957 /**************************************************************************
4958  *                              mmTaskSignal            [MMSYSTEM.903]
4959  */
4960 LRESULT WINAPI mmTaskSignal16(HTASK16 ht) 
4961 {
4962     TRACE("(%04x);\n", ht);
4963     return PostAppMessage16(ht, WM_USER, 0, 0);
4964 }
4965
4966 /**************************************************************************
4967  *                              mmGetCurrentTask        [MMSYSTEM.904]
4968  */
4969 HTASK16 WINAPI mmGetCurrentTask16(void)
4970 {
4971     return GetCurrentTask();
4972 }
4973
4974 /**************************************************************************
4975  *                              mmTaskYield             [MMSYSTEM.905]
4976  */
4977 void    WINAPI  mmTaskYield16(void)
4978 {
4979     MSG         msg;
4980
4981     if (PeekMessageA(&msg, 0, 0, 0, 0)) {
4982         K32WOWYield16();
4983     }
4984 }
4985
4986 DWORD   WINAPI  GetProcessFlags(DWORD);
4987
4988 /**************************************************************************
4989  *                              mmThreadCreate          [MMSYSTEM.1120]
4990  *
4991  * undocumented
4992  * Creates a MM thread, calling fpThreadAddr(dwPmt). 
4993  * dwFlags: 
4994  *      bit.0 set means create a 16 bit task instead of thread calling a 16 bit proc
4995  *      bit.1 set means to open a VxD for this thread (unsupported) 
4996  */
4997 LRESULT WINAPI mmThreadCreate16(FARPROC16 fpThreadAddr, LPHANDLE lpHndl, DWORD dwPmt, DWORD dwFlags) 
4998 {
4999     HANDLE16            hndl;
5000     LRESULT             ret;
5001
5002     TRACE("(%p, %p, %08lx, %08lx)!\n", fpThreadAddr, lpHndl, dwPmt, dwFlags);
5003
5004     hndl = GlobalAlloc16(sizeof(WINE_MMTHREAD), GMEM_SHARE|GMEM_ZEROINIT);
5005
5006     if (hndl == 0) {
5007         ret = 2;
5008     } else {
5009         WINE_MMTHREAD*  lpMMThd = MapSL( MAKESEGPTR(hndl, 0) );
5010
5011 #if 0
5012         /* force mmtask routines even if mmthread is required */
5013         /* this will work only if the patch about binary cmd line and NE tasks 
5014          * is committed
5015          */
5016         dwFlags |= 1;
5017 #endif
5018
5019         lpMMThd->dwSignature    = WINE_MMTHREAD_CREATED;
5020         lpMMThd->dwCounter      = 0;
5021         lpMMThd->hThread        = 0;
5022         lpMMThd->dwThreadID     = 0;
5023         lpMMThd->fpThread       = fpThreadAddr;
5024         lpMMThd->dwThreadPmt    = dwPmt;
5025         lpMMThd->dwSignalCount  = 0;
5026         lpMMThd->hEvent         = 0;
5027         lpMMThd->hVxD           = 0;
5028         lpMMThd->dwStatus       = 0;
5029         lpMMThd->dwFlags        = dwFlags;
5030         lpMMThd->hTask          = 0;
5031         
5032         if ((dwFlags & 1) == 0 && (GetProcessFlags(GetCurrentThreadId()) & 8) == 0) {
5033             lpMMThd->hEvent = CreateEventA(0, 0, 1, 0);
5034
5035             TRACE("Let's go crazy... trying new MM thread. lpMMThd=%p\n", lpMMThd);
5036             if (lpMMThd->dwFlags & 2) {
5037                 /* as long as we don't support MM VxD in wine, we don't need 
5038                  * to care about this flag
5039                  */
5040                 /* FIXME("Don't know how to properly open VxD handles\n"); */
5041                 /* lpMMThd->hVxD = OpenVxDHandle(lpMMThd->hEvent); */
5042             }
5043
5044             lpMMThd->hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)WINE_mmThreadEntryPoint, 
5045                                             (LPVOID)(DWORD)hndl, CREATE_SUSPENDED, &lpMMThd->dwThreadID);
5046             if (lpMMThd->hThread == 0) {
5047                 WARN("Couldn't create thread\n");
5048                 /* clean-up(VxDhandle...); devicedirectio... */
5049                 if (lpMMThd->hEvent != 0)
5050                     CloseHandle(lpMMThd->hEvent);
5051                 ret = 2;
5052             } else {
5053                 TRACE("Got a nice thread hndl=0x%04x id=0x%08lx\n", lpMMThd->hThread, lpMMThd->dwThreadID);
5054                 ret = 0;
5055             }
5056         } else {
5057             /* get WINE_mmThreadEntryPoint() 
5058              * 2047 is its ordinal in mmsystem.spec
5059              */
5060             FARPROC16   fp = GetProcAddress16(GetModuleHandle16("MMSYSTEM"), (LPCSTR)2047);
5061
5062             TRACE("farproc seg=0x%08lx lin=%p\n", (DWORD)fp, MapSL((SEGPTR)fp));
5063
5064             ret = (fp == 0) ? 2 : mmTaskCreate16((DWORD)fp, 0, hndl);
5065         }
5066
5067         if (ret == 0) {
5068             if (lpMMThd->hThread && !ResumeThread(lpMMThd->hThread))
5069                 WARN("Couldn't resume thread\n");
5070
5071             while (lpMMThd->dwStatus != 0x10) { /* test also HIWORD of dwStatus */
5072                 UserYield16();
5073             }
5074         }
5075     }
5076
5077     if (ret != 0) {
5078         GlobalFree16(hndl);
5079         hndl = 0;
5080     }
5081
5082     if (lpHndl)
5083         *lpHndl = hndl;
5084
5085     TRACE("ok => %ld\n", ret);
5086     return ret;
5087 }
5088
5089 /**************************************************************************
5090  *                              mmThreadSignal          [MMSYSTEM.1121]
5091  */
5092 void WINAPI mmThreadSignal16(HANDLE16 hndl) 
5093 {
5094     TRACE("(%04x)!\n", hndl);
5095
5096     if (hndl) {
5097         WINE_MMTHREAD*  lpMMThd = MapSL( MAKESEGPTR(hndl, 0) );
5098
5099         lpMMThd->dwCounter++;
5100         if (lpMMThd->hThread != 0) {
5101             InterlockedIncrement(&lpMMThd->dwSignalCount);
5102             SetEvent(lpMMThd->hEvent);
5103         } else {
5104             mmTaskSignal16(lpMMThd->hTask);
5105         }
5106         lpMMThd->dwCounter--;
5107     }
5108 }
5109
5110 /**************************************************************************
5111  *                              MMSYSTEM_ThreadBlock            [internal]
5112  */
5113 static  void    MMSYSTEM_ThreadBlock(WINE_MMTHREAD* lpMMThd)
5114 {
5115     MSG         msg;
5116     DWORD       ret;
5117
5118     if (lpMMThd->dwThreadID != GetCurrentThreadId())
5119         ERR("Not called by thread itself\n");
5120
5121     for (;;) {
5122         ResetEvent(lpMMThd->hEvent);
5123         if (InterlockedDecrement(&lpMMThd->dwSignalCount) >= 0)
5124             break;
5125         InterlockedIncrement(&lpMMThd->dwSignalCount);
5126         
5127         TRACE("S1\n");
5128         
5129         ret = MsgWaitForMultipleObjects(1, &lpMMThd->hEvent, FALSE, INFINITE, QS_ALLINPUT);
5130         switch (ret) {
5131         case WAIT_OBJECT_0:     /* Event */
5132             TRACE("S2.1\n");
5133             break;
5134         case WAIT_OBJECT_0 + 1: /* Msg */
5135             TRACE("S2.2\n");
5136             if (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
5137                 TranslateMessage(&msg);
5138                 DispatchMessageA(&msg);
5139             }
5140             break;
5141         default:
5142             WARN("S2.x unsupported ret val 0x%08lx\n", ret);
5143         }
5144         TRACE("S3\n");
5145     }
5146 }
5147
5148 /**************************************************************************
5149  *                              mmThreadBlock           [MMSYSTEM.1122]
5150  */
5151 void    WINAPI mmThreadBlock16(HANDLE16 hndl) 
5152 {
5153     TRACE("(%04x)!\n", hndl);
5154
5155     if (hndl) {
5156         WINE_MMTHREAD*  lpMMThd = MapSL( MAKESEGPTR(hndl, 0) );
5157         
5158         if (lpMMThd->hThread != 0) {
5159             DWORD       lc;
5160             
5161             ReleaseThunkLock(&lc);
5162             MMSYSTEM_ThreadBlock(lpMMThd);
5163             RestoreThunkLock(lc);
5164         } else {
5165             mmTaskBlock16(lpMMThd->hTask);
5166         }
5167     }
5168     TRACE("done\n");
5169 }
5170
5171 /**************************************************************************
5172  *                              mmThreadIsCurrent       [MMSYSTEM.1123]
5173  */
5174 BOOL16  WINAPI mmThreadIsCurrent16(HANDLE16 hndl) 
5175 {
5176     BOOL16              ret = FALSE;
5177
5178     TRACE("(%04x)!\n", hndl);
5179
5180     if (hndl && mmThreadIsValid16(hndl)) {
5181         WINE_MMTHREAD*  lpMMThd = MapSL( MAKESEGPTR(hndl, 0) );
5182         ret = (GetCurrentThreadId() == lpMMThd->dwThreadID);
5183     }
5184     TRACE("=> %d\n", ret);
5185     return ret;
5186 }
5187
5188 /**************************************************************************
5189  *                              mmThreadIsValid         [MMSYSTEM.1124]
5190  */
5191 BOOL16  WINAPI  mmThreadIsValid16(HANDLE16 hndl)
5192 {
5193     BOOL16              ret = FALSE;
5194
5195     TRACE("(%04x)!\n", hndl);
5196
5197     if (hndl) {
5198         WINE_MMTHREAD*  lpMMThd = MapSL( MAKESEGPTR(hndl, 0) );
5199
5200         if (!IsBadWritePtr(lpMMThd, sizeof(WINE_MMTHREAD)) &&
5201             lpMMThd->dwSignature == WINE_MMTHREAD_CREATED &&
5202             IsTask16(lpMMThd->hTask)) {
5203             lpMMThd->dwCounter++;
5204             if (lpMMThd->hThread != 0) {
5205                 DWORD   dwThreadRet;
5206                 if (GetExitCodeThread(lpMMThd->hThread, &dwThreadRet) &&
5207                     dwThreadRet == STATUS_PENDING) {
5208                     ret = TRUE;
5209                 }
5210             } else {
5211                 ret = TRUE;
5212             }
5213             lpMMThd->dwCounter--;
5214         }
5215     }
5216     TRACE("=> %d\n", ret);
5217     return ret;
5218 }
5219
5220 /**************************************************************************
5221  *                              mmThreadGetTask         [MMSYSTEM.1125]
5222  */
5223 HANDLE16 WINAPI mmThreadGetTask16(HANDLE16 hndl) 
5224 {
5225     HANDLE16    ret = 0;
5226
5227     TRACE("(%04x)\n", hndl);
5228
5229     if (mmThreadIsValid16(hndl)) {
5230         WINE_MMTHREAD*  lpMMThd = MapSL( MAKESEGPTR(hndl, 0) );
5231         ret = lpMMThd->hTask;
5232     }
5233     return ret;
5234 }
5235
5236 /* ### start build ### */
5237 extern LONG CALLBACK MMSYSTEM_CallTo16_long_l    (FARPROC16,LONG);
5238 /* ### stop build ### */
5239
5240 /**************************************************************************
5241  *                              __wine_mmThreadEntryPoint (MMSYSTEM.2047)
5242  */
5243 void WINAPI WINE_mmThreadEntryPoint(DWORD _pmt)
5244 {
5245     HANDLE16            hndl = (HANDLE16)_pmt;
5246     WINE_MMTHREAD*      lpMMThd = MapSL( MAKESEGPTR(hndl, 0) );
5247
5248     TRACE("(%04x %p)\n", hndl, lpMMThd);
5249
5250     lpMMThd->hTask = LOWORD(GetCurrentTask());
5251     TRACE("[10-%08x] setting hTask to 0x%08x\n", lpMMThd->hThread, lpMMThd->hTask);
5252     lpMMThd->dwStatus = 0x10;
5253     MMSYSTEM_ThreadBlock(lpMMThd);
5254     TRACE("[20-%08x]\n", lpMMThd->hThread);
5255     lpMMThd->dwStatus = 0x20;
5256     if (lpMMThd->fpThread) {
5257         MMSYSTEM_CallTo16_long_l(lpMMThd->fpThread, lpMMThd->dwThreadPmt);
5258     }
5259     lpMMThd->dwStatus = 0x30;
5260     TRACE("[30-%08x]\n", lpMMThd->hThread);
5261     while (lpMMThd->dwCounter) {
5262         Sleep(1);
5263         /* K32WOWYield16();*/
5264     }
5265     TRACE("[XX-%08x]\n", lpMMThd->hThread);
5266     /* paranoia */
5267     lpMMThd->dwSignature = WINE_MMTHREAD_DELETED;
5268     /* close lpMMThread->hVxD directIO */
5269     if (lpMMThd->hEvent)
5270         CloseHandle(lpMMThd->hEvent);
5271     GlobalFree16(hndl);
5272     TRACE("done\n");
5273 }
5274
5275 typedef BOOL16 WINAPI (*MMCPLCALLBACK)(HWND, LPCSTR, LPCSTR, LPCSTR);
5276
5277 /**************************************************************************
5278  *                      mmShowMMCPLPropertySheet        [MMSYSTEM.1150]
5279  */
5280 BOOL16  WINAPI  mmShowMMCPLPropertySheet16(HWND hWnd, LPCSTR lpStrDevice, 
5281                                            LPCSTR lpStrTab, LPCSTR lpStrTitle)
5282 {
5283     HANDLE      hndl;
5284     BOOL16      ret = FALSE;
5285
5286     TRACE("(%04x \"%s\" \"%s\" \"%s\")\n", hWnd, lpStrDevice, lpStrTab, lpStrTitle);
5287
5288     hndl = LoadLibraryA("MMSYS.CPL");
5289     if (hndl != 0) {
5290         MMCPLCALLBACK   fp = (MMCPLCALLBACK)GetProcAddress(hndl, "ShowMMCPLPropertySheet");
5291         if (fp != NULL) {
5292             DWORD       lc;
5293             ReleaseThunkLock(&lc);
5294             ret = (fp)(hWnd, lpStrDevice, lpStrTab, lpStrTitle);
5295             RestoreThunkLock(lc);
5296         }
5297         FreeLibrary(hndl);
5298     }
5299     
5300     return ret;
5301 }
5302
5303 /**************************************************************************
5304  *                      StackEnter              [MMSYSTEM.32]
5305  */
5306 void WINAPI StackEnter16(void)
5307 {
5308 #ifdef __i386__
5309     /* mmsystem.dll from Win 95 does only this: so does Wine */
5310     __asm__("stc");
5311 #endif
5312 }
5313
5314 /**************************************************************************
5315  *                      StackLeave              [MMSYSTEM.33]
5316  */
5317 void WINAPI StackLeave16(void)
5318 {
5319 #ifdef __i386__
5320     /* mmsystem.dll from Win 95 does only this: so does Wine */
5321     __asm__("stc");
5322 #endif
5323 }
5324
5325 /**************************************************************************
5326  *                      WMMMidiRunOnce          [MMSYSTEM.8]
5327  */
5328 void WINAPI WMMMidiRunOnce16(void)
5329 {
5330         FIXME("(), stub!\n");
5331 }
5332
5333 /**************************************************************************
5334  *                      OutputDebugStr          [MMSYSTEM.30]
5335  */
5336 void WINAPI OutputDebugStr16(
5337     LPCSTR str) /* [in] The message to be logged and given to the debugger. */
5338 {
5339     OutputDebugStringA( str );
5340 }