midimap.c 17.1 KB
Newer Older
1
/*
2
 * Wine MIDI mapper driver
3
 *
4 5
 * Copyright 	1999, 2000, 2001 Eric Pouech
 *
6 7 8 9 10 11 12 13 14 15 16 17
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 21 22
 * TODO:
 *	notification has to be implemented
 *	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 32
#include "winuser.h"
#include "mmddk.h"
33
#include "winreg.h"
34
#include "wine/unicode.h"
35
#include "wine/debug.h"
36

37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
/*
 * Here's how Windows stores the midiOut mapping information.
 *
 * Full form (in HKU) is:
 *
 * [Software\\Microsoft\\Windows\\CurrentVersion\\Multimedia\\MIDIMap] 988836060
 * "AutoScheme"=dword:00000000
 * "ConfigureCount"=dword:00000004
 * "CurrentInstrument"="Wine OSS midi"
 * "CurrentScheme"="epp"
 * "DriverList"=""
 * "UseScheme"=dword:00000000
 *
 * AutoScheme: 		?
 * CurrentInstrument: 	name of midiOut device to use when UseScheme is 0. Wine uses an extension
 *			of the form #n to link to n'th midiOut device of the system
 * CurrentScheme:	when UseScheme is non null, it's the scheme to use (see below)
 * DriverList:		?
 * UseScheme:		trigger for simple/complex mapping
 *
 * A scheme is defined (in HKLM) as:
 *
 * [System\\CurrentControlSet\\Control\\MediaProperties\\PrivateProperties\\Midi\\Schemes\\<nameScheme>]
 * <nameScheme>:	one key for each defined scheme (system wide)
 * under each one of these <nameScheme> keys, there's:
 * [...\\<nameScheme>\\<idxDevice>]
 * "Channels"="<bitMask>"
 * (the default value of this key also refers to the name of the device).
 *
66
 * this defines, for each midiOut device (identified by its index in <idxDevice>), which
67 68 69 70 71 72 73 74 75 76 77 78
 * channels have to be mapped onto it. The <bitMask> defines the channels (from 0 to 15)
 * will be mapped (mapping occurs for channel <ch> if bit <ch> is set in <bitMask>
 *
 * Further mapping information can also be defined in:
 * [System\\CurrentControlSet\\Control\\MediaProperties\\PrivateProperties\\Midi\\Ports\\<nameDevice>\\Instruments\\<idx>]
 * "Definition"="<.idf file>"
 * "FriendlyName"="#for .idx file#"
 * "Port"="<idxPort>"
 *
 * This last part isn't implemented (.idf file support).
 */

79
WINE_DEFAULT_DEBUG_CHANNEL(msacm);
80

81
typedef struct tagMIDIOUTPORT
82
{
83
    WCHAR		name[MAXPNAMELEN];
84
    int			loaded;
85
    HMIDIOUT		hMidi;
86 87 88 89 90
    unsigned short	uDevID;
    LPBYTE		lpbPatch;
    unsigned int	aChn[16];
} MIDIOUTPORT;

91
typedef	struct tagMIDIMAPDATA
92
{
93
    struct tagMIDIMAPDATA*	self;
94
    MIDIOUTPORT*	ChannelMap[16];
95 96
    MIDIOPENDESC	midiDesc;
    WORD		wCbFlags;
97 98
} MIDIMAPDATA;

99 100 101 102
static	MIDIOUTPORT*	midiOutPorts;
static  unsigned	numMidiOutPorts;

static	BOOL	MIDIMAP_IsBadData(MIDIMAPDATA* mm)
103
{
104 105 106 107
    if (!IsBadReadPtr(mm, sizeof(MIDIMAPDATA)) && mm->self == mm)
	return FALSE;
    TRACE("Bad midimap data (%p)\n", mm);
    return TRUE;
108 109
}

110
static BOOL	MIDIMAP_FindPort(const WCHAR* name, unsigned* dev)
111
{
112
    for (*dev = 0; *dev < numMidiOutPorts; (*dev)++)
113
    {
114 115
	TRACE("%s\n", wine_dbgstr_w(midiOutPorts[*dev].name));
	if (strcmpW(midiOutPorts[*dev].name, name) == 0)
116
	    return TRUE;
117
    }
118
    /* try the form #nnn */
119
    if (*name == '#' && isdigit(name[1]))
120
    {
121 122 123 124 125 126
        const WCHAR*  ptr = name + 1;
        *dev = 0;
        do 
        {
            *dev = *dev * 10 + *ptr - '0';
        } while (isdigit(*++ptr));
127 128 129 130
	if (*dev < numMidiOutPorts)
	    return TRUE;
    }
    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 141
	dev = 0;
    }
