mididd.c

来自「Windows NT声卡驱动VXD」· C语言 代码 · 共 1,581 行 · 第 1/4 页

C
1,581
字号
/****************************************************************************
 *
 *   mididd.c
 *
 *   Multimedia kernel driver support component (esddrv)
 *
 *   Copyright (c) 1991 - 1995 Microsoft Corporation.  All Rights Reserved.
 *
 *   Driver for midi input and output devices
 *
 *   -- Midi driver entry points (modMessage, midMessage)
 *   -- Auxiliary task (necessary for receiving Apcs and generating
 *      callbacks ASYNCRHONOUSLY)
 *   -- Interface to kernel driver (NtDeviceIoControlFile)
 *   -- Midi parsing code (ported from Windows 3.1).
 *
 *   History
 *      01-Feb-1992 - Robin Speed (RobinSp) wrote it
 *
 ***************************************************************************/

#include "esddrv.h"
#include <ntddmidi.h>

/*****************************************************************************

    internal declarations

 ****************************************************************************/

#define D1 dprintf1
#define D2 dprintf2
#define D3 dprintf3


// Stack size for our auxiliary task


#define MIDI_STACK_SIZE 300

#define SYSEX_ERROR    0xFF              // internal error for sysex's on input


// Functions for auxiliary thread to perform


typedef enum {
    MidiThreadInvalid,
    MidiThreadAddBuffer,
    MidiThreadSetState,
    MidiThreadSetData,
    MidiThreadClose,
    MidiThreadTerminate
} MIDITHREADFUNCTION;

//
// Our local buffers for interfacing to midi input


#define LOCAL_MIDI_DATA_SIZE 20
typedef struct _LOCALMIDIHDR {
    OVERLAPPED          Ovl;
    DWORD               BytesReturned;
    struct _LOCALMIDIHDR *lpNext;       // Queueing (really debug only)
    BOOL                Done;           // Driver completed buffer
    PVOID               pClient;        // Our instance data for Apcs
    MIDI_DD_INPUT_DATA  MidiData;       // What the driver wants to process
    BYTE                ExtraData[LOCAL_MIDI_DATA_SIZE - sizeof(ULONG)];
                                        // The rest of our input buffer
} LOCALMIDIHDR, *PLOCALMIDIHDR;


// Midi input data


#define NUMBER_OF_LOCAL_MIDI_BUFFERS 8

typedef struct {

    //
    // Static data for managing midi input
    //
    BOOL                fMidiInStarted; // Do we think midi in is running ?
    DWORD               dwMsg;          // Current short msg
    DWORD               dwCurData;      // Position in long message
    BYTE                status;         // Running status byte
    BOOLEAN             fSysex;         // Processing extended message
    BOOLEAN             Bad;            // Input not working properly
    BYTE                bBytesLeft;     // Bytes left in short message
    BYTE                bBytePos;       // Position in short message
    DWORD               dwCurTime;      // Latest time from driver
    DWORD               dwMsgTime;      // Time to insert into message
                                        // in milliseconds since device
                                        // was opened
    PLOCALMIDIHDR       DeviceQueue;    // Keep track of what the device
                                        // has (debugging only)
    LOCALMIDIHDR                        // Driver interface buffers
     Bufs[NUMBER_OF_LOCAL_MIDI_BUFFERS];// When input is active these
                                        // are queued on the device
                                        // except while data is being
                                        // processed from them
} LOCALMIDIDATA, *PLOCALMIDIDATA;


//
// per allocation structure for Midi
//

typedef struct tag_MIDIALLOC {
    struct tag_MIDIALLOC *Next;         // Chain of devices
    UINT                DeviceNumber;   // Number of device
    UINT                DeviceType;     // MidiInput or MidiOutput
    DWORD               dwCallback;     // client's callback
    DWORD               dwInstance;     // client's instance data
    HMIDI               hMidi;          // handle for stream
    HANDLE              hDev;           // Midi device handle
    LPMIDIHDR           lpMIQueue;      // Buffers sent to device
                                        // This is only required so that
                                        // CLOSE knows when things have
                                        // really finished.
                                        // notify.  This is only accessed
                                        // on the device thread and its
                                        // apcs so does not need any
                                        // synchronized access.
    HANDLE              Event;          // Event for driver syncrhonization
                                        // and notification of auxiliary
                                        // task operation completion.
    MIDITHREADFUNCTION  AuxFunction;    // Function for thread to perform
    union {
        LPMIDIHDR       pHdr;           // Buffer to pass in aux task
        ULONG           State;          // State to set
        struct {
            ULONG       Function;       // IOCTL to use
            PBYTE       pData;          // Data to set or get
            ULONG       DataLen;        // Length of data
        } GetSetData;

    } AuxParam;
                                        // 0 means terminate task.
    HANDLE              ThreadHandle;   // Handle for termination ONLY
    HANDLE              AuxEvent1;      // Aux thread waits on this
    HANDLE              AuxEvent2;      // Aux thread caller waits on this
    DWORD               AuxReturnCode;  // Return code from Aux task
    DWORD               dwFlags;        // Open flags
    PLOCALMIDIDATA      Mid;            // Extra midi input structures
    int                 l;              // Helper global for modMidiLength

} MIDIALLOC, *PMIDIALLOC;

