Source file: /~heha/argon/multimed.zip/MCIQA/MCICMDS.C

/****************************************************************************
 *
 *   mcicmds.c
 *
 *   Copyright (c) 1991-1992 Microsoft Corporation.  All Rights Reserved.
 *
 *
 *   Sample MCI Device Driver
 *
 *      MCI Command Message Procedures
 *
 ***************************************************************************/

#include <windows.h>
#include <mmsystem.h>
#include <mmddk.h>
#include "mciqa.h"
#include "qasample.h"

#ifdef DEBUG
int nDebugOutput;               /* non-zero for debug output enabled */

/****************************************************************************
 * Unformatted output to the debug port prefaced by the driver name and
 * followed by a line feed.
 ***************************************************************************/
void NEAR dout(PSTR szOutput)
{
    OutputDebugString(MCINAME);
    OutputDebugString(szOutput);
    OutputDebugString("\r\n");
}

/****************************************************************************
 * Formatted output to the debug port prefaced by the driver name and
 * followed by a line feed.
 *
 * This routine must have '#ifdef DEBUG' around it when called
 * to avoid allocating string space in the RETAIL version
 ***************************************************************************/
void NEAR dprintf(PSTR szFormat, ...)
{
char buf[128];
    
    if (nDebugOutput == 0)
        return;

    wvsprintf(buf, szFormat, (LPSTR)(&szFormat + 1));
    OutputDebugString(MCINAME);
    OutputDebugString(buf);
    OutputDebugString("\r\n");
}

#endif  /* #ifdef DEBUG */

/* Never a valid position for this device */
#define INVALID_POSITION    0xFFFFFFFF

/* Never a valid Windows timer ID */
#define INVALID_TIMER_ID    0

/* Returns TRUE if the given channel has a running timer */
#define ACTIVE_TIMER(nChannel)  (DriverData[nChannel].wTimerID != \
                                 INVALID_TIMER_ID)

/* Procedure instance address of timer procedure */
static FARPROC lpfnTimerProc;

/* Information kept for each channel */
typedef struct {
    int     nUseCount;          /* Incremented for each shared open */
    BOOL    fShareable;         /* TRUE if first open was shareable */
    DWORD   dwMode;             /* Not used */
    WORD    wNotifyDeviceID;    /* MCI device ID with a pending notification */
    WORD    wTimerID;           /* ID of any active timer or INVALID_TIMER_ID */
    DWORD   dwPosition;         /* Not used */
    DWORD   dwTimeFormat;       /* Not used */
    DWORD   dwToPosition;       /* 'to' position of last play command or */
                                /* INVALID_POSITION */
    HANDLE  hCallback;          /* Callback handle for pending notification */
} DriverDataRecord;

/*  This driver allows a fixed number of channels to be active.  The
 *  channel number is specified as a parameter on the device line in
 *  the [mci] section of SYSTEM.INI.
 */
DriverDataRecord DriverData[MCIQA_MAX_CHANNELS];

/****************************************************************************
 *
 * mqInitializeChannel: Reset all vital information for this channel
 *
 * Params:  nChannel    Channel to use
 *
 ***************************************************************************/
void mqInitializeChannel(int nChannel)
{
    /* Allows the channel to be opened */
    DriverData[nChannel].nUseCount = 0;

    /* Indicates no active timer (notification) */
    DriverData[nChannel].wTimerID = INVALID_TIMER_ID;

    /* Indicates no previous 'to' position */
    DriverData[nChannel].dwToPosition = INVALID_POSITION;
}

/****************************************************************************
 *
 * mqKillTimer: Kill and clear the timer for the indicated channel
 *
 * Params:  nChannel    Channel to use
 *
 ***************************************************************************/
void mqKillTimer(int nChannel)
{
    KillTimer (NULL, DriverData[nChannel].wTimerID);
    DriverData[nChannel].wTimerID = INVALID_TIMER_ID;
}

#ifdef DEBUG
/****************************************************************************
 * Print notification information to debug output
 ***************************************************************************/
void notify_debugout(WORD wDeviceID, WORD wNotifyStatus)
{
char *pszStatus;
    
    switch (wNotifyStatus) {

        case MCI_NOTIFY_SUCCESSFUL:
            pszStatus = "Successful";
            break;

        case MCI_NOTIFY_SUPERSEDED:
            pszStatus = "Superseded";
            break;

        case MCI_NOTIFY_ABORTED:
            pszStatus = "Aborted";
            break;

        case MCI_NOTIFY_FAILURE:
            pszStatus = "Failure";
            break;

    }
    dprintf ("Send Device ID %d Notify %s", wDeviceID, (LPSTR)pszStatus);
}
#endif

