usp10: Apply lookups in order over the whole range of glyphs instead of individually...
[wine] / dlls / winealsa.drv / midi.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  * Sample MIDI Wine Driver for ALSA (basically Linux)
5  *
6  * Copyright 1994       Martin Ayotte
7  * Copyright 1998       Luiz Otavio L. Zorzella (init procedures)
8  * Copyright 1998/1999  Eric POUECH :
9  *              98/7    changes for making this MIDI driver work on OSS
10  *                      current support is limited to MIDI ports of OSS systems
11  *              98/9    rewriting MCI code for MIDI
12  *              98/11   split in midi.c and mcimidi.c
13  * Copyright 2003      Christian Costa :
14  *                     ALSA port
15  *
16  * This library is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU Lesser General Public
18  * License as published by the Free Software Foundation; either
19  * version 2.1 of the License, or (at your option) any later version.
20  *
21  * This library is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24  * Lesser General Public License for more details.
25  *
26  * You should have received a copy of the GNU Lesser General Public
27  * License along with this library; if not, write to the Free Software
28  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29  *
30  * TODO: Finish midi record
31  *
32  */
33
34 #include "config.h"
35
36 #include <string.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #ifdef HAVE_UNISTD_H
40 # include <unistd.h>
41 #endif
42 #include <fcntl.h>
43 #include <errno.h>
44
45 #include "windef.h"
46 #include "winbase.h"
47 #include "wingdi.h"
48 #include "winuser.h"
49 #include "winnls.h"
50 #include "mmddk.h"
51 #include "mmreg.h"
52 #include "dsound.h"
53 #include "wine/debug.h"
54
55 #include <alsa/asoundlib.h>
56
57 WINE_DEFAULT_DEBUG_CHANNEL(midi);
58
59 #ifndef SND_SEQ_PORT_TYPE_PORT
60 #define SND_SEQ_PORT_TYPE_PORT (1<<19)  /* Appears in version 1.0.12rc1 */
61 #endif
62
63 typedef struct {
64     int                 state;                  /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
65     DWORD               bufsize;
66     MIDIOPENDESC        midiDesc;
67     WORD                wFlags;
68     LPMIDIHDR           lpQueueHdr;
69     DWORD               dwTotalPlayed;
70     unsigned char       incoming[3];
71     unsigned char       incPrev;
72     char                incLen;
73     DWORD               startTime;
74     MIDIINCAPSW         caps;
75     snd_seq_addr_t      addr;
76 } WINE_MIDIIN;
77
78 typedef struct {
79     BOOL                bEnabled;
80     DWORD               bufsize;
81     MIDIOPENDESC        midiDesc;
82     WORD                wFlags;
83     LPMIDIHDR           lpQueueHdr;
84     DWORD               dwTotalPlayed;
85     void*               lpExtra;                /* according to port type (MIDI, FM...), extra data when needed */
86     MIDIOUTCAPSW        caps;
87     snd_seq_addr_t      addr;
88 } WINE_MIDIOUT;
89
90 static WINE_MIDIIN      MidiInDev [MAX_MIDIINDRV ];
91 static WINE_MIDIOUT     MidiOutDev[MAX_MIDIOUTDRV];
92
93 /* this is the total number of MIDI out devices found (synth and port) */
94 static  int             MODM_NumDevs = 0;
95 /* this is the total number of MIDI out devices found */
96 static  int             MIDM_NumDevs = 0;
97
98 static  snd_seq_t*      midiSeq = NULL;
99 static  int             numOpenMidiSeq = 0;
100 static  int             numStartedMidiIn = 0;
101
102 static int port_in;
103 static int port_out;
104
105 static CRITICAL_SECTION crit_sect;   /* protects all MidiIn buffer queues */
106 static CRITICAL_SECTION_DEBUG critsect_debug =
107 {
108     0, 0, &crit_sect,
109     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
110       0, 0, { (DWORD_PTR)(__FILE__ ": crit_sect") }
111 };
112 static CRITICAL_SECTION crit_sect = { &critsect_debug, -1, 0, 0, 0, 0 };
113
114 static int end_thread;
115 static HANDLE hThread;
116
117 /*======================================================================*
118  *                  Low level MIDI implementation                       *
119  *======================================================================*/
120
121 static int midiOpenSeq(int);
122 static int midiCloseSeq(void);
123
124 #if 0 /* Debug Purpose */
125 static void error_handler(const char* file, int line, const char* function, int err, const char* fmt, ...)
126 {
127     va_list arg;
128     if (err == ENOENT)
129         return;
130     va_start(arg, fmt);
131     fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function);
132     vfprintf(stderr, fmt, arg);
133     if (err)
134         fprintf(stderr, ": %s", snd_strerror(err));
135     putc('\n', stderr);
136     va_end(arg);
137 }
138 #endif
139
140 /**************************************************************************
141  *                      MIDI_unixToWindowsDeviceType            [internal]
142  *
143  * return the Windows equivalent to a Unix Device Type
144  *
145  */
146 static  int     MIDI_AlsaToWindowsDeviceType(unsigned int type)
147 {
148     /* MOD_MIDIPORT     output port
149      * MOD_SYNTH        generic internal synth
150      * MOD_SQSYNTH      square wave internal synth
151      * MOD_FMSYNTH      FM internal synth
152      * MOD_MAPPER       MIDI mapper
153      * MOD_WAVETABLE    hardware wavetable internal synth
154      * MOD_SWSYNTH      software internal synth
155      */
156
157     /* FIXME Is this really the correct equivalence from ALSA to
158        Windows Sound type? */
159
160     if (type & SND_SEQ_PORT_TYPE_SYNTH)
161         return MOD_FMSYNTH;
162
163     if (type & (SND_SEQ_PORT_TYPE_DIRECT_SAMPLE|SND_SEQ_PORT_TYPE_SAMPLE))
164         return MOD_SYNTH;
165
166     if (type & (SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION))
167         return MOD_MIDIPORT;
168     
169     ERR("Cannot determine the type (alsa type is %x) of this midi device. Assuming FM Synth\n", type);
170     return MOD_FMSYNTH;
171 }
172
173 /**************************************************************************
174  *                      MIDI_NotifyClient                       [internal]
175  */
176 static void MIDI_NotifyClient(UINT wDevID, WORD wMsg,
177                               DWORD_PTR dwParam1, DWORD_PTR dwParam2)
178 {
179     DWORD               dwCallBack;
180     UINT                uFlags;
181     HANDLE              hDev;
182     DWORD               dwInstance;
183
184     TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
185           wDevID, wMsg, dwParam1, dwParam2);
186
187     switch (wMsg) {
188     case MOM_OPEN:
189     case MOM_CLOSE:
190     case MOM_DONE:
191     case MOM_POSITIONCB:
192         if (wDevID > MODM_NumDevs) return;
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) return;
208
209         dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
210         uFlags = MidiInDev[wDevID].wFlags;
211         hDev = MidiInDev[wDevID].midiDesc.hMidi;
212         dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
213         break;
214     default:
215         ERR("Unsupported MSW-MIDI message %u\n", wMsg);
216         return;
217     }
218
219     DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2);
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 = HeapAlloc(GetProcessHeap(), 0, npfd * sizeof(struct pollfd));
296         snd_seq_poll_descriptors(midiSeq, pfd, npfd, POLLIN);
297
298         /* Check if an 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                 int value = 0;
325                 /* FIXME: Should use ev->time instead for better accuracy */
326                 dwTime = GetTickCount() - MidiInDev[wDevID].startTime;
327                 TRACE("Event received, type = %x, device = %d\n", ev->type, wDevID);
328                 switch(ev->type)
329                 {
330                 case SND_SEQ_EVENT_NOTEOFF:
331                     toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_OFF | ev->data.control.channel;
332                     break;
333                 case SND_SEQ_EVENT_NOTEON:
334                     toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_ON | ev->data.control.channel;
335                     break;
336                 case SND_SEQ_EVENT_KEYPRESS:
337                     toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_PRESSURE | ev->data.control.channel;
338                     break;
339                 case SND_SEQ_EVENT_CONTROLLER: 
340                     toSend = (ev->data.control.value << 16) | (ev->data.control.param << 8) | MIDI_CMD_CONTROL | ev->data.control.channel;
341                     break;
342                 case SND_SEQ_EVENT_PITCHBEND:
343                     value = ev->data.control.value + 0x2000;
344                     toSend = (((value >> 7) & 0x7f) << 16) | ((value & 0x7f) << 8) | MIDI_CMD_BENDER | ev->data.control.channel;
345                     break;
346                 case SND_SEQ_EVENT_PGMCHANGE:
347                     toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_PGM_CHANGE | ev->data.control.channel;
348                     break;
349                 case SND_SEQ_EVENT_CHANPRESS:
350                     toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_CHANNEL_PRESSURE | ev->data.control.channel;
351                     break;
352                 case SND_SEQ_EVENT_CLOCK:
353                     toSend = 0xF8;
354                     break;
355                 case SND_SEQ_EVENT_START:
356                     toSend = 0xFA;
357                     break;
358                 case SND_SEQ_EVENT_CONTINUE:
359                     toSend = 0xFB;
360                     break;
361                 case SND_SEQ_EVENT_STOP:
362                     toSend = 0xFC;
363                     break;
364                 case SND_SEQ_EVENT_SONGPOS:
365                     toSend = (((ev->data.control.value >> 7) & 0x7f) << 16) | ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_SONG_POS;
366                     break;
367                 case SND_SEQ_EVENT_SONGSEL:
368                   toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_SONG_SELECT;
369                     break;
370                 case SND_SEQ_EVENT_RESET:
371                     toSend = 0xFF;
372                     break;
373                 case SND_SEQ_EVENT_QFRAME:
374                   toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_MTC_QUARTER;
375                     break;
376                 case SND_SEQ_EVENT_SYSEX:
377                     {
378                         int pos = 0;
379                         int len = ev->data.ext.len;
380                         LPBYTE ptr = ev->data.ext.ptr;
381                         LPMIDIHDR lpMidiHdr;
382
383                         EnterCriticalSection(&crit_sect);
384                         while (len) {
385                             if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
386                                 int copylen = min(len, lpMidiHdr->dwBufferLength - lpMidiHdr->dwBytesRecorded);
387                                 memcpy(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded, ptr + pos, copylen);
388                                 lpMidiHdr->dwBytesRecorded += copylen;
389                                 len -= copylen;
390                                 pos += copylen;
391                                 /* We check if we reach the end of buffer or the end of sysex before notifying
392                                  * to handle the case where ALSA split the sysex into several events */
393                                 if ((lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) ||
394                                     (*(BYTE*)(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded - 1) == 0xF7)) {
395                                     lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
396                                     lpMidiHdr->dwFlags |= MHDR_DONE;
397                                     MidiInDev[wDevID].lpQueueHdr = lpMidiHdr->lpNext;
398                                     MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime);
399                                 }
400                             } else {
401                                 FIXME("Sysex data received but no buffer to store it!\n");
402                                 break;
403                             }
404                         }
405                         LeaveCriticalSection(&crit_sect);
406                     }
407                     break;
408                 case SND_SEQ_EVENT_SENSING:
409                     /* Noting to do */
410                     break;
411                 default:
412                     FIXME("Unhandled event received, type = %x\n", ev->type);
413                     break;
414                 }
415                 if (toSend != 0) {
416                     TRACE("Sending event %08x (from %d %d)\n", toSend, ev->source.client, ev->source.port);
417                     MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime);
418                 }
419             }
420             snd_seq_free_event(ev);
421         } while(snd_seq_event_input_pending(midiSeq, 0) > 0);
422         
423         HeapFree(GetProcessHeap(), 0, pfd);
424     }
425     return 0;
426 }
427
428 /**************************************************************************
429  *                              midGetDevCaps                   [internal]
430  */
431 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
432 {
433     TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
434
435     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
436     if (lpCaps == NULL)         return MMSYSERR_INVALPARAM;
437
438     memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
439
440     return MMSYSERR_NOERROR;
441 }
442
443
444 /**************************************************************************
445  *                      midOpen                                 [internal]
446  */
447 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
448 {
449     TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
450
451     if (lpDesc == NULL) {
452         WARN("Invalid Parameter !\n");
453         return MMSYSERR_INVALPARAM;
454     }
455
456     /* FIXME :
457      *  how to check that content of lpDesc is correct ?
458      */
459     if (wDevID >= MIDM_NumDevs) {
460         WARN("wDevID too large (%u) !\n", wDevID);
461         return MMSYSERR_BADDEVICEID;
462     }
463     if (MidiInDev[wDevID].state == -1) {        
464         WARN("device disabled\n");
465         return MIDIERR_NODEVICE;
466     }
467     if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
468         WARN("device already open !\n");
469         return MMSYSERR_ALLOCATED;
470     }
471     if ((dwFlags & MIDI_IO_STATUS) != 0) {
472         WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
473         dwFlags &= ~MIDI_IO_STATUS;
474     }
475     if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
476         FIXME("Bad dwFlags\n");
477         return MMSYSERR_INVALFLAG;
478     }
479
480     if (midiOpenSeq(1) < 0) {
481         return MMSYSERR_ERROR;
482     }
483
484     /* Connect our app port to the device port */
485     if (snd_seq_connect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port) < 0)
486         return MMSYSERR_NOTENABLED;
487
488     TRACE("input port connected %d %d %d\n",port_in,MidiInDev[wDevID].addr.client,MidiInDev[wDevID].addr.port);
489
490     if (numStartedMidiIn++ == 0) {
491         end_thread = 0;
492         hThread = CreateThread(NULL, 0, midRecThread, NULL, 0, NULL);
493         if (!hThread) {
494             numStartedMidiIn = 0;
495             WARN("Couldn't create thread for midi-in\n");
496             midiCloseSeq();
497             return MMSYSERR_ERROR;
498         }
499         SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
500         TRACE("Created thread for midi-in\n");
501     }
502
503     MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
504
505     MidiInDev[wDevID].lpQueueHdr = NULL;
506     MidiInDev[wDevID].dwTotalPlayed = 0;
507     MidiInDev[wDevID].bufsize = 0x3FFF;
508     MidiInDev[wDevID].midiDesc = *lpDesc;
509     MidiInDev[wDevID].state = 0;
510     MidiInDev[wDevID].incLen = 0;
511     MidiInDev[wDevID].startTime = 0;
512
513     MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L);
514     return MMSYSERR_NOERROR;
515 }
516
517 /**************************************************************************
518  *                      midClose                                [internal]
519  */
520 static DWORD midClose(WORD wDevID)
521 {
522     int         ret = MMSYSERR_NOERROR;
523
524     TRACE("(%04X);\n", wDevID);
525
526     if (wDevID >= MIDM_NumDevs) {
527         WARN("wDevID too big (%u) !\n", wDevID);
528         return MMSYSERR_BADDEVICEID;
529     }
530     if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
531         WARN("device not opened !\n");
532         return MMSYSERR_ERROR;
533     }
534     if (MidiInDev[wDevID].lpQueueHdr != 0) {
535         return MIDIERR_STILLPLAYING;
536     }
537
538     if (midiSeq == NULL) {
539         WARN("ooops !\n");
540         return MMSYSERR_ERROR;
541     }
542     if (--numStartedMidiIn == 0) {
543         TRACE("Stopping thread for midi-in\n");
544         end_thread = 1;
545         if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
546             WARN("Thread end not signaled, force termination\n");
547             TerminateThread(hThread, 0);
548         }
549         TRACE("Stopped thread for midi-in\n");
550     }
551
552     snd_seq_disconnect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port);
553     midiCloseSeq();
554
555     MidiInDev[wDevID].bufsize = 0;
556     MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L);
557     MidiInDev[wDevID].midiDesc.hMidi = 0;
558
559     return ret;
560 }
561
562
563 /**************************************************************************
564  *                              midAddBuffer                    [internal]
565  */
566 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
567 {
568     TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
569
570     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
571     if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
572
573     if (lpMidiHdr == NULL)      return MMSYSERR_INVALPARAM;
574     if (dwSize < offsetof(MIDIHDR,dwOffset)) return MMSYSERR_INVALPARAM;
575     if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
576     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
577     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
578
579     EnterCriticalSection(&crit_sect);
580     lpMidiHdr->dwFlags &= ~WHDR_DONE;
581     lpMidiHdr->dwFlags |= MHDR_INQUEUE;
582     lpMidiHdr->dwBytesRecorded = 0;
583     lpMidiHdr->lpNext = 0;
584     if (MidiInDev[wDevID].lpQueueHdr == 0) {
585         MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
586     } else {
587         LPMIDIHDR       ptr;
588
589         for (ptr = MidiInDev[wDevID].lpQueueHdr; ptr->lpNext != 0;
590              ptr = ptr->lpNext);
591         ptr->lpNext = lpMidiHdr;
592     }
593     LeaveCriticalSection(&crit_sect);
594
595     return MMSYSERR_NOERROR;
596 }
597
598 /**************************************************************************
599  *                              midPrepare                      [internal]
600  */
601 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
602 {
603     TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
604
605     if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 ||
606         lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0)
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 < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 ||
627         lpMidiHdr->lpData == 0)
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         MIDI_NotifyClient(wDevID, MIM_LONGDATA,
656                           (DWORD_PTR)MidiInDev[wDevID].lpQueueHdr, dwTime);
657         MidiInDev[wDevID].lpQueueHdr = MidiInDev[wDevID].lpQueueHdr->lpNext;
658     }
659     LeaveCriticalSection(&crit_sect);
660
661     return MMSYSERR_NOERROR;
662 }
663
664 /**************************************************************************
665  *                      midStart                                [internal]
666  */
667 static DWORD midStart(WORD wDevID)
668 {
669     TRACE("(%04X);\n", wDevID);
670
671     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
672     if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
673
674     MidiInDev[wDevID].state = 1;
675     MidiInDev[wDevID].startTime = GetTickCount();
676     return MMSYSERR_NOERROR;
677 }
678
679 /**************************************************************************
680  *                      midStop                                 [internal]
681  */
682 static DWORD midStop(WORD wDevID)
683 {
684     TRACE("(%04X);\n", wDevID);
685
686     if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
687     if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
688
689     MidiInDev[wDevID].state = 0;
690     return MMSYSERR_NOERROR;
691 }
692
693 /**************************************************************************
694  *                              modGetDevCaps                   [internal]
695  */
696 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
697 {
698     TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
699
700     if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
701     if (lpCaps == NULL)         return MMSYSERR_INVALPARAM;
702
703     memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
704
705     return MMSYSERR_NOERROR;
706 }
707
708 /**************************************************************************
709  *                      modOpen                                 [internal]
710  */
711 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
712 {
713     TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
714     if (lpDesc == NULL) {
715         WARN("Invalid Parameter !\n");
716         return MMSYSERR_INVALPARAM;
717     }
718     if (wDevID >= MODM_NumDevs) {
719         TRACE("MAX_MIDIOUTDRV reached !\n");
720         return MMSYSERR_BADDEVICEID;
721     }
722     if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
723         WARN("device already open !\n");
724         return MMSYSERR_ALLOCATED;
725     }
726     if (!MidiOutDev[wDevID].bEnabled) {
727         WARN("device disabled !\n");
728         return MIDIERR_NODEVICE;
729     }
730     if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
731         WARN("bad dwFlags\n");
732         return MMSYSERR_INVALFLAG;
733     }
734     if (!MidiOutDev[wDevID].bEnabled) {
735         TRACE("disabled wDevID\n");
736         return MMSYSERR_NOTENABLED;
737     }
738
739     MidiOutDev[wDevID].lpExtra = 0;
740
741     switch (MidiOutDev[wDevID].caps.wTechnology) {
742     case MOD_FMSYNTH:
743     case MOD_MIDIPORT:
744     case MOD_SYNTH:
745         if (midiOpenSeq(1) < 0) {
746             return MMSYSERR_ALLOCATED;
747         }
748         break;
749     default:
750         WARN("Technology not supported (yet) %d !\n",
751              MidiOutDev[wDevID].caps.wTechnology);
752         return MMSYSERR_NOTENABLED;
753     }
754
755     MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
756
757     MidiOutDev[wDevID].lpQueueHdr = NULL;
758     MidiOutDev[wDevID].dwTotalPlayed = 0;
759     MidiOutDev[wDevID].bufsize = 0x3FFF;
760     MidiOutDev[wDevID].midiDesc = *lpDesc;
761
762     /* Connect our app port to the device port */
763     if (snd_seq_connect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port) < 0)
764         return MMSYSERR_NOTENABLED;
765     
766     MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L);
767     TRACE("Successful !\n");
768     return MMSYSERR_NOERROR;
769 }
770
771
772 /**************************************************************************
773  *                      modClose                                [internal]
774  */
775 static DWORD modClose(WORD wDevID)
776 {
777     int ret = MMSYSERR_NOERROR;
778
779     TRACE("(%04X);\n", wDevID);
780
781     if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
782         WARN("device not opened !\n");
783         return MMSYSERR_ERROR;
784     }
785     /* FIXME: should test that no pending buffer is still in the queue for
786      * playing */
787
788     if (midiSeq == NULL) {
789         WARN("can't close !\n");
790         return MMSYSERR_ERROR;
791     }
792
793     switch (MidiOutDev[wDevID].caps.wTechnology) {
794     case MOD_FMSYNTH:
795     case MOD_MIDIPORT:
796     case MOD_SYNTH:
797         snd_seq_disconnect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
798         midiCloseSeq();
799         break;
800     default:
801         WARN("Technology not supported (yet) %d !\n",
802              MidiOutDev[wDevID].caps.wTechnology);
803         return MMSYSERR_NOTENABLED;
804     }
805
806     HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
807     MidiOutDev[wDevID].lpExtra = 0;
808  
809     MidiOutDev[wDevID].bufsize = 0;
810     MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L);
811     MidiOutDev[wDevID].midiDesc.hMidi = 0;
812     return ret;
813 }
814
815 /**************************************************************************
816  *                      modData                                 [internal]
817  */
818 static DWORD modData(WORD wDevID, DWORD dwParam)
819 {
820     BYTE        evt = LOBYTE(LOWORD(dwParam));
821     BYTE        d1  = HIBYTE(LOWORD(dwParam));
822     BYTE        d2  = LOBYTE(HIWORD(dwParam));
823     
824     TRACE("(%04X, %08X);\n", wDevID, dwParam);
825
826     if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
827     if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
828
829     if (midiSeq == NULL) {
830         WARN("can't play !\n");
831         return MIDIERR_NODEVICE;
832     }
833     switch (MidiOutDev[wDevID].caps.wTechnology) {
834     case MOD_SYNTH:
835     case MOD_MIDIPORT:
836         {
837             int handled = 1; /* Assume event is handled */
838             snd_seq_event_t event;
839             snd_seq_ev_clear(&event);
840             snd_seq_ev_set_direct(&event);
841             snd_seq_ev_set_source(&event, port_out);
842             snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
843             
844             switch (evt & 0xF0) {
845             case MIDI_CMD_NOTE_OFF:
846                 snd_seq_ev_set_noteoff(&event, evt&0x0F, d1, d2);
847                 break;
848             case MIDI_CMD_NOTE_ON:
849                 snd_seq_ev_set_noteon(&event, evt&0x0F, d1, d2);
850                 break;
851             case MIDI_CMD_NOTE_PRESSURE:
852                 snd_seq_ev_set_keypress(&event, evt&0x0F, d1, d2);
853                 break;
854             case MIDI_CMD_CONTROL:
855                 snd_seq_ev_set_controller(&event, evt&0x0F, d1, d2);
856                 break;
857             case MIDI_CMD_BENDER:
858                 snd_seq_ev_set_pitchbend(&event, evt&0x0F, ((WORD)d2 << 7 | (WORD)d1) - 0x2000);
859                 break;
860             case MIDI_CMD_PGM_CHANGE:
861                 snd_seq_ev_set_pgmchange(&event, evt&0x0F, d1);
862                 break;
863             case MIDI_CMD_CHANNEL_PRESSURE:
864                 snd_seq_ev_set_chanpress(&event, evt&0x0F, d1);
865                 break;
866             case MIDI_CMD_COMMON_SYSEX:
867                 switch (evt & 0x0F) {
868                 case 0x00:      /* System Exclusive, don't do it on modData,
869                                  * should require modLongData*/
870                 case 0x04:      /* Undefined. */
871                 case 0x05:      /* Undefined. */
872                 case 0x07:      /* End of Exclusive. */
873                 case 0x09:      /* Undefined. */
874                 case 0x0D:      /* Undefined. */
875                     handled = 0;
876                     break;
877                 case 0x06:      /* Tune Request */
878                 case 0x08:      /* Timing Clock. */
879                 case 0x0A:      /* Start. */
880                 case 0x0B:      /* Continue */
881                 case 0x0C:      /* Stop */
882                 case 0x0E:      /* Active Sensing. */
883                     /* FIXME: Is this function suitable for these purposes
884                        (and also Song Select and Song Position Pointer) */
885                     snd_seq_ev_set_sysex(&event, 1, &evt);
886                     break;
887                 case 0x0F:      /* Reset */
888                                 /* snd_seq_ev_set_sysex(&event, 1, &evt);
889                                    this other way may be better */
890                     {
891                         BYTE reset_sysex_seq[] = {MIDI_CMD_COMMON_SYSEX, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
892                         snd_seq_ev_set_sysex(&event, sizeof(reset_sysex_seq), reset_sysex_seq);
893                     }
894                     break;
895                 case 0x01:      /* MTC Quarter frame */
896                 case 0x03:      /* Song Select. */
897                     {
898                         BYTE buf[2];
899                         buf[0] = evt;
900                         buf[1] = d1;
901                         snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
902                     }
903                     break;
904                 case 0x02:      /* Song Position Pointer. */
905                     {
906                         BYTE buf[3];
907                         buf[0] = evt;
908                         buf[1] = d1;
909                         buf[2] = d2;
910                         snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
911                     }
912                     break;
913                 }
914                 break;
915             }
916             if (handled)
917                 snd_seq_event_output_direct(midiSeq, &event);
918         }
919         break;
920     default:
921         WARN("Technology not supported (yet) %d !\n",
922              MidiOutDev[wDevID].caps.wTechnology);
923         return MMSYSERR_NOTENABLED;
924     }
925
926     return MMSYSERR_NOERROR;
927 }
928
929 /**************************************************************************
930  *              modLongData                                     [internal]
931  */
932 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
933 {
934     int         len_add = 0;
935     LPBYTE      lpData, lpNewData = NULL;
936     snd_seq_event_t event;
937
938     TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
939
940     /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
941      * but it seems to be used only for midi input.
942      * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
943      */
944     
945     if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
946     if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
947
948     if (midiSeq == NULL) {
949         WARN("can't play !\n");
950         return MIDIERR_NODEVICE;
951     }
952
953     lpData = (LPBYTE) lpMidiHdr->lpData;
954     
955     if (lpData == NULL)
956         return MIDIERR_UNPREPARED;
957     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
958         return MIDIERR_UNPREPARED;
959     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
960         return MIDIERR_STILLPLAYING;
961     lpMidiHdr->dwFlags &= ~MHDR_DONE;
962     lpMidiHdr->dwFlags |= MHDR_INQUEUE;
963
964     /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
965      * data, or can it also contain raw MIDI data, to be split up and sent to
966      * modShortData() ?
967      * If the latest is true, then the following WARNing will fire up
968      */
969     if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
970         WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
971         lpNewData = HeapAlloc(GetProcessHeap(), 0, lpMidiHdr->dwBufferLength + 2);
972     }
973
974     TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
975     TRACE("                 %02X %02X %02X ... %02X %02X %02X\n",
976           lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
977           lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
978
979     switch (MidiOutDev[wDevID].caps.wTechnology) {
980     case MOD_FMSYNTH:
981         /* FIXME: I don't think there is much to do here */
982         break;
983     case MOD_MIDIPORT:
984         if (lpData[0] != 0xF0) {
985             /* Send start of System Exclusive */
986             len_add = 1;
987             lpData[0] = 0xF0;
988             memcpy(lpNewData, lpData, lpMidiHdr->dwBufferLength);
989             WARN("Adding missing 0xF0 marker at the beginning of "
990                  "system exclusive byte stream\n");
991         }
992         if (lpData[lpMidiHdr->dwBufferLength-1] != 0xF7) {
993             /* Send end of System Exclusive */
994             memcpy(lpData + len_add, lpData, lpMidiHdr->dwBufferLength);
995             lpNewData[lpMidiHdr->dwBufferLength + len_add - 1] = 0xF0;
996             len_add++;
997             WARN("Adding missing 0xF7 marker at the end of "
998                  "system exclusive byte stream\n");
999         }
1000         snd_seq_ev_clear(&event);
1001         snd_seq_ev_set_direct(&event);
1002         snd_seq_ev_set_source(&event, port_out);
1003         snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1004         TRACE("client = %d port = %d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1005         snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData);
1006         snd_seq_event_output_direct(midiSeq, &event);
1007         if (lpNewData)
1008                 HeapFree(GetProcessHeap(), 0, lpData);
1009         break;
1010     default:
1011         WARN("Technology not supported (yet) %d !\n",
1012              MidiOutDev[wDevID].caps.wTechnology);
1013         HeapFree(GetProcessHeap(), 0, lpNewData);
1014         return MMSYSERR_NOTENABLED;
1015     }
1016
1017     lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1018     lpMidiHdr->dwFlags |= MHDR_DONE;
1019     MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD_PTR)lpMidiHdr, 0L);
1020     return MMSYSERR_NOERROR;
1021 }
1022
1023 /**************************************************************************
1024  *                      modPrepare                              [internal]
1025  */
1026 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1027 {
1028     TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1029
1030     if (midiSeq == NULL) {
1031         WARN("can't prepare !\n");
1032         return MMSYSERR_NOTENABLED;
1033     }
1034
1035     /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
1036      * asks to prepare MIDIHDR which dwFlags != 0.
1037      * So at least check for the inqueue flag
1038      */
1039     if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 ||
1040         lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0) {
1041         WARN("%p %p %08x %d\n", lpMidiHdr, lpMidiHdr ? lpMidiHdr->lpData : NULL,
1042              lpMidiHdr ? lpMidiHdr->dwFlags : 0, dwSize);
1043         return MMSYSERR_INVALPARAM;
1044     }
1045
1046     lpMidiHdr->lpNext = 0;
1047     lpMidiHdr->dwFlags |= MHDR_PREPARED;
1048     lpMidiHdr->dwFlags &= ~MHDR_DONE;
1049     return MMSYSERR_NOERROR;
1050 }
1051
1052 /**************************************************************************
1053  *                              modUnprepare                    [internal]
1054  */
1055 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1056 {
1057     TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1058
1059     if (midiSeq == NULL) {
1060         WARN("can't unprepare !\n");
1061         return MMSYSERR_NOTENABLED;
1062     }
1063
1064     if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0)
1065         return MMSYSERR_INVALPARAM;
1066     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1067         return MIDIERR_STILLPLAYING;
1068     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1069     return MMSYSERR_NOERROR;
1070 }
1071
1072 /**************************************************************************
1073  *                      modGetVolume                            [internal]
1074  */
1075 static DWORD modGetVolume(WORD wDevID, DWORD* lpdwVolume)
1076 {
1077     if (!lpdwVolume) return MMSYSERR_INVALPARAM;
1078     if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1079     *lpdwVolume = 0xFFFFFFFF;
1080     return (MidiOutDev[wDevID].caps.dwSupport & MIDICAPS_VOLUME) ? 0 : MMSYSERR_NOTSUPPORTED;
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, unsigned int cap, unsigned int type)
1116 {
1117     char midiPortName[MAXPNAMELEN];
1118
1119     if (cap & SND_SEQ_PORT_CAP_WRITE) {
1120         TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1121                                           snd_seq_client_info_get_name(cinfo),
1122                                           snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1123                                           snd_seq_port_info_get_port(pinfo),
1124                                           snd_seq_port_info_get_name(pinfo),
1125                                           type);
1126                 
1127         if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1128             return;
1129         if (!type)
1130             return;
1131
1132         MidiOutDev[MODM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1133
1134         /* Manufac ID. We do not have access to this with soundcard.h
1135          * Does not seem to be a problem, because in mmsystem.h only
1136          * Microsoft's ID is listed.
1137          */
1138         MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1139         MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001;    /* FIXME Product ID  */
1140         /* Product Version. We simply say "1" */
1141         MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1142         /* The following are mandatory for MOD_MIDIPORT */
1143         MidiOutDev[MODM_NumDevs].caps.wChannelMask   = 0xFFFF;
1144         MidiOutDev[MODM_NumDevs].caps.wVoices        = 0;
1145         MidiOutDev[MODM_NumDevs].caps.wNotes         = 0;
1146         MidiOutDev[MODM_NumDevs].caps.dwSupport      = 0;
1147
1148         /* Try to use both client and port names, if this is too long take the port name only.
1149            In the second case the port name should be explicit enough due to its big size.
1150          */
1151         if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1152             sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1153         } else {
1154             lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN);
1155         }
1156         MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1157                             MidiOutDev[MODM_NumDevs].caps.szPname,
1158                             sizeof(MidiOutDev[MODM_NumDevs].caps.szPname) / sizeof(WCHAR));
1159
1160         MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1161
1162         if (MOD_MIDIPORT != MidiOutDev[MODM_NumDevs].caps.wTechnology) {
1163             /* FIXME Do we have this information?
1164              * Assuming the soundcards can handle
1165              * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1166              * not MIDICAPS_CACHE.
1167              */
1168             MidiOutDev[MODM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1169             MidiOutDev[MODM_NumDevs].caps.wVoices   = 16;
1170
1171             /* FIXME Is it possible to know the maximum
1172              * number of simultaneous notes of a soundcard ?
1173              * I believe we don't have this information, but
1174              * it's probably equal or more than wVoices
1175              */
1176             MidiOutDev[MODM_NumDevs].caps.wNotes    = 16;
1177         }
1178         MidiOutDev[MODM_NumDevs].bEnabled    = TRUE;
1179
1180         TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1181             "\tALSA info: midi dev-type=%x, capa=0\n",
1182               MODM_NumDevs, wine_dbgstr_w(MidiOutDev[MODM_NumDevs].caps.szPname),
1183               MidiOutDev[MODM_NumDevs].caps.wTechnology,
1184               MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1185               MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1186               type);
1187                 
1188         MODM_NumDevs++;
1189     }
1190     if (cap & SND_SEQ_PORT_CAP_READ) {
1191         TRACE("IN  (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1192                                           snd_seq_client_info_get_name(cinfo),
1193                                           snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1194                                           snd_seq_port_info_get_port(pinfo),
1195                                           snd_seq_port_info_get_name(pinfo),
1196                                           type);
1197                 
1198         if (MIDM_NumDevs >= MAX_MIDIINDRV)
1199             return;
1200         if (!type)
1201             return;
1202
1203         MidiInDev[MIDM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1204
1205         /* Manufac ID. We do not have access to this with soundcard.h
1206          * Does not seem to be a problem, because in mmsystem.h only
1207          * Microsoft's ID is listed.
1208          */
1209         MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1210         MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001;     /* FIXME Product ID  */
1211         /* Product Version. We simply say "1" */
1212         MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1213         MidiInDev[MIDM_NumDevs].caps.dwSupport = 0; /* mandatory with MIDIINCAPS */
1214
1215         /* Try to use both client and port names, if this is too long take the port name only.
1216            In the second case the port name should be explicit enough due to its big size.
1217          */
1218         if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1219             sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1220         } else {
1221             lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN);
1222         }
1223         MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1224                             MidiInDev[MIDM_NumDevs].caps.szPname,
1225                             sizeof(MidiInDev[MIDM_NumDevs].caps.szPname) / sizeof(WCHAR));
1226         MidiInDev[MIDM_NumDevs].state = 0;
1227
1228         TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1229               "\tALSA info: midi dev-type=%x, capa=0\n",
1230               MIDM_NumDevs, wine_dbgstr_w(MidiInDev[MIDM_NumDevs].caps.szPname),
1231               MidiInDev[MIDM_NumDevs].caps.dwSupport,
1232               type);
1233
1234         MIDM_NumDevs++;
1235     }
1236 }
1237
1238
1239 /*======================================================================*
1240  *                          MIDI entry points                           *
1241  *======================================================================*/
1242
1243 /**************************************************************************
1244  * ALSA_MidiInit                                [internal]
1245  *
1246  * Initializes the MIDI devices information variables
1247  */
1248 static LONG ALSA_MidiInit(void)
1249 {
1250     static      BOOL    bInitDone = FALSE;
1251     snd_seq_client_info_t *cinfo;
1252     snd_seq_port_info_t *pinfo;
1253
1254     if (bInitDone)
1255         return TRUE;
1256
1257     TRACE("Initializing the MIDI variables.\n");
1258     bInitDone = TRUE;
1259
1260     /* try to open device */
1261     if (midiOpenSeq(0) == -1) {
1262         return TRUE;
1263     }
1264
1265 #if 0 /* Debug purpose */
1266     snd_lib_error_set_handler(error_handler);
1267 #endif
1268     cinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_client_info_sizeof() );
1269     pinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_port_info_sizeof() );
1270
1271     /* First, search for all internal midi devices */
1272     snd_seq_client_info_set_client(cinfo, -1);
1273     while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1274         snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1275         snd_seq_port_info_set_port(pinfo, -1);
1276         while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1277             unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1278             unsigned int type = snd_seq_port_info_get_type(pinfo);
1279             if (!(type & SND_SEQ_PORT_TYPE_PORT))
1280                 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1281         }
1282     }
1283
1284     /* Second, search for all external ports */
1285     snd_seq_client_info_set_client(cinfo, -1);
1286     while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1287         snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1288         snd_seq_port_info_set_port(pinfo, -1);
1289         while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1290             unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1291             unsigned int type = snd_seq_port_info_get_type(pinfo);
1292             if (type & SND_SEQ_PORT_TYPE_PORT)
1293                 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1294         }
1295     }
1296
1297     /* close file and exit */
1298     midiCloseSeq();
1299     HeapFree( GetProcessHeap(), 0, cinfo );
1300     HeapFree( GetProcessHeap(), 0, pinfo );
1301
1302     TRACE("End\n");
1303     return TRUE;
1304 }
1305
1306 /**************************************************************************
1307  *                      midMessage (WINEALSA.@)
1308  */
1309 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1310                             DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1311 {
1312     TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1313           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1314     switch (wMsg) {
1315     case DRVM_INIT:
1316         ALSA_MidiInit();
1317         return 0;
1318     case DRVM_EXIT:
1319     case DRVM_ENABLE:
1320     case DRVM_DISABLE:
1321         /* FIXME: Pretend this is supported */
1322         return 0;
1323     case MIDM_OPEN:
1324         return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1325     case MIDM_CLOSE:
1326         return midClose(wDevID);
1327     case MIDM_ADDBUFFER:
1328         return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1329     case MIDM_PREPARE:
1330         return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1331     case MIDM_UNPREPARE:
1332         return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1333     case MIDM_GETDEVCAPS:
1334         return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1335     case MIDM_GETNUMDEVS:
1336         return MIDM_NumDevs;
1337     case MIDM_RESET:
1338         return midReset(wDevID);
1339     case MIDM_START:
1340         return midStart(wDevID);
1341     case MIDM_STOP:
1342         return midStop(wDevID);
1343     default:
1344         TRACE("Unsupported message\n");
1345     }
1346     return MMSYSERR_NOTSUPPORTED;
1347 }
1348
1349 /**************************************************************************
1350  *                              modMessage (WINEALSA.@)
1351  */
1352 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1353                              DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1354 {
1355     TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1356           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1357
1358     switch (wMsg) {
1359     case DRVM_INIT:
1360         ALSA_MidiInit();
1361         return 0;
1362     case DRVM_EXIT:
1363     case DRVM_ENABLE:
1364     case DRVM_DISABLE:
1365         /* FIXME: Pretend this is supported */
1366         return 0;
1367     case MODM_OPEN:
1368         return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1369     case MODM_CLOSE:
1370         return modClose(wDevID);
1371     case MODM_DATA:
1372         return modData(wDevID, dwParam1);
1373     case MODM_LONGDATA:
1374         return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1375     case MODM_PREPARE:
1376         return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1377     case MODM_UNPREPARE:
1378         return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1379     case MODM_GETDEVCAPS:
1380         return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1381     case MODM_GETNUMDEVS:
1382         return MODM_NumDevs;
1383     case MODM_GETVOLUME:
1384         return modGetVolume(wDevID, (DWORD*)dwParam1);
1385     case MODM_SETVOLUME:
1386         return 0;
1387     case MODM_RESET:
1388         return modReset(wDevID);
1389     default:
1390         TRACE("Unsupported message\n");
1391     }
1392     return MMSYSERR_NOTSUPPORTED;
1393 }
1394
1395 /**************************************************************************
1396  *                              DriverProc (WINEALSA.@)
1397  */
1398 LRESULT CALLBACK ALSA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1399                                  LPARAM dwParam1, LPARAM dwParam2)
1400 {
1401 /* EPP     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",  */
1402 /* EPP    dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
1403
1404     switch(wMsg) {
1405     case DRV_LOAD:
1406     case DRV_FREE:
1407     case DRV_OPEN:
1408     case DRV_CLOSE:
1409     case DRV_ENABLE:
1410     case DRV_DISABLE:
1411     case DRV_QUERYCONFIGURE:
1412     case DRV_CONFIGURE:
1413         return 1;
1414     case DRV_INSTALL:
1415     case DRV_REMOVE:
1416         return DRV_SUCCESS;
1417     default:
1418         return 0;
1419     }
1420 }