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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 * TODO: Finish midi record
52 #include "wine/debug.h"
54 WINE_DEFAULT_DEBUG_CHANNEL(midi
);
59 int state
; /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
61 MIDIOPENDESC midiDesc
;
65 unsigned char incoming
[3];
66 unsigned char incPrev
;
76 MIDIOPENDESC midiDesc
;
80 void* lpExtra
; /* according to port type (MIDI, FM...), extra data when needed */
85 static WINE_MIDIIN MidiInDev
[MAX_MIDIINDRV
];
86 static WINE_MIDIOUT MidiOutDev
[MAX_MIDIOUTDRV
];
88 /* this is the total number of MIDI out devices found (synth and port) */
89 static int MODM_NumDevs
= 0;
90 /* this is the total number of MIDI out devices found */
91 static int MIDM_NumDevs
= 0;
93 static snd_seq_t
* midiSeq
= NULL
;
94 static int numOpenMidiSeq
= 0;
95 static int numStartedMidiIn
= 0;
100 static CRITICAL_SECTION crit_sect
; /* protects all MidiIn buffer queues */
101 static CRITICAL_SECTION_DEBUG critsect_debug
=
104 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
105 0, 0, { (DWORD_PTR
)(__FILE__
": crit_sect") }
107 static CRITICAL_SECTION crit_sect
= { &critsect_debug
, -1, 0, 0, 0, 0 };
109 static int end_thread
;
110 static HANDLE hThread
;
112 /*======================================================================*
113 * Low level MIDI implementation *
114 *======================================================================*/
116 static int midiOpenSeq(int);
117 static int midiCloseSeq(void);
119 #if 0 /* Debug Purpose */
120 static void error_handler(const char* file
, int line
, const char* function
, int err
, const char* fmt
, ...)
126 fprintf(stderr
, "ALSA lib %s:%i:(%s) ", file
, line
, function
);
127 vfprintf(stderr
, fmt
, arg
);
129 fprintf(stderr
, ": %s", snd_strerror(err
));
135 /**************************************************************************
136 * MIDI_unixToWindowsDeviceType [internal]
138 * return the Windows equivalent to a Unix Device Type
141 static int MIDI_AlsaToWindowsDeviceType(int type
)
143 /* MOD_MIDIPORT output port
144 * MOD_SYNTH generic internal synth
145 * MOD_SQSYNTH square wave internal synth
146 * MOD_FMSYNTH FM internal synth
147 * MOD_MAPPER MIDI mapper
148 * MOD_WAVETABLE hardware watetable internal synth
149 * MOD_SWSYNTH software internal synth
152 /* FIXME Is this really the correct equivalence from ALSA to
153 Windows Sound type */
155 if (type
& SND_SEQ_PORT_TYPE_SYNTH
)
158 if (type
& (SND_SEQ_PORT_TYPE_DIRECT_SAMPLE
|SND_SEQ_PORT_TYPE_SAMPLE
))
161 if (type
& SND_SEQ_PORT_TYPE_MIDI_GENERIC
)
164 ERR("Cannot determine the type (alsa type is %x) of this midi device. Assuming FM Synth\n", type
);
168 /**************************************************************************
169 * MIDI_NotifyClient [internal]
171 static DWORD
MIDI_NotifyClient(UINT wDevID
, WORD wMsg
,
172 DWORD dwParam1
, DWORD dwParam2
)
179 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04X dwParam2 = %04X\n",
180 wDevID
, wMsg
, dwParam1
, dwParam2
);
187 if (wDevID
> MODM_NumDevs
)
188 return MMSYSERR_BADDEVICEID
;
190 dwCallBack
= MidiOutDev
[wDevID
].midiDesc
.dwCallback
;
191 uFlags
= MidiOutDev
[wDevID
].wFlags
;
192 hDev
= MidiOutDev
[wDevID
].midiDesc
.hMidi
;
193 dwInstance
= MidiOutDev
[wDevID
].midiDesc
.dwInstance
;
203 if (wDevID
> MIDM_NumDevs
)
204 return MMSYSERR_BADDEVICEID
;
206 dwCallBack
= MidiInDev
[wDevID
].midiDesc
.dwCallback
;
207 uFlags
= MidiInDev
[wDevID
].wFlags
;
208 hDev
= MidiInDev
[wDevID
].midiDesc
.hMidi
;
209 dwInstance
= MidiInDev
[wDevID
].midiDesc
.dwInstance
;
212 WARN("Unsupported MSW-MIDI message %u\n", wMsg
);
213 return MMSYSERR_ERROR
;
216 return DriverCallback(dwCallBack
, uFlags
, hDev
, wMsg
, dwInstance
, dwParam1
, dwParam2
) ?
220 static int midi_warn
= 1;
221 /**************************************************************************
222 * midiOpenSeq [internal]
224 static int midiOpenSeq(int create_client
)
226 if (numOpenMidiSeq
== 0) {
227 if (snd_seq_open(&midiSeq
, "default", SND_SEQ_OPEN_DUPLEX
, 0) < 0)
231 WARN("Error opening ALSA sequencer.\n");
238 /* Setting the client name is the only init to do */
239 snd_seq_set_client_name(midiSeq
, "WINE midi driver");
241 #if 0 /* FIXME: Is it possible to use a port for READ & WRITE ops */
242 port_in
= port_out
= snd_seq_create_simple_port(midiSeq
, "WINE ALSA Input/Output", SND_SEQ_PORT_CAP_READ
|SND_SEQ_PORT_CAP_WRITE
,
243 SND_SEQ_PORT_TYPE_APPLICATION
);
245 TRACE("Unable to create output port\n");
247 TRACE("Outport port created successfully (%d)\n", port_out
);
249 port_out
= snd_seq_create_simple_port(midiSeq
, "WINE ALSA Output", SND_SEQ_PORT_CAP_READ
,
250 SND_SEQ_PORT_TYPE_APPLICATION
);
252 TRACE("Unable to create output port\n");
254 TRACE("Outport port created successfully (%d)\n", port_out
);
256 port_in
= snd_seq_create_simple_port(midiSeq
, "WINE ALSA Input", SND_SEQ_PORT_CAP_WRITE
,
257 SND_SEQ_PORT_TYPE_APPLICATION
);
259 TRACE("Unable to create input port\n");
261 TRACE("Input port created successfully (%d)\n", port_in
);
269 /**************************************************************************
270 * midiCloseSeq [internal]
272 static int midiCloseSeq(void)
274 if (--numOpenMidiSeq
== 0) {
275 snd_seq_delete_simple_port(midiSeq
, port_out
);
276 snd_seq_delete_simple_port(midiSeq
, port_in
);
277 snd_seq_close(midiSeq
);
283 static DWORD WINAPI
midRecThread(LPVOID arg
)
288 TRACE("Thread startup\n");
291 TRACE("Thread loop\n");
292 npfd
= snd_seq_poll_descriptors_count(midiSeq
, POLLIN
);
293 pfd
= HeapAlloc(GetProcessHeap(), 0, npfd
* sizeof(struct pollfd
));
294 snd_seq_poll_descriptors(midiSeq
, pfd
, npfd
, POLLIN
);
296 /* Check if an event is present */
297 if (poll(pfd
, npfd
, 250) < 0) {
298 HeapFree(GetProcessHeap(), 0, pfd
);
302 /* Note: This definitely does not work.
303 * while(snd_seq_event_input_pending(midiSeq, 0) > 0) {
305 snd_seq_event_input(midiSeq, &ev);
307 snd_seq_free_event(ev);
313 snd_seq_event_input(midiSeq
, &ev
);
314 /* Find the target device */
315 for (wDevID
= 0; wDevID
< MIDM_NumDevs
; wDevID
++)
316 if ( (ev
->source
.client
== MidiInDev
[wDevID
].addr
.client
) && (ev
->source
.port
== MidiInDev
[wDevID
].addr
.port
) )
318 if ((wDevID
== MIDM_NumDevs
) || (MidiInDev
[wDevID
].state
!= 1))
319 FIXME("Unexpected event received, type = %x from %d:%d\n", ev
->type
, ev
->source
.client
, ev
->source
.port
);
321 DWORD dwTime
, toSend
= 0;
322 /* FIXME: Should use ev->time instead for better accuracy */
323 dwTime
= GetTickCount() - MidiInDev
[wDevID
].startTime
;
324 TRACE("Event received, type = %x, device = %d\n", ev
->type
, wDevID
);
327 case SND_SEQ_EVENT_NOTEOFF
:
328 toSend
= (ev
->data
.note
.velocity
<< 16) | (ev
->data
.note
.note
<< 8) | MIDI_CMD_NOTE_OFF
| ev
->data
.control
.channel
;
330 case SND_SEQ_EVENT_NOTEON
:
331 toSend
= (ev
->data
.note
.velocity
<< 16) | (ev
->data
.note
.note
<< 8) | MIDI_CMD_NOTE_ON
| ev
->data
.control
.channel
;
333 case SND_SEQ_EVENT_KEYPRESS
:
334 toSend
= (ev
->data
.note
.velocity
<< 16) | (ev
->data
.note
.note
<< 8) | MIDI_CMD_NOTE_PRESSURE
| ev
->data
.control
.channel
;
336 case SND_SEQ_EVENT_CONTROLLER
:
337 toSend
= (ev
->data
.control
.value
<< 16) | (ev
->data
.control
.param
<< 8) | MIDI_CMD_CONTROL
| ev
->data
.control
.channel
;
339 case SND_SEQ_EVENT_PITCHBEND
:
340 toSend
= (ev
->data
.control
.value
<< 16) | (ev
->data
.control
.param
<< 8) | MIDI_CMD_BENDER
| ev
->data
.control
.channel
;
342 case SND_SEQ_EVENT_PGMCHANGE
:
343 toSend
= (ev
->data
.control
.value
<< 16) | (ev
->data
.control
.param
<< 8) | MIDI_CMD_PGM_CHANGE
| ev
->data
.control
.channel
;
345 case SND_SEQ_EVENT_CHANPRESS
:
346 toSend
= (ev
->data
.control
.value
<< 16) | (ev
->data
.control
.param
<< 8) | MIDI_CMD_CHANNEL_PRESSURE
| ev
->data
.control
.channel
;
348 case SND_SEQ_EVENT_SYSEX
:
350 int len
= ev
->data
.ext
.len
;
351 LPBYTE ptr
= (BYTE
*) ev
->data
.ext
.ptr
;
354 /* FIXME: Should handle sysex greater that a single buffer */
355 EnterCriticalSection(&crit_sect
);
356 if ((lpMidiHdr
= MidiInDev
[wDevID
].lpQueueHdr
) != NULL
) {
357 if (len
<= lpMidiHdr
->dwBufferLength
) {
358 lpMidiHdr
->dwBytesRecorded
= len
;
359 memcpy(lpMidiHdr
->lpData
, ptr
, len
);
360 lpMidiHdr
->dwFlags
&= ~MHDR_INQUEUE
;
361 lpMidiHdr
->dwFlags
|= MHDR_DONE
;
362 MidiInDev
[wDevID
].lpQueueHdr
= (LPMIDIHDR
)lpMidiHdr
->lpNext
;
363 if (MIDI_NotifyClient(wDevID
, MIM_LONGDATA
, (DWORD
)lpMidiHdr
, dwTime
) != MMSYSERR_NOERROR
)
364 WARN("Couldn't notify client\n");
366 FIXME("No enough space in the buffer to store sysex!\n");
368 FIXME("Sysex received but no buffer to store it!\n");
369 LeaveCriticalSection(&crit_sect
);
372 case SND_SEQ_EVENT_SENSING
:
376 FIXME("Unhandled event received, type = %x\n", ev
->type
);
380 TRACE("Sending event %08x (from %d %d)\n", toSend
, ev
->source
.client
, ev
->source
.port
);
381 if (MIDI_NotifyClient(wDevID
, MIM_DATA
, toSend
, dwTime
) != MMSYSERR_NOERROR
) {
382 WARN("Couldn't notify client\n");
386 snd_seq_free_event(ev
);
387 } while(snd_seq_event_input_pending(midiSeq
, 0) > 0);
389 HeapFree(GetProcessHeap(), 0, pfd
);
394 /**************************************************************************
395 * midGetDevCaps [internal]
397 static DWORD
midGetDevCaps(WORD wDevID
, LPMIDIINCAPSW lpCaps
, DWORD dwSize
)
399 TRACE("(%04X, %p, %08X);\n", wDevID
, lpCaps
, dwSize
);
401 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
402 if (lpCaps
== NULL
) return MMSYSERR_INVALPARAM
;
404 memcpy(lpCaps
, &MidiInDev
[wDevID
].caps
, min(dwSize
, sizeof(*lpCaps
)));
406 return MMSYSERR_NOERROR
;
410 /**************************************************************************
413 static DWORD
midOpen(WORD wDevID
, LPMIDIOPENDESC lpDesc
, DWORD dwFlags
)
415 TRACE("(%04X, %p, %08X);\n", wDevID
, lpDesc
, dwFlags
);
417 if (lpDesc
== NULL
) {
418 WARN("Invalid Parameter !\n");
419 return MMSYSERR_INVALPARAM
;
423 * how to check that content of lpDesc is correct ?
425 if (wDevID
>= MIDM_NumDevs
) {
426 WARN("wDevID too large (%u) !\n", wDevID
);
427 return MMSYSERR_BADDEVICEID
;
429 if (MidiInDev
[wDevID
].state
== -1) {
430 WARN("device disabled\n");
431 return MIDIERR_NODEVICE
;
433 if (MidiInDev
[wDevID
].midiDesc
.hMidi
!= 0) {
434 WARN("device already open !\n");
435 return MMSYSERR_ALLOCATED
;
437 if ((dwFlags
& MIDI_IO_STATUS
) != 0) {
438 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
439 dwFlags
&= ~MIDI_IO_STATUS
;
441 if ((dwFlags
& ~CALLBACK_TYPEMASK
) != 0) {
442 FIXME("Bad dwFlags\n");
443 return MMSYSERR_INVALFLAG
;
446 if (midiOpenSeq(1) < 0) {
447 return MMSYSERR_ERROR
;
450 /* Connect our app port to the device port */
451 if (snd_seq_connect_from(midiSeq
, port_in
, MidiInDev
[wDevID
].addr
.client
, MidiInDev
[wDevID
].addr
.port
) < 0)
452 return MMSYSERR_NOTENABLED
;
454 TRACE("input port connected %d %d %d\n",port_in
,MidiInDev
[wDevID
].addr
.client
,MidiInDev
[wDevID
].addr
.port
);
456 if (numStartedMidiIn
++ == 0) {
458 hThread
= CreateThread(NULL
, 0, midRecThread
, NULL
, 0, NULL
);
460 numStartedMidiIn
= 0;
461 WARN("Couldn't create thread for midi-in\n");
463 return MMSYSERR_ERROR
;
465 SetThreadPriority(hThread
, THREAD_PRIORITY_TIME_CRITICAL
);
466 TRACE("Created thread for midi-in\n");
469 MidiInDev
[wDevID
].wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
471 MidiInDev
[wDevID
].lpQueueHdr
= NULL
;
472 MidiInDev
[wDevID
].dwTotalPlayed
= 0;
473 MidiInDev
[wDevID
].bufsize
= 0x3FFF;
474 MidiInDev
[wDevID
].midiDesc
= *lpDesc
;
475 MidiInDev
[wDevID
].state
= 0;
476 MidiInDev
[wDevID
].incLen
= 0;
477 MidiInDev
[wDevID
].startTime
= 0;
479 if (MIDI_NotifyClient(wDevID
, MIM_OPEN
, 0L, 0L) != MMSYSERR_NOERROR
) {
480 WARN("can't notify client !\n");
481 return MMSYSERR_INVALPARAM
;
483 return MMSYSERR_NOERROR
;
486 /**************************************************************************
487 * midClose [internal]
489 static DWORD
midClose(WORD wDevID
)
491 int ret
= MMSYSERR_NOERROR
;
493 TRACE("(%04X);\n", wDevID
);
495 if (wDevID
>= MIDM_NumDevs
) {
496 WARN("wDevID too big (%u) !\n", wDevID
);
497 return MMSYSERR_BADDEVICEID
;
499 if (MidiInDev
[wDevID
].midiDesc
.hMidi
== 0) {
500 WARN("device not opened !\n");
501 return MMSYSERR_ERROR
;
503 if (MidiInDev
[wDevID
].lpQueueHdr
!= 0) {
504 return MIDIERR_STILLPLAYING
;
507 if (midiSeq
== NULL
) {
509 return MMSYSERR_ERROR
;
511 if (--numStartedMidiIn
== 0) {
512 TRACE("Stopping thread for midi-in\n");
514 if (WaitForSingleObject(hThread
, 5000) != WAIT_OBJECT_0
) {
515 WARN("Thread end not signaled, force termination\n");
516 TerminateThread(hThread
, 0);
518 TRACE("Stopped thread for midi-in\n");
521 snd_seq_disconnect_from(midiSeq
, port_in
, MidiInDev
[wDevID
].addr
.client
, MidiInDev
[wDevID
].addr
.port
);
524 MidiInDev
[wDevID
].bufsize
= 0;
525 if (MIDI_NotifyClient(wDevID
, MIM_CLOSE
, 0L, 0L) != MMSYSERR_NOERROR
) {
526 WARN("can't notify client !\n");
527 ret
= MMSYSERR_INVALPARAM
;
529 MidiInDev
[wDevID
].midiDesc
.hMidi
= 0;
535 /**************************************************************************
536 * midAddBuffer [internal]
538 static DWORD
midAddBuffer(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
540 TRACE("(%04X, %p, %08X);\n", wDevID
, lpMidiHdr
, dwSize
);
542 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
543 if (MidiInDev
[wDevID
].state
== -1) return MIDIERR_NODEVICE
;
545 if (lpMidiHdr
== NULL
) return MMSYSERR_INVALPARAM
;
546 if (sizeof(MIDIHDR
) > dwSize
) return MMSYSERR_INVALPARAM
;
547 if (lpMidiHdr
->dwBufferLength
== 0) return MMSYSERR_INVALPARAM
;
548 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
) return MIDIERR_STILLPLAYING
;
549 if (!(lpMidiHdr
->dwFlags
& MHDR_PREPARED
)) return MIDIERR_UNPREPARED
;
551 EnterCriticalSection(&crit_sect
);
552 if (MidiInDev
[wDevID
].lpQueueHdr
== 0) {
553 MidiInDev
[wDevID
].lpQueueHdr
= lpMidiHdr
;
557 for (ptr
= MidiInDev
[wDevID
].lpQueueHdr
;
559 ptr
= (LPMIDIHDR
)ptr
->lpNext
);
560 ptr
->lpNext
= (struct midihdr_tag
*)lpMidiHdr
;
562 LeaveCriticalSection(&crit_sect
);
564 return MMSYSERR_NOERROR
;
567 /**************************************************************************
568 * midPrepare [internal]
570 static DWORD
midPrepare(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
572 TRACE("(%04X, %p, %08X);\n", wDevID
, lpMidiHdr
, dwSize
);
574 if (dwSize
< sizeof(MIDIHDR
) || lpMidiHdr
== 0 ||
575 lpMidiHdr
->lpData
== 0 || (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
) != 0 ||
576 lpMidiHdr
->dwBufferLength
>= 0x10000ul
)
577 return MMSYSERR_INVALPARAM
;
579 lpMidiHdr
->lpNext
= 0;
580 lpMidiHdr
->dwFlags
|= MHDR_PREPARED
;
581 lpMidiHdr
->dwBytesRecorded
= 0;
583 return MMSYSERR_NOERROR
;
586 /**************************************************************************
587 * midUnprepare [internal]
589 static DWORD
midUnprepare(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
591 TRACE("(%04X, %p, %08X);\n", wDevID
, lpMidiHdr
, dwSize
);
593 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
594 if (MidiInDev
[wDevID
].state
== -1) return MIDIERR_NODEVICE
;
596 if (dwSize
< sizeof(MIDIHDR
) || lpMidiHdr
== 0 ||
597 lpMidiHdr
->lpData
== 0 || lpMidiHdr
->dwBufferLength
>= 0x10000ul
)
598 return MMSYSERR_INVALPARAM
;
600 if (!(lpMidiHdr
->dwFlags
& MHDR_PREPARED
)) return MIDIERR_UNPREPARED
;
601 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
) return MIDIERR_STILLPLAYING
;
603 lpMidiHdr
->dwFlags
&= ~MHDR_PREPARED
;
605 return MMSYSERR_NOERROR
;
608 /**************************************************************************
609 * midReset [internal]
611 static DWORD
midReset(WORD wDevID
)
613 DWORD dwTime
= GetTickCount();
615 TRACE("(%04X);\n", wDevID
);
617 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
618 if (MidiInDev
[wDevID
].state
== -1) return MIDIERR_NODEVICE
;
620 EnterCriticalSection(&crit_sect
);
621 while (MidiInDev
[wDevID
].lpQueueHdr
) {
622 MidiInDev
[wDevID
].lpQueueHdr
->dwFlags
&= ~MHDR_INQUEUE
;
623 MidiInDev
[wDevID
].lpQueueHdr
->dwFlags
|= MHDR_DONE
;
624 /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
625 if (MIDI_NotifyClient(wDevID
, MIM_LONGDATA
,
626 (DWORD
)MidiInDev
[wDevID
].lpQueueHdr
, dwTime
) != MMSYSERR_NOERROR
) {
627 WARN("Couldn't notify client\n");
629 MidiInDev
[wDevID
].lpQueueHdr
= (LPMIDIHDR
)MidiInDev
[wDevID
].lpQueueHdr
->lpNext
;
631 LeaveCriticalSection(&crit_sect
);
633 return MMSYSERR_NOERROR
;
636 /**************************************************************************
637 * midStart [internal]
639 static DWORD
midStart(WORD wDevID
)
641 TRACE("(%04X);\n", wDevID
);
643 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
644 if (MidiInDev
[wDevID
].state
== -1) return MIDIERR_NODEVICE
;
646 MidiInDev
[wDevID
].state
= 1;
647 MidiInDev
[wDevID
].startTime
= GetTickCount();
648 return MMSYSERR_NOERROR
;
651 /**************************************************************************
654 static DWORD
midStop(WORD wDevID
)
656 TRACE("(%04X);\n", wDevID
);
658 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
659 if (MidiInDev
[wDevID
].state
== -1) return MIDIERR_NODEVICE
;
661 MidiInDev
[wDevID
].state
= 0;
662 return MMSYSERR_NOERROR
;
665 /**************************************************************************
666 * modGetDevCaps [internal]
668 static DWORD
modGetDevCaps(WORD wDevID
, LPMIDIOUTCAPSW lpCaps
, DWORD dwSize
)
670 TRACE("(%04X, %p, %08X);\n", wDevID
, lpCaps
, dwSize
);
672 if (wDevID
>= MODM_NumDevs
) return MMSYSERR_BADDEVICEID
;
673 if (lpCaps
== NULL
) return MMSYSERR_INVALPARAM
;
675 memcpy(lpCaps
, &MidiOutDev
[wDevID
].caps
, min(dwSize
, sizeof(*lpCaps
)));
677 return MMSYSERR_NOERROR
;
680 /**************************************************************************
683 static DWORD
modOpen(WORD wDevID
, LPMIDIOPENDESC lpDesc
, DWORD dwFlags
)
685 TRACE("(%04X, %p, %08X);\n", wDevID
, lpDesc
, dwFlags
);
686 if (lpDesc
== NULL
) {
687 WARN("Invalid Parameter !\n");
688 return MMSYSERR_INVALPARAM
;
690 if (wDevID
>= MODM_NumDevs
) {
691 TRACE("MAX_MIDIOUTDRV reached !\n");
692 return MMSYSERR_BADDEVICEID
;
694 if (MidiOutDev
[wDevID
].midiDesc
.hMidi
!= 0) {
695 WARN("device already open !\n");
696 return MMSYSERR_ALLOCATED
;
698 if (!MidiOutDev
[wDevID
].bEnabled
) {
699 WARN("device disabled !\n");
700 return MIDIERR_NODEVICE
;
702 if ((dwFlags
& ~CALLBACK_TYPEMASK
) != 0) {
703 WARN("bad dwFlags\n");
704 return MMSYSERR_INVALFLAG
;
706 if (!MidiOutDev
[wDevID
].bEnabled
) {
707 TRACE("disabled wDevID\n");
708 return MMSYSERR_NOTENABLED
;
711 MidiOutDev
[wDevID
].lpExtra
= 0;
713 switch (MidiOutDev
[wDevID
].caps
.wTechnology
) {
717 if (midiOpenSeq(1) < 0) {
718 return MMSYSERR_ALLOCATED
;
722 WARN("Technology not supported (yet) %d !\n",
723 MidiOutDev
[wDevID
].caps
.wTechnology
);
724 return MMSYSERR_NOTENABLED
;
727 MidiOutDev
[wDevID
].wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
729 MidiOutDev
[wDevID
].lpQueueHdr
= NULL
;
730 MidiOutDev
[wDevID
].dwTotalPlayed
= 0;
731 MidiOutDev
[wDevID
].bufsize
= 0x3FFF;
732 MidiOutDev
[wDevID
].midiDesc
= *lpDesc
;
734 /* Connect our app port to the device port */
735 if (snd_seq_connect_to(midiSeq
, port_out
, MidiOutDev
[wDevID
].addr
.client
, MidiOutDev
[wDevID
].addr
.port
) < 0)
736 return MMSYSERR_NOTENABLED
;
738 if (MIDI_NotifyClient(wDevID
, MOM_OPEN
, 0L, 0L) != MMSYSERR_NOERROR
) {
739 WARN("can't notify client !\n");
740 return MMSYSERR_INVALPARAM
;
742 TRACE("Successful !\n");
743 return MMSYSERR_NOERROR
;
747 /**************************************************************************
748 * modClose [internal]
750 static DWORD
modClose(WORD wDevID
)
752 int ret
= MMSYSERR_NOERROR
;
754 TRACE("(%04X);\n", wDevID
);
756 if (MidiOutDev
[wDevID
].midiDesc
.hMidi
== 0) {
757 WARN("device not opened !\n");
758 return MMSYSERR_ERROR
;
760 /* FIXME: should test that no pending buffer is still in the queue for
763 if (midiSeq
== NULL
) {
764 WARN("can't close !\n");
765 return MMSYSERR_ERROR
;
768 switch (MidiOutDev
[wDevID
].caps
.wTechnology
) {
772 snd_seq_disconnect_to(midiSeq
, port_out
, MidiOutDev
[wDevID
].addr
.client
, MidiOutDev
[wDevID
].addr
.port
);
776 WARN("Technology not supported (yet) %d !\n",
777 MidiOutDev
[wDevID
].caps
.wTechnology
);
778 return MMSYSERR_NOTENABLED
;
781 HeapFree(GetProcessHeap(), 0, MidiOutDev
[wDevID
].lpExtra
);
782 MidiOutDev
[wDevID
].lpExtra
= 0;
784 MidiOutDev
[wDevID
].bufsize
= 0;
785 if (MIDI_NotifyClient(wDevID
, MOM_CLOSE
, 0L, 0L) != MMSYSERR_NOERROR
) {
786 WARN("can't notify client !\n");
787 ret
= MMSYSERR_INVALPARAM
;
789 MidiOutDev
[wDevID
].midiDesc
.hMidi
= 0;
793 /**************************************************************************
796 static DWORD
modData(WORD wDevID
, DWORD dwParam
)
798 BYTE evt
= LOBYTE(LOWORD(dwParam
));
799 BYTE d1
= HIBYTE(LOWORD(dwParam
));
800 BYTE d2
= LOBYTE(HIWORD(dwParam
));
802 TRACE("(%04X, %08X);\n", wDevID
, dwParam
);
804 if (wDevID
>= MODM_NumDevs
) return MMSYSERR_BADDEVICEID
;
805 if (!MidiOutDev
[wDevID
].bEnabled
) return MIDIERR_NODEVICE
;
807 if (midiSeq
== NULL
) {
808 WARN("can't play !\n");
809 return MIDIERR_NODEVICE
;
811 switch (MidiOutDev
[wDevID
].caps
.wTechnology
) {
815 int handled
= 1; /* Assume event is handled */
816 snd_seq_event_t event
;
817 snd_seq_ev_clear(&event
);
818 snd_seq_ev_set_direct(&event
);
819 snd_seq_ev_set_source(&event
, port_out
);
820 snd_seq_ev_set_dest(&event
, MidiOutDev
[wDevID
].addr
.client
, MidiOutDev
[wDevID
].addr
.port
);
822 switch (evt
& 0xF0) {
823 case MIDI_CMD_NOTE_OFF
:
824 snd_seq_ev_set_noteoff(&event
, evt
&0x0F, d1
, d2
);
826 case MIDI_CMD_NOTE_ON
:
827 snd_seq_ev_set_noteon(&event
, evt
&0x0F, d1
, d2
);
829 case MIDI_CMD_NOTE_PRESSURE
:
830 snd_seq_ev_set_keypress(&event
, evt
&0x0F, d1
, d2
);
832 case MIDI_CMD_CONTROL
:
833 snd_seq_ev_set_controller(&event
, evt
&0x0F, d1
, d2
);
835 case MIDI_CMD_BENDER
:
836 snd_seq_ev_set_pitchbend(&event
, evt
&0x0F, ((WORD
)d2
<< 7 | (WORD
)d1
) - 0x2000);
838 case MIDI_CMD_PGM_CHANGE
:
839 snd_seq_ev_set_pgmchange(&event
, evt
&0x0F, d1
);
841 case MIDI_CMD_CHANNEL_PRESSURE
:
842 snd_seq_ev_set_chanpress(&event
, evt
&0x0F, d1
);
844 case MIDI_CMD_COMMON_SYSEX
:
845 switch (evt
& 0x0F) {
846 case 0x00: /* System Exclusive, don't do it on modData,
847 * should require modLongData*/
848 case 0x01: /* Undefined */
849 case 0x04: /* Undefined. */
850 case 0x05: /* Undefined. */
851 case 0x07: /* End of Exclusive. */
852 case 0x09: /* Undefined. */
853 case 0x0D: /* Undefined. */
856 case 0x06: /* Tune Request */
857 case 0x08: /* Timing Clock. */
858 case 0x0A: /* Start. */
859 case 0x0B: /* Continue */
860 case 0x0C: /* Stop */
861 case 0x0E: /* Active Sensing. */
862 /* FIXME: Is this function suitable for these purposes
863 (and also Song Select and Song Position Pointer) */
864 snd_seq_ev_set_sysex(&event
, 1, &evt
);
866 case 0x0F: /* Reset */
867 /* snd_seq_ev_set_sysex(&event, 1, &evt);
868 this other way may be better */
870 BYTE reset_sysex_seq
[] = {MIDI_CMD_COMMON_SYSEX
, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
871 snd_seq_ev_set_sysex(&event
, sizeof(reset_sysex_seq
), reset_sysex_seq
);
874 case 0x03: /* Song Select. */
879 snd_seq_ev_set_sysex(&event
, sizeof(buf
), buf
);
882 case 0x02: /* Song Position Pointer. */
888 snd_seq_ev_set_sysex(&event
, sizeof(buf
), buf
);
895 snd_seq_event_output_direct(midiSeq
, &event
);
899 WARN("Technology not supported (yet) %d !\n",
900 MidiOutDev
[wDevID
].caps
.wTechnology
);
901 return MMSYSERR_NOTENABLED
;
904 return MMSYSERR_NOERROR
;
907 /**************************************************************************
908 * modLongData [internal]
910 static DWORD
modLongData(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
913 LPBYTE lpData
, lpNewData
= NULL
;
914 snd_seq_event_t event
;
916 TRACE("(%04X, %p, %08X);\n", wDevID
, lpMidiHdr
, dwSize
);
918 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
919 * but it seems to be used only for midi input.
920 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
923 if (wDevID
>= MODM_NumDevs
) return MMSYSERR_BADDEVICEID
;
924 if (!MidiOutDev
[wDevID
].bEnabled
) return MIDIERR_NODEVICE
;
926 if (midiSeq
== NULL
) {
927 WARN("can't play !\n");
928 return MIDIERR_NODEVICE
;
931 lpData
= (LPBYTE
) lpMidiHdr
->lpData
;
934 return MIDIERR_UNPREPARED
;
935 if (!(lpMidiHdr
->dwFlags
& MHDR_PREPARED
))
936 return MIDIERR_UNPREPARED
;
937 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
)
938 return MIDIERR_STILLPLAYING
;
939 lpMidiHdr
->dwFlags
&= ~MHDR_DONE
;
940 lpMidiHdr
->dwFlags
|= MHDR_INQUEUE
;
942 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
943 * data, or can it also contain raw MIDI data, to be split up and sent to
945 * If the latest is true, then the following WARNing will fire up
947 if (lpData
[0] != 0xF0 || lpData
[lpMidiHdr
->dwBufferLength
- 1] != 0xF7) {
948 WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
949 lpNewData
= HeapAlloc(GetProcessHeap(), 0, lpMidiHdr
->dwBufferLength
+ 2);
952 TRACE("dwBufferLength=%u !\n", lpMidiHdr
->dwBufferLength
);
953 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
954 lpData
[0], lpData
[1], lpData
[2], lpData
[lpMidiHdr
->dwBufferLength
-3],
955 lpData
[lpMidiHdr
->dwBufferLength
-2], lpData
[lpMidiHdr
->dwBufferLength
-1]);
957 switch (MidiOutDev
[wDevID
].caps
.wTechnology
) {
959 /* FIXME: I don't think there is much to do here */
962 if (lpData
[0] != 0xF0) {
963 /* Send start of System Exclusive */
966 memcpy(lpNewData
, lpData
, lpMidiHdr
->dwBufferLength
);
967 WARN("Adding missing 0xF0 marker at the beginning of "
968 "system exclusive byte stream\n");
970 if (lpData
[lpMidiHdr
->dwBufferLength
-1] != 0xF7) {
971 /* Send end of System Exclusive */
972 memcpy(lpData
+ len_add
, lpData
, lpMidiHdr
->dwBufferLength
);
973 lpNewData
[lpMidiHdr
->dwBufferLength
+ len_add
- 1] = 0xF0;
975 WARN("Adding missing 0xF7 marker at the end of "
976 "system exclusive byte stream\n");
978 snd_seq_ev_clear(&event
);
979 snd_seq_ev_set_direct(&event
);
980 snd_seq_ev_set_source(&event
, port_out
);
981 snd_seq_ev_set_dest(&event
, MidiOutDev
[wDevID
].addr
.client
, MidiOutDev
[wDevID
].addr
.port
);
982 TRACE("client = %d port = %d\n", MidiOutDev
[wDevID
].addr
.client
, MidiOutDev
[wDevID
].addr
.port
);
983 snd_seq_ev_set_sysex(&event
, lpMidiHdr
->dwBufferLength
+ len_add
, lpNewData
? lpNewData
: lpData
);
984 snd_seq_event_output_direct(midiSeq
, &event
);
986 HeapFree(GetProcessHeap(), 0, lpData
);
989 WARN("Technology not supported (yet) %d !\n",
990 MidiOutDev
[wDevID
].caps
.wTechnology
);
991 return MMSYSERR_NOTENABLED
;
994 lpMidiHdr
->dwFlags
&= ~MHDR_INQUEUE
;
995 lpMidiHdr
->dwFlags
|= MHDR_DONE
;
996 if (MIDI_NotifyClient(wDevID
, MOM_DONE
, (DWORD
)lpMidiHdr
, 0L) != MMSYSERR_NOERROR
) {
997 WARN("can't notify client !\n");
998 return MMSYSERR_INVALPARAM
;
1000 return MMSYSERR_NOERROR
;
1003 /**************************************************************************
1004 * modPrepare [internal]
1006 static DWORD
modPrepare(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
1008 TRACE("(%04X, %p, %08X);\n", wDevID
, lpMidiHdr
, dwSize
);
1010 if (midiSeq
== NULL
) {
1011 WARN("can't prepare !\n");
1012 return MMSYSERR_NOTENABLED
;
1015 /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
1016 * asks to prepare MIDIHDR which dwFlags != 0.
1017 * So at least check for the inqueue flag
1019 if (dwSize
< sizeof(MIDIHDR
) || lpMidiHdr
== 0 ||
1020 lpMidiHdr
->lpData
== 0 || (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
) != 0 ||
1021 lpMidiHdr
->dwBufferLength
>= 0x10000ul
) {
1022 WARN("%p %p %08x %d/%d\n", lpMidiHdr
, lpMidiHdr
->lpData
,
1023 lpMidiHdr
->dwFlags
, sizeof(MIDIHDR
), dwSize
);
1024 return MMSYSERR_INVALPARAM
;
1027 lpMidiHdr
->lpNext
= 0;
1028 lpMidiHdr
->dwFlags
|= MHDR_PREPARED
;
1029 lpMidiHdr
->dwFlags
&= ~MHDR_DONE
;
1030 return MMSYSERR_NOERROR
;
1033 /**************************************************************************
1034 * modUnprepare [internal]
1036 static DWORD
modUnprepare(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
1038 TRACE("(%04X, %p, %08X);\n", wDevID
, lpMidiHdr
, dwSize
);
1040 if (midiSeq
== NULL
) {
1041 WARN("can't unprepare !\n");
1042 return MMSYSERR_NOTENABLED
;
1045 if (dwSize
< sizeof(MIDIHDR
) || lpMidiHdr
== 0)
1046 return MMSYSERR_INVALPARAM
;
1047 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
)
1048 return MIDIERR_STILLPLAYING
;
1049 lpMidiHdr
->dwFlags
&= ~MHDR_PREPARED
;
1050 return MMSYSERR_NOERROR
;
1053 /**************************************************************************
1054 * modReset [internal]
1056 static DWORD
modReset(WORD wDevID
)
1060 TRACE("(%04X);\n", wDevID
);
1062 if (wDevID
>= MODM_NumDevs
) return MMSYSERR_BADDEVICEID
;
1063 if (!MidiOutDev
[wDevID
].bEnabled
) return MIDIERR_NODEVICE
;
1065 /* stop all notes */
1066 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1067 * it's channel dependent...
1069 for (chn
= 0; chn
< 16; chn
++) {
1070 /* turn off every note */
1071 modData(wDevID
, 0x7800 | MIDI_CMD_CONTROL
| chn
);
1072 /* remove sustain on all channels */
1073 modData(wDevID
, (MIDI_CTL_SUSTAIN
<< 8) | MIDI_CMD_CONTROL
| chn
);
1075 /* FIXME: the LongData buffers must also be returned to the app */
1076 return MMSYSERR_NOERROR
;
1080 /**************************************************************************
1081 * ALSA_AddMidiPort [internal]
1083 * Helper for ALSA_MidiInit
1085 static void ALSA_AddMidiPort(snd_seq_client_info_t
* cinfo
, snd_seq_port_info_t
* pinfo
, int cap
, int type
)
1087 if (cap
& SND_SEQ_PORT_CAP_WRITE
) {
1088 TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo
),
1089 snd_seq_client_info_get_name(cinfo
),
1090 snd_seq_client_info_get_type(cinfo
) == SND_SEQ_USER_CLIENT
? "user" : "kernel",
1091 snd_seq_port_info_get_port(pinfo
),
1092 snd_seq_port_info_get_name(pinfo
),
1095 if (MODM_NumDevs
>= MAX_MIDIOUTDRV
)
1100 memcpy(&MidiOutDev
[MODM_NumDevs
].addr
, snd_seq_port_info_get_addr(pinfo
), sizeof(snd_seq_addr_t
));
1102 /* Manufac ID. We do not have access to this with soundcard.h
1103 * Does not seem to be a problem, because in mmsystem.h only
1104 * Microsoft's ID is listed.
1106 MidiOutDev
[MODM_NumDevs
].caps
.wMid
= 0x00FF;
1107 MidiOutDev
[MODM_NumDevs
].caps
.wPid
= 0x0001; /* FIXME Product ID */
1108 /* Product Version. We simply say "1" */
1109 MidiOutDev
[MODM_NumDevs
].caps
.vDriverVersion
= 0x001;
1110 MidiOutDev
[MODM_NumDevs
].caps
.wChannelMask
= 0xFFFF;
1112 /* FIXME Do we have this information?
1113 * Assuming the soundcards can handle
1114 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1115 * not MIDICAPS_CACHE.
1117 MidiOutDev
[MODM_NumDevs
].caps
.dwSupport
= MIDICAPS_VOLUME
|MIDICAPS_LRVOLUME
;
1118 MultiByteToWideChar(CP_ACP
, 0, snd_seq_client_info_get_name(cinfo
), -1,
1119 MidiOutDev
[MODM_NumDevs
].caps
.szPname
,
1120 sizeof(MidiOutDev
[MODM_NumDevs
].caps
.szPname
) / sizeof(WCHAR
));
1122 MidiOutDev
[MODM_NumDevs
].caps
.wTechnology
= MIDI_AlsaToWindowsDeviceType(type
);
1123 MidiOutDev
[MODM_NumDevs
].caps
.wVoices
= 16;
1125 /* FIXME Is it possible to know the maximum
1126 * number of simultaneous notes of a soundcard ?
1127 * I believe we don't have this information, but
1128 * it's probably equal or more than wVoices
1130 MidiOutDev
[MODM_NumDevs
].caps
.wNotes
= 16;
1131 MidiOutDev
[MODM_NumDevs
].bEnabled
= TRUE
;
1133 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1134 "\tALSA info: midi dev-type=%lx, capa=%lx\n",
1135 MODM_NumDevs
, wine_dbgstr_w(MidiOutDev
[MODM_NumDevs
].caps
.szPname
),
1136 MidiOutDev
[MODM_NumDevs
].caps
.wTechnology
,
1137 MidiOutDev
[MODM_NumDevs
].caps
.wVoices
, MidiOutDev
[MODM_NumDevs
].caps
.wNotes
,
1138 MidiOutDev
[MODM_NumDevs
].caps
.wChannelMask
, MidiOutDev
[MODM_NumDevs
].caps
.dwSupport
,
1139 (long)type
, (long)0);
1143 if (cap
& SND_SEQ_PORT_CAP_READ
) {
1144 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo
),
1145 snd_seq_client_info_get_name(cinfo
),
1146 snd_seq_client_info_get_type(cinfo
) == SND_SEQ_USER_CLIENT
? "user" : "kernel",
1147 snd_seq_port_info_get_port(pinfo
),
1148 snd_seq_port_info_get_name(pinfo
),
1151 if (MIDM_NumDevs
>= MAX_MIDIINDRV
)
1156 memcpy(&MidiInDev
[MIDM_NumDevs
].addr
, snd_seq_port_info_get_addr(pinfo
), sizeof(snd_seq_addr_t
));
1158 /* Manufac ID. We do not have access to this with soundcard.h
1159 * Does not seem to be a problem, because in mmsystem.h only
1160 * Microsoft's ID is listed.
1162 MidiInDev
[MIDM_NumDevs
].caps
.wMid
= 0x00FF;
1163 MidiInDev
[MIDM_NumDevs
].caps
.wPid
= 0x0001; /* FIXME Product ID */
1164 /* Product Version. We simply say "1" */
1165 MidiInDev
[MIDM_NumDevs
].caps
.vDriverVersion
= 0x001;
1167 /* FIXME Do we have this information?
1168 * Assuming the soundcards can handle
1169 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1170 * not MIDICAPS_CACHE.
1172 MidiInDev
[MIDM_NumDevs
].caps
.dwSupport
= MIDICAPS_VOLUME
|MIDICAPS_LRVOLUME
;
1173 MultiByteToWideChar(CP_ACP
, 0, snd_seq_client_info_get_name(cinfo
), -1,
1174 MidiInDev
[MIDM_NumDevs
].caps
.szPname
,
1175 sizeof(MidiInDev
[MIDM_NumDevs
].caps
.szPname
) / sizeof(WCHAR
));
1176 MidiInDev
[MIDM_NumDevs
].state
= 0;
1178 TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1179 "\tALSA info: midi dev-type=%lx, capa=%lx\n",
1180 MIDM_NumDevs
, wine_dbgstr_w(MidiInDev
[MIDM_NumDevs
].caps
.szPname
),
1181 MidiInDev
[MIDM_NumDevs
].caps
.dwSupport
,
1182 (long)type
, (long)0);
1188 #endif /* HAVE_ALSA */
1191 /*======================================================================*
1192 * MIDI entry points *
1193 *======================================================================*/
1195 /**************************************************************************
1196 * ALSA_MidiInit [internal]
1198 * Initializes the MIDI devices information variables
1200 LONG
ALSA_MidiInit(void)
1203 static BOOL bInitDone
= FALSE
;
1204 snd_seq_client_info_t
*cinfo
;
1205 snd_seq_port_info_t
*pinfo
;
1210 TRACE("Initializing the MIDI variables.\n");
1213 /* try to open device */
1214 if (midiOpenSeq(0) == -1) {
1218 #if 0 /* Debug purpose */
1219 snd_lib_error_set_handler(error_handler
);
1222 snd_seq_client_info_alloca(&cinfo
);
1223 snd_seq_port_info_alloca(&pinfo
);
1225 /* First, search for all internal midi devices */
1226 snd_seq_client_info_set_client(cinfo
, -1);
1227 while(snd_seq_query_next_client(midiSeq
, cinfo
) >= 0) {
1228 snd_seq_port_info_set_client(pinfo
, snd_seq_client_info_get_client(cinfo
));
1229 snd_seq_port_info_set_port(pinfo
, -1);
1230 while (snd_seq_query_next_port(midiSeq
, pinfo
) >= 0) {
1231 int cap
= snd_seq_port_info_get_capability(pinfo
);
1232 int type
= snd_seq_port_info_get_type(pinfo
);
1233 if (type
!= SND_SEQ_PORT_TYPE_MIDI_GENERIC
)
1234 ALSA_AddMidiPort(cinfo
, pinfo
, cap
, type
);
1238 /* Second, search for all external ports */
1239 snd_seq_client_info_set_client(cinfo
, -1);
1240 while(snd_seq_query_next_client(midiSeq
, cinfo
) >= 0) {
1241 snd_seq_port_info_set_client(pinfo
, snd_seq_client_info_get_client(cinfo
));
1242 snd_seq_port_info_set_port(pinfo
, -1);
1243 while (snd_seq_query_next_port(midiSeq
, pinfo
) >= 0) {
1244 int cap
= snd_seq_port_info_get_capability(pinfo
);
1245 int type
= snd_seq_port_info_get_type(pinfo
);
1246 if (type
== SND_SEQ_PORT_TYPE_MIDI_GENERIC
)
1247 ALSA_AddMidiPort(cinfo
, pinfo
, cap
, type
);
1251 /* close file and exit */
1259 /**************************************************************************
1260 * midMessage (WINEOSS.4)
1262 DWORD WINAPI
ALSA_midMessage(UINT wDevID
, UINT wMsg
, DWORD dwUser
,
1263 DWORD dwParam1
, DWORD dwParam2
)
1265 TRACE("(%04X, %04X, %08X, %08X, %08X);\n",
1266 wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1273 /* FIXME: Pretend this is supported */
1276 return midOpen(wDevID
, (LPMIDIOPENDESC
)dwParam1
, dwParam2
);
1278 return midClose(wDevID
);
1279 case MIDM_ADDBUFFER
:
1280 return midAddBuffer(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
1282 return midPrepare(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
1283 case MIDM_UNPREPARE
:
1284 return midUnprepare(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
1285 case MIDM_GETDEVCAPS
:
1286 return midGetDevCaps(wDevID
, (LPMIDIINCAPSW
)dwParam1
,dwParam2
);
1287 case MIDM_GETNUMDEVS
:
1288 return MIDM_NumDevs
;
1290 return midReset(wDevID
);
1292 return midStart(wDevID
);
1294 return midStop(wDevID
);
1297 TRACE("Unsupported message\n");
1299 return MMSYSERR_NOTSUPPORTED
;
1302 /**************************************************************************
1303 * modMessage (WINEOSS.5)
1305 DWORD WINAPI
ALSA_modMessage(UINT wDevID
, UINT wMsg
, DWORD dwUser
,
1306 DWORD dwParam1
, DWORD dwParam2
)
1308 TRACE("(%04X, %04X, %08X, %08X, %08X);\n",
1309 wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1317 /* FIXME: Pretend this is supported */
1320 return modOpen(wDevID
, (LPMIDIOPENDESC
)dwParam1
, dwParam2
);
1322 return modClose(wDevID
);
1324 return modData(wDevID
, dwParam1
);
1326 return modLongData(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
1328 return modPrepare(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
1329 case MODM_UNPREPARE
:
1330 return modUnprepare(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
1331 case MODM_GETDEVCAPS
:
1332 return modGetDevCaps(wDevID
, (LPMIDIOUTCAPSW
)dwParam1
, dwParam2
);
1333 case MODM_GETNUMDEVS
:
1334 return MODM_NumDevs
;
1335 case MODM_GETVOLUME
:
1337 case MODM_SETVOLUME
:
1340 return modReset(wDevID
);
1343 TRACE("Unsupported message\n");
1345 return MMSYSERR_NOTSUPPORTED
;
1348 /*-----------------------------------------------------------------------*/