1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
4 * Sample MIDI Wine Driver for ALSA (basically Linux)
6 * Copyright 1994 Martin Ayotte
7 * Copyright 1998 Luiz Otavio L. Zorzella (init procedures)
8 * Copyright 1998/1999 Eric POUECH :
9 * 98/7 changes for making this MIDI driver work on OSS
10 * current support is limited to MIDI ports of OSS systems
11 * 98/9 rewriting MCI code for MIDI
12 * 98/11 splitted in midi.c and mcimidi.c
13 * Copyright 2003 Christian Costa :
16 * This library is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU Lesser General Public
18 * License as published by the Free Software Foundation; either
19 * version 2.1 of the License, or (at your option) any later version.
21 * This library is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * Lesser General Public License for more details.
26 * You should have received a copy of the GNU Lesser General Public
27 * License along with this library; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 * TODO: Finish midi record
53 #include "wine/debug.h"
55 WINE_DEFAULT_DEBUG_CHANNEL(midi
);
57 #if defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1)
60 int state
; /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
62 MIDIOPENDESC midiDesc
;
66 unsigned char incoming
[3];
67 unsigned char incPrev
;
77 MIDIOPENDESC midiDesc
;
81 void* lpExtra
; /* according to port type (MIDI, FM...), extra data when needed */
86 static WINE_MIDIIN MidiInDev
[MAX_MIDIINDRV
];
87 static WINE_MIDIOUT MidiOutDev
[MAX_MIDIOUTDRV
];
89 /* this is the total number of MIDI out devices found (synth and port) */
90 static int MODM_NumDevs
= 0;
91 /* this is the total number of MIDI out devices found */
92 static int MIDM_NumDevs
= 0;
94 static snd_seq_t
* midiSeq
= NULL
;
95 static int numOpenMidiSeq
= 0;
96 static UINT midiInTimerID
= 0;
97 static int numStartedMidiIn
= 0;
102 /*======================================================================*
103 * Low level MIDI implementation *
104 *======================================================================*/
106 static int midiOpenSeq(int);
107 static int midiCloseSeq(void);
109 #if 0 /* Debug Purpose */
110 static void error_handler(const char* file
, int line
, const char* function
, int err
, const char* fmt
, ...)
116 fprintf(stderr
, "ALSA lib %s:%i:(%s) ", file
, line
, function
);
117 vfprintf(stderr
, fmt
, arg
);
119 fprintf(stderr
, ": %s", snd_strerror(err
));
125 /**************************************************************************
126 * MIDI_unixToWindowsDeviceType [internal]
128 * return the Windows equivalent to a Unix Device Type
131 static int MIDI_AlsaToWindowsDeviceType(int type
)
133 /* MOD_MIDIPORT output port
134 * MOD_SYNTH generic internal synth
135 * MOD_SQSYNTH square wave internal synth
136 * MOD_FMSYNTH FM internal synth
137 * MOD_MAPPER MIDI mapper
138 * MOD_WAVETABLE hardware watetable internal synth
139 * MOD_SWSYNTH software internal synth
142 /* FIXME Is this really the correct equivalence from ALSA to
143 Windows Sound type */
145 if (type
& SND_SEQ_PORT_TYPE_SYNTH
)
148 if (type
& (SND_SEQ_PORT_TYPE_DIRECT_SAMPLE
|SND_SEQ_PORT_TYPE_SAMPLE
))
151 if (type
& SND_SEQ_PORT_TYPE_MIDI_GENERIC
)
154 ERR("Cannot determine the type of this midi device. Assuming FM Synth\n");
158 /**************************************************************************
159 * MIDI_NotifyClient [internal]
161 static DWORD
MIDI_NotifyClient(UINT wDevID
, WORD wMsg
,
162 DWORD dwParam1
, DWORD dwParam2
)
169 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
170 wDevID
, wMsg
, dwParam1
, dwParam2
);
177 if (wDevID
> MODM_NumDevs
)
178 return MMSYSERR_BADDEVICEID
;
180 dwCallBack
= MidiOutDev
[wDevID
].midiDesc
.dwCallback
;
181 uFlags
= MidiOutDev
[wDevID
].wFlags
;
182 hDev
= MidiOutDev
[wDevID
].midiDesc
.hMidi
;
183 dwInstance
= MidiOutDev
[wDevID
].midiDesc
.dwInstance
;
193 if (wDevID
> MIDM_NumDevs
)
194 return MMSYSERR_BADDEVICEID
;
196 dwCallBack
= MidiInDev
[wDevID
].midiDesc
.dwCallback
;
197 uFlags
= MidiInDev
[wDevID
].wFlags
;
198 hDev
= MidiInDev
[wDevID
].midiDesc
.hMidi
;
199 dwInstance
= MidiInDev
[wDevID
].midiDesc
.dwInstance
;
202 WARN("Unsupported MSW-MIDI message %u\n", wMsg
);
203 return MMSYSERR_ERROR
;
206 return DriverCallback(dwCallBack
, uFlags
, hDev
, wMsg
, dwInstance
, dwParam1
, dwParam2
) ?
210 static int midi_warn
= 1;
211 /**************************************************************************
212 * midiOpenSeq [internal]
214 static int midiOpenSeq(int create_client
)
216 if (numOpenMidiSeq
== 0) {
217 if (snd_seq_open(&midiSeq
, "default", SND_SEQ_OPEN_DUPLEX
, 0) < 0)
221 WARN("Error opening ALSA sequencer.\n");
228 /* Setting the client name is the only init to do */
229 snd_seq_set_client_name(midiSeq
, "WINE midi driver");
231 #if 0 /* FIXME: Is it possible to use a port for READ & WRITE ops */
232 port_in
= port_out
= snd_seq_create_simple_port(midiSeq
, "WINE ALSA Input/Output", SND_SEQ_PORT_CAP_READ
|SND_SEQ_PORT_CAP_WRITE
,
233 SND_SEQ_PORT_TYPE_APPLICATION
);
235 TRACE("Unable to create output port\n");
237 TRACE("Outport port created successfully (%d)\n", port_out
);
239 port_out
= snd_seq_create_simple_port(midiSeq
, "WINE ALSA Output", SND_SEQ_PORT_CAP_READ
,
240 SND_SEQ_PORT_TYPE_APPLICATION
);
242 TRACE("Unable to create output port\n");
244 TRACE("Outport port created successfully (%d)\n", port_out
);
246 port_in
= snd_seq_create_simple_port(midiSeq
, "WINE ALSA Input", SND_SEQ_PORT_CAP_WRITE
,
247 SND_SEQ_PORT_TYPE_APPLICATION
);
249 TRACE("Unable to create input port\n");
251 TRACE("Input port created successfully (%d)\n", port_in
);
259 /**************************************************************************
260 * midiCloseSeq [internal]
262 static int midiCloseSeq(void)
264 if (--numOpenMidiSeq
== 0) {
265 snd_seq_delete_simple_port(midiSeq
, port_out
);
266 snd_seq_delete_simple_port(midiSeq
, port_in
);
267 snd_seq_close(midiSeq
);
273 static VOID WINAPI
midTimeCallback(HWND hwnd
, UINT msg
, UINT id
, DWORD dwTime
)
278 TRACE("(%p, %d, %d, %lu)\n", hwnd
, msg
, id
, dwTime
);
280 npfd
= snd_seq_poll_descriptors_count(midiSeq
, POLLIN
);
281 pfd
= (struct pollfd
*)HeapAlloc(GetProcessHeap(), 0, npfd
* sizeof(struct pollfd
));
282 snd_seq_poll_descriptors(midiSeq
, pfd
, npfd
, POLLIN
);
284 /* Check if a event is present */
285 if (poll(pfd
, npfd
, 0) <= 0) {
286 HeapFree(GetProcessHeap(), 0, pfd
);
290 /* Note: This definitely does not work.
291 * while(snd_seq_event_input_pending(midiSeq, 0) > 0) {
293 snd_seq_event_input(midiSeq, &ev);
295 snd_seq_free_event(ev);
301 snd_seq_event_input(midiSeq
, &ev
);
302 /* Find the target device */
303 for (wDevID
= 0; wDevID
< MIDM_NumDevs
; wDevID
++)
304 if ( (ev
->source
.client
== MidiInDev
[wDevID
].addr
.client
) && (ev
->source
.port
== MidiInDev
[wDevID
].addr
.port
) )
306 if (wDevID
== MIDM_NumDevs
)
307 FIXME("Unexpected event received, type = %x from %d:%d\n", ev
->type
, ev
->source
.client
, ev
->source
.port
);
310 TRACE("Event received, type = %x, device = %d\n", ev
->type
, wDevID
);
313 case SND_SEQ_EVENT_NOTEOFF
:
314 toSend
= (ev
->data
.note
.velocity
<< 16) | (ev
->data
.note
.note
<< 8) | MIDI_CMD_NOTE_OFF
| ev
->data
.control
.channel
;
316 case SND_SEQ_EVENT_NOTEON
:
317 toSend
= (ev
->data
.note
.velocity
<< 16) | (ev
->data
.note
.note
<< 8) | MIDI_CMD_NOTE_ON
| ev
->data
.control
.channel
;
319 case SND_SEQ_EVENT_KEYPRESS
:
320 toSend
= (ev
->data
.note
.velocity
<< 16) | (ev
->data
.note
.note
<< 8) | MIDI_CMD_NOTE_PRESSURE
| ev
->data
.control
.channel
;
322 case SND_SEQ_EVENT_CONTROLLER
:
323 toSend
= (ev
->data
.control
.value
<< 16) | (ev
->data
.control
.param
<< 8) | MIDI_CMD_CONTROL
| ev
->data
.control
.channel
;
325 case SND_SEQ_EVENT_PITCHBEND
:
326 toSend
= (ev
->data
.control
.value
<< 16) | (ev
->data
.control
.param
<< 8) | MIDI_CMD_BENDER
| ev
->data
.control
.channel
;
328 case SND_SEQ_EVENT_PGMCHANGE
:
329 toSend
= (ev
->data
.control
.value
<< 16) | (ev
->data
.control
.param
<< 8) | MIDI_CMD_PGM_CHANGE
| ev
->data
.control
.channel
;
331 case SND_SEQ_EVENT_CHANPRESS
:
332 toSend
= (ev
->data
.control
.value
<< 16) | (ev
->data
.control
.param
<< 8) | MIDI_CMD_CHANNEL_PRESSURE
| ev
->data
.control
.channel
;
334 case SND_SEQ_EVENT_SYSEX
:
336 int len
= ev
->data
.ext
.len
;
337 LPBYTE ptr
= (BYTE
*) ev
->data
.ext
.ptr
;
338 LPMIDIHDR lpMidiHdr
= MidiInDev
[wDevID
].lpQueueHdr
;
340 /* FIXME: Should handle sysex greater that a single buffer */
342 if (len
<= lpMidiHdr
->dwBufferLength
) {
343 lpMidiHdr
->dwBytesRecorded
= len
;
344 memcpy(lpMidiHdr
->lpData
, ptr
, len
);
345 lpMidiHdr
= MidiInDev
[wDevID
].lpQueueHdr
;
346 lpMidiHdr
->dwFlags
&= ~MHDR_INQUEUE
;
347 lpMidiHdr
->dwFlags
|= MHDR_DONE
;
348 MidiInDev
[wDevID
].lpQueueHdr
= (LPMIDIHDR
)lpMidiHdr
->lpNext
;
349 if (MIDI_NotifyClient(wDevID
, MIM_LONGDATA
, (DWORD
)lpMidiHdr
, dwTime
) != MMSYSERR_NOERROR
) {
350 WARN("Couldn't notify client\n");
353 FIXME("No enough space in the buffer to store sysex!\n");
355 FIXME("Sysex received but no buffer to store it!\n");
358 case SND_SEQ_EVENT_SENSING
:
362 FIXME("Unhandled event received, type = %x\n", ev
->type
);
366 TRACE("Sending event %08lx (from %d %d)\n", toSend
, ev
->source
.client
, ev
->source
.port
);
367 /* FIXME: Should use ev->time instead for better accuracy */
368 if (MIDI_NotifyClient(wDevID
, MIM_DATA
, toSend
, dwTime
-MidiInDev
[wDevID
].startTime
) != MMSYSERR_NOERROR
) {
369 WARN("Couldn't notify client\n");
373 snd_seq_free_event(ev
);
374 } while(snd_seq_event_input_pending(midiSeq
, 0) > 0);
376 HeapFree(GetProcessHeap(), 0, pfd
);
379 /**************************************************************************
380 * midGetDevCaps [internal]
382 static DWORD
midGetDevCaps(WORD wDevID
, LPMIDIINCAPSA lpCaps
, DWORD dwSize
)
384 TRACE("(%04X, %p, %08lX);\n", wDevID
, lpCaps
, dwSize
);
386 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
387 if (lpCaps
== NULL
) return MMSYSERR_INVALPARAM
;
389 memcpy(lpCaps
, &MidiInDev
[wDevID
].caps
, min(dwSize
, sizeof(*lpCaps
)));
391 return MMSYSERR_NOERROR
;
395 /**************************************************************************
398 static DWORD
midOpen(WORD wDevID
, LPMIDIOPENDESC lpDesc
, DWORD dwFlags
)
400 TRACE("(%04X, %p, %08lX);\n", wDevID
, lpDesc
, dwFlags
);
402 if (lpDesc
== NULL
) {
403 WARN("Invalid Parameter !\n");
404 return MMSYSERR_INVALPARAM
;
408 * how to check that content of lpDesc is correct ?
410 if (wDevID
>= MIDM_NumDevs
) {
411 WARN("wDevID too large (%u) !\n", wDevID
);
412 return MMSYSERR_BADDEVICEID
;
414 if (MidiInDev
[wDevID
].state
== -1) {
415 WARN("device disabled\n");
416 return MIDIERR_NODEVICE
;
418 if (MidiInDev
[wDevID
].midiDesc
.hMidi
!= 0) {
419 WARN("device already open !\n");
420 return MMSYSERR_ALLOCATED
;
422 if ((dwFlags
& MIDI_IO_STATUS
) != 0) {
423 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
424 dwFlags
&= ~MIDI_IO_STATUS
;
426 if ((dwFlags
& ~CALLBACK_TYPEMASK
) != 0) {
427 FIXME("Bad dwFlags\n");
428 return MMSYSERR_INVALFLAG
;
431 if (midiOpenSeq(1) < 0) {
432 return MMSYSERR_ERROR
;
435 /* Connect our app port to the device port */
436 if (snd_seq_connect_from(midiSeq
, port_in
, MidiInDev
[wDevID
].addr
.client
, MidiInDev
[wDevID
].addr
.port
) < 0)
437 return MMSYSERR_NOTENABLED
;
439 TRACE("input port connected %d %d %d\n",port_in
,MidiInDev
[wDevID
].addr
.client
,MidiInDev
[wDevID
].addr
.port
);
441 if (numStartedMidiIn
++ == 0) {
442 midiInTimerID
= SetTimer(0, 0, 250, midTimeCallback
);
443 if (!midiInTimerID
) {
444 numStartedMidiIn
= 0;
445 WARN("Couldn't start timer for midi-in\n");
447 return MMSYSERR_ERROR
;
449 TRACE("Starting timer (%u) for midi-in\n", midiInTimerID
);
452 MidiInDev
[wDevID
].wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
454 MidiInDev
[wDevID
].lpQueueHdr
= NULL
;
455 MidiInDev
[wDevID
].dwTotalPlayed
= 0;
456 MidiInDev
[wDevID
].bufsize
= 0x3FFF;
457 MidiInDev
[wDevID
].midiDesc
= *lpDesc
;
458 MidiInDev
[wDevID
].state
= 0;
459 MidiInDev
[wDevID
].incLen
= 0;
460 MidiInDev
[wDevID
].startTime
= 0;
462 if (MIDI_NotifyClient(wDevID
, MIM_OPEN
, 0L, 0L) != MMSYSERR_NOERROR
) {
463 WARN("can't notify client !\n");
464 return MMSYSERR_INVALPARAM
;
466 return MMSYSERR_NOERROR
;
469 /**************************************************************************
470 * midClose [internal]
472 static DWORD
midClose(WORD wDevID
)
474 int ret
= MMSYSERR_NOERROR
;
476 TRACE("(%04X);\n", wDevID
);
478 if (wDevID
>= MIDM_NumDevs
) {
479 WARN("wDevID too big (%u) !\n", wDevID
);
480 return MMSYSERR_BADDEVICEID
;
482 if (MidiInDev
[wDevID
].midiDesc
.hMidi
== 0) {
483 WARN("device not opened !\n");
484 return MMSYSERR_ERROR
;
486 if (MidiInDev
[wDevID
].lpQueueHdr
!= 0) {
487 return MIDIERR_STILLPLAYING
;
490 if (midiSeq
== NULL
) {
492 return MMSYSERR_ERROR
;
494 if (--numStartedMidiIn
== 0) {
495 TRACE("Stopping timer for midi-in\n");
496 if (!KillTimer(0, midiInTimerID
)) {
497 WARN("Couldn't stop timer for midi-in\n");
502 snd_seq_disconnect_from(midiSeq
, port_in
, MidiInDev
[wDevID
].addr
.client
, MidiInDev
[wDevID
].addr
.port
);
505 MidiInDev
[wDevID
].bufsize
= 0;
506 if (MIDI_NotifyClient(wDevID
, MIM_CLOSE
, 0L, 0L) != MMSYSERR_NOERROR
) {
507 WARN("can't notify client !\n");
508 ret
= MMSYSERR_INVALPARAM
;
510 MidiInDev
[wDevID
].midiDesc
.hMidi
= 0;
516 /**************************************************************************
517 * midAddBuffer [internal]
519 static DWORD
midAddBuffer(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
521 TRACE("(%04X, %p, %08lX);\n", wDevID
, lpMidiHdr
, dwSize
);
523 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
524 if (MidiInDev
[wDevID
].state
== -1) return MIDIERR_NODEVICE
;
526 if (lpMidiHdr
== NULL
) return MMSYSERR_INVALPARAM
;
527 if (sizeof(MIDIHDR
) > dwSize
) return MMSYSERR_INVALPARAM
;
528 if (lpMidiHdr
->dwBufferLength
== 0) return MMSYSERR_INVALPARAM
;
529 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
) return MIDIERR_STILLPLAYING
;
530 if (!(lpMidiHdr
->dwFlags
& MHDR_PREPARED
)) return MIDIERR_UNPREPARED
;
532 if (MidiInDev
[wDevID
].lpQueueHdr
== 0) {
533 MidiInDev
[wDevID
].lpQueueHdr
= lpMidiHdr
;
537 for (ptr
= MidiInDev
[wDevID
].lpQueueHdr
;
539 ptr
= (LPMIDIHDR
)ptr
->lpNext
);
540 ptr
->lpNext
= (struct midihdr_tag
*)lpMidiHdr
;
542 return MMSYSERR_NOERROR
;
545 /**************************************************************************
546 * midPrepare [internal]
548 static DWORD
midPrepare(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
550 TRACE("(%04X, %p, %08lX);\n", wDevID
, lpMidiHdr
, dwSize
);
552 if (dwSize
< sizeof(MIDIHDR
) || lpMidiHdr
== 0 ||
553 lpMidiHdr
->lpData
== 0 || (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
) != 0 ||
554 lpMidiHdr
->dwBufferLength
>= 0x10000ul
)
555 return MMSYSERR_INVALPARAM
;
557 lpMidiHdr
->lpNext
= 0;
558 lpMidiHdr
->dwFlags
|= MHDR_PREPARED
;
559 lpMidiHdr
->dwBytesRecorded
= 0;
561 return MMSYSERR_NOERROR
;
564 /**************************************************************************
565 * midUnprepare [internal]
567 static DWORD
midUnprepare(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
569 TRACE("(%04X, %p, %08lX);\n", wDevID
, lpMidiHdr
, dwSize
);
571 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
572 if (MidiInDev
[wDevID
].state
== -1) return MIDIERR_NODEVICE
;
574 if (dwSize
< sizeof(MIDIHDR
) || lpMidiHdr
== 0 ||
575 lpMidiHdr
->lpData
== 0 || lpMidiHdr
->dwBufferLength
>= 0x10000ul
)
576 return MMSYSERR_INVALPARAM
;
578 if (!(lpMidiHdr
->dwFlags
& MHDR_PREPARED
)) return MIDIERR_UNPREPARED
;
579 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
) return MIDIERR_STILLPLAYING
;
581 lpMidiHdr
->dwFlags
&= ~MHDR_PREPARED
;
583 return MMSYSERR_NOERROR
;
586 /**************************************************************************
587 * midReset [internal]
589 static DWORD
midReset(WORD wDevID
)
591 DWORD dwTime
= GetTickCount();
593 TRACE("(%04X);\n", wDevID
);
595 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
596 if (MidiInDev
[wDevID
].state
== -1) return MIDIERR_NODEVICE
;
598 while (MidiInDev
[wDevID
].lpQueueHdr
) {
599 MidiInDev
[wDevID
].lpQueueHdr
->dwFlags
&= ~MHDR_INQUEUE
;
600 MidiInDev
[wDevID
].lpQueueHdr
->dwFlags
|= MHDR_DONE
;
601 /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
602 if (MIDI_NotifyClient(wDevID
, MIM_LONGDATA
,
603 (DWORD
)MidiInDev
[wDevID
].lpQueueHdr
, dwTime
) != MMSYSERR_NOERROR
) {
604 WARN("Couldn't notify client\n");
606 MidiInDev
[wDevID
].lpQueueHdr
= (LPMIDIHDR
)MidiInDev
[wDevID
].lpQueueHdr
->lpNext
;
609 return MMSYSERR_NOERROR
;
612 /**************************************************************************
613 * midStart [internal]
615 static DWORD
midStart(WORD wDevID
)
617 TRACE("(%04X);\n", wDevID
);
619 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
620 if (MidiInDev
[wDevID
].state
== -1) return MIDIERR_NODEVICE
;
622 MidiInDev
[wDevID
].state
= 1;
623 MidiInDev
[wDevID
].startTime
= GetTickCount();
624 return MMSYSERR_NOERROR
;
627 /**************************************************************************
630 static DWORD
midStop(WORD wDevID
)
632 TRACE("(%04X);\n", wDevID
);
634 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
635 if (MidiInDev
[wDevID
].state
== -1) return MIDIERR_NODEVICE
;
637 MidiInDev
[wDevID
].state
= 0;
638 return MMSYSERR_NOERROR
;
641 /**************************************************************************
642 * modGetDevCaps [internal]
644 static DWORD
modGetDevCaps(WORD wDevID
, LPMIDIOUTCAPSA lpCaps
, DWORD dwSize
)
646 TRACE("(%04X, %p, %08lX);\n", wDevID
, lpCaps
, dwSize
);
648 if (wDevID
>= MODM_NumDevs
) return MMSYSERR_BADDEVICEID
;
649 if (lpCaps
== NULL
) return MMSYSERR_INVALPARAM
;
651 memcpy(lpCaps
, &MidiOutDev
[wDevID
].caps
, min(dwSize
, sizeof(*lpCaps
)));
653 return MMSYSERR_NOERROR
;
656 /**************************************************************************
659 static DWORD
modOpen(WORD wDevID
, LPMIDIOPENDESC lpDesc
, DWORD dwFlags
)
661 TRACE("(%04X, %p, %08lX);\n", wDevID
, lpDesc
, dwFlags
);
662 if (lpDesc
== NULL
) {
663 WARN("Invalid Parameter !\n");
664 return MMSYSERR_INVALPARAM
;
666 if (wDevID
>= MODM_NumDevs
) {
667 TRACE("MAX_MIDIOUTDRV reached !\n");
668 return MMSYSERR_BADDEVICEID
;
670 if (MidiOutDev
[wDevID
].midiDesc
.hMidi
!= 0) {
671 WARN("device already open !\n");
672 return MMSYSERR_ALLOCATED
;
674 if (!MidiOutDev
[wDevID
].bEnabled
) {
675 WARN("device disabled !\n");
676 return MIDIERR_NODEVICE
;
678 if ((dwFlags
& ~CALLBACK_TYPEMASK
) != 0) {
679 WARN("bad dwFlags\n");
680 return MMSYSERR_INVALFLAG
;
682 if (!MidiOutDev
[wDevID
].bEnabled
) {
683 TRACE("disabled wDevID\n");
684 return MMSYSERR_NOTENABLED
;
687 MidiOutDev
[wDevID
].lpExtra
= 0;
689 switch (MidiOutDev
[wDevID
].caps
.wTechnology
) {
693 if (midiOpenSeq(1) < 0) {
694 return MMSYSERR_ALLOCATED
;
698 WARN("Technology not supported (yet) %d !\n",
699 MidiOutDev
[wDevID
].caps
.wTechnology
);
700 return MMSYSERR_NOTENABLED
;
703 MidiOutDev
[wDevID
].wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
705 MidiOutDev
[wDevID
].lpQueueHdr
= NULL
;
706 MidiOutDev
[wDevID
].dwTotalPlayed
= 0;
707 MidiOutDev
[wDevID
].bufsize
= 0x3FFF;
708 MidiOutDev
[wDevID
].midiDesc
= *lpDesc
;
710 /* Connect our app port to the device port */
711 if (snd_seq_connect_to(midiSeq
, port_out
, MidiOutDev
[wDevID
].addr
.client
, MidiOutDev
[wDevID
].addr
.port
) < 0)
712 return MMSYSERR_NOTENABLED
;
714 if (MIDI_NotifyClient(wDevID
, MOM_OPEN
, 0L, 0L) != MMSYSERR_NOERROR
) {
715 WARN("can't notify client !\n");
716 return MMSYSERR_INVALPARAM
;
718 TRACE("Successful !\n");
719 return MMSYSERR_NOERROR
;
723 /**************************************************************************
724 * modClose [internal]
726 static DWORD
modClose(WORD wDevID
)
728 int ret
= MMSYSERR_NOERROR
;
730 TRACE("(%04X);\n", wDevID
);
732 if (MidiOutDev
[wDevID
].midiDesc
.hMidi
== 0) {
733 WARN("device not opened !\n");
734 return MMSYSERR_ERROR
;
736 /* FIXME: should test that no pending buffer is still in the queue for
739 if (midiSeq
== NULL
) {
740 WARN("can't close !\n");
741 return MMSYSERR_ERROR
;
744 switch (MidiOutDev
[wDevID
].caps
.wTechnology
) {
748 snd_seq_disconnect_to(midiSeq
, port_out
, MidiOutDev
[wDevID
].addr
.client
, MidiOutDev
[wDevID
].addr
.port
);
752 WARN("Technology not supported (yet) %d !\n",
753 MidiOutDev
[wDevID
].caps
.wTechnology
);
754 return MMSYSERR_NOTENABLED
;
757 if (MidiOutDev
[wDevID
].lpExtra
!= 0) {
758 HeapFree(GetProcessHeap(), 0, MidiOutDev
[wDevID
].lpExtra
);
759 MidiOutDev
[wDevID
].lpExtra
= 0;
762 MidiOutDev
[wDevID
].bufsize
= 0;
763 if (MIDI_NotifyClient(wDevID
, MOM_CLOSE
, 0L, 0L) != MMSYSERR_NOERROR
) {
764 WARN("can't notify client !\n");
765 ret
= MMSYSERR_INVALPARAM
;
767 MidiOutDev
[wDevID
].midiDesc
.hMidi
= 0;
771 /**************************************************************************
774 static DWORD
modData(WORD wDevID
, DWORD dwParam
)
776 BYTE evt
= LOBYTE(LOWORD(dwParam
));
777 BYTE d1
= HIBYTE(LOWORD(dwParam
));
778 BYTE d2
= LOBYTE(HIWORD(dwParam
));
780 TRACE("(%04X, %08lX);\n", wDevID
, dwParam
);
782 if (wDevID
>= MODM_NumDevs
) return MMSYSERR_BADDEVICEID
;
783 if (!MidiOutDev
[wDevID
].bEnabled
) return MIDIERR_NODEVICE
;
785 if (midiSeq
== NULL
) {
786 WARN("can't play !\n");
787 return MIDIERR_NODEVICE
;
789 switch (MidiOutDev
[wDevID
].caps
.wTechnology
) {
793 int handled
= 1; /* Assume event is handled */
794 snd_seq_event_t event
;
795 snd_seq_ev_clear(&event
);
796 snd_seq_ev_set_direct(&event
);
797 snd_seq_ev_set_source(&event
, port_out
);
798 snd_seq_ev_set_dest(&event
, MidiOutDev
[wDevID
].addr
.client
, MidiOutDev
[wDevID
].addr
.port
);
800 switch (evt
& 0xF0) {
801 case MIDI_CMD_NOTE_OFF
:
802 snd_seq_ev_set_noteoff(&event
, evt
&0x0F, d1
, d2
);
804 case MIDI_CMD_NOTE_ON
:
805 snd_seq_ev_set_noteon(&event
, evt
&0x0F, d1
, d2
);
807 case MIDI_CMD_NOTE_PRESSURE
:
808 snd_seq_ev_set_keypress(&event
, evt
&0x0F, d1
, d2
);
810 case MIDI_CMD_CONTROL
:
811 snd_seq_ev_set_controller(&event
, evt
&0x0F, d1
, d2
);
813 case MIDI_CMD_BENDER
:
814 snd_seq_ev_set_pitchbend(&event
, evt
&0x0F, ((WORD
)d1
<< 7) | (WORD
)d2
);
816 case MIDI_CMD_PGM_CHANGE
:
817 snd_seq_ev_set_pgmchange(&event
, evt
&0x0F, d1
);
819 case MIDI_CMD_CHANNEL_PRESSURE
:
820 snd_seq_ev_set_chanpress(&event
, evt
&0x0F, d1
);
822 case MIDI_CMD_COMMON_SYSEX
:
823 switch (evt
& 0x0F) {
824 case 0x00: /* System Exclusive, don't do it on modData,
825 * should require modLongData*/
826 case 0x01: /* Undefined */
827 case 0x04: /* Undefined. */
828 case 0x05: /* Undefined. */
829 case 0x07: /* End of Exclusive. */
830 case 0x09: /* Undefined. */
831 case 0x0D: /* Undefined. */
834 case 0x06: /* Tune Request */
835 case 0x08: /* Timing Clock. */
836 case 0x0A: /* Start. */
837 case 0x0B: /* Continue */
838 case 0x0C: /* Stop */
839 case 0x0E: /* Active Sensing. */
840 /* FIXME: Is this function suitable for these purposes
841 (and also Song Select and Song Position Pointer) */
842 snd_seq_ev_set_sysex(&event
, 1, &evt
);
844 case 0x0F: /* Reset */
845 /* snd_seq_ev_set_sysex(&event, 1, &evt);
846 this other way may be better */
848 BYTE reset_sysex_seq
[] = {MIDI_CMD_COMMON_SYSEX
, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
849 snd_seq_ev_set_sysex(&event
, sizeof(reset_sysex_seq
), reset_sysex_seq
);
852 case 0x03: /* Song Select. */
857 snd_seq_ev_set_sysex(&event
, sizeof(buf
), buf
);
860 case 0x02: /* Song Position Pointer. */
866 snd_seq_ev_set_sysex(&event
, sizeof(buf
), buf
);
873 snd_seq_event_output_direct(midiSeq
, &event
);
877 WARN("Technology not supported (yet) %d !\n",
878 MidiOutDev
[wDevID
].caps
.wTechnology
);
879 return MMSYSERR_NOTENABLED
;
882 return MMSYSERR_NOERROR
;
885 /**************************************************************************
886 * modLongData [internal]
888 static DWORD
modLongData(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
891 LPBYTE lpData
, lpNewData
= NULL
;
892 snd_seq_event_t event
;
894 TRACE("(%04X, %p, %08lX);\n", wDevID
, lpMidiHdr
, dwSize
);
896 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
897 * but it seems to be used only for midi input.
898 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
901 if (wDevID
>= MODM_NumDevs
) return MMSYSERR_BADDEVICEID
;
902 if (!MidiOutDev
[wDevID
].bEnabled
) return MIDIERR_NODEVICE
;
904 if (midiSeq
== NULL
) {
905 WARN("can't play !\n");
906 return MIDIERR_NODEVICE
;
909 lpData
= lpMidiHdr
->lpData
;
912 return MIDIERR_UNPREPARED
;
913 if (!(lpMidiHdr
->dwFlags
& MHDR_PREPARED
))
914 return MIDIERR_UNPREPARED
;
915 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
)
916 return MIDIERR_STILLPLAYING
;
917 lpMidiHdr
->dwFlags
&= ~MHDR_DONE
;
918 lpMidiHdr
->dwFlags
|= MHDR_INQUEUE
;
920 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
921 * data, or can it also contain raw MIDI data, to be split up and sent to
923 * If the latest is true, then the following WARNing will fire up
925 if (lpData
[0] != 0xF0 || lpData
[lpMidiHdr
->dwBufferLength
- 1] != 0xF7) {
926 WARN("Alledged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
927 lpNewData
= HeapAlloc(GetProcessHeap
, 0, lpMidiHdr
->dwBufferLength
+ 2);
930 TRACE("dwBufferLength=%lu !\n", lpMidiHdr
->dwBufferLength
);
931 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
932 lpData
[0], lpData
[1], lpData
[2], lpData
[lpMidiHdr
->dwBufferLength
-3],
933 lpData
[lpMidiHdr
->dwBufferLength
-2], lpData
[lpMidiHdr
->dwBufferLength
-1]);
935 switch (MidiOutDev
[wDevID
].caps
.wTechnology
) {
937 /* FIXME: I don't think there is much to do here */
940 if (lpData
[0] != 0xF0) {
941 /* Send start of System Exclusive */
944 memcpy(lpNewData
, lpData
, lpMidiHdr
->dwBufferLength
);
945 WARN("Adding missing 0xF0 marker at the beginning of "
946 "system exclusive byte stream\n");
948 if (lpData
[lpMidiHdr
->dwBufferLength
-1] != 0xF7) {
949 /* Send end of System Exclusive */
950 memcpy(lpData
+ len_add
, lpData
, lpMidiHdr
->dwBufferLength
);
951 lpNewData
[lpMidiHdr
->dwBufferLength
+ len_add
- 1] = 0xF0;
953 WARN("Adding missing 0xF7 marker at the end of "
954 "system exclusive byte stream\n");
956 snd_seq_ev_clear(&event
);
957 snd_seq_ev_set_direct(&event
);
958 snd_seq_ev_set_source(&event
, port_out
);
959 snd_seq_ev_set_dest(&event
, MidiOutDev
[wDevID
].addr
.client
, MidiOutDev
[wDevID
].addr
.port
);
960 TRACE("client = %d port = %d\n", MidiOutDev
[wDevID
].addr
.client
, MidiOutDev
[wDevID
].addr
.port
);
961 snd_seq_ev_set_sysex(&event
, lpMidiHdr
->dwBufferLength
+ len_add
, lpNewData
? lpNewData
: lpData
);
962 snd_seq_event_output_direct(midiSeq
, &event
);
964 HeapFree(GetProcessHeap(), 0, lpData
);
967 WARN("Technology not supported (yet) %d !\n",
968 MidiOutDev
[wDevID
].caps
.wTechnology
);
969 return MMSYSERR_NOTENABLED
;
972 lpMidiHdr
->dwFlags
&= ~MHDR_INQUEUE
;
973 lpMidiHdr
->dwFlags
|= MHDR_DONE
;
974 if (MIDI_NotifyClient(wDevID
, MOM_DONE
, (DWORD
)lpMidiHdr
, 0L) != MMSYSERR_NOERROR
) {
975 WARN("can't notify client !\n");
976 return MMSYSERR_INVALPARAM
;
978 return MMSYSERR_NOERROR
;
981 /**************************************************************************
982 * modPrepare [internal]
984 static DWORD
modPrepare(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
986 TRACE("(%04X, %p, %08lX);\n", wDevID
, lpMidiHdr
, dwSize
);
988 if (midiSeq
== NULL
) {
989 WARN("can't prepare !\n");
990 return MMSYSERR_NOTENABLED
;
993 /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
994 * asks to prepare MIDIHDR which dwFlags != 0.
995 * So at least check for the inqueue flag
997 if (dwSize
< sizeof(MIDIHDR
) || lpMidiHdr
== 0 ||
998 lpMidiHdr
->lpData
== 0 || (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
) != 0 ||
999 lpMidiHdr
->dwBufferLength
>= 0x10000ul
) {
1000 WARN("%p %p %08lx %d/%ld\n", lpMidiHdr
, lpMidiHdr
->lpData
,
1001 lpMidiHdr
->dwFlags
, sizeof(MIDIHDR
), dwSize
);
1002 return MMSYSERR_INVALPARAM
;
1005 lpMidiHdr
->lpNext
= 0;
1006 lpMidiHdr
->dwFlags
|= MHDR_PREPARED
;
1007 lpMidiHdr
->dwFlags
&= ~MHDR_DONE
;
1008 return MMSYSERR_NOERROR
;
1011 /**************************************************************************
1012 * modUnprepare [internal]
1014 static DWORD
modUnprepare(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
1016 TRACE("(%04X, %p, %08lX);\n", wDevID
, lpMidiHdr
, dwSize
);
1018 if (midiSeq
== NULL
) {
1019 WARN("can't unprepare !\n");
1020 return MMSYSERR_NOTENABLED
;
1023 if (dwSize
< sizeof(MIDIHDR
) || lpMidiHdr
== 0)
1024 return MMSYSERR_INVALPARAM
;
1025 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
)
1026 return MIDIERR_STILLPLAYING
;
1027 lpMidiHdr
->dwFlags
&= ~MHDR_PREPARED
;
1028 return MMSYSERR_NOERROR
;
1031 /**************************************************************************
1032 * modReset [internal]
1034 static DWORD
modReset(WORD wDevID
)
1038 TRACE("(%04X);\n", wDevID
);
1040 if (wDevID
>= MODM_NumDevs
) return MMSYSERR_BADDEVICEID
;
1041 if (!MidiOutDev
[wDevID
].bEnabled
) return MIDIERR_NODEVICE
;
1043 /* stop all notes */
1044 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1045 * it's channel dependent...
1047 for (chn
= 0; chn
< 16; chn
++) {
1048 /* turn off every note */
1049 modData(wDevID
, 0x7800 | MIDI_CMD_CONTROL
| chn
);
1050 /* remove sustain on all channels */
1051 modData(wDevID
, (MIDI_CTL_SUSTAIN
<< 8) | MIDI_CMD_CONTROL
| chn
);
1053 /* FIXME: the LongData buffers must also be returned to the app */
1054 return MMSYSERR_NOERROR
;
1058 /**************************************************************************
1059 * ALSA_AddMidiPort [internal]
1061 * Helper for ALSA_MidiInit
1063 void ALSA_AddMidiPort(snd_seq_client_info_t
* cinfo
, snd_seq_port_info_t
* pinfo
, int cap
, int type
)
1065 if (cap
& SND_SEQ_PORT_CAP_WRITE
) {
1066 TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo
),
1067 snd_seq_client_info_get_name(cinfo
),
1068 snd_seq_client_info_get_type(cinfo
) == SND_SEQ_USER_CLIENT
? "user" : "kernel",
1069 snd_seq_port_info_get_port(pinfo
),
1070 snd_seq_port_info_get_name(pinfo
),
1073 if (MODM_NumDevs
>= MAX_MIDIOUTDRV
)
1078 memcpy(&MidiOutDev
[MODM_NumDevs
].addr
, snd_seq_port_info_get_addr(pinfo
), sizeof(snd_seq_addr_t
));
1080 /* Manufac ID. We do not have access to this with soundcard.h
1081 * Does not seem to be a problem, because in mmsystem.h only
1082 * Microsoft's ID is listed.
1084 MidiOutDev
[MODM_NumDevs
].caps
.wMid
= 0x00FF;
1085 MidiOutDev
[MODM_NumDevs
].caps
.wPid
= 0x0001; /* FIXME Product ID */
1086 /* Product Version. We simply say "1" */
1087 MidiOutDev
[MODM_NumDevs
].caps
.vDriverVersion
= 0x001;
1088 MidiOutDev
[MODM_NumDevs
].caps
.wChannelMask
= 0xFFFF;
1090 /* FIXME Do we have this information?
1091 * Assuming the soundcards can handle
1092 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1093 * not MIDICAPS_CACHE.
1095 MidiOutDev
[MODM_NumDevs
].caps
.dwSupport
= MIDICAPS_VOLUME
|MIDICAPS_LRVOLUME
;
1096 strcpy(MidiOutDev
[MODM_NumDevs
].caps
.szPname
, snd_seq_client_info_get_name(cinfo
));
1098 MidiOutDev
[MODM_NumDevs
].caps
.wTechnology
= MIDI_AlsaToWindowsDeviceType(type
);
1099 MidiOutDev
[MODM_NumDevs
].caps
.wVoices
= 16;
1101 /* FIXME Is it possible to know the maximum
1102 * number of simultaneous notes of a soundcard ?
1103 * I believe we don't have this information, but
1104 * it's probably equal or more than wVoices
1106 MidiOutDev
[MODM_NumDevs
].caps
.wNotes
= 16;
1107 MidiOutDev
[MODM_NumDevs
].bEnabled
= TRUE
;
1109 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%ld\n"
1110 "\tALSA info: midi dev-type=%lx, capa=%lx\n",
1111 MODM_NumDevs
, MidiOutDev
[MODM_NumDevs
].caps
.szPname
, MidiOutDev
[MODM_NumDevs
].caps
.wTechnology
,
1112 MidiOutDev
[MODM_NumDevs
].caps
.wVoices
, MidiOutDev
[MODM_NumDevs
].caps
.wNotes
,
1113 MidiOutDev
[MODM_NumDevs
].caps
.wChannelMask
, MidiOutDev
[MODM_NumDevs
].caps
.dwSupport
,
1114 (long)type
, (long)0);
1118 if (cap
& SND_SEQ_PORT_CAP_READ
) {
1119 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo
),
1120 snd_seq_client_info_get_name(cinfo
),
1121 snd_seq_client_info_get_type(cinfo
) == SND_SEQ_USER_CLIENT
? "user" : "kernel",
1122 snd_seq_port_info_get_port(pinfo
),
1123 snd_seq_port_info_get_name(pinfo
),
1126 if (MIDM_NumDevs
>= MAX_MIDIINDRV
)
1131 memcpy(&MidiInDev
[MIDM_NumDevs
].addr
, snd_seq_port_info_get_addr(pinfo
), sizeof(snd_seq_addr_t
));
1133 /* Manufac ID. We do not have access to this with soundcard.h
1134 * Does not seem to be a problem, because in mmsystem.h only
1135 * Microsoft's ID is listed.
1137 MidiInDev
[MIDM_NumDevs
].caps
.wMid
= 0x00FF;
1138 MidiInDev
[MIDM_NumDevs
].caps
.wPid
= 0x0001; /* FIXME Product ID */
1139 /* Product Version. We simply say "1" */
1140 MidiInDev
[MIDM_NumDevs
].caps
.vDriverVersion
= 0x001;
1142 /* FIXME Do we have this information?
1143 * Assuming the soundcards can handle
1144 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1145 * not MIDICAPS_CACHE.
1147 MidiInDev
[MIDM_NumDevs
].caps
.dwSupport
= MIDICAPS_VOLUME
|MIDICAPS_LRVOLUME
;
1148 strcpy(MidiInDev
[MIDM_NumDevs
].caps
.szPname
, snd_seq_client_info_get_name(cinfo
));
1150 MidiInDev
[MIDM_NumDevs
].state
= 0;
1152 TRACE("MidiIn [%d]\tname='%s' support=%ld\n"
1153 "\tALSA info: midi dev-type=%lx, capa=%lx\n",
1154 MIDM_NumDevs
, MidiInDev
[MIDM_NumDevs
].caps
.szPname
, MidiInDev
[MIDM_NumDevs
].caps
.dwSupport
,
1155 (long)type
, (long)0);
1161 #endif /* defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1) */
1164 /*======================================================================*
1165 * MIDI entry points *
1166 *======================================================================*/
1168 /**************************************************************************
1169 * ALSA_MidiInit [internal]
1171 * Initializes the MIDI devices information variables
1173 LONG
ALSA_MidiInit(void)
1175 #if defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1)
1176 static BOOL bInitDone
= FALSE
;
1177 snd_seq_client_info_t
*cinfo
;
1178 snd_seq_port_info_t
*pinfo
;
1183 TRACE("Initializing the MIDI variables.\n");
1186 /* try to open device */
1187 if (midiOpenSeq(0) == -1) {
1191 #if 0 /* Debug purpose */
1192 snd_lib_error_set_handler(error_handler
);
1195 snd_seq_client_info_alloca(&cinfo
);
1196 snd_seq_port_info_alloca(&pinfo
);
1198 /* First, search for all internal midi devices */
1199 snd_seq_client_info_set_client(cinfo
, -1);
1200 while(snd_seq_query_next_client(midiSeq
, cinfo
) >= 0) {
1201 snd_seq_port_info_set_client(pinfo
, snd_seq_client_info_get_client(cinfo
));
1202 snd_seq_port_info_set_port(pinfo
, -1);
1203 while (snd_seq_query_next_port(midiSeq
, pinfo
) >= 0) {
1204 int cap
= snd_seq_port_info_get_capability(pinfo
);
1205 int type
= snd_seq_port_info_get_type(pinfo
);
1206 if (type
!= SND_SEQ_PORT_TYPE_MIDI_GENERIC
)
1207 ALSA_AddMidiPort(cinfo
, pinfo
, cap
, type
);
1211 /* Second, search for all external ports */
1212 snd_seq_client_info_set_client(cinfo
, -1);
1213 while(snd_seq_query_next_client(midiSeq
, cinfo
) >= 0) {
1214 snd_seq_port_info_set_client(pinfo
, snd_seq_client_info_get_client(cinfo
));
1215 snd_seq_port_info_set_port(pinfo
, -1);
1216 while (snd_seq_query_next_port(midiSeq
, pinfo
) >= 0) {
1217 int cap
= snd_seq_port_info_get_capability(pinfo
);
1218 int type
= snd_seq_port_info_get_type(pinfo
);
1219 if (type
== SND_SEQ_PORT_TYPE_MIDI_GENERIC
)
1220 ALSA_AddMidiPort(cinfo
, pinfo
, cap
, type
);
1224 /* close file and exit */
1232 /**************************************************************************
1233 * midMessage (WINEOSS.4)
1235 DWORD WINAPI
ALSA_midMessage(UINT wDevID
, UINT wMsg
, DWORD dwUser
,
1236 DWORD dwParam1
, DWORD dwParam2
)
1238 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1239 wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1241 #if defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1)
1245 /* FIXME: Pretend this is supported */
1248 return midOpen(wDevID
, (LPMIDIOPENDESC
)dwParam1
, dwParam2
);
1250 return midClose(wDevID
);
1251 case MIDM_ADDBUFFER
:
1252 return midAddBuffer(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
1254 return midPrepare(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
1255 case MIDM_UNPREPARE
:
1256 return midUnprepare(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
1257 case MIDM_GETDEVCAPS
:
1258 return midGetDevCaps(wDevID
, (LPMIDIINCAPSA
)dwParam1
,dwParam2
);
1259 case MIDM_GETNUMDEVS
:
1260 return MIDM_NumDevs
;
1262 return midReset(wDevID
);
1264 return midStart(wDevID
);
1266 return midStop(wDevID
);
1269 TRACE("Unsupported message\n");
1271 return MMSYSERR_NOTSUPPORTED
;
1274 /**************************************************************************
1275 * modMessage (WINEOSS.5)
1277 DWORD WINAPI
ALSA_modMessage(UINT wDevID
, UINT wMsg
, DWORD dwUser
,
1278 DWORD dwParam1
, DWORD dwParam2
)
1280 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1281 wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1284 #if defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1)
1289 /* FIXME: Pretend this is supported */
1292 return modOpen(wDevID
, (LPMIDIOPENDESC
)dwParam1
, dwParam2
);
1294 return modClose(wDevID
);
1296 return modData(wDevID
, dwParam1
);
1298 return modLongData(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
1300 return modPrepare(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
1301 case MODM_UNPREPARE
:
1302 return modUnprepare(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
1303 case MODM_GETDEVCAPS
:
1304 return modGetDevCaps(wDevID
, (LPMIDIOUTCAPSA
)dwParam1
, dwParam2
);
1305 case MODM_GETNUMDEVS
:
1306 return MODM_NumDevs
;
1307 case MODM_GETVOLUME
:
1309 case MODM_SETVOLUME
:
1312 return modReset(wDevID
);
1315 TRACE("Unsupported message\n");
1317 return MMSYSERR_NOTSUPPORTED
;
1320 /*-----------------------------------------------------------------------*/