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 void 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) return;
193 dwCallBack = MidiOutDev[wDevID].midiDesc.dwCallback;
194 uFlags = MidiOutDev[wDevID].wFlags;
195 hDev = MidiOutDev[wDevID].midiDesc.hMidi;
196 dwInstance = MidiOutDev[wDevID].midiDesc.dwInstance;
206 if (wDevID > MIDM_NumDevs) return;
208 dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
209 uFlags = MidiInDev[wDevID].wFlags;
210 hDev = MidiInDev[wDevID].midiDesc.hMidi;
211 dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
214 ERR("Unsupported MSW-MIDI message %u\n", wMsg);
218 DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2);
221 static int midi_warn = 1;
222 /**************************************************************************
223 * midiOpenSeq [internal]
225 static int midiOpenSeq(int create_client)
227 if (numOpenMidiSeq == 0) {
228 if (snd_seq_open(&midiSeq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0)
232 WARN("Error opening ALSA sequencer.\n");
239 /* Setting the client name is the only init to do */
240 snd_seq_set_client_name(midiSeq, "WINE midi driver");
242 #if 0 /* FIXME: Is it possible to use a port for READ & WRITE ops */
243 port_in = port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input/Output", SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE,
244 SND_SEQ_PORT_TYPE_APPLICATION);
246 TRACE("Unable to create output port\n");
248 TRACE("Outport port created successfully (%d)\n", port_out);
250 port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Output", SND_SEQ_PORT_CAP_READ,
251 SND_SEQ_PORT_TYPE_APPLICATION);
253 TRACE("Unable to create output port\n");
255 TRACE("Outport port created successfully (%d)\n", port_out);
257 port_in = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input", SND_SEQ_PORT_CAP_WRITE,
258 SND_SEQ_PORT_TYPE_APPLICATION);
260 TRACE("Unable to create input port\n");
262 TRACE("Input port created successfully (%d)\n", port_in);
270 /**************************************************************************
271 * midiCloseSeq [internal]
273 static int midiCloseSeq(void)
275 if (--numOpenMidiSeq == 0) {
276 snd_seq_delete_simple_port(midiSeq, port_out);
277 snd_seq_delete_simple_port(midiSeq, port_in);
278 snd_seq_close(midiSeq);
284 static DWORD WINAPI midRecThread(LPVOID arg)
289 TRACE("Thread startup\n");
292 TRACE("Thread loop\n");
293 npfd = snd_seq_poll_descriptors_count(midiSeq, POLLIN);
294 pfd = HeapAlloc(GetProcessHeap(), 0, npfd * sizeof(struct pollfd));
295 snd_seq_poll_descriptors(midiSeq, pfd, npfd, POLLIN);
297 /* Check if an event is present */
298 if (poll(pfd, npfd, 250) < 0) {
299 HeapFree(GetProcessHeap(), 0, pfd);
303 /* Note: This definitely does not work.
304 * while(snd_seq_event_input_pending(midiSeq, 0) > 0) {
306 snd_seq_event_input(midiSeq, &ev);
308 snd_seq_free_event(ev);
314 snd_seq_event_input(midiSeq, &ev);
315 /* Find the target device */
316 for (wDevID = 0; wDevID < MIDM_NumDevs; wDevID++)
317 if ( (ev->source.client == MidiInDev[wDevID].addr.client) && (ev->source.port == MidiInDev[wDevID].addr.port) )
319 if ((wDevID == MIDM_NumDevs) || (MidiInDev[wDevID].state != 1))
320 FIXME("Unexpected event received, type = %x from %d:%d\n", ev->type, ev->source.client, ev->source.port);
322 DWORD dwTime, toSend = 0;
324 /* FIXME: Should use ev->time instead for better accuracy */
325 dwTime = GetTickCount() - MidiInDev[wDevID].startTime;
326 TRACE("Event received, type = %x, device = %d\n", ev->type, wDevID);
329 case SND_SEQ_EVENT_NOTEOFF:
330 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_OFF | ev->data.control.channel;
332 case SND_SEQ_EVENT_NOTEON:
333 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_ON | ev->data.control.channel;
335 case SND_SEQ_EVENT_KEYPRESS:
336 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_PRESSURE | ev->data.control.channel;
338 case SND_SEQ_EVENT_CONTROLLER:
339 toSend = (ev->data.control.value << 16) | (ev->data.control.param << 8) | MIDI_CMD_CONTROL | ev->data.control.channel;
341 case SND_SEQ_EVENT_PITCHBEND:
342 value = ev->data.control.value + 0x2000;
343 toSend = (((value >> 7) & 0x7f) << 16) | ((value & 0x7f) << 8) | MIDI_CMD_BENDER | ev->data.control.channel;
345 case SND_SEQ_EVENT_PGMCHANGE:
346 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_PGM_CHANGE | ev->data.control.channel;
348 case SND_SEQ_EVENT_CHANPRESS:
349 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_CHANNEL_PRESSURE | ev->data.control.channel;
351 case SND_SEQ_EVENT_CLOCK:
354 case SND_SEQ_EVENT_START:
357 case SND_SEQ_EVENT_CONTINUE:
360 case SND_SEQ_EVENT_STOP:
363 case SND_SEQ_EVENT_SONGPOS:
364 toSend = (((ev->data.control.value >> 7) & 0x7f) << 16) | ((ev->data.control.value & 0x7f) << 8) | 0xF2;
366 case SND_SEQ_EVENT_SONGSEL:
367 toSend = ((ev->data.control.value & 0x7f) << 8) | 0xF3;
369 case SND_SEQ_EVENT_RESET:
372 case SND_SEQ_EVENT_SYSEX:
375 int len = ev->data.ext.len;
376 LPBYTE ptr = ev->data.ext.ptr;
379 EnterCriticalSection(&crit_sect);
381 if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
382 int copylen = min(len, lpMidiHdr->dwBufferLength - lpMidiHdr->dwBytesRecorded);
383 memcpy(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded, ptr + pos, copylen);
384 lpMidiHdr->dwBytesRecorded += copylen;
387 /* We check if we reach the end of buffer or the end of sysex before notifying
388 * to handle the case where ALSA split the sysex into several events */
389 if ((lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) ||
390 (*(BYTE*)(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded - 1) == 0xF7)) {
391 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
392 lpMidiHdr->dwFlags |= MHDR_DONE;
393 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr->lpNext;
394 MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime);
397 FIXME("Sysex data received but no buffer to store it!\n");
401 LeaveCriticalSection(&crit_sect);
404 case SND_SEQ_EVENT_SENSING:
408 FIXME("Unhandled event received, type = %x\n", ev->type);
412 TRACE("Sending event %08x (from %d %d)\n", toSend, ev->source.client, ev->source.port);
413 MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime);
416 snd_seq_free_event(ev);
417 } while(snd_seq_event_input_pending(midiSeq, 0) > 0);
419 HeapFree(GetProcessHeap(), 0, pfd);
424 /**************************************************************************
425 * midGetDevCaps [internal]
427 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
429 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
431 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
432 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
434 memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
436 return MMSYSERR_NOERROR;
440 /**************************************************************************
443 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
445 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
447 if (lpDesc == NULL) {
448 WARN("Invalid Parameter !\n");
449 return MMSYSERR_INVALPARAM;
453 * how to check that content of lpDesc is correct ?
455 if (wDevID >= MIDM_NumDevs) {
456 WARN("wDevID too large (%u) !\n", wDevID);
457 return MMSYSERR_BADDEVICEID;
459 if (MidiInDev[wDevID].state == -1) {
460 WARN("device disabled\n");
461 return MIDIERR_NODEVICE;
463 if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
464 WARN("device already open !\n");
465 return MMSYSERR_ALLOCATED;
467 if ((dwFlags & MIDI_IO_STATUS) != 0) {
468 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
469 dwFlags &= ~MIDI_IO_STATUS;
471 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
472 FIXME("Bad dwFlags\n");
473 return MMSYSERR_INVALFLAG;
476 if (midiOpenSeq(1) < 0) {
477 return MMSYSERR_ERROR;
480 /* Connect our app port to the device port */
481 if (snd_seq_connect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port) < 0)
482 return MMSYSERR_NOTENABLED;
484 TRACE("input port connected %d %d %d\n",port_in,MidiInDev[wDevID].addr.client,MidiInDev[wDevID].addr.port);
486 if (numStartedMidiIn++ == 0) {
488 hThread = CreateThread(NULL, 0, midRecThread, NULL, 0, NULL);
490 numStartedMidiIn = 0;
491 WARN("Couldn't create thread for midi-in\n");
493 return MMSYSERR_ERROR;
495 SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
496 TRACE("Created thread for midi-in\n");
499 MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
501 MidiInDev[wDevID].lpQueueHdr = NULL;
502 MidiInDev[wDevID].dwTotalPlayed = 0;
503 MidiInDev[wDevID].bufsize = 0x3FFF;
504 MidiInDev[wDevID].midiDesc = *lpDesc;
505 MidiInDev[wDevID].state = 0;
506 MidiInDev[wDevID].incLen = 0;
507 MidiInDev[wDevID].startTime = 0;
509 MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L);
510 return MMSYSERR_NOERROR;
513 /**************************************************************************
514 * midClose [internal]
516 static DWORD midClose(WORD wDevID)
518 int ret = MMSYSERR_NOERROR;
520 TRACE("(%04X);\n", wDevID);
522 if (wDevID >= MIDM_NumDevs) {
523 WARN("wDevID too big (%u) !\n", wDevID);
524 return MMSYSERR_BADDEVICEID;
526 if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
527 WARN("device not opened !\n");
528 return MMSYSERR_ERROR;
530 if (MidiInDev[wDevID].lpQueueHdr != 0) {
531 return MIDIERR_STILLPLAYING;
534 if (midiSeq == NULL) {
536 return MMSYSERR_ERROR;
538 if (--numStartedMidiIn == 0) {
539 TRACE("Stopping thread for midi-in\n");
541 if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
542 WARN("Thread end not signaled, force termination\n");
543 TerminateThread(hThread, 0);
545 TRACE("Stopped thread for midi-in\n");
548 snd_seq_disconnect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port);
551 MidiInDev[wDevID].bufsize = 0;
552 MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L);
553 MidiInDev[wDevID].midiDesc.hMidi = 0;
559 /**************************************************************************
560 * midAddBuffer [internal]
562 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
564 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
566 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
567 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
569 if (lpMidiHdr == NULL) return MMSYSERR_INVALPARAM;
570 if (dwSize < offsetof(MIDIHDR,dwOffset)) return MMSYSERR_INVALPARAM;
571 if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
572 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
573 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
575 EnterCriticalSection(&crit_sect);
576 lpMidiHdr->dwFlags &= ~WHDR_DONE;
577 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
578 lpMidiHdr->dwBytesRecorded = 0;
579 lpMidiHdr->lpNext = 0;
580 if (MidiInDev[wDevID].lpQueueHdr == 0) {
581 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
585 for (ptr = MidiInDev[wDevID].lpQueueHdr; ptr->lpNext != 0;
587 ptr->lpNext = lpMidiHdr;
589 LeaveCriticalSection(&crit_sect);
591 return MMSYSERR_NOERROR;
594 /**************************************************************************
595 * midPrepare [internal]
597 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
599 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
601 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 ||
602 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0)
603 return MMSYSERR_INVALPARAM;
605 lpMidiHdr->lpNext = 0;
606 lpMidiHdr->dwFlags |= MHDR_PREPARED;
607 lpMidiHdr->dwBytesRecorded = 0;
609 return MMSYSERR_NOERROR;
612 /**************************************************************************
613 * midUnprepare [internal]
615 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
617 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
619 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
620 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
622 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 ||
623 lpMidiHdr->lpData == 0)
624 return MMSYSERR_INVALPARAM;
626 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
627 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
629 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
631 return MMSYSERR_NOERROR;
634 /**************************************************************************
635 * midReset [internal]
637 static DWORD midReset(WORD wDevID)
639 DWORD dwTime = GetTickCount();
641 TRACE("(%04X);\n", wDevID);
643 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
644 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
646 EnterCriticalSection(&crit_sect);
647 while (MidiInDev[wDevID].lpQueueHdr) {
648 MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
649 MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
650 /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
651 MIDI_NotifyClient(wDevID, MIM_LONGDATA,
652 (DWORD_PTR)MidiInDev[wDevID].lpQueueHdr, dwTime);
653 MidiInDev[wDevID].lpQueueHdr = MidiInDev[wDevID].lpQueueHdr->lpNext;
655 LeaveCriticalSection(&crit_sect);
657 return MMSYSERR_NOERROR;
660 /**************************************************************************
661 * midStart [internal]
663 static DWORD midStart(WORD wDevID)
665 TRACE("(%04X);\n", wDevID);
667 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
668 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
670 MidiInDev[wDevID].state = 1;
671 MidiInDev[wDevID].startTime = GetTickCount();
672 return MMSYSERR_NOERROR;
675 /**************************************************************************
678 static DWORD midStop(WORD wDevID)
680 TRACE("(%04X);\n", wDevID);
682 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
683 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
685 MidiInDev[wDevID].state = 0;
686 return MMSYSERR_NOERROR;
689 /**************************************************************************
690 * modGetDevCaps [internal]
692 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
694 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
696 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
697 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
699 memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
701 return MMSYSERR_NOERROR;
704 /**************************************************************************
707 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
709 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
710 if (lpDesc == NULL) {
711 WARN("Invalid Parameter !\n");
712 return MMSYSERR_INVALPARAM;
714 if (wDevID >= MODM_NumDevs) {
715 TRACE("MAX_MIDIOUTDRV reached !\n");
716 return MMSYSERR_BADDEVICEID;
718 if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
719 WARN("device already open !\n");
720 return MMSYSERR_ALLOCATED;
722 if (!MidiOutDev[wDevID].bEnabled) {
723 WARN("device disabled !\n");
724 return MIDIERR_NODEVICE;
726 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
727 WARN("bad dwFlags\n");
728 return MMSYSERR_INVALFLAG;
730 if (!MidiOutDev[wDevID].bEnabled) {
731 TRACE("disabled wDevID\n");
732 return MMSYSERR_NOTENABLED;
735 MidiOutDev[wDevID].lpExtra = 0;
737 switch (MidiOutDev[wDevID].caps.wTechnology) {
741 if (midiOpenSeq(1) < 0) {
742 return MMSYSERR_ALLOCATED;
746 WARN("Technology not supported (yet) %d !\n",
747 MidiOutDev[wDevID].caps.wTechnology);
748 return MMSYSERR_NOTENABLED;
751 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
753 MidiOutDev[wDevID].lpQueueHdr = NULL;
754 MidiOutDev[wDevID].dwTotalPlayed = 0;
755 MidiOutDev[wDevID].bufsize = 0x3FFF;
756 MidiOutDev[wDevID].midiDesc = *lpDesc;
758 /* Connect our app port to the device port */
759 if (snd_seq_connect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port) < 0)
760 return MMSYSERR_NOTENABLED;
762 MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L);
763 TRACE("Successful !\n");
764 return MMSYSERR_NOERROR;
768 /**************************************************************************
769 * modClose [internal]
771 static DWORD modClose(WORD wDevID)
773 int ret = MMSYSERR_NOERROR;
775 TRACE("(%04X);\n", wDevID);
777 if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
778 WARN("device not opened !\n");
779 return MMSYSERR_ERROR;
781 /* FIXME: should test that no pending buffer is still in the queue for
784 if (midiSeq == NULL) {
785 WARN("can't close !\n");
786 return MMSYSERR_ERROR;
789 switch (MidiOutDev[wDevID].caps.wTechnology) {
793 snd_seq_disconnect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
797 WARN("Technology not supported (yet) %d !\n",
798 MidiOutDev[wDevID].caps.wTechnology);
799 return MMSYSERR_NOTENABLED;
802 HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
803 MidiOutDev[wDevID].lpExtra = 0;
805 MidiOutDev[wDevID].bufsize = 0;
806 MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L);
807 MidiOutDev[wDevID].midiDesc.hMidi = 0;
811 /**************************************************************************
814 static DWORD modData(WORD wDevID, DWORD dwParam)
816 BYTE evt = LOBYTE(LOWORD(dwParam));
817 BYTE d1 = HIBYTE(LOWORD(dwParam));
818 BYTE d2 = LOBYTE(HIWORD(dwParam));
820 TRACE("(%04X, %08X);\n", wDevID, dwParam);
822 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
823 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
825 if (midiSeq == NULL) {
826 WARN("can't play !\n");
827 return MIDIERR_NODEVICE;
829 switch (MidiOutDev[wDevID].caps.wTechnology) {
833 int handled = 1; /* Assume event is handled */
834 snd_seq_event_t event;
835 snd_seq_ev_clear(&event);
836 snd_seq_ev_set_direct(&event);
837 snd_seq_ev_set_source(&event, port_out);
838 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
840 switch (evt & 0xF0) {
841 case MIDI_CMD_NOTE_OFF:
842 snd_seq_ev_set_noteoff(&event, evt&0x0F, d1, d2);
844 case MIDI_CMD_NOTE_ON:
845 snd_seq_ev_set_noteon(&event, evt&0x0F, d1, d2);
847 case MIDI_CMD_NOTE_PRESSURE:
848 snd_seq_ev_set_keypress(&event, evt&0x0F, d1, d2);
850 case MIDI_CMD_CONTROL:
851 snd_seq_ev_set_controller(&event, evt&0x0F, d1, d2);
853 case MIDI_CMD_BENDER:
854 snd_seq_ev_set_pitchbend(&event, evt&0x0F, ((WORD)d2 << 7 | (WORD)d1) - 0x2000);
856 case MIDI_CMD_PGM_CHANGE:
857 snd_seq_ev_set_pgmchange(&event, evt&0x0F, d1);
859 case MIDI_CMD_CHANNEL_PRESSURE:
860 snd_seq_ev_set_chanpress(&event, evt&0x0F, d1);
862 case MIDI_CMD_COMMON_SYSEX:
863 switch (evt & 0x0F) {
864 case 0x00: /* System Exclusive, don't do it on modData,
865 * should require modLongData*/
866 case 0x01: /* Undefined */
867 case 0x04: /* Undefined. */
868 case 0x05: /* Undefined. */
869 case 0x07: /* End of Exclusive. */
870 case 0x09: /* Undefined. */
871 case 0x0D: /* Undefined. */
874 case 0x06: /* Tune Request */
875 case 0x08: /* Timing Clock. */
876 case 0x0A: /* Start. */
877 case 0x0B: /* Continue */
878 case 0x0C: /* Stop */
879 case 0x0E: /* Active Sensing. */
880 /* FIXME: Is this function suitable for these purposes
881 (and also Song Select and Song Position Pointer) */
882 snd_seq_ev_set_sysex(&event, 1, &evt);
884 case 0x0F: /* Reset */
885 /* snd_seq_ev_set_sysex(&event, 1, &evt);
886 this other way may be better */
888 BYTE reset_sysex_seq[] = {MIDI_CMD_COMMON_SYSEX, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
889 snd_seq_ev_set_sysex(&event, sizeof(reset_sysex_seq), reset_sysex_seq);
892 case 0x03: /* Song Select. */
897 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
900 case 0x02: /* Song Position Pointer. */
906 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
913 snd_seq_event_output_direct(midiSeq, &event);
917 WARN("Technology not supported (yet) %d !\n",
918 MidiOutDev[wDevID].caps.wTechnology);
919 return MMSYSERR_NOTENABLED;
922 return MMSYSERR_NOERROR;
925 /**************************************************************************
926 * modLongData [internal]
928 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
931 LPBYTE lpData, lpNewData = NULL;
932 snd_seq_event_t event;
934 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
936 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
937 * but it seems to be used only for midi input.
938 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
941 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
942 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
944 if (midiSeq == NULL) {
945 WARN("can't play !\n");
946 return MIDIERR_NODEVICE;
949 lpData = (LPBYTE) lpMidiHdr->lpData;
952 return MIDIERR_UNPREPARED;
953 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
954 return MIDIERR_UNPREPARED;
955 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
956 return MIDIERR_STILLPLAYING;
957 lpMidiHdr->dwFlags &= ~MHDR_DONE;
958 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
960 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
961 * data, or can it also contain raw MIDI data, to be split up and sent to
963 * If the latest is true, then the following WARNing will fire up
965 if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
966 WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
967 lpNewData = HeapAlloc(GetProcessHeap(), 0, lpMidiHdr->dwBufferLength + 2);
970 TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
971 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
972 lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
973 lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
975 switch (MidiOutDev[wDevID].caps.wTechnology) {
977 /* FIXME: I don't think there is much to do here */
980 if (lpData[0] != 0xF0) {
981 /* Send start of System Exclusive */
984 memcpy(lpNewData, lpData, lpMidiHdr->dwBufferLength);
985 WARN("Adding missing 0xF0 marker at the beginning of "
986 "system exclusive byte stream\n");
988 if (lpData[lpMidiHdr->dwBufferLength-1] != 0xF7) {
989 /* Send end of System Exclusive */
990 memcpy(lpData + len_add, lpData, lpMidiHdr->dwBufferLength);
991 lpNewData[lpMidiHdr->dwBufferLength + len_add - 1] = 0xF0;
993 WARN("Adding missing 0xF7 marker at the end of "
994 "system exclusive byte stream\n");
996 snd_seq_ev_clear(&event);
997 snd_seq_ev_set_direct(&event);
998 snd_seq_ev_set_source(&event, port_out);
999 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1000 TRACE("client = %d port = %d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1001 snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData);
1002 snd_seq_event_output_direct(midiSeq, &event);
1004 HeapFree(GetProcessHeap(), 0, lpData);
1007 WARN("Technology not supported (yet) %d !\n",
1008 MidiOutDev[wDevID].caps.wTechnology);
1009 HeapFree(GetProcessHeap(), 0, lpNewData);
1010 return MMSYSERR_NOTENABLED;
1013 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1014 lpMidiHdr->dwFlags |= MHDR_DONE;
1015 MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD_PTR)lpMidiHdr, 0L);
1016 return MMSYSERR_NOERROR;
1019 /**************************************************************************
1020 * modPrepare [internal]
1022 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1024 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1026 if (midiSeq == NULL) {
1027 WARN("can't prepare !\n");
1028 return MMSYSERR_NOTENABLED;
1031 /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
1032 * asks to prepare MIDIHDR which dwFlags != 0.
1033 * So at least check for the inqueue flag
1035 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 ||
1036 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0) {
1037 WARN("%p %p %08x %d\n", lpMidiHdr, lpMidiHdr ? lpMidiHdr->lpData : NULL,
1038 lpMidiHdr ? lpMidiHdr->dwFlags : 0, dwSize);
1039 return MMSYSERR_INVALPARAM;
1042 lpMidiHdr->lpNext = 0;
1043 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1044 lpMidiHdr->dwFlags &= ~MHDR_DONE;
1045 return MMSYSERR_NOERROR;
1048 /**************************************************************************
1049 * modUnprepare [internal]
1051 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1053 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1055 if (midiSeq == NULL) {
1056 WARN("can't unprepare !\n");
1057 return MMSYSERR_NOTENABLED;
1060 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0)
1061 return MMSYSERR_INVALPARAM;
1062 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1063 return MIDIERR_STILLPLAYING;
1064 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1065 return MMSYSERR_NOERROR;
1068 /**************************************************************************
1069 * modReset [internal]
1071 static DWORD modReset(WORD wDevID)
1075 TRACE("(%04X);\n", wDevID);
1077 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1078 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1080 /* stop all notes */
1081 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1082 * it's channel dependent...
1084 for (chn = 0; chn < 16; chn++) {
1085 /* turn off every note */
1086 modData(wDevID, 0x7800 | MIDI_CMD_CONTROL | chn);
1087 /* remove sustain on all channels */
1088 modData(wDevID, (MIDI_CTL_SUSTAIN << 8) | MIDI_CMD_CONTROL | chn);
1090 /* FIXME: the LongData buffers must also be returned to the app */
1091 return MMSYSERR_NOERROR;
1095 /**************************************************************************
1096 * ALSA_AddMidiPort [internal]
1098 * Helper for ALSA_MidiInit
1100 static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t* pinfo, unsigned int cap, unsigned int type)
1102 char midiPortName[MAXPNAMELEN];
1104 if (cap & SND_SEQ_PORT_CAP_WRITE) {
1105 TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1106 snd_seq_client_info_get_name(cinfo),
1107 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1108 snd_seq_port_info_get_port(pinfo),
1109 snd_seq_port_info_get_name(pinfo),
1112 if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1117 MidiOutDev[MODM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1119 /* Manufac ID. We do not have access to this with soundcard.h
1120 * Does not seem to be a problem, because in mmsystem.h only
1121 * Microsoft's ID is listed.
1123 MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1124 MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1125 /* Product Version. We simply say "1" */
1126 MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1127 MidiOutDev[MODM_NumDevs].caps.wChannelMask = 0xFFFF;
1129 /* FIXME Do we have this information?
1130 * Assuming the soundcards can handle
1131 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1132 * not MIDICAPS_CACHE.
1134 MidiOutDev[MODM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1136 /* Try to use both client and port names, if this is too long take the port name only.
1137 In the second case the port name should be explicit enough due to its big size.
1139 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1140 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1142 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1143 midiPortName[MAXPNAMELEN-1] = 0;
1145 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1146 MidiOutDev[MODM_NumDevs].caps.szPname,
1147 sizeof(MidiOutDev[MODM_NumDevs].caps.szPname) / sizeof(WCHAR));
1149 MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1150 MidiOutDev[MODM_NumDevs].caps.wVoices = 16;
1152 /* FIXME Is it possible to know the maximum
1153 * number of simultaneous notes of a soundcard ?
1154 * I believe we don't have this information, but
1155 * it's probably equal or more than wVoices
1157 MidiOutDev[MODM_NumDevs].caps.wNotes = 16;
1158 MidiOutDev[MODM_NumDevs].bEnabled = TRUE;
1160 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1161 "\tALSA info: midi dev-type=%x, capa=0\n",
1162 MODM_NumDevs, wine_dbgstr_w(MidiOutDev[MODM_NumDevs].caps.szPname),
1163 MidiOutDev[MODM_NumDevs].caps.wTechnology,
1164 MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1165 MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1170 if (cap & SND_SEQ_PORT_CAP_READ) {
1171 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1172 snd_seq_client_info_get_name(cinfo),
1173 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1174 snd_seq_port_info_get_port(pinfo),
1175 snd_seq_port_info_get_name(pinfo),
1178 if (MIDM_NumDevs >= MAX_MIDIINDRV)
1183 MidiInDev[MIDM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1185 /* Manufac ID. We do not have access to this with soundcard.h
1186 * Does not seem to be a problem, because in mmsystem.h only
1187 * Microsoft's ID is listed.
1189 MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1190 MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1191 /* Product Version. We simply say "1" */
1192 MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1194 /* FIXME Do we have this information?
1195 * Assuming the soundcards can handle
1196 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1197 * not MIDICAPS_CACHE.
1199 MidiInDev[MIDM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1201 /* Try to use both client and port names, if this is too long take the port name only.
1202 In the second case the port name should be explicit enough due to its big size.
1204 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1205 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1207 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1208 midiPortName[MAXPNAMELEN-1] = 0;
1210 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1211 MidiInDev[MIDM_NumDevs].caps.szPname,
1212 sizeof(MidiInDev[MIDM_NumDevs].caps.szPname) / sizeof(WCHAR));
1213 MidiInDev[MIDM_NumDevs].state = 0;
1215 TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1216 "\tALSA info: midi dev-type=%x, capa=0\n",
1217 MIDM_NumDevs, wine_dbgstr_w(MidiInDev[MIDM_NumDevs].caps.szPname),
1218 MidiInDev[MIDM_NumDevs].caps.dwSupport,
1225 #endif /* HAVE_ALSA */
1228 /*======================================================================*
1229 * MIDI entry points *
1230 *======================================================================*/
1232 /**************************************************************************
1233 * ALSA_MidiInit [internal]
1235 * Initializes the MIDI devices information variables
1237 LONG ALSA_MidiInit(void)
1240 static BOOL bInitDone = FALSE;
1241 snd_seq_client_info_t *cinfo;
1242 snd_seq_port_info_t *pinfo;
1247 TRACE("Initializing the MIDI variables.\n");
1250 /* try to open device */
1251 if (midiOpenSeq(0) == -1) {
1255 #if 0 /* Debug purpose */
1256 snd_lib_error_set_handler(error_handler);
1258 cinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_client_info_sizeof() );
1259 pinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_port_info_sizeof() );
1261 /* First, search for all internal midi devices */
1262 snd_seq_client_info_set_client(cinfo, -1);
1263 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1264 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1265 snd_seq_port_info_set_port(pinfo, -1);
1266 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1267 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1268 unsigned int type = snd_seq_port_info_get_type(pinfo);
1269 if (!(type & SND_SEQ_PORT_TYPE_PORT))
1270 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1274 /* Second, search for all external ports */
1275 snd_seq_client_info_set_client(cinfo, -1);
1276 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1277 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1278 snd_seq_port_info_set_port(pinfo, -1);
1279 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1280 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1281 unsigned int type = snd_seq_port_info_get_type(pinfo);
1282 if (type & SND_SEQ_PORT_TYPE_PORT)
1283 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1287 /* close file and exit */
1289 HeapFree( GetProcessHeap(), 0, cinfo );
1290 HeapFree( GetProcessHeap(), 0, pinfo );
1297 /**************************************************************************
1298 * midMessage (WINEALSA.@)
1300 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1301 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1303 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1304 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1311 /* FIXME: Pretend this is supported */
1314 return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1316 return midClose(wDevID);
1317 case MIDM_ADDBUFFER:
1318 return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1320 return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1321 case MIDM_UNPREPARE:
1322 return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1323 case MIDM_GETDEVCAPS:
1324 return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1325 case MIDM_GETNUMDEVS:
1326 return MIDM_NumDevs;
1328 return midReset(wDevID);
1330 return midStart(wDevID);
1332 return midStop(wDevID);
1335 TRACE("Unsupported message\n");
1337 return MMSYSERR_NOTSUPPORTED;
1340 /**************************************************************************
1341 * modMessage (WINEALSA.@)
1343 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1344 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1346 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1347 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1355 /* FIXME: Pretend this is supported */
1358 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1360 return modClose(wDevID);
1362 return modData(wDevID, dwParam1);
1364 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1366 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1367 case MODM_UNPREPARE:
1368 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1369 case MODM_GETDEVCAPS:
1370 return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1371 case MODM_GETNUMDEVS:
1372 return MODM_NumDevs;
1373 case MODM_GETVOLUME:
1375 case MODM_SETVOLUME:
1378 return modReset(wDevID);
1381 TRACE("Unsupported message\n");
1383 return MMSYSERR_NOTSUPPORTED;
1386 /*-----------------------------------------------------------------------*/