mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2200 lines
67 KiB
2200 lines
67 KiB
/*
|
|
|
|
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
|