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