|
|
/*
DirectMusic Software Synthesizer Miniport
Copyright (c) 1996-2000 Microsoft Corporation. All rights reserved. */
#include "common.h"
#include "private.h"
#include "fltsafe.h"
#include <math.h>
#define STR_MODULENAME "DDKSynth.sys:Miniport: "
/* NYI:
more sample rates? */ #ifdef USE_OBSOLETE_FUNCS
VOID PutMessageWorker(PVOID Param); #endif
// Property handler
//
NTSTATUS PropertyHandler_Support(IN PPCPROPERTY_REQUEST); NTSTATUS PropertyHandler_Effects(IN PPCPROPERTY_REQUEST); NTSTATUS PropertyHandler_Synth(IN PPCPROPERTY_REQUEST); NTSTATUS PropertyHandler_SynthCaps(IN PPCPROPERTY_REQUEST); NTSTATUS PropertyHandler_SynthDls(IN PPCPROPERTY_REQUEST);
// Misc.
NTSTATUS DefaultSynthBasicPropertyHandler(IN PPCPROPERTY_REQUEST pRequest); NTSTATUS DefaultBasicPropertyHandler(IN PPCPROPERTY_REQUEST pRequest, IN DWORD dwSupportVerb); NTSTATUS ValidatePropertyParams(IN PPCPROPERTY_REQUEST pRequest, IN ULONG cbSize, IN DWORD dwExcludeVerb);
/*****************************************************************************
* PinDataRangesStream[] ***************************************************************************** * Structures indicating range of valid format values for streaming pins. * If your device can also support legacy MIDI, include a second data range * here that supports KSDATAFORMAT_SUBTYPE_MIDI. */ static const KSDATARANGE_MUSIC PinDataRangesStream[] = { { { sizeof(KSDATARANGE_MUSIC), 0, 0, 0, STATICGUIDOF(KSDATAFORMAT_TYPE_MUSIC), STATICGUIDOF(KSDATAFORMAT_SUBTYPE_DIRECTMUSIC), STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) }, STATICGUIDOF(KSMUSIC_TECHNOLOGY_WAVETABLE), 0, // Channels
0, // Notes
0x0000ffff // ChannelMask
} };
/*****************************************************************************
* PinDataRangePointersStream[] ***************************************************************************** * List of pointers to structures indicating range of valid format values * for streaming pins. */ static const PKSDATARANGE PinDataRangePointersStream[] = { PKSDATARANGE(&PinDataRangesStream[0]) };
/*****************************************************************************
* PinDataRangesAudio[] ***************************************************************************** * Structures indicating range of valid format values for audio pins. * * Do not include this if you are building a hardware device that does not * output audio back into the system. */ static const KSDATARANGE_AUDIO PinDataRangesAudio[] = { { { sizeof(KSDATARANGE_AUDIO), 0, 0, 0, STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) }, 2, 16, 16, 22050, 22050 } };
/*****************************************************************************
* PinDataRangePointersAudio[] ***************************************************************************** * List of pointers to structures indicating range of valid format values * for audio pins. * * Do not include this if you are building a hardware device that does not * output audio back into the system. */ static const PKSDATARANGE PinDataRangePointersAudio[] = { PKSDATARANGE(&PinDataRangesAudio[0]) };
/*****************************************************************************
* SynthProperties[] ***************************************************************************** * Array of properties supported. */ static const PCPROPERTY_ITEM SynthProperties[] = { ///////////////////////////////////////////////////////////////////
// Support items
{ &GUID_DMUS_PROP_GM_Hardware, 0, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_Support }, { &GUID_DMUS_PROP_GS_Hardware, 0, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_Support }, { &GUID_DMUS_PROP_XG_Hardware, 0, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_Support }, { &GUID_DMUS_PROP_XG_Capable, 0, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_Support }, { &GUID_DMUS_PROP_GS_Capable, 0, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_Support }, { &GUID_DMUS_PROP_DLS1, 0, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_Support }, { &GUID_DMUS_PROP_Effects, 0, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_Effects },
///////////////////////////////////////////////////////////////////
// Configuration items
// Global: Synth caps
{ &KSPROPSETID_Synth, KSPROPERTY_SYNTH_CAPS, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_SynthCaps }, // Per Stream: Synth port parameters
{ &KSPROPSETID_Synth, KSPROPERTY_SYNTH_PORTPARAMETERS, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_Synth }, // Per Stream: Volume
{ &KSPROPSETID_Synth, KSPROPERTY_SYNTH_VOLUME, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_Synth }, // Per Stream: Volume boost value
{ &KSPROPSETID_Synth, KSPROPERTY_SYNTH_VOLUMEBOOST, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_Synth }, // Per Stream: Channel groups
{ &KSPROPSETID_Synth, KSPROPERTY_SYNTH_CHANNELGROUPS, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_Synth }, // Per stream: Voice priority
{ &KSPROPSETID_Synth, KSPROPERTY_SYNTH_VOICEPRIORITY, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_Synth }, // Per Stream: Running Stats
{ &KSPROPSETID_Synth, KSPROPERTY_SYNTH_RUNNINGSTATS, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_Synth },
///////////////////////////////////////////////////////////////////
// Clock items
// Per stream: Get current latency time
{ &KSPROPSETID_Synth, KSPROPERTY_SYNTH_LATENCYCLOCK, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_Synth },
///////////////////////////////////////////////////////////////////
// DLS items
// Per stream: Download DLS sample
{ &KSPROPSETID_Synth_Dls, KSPROPERTY_SYNTH_DLS_DOWNLOAD, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_SynthDls }, // Per stream: Unload DLS sample
{ &KSPROPSETID_Synth_Dls, KSPROPERTY_SYNTH_DLS_UNLOAD, KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_SynthDls }, // Per stream: append
{ &KSPROPSETID_Synth_Dls, KSPROPERTY_SYNTH_DLS_APPEND, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_SynthDls }, // Per stream: format
{ &KSPROPSETID_Synth_Dls, KSPROPERTY_SYNTH_DLS_WAVEFORMAT, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_SynthDls } };
DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSynth, SynthProperties);
/*****************************************************************************
* MiniportPins[] ***************************************************************************** * List of pins. Do not expose a wave pin if you are writing a driver for a * hardware device that does not inject wave data back into the system. */ static const PCPIN_DESCRIPTOR MiniportPins[] = { { 1,1,1, // InstanceCount
NULL, { // KsPinDescriptor
0, // InterfacesCount
NULL, // Interfaces
0, // MediumsCount
NULL, // Mediums
SIZEOF_ARRAY(PinDataRangePointersStream), // DataRangesCount
PinDataRangePointersStream, // DataRanges
KSPIN_DATAFLOW_IN, // DataFlow
KSPIN_COMMUNICATION_SINK, // Communication
&KSCATEGORY_WDMAUD_USE_PIN_NAME, // Category
&KSNODETYPE_DMDDKSYNTH, // Name
0 // Reserved
} }, { 1,1,1, // InstanceCount
NULL, // AutomationTable
{ // KsPinDescriptor
0, // InterfacesCount
NULL, // Interfaces
0, // MediumsCount
NULL, // Mediums
SIZEOF_ARRAY(PinDataRangePointersAudio), // DataRangesCount
PinDataRangePointersAudio, // DataRanges
KSPIN_DATAFLOW_OUT, // DataFlow
KSPIN_COMMUNICATION_SOURCE, // Communication
&KSCATEGORY_AUDIO, // Category
NULL, // Name
0 // Reserved
} } };
/*****************************************************************************
* MiniportNodes[] ***************************************************************************** * List of nodes */ static const PCNODE_DESCRIPTOR MiniportNodes[] = { { 0, &AutomationSynth, &KSNODETYPE_SYNTHESIZER, &KSNODETYPE_DMSYNTH} };
/*****************************************************************************
* MiniportConnections[] ***************************************************************************** * List of connections. */ static const PCCONNECTION_DESCRIPTOR MiniportConnections[] = { // From node From pin To node To pin
//
{ PCFILTER_NODE, 0, 0, 1}, // Stream in to synth.
{ 0, 0, PCFILTER_NODE, 1} // Synth to audio out
};
/*****************************************************************************
* TopologyCategories[] ***************************************************************************** * List of categories. If your driver runs a hardware device that performs * actual audio output (i.e. contains a DAC) and does not register a physical * connection to a topology miniport, then you can use KSCATEGORY_RENDER instead * of KSCATEGORY_DATATRANSFORM. * * Note that if you use CATEGORY_RENDER instead of CATEGORY_DATATRANSFORM, * you must list _AUDIO category before _RENDER, so that SysAudio and DMusic.DLL * will agree upon what to label your device (DMusic currently expects _AUDIO). * */ static const GUID TopologyCategories[] = { STATICGUIDOF(KSCATEGORY_DATATRANSFORM), STATICGUIDOF(KSCATEGORY_AUDIO), STATICGUIDOF(KSCATEGORY_SYNTHESIZER) };
/*****************************************************************************
* MiniportFilterDescriptor ***************************************************************************** * Complete miniport description. */ static const PCFILTER_DESCRIPTOR MiniportFilterDescriptor = { 0, // Version
NULL, // AutomationTable
sizeof(PCPIN_DESCRIPTOR), // PinSize
SIZEOF_ARRAY(MiniportPins), // PinCount
MiniportPins, // Pins
sizeof(PCNODE_DESCRIPTOR), // NodeSize
SIZEOF_ARRAY(MiniportNodes), // NodeCount
MiniportNodes, // Nodes
SIZEOF_ARRAY(MiniportConnections), // ConnectionCount
MiniportConnections, // Connections
SIZEOF_ARRAY(TopologyCategories), // CategoryCount
TopologyCategories, // Categories
};
#pragma code_seg()
/*****************************************************************************
* MapHRESULT() ***************************************************************************** * Maps DMusic HRESULT to NTSTATUS */ NTSTATUS MapHRESULT(IN HRESULT hr) { PAGED_CODE();
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; // NYI: map hr to ntStatus
return ntStatus; }
/*****************************************************************************
* CreateMiniportDmSynth() ***************************************************************************** * Creates a DMus_Synth miniport driver for the adapter. * This uses a macro from STDUNK.H to do all the work. */ NTSTATUS CreateMiniportDmSynth #ifdef USE_OBSOLETE_FUNCS
( OUT PUNKNOWN * Unknown, IN PUNKNOWN UnknownOuter OPTIONAL, IN POOL_TYPE PoolType ) #else
( OUT PUNKNOWN * Unknown, IN PUNKNOWN UnknownOuter OPTIONAL, IN POOL_TYPE PoolType, IN PDEVICE_OBJECT pDeviceObject ) #endif
{ PAGED_CODE();
_DbgPrintF(DEBUGLVL_VERBOSE, ("CreateMiniportDmSynth")); ASSERT(Unknown);
#ifdef USE_OBSOLETE_FUNCS
STD_CREATE_BODY_WITH_TAG(CMiniportDmSynth, Unknown, UnknownOuter, PoolType,'pMmD'); // DmMp
#else
NTSTATUS ntStatus; CMiniportDmSynth *p = new(PoolType, 'pMmD') CMiniportDmSynth(UnknownOuter); if(p != NULL) { // the following line is the only difference between this code and
// STD_CREATE_BODY_WITH_TAG and is necessary to handle passing the
// DeviceObject pointer down without changing PortCls.
p->m_pDeviceObject = pDeviceObject;
*Unknown = reinterpret_cast<PUNKNOWN>(p); (*Unknown)->AddRef(); ntStatus = STATUS_SUCCESS; } else { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } return ntStatus; #endif
}
/*****************************************************************************
* CMiniportDmSynth::NonDelegatingQueryInterface() ***************************************************************************** * Obtains an interface. This method works just like a COM QueryInterface * call and is used if the object is not being aggregated. */ STDMETHODIMP CMiniportDmSynth::NonDelegatingQueryInterface(IN REFIID Interface, OUT PVOID * Object) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_VERBOSE, ("CMiniportDmSynth::NonDelegatingQueryInterface")); ASSERT(Object);
if (IsEqualGUIDAligned(Interface, IID_IUnknown)) { *Object = PVOID(PUNKNOWN(this)); } else if (IsEqualGUIDAligned(Interface, IID_IMiniport)) { *Object = PVOID(PMINIPORT(this)); } else if (IsEqualGUIDAligned(Interface, IID_IMiniportDMus)) { *Object = PVOID(PMINIPORTDMUS(this)); } else { *Object = NULL; }
if (*Object) { PUNKNOWN(*Object)->AddRef(); return STATUS_SUCCESS; }
return STATUS_INVALID_PARAMETER; }
/*****************************************************************************
* CMiniportDmSynth::~CMiniportDmSynth() ***************************************************************************** * Destructor for miniport object. Let go of the port reference. */ CMiniportDmSynth::~CMiniportDmSynth() { PAGED_CODE();
_DbgPrintF(DEBUGLVL_VERBOSE, ("CMiniportDmSynth::~CMiniportDmSynth"));
if (m_pPort) { m_pPort->Release(); m_pPort = NULL; }
DeleteCriticalSection(&m_CriticalSection); }
/*****************************************************************************
* CMiniportDmSynth::GetDescription() ***************************************************************************** * Gets the topology for this miniport. */ STDMETHODIMP CMiniportDmSynth::GetDescription(OUT PPCFILTER_DESCRIPTOR * OutFilterDescriptor) { PAGED_CODE();
ASSERT(OutFilterDescriptor);
_DbgPrintF(DEBUGLVL_VERBOSE, ("CMiniportDmSynth::GetDescription"));
*OutFilterDescriptor = PPCFILTER_DESCRIPTOR(&MiniportFilterDescriptor); return STATUS_SUCCESS; }
/*****************************************************************************
* CMiniportDmSynth::DataRangeIntersection() ***************************************************************************** * No data range for this miniport. */ STDMETHODIMP CMiniportDmSynth::DataRangeIntersection(IN ULONG PinId, IN PKSDATARANGE DataRange, IN PKSDATARANGE MatchingDataRange, IN ULONG OutputBufferLength, OUT PVOID ResultantFormat OPTIONAL, OUT PULONG ResultantFormatLength) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_VERBOSE, ("CMiniportDmSynth::DataRangeIntersection"));
return STATUS_NOT_IMPLEMENTED; }
/*****************************************************************************
* CMiniportDmSynth::Init() ***************************************************************************** * Initializes the miniport. */ STDMETHODIMP CMiniportDmSynth::Init ( IN PUNKNOWN Unknown OPTIONAL, IN PRESOURCELIST ResourceList, IN PPORTDMUS Port, OUT PSERVICEGROUP* ServiceGroup ) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_VERBOSE, ("CMiniportDmSynth::Init")); ASSERT(ResourceList); ASSERT(Port); ASSERT(ServiceGroup);
m_pPort = Port; m_pPort->AddRef();
*ServiceGroup = NULL;
InitializeCriticalSection(&m_CriticalSection);
return STATUS_SUCCESS; }
/*****************************************************************************
* CMiniportDmSynth::Service() ***************************************************************************** * Not used. */ STDMETHODIMP_(void) CMiniportDmSynth::Service() { }
/*****************************************************************************
* CMiniportDmSynth::NewStream() ***************************************************************************** * Create a new stream. SchedulePreFetch tells the sequencer how far in * advance to deliver events. Allocator and master clock are required. */ STDMETHODIMP CMiniportDmSynth::NewStream ( OUT PMXF * MXF, IN PUNKNOWN OuterUnknown OPTIONAL, IN POOL_TYPE PoolType, IN ULONG PinID, IN DMUS_STREAM_TYPE StreamType, IN PKSDATAFORMAT DataFormat, OUT PSERVICEGROUP * ServiceGroup, IN PAllocatorMXF AllocatorMXF, IN PMASTERCLOCK MasterClock, OUT PULONGLONG SchedulePreFetch ) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_VERBOSE, ("CMiniportDmSynth::NewStream"));
NTSTATUS ntStatus = STATUS_SUCCESS;
*MXF = NULL; *ServiceGroup = NULL; *SchedulePreFetch = DONT_HOLD_FOR_SEQUENCING;
if ((StreamType != DMUS_STREAM_WAVE_SINK) && (StreamType != DMUS_STREAM_MIDI_RENDER) ) { _DbgPrintF(DEBUGLVL_TERSE, ("CMiniportDmSynth::NewStream stream type not supported")); ntStatus = STATUS_INVALID_DEVICE_REQUEST; } else { EnterCriticalSection(&m_CriticalSection);
for (CDmSynthStream* pStreamItem = (CDmSynthStream*)m_StreamList.GetHead(); pStreamItem; pStreamItem = (CDmSynthStream*)pStreamItem->GetNext()) { if ( (StreamType == DMUS_STREAM_WAVE_SINK && !pStreamItem->m_fWaveOutCreated) || (StreamType == DMUS_STREAM_MIDI_RENDER && !pStreamItem->m_fMidiInCreated) ) { if (StreamType == DMUS_STREAM_MIDI_RENDER) { ntStatus = pStreamItem->InitMidiIn(AllocatorMXF, MasterClock); } else // DMUS_STREAM_WAVE_SINK
{ ntStatus = pStreamItem->InitWaveOut(DataFormat); }
if (NT_SUCCESS(ntStatus)) { pStreamItem->AddRef(); *MXF = PMXF(pStreamItem); } break; } }
if (!*MXF) { CDmSynthStream* pNewStream = new(PoolType,'sSmD') CDmSynthStream(OuterUnknown); // DmSs
if (pNewStream) { #ifdef USE_OBSOLETE_FUNCS
ntStatus = pNewStream->Init(this); #else
ntStatus = pNewStream->Init(this, m_pDeviceObject); #endif
if (NT_SUCCESS(ntStatus)) { if (StreamType == DMUS_STREAM_MIDI_RENDER) { ntStatus = pNewStream->InitMidiIn(AllocatorMXF, MasterClock); } else // DMUS_STREAM_WAVE_SINK
{ ntStatus = pNewStream->InitWaveOut(DataFormat); } }
if (NT_SUCCESS(ntStatus)) { m_StreamList.AddTail(pNewStream); pNewStream->AddRef(); *MXF = PMXF(pNewStream); } else { pNewStream->Release(); pNewStream = NULL; } } else { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } }
LeaveCriticalSection(&m_CriticalSection); }
return ntStatus; }
/*****************************************************************************
***************************************************************************** * CDmSynthStream implementation ***************************************************************************** *****************************************************************************/
/*****************************************************************************
* CDmSynthStream::~CDmSynthStream() ***************************************************************************** * Destructor for miniport stream (MXF). Remove the download objects, clock, * allocator, miniport, synth, etc. * * All instruments and waves downloaded to * the synth are released (though a well behaved * client should have unloaded them prior to now). */ CDmSynthStream::~CDmSynthStream() { PAGED_CODE();
_DbgPrintF(DEBUGLVL_VERBOSE, ("CDmSynthStream::~CDmSynthStream"));
if (m_pMasterClock) { m_pMasterClock->Release(); m_pMasterClock = NULL; }
if (m_pAllocator) { m_pAllocator->Release(); m_pAllocator = NULL; } #ifndef USE_OBSOLETE_FUNCS
if(m_pEventListWorkItem != NULL) { IoFreeWorkItem(m_pEventListWorkItem); } #endif
if (m_pMiniport) { EnterCriticalSection(&m_pMiniport->m_CriticalSection);
for (CDmSynthStream* pStreamItem = (CDmSynthStream*)m_pMiniport->m_StreamList.GetHead(); pStreamItem; pStreamItem = (CDmSynthStream*)pStreamItem->GetNext()) { if (pStreamItem == this) { m_pMiniport->m_StreamList.Remove(pStreamItem); break; } } LeaveCriticalSection(&m_pMiniport->m_CriticalSection);
m_pMiniport->Release(); }
if (m_pSynth) { delete m_pSynth; } }
/*****************************************************************************
* CDmSynthStream::Init() ***************************************************************************** * Initialize the miniport stream (MXF). Create a synth. */ NTSTATUS CDmSynthStream::Init #ifdef USE_OBSOLETE_FUNCS
( CMiniportDmSynth * Miniport ) #else
( CMiniportDmSynth * Miniport, PDEVICE_OBJECT pDeviceObject ) #endif
{ PAGED_CODE();
_DbgPrintF(DEBUGLVL_VERBOSE, ("CDmSynthStream::Init"));
if (!Miniport) { return STATUS_INVALID_PARAMETER; }
m_pSynth = new(NonPagedPool,'SSmD') CSynth; // DmSS
if (m_pSynth == NULL) { return STATUS_INSUFFICIENT_RESOURCES; }
// Initialize Synth with default settings.
//
if (FAILED(m_pSynth->Open(DEFAULT_CHANNEL_GROUPS, DEFAULT_VOICES))) { delete m_pSynth; m_pSynth = NULL; return STATUS_INSUFFICIENT_RESOURCES; }
m_PortParams.ChannelGroups = DEFAULT_CHANNEL_GROUPS; m_PortParams.Voices = DEFAULT_VOICES; m_PortParams.EffectsFlags = 0;
m_pMiniport = Miniport; m_pMiniport->AddRef();
m_fWaveOutCreated = FALSE; m_fMidiInCreated = FALSE;
m_State = KSSTATE_STOP;
m_EventList = NULL; KeInitializeSpinLock(&m_EventListLock);
#ifdef USE_OBSOLETE_FUNCS
ExInitializeWorkItem(&m_EventListWorkItem, (PWORKER_THREAD_ROUTINE)PutMessageWorker, (PVOID)this); #else
m_pEventListWorkItem = IoAllocateWorkItem(pDeviceObject); if(m_pEventListWorkItem == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } #endif
return STATUS_SUCCESS; }
/*****************************************************************************
* CDmSynthStream::InitMidiIn() ***************************************************************************** * Initialize the MIDI input side. Allocator and master clock are required. */ NTSTATUS CDmSynthStream::InitMidiIn ( IN PAllocatorMXF AllocatorMXF, IN PMASTERCLOCK MasterClock ) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_VERBOSE, ("CDmSynthStream::Init"));
if (!AllocatorMXF || !MasterClock) { return STATUS_INVALID_PARAMETER; }
m_pAllocator = AllocatorMXF; m_pAllocator->AddRef();
// NOTE: master clock is set on midi pin, not wave pin
m_pMasterClock = MasterClock; m_pMasterClock->AddRef();
m_fMidiInCreated = TRUE;
return STATUS_SUCCESS; }
/*****************************************************************************
* CDmSynthStream::InitWaveOut() ***************************************************************************** * Initialize the wave output side. */ NTSTATUS CDmSynthStream::InitWaveOut ( IN PKSDATAFORMAT DataFormat ) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_VERBOSE, ("CDmSynthStream::Init"));
if (!DataFormat) { return STATUS_INVALID_PARAMETER; }
RtlZeroMemory(&m_PortParams, sizeof(m_PortParams)); m_PortParams.SampleRate = PKSDATAFORMAT_WAVEFORMATEX(DataFormat)->WaveFormatEx.nSamplesPerSec; m_PortParams.AudioChannels = PKSDATAFORMAT_WAVEFORMATEX(DataFormat)->WaveFormatEx.nChannels;
m_lVolume = 0; m_lBoost = 6 * 100;
m_fWaveOutCreated = TRUE;
return STATUS_SUCCESS; }
/*****************************************************************************
* CDmSynthStream::NonDelegatingQueryInterface() ***************************************************************************** * Obtains an interface. This method works just like a COM QueryInterface * call and is used if the object is not being aggregated. */ STDMETHODIMP CDmSynthStream::NonDelegatingQueryInterface ( IN REFIID Interface, OUT PVOID* Object ) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_VERBOSE, ("CDmSynthStream::NonDelegatingQueryInterface")); ASSERT(Object);
if (IsEqualGUIDAligned(Interface, IID_IUnknown)) { *Object = PVOID(PUNKNOWN(this)); } else if (IsEqualGUIDAligned(Interface, IID_IMXF)) { *Object = PVOID(PMXF(this)); } else if (IsEqualGUIDAligned(Interface, IID_ISynthSinkDMus)) { *Object = PVOID(PSYNTHSINKDMUS(this)); } else { *Object = NULL; }
if (*Object) { //
// We reference the interface for the caller.
//
PUNKNOWN(*Object)->AddRef(); return STATUS_SUCCESS; } return STATUS_INVALID_PARAMETER; }
/*****************************************************************************
* CDmSynthStream::SetState() ***************************************************************************** * Set the state of the stream (RUN/PAUSE/ACQUIRE/STOP) and act accordingly. * Activate the synth if we are running. */ STDMETHODIMP CDmSynthStream::SetState ( IN KSSTATE NewState ) { _DbgPrintF(DEBUGLVL_VERBOSE, ("CDmSynthStream::SetState: %d", NewState));
NTSTATUS ntStatus = STATUS_SUCCESS;
m_llStartPosition = 0; m_llLastPosition = 0;
switch (NewState) { case KSSTATE_RUN: { if (m_PortParams.SampleRate && m_PortParams.AudioChannels) { if (NT_SUCCESS(ntStatus)) { HRESULT hr = m_pSynth->Activate(m_PortParams.SampleRate, m_PortParams.AudioChannels); if (FAILED(hr)) { ntStatus = MapHRESULT(hr); } } } else { _DbgPrintF(DEBUGLVL_TERSE, ("CDmSynthStream::SetState invalid port params")); ntStatus = STATUS_UNSUCCESSFUL; } break; } case KSSTATE_ACQUIRE: case KSSTATE_STOP: case KSSTATE_PAUSE: { HRESULT hr = m_pSynth->Deactivate(); if (FAILED(hr)) { ntStatus = MapHRESULT(hr); } break; } } m_State = NewState;
return ntStatus; }
/*****************************************************************************
* CDmSynthStream::ConnectOutput() ***************************************************************************** * MXF base function. This MXF does not feed another, so it is not implemented. */ STDMETHODIMP CDmSynthStream::ConnectOutput(PMXF ConnectionPoint) { _DbgPrintF(DEBUGLVL_VERBOSE, ("CDmSynthStream::ConnectOutput"));
return STATUS_SUCCESS; // do nothing
}
/*****************************************************************************
* CDmSynthStream::DisconnectOutput() ***************************************************************************** * MXF base function. This MXF does not feed another, so it is not implemented. */ STDMETHODIMP CDmSynthStream::DisconnectOutput(PMXF ConnectionPoint) { _DbgPrintF(DEBUGLVL_VERBOSE, ("CDmSynthStream::DisconnectOutput"));
return STATUS_SUCCESS; // do nothing
}
/*****************************************************************************
* PutMessageWorker() ***************************************************************************** * C function that thunks over to the stream's member function. */ #ifdef USE_OBSOLETE_FUNCS
VOID PutMessageWorker(PVOID Param) #else
VOID PutMessageWorker(PDEVICE_OBJECT pDeviceObject, PVOID Param) #endif
{ CDmSynthStream *pCDmSynthStream = (CDmSynthStream *)Param;
pCDmSynthStream->PutMessageInternal(); }
/*****************************************************************************
* CDmSynthStream::PutMessageInternal() ***************************************************************************** * Can be called at PASSIVE_LEVEL. Receive MIDI events and queue them. */ void CDmSynthStream::PutMessageInternal(void) { KIRQL oldIrql; PDMUS_KERNEL_EVENT pEvent,pDMKEvt; NTSTATUS ntStatus;
_DbgPrintF(DEBUGLVL_VERBOSE, ("CDmSynthStream::PutMessageInternal"));
// Grab everything on the list
KeAcquireSpinLock(&m_EventListLock,&oldIrql); pEvent=m_EventList; m_EventList=NULL; KeReleaseSpinLock(&m_EventListLock,oldIrql);
pDMKEvt=pEvent; while (pDMKEvt) { if (!(PACKAGE_EVT(pDMKEvt))) { PBYTE pData; if (pDMKEvt->cbEvent <= sizeof(PBYTE)) { pData = (PBYTE)&pDMKEvt->uData; } else { pData = (PBYTE)pDMKEvt->uData.pbData; }
// This is just MIDI bytes
HRESULT hr = m_pSynth->PlayBuffer(PSYNTHSINKDMUS(this), pDMKEvt->ullPresTime100ns, pData, pDMKEvt->cbEvent, (ULONG)pDMKEvt->usChannelGroup); if (FAILED(hr)) { ntStatus = MapHRESULT(hr); } } else { PutMessage(pDMKEvt->uData.pPackageEvt); pDMKEvt->uData.pPackageEvt = NULL; } pDMKEvt = pDMKEvt->pNextEvt; } m_pAllocator->PutMessage(pEvent); }
/*****************************************************************************
* CDmSynthStream::PutMessage() ***************************************************************************** * Must be called at DISPATH_LEVEL (e.g. from a DPC). We jam an event into * a queue and call a work item. If the queue already exists, we just append * (no need to call the work item). */ STDMETHODIMP CDmSynthStream::PutMessage(IN PDMUS_KERNEL_EVENT pEvent) { BOOL bQueueWorkItem;
_DbgPrintF(DEBUGLVL_VERBOSE, ("CDmSynthStream::::PutMessage"));
// Queue up on event list
KeAcquireSpinLockAtDpcLevel(&m_EventListLock);
if (!m_EventList) // If nothing on event list
{ m_EventList = pEvent; // Link to head
bQueueWorkItem = TRUE; // Need to queue work item
} else // Something already pending, queue up behind them
{ // Find last event in queue to link to
PDMUS_KERNEL_EVENT pEventTail = m_EventList; while (pEventTail->pNextEvt) { pEventTail = pEventTail->pNextEvt; } pEventTail->pNextEvt = pEvent; bQueueWorkItem = FALSE; // No need to queue new work item
} KeReleaseSpinLockFromDpcLevel(&m_EventListLock);
// Queue up the work item after we release spinlock
if (bQueueWorkItem) { #ifdef USE_OBSOLETE_FUNCS
ExQueueWorkItem(&m_EventListWorkItem, CriticalWorkQueue); #else
IoQueueWorkItem(m_pEventListWorkItem, PutMessageWorker, CriticalWorkQueue, (PVOID)this); #endif
} return STATUS_SUCCESS; }
/*****************************************************************************
* CDmSynthStream::HandlePropertySupport() ***************************************************************************** * Handle the support property. */ NTSTATUS CDmSynthStream::HandlePropertySupport(IN PPCPROPERTY_REQUEST pRequest) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB, ("CDmSynthStream::HandlePropertySupport"));
NTSTATUS ntStatus;
if (pRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) { ntStatus = DefaultBasicPropertyHandler(pRequest, KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET); } else { ntStatus = ValidatePropertyParams(pRequest, sizeof(ULONG), KSPROPERTY_TYPE_SET); if (NT_SUCCESS(ntStatus)) { if (IsEqualGUIDAligned(*pRequest->PropertyItem->Set, GUID_DMUS_PROP_GM_Hardware)) { _DbgPrintF(DEBUGLVL_BLAB,("CDmSynthStream::HandlePropertySupport GUID_DMUS_PROP_GM_Hardware"));
*(PULONG)(pRequest->Value) = FALSE; } else if (IsEqualGUIDAligned(*pRequest->PropertyItem->Set, GUID_DMUS_PROP_GS_Hardware)) { _DbgPrintF(DEBUGLVL_BLAB,("CDmSynthStream::HandlePropertySupport GUID_DMUS_PROP_GS_Hardware"));
*(PULONG)(pRequest->Value) = FALSE; } else if (IsEqualGUIDAligned(*pRequest->PropertyItem->Set, GUID_DMUS_PROP_XG_Hardware)) { _DbgPrintF(DEBUGLVL_BLAB,("CDmSynthStream::HandlePropertySupport GUID_DMUS_PROP_XG_Hardware"));
*(PULONG)(pRequest->Value) = FALSE; } else if (IsEqualGUIDAligned(*pRequest->PropertyItem->Set, GUID_DMUS_PROP_XG_Capable)) { _DbgPrintF(DEBUGLVL_BLAB,("CDmSynthStream::HandlePropertySupport GUID_DMUS_PROP_XG_Capable"));
*(PULONG)(pRequest->Value) = TRUE; } else if (IsEqualGUIDAligned(*pRequest->PropertyItem->Set, GUID_DMUS_PROP_GS_Capable)) { _DbgPrintF(DEBUGLVL_BLAB,("CDmSynthStream::HandlePropertySupport GUID_DMUS_PROP_GS_Capable"));
*(PULONG)(pRequest->Value) = TRUE; } else if (IsEqualGUIDAligned(*pRequest->PropertyItem->Set, GUID_DMUS_PROP_DLS1)) { _DbgPrintF(DEBUGLVL_BLAB,("CDmSynthStream::HandlePropertySupport GUID_DMUS_PROP_DLS1"));
*(PULONG)(pRequest->Value) = TRUE; } else { _DbgPrintF(DEBUGLVL_TERSE,("CDmSynthStream::HandlePropertySupport unrecognized set ID"));
ntStatus = STATUS_UNSUCCESSFUL; pRequest->ValueSize = 0; } }
if (NT_SUCCESS(ntStatus)) { pRequest->ValueSize = sizeof(ULONG); } }
return ntStatus; }
/*****************************************************************************
* CDmSynthStream::HandlePropertyEffects() ***************************************************************************** * Handle the effects property. */ NTSTATUS CDmSynthStream::HandlePropertyEffects(IN PPCPROPERTY_REQUEST pRequest) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB, ("CDmSynthStream::HandlePropertyEffects"));
NTSTATUS ntStatus;
if (pRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) { ntStatus = DefaultBasicPropertyHandler(pRequest, KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET); } else { ntStatus = ValidatePropertyParams(pRequest, sizeof(ULONG), 0); if (NT_SUCCESS(ntStatus)) { if (pRequest->Verb & KSPROPERTY_TYPE_GET) { PULONG pulEffects = (PULONG)pRequest->Value;
pRequest->ValueSize = sizeof(ULONG); *pulEffects = 0;
_DbgPrintF(DEBUGLVL_TERSE, ("CDmSynthStream: Get effects flags %x", *pulEffects)); } else { pRequest->ValueSize = 0; ntStatus = STATUS_INVALID_PARAMETER; } } }
return ntStatus; }
/*****************************************************************************
* CDmSynthStream::HandlePortParams() ***************************************************************************** * Handle the port parameters property. * Fix up the port params to include defaults. Cache the params as well * as passing the updated version back. */ NTSTATUS CDmSynthStream::HandlePortParams(IN PPCPROPERTY_REQUEST pRequest) { PAGED_CODE();
NTSTATUS ntStatus;
_DbgPrintF(DEBUGLVL_VERBOSE, ("CDmSynthStream::HandlePortParams"));
ntStatus = ValidatePropertyParams(pRequest, sizeof(SYNTH_PORTPARAMS), KSPROPERTY_TYPE_SET); if (!NT_SUCCESS(ntStatus)) { return ntStatus; }
if (pRequest->InstanceSize < sizeof(SYNTH_PORTPARAMS)) { _DbgPrintF(DEBUGLVL_TERSE, ("CDmSynthStream::HandlePortParams InstanceSize too small")); pRequest->ValueSize = 0; return STATUS_BUFFER_TOO_SMALL; }
RtlCopyMemory(pRequest->Value, pRequest->Instance, sizeof(SYNTH_PORTPARAMS));
PSYNTH_PORTPARAMS Params = (PSYNTH_PORTPARAMS)pRequest->Value;
if (!(Params->ValidParams & SYNTH_PORTPARAMS_VOICES)) { Params->Voices = DEFAULT_VOICES; } else if (Params->Voices > MAX_VOICES) { Params->Voices = MAX_VOICES; } else if (Params->Voices < 1) { Params->Voices = 1; }
if (!(Params->ValidParams & SYNTH_PORTPARAMS_CHANNELGROUPS)) { Params->ChannelGroups = DEFAULT_CHANNEL_GROUPS; } else if (Params->ChannelGroups > MAX_CHANNEL_GROUPS) { Params->ChannelGroups = MAX_CHANNEL_GROUPS; } else if (Params->ChannelGroups < 1) { Params->ChannelGroups = 1; }
// audio channels is fixed (chosen) by SysAudio
if (!(Params->ValidParams & SYNTH_PORTPARAMS_AUDIOCHANNELS)) { Params->AudioChannels = m_PortParams.AudioChannels; } else if (Params->AudioChannels != m_PortParams.AudioChannels) { Params->AudioChannels = m_PortParams.AudioChannels; }
// sample rate is fixed (chosen) by SysAudio
if (!(Params->ValidParams & SYNTH_PORTPARAMS_SAMPLERATE)) { Params->SampleRate = m_PortParams.SampleRate; } else if (Params->SampleRate != m_PortParams.SampleRate) { Params->SampleRate = m_PortParams.SampleRate; }
// set share. This cannot change.
Params->Share = m_PortParams.Share;
if (!(Params->ValidParams & SYNTH_PORTPARAMS_EFFECTS)) { Params->EffectsFlags = SYNTH_EFFECT_NONE; } else { Params->EffectsFlags = SYNTH_EFFECT_NONE; }
RtlCopyMemory(&m_PortParams, Params, sizeof(m_PortParams));
// Each channel groups is represented by a ControlLogic object
// (A channel groups is a set of sixteen MIDI channels)
HRESULT hr = m_pSynth->Open(m_PortParams.ChannelGroups, m_PortParams.Voices ); if (SUCCEEDED(hr)) { m_pSynth->SetGainAdjust(m_lVolume + m_lBoost); } else { ntStatus = MapHRESULT(hr); }
if (NT_SUCCESS(ntStatus)) { pRequest->ValueSize = sizeof(SYNTH_PORTPARAMS); } else { pRequest->ValueSize = 0; }
return ntStatus; }
/*****************************************************************************
* CDmSynthStream::HandleRunningStats() ***************************************************************************** * Handle the property for running statistics. */ NTSTATUS CDmSynthStream::HandleRunningStats(IN PPCPROPERTY_REQUEST pRequest) { FLOATSAFE fs;
PAGED_CODE();
NTSTATUS ntStatus; _DbgPrintF(DEBUGLVL_VERBOSE, ("CDmSynthStream::HandleRunningStats"));
ntStatus = ValidatePropertyParams(pRequest, sizeof(SYNTH_STATS), KSPROPERTY_TYPE_SET); if (!NT_SUCCESS(ntStatus)) { return ntStatus; }
PSYNTH_STATS StatsOut = (PSYNTH_STATS)pRequest->Value;
PerfStats Stats; m_pSynth->GetPerformanceStats(&Stats);
long lCPU = Stats.dwCPU;
if (Stats.dwVoices) { lCPU /= Stats.dwVoices; } else { lCPU = 0; }
StatsOut->Voices = Stats.dwVoices; StatsOut->CPUPerVoice = lCPU * 10; StatsOut->TotalCPU = Stats.dwCPU * 10; StatsOut->LostNotes = Stats.dwNotesLost; long ldB = 6;
StatsOut->ValidStats = SYNTH_STATS_VOICES | SYNTH_STATS_TOTAL_CPU | SYNTH_STATS_CPU_PER_VOICE | SYNTH_STATS_LOST_NOTES;
double fLevel = Stats.dwMaxAmplitude; if (Stats.dwMaxAmplitude < 1) { fLevel = -96.0; } else { fLevel /= 32768.0; fLevel = log10(fLevel); fLevel *= 20.0; } StatsOut->PeakVolume = (long) fLevel; StatsOut->ValidStats |= SYNTH_STATS_PEAK_VOLUME;
pRequest->ValueSize = sizeof(SYNTH_STATS);
return ntStatus; }
/*****************************************************************************
* CDmSynthStream::HandlePropertySynth() ***************************************************************************** * Handle the synth property set. */ NTSTATUS CDmSynthStream::HandlePropertySynth(IN PPCPROPERTY_REQUEST pRequest) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB, ("CDmSynthStream::HandlePropertySynth"));
NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; HRESULT hr;
if (pRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) { ntStatus = DefaultSynthBasicPropertyHandler(pRequest); } else { switch (pRequest->PropertyItem->Id) { case KSPROPERTY_SYNTH_PORTPARAMETERS: _DbgPrintF(DEBUGLVL_VERBOSE,("CDmSynthStream::HandlePropertySynth KSPROPERTY_SYNTH_PORTPARAMETERS")); ntStatus = HandlePortParams(pRequest); break; case KSPROPERTY_SYNTH_RUNNINGSTATS: _DbgPrintF(DEBUGLVL_VERBOSE,("CDmSynthStream::HandlePropertySynth KSPROPERTY_SYNTH_RUNNINGSTATS")); ntStatus = HandleRunningStats(pRequest); break; case KSPROPERTY_SYNTH_VOLUME: _DbgPrintF(DEBUGLVL_VERBOSE,("CDmSynthStream::HandlePropertySynth KSPROPERTY_SYNTH_VOLUME"));
ntStatus = ValidatePropertyParams(pRequest, sizeof(m_lVolume), 0); if (NT_SUCCESS(ntStatus)) { if (pRequest->Verb & KSPROPERTY_TYPE_GET) { pRequest->ValueSize = sizeof(m_lVolume); *(PLONG)pRequest->Value = m_lVolume; } else { m_lVolume = *(PLONG)pRequest->Value; m_pSynth->SetGainAdjust(m_lVolume + m_lBoost); } } break; case KSPROPERTY_SYNTH_VOLUMEBOOST: _DbgPrintF(DEBUGLVL_VERBOSE,("CDmSynthStream::HandlePropertySynth KSPROPERTY_SYNTH_VOLUMEBOOST"));
ntStatus = ValidatePropertyParams(pRequest, sizeof(m_lBoost), 0); if (NT_SUCCESS(ntStatus)) { if (pRequest->Verb & KSPROPERTY_TYPE_GET) { pRequest->ValueSize = sizeof(m_lBoost); *(PLONG)pRequest->Value = m_lBoost; } else { m_lBoost = *(PLONG)pRequest->Value; m_pSynth->SetGainAdjust(m_lVolume + m_lBoost); } } break; case KSPROPERTY_SYNTH_CHANNELGROUPS: _DbgPrintF(DEBUGLVL_VERBOSE,("CDmSynthStream::HandlePropertySynth KSPROPERTY_SYNTH_CHANNELGROUPS"));
ntStatus = ValidatePropertyParams(pRequest, sizeof(m_PortParams.ChannelGroups), 0); if (NT_SUCCESS(ntStatus)) { if (pRequest->Verb & KSPROPERTY_TYPE_GET) { pRequest->ValueSize = sizeof(m_PortParams.ChannelGroups); *(PULONG)pRequest->Value = m_PortParams.ChannelGroups; } else { hr = m_pSynth->SetNumChannelGroups(*(PULONG)pRequest->Value);
if (FAILED(hr)) { ntStatus = MapHRESULT(hr); } else { m_PortParams.ChannelGroups = *(PULONG)pRequest->Value; } } } break; case KSPROPERTY_SYNTH_VOICEPRIORITY: _DbgPrintF(DEBUGLVL_VERBOSE,("CDmSynthStream::HandlePropertySynth KSPROPERTY_SYNTH_VOICEPRIORITY"));
ntStatus = ValidatePropertyParams(pRequest, sizeof(DWORD), 0); if (NT_SUCCESS(ntStatus)) { if (pRequest->InstanceSize < sizeof(SYNTHVOICEPRIORITY_INSTANCE)) { ntStatus = STATUS_BUFFER_TOO_SMALL; pRequest->ValueSize = 0; } }
if (NT_SUCCESS(ntStatus)) { if (pRequest->Verb & KSPROPERTY_TYPE_GET) { PSYNTHVOICEPRIORITY_INSTANCE pVoicePriority = (PSYNTHVOICEPRIORITY_INSTANCE)pRequest->Instance;
hr = m_pSynth->GetChannelPriority(pVoicePriority->ChannelGroup, pVoicePriority->Channel, (PULONG)pRequest->Value); if (FAILED(hr)) { pRequest->ValueSize = 0; ntStatus = MapHRESULT(hr); } else { pRequest->ValueSize = sizeof(DWORD); } } else { PSYNTHVOICEPRIORITY_INSTANCE pVoicePriority = (PSYNTHVOICEPRIORITY_INSTANCE)pRequest->Instance;
hr = m_pSynth->SetChannelPriority(pVoicePriority->ChannelGroup, pVoicePriority->Channel, *(PULONG)pRequest->Value); if (FAILED(hr)) { ntStatus = MapHRESULT(hr); } } } break; case KSPROPERTY_SYNTH_LATENCYCLOCK: // This returns the latency clock created by the output audio sink object,
// which handles the output audio stream.
// The latency clock returns the current render time whenever its
// IReferenceClock::GetTime method is called. This time is always relative
// to the time established by the master clock.
// The latency time is used by clients to identify the next available time
// to start playing a note.
_DbgPrintF(DEBUGLVL_BLAB,("CDmSynthStream::HandlePropertySynth KSPROPERTY_SYNTH_LATENCYCLOCK"));
ntStatus = ValidatePropertyParams(pRequest, sizeof(ULONGLONG), KSPROPERTY_TYPE_SET); if (NT_SUCCESS(ntStatus)) { REFERENCE_TIME rtLatency; if (NT_SUCCESS(SampleToRefTime(m_llLastPosition, &rtLatency))) { if (m_pMasterClock) { REFERENCE_TIME rtMaster; if (NT_SUCCESS(m_pMasterClock->GetTime(&rtMaster))) { #if DBG
static DWORD g_dwIn = 0; #endif // DBG
if (rtLatency < rtMaster) { #if DBG
if (g_dwIn++ % 25 == 0) { _DbgPrintF(DEBUGLVL_VERBOSE,("Latency:%ld < Master:%ld", long(rtLatency / 10000), long(rtMaster / 10000))); } #endif // DBG
// REVIEW: rtLatency = rtMaster; // clamp it up
} else if (rtLatency > rtMaster + 10000000) { #if DBG
if (g_dwIn++ % 25 == 0) { _DbgPrintF(DEBUGLVL_VERBOSE,("Latency:%ld > Master:%ld", long(rtLatency / 10000), long(rtMaster / 10000))); } #endif // DBG
// REVIEW: rtLatency = rtMaster + 10000000; // clamp it down
} } } *((PULONGLONG)pRequest->Value) = rtLatency; pRequest->ValueSize = sizeof(ULONGLONG); } else { ntStatus = STATUS_UNSUCCESSFUL; } } break; default: _DbgPrintF(DEBUGLVL_TERSE,("CDmSynthStream::HandlePropertySynth unrecognized ID")); ntStatus = STATUS_UNSUCCESSFUL; break; } }
// We should return zero, if we fail.
//
if (STATUS_UNSUCCESSFUL == ntStatus || STATUS_INVALID_PARAMETER == ntStatus ) { pRequest->ValueSize = 0; }
return ntStatus; }
/*****************************************************************************
* CDmSynthStream::HandleDownload() ***************************************************************************** * Handle a download request. We carefully copy the data. * Forward to the synth and add to our list. */ NTSTATUS CDmSynthStream::HandleDownload(IN PPCPROPERTY_REQUEST pRequest) { PAGED_CODE();
NTSTATUS ntStatus; _DbgPrintF(DEBUGLVL_BLAB, ("CDmSynthStream::HandleDownload"));
ntStatus = ValidatePropertyParams(pRequest, sizeof(SYNTHDOWNLOAD), 0); if (!NT_SUCCESS(ntStatus)) { // We should return immediately. ValidatePropertyParams sets all
// error codes appropriately.
return ntStatus; }
if (pRequest->InstanceSize < sizeof(SYNTH_BUFFER)) { _DbgPrintF(DEBUGLVL_TERSE, ("CDmSynthStream::HandleDownload InstanceSize too small")); ntStatus = STATUS_BUFFER_TOO_SMALL; } if (pRequest->Instance == NULL) { _DbgPrintF(DEBUGLVL_TERSE, ("CDmSynthStream::HandleDownload Instance is NULL")); ntStatus = STATUS_BUFFER_TOO_SMALL; } if (pRequest->InstanceSize != sizeof(SYNTH_BUFFER) || pRequest->ValueSize != sizeof(SYNTHDOWNLOAD)) { _DbgPrintF(DEBUGLVL_TERSE, ("CDmSynthStream::HandleDownload InstanceSize:%lu, ValueSize:%lu", pRequest->InstanceSize, pRequest->ValueSize)); }
PSYNTH_BUFFER pDlsBuffer;
if (NT_SUCCESS(ntStatus)) { pDlsBuffer = (PSYNTH_BUFFER)pRequest->Instance;
if (!pDlsBuffer->BufferSize) { _DbgPrintF(DEBUGLVL_TERSE, ("CDmSynthStream::HandleDownload BufferSize is invalid")); ntStatus = STATUS_UNSUCCESSFUL; } }
// lock and copy user data into paged pool
BOOL pagesLocked = FALSE; PVOID pvData = NULL; if (NT_SUCCESS(ntStatus)) { PMDL pMdl = IoAllocateMdl(pDlsBuffer->BufferAddress, pDlsBuffer->BufferSize, FALSE, FALSE, NULL); if (pMdl) { __try { MmProbeAndLockPages(pMdl, KernelMode, IoReadAccess); pagesLocked = TRUE;
PVOID pvUserData = KernHelpGetSysAddrForMdl(pMdl);
pvData = (PVOID)new BYTE[pDlsBuffer->BufferSize]; if (pvData && pvUserData) { RtlCopyMemory(pvData, pvUserData, pDlsBuffer->BufferSize); ntStatus = STATUS_SUCCESS; } else { _DbgPrintF(DEBUGLVL_TERSE, ("CDmSynthStream::HandleDownload download allocate failed")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } } __except (EXCEPTION_EXECUTE_HANDLER) { _DbgPrintF(DEBUGLVL_TERSE, ("CDmSynthStream::HandleDownload lock or copy failed")); ntStatus = GetExceptionCode(); }
// cleanup
if (pagesLocked) { MmUnlockPages(pMdl); } IoFreeMdl(pMdl); } else { _DbgPrintF(DEBUGLVL_TERSE, ("CDmSynthStream::HandleDownload IoAllocateMdl failed")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; }
// download to synth
SYNTHDOWNLOAD SynthDownload; if (SUCCEEDED(ntStatus)) { HRESULT hr = m_pSynth->Download(&SynthDownload.DownloadHandle, pvData, &SynthDownload.Free); if (SUCCEEDED(hr)) { if (!SynthDownload.Free) { pvData = NULL; // prevent from being freed
}
if (SUCCEEDED(ntStatus)) { SynthDownload.Free = TRUE; // client can always free user data
ASSERT(pRequest->ValueSize >= sizeof(SynthDownload)); RtlCopyMemory(pRequest->Value, &SynthDownload, sizeof(SynthDownload)); pRequest->ValueSize = sizeof(SynthDownload); } } else { ntStatus = MapHRESULT(hr); } } }
if (pvData) { delete [] pvData; pvData = NULL; }
if (!NT_SUCCESS(ntStatus)) { pRequest->ValueSize = 0; }
return ntStatus; }
/*****************************************************************************
* CDmSynthStream::HandleUnload() ***************************************************************************** * Handle an unload request. Forward to the synth and remove from our list. */ NTSTATUS CDmSynthStream::HandleUnload(IN PPCPROPERTY_REQUEST pRequest) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB, ("CDmSynthStream::HandleUnload"));
NTSTATUS ntStatus;
ntStatus = ValidatePropertyParams(pRequest, sizeof(HANDLE), 0); if (NT_SUCCESS(ntStatus)) { HRESULT hr = m_pSynth->Unload(*(HANDLE*)pRequest->Value,NULL,NULL);
if (FAILED(hr)) { pRequest->ValueSize = 0; ntStatus = MapHRESULT(hr); } }
return ntStatus; }
/*****************************************************************************
* CDmSynthStream::HandlePropertySynthDls() ***************************************************************************** * Handles a property in the SynthDls set. */ NTSTATUS CDmSynthStream::HandlePropertySynthDls(IN PPCPROPERTY_REQUEST pRequest) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB, ("CDmSynthStream::HandlePropertySynthDls"));
NTSTATUS ntStatus;
if (pRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) { ntStatus = DefaultSynthBasicPropertyHandler(pRequest); } else { switch (pRequest->PropertyItem->Id) { case KSPROPERTY_SYNTH_DLS_DOWNLOAD: _DbgPrintF(DEBUGLVL_BLAB,("CDmSynthStream::HandlePropertySynthDls KSPROPERTY_SYNTH_DLS_DOWNLOAD")); ntStatus = HandleDownload(pRequest); break; case KSPROPERTY_SYNTH_DLS_UNLOAD: _DbgPrintF(DEBUGLVL_BLAB,("CDmSynthStream::HandlePropertySynthDls KSPROPERTY_SYNTH_DLS_UNLOAD")); ntStatus = HandleUnload(pRequest); break; case KSPROPERTY_SYNTH_DLS_APPEND: _DbgPrintF(DEBUGLVL_BLAB,("CDmSynthStream::HandlePropertySynthDls KSPROPERTY_SYNTH_DLS_APPEND")); ntStatus = ValidatePropertyParams(pRequest, sizeof(ULONG), KSPROPERTY_TYPE_SET); if (NT_SUCCESS(ntStatus)) { *(PULONG)(pRequest->Value) = 1; pRequest->ValueSize = sizeof(ULONG); } break; case KSPROPERTY_SYNTH_DLS_WAVEFORMAT: _DbgPrintF(DEBUGLVL_BLAB,("CDmSynthStream::HandlePropertySynthDls KSPROPERTY_SYNTH_DLS_WAVEFORMAT")); ntStatus = ValidatePropertyParams(pRequest, sizeof(WAVEFORMATEX), KSPROPERTY_TYPE_SET); if (NT_SUCCESS(ntStatus)) { WAVEFORMATEX *pwfex; pwfex = (WAVEFORMATEX *)pRequest->Value;
RtlZeroMemory(pwfex, sizeof(WAVEFORMATEX)); pwfex->wFormatTag = WAVE_FORMAT_PCM; pwfex->nChannels = 2; pwfex->nSamplesPerSec = 22050L; pwfex->nAvgBytesPerSec = 22050L * 2 * 2; pwfex->nBlockAlign = 4; pwfex->wBitsPerSample = 16; pwfex->cbSize = 0;
pRequest->ValueSize = sizeof(WAVEFORMATEX); }
break; default: _DbgPrintF(DEBUGLVL_TERSE,("CDmSynthStream::HandlePropertySynthDls unrecognized ID")); pRequest->ValueSize = 0; ntStatus = STATUS_UNSUCCESSFUL; break; } }
return ntStatus; }
/*****************************************************************************
* PropertyHandler_Support() ***************************************************************************** * Redirect to the correct CDMSynthStream member. */ NTSTATUS PropertyHandler_Support(IN PPCPROPERTY_REQUEST pRequest) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB, ("PropertyHandler_Support"));
ASSERT(pRequest); if (!(pRequest->MinorTarget)) { return(STATUS_INVALID_PARAMETER); }
return (PDMSYNTHSTREAM(pRequest->MinorTarget))->HandlePropertySupport(pRequest); }
/*****************************************************************************
* PropertyHandler_Effects() ***************************************************************************** * Redirect to the correct CDMSynthStream member. */ NTSTATUS PropertyHandler_Effects(IN PPCPROPERTY_REQUEST pRequest) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB, ("PropertyHandler_Effects"));
ASSERT(pRequest); if (!(pRequest->MinorTarget)) { return(STATUS_INVALID_PARAMETER); }
return (PDMSYNTHSTREAM(pRequest->MinorTarget))->HandlePropertyEffects(pRequest); }
/*****************************************************************************
* PropertyHandler_Synth() ***************************************************************************** * Redirect to the correct CDMSynthStream member. */ NTSTATUS PropertyHandler_Synth(IN PPCPROPERTY_REQUEST pRequest) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB, ("PropertyHandler_Synth"));
ASSERT(pRequest); if (!(pRequest->MinorTarget)) { return(STATUS_INVALID_PARAMETER); }
return (PDMSYNTHSTREAM(pRequest->MinorTarget))->HandlePropertySynth(pRequest); }
const WCHAR wszDescription[] = L"Microsoft DDK Kernel DLS Synthesizer";
/*****************************************************************************
* PropertyHandler_SynthCaps() ***************************************************************************** * Redirect to the correct CDMSynthStream member. */ NTSTATUS PropertyHandler_SynthCaps(IN PPCPROPERTY_REQUEST pRequest) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB, ("PropertyHandler_SynthCaps"));
ASSERT(pRequest);
NTSTATUS ntStatus;
if (pRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) { ntStatus = DefaultBasicPropertyHandler( pRequest, KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET); } else { ntStatus = ValidatePropertyParams(pRequest, sizeof(SYNTHCAPS), KSPROPERTY_TYPE_SET); if (NT_SUCCESS(ntStatus)) { SYNTHCAPS Caps; RtlZeroMemory(&Caps, sizeof(Caps));
Caps.Guid = CLSID_DDKWDMSynth; Caps.Flags = SYNTH_PC_DLS | SYNTH_PC_SOFTWARESYNTH; Caps.MemorySize = SYNTH_PC_SYSTEMMEMORY; Caps.MaxChannelGroups = MAX_CHANNEL_GROUPS; Caps.MaxVoices = MAX_VOICES; Caps.MaxAudioChannels = 2; RtlCopyMemory(Caps.Description, wszDescription, sizeof(wszDescription));
RtlCopyMemory(pRequest->Value, &Caps, sizeof(Caps)); pRequest->ValueSize = sizeof(Caps); } }
return ntStatus; }
/*****************************************************************************
* PropertyHandler_SynthDls() ***************************************************************************** * Redirect to the correct CDMSynthStream member. */ NTSTATUS PropertyHandler_SynthDls(IN PPCPROPERTY_REQUEST pRequest) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB, ("PropertyHandler_SynthDls"));
ASSERT(pRequest); if (!(pRequest->MinorTarget)) { return(STATUS_INVALID_PARAMETER); }
return (PDMSYNTHSTREAM(pRequest->MinorTarget))->HandlePropertySynthDls(pRequest); }
/*****************************************************************************
* DefaultBasicPropertyHandler() ***************************************************************************** * Finds the given property in SynthProperties and sets Support flags * accordingly. */ NTSTATUS DefaultSynthBasicPropertyHandler ( IN PPCPROPERTY_REQUEST pRequest ) { NTSTATUS ntStatus; const WORD c_wMaxProps = SIZEOF_ARRAY(SynthProperties); WORD wPropIdx;
ntStatus = ValidatePropertyParams(pRequest, sizeof(ULONG), 0); if (NT_SUCCESS(ntStatus)) { for (wPropIdx = 0; wPropIdx < c_wMaxProps; wPropIdx++) { if ( (SynthProperties[wPropIdx].Set == pRequest->PropertyItem->Set) && (SynthProperties[wPropIdx].Id == pRequest->PropertyItem->Id) ) { // if return buffer can hold a ULONG, return the access flags
PULONG AccessFlags = PULONG(pRequest->Value);
*AccessFlags = SynthProperties[wPropIdx].Flags;
// set the return value size
pRequest->ValueSize = sizeof(ULONG); break; } }
if (wPropIdx == c_wMaxProps) { _DbgPrintF(DEBUGLVL_TERSE, ("DefaultSynthBasicPropertyHandler property ID not found")); pRequest->ValueSize = 0; ntStatus = STATUS_UNSUCCESSFUL; } }
return ntStatus; } // DefaultSynthBasicPropertyHandler
/*****************************************************************************
* DefaultBasicPropertyHandler() ***************************************************************************** * Basic support Handler */ NTSTATUS DefaultBasicPropertyHandler ( IN PPCPROPERTY_REQUEST pRequest, IN DWORD dwSupportVerb ) { NTSTATUS ntStatus;
ntStatus = ValidatePropertyParams(pRequest, sizeof(ULONG), 0); if (NT_SUCCESS(ntStatus)) { pRequest->ValueSize = sizeof(ULONG); *PULONG(pRequest->Value) = dwSupportVerb; }
return ntStatus; } // DefaultBasicPropertyHandler
/*****************************************************************************
* ValidatePropertyParams() ***************************************************************************** * Checks whether the data size appropriate. */ NTSTATUS ValidatePropertyParams ( IN PPCPROPERTY_REQUEST pRequest, IN ULONG cbSize, IN DWORD dwExcludeVerb ) { NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
if (pRequest && cbSize) { // If this is an invalid request.
//
if (pRequest->Verb & dwExcludeVerb) { ntStatus = STATUS_INVALID_DEVICE_REQUEST; } // If the caller is asking for ValueSize.
//
else if (0 == pRequest->ValueSize) { pRequest->ValueSize = cbSize; ntStatus = STATUS_BUFFER_OVERFLOW; } // If the caller passed an invalid ValueSize.
//
else if (pRequest->ValueSize < cbSize) { ntStatus = STATUS_BUFFER_TOO_SMALL; } // If all parameters are OK.
//
else if (pRequest->ValueSize >= cbSize) { if (pRequest->Value) { ntStatus = STATUS_SUCCESS; //
// Caller should set ValueSize, if the property
// call is successful.
//
} } }
// Clear the ValueSize if unsuccessful.
//
if (STATUS_SUCCESS != ntStatus && STATUS_BUFFER_OVERFLOW != ntStatus && pRequest != NULL) { pRequest->ValueSize = 0; }
return ntStatus; } // ValidatePropertyParams
|