/****************************************************************************
 *
 * mqCancelNotify:   Kill and clear any timer for the indicate channel
 * and abort the pending notification
 *
 * Params:  nChannel        Channel to cancel
 *          wNotifyStatus   Notification status to return
 *
 ***************************************************************************/
void mqCancelNotify(int nChannel, WORD wNotifyStatus)
{
    if (ACTIVE_TIMER (nChannel)) {
        mqKillTimer (nChannel);
#ifdef DEBUG
        notify_debugout (DriverData[nChannel].wNotifyDeviceID, wNotifyStatus);
#endif
        mciDriverNotify (DriverData[nChannel].hCallback,
                         DriverData[nChannel].wNotifyDeviceID, wNotifyStatus);
    }
}

/****************************************************************************
 *
 * mqDriverNotify:  Function to post mm_mcinotify message if notification
 * has been requested. Used when command is actually synchronous
 *
 * Params:  wDeviceID       MCI device id.
 *          nChannel        Driver channel
 *          dwFlags         only notifiy flag applies.
 *          lpParms         std. MCI parameter block
 *          wNotifyStatus   notification status
 *
 ***************************************************************************/
void NEAR PASCAL mqDriverNotify(WORD wDeviceID, int nChannel, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms, WORD wNotifyStatus)
{
    /* If the notify flag was specified and if the callback handle is not 0 */
    if ((dwFlags & MCI_NOTIFY) && LOWORD(lpParms->dwCallback)) {
        mqCancelNotify (nChannel, MCI_NOTIFY_SUPERSEDED);
#ifdef DEBUG
        notify_debugout (wDeviceID, wNotifyStatus);
#endif
        mciDriverNotify (LOWORD(lpParms->dwCallback), wDeviceID,
                         wNotifyStatus);
    }
}

/****************************************************************************
 *
 * mqMCIOpenDriver:    Perform driver initialization
 *
 * Params:  nChannel    Device channel
 *          dwFlags     Open flags
 *          lpOpen      Open paramaters
 *
 * Returns:  MCI error code
 *
 ***************************************************************************/
DWORD NEAR PASCAL mqMCIOpenDriver(int nChannel, DWORD dwFlags, LPMCI_OPEN_PARMS lpOpen)
{
#ifdef DEBUG
    /* Set debug output on if the 'mciqa' keyname in the [mmsystem] section */
    /* of SYSTEM.INI is set */
    nDebugOutput = GetPrivateProfileInt ("mmsystem", "mciqa", 0, "system.ini");
#endif

    dout ("MCI_OPEN_DRIVER message");

    if (dwFlags & MCI_OPEN_ELEMENT)
        return MCIERR_NO_ELEMENT_ALLOWED;

    if (DriverData[nChannel].nUseCount > 0) {
    /* The driver already open on this channel */

        /* If the driver was opened shareable before and this open specifies */
        /* shareable then increment the use count */
        if (DriverData[nChannel].fShareable &&
            dwFlags & MCI_OPEN_SHAREABLE)

            ++DriverData[nChannel].nUseCount;

        /* Otherwise the device cannot be opened */
        else
            return MCIERR_MUST_USE_SHAREABLE;

    }
    else {
    /* This channel is not already open */

        DriverData[nChannel].nUseCount = 1;
        DriverData[nChannel].fShareable = dwFlags & MCI_OPEN_SHAREABLE;
        qsOpen (nChannel);
    }

    mciSetDriverData (lpOpen->wDeviceID, nChannel);

    return 0;
}

/****************************************************************************
 *
 * mqMCICloseDriver:   Perform driver termination
 *
 * Params:  nChannel    Channel to close
 *
 ***************************************************************************/
void NEAR PASCAL mqMCICloseDriver(int nChannel, WORD wDeviceID)
{
    dout("MCI_CLOSE_DRIVER message");
    mqCancelNotify (nChannel, MCI_NOTIFY_ABORTED);
    if (--DriverData[nChannel].nUseCount == 0) {
        qsClose (nChannel);

        if (wDeviceID == DriverData[nChannel].wNotifyDeviceID)
            mqKillTimer (nChannel);

        mqInitializeChannel (nChannel);
    }
}

