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 split 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
53 #include "wine/debug.h"
55 #include <alsa/asoundlib.h>
57 WINE_DEFAULT_DEBUG_CHANNEL(midi
);
59 #ifndef SND_SEQ_PORT_TYPE_PORT
60 #define SND_SEQ_PORT_TYPE_PORT (1<<19) /* Appears in version 1.0.12rc1 */
64 int state
; /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
66 MIDIOPENDESC midiDesc
;
70 unsigned char incoming
[3];
71 unsigned char incPrev
;
81 MIDIOPENDESC midiDesc
;
85 void* lpExtra
; /* according to port type (MIDI, FM...), extra data when needed */
90 static WINE_MIDIIN MidiInDev
[MAX_MIDIINDRV
];
91 static WINE_MIDIOUT MidiOutDev
[MAX_MIDIOUTDRV
];
93 /* this is the total number of MIDI out devices found (synth and port) */
94 static int MODM_NumDevs
= 0;
95 /* this is the total number of MIDI out devices found */
96 static int MIDM_NumDevs
= 0;
98 static CRITICAL_SECTION midiSeqLock
;
99 static CRITICAL_SECTION_DEBUG midiSeqLockDebug
=
102 { &midiSeqLockDebug
.ProcessLocksList
, &midiSeqLockDebug
.ProcessLocksList
},
103 0, 0, { (DWORD_PTR
)(__FILE__
": midiSeqLock") }
105 static CRITICAL_SECTION midiSeqLock
= { &midiSeqLockDebug
, -1, 0, 0, 0, 0 };
106 static snd_seq_t
* midiSeq
= NULL
;
107 static int numOpenMidiSeq
= 0;
108 static int numStartedMidiIn
= 0;
113 static CRITICAL_SECTION crit_sect
; /* protects all MidiIn buffer queues */
114 static CRITICAL_SECTION_DEBUG critsect_debug
=
117 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
118 0, 0, { (DWORD_PTR
)(__FILE__
": crit_sect") }
120 static CRITICAL_SECTION crit_sect
= { &critsect_debug
, -1, 0, 0, 0, 0 };
122 static int end_thread
;
123 static HANDLE hThread
;
125 /*======================================================================*
126 * Low level MIDI implementation *
127 *======================================================================*/
129 #if 0 /* Debug Purpose */
130 static void error_handler(const char* file
, int line
, const char* function
, int err
, const char* fmt
, ...)
136 fprintf(stderr
, "ALSA lib %s:%i:(%s) ", file
, line
, function
);
137 vfprintf(stderr
, fmt
, arg
);
139 fprintf(stderr
, ": %s", snd_strerror(err
));
145 /**************************************************************************
146 * MIDI_unixToWindowsDeviceType [internal]
148 * return the Windows equivalent to a Unix Device Type
151 static int MIDI_AlsaToWindowsDeviceType(unsigned int type
)
153 /* MOD_MIDIPORT output port
154 * MOD_SYNTH generic internal synth
155 * MOD_SQSYNTH square wave internal synth
156 * MOD_FMSYNTH FM internal synth
157 * MOD_MAPPER MIDI mapper
158 * MOD_WAVETABLE hardware wavetable internal synth
159 * MOD_SWSYNTH software internal synth
162 /* FIXME Is this really the correct equivalence from ALSA to
163 Windows Sound type? */
165 if (type
& SND_SEQ_PORT_TYPE_SYNTH
)
168 if (type
& (SND_SEQ_PORT_TYPE_DIRECT_SAMPLE
|SND_SEQ_PORT_TYPE_SAMPLE
))
171 if (type
& (SND_SEQ_PORT_TYPE_MIDI_GENERIC
|SND_SEQ_PORT_TYPE_APPLICATION
))
174 ERR("Cannot determine the type (alsa type is %x) of this midi device. Assuming FM Synth\n", type
);
178 /**************************************************************************
179 * MIDI_NotifyClient [internal]
181 static void MIDI_NotifyClient(UINT wDevID
, WORD wMsg
,
182 DWORD_PTR dwParam1
, DWORD_PTR dwParam2
)
184 DWORD_PTR dwCallBack
;
187 DWORD_PTR dwInstance
;
189 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
190 wDevID
, wMsg
, dwParam1
, dwParam2
);
197 if (wDevID
> MODM_NumDevs
) return;
199 dwCallBack
= MidiOutDev
[wDevID
].midiDesc
.dwCallback
;
200 uFlags
= MidiOutDev
[wDevID
].wFlags
;
201 hDev
= MidiOutDev
[wDevID
].midiDesc
.hMidi
;
202 dwInstance
= MidiOutDev
[wDevID
].midiDesc
.dwInstance
;
212 if (wDevID
> MIDM_NumDevs
) return;
214 dwCallBack
= MidiInDev
[wDevID
].midiDesc
.dwCallback
;
215 uFlags
= MidiInDev
[wDevID
].wFlags
;
216 hDev
= MidiInDev
[wDevID
].midiDesc
.hMidi
;
217 dwInstance
= MidiInDev
[wDevID
].midiDesc
.dwInstance
;
220 ERR("Unsupported MSW-MIDI message %u\n", wMsg
);
224 DriverCallback(dwCallBack
, uFlags
, hDev
, wMsg
, dwInstance
, dwParam1
, dwParam2
);
227 static BOOL midi_warn
= TRUE
;
228 /**************************************************************************
229 * midiOpenSeq [internal]
231 static int midiOpenSeq(BOOL create_client
)
233 EnterCriticalSection(&midiSeqLock
);
234 if (numOpenMidiSeq
== 0) {
235 if (snd_seq_open(&midiSeq
, "default", SND_SEQ_OPEN_DUPLEX
, 0) < 0)
239 WARN("Error opening ALSA sequencer.\n");
242 LeaveCriticalSection(&midiSeqLock
);
247 /* Setting the client name is the only init to do */
248 snd_seq_set_client_name(midiSeq
, "WINE midi driver");
250 port_out
= snd_seq_create_simple_port(midiSeq
, "WINE ALSA Output",
251 SND_SEQ_PORT_CAP_READ
|SND_SEQ_PORT_CAP_SUBS_READ
|SND_SEQ_PORT_CAP_SUBS_WRITE
,
252 SND_SEQ_PORT_TYPE_MIDI_GENERIC
|SND_SEQ_PORT_TYPE_APPLICATION
);
254 TRACE("Unable to create output port\n");
256 TRACE("Outport port %d created successfully\n", port_out
);
258 port_in
= snd_seq_create_simple_port(midiSeq
, "WINE ALSA Input",
259 SND_SEQ_PORT_CAP_WRITE
|SND_SEQ_PORT_CAP_SUBS_READ
|SND_SEQ_PORT_CAP_SUBS_WRITE
,
260 SND_SEQ_PORT_TYPE_MIDI_GENERIC
|SND_SEQ_PORT_TYPE_APPLICATION
);
262 TRACE("Unable to create input port\n");
264 TRACE("Input port %d created successfully\n", port_in
);
268 LeaveCriticalSection(&midiSeqLock
);
272 /**************************************************************************
273 * midiCloseSeq [internal]
275 static int midiCloseSeq(void)
277 EnterCriticalSection(&midiSeqLock
);
278 if (--numOpenMidiSeq
== 0) {
279 snd_seq_delete_simple_port(midiSeq
, port_out
);
280 snd_seq_delete_simple_port(midiSeq
, port_in
);
281 snd_seq_close(midiSeq
);
284 LeaveCriticalSection(&midiSeqLock
);
288 static void handle_midi_event(snd_seq_event_t
*ev
)
292 /* Find the target device */
293 for (wDevID
= 0; wDevID
< MIDM_NumDevs
; wDevID
++)
294 if ( (ev
->source
.client
== MidiInDev
[wDevID
].addr
.client
) && (ev
->source
.port
== MidiInDev
[wDevID
].addr
.port
) )
296 if ((wDevID
== MIDM_NumDevs
) || (MidiInDev
[wDevID
].state
!= 1))
297 FIXME("Unexpected event received, type = %x from %d:%d\n", ev
->type
, ev
->source
.client
, ev
->source
.port
);
299 DWORD dwTime
, toSend
= 0;
301 /* FIXME: Should use ev->time instead for better accuracy */
302 dwTime
= GetTickCount() - MidiInDev
[wDevID
].startTime
;
303 TRACE("Event received, type = %x, device = %d\n", ev
->type
, wDevID
);
306 case SND_SEQ_EVENT_NOTEOFF
:
307 toSend
= (ev
->data
.note
.velocity
<< 16) | (ev
->data
.note
.note
<< 8) | MIDI_CMD_NOTE_OFF
| ev
->data
.control
.channel
;
309 case SND_SEQ_EVENT_NOTEON
:
310 toSend
= (ev
->data
.note
.velocity
<< 16) | (ev
->data
.note
.note
<< 8) | MIDI_CMD_NOTE_ON
| ev
->data
.control
.channel
;
312 case SND_SEQ_EVENT_KEYPRESS
:
313 toSend
= (ev
->data
.note
.velocity
<< 16) | (ev
->data
.note
.note
<< 8) | MIDI_CMD_NOTE_PRESSURE
| ev
->data
.control
.channel
;
315 case SND_SEQ_EVENT_CONTROLLER
:
316 toSend
= (ev
->data
.control
.value
<< 16) | (ev
->data
.control
.param
<< 8) | MIDI_CMD_CONTROL
| ev
->data
.control
.channel
;
318 case SND_SEQ_EVENT_PITCHBEND
:
319 value
= ev
->data
.control
.value
+ 0x2000;
320 toSend
= (((value
>> 7) & 0x7f) << 16) | ((value
& 0x7f) << 8) | MIDI_CMD_BENDER
| ev
->data
.control
.channel
;
322 case SND_SEQ_EVENT_PGMCHANGE
:
323 toSend
= ((ev
->data
.control
.value
& 0x7f) << 8) | MIDI_CMD_PGM_CHANGE
| ev
->data
.control
.channel
;
325 case SND_SEQ_EVENT_CHANPRESS
:
326 toSend
= ((ev
->data
.control
.value
& 0x7f) << 8) | MIDI_CMD_CHANNEL_PRESSURE
| ev
->data
.control
.channel
;
328 case SND_SEQ_EVENT_CLOCK
:
331 case SND_SEQ_EVENT_START
:
334 case SND_SEQ_EVENT_CONTINUE
:
337 case SND_SEQ_EVENT_STOP
:
340 case SND_SEQ_EVENT_SONGPOS
:
341 toSend
= (((ev
->data
.control
.value
>> 7) & 0x7f) << 16) | ((ev
->data
.control
.value
& 0x7f) << 8) | MIDI_CMD_COMMON_SONG_POS
;
343 case SND_SEQ_EVENT_SONGSEL
:
344 toSend
= ((ev
->data
.control
.value
& 0x7f) << 8) | MIDI_CMD_COMMON_SONG_SELECT
;
346 case SND_SEQ_EVENT_RESET
:
349 case SND_SEQ_EVENT_QFRAME
:
350 toSend
= ((ev
->data
.control
.value
& 0x7f) << 8) | MIDI_CMD_COMMON_MTC_QUARTER
;
352 case SND_SEQ_EVENT_SYSEX
:
355 int len
= ev
->data
.ext
.len
;
356 LPBYTE ptr
= ev
->data
.ext
.ptr
;
359 EnterCriticalSection(&crit_sect
);
361 if ((lpMidiHdr
= MidiInDev
[wDevID
].lpQueueHdr
) != NULL
) {
362 int copylen
= min(len
, lpMidiHdr
->dwBufferLength
- lpMidiHdr
->dwBytesRecorded
);
363 memcpy(lpMidiHdr
->lpData
+ lpMidiHdr
->dwBytesRecorded
, ptr
+ pos
, copylen
);
364 lpMidiHdr
->dwBytesRecorded
+= copylen
;
367 /* We check if we reach the end of buffer or the end of sysex before notifying
368 * to handle the case where ALSA split the sysex into several events */
369 if ((lpMidiHdr
->dwBytesRecorded
== lpMidiHdr
->dwBufferLength
) ||
370 (*(BYTE
*)(lpMidiHdr
->lpData
+ lpMidiHdr
->dwBytesRecorded
- 1) == 0xF7)) {
371 MidiInDev
[wDevID
].lpQueueHdr
= lpMidiHdr
->lpNext
;
372 lpMidiHdr
->dwFlags
&= ~MHDR_INQUEUE
;
373 lpMidiHdr
->dwFlags
|= MHDR_DONE
;
374 MIDI_NotifyClient(wDevID
, MIM_LONGDATA
, (DWORD_PTR
)lpMidiHdr
, dwTime
);
377 FIXME("Sysex data received but no buffer to store it!\n");
381 LeaveCriticalSection(&crit_sect
);
384 case SND_SEQ_EVENT_SENSING
:
388 FIXME("Unhandled event received, type = %x\n", ev
->type
);
392 TRACE("Received event %08x from %d:%d\n", toSend
, ev
->source
.client
, ev
->source
.port
);
393 MIDI_NotifyClient(wDevID
, MIM_DATA
, toSend
, dwTime
);
398 static DWORD WINAPI
midRecThread(LPVOID arg
)
404 TRACE("Thread startup\n");
407 TRACE("Thread loop\n");
408 EnterCriticalSection(&midiSeqLock
);
409 npfd
= snd_seq_poll_descriptors_count(midiSeq
, POLLIN
);
410 pfd
= HeapAlloc(GetProcessHeap(), 0, npfd
* sizeof(struct pollfd
));
411 snd_seq_poll_descriptors(midiSeq
, pfd
, npfd
, POLLIN
);
412 LeaveCriticalSection(&midiSeqLock
);
414 /* Check if an event is present */
415 if (poll(pfd
, npfd
, 250) <= 0) {
416 HeapFree(GetProcessHeap(), 0, pfd
);
420 /* Note: This definitely does not work.
421 * while(snd_seq_event_input_pending(midiSeq, 0) > 0) {
423 snd_seq_event_input(midiSeq, &ev);
425 snd_seq_free_event(ev);
431 EnterCriticalSection(&midiSeqLock
);
432 snd_seq_event_input(midiSeq
, &ev
);
433 LeaveCriticalSection(&midiSeqLock
);
436 handle_midi_event(ev
);
437 snd_seq_free_event(ev
);
440 EnterCriticalSection(&midiSeqLock
);
441 ret
= snd_seq_event_input_pending(midiSeq
, 0);
442 LeaveCriticalSection(&midiSeqLock
);
445 HeapFree(GetProcessHeap(), 0, pfd
);
450 /**************************************************************************
451 * midGetDevCaps [internal]
453 static DWORD
midGetDevCaps(WORD wDevID
, LPMIDIINCAPSW lpCaps
, DWORD dwSize
)
455 TRACE("(%04X, %p, %08X);\n", wDevID
, lpCaps
, dwSize
);
457 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
458 if (lpCaps
== NULL
) return MMSYSERR_INVALPARAM
;
460 memcpy(lpCaps
, &MidiInDev
[wDevID
].caps
, min(dwSize
, sizeof(*lpCaps
)));
462 return MMSYSERR_NOERROR
;
466 /**************************************************************************
469 static DWORD
midOpen(WORD wDevID
, LPMIDIOPENDESC lpDesc
, DWORD dwFlags
)
473 TRACE("(%04X, %p, %08X);\n", wDevID
, lpDesc
, dwFlags
);
475 if (lpDesc
== NULL
) {
476 WARN("Invalid Parameter !\n");
477 return MMSYSERR_INVALPARAM
;
481 * how to check that content of lpDesc is correct ?
483 if (wDevID
>= MIDM_NumDevs
) {
484 WARN("wDevID too large (%u) !\n", wDevID
);
485 return MMSYSERR_BADDEVICEID
;
487 if (MidiInDev
[wDevID
].state
== -1) {
488 WARN("device disabled\n");
489 return MIDIERR_NODEVICE
;
491 if (MidiInDev
[wDevID
].midiDesc
.hMidi
!= 0) {
492 WARN("device already open !\n");
493 return MMSYSERR_ALLOCATED
;
495 if ((dwFlags
& MIDI_IO_STATUS
) != 0) {
496 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
497 dwFlags
&= ~MIDI_IO_STATUS
;
499 if ((dwFlags
& ~CALLBACK_TYPEMASK
) != 0) {
500 FIXME("Bad dwFlags\n");
501 return MMSYSERR_INVALFLAG
;
504 if (midiOpenSeq(TRUE
) < 0) {
505 return MMSYSERR_ERROR
;
508 MidiInDev
[wDevID
].wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
510 MidiInDev
[wDevID
].lpQueueHdr
= NULL
;
511 MidiInDev
[wDevID
].dwTotalPlayed
= 0;
512 MidiInDev
[wDevID
].bufsize
= 0x3FFF;
513 MidiInDev
[wDevID
].midiDesc
= *lpDesc
;
514 MidiInDev
[wDevID
].state
= 0;
515 MidiInDev
[wDevID
].incLen
= 0;
516 MidiInDev
[wDevID
].startTime
= 0;
518 /* Connect our app port to the device port */
519 EnterCriticalSection(&midiSeqLock
);
520 ret
= snd_seq_connect_from(midiSeq
, port_in
, MidiInDev
[wDevID
].addr
.client
,
521 MidiInDev
[wDevID
].addr
.port
);
522 LeaveCriticalSection(&midiSeqLock
);
524 return MMSYSERR_NOTENABLED
;
526 TRACE("Input port :%d connected %d:%d\n",port_in
,MidiInDev
[wDevID
].addr
.client
,MidiInDev
[wDevID
].addr
.port
);
528 if (numStartedMidiIn
++ == 0) {
530 hThread
= CreateThread(NULL
, 0, midRecThread
, NULL
, 0, NULL
);
532 numStartedMidiIn
= 0;
533 WARN("Couldn't create thread for midi-in\n");
535 return MMSYSERR_ERROR
;
537 SetThreadPriority(hThread
, THREAD_PRIORITY_TIME_CRITICAL
);
538 TRACE("Created thread for midi-in\n");
541 MIDI_NotifyClient(wDevID
, MIM_OPEN
, 0L, 0L);
542 return MMSYSERR_NOERROR
;
545 /**************************************************************************
546 * midClose [internal]
548 static DWORD
midClose(WORD wDevID
)
550 int ret
= MMSYSERR_NOERROR
;
552 TRACE("(%04X);\n", wDevID
);
554 if (wDevID
>= MIDM_NumDevs
) {
555 WARN("wDevID too big (%u) !\n", wDevID
);
556 return MMSYSERR_BADDEVICEID
;
558 if (MidiInDev
[wDevID
].midiDesc
.hMidi
== 0) {
559 WARN("device not opened !\n");
560 return MMSYSERR_ERROR
;
562 if (MidiInDev
[wDevID
].lpQueueHdr
!= 0) {
563 return MIDIERR_STILLPLAYING
;
566 if (midiSeq
== NULL
) {
568 return MMSYSERR_ERROR
;
570 if (--numStartedMidiIn
== 0) {
571 TRACE("Stopping thread for midi-in\n");
573 if (WaitForSingleObject(hThread
, 5000) != WAIT_OBJECT_0
) {
574 WARN("Thread end not signaled, force termination\n");
575 TerminateThread(hThread
, 0);
577 TRACE("Stopped thread for midi-in\n");
580 EnterCriticalSection(&midiSeqLock
);
581 snd_seq_disconnect_from(midiSeq
, port_in
, MidiInDev
[wDevID
].addr
.client
, MidiInDev
[wDevID
].addr
.port
);
582 LeaveCriticalSection(&midiSeqLock
);
585 MidiInDev
[wDevID
].bufsize
= 0;
586 MIDI_NotifyClient(wDevID
, MIM_CLOSE
, 0L, 0L);
587 MidiInDev
[wDevID
].midiDesc
.hMidi
= 0;
593 /**************************************************************************
594 * midAddBuffer [internal]
596 static DWORD
midAddBuffer(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
598 TRACE("(%04X, %p, %d);\n", wDevID
, lpMidiHdr
, dwSize
);
600 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
601 if (MidiInDev
[wDevID
].state
== -1) return MIDIERR_NODEVICE
;
603 if (lpMidiHdr
== NULL
) return MMSYSERR_INVALPARAM
;
604 if (dwSize
< offsetof(MIDIHDR
,dwOffset
)) return MMSYSERR_INVALPARAM
;
605 if (lpMidiHdr
->dwBufferLength
== 0) return MMSYSERR_INVALPARAM
;
606 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
) return MIDIERR_STILLPLAYING
;
607 if (!(lpMidiHdr
->dwFlags
& MHDR_PREPARED
)) return MIDIERR_UNPREPARED
;
609 EnterCriticalSection(&crit_sect
);
610 lpMidiHdr
->dwFlags
&= ~WHDR_DONE
;
611 lpMidiHdr
->dwFlags
|= MHDR_INQUEUE
;
612 lpMidiHdr
->dwBytesRecorded
= 0;
613 lpMidiHdr
->lpNext
= 0;
614 if (MidiInDev
[wDevID
].lpQueueHdr
== 0) {
615 MidiInDev
[wDevID
].lpQueueHdr
= lpMidiHdr
;
619 for (ptr
= MidiInDev
[wDevID
].lpQueueHdr
; ptr
->lpNext
!= 0;
621 ptr
->lpNext
= lpMidiHdr
;
623 LeaveCriticalSection(&crit_sect
);
625 return MMSYSERR_NOERROR
;
628 /**************************************************************************
629 * midPrepare [internal]
631 static DWORD
midPrepare(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
633 TRACE("(%04X, %p, %d);\n", wDevID
, lpMidiHdr
, dwSize
);
635 if (dwSize
< offsetof(MIDIHDR
,dwOffset
) || lpMidiHdr
== 0 || lpMidiHdr
->lpData
== 0)
636 return MMSYSERR_INVALPARAM
;
637 if (lpMidiHdr
->dwFlags
& MHDR_PREPARED
)
638 return MMSYSERR_NOERROR
;
640 lpMidiHdr
->lpNext
= 0;
641 lpMidiHdr
->dwFlags
|= MHDR_PREPARED
;
642 lpMidiHdr
->dwFlags
&= ~(MHDR_DONE
|MHDR_INQUEUE
); /* flags cleared since w2k */
644 return MMSYSERR_NOERROR
;
647 /**************************************************************************
648 * midUnprepare [internal]
650 static DWORD
midUnprepare(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
652 TRACE("(%04X, %p, %d);\n", wDevID
, lpMidiHdr
, dwSize
);
654 if (dwSize
< offsetof(MIDIHDR
,dwOffset
) || lpMidiHdr
== 0 || lpMidiHdr
->lpData
== 0)
655 return MMSYSERR_INVALPARAM
;
656 if (!(lpMidiHdr
->dwFlags
& MHDR_PREPARED
))
657 return MMSYSERR_NOERROR
;
658 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
)
659 return MIDIERR_STILLPLAYING
;
661 lpMidiHdr
->dwFlags
&= ~MHDR_PREPARED
;
663 return MMSYSERR_NOERROR
;
666 /**************************************************************************
667 * midReset [internal]
669 static DWORD
midReset(WORD wDevID
)
671 DWORD dwTime
= GetTickCount();
673 TRACE("(%04X);\n", wDevID
);
675 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
676 if (MidiInDev
[wDevID
].state
== -1) return MIDIERR_NODEVICE
;
678 EnterCriticalSection(&crit_sect
);
679 while (MidiInDev
[wDevID
].lpQueueHdr
) {
680 LPMIDIHDR lpMidiHdr
= MidiInDev
[wDevID
].lpQueueHdr
;
681 MidiInDev
[wDevID
].lpQueueHdr
= lpMidiHdr
->lpNext
;
682 lpMidiHdr
->dwFlags
&= ~MHDR_INQUEUE
;
683 lpMidiHdr
->dwFlags
|= MHDR_DONE
;
684 MIDI_NotifyClient(wDevID
, MIM_LONGDATA
, (DWORD_PTR
)lpMidiHdr
, dwTime
);
686 LeaveCriticalSection(&crit_sect
);
688 return MMSYSERR_NOERROR
;
691 /**************************************************************************
692 * midStart [internal]
694 static DWORD
midStart(WORD wDevID
)
696 TRACE("(%04X);\n", wDevID
);
698 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
699 if (MidiInDev
[wDevID
].state
== -1) return MIDIERR_NODEVICE
;
701 MidiInDev
[wDevID
].state
= 1;
702 MidiInDev
[wDevID
].startTime
= GetTickCount();
703 return MMSYSERR_NOERROR
;
706 /**************************************************************************
709 static DWORD
midStop(WORD wDevID
)
711 TRACE("(%04X);\n", wDevID
);
713 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
714 if (MidiInDev
[wDevID
].state
== -1) return MIDIERR_NODEVICE
;
716 MidiInDev
[wDevID
].state
= 0;
717 return MMSYSERR_NOERROR
;
720 /**************************************************************************
721 * modGetDevCaps [internal]
723 static DWORD
modGetDevCaps(WORD wDevID
, LPMIDIOUTCAPSW lpCaps
, DWORD dwSize
)
725 TRACE("(%04X, %p, %08X);\n", wDevID
, lpCaps
, dwSize
);
727 if (wDevID
>= MODM_NumDevs
) return MMSYSERR_BADDEVICEID
;
728 if (lpCaps
== NULL
) return MMSYSERR_INVALPARAM
;
730 memcpy(lpCaps
, &MidiOutDev
[wDevID
].caps
, min(dwSize
, sizeof(*lpCaps
)));
732 return MMSYSERR_NOERROR
;
735 /**************************************************************************
738 static DWORD
modOpen(WORD wDevID
, LPMIDIOPENDESC lpDesc
, DWORD dwFlags
)
742 TRACE("(%04X, %p, %08X);\n", wDevID
, lpDesc
, dwFlags
);
743 if (lpDesc
== NULL
) {
744 WARN("Invalid Parameter !\n");
745 return MMSYSERR_INVALPARAM
;
747 if (wDevID
>= MODM_NumDevs
) {
748 TRACE("MAX_MIDIOUTDRV reached !\n");
749 return MMSYSERR_BADDEVICEID
;
751 if (MidiOutDev
[wDevID
].midiDesc
.hMidi
!= 0) {
752 WARN("device already open !\n");
753 return MMSYSERR_ALLOCATED
;
755 if (!MidiOutDev
[wDevID
].bEnabled
) {
756 WARN("device disabled !\n");
757 return MIDIERR_NODEVICE
;
759 if ((dwFlags
& ~CALLBACK_TYPEMASK
) != 0) {
760 WARN("bad dwFlags\n");
761 return MMSYSERR_INVALFLAG
;
764 MidiOutDev
[wDevID
].lpExtra
= 0;
766 switch (MidiOutDev
[wDevID
].caps
.wTechnology
) {
770 if (midiOpenSeq(TRUE
) < 0) {
771 return MMSYSERR_ALLOCATED
;
775 WARN("Technology not supported (yet) %d !\n",
776 MidiOutDev
[wDevID
].caps
.wTechnology
);
777 return MMSYSERR_NOTENABLED
;
780 MidiOutDev
[wDevID
].wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
782 MidiOutDev
[wDevID
].lpQueueHdr
= NULL
;
783 MidiOutDev
[wDevID
].dwTotalPlayed
= 0;
784 MidiOutDev
[wDevID
].bufsize
= 0x3FFF;
785 MidiOutDev
[wDevID
].midiDesc
= *lpDesc
;
787 /* Connect our app port to the device port */
788 EnterCriticalSection(&midiSeqLock
);
789 ret
= snd_seq_connect_to(midiSeq
, port_out
, MidiOutDev
[wDevID
].addr
.client
,
790 MidiOutDev
[wDevID
].addr
.port
);
791 LeaveCriticalSection(&midiSeqLock
);
793 return MMSYSERR_NOTENABLED
;
795 TRACE("Output port :%d connected %d:%d\n",port_out
,MidiOutDev
[wDevID
].addr
.client
,MidiOutDev
[wDevID
].addr
.port
);
797 MIDI_NotifyClient(wDevID
, MOM_OPEN
, 0L, 0L);
798 return MMSYSERR_NOERROR
;
802 /**************************************************************************
803 * modClose [internal]
805 static DWORD
modClose(WORD wDevID
)
807 int ret
= MMSYSERR_NOERROR
;
809 TRACE("(%04X);\n", wDevID
);
811 if (MidiOutDev
[wDevID
].midiDesc
.hMidi
== 0) {
812 WARN("device not opened !\n");
813 return MMSYSERR_ERROR
;
815 /* FIXME: should test that no pending buffer is still in the queue for
818 if (midiSeq
== NULL
) {
819 WARN("can't close !\n");
820 return MMSYSERR_ERROR
;
823 switch (MidiOutDev
[wDevID
].caps
.wTechnology
) {
827 EnterCriticalSection(&midiSeqLock
);
828 snd_seq_disconnect_to(midiSeq
, port_out
, MidiOutDev
[wDevID
].addr
.client
, MidiOutDev
[wDevID
].addr
.port
);
829 LeaveCriticalSection(&midiSeqLock
);
833 WARN("Technology not supported (yet) %d !\n",
834 MidiOutDev
[wDevID
].caps
.wTechnology
);
835 return MMSYSERR_NOTENABLED
;
838 HeapFree(GetProcessHeap(), 0, MidiOutDev
[wDevID
].lpExtra
);
839 MidiOutDev
[wDevID
].lpExtra
= 0;
841 MidiOutDev
[wDevID
].bufsize
= 0;
842 MIDI_NotifyClient(wDevID
, MOM_CLOSE
, 0L, 0L);
843 MidiOutDev
[wDevID
].midiDesc
.hMidi
= 0;
847 /**************************************************************************
850 static DWORD
modData(WORD wDevID
, DWORD dwParam
)
852 BYTE evt
= LOBYTE(LOWORD(dwParam
));
853 BYTE d1
= HIBYTE(LOWORD(dwParam
));
854 BYTE d2
= LOBYTE(HIWORD(dwParam
));
856 TRACE("(%04X, %08X);\n", wDevID
, dwParam
);
858 if (wDevID
>= MODM_NumDevs
) return MMSYSERR_BADDEVICEID
;
859 if (!MidiOutDev
[wDevID
].bEnabled
) return MIDIERR_NODEVICE
;
861 if (midiSeq
== NULL
) {
862 WARN("can't play !\n");
863 return MIDIERR_NODEVICE
;
865 switch (MidiOutDev
[wDevID
].caps
.wTechnology
) {
869 int handled
= 1; /* Assume event is handled */
870 snd_seq_event_t event
;
871 snd_seq_ev_clear(&event
);
872 snd_seq_ev_set_direct(&event
);
873 snd_seq_ev_set_source(&event
, port_out
);
874 snd_seq_ev_set_subs(&event
);
876 switch (evt
& 0xF0) {
877 case MIDI_CMD_NOTE_OFF
:
878 snd_seq_ev_set_noteoff(&event
, evt
&0x0F, d1
, d2
);
880 case MIDI_CMD_NOTE_ON
:
881 snd_seq_ev_set_noteon(&event
, evt
&0x0F, d1
, d2
);
883 case MIDI_CMD_NOTE_PRESSURE
:
884 snd_seq_ev_set_keypress(&event
, evt
&0x0F, d1
, d2
);
886 case MIDI_CMD_CONTROL
:
887 snd_seq_ev_set_controller(&event
, evt
&0x0F, d1
, d2
);
889 case MIDI_CMD_BENDER
:
890 snd_seq_ev_set_pitchbend(&event
, evt
&0x0F, ((WORD
)d2
<< 7 | (WORD
)d1
) - 0x2000);
892 case MIDI_CMD_PGM_CHANGE
:
893 snd_seq_ev_set_pgmchange(&event
, evt
&0x0F, d1
);
895 case MIDI_CMD_CHANNEL_PRESSURE
:
896 snd_seq_ev_set_chanpress(&event
, evt
&0x0F, d1
);
898 case MIDI_CMD_COMMON_SYSEX
:
899 switch (evt
& 0x0F) {
900 case 0x00: /* System Exclusive, don't do it on modData,
901 * should require modLongData*/
902 case 0x04: /* Undefined. */
903 case 0x05: /* Undefined. */
904 case 0x07: /* End of Exclusive. */
905 case 0x09: /* Undefined. */
906 case 0x0D: /* Undefined. */
909 case 0x06: /* Tune Request */
910 case 0x08: /* Timing Clock. */
911 case 0x0A: /* Start. */
912 case 0x0B: /* Continue */
913 case 0x0C: /* Stop */
914 case 0x0E: /* Active Sensing. */
916 snd_midi_event_t
*midi_event
;
918 snd_midi_event_new(1, &midi_event
);
919 snd_midi_event_init(midi_event
);
920 snd_midi_event_encode_byte(midi_event
, evt
, &event
);
921 snd_midi_event_free(midi_event
);
924 case 0x0F: /* Reset */
925 /* snd_seq_ev_set_sysex(&event, 1, &evt);
926 this other way may be better */
928 BYTE reset_sysex_seq
[] = {MIDI_CMD_COMMON_SYSEX
, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
929 snd_seq_ev_set_sysex(&event
, sizeof(reset_sysex_seq
), reset_sysex_seq
);
932 case 0x01: /* MTC Quarter frame */
933 case 0x03: /* Song Select. */
938 snd_seq_ev_set_sysex(&event
, sizeof(buf
), buf
);
941 case 0x02: /* Song Position Pointer. */
947 snd_seq_ev_set_sysex(&event
, sizeof(buf
), buf
);
954 EnterCriticalSection(&midiSeqLock
);
955 snd_seq_event_output_direct(midiSeq
, &event
);
956 LeaveCriticalSection(&midiSeqLock
);
961 WARN("Technology not supported (yet) %d !\n",
962 MidiOutDev
[wDevID
].caps
.wTechnology
);
963 return MMSYSERR_NOTENABLED
;
966 return MMSYSERR_NOERROR
;
969 /**************************************************************************
970 * modLongData [internal]
972 static DWORD
modLongData(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
975 BYTE
*lpData
, *lpNewData
= NULL
;
976 snd_seq_event_t event
;
978 TRACE("(%04X, %p, %08X);\n", wDevID
, lpMidiHdr
, dwSize
);
980 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
981 * but it seems to be used only for midi input.
982 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
985 if (wDevID
>= MODM_NumDevs
) return MMSYSERR_BADDEVICEID
;
986 if (!MidiOutDev
[wDevID
].bEnabled
) return MIDIERR_NODEVICE
;
988 if (midiSeq
== NULL
) {
989 WARN("can't play !\n");
990 return MIDIERR_NODEVICE
;
993 lpData
= (BYTE
*)lpMidiHdr
->lpData
;
996 return MIDIERR_UNPREPARED
;
997 if (!(lpMidiHdr
->dwFlags
& MHDR_PREPARED
))
998 return MIDIERR_UNPREPARED
;
999 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
)
1000 return MIDIERR_STILLPLAYING
;
1001 lpMidiHdr
->dwFlags
&= ~MHDR_DONE
;
1002 lpMidiHdr
->dwFlags
|= MHDR_INQUEUE
;
1004 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
1005 * data, or can it also contain raw MIDI data, to be split up and sent to
1007 * If the latest is true, then the following WARNing will fire up
1009 if (lpData
[0] != 0xF0 || lpData
[lpMidiHdr
->dwBufferLength
- 1] != 0xF7) {
1010 WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
1011 lpNewData
= HeapAlloc(GetProcessHeap(), 0, lpMidiHdr
->dwBufferLength
+ 2);
1014 TRACE("dwBufferLength=%u !\n", lpMidiHdr
->dwBufferLength
);
1015 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
1016 lpData
[0], lpData
[1], lpData
[2], lpData
[lpMidiHdr
->dwBufferLength
-3],
1017 lpData
[lpMidiHdr
->dwBufferLength
-2], lpData
[lpMidiHdr
->dwBufferLength
-1]);
1019 switch (MidiOutDev
[wDevID
].caps
.wTechnology
) {
1021 /* FIXME: I don't think there is much to do here */
1022 HeapFree(GetProcessHeap(), 0, lpNewData
);
1025 if (lpData
[0] != 0xF0) {
1026 /* Send start of System Exclusive */
1028 lpNewData
[0] = 0xF0;
1029 memcpy(lpNewData
+ 1, lpData
, lpMidiHdr
->dwBufferLength
);
1030 WARN("Adding missing 0xF0 marker at the beginning of system exclusive byte stream\n");
1032 if (lpData
[lpMidiHdr
->dwBufferLength
-1] != 0xF7) {
1033 /* Send end of System Exclusive */
1035 memcpy(lpNewData
, lpData
, lpMidiHdr
->dwBufferLength
);
1036 lpNewData
[lpMidiHdr
->dwBufferLength
+ len_add
] = 0xF7;
1038 WARN("Adding missing 0xF7 marker at the end of system exclusive byte stream\n");
1040 snd_seq_ev_clear(&event
);
1041 snd_seq_ev_set_direct(&event
);
1042 snd_seq_ev_set_source(&event
, port_out
);
1043 snd_seq_ev_set_subs(&event
);
1044 snd_seq_ev_set_sysex(&event
, lpMidiHdr
->dwBufferLength
+ len_add
, lpNewData
? lpNewData
: lpData
);
1045 EnterCriticalSection(&midiSeqLock
);
1046 snd_seq_event_output_direct(midiSeq
, &event
);
1047 LeaveCriticalSection(&midiSeqLock
);
1048 HeapFree(GetProcessHeap(), 0, lpNewData
);
1051 WARN("Technology not supported (yet) %d !\n",
1052 MidiOutDev
[wDevID
].caps
.wTechnology
);
1053 HeapFree(GetProcessHeap(), 0, lpNewData
);
1054 return MMSYSERR_NOTENABLED
;
1057 lpMidiHdr
->dwFlags
&= ~MHDR_INQUEUE
;
1058 lpMidiHdr
->dwFlags
|= MHDR_DONE
;
1059 MIDI_NotifyClient(wDevID
, MOM_DONE
, (DWORD_PTR
)lpMidiHdr
, 0L);
1060 return MMSYSERR_NOERROR
;
1063 /**************************************************************************
1064 * modPrepare [internal]
1066 static DWORD
modPrepare(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
1068 TRACE("(%04X, %p, %d);\n", wDevID
, lpMidiHdr
, dwSize
);
1070 if (dwSize
< offsetof(MIDIHDR
,dwOffset
) || lpMidiHdr
== 0 || lpMidiHdr
->lpData
== 0)
1071 return MMSYSERR_INVALPARAM
;
1072 if (lpMidiHdr
->dwFlags
& MHDR_PREPARED
)
1073 return MMSYSERR_NOERROR
;
1075 lpMidiHdr
->lpNext
= 0;
1076 lpMidiHdr
->dwFlags
|= MHDR_PREPARED
;
1077 lpMidiHdr
->dwFlags
&= ~(MHDR_DONE
|MHDR_INQUEUE
); /* flags cleared since w2k */
1078 return MMSYSERR_NOERROR
;
1081 /**************************************************************************
1082 * modUnprepare [internal]
1084 static DWORD
modUnprepare(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
1086 TRACE("(%04X, %p, %d);\n", wDevID
, lpMidiHdr
, dwSize
);
1088 if (dwSize
< offsetof(MIDIHDR
,dwOffset
) || lpMidiHdr
== 0 || lpMidiHdr
->lpData
== 0)
1089 return MMSYSERR_INVALPARAM
;
1090 if (!(lpMidiHdr
->dwFlags
& MHDR_PREPARED
))
1091 return MMSYSERR_NOERROR
;
1092 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
)
1093 return MIDIERR_STILLPLAYING
;
1094 lpMidiHdr
->dwFlags
&= ~MHDR_PREPARED
;
1095 return MMSYSERR_NOERROR
;
1098 /**************************************************************************
1099 * modGetVolume [internal]
1101 static DWORD
modGetVolume(WORD wDevID
, DWORD
* lpdwVolume
)
1103 if (!lpdwVolume
) return MMSYSERR_INVALPARAM
;
1104 if (wDevID
>= MODM_NumDevs
) return MMSYSERR_BADDEVICEID
;
1105 *lpdwVolume
= 0xFFFFFFFF;
1106 return (MidiOutDev
[wDevID
].caps
.dwSupport
& MIDICAPS_VOLUME
) ? 0 : MMSYSERR_NOTSUPPORTED
;
1109 /**************************************************************************
1110 * modReset [internal]
1112 static DWORD
modReset(WORD wDevID
)
1116 TRACE("(%04X);\n", wDevID
);
1118 if (wDevID
>= MODM_NumDevs
) return MMSYSERR_BADDEVICEID
;
1119 if (!MidiOutDev
[wDevID
].bEnabled
) return MIDIERR_NODEVICE
;
1121 /* stop all notes */
1122 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1123 * it's channel dependent...
1125 for (chn
= 0; chn
< 16; chn
++) {
1126 /* turn off every note */
1127 modData(wDevID
, 0x7800 | MIDI_CMD_CONTROL
| chn
);
1128 /* remove sustain on all channels */
1129 modData(wDevID
, (MIDI_CTL_SUSTAIN
<< 8) | MIDI_CMD_CONTROL
| chn
);
1131 /* FIXME: the LongData buffers must also be returned to the app */
1132 return MMSYSERR_NOERROR
;
1136 /**************************************************************************
1137 * ALSA_AddMidiPort [internal]
1139 * Helper for ALSA_MidiInit
1141 static void ALSA_AddMidiPort(snd_seq_client_info_t
* cinfo
, snd_seq_port_info_t
* pinfo
, unsigned int cap
, unsigned int type
)
1143 char midiPortName
[MAXPNAMELEN
];
1145 if (cap
& SND_SEQ_PORT_CAP_WRITE
) {
1146 TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo
),
1147 snd_seq_client_info_get_name(cinfo
),
1148 snd_seq_client_info_get_type(cinfo
) == SND_SEQ_USER_CLIENT
? "user" : "kernel",
1149 snd_seq_port_info_get_port(pinfo
),
1150 snd_seq_port_info_get_name(pinfo
),
1153 if (MODM_NumDevs
>= MAX_MIDIOUTDRV
)
1158 MidiOutDev
[MODM_NumDevs
].addr
= *snd_seq_port_info_get_addr(pinfo
);
1160 /* Manufac ID. We do not have access to this with soundcard.h
1161 * Does not seem to be a problem, because in mmsystem.h only
1162 * Microsoft's ID is listed.
1164 MidiOutDev
[MODM_NumDevs
].caps
.wMid
= 0x00FF;
1165 MidiOutDev
[MODM_NumDevs
].caps
.wPid
= 0x0001; /* FIXME Product ID */
1166 /* Product Version. We simply say "1" */
1167 MidiOutDev
[MODM_NumDevs
].caps
.vDriverVersion
= 0x001;
1168 /* The following are mandatory for MOD_MIDIPORT */
1169 MidiOutDev
[MODM_NumDevs
].caps
.wChannelMask
= 0xFFFF;
1170 MidiOutDev
[MODM_NumDevs
].caps
.wVoices
= 0;
1171 MidiOutDev
[MODM_NumDevs
].caps
.wNotes
= 0;
1172 MidiOutDev
[MODM_NumDevs
].caps
.dwSupport
= 0;
1174 /* Try to use both client and port names, if this is too long take the port name only.
1175 In the second case the port name should be explicit enough due to its big size.
1177 if ( (strlen(snd_seq_client_info_get_name(cinfo
)) + strlen(snd_seq_port_info_get_name(pinfo
)) + 3) < MAXPNAMELEN
) {
1178 sprintf(midiPortName
, "%s - %s", snd_seq_client_info_get_name(cinfo
), snd_seq_port_info_get_name(pinfo
));
1180 lstrcpynA(midiPortName
, snd_seq_port_info_get_name(pinfo
), MAXPNAMELEN
);
1182 MultiByteToWideChar(CP_UNIXCP
, 0, midiPortName
, -1, MidiOutDev
[MODM_NumDevs
].caps
.szPname
,
1183 ARRAY_SIZE(MidiOutDev
[MODM_NumDevs
].caps
.szPname
));
1185 MidiOutDev
[MODM_NumDevs
].caps
.wTechnology
= MIDI_AlsaToWindowsDeviceType(type
);
1187 if (MOD_MIDIPORT
!= MidiOutDev
[MODM_NumDevs
].caps
.wTechnology
) {
1188 /* FIXME Do we have this information?
1189 * Assuming the soundcards can handle
1190 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1191 * not MIDICAPS_CACHE.
1193 MidiOutDev
[MODM_NumDevs
].caps
.dwSupport
= MIDICAPS_VOLUME
|MIDICAPS_LRVOLUME
;
1194 MidiOutDev
[MODM_NumDevs
].caps
.wVoices
= 16;
1196 /* FIXME Is it possible to know the maximum
1197 * number of simultaneous notes of a soundcard ?
1198 * I believe we don't have this information, but
1199 * it's probably equal or more than wVoices
1201 MidiOutDev
[MODM_NumDevs
].caps
.wNotes
= 16;
1203 MidiOutDev
[MODM_NumDevs
].bEnabled
= TRUE
;
1205 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1206 "\tALSA info: midi dev-type=%x, capa=0\n",
1207 MODM_NumDevs
, wine_dbgstr_w(MidiOutDev
[MODM_NumDevs
].caps
.szPname
),
1208 MidiOutDev
[MODM_NumDevs
].caps
.wTechnology
,
1209 MidiOutDev
[MODM_NumDevs
].caps
.wVoices
, MidiOutDev
[MODM_NumDevs
].caps
.wNotes
,
1210 MidiOutDev
[MODM_NumDevs
].caps
.wChannelMask
, MidiOutDev
[MODM_NumDevs
].caps
.dwSupport
,
1215 if (cap
& SND_SEQ_PORT_CAP_READ
) {
1216 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo
),
1217 snd_seq_client_info_get_name(cinfo
),
1218 snd_seq_client_info_get_type(cinfo
) == SND_SEQ_USER_CLIENT
? "user" : "kernel",
1219 snd_seq_port_info_get_port(pinfo
),
1220 snd_seq_port_info_get_name(pinfo
),
1223 if (MIDM_NumDevs
>= MAX_MIDIINDRV
)
1228 MidiInDev
[MIDM_NumDevs
].addr
= *snd_seq_port_info_get_addr(pinfo
);
1230 /* Manufac ID. We do not have access to this with soundcard.h
1231 * Does not seem to be a problem, because in mmsystem.h only
1232 * Microsoft's ID is listed.
1234 MidiInDev
[MIDM_NumDevs
].caps
.wMid
= 0x00FF;
1235 MidiInDev
[MIDM_NumDevs
].caps
.wPid
= 0x0001; /* FIXME Product ID */
1236 /* Product Version. We simply say "1" */
1237 MidiInDev
[MIDM_NumDevs
].caps
.vDriverVersion
= 0x001;
1238 MidiInDev
[MIDM_NumDevs
].caps
.dwSupport
= 0; /* mandatory with MIDIINCAPS */
1240 /* Try to use both client and port names, if this is too long take the port name only.
1241 In the second case the port name should be explicit enough due to its big size.
1243 if ( (strlen(snd_seq_client_info_get_name(cinfo
)) + strlen(snd_seq_port_info_get_name(pinfo
)) + 3) < MAXPNAMELEN
) {
1244 sprintf(midiPortName
, "%s - %s", snd_seq_client_info_get_name(cinfo
), snd_seq_port_info_get_name(pinfo
));
1246 lstrcpynA(midiPortName
, snd_seq_port_info_get_name(pinfo
), MAXPNAMELEN
);
1248 MultiByteToWideChar(CP_UNIXCP
, 0, midiPortName
, -1, MidiInDev
[MIDM_NumDevs
].caps
.szPname
,
1249 ARRAY_SIZE(MidiInDev
[MIDM_NumDevs
].caps
.szPname
));
1250 MidiInDev
[MIDM_NumDevs
].state
= 0;
1252 TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1253 "\tALSA info: midi dev-type=%x, capa=0\n",
1254 MIDM_NumDevs
, wine_dbgstr_w(MidiInDev
[MIDM_NumDevs
].caps
.szPname
),
1255 MidiInDev
[MIDM_NumDevs
].caps
.dwSupport
,
1263 /*======================================================================*
1264 * MIDI entry points *
1265 *======================================================================*/
1267 /**************************************************************************
1268 * ALSA_MidiInit [internal]
1270 * Initializes the MIDI devices information variables
1272 static BOOL
ALSA_MidiInit(void)
1274 static BOOL bInitDone
= FALSE
;
1275 snd_seq_client_info_t
*cinfo
;
1276 snd_seq_port_info_t
*pinfo
;
1281 TRACE("Initializing the MIDI variables.\n");
1284 /* try to open device */
1285 if (midiOpenSeq(FALSE
) == -1) {
1289 #if 0 /* Debug purpose */
1290 snd_lib_error_set_handler(error_handler
);
1292 cinfo
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_seq_client_info_sizeof() );
1293 pinfo
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_seq_port_info_sizeof() );
1295 /* First, search for all internal midi devices */
1296 snd_seq_client_info_set_client(cinfo
, -1);
1297 while(snd_seq_query_next_client(midiSeq
, cinfo
) >= 0) {
1298 snd_seq_port_info_set_client(pinfo
, snd_seq_client_info_get_client(cinfo
));
1299 snd_seq_port_info_set_port(pinfo
, -1);
1300 while (snd_seq_query_next_port(midiSeq
, pinfo
) >= 0) {
1301 unsigned int cap
= snd_seq_port_info_get_capability(pinfo
);
1302 unsigned int type
= snd_seq_port_info_get_type(pinfo
);
1303 if (!(type
& SND_SEQ_PORT_TYPE_PORT
))
1304 ALSA_AddMidiPort(cinfo
, pinfo
, cap
, type
);
1308 /* Second, search for all external ports */
1309 snd_seq_client_info_set_client(cinfo
, -1);
1310 while(snd_seq_query_next_client(midiSeq
, cinfo
) >= 0) {
1311 snd_seq_port_info_set_client(pinfo
, snd_seq_client_info_get_client(cinfo
));
1312 snd_seq_port_info_set_port(pinfo
, -1);
1313 while (snd_seq_query_next_port(midiSeq
, pinfo
) >= 0) {
1314 unsigned int cap
= snd_seq_port_info_get_capability(pinfo
);
1315 unsigned int type
= snd_seq_port_info_get_type(pinfo
);
1316 if (type
& SND_SEQ_PORT_TYPE_PORT
)
1317 ALSA_AddMidiPort(cinfo
, pinfo
, cap
, type
);
1321 /* close file and exit */
1323 HeapFree( GetProcessHeap(), 0, cinfo
);
1324 HeapFree( GetProcessHeap(), 0, pinfo
);
1330 /**************************************************************************
1331 * midMessage (WINEALSA.@)
1333 DWORD WINAPI
ALSA_midMessage(UINT wDevID
, UINT wMsg
, DWORD_PTR dwUser
,
1334 DWORD_PTR dwParam1
, DWORD_PTR dwParam2
)
1336 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1337 wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1345 /* FIXME: Pretend this is supported */
1348 return midOpen(wDevID
, (LPMIDIOPENDESC
)dwParam1
, dwParam2
);
1350 return midClose(wDevID
);
1351 case MIDM_ADDBUFFER
:
1352 return midAddBuffer(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
1354 return midPrepare(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
1355 case MIDM_UNPREPARE
:
1356 return midUnprepare(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
1357 case MIDM_GETDEVCAPS
:
1358 return midGetDevCaps(wDevID
, (LPMIDIINCAPSW
)dwParam1
,dwParam2
);
1359 case MIDM_GETNUMDEVS
:
1360 return MIDM_NumDevs
;
1362 return midReset(wDevID
);
1364 return midStart(wDevID
);
1366 return midStop(wDevID
);
1368 TRACE("Unsupported message\n");
1370 return MMSYSERR_NOTSUPPORTED
;
1373 /**************************************************************************
1374 * modMessage (WINEALSA.@)
1376 DWORD WINAPI
ALSA_modMessage(UINT wDevID
, UINT wMsg
, DWORD_PTR dwUser
,
1377 DWORD_PTR dwParam1
, DWORD_PTR dwParam2
)
1379 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1380 wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1389 /* FIXME: Pretend this is supported */
1392 return modOpen(wDevID
, (LPMIDIOPENDESC
)dwParam1
, dwParam2
);
1394 return modClose(wDevID
);
1396 return modData(wDevID
, dwParam1
);
1398 return modLongData(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
1400 return modPrepare(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
1401 case MODM_UNPREPARE
:
1402 return modUnprepare(wDevID
, (LPMIDIHDR
)dwParam1
, dwParam2
);
1403 case MODM_GETDEVCAPS
:
1404 return modGetDevCaps(wDevID
, (LPMIDIOUTCAPSW
)dwParam1
, dwParam2
);
1405 case MODM_GETNUMDEVS
:
1406 return MODM_NumDevs
;
1407 case MODM_GETVOLUME
:
1408 return modGetVolume(wDevID
, (DWORD
*)dwParam1
);
1409 case MODM_SETVOLUME
:
1412 return modReset(wDevID
);
1414 TRACE("Unsupported message\n");
1416 return MMSYSERR_NOTSUPPORTED
;
1419 /**************************************************************************
1420 * DriverProc (WINEALSA.@)
1422 LRESULT CALLBACK
ALSA_DriverProc(DWORD_PTR dwDevID
, HDRVR hDriv
, UINT wMsg
,
1423 LPARAM dwParam1
, LPARAM dwParam2
)
1425 /* EPP TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", */
1426 /* EPP dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
1435 case DRV_QUERYCONFIGURE
: