mirror of
https://github.com/reactos/reactos.git
synced 2024-11-03 05:18:55 +00:00
1905 lines
62 KiB
C
1905 lines
62 KiB
C
/*
|
|
* 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 <debug.h>
|
|
|
|
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;
|
|
}
|