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