142 143 144 145

    /* this is necessary when no midi out ports are present */
    if (dev >= numMidiOutPorts)
	return FALSE;
146 147
    /* sets default */
    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 155
    HKEY	hSchemesKey, hKey, hPortKey;
    unsigned	i, idx, dev;
156
    WCHAR       buffer[256], port[256];
157 158 159 160
    DWORD	type, size, mask;

    for (i = 0; i < 16; i++)	mom->ChannelMap[i] = NULL;

161 162 163
    if (RegOpenKeyA(HKEY_LOCAL_MACHINE,
		    "System\\CurrentControlSet\\Control\\MediaProperties\\PrivateProperties\\Midi\\Schemes",
		    &hSchemesKey))
164 165 166
    {
	return FALSE;
    }
167
    if (RegOpenKeyW(hSchemesKey, scheme, &hKey))
168 169 170 171
    {
	RegCloseKey(hSchemesKey);
	return FALSE;
    }
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 183 184
	size = sizeof(mask);
	if (RegQueryValueExA(hPortKey, "Channels", 0, &type, (void*)&mask, &size))
	    continue;
185

186
	for (i = 0; i < 16; i++)
187
	{
188
	    if (mask & (1 << i))
189
	    {
190
		if (mom->ChannelMap[i])
191 192 193 194 195
		    ERR("Quirks in registry, channel %u is mapped twice\n", i);
		mom->ChannelMap[i] = &midiOutPorts[dev];
	    }
	}
    }
196

197 198
    RegCloseKey(hSchemesKey);
    RegCloseKey(hKey);
199

200
    return TRUE;
201 202
}

203
static BOOL	MIDIMAP_LoadSettings(MIDIMAPDATA* mom)
204
{
205 206
    HKEY 	hKey;
    BOOL	ret;
207

208 209
    if (RegOpenKeyA(HKEY_CURRENT_USER,
		    "Software\\Microsoft\\Windows\\CurrentVersion\\Multimedia\\MIDIMap", &hKey))
210 211 212
    {
	ret = MIDIMAP_LoadSettingsDefault(mom, NULL);
    }
213
    else
214 215
    {
	DWORD	type, size, out;
216
	WCHAR   buffer[256];
217

218 219
	ret = 2;
	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 227 228
	    {
		if (!(ret = MIDIMAP_LoadSettingsScheme(mom, buffer)))
		    ret = MIDIMAP_LoadSettingsDefault(mom, NULL);
	    }
229
	    else
230 231 232 233
	    {
		ERR("Wrong registry: UseScheme is active, but no CurrentScheme found\n");
	    }
	}
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 241
	    {
		ret = MIDIMAP_LoadSettingsDefault(mom, buffer);
	    }
242
	    else
243 244 245 246 247 248
	    {
		ret = MIDIMAP_LoadSettingsDefault(mom, NULL);
	    }
	}
    }
    RegCloseKey(hKey);
249

250
    if (ret && TRACE_ON(msacm))
251 252
    {
	unsigned	i;
253

254
	for (i = 0; i < 16; i++)
255
	{
Jeff Smith's avatar
Jeff Smith committed
256
	    TRACE("chnMap[%2d] => %d\n",
257 258
		  i, mom->ChannelMap[i] ? mom->ChannelMap[i]->uDevID : -1);
	}
259
    }
260
    return ret;
261 262
}

263 264 265 266 267 268 269
static void MIDIMAP_NotifyClient(MIDIMAPDATA* mom, WORD wMsg,
				 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
    DriverCallback(mom->midiDesc.dwCallback, mom->wCbFlags, (HDRVR)mom->midiDesc.hMidi,
		   wMsg, mom->midiDesc.dwInstance, dwParam1, dwParam2);
}

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 286
	mom->self = mom;

287 288 289 290
	mom->wCbFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
	mom->midiDesc = *lpDesc;
	MIDIMAP_NotifyClient(mom, MOM_OPEN, 0L, 0L);

