mididd.c

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

C
1,581
字号
        CloseHandle(pClient->AuxEvent1);
    }
    if (pClient->AuxEvent2) {
        CloseHandle(pClient->AuxEvent2);
    }
    if (pClient->Event) {
        CloseHandle(pClient->Event);
    }

    HeapFree(hHeap, 0, (LPSTR)pClient);
}

/****************************************************************************
 * @doc INTERNAL
 *
 * @api DWORD | midiOutWrite | Synchronously process a midi output
 *       buffer.
 *
 * @parm LPMIDIHDR | pHdr | Pointer to a midi buffer
 *
 * @parm PMIDIALLOC | pClient | The data associated with the logical midi
 *     device.
 *
 * @rdesc A MMSYS... type return code for the application.
 ***************************************************************************/
STATIC DWORD midiOutWrite(PBYTE pData, ULONG Len, PMIDIALLOC pClient)
{
    DWORD BytesReturned;

    //
    // Try passing the request to our driver
    // We operate synchronously but allow for the driver to operate
    // asynchronously by waiting on an event.
    //

    if (!DeviceIoControl(
                    pClient->hDev,
                    IOCTL_MIDI_PLAY,
                    (PVOID)pData,                // Input buffer
                    Len,                         // Input buffer size
                    NULL,                        // Output buffer
                    0,                           // Output buffer size
                    &BytesReturned,
                    NULL)) {
        return sndTranslateStatus();
    }

    return MMSYSERR_NOERROR;
}

/****************************************************************************
 * @doc INTERNAL
 *
 * @api DWORD | midiInPutBuffer | Pass a buffer to receive midi input
 *
 * @parm LPMIDIHDR | pHdr | Pointer to a midi buffer
 *
 * @parm PMIDIALLOC | pClient | The data associated with the logical midi
 *     device.
 *
 * @rdesc A MMSYS... type return code for the application.
 ***************************************************************************/
STATIC MMRESULT midiInPutBuffer(PLOCALMIDIHDR pHdr, PMIDIALLOC pClient)
{
    DWORD BytesReturned;
    BOOL Result;

    WinAssert(!pHdr->Done);  // Flag should be clear ready for setting by Apc

    //
    // BUGBUG - nice to have a semaphore for some of this !
    //

    //
    // Try passing the request to our driver
    // We operate synchronously but allow for the driver to operate
    // asynchronously by waiting on an event.
    //

    Result = ReadFileEx(
                 pClient->hDev,
                 (LPVOID)&pHdr->MidiData,
                 sizeof(pHdr->ExtraData) +
                     sizeof(MIDI_DD_INPUT_DATA),
                 &pHdr->Ovl,
                 midiInOvl);

    //
    // Put the buffer in our queue
    //

    if (Result || GetLastError() == ERROR_IO_PENDING) {
        PLOCALMIDIHDR *ppHdr;
        pHdr->lpNext = NULL;
        ppHdr = &pClient->Mid->DeviceQueue;
        while (*ppHdr) {
           ppHdr = &(*ppHdr)->lpNext;
        }

        *ppHdr = pHdr;

        return MMSYSERR_NOERROR;
    }
    return sndTranslateStatus();
}

/****************************************************************************
 * @doc INTERNAL
 *
 * @api DWORD | midiInWrite | Pass a new buffer to the Auxiliary thread for
 *       a midi device.
 *
 * @parm LPMIDIHDR | pHdr | Pointer to a midit buffer
 *
 * @parm PMIDIALLOC | pClient | The data associated with the logical midi
 *     device.
 *
 * @rdesc A MMSYS... type return code for the application.
 *
 * @comm The buffer flags are set and the buffer is passed to the auxiliary
 *     device task for processing.
 ***************************************************************************/
STATIC DWORD midiInWrite(LPMIDIHDR pHdr, PMIDIALLOC pClient)
{
    //
    // Put the request at the end of our queue.
    //
    pHdr->dwFlags |= MHDR_INQUEUE;
    pHdr->dwFlags &= ~MHDR_DONE;
    pClient->AuxParam.pHdr = pHdr;
    return midiThreadCall(MidiThreadAddBuffer, pClient);
}

/****************************************************************************
 * @doc INTERNAL
 *
 * @api DWORD | midiSetState | Set a midi device to a given state
 *
 * @parm PMIDIALLOC | pClient | The data associated with the logical midi
 *     output device.
 *
 * @parm ULONG | State | The new state
 *
 * @rdesc A MMSYS... type return code for the application.
 ***************************************************************************/
STATIC DWORD midiSetState(PMIDIALLOC pClient, ULONG State)
{
    MMRESULT mRc;

    mRc = sndSetHandleData(pClient->hDev,
                           sizeof(State),
                           &State,
                           IOCTL_MIDI_SET_STATE,
                           pClient->Event);

    midiFlush(pClient);

    return mRc;
}



/****************************************************************************
 * @doc INTERNAL
 *
 * @api DWORD | midiThreadCall | Set the function for the thread to perform
 *     and 'call' the thread using the event pair mechanism.
 *
 * @parm MIDITHREADFUNCTION | Function | The function to perform
 *
 * @parm PMIDIALLOC | Our logical device data
 *
 * @rdesc An MMSYS... type return value suitable for returning to the
 *      application
 *
 * @comm The AuxParam field in the device data is the 'input' to
 *      the function processing loop in MidiThread().
 ***************************************************************************/
STATIC DWORD midiThreadCall(MIDITHREADFUNCTION Function, PMIDIALLOC pClient)
{
    //
    // Set the function code
    //
    pClient->AuxFunction = Function;

    //
    // Kick off the thread
    //
    SetEvent(pClient->AuxEvent1);

    //
    // Wait for it to complete
    //
    WaitForSingleObject(pClient->AuxEvent2, INFINITE);

    //
    // Return the return code that our task set.
    //
    return pClient->AuxReturnCode;
}

