1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
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
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 #include <sys/ioctl.h>
42 #include "wine/debug.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(midi);
48 #define MIDI_SEQ "/dev/sequencer"
53 MIDIOPENDESC midiDesc;
57 unsigned char incoming[3];
58 unsigned char incPrev;
66 MIDIOPENDESC midiDesc;
70 void* lpExtra; /* according to port type (MIDI, FM...), extra data when needed */
73 static WINE_MIDIIN MidiInDev [MAX_MIDIINDRV ];
74 static WINE_MIDIOUT MidiOutDev[MAX_MIDIOUTDRV];
76 /* this is the total number of MIDI out devices found */
77 static int MODM_NUMDEVS = 0;
78 /* this is the number of FM synthetizers (index from 0 to
79 NUMFMSYNTHDEVS - 1) */
80 static int MODM_NUMFMSYNTHDEVS = 0;
81 /* this is the number of Midi ports (index from NUMFMSYNTHDEVS to
82 NUMFMSYNTHDEVS + NUMMIDIDEVS - 1) */
83 static int MODM_NUMMIDIDEVS = 0;
85 /* this is the total number of MIDI out devices found */
86 static int MIDM_NUMDEVS = 0;
88 static int midiSeqFD = -1;
89 static int numOpenMidiSeq = 0;
90 static UINT midiInTimerID = 0;
91 static int numStartedMidiIn = 0;
93 /* this structure holds pointers with information for each MIDI
96 static LPMIDIOUTCAPSA midiOutDevices[MAX_MIDIOUTDRV];
98 /* this structure holds pointers with information for each MIDI
101 static LPMIDIINCAPSA midiInDevices [MAX_MIDIINDRV];
104 * FIXME : all tests on device ID for midXXX and modYYY are made against
105 * MAX_MIDIxxDRV (when they are made) but should be done against the actual
106 * number of midi devices found...
109 /*======================================================================*
110 * Low level MIDI implementation *
111 *======================================================================*/
113 static int midiOpenSeq(void);
114 static int midiCloseSeq(void);
116 /**************************************************************************
117 * unixToWindowsDeviceType [internal]
119 * return the Windows equivalent to a Unix Device Type
122 static int MIDI_UnixToWindowsDeviceType(int type)
124 /* MOD_MIDIPORT output port
125 * MOD_SYNTH generic internal synth
126 * MOD_SQSYNTH square wave internal synth
127 * MOD_FMSYNTH FM internal synth
128 * MOD_MAPPER MIDI mapper
131 /* FIXME Is this really the correct equivalence from UNIX to
132 Windows Sound type */
135 case SYNTH_TYPE_FM: return MOD_FMSYNTH;
136 case SYNTH_TYPE_SAMPLE: return MOD_SYNTH;
137 case SYNTH_TYPE_MIDI: return MOD_MIDIPORT;
139 ERR("Cannot determine the type of this midi device. "
140 "Assuming FM Synth\n");
146 /**************************************************************************
147 * OSS_MidiInit [internal]
149 * Initializes the MIDI devices information variables
151 BOOL OSS_MidiInit(void)
153 int i, status, numsynthdevs = 255, nummididevs = 255;
154 struct synth_info sinfo;
155 struct midi_info minfo;
156 static BOOL bInitDone = FALSE;
161 TRACE("Initializing the MIDI variables.\n");
164 /* try to open device */
165 if (midiOpenSeq() == -1) {
169 /* find how many Synth devices are there in the system */
170 status = ioctl(midiSeqFD, SNDCTL_SEQ_NRSYNTHS, &numsynthdevs);
173 ERR("ioctl for nr synth failed.\n");
178 if (numsynthdevs > MAX_MIDIOUTDRV) {
179 ERR("MAX_MIDIOUTDRV (%d) was enough for the number of devices (%d). "
180 "Some FM devices will not be available.\n",MAX_MIDIOUTDRV,numsynthdevs);
181 numsynthdevs = MAX_MIDIOUTDRV;
184 for (i = 0; i < numsynthdevs; i++) {
185 LPMIDIOUTCAPSA tmplpCaps;
188 status = ioctl(midiSeqFD, SNDCTL_SYNTH_INFO, &sinfo);
190 ERR("ioctl for synth info failed.\n");
195 tmplpCaps = HeapAlloc(GetProcessHeap(), 0, sizeof(MIDIOUTCAPSA));
198 /* We also have the information sinfo.synth_subtype, not used here
201 /* Manufac ID. We do not have access to this with soundcard.h
202 * Does not seem to be a problem, because in mmsystem.h only
203 * Microsoft's ID is listed.
205 tmplpCaps->wMid = 0x00FF;
206 tmplpCaps->wPid = 0x0001; /* FIXME Product ID */
207 /* Product Version. We simply say "1" */
208 tmplpCaps->vDriverVersion = 0x001;
209 strcpy(tmplpCaps->szPname, sinfo.name);
211 tmplpCaps->wTechnology = MIDI_UnixToWindowsDeviceType(sinfo.synth_type);
212 tmplpCaps->wVoices = sinfo.nr_voices;
214 /* FIXME Is it possible to know the maximum
215 * number of simultaneous notes of a soundcard ?
216 * I believe we don't have this information, but
217 * it's probably equal or more than wVoices
219 tmplpCaps->wNotes = sinfo.nr_voices;
221 /* FIXME Do we have this information?
222 * Assuming the soundcards can handle
223 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
224 * not MIDICAPS_CACHE.
226 tmplpCaps->dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
228 midiOutDevices[i] = tmplpCaps;
230 if (sinfo.capabilities & SYNTH_CAP_INPUT) {
231 FIXME("Synthesizer support MIDI in. Not supported yet (please report)\n");
234 TRACE("name='%s', techn=%d voices=%d notes=%d support=%ld\n",
235 tmplpCaps->szPname, tmplpCaps->wTechnology,
236 tmplpCaps->wVoices, tmplpCaps->wNotes, tmplpCaps->dwSupport);
237 TRACE("OSS info: synth subtype=%d capa=%lx\n",
238 sinfo.synth_subtype, (long)sinfo.capabilities);
241 /* find how many MIDI devices are there in the system */
242 status = ioctl(midiSeqFD, SNDCTL_SEQ_NRMIDIS, &nummididevs);
244 ERR("ioctl on nr midi failed.\n");
249 /* FIXME: the two restrictions below could be loosen in some cases */
250 if (numsynthdevs + nummididevs > MAX_MIDIOUTDRV) {
251 ERR("MAX_MIDIOUTDRV was not enough for the number of devices. "
252 "Some MIDI devices will not be available.\n");
253 nummididevs = MAX_MIDIOUTDRV - numsynthdevs;
256 if (nummididevs > MAX_MIDIINDRV) {
257 ERR("MAX_MIDIINDRV (%d) was not enough for the number of devices (%d). "
258 "Some MIDI devices will not be available.\n",MAX_MIDIINDRV,nummididevs);
259 nummididevs = MAX_MIDIINDRV;
262 for (i = 0; i < nummididevs; i++) {
263 LPMIDIOUTCAPSA tmplpOutCaps;
264 LPMIDIINCAPSA tmplpInCaps;
267 status = ioctl(midiSeqFD, SNDCTL_MIDI_INFO, &minfo);
269 ERR("ioctl on midi info for device %d failed.\n", i);
274 tmplpOutCaps = HeapAlloc(GetProcessHeap(), 0, sizeof(MIDIOUTCAPSA));
277 /* This whole part is somewhat obscure to me. I'll keep trying to dig
278 info about it. If you happen to know, please tell us. The very
279 descritive minfo.dev_type was not used here.
281 /* Manufac ID. We do not have access to this with soundcard.h
282 Does not seem to be a problem, because in mmsystem.h only
283 Microsoft's ID is listed */
284 tmplpOutCaps->wMid = 0x00FF;
285 tmplpOutCaps->wPid = 0x0001; /* FIXME Product ID */
286 /* Product Version. We simply say "1" */
287 tmplpOutCaps->vDriverVersion = 0x001;
288 strcpy(tmplpOutCaps->szPname, minfo.name);
290 tmplpOutCaps->wTechnology = MOD_MIDIPORT; /* FIXME Is this right? */
291 /* Does it make any difference? */
292 tmplpOutCaps->wVoices = 16;
293 /* Does it make any difference? */
294 tmplpOutCaps->wNotes = 16;
295 /* FIXME Does it make any difference? */
296 tmplpOutCaps->dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
298 midiOutDevices[numsynthdevs + i] = tmplpOutCaps;
300 tmplpInCaps = HeapAlloc(GetProcessHeap(), 0, sizeof(MIDIOUTCAPSA));
303 /* This whole part is somewhat obscure to me. I'll keep trying to dig
304 info about it. If you happen to know, please tell us. The very
305 descritive minfo.dev_type was not used here.
307 /* Manufac ID. We do not have access to this with soundcard.h
308 Does not seem to be a problem, because in mmsystem.h only
309 Microsoft's ID is listed */
310 tmplpInCaps->wMid = 0x00FF;
311 tmplpInCaps->wPid = 0x0001; /* FIXME Product ID */
312 /* Product Version. We simply say "1" */
313 tmplpInCaps->vDriverVersion = 0x001;
314 strcpy(tmplpInCaps->szPname, minfo.name);
316 /* FIXME : could we get better information than that ? */
317 tmplpInCaps->dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
319 midiInDevices[i] = tmplpInCaps;
321 TRACE("name='%s' techn=%d voices=%d notes=%d support=%ld\n",
322 tmplpOutCaps->szPname, tmplpOutCaps->wTechnology, tmplpOutCaps->wVoices,
323 tmplpOutCaps->wNotes, tmplpOutCaps->dwSupport);
324 TRACE("OSS info: midi dev-type=%d, capa=%lx\n",
325 minfo.dev_type, (long)minfo.capabilities);
328 /* windows does not seem to differentiate Synth from MIDI devices */
329 MODM_NUMFMSYNTHDEVS = numsynthdevs;
330 MODM_NUMMIDIDEVS = nummididevs;
331 MODM_NUMDEVS = numsynthdevs + nummididevs;
333 MIDM_NUMDEVS = nummididevs;
335 /* close file and exit */
341 /**************************************************************************
342 * MIDI_NotifyClient [internal]
344 static DWORD MIDI_NotifyClient(UINT wDevID, WORD wMsg,
345 DWORD dwParam1, DWORD dwParam2)
352 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
353 wDevID, wMsg, dwParam1, dwParam2);
359 if (wDevID > MAX_MIDIOUTDRV)
360 return MCIERR_INTERNAL;
362 dwCallBack = MidiOutDev[wDevID].midiDesc.dwCallback;
363 uFlags = MidiOutDev[wDevID].wFlags;
364 hDev = MidiOutDev[wDevID].midiDesc.hMidi;
365 dwInstance = MidiOutDev[wDevID].midiDesc.dwInstance;
372 if (wDevID > MAX_MIDIINDRV)
373 return MCIERR_INTERNAL;
375 dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
376 uFlags = MidiInDev[wDevID].wFlags;
377 hDev = MidiInDev[wDevID].midiDesc.hMidi;
378 dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
381 WARN("Unsupported MSW-MIDI message %u\n", wMsg);
382 return MCIERR_INTERNAL;
385 return DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2) ?
389 static int midi_warn = 1;
390 /**************************************************************************
391 * midiOpenSeq [internal]
393 static int midiOpenSeq(void)
395 if (numOpenMidiSeq == 0) {
396 midiSeqFD = open(MIDI_SEQ, O_RDWR, 0);
397 if (midiSeqFD == -1) {
400 WARN("Can't open MIDI device '%s' ! (%s). If your "
401 "program needs this (probably not): %s\n",
402 MIDI_SEQ, strerror(errno),
404 "create it ! (\"man MAKEDEV\" ?)" :
406 "load MIDI sequencer kernel driver !" :
408 "grant access ! (\"man chmod\")" : ""
414 if (fcntl(midiSeqFD, F_SETFL, O_NONBLOCK) < 0) {
415 WARN("can't set sequencer fd to non-blocking, errno %d (%s)\n", errno, strerror(errno));
420 fcntl(midiSeqFD, F_SETFD, 1); /* set close on exec flag */
421 ioctl(midiSeqFD, SNDCTL_SEQ_RESET);
427 /**************************************************************************
428 * midiCloseSeq [internal]
430 static int midiCloseSeq(void)
432 if (--numOpenMidiSeq == 0) {
439 /* FIXME: this is a bad idea, it's even not static... */
442 /* FIXME: this is not reentrant, not static - because of global variable
445 /**************************************************************************
446 * seqbuf_dump [internal]
448 void seqbuf_dump(void)
451 if (write(midiSeqFD, _seqbuf, _seqbufptr) == -1) {
452 WARN("Can't write data to sequencer %d, errno %d (%s)!\n",
453 midiSeqFD, errno, strerror(errno));
456 * in any case buffer is lost so that if many errors occur the buffer
463 static void midReceiveChar(WORD wDevID, unsigned char value, DWORD dwTime)
467 TRACE("Adding %02xh to %d[%d]\n", value, wDevID, MidiInDev[wDevID].incLen);
469 if (wDevID >= MAX_MIDIINDRV) {
473 if (MidiInDev[wDevID].state == 0) {
474 TRACE("input not started, thrown away\n");
478 if (MidiInDev[wDevID].state & 2) { /* system exclusive */
479 LPMIDIHDR lpMidiHdr = MidiInDev[wDevID].lpQueueHdr;
483 LPBYTE lpData = lpMidiHdr->lpData;
485 lpData[lpMidiHdr->dwBytesRecorded++] = value;
486 if (lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) {
490 if (value == 0xF7) { /* then end */
491 MidiInDev[wDevID].state &= ~2;
494 if (sbfb && lpMidiHdr != NULL) {
495 lpMidiHdr = MidiInDev[wDevID].lpQueueHdr;
496 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
497 lpMidiHdr->dwFlags |= MHDR_DONE;
498 MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)lpMidiHdr->lpNext;
499 if (MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD)lpMidiHdr, dwTime) != MMSYSERR_NOERROR) {
500 WARN("Couldn't notify client\n");
506 #define IS_CMD(_x) (((_x) & 0x80) == 0x80)
507 #define IS_SYS_CMD(_x) (((_x) & 0xF0) == 0xF0)
509 if (!IS_CMD(value) && MidiInDev[wDevID].incLen == 0) { /* try to reuse old cmd */
510 if (IS_CMD(MidiInDev[wDevID].incPrev) && !IS_SYS_CMD(MidiInDev[wDevID].incPrev)) {
511 MidiInDev[wDevID].incoming[0] = MidiInDev[wDevID].incPrev;
512 MidiInDev[wDevID].incLen = 1;
513 TRACE("Reusing old command %02xh\n", MidiInDev[wDevID].incPrev);
515 FIXME("error for midi-in, should generate MIM_ERROR notification:"
516 " prev=%02Xh, incLen=%02Xh\n",
517 MidiInDev[wDevID].incPrev, MidiInDev[wDevID].incLen);
521 MidiInDev[wDevID].incoming[(int)(MidiInDev[wDevID].incLen++)] = value;
522 if (MidiInDev[wDevID].incLen == 1 && !IS_SYS_CMD(MidiInDev[wDevID].incoming[0])) {
523 /* store new cmd, just in case */
524 MidiInDev[wDevID].incPrev = MidiInDev[wDevID].incoming[0];
530 switch (MidiInDev[wDevID].incoming[0] & 0xF0) {
533 case MIDI_KEY_PRESSURE:
534 case MIDI_CTL_CHANGE:
535 case MIDI_PITCH_BEND:
536 if (MidiInDev[wDevID].incLen == 3) {
537 toSend = (MidiInDev[wDevID].incoming[2] << 16) |
538 (MidiInDev[wDevID].incoming[1] << 8) |
539 (MidiInDev[wDevID].incoming[0] << 0);
542 case MIDI_PGM_CHANGE:
543 case MIDI_CHN_PRESSURE:
544 if (MidiInDev[wDevID].incLen == 2) {
545 toSend = (MidiInDev[wDevID].incoming[1] << 8) |
546 (MidiInDev[wDevID].incoming[0] << 0);
549 case MIDI_SYSTEM_PREFIX:
550 if (MidiInDev[wDevID].incoming[0] == 0xF0) {
551 MidiInDev[wDevID].state |= 2;
552 MidiInDev[wDevID].incLen = 0;
554 if (MidiInDev[wDevID].incLen == 1) {
555 toSend = (MidiInDev[wDevID].incoming[0] << 0);
560 WARN("This shouldn't happen (%02X)\n", MidiInDev[wDevID].incoming[0]);
563 TRACE("Sending event %08lx\n", toSend);
564 MidiInDev[wDevID].incLen = 0;
565 dwTime -= MidiInDev[wDevID].startTime;
566 if (MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime) != MMSYSERR_NOERROR) {
567 WARN("Couldn't notify client\n");
572 static VOID WINAPI midTimeCallback(HWND hwnd, UINT msg, UINT id, DWORD dwTime)
574 unsigned char buffer[256];
577 TRACE("(%04X, %d, %d, %lu)\n", hwnd, msg, id, dwTime);
579 len = read(midiSeqFD, buffer, sizeof(buffer));
582 if ((len % 4) != 0) {
583 WARN("Bad length %d, errno %d (%s)\n", len, errno, strerror(errno));
587 for (idx = 0; idx < len; ) {
588 if (buffer[idx] & 0x80) {
590 "Reading<8> %02x %02x %02x %02x %02x %02x %02x %02x\n",
591 buffer[idx + 0], buffer[idx + 1],
592 buffer[idx + 2], buffer[idx + 3],
593 buffer[idx + 4], buffer[idx + 5],
594 buffer[idx + 6], buffer[idx + 7]);
597 switch (buffer[idx + 0]) {
602 midReceiveChar(buffer[idx + 2], buffer[idx + 1], dwTime);
605 TRACE("Unsupported event %d\n", buffer[idx + 0]);
613 /**************************************************************************
614 * midGetDevCaps [internal]
616 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSA lpCaps, DWORD dwSize)
618 TRACE("(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize);
620 if (wDevID >= MIDM_NUMDEVS) return MMSYSERR_BADDEVICEID;
621 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
623 memcpy(lpCaps, midiInDevices[wDevID], min(dwSize, sizeof(*lpCaps)));
625 return MMSYSERR_NOERROR;
628 /**************************************************************************
631 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
633 TRACE("(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
635 if (lpDesc == NULL) {
636 WARN("Invalid Parameter !\n");
637 return MMSYSERR_INVALPARAM;
641 * how to check that content of lpDesc is correct ?
643 if (wDevID >= MAX_MIDIINDRV) {
644 WARN("wDevID too large (%u) !\n", wDevID);
645 return MMSYSERR_BADDEVICEID;
647 if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
648 WARN("device already open !\n");
649 return MMSYSERR_ALLOCATED;
651 if ((dwFlags & MIDI_IO_STATUS) != 0) {
652 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
653 dwFlags &= ~MIDI_IO_STATUS;
655 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
656 FIXME("Bad dwFlags\n");
657 return MMSYSERR_INVALFLAG;
660 if (midiOpenSeq() < 0) {
661 return MMSYSERR_ERROR;
664 if (numStartedMidiIn++ == 0) {
665 midiInTimerID = SetTimer(0, 0, 250, midTimeCallback);
666 if (!midiInTimerID) {
667 numStartedMidiIn = 0;
668 WARN("Couldn't start timer for midi-in\n");
670 return MMSYSERR_ERROR;
672 TRACE("Starting timer (%u) for midi-in\n", midiInTimerID);
675 MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
677 MidiInDev[wDevID].lpQueueHdr = NULL;
678 MidiInDev[wDevID].dwTotalPlayed = 0;
679 MidiInDev[wDevID].bufsize = 0x3FFF;
680 MidiInDev[wDevID].midiDesc = *lpDesc;
681 MidiInDev[wDevID].state = 0;
682 MidiInDev[wDevID].incLen = 0;
683 MidiInDev[wDevID].startTime = 0;
685 if (MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
686 WARN("can't notify client !\n");
687 return MMSYSERR_INVALPARAM;
689 return MMSYSERR_NOERROR;
692 /**************************************************************************
693 * midClose [internal]
695 static DWORD midClose(WORD wDevID)
697 int ret = MMSYSERR_NOERROR;
699 TRACE("(%04X);\n", wDevID);
701 if (wDevID >= MAX_MIDIINDRV) {
702 WARN("wDevID too big (%u) !\n", wDevID);
703 return MMSYSERR_BADDEVICEID;
705 if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
706 WARN("device not opened !\n");
707 return MMSYSERR_ERROR;
709 if (MidiInDev[wDevID].lpQueueHdr != 0) {
710 return MIDIERR_STILLPLAYING;
713 if (midiSeqFD == -1) {
715 return MMSYSERR_ERROR;
717 if (--numStartedMidiIn == 0) {
718 TRACE("Stopping timer for midi-in\n");
719 if (!KillTimer(0, midiInTimerID)) {
720 WARN("Couldn't stop timer for midi-in\n");
726 MidiInDev[wDevID].bufsize = 0;
727 if (MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
728 WARN("can't notify client !\n");
729 ret = MMSYSERR_INVALPARAM;
731 MidiInDev[wDevID].midiDesc.hMidi = 0;
735 /**************************************************************************
736 * midAddBuffer [internal]
738 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
740 TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
742 if (lpMidiHdr == NULL) return MMSYSERR_INVALPARAM;
743 if (sizeof(MIDIHDR) > dwSize) return MMSYSERR_INVALPARAM;
744 if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
745 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
746 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
748 if (MidiInDev[wDevID].lpQueueHdr == 0) {
749 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
753 for (ptr = MidiInDev[wDevID].lpQueueHdr;
755 ptr = (LPMIDIHDR)ptr->lpNext);
756 ptr->lpNext = (struct midihdr_tag*)lpMidiHdr;
758 return MMSYSERR_NOERROR;
761 /**************************************************************************
762 * midPrepare [internal]
764 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
766 TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
768 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
769 lpMidiHdr->lpData == 0 || lpMidiHdr->dwFlags != 0 ||
770 lpMidiHdr->dwBufferLength >= 0x10000ul)
771 return MMSYSERR_INVALPARAM;
773 lpMidiHdr->lpNext = 0;
774 lpMidiHdr->dwFlags |= MHDR_PREPARED;
775 lpMidiHdr->dwBytesRecorded = 0;
777 return MMSYSERR_NOERROR;
780 /**************************************************************************
781 * midUnprepare [internal]
783 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
785 TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
787 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
788 lpMidiHdr->lpData == 0 || lpMidiHdr->dwBufferLength >= 0x10000ul)
789 return MMSYSERR_INVALPARAM;
791 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
792 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
794 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
796 return MMSYSERR_NOERROR;
799 /**************************************************************************
800 * midReset [internal]
802 static DWORD midReset(WORD wDevID)
804 DWORD dwTime = GetTickCount();
806 TRACE("(%04X);\n", wDevID);
808 while (MidiInDev[wDevID].lpQueueHdr) {
809 MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
810 MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
811 /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
812 if (MIDI_NotifyClient(wDevID, MIM_LONGDATA,
813 (DWORD)MidiInDev[wDevID].lpQueueHdr, dwTime) != MMSYSERR_NOERROR) {
814 WARN("Couldn't notify client\n");
816 MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)MidiInDev[wDevID].lpQueueHdr->lpNext;
819 return MMSYSERR_NOERROR;
823 /**************************************************************************
824 * midStart [internal]
826 static DWORD midStart(WORD wDevID)
828 TRACE("(%04X);\n", wDevID);
830 /* FIXME : should test value of wDevID */
832 MidiInDev[wDevID].state = 1;
833 MidiInDev[wDevID].startTime = GetTickCount();
834 return MMSYSERR_NOERROR;
837 /**************************************************************************
840 static DWORD midStop(WORD wDevID)
842 TRACE("(%04X);\n", wDevID);
844 /* FIXME : should test value of wDevID */
845 MidiInDev[wDevID].state = 0;
846 return MMSYSERR_NOERROR;
849 /*-----------------------------------------------------------------------*/
851 typedef struct sVoice {
852 int note; /* 0 means not used */
854 unsigned cntMark : 30,
857 #define sVS_PLAYING 1
858 #define sVS_SUSTAINED 2
861 typedef struct sChannel {
867 int bank; /* CTL_BANK_SELECT */
868 int volume; /* CTL_MAIN_VOLUME */
869 int balance; /* CTL_BALANCE */
870 int expression; /* CTL_EXPRESSION */
871 int sustain; /* CTL_SUSTAIN */
873 unsigned char nrgPmtMSB; /* Non register Parameters */
874 unsigned char nrgPmtLSB;
875 unsigned char regPmtMSB; /* Non register Parameters */
876 unsigned char regPmtLSB;
879 typedef struct sFMextra {
882 sChannel channel[16]; /* MIDI has only 16 channels */
883 sVoice voice[1]; /* dyn allocated according to sound card */
884 /* do not append fields below voice[1] since the size of this structure
885 * depends on the number of available voices on the FM synth...
889 extern unsigned char midiFMInstrumentPatches[16 * 128];
890 extern unsigned char midiFMDrumsPatches [16 * 128];
892 /**************************************************************************
893 * modFMLoad [internal]
895 static int modFMLoad(int dev)
898 struct sbi_instrument sbi;
903 memset(sbi.operators + 16, 0, 16);
904 for (i = 0; i < 128; i++) {
906 memcpy(sbi.operators, midiFMInstrumentPatches + i * 16, 16);
908 if (write(midiSeqFD, (char*)&sbi, sizeof(sbi)) == -1) {
909 WARN("Couldn't write patch for instrument %d, errno %d (%s)!\n", sbi.channel, errno, strerror(errno));
913 for (i = 0; i < 128; i++) {
914 sbi.channel = 128 + i;
915 memcpy(sbi.operators, midiFMDrumsPatches + i * 16, 16);
917 if (write(midiSeqFD, (char*)&sbi, sizeof(sbi)) == -1) {
918 WARN("Couldn't write patch for drum %d, errno %d (%s)!\n", sbi.channel, errno, strerror(errno));
925 /**************************************************************************
926 * modFMReset [internal]
928 static void modFMReset(WORD wDevID)
930 sFMextra* extra = (sFMextra*)MidiOutDev[wDevID].lpExtra;
931 sVoice* voice = extra->voice;
932 sChannel* channel = extra->channel;
935 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
936 if (voice[i].status != sVS_UNUSED) {
937 SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
939 SEQ_KEY_PRESSURE(wDevID, i, 127, 0);
940 SEQ_CONTROL(wDevID, i, SEQ_VOLMODE, VOL_METHOD_LINEAR);
942 voice[i].channel = -1;
943 voice[i].cntMark = 0;
944 voice[i].status = sVS_UNUSED;
946 for (i = 0; i < 16; i++) {
947 channel[i].program = 0;
948 channel[i].bender = 8192;
949 channel[i].benderRange = 2;
951 channel[i].volume = 127;
952 channel[i].balance = 64;
953 channel[i].expression = 0;
954 channel[i].sustain = 0;
957 extra->drumSetMask = 1 << 9; /* channel 10 is normally drums, sometimes 16 also */
961 #define IS_DRUM_CHANNEL(_xtra, _chn) ((_xtra)->drumSetMask & (1 << (_chn)))
963 /**************************************************************************
964 * modGetDevCaps [internal]
966 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSA lpCaps, DWORD dwSize)
968 TRACE("(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize);
970 if (wDevID >= MODM_NUMDEVS) return MMSYSERR_BADDEVICEID;
971 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
973 memcpy(lpCaps, midiOutDevices[wDevID], min(dwSize, sizeof(*lpCaps)));
975 return MMSYSERR_NOERROR;
978 /**************************************************************************
981 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
983 TRACE("(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
984 if (lpDesc == NULL) {
985 WARN("Invalid Parameter !\n");
986 return MMSYSERR_INVALPARAM;
988 if (wDevID >= MAX_MIDIOUTDRV) {
989 TRACE("MAX_MIDIOUTDRV reached !\n");
990 return MMSYSERR_BADDEVICEID;
992 if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
993 WARN("device already open !\n");
994 return MMSYSERR_ALLOCATED;
996 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
997 WARN("bad dwFlags\n");
998 return MMSYSERR_INVALFLAG;
1000 if (midiOutDevices[wDevID] == NULL) {
1001 TRACE("un-allocated wDevID\n");
1002 return MMSYSERR_BADDEVICEID;
1005 MidiOutDev[wDevID].lpExtra = 0;
1007 switch (midiOutDevices[wDevID]->wTechnology) {
1010 void* extra = HeapAlloc(GetProcessHeap(), 0,
1011 sizeof(struct sFMextra) +
1012 sizeof(struct sVoice) * (midiOutDevices[wDevID]->wVoices - 1));
1015 WARN("can't alloc extra data !\n");
1016 return MMSYSERR_NOMEM;
1018 MidiOutDev[wDevID].lpExtra = extra;
1019 if (midiOpenSeq() < 0) {
1020 MidiOutDev[wDevID].lpExtra = 0;
1021 HeapFree(GetProcessHeap(), 0, extra);
1022 return MMSYSERR_ERROR;
1024 if (modFMLoad(wDevID) < 0) {
1026 MidiOutDev[wDevID].lpExtra = 0;
1027 HeapFree(GetProcessHeap(), 0, extra);
1028 return MMSYSERR_ERROR;
1034 if (midiOpenSeq() < 0) {
1035 return MMSYSERR_ALLOCATED;
1039 WARN("Technology not supported (yet) %d !\n",
1040 midiOutDevices[wDevID]->wTechnology);
1041 return MMSYSERR_NOTENABLED;
1044 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1046 MidiOutDev[wDevID].lpQueueHdr = NULL;
1047 MidiOutDev[wDevID].dwTotalPlayed = 0;
1048 MidiOutDev[wDevID].bufsize = 0x3FFF;
1049 MidiOutDev[wDevID].midiDesc = *lpDesc;
1051 if (MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1052 WARN("can't notify client !\n");
1053 return MMSYSERR_INVALPARAM;
1055 TRACE("Successful !\n");
1056 return MMSYSERR_NOERROR;
1060 /**************************************************************************
1061 * modClose [internal]
1063 static DWORD modClose(WORD wDevID)
1065 int ret = MMSYSERR_NOERROR;
1067 TRACE("(%04X);\n", wDevID);
1069 if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
1070 WARN("device not opened !\n");
1071 return MMSYSERR_ERROR;
1073 /* FIXME: should test that no pending buffer is still in the queue for
1076 if (midiSeqFD == -1) {
1077 WARN("can't close !\n");
1078 return MMSYSERR_ERROR;
1081 switch (midiOutDevices[wDevID]->wTechnology) {
1087 WARN("Technology not supported (yet) %d !\n",
1088 midiOutDevices[wDevID]->wTechnology);
1089 return MMSYSERR_NOTENABLED;
1092 if (MidiOutDev[wDevID].lpExtra != 0) {
1093 HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
1094 MidiOutDev[wDevID].lpExtra = 0;
1097 MidiOutDev[wDevID].bufsize = 0;
1098 if (MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
1099 WARN("can't notify client !\n");
1100 ret = MMSYSERR_INVALPARAM;
1102 MidiOutDev[wDevID].midiDesc.hMidi = 0;
1106 /**************************************************************************
1107 * modData [internal]
1109 static DWORD modData(WORD wDevID, DWORD dwParam)
1111 WORD evt = LOBYTE(LOWORD(dwParam));
1112 WORD d1 = HIBYTE(LOWORD(dwParam));
1113 WORD d2 = LOBYTE(HIWORD(dwParam));
1115 TRACE("(%04X, %08lX);\n", wDevID, dwParam);
1117 if (midiSeqFD == -1) {
1118 WARN("can't play !\n");
1119 return MIDIERR_NODEVICE;
1121 switch (midiOutDevices[wDevID]->wTechnology) {
1124 * - chorus depth controller is not used
1127 sFMextra* extra = (sFMextra*)MidiOutDev[wDevID].lpExtra;
1128 sVoice* voice = extra->voice;
1129 sChannel* channel = extra->channel;
1130 int chn = (evt & 0x0F);
1133 switch (evt & 0xF0) {
1135 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1136 /* don't stop sustained notes */
1137 if (voice[i].status == sVS_PLAYING && voice[i].channel == chn && voice[i].note == d1) {
1138 voice[i].status = sVS_UNUSED;
1139 SEQ_STOP_NOTE(wDevID, i, d1, d2);
1144 if (d2 == 0) { /* note off if velocity == 0 */
1145 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1146 /* don't stop sustained notes */
1147 if (voice[i].status == sVS_PLAYING && voice[i].channel == chn && voice[i].note == d1) {
1148 voice[i].status = sVS_UNUSED;
1149 SEQ_STOP_NOTE(wDevID, i, d1, 64);
1154 /* finding out in this order :
1156 * - if replaying the same note on the same channel
1157 * - the older voice (LRU)
1159 for (i = nv = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1160 if (voice[i].status == sVS_UNUSED ||
1161 (voice[i].note == d1 && voice[i].channel == chn)) {
1165 if (voice[i].cntMark < voice[0].cntMark) {
1170 "playing on voice=%d, pgm=%d, pan=0x%02X, vol=0x%02X, "
1171 "bender=0x%02X, note=0x%02X, vel=0x%02X\n",
1172 nv, channel[chn].program,
1173 channel[chn].balance,
1174 channel[chn].volume,
1175 channel[chn].bender, d1, d2);
1177 SEQ_SET_PATCH(wDevID, nv, IS_DRUM_CHANNEL(extra, chn) ?
1178 (128 + d1) : channel[chn].program);
1179 SEQ_BENDER_RANGE(wDevID, nv, channel[chn].benderRange * 100);
1180 SEQ_BENDER(wDevID, nv, channel[chn].bender);
1181 SEQ_CONTROL(wDevID, nv, CTL_PAN, channel[chn].balance);
1182 SEQ_CONTROL(wDevID, nv, CTL_EXPRESSION, channel[chn].expression);
1184 /* FIXME: does not really seem to work on my SB card and
1185 * screws everything up... so lay it down
1187 SEQ_CONTROL(wDevID, nv, CTL_MAIN_VOLUME, channel[chn].volume);
1189 SEQ_START_NOTE(wDevID, nv, d1, d2);
1190 voice[nv].status = channel[chn].sustain ? sVS_SUSTAINED : sVS_PLAYING;
1191 voice[nv].note = d1;
1192 voice[nv].channel = chn;
1193 voice[nv].cntMark = extra->counter++;
1195 case MIDI_KEY_PRESSURE:
1196 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1197 if (voice[i].status != sVS_UNUSED && voice[i].channel == chn && voice[i].note == d1) {
1198 SEQ_KEY_PRESSURE(wDevID, i, d1, d2);
1202 case MIDI_CTL_CHANGE:
1204 case CTL_BANK_SELECT: channel[chn].bank = d2; break;
1205 case CTL_MAIN_VOLUME: channel[chn].volume = d2; break;
1206 case CTL_PAN: channel[chn].balance = d2; break;
1207 case CTL_EXPRESSION: channel[chn].expression = d2; break;
1208 case CTL_SUSTAIN: channel[chn].sustain = d2;
1210 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1211 if (voice[i].status == sVS_PLAYING && voice[i].channel == chn) {
1212 voice[i].status = sVS_SUSTAINED;
1216 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1217 if (voice[i].status == sVS_SUSTAINED && voice[i].channel == chn) {
1218 voice[i].status = sVS_UNUSED;
1219 SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1224 case CTL_NONREG_PARM_NUM_LSB: channel[chn].nrgPmtLSB = d2; break;
1225 case CTL_NONREG_PARM_NUM_MSB: channel[chn].nrgPmtMSB = d2; break;
1226 case CTL_REGIST_PARM_NUM_LSB: channel[chn].regPmtLSB = d2; break;
1227 case CTL_REGIST_PARM_NUM_MSB: channel[chn].regPmtMSB = d2; break;
1228 case CTL_DATA_ENTRY:
1229 switch ((channel[chn].regPmtMSB << 8) | channel[chn].regPmtLSB) {
1231 if (channel[chn].benderRange != d2) {
1232 channel[chn].benderRange = d2;
1233 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1234 if (voice[i].channel == chn) {
1235 SEQ_BENDER_RANGE(wDevID, i, channel[chn].benderRange);
1242 channel[chn].benderRange = 2;
1243 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1244 if (voice[i].channel == chn) {
1245 SEQ_BENDER_RANGE(wDevID, i, channel[chn].benderRange);
1250 TRACE("Data entry: regPmt=0x%02x%02x, nrgPmt=0x%02x%02x with %x\n",
1251 channel[chn].regPmtMSB, channel[chn].regPmtLSB,
1252 channel[chn].nrgPmtMSB, channel[chn].nrgPmtLSB,
1258 case 0x78: /* all sounds off */
1259 /* FIXME: I don't know if I have to take care of the channel
1260 * for this control ?
1262 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1263 if (voice[i].status != sVS_UNUSED && voice[i].channel == chn) {
1264 voice[i].status = sVS_UNUSED;
1265 SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1269 case 0x7B: /* all notes off */
1270 /* FIXME: I don't know if I have to take care of the channel
1271 * for this control ?
1273 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1274 if (voice[i].status == sVS_PLAYING && voice[i].channel == chn) {
1275 voice[i].status = sVS_UNUSED;
1276 SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1281 TRACE("Dropping MIDI control event 0x%02x(%02x) on channel %d\n",
1286 case MIDI_PGM_CHANGE:
1287 channel[chn].program = d1;
1289 case MIDI_CHN_PRESSURE:
1290 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1291 if (voice[i].status != sVS_UNUSED && voice[i].channel == chn) {
1292 SEQ_KEY_PRESSURE(wDevID, i, voice[i].note, d1);
1296 case MIDI_PITCH_BEND:
1297 channel[chn].bender = (d2 << 7) + d1;
1298 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1299 if (voice[i].channel == chn) {
1300 SEQ_BENDER(wDevID, i, channel[chn].bender);
1304 case MIDI_SYSTEM_PREFIX:
1305 switch (evt & 0x0F) {
1306 case 0x0F: /* Reset */
1310 WARN("Unsupported (yet) system event %02x\n", evt & 0x0F);
1314 WARN("Internal error, shouldn't happen (event=%08x)\n", evt & 0xF0);
1315 return MMSYSERR_NOTENABLED;
1321 int dev = wDevID - MODM_NUMFMSYNTHDEVS;
1323 WARN("Internal error on devID (%u) !\n", wDevID);
1324 return MIDIERR_NODEVICE;
1327 switch (evt & 0xF0) {
1330 case MIDI_KEY_PRESSURE:
1331 case MIDI_CTL_CHANGE:
1332 case MIDI_PITCH_BEND:
1333 SEQ_MIDIOUT(dev, evt);
1334 SEQ_MIDIOUT(dev, d1);
1335 SEQ_MIDIOUT(dev, d2);
1337 case MIDI_PGM_CHANGE:
1338 case MIDI_CHN_PRESSURE:
1339 SEQ_MIDIOUT(dev, evt);
1340 SEQ_MIDIOUT(dev, d1);
1342 case MIDI_SYSTEM_PREFIX:
1343 switch (evt & 0x0F) {
1344 case 0x00: /* System Exclusive, don't do it on modData,
1345 * should require modLongData*/
1346 case 0x01: /* Undefined */
1347 case 0x04: /* Undefined. */
1348 case 0x05: /* Undefined. */
1349 case 0x07: /* End of Exclusive. */
1350 case 0x09: /* Undefined. */
1351 case 0x0D: /* Undefined. */
1353 case 0x06: /* Tune Request */
1354 case 0x08: /* Timing Clock. */
1355 case 0x0A: /* Start. */
1356 case 0x0B: /* Continue */
1357 case 0x0C: /* Stop */
1358 case 0x0E: /* Active Sensing. */
1359 SEQ_MIDIOUT(dev, evt);
1361 case 0x0F: /* Reset */
1362 /* SEQ_MIDIOUT(dev, evt);
1363 this other way may be better */
1364 SEQ_MIDIOUT(dev, MIDI_SYSTEM_PREFIX);
1365 SEQ_MIDIOUT(dev, 0x7e);
1366 SEQ_MIDIOUT(dev, 0x7f);
1367 SEQ_MIDIOUT(dev, 0x09);
1368 SEQ_MIDIOUT(dev, 0x01);
1369 SEQ_MIDIOUT(dev, 0xf7);
1371 case 0x03: /* Song Select. */
1372 SEQ_MIDIOUT(dev, evt);
1373 SEQ_MIDIOUT(dev, d1);
1374 case 0x02: /* Song Position Pointer. */
1375 SEQ_MIDIOUT(dev, evt);
1376 SEQ_MIDIOUT(dev, d1);
1377 SEQ_MIDIOUT(dev, d2);
1384 WARN("Technology not supported (yet) %d !\n",
1385 midiOutDevices[wDevID]->wTechnology);
1386 return MMSYSERR_NOTENABLED;
1391 return MMSYSERR_NOERROR;
1394 /**************************************************************************
1395 * modLongData [internal]
1397 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1402 TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1404 if (midiSeqFD == -1) {
1405 WARN("can't play !\n");
1406 return MIDIERR_NODEVICE;
1409 lpData = lpMidiHdr->lpData;
1412 return MIDIERR_UNPREPARED;
1413 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
1414 return MIDIERR_UNPREPARED;
1415 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1416 return MIDIERR_STILLPLAYING;
1417 lpMidiHdr->dwFlags &= ~MHDR_DONE;
1418 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
1420 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
1421 * data, or can it also contain raw MIDI data, to be split up and sent to
1423 * If the latest is true, then the following WARNing will fire up
1425 if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
1426 WARN("Alledged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
1429 TRACE("dwBufferLength=%lu !\n", lpMidiHdr->dwBufferLength);
1430 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
1431 lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
1432 lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
1434 switch (midiOutDevices[wDevID]->wTechnology) {
1436 /* FIXME: I don't think there is much to do here */
1439 if (lpData[0] != 0xF0) {
1440 /* Send end of System Exclusive */
1441 SEQ_MIDIOUT(wDevID - MODM_NUMFMSYNTHDEVS, 0xF0);
1442 WARN("Adding missing 0xF0 marker at the beginning of "
1443 "system exclusive byte stream\n");
1445 for (count = 0; count < lpMidiHdr->dwBytesRecorded; count++) {
1446 SEQ_MIDIOUT(wDevID - MODM_NUMFMSYNTHDEVS, lpData[count]);
1448 if (lpData[count - 1] != 0xF7) {
1449 /* Send end of System Exclusive */
1450 SEQ_MIDIOUT(wDevID - MODM_NUMFMSYNTHDEVS, 0xF7);
1451 WARN("Adding missing 0xF7 marker at the end of "
1452 "system exclusive byte stream\n");
1457 WARN("Technology not supported (yet) %d !\n",
1458 midiOutDevices[wDevID]->wTechnology);
1459 return MMSYSERR_NOTENABLED;
1462 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1463 lpMidiHdr->dwFlags |= MHDR_DONE;
1464 if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD)lpMidiHdr, 0L) != MMSYSERR_NOERROR) {
1465 WARN("can't notify client !\n");
1466 return MMSYSERR_INVALPARAM;
1468 return MMSYSERR_NOERROR;
1471 /**************************************************************************
1472 * modPrepare [internal]
1474 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1476 TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1478 if (midiSeqFD == -1) {
1479 WARN("can't prepare !\n");
1480 return MMSYSERR_NOTENABLED;
1483 /* MS doc says taht dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
1484 * asks to prepare MIDIHDR which dwFlags != 0.
1485 * So at least check for the inqueue flag
1487 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
1488 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
1489 lpMidiHdr->dwBufferLength >= 0x10000ul) {
1490 WARN("%p %p %08lx %d/%ld\n", lpMidiHdr, lpMidiHdr->lpData,
1491 lpMidiHdr->dwFlags, sizeof(MIDIHDR), dwSize);
1492 return MMSYSERR_INVALPARAM;
1495 lpMidiHdr->lpNext = 0;
1496 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1497 lpMidiHdr->dwFlags &= ~MHDR_DONE;
1498 return MMSYSERR_NOERROR;
1501 /**************************************************************************
1502 * modUnprepare [internal]
1504 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1506 TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1508 if (midiSeqFD == -1) {
1509 WARN("can't unprepare !\n");
1510 return MMSYSERR_NOTENABLED;
1513 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0)
1514 return MMSYSERR_INVALPARAM;
1515 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1516 return MIDIERR_STILLPLAYING;
1517 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1518 return MMSYSERR_NOERROR;
1521 /**************************************************************************
1522 * modReset [internal]
1524 static DWORD modReset(WORD wDevID)
1528 TRACE("(%04X);\n", wDevID);
1530 /* stop all notes */
1531 /* FIXME: check if 0x78B0 is channel dependant or not. I coded it so that
1532 * it's channel dependent...
1534 for (chn = 0; chn < 16; chn++) {
1535 /* turn off every note */
1536 modData(wDevID, 0x7800 | MIDI_CTL_CHANGE | chn);
1537 /* remove sustain on all channels */
1538 modData(wDevID, (CTL_SUSTAIN << 8) | MIDI_CTL_CHANGE | chn);
1540 /* FIXME: the LongData buffers must also be returned to the app */
1541 return MMSYSERR_NOERROR;
1544 #endif /* HAVE_OSS_MIDI */
1546 /*======================================================================*
1547 * MIDI entry points *
1548 *======================================================================*/
1550 /**************************************************************************
1551 * midMessage (WINEOSS.4)
1553 DWORD WINAPI OSS_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1554 DWORD dwParam1, DWORD dwParam2)
1556 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1557 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1559 #ifdef HAVE_OSS_MIDI
1563 /* FIXME: Pretend this is supported */
1566 return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1568 return midClose(wDevID);
1569 case MIDM_ADDBUFFER:
1570 return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1572 return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1573 case MIDM_UNPREPARE:
1574 return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1575 case MIDM_GETDEVCAPS:
1576 return midGetDevCaps(wDevID, (LPMIDIINCAPSA)dwParam1,dwParam2);
1577 case MIDM_GETNUMDEVS:
1578 return MIDM_NUMDEVS;
1580 return midReset(wDevID);
1582 return midStart(wDevID);
1584 return midStop(wDevID);
1587 TRACE("Unsupported message\n");
1589 return MMSYSERR_NOTSUPPORTED;
1592 /**************************************************************************
1593 * modMessage (WINEOSS.5)
1595 DWORD WINAPI OSS_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1596 DWORD dwParam1, DWORD dwParam2)
1598 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1599 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1602 #ifdef HAVE_OSS_MIDI
1607 /* FIXME: Pretend this is supported */
1610 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1612 return modClose(wDevID);
1614 return modData(wDevID, dwParam1);
1616 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1618 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1619 case MODM_UNPREPARE:
1620 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1621 case MODM_GETDEVCAPS:
1622 return modGetDevCaps(wDevID, (LPMIDIOUTCAPSA)dwParam1, dwParam2);
1623 case MODM_GETNUMDEVS:
1624 return MODM_NUMDEVS;
1625 case MODM_GETVOLUME:
1627 case MODM_SETVOLUME:
1630 return modReset(wDevID);
1633 TRACE("Unsupported message\n");
1635 return MMSYSERR_NOTSUPPORTED;
1638 /*-----------------------------------------------------------------------*/