PMIDIALLOC MidiHandleList;              // Our chain of wave handles

/*****************************************************************************

    internal function prototypes

 ****************************************************************************/

STATIC DWORD midiGetDevCaps(DWORD id, UINT DeviceType, LPBYTE lpCaps,
                            DWORD dwSize);
STATIC DWORD midiThread(LPVOID lpParameter);
STATIC void midiCleanUp(PMIDIALLOC pClient);
STATIC DWORD midiThreadCall(MIDITHREADFUNCTION Function, PMIDIALLOC pClient);
STATIC DWORD midiSetState(PMIDIALLOC pClient, ULONG State);
STATIC void midiInOvl(DWORD dwRet, DWORD dwBytes, LPOVERLAPPED pOverlap);
STATIC DWORD midiInWrite(LPMIDIHDR pHdr, PMIDIALLOC pClient);
STATIC DWORD midiOutWrite(PBYTE pData, ULONG Len, PMIDIALLOC pClient);
STATIC void midiBlockFinished(LPMIDIHDR lpHdr, DWORD MsgId);
STATIC void midiCallback(PMIDIALLOC pMidi, DWORD msg, DWORD dw1, DWORD dw2);
STATIC int modMIDIlength(PMIDIALLOC pClient, BYTE b);
STATIC void midByteRec(PMIDIALLOC pClient, BYTE byte);
STATIC void midSendPartBuffer(PMIDIALLOC pClient);
STATIC void midFreeQ(PMIDIALLOC pClient);
STATIC void midiFlush(PMIDIALLOC pClient);

/****************************************************************************
 * @doc INTERNAL
 *
 * @api VOID | TerminateMidi | Free all midi resources for esddrv.dll
 *
 * @rdesc None
 ***************************************************************************/
VOID TerminateMidi(VOID)
{
    //
    // Don't do any cleanup - Midi input resources cleaned up on Close.
    //
}

/****************************************************************************
 * @doc INTERNAL
 *
 * @api void | midiGetDevCaps | Get the device capabilities.
 *
 * @parm DWORD | id | Device id
 *
 * @parm UINT | DeviceType | type of device
 *
 * @parm LPBYTE | lpCaps | Far pointer to a MIDIOUTCAPS structure to
 *      receive the information.
 *
 * @parm DWORD | dwSize | Size of the MIDIOUTCAPS structure.
 *
 * @rdesc There is no return value.
 ***************************************************************************/
STATIC DWORD midiGetDevCaps(DWORD id, UINT DeviceType,
                            LPBYTE lpCaps, DWORD dwSize)
{
    return sndGetData(DeviceType, id, dwSize, lpCaps,
                      IOCTL_MIDI_GET_CAPABILITIES);
}


/****************************************************************************
 * @doc INTERNAL
 *
 * @api DWORD | midiOpen | Open midi device and set up logical device data
 *    and auxilary task for issuing requests and servicing Apc's
 *
 * @parm MIDIDEVTYPE | DeviceType | Whether it's a midi input or output device
 *
 * @parm DWORD | id | The device logical id
 *
 * @parm DWORD | msg | Input parameter to modMessage
 *
 * @parm DWORD | dwUser | Input parameter to modMessage - pointer to
 *   application's handle (generated by this routine)
 *
 * @parm DWORD | dwParam1 | Input parameter to modMessage
 *
 * @parm DWORD | dwParam2 | Input parameter to modMessage
 *
 * @rdesc modMessage return code.
 ***************************************************************************/

