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