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