- add -lossaudio if needed, and a check for <soundcard.h>, to configure.in
[wine] / multimedia / mcimidi.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  * Sample MIDI Wine Driver for Linux
5  *
6  * Copyright 1994 Martin Ayotte
7  */
8
9 /* 
10  * Eric POUECH : 
11  * 98/7 changes for making this MIDI driver work on OSS
12  *      current support is limited to MIDI ports of OSS systems
13  * 98/9 rewriting MCI code for MIDI
14  * 98/11 splitted in midi.c and mcimidi.c
15  */
16
17 #include <errno.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <sys/ioctl.h>
24 #include "windows.h"
25 #include "ldt.h"
26 #include "multimedia.h"
27 #include "user.h"
28 #include "driver.h"
29 #include "xmalloc.h"
30 #include "debug.h"
31 #include "callback.h"
32 #include "options.h"
33 #include "heap.h"
34
35 typedef struct {
36     DWORD               dwFirst;                /* offset in file of track */
37     DWORD               dwLast;                 /* number of bytes in file of track */
38     DWORD               dwIndex;                /* current index in file (dwFirst <= dwIndex < dwLast) */
39     DWORD               dwLength;               /* number of pulses in this track */
40     DWORD               dwEventPulse;           /* current pulse # (event) pointed by dwIndex */
41     DWORD               dwEventData;            /* current data    (event) pointed by dwIndex */
42     WORD                wEventLength;           /* current length  (event) pointed by dwIndex */
43     WORD                wStatus : 1,            /* 1 : playing, 0 : done */
44                         wTrackNr : 7,
45                         wLastCommand : 8;
46 } MCI_MIDITRACK;
47
48 typedef struct {
49     int                 nUseCount;              /* Incremented for each shared open          */
50     WORD                wNotifyDeviceID;        /* MCI device ID with a pending notification */
51     HANDLE16            hCallback;              /* Callback handle for pending notification  */
52     HMMIO32             hFile;                  /* mmio file handle open as Element          */
53     MCI_OPEN_PARMS32A   openParms;
54     HLOCAL16            hMidiHdr;
55     WORD                dwStatus;               /* one from MCI_MODE_xxxx */
56     DWORD               dwMciTimeFormat;        /* One of the supported MCI_FORMAT_xxxx */             
57     WORD                wFormat;                /* Format of MIDI hFile (0, 1 or 2) */
58     WORD                nTracks;                /* Number of tracks in hFile */
59     WORD                nDivision;              /* Number of division in hFile PPQN or SMPTE */
60     WORD                wStartedPlaying;
61     DWORD               dwTempo;                /* Tempo (# of 1/4 note per second */
62     MCI_MIDITRACK*      tracks;                 /* Content of each track */
63     DWORD               dwPulse;                
64     DWORD               dwPositionMS;
65     DWORD               dwStartTicks;
66 } WINE_MCIMIDI;
67
68 static WINE_MCIMIDI     MCIMidiDev[MAX_MCIMIDIDRV];
69
70 /*======================================================================*
71  *                          MCI MIDI implemantation                     *
72  *======================================================================*/
73
74 #ifdef SNDCTL_MIDI_INFO
75 /**************************************************************************
76  *                              MIDI_mciGetOpenDev              [internal]      
77  */
78 static WINE_MCIMIDI*  MIDI_mciGetOpenDev(UINT16 wDevID)
79 {
80     if (wDevID >= MAX_MCIMIDIDRV || MCIMidiDev[wDevID].nUseCount == 0) {
81         WARN(mcimidi, "Invalid wDevID=%u\n", wDevID);
82         return 0;
83     }
84     return &MCIMidiDev[wDevID];
85 }
86
87 /**************************************************************************
88  *                              MIDI_mciReadByte                [internal]      
89  */
90 static DWORD MIDI_mciReadByte(WINE_MCIMIDI* wmm, BYTE *lpbyt)
91 {
92     DWORD       ret = 0;
93
94     if (lpbyt == NULL || 
95         mmioRead32(wmm->hFile, (HPSTR)lpbyt, (long)sizeof(BYTE)) != (long)sizeof(BYTE)) {
96         WARN(mcimidi, "Error reading wmm=%p\n", wmm);
97         ret = MCIERR_INTERNAL;
98     }
99
100     return ret;
101 }
102
103 /**************************************************************************
104  *                              MIDI_mciReadWord                [internal]      
105  */
106 static DWORD MIDI_mciReadWord(WINE_MCIMIDI* wmm, LPWORD lpw)
107 {
108     BYTE        hibyte, lobyte;
109     DWORD       ret = MCIERR_INTERNAL;
110
111     if (lpw != NULL && 
112         MIDI_mciReadByte(wmm, &hibyte) == 0 && 
113         MIDI_mciReadByte(wmm, &lobyte) == 0) {
114         *lpw = ((WORD)hibyte << 8) + lobyte;
115         ret = 0;
116     }
117     return ret;
118 }
119
120 /**************************************************************************
121  *                              MIDI_mciReadLong                [internal]      
122  */
123 static DWORD MIDI_mciReadLong(WINE_MCIMIDI* wmm, LPDWORD lpdw)
124 {
125     WORD        hiword, loword;
126     DWORD       ret = MCIERR_INTERNAL;
127
128     if (lpdw != NULL &&
129         MIDI_mciReadWord(wmm, &hiword) == 0 &&
130         MIDI_mciReadWord(wmm, &loword) == 0) {
131         *lpdw = MAKELONG(loword, hiword);
132         ret = 0;
133     }
134     return ret;
135 }
136
137 /**************************************************************************
138  *                              MIDI_mciReadVaryLen             [internal]      
139  */
140 static WORD MIDI_mciReadVaryLen(WINE_MCIMIDI* wmm, LPDWORD lpdw)
141 {
142     BYTE        byte;
143     DWORD       value = 0;
144     WORD        ret = 0;
145
146     if (lpdw == NULL) {
147         ret = MCIERR_INTERNAL;
148     } else {
149         do {
150             if (MIDI_mciReadByte(wmm, &byte) != 0) {
151                 return 0;
152             }
153             value = (value << 7) + (byte & 0x7F);
154             ret++; 
155         } while (byte & 0x80);
156         *lpdw = value;
157         /*
158           TRACE(mcimidi, "val=%08lX \n", value);
159         */
160     }
161     return ret;
162 }
163
164 /**************************************************************************
165  *                              MIDI_mciReadNextEvent           [internal]      
166  */
167 static DWORD    MIDI_mciReadNextEvent(WINE_MCIMIDI* wmm, MCI_MIDITRACK* mmt) 
168 {
169     BYTE        b1, b2 = 0, b3;
170     WORD        hw = 0;
171     DWORD       evtPulse;
172     DWORD       evtLength;
173     DWORD       tmp;
174
175     if (mmioSeek32(wmm->hFile, mmt->dwIndex, SEEK_SET) != mmt->dwIndex) {
176         WARN(mcimidi, "Can't seek at %08lX \n", mmt->dwIndex);
177         return MCIERR_INTERNAL;
178     }
179     evtLength = MIDI_mciReadVaryLen(wmm, &evtPulse) + 1;        /* > 0 */
180     MIDI_mciReadByte(wmm, &b1);
181     switch (b1) {
182     case 0xF0:
183     case 0xF7:
184         evtLength += MIDI_mciReadVaryLen(wmm, &tmp);
185         evtLength += tmp;
186         break;
187     case 0xFF:
188         MIDI_mciReadByte(wmm, &b2);     evtLength++;
189         
190         evtLength += MIDI_mciReadVaryLen(wmm, &tmp);
191         if (evtLength >= 0x10000u) {
192             /* this limitation shouldn't be a problem */
193             WARN(mcimidi, "Ouch !! Implementation limitation to 64k bytes for a MIDI event is overflowed\n");
194             hw = 0xFFFF;
195         } else {
196             hw = LOWORD(evtLength);
197         }
198         evtLength += tmp;
199         break;
200     default:
201         if (b1 & 0x80) { /* use running status ? */
202             mmt->wLastCommand = b1;
203             MIDI_mciReadByte(wmm, &b2); evtLength++;
204         } else {
205             b2 = b1;
206             b1 = mmt->wLastCommand;
207         }
208         switch ((b1 >> 4) & 0x07) {
209         case 0: case 1: case 2: case 3: case 6:
210             MIDI_mciReadByte(wmm, &b3); evtLength++;
211             hw = b3;
212             break;
213         case 4: case 5:
214             break;
215         case 7:
216             WARN(mcimidi, "Strange indeed b1=0x%02x\n", b1);
217         }
218         break;
219     }
220     if (mmt->dwIndex + evtLength > mmt->dwLast)
221         return MCIERR_INTERNAL;
222
223     mmt->dwEventPulse += evtPulse;
224     mmt->dwEventData   = (hw << 16) + (b2 << 8) + b1;
225     mmt->wEventLength  = evtLength;
226
227     /*
228     TRACE(mcimidi, "[%u] => pulse=%08lx(%08lx), data=%08lx, length=%u\n", 
229           mmt->wTrackNr, mmt->dwEventPulse, evtPulse, 
230           mmt->dwEventData, mmt->wEventLength);
231     */
232     return 0;
233 }
234
235 /**************************************************************************
236  *                              MIDI_mciReadMTrk                [internal]      
237  */
238 static DWORD MIDI_mciReadMTrk(WINE_MCIMIDI* wmm, MCI_MIDITRACK* mmt)
239 {
240     DWORD               toberead;
241     FOURCC              fourcc;
242
243     if (mmioRead32(wmm->hFile, (HPSTR)&fourcc, (long)sizeof(FOURCC)) != 
244         (long)sizeof(FOURCC)) {
245         return MCIERR_INTERNAL;
246     }
247
248     if (fourcc != mmioFOURCC('M', 'T', 'r', 'k')) {
249         WARN(mcimidi, "Can't synchronize on 'MTrk' !\n");
250         return MCIERR_INTERNAL;
251     }
252
253     if (MIDI_mciReadLong(wmm, &toberead) != 0) {
254         return MCIERR_INTERNAL;
255     }
256     mmt->dwFirst = mmioSeek32(wmm->hFile, 0, SEEK_CUR); /* >= 0 */
257     mmt->dwLast = mmt->dwFirst + toberead;
258
259     /* compute # of pulses in this track */
260     mmt->dwIndex = mmt->dwFirst;
261     mmt->dwEventPulse = 0;
262
263     while (MIDI_mciReadNextEvent(wmm, mmt) == 0 && 
264            LOWORD(mmt->dwEventData) != 0x2FFF) {        
265         mmt->dwIndex += mmt->wEventLength;
266     }
267     mmt->dwLength = mmt->dwEventPulse;
268
269     TRACE(mcimidi, "Track %u has %lu bytes and %lu pulses\n", mmt->wTrackNr, toberead, mmt->dwLength);
270
271     /* reset track data */
272     mmt->wStatus = 1;   /* ok, playing */
273     mmt->dwIndex = mmt->dwFirst;
274     mmt->dwEventPulse = 0;
275
276     if (mmioSeek32(wmm->hFile, 0, SEEK_CUR) != mmt->dwLast) {
277         WARN(mcimidi, "Ouch, out of sync seek=%lu track=%lu\n", 
278              mmioSeek32(wmm->hFile, 0, SEEK_CUR), mmt->dwLast);
279         /* position at end of this track, to be ready to read next track */
280         mmioSeek32(wmm->hFile, mmt->dwLast, SEEK_SET);
281     }
282
283     return 0;
284 }
285
286 /**************************************************************************
287  *                              MIDI_mciReadMThd                [internal]      
288  */
289 static DWORD MIDI_mciReadMThd(WINE_MCIMIDI* wmm, DWORD dwOffset)
290 {
291     DWORD       toberead;
292     FOURCC      fourcc;
293     WORD        nt;
294
295     TRACE(mcimidi, "(%p, %08lX);\n", wmm, dwOffset);
296
297     if (mmioSeek32(wmm->hFile, dwOffset, SEEK_SET) != dwOffset) {
298         WARN(mcimidi, "Can't seek at %08lX begin of 'MThd' \n", dwOffset);
299         return MCIERR_INTERNAL;
300     }
301     if (mmioRead32(wmm->hFile, (HPSTR)&fourcc,
302                    (long) sizeof(FOURCC)) != (long) sizeof(FOURCC))
303         return MCIERR_INTERNAL;
304
305     if (fourcc != mmioFOURCC('M', 'T', 'h', 'd')) {
306         WARN(mcimidi, "Can't synchronize on 'MThd' !\n");
307         return MCIERR_INTERNAL;
308     }
309
310     if (MIDI_mciReadLong(wmm, &toberead) != 0 || toberead < 3 * sizeof(WORD))
311         return MCIERR_INTERNAL;
312
313     if (MIDI_mciReadWord(wmm, &wmm->wFormat) != 0 ||
314         MIDI_mciReadWord(wmm, &wmm->nTracks) != 0 ||
315         MIDI_mciReadWord(wmm, &wmm->nDivision) != 0) {
316         return MCIERR_INTERNAL;
317     }
318
319     TRACE(mcimidi, "toberead=0x%08lX, wFormat=0x%04X nTracks=0x%04X nDivision=0x%04X\n",
320           toberead, wmm->wFormat, wmm->nTracks, wmm->nDivision);
321
322     /* MS doc says that the MIDI MCI time format must be put by default to the format
323      * stored in the MIDI file...
324      */
325     if (wmm->nDivision > 0x8000) {
326         /* eric.pouech@lemel.fr 98/11
327          * In did not check this very code (pulses are expressed as SMPTE sub-frames).
328          * In about 40 MB of MIDI files I have, none was SMPTE based...
329          * I'm just wondering if this is widely used :-). So, if someone has one of 
330          * these files, I'd like to know about.
331          */
332         FIXME(mcimidi, "Handling SMPTE time in MIDI files has not been tested\n"
333              "Please report to comp.emulators.ms-windows.wine with MIDI file !\n");
334
335         switch (HIBYTE(wmm->nDivision)) {
336         case 0xE8:      wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_24;     break;  /* -24 */
337         case 0xE7:      wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_25;     break;  /* -25 */
338         case 0xE3:      wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_30DROP; break;  /* -29 */ /* is the MCI constant correct ? */
339         case 0xE2:      wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_30;     break;  /* -30 */
340         default:
341             WARN(mcimidi, "Unsupported number of frames %d\n", -(char)HIBYTE(wmm->nDivision));
342             return MCIERR_INTERNAL;
343         }
344         switch (LOBYTE(wmm->nDivision)) {
345         case 4: /* MIDI Time Code */
346         case 8:
347         case 10:
348         case 80: /* SMPTE bit resolution */
349         case 100:
350         default:
351             WARN(mcimidi, "Unsupported number of sub-frames %d\n", LOBYTE(wmm->nDivision));
352             return MCIERR_INTERNAL;
353         }
354         return MCIERR_INTERNAL;
355     } else if (wmm->nDivision == 0) {
356         WARN(mcimidi, "Number of division is 0, can't support that !!\n");
357         return MCIERR_INTERNAL;
358     } else {
359         wmm->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
360     }
361
362     switch (wmm->wFormat) {
363     case 0:
364         if (wmm->nTracks != 1) {
365             WARN(mcimidi, "Got type 0 file whose number of track is not 1. Setting it to 1\n");
366             wmm->nTracks = 1;
367         }
368         break;
369     case 1:
370     case 2:
371         break;
372     default:
373         WARN(mcimidi, "Handling MIDI files which format = %d is not (yet) supported\n"
374                    "Please report with MIDI file !\n", wmm->wFormat);
375         return MCIERR_INTERNAL;
376     }
377
378     if (wmm->nTracks & 0x8000) {
379         /* this shouldn't be a problem... */
380         WARN(mcimidi, "Ouch !! Implementation limitation to 32k tracks per MIDI file is overflowed\n");
381         wmm->nTracks = 0x7FFF;
382     }
383
384     wmm->tracks = xmalloc(sizeof(MCI_MIDITRACK) * wmm->nTracks);
385
386     toberead -= 3 * sizeof(WORD); 
387     if (toberead > 0) {
388         TRACE(mcimidi, "Size of MThd > 6, skipping %ld extra bytes\n", toberead);
389         mmioSeek32(wmm->hFile, toberead, SEEK_CUR);
390     }
391
392     for (nt = 0; nt < wmm->nTracks; nt++) {
393         wmm->tracks[nt].wTrackNr = nt;
394         if (MIDI_mciReadMTrk(wmm, &wmm->tracks[nt]) != 0) {
395             WARN(mcimidi, "Can't read 'MTrk' header \n");
396             return MCIERR_INTERNAL;
397         }
398     }
399
400     wmm->dwTempo = 500000;
401
402     return 0;
403 }
404
405 /**************************************************************************
406  *                      MIDI_ConvertPulseToMS                   [internal]
407  */
408 static  DWORD   MIDI_ConvertPulseToMS(WINE_MCIMIDI* wmm, DWORD pulse)
409 {
410     DWORD       ret = 0;
411
412     /* FIXME: this function may return false values since the tempo (wmm->dwTempo)
413      * may change during file playing
414      */
415     if (wmm->nDivision == 0) {
416         FIXME(mcimidi, "Shouldn't happen. wmm->nDivision = 0\n");
417     } else if (wmm->nDivision > 0x8000) { /* SMPTE, unchecked FIXME? */
418         int     nf = -(char)HIBYTE(wmm->nDivision);     /* number of frames     */
419         int     nsf = LOBYTE(wmm->nDivision);           /* number of sub-frames */
420         ret = (pulse * 1000) / (nf * nsf);
421     } else {
422         ret = (DWORD)((double)pulse * ((double)wmm->dwTempo / 1000) /   
423                       (double)wmm->nDivision);
424     }
425
426     /*
427       TRACE(mcimidi, "pulse=%lu tempo=%lu division=%u=0x%04x => ms=%lu\n", 
428       pulse, wmm->dwTempo, wmm->nDivision, wmm->nDivision, ret);
429     */
430
431     return ret;
432 }
433
434 #define TIME_MS_IN_ONE_HOUR     (60*60*1000)
435 #define TIME_MS_IN_ONE_MINUTE   (60*1000)
436 #define TIME_MS_IN_ONE_SECOND   (1000)
437
438 /**************************************************************************
439  *                      MIDI_ConvertTimeFormatToMS              [internal]
440  */
441 static  DWORD   MIDI_ConvertTimeFormatToMS(WINE_MCIMIDI* wmm, DWORD val)
442 {
443     DWORD       ret = 0;
444
445     switch (wmm->dwMciTimeFormat) {
446     case MCI_FORMAT_MILLISECONDS:       
447         ret = val;              
448         break;
449     case MCI_FORMAT_SMPTE_24:
450         ret = 
451             (HIBYTE(HIWORD(val)) * 125) / 3 +             LOBYTE(HIWORD(val)) * TIME_MS_IN_ONE_SECOND +
452             HIBYTE(LOWORD(val)) * TIME_MS_IN_ONE_MINUTE + LOBYTE(LOWORD(val)) * TIME_MS_IN_ONE_HOUR;
453         break;
454     case MCI_FORMAT_SMPTE_25:           
455         ret = 
456             HIBYTE(HIWORD(val)) * 40 +                    LOBYTE(HIWORD(val)) * TIME_MS_IN_ONE_SECOND +
457             HIBYTE(LOWORD(val)) * TIME_MS_IN_ONE_MINUTE + LOBYTE(LOWORD(val)) * TIME_MS_IN_ONE_HOUR;
458         break;
459     case MCI_FORMAT_SMPTE_30:           
460         ret = 
461             (HIBYTE(HIWORD(val)) * 100) / 3 +             LOBYTE(HIWORD(val)) * TIME_MS_IN_ONE_SECOND +
462             HIBYTE(LOWORD(val)) * TIME_MS_IN_ONE_MINUTE + LOBYTE(LOWORD(val)) * TIME_MS_IN_ONE_HOUR;
463         break;
464     default:
465         WARN(mcimidi, "Bad time format %lu!\n", wmm->dwMciTimeFormat);
466     }
467     /*
468       TRACE(mcimidi, "val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmm->dwMciTimeFormat, ret);
469     */
470     return ret;
471 }       
472
473 /**************************************************************************
474  *                      MIDI_ConvertMSToTimeFormat              [internal]
475  */
476 static  DWORD   MIDI_ConvertMSToTimeFormat(WINE_MCIMIDI* wmm, DWORD _val)
477 {
478     DWORD       ret = 0, val = _val;
479     DWORD       h, m, s, f;
480
481     switch (wmm->dwMciTimeFormat) {
482     case MCI_FORMAT_MILLISECONDS:       
483         ret = val;              
484         break;
485     case MCI_FORMAT_SMPTE_24:
486     case MCI_FORMAT_SMPTE_25:           
487     case MCI_FORMAT_SMPTE_30:           
488         h = val / TIME_MS_IN_ONE_HOUR;  
489         m = (val -= h * TIME_MS_IN_ONE_HOUR)   / TIME_MS_IN_ONE_MINUTE; 
490         s = (val -= m * TIME_MS_IN_ONE_MINUTE) / TIME_MS_IN_ONE_SECOND;
491         switch (wmm->dwMciTimeFormat) {
492         case MCI_FORMAT_SMPTE_24:
493             /* one frame is 1000/24 val long, 1000/24 == 125/3 */
494             f = (val * 3) / 125;        val -= (f * 125) / 3;
495             break;
496         case MCI_FORMAT_SMPTE_25:               
497             /* one frame is 1000/25 ms long, 1000/25 == 40 */
498             f = val / 40;               val -= f * 40;
499             break;
500         case MCI_FORMAT_SMPTE_30:               
501             /* one frame is 1000/30 ms long, 1000/30 == 100/3 */
502             f = (val * 3) / 100;        val -= (f * 100) / 3;
503             break;
504         default:
505             FIXME(mcimidi, "There must be some bad bad programmer\n");
506             f = 0;
507         }
508         /* val contains the number of ms which cannot make a complete frame */
509         /* FIXME: is this correct ? programs seem to be happy with that */
510         ret = (f << 24) | (s << 16) | (m << 8) | (h << 0);
511         break;
512     default:
513         WARN(mcimidi, "Bad time format %lu!\n", wmm->dwMciTimeFormat);
514     }
515     /*
516       TRACE(mcimidi, "val=%lu [tf=%lu] => ret=%lu=0x%08lx\n", _val, wmm->dwMciTimeFormat, ret, ret);
517     */
518     return ret;
519 }       
520
521 /**************************************************************************
522  *                      MIDI_GetMThdLengthMS                    [internal]
523  */
524 static  DWORD   MIDI_GetMThdLengthMS(WINE_MCIMIDI* wmm)
525 {
526     WORD        nt;
527     DWORD       ret = 0;
528     
529     for (nt = 0; nt < wmm->nTracks; nt++) {
530         if (wmm->wFormat == 2) {
531             ret += wmm->tracks[nt].dwLength;
532         } else if (wmm->tracks[nt].dwLength > ret) {
533             ret = wmm->tracks[nt].dwLength;
534         }
535     }
536     /* FIXME: this is wrong if there is a tempo change inside the file */
537     return MIDI_ConvertPulseToMS(wmm, ret);
538 }
539
540 /**************************************************************************
541  *                              MIDI_mciOpen                    [internal]      
542  */
543 static DWORD MIDI_mciOpen(UINT16 wDevID, DWORD dwFlags, LPMCI_OPEN_PARMS32A lpParms)
544 {
545     MIDIOPENDESC        midiOpenDesc;
546     DWORD               dwRet = 0;
547     DWORD               dwDeviceID;
548     WINE_MCIMIDI*       wmm;
549
550     TRACE(mcimidi, "(%04x, %08lX, %p)\n", wDevID, dwFlags, lpParms);
551      
552     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
553     if (wDevID >= MAX_MCIMIDIDRV) {
554         WARN(mcimidi, "Invalid wDevID=%u\n", wDevID);
555         return MCIERR_INVALID_DEVICE_ID;
556     }
557     wmm = &MCIMidiDev[wDevID];
558
559     if (wmm->nUseCount > 0) {
560         /* The driver is already opened on this channel
561          * MIDI sequencer cannot be shared
562          */
563         return MCIERR_DEVICE_OPEN;
564     }
565     wmm->nUseCount++;
566     
567     wmm->hFile = 0;
568     dwDeviceID = lpParms->wDeviceID;
569     
570     TRACE(mcimidi, "wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID, dwDeviceID);
571     /*  lpParms->wDeviceID = wDevID;*/
572
573     if (dwFlags & MCI_OPEN_ELEMENT) {
574         LPSTR           lpstrElementName;
575
576         lpstrElementName = lpParms->lpstrElementName;
577
578         TRACE(mcimidi, "MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName);
579         if (lpstrElementName && strlen(lpstrElementName) > 0) {
580             wmm->hFile = mmioOpen32A(lpstrElementName, NULL, 
581                                      MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_EXCLUSIVE);
582             if (wmm->hFile == 0) {
583                 WARN(mcimidi, "Can't find file='%s' !\n", lpstrElementName);
584                 wmm->nUseCount--;
585                 return MCIERR_FILE_NOT_FOUND;
586             }
587         } else {
588             wmm->hFile = 0;
589         }
590     }
591     TRACE(mcimidi, "hFile=%u\n", wmm->hFile);
592
593     memcpy(&wmm->openParms, lpParms, sizeof(MCI_OPEN_PARMS32A));
594
595     wmm->wNotifyDeviceID = dwDeviceID;
596     wmm->dwStatus = MCI_MODE_NOT_READY; /* while loading file contents */
597     /* spec says it should be the default format from the MIDI file... */
598     wmm->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
599
600     midiOpenDesc.hMidi = 0;
601
602     if (wmm->hFile != 0) {
603         MMCKINFO        ckMainRIFF;
604         MMCKINFO        mmckInfo;
605         DWORD           dwOffset = 0;
606
607         if (mmioDescend(wmm->hFile, &ckMainRIFF, NULL, 0) != 0) {
608             dwRet = MCIERR_INVALID_FILE;
609         } else {
610         TRACE(mcimidi,"ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
611               (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize);
612
613             if (ckMainRIFF.ckid == FOURCC_RIFF && ckMainRIFF.fccType == mmioFOURCC('R', 'M', 'I', 'D')) {
614                 mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a');
615                 if (mmioDescend(wmm->hFile, &mmckInfo, &ckMainRIFF, MMIO_FINDCHUNK) == 0) {
616             TRACE(mcimidi, "... is a 'RMID' file \n");
617                     dwOffset = mmckInfo.dwDataOffset;
618                 } else {
619                     dwRet = MCIERR_INVALID_FILE;
620         }
621             }
622             if (dwRet == 0 && MIDI_mciReadMThd(wmm, dwOffset) != 0) {
623             WARN(mcimidi, "Can't read 'MThd' header \n");
624                 dwRet = MCIERR_INVALID_FILE;
625         }
626         }
627     } else {
628         TRACE(mcimidi, "hFile==0, setting #tracks to 0; is this correct ?\n");
629         wmm->nTracks = 0;
630         wmm->wFormat = 0;
631         wmm->nDivision = 1;
632     }
633     if (dwRet != 0) {
634         wmm->nUseCount--;
635         if (wmm->hFile != 0)
636             mmioClose32(wmm->hFile, 0);
637         wmm->hFile = 0;
638     } else {
639     wmm->dwPositionMS = 0;
640     wmm->dwStatus = MCI_MODE_STOP;
641         wmm->hMidiHdr = USER_HEAP_ALLOC(sizeof(MIDIHDR));
642
643     dwRet = modMessage(wDevID, MODM_OPEN, 0, (DWORD)&midiOpenDesc, CALLBACK_NULL);
644     /*  dwRet = midMessage(wDevID, MIDM_OPEN, 0, (DWORD)&midiOpenDesc, CALLBACK_NULL);*/
645     }
646     return dwRet;
647 }
648
649 /**************************************************************************
650  *                              MIDI_mciStop                    [internal]
651  */
652 static DWORD MIDI_mciStop(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
653 {    
654     WINE_MCIMIDI*       wmm = MIDI_mciGetOpenDev(wDevID);
655
656     TRACE(mcimidi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
657
658     if (wmm == NULL)    return MCIERR_INVALID_DEVICE_ID;
659
660     wmm->dwStatus = MCI_MODE_STOP;
661     TRACE(mcimidi, "wmm->dwStatus=%d\n", wmm->dwStatus);
662
663     if (lpParms && (dwFlags & MCI_NOTIFY)) {
664         TRACE(mcimidi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
665         mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback), 
666                           wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
667     }
668     return 0;
669 }
670
671 /**************************************************************************
672  *                              MIDI_mciClose                   [internal]
673  */
674 static DWORD MIDI_mciClose(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
675 {
676     DWORD               dwRet;
677     WINE_MCIMIDI*       wmm = MIDI_mciGetOpenDev(wDevID);
678
679     TRACE(mcimidi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
680
681     if (wmm == NULL)    return MCIERR_INVALID_DEVICE_ID;
682
683     if (wmm->dwStatus != MCI_MODE_STOP) {
684         MIDI_mciStop(wDevID, MCI_WAIT, lpParms);
685     }
686
687     wmm->dwStatus = MCI_MODE_STOP;
688     wmm->nUseCount--;
689     if (wmm->nUseCount == 0) {
690         if (wmm->hFile != 0) {
691             mmioClose32(wmm->hFile, 0);
692             wmm->hFile = 0;
693             TRACE(mcimidi, "hFile closed !\n");
694         }
695         USER_HEAP_FREE(wmm->hMidiHdr);
696         free(wmm->tracks);
697         dwRet = modMessage(wDevID, MODM_CLOSE, 0, 0L, 0L);
698         if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
699         /*
700           dwRet = midMessage(wDevID, MIDM_CLOSE, 0, 0L, 0L);
701           if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
702         */
703     } else {
704         TRACE(mcimidi, "Shouldn't happen... nUseCount=%d\n", wmm->nUseCount);
705         return MCIERR_INTERNAL;
706     }
707
708     if (lpParms && (dwFlags & MCI_NOTIFY)) {
709         TRACE(mcimidi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
710         mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback), 
711                         wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
712     }
713     return 0;
714 }
715
716 /**************************************************************************
717  *                              MIDI_mciFindNextEvent           [internal]
718  */
719 static MCI_MIDITRACK*   MIDI_mciFindNextEvent(WINE_MCIMIDI* wmm, LPDWORD hiPulse) 
720 {
721     WORD                cnt, nt;
722     MCI_MIDITRACK*      mmt;
723
724     *hiPulse = 0xFFFFFFFFul;
725     cnt = 0xFFFFu;
726     for (nt = 0; nt < wmm->nTracks; nt++) {
727         mmt = &wmm->tracks[nt];
728             
729         if (mmt->wStatus == 0)
730             continue;
731         if (mmt->dwEventPulse < *hiPulse) {
732             *hiPulse = mmt->dwEventPulse;
733             cnt = nt;
734         }
735     }
736     return (cnt == 0xFFFFu) ? 0 /* no more event on all tracks */
737         : &wmm->tracks[cnt];
738 }
739
740 /**************************************************************************
741  *                              MIDI_mciPlay                    [internal]
742  */
743 static DWORD MIDI_mciPlay(UINT16 wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
744 {
745     DWORD               dwStartMS, dwEndMS, dwRet;
746     WORD                doPlay, nt;
747     MCI_MIDITRACK*      mmt;
748     DWORD               hiPulse;
749     WINE_MCIMIDI*       wmm = MIDI_mciGetOpenDev(wDevID);
750
751     TRACE(mcimidi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
752
753     if (wmm == NULL)    return MCIERR_INVALID_DEVICE_ID;
754
755     if (wmm->hFile == 0) {
756         WARN(mcimidi, "Can't play: no file '%s' !\n", wmm->openParms.lpstrElementName);
757         return MCIERR_FILE_NOT_FOUND;
758     }
759
760     if (wmm->dwStatus != MCI_MODE_STOP) {
761         WARN(mcimidi, "Can't play: device is not stopped !\n");
762         return MCIERR_INTERNAL;
763     }
764
765     if (!(dwFlags & MCI_WAIT)) {        
766         /** FIXME: I'm not 100% sure that wNotifyDeviceID is the right value in all cases ??? */
767         return MCI_SendCommandAsync32(wmm->wNotifyDeviceID, MCI_PLAY, dwFlags, (DWORD)lpParms);
768     }
769
770     if (lpParms && (dwFlags & MCI_FROM)) {
771         dwStartMS = MIDI_ConvertTimeFormatToMS(wmm, lpParms->dwFrom); 
772     } else {
773         dwStartMS = wmm->dwPositionMS;
774     }
775
776     if (lpParms && (dwFlags & MCI_TO)) {
777         dwEndMS = MIDI_ConvertTimeFormatToMS(wmm, lpParms->dwTo);
778     } else {
779         dwEndMS = 0xFFFFFFFFul;
780     }
781
782     TRACE(mcimidi, "Playing from %lu to %lu\n", dwStartMS, dwEndMS);
783
784     /* init tracks */
785     for (nt = 0; nt < wmm->nTracks; nt++) {
786         mmt = &wmm->tracks[nt];
787
788         mmt->wStatus = 1;       /* ok, playing */
789         mmt->dwIndex = mmt->dwFirst;
790         if (wmm->wFormat == 2 && nt > 0) {
791             mmt->dwEventPulse = wmm->tracks[nt - 1].dwLength;
792         } else {
793             mmt->dwEventPulse = 0;
794         }
795         MIDI_mciReadNextEvent(wmm, mmt); /* FIXME == 0 */
796     }
797         
798     wmm->dwPulse = 0;
799     wmm->dwTempo = 500000;
800     wmm->dwStatus = MCI_MODE_PLAY;
801     wmm->dwPositionMS = 0;
802     wmm->wStartedPlaying = FALSE;
803
804     while (wmm->dwStatus != MCI_MODE_STOP) {
805         /* it seems that in case of multi-threading, gcc is optimizing just a little bit 
806          * too much. Tell gcc not to optimize status value using volatile. 
807          */
808         while (((volatile WINE_MCIMIDI*)wmm)->dwStatus == MCI_MODE_PAUSE);
809
810         doPlay = (wmm->dwPositionMS >= dwStartMS && wmm->dwPositionMS <= dwEndMS);
811
812         TRACE(mcimidi, "wmm->dwStatus=%d, doPlay=%s\n", wmm->dwStatus, doPlay ? "T" : "F");
813
814         if ((mmt = MIDI_mciFindNextEvent(wmm, &hiPulse)) == NULL)
815             break;  /* no more event on tracks */
816
817         /* if starting playing, then set StartTicks to the value it would have had
818          * if play had started at position 0
819          */
820         if (doPlay && !wmm->wStartedPlaying) {
821             wmm->dwStartTicks = GetTickCount() - MIDI_ConvertPulseToMS(wmm, wmm->dwPulse);
822             wmm->wStartedPlaying = TRUE;
823             TRACE(mcimidi, "Setting dwStartTicks to %lu\n", wmm->dwStartTicks);
824         }
825         
826         if (hiPulse > wmm->dwPulse) {
827             if (doPlay) {
828                 DWORD   togo = wmm->dwStartTicks + wmm->dwPositionMS + MIDI_ConvertPulseToMS(wmm, hiPulse - wmm->dwPulse);
829                 DWORD   tc = GetTickCount();
830
831                 TRACE(mcimidi, "Pulses hi=0x%08lx <> cur=0x%08lx\n", hiPulse, wmm->dwPulse);
832                 TRACE(mcimidi, "Wait until %lu => %lu ms\n",    
833                       tc - wmm->dwStartTicks, togo - wmm->dwStartTicks);
834                 if (tc < togo)  
835                     Sleep(togo - tc);
836             }
837             wmm->dwPositionMS += MIDI_ConvertPulseToMS(wmm, hiPulse - wmm->dwPulse);
838             wmm->dwPulse = hiPulse;
839         }
840             
841         switch (LOBYTE(LOWORD(mmt->dwEventData))) {
842         case 0xF0:
843         case 0xF7:      /* sysex events */
844             {
845                 FIXME(mcimidi, "Not handling SysEx events (yet)\n");
846             }
847             break;
848         case 0xFF:
849             /* position after meta data header */
850             mmioSeek32(wmm->hFile, mmt->dwIndex + HIWORD(mmt->dwEventData), SEEK_SET);
851             switch (HIBYTE(LOWORD(mmt->dwEventData))) {
852             case 0x00: /* 16-bit sequence number */
853                 if (TRACE_ON(midi)) {
854                     WORD        twd;
855
856                     MIDI_mciReadWord(wmm, &twd);        /* == 0 */
857                     TRACE(mcimidi, "Got sequence number %u\n", twd);
858                 }
859                 break;
860             case 0x01: /* any text */
861             case 0x02: /* Copyright Message text */
862             case 0x03: /* Sequence/Track Name text */
863             case 0x04: /* Instrument Name text */
864             case 0x05: /* Lyric text */
865             case 0x06: /* Marker text */
866             case 0x07: /* Cue-point text */
867                 if (TRACE_ON(mcimidi)) {
868                     char        buf[1024];
869                     WORD        len = mmt->wEventLength - HIWORD(mmt->dwEventData);
870                     static      char*   info[8] = {"", "Text", "Copyright", "Seq/Trk name", 
871                                                    "Instrument", "Lyric", "Marker", "Cue-point"};
872                     WORD        idx = HIBYTE(LOWORD(mmt->dwEventData));
873
874                     if (len >= sizeof(buf)) {
875                         WARN(mcimidi, "Buffer for text is too small (%d bytes, when %u are needed)\n", sizeof(buf) - 1, len);
876                         len = sizeof(buf) - 1;
877                     }
878                     if (mmioRead32(wmm->hFile, (HPSTR)buf, len) == len) {
879                         buf[len] = 0;   /* end string in case */
880                         TRACE(mcimidi, "%s => \"%s\"\n", (idx < 8 ) ? info[idx] : "", buf);
881                     } else {
882                         WARN(mcimidi, "Couldn't read data for %s\n", (idx < 8 ) ? info[idx] : "");
883                     }
884                 }
885                 break;
886             case 0x20: 
887                 /* MIDI channel (cc) */
888                 if (FIXME_ON(mcimidi)) {
889                     BYTE        bt;
890                     
891                     MIDI_mciReadByte(wmm, &bt); /* == 0 */
892                     FIXME(mcimidi, "NIY: MIDI channel=%u, track=%u\n", bt, mmt->wTrackNr);
893                 }
894                 break;
895             case 0x21:
896                 /* MIDI port (pp) */
897                 if (FIXME_ON(mcimidi)) {
898                     BYTE        bt;
899                     
900                     MIDI_mciReadByte(wmm, &bt); /* == 0 */
901                     FIXME(mcimidi, "NIY: MIDI port=%u, track=%u\n", bt, mmt->wTrackNr);
902                 }
903                 break;
904             case 0x2F: /* end of track */
905                 mmt->wStatus = 0;
906                 break;
907             case 0x51:/* set tempo */
908                 /* Tempo is expressed in Âµ-seconds per midi quarter note
909                  * for format 1 MIDI files, this can only be present on track #0
910                  */
911                 if (mmt->wTrackNr != 0 && wmm->wFormat == 1) {
912                     WARN(mcimidi, "For format #1 MIDI files, tempo can only be changed on track #0 (%u)\n", mmt->wTrackNr);
913                 } else { 
914                     BYTE        tbt;
915                     DWORD       value = 0;
916                     
917                     MIDI_mciReadByte(wmm, &tbt);        value  = ((DWORD)tbt) << 16;
918                     MIDI_mciReadByte(wmm, &tbt);        value |= ((DWORD)tbt) << 8;
919                     MIDI_mciReadByte(wmm, &tbt);        value |= ((DWORD)tbt) << 0;
920                     TRACE(mcimidi, "Setting tempo to %ld (BPM=%ld)\n", wmm->dwTempo, (value) ? (60000000l / value) : 0);
921                     wmm->dwTempo = value;
922                 }
923                 break;
924             case 0x54: /* (hour) (min) (second) (frame) (fractional-frame) - SMPTE track start */
925                 if (mmt->wTrackNr != 0 && wmm->wFormat == 1) {
926                     WARN(mcimidi, "For format #1 MIDI files, SMPTE track start can only be expressed on track #0 (%u)\n", mmt->wTrackNr);
927                 } if (mmt->dwEventPulse != 0) {
928                     WARN(mcimidi, "SMPTE track start can only be expressed at start of track (%lu)\n", mmt->dwEventPulse);
929                 } else {
930                     BYTE        h, m, s, f, ff;
931
932                     MIDI_mciReadByte(wmm, &h);
933                     MIDI_mciReadByte(wmm, &m);
934                     MIDI_mciReadByte(wmm, &s);
935                     MIDI_mciReadByte(wmm, &f);
936                     MIDI_mciReadByte(wmm, &ff);
937                     FIXME(mcimidi, "NIY: SMPTE track start %u:%u:%u %u.%u\n", h, m, s, f, ff);
938                 }
939                 break;
940             case 0x58: /* file rythm */
941                 if (TRACE_ON(mcimidi)) {
942                     BYTE        num, den, cpmc, _32npqn;
943                     
944                     MIDI_mciReadByte(wmm, &num);        
945                     MIDI_mciReadByte(wmm, &den);                /* to notate e.g. 6/8 */
946                     MIDI_mciReadByte(wmm, &cpmc);               /* number of MIDI clocks per metronome click */ 
947                     MIDI_mciReadByte(wmm, &_32npqn);    /* number of notated 32nd notes per MIDI quarter note */
948
949                     TRACE(mcimidi, "%u/%u, clock per metronome click=%u, 32nd notes by 1/4 note=%u\n", num, 1 << den, cpmc, _32npqn);
950                 }
951                 break;
952             case 0x59: /* key signature */
953                 if (TRACE_ON(mcimidi)) {
954                     BYTE        sf, mm;
955
956                     MIDI_mciReadByte(wmm, &sf);         
957                     MIDI_mciReadByte(wmm, &mm); 
958
959                     if (sf >= 0x80)     TRACE(mcimidi, "%d flats\n", -(char)sf);         
960                     else if (sf > 0)    TRACE(mcimidi, "%d sharps\n", (char)sf);         
961                     else                TRACE(mcimidi, "Key of C\n");
962                     TRACE(mcimidi, "Mode: %s\n", (mm = 0) ? "major" : "minor");
963                 }
964                 break;
965             default:
966                 WARN(mcimidi, "Unknown MIDI meta event %02x. Skipping...\n", HIBYTE(LOWORD(mmt->dwEventData)));
967                 break;
968             }
969             break;
970         default:
971             if (doPlay) {
972                 dwRet = modMessage(wDevID, MODM_DATA, 0, mmt->dwEventData, 0);
973             } else {
974                 switch (LOBYTE(LOWORD(mmt->dwEventData)) & 0xF0) {
975                 case MIDI_NOTEON:
976                 case MIDI_NOTEOFF:
977                     dwRet = 0;  
978                     break;
979                 default:
980                     dwRet = modMessage(wDevID, MODM_DATA, 0, mmt->dwEventData, 0);
981                 }
982             }
983         }
984         mmt->dwIndex += mmt->wEventLength;
985         if (mmt->dwIndex < mmt->dwFirst || mmt->dwIndex >= mmt->dwLast) {
986             mmt->wStatus = 0;
987         } 
988         if (mmt->wStatus) {     
989             MIDI_mciReadNextEvent(wmm, mmt);
990         }
991     }
992
993     /* stop all notes */
994     /* should be (0x7800 | MIDI_CTL_CHANGE) instead of 0x78B0,
995      * but MIDI_CTL_CHANGE is defined in OSS's soundcard.h and MCI must compile
996      * without any reference to OSS
997      */
998     /* FIXME: check if 0x78B0 is channel dependant or not. I coded it so that 
999      * it's channel dependent...
1000      */
1001     {
1002         unsigned chn;
1003         for (chn = 0; chn < 16; chn++)
1004             modMessage(wDevID, MODM_DATA, 0, 0x78B0 | chn, 0);
1005     }
1006
1007     wmm->dwStatus = MCI_MODE_STOP;
1008
1009     /* to restart playing at beginning when it's over */
1010     wmm->dwPositionMS = 0;
1011
1012     if (lpParms && (dwFlags & MCI_NOTIFY)) {
1013         TRACE(mcimidi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1014         mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback), 
1015                         wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1016     }
1017     return 0;
1018 }
1019
1020 /**************************************************************************
1021  *                              MIDI_mciRecord                  [internal]
1022  */
1023 static DWORD MIDI_mciRecord(UINT16 wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
1024 {
1025     int                 start, end;
1026     LPMIDIHDR           lpMidiHdr;
1027     DWORD               dwRet;
1028     WINE_MCIMIDI*       wmm = MIDI_mciGetOpenDev(wDevID);
1029     
1030     TRACE(mcimidi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1031
1032     if (wmm == 0)       return MCIERR_INVALID_DEVICE_ID;
1033
1034     if (wmm->hFile == 0) {
1035         WARN(mcimidi, "Can't find file='%08lx' !\n", 
1036              (DWORD)wmm->openParms.lpstrElementName);
1037         return MCIERR_FILE_NOT_FOUND;
1038     }
1039     start = 1;          end = 99999;
1040     if (lpParms && (dwFlags & MCI_FROM)) {
1041         start = lpParms->dwFrom; 
1042         TRACE(mcimidi, "MCI_FROM=%d \n", start);
1043     }
1044     if (lpParms && (dwFlags & MCI_TO)) {
1045         end = lpParms->dwTo;
1046         TRACE(mcimidi, "MCI_TO=%d \n", end);
1047     }
1048     lpMidiHdr = USER_HEAP_LIN_ADDR(wmm->hMidiHdr);
1049     lpMidiHdr->lpData = (LPSTR) xmalloc(1200);
1050     lpMidiHdr->dwBufferLength = 1024;
1051     lpMidiHdr->dwUser = 0L;
1052     lpMidiHdr->dwFlags = 0L;
1053     dwRet = midMessage(wDevID, MIDM_PREPARE, 0, (DWORD)lpMidiHdr, sizeof(MIDIHDR));
1054     TRACE(mcimidi, "After MIDM_PREPARE \n");
1055     wmm->dwStatus = MCI_MODE_RECORD;
1056     while (wmm->dwStatus != MCI_MODE_STOP) {
1057         TRACE(mcimidi, "wmm->dwStatus=%p %d\n",
1058               &wmm->dwStatus, wmm->dwStatus);
1059         lpMidiHdr->dwBytesRecorded = 0;
1060         dwRet = midMessage(wDevID, MIDM_START, 0, 0L, 0L);
1061         TRACE(mcimidi, "After MIDM_START lpMidiHdr=%p dwBytesRecorded=%lu\n",
1062               lpMidiHdr, lpMidiHdr->dwBytesRecorded);
1063         if (lpMidiHdr->dwBytesRecorded == 0) break;
1064     }
1065     TRACE(mcimidi, "Before MIDM_UNPREPARE \n");
1066     dwRet = midMessage(wDevID, MIDM_UNPREPARE, 0, (DWORD)lpMidiHdr, sizeof(MIDIHDR));
1067     TRACE(mcimidi, "After MIDM_UNPREPARE \n");
1068     if (lpMidiHdr->lpData != NULL) {
1069         free(lpMidiHdr->lpData);
1070         lpMidiHdr->lpData = NULL;
1071     }
1072     wmm->dwStatus = MCI_MODE_STOP;
1073     if (lpParms && (dwFlags & MCI_NOTIFY)) {
1074         TRACE(mcimidi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1075         mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback), 
1076                         wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1077     }
1078     return 0;
1079 }
1080
1081 /**************************************************************************
1082  *                              MIDI_mciPause                   [internal]
1083  */
1084 static DWORD MIDI_mciPause(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1085 {
1086     WINE_MCIMIDI*       wmm = MIDI_mciGetOpenDev(wDevID);
1087
1088     TRACE(mcimidi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1089
1090     if (wmm == NULL)    return MCIERR_INVALID_DEVICE_ID;
1091
1092     if (wmm->dwStatus == MCI_MODE_PLAY) {
1093         /* stop all notes */
1094         /* see note in MIDI_mciPlay */
1095         unsigned chn;
1096         for (chn = 0; chn < 16; chn++)
1097             modMessage(wDevID, MODM_DATA, 0, 0x78B0 | chn, 0);
1098         wmm->dwStatus = MCI_MODE_PAUSE;
1099     } 
1100     if (lpParms && (dwFlags & MCI_NOTIFY)) {
1101         TRACE(mcimidi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1102         mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback), 
1103                         wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1104     }
1105         
1106     return 0;
1107 }
1108
1109 /**************************************************************************
1110  *                              MIDI_mciResume                  [internal]
1111  */
1112 static DWORD MIDI_mciResume(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1113 {
1114     WINE_MCIMIDI*       wmm = MIDI_mciGetOpenDev(wDevID);
1115
1116     TRACE(mcimidi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1117
1118     if (wmm == NULL)    return MCIERR_INVALID_DEVICE_ID;
1119
1120     if (wmm->dwStatus == MCI_MODE_PAUSE) {
1121         wmm->wStartedPlaying = FALSE;
1122         wmm->dwStatus = MCI_MODE_PLAY;
1123     } 
1124     if (lpParms && (dwFlags & MCI_NOTIFY)) {
1125         TRACE(mcimidi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1126         mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback), 
1127                         wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1128     }
1129     return 0;
1130 }
1131
1132 /**************************************************************************
1133  *                              MIDI_mciSet                     [internal]
1134  */
1135 static DWORD MIDI_mciSet(UINT16 wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1136 {
1137     WINE_MCIMIDI*       wmm = MIDI_mciGetOpenDev(wDevID);
1138
1139     TRACE(mcimidi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1140
1141     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1142     if (wmm == NULL)            return MCIERR_INVALID_DEVICE_ID;
1143
1144     if (dwFlags & MCI_SET_TIME_FORMAT) {
1145         switch (lpParms->dwTimeFormat) {
1146         case MCI_FORMAT_MILLISECONDS:
1147             TRACE(mcimidi, "MCI_FORMAT_MILLISECONDS !\n");
1148             wmm->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1149             break;
1150         case MCI_FORMAT_SMPTE_24:
1151             TRACE(mcimidi, "MCI_FORMAT_SMPTE_24 !\n");
1152             wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_24;
1153             break;
1154         case MCI_FORMAT_SMPTE_25:
1155             TRACE(mcimidi, "MCI_FORMAT_SMPTE_25 !\n");
1156             wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_25;
1157             break;
1158         case MCI_FORMAT_SMPTE_30:
1159             TRACE(mcimidi, "MCI_FORMAT_SMPTE_30 !\n");
1160             wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_30;
1161             break;
1162         default:
1163             WARN(mcimidi, "Bad time format %lu!\n", lpParms->dwTimeFormat);
1164             return MCIERR_BAD_TIME_FORMAT;
1165         }
1166     }
1167     if (dwFlags & MCI_SET_VIDEO) {
1168         TRACE(mcimidi, "No support for video !\n");
1169         return MCIERR_UNSUPPORTED_FUNCTION;
1170     }
1171     if (dwFlags & MCI_SET_DOOR_OPEN) {
1172         TRACE(mcimidi, "No support for door open !\n");
1173         return MCIERR_UNSUPPORTED_FUNCTION;
1174     }
1175     if (dwFlags & MCI_SET_DOOR_CLOSED) {
1176         TRACE(mcimidi, "No support for door close !\n");
1177         return MCIERR_UNSUPPORTED_FUNCTION;
1178     }
1179     if (dwFlags & MCI_SET_AUDIO) {
1180         if (dwFlags & MCI_SET_ON) {
1181             TRACE(mcimidi, "MCI_SET_ON audio !\n");
1182         } else if (dwFlags & MCI_SET_OFF) {
1183             TRACE(mcimidi, "MCI_SET_OFF audio !\n");
1184         } else {
1185             WARN(mcimidi, "MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1186             return MCIERR_BAD_INTEGER;
1187         }
1188             
1189         if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
1190             TRACE(mcimidi, "MCI_SET_AUDIO_ALL !\n");
1191         if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
1192             TRACE(mcimidi, "MCI_SET_AUDIO_LEFT !\n");
1193         if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
1194             TRACE(mcimidi, "MCI_SET_AUDIO_RIGHT !\n");
1195     }
1196
1197     if (dwFlags & MCI_SEQ_SET_MASTER)
1198         TRACE(mcimidi, "MCI_SEQ_SET_MASTER !\n");
1199     if (dwFlags & MCI_SEQ_SET_SLAVE)
1200         TRACE(mcimidi, "MCI_SEQ_SET_SLAVE !\n");
1201     if (dwFlags & MCI_SEQ_SET_OFFSET)
1202         TRACE(mcimidi, "MCI_SEQ_SET_OFFSET !\n");
1203     if (dwFlags & MCI_SEQ_SET_PORT)
1204         TRACE(mcimidi, "MCI_SEQ_SET_PORT !\n");
1205     if (dwFlags & MCI_SEQ_SET_TEMPO)
1206         TRACE(mcimidi, "MCI_SEQ_SET_TEMPO !\n");
1207     return 0;
1208 }
1209
1210 /**************************************************************************
1211  *                              MIDI_mciStatus                  [internal]
1212  */
1213 static DWORD MIDI_mciStatus(UINT16 wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1214 {
1215     WINE_MCIMIDI*       wmm = MIDI_mciGetOpenDev(wDevID);
1216
1217     TRACE(mcimidi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1218
1219     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1220     if (wmm == NULL)            return MCIERR_INVALID_DEVICE_ID;
1221
1222     if (dwFlags & MCI_STATUS_ITEM) {
1223         switch (lpParms->dwItem) {
1224         case MCI_STATUS_CURRENT_TRACK:
1225             /* FIXME in Format 2 */
1226             lpParms->dwReturn = 1;
1227             TRACE(mcimidi, "MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1228             break;
1229         case MCI_STATUS_LENGTH:
1230             if ((dwFlags & MCI_TRACK) && wmm->wFormat == 2) {
1231                 if (lpParms->dwTrack >= wmm->nTracks)
1232                     return MCIERR_BAD_INTEGER;
1233                 /* FIXME: this is wrong if there is a tempo change inside the file */
1234                 lpParms->dwReturn = MIDI_ConvertPulseToMS(wmm, wmm->tracks[lpParms->dwTrack].dwLength);
1235             } else {
1236                 lpParms->dwReturn = MIDI_GetMThdLengthMS(wmm);
1237             }
1238             lpParms->dwReturn = MIDI_ConvertMSToTimeFormat(wmm, lpParms->dwReturn);
1239             TRACE(mcimidi, "MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1240             break;
1241         case MCI_STATUS_MODE:
1242             lpParms->dwReturn = wmm->dwStatus;
1243             TRACE(mcimidi, "MCI_STATUS_MODE => %lu\n", lpParms->dwReturn);
1244             break;
1245         case MCI_STATUS_MEDIA_PRESENT:
1246             TRACE(mcimidi, "MCI_STATUS_MEDIA_PRESENT => TRUE\n");
1247             lpParms->dwReturn = TRUE;
1248             break;
1249         case MCI_STATUS_NUMBER_OF_TRACKS:
1250             lpParms->dwReturn = (wmm->wFormat == 2) ? wmm->nTracks : 1;
1251             TRACE(mcimidi, "MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
1252             break;
1253         case MCI_STATUS_POSITION:
1254             /* FIXME: do I need to use MCI_TRACK ? */
1255             lpParms->dwReturn = MIDI_ConvertMSToTimeFormat(wmm, 
1256                                                            (dwFlags & MCI_STATUS_START) ? 0 : wmm->dwPositionMS);
1257             TRACE(mcimidi, "MCI_STATUS_POSITION %s => %lu\n", 
1258                   (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1259             break;
1260         case MCI_STATUS_READY:
1261             lpParms->dwReturn = (wmm->dwStatus != MCI_MODE_NOT_READY);
1262             TRACE(mcimidi, "MCI_STATUS_READY = %lu\n", lpParms->dwReturn);
1263             break;
1264         case MCI_STATUS_TIME_FORMAT:
1265             lpParms->dwReturn = wmm->dwMciTimeFormat;
1266             TRACE(mcimidi, "MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1267             break;
1268         case MCI_SEQ_STATUS_DIVTYPE:
1269             TRACE(mcimidi, "MCI_SEQ_STATUS_DIVTYPE !\n");
1270             if (wmm->nDivision > 0x8000) {
1271                 switch (wmm->nDivision) {
1272                 case 0xE8:      lpParms->dwReturn = MCI_SEQ_DIV_SMPTE_24;       break;  /* -24 */
1273                 case 0xE7:      lpParms->dwReturn = MCI_SEQ_DIV_SMPTE_25;       break;  /* -25 */
1274                 case 0xE3:      lpParms->dwReturn = MCI_SEQ_DIV_SMPTE_30DROP;   break;  /* -29 */ /* is the MCI constant correct ? */
1275                 case 0xE2:      lpParms->dwReturn = MCI_SEQ_DIV_SMPTE_30;       break;  /* -30 */
1276                 default:        FIXME(mcimidi, "There is a bad bad programmer\n");
1277                 }
1278             } else {
1279                 lpParms->dwReturn = MCI_SEQ_DIV_PPQN;
1280             }
1281             break;
1282         case MCI_SEQ_STATUS_MASTER:
1283             TRACE(mcimidi, "MCI_SEQ_STATUS_MASTER !\n");
1284             lpParms->dwReturn = 0;
1285             break;
1286         case MCI_SEQ_STATUS_SLAVE:
1287             TRACE(mcimidi, "MCI_SEQ_STATUS_SLAVE !\n");
1288             lpParms->dwReturn = 0;
1289             break;
1290         case MCI_SEQ_STATUS_OFFSET:
1291             TRACE(mcimidi, "MCI_SEQ_STATUS_OFFSET !\n");
1292             lpParms->dwReturn = 0;
1293             break;
1294         case MCI_SEQ_STATUS_PORT:
1295             TRACE(mcimidi, "MCI_SEQ_STATUS_PORT !\n");
1296             lpParms->dwReturn = 0;
1297             break;
1298         case MCI_SEQ_STATUS_TEMPO:
1299             TRACE(mcimidi, "MCI_SEQ_STATUS_TEMPO !\n");
1300             lpParms->dwReturn = wmm->dwTempo;
1301             break;
1302         default:
1303             WARN(mcimidi, "Unknowm command %08lX !\n", lpParms->dwItem);
1304             return MCIERR_UNRECOGNIZED_COMMAND;
1305         }
1306     } else {
1307         WARN(mcimidi, "No Status-Item!\n");
1308         return MCIERR_UNRECOGNIZED_COMMAND;
1309     }
1310     if (dwFlags & MCI_NOTIFY) {
1311         TRACE(mcimidi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1312         mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback), 
1313                         wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1314     }
1315     return 0;
1316 }
1317
1318 /**************************************************************************
1319  *                              MIDI_mciGetDevCaps              [internal]
1320  */
1321 static DWORD MIDI_mciGetDevCaps(UINT16 wDevID, DWORD dwFlags, 
1322                                 LPMCI_GETDEVCAPS_PARMS lpParms)
1323 {
1324     WINE_MCIMIDI*       wmm = MIDI_mciGetOpenDev(wDevID);
1325
1326     TRACE(mcimidi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1327
1328     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1329     if (wmm == NULL)            return MCIERR_INVALID_DEVICE_ID;
1330
1331     if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1332         switch (lpParms->dwItem) {
1333         case MCI_GETDEVCAPS_DEVICE_TYPE:
1334             TRACE(mcimidi, "MCI_GETDEVCAPS_DEVICE_TYPE !\n");
1335             lpParms->dwReturn = MCI_DEVTYPE_SEQUENCER;
1336             break;
1337         case MCI_GETDEVCAPS_HAS_AUDIO:
1338             TRACE(mcimidi, "MCI_GETDEVCAPS_HAS_AUDIO !\n");
1339             lpParms->dwReturn = TRUE;
1340             break;
1341         case MCI_GETDEVCAPS_HAS_VIDEO:
1342             TRACE(mcimidi, "MCI_GETDEVCAPS_HAS_VIDEO !\n");
1343             lpParms->dwReturn = FALSE;
1344             break;
1345         case MCI_GETDEVCAPS_USES_FILES:
1346             TRACE(mcimidi, "MCI_GETDEVCAPS_USES_FILES !\n");
1347             lpParms->dwReturn = TRUE;
1348             break;
1349         case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1350             TRACE(mcimidi, "MCI_GETDEVCAPS_COMPOUND_DEVICE !\n");
1351             lpParms->dwReturn = TRUE;
1352             break;
1353         case MCI_GETDEVCAPS_CAN_EJECT:
1354             TRACE(mcimidi, "MCI_GETDEVCAPS_CAN_EJECT !\n");
1355             lpParms->dwReturn = FALSE;
1356             break;
1357         case MCI_GETDEVCAPS_CAN_PLAY:
1358             TRACE(mcimidi, "MCI_GETDEVCAPS_CAN_PLAY !\n");
1359             lpParms->dwReturn = TRUE;
1360             break;
1361         case MCI_GETDEVCAPS_CAN_RECORD:
1362             TRACE(mcimidi, "MCI_GETDEVCAPS_CAN_RECORD !\n");
1363             lpParms->dwReturn = TRUE;
1364             break;
1365         case MCI_GETDEVCAPS_CAN_SAVE:
1366             TRACE(mcimidi, "MCI_GETDEVCAPS_CAN_SAVE !\n");
1367             lpParms->dwReturn = FALSE;
1368             break;
1369         default:
1370             TRACE(mcimidi, "Unknown capability (%08lx) !\n", lpParms->dwItem);
1371             return MCIERR_UNRECOGNIZED_COMMAND;
1372         }
1373     } else {
1374         TRACE(mcimidi, "No GetDevCaps-Item !\n");
1375         return MCIERR_UNRECOGNIZED_COMMAND;
1376     }
1377     return 0;
1378 }
1379
1380 /**************************************************************************
1381  *                              MIDI_mciInfo                    [internal]
1382  */
1383 static DWORD MIDI_mciInfo(UINT16 wDevID, DWORD dwFlags, LPMCI_INFO_PARMS32A lpParms)
1384 {
1385     DWORD               ret = 0;
1386     LPSTR               str = 0;
1387     WINE_MCIMIDI*       wmm = MIDI_mciGetOpenDev(wDevID);
1388
1389     TRACE(mcimidi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1390
1391     if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1392         ret = MCIERR_NULL_PARAMETER_BLOCK;
1393     } else if (wmm == NULL) {
1394         ret = MCIERR_INVALID_DEVICE_ID;
1395     } else {
1396         TRACE(mcimidi, "buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1397
1398         switch (dwFlags) {
1399         case MCI_INFO_PRODUCT:
1400             str = "Wine's MIDI sequencer";
1401             break;
1402         case MCI_INFO_FILE:
1403             str = wmm->openParms.lpstrElementName;
1404             break;
1405 #if 0
1406         /* FIXME: the following manifest constants are not defined in <WINE>/include/mmsystem.h */
1407         case MCI_INFO_COPYRIGHT:
1408             break;
1409         case MCI_INFO_NAME:
1410             break;
1411 #endif
1412         default:
1413             WARN(mcimidi, "Don't know this info command (%lu)\n", dwFlags);
1414             ret = MCIERR_UNRECOGNIZED_COMMAND;
1415         }
1416     }
1417     if (str) {
1418         ret = MCI_WriteString(lpParms->lpstrReturn, lpParms->dwRetSize, str);
1419     } else {
1420         lpParms->lpstrReturn[0] = 0;
1421     }
1422
1423     return ret;
1424 }
1425
1426 /**************************************************************************
1427  *                              MIDI_mciSeek                    [internal]
1428  */
1429 static DWORD MIDI_mciSeek(UINT16 wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1430 {
1431     DWORD               ret = 0;
1432     WINE_MCIMIDI*       wmm = MIDI_mciGetOpenDev(wDevID);
1433
1434     TRACE(mcimidi, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1435
1436     if (lpParms == NULL) {
1437         ret = MCIERR_NULL_PARAMETER_BLOCK;
1438     } else if (wmm == NULL) {
1439         ret = MCIERR_INVALID_DEVICE_ID;
1440     } else {
1441         MIDI_mciStop(wDevID, MCI_WAIT, 0);
1442         
1443         if (dwFlags & MCI_SEEK_TO_START) {
1444             wmm->dwPositionMS = 0;
1445         } else if (dwFlags & MCI_SEEK_TO_END) {
1446             wmm->dwPositionMS = 0xFFFFFFFF; /* fixme */
1447         } else if (dwFlags & MCI_TO) {
1448             wmm->dwPositionMS = MIDI_ConvertTimeFormatToMS(wmm, lpParms->dwTo);
1449         } else {
1450             WARN(mcimidi, "dwFlag doesn't tell where to seek to...\n");
1451             return MCIERR_MISSING_PARAMETER;
1452         }
1453         
1454         TRACE(mcimidi, "Seeking to position=%lu ms\n", wmm->dwPositionMS);
1455         
1456         if (dwFlags & MCI_NOTIFY) {
1457             TRACE(mcimidi, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1458             mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback), 
1459                             wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1460         }
1461     }
1462     return ret; 
1463 }    
1464 #endif
1465
1466 /*======================================================================*
1467  *                          MIDI entry points                           *
1468  *======================================================================*/
1469
1470 /**************************************************************************
1471  *                              MCIMIDI_DriverProc32    [sample driver]
1472  */
1473 LONG MCIMIDI_DriverProc32(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg, 
1474                           DWORD dwParam1, DWORD dwParam2)
1475 {
1476     switch (wMsg) {
1477     case DRV_LOAD:              return 1;
1478     case DRV_FREE:              return 1;
1479     case DRV_OPEN:              return 1;
1480     case DRV_CLOSE:             return 1;
1481     case DRV_ENABLE:            return 1;
1482     case DRV_DISABLE:           return 1;
1483     case DRV_QUERYCONFIGURE:    return 1;
1484     case DRV_CONFIGURE:         MessageBox16(0, "Sample Midi Linux Driver !", "MMLinux Driver", MB_OK); return 1;
1485     case DRV_INSTALL:           return DRVCNF_RESTART;
1486     case DRV_REMOVE:            return DRVCNF_RESTART;
1487 #ifdef SNDCTL_MIDI_INFO
1488     case MCI_OPEN_DRIVER:       return MIDI_mciOpen      (dwDevID, dwParam1, (LPMCI_OPEN_PARMS32A)   dwParam2);
1489     case MCI_CLOSE_DRIVER:      return MIDI_mciClose     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)   dwParam2);
1490     case MCI_PLAY:              return MIDI_mciPlay      (dwDevID, dwParam1, (LPMCI_PLAY_PARMS)      dwParam2);
1491     case MCI_RECORD:            return MIDI_mciRecord    (dwDevID, dwParam1, (LPMCI_RECORD_PARMS)    dwParam2);
1492     case MCI_STOP:              return MIDI_mciStop      (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)   dwParam2);
1493     case MCI_SET:               return MIDI_mciSet       (dwDevID, dwParam1, (LPMCI_SET_PARMS)       dwParam2);
1494     case MCI_PAUSE:             return MIDI_mciPause     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)   dwParam2);
1495     case MCI_RESUME:            return MIDI_mciResume    (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)   dwParam2);
1496     case MCI_STATUS:            return MIDI_mciStatus    (dwDevID, dwParam1, (LPMCI_STATUS_PARMS)    dwParam2);
1497     case MCI_GETDEVCAPS:        return MIDI_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
1498     case MCI_INFO:              return MIDI_mciInfo      (dwDevID, dwParam1, (LPMCI_INFO_PARMS32A)   dwParam2);
1499     case MCI_SEEK:              return MIDI_mciSeek      (dwDevID, dwParam1, (LPMCI_SEEK_PARMS)      dwParam2);
1500 #else
1501     case MCI_OPEN_DRIVER:
1502     case MCI_CLOSE_DRIVER:
1503     case MCI_PLAY:
1504     case MCI_RECORD:
1505     case MCI_STOP:
1506     case MCI_SET:
1507     case MCI_PAUSE:
1508     case MCI_RESUME:
1509     case MCI_STATUS:
1510     case MCI_GETDEVCAPS:
1511     case MCI_INFO:
1512     case MCI_SEEK:
1513 #endif
1514     case MCI_LOAD:              
1515     case MCI_SAVE:              
1516     case MCI_FREEZE:            
1517     case MCI_PUT:               
1518     case MCI_REALIZE:           
1519     case MCI_UNFREEZE:          
1520     case MCI_UPDATE:            
1521     case MCI_WHERE:             
1522     case MCI_WINDOW:            
1523     case MCI_STEP:              
1524     case MCI_SPIN:              
1525     case MCI_ESCAPE:            
1526     case MCI_COPY:              
1527     case MCI_CUT:               
1528     case MCI_DELETE:            
1529     case MCI_PASTE:             
1530         WARN(mcimidi, "Unsupported command=%s\n", MCI_CommandToString(wMsg));
1531         break;
1532     case MCI_OPEN:
1533     case MCI_CLOSE:
1534         FIXME(mcimidi, "Shouldn't receive a MCI_OPEN or CLOSE message\n");
1535         break;
1536     default:                    
1537         TRACE(mcimidi, "Sending msg=%s to default driver proc\n", MCI_CommandToString(wMsg));
1538         return DefDriverProc32(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1539     }
1540     return MCIERR_UNRECOGNIZED_COMMAND;
1541 }
1542
1543 /*-----------------------------------------------------------------------*/