ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

mcimidi.c
Go to the documentation of this file.
00001 /*
00002  * Sample MIDI Wine Driver for Linux
00003  *
00004  * Copyright 1994 Martin Ayotte
00005  * Copyright 1999 Eric Pouech
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this library; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00020  */
00021 
00022 /* TODO:
00023  *      + implement it correctly
00024  *      + finish asynchronous commands
00025  *      + better implement non waiting command (without the MCI_WAIT flag).
00026  */
00027 
00028 #include <stdlib.h>
00029 #include <stdarg.h>
00030 #include <stdio.h>
00031 #include <string.h>
00032 
00033 #include "windef.h"
00034 #include "winbase.h"
00035 #include "wingdi.h"
00036 #include "winuser.h"
00037 #include "wownt32.h"
00038 #include "mmddk.h"
00039 #include "wine/debug.h"
00040 #include "wine/unicode.h"
00041 
00042 WINE_DEFAULT_DEBUG_CHANNEL(mcimidi);
00043 
00044 #define MIDI_NOTEOFF             0x80
00045 #define MIDI_NOTEON              0x90
00046 
00047 typedef struct {
00048     DWORD       dwFirst;        /* offset in file of track */
00049     DWORD       dwLast;         /* number of bytes in file of track */
00050     DWORD       dwIndex;        /* current index in file (dwFirst <= dwIndex < dwLast) */
00051     DWORD       dwLength;       /* number of pulses in this track */
00052     DWORD       dwEventPulse;       /* current pulse # (event) pointed by dwIndex */
00053     DWORD       dwEventData;        /* current data    (event) pointed by dwIndex */
00054     WORD        wEventLength;       /* current length  (event) pointed by dwIndex */
00055     WORD        wStatus : 1,        /* 1 : playing, 0 : done */
00056                     wTrackNr : 7,
00057                     wLastCommand : 8;   /* last MIDI command on track */
00058 } MCI_MIDITRACK;
00059 
00060 typedef struct tagWINE_MCIMIDI {
00061     UINT        wDevID;         /* the MCI one */
00062     HMIDI       hMidi;
00063     int         nUseCount;              /* Incremented for each shared open          */
00064     HANDLE      hCallback;          /* Callback handle for pending notification  */
00065     HMMIO       hFile;                  /* mmio file handle open as Element          */
00066     LPWSTR      lpstrElementName;       /* Name of file (if any)                     */
00067     LPWSTR      lpstrCopyright;
00068     LPWSTR      lpstrName;
00069     WORD        wPort;          /* the WINMM device unit */
00070     WORD        dwStatus;       /* one from MCI_MODE_xxxx */
00071     DWORD       dwMciTimeFormat;    /* One of the supported MCI_FORMAT_xxxx */
00072     WORD        wFormat;        /* Format of MIDI hFile (0, 1 or 2) */
00073     WORD        nTracks;        /* Number of tracks in hFile */
00074     WORD        nDivision;      /* Number of division in hFile PPQN or SMPTE */
00075     WORD        wStartedPlaying;
00076     DWORD       dwTempo;        /* Tempo (# of 1/4 note per second */
00077     MCI_MIDITRACK*      tracks;         /* Content of each track */
00078     DWORD       dwPulse;
00079     DWORD       dwPositionMS;
00080     DWORD       dwStartTicks;
00081 } WINE_MCIMIDI;
00082 
00083 /* ===================================================================
00084  * ===================================================================
00085  * FIXME: should be using the new mmThreadXXXX functions from WINMM
00086  * instead of those
00087  * it would require to add a wine internal flag to mmThreadCreate
00088  * in order to pass a 32 bit function instead of a 16 bit
00089  * ===================================================================
00090  * =================================================================== */
00091 
00092 struct SCA {
00093     UINT    wDevID;
00094     UINT    wMsg;
00095     DWORD_PTR   dwParam1;
00096     DWORD_PTR   dwParam2;
00097 };
00098 
00099 /* EPP DWORD WINAPI mciSendCommandA(UINT wDevID, UINT wMsg, DWORD dwParam1, DWORD dwParam2); */
00100 
00101 /**************************************************************************
00102  *              MCI_SCAStarter          [internal]
00103  */
00104 static DWORD CALLBACK   MCI_SCAStarter(LPVOID arg)
00105 {
00106     struct SCA* sca = arg;
00107     DWORD       ret;
00108 
00109     TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
00110       sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
00111     ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
00112     TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
00113       sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
00114     HeapFree(GetProcessHeap(), 0, sca);
00115     return ret;
00116 }
00117 
00118 /**************************************************************************
00119  *              MCI_SendCommandAsync        [internal]
00120  */
00121 static  DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD_PTR dwParam1,
00122                    DWORD_PTR dwParam2, UINT size)
00123 {
00124     HANDLE handle;
00125     struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
00126 
00127     if (sca == 0)
00128     return MCIERR_OUT_OF_MEMORY;
00129 
00130     sca->wDevID   = wDevID;
00131     sca->wMsg     = wMsg;
00132     sca->dwParam1 = dwParam1;
00133 
00134     if (size && dwParam2) {
00135     sca->dwParam2 = (DWORD_PTR)sca + sizeof(struct SCA);
00136     /* copy structure passed by program in dwParam2 to be sure
00137      * we can still use it whatever the program does
00138      */
00139     memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
00140     } else {
00141     sca->dwParam2 = dwParam2;
00142     }
00143 
00144     if ((handle = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
00145     WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
00146     return MCI_SCAStarter(sca);
00147     }
00148     SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL);
00149     CloseHandle(handle);
00150     return 0;
00151 }
00152 
00153 /*======================================================================*
00154  *                          MCI MIDI implementation         *
00155  *======================================================================*/
00156 
00157 static DWORD MIDI_mciResume(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
00158 
00159 /**************************************************************************
00160  *              MIDI_drvOpen            [internal]
00161  */
00162 static  DWORD   MIDI_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
00163 {
00164     WINE_MCIMIDI*   wmm;
00165 
00166     if (!modp) return 0xFFFFFFFF;
00167 
00168     wmm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIMIDI));
00169 
00170     if (!wmm)
00171     return 0;
00172 
00173     wmm->wDevID = modp->wDeviceID;
00174     mciSetDriverData(wmm->wDevID, (DWORD_PTR)wmm);
00175     modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
00176     modp->wType = MCI_DEVTYPE_SEQUENCER;
00177     return modp->wDeviceID;
00178 }
00179 
00180 /**************************************************************************
00181  *              MCIMIDI_drvClose        [internal]
00182  */
00183 static  DWORD   MIDI_drvClose(DWORD dwDevID)
00184 {
00185     WINE_MCIMIDI*  wmm = (WINE_MCIMIDI*)mciGetDriverData(dwDevID);
00186 
00187     if (wmm) {
00188     HeapFree(GetProcessHeap(), 0, wmm);
00189     mciSetDriverData(dwDevID, 0);
00190     return 1;
00191     }
00192     return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
00193 }
00194 
00195 /**************************************************************************
00196  *              MIDI_mciGetOpenDev      [internal]
00197  */
00198 static WINE_MCIMIDI*  MIDI_mciGetOpenDev(MCIDEVICEID wDevID, UINT wMsg)
00199 {
00200     WINE_MCIMIDI*   wmm = (WINE_MCIMIDI*)mciGetDriverData(wDevID);
00201 
00202     if (wmm == NULL || ((wmm->nUseCount == 0) ^ (wMsg == MCI_OPEN_DRIVER))) {
00203     WARN("Invalid wDevID=%u\n", wDevID);
00204     return 0;
00205     }
00206     return wmm;
00207 }
00208 
00209 /**************************************************************************
00210  *              MIDI_mciNotify          [internal]
00211  *
00212  * Notifications in MCI work like a 1-element queue.
00213  * Each new notification request supersedes the previous one.
00214  * This affects Play and Record; other commands are immediate.
00215  */
00216 static void MIDI_mciNotify(DWORD_PTR hWndCallBack, WINE_MCIMIDI* wmm, UINT wStatus)
00217 {
00218     /* We simply save one parameter by not passing the wDevID local
00219      * to the command.  They are the same (via mciGetDriverData).
00220      */
00221     MCIDEVICEID wDevID = wmm->wDevID;
00222     HANDLE old = InterlockedExchangePointer(&wmm->hCallback, NULL);
00223     if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED);
00224     mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus);
00225 }
00226 
00227 /**************************************************************************
00228  *              MIDI_mciReadByte        [internal]
00229  */
00230 static DWORD MIDI_mciReadByte(WINE_MCIMIDI* wmm, BYTE *lpbyt)
00231 {
00232     DWORD   ret = 0;
00233 
00234     if (lpbyt == NULL ||
00235     mmioRead(wmm->hFile, (HPSTR)lpbyt, sizeof(BYTE)) != (long)sizeof(BYTE)) {
00236     WARN("Error reading wmm=%p\n", wmm);
00237     ret = MCIERR_INVALID_FILE;
00238     }
00239 
00240     return ret;
00241 }
00242 
00243 /**************************************************************************
00244  *              MIDI_mciReadWord        [internal]
00245  */
00246 static DWORD MIDI_mciReadWord(WINE_MCIMIDI* wmm, LPWORD lpw)
00247 {
00248     BYTE    hibyte, lobyte;
00249     DWORD   ret = MCIERR_INVALID_FILE;
00250 
00251     if (lpw != NULL &&
00252     MIDI_mciReadByte(wmm, &hibyte) == 0 &&
00253     MIDI_mciReadByte(wmm, &lobyte) == 0) {
00254     *lpw = ((WORD)hibyte << 8) + lobyte;
00255     ret = 0;
00256     }
00257     return ret;
00258 }
00259 
00260 /**************************************************************************
00261  *              MIDI_mciReadLong        [internal]
00262  */
00263 static DWORD MIDI_mciReadLong(WINE_MCIMIDI* wmm, LPDWORD lpdw)
00264 {
00265     WORD    hiword, loword;
00266     DWORD   ret = MCIERR_INVALID_FILE;
00267 
00268     if (lpdw != NULL &&
00269     MIDI_mciReadWord(wmm, &hiword) == 0 &&
00270     MIDI_mciReadWord(wmm, &loword) == 0) {
00271     *lpdw = MAKELONG(loword, hiword);
00272     ret = 0;
00273     }
00274     return ret;
00275 }
00276 
00277 /**************************************************************************
00278  *                  MIDI_mciReadVaryLen     [internal]
00279  */
00280 static WORD MIDI_mciReadVaryLen(WINE_MCIMIDI* wmm, LPDWORD lpdw)
00281 {
00282     BYTE    byte;
00283     DWORD   value = 0;
00284     WORD    ret = 0;
00285 
00286     if (lpdw == NULL) {
00287     ret = MCIERR_INVALID_FILE;
00288     } else {
00289     do {
00290         if (MIDI_mciReadByte(wmm, &byte) != 0) {
00291         return 0;
00292         }
00293         value = (value << 7) + (byte & 0x7F);
00294         ret++;
00295     } while (byte & 0x80);
00296     *lpdw = value;
00297     /*
00298       TRACE("val=%08X\n", value);
00299     */
00300     }
00301     return ret;
00302 }
00303 
00304 /**************************************************************************
00305  *              MIDI_mciReadNextEvent       [internal]
00306  */
00307 static DWORD    MIDI_mciReadNextEvent(WINE_MCIMIDI* wmm, MCI_MIDITRACK* mmt)
00308 {
00309     BYTE    b1, b2 = 0, b3;
00310     WORD    hw = 0;
00311     DWORD   evtPulse;
00312     DWORD   evtLength;
00313     DWORD   tmp;
00314 
00315     if (mmioSeek(wmm->hFile, mmt->dwIndex, SEEK_SET) != mmt->dwIndex) {
00316     WARN("Can't seek at %08X\n", mmt->dwIndex);
00317     return MCIERR_INVALID_FILE;
00318     }
00319     evtLength = MIDI_mciReadVaryLen(wmm, &evtPulse) + 1;    /* > 0 */
00320     MIDI_mciReadByte(wmm, &b1);
00321     switch (b1) {
00322     case 0xF0:
00323     case 0xF7:
00324     evtLength += MIDI_mciReadVaryLen(wmm, &tmp);
00325     evtLength += tmp;
00326     break;
00327     case 0xFF:
00328     MIDI_mciReadByte(wmm, &b2); evtLength++;
00329 
00330     evtLength += MIDI_mciReadVaryLen(wmm, &tmp);
00331     if (evtLength >= 0x10000u) {
00332         /* this limitation shouldn't be a problem */
00333         WARN("Ouch !! Implementation limitation to 64k bytes for a MIDI event is overflowed\n");
00334         hw = 0xFFFF;
00335     } else {
00336         hw = LOWORD(evtLength);
00337     }
00338     evtLength += tmp;
00339     break;
00340     default:
00341     if (b1 & 0x80) { /* use running status ? */
00342         mmt->wLastCommand = b1;
00343         MIDI_mciReadByte(wmm, &b2); evtLength++;
00344     } else {
00345         b2 = b1;
00346         b1 = mmt->wLastCommand;
00347     }
00348     switch ((b1 >> 4) & 0x07) {
00349     case 0: case 1: case 2: case 3: case 6:
00350         MIDI_mciReadByte(wmm, &b3); evtLength++;
00351         hw = b3;
00352         break;
00353     case 4: case 5:
00354         break;
00355     case 7:
00356         WARN("Strange indeed b1=0x%02x\n", b1);
00357     }
00358     break;
00359     }
00360     if (mmt->dwIndex + evtLength > mmt->dwLast)
00361     return MCIERR_INTERNAL;
00362 
00363     mmt->dwEventPulse += evtPulse;
00364     mmt->dwEventData   = (hw << 16) + (b2 << 8) + b1;
00365     mmt->wEventLength  = evtLength;
00366 
00367     /*
00368       TRACE("[%u] => pulse=%08x(%08x), data=%08x, length=%u\n",
00369       mmt->wTrackNr, mmt->dwEventPulse, evtPulse,
00370       mmt->dwEventData, mmt->wEventLength);
00371     */
00372     return 0;
00373 }
00374 
00375 /**************************************************************************
00376  *              MIDI_mciReadMTrk        [internal]
00377  */
00378 static DWORD MIDI_mciReadMTrk(WINE_MCIMIDI* wmm, MCI_MIDITRACK* mmt)
00379 {
00380     DWORD       toberead;
00381     FOURCC      fourcc;
00382 
00383     if (mmioRead(wmm->hFile, (HPSTR)&fourcc, (long)sizeof(FOURCC)) !=
00384     (long)sizeof(FOURCC)) {
00385     return MCIERR_INVALID_FILE;
00386     }
00387 
00388     if (fourcc != mmioFOURCC('M', 'T', 'r', 'k')) {
00389     WARN("Can't synchronize on 'MTrk' !\n");
00390     return MCIERR_INVALID_FILE;
00391     }
00392 
00393     if (MIDI_mciReadLong(wmm, &toberead) != 0) {
00394     return MCIERR_INVALID_FILE;
00395     }
00396     mmt->dwFirst = mmioSeek(wmm->hFile, 0, SEEK_CUR); /* >= 0 */
00397     mmt->dwLast = mmt->dwFirst + toberead;
00398 
00399     /* compute # of pulses in this track */
00400     mmt->dwIndex = mmt->dwFirst;
00401     mmt->dwEventPulse = 0;
00402 
00403     while (MIDI_mciReadNextEvent(wmm, mmt) == 0 && LOWORD(mmt->dwEventData) != 0x2FFF) {
00404     char    buf[1024];
00405     WORD    len;
00406 
00407     mmt->dwIndex += mmt->wEventLength;
00408 
00409     switch (LOWORD(mmt->dwEventData)) {
00410     case 0x02FF:
00411     case 0x03FF:
00412         /* position after meta data header */
00413         mmioSeek(wmm->hFile, mmt->dwIndex + HIWORD(mmt->dwEventData), SEEK_SET);
00414         len = mmt->wEventLength - HIWORD(mmt->dwEventData);
00415 
00416         if (len >= sizeof(buf)) {
00417         WARN("Buffer for text is too small (%u are needed)\n", len);
00418         len = sizeof(buf) - 1;
00419         }
00420             if (mmioRead(wmm->hFile, buf, len) == len) {
00421         buf[len] = 0;   /* end string in case */
00422         switch (HIBYTE(LOWORD(mmt->dwEventData))) {
00423         case 0x02:
00424             if (wmm->lpstrCopyright) {
00425             WARN("Two copyright notices (%s|%s)\n", debugstr_w(wmm->lpstrCopyright), buf);
00426             } else {
00427                         len = MultiByteToWideChar( CP_ACP, 0, buf, -1, NULL, 0 );
00428                         wmm->lpstrCopyright = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
00429                         MultiByteToWideChar( CP_ACP, 0, buf, -1, wmm->lpstrCopyright, len );
00430             }
00431             break;
00432         case 0x03:
00433             if (wmm->lpstrName) {
00434             WARN("Two names (%s|%s)\n", debugstr_w(wmm->lpstrName), buf);
00435             } else {
00436                         len = MultiByteToWideChar( CP_ACP, 0, buf, -1, NULL, 0 );
00437                         wmm->lpstrName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
00438                         MultiByteToWideChar( CP_ACP, 0, buf, -1, wmm->lpstrName, len );
00439             }
00440             break;
00441         }
00442         }
00443         break;
00444     }
00445     }
00446     mmt->dwLength = mmt->dwEventPulse;
00447 
00448     TRACE("Track %u has %u bytes and %u pulses\n", mmt->wTrackNr, toberead, mmt->dwLength);
00449 
00450     /* reset track data */
00451     mmt->wStatus = 1;   /* ok, playing */
00452     mmt->dwIndex = mmt->dwFirst;
00453     mmt->dwEventPulse = 0;
00454 
00455     if (mmioSeek(wmm->hFile, 0, SEEK_CUR) != mmt->dwLast) {
00456     WARN("Ouch, out of sync seek=%u track=%u\n",
00457          mmioSeek(wmm->hFile, 0, SEEK_CUR), mmt->dwLast);
00458     /* position at end of this track, to be ready to read next track */
00459     mmioSeek(wmm->hFile, mmt->dwLast, SEEK_SET);
00460     }
00461 
00462     return 0;
00463 }
00464 
00465 /**************************************************************************
00466  *              MIDI_mciReadMThd        [internal]
00467  */
00468 static DWORD MIDI_mciReadMThd(WINE_MCIMIDI* wmm, DWORD dwOffset)
00469 {
00470     DWORD   toberead;
00471     FOURCC  fourcc;
00472     WORD    nt;
00473 
00474     TRACE("(%p, %08X);\n", wmm, dwOffset);
00475 
00476     if (mmioSeek(wmm->hFile, dwOffset, SEEK_SET) != dwOffset) {
00477     WARN("Can't seek at %08X begin of 'MThd'\n", dwOffset);
00478     return MCIERR_INVALID_FILE;
00479     }
00480     if (mmioRead(wmm->hFile, (HPSTR)&fourcc,
00481            (long) sizeof(FOURCC)) != (long) sizeof(FOURCC))
00482     return MCIERR_INVALID_FILE;
00483 
00484     if (fourcc != mmioFOURCC('M', 'T', 'h', 'd')) {
00485     WARN("Can't synchronize on 'MThd' !\n");
00486     return MCIERR_INVALID_FILE;
00487     }
00488 
00489     if (MIDI_mciReadLong(wmm, &toberead) != 0 || toberead < 3 * sizeof(WORD))
00490     return MCIERR_INVALID_FILE;
00491 
00492     if (MIDI_mciReadWord(wmm, &wmm->wFormat) != 0 ||
00493     MIDI_mciReadWord(wmm, &wmm->nTracks) != 0 ||
00494     MIDI_mciReadWord(wmm, &wmm->nDivision) != 0) {
00495     return MCIERR_INVALID_FILE;
00496     }
00497 
00498     TRACE("toberead=0x%08X, wFormat=0x%04X nTracks=0x%04X nDivision=0x%04X\n",
00499       toberead, wmm->wFormat, wmm->nTracks, wmm->nDivision);
00500 
00501     /* MS doc says that the MIDI MCI time format must be put by default to the format
00502      * stored in the MIDI file...
00503      */
00504     if (wmm->nDivision > 0x8000) {
00505     /* eric.pouech@lemel.fr 98/11
00506      * I did not check this very code (pulses are expressed as SMPTE sub-frames).
00507      * In about 40 MB of MIDI files I have, none was SMPTE based...
00508      * I'm just wondering if this is widely used :-). So, if someone has one of
00509      * these files, I'd like to know about it.
00510      */
00511     FIXME("Handling SMPTE time in MIDI files has not been tested\n"
00512           "Please report to comp.emulators.ms-windows.wine with MIDI file !\n");
00513 
00514     switch (HIBYTE(wmm->nDivision)) {
00515     case 0xE8:  wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_24; break;  /* -24 */
00516     case 0xE7:  wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_25; break;  /* -25 */
00517     case 0xE3:  wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_30DROP; break;  /* -29 */ /* is the MCI constant correct ? */
00518     case 0xE2:  wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_30; break;  /* -30 */
00519     default:
00520         WARN("Unsupported number of frames %d\n", -(char)HIBYTE(wmm->nDivision));
00521         return MCIERR_INVALID_FILE;
00522     }
00523     switch (LOBYTE(wmm->nDivision)) {
00524     case 4: /* MIDI Time Code */
00525     case 8:
00526     case 10:
00527     case 80: /* SMPTE bit resolution */
00528     case 100:
00529     default:
00530         WARN("Unsupported number of sub-frames %d\n", LOBYTE(wmm->nDivision));
00531         return MCIERR_INVALID_FILE;
00532     }
00533     } else if (wmm->nDivision == 0) {
00534     WARN("Number of division is 0, can't support that !!\n");
00535     return MCIERR_INVALID_FILE;
00536     } else {
00537     wmm->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
00538     }
00539 
00540     switch (wmm->wFormat) {
00541     case 0:
00542     if (wmm->nTracks != 1) {
00543         WARN("Got type 0 file whose number of track is not 1. Setting it to 1\n");
00544         wmm->nTracks = 1;
00545     }
00546     break;
00547     case 1:
00548     case 2:
00549     break;
00550     default:
00551     WARN("Handling MIDI files which format = %d is not (yet) supported\n"
00552          "Please report with MIDI file !\n", wmm->wFormat);
00553     return MCIERR_INVALID_FILE;
00554     }
00555 
00556     if (wmm->nTracks & 0x8000) {
00557     /* this shouldn't be a problem... */
00558     WARN("Ouch !! Implementation limitation to 32k tracks per MIDI file is overflowed\n");
00559     wmm->nTracks = 0x7FFF;
00560     }
00561 
00562     if ((wmm->tracks = HeapAlloc(GetProcessHeap(), 0, sizeof(MCI_MIDITRACK) * wmm->nTracks)) == NULL) {
00563     return MCIERR_OUT_OF_MEMORY;
00564     }
00565 
00566     toberead -= 3 * sizeof(WORD);
00567     if (toberead > 0) {
00568     TRACE("Size of MThd > 6, skipping %d extra bytes\n", toberead);
00569     mmioSeek(wmm->hFile, toberead, SEEK_CUR);
00570     }
00571 
00572     for (nt = 0; nt < wmm->nTracks; nt++) {
00573     wmm->tracks[nt].wTrackNr = nt;
00574     if (MIDI_mciReadMTrk(wmm, &wmm->tracks[nt]) != 0) {
00575         WARN("Can't read 'MTrk' header\n");
00576         return MCIERR_INVALID_FILE;
00577     }
00578     }
00579 
00580     wmm->dwTempo = 500000;
00581 
00582     return 0;
00583 }
00584 
00585 /**************************************************************************
00586  *          MIDI_ConvertPulseToMS           [internal]
00587  */
00588 static  DWORD   MIDI_ConvertPulseToMS(WINE_MCIMIDI* wmm, DWORD pulse)
00589 {
00590     DWORD   ret = 0;
00591 
00592     /* FIXME: this function may return false values since the tempo (wmm->dwTempo)
00593      * may change during file playing
00594      */
00595     if (wmm->nDivision == 0) {
00596     FIXME("Shouldn't happen. wmm->nDivision = 0\n");
00597     } else if (wmm->nDivision > 0x8000) { /* SMPTE, unchecked FIXME? */
00598     int nf = -(char)HIBYTE(wmm->nDivision); /* number of frames     */
00599     int nsf = LOBYTE(wmm->nDivision);       /* number of sub-frames */
00600     ret = (pulse * 1000) / (nf * nsf);
00601     } else {
00602     ret = (DWORD)((double)pulse * ((double)wmm->dwTempo / 1000) /
00603               (double)wmm->nDivision);
00604     }
00605 
00606     /*
00607       TRACE("pulse=%u tempo=%u division=%u=0x%04x => ms=%u\n",
00608       pulse, wmm->dwTempo, wmm->nDivision, wmm->nDivision, ret);
00609     */
00610 
00611     return ret;
00612 }
00613 
00614 #define TIME_MS_IN_ONE_HOUR (60*60*1000)
00615 #define TIME_MS_IN_ONE_MINUTE   (60*1000)
00616 #define TIME_MS_IN_ONE_SECOND   (1000)
00617 
00618 /**************************************************************************
00619  *          MIDI_ConvertTimeFormatToMS      [internal]
00620  */
00621 static  DWORD   MIDI_ConvertTimeFormatToMS(WINE_MCIMIDI* wmm, DWORD val)
00622 {
00623     DWORD   ret = 0;
00624 
00625     switch (wmm->dwMciTimeFormat) {
00626     case MCI_FORMAT_MILLISECONDS:
00627     ret = val;
00628     break;
00629     case MCI_FORMAT_SMPTE_24:
00630     ret =
00631         (HIBYTE(HIWORD(val)) * 125) / 3 +             LOBYTE(HIWORD(val)) * TIME_MS_IN_ONE_SECOND +
00632         HIBYTE(LOWORD(val)) * TIME_MS_IN_ONE_MINUTE + LOBYTE(LOWORD(val)) * TIME_MS_IN_ONE_HOUR;
00633     break;
00634     case MCI_FORMAT_SMPTE_25:
00635     ret =
00636         HIBYTE(HIWORD(val)) * 40 +            LOBYTE(HIWORD(val)) * TIME_MS_IN_ONE_SECOND +
00637         HIBYTE(LOWORD(val)) * TIME_MS_IN_ONE_MINUTE + LOBYTE(LOWORD(val)) * TIME_MS_IN_ONE_HOUR;
00638     break;
00639     case MCI_FORMAT_SMPTE_30:
00640     ret =
00641         (HIBYTE(HIWORD(val)) * 100) / 3 +         LOBYTE(HIWORD(val)) * TIME_MS_IN_ONE_SECOND +
00642         HIBYTE(LOWORD(val)) * TIME_MS_IN_ONE_MINUTE + LOBYTE(LOWORD(val)) * TIME_MS_IN_ONE_HOUR;
00643     break;
00644     default:
00645     WARN("Bad time format %u!\n", wmm->dwMciTimeFormat);
00646     }
00647     /*
00648       TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmm->dwMciTimeFormat, ret);
00649     */
00650     return ret;
00651 }
00652 
00653 /**************************************************************************
00654  *          MIDI_ConvertMSToTimeFormat      [internal]
00655  */
00656 static  DWORD   MIDI_ConvertMSToTimeFormat(WINE_MCIMIDI* wmm, DWORD _val)
00657 {
00658     DWORD   ret = 0, val = _val;
00659     DWORD   h, m, s, f;
00660 
00661     switch (wmm->dwMciTimeFormat) {
00662     case MCI_FORMAT_MILLISECONDS:
00663     ret = val;
00664     break;
00665     case MCI_FORMAT_SMPTE_24:
00666     case MCI_FORMAT_SMPTE_25:
00667     case MCI_FORMAT_SMPTE_30:
00668     h = val / TIME_MS_IN_ONE_HOUR;
00669     m = (val -= h * TIME_MS_IN_ONE_HOUR)   / TIME_MS_IN_ONE_MINUTE;
00670     s = (val -= m * TIME_MS_IN_ONE_MINUTE) / TIME_MS_IN_ONE_SECOND;
00671     switch (wmm->dwMciTimeFormat) {
00672     case MCI_FORMAT_SMPTE_24:
00673         /* one frame is 1000/24 val long, 1000/24 == 125/3 */
00674         f = (val * 3) / 125;    val -= (f * 125) / 3;
00675         break;
00676     case MCI_FORMAT_SMPTE_25:
00677         /* one frame is 1000/25 ms long, 1000/25 == 40 */
00678         f = val / 40;       val -= f * 40;
00679         break;
00680     case MCI_FORMAT_SMPTE_30:
00681         /* one frame is 1000/30 ms long, 1000/30 == 100/3 */
00682         f = (val * 3) / 100;    val -= (f * 100) / 3;
00683         break;
00684     default:
00685         FIXME("There must be some bad bad programmer\n");
00686         f = 0;
00687     }
00688     /* val contains the number of ms which cannot make a complete frame */
00689     /* FIXME: is this correct ? programs seem to be happy with that */
00690     ret = (f << 24) | (s << 16) | (m << 8) | (h << 0);
00691     break;
00692     default:
00693     WARN("Bad time format %u!\n", wmm->dwMciTimeFormat);
00694     }
00695     /*
00696       TRACE("val=%u [tf=%u] => ret=%u=0x%08x\n", _val, wmm->dwMciTimeFormat, ret, ret);
00697     */
00698     return ret;
00699 }
00700 
00701 /**************************************************************************
00702  *          MIDI_GetMThdLengthMS            [internal]
00703  */
00704 static  DWORD   MIDI_GetMThdLengthMS(WINE_MCIMIDI* wmm)
00705 {
00706     WORD    nt;
00707     DWORD   ret = 0;
00708 
00709     for (nt = 0; nt < wmm->nTracks; nt++) {
00710     if (wmm->wFormat == 2) {
00711         ret += wmm->tracks[nt].dwLength;
00712     } else if (wmm->tracks[nt].dwLength > ret) {
00713         ret = wmm->tracks[nt].dwLength;
00714     }
00715     }
00716     /* FIXME: this is wrong if there is a tempo change inside the file */
00717     return MIDI_ConvertPulseToMS(wmm, ret);
00718 }
00719 
00720 /**************************************************************************
00721  *              MIDI_mciOpen            [internal]
00722  */
00723 static DWORD MIDI_mciOpen(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_OPEN_PARMSW lpParms)
00724 {
00725     DWORD       dwRet = 0;
00726 
00727     TRACE("(%d, %08X, %p)\n", wmm->wDevID, dwFlags, lpParms);
00728 
00729     if (lpParms == NULL)    return MCIERR_NULL_PARAMETER_BLOCK;
00730     if (dwFlags & MCI_OPEN_SHAREABLE)
00731     return MCIERR_HARDWARE;
00732 
00733     if (wmm->nUseCount > 0) {
00734     /* The driver is already opened on this channel
00735      * MIDI sequencer cannot be shared
00736      */
00737     return MCIERR_DEVICE_OPEN;
00738     }
00739     wmm->nUseCount++;
00740 
00741     wmm->hFile = 0;
00742     wmm->hMidi = 0;
00743     wmm->wPort = MIDI_MAPPER;
00744     wmm->lpstrElementName = NULL;
00745 
00746     TRACE("wDevID=%d (lpParams->wDeviceID=%d)\n", wmm->wDevID, lpParms->wDeviceID);
00747     /*  lpParms->wDeviceID = wDevID;*/
00748 
00749     if (dwFlags & MCI_OPEN_ELEMENT) {
00750     TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(lpParms->lpstrElementName));
00751     if (lpParms->lpstrElementName && strlenW(lpParms->lpstrElementName) > 0) {
00752         wmm->hFile = mmioOpenW((LPWSTR)lpParms->lpstrElementName, NULL,
00753                    MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE);
00754         if (wmm->hFile == 0) {
00755         WARN("Can't find file %s!\n", debugstr_w(lpParms->lpstrElementName));
00756         wmm->nUseCount--;
00757         return MCIERR_FILE_NOT_FOUND;
00758         }
00759             wmm->lpstrElementName = HeapAlloc(GetProcessHeap(), 0, 
00760                                               (strlenW(lpParms->lpstrElementName) + 1) * sizeof(WCHAR));
00761             strcpyW(wmm->lpstrElementName, lpParms->lpstrElementName);
00762     }
00763     }
00764     TRACE("hFile=%p\n", wmm->hFile);
00765 
00766     wmm->lpstrCopyright = NULL;
00767     wmm->lpstrName = NULL;
00768 
00769     wmm->dwStatus = MCI_MODE_NOT_READY; /* while loading file contents */
00770     /* spec says it should be the default format from the MIDI file... */
00771     wmm->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
00772 
00773     if (wmm->hFile != 0) {
00774     MMCKINFO    ckMainRIFF;
00775     MMCKINFO    mmckInfo;
00776     DWORD       dwOffset = 0;
00777 
00778     if (mmioDescend(wmm->hFile, &ckMainRIFF, NULL, 0) != 0) {
00779         dwRet = MCIERR_INVALID_FILE;
00780     } else {
00781         TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
00782           (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize);
00783 
00784         if (ckMainRIFF.ckid == FOURCC_RIFF && ckMainRIFF.fccType == mmioFOURCC('R', 'M', 'I', 'D')) {
00785         mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a');
00786         mmioSeek(wmm->hFile, ckMainRIFF.dwDataOffset + ((ckMainRIFF.cksize + 1) & ~1), SEEK_SET);
00787         if (mmioDescend(wmm->hFile, &mmckInfo, &ckMainRIFF, MMIO_FINDCHUNK) == 0) {
00788             TRACE("... is a 'RMID' file\n");
00789             dwOffset = mmckInfo.dwDataOffset;
00790         } else {
00791             dwRet = MCIERR_INVALID_FILE;
00792         }
00793         }
00794         if (dwRet == 0 && MIDI_mciReadMThd(wmm, dwOffset) != 0) {
00795         WARN("Can't read 'MThd' header\n");
00796         dwRet = MCIERR_INVALID_FILE;
00797         }
00798     }
00799     } else {
00800     TRACE("hFile==0, setting #tracks to 0; is this correct ?\n");
00801     wmm->nTracks = 0;
00802     wmm->wFormat = 0;
00803     wmm->nDivision = 1;
00804     }
00805     if (dwRet != 0) {
00806     wmm->nUseCount--;
00807     if (wmm->hFile != 0)
00808         mmioClose(wmm->hFile, 0);
00809     wmm->hFile = 0;
00810     } else {
00811     wmm->dwPositionMS = 0;
00812     wmm->dwStatus = MCI_MODE_STOP;
00813     if (dwFlags & MCI_NOTIFY)
00814         MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
00815     }
00816     return dwRet;
00817 }
00818 
00819 /**************************************************************************
00820  *              MIDI_mciStop            [internal]
00821  */
00822 static DWORD MIDI_mciStop(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
00823 {
00824     DWORD       dwRet = 0;
00825 
00826     TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
00827 
00828     if (wmm->dwStatus != MCI_MODE_STOP) {
00829     HANDLE old = InterlockedExchangePointer(&wmm->hCallback, NULL);
00830     if (old) mciDriverNotify(old, wmm->wDevID, MCI_NOTIFY_ABORTED);
00831     }
00832 
00833     if (wmm->dwStatus != MCI_MODE_STOP) {
00834     int oldstat = wmm->dwStatus;
00835 
00836     wmm->dwStatus = MCI_MODE_NOT_READY;
00837     if (oldstat == MCI_MODE_PAUSE)
00838         dwRet = midiOutReset((HMIDIOUT)wmm->hMidi);
00839 
00840     while (wmm->dwStatus != MCI_MODE_STOP)
00841         Sleep(10);
00842     }
00843 
00844     /* sanity reset */
00845     wmm->dwStatus = MCI_MODE_STOP;
00846 
00847     if ((dwFlags & MCI_NOTIFY) && lpParms && MMSYSERR_NOERROR==dwRet)
00848     MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
00849     return dwRet;
00850 }
00851 
00852 /**************************************************************************
00853  *              MIDI_mciClose           [internal]
00854  */
00855 static DWORD MIDI_mciClose(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
00856 {
00857 
00858     TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
00859 
00860     if (wmm->dwStatus != MCI_MODE_STOP) {
00861     /* mciStop handles MCI_NOTIFY_ABORTED */
00862     MIDI_mciStop(wmm, MCI_WAIT, lpParms);
00863     }
00864 
00865     wmm->nUseCount--;
00866     if (wmm->nUseCount == 0) {
00867     if (wmm->hFile != 0) {
00868         mmioClose(wmm->hFile, 0);
00869         wmm->hFile = 0;
00870         TRACE("hFile closed !\n");
00871     }
00872     HeapFree(GetProcessHeap(), 0, wmm->tracks);
00873     HeapFree(GetProcessHeap(), 0, wmm->lpstrElementName);
00874     HeapFree(GetProcessHeap(), 0, wmm->lpstrCopyright);
00875     HeapFree(GetProcessHeap(), 0, wmm->lpstrName);
00876     } else {
00877     TRACE("Shouldn't happen... nUseCount=%d\n", wmm->nUseCount);
00878     return MCIERR_INTERNAL;
00879     }
00880 
00881     if ((dwFlags & MCI_NOTIFY) && lpParms) {
00882     MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
00883     }
00884     return 0;
00885 }
00886 
00887 /**************************************************************************
00888  *              MIDI_mciFindNextEvent       [internal]
00889  */
00890 static MCI_MIDITRACK*   MIDI_mciFindNextEvent(WINE_MCIMIDI* wmm, LPDWORD hiPulse)
00891 {
00892     WORD        cnt, nt;
00893     MCI_MIDITRACK*  mmt;
00894 
00895     *hiPulse = 0xFFFFFFFFul;
00896     cnt = 0xFFFFu;
00897     for (nt = 0; nt < wmm->nTracks; nt++) {
00898     mmt = &wmm->tracks[nt];
00899 
00900     if (mmt->wStatus == 0)
00901         continue;
00902     if (mmt->dwEventPulse < *hiPulse) {
00903         *hiPulse = mmt->dwEventPulse;
00904         cnt = nt;
00905     }
00906     }
00907     return (cnt == 0xFFFFu) ? 0 /* no more event on all tracks */
00908     : &wmm->tracks[cnt];
00909 }
00910 
00911 /**************************************************************************
00912  *              MIDI_mciPlay            [internal]
00913  */
00914 static DWORD MIDI_mciPlay(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
00915 {
00916     DWORD       dwStartMS, dwEndMS;
00917     DWORD       dwRet = 0;
00918     WORD        doPlay, nt;
00919     MCI_MIDITRACK*  mmt;
00920     DWORD       hiPulse;
00921     HANDLE      oldcb;
00922 
00923     TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
00924 
00925     if (wmm->hFile == 0) {
00926     WARN("Can't play: no file %s!\n", debugstr_w(wmm->lpstrElementName));
00927     return MCIERR_FILE_NOT_FOUND;
00928     }
00929 
00930     if (wmm->dwStatus != MCI_MODE_STOP) {
00931     if (wmm->dwStatus == MCI_MODE_PAUSE) {
00932         /* FIXME: parameters (start/end) in lpParams may not be used */
00933         return MIDI_mciResume(wmm, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
00934     }
00935     WARN("Can't play: device is not stopped !\n");
00936     return MCIERR_INTERNAL;
00937     }
00938 
00939     if (!(dwFlags & MCI_WAIT)) {
00940     return MCI_SendCommandAsync(wmm->wDevID, MCI_PLAY, dwFlags, (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
00941     }
00942 
00943     if (lpParms && (dwFlags & MCI_FROM)) {
00944     dwStartMS = MIDI_ConvertTimeFormatToMS(wmm, lpParms->dwFrom);
00945     } else {
00946     dwStartMS = wmm->dwPositionMS;
00947     }
00948 
00949     if (lpParms && (dwFlags & MCI_TO)) {
00950     dwEndMS = MIDI_ConvertTimeFormatToMS(wmm, lpParms->dwTo);
00951     } else {
00952     dwEndMS = 0xFFFFFFFFul;
00953     }
00954 
00955     TRACE("Playing from %u to %u\n", dwStartMS, dwEndMS);
00956 
00957     oldcb = InterlockedExchangePointer(&wmm->hCallback,
00958     (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
00959     if (oldcb) mciDriverNotify(oldcb, wmm->wDevID, MCI_NOTIFY_ABORTED);
00960     oldcb = NULL;
00961 
00962     /* init tracks */
00963     for (nt = 0; nt < wmm->nTracks; nt++) {
00964     mmt = &wmm->tracks[nt];
00965 
00966     mmt->wStatus = 1;   /* ok, playing */
00967     mmt->dwIndex = mmt->dwFirst;
00968     if (wmm->wFormat == 2 && nt > 0) {
00969         mmt->dwEventPulse = wmm->tracks[nt - 1].dwLength;
00970     } else {
00971         mmt->dwEventPulse = 0;
00972     }
00973     MIDI_mciReadNextEvent(wmm, mmt); /* FIXME == 0 */
00974     }
00975 
00976     dwRet = midiOutOpen((LPHMIDIOUT)&wmm->hMidi, wmm->wPort, 0L, 0L, CALLBACK_NULL);
00977     if (dwRet != MMSYSERR_NOERROR) {
00978     return dwRet;
00979     }
00980 
00981     wmm->dwPulse = 0;
00982     wmm->dwTempo = 500000;
00983     wmm->dwStatus = MCI_MODE_PLAY;
00984     wmm->dwPositionMS = 0;
00985     wmm->wStartedPlaying = FALSE;
00986 
00987     while (wmm->dwStatus != MCI_MODE_STOP && wmm->dwStatus != MCI_MODE_NOT_READY) {
00988     /* it seems that in case of multi-threading, gcc is optimizing just a little bit
00989      * too much. Tell gcc not to optimize status value using volatile.
00990      */
00991     while (((volatile WINE_MCIMIDI*)wmm)->dwStatus == MCI_MODE_PAUSE);
00992 
00993     doPlay = (wmm->dwPositionMS >= dwStartMS && wmm->dwPositionMS <= dwEndMS);
00994 
00995     TRACE("wmm->dwStatus=%d, doPlay=%c\n", wmm->dwStatus, doPlay ? 'T' : 'F');
00996 
00997     if ((mmt = MIDI_mciFindNextEvent(wmm, &hiPulse)) == NULL)
00998         break;  /* no more event on tracks */
00999 
01000     /* if starting playing, then set StartTicks to the value it would have had
01001      * if play had started at position 0
01002      */
01003     if (doPlay && !wmm->wStartedPlaying) {
01004         wmm->dwStartTicks = GetTickCount() - MIDI_ConvertPulseToMS(wmm, wmm->dwPulse);
01005         wmm->wStartedPlaying = TRUE;
01006         TRACE("Setting dwStartTicks to %u\n", wmm->dwStartTicks);
01007     }
01008 
01009     if (hiPulse > wmm->dwPulse) {
01010         wmm->dwPositionMS += MIDI_ConvertPulseToMS(wmm, hiPulse - wmm->dwPulse);
01011         if (doPlay) {
01012         DWORD   togo = wmm->dwStartTicks + wmm->dwPositionMS;
01013         DWORD   tc = GetTickCount();
01014 
01015         TRACE("Pulses hi=0x%08x <> cur=0x%08x\n", hiPulse, wmm->dwPulse);
01016         TRACE("Wait until %u => %u ms\n",
01017               tc - wmm->dwStartTicks, togo - wmm->dwStartTicks);
01018         if (tc < togo)
01019             Sleep(togo - tc);
01020         }
01021         wmm->dwPulse = hiPulse;
01022     }
01023 
01024     switch (LOBYTE(LOWORD(mmt->dwEventData))) {
01025     case 0xF0:
01026     case 0xF7:  /* sysex events */
01027         {
01028         FIXME("Not handling SysEx events (yet)\n");
01029         }
01030         break;
01031     case 0xFF:
01032         /* position after meta data header */
01033         mmioSeek(wmm->hFile, mmt->dwIndex + HIWORD(mmt->dwEventData), SEEK_SET);
01034         switch (HIBYTE(LOWORD(mmt->dwEventData))) {
01035         case 0x00: /* 16-bit sequence number */
01036         if (TRACE_ON(mcimidi)) {
01037             WORD    twd;
01038 
01039             MIDI_mciReadWord(wmm, &twd);    /* == 0 */
01040             TRACE("Got sequence number %u\n", twd);
01041         }
01042         break;
01043         case 0x01: /* any text */
01044         case 0x02: /* Copyright Message text */
01045         case 0x03: /* Sequence/Track Name text */
01046         case 0x04: /* Instrument Name text */
01047         case 0x05: /* Lyric text */
01048         case 0x06: /* Marker text */
01049         case 0x07: /* Cue-point text */
01050         if (TRACE_ON(mcimidi)) {
01051             char    buf[1024];
01052             WORD    len = mmt->wEventLength - HIWORD(mmt->dwEventData);
01053             static const char* const    info[8] = {"", "Text", "Copyright", "Seq/Trk name",
01054                                "Instrument", "Lyric", "Marker", "Cue-point"};
01055             WORD    idx = HIBYTE(LOWORD(mmt->dwEventData));
01056 
01057             if (len >= sizeof(buf)) {
01058             WARN("Buffer for text is too small (%u are needed)\n", len);
01059             len = sizeof(buf) - 1;
01060             }
01061                     if (mmioRead(wmm->hFile, buf, len) == len) {
01062             buf[len] = 0;   /* end string in case */
01063             TRACE("%s => \"%s\"\n", (idx < 8 ) ? info[idx] : "", buf);
01064             } else {
01065             WARN("Couldn't read data for %s\n", (idx < 8) ? info[idx] : "");
01066             }
01067         }
01068         break;
01069         case 0x20:
01070         /* MIDI channel (cc) */
01071         if (FIXME_ON(mcimidi)) {
01072             BYTE    bt;
01073 
01074             MIDI_mciReadByte(wmm, &bt); /* == 0 */
01075             FIXME("NIY: MIDI channel=%u, track=%u\n", bt, mmt->wTrackNr);
01076         }
01077         break;
01078         case 0x21:
01079         /* MIDI port (pp) */
01080         if (FIXME_ON(mcimidi)) {
01081             BYTE    bt;
01082 
01083             MIDI_mciReadByte(wmm, &bt); /* == 0 */
01084             FIXME("NIY: MIDI port=%u, track=%u\n", bt, mmt->wTrackNr);
01085         }
01086         break;
01087         case 0x2F: /* end of track */
01088         mmt->wStatus = 0;
01089         break;
01090         case 0x51:/* set tempo */
01091         /* Tempo is expressed in -seconds per midi quarter note
01092          * for format 1 MIDI files, this can only be present on track #0
01093          */
01094         if (mmt->wTrackNr != 0 && wmm->wFormat == 1) {
01095             WARN("For format #1 MIDI files, tempo can only be changed on track #0 (%u)\n", mmt->wTrackNr);
01096         } else {
01097             BYTE    tbt;
01098             DWORD   value = 0;
01099 
01100             MIDI_mciReadByte(wmm, &tbt);    value  = ((DWORD)tbt) << 16;
01101             MIDI_mciReadByte(wmm, &tbt);    value |= ((DWORD)tbt) << 8;
01102             MIDI_mciReadByte(wmm, &tbt);    value |= ((DWORD)tbt) << 0;
01103             TRACE("Setting tempo to %d (BPM=%d)\n", wmm->dwTempo, (value) ? (60000000 / value) : 0);
01104             wmm->dwTempo = value;
01105         }
01106         break;
01107         case 0x54: /* (hour) (min) (second) (frame) (fractional-frame) - SMPTE track start */
01108         if (mmt->wTrackNr != 0 && wmm->wFormat == 1) {
01109             WARN("For format #1 MIDI files, SMPTE track start can only be expressed on track #0 (%u)\n", mmt->wTrackNr);
01110         } if (mmt->dwEventPulse != 0) {
01111             WARN("SMPTE track start can only be expressed at start of track (%u)\n", mmt->dwEventPulse);
01112         } else {
01113             BYTE    h, m, s, f, ff;
01114 
01115             MIDI_mciReadByte(wmm, &h);
01116             MIDI_mciReadByte(wmm, &m);
01117             MIDI_mciReadByte(wmm, &s);
01118             MIDI_mciReadByte(wmm, &f);
01119             MIDI_mciReadByte(wmm, &ff);
01120             FIXME("NIY: SMPTE track start %u:%u:%u %u.%u\n", h, m, s, f, ff);
01121         }
01122         break;
01123         case 0x58: /* file rhythm */
01124         if (TRACE_ON(mcimidi)) {
01125             BYTE    num, den, cpmc, _32npqn;
01126 
01127             MIDI_mciReadByte(wmm, &num);
01128             MIDI_mciReadByte(wmm, &den);        /* to notate e.g. 6/8 */
01129             MIDI_mciReadByte(wmm, &cpmc);       /* number of MIDI clocks per metronome click */
01130             MIDI_mciReadByte(wmm, &_32npqn);        /* number of notated 32nd notes per MIDI quarter note */
01131 
01132             TRACE("%u/%u, clock per metronome click=%u, 32nd notes by 1/4 note=%u\n", num, 1 << den, cpmc, _32npqn);
01133         }
01134         break;
01135         case 0x59: /* key signature */
01136         if (TRACE_ON(mcimidi)) {
01137             BYTE    sf, mm;
01138 
01139             MIDI_mciReadByte(wmm, &sf);
01140             MIDI_mciReadByte(wmm, &mm);
01141 
01142             if (sf >= 0x80)     TRACE("%d flats\n", -(char)sf);
01143             else if (sf > 0)    TRACE("%d sharps\n", (char)sf);
01144             else        TRACE("Key of C\n");
01145             TRACE("Mode: %s\n", (mm == 0) ? "major" : "minor");
01146         }
01147         break;
01148         default:
01149         WARN("Unknown MIDI meta event %02x. Skipping...\n", HIBYTE(LOWORD(mmt->dwEventData)));
01150         break;
01151         }
01152         break;
01153     default:
01154         if (doPlay) {
01155         dwRet = midiOutShortMsg((HMIDIOUT)wmm->hMidi, mmt->dwEventData);
01156         } else {
01157         switch (LOBYTE(LOWORD(mmt->dwEventData)) & 0xF0) {
01158         case MIDI_NOTEON:
01159         case MIDI_NOTEOFF:
01160             dwRet = 0;
01161             break;
01162         default:
01163             dwRet = midiOutShortMsg((HMIDIOUT)wmm->hMidi, mmt->dwEventData);
01164         }
01165         }
01166     }
01167     mmt->dwIndex += mmt->wEventLength;
01168     if (mmt->dwIndex < mmt->dwFirst || mmt->dwIndex >= mmt->dwLast) {
01169         mmt->wStatus = 0;
01170     }
01171     if (mmt->wStatus) {
01172         MIDI_mciReadNextEvent(wmm, mmt);
01173     }
01174     }
01175 
01176     midiOutReset((HMIDIOUT)wmm->hMidi);
01177 
01178     dwRet = midiOutClose((HMIDIOUT)wmm->hMidi);
01179 
01180     if (dwFlags & MCI_NOTIFY)
01181     oldcb = InterlockedExchangePointer(&wmm->hCallback, NULL);
01182 
01183     wmm->dwStatus = MCI_MODE_STOP;
01184 
01185     /* Let the potentially asynchronous commands support FAILURE notification. */
01186     if (oldcb) mciDriverNotify(oldcb, wmm->wDevID,
01187     dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
01188     return dwRet;
01189 }
01190 
01191 /**************************************************************************
01192  *              MIDI_mciPause           [internal]
01193  */
01194 static DWORD MIDI_mciPause(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
01195 {
01196     TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
01197 
01198     if (wmm->dwStatus == MCI_MODE_PLAY) {
01199     /* stop all notes */
01200     unsigned chn;
01201     for (chn = 0; chn < 16; chn++)
01202         midiOutShortMsg((HMIDIOUT)(wmm->hMidi), 0x78B0 | chn);
01203     wmm->dwStatus = MCI_MODE_PAUSE;
01204     }
01205     if ((dwFlags & MCI_NOTIFY) && lpParms)
01206     MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
01207     return 0;
01208 }
01209 
01210 /**************************************************************************
01211  *              MIDI_mciResume          [internal]
01212  */
01213 static DWORD MIDI_mciResume(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
01214 {
01215     TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
01216 
01217     if (wmm->dwStatus == MCI_MODE_PAUSE) {
01218     wmm->wStartedPlaying = FALSE;
01219     wmm->dwStatus = MCI_MODE_PLAY;
01220     }
01221     if ((dwFlags & MCI_NOTIFY) && lpParms)
01222     MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
01223     return 0;
01224 }
01225 
01226 /**************************************************************************
01227  *              MIDI_mciSet         [internal]
01228  */
01229 static DWORD MIDI_mciSet(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_SEQ_SET_PARMS lpParms)
01230 {
01231     TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
01232 
01233     if (lpParms == NULL)    return MCIERR_NULL_PARAMETER_BLOCK;
01234 
01235     if (dwFlags & MCI_SET_TIME_FORMAT) {
01236     switch (lpParms->dwTimeFormat) {
01237     case MCI_FORMAT_MILLISECONDS:
01238         TRACE("MCI_FORMAT_MILLISECONDS !\n");
01239         wmm->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
01240         break;
01241     case MCI_FORMAT_SMPTE_24:
01242         TRACE("MCI_FORMAT_SMPTE_24 !\n");
01243         wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_24;
01244         break;
01245     case MCI_FORMAT_SMPTE_25:
01246         TRACE("MCI_FORMAT_SMPTE_25 !\n");
01247         wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_25;
01248         break;
01249     case MCI_FORMAT_SMPTE_30:
01250         TRACE("MCI_FORMAT_SMPTE_30 !\n");
01251         wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_30;
01252         break;
01253     default:
01254         WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
01255         return MCIERR_BAD_TIME_FORMAT;
01256     }
01257     }
01258     if (dwFlags & MCI_SET_VIDEO) {
01259     TRACE("No support for video !\n");
01260     return MCIERR_UNSUPPORTED_FUNCTION;
01261     }
01262     if (dwFlags & MCI_SET_DOOR_OPEN) {
01263     TRACE("No support for door open !\n");
01264     return MCIERR_UNSUPPORTED_FUNCTION;
01265     }
01266     if (dwFlags & MCI_SET_DOOR_CLOSED) {
01267     TRACE("No support for door close !\n");
01268     return MCIERR_UNSUPPORTED_FUNCTION;
01269     }
01270     if (dwFlags & MCI_SET_AUDIO) {
01271     if (dwFlags & MCI_SET_ON) {
01272         TRACE("MCI_SET_ON audio !\n");
01273     } else if (dwFlags & MCI_SET_OFF) {
01274         TRACE("MCI_SET_OFF audio !\n");
01275     } else {
01276         WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
01277         return MCIERR_BAD_INTEGER;
01278     }
01279 
01280     switch (lpParms->dwAudio)
01281         {
01282         case MCI_SET_AUDIO_ALL:         TRACE("MCI_SET_AUDIO_ALL !\n"); break;
01283         case MCI_SET_AUDIO_LEFT:        TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
01284         case MCI_SET_AUDIO_RIGHT:       TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
01285         default:                        WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
01286         }
01287     }
01288 
01289     if (dwFlags & MCI_SEQ_SET_MASTER)
01290     TRACE("MCI_SEQ_SET_MASTER !\n");
01291     if (dwFlags & MCI_SEQ_SET_SLAVE)
01292     TRACE("MCI_SEQ_SET_SLAVE !\n");
01293     if (dwFlags & MCI_SEQ_SET_OFFSET)
01294     TRACE("MCI_SEQ_SET_OFFSET !\n");
01295     if (dwFlags & MCI_SEQ_SET_PORT) {
01296     TRACE("MCI_SEQ_SET_PORT = %d\n", lpParms->dwPort);
01297     if ((UINT16)lpParms->dwPort != (UINT16)MIDI_MAPPER &&
01298         (UINT16)lpParms->dwPort >= midiOutGetNumDevs())
01299         /* FIXME: input/output port distinction? */
01300         return MCIERR_SEQ_PORT_NONEXISTENT;
01301     /* FIXME: Native manages to swap the device while playing! */
01302     wmm->wPort = lpParms->dwPort;
01303     }
01304     if (dwFlags & MCI_SEQ_SET_TEMPO)
01305     TRACE("MCI_SEQ_SET_TEMPO !\n");
01306     if (dwFlags & MCI_NOTIFY)
01307     MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
01308     return 0;
01309 }
01310 
01311 /**************************************************************************
01312  *              MIDI_mciStatus          [internal]
01313  */
01314 static DWORD MIDI_mciStatus(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
01315 {
01316     DWORD       ret = 0;
01317 
01318     TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
01319 
01320     if (lpParms == NULL)    return MCIERR_NULL_PARAMETER_BLOCK;
01321 
01322     if (dwFlags & MCI_STATUS_ITEM) {
01323     switch (lpParms->dwItem) {
01324     case MCI_STATUS_CURRENT_TRACK:
01325         /* FIXME in Format 2 */
01326         lpParms->dwReturn = 1;
01327         TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
01328         break;
01329     case MCI_STATUS_LENGTH:
01330         if ((dwFlags & MCI_TRACK) && wmm->wFormat == 2) {
01331         if (lpParms->dwTrack >= wmm->nTracks)
01332             return MCIERR_BAD_INTEGER;
01333         /* FIXME: this is wrong if there is a tempo change inside the file */
01334         lpParms->dwReturn = MIDI_ConvertPulseToMS(wmm, wmm->tracks[lpParms->dwTrack].dwLength);
01335         } else {
01336         lpParms->dwReturn = MIDI_GetMThdLengthMS(wmm);
01337         }
01338         lpParms->dwReturn = MIDI_ConvertMSToTimeFormat(wmm, lpParms->dwReturn);
01339         TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
01340         break;
01341     case MCI_STATUS_MODE:
01342         TRACE("MCI_STATUS_MODE => %u\n", wmm->dwStatus);
01343         lpParms->dwReturn = MAKEMCIRESOURCE(wmm->dwStatus, wmm->dwStatus);
01344         ret = MCI_RESOURCE_RETURNED;
01345         break;
01346     case MCI_STATUS_MEDIA_PRESENT:
01347         TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE\n");
01348         lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
01349         ret = MCI_RESOURCE_RETURNED;
01350         break;
01351     case MCI_STATUS_NUMBER_OF_TRACKS:
01352         lpParms->dwReturn = (wmm->wFormat == 2) ? wmm->nTracks : 1;
01353         TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
01354         break;
01355     case MCI_STATUS_POSITION:
01356         /* FIXME: do I need to use MCI_TRACK ? */
01357         lpParms->dwReturn = MIDI_ConvertMSToTimeFormat(wmm,
01358                                (dwFlags & MCI_STATUS_START) ? 0 : wmm->dwPositionMS);
01359         TRACE("MCI_STATUS_POSITION %s => %lu\n",
01360           (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
01361         break;
01362     case MCI_STATUS_READY:
01363         lpParms->dwReturn = (wmm->dwStatus == MCI_MODE_NOT_READY) ?
01364         MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
01365         ret = MCI_RESOURCE_RETURNED;
01366         TRACE("MCI_STATUS_READY = %u\n", LOWORD(lpParms->dwReturn));
01367         break;
01368     case MCI_STATUS_TIME_FORMAT:
01369         lpParms->dwReturn = MAKEMCIRESOURCE(wmm->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmm->dwMciTimeFormat);
01370         TRACE("MCI_STATUS_TIME_FORMAT => %u\n", LOWORD(lpParms->dwReturn));
01371         ret = MCI_RESOURCE_RETURNED;
01372         break;
01373     case MCI_SEQ_STATUS_DIVTYPE:
01374         TRACE("MCI_SEQ_STATUS_DIVTYPE !\n");
01375         if (wmm->nDivision > 0x8000) {
01376         switch (wmm->nDivision) {
01377         case 0xE8:  lpParms->dwReturn = MCI_SEQ_DIV_SMPTE_24;   break;  /* -24 */
01378         case 0xE7:  lpParms->dwReturn = MCI_SEQ_DIV_SMPTE_25;   break;  /* -25 */
01379         case 0xE3:  lpParms->dwReturn = MCI_SEQ_DIV_SMPTE_30DROP;   break;  /* -29 */ /* is the MCI constant correct ? */
01380         case 0xE2:  lpParms->dwReturn = MCI_SEQ_DIV_SMPTE_30;   break;  /* -30 */
01381         default:    FIXME("There is a bad bad programmer\n");
01382         }
01383         } else {
01384         lpParms->dwReturn = MCI_SEQ_DIV_PPQN;
01385         }
01386         lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn,lpParms->dwReturn);
01387         ret = MCI_RESOURCE_RETURNED;
01388         break;
01389     case MCI_SEQ_STATUS_MASTER:
01390         TRACE("MCI_SEQ_STATUS_MASTER !\n");
01391         lpParms->dwReturn = 0;
01392         break;
01393     case MCI_SEQ_STATUS_SLAVE:
01394         TRACE("MCI_SEQ_STATUS_SLAVE !\n");
01395         lpParms->dwReturn = 0;
01396         break;
01397     case MCI_SEQ_STATUS_OFFSET:
01398         TRACE("MCI_SEQ_STATUS_OFFSET !\n");
01399         lpParms->dwReturn = 0;
01400         break;
01401     case MCI_SEQ_STATUS_PORT:
01402         if (wmm->wPort != (UINT16)MIDI_MAPPER)
01403         lpParms->dwReturn = wmm->wPort;
01404         else {
01405         lpParms->dwReturn = MAKEMCIRESOURCE(MIDI_MAPPER, MCI_SEQ_MAPPER_S);
01406         ret = MCI_RESOURCE_RETURNED;
01407         }
01408         TRACE("MCI_SEQ_STATUS_PORT (%u) => %d\n", wmm->wDevID, wmm->wPort);
01409         break;
01410     case MCI_SEQ_STATUS_TEMPO:
01411         TRACE("MCI_SEQ_STATUS_TEMPO !\n");
01412         lpParms->dwReturn = wmm->dwTempo;
01413         break;
01414     default:
01415         FIXME("Unknown command %08X !\n", lpParms->dwItem);
01416         return MCIERR_UNRECOGNIZED_COMMAND;
01417     }
01418     } else {
01419     WARN("No Status-Item!\n");
01420     return MCIERR_UNRECOGNIZED_COMMAND;
01421     }
01422     if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
01423     MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
01424     return ret;
01425 }
01426 
01427 /**************************************************************************
01428  *              MIDI_mciGetDevCaps      [internal]
01429  */
01430 static DWORD MIDI_mciGetDevCaps(WINE_MCIMIDI* wmm, DWORD dwFlags,
01431                 LPMCI_GETDEVCAPS_PARMS lpParms)
01432 {
01433     DWORD       ret;
01434 
01435     TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
01436 
01437     if (lpParms == NULL)    return MCIERR_NULL_PARAMETER_BLOCK;
01438 
01439     if (dwFlags & MCI_GETDEVCAPS_ITEM) {
01440     switch (lpParms->dwItem) {
01441     case MCI_GETDEVCAPS_DEVICE_TYPE:
01442         TRACE("MCI_GETDEVCAPS_DEVICE_TYPE !\n");
01443         lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_SEQUENCER, MCI_DEVTYPE_SEQUENCER);
01444         ret = MCI_RESOURCE_RETURNED;
01445         break;
01446     case MCI_GETDEVCAPS_HAS_AUDIO:
01447         TRACE("MCI_GETDEVCAPS_HAS_AUDIO !\n");
01448         lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
01449         ret = MCI_RESOURCE_RETURNED;
01450         break;
01451     case MCI_GETDEVCAPS_HAS_VIDEO:
01452         TRACE("MCI_GETDEVCAPS_HAS_VIDEO !\n");
01453         lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
01454         ret = MCI_RESOURCE_RETURNED;
01455         break;
01456     case MCI_GETDEVCAPS_USES_FILES:
01457         TRACE("MCI_GETDEVCAPS_USES_FILES !\n");
01458         lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
01459         ret = MCI_RESOURCE_RETURNED;
01460         break;
01461     case MCI_GETDEVCAPS_COMPOUND_DEVICE:
01462         TRACE("MCI_GETDEVCAPS_COMPOUND_DEVICE !\n");
01463         lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
01464         ret = MCI_RESOURCE_RETURNED;
01465         break;
01466     case MCI_GETDEVCAPS_CAN_EJECT:
01467         TRACE("MCI_GETDEVCAPS_CAN_EJECT !\n");
01468         lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
01469         ret = MCI_RESOURCE_RETURNED;
01470         break;
01471     case MCI_GETDEVCAPS_CAN_PLAY:
01472         TRACE("MCI_GETDEVCAPS_CAN_PLAY !\n");
01473         lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
01474         ret = MCI_RESOURCE_RETURNED;
01475         break;
01476     case MCI_GETDEVCAPS_CAN_RECORD:
01477         TRACE("MCI_GETDEVCAPS_CAN_RECORD !\n");
01478         lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
01479         ret = MCI_RESOURCE_RETURNED;
01480         break;
01481     case MCI_GETDEVCAPS_CAN_SAVE:
01482         TRACE("MCI_GETDEVCAPS_CAN_SAVE !\n");
01483         lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
01484         ret = MCI_RESOURCE_RETURNED;
01485         break;
01486     default:
01487         FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
01488         return MCIERR_UNRECOGNIZED_COMMAND;
01489     }
01490     } else {
01491     WARN("No GetDevCaps-Item !\n");
01492     return MCIERR_UNRECOGNIZED_COMMAND;
01493     }
01494     if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
01495     MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
01496     return ret;
01497 }
01498 
01499 /**************************************************************************
01500  *              MIDI_mciInfo            [internal]
01501  */
01502 static DWORD MIDI_mciInfo(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
01503 {
01504     LPCWSTR     str = 0;
01505     DWORD       ret = 0;
01506     static const WCHAR wszMidiSeq[] = {'W','i','n','e','\'','s',' ','M','I','D','I',' ','s','e','q','u','e','n','c','e','r',0};
01507 
01508     TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
01509 
01510     if (lpParms == NULL || lpParms->lpstrReturn == NULL)
01511     return MCIERR_NULL_PARAMETER_BLOCK;
01512 
01513     TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
01514 
01515     switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
01516     case MCI_INFO_PRODUCT:      str = wszMidiSeq; break;
01517     case MCI_INFO_FILE:         str = wmm->lpstrElementName; break;
01518     case MCI_INFO_COPYRIGHT:    str = wmm->lpstrCopyright; break;
01519     case MCI_INFO_NAME:         str = wmm->lpstrName; break;
01520     default:
01521     WARN("Don't know this info command (%u)\n", dwFlags);
01522     return MCIERR_UNRECOGNIZED_COMMAND;
01523     }
01524     if (!ret) {
01525     if (lpParms->dwRetSize) {
01526         WCHAR zero = 0;
01527         /* FIXME? Since NT, mciwave, mciseq and mcicda set dwRetSize
01528          *        to the number of characters written, excluding \0. */
01529         lstrcpynW(lpParms->lpstrReturn, str ? str : &zero, lpParms->dwRetSize);
01530     } else ret = MCIERR_PARAM_OVERFLOW;
01531     }
01532     if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
01533     MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
01534     return ret;
01535 }
01536 
01537 /**************************************************************************
01538  *              MIDI_mciSeek            [internal]
01539  */
01540 static DWORD MIDI_mciSeek(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
01541 {
01542     DWORD       ret = 0;
01543 
01544     TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
01545 
01546     if (lpParms == NULL) {
01547     ret = MCIERR_NULL_PARAMETER_BLOCK;
01548     } else {
01549     MIDI_mciStop(wmm, MCI_WAIT, 0);
01550 
01551     if (dwFlags & MCI_SEEK_TO_START) {
01552         wmm->dwPositionMS = 0;
01553     } else if (dwFlags & MCI_SEEK_TO_END) {
01554         wmm->dwPositionMS = 0xFFFFFFFF; /* FIXME */
01555     } else if (dwFlags & MCI_TO) {
01556         wmm->dwPositionMS = MIDI_ConvertTimeFormatToMS(wmm, lpParms->dwTo);
01557     } else {
01558         WARN("dwFlag doesn't tell where to seek to...\n");
01559         return MCIERR_MISSING_PARAMETER;
01560     }
01561 
01562     TRACE("Seeking to position=%u ms\n", wmm->dwPositionMS);
01563 
01564     if (dwFlags & MCI_NOTIFY)
01565         MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
01566     }
01567     return ret;
01568 }
01569 
01570 /*======================================================================*
01571  *                          MIDI entry points               *
01572  *======================================================================*/
01573 
01574 /**************************************************************************
01575  *              DriverProc (MCISEQ.@)
01576  */
01577 LRESULT CALLBACK MCIMIDI_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
01578                                     LPARAM dwParam1, LPARAM dwParam2)
01579 {
01580     WINE_MCIMIDI*   wmm;
01581     switch (wMsg) {
01582     case DRV_LOAD:      return 1;
01583     case DRV_FREE:      return 1;
01584     case DRV_ENABLE:        return 1;
01585     case DRV_DISABLE:       return 1;
01586     case DRV_QUERYCONFIGURE:    return 1;
01587     case DRV_CONFIGURE:     MessageBoxA(0, "Sample Midi Driver !", "OSS Driver", MB_OK); return 1;
01588     case DRV_INSTALL:       return DRVCNF_RESTART;
01589     case DRV_REMOVE:        return DRVCNF_RESTART;
01590     case DRV_OPEN:      return MIDI_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
01591     case DRV_CLOSE:     return MIDI_drvClose(dwDevID);
01592     }
01593     if ((wMsg < DRV_MCI_FIRST) || (wMsg > DRV_MCI_LAST)) {
01594     TRACE("Sending msg %04x to default driver proc\n", wMsg);
01595     return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
01596     }
01597 
01598     wmm = MIDI_mciGetOpenDev(dwDevID, wMsg);
01599     if (wmm == NULL)        return MCIERR_INVALID_DEVICE_ID;
01600 
01601     switch (wMsg) {
01602     case MCI_OPEN_DRIVER:   return MIDI_mciOpen      (wmm, dwParam1, (LPMCI_OPEN_PARMSW)     dwParam2);
01603     case MCI_CLOSE_DRIVER:  return MIDI_mciClose     (wmm, dwParam1, (LPMCI_GENERIC_PARMS)   dwParam2);
01604     case MCI_PLAY:      return MIDI_mciPlay      (wmm, dwParam1, (LPMCI_PLAY_PARMS)      dwParam2);
01605     case MCI_STOP:      return MIDI_mciStop      (wmm, dwParam1, (LPMCI_GENERIC_PARMS)   dwParam2);
01606     case MCI_SET:       return MIDI_mciSet       (wmm, dwParam1, (LPMCI_SEQ_SET_PARMS)   dwParam2);
01607     case MCI_PAUSE:     return MIDI_mciPause     (wmm, dwParam1, (LPMCI_GENERIC_PARMS)   dwParam2);
01608     case MCI_RESUME:        return MIDI_mciResume    (wmm, dwParam1, (LPMCI_GENERIC_PARMS)   dwParam2);
01609     case MCI_STATUS:        return MIDI_mciStatus    (wmm, dwParam1, (LPMCI_STATUS_PARMS)    dwParam2);
01610     case MCI_GETDEVCAPS:    return MIDI_mciGetDevCaps(wmm, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
01611     case MCI_INFO:      return MIDI_mciInfo      (wmm, dwParam1, (LPMCI_INFO_PARMSW)     dwParam2);
01612     case MCI_SEEK:      return MIDI_mciSeek      (wmm, dwParam1, (LPMCI_SEEK_PARMS)      dwParam2);
01613     case MCI_OPEN:
01614     case MCI_CLOSE:
01615     FIXME("Shouldn't receive a MCI_OPEN or CLOSE message\n");
01616     /* fall through */
01617     default:
01618     TRACE("Unsupported command [0x%x]\n", wMsg);
01619         return MCIERR_UNSUPPORTED_FUNCTION; /* Win9x: MCIERR_UNRECOGNIZED_COMMAND */
01620     }
01621 }

Generated on Sun May 27 2012 04:24:40 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.