/****************************************************************************
 *
 * mqSetDWTimer:    Sets a timer for the minimum of 0xFFFF and dwDelay
 *                  milliseconds
 *
 * Params:  nChannel    Channel to use
 *          dwDelay     Delay in milliseconds
 *
 * Returns: wTimerID if successful or 0 on failure
 *
 ***************************************************************************/
WORD mqSetDWTimer(int nChannel, DWORD dwDelay)
{
    return SetTimer (NULL, 0, dwDelay > 0xFFFF ? 0xFFFF : (WORD)dwDelay,
                     lpfnTimerProc);
}

/****************************************************************************
 * TimerProc:  Standard Windows timer procedure to handle notification
 * on play commands
 ***************************************************************************/
void FAR PASCAL TimerProc(HWND hWnd, unsigned iMessage, WORD wTimerID, DWORD dwSystemTime)
{
int   nChannel;
WORD  wStatus, wMode;
DWORD dwPosition;

    /* What channel does this timer belong to? */
    for (nChannel = 0; nChannel < MCIQA_MAX_CHANNELS; ++nChannel)

        /* The timer is for this channel */
        if (DriverData[nChannel].wTimerID == wTimerID) {

            /* Stop the timer */
            mqKillTimer (nChannel);

            /* Where are we? */
            dwPosition = qsCurrentPosition (nChannel);

            /* If the device is still playing or is paused */
            wMode = qsCurrentMode (nChannel);
            if (wMode == QS_PLAYING || wMode == QS_PAUSED)

                /* Reset the timer and try again (the delay was */
                /* probably > 0xFFFF) */
                DriverData[nChannel].wTimerID = mqSetDWTimer (nChannel,
                              DriverData[nChannel].dwToPosition - dwPosition);
            else {
            /* The device is not playing */

                /* Device arrived OK so indicate success */
                if (dwPosition == DriverData[nChannel].dwToPosition)
                    wStatus = MCI_NOTIFY_SUCCESSFUL;

                /* Device did not arrive at 'to' position so indicate failure */
                else
                    wStatus = MCI_NOTIFY_FAILURE;

#ifdef DEBUG
               /* Notify the application */
                notify_debugout (DriverData[nChannel].wNotifyDeviceID, wStatus);
#endif
                mciDriverNotify (DriverData[nChannel].hCallback,
                                DriverData[nChannel].wNotifyDeviceID, wStatus);
            }
        }
}

/****************************************************************************
 *
 * mqInitialize:    Do MCI device driver initializeation
 *
 * Return:  TRUE on success
 *
 ***************************************************************************/
BOOL mqInitialize ()
{
int n;

    /* Make callable of timer procedure */
    lpfnTimerProc = MakeProcInstance (TimerProc, hModuleInstance);

    /* Initialize device data table */
    for (n = 0; n < MCIQA_MAX_CHANNELS; ++n)
        mqInitializeChannel (n);

    return TRUE;
}

/****************************************************************************
 *
 * mqNotifyAt:  Set up to notify at the given position
 *
 * Params:  nChannel    Channel number to use
 *          dwTo        Position
 *          hCallback   Callback window handle
 *
 ***************************************************************************/
void mqNotifyAt(int nChannel, WORD wDeviceID, DWORD dwTo, HANDLE hCallback)
{
DWORD dwCurrentPosition;
WORD  wDelay;

    dwCurrentPosition = qsCurrentPosition (nChannel);

    if (dwCurrentPosition > dwTo)
        return;

    DriverData[nChannel].hCallback = hCallback;
    DriverData[nChannel].wNotifyDeviceID = wDeviceID;

    if ((DriverData[nChannel].wTimerID =
            mqSetDWTimer (nChannel, dwTo - dwCurrentPosition))
         == INVALID_TIMER_ID)

        return;
}

/****************************************************************************
 *
 * mqWaitUntil:  Do not return until the given position is reached
 *
 * Params:  nChannel    Channel number to use
 *          dwTo        Position
 *
 * Returns: Notification status to use
 *
 ***************************************************************************/
