Added missing notifications to MIDI_NotifyClient.
[wine/wine-gecko.git] / dlls / winmm / winealsa / midi.c
blobd36cdc0794cc55fd8681ad4be27738ffd79ba048
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 /*
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 splitted in midi.c and mcimidi.c
13 * Copyright 2003 Christian Costa :
14 * ALSA port
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 * TODO: Finish midi record
34 #include "config.h"
36 #include <string.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #ifdef HAVE_UNISTD_H
40 # include <unistd.h>
41 #endif
42 #include <fcntl.h>
43 #include <errno.h>
45 #include "windef.h"
46 #include "winbase.h"
47 #include "wingdi.h"
48 #include "winuser.h"
49 #include "mmddk.h"
50 #ifdef HAVE_ALSA
51 # include "alsa.h"
52 #endif
53 #include "wine/debug.h"
55 WINE_DEFAULT_DEBUG_CHANNEL(midi);
57 #if defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1)
59 typedef struct {
60 int state; /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
61 DWORD bufsize;
62 MIDIOPENDESC midiDesc;
63 WORD wFlags;
64 LPMIDIHDR lpQueueHdr;
65 DWORD dwTotalPlayed;
66 unsigned char incoming[3];
67 unsigned char incPrev;
68 char incLen;
69 DWORD startTime;
70 MIDIINCAPSA caps;
71 snd_seq_addr_t addr;
72 } WINE_MIDIIN;
74 typedef struct {
75 BOOL bEnabled;
76 DWORD bufsize;
77 MIDIOPENDESC midiDesc;
78 WORD wFlags;
79 LPMIDIHDR lpQueueHdr;
80 DWORD dwTotalPlayed;
81 void* lpExtra; /* according to port type (MIDI, FM...), extra data when needed */
82 MIDIOUTCAPSA caps;
83 snd_seq_addr_t addr;
84 } WINE_MIDIOUT;
86 static WINE_MIDIIN MidiInDev [MAX_MIDIINDRV ];
87 static WINE_MIDIOUT MidiOutDev[MAX_MIDIOUTDRV];
89 /* this is the total number of MIDI out devices found (synth and port) */
90 static int MODM_NumDevs = 0;
91 /* this is the total number of MIDI out devices found */
92 static int MIDM_NumDevs = 0;
94 static snd_seq_t* midiSeq = NULL;
95 static int numOpenMidiSeq = 0;
96 static UINT midiInTimerID = 0;
97 static int numStartedMidiIn = 0;
99 static int port_in;
100 static int port_out;
102 /*======================================================================*
103 * Low level MIDI implementation *
104 *======================================================================*/
106 static int midiOpenSeq(int);
107 static int midiCloseSeq(void);
109 #if 0 /* Debug Purpose */
110 static void error_handler(const char* file, int line, const char* function, int err, const char* fmt, ...)
112 va_list arg;
113 if (err == ENOENT)
114 return;
115 va_start(arg, fmt);
116 fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function);
117 vfprintf(stderr, fmt, arg);
118 if (err)
119 fprintf(stderr, ": %s", snd_strerror(err));
120 putc('\n', stderr);
121 va_end(arg);
123 #endif
125 /**************************************************************************
126 * MIDI_unixToWindowsDeviceType [internal]
128 * return the Windows equivalent to a Unix Device Type
131 static int MIDI_AlsaToWindowsDeviceType(int type)
133 /* MOD_MIDIPORT output port
134 * MOD_SYNTH generic internal synth
135 * MOD_SQSYNTH square wave internal synth
136 * MOD_FMSYNTH FM internal synth
137 * MOD_MAPPER MIDI mapper
138 * MOD_WAVETABLE hardware watetable internal synth
139 * MOD_SWSYNTH software internal synth
142 /* FIXME Is this really the correct equivalence from ALSA to
143 Windows Sound type */
145 if (type & SND_SEQ_PORT_TYPE_SYNTH)
146 return MOD_FMSYNTH;
148 if (type & (SND_SEQ_PORT_TYPE_DIRECT_SAMPLE|SND_SEQ_PORT_TYPE_SAMPLE))
149 return MOD_SYNTH;
151 if (type & SND_SEQ_PORT_TYPE_MIDI_GENERIC)
152 return MOD_MIDIPORT;
154 ERR("Cannot determine the type of this midi device. Assuming FM Synth\n");
155 return MOD_FMSYNTH;
158 /**************************************************************************
159 * MIDI_NotifyClient [internal]
161 static DWORD MIDI_NotifyClient(UINT wDevID, WORD wMsg,
162 DWORD dwParam1, DWORD dwParam2)
164 DWORD dwCallBack;
165 UINT uFlags;
166 HANDLE hDev;
167 DWORD dwInstance;
169 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
170 wDevID, wMsg, dwParam1, dwParam2);
172 switch (wMsg) {
173 case MOM_OPEN:
174 case MOM_CLOSE:
175 case MOM_DONE:
176 case MOM_POSITIONCB:
177 if (wDevID > MODM_NumDevs)
178 return MMSYSERR_BADDEVICEID;
180 dwCallBack = MidiOutDev[wDevID].midiDesc.dwCallback;
181 uFlags = MidiOutDev[wDevID].wFlags;
182 hDev = MidiOutDev[wDevID].midiDesc.hMidi;
183 dwInstance = MidiOutDev[wDevID].midiDesc.dwInstance;
184 break;
186 case MIM_OPEN:
187 case MIM_CLOSE:
188 case MIM_DATA:
189 case MIM_LONGDATA:
190 case MIM_ERROR:
191 case MIM_LONGERROR:
192 case MIM_MOREDATA:
193 if (wDevID > MIDM_NumDevs)
194 return MMSYSERR_BADDEVICEID;
196 dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
197 uFlags = MidiInDev[wDevID].wFlags;
198 hDev = MidiInDev[wDevID].midiDesc.hMidi;
199 dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
200 break;
201 default:
202 WARN("Unsupported MSW-MIDI message %u\n", wMsg);
203 return MMSYSERR_ERROR;
206 return DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2) ?
207 0 : MMSYSERR_ERROR;
210 static int midi_warn = 1;
211 /**************************************************************************
212 * midiOpenSeq [internal]
214 static int midiOpenSeq(int create_client)
216 if (numOpenMidiSeq == 0) {
217 if (snd_seq_open(&midiSeq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0)
219 if (midi_warn)
221 WARN("Error opening ALSA sequencer.\n");
223 midi_warn = 0;
224 return -1;
227 if (create_client) {
228 /* Setting the client name is the only init to do */
229 snd_seq_set_client_name(midiSeq, "WINE midi driver");
231 #if 0 /* FIXME: Is it possible to use a port for READ & WRITE ops */
232 port_in = port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input/Output", SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE,
233 SND_SEQ_PORT_TYPE_APPLICATION);
234 if (port_out < 0)
235 TRACE("Unable to create output port\n");
236 else
237 TRACE("Outport port created successfully (%d)\n", port_out);
238 #else
239 port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Output", SND_SEQ_PORT_CAP_READ,
240 SND_SEQ_PORT_TYPE_APPLICATION);
241 if (port_out < 0)
242 TRACE("Unable to create output port\n");
243 else
244 TRACE("Outport port created successfully (%d)\n", port_out);
246 port_in = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input", SND_SEQ_PORT_CAP_WRITE,
247 SND_SEQ_PORT_TYPE_APPLICATION);
248 if (port_in < 0)
249 TRACE("Unable to create input port\n");
250 else
251 TRACE("Input port created successfully (%d)\n", port_in);
252 #endif
255 numOpenMidiSeq++;
256 return 0;
259 /**************************************************************************
260 * midiCloseSeq [internal]
262 static int midiCloseSeq(void)
264 if (--numOpenMidiSeq == 0) {
265 snd_seq_delete_simple_port(midiSeq, port_out);
266 snd_seq_delete_simple_port(midiSeq, port_in);
267 snd_seq_close(midiSeq);
268 midiSeq = NULL;
270 return 0;
273 static VOID WINAPI midTimeCallback(HWND hwnd, UINT msg, UINT id, DWORD dwTime)
275 int npfd;
276 struct pollfd *pfd;
278 TRACE("(%p, %d, %d, %lu)\n", hwnd, msg, id, dwTime);
280 npfd = snd_seq_poll_descriptors_count(midiSeq, POLLIN);
281 pfd = (struct pollfd *)HeapAlloc(GetProcessHeap(), 0, npfd * sizeof(struct pollfd));
282 snd_seq_poll_descriptors(midiSeq, pfd, npfd, POLLIN);
284 /* Check if a event is present */
285 if (poll(pfd, npfd, 0) <= 0) {
286 HeapFree(GetProcessHeap(), 0, pfd);
287 return;
290 /* Note: This definitely does not work.
291 * while(snd_seq_event_input_pending(midiSeq, 0) > 0) {
292 snd_seq_event_t* ev;
293 snd_seq_event_input(midiSeq, &ev);
294 ....................
295 snd_seq_free_event(ev);
298 do {
299 WORD wDevID;
300 snd_seq_event_t* ev;
301 snd_seq_event_input(midiSeq, &ev);
302 /* Find the target device */
303 for (wDevID = 0; wDevID < MIDM_NumDevs; wDevID++)
304 if ( (ev->source.client == MidiInDev[wDevID].addr.client) && (ev->source.port == MidiInDev[wDevID].addr.port) )
305 break;
306 if (wDevID == MIDM_NumDevs)
307 FIXME("Unexpected event received, type = %x from %d:%d\n", ev->type, ev->source.client, ev->source.port);
308 else {
309 DWORD toSend = 0;
310 TRACE("Event received, type = %x, device = %d\n", ev->type, wDevID);
311 switch(ev->type)
313 case SND_SEQ_EVENT_NOTEOFF:
314 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_OFF | ev->data.control.channel;
315 break;
316 case SND_SEQ_EVENT_NOTEON:
317 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_ON | ev->data.control.channel;
318 break;
319 case SND_SEQ_EVENT_KEYPRESS:
320 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_PRESSURE | ev->data.control.channel;
321 break;
322 case SND_SEQ_EVENT_CONTROLLER:
323 toSend = (ev->data.control.value << 16) | (ev->data.control.param << 8) | MIDI_CMD_CONTROL | ev->data.control.channel;
324 break;
325 case SND_SEQ_EVENT_PITCHBEND:
326 toSend = (ev->data.control.value << 16) | (ev->data.control.param << 8) | MIDI_CMD_BENDER | ev->data.control.channel;
327 break;
328 case SND_SEQ_EVENT_PGMCHANGE:
329 toSend = (ev->data.control.value << 16) | (ev->data.control.param << 8) | MIDI_CMD_PGM_CHANGE | ev->data.control.channel;
330 break;
331 case SND_SEQ_EVENT_CHANPRESS:
332 toSend = (ev->data.control.value << 16) | (ev->data.control.param << 8) | MIDI_CMD_CHANNEL_PRESSURE | ev->data.control.channel;
333 break;
334 case SND_SEQ_EVENT_SYSEX:
336 int len = ev->data.ext.len;
337 LPBYTE ptr = (BYTE*) ev->data.ext.ptr;
338 LPMIDIHDR lpMidiHdr = MidiInDev[wDevID].lpQueueHdr;
340 /* FIXME: Should handle sysex greater that a single buffer */
341 if (lpMidiHdr) {
342 if (len <= lpMidiHdr->dwBufferLength) {
343 lpMidiHdr->dwBytesRecorded = len;
344 memcpy(lpMidiHdr->lpData, ptr, len);
345 lpMidiHdr = MidiInDev[wDevID].lpQueueHdr;
346 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
347 lpMidiHdr->dwFlags |= MHDR_DONE;
348 MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)lpMidiHdr->lpNext;
349 if (MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD)lpMidiHdr, dwTime) != MMSYSERR_NOERROR) {
350 WARN("Couldn't notify client\n");
352 } else
353 FIXME("No enough space in the buffer to store sysex!\n");
354 } else
355 FIXME("Sysex received but no buffer to store it!\n");
357 break;
358 case SND_SEQ_EVENT_SENSING:
359 /* Noting to do */
360 break;
361 default:
362 FIXME("Unhandled event received, type = %x\n", ev->type);
363 break;
365 if (toSend != 0) {
366 TRACE("Sending event %08lx (from %d %d)\n", toSend, ev->source.client, ev->source.port);
367 /* FIXME: Should use ev->time instead for better accuracy */
368 if (MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime-MidiInDev[wDevID].startTime) != MMSYSERR_NOERROR) {
369 WARN("Couldn't notify client\n");
373 snd_seq_free_event(ev);
374 } while(snd_seq_event_input_pending(midiSeq, 0) > 0);
376 HeapFree(GetProcessHeap(), 0, pfd);
379 /**************************************************************************
380 * midGetDevCaps [internal]
382 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSA lpCaps, DWORD dwSize)
384 TRACE("(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize);
386 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
387 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
389 memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
391 return MMSYSERR_NOERROR;
395 /**************************************************************************
396 * midOpen [internal]
398 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
400 TRACE("(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
402 if (lpDesc == NULL) {
403 WARN("Invalid Parameter !\n");
404 return MMSYSERR_INVALPARAM;
407 /* FIXME :
408 * how to check that content of lpDesc is correct ?
410 if (wDevID >= MIDM_NumDevs) {
411 WARN("wDevID too large (%u) !\n", wDevID);
412 return MMSYSERR_BADDEVICEID;
414 if (MidiInDev[wDevID].state == -1) {
415 WARN("device disabled\n");
416 return MIDIERR_NODEVICE;
418 if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
419 WARN("device already open !\n");
420 return MMSYSERR_ALLOCATED;
422 if ((dwFlags & MIDI_IO_STATUS) != 0) {
423 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
424 dwFlags &= ~MIDI_IO_STATUS;
426 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
427 FIXME("Bad dwFlags\n");
428 return MMSYSERR_INVALFLAG;
431 if (midiOpenSeq(1) < 0) {
432 return MMSYSERR_ERROR;
435 /* Connect our app port to the device port */
436 if (snd_seq_connect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port) < 0)
437 return MMSYSERR_NOTENABLED;
439 TRACE("input port connected %d %d %d\n",port_in,MidiInDev[wDevID].addr.client,MidiInDev[wDevID].addr.port);
441 if (numStartedMidiIn++ == 0) {
442 midiInTimerID = SetTimer(0, 0, 250, midTimeCallback);
443 if (!midiInTimerID) {
444 numStartedMidiIn = 0;
445 WARN("Couldn't start timer for midi-in\n");
446 midiCloseSeq();
447 return MMSYSERR_ERROR;
449 TRACE("Starting timer (%u) for midi-in\n", midiInTimerID);
452 MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
454 MidiInDev[wDevID].lpQueueHdr = NULL;
455 MidiInDev[wDevID].dwTotalPlayed = 0;
456 MidiInDev[wDevID].bufsize = 0x3FFF;
457 MidiInDev[wDevID].midiDesc = *lpDesc;
458 MidiInDev[wDevID].state = 0;
459 MidiInDev[wDevID].incLen = 0;
460 MidiInDev[wDevID].startTime = 0;
462 if (MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
463 WARN("can't notify client !\n");
464 return MMSYSERR_INVALPARAM;
466 return MMSYSERR_NOERROR;
469 /**************************************************************************
470 * midClose [internal]
472 static DWORD midClose(WORD wDevID)
474 int ret = MMSYSERR_NOERROR;
476 TRACE("(%04X);\n", wDevID);
478 if (wDevID >= MIDM_NumDevs) {
479 WARN("wDevID too big (%u) !\n", wDevID);
480 return MMSYSERR_BADDEVICEID;
482 if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
483 WARN("device not opened !\n");
484 return MMSYSERR_ERROR;
486 if (MidiInDev[wDevID].lpQueueHdr != 0) {
487 return MIDIERR_STILLPLAYING;
490 if (midiSeq == NULL) {
491 WARN("ooops !\n");
492 return MMSYSERR_ERROR;
494 if (--numStartedMidiIn == 0) {
495 TRACE("Stopping timer for midi-in\n");
496 if (!KillTimer(0, midiInTimerID)) {
497 WARN("Couldn't stop timer for midi-in\n");
499 midiInTimerID = 0;
502 snd_seq_disconnect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port);
503 midiCloseSeq();
505 MidiInDev[wDevID].bufsize = 0;
506 if (MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
507 WARN("can't notify client !\n");
508 ret = MMSYSERR_INVALPARAM;
510 MidiInDev[wDevID].midiDesc.hMidi = 0;
512 return ret;
516 /**************************************************************************
517 * midAddBuffer [internal]
519 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
521 TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
523 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
524 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
526 if (lpMidiHdr == NULL) return MMSYSERR_INVALPARAM;
527 if (sizeof(MIDIHDR) > dwSize) return MMSYSERR_INVALPARAM;
528 if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
529 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
530 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
532 if (MidiInDev[wDevID].lpQueueHdr == 0) {
533 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
534 } else {
535 LPMIDIHDR ptr;
537 for (ptr = MidiInDev[wDevID].lpQueueHdr;
538 ptr->lpNext != 0;
539 ptr = (LPMIDIHDR)ptr->lpNext);
540 ptr->lpNext = (struct midihdr_tag*)lpMidiHdr;
542 return MMSYSERR_NOERROR;
545 /**************************************************************************
546 * midPrepare [internal]
548 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
550 TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
552 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
553 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
554 lpMidiHdr->dwBufferLength >= 0x10000ul)
555 return MMSYSERR_INVALPARAM;
557 lpMidiHdr->lpNext = 0;
558 lpMidiHdr->dwFlags |= MHDR_PREPARED;
559 lpMidiHdr->dwBytesRecorded = 0;
561 return MMSYSERR_NOERROR;
564 /**************************************************************************
565 * midUnprepare [internal]
567 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
569 TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
571 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
572 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
574 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
575 lpMidiHdr->lpData == 0 || lpMidiHdr->dwBufferLength >= 0x10000ul)
576 return MMSYSERR_INVALPARAM;
578 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
579 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
581 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
583 return MMSYSERR_NOERROR;
586 /**************************************************************************
587 * midReset [internal]
589 static DWORD midReset(WORD wDevID)
591 DWORD dwTime = GetTickCount();
593 TRACE("(%04X);\n", wDevID);
595 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
596 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
598 while (MidiInDev[wDevID].lpQueueHdr) {
599 MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
600 MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
601 /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
602 if (MIDI_NotifyClient(wDevID, MIM_LONGDATA,
603 (DWORD)MidiInDev[wDevID].lpQueueHdr, dwTime) != MMSYSERR_NOERROR) {
604 WARN("Couldn't notify client\n");
606 MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)MidiInDev[wDevID].lpQueueHdr->lpNext;
609 return MMSYSERR_NOERROR;
612 /**************************************************************************
613 * midStart [internal]
615 static DWORD midStart(WORD wDevID)
617 TRACE("(%04X);\n", wDevID);
619 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
620 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
622 MidiInDev[wDevID].state = 1;
623 MidiInDev[wDevID].startTime = GetTickCount();
624 return MMSYSERR_NOERROR;
627 /**************************************************************************
628 * midStop [internal]
630 static DWORD midStop(WORD wDevID)
632 TRACE("(%04X);\n", wDevID);
634 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
635 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
637 MidiInDev[wDevID].state = 0;
638 return MMSYSERR_NOERROR;
641 /**************************************************************************
642 * modGetDevCaps [internal]
644 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSA lpCaps, DWORD dwSize)
646 TRACE("(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize);
648 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
649 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
651 memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
653 return MMSYSERR_NOERROR;
656 /**************************************************************************
657 * modOpen [internal]
659 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
661 TRACE("(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
662 if (lpDesc == NULL) {
663 WARN("Invalid Parameter !\n");
664 return MMSYSERR_INVALPARAM;
666 if (wDevID >= MODM_NumDevs) {
667 TRACE("MAX_MIDIOUTDRV reached !\n");
668 return MMSYSERR_BADDEVICEID;
670 if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
671 WARN("device already open !\n");
672 return MMSYSERR_ALLOCATED;
674 if (!MidiOutDev[wDevID].bEnabled) {
675 WARN("device disabled !\n");
676 return MIDIERR_NODEVICE;
678 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
679 WARN("bad dwFlags\n");
680 return MMSYSERR_INVALFLAG;
682 if (!MidiOutDev[wDevID].bEnabled) {
683 TRACE("disabled wDevID\n");
684 return MMSYSERR_NOTENABLED;
687 MidiOutDev[wDevID].lpExtra = 0;
689 switch (MidiOutDev[wDevID].caps.wTechnology) {
690 case MOD_FMSYNTH:
691 case MOD_MIDIPORT:
692 case MOD_SYNTH:
693 if (midiOpenSeq(1) < 0) {
694 return MMSYSERR_ALLOCATED;
696 break;
697 default:
698 WARN("Technology not supported (yet) %d !\n",
699 MidiOutDev[wDevID].caps.wTechnology);
700 return MMSYSERR_NOTENABLED;
703 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
705 MidiOutDev[wDevID].lpQueueHdr = NULL;
706 MidiOutDev[wDevID].dwTotalPlayed = 0;
707 MidiOutDev[wDevID].bufsize = 0x3FFF;
708 MidiOutDev[wDevID].midiDesc = *lpDesc;
710 /* Connect our app port to the device port */
711 if (snd_seq_connect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port) < 0)
712 return MMSYSERR_NOTENABLED;
714 if (MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
715 WARN("can't notify client !\n");
716 return MMSYSERR_INVALPARAM;
718 TRACE("Successful !\n");
719 return MMSYSERR_NOERROR;
723 /**************************************************************************
724 * modClose [internal]
726 static DWORD modClose(WORD wDevID)
728 int ret = MMSYSERR_NOERROR;
730 TRACE("(%04X);\n", wDevID);
732 if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
733 WARN("device not opened !\n");
734 return MMSYSERR_ERROR;
736 /* FIXME: should test that no pending buffer is still in the queue for
737 * playing */
739 if (midiSeq == NULL) {
740 WARN("can't close !\n");
741 return MMSYSERR_ERROR;
744 switch (MidiOutDev[wDevID].caps.wTechnology) {
745 case MOD_FMSYNTH:
746 case MOD_MIDIPORT:
747 case MOD_SYNTH:
748 snd_seq_disconnect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
749 midiCloseSeq();
750 break;
751 default:
752 WARN("Technology not supported (yet) %d !\n",
753 MidiOutDev[wDevID].caps.wTechnology);
754 return MMSYSERR_NOTENABLED;
757 if (MidiOutDev[wDevID].lpExtra != 0) {
758 HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
759 MidiOutDev[wDevID].lpExtra = 0;
762 MidiOutDev[wDevID].bufsize = 0;
763 if (MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
764 WARN("can't notify client !\n");
765 ret = MMSYSERR_INVALPARAM;
767 MidiOutDev[wDevID].midiDesc.hMidi = 0;
768 return ret;
771 /**************************************************************************
772 * modData [internal]
774 static DWORD modData(WORD wDevID, DWORD dwParam)
776 BYTE evt = LOBYTE(LOWORD(dwParam));
777 BYTE d1 = HIBYTE(LOWORD(dwParam));
778 BYTE d2 = LOBYTE(HIWORD(dwParam));
780 TRACE("(%04X, %08lX);\n", wDevID, dwParam);
782 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
783 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
785 if (midiSeq == NULL) {
786 WARN("can't play !\n");
787 return MIDIERR_NODEVICE;
789 switch (MidiOutDev[wDevID].caps.wTechnology) {
790 case MOD_SYNTH:
791 case MOD_MIDIPORT:
793 int handled = 1; /* Assume event is handled */
794 snd_seq_event_t event;
795 snd_seq_ev_clear(&event);
796 snd_seq_ev_set_direct(&event);
797 snd_seq_ev_set_source(&event, port_out);
798 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
800 switch (evt & 0xF0) {
801 case MIDI_CMD_NOTE_OFF:
802 snd_seq_ev_set_noteoff(&event, evt&0x0F, d1, d2);
803 break;
804 case MIDI_CMD_NOTE_ON:
805 snd_seq_ev_set_noteon(&event, evt&0x0F, d1, d2);
806 break;
807 case MIDI_CMD_NOTE_PRESSURE:
808 snd_seq_ev_set_keypress(&event, evt&0x0F, d1, d2);
809 break;
810 case MIDI_CMD_CONTROL:
811 snd_seq_ev_set_controller(&event, evt&0x0F, d1, d2);
812 break;
813 case MIDI_CMD_BENDER:
814 snd_seq_ev_set_pitchbend(&event, evt&0x0F, ((WORD)d1 << 7) | (WORD)d2);
815 break;
816 case MIDI_CMD_PGM_CHANGE:
817 snd_seq_ev_set_pgmchange(&event, evt&0x0F, d1);
818 break;
819 case MIDI_CMD_CHANNEL_PRESSURE:
820 snd_seq_ev_set_chanpress(&event, evt&0x0F, d1);
821 break;
822 case MIDI_CMD_COMMON_SYSEX:
823 switch (evt & 0x0F) {
824 case 0x00: /* System Exclusive, don't do it on modData,
825 * should require modLongData*/
826 case 0x01: /* Undefined */
827 case 0x04: /* Undefined. */
828 case 0x05: /* Undefined. */
829 case 0x07: /* End of Exclusive. */
830 case 0x09: /* Undefined. */
831 case 0x0D: /* Undefined. */
832 handled = 0;
833 break;
834 case 0x06: /* Tune Request */
835 case 0x08: /* Timing Clock. */
836 case 0x0A: /* Start. */
837 case 0x0B: /* Continue */
838 case 0x0C: /* Stop */
839 case 0x0E: /* Active Sensing. */
840 /* FIXME: Is this function suitable for these purposes
841 (and also Song Select and Song Position Pointer) */
842 snd_seq_ev_set_sysex(&event, 1, &evt);
843 break;
844 case 0x0F: /* Reset */
845 /* snd_seq_ev_set_sysex(&event, 1, &evt);
846 this other way may be better */
848 BYTE reset_sysex_seq[] = {MIDI_CMD_COMMON_SYSEX, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
849 snd_seq_ev_set_sysex(&event, sizeof(reset_sysex_seq), reset_sysex_seq);
851 break;
852 case 0x03: /* Song Select. */
854 BYTE buf[2];
855 buf[0] = evt;
856 buf[1] = d1;
857 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
859 break;
860 case 0x02: /* Song Position Pointer. */
862 BYTE buf[3];
863 buf[0] = evt;
864 buf[1] = d1;
865 buf[2] = d2;
866 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
868 break;
870 break;
872 if (handled)
873 snd_seq_event_output_direct(midiSeq, &event);
875 break;
876 default:
877 WARN("Technology not supported (yet) %d !\n",
878 MidiOutDev[wDevID].caps.wTechnology);
879 return MMSYSERR_NOTENABLED;
882 return MMSYSERR_NOERROR;
885 /**************************************************************************
886 * modLongData [internal]
888 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
890 int len_add = 0;
891 LPBYTE lpData, lpNewData = NULL;
892 snd_seq_event_t event;
894 TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
896 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
897 * but it seems to be used only for midi input.
898 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
901 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
902 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
904 if (midiSeq == NULL) {
905 WARN("can't play !\n");
906 return MIDIERR_NODEVICE;
909 lpData = lpMidiHdr->lpData;
911 if (lpData == NULL)
912 return MIDIERR_UNPREPARED;
913 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
914 return MIDIERR_UNPREPARED;
915 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
916 return MIDIERR_STILLPLAYING;
917 lpMidiHdr->dwFlags &= ~MHDR_DONE;
918 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
920 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
921 * data, or can it also contain raw MIDI data, to be split up and sent to
922 * modShortData() ?
923 * If the latest is true, then the following WARNing will fire up
925 if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
926 WARN("Alledged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
927 lpNewData = HeapAlloc(GetProcessHeap, 0, lpMidiHdr->dwBufferLength + 2);
930 TRACE("dwBufferLength=%lu !\n", lpMidiHdr->dwBufferLength);
931 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
932 lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
933 lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
935 switch (MidiOutDev[wDevID].caps.wTechnology) {
936 case MOD_FMSYNTH:
937 /* FIXME: I don't think there is much to do here */
938 break;
939 case MOD_MIDIPORT:
940 if (lpData[0] != 0xF0) {
941 /* Send start of System Exclusive */
942 len_add = 1;
943 lpData[0] = 0xF0;
944 memcpy(lpNewData, lpData, lpMidiHdr->dwBufferLength);
945 WARN("Adding missing 0xF0 marker at the beginning of "
946 "system exclusive byte stream\n");
948 if (lpData[lpMidiHdr->dwBufferLength-1] != 0xF7) {
949 /* Send end of System Exclusive */
950 memcpy(lpData + len_add, lpData, lpMidiHdr->dwBufferLength);
951 lpNewData[lpMidiHdr->dwBufferLength + len_add - 1] = 0xF0;
952 len_add++;
953 WARN("Adding missing 0xF7 marker at the end of "
954 "system exclusive byte stream\n");
956 snd_seq_ev_clear(&event);
957 snd_seq_ev_set_direct(&event);
958 snd_seq_ev_set_source(&event, port_out);
959 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
960 TRACE("client = %d port = %d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
961 snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData);
962 snd_seq_event_output_direct(midiSeq, &event);
963 if (lpNewData)
964 HeapFree(GetProcessHeap(), 0, lpData);
965 break;
966 default:
967 WARN("Technology not supported (yet) %d !\n",
968 MidiOutDev[wDevID].caps.wTechnology);
969 return MMSYSERR_NOTENABLED;
972 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
973 lpMidiHdr->dwFlags |= MHDR_DONE;
974 if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD)lpMidiHdr, 0L) != MMSYSERR_NOERROR) {
975 WARN("can't notify client !\n");
976 return MMSYSERR_INVALPARAM;
978 return MMSYSERR_NOERROR;
981 /**************************************************************************
982 * modPrepare [internal]
984 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
986 TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
988 if (midiSeq == NULL) {
989 WARN("can't prepare !\n");
990 return MMSYSERR_NOTENABLED;
993 /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
994 * asks to prepare MIDIHDR which dwFlags != 0.
995 * So at least check for the inqueue flag
997 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
998 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
999 lpMidiHdr->dwBufferLength >= 0x10000ul) {
1000 WARN("%p %p %08lx %d/%ld\n", lpMidiHdr, lpMidiHdr->lpData,
1001 lpMidiHdr->dwFlags, sizeof(MIDIHDR), dwSize);
1002 return MMSYSERR_INVALPARAM;
1005 lpMidiHdr->lpNext = 0;
1006 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1007 lpMidiHdr->dwFlags &= ~MHDR_DONE;
1008 return MMSYSERR_NOERROR;
1011 /**************************************************************************
1012 * modUnprepare [internal]
1014 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1016 TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1018 if (midiSeq == NULL) {
1019 WARN("can't unprepare !\n");
1020 return MMSYSERR_NOTENABLED;
1023 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0)
1024 return MMSYSERR_INVALPARAM;
1025 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1026 return MIDIERR_STILLPLAYING;
1027 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1028 return MMSYSERR_NOERROR;
1031 /**************************************************************************
1032 * modReset [internal]
1034 static DWORD modReset(WORD wDevID)
1036 unsigned chn;
1038 TRACE("(%04X);\n", wDevID);
1040 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1041 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1043 /* stop all notes */
1044 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1045 * it's channel dependent...
1047 for (chn = 0; chn < 16; chn++) {
1048 /* turn off every note */
1049 modData(wDevID, 0x7800 | MIDI_CMD_CONTROL | chn);
1050 /* remove sustain on all channels */
1051 modData(wDevID, (MIDI_CTL_SUSTAIN << 8) | MIDI_CMD_CONTROL | chn);
1053 /* FIXME: the LongData buffers must also be returned to the app */
1054 return MMSYSERR_NOERROR;
1058 /**************************************************************************
1059 * ALSA_AddMidiPort [internal]
1061 * Helper for ALSA_MidiInit
1063 void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t* pinfo, int cap, int type)
1065 if (cap & SND_SEQ_PORT_CAP_WRITE) {
1066 TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1067 snd_seq_client_info_get_name(cinfo),
1068 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1069 snd_seq_port_info_get_port(pinfo),
1070 snd_seq_port_info_get_name(pinfo),
1071 type);
1073 if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1074 return;
1075 if (!type)
1076 return;
1078 memcpy(&MidiOutDev[MODM_NumDevs].addr, snd_seq_port_info_get_addr(pinfo), sizeof(snd_seq_addr_t));
1080 /* Manufac ID. We do not have access to this with soundcard.h
1081 * Does not seem to be a problem, because in mmsystem.h only
1082 * Microsoft's ID is listed.
1084 MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1085 MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1086 /* Product Version. We simply say "1" */
1087 MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1088 MidiOutDev[MODM_NumDevs].caps.wChannelMask = 0xFFFF;
1090 /* FIXME Do we have this information?
1091 * Assuming the soundcards can handle
1092 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1093 * not MIDICAPS_CACHE.
1095 MidiOutDev[MODM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1096 strcpy(MidiOutDev[MODM_NumDevs].caps.szPname, snd_seq_client_info_get_name(cinfo));
1098 MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1099 MidiOutDev[MODM_NumDevs].caps.wVoices = 16;
1101 /* FIXME Is it possible to know the maximum
1102 * number of simultaneous notes of a soundcard ?
1103 * I believe we don't have this information, but
1104 * it's probably equal or more than wVoices
1106 MidiOutDev[MODM_NumDevs].caps.wNotes = 16;
1107 MidiOutDev[MODM_NumDevs].bEnabled = TRUE;
1109 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%ld\n"
1110 "\tALSA info: midi dev-type=%lx, capa=%lx\n",
1111 MODM_NumDevs, MidiOutDev[MODM_NumDevs].caps.szPname, MidiOutDev[MODM_NumDevs].caps.wTechnology,
1112 MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1113 MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1114 (long)type, (long)0);
1116 MODM_NumDevs++;
1118 if (cap & SND_SEQ_PORT_CAP_READ) {
1119 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1120 snd_seq_client_info_get_name(cinfo),
1121 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1122 snd_seq_port_info_get_port(pinfo),
1123 snd_seq_port_info_get_name(pinfo),
1124 type);
1126 if (MIDM_NumDevs >= MAX_MIDIINDRV)
1127 return;
1128 if (!type)
1129 return;
1131 memcpy(&MidiInDev[MIDM_NumDevs].addr, snd_seq_port_info_get_addr(pinfo), sizeof(snd_seq_addr_t));
1133 /* Manufac ID. We do not have access to this with soundcard.h
1134 * Does not seem to be a problem, because in mmsystem.h only
1135 * Microsoft's ID is listed.
1137 MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1138 MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1139 /* Product Version. We simply say "1" */
1140 MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1142 /* FIXME Do we have this information?
1143 * Assuming the soundcards can handle
1144 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1145 * not MIDICAPS_CACHE.
1147 MidiInDev[MIDM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1148 strcpy(MidiInDev[MIDM_NumDevs].caps.szPname, snd_seq_client_info_get_name(cinfo));
1150 MidiInDev[MIDM_NumDevs].state = 0;
1152 TRACE("MidiIn [%d]\tname='%s' support=%ld\n"
1153 "\tALSA info: midi dev-type=%lx, capa=%lx\n",
1154 MIDM_NumDevs, MidiInDev[MIDM_NumDevs].caps.szPname, MidiInDev[MIDM_NumDevs].caps.dwSupport,
1155 (long)type, (long)0);
1157 MIDM_NumDevs++;
1161 #endif /* defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1) */
1164 /*======================================================================*
1165 * MIDI entry points *
1166 *======================================================================*/
1168 /**************************************************************************
1169 * ALSA_MidiInit [internal]
1171 * Initializes the MIDI devices information variables
1173 LONG ALSA_MidiInit(void)
1175 #if defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1)
1176 static BOOL bInitDone = FALSE;
1177 snd_seq_client_info_t *cinfo;
1178 snd_seq_port_info_t *pinfo;
1180 if (bInitDone)
1181 return TRUE;
1183 TRACE("Initializing the MIDI variables.\n");
1184 bInitDone = TRUE;
1186 /* try to open device */
1187 if (midiOpenSeq(0) == -1) {
1188 return TRUE;
1191 #if 0 /* Debug purpose */
1192 snd_lib_error_set_handler(error_handler);
1193 #endif
1195 snd_seq_client_info_alloca(&cinfo);
1196 snd_seq_port_info_alloca(&pinfo);
1198 /* First, search for all internal midi devices */
1199 snd_seq_client_info_set_client(cinfo, -1);
1200 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1201 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1202 snd_seq_port_info_set_port(pinfo, -1);
1203 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1204 int cap = snd_seq_port_info_get_capability(pinfo);
1205 int type = snd_seq_port_info_get_type(pinfo);
1206 if (type != SND_SEQ_PORT_TYPE_MIDI_GENERIC)
1207 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1211 /* Second, search for all external ports */
1212 snd_seq_client_info_set_client(cinfo, -1);
1213 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1214 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1215 snd_seq_port_info_set_port(pinfo, -1);
1216 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1217 int cap = snd_seq_port_info_get_capability(pinfo);
1218 int type = snd_seq_port_info_get_type(pinfo);
1219 if (type == SND_SEQ_PORT_TYPE_MIDI_GENERIC)
1220 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1224 /* close file and exit */
1225 midiCloseSeq();
1227 TRACE("End\n");
1228 #endif
1229 return TRUE;
1232 /**************************************************************************
1233 * midMessage (WINEOSS.4)
1235 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1236 DWORD dwParam1, DWORD dwParam2)
1238 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1239 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1240 switch (wMsg) {
1241 #if defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1)
1242 case DRVM_INIT:
1243 case DRVM_ENABLE:
1244 case DRVM_DISABLE:
1245 /* FIXME: Pretend this is supported */
1246 return 0;
1247 case MIDM_OPEN:
1248 return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1249 case MIDM_CLOSE:
1250 return midClose(wDevID);
1251 case MIDM_ADDBUFFER:
1252 return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1253 case MIDM_PREPARE:
1254 return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1255 case MIDM_UNPREPARE:
1256 return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1257 case MIDM_GETDEVCAPS:
1258 return midGetDevCaps(wDevID, (LPMIDIINCAPSA)dwParam1,dwParam2);
1259 case MIDM_GETNUMDEVS:
1260 return MIDM_NumDevs;
1261 case MIDM_RESET:
1262 return midReset(wDevID);
1263 case MIDM_START:
1264 return midStart(wDevID);
1265 case MIDM_STOP:
1266 return midStop(wDevID);
1267 #endif
1268 default:
1269 TRACE("Unsupported message\n");
1271 return MMSYSERR_NOTSUPPORTED;
1274 /**************************************************************************
1275 * modMessage (WINEOSS.5)
1277 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1278 DWORD dwParam1, DWORD dwParam2)
1280 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1281 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1283 switch (wMsg) {
1284 #if defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1)
1285 case DRVM_INIT:
1286 case DRVM_EXIT:
1287 case DRVM_ENABLE:
1288 case DRVM_DISABLE:
1289 /* FIXME: Pretend this is supported */
1290 return 0;
1291 case MODM_OPEN:
1292 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1293 case MODM_CLOSE:
1294 return modClose(wDevID);
1295 case MODM_DATA:
1296 return modData(wDevID, dwParam1);
1297 case MODM_LONGDATA:
1298 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1299 case MODM_PREPARE:
1300 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1301 case MODM_UNPREPARE:
1302 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1303 case MODM_GETDEVCAPS:
1304 return modGetDevCaps(wDevID, (LPMIDIOUTCAPSA)dwParam1, dwParam2);
1305 case MODM_GETNUMDEVS:
1306 return MODM_NumDevs;
1307 case MODM_GETVOLUME:
1308 return 0;
1309 case MODM_SETVOLUME:
1310 return 0;
1311 case MODM_RESET:
1312 return modReset(wDevID);
1313 #endif
1314 default:
1315 TRACE("Unsupported message\n");
1317 return MMSYSERR_NOTSUPPORTED;
1320 /*-----------------------------------------------------------------------*/