windows-nt/Source/XPSP1/NT/drivers/wdm/audio/sysaudio/virtual.cpp

1784 lines
44 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
//---------------------------------------------------------------------------
//
// Module: virtual.c
//
// Description:
// Virtual Source Stuff
//
//
//@@BEGIN_MSINTERNAL
// Development Team:
// Andy Nicholson
//
// History: Date Author Comment
//
// To Do: Date Author Comment
//
//@@END_MSINTERNAL
//---------------------------------------------------------------------------
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1996-1999 Microsoft Corporation. All Rights Reserved.
//
//---------------------------------------------------------------------------
#include "common.h"
//---------------------------------------------------------------------------
// Virtual Source Data
KSDATARANGE DataRangeWildCard =
{
sizeof(KSDATARANGE),
0,
0,
0,
STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WILDCARD),
};
KSPIN_MEDIUM VirtualPinMedium =
{
STATICGUIDOF(KSMEDIUMSETID_Standard),
KSMEDIUM_STANDARD_DEVIO
};
KSDATARANGE VirtualPinDataRange =
{
sizeof(KSDATARANGE),
0,
0,
0,
STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG),
STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE),
};
//---------------------------------------------------------------------------
NTSTATUS
CreateVirtualMixer(
PDEVICE_NODE pDeviceNode
)
{
PTOPOLOGY_CONNECTION pTopologyConnection;
PVIRTUAL_SOURCE_LINE pVirtualSourceLine;
PLOGICAL_FILTER_NODE pLogicalFilterNode;
PTOPOLOGY_PIN pTopologyPinSumOutput;
PTOPOLOGY_NODE pTopologyNodeSum;
NTSTATUS Status = STATUS_SUCCESS;
PFILTER_NODE pFilterNode;
PPIN_INFO pPinInfo;
PPIN_NODE pPinNode;
//
// Create a special "virtual source" filter node and related structures
//
pFilterNode = new FILTER_NODE(FILTER_TYPE_AUDIO | FILTER_TYPE_VIRTUAL);
if(pFilterNode == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
Trap();
goto exit;
}
pFilterNode->SetFriendlyName(L"Virtual Mixer");
Status = pFilterNode->AddDeviceInterfaceMatch(
pDeviceNode->GetDeviceInterface());
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
//
// Create the logical filter node for this "virtual" filter
//
Status = CLogicalFilterNode::Create(&pLogicalFilterNode, pFilterNode);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
Status = CTopologyNode::Create(
&pTopologyNodeSum,
pFilterNode,
MAXULONG,
(GUID *)&KSNODETYPE_SUM);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
pTopologyNodeSum->iVirtualSource = 0;
Status = pLogicalFilterNode->lstTopologyNode.AddList(pTopologyNodeSum);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
Status = pLogicalFilterNode->AddList(
&pTopologyNodeSum->lstLogicalFilterNode);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
Status = CTopologyPin::Create(
&pTopologyPinSumOutput,
0, // 0 output
pTopologyNodeSum);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
//
// Create a virtual sysaudio source pin
//
pPinInfo = new PIN_INFO(pFilterNode, 0);
if(pPinInfo == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
Trap();
goto exit;
}
pPinInfo->DataFlow = KSPIN_DATAFLOW_OUT;
pPinInfo->Communication = KSPIN_COMMUNICATION_SOURCE;
pPinInfo->cPinInstances.PossibleCount = MAXULONG;
pFilterNode->cPins++;
pPinNode = new PIN_NODE(pPinInfo);
if(pPinNode == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
Trap();
goto exit;
}
pPinNode->pMedium = INTERNAL_WILDCARD;
pPinNode->pInterface = INTERNAL_WILDCARD;
pPinNode->pDataRange = &DataRangeWildCard;
Status = pLogicalFilterNode->lstPinNode.AddList(pPinNode);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
pPinNode->pLogicalFilterNode = pLogicalFilterNode;
//
// Connect the out of sum node to the source pin
//
Status = CTopologyConnection::Create(
&pTopologyConnection,
pFilterNode, // pFilterNode
NULL, // pGraphNode
pTopologyPinSumOutput, // pTopologyPin From
NULL, // pTopologyPin To
NULL, // pPinInfo From
pPinInfo); // pPinInfo To
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
Status = pTopologyConnection->AddList(
&pLogicalFilterNode->lstTopologyConnection);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
FOR_EACH_LIST_ITEM(gplstVirtualSourceLine, pVirtualSourceLine) {
ASSERT(pVirtualSourceLine->iVirtualSource <
pDeviceNode->cVirtualSourceData);
if(pDeviceNode->papVirtualSourceData[
pVirtualSourceLine->iVirtualSource]->pTopologyNode != NULL) {
continue;
}
Status = CreateVirtualLine(
pDeviceNode,
pFilterNode,
pTopologyNodeSum,
pLogicalFilterNode,
pVirtualSourceLine);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
} END_EACH_LIST_ITEM
// if new virtual source lines were created
if(pFilterNode->cPins > 1) {
pDeviceNode->pFilterNodeVirtual = pFilterNode;
}
else {
delete pFilterNode;
}
exit:
if(!NT_SUCCESS(Status)) {
Trap();
delete pFilterNode;
}
return(Status);
}
NTSTATUS
CreateVirtualLine(
PDEVICE_NODE pDeviceNode,
PFILTER_NODE pFilterNode,
PTOPOLOGY_NODE pTopologyNodeSum,
PLOGICAL_FILTER_NODE pLogicalFilterNode,
PVIRTUAL_SOURCE_LINE pVirtualSourceLine
)
{
PTOPOLOGY_CONNECTION pTopologyConnection;
NTSTATUS Status = STATUS_SUCCESS;
PTOPOLOGY_NODE pTopologyNode;
PTOPOLOGY_PIN pTopologyPinSumInput;
PTOPOLOGY_PIN pTopologyPinVolume;
PTOPOLOGY_PIN pTopologyPinMute;
PPIN_INFO pPinInfo;
PPIN_NODE pPinNode;
//
// Create a virtual sysaudio pin
//
pPinInfo = new PIN_INFO(pFilterNode, pVirtualSourceLine->iVirtualSource+1);
if(pPinInfo == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
Trap();
goto exit;
}
pPinInfo->DataFlow = KSPIN_DATAFLOW_IN;
pPinInfo->Communication = KSPIN_COMMUNICATION_NONE;
pPinInfo->pguidCategory = &pVirtualSourceLine->guidCategory;
pPinInfo->pguidName = &pVirtualSourceLine->guidName;
pFilterNode->cPins++;
pPinNode = new PIN_NODE(pPinInfo);
if(pPinNode == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
Trap();
goto exit;
}
pPinNode->pMedium = &VirtualPinMedium;
pPinNode->pDataRange = &VirtualPinDataRange;
Status = pLogicalFilterNode->lstPinNode.AddList(pPinNode);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
pPinNode->pLogicalFilterNode = pLogicalFilterNode;
//
// Create a virtual volume topology node and input topology pin
//
Status = CTopologyNode::Create(
&pTopologyNode,
pFilterNode,
MAXULONG,
(GUID *)&KSNODETYPE_VOLUME);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
pTopologyNode->iVirtualSource = pVirtualSourceLine->iVirtualSource;
ASSERT(pVirtualSourceLine->iVirtualSource <
pDeviceNode->cVirtualSourceData);
pDeviceNode->papVirtualSourceData[
pVirtualSourceLine->iVirtualSource]->pTopologyNode = pTopologyNode;
Status = pLogicalFilterNode->lstTopologyNode.AddList(pTopologyNode);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
Status = pLogicalFilterNode->AddList(&pTopologyNode->lstLogicalFilterNode);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
Status = CTopologyPin::Create(
&pTopologyPinVolume,
KSNODEPIN_STANDARD_IN, // 1 = input
pTopologyNode);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
//
// Create a connection from virtual pin to volume node
//
Status = CTopologyConnection::Create(
&pTopologyConnection,
pFilterNode, // pFilterNode
NULL, // pGraphNode
NULL, // pTopologyPin From
pTopologyPinVolume, // pTopologyPin To
pPinInfo, // pPinInfo From
NULL); // pPinInfo To
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
Status = pTopologyConnection->AddList(
&pLogicalFilterNode->lstTopologyConnection);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
Status = CTopologyPin::Create(
&pTopologyPinVolume,
KSNODEPIN_STANDARD_OUT, // 0 = output
pTopologyNode);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
//
// Create a virtual mute topology node and input topology pin
//
Status = CTopologyNode::Create(
&pTopologyNode,
pFilterNode,
MAXULONG,
(GUID *)&KSNODETYPE_MUTE);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
pTopologyNode->iVirtualSource = pVirtualSourceLine->iVirtualSource;
Status = pLogicalFilterNode->lstTopologyNode.AddList(pTopologyNode);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
Status = pLogicalFilterNode->AddList(&pTopologyNode->lstLogicalFilterNode);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
Status = CTopologyPin::Create(
&pTopologyPinMute,
KSNODEPIN_STANDARD_IN, // 1 = input
pTopologyNode);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
//
// Create a connection from volume node to mute node pin
//
Status = CTopologyConnection::Create(
&pTopologyConnection,
pFilterNode, // pFilterNode
NULL, // pGraphNode
pTopologyPinVolume, // pTopologyPin From
pTopologyPinMute, // pTopologyPin To
NULL, // pPinInfo From
NULL); // pPinInfo To
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
Status = pTopologyConnection->AddList(
&pLogicalFilterNode->lstTopologyConnection);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
Status = CTopologyPin::Create(
&pTopologyPinMute,
KSNODEPIN_STANDARD_OUT, // 1 = output
pTopologyNode);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
Status = CTopologyPin::Create(
&pTopologyPinSumInput,
pVirtualSourceLine->iVirtualSource + 1, // >= 1 input
pTopologyNodeSum);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
//
// Create a connection from mute node to sum node pin
//
Status = CTopologyConnection::Create(
&pTopologyConnection,
pFilterNode, // pFilterNode
NULL, // pGraphNode
pTopologyPinMute, // pTopologyPin From
pTopologyPinSumInput, // pTopologyPin To
NULL, // pPinInfo From
NULL); // pPinInfo To
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
Status = pTopologyConnection->AddList(
&pLogicalFilterNode->lstTopologyConnection);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
exit:
return(Status);
}
//---------------------------------------------------------------------------
ENUMFUNC
VirtualizeFindPin(
IN PTOPOLOGY_CONNECTION pTopologyConnection,
IN BOOL fToDirection,
IN PVOID pReference
)
{
NTSTATUS Status = STATUS_CONTINUE;
IN PPIN_INFO pPinInfo;
Assert(pTopologyConnection);
if(!IS_CONNECTION_TYPE(pTopologyConnection, FILTER)) {
Status = STATUS_DEAD_END;
goto exit;
}
if(fToDirection) {
pPinInfo = pTopologyConnection->pPinInfoTo;
}
else {
pPinInfo = pTopologyConnection->pPinInfoFrom;
}
if(pPinInfo == NULL) {
ASSERT(Status == STATUS_CONTINUE);
goto exit;
}
if(pPinInfo->pguidCategory == NULL) {
ASSERT(Status == STATUS_CONTINUE);
goto exit;
}
if(IsEqualGUID(pPinInfo->pguidCategory, &KSNODETYPE_SPEAKER)) {
Status = STATUS_SUCCESS;
}
exit:
return(Status);
}
ENUMFUNC
EnumerateVirtualizeFindPin(
IN PTOPOLOGY_CONNECTION pTopologyConnection,
IN BOOL fToDirection
)
{
ENUM_TOPOLOGY EnumTopology;
NTSTATUS Status;
Assert(pTopologyConnection);
EnumTopology.cTopologyRecursion = 0;
EnumTopology.Function = VirtualizeFindPin;
EnumTopology.fToDirection = fToDirection;
EnumTopology.pReference = NULL;
Status = EnumerateTopologyConnection(pTopologyConnection, &EnumTopology);
return(Status);
}
ENUMFUNC
VirtualizeFindNode(
IN PTOPOLOGY_CONNECTION pTopologyConnection,
IN BOOL fToDirection,
OUT PTOPOLOGY_NODE *ppTopologyNode,
IN GUID const *pguidType
)
{
NTSTATUS Status = STATUS_CONTINUE;
PTOPOLOGY_PIN pTopologyPin;
Assert(pTopologyConnection);
if(!IS_CONNECTION_TYPE(pTopologyConnection, FILTER)) {
Status = STATUS_DEAD_END;
goto exit;
}
if(fToDirection) {
pTopologyPin = pTopologyConnection->pTopologyPinTo;
}
else {
pTopologyPin = pTopologyConnection->pTopologyPinFrom;
}
if(pTopologyPin == NULL) {
ASSERT(Status == STATUS_CONTINUE);
goto exit;
}
if(IsEqualGUID(pTopologyPin->pTopologyNode->pguidType, &KSNODETYPE_SUM)) {
Status = STATUS_DEAD_END;
goto exit;
}
if(IsEqualGUID(pTopologyPin->pTopologyNode->pguidType, &KSNODETYPE_MUX)) {
Status = STATUS_DEAD_END;
goto exit;
}
if(IsEqualGUID(pTopologyPin->pTopologyNode->pguidType, pguidType)) {
Status = EnumerateVirtualizeFindPin(pTopologyConnection, fToDirection);
if(NT_SUCCESS(Status)) {
*ppTopologyNode = pTopologyPin->pTopologyNode;
}
ASSERT(Status != STATUS_DEAD_END);
}
exit:
return(Status);
}
ENUMFUNC
VirtualizeFindMute(
IN PTOPOLOGY_CONNECTION pTopologyConnection,
IN BOOL fToDirection,
OUT PTOPOLOGY_NODE *ppTopologyNode
)
{
return(VirtualizeFindNode(
pTopologyConnection,
fToDirection,
ppTopologyNode,
&KSNODETYPE_MUTE));
}
ENUMFUNC
VirtualizeFindVolume(
IN PTOPOLOGY_CONNECTION pTopologyConnection,
IN BOOL fToDirection,
OUT PTOPOLOGY_NODE *ppTopologyNode
)
{
return(VirtualizeFindNode(
pTopologyConnection,
fToDirection,
ppTopologyNode,
&KSNODETYPE_VOLUME));
}
VOID
VirtualizeTopologyNode(
IN PDEVICE_NODE pDeviceNode,
IN PTOPOLOGY_NODE pTopologyNode,
IN PVIRTUAL_SOURCE_LINE pVirtualSourceLine
)
{
DPF3(100, "VirtualizeTopologyNode: real node #%d index %d %s",
pTopologyNode->ulRealNodeNumber,
pVirtualSourceLine->iVirtualSource,
DbgGuid2Sz(&pVirtualSourceLine->guidCategory));
ASSERT(
(pTopologyNode->iVirtualSource == MAXULONG) ||
(pTopologyNode->iVirtualSource == pVirtualSourceLine->iVirtualSource));
// The PinId of a virtual pininfo has VirtualSourceData index
pTopologyNode->iVirtualSource = pVirtualSourceLine->iVirtualSource;
if(pVirtualSourceLine->ulFlags & VSL_FLAGS_CREATE_ONLY) {
pTopologyNode->ulFlags |= TN_FLAGS_DONT_FORWARD;
}
ASSERT(pTopologyNode->iVirtualSource < pDeviceNode->cVirtualSourceData);
if(IsEqualGUID(pTopologyNode->pguidType, &KSNODETYPE_VOLUME)) {
pDeviceNode->papVirtualSourceData[
pTopologyNode->iVirtualSource]->pTopologyNode = pTopologyNode;
}
}
NTSTATUS
AddVirtualMute(
IN PDEVICE_NODE pDeviceNode,
IN PTOPOLOGY_NODE pTopologyNodeVolume,
IN PVIRTUAL_SOURCE_LINE pVirtualSourceLine
)
{
PTOPOLOGY_CONNECTION pTopologyConnectionNew = NULL;
PTOPOLOGY_CONNECTION pTopologyConnection = NULL;
PTOPOLOGY_NODE pTopologyNodeMute = NULL;
PTOPOLOGY_PIN pTopologyPinMuteInput = NULL;
PTOPOLOGY_PIN pTopologyPinMuteOutput = NULL;
PTOPOLOGY_PIN pTopologyPinVolumeOutput = NULL;
PLOGICAL_FILTER_NODE pLogicalFilterNode;
NTSTATUS Status;
ASSERT(pTopologyNodeVolume->iVirtualSource != MAXULONG);
FOR_EACH_LIST_ITEM(
&pTopologyNodeVolume->lstTopologyPin,
pTopologyPinVolumeOutput) {
if(pTopologyPinVolumeOutput->ulPinNumber == KSNODEPIN_STANDARD_OUT) {
break;
}
} END_EACH_LIST_ITEM
if(pTopologyPinVolumeOutput == NULL) {
Trap();
Status = STATUS_INVALID_DEVICE_REQUEST;
goto exit;
}
ASSERT(pTopologyPinVolumeOutput->ulPinNumber == KSNODEPIN_STANDARD_OUT);
FOR_EACH_LIST_ITEM(
&pTopologyPinVolumeOutput->lstTopologyConnection,
pTopologyConnection) {
Assert(pTopologyConnection);
if(EnumerateVirtualizeFindPin(
pTopologyConnection,
TRUE) == STATUS_SUCCESS) { // Assumes KSPIN_DATAFLOW_IN
break;
}
} END_EACH_LIST_ITEM
if(pTopologyConnection == NULL) {
Trap();
Status = STATUS_INVALID_DEVICE_REQUEST;
goto exit;
}
ASSERT(pTopologyConnection->pTopologyPinFrom == pTopologyPinVolumeOutput);
Status = CTopologyNode::Create(
&pTopologyNodeMute,
pTopologyNodeVolume->pFilterNode,
MAXULONG,
(GUID *)&KSNODETYPE_MUTE);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
VirtualizeTopologyNode(pDeviceNode, pTopologyNodeMute, pVirtualSourceLine);
Status = CTopologyPin::Create(
&pTopologyPinMuteInput,
KSNODEPIN_STANDARD_IN, // 1 = input
pTopologyNodeMute);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
Status = CTopologyPin::Create(
&pTopologyPinMuteOutput,
KSNODEPIN_STANDARD_OUT, // 0 = output
pTopologyNodeMute);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
Status = CTopologyConnection::Create(
&pTopologyConnectionNew,
pTopologyNodeVolume->pFilterNode, // pFilterNode
NULL, // pGraphNode
pTopologyPinVolumeOutput, // pTopologyPin From
pTopologyPinMuteInput, // pTopologyPin To
NULL, // pPinInfo From
NULL); // pPinInfo To
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
Status = pTopologyConnection->AddList(
&pTopologyPinMuteOutput->lstTopologyConnection);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
FOR_EACH_LIST_ITEM(
&pTopologyNodeVolume->lstLogicalFilterNode,
pLogicalFilterNode) {
Status = pLogicalFilterNode->AddList(
&pTopologyNodeMute->lstLogicalFilterNode);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
Status = pLogicalFilterNode->lstTopologyNode.AddList(pTopologyNodeMute);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
Status = pTopologyConnectionNew->AddList(
&pLogicalFilterNode->lstTopologyConnection);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
} END_EACH_LIST_ITEM
pTopologyConnection->pTopologyPinFrom = pTopologyPinMuteOutput;
pTopologyConnection->RemoveList(
&pTopologyPinVolumeOutput->lstTopologyConnection);
exit:
if(!NT_SUCCESS(Status)) {
Trap();
if(pTopologyConnectionNew != NULL) {
Trap();
pTopologyConnectionNew->Destroy();
}
if(pTopologyNodeMute != NULL) {
if(pTopologyNodeVolume != NULL) {
Trap();
pTopologyNodeMute->RemoveList(
&pTopologyNodeVolume->pFilterNode->lstTopologyNode);
FOR_EACH_LIST_ITEM(
&pTopologyNodeVolume->lstLogicalFilterNode,
pLogicalFilterNode) {
pLogicalFilterNode->lstTopologyNode.RemoveList(
pTopologyNodeMute);
} END_EACH_LIST_ITEM
}
pTopologyNodeMute->Destroy();
}
}
return(Status);
}
NTSTATUS
VirtualizeTopology(
PDEVICE_NODE pDeviceNode,
PFILTER_NODE pFilterNode
)
{
PVIRTUAL_SOURCE_LINE pVirtualSourceLine;
PTOPOLOGY_NODE pTopologyNodeVolume;
PTOPOLOGY_NODE pTopologyNodeMute;
NTSTATUS Status = STATUS_SUCCESS;
PPIN_INFO pPinInfo;
FOR_EACH_LIST_ITEM(&pFilterNode->lstPinInfo, pPinInfo) {
if(pPinInfo->pguidCategory == NULL) {
continue;
}
FOR_EACH_LIST_ITEM(gplstVirtualSourceLine, pVirtualSourceLine) {
if(pPinInfo->DataFlow != KSPIN_DATAFLOW_IN) {
continue;
}
if(!IsEqualGUID(
pPinInfo->pguidCategory,
&pVirtualSourceLine->guidCategory)) {
continue;
}
if(EnumerateTopology(
pPinInfo,
(TOP_PFN)VirtualizeFindVolume,
&pTopologyNodeVolume) == STATUS_SUCCESS) {
VirtualizeTopologyNode(
pDeviceNode,
pTopologyNodeVolume,
pVirtualSourceLine);
if(EnumerateTopology(
pPinInfo,
(TOP_PFN)VirtualizeFindMute,
&pTopologyNodeMute) == STATUS_SUCCESS) {
VirtualizeTopologyNode(
pDeviceNode,
pTopologyNodeMute,
pVirtualSourceLine);
}
else {
Status = AddVirtualMute(
pDeviceNode,
pTopologyNodeVolume,
pVirtualSourceLine);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
}
}
} END_EACH_LIST_ITEM
} END_EACH_LIST_ITEM
exit:
return(Status);
}
//---------------------------------------------------------------------------
NTSTATUS
CreateVirtualSource(
IN PIRP pIrp,
PSYSAUDIO_CREATE_VIRTUAL_SOURCE pCreateVirtualSource,
OUT PULONG pulMixerPinId
)
{
PVIRTUAL_SOURCE_LINE pVirtualSourceLine = NULL;
NTSTATUS Status = STATUS_SUCCESS;
PFILTER_INSTANCE pFilterInstance;
PIO_STACK_LOCATION pIrpStack;
PDEVICE_NODE pDeviceNode;
pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
pFilterInstance = (PFILTER_INSTANCE)pIrpStack->FileObject->FsContext;
Assert(pFilterInstance);
if(!pFilterInstance->IsChildInstance()) {
Trap();
DPF(5, "CreateVirtualSource: FAILED - open pin instances");
Status = STATUS_INVALID_DEVICE_REQUEST;
goto exit;
}
FOR_EACH_LIST_ITEM(gplstVirtualSourceLine, pVirtualSourceLine) {
if(!IsEqualGUID(
&pVirtualSourceLine->guidCategory,
&pCreateVirtualSource->PinCategory)) {
continue;
}
if(!IsEqualGUID(
&pVirtualSourceLine->guidName,
&pCreateVirtualSource->PinName)) {
continue;
}
ASSERT(NT_SUCCESS(Status));
goto dup;
} END_EACH_LIST_ITEM
pVirtualSourceLine = new VIRTUAL_SOURCE_LINE(pCreateVirtualSource);
if(pVirtualSourceLine == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
Trap();
goto exit;
}
FOR_EACH_LIST_ITEM(gplstDeviceNode, pDeviceNode) {
DPF2(60, "CreateVirtualSource: DN %08x %s",
pDeviceNode,
pDeviceNode->DumpName());
Status = pDeviceNode->Update();
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
} END_EACH_LIST_ITEM
dup:
*pulMixerPinId = pVirtualSourceLine->iVirtualSource;
pIrp->IoStatus.Information = sizeof(ULONG);
exit:
if(!NT_SUCCESS(Status)) {
delete pVirtualSourceLine;
}
return(Status);
}
NTSTATUS
AttachVirtualSource(
IN PIRP pIrp,
IN PSYSAUDIO_ATTACH_VIRTUAL_SOURCE pAttachVirtualSource,
IN OUT PVOID pData
)
{
PVIRTUAL_NODE_DATA pVirtualNodeData = NULL;
PSTART_NODE_INSTANCE pStartNodeInstance;
PVIRTUAL_SOURCE_DATA pVirtualSourceData;
NTSTATUS Status = STATUS_SUCCESS;
PPIN_INSTANCE pPinInstance;
PDEVICE_NODE pDeviceNode;
LONG Channel;
Status = ::GetStartNodeInstance(pIrp, &pStartNodeInstance);
if(!NT_SUCCESS(Status)) {
goto exit;
}
pPinInstance = pStartNodeInstance->pPinInstance;
Assert(pPinInstance);
Assert(pPinInstance->pFilterInstance);
Assert(pPinInstance->pFilterInstance->pGraphNodeInstance);
pDeviceNode = pPinInstance->pFilterInstance->GetDeviceNode();
Assert(pDeviceNode);
if(pAttachVirtualSource->MixerPinId >= pDeviceNode->cVirtualSourceData) {
DPF(5, "AttachVirtualSource: invalid MixerPinId");
Status = STATUS_INVALID_DEVICE_REQUEST;
goto exit;
}
ASSERT(pDeviceNode->papVirtualSourceData != NULL);
pVirtualSourceData =
pDeviceNode->papVirtualSourceData[pAttachVirtualSource->MixerPinId];
Assert(pVirtualSourceData);
Status = GetVolumeNodeNumber(pPinInstance, pVirtualSourceData);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
if(pPinInstance->ulVolumeNodeNumber == MAXULONG) {
ASSERT(NT_SUCCESS(Status));
goto exit;
}
pVirtualNodeData = new VIRTUAL_NODE_DATA(
pStartNodeInstance,
pVirtualSourceData);
if(pVirtualNodeData == NULL) {
Trap();
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
if(pVirtualSourceData->pTopologyNode->ulRealNodeNumber != MAXULONG) {
ULONG ulNodeNumber;
//
// Get the volume control range for the physical node
//
ulNodeNumber = pPinInstance->pFilterInstance->pGraphNodeInstance->
paulNodeNumber[pAttachVirtualSource->MixerPinId];
if(ulNodeNumber == pPinInstance->ulVolumeNodeNumber) {
ASSERT(NT_SUCCESS(Status));
delete pVirtualNodeData;
goto exit;
}
Status = pStartNodeInstance->GetTopologyNodeFileObject(
&pVirtualNodeData->pFileObject,
ulNodeNumber);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
pVirtualNodeData->NodeId =
pVirtualSourceData->pTopologyNode->ulRealNodeNumber;
Status = GetControlRange(pVirtualNodeData);
if(!NT_SUCCESS(Status)) {
goto exit;
}
pVirtualSourceData->MinimumValue = pVirtualNodeData->MinimumValue;
pVirtualSourceData->MaximumValue = pVirtualNodeData->MaximumValue;
pVirtualSourceData->Steps = pVirtualNodeData->Steps;
}
Status = pStartNodeInstance->GetTopologyNodeFileObject(
&pVirtualNodeData->pFileObject,
pPinInstance->ulVolumeNodeNumber);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
pVirtualNodeData->NodeId = pPinInstance->pFilterInstance->
pGraphNodeInstance->papTopologyNode[pPinInstance->ulVolumeNodeNumber]->
ulRealNodeNumber;
Status = GetControlRange(pVirtualNodeData);
if(!NT_SUCCESS(Status)) {
goto exit;
}
for(Channel = 0; Channel < pVirtualSourceData->cChannels; Channel++) {
Status = SetVirtualVolume(pVirtualNodeData, Channel);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
}
exit:
if(!NT_SUCCESS(Status)) {
delete pVirtualNodeData;
}
return(Status);
}
NTSTATUS
FilterVirtualPropertySupportHandler(
IN PIRP pIrp,
IN PKSNODEPROPERTY pNodeProperty,
IN OUT PVOID pData
)
{
PGRAPH_NODE_INSTANCE pGraphNodeInstance;
PTOPOLOGY_NODE pTopologyNode;
NTSTATUS Status;
Status = GetGraphNodeInstance(pIrp, &pGraphNodeInstance);
if(!NT_SUCCESS(Status)) {
goto exit;
}
Assert(pGraphNodeInstance);
Status = STATUS_NOT_FOUND;
if((pNodeProperty->Property.Flags & KSPROPERTY_TYPE_TOPOLOGY) == 0) {
DPF(5, "FilterVirtualPropertySupportHandler: no TOPOLOGY bit");
ASSERT(Status == STATUS_NOT_FOUND);
goto exit;
}
if(pNodeProperty->NodeId >=
pGraphNodeInstance->Topology.TopologyNodesCount) {
DPF(5, "FilterVirtualPropertySupportHandler: invalid node #");
ASSERT(Status == STATUS_NOT_FOUND);
goto exit;
}
pTopologyNode = pGraphNodeInstance->papTopologyNode[pNodeProperty->NodeId];
Assert(pTopologyNode);
if(pTopologyNode->ulRealNodeNumber == MAXULONG) {
ASSERT(pTopologyNode->iVirtualSource != MAXULONG);
Status = STATUS_SOME_NOT_MAPPED;
goto exit;
}
ASSERT(Status == STATUS_NOT_FOUND);
exit:
return(Status);
}
NTSTATUS
FilterVirtualPropertyHandler(
IN PIRP pIrp,
IN PKSNODEPROPERTY pNodeProperty,
IN OUT PLONG plLevel
)
{
PGRAPH_NODE_INSTANCE pGraphNodeInstance;
PVIRTUAL_SOURCE_DATA pVirtualSourceData;
PVIRTUAL_NODE_DATA pVirtualNodeData;
PIO_STACK_LOCATION pIrpStack;
PTOPOLOGY_NODE pTopologyNode;
LONG StopChannel, Channel;
NTSTATUS Status;
pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
Status = GetGraphNodeInstance(pIrp, &pGraphNodeInstance);
if(!NT_SUCCESS(Status)) {
goto exit;
}
Assert(pGraphNodeInstance);
if(((pNodeProperty->Property.Flags & KSPROPERTY_TYPE_TOPOLOGY) == 0) ||
(pNodeProperty->NodeId >=
pGraphNodeInstance->Topology.TopologyNodesCount)) {
DPF(5, "FilterVirtualPropertyHandler: invalid property");
Status = STATUS_NOT_FOUND;
goto exit;
}
pTopologyNode = pGraphNodeInstance->papTopologyNode[pNodeProperty->NodeId];
Assert(pTopologyNode);
if(pTopologyNode->iVirtualSource == MAXULONG) {
Status = STATUS_NOT_FOUND;
goto exit;
}
ASSERT(pTopologyNode->iVirtualSource < gcVirtualSources);
ASSERT(pGraphNodeInstance->pGraphNode->pDeviceNode->
papVirtualSourceData != NULL);
pVirtualSourceData = pGraphNodeInstance->pGraphNode->pDeviceNode->
papVirtualSourceData[pTopologyNode->iVirtualSource];
Assert(pVirtualSourceData);
if(pIrpStack->Parameters.DeviceIoControl.InputBufferLength <
sizeof(KSNODEPROPERTY_AUDIO_CHANNEL) ||
(pNodeProperty->Property.Id != KSPROPERTY_AUDIO_VOLUMELEVEL &&
pNodeProperty->Property.Id != KSPROPERTY_AUDIO_MUTE)) {
Trap();
Status = STATUS_INVALID_DEVICE_REQUEST;
goto exit;
}
Channel = ((PKSNODEPROPERTY_AUDIO_CHANNEL)pNodeProperty)->Channel;
StopChannel = Channel;
if(Channel == MAXULONG) {
Channel = 0;
StopChannel = pVirtualSourceData->cChannels - 1;
}
if(Channel >= MAX_NUM_CHANNELS || Channel < 0) {
Status = STATUS_INVALID_DEVICE_REQUEST;
goto exit;
}
for(; Channel <= StopChannel; Channel++) {
if(IsEqualGUID(pTopologyNode->pguidType, &KSNODETYPE_MUTE)) {
if(pNodeProperty->Property.Flags & KSPROPERTY_TYPE_GET) {
*plLevel = pVirtualSourceData->fMuted[Channel];
pIrp->IoStatus.Information = sizeof(LONG);
}
else {
ASSERT(pNodeProperty->Property.Flags & KSPROPERTY_TYPE_SET);
pVirtualSourceData->fMuted[Channel] = *plLevel;
if(pTopologyNode->ulRealNodeNumber == MAXULONG) {
Status = SetPhysicalVolume(
pGraphNodeInstance,
pVirtualSourceData,
Channel);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
}
}
}
else if(IsEqualGUID(pTopologyNode->pguidType, &KSNODETYPE_VOLUME)) {
if(pNodeProperty->Property.Flags & KSPROPERTY_TYPE_GET) {
*plLevel = pVirtualSourceData->lLevel[Channel];
pIrp->IoStatus.Information = sizeof(LONG);
}
else {
ASSERT(pNodeProperty->Property.Flags & KSPROPERTY_TYPE_SET);
pVirtualSourceData->lLevel[Channel] = *plLevel;
}
}
else {
DPF2(5, "Invalid TopologyNode Prop.Id %d Node.Id %d",
pNodeProperty->Property.Id,
pTopologyNode->ulRealNodeNumber);
Status = STATUS_INVALID_DEVICE_REQUEST;
goto exit;
}
ASSERT(NT_SUCCESS(Status));
if(pNodeProperty->Property.Flags & KSPROPERTY_TYPE_SET) {
FOR_EACH_LIST_ITEM(
&pVirtualSourceData->lstVirtualNodeData,
pVirtualNodeData) {
ASSERT(pVirtualSourceData ==
pVirtualNodeData->pVirtualSourceData);
Status = SetVirtualVolume(pVirtualNodeData, Channel);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
} END_EACH_LIST_ITEM
}
}
if(pTopologyNode->ulRealNodeNumber == MAXULONG ||
pTopologyNode->ulFlags & TN_FLAGS_DONT_FORWARD ||
pNodeProperty->Property.Flags & KSPROPERTY_TYPE_GET) {
Status = STATUS_SUCCESS;
}
else {
// If topology node has a real node number, forward the irp to it
Status = STATUS_NOT_FOUND;
}
exit:
return(Status);
}
NTSTATUS
PinVirtualPropertySupportHandler(
IN PIRP pIrp,
IN PKSNODEPROPERTY pNodeProperty,
IN OUT PVOID pData
)
{
return(STATUS_NOT_FOUND);
}
NTSTATUS
PinVirtualPropertyHandler(
IN PIRP pIrp,
IN PKSNODEPROPERTY_AUDIO_CHANNEL pNodePropertyAudioChannel,
IN OUT PLONG plLevel
)
{
PSTART_NODE_INSTANCE pStartNodeInstance;
PFILTER_INSTANCE pFilterInstance;
NTSTATUS Status = STATUS_SUCCESS;
PKSNODEPROPERTY pNodeProperty;
PPIN_INSTANCE pPinInstance;
LONG StopChannel, Channel;
Status = ::GetStartNodeInstance(pIrp, &pStartNodeInstance);
if(!NT_SUCCESS(Status)) {
goto exit;
}
pPinInstance = pStartNodeInstance->pPinInstance;
Assert(pPinInstance);
pFilterInstance = pPinInstance->pFilterInstance;
Assert(pFilterInstance);
Assert(pFilterInstance->pGraphNodeInstance);
pNodeProperty = &pNodePropertyAudioChannel->NodeProperty;
if(((pNodeProperty->Property.Flags & KSPROPERTY_TYPE_TOPOLOGY) == 0) ||
(pNodeProperty->NodeId >=
pFilterInstance->pGraphNodeInstance->Topology.TopologyNodesCount)) {
DPF(5, "PinVirtualPropertyHandler: invalid property");
Status = STATUS_INVALID_DEVICE_REQUEST;
goto exit;
}
if(pStartNodeInstance->pVirtualNodeData == NULL ||
pPinInstance->ulVolumeNodeNumber == MAXULONG ||
pPinInstance->ulVolumeNodeNumber != pNodeProperty->NodeId) {
Status = STATUS_NOT_FOUND;
goto exit;
}
Assert(pStartNodeInstance->pVirtualNodeData);
Assert(pStartNodeInstance->pVirtualNodeData->pVirtualSourceData);
StopChannel = Channel = pNodePropertyAudioChannel->Channel;
if(Channel == MAXULONG) {
Channel = 0;
StopChannel = pStartNodeInstance->pVirtualNodeData->
pVirtualSourceData->cChannels - 1;
}
if(Channel >= MAX_NUM_CHANNELS || Channel < 0) {
Status = STATUS_INVALID_DEVICE_REQUEST;
goto exit;
}
for(; Channel <= StopChannel; Channel++) {
if(pNodeProperty->Property.Flags & KSPROPERTY_TYPE_GET) {
*plLevel = pStartNodeInstance->pVirtualNodeData->lLevel[Channel];
pIrp->IoStatus.Information = sizeof(LONG);
ASSERT(NT_SUCCESS(Status));
}
else {
ASSERT(pNodeProperty->Property.Flags & KSPROPERTY_TYPE_SET);
pStartNodeInstance->pVirtualNodeData->lLevel[Channel] = *plLevel;
Status = SetVirtualVolume(
pStartNodeInstance->pVirtualNodeData,
Channel);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
}
}
exit:
return(Status);
}
NTSTATUS
GetControlRange(
PVIRTUAL_NODE_DATA pVirtualNodeData
)
{
PKSPROPERTY_DESCRIPTION pPropertyDescription = NULL;
PKSPROPERTY_MEMBERSHEADER pMemberHeader;
PKSPROPERTY_STEPPING_LONG pSteppingLong;
NTSTATUS Status = STATUS_SUCCESS;
// Setup the defaults
pVirtualNodeData->MinimumValue = (-96 * 65536);
pVirtualNodeData->MaximumValue = 0;
pVirtualNodeData->Steps = (65536/2); // 1/2 db steps
Status = QueryPropertyRange(
pVirtualNodeData->pFileObject,
&KSPROPSETID_Audio,
KSPROPERTY_AUDIO_VOLUMELEVEL,
pVirtualNodeData->NodeId,
&pPropertyDescription);
if(!NT_SUCCESS(Status)) {
goto exit;
}
if((pPropertyDescription->MembersListCount == 0) ||
(!IsEqualGUID(
&pPropertyDescription->PropTypeSet.Set,
&KSPROPTYPESETID_General)) ||
(pPropertyDescription->PropTypeSet.Id != VT_I4)) {
Status = STATUS_NOT_SUPPORTED;
goto exit;
}
pMemberHeader = (PKSPROPERTY_MEMBERSHEADER)(pPropertyDescription + 1);
if(pMemberHeader->MembersFlags & KSPROPERTY_MEMBER_STEPPEDRANGES) {
pSteppingLong = (PKSPROPERTY_STEPPING_LONG)(pMemberHeader + 1);
pVirtualNodeData->MinimumValue = pSteppingLong->Bounds.SignedMinimum;
pVirtualNodeData->MaximumValue = pSteppingLong->Bounds.SignedMaximum;
pVirtualNodeData->Steps = pSteppingLong->SteppingDelta;
}
else {
Trap();
Status = STATUS_NOT_SUPPORTED;
goto exit;
}
exit:
delete pPropertyDescription;
return(STATUS_SUCCESS);
}
NTSTATUS
QueryPropertyRange(
PFILE_OBJECT pFileObject,
CONST GUID *pguidPropertySet,
ULONG ulPropertyId,
ULONG ulNodeId,
PKSPROPERTY_DESCRIPTION *ppPropertyDescription
)
{
KSPROPERTY_DESCRIPTION PropertyDescription;
KSNODEPROPERTY NodeProperty;
ULONG BytesReturned;
NTSTATUS Status;
NodeProperty.Property.Set = *pguidPropertySet;
NodeProperty.Property.Id = ulPropertyId;
NodeProperty.Property.Flags =
KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_TOPOLOGY;
NodeProperty.NodeId = ulNodeId;
NodeProperty.Reserved = 0;
AssertFileObject(pFileObject);
Status = KsSynchronousIoControlDevice(
pFileObject,
KernelMode,
IOCTL_KS_PROPERTY,
&NodeProperty,
sizeof(NodeProperty),
&PropertyDescription,
sizeof(PropertyDescription),
&BytesReturned);
if(!NT_SUCCESS(Status)) {
goto exit;
}
ASSERT(BytesReturned == sizeof(PropertyDescription));
*ppPropertyDescription =
(PKSPROPERTY_DESCRIPTION)new BYTE[PropertyDescription.DescriptionSize];
if(*ppPropertyDescription == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
AssertFileObject(pFileObject);
Status = KsSynchronousIoControlDevice(
pFileObject,
KernelMode,
IOCTL_KS_PROPERTY,
&NodeProperty,
sizeof( NodeProperty ),
*ppPropertyDescription,
PropertyDescription.DescriptionSize,
&BytesReturned);
if(!NT_SUCCESS(Status)) {
delete *ppPropertyDescription;
*ppPropertyDescription = NULL;
goto exit;
}
exit:
return(Status);
}
NTSTATUS
SetVirtualVolume(
PVIRTUAL_NODE_DATA pVirtualNodeData,
LONG Channel
)
{
KSNODEPROPERTY_AUDIO_CHANNEL NodePropertyAudioChannel;
NTSTATUS Status = STATUS_SUCCESS;
ULONG BytesReturned;
LONG lLevel;
ASSERT(pVirtualNodeData->NodeId != MAXULONG);
Assert(pVirtualNodeData->pVirtualSourceData);
NodePropertyAudioChannel.NodeProperty.Property.Set =
KSPROPSETID_Audio;
NodePropertyAudioChannel.NodeProperty.Property.Id =
KSPROPERTY_AUDIO_VOLUMELEVEL;
NodePropertyAudioChannel.NodeProperty.Property.Flags =
KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_TOPOLOGY;
NodePropertyAudioChannel.NodeProperty.NodeId = pVirtualNodeData->NodeId;
NodePropertyAudioChannel.Channel = Channel;
NodePropertyAudioChannel.Reserved = 0;
if(pVirtualNodeData->pVirtualSourceData->fMuted[Channel]) {
lLevel = LONG_MIN;
}
else {
if(pVirtualNodeData->pVirtualSourceData->lLevel[Channel] == LONG_MIN) {
lLevel = LONG_MIN;
}
else {
lLevel = pVirtualNodeData->lLevel[Channel];
if(lLevel != LONG_MIN) {
lLevel += pVirtualNodeData->pVirtualSourceData->lLevel[Channel];
lLevel = MapVirtualLevel(pVirtualNodeData, lLevel);
}
}
}
AssertFileObject(pVirtualNodeData->pFileObject);
Status = KsSynchronousIoControlDevice(
pVirtualNodeData->pFileObject,
KernelMode,
IOCTL_KS_PROPERTY,
&NodePropertyAudioChannel,
sizeof(KSNODEPROPERTY_AUDIO_CHANNEL),
&lLevel,
sizeof(LONG),
&BytesReturned);
if(!NT_SUCCESS(Status)) {
DPF4(10, "SetVirtualVolume: [%d] SNI %08x N# %u FAILED %08x",
Channel,
pVirtualNodeData->pStartNodeInstance,
pVirtualNodeData->NodeId,
Status);
}
return(STATUS_SUCCESS);
}
NTSTATUS
SetPhysicalVolume(
PGRAPH_NODE_INSTANCE pGraphNodeInstance,
PVIRTUAL_SOURCE_DATA pVirtualSourceData,
LONG Channel
)
{
KSNODEPROPERTY_AUDIO_CHANNEL NodePropertyAudioChannel;
NTSTATUS Status = STATUS_SUCCESS;
PFILE_OBJECT pFileObject;
ULONG BytesReturned;
LONG lLevel;
Assert(pGraphNodeInstance);
Assert(pVirtualSourceData);
ASSERT(IsEqualGUID(
pVirtualSourceData->pTopologyNode->pguidType,
&KSNODETYPE_VOLUME));
if(pVirtualSourceData->pTopologyNode->ulRealNodeNumber == MAXULONG) {
ASSERT(NT_SUCCESS(Status));
goto exit;
}
ASSERT(pVirtualSourceData->pTopologyNode->iVirtualSource <
gcVirtualSources);
Status = pGraphNodeInstance->GetTopologyNodeFileObject(
&pFileObject,
pGraphNodeInstance->paulNodeNumber[
pVirtualSourceData->pTopologyNode->iVirtualSource]);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
NodePropertyAudioChannel.NodeProperty.Property.Set =
KSPROPSETID_Audio;
NodePropertyAudioChannel.NodeProperty.Property.Id =
KSPROPERTY_AUDIO_VOLUMELEVEL;
NodePropertyAudioChannel.NodeProperty.Property.Flags =
KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_TOPOLOGY;
NodePropertyAudioChannel.NodeProperty.NodeId =
pVirtualSourceData->pTopologyNode->ulRealNodeNumber;
NodePropertyAudioChannel.Channel = Channel;
NodePropertyAudioChannel.Reserved = 0;
if(pVirtualSourceData->fMuted[Channel]) {
lLevel = LONG_MIN;
}
else {
lLevel = pVirtualSourceData->lLevel[Channel];
}
AssertFileObject(pFileObject);
Status = KsSynchronousIoControlDevice(
pFileObject,
KernelMode,
IOCTL_KS_PROPERTY,
&NodePropertyAudioChannel,
sizeof(KSNODEPROPERTY_AUDIO_CHANNEL),
&lLevel,
sizeof(LONG),
&BytesReturned);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
exit:
if(!NT_SUCCESS(Status)) {
DPF2(10, "SetPhysicalVolume: [%d] FAILED %08x", Channel, Status);
}
return(Status);
}
LONG
MapVirtualLevel(
PVIRTUAL_NODE_DATA pVirtualNodeData,
LONG lLevel
)
{
DPF4(100, "MapVirtualLevel: from %d max %d min %d step %d",
lLevel / 65536,
pVirtualNodeData->pVirtualSourceData->MaximumValue / 65536,
pVirtualNodeData->pVirtualSourceData->MinimumValue / 65536,
pVirtualNodeData->pVirtualSourceData->Steps / 65536);
if(lLevel != LONG_MIN) {
lLevel += pVirtualNodeData->MaximumValue -
pVirtualNodeData->pVirtualSourceData->MaximumValue;
}
DPF4(100, "MapVirtualLevel: to %d max %d min %d step %d",
lLevel / 65536,
pVirtualNodeData->MaximumValue / 65536,
pVirtualNodeData->MinimumValue / 65536,
pVirtualNodeData->Steps / 65536);
return(lLevel);
}
NTSTATUS
GetVolumeNodeNumber(
PPIN_INSTANCE pPinInstance,
PVIRTUAL_SOURCE_DATA pVirtualSourceData OPTIONAL
)
{
PSTART_NODE pStartNode;
NTSTATUS Status = STATUS_SUCCESS;
KSAUDIO_MIXCAP_TABLE AudioMixCapTable;
// This indicates to GetSuperMixCaps to only get in/out channels
PKSAUDIO_MIXCAP_TABLE pAudioMixCapTable = &AudioMixCapTable;
Assert(pPinInstance);
Assert(pPinInstance->pFilterInstance);
Assert(pPinInstance->pFilterInstance->pGraphNodeInstance);
if(pPinInstance->ulVolumeNodeNumber == MAXULONG) {
Assert(pPinInstance->pStartNodeInstance);
pStartNode = pPinInstance->pStartNodeInstance->pStartNode;
Assert(pStartNode);
Assert(pStartNode->GetStartInfo());
pPinInstance->ulVolumeNodeNumber =
pStartNode->GetStartInfo()->ulVolumeNodeNumberPre;
if(pStartNode->GetStartInfo()->ulVolumeNodeNumberSuperMix != MAXULONG &&
pStartNode->GetStartInfo()->ulVolumeNodeNumberPost != MAXULONG) {
Status = GetSuperMixCaps(
&pAudioMixCapTable,
pPinInstance->pStartNodeInstance,
pStartNode->GetStartInfo()->ulVolumeNodeNumberSuperMix);
if(!NT_SUCCESS(Status)) {
Status = STATUS_SUCCESS;
goto exit;
}
if(pAudioMixCapTable->OutputChannels != 1) {
pPinInstance->ulVolumeNodeNumber =
pStartNode->GetStartInfo()->ulVolumeNodeNumberPost;
if(pVirtualSourceData != NULL) {
pVirtualSourceData->cChannels =
pAudioMixCapTable->OutputChannels;
}
}
}
DPF2(100, "GetVolumeNodeNumber: SN %08x %02x",
pStartNode,
pPinInstance->ulVolumeNodeNumber);
}
exit:
return(Status);
}
NTSTATUS
GetSuperMixCaps(
OUT PKSAUDIO_MIXCAP_TABLE *ppAudioMixCapTable,
IN PSTART_NODE_INSTANCE pStartNodeInstance,
IN ULONG NodeId
)
{
KSAUDIO_MIXCAP_TABLE AudioMixCapTable;
NTSTATUS Status = STATUS_SUCCESS;
KSNODEPROPERTY NodeProperty;
PFILE_OBJECT pFileObject;
ULONG BytesReturned;
ULONG cb;
Assert(pStartNodeInstance);
ASSERT(NodeId != MAXULONG);
Status = pStartNodeInstance->GetTopologyNodeFileObject(
&pFileObject,
NodeId);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
NodeProperty.Property.Set = KSPROPSETID_Audio;
NodeProperty.Property.Id = KSPROPERTY_AUDIO_MIX_LEVEL_CAPS;
NodeProperty.Property.Flags =
KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_TOPOLOGY;
NodeProperty.NodeId = pStartNodeInstance->pPinInstance->pFilterInstance->
pGraphNodeInstance->papTopologyNode[NodeId]->ulRealNodeNumber;
NodeProperty.Reserved = 0;
ASSERT(NodeProperty.NodeId != MAXULONG);
AssertFileObject(pFileObject);
Status = KsSynchronousIoControlDevice(
pFileObject,
KernelMode,
IOCTL_KS_PROPERTY,
&NodeProperty,
sizeof(KSNODEPROPERTY),
&AudioMixCapTable,
sizeof(KSAUDIO_MIXCAP_TABLE) - sizeof(KSAUDIO_MIX_CAPS),
&BytesReturned);
if(!NT_SUCCESS(Status)) {
goto exit;
}
if(*ppAudioMixCapTable != NULL) {
**ppAudioMixCapTable = AudioMixCapTable;
ASSERT(NT_SUCCESS(Status));
goto exit;
}
cb = (AudioMixCapTable.InputChannels *
AudioMixCapTable.OutputChannels *
sizeof(KSAUDIO_MIX_CAPS)) +
sizeof(KSAUDIO_MIXCAP_TABLE) - sizeof(KSAUDIO_MIX_CAPS);
*ppAudioMixCapTable = (PKSAUDIO_MIXCAP_TABLE)new BYTE[cb];
if(*ppAudioMixCapTable == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
AssertFileObject(pFileObject);
Status = KsSynchronousIoControlDevice(
pFileObject,
KernelMode,
IOCTL_KS_PROPERTY,
&NodeProperty,
sizeof(KSNODEPROPERTY),
*ppAudioMixCapTable,
cb,
&BytesReturned);
if(!NT_SUCCESS(Status)) {
Trap();
goto exit;
}
exit:
return(Status);
}