windows-nt/Source/XPSP1/NT/drivers/ddk/wdmaudio/ddksynth/miniport.cpp

2201 lines
67 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*
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