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