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 = (BYTE*) 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 splitted 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)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;
599 ptr = (LPMIDIHDR)ptr->lpNext);
600 ptr->lpNext = (struct midihdr_tag*)lpMidiHdr;
602 LeaveCriticalSection(&crit_sect);
604 return MMSYSERR_NOERROR;
607 /**************************************************************************
608 * midPrepare [internal]
610 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
612 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
614 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
615 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
616 lpMidiHdr->dwBufferLength >= 0x10000ul)
617 return MMSYSERR_INVALPARAM;
619 lpMidiHdr->lpNext = 0;
620 lpMidiHdr->dwFlags |= MHDR_PREPARED;
621 lpMidiHdr->dwBytesRecorded = 0;
623 return MMSYSERR_NOERROR;
626 /**************************************************************************
627 * midUnprepare [internal]
629 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
631 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
633 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
634 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
636 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
637 lpMidiHdr->lpData == 0 || lpMidiHdr->dwBufferLength >= 0x10000ul)
638 return MMSYSERR_INVALPARAM;
640 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
641 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
643 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
645 return MMSYSERR_NOERROR;
648 /**************************************************************************
649 * midReset [internal]
651 static DWORD midReset(WORD wDevID)
653 DWORD dwTime = GetTickCount();
655 TRACE("(%04X);\n", wDevID);
657 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
658 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
660 EnterCriticalSection(&crit_sect);
661 while (MidiInDev[wDevID].lpQueueHdr) {
662 MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
663 MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
664 /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
665 if (MIDI_NotifyClient(wDevID, MIM_LONGDATA,
666 (DWORD_PTR)MidiInDev[wDevID].lpQueueHdr, dwTime) != MMSYSERR_NOERROR) {
667 WARN("Couldn't notify client\n");
669 MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)MidiInDev[wDevID].lpQueueHdr->lpNext;
671 LeaveCriticalSection(&crit_sect);
673 return MMSYSERR_NOERROR;
676 /**************************************************************************
677 * midStart [internal]
679 static DWORD midStart(WORD wDevID)
681 TRACE("(%04X);\n", wDevID);
683 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
684 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
686 MidiInDev[wDevID].state = 1;
687 MidiInDev[wDevID].startTime = GetTickCount();
688 return MMSYSERR_NOERROR;
691 /**************************************************************************
694 static DWORD midStop(WORD wDevID)
696 TRACE("(%04X);\n", wDevID);
698 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
699 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
701 MidiInDev[wDevID].state = 0;
702 return MMSYSERR_NOERROR;
705 /**************************************************************************
706 * modGetDevCaps [internal]
708 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
710 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
712 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
713 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
715 memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
717 return MMSYSERR_NOERROR;
720 /**************************************************************************
723 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
725 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
726 if (lpDesc == NULL) {
727 WARN("Invalid Parameter !\n");
728 return MMSYSERR_INVALPARAM;
730 if (wDevID >= MODM_NumDevs) {
731 TRACE("MAX_MIDIOUTDRV reached !\n");
732 return MMSYSERR_BADDEVICEID;
734 if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
735 WARN("device already open !\n");
736 return MMSYSERR_ALLOCATED;
738 if (!MidiOutDev[wDevID].bEnabled) {
739 WARN("device disabled !\n");
740 return MIDIERR_NODEVICE;
742 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
743 WARN("bad dwFlags\n");
744 return MMSYSERR_INVALFLAG;
746 if (!MidiOutDev[wDevID].bEnabled) {
747 TRACE("disabled wDevID\n");
748 return MMSYSERR_NOTENABLED;
751 MidiOutDev[wDevID].lpExtra = 0;
753 switch (MidiOutDev[wDevID].caps.wTechnology) {
757 if (midiOpenSeq(1) < 0) {
758 return MMSYSERR_ALLOCATED;
762 WARN("Technology not supported (yet) %d !\n",
763 MidiOutDev[wDevID].caps.wTechnology);
764 return MMSYSERR_NOTENABLED;
767 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
769 MidiOutDev[wDevID].lpQueueHdr = NULL;
770 MidiOutDev[wDevID].dwTotalPlayed = 0;
771 MidiOutDev[wDevID].bufsize = 0x3FFF;
772 MidiOutDev[wDevID].midiDesc = *lpDesc;
774 /* Connect our app port to the device port */
775 if (snd_seq_connect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port) < 0)
776 return MMSYSERR_NOTENABLED;
778 if (MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
779 WARN("can't notify client !\n");
780 return MMSYSERR_INVALPARAM;
782 TRACE("Successful !\n");
783 return MMSYSERR_NOERROR;
787 /**************************************************************************
788 * modClose [internal]
790 static DWORD modClose(WORD wDevID)
792 int ret = MMSYSERR_NOERROR;
794 TRACE("(%04X);\n", wDevID);
796 if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
797 WARN("device not opened !\n");
798 return MMSYSERR_ERROR;
800 /* FIXME: should test that no pending buffer is still in the queue for
803 if (midiSeq == NULL) {
804 WARN("can't close !\n");
805 return MMSYSERR_ERROR;
808 switch (MidiOutDev[wDevID].caps.wTechnology) {
812 snd_seq_disconnect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
816 WARN("Technology not supported (yet) %d !\n",
817 MidiOutDev[wDevID].caps.wTechnology);
818 return MMSYSERR_NOTENABLED;
821 HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
822 MidiOutDev[wDevID].lpExtra = 0;
824 MidiOutDev[wDevID].bufsize = 0;
825 if (MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
826 WARN("can't notify client !\n");
827 ret = MMSYSERR_INVALPARAM;
829 MidiOutDev[wDevID].midiDesc.hMidi = 0;
833 /**************************************************************************
836 static DWORD modData(WORD wDevID, DWORD dwParam)
838 BYTE evt = LOBYTE(LOWORD(dwParam));
839 BYTE d1 = HIBYTE(LOWORD(dwParam));
840 BYTE d2 = LOBYTE(HIWORD(dwParam));
842 TRACE("(%04X, %08X);\n", wDevID, dwParam);
844 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
845 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
847 if (midiSeq == NULL) {
848 WARN("can't play !\n");
849 return MIDIERR_NODEVICE;
851 switch (MidiOutDev[wDevID].caps.wTechnology) {
855 int handled = 1; /* Assume event is handled */
856 snd_seq_event_t event;
857 snd_seq_ev_clear(&event);
858 snd_seq_ev_set_direct(&event);
859 snd_seq_ev_set_source(&event, port_out);
860 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
862 switch (evt & 0xF0) {
863 case MIDI_CMD_NOTE_OFF:
864 snd_seq_ev_set_noteoff(&event, evt&0x0F, d1, d2);
866 case MIDI_CMD_NOTE_ON:
867 snd_seq_ev_set_noteon(&event, evt&0x0F, d1, d2);
869 case MIDI_CMD_NOTE_PRESSURE:
870 snd_seq_ev_set_keypress(&event, evt&0x0F, d1, d2);
872 case MIDI_CMD_CONTROL:
873 snd_seq_ev_set_controller(&event, evt&0x0F, d1, d2);
875 case MIDI_CMD_BENDER:
876 snd_seq_ev_set_pitchbend(&event, evt&0x0F, ((WORD)d2 << 7 | (WORD)d1) - 0x2000);
878 case MIDI_CMD_PGM_CHANGE:
879 snd_seq_ev_set_pgmchange(&event, evt&0x0F, d1);
881 case MIDI_CMD_CHANNEL_PRESSURE:
882 snd_seq_ev_set_chanpress(&event, evt&0x0F, d1);
884 case MIDI_CMD_COMMON_SYSEX:
885 switch (evt & 0x0F) {
886 case 0x00: /* System Exclusive, don't do it on modData,
887 * should require modLongData*/
888 case 0x01: /* Undefined */
889 case 0x04: /* Undefined. */
890 case 0x05: /* Undefined. */
891 case 0x07: /* End of Exclusive. */
892 case 0x09: /* Undefined. */
893 case 0x0D: /* Undefined. */
896 case 0x06: /* Tune Request */
897 case 0x08: /* Timing Clock. */
898 case 0x0A: /* Start. */
899 case 0x0B: /* Continue */
900 case 0x0C: /* Stop */
901 case 0x0E: /* Active Sensing. */
902 /* FIXME: Is this function suitable for these purposes
903 (and also Song Select and Song Position Pointer) */
904 snd_seq_ev_set_sysex(&event, 1, &evt);
906 case 0x0F: /* Reset */
907 /* snd_seq_ev_set_sysex(&event, 1, &evt);
908 this other way may be better */
910 BYTE reset_sysex_seq[] = {MIDI_CMD_COMMON_SYSEX, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
911 snd_seq_ev_set_sysex(&event, sizeof(reset_sysex_seq), reset_sysex_seq);
914 case 0x03: /* Song Select. */
919 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
922 case 0x02: /* Song Position Pointer. */
928 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
935 snd_seq_event_output_direct(midiSeq, &event);
939 WARN("Technology not supported (yet) %d !\n",
940 MidiOutDev[wDevID].caps.wTechnology);
941 return MMSYSERR_NOTENABLED;
944 return MMSYSERR_NOERROR;
947 /**************************************************************************
948 * modLongData [internal]
950 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
953 LPBYTE lpData, lpNewData = NULL;
954 snd_seq_event_t event;
956 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
958 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
959 * but it seems to be used only for midi input.
960 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
963 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
964 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
966 if (midiSeq == NULL) {
967 WARN("can't play !\n");
968 return MIDIERR_NODEVICE;
971 lpData = (LPBYTE) lpMidiHdr->lpData;
974 return MIDIERR_UNPREPARED;
975 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
976 return MIDIERR_UNPREPARED;
977 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
978 return MIDIERR_STILLPLAYING;
979 lpMidiHdr->dwFlags &= ~MHDR_DONE;
980 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
982 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
983 * data, or can it also contain raw MIDI data, to be split up and sent to
985 * If the latest is true, then the following WARNing will fire up
987 if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
988 WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
989 lpNewData = HeapAlloc(GetProcessHeap(), 0, lpMidiHdr->dwBufferLength + 2);
992 TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
993 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
994 lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
995 lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
997 switch (MidiOutDev[wDevID].caps.wTechnology) {
999 /* FIXME: I don't think there is much to do here */
1002 if (lpData[0] != 0xF0) {
1003 /* Send start of System Exclusive */
1006 memcpy(lpNewData, lpData, lpMidiHdr->dwBufferLength);
1007 WARN("Adding missing 0xF0 marker at the beginning of "
1008 "system exclusive byte stream\n");
1010 if (lpData[lpMidiHdr->dwBufferLength-1] != 0xF7) {
1011 /* Send end of System Exclusive */
1012 memcpy(lpData + len_add, lpData, lpMidiHdr->dwBufferLength);
1013 lpNewData[lpMidiHdr->dwBufferLength + len_add - 1] = 0xF0;
1015 WARN("Adding missing 0xF7 marker at the end of "
1016 "system exclusive byte stream\n");
1018 snd_seq_ev_clear(&event);
1019 snd_seq_ev_set_direct(&event);
1020 snd_seq_ev_set_source(&event, port_out);
1021 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1022 TRACE("client = %d port = %d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1023 snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData);
1024 snd_seq_event_output_direct(midiSeq, &event);
1026 HeapFree(GetProcessHeap(), 0, lpData);
1029 WARN("Technology not supported (yet) %d !\n",
1030 MidiOutDev[wDevID].caps.wTechnology);
1031 HeapFree(GetProcessHeap(), 0, lpNewData);
1032 return MMSYSERR_NOTENABLED;
1035 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1036 lpMidiHdr->dwFlags |= MHDR_DONE;
1037 if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD_PTR)lpMidiHdr, 0L) != MMSYSERR_NOERROR) {
1038 WARN("can't notify client !\n");
1039 return MMSYSERR_INVALPARAM;
1041 return MMSYSERR_NOERROR;
1044 /**************************************************************************
1045 * modPrepare [internal]
1047 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1049 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1051 if (midiSeq == NULL) {
1052 WARN("can't prepare !\n");
1053 return MMSYSERR_NOTENABLED;
1056 /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
1057 * asks to prepare MIDIHDR which dwFlags != 0.
1058 * So at least check for the inqueue flag
1060 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
1061 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
1062 lpMidiHdr->dwBufferLength >= 0x10000ul) {
1063 WARN("%p %p %08x %d\n", lpMidiHdr, lpMidiHdr ? lpMidiHdr->lpData : NULL,
1064 lpMidiHdr ? lpMidiHdr->dwFlags : 0, dwSize);
1065 return MMSYSERR_INVALPARAM;
1068 lpMidiHdr->lpNext = 0;
1069 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1070 lpMidiHdr->dwFlags &= ~MHDR_DONE;
1071 return MMSYSERR_NOERROR;
1074 /**************************************************************************
1075 * modUnprepare [internal]
1077 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1079 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1081 if (midiSeq == NULL) {
1082 WARN("can't unprepare !\n");
1083 return MMSYSERR_NOTENABLED;
1086 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0)
1087 return MMSYSERR_INVALPARAM;
1088 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1089 return MIDIERR_STILLPLAYING;
1090 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1091 return MMSYSERR_NOERROR;
1094 /**************************************************************************
1095 * modReset [internal]
1097 static DWORD modReset(WORD wDevID)
1101 TRACE("(%04X);\n", wDevID);
1103 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1104 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1106 /* stop all notes */
1107 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1108 * it's channel dependent...
1110 for (chn = 0; chn < 16; chn++) {
1111 /* turn off every note */
1112 modData(wDevID, 0x7800 | MIDI_CMD_CONTROL | chn);
1113 /* remove sustain on all channels */
1114 modData(wDevID, (MIDI_CTL_SUSTAIN << 8) | MIDI_CMD_CONTROL | chn);
1116 /* FIXME: the LongData buffers must also be returned to the app */
1117 return MMSYSERR_NOERROR;
1121 /**************************************************************************
1122 * ALSA_AddMidiPort [internal]
1124 * Helper for ALSA_MidiInit
1126 static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t* pinfo, unsigned int cap, unsigned int type)
1128 char midiPortName[MAXPNAMELEN];
1130 if (cap & SND_SEQ_PORT_CAP_WRITE) {
1131 TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1132 snd_seq_client_info_get_name(cinfo),
1133 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1134 snd_seq_port_info_get_port(pinfo),
1135 snd_seq_port_info_get_name(pinfo),
1138 if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1143 MidiOutDev[MODM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1145 /* Manufac ID. We do not have access to this with soundcard.h
1146 * Does not seem to be a problem, because in mmsystem.h only
1147 * Microsoft's ID is listed.
1149 MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1150 MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1151 /* Product Version. We simply say "1" */
1152 MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1153 MidiOutDev[MODM_NumDevs].caps.wChannelMask = 0xFFFF;
1155 /* FIXME Do we have this information?
1156 * Assuming the soundcards can handle
1157 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1158 * not MIDICAPS_CACHE.
1160 MidiOutDev[MODM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1162 /* Try to use both client and port names, if this is too long take the port name only.
1163 In the second case the port name should be explicit enough due to its big size.
1165 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1166 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1168 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1169 midiPortName[MAXPNAMELEN-1] = 0;
1171 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1172 MidiOutDev[MODM_NumDevs].caps.szPname,
1173 sizeof(MidiOutDev[MODM_NumDevs].caps.szPname) / sizeof(WCHAR));
1175 MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1176 MidiOutDev[MODM_NumDevs].caps.wVoices = 16;
1178 /* FIXME Is it possible to know the maximum
1179 * number of simultaneous notes of a soundcard ?
1180 * I believe we don't have this information, but
1181 * it's probably equal or more than wVoices
1183 MidiOutDev[MODM_NumDevs].caps.wNotes = 16;
1184 MidiOutDev[MODM_NumDevs].bEnabled = TRUE;
1186 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1187 "\tALSA info: midi dev-type=%x, capa=0\n",
1188 MODM_NumDevs, wine_dbgstr_w(MidiOutDev[MODM_NumDevs].caps.szPname),
1189 MidiOutDev[MODM_NumDevs].caps.wTechnology,
1190 MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1191 MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1196 if (cap & SND_SEQ_PORT_CAP_READ) {
1197 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1198 snd_seq_client_info_get_name(cinfo),
1199 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1200 snd_seq_port_info_get_port(pinfo),
1201 snd_seq_port_info_get_name(pinfo),
1204 if (MIDM_NumDevs >= MAX_MIDIINDRV)
1209 MidiInDev[MIDM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1211 /* Manufac ID. We do not have access to this with soundcard.h
1212 * Does not seem to be a problem, because in mmsystem.h only
1213 * Microsoft's ID is listed.
1215 MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1216 MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1217 /* Product Version. We simply say "1" */
1218 MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1220 /* FIXME Do we have this information?
1221 * Assuming the soundcards can handle
1222 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1223 * not MIDICAPS_CACHE.
1225 MidiInDev[MIDM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1227 /* Try to use both client and port names, if this is too long take the port name only.
1228 In the second case the port name should be explicit enough due to its big size.
1230 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1231 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1233 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1234 midiPortName[MAXPNAMELEN-1] = 0;
1236 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1237 MidiInDev[MIDM_NumDevs].caps.szPname,
1238 sizeof(MidiInDev[MIDM_NumDevs].caps.szPname) / sizeof(WCHAR));
1239 MidiInDev[MIDM_NumDevs].state = 0;
1241 TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1242 "\tALSA info: midi dev-type=%x, capa=0\n",
1243 MIDM_NumDevs, wine_dbgstr_w(MidiInDev[MIDM_NumDevs].caps.szPname),
1244 MidiInDev[MIDM_NumDevs].caps.dwSupport,
1251 #endif /* HAVE_ALSA */
1254 /*======================================================================*
1255 * MIDI entry points *
1256 *======================================================================*/
1258 /**************************************************************************
1259 * ALSA_MidiInit [internal]
1261 * Initializes the MIDI devices information variables
1263 LONG ALSA_MidiInit(void)
1266 static BOOL bInitDone = FALSE;
1267 snd_seq_client_info_t *cinfo;
1268 snd_seq_port_info_t *pinfo;
1273 TRACE("Initializing the MIDI variables.\n");
1276 /* try to open device */
1277 if (midiOpenSeq(0) == -1) {
1281 #if 0 /* Debug purpose */
1282 snd_lib_error_set_handler(error_handler);
1284 cinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_client_info_sizeof() );
1285 pinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_port_info_sizeof() );
1287 /* First, search for all internal midi devices */
1288 snd_seq_client_info_set_client(cinfo, -1);
1289 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1290 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1291 snd_seq_port_info_set_port(pinfo, -1);
1292 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1293 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1294 unsigned int type = snd_seq_port_info_get_type(pinfo);
1295 if (!(type & SND_SEQ_PORT_TYPE_PORT))
1296 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1300 /* Second, search for all external ports */
1301 snd_seq_client_info_set_client(cinfo, -1);
1302 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1303 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1304 snd_seq_port_info_set_port(pinfo, -1);
1305 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1306 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1307 unsigned int type = snd_seq_port_info_get_type(pinfo);
1308 if (type & SND_SEQ_PORT_TYPE_PORT)
1309 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1313 /* close file and exit */
1315 HeapFree( GetProcessHeap(), 0, cinfo );
1316 HeapFree( GetProcessHeap(), 0, pinfo );
1323 /**************************************************************************
1324 * midMessage (WINEALSA.@)
1326 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1327 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1329 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1330 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1337 /* FIXME: Pretend this is supported */
1340 return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1342 return midClose(wDevID);
1343 case MIDM_ADDBUFFER:
1344 return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1346 return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1347 case MIDM_UNPREPARE:
1348 return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1349 case MIDM_GETDEVCAPS:
1350 return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1351 case MIDM_GETNUMDEVS:
1352 return MIDM_NumDevs;
1354 return midReset(wDevID);
1356 return midStart(wDevID);
1358 return midStop(wDevID);
1361 TRACE("Unsupported message\n");
1363 return MMSYSERR_NOTSUPPORTED;
1366 /**************************************************************************
1367 * modMessage (WINEALSA.@)
1369 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1370 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1372 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1373 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1381 /* FIXME: Pretend this is supported */
1384 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1386 return modClose(wDevID);
1388 return modData(wDevID, dwParam1);
1390 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1392 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1393 case MODM_UNPREPARE:
1394 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1395 case MODM_GETDEVCAPS:
1396 return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1397 case MODM_GETNUMDEVS:
1398 return MODM_NumDevs;
1399 case MODM_GETVOLUME:
1401 case MODM_SETVOLUME:
1404 return modReset(wDevID);
1407 TRACE("Unsupported message\n");
1409 return MMSYSERR_NOTSUPPORTED;
1412 /*-----------------------------------------------------------------------*/