midimap: Defer initialisation.
[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 ||
429         lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE))
430         return MMSYSERR_INVALPARAM;
431
432     lpMidiHdr->dwFlags |= MHDR_PREPARED;
433     lpMidiHdr->dwFlags &= ~MHDR_DONE;
434     return MMSYSERR_NOERROR;
435 }
436
437 static DWORD modUnprepare(MIDIMAPDATA* mom, LPMIDIHDR lpMidiHdr, DWORD_PTR dwParam2)
438 {
439     if (MIDIMAP_IsBadData(mom)) return MMSYSERR_ERROR;
440     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
441     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
442
443     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
444     return MMSYSERR_NOERROR;
445 }
446
447 static DWORD modGetVolume(MIDIMAPDATA* mom, DWORD* lpdwVolume)
448 {
449     if (MIDIMAP_IsBadData(mom)) return MMSYSERR_ERROR;
450     if (!lpdwVolume) return MMSYSERR_INVALPARAM;
451     *lpdwVolume = 0xFFFFFFFF; /* tests show this initial value */
452     return MMSYSERR_NOERROR;
453 }
454
455 static DWORD modSetVolume(MIDIMAPDATA* mom, DWORD dwVolume)
456 {
457     /* Native forwards it to some underlying device
458      * GetVolume returns what was last set here. */
459     FIXME("stub\n");
460     return MMSYSERR_NOERROR;
461 }
462
463 static DWORD modGetDevCaps(UINT wDevID, MIDIMAPDATA* mom, LPMIDIOUTCAPSW lpMidiCaps, DWORD_PTR size)
464 {
465     static const MIDIOUTCAPSW mappercaps = {
466         0x00FF, 0x0001, 0x0100, /* Manufacturer and Product ID */
467         {'W','i','n','e',' ','m','i','d','i',' ','m','a','p','p','e','r',0},
468         MOD_MAPPER, 0, 0, 0xFFFF,
469         /* Native returns volume caps of underlying device + MIDICAPS_STREAM */
470         MIDICAPS_VOLUME|MIDICAPS_LRVOLUME
471     };
472     if (lpMidiCaps == NULL) return MMSYSERR_INVALPARAM;
473     if (!numMidiOutPorts) return MMSYSERR_BADDEVICEID;
474
475     memcpy(lpMidiCaps, &mappercaps, min(size, sizeof(*lpMidiCaps)));
476     return MMSYSERR_NOERROR;
477 }
478
479 static  DWORD   modReset(MIDIMAPDATA* mom)
480 {
481     WORD        chn;
482     DWORD       ret = MMSYSERR_NOERROR;
483
484     if (MIDIMAP_IsBadData(mom))
485         return MMSYSERR_ERROR;
486
487     for (chn = 0; chn < 16; chn++)
488     {
489         if (mom->ChannelMap[chn] && mom->ChannelMap[chn]->loaded > 0)
490         {
491             ret = midiOutReset(mom->ChannelMap[chn]->hMidi);
492             if (ret != MMSYSERR_NOERROR) break;
493         }
494     }
495     return ret;
496 }
497
498 static LRESULT MIDIMAP_drvOpen(void);
499 static LRESULT MIDIMAP_drvClose(void);
500
501 /**************************************************************************
502  *                              modMessage (MIDIMAP.@)
503  */
504 DWORD WINAPI MIDIMAP_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
505                                 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
506 {
507     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
508           wDevID, wMsg, dwUser, dwParam1, dwParam2);
509
510     switch (wMsg)
511     {
512     case DRVM_INIT:
513         return MIDIMAP_drvOpen();
514     case DRVM_EXIT:
515         return MIDIMAP_drvClose();
516     case DRVM_ENABLE:
517     case DRVM_DISABLE:
518         /* FIXME: Pretend this is supported */
519         return 0;
520
521     case MODM_OPEN: return modOpen((DWORD_PTR *)dwUser, (LPMIDIOPENDESC)dwParam1, dwParam2);
522     case MODM_CLOSE:            return modClose         ((MIDIMAPDATA*)dwUser);
523
524     case MODM_DATA:             return modData          ((MIDIMAPDATA*)dwUser, dwParam1);
525     case MODM_LONGDATA:         return modLongData      ((MIDIMAPDATA*)dwUser, (LPMIDIHDR)dwParam1,     dwParam2);
526     case MODM_PREPARE:          return modPrepare       ((MIDIMAPDATA*)dwUser, (LPMIDIHDR)dwParam1,     dwParam2);
527     case MODM_UNPREPARE:        return modUnprepare     ((MIDIMAPDATA*)dwUser, (LPMIDIHDR)dwParam1,     dwParam2);
528     case MODM_RESET:            return modReset         ((MIDIMAPDATA*)dwUser);
529
530     case MODM_GETDEVCAPS:       return modGetDevCaps    (wDevID, (MIDIMAPDATA*)dwUser, (LPMIDIOUTCAPSW)dwParam1,dwParam2);
531     case MODM_GETNUMDEVS:       return 1;
532     case MODM_GETVOLUME:        return modGetVolume     ((MIDIMAPDATA*)dwUser, (DWORD*)dwParam1);
533     case MODM_SETVOLUME:        return modSetVolume     ((MIDIMAPDATA*)dwUser, dwParam1);
534     default:
535         FIXME("unknown message %d!\n", wMsg);
536     }
537     return MMSYSERR_NOTSUPPORTED;
538 }
539
540 /*======================================================================*
541  *                  Driver part                                         *
542  *======================================================================*/
543
544 /**************************************************************************
545  *                              MIDIMAP_drvOpen                 [internal]
546  */
547 static LRESULT MIDIMAP_drvOpen(void)
548 {
549     MIDIOUTCAPSW        moc;
550     unsigned            dev, i;
551
552     if (midiOutPorts)
553         return 0;
554
555     numMidiOutPorts = midiOutGetNumDevs();
556     midiOutPorts = HeapAlloc(GetProcessHeap(), 0,
557                              numMidiOutPorts * sizeof(MIDIOUTPORT));
558     for (dev = 0; dev < numMidiOutPorts; dev++)
559     {
560         if (midiOutGetDevCapsW(dev, &moc, sizeof(moc)) == 0L)
561         {
562             strcpyW(midiOutPorts[dev].name, moc.szPname);
563             midiOutPorts[dev].loaded = 0;
564             midiOutPorts[dev].hMidi = 0;
565             midiOutPorts[dev].uDevID = dev;
566             midiOutPorts[dev].lpbPatch = NULL;
567             for (i = 0; i < 16; i++)
568                 midiOutPorts[dev].aChn[i] = i;
569         }
570         else
571         {
572             midiOutPorts[dev].loaded = -1;
573         }
574     }
575
576     return 1;
577 }
578
579 /**************************************************************************
580  *                              MIDIMAP_drvClose                [internal]
581  */
582 static LRESULT MIDIMAP_drvClose(void)
583 {
584     if (midiOutPorts)
585     {
586         HeapFree(GetProcessHeap(), 0, midiOutPorts);
587         midiOutPorts = NULL;
588         return 1;
589     }
590     return 0;
591 }
592
593 /**************************************************************************
594  *                              DriverProc (MIDIMAP.@)
595  */
596 LRESULT CALLBACK MIDIMAP_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
597                                     LPARAM dwParam1, LPARAM dwParam2)
598 {
599 /* EPP     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",  */
600 /* EPP    dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
601
602     switch (wMsg)
603     {
604     case DRV_LOAD:              return 1;
605     case DRV_FREE:              return 1;
606     case DRV_OPEN:              return 1;
607     case DRV_CLOSE:             return 1;
608     case DRV_ENABLE:            return 1;
609     case DRV_DISABLE:           return 1;
610     case DRV_QUERYCONFIGURE:    return 1;
611     case DRV_CONFIGURE:         MessageBoxA(0, "MIDIMAP MultiMedia Driver !", "OSS Driver", MB_OK);     return 1;
612     case DRV_INSTALL:           return DRVCNF_RESTART;
613     case DRV_REMOVE:            return DRVCNF_RESTART;
614     default:
615         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
616     }
617 }