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