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