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