advapi32: Add support for registry symlinks.
[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 (sizeof(MIDIHDR) > dwSize) 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 < sizeof(MIDIHDR) || 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 < sizeof(MIDIHDR) || 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 < sizeof(MIDIHDR) || 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 < sizeof(MIDIHDR) || 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  *                      modReset                                [internal]
1070  */
1071 static DWORD modReset(WORD wDevID)
1072 {
1073     unsigned chn;
1074
1075     TRACE("(%04X);\n", wDevID);
1076
1077     if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1078     if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1079
1080     /* stop all notes */
1081     /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1082      * it's channel dependent...
1083      */
1084     for (chn = 0; chn < 16; chn++) {
1085         /* turn off every note */
1086         modData(wDevID, 0x7800 | MIDI_CMD_CONTROL | chn);
1087         /* remove sustain on all channels */
1088         modData(wDevID, (MIDI_CTL_SUSTAIN << 8) | MIDI_CMD_CONTROL | chn);
1089     }
1090     /* FIXME: the LongData buffers must also be returned to the app */
1091     return MMSYSERR_NOERROR;
1092 }
1093
1094
1095 /**************************************************************************
1096  *                      ALSA_AddMidiPort                        [internal]
1097  *
1098  * Helper for ALSA_MidiInit
1099  */
1100 static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t* pinfo, unsigned int cap, unsigned int type)
1101 {
1102     char midiPortName[MAXPNAMELEN];
1103
1104     if (cap & SND_SEQ_PORT_CAP_WRITE) {
1105         TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1106                                           snd_seq_client_info_get_name(cinfo),
1107                                           snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1108                                           snd_seq_port_info_get_port(pinfo),
1109                                           snd_seq_port_info_get_name(pinfo),
1110                                           type);
1111                 
1112         if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1113             return;
1114         if (!type)
1115             return;
1116
1117         MidiOutDev[MODM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1118
1119         /* Manufac ID. We do not have access to this with soundcard.h
1120          * Does not seem to be a problem, because in mmsystem.h only
1121          * Microsoft's ID is listed.
1122          */
1123         MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1124         MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001;    /* FIXME Product ID  */
1125         /* Product Version. We simply say "1" */
1126         MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1127         MidiOutDev[MODM_NumDevs].caps.wChannelMask   = 0xFFFF;
1128
1129         /* FIXME Do we have this information?
1130          * Assuming the soundcards can handle
1131          * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1132          * not MIDICAPS_CACHE.
1133          */
1134         MidiOutDev[MODM_NumDevs].caps.dwSupport      = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1135
1136         /* Try to use both client and port names, if this is too long take the port name only.
1137            In the second case the port name should be explicit enough due to its big size.
1138          */
1139         if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1140             sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1141         } else {
1142             lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1143             midiPortName[MAXPNAMELEN-1] = 0;
1144         }
1145         MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1146                             MidiOutDev[MODM_NumDevs].caps.szPname,
1147                             sizeof(MidiOutDev[MODM_NumDevs].caps.szPname) / sizeof(WCHAR));
1148
1149         MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1150         MidiOutDev[MODM_NumDevs].caps.wVoices     = 16;
1151
1152         /* FIXME Is it possible to know the maximum
1153          * number of simultaneous notes of a soundcard ?
1154          * I believe we don't have this information, but
1155          * it's probably equal or more than wVoices
1156          */
1157         MidiOutDev[MODM_NumDevs].caps.wNotes = 16;
1158         MidiOutDev[MODM_NumDevs].bEnabled    = TRUE;
1159
1160         TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1161             "\tALSA info: midi dev-type=%x, capa=0\n",
1162               MODM_NumDevs, wine_dbgstr_w(MidiOutDev[MODM_NumDevs].caps.szPname),
1163               MidiOutDev[MODM_NumDevs].caps.wTechnology,
1164               MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1165               MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1166               type);
1167                 
1168         MODM_NumDevs++;
1169     }
1170     if (cap & SND_SEQ_PORT_CAP_READ) {
1171         TRACE("IN  (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1172                                           snd_seq_client_info_get_name(cinfo),
1173                                           snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1174                                           snd_seq_port_info_get_port(pinfo),
1175                                           snd_seq_port_info_get_name(pinfo),
1176                                           type);
1177                 
1178         if (MIDM_NumDevs >= MAX_MIDIINDRV)
1179             return;
1180         if (!type)
1181             return;
1182
1183         MidiInDev[MIDM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1184
1185         /* Manufac ID. We do not have access to this with soundcard.h
1186          * Does not seem to be a problem, because in mmsystem.h only
1187          * Microsoft's ID is listed.
1188          */
1189         MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1190         MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001;     /* FIXME Product ID  */
1191         /* Product Version. We simply say "1" */
1192         MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1193
1194         /* FIXME Do we have this information?
1195          * Assuming the soundcards can handle
1196          * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1197          * not MIDICAPS_CACHE.
1198          */
1199         MidiInDev[MIDM_NumDevs].caps.dwSupport      = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1200
1201         /* Try to use both client and port names, if this is too long take the port name only.
1202            In the second case the port name should be explicit enough due to its big size.
1203          */
1204         if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1205             sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1206         } else {
1207             lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1208             midiPortName[MAXPNAMELEN-1] = 0;
1209         }
1210         MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1211                             MidiInDev[MIDM_NumDevs].caps.szPname,
1212                             sizeof(MidiInDev[MIDM_NumDevs].caps.szPname) / sizeof(WCHAR));
1213         MidiInDev[MIDM_NumDevs].state = 0;
1214
1215         TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1216               "\tALSA info: midi dev-type=%x, capa=0\n",
1217               MIDM_NumDevs, wine_dbgstr_w(MidiInDev[MIDM_NumDevs].caps.szPname),
1218               MidiInDev[MIDM_NumDevs].caps.dwSupport,
1219               type);
1220
1221         MIDM_NumDevs++;
1222     }
1223 }
1224
1225 #endif /* HAVE_ALSA */
1226
1227
1228 /*======================================================================*
1229  *                          MIDI entry points                           *
1230  *======================================================================*/
1231
1232 /**************************************************************************
1233  * ALSA_MidiInit                                [internal]
1234  *
1235  * Initializes the MIDI devices information variables
1236  */
1237 LONG ALSA_MidiInit(void)
1238 {
1239 #ifdef HAVE_ALSA
1240     static      BOOL    bInitDone = FALSE;
1241     snd_seq_client_info_t *cinfo;
1242     snd_seq_port_info_t *pinfo;
1243
1244     if (bInitDone)
1245         return TRUE;
1246
1247     TRACE("Initializing the MIDI variables.\n");
1248     bInitDone = TRUE;
1249
1250     /* try to open device */
1251     if (midiOpenSeq(0) == -1) {
1252         return TRUE;
1253     }
1254
1255 #if 0 /* Debug purpose */
1256     snd_lib_error_set_handler(error_handler);
1257 #endif
1258     cinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_client_info_sizeof() );
1259     pinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_port_info_sizeof() );
1260
1261     /* First, search for all internal midi devices */
1262     snd_seq_client_info_set_client(cinfo, -1);
1263     while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1264         snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1265         snd_seq_port_info_set_port(pinfo, -1);
1266         while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1267             unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1268             unsigned int type = snd_seq_port_info_get_type(pinfo);
1269             if (!(type & SND_SEQ_PORT_TYPE_PORT))
1270                 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1271         }
1272     }
1273
1274     /* Second, search for all external ports */
1275     snd_seq_client_info_set_client(cinfo, -1);
1276     while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1277         snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1278         snd_seq_port_info_set_port(pinfo, -1);
1279         while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1280             unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1281             unsigned int type = snd_seq_port_info_get_type(pinfo);
1282             if (type & SND_SEQ_PORT_TYPE_PORT)
1283                 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1284         }
1285     }
1286
1287     /* close file and exit */
1288     midiCloseSeq();
1289     HeapFree( GetProcessHeap(), 0, cinfo );
1290     HeapFree( GetProcessHeap(), 0, pinfo );
1291
1292     TRACE("End\n");
1293 #endif
1294     return TRUE;
1295 }
1296
1297 /**************************************************************************
1298  *                      midMessage (WINEALSA.@)
1299  */
1300 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1301                             DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1302 {
1303     TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1304           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1305     switch (wMsg) {
1306 #ifdef HAVE_ALSA
1307     case DRVM_INIT:
1308     case DRVM_EXIT:
1309     case DRVM_ENABLE:
1310     case DRVM_DISABLE:
1311         /* FIXME: Pretend this is supported */
1312         return 0;
1313     case MIDM_OPEN:
1314         return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1315     case MIDM_CLOSE:
1316         return midClose(wDevID);
1317     case MIDM_ADDBUFFER:
1318         return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1319     case MIDM_PREPARE:
1320         return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1321     case MIDM_UNPREPARE:
1322         return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1323     case MIDM_GETDEVCAPS:
1324         return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1325     case MIDM_GETNUMDEVS:
1326         return MIDM_NumDevs;
1327     case MIDM_RESET:
1328         return midReset(wDevID);
1329     case MIDM_START:
1330         return midStart(wDevID);
1331     case MIDM_STOP:
1332         return midStop(wDevID);
1333 #endif
1334     default:
1335         TRACE("Unsupported message\n");
1336     }
1337     return MMSYSERR_NOTSUPPORTED;
1338 }
1339
1340 /**************************************************************************
1341  *                              modMessage (WINEALSA.@)
1342  */
1343 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1344                              DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1345 {
1346     TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1347           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1348
1349     switch (wMsg) {
1350 #ifdef HAVE_ALSA
1351     case DRVM_INIT:
1352     case DRVM_EXIT:
1353     case DRVM_ENABLE:
1354     case DRVM_DISABLE:
1355         /* FIXME: Pretend this is supported */
1356         return 0;
1357     case MODM_OPEN:
1358         return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1359     case MODM_CLOSE:
1360         return modClose(wDevID);
1361     case MODM_DATA:
1362         return modData(wDevID, dwParam1);
1363     case MODM_LONGDATA:
1364         return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1365     case MODM_PREPARE:
1366         return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1367     case MODM_UNPREPARE:
1368         return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1369     case MODM_GETDEVCAPS:
1370         return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1371     case MODM_GETNUMDEVS:
1372         return MODM_NumDevs;
1373     case MODM_GETVOLUME:
1374         return 0;
1375     case MODM_SETVOLUME:
1376         return 0;
1377     case MODM_RESET:
1378         return modReset(wDevID);
1379 #endif
1380     default:
1381         TRACE("Unsupported message\n");
1382     }
1383     return MMSYSERR_NOTSUPPORTED;
1384 }
1385
1386 /*-----------------------------------------------------------------------*/