Moved VGA-emulating DirectX code from dispdib.c to a separate
[wine/wine-kai.git] / multimedia / midi.c
blob792c3645ab57fffe7895c0ca5b82cf4fbd173688
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 /*
4 * Sample MIDI Wine Driver for Linux
6 * Copyright 1994 Martin Ayotte
7 */
9 /*
10 * Eric POUECH :
11 * 98/7 changes for making this MIDI driver work on OSS
12 * current support is limited to MIDI ports of OSS systems
13 * 98/9 rewriting MCI code for MIDI
16 #include <errno.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <sys/ioctl.h>
23 #include "windows.h"
24 #include "ldt.h"
25 #include "multimedia.h"
26 #include "user.h"
27 #include "driver.h"
28 #include "mmsystem.h"
29 #include "xmalloc.h"
30 #include "debug.h"
31 #include "callback.h"
32 #include "options.h"
34 typedef struct {
35 #ifndef HAVE_OSS
36 int unixdev;
37 #endif
38 int state;
39 DWORD bufsize;
40 LPMIDIOPENDESC midiDesc;
41 WORD wFlags;
42 LPMIDIHDR lpQueueHdr;
43 DWORD dwTotalPlayed;
44 #ifdef HAVE_OSS
45 unsigned char incoming[3];
46 unsigned char incPrev;
47 char incLen;
48 DWORD startTime;
49 #endif
50 } WINE_MIDIIN;
52 typedef struct {
53 #ifndef HAVE_OSS
54 int unixdev;
55 #endif
56 int state;
57 DWORD bufsize;
58 LPMIDIOPENDESC midiDesc;
59 WORD wFlags;
60 LPMIDIHDR lpQueueHdr;
61 DWORD dwTotalPlayed;
62 #ifdef HAVE_OSS
63 void* lpExtra; /* according to port type (MIDI, FM...), extra data when needed */
64 #endif
65 } WINE_MIDIOUT;
67 typedef struct {
68 DWORD dwFirst; /* offset in file of track */
69 DWORD dwLast; /* number of bytes in file of track */
70 DWORD dwIndex; /* current index in file (dwFirst <= dwIndex < dwLast) */
71 DWORD dwLength; /* number of pulses in this track */
72 DWORD dwEventPulse; /* current pulse # (event) pointed by dwIndex */
73 DWORD dwEventData; /* current data (event) pointed by dwIndex */
74 WORD wEventLength; /* current length (event) pointed by dwIndex */
75 WORD wStatus : 1, /* 1 : playing, 0 : done */
76 wTrackNr : 7,
77 wLastCommand : 8;
78 } MCI_MIDITRACK;
80 typedef struct {
81 int nUseCount; /* Incremented for each shared open */
82 BOOL16 fShareable; /* TRUE if first open was shareable */
83 WORD wNotifyDeviceID; /* MCI device ID with a pending notification */
84 HANDLE16 hCallback; /* Callback handle for pending notification */
85 HMMIO32 hFile; /* mmio file handle open as Element */
86 WORD wFormat;
87 WORD nTracks;
88 WORD nDivision;
89 DWORD dwTempo;
90 MCI_OPEN_PARMS16 openParms;
91 WORD dwStatus;
92 MCI_MIDITRACK* tracks;
93 DWORD dwPulse;
94 DWORD dwPositionMS;
95 DWORD dwStartTicks;
96 HLOCAL16 hMidiHdr;
97 } WINE_MCIMIDI;
99 static WINE_MIDIIN MidiInDev [MAX_MIDIINDRV ];
100 static WINE_MIDIOUT MidiOutDev[MAX_MIDIOUTDRV];
101 static WINE_MCIMIDI MCIMidiDev[MAX_MCIMIDIDRV];
103 /* this is the total number of MIDI out devices found */
104 int MODM_NUMDEVS = 0;
105 /* this is the number of FM synthetizers (index from 0 to
106 NUMFMSYNTHDEVS - 1) */
107 int MODM_NUMFMSYNTHDEVS = 0;
108 /* this is the number of Midi ports (index from NUMFMSYNTHDEVS to
109 NUMFMSYNTHDEVS + NUMMIDIDEVS - 1) */
110 int MODM_NUMMIDIDEVS = 0;
112 /* this is the total number of MIDI out devices found */
113 int MIDM_NUMDEVS = 0;
115 #ifdef HAVE_OSS
116 static int midiSeqFD = -1;
117 static int numOpenMidiSeq = 0;
118 static UINT32 midiInTimerID = 0;
119 static int numStartedMidiIn = 0;
120 #endif /* HAVE_OSS */
122 /* this structure holds pointers with information for each MIDI
123 * out device found.
125 LPMIDIOUTCAPS16 midiOutDevices[MAX_MIDIOUTDRV];
127 /* this structure holds pointers with information for each MIDI
128 * in device found.
130 LPMIDIINCAPS16 midiInDevices [MAX_MIDIINDRV];
133 * FIXME : all tests on device ID for midXXX and modYYY are made against
134 * MAX_MIDIxxDRV (when they are made) but should be done against the actual
135 * number of midi devices found...
136 * check also when HAVE_OSS is defined that midiDesc is not NULL
139 /**************************************************************************
140 * MIDI_NotifyClient [internal]
142 static DWORD MIDI_NotifyClient(UINT16 wDevID, WORD wMsg,
143 DWORD dwParam1, DWORD dwParam2)
145 DWORD dwCallBack;
146 UINT16 uFlags;
147 HANDLE16 hDev;
148 DWORD dwInstance;
150 TRACE(midi,"wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
151 wDevID, wMsg, dwParam1, dwParam2);
153 switch (wMsg) {
154 case MOM_OPEN:
155 case MOM_CLOSE:
156 case MOM_DONE:
157 if (wDevID > MAX_MIDIOUTDRV)
158 return MCIERR_INTERNAL;
160 dwCallBack = MidiOutDev[wDevID].midiDesc->dwCallback;
161 uFlags = MidiOutDev[wDevID].wFlags;
162 hDev = MidiOutDev[wDevID].midiDesc->hMidi;
163 dwInstance = MidiOutDev[wDevID].midiDesc->dwInstance;
164 break;
166 case MIM_OPEN:
167 case MIM_CLOSE:
168 case MIM_DATA:
169 case MIM_ERROR:
170 if (wDevID > MAX_MIDIINDRV)
171 return MCIERR_INTERNAL;
173 dwCallBack = MidiInDev[wDevID].midiDesc->dwCallback;
174 uFlags = MidiInDev[wDevID].wFlags;
175 hDev = MidiInDev[wDevID].midiDesc->hMidi;
176 dwInstance = MidiInDev[wDevID].midiDesc->dwInstance;
177 break;
178 default:
179 WARN(midi, "Unsupported MSW-MIDI message %u\n", wMsg);
180 return MCIERR_INTERNAL;
183 return DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2) ?
184 0 : MCIERR_INTERNAL;
187 /**************************************************************************
188 * MIDI_ReadByte [internal]
190 static DWORD MIDI_ReadByte(UINT16 wDevID, BYTE *lpbyt)
192 if (lpbyt != NULL) {
193 if (mmioRead32(MCIMidiDev[wDevID].hFile, (HPSTR)lpbyt,
194 (long) sizeof(BYTE)) == (long) sizeof(BYTE)) {
195 return 0;
198 WARN(midi, "error reading wDevID=%04X\n", wDevID);
199 return MCIERR_INTERNAL;
203 /**************************************************************************
204 * MIDI_ReadWord [internal]
206 static DWORD MIDI_ReadWord(UINT16 wDevID, LPWORD lpw)
208 BYTE hibyte, lobyte;
210 if (lpw != NULL) {
211 if (MIDI_ReadByte(wDevID, &hibyte) == 0) {
212 if (MIDI_ReadByte(wDevID, &lobyte) == 0) {
213 *lpw = ((WORD)hibyte << 8) + lobyte;
214 return 0;
218 WARN(midi, "error reading wDevID=%04X\n", wDevID);
219 return MCIERR_INTERNAL;
222 /**************************************************************************
223 * MIDI_ReadLong [internal]
225 static DWORD MIDI_ReadLong(UINT16 wDevID, LPDWORD lpdw)
227 WORD hiword, loword;
229 if (lpdw != NULL) {
230 if (MIDI_ReadWord(wDevID, &hiword) == 0) {
231 if (MIDI_ReadWord(wDevID, &loword) == 0) {
232 *lpdw = MAKELONG(loword, hiword);
233 return 0;
237 WARN(midi, "error reading wDevID=%04X\n", wDevID);
238 return MCIERR_INTERNAL;
241 /**************************************************************************
242 * MIDI_ReadVaryLen [internal]
244 static WORD MIDI_ReadVaryLen(UINT16 wDevID, LPDWORD lpdw)
246 BYTE byte;
247 DWORD value = 0;
248 WORD ret = 0;
250 if (lpdw == NULL)
251 return MCIERR_INTERNAL;
253 do {
254 if (MIDI_ReadByte(wDevID, &byte) != 0) {
255 WARN(midi, "error reading wDevID=%04X\n", wDevID);
256 return 0;
258 value = (value << 7) + (byte & 0x7F);
259 ret++;
260 } while (byte & 0x80);
261 *lpdw = value;
263 TRACE(midi, "val=%08lX \n", value);
265 return ret;
268 /**************************************************************************
269 * MIDI_ReadNextEvent [internal]
271 static DWORD MIDI_ReadNextEvent(UINT16 wDevID, MCI_MIDITRACK* mmt)
273 BYTE b1, b2 = 0, b3;
274 WORD hw = 0;
275 DWORD evtPulse;
276 DWORD evtLength;
277 DWORD tmp;
279 if (mmioSeek32(MCIMidiDev[wDevID].hFile, mmt->dwIndex, SEEK_SET) != mmt->dwIndex) {
280 WARN(midi, "can't seek at %08lX \n", mmt->dwIndex);
281 return MCIERR_INTERNAL;
283 evtLength = MIDI_ReadVaryLen(wDevID, &evtPulse) + 1; /* > 0 */
284 MIDI_ReadByte(wDevID, &b1);
285 switch (b1) {
286 case 0xF0:
287 case 0xF7:
288 evtLength += MIDI_ReadVaryLen(wDevID, &tmp);
289 evtLength += tmp;
290 break;
291 case 0xFF:
292 MIDI_ReadByte(wDevID, &b2); evtLength++;
294 evtLength += MIDI_ReadVaryLen(wDevID, &tmp);
295 if (evtLength >= 0x10000u) {
296 /* this limitation shouldn't be a problem */
297 WARN(midi, "Ouch !! Implementation limitation to 64k bytes for a MIDI event is overflowed\n");
298 hw = 0xFFFF;
299 } else {
300 hw = LOWORD(evtLength);
302 evtLength += tmp;
303 break;
304 default:
305 if (b1 & 0x80) { // use running status ?
306 mmt->wLastCommand = b1;
307 MIDI_ReadByte(wDevID, &b2); evtLength++;
308 } else {
309 b2 = b1;
310 b1 = mmt->wLastCommand;
312 switch ((b1 >> 4) & 0x07) {
313 case 0: case 1: case 2: case 3: case 6:
314 MIDI_ReadByte(wDevID, &b3); evtLength++;
315 hw = b3;
316 break;
317 case 4: case 5:
318 break;
319 case 7:
320 WARN(midi, "Strange indeed b1=0x%02x\n", b1);
322 break;
324 if (mmt->dwIndex + evtLength > mmt->dwLast)
325 return MCIERR_INTERNAL;
327 mmt->dwEventPulse += evtPulse;
328 mmt->dwEventData = (hw << 16) + (b2 << 8) + b1;
329 mmt->wEventLength = evtLength;
332 TRACE(midi, "[%u] => pulse=%08lx(%08lx), data=%08lx, length=%u\n",
333 mmt->wTrackNr, mmt->dwEventPulse, evtPulse,
334 mmt->dwEventData, mmt->wEventLength);
336 return 0;
339 /**************************************************************************
340 * MIDI_ReadMTrk [internal]
342 static DWORD MIDI_ReadMTrk(UINT16 wDevID, MCI_MIDITRACK* mmt)
344 DWORD toberead;
345 FOURCC fourcc;
347 if (mmioRead32(MCIMidiDev[wDevID].hFile, (HPSTR)&fourcc,
348 (long) sizeof(FOURCC)) != (long) sizeof(FOURCC)) {
349 return MCIERR_INTERNAL;
352 if (fourcc != mmioFOURCC('M', 'T', 'r', 'k')) {
353 WARN(midi, "cannot synchronize on MTrk !\n");
354 return MCIERR_INTERNAL;
357 if (MIDI_ReadLong(wDevID, &toberead) != 0) {
358 return MCIERR_INTERNAL;
360 mmt->dwFirst = mmioSeek32(MCIMidiDev[wDevID].hFile, 0, SEEK_CUR); /* >= 0 */
361 mmt->dwLast = mmt->dwFirst + toberead;
363 /* compute # of pulses in this track */
364 mmt->dwIndex = mmt->dwFirst;
365 mmt->dwEventPulse = 0;
366 while (MIDI_ReadNextEvent(wDevID, mmt) == 0 &&
367 LOWORD(mmt->dwEventData) != 0x2FFF) {
368 mmt->dwIndex += mmt->wEventLength;
370 mmt->dwLength = mmt->dwEventPulse;
372 TRACE(midi, "Track %u has %lu bytes and %lu pulses\n",
373 mmt->wTrackNr, toberead, mmt->dwLength);
375 /* reset track data */
376 mmt->wStatus = 1; /* ok, playing */
377 mmt->dwIndex = mmt->dwFirst;
378 mmt->dwEventPulse = 0;
380 if (mmioSeek32(MCIMidiDev[wDevID].hFile, 0, SEEK_CUR) != mmt->dwLast) {
381 WARN(midi, "Ouch, out of sync seek=%lu track=%lu\n",
382 mmioSeek32(MCIMidiDev[wDevID].hFile, 0, SEEK_CUR), mmt->dwLast);
383 /* position at end of this track, to be ready to read next track */
384 mmioSeek32(MCIMidiDev[wDevID].hFile, mmt->dwLast, SEEK_SET);
387 return 0;
390 /**************************************************************************
391 * MIDI_ReadMThd [internal]
393 static DWORD MIDI_ReadMThd(UINT16 wDevID, DWORD dwOffset)
395 DWORD toberead;
396 FOURCC fourcc;
397 WORD nt;
399 TRACE(midi, "(%04X, %08lX);\n", wDevID, dwOffset);
400 if (mmioSeek32(MCIMidiDev[wDevID].hFile, dwOffset, SEEK_SET) != dwOffset) {
401 WARN(midi, "can't seek at %08lX begin of 'MThd' \n", dwOffset);
402 return MCIERR_INTERNAL;
404 if (mmioRead32(MCIMidiDev[wDevID].hFile, (HPSTR)&fourcc,
405 (long) sizeof(FOURCC)) != (long) sizeof(FOURCC))
406 return MCIERR_INTERNAL;
408 if (fourcc != mmioFOURCC('M', 'T', 'h', 'd')) {
409 WARN(midi, "cannot synchronize on MThd !\n");
410 return MCIERR_INTERNAL;
413 if (MIDI_ReadLong(wDevID, &toberead) != 0 || toberead < 3 * sizeof(WORD))
414 return MCIERR_INTERNAL;
415 if (MIDI_ReadWord(wDevID, &MCIMidiDev[wDevID].wFormat) != 0)
416 return MCIERR_INTERNAL;
417 if (MIDI_ReadWord(wDevID, &MCIMidiDev[wDevID].nTracks) != 0)
418 return MCIERR_INTERNAL;
419 if (MIDI_ReadWord(wDevID, &MCIMidiDev[wDevID].nDivision) != 0)
420 return MCIERR_INTERNAL;
422 TRACE(midi, "toberead=0x%08lX, wFormat=0x%04X nTracks=0x%04X nDivision=0x%04X\n",
423 toberead, MCIMidiDev[wDevID].wFormat,
424 MCIMidiDev[wDevID].nTracks,
425 MCIMidiDev[wDevID].nDivision);
427 if (MCIMidiDev[wDevID].nDivision > 0x8000) {
428 WARN(midi, "Handling SMPTE time in MIDI files is not (yet) supported\n"
429 "Please report with MIDI file !\n");
430 return MCIERR_INTERNAL;
433 switch (MCIMidiDev[wDevID].wFormat) {
434 case 0:
435 if (MCIMidiDev[wDevID].nTracks != 1) {
436 WARN(midi, "Got type 0 file whose number of track is not 1. Setting it to 1\n");
437 MCIMidiDev[wDevID].nTracks = 1;
439 break;
440 case 1:
441 case 2:
442 break;
443 default:
444 WARN(midi, "Handling MIDI files which format = %d is not (yet) supported\n"
445 "Please report with MIDI file !\n", MCIMidiDev[wDevID].wFormat);
446 return MCIERR_INTERNAL;
449 if (MCIMidiDev[wDevID].nTracks & 0x8000) {
450 /* this shouldn't be a problem... */
451 WARN(midi, "Ouch !! Implementation limitation to 32k tracks per MIDI file is overflowed\n");
452 MCIMidiDev[wDevID].nTracks = 0x7FFF;
454 MCIMidiDev[wDevID].tracks = xmalloc(sizeof(MCI_MIDITRACK) * MCIMidiDev[wDevID].nTracks);
456 toberead -= 3 * sizeof(WORD);
457 if (toberead > 0) {
458 TRACE(midi, "Size of MThd > 6, skipping %ld extra bytes\n", toberead);
459 mmioSeek32(MCIMidiDev[wDevID].hFile, toberead, SEEK_CUR);
462 for (nt = 0; nt < MCIMidiDev[wDevID].nTracks; nt++) {
463 MCIMidiDev[wDevID].tracks[nt].wTrackNr = nt;
464 if (MIDI_ReadMTrk(wDevID, &MCIMidiDev[wDevID].tracks[nt]) != 0) {
465 WARN(midi, "can't read 'MTrk' header \n");
466 return MCIERR_INTERNAL;
470 return 0;
473 /**************************************************************************
474 * MIDI_mciOpen [internal]
476 static DWORD MIDI_mciOpen(UINT16 wDevID, DWORD dwFlags, void* lp, BOOL32 is32)
478 MIDIOPENDESC MidiDesc;
479 DWORD dwRet;
480 DWORD dwDeviceID;
482 TRACE(midi, "(%08lX, %p)\n", dwFlags, lp);
484 if (lp == NULL) return MCIERR_INTERNAL;
486 if (MCIMidiDev[wDevID].nUseCount > 0) {
487 /* The driver already open on this channel */
488 /* If the driver was opened shareable before and this open specifies */
489 /* shareable then increment the use count */
490 if (MCIMidiDev[wDevID].fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
491 ++MCIMidiDev[wDevID].nUseCount;
492 else
493 return MCIERR_MUST_USE_SHAREABLE;
494 } else {
495 MCIMidiDev[wDevID].nUseCount = 1;
496 MCIMidiDev[wDevID].fShareable = dwFlags & MCI_OPEN_SHAREABLE;
497 MCIMidiDev[wDevID].hMidiHdr = USER_HEAP_ALLOC(sizeof(MIDIHDR));
500 if (is32) dwDeviceID = ((LPMCI_OPEN_PARMS32A)lp)->wDeviceID;
501 else dwDeviceID = ((LPMCI_OPEN_PARMS16)lp)->wDeviceID;
503 TRACE(midi, "wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID, dwDeviceID);
504 /* lpParms->wDeviceID = wDevID;*/
505 TRACE(midi, "before OPEN_ELEMENT\n");
506 if (dwFlags & MCI_OPEN_ELEMENT) {
507 LPSTR lpstrElementName;
509 if (is32) lpstrElementName = ((LPMCI_OPEN_PARMS32A)lp)->lpstrElementName;
510 else lpstrElementName = (LPSTR)PTR_SEG_TO_LIN(((LPMCI_OPEN_PARMS16)lp)->lpstrElementName);
512 TRACE(midi, "MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName);
513 if (strlen(lpstrElementName) > 0) {
514 MCIMidiDev[wDevID].hFile = mmioOpen32A(lpstrElementName, NULL,
515 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_EXCLUSIVE);
516 if (MCIMidiDev[wDevID].hFile == 0) {
517 WARN(midi, "can't find file='%s' !\n", lpstrElementName);
518 return MCIERR_FILE_NOT_FOUND;
520 } else
521 MCIMidiDev[wDevID].hFile = 0;
523 TRACE(midi, "hFile=%u\n", MCIMidiDev[wDevID].hFile);
525 /* should be of same size in all cases */
526 memcpy(&MCIMidiDev[wDevID].openParms, lp, sizeof(MCI_OPEN_PARMS16));
528 MCIMidiDev[wDevID].wNotifyDeviceID = dwDeviceID;
529 MCIMidiDev[wDevID].dwStatus = MCI_MODE_STOP;
530 MidiDesc.hMidi = 0;
532 if (MCIMidiDev[wDevID].hFile != 0) {
533 MMCKINFO ckMainRIFF;
534 DWORD dwOffset = 0;
536 if (mmioDescend(MCIMidiDev[wDevID].hFile, &ckMainRIFF, NULL, 0) != 0) {
537 return MCIERR_INTERNAL;
539 TRACE(midi,"ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
540 (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType,
541 ckMainRIFF.cksize);
543 if (ckMainRIFF.ckid == mmioFOURCC('R', 'M', 'I', 'D')) {
544 TRACE(midi, "is a 'RMID' file \n");
545 dwOffset = ckMainRIFF.dwDataOffset;
546 FIXME(midi, "Setting #tracks for RMID to 1: is this correct ?\n");
547 MCIMidiDev[wDevID].nTracks = 1;
549 if (MIDI_ReadMThd(wDevID, dwOffset) != 0) {
550 WARN(midi, "can't read 'MThd' header \n");
551 return MCIERR_INTERNAL;
554 TRACE(midi, "Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
555 (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType,
556 ckMainRIFF.cksize);
559 dwRet = modMessage(wDevID, MODM_OPEN, 0, (DWORD)&MidiDesc, CALLBACK_NULL);
560 /* dwRet = midMessage(wDevID, MIDM_OPEN, 0, (DWORD)&MidiDesc, CALLBACK_NULL);*/
562 return 0;
565 static DWORD MIDI_ConvertPulseToMS(UINT16 wDevID, DWORD pulse)
567 DWORD ret = (DWORD)((double)pulse * ((double)MCIMidiDev[wDevID].dwTempo / 1000) /
568 (double)MCIMidiDev[wDevID].nDivision);
571 TRACE(midi, "pulse=%lu tempo=%lu division=%u => ms=%lu\n",
572 pulse, MCIMidiDev[wDevID].dwTempo, MCIMidiDev[wDevID].nDivision, ret);
574 return ret;
577 /**************************************************************************
578 * MIDI_mciStop [internal]
580 static DWORD MIDI_mciStop(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
582 TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
584 if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) {
585 WARN(midi, "Invalid wDevID=%u\n", wDevID);
586 return MCIERR_INVALID_DEVICE_ID;
589 MCIMidiDev[wDevID].dwStatus = MCI_MODE_STOP;
590 TRACE(midi, "MCIMidiDev[wDevID].dwStatus=%d\n", MCIMidiDev[wDevID].dwStatus);
591 if (lpParms && (dwFlags & MCI_NOTIFY)) {
592 TRACE(midi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
593 mciDriverNotify((HWND16)LOWORD(lpParms->dwCallback),
594 MCIMidiDev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
596 return 0;
599 /**************************************************************************
600 * MIDI_mciClose [internal]
602 static DWORD MIDI_mciClose(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
604 DWORD dwRet;
606 TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
608 if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) {
609 WARN(midi, "Invalid wDevID=%u\n", wDevID);
610 return MCIERR_INVALID_DEVICE_ID;
613 if (MCIMidiDev[wDevID].dwStatus != MCI_MODE_STOP) {
614 MIDI_mciStop(wDevID, MCI_WAIT, lpParms);
617 MCIMidiDev[wDevID].dwStatus = MCI_MODE_STOP;
618 MCIMidiDev[wDevID].nUseCount--;
619 if (MCIMidiDev[wDevID].nUseCount == 0) {
620 if (MCIMidiDev[wDevID].hFile != 0) {
621 mmioClose32(MCIMidiDev[wDevID].hFile, 0);
622 MCIMidiDev[wDevID].hFile = 0;
623 TRACE(midi, "hFile closed !\n");
625 USER_HEAP_FREE(MCIMidiDev[wDevID].hMidiHdr);
626 free(MCIMidiDev[wDevID].tracks);
627 dwRet = modMessage(wDevID, MODM_CLOSE, 0, 0L, 0L);
628 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
630 dwRet = midMessage(wDevID, MIDM_CLOSE, 0, 0L, 0L);
631 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
634 if (lpParms && (dwFlags & MCI_NOTIFY)) {
635 TRACE(midi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
636 mciDriverNotify((HWND16)LOWORD(lpParms->dwCallback),
637 MCIMidiDev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
639 return 0;
642 /**************************************************************************
643 * MIDI_mciPlay [internal]
645 static DWORD MIDI_mciPlay(UINT16 wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
647 DWORD start = 0, end = 0xFFFFFFFFul;
648 DWORD dwRet;
649 WORD nt, cnt;
651 TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
653 if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) {
654 WARN(midi, "Invalid wDevID=%u\n", wDevID);
655 return MCIERR_INVALID_DEVICE_ID;
658 if (MCIMidiDev[wDevID].hFile == 0) {
659 WARN(midi, "Cannot play : can't find file='%08lx' !\n",
660 (DWORD)MCIMidiDev[wDevID].openParms.lpstrElementName);
661 return MCIERR_FILE_NOT_FOUND;
664 if (MCIMidiDev[wDevID].dwStatus != MCI_MODE_STOP) {
665 WARN(midi, "Cannot play : device is not stopped !\n");
666 return MCIERR_INTERNAL;
669 if (lpParms && (dwFlags & MCI_FROM)) {
670 start = lpParms->dwFrom;
671 FIXME(midi, "MCI_FROM=%lu \n", start);
673 if (lpParms && (dwFlags & MCI_TO)) {
674 end = lpParms->dwTo;
675 FIXME(midi, "MCI_TO=%lu \n", end);
678 if (!(dwFlags & MCI_WAIT)) {
679 /** FIXME: I'm not 100% sure that wNotifyDeviceID is the right value in all cases ??? */
680 return mciSendCommandAsync32(MCIMidiDev[wDevID].wNotifyDeviceID, MCI_PLAY, dwFlags, (DWORD)lpParms);
683 /* init tracks */
684 for (nt = 0; nt < MCIMidiDev[wDevID].nTracks; nt++) {
685 MCI_MIDITRACK* mmt = &MCIMidiDev[wDevID].tracks[nt];
687 mmt->wStatus = 1; /* ok, playing */
688 mmt->dwIndex = mmt->dwFirst;
689 if (MCIMidiDev[wDevID].wFormat == 2 && nt > 0) {
690 mmt->dwEventPulse = MCIMidiDev[wDevID].tracks[nt - 1].dwLength;
691 } else {
692 mmt->dwEventPulse = 0;
694 MIDI_ReadNextEvent(wDevID, mmt); /* FIXME == 0 */
697 MCIMidiDev[wDevID].dwPulse = 0;
698 MCIMidiDev[wDevID].dwPositionMS = 0;
699 MCIMidiDev[wDevID].dwStartTicks = GetTickCount();
700 MCIMidiDev[wDevID].dwTempo = 500000;
701 MCIMidiDev[wDevID].dwStatus = MCI_MODE_PLAY;
703 while (MCIMidiDev[wDevID].dwStatus != MCI_MODE_STOP) {
704 MCI_MIDITRACK* mmt;
705 DWORD hiPulse;
707 TRACE(midi, "MCIMidiDev[wDevID].dwStatus=%p %d\n",
708 &MCIMidiDev[wDevID].dwStatus, MCIMidiDev[wDevID].dwStatus);
710 while (MCIMidiDev[wDevID].dwStatus == MCI_MODE_PAUSE);
712 /* find first event */
713 hiPulse = 0xFFFFFFFFul;
714 cnt = 0xFFFFu;
715 for (nt = 0; nt < MCIMidiDev[wDevID].nTracks; nt++) {
716 mmt = &MCIMidiDev[wDevID].tracks[nt];
718 if (mmt->wStatus == 0)
719 continue;
720 if (mmt->dwEventPulse < hiPulse) {
721 hiPulse = mmt->dwEventPulse;
722 cnt = nt;
725 if (cnt == 0xFFFFu) /* no more event on tracks */
726 break;
728 mmt = &MCIMidiDev[wDevID].tracks[cnt];
729 if (hiPulse > MCIMidiDev[wDevID].dwPulse) {
730 DWORD togo = MCIMidiDev[wDevID].dwStartTicks +
731 MIDI_ConvertPulseToMS(wDevID, hiPulse);
732 DWORD tc = GetTickCount();
734 TRACE(midi, "Pulses hi=0x%08lx <> cur=0x%08lx\n", hiPulse, MCIMidiDev[wDevID].dwPulse);
735 TRACE(midi, "Wait til %08lx -> %08lx ms\n",
736 tc - MCIMidiDev[wDevID].dwStartTicks,
737 togo - MCIMidiDev[wDevID].dwStartTicks);
738 if (tc < togo) Sleep(togo - tc);
739 MCIMidiDev[wDevID].dwPositionMS += MIDI_ConvertPulseToMS(wDevID, hiPulse - MCIMidiDev[wDevID].dwPulse);
740 MCIMidiDev[wDevID].dwPulse = hiPulse;
743 switch (LOBYTE(LOWORD(mmt->dwEventData))) {
744 case 0xF0:
745 case 0xF7: /* sysex events */
746 FIXME(midi, "Not handling SysEx events (yet)\n");
747 break;
748 case 0xFF:
749 /* position after meta data header */
750 mmioSeek32(MCIMidiDev[wDevID].hFile, mmt->dwIndex + HIWORD(mmt->dwEventData), SEEK_SET);
751 switch (HIBYTE(LOWORD(mmt->dwEventData))) {
752 case 0x00: /* 16-bit sequence number */
753 if (TRACE_ON(midi)) {
754 WORD twd;
756 MIDI_ReadWord(wDevID, &twd); /* == 0 */
757 TRACE(midi, "Got sequence number %u\n", twd);
759 break;
760 case 0x01: /* any text */
761 case 0x02: /* Copyright Message text */
762 case 0x03: /* Sequence/Track Name text */
763 case 0x04: /* Instrument Name text */
764 case 0x05: /* Lyric text */
765 case 0x06: /* Marker text */
766 case 0x07: /* Cue-point text */
767 if (TRACE_ON(midi)) {
768 char buf[1024];
769 WORD len = mmt->wEventLength - HIWORD(mmt->dwEventData);
770 static char* info[8] = {"", "Text", "Copyright", "Seq/Trk name",
771 "Instrument", "Lyric", "Marker", "Cue-point"};
772 WORD idx = HIBYTE(LOWORD(mmt->dwEventData));
774 if (len >= sizeof(buf)) {
775 WARN(midi, "Buffer for text is too small (%d bytes, when %u are needed)\n", sizeof(buf) - 1, len);
776 len = sizeof(buf) - 1;
778 if (mmioRead32(MCIMidiDev[wDevID].hFile, (HPSTR)buf, len) == len) {
779 buf[len] = 0; /* end string in case */
780 TRACE(midi, "%s => \"%s\"\n", (idx < 8 ) ? info[idx] : "", buf);
781 } else {
782 WARN(midi, "Couldn't read data for %s\n", (idx < 8 ) ? info[idx] : "");
785 break;
786 case 0x20:
787 /* MIDI channel
788 * cc
790 if (TRACE_ON(midi)) {
791 BYTE bt;
793 MIDI_ReadByte(wDevID, &bt); /* == 0 */
794 FIXME(midi, "NIY: MIDI channel %u\n", bt);
796 } else {
797 FIXME(midi, "NIY: MIDI channel\n");
799 break;
800 case 0x21:
801 /* MIDI port
802 * pp
804 if (TRACE_ON(midi)) {
805 BYTE bt;
807 MIDI_ReadByte(wDevID, &bt); /* == 0 */
808 FIXME(midi, "NIY: MIDI port %u\n", bt);
809 } else {
810 FIXME(midi, "NIY: MIDI port\n");
812 break;
813 case 0x2F: /* end of track */
814 mmt->wStatus = 0;
815 break;
816 case 0x51:/* set tempo */
817 /* Tempo is expressed in µ-seconds per midi quarter note
818 * for format 1 MIDI files, this can only be present on track #0
820 if (mmt->wTrackNr != 0 && MCIMidiDev[wDevID].wFormat == 1) {
821 WARN(midi, "For format #1 MIDI files, tempo can only be changed on track #0 (%u)\n", mmt->wTrackNr);
822 } else {
823 BYTE tbt;
825 MIDI_ReadByte(wDevID, &tbt); MCIMidiDev[wDevID].dwTempo = ((DWORD)tbt) << 16;
826 MIDI_ReadByte(wDevID, &tbt); MCIMidiDev[wDevID].dwTempo |= ((DWORD)tbt) << 8;
827 MIDI_ReadByte(wDevID, &tbt); MCIMidiDev[wDevID].dwTempo |= ((DWORD)tbt) << 0;
828 TRACE(midi, "Setting tempo to %ld (BPM=%ld)\n", MCIMidiDev[wDevID].dwTempo, 60000000l / MCIMidiDev[wDevID].dwTempo);
831 break;
832 case 0x54: /* (hour) (min) (second) (frame) (fractional-frame) - SMPTE track start */
833 FIXME(midi, "NIY: SMPTE track start\n");
834 break;
835 case 0x58:
836 if (TRACE_ON(midi)) {
837 BYTE num, den, cpmc, _32npqn;
839 MIDI_ReadByte(wDevID, &num);
840 MIDI_ReadByte(wDevID, &den); /* to notate e.g. 6/8 */
841 MIDI_ReadByte(wDevID, &cpmc); /* number of MIDI clocks per metronome click */
842 MIDI_ReadByte(wDevID, &_32npqn); /* number of notated 32nd notes per MIDI quarter note */
844 TRACE(midi, "%u/%u, clock per metronome click=%u, 32nd notes by 1/4 note=%u\n", num, 1 << den, cpmc, _32npqn);
846 break;
847 case 0x59:
848 if (TRACE_ON(midi)) {
849 BYTE sf, mm;
851 MIDI_ReadByte(wDevID, &sf); MIDI_ReadByte(wDevID, &mm);
853 if (sf >= 0x80) TRACE(midi, "%d flats\n", -(char)sf);
854 else if (sf > 0) TRACE(midi, "%d sharps\n", (char)sf);
855 else TRACE(midi, "Key of C\n");
856 TRACE(midi, "Mode: %s\n", (mm = 0) ? "major" : "minor");
858 break;
859 default:
860 WARN(midi, "Unknown MIDI meta event %02x. Skipping...\n", HIBYTE(LOWORD(mmt->dwEventData)));
861 break;
863 break;
864 default:
865 dwRet = modMessage(wDevID, MODM_DATA, 0, mmt->dwEventData, 0);
867 mmt->dwIndex += mmt->wEventLength;
868 if (mmt->dwIndex < mmt->dwFirst || mmt->dwIndex >= mmt->dwLast) {
869 mmt->wStatus = 0;
871 if (mmt->wStatus) {
872 MIDI_ReadNextEvent(wDevID, mmt);
876 /* stop all notes */
877 #ifdef HAVE_OSS
878 modMessage(wDevID, MODM_DATA, 0, (MIDI_CTL_CHANGE << 8) | 0x78, 0);
879 #endif
880 MCIMidiDev[wDevID].dwStatus = MCI_MODE_STOP;
881 if (lpParms && (dwFlags & MCI_NOTIFY)) {
882 TRACE(midi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
883 mciDriverNotify((HWND16)LOWORD(lpParms->dwCallback),
884 MCIMidiDev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
886 return 0;
889 /**************************************************************************
890 * MIDI_mciRecord [internal]
892 static DWORD MIDI_mciRecord(UINT16 wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
894 int start, end;
895 LPMIDIHDR lpMidiHdr;
896 DWORD dwRet;
898 TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
900 if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) {
901 WARN(midi, "Invalid wDevID=%u\n", wDevID);
902 return MCIERR_INVALID_DEVICE_ID;
905 if (MCIMidiDev[wDevID].hFile == 0) {
906 WARN(midi, "can't find file='%08lx' !\n",
907 (DWORD)MCIMidiDev[wDevID].openParms.lpstrElementName);
908 return MCIERR_FILE_NOT_FOUND;
910 start = 1; end = 99999;
911 if (lpParms && (dwFlags & MCI_FROM)) {
912 start = lpParms->dwFrom;
913 TRACE(midi, "MCI_FROM=%d \n", start);
915 if (lpParms && (dwFlags & MCI_TO)) {
916 end = lpParms->dwTo;
917 TRACE(midi, "MCI_TO=%d \n", end);
919 lpMidiHdr = USER_HEAP_LIN_ADDR(MCIMidiDev[wDevID].hMidiHdr);
920 lpMidiHdr->lpData = (LPSTR) xmalloc(1200);
921 lpMidiHdr->dwBufferLength = 1024;
922 lpMidiHdr->dwUser = 0L;
923 lpMidiHdr->dwFlags = 0L;
924 dwRet = midMessage(wDevID, MIDM_PREPARE, 0, (DWORD)lpMidiHdr, sizeof(MIDIHDR));
925 TRACE(midi, "after MIDM_PREPARE \n");
926 MCIMidiDev[wDevID].dwStatus = MCI_MODE_RECORD;
927 while (MCIMidiDev[wDevID].dwStatus != MCI_MODE_STOP) {
928 TRACE(midi, "MCIMidiDev[wDevID].dwStatus=%p %d\n",
929 &MCIMidiDev[wDevID].dwStatus, MCIMidiDev[wDevID].dwStatus);
930 lpMidiHdr->dwBytesRecorded = 0;
931 dwRet = midMessage(wDevID, MIDM_START, 0, 0L, 0L);
932 TRACE(midi, "after MIDM_START lpMidiHdr=%p dwBytesRecorded=%lu\n",
933 lpMidiHdr, lpMidiHdr->dwBytesRecorded);
934 if (lpMidiHdr->dwBytesRecorded == 0) break;
936 TRACE(midi, "before MIDM_UNPREPARE \n");
937 dwRet = midMessage(wDevID, MIDM_UNPREPARE, 0, (DWORD)lpMidiHdr, sizeof(MIDIHDR));
938 TRACE(midi, "after MIDM_UNPREPARE \n");
939 if (lpMidiHdr->lpData != NULL) {
940 free(lpMidiHdr->lpData);
941 lpMidiHdr->lpData = NULL;
943 MCIMidiDev[wDevID].dwStatus = MCI_MODE_STOP;
944 if (lpParms && (dwFlags & MCI_NOTIFY)) {
945 TRACE(midi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
946 mciDriverNotify((HWND16)LOWORD(lpParms->dwCallback),
947 MCIMidiDev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
949 return 0;
952 /**************************************************************************
953 * MIDI_mciPause [internal]
955 static DWORD MIDI_mciPause(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
957 TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
959 if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) {
960 WARN(midi, "Invalid wDevID=%u\n", wDevID);
961 return MCIERR_INVALID_DEVICE_ID;
964 if (MCIMidiDev[wDevID].dwStatus == MCI_MODE_PLAY) {
965 MCIMidiDev[wDevID].dwStatus = MCI_MODE_PAUSE;
967 if (lpParms && (dwFlags & MCI_NOTIFY)) {
968 TRACE(midi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
969 mciDriverNotify((HWND16)LOWORD(lpParms->dwCallback),
970 MCIMidiDev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
973 return 0;
976 /**************************************************************************
977 * MIDI_mciResume [internal]
979 static DWORD MIDI_mciResume(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
981 TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
983 if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) {
984 WARN(midi, "Invalid wDevID=%u\n", wDevID);
985 return MCIERR_INVALID_DEVICE_ID;
988 if (MCIMidiDev[wDevID].dwStatus == MCI_MODE_PAUSE) {
989 MCIMidiDev[wDevID].dwStatus = MCI_MODE_PLAY;
991 if (lpParms && (dwFlags & MCI_NOTIFY)) {
992 TRACE(midi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
993 mciDriverNotify((HWND16)LOWORD(lpParms->dwCallback),
994 MCIMidiDev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
996 return 0;
999 /**************************************************************************
1000 * MIDI_mciSet [internal]
1002 static DWORD MIDI_mciSet(UINT16 wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1004 TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1006 if (lpParms == NULL) return MCIERR_INTERNAL;
1008 if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) {
1009 WARN(midi, "Invalid wDevID=%u\n", wDevID);
1010 return MCIERR_INVALID_DEVICE_ID;
1013 TRACE(midi, "dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
1014 TRACE(midi, "dwAudio=%08lX\n", lpParms->dwAudio);
1016 if (dwFlags & MCI_SET_TIME_FORMAT) {
1017 switch (lpParms->dwTimeFormat) {
1018 case MCI_FORMAT_MILLISECONDS:
1019 TRACE(midi, "MCI_FORMAT_MILLISECONDS !\n");
1020 break;
1021 case MCI_FORMAT_BYTES:
1022 TRACE(midi, "MCI_FORMAT_BYTES !\n");
1023 break;
1024 case MCI_FORMAT_SAMPLES:
1025 TRACE(midi, "MCI_FORMAT_SAMPLES !\n");
1026 break;
1027 default:
1028 WARN(midi, "bad time format !\n");
1029 return MCIERR_BAD_TIME_FORMAT;
1032 if (dwFlags & MCI_SET_VIDEO) {
1033 TRACE(midi, "No support for video !\n");
1034 return MCIERR_UNSUPPORTED_FUNCTION;
1036 if (dwFlags & MCI_SET_DOOR_OPEN) {
1037 TRACE(midi, "No support for door open !\n");
1038 return MCIERR_UNSUPPORTED_FUNCTION;
1040 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1041 TRACE(midi, "No support for door close !\n");
1042 return MCIERR_UNSUPPORTED_FUNCTION;
1045 if (dwFlags && MCI_SET_ON) {
1046 TRACE(midi, "MCI_SET_ON !\n");
1047 if (dwFlags & MCI_SET_AUDIO) {
1048 TRACE(midi, "MCI_SET_AUDIO !\n");
1050 if (dwFlags && MCI_SET_AUDIO_LEFT)
1051 TRACE(midi, "MCI_SET_AUDIO_LEFT !\n");
1052 if (dwFlags && MCI_SET_AUDIO_RIGHT)
1053 TRACE(midi, "MCI_SET_AUDIO_RIGHT !\n");
1055 if (dwFlags & MCI_SET_OFF) {
1056 TRACE(midi, "MCI_SET_OFF !\n");
1057 if (dwFlags & MCI_SET_AUDIO) {
1058 TRACE(midi, "MCI_SET_AUDIO !\n");
1060 if (dwFlags && MCI_SET_AUDIO_LEFT)
1061 TRACE(midi, "MCI_SET_AUDIO_LEFT !\n");
1062 if (dwFlags && MCI_SET_AUDIO_RIGHT)
1063 TRACE(midi, "MCI_SET_AUDIO_RIGHT !\n");
1065 if (dwFlags & MCI_SEQ_SET_MASTER)
1066 TRACE(midi, "MCI_SEQ_SET_MASTER !\n");
1067 if (dwFlags & MCI_SEQ_SET_SLAVE)
1068 TRACE(midi, "MCI_SEQ_SET_SLAVE !\n");
1069 if (dwFlags & MCI_SEQ_SET_OFFSET)
1070 TRACE(midi, "MCI_SEQ_SET_OFFSET !\n");
1071 if (dwFlags & MCI_SEQ_SET_PORT)
1072 TRACE(midi, "MCI_SEQ_SET_PORT !\n");
1073 if (dwFlags & MCI_SEQ_SET_TEMPO)
1074 TRACE(midi, "MCI_SEQ_SET_TEMPO !\n");
1075 return 0;
1078 /**************************************************************************
1079 * MIDI_mciStatus [internal]
1081 static DWORD MIDI_mciStatus(UINT16 wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1083 TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1085 if (lpParms == NULL) return MCIERR_INTERNAL;
1087 if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) {
1088 WARN(midi, "Invalid wDevID=%u\n", wDevID);
1089 return MCIERR_INVALID_DEVICE_ID;
1092 if (dwFlags & MCI_STATUS_ITEM) {
1093 switch (lpParms->dwItem) {
1094 case MCI_STATUS_CURRENT_TRACK:
1095 TRACE(midi, "MCI_STATUS_CURRENT_TRACK !\n");
1096 lpParms->dwReturn = 1;
1097 break;
1098 case MCI_STATUS_LENGTH:
1099 TRACE(midi, "MCI_STATUS_LENGTH !\n");
1101 WORD nt;
1102 DWORD ret = 0;
1104 for (nt = 0; nt < MCIMidiDev[wDevID].nTracks; nt++) {
1105 if (MCIMidiDev[wDevID].wFormat == 2) {
1106 ret += MCIMidiDev[wDevID].tracks[nt].dwLength;
1107 } else {
1108 if (MCIMidiDev[wDevID].tracks[nt].dwLength > ret)
1109 ret = MCIMidiDev[wDevID].tracks[nt].dwLength;
1112 lpParms->dwReturn = MIDI_ConvertPulseToMS(wDevID, ret);
1114 break;
1115 case MCI_STATUS_MODE:
1116 TRACE(midi, "MCI_STATUS_MODE !\n");
1117 lpParms->dwReturn = MCIMidiDev[wDevID].dwStatus;
1118 break;
1119 case MCI_STATUS_MEDIA_PRESENT:
1120 TRACE(midi, "MCI_STATUS_MEDIA_PRESENT !\n");
1121 lpParms->dwReturn = TRUE;
1122 break;
1123 case MCI_STATUS_NUMBER_OF_TRACKS:
1124 TRACE(midi, "MCI_STATUS_NUMBER_OF_TRACKS !\n");
1125 lpParms->dwReturn = 1; /* FIXME: except in format 2 */
1126 break;
1127 case MCI_STATUS_POSITION:
1128 TRACE(midi, "MCI_STATUS_POSITION !\n");
1129 if (dwFlags & MCI_STATUS_START) {
1130 lpParms->dwReturn = 0;
1131 } else {
1132 lpParms->dwReturn = MCIMidiDev[wDevID].dwPositionMS;
1134 break;
1135 case MCI_STATUS_READY:
1136 TRACE(midi, "MCI_STATUS_READY !\n");
1137 lpParms->dwReturn = TRUE;
1138 break;
1139 case MCI_STATUS_TIME_FORMAT:
1140 TRACE(midi, "MCI_STATUS_TIME_FORMAT !\n");
1141 lpParms->dwReturn = MCI_FORMAT_MILLISECONDS;
1142 break;
1143 case MCI_SEQ_STATUS_DIVTYPE:
1144 TRACE(midi, "MCI_SEQ_STATUS_DIVTYPE !\n");
1145 lpParms->dwReturn = MCI_SEQ_DIV_PPQN;
1146 break;
1147 case MCI_SEQ_STATUS_MASTER:
1148 TRACE(midi, "MCI_SEQ_STATUS_MASTER !\n");
1149 lpParms->dwReturn = 0;
1150 break;
1151 case MCI_SEQ_STATUS_SLAVE:
1152 TRACE(midi, "MCI_SEQ_STATUS_SLAVE !\n");
1153 lpParms->dwReturn = 0;
1154 break;
1155 case MCI_SEQ_STATUS_OFFSET:
1156 TRACE(midi, "MCI_SEQ_STATUS_OFFSET !\n");
1157 lpParms->dwReturn = 0;
1158 break;
1159 case MCI_SEQ_STATUS_PORT:
1160 TRACE(midi, "MCI_SEQ_STATUS_PORT !\n");
1161 lpParms->dwReturn = 0;
1162 break;
1163 case MCI_SEQ_STATUS_TEMPO:
1164 TRACE(midi, "MCI_SEQ_STATUS_TEMPO !\n");
1165 lpParms->dwReturn = MCIMidiDev[wDevID].dwTempo;
1166 break;
1167 default:
1168 WARN(midi, "unknowm command %08lX !\n", lpParms->dwItem);
1169 return MCIERR_UNRECOGNIZED_COMMAND;
1171 } else {
1172 WARN(midi, "No Status-Item!\n");
1173 return MCIERR_UNRECOGNIZED_COMMAND;
1175 if (dwFlags & MCI_NOTIFY) {
1176 TRACE(midi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1177 mciDriverNotify((HWND16)LOWORD(lpParms->dwCallback),
1178 MCIMidiDev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1180 return 0;
1183 /**************************************************************************
1184 * MIDI_mciGetDevCaps [internal]
1186 static DWORD MIDI_mciGetDevCaps(UINT16 wDevID, DWORD dwFlags,
1187 LPMCI_GETDEVCAPS_PARMS lpParms)
1189 TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1191 if (lpParms == NULL) return MCIERR_INTERNAL;
1193 if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) {
1194 WARN(midi, "Invalid wDevID=%u\n", wDevID);
1195 return MCIERR_INVALID_DEVICE_ID;
1198 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1199 switch (lpParms->dwItem) {
1200 case MCI_GETDEVCAPS_CAN_RECORD:
1201 TRACE(midi, "MCI_GETDEVCAPS_CAN_RECORD !\n");
1202 lpParms->dwReturn = TRUE;
1203 break;
1204 case MCI_GETDEVCAPS_HAS_AUDIO:
1205 TRACE(midi, "MCI_GETDEVCAPS_HAS_AUDIO !\n");
1206 lpParms->dwReturn = TRUE;
1207 break;
1208 case MCI_GETDEVCAPS_HAS_VIDEO:
1209 TRACE(midi, "MCI_GETDEVCAPS_HAS_VIDEO !\n");
1210 lpParms->dwReturn = FALSE;
1211 break;
1212 case MCI_GETDEVCAPS_DEVICE_TYPE:
1213 TRACE(midi, "MCI_GETDEVCAPS_DEVICE_TYPE !\n");
1214 lpParms->dwReturn = MCI_DEVTYPE_SEQUENCER;
1215 break;
1216 case MCI_GETDEVCAPS_USES_FILES:
1217 TRACE(midi, "MCI_GETDEVCAPS_USES_FILES !\n");
1218 lpParms->dwReturn = TRUE;
1219 break;
1220 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1221 TRACE(midi, "MCI_GETDEVCAPS_COMPOUND_DEVICE !\n");
1222 lpParms->dwReturn = TRUE;
1223 break;
1224 case MCI_GETDEVCAPS_CAN_EJECT:
1225 TRACE(midi, "MCI_GETDEVCAPS_CAN_EJECT !\n");
1226 lpParms->dwReturn = FALSE;
1227 break;
1228 case MCI_GETDEVCAPS_CAN_PLAY:
1229 TRACE(midi, "MCI_GETDEVCAPS_CAN_PLAY !\n");
1230 lpParms->dwReturn = TRUE;
1231 break;
1232 case MCI_GETDEVCAPS_CAN_SAVE:
1233 TRACE(midi, "MCI_GETDEVCAPS_CAN_SAVE !\n");
1234 lpParms->dwReturn = FALSE;
1235 break;
1236 default:
1237 TRACE(midi, "Unknown capability (%08lx) !\n", lpParms->dwItem);
1238 return MCIERR_UNRECOGNIZED_COMMAND;
1240 } else {
1241 TRACE(midi, "No GetDevCaps-Item !\n");
1242 return MCIERR_UNRECOGNIZED_COMMAND;
1244 return 0;
1247 /**************************************************************************
1248 * MIDI_mciInfo [internal]
1250 static DWORD MIDI_mciInfo(UINT16 wDevID, DWORD dwFlags, LPMCI_INFO_PARMS16 lpParms)
1252 TRACE(midi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1254 if (lpParms == NULL) return MCIERR_INTERNAL;
1256 if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) {
1257 WARN(midi, "Invalid wDevID=%u\n", wDevID);
1258 return MCIERR_INVALID_DEVICE_ID;
1261 lpParms->lpstrReturn = NULL;
1263 switch (dwFlags) {
1264 case MCI_INFO_PRODUCT:
1265 lpParms->lpstrReturn = "Linux Sound System 0.5";
1266 break;
1267 case MCI_INFO_FILE:
1268 lpParms->lpstrReturn = "FileName";
1269 break;
1270 default:
1271 return MCIERR_UNRECOGNIZED_COMMAND;
1273 lpParms->dwRetSize = (lpParms->lpstrReturn != NULL) ? strlen(lpParms->lpstrReturn) : 0;
1274 return 0;
1277 /*-----------------------------------------------------------------------*/
1279 #ifdef HAVE_OSS
1280 /**************************************************************************
1281 * midiOpenSeq [internal]
1283 static int midiOpenSeq(void)
1285 if (numOpenMidiSeq == 0) {
1286 midiSeqFD = open(MIDI_SEQ, O_RDWR, 0);
1287 if (midiSeqFD == -1) {
1288 ERR(midi, "can't open '%s' ! (%d)\n", MIDI_SEQ, errno);
1289 return -1;
1291 if (fcntl(midiSeqFD, F_SETFL, O_NONBLOCK) < 0) {
1292 WARN(midi, "can't set sequencer fd to non blocking (%d)\n", errno);
1293 close(midiSeqFD);
1294 midiSeqFD = -1;
1295 return -1;
1297 ioctl(midiSeqFD, SNDCTL_SEQ_RESET);
1299 numOpenMidiSeq++;
1300 return 0;
1303 /**************************************************************************
1304 * midiCloseSeq [internal]
1306 static int midiCloseSeq(void)
1308 if (--numOpenMidiSeq == 0) {
1309 close(midiSeqFD);
1310 midiSeqFD = -1;
1312 return 0;
1315 /* FIXME: this is a bad idea, it's even not static... */
1316 SEQ_DEFINEBUF(1024);
1318 /* FIXME: this is not reentrant, not static - because of global variable
1319 * _seqbuf and al. */
1320 /**************************************************************************
1321 * seqbuf_dump [internal]
1323 void seqbuf_dump(void)
1325 if (_seqbufptr) {
1326 if (write(midiSeqFD, _seqbuf, _seqbufptr) == -1) {
1327 WARN(midi, "Can't write data to sequencer (%d/%d)!\n",
1328 midiSeqFD, errno);
1330 /* FIXME:
1331 * in any case buffer is lost so that if many errors occur the buffer
1332 * will not overrun
1334 _seqbufptr = 0;
1337 #endif /* HAVE_OSS */
1339 #ifdef HAVE_OSS
1341 static void midReceiveChar(WORD wDevID, unsigned char value, DWORD dwTime)
1343 DWORD toSend = 0;
1345 TRACE(midi, "Adding %02xh to %d[%d]\n", value, wDevID, MidiInDev[wDevID].incLen);
1347 if (wDevID >= MAX_MIDIINDRV) {
1348 WARN(midi, "bad devID\n");
1349 return;
1351 if (MidiInDev[wDevID].state == 0) {
1352 TRACE(midi, "input not started, thrown away\n");
1353 return;
1356 if (MidiInDev[wDevID].state & 2) { /* system exclusive */
1357 LPMIDIHDR ptr = MidiInDev[wDevID].lpQueueHdr;
1358 WORD sbfb = FALSE;
1360 if (ptr) {
1361 ((LPSTR)(ptr->reserved))[ptr->dwBytesRecorded++] = value;
1362 if (ptr->dwBytesRecorded == ptr->dwBufferLength) {
1363 sbfb = TRUE;
1366 if (value == 0xF7) { /* then end */
1367 MidiInDev[wDevID].state &= ~2;
1368 sbfb = TRUE;
1370 if (sbfb && ptr != NULL) {
1371 ptr = MidiInDev[wDevID].lpQueueHdr;
1372 ptr->dwFlags &= ~MHDR_INQUEUE;
1373 ptr->dwFlags |= MHDR_DONE;
1374 MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)ptr->lpNext;
1375 if (MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD)ptr, dwTime) != MMSYSERR_NOERROR) {
1376 WARN(midi, "Couldn't notify client\n");
1379 return;
1382 #define IS_CMD(_x) (((_x) & 0x80) == 0x80)
1383 #define IS_SYS_CMD(_x) (((_x) & 0xF0) == 0xF0)
1385 if (!IS_CMD(value) && MidiInDev[wDevID].incLen == 0) { /* try to reuse old cmd */
1386 if (IS_CMD(MidiInDev[wDevID].incPrev) && !IS_SYS_CMD(MidiInDev[wDevID].incPrev)) {
1387 MidiInDev[wDevID].incoming[0] = MidiInDev[wDevID].incPrev;
1388 MidiInDev[wDevID].incLen = 1;
1389 TRACE(midi, "Reusing old command %02xh\n", MidiInDev[wDevID].incPrev);
1390 } else {
1391 FIXME(midi, "error for midi-in, should generate MIM_ERROR notification:"
1392 " prev=%02Xh, incLen=%02Xh\n",
1393 MidiInDev[wDevID].incPrev, MidiInDev[wDevID].incLen);
1394 return;
1397 MidiInDev[wDevID].incoming[(int)(MidiInDev[wDevID].incLen++)] = value;
1398 if (MidiInDev[wDevID].incLen == 1 && !IS_SYS_CMD(MidiInDev[wDevID].incoming[0])) {
1399 /* store new cmd, just in case */
1400 MidiInDev[wDevID].incPrev = MidiInDev[wDevID].incoming[0];
1403 #undef IS_CMD(_x)
1404 #undef IS_SYS_CMD(_x)
1406 switch (MidiInDev[wDevID].incoming[0] & 0xF0) {
1407 case MIDI_NOTEOFF:
1408 case MIDI_NOTEON:
1409 case MIDI_KEY_PRESSURE:
1410 case MIDI_CTL_CHANGE:
1411 case MIDI_PITCH_BEND:
1412 if (MidiInDev[wDevID].incLen == 3) {
1413 toSend = (MidiInDev[wDevID].incoming[2] << 16) |
1414 (MidiInDev[wDevID].incoming[1] << 8) |
1415 (MidiInDev[wDevID].incoming[0] << 0);
1417 break;
1418 case MIDI_PGM_CHANGE:
1419 case MIDI_CHN_PRESSURE:
1420 if (MidiInDev[wDevID].incLen == 2) {
1421 toSend = (MidiInDev[wDevID].incoming[1] << 8) |
1422 (MidiInDev[wDevID].incoming[0] << 0);
1424 break;
1425 case MIDI_SYSTEM_PREFIX:
1426 if (MidiInDev[wDevID].incoming[0] == 0xF0) {
1427 MidiInDev[wDevID].state |= 2;
1428 MidiInDev[wDevID].incLen = 0;
1429 } else {
1430 if (MidiInDev[wDevID].incLen == 1) {
1431 toSend = (MidiInDev[wDevID].incoming[0] << 0);
1434 break;
1435 default:
1436 WARN(midi, "This shouldn't happen (%02X)\n", MidiInDev[wDevID].incoming[0]);
1438 if (toSend != 0) {
1439 TRACE(midi, "Sending event %08lx\n", toSend);
1440 MidiInDev[wDevID].incLen = 0;
1441 dwTime -= MidiInDev[wDevID].startTime;
1442 if (MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime) != MMSYSERR_NOERROR) {
1443 WARN(midi, "Couldn't notify client\n");
1448 static VOID WINAPI midTimeCallback(HWND32 hwnd, UINT32 msg, UINT32 id, DWORD dwTime)
1450 unsigned char buffer[256];
1451 int len, idx;
1453 TRACE(midi, "(%04X, %d, %d, %lu)\n", hwnd, msg, id, dwTime);
1455 len = read(midiSeqFD, buffer, sizeof(buffer));
1457 if ((len % 4) != 0) {
1458 WARN(midi, "bad length %d (%d)\n", len, errno);
1459 return;
1462 for (idx = 0; idx < len; ) {
1463 if (buffer[idx] & 0x80) {
1464 TRACE(midi,
1465 "reading<8> %02x %02x %02x %02x %02x %02x %02x %02x\n",
1466 buffer[idx + 0], buffer[idx + 1],
1467 buffer[idx + 2], buffer[idx + 3],
1468 buffer[idx + 4], buffer[idx + 5],
1469 buffer[idx + 6], buffer[idx + 7]);
1470 idx += 8;
1471 } else {
1472 switch (buffer[idx + 0]) {
1473 case SEQ_WAIT:
1474 case SEQ_ECHO:
1475 break;
1476 case SEQ_MIDIPUTC:
1477 midReceiveChar(buffer[idx + 2], buffer[idx + 1], dwTime);
1478 break;
1479 default:
1480 TRACE(midi, "Unsupported event %d\n", buffer[idx + 0]);
1481 break;
1483 idx += 4;
1487 #endif /* HAVE_OSS */
1489 /**************************************************************************
1490 * midGetDevCaps [internal]
1492 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPS16 lpCaps, DWORD dwSize)
1494 LPMIDIINCAPS16 tmplpCaps;
1496 TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize);
1498 if (wDevID >= MIDM_NUMDEVS) {
1499 return MMSYSERR_BADDEVICEID;
1501 if (lpCaps == NULL) {
1502 return MMSYSERR_INVALPARAM;
1505 tmplpCaps = midiInDevices[wDevID];
1506 lpCaps->wMid = tmplpCaps->wMid;
1507 lpCaps->wPid = tmplpCaps->wPid;
1508 lpCaps->vDriverVersion = tmplpCaps->vDriverVersion;
1509 strcpy(lpCaps->szPname, tmplpCaps->szPname);
1510 if (dwSize == sizeof(MIDIINCAPS16)) {
1511 /* we should run win 95, so make use of dwSupport */
1512 lpCaps->dwSupport = tmplpCaps->dwSupport;
1513 } else if (dwSize != sizeof(MIDIINCAPS16) - sizeof(DWORD)) {
1514 TRACE(midi, "bad size for lpCaps\n");
1515 return MMSYSERR_INVALPARAM;
1518 return MMSYSERR_NOERROR;
1521 /**************************************************************************
1522 * midOpen [internal]
1524 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
1526 TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1528 if (lpDesc == NULL) {
1529 WARN(midi, "Invalid Parameter !\n");
1530 return MMSYSERR_INVALPARAM;
1532 /* FIXME :
1533 * how to check that content of lpDesc is correct ?
1535 if (wDevID >= MAX_MIDIINDRV) {
1536 WARN(midi,"wDevID too large (%u) !\n", wDevID);
1537 return MMSYSERR_BADDEVICEID;
1539 if (MidiInDev[wDevID].midiDesc != 0) {
1540 WARN(midi, "device already open !\n");
1541 return MMSYSERR_ALLOCATED;
1543 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
1544 FIXME(midi, "No support for MIDI_IO_STATUS in dwFlags\n");
1545 return MMSYSERR_INVALFLAG;
1548 #ifdef HAVE_OSS
1549 if (midiOpenSeq() < 0) {
1550 return MMSYSERR_ERROR;
1553 if (numStartedMidiIn++ == 0) {
1554 midiInTimerID = SetTimer32(0, 0, 250, midTimeCallback);
1555 if (!midiInTimerID) {
1556 numStartedMidiIn = 0;
1557 WARN(midi, "Couldn't start timer for midi-in\n");
1558 midiCloseSeq();
1559 return MMSYSERR_ERROR;
1561 TRACE(midi, "Starting timer (%u) for midi-in\n", midiInTimerID);
1563 #else /* HAVE_OSS */
1565 int midi = open(MIDI_DEV, O_WRONLY, 0);
1567 MidiInDev[wDevID].unixdev = 0;
1568 if (midi == -1) {
1569 WARN(midi,"can't open '%s' (%d)!\n", MIDI_DEV, errno);
1570 return MMSYSERR_ALLOCATED;
1572 MidiInDev[wDevID].unixdev = midi;
1574 #endif /* HAVE_OSS */
1576 MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1577 switch (MidiInDev[wDevID].wFlags) {
1578 case DCB_NULL:
1579 TRACE(midi,"CALLBACK_NULL !\n");
1580 break;
1581 case DCB_WINDOW:
1582 TRACE(midi, "CALLBACK_WINDOW !\n");
1583 break;
1584 case DCB_TASK:
1585 TRACE(midi, "CALLBACK_TASK !\n");
1586 break;
1587 case DCB_FUNCTION:
1588 TRACE(midi, "CALLBACK_FUNCTION (%08lX)!\n", lpDesc->dwCallback);
1589 break;
1591 MidiInDev[wDevID].lpQueueHdr = NULL;
1592 MidiInDev[wDevID].dwTotalPlayed = 0;
1593 MidiInDev[wDevID].bufsize = 0x3FFF;
1594 MidiInDev[wDevID].midiDesc = lpDesc;
1595 MidiInDev[wDevID].state = 0;
1596 #ifdef HAVE_OSS
1597 MidiInDev[wDevID].incLen = 0;
1598 MidiInDev[wDevID].startTime = 0;
1599 #endif /* HAVE_OSS */
1600 if (MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1601 WARN(midi,"can't notify client !\n");
1602 return MMSYSERR_INVALPARAM;
1604 return MMSYSERR_NOERROR;
1607 /**************************************************************************
1608 * midClose [internal]
1610 static DWORD midClose(WORD wDevID)
1612 int ret = MMSYSERR_NOERROR;
1614 TRACE(midi, "(%04X);\n", wDevID);
1616 if (wDevID >= MAX_MIDIINDRV) {
1617 WARN(midi,"wDevID too bif (%u) !\n", wDevID);
1618 return MMSYSERR_BADDEVICEID;
1620 if (MidiInDev[wDevID].midiDesc == 0) {
1621 WARN(midi, "device not opened !\n");
1622 return MMSYSERR_ERROR;
1624 if (MidiInDev[wDevID].lpQueueHdr != 0) {
1625 return MIDIERR_STILLPLAYING;
1628 #ifdef HAVE_OSS
1629 if (midiSeqFD == -1) {
1630 WARN(midi,"ooops !\n");
1631 return MMSYSERR_ERROR;
1633 if (--numStartedMidiIn == 0) {
1634 TRACE(midi, "Stopping timer for midi-in\n");
1635 if (!KillTimer32(0, midiInTimerID)) {
1636 WARN(midi, "Couldn't stop timer for midi-in\n");
1638 midiInTimerID = 0;
1640 midiCloseSeq();
1641 #else /* HAVE_OSS */
1642 if (MidiInDev[wDevID].unixdev == 0) {
1643 WARN(midi,"ooops !\n");
1644 return MMSYSERR_ERROR;
1646 close(MidiInDev[wDevID].unixdev);
1647 MidiInDev[wDevID].unixdev = 0;
1648 #endif /* HAVE_OSS */
1649 MidiInDev[wDevID].bufsize = 0;
1650 if (MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
1651 WARN(midi,"can't notify client !\n");
1652 ret = MMSYSERR_INVALPARAM;
1654 MidiInDev[wDevID].midiDesc = 0;
1655 return ret;
1658 /**************************************************************************
1659 * midAddBuffer [internal]
1661 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1663 TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1665 if (lpMidiHdr == NULL) return MMSYSERR_INVALPARAM;
1666 if (sizeof(MIDIHDR) != dwSize) return MMSYSERR_INVALPARAM;
1667 if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
1668 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
1669 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
1671 if (MidiInDev[wDevID].lpQueueHdr == 0) {
1672 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
1673 } else {
1674 LPMIDIHDR ptr;
1676 for (ptr = MidiInDev[wDevID].lpQueueHdr;
1677 ptr->lpNext != 0;
1678 ptr = (LPMIDIHDR)ptr->lpNext);
1679 ptr->lpNext = (struct midihdr_tag*)lpMidiHdr;
1681 return MMSYSERR_NOERROR;
1684 /**************************************************************************
1685 * midPrepare [internal]
1687 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1689 TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1691 if (dwSize != sizeof(MIDIHDR) || lpMidiHdr == 0 ||
1692 lpMidiHdr->lpData == 0 || lpMidiHdr->dwFlags != 0 ||
1693 lpMidiHdr->dwBufferLength >= 0x10000ul)
1694 return MMSYSERR_INVALPARAM;
1696 lpMidiHdr->lpNext = 0;
1697 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1698 lpMidiHdr->dwBytesRecorded = 0;
1700 return MMSYSERR_NOERROR;
1703 /**************************************************************************
1704 * midUnprepare [internal]
1706 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1708 TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
1710 if (dwSize != sizeof(MIDIHDR) || lpMidiHdr == 0 ||
1711 lpMidiHdr->lpData == 0 || lpMidiHdr->dwBufferLength >= 0x10000ul)
1712 return MMSYSERR_INVALPARAM;
1714 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
1715 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
1717 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1719 return MMSYSERR_NOERROR;
1722 /**************************************************************************
1723 * midReset [internal]
1725 static DWORD midReset(WORD wDevID)
1727 DWORD dwTime = GetTickCount();
1729 TRACE(midi, "(%04X);\n", wDevID);
1731 while (MidiInDev[wDevID].lpQueueHdr) {
1732 MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
1733 MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
1734 if (MIDI_NotifyClient(wDevID, MIM_LONGDATA,
1735 (DWORD)MidiInDev[wDevID].lpQueueHdr, dwTime) != MMSYSERR_NOERROR) {
1736 WARN(midi, "Couldn't notify client\n");
1738 MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)MidiInDev[wDevID].lpQueueHdr->lpNext;
1741 return MMSYSERR_NOERROR;
1745 /**************************************************************************
1746 * midStart [internal]
1748 static DWORD midStart(WORD wDevID)
1750 TRACE(midi, "(%04X);\n", wDevID);
1752 /* FIXME : should test value of wDevID */
1754 #ifdef HAVE_OSS
1755 MidiInDev[wDevID].state = 1;
1756 MidiInDev[wDevID].startTime = GetTickCount();
1757 return MMSYSERR_NOERROR;
1758 #else
1759 return MMSYSERR_NOTENABLED;
1760 #endif /* HAVE_OSS */
1763 /**************************************************************************
1764 * midStop [internal]
1766 static DWORD midStop(WORD wDevID)
1768 TRACE(midi, "(%04X);\n", wDevID);
1770 /* FIXME : should test value of wDevID */
1772 #ifdef HAVE_OSS
1773 MidiInDev[wDevID].state = 0;
1774 return MMSYSERR_NOERROR;
1775 #else /* HAVE_OSS */
1776 return MMSYSERR_NOTENABLED;
1777 #endif /* HAVE_OSS */
1780 /**************************************************************************
1781 * midMessage [sample driver]
1783 DWORD WINAPI midMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1784 DWORD dwParam1, DWORD dwParam2)
1786 TRACE(midi, "(%04X, %04X, %08lX, %08lX, %08lX);\n",
1787 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1788 switch (wMsg) {
1789 case MIDM_OPEN:
1790 return midOpen(wDevID,(LPMIDIOPENDESC)dwParam1, dwParam2);
1791 case MIDM_CLOSE:
1792 return midClose(wDevID);
1793 case MIDM_ADDBUFFER:
1794 return midAddBuffer(wDevID,(LPMIDIHDR)dwParam1, dwParam2);
1795 case MIDM_PREPARE:
1796 return midPrepare(wDevID,(LPMIDIHDR)dwParam1, dwParam2);
1797 case MIDM_UNPREPARE:
1798 return midUnprepare(wDevID,(LPMIDIHDR)dwParam1, dwParam2);
1799 case MIDM_GETDEVCAPS:
1800 return midGetDevCaps(wDevID,(LPMIDIINCAPS16)dwParam1,dwParam2);
1801 case MIDM_GETNUMDEVS:
1802 return MIDM_NUMDEVS;
1803 case MIDM_RESET:
1804 return midReset(wDevID);
1805 case MIDM_START:
1806 return midStart(wDevID);
1807 case MIDM_STOP:
1808 return midStop(wDevID);
1809 default:
1810 TRACE(midi, "Unsupported message\n");
1812 return MMSYSERR_NOTSUPPORTED;
1815 /*-----------------------------------------------------------------------*/
1817 #ifdef HAVE_OSS
1819 typedef struct sVoice {
1820 int note; /* 0 means not used */
1821 int channel;
1822 unsigned cntMark : 30,
1823 status : 2;
1824 #define sVS_UNUSED 0
1825 #define sVS_PLAYING 1
1826 #define sVS_SUSTAINED 2
1827 } sVoice;
1829 typedef struct sChannel {
1830 int program;
1832 int bender;
1833 int benderRange;
1834 /* controlers */
1835 int bank; /* CTL_BANK_SELECT */
1836 int volume; /* CTL_MAIN_VOLUME */
1837 int balance; /* CTL_BALANCE */
1838 int expression; /* CTL_EXPRESSION */
1839 int sustain; /* CTL_SUSTAIN */
1841 unsigned char nrgPmtMSB; /* Non register Parameters */
1842 unsigned char nrgPmtLSB;
1843 unsigned char regPmtMSB; /* Non register Parameters */
1844 unsigned char regPmtLSB;
1845 } sChannel;
1847 typedef struct sFMextra {
1848 unsigned counter;
1849 int drumSetMask;
1850 sChannel channel[16]; /* MIDI has only 16 channels */
1851 sVoice voice[1]; /* dyn allocated according to sound card */
1852 /* do not append fields below voice[1] since the size of this structure
1853 * depends on the number of available voices on the FM synth...
1855 } sFMextra;
1857 extern unsigned char midiFMInstrumentPatches[16 * 128];
1858 extern unsigned char midiFMDrumsPatches [16 * 128];
1860 /**************************************************************************
1861 * modFMLoad [internal]
1863 static int modFMLoad(int dev)
1865 int i;
1866 struct sbi_instrument sbi;
1868 sbi.device = dev;
1869 sbi.key = FM_PATCH;
1871 memset(sbi.operators + 16, 0, 16);
1872 for (i = 0; i < 128; i++) {
1873 sbi.channel = i;
1874 memcpy(sbi.operators, midiFMInstrumentPatches + i * 16, 16);
1876 if (write(midiSeqFD, (char*)&sbi, sizeof(sbi)) == -1) {
1877 WARN(midi, "Couldn't write patch for instrument %d (%d)!\n", sbi.channel, errno);
1878 return -1;
1881 for (i = 0; i < 128; i++) {
1882 sbi.channel = 128 + i;
1883 memcpy(sbi.operators, midiFMDrumsPatches + i * 16, 16);
1885 if (write(midiSeqFD, (char*)&sbi, sizeof(sbi)) == -1) {
1886 WARN(midi, "Couldn't write patch for drum %d (%d)!\n", sbi.channel, errno);
1887 return -1;
1890 return 0;
1893 /**************************************************************************
1894 * modFMReset [internal]
1896 static void modFMReset(WORD wDevID)
1898 sFMextra* extra = (sFMextra*)MidiOutDev[wDevID].lpExtra;
1899 sVoice* voice = extra->voice;
1900 sChannel* channel = extra->channel;
1901 int i;
1903 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
1904 if (voice[i].status != sVS_UNUSED) {
1905 SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
1907 SEQ_KEY_PRESSURE(wDevID, i, 127, 0);
1908 SEQ_CONTROL(wDevID, i, SEQ_VOLMODE, VOL_METHOD_LINEAR);
1909 voice[i].note = 0;
1910 voice[i].channel = -1;
1911 voice[i].cntMark = 0;
1912 voice[i].status = sVS_UNUSED;
1914 for (i = 0; i < 16; i++) {
1915 channel[i].program = 0;
1916 channel[i].bender = 8192;
1917 channel[i].benderRange = 2;
1918 channel[i].bank = 0;
1919 channel[i].volume = 127;
1920 channel[i].balance = 64;
1921 channel[i].expression = 0;
1922 channel[i].sustain = 0;
1924 extra->counter = 0;
1925 extra->drumSetMask = 1 << 9; /* channel 10 is normally drums, sometimes 16 also */
1926 SEQ_DUMPBUF();
1929 #define IS_DRUM_CHANNEL(_xtra, _chn) ((_xtra)->drumSetMask & (1 << (_chn)))
1931 #endif /* HAVE_OSS */
1933 /**************************************************************************
1934 * modGetDevCaps [internal]
1936 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPS16 lpCaps,
1937 DWORD dwSize)
1939 TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize);
1940 if (wDevID == (WORD) MIDI_MAPPER) {
1941 lpCaps->wMid = 0x00FF; /* Manufac ID */
1942 lpCaps->wPid = 0x0001; /* Product ID */
1943 lpCaps->vDriverVersion = 0x001; /* Product Version */
1944 strcpy(lpCaps->szPname, "MIDI Mapper (not functional yet)");
1945 /* FIXME Does it make any difference ? */
1946 lpCaps->wTechnology = MOD_FMSYNTH;
1947 lpCaps->wVoices = 14; /* FIXME */
1948 lpCaps->wNotes = 14; /* FIXME */
1949 /* FIXME Does it make any difference ? */
1950 lpCaps->dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1951 } else {
1952 LPMIDIOUTCAPS16 tmplpCaps;
1954 if (wDevID >= MODM_NUMDEVS) {
1955 TRACE(midi, "MAX_MIDIOUTDRV reached !\n");
1956 return MMSYSERR_BADDEVICEID;
1958 /* FIXME There is a way to do it so easily, but I'm too
1959 * sleepy to think and I want to test
1962 tmplpCaps = midiOutDevices[wDevID];
1963 lpCaps->wMid = tmplpCaps->wMid;
1964 lpCaps->wPid = tmplpCaps->wPid;
1965 lpCaps->vDriverVersion = tmplpCaps->vDriverVersion;
1966 strcpy(lpCaps->szPname, tmplpCaps->szPname);
1967 lpCaps->wTechnology = tmplpCaps->wTechnology;
1968 lpCaps->wVoices = tmplpCaps->wVoices;
1969 lpCaps->wNotes = tmplpCaps->wNotes;
1970 lpCaps->dwSupport = tmplpCaps->dwSupport;
1972 return MMSYSERR_NOERROR;
1975 /**************************************************************************
1976 * modOpen [internal]
1978 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
1980 TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1981 if (lpDesc == NULL) {
1982 WARN(midi, "Invalid Parameter !\n");
1983 return MMSYSERR_INVALPARAM;
1985 if (wDevID >= MAX_MIDIOUTDRV) {
1986 TRACE(midi,"MAX_MIDIOUTDRV reached !\n");
1987 return MMSYSERR_BADDEVICEID;
1989 if (MidiOutDev[wDevID].midiDesc != 0) {
1990 WARN(midi, "device already open !\n");
1991 return MMSYSERR_ALLOCATED;
1993 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
1994 WARN(midi, "bad dwFlags\n");
1995 return MMSYSERR_INVALFLAG;
1998 #ifdef HAVE_OSS
1999 MidiOutDev[wDevID].lpExtra = 0;
2001 switch (midiOutDevices[wDevID]->wTechnology) {
2002 case MOD_FMSYNTH:
2004 void* extra = xmalloc(sizeof(struct sFMextra) +
2005 sizeof(struct sVoice) *
2006 (midiOutDevices[wDevID]->wVoices - 1));
2008 if (extra == 0) {
2009 WARN(midi, "can't alloc extra data !\n");
2010 return MMSYSERR_NOMEM;
2012 MidiOutDev[wDevID].lpExtra = extra;
2013 if (midiOpenSeq() < 0) {
2014 MidiOutDev[wDevID].lpExtra = 0;
2015 free(extra);
2016 return MMSYSERR_ERROR;
2018 if (modFMLoad(wDevID) < 0) {
2019 midiCloseSeq();
2020 MidiOutDev[wDevID].lpExtra = 0;
2021 free(extra);
2022 return MMSYSERR_ERROR;
2024 modFMReset(wDevID);
2026 break;
2027 case MOD_MIDIPORT:
2028 if (midiOpenSeq() < 0) {
2029 return MMSYSERR_ALLOCATED;
2031 break;
2032 default:
2033 WARN(midi,"Technology not supported (yet) %d !\n",
2034 midiOutDevices[wDevID]->wTechnology);
2035 return MMSYSERR_NOTENABLED;
2037 #else /* HAVE_OSS */
2039 int midi = open (MIDI_DEV, O_WRONLY, 0);
2040 MidiOutDev[wDevID].unixdev = 0;
2041 if (midi == -1) {
2042 WARN(midi, "can't open !\n");
2043 return MMSYSERR_ALLOCATED;
2045 MidiOutDev[wDevID].unixdev = midi;
2047 #endif /* HAVE_OSS */
2049 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
2050 switch (MidiOutDev[wDevID].wFlags) {
2051 case DCB_NULL:
2052 TRACE(midi,"CALLBACK_NULL !\n");
2053 break;
2054 case DCB_WINDOW:
2055 TRACE(midi, "CALLBACK_WINDOW !\n");
2056 break;
2057 case DCB_TASK:
2058 TRACE(midi, "CALLBACK_TASK !\n");
2059 break;
2060 case DCB_FUNCTION:
2061 TRACE(midi, "CALLBACK_FUNCTION !\n");
2062 break;
2064 MidiOutDev[wDevID].lpQueueHdr = NULL;
2065 MidiOutDev[wDevID].dwTotalPlayed = 0;
2066 MidiOutDev[wDevID].bufsize = 0x3FFF;
2067 MidiOutDev[wDevID].midiDesc = lpDesc;
2069 if (MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
2070 WARN(midi,"can't notify client !\n");
2071 return MMSYSERR_INVALPARAM;
2073 TRACE(midi, "Succesful !\n");
2074 return MMSYSERR_NOERROR;
2078 /**************************************************************************
2079 * modClose [internal]
2081 static DWORD modClose(WORD wDevID)
2083 int ret = MMSYSERR_NOERROR;
2085 TRACE(midi, "(%04X);\n", wDevID);
2087 if (MidiOutDev[wDevID].midiDesc == 0) {
2088 WARN(midi, "device not opened !\n");
2089 return MMSYSERR_ERROR;
2091 /* FIXME: should test that no pending buffer is still in the queue for
2092 * playing */
2094 #ifdef HAVE_OSS
2095 if (midiSeqFD == -1) {
2096 WARN(midi,"can't close !\n");
2097 return MMSYSERR_ERROR;
2100 switch (midiOutDevices[wDevID]->wTechnology) {
2101 case MOD_FMSYNTH:
2102 case MOD_MIDIPORT:
2103 midiCloseSeq();
2104 break;
2105 default:
2106 WARN(midi,"Technology not supported (yet) %d !\n",
2107 midiOutDevices[wDevID]->wTechnology);
2108 return MMSYSERR_NOTENABLED;
2111 if (MidiOutDev[wDevID].lpExtra != 0) {
2112 free(MidiOutDev[wDevID].lpExtra);
2113 MidiOutDev[wDevID].lpExtra = 0;
2115 #else
2116 if (MidiOutDev[wDevID].unixdev == 0) {
2117 WARN(midi,"can't close !\n");
2118 return MMSYSERR_NOTENABLED;
2120 close(MidiOutDev[wDevID].unixdev);
2121 MidiOutDev[wDevID].unixdev = 0;
2122 #endif /* HAVE_OSS */
2124 MidiOutDev[wDevID].bufsize = 0;
2125 if (MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
2126 WARN(midi,"can't notify client !\n");
2127 ret = MMSYSERR_INVALPARAM;
2129 MidiOutDev[wDevID].midiDesc = 0;
2130 return ret;
2133 /**************************************************************************
2134 * modData [internal]
2136 static DWORD modData(WORD wDevID, DWORD dwParam)
2138 WORD evt = LOBYTE(LOWORD(dwParam));
2139 WORD d1 = HIBYTE(LOWORD(dwParam));
2140 WORD d2 = LOBYTE(HIWORD(dwParam));
2142 TRACE(midi, "(%04X, %08lX);\n", wDevID, dwParam);
2144 #ifdef HAVE_OSS
2145 if (midiSeqFD == -1) {
2146 WARN(midi,"can't play !\n");
2147 return MIDIERR_NODEVICE;
2149 switch (midiOutDevices[wDevID]->wTechnology) {
2150 case MOD_FMSYNTH:
2151 /* FIXME:
2152 * - chorus depth controller is not used
2155 sFMextra* extra = (sFMextra*)MidiOutDev[wDevID].lpExtra;
2156 sVoice* voice = extra->voice;
2157 sChannel* channel = extra->channel;
2158 int chn = (evt & 0x0F);
2159 int i, nv;
2161 switch (evt & 0xF0) {
2162 case MIDI_NOTEOFF:
2163 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
2164 /* don't stop sustained notes */
2165 if (voice[i].status == sVS_PLAYING && voice[i].channel == chn && voice[i].note == d1) {
2166 voice[i].status = sVS_UNUSED;
2167 SEQ_STOP_NOTE(wDevID, i, d1, d2);
2170 break;
2171 case MIDI_NOTEON:
2172 if (d2 == 0) { /* note off if velocity == 0 */
2173 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
2174 /* don't stop sustained notes */
2175 if (voice[i].status == sVS_PLAYING && voice[i].channel == chn && voice[i].note == d1) {
2176 voice[i].status = sVS_UNUSED;
2177 SEQ_STOP_NOTE(wDevID, i, d1, 64);
2180 break;
2182 /* finding out in this order :
2183 * - an empty voice
2184 * - if replaying the same note on the same channel
2185 * - the older voice (LRU)
2187 for (i = nv = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
2188 if (voice[i].status == sVS_UNUSED ||
2189 (voice[i].note == d1 && voice[i].channel == chn)) {
2190 nv = i;
2191 break;
2193 if (voice[i].cntMark < voice[0].cntMark) {
2194 nv = i;
2197 TRACE(midi,
2198 "playing on voice=%d, pgm=%d, pan=0x%02X, vol=0x%02X, "
2199 "bender=0x%02X, note=0x%02X, vel=0x%02X\n",
2200 nv, channel[chn].program,
2201 channel[chn].balance,
2202 channel[chn].volume,
2203 channel[chn].bender, d1, d2);
2205 SEQ_SET_PATCH(wDevID, nv, IS_DRUM_CHANNEL(extra, chn) ?
2206 (128 + d1) : channel[chn].program);
2207 SEQ_BENDER_RANGE(wDevID, nv, channel[chn].benderRange * 100);
2208 SEQ_BENDER(wDevID, nv, channel[chn].bender);
2209 SEQ_CONTROL(wDevID, nv, CTL_PAN, channel[chn].balance);
2210 SEQ_CONTROL(wDevID, nv, CTL_EXPRESSION, channel[chn].expression);
2211 #if 0
2212 /* FIXME: does not really seem to work on my SB card and
2213 * screws everything up... so lay it down
2215 SEQ_CONTROL(wDevID, nv, CTL_MAIN_VOLUME, channel[chn].volume);
2216 #endif
2217 SEQ_START_NOTE(wDevID, nv, d1, d2);
2218 voice[nv].status = channel[chn].sustain ? sVS_SUSTAINED : sVS_PLAYING;
2219 voice[nv].note = d1;
2220 voice[nv].channel = chn;
2221 voice[nv].cntMark = extra->counter++;
2222 break;
2223 case MIDI_KEY_PRESSURE:
2224 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
2225 if (voice[i].status != sVS_UNUSED && voice[i].channel == chn && voice[i].note == d1) {
2226 SEQ_KEY_PRESSURE(wDevID, i, d1, d2);
2229 break;
2230 case MIDI_CTL_CHANGE:
2231 switch (d1) {
2232 case CTL_BANK_SELECT: channel[chn].bank = d2; break;
2233 case CTL_MAIN_VOLUME: channel[chn].volume = d2; break;
2234 case CTL_PAN: channel[chn].balance = d2; break;
2235 case CTL_EXPRESSION: channel[chn].expression = d2; break;
2236 case CTL_SUSTAIN: channel[chn].sustain = d2;
2237 if (d2) {
2238 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
2239 if (voice[i].status == sVS_PLAYING && voice[i].channel == chn) {
2240 voice[i].status = sVS_SUSTAINED;
2243 } else {
2244 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
2245 if (voice[i].status == sVS_SUSTAINED && voice[i].channel == chn) {
2246 voice[i].status = sVS_UNUSED;
2247 SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64);
2251 break;
2252 case CTL_NONREG_PARM_NUM_LSB: channel[chn].nrgPmtLSB = d2; break;
2253 case CTL_NONREG_PARM_NUM_MSB: channel[chn].nrgPmtMSB = d2; break;
2254 case CTL_REGIST_PARM_NUM_LSB: channel[chn].regPmtLSB = d2; break;
2255 case CTL_REGIST_PARM_NUM_MSB: channel[chn].regPmtMSB = d2; break;
2257 case CTL_DATA_ENTRY:
2258 switch ((channel[chn].regPmtMSB << 8) | channel[chn].regPmtLSB) {
2259 case 0x0000:
2260 if (channel[chn].benderRange != d2) {
2261 channel[chn].benderRange = d2;
2262 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
2263 if (voice[i].channel == chn) {
2264 SEQ_BENDER_RANGE(wDevID, i, channel[chn].benderRange);
2268 break;
2270 case 0x7F7F:
2271 channel[chn].benderRange = 2;
2272 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
2273 if (voice[i].channel == chn) {
2274 SEQ_BENDER_RANGE(wDevID, i, channel[chn].benderRange);
2277 break;
2278 default:
2279 TRACE(midi, "Data entry: regPmt=0x%02x%02x, nrgPmt=0x%02x%02x with %x\n",
2280 channel[chn].regPmtMSB, channel[chn].regPmtLSB,
2281 channel[chn].nrgPmtMSB, channel[chn].nrgPmtLSB,
2282 d2);
2283 break;
2285 break;
2287 case 0x78: /* all sounds off */
2288 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
2289 if (voice[i].status != sVS_UNUSED && voice[i].channel == chn) {
2290 voice[i].status = sVS_UNUSED;
2291 SEQ_STOP_NOTE(wDevID, i, voice[i].note, 0);
2294 break;
2295 case 0x7B: /* all notes off */
2296 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
2297 if (voice[i].status == sVS_PLAYING && voice[i].channel == chn) {
2298 voice[i].status = sVS_UNUSED;
2299 SEQ_STOP_NOTE(wDevID, i, voice[i].note, 0);
2302 break;
2303 default:
2304 TRACE(midi, "Dropping MIDI control event 0x%02x(%02x) on channel %d\n",
2305 d1, d2, chn);
2306 break;
2308 break;
2309 case MIDI_PGM_CHANGE:
2310 channel[chn].program = d1;
2311 break;
2312 case MIDI_CHN_PRESSURE:
2313 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
2314 if (voice[i].status != sVS_UNUSED && voice[i].channel == chn) {
2315 SEQ_KEY_PRESSURE(wDevID, i, voice[i].note, d1);
2318 break;
2319 case MIDI_PITCH_BEND:
2320 channel[chn].bender = (d2 << 7) + d1;
2321 for (i = 0; i < midiOutDevices[wDevID]->wVoices; i++) {
2322 if (voice[i].channel == chn) {
2323 SEQ_BENDER(wDevID, i, channel[chn].bender);
2326 break;
2327 case MIDI_SYSTEM_PREFIX:
2328 switch (evt & 0x0F) {
2329 case 0x0F: /* Reset */
2330 modFMReset(wDevID);
2331 break;
2332 default:
2333 WARN(midi,
2334 "Unsupported (yet) system event %02x\n",
2335 evt & 0x0F);
2337 break;
2338 default:
2339 WARN(midi, "Internal error, shouldn't happen (event=%08x)\n", evt & 0xF0);
2340 return MMSYSERR_NOTENABLED;
2343 break;
2344 case MOD_MIDIPORT:
2346 int dev = wDevID - MODM_NUMFMSYNTHDEVS;
2347 if (dev < 0) {
2348 WARN(midi, "Internal error on devID (%u) !\n", wDevID);
2349 return MIDIERR_NODEVICE;
2352 switch (evt & 0xF0) {
2353 case MIDI_NOTEOFF:
2354 case MIDI_NOTEON:
2355 case MIDI_KEY_PRESSURE:
2356 case MIDI_CTL_CHANGE:
2357 case MIDI_PITCH_BEND:
2358 SEQ_MIDIOUT(dev, evt);
2359 SEQ_MIDIOUT(dev, d1);
2360 SEQ_MIDIOUT(dev, d2);
2361 break;
2362 case MIDI_PGM_CHANGE:
2363 case MIDI_CHN_PRESSURE:
2364 SEQ_MIDIOUT(dev, evt);
2365 SEQ_MIDIOUT(dev, d1);
2366 break;
2367 case MIDI_SYSTEM_PREFIX:
2368 switch (evt & 0x0F) {
2369 case 0x00: /* System Exclusive, don't do it on modData,
2370 * should require modLongData*/
2371 case 0x01: /* Undefined */
2372 case 0x04: /* Undefined. */
2373 case 0x05: /* Undefined. */
2374 case 0x07: /* End of Exclusive. */
2375 case 0x09: /* Undefined. */
2376 case 0x0D: /* Undefined. */
2377 break;
2378 case 0x06: /* Tune Request */
2379 case 0x08: /* Timing Clock. */
2380 case 0x0A: /* Start. */
2381 case 0x0B: /* Continue */
2382 case 0x0C: /* Stop */
2383 case 0x0E: /* Active Sensing. */
2384 SEQ_MIDIOUT(dev, evt);
2385 break;
2386 case 0x0F: /* Reset */
2387 /* SEQ_MIDIOUT(dev, evt);
2388 this other way may be better */
2389 SEQ_MIDIOUT(dev, MIDI_SYSTEM_PREFIX);
2390 SEQ_MIDIOUT(dev, 0x7e);
2391 SEQ_MIDIOUT(dev, 0x7f);
2392 SEQ_MIDIOUT(dev, 0x09);
2393 SEQ_MIDIOUT(dev, 0x01);
2394 SEQ_MIDIOUT(dev, 0xf7);
2395 break;
2396 case 0x03: /* Song Select. */
2397 SEQ_MIDIOUT(dev, evt);
2398 SEQ_MIDIOUT(dev, d1);
2399 case 0x02: /* Song Position Pointer. */
2400 SEQ_MIDIOUT(dev, evt);
2401 SEQ_MIDIOUT(dev, d1);
2402 SEQ_MIDIOUT(dev, d2);
2404 break;
2407 break;
2408 default:
2409 WARN(midi, "Technology not supported (yet) %d !\n",
2410 midiOutDevices[wDevID]->wTechnology);
2411 return MMSYSERR_NOTENABLED;
2414 SEQ_DUMPBUF();
2415 #else
2416 if (MidiOutDev[wDevID].unixdev == 0) {
2417 WARN(midi,"can't play !\n");
2418 return MIDIERR_NODEVICE;
2421 WORD event = LOWORD(dwParam);
2422 if (write (MidiOutDev[wDevID].unixdev,
2423 &event, sizeof(event)) != sizeof(WORD)) {
2424 WARN(midi, "error writting unixdev !\n");
2427 #endif /* HAVE_OSS */
2428 return MMSYSERR_NOERROR;
2431 /**************************************************************************
2432 * modLongData [internal]
2434 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
2436 int count;
2438 TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
2440 #ifdef HAVE_OSS
2441 if (midiSeqFD == -1) {
2442 WARN(midi,"can't play !\n");
2443 return MIDIERR_NODEVICE;
2445 #else /* HAVE_OSS */
2446 if (MidiOutDev[wDevID].unixdev == 0) {
2447 WARN(midi,"can't play !\n");
2448 return MIDIERR_NODEVICE;
2450 #endif /* HAVE_OSS */
2452 if (lpMidiHdr->lpData == NULL)
2453 return MIDIERR_UNPREPARED;
2454 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
2455 return MIDIERR_UNPREPARED;
2456 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
2457 return MIDIERR_STILLPLAYING;
2458 lpMidiHdr->dwFlags &= ~MHDR_DONE;
2459 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
2461 TRACE(midi, "dwBytesRecorded %lu !\n", lpMidiHdr->dwBytesRecorded);
2462 TRACE(midi, " %02X %02X %02X %02X\n",
2463 ((LPBYTE)(lpMidiHdr->reserved))[0],
2464 ((LPBYTE)(lpMidiHdr->reserved))[1],
2465 ((LPBYTE)(lpMidiHdr->reserved))[2],
2466 ((LPBYTE)(lpMidiHdr->reserved))[3]);
2467 #ifdef HAVE_OSS
2468 switch (midiOutDevices[wDevID]->wTechnology) {
2469 case MOD_FMSYNTH:
2470 /* FIXME: I don't think there is much to do here */
2471 break;
2472 case MOD_MIDIPORT:
2473 if (((LPBYTE)lpMidiHdr->reserved)[0] != 0xF0) {
2474 /* Send end of System Exclusive */
2475 SEQ_MIDIOUT(wDevID - MODM_NUMFMSYNTHDEVS, 0xF0);
2476 TRACE(midi, "Adding missing 0xF0 marker at the begining of "
2477 "system exclusive byte stream\n");
2479 for (count = 0; count < lpMidiHdr->dwBytesRecorded; count++) {
2480 SEQ_MIDIOUT(wDevID - MODM_NUMFMSYNTHDEVS,
2481 ((LPBYTE)lpMidiHdr->reserved)[count]);
2483 if (((LPBYTE)lpMidiHdr->reserved)[count - 1] != 0xF7) {
2484 /* Send end of System Exclusive */
2485 SEQ_MIDIOUT(wDevID - MODM_NUMFMSYNTHDEVS, 0xF7);
2486 TRACE(midi, "Adding missing 0xF7 marker at the end of "
2487 "system exclusive byte stream\n");
2489 SEQ_DUMPBUF();
2490 break;
2491 default:
2492 WARN(midi, "Technology not supported (yet) %d !\n",
2493 midiOutDevices[wDevID]->wTechnology);
2494 return MMSYSERR_NOTENABLED;
2496 #else /* HAVE_OSS */
2498 LPWORD ptr = (LPWORD)lpMidiHdr->reserved;
2499 int en;
2501 for (count = 0; count < lpMidiHdr->dwBytesRecorded; count++) {
2502 if (write(MidiOutDev[wDevID].unixdev,
2503 ptr, sizeof(WORD)) != sizeof(WORD))
2504 break;
2505 ptr++;
2508 en = errno;
2509 TRACE(midi, "after write count = %d\n",count);
2510 if (count != lpMidiHdr->dwBytesRecorded) {
2511 WARN(midi, "error writting unixdev #%d ! (%d != %ld)\n",
2512 MidiOutDev[wDevID].unixdev, count,
2513 lpMidiHdr->dwBytesRecorded);
2514 TRACE(midi, "\terrno = %d error = %s\n",en,strerror(en));
2515 return MMSYSERR_NOTENABLED;
2518 #endif /* HAVE_OSS */
2520 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
2521 lpMidiHdr->dwFlags |= MHDR_DONE;
2522 if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD)lpMidiHdr, 0L) != MMSYSERR_NOERROR) {
2523 WARN(midi,"can't notify client !\n");
2524 return MMSYSERR_INVALPARAM;
2526 return MMSYSERR_NOERROR;
2529 /**************************************************************************
2530 * modPrepare [internal]
2532 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
2534 TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
2536 #ifdef HAVE_OSS
2537 if (midiSeqFD == -1) {
2538 WARN(midi,"can't prepare !\n");
2539 return MMSYSERR_NOTENABLED;
2541 #else /* HAVE_OSS */
2542 if (MidiOutDev[wDevID].unixdev == 0) {
2543 WARN(midi,"can't prepare !\n");
2544 return MMSYSERR_NOTENABLED;
2546 #endif /* HAVE_OSS */
2547 if (dwSize != sizeof(MIDIHDR) || lpMidiHdr == 0 ||
2548 lpMidiHdr->lpData == 0 || lpMidiHdr->dwFlags != 0 ||
2549 lpMidiHdr->dwBufferLength >= 0x10000ul)
2550 return MMSYSERR_INVALPARAM;
2552 lpMidiHdr->lpNext = 0;
2553 lpMidiHdr->dwFlags |= MHDR_PREPARED;
2554 lpMidiHdr->dwFlags &= ~MHDR_DONE;
2555 return MMSYSERR_NOERROR;
2558 /**************************************************************************
2559 * modUnprepare [internal]
2561 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
2563 TRACE(midi, "(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize);
2565 #ifdef HAVE_OSS
2566 if (midiSeqFD == -1) {
2567 WARN(midi,"can't unprepare !\n");
2568 return MMSYSERR_NOTENABLED;
2570 #else /* HAVE_OSS */
2571 if (MidiOutDev[wDevID].unixdev == 0) {
2572 WARN(midi,"can't unprepare !\n");
2573 return MMSYSERR_NOTENABLED;
2575 #endif /* HAVE_OSS */
2576 if (dwSize != sizeof(MIDIHDR) || lpMidiHdr == 0)
2577 return MMSYSERR_INVALPARAM;
2578 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
2579 return MIDIERR_STILLPLAYING;
2580 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
2581 return MMSYSERR_NOERROR;
2584 /**************************************************************************
2585 * modReset [internal]
2587 static DWORD modReset(WORD wDevID)
2589 TRACE(midi, "(%04X);\n", wDevID);
2590 /* FIXME: this function should :
2591 * turn off every note, remove sustain on all channels
2592 * remove any pending buffers
2594 return MMSYSERR_NOTENABLED;
2597 /**************************************************************************
2598 * modMessage [sample driver]
2600 DWORD WINAPI modMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
2601 DWORD dwParam1, DWORD dwParam2)
2603 TRACE(midi, "(%04X, %04X, %08lX, %08lX, %08lX);\n",
2604 wDevID, wMsg, dwUser, dwParam1, dwParam2);
2606 switch (wMsg) {
2607 case MODM_OPEN:
2608 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
2609 case MODM_CLOSE:
2610 return modClose(wDevID);
2611 case MODM_DATA:
2612 return modData(wDevID, dwParam1);
2613 case MODM_LONGDATA:
2614 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
2615 case MODM_PREPARE:
2616 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
2617 case MODM_UNPREPARE:
2618 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
2619 case MODM_GETDEVCAPS:
2620 return modGetDevCaps(wDevID,(LPMIDIOUTCAPS16)dwParam1,dwParam2);
2621 case MODM_GETNUMDEVS:
2622 return MODM_NUMDEVS;
2623 case MODM_GETVOLUME:
2624 return 0;
2625 case MODM_SETVOLUME:
2626 return 0;
2627 case MODM_RESET:
2628 return modReset(wDevID);
2629 default:
2630 TRACE(midi, "Unsupported message\n");
2632 return MMSYSERR_NOTSUPPORTED;
2635 /**************************************************************************
2636 * MIDI_DriverProc16 [sample driver]
2638 LONG MIDI_DriverProc16(DWORD dwDevID, HDRVR16 hDriv, WORD wMsg,
2639 DWORD dwParam1, DWORD dwParam2)
2641 switch (wMsg) {
2642 case DRV_LOAD: return 1;
2643 case DRV_FREE: return 1;
2644 case DRV_OPEN: return 1;
2645 case DRV_CLOSE: return 1;
2646 case DRV_ENABLE: return 1;
2647 case DRV_DISABLE: return 1;
2648 case DRV_QUERYCONFIGURE: return 1;
2649 case DRV_CONFIGURE: MessageBox16(0, "Sample Midi Linux Driver !", "MMLinux Driver", MB_OK); return 1;
2650 case DRV_INSTALL: return DRVCNF_RESTART;
2651 case DRV_REMOVE: return DRVCNF_RESTART;
2652 case MCI_OPEN_DRIVER:
2653 case MCI_OPEN: return MIDI_mciOpen(dwDevID, dwParam1, PTR_SEG_TO_LIN(dwParam2), FALSE);
2654 case MCI_CLOSE_DRIVER:
2655 case MCI_CLOSE: return MIDI_mciClose(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)PTR_SEG_TO_LIN(dwParam2));
2656 case MCI_PLAY: return MIDI_mciPlay(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)PTR_SEG_TO_LIN(dwParam2));
2657 case MCI_RECORD: return MIDI_mciRecord(dwDevID, dwParam1, (LPMCI_RECORD_PARMS)PTR_SEG_TO_LIN(dwParam2));
2658 case MCI_STOP: return MIDI_mciStop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)PTR_SEG_TO_LIN(dwParam2));
2659 case MCI_SET: return MIDI_mciSet(dwDevID, dwParam1, (LPMCI_SET_PARMS)PTR_SEG_TO_LIN(dwParam2));
2660 case MCI_PAUSE: return MIDI_mciPause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)PTR_SEG_TO_LIN(dwParam2));
2661 case MCI_RESUME: return MIDI_mciResume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)PTR_SEG_TO_LIN(dwParam2));
2662 case MCI_STATUS: return MIDI_mciStatus(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)PTR_SEG_TO_LIN(dwParam2));
2663 case MCI_GETDEVCAPS: return MIDI_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)PTR_SEG_TO_LIN(dwParam2));
2664 case MCI_INFO: return MIDI_mciInfo(dwDevID, dwParam1, (LPMCI_INFO_PARMS16)PTR_SEG_TO_LIN(dwParam2));
2665 default: return DefDriverProc16(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
2669 /**************************************************************************
2670 * MIDI_DriverProc32 [sample driver]
2672 LONG MIDI_DriverProc32(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
2673 DWORD dwParam1, DWORD dwParam2)
2675 switch (wMsg) {
2676 case DRV_LOAD: return 1;
2677 case DRV_FREE: return 1;
2678 case DRV_OPEN: return 1;
2679 case DRV_CLOSE: return 1;
2680 case DRV_ENABLE: return 1;
2681 case DRV_DISABLE: return 1;
2682 case DRV_QUERYCONFIGURE: return 1;
2683 case DRV_CONFIGURE: MessageBox16(0, "Sample Midi Linux Driver !", "MMLinux Driver", MB_OK); return 1;
2684 case DRV_INSTALL: return DRVCNF_RESTART;
2685 case DRV_REMOVE: return DRVCNF_RESTART;
2686 case MCI_OPEN_DRIVER:
2687 case MCI_OPEN: return MIDI_mciOpen(dwDevID, dwParam1, (void*)dwParam2, TRUE);
2688 case MCI_CLOSE_DRIVER:
2689 case MCI_CLOSE: return MIDI_mciClose(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
2690 case MCI_PLAY: return MIDI_mciPlay(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2);
2691 case MCI_RECORD: return MIDI_mciRecord(dwDevID, dwParam1, (LPMCI_RECORD_PARMS)dwParam2);
2692 case MCI_STOP: return MIDI_mciStop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
2693 case MCI_SET: return MIDI_mciSet(dwDevID, dwParam1, (LPMCI_SET_PARMS)dwParam2);
2694 case MCI_PAUSE: return MIDI_mciPause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
2695 case MCI_RESUME: return MIDI_mciResume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
2696 case MCI_STATUS: return MIDI_mciStatus(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2);
2697 case MCI_GETDEVCAPS: return MIDI_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
2698 case MCI_INFO: return MIDI_mciInfo(dwDevID, dwParam1, (LPMCI_INFO_PARMS16)dwParam2);
2699 default: return DefDriverProc32(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
2702 /*-----------------------------------------------------------------------*/