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