windows-nt/Source/XPSP1/NT/drivers/wdm/audio/legacy/wdmaud.drv/midiin.c
2020-09-26 16:20:57 +08:00

692 lines
20 KiB
C

/****************************************************************************
*
* midiin.c
*
* WDM Audio support for Midi Input devices
*
* Copyright (C) Microsoft Corporation, 1997 - 1999 All Rights Reserved.
*
* History
* 5-12-97 - Noel Cross (NoelC)
*
***************************************************************************/
#include "wdmdrv.h"
#include <stdarg.h>
#ifndef UNDER_NT
#pragma alloc_text(FIXCODE, midiCallback)
#pragma alloc_text(FIXCODE, midiInCompleteHeader)
#endif
/****************************************************************************
This function conforms to the standard Midi input driver message proc
(midMessage), which is documented in mmddk.h
****************************************************************************/
DWORD FAR PASCAL _loadds midMessage
(
UINT id,
UINT msg,
DWORD_PTR dwUser,
DWORD_PTR dwParam1,
DWORD_PTR dwParam2
)
{
LPDEVICEINFO DeviceInfo;
LPDEVICEINFO pInClient;
MMRESULT mmr;
switch (msg)
{
case MIDM_INIT:
DPF(DL_TRACE|FA_MIDI, ("MIDM_INIT") );
return wdmaudAddRemoveDevNode(MidiInDevice, (LPCWSTR)dwParam2, TRUE);
case DRVM_EXIT:
DPF(DL_TRACE|FA_MIDI, ("DRVM_EXIT: MidiIn") );
return wdmaudAddRemoveDevNode(MidiInDevice, (LPCWSTR)dwParam2, FALSE);
case MIDM_GETNUMDEVS:
DPF(DL_TRACE|FA_MIDI, ("MIDM_GETNUMDEVS") );
return wdmaudGetNumDevs(MidiInDevice, (LPCWSTR)dwParam1);
case MIDM_GETDEVCAPS:
DPF(DL_TRACE|FA_MIDI, ("MIDM_GETDEVCAPS") );
if (DeviceInfo = GlobalAllocDeviceInfo((LPWSTR)dwParam2))
{
DeviceInfo->DeviceType = MidiInDevice;
DeviceInfo->DeviceNumber = id;
mmr = wdmaudGetDevCaps(DeviceInfo, (MDEVICECAPSEX FAR*)dwParam1);
GlobalFreeDeviceInfo(DeviceInfo);
return mmr;
} else {
MMRRETURN( MMSYSERR_NOMEM );
}
case MIDM_OPEN:
{
LPMIDIOPENDESC pmod = (LPMIDIOPENDESC)dwParam1;
DPF(DL_TRACE|FA_MIDI, ("MIDM_OPEN") );
if (DeviceInfo = GlobalAllocDeviceInfo((LPWSTR)pmod->dnDevNode))
{
DeviceInfo->DeviceType = MidiInDevice;
DeviceInfo->DeviceNumber = id;
mmr = midiOpen(DeviceInfo, dwUser, pmod, (DWORD)dwParam2);
DPF(DL_TRACE|FA_MIDI,("dwUser(DI)=%08X",dwUser) );
GlobalFreeDeviceInfo(DeviceInfo);
return mmr;
} else {
MMRRETURN( MMSYSERR_NOMEM );
}
}
case MIDM_CLOSE:
DPF(DL_TRACE|FA_MIDI, ("MIDM_CLOSE dwUser(DI)=%08X",dwUser) );
pInClient = (LPDEVICEINFO)dwUser;
if( (mmr=IsValidDeviceInfo(pInClient)) != MMSYSERR_NOERROR )
{
MMRRETURN( mmr );
}
mmr = wdmaudCloseDev( pInClient );
if (MMSYSERR_NOERROR == mmr)
{
#ifdef UNDER_NT
//
// Wait for all of the queued up I/O to come back from
// wdmaud.sys.
//
wdmaudDestroyCompletionThread ( pInClient );
#endif
//
// Tell the caller we're done
//
midiCallback( pInClient, MIM_CLOSE, 0L, 0L);
ISVALIDDEVICEINFO(pInClient);
ISVALIDDEVICESTATE(pInClient->DeviceState,FALSE);
midiCleanUp( pInClient );
}
return mmr;
case MIDM_ADDBUFFER:
DPF(DL_TRACE|FA_MIDI, ("MIDM_ADDBUFFER") );
//
// Don't touch bad pointers!
//
pInClient = (LPDEVICEINFO)dwUser;
if( ( (mmr=IsValidDeviceInfo(pInClient)) != MMSYSERR_NOERROR) ||
( (mmr=IsValidDeviceState(pInClient->DeviceState,FALSE)) != MMSYSERR_NOERROR ) ||
( (mmr=IsValidMidiHeader((LPMIDIHDR)dwParam1)) != MMSYSERR_NOERROR)
)
{
MMRRETURN( mmr );
}
// check if it's been prepared
if (!(((LPMIDIHDR)dwParam1)->dwFlags & MHDR_PREPARED))
MMRRETURN( MIDIERR_UNPREPARED );
DPFASSERT(!(((LPMIDIHDR)dwParam1)->dwFlags & MHDR_INQUEUE));
// if it is already in our Q, then we cannot do this
if ( ((LPMIDIHDR)dwParam1)->dwFlags & MHDR_INQUEUE )
MMRRETURN( MIDIERR_STILLPLAYING );
DPF(DL_TRACE|FA_MIDI,("dwUser(DI)=%08X,dwParam1(HDR)=%08X",pInClient,dwParam1) );
return midiInRead( pInClient, (LPMIDIHDR)dwParam1);
case MIDM_STOP:
DPF(DL_TRACE|FA_MIDI, ("MIDM_STOP dwUser(DI)=%08X",dwUser) );
pInClient = (LPDEVICEINFO)dwUser;
return wdmaudSetDeviceState(pInClient,
IOCTL_WDMAUD_MIDI_IN_STOP);
case MIDM_START:
DPF(DL_TRACE|FA_MIDI, ("MIDM_START dwUser(DI)=%08X",dwUser) );
pInClient = (LPDEVICEINFO)dwUser;
return wdmaudSetDeviceState(pInClient,
IOCTL_WDMAUD_MIDI_IN_RECORD);
case MIDM_RESET:
DPF(DL_TRACE|FA_MIDI, ("MIDM_RESET dwUser(DI)=%08X",dwUser) );
pInClient = (LPDEVICEINFO)dwUser;
return wdmaudSetDeviceState(pInClient,
IOCTL_WDMAUD_MIDI_IN_RESET);
#ifdef MIDI_THRU
case DRVM_ADD_THRU:
case DRVM_REMOVE_THRU:
#endif
default:
MMRRETURN( MMSYSERR_NOTSUPPORTED );
}
//
// Should not get here
//
DPFASSERT(0);
MMRRETURN( MMSYSERR_NOTSUPPORTED );
}
/****************************************************************************
* @doc INTERNAL
*
* @api void | midiCallback | This calls DriverCallback for a MIDIHDR.
*
* @parm LPDEVICEINFO | pMidiDevice | pointer to midi device.
*
* @parm UINT | msg | The message.
*
* @parm DWORD | dw1 | message DWORD (dw2 is always set to 0).
*
* @rdesc There is no return value.
***************************************************************************/
VOID FAR midiCallback
(
LPDEVICEINFO pMidiDevice,
UINT msg,
DWORD_PTR dw1,
DWORD_PTR dw2
)
{
// invoke the callback function, if it exists. dwFlags contains
// midi driver specific flags in the LOWORD and generic driver
// flags in the HIWORD
if (pMidiDevice->dwCallback)
DriverCallback(pMidiDevice->dwCallback, // user's callback DWORD
HIWORD(pMidiDevice->dwFlags), // callback flags
(HDRVR)pMidiDevice->DeviceHandle, // handle to the midi device
msg, // the message
pMidiDevice->dwInstance, // user's instance data
dw1, // first DWORD
dw2); // second DWORD
}
/****************************************************************************
* @doc INTERNAL
*
* @api DWORD | midiOpen | Open midi device and set up logical device data
*
* @parm LPDEVICEINFO | DeviceInfo | Specifies if it's a midi input or output
* device
*
* @parm DWORD | dwUser | Input parameter to modMessage - pointer to
* application's handle (generated by this routine)
*
* @parm DWORD | pmod | pointer to MIDIOPENDESC, was dwParam1 parameter
* to modMessage
*
* @parm DWORD | dwParam2 | Input parameter to modMessage
*
* @rdesc modMessage return code.
***************************************************************************/
MMRESULT FAR midiOpen
(
LPDEVICEINFO DeviceInfo,
DWORD_PTR dwUser,
LPMIDIOPENDESC pmod,
DWORD dwParam2
)
{
LPDEVICEINFO pClient; // pointer to client information structure
MMRESULT mmr;
#ifndef UNDER_NT
DWORD dwCallback16;
#else
ULONG BufferCount;
#endif
// pmod 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
//
pClient = GlobalAllocDeviceInfo(DeviceInfo->wstrDeviceInterface);
if (NULL == pClient)
{
MMRRETURN( MMSYSERR_NOMEM );
}
pClient->DeviceState = (LPVOID) GlobalAllocPtr( GPTR, sizeof( DEVICESTATE ) );
if (NULL == pClient->DeviceState)
{
GlobalFreeDeviceInfo( pClient );
MMRRETURN( MMSYSERR_NOMEM );
}
#ifdef UNDER_NT
//
// Allocate memory for our critical section
//
pClient->DeviceState->csQueue = (LPVOID) GlobalAllocPtr( GPTR, sizeof( CRITICAL_SECTION ) );
if (NULL == pClient->DeviceState->csQueue)
{
GlobalFreePtr( pClient->DeviceState );
GlobalFreeDeviceInfo( pClient );
MMRRETURN( MMSYSERR_NOMEM );
}
try
{
InitializeCriticalSection( (LPCRITICAL_SECTION)pClient->DeviceState->csQueue );
}
except(EXCEPTION_EXECUTE_HANDLER)
{
GlobalFreePtr( pClient->DeviceState->csQueue );
GlobalFreePtr( pClient->DeviceState );
GlobalFreeDeviceInfo( pClient );
MMRRETURN( MMSYSERR_NOMEM );
}
#endif
//
// fill out context data
//
pClient->DeviceNumber= DeviceInfo->DeviceNumber;
pClient->DeviceType = DeviceInfo->DeviceType;
pClient->dwInstance = pmod->dwInstance;
pClient->dwCallback = pmod->dwCallback;
#ifdef UNDER_NT
pClient->DeviceHandle= (HANDLE32)pmod->hMidi;
#else
pClient->DeviceHandle= (HANDLE32)MAKELONG(pmod->hMidi,0);
_asm
{
mov ax, offset MidiEventDeviceCallback
mov word ptr [dwCallback16], ax
mov ax, seg MidiEventDeviceCallback
mov word ptr [dwCallback16+2], ax
}
pClient->dwCallback16= dwCallback16;
#endif
pClient->dwFlags = dwParam2;
//
// initialize the device state
//
DPF(DL_TRACE|FA_SYNC,("DI=%08X New DeviceState",pClient) );
pClient->DeviceState->lpMidiInQueue= NULL;
pClient->DeviceState->fPaused = FALSE;
pClient->DeviceState->fRunning = FALSE;
pClient->DeviceState->fExit = FALSE;
pClient->DeviceState->bMidiStatus = 0;
pClient->DeviceState->lpNoteOnMap = NULL;
#ifdef DEBUG
pClient->DeviceState->dwSig = DEVICESTATE_SIGNATURE;
#endif
if (pClient->DeviceType == MidiOutDevice)
{
//
// For MIDI out, allocate one byte per note per channel to track
// what's been played. This is used to avoid doing a brute-force
// all notes off on MODM_RESET.
//
pClient->DeviceState->lpNoteOnMap = GlobalAllocPtr( GPTR, MIDI_NOTE_MAP_SIZE );
if (NULL == pClient->DeviceState->lpNoteOnMap)
{
#ifdef UNDER_NT
GlobalFreePtr( pClient->DeviceState->csQueue );
#endif
GlobalFreePtr( pClient->DeviceState );
GlobalFreeDeviceInfo( pClient );
MMRRETURN( MMSYSERR_NOMEM );
}
}
//
// See if we can open our device
//
mmr = wdmaudOpenDev( pClient, NULL );
if (mmr != MMSYSERR_NOERROR)
{
if ( pClient->DeviceState->lpNoteOnMap )
{
GlobalFreePtr( pClient->DeviceState->lpNoteOnMap );
}
#ifdef UNDER_NT
DeleteCriticalSection( (LPCRITICAL_SECTION)pClient->DeviceState->csQueue );
GlobalFreePtr( pClient->DeviceState->csQueue );
#endif
GlobalFreePtr( pClient->DeviceState );
GlobalFreeDeviceInfo( pClient ) ;
return mmr;
}
//
// Add instance to chain of devices
//
EnterCriticalSection(&wdmaudCritSec);
pClient->Next = pMidiDeviceList;
pMidiDeviceList = pClient;
LeaveCriticalSection(&wdmaudCritSec);
//
// give the client my driver dw
//
{
LPDEVICEINFO FAR *pUserHandle;
pUserHandle = (LPDEVICEINFO FAR *)dwUser;
*pUserHandle = pClient;
}
#ifndef UNDER_NT
// If this is a MIDI output device, page lock the memory because it can
// be accessed at interrupt time.
//
GlobalSmartPageLock( (HGLOBAL)HIWORD( pClient ));
GlobalSmartPageLock( (HGLOBAL)HIWORD( pClient->DeviceState ));
GlobalSmartPageLock( (HGLOBAL)HIWORD( pClient->DeviceState->lpNoteOnMap ));
#endif
#ifdef UNDER_NT
//
// If this is a MIDI input device on NT, send some buffers
// down to the device in order so that once recording starts
// we can get some data back. The pin is paused after
// the call to wdmaudOpenDev.
//
if ( MidiInDevice == pClient->DeviceType )
{
for (BufferCount = 0; BufferCount < STREAM_BUFFERS; BufferCount++)
{
mmr = wdmaudGetMidiData( pClient, NULL );
if ( MMSYSERR_NOERROR != mmr )
{
//
// We hope that this doesn't happen, but if it does
// we need to try to close down the device.
//
if ( MMSYSERR_NOERROR == wdmaudCloseDev( pClient ) )
{
wdmaudDestroyCompletionThread ( pClient );
midiCleanUp( pClient );
}
DPF( 2, ("midiInOpen failed: returning mmr = %d", mmr ) );
return mmr;
}
}
}
#endif
//
// sent client his OPEN callback message
//
midiCallback(pClient, DeviceInfo->DeviceType == MidiOutDevice ? MOM_OPEN : MIM_OPEN,
0L, 0L);
return MMSYSERR_NOERROR;
}
/****************************************************************************
* @doc INTERNAL
*
* @api void | midiCleanUp | Free resources for a midi device
*
* @parm LPDEVICEINFO | pClient | Pointer to a DEVICEINFO 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.
***************************************************************************/
VOID FAR midiCleanUp
(
LPDEVICEINFO pClient
)
{
LPDEVICEINFO FAR *ppCur ;
//
// remove from device chain
//
EnterCriticalSection(&wdmaudCritSec);
for (ppCur = &pMidiDeviceList;
*ppCur != NULL;
ppCur = &(*ppCur)->Next)
{
if (*ppCur == pClient)
{
*ppCur = (*ppCur)->Next;
break;
}
}
LeaveCriticalSection(&wdmaudCritSec);
DPF(DL_TRACE|FA_SYNC,("DI=%08X Freed DeviceState",pClient) );
#ifdef UNDER_NT
DeleteCriticalSection( (LPCRITICAL_SECTION)pClient->DeviceState->csQueue );
GlobalFreePtr( pClient->DeviceState->csQueue );
#endif
if (pClient->DeviceState->lpNoteOnMap)
{
#ifndef UNDER_NT
GlobalSmartPageUnlock( (HGLOBAL)HIWORD( pClient->DeviceState->lpNoteOnMap ));
#endif
GlobalFreePtr( pClient->DeviceState->lpNoteOnMap );
}
#ifndef UNDER_NT
GlobalSmartPageUnlock( (HGLOBAL)HIWORD( pClient->DeviceState ));
GlobalSmartPageUnlock( (HGLOBAL)HIWORD( pClient ));
#endif
#ifdef DEBUG
//
// In debug, let's set all the values in the DEVICESTATE structure to bad
// values.
//
pClient->DeviceState->cSampleBits=0xDEADBEEF;
pClient->DeviceState->hThread=NULL;
pClient->DeviceState->dwThreadId=0xDEADBEEF;
pClient->DeviceState->lpWaveQueue=NULL;
pClient->DeviceState->csQueue=NULL;
pClient->DeviceState->hevtQueue=NULL;
pClient->DeviceState->hevtExitThread=NULL;
#endif
GlobalFreePtr( pClient->DeviceState );
pClient->DeviceState=NULL;
#ifdef DEBUG
//
// Now set all the values in the DEVICEINFO structure to bad values.
//
// pClient->Next=(LPDEVICEINFO)0xDEADBEEF;
pClient->DeviceNumber=-1;
pClient->DeviceType=0xDEADBEEF;
pClient->DeviceHandle=NULL;
pClient->dwInstance=(DWORD_PTR)NULL;
pClient->dwCallback=(DWORD_PTR)NULL;
pClient->dwCallback16=0xDEADBEEF;
pClient->dwFlags=0xDEADBEEF;
pClient->DataBuffer=NULL;
pClient->HardwareCallbackEventHandle=NULL;
pClient->dwCallbackType=0xDEADBEEF;
pClient->dwLineID=0xDEADBEEF;
pClient->dwFormat=0xDEADBEEF;
// pClient->DeviceState=(LPDEVICESTATE)0xDEADBEEF;
#endif
GlobalFreeDeviceInfo( pClient ) ;
pClient=NULL;
}
/****************************************************************************
* @doc INTERNAL
*
* @api DWORD | midiInRead | Pass a new buffer to the Auxiliary thread for
* a midi device.
*
* @parm LPDEVICEINFO | pClient | The data associated with the logical midi
* device.
*
* @parm LPMIDIHDR | pHdr | Pointer to a midi buffer
*
* @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.
***************************************************************************/
MMRESULT midiInRead
(
LPDEVICEINFO DeviceInfo,
LPMIDIHDR pHdr
)
{
MMRESULT mmr = MMSYSERR_NOERROR;
LPMIDIHDR pTemp;
DWORD dwCallback16;
//
// Put the request at the end of our queue.
//
pHdr->dwFlags |= MHDR_INQUEUE;
pHdr->dwFlags &= ~MHDR_DONE;
pHdr->dwBytesRecorded = 0;
pHdr->lpNext = NULL;
//
// Store the context for this write in the header so that
// we know which client to send this back to on completion.
//
pHdr->reserved = (DWORD_PTR)DeviceInfo;
#ifndef UNDER_NT
//
// Put the long message callback handler into the
// DeviceInfo structure.
//
_asm
{
mov ax, offset MidiInDeviceCallback
mov word ptr [dwCallback16], ax
mov ax, seg MidiInDeviceCallback
mov word ptr [dwCallback16+2], ax
}
DeviceInfo->dwCallback16 = dwCallback16;
#endif
//
// Add the MIDI header to the queue
//
CRITENTER ;
if (!DeviceInfo->DeviceState->lpMidiInQueue)
{
DeviceInfo->DeviceState->lpMidiInQueue = pHdr;
pTemp = NULL;
#ifdef UNDER_NT
if( (DeviceInfo->DeviceState->hevtQueue) &&
(DeviceInfo->DeviceState->hevtQueue != (HANDLE)FOURTYTHREE) &&
(DeviceInfo->DeviceState->hevtQueue != (HANDLE)FOURTYTWO) )
{
DPF(DL_TRACE|FA_SYNC,("REMOVED: SetEvent on hevtQueue") );
// SetEvent( DeviceInfo->DeviceState->hevtQueue );
}
#endif
}
else
{
for (pTemp = DeviceInfo->DeviceState->lpMidiInQueue;
pTemp->lpNext != NULL;
pTemp = pTemp->lpNext);
pTemp->lpNext = pHdr;
}
CRITLEAVE ;
#ifndef UNDER_NT
//
// Call the 16 routine to send the buffer down
// to the kernel. On NT, do all the processing
// of the MIDI data in User mode.
//
mmr = wdmaudSubmitMidiInHeader(DeviceInfo, pHdr);
if (mmr != MMSYSERR_NOERROR)
{
// Unlink...
GlobalFreePtr( pHdr );
if (pTemp)
{
pTemp->lpNext = NULL;
}
else
DeviceInfo->DeviceState->lpMidiInQueue = NULL;
pHdr->dwFlags &= ~WHDR_INQUEUE;
DbgBreak();
MMRRETURN( mmr ); // used to return MMSYSERR_INVALPARAM for all errors!
}
#endif
return mmr;
}
/****************************************************************************
* @doc INTERNAL
*
* @api VOID | midiInCompleteHeader |
*
* @parm LPDEVICEINFO | DeviceInfo | The data associated with the logical midi
* device.
*
* @comm The buffer flags are set and the buffer is passed to the auxiliary
* device task for processing.
****************************************************************************/
VOID midiInCompleteHeader
(
LPDEVICEINFO DeviceInfo,
DWORD dwTimeStamp,
WORD wDataType
)
{
LPMIDIHDR pHdr;
DPFASSERT(DeviceInfo);
//
// Only remove headers from the front of the queue
// so that order is maintained.
//
if (pHdr = DeviceInfo->DeviceState->lpMidiInQueue)
{
DeviceInfo->DeviceState->lpMidiInQueue = DeviceInfo->DeviceState->lpMidiInQueue->lpNext;
DPF(DL_TRACE|FA_MIDI, ("Pulling header out of queue") );
pHdr->dwFlags &= ~MHDR_INQUEUE;
pHdr->dwFlags |= MHDR_DONE;
pHdr->lpNext = NULL;
midiCallback((LPDEVICEINFO)pHdr->reserved,
wDataType, // MIM_LONGDATA or MIM_LONGERROR
(DWORD_PTR)pHdr,
dwTimeStamp);
}
}