shell32: Updated French translation.
[wine] / dlls / winmm / winmm.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  * WINMM functions
5  *
6  * Copyright 1993      Martin Ayotte
7  *           1998-2002 Eric Pouech
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 /*
25  * Eric POUECH :
26  *      98/9    added Win32 MCI support
27  *      99/4    added midiStream support
28  *      99/9    added support for loadable low level drivers
29  */
30
31 /* TODO
32  *      + it seems that some programs check what's installed in
33  *        registry against the value returned by drivers. Wine is
34  *        currently broken regarding this point.
35  *      + check thread-safeness for MMSYSTEM and WINMM entry points
36  *      + unicode entry points are badly supported (would require
37  *        moving 32 bit drivers as Unicode as they are supposed to be)
38  *      + allow joystick and timer external calls as we do for wave,
39  *        midi, mixer and aux
40  */
41
42 #include <stdio.h>
43 #include <stdarg.h>
44 #include <string.h>
45
46 #define NONAMELESSUNION
47 #define NONAMELESSSTRUCT
48 #include "windef.h"
49 #include "winbase.h"
50 #include "mmsystem.h"
51 #include "winuser.h"
52 #include "winnls.h"
53 #include "winternl.h"
54 #include "winemm.h"
55
56 #include "wine/debug.h"
57
58 WINE_DEFAULT_DEBUG_CHANNEL(winmm);
59
60 void    (WINAPI *pFnReleaseThunkLock)(DWORD*);
61 void    (WINAPI *pFnRestoreThunkLock)(DWORD);
62
63 /* ========================================================================
64  *                   G L O B A L   S E T T I N G S
65  * ========================================================================*/
66
67 HINSTANCE hWinMM32Instance;
68 HANDLE psLastEvent;
69 HANDLE psStopEvent;
70
71 static CRITICAL_SECTION_DEBUG critsect_debug =
72 {
73     0, 0, &WINMM_cs,
74     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
75       0, 0, { (DWORD_PTR)(__FILE__ ": WINMM_cs") }
76 };
77 CRITICAL_SECTION WINMM_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
78
79 /**************************************************************************
80  *                      WINMM_CreateIData                       [internal]
81  */
82 static  BOOL    WINMM_CreateIData(HINSTANCE hInstDLL)
83 {
84     hWinMM32Instance = hInstDLL;
85     psStopEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
86     psLastEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
87     return TRUE;
88 }
89
90 /**************************************************************************
91  *                      WINMM_DeleteIData                       [internal]
92  */
93 static  void WINMM_DeleteIData(void)
94 {
95     TIME_MMTimeStop();
96
97     /* FIXME: should also free content and resources allocated
98      * inside WINMM_IData */
99     CloseHandle(psStopEvent);
100     CloseHandle(psLastEvent);
101     DeleteCriticalSection(&WINMM_cs);
102 }
103
104 /******************************************************************
105  *             WINMM_LoadMMSystem
106  *
107  */
108 static HANDLE (WINAPI *pGetModuleHandle16)(LPCSTR);
109 static DWORD (WINAPI *pLoadLibrary16)(LPCSTR);
110
111 BOOL WINMM_CheckForMMSystem(void)
112 {
113     /* 0 is not checked yet, -1 is not present, 1 is present */
114     static      int    loaded /* = 0 */;
115
116     if (loaded == 0)
117     {
118         HANDLE      h = GetModuleHandleA("kernel32");
119         loaded = -1;
120         if (h)
121         {
122             pGetModuleHandle16 = (void*)GetProcAddress(h, "GetModuleHandle16");
123             pLoadLibrary16 = (void*)GetProcAddress(h, (LPCSTR)35); /* ordinal for LoadLibrary16 */
124             if (pGetModuleHandle16 && pLoadLibrary16 &&
125                 (pGetModuleHandle16("MMSYSTEM.DLL") || pLoadLibrary16("MMSYSTEM.DLL")))
126                 loaded = 1;
127         }
128     }
129     return loaded > 0;
130 }
131
132 /******************************************************************
133  *             WINMM_ErrorToString
134  */
135 const char* WINMM_ErrorToString(MMRESULT error)
136 {
137 #define ERR_TO_STR(dev) case dev: return #dev
138     static char unknown[32];
139     switch (error) {
140     ERR_TO_STR(MMSYSERR_NOERROR);
141     ERR_TO_STR(MMSYSERR_ERROR);
142     ERR_TO_STR(MMSYSERR_BADDEVICEID);
143     ERR_TO_STR(MMSYSERR_NOTENABLED);
144     ERR_TO_STR(MMSYSERR_ALLOCATED);
145     ERR_TO_STR(MMSYSERR_INVALHANDLE);
146     ERR_TO_STR(MMSYSERR_NODRIVER);
147     ERR_TO_STR(MMSYSERR_NOMEM);
148     ERR_TO_STR(MMSYSERR_NOTSUPPORTED);
149     ERR_TO_STR(MMSYSERR_BADERRNUM);
150     ERR_TO_STR(MMSYSERR_INVALFLAG);
151     ERR_TO_STR(MMSYSERR_INVALPARAM);
152     ERR_TO_STR(MMSYSERR_HANDLEBUSY);
153     ERR_TO_STR(MMSYSERR_INVALIDALIAS);
154     ERR_TO_STR(MMSYSERR_BADDB);
155     ERR_TO_STR(MMSYSERR_KEYNOTFOUND);
156     ERR_TO_STR(MMSYSERR_READERROR);
157     ERR_TO_STR(MMSYSERR_WRITEERROR);
158     ERR_TO_STR(MMSYSERR_DELETEERROR);
159     ERR_TO_STR(MMSYSERR_VALNOTFOUND);
160     ERR_TO_STR(MMSYSERR_NODRIVERCB);
161     ERR_TO_STR(WAVERR_BADFORMAT);
162     ERR_TO_STR(WAVERR_STILLPLAYING);
163     ERR_TO_STR(WAVERR_UNPREPARED);
164     ERR_TO_STR(WAVERR_SYNC);
165     }
166     sprintf(unknown, "Unknown(0x%08x)", error);
167     return unknown;
168 #undef ERR_TO_STR
169 }
170
171 /**************************************************************************
172  *              DllMain (WINMM.init)
173  *
174  * WINMM DLL entry point
175  *
176  */
177 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID fImpLoad)
178 {
179     TRACE("%p 0x%x %p\n", hInstDLL, fdwReason, fImpLoad);
180
181     switch (fdwReason) {
182     case DLL_PROCESS_ATTACH:
183         DisableThreadLibraryCalls(hInstDLL);
184
185         if (!WINMM_CreateIData(hInstDLL))
186             return FALSE;
187         if (!MMDRV_Init()) {
188             WINMM_DeleteIData();
189             return FALSE;
190         }
191         break;
192     case DLL_PROCESS_DETACH:
193         /* close all opened MCI drivers */
194         MCI_SendCommand(MCI_ALL_DEVICE_ID, MCI_CLOSE, MCI_WAIT, 0L, TRUE);
195         MMDRV_Exit();
196         /* now unload all remaining drivers... */
197         DRIVER_UnloadAll();
198
199         WINMM_DeleteIData();
200         break;
201     }
202     return TRUE;
203 }
204
205 /**************************************************************************
206  *      Mixer devices. New to Win95
207  */
208
209 /**************************************************************************
210  * find out the real mixer ID depending on hmix (depends on dwFlags)
211  */
212 static UINT MIXER_GetDev(HMIXEROBJ hmix, DWORD dwFlags, LPWINE_MIXER * lplpwm)
213 {
214     LPWINE_MIXER        lpwm = NULL;
215     UINT                uRet = MMSYSERR_NOERROR;
216
217     switch (dwFlags & 0xF0000000ul) {
218     case MIXER_OBJECTF_MIXER:
219         lpwm = (LPWINE_MIXER)MMDRV_Get(hmix, MMDRV_MIXER, TRUE);
220         break;
221     case MIXER_OBJECTF_HMIXER:
222         lpwm = (LPWINE_MIXER)MMDRV_Get(hmix, MMDRV_MIXER, FALSE);
223         break;
224     case MIXER_OBJECTF_WAVEOUT:
225         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEOUT, TRUE,  MMDRV_MIXER);
226         break;
227     case MIXER_OBJECTF_HWAVEOUT:
228         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEOUT, FALSE, MMDRV_MIXER);
229         break;
230     case MIXER_OBJECTF_WAVEIN:
231         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEIN,  TRUE,  MMDRV_MIXER);
232         break;
233     case MIXER_OBJECTF_HWAVEIN:
234         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEIN,  FALSE, MMDRV_MIXER);
235         break;
236     case MIXER_OBJECTF_MIDIOUT:
237         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIOUT, TRUE,  MMDRV_MIXER);
238         break;
239     case MIXER_OBJECTF_HMIDIOUT:
240         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIOUT, FALSE, MMDRV_MIXER);
241         break;
242     case MIXER_OBJECTF_MIDIIN:
243         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIIN,  TRUE,  MMDRV_MIXER);
244         break;
245     case MIXER_OBJECTF_HMIDIIN:
246         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIIN,  FALSE, MMDRV_MIXER);
247         break;
248     case MIXER_OBJECTF_AUX:
249         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_AUX,     TRUE,  MMDRV_MIXER);
250         break;
251     default:
252         WARN("Unsupported flag (%08lx)\n", dwFlags & 0xF0000000ul);
253         lpwm = 0;
254         uRet = MMSYSERR_INVALFLAG;
255         break;
256     }
257     *lplpwm = lpwm;
258     if (lpwm == 0 && uRet == MMSYSERR_NOERROR)
259         uRet = MMSYSERR_INVALPARAM;
260     return uRet;
261 }
262
263 /**************************************************************************
264  *                              mixerGetNumDevs                 [WINMM.@]
265  */
266 UINT WINAPI mixerGetNumDevs(void)
267 {
268     return MMDRV_GetNum(MMDRV_MIXER);
269 }
270
271 /**************************************************************************
272  *                              mixerGetDevCapsA                [WINMM.@]
273  */
274 UINT WINAPI mixerGetDevCapsA(UINT_PTR uDeviceID, LPMIXERCAPSA lpCaps, UINT uSize)
275 {
276     MIXERCAPSW micW;
277     UINT       ret;
278
279     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
280
281     ret = mixerGetDevCapsW(uDeviceID, &micW, sizeof(micW));
282
283     if (ret == MMSYSERR_NOERROR) {
284         MIXERCAPSA micA;
285         micA.wMid           = micW.wMid;
286         micA.wPid           = micW.wPid;
287         micA.vDriverVersion = micW.vDriverVersion;
288         WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,
289                              sizeof(micA.szPname), NULL, NULL );
290         micA.fdwSupport     = micW.fdwSupport;
291         micA.cDestinations  = micW.cDestinations;
292         memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));
293     }
294     return ret;
295 }
296
297 /**************************************************************************
298  *                              mixerGetDevCapsW                [WINMM.@]
299  */
300 UINT WINAPI mixerGetDevCapsW(UINT_PTR uDeviceID, LPMIXERCAPSW lpCaps, UINT uSize)
301 {
302     LPWINE_MLD wmld;
303
304     if (lpCaps == NULL)        return MMSYSERR_INVALPARAM;
305
306     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIXER, TRUE)) == NULL)
307         return MMSYSERR_BADDEVICEID;
308
309     return MMDRV_Message(wmld, MXDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
310 }
311
312 static void CALLBACK MIXER_WCallback(HMIXEROBJ hmx, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam, DWORD_PTR param2)
313 {
314     HWND hWnd = (HWND)dwInstance;
315
316     if (!dwInstance)
317         return;
318
319     PostMessageW(hWnd, uMsg, (WPARAM)hmx, (LPARAM)dwParam);
320 }
321
322 UINT  MIXER_Open(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,
323                  DWORD_PTR dwInstance, DWORD fdwOpen, BOOL bFrom32)
324 {
325     HANDLE              hMix;
326     LPWINE_MLD          wmld;
327     DWORD               dwRet = 0;
328     MIXEROPENDESC       mod;
329
330     TRACE("(%p, %d, %08lx, %08lx, %08x)\n",
331           lphMix, uDeviceID, dwCallback, dwInstance, fdwOpen);
332
333     mod.dwCallback = (DWORD_PTR)MIXER_WCallback;
334     mod.dwInstance = 0;
335
336 /* If callback is a function,
337  * dwCallback contains function pointer
338  * dwInstance private data
339  *
340  * if callback is a window
341  * dwCallback contains a window handle
342  */
343     switch (fdwOpen & CALLBACK_TYPEMASK) {
344     default:
345         return MMSYSERR_INVALFLAG;
346
347     case CALLBACK_NULL:
348         break;
349
350     case CALLBACK_WINDOW:
351         mod.dwInstance = dwCallback;
352         if (!IsWindow((HWND)dwCallback))
353             return MMSYSERR_INVALPARAM;
354         break;
355     }
356
357     wmld = MMDRV_Alloc(sizeof(WINE_MIXER), MMDRV_MIXER, &hMix, &fdwOpen,
358                        &dwCallback, &dwInstance, bFrom32);
359     wmld->uDeviceID = uDeviceID;
360     mod.hmx = (HMIXEROBJ)hMix;
361
362     dwRet = MMDRV_Open(wmld, MXDM_OPEN, (DWORD)&mod, CALLBACK_FUNCTION);
363
364     if (dwRet != MMSYSERR_NOERROR) {
365         MMDRV_Free(hMix, wmld);
366         hMix = 0;
367     }
368     if (lphMix) *lphMix = hMix;
369     TRACE("=> %d hMixer=%p\n", dwRet, hMix);
370
371     return dwRet;
372 }
373
374 /**************************************************************************
375  *                              mixerOpen                       [WINMM.@]
376  */
377 UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,
378                      DWORD_PTR dwInstance, DWORD fdwOpen)
379 {
380     return MIXER_Open(lphMix, uDeviceID, dwCallback, dwInstance, fdwOpen, TRUE);
381 }
382
383 /**************************************************************************
384  *                              mixerClose                      [WINMM.@]
385  */
386 UINT WINAPI mixerClose(HMIXER hMix)
387 {
388     LPWINE_MLD          wmld;
389     DWORD               dwRet;
390
391     TRACE("(%p)\n", hMix);
392
393     if ((wmld = MMDRV_Get(hMix, MMDRV_MIXER, FALSE)) == NULL) return MMSYSERR_INVALHANDLE;
394
395     dwRet = MMDRV_Close(wmld, MXDM_CLOSE);
396     MMDRV_Free(hMix, wmld);
397
398     return dwRet;
399 }
400
401 /**************************************************************************
402  *                              mixerGetID                      [WINMM.@]
403  */
404 UINT WINAPI mixerGetID(HMIXEROBJ hmix, LPUINT lpid, DWORD fdwID)
405 {
406     LPWINE_MIXER        lpwm;
407     UINT                uRet = MMSYSERR_NOERROR;
408
409     TRACE("(%p %p %08x)\n", hmix, lpid, fdwID);
410
411     if ((uRet = MIXER_GetDev(hmix, fdwID, &lpwm)) != MMSYSERR_NOERROR)
412         return uRet;
413
414     if (lpid)
415       *lpid = lpwm->mld.uDeviceID;
416
417     return uRet;
418 }
419
420 /**************************************************************************
421  *                              mixerGetControlDetailsW         [WINMM.@]
422  */
423 UINT WINAPI mixerGetControlDetailsW(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdW,
424                                     DWORD fdwDetails)
425 {
426     LPWINE_MIXER        lpwm;
427     UINT                uRet = MMSYSERR_NOERROR;
428
429     TRACE("(%p, %p, %08x)\n", hmix, lpmcdW, fdwDetails);
430
431     if ((uRet = MIXER_GetDev(hmix, fdwDetails, &lpwm)) != MMSYSERR_NOERROR)
432         return uRet;
433
434     if (lpmcdW == NULL || lpmcdW->cbStruct != sizeof(*lpmcdW))
435         return MMSYSERR_INVALPARAM;
436
437     return MMDRV_Message(&lpwm->mld, MXDM_GETCONTROLDETAILS, (DWORD_PTR)lpmcdW,
438                          fdwDetails, TRUE);
439 }
440
441 /**************************************************************************
442  *                              mixerGetControlDetailsA [WINMM.@]
443  */
444 UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
445                                     DWORD fdwDetails)
446 {
447     DWORD                       ret = MMSYSERR_NOTENABLED;
448
449     TRACE("(%p, %p, %08x)\n", hmix, lpmcdA, fdwDetails);
450
451     if (lpmcdA == NULL || lpmcdA->cbStruct != sizeof(*lpmcdA))
452         return MMSYSERR_INVALPARAM;
453
454     switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
455     case MIXER_GETCONTROLDETAILSF_VALUE:
456         /* can savely use A structure as it is, no string inside */
457         ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
458         break;
459     case MIXER_GETCONTROLDETAILSF_LISTTEXT:
460         {
461             MIXERCONTROLDETAILS_LISTTEXTA *pDetailsA = (MIXERCONTROLDETAILS_LISTTEXTA *)lpmcdA->paDetails;
462             MIXERCONTROLDETAILS_LISTTEXTW *pDetailsW;
463             int size = max(1, lpmcdA->cChannels) * sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
464             unsigned int i;
465
466             if (lpmcdA->u.cMultipleItems != 0) {
467                 size *= lpmcdA->u.cMultipleItems;
468             }
469             pDetailsW = HeapAlloc(GetProcessHeap(), 0, size);
470             lpmcdA->paDetails = pDetailsW;
471             lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
472             /* set up lpmcd->paDetails */
473             ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
474             /* copy from lpmcd->paDetails back to paDetailsW; */
475             if (ret == MMSYSERR_NOERROR) {
476                 for (i = 0; i < lpmcdA->u.cMultipleItems * lpmcdA->cChannels; i++) {
477                     pDetailsA->dwParam1 = pDetailsW->dwParam1;
478                     pDetailsA->dwParam2 = pDetailsW->dwParam2;
479                     WideCharToMultiByte( CP_ACP, 0, pDetailsW->szName, -1,
480                                          pDetailsA->szName,
481                                          sizeof(pDetailsA->szName), NULL, NULL );
482                     pDetailsA++;
483                     pDetailsW++;
484                 }
485                 pDetailsA -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
486                 pDetailsW -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
487             }
488             HeapFree(GetProcessHeap(), 0, pDetailsW);
489             lpmcdA->paDetails = pDetailsA;
490             lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTA);
491         }
492         break;
493     default:
494         ERR("Unsupported fdwDetails=0x%08x\n", fdwDetails);
495     }
496
497     return ret;
498 }
499
500 /**************************************************************************
501  *                              mixerGetLineControlsA   [WINMM.@]
502  */
503 UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,
504                                   DWORD fdwControls)
505 {
506     MIXERLINECONTROLSW  mlcW;
507     DWORD               ret;
508     unsigned int        i;
509
510     TRACE("(%p, %p, %08x)\n", hmix, lpmlcA, fdwControls);
511
512     if (lpmlcA == NULL || lpmlcA->cbStruct != sizeof(*lpmlcA) ||
513         lpmlcA->cbmxctrl != sizeof(MIXERCONTROLA))
514         return MMSYSERR_INVALPARAM;
515
516     mlcW.cbStruct = sizeof(mlcW);
517     mlcW.dwLineID = lpmlcA->dwLineID;
518     mlcW.u.dwControlID = lpmlcA->u.dwControlID;
519     mlcW.u.dwControlType = lpmlcA->u.dwControlType;
520
521     /* Debugging on Windows shows for MIXER_GETLINECONTROLSF_ONEBYTYPE only,
522        the control count is assumed to be 1 - This is relied upon by a game,
523        "Dynomite Deluze"                                                    */
524     if (MIXER_GETLINECONTROLSF_ONEBYTYPE == fdwControls) {
525         mlcW.cControls = 1;
526     } else {
527         mlcW.cControls = lpmlcA->cControls;
528     }
529     mlcW.cbmxctrl = sizeof(MIXERCONTROLW);
530     mlcW.pamxctrl = HeapAlloc(GetProcessHeap(), 0,
531                               mlcW.cControls * mlcW.cbmxctrl);
532
533     ret = mixerGetLineControlsW(hmix, &mlcW, fdwControls);
534
535     if (ret == MMSYSERR_NOERROR) {
536         lpmlcA->dwLineID = mlcW.dwLineID;
537         lpmlcA->u.dwControlID = mlcW.u.dwControlID;
538         lpmlcA->u.dwControlType = mlcW.u.dwControlType;
539         lpmlcA->cControls = mlcW.cControls;
540
541         for (i = 0; i < mlcW.cControls; i++) {
542             lpmlcA->pamxctrl[i].cbStruct = sizeof(MIXERCONTROLA);
543             lpmlcA->pamxctrl[i].dwControlID = mlcW.pamxctrl[i].dwControlID;
544             lpmlcA->pamxctrl[i].dwControlType = mlcW.pamxctrl[i].dwControlType;
545             lpmlcA->pamxctrl[i].fdwControl = mlcW.pamxctrl[i].fdwControl;
546             lpmlcA->pamxctrl[i].cMultipleItems = mlcW.pamxctrl[i].cMultipleItems;
547             WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szShortName, -1,
548                                  lpmlcA->pamxctrl[i].szShortName,
549                                  sizeof(lpmlcA->pamxctrl[i].szShortName), NULL, NULL );
550             WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szName, -1,
551                                  lpmlcA->pamxctrl[i].szName,
552                                  sizeof(lpmlcA->pamxctrl[i].szName), NULL, NULL );
553             /* sizeof(lpmlcA->pamxctrl[i].Bounds) ==
554              * sizeof(mlcW.pamxctrl[i].Bounds) */
555             memcpy(&lpmlcA->pamxctrl[i].Bounds, &mlcW.pamxctrl[i].Bounds,
556                    sizeof(mlcW.pamxctrl[i].Bounds));
557             /* sizeof(lpmlcA->pamxctrl[i].Metrics) ==
558              * sizeof(mlcW.pamxctrl[i].Metrics) */
559             memcpy(&lpmlcA->pamxctrl[i].Metrics, &mlcW.pamxctrl[i].Metrics,
560                    sizeof(mlcW.pamxctrl[i].Metrics));
561         }
562     }
563
564     HeapFree(GetProcessHeap(), 0, mlcW.pamxctrl);
565
566     return ret;
567 }
568
569 /**************************************************************************
570  *                              mixerGetLineControlsW           [WINMM.@]
571  */
572 UINT WINAPI mixerGetLineControlsW(HMIXEROBJ hmix, LPMIXERLINECONTROLSW lpmlcW,
573                                   DWORD fdwControls)
574 {
575     LPWINE_MIXER        lpwm;
576     UINT                uRet = MMSYSERR_NOERROR;
577
578     TRACE("(%p, %p, %08x)\n", hmix, lpmlcW, fdwControls);
579
580     if ((uRet = MIXER_GetDev(hmix, fdwControls, &lpwm)) != MMSYSERR_NOERROR)
581         return uRet;
582
583     if (lpmlcW == NULL || lpmlcW->cbStruct != sizeof(*lpmlcW))
584         return MMSYSERR_INVALPARAM;
585
586     return MMDRV_Message(&lpwm->mld, MXDM_GETLINECONTROLS, (DWORD_PTR)lpmlcW,
587                          fdwControls, TRUE);
588 }
589
590 /**************************************************************************
591  *                              mixerGetLineInfoW               [WINMM.@]
592  */
593 UINT WINAPI mixerGetLineInfoW(HMIXEROBJ hmix, LPMIXERLINEW lpmliW, DWORD fdwInfo)
594 {
595     LPWINE_MIXER        lpwm;
596     UINT                uRet = MMSYSERR_NOERROR;
597
598     TRACE("(%p, %p, %08x)\n", hmix, lpmliW, fdwInfo);
599
600     if ((uRet = MIXER_GetDev(hmix, fdwInfo, &lpwm)) != MMSYSERR_NOERROR)
601         return uRet;
602
603     return MMDRV_Message(&lpwm->mld, MXDM_GETLINEINFO, (DWORD_PTR)lpmliW,
604                          fdwInfo, TRUE);
605 }
606
607 /**************************************************************************
608  *                              mixerGetLineInfoA               [WINMM.@]
609  */
610 UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliA,
611                               DWORD fdwInfo)
612 {
613     MIXERLINEW          mliW;
614     UINT                ret;
615
616     TRACE("(%p, %p, %08x)\n", hmix, lpmliA, fdwInfo);
617
618     if (lpmliA == NULL || lpmliA->cbStruct != sizeof(*lpmliA))
619         return MMSYSERR_INVALPARAM;
620
621     mliW.cbStruct = sizeof(mliW);
622     switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {
623     case MIXER_GETLINEINFOF_COMPONENTTYPE:
624         mliW.dwComponentType = lpmliA->dwComponentType;
625         break;
626     case MIXER_GETLINEINFOF_DESTINATION:
627         mliW.dwDestination = lpmliA->dwDestination;
628         break;
629     case MIXER_GETLINEINFOF_LINEID:
630         mliW.dwLineID = lpmliA->dwLineID;
631         break;
632     case MIXER_GETLINEINFOF_SOURCE:
633         mliW.dwDestination = lpmliA->dwDestination;
634         mliW.dwSource = lpmliA->dwSource;
635         break;
636     case MIXER_GETLINEINFOF_TARGETTYPE:
637         mliW.Target.dwType = lpmliA->Target.dwType;
638         mliW.Target.wMid = lpmliA->Target.wMid;
639         mliW.Target.wPid = lpmliA->Target.wPid;
640         mliW.Target.vDriverVersion = lpmliA->Target.vDriverVersion;
641         MultiByteToWideChar( CP_ACP, 0, lpmliA->Target.szPname, -1, mliW.Target.szPname, sizeof(mliW.Target.szPname)/sizeof(WCHAR));
642         break;
643     default:
644         WARN("Unsupported fdwControls=0x%08x\n", fdwInfo);
645         return MMSYSERR_INVALFLAG;
646     }
647
648     ret = mixerGetLineInfoW(hmix, &mliW, fdwInfo);
649
650     lpmliA->dwDestination = mliW.dwDestination;
651     lpmliA->dwSource = mliW.dwSource;
652     lpmliA->dwLineID = mliW.dwLineID;
653     lpmliA->fdwLine = mliW.fdwLine;
654     lpmliA->dwUser = mliW.dwUser;
655     lpmliA->dwComponentType = mliW.dwComponentType;
656     lpmliA->cChannels = mliW.cChannels;
657     lpmliA->cConnections = mliW.cConnections;
658     lpmliA->cControls = mliW.cControls;
659     WideCharToMultiByte( CP_ACP, 0, mliW.szShortName, -1, lpmliA->szShortName,
660                          sizeof(lpmliA->szShortName), NULL, NULL);
661     WideCharToMultiByte( CP_ACP, 0, mliW.szName, -1, lpmliA->szName,
662                          sizeof(lpmliA->szName), NULL, NULL );
663     lpmliA->Target.dwType = mliW.Target.dwType;
664     lpmliA->Target.dwDeviceID = mliW.Target.dwDeviceID;
665     lpmliA->Target.wMid = mliW.Target.wMid;
666     lpmliA->Target.wPid = mliW.Target.wPid;
667     lpmliA->Target.vDriverVersion = mliW.Target.vDriverVersion;
668     WideCharToMultiByte( CP_ACP, 0, mliW.Target.szPname, -1, lpmliA->Target.szPname,
669                          sizeof(lpmliA->Target.szPname), NULL, NULL );
670
671     return ret;
672 }
673
674 /**************************************************************************
675  *                              mixerSetControlDetails  [WINMM.@]
676  */
677 UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd,
678                                    DWORD fdwDetails)
679 {
680     LPWINE_MIXER        lpwm;
681     UINT                uRet = MMSYSERR_NOERROR;
682
683     TRACE("(%p, %p, %08x)\n", hmix, lpmcd, fdwDetails);
684
685     if ((uRet = MIXER_GetDev(hmix, fdwDetails, &lpwm)) != MMSYSERR_NOERROR)
686         return uRet;
687
688     return MMDRV_Message(&lpwm->mld, MXDM_SETCONTROLDETAILS, (DWORD_PTR)lpmcd,
689                          fdwDetails, TRUE);
690 }
691
692 /**************************************************************************
693  *                              mixerMessage            [WINMM.@]
694  */
695 DWORD WINAPI mixerMessage(HMIXER hmix, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
696 {
697     LPWINE_MLD          wmld;
698
699     TRACE("(%p, %d, %08lx, %08lx): semi-stub?\n",
700           hmix, uMsg, dwParam1, dwParam2);
701
702     if ((wmld = MMDRV_Get(hmix, MMDRV_MIXER, FALSE)) == NULL)
703         return MMSYSERR_INVALHANDLE;
704
705     return MMDRV_Message(wmld, uMsg, dwParam1, dwParam2, TRUE);
706 }
707
708 /**************************************************************************
709  *                              auxGetNumDevs           [WINMM.@]
710  */
711 UINT WINAPI auxGetNumDevs(void)
712 {
713     return MMDRV_GetNum(MMDRV_AUX);
714 }
715
716 /**************************************************************************
717  *                              auxGetDevCapsW          [WINMM.@]
718  */
719 UINT WINAPI auxGetDevCapsW(UINT_PTR uDeviceID, LPAUXCAPSW lpCaps, UINT uSize)
720 {
721     LPWINE_MLD          wmld;
722
723     TRACE("(%04lX, %p, %d) !\n", uDeviceID, lpCaps, uSize);
724
725     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
726
727     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
728         return MMSYSERR_INVALHANDLE;
729     return MMDRV_Message(wmld, AUXDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
730 }
731
732 /**************************************************************************
733  *                              auxGetDevCapsA          [WINMM.@]
734  */
735 UINT WINAPI auxGetDevCapsA(UINT_PTR uDeviceID, LPAUXCAPSA lpCaps, UINT uSize)
736 {
737     AUXCAPSW    acW;
738     UINT        ret;
739
740     if (lpCaps == NULL)        return MMSYSERR_INVALPARAM;
741
742     ret = auxGetDevCapsW(uDeviceID, &acW, sizeof(acW));
743
744     if (ret == MMSYSERR_NOERROR) {
745         AUXCAPSA acA;
746         acA.wMid           = acW.wMid;
747         acA.wPid           = acW.wPid;
748         acA.vDriverVersion = acW.vDriverVersion;
749         WideCharToMultiByte( CP_ACP, 0, acW.szPname, -1, acA.szPname,
750                              sizeof(acA.szPname), NULL, NULL );
751         acA.wTechnology    = acW.wTechnology;
752         acA.dwSupport      = acW.dwSupport;
753         memcpy(lpCaps, &acA, min(uSize, sizeof(acA)));
754     }
755     return ret;
756 }
757
758 /**************************************************************************
759  *                              auxGetVolume            [WINMM.@]
760  */
761 UINT WINAPI auxGetVolume(UINT uDeviceID, DWORD* lpdwVolume)
762 {
763     LPWINE_MLD          wmld;
764
765     TRACE("(%04X, %p) !\n", uDeviceID, lpdwVolume);
766
767     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
768         return MMSYSERR_INVALHANDLE;
769     return MMDRV_Message(wmld, AUXDM_GETVOLUME, (DWORD_PTR)lpdwVolume, 0L, TRUE);
770 }
771
772 /**************************************************************************
773  *                              auxSetVolume            [WINMM.@]
774  */
775 UINT WINAPI auxSetVolume(UINT uDeviceID, DWORD dwVolume)
776 {
777     LPWINE_MLD          wmld;
778
779     TRACE("(%04X, %u) !\n", uDeviceID, dwVolume);
780
781     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
782         return MMSYSERR_INVALHANDLE;
783     return MMDRV_Message(wmld, AUXDM_SETVOLUME, dwVolume, 0L, TRUE);
784 }
785
786 /**************************************************************************
787  *                              auxOutMessage           [WINMM.@]
788  */
789 UINT WINAPI auxOutMessage(UINT uDeviceID, UINT uMessage, DWORD_PTR dw1, DWORD_PTR dw2)
790 {
791     LPWINE_MLD          wmld;
792
793     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
794         return MMSYSERR_INVALHANDLE;
795
796     return MMDRV_Message(wmld, uMessage, dw1, dw2, TRUE);
797 }
798
799 /**************************************************************************
800  *                              midiOutGetNumDevs       [WINMM.@]
801  */
802 UINT WINAPI midiOutGetNumDevs(void)
803 {
804     return MMDRV_GetNum(MMDRV_MIDIOUT);
805 }
806
807 /**************************************************************************
808  *                              midiOutGetDevCapsW      [WINMM.@]
809  */
810 UINT WINAPI midiOutGetDevCapsW(UINT_PTR uDeviceID, LPMIDIOUTCAPSW lpCaps,
811                                UINT uSize)
812 {
813     LPWINE_MLD  wmld;
814
815     TRACE("(%lu, %p, %u);\n", uDeviceID, lpCaps, uSize);
816
817     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
818
819     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIDIOUT, TRUE)) == NULL)
820         return MMSYSERR_INVALHANDLE;
821
822     return MMDRV_Message(wmld, MODM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
823 }
824
825 /**************************************************************************
826  *                              midiOutGetDevCapsA      [WINMM.@]
827  */
828 UINT WINAPI midiOutGetDevCapsA(UINT_PTR uDeviceID, LPMIDIOUTCAPSA lpCaps,
829                                UINT uSize)
830 {
831     MIDIOUTCAPSW        mocW;
832     UINT                ret;
833
834     if (lpCaps == NULL)        return MMSYSERR_INVALPARAM;
835
836     ret = midiOutGetDevCapsW(uDeviceID, &mocW, sizeof(mocW));
837
838     if (ret == MMSYSERR_NOERROR) {
839         MIDIOUTCAPSA mocA;
840         mocA.wMid               = mocW.wMid;
841         mocA.wPid               = mocW.wPid;
842         mocA.vDriverVersion     = mocW.vDriverVersion;
843         WideCharToMultiByte( CP_ACP, 0, mocW.szPname, -1, mocA.szPname,
844                              sizeof(mocA.szPname), NULL, NULL );
845         mocA.wTechnology        = mocW.wTechnology;
846         mocA.wVoices            = mocW.wVoices;
847         mocA.wNotes             = mocW.wNotes;
848         mocA.wChannelMask       = mocW.wChannelMask;
849         mocA.dwSupport          = mocW.dwSupport;
850         memcpy(lpCaps, &mocA, min(uSize, sizeof(mocA)));
851     }
852     return ret;
853 }
854
855 /**************************************************************************
856  *                              midiOutGetErrorTextA    [WINMM.@]
857  *                              midiInGetErrorTextA     [WINMM.@]
858  */
859 UINT WINAPI midiOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
860 {
861     UINT        ret;
862
863     if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
864     else if (uSize == 0) ret = MMSYSERR_NOERROR;
865     else
866     {
867         LPWSTR  xstr = HeapAlloc(GetProcessHeap(), 0, uSize * sizeof(WCHAR));
868         if (!xstr) ret = MMSYSERR_NOMEM;
869         else
870         {
871             ret = midiOutGetErrorTextW(uError, xstr, uSize);
872             if (ret == MMSYSERR_NOERROR)
873                 WideCharToMultiByte(CP_ACP, 0, xstr, -1, lpText, uSize, NULL, NULL);
874             HeapFree(GetProcessHeap(), 0, xstr);
875         }
876     }
877     return ret;
878 }
879
880 /**************************************************************************
881  *                              midiOutGetErrorTextW    [WINMM.@]
882  *                              midiInGetErrorTextW     [WINMM.@]
883  */
884 UINT WINAPI midiOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
885 {
886     UINT        ret = MMSYSERR_BADERRNUM;
887
888     if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
889     else if (uSize == 0) ret = MMSYSERR_NOERROR;
890     else if (
891                /* test has been removed 'coz MMSYSERR_BASE is 0, and gcc did emit
892                 * a warning for the test was always true */
893                (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||
894                (uError >= MIDIERR_BASE  && uError <= MIDIERR_LASTERROR)) {
895         if (LoadStringW(hWinMM32Instance, uError, lpText, uSize) > 0) {
896             ret = MMSYSERR_NOERROR;
897         }
898     }
899     return ret;
900 }
901
902 /**************************************************************************
903  *                              MIDI_OutAlloc                   [internal]
904  */
905 static  LPWINE_MIDI     MIDI_OutAlloc(HMIDIOUT* lphMidiOut, DWORD_PTR* lpdwCallback,
906                                       DWORD_PTR* lpdwInstance, LPDWORD lpdwFlags,
907                                       DWORD cIDs, MIDIOPENSTRMID* lpIDs, BOOL bFrom32)
908 {
909     HANDLE              hMidiOut;
910     LPWINE_MIDI         lpwm;
911     UINT                size;
912
913     size = sizeof(WINE_MIDI) + (cIDs ? (cIDs-1) : 0) * sizeof(MIDIOPENSTRMID);
914
915     lpwm = (LPWINE_MIDI)MMDRV_Alloc(size, MMDRV_MIDIOUT, &hMidiOut, lpdwFlags,
916                                     lpdwCallback, lpdwInstance, bFrom32);
917
918     if (lphMidiOut != NULL)
919         *lphMidiOut = hMidiOut;
920
921     if (lpwm) {
922         lpwm->mod.hMidi = (HMIDI) hMidiOut;
923         lpwm->mod.dwCallback = *lpdwCallback;
924         lpwm->mod.dwInstance = *lpdwInstance;
925         lpwm->mod.dnDevNode = 0;
926         lpwm->mod.cIds = cIDs;
927         if (cIDs)
928             memcpy(&(lpwm->mod.rgIds), lpIDs, cIDs * sizeof(MIDIOPENSTRMID));
929     }
930     return lpwm;
931 }
932
933 UINT MIDI_OutOpen(LPHMIDIOUT lphMidiOut, UINT uDeviceID, DWORD_PTR dwCallback,
934                   DWORD_PTR dwInstance, DWORD dwFlags, BOOL bFrom32)
935 {
936     HMIDIOUT            hMidiOut;
937     LPWINE_MIDI         lpwm;
938     UINT                dwRet = 0;
939
940     TRACE("(%p, %d, %08lX, %08lX, %08X);\n",
941           lphMidiOut, uDeviceID, dwCallback, dwInstance, dwFlags);
942
943     if (lphMidiOut != NULL) *lphMidiOut = 0;
944
945     lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &dwFlags,
946                          0, NULL, bFrom32);
947
948     if (lpwm == NULL)
949         return MMSYSERR_NOMEM;
950
951     lpwm->mld.uDeviceID = uDeviceID;
952
953     dwRet = MMDRV_Open((LPWINE_MLD)lpwm, MODM_OPEN, (DWORD)&lpwm->mod, dwFlags);
954
955     if (dwRet != MMSYSERR_NOERROR) {
956         MMDRV_Free(hMidiOut, (LPWINE_MLD)lpwm);
957         hMidiOut = 0;
958     }
959
960     if (lphMidiOut) *lphMidiOut = hMidiOut;
961     TRACE("=> %d hMidi=%p\n", dwRet, hMidiOut);
962
963     return dwRet;
964 }
965
966 /**************************************************************************
967  *                              midiOutOpen             [WINMM.@]
968  */
969 UINT WINAPI midiOutOpen(LPHMIDIOUT lphMidiOut, UINT uDeviceID,
970                        DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD dwFlags)
971 {
972     return MIDI_OutOpen(lphMidiOut, uDeviceID, dwCallback, dwInstance, dwFlags, TRUE);
973 }
974
975 /**************************************************************************
976  *                              midiOutClose            [WINMM.@]
977  */
978 UINT WINAPI midiOutClose(HMIDIOUT hMidiOut)
979 {
980     LPWINE_MLD          wmld;
981     DWORD               dwRet;
982
983     TRACE("(%p)\n", hMidiOut);
984
985     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
986         return MMSYSERR_INVALHANDLE;
987
988     dwRet = MMDRV_Close(wmld, MODM_CLOSE);
989     MMDRV_Free(hMidiOut, wmld);
990
991     return dwRet;
992 }
993
994 /**************************************************************************
995  *                              midiOutPrepareHeader    [WINMM.@]
996  */
997 UINT WINAPI midiOutPrepareHeader(HMIDIOUT hMidiOut,
998                                  MIDIHDR* lpMidiOutHdr, UINT uSize)
999 {
1000     LPWINE_MLD          wmld;
1001
1002     TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
1003
1004     if (lpMidiOutHdr == NULL || uSize < sizeof (MIDIHDR))
1005         return MMSYSERR_INVALPARAM;
1006
1007     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1008         return MMSYSERR_INVALHANDLE;
1009
1010     return MMDRV_Message(wmld, MODM_PREPARE, (DWORD_PTR)lpMidiOutHdr, uSize, TRUE);
1011 }
1012
1013 /**************************************************************************
1014  *                              midiOutUnprepareHeader  [WINMM.@]
1015  */
1016 UINT WINAPI midiOutUnprepareHeader(HMIDIOUT hMidiOut,
1017                                    MIDIHDR* lpMidiOutHdr, UINT uSize)
1018 {
1019     LPWINE_MLD          wmld;
1020
1021     TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
1022
1023     if (lpMidiOutHdr == NULL || uSize < sizeof (MIDIHDR))
1024         return MMSYSERR_INVALPARAM;
1025
1026     if (!(lpMidiOutHdr->dwFlags & MHDR_PREPARED)) {
1027         return MMSYSERR_NOERROR;
1028     }
1029
1030     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1031         return MMSYSERR_INVALHANDLE;
1032
1033     return MMDRV_Message(wmld, MODM_UNPREPARE, (DWORD_PTR)lpMidiOutHdr, uSize, TRUE);
1034 }
1035
1036 /**************************************************************************
1037  *                              midiOutShortMsg         [WINMM.@]
1038  */
1039 UINT WINAPI midiOutShortMsg(HMIDIOUT hMidiOut, DWORD dwMsg)
1040 {
1041     LPWINE_MLD          wmld;
1042
1043     TRACE("(%p, %08X)\n", hMidiOut, dwMsg);
1044
1045     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1046         return MMSYSERR_INVALHANDLE;
1047
1048     return MMDRV_Message(wmld, MODM_DATA, dwMsg, 0L, TRUE);
1049 }
1050
1051 /**************************************************************************
1052  *                              midiOutLongMsg          [WINMM.@]
1053  */
1054 UINT WINAPI midiOutLongMsg(HMIDIOUT hMidiOut,
1055                            MIDIHDR* lpMidiOutHdr, UINT uSize)
1056 {
1057     LPWINE_MLD          wmld;
1058
1059     TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
1060
1061     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1062         return MMSYSERR_INVALHANDLE;
1063
1064     return MMDRV_Message(wmld, MODM_LONGDATA, (DWORD_PTR)lpMidiOutHdr, uSize, TRUE);
1065 }
1066
1067 /**************************************************************************
1068  *                              midiOutReset            [WINMM.@]
1069  */
1070 UINT WINAPI midiOutReset(HMIDIOUT hMidiOut)
1071 {
1072     LPWINE_MLD          wmld;
1073
1074     TRACE("(%p)\n", hMidiOut);
1075
1076     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1077         return MMSYSERR_INVALHANDLE;
1078
1079     return MMDRV_Message(wmld, MODM_RESET, 0L, 0L, TRUE);
1080 }
1081
1082 /**************************************************************************
1083  *                              midiOutGetVolume        [WINMM.@]
1084  */
1085 UINT WINAPI midiOutGetVolume(HMIDIOUT hMidiOut, DWORD* lpdwVolume)
1086 {
1087     LPWINE_MLD          wmld;
1088
1089     TRACE("(%p, %p);\n", hMidiOut, lpdwVolume);
1090
1091     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) == NULL)
1092         return MMSYSERR_INVALHANDLE;
1093
1094     return MMDRV_Message(wmld, MODM_GETVOLUME, (DWORD_PTR)lpdwVolume, 0L, TRUE);
1095 }
1096
1097 /**************************************************************************
1098  *                              midiOutSetVolume        [WINMM.@]
1099  */
1100 UINT WINAPI midiOutSetVolume(HMIDIOUT hMidiOut, DWORD dwVolume)
1101 {
1102     LPWINE_MLD          wmld;
1103
1104     TRACE("(%p, %d);\n", hMidiOut, dwVolume);
1105
1106     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) == NULL)
1107         return MMSYSERR_INVALHANDLE;
1108
1109     return MMDRV_Message(wmld, MODM_SETVOLUME, dwVolume, 0L, TRUE);
1110 }
1111
1112 /**************************************************************************
1113  *                              midiOutCachePatches             [WINMM.@]
1114  */
1115 UINT WINAPI midiOutCachePatches(HMIDIOUT hMidiOut, UINT uBank,
1116                                 WORD* lpwPatchArray, UINT uFlags)
1117 {
1118     /* not really necessary to support this */
1119     FIXME("not supported yet\n");
1120     return MMSYSERR_NOTSUPPORTED;
1121 }
1122
1123 /**************************************************************************
1124  *                              midiOutCacheDrumPatches [WINMM.@]
1125  */
1126 UINT WINAPI midiOutCacheDrumPatches(HMIDIOUT hMidiOut, UINT uPatch,
1127                                     WORD* lpwKeyArray, UINT uFlags)
1128 {
1129     FIXME("not supported yet\n");
1130     return MMSYSERR_NOTSUPPORTED;
1131 }
1132
1133 /**************************************************************************
1134  *                              midiOutGetID            [WINMM.@]
1135  */
1136 UINT WINAPI midiOutGetID(HMIDIOUT hMidiOut, UINT* lpuDeviceID)
1137 {
1138     LPWINE_MLD          wmld;
1139
1140     TRACE("(%p, %p)\n", hMidiOut, lpuDeviceID);
1141
1142     if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
1143     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1144         return MMSYSERR_INVALHANDLE;
1145
1146     *lpuDeviceID = wmld->uDeviceID;
1147     return MMSYSERR_NOERROR;
1148 }
1149
1150 /**************************************************************************
1151  *                              midiOutMessage          [WINMM.@]
1152  */
1153 UINT WINAPI midiOutMessage(HMIDIOUT hMidiOut, UINT uMessage,
1154                            DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1155 {
1156     LPWINE_MLD          wmld;
1157
1158     TRACE("(%p, %04X, %08lX, %08lX)\n", hMidiOut, uMessage, dwParam1, dwParam2);
1159
1160     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL) {
1161         /* HACK... */
1162         if (uMessage == 0x0001) {
1163             *(LPDWORD)dwParam1 = 1;
1164             return 0;
1165         }
1166         if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) != NULL) {
1167             return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
1168         }
1169         return MMSYSERR_INVALHANDLE;
1170     }
1171
1172     switch (uMessage) {
1173     case MODM_OPEN:
1174     case MODM_CLOSE:
1175         FIXME("can't handle OPEN or CLOSE message!\n");
1176         return MMSYSERR_NOTSUPPORTED;
1177     }
1178     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
1179 }
1180
1181 /**************************************************************************
1182  *                              midiInGetNumDevs        [WINMM.@]
1183  */
1184 UINT WINAPI midiInGetNumDevs(void)
1185 {
1186     return MMDRV_GetNum(MMDRV_MIDIIN);
1187 }
1188
1189 /**************************************************************************
1190  *                              midiInGetDevCapsW       [WINMM.@]
1191  */
1192 UINT WINAPI midiInGetDevCapsW(UINT_PTR uDeviceID, LPMIDIINCAPSW lpCaps, UINT uSize)
1193 {
1194     LPWINE_MLD  wmld;
1195
1196     TRACE("(%ld, %p, %d);\n", uDeviceID, lpCaps, uSize);
1197
1198     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
1199
1200     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIDIIN, TRUE)) == NULL)
1201         return MMSYSERR_INVALHANDLE;
1202
1203    return MMDRV_Message(wmld, MIDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
1204 }
1205
1206 /**************************************************************************
1207  *                              midiInGetDevCapsA       [WINMM.@]
1208  */
1209 UINT WINAPI midiInGetDevCapsA(UINT_PTR uDeviceID, LPMIDIINCAPSA lpCaps, UINT uSize)
1210 {
1211     MIDIINCAPSW         micW;
1212     UINT                ret;
1213
1214     if (lpCaps == NULL)        return MMSYSERR_INVALPARAM;
1215
1216     ret = midiInGetDevCapsW(uDeviceID, &micW, sizeof(micW));
1217
1218     if (ret == MMSYSERR_NOERROR) {
1219         MIDIINCAPSA micA;
1220         micA.wMid           = micW.wMid;
1221         micA.wPid           = micW.wPid;
1222         micA.vDriverVersion = micW.vDriverVersion;
1223         WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,
1224                              sizeof(micA.szPname), NULL, NULL );
1225         micA.dwSupport      = micW.dwSupport;
1226         memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));
1227     }
1228     return ret;
1229 }
1230
1231 UINT MIDI_InOpen(HMIDIIN* lphMidiIn, UINT uDeviceID, DWORD_PTR dwCallback,
1232                  DWORD_PTR dwInstance, DWORD dwFlags, BOOL bFrom32)
1233 {
1234     HANDLE              hMidiIn;
1235     LPWINE_MIDI         lpwm;
1236     DWORD               dwRet = 0;
1237
1238     TRACE("(%p, %d, %08lX, %08lX, %08X);\n",
1239           lphMidiIn, uDeviceID, dwCallback, dwInstance, dwFlags);
1240
1241     if (lphMidiIn != NULL) *lphMidiIn = 0;
1242
1243     lpwm = (LPWINE_MIDI)MMDRV_Alloc(sizeof(WINE_MIDI), MMDRV_MIDIIN, &hMidiIn,
1244                                     &dwFlags, &dwCallback, &dwInstance, bFrom32);
1245
1246     if (lpwm == NULL)
1247         return MMSYSERR_NOMEM;
1248
1249     lpwm->mod.hMidi = (HMIDI) hMidiIn;
1250     lpwm->mod.dwCallback = dwCallback;
1251     lpwm->mod.dwInstance = dwInstance;
1252
1253     lpwm->mld.uDeviceID = uDeviceID;
1254     dwRet = MMDRV_Open(&lpwm->mld, MIDM_OPEN, (DWORD)&lpwm->mod, dwFlags);
1255
1256     if (dwRet != MMSYSERR_NOERROR) {
1257         MMDRV_Free(hMidiIn, &lpwm->mld);
1258         hMidiIn = 0;
1259     }
1260     if (lphMidiIn != NULL) *lphMidiIn = hMidiIn;
1261     TRACE("=> %d hMidi=%p\n", dwRet, hMidiIn);
1262
1263     return dwRet;
1264 }
1265
1266 /**************************************************************************
1267  *                              midiInOpen              [WINMM.@]
1268  */
1269 UINT WINAPI midiInOpen(HMIDIIN* lphMidiIn, UINT uDeviceID,
1270                        DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD dwFlags)
1271 {
1272     return MIDI_InOpen(lphMidiIn, uDeviceID, dwCallback, dwInstance, dwFlags, TRUE);
1273 }
1274
1275 /**************************************************************************
1276  *                              midiInClose             [WINMM.@]
1277  */
1278 UINT WINAPI midiInClose(HMIDIIN hMidiIn)
1279 {
1280     LPWINE_MLD          wmld;
1281     DWORD               dwRet;
1282
1283     TRACE("(%p)\n", hMidiIn);
1284
1285     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1286         return MMSYSERR_INVALHANDLE;
1287
1288     dwRet = MMDRV_Close(wmld, MIDM_CLOSE);
1289     MMDRV_Free(hMidiIn, wmld);
1290     return dwRet;
1291 }
1292
1293 /**************************************************************************
1294  *                              midiInPrepareHeader     [WINMM.@]
1295  */
1296 UINT WINAPI midiInPrepareHeader(HMIDIIN hMidiIn,
1297                                 MIDIHDR* lpMidiInHdr, UINT uSize)
1298 {
1299     LPWINE_MLD          wmld;
1300
1301     TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
1302
1303     if (lpMidiInHdr == NULL || uSize < sizeof (MIDIHDR))
1304         return MMSYSERR_INVALPARAM;
1305
1306     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1307         return MMSYSERR_INVALHANDLE;
1308
1309     return MMDRV_Message(wmld, MIDM_PREPARE, (DWORD_PTR)lpMidiInHdr, uSize, TRUE);
1310 }
1311
1312 /**************************************************************************
1313  *                              midiInUnprepareHeader   [WINMM.@]
1314  */
1315 UINT WINAPI midiInUnprepareHeader(HMIDIIN hMidiIn,
1316                                   MIDIHDR* lpMidiInHdr, UINT uSize)
1317 {
1318     LPWINE_MLD          wmld;
1319
1320     TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
1321
1322     if (lpMidiInHdr == NULL || uSize < sizeof (MIDIHDR))
1323         return MMSYSERR_INVALPARAM;
1324
1325     if (!(lpMidiInHdr->dwFlags & MHDR_PREPARED)) {
1326         return MMSYSERR_NOERROR;
1327     }
1328
1329     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1330         return MMSYSERR_INVALHANDLE;
1331
1332     return MMDRV_Message(wmld, MIDM_UNPREPARE, (DWORD_PTR)lpMidiInHdr, uSize, TRUE);
1333 }
1334
1335 /**************************************************************************
1336  *                              midiInAddBuffer         [WINMM.@]
1337  */
1338 UINT WINAPI midiInAddBuffer(HMIDIIN hMidiIn,
1339                             MIDIHDR* lpMidiInHdr, UINT uSize)
1340 {
1341     LPWINE_MLD          wmld;
1342
1343     TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
1344
1345     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1346         return MMSYSERR_INVALHANDLE;
1347
1348     return MMDRV_Message(wmld, MIDM_ADDBUFFER, (DWORD_PTR)lpMidiInHdr, uSize, TRUE);
1349 }
1350
1351 /**************************************************************************
1352  *                              midiInStart                     [WINMM.@]
1353  */
1354 UINT WINAPI midiInStart(HMIDIIN hMidiIn)
1355 {
1356     LPWINE_MLD          wmld;
1357
1358     TRACE("(%p)\n", hMidiIn);
1359
1360     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1361         return MMSYSERR_INVALHANDLE;
1362
1363     return MMDRV_Message(wmld, MIDM_START, 0L, 0L, TRUE);
1364 }
1365
1366 /**************************************************************************
1367  *                              midiInStop                      [WINMM.@]
1368  */
1369 UINT WINAPI midiInStop(HMIDIIN hMidiIn)
1370 {
1371     LPWINE_MLD          wmld;
1372
1373     TRACE("(%p)\n", hMidiIn);
1374
1375     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1376         return MMSYSERR_INVALHANDLE;
1377
1378     return MMDRV_Message(wmld, MIDM_STOP, 0L, 0L, TRUE);
1379 }
1380
1381 /**************************************************************************
1382  *                              midiInReset                     [WINMM.@]
1383  */
1384 UINT WINAPI midiInReset(HMIDIIN hMidiIn)
1385 {
1386     LPWINE_MLD          wmld;
1387
1388     TRACE("(%p)\n", hMidiIn);
1389
1390     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1391         return MMSYSERR_INVALHANDLE;
1392
1393     return MMDRV_Message(wmld, MIDM_RESET, 0L, 0L, TRUE);
1394 }
1395
1396 /**************************************************************************
1397  *                              midiInGetID                     [WINMM.@]
1398  */
1399 UINT WINAPI midiInGetID(HMIDIIN hMidiIn, UINT* lpuDeviceID)
1400 {
1401     LPWINE_MLD          wmld;
1402
1403     TRACE("(%p, %p)\n", hMidiIn, lpuDeviceID);
1404
1405     if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
1406
1407     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, TRUE)) == NULL)
1408         return MMSYSERR_INVALHANDLE;
1409
1410     *lpuDeviceID = wmld->uDeviceID;
1411
1412     return MMSYSERR_NOERROR;
1413 }
1414
1415 /**************************************************************************
1416  *                              midiInMessage           [WINMM.@]
1417  */
1418 UINT WINAPI midiInMessage(HMIDIIN hMidiIn, UINT uMessage,
1419                           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1420 {
1421     LPWINE_MLD          wmld;
1422
1423     TRACE("(%p, %04X, %08lX, %08lX)\n", hMidiIn, uMessage, dwParam1, dwParam2);
1424
1425     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1426         return MMSYSERR_INVALHANDLE;
1427
1428     switch (uMessage) {
1429     case MIDM_OPEN:
1430     case MIDM_CLOSE:
1431         FIXME("can't handle OPEN or CLOSE message!\n");
1432         return MMSYSERR_NOTSUPPORTED;
1433     }
1434     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
1435 }
1436
1437 typedef struct WINE_MIDIStream {
1438     HMIDIOUT                    hDevice;
1439     HANDLE                      hThread;
1440     DWORD                       dwThreadID;
1441     DWORD                       dwTempo;
1442     DWORD                       dwTimeDiv;
1443     DWORD                       dwPositionMS;
1444     DWORD                       dwPulses;
1445     DWORD                       dwStartTicks;
1446     WORD                        wFlags;
1447     HANDLE                      hEvent;
1448     LPMIDIHDR                   lpMidiHdr;
1449 } WINE_MIDIStream;
1450
1451 #define WINE_MSM_HEADER         (WM_USER+0)
1452 #define WINE_MSM_STOP           (WM_USER+1)
1453
1454 /**************************************************************************
1455  *                              MMSYSTEM_GetMidiStream          [internal]
1456  */
1457 static  BOOL    MMSYSTEM_GetMidiStream(HMIDISTRM hMidiStrm, WINE_MIDIStream** lpMidiStrm, WINE_MIDI** lplpwm)
1458 {
1459     WINE_MIDI* lpwm = (LPWINE_MIDI)MMDRV_Get(hMidiStrm, MMDRV_MIDIOUT, FALSE);
1460
1461     if (lplpwm)
1462         *lplpwm = lpwm;
1463
1464     if (lpwm == NULL) {
1465         return FALSE;
1466     }
1467
1468     *lpMidiStrm = (WINE_MIDIStream*)lpwm->mod.rgIds.dwStreamID;
1469
1470     return *lpMidiStrm != NULL;
1471 }
1472
1473 /**************************************************************************
1474  *                              MMSYSTEM_MidiStream_Convert     [internal]
1475  */
1476 static  DWORD   MMSYSTEM_MidiStream_Convert(WINE_MIDIStream* lpMidiStrm, DWORD pulse)
1477 {
1478     DWORD       ret = 0;
1479
1480     if (lpMidiStrm->dwTimeDiv == 0) {
1481         FIXME("Shouldn't happen. lpMidiStrm->dwTimeDiv = 0\n");
1482     } else if (lpMidiStrm->dwTimeDiv > 0x8000) { /* SMPTE, unchecked FIXME? */
1483         int     nf = -(char)HIBYTE(lpMidiStrm->dwTimeDiv);      /* number of frames     */
1484         int     nsf = LOBYTE(lpMidiStrm->dwTimeDiv);            /* number of sub-frames */
1485         ret = (pulse * 1000) / (nf * nsf);
1486     } else {
1487         ret = (DWORD)((double)pulse * ((double)lpMidiStrm->dwTempo / 1000) /
1488                       (double)lpMidiStrm->dwTimeDiv);
1489     }
1490
1491     return ret;
1492 }
1493
1494 /**************************************************************************
1495  *                      MMSYSTEM_MidiStream_MessageHandler      [internal]
1496  */
1497 static  BOOL    MMSYSTEM_MidiStream_MessageHandler(WINE_MIDIStream* lpMidiStrm, LPWINE_MIDI lpwm, LPMSG msg)
1498 {
1499     LPMIDIHDR   lpMidiHdr;
1500     LPMIDIHDR*  lpmh;
1501     LPBYTE      lpData;
1502
1503     switch (msg->message) {
1504     case WM_QUIT:
1505         SetEvent(lpMidiStrm->hEvent);
1506         return FALSE;
1507     case WINE_MSM_STOP:
1508         TRACE("STOP\n");
1509         /* this is not quite what MS doc says... */
1510         midiOutReset(lpMidiStrm->hDevice);
1511         /* empty list of already submitted buffers */
1512         for (lpMidiHdr = lpMidiStrm->lpMidiHdr; lpMidiHdr; lpMidiHdr = (LPMIDIHDR)lpMidiHdr->lpNext) {
1513             lpMidiHdr->dwFlags |= MHDR_DONE;
1514             lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1515
1516             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1517                            (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1518                            lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
1519         }
1520         lpMidiStrm->lpMidiHdr = 0;
1521         SetEvent(lpMidiStrm->hEvent);
1522         break;
1523     case WINE_MSM_HEADER:
1524         /* sets initial tick count for first MIDIHDR */
1525         if (!lpMidiStrm->dwStartTicks)
1526             lpMidiStrm->dwStartTicks = GetTickCount();
1527
1528         /* FIXME(EPP): "I don't understand the content of the first MIDIHDR sent
1529          * by native mcimidi, it doesn't look like a correct one".
1530          * this trick allows to throw it away... but I don't like it.
1531          * It looks like part of the file I'm trying to play and definitively looks
1532          * like raw midi content
1533          * I'd really like to understand why native mcimidi sends it. Perhaps a bad
1534          * synchronization issue where native mcimidi is still processing raw MIDI
1535          * content before generating MIDIEVENTs ?
1536          *
1537          * 4c 04 89 3b 00 81 7c 99 3b 43 00 99 23 5e 04 89 L..;..|.;C..#^..
1538          * 3b 00 00 89 23 00 7c 99 3b 45 00 99 28 62 04 89 ;...#.|.;E..(b..
1539          * 3b 00 00 89 28 00 81 7c 99 3b 4e 00 99 23 5e 04 ;...(..|.;N..#^.
1540          * 89 3b 00 00 89 23 00 7c 99 3b 45 00 99 23 78 04 .;...#.|.;E..#x.
1541          * 89 3b 00 00 89 23 00 81 7c 99 3b 48 00 99 23 5e .;...#..|.;H..#^
1542          * 04 89 3b 00 00 89 23 00 7c 99 3b 4e 00 99 28 62 ..;...#.|.;N..(b
1543          * 04 89 3b 00 00 89 28 00 81 7c 99 39 4c 00 99 23 ..;...(..|.9L..#
1544          * 5e 04 89 39 00 00 89 23 00 82 7c 99 3b 4c 00 99 ^..9...#..|.;L..
1545          * 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 00 99 #^..;...#.|.;H..
1546          * 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b 3f 04 (b..;...(..|.;?.
1547          * 89 3b 00 1c 99 23 5e 04 89 23 00 5c 99 3b 45 00 .;...#^..#.\.;E.
1548          * 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 3b 46 .#x..;...#..|.;F
1549          * 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 ..#^..;...#.|.;H
1550          * 00 99 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b ..(b..;...(..|.;
1551          * 46 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b F..#^..;...#.|.;
1552          * 48 00 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 H..#x..;...#..|.
1553          * 3b 4c 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 ;L..#^..;...#.|.
1554          */
1555         lpMidiHdr = (LPMIDIHDR)msg->lParam;
1556         lpData = (LPBYTE)lpMidiHdr->lpData;
1557         TRACE("Adding %s lpMidiHdr=%p [lpData=0x%p dwBufferLength=%u/%u dwFlags=0x%08x size=%lu]\n",
1558               (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular", lpMidiHdr,
1559               lpMidiHdr, lpMidiHdr->dwBufferLength, lpMidiHdr->dwBytesRecorded,
1560               lpMidiHdr->dwFlags, msg->wParam);
1561 #if 0
1562         /* dumps content of lpMidiHdr->lpData
1563          * FIXME: there should be a debug routine somewhere that already does this
1564          * I hate spreading this type of shit all around the code
1565          */
1566         for (dwToGo = 0; dwToGo < lpMidiHdr->dwBufferLength; dwToGo += 16) {
1567             DWORD       i;
1568             BYTE        ch;
1569
1570             for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++)
1571                 printf("%02x ", lpData[dwToGo + i]);
1572             for (; i < 16; i++)
1573                 printf("   ");
1574             for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++) {
1575                 ch = lpData[dwToGo + i];
1576                 printf("%c", (ch >= 0x20 && ch <= 0x7F) ? ch : '.');
1577             }
1578             printf("\n");
1579         }
1580 #endif
1581         if (((LPMIDIEVENT)lpData)->dwStreamID != 0 &&
1582             ((LPMIDIEVENT)lpData)->dwStreamID != 0xFFFFFFFF &&
1583             ((LPMIDIEVENT)lpData)->dwStreamID != (DWORD)lpMidiStrm) {
1584             FIXME("Dropping bad %s lpMidiHdr (streamID=%08x)\n",
1585                   (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular",
1586                   ((LPMIDIEVENT)lpData)->dwStreamID);
1587             lpMidiHdr->dwFlags |= MHDR_DONE;
1588             lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1589
1590             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1591                            (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1592                            lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
1593             break;
1594         }
1595
1596         for (lpmh = &lpMidiStrm->lpMidiHdr; *lpmh; lpmh = (LPMIDIHDR*)&((*lpmh)->lpNext));
1597         *lpmh = lpMidiHdr;
1598         lpMidiHdr = (LPMIDIHDR)msg->lParam;
1599         lpMidiHdr->lpNext = 0;
1600         lpMidiHdr->dwFlags |= MHDR_INQUEUE;
1601         lpMidiHdr->dwFlags &= ~MHDR_DONE;
1602         lpMidiHdr->dwOffset = 0;
1603
1604         break;
1605     default:
1606         FIXME("Unknown message %d\n", msg->message);
1607         break;
1608     }
1609     return TRUE;
1610 }
1611
1612 /**************************************************************************
1613  *                              MMSYSTEM_MidiStream_Player      [internal]
1614  */
1615 static  DWORD   CALLBACK        MMSYSTEM_MidiStream_Player(LPVOID pmt)
1616 {
1617     WINE_MIDIStream*    lpMidiStrm = pmt;
1618     WINE_MIDI*          lpwm;
1619     MSG                 msg;
1620     DWORD               dwToGo;
1621     DWORD               dwCurrTC;
1622     LPMIDIHDR           lpMidiHdr;
1623     LPMIDIEVENT         me;
1624     LPBYTE              lpData = 0;
1625
1626     TRACE("(%p)!\n", lpMidiStrm);
1627
1628     if (!lpMidiStrm ||
1629         (lpwm = (LPWINE_MIDI)MMDRV_Get(lpMidiStrm->hDevice, MMDRV_MIDIOUT, FALSE)) == NULL)
1630         goto the_end;
1631
1632     /* force thread's queue creation */
1633     /* Used to be InitThreadInput16(0, 5); */
1634     /* but following works also with hack in midiStreamOpen */
1635     PeekMessageA(&msg, 0, 0, 0, 0);
1636
1637     /* FIXME: this next line must be called before midiStreamOut or midiStreamRestart are called */
1638     SetEvent(lpMidiStrm->hEvent);
1639     TRACE("Ready to go 1\n");
1640     /* thread is started in paused mode */
1641     SuspendThread(lpMidiStrm->hThread);
1642     TRACE("Ready to go 2\n");
1643
1644     lpMidiStrm->dwStartTicks = 0;
1645     lpMidiStrm->dwPulses = 0;
1646
1647     lpMidiStrm->lpMidiHdr = 0;
1648
1649     for (;;) {
1650         lpMidiHdr = lpMidiStrm->lpMidiHdr;
1651         if (!lpMidiHdr) {
1652             /* for first message, block until one arrives, then process all that are available */
1653             GetMessageA(&msg, 0, 0, 0);
1654             do {
1655                 if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
1656                     goto the_end;
1657             } while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE));
1658             lpData = 0;
1659             continue;
1660         }
1661
1662         if (!lpData)
1663             lpData = (LPBYTE)lpMidiHdr->lpData;
1664
1665         me = (LPMIDIEVENT)(lpData + lpMidiHdr->dwOffset);
1666
1667         /* do we have to wait ? */
1668         if (me->dwDeltaTime) {
1669             lpMidiStrm->dwPositionMS += MMSYSTEM_MidiStream_Convert(lpMidiStrm, me->dwDeltaTime);
1670             lpMidiStrm->dwPulses += me->dwDeltaTime;
1671
1672             dwToGo = lpMidiStrm->dwStartTicks + lpMidiStrm->dwPositionMS;
1673
1674             TRACE("%d/%d/%d\n", dwToGo, GetTickCount(), me->dwDeltaTime);
1675             while ((dwCurrTC = GetTickCount()) < dwToGo) {
1676                 if (MsgWaitForMultipleObjects(0, NULL, FALSE, dwToGo - dwCurrTC, QS_ALLINPUT) == WAIT_OBJECT_0) {
1677                     /* got a message, handle it */
1678                     while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
1679                         if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
1680                             goto the_end;
1681                     }
1682                     lpData = 0;
1683                 } else {
1684                     /* timeout, so me->dwDeltaTime is elapsed, can break the while loop */
1685                     break;
1686                 }
1687             }
1688         }
1689         switch (MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK)) {
1690         case MEVT_COMMENT:
1691             FIXME("NIY: MEVT_COMMENT\n");
1692             /* do nothing, skip bytes */
1693             break;
1694         case MEVT_LONGMSG:
1695             FIXME("NIY: MEVT_LONGMSG, aka sending Sysex event\n");
1696             break;
1697         case MEVT_NOP:
1698             break;
1699         case MEVT_SHORTMSG:
1700             midiOutShortMsg(lpMidiStrm->hDevice, MEVT_EVENTPARM(me->dwEvent));
1701             break;
1702         case MEVT_TEMPO:
1703             lpMidiStrm->dwTempo = MEVT_EVENTPARM(me->dwEvent);
1704             break;
1705         case MEVT_VERSION:
1706             break;
1707         default:
1708             FIXME("Unknown MEVT (0x%02x)\n", MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK));
1709             break;
1710         }
1711         if (me->dwEvent & MEVT_F_CALLBACK) {
1712             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1713                            (HDRVR)lpMidiStrm->hDevice, MM_MOM_POSITIONCB,
1714                            lpwm->mod.dwInstance, (LPARAM)lpMidiHdr, 0L);
1715         }
1716         lpMidiHdr->dwOffset += sizeof(MIDIEVENT) - sizeof(me->dwParms);
1717         if (me->dwEvent & MEVT_F_LONG)
1718             lpMidiHdr->dwOffset += (MEVT_EVENTPARM(me->dwEvent) + 3) & ~3;
1719         if (lpMidiHdr->dwOffset >= lpMidiHdr->dwBufferLength) {
1720             /* done with this header */
1721             lpMidiHdr->dwFlags |= MHDR_DONE;
1722             lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1723
1724             lpMidiStrm->lpMidiHdr = (LPMIDIHDR)lpMidiHdr->lpNext;
1725             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1726                            (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1727                            lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
1728             lpData = 0;
1729         }
1730     }
1731 the_end:
1732     TRACE("End of thread\n");
1733     ExitThread(0);
1734     return 0;   /* for removing the warning, never executed */
1735 }
1736
1737 /**************************************************************************
1738  *                              MMSYSTEM_MidiStream_PostMessage [internal]
1739  */
1740 static  BOOL MMSYSTEM_MidiStream_PostMessage(WINE_MIDIStream* lpMidiStrm, WORD msg, DWORD pmt1, DWORD pmt2)
1741 {
1742     if (PostThreadMessageA(lpMidiStrm->dwThreadID, msg, pmt1, pmt2)) {
1743         DWORD   count;
1744
1745         if (pFnReleaseThunkLock) pFnReleaseThunkLock(&count);
1746         WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);
1747         if (pFnRestoreThunkLock) pFnRestoreThunkLock(count);
1748     } else {
1749         WARN("bad PostThreadMessageA\n");
1750         return FALSE;
1751     }
1752     return TRUE;
1753 }
1754
1755 /**************************************************************************
1756  *                              midiStreamClose                 [WINMM.@]
1757  */
1758 MMRESULT WINAPI midiStreamClose(HMIDISTRM hMidiStrm)
1759 {
1760     WINE_MIDIStream*    lpMidiStrm;
1761
1762     TRACE("(%p)!\n", hMidiStrm);
1763
1764     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL))
1765         return MMSYSERR_INVALHANDLE;
1766
1767     midiStreamStop(hMidiStrm);
1768     MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WM_QUIT, 0, 0);
1769     HeapFree(GetProcessHeap(), 0, lpMidiStrm);
1770     CloseHandle(lpMidiStrm->hEvent);
1771
1772     return midiOutClose((HMIDIOUT)hMidiStrm);
1773 }
1774
1775 /**************************************************************************
1776  *                              MMSYSTEM_MidiStream_Open        [internal]
1777  */
1778 MMRESULT MIDI_StreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID, DWORD cMidi,
1779                          DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen, 
1780                          BOOL bFrom32)
1781 {
1782     WINE_MIDIStream*    lpMidiStrm;
1783     MMRESULT            ret;
1784     MIDIOPENSTRMID      mosm;
1785     LPWINE_MIDI         lpwm;
1786     HMIDIOUT            hMidiOut;
1787
1788     TRACE("(%p, %p, %d, 0x%08lx, 0x%08lx, 0x%08x)!\n",
1789           lphMidiStrm, lpuDeviceID, cMidi, dwCallback, dwInstance, fdwOpen);
1790
1791     if (cMidi != 1 || lphMidiStrm == NULL || lpuDeviceID == NULL)
1792         return MMSYSERR_INVALPARAM;
1793
1794     lpMidiStrm = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_MIDIStream));
1795     if (!lpMidiStrm)
1796         return MMSYSERR_NOMEM;
1797
1798     lpMidiStrm->dwTempo = 500000;
1799     lpMidiStrm->dwTimeDiv = 480;        /* 480 is 120 quater notes per minute *//* FIXME ??*/
1800     lpMidiStrm->dwPositionMS = 0;
1801
1802     mosm.dwStreamID = (DWORD)lpMidiStrm;
1803     /* FIXME: the correct value is not allocated yet for MAPPER */
1804     mosm.wDeviceID  = *lpuDeviceID;
1805     lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &fdwOpen, 1, &mosm, bFrom32);
1806     lpMidiStrm->hDevice = hMidiOut;
1807     if (lphMidiStrm)
1808         *lphMidiStrm = (HMIDISTRM)hMidiOut;
1809
1810     lpwm->mld.uDeviceID = *lpuDeviceID;
1811
1812     ret = MMDRV_Open(&lpwm->mld, MODM_OPEN, (DWORD)&lpwm->mod, fdwOpen);
1813     lpMidiStrm->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1814     lpMidiStrm->wFlags = HIWORD(fdwOpen);
1815
1816     lpMidiStrm->hThread = CreateThread(NULL, 0, MMSYSTEM_MidiStream_Player,
1817                                        lpMidiStrm, 0, &(lpMidiStrm->dwThreadID));
1818
1819     if (!lpMidiStrm->hThread) {
1820         midiStreamClose((HMIDISTRM)hMidiOut);
1821         return MMSYSERR_NOMEM;
1822     }
1823     SetThreadPriority(lpMidiStrm->hThread, THREAD_PRIORITY_TIME_CRITICAL);
1824
1825     /* wait for thread to have started, and for its queue to be created */
1826     {
1827         DWORD   count;
1828
1829         /* (Release|Restore)ThunkLock() is needed when this method is called from 16 bit code,
1830          * (meaning the Win16Lock is set), so that it's released and the 32 bit thread running
1831          * MMSYSTEM_MidiStreamPlayer can acquire Win16Lock to create its queue.
1832          */
1833         if (pFnReleaseThunkLock) pFnReleaseThunkLock(&count);
1834         WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);
1835         if (pFnRestoreThunkLock) pFnRestoreThunkLock(count);
1836     }
1837
1838     TRACE("=> (%u/%d) hMidi=%p ret=%d lpMidiStrm=%p\n",
1839           *lpuDeviceID, lpwm->mld.uDeviceID, *lphMidiStrm, ret, lpMidiStrm);
1840     return ret;
1841 }
1842
1843 /**************************************************************************
1844  *                              midiStreamOpen                  [WINMM.@]
1845  */
1846 MMRESULT WINAPI midiStreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID,
1847                                DWORD cMidi, DWORD_PTR dwCallback,
1848                                DWORD_PTR dwInstance, DWORD fdwOpen)
1849 {
1850     return MIDI_StreamOpen(lphMidiStrm, lpuDeviceID, cMidi, dwCallback,
1851                            dwInstance, fdwOpen, TRUE);
1852 }
1853
1854 /**************************************************************************
1855  *                              midiStreamOut                   [WINMM.@]
1856  */
1857 MMRESULT WINAPI midiStreamOut(HMIDISTRM hMidiStrm, LPMIDIHDR lpMidiHdr,
1858                               UINT cbMidiHdr)
1859 {
1860     WINE_MIDIStream*    lpMidiStrm;
1861     DWORD               ret = MMSYSERR_NOERROR;
1862
1863     TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMidiHdr, cbMidiHdr);
1864
1865     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1866         ret = MMSYSERR_INVALHANDLE;
1867     } else if (!lpMidiHdr) {
1868         ret = MMSYSERR_INVALPARAM;
1869     } else {
1870         if (!PostThreadMessageA(lpMidiStrm->dwThreadID,
1871                                 WINE_MSM_HEADER, cbMidiHdr,
1872                                 (DWORD)lpMidiHdr)) {
1873             WARN("bad PostThreadMessageA\n");
1874             ret = MMSYSERR_ERROR;
1875         }
1876     }
1877     return ret;
1878 }
1879
1880 /**************************************************************************
1881  *                              midiStreamPause                 [WINMM.@]
1882  */
1883 MMRESULT WINAPI midiStreamPause(HMIDISTRM hMidiStrm)
1884 {
1885     WINE_MIDIStream*    lpMidiStrm;
1886     DWORD               ret = MMSYSERR_NOERROR;
1887
1888     TRACE("(%p)!\n", hMidiStrm);
1889
1890     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1891         ret = MMSYSERR_INVALHANDLE;
1892     } else {
1893         if (SuspendThread(lpMidiStrm->hThread) == 0xFFFFFFFF) {
1894             WARN("bad Suspend (%d)\n", GetLastError());
1895             ret = MMSYSERR_ERROR;
1896         }
1897     }
1898     return ret;
1899 }
1900
1901 /**************************************************************************
1902  *                              midiStreamPosition              [WINMM.@]
1903  */
1904 MMRESULT WINAPI midiStreamPosition(HMIDISTRM hMidiStrm, LPMMTIME lpMMT, UINT cbmmt)
1905 {
1906     WINE_MIDIStream*    lpMidiStrm;
1907     DWORD               ret = MMSYSERR_NOERROR;
1908
1909     TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMMT, cbmmt);
1910
1911     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1912         ret = MMSYSERR_INVALHANDLE;
1913     } else if (lpMMT == NULL || cbmmt != sizeof(MMTIME)) {
1914         ret = MMSYSERR_INVALPARAM;
1915     } else {
1916         switch (lpMMT->wType) {
1917         case TIME_MS:
1918             lpMMT->u.ms = lpMidiStrm->dwPositionMS;
1919             TRACE("=> %d ms\n", lpMMT->u.ms);
1920             break;
1921         case TIME_TICKS:
1922             lpMMT->u.ticks = lpMidiStrm->dwPulses;
1923             TRACE("=> %d ticks\n", lpMMT->u.ticks);
1924             break;
1925         default:
1926             WARN("Unsupported time type %d\n", lpMMT->wType);
1927             lpMMT->wType = TIME_MS;
1928             ret = MMSYSERR_INVALPARAM;
1929             break;
1930         }
1931     }
1932     return ret;
1933 }
1934
1935 /**************************************************************************
1936  *                              midiStreamProperty              [WINMM.@]
1937  */
1938 MMRESULT WINAPI midiStreamProperty(HMIDISTRM hMidiStrm, LPBYTE lpPropData, DWORD dwProperty)
1939 {
1940     WINE_MIDIStream*    lpMidiStrm;
1941     MMRESULT            ret = MMSYSERR_NOERROR;
1942
1943     TRACE("(%p, %p, %x)\n", hMidiStrm, lpPropData, dwProperty);
1944
1945     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1946         ret = MMSYSERR_INVALHANDLE;
1947     } else if ((dwProperty & (MIDIPROP_GET|MIDIPROP_SET)) == 0) {
1948         ret = MMSYSERR_INVALPARAM;
1949     } else if (dwProperty & MIDIPROP_TEMPO) {
1950         MIDIPROPTEMPO*  mpt = (MIDIPROPTEMPO*)lpPropData;
1951
1952         if (sizeof(MIDIPROPTEMPO) != mpt->cbStruct) {
1953             ret = MMSYSERR_INVALPARAM;
1954         } else if (dwProperty & MIDIPROP_SET) {
1955             lpMidiStrm->dwTempo = mpt->dwTempo;
1956             TRACE("Setting tempo to %d\n", mpt->dwTempo);
1957         } else if (dwProperty & MIDIPROP_GET) {
1958             mpt->dwTempo = lpMidiStrm->dwTempo;
1959             TRACE("Getting tempo <= %d\n", mpt->dwTempo);
1960         }
1961     } else if (dwProperty & MIDIPROP_TIMEDIV) {
1962         MIDIPROPTIMEDIV*        mptd = (MIDIPROPTIMEDIV*)lpPropData;
1963
1964         if (sizeof(MIDIPROPTIMEDIV) != mptd->cbStruct) {
1965             ret = MMSYSERR_INVALPARAM;
1966         } else if (dwProperty & MIDIPROP_SET) {
1967             lpMidiStrm->dwTimeDiv = mptd->dwTimeDiv;
1968             TRACE("Setting time div to %d\n", mptd->dwTimeDiv);
1969         } else if (dwProperty & MIDIPROP_GET) {
1970             mptd->dwTimeDiv = lpMidiStrm->dwTimeDiv;
1971             TRACE("Getting time div <= %d\n", mptd->dwTimeDiv);
1972         }
1973     } else {
1974         ret = MMSYSERR_INVALPARAM;
1975     }
1976
1977     return ret;
1978 }
1979
1980 /**************************************************************************
1981  *                              midiStreamRestart               [WINMM.@]
1982  */
1983 MMRESULT WINAPI midiStreamRestart(HMIDISTRM hMidiStrm)
1984 {
1985     WINE_MIDIStream*    lpMidiStrm;
1986     MMRESULT            ret = MMSYSERR_NOERROR;
1987
1988     TRACE("(%p)!\n", hMidiStrm);
1989
1990     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1991         ret = MMSYSERR_INVALHANDLE;
1992     } else {
1993         DWORD   ret;
1994
1995         /* since we increase the thread suspend count on each midiStreamPause
1996          * there may be a need for several midiStreamResume
1997          */
1998         do {
1999             ret = ResumeThread(lpMidiStrm->hThread);
2000         } while (ret != 0xFFFFFFFF && ret != 0);
2001         if (ret == 0xFFFFFFFF) {
2002             WARN("bad Resume (%d)\n", GetLastError());
2003             ret = MMSYSERR_ERROR;
2004         } else {
2005             lpMidiStrm->dwStartTicks = GetTickCount() - lpMidiStrm->dwPositionMS;
2006         }
2007     }
2008     return ret;
2009 }
2010
2011 /**************************************************************************
2012  *                              midiStreamStop                  [WINMM.@]
2013  */
2014 MMRESULT WINAPI midiStreamStop(HMIDISTRM hMidiStrm)
2015 {
2016     WINE_MIDIStream*    lpMidiStrm;
2017     MMRESULT            ret = MMSYSERR_NOERROR;
2018
2019     TRACE("(%p)!\n", hMidiStrm);
2020
2021     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
2022         ret = MMSYSERR_INVALHANDLE;
2023     } else {
2024         /* in case stream has been paused... FIXME is the current state correct ? */
2025         midiStreamRestart(hMidiStrm);
2026         MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WINE_MSM_STOP, 0, 0);
2027     }
2028     return ret;
2029 }
2030
2031 UINT WAVE_Open(HANDLE* lphndl, UINT uDeviceID, UINT uType, 
2032                LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback, 
2033                DWORD_PTR dwInstance, DWORD dwFlags, BOOL bFrom32)
2034 {
2035     HANDLE              handle;
2036     LPWINE_MLD          wmld;
2037     DWORD               dwRet = MMSYSERR_NOERROR;
2038     WAVEOPENDESC        wod;
2039
2040     TRACE("(%p, %d, %s, %p, %08lX, %08lX, %08X, %d);\n",
2041           lphndl, (int)uDeviceID, (uType==MMDRV_WAVEOUT)?"Out":"In", lpFormat, dwCallback,
2042           dwInstance, dwFlags, bFrom32?32:16);
2043
2044     if (dwFlags & WAVE_FORMAT_QUERY)
2045         TRACE("WAVE_FORMAT_QUERY requested !\n");
2046
2047     if (lpFormat == NULL) {
2048         WARN("bad format\n");
2049         return WAVERR_BADFORMAT;
2050     }
2051
2052     if ((dwFlags & WAVE_MAPPED) && (uDeviceID == (UINT)-1)) {
2053         WARN("invalid parameter\n");
2054         return MMSYSERR_INVALPARAM;
2055     }
2056
2057     /* may have a PCMWAVEFORMAT rather than a WAVEFORMATEX so don't read cbSize */
2058     TRACE("wFormatTag=%u, nChannels=%u, nSamplesPerSec=%u, nAvgBytesPerSec=%u, nBlockAlign=%u, wBitsPerSample=%u\n",
2059           lpFormat->wFormatTag, lpFormat->nChannels, lpFormat->nSamplesPerSec,
2060           lpFormat->nAvgBytesPerSec, lpFormat->nBlockAlign, lpFormat->wBitsPerSample);
2061
2062     if ((wmld = MMDRV_Alloc(sizeof(WINE_WAVE), uType, &handle,
2063                             &dwFlags, &dwCallback, &dwInstance, bFrom32)) == NULL) {
2064         WARN("no memory\n");
2065         return MMSYSERR_NOMEM;
2066     }
2067
2068     wod.hWave = handle;
2069     wod.lpFormat = (LPWAVEFORMATEX)lpFormat;  /* should the struct be copied iso pointer? */
2070     wod.dwCallback = dwCallback;
2071     wod.dwInstance = dwInstance;
2072     wod.dnDevNode = 0L;
2073
2074     TRACE("cb=%08x\n", wod.dwCallback);
2075
2076     for (;;) {
2077         if (dwFlags & WAVE_MAPPED) {
2078             wod.uMappedDeviceID = uDeviceID;
2079             uDeviceID = WAVE_MAPPER;
2080         } else {
2081             wod.uMappedDeviceID = -1;
2082         }
2083         wmld->uDeviceID = uDeviceID;
2084     
2085         dwRet = MMDRV_Open(wmld, (uType == MMDRV_WAVEOUT) ? WODM_OPEN : WIDM_OPEN, 
2086                            (DWORD)&wod, dwFlags);
2087
2088         TRACE("dwRet = %s\n", WINMM_ErrorToString(dwRet));
2089         if (dwRet != WAVERR_BADFORMAT ||
2090             ((dwFlags & (WAVE_MAPPED|WAVE_FORMAT_DIRECT)) != 0) || (uDeviceID == WAVE_MAPPER)) break;
2091         /* if we ask for a format which isn't supported by the physical driver, 
2092          * let's try to map it through the wave mapper (except, if we already tried
2093          * or user didn't allow us to use acm codecs or the device is already the mapper)
2094          */
2095         dwFlags |= WAVE_MAPPED;
2096         /* we shall loop only one */
2097     }
2098
2099     if ((dwFlags & WAVE_FORMAT_QUERY) || dwRet != MMSYSERR_NOERROR) {
2100         MMDRV_Free(handle, wmld);
2101         handle = 0;
2102     }
2103
2104     if (lphndl != NULL) *lphndl = handle;
2105     TRACE("=> %s hWave=%p\n", WINMM_ErrorToString(dwRet), handle);
2106
2107     return dwRet;
2108 }
2109
2110 /**************************************************************************
2111  *                              waveOutGetNumDevs               [WINMM.@]
2112  */
2113 UINT WINAPI waveOutGetNumDevs(void)
2114 {
2115     return MMDRV_GetNum(MMDRV_WAVEOUT);
2116 }
2117
2118 /**************************************************************************
2119  *                              waveOutGetDevCapsA              [WINMM.@]
2120  */
2121 UINT WINAPI waveOutGetDevCapsA(UINT_PTR uDeviceID, LPWAVEOUTCAPSA lpCaps,
2122                                UINT uSize)
2123 {
2124     WAVEOUTCAPSW        wocW;
2125     UINT                ret;
2126
2127     if (lpCaps == NULL)        return MMSYSERR_INVALPARAM;
2128
2129     ret = waveOutGetDevCapsW(uDeviceID, &wocW, sizeof(wocW));
2130
2131     if (ret == MMSYSERR_NOERROR) {
2132         WAVEOUTCAPSA wocA;
2133         wocA.wMid           = wocW.wMid;
2134         wocA.wPid           = wocW.wPid;
2135         wocA.vDriverVersion = wocW.vDriverVersion;
2136         WideCharToMultiByte( CP_ACP, 0, wocW.szPname, -1, wocA.szPname,
2137                              sizeof(wocA.szPname), NULL, NULL );
2138         wocA.dwFormats      = wocW.dwFormats;
2139         wocA.wChannels      = wocW.wChannels;
2140         wocA.dwSupport      = wocW.dwSupport;
2141         memcpy(lpCaps, &wocA, min(uSize, sizeof(wocA)));
2142     }
2143     return ret;
2144 }
2145
2146 /**************************************************************************
2147  *                              waveOutGetDevCapsW              [WINMM.@]
2148  */
2149 UINT WINAPI waveOutGetDevCapsW(UINT_PTR uDeviceID, LPWAVEOUTCAPSW lpCaps,
2150                                UINT uSize)
2151 {
2152     LPWINE_MLD          wmld;
2153
2154     TRACE("(%lu %p %u)!\n", uDeviceID, lpCaps, uSize);
2155
2156     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2157
2158     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_WAVEOUT, TRUE)) == NULL)
2159         return MMSYSERR_BADDEVICEID;
2160
2161     return MMDRV_Message(wmld, WODM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
2162
2163 }
2164
2165 /**************************************************************************
2166  *                              waveOutGetErrorTextA    [WINMM.@]
2167  *                              waveInGetErrorTextA     [WINMM.@]
2168  */
2169 UINT WINAPI waveOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
2170 {
2171     UINT        ret;
2172
2173     if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
2174     else if (uSize == 0) ret = MMSYSERR_NOERROR;
2175     else
2176     {
2177         LPWSTR  xstr = HeapAlloc(GetProcessHeap(), 0, uSize * sizeof(WCHAR));
2178         if (!xstr) ret = MMSYSERR_NOMEM;
2179         else
2180         {
2181             ret = waveOutGetErrorTextW(uError, xstr, uSize);
2182             if (ret == MMSYSERR_NOERROR)
2183                 WideCharToMultiByte(CP_ACP, 0, xstr, -1, lpText, uSize, NULL, NULL);
2184             HeapFree(GetProcessHeap(), 0, xstr);
2185         }
2186     }
2187     return ret;
2188 }
2189
2190 /**************************************************************************
2191  *                              waveOutGetErrorTextW    [WINMM.@]
2192  *                              waveInGetErrorTextW     [WINMM.@]
2193  */
2194 UINT WINAPI waveOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
2195 {
2196     UINT        ret = MMSYSERR_BADERRNUM;
2197
2198     if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
2199     else if (uSize == 0) ret = MMSYSERR_NOERROR;
2200     else if (
2201                /* test has been removed 'coz MMSYSERR_BASE is 0, and gcc did emit
2202                 * a warning for the test was always true */
2203                (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||
2204                (uError >= WAVERR_BASE  && uError <= WAVERR_LASTERROR)) {
2205         if (LoadStringW(hWinMM32Instance,
2206                         uError, lpText, uSize) > 0) {
2207             ret = MMSYSERR_NOERROR;
2208         }
2209     }
2210     return ret;
2211 }
2212
2213 /**************************************************************************
2214  *                      waveOutOpen                     [WINMM.@]
2215  * All the args/structs have the same layout as the win16 equivalents
2216  */
2217 MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
2218                        LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2219                        DWORD_PTR dwInstance, DWORD dwFlags)
2220 {
2221     return WAVE_Open((HANDLE*)lphWaveOut, uDeviceID, MMDRV_WAVEOUT, lpFormat,
2222                      dwCallback, dwInstance, dwFlags, TRUE);
2223 }
2224
2225 /**************************************************************************
2226  *                              waveOutClose            [WINMM.@]
2227  */
2228 UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
2229 {
2230     LPWINE_MLD          wmld;
2231     DWORD               dwRet;
2232
2233     TRACE("(%p)\n", hWaveOut);
2234
2235     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2236         return MMSYSERR_INVALHANDLE;
2237
2238     dwRet = MMDRV_Close(wmld, WODM_CLOSE);
2239     if (dwRet != WAVERR_STILLPLAYING)
2240         MMDRV_Free(hWaveOut, wmld);
2241
2242     return dwRet;
2243 }
2244
2245 /**************************************************************************
2246  *                              waveOutPrepareHeader    [WINMM.@]
2247  */
2248 UINT WINAPI waveOutPrepareHeader(HWAVEOUT hWaveOut,
2249                                  WAVEHDR* lpWaveOutHdr, UINT uSize)
2250 {
2251     LPWINE_MLD          wmld;
2252     UINT                result;
2253
2254     TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
2255
2256     if (lpWaveOutHdr == NULL || uSize < sizeof (WAVEHDR))
2257         return MMSYSERR_INVALPARAM;
2258
2259     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2260         return MMSYSERR_INVALHANDLE;
2261
2262     if ((result = MMDRV_Message(wmld, WODM_PREPARE, (DWORD_PTR)lpWaveOutHdr,
2263                                 uSize, TRUE)) != MMSYSERR_NOTSUPPORTED)
2264         return result;
2265
2266     if (lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
2267         return WAVERR_STILLPLAYING;
2268
2269     lpWaveOutHdr->dwFlags |= WHDR_PREPARED;
2270     lpWaveOutHdr->dwFlags &= ~WHDR_DONE;
2271
2272     return MMSYSERR_NOERROR;
2273 }
2274
2275 /**************************************************************************
2276  *                              waveOutUnprepareHeader  [WINMM.@]
2277  */
2278 UINT WINAPI waveOutUnprepareHeader(HWAVEOUT hWaveOut,
2279                                    LPWAVEHDR lpWaveOutHdr, UINT uSize)
2280 {
2281     LPWINE_MLD          wmld;
2282     UINT                result;
2283
2284     TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
2285
2286     if (lpWaveOutHdr == NULL || uSize < sizeof (WAVEHDR))
2287         return MMSYSERR_INVALPARAM;
2288     
2289     if (!(lpWaveOutHdr->dwFlags & WHDR_PREPARED)) {
2290         return MMSYSERR_NOERROR;
2291     }
2292
2293     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2294         return MMSYSERR_INVALHANDLE;
2295
2296     if ((result = MMDRV_Message(wmld, WODM_UNPREPARE, (DWORD_PTR)lpWaveOutHdr,
2297                                 uSize, TRUE)) != MMSYSERR_NOTSUPPORTED)
2298         return result;
2299
2300     if (lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
2301         return WAVERR_STILLPLAYING;
2302
2303     lpWaveOutHdr->dwFlags &= ~WHDR_PREPARED;
2304     lpWaveOutHdr->dwFlags |= WHDR_DONE;
2305
2306     return MMSYSERR_NOERROR;
2307 }
2308
2309 /**************************************************************************
2310  *                              waveOutWrite            [WINMM.@]
2311  */
2312 UINT WINAPI waveOutWrite(HWAVEOUT hWaveOut, LPWAVEHDR lpWaveOutHdr,
2313                          UINT uSize)
2314 {
2315     LPWINE_MLD          wmld;
2316
2317     TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
2318
2319     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2320         return MMSYSERR_INVALHANDLE;
2321
2322     return MMDRV_Message(wmld, WODM_WRITE, (DWORD_PTR)lpWaveOutHdr, uSize, TRUE);
2323 }
2324
2325 /**************************************************************************
2326  *                              waveOutBreakLoop        [WINMM.@]
2327  */
2328 UINT WINAPI waveOutBreakLoop(HWAVEOUT hWaveOut)
2329 {
2330     LPWINE_MLD          wmld;
2331
2332     TRACE("(%p);\n", hWaveOut);
2333
2334     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2335         return MMSYSERR_INVALHANDLE;
2336     return MMDRV_Message(wmld, WODM_BREAKLOOP, 0L, 0L, TRUE);
2337 }
2338
2339 /**************************************************************************
2340  *                              waveOutPause            [WINMM.@]
2341  */
2342 UINT WINAPI waveOutPause(HWAVEOUT hWaveOut)
2343 {
2344     LPWINE_MLD          wmld;
2345
2346     TRACE("(%p);\n", hWaveOut);
2347
2348     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2349         return MMSYSERR_INVALHANDLE;
2350     return MMDRV_Message(wmld, WODM_PAUSE, 0L, 0L, TRUE);
2351 }
2352
2353 /**************************************************************************
2354  *                              waveOutReset            [WINMM.@]
2355  */
2356 UINT WINAPI waveOutReset(HWAVEOUT hWaveOut)
2357 {
2358     LPWINE_MLD          wmld;
2359
2360     TRACE("(%p);\n", hWaveOut);
2361
2362     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2363         return MMSYSERR_INVALHANDLE;
2364     return MMDRV_Message(wmld, WODM_RESET, 0L, 0L, TRUE);
2365 }
2366
2367 /**************************************************************************
2368  *                              waveOutRestart          [WINMM.@]
2369  */
2370 UINT WINAPI waveOutRestart(HWAVEOUT hWaveOut)
2371 {
2372     LPWINE_MLD          wmld;
2373
2374     TRACE("(%p);\n", hWaveOut);
2375
2376     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2377         return MMSYSERR_INVALHANDLE;
2378     return MMDRV_Message(wmld, WODM_RESTART, 0L, 0L, TRUE);
2379 }
2380
2381 /**************************************************************************
2382  *                              waveOutGetPosition      [WINMM.@]
2383  */
2384 UINT WINAPI waveOutGetPosition(HWAVEOUT hWaveOut, LPMMTIME lpTime,
2385                                UINT uSize)
2386 {
2387     LPWINE_MLD          wmld;
2388
2389     TRACE("(%p, %p, %u);\n", hWaveOut, lpTime, uSize);
2390
2391     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2392         return MMSYSERR_INVALHANDLE;
2393
2394     return MMDRV_Message(wmld, WODM_GETPOS, (DWORD_PTR)lpTime, uSize, TRUE);
2395 }
2396
2397 /**************************************************************************
2398  *                              waveOutGetPitch         [WINMM.@]
2399  */
2400 UINT WINAPI waveOutGetPitch(HWAVEOUT hWaveOut, LPDWORD lpdw)
2401 {
2402     LPWINE_MLD          wmld;
2403
2404     TRACE("(%p, %p);\n", hWaveOut, lpdw);
2405
2406     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2407         return MMSYSERR_INVALHANDLE;
2408     return MMDRV_Message(wmld, WODM_GETPITCH, (DWORD_PTR)lpdw, 0L, TRUE);
2409 }
2410
2411 /**************************************************************************
2412  *                              waveOutSetPitch         [WINMM.@]
2413  */
2414 UINT WINAPI waveOutSetPitch(HWAVEOUT hWaveOut, DWORD dw)
2415 {
2416     LPWINE_MLD          wmld;
2417
2418     TRACE("(%p, %08x);\n", hWaveOut, dw);
2419
2420     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2421         return MMSYSERR_INVALHANDLE;
2422     return MMDRV_Message(wmld, WODM_SETPITCH, dw, 0L, TRUE);
2423 }
2424
2425 /**************************************************************************
2426  *                              waveOutGetPlaybackRate  [WINMM.@]
2427  */
2428 UINT WINAPI waveOutGetPlaybackRate(HWAVEOUT hWaveOut, LPDWORD lpdw)
2429 {
2430     LPWINE_MLD          wmld;
2431
2432     TRACE("(%p, %p);\n", hWaveOut, lpdw);
2433
2434     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2435         return MMSYSERR_INVALHANDLE;
2436     return MMDRV_Message(wmld, WODM_GETPLAYBACKRATE, (DWORD_PTR)lpdw, 0L, TRUE);
2437 }
2438
2439 /**************************************************************************
2440  *                              waveOutSetPlaybackRate  [WINMM.@]
2441  */
2442 UINT WINAPI waveOutSetPlaybackRate(HWAVEOUT hWaveOut, DWORD dw)
2443 {
2444     LPWINE_MLD          wmld;
2445
2446     TRACE("(%p, %08x);\n", hWaveOut, dw);
2447
2448     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2449         return MMSYSERR_INVALHANDLE;
2450     return MMDRV_Message(wmld, WODM_SETPLAYBACKRATE, dw, 0L, TRUE);
2451 }
2452
2453 /**************************************************************************
2454  *                              waveOutGetVolume        [WINMM.@]
2455  */
2456 UINT WINAPI waveOutGetVolume(HWAVEOUT hWaveOut, LPDWORD lpdw)
2457 {
2458     LPWINE_MLD          wmld;
2459
2460     TRACE("(%p, %p);\n", hWaveOut, lpdw);
2461
2462     if (lpdw == NULL) {
2463         WARN("invalid parameter\n");
2464         return MMSYSERR_INVALPARAM;
2465     }
2466
2467     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) == NULL)
2468         return MMSYSERR_INVALHANDLE;
2469
2470     return MMDRV_Message(wmld, WODM_GETVOLUME, (DWORD_PTR)lpdw, 0L, TRUE);
2471 }
2472
2473 /**************************************************************************
2474  *                              waveOutSetVolume        [WINMM.@]
2475  */
2476 UINT WINAPI waveOutSetVolume(HWAVEOUT hWaveOut, DWORD dw)
2477 {
2478     LPWINE_MLD          wmld;
2479
2480     TRACE("(%p, %08x);\n", hWaveOut, dw);
2481
2482      if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) == NULL)
2483         return MMSYSERR_INVALHANDLE;
2484
2485     return MMDRV_Message(wmld, WODM_SETVOLUME, dw, 0L, TRUE);
2486 }
2487
2488 /**************************************************************************
2489  *                              waveOutGetID            [WINMM.@]
2490  */
2491 UINT WINAPI waveOutGetID(HWAVEOUT hWaveOut, UINT* lpuDeviceID)
2492 {
2493     LPWINE_MLD          wmld;
2494
2495     TRACE("(%p, %p);\n", hWaveOut, lpuDeviceID);
2496
2497     if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
2498
2499     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2500         return MMSYSERR_INVALHANDLE;
2501
2502     *lpuDeviceID = wmld->uDeviceID;
2503     return 0;
2504 }
2505
2506 /**************************************************************************
2507  *                              waveOutMessage          [WINMM.@]
2508  */
2509 UINT WINAPI waveOutMessage(HWAVEOUT hWaveOut, UINT uMessage,
2510                            DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2511 {
2512     LPWINE_MLD          wmld;
2513
2514     TRACE("(%p, %u, %ld, %ld)\n", hWaveOut, uMessage, dwParam1, dwParam2);
2515
2516     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) {
2517         if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) != NULL) {
2518             return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
2519         }
2520         WARN("invalid handle\n");
2521         return MMSYSERR_INVALHANDLE;
2522     }
2523
2524     /* from M$ KB */
2525     if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER)) {
2526         WARN("invalid parameter\n");
2527         return MMSYSERR_INVALPARAM;
2528     }
2529
2530     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
2531 }
2532
2533 /**************************************************************************
2534  *                              waveInGetNumDevs                [WINMM.@]
2535  */
2536 UINT WINAPI waveInGetNumDevs(void)
2537 {
2538     return MMDRV_GetNum(MMDRV_WAVEIN);
2539 }
2540
2541 /**************************************************************************
2542  *                              waveInGetDevCapsW               [WINMM.@]
2543  */
2544 UINT WINAPI waveInGetDevCapsW(UINT_PTR uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)
2545 {
2546     LPWINE_MLD          wmld;
2547
2548     TRACE("(%lu %p %u)!\n", uDeviceID, lpCaps, uSize);
2549
2550     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2551
2552     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_WAVEIN, TRUE)) == NULL)
2553         return MMSYSERR_BADDEVICEID;
2554
2555     return MMDRV_Message(wmld, WIDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
2556 }
2557
2558 /**************************************************************************
2559  *                              waveInGetDevCapsA               [WINMM.@]
2560  */
2561 UINT WINAPI waveInGetDevCapsA(UINT_PTR uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)
2562 {
2563     WAVEINCAPSW         wicW;
2564     UINT                ret;
2565
2566     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2567
2568     ret = waveInGetDevCapsW(uDeviceID, &wicW, sizeof(wicW));
2569
2570     if (ret == MMSYSERR_NOERROR) {
2571         WAVEINCAPSA wicA;
2572         wicA.wMid           = wicW.wMid;
2573         wicA.wPid           = wicW.wPid;
2574         wicA.vDriverVersion = wicW.vDriverVersion;
2575         WideCharToMultiByte( CP_ACP, 0, wicW.szPname, -1, wicA.szPname,
2576                              sizeof(wicA.szPname), NULL, NULL );
2577         wicA.dwFormats      = wicW.dwFormats;
2578         wicA.wChannels      = wicW.wChannels;
2579         memcpy(lpCaps, &wicA, min(uSize, sizeof(wicA)));
2580     }
2581     return ret;
2582 }
2583
2584 /**************************************************************************
2585  *                              waveInOpen                      [WINMM.@]
2586  */
2587 MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
2588                        LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2589                        DWORD_PTR dwInstance, DWORD dwFlags)
2590 {
2591     return WAVE_Open((HANDLE*)lphWaveIn, uDeviceID, MMDRV_WAVEIN, lpFormat,
2592                      dwCallback, dwInstance, dwFlags, TRUE);
2593 }
2594
2595 /**************************************************************************
2596  *                              waveInClose                     [WINMM.@]
2597  */
2598 UINT WINAPI waveInClose(HWAVEIN hWaveIn)
2599 {
2600     LPWINE_MLD          wmld;
2601     DWORD               dwRet;
2602
2603     TRACE("(%p)\n", hWaveIn);
2604
2605     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2606         return MMSYSERR_INVALHANDLE;
2607
2608     dwRet = MMDRV_Message(wmld, WIDM_CLOSE, 0L, 0L, TRUE);
2609     if (dwRet != WAVERR_STILLPLAYING)
2610         MMDRV_Free(hWaveIn, wmld);
2611     return dwRet;
2612 }
2613
2614 /**************************************************************************
2615  *                              waveInPrepareHeader             [WINMM.@]
2616  */
2617 UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2618                                 UINT uSize)
2619 {
2620     LPWINE_MLD          wmld;
2621     UINT                result;
2622
2623     TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
2624
2625     if (lpWaveInHdr == NULL || uSize < sizeof (WAVEHDR))
2626         return MMSYSERR_INVALPARAM;
2627
2628     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2629         return MMSYSERR_INVALHANDLE;
2630
2631     if ((result = MMDRV_Message(wmld, WIDM_PREPARE, (DWORD_PTR)lpWaveInHdr,
2632                                 uSize, TRUE)) != MMSYSERR_NOTSUPPORTED)
2633         return result;
2634
2635     if (lpWaveInHdr->dwFlags & WHDR_INQUEUE)
2636         return WAVERR_STILLPLAYING;
2637
2638     lpWaveInHdr->dwFlags |= WHDR_PREPARED;
2639     lpWaveInHdr->dwFlags &= ~WHDR_DONE;
2640     lpWaveInHdr->dwBytesRecorded = 0;
2641
2642     return MMSYSERR_NOERROR;
2643 }
2644
2645 /**************************************************************************
2646  *                              waveInUnprepareHeader   [WINMM.@]
2647  */
2648 UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2649                                   UINT uSize)
2650 {
2651     LPWINE_MLD          wmld;
2652     UINT                result;
2653
2654     TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
2655
2656     if (lpWaveInHdr == NULL || uSize < sizeof (WAVEHDR))
2657         return MMSYSERR_INVALPARAM;
2658
2659     if (!(lpWaveInHdr->dwFlags & WHDR_PREPARED))
2660         return MMSYSERR_NOERROR;
2661
2662     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2663         return MMSYSERR_INVALHANDLE;
2664
2665     if ((result = MMDRV_Message(wmld, WIDM_UNPREPARE, (DWORD_PTR)lpWaveInHdr,
2666                                 uSize, TRUE)) != MMSYSERR_NOTSUPPORTED)
2667         return result;
2668
2669     if (lpWaveInHdr->dwFlags & WHDR_INQUEUE)
2670         return WAVERR_STILLPLAYING;
2671
2672     lpWaveInHdr->dwFlags &= ~WHDR_PREPARED;
2673     lpWaveInHdr->dwFlags |= WHDR_DONE;
2674
2675     return MMSYSERR_NOERROR;
2676 }
2677
2678 /**************************************************************************
2679  *                              waveInAddBuffer         [WINMM.@]
2680  */
2681 UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn,
2682                             WAVEHDR* lpWaveInHdr, UINT uSize)
2683 {
2684     LPWINE_MLD          wmld;
2685
2686     TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
2687
2688     if (lpWaveInHdr == NULL) return MMSYSERR_INVALPARAM;
2689     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2690         return MMSYSERR_INVALHANDLE;
2691
2692     return MMDRV_Message(wmld, WIDM_ADDBUFFER, (DWORD_PTR)lpWaveInHdr, uSize, TRUE);
2693 }
2694
2695 /**************************************************************************
2696  *                              waveInReset             [WINMM.@]
2697  */
2698 UINT WINAPI waveInReset(HWAVEIN hWaveIn)
2699 {
2700     LPWINE_MLD          wmld;
2701
2702     TRACE("(%p);\n", hWaveIn);
2703
2704     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2705         return MMSYSERR_INVALHANDLE;
2706
2707     return MMDRV_Message(wmld, WIDM_RESET, 0L, 0L, TRUE);
2708 }
2709
2710 /**************************************************************************
2711  *                              waveInStart             [WINMM.@]
2712  */
2713 UINT WINAPI waveInStart(HWAVEIN hWaveIn)
2714 {
2715     LPWINE_MLD          wmld;
2716
2717     TRACE("(%p);\n", hWaveIn);
2718
2719     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2720         return MMSYSERR_INVALHANDLE;
2721
2722     return MMDRV_Message(wmld, WIDM_START, 0L, 0L, TRUE);
2723 }
2724
2725 /**************************************************************************
2726  *                              waveInStop              [WINMM.@]
2727  */
2728 UINT WINAPI waveInStop(HWAVEIN hWaveIn)
2729 {
2730     LPWINE_MLD          wmld;
2731
2732     TRACE("(%p);\n", hWaveIn);
2733
2734     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2735         return MMSYSERR_INVALHANDLE;
2736
2737     return MMDRV_Message(wmld,WIDM_STOP, 0L, 0L, TRUE);
2738 }
2739
2740 /**************************************************************************
2741  *                              waveInGetPosition       [WINMM.@]
2742  */
2743 UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,
2744                               UINT uSize)
2745 {
2746     LPWINE_MLD          wmld;
2747
2748     TRACE("(%p, %p, %u);\n", hWaveIn, lpTime, uSize);
2749
2750     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2751         return MMSYSERR_INVALHANDLE;
2752
2753     return MMDRV_Message(wmld, WIDM_GETPOS, (DWORD_PTR)lpTime, uSize, TRUE);
2754 }
2755
2756 /**************************************************************************
2757  *                              waveInGetID                     [WINMM.@]
2758  */
2759 UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)
2760 {
2761     LPWINE_MLD          wmld;
2762
2763     TRACE("(%p, %p);\n", hWaveIn, lpuDeviceID);
2764
2765     if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
2766
2767     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2768         return MMSYSERR_INVALHANDLE;
2769
2770     *lpuDeviceID = wmld->uDeviceID;
2771     return MMSYSERR_NOERROR;
2772 }
2773
2774 /**************************************************************************
2775  *                              waveInMessage           [WINMM.@]
2776  */
2777 UINT WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
2778                           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2779 {
2780     LPWINE_MLD          wmld;
2781
2782     TRACE("(%p, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
2783
2784     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) {
2785         if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, TRUE)) != NULL) {
2786             return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
2787         }
2788         return MMSYSERR_INVALHANDLE;
2789     }
2790
2791     /* from M$ KB */
2792     if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER))
2793         return MMSYSERR_INVALPARAM;
2794
2795
2796     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
2797 }
2798
2799 struct mm_starter
2800 {
2801     LPTASKCALLBACK      cb;
2802     DWORD               client;
2803     HANDLE              event;
2804 };
2805
2806 static DWORD WINAPI mmTaskRun(void* pmt)
2807 {
2808     struct mm_starter mms;
2809
2810     memcpy(&mms, pmt, sizeof(struct mm_starter));
2811     HeapFree(GetProcessHeap(), 0, pmt);
2812     mms.cb(mms.client);
2813     if (mms.event) SetEvent(mms.event);
2814     return 0;
2815 }
2816
2817 /******************************************************************
2818  *              mmTaskCreate (WINMM.@)
2819  */
2820 UINT     WINAPI mmTaskCreate(LPTASKCALLBACK cb, HANDLE* ph, DWORD_PTR client)
2821 {
2822     HANDLE               hThread;
2823     HANDLE               hEvent = 0;
2824     struct mm_starter   *mms;
2825
2826     mms = HeapAlloc(GetProcessHeap(), 0, sizeof(struct mm_starter));
2827     if (mms == NULL) return TASKERR_OUTOFMEMORY;
2828
2829     mms->cb = cb;
2830     mms->client = client;
2831     if (ph) hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
2832     mms->event = hEvent;
2833
2834     hThread = CreateThread(0, 0, mmTaskRun, mms, 0, NULL);
2835     if (!hThread) {
2836         HeapFree(GetProcessHeap(), 0, mms);
2837         if (hEvent) CloseHandle(hEvent);
2838         return TASKERR_OUTOFMEMORY;
2839     }
2840     SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
2841     if (ph) *ph = hEvent;
2842     CloseHandle(hThread);
2843     return 0;
2844 }
2845
2846 /******************************************************************
2847  *              mmTaskBlock (WINMM.@)
2848  */
2849 VOID     WINAPI mmTaskBlock(DWORD tid)
2850 {
2851     MSG         msg;
2852
2853     do
2854     {
2855         GetMessageA(&msg, 0, 0, 0);
2856         if (msg.hwnd) DispatchMessageA(&msg);
2857     } while (msg.message != WM_USER);
2858 }
2859
2860 /******************************************************************
2861  *              mmTaskSignal (WINMM.@)
2862  */
2863 BOOL     WINAPI mmTaskSignal(DWORD tid)
2864 {
2865     return PostThreadMessageW(tid, WM_USER, 0, 0);
2866 }
2867
2868 /******************************************************************
2869  *              mmTaskYield (WINMM.@)
2870  */
2871 VOID     WINAPI mmTaskYield(VOID) {}
2872
2873 /******************************************************************
2874  *              mmGetCurrentTask (WINMM.@)
2875  */
2876 DWORD    WINAPI mmGetCurrentTask(VOID)
2877 {
2878     return GetCurrentThreadId();
2879 }