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
52 #include "wine/debug.h"
54 WINE_DEFAULT_DEBUG_CHANNEL(midi);
58 #ifndef SND_SEQ_PORT_TYPE_PORT
59 #define SND_SEQ_PORT_TYPE_PORT (1<<19) /* Appears in version 1.0.12rc1 */
63 int state; /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
65 MIDIOPENDESC midiDesc;
69 unsigned char incoming[3];
70 unsigned char incPrev;
80 MIDIOPENDESC midiDesc;
84 void* lpExtra; /* according to port type (MIDI, FM...), extra data when needed */
89 static WINE_MIDIIN MidiInDev [MAX_MIDIINDRV ];
90 static WINE_MIDIOUT MidiOutDev[MAX_MIDIOUTDRV];
92 /* this is the total number of MIDI out devices found (synth and port) */
93 static int MODM_NumDevs = 0;
94 /* this is the total number of MIDI out devices found */
95 static int MIDM_NumDevs = 0;
97 static snd_seq_t* midiSeq = NULL;
98 static int numOpenMidiSeq = 0;
99 static int numStartedMidiIn = 0;
104 static CRITICAL_SECTION crit_sect; /* protects all MidiIn buffer queues */
105 static CRITICAL_SECTION_DEBUG critsect_debug =
108 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
109 0, 0, { (DWORD_PTR)(__FILE__ ": crit_sect") }
111 static CRITICAL_SECTION crit_sect = { &critsect_debug, -1, 0, 0, 0, 0 };
113 static int end_thread;
114 static HANDLE hThread;
116 /*======================================================================*
117 * Low level MIDI implementation *
118 *======================================================================*/
120 static int midiOpenSeq(int);
121 static int midiCloseSeq(void);
123 #if 0 /* Debug Purpose */
124 static void error_handler(const char* file, int line, const char* function, int err, const char* fmt, ...)
130 fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function);
131 vfprintf(stderr, fmt, arg);
133 fprintf(stderr, ": %s", snd_strerror(err));
139 /**************************************************************************
140 * MIDI_unixToWindowsDeviceType [internal]
142 * return the Windows equivalent to a Unix Device Type
145 static int MIDI_AlsaToWindowsDeviceType(unsigned int type)
147 /* MOD_MIDIPORT output port
148 * MOD_SYNTH generic internal synth
149 * MOD_SQSYNTH square wave internal synth
150 * MOD_FMSYNTH FM internal synth
151 * MOD_MAPPER MIDI mapper
152 * MOD_WAVETABLE hardware watetable internal synth
153 * MOD_SWSYNTH software internal synth
156 /* FIXME Is this really the correct equivalence from ALSA to
157 Windows Sound type */
159 if (type & SND_SEQ_PORT_TYPE_SYNTH)
162 if (type & (SND_SEQ_PORT_TYPE_DIRECT_SAMPLE|SND_SEQ_PORT_TYPE_SAMPLE))
165 if (type & (SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION))
168 ERR("Cannot determine the type (alsa type is %x) of this midi device. Assuming FM Synth\n", type);
172 /**************************************************************************
173 * MIDI_NotifyClient [internal]
175 static DWORD MIDI_NotifyClient(UINT wDevID, WORD wMsg,
176 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
183 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
184 wDevID, wMsg, dwParam1, dwParam2);
191 if (wDevID > MODM_NumDevs)
192 return MMSYSERR_BADDEVICEID;
194 dwCallBack = MidiOutDev[wDevID].midiDesc.dwCallback;
195 uFlags = MidiOutDev[wDevID].wFlags;
196 hDev = MidiOutDev[wDevID].midiDesc.hMidi;
197 dwInstance = MidiOutDev[wDevID].midiDesc.dwInstance;
207 if (wDevID > MIDM_NumDevs)
208 return MMSYSERR_BADDEVICEID;
210 dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
211 uFlags = MidiInDev[wDevID].wFlags;
212 hDev = MidiInDev[wDevID].midiDesc.hMidi;
213 dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
216 WARN("Unsupported MSW-MIDI message %u\n", wMsg);
217 return MMSYSERR_ERROR;
220 return DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2) ?
224 static int midi_warn = 1;
225 /**************************************************************************
226 * midiOpenSeq [internal]
228 static int midiOpenSeq(int create_client)
230 if (numOpenMidiSeq == 0) {
231 if (snd_seq_open(&midiSeq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0)
235 WARN("Error opening ALSA sequencer.\n");
242 /* Setting the client name is the only init to do */
243 snd_seq_set_client_name(midiSeq, "WINE midi driver");
245 #if 0 /* FIXME: Is it possible to use a port for READ & WRITE ops */
246 port_in = port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input/Output", SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE,
247 SND_SEQ_PORT_TYPE_APPLICATION);
249 TRACE("Unable to create output port\n");
251 TRACE("Outport port created successfully (%d)\n", port_out);
253 port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Output", SND_SEQ_PORT_CAP_READ,
254 SND_SEQ_PORT_TYPE_APPLICATION);
256 TRACE("Unable to create output port\n");
258 TRACE("Outport port created successfully (%d)\n", port_out);
260 port_in = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input", SND_SEQ_PORT_CAP_WRITE,
261 SND_SEQ_PORT_TYPE_APPLICATION);
263 TRACE("Unable to create input port\n");
265 TRACE("Input port created successfully (%d)\n", port_in);
273 /**************************************************************************
274 * midiCloseSeq [internal]
276 static int midiCloseSeq(void)
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);
287 static DWORD WINAPI midRecThread(LPVOID arg)
292 TRACE("Thread startup\n");
295 TRACE("Thread loop\n");
296 npfd = snd_seq_poll_descriptors_count(midiSeq, POLLIN);
297 pfd = HeapAlloc(GetProcessHeap(), 0, npfd * sizeof(struct pollfd));
298 snd_seq_poll_descriptors(midiSeq, pfd, npfd, POLLIN);
300 /* Check if an event is present */
301 if (poll(pfd, npfd, 250) < 0) {
302 HeapFree(GetProcessHeap(), 0, pfd);
306 /* Note: This definitely does not work.
307 * while(snd_seq_event_input_pending(midiSeq, 0) > 0) {
309 snd_seq_event_input(midiSeq, &ev);
311 snd_seq_free_event(ev);
317 snd_seq_event_input(midiSeq, &ev);
318 /* Find the target device */
319 for (wDevID = 0; wDevID < MIDM_NumDevs; wDevID++)
320 if ( (ev->source.client == MidiInDev[wDevID].addr.client) && (ev->source.port == MidiInDev[wDevID].addr.port) )
322 if ((wDevID == MIDM_NumDevs) || (MidiInDev[wDevID].state != 1))
323 FIXME("Unexpected event received, type = %x from %d:%d\n", ev->type, ev->source.client, ev->source.port);
325 DWORD dwTime, toSend = 0;
327 /* FIXME: Should use ev->time instead for better accuracy */
328 dwTime = GetTickCount() - MidiInDev[wDevID].startTime;
329 TRACE("Event received, type = %x, device = %d\n", ev->type, wDevID);
332 case SND_SEQ_EVENT_NOTEOFF:
333 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_OFF | ev->data.control.channel;
335 case SND_SEQ_EVENT_NOTEON:
336 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_ON | ev->data.control.channel;
338 case SND_SEQ_EVENT_KEYPRESS:
339 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_PRESSURE | ev->data.control.channel;
341 case SND_SEQ_EVENT_CONTROLLER:
342 toSend = (ev->data.control.value << 16) | (ev->data.control.param << 8) | MIDI_CMD_CONTROL | ev->data.control.channel;
344 case SND_SEQ_EVENT_PITCHBEND:
345 value = ev->data.control.value + 0x2000;
346 toSend = (((value >> 7) & 0x7f) << 16) | ((value & 0x7f) << 8) | MIDI_CMD_BENDER | ev->data.control.channel;
348 case SND_SEQ_EVENT_PGMCHANGE:
349 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_PGM_CHANGE | ev->data.control.channel;
351 case SND_SEQ_EVENT_CHANPRESS:
352 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_CHANNEL_PRESSURE | ev->data.control.channel;
354 case SND_SEQ_EVENT_CLOCK:
357 case SND_SEQ_EVENT_START:
360 case SND_SEQ_EVENT_CONTINUE:
363 case SND_SEQ_EVENT_STOP:
366 case SND_SEQ_EVENT_SONGPOS:
367 toSend = (((ev->data.control.value >> 7) & 0x7f) << 16) | ((ev->data.control.value & 0x7f) << 8) | 0xF2;
369 case SND_SEQ_EVENT_SONGSEL:
370 toSend = ((ev->data.control.value & 0x7f) << 8) | 0xF3;
372 case SND_SEQ_EVENT_RESET:
375 case SND_SEQ_EVENT_SYSEX:
378 int len = ev->data.ext.len;
379 LPBYTE ptr = ev->data.ext.ptr;
382 EnterCriticalSection(&crit_sect);
384 if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
385 int copylen = min(len, lpMidiHdr->dwBufferLength - lpMidiHdr->dwBytesRecorded);
386 memcpy(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded, ptr + pos, copylen);
387 lpMidiHdr->dwBytesRecorded += copylen;
390 /* We check if we reach the end of buffer or the end of sysex before notifying
391 * to handle the case where ALSA split the sysex into several events */
392 if ((lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) ||
393 (*(BYTE*)(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded - 1) == 0xF7)) {
394 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
395 lpMidiHdr->dwFlags |= MHDR_DONE;
396 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr->lpNext;
397 if (MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime) != MMSYSERR_NOERROR)
398 WARN("Couldn't notify client\n");
401 FIXME("Sysex data received but no buffer to store it!\n");
405 LeaveCriticalSection(&crit_sect);
408 case SND_SEQ_EVENT_SENSING:
412 FIXME("Unhandled event received, type = %x\n", ev->type);
416 TRACE("Sending event %08x (from %d %d)\n", toSend, ev->source.client, ev->source.port);
417 if (MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime) != MMSYSERR_NOERROR) {
418 WARN("Couldn't notify client\n");
422 snd_seq_free_event(ev);
423 } while(snd_seq_event_input_pending(midiSeq, 0) > 0);
425 HeapFree(GetProcessHeap(), 0, pfd);
430 /**************************************************************************
431 * midGetDevCaps [internal]
433 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
435 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
437 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
438 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
440 memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
442 return MMSYSERR_NOERROR;
446 /**************************************************************************
449 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
451 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
453 if (lpDesc == NULL) {
454 WARN("Invalid Parameter !\n");
455 return MMSYSERR_INVALPARAM;
459 * how to check that content of lpDesc is correct ?
461 if (wDevID >= MIDM_NumDevs) {
462 WARN("wDevID too large (%u) !\n", wDevID);
463 return MMSYSERR_BADDEVICEID;
465 if (MidiInDev[wDevID].state == -1) {
466 WARN("device disabled\n");
467 return MIDIERR_NODEVICE;
469 if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
470 WARN("device already open !\n");
471 return MMSYSERR_ALLOCATED;
473 if ((dwFlags & MIDI_IO_STATUS) != 0) {
474 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
475 dwFlags &= ~MIDI_IO_STATUS;
477 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
478 FIXME("Bad dwFlags\n");
479 return MMSYSERR_INVALFLAG;
482 if (midiOpenSeq(1) < 0) {
483 return MMSYSERR_ERROR;
486 /* Connect our app port to the device port */
487 if (snd_seq_connect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port) < 0)
488 return MMSYSERR_NOTENABLED;
490 TRACE("input port connected %d %d %d\n",port_in,MidiInDev[wDevID].addr.client,MidiInDev[wDevID].addr.port);
492 if (numStartedMidiIn++ == 0) {
494 hThread = CreateThread(NULL, 0, midRecThread, NULL, 0, NULL);
496 numStartedMidiIn = 0;
497 WARN("Couldn't create thread for midi-in\n");
499 return MMSYSERR_ERROR;
501 SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
502 TRACE("Created thread for midi-in\n");
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 if (MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
516 WARN("can't notify client !\n");
517 return MMSYSERR_INVALPARAM;
519 return MMSYSERR_NOERROR;
522 /**************************************************************************
523 * midClose [internal]
525 static DWORD midClose(WORD wDevID)
527 int ret = MMSYSERR_NOERROR;
529 TRACE("(%04X);\n", wDevID);
531 if (wDevID >= MIDM_NumDevs) {
532 WARN("wDevID too big (%u) !\n", wDevID);
533 return MMSYSERR_BADDEVICEID;
535 if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
536 WARN("device not opened !\n");
537 return MMSYSERR_ERROR;
539 if (MidiInDev[wDevID].lpQueueHdr != 0) {
540 return MIDIERR_STILLPLAYING;
543 if (midiSeq == NULL) {
545 return MMSYSERR_ERROR;
547 if (--numStartedMidiIn == 0) {
548 TRACE("Stopping thread for midi-in\n");
550 if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
551 WARN("Thread end not signaled, force termination\n");
552 TerminateThread(hThread, 0);
554 TRACE("Stopped thread for midi-in\n");
557 snd_seq_disconnect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port);
560 MidiInDev[wDevID].bufsize = 0;
561 if (MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
562 WARN("can't notify client !\n");
563 ret = MMSYSERR_INVALPARAM;
565 MidiInDev[wDevID].midiDesc.hMidi = 0;
571 /**************************************************************************
572 * midAddBuffer [internal]
574 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
576 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
578 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
579 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
581 if (lpMidiHdr == NULL) return MMSYSERR_INVALPARAM;
582 if (sizeof(MIDIHDR) > dwSize) return MMSYSERR_INVALPARAM;
583 if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
584 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
585 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
587 EnterCriticalSection(&crit_sect);
588 lpMidiHdr->dwFlags &= ~WHDR_DONE;
589 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
590 lpMidiHdr->dwBytesRecorded = 0;
591 lpMidiHdr->lpNext = 0;
592 if (MidiInDev[wDevID].lpQueueHdr == 0) {
593 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
597 for (ptr = MidiInDev[wDevID].lpQueueHdr; ptr->lpNext != 0;
599 ptr->lpNext = lpMidiHdr;
601 LeaveCriticalSection(&crit_sect);
603 return MMSYSERR_NOERROR;
606 /**************************************************************************
607 * midPrepare [internal]
609 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
611 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
613 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
614 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
615 lpMidiHdr->dwBufferLength >= 0x10000ul)
616 return MMSYSERR_INVALPARAM;
618 lpMidiHdr->lpNext = 0;
619 lpMidiHdr->dwFlags |= MHDR_PREPARED;
620 lpMidiHdr->dwBytesRecorded = 0;
622 return MMSYSERR_NOERROR;
625 /**************************************************************************
626 * midUnprepare [internal]
628 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
630 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
632 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
633 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
635 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
636 lpMidiHdr->lpData == 0 || lpMidiHdr->dwBufferLength >= 0x10000ul)
637 return MMSYSERR_INVALPARAM;
639 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
640 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
642 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
644 return MMSYSERR_NOERROR;
647 /**************************************************************************
648 * midReset [internal]
650 static DWORD midReset(WORD wDevID)
652 DWORD dwTime = GetTickCount();
654 TRACE("(%04X);\n", wDevID);
656 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
657 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
659 EnterCriticalSection(&crit_sect);
660 while (MidiInDev[wDevID].lpQueueHdr) {
661 MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
662 MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
663 /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
664 if (MIDI_NotifyClient(wDevID, MIM_LONGDATA,
665 (DWORD_PTR)MidiInDev[wDevID].lpQueueHdr, dwTime) != MMSYSERR_NOERROR) {
666 WARN("Couldn't notify client\n");
668 MidiInDev[wDevID].lpQueueHdr = MidiInDev[wDevID].lpQueueHdr->lpNext;
670 LeaveCriticalSection(&crit_sect);
672 return MMSYSERR_NOERROR;
675 /**************************************************************************
676 * midStart [internal]
678 static DWORD midStart(WORD wDevID)
680 TRACE("(%04X);\n", wDevID);
682 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
683 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
685 MidiInDev[wDevID].state = 1;
686 MidiInDev[wDevID].startTime = GetTickCount();
687 return MMSYSERR_NOERROR;
690 /**************************************************************************
693 static DWORD midStop(WORD wDevID)
695 TRACE("(%04X);\n", wDevID);
697 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
698 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
700 MidiInDev[wDevID].state = 0;
701 return MMSYSERR_NOERROR;
704 /**************************************************************************
705 * modGetDevCaps [internal]
707 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
709 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
711 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
712 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
714 memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
716 return MMSYSERR_NOERROR;
719 /**************************************************************************
722 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
724 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
725 if (lpDesc == NULL) {
726 WARN("Invalid Parameter !\n");
727 return MMSYSERR_INVALPARAM;
729 if (wDevID >= MODM_NumDevs) {
730 TRACE("MAX_MIDIOUTDRV reached !\n");
731 return MMSYSERR_BADDEVICEID;
733 if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
734 WARN("device already open !\n");
735 return MMSYSERR_ALLOCATED;
737 if (!MidiOutDev[wDevID].bEnabled) {
738 WARN("device disabled !\n");
739 return MIDIERR_NODEVICE;
741 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
742 WARN("bad dwFlags\n");
743 return MMSYSERR_INVALFLAG;
745 if (!MidiOutDev[wDevID].bEnabled) {
746 TRACE("disabled wDevID\n");
747 return MMSYSERR_NOTENABLED;
750 MidiOutDev[wDevID].lpExtra = 0;
752 switch (MidiOutDev[wDevID].caps.wTechnology) {
756 if (midiOpenSeq(1) < 0) {
757 return MMSYSERR_ALLOCATED;
761 WARN("Technology not supported (yet) %d !\n",
762 MidiOutDev[wDevID].caps.wTechnology);
763 return MMSYSERR_NOTENABLED;
766 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
768 MidiOutDev[wDevID].lpQueueHdr = NULL;
769 MidiOutDev[wDevID].dwTotalPlayed = 0;
770 MidiOutDev[wDevID].bufsize = 0x3FFF;
771 MidiOutDev[wDevID].midiDesc = *lpDesc;
773 /* Connect our app port to the device port */
774 if (snd_seq_connect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port) < 0)
775 return MMSYSERR_NOTENABLED;
777 if (MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
778 WARN("can't notify client !\n");
779 return MMSYSERR_INVALPARAM;
781 TRACE("Successful !\n");
782 return MMSYSERR_NOERROR;
786 /**************************************************************************
787 * modClose [internal]
789 static DWORD modClose(WORD wDevID)
791 int ret = MMSYSERR_NOERROR;
793 TRACE("(%04X);\n", wDevID);
795 if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
796 WARN("device not opened !\n");
797 return MMSYSERR_ERROR;
799 /* FIXME: should test that no pending buffer is still in the queue for
802 if (midiSeq == NULL) {
803 WARN("can't close !\n");
804 return MMSYSERR_ERROR;
807 switch (MidiOutDev[wDevID].caps.wTechnology) {
811 snd_seq_disconnect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
815 WARN("Technology not supported (yet) %d !\n",
816 MidiOutDev[wDevID].caps.wTechnology);
817 return MMSYSERR_NOTENABLED;
820 HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
821 MidiOutDev[wDevID].lpExtra = 0;
823 MidiOutDev[wDevID].bufsize = 0;
824 if (MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
825 WARN("can't notify client !\n");
826 ret = MMSYSERR_INVALPARAM;
828 MidiOutDev[wDevID].midiDesc.hMidi = 0;
832 /**************************************************************************
835 static DWORD modData(WORD wDevID, DWORD dwParam)
837 BYTE evt = LOBYTE(LOWORD(dwParam));
838 BYTE d1 = HIBYTE(LOWORD(dwParam));
839 BYTE d2 = LOBYTE(HIWORD(dwParam));
841 TRACE("(%04X, %08X);\n", wDevID, dwParam);
843 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
844 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
846 if (midiSeq == NULL) {
847 WARN("can't play !\n");
848 return MIDIERR_NODEVICE;
850 switch (MidiOutDev[wDevID].caps.wTechnology) {
854 int handled = 1; /* Assume event is handled */
855 snd_seq_event_t event;
856 snd_seq_ev_clear(&event);
857 snd_seq_ev_set_direct(&event);
858 snd_seq_ev_set_source(&event, port_out);
859 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
861 switch (evt & 0xF0) {
862 case MIDI_CMD_NOTE_OFF:
863 snd_seq_ev_set_noteoff(&event, evt&0x0F, d1, d2);
865 case MIDI_CMD_NOTE_ON:
866 snd_seq_ev_set_noteon(&event, evt&0x0F, d1, d2);
868 case MIDI_CMD_NOTE_PRESSURE:
869 snd_seq_ev_set_keypress(&event, evt&0x0F, d1, d2);
871 case MIDI_CMD_CONTROL:
872 snd_seq_ev_set_controller(&event, evt&0x0F, d1, d2);
874 case MIDI_CMD_BENDER:
875 snd_seq_ev_set_pitchbend(&event, evt&0x0F, ((WORD)d2 << 7 | (WORD)d1) - 0x2000);
877 case MIDI_CMD_PGM_CHANGE:
878 snd_seq_ev_set_pgmchange(&event, evt&0x0F, d1);
880 case MIDI_CMD_CHANNEL_PRESSURE:
881 snd_seq_ev_set_chanpress(&event, evt&0x0F, d1);
883 case MIDI_CMD_COMMON_SYSEX:
884 switch (evt & 0x0F) {
885 case 0x00: /* System Exclusive, don't do it on modData,
886 * should require modLongData*/
887 case 0x01: /* Undefined */
888 case 0x04: /* Undefined. */
889 case 0x05: /* Undefined. */
890 case 0x07: /* End of Exclusive. */
891 case 0x09: /* Undefined. */
892 case 0x0D: /* Undefined. */
895 case 0x06: /* Tune Request */
896 case 0x08: /* Timing Clock. */
897 case 0x0A: /* Start. */
898 case 0x0B: /* Continue */
899 case 0x0C: /* Stop */
900 case 0x0E: /* Active Sensing. */
901 /* FIXME: Is this function suitable for these purposes
902 (and also Song Select and Song Position Pointer) */
903 snd_seq_ev_set_sysex(&event, 1, &evt);
905 case 0x0F: /* Reset */
906 /* snd_seq_ev_set_sysex(&event, 1, &evt);
907 this other way may be better */
909 BYTE reset_sysex_seq[] = {MIDI_CMD_COMMON_SYSEX, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
910 snd_seq_ev_set_sysex(&event, sizeof(reset_sysex_seq), reset_sysex_seq);
913 case 0x03: /* Song Select. */
918 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
921 case 0x02: /* Song Position Pointer. */
927 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
934 snd_seq_event_output_direct(midiSeq, &event);
938 WARN("Technology not supported (yet) %d !\n",
939 MidiOutDev[wDevID].caps.wTechnology);
940 return MMSYSERR_NOTENABLED;
943 return MMSYSERR_NOERROR;
946 /**************************************************************************
947 * modLongData [internal]
949 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
952 LPBYTE lpData, lpNewData = NULL;
953 snd_seq_event_t event;
955 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
957 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
958 * but it seems to be used only for midi input.
959 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
962 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
963 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
965 if (midiSeq == NULL) {
966 WARN("can't play !\n");
967 return MIDIERR_NODEVICE;
970 lpData = (LPBYTE) lpMidiHdr->lpData;
973 return MIDIERR_UNPREPARED;
974 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
975 return MIDIERR_UNPREPARED;
976 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
977 return MIDIERR_STILLPLAYING;
978 lpMidiHdr->dwFlags &= ~MHDR_DONE;
979 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
981 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
982 * data, or can it also contain raw MIDI data, to be split up and sent to
984 * If the latest is true, then the following WARNing will fire up
986 if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
987 WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
988 lpNewData = HeapAlloc(GetProcessHeap(), 0, lpMidiHdr->dwBufferLength + 2);
991 TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
992 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
993 lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
994 lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
996 switch (MidiOutDev[wDevID].caps.wTechnology) {
998 /* FIXME: I don't think there is much to do here */
1001 if (lpData[0] != 0xF0) {
1002 /* Send start of System Exclusive */
1005 memcpy(lpNewData, lpData, lpMidiHdr->dwBufferLength);
1006 WARN("Adding missing 0xF0 marker at the beginning of "
1007 "system exclusive byte stream\n");
1009 if (lpData[lpMidiHdr->dwBufferLength-1] != 0xF7) {
1010 /* Send end of System Exclusive */
1011 memcpy(lpData + len_add, lpData, lpMidiHdr->dwBufferLength);
1012 lpNewData[lpMidiHdr->dwBufferLength + len_add - 1] = 0xF0;
1014 WARN("Adding missing 0xF7 marker at the end of "
1015 "system exclusive byte stream\n");
1017 snd_seq_ev_clear(&event);
1018 snd_seq_ev_set_direct(&event);
1019 snd_seq_ev_set_source(&event, port_out);
1020 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1021 TRACE("client = %d port = %d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1022 snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData);
1023 snd_seq_event_output_direct(midiSeq, &event);
1025 HeapFree(GetProcessHeap(), 0, lpData);
1028 WARN("Technology not supported (yet) %d !\n",
1029 MidiOutDev[wDevID].caps.wTechnology);
1030 HeapFree(GetProcessHeap(), 0, lpNewData);
1031 return MMSYSERR_NOTENABLED;
1034 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1035 lpMidiHdr->dwFlags |= MHDR_DONE;
1036 if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD_PTR)lpMidiHdr, 0L) != MMSYSERR_NOERROR) {
1037 WARN("can't notify client !\n");
1038 return MMSYSERR_INVALPARAM;
1040 return MMSYSERR_NOERROR;
1043 /**************************************************************************
1044 * modPrepare [internal]
1046 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1048 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1050 if (midiSeq == NULL) {
1051 WARN("can't prepare !\n");
1052 return MMSYSERR_NOTENABLED;
1055 /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
1056 * asks to prepare MIDIHDR which dwFlags != 0.
1057 * So at least check for the inqueue flag
1059 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
1060 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
1061 lpMidiHdr->dwBufferLength >= 0x10000ul) {
1062 WARN("%p %p %08x %d\n", lpMidiHdr, lpMidiHdr ? lpMidiHdr->lpData : NULL,
1063 lpMidiHdr ? lpMidiHdr->dwFlags : 0, dwSize);
1064 return MMSYSERR_INVALPARAM;
1067 lpMidiHdr->lpNext = 0;
1068 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1069 lpMidiHdr->dwFlags &= ~MHDR_DONE;
1070 return MMSYSERR_NOERROR;
1073 /**************************************************************************
1074 * modUnprepare [internal]
1076 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1078 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1080 if (midiSeq == NULL) {
1081 WARN("can't unprepare !\n");
1082 return MMSYSERR_NOTENABLED;
1085 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0)
1086 return MMSYSERR_INVALPARAM;
1087 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1088 return MIDIERR_STILLPLAYING;
1089 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1090 return MMSYSERR_NOERROR;
1093 /**************************************************************************
1094 * modReset [internal]
1096 static DWORD modReset(WORD wDevID)
1100 TRACE("(%04X);\n", wDevID);
1102 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1103 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1105 /* stop all notes */
1106 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1107 * it's channel dependent...
1109 for (chn = 0; chn < 16; chn++) {
1110 /* turn off every note */
1111 modData(wDevID, 0x7800 | MIDI_CMD_CONTROL | chn);
1112 /* remove sustain on all channels */
1113 modData(wDevID, (MIDI_CTL_SUSTAIN << 8) | MIDI_CMD_CONTROL | chn);
1115 /* FIXME: the LongData buffers must also be returned to the app */
1116 return MMSYSERR_NOERROR;
1120 /**************************************************************************
1121 * ALSA_AddMidiPort [internal]
1123 * Helper for ALSA_MidiInit
1125 static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t* pinfo, unsigned int cap, unsigned int type)
1127 char midiPortName[MAXPNAMELEN];
1129 if (cap & SND_SEQ_PORT_CAP_WRITE) {
1130 TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1131 snd_seq_client_info_get_name(cinfo),
1132 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1133 snd_seq_port_info_get_port(pinfo),
1134 snd_seq_port_info_get_name(pinfo),
1137 if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1142 MidiOutDev[MODM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1144 /* Manufac ID. We do not have access to this with soundcard.h
1145 * Does not seem to be a problem, because in mmsystem.h only
1146 * Microsoft's ID is listed.
1148 MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1149 MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1150 /* Product Version. We simply say "1" */
1151 MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1152 MidiOutDev[MODM_NumDevs].caps.wChannelMask = 0xFFFF;
1154 /* FIXME Do we have this information?
1155 * Assuming the soundcards can handle
1156 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1157 * not MIDICAPS_CACHE.
1159 MidiOutDev[MODM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1161 /* Try to use both client and port names, if this is too long take the port name only.
1162 In the second case the port name should be explicit enough due to its big size.
1164 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1165 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1167 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1168 midiPortName[MAXPNAMELEN-1] = 0;
1170 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1171 MidiOutDev[MODM_NumDevs].caps.szPname,
1172 sizeof(MidiOutDev[MODM_NumDevs].caps.szPname) / sizeof(WCHAR));
1174 MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1175 MidiOutDev[MODM_NumDevs].caps.wVoices = 16;
1177 /* FIXME Is it possible to know the maximum
1178 * number of simultaneous notes of a soundcard ?
1179 * I believe we don't have this information, but
1180 * it's probably equal or more than wVoices
1182 MidiOutDev[MODM_NumDevs].caps.wNotes = 16;
1183 MidiOutDev[MODM_NumDevs].bEnabled = TRUE;
1185 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1186 "\tALSA info: midi dev-type=%x, capa=0\n",
1187 MODM_NumDevs, wine_dbgstr_w(MidiOutDev[MODM_NumDevs].caps.szPname),
1188 MidiOutDev[MODM_NumDevs].caps.wTechnology,
1189 MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1190 MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1195 if (cap & SND_SEQ_PORT_CAP_READ) {
1196 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1197 snd_seq_client_info_get_name(cinfo),
1198 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1199 snd_seq_port_info_get_port(pinfo),
1200 snd_seq_port_info_get_name(pinfo),
1203 if (MIDM_NumDevs >= MAX_MIDIINDRV)
1208 MidiInDev[MIDM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1210 /* Manufac ID. We do not have access to this with soundcard.h
1211 * Does not seem to be a problem, because in mmsystem.h only
1212 * Microsoft's ID is listed.
1214 MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1215 MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1216 /* Product Version. We simply say "1" */
1217 MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1219 /* FIXME Do we have this information?
1220 * Assuming the soundcards can handle
1221 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1222 * not MIDICAPS_CACHE.
1224 MidiInDev[MIDM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1226 /* Try to use both client and port names, if this is too long take the port name only.
1227 In the second case the port name should be explicit enough due to its big size.
1229 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1230 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1232 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1233 midiPortName[MAXPNAMELEN-1] = 0;
1235 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1236 MidiInDev[MIDM_NumDevs].caps.szPname,
1237 sizeof(MidiInDev[MIDM_NumDevs].caps.szPname) / sizeof(WCHAR));
1238 MidiInDev[MIDM_NumDevs].state = 0;
1240 TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1241 "\tALSA info: midi dev-type=%x, capa=0\n",
1242 MIDM_NumDevs, wine_dbgstr_w(MidiInDev[MIDM_NumDevs].caps.szPname),
1243 MidiInDev[MIDM_NumDevs].caps.dwSupport,
1250 #endif /* HAVE_ALSA */
1253 /*======================================================================*
1254 * MIDI entry points *
1255 *======================================================================*/
1257 /**************************************************************************
1258 * ALSA_MidiInit [internal]
1260 * Initializes the MIDI devices information variables
1262 LONG ALSA_MidiInit(void)
1265 static BOOL bInitDone = FALSE;
1266 snd_seq_client_info_t *cinfo;
1267 snd_seq_port_info_t *pinfo;
1272 TRACE("Initializing the MIDI variables.\n");
1275 /* try to open device */
1276 if (midiOpenSeq(0) == -1) {
1280 #if 0 /* Debug purpose */
1281 snd_lib_error_set_handler(error_handler);
1283 cinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_client_info_sizeof() );
1284 pinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_port_info_sizeof() );
1286 /* First, search for all internal midi devices */
1287 snd_seq_client_info_set_client(cinfo, -1);
1288 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1289 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1290 snd_seq_port_info_set_port(pinfo, -1);
1291 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1292 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1293 unsigned int type = snd_seq_port_info_get_type(pinfo);
1294 if (!(type & SND_SEQ_PORT_TYPE_PORT))
1295 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1299 /* Second, search for all external ports */
1300 snd_seq_client_info_set_client(cinfo, -1);
1301 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1302 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1303 snd_seq_port_info_set_port(pinfo, -1);
1304 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1305 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1306 unsigned int type = snd_seq_port_info_get_type(pinfo);
1307 if (type & SND_SEQ_PORT_TYPE_PORT)
1308 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1312 /* close file and exit */
1314 HeapFree( GetProcessHeap(), 0, cinfo );
1315 HeapFree( GetProcessHeap(), 0, pinfo );
1322 /**************************************************************************
1323 * midMessage (WINEALSA.@)
1325 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1326 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1328 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1329 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1336 /* FIXME: Pretend this is supported */
1339 return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1341 return midClose(wDevID);
1342 case MIDM_ADDBUFFER:
1343 return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1345 return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1346 case MIDM_UNPREPARE:
1347 return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1348 case MIDM_GETDEVCAPS:
1349 return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1350 case MIDM_GETNUMDEVS:
1351 return MIDM_NumDevs;
1353 return midReset(wDevID);
1355 return midStart(wDevID);
1357 return midStop(wDevID);
1360 TRACE("Unsupported message\n");
1362 return MMSYSERR_NOTSUPPORTED;
1365 /**************************************************************************
1366 * modMessage (WINEALSA.@)
1368 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1369 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1371 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1372 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1380 /* FIXME: Pretend this is supported */
1383 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1385 return modClose(wDevID);
1387 return modData(wDevID, dwParam1);
1389 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1391 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1392 case MODM_UNPREPARE:
1393 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1394 case MODM_GETDEVCAPS:
1395 return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1396 case MODM_GETNUMDEVS:
1397 return MODM_NumDevs;
1398 case MODM_GETVOLUME:
1400 case MODM_SETVOLUME:
1403 return modReset(wDevID);
1406 TRACE("Unsupported message\n");
1408 return MMSYSERR_NOTSUPPORTED;
1411 /*-----------------------------------------------------------------------*/