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