WORD mqWaitUntil(int nChannel, WORD wDeviceID, DWORD dwTo)
{
/* We would like to have some sort of lower level operating system */
/* delay function here but instead must loop */

    /* Loop while the device is playing */
    while (qsCurrentMode (nChannel) == QS_PLAYING) {

        DWORD nCounter;

        /*  mciDriverYield() calls the normal Windows Yield() procedure and
         *  checks for any application generated cancel condition such as a
         *  break key
         */
        if (mciDriverYield (wDeviceID)) {
            /* The application has generated a cancel condition so cancel */
            /* any notification on this play command */
            mqCancelNotify (nChannel, MCI_NOTIFY_ABORTED);
            return MCI_NOTIFY_ABORTED;
        }
/*      for (nCounter = 80000L; nCounter != 0; --nCounter) ; */
    }

    if (qsCurrentPosition (nChannel) == dwTo)
        return MCI_NOTIFY_SUCCESSFUL;
    else
        return MCI_NOTIFY_FAILURE;
}

/****************************************************************************
 *
 * mqMCIPlay:  Setup to play wave file
 *
 * Params:  nChannel    Channel number to play
 *          dwFlags     play flags
 *          lpPlay      play parameters
 *
 * Returns: MCI error code
 *
 ***************************************************************************/
DWORD mqMCIPlay(int nChannel, WORD wDeviceID, DWORD dwFlags, LPMCI_PLAY_PARMS lpPlay)
{
DWORD dwFrom, dwTo, dwLen;
WORD wNotifyStatus, wErr = MCIERR_NO_ERROR;

    dout("MCI_PLAY message");

    dwLen = qsMediaLength(nChannel);

    if (dwFlags & MCI_FROM) {
        dwFrom = lpPlay->dwFrom;
        if (dwFrom > dwLen)
            return MCIERR_OUTOFRANGE;
    }
    else
        dwFrom = qsCurrentPosition (nChannel);


    if (dwFlags & MCI_TO) {
        dwTo = lpPlay->dwTo;
        if (dwTo < dwFrom || dwTo > dwLen)
            return MCIERR_OUTOFRANGE;
    }
    else
        dwTo = dwLen;

    /* If there was a 'from' position or a new 'to' position then cancel */
    /* any pending notificiation */
    if (dwFlags & MCI_FROM ||
        (DriverData[nChannel].dwToPosition != INVALID_POSITION &&
         DriverData[nChannel].dwToPosition != dwTo))

        mqCancelNotify (nChannel, MCI_NOTIFY_ABORTED);

    DriverData[nChannel].dwToPosition = dwTo;

#ifdef DEBUG
    dprintf("   From:%08lX To:%08lX", dwFrom, dwTo);
#endif

    if (!qsPlay (nChannel, dwFrom, dwTo))
        return MCIERR_HARDWARE;

    if (dwFlags & MCI_WAIT) {
        /* Wait until device stops or application cancels */
        wNotifyStatus = mqWaitUntil (nChannel, wDeviceID, dwTo);

        /* Generate any notification */
        mqDriverNotify (wDeviceID, nChannel, dwFlags,
                        (LPMCI_GENERIC_PARMS)lpPlay, wNotifyStatus);
        if (wNotifyStatus == MCI_NOTIFY_FAILURE)
            wErr = MCIERR_HARDWARE;

    }
    else if (dwFlags & MCI_NOTIFY) {
        /* Set up for delayed notification */
        mqCancelNotify (nChannel, MCI_NOTIFY_SUPERSEDED);
        mqNotifyAt (nChannel, wDeviceID, dwTo, (HANDLE)lpPlay->dwCallback);
    }

    return wErr;
}

/****************************************************************************
 *
 * mqMCIStop:  Stop playing
 *
 * Params:  nChannel    Channel number to stop
 *
 * Returns: MCI error code
 *
 ***************************************************************************/
DWORD NEAR PASCAL mqMCIStop(int nChannel)
{
    dout("MCI_STOP message");
    if (!qsStop (nChannel))
        return MCIERR_HARDWARE;

    mqCancelNotify (nChannel, MCI_NOTIFY_ABORTED);
    return MCIERR_NO_ERROR;
}

/****************************************************************************
 *
 * mqMCIPause:  Pause
 *
 * Params:  nChannel    Channel number to pause
 *
 * Returns: MCI error code
 *
 ***************************************************************************/
DWORD NEAR PASCAL mqMCIPause(int nChannel)
{
    dout("MCI_PAUSE message");
    if (!qsPause (nChannel))
        return MCIERR_HARDWARE;
    else
        return MCIERR_NO_ERROR;
    return(MCIERR_NO_ERROR);
}

