|
|
/****************************************************************************
* * midiout.c * * WDM Audio support for Midi Output devices * * Copyright (C) Microsoft Corporation, 1997 - 1999 All Rights Reserved. * * History * 5-12-97 - Noel Cross (NoelC) * ***************************************************************************/
#include "wdmdrv.h"
#ifndef UNDER_NT
#pragma alloc_text(FIXCODE, modMessage)
#pragma alloc_text(FIXCODE, midiOutWrite)
#endif
/****************************************************************************
This function conforms to the standard Midi output driver message proc (modMessage), which is documented in mmddk.h
****************************************************************************/
DWORD FAR PASCAL _loadds modMessage ( UINT id, UINT msg, DWORD_PTR dwUser, DWORD_PTR dwParam1, DWORD_PTR dwParam2 ) { LPDEVICEINFO pOutClient; LPDWORD pVolume; LPDEVICEINFO DeviceInfo; MMRESULT mmr;
switch (msg) { case MODM_INIT: DPF(DL_TRACE|FA_MIDI, ("MODM_INIT") ); return wdmaudAddRemoveDevNode(MidiOutDevice, (LPCWSTR)dwParam2, TRUE);
case DRVM_EXIT: DPF(DL_TRACE|FA_MIDI, ("DRVM_EXIT: MidiOut") ); return wdmaudAddRemoveDevNode(MidiOutDevice, (LPCWSTR)dwParam2, FALSE);
case MODM_GETNUMDEVS: DPF(DL_TRACE|FA_MIDI, ("MODM_GETNUMDEVS") ); return wdmaudGetNumDevs(MidiOutDevice, (LPCWSTR)dwParam1);
case MODM_GETDEVCAPS: DPF(DL_TRACE|FA_MIDI, ("MODM_GETDEVCAPS") ); if (DeviceInfo = GlobalAllocDeviceInfo((LPWSTR)dwParam2)) { DeviceInfo->DeviceType = MidiOutDevice; DeviceInfo->DeviceNumber = id; mmr = wdmaudGetDevCaps(DeviceInfo, (MDEVICECAPSEX FAR*)dwParam1); GlobalFreeDeviceInfo(DeviceInfo); return mmr; } else { MMRRETURN( MMSYSERR_NOMEM ); }
case MODM_PREFERRED: DPF(DL_TRACE|FA_MIDI, ("MODM_PREFERRED") ); return wdmaudSetPreferredDevice( MidiOutDevice, id, dwParam1, dwParam2);
case MODM_OPEN: { LPMIDIOPENDESC pmod = (LPMIDIOPENDESC)dwParam1;
DPF(DL_TRACE|FA_MIDI, ("MODM_OPEN") ); if (DeviceInfo = GlobalAllocDeviceInfo((LPWSTR)pmod->dnDevNode)) { DeviceInfo->DeviceType = MidiOutDevice; DeviceInfo->DeviceNumber = id; #ifdef UNDER_NT
DeviceInfo->DeviceHandle = (HANDLE32)pmod->hMidi; #else
DeviceInfo->DeviceHandle = (HANDLE32)MAKELONG(pmod->hMidi,0); #endif
mmr = midiOpen(DeviceInfo, dwUser, pmod, (DWORD)dwParam2); GlobalFreeDeviceInfo(DeviceInfo); return mmr; } else { MMRRETURN( MMSYSERR_NOMEM ); } }
case MODM_CLOSE: DPF(DL_TRACE|FA_MIDI, ("MODM_CLOSE") ); pOutClient = (LPDEVICEINFO)dwUser;
if( ( (mmr=IsValidDeviceInfo(pOutClient)) != MMSYSERR_NOERROR) || ( (mmr=IsValidDeviceState(pOutClient->DeviceState,FALSE)) != MMSYSERR_NOERROR ) ) { MMRRETURN( mmr ); }
midiOutAllNotesOff( pOutClient ); mmr = wdmaudCloseDev( pOutClient );
if (MMSYSERR_NOERROR == mmr) { //
// Tell the caller we're done
//
midiCallback(pOutClient, MOM_CLOSE, 0L, 0L);
ISVALIDDEVICEINFO(pOutClient); ISVALIDDEVICESTATE(pOutClient->DeviceState,FALSE);
midiCleanUp(pOutClient); }
return mmr;
case MODM_DATA: DPF(DL_TRACE|FA_MIDI, ("MODM_DATA") );
if( ( (mmr=IsValidDeviceInfo((LPDEVICEINFO)dwUser)) != MMSYSERR_NOERROR ) || ( (mmr=IsValidDeviceState(((LPDEVICEINFO)dwUser)->DeviceState,FALSE)) != MMSYSERR_NOERROR ) ) { MMRRETURN( mmr ); } //
// dwParam1 = MIDI event dword (1, 2 or 3 bytes)
//
return midiOutWrite((LPDEVICEINFO)dwUser, (DWORD)dwParam1);
case MODM_LONGDATA: DPF(DL_TRACE|FA_MIDI, ("MODM_LONGDATA") );
pOutClient = (LPDEVICEINFO)dwUser; { LPMIDIHDR lpHdr;
if( ( (mmr=IsValidDeviceInfo(pOutClient)) != MMSYSERR_NOERROR ) || ( (mmr=IsValidDeviceState(pOutClient->DeviceState,FALSE)) != MMSYSERR_NOERROR ) || ( (mmr=IsValidMidiHeader((LPMIDIHDR)dwParam1)) != MMSYSERR_NOERROR) ) { MMRRETURN( mmr ); }
//
// check if it's been prepared
//
lpHdr = (LPMIDIHDR)dwParam1; if (!(lpHdr->dwFlags & MHDR_PREPARED)) { MMRRETURN( MIDIERR_UNPREPARED ); }
// Send the data long....
mmr = wdmaudSubmitMidiOutHeader(pOutClient, lpHdr); //
// The docs say that this call can return an error. Why we didn't
// I don't know. Thus, these lines are getting commented out.
//
// DPFASSERT( mmr == MMSYSERR_NOERROR );
// mmr = MMSYSERR_NOERROR;
// note that clearing the done bit or setting the inqueue bit
// isn't necessary here since this function is synchronous -
// the client will not get control back until it's done.
lpHdr->dwFlags |= MHDR_DONE;
// notify client
//BUGBUG: this is a no-op from the set above?
if (mmr == MMSYSERR_NOERROR) { midiCallback(pOutClient, MOM_DONE, (DWORD_PTR)lpHdr, 0L); }
return mmr; }
case MODM_RESET: DPF(DL_TRACE|FA_MIDI, ("MODM_RESET") );
pOutClient = (LPDEVICEINFO)dwUser;
if( ( (mmr=IsValidDeviceInfo(pOutClient)) != MMSYSERR_NOERROR ) || ( (mmr=IsValidDeviceState(pOutClient->DeviceState,FALSE)) != MMSYSERR_NOERROR ) ) { MMRRETURN( mmr ); }
midiOutAllNotesOff(pOutClient);
return MMSYSERR_NOERROR;
case MODM_SETVOLUME: DPF(DL_TRACE|FA_MIDI, ("MODM_SETVOLUME") );
pOutClient = GlobalAllocDeviceInfo((LPWSTR)dwParam2); if (NULL == pOutClient) { MMRRETURN( MMSYSERR_NOMEM ); }
pOutClient->DeviceType = MidiOutDevice; pOutClient->DeviceNumber = id; pOutClient->OpenDone = 0; PRESETERROR(pOutClient);
mmr = wdmaudIoControl(pOutClient, sizeof(DWORD), (LPBYTE)&dwParam1, IOCTL_WDMAUD_MIDI_OUT_SET_VOLUME); POSTEXTRACTERROR(mmr,pOutClient);
GlobalFreeDeviceInfo(pOutClient); return mmr;
case MODM_GETVOLUME: DPF(DL_TRACE|FA_MIDI, ("MODM_GETVOLUME") );
pOutClient = GlobalAllocDeviceInfo((LPWSTR)dwParam2); if (pOutClient) { pVolume = (LPDWORD) GlobalAllocPtr( GPTR, sizeof(DWORD)); if (pVolume) { pOutClient->DeviceType = MidiOutDevice; pOutClient->DeviceNumber = id; pOutClient->OpenDone = 0; PRESETERROR(pOutClient);
mmr = wdmaudIoControl(pOutClient, sizeof(DWORD), (LPBYTE)pVolume, IOCTL_WDMAUD_MIDI_OUT_GET_VOLUME); POSTEXTRACTERROR(mmr,pOutClient);
//
// Only copy back info on success.
//
if( MMSYSERR_NOERROR == mmr ) *((DWORD FAR *) dwParam1) = *pVolume;
GlobalFreePtr(pVolume); } else { mmr = MMSYSERR_NOMEM; }
GlobalFreeDeviceInfo(pOutClient); } else { mmr = MMSYSERR_NOMEM; }
return mmr;
#ifdef MIDI_STREAM
// TODO: Are we going to support the Midi Streaming
// messages in this rev?
case MODM_PROPERTIES: return modProperty (&gMidiOutClient, (LPBYTE)dwParam1, dwParam2);
case MODM_STRMDATA: return modStreamData (&gMidiOutClient, (LPMIDIHDR)dwParam1, (UINT)dwParam2);
case MODM_GETPOS: return modGetStreamPosition (&gMidiOutClient, (LPMMTIME)dwParam1);
case MODM_STOP: return modStreamReset (&gMidiOutClient);
case MODM_RESTART: return modStreamRestart (&gMidiOutClient, dwParam1, dwParam2);
case MODM_PAUSE: return modStreamPause (&gMidiOutClient);
#endif // MIDI_STREAM support
#ifdef MIDI_THRU
case DRVM_ADD_THRU: case DRVM_REMOVE_THRU: // TODO: How do a support thruing in the kernel if I
// only get a device handle from this message.
#endif // MIDI_THRU support
default: MMRRETURN( MMSYSERR_NOTSUPPORTED ); }
//
// Should not get here
//
DPFASSERT(0); MMRRETURN( MMSYSERR_NOTSUPPORTED ); }
/****************************************************************************
* @doc INTERNAL * * @api DWORD | midiOutWrite | Synchronously process a midi output * buffer. * * @rdesc A MMSYS... type return code for the application. ***************************************************************************/ MMRESULT FAR midiOutWrite ( LPDEVICEINFO pClient, DWORD ulEvent ) { MMRESULT mmr = MMSYSERR_ERROR; BYTE bStatus; BYTE bNote; BYTE bVelocity; UINT uChannel; DWORD idx; LPBYTE lpEntry;
bStatus = (BYTE)(ulEvent & 0xFF);
if (!IS_STATUS( bStatus )) { bNote = bStatus; bVelocity = (BYTE)(( ulEvent >> 8 ) & 0x0FF );
bStatus = pClient->DeviceState->bMidiStatus; } else { bNote = (BYTE)(( ulEvent >> 8 ) & 0xFF ); bVelocity = (BYTE)(( ulEvent >> 16 ) & 0xFF );
pClient->DeviceState->bMidiStatus = bStatus; }
uChannel = MIDI_CHANNEL( bStatus ); bStatus = MIDI_STATUS( bStatus );
if (MIDI_NOTEON == bStatus || MIDI_NOTEOFF == bStatus) { idx = ( uChannel << 7 ) | bNote; lpEntry = &pClient->DeviceState->lpNoteOnMap[idx];
if (( 0 == bVelocity ) || ( MIDI_NOTEOFF == bStatus )) { if (*lpEntry) { --*lpEntry; } } else { if (*lpEntry < 255) { ++*lpEntry; } } } //
// Send the MIDI short message
//
mmr = wdmaudIoControl(pClient, 0, // DataBuffer contains a value and not a pointer
// so we don't need a size.
#ifdef UNDER_NT
UlongToPtr(ulEvent), #else
&ulEvent, #endif
IOCTL_WDMAUD_MIDI_OUT_WRITE_DATA); return mmr; }
/****************************************************************************
* @doc INTERNAL * * @api VOID | midiOutAllNotesOff | Turn off all the notes on this client, * using the note on map that has been built from outgoing short messages. * ***************************************************************************/ VOID FAR midiOutAllNotesOff ( LPDEVICEINFO pClient ) { UINT uNote; UINT uChannel; LPBYTE lpNoteOnMap; LPBYTE lpNoteOnMapEnd; DWORD dwMessage; UINT uNoteOffs = 0;
// First turn off the sustain controller on all channels to terminate
// post-note off sound
//
for (uChannel = 0; uChannel < MIDI_CHANNELS; uChannel++) { dwMessage = MIDI_SUSTAIN( 0, uChannel );
// WorkItem: shouldn't we check the return value here?
wdmaudIoControl(pClient, 0, // DataBuffer contains a value and not a pointer
// so we don't need a size.
#ifdef UNDER_NT
UlongToPtr(dwMessage), #else
&dwMessage, #endif
IOCTL_WDMAUD_MIDI_OUT_WRITE_DATA); }
// Iterate through the map and track what note and channel each entry corresponds
// to
//
lpNoteOnMap = pClient->DeviceState->lpNoteOnMap; lpNoteOnMapEnd = lpNoteOnMap + MIDI_NOTE_MAP_SIZE; uNote = 0; uChannel = 0;
for ( ; lpNoteOnMap < lpNoteOnMapEnd; lpNoteOnMap++ ) { BYTE bCount = *lpNoteOnMap;
if (bCount) {
// This note on this channel has some instances playing. Build a note off
// and shut them down
//
*lpNoteOnMap = 0; dwMessage = MIDI_NOTE_OFF( uNote, uChannel );
while (bCount--) { wdmaudIoControl(pClient, 0, // DataBuffer contains a value and not a pointer
// so we don't need a size.
#ifdef UNDER_NT
UlongToPtr(dwMessage), #else
&dwMessage, #endif
IOCTL_WDMAUD_MIDI_OUT_WRITE_DATA);
uNoteOffs++; } }
if (++uNote >= MIDI_NOTES) { uNote = 0; ++uChannel; } } }
|