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