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