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