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 #if 0 /* FIXME: Is it possible to use a port for READ & WRITE ops */
251 port_in
= port_out
= snd_seq_create_simple_port(midiSeq
, "WINE ALSA Input/Output", SND_SEQ_PORT_CAP_READ
|SND_SEQ_PORT_CAP_WRITE
,
252 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_out
= snd_seq_create_simple_port(midiSeq
, "WINE ALSA Output", SND_SEQ_PORT_CAP_READ
,
259 SND_SEQ_PORT_TYPE_APPLICATION
);
261 TRACE("Unable to create output port\n");
263 TRACE("Outport port %d created successfully\n", port_out
);
265 port_in
= snd_seq_create_simple_port(midiSeq
, "WINE ALSA Input", SND_SEQ_PORT_CAP_WRITE
,
266 SND_SEQ_PORT_TYPE_APPLICATION
);
268 TRACE("Unable to create input port\n");
270 TRACE("Input port %d created successfully\n", port_in
);
275 LeaveCriticalSection(&midiSeqLock
);
279 /**************************************************************************
280 * midiCloseSeq [internal]
282 static int midiCloseSeq(void)
284 EnterCriticalSection(&midiSeqLock
);
285 if (--numOpenMidiSeq
== 0) {
286 snd_seq_delete_simple_port(midiSeq
, port_out
);
287 snd_seq_delete_simple_port(midiSeq
, port_in
);
288 snd_seq_close(midiSeq
);
291 LeaveCriticalSection(&midiSeqLock
);
295 static DWORD WINAPI
midRecThread(LPVOID arg
)
301 TRACE("Thread startup\n");
304 TRACE("Thread loop\n");
305 EnterCriticalSection(&midiSeqLock
);
306 npfd
= snd_seq_poll_descriptors_count(midiSeq
, POLLIN
);
307 pfd
= HeapAlloc(GetProcessHeap(), 0, npfd
* sizeof(struct pollfd
));
308 snd_seq_poll_descriptors(midiSeq
, pfd
, npfd
, POLLIN
);
309 LeaveCriticalSection(&midiSeqLock
);
311 /* Check if an event is present */
312 if (poll(pfd
, npfd
, 250) <= 0) {
313 HeapFree(GetProcessHeap(), 0, pfd
);
317 /* Note: This definitely does not work.
318 * while(snd_seq_event_input_pending(midiSeq, 0) > 0) {
320 snd_seq_event_input(midiSeq, &ev);
322 snd_seq_free_event(ev);
328 EnterCriticalSection(&midiSeqLock
);
329 snd_seq_event_input(midiSeq
, &ev
);
330 LeaveCriticalSection(&midiSeqLock
);
331 /* Find the target device */
332 for (wDevID
= 0; wDevID
< MIDM_NumDevs
; wDevID
++)
333 if ( (ev
->source
.client
== MidiInDev
[wDevID
].addr
.client
) && (ev
->source
.port
== MidiInDev
[wDevID
].addr
.port
) )
335 if ((wDevID
== MIDM_NumDevs
) || (MidiInDev
[wDevID
].state
!= 1))
336 FIXME("Unexpected event received, type = %x from %d:%d\n", ev
->type
, ev
->source
.client
, ev
->source
.port
);
338 DWORD dwTime
, toSend
= 0;
340 /* FIXME: Should use ev->time instead for better accuracy */
341 dwTime
= GetTickCount() - MidiInDev
[wDevID
].startTime
;
342 TRACE("Event received, type = %x, device = %d\n", ev
->type
, wDevID
);
345 case SND_SEQ_EVENT_NOTEOFF
:
346 toSend
= (ev
->data
.note
.velocity
<< 16) | (ev
->data
.note
.note
<< 8) | MIDI_CMD_NOTE_OFF
| ev
->data
.control
.channel
;
348 case SND_SEQ_EVENT_NOTEON
:
349 toSend
= (ev
->data
.note
.velocity
<< 16) | (ev
->data
.note
.note
<< 8) | MIDI_CMD_NOTE_ON
| ev
->data
.control
.channel
;
351 case SND_SEQ_EVENT_KEYPRESS
:
352 toSend
= (ev
->data
.note
.velocity
<< 16) | (ev
->data
.note
.note
<< 8) | MIDI_CMD_NOTE_PRESSURE
| ev
->data
.control
.channel
;
354 case SND_SEQ_EVENT_CONTROLLER
:
355 toSend
= (ev
->data
.control
.value
<< 16) | (ev
->data
.control
.param
<< 8) | MIDI_CMD_CONTROL
| ev
->data
.control
.channel
;
357 case SND_SEQ_EVENT_PITCHBEND
:
358 value
= ev
->data
.control
.value
+ 0x2000;
359 toSend
= (((value
>> 7) & 0x7f) << 16) | ((value
& 0x7f) << 8) | MIDI_CMD_BENDER
| ev
->data
.control
.channel
;
361 case SND_SEQ_EVENT_PGMCHANGE
:
362 toSend
= ((ev
->data
.control
.value
& 0x7f) << 8) | MIDI_CMD_PGM_CHANGE
| ev
->data
.control
.channel
;
364 case SND_SEQ_EVENT_CHANPRESS
:
365 toSend
= ((ev
->data
.control
.value
& 0x7f) << 8) | MIDI_CMD_CHANNEL_PRESSURE
| ev
->data
.control
.channel
;
367 case SND_SEQ_EVENT_CLOCK
:
370 case SND_SEQ_EVENT_START
:
373 case SND_SEQ_EVENT_CONTINUE
:
376 case SND_SEQ_EVENT_STOP
:
379 case SND_SEQ_EVENT_SONGPOS
:
380 toSend
= (((ev
->data
.control
.value
>> 7) & 0x7f) << 16) | ((ev
->data
.control
.value
& 0x7f) << 8) | MIDI_CMD_COMMON_SONG_POS
;
382 case SND_SEQ_EVENT_SONGSEL
:
383 toSend
= ((ev
->data
.control
.value
& 0x7f) << 8) | MIDI_CMD_COMMON_SONG_SELECT
;
385 case SND_SEQ_EVENT_RESET
:
388 case SND_SEQ_EVENT_QFRAME
:
389 toSend
= ((ev
->data
.control
.value
& 0x7f) << 8) | MIDI_CMD_COMMON_MTC_QUARTER
;
391 case SND_SEQ_EVENT_SYSEX
:
394 int len
= ev
->data
.ext
.len
;
395 LPBYTE ptr
= ev
->data
.ext
.ptr
;
398 EnterCriticalSection(&crit_sect
);
400 if ((lpMidiHdr
= MidiInDev
[wDevID
].lpQueueHdr
) != NULL
) {
401 int copylen
= min(len
, lpMidiHdr
->dwBufferLength
- lpMidiHdr
->dwBytesRecorded
);
402 memcpy(lpMidiHdr
->lpData
+ lpMidiHdr
->dwBytesRecorded
, ptr
+ pos
, copylen
);
403 lpMidiHdr
->dwBytesRecorded
+= copylen
;
406 /* We check if we reach the end of buffer or the end of sysex before notifying
407 * to handle the case where ALSA split the sysex into several events */
408 if ((lpMidiHdr
->dwBytesRecorded
== lpMidiHdr
->dwBufferLength
) ||
409 (*(BYTE
*)(lpMidiHdr
->lpData
+ lpMidiHdr
->dwBytesRecorded
- 1) == 0xF7)) {
410 MidiInDev
[wDevID
].lpQueueHdr
= lpMidiHdr
->lpNext
;
411 lpMidiHdr
->dwFlags
&= ~MHDR_INQUEUE
;
412 lpMidiHdr
->dwFlags
|= MHDR_DONE
;
413 MIDI_NotifyClient(wDevID
, MIM_LONGDATA
, (DWORD_PTR
)lpMidiHdr
, dwTime
);
416 FIXME("Sysex data received but no buffer to store it!\n");
420 LeaveCriticalSection(&crit_sect
);
423 case SND_SEQ_EVENT_SENSING
:
427 FIXME("Unhandled event received, type = %x\n", ev
->type
);
431 TRACE("Received event %08x from %d:%d\n", toSend
, ev
->source
.client
, ev
->source
.port
);
432 MIDI_NotifyClient(wDevID
, MIM_DATA
, toSend
, dwTime
);
435 snd_seq_free_event(ev
);
437 EnterCriticalSection(&midiSeqLock
);
438 ret
= snd_seq_event_input_pending(midiSeq
, 0);
439 LeaveCriticalSection(&midiSeqLock
);
442 HeapFree(GetProcessHeap(), 0, pfd
);
447 /**************************************************************************
448 * midGetDevCaps [internal]
450 static DWORD
midGetDevCaps(WORD wDevID
, LPMIDIINCAPSW lpCaps
, DWORD dwSize
)
452 TRACE("(%04X, %p, %08X);\n", wDevID
, lpCaps
, dwSize
);
454 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
455 if (lpCaps
== NULL
) return MMSYSERR_INVALPARAM
;
457 memcpy(lpCaps
, &MidiInDev
[wDevID
].caps
, min(dwSize
, sizeof(*lpCaps
)));
459 return MMSYSERR_NOERROR
;
463 /**************************************************************************
466 static DWORD
midOpen(WORD wDevID
, LPMIDIOPENDESC lpDesc
, DWORD dwFlags
)
470 TRACE("(%04X, %p, %08X);\n", wDevID
, lpDesc
, dwFlags
);
472 if (lpDesc
== NULL
) {
473 WARN("Invalid Parameter !\n");
474 return MMSYSERR_INVALPARAM
;
478 * how to check that content of lpDesc is correct ?
480 if (wDevID
>= MIDM_NumDevs
) {
481 WARN("wDevID too large (%u) !\n", wDevID
);
482 return MMSYSERR_BADDEVICEID
;
484 if (MidiInDev
[wDevID
].state
== -1) {
485 WARN("device disabled\n");
486 return MIDIERR_NODEVICE
;
488 if (MidiInDev
[wDevID
].midiDesc
.hMidi
!= 0) {
489 WARN("device already open !\n");
490 return MMSYSERR_ALLOCATED
;
492 if ((dwFlags
& MIDI_IO_STATUS
) != 0) {
493 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
494 dwFlags
&= ~MIDI_IO_STATUS
;
496 if ((dwFlags
& ~CALLBACK_TYPEMASK
) != 0) {
497 FIXME("Bad dwFlags\n");
498 return MMSYSERR_INVALFLAG
;
501 if (midiOpenSeq(TRUE
) < 0) {
502 return MMSYSERR_ERROR
;
505 MidiInDev
[wDevID
].wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
507 MidiInDev
[wDevID
].lpQueueHdr
= NULL
;
508 MidiInDev
[wDevID
].dwTotalPlayed
= 0;
509 MidiInDev
[wDevID
].bufsize
= 0x3FFF;
510 MidiInDev
[wDevID
].midiDesc
= *lpDesc
;
511 MidiInDev
[wDevID
].state
= 0;
512 MidiInDev
[wDevID
].incLen
= 0;
513 MidiInDev
[wDevID
].startTime
= 0;
515 /* Connect our app port to the device port */
516 EnterCriticalSection(&midiSeqLock
);
517 ret
= snd_seq_connect_from(midiSeq
, port_in
, MidiInDev
[wDevID
].addr
.client
,
518 MidiInDev
[wDevID
].addr
.port
);
519 LeaveCriticalSection(&midiSeqLock
);
521 return MMSYSERR_NOTENABLED
;
523 TRACE("Input port :%d connected %d:%d\n",port_in
,MidiInDev
[wDevID
].addr
.client
,MidiInDev
[wDevID
].addr
.port
);
525 if (numStartedMidiIn
++ == 0) {
527 hThread
= CreateThread(NULL
, 0, midRecThread
, NULL
, 0, NULL
);
529 numStartedMidiIn
= 0;
530 WARN("Couldn't create thread for midi-in\n");
532 return MMSYSERR_ERROR
;
534 SetThreadPriority(hThread
, THREAD_PRIORITY_TIME_CRITICAL
);
535 TRACE("Created thread for midi-in\n");
538 MIDI_NotifyClient(wDevID
, MIM_OPEN
, 0L, 0L);
539 return MMSYSERR_NOERROR
;
542 /**************************************************************************
543 * midClose [internal]
545 static DWORD
midClose(WORD wDevID
)
547 int ret
= MMSYSERR_NOERROR
;
549 TRACE("(%04X);\n", wDevID
);
551 if (wDevID
>= MIDM_NumDevs
) {
552 WARN("wDevID too big (%u) !\n", wDevID
);
553 return MMSYSERR_BADDEVICEID
;
555 if (MidiInDev
[wDevID
].midiDesc
.hMidi
== 0) {
556 WARN("device not opened !\n");
557 return MMSYSERR_ERROR
;
559 if (MidiInDev
[wDevID
].lpQueueHdr
!= 0) {
560 return MIDIERR_STILLPLAYING
;
563 if (midiSeq
== NULL
) {
565 return MMSYSERR_ERROR
;
567 if (--numStartedMidiIn
== 0) {
568 TRACE("Stopping thread for midi-in\n");
570 if (WaitForSingleObject(hThread
, 5000) != WAIT_OBJECT_0
) {
571 WARN("Thread end not signaled, force termination\n");
572 TerminateThread(hThread
, 0);
574 TRACE("Stopped thread for midi-in\n");
577 EnterCriticalSection(&midiSeqLock
);
578 snd_seq_disconnect_from(midiSeq
, port_in
, MidiInDev
[wDevID
].addr
.client
, MidiInDev
[wDevID
].addr
.port
);
579 LeaveCriticalSection(&midiSeqLock
);
582 MidiInDev
[wDevID
].bufsize
= 0;
583 MIDI_NotifyClient(wDevID
, MIM_CLOSE
, 0L, 0L);
584 MidiInDev
[wDevID
].midiDesc
.hMidi
= 0;
590 /**************************************************************************
591 * midAddBuffer [internal]
593 static DWORD
midAddBuffer(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
595 TRACE("(%04X, %p, %d);\n", wDevID
, lpMidiHdr
, dwSize
);
597 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
598 if (MidiInDev
[wDevID
].state
== -1) return MIDIERR_NODEVICE
;
600 if (lpMidiHdr
== NULL
) return MMSYSERR_INVALPARAM
;
601 if (dwSize
< offsetof(MIDIHDR
,dwOffset
)) return MMSYSERR_INVALPARAM
;
602 if (lpMidiHdr
->dwBufferLength
== 0) return MMSYSERR_INVALPARAM
;
603 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
) return MIDIERR_STILLPLAYING
;
604 if (!(lpMidiHdr
->dwFlags
& MHDR_PREPARED
)) return MIDIERR_UNPREPARED
;
606 EnterCriticalSection(&crit_sect
);
607 lpMidiHdr
->dwFlags
&= ~WHDR_DONE
;
608 lpMidiHdr
->dwFlags
|= MHDR_INQUEUE
;
609 lpMidiHdr
->dwBytesRecorded
= 0;
610 lpMidiHdr
->lpNext
= 0;
611 if (MidiInDev
[wDevID
].lpQueueHdr
== 0) {
612 MidiInDev
[wDevID
].lpQueueHdr
= lpMidiHdr
;
616 for (ptr
= MidiInDev
[wDevID
].lpQueueHdr
; ptr
->lpNext
!= 0;
618 ptr
->lpNext
= lpMidiHdr
;
620 LeaveCriticalSection(&crit_sect
);
622 return MMSYSERR_NOERROR
;
625 /**************************************************************************
626 * midPrepare [internal]
628 static DWORD
midPrepare(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
630 TRACE("(%04X, %p, %d);\n", wDevID
, lpMidiHdr
, dwSize
);
632 if (dwSize
< offsetof(MIDIHDR
,dwOffset
) || lpMidiHdr
== 0 || lpMidiHdr
->lpData
== 0)
633 return MMSYSERR_INVALPARAM
;
634 if (lpMidiHdr
->dwFlags
& MHDR_PREPARED
)
635 return MMSYSERR_NOERROR
;
637 lpMidiHdr
->lpNext
= 0;
638 lpMidiHdr
->dwFlags
|= MHDR_PREPARED
;
639 lpMidiHdr
->dwFlags
&= ~(MHDR_DONE
|MHDR_INQUEUE
); /* flags cleared since w2k */
641 return MMSYSERR_NOERROR
;
644 /**************************************************************************
645 * midUnprepare [internal]
647 static DWORD
midUnprepare(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
649 TRACE("(%04X, %p, %d);\n", wDevID
, lpMidiHdr
, dwSize
);
651 if (dwSize
< offsetof(MIDIHDR
,dwOffset
) || lpMidiHdr
== 0 || lpMidiHdr
->lpData
== 0)
652 return MMSYSERR_INVALPARAM
;
653 if (!(lpMidiHdr
->dwFlags
& MHDR_PREPARED
))
654 return MMSYSERR_NOERROR
;
655 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
)
656 return MIDIERR_STILLPLAYING
;
658 lpMidiHdr
->dwFlags
&= ~MHDR_PREPARED
;
660 return MMSYSERR_NOERROR
;
663 /**************************************************************************
664 * midReset [internal]
666 static DWORD
midReset(WORD wDevID
)
668 DWORD dwTime
= GetTickCount();
670 TRACE("(%04X);\n", wDevID
);
672 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
673 if (MidiInDev
[wDevID
].state
== -1) return MIDIERR_NODEVICE
;
675 EnterCriticalSection(&crit_sect
);
676 while (MidiInDev
[wDevID
].lpQueueHdr
) {
677 LPMIDIHDR lpMidiHdr
= MidiInDev
[wDevID
].lpQueueHdr
;
678 MidiInDev
[wDevID
].lpQueueHdr
= lpMidiHdr
->lpNext
;
679 lpMidiHdr
->dwFlags
&= ~MHDR_INQUEUE
;
680 lpMidiHdr
->dwFlags
|= MHDR_DONE
;
681 MIDI_NotifyClient(wDevID
, MIM_LONGDATA
, (DWORD_PTR
)lpMidiHdr
, dwTime
);
683 LeaveCriticalSection(&crit_sect
);
685 return MMSYSERR_NOERROR
;
688 /**************************************************************************
689 * midStart [internal]
691 static DWORD
midStart(WORD wDevID
)
693 TRACE("(%04X);\n", wDevID
);
695 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
696 if (MidiInDev
[wDevID
].state
== -1) return MIDIERR_NODEVICE
;
698 MidiInDev
[wDevID
].state
= 1;
699 MidiInDev
[wDevID
].startTime
= GetTickCount();
700 return MMSYSERR_NOERROR
;
703 /**************************************************************************
706 static DWORD
midStop(WORD wDevID
)
708 TRACE("(%04X);\n", wDevID
);
710 if (wDevID
>= MIDM_NumDevs
) return MMSYSERR_BADDEVICEID
;
711 if (MidiInDev
[wDevID
].state
== -1) return MIDIERR_NODEVICE
;
713 MidiInDev
[wDevID
].state
= 0;
714 return MMSYSERR_NOERROR
;
717 /**************************************************************************
718 * modGetDevCaps [internal]
720 static DWORD
modGetDevCaps(WORD wDevID
, LPMIDIOUTCAPSW lpCaps
, DWORD dwSize
)
722 TRACE("(%04X, %p, %08X);\n", wDevID
, lpCaps
, dwSize
);
724 if (wDevID
>= MODM_NumDevs
) return MMSYSERR_BADDEVICEID
;
725 if (lpCaps
== NULL
) return MMSYSERR_INVALPARAM
;
727 memcpy(lpCaps
, &MidiOutDev
[wDevID
].caps
, min(dwSize
, sizeof(*lpCaps
)));
729 return MMSYSERR_NOERROR
;
732 /**************************************************************************
735 static DWORD
modOpen(WORD wDevID
, LPMIDIOPENDESC lpDesc
, DWORD dwFlags
)
739 TRACE("(%04X, %p, %08X);\n", wDevID
, lpDesc
, dwFlags
);
740 if (lpDesc
== NULL
) {
741 WARN("Invalid Parameter !\n");
742 return MMSYSERR_INVALPARAM
;
744 if (wDevID
>= MODM_NumDevs
) {
745 TRACE("MAX_MIDIOUTDRV reached !\n");
746 return MMSYSERR_BADDEVICEID
;
748 if (MidiOutDev
[wDevID
].midiDesc
.hMidi
!= 0) {
749 WARN("device already open !\n");
750 return MMSYSERR_ALLOCATED
;
752 if (!MidiOutDev
[wDevID
].bEnabled
) {
753 WARN("device disabled !\n");
754 return MIDIERR_NODEVICE
;
756 if ((dwFlags
& ~CALLBACK_TYPEMASK
) != 0) {
757 WARN("bad dwFlags\n");
758 return MMSYSERR_INVALFLAG
;
761 MidiOutDev
[wDevID
].lpExtra
= 0;
763 switch (MidiOutDev
[wDevID
].caps
.wTechnology
) {
767 if (midiOpenSeq(TRUE
) < 0) {
768 return MMSYSERR_ALLOCATED
;
772 WARN("Technology not supported (yet) %d !\n",
773 MidiOutDev
[wDevID
].caps
.wTechnology
);
774 return MMSYSERR_NOTENABLED
;
777 MidiOutDev
[wDevID
].wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
779 MidiOutDev
[wDevID
].lpQueueHdr
= NULL
;
780 MidiOutDev
[wDevID
].dwTotalPlayed
= 0;
781 MidiOutDev
[wDevID
].bufsize
= 0x3FFF;
782 MidiOutDev
[wDevID
].midiDesc
= *lpDesc
;
784 /* Connect our app port to the device port */
785 EnterCriticalSection(&midiSeqLock
);
786 ret
= snd_seq_connect_to(midiSeq
, port_out
, MidiOutDev
[wDevID
].addr
.client
,
787 MidiOutDev
[wDevID
].addr
.port
);
788 LeaveCriticalSection(&midiSeqLock
);
790 return MMSYSERR_NOTENABLED
;
792 TRACE("Output port :%d connected %d:%d\n",port_out
,MidiOutDev
[wDevID
].addr
.client
,MidiOutDev
[wDevID
].addr
.port
);
794 MIDI_NotifyClient(wDevID
, MOM_OPEN
, 0L, 0L);
795 return MMSYSERR_NOERROR
;
799 /**************************************************************************
800 * modClose [internal]
802 static DWORD
modClose(WORD wDevID
)
804 int ret
= MMSYSERR_NOERROR
;
806 TRACE("(%04X);\n", wDevID
);
808 if (MidiOutDev
[wDevID
].midiDesc
.hMidi
== 0) {
809 WARN("device not opened !\n");
810 return MMSYSERR_ERROR
;
812 /* FIXME: should test that no pending buffer is still in the queue for
815 if (midiSeq
== NULL
) {
816 WARN("can't close !\n");
817 return MMSYSERR_ERROR
;
820 switch (MidiOutDev
[wDevID
].caps
.wTechnology
) {
824 EnterCriticalSection(&midiSeqLock
);
825 snd_seq_disconnect_to(midiSeq
, port_out
, MidiOutDev
[wDevID
].addr
.client
, MidiOutDev
[wDevID
].addr
.port
);
826 LeaveCriticalSection(&midiSeqLock
);
830 WARN("Technology not supported (yet) %d !\n",
831 MidiOutDev
[wDevID
].caps
.wTechnology
);
832 return MMSYSERR_NOTENABLED
;
835 HeapFree(GetProcessHeap(), 0, MidiOutDev
[wDevID
].lpExtra
);
836 MidiOutDev
[wDevID
].lpExtra
= 0;
838 MidiOutDev
[wDevID
].bufsize
= 0;
839 MIDI_NotifyClient(wDevID
, MOM_CLOSE
, 0L, 0L);
840 MidiOutDev
[wDevID
].midiDesc
.hMidi
= 0;
844 /**************************************************************************
847 static DWORD
modData(WORD wDevID
, DWORD dwParam
)
849 BYTE evt
= LOBYTE(LOWORD(dwParam
));
850 BYTE d1
= HIBYTE(LOWORD(dwParam
));
851 BYTE d2
= LOBYTE(HIWORD(dwParam
));
853 TRACE("(%04X, %08X);\n", wDevID
, dwParam
);
855 if (wDevID
>= MODM_NumDevs
) return MMSYSERR_BADDEVICEID
;
856 if (!MidiOutDev
[wDevID
].bEnabled
) return MIDIERR_NODEVICE
;
858 if (midiSeq
== NULL
) {
859 WARN("can't play !\n");
860 return MIDIERR_NODEVICE
;
862 switch (MidiOutDev
[wDevID
].caps
.wTechnology
) {
866 int handled
= 1; /* Assume event is handled */
867 snd_seq_event_t event
;
868 snd_seq_ev_clear(&event
);
869 snd_seq_ev_set_direct(&event
);
870 snd_seq_ev_set_source(&event
, port_out
);
871 snd_seq_ev_set_dest(&event
, MidiOutDev
[wDevID
].addr
.client
, MidiOutDev
[wDevID
].addr
.port
);
873 switch (evt
& 0xF0) {
874 case MIDI_CMD_NOTE_OFF
:
875 snd_seq_ev_set_noteoff(&event
, evt
&0x0F, d1
, d2
);
877 case MIDI_CMD_NOTE_ON
:
878 snd_seq_ev_set_noteon(&event
, evt
&0x0F, d1
, d2
);
880 case MIDI_CMD_NOTE_PRESSURE
:
881 snd_seq_ev_set_keypress(&event
, evt
&0x0F, d1
, d2
);
883 case MIDI_CMD_CONTROL
:
884 snd_seq_ev_set_controller(&event
, evt
&0x0F, d1
, d2
);
886 case MIDI_CMD_BENDER
:
887 snd_seq_ev_set_pitchbend(&event
, evt
&0x0F, ((WORD
)d2
<< 7 | (WORD
)d1
) - 0x2000);
889 case MIDI_CMD_PGM_CHANGE
:
890 snd_seq_ev_set_pgmchange(&event
, evt
&0x0F, d1
);
892 case MIDI_CMD_CHANNEL_PRESSURE
:
893 snd_seq_ev_set_chanpress(&event
, evt
&0x0F, d1
);
895 case MIDI_CMD_COMMON_SYSEX
:
896 switch (evt
& 0x0F) {
897 case 0x00: /* System Exclusive, don't do it on modData,
898 * should require modLongData*/
899 case 0x04: /* Undefined. */
900 case 0x05: /* Undefined. */
901 case 0x07: /* End of Exclusive. */
902 case 0x09: /* Undefined. */
903 case 0x0D: /* Undefined. */
906 case 0x06: /* Tune Request */
907 case 0x08: /* Timing Clock. */
908 case 0x0A: /* Start. */
909 case 0x0B: /* Continue */
910 case 0x0C: /* Stop */
911 case 0x0E: /* Active Sensing. */
913 snd_midi_event_t
*midi_event
;
915 snd_midi_event_new(1, &midi_event
);
916 snd_midi_event_init(midi_event
);
917 snd_midi_event_encode_byte(midi_event
, evt
, &event
);
918 snd_midi_event_free(midi_event
);
921 case 0x0F: /* Reset */
922 /* snd_seq_ev_set_sysex(&event, 1, &evt);
923 this other way may be better */
925 BYTE reset_sysex_seq
[] = {MIDI_CMD_COMMON_SYSEX
, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
926 snd_seq_ev_set_sysex(&event
, sizeof(reset_sysex_seq
), reset_sysex_seq
);
929 case 0x01: /* MTC Quarter frame */
930 case 0x03: /* Song Select. */
935 snd_seq_ev_set_sysex(&event
, sizeof(buf
), buf
);
938 case 0x02: /* Song Position Pointer. */
944 snd_seq_ev_set_sysex(&event
, sizeof(buf
), buf
);
951 EnterCriticalSection(&midiSeqLock
);
952 snd_seq_event_output_direct(midiSeq
, &event
);
953 LeaveCriticalSection(&midiSeqLock
);
958 WARN("Technology not supported (yet) %d !\n",
959 MidiOutDev
[wDevID
].caps
.wTechnology
);
960 return MMSYSERR_NOTENABLED
;
963 return MMSYSERR_NOERROR
;
966 /**************************************************************************
967 * modLongData [internal]
969 static DWORD
modLongData(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
972 BYTE
*lpData
, *lpNewData
= NULL
;
973 snd_seq_event_t event
;
975 TRACE("(%04X, %p, %08X);\n", wDevID
, lpMidiHdr
, dwSize
);
977 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
978 * but it seems to be used only for midi input.
979 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
982 if (wDevID
>= MODM_NumDevs
) return MMSYSERR_BADDEVICEID
;
983 if (!MidiOutDev
[wDevID
].bEnabled
) return MIDIERR_NODEVICE
;
985 if (midiSeq
== NULL
) {
986 WARN("can't play !\n");
987 return MIDIERR_NODEVICE
;
990 lpData
= (BYTE
*)lpMidiHdr
->lpData
;
993 return MIDIERR_UNPREPARED
;
994 if (!(lpMidiHdr
->dwFlags
& MHDR_PREPARED
))
995 return MIDIERR_UNPREPARED
;
996 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
)
997 return MIDIERR_STILLPLAYING
;
998 lpMidiHdr
->dwFlags
&= ~MHDR_DONE
;
999 lpMidiHdr
->dwFlags
|= MHDR_INQUEUE
;
1001 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
1002 * data, or can it also contain raw MIDI data, to be split up and sent to
1004 * If the latest is true, then the following WARNing will fire up
1006 if (lpData
[0] != 0xF0 || lpData
[lpMidiHdr
->dwBufferLength
- 1] != 0xF7) {
1007 WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
1008 lpNewData
= HeapAlloc(GetProcessHeap(), 0, lpMidiHdr
->dwBufferLength
+ 2);
1011 TRACE("dwBufferLength=%u !\n", lpMidiHdr
->dwBufferLength
);
1012 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
1013 lpData
[0], lpData
[1], lpData
[2], lpData
[lpMidiHdr
->dwBufferLength
-3],
1014 lpData
[lpMidiHdr
->dwBufferLength
-2], lpData
[lpMidiHdr
->dwBufferLength
-1]);
1016 switch (MidiOutDev
[wDevID
].caps
.wTechnology
) {
1018 /* FIXME: I don't think there is much to do here */
1019 HeapFree(GetProcessHeap(), 0, lpNewData
);
1022 if (lpData
[0] != 0xF0) {
1023 /* Send start of System Exclusive */
1025 lpNewData
[0] = 0xF0;
1026 memcpy(lpNewData
+ 1, lpData
, lpMidiHdr
->dwBufferLength
);
1027 WARN("Adding missing 0xF0 marker at the beginning of system exclusive byte stream\n");
1029 if (lpData
[lpMidiHdr
->dwBufferLength
-1] != 0xF7) {
1030 /* Send end of System Exclusive */
1032 memcpy(lpNewData
, lpData
, lpMidiHdr
->dwBufferLength
);
1033 lpNewData
[lpMidiHdr
->dwBufferLength
+ len_add
] = 0xF7;
1035 WARN("Adding missing 0xF7 marker at the end of system exclusive byte stream\n");
1037 snd_seq_ev_clear(&event
);
1038 snd_seq_ev_set_direct(&event
);
1039 snd_seq_ev_set_source(&event
, port_out
);
1040 snd_seq_ev_set_dest(&event
, MidiOutDev
[wDevID
].addr
.client
, MidiOutDev
[wDevID
].addr
.port
);
1041 TRACE("destination %d:%d\n", MidiOutDev
[wDevID
].addr
.client
, MidiOutDev
[wDevID
].addr
.port
);
1042 snd_seq_ev_set_sysex(&event
, lpMidiHdr
->dwBufferLength
+ len_add
, lpNewData
? lpNewData
: lpData
);
1043 EnterCriticalSection(&midiSeqLock
);
1044 snd_seq_event_output_direct(midiSeq
, &event
);
1045 LeaveCriticalSection(&midiSeqLock
);
1046 HeapFree(GetProcessHeap(), 0, lpNewData
);
1049 WARN("Technology not supported (yet) %d !\n",
1050 MidiOutDev
[wDevID
].caps
.wTechnology
);
1051 HeapFree(GetProcessHeap(), 0, lpNewData
);
1052 return MMSYSERR_NOTENABLED
;
1055 lpMidiHdr
->dwFlags
&= ~MHDR_INQUEUE
;
1056 lpMidiHdr
->dwFlags
|= MHDR_DONE
;
1057 MIDI_NotifyClient(wDevID
, MOM_DONE
, (DWORD_PTR
)lpMidiHdr
, 0L);
1058 return MMSYSERR_NOERROR
;
1061 /**************************************************************************
1062 * modPrepare [internal]
1064 static DWORD
modPrepare(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
1066 TRACE("(%04X, %p, %d);\n", wDevID
, lpMidiHdr
, dwSize
);
1068 if (dwSize
< offsetof(MIDIHDR
,dwOffset
) || lpMidiHdr
== 0 || lpMidiHdr
->lpData
== 0)
1069 return MMSYSERR_INVALPARAM
;
1070 if (lpMidiHdr
->dwFlags
& MHDR_PREPARED
)
1071 return MMSYSERR_NOERROR
;
1073 lpMidiHdr
->lpNext
= 0;
1074 lpMidiHdr
->dwFlags
|= MHDR_PREPARED
;
1075 lpMidiHdr
->dwFlags
&= ~(MHDR_DONE
|MHDR_INQUEUE
); /* flags cleared since w2k */
1076 return MMSYSERR_NOERROR
;
1079 /**************************************************************************
1080 * modUnprepare [internal]
1082 static DWORD
modUnprepare(WORD wDevID
, LPMIDIHDR lpMidiHdr
, DWORD dwSize
)
1084 TRACE("(%04X, %p, %d);\n", wDevID
, lpMidiHdr
, dwSize
);
1086 if (dwSize
< offsetof(MIDIHDR
,dwOffset
) || lpMidiHdr
== 0 || lpMidiHdr
->lpData
== 0)
1087 return MMSYSERR_INVALPARAM
;
1088 if (!(lpMidiHdr
->dwFlags
& MHDR_PREPARED
))
1089 return MMSYSERR_NOERROR
;
1090 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
)
1091 return MIDIERR_STILLPLAYING
;
1092 lpMidiHdr
->dwFlags
&= ~MHDR_PREPARED
;
1093 return MMSYSERR_NOERROR
;
1096 /**************************************************************************
1097 * modGetVolume [internal]
1099 static DWORD
modGetVolume(WORD wDevID
, DWORD
* lpdwVolume
)
1101 if (!lpdwVolume
) return MMSYSERR_INVALPARAM
;
1102 if (wDevID
>= MODM_NumDevs
) return MMSYSERR_BADDEVICEID
;
1103 *lpdwVolume
= 0xFFFFFFFF;
1104 return (MidiOutDev
[wDevID
].caps
.dwSupport
& MIDICAPS_VOLUME
) ? 0 : MMSYSERR_NOTSUPPORTED
;
1107 /**************************************************************************
1108 * modReset [internal]
1110 static DWORD
modReset(WORD wDevID
)
1114 TRACE("(%04X);\n", wDevID
);
1116 if (wDevID
>= MODM_NumDevs
) return MMSYSERR_BADDEVICEID
;
1117 if (!MidiOutDev
[wDevID
].bEnabled
) return MIDIERR_NODEVICE
;
1119 /* stop all notes */
1120 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1121 * it's channel dependent...
1123 for (chn
= 0; chn
< 16; chn
++) {
1124 /* turn off every note */
1125 modData(wDevID
, 0x7800 | MIDI_CMD_CONTROL
| chn
);
1126 /* remove sustain on all channels */
1127 modData(wDevID
, (MIDI_CTL_SUSTAIN
<< 8) | MIDI_CMD_CONTROL
| chn
);
1129 /* FIXME: the LongData buffers must also be returned to the app */
1130 return MMSYSERR_NOERROR
;
1134 /**************************************************************************
1135 * ALSA_AddMidiPort [internal]
1137 * Helper for ALSA_MidiInit
1139 static void ALSA_AddMidiPort(snd_seq_client_info_t
* cinfo
, snd_seq_port_info_t
* pinfo
, unsigned int cap
, unsigned int type
)
1141 char midiPortName
[MAXPNAMELEN
];
1143 if (cap
& SND_SEQ_PORT_CAP_WRITE
) {
1144 TRACE("OUT (%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 (MODM_NumDevs
>= MAX_MIDIOUTDRV
)
1156 MidiOutDev
[MODM_NumDevs
].addr
= *snd_seq_port_info_get_addr(pinfo
);
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 MidiOutDev
[MODM_NumDevs
].caps
.wMid
= 0x00FF;
1163 MidiOutDev
[MODM_NumDevs
].caps
.wPid
= 0x0001; /* FIXME Product ID */
1164 /* Product Version. We simply say "1" */
1165 MidiOutDev
[MODM_NumDevs
].caps
.vDriverVersion
= 0x001;
1166 /* The following are mandatory for MOD_MIDIPORT */
1167 MidiOutDev
[MODM_NumDevs
].caps
.wChannelMask
= 0xFFFF;
1168 MidiOutDev
[MODM_NumDevs
].caps
.wVoices
= 0;
1169 MidiOutDev
[MODM_NumDevs
].caps
.wNotes
= 0;
1170 MidiOutDev
[MODM_NumDevs
].caps
.dwSupport
= 0;
1172 /* Try to use both client and port names, if this is too long take the port name only.
1173 In the second case the port name should be explicit enough due to its big size.
1175 if ( (strlen(snd_seq_client_info_get_name(cinfo
)) + strlen(snd_seq_port_info_get_name(pinfo
)) + 3) < MAXPNAMELEN
) {
1176 sprintf(midiPortName
, "%s - %s", snd_seq_client_info_get_name(cinfo
), snd_seq_port_info_get_name(pinfo
));
1178 lstrcpynA(midiPortName
, snd_seq_port_info_get_name(pinfo
), MAXPNAMELEN
);
1180 MultiByteToWideChar(CP_UNIXCP
, 0, midiPortName
, -1,
1181 MidiOutDev
[MODM_NumDevs
].caps
.szPname
,
1182 sizeof(MidiOutDev
[MODM_NumDevs
].caps
.szPname
) / sizeof(WCHAR
));
1184 MidiOutDev
[MODM_NumDevs
].caps
.wTechnology
= MIDI_AlsaToWindowsDeviceType(type
);
1186 if (MOD_MIDIPORT
!= MidiOutDev
[MODM_NumDevs
].caps
.wTechnology
) {
1187 /* FIXME Do we have this information?
1188 * Assuming the soundcards can handle
1189 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1190 * not MIDICAPS_CACHE.
1192 MidiOutDev
[MODM_NumDevs
].caps
.dwSupport
= MIDICAPS_VOLUME
|MIDICAPS_LRVOLUME
;
1193 MidiOutDev
[MODM_NumDevs
].caps
.wVoices
= 16;
1195 /* FIXME Is it possible to know the maximum
1196 * number of simultaneous notes of a soundcard ?
1197 * I believe we don't have this information, but
1198 * it's probably equal or more than wVoices
1200 MidiOutDev
[MODM_NumDevs
].caps
.wNotes
= 16;
1202 MidiOutDev
[MODM_NumDevs
].bEnabled
= TRUE
;
1204 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1205 "\tALSA info: midi dev-type=%x, capa=0\n",
1206 MODM_NumDevs
, wine_dbgstr_w(MidiOutDev
[MODM_NumDevs
].caps
.szPname
),
1207 MidiOutDev
[MODM_NumDevs
].caps
.wTechnology
,
1208 MidiOutDev
[MODM_NumDevs
].caps
.wVoices
, MidiOutDev
[MODM_NumDevs
].caps
.wNotes
,
1209 MidiOutDev
[MODM_NumDevs
].caps
.wChannelMask
, MidiOutDev
[MODM_NumDevs
].caps
.dwSupport
,
1214 if (cap
& SND_SEQ_PORT_CAP_READ
) {
1215 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo
),
1216 snd_seq_client_info_get_name(cinfo
),
1217 snd_seq_client_info_get_type(cinfo
) == SND_SEQ_USER_CLIENT
? "user" : "kernel",
1218 snd_seq_port_info_get_port(pinfo
),
1219 snd_seq_port_info_get_name(pinfo
),
1222 if (MIDM_NumDevs
>= MAX_MIDIINDRV
)
1227 MidiInDev
[MIDM_NumDevs
].addr
= *snd_seq_port_info_get_addr(pinfo
);
1229 /* Manufac ID. We do not have access to this with soundcard.h
1230 * Does not seem to be a problem, because in mmsystem.h only
1231 * Microsoft's ID is listed.
1233 MidiInDev
[MIDM_NumDevs
].caps
.wMid
= 0x00FF;
1234 MidiInDev
[MIDM_NumDevs
].caps
.wPid
= 0x0001; /* FIXME Product ID */
1235 /* Product Version. We simply say "1" */
1236 MidiInDev
[MIDM_NumDevs
].caps
.vDriverVersion
= 0x001;
1237 MidiInDev
[MIDM_NumDevs
].caps
.dwSupport
= 0; /* mandatory with MIDIINCAPS */
1239 /* Try to use both client and port names, if this is too long take the port name only.
1240 In the second case the port name should be explicit enough due to its big size.
1242 if ( (strlen(snd_seq_client_info_get_name(cinfo
)) + strlen(snd_seq_port_info_get_name(pinfo
)) + 3) < MAXPNAMELEN
) {
1243 sprintf(midiPortName
, "%s - %s", snd_seq_client_info_get_name(cinfo
), snd_seq_port_info_get_name(pinfo
));
1245 lstrcpynA(midiPortName
, snd_seq_port_info_get_name(pinfo
), MAXPNAMELEN
);
1247 MultiByteToWideChar(CP_UNIXCP
, 0, midiPortName
, -1,
1248 MidiInDev
[MIDM_NumDevs
].caps
.szPname
,
1249 sizeof(MidiInDev
[MIDM_NumDevs
].caps
.szPname
) / sizeof(WCHAR
));
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
: