1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Wine Midi mapper driver
5 * Copyright 1999, 2000, 2001 Eric Pouech
8 * notification has to be implemented
20 #include "debugtools.h"
23 * Here's how Windows stores the midiOut mapping information.
25 * Full form (in HKU) is:
27 * [Software\\Microsoft\\Windows\\CurrentVersion\\Multimedia\\MIDIMap] 988836060
28 * "AutoScheme"=dword:00000000
29 * "ConfigureCount"=dword:00000004
30 * "CurrentInstrument"="Wine OSS midi"
31 * "CurrentScheme"="epp"
33 * "UseScheme"=dword:00000000
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)
40 * UseScheme: trigger for simple/complex mapping
42 * A scheme is defined (in HKLM) as:
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).
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>
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#"
61 * This last part isn't implemented (.idf file support).
64 DEFAULT_DEBUG_CHANNEL(msacm
);
66 typedef struct tagMIDIOUTPORT
68 char name
[MAXPNAMELEN
];
71 unsigned short uDevID
;
73 unsigned int aChn
[16];
76 typedef struct tagMIDIMAPDATA
78 struct tagMIDIMAPDATA
* self
;
79 MIDIOUTPORT
* ChannelMap
[16];
82 static MIDIOUTPORT
* midiOutPorts
;
83 static unsigned numMidiOutPorts
;
85 static BOOL
MIDIMAP_IsBadData(MIDIMAPDATA
* mm
)
87 if (!IsBadReadPtr(mm
, sizeof(MIDIMAPDATA
)) && mm
->self
== mm
)
89 TRACE("Bad midimap data (%p)\n", mm
);
93 static BOOL
MIDIMAP_FindPort(const char* name
, unsigned* dev
)
95 for (*dev
= 0; *dev
< numMidiOutPorts
; (*dev
)++)
97 TRACE("%s\n", midiOutPorts
[*dev
].name
);
98 if (strcmp(midiOutPorts
[*dev
].name
, name
) == 0)
101 /* try the form #nnn */
102 if (*name
== '#' && isdigit(name
[1]))
104 *dev
= atoi(name
+ 1);
105 if (*dev
< numMidiOutPorts
)
111 static BOOL
MIDIMAP_LoadSettingsDefault(MIDIMAPDATA
* mom
, const char* port
)
115 if (port
!= NULL
&& !MIDIMAP_FindPort(port
, &dev
))
117 ERR("Registry glitch: couldn't find midi out (%s)\n", port
);
121 /* this is necessary when no midi out ports are present */
122 if (dev
>= numMidiOutPorts
)
125 for (i
= 0; i
< 16; i
++) mom
->ChannelMap
[i
] = &midiOutPorts
[dev
];
130 static BOOL
MIDIMAP_LoadSettingsScheme(MIDIMAPDATA
* mom
, const char* scheme
)
132 HKEY hSchemesKey
, hKey
, hPortKey
;
133 unsigned i
, idx
, dev
;
134 char buffer
[256], port
[256];
135 DWORD type
, size
, mask
;
137 for (i
= 0; i
< 16; i
++) mom
->ChannelMap
[i
] = NULL
;
139 if (RegOpenKeyA(HKEY_LOCAL_MACHINE
,
140 "System\\CurrentControlSet\\Control\\MediaProperties\\PrivateProperties\\Midi\\Schemes",
145 if (RegOpenKeyA(hSchemesKey
, scheme
, &hKey
))
147 RegCloseKey(hSchemesKey
);
151 for (idx
= 0; !RegEnumKeyA(hKey
, idx
, buffer
, sizeof(buffer
)); idx
++)
153 if (RegOpenKeyA(hKey
, buffer
, &hPortKey
)) continue;
156 if (RegQueryValueExA(hPortKey
, NULL
, 0, &type
, port
, &size
)) continue;
158 if (!MIDIMAP_FindPort(port
, &dev
)) continue;
161 if (RegQueryValueExA(hPortKey
, "Channels", 0, &type
, (void*)&mask
, &size
))
164 for (i
= 0; i
< 16; i
++)
168 if (mom
->ChannelMap
[i
])
169 ERR("Quirks in registry, channel %u is mapped twice\n", i
);
170 mom
->ChannelMap
[i
] = &midiOutPorts
[dev
];
175 RegCloseKey(hSchemesKey
);
181 static BOOL
MIDIMAP_LoadSettings(MIDIMAPDATA
* mom
)
186 if (RegOpenKeyA(HKEY_CURRENT_USER
,
187 "Software\\Microsoft\\Windows\\CurrentVersion\\Multimedia\\MIDIMap", &hKey
))
189 ret
= MIDIMAP_LoadSettingsDefault(mom
, NULL
);
193 DWORD type
, size
, out
;
198 if (!RegQueryValueExA(hKey
, "UseScheme", 0, &type
, (void*)&out
, &size
) && out
)
200 size
= sizeof(buffer
);
201 if (!RegQueryValueExA(hKey
, "CurrentScheme", 0, &type
, buffer
, &size
))
203 if (!(ret
= MIDIMAP_LoadSettingsScheme(mom
, buffer
)))
204 ret
= MIDIMAP_LoadSettingsDefault(mom
, NULL
);
208 ERR("Wrong registry: UseScheme is active, but no CurrentScheme found\n");
213 size
= sizeof(buffer
);
214 if (!RegQueryValueExA(hKey
, "CurrentInstrument", 0, &type
, buffer
, &size
) && *buffer
)
216 ret
= MIDIMAP_LoadSettingsDefault(mom
, buffer
);
220 ret
= MIDIMAP_LoadSettingsDefault(mom
, NULL
);
226 if (ret
&& TRACE_ON(msacm
))
230 for (i
= 0; i
< 16; i
++)
232 TRACE("chnMap[% 2d] => %d\n",
233 i
, mom
->ChannelMap
[i
] ? mom
->ChannelMap
[i
]->uDevID
: -1);
239 static DWORD
modOpen(LPDWORD lpdwUser
, LPMIDIOPENDESC lpDesc
, DWORD dwFlags
)
241 MIDIMAPDATA
* mom
= HeapAlloc(GetProcessHeap(), 0, sizeof(MIDIMAPDATA
));
243 TRACE("(%p %p %08lx\n", lpdwUser
, lpDesc
, dwFlags
);
245 if (!mom
) return MMSYSERR_NOMEM
;
247 if (MIDIMAP_LoadSettings(mom
))
249 *lpdwUser
= (DWORD
)mom
;
252 return MMSYSERR_NOERROR
;
254 HeapFree(GetProcessHeap(), 0, mom
);
255 return MIDIERR_INVALIDSETUP
;
258 static DWORD
modClose(MIDIMAPDATA
* mom
)
261 DWORD ret
= MMSYSERR_NOERROR
;
263 if (MIDIMAP_IsBadData(mom
)) return MMSYSERR_ERROR
;
265 for (i
= 0; i
< 16; i
++)
268 if (mom
->ChannelMap
[i
] && mom
->ChannelMap
[i
]->loaded
> 0)
270 t
= midiOutClose(mom
->ChannelMap
[i
]->hMidi
);
271 if (t
== MMSYSERR_NOERROR
)
273 mom
->ChannelMap
[i
]->loaded
= 0;
274 mom
->ChannelMap
[i
]->hMidi
= 0;
276 else if (ret
== MMSYSERR_NOERROR
)
280 if (ret
== MMSYSERR_NOERROR
)
281 HeapFree(GetProcessHeap(), 0, mom
);
285 static DWORD
modLongData(MIDIMAPDATA
* mom
, LPMIDIHDR lpMidiHdr
, DWORD dwParam2
)
288 DWORD ret
= MMSYSERR_NOERROR
;
291 if (MIDIMAP_IsBadData(mom
))
292 return MMSYSERR_ERROR
;
295 for (chn
= 0; chn
< 16; chn
++)
297 if (mom
->ChannelMap
[chn
] && mom
->ChannelMap
[chn
]->loaded
> 0)
300 midiOutPrepareHeader(mom
->ChannelMap
[chn
]->hMidi
, &mh
, sizeof(mh
));
301 ret
= midiOutLongMsg(mom
->ChannelMap
[chn
]->hMidi
, &mh
, sizeof(mh
));
302 midiOutUnprepareHeader(mom
->ChannelMap
[chn
]->hMidi
, &mh
, sizeof(mh
));
303 if (ret
!= MMSYSERR_NOERROR
) break;
309 static DWORD
modData(MIDIMAPDATA
* mom
, DWORD dwParam
)
311 BYTE lb
= LOBYTE(LOWORD(dwParam
));
312 WORD chn
= lb
& 0x0F;
313 DWORD ret
= MMSYSERR_NOERROR
;
315 if (MIDIMAP_IsBadData(mom
))
316 return MMSYSERR_ERROR
;
318 if (!mom
->ChannelMap
[chn
]) return MMSYSERR_NOERROR
;
329 if (mom
->ChannelMap
[chn
]->loaded
== 0)
331 if (midiOutOpen(&mom
->ChannelMap
[chn
]->hMidi
, mom
->ChannelMap
[chn
]->uDevID
,
332 0L, 0L, CALLBACK_NULL
) == MMSYSERR_NOERROR
)
333 mom
->ChannelMap
[chn
]->loaded
= 1;
335 mom
->ChannelMap
[chn
]->loaded
= -1;
336 /* FIXME: should load here the IDF midi data... and allow channel and
340 if (mom
->ChannelMap
[chn
]->loaded
> 0)
344 dwParam
|= mom
->ChannelMap
[chn
]->aChn
[chn
];
346 if ((LOBYTE(LOWORD(dwParam
)) & 0xF0) == 0xC0 /* program change */ &&
347 mom
->ChannelMap
[chn
]->lpbPatch
)
349 BYTE patch
= HIBYTE(LOWORD(dwParam
));
352 dwParam
&= ~0x0000FF00;
353 dwParam
|= mom
->ChannelMap
[chn
]->lpbPatch
[patch
];
355 ret
= midiOutShortMsg(mom
->ChannelMap
[chn
]->hMidi
, dwParam
);
359 for (chn
= 0; chn
< 16; chn
++)
361 if (mom
->ChannelMap
[chn
]->loaded
> 0)
362 ret
= midiOutShortMsg(mom
->ChannelMap
[chn
]->hMidi
, dwParam
);
366 FIXME("ooch %lu\n", dwParam
);
372 static DWORD
modPrepare(MIDIMAPDATA
* mom
, LPMIDIHDR lpMidiHdr
, DWORD dwParam2
)
374 if (MIDIMAP_IsBadData(mom
)) return MMSYSERR_ERROR
;
375 if (lpMidiHdr
->dwFlags
& (MHDR_ISSTRM
|MHDR_PREPARED
))
376 return MMSYSERR_INVALPARAM
;
378 lpMidiHdr
->dwFlags
|= MHDR_PREPARED
;
379 return MMSYSERR_NOERROR
;
382 static DWORD
modUnprepare(MIDIMAPDATA
* mom
, LPMIDIHDR lpMidiHdr
, DWORD dwParam2
)
384 if (MIDIMAP_IsBadData(mom
)) return MMSYSERR_ERROR
;
385 if ((lpMidiHdr
->dwFlags
& MHDR_ISSTRM
) || !(lpMidiHdr
->dwFlags
& MHDR_PREPARED
))
386 return MMSYSERR_INVALPARAM
;
388 lpMidiHdr
->dwFlags
&= ~MHDR_PREPARED
;
389 return MMSYSERR_NOERROR
;
392 static DWORD
modGetDevCaps(UINT wDevID
, MIDIMAPDATA
* mom
, LPMIDIOUTCAPSA lpMidiCaps
, DWORD size
)
394 lpMidiCaps
->wMid
= 0x00FF;
395 lpMidiCaps
->wPid
= 0x0001;
396 lpMidiCaps
->vDriverVersion
= 0x0100;
397 strcpy(lpMidiCaps
->szPname
, "Wine midi out mapper");
398 lpMidiCaps
->wTechnology
= MOD_MAPPER
;
399 lpMidiCaps
->wVoices
= 0;
400 lpMidiCaps
->wNotes
= 0;
401 lpMidiCaps
->wChannelMask
= 0xFFFF;
402 lpMidiCaps
->dwSupport
= 0L;
404 return MMSYSERR_NOERROR
;
407 static DWORD
modReset(MIDIMAPDATA
* mom
)
410 DWORD ret
= MMSYSERR_NOERROR
;
412 if (MIDIMAP_IsBadData(mom
))
413 return MMSYSERR_ERROR
;
415 for (chn
= 0; chn
< 16; chn
++)
417 if (mom
->ChannelMap
[chn
] && mom
->ChannelMap
[chn
]->loaded
> 0)
419 ret
= midiOutReset(mom
->ChannelMap
[chn
]->hMidi
);
420 if (ret
!= MMSYSERR_NOERROR
) break;
426 /**************************************************************************
427 * modMessage (MIDIMAP.@)
429 DWORD WINAPI
MIDIMAP_modMessage(UINT wDevID
, UINT wMsg
, DWORD dwUser
,
430 DWORD dwParam1
, DWORD dwParam2
)
432 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
433 wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
441 /* FIXME: Pretend this is supported */
444 case MODM_OPEN
: return modOpen ((LPDWORD
)dwUser
, (LPMIDIOPENDESC
)dwParam1
,dwParam2
);
445 case MODM_CLOSE
: return modClose ((MIDIMAPDATA
*)dwUser
);
447 case MODM_DATA
: return modData ((MIDIMAPDATA
*)dwUser
, dwParam1
);
448 case MODM_LONGDATA
: return modLongData ((MIDIMAPDATA
*)dwUser
, (LPMIDIHDR
)dwParam1
, dwParam2
);
449 case MODM_PREPARE
: return modPrepare ((MIDIMAPDATA
*)dwUser
, (LPMIDIHDR
)dwParam1
, dwParam2
);
450 case MODM_UNPREPARE
: return modUnprepare ((MIDIMAPDATA
*)dwUser
, (LPMIDIHDR
)dwParam1
, dwParam2
);
451 case MODM_RESET
: return modReset ((MIDIMAPDATA
*)dwUser
);
453 case MODM_GETDEVCAPS
: return modGetDevCaps (wDevID
, (MIDIMAPDATA
*)dwUser
, (LPMIDIOUTCAPSA
)dwParam1
,dwParam2
);
454 case MODM_GETNUMDEVS
: return 1;
455 case MODM_GETVOLUME
: return MMSYSERR_NOTSUPPORTED
;
456 case MODM_SETVOLUME
: return MMSYSERR_NOTSUPPORTED
;
458 FIXME("unknown message %d!\n", wMsg
);
460 return MMSYSERR_NOTSUPPORTED
;
463 /*======================================================================*
465 *======================================================================*/
467 /**************************************************************************
468 * MIDIMAP_drvOpen [internal]
470 static DWORD
MIDIMAP_drvOpen(LPSTR str
)
478 numMidiOutPorts
= midiOutGetNumDevs();
479 midiOutPorts
= HeapAlloc(GetProcessHeap(), 0,
480 numMidiOutPorts
* sizeof(MIDIOUTPORT
));
481 for (dev
= 0; dev
< numMidiOutPorts
; dev
++)
483 if (midiOutGetDevCapsA((HMIDIOUT
)dev
, &moc
, sizeof(moc
)) == 0L)
485 strcpy(midiOutPorts
[dev
].name
, moc
.szPname
);
486 midiOutPorts
[dev
].loaded
= 0;
487 midiOutPorts
[dev
].hMidi
= 0;
488 midiOutPorts
[dev
].uDevID
= 0;
489 midiOutPorts
[dev
].lpbPatch
= NULL
;
490 for (i
= 0; i
< 16; i
++)
491 midiOutPorts
[dev
].aChn
[i
] = i
;
495 midiOutPorts
[dev
].loaded
= -1;
502 /**************************************************************************
503 * MIDIMAP_drvClose [internal]
505 static DWORD
MIDIMAP_drvClose(DWORD dwDevID
)
509 HeapFree(GetProcessHeap(), 0, midiOutPorts
);
516 /**************************************************************************
517 * DriverProc (MIDIMAP.@)
519 LONG CALLBACK
MIDIMAP_DriverProc(DWORD dwDevID
, HDRVR hDriv
, DWORD wMsg
,
520 DWORD dwParam1
, DWORD dwParam2
)
522 /* EPP TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", */
523 /* EPP dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
527 case DRV_LOAD
: return 1;
528 case DRV_FREE
: return 1;
529 case DRV_OPEN
: return MIDIMAP_drvOpen((LPSTR
)dwParam1
);
530 case DRV_CLOSE
: return MIDIMAP_drvClose(dwDevID
);
531 case DRV_ENABLE
: return 1;
532 case DRV_DISABLE
: return 1;
533 case DRV_QUERYCONFIGURE
: return 1;
534 case DRV_CONFIGURE
: MessageBoxA(0, "MIDIMAP MultiMedia Driver !", "OSS Driver", MB_OK
); return 1;
535 case DRV_INSTALL
: return DRVCNF_RESTART
;
536 case DRV_REMOVE
: return DRVCNF_RESTART
;
538 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);