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