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