/****************************************************************************
 *
 * mqMCICue:  Cue
 *
 * Params:  nChannel    Channel number to cue
 *
 * Returns: MCI error code
 *
 ***************************************************************************/
DWORD NEAR PASCAL mqMCICue(int nChannel)
{
    dout("MCI_CUE message");
    return(MCIERR_NO_ERROR);
}

/****************************************************************************
 *
 * mqMCISeek:  Seek
 *
 * Params:  nChannel    Channel number to cue
 *
 * Returns: MCI error code
 *
 ***************************************************************************/
DWORD NEAR PASCAL mqMCISeek(int nChannel, DWORD dwFlags, LPMCI_SEEK_PARMS lpSeek)
{
    dout("MCI_SEEK message");

    if (dwFlags & MCI_TO) {
        /* seek to lpSeek->dwTo */
        if (lpSeek->dwTo > qsMediaLength (nChannel))
            return MCIERR_OUTOFRANGE;

        if (!qsSeek (nChannel, lpSeek->dwTo)) {
#ifdef DEBUG            
            dprintf(" To:%08lX", lpSeek->dwTo);
#endif

            return MCIERR_HARDWARE;
        }
    }
    else if (dwFlags & MCI_SEEK_TO_START) {
        if (!qsSeek (nChannel, 0)) {
#ifdef DEBUG
            dprintf(" To:%08lX", 0);
#endif

            return MCIERR_HARDWARE;
        }
    }
    else if (dwFlags & MCI_SEEK_TO_END) {
        if (!qsSeek (nChannel, qsMediaLength (nChannel))) {
#ifdef DEBUG
            dprintf(" To:%08lX", qsMediaLength(nChannel));
#endif

            return MCIERR_HARDWARE;
        }
    } else
            return MCIERR_MISSING_PARAMETER;

    mqCancelNotify (nChannel, MCI_NOTIFY_ABORTED);

    return MCIERR_NO_ERROR;
}

/****************************************************************************
 *
 * mqMCIStatus: Respond to a status command
 *
 * Params:  nChannel    Channel number for status
 *          dwFlags     flags
 *          lpStatus    status parameters
 *
 * Returns: MCI error code
 *
 ***************************************************************************/
DWORD NEAR PASCAL mqMCIStatus (int nChannel, DWORD dwFlags, LPMCI_STATUS_PARMS lpStatus)
{
DWORD dwRes;
LPQS_INFO lpInfo;

    dout("MCI_STATUS message");

    if ((lpInfo = GAllocPtr (sizeof(QS_INFO))) == NULL)
        return MCIERR_OUT_OF_MEMORY;

    if (!qsInfo (nChannel, lpInfo)) {
        dwRes = MCIERR_DRIVER_INTERNAL;
        goto cleanup;
    }

    if (dwFlags & MCI_STATUS_ITEM)

        switch (lpStatus->dwItem) {

            case MCI_STATUS_POSITION:

                if (dwFlags & MCI_TRACK) {
                    if (lpStatus->dwTrack == 1) {
                        lpStatus->dwReturn = 0;
                        dwRes = MCIERR_NO_ERROR;
                    }
                    else
                        dwRes = MCIERR_OUTOFRANGE;
                }
                else if (dwFlags & MCI_STATUS_START) {
                    lpStatus->dwReturn = 0;
                    dwRes = MCIERR_NO_ERROR;
                }
                else {
                    dwRes = MCIERR_NO_ERROR; 
                    lpStatus->dwReturn =  lpInfo->dwPosition;
                }
                break;

            case MCI_STATUS_LENGTH:
                if ((dwFlags & MCI_TRACK) && (lpStatus->dwTrack != 1))
                    return MCIERR_OUTOFRANGE;

                lpStatus->dwReturn = lpInfo->dwLength;
                dwRes = MCIERR_NO_ERROR;
                break;

            case MCI_STATUS_CURRENT_TRACK:
                lpStatus->dwReturn = 1;
                dwRes = MCIERR_NO_ERROR;
                break;

            case MCI_STATUS_NUMBER_OF_TRACKS:
                lpStatus->dwReturn = 1;
                dwRes = MCIERR_NO_ERROR;
                break;

            case MCI_STATUS_READY:

/*  The MAKEMCIRESOURCE macro is used to return both a WORD value to
 *  be used by the application programing making this call and a
 *  string resource ID value to be used by the MCI core to look up
 *  a string value to be returned to the called of mciSendString.
 *  This second value has no meaning and is not available to callers
 *  of mciSendCommand
 */
                lpStatus->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);

/*  Indicates that the high word of the return value contains a string
 *  resource ID.  The resource is a standard string from MMSYSTEM.DLL.
 *  To indicate that the resource ID refers to a custom string contained in
 *  the driver module (MCIQA.DLL) bitwise or the MCI_RESOURCE_DRIVER
 *  flag in with MCI_RESOURCE_RETURNED.
 */

                dwRes = MCI_RESOURCE_RETURNED;
                break;

            case MCI_STATUS_MODE:

            { int n;

                switch (lpInfo->nMode) {

                    case QS_STOPPED:
                        n = MCI_MODE_STOP;
                        break;
                    case QS_PLAYING:
                        n = MCI_MODE_PLAY;
                        break;
                    case QS_PAUSED:
                        n = MCI_MODE_PAUSE;
                        break;
                    default:
                        n = MCI_MODE_NOT_READY;
                        break;
                }
                lpStatus->dwReturn = MAKEMCIRESOURCE (n, n);
                dwRes = MCI_RESOURCE_RETURNED;
                break;
            }

            case MCI_STATUS_TIME_FORMAT:
                lpStatus->dwReturn =
                    MAKEMCIRESOURCE(MCI_FORMAT_MILLISECONDS,
                                    MCI_FORMAT_MILLISECONDS_S);
                dwRes = MCI_RESOURCE_RETURNED;
                break;
                
            default:
                dwRes = MCIERR_MISSING_PARAMETER;
        }
    else
        dwRes = MCIERR_MISSING_PARAMETER;

