//--------------------------------------------------------------------------- // // 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); // // Store the topologyNode in virtualsource structures for further usage. // 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); // // VirtualSources are not created if there is already an active pin // instance on this filter. // if(!pFilterInstance->IsChildInstance()) { DPF(5, "CreateVirtualSource: FAILED - open pin instances"); Status = STATUS_INVALID_DEVICE_REQUEST; goto exit; } // // Make sure that this is not a duplicate. // 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); // // ISSUE: 03/07/2002 These checks are totally irrelevant. // KS would have never called this functions if these conditions // have not been met before. // 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; 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( &AudioMixCapTable, pPinInstance->pStartNodeInstance, pStartNode->GetStartInfo()->ulVolumeNodeNumberSuperMix); if(!NT_SUCCESS(Status)) { Status = STATUS_SUCCESS; goto exit; } if(AudioMixCapTable.OutputChannels != 1) { pPinInstance->ulVolumeNodeNumber = pStartNode->GetStartInfo()->ulVolumeNodeNumberPost; if(pVirtualSourceData != NULL) { pVirtualSourceData->cChannels = AudioMixCapTable.OutputChannels; } } } DPF2(100, "GetVolumeNodeNumber: SN %08x %02x", pStartNode, pPinInstance->ulVolumeNodeNumber); } exit: return(Status); } NTSTATUS GetSuperMixCaps( OUT PKSAUDIO_MIXCAP_TABLE pAudioMixCapTable, IN PSTART_NODE_INSTANCE pStartNodeInstance, IN ULONG NodeId ) { NTSTATUS Status = STATUS_SUCCESS; KSNODEPROPERTY NodeProperty; PFILE_OBJECT pFileObject; ULONG BytesReturned; 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), pAudioMixCapTable, sizeof(KSAUDIO_MIXCAP_TABLE) - sizeof(KSAUDIO_MIX_CAPS), &BytesReturned); exit: return(Status); }