/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Kernel Streaming * FILE: lib/drivers/sound/mmixer/controls.c * PURPOSE: Mixer Control Iteration Functions * PROGRAMMER: Johannes Anderwald */ #include "precomp.h" #define YDEBUG #include const GUID KSNODETYPE_DESKTOP_MICROPHONE = {0xDFF21BE2, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; const GUID KSNODETYPE_LEGACY_AUDIO_CONNECTOR = {0xDFF21FE4, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; const GUID KSNODETYPE_TELEPHONE = {0xDFF21EE2, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; const GUID KSNODETYPE_PHONE_LINE = {0xDFF21EE1, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; const GUID KSNODETYPE_DOWN_LINE_PHONE = {0xDFF21EE3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; const GUID KSNODETYPE_DESKTOP_SPEAKER = {0xDFF21CE4, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; const GUID KSNODETYPE_ROOM_SPEAKER = {0xDFF21CE5, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; const GUID KSNODETYPE_COMMUNICATION_SPEAKER = {0xDFF21CE6, 0xF70F, 0x11D0, {0xB9,0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; const GUID KSNODETYPE_HEADPHONES = {0xDFF21CE2, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; const GUID KSNODETYPE_HEAD_MOUNTED_DISPLAY_AUDIO = {0xDFF21CE3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; const GUID KSNODETYPE_MICROPHONE = {0xDFF21BE1, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9,0x22, 0x31, 0x96}}; const GUID KSCATEGORY_AUDIO = {0x6994AD04L, 0x93EF, 0x11D0, {0xA3, 0xCC, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; const GUID KSNODETYPE_SPDIF_INTERFACE = {0xDFF21FE5, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; const GUID KSNODETYPE_ANALOG_CONNECTOR = {0xDFF21FE1, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; const GUID KSNODETYPE_SPEAKER = {0xDFF21CE1, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; const GUID KSNODETYPE_CD_PLAYER = {0xDFF220E3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; const GUID KSNODETYPE_SYNTHESIZER = {0xDFF220F3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; const GUID KSNODETYPE_LINE_CONNECTOR = {0xDFF21FE3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0,0xC9, 0x22, 0x31, 0x96}}; const GUID PINNAME_VIDEO_CAPTURE = {0xfb6c4281, 0x353, 0x11d1, {0x90, 0x5f, 0x0, 0x0, 0xc0, 0xcc, 0x16, 0xba}}; MIXER_STATUS MMixerAddMixerControl( IN PMIXER_CONTEXT MixerContext, IN LPMIXER_INFO MixerInfo, IN HANDLE hMixer, IN PTOPOLOGY Topology, IN ULONG NodeIndex, IN LPMIXERLINE_EXT MixerLine, IN ULONG MaxChannels) { LPGUID NodeType; KSP_NODE Node; ULONG BytesReturned; MIXER_STATUS Status; LPWSTR Name; LPMIXERCONTROL_EXT MixerControl; /* allocate mixer control */ MixerControl = MixerContext->Alloc(sizeof(MIXERCONTROL_EXT)); if (!MixerControl) { /* no memory */ return MM_STATUS_NO_MEMORY; } /* initialize mixer control */ MixerControl->hDevice = hMixer; MixerControl->NodeID = NodeIndex; MixerControl->ExtraData = NULL; MixerControl->Control.cbStruct = sizeof(MIXERCONTROLW); MixerControl->Control.dwControlID = MixerInfo->ControlId; /* get node type */ NodeType = MMixerGetNodeTypeFromTopology(Topology, NodeIndex); /* store control type */ MixerControl->Control.dwControlType = MMixerGetControlTypeFromTopologyNode(NodeType); MixerControl->Control.fdwControl = (MaxChannels > 1 ? 0 : MIXERCONTROL_CONTROLF_UNIFORM); MixerControl->Control.cMultipleItems = 0; /* setup request to retrieve name */ Node.NodeId = NodeIndex; Node.Property.Id = KSPROPERTY_TOPOLOGY_NAME; Node.Property.Flags = KSPROPERTY_TYPE_GET; Node.Property.Set = KSPROPSETID_Topology; Node.Reserved = 0; /* get node name size */ Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), NULL, 0, &BytesReturned); if (Status == MM_STATUS_MORE_ENTRIES) { ASSERT(BytesReturned != 0); Name = (LPWSTR)MixerContext->Alloc(BytesReturned); if (!Name) { /* not enough memory */ return MM_STATUS_NO_MEMORY; } /* get node name */ Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), (LPVOID)Name, BytesReturned, &BytesReturned); if (Status == MM_STATUS_SUCCESS) { MixerContext->Copy(MixerControl->Control.szShortName, Name, (min(MIXER_SHORT_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR)); MixerControl->Control.szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0'; MixerContext->Copy(MixerControl->Control.szName, Name, (min(MIXER_LONG_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR)); MixerControl->Control.szName[MIXER_LONG_NAME_CHARS-1] = L'\0'; } /* free name buffer */ MixerContext->Free(Name); } /* increment control count */ MixerInfo->ControlId++; /* insert control */ InsertTailList(&MixerLine->ControlsList, &MixerControl->Entry); if (MixerControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX) { ULONG NodesCount; PULONG Nodes; /* allocate topology nodes array */ Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &Nodes); if (Status != MM_STATUS_SUCCESS) { /* out of memory */ return STATUS_NO_MEMORY; } /* get connected node count */ MMixerGetNextNodesFromNodeIndex(MixerContext, Topology, NodeIndex, TRUE, &NodesCount, Nodes); /* TODO */ MixerContext->Free(Nodes); /* setup mux bounds */ MixerControl->Control.Bounds.dwMinimum = 0; MixerControl->Control.Bounds.dwMaximum = NodesCount - 1; MixerControl->Control.Metrics.dwReserved[0] = NodesCount; MixerControl->Control.cMultipleItems = NodesCount; MixerControl->Control.fdwControl |= MIXERCONTROL_CONTROLF_UNIFORM | MIXERCONTROL_CONTROLF_MULTIPLE; } else if (MixerControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) { MixerControl->Control.Bounds.dwMinimum = 0; MixerControl->Control.Bounds.dwMaximum = 1; } else if (MixerControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_ONOFF) { /* only needs to set bounds */ MixerControl->Control.Bounds.dwMinimum = 0; MixerControl->Control.Bounds.dwMaximum = 1; } else if (MixerControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME) { KSNODEPROPERTY_AUDIO_CHANNEL Property; ULONG Length; PKSPROPERTY_DESCRIPTION Desc; PKSPROPERTY_MEMBERSHEADER Members; PKSPROPERTY_STEPPING_LONG Range; MixerControl->Control.Bounds.dwMinimum = 0; MixerControl->Control.Bounds.dwMaximum = 0xFFFF; MixerControl->Control.Metrics.cSteps = 0xC0; /* FIXME */ Length = sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) + sizeof(KSPROPERTY_STEPPING_LONG); Desc = (PKSPROPERTY_DESCRIPTION)MixerContext->Alloc(Length); ASSERT(Desc); /* setup the request */ RtlZeroMemory(&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL)); Property.NodeProperty.NodeId = NodeIndex; Property.NodeProperty.Property.Id = KSPROPERTY_AUDIO_VOLUMELEVEL; Property.NodeProperty.Property.Flags = KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_TOPOLOGY; Property.NodeProperty.Property.Set = KSPROPSETID_Audio; /* get node volume level info */ Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL), Desc, Length, &BytesReturned); if (Status == MM_STATUS_SUCCESS) { LPMIXERVOLUME_DATA VolumeData; ULONG Steps, MaxRange, Index; LONG Value; Members = (PKSPROPERTY_MEMBERSHEADER)(Desc + 1); Range = (PKSPROPERTY_STEPPING_LONG)(Members + 1); DPRINT("NodeIndex %u Range Min %d Max %d Steps %x UMin %x UMax %x\n", NodeIndex, Range->Bounds.SignedMinimum, Range->Bounds.SignedMaximum, Range->SteppingDelta, Range->Bounds.UnsignedMinimum, Range->Bounds.UnsignedMaximum); MaxRange = Range->Bounds.UnsignedMaximum - Range->Bounds.UnsignedMinimum; if (MaxRange) { ASSERT(MaxRange); VolumeData = (LPMIXERVOLUME_DATA)MixerContext->Alloc(sizeof(MIXERVOLUME_DATA)); if (!VolumeData) return MM_STATUS_NO_MEMORY; Steps = MaxRange / Range->SteppingDelta + 1; /* store mixer control info there */ VolumeData->Header.dwControlID = MixerControl->Control.dwControlID; VolumeData->SignedMaximum = Range->Bounds.SignedMaximum; VolumeData->SignedMinimum = Range->Bounds.SignedMinimum; VolumeData->SteppingDelta = Range->SteppingDelta; VolumeData->ValuesCount = Steps; VolumeData->InputSteppingDelta = 0x10000 / Steps; VolumeData->Values = (PLONG)MixerContext->Alloc(sizeof(LONG) * Steps); if (!VolumeData->Values) { MixerContext->Free(Desc); MixerContext->Free(VolumeData); return MM_STATUS_NO_MEMORY; } Value = Range->Bounds.SignedMinimum; for(Index = 0; Index < Steps; Index++) { VolumeData->Values[Index] = Value; Value += Range->SteppingDelta; } MixerControl->ExtraData = VolumeData; } } MixerContext->Free(Desc); } DPRINT("Status %x Name %S\n", Status, MixerControl->Control.szName); return MM_STATUS_SUCCESS; } MIXER_STATUS MMixerCreateDestinationLine( IN PMIXER_CONTEXT MixerContext, IN LPMIXER_INFO MixerInfo, IN ULONG bInputMixer, IN LPWSTR LineName) { LPMIXERLINE_EXT DestinationLine; /* allocate a mixer destination line */ DestinationLine = (LPMIXERLINE_EXT) MixerContext->Alloc(sizeof(MIXERLINE_EXT)); if (!MixerInfo) { /* no memory */ return MM_STATUS_NO_MEMORY; } /* initialize mixer destination line */ DestinationLine->Line.cbStruct = sizeof(MIXERLINEW); DestinationLine->Line.cChannels = 2; /* FIXME */ DestinationLine->Line.cConnections = 0; DestinationLine->Line.cControls = 0; DestinationLine->Line.dwComponentType = (bInputMixer == 0 ? MIXERLINE_COMPONENTTYPE_DST_SPEAKERS : MIXERLINE_COMPONENTTYPE_DST_WAVEIN); DestinationLine->Line.dwDestination = MixerInfo->MixCaps.cDestinations; DestinationLine->Line.dwLineID = MixerInfo->MixCaps.cDestinations + DESTINATION_LINE; DestinationLine->Line.dwSource = MAXULONG; DestinationLine->Line.dwUser = 0; DestinationLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE; if (LineName) { MixerContext->Copy(DestinationLine->Line.szShortName, LineName, (min(MIXER_SHORT_NAME_CHARS, wcslen(LineName)+1)) * sizeof(WCHAR)); DestinationLine->Line.szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0'; MixerContext->Copy(DestinationLine->Line.szName, LineName, (min(MIXER_LONG_NAME_CHARS, wcslen(LineName)+1)) * sizeof(WCHAR)); DestinationLine->Line.szName[MIXER_LONG_NAME_CHARS-1] = L'\0'; } DestinationLine->Line.Target.dwType = (bInputMixer == 0 ? MIXERLINE_TARGETTYPE_WAVEOUT : MIXERLINE_TARGETTYPE_WAVEIN); DestinationLine->Line.Target.dwDeviceID = 0; //FIXME DestinationLine->Line.Target.wMid = MixerInfo->MixCaps.wMid; DestinationLine->Line.Target.wPid = MixerInfo->MixCaps.wPid; DestinationLine->Line.Target.vDriverVersion = MixerInfo->MixCaps.vDriverVersion; ASSERT(MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] == 0); wcscpy(DestinationLine->Line.Target.szPname, MixerInfo->MixCaps.szPname); /* initialize extra line */ InitializeListHead(&DestinationLine->ControlsList); /* insert into mixer info */ InsertTailList(&MixerInfo->LineList, &DestinationLine->Entry); /* increment destination count */ MixerInfo->MixCaps.cDestinations++; /* done */ return MM_STATUS_SUCCESS; } MIXER_STATUS MMixerGetPinName( IN PMIXER_CONTEXT MixerContext, IN LPMIXER_INFO MixerInfo, IN HANDLE hMixer, IN ULONG PinId, IN OUT LPWSTR * OutBuffer) { KSP_PIN Pin; ULONG BytesReturned; LPWSTR Buffer; MIXER_STATUS Status; /* prepare pin */ Pin.PinId = PinId; Pin.Reserved = 0; Pin.Property.Flags = KSPROPERTY_TYPE_GET; Pin.Property.Set = KSPROPSETID_Pin; Pin.Property.Id = KSPROPERTY_PIN_NAME; /* try get pin name size */ Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), NULL, 0, &BytesReturned); /* check if buffer overflowed */ if (Status == MM_STATUS_MORE_ENTRIES) { /* allocate buffer */ Buffer = (LPWSTR)MixerContext->Alloc(BytesReturned); if (!Buffer) { /* out of memory */ return MM_STATUS_NO_MEMORY; } /* try get pin name */ Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (PVOID)Buffer, BytesReturned, &BytesReturned); if (Status != MM_STATUS_SUCCESS) { /* failed to get pin name */ MixerContext->Free((PVOID)Buffer); return Status; } /* successfully obtained pin name */ *OutBuffer = Buffer; return MM_STATUS_SUCCESS; } /* failed to get pin name */ return Status; } MIXER_STATUS MMixerBuildMixerDestinationLine( IN PMIXER_CONTEXT MixerContext, IN OUT LPMIXER_INFO MixerInfo, IN HANDLE hMixer, IN ULONG PinId, IN ULONG bInput) { LPWSTR PinName; MIXER_STATUS Status; /* try get pin name */ Status = MMixerGetPinName(MixerContext, MixerInfo, hMixer, PinId, &PinName); if (Status == MM_STATUS_SUCCESS) { /* create mixer destination line */ Status = MMixerCreateDestinationLine(MixerContext, MixerInfo, bInput, PinName); /* free pin name */ MixerContext->Free(PinName); } else { /* create mixer destination line unlocalized */ Status = MMixerCreateDestinationLine(MixerContext, MixerInfo, bInput, L"No Name"); } return Status; } MIXER_STATUS MMixerBuildTopology( IN PMIXER_CONTEXT MixerContext, IN LPMIXER_DATA MixerData, OUT PTOPOLOGY * OutTopology) { ULONG PinsCount; PKSMULTIPLE_ITEM NodeTypes = NULL; PKSMULTIPLE_ITEM NodeConnections = NULL; MIXER_STATUS Status; if (MixerData->Topology) { /* re-use existing topology */ *OutTopology = MixerData->Topology; return MM_STATUS_SUCCESS; } /* get connected filter pin count */ PinsCount = MMixerGetFilterPinCount(MixerContext, MixerData->hDevice); if (!PinsCount) { /* referenced filter does not have any pins */ return MM_STATUS_UNSUCCESSFUL; } /* get topology node types */ Status = MMixerGetFilterTopologyProperty(MixerContext, MixerData->hDevice, KSPROPERTY_TOPOLOGY_NODES, &NodeTypes); if (Status != MM_STATUS_SUCCESS) { /* failed to get topology node types */ return Status; } /* get topology connections */ Status = MMixerGetFilterTopologyProperty(MixerContext, MixerData->hDevice, KSPROPERTY_TOPOLOGY_CONNECTIONS, &NodeConnections); if (Status != MM_STATUS_SUCCESS) { /* failed to get topology connections */ MixerContext->Free(NodeTypes); return Status; } /* create a topology */ Status = MMixerCreateTopology(MixerContext, PinsCount, NodeConnections, NodeTypes, OutTopology); /* free node types & connections */ MixerContext->Free(NodeConnections); MixerContext->Free(NodeTypes); if (Status == MM_STATUS_SUCCESS) { /* store topology object */ MixerData->Topology = *OutTopology; } /* done */ return Status; } MIXER_STATUS MMixerCountMixerControls( IN PMIXER_CONTEXT MixerContext, IN PTOPOLOGY Topology, IN ULONG PinId, IN ULONG bInputMixer, IN ULONG bUpStream, OUT PULONG OutNodesCount, OUT PULONG OutNodes, OUT PULONG OutLineTerminator) { PULONG Nodes; ULONG NodesCount, NodeIndex, Count, bTerminator; MIXER_STATUS Status; /* allocate an array to store all nodes which are upstream of this pin */ Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &Nodes); if (Status != MM_STATUS_SUCCESS) { /* out of memory */ return STATUS_NO_MEMORY; } /* mark result array as zero */ *OutNodesCount = 0; /* get next nodes */ MMixerGetNextNodesFromPinIndex(MixerContext, Topology, PinId, bUpStream, &NodesCount, Nodes); if (NodesCount == 0) { /* a pin which is not connected from any node * a) it is a topology bug (driver bug) * b) the request is from an alternative mixer alternative mixer code scans all pins which have not been used and tries to build lines */ DPRINT1("MMixerCountMixerControls PinId %lu is not connected by any node\n", PinId); MMixerPrintTopology(Topology); return MM_STATUS_UNSUCCESSFUL; } /* assume no topology split before getting line terminator */ ASSERT(NodesCount == 1); /* get first node */ NodeIndex = Nodes[0]; Count = 0; do { /* check if the node is a terminator */ MMixerIsNodeTerminator(Topology, NodeIndex, &bTerminator); if (bTerminator) { /* found terminator */ if (bInputMixer) { /* add mux source for source destination line */ OutNodes[Count] = NodeIndex; Count++; } break; } /* store node id */ OutNodes[Count] = NodeIndex; /* increment node count */ Count++; /* get next nodes upstream */ MMixerGetNextNodesFromNodeIndex(MixerContext, Topology, NodeIndex, bUpStream, &NodesCount, Nodes); if (NodesCount != 1) { DPRINT("PinId %lu bInputMixer %lu bUpStream %lu NodeIndex %lu is not connected", PinId, bInputMixer, bUpStream, NodeIndex); break; } ASSERT(NodesCount == 1); /* use first index */ NodeIndex = Nodes[0]; }while(TRUE); /* free node index */ MixerContext->Free(Nodes); /* store nodes count */ *OutNodesCount = Count; /* store line terminator */ *OutLineTerminator = NodeIndex; /* done */ return MM_STATUS_SUCCESS; } MIXER_STATUS MMixerGetChannelCountEnhanced( IN PMIXER_CONTEXT MixerContext, IN LPMIXER_INFO MixerInfo, IN HANDLE hMixer, IN ULONG NodeId, OUT PULONG MaxChannels) { KSPROPERTY_DESCRIPTION Description; PKSPROPERTY_DESCRIPTION NewDescription; PKSPROPERTY_MEMBERSHEADER Header; ULONG BytesReturned; KSP_NODE Request; MIXER_STATUS Status; /* try #1 obtain it via description */ Request.NodeId = NodeId; Request.Reserved = 0; Request.Property.Set = KSPROPSETID_Audio; Request.Property.Flags = KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_TOPOLOGY; Request.Property.Id = KSPROPERTY_AUDIO_VOLUMELEVEL; /* get description */ Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSP_NODE), (PVOID)&Description, sizeof(KSPROPERTY_DESCRIPTION), &BytesReturned); if (Status == MM_STATUS_SUCCESS) { if (Description.DescriptionSize >= sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) && (Description.MembersListCount > 0)) { /* allocate new description */ NewDescription = MixerContext->Alloc(Description.DescriptionSize); if (!NewDescription) { /* not enough memory */ return MM_STATUS_NO_MEMORY; } /* get description */ Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSP_NODE), (PVOID)NewDescription, Description.DescriptionSize, &BytesReturned); if (Status == MM_STATUS_SUCCESS) { /* get header */ Header = (PKSPROPERTY_MEMBERSHEADER)(NewDescription + 1); if (Header->Flags & KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL) { /* found enhanced flag */ ASSERT(Header->MembersCount > 1); /* store channel count */ *MaxChannels = Header->MembersCount; /* free description */ MixerContext->Free(NewDescription); /* done */ return MM_STATUS_SUCCESS; } } /* free description */ MixerContext->Free(NewDescription); } } /* failed to get channel count enhanced */ return MM_STATUS_UNSUCCESSFUL; } VOID MMixerGetChannelCountLegacy( IN PMIXER_CONTEXT MixerContext, IN LPMIXER_INFO MixerInfo, IN HANDLE hMixer, IN ULONG NodeId, OUT PULONG MaxChannels) { ULONG BytesReturned; MIXER_STATUS Status; KSNODEPROPERTY_AUDIO_CHANNEL Channel; LONG Volume; /* setup request */ Channel.Reserved = 0; Channel.NodeProperty.NodeId = NodeId; Channel.NodeProperty.Reserved = 0; Channel.NodeProperty.Property.Flags = KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_TOPOLOGY; Channel.NodeProperty.Property.Set = KSPROPSETID_Audio; Channel.Channel = 0; Channel.NodeProperty.Property.Id = KSPROPERTY_AUDIO_VOLUMELEVEL; do { /* get channel volume */ Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Channel, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL), (PVOID)&Volume, sizeof(LONG), &BytesReturned); if (Status != MM_STATUS_SUCCESS) break; /* increment channel count */ Channel.Channel++; }while(TRUE); /* store channel count */ *MaxChannels = Channel.Channel; } VOID MMixerGetMaxChannelsForNode( IN PMIXER_CONTEXT MixerContext, IN LPMIXER_INFO MixerInfo, IN HANDLE hMixer, IN ULONG NodeId, OUT PULONG MaxChannels) { MIXER_STATUS Status; /* try to get it enhanced */ Status = MMixerGetChannelCountEnhanced(MixerContext, MixerInfo, hMixer, NodeId, MaxChannels); if (Status != MM_STATUS_SUCCESS) { /* get it old-fashioned way */ MMixerGetChannelCountLegacy(MixerContext, MixerInfo, hMixer, NodeId, MaxChannels); } } MIXER_STATUS MMixerAddMixerControlsToMixerLineByNodeIndexArray( IN PMIXER_CONTEXT MixerContext, IN LPMIXER_INFO MixerInfo, IN HANDLE hMixer, IN PTOPOLOGY Topology, IN OUT LPMIXERLINE_EXT DstLine, IN ULONG NodesCount, IN PULONG Nodes) { ULONG Index, Count, bReserved; MIXER_STATUS Status; LPGUID NodeType; ULONG MaxChannels; /* initialize control count */ Count = 0; for(Index = 0; Index < NodesCount; Index++) { /* check if the node has already been reserved to a line */ MMixerIsTopologyNodeReserved(Topology, Nodes[Index], &bReserved); #if 0 /* MS lies */ if (bReserved) { /* node is already used, skip it */ continue; } #endif /* set node status as used */ MMixerSetTopologyNodeReserved(Topology, Nodes[Index]); /* query node type */ NodeType = MMixerGetNodeTypeFromTopology(Topology, Nodes[Index]); if (IsEqualGUIDAligned(NodeType, &KSNODETYPE_VOLUME)) { /* calculate maximum channel count for node */ MMixerGetMaxChannelsForNode(MixerContext, MixerInfo, hMixer, Nodes[Index], &MaxChannels); DPRINT("NodeId %lu MaxChannels %lu Line %S Id %lu\n", Nodes[Index], MaxChannels, DstLine->Line.szName, DstLine->Line.dwLineID); /* calculate maximum channels */ DstLine->Line.cChannels = min(DstLine->Line.cChannels, MaxChannels); } else { /* use default of one channel */ MaxChannels = 1; } /* now add the mixer control */ Status = MMixerAddMixerControl(MixerContext, MixerInfo, hMixer, Topology, Nodes[Index], DstLine, MaxChannels); if (Status == MM_STATUS_SUCCESS) { /* increment control count */ Count++; } } /* store control count */ DstLine->Line.cControls = Count; /* done */ return MM_STATUS_SUCCESS; } MIXER_STATUS MMixerGetComponentAndTargetType( IN PMIXER_CONTEXT MixerContext, IN OUT LPMIXER_INFO MixerInfo, IN HANDLE hMixer, IN ULONG PinId, OUT PULONG ComponentType, OUT PULONG TargetType) { KSPIN_DATAFLOW DataFlow; KSPIN_COMMUNICATION Communication; MIXER_STATUS Status; KSP_PIN Request; ULONG BytesReturned; GUID Guid; BOOLEAN BridgePin = FALSE; PKSPIN_PHYSICALCONNECTION Connection; /* first dataflow type */ Status = MMixerGetPinDataFlowAndCommunication(MixerContext, hMixer, PinId, &DataFlow, &Communication); if (Status != MM_STATUS_SUCCESS) { /* failed to get dataflow */ return Status; } /* now get pin category guid */ Request.PinId = PinId; Request.Reserved = 0; Request.Property.Flags = KSPROPERTY_TYPE_GET; Request.Property.Set = KSPROPSETID_Pin; Request.Property.Id = KSPROPERTY_PIN_CATEGORY; /* get pin category */ Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSP_PIN), &Guid, sizeof(GUID), &BytesReturned); if (Status != MM_STATUS_SUCCESS) { /* failed to get dataflow */ return Status; } /* check if it has a physical connection */ Status = MMixerGetPhysicalConnection(MixerContext, hMixer, PinId, &Connection); if (Status == MM_STATUS_SUCCESS) { /* pin is a brige pin */ BridgePin = TRUE; /* free physical connection */ MixerContext->Free(Connection); } if (DataFlow == KSPIN_DATAFLOW_IN) { if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_MICROPHONE) || IsEqualGUIDAligned(&Guid, &KSNODETYPE_DESKTOP_MICROPHONE)) { /* type microphone */ *TargetType = MIXERLINE_TARGETTYPE_WAVEIN; *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE; } else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_LEGACY_AUDIO_CONNECTOR) || IsEqualGUIDAligned(&Guid, &KSCATEGORY_AUDIO) || IsEqualGUIDAligned(&Guid, &KSNODETYPE_SPEAKER)) { /* type waveout */ *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT; *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT; } else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_CD_PLAYER)) { /* type cd player */ *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED; *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC; } else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_SYNTHESIZER)) { /* type synthesizer */ *TargetType = MIXERLINE_TARGETTYPE_MIDIOUT; *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER; } else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_LINE_CONNECTOR)) { /* type line */ *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED; *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE; } else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_TELEPHONE) || IsEqualGUIDAligned(&Guid, &KSNODETYPE_PHONE_LINE) || IsEqualGUIDAligned(&Guid, &KSNODETYPE_DOWN_LINE_PHONE)) { /* type telephone */ *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED; *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE; } else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_ANALOG_CONNECTOR)) { /* type analog */ if (BridgePin) *TargetType = MIXERLINE_TARGETTYPE_WAVEIN; else *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT; *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_ANALOG; } else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_SPDIF_INTERFACE)) { /* type analog */ if (BridgePin) *TargetType = MIXERLINE_TARGETTYPE_WAVEIN; else *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT; *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_DIGITAL; } else { /* unknown type */ *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED; *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED; DPRINT1("Unknown Category for PinId %lu BridgePin %lu\n", PinId, BridgePin); } } else { if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_SPEAKER) || IsEqualGUIDAligned(&Guid, &KSNODETYPE_DESKTOP_SPEAKER) || IsEqualGUIDAligned(&Guid, &KSNODETYPE_ROOM_SPEAKER) || IsEqualGUIDAligned(&Guid, &KSNODETYPE_COMMUNICATION_SPEAKER)) { /* type waveout */ *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT; *ComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS; } else if (IsEqualGUIDAligned(&Guid, &KSCATEGORY_AUDIO) || IsEqualGUIDAligned(&Guid, &PINNAME_CAPTURE)) { /* type wavein */ *TargetType = MIXERLINE_TARGETTYPE_WAVEIN; *ComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN; } else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_HEADPHONES) || IsEqualGUIDAligned(&Guid, &KSNODETYPE_HEAD_MOUNTED_DISPLAY_AUDIO)) { /* type head phones */ *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT; *ComponentType = MIXERLINE_COMPONENTTYPE_DST_HEADPHONES; } else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_TELEPHONE) || IsEqualGUIDAligned(&Guid, &KSNODETYPE_PHONE_LINE) || IsEqualGUIDAligned(&Guid, &KSNODETYPE_DOWN_LINE_PHONE)) { /* type waveout */ *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED; *ComponentType = MIXERLINE_COMPONENTTYPE_DST_TELEPHONE; } else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_ANALOG_CONNECTOR)) { /* type analog */ if (BridgePin) { *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT; *ComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS; } else { *TargetType = MIXERLINE_TARGETTYPE_WAVEIN; *ComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN; } } else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_SPDIF_INTERFACE)) { /* type spdif */ if (BridgePin) { *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT; *ComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS; } else { *TargetType = MIXERLINE_TARGETTYPE_WAVEIN; *ComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN; } } else { /* unknown type */ *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED; *ComponentType = MIXERLINE_COMPONENTTYPE_DST_UNDEFINED; DPRINT1("Unknown Category for PinId %lu BridgePin %lu\n", PinId, BridgePin); } } /* done */ return MM_STATUS_SUCCESS; } MIXER_STATUS MMixerBuildMixerSourceLine( IN PMIXER_CONTEXT MixerContext, IN OUT LPMIXER_INFO MixerInfo, IN HANDLE hMixer, IN PTOPOLOGY Topology, IN ULONG PinId, IN ULONG NodesCount, IN PULONG Nodes, IN ULONG DestinationLineID, OUT LPMIXERLINE_EXT * OutSrcLine) { LPMIXERLINE_EXT SrcLine, DstLine; LPWSTR PinName; MIXER_STATUS Status; ULONG ComponentType, TargetType; /* get component and target type */ Status = MMixerGetComponentAndTargetType(MixerContext, MixerInfo, hMixer, PinId, &ComponentType, &TargetType); if (Status != MM_STATUS_SUCCESS) { /* failed to get component status */ TargetType = MIXERLINE_TARGETTYPE_UNDEFINED; ComponentType = MIXERLINE_COMPONENTTYPE_DST_UNDEFINED; } /* construct source line */ SrcLine = (LPMIXERLINE_EXT)MixerContext->Alloc(sizeof(MIXERLINE_EXT)); if (!SrcLine) { /* no memory */ return MM_STATUS_NO_MEMORY; } /* get destination line */ DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineID); ASSERT(DstLine); /* initialize mixer src line */ SrcLine->PinId = PinId; /* initialize mixer line */ SrcLine->Line.cbStruct = sizeof(MIXERLINEW); SrcLine->Line.dwDestination = MixerInfo->MixCaps.cDestinations-1; SrcLine->Line.dwSource = DstLine->Line.cConnections; SrcLine->Line.dwLineID = (DstLine->Line.cConnections * SOURCE_LINE)+ (MixerInfo->MixCaps.cDestinations-1); SrcLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE; SrcLine->Line.dwComponentType = ComponentType; SrcLine->Line.dwUser = 0; SrcLine->Line.cChannels = DstLine->Line.cChannels; SrcLine->Line.cConnections = 0; SrcLine->Line.Target.dwType = TargetType; SrcLine->Line.Target.dwDeviceID = DstLine->Line.Target.dwDeviceID; SrcLine->Line.Target.wMid = MixerInfo->MixCaps.wMid; SrcLine->Line.Target.wPid = MixerInfo->MixCaps.wPid; SrcLine->Line.Target.vDriverVersion = MixerInfo->MixCaps.vDriverVersion; InitializeListHead(&SrcLine->ControlsList); /* copy name */ ASSERT(MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] == L'\0'); wcscpy(SrcLine->Line.Target.szPname, MixerInfo->MixCaps.szPname); /* get pin name */ Status = MMixerGetPinName(MixerContext, MixerInfo, hMixer, PinId, &PinName); if (Status == MM_STATUS_SUCCESS) { /* store pin name as line name */ MixerContext->Copy(SrcLine->Line.szShortName, PinName, (min(MIXER_SHORT_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR)); SrcLine->Line.szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0'; MixerContext->Copy(SrcLine->Line.szName, PinName, (min(MIXER_LONG_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR)); SrcLine->Line.szName[MIXER_LONG_NAME_CHARS-1] = L'\0'; /* free pin name buffer */ MixerContext->Free(PinName); } /* add the controls to mixer line */ Status = MMixerAddMixerControlsToMixerLineByNodeIndexArray(MixerContext, MixerInfo, hMixer, Topology, SrcLine, NodesCount, Nodes); if (Status != MM_STATUS_SUCCESS) { /* failed */ return Status; } /* store result */ *OutSrcLine = SrcLine; return MM_STATUS_SUCCESS; } MIXER_STATUS MMixerAddMixerSourceLines( IN PMIXER_CONTEXT MixerContext, IN OUT LPMIXER_INFO MixerInfo, IN HANDLE hMixer, IN PTOPOLOGY Topology, IN ULONG DestinationLineID, IN ULONG LineTerminator) { PULONG AllNodes, AllPins, AllPinNodes; ULONG AllNodesCount, AllPinsCount, AllPinNodesCount; ULONG Index, SubIndex, PinId, CurNode, bConnected; MIXER_STATUS Status; LPMIXERLINE_EXT DstLine, SrcLine; /* get destination line */ DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineID); ASSERT(DstLine); /* allocate an array to store all nodes which are upstream of the line terminator */ Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &AllNodes); /* check for success */ if (Status != MM_STATUS_SUCCESS) { /* out of memory */ return MM_STATUS_NO_MEMORY; } /* allocate an array to store all nodes which are downstream of a particular pin */ Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &AllPinNodes); /* allocate an array to store all pins which are upstream of this pin */ Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &AllPins); /* check for success */ if (Status != MM_STATUS_SUCCESS) { /* out of memory */ MixerContext->Free(AllNodes); return MM_STATUS_NO_MEMORY; } /* get all nodes which indirectly / directly connect to this node */ AllNodesCount = 0; MMixerGetAllUpOrDownstreamNodesFromNodeIndex(MixerContext, Topology, LineTerminator, TRUE, &AllNodesCount, AllNodes); /* get all pins which indirectly / directly connect to this node */ AllPinsCount = 0; MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, LineTerminator, TRUE, &AllPinsCount, AllPins); DPRINT("LineTerminator %lu\n", LineTerminator); DPRINT("PinCount %lu\n", AllPinsCount); DPRINT("AllNodesCount %lu\n", AllNodesCount); /* now construct the source lines which are attached to the destination line */ Index = AllPinsCount; do { /* get current pin id */ PinId = AllPins[Index - 1]; /* reset nodes count */ AllPinNodesCount = 0; /* now scan all nodes and add them to AllPinNodes array when they are connected to this pin */ for(SubIndex = 0; SubIndex < AllNodesCount; SubIndex++) { /* get current node index */ CurNode = AllNodes[SubIndex]; if (CurNode != MAXULONG && CurNode != LineTerminator) { /* check if that node is connected in some way to the current pin */ Status = MMixerIsNodeConnectedToPin(MixerContext, Topology, CurNode, PinId, TRUE, &bConnected); if (Status != MM_STATUS_SUCCESS) break; if (bConnected) { /* it is connected */ AllPinNodes[AllPinNodesCount] = CurNode; AllPinNodesCount++; /* clear current index */ AllNodes[SubIndex] = MAXULONG; } } } /* decrement pin index */ Index--; if (AllPinNodesCount) { #ifdef MMIXER_DEBUG ULONG TempIndex; #endif /* now build the mixer source line */ Status = MMixerBuildMixerSourceLine(MixerContext, MixerInfo, hMixer, Topology, PinId, AllPinNodesCount, AllPinNodes, DestinationLineID, &SrcLine); if (Status == MM_STATUS_SUCCESS) { /* insert into line list */ InsertTailList(&MixerInfo->LineList, &SrcLine->Entry); /* increment destination line count */ DstLine->Line.cConnections++; /* mark pin as reserved */ MMixerSetTopologyPinReserved(Topology, PinId); #ifdef MMIXER_DEBUG DPRINT1("Adding PinId %lu AllPinNodesCount %lu to DestinationLine %lu\n", PinId, AllPinNodesCount, DestinationLineID); for(TempIndex = 0; TempIndex < AllPinNodesCount; TempIndex++) DPRINT1("NodeIndex %lu\n", AllPinNodes[TempIndex]); #endif } } else { #ifdef MMIXER_DEBUG DPRINT1("Discarding DestinationLineID %lu PinId %lu NO NODES!\n", DestinationLineID, PinId); #endif } }while(Index != 0); return MM_STATUS_SUCCESS; } MIXER_STATUS MMixerAddMixerControlsToDestinationLine( IN PMIXER_CONTEXT MixerContext, IN OUT LPMIXER_INFO MixerInfo, IN HANDLE hMixer, IN PTOPOLOGY Topology, IN ULONG PinId, IN ULONG bInput, IN ULONG DestinationLineId, OUT PULONG OutLineTerminator) { PULONG Nodes; ULONG NodesCount, LineTerminator; MIXER_STATUS Status; LPMIXERLINE_EXT DstLine; /* allocate nodes index array */ Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &Nodes); /* check for success */ if (Status != MM_STATUS_SUCCESS) { /* out of memory */ return Status; } /* get all destination line controls */ Status = MMixerCountMixerControls(MixerContext, Topology, PinId, bInput, TRUE, &NodesCount, Nodes, &LineTerminator); /* check for success */ if (Status != MM_STATUS_SUCCESS) { /* failed to count controls */ MixerContext->Free(Nodes); return Status; } /* get destination mixer line */ DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineId); /* sanity check */ ASSERT(DstLine); if (NodesCount > 0) { /* add all nodes as mixer controls to the destination line */ Status = MMixerAddMixerControlsToMixerLineByNodeIndexArray(MixerContext, MixerInfo, hMixer, Topology, DstLine, NodesCount, Nodes); if (Status != MM_STATUS_SUCCESS) { /* failed to add controls */ MixerContext->Free(Nodes); return Status; } } /* store result */ *OutLineTerminator = LineTerminator; /* return result */ return Status; } VOID MMixerApplyOutputFilterHack( IN PMIXER_CONTEXT MixerContext, IN LPMIXER_DATA MixerData, IN HANDLE hMixer, IN OUT PULONG PinsCount, IN OUT PULONG Pins) { ULONG Count = 0, Index; MIXER_STATUS Status; PKSPIN_PHYSICALCONNECTION Connection; for(Index = 0; Index < *PinsCount; Index++) { /* check if it has a physical connection */ Status = MMixerGetPhysicalConnection(MixerContext, hMixer, Pins[Index], &Connection); if (Status == MM_STATUS_SUCCESS) { /* remove pin */ MixerContext->Copy(&Pins[Index], &Pins[Index + 1], (*PinsCount - (Index + 1)) * sizeof(ULONG)); /* free physical connection */ MixerContext->Free(Connection); /* decrement index */ Index--; /* decrement pin count */ (*PinsCount)--; } else { /* simple pin */ Count++; } } /* store result */ *PinsCount = Count; } MIXER_STATUS MMixerHandleTopologyFilter( IN PMIXER_CONTEXT MixerContext, IN PMIXER_LIST MixerList, IN LPMIXER_DATA MixerData, IN OUT LPMIXER_INFO MixerInfo, IN ULONG bInput, IN ULONG Pin) { MIXER_STATUS Status; ULONG PinsCount, LineTerminator, DestinationLineID; PULONG Pins; PTOPOLOGY Topology; /* re-use existing topology */ Topology = MixerData->Topology; if (!bInput) { /* allocate pin index array which will hold all referenced pins */ Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins); if (Status != MM_STATUS_SUCCESS) { /* failed to create topology */ return Status; } /* the mixer is an output mixer * find end pin of the node path */ PinsCount = 0; Status = MMixerGetAllUpOrDownstreamPinsFromPinIndex(MixerContext, Topology, Pin, FALSE, &PinsCount, Pins); /* check for success */ if (Status != MM_STATUS_SUCCESS) { /* failed to get end pin */ MixerContext->Free(Pins); //MMixerFreeTopology(Topology); /* return error code */ return Status; } /* HACK: * some topologies do not have strict boundaries * WorkArround: remove all pin ids which have a physical connection * because bridge pins may belong to different render paths */ MMixerApplyOutputFilterHack(MixerContext, MixerData, MixerData->hDevice, &PinsCount, Pins); /* sanity checks */ ASSERT(PinsCount != 0); if (PinsCount != 1) { DPRINT1("MMixerHandlePhysicalConnection Expected 1 pin but got %lu\n", PinsCount); } /* create destination line */ Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Pins[0], bInput); /* calculate destination line id */ DestinationLineID = (DESTINATION_LINE + MixerInfo->MixCaps.cDestinations - 1); if (Status != MM_STATUS_SUCCESS) { /* failed to build destination line */ MixerContext->Free(Pins); /* return error code */ return Status; } /* add mixer controls to destination line */ Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Topology, Pins[0], bInput, DestinationLineID, &LineTerminator); if (Status == MM_STATUS_SUCCESS) { /* now add the rest of the source lines */ Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, MixerData->hDevice, Topology, DestinationLineID, LineTerminator); } /* mark pin as consumed */ MMixerSetTopologyPinReserved(Topology, Pins[0]); /* free topology pin array */ MixerContext->Free(Pins); } else { /* calculate destination line id */ DestinationLineID = (DESTINATION_LINE + MixerInfo->MixCaps.cDestinations - 1); /* add mixer controls */ Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Topology, Pin, bInput, DestinationLineID, &LineTerminator); if (Status == MM_STATUS_SUCCESS) { /* now add the rest of the source lines */ Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, MixerData->hDevice, Topology, DestinationLineID, LineTerminator); } } return Status; } MIXER_STATUS MMixerHandlePhysicalConnection( IN PMIXER_CONTEXT MixerContext, IN PMIXER_LIST MixerList, IN LPMIXER_DATA MixerData, IN OUT LPMIXER_INFO MixerInfo, IN ULONG bInput, IN PKSPIN_PHYSICALCONNECTION OutConnection) { MIXER_STATUS Status; ULONG PinsCount, LineTerminator, DestinationLineID; PULONG Pins; PTOPOLOGY Topology; /* first try to open the connected filter */ OutConnection->SymbolicLinkName[1] = L'\\'; MixerData = MMixerGetDataByDeviceName(MixerList, OutConnection->SymbolicLinkName); /* check if the linked connection is found */ if (!MixerData) { /* filter references invalid physical connection */ return MM_STATUS_UNSUCCESSFUL; } DPRINT("Name %S, Pin %lu bInput %lu\n", OutConnection->SymbolicLinkName, OutConnection->Pin, bInput); /* sanity check */ ASSERT(MixerData->MixerInfo == NULL || MixerData->MixerInfo == MixerInfo); /* associate with mixer */ MixerData->MixerInfo = MixerInfo; if (MixerData->Topology == NULL) { /* construct new topology */ Status = MMixerBuildTopology(MixerContext, MixerData, &Topology); if (Status != MM_STATUS_SUCCESS) { /* failed to create topology */ return Status; } /* store topology */ MixerData->Topology = Topology; } else { /* re-use existing topology */ Topology = MixerData->Topology; } /* mark pin as consumed */ MMixerSetTopologyPinReserved(Topology, OutConnection->Pin); if (!bInput) { /* allocate pin index array which will hold all referenced pins */ Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins); if (Status != MM_STATUS_SUCCESS) { /* failed to create topology */ return Status; } /* the mixer is an output mixer * find end pin of the node path */ PinsCount = 0; Status = MMixerGetAllUpOrDownstreamPinsFromPinIndex(MixerContext, Topology, OutConnection->Pin, FALSE, &PinsCount, Pins); /* check for success */ if (Status != MM_STATUS_SUCCESS) { /* failed to get end pin */ MixerContext->Free(Pins); //MMixerFreeTopology(Topology); /* return error code */ return Status; } /* HACK: * some topologies do not have strict boundaries * WorkArround: remove all pin ids which have a physical connection * because bridge pins may belong to different render paths */ MMixerApplyOutputFilterHack(MixerContext, MixerData, MixerData->hDevice, &PinsCount, Pins); /* sanity checks */ ASSERT(PinsCount != 0); if (PinsCount != 1) { DPRINT1("MMixerHandlePhysicalConnection Expected 1 pin but got %lu\n", PinsCount); } /* create destination line */ Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Pins[0], bInput); /* calculate destination line id */ DestinationLineID = (DESTINATION_LINE + MixerInfo->MixCaps.cDestinations-1); if (Status != MM_STATUS_SUCCESS) { /* failed to build destination line */ MixerContext->Free(Pins); /* return error code */ return Status; } /* add mixer controls to destination line */ Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Topology, Pins[0], bInput, DestinationLineID, &LineTerminator); if (Status == MM_STATUS_SUCCESS) { /* now add the rest of the source lines */ Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, MixerData->hDevice, Topology, DestinationLineID, LineTerminator); } /* mark pin as consumed */ MMixerSetTopologyPinReserved(Topology, Pins[0]); /* free topology pin array */ MixerContext->Free(Pins); } else { /* calculate destination line id */ DestinationLineID = (DESTINATION_LINE + MixerInfo->MixCaps.cDestinations-1); /* add mixer controls */ Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Topology, OutConnection->Pin, bInput, DestinationLineID, &LineTerminator); if (Status == MM_STATUS_SUCCESS) { /* now add the rest of the source lines */ Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, MixerData->hDevice, Topology, DestinationLineID, LineTerminator); } } return Status; } MIXER_STATUS MMixerInitializeFilter( IN PMIXER_CONTEXT MixerContext, IN PMIXER_LIST MixerList, IN LPMIXER_DATA MixerData, IN LPMIXER_INFO MixerInfo, IN PTOPOLOGY Topology, IN ULONG NodeIndex, IN ULONG bInputMixer, IN OUT LPMIXER_INFO * OutMixerInfo) { ULONG Index; MIXER_STATUS Status; PKSPIN_PHYSICALCONNECTION OutConnection; ULONG * Pins; ULONG PinsFound; ULONG NewMixerInfo = FALSE; if (MixerInfo == NULL) { /* allocate a mixer info struct */ MixerInfo = (LPMIXER_INFO) MixerContext->Alloc(sizeof(MIXER_INFO)); if (!MixerInfo) { /* no memory */ return MM_STATUS_NO_MEMORY; } /* new mixer info */ NewMixerInfo = TRUE; /* intialize mixer caps */ MixerInfo->MixCaps.wMid = MM_MICROSOFT; /* FIXME */ MixerInfo->MixCaps.wPid = MM_PID_UNMAPPED; /* FIXME */ MixerInfo->MixCaps.vDriverVersion = 1; /* FIXME */ MixerInfo->MixCaps.fdwSupport = 0; MixerInfo->MixCaps.cDestinations = 0; /* get mixer name */ Status = MMixerGetDeviceName(MixerContext, MixerInfo->MixCaps.szPname, MixerData->hDeviceInterfaceKey); if (Status != MM_STATUS_SUCCESS) { /* try get name with component id */ Status = MMixerGetDeviceNameWithComponentId(MixerContext, MixerData->hDevice, MixerInfo->MixCaps.szPname); } /* initialize line list */ InitializeListHead(&MixerInfo->LineList); InitializeListHead(&MixerInfo->EventList); /* associate with mixer data */ MixerData->MixerInfo = MixerInfo; } /* store mixer info */ *OutMixerInfo = MixerInfo; /* now allocate an array which will receive the indices of the pin * which has a ADC / DAC nodetype in its path */ Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins); ASSERT(Status == MM_STATUS_SUCCESS); PinsFound = 0; /* now get all sink / source pins, which are attached to the ADC / DAC node * For sink pins (wave out) search up stream * For source pins (wave in) search down stream * The search direction is always the opposite of the current mixer type */ PinsFound = 0; MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, NodeIndex, !bInputMixer, &PinsFound, Pins); /* if there is no pin found, we have a broken topology */ ASSERT(PinsFound != 0); /* now create a wave info struct */ Status = MMixerInitializeWaveInfo(MixerContext, MixerList, MixerData, MixerInfo->MixCaps.szPname, bInputMixer, PinsFound, Pins); if (Status != MM_STATUS_SUCCESS) { /* failed to create wave info struct */ MixerContext->Free(MixerInfo); MixerContext->Free(Pins); return Status; } /* mark all found pins as reserved */ for(Index = 0; Index < PinsFound; Index++) { MMixerSetTopologyPinReserved(Topology, Pins[Index]); } if (bInputMixer) { /* pre create the mixer destination line for input mixers */ Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Pins[0], bInputMixer); if (Status != MM_STATUS_SUCCESS) { /* failed to create mixer destination line */ return Status; } } /* now get the bridge pin which is at the end of node path * For sink pins (wave out) search down stream * For source pins (wave in) search up stream */ MixerContext->Free(Pins); Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins); ASSERT(Status == MM_STATUS_SUCCESS); PinsFound = 0; MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, NodeIndex, bInputMixer, &PinsFound, Pins); /* if there is no pin found, we have a broken topology */ ASSERT(PinsFound != 0); /* there should be exactly one bridge pin */ ASSERT(PinsFound == 1); DPRINT("BridgePin %lu bInputMixer %lu\n", Pins[0], bInputMixer); /* does the pin have a physical connection */ Status = MMixerGetPhysicalConnection(MixerContext, MixerData->hDevice, Pins[0], &OutConnection); if (Status == MM_STATUS_SUCCESS) { /* mark pin as reserved */ MMixerSetTopologyPinReserved(Topology, Pins[0]); /* topology on the topoloy filter */ Status = MMixerHandlePhysicalConnection(MixerContext, MixerList, MixerData, MixerInfo, bInputMixer, OutConnection); /* free physical connection data */ MixerContext->Free(OutConnection); } else { /* topology is on the same filter */ Status = MMixerHandleTopologyFilter(MixerContext, MixerList, MixerData, MixerInfo, bInputMixer, Pins[0]); } /* free pins */ MixerContext->Free(Pins); if (NewMixerInfo) { /* insert mixer */ InsertHeadList(&MixerList->MixerList, &MixerInfo->Entry); /* increment mixer count */ MixerList->MixerListCount++; } /* done */ return Status; } VOID MMixerHandleAlternativeMixers( IN PMIXER_CONTEXT MixerContext, IN PMIXER_LIST MixerList, IN LPMIXER_DATA MixerData, IN PTOPOLOGY Topology) { ULONG Index, PinCount, Reserved; MIXER_STATUS Status; ULONG DestinationLineID, LineTerminator; LPMIXERLINE_EXT DstLine; DPRINT("DeviceName %S\n", MixerData->DeviceName); /* get topology pin count */ MMixerGetTopologyPinCount(Topology, &PinCount); for(Index = 0; Index < PinCount; Index++) { MMixerIsTopologyPinReserved(Topology, Index, &Reserved); /* check if it has already been reserved */ if (Reserved) { /* pin has already been reserved */ continue; } DPRINT("MixerName %S Available PinID %lu\n", MixerData->DeviceName, Index); /* sanity check */ //ASSERT(MixerData->MixerInfo); if (!MixerData->MixerInfo) { DPRINT1("Expected mixer info\n"); continue; } /* build the destination line */ Status = MMixerBuildMixerDestinationLine(MixerContext, MixerData->MixerInfo, MixerData->hDevice, Index, TRUE); if (Status != MM_STATUS_SUCCESS) { /* failed to build destination line */ continue; } /* calculate destination line id */ DestinationLineID = (DESTINATION_LINE + MixerData->MixerInfo->MixCaps.cDestinations-1); /* add mixer controls to destination line */ Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerData->MixerInfo, MixerData->hDevice, MixerData->Topology, Index, TRUE, DestinationLineID, &LineTerminator); if (Status == MM_STATUS_SUCCESS) { /* now add the rest of the source lines */ Status = MMixerAddMixerSourceLines(MixerContext, MixerData->MixerInfo, MixerData->hDevice, MixerData->Topology, DestinationLineID, LineTerminator); } /* mark pin as consumed */ MMixerSetTopologyPinReserved(Topology, Index); /* now grab destination line */ DstLine = MMixerGetSourceMixerLineByLineId(MixerData->MixerInfo, DestinationLineID); /* set type and target as undefined */ DstLine->Line.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_UNDEFINED; DstLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED; DstLine->Line.Target.vDriverVersion = 0; DstLine->Line.Target.wMid = 0; DstLine->Line.Target.wPid = 0; } } MIXER_STATUS MMixerSetupFilter( IN PMIXER_CONTEXT MixerContext, IN PMIXER_LIST MixerList, IN LPMIXER_DATA MixerData, IN PULONG DeviceCount) { MIXER_STATUS Status = MM_STATUS_SUCCESS; PTOPOLOGY Topology; ULONG NodeIndex; LPMIXER_INFO MixerInfo = NULL; /* check if topology has already been built */ if (MixerData->Topology == NULL) { /* build topology */ Status = MMixerBuildTopology(MixerContext, MixerData, &Topology); if (Status != MM_STATUS_SUCCESS) { /* failed to build topology */ return Status; } /* store topology */ MixerData->Topology = Topology; } else { /* re-use topology */ Topology = MixerData->Topology; } /* check if the filter has an wave out node */ NodeIndex = MMixerGetNodeIndexFromGuid(Topology, &KSNODETYPE_DAC); if (NodeIndex != MAXULONG) { /* it has */ Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, NULL, Topology, NodeIndex, FALSE, &MixerInfo); /* check for success */ if (Status == MM_STATUS_SUCCESS) { /* increment mixer count */ (*DeviceCount)++; } else { /* reset mixer info in case of error */ MixerInfo = NULL; } } /* check if the filter has an wave in node */ NodeIndex = MMixerGetNodeIndexFromGuid(Topology, &KSNODETYPE_ADC); if (NodeIndex != MAXULONG) { /* it has */ Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, MixerInfo, Topology, NodeIndex, TRUE, &MixerInfo); /* check for success */ if (Status == MM_STATUS_SUCCESS) { /* increment mixer count */ (*DeviceCount)++; } } /* TODO: apply hacks for Wave source line */ /* activate midi devices */ //MMixerInitializeMidiForFilter(MixerContext, MixerList, MixerData, Topology); /* done */ return Status; } MIXER_STATUS MMixerAddEvent( IN PMIXER_CONTEXT MixerContext, IN OUT LPMIXER_INFO MixerInfo, IN PVOID MixerEventContext, IN PMIXER_EVENT MixerEventRoutine) { //KSE_NODE Property; //KSEVENTDATA EventData //ULONG BytesReturned; //MIXER_STATUS Status; PEVENT_NOTIFICATION_ENTRY EventNotification; EventNotification = (PEVENT_NOTIFICATION_ENTRY)MixerContext->Alloc(sizeof(EVENT_NOTIFICATION_ENTRY)); if (!EventNotification) { /* not enough memory */ return MM_STATUS_NO_MEMORY; } /* FIXME: what is it supposed to happen with KSEVENTDATA ? */ #if 0 /* setup request */ Property.Event.Set = KSEVENTSETID_AudioControlChange; Property.Event.Flags = KSEVENT_TYPE_TOPOLOGY|KSEVENT_TYPE_ENABLE; Property.Event.Id = KSEVENT_CONTROL_CHANGE; Property.NodeId = NodeId; Property.Reserved = 0; Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_ENABLE_EVENT, (PVOID)&Property, sizeof(KSP_NODE), (PVOID)EventData, sizeof(KSEVENTDATA), &BytesReturned); if (Status != MM_STATUS_SUCCESS) { /* failed to add event */ MixerContext->FreeEventData(EventData); return Status; } #endif /* initialize notification entry */ EventNotification->MixerEventContext = MixerEventContext; EventNotification->MixerEventRoutine = MixerEventRoutine; /* store event */ InsertTailList(&MixerInfo->EventList, &EventNotification->Entry); return MM_STATUS_SUCCESS; } MIXER_STATUS MMixerRemoveEvent( IN PMIXER_CONTEXT MixerContext, IN OUT LPMIXER_INFO MixerInfo, IN PVOID MixerEventContext, IN PMIXER_EVENT MixerEventRoutine) { PLIST_ENTRY EventList; PEVENT_NOTIFICATION_ENTRY NotificationEntry; /* Lookup through mixers */ EventList = MixerInfo->EventList.Flink; while(EventList != &MixerInfo->EventList) { NotificationEntry = CONTAINING_RECORD(EventList, EVENT_NOTIFICATION_ENTRY, Entry); EventList = EventList->Flink; /* TODO: find a better way to identify an event ? */ if(NotificationEntry->MixerEventRoutine == MixerEventRoutine && NotificationEntry->MixerEventContext == MixerEventContext) { DPRINT1("Freeing entry %p\n", NotificationEntry); /* We found the event to remove */ RemoveEntryList(&NotificationEntry->Entry); MixerContext->Free(NotificationEntry); } } return MM_STATUS_SUCCESS; }