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