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(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 dwParam1, DWORD dwParam2)
183 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04X dwParam2 = %04X\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:
377 int len = ev->data.ext.len;
378 LPBYTE ptr = (BYTE*) ev->data.ext.ptr;
381 /* FIXME: Should handle sysex greater than lpMidiHdr->dwBufferLength */
382 EnterCriticalSection(&crit_sect);
383 if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
384 if (lpMidiHdr->dwBytesRecorded + len <= lpMidiHdr->dwBufferLength) {
385 memcpy(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded, ptr, len);
386 lpMidiHdr->dwBytesRecorded += len;
387 if (*(ptr + (len-1)) == 0xF7) {
388 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
389 lpMidiHdr->dwFlags |= MHDR_DONE;
390 MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)lpMidiHdr->lpNext;
391 if (MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD)lpMidiHdr, dwTime) != MMSYSERR_NOERROR)
392 WARN("Couldn't notify client\n");
395 FIXME("No enough space in the buffer to store sysex!\n");
397 FIXME("Sysex received but no buffer to store it!\n");
398 LeaveCriticalSection(&crit_sect);
401 case SND_SEQ_EVENT_SENSING:
405 FIXME("Unhandled event received, type = %x\n", ev->type);
409 TRACE("Sending event %08x (from %d %d)\n", toSend, ev->source.client, ev->source.port);
410 if (MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime) != MMSYSERR_NOERROR) {
411 WARN("Couldn't notify client\n");
415 snd_seq_free_event(ev);
416 } while(snd_seq_event_input_pending(midiSeq, 0) > 0);
418 HeapFree(GetProcessHeap(), 0, pfd);
423 /**************************************************************************
424 * midGetDevCaps [internal]
426 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
428 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
430 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
431 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
433 memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
435 return MMSYSERR_NOERROR;
439 /**************************************************************************
442 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
444 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
446 if (lpDesc == NULL) {
447 WARN("Invalid Parameter !\n");
448 return MMSYSERR_INVALPARAM;
452 * how to check that content of lpDesc is correct ?
454 if (wDevID >= MIDM_NumDevs) {
455 WARN("wDevID too large (%u) !\n", wDevID);
456 return MMSYSERR_BADDEVICEID;
458 if (MidiInDev[wDevID].state == -1) {
459 WARN("device disabled\n");
460 return MIDIERR_NODEVICE;
462 if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
463 WARN("device already open !\n");
464 return MMSYSERR_ALLOCATED;
466 if ((dwFlags & MIDI_IO_STATUS) != 0) {
467 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
468 dwFlags &= ~MIDI_IO_STATUS;
470 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
471 FIXME("Bad dwFlags\n");
472 return MMSYSERR_INVALFLAG;
475 if (midiOpenSeq(1) < 0) {
476 return MMSYSERR_ERROR;
479 /* Connect our app port to the device port */
480 if (snd_seq_connect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port) < 0)
481 return MMSYSERR_NOTENABLED;
483 TRACE("input port connected %d %d %d\n",port_in,MidiInDev[wDevID].addr.client,MidiInDev[wDevID].addr.port);
485 if (numStartedMidiIn++ == 0) {
487 hThread = CreateThread(NULL, 0, midRecThread, NULL, 0, NULL);
489 numStartedMidiIn = 0;
490 WARN("Couldn't create thread for midi-in\n");
492 return MMSYSERR_ERROR;
494 SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
495 TRACE("Created thread for midi-in\n");
498 MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
500 MidiInDev[wDevID].lpQueueHdr = NULL;
501 MidiInDev[wDevID].dwTotalPlayed = 0;
502 MidiInDev[wDevID].bufsize = 0x3FFF;
503 MidiInDev[wDevID].midiDesc = *lpDesc;
504 MidiInDev[wDevID].state = 0;
505 MidiInDev[wDevID].incLen = 0;
506 MidiInDev[wDevID].startTime = 0;
508 if (MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
509 WARN("can't notify client !\n");
510 return MMSYSERR_INVALPARAM;
512 return MMSYSERR_NOERROR;
515 /**************************************************************************
516 * midClose [internal]
518 static DWORD midClose(WORD wDevID)
520 int ret = MMSYSERR_NOERROR;
522 TRACE("(%04X);\n", wDevID);
524 if (wDevID >= MIDM_NumDevs) {
525 WARN("wDevID too big (%u) !\n", wDevID);
526 return MMSYSERR_BADDEVICEID;
528 if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
529 WARN("device not opened !\n");
530 return MMSYSERR_ERROR;
532 if (MidiInDev[wDevID].lpQueueHdr != 0) {
533 return MIDIERR_STILLPLAYING;
536 if (midiSeq == NULL) {
538 return MMSYSERR_ERROR;
540 if (--numStartedMidiIn == 0) {
541 TRACE("Stopping thread for midi-in\n");
543 if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
544 WARN("Thread end not signaled, force termination\n");
545 TerminateThread(hThread, 0);
547 TRACE("Stopped thread for midi-in\n");
550 snd_seq_disconnect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port);
553 MidiInDev[wDevID].bufsize = 0;
554 if (MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
555 WARN("can't notify client !\n");
556 ret = MMSYSERR_INVALPARAM;
558 MidiInDev[wDevID].midiDesc.hMidi = 0;
564 /**************************************************************************
565 * midAddBuffer [internal]
567 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
569 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
571 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
572 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
574 if (lpMidiHdr == NULL) return MMSYSERR_INVALPARAM;
575 if (sizeof(MIDIHDR) > dwSize) return MMSYSERR_INVALPARAM;
576 if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
577 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
578 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
580 EnterCriticalSection(&crit_sect);
581 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
582 if (MidiInDev[wDevID].lpQueueHdr == 0) {
583 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
587 for (ptr = MidiInDev[wDevID].lpQueueHdr;
589 ptr = (LPMIDIHDR)ptr->lpNext);
590 ptr->lpNext = (struct midihdr_tag*)lpMidiHdr;
592 LeaveCriticalSection(&crit_sect);
594 return MMSYSERR_NOERROR;
597 /**************************************************************************
598 * midPrepare [internal]
600 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
602 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
604 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
605 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
606 lpMidiHdr->dwBufferLength >= 0x10000ul)
607 return MMSYSERR_INVALPARAM;
609 lpMidiHdr->lpNext = 0;
610 lpMidiHdr->dwFlags |= MHDR_PREPARED;
611 lpMidiHdr->dwBytesRecorded = 0;
613 return MMSYSERR_NOERROR;
616 /**************************************************************************
617 * midUnprepare [internal]
619 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
621 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
623 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
624 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
626 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
627 lpMidiHdr->lpData == 0 || lpMidiHdr->dwBufferLength >= 0x10000ul)
628 return MMSYSERR_INVALPARAM;
630 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
631 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
633 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
635 return MMSYSERR_NOERROR;
638 /**************************************************************************
639 * midReset [internal]
641 static DWORD midReset(WORD wDevID)
643 DWORD dwTime = GetTickCount();
645 TRACE("(%04X);\n", wDevID);
647 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
648 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
650 EnterCriticalSection(&crit_sect);
651 while (MidiInDev[wDevID].lpQueueHdr) {
652 MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
653 MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
654 /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
655 if (MIDI_NotifyClient(wDevID, MIM_LONGDATA,
656 (DWORD)MidiInDev[wDevID].lpQueueHdr, dwTime) != MMSYSERR_NOERROR) {
657 WARN("Couldn't notify client\n");
659 MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)MidiInDev[wDevID].lpQueueHdr->lpNext;
661 LeaveCriticalSection(&crit_sect);
663 return MMSYSERR_NOERROR;
666 /**************************************************************************
667 * midStart [internal]
669 static DWORD midStart(WORD wDevID)
671 TRACE("(%04X);\n", wDevID);
673 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
674 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
676 MidiInDev[wDevID].state = 1;
677 MidiInDev[wDevID].startTime = GetTickCount();
678 return MMSYSERR_NOERROR;
681 /**************************************************************************
684 static DWORD midStop(WORD wDevID)
686 TRACE("(%04X);\n", wDevID);
688 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
689 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
691 MidiInDev[wDevID].state = 0;
692 return MMSYSERR_NOERROR;
695 /**************************************************************************
696 * modGetDevCaps [internal]
698 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
700 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
702 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
703 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
705 memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
707 return MMSYSERR_NOERROR;
710 /**************************************************************************
713 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
715 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
716 if (lpDesc == NULL) {
717 WARN("Invalid Parameter !\n");
718 return MMSYSERR_INVALPARAM;
720 if (wDevID >= MODM_NumDevs) {
721 TRACE("MAX_MIDIOUTDRV reached !\n");
722 return MMSYSERR_BADDEVICEID;
724 if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
725 WARN("device already open !\n");
726 return MMSYSERR_ALLOCATED;
728 if (!MidiOutDev[wDevID].bEnabled) {
729 WARN("device disabled !\n");
730 return MIDIERR_NODEVICE;
732 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
733 WARN("bad dwFlags\n");
734 return MMSYSERR_INVALFLAG;
736 if (!MidiOutDev[wDevID].bEnabled) {
737 TRACE("disabled wDevID\n");
738 return MMSYSERR_NOTENABLED;
741 MidiOutDev[wDevID].lpExtra = 0;
743 switch (MidiOutDev[wDevID].caps.wTechnology) {
747 if (midiOpenSeq(1) < 0) {
748 return MMSYSERR_ALLOCATED;
752 WARN("Technology not supported (yet) %d !\n",
753 MidiOutDev[wDevID].caps.wTechnology);
754 return MMSYSERR_NOTENABLED;
757 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
759 MidiOutDev[wDevID].lpQueueHdr = NULL;
760 MidiOutDev[wDevID].dwTotalPlayed = 0;
761 MidiOutDev[wDevID].bufsize = 0x3FFF;
762 MidiOutDev[wDevID].midiDesc = *lpDesc;
764 /* Connect our app port to the device port */
765 if (snd_seq_connect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port) < 0)
766 return MMSYSERR_NOTENABLED;
768 if (MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
769 WARN("can't notify client !\n");
770 return MMSYSERR_INVALPARAM;
772 TRACE("Successful !\n");
773 return MMSYSERR_NOERROR;
777 /**************************************************************************
778 * modClose [internal]
780 static DWORD modClose(WORD wDevID)
782 int ret = MMSYSERR_NOERROR;
784 TRACE("(%04X);\n", wDevID);
786 if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
787 WARN("device not opened !\n");
788 return MMSYSERR_ERROR;
790 /* FIXME: should test that no pending buffer is still in the queue for
793 if (midiSeq == NULL) {
794 WARN("can't close !\n");
795 return MMSYSERR_ERROR;
798 switch (MidiOutDev[wDevID].caps.wTechnology) {
802 snd_seq_disconnect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
806 WARN("Technology not supported (yet) %d !\n",
807 MidiOutDev[wDevID].caps.wTechnology);
808 return MMSYSERR_NOTENABLED;
811 HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
812 MidiOutDev[wDevID].lpExtra = 0;
814 MidiOutDev[wDevID].bufsize = 0;
815 if (MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
816 WARN("can't notify client !\n");
817 ret = MMSYSERR_INVALPARAM;
819 MidiOutDev[wDevID].midiDesc.hMidi = 0;
823 /**************************************************************************
826 static DWORD modData(WORD wDevID, DWORD dwParam)
828 BYTE evt = LOBYTE(LOWORD(dwParam));
829 BYTE d1 = HIBYTE(LOWORD(dwParam));
830 BYTE d2 = LOBYTE(HIWORD(dwParam));
832 TRACE("(%04X, %08X);\n", wDevID, dwParam);
834 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
835 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
837 if (midiSeq == NULL) {
838 WARN("can't play !\n");
839 return MIDIERR_NODEVICE;
841 switch (MidiOutDev[wDevID].caps.wTechnology) {
845 int handled = 1; /* Assume event is handled */
846 snd_seq_event_t event;
847 snd_seq_ev_clear(&event);
848 snd_seq_ev_set_direct(&event);
849 snd_seq_ev_set_source(&event, port_out);
850 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
852 switch (evt & 0xF0) {
853 case MIDI_CMD_NOTE_OFF:
854 snd_seq_ev_set_noteoff(&event, evt&0x0F, d1, d2);
856 case MIDI_CMD_NOTE_ON:
857 snd_seq_ev_set_noteon(&event, evt&0x0F, d1, d2);
859 case MIDI_CMD_NOTE_PRESSURE:
860 snd_seq_ev_set_keypress(&event, evt&0x0F, d1, d2);
862 case MIDI_CMD_CONTROL:
863 snd_seq_ev_set_controller(&event, evt&0x0F, d1, d2);
865 case MIDI_CMD_BENDER:
866 snd_seq_ev_set_pitchbend(&event, evt&0x0F, ((WORD)d2 << 7 | (WORD)d1) - 0x2000);
868 case MIDI_CMD_PGM_CHANGE:
869 snd_seq_ev_set_pgmchange(&event, evt&0x0F, d1);
871 case MIDI_CMD_CHANNEL_PRESSURE:
872 snd_seq_ev_set_chanpress(&event, evt&0x0F, d1);
874 case MIDI_CMD_COMMON_SYSEX:
875 switch (evt & 0x0F) {
876 case 0x00: /* System Exclusive, don't do it on modData,
877 * should require modLongData*/
878 case 0x01: /* Undefined */
879 case 0x04: /* Undefined. */
880 case 0x05: /* Undefined. */
881 case 0x07: /* End of Exclusive. */
882 case 0x09: /* Undefined. */
883 case 0x0D: /* Undefined. */
886 case 0x06: /* Tune Request */
887 case 0x08: /* Timing Clock. */
888 case 0x0A: /* Start. */
889 case 0x0B: /* Continue */
890 case 0x0C: /* Stop */
891 case 0x0E: /* Active Sensing. */
892 /* FIXME: Is this function suitable for these purposes
893 (and also Song Select and Song Position Pointer) */
894 snd_seq_ev_set_sysex(&event, 1, &evt);
896 case 0x0F: /* Reset */
897 /* snd_seq_ev_set_sysex(&event, 1, &evt);
898 this other way may be better */
900 BYTE reset_sysex_seq[] = {MIDI_CMD_COMMON_SYSEX, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
901 snd_seq_ev_set_sysex(&event, sizeof(reset_sysex_seq), reset_sysex_seq);
904 case 0x03: /* Song Select. */
909 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
912 case 0x02: /* Song Position Pointer. */
918 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
925 snd_seq_event_output_direct(midiSeq, &event);
929 WARN("Technology not supported (yet) %d !\n",
930 MidiOutDev[wDevID].caps.wTechnology);
931 return MMSYSERR_NOTENABLED;
934 return MMSYSERR_NOERROR;
937 /**************************************************************************
938 * modLongData [internal]
940 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
943 LPBYTE lpData, lpNewData = NULL;
944 snd_seq_event_t event;
946 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
948 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
949 * but it seems to be used only for midi input.
950 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
953 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
954 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
956 if (midiSeq == NULL) {
957 WARN("can't play !\n");
958 return MIDIERR_NODEVICE;
961 lpData = (LPBYTE) lpMidiHdr->lpData;
964 return MIDIERR_UNPREPARED;
965 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
966 return MIDIERR_UNPREPARED;
967 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
968 return MIDIERR_STILLPLAYING;
969 lpMidiHdr->dwFlags &= ~MHDR_DONE;
970 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
972 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
973 * data, or can it also contain raw MIDI data, to be split up and sent to
975 * If the latest is true, then the following WARNing will fire up
977 if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
978 WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
979 lpNewData = HeapAlloc(GetProcessHeap(), 0, lpMidiHdr->dwBufferLength + 2);
982 TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
983 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
984 lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
985 lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
987 switch (MidiOutDev[wDevID].caps.wTechnology) {
989 /* FIXME: I don't think there is much to do here */
992 if (lpData[0] != 0xF0) {
993 /* Send start of System Exclusive */
996 memcpy(lpNewData, lpData, lpMidiHdr->dwBufferLength);
997 WARN("Adding missing 0xF0 marker at the beginning of "
998 "system exclusive byte stream\n");
1000 if (lpData[lpMidiHdr->dwBufferLength-1] != 0xF7) {
1001 /* Send end of System Exclusive */
1002 memcpy(lpData + len_add, lpData, lpMidiHdr->dwBufferLength);
1003 lpNewData[lpMidiHdr->dwBufferLength + len_add - 1] = 0xF0;
1005 WARN("Adding missing 0xF7 marker at the end of "
1006 "system exclusive byte stream\n");
1008 snd_seq_ev_clear(&event);
1009 snd_seq_ev_set_direct(&event);
1010 snd_seq_ev_set_source(&event, port_out);
1011 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1012 TRACE("client = %d port = %d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1013 snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData);
1014 snd_seq_event_output_direct(midiSeq, &event);
1016 HeapFree(GetProcessHeap(), 0, lpData);
1019 WARN("Technology not supported (yet) %d !\n",
1020 MidiOutDev[wDevID].caps.wTechnology);
1021 HeapFree(GetProcessHeap(), 0, lpNewData);
1022 return MMSYSERR_NOTENABLED;
1025 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1026 lpMidiHdr->dwFlags |= MHDR_DONE;
1027 if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD)lpMidiHdr, 0L) != MMSYSERR_NOERROR) {
1028 WARN("can't notify client !\n");
1029 return MMSYSERR_INVALPARAM;
1031 return MMSYSERR_NOERROR;
1034 /**************************************************************************
1035 * modPrepare [internal]
1037 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1039 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1041 if (midiSeq == NULL) {
1042 WARN("can't prepare !\n");
1043 return MMSYSERR_NOTENABLED;
1046 /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
1047 * asks to prepare MIDIHDR which dwFlags != 0.
1048 * So at least check for the inqueue flag
1050 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
1051 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
1052 lpMidiHdr->dwBufferLength >= 0x10000ul) {
1053 WARN("%p %p %08x %d/%d\n", lpMidiHdr, lpMidiHdr->lpData,
1054 lpMidiHdr->dwFlags, sizeof(MIDIHDR), dwSize);
1055 return MMSYSERR_INVALPARAM;
1058 lpMidiHdr->lpNext = 0;
1059 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1060 lpMidiHdr->dwFlags &= ~MHDR_DONE;
1061 return MMSYSERR_NOERROR;
1064 /**************************************************************************
1065 * modUnprepare [internal]
1067 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1069 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1071 if (midiSeq == NULL) {
1072 WARN("can't unprepare !\n");
1073 return MMSYSERR_NOTENABLED;
1076 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0)
1077 return MMSYSERR_INVALPARAM;
1078 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1079 return MIDIERR_STILLPLAYING;
1080 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1081 return MMSYSERR_NOERROR;
1084 /**************************************************************************
1085 * modReset [internal]
1087 static DWORD modReset(WORD wDevID)
1091 TRACE("(%04X);\n", wDevID);
1093 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1094 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1096 /* stop all notes */
1097 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1098 * it's channel dependent...
1100 for (chn = 0; chn < 16; chn++) {
1101 /* turn off every note */
1102 modData(wDevID, 0x7800 | MIDI_CMD_CONTROL | chn);
1103 /* remove sustain on all channels */
1104 modData(wDevID, (MIDI_CTL_SUSTAIN << 8) | MIDI_CMD_CONTROL | chn);
1106 /* FIXME: the LongData buffers must also be returned to the app */
1107 return MMSYSERR_NOERROR;
1111 /**************************************************************************
1112 * ALSA_AddMidiPort [internal]
1114 * Helper for ALSA_MidiInit
1116 static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t* pinfo, int cap, int type)
1118 char midiPortName[MAXPNAMELEN];
1120 if (cap & SND_SEQ_PORT_CAP_WRITE) {
1121 TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1122 snd_seq_client_info_get_name(cinfo),
1123 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1124 snd_seq_port_info_get_port(pinfo),
1125 snd_seq_port_info_get_name(pinfo),
1128 if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1133 MidiOutDev[MODM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1135 /* Manufac ID. We do not have access to this with soundcard.h
1136 * Does not seem to be a problem, because in mmsystem.h only
1137 * Microsoft's ID is listed.
1139 MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1140 MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1141 /* Product Version. We simply say "1" */
1142 MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1143 MidiOutDev[MODM_NumDevs].caps.wChannelMask = 0xFFFF;
1145 /* FIXME Do we have this information?
1146 * Assuming the soundcards can handle
1147 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1148 * not MIDICAPS_CACHE.
1150 MidiOutDev[MODM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1152 /* Try to use both client and port names, if this is too long take the port name only.
1153 In the second case the port name should be explicit enough due to its big size.
1155 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1156 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1158 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1159 midiPortName[MAXPNAMELEN-1] = 0;
1161 MultiByteToWideChar(CP_ACP, 0, midiPortName, -1,
1162 MidiOutDev[MODM_NumDevs].caps.szPname,
1163 sizeof(MidiOutDev[MODM_NumDevs].caps.szPname) / sizeof(WCHAR));
1165 MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1166 MidiOutDev[MODM_NumDevs].caps.wVoices = 16;
1168 /* FIXME Is it possible to know the maximum
1169 * number of simultaneous notes of a soundcard ?
1170 * I believe we don't have this information, but
1171 * it's probably equal or more than wVoices
1173 MidiOutDev[MODM_NumDevs].caps.wNotes = 16;
1174 MidiOutDev[MODM_NumDevs].bEnabled = TRUE;
1176 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1177 "\tALSA info: midi dev-type=%lx, capa=%lx\n",
1178 MODM_NumDevs, wine_dbgstr_w(MidiOutDev[MODM_NumDevs].caps.szPname),
1179 MidiOutDev[MODM_NumDevs].caps.wTechnology,
1180 MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1181 MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1182 (long)type, (long)0);
1186 if (cap & SND_SEQ_PORT_CAP_READ) {
1187 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1188 snd_seq_client_info_get_name(cinfo),
1189 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1190 snd_seq_port_info_get_port(pinfo),
1191 snd_seq_port_info_get_name(pinfo),
1194 if (MIDM_NumDevs >= MAX_MIDIINDRV)
1199 MidiInDev[MIDM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1201 /* Manufac ID. We do not have access to this with soundcard.h
1202 * Does not seem to be a problem, because in mmsystem.h only
1203 * Microsoft's ID is listed.
1205 MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1206 MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1207 /* Product Version. We simply say "1" */
1208 MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1210 /* FIXME Do we have this information?
1211 * Assuming the soundcards can handle
1212 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1213 * not MIDICAPS_CACHE.
1215 MidiInDev[MIDM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1217 /* Try to use both client and port names, if this is too long take the port name only.
1218 In the second case the port name should be explicit enough due to its big size.
1220 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1221 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1223 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1224 midiPortName[MAXPNAMELEN-1] = 0;
1226 MultiByteToWideChar(CP_ACP, 0, midiPortName, -1,
1227 MidiInDev[MIDM_NumDevs].caps.szPname,
1228 sizeof(MidiInDev[MIDM_NumDevs].caps.szPname) / sizeof(WCHAR));
1229 MidiInDev[MIDM_NumDevs].state = 0;
1231 TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1232 "\tALSA info: midi dev-type=%lx, capa=%lx\n",
1233 MIDM_NumDevs, wine_dbgstr_w(MidiInDev[MIDM_NumDevs].caps.szPname),
1234 MidiInDev[MIDM_NumDevs].caps.dwSupport,
1235 (long)type, (long)0);
1241 #endif /* HAVE_ALSA */
1244 /*======================================================================*
1245 * MIDI entry points *
1246 *======================================================================*/
1248 /**************************************************************************
1249 * ALSA_MidiInit [internal]
1251 * Initializes the MIDI devices information variables
1253 LONG ALSA_MidiInit(void)
1256 static BOOL bInitDone = FALSE;
1257 snd_seq_client_info_t *cinfo;
1258 snd_seq_port_info_t *pinfo;
1263 TRACE("Initializing the MIDI variables.\n");
1266 /* try to open device */
1267 if (midiOpenSeq(0) == -1) {
1271 #if 0 /* Debug purpose */
1272 snd_lib_error_set_handler(error_handler);
1274 cinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_client_info_sizeof() );
1275 pinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_port_info_sizeof() );
1277 /* First, search for all internal midi devices */
1278 snd_seq_client_info_set_client(cinfo, -1);
1279 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1280 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1281 snd_seq_port_info_set_port(pinfo, -1);
1282 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1283 int cap = snd_seq_port_info_get_capability(pinfo);
1284 int type = snd_seq_port_info_get_type(pinfo);
1285 if (!(type & SND_SEQ_PORT_TYPE_PORT))
1286 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1290 /* Second, search for all external ports */
1291 snd_seq_client_info_set_client(cinfo, -1);
1292 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1293 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1294 snd_seq_port_info_set_port(pinfo, -1);
1295 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1296 int cap = snd_seq_port_info_get_capability(pinfo);
1297 int type = snd_seq_port_info_get_type(pinfo);
1298 if (type & SND_SEQ_PORT_TYPE_PORT)
1299 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1303 /* close file and exit */
1305 HeapFree( GetProcessHeap(), 0, cinfo );
1306 HeapFree( GetProcessHeap(), 0, pinfo );
1313 /**************************************************************************
1314 * midMessage (WINEOSS.4)
1316 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1317 DWORD dwParam1, DWORD dwParam2)
1319 TRACE("(%04X, %04X, %08X, %08X, %08X);\n",
1320 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1327 /* FIXME: Pretend this is supported */
1330 return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1332 return midClose(wDevID);
1333 case MIDM_ADDBUFFER:
1334 return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1336 return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1337 case MIDM_UNPREPARE:
1338 return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1339 case MIDM_GETDEVCAPS:
1340 return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1341 case MIDM_GETNUMDEVS:
1342 return MIDM_NumDevs;
1344 return midReset(wDevID);
1346 return midStart(wDevID);
1348 return midStop(wDevID);
1351 TRACE("Unsupported message\n");
1353 return MMSYSERR_NOTSUPPORTED;
1356 /**************************************************************************
1357 * modMessage (WINEOSS.5)
1359 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1360 DWORD dwParam1, DWORD dwParam2)
1362 TRACE("(%04X, %04X, %08X, %08X, %08X);\n",
1363 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1371 /* FIXME: Pretend this is supported */
1374 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1376 return modClose(wDevID);
1378 return modData(wDevID, dwParam1);
1380 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1382 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1383 case MODM_UNPREPARE:
1384 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1385 case MODM_GETDEVCAPS:
1386 return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1387 case MODM_GETNUMDEVS:
1388 return MODM_NumDevs;
1389 case MODM_GETVOLUME:
1391 case MODM_SETVOLUME:
1394 return modReset(wDevID);
1397 TRACE("Unsupported message\n");
1399 return MMSYSERR_NOTSUPPORTED;
1402 /*-----------------------------------------------------------------------*/