cleanup:;
    GFreePtr (lpInfo);
    return (dwRes);
}

/****************************************************************************
 *
 * mqMCIInfo:  Respond to MCI_INFO message
 *
 * Parms:   dwFlags     flags
 *          lpInfo      info parameters
 *
 * Return:  MCI error code
 *
 ***************************************************************************/
DWORD NEAR PASCAL mqMCIInfo(DWORD dwFlags, LPMCI_INFO_PARMS lpInfo)
{
DWORD dwRes = MCIERR_NO_ERROR;

    dout("MCI_INFO message");

    if (dwFlags & MCI_INFO_PRODUCT && lpInfo->lpstrReturn != NULL)
        lstrcpy (lpInfo->lpstrReturn, MCIQA_PRODUCT);
    else
        dwRes = MCIERR_MISSING_PARAMETER;

    return dwRes;
}

/****************************************************************************
 *
 * mqMCISet:   Respond to the MCI_SET message
 *
 * Parms:   nChannel    Channel number
 *          dwFlags     flags
 *          lpSet       set parameters
 *
 * Return:  MCI error code
 *
 ***************************************************************************/
DWORD NEAR PASCAL mqMCISet(WORD nChannel, DWORD dwFlags, LPMCI_WAVE_SET_PARMS lpSet)
{
DWORD dwRes;
    
    dout("MCI_SET message");

    if (dwFlags & MCI_SET_TIME_FORMAT) {
        DriverData[nChannel].dwTimeFormat = lpSet->dwTimeFormat;
        dwRes = MCIERR_NO_ERROR;
    }
    else if (dwFlags & MCI_SET_DOOR_OPEN ||
               dwFlags & MCI_SET_DOOR_CLOSED ||
               dwFlags & MCI_SET_VIDEO ||
               dwFlags & MCI_SET_AUDIO) {
        dwRes = MCIERR_UNSUPPORTED_FUNCTION;
    }
    else
        dwRes = MCIERR_MISSING_PARAMETER;

    return dwRes;
}

/****************************************************************************
 *
 * mqMCIGetDevCaps:  Respond to device capabilities command
 *
 * Parms:
 *          nChannel    Channel number for caps
 *          dwFlags     flags
 *          lpCaps      capability parameters
 *
 * Return:  MCI error code
 *
 ***************************************************************************/