/****************************************************************************
 * @doc INTERNAL
 *
 * @api void | midiInApc | Apc routine.  Called when a kernel sound driver
 *     completes processing of a midi buffer.
 *
 * @parm PVOID | ApcContext | The Apc parameter.  In our case this is a
 *     pointer to our midi device data.
 *
 * @parm PIO_STATUS_BLOCK | pIosb | Pointer to the Io status block
 *     used for the request.
 *
 * @rdesc There is no return code.
 ***************************************************************************/
STATIC void midiInOvl(DWORD dwRet, DWORD dwBytesReturned, LPOVERLAPPED pOverlap)
{
    PLOCALMIDIHDR pHdr;

    pHdr = ((PLOCALMIDIHDR)pOverlap);

    WinAssert(((PMIDIALLOC)pHdr->pClient)->DeviceType == MidiInDevice);

    //
    // Note that the buffer is complete.  We don't do anything else here
    // because funny things happen if we call the client's callback
    // routine from within an Apc.
    //

    pHdr->BytesReturned = dwBytesReturned;
    pHdr->Done = TRUE;
}

/****************************************************************************
 * @doc INTERNAL
 *
 * @api void | midiFlush | Buffer completion routine.  This completes
 *     the work of the Apc routine at below Apc priority.  This gets
 *     round the nasty situations arising when the user's callback
 *     causes more apcs to run (I strongly suspect this is a kernel
 *     but).
 *
 * @parm PMIDIALLOC | pClient | The client's handle data
 *
 * @rdesc There is no return code.
 ***************************************************************************/

STATIC void midiFlush(PMIDIALLOC pClient)
{
    //
    // Process any completed buffers - the Apc routine
    // set the 'Done' flag in any completed requests.
    // Note that the call to the user's callback can
    // cause more requests to become complete
    //

    if (pClient->DeviceType == MidiInDevice) {  // Output is synchronous
        while (pClient->Mid->DeviceQueue &&
               pClient->Mid->DeviceQueue->Done) {

            PLOCALMIDIHDR pHdr;

            pHdr = pClient->Mid->DeviceQueue;

            //
            // Clear our flag ready for next time
            //

            pHdr->Done = FALSE;

            //
            // Take buffer off the device queue
            //


            pClient->Mid->DeviceQueue = pHdr->lpNext;

            //
            // Grab the latest time estimate - convert from 100ns units
            // to milliseconds
            //

            pClient->Mid->dwCurTime =
                (DWORD)(pHdr->MidiData.Time.QuadPart / 10000);

            //
            // Complete our buffer
            //

            if (!pClient->Mid->Bad) {
                int i;
                for (i = 0;
                             i + sizeof(LARGE_INTEGER) < pHdr->BytesReturned;
                                 i++) {
                    midByteRec(pClient, pHdr->MidiData.Data[i]);
                }
                //
                // Requeue our buffer if we're still recording
                //
                if (pClient->Mid->fMidiInStarted) {
                    if (midiInPutBuffer(pHdr, pClient) != MMSYSERR_NOERROR) {
                        pClient->Mid->Bad = TRUE;
                    }
                }
            }
        } // End of processing completed buffers
    }
}

/****************************************************************************
 * @doc INTERNAL
 *
 * @api DWORD | midiThread | Midi device auxiliary thread.
 *
 * @parm LPVOID | lpParameter | The thread parameter.  In our case this is a
 *     pointer to our midi device data.
 *
 * @rdesc Thread return code.
 ***************************************************************************/
STATIC DWORD midiThread(LPVOID lpParameter)
{
    PMIDIALLOC pClient;
    BOOL Close;

    Close = FALSE;

    pClient = (PMIDIALLOC)lpParameter;

    //
    // Set our thread to high priority so we don't fail to pass
    // new buffers to the device when we get them back.  Also
    // we don't want any gaps if callbacks are meant to play
    // notes just received.
    //

    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);

    //
    // We start notifying our creator we have started and
    // waiting for something to do.
    //

    SetEvent(pClient->AuxEvent2);
    WaitForSingleObject(pClient->AuxEvent1, INFINITE);

    //
    // Now we're going
    //

    for(;;) {
        //
        // Initialize our return code
        //

        pClient->AuxReturnCode = MMSYSERR_NOERROR;

        //
        // Decode function number to perform
        //

        switch (pClient->AuxFunction) {
        case MidiThreadAddBuffer:

            //
            // Add the buffer to our list to be processed
            //
            {
                LPMIDIHDR *pHdrSearch;

                pClient->AuxParam.pHdr->lpNext = NULL;

                pHdrSearch = &pClient->lpMIQueue;
                while (*pHdrSearch) {
                    pHdrSearch = &(*pHdrSearch)->lpNext;
                }

                *pHdrSearch = pClient->AuxParam.pHdr;
            }
            break;

        case MidiThreadSetState:



            switch (pClient->AuxParam.State) {
            case MIDI_DD_RECORD:
                //
                // Start means we must add our buffers to the driver's list
                //
                if (!pClient->Mid->fMidiInStarted && !pClient->Mid->Bad) {
                    int i;
                    for (i = 0; i < NUMBER_OF_LOCAL_MIDI_BUFFERS; i++) {
                        pClient->AuxReturnCode =
                            midiInPutBuffer(&pClient->Mid->Bufs[i], pClient);

                        if (pClient->AuxReturnCode != MMSYSERR_NOERROR) {

⌨️ 快捷键说明

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