1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
4 * Sample MIDI Wine Driver for ALSA (basically Linux)
6 * Copyright 1994 Martin Ayotte
7 * Copyright 1998 Luiz Otavio L. Zorzella (init procedures)
8 * Copyright 1998/1999 Eric POUECH :
9 * 98/7 changes for making this MIDI driver work on OSS
10 * current support is limited to MIDI ports of OSS systems
11 * 98/9 rewriting MCI code for MIDI
12 * 98/11 split in midi.c and mcimidi.c
13 * Copyright 2003 Christian Costa :
16 * This library is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU Lesser General Public
18 * License as published by the Free Software Foundation; either
19 * version 2.1 of the License, or (at your option) any later version.
21 * This library is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * Lesser General Public License for more details.
26 * You should have received a copy of the GNU Lesser General Public
27 * License along with this library; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 * TODO: Finish midi record
53 #include "wine/debug.h"
55 #include <alsa/asoundlib.h>
57 WINE_DEFAULT_DEBUG_CHANNEL(midi);
59 #ifndef SND_SEQ_PORT_TYPE_PORT
60 #define SND_SEQ_PORT_TYPE_PORT (1<<19) /* Appears in version 1.0.12rc1 */
64 int state; /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
66 MIDIOPENDESC midiDesc;
70 unsigned char incoming[3];
71 unsigned char incPrev;
81 MIDIOPENDESC midiDesc;
85 void* lpExtra; /* according to port type (MIDI, FM...), extra data when needed */
90 static WINE_MIDIIN MidiInDev [MAX_MIDIINDRV ];
91 static WINE_MIDIOUT MidiOutDev[MAX_MIDIOUTDRV];
93 /* this is the total number of MIDI out devices found (synth and port) */
94 static int MODM_NumDevs = 0;
95 /* this is the total number of MIDI out devices found */
96 static int MIDM_NumDevs = 0;
98 static snd_seq_t* midiSeq = NULL;
99 static int numOpenMidiSeq = 0;
100 static int numStartedMidiIn = 0;
105 static CRITICAL_SECTION crit_sect; /* protects all MidiIn buffer queues */
106 static CRITICAL_SECTION_DEBUG critsect_debug =
109 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
110 0, 0, { (DWORD_PTR)(__FILE__ ": crit_sect") }
112 static CRITICAL_SECTION crit_sect = { &critsect_debug, -1, 0, 0, 0, 0 };
114 static int end_thread;
115 static HANDLE hThread;
117 /*======================================================================*
118 * Low level MIDI implementation *
119 *======================================================================*/
121 static int midiOpenSeq(int);
122 static int midiCloseSeq(void);
124 #if 0 /* Debug Purpose */
125 static void error_handler(const char* file, int line, const char* function, int err, const char* fmt, ...)
131 fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function);
132 vfprintf(stderr, fmt, arg);
134 fprintf(stderr, ": %s", snd_strerror(err));
140 /**************************************************************************
141 * MIDI_unixToWindowsDeviceType [internal]
143 * return the Windows equivalent to a Unix Device Type
146 static int MIDI_AlsaToWindowsDeviceType(unsigned int type)
148 /* MOD_MIDIPORT output port
149 * MOD_SYNTH generic internal synth
150 * MOD_SQSYNTH square wave internal synth
151 * MOD_FMSYNTH FM internal synth
152 * MOD_MAPPER MIDI mapper
153 * MOD_WAVETABLE hardware wavetable internal synth
154 * MOD_SWSYNTH software internal synth
157 /* FIXME Is this really the correct equivalence from ALSA to
158 Windows Sound type? */
160 if (type & SND_SEQ_PORT_TYPE_SYNTH)
163 if (type & (SND_SEQ_PORT_TYPE_DIRECT_SAMPLE|SND_SEQ_PORT_TYPE_SAMPLE))
166 if (type & (SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION))
169 ERR("Cannot determine the type (alsa type is %x) of this midi device. Assuming FM Synth\n", type);
173 /**************************************************************************
174 * MIDI_NotifyClient [internal]
176 static void MIDI_NotifyClient(UINT wDevID, WORD wMsg,
177 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
179 DWORD_PTR dwCallBack;
182 DWORD_PTR dwInstance;
184 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
185 wDevID, wMsg, dwParam1, dwParam2);
192 if (wDevID > MODM_NumDevs) return;
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) return;
209 dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
210 uFlags = MidiInDev[wDevID].wFlags;
211 hDev = MidiInDev[wDevID].midiDesc.hMidi;
212 dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
215 ERR("Unsupported MSW-MIDI message %u\n", wMsg);
219 DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2);
222 static int midi_warn = 1;
223 /**************************************************************************
224 * midiOpenSeq [internal]
226 static int midiOpenSeq(int create_client)
228 if (numOpenMidiSeq == 0) {
229 if (snd_seq_open(&midiSeq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0)
233 WARN("Error opening ALSA sequencer.\n");
240 /* Setting the client name is the only init to do */
241 snd_seq_set_client_name(midiSeq, "WINE midi driver");
243 #if 0 /* FIXME: Is it possible to use a port for READ & WRITE ops */
244 port_in = port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input/Output", SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE,
245 SND_SEQ_PORT_TYPE_APPLICATION);
247 TRACE("Unable to create output port\n");
249 TRACE("Outport port created successfully (%d)\n", port_out);
251 port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Output", SND_SEQ_PORT_CAP_READ,
252 SND_SEQ_PORT_TYPE_APPLICATION);
254 TRACE("Unable to create output port\n");
256 TRACE("Outport port created successfully (%d)\n", port_out);
258 port_in = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input", SND_SEQ_PORT_CAP_WRITE,
259 SND_SEQ_PORT_TYPE_APPLICATION);
261 TRACE("Unable to create input port\n");
263 TRACE("Input port created successfully (%d)\n", port_in);
271 /**************************************************************************
272 * midiCloseSeq [internal]
274 static int midiCloseSeq(void)
276 if (--numOpenMidiSeq == 0) {
277 snd_seq_delete_simple_port(midiSeq, port_out);
278 snd_seq_delete_simple_port(midiSeq, port_in);
279 snd_seq_close(midiSeq);
285 static DWORD WINAPI midRecThread(LPVOID arg)
290 TRACE("Thread startup\n");
293 TRACE("Thread loop\n");
294 npfd = snd_seq_poll_descriptors_count(midiSeq, POLLIN);
295 pfd = HeapAlloc(GetProcessHeap(), 0, npfd * sizeof(struct pollfd));
296 snd_seq_poll_descriptors(midiSeq, pfd, npfd, POLLIN);
298 /* Check if an event is present */
299 if (poll(pfd, npfd, 250) <= 0) {
300 HeapFree(GetProcessHeap(), 0, pfd);
304 /* Note: This definitely does not work.
305 * while(snd_seq_event_input_pending(midiSeq, 0) > 0) {
307 snd_seq_event_input(midiSeq, &ev);
309 snd_seq_free_event(ev);
315 snd_seq_event_input(midiSeq, &ev);
316 /* Find the target device */
317 for (wDevID = 0; wDevID < MIDM_NumDevs; wDevID++)
318 if ( (ev->source.client == MidiInDev[wDevID].addr.client) && (ev->source.port == MidiInDev[wDevID].addr.port) )
320 if ((wDevID == MIDM_NumDevs) || (MidiInDev[wDevID].state != 1))
321 FIXME("Unexpected event received, type = %x from %d:%d\n", ev->type, ev->source.client, ev->source.port);
323 DWORD dwTime, toSend = 0;
325 /* FIXME: Should use ev->time instead for better accuracy */
326 dwTime = GetTickCount() - MidiInDev[wDevID].startTime;
327 TRACE("Event received, type = %x, device = %d\n", ev->type, wDevID);
330 case SND_SEQ_EVENT_NOTEOFF:
331 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_OFF | ev->data.control.channel;
333 case SND_SEQ_EVENT_NOTEON:
334 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_ON | ev->data.control.channel;
336 case SND_SEQ_EVENT_KEYPRESS:
337 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_PRESSURE | ev->data.control.channel;
339 case SND_SEQ_EVENT_CONTROLLER:
340 toSend = (ev->data.control.value << 16) | (ev->data.control.param << 8) | MIDI_CMD_CONTROL | ev->data.control.channel;
342 case SND_SEQ_EVENT_PITCHBEND:
343 value = ev->data.control.value + 0x2000;
344 toSend = (((value >> 7) & 0x7f) << 16) | ((value & 0x7f) << 8) | MIDI_CMD_BENDER | ev->data.control.channel;
346 case SND_SEQ_EVENT_PGMCHANGE:
347 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_PGM_CHANGE | ev->data.control.channel;
349 case SND_SEQ_EVENT_CHANPRESS:
350 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_CHANNEL_PRESSURE | ev->data.control.channel;
352 case SND_SEQ_EVENT_CLOCK:
355 case SND_SEQ_EVENT_START:
358 case SND_SEQ_EVENT_CONTINUE:
361 case SND_SEQ_EVENT_STOP:
364 case SND_SEQ_EVENT_SONGPOS:
365 toSend = (((ev->data.control.value >> 7) & 0x7f) << 16) | ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_SONG_POS;
367 case SND_SEQ_EVENT_SONGSEL:
368 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_SONG_SELECT;
370 case SND_SEQ_EVENT_RESET:
373 case SND_SEQ_EVENT_QFRAME:
374 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_MTC_QUARTER;
376 case SND_SEQ_EVENT_SYSEX:
379 int len = ev->data.ext.len;
380 LPBYTE ptr = ev->data.ext.ptr;
383 EnterCriticalSection(&crit_sect);
385 if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
386 int copylen = min(len, lpMidiHdr->dwBufferLength - lpMidiHdr->dwBytesRecorded);
387 memcpy(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded, ptr + pos, copylen);
388 lpMidiHdr->dwBytesRecorded += copylen;
391 /* We check if we reach the end of buffer or the end of sysex before notifying
392 * to handle the case where ALSA split the sysex into several events */
393 if ((lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) ||
394 (*(BYTE*)(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded - 1) == 0xF7)) {
395 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
396 lpMidiHdr->dwFlags |= MHDR_DONE;
397 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr->lpNext;
398 MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime);
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 MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime);
420 snd_seq_free_event(ev);
421 } while(snd_seq_event_input_pending(midiSeq, 0) > 0);
423 HeapFree(GetProcessHeap(), 0, pfd);
428 /**************************************************************************
429 * midGetDevCaps [internal]
431 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
433 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
435 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
436 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
438 memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
440 return MMSYSERR_NOERROR;
444 /**************************************************************************
447 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
449 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
451 if (lpDesc == NULL) {
452 WARN("Invalid Parameter !\n");
453 return MMSYSERR_INVALPARAM;
457 * how to check that content of lpDesc is correct ?
459 if (wDevID >= MIDM_NumDevs) {
460 WARN("wDevID too large (%u) !\n", wDevID);
461 return MMSYSERR_BADDEVICEID;
463 if (MidiInDev[wDevID].state == -1) {
464 WARN("device disabled\n");
465 return MIDIERR_NODEVICE;
467 if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
468 WARN("device already open !\n");
469 return MMSYSERR_ALLOCATED;
471 if ((dwFlags & MIDI_IO_STATUS) != 0) {
472 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
473 dwFlags &= ~MIDI_IO_STATUS;
475 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
476 FIXME("Bad dwFlags\n");
477 return MMSYSERR_INVALFLAG;
480 if (midiOpenSeq(1) < 0) {
481 return MMSYSERR_ERROR;
484 /* Connect our app port to the device port */
485 if (snd_seq_connect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port) < 0)
486 return MMSYSERR_NOTENABLED;
488 TRACE("input port connected %d %d %d\n",port_in,MidiInDev[wDevID].addr.client,MidiInDev[wDevID].addr.port);
490 if (numStartedMidiIn++ == 0) {
492 hThread = CreateThread(NULL, 0, midRecThread, NULL, 0, NULL);
494 numStartedMidiIn = 0;
495 WARN("Couldn't create thread for midi-in\n");
497 return MMSYSERR_ERROR;
499 SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
500 TRACE("Created thread for midi-in\n");
503 MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
505 MidiInDev[wDevID].lpQueueHdr = NULL;
506 MidiInDev[wDevID].dwTotalPlayed = 0;
507 MidiInDev[wDevID].bufsize = 0x3FFF;
508 MidiInDev[wDevID].midiDesc = *lpDesc;
509 MidiInDev[wDevID].state = 0;
510 MidiInDev[wDevID].incLen = 0;
511 MidiInDev[wDevID].startTime = 0;
513 MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L);
514 return MMSYSERR_NOERROR;
517 /**************************************************************************
518 * midClose [internal]
520 static DWORD midClose(WORD wDevID)
522 int ret = MMSYSERR_NOERROR;
524 TRACE("(%04X);\n", wDevID);
526 if (wDevID >= MIDM_NumDevs) {
527 WARN("wDevID too big (%u) !\n", wDevID);
528 return MMSYSERR_BADDEVICEID;
530 if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
531 WARN("device not opened !\n");
532 return MMSYSERR_ERROR;
534 if (MidiInDev[wDevID].lpQueueHdr != 0) {
535 return MIDIERR_STILLPLAYING;
538 if (midiSeq == NULL) {
540 return MMSYSERR_ERROR;
542 if (--numStartedMidiIn == 0) {
543 TRACE("Stopping thread for midi-in\n");
545 if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
546 WARN("Thread end not signaled, force termination\n");
547 TerminateThread(hThread, 0);
549 TRACE("Stopped thread for midi-in\n");
552 snd_seq_disconnect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port);
555 MidiInDev[wDevID].bufsize = 0;
556 MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L);
557 MidiInDev[wDevID].midiDesc.hMidi = 0;
563 /**************************************************************************
564 * midAddBuffer [internal]
566 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
568 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
570 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
571 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
573 if (lpMidiHdr == NULL) return MMSYSERR_INVALPARAM;
574 if (dwSize < offsetof(MIDIHDR,dwOffset)) return MMSYSERR_INVALPARAM;
575 if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
576 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
577 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
579 EnterCriticalSection(&crit_sect);
580 lpMidiHdr->dwFlags &= ~WHDR_DONE;
581 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
582 lpMidiHdr->dwBytesRecorded = 0;
583 lpMidiHdr->lpNext = 0;
584 if (MidiInDev[wDevID].lpQueueHdr == 0) {
585 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
589 for (ptr = MidiInDev[wDevID].lpQueueHdr; ptr->lpNext != 0;
591 ptr->lpNext = lpMidiHdr;
593 LeaveCriticalSection(&crit_sect);
595 return MMSYSERR_NOERROR;
598 /**************************************************************************
599 * midPrepare [internal]
601 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
603 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
605 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
606 return MMSYSERR_INVALPARAM;
607 if (lpMidiHdr->dwFlags & MHDR_PREPARED)
608 return MMSYSERR_NOERROR;
610 lpMidiHdr->lpNext = 0;
611 lpMidiHdr->dwFlags |= MHDR_PREPARED;
612 lpMidiHdr->dwFlags &= ~(MHDR_DONE|MHDR_INQUEUE); /* flags cleared since w2k */
614 return MMSYSERR_NOERROR;
617 /**************************************************************************
618 * midUnprepare [internal]
620 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
622 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
624 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
625 return MMSYSERR_INVALPARAM;
626 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
627 return MMSYSERR_NOERROR;
628 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
629 return MIDIERR_STILLPLAYING;
631 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
633 return MMSYSERR_NOERROR;
636 /**************************************************************************
637 * midReset [internal]
639 static DWORD midReset(WORD wDevID)
641 DWORD dwTime = GetTickCount();
643 TRACE("(%04X);\n", wDevID);
645 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
646 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
648 EnterCriticalSection(&crit_sect);
649 while (MidiInDev[wDevID].lpQueueHdr) {
650 MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
651 MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
652 /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
653 MIDI_NotifyClient(wDevID, MIM_LONGDATA,
654 (DWORD_PTR)MidiInDev[wDevID].lpQueueHdr, dwTime);
655 MidiInDev[wDevID].lpQueueHdr = MidiInDev[wDevID].lpQueueHdr->lpNext;
657 LeaveCriticalSection(&crit_sect);
659 return MMSYSERR_NOERROR;
662 /**************************************************************************
663 * midStart [internal]
665 static DWORD midStart(WORD wDevID)
667 TRACE("(%04X);\n", wDevID);
669 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
670 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
672 MidiInDev[wDevID].state = 1;
673 MidiInDev[wDevID].startTime = GetTickCount();
674 return MMSYSERR_NOERROR;
677 /**************************************************************************
680 static DWORD midStop(WORD wDevID)
682 TRACE("(%04X);\n", wDevID);
684 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
685 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
687 MidiInDev[wDevID].state = 0;
688 return MMSYSERR_NOERROR;
691 /**************************************************************************
692 * modGetDevCaps [internal]
694 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
696 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
698 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
699 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
701 memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
703 return MMSYSERR_NOERROR;
706 /**************************************************************************
709 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
711 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
712 if (lpDesc == NULL) {
713 WARN("Invalid Parameter !\n");
714 return MMSYSERR_INVALPARAM;
716 if (wDevID >= MODM_NumDevs) {
717 TRACE("MAX_MIDIOUTDRV reached !\n");
718 return MMSYSERR_BADDEVICEID;
720 if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
721 WARN("device already open !\n");
722 return MMSYSERR_ALLOCATED;
724 if (!MidiOutDev[wDevID].bEnabled) {
725 WARN("device disabled !\n");
726 return MIDIERR_NODEVICE;
728 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
729 WARN("bad dwFlags\n");
730 return MMSYSERR_INVALFLAG;
732 if (!MidiOutDev[wDevID].bEnabled) {
733 TRACE("disabled wDevID\n");
734 return MMSYSERR_NOTENABLED;
737 MidiOutDev[wDevID].lpExtra = 0;
739 switch (MidiOutDev[wDevID].caps.wTechnology) {
743 if (midiOpenSeq(1) < 0) {
744 return MMSYSERR_ALLOCATED;
748 WARN("Technology not supported (yet) %d !\n",
749 MidiOutDev[wDevID].caps.wTechnology);
750 return MMSYSERR_NOTENABLED;
753 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
755 MidiOutDev[wDevID].lpQueueHdr = NULL;
756 MidiOutDev[wDevID].dwTotalPlayed = 0;
757 MidiOutDev[wDevID].bufsize = 0x3FFF;
758 MidiOutDev[wDevID].midiDesc = *lpDesc;
760 /* Connect our app port to the device port */
761 if (snd_seq_connect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port) < 0)
762 return MMSYSERR_NOTENABLED;
764 MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L);
765 TRACE("Successful !\n");
766 return MMSYSERR_NOERROR;
770 /**************************************************************************
771 * modClose [internal]
773 static DWORD modClose(WORD wDevID)
775 int ret = MMSYSERR_NOERROR;
777 TRACE("(%04X);\n", wDevID);
779 if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
780 WARN("device not opened !\n");
781 return MMSYSERR_ERROR;
783 /* FIXME: should test that no pending buffer is still in the queue for
786 if (midiSeq == NULL) {
787 WARN("can't close !\n");
788 return MMSYSERR_ERROR;
791 switch (MidiOutDev[wDevID].caps.wTechnology) {
795 snd_seq_disconnect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
799 WARN("Technology not supported (yet) %d !\n",
800 MidiOutDev[wDevID].caps.wTechnology);
801 return MMSYSERR_NOTENABLED;
804 HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
805 MidiOutDev[wDevID].lpExtra = 0;
807 MidiOutDev[wDevID].bufsize = 0;
808 MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L);
809 MidiOutDev[wDevID].midiDesc.hMidi = 0;
813 /**************************************************************************
816 static DWORD modData(WORD wDevID, DWORD dwParam)
818 BYTE evt = LOBYTE(LOWORD(dwParam));
819 BYTE d1 = HIBYTE(LOWORD(dwParam));
820 BYTE d2 = LOBYTE(HIWORD(dwParam));
822 TRACE("(%04X, %08X);\n", wDevID, dwParam);
824 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
825 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
827 if (midiSeq == NULL) {
828 WARN("can't play !\n");
829 return MIDIERR_NODEVICE;
831 switch (MidiOutDev[wDevID].caps.wTechnology) {
835 int handled = 1; /* Assume event is handled */
836 snd_seq_event_t event;
837 snd_seq_ev_clear(&event);
838 snd_seq_ev_set_direct(&event);
839 snd_seq_ev_set_source(&event, port_out);
840 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
842 switch (evt & 0xF0) {
843 case MIDI_CMD_NOTE_OFF:
844 snd_seq_ev_set_noteoff(&event, evt&0x0F, d1, d2);
846 case MIDI_CMD_NOTE_ON:
847 snd_seq_ev_set_noteon(&event, evt&0x0F, d1, d2);
849 case MIDI_CMD_NOTE_PRESSURE:
850 snd_seq_ev_set_keypress(&event, evt&0x0F, d1, d2);
852 case MIDI_CMD_CONTROL:
853 snd_seq_ev_set_controller(&event, evt&0x0F, d1, d2);
855 case MIDI_CMD_BENDER:
856 snd_seq_ev_set_pitchbend(&event, evt&0x0F, ((WORD)d2 << 7 | (WORD)d1) - 0x2000);
858 case MIDI_CMD_PGM_CHANGE:
859 snd_seq_ev_set_pgmchange(&event, evt&0x0F, d1);
861 case MIDI_CMD_CHANNEL_PRESSURE:
862 snd_seq_ev_set_chanpress(&event, evt&0x0F, d1);
864 case MIDI_CMD_COMMON_SYSEX:
865 switch (evt & 0x0F) {
866 case 0x00: /* System Exclusive, don't do it on modData,
867 * should require modLongData*/
868 case 0x04: /* Undefined. */
869 case 0x05: /* Undefined. */
870 case 0x07: /* End of Exclusive. */
871 case 0x09: /* Undefined. */
872 case 0x0D: /* Undefined. */
875 case 0x06: /* Tune Request */
876 case 0x08: /* Timing Clock. */
877 case 0x0A: /* Start. */
878 case 0x0B: /* Continue */
879 case 0x0C: /* Stop */
880 case 0x0E: /* Active Sensing. */
881 /* FIXME: Is this function suitable for these purposes
882 (and also Song Select and Song Position Pointer) */
883 snd_seq_ev_set_sysex(&event, 1, &evt);
885 case 0x0F: /* Reset */
886 /* snd_seq_ev_set_sysex(&event, 1, &evt);
887 this other way may be better */
889 BYTE reset_sysex_seq[] = {MIDI_CMD_COMMON_SYSEX, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
890 snd_seq_ev_set_sysex(&event, sizeof(reset_sysex_seq), reset_sysex_seq);
893 case 0x01: /* MTC Quarter frame */
894 case 0x03: /* Song Select. */
899 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
902 case 0x02: /* Song Position Pointer. */
908 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
915 snd_seq_event_output_direct(midiSeq, &event);
919 WARN("Technology not supported (yet) %d !\n",
920 MidiOutDev[wDevID].caps.wTechnology);
921 return MMSYSERR_NOTENABLED;
924 return MMSYSERR_NOERROR;
927 /**************************************************************************
928 * modLongData [internal]
930 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
933 BYTE *lpData, *lpNewData = NULL;
934 snd_seq_event_t event;
936 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
938 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
939 * but it seems to be used only for midi input.
940 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
943 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
944 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
946 if (midiSeq == NULL) {
947 WARN("can't play !\n");
948 return MIDIERR_NODEVICE;
951 lpData = (BYTE*)lpMidiHdr->lpData;
954 return MIDIERR_UNPREPARED;
955 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
956 return MIDIERR_UNPREPARED;
957 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
958 return MIDIERR_STILLPLAYING;
959 lpMidiHdr->dwFlags &= ~MHDR_DONE;
960 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
962 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
963 * data, or can it also contain raw MIDI data, to be split up and sent to
965 * If the latest is true, then the following WARNing will fire up
967 if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
968 WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
969 lpNewData = HeapAlloc(GetProcessHeap(), 0, lpMidiHdr->dwBufferLength + 2);
972 TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
973 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
974 lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
975 lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
977 switch (MidiOutDev[wDevID].caps.wTechnology) {
979 /* FIXME: I don't think there is much to do here */
980 HeapFree(GetProcessHeap(), 0, lpNewData);
983 if (lpData[0] != 0xF0) {
984 /* Send start of System Exclusive */
987 memcpy(lpNewData + 1, lpData, lpMidiHdr->dwBufferLength);
988 WARN("Adding missing 0xF0 marker at the beginning of system exclusive byte stream\n");
990 if (lpData[lpMidiHdr->dwBufferLength-1] != 0xF7) {
991 /* Send end of System Exclusive */
993 memcpy(lpNewData, lpData, lpMidiHdr->dwBufferLength);
994 lpNewData[lpMidiHdr->dwBufferLength + len_add] = 0xF7;
996 WARN("Adding missing 0xF7 marker at the end of system exclusive byte stream\n");
998 snd_seq_ev_clear(&event);
999 snd_seq_ev_set_direct(&event);
1000 snd_seq_ev_set_source(&event, port_out);
1001 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1002 TRACE("client = %d port = %d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1003 snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData);
1004 snd_seq_event_output_direct(midiSeq, &event);
1005 HeapFree(GetProcessHeap(), 0, lpNewData);
1008 WARN("Technology not supported (yet) %d !\n",
1009 MidiOutDev[wDevID].caps.wTechnology);
1010 HeapFree(GetProcessHeap(), 0, lpNewData);
1011 return MMSYSERR_NOTENABLED;
1014 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1015 lpMidiHdr->dwFlags |= MHDR_DONE;
1016 MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD_PTR)lpMidiHdr, 0L);
1017 return MMSYSERR_NOERROR;
1020 /**************************************************************************
1021 * modPrepare [internal]
1023 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1025 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
1027 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
1028 return MMSYSERR_INVALPARAM;
1029 if (lpMidiHdr->dwFlags & MHDR_PREPARED)
1030 return MMSYSERR_NOERROR;
1032 lpMidiHdr->lpNext = 0;
1033 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1034 lpMidiHdr->dwFlags &= ~(MHDR_DONE|MHDR_INQUEUE); /* flags cleared since w2k */
1035 return MMSYSERR_NOERROR;
1038 /**************************************************************************
1039 * modUnprepare [internal]
1041 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1043 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
1045 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
1046 return MMSYSERR_INVALPARAM;
1047 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
1048 return MMSYSERR_NOERROR;
1049 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1050 return MIDIERR_STILLPLAYING;
1051 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1052 return MMSYSERR_NOERROR;
1055 /**************************************************************************
1056 * modGetVolume [internal]
1058 static DWORD modGetVolume(WORD wDevID, DWORD* lpdwVolume)
1060 if (!lpdwVolume) return MMSYSERR_INVALPARAM;
1061 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1062 *lpdwVolume = 0xFFFFFFFF;
1063 return (MidiOutDev[wDevID].caps.dwSupport & MIDICAPS_VOLUME) ? 0 : MMSYSERR_NOTSUPPORTED;
1066 /**************************************************************************
1067 * modReset [internal]
1069 static DWORD modReset(WORD wDevID)
1073 TRACE("(%04X);\n", wDevID);
1075 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1076 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1078 /* stop all notes */
1079 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1080 * it's channel dependent...
1082 for (chn = 0; chn < 16; chn++) {
1083 /* turn off every note */
1084 modData(wDevID, 0x7800 | MIDI_CMD_CONTROL | chn);
1085 /* remove sustain on all channels */
1086 modData(wDevID, (MIDI_CTL_SUSTAIN << 8) | MIDI_CMD_CONTROL | chn);
1088 /* FIXME: the LongData buffers must also be returned to the app */
1089 return MMSYSERR_NOERROR;
1093 /**************************************************************************
1094 * ALSA_AddMidiPort [internal]
1096 * Helper for ALSA_MidiInit
1098 static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t* pinfo, unsigned int cap, unsigned int type)
1100 char midiPortName[MAXPNAMELEN];
1102 if (cap & SND_SEQ_PORT_CAP_WRITE) {
1103 TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1104 snd_seq_client_info_get_name(cinfo),
1105 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1106 snd_seq_port_info_get_port(pinfo),
1107 snd_seq_port_info_get_name(pinfo),
1110 if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1115 MidiOutDev[MODM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1117 /* Manufac ID. We do not have access to this with soundcard.h
1118 * Does not seem to be a problem, because in mmsystem.h only
1119 * Microsoft's ID is listed.
1121 MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1122 MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1123 /* Product Version. We simply say "1" */
1124 MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1125 /* The following are mandatory for MOD_MIDIPORT */
1126 MidiOutDev[MODM_NumDevs].caps.wChannelMask = 0xFFFF;
1127 MidiOutDev[MODM_NumDevs].caps.wVoices = 0;
1128 MidiOutDev[MODM_NumDevs].caps.wNotes = 0;
1129 MidiOutDev[MODM_NumDevs].caps.dwSupport = 0;
1131 /* Try to use both client and port names, if this is too long take the port name only.
1132 In the second case the port name should be explicit enough due to its big size.
1134 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1135 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1137 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN);
1139 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1140 MidiOutDev[MODM_NumDevs].caps.szPname,
1141 sizeof(MidiOutDev[MODM_NumDevs].caps.szPname) / sizeof(WCHAR));
1143 MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1145 if (MOD_MIDIPORT != MidiOutDev[MODM_NumDevs].caps.wTechnology) {
1146 /* FIXME Do we have this information?
1147 * Assuming the soundcards can handle
1148 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1149 * not MIDICAPS_CACHE.
1151 MidiOutDev[MODM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1152 MidiOutDev[MODM_NumDevs].caps.wVoices = 16;
1154 /* FIXME Is it possible to know the maximum
1155 * number of simultaneous notes of a soundcard ?
1156 * I believe we don't have this information, but
1157 * it's probably equal or more than wVoices
1159 MidiOutDev[MODM_NumDevs].caps.wNotes = 16;
1161 MidiOutDev[MODM_NumDevs].bEnabled = TRUE;
1163 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1164 "\tALSA info: midi dev-type=%x, capa=0\n",
1165 MODM_NumDevs, wine_dbgstr_w(MidiOutDev[MODM_NumDevs].caps.szPname),
1166 MidiOutDev[MODM_NumDevs].caps.wTechnology,
1167 MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1168 MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1173 if (cap & SND_SEQ_PORT_CAP_READ) {
1174 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1175 snd_seq_client_info_get_name(cinfo),
1176 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1177 snd_seq_port_info_get_port(pinfo),
1178 snd_seq_port_info_get_name(pinfo),
1181 if (MIDM_NumDevs >= MAX_MIDIINDRV)
1186 MidiInDev[MIDM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1188 /* Manufac ID. We do not have access to this with soundcard.h
1189 * Does not seem to be a problem, because in mmsystem.h only
1190 * Microsoft's ID is listed.
1192 MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1193 MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1194 /* Product Version. We simply say "1" */
1195 MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1196 MidiInDev[MIDM_NumDevs].caps.dwSupport = 0; /* mandatory with MIDIINCAPS */
1198 /* Try to use both client and port names, if this is too long take the port name only.
1199 In the second case the port name should be explicit enough due to its big size.
1201 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1202 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1204 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN);
1206 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1207 MidiInDev[MIDM_NumDevs].caps.szPname,
1208 sizeof(MidiInDev[MIDM_NumDevs].caps.szPname) / sizeof(WCHAR));
1209 MidiInDev[MIDM_NumDevs].state = 0;
1211 TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1212 "\tALSA info: midi dev-type=%x, capa=0\n",
1213 MIDM_NumDevs, wine_dbgstr_w(MidiInDev[MIDM_NumDevs].caps.szPname),
1214 MidiInDev[MIDM_NumDevs].caps.dwSupport,
1222 /*======================================================================*
1223 * MIDI entry points *
1224 *======================================================================*/
1226 /**************************************************************************
1227 * ALSA_MidiInit [internal]
1229 * Initializes the MIDI devices information variables
1231 static LONG ALSA_MidiInit(void)
1233 static BOOL bInitDone = FALSE;
1234 snd_seq_client_info_t *cinfo;
1235 snd_seq_port_info_t *pinfo;
1240 TRACE("Initializing the MIDI variables.\n");
1243 /* try to open device */
1244 if (midiOpenSeq(0) == -1) {
1248 #if 0 /* Debug purpose */
1249 snd_lib_error_set_handler(error_handler);
1251 cinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_client_info_sizeof() );
1252 pinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_port_info_sizeof() );
1254 /* First, search for all internal midi devices */
1255 snd_seq_client_info_set_client(cinfo, -1);
1256 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1257 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1258 snd_seq_port_info_set_port(pinfo, -1);
1259 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1260 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1261 unsigned int type = snd_seq_port_info_get_type(pinfo);
1262 if (!(type & SND_SEQ_PORT_TYPE_PORT))
1263 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1267 /* Second, search for all external ports */
1268 snd_seq_client_info_set_client(cinfo, -1);
1269 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1270 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1271 snd_seq_port_info_set_port(pinfo, -1);
1272 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1273 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1274 unsigned int type = snd_seq_port_info_get_type(pinfo);
1275 if (type & SND_SEQ_PORT_TYPE_PORT)
1276 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1280 /* close file and exit */
1282 HeapFree( GetProcessHeap(), 0, cinfo );
1283 HeapFree( GetProcessHeap(), 0, pinfo );
1289 /**************************************************************************
1290 * midMessage (WINEALSA.@)
1292 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1293 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1295 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1296 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1304 /* FIXME: Pretend this is supported */
1307 return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1309 return midClose(wDevID);
1310 case MIDM_ADDBUFFER:
1311 return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1313 return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1314 case MIDM_UNPREPARE:
1315 return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1316 case MIDM_GETDEVCAPS:
1317 return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1318 case MIDM_GETNUMDEVS:
1319 return MIDM_NumDevs;
1321 return midReset(wDevID);
1323 return midStart(wDevID);
1325 return midStop(wDevID);
1327 TRACE("Unsupported message\n");
1329 return MMSYSERR_NOTSUPPORTED;
1332 /**************************************************************************
1333 * modMessage (WINEALSA.@)
1335 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1336 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1338 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1339 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1348 /* FIXME: Pretend this is supported */
1351 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1353 return modClose(wDevID);
1355 return modData(wDevID, dwParam1);
1357 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1359 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1360 case MODM_UNPREPARE:
1361 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1362 case MODM_GETDEVCAPS:
1363 return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1364 case MODM_GETNUMDEVS:
1365 return MODM_NumDevs;
1366 case MODM_GETVOLUME:
1367 return modGetVolume(wDevID, (DWORD*)dwParam1);
1368 case MODM_SETVOLUME:
1371 return modReset(wDevID);
1373 TRACE("Unsupported message\n");
1375 return MMSYSERR_NOTSUPPORTED;
1378 /**************************************************************************
1379 * DriverProc (WINEALSA.@)
1381 LRESULT CALLBACK ALSA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1382 LPARAM dwParam1, LPARAM dwParam2)
1384 /* EPP TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", */
1385 /* EPP dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
1394 case DRV_QUERYCONFIGURE: