winealsa.drv: Improve midi port names.
[wine] / dlls / winealsa.drv / midi.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  * Sample MIDI Wine Driver for ALSA (basically Linux)
5  *
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 :
14  *                     ALSA port
15  *
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.
20  *
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.
25  *
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
29  *
30  * TODO: Finish midi record
31  *
32  */
33
34 #include "config.h"
35
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>
44
45 #include "windef.h"
46 #include "winbase.h"
47 #include "wingdi.h"
48 #include "winuser.h"
49 #include "winnls.h"
50 #include "mmddk.h"
51 #include "alsa.h"
52 #include "wine/debug.h"
53
54 WINE_DEFAULT_DEBUG_CHANNEL(midi);
55
56 #ifdef HAVE_ALSA
57
58 #ifndef SND_SEQ_PORT_TYPE_PORT
59 #define SND_SEQ_PORT_TYPE_PORT (1<<19)  /* Appears in version 1.0.12rc1 */
60 #endif
61
62 typedef struct {
63     int                 state;                  /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
64     DWORD               bufsize;
65     MIDIOPENDESC        midiDesc;
66     WORD                wFlags;
67     LPMIDIHDR           lpQueueHdr;
68     DWORD               dwTotalPlayed;
69     unsigned char       incoming[3];
70     unsigned char       incPrev;
71     char                incLen;
72     DWORD               startTime;
73     MIDIINCAPSW         caps;
74     snd_seq_addr_t      addr;
75 } WINE_MIDIIN;
76
77 typedef struct {
78     BOOL                bEnabled;
79     DWORD               bufsize;
80     MIDIOPENDESC        midiDesc;
81     WORD                wFlags;
82     LPMIDIHDR           lpQueueHdr;
83     DWORD               dwTotalPlayed;
84     void*               lpExtra;                /* according to port type (MIDI, FM...), extra data when needed */
85     MIDIOUTCAPSW        caps;
86     snd_seq_addr_t      addr;
87 } WINE_MIDIOUT;
88
89 static WINE_MIDIIN      MidiInDev [MAX_MIDIINDRV ];
90 static WINE_MIDIOUT     MidiOutDev[MAX_MIDIOUTDRV];
91
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;
96
97 static  snd_seq_t*      midiSeq = NULL;
98 static  int             numOpenMidiSeq = 0;
99 static  int             numStartedMidiIn = 0;
100
101 static int port_in;
102 static int port_out;
103
104 static CRITICAL_SECTION crit_sect;   /* protects all MidiIn buffer queues */
105 static CRITICAL_SECTION_DEBUG critsect_debug =
106 {
107     0, 0, &crit_sect,
108     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
109       0, 0, { (DWORD_PTR)(__FILE__ ": crit_sect") }
110 };
111 static CRITICAL_SECTION crit_sect = { &critsect_debug, -1, 0, 0, 0, 0 };
112
113 static int end_thread;
114 static HANDLE hThread;
115
116 /*======================================================================*
117  *                  Low level MIDI implementation                       *
118  *======================================================================*/
119
120 static int midiOpenSeq(int);
121 static int midiCloseSeq(void);
122
123 #if 0 /* Debug Purpose */
124 static void error_handler(const char* file, int line, const char* function, int err, const char* fmt, ...)
125 {
126     va_list arg;
127     if (err == ENOENT)
128         return;
129     va_start(arg, fmt);
130     fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function);
131     vfprintf(stderr, fmt, arg);
132     if (err)
133         fprintf(stderr, ": %s", snd_strerror(err));
134     putc('\n', stderr);
135     va_end(arg);
136 }
137 #endif
138
139 /**************************************************************************
140  *                      MIDI_unixToWindowsDeviceType            [internal]
141  *
142  * return the Windows equivalent to a Unix Device Type
143  *
144  */
145 static  int     MIDI_AlsaToWindowsDeviceType(int type)
146 {
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
154      */
155
156     /* FIXME Is this really the correct equivalence from ALSA to
157        Windows Sound type */
158
159     if (type & SND_SEQ_PORT_TYPE_SYNTH)
160         return MOD_FMSYNTH;
161
162     if (type & (SND_SEQ_PORT_TYPE_DIRECT_SAMPLE|SND_SEQ_PORT_TYPE_SAMPLE))
163         return MOD_SYNTH;
164
165     if (type & (SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION))
166         return MOD_MIDIPORT;
167     
168     ERR("Cannot determine the type (alsa type is %x) of this midi device. Assuming FM Synth\n", type);
169     return MOD_FMSYNTH;
170 }
171
172 /**************************************************************************
173  *                      MIDI_NotifyClient                       [internal]
174  */
175 static DWORD MIDI_NotifyClient(UINT wDevID, WORD wMsg,
176                                DWORD dwParam1, DWORD dwParam2)
177 {
178     DWORD               dwCallBack;
179     UINT                uFlags;
180     HANDLE              hDev;
181     DWORD               dwInstance;
182
183     TRACE("wDevID = %04X wMsg = %d dwParm1 = %04X dwParam2 = %04X\n",
184           wDevID, wMsg, dwParam1, dwParam2);
185
186     switch (wMsg) {
187     case MOM_OPEN:
188     case MOM_CLOSE:
189     case MOM_DONE:
190     case MOM_POSITIONCB:
191         if (wDevID > MODM_NumDevs)
192             return MMSYSERR_BADDEVICEID;
193
194         dwCallBack = MidiOutDev[wDevID].midiDesc.dwCallback;
195         uFlags = MidiOutDev[wDevID].wFlags;
196         hDev = MidiOutDev[wDevID].midiDesc.hMidi;
197         dwInstance = MidiOutDev[wDevID].midiDesc.dwInstance;
198         break;
199
200     case MIM_OPEN:
201     case MIM_CLOSE:
202     case MIM_DATA:
203     case MIM_LONGDATA:
204     case MIM_ERROR:
205     case MIM_LONGERROR:
206     case MIM_MOREDATA:
207         if (wDevID > MIDM_NumDevs)
208             return MMSYSERR_BADDEVICEID;
209
210         dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
211         uFlags = MidiInDev[wDevID].wFlags;
212         hDev = MidiInDev[wDevID].midiDesc.hMidi;
213         dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
214         break;
215     default:
216         WARN("Unsupported MSW-MIDI message %u\n", wMsg);
217         return MMSYSERR_ERROR;
218     }
219
220     return DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2) ?
221         0 : MMSYSERR_ERROR;
222 }
223
224 static int midi_warn = 1;
225 /**************************************************************************
226  *                      midiOpenSeq                             [internal]
227  */
228 static int midiOpenSeq(int create_client)
229 {
230     if (numOpenMidiSeq == 0) {
231         if (snd_seq_open(&midiSeq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0)
232         {
233             if (midi_warn)
234             {
235                 WARN("Error opening ALSA sequencer.\n");
236             }
237             midi_warn = 0;
238             return -1;
239         }
240
241         if (create_client) {
242             /* Setting the client name is the only init to do */
243             snd_seq_set_client_name(midiSeq, "WINE midi driver");
244
245 #if 0 /* FIXME: Is it possible to use a port for READ & WRITE ops */
246             port_in = port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input/Output", SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE,
247                                                                          SND_SEQ_PORT_TYPE_APPLICATION);
248             if (port_out < 0)
249                TRACE("Unable to create output port\n");
250             else
251                TRACE("Outport port created successfully (%d)\n", port_out);
252 #else
253             port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Output", SND_SEQ_PORT_CAP_READ,
254                                                                                  SND_SEQ_PORT_TYPE_APPLICATION);
255             if (port_out < 0)
256                 TRACE("Unable to create output port\n");
257             else
258                 TRACE("Outport port created successfully (%d)\n", port_out);
259
260             port_in = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input", SND_SEQ_PORT_CAP_WRITE,
261                                                                        SND_SEQ_PORT_TYPE_APPLICATION);
262             if (port_in < 0)
263                 TRACE("Unable to create input port\n");
264             else
265                 TRACE("Input port created successfully (%d)\n", port_in);
266 #endif
267        }
268     }
269     numOpenMidiSeq++;
270     return 0;
271 }
272
273 /**************************************************************************
274  *                      midiCloseSeq                            [internal]
275  */
276 static int midiCloseSeq(void)
277 {
278     if (--numOpenMidiSeq == 0) {
279         snd_seq_delete_simple_port(midiSeq, port_out);
280         snd_seq_delete_simple_port(midiSeq, port_in);
281         snd_seq_close(midiSeq);
282         midiSeq = NULL;
283     }
284     return 0;
285 }
286
287 static DWORD WINAPI midRecThread(LPVOID arg)
288 {
289     int npfd;
290     struct pollfd *pfd;
291
292     TRACE("Thread startup\n");
293
294     while(!end_thread) {
295         TRACE("Thread loop\n");
296         npfd = snd_seq_poll_descriptors_count(midiSeq, POLLIN);
297         pfd = HeapAlloc(GetProcessHeap(), 0, npfd * sizeof(struct pollfd));
298         snd_seq_poll_descriptors(midiSeq, pfd, npfd, POLLIN);
299
300         /* Check if an event is present */
301         if (poll(pfd, npfd, 250) < 0) {
302             HeapFree(GetProcessHeap(), 0, pfd);
303             continue;
304         }
305
306         /* Note: This definitely does not work.  
307          * while(snd_seq_event_input_pending(midiSeq, 0) > 0) {
308                snd_seq_event_t* ev;
309                snd_seq_event_input(midiSeq, &ev);
310                ....................
311                snd_seq_free_event(ev);
312            }*/
313
314         do {
315             WORD wDevID;
316             snd_seq_event_t* ev;
317             snd_seq_event_input(midiSeq, &ev);
318             /* Find the target device */
319             for (wDevID = 0; wDevID < MIDM_NumDevs; wDevID++)
320                 if ( (ev->source.client == MidiInDev[wDevID].addr.client) && (ev->source.port == MidiInDev[wDevID].addr.port) )
321                     break;
322             if ((wDevID == MIDM_NumDevs) || (MidiInDev[wDevID].state != 1))
323                 FIXME("Unexpected event received, type = %x from %d:%d\n", ev->type, ev->source.client, ev->source.port);
324             else {
325                 DWORD dwTime, toSend = 0;
326                 int value = 0;
327                 /* FIXME: Should use ev->time instead for better accuracy */
328                 dwTime = GetTickCount() - MidiInDev[wDevID].startTime;
329                 TRACE("Event received, type = %x, device = %d\n", ev->type, wDevID);
330                 switch(ev->type)
331                 {
332                 case SND_SEQ_EVENT_NOTEOFF:
333                     toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_OFF | ev->data.control.channel;
334                     break;
335                 case SND_SEQ_EVENT_NOTEON:
336                     toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_ON | ev->data.control.channel;
337                     break;
338                 case SND_SEQ_EVENT_KEYPRESS:
339                     toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_PRESSURE | ev->data.control.channel;
340                     break;
341                 case SND_SEQ_EVENT_CONTROLLER: 
342                     toSend = (ev->data.control.value << 16) | (ev->data.control.param << 8) | MIDI_CMD_CONTROL | ev->data.control.channel;
343                     break;
344                 case SND_SEQ_EVENT_PITCHBEND:
345                     value = ev->data.control.value + 0x2000;
346                     toSend = (((value >> 7) & 0x7f) << 16) | ((value & 0x7f) << 8) | MIDI_CMD_BENDER | ev->data.control.channel;
347                     break;
348                 case SND_SEQ_EVENT_PGMCHANGE:
349                     toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_PGM_CHANGE | ev->data.control.channel;
350                     break;
351                 case SND_SEQ_EVENT_CHANPRESS:
352                     toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_CHANNEL_PRESSURE | ev->data.control.channel;
353                     break;
354                 case SND_SEQ_EVENT_CLOCK:
355                     toSend = 0xF8;
356                     break;
357                 case SND_SEQ_EVENT_START:
358                     toSend = 0xFA;
359                     break;
360                 case SND_SEQ_EVENT_CONTINUE:
361                     toSend = 0xFB;
362                     break;
363                 case SND_SEQ_EVENT_STOP:
364                     toSend = 0xFC;
365                     break;
366                 case SND_SEQ_EVENT_SONGPOS:
367                     toSend = (((ev->data.control.value >> 7) & 0x7f) << 16) | ((ev->data.control.value & 0x7f) << 8) | 0xF2;
368                     break;
369                 case SND_SEQ_EVENT_SONGSEL:
370                   toSend = ((ev->data.control.value & 0x7f) << 8) | 0xF3;
371                     break;
372                 case SND_SEQ_EVENT_RESET:
373                     toSend = 0xFF;
374                     break;
375                 case SND_SEQ_EVENT_SYSEX:
376                     {
377                         int len = ev->data.ext.len;
378                         LPBYTE ptr = (BYTE*) ev->data.ext.ptr;
379                         LPMIDIHDR lpMidiHdr;
380
381                         /* FIXME: Should handle sysex greater than lpMidiHdr->dwBufferLength */
382                         EnterCriticalSection(&crit_sect);
383                         if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
384                             if (lpMidiHdr->dwBytesRecorded + len <= lpMidiHdr->dwBufferLength) {
385                                 memcpy(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded, ptr, len);
386                                 lpMidiHdr->dwBytesRecorded += len;
387                                 if (*(ptr + (len-1)) == 0xF7) {
388                                     lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
389                                     lpMidiHdr->dwFlags |= MHDR_DONE;
390                                     MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)lpMidiHdr->lpNext;
391                                     if (MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD)lpMidiHdr, dwTime) != MMSYSERR_NOERROR)
392                                         WARN("Couldn't notify client\n");
393                                 }
394                             } else
395                                 FIXME("No enough space in the buffer to store sysex!\n");
396                         } else
397                             FIXME("Sysex received but no buffer to store it!\n");
398                         LeaveCriticalSection(&crit_sect);
399                     }
400                     break;
401                 case SND_SEQ_EVENT_SENSING:
402                     /* Noting to do */
403                     break;
404                 default:
405                     FIXME("Unhandled event received, type = %x\n", ev->type);
406                     break;
407                 }
408                 if (toSend != 0) {
409                     TRACE("Sending event %08x (from %d %d)\n", toSend, ev->source.client, ev->source.port);
410                     if (MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime) != MMSYSERR_NOERROR) {
411                         WARN("Couldn't notify client\n");
412                     }
413                 }
414             }
415             snd_seq_free_event(ev);
416         } while(snd_seq_event_input_pending(midiSeq, 0) > 0);
417         
418         HeapFree(GetProcessHeap(), 0, pfd);
419     }
420     return 0;
421 }
422
423 /**************************************************************************
424  *                              midGetDevCaps                   [internal]
425  */
426 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
427 {
428     TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
429
430     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
431     if (lpCaps == NULL)         return MMSYSERR_INVALPARAM;
432
433     memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
434
435     return MMSYSERR_NOERROR;
436 }
437
438
439 /**************************************************************************
440  *                      midOpen                                 [internal]
441  */
442 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
443 {
444     TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
445
446     if (lpDesc == NULL) {
447         WARN("Invalid Parameter !\n");
448         return MMSYSERR_INVALPARAM;
449     }
450
451     /* FIXME :
452      *  how to check that content of lpDesc is correct ?
453      */
454     if (wDevID >= MIDM_NumDevs) {
455         WARN("wDevID too large (%u) !\n", wDevID);
456         return MMSYSERR_BADDEVICEID;
457     }
458     if (MidiInDev[wDevID].state == -1) {        
459         WARN("device disabled\n");
460         return MIDIERR_NODEVICE;
461     }
462     if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
463         WARN("device already open !\n");
464         return MMSYSERR_ALLOCATED;
465     }
466     if ((dwFlags & MIDI_IO_STATUS) != 0) {
467         WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
468         dwFlags &= ~MIDI_IO_STATUS;
469     }
470     if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
471         FIXME("Bad dwFlags\n");
472         return MMSYSERR_INVALFLAG;
473     }
474
475     if (midiOpenSeq(1) < 0) {
476         return MMSYSERR_ERROR;
477     }
478
479     /* Connect our app port to the device port */
480     if (snd_seq_connect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port) < 0)
481         return MMSYSERR_NOTENABLED;
482
483     TRACE("input port connected %d %d %d\n",port_in,MidiInDev[wDevID].addr.client,MidiInDev[wDevID].addr.port);
484
485     if (numStartedMidiIn++ == 0) {
486         end_thread = 0;
487         hThread = CreateThread(NULL, 0, midRecThread, NULL, 0, NULL);
488         if (!hThread) {
489             numStartedMidiIn = 0;
490             WARN("Couldn't create thread for midi-in\n");
491             midiCloseSeq();
492             return MMSYSERR_ERROR;
493         }
494         SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
495         TRACE("Created thread for midi-in\n");
496     }
497
498     MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
499
500     MidiInDev[wDevID].lpQueueHdr = NULL;
501     MidiInDev[wDevID].dwTotalPlayed = 0;
502     MidiInDev[wDevID].bufsize = 0x3FFF;
503     MidiInDev[wDevID].midiDesc = *lpDesc;
504     MidiInDev[wDevID].state = 0;
505     MidiInDev[wDevID].incLen = 0;
506     MidiInDev[wDevID].startTime = 0;
507
508     if (MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
509         WARN("can't notify client !\n");
510         return MMSYSERR_INVALPARAM;
511     }
512     return MMSYSERR_NOERROR;
513 }
514
515 /**************************************************************************
516  *                      midClose                                [internal]
517  */
518 static DWORD midClose(WORD wDevID)
519 {
520     int         ret = MMSYSERR_NOERROR;
521
522     TRACE("(%04X);\n", wDevID);
523
524     if (wDevID >= MIDM_NumDevs) {
525         WARN("wDevID too big (%u) !\n", wDevID);
526         return MMSYSERR_BADDEVICEID;
527     }
528     if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
529         WARN("device not opened !\n");
530         return MMSYSERR_ERROR;
531     }
532     if (MidiInDev[wDevID].lpQueueHdr != 0) {
533         return MIDIERR_STILLPLAYING;
534     }
535
536     if (midiSeq == NULL) {
537         WARN("ooops !\n");
538         return MMSYSERR_ERROR;
539     }
540     if (--numStartedMidiIn == 0) {
541         TRACE("Stopping thread for midi-in\n");
542         end_thread = 1;
543         if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
544             WARN("Thread end not signaled, force termination\n");
545             TerminateThread(hThread, 0);
546         }
547         TRACE("Stopped thread for midi-in\n");
548     }
549
550     snd_seq_disconnect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port);
551     midiCloseSeq();
552
553     MidiInDev[wDevID].bufsize = 0;
554     if (MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
555         WARN("can't notify client !\n");
556         ret = MMSYSERR_INVALPARAM;
557     }
558     MidiInDev[wDevID].midiDesc.hMidi = 0;
559
560     return ret;
561 }
562
563
564 /**************************************************************************
565  *                              midAddBuffer                    [internal]
566  */
567 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
568 {
569     TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
570
571     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
572     if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
573
574     if (lpMidiHdr == NULL)      return MMSYSERR_INVALPARAM;
575     if (sizeof(MIDIHDR) > dwSize) return MMSYSERR_INVALPARAM;
576     if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
577     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
578     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
579
580     EnterCriticalSection(&crit_sect);
581     lpMidiHdr->dwFlags |= MHDR_INQUEUE;
582     if (MidiInDev[wDevID].lpQueueHdr == 0) {
583         MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
584     } else {
585         LPMIDIHDR       ptr;
586
587         for (ptr = MidiInDev[wDevID].lpQueueHdr;
588              ptr->lpNext != 0;
589              ptr = (LPMIDIHDR)ptr->lpNext);
590         ptr->lpNext = (struct midihdr_tag*)lpMidiHdr;
591     }
592     LeaveCriticalSection(&crit_sect);
593
594     return MMSYSERR_NOERROR;
595 }
596
597 /**************************************************************************
598  *                              midPrepare                      [internal]
599  */
600 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
601 {
602     TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
603
604     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
605         lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
606         lpMidiHdr->dwBufferLength >= 0x10000ul)
607         return MMSYSERR_INVALPARAM;
608
609     lpMidiHdr->lpNext = 0;
610     lpMidiHdr->dwFlags |= MHDR_PREPARED;
611     lpMidiHdr->dwBytesRecorded = 0;
612
613     return MMSYSERR_NOERROR;
614 }
615
616 /**************************************************************************
617  *                              midUnprepare                    [internal]
618  */
619 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
620 {
621     TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
622
623     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
624     if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
625
626     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
627         lpMidiHdr->lpData == 0 || lpMidiHdr->dwBufferLength >= 0x10000ul)
628         return MMSYSERR_INVALPARAM;
629
630     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
631     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
632
633     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
634
635     return MMSYSERR_NOERROR;
636 }
637
638 /**************************************************************************
639  *                      midReset                                [internal]
640  */
641 static DWORD midReset(WORD wDevID)
642 {
643     DWORD               dwTime = GetTickCount();
644
645     TRACE("(%04X);\n", wDevID);
646
647     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
648     if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
649
650     EnterCriticalSection(&crit_sect);
651     while (MidiInDev[wDevID].lpQueueHdr) {
652         MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
653         MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
654         /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
655         if (MIDI_NotifyClient(wDevID, MIM_LONGDATA,
656                               (DWORD)MidiInDev[wDevID].lpQueueHdr, dwTime) != MMSYSERR_NOERROR) {
657             WARN("Couldn't notify client\n");
658         }
659         MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)MidiInDev[wDevID].lpQueueHdr->lpNext;
660     }
661     LeaveCriticalSection(&crit_sect);
662
663     return MMSYSERR_NOERROR;
664 }
665
666 /**************************************************************************
667  *                      midStart                                [internal]
668  */
669 static DWORD midStart(WORD wDevID)
670 {
671     TRACE("(%04X);\n", wDevID);
672
673     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
674     if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
675
676     MidiInDev[wDevID].state = 1;
677     MidiInDev[wDevID].startTime = GetTickCount();
678     return MMSYSERR_NOERROR;
679 }
680
681 /**************************************************************************
682  *                      midStop                                 [internal]
683  */
684 static DWORD midStop(WORD wDevID)
685 {
686     TRACE("(%04X);\n", wDevID);
687
688     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
689     if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
690
691     MidiInDev[wDevID].state = 0;
692     return MMSYSERR_NOERROR;
693 }
694
695 /**************************************************************************
696  *                              modGetDevCaps                   [internal]
697  */
698 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
699 {
700     TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
701
702     if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
703     if (lpCaps == NULL)         return MMSYSERR_INVALPARAM;
704
705     memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
706
707     return MMSYSERR_NOERROR;
708 }
709
710 /**************************************************************************
711  *                      modOpen                                 [internal]
712  */
713 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
714 {
715     TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
716     if (lpDesc == NULL) {
717         WARN("Invalid Parameter !\n");
718         return MMSYSERR_INVALPARAM;
719     }
720     if (wDevID >= MODM_NumDevs) {
721         TRACE("MAX_MIDIOUTDRV reached !\n");
722         return MMSYSERR_BADDEVICEID;
723     }
724     if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
725         WARN("device already open !\n");
726         return MMSYSERR_ALLOCATED;
727     }
728     if (!MidiOutDev[wDevID].bEnabled) {
729         WARN("device disabled !\n");
730         return MIDIERR_NODEVICE;
731     }
732     if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
733         WARN("bad dwFlags\n");
734         return MMSYSERR_INVALFLAG;
735     }
736     if (!MidiOutDev[wDevID].bEnabled) {
737         TRACE("disabled wDevID\n");
738         return MMSYSERR_NOTENABLED;
739     }
740
741     MidiOutDev[wDevID].lpExtra = 0;
742
743     switch (MidiOutDev[wDevID].caps.wTechnology) {
744     case MOD_FMSYNTH:
745     case MOD_MIDIPORT:
746     case MOD_SYNTH:
747         if (midiOpenSeq(1) < 0) {
748             return MMSYSERR_ALLOCATED;
749         }
750         break;
751     default:
752         WARN("Technology not supported (yet) %d !\n",
753              MidiOutDev[wDevID].caps.wTechnology);
754         return MMSYSERR_NOTENABLED;
755     }
756
757     MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
758
759     MidiOutDev[wDevID].lpQueueHdr = NULL;
760     MidiOutDev[wDevID].dwTotalPlayed = 0;
761     MidiOutDev[wDevID].bufsize = 0x3FFF;
762     MidiOutDev[wDevID].midiDesc = *lpDesc;
763
764     /* Connect our app port to the device port */
765     if (snd_seq_connect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port) < 0)
766         return MMSYSERR_NOTENABLED;
767     
768     if (MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
769         WARN("can't notify client !\n");
770         return MMSYSERR_INVALPARAM;
771     }
772     TRACE("Successful !\n");
773     return MMSYSERR_NOERROR;
774 }
775
776
777 /**************************************************************************
778  *                      modClose                                [internal]
779  */
780 static DWORD modClose(WORD wDevID)
781 {
782     int ret = MMSYSERR_NOERROR;
783
784     TRACE("(%04X);\n", wDevID);
785
786     if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
787         WARN("device not opened !\n");
788         return MMSYSERR_ERROR;
789     }
790     /* FIXME: should test that no pending buffer is still in the queue for
791      * playing */
792
793     if (midiSeq == NULL) {
794         WARN("can't close !\n");
795         return MMSYSERR_ERROR;
796     }
797
798     switch (MidiOutDev[wDevID].caps.wTechnology) {
799     case MOD_FMSYNTH:
800     case MOD_MIDIPORT:
801     case MOD_SYNTH:
802         snd_seq_disconnect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
803         midiCloseSeq();
804         break;
805     default:
806         WARN("Technology not supported (yet) %d !\n",
807              MidiOutDev[wDevID].caps.wTechnology);
808         return MMSYSERR_NOTENABLED;
809     }
810
811     HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
812     MidiOutDev[wDevID].lpExtra = 0;
813  
814     MidiOutDev[wDevID].bufsize = 0;
815     if (MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
816         WARN("can't notify client !\n");
817         ret = MMSYSERR_INVALPARAM;
818     }
819     MidiOutDev[wDevID].midiDesc.hMidi = 0;
820     return ret;
821 }
822
823 /**************************************************************************
824  *                      modData                                 [internal]
825  */
826 static DWORD modData(WORD wDevID, DWORD dwParam)
827 {
828     BYTE        evt = LOBYTE(LOWORD(dwParam));
829     BYTE        d1  = HIBYTE(LOWORD(dwParam));
830     BYTE        d2  = LOBYTE(HIWORD(dwParam));
831     
832     TRACE("(%04X, %08X);\n", wDevID, dwParam);
833
834     if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
835     if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
836
837     if (midiSeq == NULL) {
838         WARN("can't play !\n");
839         return MIDIERR_NODEVICE;
840     }
841     switch (MidiOutDev[wDevID].caps.wTechnology) {
842     case MOD_SYNTH:
843     case MOD_MIDIPORT:
844         {
845             int handled = 1; /* Assume event is handled */
846             snd_seq_event_t event;
847             snd_seq_ev_clear(&event);
848             snd_seq_ev_set_direct(&event);
849             snd_seq_ev_set_source(&event, port_out);
850             snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
851             
852             switch (evt & 0xF0) {
853             case MIDI_CMD_NOTE_OFF:
854                 snd_seq_ev_set_noteoff(&event, evt&0x0F, d1, d2);
855                 break;
856             case MIDI_CMD_NOTE_ON:
857                 snd_seq_ev_set_noteon(&event, evt&0x0F, d1, d2);
858                 break;
859             case MIDI_CMD_NOTE_PRESSURE:
860                 snd_seq_ev_set_keypress(&event, evt&0x0F, d1, d2);
861                 break;
862             case MIDI_CMD_CONTROL:
863                 snd_seq_ev_set_controller(&event, evt&0x0F, d1, d2);
864                 break;
865             case MIDI_CMD_BENDER:
866                 snd_seq_ev_set_pitchbend(&event, evt&0x0F, ((WORD)d2 << 7 | (WORD)d1) - 0x2000);
867                 break;
868             case MIDI_CMD_PGM_CHANGE:
869                 snd_seq_ev_set_pgmchange(&event, evt&0x0F, d1);
870                 break;
871             case MIDI_CMD_CHANNEL_PRESSURE:
872                 snd_seq_ev_set_chanpress(&event, evt&0x0F, d1);
873                 break;
874             case MIDI_CMD_COMMON_SYSEX:
875                 switch (evt & 0x0F) {
876                 case 0x00:      /* System Exclusive, don't do it on modData,
877                                  * should require modLongData*/
878                 case 0x01:      /* Undefined */
879                 case 0x04:      /* Undefined. */
880                 case 0x05:      /* Undefined. */
881                 case 0x07:      /* End of Exclusive. */
882                 case 0x09:      /* Undefined. */
883                 case 0x0D:      /* Undefined. */
884                     handled = 0;
885                     break;
886                 case 0x06:      /* Tune Request */
887                 case 0x08:      /* Timing Clock. */
888                 case 0x0A:      /* Start. */
889                 case 0x0B:      /* Continue */
890                 case 0x0C:      /* Stop */
891                 case 0x0E:      /* Active Sensing. */
892                     /* FIXME: Is this function suitable for these purposes
893                        (and also Song Select and Song Position Pointer) */
894                     snd_seq_ev_set_sysex(&event, 1, &evt);
895                     break;
896                 case 0x0F:      /* Reset */
897                                 /* snd_seq_ev_set_sysex(&event, 1, &evt);
898                                    this other way may be better */
899                     {
900                         BYTE reset_sysex_seq[] = {MIDI_CMD_COMMON_SYSEX, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
901                         snd_seq_ev_set_sysex(&event, sizeof(reset_sysex_seq), reset_sysex_seq);
902                     }
903                     break;
904                 case 0x03:      /* Song Select. */
905                     {
906                         BYTE buf[2];
907                         buf[0] = evt;
908                         buf[1] = d1;
909                         snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
910                     }
911                     break;
912                 case 0x02:      /* Song Position Pointer. */
913                     {
914                         BYTE buf[3];
915                         buf[0] = evt;
916                         buf[1] = d1;
917                         buf[2] = d2;
918                         snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
919                     }
920                     break;
921                 }
922                 break;
923             }
924             if (handled)
925                 snd_seq_event_output_direct(midiSeq, &event);
926         }
927         break;
928     default:
929         WARN("Technology not supported (yet) %d !\n",
930              MidiOutDev[wDevID].caps.wTechnology);
931         return MMSYSERR_NOTENABLED;
932     }
933
934     return MMSYSERR_NOERROR;
935 }
936
937 /**************************************************************************
938  *              modLongData                                     [internal]
939  */
940 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
941 {
942     int         len_add = 0;
943     LPBYTE      lpData, lpNewData = NULL;
944     snd_seq_event_t event;
945
946     TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
947
948     /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
949      * but it seems to be used only for midi input.
950      * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
951      */
952     
953     if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
954     if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
955
956     if (midiSeq == NULL) {
957         WARN("can't play !\n");
958         return MIDIERR_NODEVICE;
959     }
960
961     lpData = (LPBYTE) lpMidiHdr->lpData;
962     
963     if (lpData == NULL)
964         return MIDIERR_UNPREPARED;
965     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
966         return MIDIERR_UNPREPARED;
967     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
968         return MIDIERR_STILLPLAYING;
969     lpMidiHdr->dwFlags &= ~MHDR_DONE;
970     lpMidiHdr->dwFlags |= MHDR_INQUEUE;
971
972     /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
973      * data, or can it also contain raw MIDI data, to be split up and sent to
974      * modShortData() ?
975      * If the latest is true, then the following WARNing will fire up
976      */
977     if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
978         WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
979         lpNewData = HeapAlloc(GetProcessHeap(), 0, lpMidiHdr->dwBufferLength + 2);
980     }
981
982     TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
983     TRACE("                 %02X %02X %02X ... %02X %02X %02X\n",
984           lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
985           lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
986
987     switch (MidiOutDev[wDevID].caps.wTechnology) {
988     case MOD_FMSYNTH:
989         /* FIXME: I don't think there is much to do here */
990         break;
991     case MOD_MIDIPORT:
992         if (lpData[0] != 0xF0) {
993             /* Send start of System Exclusive */
994             len_add = 1;
995             lpData[0] = 0xF0;
996             memcpy(lpNewData, lpData, lpMidiHdr->dwBufferLength);
997             WARN("Adding missing 0xF0 marker at the beginning of "
998                  "system exclusive byte stream\n");
999         }
1000         if (lpData[lpMidiHdr->dwBufferLength-1] != 0xF7) {
1001             /* Send end of System Exclusive */
1002             memcpy(lpData + len_add, lpData, lpMidiHdr->dwBufferLength);
1003             lpNewData[lpMidiHdr->dwBufferLength + len_add - 1] = 0xF0;
1004             len_add++;
1005             WARN("Adding missing 0xF7 marker at the end of "
1006                  "system exclusive byte stream\n");
1007         }
1008         snd_seq_ev_clear(&event);
1009         snd_seq_ev_set_direct(&event);
1010         snd_seq_ev_set_source(&event, port_out);
1011         snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1012         TRACE("client = %d port = %d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1013         snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData);
1014         snd_seq_event_output_direct(midiSeq, &event);
1015         if (lpNewData)
1016                 HeapFree(GetProcessHeap(), 0, lpData);
1017         break;
1018     default:
1019         WARN("Technology not supported (yet) %d !\n",
1020              MidiOutDev[wDevID].caps.wTechnology);
1021         HeapFree(GetProcessHeap(), 0, lpNewData);
1022         return MMSYSERR_NOTENABLED;
1023     }
1024
1025     lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1026     lpMidiHdr->dwFlags |= MHDR_DONE;
1027     if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD)lpMidiHdr, 0L) != MMSYSERR_NOERROR) {
1028         WARN("can't notify client !\n");
1029         return MMSYSERR_INVALPARAM;
1030     }
1031     return MMSYSERR_NOERROR;
1032 }
1033
1034 /**************************************************************************
1035  *                      modPrepare                              [internal]
1036  */
1037 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1038 {
1039     TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1040
1041     if (midiSeq == NULL) {
1042         WARN("can't prepare !\n");
1043         return MMSYSERR_NOTENABLED;
1044     }
1045
1046     /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
1047      * asks to prepare MIDIHDR which dwFlags != 0.
1048      * So at least check for the inqueue flag
1049      */
1050     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
1051         lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
1052         lpMidiHdr->dwBufferLength >= 0x10000ul) {
1053         WARN("%p %p %08x %d/%d\n", lpMidiHdr, lpMidiHdr->lpData,
1054                    lpMidiHdr->dwFlags, sizeof(MIDIHDR), dwSize);
1055         return MMSYSERR_INVALPARAM;
1056     }
1057
1058     lpMidiHdr->lpNext = 0;
1059     lpMidiHdr->dwFlags |= MHDR_PREPARED;
1060     lpMidiHdr->dwFlags &= ~MHDR_DONE;
1061     return MMSYSERR_NOERROR;
1062 }
1063
1064 /**************************************************************************
1065  *                              modUnprepare                    [internal]
1066  */
1067 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1068 {
1069     TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1070
1071     if (midiSeq == NULL) {
1072         WARN("can't unprepare !\n");
1073         return MMSYSERR_NOTENABLED;
1074     }
1075
1076     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0)
1077         return MMSYSERR_INVALPARAM;
1078     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1079         return MIDIERR_STILLPLAYING;
1080     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1081     return MMSYSERR_NOERROR;
1082 }
1083
1084 /**************************************************************************
1085  *                      modReset                                [internal]
1086  */
1087 static DWORD modReset(WORD wDevID)
1088 {
1089     unsigned chn;
1090
1091     TRACE("(%04X);\n", wDevID);
1092
1093     if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1094     if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1095
1096     /* stop all notes */
1097     /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1098      * it's channel dependent...
1099      */
1100     for (chn = 0; chn < 16; chn++) {
1101         /* turn off every note */
1102         modData(wDevID, 0x7800 | MIDI_CMD_CONTROL | chn);
1103         /* remove sustain on all channels */
1104         modData(wDevID, (MIDI_CTL_SUSTAIN << 8) | MIDI_CMD_CONTROL | chn);
1105     }
1106     /* FIXME: the LongData buffers must also be returned to the app */
1107     return MMSYSERR_NOERROR;
1108 }
1109
1110
1111 /**************************************************************************
1112  *                      ALSA_AddMidiPort                        [internal]
1113  *
1114  * Helper for ALSA_MidiInit
1115  */
1116 static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t* pinfo, int cap, int type)
1117 {
1118     char midiPortName[MAXPNAMELEN];
1119
1120     if (cap & SND_SEQ_PORT_CAP_WRITE) {
1121         TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1122                                           snd_seq_client_info_get_name(cinfo),
1123                                           snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1124                                           snd_seq_port_info_get_port(pinfo),
1125                                           snd_seq_port_info_get_name(pinfo),
1126                                           type);
1127                 
1128         if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1129             return;
1130         if (!type)
1131             return;
1132
1133         MidiOutDev[MODM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1134
1135         /* Manufac ID. We do not have access to this with soundcard.h
1136          * Does not seem to be a problem, because in mmsystem.h only
1137          * Microsoft's ID is listed.
1138          */
1139         MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1140         MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001;    /* FIXME Product ID  */
1141         /* Product Version. We simply say "1" */
1142         MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1143         MidiOutDev[MODM_NumDevs].caps.wChannelMask   = 0xFFFF;
1144
1145         /* FIXME Do we have this information?
1146          * Assuming the soundcards can handle
1147          * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1148          * not MIDICAPS_CACHE.
1149          */
1150         MidiOutDev[MODM_NumDevs].caps.dwSupport      = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1151
1152         /* Try to use both client and port names, if this is too long take the port name only.
1153            In the second case the port name should be explicit enough due to its big size.
1154          */
1155         if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1156             sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1157         } else {
1158             lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1159             midiPortName[MAXPNAMELEN-1] = 0;
1160         }
1161         MultiByteToWideChar(CP_ACP, 0, midiPortName, -1,
1162                             MidiOutDev[MODM_NumDevs].caps.szPname,
1163                             sizeof(MidiOutDev[MODM_NumDevs].caps.szPname) / sizeof(WCHAR));
1164
1165         MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1166         MidiOutDev[MODM_NumDevs].caps.wVoices     = 16;
1167
1168         /* FIXME Is it possible to know the maximum
1169          * number of simultaneous notes of a soundcard ?
1170          * I believe we don't have this information, but
1171          * it's probably equal or more than wVoices
1172          */
1173         MidiOutDev[MODM_NumDevs].caps.wNotes = 16;
1174         MidiOutDev[MODM_NumDevs].bEnabled    = TRUE;
1175
1176         TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1177             "\tALSA info: midi dev-type=%lx, capa=%lx\n",
1178               MODM_NumDevs, wine_dbgstr_w(MidiOutDev[MODM_NumDevs].caps.szPname),
1179               MidiOutDev[MODM_NumDevs].caps.wTechnology,
1180               MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1181               MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1182               (long)type, (long)0);
1183                 
1184         MODM_NumDevs++;
1185     }
1186     if (cap & SND_SEQ_PORT_CAP_READ) {
1187         TRACE("IN  (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1188                                           snd_seq_client_info_get_name(cinfo),
1189                                           snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1190                                           snd_seq_port_info_get_port(pinfo),
1191                                           snd_seq_port_info_get_name(pinfo),
1192                                           type);
1193                 
1194         if (MIDM_NumDevs >= MAX_MIDIINDRV)
1195             return;
1196         if (!type)
1197             return;
1198
1199         MidiInDev[MIDM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1200
1201         /* Manufac ID. We do not have access to this with soundcard.h
1202          * Does not seem to be a problem, because in mmsystem.h only
1203          * Microsoft's ID is listed.
1204          */
1205         MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1206         MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001;     /* FIXME Product ID  */
1207         /* Product Version. We simply say "1" */
1208         MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1209
1210         /* FIXME Do we have this information?
1211          * Assuming the soundcards can handle
1212          * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1213          * not MIDICAPS_CACHE.
1214          */
1215         MidiInDev[MIDM_NumDevs].caps.dwSupport      = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1216
1217         /* Try to use both client and port names, if this is too long take the port name only.
1218            In the second case the port name should be explicit enough due to its big size.
1219          */
1220         if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1221             sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1222         } else {
1223             lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1224             midiPortName[MAXPNAMELEN-1] = 0;
1225         }
1226         MultiByteToWideChar(CP_ACP, 0, midiPortName, -1,
1227                             MidiInDev[MIDM_NumDevs].caps.szPname,
1228                             sizeof(MidiInDev[MIDM_NumDevs].caps.szPname) / sizeof(WCHAR));
1229         MidiInDev[MIDM_NumDevs].state = 0;
1230
1231         TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1232               "\tALSA info: midi dev-type=%lx, capa=%lx\n",
1233               MIDM_NumDevs, wine_dbgstr_w(MidiInDev[MIDM_NumDevs].caps.szPname),
1234               MidiInDev[MIDM_NumDevs].caps.dwSupport,
1235               (long)type, (long)0);
1236
1237         MIDM_NumDevs++;
1238     }
1239 }
1240
1241 #endif /* HAVE_ALSA */
1242
1243
1244 /*======================================================================*
1245  *                          MIDI entry points                           *
1246  *======================================================================*/
1247
1248 /**************************************************************************
1249  * ALSA_MidiInit                                [internal]
1250  *
1251  * Initializes the MIDI devices information variables
1252  */
1253 LONG ALSA_MidiInit(void)
1254 {
1255 #ifdef HAVE_ALSA
1256     static      BOOL    bInitDone = FALSE;
1257     snd_seq_client_info_t *cinfo;
1258     snd_seq_port_info_t *pinfo;
1259
1260     if (bInitDone)
1261         return TRUE;
1262
1263     TRACE("Initializing the MIDI variables.\n");
1264     bInitDone = TRUE;
1265
1266     /* try to open device */
1267     if (midiOpenSeq(0) == -1) {
1268         return TRUE;
1269     }
1270
1271 #if 0 /* Debug purpose */
1272     snd_lib_error_set_handler(error_handler);
1273 #endif
1274     cinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_client_info_sizeof() );
1275     pinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_port_info_sizeof() );
1276
1277     /* First, search for all internal midi devices */
1278     snd_seq_client_info_set_client(cinfo, -1);
1279     while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1280         snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1281         snd_seq_port_info_set_port(pinfo, -1);
1282         while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1283             int cap = snd_seq_port_info_get_capability(pinfo);
1284             int type = snd_seq_port_info_get_type(pinfo);
1285             if (!(type & SND_SEQ_PORT_TYPE_PORT))
1286                 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1287         }
1288     }
1289
1290     /* Second, search for all external ports */
1291     snd_seq_client_info_set_client(cinfo, -1);
1292     while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1293         snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1294         snd_seq_port_info_set_port(pinfo, -1);
1295         while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1296             int cap = snd_seq_port_info_get_capability(pinfo);
1297             int type = snd_seq_port_info_get_type(pinfo);
1298             if (type & SND_SEQ_PORT_TYPE_PORT)
1299                 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1300         }
1301     }
1302
1303     /* close file and exit */
1304     midiCloseSeq();
1305     HeapFree( GetProcessHeap(), 0, cinfo );
1306     HeapFree( GetProcessHeap(), 0, pinfo );
1307
1308     TRACE("End\n");
1309 #endif
1310     return TRUE;
1311 }
1312
1313 /**************************************************************************
1314  *                      midMessage (WINEOSS.4)
1315  */
1316 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1317                             DWORD dwParam1, DWORD dwParam2)
1318 {
1319     TRACE("(%04X, %04X, %08X, %08X, %08X);\n",
1320           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1321     switch (wMsg) {
1322 #ifdef HAVE_ALSA
1323     case DRVM_INIT:
1324     case DRVM_EXIT:
1325     case DRVM_ENABLE:
1326     case DRVM_DISABLE:
1327         /* FIXME: Pretend this is supported */
1328         return 0;
1329     case MIDM_OPEN:
1330         return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1331     case MIDM_CLOSE:
1332         return midClose(wDevID);
1333     case MIDM_ADDBUFFER:
1334         return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1335     case MIDM_PREPARE:
1336         return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1337     case MIDM_UNPREPARE:
1338         return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1339     case MIDM_GETDEVCAPS:
1340         return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1341     case MIDM_GETNUMDEVS:
1342         return MIDM_NumDevs;
1343     case MIDM_RESET:
1344         return midReset(wDevID);
1345     case MIDM_START:
1346         return midStart(wDevID);
1347     case MIDM_STOP:
1348         return midStop(wDevID);
1349 #endif
1350     default:
1351         TRACE("Unsupported message\n");
1352     }
1353     return MMSYSERR_NOTSUPPORTED;
1354 }
1355
1356 /**************************************************************************
1357  *                              modMessage (WINEOSS.5)
1358  */
1359 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1360                             DWORD dwParam1, DWORD dwParam2)
1361 {
1362     TRACE("(%04X, %04X, %08X, %08X, %08X);\n",
1363           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1364
1365     switch (wMsg) {
1366 #ifdef HAVE_ALSA
1367     case DRVM_INIT:
1368     case DRVM_EXIT:
1369     case DRVM_ENABLE:
1370     case DRVM_DISABLE:
1371         /* FIXME: Pretend this is supported */
1372         return 0;
1373     case MODM_OPEN:
1374         return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1375     case MODM_CLOSE:
1376         return modClose(wDevID);
1377     case MODM_DATA:
1378         return modData(wDevID, dwParam1);
1379     case MODM_LONGDATA:
1380         return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1381     case MODM_PREPARE:
1382         return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1383     case MODM_UNPREPARE:
1384         return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1385     case MODM_GETDEVCAPS:
1386         return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1387     case MODM_GETNUMDEVS:
1388         return MODM_NumDevs;
1389     case MODM_GETVOLUME:
1390         return 0;
1391     case MODM_SETVOLUME:
1392         return 0;
1393     case MODM_RESET:
1394         return modReset(wDevID);
1395 #endif
1396     default:
1397         TRACE("Unsupported message\n");
1398     }
1399     return MMSYSERR_NOTSUPPORTED;
1400 }
1401
1402 /*-----------------------------------------------------------------------*/