dinput: Keep a list of all IDirectInput objects and check it instead of refcount...
[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   splitted 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 << 16) | (ev->data.control.param << 8) | MIDI_CMD_PGM_CHANGE | ev->data.control.channel;
350                     break;
351                 case SND_SEQ_EVENT_CHANPRESS:
352                     toSend = (ev->data.control.value << 16) | (ev->data.control.param << 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         return MMSYSERR_NOTENABLED;
1022     }
1023
1024     lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1025     lpMidiHdr->dwFlags |= MHDR_DONE;
1026     if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD)lpMidiHdr, 0L) != MMSYSERR_NOERROR) {
1027         WARN("can't notify client !\n");
1028         return MMSYSERR_INVALPARAM;
1029     }
1030     return MMSYSERR_NOERROR;
1031 }
1032
1033 /**************************************************************************
1034  *                      modPrepare                              [internal]
1035  */
1036 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1037 {
1038     TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1039
1040     if (midiSeq == NULL) {
1041         WARN("can't prepare !\n");
1042         return MMSYSERR_NOTENABLED;
1043     }
1044
1045     /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
1046      * asks to prepare MIDIHDR which dwFlags != 0.
1047      * So at least check for the inqueue flag
1048      */
1049     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
1050         lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
1051         lpMidiHdr->dwBufferLength >= 0x10000ul) {
1052         WARN("%p %p %08x %d/%d\n", lpMidiHdr, lpMidiHdr->lpData,
1053                    lpMidiHdr->dwFlags, sizeof(MIDIHDR), dwSize);
1054         return MMSYSERR_INVALPARAM;
1055     }
1056
1057     lpMidiHdr->lpNext = 0;
1058     lpMidiHdr->dwFlags |= MHDR_PREPARED;
1059     lpMidiHdr->dwFlags &= ~MHDR_DONE;
1060     return MMSYSERR_NOERROR;
1061 }
1062
1063 /**************************************************************************
1064  *                              modUnprepare                    [internal]
1065  */
1066 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1067 {
1068     TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1069
1070     if (midiSeq == NULL) {
1071         WARN("can't unprepare !\n");
1072         return MMSYSERR_NOTENABLED;
1073     }
1074
1075     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0)
1076         return MMSYSERR_INVALPARAM;
1077     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1078         return MIDIERR_STILLPLAYING;
1079     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1080     return MMSYSERR_NOERROR;
1081 }
1082
1083 /**************************************************************************
1084  *                      modReset                                [internal]
1085  */
1086 static DWORD modReset(WORD wDevID)
1087 {
1088     unsigned chn;
1089
1090     TRACE("(%04X);\n", wDevID);
1091
1092     if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1093     if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1094
1095     /* stop all notes */
1096     /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1097      * it's channel dependent...
1098      */
1099     for (chn = 0; chn < 16; chn++) {
1100         /* turn off every note */
1101         modData(wDevID, 0x7800 | MIDI_CMD_CONTROL | chn);
1102         /* remove sustain on all channels */
1103         modData(wDevID, (MIDI_CTL_SUSTAIN << 8) | MIDI_CMD_CONTROL | chn);
1104     }
1105     /* FIXME: the LongData buffers must also be returned to the app */
1106     return MMSYSERR_NOERROR;
1107 }
1108
1109
1110 /**************************************************************************
1111  *                      ALSA_AddMidiPort                        [internal]
1112  *
1113  * Helper for ALSA_MidiInit
1114  */
1115 static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t* pinfo, int cap, int type)
1116 {
1117     if (cap & SND_SEQ_PORT_CAP_WRITE) {
1118         TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1119                                           snd_seq_client_info_get_name(cinfo),
1120                                           snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1121                                           snd_seq_port_info_get_port(pinfo),
1122                                           snd_seq_port_info_get_name(pinfo),
1123                                           type);
1124                 
1125         if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1126             return;
1127         if (!type)
1128             return;
1129
1130         memcpy(&MidiOutDev[MODM_NumDevs].addr, snd_seq_port_info_get_addr(pinfo), sizeof(snd_seq_addr_t));
1131                 
1132         /* Manufac ID. We do not have access to this with soundcard.h
1133          * Does not seem to be a problem, because in mmsystem.h only
1134          * Microsoft's ID is listed.
1135          */
1136         MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1137         MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001;    /* FIXME Product ID  */
1138         /* Product Version. We simply say "1" */
1139         MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1140         MidiOutDev[MODM_NumDevs].caps.wChannelMask   = 0xFFFF;
1141
1142         /* FIXME Do we have this information?
1143          * Assuming the soundcards can handle
1144          * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1145          * not MIDICAPS_CACHE.
1146          */
1147         MidiOutDev[MODM_NumDevs].caps.dwSupport      = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1148         MultiByteToWideChar(CP_ACP, 0, snd_seq_client_info_get_name(cinfo), -1,
1149                             MidiOutDev[MODM_NumDevs].caps.szPname,
1150                             sizeof(MidiOutDev[MODM_NumDevs].caps.szPname) / sizeof(WCHAR));
1151
1152         MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1153         MidiOutDev[MODM_NumDevs].caps.wVoices     = 16;
1154
1155         /* FIXME Is it possible to know the maximum
1156          * number of simultaneous notes of a soundcard ?
1157          * I believe we don't have this information, but
1158          * it's probably equal or more than wVoices
1159          */
1160         MidiOutDev[MODM_NumDevs].caps.wNotes = 16;
1161         MidiOutDev[MODM_NumDevs].bEnabled    = TRUE;
1162
1163         TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1164             "\tALSA info: midi dev-type=%lx, capa=%lx\n",
1165               MODM_NumDevs, wine_dbgstr_w(MidiOutDev[MODM_NumDevs].caps.szPname),
1166               MidiOutDev[MODM_NumDevs].caps.wTechnology,
1167               MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1168               MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1169               (long)type, (long)0);
1170                 
1171         MODM_NumDevs++;
1172     }
1173     if (cap & SND_SEQ_PORT_CAP_READ) {
1174         TRACE("IN  (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1175                                           snd_seq_client_info_get_name(cinfo),
1176                                           snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1177                                           snd_seq_port_info_get_port(pinfo),
1178                                           snd_seq_port_info_get_name(pinfo),
1179                                           type);
1180                 
1181         if (MIDM_NumDevs >= MAX_MIDIINDRV)
1182             return;
1183         if (!type)
1184             return;
1185
1186         memcpy(&MidiInDev[MIDM_NumDevs].addr, snd_seq_port_info_get_addr(pinfo), sizeof(snd_seq_addr_t));
1187                 
1188         /* Manufac ID. We do not have access to this with soundcard.h
1189          * Does not seem to be a problem, because in mmsystem.h only
1190          * Microsoft's ID is listed.
1191          */
1192         MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1193         MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001;     /* FIXME Product ID  */
1194         /* Product Version. We simply say "1" */
1195         MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1196
1197         /* FIXME Do we have this information?
1198          * Assuming the soundcards can handle
1199          * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1200          * not MIDICAPS_CACHE.
1201          */
1202         MidiInDev[MIDM_NumDevs].caps.dwSupport      = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1203         MultiByteToWideChar(CP_ACP, 0, snd_seq_client_info_get_name(cinfo), -1,
1204                             MidiInDev[MIDM_NumDevs].caps.szPname,
1205                             sizeof(MidiInDev[MIDM_NumDevs].caps.szPname) / sizeof(WCHAR));
1206         MidiInDev[MIDM_NumDevs].state = 0;
1207
1208         TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1209               "\tALSA info: midi dev-type=%lx, capa=%lx\n",
1210               MIDM_NumDevs, wine_dbgstr_w(MidiInDev[MIDM_NumDevs].caps.szPname),
1211               MidiInDev[MIDM_NumDevs].caps.dwSupport,
1212               (long)type, (long)0);
1213
1214         MIDM_NumDevs++;
1215     }
1216 }
1217
1218 #endif /* HAVE_ALSA */
1219
1220
1221 /*======================================================================*
1222  *                          MIDI entry points                           *
1223  *======================================================================*/
1224
1225 /**************************************************************************
1226  * ALSA_MidiInit                                [internal]
1227  *
1228  * Initializes the MIDI devices information variables
1229  */
1230 LONG ALSA_MidiInit(void)
1231 {
1232 #ifdef HAVE_ALSA
1233     static      BOOL    bInitDone = FALSE;
1234     snd_seq_client_info_t *cinfo;
1235     snd_seq_port_info_t *pinfo;
1236
1237     if (bInitDone)
1238         return TRUE;
1239
1240     TRACE("Initializing the MIDI variables.\n");
1241     bInitDone = TRUE;
1242
1243     /* try to open device */
1244     if (midiOpenSeq(0) == -1) {
1245         return TRUE;
1246     }
1247
1248 #if 0 /* Debug purpose */
1249     snd_lib_error_set_handler(error_handler);
1250 #endif
1251     
1252     snd_seq_client_info_alloca(&cinfo);
1253     snd_seq_port_info_alloca(&pinfo);
1254
1255     /* First, search for all internal midi devices */
1256     snd_seq_client_info_set_client(cinfo, -1);
1257     while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1258         snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1259         snd_seq_port_info_set_port(pinfo, -1);
1260         while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1261             int cap = snd_seq_port_info_get_capability(pinfo);
1262             int type = snd_seq_port_info_get_type(pinfo);
1263             if (!(type & SND_SEQ_PORT_TYPE_PORT))
1264                 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1265         }
1266     }
1267
1268     /* Second, search for all external ports */
1269     snd_seq_client_info_set_client(cinfo, -1);
1270     while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1271         snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1272         snd_seq_port_info_set_port(pinfo, -1);
1273         while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1274             int cap = snd_seq_port_info_get_capability(pinfo);
1275             int type = snd_seq_port_info_get_type(pinfo);
1276             if (type & SND_SEQ_PORT_TYPE_PORT)
1277                 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1278         }
1279     }
1280
1281     /* close file and exit */
1282     midiCloseSeq();
1283
1284     TRACE("End\n");
1285 #endif
1286     return TRUE;
1287 }
1288
1289 /**************************************************************************
1290  *                      midMessage (WINEOSS.4)
1291  */
1292 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1293                             DWORD dwParam1, DWORD dwParam2)
1294 {
1295     TRACE("(%04X, %04X, %08X, %08X, %08X);\n",
1296           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1297     switch (wMsg) {
1298 #ifdef HAVE_ALSA
1299     case DRVM_INIT:
1300     case DRVM_EXIT:
1301     case DRVM_ENABLE:
1302     case DRVM_DISABLE:
1303         /* FIXME: Pretend this is supported */
1304         return 0;
1305     case MIDM_OPEN:
1306         return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1307     case MIDM_CLOSE:
1308         return midClose(wDevID);
1309     case MIDM_ADDBUFFER:
1310         return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1311     case MIDM_PREPARE:
1312         return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1313     case MIDM_UNPREPARE:
1314         return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1315     case MIDM_GETDEVCAPS:
1316         return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1317     case MIDM_GETNUMDEVS:
1318         return MIDM_NumDevs;
1319     case MIDM_RESET:
1320         return midReset(wDevID);
1321     case MIDM_START:
1322         return midStart(wDevID);
1323     case MIDM_STOP:
1324         return midStop(wDevID);
1325 #endif
1326     default:
1327         TRACE("Unsupported message\n");
1328     }
1329     return MMSYSERR_NOTSUPPORTED;
1330 }
1331
1332 /**************************************************************************
1333  *                              modMessage (WINEOSS.5)
1334  */
1335 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1336                             DWORD dwParam1, DWORD dwParam2)
1337 {
1338     TRACE("(%04X, %04X, %08X, %08X, %08X);\n",
1339           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1340
1341     switch (wMsg) {
1342 #ifdef HAVE_ALSA
1343     case DRVM_INIT:
1344     case DRVM_EXIT:
1345     case DRVM_ENABLE:
1346     case DRVM_DISABLE:
1347         /* FIXME: Pretend this is supported */
1348         return 0;
1349     case MODM_OPEN:
1350         return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1351     case MODM_CLOSE:
1352         return modClose(wDevID);
1353     case MODM_DATA:
1354         return modData(wDevID, dwParam1);
1355     case MODM_LONGDATA:
1356         return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1357     case MODM_PREPARE:
1358         return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1359     case MODM_UNPREPARE:
1360         return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1361     case MODM_GETDEVCAPS:
1362         return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1363     case MODM_GETNUMDEVS:
1364         return MODM_NumDevs;
1365     case MODM_GETVOLUME:
1366         return 0;
1367     case MODM_SETVOLUME:
1368         return 0;
1369     case MODM_RESET:
1370         return modReset(wDevID);
1371 #endif
1372     default:
1373         TRACE("Unsupported message\n");
1374     }
1375     return MMSYSERR_NOTSUPPORTED;
1376 }
1377
1378 /*-----------------------------------------------------------------------*/