Update shell xxxAW wrapper prototypes for fixed SHLWAPI functions.
[wine] / dlls / winmm / wineoss / midi.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  * Sample MIDI Wine Driver for Open Sound System (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   splitted in midi.c and mcimidi.c
13  *
14  * This library is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU Lesser General Public
16  * License as published by the Free Software Foundation; either
17  * version 2.1 of the License, or (at your option) any later version.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public
25  * License along with this library; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27  */
28
29 #include "config.h"
30
31 #include <string.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <sys/ioctl.h>
35
36 #include "windef.h"
37 #include "winbase.h"
38 #include "wingdi.h"
39 #include "winuser.h"
40 #include "mmddk.h"
41 #include "oss.h"
42 #include "wine/debug.h"
43
44 WINE_DEFAULT_DEBUG_CHANNEL(midi);
45
46 #ifdef HAVE_OSS_MIDI
47
48 #define MIDI_SEQ "/dev/sequencer"
49
50 typedef struct {
51     int                 state;
52     DWORD               bufsize;
53     MIDIOPENDESC        midiDesc;
54     WORD                wFlags;
55     LPMIDIHDR           lpQueueHdr;
56     DWORD               dwTotalPlayed;
57     unsigned char       incoming[3];
58     unsigned char       incPrev;
59     char                incLen;
60     DWORD               startTime;
61 } WINE_MIDIIN;
62
63 typedef struct {
64     int                 state;
65     DWORD               bufsize;
66     MIDIOPENDESC        midiDesc;
67     WORD                wFlags;
68     LPMIDIHDR           lpQueueHdr;
69     DWORD               dwTotalPlayed;
70     void*               lpExtra;                /* according to port type (MIDI, FM...), extra data when needed */
71 } WINE_MIDIOUT;
72
73 static WINE_MIDIIN      MidiInDev [MAX_MIDIINDRV ];
74 static WINE_MIDIOUT     MidiOutDev[MAX_MIDIOUTDRV];
75
76 /* this is the total number of MIDI out devices found */
77 static  int             MODM_NUMDEVS = 0;                               
78 /* this is the number of FM synthetizers (index from 0 to 
79    NUMFMSYNTHDEVS - 1) */
80 static  int             MODM_NUMFMSYNTHDEVS = 0;        
81 /* this is the number of Midi ports (index from NUMFMSYNTHDEVS to 
82    NUMFMSYNTHDEVS + NUMMIDIDEVS - 1) */
83 static  int             MODM_NUMMIDIDEVS = 0;           
84
85 /* this is the total number of MIDI out devices found */
86 static  int             MIDM_NUMDEVS = 0;                               
87
88 static  int             midiSeqFD = -1;
89 static  int             numOpenMidiSeq = 0;
90 static  UINT            midiInTimerID = 0;
91 static  int             numStartedMidiIn = 0;
92
93 /* this structure holds pointers with information for each MIDI
94  * out device found.
95  */
96 static  LPMIDIOUTCAPSA  midiOutDevices[MAX_MIDIOUTDRV];
97
98 /* this structure holds pointers with information for each MIDI
99  * in device found.
100  */
101 static  LPMIDIINCAPSA   midiInDevices [MAX_MIDIINDRV];
102
103 /* 
104  * FIXME : all tests on device ID for midXXX and modYYY are made against 
105  * MAX_MIDIxxDRV (when they are made) but should be done against the actual
106  * number of midi devices found...
107  */
108
109 /*======================================================================*
110  *                  Low level MIDI implementation                       *
111  *======================================================================*/
112
113 static int midiOpenSeq(void);
114 static int midiCloseSeq(void);
115
116 /**************************************************************************
117  *                      unixToWindowsDeviceType                 [internal]
118  *
119  * return the Windows equivalent to a Unix Device Type
120  *
121  */
122 static  int     MIDI_UnixToWindowsDeviceType(int type)
123 {
124     /* MOD_MIDIPORT     output port 
125      * MOD_SYNTH        generic internal synth 
126      * MOD_SQSYNTH      square wave internal synth 
127      * MOD_FMSYNTH      FM internal synth 
128      * MOD_MAPPER       MIDI mapper
129      */
130     
131     /* FIXME Is this really the correct equivalence from UNIX to 
132        Windows Sound type */
133     
134     switch (type) {
135     case SYNTH_TYPE_FM:     return MOD_FMSYNTH;
136     case SYNTH_TYPE_SAMPLE: return MOD_SYNTH;
137     case SYNTH_TYPE_MIDI:   return MOD_MIDIPORT;
138     default:
139         ERR("Cannot determine the type of this midi device. "
140             "Assuming FM Synth\n");
141         return MOD_FMSYNTH;
142     }
143     return MOD_FMSYNTH;
144 }
145
146 /**************************************************************************
147  *                      OSS_MidiInit                            [internal]
148  *
149  * Initializes the MIDI devices information variables
150  */
151 BOOL OSS_MidiInit(void)
152 {
153     int                 i, status, numsynthdevs = 255, nummididevs = 255;
154     struct synth_info   sinfo;
155     struct midi_info    minfo;
156     static      BOOL    bInitDone = FALSE;
157
158     if (bInitDone)
159         return TRUE;
160
161     TRACE("Initializing the MIDI variables.\n");
162     bInitDone = TRUE;
163     
164     /* try to open device */
165     if (midiOpenSeq() == -1) {
166         return TRUE;
167     }
168     
169     /* find how many Synth devices are there in the system */
170     status = ioctl(midiSeqFD, SNDCTL_SEQ_NRSYNTHS, &numsynthdevs);
171     
172     if (status == -1) {
173         ERR("ioctl for nr synth failed.\n");
174         midiCloseSeq();
175         return TRUE;
176     }
177
178     if (numsynthdevs > MAX_MIDIOUTDRV) {
179         ERR("MAX_MIDIOUTDRV (%d) was enough for the number of devices (%d). "
180             "Some FM devices will not be available.\n",MAX_MIDIOUTDRV,numsynthdevs);
181         numsynthdevs = MAX_MIDIOUTDRV;
182     }
183     
184     for (i = 0; i < numsynthdevs; i++) {
185         LPMIDIOUTCAPSA  tmplpCaps;
186         
187         sinfo.device = i;
188         status = ioctl(midiSeqFD, SNDCTL_SYNTH_INFO, &sinfo);
189         if (status == -1) {
190             ERR("ioctl for synth info failed.\n");
191             midiCloseSeq();
192             return TRUE;
193         }
194         
195         tmplpCaps = HeapAlloc(GetProcessHeap(), 0, sizeof(MIDIOUTCAPSA));
196         if (!tmplpCaps)
197             break;
198         /* We also have the information sinfo.synth_subtype, not used here
199          */
200         
201         /* Manufac ID. We do not have access to this with soundcard.h
202          * Does not seem to be a problem, because in mmsystem.h only
203          * Microsoft's ID is listed.
204          */
205         tmplpCaps->wMid = 0x00FF; 
206         tmplpCaps->wPid = 0x0001;       /* FIXME Product ID  */
207         /* Product Version. We simply say "1" */
208         tmplpCaps->vDriverVersion = 0x001; 
209         strcpy(tmplpCaps->szPname, sinfo.name);
210         
211         tmplpCaps->wTechnology = MIDI_UnixToWindowsDeviceType(sinfo.synth_type);
212         tmplpCaps->wVoices     = sinfo.nr_voices;
213         
214         /* FIXME Is it possible to know the maximum
215          * number of simultaneous notes of a soundcard ?
216          * I believe we don't have this information, but
217          * it's probably equal or more than wVoices
218          */
219         tmplpCaps->wNotes      = sinfo.nr_voices;  
220         
221         /* FIXME Do we have this information?
222          * Assuming the soundcards can handle
223          * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
224          * not MIDICAPS_CACHE.
225          */
226         tmplpCaps->dwSupport   = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
227         
228         midiOutDevices[i] = tmplpCaps;
229         
230         if (sinfo.capabilities & SYNTH_CAP_INPUT) {
231             FIXME("Synthesizer support MIDI in. Not supported yet (please report)\n");
232         }
233         
234         TRACE("name='%s', techn=%d voices=%d notes=%d support=%ld\n", 
235               tmplpCaps->szPname, tmplpCaps->wTechnology,
236               tmplpCaps->wVoices, tmplpCaps->wNotes, tmplpCaps->dwSupport);
237         TRACE("OSS info: synth subtype=%d capa=%lx\n", 
238               sinfo.synth_subtype, (long)sinfo.capabilities);
239     }
240     
241     /* find how many MIDI devices are there in the system */
242     status = ioctl(midiSeqFD, SNDCTL_SEQ_NRMIDIS, &nummididevs);
243     if (status == -1) {
244         ERR("ioctl on nr midi failed.\n");
245         midiCloseSeq();
246         return TRUE;
247     }
248     
249     /* FIXME: the two restrictions below could be loosen in some cases */
250     if (numsynthdevs + nummididevs > MAX_MIDIOUTDRV) {
251         ERR("MAX_MIDIOUTDRV was not enough for the number of devices. "
252             "Some MIDI devices will not be available.\n");
253         nummididevs = MAX_MIDIOUTDRV - numsynthdevs;
254     }
255     
256     if (nummididevs > MAX_MIDIINDRV) {
257         ERR("MAX_MIDIINDRV (%d) was not enough for the number of devices (%d). "
258             "Some MIDI devices will not be available.\n",MAX_MIDIINDRV,nummididevs);
259         nummididevs = MAX_MIDIINDRV;
260     }
261     
262     for (i = 0; i < nummididevs; i++) {
263         LPMIDIOUTCAPSA  tmplpOutCaps;
264         LPMIDIINCAPSA   tmplpInCaps;
265         
266         minfo.device = i;
267         status = ioctl(midiSeqFD, SNDCTL_MIDI_INFO, &minfo);
268         if (status == -1) {
269             ERR("ioctl on midi info for device %d failed.\n", i);
270             midiCloseSeq();
271             return TRUE;
272         }
273         
274         tmplpOutCaps = HeapAlloc(GetProcessHeap(), 0, sizeof(MIDIOUTCAPSA));
275         if (!tmplpOutCaps)
276             break;
277         /* This whole part is somewhat obscure to me. I'll keep trying to dig
278            info about it. If you happen to know, please tell us. The very 
279            descritive minfo.dev_type was not used here.
280         */
281         /* Manufac ID. We do not have access to this with soundcard.h
282            Does not seem to be a problem, because in mmsystem.h only
283            Microsoft's ID is listed */
284         tmplpOutCaps->wMid = 0x00FF;    
285         tmplpOutCaps->wPid = 0x0001;    /* FIXME Product ID */
286         /* Product Version. We simply say "1" */
287         tmplpOutCaps->vDriverVersion = 0x001; 
288         strcpy(tmplpOutCaps->szPname, minfo.name);
289         
290         tmplpOutCaps->wTechnology = MOD_MIDIPORT; /* FIXME Is this right? */
291         /* Does it make any difference? */
292         tmplpOutCaps->wVoices     = 16;            
293         /* Does it make any difference? */
294         tmplpOutCaps->wNotes      = 16;
295         /* FIXME Does it make any difference? */
296         tmplpOutCaps->dwSupport   = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME; 
297         
298         midiOutDevices[numsynthdevs + i] = tmplpOutCaps;
299         
300         tmplpInCaps = HeapAlloc(GetProcessHeap(), 0, sizeof(MIDIOUTCAPSA));
301         if (!tmplpInCaps)
302             break;
303         /* This whole part is somewhat obscure to me. I'll keep trying to dig
304            info about it. If you happen to know, please tell us. The very 
305            descritive minfo.dev_type was not used here.
306         */
307         /* Manufac ID. We do not have access to this with soundcard.h
308            Does not seem to be a problem, because in mmsystem.h only
309            Microsoft's ID is listed */
310         tmplpInCaps->wMid = 0x00FF;     
311         tmplpInCaps->wPid = 0x0001;     /* FIXME Product ID */
312         /* Product Version. We simply say "1" */
313         tmplpInCaps->vDriverVersion = 0x001; 
314         strcpy(tmplpInCaps->szPname, minfo.name);
315         
316         /* FIXME : could we get better information than that ? */
317         tmplpInCaps->dwSupport   = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME; 
318         
319         midiInDevices[i] = tmplpInCaps;
320         
321         TRACE("name='%s' techn=%d voices=%d notes=%d support=%ld\n",
322               tmplpOutCaps->szPname, tmplpOutCaps->wTechnology, tmplpOutCaps->wVoices,
323               tmplpOutCaps->wNotes, tmplpOutCaps->dwSupport);
324         TRACE("OSS info: midi dev-type=%d, capa=%lx\n", 
325               minfo.dev_type, (long)minfo.capabilities);
326     }
327     
328     /* windows does not seem to differentiate Synth from MIDI devices */
329     MODM_NUMFMSYNTHDEVS = numsynthdevs;
330     MODM_NUMMIDIDEVS    = nummididevs;
331     MODM_NUMDEVS        = numsynthdevs + nummididevs;
332     
333     MIDM_NUMDEVS        = nummididevs;
334     
335     /* close file and exit */
336     midiCloseSeq();
337     
338     return TRUE;
339 }
340
341 /**************************************************************************
342  *                      MIDI_NotifyClient                       [internal]
343  */
344 static DWORD MIDI_NotifyClient(UINT wDevID, WORD wMsg, 
345                                DWORD dwParam1, DWORD dwParam2)
346 {
347     DWORD               dwCallBack;
348     UINT                uFlags;
349     HANDLE              hDev;
350     DWORD               dwInstance;
351     
352     TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n", 
353           wDevID, wMsg, dwParam1, dwParam2);
354     
355     switch (wMsg) {
356     case MOM_OPEN:
357     case MOM_CLOSE:
358     case MOM_DONE:
359         if (wDevID > MAX_MIDIOUTDRV) 
360             return MCIERR_INTERNAL;
361         
362         dwCallBack = MidiOutDev[wDevID].midiDesc.dwCallback;
363         uFlags = MidiOutDev[wDevID].wFlags;
364         hDev = MidiOutDev[wDevID].midiDesc.hMidi;
365         dwInstance = MidiOutDev[wDevID].midiDesc.dwInstance;
366         break;
367         
368     case MIM_OPEN:
369     case MIM_CLOSE:
370     case MIM_DATA:
371     case MIM_ERROR:
372         if (wDevID > MAX_MIDIINDRV) 
373             return MCIERR_INTERNAL;
374         
375         dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
376         uFlags = MidiInDev[wDevID].wFlags;
377         hDev = MidiInDev[wDevID].midiDesc.hMidi;
378         dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
379         break;
380     default:
381         WARN("Unsupported MSW-MIDI message %u\n", wMsg);
382         return MCIERR_INTERNAL;
383     }
384     
385     return DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2) ?
386         0 : MCIERR_INTERNAL;
387 }
388
389 static int midi_warn = 1;
390 /**************************************************************************
391  *                      midiOpenSeq                             [internal]
392  */
393 static int midiOpenSeq(void)
394 {
395     if (numOpenMidiSeq == 0) {
396         midiSeqFD = open(MIDI_SEQ, O_RDWR, 0);
397         if (midiSeqFD == -1) {
398             if (midi_warn)
399             {
400                 WARN("Can't open MIDI device '%s' ! (%s). If your "
401                         "program needs this (probably not): %s\n",
402                         MIDI_SEQ, strerror(errno),
403                         errno == ENOENT ?
404                         "create it ! (\"man MAKEDEV\" ?)" :
405                         errno == ENODEV ?
406                         "load MIDI sequencer kernel driver !" :
407                         errno == EACCES ?
408                         "grant access ! (\"man chmod\")" : ""
409                 );
410             }
411             midi_warn = 0;
412             return -1;
413         }
414         if (fcntl(midiSeqFD, F_SETFL, O_NONBLOCK) < 0) {
415             WARN("can't set sequencer fd to non-blocking, errno %d (%s)\n", errno, strerror(errno));
416             close(midiSeqFD);
417             midiSeqFD = -1;
418             return -1;
419         }
420         fcntl(midiSeqFD, F_SETFD, 1); /* set close on exec flag */
421         ioctl(midiSeqFD, SNDCTL_SEQ_RESET);
422     }
423     numOpenMidiSeq++;
424     return 0;
425 }
426
427 /**************************************************************************
428  *                      midiCloseSeq                            [internal]
429  */
430 static int midiCloseSeq(void)
431 {
432     if (--numOpenMidiSeq == 0) {
433         close(midiSeqFD);
434         midiSeqFD = -1;
435     }
436     return 0;
437 }
438
439 /* FIXME: this is a bad idea, it's even not static... */
440 SEQ_DEFINEBUF(1024);
441
442 /* FIXME: this is not reentrant, not static - because of global variable 
443  * _seqbuf and al. 
444  */
445 /**************************************************************************
446  *                      seqbuf_dump                             [internal]
447  */
448 void seqbuf_dump(void)
449 {
450     if (_seqbufptr) {
451         if (write(midiSeqFD, _seqbuf, _seqbufptr) == -1) {
452             WARN("Can't write data to sequencer %d, errno %d (%s)!\n", 
453                  midiSeqFD, errno, strerror(errno));
454         }
455         /* FIXME:
456          *      in any case buffer is lost so that if many errors occur the buffer 
457          * will not overrun 
458          */
459         _seqbufptr = 0;
460     }
461 }
462
463 static void midReceiveChar(WORD wDevID, unsigned char value, DWORD dwTime)
464 {
465     DWORD               toSend = 0;
466     
467     TRACE("Adding %02xh to %d[%d]\n", value, wDevID, MidiInDev[wDevID].incLen);
468     
469     if (wDevID >= MAX_MIDIINDRV) {
470         WARN("bad devID\n");
471         return;
472     }
473     if (MidiInDev[wDevID].state == 0) {
474         TRACE("input not started, thrown away\n");
475         return;
476     }
477
478     if (MidiInDev[wDevID].state & 2) { /* system exclusive */
479         LPMIDIHDR       lpMidiHdr = MidiInDev[wDevID].lpQueueHdr;
480         WORD            sbfb = FALSE;
481
482         if (lpMidiHdr) {
483             LPBYTE      lpData = lpMidiHdr->lpData;
484         
485             lpData[lpMidiHdr->dwBytesRecorded++] = value;
486             if (lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) {
487                 sbfb = TRUE;
488             } 
489         }
490         if (value == 0xF7) { /* then end */
491             MidiInDev[wDevID].state &= ~2;
492             sbfb = TRUE;
493         }
494         if (sbfb && lpMidiHdr != NULL) {
495             lpMidiHdr = MidiInDev[wDevID].lpQueueHdr;
496             lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
497             lpMidiHdr->dwFlags |= MHDR_DONE;
498             MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)lpMidiHdr->lpNext;
499             if (MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD)lpMidiHdr, dwTime) != MMSYSERR_NOERROR) {
500                 WARN("Couldn't notify client\n");
501             }
502         }
503         return;
504     }
505     
506 #define IS_CMD(_x)      (((_x) & 0x80) == 0x80)
507 #define IS_SYS_CMD(_x)  (((_x) & 0xF0) == 0xF0)
508     
509     if (!IS_CMD(value) && MidiInDev[wDevID].incLen == 0) { /* try to reuse old cmd */
510         if (IS_CMD(MidiInDev[wDevID].incPrev) && !IS_SYS_CMD(MidiInDev[wDevID].incPrev)) {
511             MidiInDev[wDevID].incoming[0] = MidiInDev[wDevID].incPrev;
512             MidiInDev[wDevID].incLen = 1;
513             TRACE("Reusing old command %02xh\n", MidiInDev[wDevID].incPrev);
514         } else {
515             FIXME("error for midi-in, should generate MIM_ERROR notification:"
516                   " prev=%02Xh, incLen=%02Xh\n", 
517                   MidiInDev[wDevID].incPrev, MidiInDev[wDevID].incLen);
518             return;
519         }
520     }
521     MidiInDev[wDevID].incoming[(int)(MidiInDev[wDevID].incLen++)] = value;
522     if (MidiInDev[wDevID].incLen == 1 && !IS_SYS_CMD(MidiInDev[wDevID].incoming[0])) {
523         /* store new cmd, just in case */
524         MidiInDev[wDevID].incPrev = MidiInDev[wDevID].incoming[0];
525     }
526
527 #undef IS_CMD
528 #undef IS_SYS_CMD
529
530     switch (MidiInDev[wDevID].incoming[0] & 0xF0) {
531     case MIDI_NOTEOFF:
532     case MIDI_NOTEON:
533     case MIDI_KEY_PRESSURE:
534     case MIDI_CTL_CHANGE:
535     case MIDI_PITCH_BEND:
536         if (MidiInDev[wDevID].incLen == 3) {
537             toSend = (MidiInDev[wDevID].incoming[2] << 16) | 
538                 (MidiInDev[wDevID].incoming[1] <<  8) |
539                 (MidiInDev[wDevID].incoming[0] <<  0);
540         }
541         break;
542     case MIDI_PGM_CHANGE:
543     case MIDI_CHN_PRESSURE:
544         if (MidiInDev[wDevID].incLen == 2) {
545             toSend = (MidiInDev[wDevID].incoming[1] <<  8) |
546                 (MidiInDev[wDevID].incoming[0] <<  0);
547         }
548         break;
549     case MIDI_SYSTEM_PREFIX:
550         if (MidiInDev[wDevID].incoming[0] == 0xF0) {
551             MidiInDev[wDevID].state |= 2;
552             MidiInDev[wDevID].incLen = 0;
553         } else {                
554             if (MidiInDev[wDevID].incLen == 1) {
555                 toSend = (MidiInDev[wDevID].incoming[0] <<  0);
556             }
557         }
558         break;
559     default:
560         WARN("This shouldn't happen (%02X)\n", MidiInDev[wDevID].incoming[0]);
561     }
562     if (toSend != 0) {
563         TRACE("Sending event %08lx\n", toSend);
564         MidiInDev[wDevID].incLen =      0;
565         dwTime -= MidiInDev[wDevID].startTime;
566         if (MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime) != MMSYSERR_NOERROR) {
567             WARN("Couldn't notify client\n");
568         }
569     }
570 }
571
572 static VOID WINAPI midTimeCallback(HWND hwnd, UINT msg, UINT id, DWORD dwTime)
573 {
574     unsigned    char            buffer[256];
575     int                         len, idx;
576     
577     TRACE("(%04X, %d, %d, %lu)\n", hwnd, msg, id, dwTime);
578     
579     len = read(midiSeqFD, buffer, sizeof(buffer));
580     
581     if (len < 0) return;
582     if ((len % 4) != 0) {
583         WARN("Bad length %d, errno %d (%s)\n", len, errno, strerror(errno));
584         return;
585     }
586     
587     for (idx = 0; idx < len; ) {
588         if (buffer[idx] & 0x80) {
589             TRACE(
590                   "Reading<8> %02x %02x %02x %02x %02x %02x %02x %02x\n", 
591                   buffer[idx + 0], buffer[idx + 1], 
592                   buffer[idx + 2], buffer[idx + 3], 
593                   buffer[idx + 4], buffer[idx + 5], 
594                   buffer[idx + 6], buffer[idx + 7]);
595             idx += 8;
596         } else {
597             switch (buffer[idx + 0]) {
598             case SEQ_WAIT:
599             case SEQ_ECHO:
600                 break;
601             case SEQ_MIDIPUTC:
602                 midReceiveChar(buffer[idx + 2], buffer[idx + 1], dwTime);
603                 break;
604             default:
605                 TRACE("Unsupported event %d\n", buffer[idx + 0]);
606                 break;
607             }
608             idx += 4;
609         }                               
610     }
611 }
612
613 /**************************************************************************
614  *                              midGetDevCaps                   [internal]
615  */
616 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSA lpCaps, DWORD dwSize)
617 {
618     TRACE("(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize);
619     
620     if (wDevID >= MIDM_NUMDEVS) return MMSYSERR_BADDEVICEID;
621     if (lpCaps == NULL)         return MMSYSERR_INVALPARAM;
622
623     memcpy(lpCaps, midiInDevices[wDevID], min(dwSize, sizeof(*lpCaps)));
624     
625     return MMSYSERR_NOERROR;
626 }
627
628 /**************************************************************************
629  *                      midOpen                                 [internal]
630  */
631 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
632 {
633     TRACE("(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
634     
635     if (lpDesc == NULL) {
636         WARN("Invalid Parameter !\n");
637         return MMSYSERR_INVALPARAM;
638     }
639
640     /* FIXME :
641      *  how to check that content of lpDesc is correct ?
642      */
643     if (wDevID >= MAX_MIDIINDRV) {
644         WARN("wDevID too large (%u) !\n", wDevID);
645         return MMSYSERR_BADDEVICEID;
646     }
647     if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
648         WARN("device already open !\n");
649         return MMSYSERR_ALLOCATED;
650     }
651     if ((dwFlags & MIDI_IO_STATUS) != 0) { 
652         WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
653         dwFlags &= ~MIDI_IO_STATUS;
654     }
655     if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) { 
656         FIXME("Bad dwFlags\n");
657         return MMSYSERR_INVALFLAG;
658     }
659     
660     if (midiOpenSeq() < 0) {
661         return MMSYSERR_ERROR;
662     }
663     
664     if (numStartedMidiIn++ == 0) {
665         midiInTimerID = SetTimer(0, 0, 250, midTimeCallback);
666         if (!midiInTimerID) {
667             numStartedMidiIn = 0;
668             WARN("Couldn't start timer for midi-in\n");
669             midiCloseSeq();
670             return MMSYSERR_ERROR;
671         }
672         TRACE("Starting timer (%u) for midi-in\n", midiInTimerID);
673     }
674     
675     MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
676
677     MidiInDev[wDevID].lpQueueHdr = NULL;
678     MidiInDev[wDevID].dwTotalPlayed = 0;
679     MidiInDev[wDevID].bufsize = 0x3FFF;
680     MidiInDev[wDevID].midiDesc = *lpDesc;
681     MidiInDev[wDevID].state = 0;
682     MidiInDev[wDevID].incLen = 0;
683     MidiInDev[wDevID].startTime = 0;
684
685     if (MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
686         WARN("can't notify client !\n");
687         return MMSYSERR_INVALPARAM;
688     }
689     return MMSYSERR_NOERROR;
690 }
691
692 /**************************************************************************
693  *                      midClose                                [internal]
694  */
695 static DWORD midClose(WORD wDevID)
696 {
697     int         ret = MMSYSERR_NOERROR;
698     
699     TRACE("(%04X);\n", wDevID);
700     
701     if (wDevID >= MAX_MIDIINDRV) {
702         WARN("wDevID too big (%u) !\n", wDevID);
703         return MMSYSERR_BADDEVICEID;
704     }
705     if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
706         WARN("device not opened !\n");
707         return MMSYSERR_ERROR;
708     }
709     if (MidiInDev[wDevID].lpQueueHdr != 0) {
710         return MIDIERR_STILLPLAYING;
711     }
712     
713     if (midiSeqFD == -1) {
714         WARN("ooops !\n");
715         return MMSYSERR_ERROR;
716     }
717     if (--numStartedMidiIn == 0) {
718         TRACE("Stopping timer for midi-in\n");
719         if (!KillTimer(0, midiInTimerID)) {
720             WARN("Couldn't stop timer for midi-in\n");
721         }                       
722         midiInTimerID = 0;
723     }
724     midiCloseSeq();
725
726     MidiInDev[wDevID].bufsize = 0;
727     if (MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
728         WARN("can't notify client !\n");
729         ret = MMSYSERR_INVALPARAM;
730     }
731     MidiInDev[wDevID].midiDesc.hMidi = 0;
732     return ret;
733 }
734
735 /**************************************************************************
736  *                              midAddBuffer                    [internal]
737  */
738 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
739 {
740     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
741     
742     if (lpMidiHdr == NULL)      return MMSYSERR_INVALPARAM;
743     if (sizeof(MIDIHDR) > dwSize) return MMSYSERR_INVALPARAM;
744     if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
745     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
746     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
747     
748     if (MidiInDev[wDevID].lpQueueHdr == 0) {
749         MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
750     } else {
751         LPMIDIHDR       ptr;
752         
753         for (ptr = MidiInDev[wDevID].lpQueueHdr; 
754              ptr->lpNext != 0; 
755              ptr = (LPMIDIHDR)ptr->lpNext);
756         ptr->lpNext = (struct midihdr_tag*)lpMidiHdr;
757     }
758     return MMSYSERR_NOERROR;
759 }
760
761 /**************************************************************************
762  *                              midPrepare                      [internal]
763  */
764 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
765 {
766     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
767     
768     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 || 
769         lpMidiHdr->lpData == 0 || lpMidiHdr->dwFlags != 0 || 
770         lpMidiHdr->dwBufferLength >= 0x10000ul)
771         return MMSYSERR_INVALPARAM;
772     
773     lpMidiHdr->lpNext = 0;
774     lpMidiHdr->dwFlags |= MHDR_PREPARED;
775     lpMidiHdr->dwBytesRecorded = 0;
776     
777     return MMSYSERR_NOERROR;
778 }
779
780 /**************************************************************************
781  *                              midUnprepare                    [internal]
782  */
783 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
784 {
785     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
786     
787     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 || 
788         lpMidiHdr->lpData == 0 || lpMidiHdr->dwBufferLength >= 0x10000ul)
789         return MMSYSERR_INVALPARAM;
790     
791     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
792     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
793     
794     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
795     
796     return MMSYSERR_NOERROR;
797 }
798
799 /**************************************************************************
800  *                      midReset                                [internal]
801  */
802 static DWORD midReset(WORD wDevID)
803 {
804     DWORD               dwTime = GetTickCount();
805     
806     TRACE("(%04X);\n", wDevID);
807     
808     while (MidiInDev[wDevID].lpQueueHdr) {
809         MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
810         MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
811         /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
812         if (MIDI_NotifyClient(wDevID, MIM_LONGDATA, 
813                               (DWORD)MidiInDev[wDevID].lpQueueHdr, dwTime) != MMSYSERR_NOERROR) {
814             WARN("Couldn't notify client\n");
815         }
816         MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)MidiInDev[wDevID].lpQueueHdr->lpNext;
817     }
818     
819     return MMSYSERR_NOERROR;
820 }
821
822
823 /**************************************************************************
824  *                      midStart                                [internal]
825  */
826 static DWORD midStart(WORD wDevID)
827 {
828     TRACE("(%04X);\n", wDevID);
829     
830     /* FIXME : should test value of wDevID */
831     
832     MidiInDev[wDevID].state = 1;
833     MidiInDev[wDevID].startTime = GetTickCount();
834     return MMSYSERR_NOERROR;
835 }
836
837 /**************************************************************************
838  *                      midStop                                 [internal]
839  */
840 static DWORD midStop(WORD wDevID)
841 {
842     TRACE("(%04X);\n", wDevID);
843     
844     /* FIXME : should test value of wDevID */
845     MidiInDev[wDevID].state = 0;
846     return MMSYSERR_NOERROR;
847 }
848
849 /*-----------------------------------------------------------------------*/
850
851 typedef struct sVoice {
852     int                 note;                   /* 0 means not used */
853     int                 channel;
854     unsigned            cntMark : 30,
855                         status : 2;
856 #define sVS_UNUSED      0
857 #define sVS_PLAYING     1
858 #define sVS_SUSTAINED   2
859 } sVoice;
860
861 typedef struct sChannel {
862     int                 program;
863     
864     int                 bender;
865     int                 benderRange;
866     /* controlers */
867     int                 bank;           /* CTL_BANK_SELECT */
868     int                 volume;         /* CTL_MAIN_VOLUME */
869     int                 balance;        /* CTL_BALANCE     */
870     int                 expression;     /* CTL_EXPRESSION  */
871     int                 sustain;        /* CTL_SUSTAIN     */
872     
873     unsigned char       nrgPmtMSB;      /* Non register Parameters */
874     unsigned char       nrgPmtLSB;
875     unsigned char       regPmtMSB;      /* Non register Parameters */
876     unsigned char       regPmtLSB;
877 } sChannel;
878
879 typedef struct sFMextra {
880     unsigned            counter;
881     int                 drumSetMask;
882     sChannel            channel[16];    /* MIDI has only 16 channels */
883     sVoice              voice[1];       /* dyn allocated according to sound card */
884     /* do not append fields below voice[1] since the size of this structure 
885      * depends on the number of available voices on the FM synth...
886      */
887 } sFMextra;
888
889 extern  unsigned char midiFMInstrumentPatches[16 * 128];
890 extern  unsigned char midiFMDrumsPatches     [16 * 128];
891
892 /**************************************************************************
893  *                      modFMLoad                               [internal]
894  */
895 static int modFMLoad(int dev)
896 {
897     int                         i;
898     struct sbi_instrument       sbi;
899     
900     sbi.device = dev;
901     sbi.key = FM_PATCH;
902     
903     memset(sbi.operators + 16, 0, 16);
904     for (i = 0; i < 128; i++) {
905         sbi.channel = i;
906         memcpy(sbi.operators, midiFMInstrumentPatches + i * 16, 16);
907         
908         if (write(midiSeqFD, (char*)&sbi, sizeof(sbi)) == -1) {
909             WARN("Couldn't write patch for instrument %d, errno %d (%s)!\n", sbi.channel, errno, strerror(errno));
910             return -1;
911         }
912     } 
913     for (i = 0; i < 128; i++) {
914         sbi.channel = 128 + i;
915         memcpy(sbi.operators, midiFMDrumsPatches + i * 16, 16);
916         
917         if (write(midiSeqFD, (char*)&sbi, sizeof(sbi)) == -1) {
918             WARN("Couldn't write patch for drum %d, errno %d (%s)!\n", sbi.channel, errno, strerror(errno));
919             return -1;
920         }
921     } 
922     return 0;
923 }
924
925 /**************************************************************************
926  *                      modFMReset                              [internal]
927  */
928 static  void modFMReset(WORD wDevID)
929 {
930     sFMextra*   extra   = (sFMextra*)MidiOutDev[wDevID].lpExtra;
931     sVoice*     voice   = extra->voice;
932     sChannel*   channel = extra->channel;
933     int         i;
934     
935     for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
936         if (voice[i].status != sVS_UNUSED) {
937             SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
938         }
939         SEQ_KEY_PRESSURE(wDevID, i, 127, 0);
940         SEQ_CONTROL(wDevID, i, SEQ_VOLMODE, VOL_METHOD_LINEAR);
941         voice[i].note = 0;
942         voice[i].channel = -1;
943         voice[i].cntMark = 0;
944         voice[i].status = sVS_UNUSED;
945     }
946     for (i = 0; i < 16; i++) {
947         channel[i].program = 0;
948         channel[i].bender = 8192;
949         channel[i].benderRange = 2;
950         channel[i].bank = 0;
951         channel[i].volume = 127;
952         channel[i].balance = 64;
953         channel[i].expression = 0;      
954         channel[i].sustain = 0; 
955     }
956     extra->counter = 0;
957     extra->drumSetMask = 1 << 9; /* channel 10 is normally drums, sometimes 16 also */
958     SEQ_DUMPBUF();
959 }
960
961 #define         IS_DRUM_CHANNEL(_xtra, _chn)    ((_xtra)->drumSetMask & (1 << (_chn)))
962
963 /**************************************************************************
964  *                              modGetDevCaps                   [internal]
965  */
966 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSA lpCaps, DWORD dwSize)
967 {
968     TRACE("(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize);
969
970     if (wDevID >= MODM_NUMDEVS) return MMSYSERR_BADDEVICEID;
971     if (lpCaps == NULL)         return MMSYSERR_INVALPARAM;
972
973     memcpy(lpCaps, midiOutDevices[wDevID], min(dwSize, sizeof(*lpCaps)));
974
975     return MMSYSERR_NOERROR;
976 }
977
978 /**************************************************************************
979  *                      modOpen                                 [internal]
980  */
981 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
982 {
983     TRACE("(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
984     if (lpDesc == NULL) {
985         WARN("Invalid Parameter !\n");
986         return MMSYSERR_INVALPARAM;
987     }
988     if (wDevID >= MAX_MIDIOUTDRV) {
989         TRACE("MAX_MIDIOUTDRV reached !\n");
990         return MMSYSERR_BADDEVICEID;
991     }
992     if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
993         WARN("device already open !\n");
994         return MMSYSERR_ALLOCATED;
995     }
996     if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) { 
997         WARN("bad dwFlags\n");
998         return MMSYSERR_INVALFLAG;
999     }
1000     if (midiOutDevices[wDevID] == NULL) {
1001         TRACE("un-allocated wDevID\n");
1002         return MMSYSERR_BADDEVICEID;
1003     }
1004     
1005     MidiOutDev[wDevID].lpExtra = 0;
1006     
1007     switch (midiOutDevices[wDevID]->wTechnology) {
1008     case MOD_FMSYNTH:
1009         {
1010             void*       extra = HeapAlloc(GetProcessHeap(), 0, 
1011                                           sizeof(struct sFMextra) + 
1012                                           sizeof(struct sVoice) * (midiOutDevices[wDevID]->wVoices - 1));
1013             
1014             if (extra == 0) {
1015                 WARN("can't alloc extra data !\n");
1016                 return MMSYSERR_NOMEM;
1017             }
1018             MidiOutDev[wDevID].lpExtra = extra;
1019             if (midiOpenSeq() < 0) {
1020                 MidiOutDev[wDevID].lpExtra = 0;
1021                 HeapFree(GetProcessHeap(), 0, extra);
1022                 return MMSYSERR_ERROR;
1023             }
1024             if (modFMLoad(wDevID) < 0) {
1025                 midiCloseSeq();
1026                 MidiOutDev[wDevID].lpExtra = 0;
1027                 HeapFree(GetProcessHeap(), 0, extra);
1028                 return MMSYSERR_ERROR;
1029             }
1030             modFMReset(wDevID);
1031         }
1032         break;
1033     case MOD_MIDIPORT:
1034         if (midiOpenSeq() < 0) {
1035             return MMSYSERR_ALLOCATED;
1036         }
1037         break;
1038     default:
1039         WARN("Technology not supported (yet) %d !\n", 
1040              midiOutDevices[wDevID]->wTechnology);
1041         return MMSYSERR_NOTENABLED;
1042     }
1043     
1044     MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1045
1046     MidiOutDev[wDevID].lpQueueHdr = NULL;
1047     MidiOutDev[wDevID].dwTotalPlayed = 0;
1048     MidiOutDev[wDevID].bufsize = 0x3FFF;
1049     MidiOutDev[wDevID].midiDesc = *lpDesc;
1050     
1051     if (MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1052         WARN("can't notify client !\n");
1053         return MMSYSERR_INVALPARAM;
1054     }
1055     TRACE("Successful !\n");
1056     return MMSYSERR_NOERROR;
1057 }
1058
1059
1060 /**************************************************************************
1061  *                      modClose                                [internal]
1062  */
1063 static DWORD modClose(WORD wDevID)
1064 {
1065     int ret = MMSYSERR_NOERROR;
1066
1067     TRACE("(%04X);\n", wDevID);
1068     
1069     if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
1070         WARN("device not opened !\n");
1071         return MMSYSERR_ERROR;
1072     }
1073     /* FIXME: should test that no pending buffer is still in the queue for
1074      * playing */
1075     
1076     if (midiSeqFD == -1) {
1077         WARN("can't close !\n");
1078         return MMSYSERR_ERROR;
1079     }
1080     
1081     switch (midiOutDevices[wDevID]->wTechnology) {
1082     case MOD_FMSYNTH:
1083     case MOD_MIDIPORT:
1084         midiCloseSeq();
1085         break;
1086     default:
1087         WARN("Technology not supported (yet) %d !\n", 
1088              midiOutDevices[wDevID]->wTechnology);
1089         return MMSYSERR_NOTENABLED;
1090     }
1091     
1092     if (MidiOutDev[wDevID].lpExtra != 0) {
1093         HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
1094         MidiOutDev[wDevID].lpExtra = 0;
1095     }
1096     
1097     MidiOutDev[wDevID].bufsize = 0;
1098     if (MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
1099         WARN("can't notify client !\n");
1100         ret = MMSYSERR_INVALPARAM;
1101     }
1102     MidiOutDev[wDevID].midiDesc.hMidi = 0;
1103     return ret;
1104 }
1105
1106 /**************************************************************************
1107  *                      modData                                 [internal]
1108  */
1109 static DWORD modData(WORD wDevID, DWORD dwParam)
1110 {
1111     WORD        evt = LOBYTE(LOWORD(dwParam));
1112     WORD        d1  = HIBYTE(LOWORD(dwParam));
1113     WORD        d2  = LOBYTE(HIWORD(dwParam));
1114     
1115     TRACE("(%04X, %08lX);\n", wDevID, dwParam);
1116     
1117     if (midiSeqFD == -1) {
1118         WARN("can't play !\n");
1119         return MIDIERR_NODEVICE;
1120     }
1121     switch (midiOutDevices[wDevID]->wTechnology) {
1122     case MOD_FMSYNTH:
1123         /* FIXME:
1124          *      - chorus depth controller is not used
1125          */
1126         {
1127             sFMextra*   extra   = (sFMextra*)MidiOutDev[wDevID].lpExtra;
1128             sVoice*     voice   = extra->voice;
1129             sChannel*   channel = extra->channel;
1130             int         chn = (evt & 0x0F);
1131             int         i, nv;
1132             
1133             switch (evt & 0xF0) {
1134             case MIDI_NOTEOFF:
1135                 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1136                                 /* don't stop sustained notes */
1137                     if (voice[i].status == sVS_PLAYING && voice[i].channel == chn && voice[i].note == d1) {
1138                         voice[i].status = sVS_UNUSED;
1139                         SEQ_STOP_NOTE(wDevID, i, d1, d2);
1140                     }
1141                 }
1142                 break;  
1143             case MIDI_NOTEON:
1144                 if (d2 == 0) { /* note off if velocity == 0 */
1145                     for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1146                         /* don't stop sustained notes */
1147                         if (voice[i].status == sVS_PLAYING && voice[i].channel == chn && voice[i].note == d1) {
1148                             voice[i].status = sVS_UNUSED;
1149                             SEQ_STOP_NOTE(wDevID, i, d1, 64);
1150                         }
1151                     }
1152                     break;
1153                 }
1154                 /* finding out in this order :
1155                  *      - an empty voice
1156                  *      - if replaying the same note on the same channel
1157                  *      - the older voice (LRU)
1158                  */
1159                 for (i = nv = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1160                     if (voice[i].status == sVS_UNUSED || 
1161                         (voice[i].note == d1 && voice[i].channel == chn)) {
1162                         nv = i;
1163                         break;
1164                     }
1165                     if (voice[i].cntMark < voice[0].cntMark) {
1166                         nv = i;
1167                     }
1168                 }
1169                 TRACE(
1170                       "playing on voice=%d, pgm=%d, pan=0x%02X, vol=0x%02X, "
1171                       "bender=0x%02X, note=0x%02X, vel=0x%02X\n", 
1172                       nv, channel[chn].program, 
1173                       channel[chn].balance, 
1174                       channel[chn].volume, 
1175                       channel[chn].bender, d1, d2);
1176                 
1177                 SEQ_SET_PATCH(wDevID, nv, IS_DRUM_CHANNEL(extra, chn) ? 
1178                               (128 + d1) : channel[chn].program);
1179                 SEQ_BENDER_RANGE(wDevID, nv, channel[chn].benderRange * 100);
1180                 SEQ_BENDER(wDevID, nv, channel[chn].bender);
1181                 SEQ_CONTROL(wDevID, nv, CTL_PAN, channel[chn].balance);
1182                 SEQ_CONTROL(wDevID, nv, CTL_EXPRESSION, channel[chn].expression);
1183 #if 0   
1184                 /* FIXME: does not really seem to work on my SB card and
1185                  * screws everything up... so lay it down
1186                  */
1187                 SEQ_CONTROL(wDevID, nv, CTL_MAIN_VOLUME, channel[chn].volume);
1188 #endif  
1189                 SEQ_START_NOTE(wDevID, nv, d1, d2);
1190                 voice[nv].status = channel[chn].sustain ? sVS_SUSTAINED : sVS_PLAYING;
1191                 voice[nv].note = d1;
1192                 voice[nv].channel = chn;
1193                 voice[nv].cntMark = extra->counter++;
1194                 break;
1195             case MIDI_KEY_PRESSURE:
1196                 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1197                     if (voice[i].status != sVS_UNUSED && voice[i].channel == chn && voice[i].note == d1) {
1198                         SEQ_KEY_PRESSURE(wDevID, i, d1, d2);
1199                     }
1200                 }
1201                 break;
1202             case MIDI_CTL_CHANGE:
1203                 switch (d1) {
1204                 case CTL_BANK_SELECT:   channel[chn].bank = d2;         break;
1205                 case CTL_MAIN_VOLUME:   channel[chn].volume = d2;       break;
1206                 case CTL_PAN:           channel[chn].balance = d2;      break;
1207                 case CTL_EXPRESSION:    channel[chn].expression = d2;   break;
1208                 case CTL_SUSTAIN:       channel[chn].sustain = d2;
1209                     if (d2) {
1210                         for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1211                             if (voice[i].status == sVS_PLAYING && voice[i].channel == chn) {
1212                                 voice[i].status = sVS_SUSTAINED;
1213                             }
1214                         }
1215                     } else {
1216                         for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1217                             if (voice[i].status == sVS_SUSTAINED && voice[i].channel == chn) {
1218                                 voice[i].status = sVS_UNUSED;
1219                                 SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1220                             }
1221                         }
1222                     }
1223                     break;
1224                 case CTL_NONREG_PARM_NUM_LSB:   channel[chn].nrgPmtLSB = d2;    break;
1225                 case CTL_NONREG_PARM_NUM_MSB:   channel[chn].nrgPmtMSB = d2;    break;
1226                 case CTL_REGIST_PARM_NUM_LSB:   channel[chn].regPmtLSB = d2;    break;
1227                 case CTL_REGIST_PARM_NUM_MSB:   channel[chn].regPmtMSB = d2;    break;              
1228                 case CTL_DATA_ENTRY:
1229                     switch ((channel[chn].regPmtMSB << 8) | channel[chn].regPmtLSB) {
1230                     case 0x0000: 
1231                         if (channel[chn].benderRange != d2) {
1232                             channel[chn].benderRange = d2;
1233                             for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1234                                 if (voice[i].channel == chn) {
1235                                     SEQ_BENDER_RANGE(wDevID, i, channel[chn].benderRange);
1236                                 }
1237                             }
1238                         }
1239                         break;
1240                         
1241                     case 0x7F7F:
1242                         channel[chn].benderRange = 2;
1243                         for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1244                             if (voice[i].channel == chn) {
1245                                 SEQ_BENDER_RANGE(wDevID, i, channel[chn].benderRange);
1246                             }
1247                         }
1248                         break;
1249                     default:
1250                         TRACE("Data entry: regPmt=0x%02x%02x, nrgPmt=0x%02x%02x with %x\n",
1251                               channel[chn].regPmtMSB, channel[chn].regPmtLSB,
1252                               channel[chn].nrgPmtMSB, channel[chn].nrgPmtLSB,
1253                               d2);
1254                         break;
1255                     }
1256                     break;
1257                     
1258                 case 0x78: /* all sounds off */
1259                     /* FIXME: I don't know if I have to take care of the channel 
1260                      * for this control ?
1261                      */
1262                     for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1263                         if (voice[i].status != sVS_UNUSED && voice[i].channel == chn) {
1264                             voice[i].status = sVS_UNUSED;
1265                             SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1266                         }
1267                     }
1268                     break;
1269                 case 0x7B: /* all notes off */
1270                     /* FIXME: I don't know if I have to take care of the channel 
1271                      * for this control ?
1272                      */
1273                     for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1274                         if (voice[i].status == sVS_PLAYING && voice[i].channel == chn) {
1275                             voice[i].status = sVS_UNUSED;
1276                             SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1277                         }
1278                     }
1279                     break;      
1280                 default:
1281                     TRACE("Dropping MIDI control event 0x%02x(%02x) on channel %d\n", 
1282                           d1, d2, chn);
1283                     break;
1284                 }
1285                 break;
1286             case MIDI_PGM_CHANGE:
1287                 channel[chn].program = d1;
1288                 break;
1289             case MIDI_CHN_PRESSURE:
1290                 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1291                     if (voice[i].status != sVS_UNUSED && voice[i].channel == chn) {
1292                         SEQ_KEY_PRESSURE(wDevID, i, voice[i].note, d1);
1293                     }
1294                 }
1295                 break;
1296             case MIDI_PITCH_BEND:
1297                 channel[chn].bender = (d2 << 7) + d1;
1298                 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1299                     if (voice[i].channel == chn) {
1300                         SEQ_BENDER(wDevID, i, channel[chn].bender);
1301                     }
1302                 }
1303                 break;
1304             case MIDI_SYSTEM_PREFIX:
1305                 switch (evt & 0x0F) {
1306                 case 0x0F:      /* Reset */
1307                     modFMReset(wDevID);
1308                     break; 
1309                 default:
1310                     WARN("Unsupported (yet) system event %02x\n", evt & 0x0F);
1311                 }
1312                 break;
1313             default:    
1314                 WARN("Internal error, shouldn't happen (event=%08x)\n", evt & 0xF0);
1315                 return MMSYSERR_NOTENABLED;
1316             }
1317         }
1318         break;
1319     case MOD_MIDIPORT:
1320         {
1321             int dev = wDevID - MODM_NUMFMSYNTHDEVS;
1322             if (dev < 0) {
1323                 WARN("Internal error on devID (%u) !\n", wDevID);
1324                 return MIDIERR_NODEVICE;
1325             }
1326             
1327             switch (evt & 0xF0) {
1328             case MIDI_NOTEOFF:
1329             case MIDI_NOTEON:
1330             case MIDI_KEY_PRESSURE:
1331             case MIDI_CTL_CHANGE:
1332             case MIDI_PITCH_BEND:
1333                 SEQ_MIDIOUT(dev, evt);  
1334                 SEQ_MIDIOUT(dev, d1);   
1335                 SEQ_MIDIOUT(dev, d2);   
1336                 break;
1337             case MIDI_PGM_CHANGE:
1338             case MIDI_CHN_PRESSURE:
1339                 SEQ_MIDIOUT(dev, evt);  
1340                 SEQ_MIDIOUT(dev, d1);                                                                           
1341                 break;  
1342             case MIDI_SYSTEM_PREFIX:
1343                 switch (evt & 0x0F) {
1344                 case 0x00:      /* System Exclusive, don't do it on modData, 
1345                                  * should require modLongData*/
1346                 case 0x01:      /* Undefined */
1347                 case 0x04:      /* Undefined. */
1348                 case 0x05:      /* Undefined. */
1349                 case 0x07:      /* End of Exclusive. */
1350                 case 0x09:      /* Undefined. */
1351                 case 0x0D:      /* Undefined. */
1352                     break;
1353                 case 0x06:      /* Tune Request */
1354                 case 0x08:      /* Timing Clock. */
1355                 case 0x0A:      /* Start. */
1356                 case 0x0B:      /* Continue */
1357                 case 0x0C:      /* Stop */
1358                 case 0x0E:      /* Active Sensing. */
1359                     SEQ_MIDIOUT(dev, evt);      
1360                     break;
1361                 case 0x0F:      /* Reset */
1362                                 /* SEQ_MIDIOUT(dev, evt);
1363                                    this other way may be better */
1364                     SEQ_MIDIOUT(dev, MIDI_SYSTEM_PREFIX);
1365                     SEQ_MIDIOUT(dev, 0x7e);
1366                     SEQ_MIDIOUT(dev, 0x7f);
1367                     SEQ_MIDIOUT(dev, 0x09);
1368                     SEQ_MIDIOUT(dev, 0x01);
1369                     SEQ_MIDIOUT(dev, 0xf7);
1370                     break;
1371                 case 0x03:      /* Song Select. */
1372                     SEQ_MIDIOUT(dev, evt);      
1373                     SEQ_MIDIOUT(dev, d1);                                                                               
1374                 case 0x02:      /* Song Position Pointer. */
1375                     SEQ_MIDIOUT(dev, evt);      
1376                     SEQ_MIDIOUT(dev, d1);
1377                     SEQ_MIDIOUT(dev, d2);
1378                 }
1379                 break;
1380             }
1381         }
1382         break;
1383     default:
1384         WARN("Technology not supported (yet) %d !\n", 
1385              midiOutDevices[wDevID]->wTechnology);
1386         return MMSYSERR_NOTENABLED;
1387     }
1388     
1389     SEQ_DUMPBUF();
1390
1391     return MMSYSERR_NOERROR;
1392 }
1393
1394 /**************************************************************************
1395  *              modLongData                                     [internal]
1396  */
1397 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1398 {
1399     int         count;
1400     LPBYTE      lpData;
1401
1402     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1403     
1404     if (midiSeqFD == -1) {
1405         WARN("can't play !\n");
1406         return MIDIERR_NODEVICE;
1407     }
1408     
1409     lpData = lpMidiHdr->lpData;
1410
1411     if (lpData == NULL) 
1412         return MIDIERR_UNPREPARED;
1413     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) 
1414         return MIDIERR_UNPREPARED;
1415     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) 
1416         return MIDIERR_STILLPLAYING;
1417     lpMidiHdr->dwFlags &= ~MHDR_DONE;
1418     lpMidiHdr->dwFlags |= MHDR_INQUEUE;
1419
1420     /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
1421      * data, or can it also contain raw MIDI data, to be split up and sent to 
1422      * modShortData() ?
1423      * If the latest is true, then the following WARNing will fire up
1424      */
1425     if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
1426         WARN("Alledged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
1427     }
1428
1429     TRACE("dwBufferLength=%lu !\n", lpMidiHdr->dwBufferLength);
1430     TRACE("                 %02X %02X %02X ... %02X %02X %02X\n",
1431           lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3], 
1432           lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
1433
1434     switch (midiOutDevices[wDevID]->wTechnology) {
1435     case MOD_FMSYNTH:
1436         /* FIXME: I don't think there is much to do here */
1437         break;
1438     case MOD_MIDIPORT:
1439         if (lpData[0] != 0xF0) {
1440             /* Send end of System Exclusive */
1441             SEQ_MIDIOUT(wDevID - MODM_NUMFMSYNTHDEVS, 0xF0);
1442             WARN("Adding missing 0xF0 marker at the beginning of "
1443                  "system exclusive byte stream\n");
1444         }
1445         for (count = 0; count < lpMidiHdr->dwBytesRecorded; count++) {
1446             SEQ_MIDIOUT(wDevID - MODM_NUMFMSYNTHDEVS, lpData[count]);   
1447         }
1448         if (lpData[count - 1] != 0xF7) {
1449             /* Send end of System Exclusive */
1450             SEQ_MIDIOUT(wDevID - MODM_NUMFMSYNTHDEVS, 0xF7);
1451             WARN("Adding missing 0xF7 marker at the end of "
1452                  "system exclusive byte stream\n");
1453         }
1454         SEQ_DUMPBUF();
1455         break;
1456     default:
1457         WARN("Technology not supported (yet) %d !\n", 
1458              midiOutDevices[wDevID]->wTechnology);
1459         return MMSYSERR_NOTENABLED;
1460     }
1461     
1462     lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1463     lpMidiHdr->dwFlags |= MHDR_DONE;
1464     if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD)lpMidiHdr, 0L) != MMSYSERR_NOERROR) {
1465         WARN("can't notify client !\n");
1466         return MMSYSERR_INVALPARAM;
1467     }
1468     return MMSYSERR_NOERROR;
1469 }
1470
1471 /**************************************************************************
1472  *                      modPrepare                              [internal]
1473  */
1474 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1475 {
1476     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1477     
1478     if (midiSeqFD == -1) {
1479         WARN("can't prepare !\n");
1480         return MMSYSERR_NOTENABLED;
1481     }
1482
1483     /* MS doc says taht dwFlags must be set to zero, but (kinda funny) MS mciseq drivers 
1484      * asks to prepare MIDIHDR which dwFlags != 0.
1485      * So at least check for the inqueue flag
1486      */
1487     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 || 
1488         lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 || 
1489         lpMidiHdr->dwBufferLength >= 0x10000ul) {
1490         WARN("%p %p %08lx %d/%ld\n", lpMidiHdr, lpMidiHdr->lpData, 
1491                    lpMidiHdr->dwFlags, sizeof(MIDIHDR), dwSize);
1492         return MMSYSERR_INVALPARAM;
1493     }
1494     
1495     lpMidiHdr->lpNext = 0;
1496     lpMidiHdr->dwFlags |= MHDR_PREPARED;
1497     lpMidiHdr->dwFlags &= ~MHDR_DONE;
1498     return MMSYSERR_NOERROR;
1499 }
1500
1501 /**************************************************************************
1502  *                              modUnprepare                    [internal]
1503  */
1504 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1505 {
1506     TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1507
1508     if (midiSeqFD == -1) {
1509         WARN("can't unprepare !\n");
1510         return MMSYSERR_NOTENABLED;
1511     }
1512
1513     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0)
1514         return MMSYSERR_INVALPARAM;
1515     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1516         return MIDIERR_STILLPLAYING;
1517     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1518     return MMSYSERR_NOERROR;
1519 }
1520
1521 /**************************************************************************
1522  *                      modReset                                [internal]
1523  */
1524 static DWORD modReset(WORD wDevID)
1525 {
1526     unsigned chn;
1527     
1528     TRACE("(%04X);\n", wDevID);
1529
1530     /* stop all notes */
1531     /* FIXME: check if 0x78B0 is channel dependant or not. I coded it so that 
1532      * it's channel dependent...
1533      */
1534     for (chn = 0; chn < 16; chn++) {
1535         /* turn off every note */
1536         modData(wDevID, 0x7800 | MIDI_CTL_CHANGE | chn);
1537         /* remove sustain on all channels */
1538         modData(wDevID, (CTL_SUSTAIN << 8) | MIDI_CTL_CHANGE | chn);
1539     }
1540     /* FIXME: the LongData buffers must also be returned to the app */
1541     return MMSYSERR_NOERROR;
1542 }
1543
1544 #endif /* HAVE_OSS_MIDI */
1545
1546 /*======================================================================*
1547  *                          MIDI entry points                           *
1548  *======================================================================*/
1549
1550 /**************************************************************************
1551  *                      midMessage (WINEOSS.4)
1552  */
1553 DWORD WINAPI OSS_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser, 
1554                             DWORD dwParam1, DWORD dwParam2)
1555 {
1556     TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", 
1557           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1558     switch (wMsg) {
1559 #ifdef HAVE_OSS_MIDI
1560     case DRVM_INIT:
1561     case DRVM_ENABLE:
1562     case DRVM_DISABLE:
1563         /* FIXME: Pretend this is supported */
1564         return 0;
1565     case MIDM_OPEN:
1566         return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1567     case MIDM_CLOSE:
1568         return midClose(wDevID);
1569     case MIDM_ADDBUFFER:
1570         return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1571     case MIDM_PREPARE:
1572         return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1573     case MIDM_UNPREPARE:
1574         return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1575     case MIDM_GETDEVCAPS:
1576         return midGetDevCaps(wDevID, (LPMIDIINCAPSA)dwParam1,dwParam2);
1577     case MIDM_GETNUMDEVS:
1578         return MIDM_NUMDEVS; 
1579     case MIDM_RESET:
1580         return midReset(wDevID);
1581     case MIDM_START:
1582         return midStart(wDevID);
1583     case MIDM_STOP:
1584         return midStop(wDevID);
1585 #endif
1586     default:
1587         TRACE("Unsupported message\n");
1588     }
1589     return MMSYSERR_NOTSUPPORTED;
1590 }
1591
1592 /**************************************************************************
1593  *                              modMessage (WINEOSS.5)
1594  */
1595 DWORD WINAPI OSS_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser, 
1596                             DWORD dwParam1, DWORD dwParam2)
1597 {
1598     TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", 
1599           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1600
1601     switch (wMsg) {
1602 #ifdef HAVE_OSS_MIDI
1603     case DRVM_INIT:
1604     case DRVM_EXIT:
1605     case DRVM_ENABLE:
1606     case DRVM_DISABLE:
1607         /* FIXME: Pretend this is supported */
1608         return 0;
1609     case MODM_OPEN:
1610         return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1611     case MODM_CLOSE:
1612         return modClose(wDevID);
1613     case MODM_DATA:
1614         return modData(wDevID, dwParam1);
1615     case MODM_LONGDATA:
1616         return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1617     case MODM_PREPARE:
1618         return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1619     case MODM_UNPREPARE:
1620         return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1621     case MODM_GETDEVCAPS:
1622         return modGetDevCaps(wDevID, (LPMIDIOUTCAPSA)dwParam1, dwParam2);
1623     case MODM_GETNUMDEVS:
1624         return MODM_NUMDEVS;
1625     case MODM_GETVOLUME:
1626         return 0;
1627     case MODM_SETVOLUME:
1628         return 0;
1629     case MODM_RESET:
1630         return modReset(wDevID);
1631 #endif
1632     default:
1633         TRACE("Unsupported message\n");
1634     }
1635     return MMSYSERR_NOTSUPPORTED;
1636 }
1637
1638 /*-----------------------------------------------------------------------*/