widl: Generate helper macros for WinRT implementation.
[wine/zf.git] / dlls / wineoss.drv / midi.c
blob24b3e7037b084fba3174c820b472c86414e40e4a
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 split 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 /* TODO:
30 * + use better instrument definition for OPL/2 (midiPatch.c) or
31 * use existing instrument definition (from playmidi or kmid)
32 * with a .winerc option
33 * + have a look at OPL/3 ?
34 * + implement asynchronous playback of MidiHdr
35 * + implement STREAM'ed MidiHdr (question: how shall we share the
36 * code between the midiStream functions in MMSYSTEM/WINMM and
37 * the code for the low level driver)
38 * + use a more accurate read mechanism than the one of snooping on
39 * timers (like select on fd)
42 #include "config.h"
43 #include "wine/port.h"
45 #include <stdlib.h>
46 #include <string.h>
47 #include <stdarg.h>
48 #include <stdio.h>
49 #ifdef HAVE_UNISTD_H
50 # include <unistd.h>
51 #endif
52 #include <fcntl.h>
53 #include <errno.h>
54 #ifdef HAVE_SYS_IOCTL_H
55 # include <sys/ioctl.h>
56 #endif
57 #ifdef HAVE_POLL_H
58 #include <poll.h>
59 #endif
60 #ifdef HAVE_SYS_POLL_H
61 #include <sys/poll.h>
62 #endif
63 #include <sys/soundcard.h>
65 #include "windef.h"
66 #include "winbase.h"
67 #include "wingdi.h"
68 #include "winuser.h"
69 #include "winnls.h"
70 #include "mmddk.h"
71 #include "wine/unicode.h"
72 #include "wine/debug.h"
74 WINE_DEFAULT_DEBUG_CHANNEL(midi);
76 #ifdef SNDCTL_SEQ_NRMIDIS
78 typedef struct {
79 int state; /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
80 DWORD bufsize;
81 MIDIOPENDESC midiDesc;
82 WORD wFlags;
83 LPMIDIHDR lpQueueHdr;
84 DWORD dwTotalPlayed;
85 unsigned char incoming[3];
86 unsigned char incPrev;
87 char incLen;
88 DWORD startTime;
89 MIDIINCAPSW caps;
90 } WINE_MIDIIN;
92 typedef struct {
93 BOOL bEnabled;
94 DWORD bufsize;
95 MIDIOPENDESC midiDesc;
96 WORD wFlags;
97 LPMIDIHDR lpQueueHdr;
98 DWORD dwTotalPlayed;
99 void* lpExtra; /* according to port type (MIDI, FM...), extra data when needed */
100 MIDIOUTCAPSW caps;
101 } WINE_MIDIOUT;
103 static WINE_MIDIIN MidiInDev [MAX_MIDIINDRV ];
104 static WINE_MIDIOUT MidiOutDev[MAX_MIDIOUTDRV];
106 /* this is the total number of MIDI out devices found (synth and port) */
107 static int MODM_NumDevs = 0;
108 /* this is the number of FM synthesizers (index from 0 to NUMFMSYNTHDEVS - 1) */
109 static int MODM_NumFMSynthDevs = 0;
110 /* the Midi ports have index from NUMFMSYNTHDEVS to NumDevs - 1 */
112 /* this is the total number of MIDI out devices found */
113 static int MIDM_NumDevs = 0;
115 static int midiSeqFD = -1;
116 static int numOpenMidiSeq = 0;
117 static int numStartedMidiIn = 0;
119 static CRITICAL_SECTION crit_sect; /* protects all MidiIn buffers queues */
120 static CRITICAL_SECTION_DEBUG critsect_debug =
122 0, 0, &crit_sect,
123 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
124 0, 0, { (DWORD_PTR)(__FILE__ ": crit_sect") }
126 static CRITICAL_SECTION crit_sect = { &critsect_debug, -1, 0, 0, 0, 0 };
128 static int end_thread;
129 static HANDLE hThread;
131 /*======================================================================*
132 * Low level MIDI implementation *
133 *======================================================================*/
135 static int midiOpenSeq(void);
136 static int midiCloseSeq(void);
138 /**************************************************************************
139 * MIDI_unixToWindowsDeviceType [internal]
141 * return the Windows equivalent to a Unix Device Type
144 static int MIDI_UnixToWindowsDeviceType(int type)
146 /* MOD_MIDIPORT output port
147 * MOD_SYNTH generic internal synth
148 * MOD_SQSYNTH square wave internal synth
149 * MOD_FMSYNTH FM internal synth
150 * MOD_MAPPER MIDI mapper
151 * MOD_WAVETABLE hardware wavetable internal synth
152 * MOD_SWSYNTH software internal synth
155 /* FIXME Is this really the correct equivalence from UNIX to
156 Windows Sound type */
158 switch (type) {
159 case SYNTH_TYPE_FM: return MOD_FMSYNTH;
160 case SYNTH_TYPE_SAMPLE: return MOD_SYNTH;
161 case SYNTH_TYPE_MIDI: return MOD_MIDIPORT;
162 default:
163 ERR("Cannot determine the type of this midi device. "
164 "Assuming FM Synth\n");
165 return MOD_FMSYNTH;
169 static int MIDI_loadcount;
170 /**************************************************************************
171 * OSS_MidiInit [internal]
173 * Initializes the MIDI devices information variables
175 static LRESULT OSS_MidiInit(void)
177 int i, status, numsynthdevs = 255, nummididevs = 255;
178 struct synth_info sinfo;
179 struct midi_info minfo;
181 TRACE("(%i)\n", MIDI_loadcount);
182 if (MIDI_loadcount++)
183 return 1;
185 TRACE("Initializing the MIDI variables.\n");
187 /* try to open device */
188 if (midiOpenSeq() == -1) {
189 return -1;
192 /* find how many Synth devices are there in the system */
193 status = ioctl(midiSeqFD, SNDCTL_SEQ_NRSYNTHS, &numsynthdevs);
195 if (status == -1) {
196 ERR("ioctl for nr synth failed.\n");
197 midiCloseSeq();
198 return -1;
201 if (numsynthdevs > MAX_MIDIOUTDRV) {
202 ERR("MAX_MIDIOUTDRV (%d) was enough for the number of devices (%d). "
203 "Some FM devices will not be available.\n",MAX_MIDIOUTDRV,numsynthdevs);
204 numsynthdevs = MAX_MIDIOUTDRV;
207 for (i = 0; i < numsynthdevs; i++) {
208 /* Manufac ID. We do not have access to this with soundcard.h
209 * Does not seem to be a problem, because in mmsystem.h only
210 * Microsoft's ID is listed.
212 MidiOutDev[i].caps.wMid = 0x00FF;
213 MidiOutDev[i].caps.wPid = 0x0001; /* FIXME Product ID */
214 /* Product Version. We simply say "1" */
215 MidiOutDev[i].caps.vDriverVersion = 0x001;
216 /* The following are mandatory for MOD_MIDIPORT */
217 MidiOutDev[i].caps.wChannelMask = 0xFFFF;
218 MidiOutDev[i].caps.wVoices = 0;
219 MidiOutDev[i].caps.wNotes = 0;
220 MidiOutDev[i].caps.dwSupport = 0;
222 sinfo.device = i;
223 status = ioctl(midiSeqFD, SNDCTL_SYNTH_INFO, &sinfo);
224 if (status == -1) {
225 static const WCHAR fmt[] = {'W','i','n','e',' ','O','S','S',' ','M','i','d','i',' ','O','u','t',' ','#','%','d',' ','d','i','s','a','b','l','e','d',0};
226 ERR("ioctl for synth info failed on %d, disabling it.\n", i);
228 wsprintfW(MidiOutDev[i].caps.szPname, fmt, i);
230 MidiOutDev[i].caps.wTechnology = MOD_MIDIPORT;
231 MidiOutDev[i].bEnabled = FALSE;
232 } else {
233 MultiByteToWideChar(CP_UNIXCP, 0, sinfo.name, -1, MidiOutDev[i].caps.szPname,
234 ARRAY_SIZE(MidiOutDev[i].caps.szPname));
235 MidiOutDev[i].caps.wTechnology = MIDI_UnixToWindowsDeviceType(sinfo.synth_type);
237 if (MOD_MIDIPORT != MidiOutDev[i].caps.wTechnology) {
238 /* FIXME Do we have this information?
239 * Assuming the soundcards can handle
240 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
241 * not MIDICAPS_CACHE.
243 MidiOutDev[i].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
244 MidiOutDev[i].caps.wVoices = sinfo.nr_voices;
246 /* FIXME Is it possible to know the maximum
247 * number of simultaneous notes of a soundcard ?
248 * I believe we don't have this information, but
249 * it's probably equal or more than wVoices
251 MidiOutDev[i].caps.wNotes = sinfo.nr_voices;
253 MidiOutDev[i].bEnabled = TRUE;
255 /* We also have the information sinfo.synth_subtype, not used here
257 if (sinfo.capabilities & SYNTH_CAP_INPUT) {
258 FIXME("Synthesizer supports MIDI in. Not yet supported.\n");
260 TRACE("SynthOut[%d]\tOSS info: synth type=%d/%d capa=%lx\n",
261 i, sinfo.synth_type, sinfo.synth_subtype, (long)sinfo.capabilities);
264 TRACE("SynthOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n",
265 i, wine_dbgstr_w(MidiOutDev[i].caps.szPname),
266 MidiOutDev[i].caps.wTechnology,
267 MidiOutDev[i].caps.wVoices, MidiOutDev[i].caps.wNotes,
268 MidiOutDev[i].caps.wChannelMask, MidiOutDev[i].caps.dwSupport);
271 /* find how many MIDI devices are there in the system */
272 status = ioctl(midiSeqFD, SNDCTL_SEQ_NRMIDIS, &nummididevs);
273 if (status == -1) {
274 ERR("ioctl on nr midi failed.\n");
275 nummididevs = 0;
276 goto wrapup;
279 /* FIXME: the two restrictions below could be loosened in some cases */
280 if (numsynthdevs + nummididevs > MAX_MIDIOUTDRV) {
281 ERR("MAX_MIDIOUTDRV was not enough for the number of devices. "
282 "Some MIDI devices will not be available.\n");
283 nummididevs = MAX_MIDIOUTDRV - numsynthdevs;
286 if (nummididevs > MAX_MIDIINDRV) {
287 ERR("MAX_MIDIINDRV (%d) was not enough for the number of devices (%d). "
288 "Some MIDI devices will not be available.\n",MAX_MIDIINDRV,nummididevs);
289 nummididevs = MAX_MIDIINDRV;
292 for (i = 0; i < nummididevs; i++) {
293 minfo.device = i;
294 status = ioctl(midiSeqFD, SNDCTL_MIDI_INFO, &minfo);
295 if (status == -1) WARN("ioctl on midi info for device %d failed.\n", i);
297 /* This whole part is somewhat obscure to me. I'll keep trying to dig
298 info about it. If you happen to know, please tell us. The very
299 descriptive minfo.dev_type was not used here.
301 /* Manufacturer ID. We do not have access to this with soundcard.h
302 Does not seem to be a problem, because in mmsystem.h only
303 Microsoft's ID is listed */
304 MidiOutDev[numsynthdevs + i].caps.wMid = 0x00FF;
305 MidiOutDev[numsynthdevs + i].caps.wPid = 0x0001; /* FIXME Product ID */
306 /* Product Version. We simply say "1" */
307 MidiOutDev[numsynthdevs + i].caps.vDriverVersion = 0x001;
308 if (status == -1) {
309 static const WCHAR fmt[] = {'W','i','n','e',' ','O','S','S',' ','M','i','d','i',' ','O','u','t',' ','#','%','d',' ','d','i','s','a','b','l','e','d',0};
310 wsprintfW(MidiOutDev[numsynthdevs + i].caps.szPname, fmt, numsynthdevs + i);
311 MidiOutDev[numsynthdevs + i].bEnabled = FALSE;
312 } else {
313 MultiByteToWideChar(CP_UNIXCP, 0, minfo.name, -1,
314 MidiOutDev[numsynthdevs + i].caps.szPname,
315 ARRAY_SIZE(MidiOutDev[numsynthdevs + i].caps.szPname));
316 MidiOutDev[numsynthdevs + i].bEnabled = TRUE;
318 MidiOutDev[numsynthdevs + i].caps.wTechnology = MOD_MIDIPORT;
319 MidiOutDev[numsynthdevs + i].caps.wVoices = 0;
320 MidiOutDev[numsynthdevs + i].caps.wNotes = 0;
321 MidiOutDev[numsynthdevs + i].caps.wChannelMask= 0xFFFF;
322 MidiOutDev[numsynthdevs + i].caps.dwSupport = 0;
324 /* This whole part is somewhat obscure to me. I'll keep trying to dig
325 info about it. If you happen to know, please tell us. The very
326 descriptive minfo.dev_type was not used here.
328 /* Manufac ID. We do not have access to this with soundcard.h
329 Does not seem to be a problem, because in mmsystem.h only
330 Microsoft's ID is listed */
331 MidiInDev[i].caps.wMid = 0x00FF;
332 MidiInDev[i].caps.wPid = 0x0001; /* FIXME Product ID */
333 /* Product Version. We simply say "1" */
334 MidiInDev[i].caps.vDriverVersion = 0x001;
335 if (status == -1) {
336 static const WCHAR fmt[] = {'W','i','n','e',' ','O','S','S',' ','M','i','d','i',' ','I','n',' ','#','%','d',' ','d','i','s','a','b','l','e','d',0};
337 wsprintfW(MidiInDev[i].caps.szPname, fmt, numsynthdevs + i);
338 MidiInDev[i].state = -1;
339 } else {
340 MultiByteToWideChar(CP_UNIXCP, 0, minfo.name, -1, MidiInDev[i].caps.szPname,
341 ARRAY_SIZE(MidiInDev[i].caps.szPname));
342 MidiInDev[i].state = 0;
344 MidiInDev[i].caps.dwSupport = 0; /* mandatory with MIDIINCAPS */
346 TRACE("OSS info: midi[%d] dev-type=%d capa=%lx\n"
347 "\tMidiOut[%d] name='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
348 "\tMidiIn [%d] name='%s' support=%d\n",
349 i, minfo.dev_type, (long)minfo.capabilities,
350 numsynthdevs + i, wine_dbgstr_w(MidiOutDev[numsynthdevs + i].caps.szPname),
351 MidiOutDev[numsynthdevs + i].caps.wTechnology,
352 MidiOutDev[numsynthdevs + i].caps.wVoices, MidiOutDev[numsynthdevs + i].caps.wNotes,
353 MidiOutDev[numsynthdevs + i].caps.wChannelMask, MidiOutDev[numsynthdevs + i].caps.dwSupport,
354 i, wine_dbgstr_w(MidiInDev[i].caps.szPname), MidiInDev[i].caps.dwSupport);
357 wrapup:
358 /* windows does not seem to differentiate Synth from MIDI devices */
359 MODM_NumFMSynthDevs = numsynthdevs;
360 MODM_NumDevs = numsynthdevs + nummididevs;
362 MIDM_NumDevs = nummididevs;
364 /* close file and exit */
365 midiCloseSeq();
367 return 0;
370 /**************************************************************************
371 * OSS_MidiExit [internal]
373 * Release the MIDI devices information variables
375 static LRESULT OSS_MidiExit(void)
377 TRACE("(%i)\n", MIDI_loadcount);
379 if (--MIDI_loadcount)
380 return 1;
382 ZeroMemory(MidiInDev, sizeof(MidiInDev));
383 ZeroMemory(MidiOutDev, sizeof(MidiOutDev));
385 MODM_NumDevs = 0;
386 MODM_NumFMSynthDevs = 0;
387 MIDM_NumDevs = 0;
389 return 0;
392 /**************************************************************************
393 * MIDI_NotifyClient [internal]
395 static void MIDI_NotifyClient(UINT wDevID, WORD wMsg,
396 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
398 DWORD_PTR dwCallBack;
399 UINT uFlags;
400 HANDLE hDev;
401 DWORD_PTR dwInstance;
403 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
404 wDevID, wMsg, dwParam1, dwParam2);
406 switch (wMsg) {
407 case MOM_OPEN:
408 case MOM_CLOSE:
409 case MOM_DONE:
410 case MOM_POSITIONCB:
411 if (wDevID > MODM_NumDevs) return;
413 dwCallBack = MidiOutDev[wDevID].midiDesc.dwCallback;
414 uFlags = MidiOutDev[wDevID].wFlags;
415 hDev = MidiOutDev[wDevID].midiDesc.hMidi;
416 dwInstance = MidiOutDev[wDevID].midiDesc.dwInstance;
417 break;
419 case MIM_OPEN:
420 case MIM_CLOSE:
421 case MIM_DATA:
422 case MIM_LONGDATA:
423 case MIM_ERROR:
424 case MIM_LONGERROR:
425 case MIM_MOREDATA:
426 if (wDevID > MIDM_NumDevs) return;
428 dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
429 uFlags = MidiInDev[wDevID].wFlags;
430 hDev = MidiInDev[wDevID].midiDesc.hMidi;
431 dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
432 break;
433 default:
434 ERR("Unsupported MSW-MIDI message %u\n", wMsg);
435 return;
438 DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2);
441 static int midi_warn = 1;
442 /**************************************************************************
443 * midiOpenSeq [internal]
445 static int midiOpenSeq(void)
447 if (numOpenMidiSeq == 0) {
448 const char* device;
449 device=getenv("MIDIDEV");
450 if (!device) device="/dev/sequencer";
451 midiSeqFD = open(device, O_RDWR, 0);
452 if (midiSeqFD == -1) {
453 if (midi_warn)
455 WARN("Can't open MIDI device '%s' ! (%s). If your "
456 "program needs this (probably not): %s\n",
457 device, strerror(errno),
458 errno == ENOENT ?
459 "create it ! (\"man MAKEDEV\" ?)" :
460 errno == ENODEV ?
461 "load MIDI sequencer kernel driver !" :
462 errno == EACCES ?
463 "grant access ! (\"man chmod\")" : ""
466 midi_warn = 0;
467 return -1;
469 #if 0
470 if (fcntl(midiSeqFD, F_SETFL, O_NONBLOCK) < 0) {
471 WARN("can't set sequencer fd to non-blocking, errno %d (%s)\n", errno, strerror(errno));
472 close(midiSeqFD);
473 midiSeqFD = -1;
474 return -1;
476 #endif
477 fcntl(midiSeqFD, F_SETFD, 1); /* set close on exec flag */
478 ioctl(midiSeqFD, SNDCTL_SEQ_RESET);
480 numOpenMidiSeq++;
481 return 0;
484 /**************************************************************************
485 * midiCloseSeq [internal]
487 static int midiCloseSeq(void)
489 if (--numOpenMidiSeq == 0) {
490 close(midiSeqFD);
491 midiSeqFD = -1;
493 return 0;
496 /* FIXME: this is a bad idea, it's even not static... */
497 SEQ_DEFINEBUF(1024);
499 /* FIXME: this is not reentrant, not static - because of global variable
500 * _seqbuf and al.
502 /**************************************************************************
503 * seqbuf_dump [internal]
505 * Used by SEQ_DUMPBUF to flush the buffer.
508 void seqbuf_dump(void)
510 if (_seqbufptr) {
511 if (write(midiSeqFD, _seqbuf, _seqbufptr) == -1) {
512 WARN("Can't write data to sequencer %d, errno %d (%s)!\n",
513 midiSeqFD, errno, strerror(errno));
515 /* FIXME:
516 * in any case buffer is lost so that if many errors occur the buffer
517 * will not overrun
519 _seqbufptr = 0;
523 /**************************************************************************
524 * midReceiveChar [internal]
526 static void midReceiveChar(WORD wDevID, unsigned char value, DWORD dwTime)
528 DWORD toSend = 0;
530 TRACE("Adding %02xh to %d[%d]\n", value, wDevID, MidiInDev[wDevID].incLen);
532 if (wDevID >= MIDM_NumDevs) {
533 WARN("bad devID\n");
534 return;
536 if (MidiInDev[wDevID].state <= 0) {
537 TRACE("disabled or input not started, thrown away\n");
538 return;
541 if (MidiInDev[wDevID].state & 2) { /* system exclusive */
542 LPMIDIHDR lpMidiHdr;
543 BOOL sbfb = FALSE;
545 EnterCriticalSection(&crit_sect);
546 if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
547 LPBYTE lpData = (LPBYTE) lpMidiHdr->lpData;
549 lpData[lpMidiHdr->dwBytesRecorded++] = value;
550 if (lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) {
551 sbfb = TRUE;
554 if (value == 0xF7) { /* then end */
555 MidiInDev[wDevID].state &= ~2;
556 sbfb = TRUE;
558 if (sbfb && lpMidiHdr != NULL) {
559 lpMidiHdr = MidiInDev[wDevID].lpQueueHdr;
560 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr->lpNext;
561 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
562 lpMidiHdr->dwFlags |= MHDR_DONE;
563 MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime);
565 LeaveCriticalSection(&crit_sect);
566 return;
569 #define IS_CMD(_x) (((_x) & 0x80) == 0x80)
570 #define IS_SYS_CMD(_x) (((_x) & 0xF0) == 0xF0)
572 if (!IS_CMD(value) && MidiInDev[wDevID].incLen == 0) { /* try to reuse old cmd */
573 if (IS_CMD(MidiInDev[wDevID].incPrev) && !IS_SYS_CMD(MidiInDev[wDevID].incPrev)) {
574 MidiInDev[wDevID].incoming[0] = MidiInDev[wDevID].incPrev;
575 MidiInDev[wDevID].incLen = 1;
576 TRACE("Reusing old command %02xh\n", MidiInDev[wDevID].incPrev);
577 } else {
578 FIXME("error for midi-in, should generate MIM_ERROR notification:"
579 " prev=%02Xh, incLen=%02Xh\n",
580 MidiInDev[wDevID].incPrev, MidiInDev[wDevID].incLen);
581 return;
584 MidiInDev[wDevID].incoming[(int)(MidiInDev[wDevID].incLen++)] = value;
585 if (MidiInDev[wDevID].incLen == 1 && !IS_SYS_CMD(MidiInDev[wDevID].incoming[0])) {
586 /* store new cmd, just in case */
587 MidiInDev[wDevID].incPrev = MidiInDev[wDevID].incoming[0];
590 #undef IS_CMD
591 #undef IS_SYS_CMD
593 switch (MidiInDev[wDevID].incoming[0] & 0xF0) {
594 case MIDI_NOTEOFF:
595 case MIDI_NOTEON:
596 case MIDI_KEY_PRESSURE:
597 case MIDI_CTL_CHANGE:
598 case MIDI_PITCH_BEND:
599 if (MidiInDev[wDevID].incLen == 3) {
600 toSend = (MidiInDev[wDevID].incoming[2] << 16) |
601 (MidiInDev[wDevID].incoming[1] << 8) |
602 (MidiInDev[wDevID].incoming[0] << 0);
604 break;
605 case MIDI_PGM_CHANGE:
606 case MIDI_CHN_PRESSURE:
607 if (MidiInDev[wDevID].incLen == 2) {
608 toSend = (MidiInDev[wDevID].incoming[1] << 8) |
609 (MidiInDev[wDevID].incoming[0] << 0);
611 break;
612 case MIDI_SYSTEM_PREFIX:
613 if (MidiInDev[wDevID].incoming[0] == 0xF0) {
614 MidiInDev[wDevID].state |= 2;
615 MidiInDev[wDevID].incLen = 0;
616 } else {
617 if (MidiInDev[wDevID].incLen == 1) {
618 toSend = (MidiInDev[wDevID].incoming[0] << 0);
621 break;
622 default:
623 WARN("This shouldn't happen (%02X)\n", MidiInDev[wDevID].incoming[0]);
625 if (toSend != 0) {
626 TRACE("Sending event %08x\n", toSend);
627 MidiInDev[wDevID].incLen = 0;
628 dwTime -= MidiInDev[wDevID].startTime;
629 MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime);
633 static DWORD WINAPI midRecThread(LPVOID arg)
635 unsigned char buffer[256];
636 int len, idx;
637 DWORD dwTime;
638 struct pollfd pfd;
640 TRACE("Thread startup\n");
642 pfd.fd = midiSeqFD;
643 pfd.events = POLLIN;
645 while(!end_thread) {
646 TRACE("Thread loop\n");
648 /* Check if an event is present */
649 if (poll(&pfd, 1, 250) <= 0)
650 continue;
652 len = read(midiSeqFD, buffer, sizeof(buffer));
653 TRACE("Received %d bytes\n", len);
655 if (len < 0) continue;
656 if ((len % 4) != 0) {
657 WARN("Bad length %d, errno %d (%s)\n", len, errno, strerror(errno));
658 continue;
661 dwTime = GetTickCount();
663 for (idx = 0; idx < len; ) {
664 if (buffer[idx] & 0x80) {
665 TRACE(
666 "Reading<8> %02x %02x %02x %02x %02x %02x %02x %02x\n",
667 buffer[idx + 0], buffer[idx + 1],
668 buffer[idx + 2], buffer[idx + 3],
669 buffer[idx + 4], buffer[idx + 5],
670 buffer[idx + 6], buffer[idx + 7]);
671 idx += 8;
672 } else {
673 switch (buffer[idx + 0]) {
674 case SEQ_WAIT:
675 case SEQ_ECHO:
676 break;
677 case SEQ_MIDIPUTC:
678 midReceiveChar(buffer[idx + 2], buffer[idx + 1], dwTime);
679 break;
680 default:
681 TRACE("Unsupported event %d\n", buffer[idx + 0]);
682 break;
684 idx += 4;
688 return 0;
691 /**************************************************************************
692 * midGetDevCaps [internal]
694 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
696 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
698 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
699 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
701 memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
703 return MMSYSERR_NOERROR;
706 /**************************************************************************
707 * midOpen [internal]
709 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
711 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
713 if (lpDesc == NULL) {
714 WARN("Invalid Parameter !\n");
715 return MMSYSERR_INVALPARAM;
718 /* FIXME :
719 * how to check that content of lpDesc is correct ?
721 if (wDevID >= MIDM_NumDevs) {
722 WARN("wDevID too large (%u) !\n", wDevID);
723 return MMSYSERR_BADDEVICEID;
725 if (MidiInDev[wDevID].state == -1) {
726 WARN("device disabled\n");
727 return MIDIERR_NODEVICE;
729 if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
730 WARN("device already open !\n");
731 return MMSYSERR_ALLOCATED;
733 if ((dwFlags & MIDI_IO_STATUS) != 0) {
734 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
735 dwFlags &= ~MIDI_IO_STATUS;
737 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
738 FIXME("Bad dwFlags\n");
739 return MMSYSERR_INVALFLAG;
742 if (midiOpenSeq() < 0) {
743 return MMSYSERR_ERROR;
746 if (numStartedMidiIn++ == 0) {
747 end_thread = 0;
748 hThread = CreateThread(NULL, 0, midRecThread, NULL, 0, NULL);
749 if (!hThread) {
750 numStartedMidiIn = 0;
751 WARN("Couldn't create thread for midi-in\n");
752 midiCloseSeq();
753 return MMSYSERR_ERROR;
755 SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
756 TRACE("Created thread for midi-in\n");
759 MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
761 MidiInDev[wDevID].lpQueueHdr = NULL;
762 MidiInDev[wDevID].dwTotalPlayed = 0;
763 MidiInDev[wDevID].bufsize = 0x3FFF;
764 MidiInDev[wDevID].midiDesc = *lpDesc;
765 MidiInDev[wDevID].state = 0;
766 MidiInDev[wDevID].incLen = 0;
767 MidiInDev[wDevID].startTime = 0;
769 MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L);
770 return MMSYSERR_NOERROR;
773 /**************************************************************************
774 * midClose [internal]
776 static DWORD midClose(WORD wDevID)
778 int ret = MMSYSERR_NOERROR;
780 TRACE("(%04X);\n", wDevID);
782 if (wDevID >= MIDM_NumDevs) {
783 WARN("wDevID too big (%u) !\n", wDevID);
784 return MMSYSERR_BADDEVICEID;
786 if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
787 WARN("device not opened !\n");
788 return MMSYSERR_ERROR;
790 if (MidiInDev[wDevID].lpQueueHdr != 0) {
791 return MIDIERR_STILLPLAYING;
794 if (midiSeqFD == -1) {
795 WARN("ooops !\n");
796 return MMSYSERR_ERROR;
798 if (--numStartedMidiIn == 0) {
799 TRACE("Stopping thread for midi-in\n");
800 end_thread = 1;
801 if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
802 WARN("Thread end not signaled, force termination\n");
803 TerminateThread(hThread, 0);
805 TRACE("Stopped thread for midi-in\n");
807 midiCloseSeq();
809 MidiInDev[wDevID].bufsize = 0;
810 MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L);
811 MidiInDev[wDevID].midiDesc.hMidi = 0;
812 return ret;
815 /**************************************************************************
816 * midAddBuffer [internal]
818 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
820 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
822 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
823 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
825 if (lpMidiHdr == NULL) return MMSYSERR_INVALPARAM;
826 if (dwSize < offsetof(MIDIHDR,dwOffset)) return MMSYSERR_INVALPARAM;
827 if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
828 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
829 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
831 EnterCriticalSection(&crit_sect);
832 lpMidiHdr->dwFlags &= ~WHDR_DONE;
833 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
834 lpMidiHdr->dwBytesRecorded = 0;
835 lpMidiHdr->lpNext = 0;
836 if (MidiInDev[wDevID].lpQueueHdr == 0) {
837 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
838 } else {
839 LPMIDIHDR ptr;
841 for (ptr = MidiInDev[wDevID].lpQueueHdr;
842 ptr->lpNext != 0;
843 ptr = ptr->lpNext);
844 ptr->lpNext = lpMidiHdr;
846 LeaveCriticalSection(&crit_sect);
848 return MMSYSERR_NOERROR;
851 /**************************************************************************
852 * midPrepare [internal]
854 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
856 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
858 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
859 return MMSYSERR_INVALPARAM;
860 if (lpMidiHdr->dwFlags & MHDR_PREPARED)
861 return MMSYSERR_NOERROR;
863 lpMidiHdr->lpNext = 0;
864 lpMidiHdr->dwFlags |= MHDR_PREPARED;
865 lpMidiHdr->dwFlags &= ~(MHDR_DONE|MHDR_INQUEUE); /* flags cleared since w2k */
867 return MMSYSERR_NOERROR;
870 /**************************************************************************
871 * midUnprepare [internal]
873 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
875 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
877 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
878 return MMSYSERR_INVALPARAM;
879 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
880 return MMSYSERR_NOERROR;
881 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
882 return MIDIERR_STILLPLAYING;
884 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
886 return MMSYSERR_NOERROR;
889 /**************************************************************************
890 * midReset [internal]
892 static DWORD midReset(WORD wDevID)
894 DWORD dwTime = GetTickCount();
896 TRACE("(%04X);\n", wDevID);
898 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
899 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
901 EnterCriticalSection(&crit_sect);
902 while (MidiInDev[wDevID].lpQueueHdr) {
903 LPMIDIHDR lpMidiHdr = MidiInDev[wDevID].lpQueueHdr;
904 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr->lpNext;
905 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
906 lpMidiHdr->dwFlags |= MHDR_DONE;
907 MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime);
909 LeaveCriticalSection(&crit_sect);
911 return MMSYSERR_NOERROR;
914 /**************************************************************************
915 * midStart [internal]
917 static DWORD midStart(WORD wDevID)
919 TRACE("(%04X);\n", wDevID);
921 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
922 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
924 MidiInDev[wDevID].state = 1;
925 MidiInDev[wDevID].startTime = GetTickCount();
926 return MMSYSERR_NOERROR;
929 /**************************************************************************
930 * midStop [internal]
932 static DWORD midStop(WORD wDevID)
934 TRACE("(%04X);\n", wDevID);
936 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
937 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
939 MidiInDev[wDevID].state = 0;
940 return MMSYSERR_NOERROR;
943 /*-----------------------------------------------------------------------*/
945 typedef struct sVoice {
946 int note; /* 0 means not used */
947 int channel;
948 unsigned cntMark : 30,
949 status : 2;
950 #define sVS_UNUSED 0
951 #define sVS_PLAYING 1
952 #define sVS_SUSTAINED 2
953 } sVoice;
955 typedef struct sChannel {
956 int program;
958 int bender;
959 int benderRange;
960 /* controllers */
961 int bank; /* CTL_BANK_SELECT */
962 int volume; /* CTL_MAIN_VOLUME */
963 int balance; /* CTL_BALANCE */
964 int expression; /* CTL_EXPRESSION */
965 int sustain; /* CTL_SUSTAIN */
967 unsigned char nrgPmtMSB; /* Non register Parameters */
968 unsigned char nrgPmtLSB;
969 unsigned char regPmtMSB; /* Non register Parameters */
970 unsigned char regPmtLSB;
971 } sChannel;
973 typedef struct sFMextra {
974 unsigned counter;
975 int drumSetMask;
976 sChannel channel[16]; /* MIDI has only 16 channels */
977 sVoice voice[1]; /* dyn allocated according to sound card */
978 /* do not append fields below voice[1] since the size of this structure
979 * depends on the number of available voices on the FM synth...
981 } sFMextra;
983 extern const unsigned char midiFMInstrumentPatches[16 * 128];
984 extern const unsigned char midiFMDrumsPatches [16 * 128];
986 /**************************************************************************
987 * modFMLoad [internal]
989 static int modFMLoad(int dev)
991 int i;
992 struct sbi_instrument sbi;
994 sbi.device = dev;
995 sbi.key = FM_PATCH;
997 memset(sbi.operators + 16, 0, 16);
998 for (i = 0; i < 128; i++) {
999 sbi.channel = i;
1000 memcpy(sbi.operators, midiFMInstrumentPatches + i * 16, 16);
1002 if (write(midiSeqFD, &sbi, sizeof(sbi)) == -1) {
1003 WARN("Couldn't write patch for instrument %d, errno %d (%s)!\n", sbi.channel, errno, strerror(errno));
1004 return -1;
1007 for (i = 0; i < 128; i++) {
1008 sbi.channel = 128 + i;
1009 memcpy(sbi.operators, midiFMDrumsPatches + i * 16, 16);
1011 if (write(midiSeqFD, &sbi, sizeof(sbi)) == -1) {
1012 WARN("Couldn't write patch for drum %d, errno %d (%s)!\n", sbi.channel, errno, strerror(errno));
1013 return -1;
1016 return 0;
1019 /**************************************************************************
1020 * modFMReset [internal]
1022 static void modFMReset(WORD wDevID)
1024 sFMextra* extra = MidiOutDev[wDevID].lpExtra;
1025 sVoice* voice = extra->voice;
1026 sChannel* channel = extra->channel;
1027 int i;
1029 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1030 if (voice[i].status != sVS_UNUSED) {
1031 SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1033 SEQ_KEY_PRESSURE(wDevID, i, 127, 0);
1034 SEQ_CONTROL(wDevID, i, SEQ_VOLMODE, VOL_METHOD_LINEAR);
1035 voice[i].note = 0;
1036 voice[i].channel = -1;
1037 voice[i].cntMark = 0;
1038 voice[i].status = sVS_UNUSED;
1040 for (i = 0; i < 16; i++) {
1041 channel[i].program = 0;
1042 channel[i].bender = 8192;
1043 channel[i].benderRange = 2;
1044 channel[i].bank = 0;
1045 channel[i].volume = 127;
1046 channel[i].balance = 64;
1047 channel[i].expression = 0;
1048 channel[i].sustain = 0;
1050 extra->counter = 0;
1051 extra->drumSetMask = 1 << 9; /* channel 10 is normally drums, sometimes 16 also */
1052 SEQ_DUMPBUF();
1055 #define IS_DRUM_CHANNEL(_xtra, _chn) ((_xtra)->drumSetMask & (1 << (_chn)))
1057 /**************************************************************************
1058 * modGetDevCaps [internal]
1060 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
1062 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
1064 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1065 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
1067 memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1069 return MMSYSERR_NOERROR;
1072 /**************************************************************************
1073 * modOpen [internal]
1075 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
1077 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
1078 if (lpDesc == NULL) {
1079 WARN("Invalid Parameter !\n");
1080 return MMSYSERR_INVALPARAM;
1082 if (wDevID >= MODM_NumDevs) {
1083 TRACE("MAX_MIDIOUTDRV reached !\n");
1084 return MMSYSERR_BADDEVICEID;
1086 if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
1087 WARN("device already open !\n");
1088 return MMSYSERR_ALLOCATED;
1090 if (!MidiOutDev[wDevID].bEnabled) {
1091 WARN("device disabled !\n");
1092 return MIDIERR_NODEVICE;
1094 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
1095 WARN("bad dwFlags\n");
1096 return MMSYSERR_INVALFLAG;
1099 MidiOutDev[wDevID].lpExtra = 0;
1101 switch (MidiOutDev[wDevID].caps.wTechnology) {
1102 case MOD_FMSYNTH:
1104 void* extra;
1106 extra = HeapAlloc(GetProcessHeap(), 0,
1107 offsetof(struct sFMextra, voice[MidiOutDev[wDevID].caps.wVoices]));
1109 if (extra == 0) {
1110 WARN("can't alloc extra data !\n");
1111 return MMSYSERR_NOMEM;
1113 MidiOutDev[wDevID].lpExtra = extra;
1114 if (midiOpenSeq() < 0) {
1115 MidiOutDev[wDevID].lpExtra = 0;
1116 HeapFree(GetProcessHeap(), 0, extra);
1117 return MMSYSERR_ERROR;
1119 if (modFMLoad(wDevID) < 0) {
1120 midiCloseSeq();
1121 MidiOutDev[wDevID].lpExtra = 0;
1122 HeapFree(GetProcessHeap(), 0, extra);
1123 return MMSYSERR_ERROR;
1125 modFMReset(wDevID);
1127 break;
1128 case MOD_MIDIPORT:
1129 case MOD_SYNTH:
1130 if (midiOpenSeq() < 0) {
1131 return MMSYSERR_ALLOCATED;
1133 break;
1134 default:
1135 WARN("Technology not supported (yet) %d !\n",
1136 MidiOutDev[wDevID].caps.wTechnology);
1137 return MMSYSERR_NOTENABLED;
1140 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1142 MidiOutDev[wDevID].lpQueueHdr = NULL;
1143 MidiOutDev[wDevID].dwTotalPlayed = 0;
1144 MidiOutDev[wDevID].bufsize = 0x3FFF;
1145 MidiOutDev[wDevID].midiDesc = *lpDesc;
1147 MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L);
1148 TRACE("Successful !\n");
1149 return MMSYSERR_NOERROR;
1153 /**************************************************************************
1154 * modClose [internal]
1156 static DWORD modClose(WORD wDevID)
1158 int ret = MMSYSERR_NOERROR;
1160 TRACE("(%04X);\n", wDevID);
1162 if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
1163 WARN("device not opened !\n");
1164 return MMSYSERR_ERROR;
1166 /* FIXME: should test that no pending buffer is still in the queue for
1167 * playing */
1169 if (midiSeqFD == -1) {
1170 WARN("can't close !\n");
1171 return MMSYSERR_ERROR;
1174 switch (MidiOutDev[wDevID].caps.wTechnology) {
1175 case MOD_FMSYNTH:
1176 case MOD_MIDIPORT:
1177 midiCloseSeq();
1178 break;
1179 default:
1180 WARN("Technology not supported (yet) %d !\n",
1181 MidiOutDev[wDevID].caps.wTechnology);
1182 return MMSYSERR_NOTENABLED;
1185 HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
1186 MidiOutDev[wDevID].lpExtra = 0;
1188 MidiOutDev[wDevID].bufsize = 0;
1189 MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L);
1190 MidiOutDev[wDevID].midiDesc.hMidi = 0;
1191 return ret;
1194 /**************************************************************************
1195 * modData [internal]
1197 static DWORD modData(WORD wDevID, DWORD dwParam)
1199 WORD evt = LOBYTE(LOWORD(dwParam));
1200 WORD d1 = HIBYTE(LOWORD(dwParam));
1201 WORD d2 = LOBYTE(HIWORD(dwParam));
1203 TRACE("(%04X, %08X);\n", wDevID, dwParam);
1205 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1206 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1208 if (midiSeqFD == -1) {
1209 WARN("can't play !\n");
1210 return MIDIERR_NODEVICE;
1212 switch (MidiOutDev[wDevID].caps.wTechnology) {
1213 case MOD_FMSYNTH:
1214 /* FIXME:
1215 * - chorus depth controller is not used
1218 sFMextra* extra = MidiOutDev[wDevID].lpExtra;
1219 sVoice* voice = extra->voice;
1220 sChannel* channel = extra->channel;
1221 int chn = (evt & 0x0F);
1222 int i, nv;
1224 switch (evt & 0xF0) {
1225 case MIDI_NOTEOFF:
1226 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1227 /* don't stop sustained notes */
1228 if (voice[i].status == sVS_PLAYING && voice[i].channel == chn && voice[i].note == d1) {
1229 voice[i].status = sVS_UNUSED;
1230 SEQ_STOP_NOTE(wDevID, i, d1, d2);
1233 break;
1234 case MIDI_NOTEON:
1235 if (d2 == 0) { /* note off if velocity == 0 */
1236 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1237 /* don't stop sustained notes */
1238 if (voice[i].status == sVS_PLAYING && voice[i].channel == chn && voice[i].note == d1) {
1239 voice[i].status = sVS_UNUSED;
1240 SEQ_STOP_NOTE(wDevID, i, d1, 64);
1243 break;
1245 /* finding out in this order :
1246 * - an empty voice
1247 * - if replaying the same note on the same channel
1248 * - the older voice (LRU)
1250 for (i = nv = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1251 if (voice[i].status == sVS_UNUSED ||
1252 (voice[i].note == d1 && voice[i].channel == chn)) {
1253 nv = i;
1254 break;
1256 if (voice[i].cntMark < voice[0].cntMark) {
1257 nv = i;
1260 TRACE(
1261 "playing on voice=%d, pgm=%d, pan=0x%02X, vol=0x%02X, "
1262 "bender=0x%02X, note=0x%02X, vel=0x%02X\n",
1263 nv, channel[chn].program,
1264 channel[chn].balance,
1265 channel[chn].volume,
1266 channel[chn].bender, d1, d2);
1268 SEQ_SET_PATCH(wDevID, nv, IS_DRUM_CHANNEL(extra, chn) ?
1269 (128 + d1) : channel[chn].program);
1270 SEQ_BENDER_RANGE(wDevID, nv, channel[chn].benderRange * 100);
1271 SEQ_BENDER(wDevID, nv, channel[chn].bender);
1272 SEQ_CONTROL(wDevID, nv, CTL_PAN, channel[chn].balance);
1273 SEQ_CONTROL(wDevID, nv, CTL_EXPRESSION, channel[chn].expression);
1274 #if 0
1275 /* FIXME: does not really seem to work on my SB card and
1276 * screws everything up... so lay it down
1278 SEQ_CONTROL(wDevID, nv, CTL_MAIN_VOLUME, channel[chn].volume);
1279 #endif
1280 SEQ_START_NOTE(wDevID, nv, d1, d2);
1281 voice[nv].status = channel[chn].sustain ? sVS_SUSTAINED : sVS_PLAYING;
1282 voice[nv].note = d1;
1283 voice[nv].channel = chn;
1284 voice[nv].cntMark = extra->counter++;
1285 break;
1286 case MIDI_KEY_PRESSURE:
1287 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1288 if (voice[i].status != sVS_UNUSED && voice[i].channel == chn && voice[i].note == d1) {
1289 SEQ_KEY_PRESSURE(wDevID, i, d1, d2);
1292 break;
1293 case MIDI_CTL_CHANGE:
1294 switch (d1) {
1295 case CTL_BANK_SELECT: channel[chn].bank = d2; break;
1296 case CTL_MAIN_VOLUME: channel[chn].volume = d2; break;
1297 case CTL_PAN: channel[chn].balance = d2; break;
1298 case CTL_EXPRESSION: channel[chn].expression = d2; break;
1299 case CTL_SUSTAIN: channel[chn].sustain = d2;
1300 if (d2) {
1301 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1302 if (voice[i].status == sVS_PLAYING && voice[i].channel == chn) {
1303 voice[i].status = sVS_SUSTAINED;
1306 } else {
1307 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1308 if (voice[i].status == sVS_SUSTAINED && voice[i].channel == chn) {
1309 voice[i].status = sVS_UNUSED;
1310 SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1314 break;
1315 case CTL_NONREG_PARM_NUM_LSB: channel[chn].nrgPmtLSB = d2; break;
1316 case CTL_NONREG_PARM_NUM_MSB: channel[chn].nrgPmtMSB = d2; break;
1317 case CTL_REGIST_PARM_NUM_LSB: channel[chn].regPmtLSB = d2; break;
1318 case CTL_REGIST_PARM_NUM_MSB: channel[chn].regPmtMSB = d2; break;
1319 case CTL_DATA_ENTRY:
1320 switch ((channel[chn].regPmtMSB << 8) | channel[chn].regPmtLSB) {
1321 case 0x0000:
1322 if (channel[chn].benderRange != d2) {
1323 channel[chn].benderRange = d2;
1324 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1325 if (voice[i].channel == chn) {
1326 SEQ_BENDER_RANGE(wDevID, i, channel[chn].benderRange);
1330 break;
1332 case 0x7F7F:
1333 channel[chn].benderRange = 2;
1334 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1335 if (voice[i].channel == chn) {
1336 SEQ_BENDER_RANGE(wDevID, i, channel[chn].benderRange);
1339 break;
1340 default:
1341 TRACE("Data entry: regPmt=0x%02x%02x, nrgPmt=0x%02x%02x with %x\n",
1342 channel[chn].regPmtMSB, channel[chn].regPmtLSB,
1343 channel[chn].nrgPmtMSB, channel[chn].nrgPmtLSB,
1344 d2);
1345 break;
1347 break;
1349 case 0x78: /* all sounds off */
1350 /* FIXME: I don't know if I have to take care of the channel
1351 * for this control ?
1353 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1354 if (voice[i].status != sVS_UNUSED && voice[i].channel == chn) {
1355 voice[i].status = sVS_UNUSED;
1356 SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1359 break;
1360 case 0x7B: /* all notes off */
1361 /* FIXME: I don't know if I have to take care of the channel
1362 * for this control ?
1364 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1365 if (voice[i].status == sVS_PLAYING && voice[i].channel == chn) {
1366 voice[i].status = sVS_UNUSED;
1367 SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1370 break;
1371 default:
1372 TRACE("Dropping MIDI control event 0x%02x(%02x) on channel %d\n",
1373 d1, d2, chn);
1374 break;
1376 break;
1377 case MIDI_PGM_CHANGE:
1378 channel[chn].program = d1;
1379 break;
1380 case MIDI_CHN_PRESSURE:
1381 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1382 if (voice[i].status != sVS_UNUSED && voice[i].channel == chn) {
1383 SEQ_KEY_PRESSURE(wDevID, i, voice[i].note, d1);
1386 break;
1387 case MIDI_PITCH_BEND:
1388 channel[chn].bender = (d2 << 7) + d1;
1389 for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
1390 if (voice[i].channel == chn) {
1391 SEQ_BENDER(wDevID, i, channel[chn].bender);
1394 break;
1395 case MIDI_SYSTEM_PREFIX:
1396 switch (evt & 0x0F) {
1397 case 0x0F: /* Reset */
1398 modFMReset(wDevID);
1399 break;
1400 default:
1401 WARN("Unsupported (yet) system event %02x\n", evt & 0x0F);
1403 break;
1404 default:
1405 WARN("Internal error, shouldn't happen (event=%08x)\n", evt & 0xF0);
1406 return MMSYSERR_NOTENABLED;
1409 break;
1410 case MOD_MIDIPORT:
1412 int dev = wDevID - MODM_NumFMSynthDevs;
1413 if (dev < 0) {
1414 WARN("Internal error on devID (%u) !\n", wDevID);
1415 return MIDIERR_NODEVICE;
1418 switch (evt & 0xF0) {
1419 case MIDI_NOTEOFF:
1420 case MIDI_NOTEON:
1421 case MIDI_KEY_PRESSURE:
1422 case MIDI_CTL_CHANGE:
1423 case MIDI_PITCH_BEND:
1424 SEQ_MIDIOUT(dev, evt);
1425 SEQ_MIDIOUT(dev, d1);
1426 SEQ_MIDIOUT(dev, d2);
1427 break;
1428 case MIDI_PGM_CHANGE:
1429 case MIDI_CHN_PRESSURE:
1430 SEQ_MIDIOUT(dev, evt);
1431 SEQ_MIDIOUT(dev, d1);
1432 break;
1433 case MIDI_SYSTEM_PREFIX:
1434 switch (evt & 0x0F) {
1435 case 0x00: /* System Exclusive, don't do it on modData,
1436 * should require modLongData*/
1437 case 0x04: /* Undefined. */
1438 case 0x05: /* Undefined. */
1439 case 0x07: /* End of Exclusive. */
1440 case 0x09: /* Undefined. */
1441 case 0x0D: /* Undefined. */
1442 break;
1443 case 0x06: /* Tune Request */
1444 case 0x08: /* Timing Clock. */
1445 case 0x0A: /* Start. */
1446 case 0x0B: /* Continue */
1447 case 0x0C: /* Stop */
1448 case 0x0E: /* Active Sensing. */
1449 SEQ_MIDIOUT(dev, evt);
1450 break;
1451 case 0x0F: /* Reset */
1452 /* SEQ_MIDIOUT(dev, evt);
1453 this other way may be better */
1454 SEQ_MIDIOUT(dev, MIDI_SYSTEM_PREFIX);
1455 SEQ_MIDIOUT(dev, 0x7e);
1456 SEQ_MIDIOUT(dev, 0x7f);
1457 SEQ_MIDIOUT(dev, 0x09);
1458 SEQ_MIDIOUT(dev, 0x01);
1459 SEQ_MIDIOUT(dev, 0xf7);
1460 break;
1461 case 0x01: /* MTC Quarter frame */
1462 case 0x03: /* Song Select. */
1463 SEQ_MIDIOUT(dev, evt);
1464 SEQ_MIDIOUT(dev, d1);
1465 case 0x02: /* Song Position Pointer. */
1466 SEQ_MIDIOUT(dev, evt);
1467 SEQ_MIDIOUT(dev, d1);
1468 SEQ_MIDIOUT(dev, d2);
1470 break;
1473 break;
1474 default:
1475 WARN("Technology not supported (yet) %d !\n",
1476 MidiOutDev[wDevID].caps.wTechnology);
1477 return MMSYSERR_NOTENABLED;
1480 SEQ_DUMPBUF();
1482 return MMSYSERR_NOERROR;
1485 /**************************************************************************
1486 * modLongData [internal]
1488 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1490 int count;
1491 LPBYTE lpData;
1493 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1495 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
1496 * but it seems to be used only for midi input.
1497 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
1500 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1501 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1503 if (midiSeqFD == -1) {
1504 WARN("can't play !\n");
1505 return MIDIERR_NODEVICE;
1508 lpData = (LPBYTE) lpMidiHdr->lpData;
1510 if (lpData == NULL)
1511 return MIDIERR_UNPREPARED;
1512 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
1513 return MIDIERR_UNPREPARED;
1514 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1515 return MIDIERR_STILLPLAYING;
1516 lpMidiHdr->dwFlags &= ~MHDR_DONE;
1517 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
1519 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
1520 * data, or can it also contain raw MIDI data, to be split up and sent to
1521 * modShortData() ?
1522 * If the latter is true, then the following WARNing will fire up
1524 if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
1525 WARN("The allegedly system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
1528 TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
1529 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
1530 lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
1531 lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
1533 switch (MidiOutDev[wDevID].caps.wTechnology) {
1534 case MOD_FMSYNTH:
1535 /* FIXME: I don't think there is much to do here */
1536 break;
1537 case MOD_MIDIPORT:
1538 if (lpData[0] != 0xF0) {
1539 /* Send end of System Exclusive */
1540 SEQ_MIDIOUT(wDevID - MODM_NumFMSynthDevs, 0xF0);
1541 WARN("Adding missing 0xF0 marker at the beginning of "
1542 "system exclusive byte stream\n");
1544 for (count = 0; count < lpMidiHdr->dwBufferLength; count++) {
1545 SEQ_MIDIOUT(wDevID - MODM_NumFMSynthDevs, lpData[count]);
1547 if (lpData[count - 1] != 0xF7) {
1548 /* Send end of System Exclusive */
1549 SEQ_MIDIOUT(wDevID - MODM_NumFMSynthDevs, 0xF7);
1550 WARN("Adding missing 0xF7 marker at the end of "
1551 "system exclusive byte stream\n");
1553 SEQ_DUMPBUF();
1554 break;
1555 default:
1556 WARN("Technology not supported (yet) %d !\n",
1557 MidiOutDev[wDevID].caps.wTechnology);
1558 return MMSYSERR_NOTENABLED;
1561 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1562 lpMidiHdr->dwFlags |= MHDR_DONE;
1563 MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD_PTR)lpMidiHdr, 0L);
1564 return MMSYSERR_NOERROR;
1567 /**************************************************************************
1568 * modPrepare [internal]
1570 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1572 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
1574 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
1575 return MMSYSERR_INVALPARAM;
1576 if (lpMidiHdr->dwFlags & MHDR_PREPARED)
1577 return MMSYSERR_NOERROR;
1579 lpMidiHdr->lpNext = 0;
1580 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1581 lpMidiHdr->dwFlags &= ~(MHDR_DONE|MHDR_INQUEUE); /* flags cleared since w2k */
1582 return MMSYSERR_NOERROR;
1585 /**************************************************************************
1586 * modUnprepare [internal]
1588 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1590 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
1592 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
1593 return MMSYSERR_INVALPARAM;
1594 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
1595 return MMSYSERR_NOERROR;
1596 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1597 return MIDIERR_STILLPLAYING;
1598 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1599 return MMSYSERR_NOERROR;
1602 /**************************************************************************
1603 * modGetVolume [internal]
1605 static DWORD modGetVolume(WORD wDevID, DWORD* lpdwVolume)
1607 if (!lpdwVolume) return MMSYSERR_INVALPARAM;
1608 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1609 *lpdwVolume = 0xFFFFFFFF;
1610 return (MidiOutDev[wDevID].caps.dwSupport & MIDICAPS_VOLUME) ? 0 : MMSYSERR_NOTSUPPORTED;
1613 /**************************************************************************
1614 * modReset [internal]
1616 static DWORD modReset(WORD wDevID)
1618 unsigned chn;
1620 TRACE("(%04X);\n", wDevID);
1622 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1623 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1625 /* stop all notes */
1626 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1627 * it's channel dependent...
1629 for (chn = 0; chn < 16; chn++) {
1630 /* turn off every note */
1631 modData(wDevID, 0x7800 | MIDI_CTL_CHANGE | chn);
1632 /* remove sustain on all channels */
1633 modData(wDevID, (CTL_SUSTAIN << 8) | MIDI_CTL_CHANGE | chn);
1635 /* FIXME: the LongData buffers must also be returned to the app */
1636 return MMSYSERR_NOERROR;
1639 #endif /* SNDCTL_SEQ_NRMIDIS */
1641 /*======================================================================*
1642 * MIDI entry points *
1643 *======================================================================*/
1645 /**************************************************************************
1646 * midMessage (WINEOSS.@)
1648 DWORD WINAPI OSS_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1649 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1651 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1652 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1653 switch (wMsg) {
1654 #ifdef SNDCTL_SEQ_NRMIDIS
1655 case DRVM_INIT:
1656 return OSS_MidiInit();
1657 case DRVM_EXIT:
1658 return OSS_MidiExit();
1659 case DRVM_ENABLE:
1660 case DRVM_DISABLE:
1661 /* FIXME: Pretend this is supported */
1662 return 0;
1663 case MIDM_OPEN:
1664 return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1665 case MIDM_CLOSE:
1666 return midClose(wDevID);
1667 case MIDM_ADDBUFFER:
1668 return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1669 case MIDM_PREPARE:
1670 return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1671 case MIDM_UNPREPARE:
1672 return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1673 case MIDM_GETDEVCAPS:
1674 return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1675 case MIDM_GETNUMDEVS:
1676 return MIDM_NumDevs;
1677 case MIDM_RESET:
1678 return midReset(wDevID);
1679 case MIDM_START:
1680 return midStart(wDevID);
1681 case MIDM_STOP:
1682 return midStop(wDevID);
1683 #else
1684 case DRVM_INIT:
1685 case MIDM_GETNUMDEVS:
1686 return 0;
1687 #endif
1688 default:
1689 TRACE("Unsupported message\n");
1691 return MMSYSERR_NOTSUPPORTED;
1694 /**************************************************************************
1695 * modMessage (WINEOSS.@)
1697 DWORD WINAPI OSS_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1698 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1700 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1701 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1703 switch (wMsg) {
1704 #ifdef SNDCTL_SEQ_NRMIDIS
1705 case DRVM_INIT:
1706 return OSS_MidiInit();
1707 case DRVM_EXIT:
1708 return OSS_MidiExit();
1709 case DRVM_ENABLE:
1710 case DRVM_DISABLE:
1711 /* FIXME: Pretend this is supported */
1712 return 0;
1713 case MODM_OPEN:
1714 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1715 case MODM_CLOSE:
1716 return modClose(wDevID);
1717 case MODM_DATA:
1718 return modData(wDevID, dwParam1);
1719 case MODM_LONGDATA:
1720 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1721 case MODM_PREPARE:
1722 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1723 case MODM_UNPREPARE:
1724 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1725 case MODM_GETDEVCAPS:
1726 return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1727 case MODM_GETNUMDEVS:
1728 return MODM_NumDevs;
1729 case MODM_GETVOLUME:
1730 return modGetVolume(wDevID, (DWORD*)dwParam1);
1731 case MODM_SETVOLUME:
1732 return 0;
1733 case MODM_RESET:
1734 return modReset(wDevID);
1735 #else
1736 case DRVM_INIT:
1737 case MODM_GETNUMDEVS:
1738 return 0;
1739 #endif
1740 default:
1741 TRACE("Unsupported message\n");
1743 return MMSYSERR_NOTSUPPORTED;
1746 /**************************************************************************
1747 * DriverProc (WINEOSS.1)
1749 LRESULT CALLBACK OSS_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1750 LPARAM dwParam1, LPARAM dwParam2)
1752 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1753 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1755 switch(wMsg) {
1756 case DRV_LOAD:
1757 case DRV_FREE:
1758 case DRV_OPEN:
1759 case DRV_CLOSE:
1760 case DRV_ENABLE:
1761 case DRV_DISABLE:
1762 case DRV_QUERYCONFIGURE:
1763 case DRV_CONFIGURE:
1764 return 1;
1765 case DRV_INSTALL:
1766 case DRV_REMOVE:
1767 return DRV_SUCCESS;
1768 default:
1769 return 0;