291
	return MMSYSERR_NOERROR;
292
    }
293 294
    HeapFree(GetProcessHeap(), 0, mom);
    return MIDIERR_INVALIDSETUP;
295 296
}

297
static	DWORD	modClose(MIDIMAPDATA* mom)
298
{
299 300 301 302 303
    UINT	i;
    DWORD	ret = MMSYSERR_NOERROR;

    if (MIDIMAP_IsBadData(mom)) 	return MMSYSERR_ERROR;

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 313 314 315 316 317 318
	    {
		mom->ChannelMap[i]->loaded = 0;
		mom->ChannelMap[i]->hMidi = 0;
	    }
	    else if (ret == MMSYSERR_NOERROR)
		ret = t;
	}
    }
319 320
    if (ret == MMSYSERR_NOERROR) {
	MIDIMAP_NotifyClient(mom, MOM_CLOSE, 0L, 0L);
321
	HeapFree(GetProcessHeap(), 0, mom);
322
    }
323 324 325
    return ret;
}

326
static DWORD modLongData(MIDIMAPDATA* mom, LPMIDIHDR lpMidiHdr, DWORD_PTR dwParam2)
327
{
328 329 330 331 332 333
    WORD	chn;
    DWORD	ret = MMSYSERR_NOERROR;
    MIDIHDR	mh;

    if (MIDIMAP_IsBadData(mom))
	return MMSYSERR_ERROR;
334 335 336 337
    if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
	return MIDIERR_UNPREPARED;
    if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
	return MIDIERR_STILLPLAYING;
338 339

    mh = *lpMidiHdr;
340 341
    lpMidiHdr->dwFlags &= ~MHDR_DONE;
    lpMidiHdr->dwFlags |= MHDR_INQUEUE;
342
    for (chn = 0; chn < 16; chn++)
343
    {
344
	if (mom->ChannelMap[chn] && mom->ChannelMap[chn]->loaded > 0)
345 346 347 348
	{
	    mh.dwFlags = 0;
	    midiOutPrepareHeader(mom->ChannelMap[chn]->hMidi, &mh, sizeof(mh));
	    ret = midiOutLongMsg(mom->ChannelMap[chn]->hMidi, &mh, sizeof(mh));
349 350 351
	    /* As of 2009, wineXYZ.drv's LongData handlers are synchronous */
	    if (!ret && !(mh.dwFlags & MHDR_DONE))
		FIXME("wait until MHDR_DONE\n");
352 353 354 355
	    midiOutUnprepareHeader(mom->ChannelMap[chn]->hMidi, &mh, sizeof(mh));
	    if (ret != MMSYSERR_NOERROR) break;
	}
    }
356 357
    lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
    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 365 366 367 368 369 370 371 372
    BYTE	lb = LOBYTE(LOWORD(dwParam));
    WORD	chn = lb & 0x0F;
    DWORD	ret = MMSYSERR_NOERROR;

    if (MIDIMAP_IsBadData(mom))
	return MMSYSERR_ERROR;

    if (!mom->ChannelMap[chn]) return MMSYSERR_NOERROR;

373
    switch (lb & 0xF0)
374 375 376 377 378 379 380 381
    {
    case 0x80:
    case 0x90:
    case 0xA0:
    case 0xB0:
    case 0xC0:
    case 0xD0:
    case 0xE0:
382
	if (mom->ChannelMap[chn]->loaded == 0)
383 384 385 386 387 388 389
	{
	    if (midiOutOpen(&mom->ChannelMap[chn]->hMidi, mom->ChannelMap[chn]->uDevID,
			    0L, 0L, CALLBACK_NULL) == MMSYSERR_NOERROR)
		mom->ChannelMap[chn]->loaded = 1;
	    else
		mom->ChannelMap[chn]->loaded = -1;
	    /* 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 396 397 398 399
	{
	    /* change channel */
	    dwParam &= ~0x0F;
	    dwParam |= mom->ChannelMap[chn]->aChn[chn];

	    if ((LOBYTE(LOWORD(dwParam)) & 0xF0) == 0xC0 /* program change */ &&
400
		mom->ChannelMap[chn]->lpbPatch)
401 402 403 404 405 406 407 408 409 410 411
	    {
		BYTE patch = HIBYTE(LOWORD(dwParam));

		/* change patch */
		dwParam &= ~0x0000FF00;
		dwParam |= mom->ChannelMap[chn]->lpbPatch[patch];
	    }
	    ret = midiOutShortMsg(mom->ChannelMap[chn]->hMidi, dwParam);
	}
	break;
    case 0xF0:
412
	for (chn = 0; chn < 16; chn++)
413 414 415 416 417 418
	{
	    if (mom->ChannelMap[chn]->loaded > 0)
		ret = midiOutShortMsg(mom->ChannelMap[chn]->hMidi, dwParam);
	}
	break;
    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 441
    if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
    if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
442 443 444

    lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
    return MMSYSERR_NOERROR;
445 446
}

447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
static DWORD modGetVolume(MIDIMAPDATA* mom, DWORD* lpdwVolume)
{
    if (!lpdwVolume) return MMSYSERR_INVALPARAM;
    *lpdwVolume = 0xFFFFFFFF; /* tests show this initial value */
    return MMSYSERR_NOERROR;
}

static DWORD modSetVolume(MIDIMAPDATA* mom, DWORD dwVolume)
{
    /* Native forwards it to some underlying device
     * GetVolume returns what was last set here. */
    FIXME("stub\n");
    return MMSYSERR_NOERROR;
}

462
static DWORD modGetDevCaps(UINT wDevID, MIDIMAPDATA* mom, LPMIDIOUTCAPSW lpMidiCaps, DWORD_PTR size)
463
{
464 465 466 467 468 469 470 471
    static const MIDIOUTCAPSW mappercaps = {
	0x00FF, 0x0001, 0x0100, /* Manufacturer and Product ID */
	{'W','i','n','e',' ','m','i','d','i',' ','m','a','p','p','e','r',0},
	MOD_MAPPER, 0, 0, 0xFFFF,
	/* Native returns volume caps of underlying device + MIDICAPS_STREAM */
	MIDICAPS_VOLUME|MIDICAPS_LRVOLUME
    };
    if (lpMidiCaps == NULL) return MMSYSERR_INVALPARAM;
472
    if (!numMidiOutPorts) return MMSYSERR_BADDEVICEID;
473 474

    memcpy(lpMidiCaps, &mappercaps, min(size, sizeof(*lpMidiCaps)));
475
    return MMSYSERR_NOERROR;
476 477
}

478
static	DWORD	modReset(MIDIMAPDATA* mom)
479
{
480 481 482 483 484 485
    WORD	chn;
    DWORD	ret = MMSYSERR_NOERROR;

    if (MIDIMAP_IsBadData(mom))
	return MMSYSERR_ERROR;

486
    for (chn = 0; chn < 16; chn++)
487
    {
488
	if (mom->ChannelMap[chn] && mom->ChannelMap[chn]->loaded > 0)
489 490 491 492 493 494
	{
	    ret = midiOutReset(mom->ChannelMap[chn]->hMidi);
	    if (ret != MMSYSERR_NOERROR) break;
	}
    }
    return ret;
495 496
}

497 498 499
static LRESULT MIDIMAP_drvOpen(void);
static LRESULT MIDIMAP_drvClose(void);

500
/**************************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
501
 * 				modMessage (MIDIMAP.@)
502
 */
503 504
DWORD WINAPI MIDIMAP_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
				DWORD_PTR dwParam1, DWORD_PTR dwParam2)
