2 * MIDI driver for macOS (PE-side)
4 * Copyright 1994 Martin Ayotte
5 * Copyright 1998 Luiz Otavio L. Zorzella
6 * Copyright 1998, 1999 Eric POUECH
7 * Copyright 2005, 2006 Emmanuel Maillard
8 * Copyright 2021 Huw Davies
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "wine/port.h"
38 #include "mmdeviceapi.h"
39 #include "audioclient.h"
40 #include "wine/unicode.h"
41 #include "wine/debug.h"
42 #include "wine/unixlib.h"
43 #include "coreaudio.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(midi
);
48 #include <CoreAudio/CoreAudio.h>
50 #define WINE_DEFINITIONS
53 static DWORD MIDIOut_NumDevs
= 0;
54 static DWORD MIDIIn_NumDevs
= 0;
56 static CRITICAL_SECTION midiInLock
; /* Critical section for MIDI In */
57 static CFStringRef MIDIInThreadPortName
;
59 static DWORD WINAPI
MIDIIn_MessageThread(LPVOID p
);
61 static MIDIPortRef MIDIInPort
= NULL
;
62 static MIDIPortRef MIDIOutPort
= NULL
;
64 MIDIDestination
*destinations
;
67 static void notify_client(struct notify_context
*notify
)
69 TRACE("dev_id=%d msg=%d param1=%04lX param2=%04lX\n", notify
->dev_id
, notify
->msg
, notify
->param_1
, notify
->param_2
);
71 DriverCallback(notify
->callback
, notify
->flags
, notify
->device
, notify
->msg
,
72 notify
->instance
, notify
->param_1
, notify
->param_2
);
75 static LONG
CoreAudio_MIDIInit(void)
77 struct midi_init_params params
;
82 UNIX_CALL(midi_init
, ¶ms
);
83 if (err
!= DRV_SUCCESS
)
85 ERR("can't create midi client\n");
89 MIDIOut_NumDevs
= params
.num_dests
;
90 MIDIIn_NumDevs
= params
.num_srcs
;
91 destinations
= params
.dests
;
92 sources
= params
.srcs
;
93 MIDIOutPort
= params
.midi_out_port
;
94 MIDIInPort
= params
.midi_in_port
;
96 if (MIDIIn_NumDevs
> 0)
98 InitializeCriticalSection(&midiInLock
);
99 midiInLock
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": midiInLock");
101 MIDIInThreadPortName
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("MIDIInThreadPortName.%u"), getpid());
102 CloseHandle( CreateThread(NULL
, 0, MIDIIn_MessageThread
, NULL
, 0, NULL
));
107 static LONG
CoreAudio_MIDIRelease(void)
111 UNIX_CALL(midi_release
, NULL
);
115 if (MIDIIn_NumDevs
> 0)
117 midiInLock
.DebugInfo
->Spare
[0] = 0;
118 DeleteCriticalSection(&midiInLock
);
125 /**************************************************************************
126 * MIDI_NotifyClient [internal]
128 static void MIDI_NotifyClient(UINT wDevID
, WORD wMsg
, DWORD_PTR dwParam1
, DWORD_PTR dwParam2
)
130 DWORD_PTR dwCallBack
;
133 DWORD_PTR dwInstance
;
135 TRACE("wDevID=%d wMsg=%d dwParm1=%04lX dwParam2=%04lX\n", wDevID
, wMsg
, dwParam1
, dwParam2
);
145 dwCallBack
= sources
[wDevID
].midiDesc
.dwCallback
;
146 uFlags
= sources
[wDevID
].wFlags
;
147 hDev
= sources
[wDevID
].midiDesc
.hMidi
;
148 dwInstance
= sources
[wDevID
].midiDesc
.dwInstance
;
151 ERR("Unsupported MSW-MIDI message %u\n", wMsg
);
155 DriverCallback(dwCallBack
, uFlags
, hDev
, wMsg
, dwInstance
, dwParam1
, dwParam2
);
158 static DWORD
MIDIOut_GetNumDevs(void)
161 return MIDIOut_NumDevs
;
164 static DWORD
MIDIOut_GetVolume(WORD wDevID
, DWORD
*lpdwVolume
)
166 TRACE("%d\n", wDevID
);
168 if (wDevID
>= MIDIOut_NumDevs
) {
169 WARN("bad device ID : %d\n", wDevID
);
170 return MMSYSERR_BADDEVICEID
;
172 if (lpdwVolume
== NULL
) {
173 WARN("Invalid Parameter\n");
174 return MMSYSERR_INVALPARAM
;
177 if (destinations
[wDevID
].caps
.wTechnology
== MOD_SYNTH
)
181 AudioUnit_GetVolume(destinations
[wDevID
].synth
, &left
, &right
);
183 *lpdwVolume
= (WORD
) (left
* 0xFFFF) + ((WORD
) (right
* 0xFFFF) << 16);
185 return MMSYSERR_NOERROR
;
188 return MMSYSERR_NOTSUPPORTED
;
191 static DWORD
MIDIOut_SetVolume(WORD wDevID
, DWORD dwVolume
)
193 TRACE("%d\n", wDevID
);
195 if (wDevID
>= MIDIOut_NumDevs
) {
196 WARN("bad device ID : %d\n", wDevID
);
197 return MMSYSERR_BADDEVICEID
;
199 if (destinations
[wDevID
].caps
.wTechnology
== MOD_SYNTH
)
204 left
= LOWORD(dwVolume
) / 65535.0f
;
205 right
= HIWORD(dwVolume
) / 65535.0f
;
206 AudioUnit_SetVolume(destinations
[wDevID
].synth
, left
, right
);
208 return MMSYSERR_NOERROR
;
211 return MMSYSERR_NOTSUPPORTED
;
214 static DWORD
MIDIOut_Reset(WORD wDevID
)
218 TRACE("%d\n", wDevID
);
220 if (wDevID
>= MIDIOut_NumDevs
) {
221 WARN("bad device ID : %d\n", wDevID
);
222 return MMSYSERR_BADDEVICEID
;
224 if (destinations
[wDevID
].caps
.wTechnology
== MOD_SYNTH
)
226 for (chn
= 0; chn
< 16; chn
++) {
227 /* turn off every note */
228 MusicDeviceMIDIEvent(destinations
[wDevID
].synth
, 0xB0 | chn
, 0x7B, 0, 0);
229 /* remove sustain on channel */
230 MusicDeviceMIDIEvent(destinations
[wDevID
].synth
, 0xB0 | chn
, 0x40, 0, 0);
233 else FIXME("MOD_MIDIPORT\n");
235 /* FIXME: the LongData buffers must also be returned to the app */
236 return MMSYSERR_NOERROR
;
239 static DWORD
MIDIIn_Open(WORD wDevID
, LPMIDIOPENDESC lpDesc
, DWORD dwFlags
)
241 TRACE("wDevID=%d lpDesc=%p dwFlags=%08x\n", wDevID
, lpDesc
, dwFlags
);
243 if (lpDesc
== NULL
) {
244 WARN("Invalid Parameter\n");
245 return MMSYSERR_INVALPARAM
;
247 if (wDevID
>= MIDIIn_NumDevs
) {
248 WARN("bad device ID : %d\n", wDevID
);
249 return MMSYSERR_BADDEVICEID
;
251 if (sources
[wDevID
].midiDesc
.hMidi
!= 0) {
252 WARN("device already open !\n");
253 return MMSYSERR_ALLOCATED
;
255 if ((dwFlags
& MIDI_IO_STATUS
) != 0) {
256 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
257 dwFlags
&= ~MIDI_IO_STATUS
;
259 if ((dwFlags
& ~CALLBACK_TYPEMASK
) != 0) {
260 FIXME("Bad dwFlags\n");
261 return MMSYSERR_INVALFLAG
;
264 sources
[wDevID
].wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
265 sources
[wDevID
].lpQueueHdr
= NULL
;
266 sources
[wDevID
].midiDesc
= *lpDesc
;
267 sources
[wDevID
].startTime
= 0;
268 sources
[wDevID
].state
= 0;
270 MIDI_NotifyClient(wDevID
, MIM_OPEN
, 0L, 0L);
271 return MMSYSERR_NOERROR
;
274 static DWORD
MIDIIn_Close(WORD wDevID
)
276 DWORD ret
= MMSYSERR_NOERROR
;
278 TRACE("wDevID=%d\n", wDevID
);
280 if (wDevID
>= MIDIIn_NumDevs
) {
281 WARN("bad device ID : %d\n", wDevID
);
282 return MMSYSERR_BADDEVICEID
;
285 if (sources
[wDevID
].midiDesc
.hMidi
== 0) {
286 WARN("device not opened !\n");
287 return MMSYSERR_ERROR
;
289 if (sources
[wDevID
].lpQueueHdr
!= 0) {
290 return MIDIERR_STILLPLAYING
;
293 MIDI_NotifyClient(wDevID
, MIM_CLOSE
, 0L, 0L);
294 sources
[wDevID
].midiDesc
.hMidi
= 0;
298 static DWORD
MIDIIn_AddBuffer(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
300 TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID
, lpMidiHdr
, dwSize
);
302 if (wDevID
>= MIDIIn_NumDevs
) {
303 WARN("bad device ID : %d\n", wDevID
);
304 return MMSYSERR_BADDEVICEID
;
306 if (lpMidiHdr
== NULL
) {
307 WARN("Invalid Parameter\n");
308 return MMSYSERR_INVALPARAM
;
310 if (dwSize
< offsetof(MIDIHDR
,dwOffset
)) {
311 WARN("Invalid Parameter\n");
312 return MMSYSERR_INVALPARAM
;
314 if (lpMidiHdr
->dwBufferLength
== 0) {
315 WARN("Invalid Parameter\n");
316 return MMSYSERR_INVALPARAM
;
318 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
) {
319 WARN("Still playing\n");
320 return MIDIERR_STILLPLAYING
;
322 if (!(lpMidiHdr
->dwFlags
& MHDR_PREPARED
)) {
323 WARN("Unprepared\n");
324 return MIDIERR_UNPREPARED
;
327 EnterCriticalSection(&midiInLock
);
328 lpMidiHdr
->dwFlags
&= ~WHDR_DONE
;
329 lpMidiHdr
->dwFlags
|= MHDR_INQUEUE
;
330 lpMidiHdr
->dwBytesRecorded
= 0;
331 lpMidiHdr
->lpNext
= 0;
332 if (sources
[wDevID
].lpQueueHdr
== 0) {
333 sources
[wDevID
].lpQueueHdr
= lpMidiHdr
;
336 for (ptr
= sources
[wDevID
].lpQueueHdr
;
339 ptr
->lpNext
= lpMidiHdr
;
341 LeaveCriticalSection(&midiInLock
);
343 return MMSYSERR_NOERROR
;
346 static DWORD
MIDIIn_Prepare(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
348 TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID
, lpMidiHdr
, dwSize
);
350 if (dwSize
< offsetof(MIDIHDR
,dwOffset
) || lpMidiHdr
== 0 || lpMidiHdr
->lpData
== 0)
351 return MMSYSERR_INVALPARAM
;
352 if (lpMidiHdr
->dwFlags
& MHDR_PREPARED
)
353 return MMSYSERR_NOERROR
;
355 lpMidiHdr
->lpNext
= 0;
356 lpMidiHdr
->dwFlags
|= MHDR_PREPARED
;
357 lpMidiHdr
->dwFlags
&= ~(MHDR_DONE
|MHDR_INQUEUE
); /* flags cleared since w2k */
358 return MMSYSERR_NOERROR
;
361 static DWORD
MIDIIn_Unprepare(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
363 TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID
, lpMidiHdr
, dwSize
);
365 if (dwSize
< offsetof(MIDIHDR
,dwOffset
) || lpMidiHdr
== 0 || lpMidiHdr
->lpData
== 0)
366 return MMSYSERR_INVALPARAM
;
367 if (!(lpMidiHdr
->dwFlags
& MHDR_PREPARED
))
368 return MMSYSERR_NOERROR
;
369 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
)
370 return MIDIERR_STILLPLAYING
;
372 lpMidiHdr
->dwFlags
&= ~MHDR_PREPARED
;
373 return MMSYSERR_NOERROR
;
376 static DWORD
MIDIIn_GetDevCaps(WORD wDevID
, LPMIDIINCAPSW lpCaps
, DWORD dwSize
)
378 TRACE("wDevID=%d lpCaps=%p dwSize=%d\n", wDevID
, lpCaps
, dwSize
);
380 if (lpCaps
== NULL
) {
381 WARN("Invalid Parameter\n");
382 return MMSYSERR_INVALPARAM
;
385 if (wDevID
>= MIDIIn_NumDevs
) {
386 WARN("bad device ID : %d\n", wDevID
);
387 return MMSYSERR_BADDEVICEID
;
389 memcpy(lpCaps
, &sources
[wDevID
].caps
, min(dwSize
, sizeof(*lpCaps
)));
390 return MMSYSERR_NOERROR
;
393 static DWORD
MIDIIn_GetNumDevs(void)
396 return MIDIIn_NumDevs
;
399 static DWORD
MIDIIn_Start(WORD wDevID
)
401 TRACE("%d\n", wDevID
);
403 if (wDevID
>= MIDIIn_NumDevs
) {
404 WARN("bad device ID : %d\n", wDevID
);
405 return MMSYSERR_BADDEVICEID
;
407 sources
[wDevID
].state
= 1;
408 sources
[wDevID
].startTime
= GetTickCount();
409 return MMSYSERR_NOERROR
;
412 static DWORD
MIDIIn_Stop(WORD wDevID
)
414 TRACE("%d\n", wDevID
);
415 if (wDevID
>= MIDIIn_NumDevs
) {
416 WARN("bad device ID : %d\n", wDevID
);
417 return MMSYSERR_BADDEVICEID
;
419 sources
[wDevID
].state
= 0;
420 return MMSYSERR_NOERROR
;
423 static DWORD
MIDIIn_Reset(WORD wDevID
)
425 DWORD dwTime
= GetTickCount();
427 TRACE("%d\n", wDevID
);
428 if (wDevID
>= MIDIIn_NumDevs
) {
429 WARN("bad device ID : %d\n", wDevID
);
430 return MMSYSERR_BADDEVICEID
;
433 EnterCriticalSection(&midiInLock
);
434 while (sources
[wDevID
].lpQueueHdr
) {
435 LPMIDIHDR lpMidiHdr
= sources
[wDevID
].lpQueueHdr
;
436 sources
[wDevID
].lpQueueHdr
= lpMidiHdr
->lpNext
;
437 lpMidiHdr
->dwFlags
&= ~MHDR_INQUEUE
;
438 lpMidiHdr
->dwFlags
|= MHDR_DONE
;
439 /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
440 MIDI_NotifyClient(wDevID
, MIM_LONGDATA
, (DWORD_PTR
)lpMidiHdr
, dwTime
);
442 LeaveCriticalSection(&midiInLock
);
444 return MMSYSERR_NOERROR
;
448 * MIDI In Mach message handling
450 static CFDataRef
MIDIIn_MessageHandler(CFMessagePortRef local
, SInt32 msgid
, CFDataRef data
, void *info
)
452 MIDIMessage
*msg
= NULL
;
454 MIDISource
*src
= NULL
;
463 msg
= (MIDIMessage
*) CFDataGetBytePtr(data
);
464 TRACE("devID=%d\n", msg
->devID
);
465 for (i
= 0; i
< msg
->length
; ++i
) {
466 TRACE("%02X ", msg
->data
[i
]);
469 src
= &sources
[msg
->devID
];
472 TRACE("input not started, thrown away\n");
476 sysexStart
= (msg
->data
[0] == 0xF0);
478 if (sysexStart
|| src
->state
& 2) {
480 int len
= msg
->length
;
483 TRACE("Receiving sysex message\n");
487 EnterCriticalSection(&midiInLock
);
488 currentTime
= GetTickCount() - src
->startTime
;
491 LPMIDIHDR lpMidiHdr
= src
->lpQueueHdr
;
493 if (lpMidiHdr
!= NULL
) {
494 int copylen
= min(len
, lpMidiHdr
->dwBufferLength
- lpMidiHdr
->dwBytesRecorded
);
495 memcpy(lpMidiHdr
->lpData
+ lpMidiHdr
->dwBytesRecorded
, msg
->data
+ pos
, copylen
);
496 lpMidiHdr
->dwBytesRecorded
+= copylen
;
500 TRACE("Copied %d bytes of sysex message\n", copylen
);
502 if ((lpMidiHdr
->dwBytesRecorded
== lpMidiHdr
->dwBufferLength
) ||
503 (*(BYTE
*)(lpMidiHdr
->lpData
+ lpMidiHdr
->dwBytesRecorded
- 1) == 0xF7)) {
504 TRACE("Sysex message complete (or buffer limit reached), dispatching %d bytes\n", lpMidiHdr
->dwBytesRecorded
);
505 src
->lpQueueHdr
= lpMidiHdr
->lpNext
;
506 lpMidiHdr
->dwFlags
&= ~MHDR_INQUEUE
;
507 lpMidiHdr
->dwFlags
|= MHDR_DONE
;
508 MIDI_NotifyClient(msg
->devID
, MIM_LONGDATA
, (DWORD_PTR
)lpMidiHdr
, currentTime
);
513 FIXME("Sysex data received but no buffer to store it!\n");
518 LeaveCriticalSection(&midiInLock
);
522 EnterCriticalSection(&midiInLock
);
523 currentTime
= GetTickCount() - src
->startTime
;
525 while (pos
< msg
->length
)
528 switch (msg
->data
[pos
] & 0xF0)
531 sendData
= (msg
->data
[pos
] << 0);
537 sendData
= (msg
->data
[pos
+ 1] << 8) | (msg
->data
[pos
] << 0);
541 sendData
= (msg
->data
[pos
+ 2] << 16) |
542 (msg
->data
[pos
+ 1] << 8) |
543 (msg
->data
[pos
] << 0);
547 MIDI_NotifyClient(msg
->devID
, MIM_DATA
, sendData
, currentTime
);
549 LeaveCriticalSection(&midiInLock
);
552 CFRunLoopStop(CFRunLoopGetCurrent());
558 static DWORD WINAPI
MIDIIn_MessageThread(LPVOID p
)
560 CFMessagePortRef local
;
561 CFRunLoopSourceRef source
;
564 local
= CFMessagePortCreateLocal(kCFAllocatorDefault
, MIDIInThreadPortName
, &MIDIIn_MessageHandler
, NULL
, &info
);
566 source
= CFMessagePortCreateRunLoopSource(kCFAllocatorDefault
, local
, 0);
567 CFRunLoopAddSource(CFRunLoopGetCurrent(), source
, kCFRunLoopDefaultMode
);
571 CFRunLoopSourceInvalidate(source
);
574 CFRelease(MIDIInThreadPortName
);
575 MIDIInThreadPortName
= NULL
;
580 /**************************************************************************
583 DWORD WINAPI
CoreAudio_modMessage(UINT wDevID
, UINT wMsg
, DWORD_PTR dwUser
, DWORD_PTR dwParam1
, DWORD_PTR dwParam2
)
585 struct midi_out_message_params params
;
586 struct notify_context notify
;
589 TRACE("%d %08x %08lx %08lx %08lx\n", wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
592 case MODM_GETNUMDEVS
:
593 return MIDIOut_GetNumDevs();
595 return MIDIOut_GetVolume(wDevID
, (DWORD
*)dwParam1
);
597 return MIDIOut_SetVolume(wDevID
, dwParam1
);
599 return MIDIOut_Reset(wDevID
);
602 params
.dev_id
= wDevID
;
604 params
.user
= dwUser
;
605 params
.param_1
= dwParam1
;
606 params
.param_2
= dwParam2
;
608 params
.notify
= ¬ify
;
610 UNIX_CALL(midi_out_message
, ¶ms
);
612 if (!err
&& notify
.send_notify
) notify_client(¬ify
);
617 /**************************************************************************
620 DWORD WINAPI
CoreAudio_midMessage(UINT wDevID
, UINT wMsg
, DWORD_PTR dwUser
, DWORD_PTR dwParam1
, DWORD_PTR dwParam2
)
622 TRACE("%d %08x %08lx %08lx %08lx\n", wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
630 return MIDIIn_Open(wDevID
, (LPMIDIOPENDESC
)dwParam1
, dwParam2
);
632 return MIDIIn_Close(wDevID
);
634 return MIDIIn_AddBuffer(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
636 return MIDIIn_Prepare(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
638 return MIDIIn_Unprepare(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
639 case MIDM_GETDEVCAPS
:
640 return MIDIIn_GetDevCaps(wDevID
, (LPMIDIINCAPSW
) dwParam1
, dwParam2
);
641 case MIDM_GETNUMDEVS
:
642 return MIDIIn_GetNumDevs();
644 return MIDIIn_Start(wDevID
);
646 return MIDIIn_Stop(wDevID
);
648 return MIDIIn_Reset(wDevID
);
650 TRACE("Unsupported message\n");
652 return MMSYSERR_NOTSUPPORTED
;
655 /**************************************************************************
656 * CoreAudio_drvLoad [internal]
658 static LRESULT
CoreAudio_drvLoad(void)
662 if (CoreAudio_MIDIInit() != DRV_SUCCESS
)
668 /**************************************************************************
669 * CoreAudio_drvFree [internal]
671 static LRESULT
CoreAudio_drvFree(void)
674 CoreAudio_MIDIRelease();
678 /**************************************************************************
679 * CoreAudio_drvOpen [internal]
681 static LRESULT
CoreAudio_drvOpen(LPSTR str
)
683 TRACE("(%s)\n", str
);
687 /**************************************************************************
688 * CoreAudio_drvClose [internal]
690 static DWORD
CoreAudio_drvClose(DWORD dwDevID
)
692 TRACE("(%08x)\n", dwDevID
);
696 /**************************************************************************
697 * DriverProc (WINECOREAUDIO.1)
699 LRESULT CALLBACK
CoreAudio_DriverProc(DWORD_PTR dwDevID
, HDRVR hDriv
, UINT wMsg
,
700 LPARAM dwParam1
, LPARAM dwParam2
)
702 TRACE("(%08lX, %p, %s (%08X), %08lX, %08lX)\n",
703 dwDevID
, hDriv
, wMsg
== DRV_LOAD
? "DRV_LOAD" :
704 wMsg
== DRV_FREE
? "DRV_FREE" :
705 wMsg
== DRV_OPEN
? "DRV_OPEN" :
706 wMsg
== DRV_CLOSE
? "DRV_CLOSE" :
707 wMsg
== DRV_ENABLE
? "DRV_ENABLE" :
708 wMsg
== DRV_DISABLE
? "DRV_DISABLE" :
709 wMsg
== DRV_QUERYCONFIGURE
? "DRV_QUERYCONFIGURE" :
710 wMsg
== DRV_CONFIGURE
? "DRV_CONFIGURE" :
711 wMsg
== DRV_INSTALL
? "DRV_INSTALL" :
712 wMsg
== DRV_REMOVE
? "DRV_REMOVE" : "UNKNOWN",
713 wMsg
, dwParam1
, dwParam2
);
716 case DRV_LOAD
: return CoreAudio_drvLoad();
717 case DRV_FREE
: return CoreAudio_drvFree();
718 case DRV_OPEN
: return CoreAudio_drvOpen((LPSTR
)dwParam1
);
719 case DRV_CLOSE
: return CoreAudio_drvClose(dwDevID
);
720 case DRV_ENABLE
: return 1;
721 case DRV_DISABLE
: return 1;
722 case DRV_QUERYCONFIGURE
: return 1;
723 case DRV_CONFIGURE
: MessageBoxA(0, "CoreAudio driver!", "CoreAudio driver", MB_OK
); return 1;
724 case DRV_INSTALL
: return DRVCNF_RESTART
;
725 case DRV_REMOVE
: return DRVCNF_RESTART
;
727 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);