Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

941 lines
23 KiB

// Copyright (c) 1998 Microsoft Corporation
//
// DirectMusic Software Synthesizer
//
#include "common.h"
#include "private.h"
#include "dmusicks.h"
#define STR_MODULENAME "DmSynth: "
#pragma code_seg("PAGE")
// Property handlers
//
NTSTATUS PropertyHandler_SynthCaps(IN PPCPROPERTY_REQUEST);
NTSTATUS PropertyHandler_SynthPortParameters(IN PPCPROPERTY_REQUEST);
NTSTATUS PropertyHandler_SynthMasterClock(IN PPCPROPERTY_REQUEST);
NTSTATUS PropertyHandler_SynthPortChannelGroups(IN PPCPROPERTY_REQUEST);
NTSTATUS PropertyHandler_DlsDownload(IN PPCPROPERTY_REQUEST);
NTSTATUS PropertyHandler_DlsUnload(IN PPCPROPERTY_REQUEST);
NTSTATUS PropertyHandler_DlsCompact(IN PPCPROPERTY_REQUEST);
NTSTATUS PropertyHandler_DlsAppend(IN PPCPROPERTY_REQUEST);
NTSTATUS PropertyHandler_DlsVolume(IN PPCPROPERTY_REQUEST);
NTSTATUS PropertyHandler_GetLatency(IN PPCPROPERTY_REQUEST);
NTSTATUS PropertyHandler_GetLatencyClock(IN PPCPROPERTY_REQUEST);
// CreateMiniportDirectMusic
//
//
NTSTATUS CreateMiniportDmSynth
(
OUT PUNKNOWN * Unknown,
IN PUNKNOWN UnknownOuter OPTIONAL,
IN POOL_TYPE PoolType
)
{
PAGED_CODE();
ASSERT(Unknown);
_DbgPrintF(DEBUGLVL_TERSE, ("Creating DirectMusic synth miniport"));
STD_CREATE_BODY(CMiniportDmSynth, Unknown, UnknownOuter, PoolType);
}
STDMETHODIMP CMiniportDmSynth::NonDelegatingQueryInterface
(
IN REFIID Interface,
OUT PVOID* Object
)
{
PAGED_CODE();
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_IMiniportSynthesizer))
{
*Object = PVOID(PMINIPORTSYNTHESIZER(this));
}
else
{
*Object = NULL;
}
if (*Object)
{
PUNKNOWN(*Object)->AddRef();
return STATUS_SUCCESS;
}
return STATUS_INVALID_PARAMETER;
}
CMiniportDmSynth::~CMiniportDmSynth()
{
}
STDMETHODIMP CMiniportDmSynth::Init
(
IN PUNKNOWN Unknown OPTIONAL,
IN PRESOURCELIST ResourceList,
IN PPORTSYNTHESIZER Port_,
OUT PSERVICEGROUP* ServiceGroup
)
{
_DbgPrintF(DEBUGLVL_TERSE, ("[CMiniportDmSynth::Init]"));
ASSERT(ResourceList);
ASSERT(Port_);
ASSERT(ServiceGroup);
Port = Port_;
Port->AddRef();
Stream = NULL;
*ServiceGroup = NULL;
return STATUS_SUCCESS;
}
STDMETHODIMP CMiniportDmSynth::NewStream
(
OUT PMINIPORTSYNTHESIZERSTREAM * Stream_,
IN PUNKNOWN OuterUnknown OPTIONAL,
IN POOL_TYPE PoolType,
IN ULONG Pin,
IN BOOLEAN Capture,
IN PKSDATAFORMAT DataFormat,
OUT PSERVICEGROUP * ServiceGroup
)
{
_DbgPrintF(DEBUGLVL_TERSE, ("[CMiniportDmSynth::NewStream]"));
NTSTATUS nt = STATUS_SUCCESS;
if (Stream)
{
// XXX Multiinstance!!!
//
nt = STATUS_INVALID_DEVICE_REQUEST;
}
else
{
CDmSynthStream *Stream = new(PoolType) CDmSynthStream(OuterUnknown);
if (Stream)
{
nt = Stream->Init(this);
if (NT_SUCCESS(nt))
{
Stream->AddRef();
*Stream_ = PMINIPORTSYNTHESIZERSTREAM(Stream);
}
else
{
Stream->Release();
Stream = NULL;
}
}
else
{
nt = STATUS_INSUFFICIENT_RESOURCES;
}
}
return nt;
}
STDMETHODIMP_(void) CMiniportDmSynth::Service()
{
}
// ==============================================================================
// PinDataRangesStream
// Structures indicating range of valid format values for streaming pins.
// ==============================================================================
static
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
PKSDATARANGE PinDataRangePointersStream[] =
{
PKSDATARANGE(&PinDataRangesStream[0])
};
#if 0
// ==============================================================================
// PinDataRangesBridge
// Structures indicating range of valid format values for bridge pins.
// ==============================================================================
static
KSDATARANGE PinDataRangesBridge[] =
{
{
sizeof(KSDATARANGE),
0,
0,
0,
STATICGUIDOF(KSDATAFORMAT_TYPE_MUSIC),
STATICGUIDOF(KSDATAFORMAT_SUBTYPE_MIDI_BUS),
STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
}
};
// ==============================================================================
// PinDataRangePointersBridge
// List of pointers to structures indicating range of valid format values
// for bridge pins.
// ==============================================================================
static
PKSDATARANGE PinDataRangePointersBridge[] =
{
&PinDataRangesBridge[0]
};
#endif
// ==============================================================================
// PinDataRangesAudio
// Structures indicating range of valid format values for audio pins.
// ==============================================================================
static
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.
// ==============================================================================
static
PKSDATARANGE PinDataRangePointersAudio[] =
{
(PKSDATARANGE)&PinDataRangesAudio
};
static
PCPROPERTY_ITEM
SynthProperties[] =
{
///////////////////////////////////////////////////////////////////
//
// Configuration items
//
// Global: Synth caps
//
{
&KSPROPSETID_Synth,
KSPROPERTY_SYNTH_CAPS,
KSPROPERTY_TYPE_GET,
PropertyHandler_SynthCaps
},
// Per Stream: Synth port parameters
//
{
&KSPROPSETID_Synth,
KSPROPERTY_SYNTH_PORTPARAMETERS,
KSPROPERTY_TYPE_GET,
PropertyHandler_SynthPortParameters
},
// Global: Master clock
//
{
&KSPROPSETID_Synth,
KSPROPERTY_SYNTH_MASTERCLOCK,
KSPROPERTY_TYPE_SET,
PropertyHandler_SynthMasterClock
},
// Per Stream: Channel groups
//
{
&KSPROPSETID_Synth,
KSPROPERTY_SYNTH_CHANNELGROUPS,
KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET,
PropertyHandler_SynthPortChannelGroups
},
///////////////////////////////////////////////////////////////////
//
// DLS items
//
// Per stream: Download DLS sample
//
{
&KSPROPSETID_Synth_Dls,
KSPROPERTY_SYNTH_DOWNLOAD,
KSPROPERTY_TYPE_GET,
PropertyHandler_DlsDownload
},
// Per stream: Unload DLS sample
//
{
&KSPROPSETID_Synth_Dls,
KSPROPERTY_SYNTH_UNLOAD,
KSPROPERTY_TYPE_SET,
PropertyHandler_DlsUnload
},
// Global: Compact DLS memory
//
{
&KSPROPSETID_Synth_Dls,
KSPROPERTY_SYNTH_COMPACT,
KSPROPERTY_TYPE_SET,
PropertyHandler_DlsCompact
},
// Per stream: append
//
{
&KSPROPSETID_Synth_Dls,
KSPROPERTY_SYNTH_APPEND,
KSPROPERTY_TYPE_SET,
PropertyHandler_DlsAppend
},
// Per stream: volume
//
{
&KSPROPSETID_Synth_Dls,
KSPROPERTY_SYNTH_VOLUME,
KSPROPERTY_TYPE_SET,
PropertyHandler_DlsVolume
},
///////////////////////////////////////////////////////////////////
//
// Clock items
//
// Per stream: Get desired latency
//
{
&KSPROPSETID_Synth,
KSPROPERTY_SYNTH_LATENCY,
KSPROPERTY_TYPE_GET,
PropertyHandler_GetLatency
},
// Per stream: Get current latency time
//
{
&KSPROPSETID_Synth,
KSPROPERTY_SYNTH_LatencyClock,
KSPROPERTY_TYPE_GET,
PropertyHandler_GetLatencyClock
}
};
DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSynth, SynthProperties);
// ==============================================================================
// MiniportPins
// List of pins.
// ==============================================================================
static
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
(GUID *) &KSCATEGORY_AUDIO, // Category
NULL, // Name
0 // Reserved
}
},
#if 0
{
0,0,0, // InstanceCount
NULL, // AutomationTable
{ // KsPinDescriptor
0, // InterfacesCount
NULL, // Interfaces
0, // MediumsCount
NULL, // Mediums
SIZEOF_ARRAY(PinDataRangePointersBridge), // DataRangesCount
PinDataRangePointersBridge, // DataRanges
KSPIN_DATAFLOW_OUT, // DataFlow
KSPIN_COMMUNICATION_NONE, // Communication
(GUID *) &KSCATEGORY_AUDIO, // Category
NULL, // Name
0 // Reserved
}
}
#else
{
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
(GUID *) &KSCATEGORY_AUDIO, // Category
NULL, // Name
0 // Reserved
}
}
#endif
};
// ==============================================================================
// MiniportNodes
// List of nodes.
// ==============================================================================
#define CONST_PCNODE_DESCRIPTOR(n) { 0, NULL, &n, NULL }
#define CONST_PCNODE_DESCRIPTOR_AUTO(n,a) { 0, &a, &n, NULL }
static
PCNODE_DESCRIPTOR MiniportNodes[] =
{
CONST_PCNODE_DESCRIPTOR_AUTO(KSNODETYPE_SYNTHESIZER, AutomationSynth)
};
// ==============================================================================
// MiniportConnections
// List of connections.
// ==============================================================================
static
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 bridge out.
};
/*****************************************************************************
* MiniportFilterDescriptor
*****************************************************************************
* Complete miniport description.
*/
static
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
0, // CategoryCount
NULL // Categories
};
STDMETHODIMP CMiniportDmSynth::GetDescription
(
OUT PPCFILTER_DESCRIPTOR * OutFilterDescriptor
)
{
PAGED_CODE();
ASSERT(OutFilterDescriptor);
_DbgPrintF(DEBUGLVL_VERBOSE, ("GetDescription"));
*OutFilterDescriptor = &MiniportFilterDescriptor;
return STATUS_SUCCESS;
}
STDMETHODIMP CMiniportDmSynth::DataRangeIntersection
(
IN ULONG PinId,
IN PKSDATARANGE DataRange,
IN PKSDATARANGE MatchingDataRange,
IN ULONG OutputBufferLength,
OUT PVOID ResultantFormat OPTIONAL,
OUT PULONG ResultantFormatLength
)
{
// XXX ???
//
return STATUS_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
//
// CDmSynthStream
//
CDmSynthStream::~CDmSynthStream()
{
_DbgPrintF(DEBUGLVL_TERSE, ("[CDmSynthStream destruct]"));
if (Miniport)
{
Miniport->Stream = NULL;
Miniport->Release();
}
if (Synth)
{
delete Synth;
}
if (Sink)
{
Sink->Release();
}
}
NTSTATUS CDmSynthStream::Init
(
CMiniportDmSynth *Miniport_
)
{
_DbgPrintF(DEBUGLVL_TERSE, ("[CDmSynthStream::Init]"));
_DbgPrintF(DEBUGLVL_TERSE, ("Stream IUnkown is %08X", DWORD(PVOID(PUNKNOWN(this)))));
Miniport = Miniport_;
Miniport->AddRef();
Synth = new CSynth;
if (Synth == NULL)
{
Miniport->Release();
return STATUS_NO_MEMORY;
}
Sink = new CSysLink;
if (Sink == NULL)
{
delete Synth;
Synth = NULL;
Miniport->Release();
return STATUS_NO_MEMORY;
}
return STATUS_SUCCESS;
}
STDMETHODIMP CDmSynthStream::NonDelegatingQueryInterface
(
IN REFIID Interface,
OUT PVOID* Object
)
{
PAGED_CODE();
ASSERT(Object);
if (IsEqualGUIDAligned(Interface, IID_IUnknown))
{
*Object = PVOID(PUNKNOWN(this));
}
else if (IsEqualGUIDAligned(Interface, IID_IMiniportSynthesizerStream))
{
*Object = PVOID(PMINIPORTSYNTHESIZERSTREAM(this));
}
else
{
*Object = NULL;
}
if (*Object)
{
//
// We reference the interface for the caller.
//
PUNKNOWN(*Object)->AddRef();
return STATUS_SUCCESS;
}
return STATUS_INVALID_PARAMETER;
}
STDMETHODIMP CDmSynthStream::SetState
(
IN KSSTATE NewState
)
{
PAGED_CODE();
_DbgPrintF(DEBUGLVL_VERBOSE, ("[CDmSynthStream::SetState]"));
NTSTATUS nt = STATUS_SUCCESS;
// XXX Propogate to activate state
//
switch (NewState)
{
case KSSTATE_RUN:
nt = Synth->Activate(PortParams.SampleRate,
PortParams.Stereo ? 2 : 1);
break;
case KSSTATE_ACQUIRE:
case KSSTATE_STOP:
case KSSTATE_PAUSE:
nt = Synth->Deactivate();
break;
}
return nt;
}
STDMETHODIMP CDmSynthStream::ConnectOutput
(
PMXFFILTER ConnectionPoint
)
{
return STATUS_NOT_IMPLEMENTED;
}
STDMETHODIMP CDmSynthStream::DisconnectOutput
(
PMXFFILTER ConnectionPoint
)
{
return STATUS_NOT_IMPLEMENTED;
}
STDMETHODIMP CDmSynthStream::PutMessage
(
IN PDMUS_KERNEL_EVENT Event
)
{
PBYTE Data = (Event->ByteCount <= sizeof(PBYTE) ? &Event->ActualData.Data[0] : Event->ActualData.DataPtr);
// This is just MIDI bytes
//
return Synth->PlayBuffer(Sink,
Event->PresTime100Ns,
Data,
Event->ByteCount,
(ULONG)Event->ChannelGroup);
}
// CDmSynthStream::HandlePortParams
//
// Fix up the port params to include defaults. Cache the params as well
// as passing the updated version back.
//
STDMETHODIMP CDmSynthStream::HandlePortParams
(
IN PPCPROPERTY_REQUEST pRequest
)
{
BOOL ValidParamChanged = FALSE;
SYNTH_PORTPARAMS *Params = (SYNTH_PORTPARAMS*)pRequest->Value;
if (pRequest->ValueSize < sizeof(SYNTH_PORTPARAMS))
{
return STATUS_BUFFER_TOO_SMALL;
}
if (!(Params->ValidParams & SYNTH_PORTPARAMS_VOICES))
{
Params->Voices = 32;
}
else if (Params->Voices > 32)
{
Params->Voices = 32;
ValidParamChanged = TRUE;
}
if (!(Params->ValidParams & SYNTH_PORTPARAMS_CHANNELGROUPS))
{
Params->ChannelGroups = 32;
}
else if (Params->ChannelGroups > 32)
{
Params->ChannelGroups = 32;
ValidParamChanged = TRUE;
}
if (!(Params->ValidParams & SYNTH_PORTPARAMS_SAMPLERATE))
{
Params->SampleRate = 22050;
}
else if (Params->SampleRate != 11025 && Params->SampleRate != 22050 && Params->SampleRate != 44100)
{
Params->SampleRate = 22050;
ValidParamChanged = TRUE;
}
if (!(Params->ValidParams & SYNTH_PORTPARAMS_REVERB))
{
Params->Reverb = FALSE;
}
else if (Params->Reverb)
{
Params->Reverb = FALSE;
ValidParamChanged = TRUE;
}
RtlCopyMemory(&PortParams, Params, sizeof(PortParams));
return ValidParamChanged ? STATUS_NOT_ALL_ASSIGNED : STATUS_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
//
// Property dispatchers
//
// XXX All of these need to be connected
//
NTSTATUS PropertyHandler_SynthCaps
(
IN PPCPROPERTY_REQUEST pRequest
)
{
SYNTHCAPS caps;
caps.Flags = SYNTH_PC_DLS | SYNTH_PC_SOFTWARESYNTH;
caps.MemorySize = SYNTH_PC_SYSTEMMEMORY;
caps.MaxChannelGroups = 32;
caps.MaxVoices = 32;
pRequest->ValueSize = min(pRequest->ValueSize, sizeof(caps));
RtlCopyMemory(pRequest->Value, &caps, pRequest->ValueSize);
return STATUS_SUCCESS;
}
// PropertyHandler_SynthPortParameters
//
NTSTATUS PropertyHandler_SynthPortParameters
(
IN PPCPROPERTY_REQUEST pRequest
)
{
ASSERT(pRequest);
ASSERT(pRequest->MinorTarget);
return (PDMSYNTHSTREAM(pRequest->MinorTarget))->HandlePortParams(pRequest);
}
// PropertyHandler_SynthMasterClock
//
NTSTATUS PropertyHandler_SynthMasterClock
(
IN PPCPROPERTY_REQUEST pRequest
)
{
return STATUS_SUCCESS;
}
// PropertyHandler_SynthPortChannelGroups
//
NTSTATUS PropertyHandler_SynthPortChannelGroups
(
IN PPCPROPERTY_REQUEST pRequest
)
{
ASSERT(pRequest);
ASSERT(pRequest->MinorTarget);
if (pRequest->ValueSize < sizeof(ULONG))
{
return STATUS_BUFFER_TOO_SMALL;
}
ULONG ChannelGroups = *(PULONG)(pRequest->Value);
return (PDMSYNTHSTREAM(pRequest->MinorTarget))->Synth->SetNumChannelGroups(ChannelGroups);
}
// PropertyHandler_DlsDownload
//
NTSTATUS PropertyHandler_DlsDownload
(
IN PPCPROPERTY_REQUEST pRequest
)
{
// XXX Lock down this memory
//
// XXX Validate entire buffer size???
//
HANDLE DownloadHandle;
BOOL Free;
NTSTATUS Status = (PDMSYNTHSTREAM(pRequest->MinorTarget))->Synth->Download(
&DownloadHandle,
pRequest->Value,
&Free);
if (SUCCEEDED(Status))
{
ASSERT(pRequest->ValueSize >= sizeof(DownloadHandle));
RtlCopyMemory(pRequest->Value, &DownloadHandle, sizeof(DownloadHandle));
pRequest->ValueSize = sizeof(DownloadHandle);
}
return Status;
}
// PropertyHandler_DlsUnload
//
HRESULT CALLBACK UnloadComplete(HANDLE,HANDLE);
NTSTATUS PropertyHandler_DlsUnload
(
IN PPCPROPERTY_REQUEST pRequest
)
{
ASSERT(pRequest);
ASSERT(pRequest->MinorTarget);
if (pRequest->ValueSize < sizeof(HANDLE))
{
return STATUS_BUFFER_TOO_SMALL;
}
// XXX Need some concurrency control here
//
NTSTATUS Status = (PDMSYNTHSTREAM(pRequest->MinorTarget))->Synth->Unload(
*(HANDLE*)pRequest->Value,
UnloadComplete,
(HANDLE)pRequest);
return STATUS_SUCCESS;
}
HRESULT CALLBACK UnloadComplete(HANDLE WhichDownload, HANDLE CallbackInstance)
{
PPCPROPERTY_REQUEST pRequest = (PPCPROPERTY_REQUEST)CallbackInstance;
PcCompletePendingPropertyRequest(pRequest, STATUS_SUCCESS);
return STATUS_SUCCESS;
}
// PropertyHandler_DlsCompact
//
// We don't care
//
NTSTATUS PropertyHandler_DlsCompact
(
IN PPCPROPERTY_REQUEST pRequest
)
{
return STATUS_SUCCESS;
}
NTSTATUS PropertyHandler_DlsAppend
(
IN PPCPROPERTY_REQUEST pRequest
)
{
ASSERT(pRequest);
if (pRequest->ValueSize < sizeof(ULONG))
{
return STATUS_BUFFER_TOO_SMALL;
}
*(PULONG)(pRequest->Value) = 4;
pRequest->ValueSize = sizeof(ULONG);
return STATUS_SUCCESS;
}
NTSTATUS PropertyHandler_DlsVolume
(
IN PPCPROPERTY_REQUEST pRequest
)
{
// XXX *Both* versions of the synth need this
//
return STATUS_SUCCESS;
}
NTSTATUS PropertyHandler_GetLatency
(
IN PPCPROPERTY_REQUEST pRequest
)
{
if (pRequest->ValueSize < sizeof(ULONGLONG))
{
return STATUS_BUFFER_TOO_SMALL;
}
*((PULONGLONG)pRequest->Value) = 0;
pRequest->ValueSize = sizeof(ULONGLONG);
return STATUS_SUCCESS;
}
NTSTATUS PropertyHandler_GetLatencyClock
(
IN PPCPROPERTY_REQUEST pRequest
)
{
// XXX This depends on the synth sink
//
return STATUS_SUCCESS;
}