505
{
506
    TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
507
	  wDevID, wMsg, dwUser, dwParam1, dwParam2);
508 509

    switch (wMsg)
510
    {
511
    case DRVM_INIT:
512
        return MIDIMAP_drvOpen();
513
    case DRVM_EXIT:
514
        return MIDIMAP_drvClose();
515 516 517 518 519
    case DRVM_ENABLE:
    case DRVM_DISABLE:
	/* FIXME: Pretend this is supported */
	return 0;

520
    case MODM_OPEN: return modOpen((DWORD_PTR *)dwUser, (LPMIDIOPENDESC)dwParam1, dwParam2);
521 522 523 524 525 526 527 528
    case MODM_CLOSE:	 	return modClose		((MIDIMAPDATA*)dwUser);

    case MODM_DATA:		return modData		((MIDIMAPDATA*)dwUser, dwParam1);
    case MODM_LONGDATA:		return modLongData      ((MIDIMAPDATA*)dwUser, (LPMIDIHDR)dwParam1,     dwParam2);
    case MODM_PREPARE:	 	return modPrepare	((MIDIMAPDATA*)dwUser, (LPMIDIHDR)dwParam1, 	dwParam2);
    case MODM_UNPREPARE: 	return modUnprepare	((MIDIMAPDATA*)dwUser, (LPMIDIHDR)dwParam1, 	dwParam2);
    case MODM_RESET:		return modReset		((MIDIMAPDATA*)dwUser);

529
    case MODM_GETDEVCAPS:	return modGetDevCaps	(wDevID, (MIDIMAPDATA*)dwUser, (LPMIDIOUTCAPSW)dwParam1,dwParam2);
530
    case MODM_GETNUMDEVS:	return 1;
531 532
    case MODM_GETVOLUME:	return modGetVolume	((MIDIMAPDATA*)dwUser, (DWORD*)dwParam1);
    case MODM_SETVOLUME:	return modSetVolume	((MIDIMAPDATA*)dwUser, dwParam1);
533
    default:
534
	FIXME("unknown message %d!\n", wMsg);
535 536 537 538 539 540 541 542 543
    }
    return MMSYSERR_NOTSUPPORTED;
}

