Added an unknown VxD error code.
[wine] / dlls / winmm / midimap / midimap.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*                                 
3  * Wine Midi mapper driver
4  *
5  * Copyright    1999, 2000, 2001 Eric Pouech
6  *
7  * TODO:
8  *      notification has to be implemented
9  *      IDF file loading
10  */
11
12 #include <string.h>
13 #include <stdlib.h>
14 #include "windef.h"
15 #include "winbase.h"
16 #include "wingdi.h"
17 #include "winuser.h"
18 #include "mmddk.h"
19 #include "winreg.h"
20 #include "debugtools.h"
21
22 /*
23  * Here's how Windows stores the midiOut mapping information.
24  *
25  * Full form (in HKU) is:
26  *
27  * [Software\\Microsoft\\Windows\\CurrentVersion\\Multimedia\\MIDIMap] 988836060
28  * "AutoScheme"=dword:00000000
29  * "ConfigureCount"=dword:00000004
30  * "CurrentInstrument"="Wine OSS midi"
31  * "CurrentScheme"="epp"
32  * "DriverList"=""
33  * "UseScheme"=dword:00000000
34  *
35  * AutoScheme:          ?
36  * CurrentInstrument:   name of midiOut device to use when UseScheme is 0. Wine uses an extension
37  *                      of the form #n to link to n'th midiOut device of the system
38  * CurrentScheme:       when UseScheme is non null, it's the scheme to use (see below)
39  * DriverList:          ?
40  * UseScheme:           trigger for simple/complex mapping
41  *
42  * A scheme is defined (in HKLM) as:
43  *
44  * [System\\CurrentControlSet\\Control\\MediaProperties\\PrivateProperties\\Midi\\Schemes\\<nameScheme>]
45  * <nameScheme>:        one key for each defined scheme (system wide)
46  * under each one of these <nameScheme> keys, there's:
47  * [...\\<nameScheme>\\<idxDevice>]
48  * "Channels"="<bitMask>"
49  * (the default value of this key also refers to the name of the device).
50  *
51  * this defines, for each midiOut device (identified by its index in <idxDevice>), which 
52  * channels have to be mapped onto it. The <bitMask> defines the channels (from 0 to 15)
53  * will be mapped (mapping occurs for channel <ch> if bit <ch> is set in <bitMask>
54  *
55  * Further mapping information can also be defined in:
56  * [System\\CurrentControlSet\\Control\\MediaProperties\\PrivateProperties\\Midi\\Ports\\<nameDevice>\\Instruments\\<idx>]
57  * "Definition"="<.idf file>"
58  * "FriendlyName"="#for .idx file#"
59  * "Port"="<idxPort>"
60  *
61  * This last part isn't implemented (.idf file support).
62  */
63
64 DEFAULT_DEBUG_CHANNEL(msacm);
65
66 typedef struct tagMIDIOUTPORT 
67 {
68     char                name[MAXPNAMELEN];
69     int                 loaded;
70     HMIDI               hMidi;
71     unsigned short      uDevID;
72     LPBYTE              lpbPatch;
73     unsigned int        aChn[16];
74 } MIDIOUTPORT;
75
76 typedef struct tagMIDIMAPDATA 
77 {
78     struct tagMIDIMAPDATA*      self;
79     MIDIOUTPORT*        ChannelMap[16];
80 } MIDIMAPDATA;
81
82 static  MIDIOUTPORT*    midiOutPorts;
83 static  unsigned        numMidiOutPorts;
84
85 static  BOOL    MIDIMAP_IsBadData(MIDIMAPDATA* mm)
86 {
87     if (!IsBadReadPtr(mm, sizeof(MIDIMAPDATA)) && mm->self == mm)
88         return FALSE;
89     TRACE("Bad midimap data (%p)\n", mm);
90     return TRUE;
91 }
92
93 static BOOL     MIDIMAP_FindPort(const char* name, unsigned* dev)
94 {
95     for (*dev = 0; *dev < numMidiOutPorts; (*dev)++) 
96     {
97         TRACE("%s\n", midiOutPorts[*dev].name);
98         if (strcmp(midiOutPorts[*dev].name, name) == 0)
99             return TRUE;
100     }
101     /* try the form #nnn */
102     if (*name == '#' && isdigit(name[1])) 
103     {
104         *dev = atoi(name + 1);
105         if (*dev < numMidiOutPorts)
106             return TRUE;
107     }
108     return FALSE;
109 }
110
111 static BOOL     MIDIMAP_LoadSettingsDefault(MIDIMAPDATA* mom, const char* port)
112 {
113     unsigned i, dev = 0;
114
115     if (port != NULL && !MIDIMAP_FindPort(port, &dev)) 
116     {
117         ERR("Registry glitch: couldn't find midi out (%s)\n", port);
118         dev = 0;
119     }
120     
121     /* sets default */
122     for (i = 0; i < 16; i++) mom->ChannelMap[i] = &midiOutPorts[dev];
123
124     return TRUE;
125 }
126
127 static BOOL     MIDIMAP_LoadSettingsScheme(MIDIMAPDATA* mom, const char* scheme)
128 {
129     HKEY        hSchemesKey, hKey, hPortKey;
130     unsigned    i, idx, dev;
131     char        buffer[256], port[256];
132     DWORD       type, size, mask;
133
134     for (i = 0; i < 16; i++)    mom->ChannelMap[i] = NULL;
135
136     if (RegOpenKeyA(HKEY_LOCAL_MACHINE, 
137                     "System\\CurrentControlSet\\Control\\MediaProperties\\PrivateProperties\\Midi\\Schemes", 
138                     &hSchemesKey)) 
139     {
140         return FALSE;
141     }
142     if (RegOpenKeyA(hSchemesKey, scheme, &hKey)) 
143     {
144         RegCloseKey(hSchemesKey);
145         return FALSE;
146     }
147
148     for (idx = 0; !RegEnumKeyA(hKey, idx, buffer, sizeof(buffer)); idx++) 
149     {
150         if (RegOpenKeyA(hKey, buffer, &hPortKey)) continue;
151
152         size = sizeof(port);
153         if (RegQueryValueExA(hPortKey, NULL, 0, &type, port, &size)) continue;
154
155         if (!MIDIMAP_FindPort(port, &dev)) continue;
156
157         size = sizeof(mask);
158         if (RegQueryValueExA(hPortKey, "Channels", 0, &type, (void*)&mask, &size))
159             continue;
160
161         for (i = 0; i < 16; i++) 
162         {
163             if (mask & (1 << i)) 
164             {
165                 if (mom->ChannelMap[i]) 
166                     ERR("Quirks in registry, channel %u is mapped twice\n", i);
167                 mom->ChannelMap[i] = &midiOutPorts[dev];
168             }
169         }
170     }
171
172     RegCloseKey(hSchemesKey);
173     RegCloseKey(hKey);
174     
175     return TRUE;
176 }
177
178 static BOOL     MIDIMAP_LoadSettings(MIDIMAPDATA* mom)
179 {
180     HKEY        hKey;
181     BOOL        ret;
182
183     if (RegOpenKeyA(HKEY_CURRENT_USER, 
184                     "Software\\Microsoft\\Windows\\CurrentVersion\\Multimedia\\MIDIMap", &hKey)) 
185     {
186         ret = MIDIMAP_LoadSettingsDefault(mom, NULL);
187     }
188     else 
189     {
190         DWORD   type, size, out;
191         char    buffer[256];
192     
193         ret = 2;
194         size = sizeof(out);
195         if (!RegQueryValueExA(hKey, "UseScheme", 0, &type, (void*)&out, &size) && out) 
196         {
197             size = sizeof(buffer);
198             if (!RegQueryValueExA(hKey, "CurrentScheme", 0, &type, buffer, &size)) 
199             {
200                 if (!(ret = MIDIMAP_LoadSettingsScheme(mom, buffer)))
201                     ret = MIDIMAP_LoadSettingsDefault(mom, NULL);
202             }
203             else 
204             {
205                 ERR("Wrong registry: UseScheme is active, but no CurrentScheme found\n");
206             }
207         }
208         if (ret == 2) 
209         {
210             size = sizeof(buffer);
211             if (!RegQueryValueExA(hKey, "CurrentInstrument", 0, &type, buffer, &size) && *buffer) 
212             {
213                 ret = MIDIMAP_LoadSettingsDefault(mom, buffer);
214             }
215             else 
216             {
217                 ret = MIDIMAP_LoadSettingsDefault(mom, NULL);
218             }
219         }
220     }
221     RegCloseKey(hKey);
222
223     if (TRACE_ON(msacm)) 
224     {
225         unsigned        i;
226
227         for (i = 0; i < 16; i++) 
228         {
229             TRACE("chnMap[% 2d] => %d\n", 
230                   i, mom->ChannelMap[i] ? mom->ChannelMap[i]->uDevID : -1);
231         }
232     }
233     return ret;
234 }
235
236 static  DWORD   modOpen(LPDWORD lpdwUser, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
237 {
238     MIDIMAPDATA*        mom = HeapAlloc(GetProcessHeap(), 0, sizeof(MIDIMAPDATA));
239
240     TRACE("(%p %p %08lx\n", lpdwUser, lpDesc, dwFlags);
241
242     if (!mom) return MMSYSERR_NOMEM;
243
244     if (MIDIMAP_LoadSettings(mom)) 
245     {
246         *lpdwUser = (DWORD)mom;
247         mom->self = mom;
248
249         return MMSYSERR_NOERROR;
250     }
251     HeapFree(GetProcessHeap(), 0, mom);
252     return MIDIERR_INVALIDSETUP;
253 }
254
255 static  DWORD   modClose(MIDIMAPDATA* mom)
256 {
257     UINT        i;
258     DWORD       ret = MMSYSERR_NOERROR;
259
260     if (MIDIMAP_IsBadData(mom))         return MMSYSERR_ERROR;
261
262     for (i = 0; i < 16; i++) 
263     {
264         DWORD   t;
265         if (mom->ChannelMap[i] && mom->ChannelMap[i]->loaded > 0) 
266         {
267             t = midiOutClose(mom->ChannelMap[i]->hMidi);
268             if (t == MMSYSERR_NOERROR) 
269             {
270                 mom->ChannelMap[i]->loaded = 0;
271                 mom->ChannelMap[i]->hMidi = 0;
272             }
273             else if (ret == MMSYSERR_NOERROR)
274                 ret = t;
275         }
276     }
277     if (ret == MMSYSERR_NOERROR)
278         HeapFree(GetProcessHeap(), 0, mom);
279     return ret;
280 }
281
282 static  DWORD   modLongData(MIDIMAPDATA* mom, LPMIDIHDR lpMidiHdr, DWORD dwParam2)
283 {
284     WORD        chn;
285     DWORD       ret = MMSYSERR_NOERROR;
286     MIDIHDR     mh;
287
288     if (MIDIMAP_IsBadData(mom))
289         return MMSYSERR_ERROR;
290
291     mh = *lpMidiHdr;
292     for (chn = 0; chn < 16; chn++) 
293     {
294         if (mom->ChannelMap[chn] && mom->ChannelMap[chn]->loaded > 0) 
295         {
296             mh.dwFlags = 0;
297             midiOutPrepareHeader(mom->ChannelMap[chn]->hMidi, &mh, sizeof(mh));
298             ret = midiOutLongMsg(mom->ChannelMap[chn]->hMidi, &mh, sizeof(mh));
299             midiOutUnprepareHeader(mom->ChannelMap[chn]->hMidi, &mh, sizeof(mh));
300             if (ret != MMSYSERR_NOERROR) break;
301         }
302     }
303     return ret;
304 }
305
306 static  DWORD   modData(MIDIMAPDATA* mom, DWORD dwParam)
307 {
308     BYTE        lb = LOBYTE(LOWORD(dwParam));
309     WORD        chn = lb & 0x0F;
310     DWORD       ret = MMSYSERR_NOERROR;
311
312     if (MIDIMAP_IsBadData(mom))
313         return MMSYSERR_ERROR;
314
315     if (!mom->ChannelMap[chn]) return MMSYSERR_NOERROR;
316
317     switch (lb & 0xF0) 
318     {
319     case 0x80:
320     case 0x90:
321     case 0xA0:
322     case 0xB0:
323     case 0xC0:
324     case 0xD0:
325     case 0xE0:
326         if (mom->ChannelMap[chn]->loaded == 0) 
327         {
328             if (midiOutOpen(&mom->ChannelMap[chn]->hMidi, mom->ChannelMap[chn]->uDevID,
329                             0L, 0L, CALLBACK_NULL) == MMSYSERR_NOERROR)
330                 mom->ChannelMap[chn]->loaded = 1;
331             else
332                 mom->ChannelMap[chn]->loaded = -1;
333             /* FIXME: should load here the IDF midi data... and allow channel and
334              * patch mappings 
335              */
336         }
337         if (mom->ChannelMap[chn]->loaded > 0) 
338         {
339             /* change channel */
340             dwParam &= ~0x0F;
341             dwParam |= mom->ChannelMap[chn]->aChn[chn];
342
343             if ((LOBYTE(LOWORD(dwParam)) & 0xF0) == 0xC0 /* program change */ &&
344                 mom->ChannelMap[chn]->lpbPatch) 
345             {
346                 BYTE patch = HIBYTE(LOWORD(dwParam));
347
348                 /* change patch */
349                 dwParam &= ~0x0000FF00;
350                 dwParam |= mom->ChannelMap[chn]->lpbPatch[patch];
351             }
352             ret = midiOutShortMsg(mom->ChannelMap[chn]->hMidi, dwParam);
353         }
354         break;
355     case 0xF0:
356         for (chn = 0; chn < 16; chn++) 
357         {
358             if (mom->ChannelMap[chn]->loaded > 0)
359                 ret = midiOutShortMsg(mom->ChannelMap[chn]->hMidi, dwParam);
360         }
361         break;
362     default:
363         FIXME("ooch %lu\n", dwParam);
364     }
365
366     return ret;
367 }
368
369 static  DWORD   modPrepare(MIDIMAPDATA* mom, LPMIDIHDR lpMidiHdr, DWORD dwParam2)
370 {
371     if (MIDIMAP_IsBadData(mom)) return MMSYSERR_ERROR;
372     if (lpMidiHdr->dwFlags & (MHDR_ISSTRM|MHDR_PREPARED))
373         return MMSYSERR_INVALPARAM;
374
375     lpMidiHdr->dwFlags |= MHDR_PREPARED;
376     return MMSYSERR_NOERROR;
377 }
378
379 static  DWORD   modUnprepare(MIDIMAPDATA* mom, LPMIDIHDR lpMidiHdr, DWORD dwParam2)
380 {
381     if (MIDIMAP_IsBadData(mom)) return MMSYSERR_ERROR;
382     if ((lpMidiHdr->dwFlags & MHDR_ISSTRM) || !(lpMidiHdr->dwFlags & MHDR_PREPARED))
383         return MMSYSERR_INVALPARAM;
384
385     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
386     return MMSYSERR_NOERROR;
387 }
388
389 static  DWORD   modGetDevCaps(UINT wDevID, MIDIMAPDATA* mom, LPMIDIOUTCAPSA lpMidiCaps, DWORD size)
390 {
391     lpMidiCaps->wMid = 0x00FF;
392     lpMidiCaps->wPid = 0x0001;
393     lpMidiCaps->vDriverVersion = 0x0100;
394     strcpy(lpMidiCaps->szPname, "Wine midi out mapper");
395     lpMidiCaps->wTechnology = MOD_MAPPER;
396     lpMidiCaps->wVoices = 0;
397     lpMidiCaps->wNotes = 0;
398     lpMidiCaps->wChannelMask = 0xFFFF;
399     lpMidiCaps->dwSupport = 0L;
400
401     return MMSYSERR_NOERROR;
402 }
403
404 static  DWORD   modReset(MIDIMAPDATA* mom)
405 {
406     WORD        chn;
407     DWORD       ret = MMSYSERR_NOERROR;
408
409     if (MIDIMAP_IsBadData(mom))
410         return MMSYSERR_ERROR;
411
412     for (chn = 0; chn < 16; chn++) 
413     {
414         if (mom->ChannelMap[chn] && mom->ChannelMap[chn]->loaded > 0) 
415         {
416             ret = midiOutReset(mom->ChannelMap[chn]->hMidi);
417             if (ret != MMSYSERR_NOERROR) break;
418         }
419     }
420     return ret;
421 }
422
423 /**************************************************************************
424  *                              MIDIMAP_modMessage      [sample driver]
425  */
426 DWORD WINAPI MIDIMAP_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser, 
427                                 DWORD dwParam1, DWORD dwParam2)
428 {
429     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
430           wDevID, wMsg, dwUser, dwParam1, dwParam2);
431     
432     switch (wMsg) 
433     {
434     case DRVM_INIT:
435     case DRVM_EXIT:
436     case DRVM_ENABLE:
437     case DRVM_DISABLE:
438         /* FIXME: Pretend this is supported */
439         return 0;
440
441     case MODM_OPEN:             return modOpen          ((LPDWORD)dwUser,      (LPMIDIOPENDESC)dwParam1,dwParam2);
442     case MODM_CLOSE:            return modClose         ((MIDIMAPDATA*)dwUser);
443
444     case MODM_DATA:             return modData          ((MIDIMAPDATA*)dwUser, dwParam1);
445     case MODM_LONGDATA:         return modLongData      ((MIDIMAPDATA*)dwUser, (LPMIDIHDR)dwParam1,     dwParam2);
446     case MODM_PREPARE:          return modPrepare       ((MIDIMAPDATA*)dwUser, (LPMIDIHDR)dwParam1,     dwParam2);
447     case MODM_UNPREPARE:        return modUnprepare     ((MIDIMAPDATA*)dwUser, (LPMIDIHDR)dwParam1,     dwParam2);
448     case MODM_RESET:            return modReset         ((MIDIMAPDATA*)dwUser);
449
450     case MODM_GETDEVCAPS:       return modGetDevCaps    (wDevID, (MIDIMAPDATA*)dwUser, (LPMIDIOUTCAPSA)dwParam1,dwParam2);
451     case MODM_GETNUMDEVS:       return 1;
452     case MODM_GETVOLUME:        return MMSYSERR_NOTSUPPORTED;
453     case MODM_SETVOLUME:        return MMSYSERR_NOTSUPPORTED;
454     default:
455         FIXME("unknown message %d!\n", wMsg);
456     }
457     return MMSYSERR_NOTSUPPORTED;
458 }
459
460 /*======================================================================*
461  *                  Driver part                                         *
462  *======================================================================*/
463
464 /**************************************************************************
465  *                              MIDIMAP_drvOpen                 [internal]      
466  */
467 static  DWORD   MIDIMAP_drvOpen(LPSTR str)
468 {
469     MIDIOUTCAPSA        moc;
470     unsigned            dev, i;
471
472     if (midiOutPorts)
473         return 0;
474     
475     numMidiOutPorts = midiOutGetNumDevs();
476     midiOutPorts = HeapAlloc(GetProcessHeap(), 0, 
477                              numMidiOutPorts * sizeof(MIDIOUTPORT));
478     for (dev = 0; dev < numMidiOutPorts; dev++) 
479     {
480         if (midiOutGetDevCapsA((HMIDIOUT)dev, &moc, sizeof(moc)) == 0L) 
481         {
482             strcpy(midiOutPorts[dev].name, moc.szPname);
483             midiOutPorts[dev].loaded = 0;
484             midiOutPorts[dev].hMidi = 0;
485             midiOutPorts[dev].uDevID = 0;
486             midiOutPorts[dev].lpbPatch = NULL;
487             for (i = 0; i < 16; i++)
488                 midiOutPorts[dev].aChn[i] = i;
489         }
490         else 
491         {
492             midiOutPorts[dev].loaded = -1;
493         }
494     }
495
496     return 1;
497 }
498
499 /**************************************************************************
500  *                              MIDIMAP_drvClose                [internal]      
501  */
502 static  DWORD   MIDIMAP_drvClose(DWORD dwDevID)
503 {
504     if (midiOutPorts) 
505     {
506         HeapFree(GetProcessHeap(), 0, midiOutPorts);
507         midiOutPorts = NULL;
508         return 1;
509     }
510     return 0;
511 }
512
513 /**************************************************************************
514  *                              MIDIMAP_DriverProc              [internal]
515  */
516 LONG CALLBACK   MIDIMAP_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg, 
517                                    DWORD dwParam1, DWORD dwParam2)
518 {
519 /* EPP     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",  */
520 /* EPP    dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
521     
522     switch (wMsg) 
523     {
524     case DRV_LOAD:              return 1;
525     case DRV_FREE:              return 1;
526     case DRV_OPEN:              return MIDIMAP_drvOpen((LPSTR)dwParam1);
527     case DRV_CLOSE:             return MIDIMAP_drvClose(dwDevID);
528     case DRV_ENABLE:            return 1;
529     case DRV_DISABLE:           return 1;
530     case DRV_QUERYCONFIGURE:    return 1;
531     case DRV_CONFIGURE:         MessageBoxA(0, "MIDIMAP MultiMedia Driver !", "OSS Driver", MB_OK);     return 1;
532     case DRV_INSTALL:           return DRVCNF_RESTART;
533     case DRV_REMOVE:            return DRVCNF_RESTART;
534     default:
535         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
536     }
537 }
538
539