|
|
/****************************************************************************
* * 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); } }
|