/*======================================================================*
 *                  Driver part                                         *
 *======================================================================*/

/**************************************************************************
544
 * 				MIDIMAP_drvOpen			[internal]
545
 */
546
static LRESULT MIDIMAP_drvOpen(void)
547
{
548
    MIDIOUTCAPSW	moc;
549 550 551
    unsigned		dev, i;

    if (midiOutPorts)
552
	return 0;
553

554
    numMidiOutPorts = midiOutGetNumDevs();
555
    midiOutPorts = HeapAlloc(GetProcessHeap(), 0,
556
			     numMidiOutPorts * sizeof(MIDIOUTPORT));
557
    for (dev = 0; dev < numMidiOutPorts; dev++)
558
    {
559
	if (midiOutGetDevCapsW(dev, &moc, sizeof(moc)) == 0L)
560
	{
561
	    strcpyW(midiOutPorts[dev].name, moc.szPname);
562 563
	    midiOutPorts[dev].loaded = 0;
	    midiOutPorts[dev].hMidi = 0;
Jeff Smith's avatar
Jeff Smith committed
564
	    midiOutPorts[dev].uDevID = dev;
565 566 567 568
	    midiOutPorts[dev].lpbPatch = NULL;
	    for (i = 0; i < 16; i++)
		midiOutPorts[dev].aChn[i] = i;
	}
569
	else
570 571 572 573 574
	{
	    midiOutPorts[dev].loaded = -1;
	}
    }

575 576 577 578
    return 1;
}

/**************************************************************************
579
 * 				MIDIMAP_drvClose		[internal]
580
 */
581
static LRESULT MIDIMAP_drvClose(void)
582
{
583
    if (midiOutPorts)
584 585 586
    {
	HeapFree(GetProcessHeap(), 0, midiOutPorts);
	midiOutPorts = NULL;
587 588 589 590 591 592
	return 1;
    }
    return 0;
}

/**************************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
593
 * 				DriverProc (MIDIMAP.@)
594
 */
595 596
LRESULT CALLBACK MIDIMAP_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
                                    LPARAM dwParam1, LPARAM dwParam2)
597 598 599
{
/* EPP     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",  */
/* EPP 	  dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
600 601

    switch (wMsg)
602
    {
603 604
    case DRV_LOAD:		return 1;
    case DRV_FREE:		return 1;
605 606
    case DRV_OPEN:		return 1;
    case DRV_CLOSE:		return 1;
607 608 609 610 611 612 613 614 615 616
    case DRV_ENABLE:		return 1;
    case DRV_DISABLE:		return 1;
    case DRV_QUERYCONFIGURE:	return 1;
    case DRV_CONFIGURE:		MessageBoxA(0, "MIDIMAP MultiMedia Driver !", "OSS Driver", MB_OK);	return 1;
    case DRV_INSTALL:		return DRVCNF_RESTART;
    case DRV_REMOVE:		return DRVCNF_RESTART;
    default:
	return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
    }
}