When the vertical scrollbar is removed, the background was not
[wine/testsucceed.git] / multimedia / midi.c
blobaef23d666eb18678ae7ffbf6084a3a62c229ff8a
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 /*
4 * Sample MIDI Wine Driver for Linux
6 * Copyright 1994 Martin Ayotte
7 */
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
17 #include "config.h"
18 #include <errno.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <sys/ioctl.h>
25 #include "wine/winuser16.h"
26 #include "multimedia.h"
27 #include "driver.h"
28 #include "xmalloc.h"
29 #include "debug.h"
30 #include "heap.h"
31 #include "ldt.h"
33 typedef struct {
34 #ifndef HAVE_OSS
35 int unixdev;
36 #endif
37 int state;
38 DWORD bufsize;
39 LPMIDIOPENDESC midiDesc;
40 WORD wFlags;
41 LPMIDIHDR16 lpQueueHdr;
42 DWORD dwTotalPlayed;
43 #ifdef HAVE_OSS
44 unsigned char incoming[3];
45 unsigned char incPrev;
46 char incLen;
47 DWORD startTime;
48 #endif
49 } WINE_MIDIIN;
51 typedef struct {
52 #ifndef HAVE_OSS
53 int unixdev;
54 #endif
55 int state;
56 DWORD bufsize;
57 LPMIDIOPENDESC midiDesc;
58 WORD wFlags;
59 LPMIDIHDR16 lpQueueHdr;
60 DWORD dwTotalPlayed;
61 #ifdef HAVE_OSS
62 void* lpExtra; /* according to port type (MIDI, FM...), extra data when needed */
63 #endif
64 } WINE_MIDIOUT;
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 */
70 int MODM_NUMDEVS = 0;
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 */
79 int MIDM_NUMDEVS = 0;
81 #ifdef HAVE_OSS
82 static int midiSeqFD = -1;
83 static int numOpenMidiSeq = 0;
84 static UINT midiInTimerID = 0;
85 static int numStartedMidiIn = 0;
86 #endif /* HAVE_OSS */
88 /* this structure holds pointers with information for each MIDI
89 * out device found.
91 LPMIDIOUTCAPS16 midiOutDevices[MAX_MIDIOUTDRV];
93 /* this structure holds pointers with information for each MIDI
94 * in device found.
96 LPMIDIINCAPS16 midiInDevices [MAX_MIDIINDRV];
98 /*
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)
115 DWORD dwCallBack;
116 UINT16 uFlags;
117 HANDLE16 hDev;
118 DWORD dwInstance;
120 TRACE(midi,"wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
121 wDevID, wMsg, dwParam1, dwParam2);
123 switch (wMsg) {
124 case MOM_OPEN:
125 case MOM_CLOSE:
126 case MOM_DONE:
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;
134 break;
136 case MIM_OPEN:
137 case MIM_CLOSE:
138 case MIM_DATA:
139 case MIM_ERROR:
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;
147 break;
148 default:
149 WARN(midi, "Unsupported MSW-MIDI message %u\n", wMsg);
150 return MCIERR_INTERNAL;
153 return DriverCallback16(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2) ?
154 0 : MCIERR_INTERNAL;
157 #ifdef HAVE_OSS
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);
167 return -1;
169 if (fcntl(midiSeqFD, F_SETFL, O_NONBLOCK) < 0) {
170 WARN(midi, "can't set sequencer fd to non blocking (%d)\n", errno);
171 close(midiSeqFD);
172 midiSeqFD = -1;
173 return -1;
175 ioctl(midiSeqFD, SNDCTL_SEQ_RESET);
177 numOpenMidiSeq++;
178 return 0;
181 /**************************************************************************
182 * midiCloseSeq [internal]
184 static int midiCloseSeq(void)
186 if (--numOpenMidiSeq == 0) {
187 close(midiSeqFD);
188 midiSeqFD = -1;
190 return 0;
193 /* FIXME: this is a bad idea, it's even not static... */
194 SEQ_DEFINEBUF(1024);
196 /* FIXME: this is not reentrant, not static - because of global variable
197 * _seqbuf and al. */
198 /**************************************************************************
199 * seqbuf_dump [internal]
201 void seqbuf_dump(void)
203 if (_seqbufptr) {
204 if (write(midiSeqFD, _seqbuf, _seqbufptr) == -1) {
205 WARN(midi, "Can't write data to sequencer (%d/%d)!\n",
206 midiSeqFD, errno);
208 /* FIXME:
209 * in any case buffer is lost so that if many errors occur the buffer
210 * will not overrun
212 _seqbufptr = 0;
215 #endif /* HAVE_OSS */
217 #ifdef HAVE_OSS
219 static void midReceiveChar(WORD wDevID, unsigned char value, DWORD dwTime)
221 DWORD toSend = 0;
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");
227 return;
229 if (MidiInDev[wDevID].state == 0) {
230 TRACE(midi, "input not started, thrown away\n");
231 return;
234 if (MidiInDev[wDevID].state & 2) { /* system exclusive */
235 LPMIDIHDR16 lpMidiHdr = MidiInDev[wDevID].lpQueueHdr;
236 WORD sbfb = FALSE;
238 if (lpMidiHdr) {
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) {
244 sbfb = TRUE;
247 if (value == 0xF7) { /* then end */
248 MidiInDev[wDevID].state &= ~2;
249 sbfb = TRUE;
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");
260 return;
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);
271 } else {
272 FIXME(midi, "error for midi-in, should generate MIM_ERROR notification:"
273 " prev=%02Xh, incLen=%02Xh\n",
274 MidiInDev[wDevID].incPrev, MidiInDev[wDevID].incLen);
275 return;
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];
284 #undef IS_CMD(_x)
285 #undef IS_SYS_CMD(_x)
287 switch (MidiInDev[wDevID].incoming[0] & 0xF0) {
288 case MIDI_NOTEOFF:
289 case MIDI_NOTEON:
290 case MIDI_KEY_PRESSURE:
291 case MIDI_CTL_CHANGE:
292 case MIDI_PITCH_BEND:
293 if (MidiInDev[wDevID].incLen == 3) {
294 toSend = (MidiInDev[wDevID].incoming[2] << 16) |
295 (MidiInDev[wDevID].incoming[1] << 8) |
296 (MidiInDev[wDevID].incoming[0] << 0);
298 break;
299 case MIDI_PGM_CHANGE:
300 case MIDI_CHN_PRESSURE:
301 if (MidiInDev[wDevID].incLen == 2) {
302 toSend = (MidiInDev[wDevID].incoming[1] << 8) |
303 (MidiInDev[wDevID].incoming[0] << 0);
305 break;
306 case MIDI_SYSTEM_PREFIX:
307 if (MidiInDev[wDevID].incoming[0] == 0xF0) {
308 MidiInDev[wDevID].state |= 2;
309 MidiInDev[wDevID].incLen = 0;
310 } else {
311 if (MidiInDev[wDevID].incLen == 1) {
312 toSend = (MidiInDev[wDevID].incoming[0] << 0);
315 break;
316 default:
317 WARN(midi, "This shouldn't happen (%02X)\n", MidiInDev[wDevID].incoming[0]);
319 if (toSend != 0) {
320 TRACE(midi, "Sending event %08lx\n", toSend);
321 MidiInDev[wDevID].incLen = 0;
322 dwTime -= MidiInDev[wDevID].startTime;
323 if (MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime) != MMSYSERR_NOERROR) {
324 WARN(midi, "Couldn't notify client\n");
329 static VOID WINAPI midTimeCallback(HWND hwnd, UINT msg, UINT id, DWORD dwTime)
331 unsigned char buffer[256];
332 int len, idx;
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);
340 return;
343 for (idx = 0; idx < len; ) {
344 if (buffer[idx] & 0x80) {
345 TRACE(midi,
346 "reading<8> %02x %02x %02x %02x %02x %02x %02x %02x\n",
347 buffer[idx + 0], buffer[idx + 1],
348 buffer[idx + 2], buffer[idx + 3],
349 buffer[idx + 4], buffer[idx + 5],
350 buffer[idx + 6], buffer[idx + 7]);
351 idx += 8;
352 } else {
353 switch (buffer[idx + 0]) {
354 case SEQ_WAIT:
355 case SEQ_ECHO:
356 break;
357 case SEQ_MIDIPUTC:
358 midReceiveChar(buffer[idx + 2], buffer[idx + 1], dwTime);
359 break;
360 default:
361 TRACE(midi, "Unsupported event %d\n", buffer[idx + 0]);
362 break;
364 idx += 4;
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 /**************************************************************************
403 * midOpen [internal]
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;
413 /* FIXME :
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;
429 #ifdef HAVE_OSS
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");
439 midiCloseSeq();
440 return MMSYSERR_ERROR;
442 TRACE(midi, "Starting timer (%u) for midi-in\n", midiInTimerID);
444 #else /* HAVE_OSS */
446 int midi = open(MIDI_DEV, O_WRONLY, 0);
448 MidiInDev[wDevID].unixdev = 0;
449 if (midi == -1) {
450 WARN(midi,"can't open '%s' (%d)!\n", MIDI_DEV, errno);
451 return MMSYSERR_ALLOCATED;
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;
464 #ifdef HAVE_OSS
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;
496 #ifdef HAVE_OSS
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");
506 midiInTimerID = 0;
508 midiCloseSeq();
509 #else /* HAVE_OSS */
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;
523 return ret;
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;
541 } else {
542 LPMIDIHDR16 ptr;
544 for (ptr = MidiInDev[wDevID].lpQueueHdr;
545 ptr->lpNext != 0;
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 */
623 #ifdef HAVE_OSS
624 MidiInDev[wDevID].state = 1;
625 MidiInDev[wDevID].startTime = GetTickCount();
626 return MMSYSERR_NOERROR;
627 #else
628 return MMSYSERR_NOTENABLED;
629 #endif /* HAVE_OSS */
632 /**************************************************************************
633 * midStop [internal]
635 static DWORD midStop(WORD wDevID)
637 TRACE(midi, "(%04X);\n", wDevID);
639 /* FIXME : should test value of wDevID */
641 #ifdef HAVE_OSS
642 MidiInDev[wDevID].state = 0;
643 return MMSYSERR_NOERROR;
644 #else /* HAVE_OSS */
645 return MMSYSERR_NOTENABLED;
646 #endif /* HAVE_OSS */
649 /*-----------------------------------------------------------------------*/
651 #ifdef HAVE_OSS
653 typedef struct sVoice {
654 int note; /* 0 means not used */
655 int channel;
656 unsigned cntMark : 30,
657 status : 2;
658 #define sVS_UNUSED 0
659 #define sVS_PLAYING 1
660 #define sVS_SUSTAINED 2
661 } sVoice;
663 typedef struct sChannel {
664 int program;
666 int bender;
667 int benderRange;
668 /* controlers */
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;
679 } sChannel;
681 typedef struct sFMextra {
682 unsigned counter;
683 int drumSetMask;
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...
689 } sFMextra;
691 extern unsigned char midiFMInstrumentPatches[16 * 128];
692 extern unsigned char midiFMDrumsPatches [16 * 128];
694 /**************************************************************************
695 * modFMLoad [internal]
697 static int modFMLoad(int dev)
699 int i;
700 struct sbi_instrument sbi;
702 sbi.device = dev;
703 sbi.key = FM_PATCH;
705 memset(sbi.operators + 16, 0, 16);
706 for (i = 0; i < 128; i++) {
707 sbi.channel = 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);
712 return -1;
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);
721 return -1;
724 return 0;
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;
735 int i;
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);
743 voice[i].note = 0;
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;
752 channel[i].bank = 0;
753 channel[i].volume = 127;
754 channel[i].balance = 64;
755 channel[i].expression = 0;
756 channel[i].sustain = 0;
758 extra->counter = 0;
759 extra->drumSetMask = 1 << 9; /* channel 10 is normally drums, sometimes 16 also */
760 SEQ_DUMPBUF();
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,
771 DWORD dwSize)
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;
785 } else {
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 /**************************************************************************
810 * modOpen [internal]
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;
836 #ifdef HAVE_OSS
837 MidiOutDev[wDevID].lpExtra = 0;
839 switch (midiOutDevices[wDevID]->wTechnology) {
840 case MOD_FMSYNTH:
842 void* extra = xmalloc(sizeof(struct sFMextra) +
843 sizeof(struct sVoice) *
844 (midiOutDevices[wDevID]->wVoices - 1));
846 if (extra == 0) {
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;
853 free(extra);
854 return MMSYSERR_ERROR;
856 if (modFMLoad(wDevID) < 0) {
857 midiCloseSeq();
858 MidiOutDev[wDevID].lpExtra = 0;
859 free(extra);
860 return MMSYSERR_ERROR;
862 modFMReset(wDevID);
864 break;
865 case MOD_MIDIPORT:
866 if (midiOpenSeq() < 0) {
867 return MMSYSERR_ALLOCATED;
869 break;
870 default:
871 WARN(midi,"Technology not supported (yet) %d !\n",
872 midiOutDevices[wDevID]->wTechnology);
873 return MMSYSERR_NOTENABLED;
875 #else /* HAVE_OSS */
877 int midi = open (MIDI_DEV, O_WRONLY, 0);
878 MidiOutDev[wDevID].unixdev = 0;
879 if (midi == -1) {
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
917 * playing */
919 #ifdef HAVE_OSS
920 if (midiSeqFD == -1) {
921 WARN(midi,"can't close !\n");
922 return MMSYSERR_ERROR;
925 switch (midiOutDevices[wDevID]->wTechnology) {
926 case MOD_FMSYNTH:
927 case MOD_MIDIPORT:
928 midiCloseSeq();
929 break;
930 default:
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;
940 #else
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;
955 return ret;
958 /**************************************************************************
959 * modData [internal]
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);
969 #ifdef HAVE_OSS
970 if (midiSeqFD == -1) {
971 WARN(midi,"can't play !\n");
972 return MIDIERR_NODEVICE;
974 switch (midiOutDevices[wDevID]->wTechnology) {
975 case MOD_FMSYNTH:
976 /* FIXME:
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);
984 int i, nv;
986 switch (evt & 0xF0) {
987 case MIDI_NOTEOFF:
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);
995 break;
996 case MIDI_NOTEON:
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);
1005 break;
1007 /* finding out in this order :
1008 * - an empty voice
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)) {
1015 nv = i;
1016 break;
1018 if (voice[i].cntMark < voice[0].cntMark) {
1019 nv = i;
1022 TRACE(midi,
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);
1036 #if 0
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);
1041 #endif
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++;
1047 break;
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);
1054 break;
1055 case MIDI_CTL_CHANGE:
1056 switch (d1) {
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;
1062 if (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;
1068 } else {
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);
1076 break;
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) {
1084 case 0x0000:
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);
1093 break;
1095 case 0x7F7F:
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);
1102 break;
1103 default:
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,
1107 d2);
1108 break;
1110 break;
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);
1122 break;
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);
1133 break;
1134 default:
1135 TRACE(midi, "Dropping MIDI control event 0x%02x(%02x) on channel %d\n",
1136 d1, d2, chn);
1137 break;
1139 break;
1140 case MIDI_PGM_CHANGE:
1141 channel[chn].program = d1;
1142 break;
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);
1149 break;
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);
1157 break;
1158 case MIDI_SYSTEM_PREFIX:
1159 switch (evt & 0x0F) {
1160 case 0x0F: /* Reset */
1161 modFMReset(wDevID);
1162 break;
1163 default:
1164 WARN(midi, "Unsupported (yet) system event %02x\n", evt & 0x0F);
1166 break;
1167 default:
1168 WARN(midi, "Internal error, shouldn't happen (event=%08x)\n", evt & 0xF0);
1169 return MMSYSERR_NOTENABLED;
1172 break;
1173 case MOD_MIDIPORT:
1175 int dev = wDevID - MODM_NUMFMSYNTHDEVS;
1176 if (dev < 0) {
1177 WARN(midi, "Internal error on devID (%u) !\n", wDevID);
1178 return MIDIERR_NODEVICE;
1181 switch (evt & 0xF0) {
1182 case MIDI_NOTEOFF:
1183 case MIDI_NOTEON:
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);
1190 break;
1191 case MIDI_PGM_CHANGE:
1192 case MIDI_CHN_PRESSURE:
1193 SEQ_MIDIOUT(dev, evt);
1194 SEQ_MIDIOUT(dev, d1);
1195 break;
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. */
1206 break;
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);
1214 break;
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);
1224 break;
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);
1233 break;
1236 break;
1237 default:
1238 WARN(midi, "Technology not supported (yet) %d !\n",
1239 midiOutDevices[wDevID]->wTechnology);
1240 return MMSYSERR_NOTENABLED;
1243 SEQ_DUMPBUF();
1244 #else
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)
1265 int count;
1266 LPBYTE lpData;
1268 TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1270 #ifdef HAVE_OSS
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);
1285 if (lpData == NULL)
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
1296 * modShortData() ?
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]);
1307 #ifdef HAVE_OSS
1308 switch (midiOutDevices[wDevID]->wTechnology) {
1309 case MOD_FMSYNTH:
1310 /* FIXME: I don't think there is much to do here */
1311 break;
1312 case MOD_MIDIPORT:
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");
1328 SEQ_DUMPBUF();
1329 break;
1330 default:
1331 WARN(midi, "Technology not supported (yet) %d !\n",
1332 midiOutDevices[wDevID]->wTechnology);
1333 return MMSYSERR_NOTENABLED;
1335 #else /* HAVE_OSS */
1337 int en;
1339 for (count = 0; count < lpMidiHdr->dwBytesRecorded; count++) {
1340 if (write(MidiOutDev[wDevID].unixdev,
1341 lpData, sizeof(WORD)) != sizeof(WORD))
1342 break;
1343 ptr += 2;
1346 en = errno;
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);
1374 #ifdef HAVE_OSS
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);
1406 #ifdef HAVE_OSS
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;
1437 #endif
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);
1451 switch (wMsg) {
1452 #ifdef SNDCTL_MIDI_INFO
1453 case MIDM_OPEN:
1454 return midOpen(wDevID,(LPMIDIOPENDESC)dwParam1, dwParam2);
1455 case MIDM_CLOSE:
1456 return midClose(wDevID);
1457 case MIDM_ADDBUFFER:
1458 return midAddBuffer(wDevID,(LPMIDIHDR16)dwParam1, dwParam2);
1459 case MIDM_PREPARE:
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;
1467 case MIDM_RESET:
1468 return midReset(wDevID);
1469 case MIDM_START:
1470 return midStart(wDevID);
1471 case MIDM_STOP:
1472 return midStop(wDevID);
1473 #endif
1474 default:
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);
1489 switch (wMsg) {
1490 #ifdef SNDCTL_MIDI_INFO
1491 case MODM_OPEN:
1492 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1493 case MODM_CLOSE:
1494 return modClose(wDevID);
1495 case MODM_DATA:
1496 return modData(wDevID, dwParam1);
1497 case MODM_LONGDATA:
1498 return modLongData(wDevID, (LPMIDIHDR16)dwParam1, dwParam2);
1499 case MODM_PREPARE:
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:
1508 return 0;
1509 case MODM_SETVOLUME:
1510 return 0;
1511 case MODM_RESET:
1512 return modReset(wDevID);
1513 #endif
1514 default:
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)
1526 switch (wMsg) {
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;
1537 default:
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)
1549 switch (wMsg) {
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;
1560 default:
1561 TRACE(midi, "Sending msg=%u to default driver proc\n", wMsg);
1562 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1566 /*-----------------------------------------------------------------------*/