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