1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
4 * Sample MIDI Wine Driver for Linux
6 * Copyright 1994 Martin Ayotte
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
24 #include <sys/ioctl.h>
25 #include "wine/winuser16.h"
26 #include "multimedia.h"
39 LPMIDIOPENDESC midiDesc
;
41 LPMIDIHDR16 lpQueueHdr
;
44 unsigned char incoming
[3];
45 unsigned char incPrev
;
57 LPMIDIOPENDESC midiDesc
;
59 LPMIDIHDR16 lpQueueHdr
;
62 void* lpExtra
; /* according to port type (MIDI, FM...), extra data when needed */
66 static WINE_MIDIIN MidiInDev
[MAX_MIDIINDRV
];
67 static WINE_MIDIOUT MidiOutDev
[MAX_MIDIOUTDRV
];
69 /* this is the total number of MIDI out devices found */
71 /* this is the number of FM synthetizers (index from 0 to
72 NUMFMSYNTHDEVS - 1) */
73 int MODM_NUMFMSYNTHDEVS
= 0;
74 /* this is the number of Midi ports (index from NUMFMSYNTHDEVS to
75 NUMFMSYNTHDEVS + NUMMIDIDEVS - 1) */
76 int MODM_NUMMIDIDEVS
= 0;
78 /* this is the total number of MIDI out devices found */
82 static int midiSeqFD
= -1;
83 static int numOpenMidiSeq
= 0;
84 static UINT midiInTimerID
= 0;
85 static int numStartedMidiIn
= 0;
88 /* this structure holds pointers with information for each MIDI
91 LPMIDIOUTCAPS16 midiOutDevices
[MAX_MIDIOUTDRV
];
93 /* this structure holds pointers with information for each MIDI
96 LPMIDIINCAPS16 midiInDevices
[MAX_MIDIINDRV
];
99 * FIXME : all tests on device ID for midXXX and modYYY are made against
100 * MAX_MIDIxxDRV (when they are made) but should be done against the actual
101 * number of midi devices found...
102 * check also when HAVE_OSS is defined that midiDesc is not NULL
105 /*======================================================================*
106 * Low level MIDI implemantation *
107 *======================================================================*/
108 #ifdef SNDCTL_MIDI_INFO
109 /**************************************************************************
110 * MIDI_NotifyClient [internal]
112 static DWORD
MIDI_NotifyClient(UINT16 wDevID
, WORD wMsg
,
113 DWORD dwParam1
, DWORD dwParam2
)
120 TRACE(midi
,"wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
121 wDevID
, wMsg
, dwParam1
, dwParam2
);
127 if (wDevID
> MAX_MIDIOUTDRV
)
128 return MCIERR_INTERNAL
;
130 dwCallBack
= MidiOutDev
[wDevID
].midiDesc
->dwCallback
;
131 uFlags
= MidiOutDev
[wDevID
].wFlags
;
132 hDev
= MidiOutDev
[wDevID
].midiDesc
->hMidi
;
133 dwInstance
= MidiOutDev
[wDevID
].midiDesc
->dwInstance
;
140 if (wDevID
> MAX_MIDIINDRV
)
141 return MCIERR_INTERNAL
;
143 dwCallBack
= MidiInDev
[wDevID
].midiDesc
->dwCallback
;
144 uFlags
= MidiInDev
[wDevID
].wFlags
;
145 hDev
= MidiInDev
[wDevID
].midiDesc
->hMidi
;
146 dwInstance
= MidiInDev
[wDevID
].midiDesc
->dwInstance
;
149 WARN(midi
, "Unsupported MSW-MIDI message %u\n", wMsg
);
150 return MCIERR_INTERNAL
;
153 return DriverCallback16(dwCallBack
, uFlags
, hDev
, wMsg
, dwInstance
, dwParam1
, dwParam2
) ?
158 /**************************************************************************
159 * midiOpenSeq [internal]
161 static int midiOpenSeq(void)
163 if (numOpenMidiSeq
== 0) {
164 midiSeqFD
= open(MIDI_SEQ
, O_RDWR
, 0);
165 if (midiSeqFD
== -1) {
166 ERR(midi
, "can't open '%s' ! (%d)\n", MIDI_SEQ
, errno
);
169 if (fcntl(midiSeqFD
, F_SETFL
, O_NONBLOCK
) < 0) {
170 WARN(midi
, "can't set sequencer fd to non blocking (%d)\n", errno
);
175 ioctl(midiSeqFD
, SNDCTL_SEQ_RESET
);
181 /**************************************************************************
182 * midiCloseSeq [internal]
184 static int midiCloseSeq(void)
186 if (--numOpenMidiSeq
== 0) {
193 /* FIXME: this is a bad idea, it's even not static... */
196 /* FIXME: this is not reentrant, not static - because of global variable
198 /**************************************************************************
199 * seqbuf_dump [internal]
201 void seqbuf_dump(void)
204 if (write(midiSeqFD
, _seqbuf
, _seqbufptr
) == -1) {
205 WARN(midi
, "Can't write data to sequencer (%d/%d)!\n",
209 * in any case buffer is lost so that if many errors occur the buffer
215 #endif /* HAVE_OSS */
219 static void midReceiveChar(WORD wDevID
, unsigned char value
, DWORD dwTime
)
223 TRACE(midi
, "Adding %02xh to %d[%d]\n", value
, wDevID
, MidiInDev
[wDevID
].incLen
);
225 if (wDevID
>= MAX_MIDIINDRV
) {
226 WARN(midi
, "bad devID\n");
229 if (MidiInDev
[wDevID
].state
== 0) {
230 TRACE(midi
, "input not started, thrown away\n");
234 if (MidiInDev
[wDevID
].state
& 2) { /* system exclusive */
235 LPMIDIHDR16 lpMidiHdr
= MidiInDev
[wDevID
].lpQueueHdr
;
239 LPBYTE lpData
= ((DWORD
)lpMidiHdr
== lpMidiHdr
->reserved
) ?
240 (LPBYTE
)lpMidiHdr
->lpData
: (LPBYTE
)PTR_SEG_TO_LIN(lpMidiHdr
->lpData
);
242 lpData
[lpMidiHdr
->dwBytesRecorded
++] = value
;
243 if (lpMidiHdr
->dwBytesRecorded
== lpMidiHdr
->dwBufferLength
) {
247 if (value
== 0xF7) { /* then end */
248 MidiInDev
[wDevID
].state
&= ~2;
251 if (sbfb
&& lpMidiHdr
!= NULL
) {
252 lpMidiHdr
= MidiInDev
[wDevID
].lpQueueHdr
;
253 lpMidiHdr
->dwFlags
&= ~MHDR_INQUEUE
;
254 lpMidiHdr
->dwFlags
|= MHDR_DONE
;
255 MidiInDev
[wDevID
].lpQueueHdr
= (LPMIDIHDR16
)lpMidiHdr
->lpNext
;
256 if (MIDI_NotifyClient(wDevID
, MIM_LONGDATA
, (DWORD
)lpMidiHdr
->reserved
, dwTime
) != MMSYSERR_NOERROR
) {
257 WARN(midi
, "Couldn't notify client\n");
263 #define IS_CMD(_x) (((_x) & 0x80) == 0x80)
264 #define IS_SYS_CMD(_x) (((_x) & 0xF0) == 0xF0)
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
);
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
);
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];
285 #undef IS_SYS_CMD(_x)
287 switch (MidiInDev
[wDevID
].incoming
[0] & 0xF0) {
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);
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);
306 case MIDI_SYSTEM_PREFIX
:
307 if (MidiInDev
[wDevID
].incoming
[0] == 0xF0) {
308 MidiInDev
[wDevID
].state
|= 2;
309 MidiInDev
[wDevID
].incLen
= 0;
311 if (MidiInDev
[wDevID
].incLen
== 1) {
312 toSend
= (MidiInDev
[wDevID
].incoming
[0] << 0);
317 WARN(midi
, "This shouldn't happen (%02X)\n", MidiInDev
[wDevID
].incoming
[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");
329 static VOID WINAPI
midTimeCallback(HWND hwnd
, UINT msg
, UINT id
, DWORD dwTime
)
331 unsigned char buffer
[256];
334 TRACE(midi
, "(%04X, %d, %d, %lu)\n", hwnd
, msg
, id
, dwTime
);
336 len
= read(midiSeqFD
, buffer
, sizeof(buffer
));
338 if ((len
% 4) != 0) {
339 WARN(midi
, "bad length %d (%d)\n", len
, errno
);
343 for (idx
= 0; idx
< len
; ) {
344 if (buffer
[idx
] & 0x80) {
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]);
353 switch (buffer
[idx
+ 0]) {
358 midReceiveChar(buffer
[idx
+ 2], buffer
[idx
+ 1], dwTime
);
361 TRACE(midi
, "Unsupported event %d\n", buffer
[idx
+ 0]);
368 #endif /* HAVE_OSS */
370 /**************************************************************************
371 * midGetDevCaps [internal]
373 static DWORD
midGetDevCaps(WORD wDevID
, LPMIDIINCAPS16 lpCaps
, DWORD dwSize
)
375 LPMIDIINCAPS16 tmplpCaps
;
377 TRACE(midi
, "(%04X, %p, %08lX);\n", wDevID
, lpCaps
, dwSize
);
379 if (wDevID
>= MIDM_NUMDEVS
) {
380 return MMSYSERR_BADDEVICEID
;
382 if (lpCaps
== NULL
) {
383 return MMSYSERR_INVALPARAM
;
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
;
399 return MMSYSERR_NOERROR
;
402 /**************************************************************************
405 static DWORD
midOpen(WORD wDevID
, LPMIDIOPENDESC lpDesc
, DWORD dwFlags
)
407 TRACE(midi
, "(%04X, %p, %08lX);\n", wDevID
, lpDesc
, dwFlags
);
409 if (lpDesc
== NULL
) {
410 WARN(midi
, "Invalid Parameter !\n");
411 return MMSYSERR_INVALPARAM
;
414 * how to check that content of lpDesc is correct ?
416 if (wDevID
>= MAX_MIDIINDRV
) {
417 WARN(midi
,"wDevID too large (%u) !\n", wDevID
);
418 return MMSYSERR_BADDEVICEID
;
420 if (MidiInDev
[wDevID
].midiDesc
!= 0) {
421 WARN(midi
, "device already open !\n");
422 return MMSYSERR_ALLOCATED
;
424 if ((dwFlags
& ~CALLBACK_TYPEMASK
) != 0) {
425 FIXME(midi
, "No support for MIDI_IO_STATUS in dwFlags\n");
426 return MMSYSERR_INVALFLAG
;
430 if (midiOpenSeq() < 0) {
431 return MMSYSERR_ERROR
;
434 if (numStartedMidiIn
++ == 0) {
435 midiInTimerID
= SetTimer(0, 0, 250, midTimeCallback
);
436 if (!midiInTimerID
) {
437 numStartedMidiIn
= 0;
438 WARN(midi
, "Couldn't start timer for midi-in\n");
440 return MMSYSERR_ERROR
;
442 TRACE(midi
, "Starting timer (%u) for midi-in\n", midiInTimerID
);
446 int midi
= open(MIDI_DEV
, O_WRONLY
, 0);
448 MidiInDev
[wDevID
].unixdev
= 0;
450 WARN(midi
,"can't open '%s' (%d)!\n", MIDI_DEV
, errno
);
451 return MMSYSERR_ALLOCATED
;
453 MidiInDev
[wDevID
].unixdev
= midi
;
455 #endif /* HAVE_OSS */
457 MidiInDev
[wDevID
].wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
459 MidiInDev
[wDevID
].lpQueueHdr
= NULL
;
460 MidiInDev
[wDevID
].dwTotalPlayed
= 0;
461 MidiInDev
[wDevID
].bufsize
= 0x3FFF;
462 MidiInDev
[wDevID
].midiDesc
= lpDesc
;
463 MidiInDev
[wDevID
].state
= 0;
465 MidiInDev
[wDevID
].incLen
= 0;
466 MidiInDev
[wDevID
].startTime
= 0;
467 #endif /* HAVE_OSS */
468 if (MIDI_NotifyClient(wDevID
, MIM_OPEN
, 0L, 0L) != MMSYSERR_NOERROR
) {
469 WARN(midi
,"can't notify client !\n");
470 return MMSYSERR_INVALPARAM
;
472 return MMSYSERR_NOERROR
;
475 /**************************************************************************
476 * midClose [internal]
478 static DWORD
midClose(WORD wDevID
)
480 int ret
= MMSYSERR_NOERROR
;
482 TRACE(midi
, "(%04X);\n", wDevID
);
484 if (wDevID
>= MAX_MIDIINDRV
) {
485 WARN(midi
,"wDevID too bif (%u) !\n", wDevID
);
486 return MMSYSERR_BADDEVICEID
;
488 if (MidiInDev
[wDevID
].midiDesc
== 0) {
489 WARN(midi
, "device not opened !\n");
490 return MMSYSERR_ERROR
;
492 if (MidiInDev
[wDevID
].lpQueueHdr
!= 0) {
493 return MIDIERR_STILLPLAYING
;
497 if (midiSeqFD
== -1) {
498 WARN(midi
,"ooops !\n");
499 return MMSYSERR_ERROR
;
501 if (--numStartedMidiIn
== 0) {
502 TRACE(midi
, "Stopping timer for midi-in\n");
503 if (!KillTimer(0, midiInTimerID
)) {
504 WARN(midi
, "Couldn't stop timer for midi-in\n");
510 if (MidiInDev
[wDevID
].unixdev
== 0) {
511 WARN(midi
,"ooops !\n");
512 return MMSYSERR_ERROR
;
514 close(MidiInDev
[wDevID
].unixdev
);
515 MidiInDev
[wDevID
].unixdev
= 0;
516 #endif /* HAVE_OSS */
517 MidiInDev
[wDevID
].bufsize
= 0;
518 if (MIDI_NotifyClient(wDevID
, MIM_CLOSE
, 0L, 0L) != MMSYSERR_NOERROR
) {
519 WARN(midi
,"can't notify client !\n");
520 ret
= MMSYSERR_INVALPARAM
;
522 MidiInDev
[wDevID
].midiDesc
= 0;
526 /**************************************************************************
527 * midAddBuffer [internal]
529 static DWORD
midAddBuffer(WORD wDevID
, LPMIDIHDR16 lpMidiHdr
, DWORD dwSize
)
531 TRACE(midi
, "(%04X, %p, %08lX);\n", wDevID
, lpMidiHdr
, dwSize
);
533 if (lpMidiHdr
== NULL
) return MMSYSERR_INVALPARAM
;
534 if (sizeof(MIDIHDR16
) > dwSize
) return MMSYSERR_INVALPARAM
;
535 if (lpMidiHdr
->dwBufferLength
== 0) return MMSYSERR_INVALPARAM
;
536 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
) return MIDIERR_STILLPLAYING
;
537 if (!(lpMidiHdr
->dwFlags
& MHDR_PREPARED
)) return MIDIERR_UNPREPARED
;
539 if (MidiInDev
[wDevID
].lpQueueHdr
== 0) {
540 MidiInDev
[wDevID
].lpQueueHdr
= lpMidiHdr
;
544 for (ptr
= MidiInDev
[wDevID
].lpQueueHdr
;
546 ptr
= (LPMIDIHDR16
)ptr
->lpNext
);
547 ptr
->lpNext
= (struct midihdr_tag
*)lpMidiHdr
;
549 return MMSYSERR_NOERROR
;
552 /**************************************************************************
553 * midPrepare [internal]
555 static DWORD
midPrepare(WORD wDevID
, LPMIDIHDR16 lpMidiHdr
, DWORD dwSize
)
557 TRACE(midi
, "(%04X, %p, %08lX);\n", wDevID
, lpMidiHdr
, dwSize
);
559 if (dwSize
< sizeof(MIDIHDR16
) || lpMidiHdr
== 0 ||
560 lpMidiHdr
->lpData
== 0 || lpMidiHdr
->dwFlags
!= 0 ||
561 lpMidiHdr
->dwBufferLength
>= 0x10000ul
)
562 return MMSYSERR_INVALPARAM
;
564 lpMidiHdr
->lpNext
= 0;
565 lpMidiHdr
->dwFlags
|= MHDR_PREPARED
;
566 lpMidiHdr
->dwBytesRecorded
= 0;
568 return MMSYSERR_NOERROR
;
571 /**************************************************************************
572 * midUnprepare [internal]
574 static DWORD
midUnprepare(WORD wDevID
, LPMIDIHDR16 lpMidiHdr
, DWORD dwSize
)
576 TRACE(midi
, "(%04X, %p, %08lX);\n", wDevID
, lpMidiHdr
, dwSize
);
578 if (dwSize
< sizeof(MIDIHDR16
) || lpMidiHdr
== 0 ||
579 lpMidiHdr
->lpData
== 0 || lpMidiHdr
->dwBufferLength
>= 0x10000ul
)
580 return MMSYSERR_INVALPARAM
;
582 if (!(lpMidiHdr
->dwFlags
& MHDR_PREPARED
)) return MIDIERR_UNPREPARED
;
583 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
) return MIDIERR_STILLPLAYING
;
585 lpMidiHdr
->dwFlags
&= ~MHDR_PREPARED
;
587 return MMSYSERR_NOERROR
;
590 /**************************************************************************
591 * midReset [internal]
593 static DWORD
midReset(WORD wDevID
)
595 DWORD dwTime
= GetTickCount();
597 TRACE(midi
, "(%04X);\n", wDevID
);
599 while (MidiInDev
[wDevID
].lpQueueHdr
) {
600 MidiInDev
[wDevID
].lpQueueHdr
->dwFlags
&= ~MHDR_INQUEUE
;
601 MidiInDev
[wDevID
].lpQueueHdr
->dwFlags
|= MHDR_DONE
;
602 /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
603 if (MIDI_NotifyClient(wDevID
, MIM_LONGDATA
,
604 (DWORD
)MidiInDev
[wDevID
].lpQueueHdr
, dwTime
) != MMSYSERR_NOERROR
) {
605 WARN(midi
, "Couldn't notify client\n");
607 MidiInDev
[wDevID
].lpQueueHdr
= (LPMIDIHDR16
)MidiInDev
[wDevID
].lpQueueHdr
->lpNext
;
610 return MMSYSERR_NOERROR
;
614 /**************************************************************************
615 * midStart [internal]
617 static DWORD
midStart(WORD wDevID
)
619 TRACE(midi
, "(%04X);\n", wDevID
);
621 /* FIXME : should test value of wDevID */
624 MidiInDev
[wDevID
].state
= 1;
625 MidiInDev
[wDevID
].startTime
= GetTickCount();
626 return MMSYSERR_NOERROR
;
628 return MMSYSERR_NOTENABLED
;
629 #endif /* HAVE_OSS */
632 /**************************************************************************
635 static DWORD
midStop(WORD wDevID
)
637 TRACE(midi
, "(%04X);\n", wDevID
);
639 /* FIXME : should test value of wDevID */
642 MidiInDev
[wDevID
].state
= 0;
643 return MMSYSERR_NOERROR
;
645 return MMSYSERR_NOTENABLED
;
646 #endif /* HAVE_OSS */
649 /*-----------------------------------------------------------------------*/
653 typedef struct sVoice
{
654 int note
; /* 0 means not used */
656 unsigned cntMark
: 30,
659 #define sVS_PLAYING 1
660 #define sVS_SUSTAINED 2
663 typedef struct sChannel
{
669 int bank
; /* CTL_BANK_SELECT */
670 int volume
; /* CTL_MAIN_VOLUME */
671 int balance
; /* CTL_BALANCE */
672 int expression
; /* CTL_EXPRESSION */
673 int sustain
; /* CTL_SUSTAIN */
675 unsigned char nrgPmtMSB
; /* Non register Parameters */
676 unsigned char nrgPmtLSB
;
677 unsigned char regPmtMSB
; /* Non register Parameters */
678 unsigned char regPmtLSB
;
681 typedef struct sFMextra
{
684 sChannel channel
[16]; /* MIDI has only 16 channels */
685 sVoice voice
[1]; /* dyn allocated according to sound card */
686 /* do not append fields below voice[1] since the size of this structure
687 * depends on the number of available voices on the FM synth...
691 extern unsigned char midiFMInstrumentPatches
[16 * 128];
692 extern unsigned char midiFMDrumsPatches
[16 * 128];
694 /**************************************************************************
695 * modFMLoad [internal]
697 static int modFMLoad(int dev
)
700 struct sbi_instrument sbi
;
705 memset(sbi
.operators
+ 16, 0, 16);
706 for (i
= 0; i
< 128; i
++) {
708 memcpy(sbi
.operators
, midiFMInstrumentPatches
+ i
* 16, 16);
710 if (write(midiSeqFD
, (char*)&sbi
, sizeof(sbi
)) == -1) {
711 WARN(midi
, "Couldn't write patch for instrument %d (%d)!\n", sbi
.channel
, errno
);
715 for (i
= 0; i
< 128; i
++) {
716 sbi
.channel
= 128 + i
;
717 memcpy(sbi
.operators
, midiFMDrumsPatches
+ i
* 16, 16);
719 if (write(midiSeqFD
, (char*)&sbi
, sizeof(sbi
)) == -1) {
720 WARN(midi
, "Couldn't write patch for drum %d (%d)!\n", sbi
.channel
, errno
);
727 /**************************************************************************
728 * modFMReset [internal]
730 static void modFMReset(WORD wDevID
)
732 sFMextra
* extra
= (sFMextra
*)MidiOutDev
[wDevID
].lpExtra
;
733 sVoice
* voice
= extra
->voice
;
734 sChannel
* channel
= extra
->channel
;
737 for (i
= 0; i
< midiOutDevices
[wDevID
]->wVoices
; i
++) {
738 if (voice
[i
].status
!= sVS_UNUSED
) {
739 SEQ_STOP_NOTE(wDevID
, i
, voice
[i
].note
, 64);
741 SEQ_KEY_PRESSURE(wDevID
, i
, 127, 0);
742 SEQ_CONTROL(wDevID
, i
, SEQ_VOLMODE
, VOL_METHOD_LINEAR
);
744 voice
[i
].channel
= -1;
745 voice
[i
].cntMark
= 0;
746 voice
[i
].status
= sVS_UNUSED
;
748 for (i
= 0; i
< 16; i
++) {
749 channel
[i
].program
= 0;
750 channel
[i
].bender
= 8192;
751 channel
[i
].benderRange
= 2;
753 channel
[i
].volume
= 127;
754 channel
[i
].balance
= 64;
755 channel
[i
].expression
= 0;
756 channel
[i
].sustain
= 0;
759 extra
->drumSetMask
= 1 << 9; /* channel 10 is normally drums, sometimes 16 also */
763 #define IS_DRUM_CHANNEL(_xtra, _chn) ((_xtra)->drumSetMask & (1 << (_chn)))
765 #endif /* HAVE_OSS */
767 /**************************************************************************
768 * modGetDevCaps [internal]
770 static DWORD
modGetDevCaps(WORD wDevID
, LPMIDIOUTCAPS16 lpCaps
,
773 TRACE(midi
, "(%04X, %p, %08lX);\n", wDevID
, lpCaps
, dwSize
);
774 if (wDevID
== (WORD
) MIDI_MAPPER
) {
775 lpCaps
->wMid
= 0x00FF; /* Manufac ID */
776 lpCaps
->wPid
= 0x0001; /* Product ID */
777 lpCaps
->vDriverVersion
= 0x001; /* Product Version */
778 strcpy(lpCaps
->szPname
, "MIDI Mapper (not functional yet)");
779 /* FIXME Does it make any difference ? */
780 lpCaps
->wTechnology
= MOD_FMSYNTH
;
781 lpCaps
->wVoices
= 14; /* FIXME */
782 lpCaps
->wNotes
= 14; /* FIXME */
783 /* FIXME Does it make any difference ? */
784 lpCaps
->dwSupport
= MIDICAPS_VOLUME
|MIDICAPS_LRVOLUME
;
786 LPMIDIOUTCAPS16 tmplpCaps
;
788 if (wDevID
>= MODM_NUMDEVS
) {
789 TRACE(midi
, "MAX_MIDIOUTDRV reached !\n");
790 return MMSYSERR_BADDEVICEID
;
792 /* FIXME There is a way to do it so easily, but I'm too
793 * sleepy to think and I want to test
796 tmplpCaps
= midiOutDevices
[wDevID
];
797 lpCaps
->wMid
= tmplpCaps
->wMid
;
798 lpCaps
->wPid
= tmplpCaps
->wPid
;
799 lpCaps
->vDriverVersion
= tmplpCaps
->vDriverVersion
;
800 strcpy(lpCaps
->szPname
, tmplpCaps
->szPname
);
801 lpCaps
->wTechnology
= tmplpCaps
->wTechnology
;
802 lpCaps
->wVoices
= tmplpCaps
->wVoices
;
803 lpCaps
->wNotes
= tmplpCaps
->wNotes
;
804 lpCaps
->dwSupport
= tmplpCaps
->dwSupport
;
806 return MMSYSERR_NOERROR
;
809 /**************************************************************************
812 static DWORD
modOpen(WORD wDevID
, LPMIDIOPENDESC lpDesc
, DWORD dwFlags
)
814 TRACE(midi
, "(%04X, %p, %08lX);\n", wDevID
, lpDesc
, dwFlags
);
815 if (lpDesc
== NULL
) {
816 WARN(midi
, "Invalid Parameter !\n");
817 return MMSYSERR_INVALPARAM
;
819 if (wDevID
>= MAX_MIDIOUTDRV
) {
820 TRACE(midi
,"MAX_MIDIOUTDRV reached !\n");
821 return MMSYSERR_BADDEVICEID
;
823 if (MidiOutDev
[wDevID
].midiDesc
!= 0) {
824 WARN(midi
, "device already open !\n");
825 return MMSYSERR_ALLOCATED
;
827 if ((dwFlags
& ~CALLBACK_TYPEMASK
) != 0) {
828 WARN(midi
, "bad dwFlags\n");
829 return MMSYSERR_INVALFLAG
;
831 if (midiOutDevices
[wDevID
] == NULL
) {
832 TRACE(midi
, "un-allocated wDevID\n");
833 return MMSYSERR_BADDEVICEID
;
837 MidiOutDev
[wDevID
].lpExtra
= 0;
839 switch (midiOutDevices
[wDevID
]->wTechnology
) {
842 void* extra
= xmalloc(sizeof(struct sFMextra
) +
843 sizeof(struct sVoice
) *
844 (midiOutDevices
[wDevID
]->wVoices
- 1));
847 WARN(midi
, "can't alloc extra data !\n");
848 return MMSYSERR_NOMEM
;
850 MidiOutDev
[wDevID
].lpExtra
= extra
;
851 if (midiOpenSeq() < 0) {
852 MidiOutDev
[wDevID
].lpExtra
= 0;
854 return MMSYSERR_ERROR
;
856 if (modFMLoad(wDevID
) < 0) {
858 MidiOutDev
[wDevID
].lpExtra
= 0;
860 return MMSYSERR_ERROR
;
866 if (midiOpenSeq() < 0) {
867 return MMSYSERR_ALLOCATED
;
871 WARN(midi
,"Technology not supported (yet) %d !\n",
872 midiOutDevices
[wDevID
]->wTechnology
);
873 return MMSYSERR_NOTENABLED
;
877 int midi
= open (MIDI_DEV
, O_WRONLY
, 0);
878 MidiOutDev
[wDevID
].unixdev
= 0;
880 WARN(midi
, "can't open !\n");
881 return MMSYSERR_ALLOCATED
;
883 MidiOutDev
[wDevID
].unixdev
= midi
;
885 #endif /* HAVE_OSS */
887 MidiOutDev
[wDevID
].wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
889 MidiOutDev
[wDevID
].lpQueueHdr
= NULL
;
890 MidiOutDev
[wDevID
].dwTotalPlayed
= 0;
891 MidiOutDev
[wDevID
].bufsize
= 0x3FFF;
892 MidiOutDev
[wDevID
].midiDesc
= lpDesc
;
894 if (MIDI_NotifyClient(wDevID
, MOM_OPEN
, 0L, 0L) != MMSYSERR_NOERROR
) {
895 WARN(midi
,"can't notify client !\n");
896 return MMSYSERR_INVALPARAM
;
898 TRACE(midi
, "Succesful !\n");
899 return MMSYSERR_NOERROR
;
903 /**************************************************************************
904 * modClose [internal]
906 static DWORD
modClose(WORD wDevID
)
908 int ret
= MMSYSERR_NOERROR
;
910 TRACE(midi
, "(%04X);\n", wDevID
);
912 if (MidiOutDev
[wDevID
].midiDesc
== 0) {
913 WARN(midi
, "device not opened !\n");
914 return MMSYSERR_ERROR
;
916 /* FIXME: should test that no pending buffer is still in the queue for
920 if (midiSeqFD
== -1) {
921 WARN(midi
,"can't close !\n");
922 return MMSYSERR_ERROR
;
925 switch (midiOutDevices
[wDevID
]->wTechnology
) {
931 WARN(midi
,"Technology not supported (yet) %d !\n",
932 midiOutDevices
[wDevID
]->wTechnology
);
933 return MMSYSERR_NOTENABLED
;
936 if (MidiOutDev
[wDevID
].lpExtra
!= 0) {
937 free(MidiOutDev
[wDevID
].lpExtra
);
938 MidiOutDev
[wDevID
].lpExtra
= 0;
941 if (MidiOutDev
[wDevID
].unixdev
== 0) {
942 WARN(midi
,"can't close !\n");
943 return MMSYSERR_NOTENABLED
;
945 close(MidiOutDev
[wDevID
].unixdev
);
946 MidiOutDev
[wDevID
].unixdev
= 0;
947 #endif /* HAVE_OSS */
949 MidiOutDev
[wDevID
].bufsize
= 0;
950 if (MIDI_NotifyClient(wDevID
, MOM_CLOSE
, 0L, 0L) != MMSYSERR_NOERROR
) {
951 WARN(midi
,"can't notify client !\n");
952 ret
= MMSYSERR_INVALPARAM
;
954 MidiOutDev
[wDevID
].midiDesc
= 0;
958 /**************************************************************************
961 static DWORD
modData(WORD wDevID
, DWORD dwParam
)
963 WORD evt
= LOBYTE(LOWORD(dwParam
));
964 WORD d1
= HIBYTE(LOWORD(dwParam
));
965 WORD d2
= LOBYTE(HIWORD(dwParam
));
967 TRACE(midi
, "(%04X, %08lX);\n", wDevID
, dwParam
);
970 if (midiSeqFD
== -1) {
971 WARN(midi
,"can't play !\n");
972 return MIDIERR_NODEVICE
;
974 switch (midiOutDevices
[wDevID
]->wTechnology
) {
977 * - chorus depth controller is not used
980 sFMextra
* extra
= (sFMextra
*)MidiOutDev
[wDevID
].lpExtra
;
981 sVoice
* voice
= extra
->voice
;
982 sChannel
* channel
= extra
->channel
;
983 int chn
= (evt
& 0x0F);
986 switch (evt
& 0xF0) {
988 for (i
= 0; i
< midiOutDevices
[wDevID
]->wVoices
; i
++) {
989 /* don't stop sustained notes */
990 if (voice
[i
].status
== sVS_PLAYING
&& voice
[i
].channel
== chn
&& voice
[i
].note
== d1
) {
991 voice
[i
].status
= sVS_UNUSED
;
992 SEQ_STOP_NOTE(wDevID
, i
, d1
, d2
);
997 if (d2
== 0) { /* note off if velocity == 0 */
998 for (i
= 0; i
< midiOutDevices
[wDevID
]->wVoices
; i
++) {
999 /* don't stop sustained notes */
1000 if (voice
[i
].status
== sVS_PLAYING
&& voice
[i
].channel
== chn
&& voice
[i
].note
== d1
) {
1001 voice
[i
].status
= sVS_UNUSED
;
1002 SEQ_STOP_NOTE(wDevID
, i
, d1
, 64);
1007 /* finding out in this order :
1009 * - if replaying the same note on the same channel
1010 * - the older voice (LRU)
1012 for (i
= nv
= 0; i
< midiOutDevices
[wDevID
]->wVoices
; i
++) {
1013 if (voice
[i
].status
== sVS_UNUSED
||
1014 (voice
[i
].note
== d1
&& voice
[i
].channel
== chn
)) {
1018 if (voice
[i
].cntMark
< voice
[0].cntMark
) {
1023 "playing on voice=%d, pgm=%d, pan=0x%02X, vol=0x%02X, "
1024 "bender=0x%02X, note=0x%02X, vel=0x%02X\n",
1025 nv
, channel
[chn
].program
,
1026 channel
[chn
].balance
,
1027 channel
[chn
].volume
,
1028 channel
[chn
].bender
, d1
, d2
);
1030 SEQ_SET_PATCH(wDevID
, nv
, IS_DRUM_CHANNEL(extra
, chn
) ?
1031 (128 + d1
) : channel
[chn
].program
);
1032 SEQ_BENDER_RANGE(wDevID
, nv
, channel
[chn
].benderRange
* 100);
1033 SEQ_BENDER(wDevID
, nv
, channel
[chn
].bender
);
1034 SEQ_CONTROL(wDevID
, nv
, CTL_PAN
, channel
[chn
].balance
);
1035 SEQ_CONTROL(wDevID
, nv
, CTL_EXPRESSION
, channel
[chn
].expression
);
1037 /* FIXME: does not really seem to work on my SB card and
1038 * screws everything up... so lay it down
1040 SEQ_CONTROL(wDevID
, nv
, CTL_MAIN_VOLUME
, channel
[chn
].volume
);
1042 SEQ_START_NOTE(wDevID
, nv
, d1
, d2
);
1043 voice
[nv
].status
= channel
[chn
].sustain
? sVS_SUSTAINED
: sVS_PLAYING
;
1044 voice
[nv
].note
= d1
;
1045 voice
[nv
].channel
= chn
;
1046 voice
[nv
].cntMark
= extra
->counter
++;
1048 case MIDI_KEY_PRESSURE
:
1049 for (i
= 0; i
< midiOutDevices
[wDevID
]->wVoices
; i
++) {
1050 if (voice
[i
].status
!= sVS_UNUSED
&& voice
[i
].channel
== chn
&& voice
[i
].note
== d1
) {
1051 SEQ_KEY_PRESSURE(wDevID
, i
, d1
, d2
);
1055 case MIDI_CTL_CHANGE
:
1057 case CTL_BANK_SELECT
: channel
[chn
].bank
= d2
; break;
1058 case CTL_MAIN_VOLUME
: channel
[chn
].volume
= d2
; break;
1059 case CTL_PAN
: channel
[chn
].balance
= d2
; break;
1060 case CTL_EXPRESSION
: channel
[chn
].expression
= d2
; break;
1061 case CTL_SUSTAIN
: channel
[chn
].sustain
= d2
;
1063 for (i
= 0; i
< midiOutDevices
[wDevID
]->wVoices
; i
++) {
1064 if (voice
[i
].status
== sVS_PLAYING
&& voice
[i
].channel
== chn
) {
1065 voice
[i
].status
= sVS_SUSTAINED
;
1069 for (i
= 0; i
< midiOutDevices
[wDevID
]->wVoices
; i
++) {
1070 if (voice
[i
].status
== sVS_SUSTAINED
&& voice
[i
].channel
== chn
) {
1071 voice
[i
].status
= sVS_UNUSED
;
1072 SEQ_STOP_NOTE(wDevID
, i
, voice
[i
].note
, 64);
1077 case CTL_NONREG_PARM_NUM_LSB
: channel
[chn
].nrgPmtLSB
= d2
; break;
1078 case CTL_NONREG_PARM_NUM_MSB
: channel
[chn
].nrgPmtMSB
= d2
; break;
1079 case CTL_REGIST_PARM_NUM_LSB
: channel
[chn
].regPmtLSB
= d2
; break;
1080 case CTL_REGIST_PARM_NUM_MSB
: channel
[chn
].regPmtMSB
= d2
; break;
1082 case CTL_DATA_ENTRY
:
1083 switch ((channel
[chn
].regPmtMSB
<< 8) | channel
[chn
].regPmtLSB
) {
1085 if (channel
[chn
].benderRange
!= d2
) {
1086 channel
[chn
].benderRange
= d2
;
1087 for (i
= 0; i
< midiOutDevices
[wDevID
]->wVoices
; i
++) {
1088 if (voice
[i
].channel
== chn
) {
1089 SEQ_BENDER_RANGE(wDevID
, i
, channel
[chn
].benderRange
);
1096 channel
[chn
].benderRange
= 2;
1097 for (i
= 0; i
< midiOutDevices
[wDevID
]->wVoices
; i
++) {
1098 if (voice
[i
].channel
== chn
) {
1099 SEQ_BENDER_RANGE(wDevID
, i
, channel
[chn
].benderRange
);
1104 TRACE(midi
, "Data entry: regPmt=0x%02x%02x, nrgPmt=0x%02x%02x with %x\n",
1105 channel
[chn
].regPmtMSB
, channel
[chn
].regPmtLSB
,
1106 channel
[chn
].nrgPmtMSB
, channel
[chn
].nrgPmtLSB
,
1112 case 0x78: /* all sounds off */
1113 /* FIXME: I don't know if I have to take care of the channel
1114 * for this control ?
1116 for (i
= 0; i
< midiOutDevices
[wDevID
]->wVoices
; i
++) {
1117 if (voice
[i
].status
!= sVS_UNUSED
&& voice
[i
].channel
== chn
) {
1118 voice
[i
].status
= sVS_UNUSED
;
1119 SEQ_STOP_NOTE(wDevID
, i
, voice
[i
].note
, 0);
1123 case 0x7B: /* all notes off */
1124 /* FIXME: I don't know if I have to take care of the channel
1125 * for this control ?
1127 for (i
= 0; i
< midiOutDevices
[wDevID
]->wVoices
; i
++) {
1128 if (voice
[i
].status
== sVS_PLAYING
&& voice
[i
].channel
== chn
) {
1129 voice
[i
].status
= sVS_UNUSED
;
1130 SEQ_STOP_NOTE(wDevID
, i
, voice
[i
].note
, 0);
1135 TRACE(midi
, "Dropping MIDI control event 0x%02x(%02x) on channel %d\n",
1140 case MIDI_PGM_CHANGE
:
1141 channel
[chn
].program
= d1
;
1143 case MIDI_CHN_PRESSURE
:
1144 for (i
= 0; i
< midiOutDevices
[wDevID
]->wVoices
; i
++) {
1145 if (voice
[i
].status
!= sVS_UNUSED
&& voice
[i
].channel
== chn
) {
1146 SEQ_KEY_PRESSURE(wDevID
, i
, voice
[i
].note
, d1
);
1150 case MIDI_PITCH_BEND
:
1151 channel
[chn
].bender
= (d2
<< 7) + d1
;
1152 for (i
= 0; i
< midiOutDevices
[wDevID
]->wVoices
; i
++) {
1153 if (voice
[i
].channel
== chn
) {
1154 SEQ_BENDER(wDevID
, i
, channel
[chn
].bender
);
1158 case MIDI_SYSTEM_PREFIX
:
1159 switch (evt
& 0x0F) {
1160 case 0x0F: /* Reset */
1164 WARN(midi
, "Unsupported (yet) system event %02x\n", evt
& 0x0F);
1168 WARN(midi
, "Internal error, shouldn't happen (event=%08x)\n", evt
& 0xF0);
1169 return MMSYSERR_NOTENABLED
;
1175 int dev
= wDevID
- MODM_NUMFMSYNTHDEVS
;
1177 WARN(midi
, "Internal error on devID (%u) !\n", wDevID
);
1178 return MIDIERR_NODEVICE
;
1181 switch (evt
& 0xF0) {
1184 case MIDI_KEY_PRESSURE
:
1185 case MIDI_CTL_CHANGE
:
1186 case MIDI_PITCH_BEND
:
1187 SEQ_MIDIOUT(dev
, evt
);
1188 SEQ_MIDIOUT(dev
, d1
);
1189 SEQ_MIDIOUT(dev
, d2
);
1191 case MIDI_PGM_CHANGE
:
1192 case MIDI_CHN_PRESSURE
:
1193 SEQ_MIDIOUT(dev
, evt
);
1194 SEQ_MIDIOUT(dev
, d1
);
1196 case MIDI_SYSTEM_PREFIX
:
1197 switch (evt
& 0x0F) {
1198 case 0x00: /* System Exclusive, don't do it on modData,
1199 * should require modLongData*/
1200 case 0x01: /* Undefined */
1201 case 0x04: /* Undefined. */
1202 case 0x05: /* Undefined. */
1203 case 0x07: /* End of Exclusive. */
1204 case 0x09: /* Undefined. */
1205 case 0x0D: /* Undefined. */
1207 case 0x06: /* Tune Request */
1208 case 0x08: /* Timing Clock. */
1209 case 0x0A: /* Start. */
1210 case 0x0B: /* Continue */
1211 case 0x0C: /* Stop */
1212 case 0x0E: /* Active Sensing. */
1213 SEQ_MIDIOUT(dev
, evt
);
1215 case 0x0F: /* Reset */
1216 /* SEQ_MIDIOUT(dev, evt);
1217 this other way may be better */
1218 SEQ_MIDIOUT(dev
, MIDI_SYSTEM_PREFIX
);
1219 SEQ_MIDIOUT(dev
, 0x7e);
1220 SEQ_MIDIOUT(dev
, 0x7f);
1221 SEQ_MIDIOUT(dev
, 0x09);
1222 SEQ_MIDIOUT(dev
, 0x01);
1223 SEQ_MIDIOUT(dev
, 0xf7);
1225 case 0x03: /* Song Select. */
1226 SEQ_MIDIOUT(dev
, evt
);
1227 SEQ_MIDIOUT(dev
, d1
);
1228 case 0x02: /* Song Position Pointer. */
1229 SEQ_MIDIOUT(dev
, evt
);
1230 SEQ_MIDIOUT(dev
, d1
);
1231 SEQ_MIDIOUT(dev
, d2
);
1238 WARN(midi
, "Technology not supported (yet) %d !\n",
1239 midiOutDevices
[wDevID
]->wTechnology
);
1240 return MMSYSERR_NOTENABLED
;
1245 if (MidiOutDev
[wDevID
].unixdev
== 0) {
1246 WARN(midi
,"can't play !\n");
1247 return MIDIERR_NODEVICE
;
1250 WORD event
= LOWORD(dwParam
);
1251 if (write (MidiOutDev
[wDevID
].unixdev
,
1252 &event
, sizeof(event
)) != sizeof(WORD
)) {
1253 WARN(midi
, "error writting unixdev !\n");
1256 #endif /* HAVE_OSS */
1257 return MMSYSERR_NOERROR
;
1260 /**************************************************************************
1261 * modLongData [internal]
1263 static DWORD
modLongData(WORD wDevID
, LPMIDIHDR16 lpMidiHdr
, DWORD dwSize
)
1268 TRACE(midi
, "(%04X, %p, %08lX);\n", wDevID
, lpMidiHdr
, dwSize
);
1271 if (midiSeqFD
== -1) {
1272 WARN(midi
,"can't play !\n");
1273 return MIDIERR_NODEVICE
;
1275 #else /* HAVE_OSS */
1276 if (MidiOutDev
[wDevID
].unixdev
== 0) {
1277 WARN(midi
,"can't play !\n");
1278 return MIDIERR_NODEVICE
;
1280 #endif /* HAVE_OSS */
1282 lpData
= ((DWORD
)lpMidiHdr
== lpMidiHdr
->reserved
) ?
1283 (LPBYTE
)lpMidiHdr
->lpData
: (LPBYTE
)PTR_SEG_TO_LIN(lpMidiHdr
->lpData
);
1286 return MIDIERR_UNPREPARED
;
1287 if (!(lpMidiHdr
->dwFlags
& MHDR_PREPARED
))
1288 return MIDIERR_UNPREPARED
;
1289 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
)
1290 return MIDIERR_STILLPLAYING
;
1291 lpMidiHdr
->dwFlags
&= ~MHDR_DONE
;
1292 lpMidiHdr
->dwFlags
|= MHDR_INQUEUE
;
1294 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
1295 * data, or can it also contain raw MIDI data, to be split up and sent to
1297 * If the latest is true, then the following WARNing will fire up
1299 if (lpData
[0] != 0xF0 || lpData
[lpMidiHdr
->dwBytesRecorded
- 1] != 0xF7) {
1300 WARN(midi
, "Alledged system exclusive buffer is not correct\nPlease report with MIDI file\n");
1303 TRACE(midi
, "dwBytesRecorded %lu !\n", lpMidiHdr
->dwBytesRecorded
);
1304 TRACE(midi
, " %02X %02X %02X %02X\n",
1305 lpData
[0], lpData
[1], lpData
[2], lpData
[3]);
1308 switch (midiOutDevices
[wDevID
]->wTechnology
) {
1310 /* FIXME: I don't think there is much to do here */
1313 if (lpData
[0] != 0xF0) {
1314 /* Send end of System Exclusive */
1315 SEQ_MIDIOUT(wDevID
- MODM_NUMFMSYNTHDEVS
, 0xF0);
1316 WARN(midi
, "Adding missing 0xF0 marker at the begining of "
1317 "system exclusive byte stream\n");
1319 for (count
= 0; count
< lpMidiHdr
->dwBytesRecorded
; count
++) {
1320 SEQ_MIDIOUT(wDevID
- MODM_NUMFMSYNTHDEVS
, lpData
[count
]);
1322 if (lpData
[count
- 1] != 0xF7) {
1323 /* Send end of System Exclusive */
1324 SEQ_MIDIOUT(wDevID
- MODM_NUMFMSYNTHDEVS
, 0xF7);
1325 WARN(midi
, "Adding missing 0xF7 marker at the end of "
1326 "system exclusive byte stream\n");
1331 WARN(midi
, "Technology not supported (yet) %d !\n",
1332 midiOutDevices
[wDevID
]->wTechnology
);
1333 return MMSYSERR_NOTENABLED
;
1335 #else /* HAVE_OSS */
1339 for (count
= 0; count
< lpMidiHdr
->dwBytesRecorded
; count
++) {
1340 if (write(MidiOutDev
[wDevID
].unixdev
,
1341 lpData
, sizeof(WORD
)) != sizeof(WORD
))
1347 TRACE(midi
, "after write count = %d\n",count
);
1348 if (count
!= lpMidiHdr
->dwBytesRecorded
) {
1349 WARN(midi
, "error writting unixdev #%d ! (%d != %ld)\n",
1350 MidiOutDev
[wDevID
].unixdev
, count
,
1351 lpMidiHdr
->dwBytesRecorded
);
1352 TRACE(midi
, "\terrno = %d error = %s\n",en
,strerror(en
));
1353 return MMSYSERR_NOTENABLED
;
1356 #endif /* HAVE_OSS */
1358 lpMidiHdr
->dwFlags
&= ~MHDR_INQUEUE
;
1359 lpMidiHdr
->dwFlags
|= MHDR_DONE
;
1360 if (MIDI_NotifyClient(wDevID
, MOM_DONE
, lpMidiHdr
->reserved
, 0L) != MMSYSERR_NOERROR
) {
1361 WARN(midi
,"can't notify client !\n");
1362 return MMSYSERR_INVALPARAM
;
1364 return MMSYSERR_NOERROR
;
1367 /**************************************************************************
1368 * modPrepare [internal]
1370 static DWORD
modPrepare(WORD wDevID
, LPMIDIHDR16 lpMidiHdr
, DWORD dwSize
)
1372 TRACE(midi
, "(%04X, %p, %08lX);\n", wDevID
, lpMidiHdr
, dwSize
);
1375 if (midiSeqFD
== -1) {
1376 WARN(midi
,"can't prepare !\n");
1377 return MMSYSERR_NOTENABLED
;
1379 #else /* HAVE_OSS */
1380 if (MidiOutDev
[wDevID
].unixdev
== 0) {
1381 WARN(midi
,"can't prepare !\n");
1382 return MMSYSERR_NOTENABLED
;
1384 #endif /* HAVE_OSS */
1385 if (dwSize
< sizeof(MIDIHDR16
) || lpMidiHdr
== 0 ||
1386 lpMidiHdr
->lpData
== 0 || lpMidiHdr
->dwFlags
!= 0 ||
1387 lpMidiHdr
->dwBufferLength
>= 0x10000ul
) {
1388 WARN(midi
, "%p %p %08lx %d/%ld\n", lpMidiHdr
, lpMidiHdr
->lpData
,
1389 lpMidiHdr
->dwFlags
, sizeof(MIDIHDR16
), dwSize
);
1390 return MMSYSERR_INVALPARAM
;
1393 lpMidiHdr
->lpNext
= 0;
1394 lpMidiHdr
->dwFlags
|= MHDR_PREPARED
;
1395 lpMidiHdr
->dwFlags
&= ~MHDR_DONE
;
1396 return MMSYSERR_NOERROR
;
1399 /**************************************************************************
1400 * modUnprepare [internal]
1402 static DWORD
modUnprepare(WORD wDevID
, LPMIDIHDR16 lpMidiHdr
, DWORD dwSize
)
1404 TRACE(midi
, "(%04X, %p, %08lX);\n", wDevID
, lpMidiHdr
, dwSize
);
1407 if (midiSeqFD
== -1) {
1408 WARN(midi
,"can't unprepare !\n");
1409 return MMSYSERR_NOTENABLED
;
1411 #else /* HAVE_OSS */
1412 if (MidiOutDev
[wDevID
].unixdev
== 0) {
1413 WARN(midi
,"can't unprepare !\n");
1414 return MMSYSERR_NOTENABLED
;
1416 #endif /* HAVE_OSS */
1417 if (dwSize
< sizeof(MIDIHDR16
) || lpMidiHdr
== 0)
1418 return MMSYSERR_INVALPARAM
;
1419 if (lpMidiHdr
->dwFlags
& MHDR_INQUEUE
)
1420 return MIDIERR_STILLPLAYING
;
1421 lpMidiHdr
->dwFlags
&= ~MHDR_PREPARED
;
1422 return MMSYSERR_NOERROR
;
1425 /**************************************************************************
1426 * modReset [internal]
1428 static DWORD
modReset(WORD wDevID
)
1430 TRACE(midi
, "(%04X);\n", wDevID
);
1431 /* FIXME: this function should :
1432 * turn off every note, remove sustain on all channels
1433 * remove any pending buffers
1435 return MMSYSERR_NOTENABLED
;
1439 /*======================================================================*
1440 * MIDI entry points *
1441 *======================================================================*/
1443 /**************************************************************************
1444 * midMessage [sample driver]
1446 DWORD WINAPI
midMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
1447 DWORD dwParam1
, DWORD dwParam2
)
1449 TRACE(midi
, "(%04X, %04X, %08lX, %08lX, %08lX);\n",
1450 wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1452 #ifdef SNDCTL_MIDI_INFO
1454 return midOpen(wDevID
,(LPMIDIOPENDESC
)dwParam1
, dwParam2
);
1456 return midClose(wDevID
);
1457 case MIDM_ADDBUFFER
:
1458 return midAddBuffer(wDevID
,(LPMIDIHDR16
)dwParam1
, dwParam2
);
1460 return midPrepare(wDevID
,(LPMIDIHDR16
)dwParam1
, dwParam2
);
1461 case MIDM_UNPREPARE
:
1462 return midUnprepare(wDevID
,(LPMIDIHDR16
)dwParam1
, dwParam2
);
1463 case MIDM_GETDEVCAPS
:
1464 return midGetDevCaps(wDevID
,(LPMIDIINCAPS16
)dwParam1
,dwParam2
);
1465 case MIDM_GETNUMDEVS
:
1466 return MIDM_NUMDEVS
;
1468 return midReset(wDevID
);
1470 return midStart(wDevID
);
1472 return midStop(wDevID
);
1475 TRACE(midi
, "Unsupported message\n");
1477 return MMSYSERR_NOTSUPPORTED
;
1480 /**************************************************************************
1481 * modMessage [sample driver]
1483 DWORD WINAPI
modMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
1484 DWORD dwParam1
, DWORD dwParam2
)
1486 TRACE(midi
, "(%04X, %04X, %08lX, %08lX, %08lX);\n",
1487 wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1490 #ifdef SNDCTL_MIDI_INFO
1492 return modOpen(wDevID
, (LPMIDIOPENDESC
)dwParam1
, dwParam2
);
1494 return modClose(wDevID
);
1496 return modData(wDevID
, dwParam1
);
1498 return modLongData(wDevID
, (LPMIDIHDR16
)dwParam1
, dwParam2
);
1500 return modPrepare(wDevID
, (LPMIDIHDR16
)dwParam1
, dwParam2
);
1501 case MODM_UNPREPARE
:
1502 return modUnprepare(wDevID
, (LPMIDIHDR16
)dwParam1
, dwParam2
);
1503 case MODM_GETDEVCAPS
:
1504 return modGetDevCaps(wDevID
, (LPMIDIOUTCAPS16
)dwParam1
, dwParam2
);
1505 case MODM_GETNUMDEVS
:
1506 return MODM_NUMDEVS
;
1507 case MODM_GETVOLUME
:
1509 case MODM_SETVOLUME
:
1512 return modReset(wDevID
);
1515 TRACE(midi
, "Unsupported message\n");
1517 return MMSYSERR_NOTSUPPORTED
;
1520 /**************************************************************************
1521 * MIDI_DriverProc32 [sample driver]
1523 LONG
MIDI_DriverProc(DWORD dwDevID
, HDRVR16 hDriv
, DWORD wMsg
,
1524 DWORD dwParam1
, DWORD dwParam2
)
1527 case DRV_LOAD
: return 1;
1528 case DRV_FREE
: return 1;
1529 case DRV_OPEN
: return 1;
1530 case DRV_CLOSE
: return 1;
1531 case DRV_ENABLE
: return 1;
1532 case DRV_DISABLE
: return 1;
1533 case DRV_QUERYCONFIGURE
: return 1;
1534 case DRV_CONFIGURE
: MessageBoxA(0, "Sample Midi Linux Driver !", "MMLinux Driver", MB_OK
); return 1;
1535 case DRV_INSTALL
: return DRVCNF_RESTART
;
1536 case DRV_REMOVE
: return DRVCNF_RESTART
;
1538 TRACE(midi
, "Sending msg=%lu to default driver proc\n", wMsg
);
1539 return DefDriverProc16(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1543 /**************************************************************************
1544 * MIDI_DriverProc16 [sample driver]
1546 LONG
MIDI_DriverProc16(DWORD dwDevID
, HDRVR16 hDriv
, WORD wMsg
,
1547 DWORD dwParam1
, DWORD dwParam2
)
1550 case DRV_LOAD
: return 1;
1551 case DRV_FREE
: return 1;
1552 case DRV_OPEN
: return 1;
1553 case DRV_CLOSE
: return 1;
1554 case DRV_ENABLE
: return 1;
1555 case DRV_DISABLE
: return 1;
1556 case DRV_QUERYCONFIGURE
: return 1;
1557 case DRV_CONFIGURE
: MessageBoxA(0, "Sample Midi Linux Driver !", "MMLinux Driver", MB_OK
); return 1;
1558 case DRV_INSTALL
: return DRVCNF_RESTART
;
1559 case DRV_REMOVE
: return DRVCNF_RESTART
;
1561 TRACE(midi
, "Sending msg=%u to default driver proc\n", wMsg
);
1562 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1566 /*-----------------------------------------------------------------------*/