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