d3drm: Avoid LPDIRECT3DRMVISUAL.
[wine] / dlls / midimap / midimap.c
1 /*
2  * Wine MIDI mapper driver
3  *
4  * Copyright    1999, 2000, 2001 Eric Pouech
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * TODO:
21  *      notification has to be implemented
22  *      IDF file loading
23  */
24
25 #include <stdarg.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include "windef.h"
29 #include "winbase.h"
30 #include "wingdi.h"
31 #include "winuser.h"
32 #include "mmddk.h"
33 #include "winreg.h"
34 #include "wine/unicode.h"
35 #include "wine/debug.h"
36
37 /*
38  * Here's how Windows stores the midiOut mapping information.
39  *
40  * Full form (in HKU) is:
41  *
42  * [Software\\Microsoft\\Windows\\CurrentVersion\\Multimedia\\MIDIMap] 988836060
43  * "AutoScheme"=dword:00000000
44  * "ConfigureCount"=dword:00000004
45  * "CurrentInstrument"="Wine OSS midi"
46  * "CurrentScheme"="epp"
47  * "DriverList"=""
48  * "UseScheme"=dword:00000000
49  *
50  * AutoScheme:          ?
51  * CurrentInstrument:   name of midiOut device to use when UseScheme is 0. Wine uses an extension
52  *                      of the form #n to link to n'th midiOut device of the system
53  * CurrentScheme:       when UseScheme is non null, it's the scheme to use (see below)
54  * DriverList:          ?
55  * UseScheme:           trigger for simple/complex mapping
56  *
57  * A scheme is defined (in HKLM) as:
58  *
59  * [System\\CurrentControlSet\\Control\\MediaProperties\\PrivateProperties\\Midi\\Schemes\\<nameScheme>]
60  * <nameScheme>:        one key for each defined scheme (system wide)
61  * under each one of these <nameScheme> keys, there's:
62  * [...\\<nameScheme>\\<idxDevice>]
63  * "Channels"="<bitMask>"
64  * (the default value of this key also refers to the name of the device).
65  *
66  * this defines, for each midiOut device (identified by its index in <idxDevice>), which
67  * channels have to be mapped onto it. The <bitMask> defines the channels (from 0 to 15)
68  * will be mapped (mapping occurs for channel <ch> if bit <ch> is set in <bitMask>
69  *
70  * Further mapping information can also be defined in:
71  * [System\\CurrentControlSet\\Control\\MediaProperties\\PrivateProperties\\Midi\\Ports\\<nameDevice>\\Instruments\\<idx>]
72  * "Definition"="<.idf file>"
73  * "FriendlyName"="#for .idx file#"
74  * "Port"="<idxPort>"
75  *
76  * This last part isn't implemented (.idf file support).
77  */
78
79 WINE_DEFAULT_DEBUG_CHANNEL(msacm);
80
81 typedef struct tagMIDIOUTPORT
82 {
83     WCHAR               name[MAXPNAMELEN];
84     int                 loaded;
85     HMIDIOUT            hMidi;
86     unsigned short      uDevID;
87     LPBYTE              lpbPatch;
88     unsigned int        aChn[16];
89 } MIDIOUTPORT;
90
91 typedef struct tagMIDIMAPDATA
92 {
93     struct tagMIDIMAPDATA*      self;
94     MIDIOUTPORT*        ChannelMap[16];
95     MIDIOPENDESC        midiDesc;
96     WORD                wCbFlags;
97 } MIDIMAPDATA;
98
99 static  MIDIOUTPORT*    midiOutPorts;
100 static  unsigned        numMidiOutPorts;
101
102 static  BOOL    MIDIMAP_IsBadData(MIDIMAPDATA* mm)
103 {
104     if (!IsBadReadPtr(mm, sizeof(MIDIMAPDATA)) && mm->self == mm)
105         return FALSE;
106     TRACE("Bad midimap data (%p)\n", mm);
107     return TRUE;
108 }
109
110 static BOOL     MIDIMAP_FindPort(const WCHAR* name, unsigned* dev)
111 {
112     for (*dev = 0; *dev < numMidiOutPorts; (*dev)++)
113     {
114         TRACE("%s\n", wine_dbgstr_w(midiOutPorts[*dev].name));
115         if (strcmpW(midiOutPorts[*dev].name, name) == 0)
116             return TRUE;
117     }
118     /* try the form #nnn */
119     if (*name == '#' && isdigit(name[1]))
120     {
121         const WCHAR*  ptr = name + 1;
122         *dev = 0;
123         do 
124         {
125             *dev = *dev * 10 + *ptr - '0';
126         } while (isdigit(*++ptr));
127         if (*dev < numMidiOutPorts)
128             return TRUE;
129     }
130     return FALSE;
131 }
132
133 static BOOL     MIDIMAP_LoadSettingsDefault(MIDIMAPDATA* mom, const WCHAR* port)
134 {
135     unsigned i, dev = 0;
136
137     if (port != NULL && !MIDIMAP_FindPort(port, &dev))
138     {
139         ERR("Registry glitch: couldn't find midi out (%s)\n", wine_dbgstr_w(port));
140         dev = 0;
141     }
142
143     /* this is necessary when no midi out ports are present */
144     if (dev >= numMidiOutPorts)
145         return FALSE;
146     /* sets default */
147     for (i = 0; i < 16; i++) mom->ChannelMap[i] = &midiOutPorts[dev];
148
149     return TRUE;
150 }
151
152 static BOOL     MIDIMAP_LoadSettingsScheme(MIDIMAPDATA* mom, const WCHAR* scheme)
153 {
154     HKEY        hSchemesKey, hKey, hPortKey;
155     unsigned    i, idx, dev;
156     WCHAR       buffer[256], port[256];
157     DWORD       type, size, mask;
158
159     for (i = 0; i < 16; i++)    mom->ChannelMap[i] = NULL;
160
161     if (RegOpenKeyA(HKEY_LOCAL_MACHINE,
162                     "System\\CurrentControlSet\\Control\\MediaProperties\\PrivateProperties\\Midi\\Schemes",
163                     &hSchemesKey))
164     {
165         return FALSE;
166     }
167     if (RegOpenKeyW(hSchemesKey, scheme, &hKey))
168     {
169         RegCloseKey(hSchemesKey);
170         return FALSE;
171     }
172
173     for (idx = 0; !RegEnumKeyW(hKey, idx, buffer, sizeof(buffer)/sizeof(buffer[0])); idx++)
174     {
175         if (RegOpenKeyW(hKey, buffer, &hPortKey)) continue;
176
177         size = sizeof(port);
178         if (RegQueryValueExW(hPortKey, NULL, 0, &type, (void*)port, &size)) continue;
179
180         if (!MIDIMAP_FindPort(port, &dev)) continue;
181
182         size = sizeof(mask);
183         if (RegQueryValueExA(hPortKey, "Channels", 0, &type, (void*)&mask, &size))
184             continue;
185
186         for (i = 0; i < 16; i++)
187         {
188             if (mask & (1 << i))
189             {
190                 if (mom->ChannelMap[i])
191                     ERR("Quirks in registry, channel %u is mapped twice\n", i);
192                 mom->ChannelMap[i] = &midiOutPorts[dev];
193             }
194         }
195     }
196
197     RegCloseKey(hSchemesKey);
198     RegCloseKey(hKey);
199
200     return TRUE;
201 }
202
203 static BOOL     MIDIMAP_LoadSettings(MIDIMAPDATA* mom)
204 {
205     HKEY        hKey;
206     BOOL        ret;
207
208     if (RegOpenKeyA(HKEY_CURRENT_USER,
209                     "Software\\Microsoft\\Windows\\CurrentVersion\\Multimedia\\MIDIMap", &hKey))
210     {
211         ret = MIDIMAP_LoadSettingsDefault(mom, NULL);
212     }
213     else
214     {
215         DWORD   type, size, out;
216         WCHAR   buffer[256];
217
218         ret = 2;
219         size = sizeof(out);
220         if (!RegQueryValueExA(hKey, "UseScheme", 0, &type, (void*)&out, &size) && out)
221         {
222             static const WCHAR cs[] = {'C','u','r','r','e','n','t','S','c','h','e','m','e',0};
223             size = sizeof(buffer);
224             if (!RegQueryValueExW(hKey, cs, 0, &type, (void*)buffer, &size))
225             {
226                 if (!(ret = MIDIMAP_LoadSettingsScheme(mom, buffer)))
227                     ret = MIDIMAP_LoadSettingsDefault(mom, NULL);
228             }
229             else
230             {
231                 ERR("Wrong registry: UseScheme is active, but no CurrentScheme found\n");
232             }
233         }
234         if (ret == 2)
235         {
236             static const WCHAR ci[] = {'C','u','r','r','e','n','t','I','n','s','t','r','u','m','e','n','t',0};
237             size = sizeof(buffer);
238             if (!RegQueryValueExW(hKey, ci, 0, &type, (void*)buffer, &size) && *buffer)
239             {
240                 ret = MIDIMAP_LoadSettingsDefault(mom, buffer);
241             }
242             else
243             {
244                 ret = MIDIMAP_LoadSettingsDefault(mom, NULL);
245             }
246         }
247     }
248     RegCloseKey(hKey);
249
250     if (ret && TRACE_ON(msacm))
251     {
252         unsigned        i;
253
254         for (i = 0; i < 16; i++)
255         {
256             TRACE("chnMap[%2d] => %d\n",
257                   i, mom->ChannelMap[i] ? mom->ChannelMap[i]->uDevID : -1);
258         }
259     }
260     return ret;
261 }
262
263 static void MIDIMAP_NotifyClient(MIDIMAPDATA* mom, WORD wMsg,
264                                  DWORD_PTR dwParam1, DWORD_PTR dwParam2)
265 {
266     DriverCallback(mom->midiDesc.dwCallback, mom->wCbFlags, (HDRVR)mom->midiDesc.hMidi,
267                    wMsg, mom->midiDesc.dwInstance, dwParam1, dwParam2);
268 }
269
270 static DWORD modOpen(DWORD_PTR *lpdwUser, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
271 {
272     MIDIMAPDATA*        mom = HeapAlloc(GetProcessHeap(), 0, sizeof(MIDIMAPDATA));
273
274     TRACE("(%p %p %08x)\n", lpdwUser, lpDesc, dwFlags);
275
276     if (!mom) return MMSYSERR_NOMEM;
277     if (!lpDesc) {
278         HeapFree(GetProcessHeap(), 0, mom);
279         return MMSYSERR_INVALPARAM;
280     }
281
282     if (MIDIMAP_LoadSettings(mom))
283     {
284         *lpdwUser = (DWORD_PTR)mom;
285         mom->self = mom;
286
287         mom->wCbFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
288         mom->midiDesc = *lpDesc;
289         MIDIMAP_NotifyClient(mom, MOM_OPEN, 0L, 0L);
290
291         return MMSYSERR_NOERROR;
292     }
293     HeapFree(GetProcessHeap(), 0, mom);
294     return MIDIERR_INVALIDSETUP;
295 }
296
297 static  DWORD   modClose(MIDIMAPDATA* mom)
298 {
299     UINT        i;
300     DWORD       ret = MMSYSERR_NOERROR;
301
302     if (MIDIMAP_IsBadData(mom))         return MMSYSERR_ERROR;
303
304     for (i = 0; i < 16; i++)
305     {
306         DWORD   t;
307         if (mom->ChannelMap[i] && mom->ChannelMap[i]->loaded > 0)
308         {
309             t = midiOutClose(mom->ChannelMap[i]->hMidi);
310             if (t == MMSYSERR_NOERROR)
311             {
312                 mom->ChannelMap[i]->loaded = 0;
313                 mom->ChannelMap[i]->hMidi = 0;
314             }
315             else if (ret == MMSYSERR_NOERROR)
316                 ret = t;
317         }
318     }
319     if (ret == MMSYSERR_NOERROR) {
320         MIDIMAP_NotifyClient(mom, MOM_CLOSE, 0L, 0L);
321         HeapFree(GetProcessHeap(), 0, mom);
322     }
323     return ret;
324 }
325
326 static DWORD modLongData(MIDIMAPDATA* mom, LPMIDIHDR lpMidiHdr, DWORD_PTR dwParam2)
327 {
328     WORD        chn;
329     DWORD       ret = MMSYSERR_NOERROR;
330     MIDIHDR     mh;
331
332     if (MIDIMAP_IsBadData(mom))
333         return MMSYSERR_ERROR;
334     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
335         return MIDIERR_UNPREPARED;
336     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
337         return MIDIERR_STILLPLAYING;
338
339     mh = *lpMidiHdr;
340     lpMidiHdr->dwFlags &= ~MHDR_DONE;
341     lpMidiHdr->dwFlags |= MHDR_INQUEUE;
342     for (chn = 0; chn < 16; chn++)
343     {
344         if (mom->ChannelMap[chn] && mom->ChannelMap[chn]->loaded > 0)
345         {
346             mh.dwFlags = 0;
347             midiOutPrepareHeader(mom->ChannelMap[chn]->hMidi, &mh, sizeof(mh));
348             ret = midiOutLongMsg(mom->ChannelMap[chn]->hMidi, &mh, sizeof(mh));
349             /* As of 2009, wineXYZ.drv's LongData handlers are synchronous */
350             if (!ret && !(mh.dwFlags & MHDR_DONE))
351                 FIXME("wait until MHDR_DONE\n");
352             midiOutUnprepareHeader(mom->ChannelMap[chn]->hMidi, &mh, sizeof(mh));
353             if (ret != MMSYSERR_NOERROR) break;
354         }
355     }
356     lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
357     lpMidiHdr->dwFlags |= MHDR_DONE;
358     MIDIMAP_NotifyClient(mom, MOM_DONE, (DWORD_PTR)lpMidiHdr, 0L);
359     return ret;
360 }
361
362 static DWORD modData(MIDIMAPDATA* mom, DWORD_PTR dwParam)
363 {
364     BYTE        lb = LOBYTE(LOWORD(dwParam));
365     WORD        chn = lb & 0x0F;
366     DWORD       ret = MMSYSERR_NOERROR;
367
368     if (MIDIMAP_IsBadData(mom))
369         return MMSYSERR_ERROR;
370
371     if (!mom->ChannelMap[chn]) return MMSYSERR_NOERROR;
372
373     switch (lb & 0xF0)
374     {
375     case 0x80:
376     case 0x90:
377     case 0xA0:
378     case 0xB0:
379     case 0xC0:
380     case 0xD0:
381     case 0xE0:
382         if (mom->ChannelMap[chn]->loaded == 0)
383         {
384             if (midiOutOpen(&mom->ChannelMap[chn]->hMidi, mom->ChannelMap[chn]->uDevID,
385                             0L, 0L, CALLBACK_NULL) == MMSYSERR_NOERROR)
386                 mom->ChannelMap[chn]->loaded = 1;
387             else
388                 mom->ChannelMap[chn]->loaded = -1;
389             /* FIXME: should load here the IDF midi data... and allow channel and
390              * patch mappings
391              */
392         }
393         if (mom->ChannelMap[chn]->loaded > 0)
394         {
395             /* change channel */
396             dwParam &= ~0x0F;
397             dwParam |= mom->ChannelMap[chn]->aChn[chn];
398
399             if ((LOBYTE(LOWORD(dwParam)) & 0xF0) == 0xC0 /* program change */ &&
400                 mom->ChannelMap[chn]->lpbPatch)
401             {
402                 BYTE patch = HIBYTE(LOWORD(dwParam));
403
404                 /* change patch */
405                 dwParam &= ~0x0000FF00;
406                 dwParam |= mom->ChannelMap[chn]->lpbPatch[patch];
407             }
408             ret = midiOutShortMsg(mom->ChannelMap[chn]->hMidi, dwParam);
409         }
410         break;
411     case 0xF0:
412         for (chn = 0; chn < 16; chn++)
413         {
414             if (mom->ChannelMap[chn]->loaded > 0)
415                 ret = midiOutShortMsg(mom->ChannelMap[chn]->hMidi, dwParam);
416         }
417         break;
418     default:
419         FIXME("ooch %lx\n", dwParam);
420     }
421
422     return ret;
423 }
424
425 static DWORD modPrepare(MIDIMAPDATA* mom, LPMIDIHDR lpMidiHdr, DWORD_PTR dwSize)
426 {
427     if (MIDIMAP_IsBadData(mom)) return MMSYSERR_ERROR;
428     if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
429         return MMSYSERR_INVALPARAM;
430     if (lpMidiHdr->dwFlags & MHDR_PREPARED)
431         return MMSYSERR_NOERROR;
432
433     lpMidiHdr->dwFlags |= MHDR_PREPARED;
434     lpMidiHdr->dwFlags &= ~(MHDR_DONE|MHDR_INQUEUE); /* flags cleared since w2k */
435     return MMSYSERR_NOERROR;
436 }
437
438 static DWORD modUnprepare(MIDIMAPDATA* mom, LPMIDIHDR lpMidiHdr, DWORD_PTR dwSize)
439 {
440     if (MIDIMAP_IsBadData(mom)) return MMSYSERR_ERROR;
441     if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
442         return MMSYSERR_INVALPARAM;
443     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
444         return MMSYSERR_NOERROR;
445     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
446         return MIDIERR_STILLPLAYING;
447
448     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
449     return MMSYSERR_NOERROR;
450 }
451
452 static DWORD modGetVolume(MIDIMAPDATA* mom, DWORD* lpdwVolume)
453 {
454     if (!lpdwVolume) return MMSYSERR_INVALPARAM;
455     *lpdwVolume = 0xFFFFFFFF; /* tests show this initial value */
456     return MMSYSERR_NOERROR;
457 }
458
459 static DWORD modSetVolume(MIDIMAPDATA* mom, DWORD dwVolume)
460 {
461     /* Native forwards it to some underlying device
462      * GetVolume returns what was last set here. */
463     FIXME("stub\n");
464     return MMSYSERR_NOERROR;
465 }
466
467 static DWORD modGetDevCaps(UINT wDevID, MIDIMAPDATA* mom, LPMIDIOUTCAPSW lpMidiCaps, DWORD_PTR size)
468 {
469     static const MIDIOUTCAPSW mappercaps = {
470         0x00FF, 0x0001, 0x0100, /* Manufacturer and Product ID */
471         {'W','i','n','e',' ','m','i','d','i',' ','m','a','p','p','e','r',0},
472         MOD_MAPPER, 0, 0, 0xFFFF,
473         /* Native returns volume caps of underlying device + MIDICAPS_STREAM */
474         MIDICAPS_VOLUME|MIDICAPS_LRVOLUME
475     };
476     if (lpMidiCaps == NULL) return MMSYSERR_INVALPARAM;
477     if (!numMidiOutPorts) return MMSYSERR_BADDEVICEID;
478
479     memcpy(lpMidiCaps, &mappercaps, min(size, sizeof(*lpMidiCaps)));
480     return MMSYSERR_NOERROR;
481 }
482
483 static  DWORD   modReset(MIDIMAPDATA* mom)
484 {
485     WORD        chn;
486     DWORD       ret = MMSYSERR_NOERROR;
487
488     if (MIDIMAP_IsBadData(mom))
489         return MMSYSERR_ERROR;
490
491     for (chn = 0; chn < 16; chn++)
492     {
493         if (mom->ChannelMap[chn] && mom->ChannelMap[chn]->loaded > 0)
494         {
495             ret = midiOutReset(mom->ChannelMap[chn]->hMidi);
496             if (ret != MMSYSERR_NOERROR) break;
497         }
498     }
499     return ret;
500 }
501
502 static LRESULT MIDIMAP_drvOpen(void);
503 static LRESULT MIDIMAP_drvClose(void);
504
505 /**************************************************************************
506  *                              modMessage (MIDIMAP.@)
507  */
508 DWORD WINAPI MIDIMAP_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
509                                 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
510 {
511     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
512           wDevID, wMsg, dwUser, dwParam1, dwParam2);
513
514     switch (wMsg)
515     {
516     case DRVM_INIT:
517         return MIDIMAP_drvOpen();
518     case DRVM_EXIT:
519         return MIDIMAP_drvClose();
520     case DRVM_ENABLE:
521     case DRVM_DISABLE:
522         /* FIXME: Pretend this is supported */
523         return 0;
524
525     case MODM_OPEN: return modOpen((DWORD_PTR *)dwUser, (LPMIDIOPENDESC)dwParam1, dwParam2);
526     case MODM_CLOSE:            return modClose         ((MIDIMAPDATA*)dwUser);
527
528     case MODM_DATA:             return modData          ((MIDIMAPDATA*)dwUser, dwParam1);
529     case MODM_LONGDATA:         return modLongData      ((MIDIMAPDATA*)dwUser, (LPMIDIHDR)dwParam1,     dwParam2);
530     case MODM_PREPARE:          return modPrepare       ((MIDIMAPDATA*)dwUser, (LPMIDIHDR)dwParam1,     dwParam2);
531     case MODM_UNPREPARE:        return modUnprepare     ((MIDIMAPDATA*)dwUser, (LPMIDIHDR)dwParam1,     dwParam2);
532     case MODM_RESET:            return modReset         ((MIDIMAPDATA*)dwUser);
533
534     case MODM_GETDEVCAPS:       return modGetDevCaps    (wDevID, (MIDIMAPDATA*)dwUser, (LPMIDIOUTCAPSW)dwParam1,dwParam2);
535     case MODM_GETNUMDEVS:       return 1;
536     case MODM_GETVOLUME:        return modGetVolume     ((MIDIMAPDATA*)dwUser, (DWORD*)dwParam1);
537     case MODM_SETVOLUME:        return modSetVolume     ((MIDIMAPDATA*)dwUser, dwParam1);
538     default:
539         FIXME("unknown message %d!\n", wMsg);
540     }
541     return MMSYSERR_NOTSUPPORTED;
542 }
543
544 /*======================================================================*
545  *                  Driver part                                         *
546  *======================================================================*/
547
548 /**************************************************************************
549  *                              MIDIMAP_drvOpen                 [internal]
550  */
551 static LRESULT MIDIMAP_drvOpen(void)
552 {
553     MIDIOUTCAPSW        moc;
554     unsigned            dev, i;
555
556     if (midiOutPorts)
557         return 0;
558
559     numMidiOutPorts = midiOutGetNumDevs();
560     midiOutPorts = HeapAlloc(GetProcessHeap(), 0,
561                              numMidiOutPorts * sizeof(MIDIOUTPORT));
562     for (dev = 0; dev < numMidiOutPorts; dev++)
563     {
564         if (midiOutGetDevCapsW(dev, &moc, sizeof(moc)) == 0L)
565         {
566             strcpyW(midiOutPorts[dev].name, moc.szPname);
567             midiOutPorts[dev].loaded = 0;
568             midiOutPorts[dev].hMidi = 0;
569             midiOutPorts[dev].uDevID = dev;
570             midiOutPorts[dev].lpbPatch = NULL;
571             for (i = 0; i < 16; i++)
572                 midiOutPorts[dev].aChn[i] = i;
573         }
574         else
575         {
576             midiOutPorts[dev].loaded = -1;
577         }
578     }
579
580     return 1;
581 }
582
583 /**************************************************************************
584  *                              MIDIMAP_drvClose                [internal]
585  */
586 static LRESULT MIDIMAP_drvClose(void)
587 {
588     if (midiOutPorts)
589     {
590         HeapFree(GetProcessHeap(), 0, midiOutPorts);
591         midiOutPorts = NULL;
592         return 1;
593     }
594     return 0;
595 }
596
597 /**************************************************************************
598  *                              DriverProc (MIDIMAP.@)
599  */
600 LRESULT CALLBACK MIDIMAP_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
601                                     LPARAM dwParam1, LPARAM dwParam2)
602 {
603 /* EPP     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",  */
604 /* EPP    dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
605
606     switch (wMsg)
607     {
608     case DRV_LOAD:              return 1;
609     case DRV_FREE:              return 1;
610     case DRV_OPEN:              return 1;
611     case DRV_CLOSE:             return 1;
612     case DRV_ENABLE:            return 1;
613     case DRV_DISABLE:           return 1;
614     case DRV_QUERYCONFIGURE:    return 1;
615     case DRV_CONFIGURE:         MessageBoxA(0, "MIDIMAP MultiMedia Driver !", "OSS Driver", MB_OK);     return 1;
616     case DRV_INSTALL:           return DRVCNF_RESTART;
617     case DRV_REMOVE:            return DRVCNF_RESTART;
618     default:
619         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
620     }
621 }