/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Kernel Streaming * FILE: lib/drivers/sound/mmixer/sup.c * PURPOSE: Mixer Support Functions * PROGRAMMER: Johannes Anderwald */ #include "precomp.h" // #define NDEBUG #include const GUID KSNODETYPE_SUM = {0xDA441A60L, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; const GUID KSNODETYPE_DAC = {0x507AE360L, 0xC554, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; const GUID KSNODETYPE_ADC = {0x4D837FE0L, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; const GUID KSNODETYPE_AGC = {0xE88C9BA0L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; const GUID KSNODETYPE_LOUDNESS = {0x41887440L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; const GUID KSNODETYPE_MUTE = {0x02B223C0L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; const GUID KSNODETYPE_TONE = {0x7607E580L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; const GUID KSNODETYPE_VOLUME = {0x3A5ACC00L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; const GUID KSNODETYPE_PEAKMETER = {0xa085651e, 0x5f0d, 0x4b36, {0xa8, 0x69, 0xd1, 0x95, 0xd6, 0xab, 0x4b, 0x9e}}; const GUID KSNODETYPE_MUX = {0x2CEAF780, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; const GUID KSNODETYPE_STEREO_WIDE = {0xA9E69800L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; const GUID KSNODETYPE_CHORUS = {0x20173F20L, 0xC559, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; const GUID KSNODETYPE_REVERB = {0xEF0328E0L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; const GUID KSNODETYPE_SUPERMIX = {0xE573ADC0L, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}}; const GUID KSPROPSETID_Audio = {0x45FFAAA0L, 0x6E1B, 0x11D0, {0xBC, 0xF2, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}; const GUID KSPROPSETID_Pin = {0x8C134960L, 0x51AD, 0x11CF, {0x87, 0x8A, 0x94, 0xF8, 0x01, 0xC1, 0x00, 0x00}}; const GUID KSPROPSETID_General = {0x1464EDA5L, 0x6A8F, 0x11D1, {0x9A, 0xA7, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; const GUID KSPROPSETID_Topology = {0x720D4AC0L, 0x7533, 0x11D0, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}}; const GUID KSEVENTSETID_AudioControlChange = {0xE85E9698L, 0xFA2F, 0x11D1, {0x95, 0xBD, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3}}; const GUID KSDATAFORMAT_TYPE_MUSIC = {0xE725D360L, 0x62CC, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}}; const GUID KSDATAFORMAT_SUBTYPE_MIDI = {0x1D262760L, 0xE957, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}}; const GUID KSDATAFORMAT_SPECIFIER_NONE = {0x0F6417D6L, 0xC318, 0x11D0, {0xA4, 0x3F, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; MIXER_STATUS MMixerVerifyContext( IN PMIXER_CONTEXT MixerContext) { if (MixerContext->SizeOfStruct != sizeof(MIXER_CONTEXT)) return MM_STATUS_INVALID_PARAMETER; if (!MixerContext->Alloc || !MixerContext->Control || !MixerContext->Free || !MixerContext->Open || !MixerContext->AllocEventData || !MixerContext->FreeEventData || !MixerContext->Close || !MixerContext->OpenKey || !MixerContext->QueryKeyValue || !MixerContext->CloseKey) return MM_STATUS_INVALID_PARAMETER; if (!MixerContext->MixerContext) return MM_STATUS_INVALID_PARAMETER; return MM_STATUS_SUCCESS; } LPMIXERLINE_EXT MMixerGetMixerLineContainingNodeId( IN LPMIXER_INFO MixerInfo, IN ULONG NodeID) { PLIST_ENTRY Entry, ControlEntry; LPMIXERLINE_EXT MixerLineSrc; LPMIXERCONTROL_EXT MixerControl; /* get first entry */ Entry = MixerInfo->LineList.Flink; while(Entry != &MixerInfo->LineList) { MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry); ControlEntry = MixerLineSrc->ControlsList.Flink; while(ControlEntry != &MixerLineSrc->ControlsList) { MixerControl = (LPMIXERCONTROL_EXT)CONTAINING_RECORD(ControlEntry, MIXERCONTROL_EXT, Entry); if (MixerControl->NodeID == NodeID) { return MixerLineSrc; } ControlEntry = ControlEntry->Flink; } Entry = Entry->Flink; } return NULL; } VOID MMixerGetLowestLogicalTopologyPinOffsetFromArray( IN ULONG LogicalPinArrayCount, IN PULONG LogicalPinArray, OUT PULONG PinOffset) { ULONG Index; ULONG LowestId = 0; for(Index = 1; Index < LogicalPinArrayCount; Index++) { if (LogicalPinArray[Index] != MAXULONG) { /* sanity check: logical pin id must be unique */ ASSERT(LogicalPinArray[Index] != LogicalPinArray[LowestId]); } if (LogicalPinArray[Index] < LogicalPinArray[LowestId]) LowestId = Index; } /* store result */ *PinOffset = LowestId; } VOID MMixerFreeMixerInfo( IN PMIXER_CONTEXT MixerContext, IN LPMIXER_INFO MixerInfo) { /* UNIMPLEMENTED; * FIXME * free all lines */ MixerContext->Free((PVOID)MixerInfo); } LPMIXER_DATA MMixerGetMixerDataByDeviceHandle( IN PMIXER_CONTEXT MixerContext, IN HANDLE hDevice) { LPMIXER_DATA MixerData; PLIST_ENTRY Entry; PMIXER_LIST MixerList; /* get mixer list */ MixerList = (PMIXER_LIST)MixerContext->MixerContext; if (!MixerList->MixerDataCount) return NULL; Entry = MixerList->MixerData.Flink; while(Entry != &MixerList->MixerData) { MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry); if (MixerData->hDevice == hDevice) return MixerData; /* move to next mixer entry */ Entry = Entry->Flink; } return NULL; } LPMIXER_INFO MMixerGetMixerInfoByIndex( IN PMIXER_CONTEXT MixerContext, IN ULONG MixerIndex) { LPMIXER_INFO MixerInfo; PLIST_ENTRY Entry; PMIXER_LIST MixerList; ULONG Index = 0; /* get mixer list */ MixerList = (PMIXER_LIST)MixerContext->MixerContext; if (!MixerList->MixerListCount) return NULL; Entry = MixerList->MixerList.Flink; while(Entry != &MixerList->MixerList) { MixerInfo = (LPMIXER_INFO)CONTAINING_RECORD(Entry, MIXER_INFO, Entry); if (Index == MixerIndex) return MixerInfo; /* move to next mixer entry */ Index++; Entry = Entry->Flink; } return NULL; } MIXER_STATUS MMixerGetMixerByName( IN PMIXER_LIST MixerList, IN LPWSTR MixerName, OUT LPMIXER_INFO *OutMixerInfo) { LPMIXER_INFO MixerInfo; PLIST_ENTRY Entry; Entry = MixerList->MixerList.Flink; while(Entry != &MixerList->MixerList) { MixerInfo = (LPMIXER_INFO)CONTAINING_RECORD(Entry, MIXER_INFO, Entry); DPRINT1("MixerName %S MixerName %S\n", MixerInfo->MixCaps.szPname, MixerName); if (wcsicmp(MixerInfo->MixCaps.szPname, MixerName) == 0) { *OutMixerInfo = MixerInfo; return MM_STATUS_SUCCESS; } /* move to next mixer entry */ Entry = Entry->Flink; } return MM_STATUS_UNSUCCESSFUL; } LPMIXERLINE_EXT MMixerGetSourceMixerLineByLineId( LPMIXER_INFO MixerInfo, DWORD dwLineID) { PLIST_ENTRY Entry; LPMIXERLINE_EXT MixerLineSrc; /* get first entry */ Entry = MixerInfo->LineList.Flink; while(Entry != &MixerInfo->LineList) { MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry); DPRINT("dwLineID %x dwLineID %x MixerLineSrc %p\n", MixerLineSrc->Line.dwLineID, dwLineID, MixerLineSrc); if (MixerLineSrc->Line.dwLineID == dwLineID) return MixerLineSrc; Entry = Entry->Flink; } return NULL; } LPGUID MMixerGetNodeType( IN PKSMULTIPLE_ITEM MultipleItem, IN ULONG Index) { LPGUID NodeType; ASSERT(Index < MultipleItem->Count); NodeType = (LPGUID)(MultipleItem + 1); return &NodeType[Index]; } LPMIXERLINE_EXT MMixerGetSourceMixerLineByComponentType( LPMIXER_INFO MixerInfo, DWORD dwComponentType) { PLIST_ENTRY Entry; LPMIXERLINE_EXT MixerLineSrc; /* get first entry */ Entry = MixerInfo->LineList.Flink; while(Entry != &MixerInfo->LineList) { MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry); if (MixerLineSrc->Line.dwComponentType == dwComponentType) return MixerLineSrc; Entry = Entry->Flink; } return NULL; } MIXER_STATUS MMixerGetMixerControlById( LPMIXER_INFO MixerInfo, DWORD dwControlID, LPMIXERLINE_EXT *OutMixerLine, LPMIXERCONTROL_EXT *OutMixerControl, PULONG NodeId) { PLIST_ENTRY Entry, ControlEntry; LPMIXERLINE_EXT MixerLineSrc; LPMIXERCONTROL_EXT MixerControl; /* get first entry */ Entry = MixerInfo->LineList.Flink; while(Entry != &MixerInfo->LineList) { MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry); ControlEntry = MixerLineSrc->ControlsList.Flink; while(ControlEntry != &MixerLineSrc->ControlsList) { MixerControl = (LPMIXERCONTROL_EXT)CONTAINING_RECORD(ControlEntry, MIXERCONTROL_EXT, Entry); if (MixerControl->Control.dwControlID == dwControlID) { if (OutMixerLine) *OutMixerLine = MixerLineSrc; if (OutMixerControl) *OutMixerControl = MixerControl; if (NodeId) *NodeId = MixerControl->NodeID; return MM_STATUS_SUCCESS; } ControlEntry = ControlEntry->Flink; } Entry = Entry->Flink; } return MM_STATUS_UNSUCCESSFUL; } ULONG MMixerGetVolumeControlIndex( LPMIXERVOLUME_DATA VolumeData, LONG Value) { ULONG Index; for(Index = 0; Index < VolumeData->ValuesCount; Index++) { if (VolumeData->Values[Index] > Value) { return VolumeData->InputSteppingDelta * Index; } } return VolumeData->InputSteppingDelta * (VolumeData->ValuesCount-1); } VOID MMixerNotifyControlChange( IN PMIXER_CONTEXT MixerContext, IN LPMIXER_INFO MixerInfo, IN ULONG NotificationType, IN ULONG Value) { PLIST_ENTRY Entry; PEVENT_NOTIFICATION_ENTRY NotificationEntry; /* enumerate list and perform notification */ Entry = MixerInfo->EventList.Flink; while(Entry != &MixerInfo->EventList) { /* get notification entry offset */ NotificationEntry = (PEVENT_NOTIFICATION_ENTRY)CONTAINING_RECORD(Entry, EVENT_NOTIFICATION_ENTRY, Entry); if (NotificationEntry->MixerEventRoutine) { /* now perform the callback */ NotificationEntry->MixerEventRoutine(NotificationEntry->MixerEventContext, (HANDLE)MixerInfo, NotificationType, Value); } /* move to next notification entry */ Entry = Entry->Flink; } } MIXER_STATUS MMixerSetGetMuteControlDetails( IN PMIXER_CONTEXT MixerContext, IN LPMIXER_INFO MixerInfo, IN LPMIXERCONTROL_EXT MixerControl, IN ULONG dwLineID, IN LPMIXERCONTROLDETAILS MixerControlDetails, IN ULONG bSet) { LPMIXERCONTROLDETAILS_BOOLEAN Input; LONG Value; MIXER_STATUS Status; if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) return MM_STATUS_INVALID_PARAMETER; /* get input */ Input = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails; /* FIXME SEH */ if (bSet) Value = Input->fValue; /* set control details */ Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, MixerControl->NodeID, bSet, KSPROPERTY_AUDIO_MUTE, 0, &Value); if (Status != MM_STATUS_SUCCESS) return Status; /* FIXME SEH */ if (!bSet) { Input->fValue = Value; return Status; } else { /* notify wdmaud clients MM_MIXM_LINE_CHANGE dwLineID */ MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_LINE_CHANGE, dwLineID); } return Status; } MIXER_STATUS MMixerSetGetMuxControlDetails( IN PMIXER_CONTEXT MixerContext, IN LPMIXER_INFO MixerInfo, IN ULONG NodeId, IN ULONG bSet, IN ULONG Flags, IN LPMIXERCONTROL_EXT MixerControl, IN LPMIXERCONTROLDETAILS MixerControlDetails, IN LPMIXERLINE_EXT MixerLine) { MIXER_STATUS Status; PULONG LogicalNodes, ConnectedNodes; ULONG LogicalNodesCount, ConnectedNodesCount, Index, CurLogicalPinOffset, BytesReturned, OldLogicalPinOffset; LPMIXER_DATA MixerData; LPMIXERCONTROLDETAILS_LISTTEXTW ListText; LPMIXERCONTROLDETAILS_BOOLEAN Values; LPMIXERLINE_EXT SourceLine; KSNODEPROPERTY Request; DPRINT("MixerControlDetails %p\n", MixerControlDetails); DPRINT("bSet %lx\n", bSet); DPRINT("Flags %lx\n", Flags); DPRINT("NodeId %lu\n", MixerControl->NodeID); DPRINT("MixerControlDetails dwControlID %lu\n", MixerControlDetails->dwControlID); DPRINT("MixerControlDetails cChannels %lu\n", MixerControlDetails->cChannels); DPRINT("MixerControlDetails cMultipleItems %lu\n", MixerControlDetails->cMultipleItems); DPRINT("MixerControlDetails cbDetails %lu\n", MixerControlDetails->cbDetails); DPRINT("MixerControlDetails paDetails %p\n", MixerControlDetails->paDetails); if (MixerControl->Control.fdwControl & MIXERCONTROL_CONTROLF_UNIFORM) { /* control acts uniform */ if (MixerControlDetails->cChannels != 1) { /* expected 1 channel */ DPRINT1("Expected 1 channel but got %lu\n", MixerControlDetails->cChannels); return MM_STATUS_UNSUCCESSFUL; } } /* check if multiple items match */ if (MixerControlDetails->cMultipleItems != MixerControl->Control.cMultipleItems) { DPRINT1("MultipleItems mismatch %lu expected %lu\n", MixerControlDetails->cMultipleItems, MixerControl->Control.cMultipleItems); return MM_STATUS_UNSUCCESSFUL; } if (bSet) { if ((Flags & MIXER_SETCONTROLDETAILSF_QUERYMASK) == MIXER_SETCONTROLDETAILSF_CUSTOM) { /* tell me when this is hit */ ASSERT(FALSE); } else if ((Flags & (MIXER_SETCONTROLDETAILSF_VALUE | MIXER_SETCONTROLDETAILSF_CUSTOM)) == MIXER_SETCONTROLDETAILSF_VALUE) { /* sanity check */ ASSERT(bSet == TRUE); ASSERT(MixerControlDetails->cbDetails == sizeof(MIXERCONTROLDETAILS_BOOLEAN)); Values = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails; CurLogicalPinOffset = MAXULONG; for(Index = 0; Index < MixerControlDetails->cMultipleItems; Index++) { if (Values[Index].fValue) { /* mux can only activate one line at a time */ ASSERT(CurLogicalPinOffset == MAXULONG); CurLogicalPinOffset = Index; } } /* setup request */ Request.NodeId = NodeId; Request.Reserved = 0; Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_GET; Request.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE; Request.Property.Set = KSPROPSETID_Audio; /* perform getting source */ Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &OldLogicalPinOffset, sizeof(ULONG), &BytesReturned); if (Status != MM_STATUS_SUCCESS) { /* failed to get source */ return Status; } DPRINT("OldLogicalPinOffset %lu CurLogicalPinOffset %lu\n", OldLogicalPinOffset, CurLogicalPinOffset); if (OldLogicalPinOffset == CurLogicalPinOffset) { /* cannot be unselected */ return MM_STATUS_UNSUCCESSFUL; } /* perform setting source */ Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_SET; Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &CurLogicalPinOffset, sizeof(ULONG), &BytesReturned); if (Status != MM_STATUS_SUCCESS) { /* failed to set source */ return Status; } /* notify control change */ MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_CONTROL_CHANGE, MixerControl->Control.dwControlID ); return Status; } } else { if ((Flags & MIXER_GETCONTROLDETAILSF_QUERYMASK) == MIXER_GETCONTROLDETAILSF_VALUE) { /* setup request */ Request.NodeId = NodeId; Request.Reserved = 0; Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_GET; Request.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE; Request.Property.Set = KSPROPSETID_Audio; /* perform getting source */ Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &OldLogicalPinOffset, sizeof(ULONG), &BytesReturned); if (Status != MM_STATUS_SUCCESS) { /* failed to get source */ return Status; } /* gets the corresponding mixer data */ MixerData = MMixerGetMixerDataByDeviceHandle(MixerContext, MixerControl->hDevice); /* sanity check */ ASSERT(MixerData); ASSERT(MixerData->Topology); ASSERT(MixerData->MixerInfo == MixerInfo); /* now allocate logical pin array */ Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &LogicalNodes); if (Status != MM_STATUS_SUCCESS) { /* no memory */ return MM_STATUS_NO_MEMORY; } /* get logical pin nodes */ MMixerGetConnectedFromLogicalTopologyPins(MixerData->Topology, MixerControl->NodeID, &LogicalNodesCount, LogicalNodes); /* sanity check */ ASSERT(LogicalNodesCount == MixerControlDetails->cMultipleItems); ASSERT(LogicalNodesCount == MixerControl->Control.Metrics.dwReserved[0]); Values = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails; for(Index = 0; Index < LogicalNodesCount; Index++) { /* getting logical pin offset */ MMixerGetLowestLogicalTopologyPinOffsetFromArray(LogicalNodesCount, LogicalNodes, &CurLogicalPinOffset); if (CurLogicalPinOffset == OldLogicalPinOffset) { /* mark index as active */ Values[Index].fValue = TRUE; } else { /* index not active */ Values[Index].fValue = FALSE; } /* mark offset as consumed */ LogicalNodes[CurLogicalPinOffset] = MAXULONG; } /* cleanup */ MixerContext->Free(LogicalNodes); /* done */ return MM_STATUS_SUCCESS; } else if ((Flags & MIXER_GETCONTROLDETAILSF_QUERYMASK) == MIXER_GETCONTROLDETAILSF_LISTTEXT) { /* sanity check */ ASSERT(bSet == FALSE); /* gets the corresponding mixer data */ MixerData = MMixerGetMixerDataByDeviceHandle(MixerContext, MixerControl->hDevice); /* sanity check */ ASSERT(MixerData); ASSERT(MixerData->Topology); ASSERT(MixerData->MixerInfo == MixerInfo); /* now allocate logical pin array */ Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &LogicalNodes); if (Status != MM_STATUS_SUCCESS) { /* no memory */ return MM_STATUS_NO_MEMORY; } /* allocate connected node array */ Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &ConnectedNodes); if (Status != MM_STATUS_SUCCESS) { /* no memory */ MixerContext->Free(LogicalNodes); return MM_STATUS_NO_MEMORY; } /* get logical pin nodes */ MMixerGetConnectedFromLogicalTopologyPins(MixerData->Topology, MixerControl->NodeID, &LogicalNodesCount, LogicalNodes); /* get connected nodes */ MMixerGetNextNodesFromNodeIndex(MixerContext, MixerData->Topology, MixerControl->NodeID, TRUE, &ConnectedNodesCount, ConnectedNodes); /* sanity check */ ASSERT(ConnectedNodesCount == LogicalNodesCount); ASSERT(ConnectedNodesCount == MixerControlDetails->cMultipleItems); ASSERT(ConnectedNodesCount == MixerControl->Control.Metrics.dwReserved[0]); ListText = (LPMIXERCONTROLDETAILS_LISTTEXTW)MixerControlDetails->paDetails; for(Index = 0; Index < ConnectedNodesCount; Index++) { /* getting logical pin offset */ MMixerGetLowestLogicalTopologyPinOffsetFromArray(LogicalNodesCount, LogicalNodes, &CurLogicalPinOffset); /* get mixer line with that node */ SourceLine = MMixerGetMixerLineContainingNodeId(MixerInfo, ConnectedNodes[CurLogicalPinOffset]); /* sanity check */ ASSERT(SourceLine); DPRINT1("PinOffset %lu LogicalPin %lu NodeId %lu LineName %S\n", CurLogicalPinOffset, LogicalNodes[CurLogicalPinOffset], ConnectedNodes[CurLogicalPinOffset], SourceLine->Line.szName); /* copy details */ ListText[Index].dwParam1 = SourceLine->Line.dwLineID; ListText[Index].dwParam2 = SourceLine->Line.dwComponentType; MixerContext->Copy(ListText[Index].szName, SourceLine->Line.szName, (wcslen(SourceLine->Line.szName) + 1) * sizeof(WCHAR)); /* mark offset as consumed */ LogicalNodes[CurLogicalPinOffset] = MAXULONG; } /* cleanup */ MixerContext->Free(LogicalNodes); MixerContext->Free(ConnectedNodes); /* done */ return MM_STATUS_SUCCESS; } } return MM_STATUS_NOT_IMPLEMENTED; } MIXER_STATUS MMixerSetGetVolumeControlDetails( IN PMIXER_CONTEXT MixerContext, IN LPMIXER_INFO MixerInfo, IN ULONG NodeId, IN ULONG bSet, LPMIXERCONTROL_EXT MixerControl, IN LPMIXERCONTROLDETAILS MixerControlDetails, LPMIXERLINE_EXT MixerLine) { LPMIXERCONTROLDETAILS_UNSIGNED Input; LONG Value; ULONG Index, Channel = 0; ULONG dwValue; MIXER_STATUS Status; LPMIXERVOLUME_DATA VolumeData; if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_SIGNED)) return MM_STATUS_INVALID_PARAMETER; VolumeData = (LPMIXERVOLUME_DATA)MixerControl->ExtraData; if (!VolumeData) return MM_STATUS_UNSUCCESSFUL; /* get input */ Input = (LPMIXERCONTROLDETAILS_UNSIGNED)MixerControlDetails->paDetails; if (!Input) return MM_STATUS_UNSUCCESSFUL; /* to prevent dereferencing NULL */ if (bSet) { /* FIXME SEH */ Value = Input->dwValue; Index = Value / VolumeData->InputSteppingDelta; if (Index >= VolumeData->ValuesCount) { DPRINT1("Index %u out of bounds %u \n", Index, VolumeData->ValuesCount); return MM_STATUS_INVALID_PARAMETER; } Value = VolumeData->Values[Index]; } /* set control details */ if (bSet) { /* TODO */ Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 0, &Value); Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 1, &Value); } else { Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, Channel, &Value); } if (!bSet) { dwValue = MMixerGetVolumeControlIndex(VolumeData, (LONG)Value); /* FIXME SEH */ Input->dwValue = dwValue; } else { /* notify clients of a line change MM_MIXM_CONTROL_CHANGE with MixerControl->dwControlID */ MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_CONTROL_CHANGE, MixerControl->Control.dwControlID); } return Status; } LPMIXER_DATA MMixerGetDataByDeviceId( IN PMIXER_LIST MixerList, IN ULONG DeviceId) { PLIST_ENTRY Entry; LPMIXER_DATA MixerData; Entry = MixerList->MixerData.Flink; while(Entry != &MixerList->MixerData) { MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry); if (MixerData->DeviceId == DeviceId) { return MixerData; } Entry = Entry->Flink; } return NULL; } LPMIXER_DATA MMixerGetDataByDeviceName( IN PMIXER_LIST MixerList, IN LPWSTR DeviceName) { PLIST_ENTRY Entry; LPMIXER_DATA MixerData; Entry = MixerList->MixerData.Flink; while(Entry != &MixerList->MixerData) { MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry); if (wcsicmp(&DeviceName[2], &MixerData->DeviceName[2]) == 0) { /* found entry */ return MixerData; } Entry = Entry->Flink; } return NULL; } MIXER_STATUS MMixerCreateMixerData( IN PMIXER_CONTEXT MixerContext, IN PMIXER_LIST MixerList, IN ULONG DeviceId, IN LPWSTR DeviceName, IN HANDLE hDevice, IN HANDLE hKey) { LPMIXER_DATA MixerData; MixerData = (LPMIXER_DATA)MixerContext->Alloc(sizeof(MIXER_DATA)); if (!MixerData) return MM_STATUS_NO_MEMORY; MixerData->DeviceId = DeviceId; MixerData->DeviceName = DeviceName; MixerData->hDevice = hDevice; MixerData->hDeviceInterfaceKey = hKey; MixerData->Topology = NULL; InsertTailList(&MixerList->MixerData, &MixerData->Entry); MixerList->MixerDataCount++; return MM_STATUS_SUCCESS; } MIXER_STATUS MMixerGetDeviceNameWithComponentId( IN PMIXER_CONTEXT MixerContext, IN HANDLE hMixer, OUT LPWSTR OutDeviceName) { MIXER_STATUS Status; KSPROPERTY Property; KSCOMPONENTID ComponentId; ULONG Length; UNICODE_STRING GuidString; ULONG ResultLength, KeyType; HANDLE hMediaKey, hGuidKey; LPWSTR DeviceName; /* prepare property */ Property.Flags = KSPROPERTY_TYPE_GET; Property.Set = KSPROPSETID_General; Property.Id = KSPROPERTY_GENERAL_COMPONENTID; /* try get component id */ Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSPROPERTY), &ComponentId, sizeof(KSCOMPONENTID), &Length); if (Status == MM_STATUS_SUCCESS) { Status = MixerContext->OpenKey(NULL, L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\MediaCategories", KEY_READ, &hMediaKey); if (Status == MM_STATUS_SUCCESS) { RtlStringFromGUID(&ComponentId.Name, &GuidString); Status = MixerContext->OpenKey(hMediaKey, GuidString.Buffer, KEY_READ, &hGuidKey); RtlFreeUnicodeString(&GuidString); if (Status == MM_STATUS_SUCCESS) { Status = MixerContext->QueryKeyValue(hGuidKey, L"Name", (PVOID*)&DeviceName, &ResultLength, &KeyType); if (Status == MM_STATUS_SUCCESS) { MixerContext->Copy(OutDeviceName, DeviceName, min(ResultLength, (MAXPNAMELEN-1)*2)); MixerContext->Free(DeviceName); } MixerContext->CloseKey(hGuidKey); } MixerContext->CloseKey(hMediaKey); } } return Status; } MIXER_STATUS MMixerGetDeviceName( IN PMIXER_CONTEXT MixerContext, OUT LPWSTR DeviceName, IN HANDLE hKey) { LPWSTR Name; HANDLE hTemp; ULONG Length; ULONG Type; MIXER_STATUS Status; Status = MixerContext->QueryKeyValue(hKey, L"FriendlyName", (PVOID*)&Name, &Length, &Type); if (Status == MM_STATUS_SUCCESS) { /* copy device name */ MixerContext->Copy(DeviceName, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR)); /* make sure its null terminated */ DeviceName[MAXPNAMELEN-1] = L'\0'; /* free device name */ MixerContext->Free(Name); /* done */ return Status; } Status = MixerContext->OpenKey(hKey, L"Device Parameters", KEY_READ, &hTemp); if (Status != MM_STATUS_SUCCESS) return Status; Status = MixerContext->QueryKeyValue(hTemp, L"FriendlyName", (PVOID*)&Name, &Length, &Type); if (Status == MM_STATUS_SUCCESS) { /* copy device name */ MixerContext->Copy(DeviceName, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR)); /* make sure its null terminated */ DeviceName[MAXPNAMELEN-1] = L'\0'; /* free device name */ MixerContext->Free(Name); } MixerContext->CloseKey(hTemp); return Status; } VOID MMixerInitializePinConnect( IN OUT PKSPIN_CONNECT PinConnect, IN ULONG PinId) { PinConnect->Interface.Set = KSINTERFACESETID_Standard; PinConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING; PinConnect->Interface.Flags = 0; PinConnect->Medium.Set = KSMEDIUMSETID_Standard; PinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE; PinConnect->Medium.Flags = 0; PinConnect->PinToHandle = NULL; PinConnect->PinId = PinId; PinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL; PinConnect->Priority.PrioritySubClass = 1; }