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