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);
56 #ifndef SND_SEQ_PORT_TYPE_PORT
57 #define SND_SEQ_PORT_TYPE_PORT (1<<19) /* Appears in version 1.0.12rc1 */
61 int state; /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
63 MIDIOPENDESC midiDesc;
67 unsigned char incoming[3];
68 unsigned char incPrev;
78 MIDIOPENDESC midiDesc;
82 void* lpExtra; /* according to port type (MIDI, FM...), extra data when needed */
87 static WINE_MIDIIN MidiInDev [MAX_MIDIINDRV ];
88 static WINE_MIDIOUT MidiOutDev[MAX_MIDIOUTDRV];
90 /* this is the total number of MIDI out devices found (synth and port) */
91 static int MODM_NumDevs = 0;
92 /* this is the total number of MIDI out devices found */
93 static int MIDM_NumDevs = 0;
95 static snd_seq_t* midiSeq = NULL;
96 static int numOpenMidiSeq = 0;
97 static int numStartedMidiIn = 0;
102 static CRITICAL_SECTION crit_sect; /* protects all MidiIn buffer queues */
103 static CRITICAL_SECTION_DEBUG critsect_debug =
106 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
107 0, 0, { (DWORD_PTR)(__FILE__ ": crit_sect") }
109 static CRITICAL_SECTION crit_sect = { &critsect_debug, -1, 0, 0, 0, 0 };
111 static int end_thread;
112 static HANDLE hThread;
114 /*======================================================================*
115 * Low level MIDI implementation *
116 *======================================================================*/
118 static int midiOpenSeq(int);
119 static int midiCloseSeq(void);
121 #if 0 /* Debug Purpose */
122 static void error_handler(const char* file, int line, const char* function, int err, const char* fmt, ...)
128 fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function);
129 vfprintf(stderr, fmt, arg);
131 fprintf(stderr, ": %s", snd_strerror(err));
137 /**************************************************************************
138 * MIDI_unixToWindowsDeviceType [internal]
140 * return the Windows equivalent to a Unix Device Type
143 static int MIDI_AlsaToWindowsDeviceType(unsigned int type)
145 /* MOD_MIDIPORT output port
146 * MOD_SYNTH generic internal synth
147 * MOD_SQSYNTH square wave internal synth
148 * MOD_FMSYNTH FM internal synth
149 * MOD_MAPPER MIDI mapper
150 * MOD_WAVETABLE hardware watetable internal synth
151 * MOD_SWSYNTH software internal synth
154 /* FIXME Is this really the correct equivalence from ALSA to
155 Windows Sound type */
157 if (type & SND_SEQ_PORT_TYPE_SYNTH)
160 if (type & (SND_SEQ_PORT_TYPE_DIRECT_SAMPLE|SND_SEQ_PORT_TYPE_SAMPLE))
163 if (type & (SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION))
166 ERR("Cannot determine the type (alsa type is %x) of this midi device. Assuming FM Synth\n", type);
170 /**************************************************************************
171 * MIDI_NotifyClient [internal]
173 static void MIDI_NotifyClient(UINT wDevID, WORD wMsg,
174 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
181 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
182 wDevID, wMsg, dwParam1, dwParam2);
189 if (wDevID > MODM_NumDevs) return;
191 dwCallBack = MidiOutDev[wDevID].midiDesc.dwCallback;
192 uFlags = MidiOutDev[wDevID].wFlags;
193 hDev = MidiOutDev[wDevID].midiDesc.hMidi;
194 dwInstance = MidiOutDev[wDevID].midiDesc.dwInstance;
204 if (wDevID > MIDM_NumDevs) return;
206 dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
207 uFlags = MidiInDev[wDevID].wFlags;
208 hDev = MidiInDev[wDevID].midiDesc.hMidi;
209 dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
212 ERR("Unsupported MSW-MIDI message %u\n", wMsg);
216 DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2);
219 static int midi_warn = 1;
220 /**************************************************************************
221 * midiOpenSeq [internal]
223 static int midiOpenSeq(int create_client)
225 if (numOpenMidiSeq == 0) {
226 if (snd_seq_open(&midiSeq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0)
230 WARN("Error opening ALSA sequencer.\n");
237 /* Setting the client name is the only init to do */
238 snd_seq_set_client_name(midiSeq, "WINE midi driver");
240 #if 0 /* FIXME: Is it possible to use a port for READ & WRITE ops */
241 port_in = port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input/Output", SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE,
242 SND_SEQ_PORT_TYPE_APPLICATION);
244 TRACE("Unable to create output port\n");
246 TRACE("Outport port created successfully (%d)\n", port_out);
248 port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Output", SND_SEQ_PORT_CAP_READ,
249 SND_SEQ_PORT_TYPE_APPLICATION);
251 TRACE("Unable to create output port\n");
253 TRACE("Outport port created successfully (%d)\n", port_out);
255 port_in = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input", SND_SEQ_PORT_CAP_WRITE,
256 SND_SEQ_PORT_TYPE_APPLICATION);
258 TRACE("Unable to create input port\n");
260 TRACE("Input port created successfully (%d)\n", port_in);
268 /**************************************************************************
269 * midiCloseSeq [internal]
271 static int midiCloseSeq(void)
273 if (--numOpenMidiSeq == 0) {
274 snd_seq_delete_simple_port(midiSeq, port_out);
275 snd_seq_delete_simple_port(midiSeq, port_in);
276 snd_seq_close(midiSeq);
282 static DWORD WINAPI midRecThread(LPVOID arg)
287 TRACE("Thread startup\n");
290 TRACE("Thread loop\n");
291 npfd = snd_seq_poll_descriptors_count(midiSeq, POLLIN);
292 pfd = HeapAlloc(GetProcessHeap(), 0, npfd * sizeof(struct pollfd));
293 snd_seq_poll_descriptors(midiSeq, pfd, npfd, POLLIN);
295 /* Check if an event is present */
296 if (poll(pfd, npfd, 250) < 0) {
297 HeapFree(GetProcessHeap(), 0, pfd);
301 /* Note: This definitely does not work.
302 * while(snd_seq_event_input_pending(midiSeq, 0) > 0) {
304 snd_seq_event_input(midiSeq, &ev);
306 snd_seq_free_event(ev);
312 snd_seq_event_input(midiSeq, &ev);
313 /* Find the target device */
314 for (wDevID = 0; wDevID < MIDM_NumDevs; wDevID++)
315 if ( (ev->source.client == MidiInDev[wDevID].addr.client) && (ev->source.port == MidiInDev[wDevID].addr.port) )
317 if ((wDevID == MIDM_NumDevs) || (MidiInDev[wDevID].state != 1))
318 FIXME("Unexpected event received, type = %x from %d:%d\n", ev->type, ev->source.client, ev->source.port);
320 DWORD dwTime, toSend = 0;
322 /* FIXME: Should use ev->time instead for better accuracy */
323 dwTime = GetTickCount() - MidiInDev[wDevID].startTime;
324 TRACE("Event received, type = %x, device = %d\n", ev->type, wDevID);
327 case SND_SEQ_EVENT_NOTEOFF:
328 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_OFF | ev->data.control.channel;
330 case SND_SEQ_EVENT_NOTEON:
331 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_ON | ev->data.control.channel;
333 case SND_SEQ_EVENT_KEYPRESS:
334 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_PRESSURE | ev->data.control.channel;
336 case SND_SEQ_EVENT_CONTROLLER:
337 toSend = (ev->data.control.value << 16) | (ev->data.control.param << 8) | MIDI_CMD_CONTROL | ev->data.control.channel;
339 case SND_SEQ_EVENT_PITCHBEND:
340 value = ev->data.control.value + 0x2000;
341 toSend = (((value >> 7) & 0x7f) << 16) | ((value & 0x7f) << 8) | MIDI_CMD_BENDER | ev->data.control.channel;
343 case SND_SEQ_EVENT_PGMCHANGE:
344 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_PGM_CHANGE | ev->data.control.channel;
346 case SND_SEQ_EVENT_CHANPRESS:
347 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_CHANNEL_PRESSURE | ev->data.control.channel;
349 case SND_SEQ_EVENT_CLOCK:
352 case SND_SEQ_EVENT_START:
355 case SND_SEQ_EVENT_CONTINUE:
358 case SND_SEQ_EVENT_STOP:
361 case SND_SEQ_EVENT_SONGPOS:
362 toSend = (((ev->data.control.value >> 7) & 0x7f) << 16) | ((ev->data.control.value & 0x7f) << 8) | 0xF2;
364 case SND_SEQ_EVENT_SONGSEL:
365 toSend = ((ev->data.control.value & 0x7f) << 8) | 0xF3;
367 case SND_SEQ_EVENT_RESET:
370 case SND_SEQ_EVENT_SYSEX:
373 int len = ev->data.ext.len;
374 LPBYTE ptr = ev->data.ext.ptr;
377 EnterCriticalSection(&crit_sect);
379 if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
380 int copylen = min(len, lpMidiHdr->dwBufferLength - lpMidiHdr->dwBytesRecorded);
381 memcpy(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded, ptr + pos, copylen);
382 lpMidiHdr->dwBytesRecorded += copylen;
385 /* We check if we reach the end of buffer or the end of sysex before notifying
386 * to handle the case where ALSA split the sysex into several events */
387 if ((lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) ||
388 (*(BYTE*)(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded - 1) == 0xF7)) {
389 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
390 lpMidiHdr->dwFlags |= MHDR_DONE;
391 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr->lpNext;
392 MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime);
395 FIXME("Sysex data received but no buffer to store it!\n");
399 LeaveCriticalSection(&crit_sect);
402 case SND_SEQ_EVENT_SENSING:
406 FIXME("Unhandled event received, type = %x\n", ev->type);
410 TRACE("Sending event %08x (from %d %d)\n", toSend, ev->source.client, ev->source.port);
411 MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime);
414 snd_seq_free_event(ev);
415 } while(snd_seq_event_input_pending(midiSeq, 0) > 0);
417 HeapFree(GetProcessHeap(), 0, pfd);
422 /**************************************************************************
423 * midGetDevCaps [internal]
425 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
427 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
429 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
430 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
432 memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
434 return MMSYSERR_NOERROR;
438 /**************************************************************************
441 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
443 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
445 if (lpDesc == NULL) {
446 WARN("Invalid Parameter !\n");
447 return MMSYSERR_INVALPARAM;
451 * how to check that content of lpDesc is correct ?
453 if (wDevID >= MIDM_NumDevs) {
454 WARN("wDevID too large (%u) !\n", wDevID);
455 return MMSYSERR_BADDEVICEID;
457 if (MidiInDev[wDevID].state == -1) {
458 WARN("device disabled\n");
459 return MIDIERR_NODEVICE;
461 if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
462 WARN("device already open !\n");
463 return MMSYSERR_ALLOCATED;
465 if ((dwFlags & MIDI_IO_STATUS) != 0) {
466 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
467 dwFlags &= ~MIDI_IO_STATUS;
469 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
470 FIXME("Bad dwFlags\n");
471 return MMSYSERR_INVALFLAG;
474 if (midiOpenSeq(1) < 0) {
475 return MMSYSERR_ERROR;
478 /* Connect our app port to the device port */
479 if (snd_seq_connect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port) < 0)
480 return MMSYSERR_NOTENABLED;
482 TRACE("input port connected %d %d %d\n",port_in,MidiInDev[wDevID].addr.client,MidiInDev[wDevID].addr.port);
484 if (numStartedMidiIn++ == 0) {
486 hThread = CreateThread(NULL, 0, midRecThread, NULL, 0, NULL);
488 numStartedMidiIn = 0;
489 WARN("Couldn't create thread for midi-in\n");
491 return MMSYSERR_ERROR;
493 SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
494 TRACE("Created thread for midi-in\n");
497 MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
499 MidiInDev[wDevID].lpQueueHdr = NULL;
500 MidiInDev[wDevID].dwTotalPlayed = 0;
501 MidiInDev[wDevID].bufsize = 0x3FFF;
502 MidiInDev[wDevID].midiDesc = *lpDesc;
503 MidiInDev[wDevID].state = 0;
504 MidiInDev[wDevID].incLen = 0;
505 MidiInDev[wDevID].startTime = 0;
507 MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L);
508 return MMSYSERR_NOERROR;
511 /**************************************************************************
512 * midClose [internal]
514 static DWORD midClose(WORD wDevID)
516 int ret = MMSYSERR_NOERROR;
518 TRACE("(%04X);\n", wDevID);
520 if (wDevID >= MIDM_NumDevs) {
521 WARN("wDevID too big (%u) !\n", wDevID);
522 return MMSYSERR_BADDEVICEID;
524 if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
525 WARN("device not opened !\n");
526 return MMSYSERR_ERROR;
528 if (MidiInDev[wDevID].lpQueueHdr != 0) {
529 return MIDIERR_STILLPLAYING;
532 if (midiSeq == NULL) {
534 return MMSYSERR_ERROR;
536 if (--numStartedMidiIn == 0) {
537 TRACE("Stopping thread for midi-in\n");
539 if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
540 WARN("Thread end not signaled, force termination\n");
541 TerminateThread(hThread, 0);
543 TRACE("Stopped thread for midi-in\n");
546 snd_seq_disconnect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port);
549 MidiInDev[wDevID].bufsize = 0;
550 MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L);
551 MidiInDev[wDevID].midiDesc.hMidi = 0;
557 /**************************************************************************
558 * midAddBuffer [internal]
560 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
562 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
564 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
565 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
567 if (lpMidiHdr == NULL) return MMSYSERR_INVALPARAM;
568 if (dwSize < offsetof(MIDIHDR,dwOffset)) return MMSYSERR_INVALPARAM;
569 if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
570 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
571 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
573 EnterCriticalSection(&crit_sect);
574 lpMidiHdr->dwFlags &= ~WHDR_DONE;
575 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
576 lpMidiHdr->dwBytesRecorded = 0;
577 lpMidiHdr->lpNext = 0;
578 if (MidiInDev[wDevID].lpQueueHdr == 0) {
579 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
583 for (ptr = MidiInDev[wDevID].lpQueueHdr; ptr->lpNext != 0;
585 ptr->lpNext = lpMidiHdr;
587 LeaveCriticalSection(&crit_sect);
589 return MMSYSERR_NOERROR;
592 /**************************************************************************
593 * midPrepare [internal]
595 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
597 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
599 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 ||
600 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0)
601 return MMSYSERR_INVALPARAM;
603 lpMidiHdr->lpNext = 0;
604 lpMidiHdr->dwFlags |= MHDR_PREPARED;
605 lpMidiHdr->dwBytesRecorded = 0;
607 return MMSYSERR_NOERROR;
610 /**************************************************************************
611 * midUnprepare [internal]
613 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
615 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
617 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
618 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
620 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 ||
621 lpMidiHdr->lpData == 0)
622 return MMSYSERR_INVALPARAM;
624 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
625 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
627 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
629 return MMSYSERR_NOERROR;
632 /**************************************************************************
633 * midReset [internal]
635 static DWORD midReset(WORD wDevID)
637 DWORD dwTime = GetTickCount();
639 TRACE("(%04X);\n", wDevID);
641 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
642 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
644 EnterCriticalSection(&crit_sect);
645 while (MidiInDev[wDevID].lpQueueHdr) {
646 MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
647 MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
648 /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
649 MIDI_NotifyClient(wDevID, MIM_LONGDATA,
650 (DWORD_PTR)MidiInDev[wDevID].lpQueueHdr, dwTime);
651 MidiInDev[wDevID].lpQueueHdr = MidiInDev[wDevID].lpQueueHdr->lpNext;
653 LeaveCriticalSection(&crit_sect);
655 return MMSYSERR_NOERROR;
658 /**************************************************************************
659 * midStart [internal]
661 static DWORD midStart(WORD wDevID)
663 TRACE("(%04X);\n", wDevID);
665 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
666 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
668 MidiInDev[wDevID].state = 1;
669 MidiInDev[wDevID].startTime = GetTickCount();
670 return MMSYSERR_NOERROR;
673 /**************************************************************************
676 static DWORD midStop(WORD wDevID)
678 TRACE("(%04X);\n", wDevID);
680 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
681 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
683 MidiInDev[wDevID].state = 0;
684 return MMSYSERR_NOERROR;
687 /**************************************************************************
688 * modGetDevCaps [internal]
690 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
692 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
694 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
695 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
697 memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
699 return MMSYSERR_NOERROR;
702 /**************************************************************************
705 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
707 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
708 if (lpDesc == NULL) {
709 WARN("Invalid Parameter !\n");
710 return MMSYSERR_INVALPARAM;
712 if (wDevID >= MODM_NumDevs) {
713 TRACE("MAX_MIDIOUTDRV reached !\n");
714 return MMSYSERR_BADDEVICEID;
716 if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
717 WARN("device already open !\n");
718 return MMSYSERR_ALLOCATED;
720 if (!MidiOutDev[wDevID].bEnabled) {
721 WARN("device disabled !\n");
722 return MIDIERR_NODEVICE;
724 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
725 WARN("bad dwFlags\n");
726 return MMSYSERR_INVALFLAG;
728 if (!MidiOutDev[wDevID].bEnabled) {
729 TRACE("disabled wDevID\n");
730 return MMSYSERR_NOTENABLED;
733 MidiOutDev[wDevID].lpExtra = 0;
735 switch (MidiOutDev[wDevID].caps.wTechnology) {
739 if (midiOpenSeq(1) < 0) {
740 return MMSYSERR_ALLOCATED;
744 WARN("Technology not supported (yet) %d !\n",
745 MidiOutDev[wDevID].caps.wTechnology);
746 return MMSYSERR_NOTENABLED;
749 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
751 MidiOutDev[wDevID].lpQueueHdr = NULL;
752 MidiOutDev[wDevID].dwTotalPlayed = 0;
753 MidiOutDev[wDevID].bufsize = 0x3FFF;
754 MidiOutDev[wDevID].midiDesc = *lpDesc;
756 /* Connect our app port to the device port */
757 if (snd_seq_connect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port) < 0)
758 return MMSYSERR_NOTENABLED;
760 MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L);
761 TRACE("Successful !\n");
762 return MMSYSERR_NOERROR;
766 /**************************************************************************
767 * modClose [internal]
769 static DWORD modClose(WORD wDevID)
771 int ret = MMSYSERR_NOERROR;
773 TRACE("(%04X);\n", wDevID);
775 if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
776 WARN("device not opened !\n");
777 return MMSYSERR_ERROR;
779 /* FIXME: should test that no pending buffer is still in the queue for
782 if (midiSeq == NULL) {
783 WARN("can't close !\n");
784 return MMSYSERR_ERROR;
787 switch (MidiOutDev[wDevID].caps.wTechnology) {
791 snd_seq_disconnect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
795 WARN("Technology not supported (yet) %d !\n",
796 MidiOutDev[wDevID].caps.wTechnology);
797 return MMSYSERR_NOTENABLED;
800 HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
801 MidiOutDev[wDevID].lpExtra = 0;
803 MidiOutDev[wDevID].bufsize = 0;
804 MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L);
805 MidiOutDev[wDevID].midiDesc.hMidi = 0;
809 /**************************************************************************
812 static DWORD modData(WORD wDevID, DWORD dwParam)
814 BYTE evt = LOBYTE(LOWORD(dwParam));
815 BYTE d1 = HIBYTE(LOWORD(dwParam));
816 BYTE d2 = LOBYTE(HIWORD(dwParam));
818 TRACE("(%04X, %08X);\n", wDevID, dwParam);
820 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
821 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
823 if (midiSeq == NULL) {
824 WARN("can't play !\n");
825 return MIDIERR_NODEVICE;
827 switch (MidiOutDev[wDevID].caps.wTechnology) {
831 int handled = 1; /* Assume event is handled */
832 snd_seq_event_t event;
833 snd_seq_ev_clear(&event);
834 snd_seq_ev_set_direct(&event);
835 snd_seq_ev_set_source(&event, port_out);
836 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
838 switch (evt & 0xF0) {
839 case MIDI_CMD_NOTE_OFF:
840 snd_seq_ev_set_noteoff(&event, evt&0x0F, d1, d2);
842 case MIDI_CMD_NOTE_ON:
843 snd_seq_ev_set_noteon(&event, evt&0x0F, d1, d2);
845 case MIDI_CMD_NOTE_PRESSURE:
846 snd_seq_ev_set_keypress(&event, evt&0x0F, d1, d2);
848 case MIDI_CMD_CONTROL:
849 snd_seq_ev_set_controller(&event, evt&0x0F, d1, d2);
851 case MIDI_CMD_BENDER:
852 snd_seq_ev_set_pitchbend(&event, evt&0x0F, ((WORD)d2 << 7 | (WORD)d1) - 0x2000);
854 case MIDI_CMD_PGM_CHANGE:
855 snd_seq_ev_set_pgmchange(&event, evt&0x0F, d1);
857 case MIDI_CMD_CHANNEL_PRESSURE:
858 snd_seq_ev_set_chanpress(&event, evt&0x0F, d1);
860 case MIDI_CMD_COMMON_SYSEX:
861 switch (evt & 0x0F) {
862 case 0x00: /* System Exclusive, don't do it on modData,
863 * should require modLongData*/
864 case 0x01: /* Undefined */
865 case 0x04: /* Undefined. */
866 case 0x05: /* Undefined. */
867 case 0x07: /* End of Exclusive. */
868 case 0x09: /* Undefined. */
869 case 0x0D: /* Undefined. */
872 case 0x06: /* Tune Request */
873 case 0x08: /* Timing Clock. */
874 case 0x0A: /* Start. */
875 case 0x0B: /* Continue */
876 case 0x0C: /* Stop */
877 case 0x0E: /* Active Sensing. */
878 /* FIXME: Is this function suitable for these purposes
879 (and also Song Select and Song Position Pointer) */
880 snd_seq_ev_set_sysex(&event, 1, &evt);
882 case 0x0F: /* Reset */
883 /* snd_seq_ev_set_sysex(&event, 1, &evt);
884 this other way may be better */
886 BYTE reset_sysex_seq[] = {MIDI_CMD_COMMON_SYSEX, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
887 snd_seq_ev_set_sysex(&event, sizeof(reset_sysex_seq), reset_sysex_seq);
890 case 0x03: /* Song Select. */
895 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
898 case 0x02: /* Song Position Pointer. */
904 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
911 snd_seq_event_output_direct(midiSeq, &event);
915 WARN("Technology not supported (yet) %d !\n",
916 MidiOutDev[wDevID].caps.wTechnology);
917 return MMSYSERR_NOTENABLED;
920 return MMSYSERR_NOERROR;
923 /**************************************************************************
924 * modLongData [internal]
926 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
929 LPBYTE lpData, lpNewData = NULL;
930 snd_seq_event_t event;
932 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
934 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
935 * but it seems to be used only for midi input.
936 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
939 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
940 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
942 if (midiSeq == NULL) {
943 WARN("can't play !\n");
944 return MIDIERR_NODEVICE;
947 lpData = (LPBYTE) lpMidiHdr->lpData;
950 return MIDIERR_UNPREPARED;
951 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
952 return MIDIERR_UNPREPARED;
953 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
954 return MIDIERR_STILLPLAYING;
955 lpMidiHdr->dwFlags &= ~MHDR_DONE;
956 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
958 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
959 * data, or can it also contain raw MIDI data, to be split up and sent to
961 * If the latest is true, then the following WARNing will fire up
963 if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
964 WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
965 lpNewData = HeapAlloc(GetProcessHeap(), 0, lpMidiHdr->dwBufferLength + 2);
968 TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
969 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
970 lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
971 lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
973 switch (MidiOutDev[wDevID].caps.wTechnology) {
975 /* FIXME: I don't think there is much to do here */
978 if (lpData[0] != 0xF0) {
979 /* Send start of System Exclusive */
982 memcpy(lpNewData, lpData, lpMidiHdr->dwBufferLength);
983 WARN("Adding missing 0xF0 marker at the beginning of "
984 "system exclusive byte stream\n");
986 if (lpData[lpMidiHdr->dwBufferLength-1] != 0xF7) {
987 /* Send end of System Exclusive */
988 memcpy(lpData + len_add, lpData, lpMidiHdr->dwBufferLength);
989 lpNewData[lpMidiHdr->dwBufferLength + len_add - 1] = 0xF0;
991 WARN("Adding missing 0xF7 marker at the end of "
992 "system exclusive byte stream\n");
994 snd_seq_ev_clear(&event);
995 snd_seq_ev_set_direct(&event);
996 snd_seq_ev_set_source(&event, port_out);
997 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
998 TRACE("client = %d port = %d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
999 snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData);
1000 snd_seq_event_output_direct(midiSeq, &event);
1002 HeapFree(GetProcessHeap(), 0, lpData);
1005 WARN("Technology not supported (yet) %d !\n",
1006 MidiOutDev[wDevID].caps.wTechnology);
1007 HeapFree(GetProcessHeap(), 0, lpNewData);
1008 return MMSYSERR_NOTENABLED;
1011 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1012 lpMidiHdr->dwFlags |= MHDR_DONE;
1013 MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD_PTR)lpMidiHdr, 0L);
1014 return MMSYSERR_NOERROR;
1017 /**************************************************************************
1018 * modPrepare [internal]
1020 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1022 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1024 if (midiSeq == NULL) {
1025 WARN("can't prepare !\n");
1026 return MMSYSERR_NOTENABLED;
1029 /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
1030 * asks to prepare MIDIHDR which dwFlags != 0.
1031 * So at least check for the inqueue flag
1033 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 ||
1034 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0) {
1035 WARN("%p %p %08x %d\n", lpMidiHdr, lpMidiHdr ? lpMidiHdr->lpData : NULL,
1036 lpMidiHdr ? lpMidiHdr->dwFlags : 0, dwSize);
1037 return MMSYSERR_INVALPARAM;
1040 lpMidiHdr->lpNext = 0;
1041 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1042 lpMidiHdr->dwFlags &= ~MHDR_DONE;
1043 return MMSYSERR_NOERROR;
1046 /**************************************************************************
1047 * modUnprepare [internal]
1049 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1051 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1053 if (midiSeq == NULL) {
1054 WARN("can't unprepare !\n");
1055 return MMSYSERR_NOTENABLED;
1058 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0)
1059 return MMSYSERR_INVALPARAM;
1060 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1061 return MIDIERR_STILLPLAYING;
1062 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1063 return MMSYSERR_NOERROR;
1066 /**************************************************************************
1067 * modGetVolume [internal]
1069 static DWORD modGetVolume(WORD wDevID, DWORD* lpdwVolume)
1071 if (!lpdwVolume) return MMSYSERR_INVALPARAM;
1072 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1073 *lpdwVolume = 0xFFFFFFFF;
1074 return (MidiOutDev[wDevID].caps.dwSupport & MIDICAPS_VOLUME) ? 0 : MMSYSERR_NOTSUPPORTED;
1077 /**************************************************************************
1078 * modReset [internal]
1080 static DWORD modReset(WORD wDevID)
1084 TRACE("(%04X);\n", wDevID);
1086 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1087 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1089 /* stop all notes */
1090 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1091 * it's channel dependent...
1093 for (chn = 0; chn < 16; chn++) {
1094 /* turn off every note */
1095 modData(wDevID, 0x7800 | MIDI_CMD_CONTROL | chn);
1096 /* remove sustain on all channels */
1097 modData(wDevID, (MIDI_CTL_SUSTAIN << 8) | MIDI_CMD_CONTROL | chn);
1099 /* FIXME: the LongData buffers must also be returned to the app */
1100 return MMSYSERR_NOERROR;
1104 /**************************************************************************
1105 * ALSA_AddMidiPort [internal]
1107 * Helper for ALSA_MidiInit
1109 static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t* pinfo, unsigned int cap, unsigned int type)
1111 char midiPortName[MAXPNAMELEN];
1113 if (cap & SND_SEQ_PORT_CAP_WRITE) {
1114 TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1115 snd_seq_client_info_get_name(cinfo),
1116 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1117 snd_seq_port_info_get_port(pinfo),
1118 snd_seq_port_info_get_name(pinfo),
1121 if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1126 MidiOutDev[MODM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1128 /* Manufac ID. We do not have access to this with soundcard.h
1129 * Does not seem to be a problem, because in mmsystem.h only
1130 * Microsoft's ID is listed.
1132 MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1133 MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1134 /* Product Version. We simply say "1" */
1135 MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1136 /* The following are mandatory for MOD_MIDIPORT */
1137 MidiOutDev[MODM_NumDevs].caps.wChannelMask = 0xFFFF;
1138 MidiOutDev[MODM_NumDevs].caps.wVoices = 0;
1139 MidiOutDev[MODM_NumDevs].caps.wNotes = 0;
1140 MidiOutDev[MODM_NumDevs].caps.dwSupport = 0;
1142 /* Try to use both client and port names, if this is too long take the port name only.
1143 In the second case the port name should be explicit enough due to its big size.
1145 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1146 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1148 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN);
1150 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1151 MidiOutDev[MODM_NumDevs].caps.szPname,
1152 sizeof(MidiOutDev[MODM_NumDevs].caps.szPname) / sizeof(WCHAR));
1154 MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1156 if (MOD_MIDIPORT != MidiOutDev[MODM_NumDevs].caps.wTechnology) {
1157 /* FIXME Do we have this information?
1158 * Assuming the soundcards can handle
1159 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1160 * not MIDICAPS_CACHE.
1162 MidiOutDev[MODM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1163 MidiOutDev[MODM_NumDevs].caps.wVoices = 16;
1165 /* FIXME Is it possible to know the maximum
1166 * number of simultaneous notes of a soundcard ?
1167 * I believe we don't have this information, but
1168 * it's probably equal or more than wVoices
1170 MidiOutDev[MODM_NumDevs].caps.wNotes = 16;
1172 MidiOutDev[MODM_NumDevs].bEnabled = TRUE;
1174 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1175 "\tALSA info: midi dev-type=%x, capa=0\n",
1176 MODM_NumDevs, wine_dbgstr_w(MidiOutDev[MODM_NumDevs].caps.szPname),
1177 MidiOutDev[MODM_NumDevs].caps.wTechnology,
1178 MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1179 MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1184 if (cap & SND_SEQ_PORT_CAP_READ) {
1185 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1186 snd_seq_client_info_get_name(cinfo),
1187 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1188 snd_seq_port_info_get_port(pinfo),
1189 snd_seq_port_info_get_name(pinfo),
1192 if (MIDM_NumDevs >= MAX_MIDIINDRV)
1197 MidiInDev[MIDM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1199 /* Manufac ID. We do not have access to this with soundcard.h
1200 * Does not seem to be a problem, because in mmsystem.h only
1201 * Microsoft's ID is listed.
1203 MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1204 MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1205 /* Product Version. We simply say "1" */
1206 MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1207 MidiInDev[MIDM_NumDevs].caps.dwSupport = 0; /* mandatory with MIDIINCAPS */
1209 /* Try to use both client and port names, if this is too long take the port name only.
1210 In the second case the port name should be explicit enough due to its big size.
1212 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1213 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1215 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN);
1217 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1218 MidiInDev[MIDM_NumDevs].caps.szPname,
1219 sizeof(MidiInDev[MIDM_NumDevs].caps.szPname) / sizeof(WCHAR));
1220 MidiInDev[MIDM_NumDevs].state = 0;
1222 TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1223 "\tALSA info: midi dev-type=%x, capa=0\n",
1224 MIDM_NumDevs, wine_dbgstr_w(MidiInDev[MIDM_NumDevs].caps.szPname),
1225 MidiInDev[MIDM_NumDevs].caps.dwSupport,
1233 /*======================================================================*
1234 * MIDI entry points *
1235 *======================================================================*/
1237 /**************************************************************************
1238 * ALSA_MidiInit [internal]
1240 * Initializes the MIDI devices information variables
1242 static LONG ALSA_MidiInit(void)
1244 static BOOL bInitDone = FALSE;
1245 snd_seq_client_info_t *cinfo;
1246 snd_seq_port_info_t *pinfo;
1251 TRACE("Initializing the MIDI variables.\n");
1254 /* try to open device */
1255 if (midiOpenSeq(0) == -1) {
1259 #if 0 /* Debug purpose */
1260 snd_lib_error_set_handler(error_handler);
1262 cinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_client_info_sizeof() );
1263 pinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_port_info_sizeof() );
1265 /* First, search for all internal midi devices */
1266 snd_seq_client_info_set_client(cinfo, -1);
1267 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1268 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1269 snd_seq_port_info_set_port(pinfo, -1);
1270 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1271 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1272 unsigned int type = snd_seq_port_info_get_type(pinfo);
1273 if (!(type & SND_SEQ_PORT_TYPE_PORT))
1274 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1278 /* Second, search for all external ports */
1279 snd_seq_client_info_set_client(cinfo, -1);
1280 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1281 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1282 snd_seq_port_info_set_port(pinfo, -1);
1283 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1284 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1285 unsigned int type = snd_seq_port_info_get_type(pinfo);
1286 if (type & SND_SEQ_PORT_TYPE_PORT)
1287 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1291 /* close file and exit */
1293 HeapFree( GetProcessHeap(), 0, cinfo );
1294 HeapFree( GetProcessHeap(), 0, pinfo );
1300 /**************************************************************************
1301 * midMessage (WINEALSA.@)
1303 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1304 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1306 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1307 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1314 /* FIXME: Pretend this is supported */
1317 return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1319 return midClose(wDevID);
1320 case MIDM_ADDBUFFER:
1321 return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1323 return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1324 case MIDM_UNPREPARE:
1325 return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1326 case MIDM_GETDEVCAPS:
1327 return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1328 case MIDM_GETNUMDEVS:
1329 return MIDM_NumDevs;
1331 return midReset(wDevID);
1333 return midStart(wDevID);
1335 return midStop(wDevID);
1337 TRACE("Unsupported message\n");
1339 return MMSYSERR_NOTSUPPORTED;
1342 /**************************************************************************
1343 * modMessage (WINEALSA.@)
1345 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1346 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1348 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1349 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1357 /* FIXME: Pretend this is supported */
1360 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1362 return modClose(wDevID);
1364 return modData(wDevID, dwParam1);
1366 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1368 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1369 case MODM_UNPREPARE:
1370 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1371 case MODM_GETDEVCAPS:
1372 return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1373 case MODM_GETNUMDEVS:
1374 return MODM_NumDevs;
1375 case MODM_GETVOLUME:
1376 return modGetVolume(wDevID, (DWORD*)dwParam1);
1377 case MODM_SETVOLUME:
1380 return modReset(wDevID);
1382 TRACE("Unsupported message\n");
1384 return MMSYSERR_NOTSUPPORTED;
1387 /*-----------------------------------------------------------------------*/