STATIC DWORD midiOpen(UINT  DeviceType,
                      DWORD id,
                      DWORD dwUser,
                      DWORD dwParam1,
                      DWORD dwParam2)
{
    PMIDIALLOC     pClient;  // pointer to client information structure
    MMRESULT mRet;

    // dwParam1 contains a pointer to a MIDIOPENDESC
    // dwParam2 contains midi driver specific flags in the LOWORD
    // and generic driver flags in the HIWORD

    //
    // allocate my per-client structure
    //
    if (DeviceType == MidiOutDevice) {
        pClient = (PMIDIALLOC)HeapAlloc(hHeap, 0, sizeof(MIDIALLOC));

        if (pClient != NULL) {
            memset(pClient, 0, sizeof(MIDIALLOC));
        }
    } else {
        WinAssert(DeviceType == MidiInDevice);
        pClient = (PMIDIALLOC)HeapAlloc(hHeap, 0,
                   sizeof(struct _x{MIDIALLOC S1; LOCALMIDIDATA S2;}));

        if (pClient != NULL) {
            memset(pClient, 0, sizeof(struct _x{MIDIALLOC S1; LOCALMIDIDATA S2;}));
        }
    }

    if (pClient == NULL) {
        return MMSYSERR_NOMEM;
    }

    if (DeviceType == MidiInDevice) {
        int i;
        pClient->Mid = (PLOCALMIDIDATA)(pClient + 1);
        for (i = 0 ;i < NUMBER_OF_LOCAL_MIDI_BUFFERS ; i++) {
            pClient->Mid->Bufs[i].pClient = pClient;
        }
    }

    //
    // and fill it with info
    //
    // (note that setting everything to 0 correctly initialized our
    //  midi input processing static data).

    pClient->DeviceType  = DeviceType;
    pClient->dwCallback  = ((LPMIDIOPENDESC)dwParam1)->dwCallback;
    pClient->dwInstance  = ((LPMIDIOPENDESC)dwParam1)->dwInstance;
    pClient->hMidi       = ((LPMIDIOPENDESC)dwParam1)->hMidi;
    pClient->dwFlags     = dwParam2;

    //
    // See if we can open our device
    // If it's only a query be sure only to open for read, otherwise
    // we could get STATUS_BUSY if someone else is writing to the
    // device.
    //

    mRet = sndOpenDev(DeviceType,
                       id,
                       &pClient->hDev,
                       (GENERIC_READ | GENERIC_WRITE));

    if (mRet != MMSYSERR_NOERROR) {

        midiCleanUp(pClient);
        return mRet;
    }


    //
    // Create our event for syncrhonization with the device driver
    //

    pClient->Event = CreateEvent(NULL, FALSE, FALSE, NULL);

    if (pClient->Event == NULL) {
        midiCleanUp(pClient);
        return MMSYSERR_NOMEM;
    }

    if (DeviceType == MidiInDevice) {

        //
        // Create our event pair for synchronization with the auxiliary
        // thread
        //

        pClient->AuxEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL);
        if (pClient->AuxEvent1 == NULL) {
            midiCleanUp(pClient);
            return MMSYSERR_NOMEM;
        }
        pClient->AuxEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL);
        if (pClient->AuxEvent2 == NULL) {
            midiCleanUp(pClient);
            return MMSYSERR_NOMEM;
        }

        //
        // Create our auxiliary thread for sending buffers to the driver
        // and collecting Apcs
        //

        mRet = mmTaskCreate((LPTASKCALLBACK)midiThread,
                            &pClient->ThreadHandle,
                            (DWORD)pClient);

        if (mRet != MMSYSERR_NOERROR) {
            midiCleanUp(pClient);
            return MMSYSERR_NOMEM;
        }

        //
        // Make sure the thread has really started
        //

        WaitForSingleObject(pClient->AuxEvent2, INFINITE);
    }

    //
    // give the client my driver dw
    //
    {
        PMIDIALLOC *pUserHandle;
        pUserHandle = (PMIDIALLOC *)dwUser;
        *pUserHandle = pClient;
    }

    //
    // sent client his OPEN callback message
    //
    midiCallback(pClient, DeviceType == MidiOutDevice ? MOM_OPEN : MIM_OPEN,
                 0L, 0L);

    return MMSYSERR_NOERROR;
}

/****************************************************************************
 * @doc INTERNAL
 *
 * @api void | midiCleanUp | Free resources for a midi device
 *
 * @parm PMIDIALLOC | pClient | Pointer to a MIDIALLOC structure describing
 *      resources to be freed.
 *
 * @rdesc There is no return value.
 *
 * @comm If the pointer to the resource is NULL then the resource has not
 *     been allocated.
 ***************************************************************************/
STATIC void midiCleanUp(PMIDIALLOC pClient)
{
    if (pClient->hDev != INVALID_HANDLE_VALUE) {
        CloseHandle(pClient->hDev);
    }
    if (pClient->AuxEvent1) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?