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