DWORD NEAR PASCAL mqMCIGetDevCaps(int nChannel, DWORD dwFlags, LPMCI_GETDEVCAPS_PARMS lpCaps)
{
DWORD dwRes;

    dout("MCI_GETDEVCAPS message");

    if (!dwFlags & MCI_GETDEVCAPS_ITEM)
        return MCIERR_MISSING_PARAMETER;

    switch (lpCaps->dwItem) {

        case MCI_GETDEVCAPS_CAN_RECORD:
        case MCI_GETDEVCAPS_CAN_SAVE:
        case MCI_GETDEVCAPS_CAN_EJECT:
        case MCI_GETDEVCAPS_USES_FILES:
        case MCI_GETDEVCAPS_HAS_AUDIO:
        case MCI_GETDEVCAPS_HAS_VIDEO:
        case MCI_GETDEVCAPS_COMPOUND_DEVICE:
            lpCaps->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
            dwRes = MCI_RESOURCE_RETURNED;
            break;

        case MCI_GETDEVCAPS_CAN_PLAY:
            lpCaps->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
            dwRes = MCI_RESOURCE_RETURNED;
            break;

        case MCI_GETDEVCAPS_DEVICE_TYPE:
            lpCaps->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_OTHER,
                                               MCI_DEVTYPE_OTHER);
            dwRes = MCI_RESOURCE_RETURNED;
            break;

        default:
            dwRes = MCIERR_MISSING_PARAMETER;
            break;
    }

    return dwRes;
}

/****************************************************************************
 *
 * mqMCIProc:  All MCI specific messages go through this point
 *
 * Params:  wDeviceID   The MCI deviceID
 *          wMessage    The requested action to be performed
 *          dwParam1    Data.  Defined seperately for each message
 *          dwParam2    Data.  Defined seperately for each message
 *
 * Return:  Defined seperately for each message
 *
 ***************************************************************************/
DWORD FAR PASCAL mqMCIProc(WORD wDeviceID, WORD wMessage, DWORD dwParam1, DWORD dwParam2)
{
DWORD   dwRes = MCIERR_UNRECOGNIZED_COMMAND;
int     nChannel;
BOOL    fNotify = TRUE;

    nChannel = mciGetDriverData(wDeviceID);

    switch (wMessage) {

        case MCI_OPEN_DRIVER:
            dwRes = mqMCIOpenDriver(nChannel, dwParam1,
                                 (LPMCI_OPEN_PARMS)dwParam2);
            break;

        case MCI_CLOSE_DRIVER:
            mqMCICloseDriver(nChannel, wDeviceID);
            dwRes = MCIERR_NO_ERROR;
            break;

        case MCI_PLAY:
            dwRes = mqMCIPlay(nChannel, wDeviceID, dwParam1,
                               (LPMCI_PLAY_PARMS)dwParam2);
            fNotify = FALSE;
            break;                
                      
        case MCI_STOP:
            dwRes = mqMCIStop(nChannel);
            break;

        case MCI_PAUSE:
            dwRes = mqMCIPause(nChannel);
            break;

        case MCI_CUE:
            dwRes = mqMCICue(nChannel);
            break;

        case MCI_SEEK:
            dwRes = mqMCISeek(nChannel, dwParam1, (LPMCI_SEEK_PARMS)dwParam2);
            break;

        case MCI_STATUS:
            dwRes = mqMCIStatus(nChannel, dwParam1,
                                (LPMCI_STATUS_PARMS)dwParam2);
            break;  

        case MCI_GETDEVCAPS:
            dwRes = mqMCIGetDevCaps(nChannel, dwParam1,
                                    (LPMCI_GETDEVCAPS_PARMS)dwParam2);
            break;

        case MCI_INFO:
            dwRes = mqMCIInfo(dwParam1, (LPMCI_INFO_PARMS)dwParam2);
            break;

        case MCI_SET:
            dwRes = mqMCISet(nChannel, dwParam1,
                             (LPMCI_WAVE_SET_PARMS)dwParam2);
            break;

        case MCI_RESUME:
        case MCI_RECORD:
        case MCI_LOAD:
        case MCI_SAVE:
            dwRes =  MCIERR_UNSUPPORTED_FUNCTION;
            break;
    }

    /*  If the message has not already handled notification and no
     *  error occured then process possible notification
     *
     *  Note, the LOWORD of the error is checked because the HIWORD may
     *  contain non-error condition flags
     */
    if (fNotify && LOWORD(dwRes) == 0)
        mqDriverNotify(wDeviceID, nChannel, 
                       dwParam1, (LPMCI_GENERIC_PARMS)dwParam2,
                       MCI_NOTIFY_SUCCESSFUL);

    return dwRes;
}
Detected encoding: ASCII (7 bit)2