Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

5676 lines
170 KiB

//---------------------------------------------------------------------------
//
// Module: mixer.c
//
// Description:
// Contains the kernel mode portion of the mixer line driver.
//
//
//@@BEGIN_MSINTERNAL
// Development Team:
// D. Baumberger
//
// History: Date Author Comment
//
//@@END_MSINTERNAL
//
//---------------------------------------------------------------------------
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (C) Microsoft Corporation, 1997 - 1999 All Rights Reserved.
//
//---------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// //
// I N C L U D E S //
// //
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
#include "WDMSYS.H"
typedef struct {
VOID *pNext;
PMXLCONTROL pControl;
#ifdef DEBUG
DWORD dwSig; // CONTROLLINK_SIGNATURE
#endif
} CONTROLLINK, *PCONTROLLINK;
//
// Data structure for Notification list.
//
typedef struct {
PVOID pNext; // Pointer to next node in linked list.
DWORD NodeId; // This is the control's Id as seen by SysAudio
DWORD LineID; // Line that this control lives on
DWORD ControlID; // ControlID from pControl->Control.dwControlID
DWORD ControlType; //
// context specific stuff...
PWDMACONTEXT pContext; // Pointer to Global Context structure
PMIXERDEVICE pmxd; // The mixer device for this control
// PFILE_OBJECT pfo; // File Object to SysAudio for this Context
PCONTROLLINK pcLink; // points to structure that contains valid
// pControl addresses in this context.
KDPC NodeEventDPC; // For handling the DPCs
KSEVENTDATA NodeEventData;
#ifdef DEBUG
DWORD dwSig; // NOTIFICATION_SIGNATURE
#endif
} NOTENODE, *PNOTENODE;
#ifdef DEBUG
BOOL
IsValidNoteNode(
IN PNOTENODE pnnode
);
BOOL
IsValidControlLink(
IN PCONTROLLINK pcLink
);
/////////////////////////////////////////////////////////////////////////////
//
// IsValidNoteNode
//
// Validates that the pointer is a PNOTENODE type.
//
BOOL
IsValidNoteNode(
IN PNOTENODE pnnode)
{
NTSTATUS Status=STATUS_SUCCESS;
try
{
if(pnnode->dwSig != NOTIFICATION_SIGNATURE)
{
DPF(DL_ERROR|FA_ASSERT,("Invalid pnnode->dwSig(%08X)",pnnode->dwSig) );
Status=STATUS_UNSUCCESSFUL;
}
/*
if(pnnode->pfo == NULL)
{
DPF(DL_ERROR|FA_ASSERT,("Invalid pnnode->pfo(%08X)",pnnode->pfo) );
Status=STATUS_UNSUCCESSFUL;
}
*/
if( !IsValidWdmaContext(pnnode->pContext) )
{
DPF(DL_ERROR|FA_ASSERT,("Invalid pnnode->pContext(%08X)",pnnode->pContext) );
Status=STATUS_UNSUCCESSFUL;
}
if( !IsValidMixerDevice(pnnode->pmxd) )
{
DPF(DL_ERROR|FA_ASSERT,("Invalid pnnode->pmxd(%08X)",pnnode->pmxd) );
Status=STATUS_UNSUCCESSFUL;
}
if( !IsValidControlLink(pnnode->pcLink) )
{
DPF(DL_ERROR|FA_ASSERT,("Invalid pnnode->pcLink(%08X)",pnnode->pcLink) );
Status=STATUS_UNSUCCESSFUL;
}
}
except (EXCEPTION_EXECUTE_HANDLER)
{
Status = GetExceptionCode();
}
if( NT_SUCCESS(Status) )
{
return TRUE;
} else {
return FALSE;
}
}
/////////////////////////////////////////////////////////////////////////////
//
// IsValidControlLink
//
// Validates that the pointer is a PNOTENODE type.
//
BOOL
IsValidControlLink(
IN PCONTROLLINK pcLink)
{
NTSTATUS Status=STATUS_SUCCESS;
try
{
if(pcLink->dwSig != CONTROLLINK_SIGNATURE)
{
DPF(DL_ERROR|FA_ASSERT,("Invalid pcLink->dwSig(%08X)",pcLink->dwSig) );
Status=STATUS_UNSUCCESSFUL;
}
if( !IsValidControl(pcLink->pControl) )
{
Status=STATUS_UNSUCCESSFUL;
}
//
// pcLink->pNext is a pointer to another CONTROLLINK structure. Thus,
// if it's not NULL, the structure that it points to should also be valid.
//
if( pcLink->pNext )
{
PCONTROLLINK pTmp=pcLink->pNext;
if( pTmp->dwSig != CONTROLLINK_SIGNATURE )
{
DPF(DL_ERROR|FA_ASSERT,("Invalid pcLink->pNext->dwSig(%08X)",pTmp->dwSig) );
Status=STATUS_UNSUCCESSFUL;
}
}
}
except (EXCEPTION_EXECUTE_HANDLER)
{
Status = GetExceptionCode();
}
if( NT_SUCCESS(Status) )
{
return TRUE;
} else {
return FALSE;
}
}
#endif
PNOTENODE
kmxlNewNoteNode(
);
PCONTROLLINK
kmxlNewControlLink(
IN PMXLCONTROL pControl
);
VOID
kmxlFreeControlLink(
IN OUT PCONTROLLINK pcLink
);
VOID
kmxlFreeNoteNode(
IN OUT PNOTENODE pnnode
);
VOID
kmxlAddNoteNodeToList(
IN OUT PNOTENODE pnnode
);
VOID
kmxlRemoveNoteNodeFromList(
IN OUT PNOTENODE pnnode
);
PNOTENODE
kmxlFindControlInNoteList(
IN PMXLCONTROL pControl
);
NTSTATUS
kmxlFindNodeInNoteList(
IN PNOTENODE pnlookupnode
);
PNOTENODE
kmxlFindIdInContextInNoteList(
IN PWDMACONTEXT pWdmaContext,
IN PMIXERDEVICE pmxd,
IN DWORD Id
);
PNOTENODE
kmxlFindContextInNoteList(
IN PWDMACONTEXT pWdmaContext
);
NTSTATUS
kmxlAddControlToNoteList(
IN OUT PNOTENODE pnnode,
IN PMXLCONTROL pControl
);
PCONTROLLINK
kmxlRemoveControlFromNoteList(
IN OUT PNOTENODE pnnode,
IN PMXLCONTROL pControl
);
NTSTATUS
kmxlQueryControlChange(
IN PFILE_OBJECT pfo,
IN ULONG NodeId
);
NTSTATUS
kmxlEnableControlChange(
IN PFILE_OBJECT pfo,
IN ULONG NodeId,
IN OUT PKSEVENTDATA pksed
);
NTSTATUS
kmxlDisableControlChange(
IN PFILE_OBJECT pfo,
IN ULONG NodeId,
IN OUT PKSEVENTDATA pksed
);
NTSTATUS
kmxlEnableControlChangeNotifications(
IN PMIXEROBJECT pmxobj,
IN PMXLLINE pLine,
IN PMXLCONTROL pControl
);
VOID
kmxlRemoveContextFromNoteList(
IN PWDMACONTEXT pWdmaContext
);
NTSTATUS
kmxlEnableAllControls(
IN PMIXEROBJECT pmxobj
);
//
// Used in persist
//
extern NTSTATUS
kmxlPersistSingleControl(
IN PFILE_OBJECT pfo,
IN PMIXERDEVICE pmxd,
IN PMXLCONTROL pControl,
IN PVOID paDetails
);
VOID
PersistHWControlWorker(
IN LPVOID pData
);
VOID
kmxlGrabNoteMutex(
);
VOID
kmxlReleaseNoteMutex(
);
VOID
kmxlCloseMixerDevice(
IN OUT PMIXERDEVICE pmxd
);
#pragma LOCKED_CODE
#pragma LOCKED_DATA
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// //
// F U N C T I O N S //
// //
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
//
// This is the head of the notification list. mtxNotification is used to
// handle the list manipulcation. The allocated memory in the firstnotenode
// list will be touched at DPC level.
//
PNOTENODE firstnotenode=NULL;
extern KMUTEX mtxNote;
#ifdef DEBUG
LONG totalnotificationcount=0;
#endif
#define CALLBACKARRAYSIZE 128
typedef struct {
DWORD dwControlID;
DWORD dwLineID;
DWORD dwCallbackType;
} CBINFO, *PCBINFO;
ULONG emptyindex=0;
volatile ULONG loadindex=0;
CBINFO callbacks[CALLBACKARRAYSIZE]={0};
KSPIN_LOCK HardwareCallbackSpinLock;
LIST_ENTRY HardwareCallbackListHead;
PKEVENT pHardwareCallbackEvent=NULL;
PKSWORKER HardwareCallbackWorkerObject=NULL;
WORK_QUEUE_ITEM HardwareCallbackWorkItem;
ULONG HardwareCallbackScheduled=0;
typedef struct tagHWLink {
LIST_ENTRY Next;
PNOTENODE pnnode;
#ifdef DEBUG
DWORD dwSig;
#endif
} HWLINK, *PHWLINK;
/////////////////////////////////////////////////////////////////////////////
//
// NodeEventDPCHandler
//
// This routine is called at DISPATCH_LEVEL, thus you can not touch anything
// but pnnode ellements. pnnode is fixed in memory thus it is safe.
//
VOID NodeEventDPCHandler(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
{
PHWLINK phwlink=NULL;
PNOTENODE pnnode=(PNOTENODE)DeferredContext;
PCBINFO pcbinfo;
//
// WorkItem: Use proper synchronization so that we never go away if there is
// a work item scheduled!
//
DPFASSERT( pnnode->dwSig == NOTIFICATION_SIGNATURE );
DPF(DL_TRACE|FA_HARDWAREEVENT, ("** ++ Event Signaled ++ **") );
DPF(DL_TRACE|FA_HARDWAREEVENT, ("NodeId = %d LineID = %X ControlID = %X ControlType = %X",
pnnode->NodeId,pnnode->LineID,
pnnode->ControlID,pnnode->ControlType) );
callbacks[loadindex%CALLBACKARRAYSIZE].dwControlID=pnnode->ControlID;
callbacks[loadindex%CALLBACKARRAYSIZE].dwLineID=pnnode->LineID;
callbacks[loadindex%CALLBACKARRAYSIZE].dwCallbackType=MIXER_CONTROL_CALLBACK;
//
// Now we want to setup a work item to persist the hardware event. The idea
// is that we'll put all the events in a list and then remove them from the list
// in the handler. If we already have a workitem outstanding to service the
// list, we don't schedule another.
if( HardwareCallbackWorkerObject )
{
//
// Always allocate an entry in our list to service this DPC. We'll free this
// event in the worker routine.
//
if( NT_SUCCESS(AudioAllocateMemory_Fixed(sizeof(HWLINK),
TAG_AuDF_HARDWAREEVENT,
ZERO_FILL_MEMORY,
&phwlink) ) )
{
phwlink->pnnode=pnnode;
#ifdef DEBUG
phwlink->dwSig=HWLINK_SIGNATURE;
#endif
ExInterlockedInsertTailList(&HardwareCallbackListHead,
&phwlink->Next,
&HardwareCallbackSpinLock);
//
// Now, if we haven't scheduled a work item yet, do so to service this
// info in the list. HardwareCallbackSchedule will be 0 at
// initialization time. This variable will be set to 0 in the
// work item handler. If we increment and it's any other value other
// then 1, we've already scheduled a work item.
//
if( InterlockedIncrement(&HardwareCallbackScheduled) == 1 )
{
KsQueueWorkItem(HardwareCallbackWorkerObject, &HardwareCallbackWorkItem);
}
}
}
// Now if the control was a mute, then send line change as well.
if (pnnode->ControlType==MIXERCONTROL_CONTROLTYPE_MUTE) {
callbacks[loadindex%CALLBACKARRAYSIZE].dwCallbackType|=MIXER_LINE_CALLBACK;
}
loadindex++;
if (pHardwareCallbackEvent!=NULL) {
KeSetEvent(pHardwareCallbackEvent, 0 , FALSE);
}
}
#pragma PAGEABLE_CODE
#pragma PAGEABLE_DATA
/////////////////////////////////////////////////////////////////////////////
//
// kmxlNewNoteNode
//
// This routine allocates another Notification node. Note that AudioAllocateMemory zeros
// all successful memory allocations.
//
// The return value is a pointer to a Notification Node or NULL.
//
PNOTENODE
kmxlNewNoteNode(
)
{
PNOTENODE pnnode = NULL;
NTSTATUS Status;
PAGED_CODE();
Status = AudioAllocateMemory_Fixed(sizeof( NOTENODE ),
TAG_AuDN_NOTIFICATION,
ZERO_FILL_MEMORY,
&pnnode );
if( !NT_SUCCESS( Status ) ) {
return( NULL );
}
DPF(DL_MAX|FA_NOTE, ("New notification node allocated %08X",pnnode) );
#ifdef DEBUG
pnnode->dwSig=NOTIFICATION_SIGNATURE;
#endif
return pnnode;
}
/////////////////////////////////////////////////////////////////////////////
//
// kmxlNewControlLink
//
// This routine allocates another CONTROLLINK node. Note that AudioAllocateMemory zeros
// all successful memory allocations.
//
// The return value is a pointer to a Notification Node or NULL.
//
PCONTROLLINK
kmxlNewControlLink(
IN PMXLCONTROL pControl
)
{
PCONTROLLINK pcLink = NULL;
NTSTATUS Status;
PAGED_CODE();
Status = AudioAllocateMemory_Fixed(sizeof( CONTROLLINK ),
TAG_AuDL_LINK,
ZERO_FILL_MEMORY,
&pcLink );
if( !NT_SUCCESS( Status ) ) {
return( NULL );
}
DPF(DL_MAX|FA_NOTE, ("New controllink node allocated %08X",pcLink) );
#ifdef DEBUG
pcLink->dwSig=CONTROLLINK_SIGNATURE;
#endif
//
// Setup the pcontrol first and then link it in.
//
pcLink->pControl=pControl;
return pcLink;
}
VOID
kmxlFreeControlLink(
IN OUT PCONTROLLINK pcLink
)
{
DPFASSERT(IsValidControlLink(pcLink));
PAGED_CODE();
#ifdef DEBUG
pcLink->dwSig=(DWORD)0xDEADBEEF;
#endif
AudioFreeMemory(sizeof( CONTROLLINK ),&pcLink);
}
/////////////////////////////////////////////////////////////////////////////
//
// kmxlFreeNoteNode
//
// This routine frees a Notification Node.
//
VOID
kmxlFreeNoteNode(
IN OUT PNOTENODE pnnode
)
{
PCONTROLLINK pcLink,pcTmp;
PAGED_CODE();
DPFASSERT( pnnode->dwSig == NOTIFICATION_SIGNATURE );
DPFASSERT( pnnode->pNext == NULL );
DPFASSERT( pnnode->pcLink == NULL );
DPF(DL_MAX|FA_NOTE,("NotificationNode freed %08X",pnnode) );
AudioFreeMemory( sizeof( NOTENODE ),&pnnode );
}
/////////////////////////////////////////////////////////////////////////////
//
// kmxlAddNoteNodeToList
//
// This routine adds the NotificationNode to the list. It places the node
// at the head of the list.
//
VOID
kmxlAddNoteNodeToList(
IN OUT PNOTENODE pnnode
)
{
PAGED_CODE();
pnnode->pNext=firstnotenode;
firstnotenode=pnnode;
#ifdef DEBUG
totalnotificationcount++;
#endif
DPF(DL_TRACE|FA_INSTANCE,("New NoteNode head %08X, Total=%d",pnnode,totalnotificationcount) );
}
/////////////////////////////////////////////////////////////////////////////
//
// kmxlRemoveNoteNodeFromList
//
// This routine removes a node from the list.
//
VOID
kmxlRemoveNoteNodeFromList(
IN OUT PNOTENODE pnnode
)
{
PNOTENODE pTmp;
PAGED_CODE();
DPFASSERT(pnnode->dwSig == NOTIFICATION_SIGNATURE);
// are we the head of the list? If so, move the next to the head.
if( pnnode == firstnotenode )
{
firstnotenode=firstnotenode->pNext;
DPF(DL_MAX|FA_NOTE,("removed notenode head") );
} else {
// we are somewhere in the list we need to walk the list until we find
// our node and clip it out.
for (pTmp=firstnotenode;pTmp!=NULL;pTmp=pTmp->pNext)
{
DPFASSERT(pTmp->dwSig==NOTIFICATION_SIGNATURE);
// Did we find our node in the next position? If so, we
// need to clip it out. Thus, pTmp.next needs to point to
// (our node)'s next.
if(pTmp->pNext == pnnode)
{
pTmp->pNext = pnnode->pNext;
DPF(DL_MAX|FA_NOTE,("removed middle") );
break;
}
}
}
#ifdef DEBUG
totalnotificationcount--;
#endif
//
// to indicate that this node has been removed, pNext gets set to NULL!
//
pnnode->pNext=NULL;
}
/////////////////////////////////////////////////////////////////////////////
//
// kmxlFindControlInNoteList
//
// This routine walks the linked list looking for this control. Because all controls
// are globally allocated, all pControl addresses will be unique. Thus, all
// we really need to do is find the control. If an exact match is found,
// pnnode is returned.
//
PNOTENODE
kmxlFindControlInNoteList(
IN PMXLCONTROL pControl )
{
PNOTENODE pnnode;
PCONTROLLINK pcLink;
PAGED_CODE();
for (pnnode=firstnotenode;pnnode!=NULL;pnnode=pnnode->pNext)
{
//
// Can't check entire structure because pmxd may have been cleaned out.
//
DPFASSERT(pnnode->dwSig == NOTIFICATION_SIGNATURE);
for(pcLink=pnnode->pcLink;pcLink!=NULL;pcLink=pcLink->pNext)
{
DPFASSERT(IsValidControlLink(pcLink));
if( pcLink->pControl == pControl )
{
//
// We found this control in the list!
//
return pnnode;
}
}
}
return NULL;
}
/////////////////////////////////////////////////////////////////////////////
//
// kmxlFindControlTypeInList
//
// This routine walks
//
PMXLCONTROL
kmxlFindControlTypeInList(
IN PNOTENODE pnnode,
IN DWORD dwControlType )
{
PCONTROLLINK pcLink;
PAGED_CODE();
for(pcLink=pnnode->pcLink;pcLink!=NULL;pcLink=pcLink->pNext)
{
DPFASSERT(IsValidControlLink(pcLink));
if( pcLink->pControl->Control.dwControlType == dwControlType )
{
//
// We found this control in the list! Types match.
//
DPF(DL_TRACE|FA_NOTE,("Found Correct pControl %08X",pcLink->pControl) );
return pcLink->pControl;
}
}
return NULL;
}
#ifdef DEBUG
/////////////////////////////////////////////////////////////////////////////
//
// kmxlFindAddressInNoteList
//
// This routine walks the linked list looking for this control within this
// context. If an exact match is found, pnnode is returned.
//
VOID
kmxlFindAddressInNoteList(
IN PMXLCONTROL pControl
)
{
PNOTENODE pnnode;
PCONTROLLINK pcLink;
PAGED_CODE();
for (pnnode=firstnotenode;pnnode!=NULL;pnnode=pnnode->pNext)
{
DPFASSERT(pnnode->dwSig == NOTIFICATION_SIGNATURE);
//
// Let's look to see if this address is one of our pControls!
//
for(pcLink=pnnode->pcLink;pcLink!=NULL;pcLink=pcLink->pNext)
{
DPFASSERT(pcLink->dwSig == CONTROLLINK_SIGNATURE);
if( pcLink->pControl == pControl )
{
//
// We found this control in the list - in the right context!
//
DPF(DL_ERROR|FA_NOTE,("Found pControl(%08X) in our list!",pControl) );
return ;
}
}
}
return ;
}
#endif
/////////////////////////////////////////////////////////////////////////////
//
// kmxlFindNodeInNoteList
//
// Walks the node list looking for this node. This must be called within the
// mtxMutex.
//
NTSTATUS
kmxlFindNodeInNoteList(
IN PNOTENODE pnlookupnode )
{
PNOTENODE pnnode;
PAGED_CODE();
for (pnnode=firstnotenode;pnnode!=NULL;pnnode=pnnode->pNext)
{
if( pnnode == pnlookupnode )
{
//
// Only if we find what we're looking for do we actually verify
// that it's still good.
//
DPFASSERT(IsValidNoteNode(pnnode));
//
// we found this control in the list
//
return STATUS_SUCCESS;
}
}
return STATUS_UNSUCCESSFUL;
}
/////////////////////////////////////////////////////////////////////////////
//
// kmxlFindIdInContextInNoteList
//
// Walks the notification list looking for the node that contains this id in
// this context.
//
PNOTENODE
kmxlFindIdInContextInNoteList(
IN PWDMACONTEXT pWdmaContext,
IN PMIXERDEVICE pmxd,
IN DWORD NodeId
)
{
PNOTENODE pnTmp;
PAGED_CODE();
for (pnTmp=firstnotenode;pnTmp!=NULL;pnTmp=pnTmp->pNext)
{
DPFASSERT(IsValidNoteNode(pnTmp));
if(( pnTmp->NodeId == NodeId ) &&
( pnTmp->pmxd == pmxd ) &&
( pnTmp->pContext == pWdmaContext ) )
{
//
// we found this control in the list in this context.
//
return pnTmp;
}
}
//
// We have walked the list. There is no control with this ID in this Context.
//
return NULL;
}
/////////////////////////////////////////////////////////////////////////////
//
// kmxlFindContextInNoteList
//
PNOTENODE
kmxlFindContextInNoteList(
IN PWDMACONTEXT pWdmaContext
)
{
PNOTENODE pnTmp;
PAGED_CODE();
for (pnTmp=firstnotenode;pnTmp!=NULL;pnTmp=pnTmp->pNext)
{
DPFASSERT(IsValidNoteNode(pnTmp));
if( pnTmp->pContext == pWdmaContext )
{
//
// Found this context in our list.
//
DPFBTRAP();
return pnTmp;
}
}
//
// We have walked the list. There is no control with this ID in this Context.
//
return NULL;
}
/////////////////////////////////////////////////////////////////////////////
//
// kmxlAddControlToNoteList
//
// Adds this pControl to pcLink list.
//
NTSTATUS
kmxlAddControlToNoteList(
IN OUT PNOTENODE pnnode,
IN PMXLCONTROL pControl
)
{
NTSTATUS Status=STATUS_SUCCESS;
PCONTROLLINK pcLink = NULL;
PAGED_CODE();
#ifdef DEBUG
//
// Let's walk the list and make double sure that this node is not already in
// the list.
//
for(pcLink=pnnode->pcLink;pcLink!=NULL;pcLink=pcLink->pNext)
{
if( pcLink->pControl == pControl )
{
DPF(DL_ERROR|FA_NOTE,("pControl(%08X) already in list!") );
return STATUS_UNSUCCESSFUL;
}
}
#endif
Status = AudioAllocateMemory_Fixed(sizeof( CONTROLLINK ),
TAG_AuDL_LINK,
ZERO_FILL_MEMORY,
&pcLink );
if( !NT_SUCCESS( Status ) ) {
DPF(DL_ERROR|FA_NOTE,("Wasn't able to add control to list! Status=%08X",Status) );
return Status;
}
#ifdef DEBUG
pcLink->dwSig=CONTROLLINK_SIGNATURE;
#endif
pcLink->pControl=pControl;
//
// Make new head.
//
// if( pnnode->pcLink != NULL )
// _asm int 3
pcLink->pNext=pnnode->pcLink;
pnnode->pcLink=pcLink;
DPF(DL_TRACE|FA_NOTE,("Added pControl %d to pnnode(%08X)",pControl->Control.dwControlID,pnnode) );
return Status;
}
PCONTROLLINK
kmxlRemoveControlFromNoteList(
IN OUT PNOTENODE pnnode,
IN PMXLCONTROL pControl
)
{
PCONTROLLINK pcLink,pcTmp;
PAGED_CODE();
//
// Does the first node contain our pControl?
//
DPFASSERT(IsValidControlLink(pnnode->pcLink));
for(pcLink=pnnode->pcLink;pcLink!=NULL;pcLink=pcLink->pNext)
{
if( pcLink->pControl == pControl )
break;
}
if( pcLink == pnnode->pcLink )
{
pnnode->pcLink = pcLink->pNext;
DPF(DL_TRACE|FA_NOTE,("removed head pControlLink") );
} else {
for( pcTmp=pnnode->pcLink;pcTmp!=NULL;pcTmp=pcTmp->pNext)
{
if( pcTmp->pNext == pcLink )
{
pcTmp->pNext = pcLink->pNext;
DPF(DL_TRACE|FA_NOTE,("Removed Middle pControlLink") );
break;
}
}
}
// DPFASSERT(IsValidNoteNode(pnnode));
return pcLink;
}
/////////////////////////////////////////////////////////////////////////////
//
// kmxlQueryControlChange
//
// Query's to see if control change notifications are supported on this
// control.
//
NTSTATUS
kmxlQueryControlChange(
IN PFILE_OBJECT pfo, // Handle of the topology driver instance
IN ULONG NodeId //PMXLCONTROL pControl
)
{
NTSTATUS Status;
KSE_NODE KsNodeEvent;
ULONG BytesReturned;
PAGED_CODE();
// initialize event for basic support query
RtlZeroMemory(&KsNodeEvent,sizeof(KSE_NODE));
KsNodeEvent.Event.Set = KSEVENTSETID_AudioControlChange;
KsNodeEvent.Event.Id = KSEVENT_CONTROL_CHANGE;
KsNodeEvent.Event.Flags = KSEVENT_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_TOPOLOGY;
KsNodeEvent.NodeId = NodeId;
DPF(DL_TRACE|FA_SYSAUDIO,("IOCTL_KS_ENABLE_EVENT") );
Status = KsSynchronousIoControlDevice(
pfo, // The FILE_OBJECT for SysAudio
KernelMode, // Call originates in Kernel mode
IOCTL_KS_ENABLE_EVENT, // KS PROPERTY IOCTL
&KsNodeEvent, // Pointer to the KSNODEPROPERTY struct
sizeof(KSE_NODE), // Number or bytes input
NULL, // Pointer to the buffer to store output
0, // Size of the output buffer
&BytesReturned // Number of bytes returned from the call
);
if (!NT_SUCCESS(Status)) {
DPF( DL_MAX|FA_HARDWAREEVENT, ("NODE #%d: KSEVENT_CONTROL_CHANGE Not Supported Error %08X",NodeId,Status) );
RETURN( Status );
}
DPF(DL_TRACE|FA_HARDWAREEVENT ,("NodeId #%d: KSEVENT_CONTROL_CHANGE Supported",NodeId) );
return Status;
}
/////////////////////////////////////////////////////////////////////////////
//
// kmxlEnableControlChange
//
// Turns on Control chnage notifications on this control.
//
NTSTATUS
kmxlEnableControlChange(
IN PFILE_OBJECT pfo, // Handle of the topology driver instance
IN ULONG NodeId, //PMXLCONTROL pControl,
IN OUT PKSEVENTDATA pksed
)
{
NTSTATUS Status;
KSE_NODE KsNodeEvent;
ULONG BytesReturned;
PAGED_CODE();
// try to add an event
RtlZeroMemory(&KsNodeEvent,sizeof(KSE_NODE));
KsNodeEvent.Event.Set = KSEVENTSETID_AudioControlChange;
KsNodeEvent.Event.Id = KSEVENT_CONTROL_CHANGE;
KsNodeEvent.Event.Flags = KSEVENT_TYPE_ENABLE | KSPROPERTY_TYPE_TOPOLOGY;
KsNodeEvent.NodeId = NodeId;
DPF(DL_TRACE|FA_SYSAUDIO,("IOCTL_KS_ENABLE_EVENT") );
Status = KsSynchronousIoControlDevice(
pfo, // The FILE_OBJECT for SysAudio
KernelMode, // Call originates in Kernel mode
IOCTL_KS_ENABLE_EVENT, // KS PROPERTY IOCTL
&KsNodeEvent, // Pointer to the KSNODEPROPERTY struct
sizeof(KSE_NODE), // Number or bytes input
pksed, // Pointer to the buffer to store output
sizeof(KSEVENTDATA), // Size of the output buffer
&BytesReturned // Number of bytes returned from the call
);
if (!NT_SUCCESS(Status)) {
DPF(DL_WARNING|FA_HARDWAREEVENT ,("KSEVENT_CONTROL_CHANGE Enable FAILED Error %08X",Status) );
RETURN( Status );
}
DPF(DL_TRACE|FA_HARDWAREEVENT ,
("KSEVENT_CONTROL_CHANGE Enabled on NodeId #%d",NodeId) );
return Status;
}
/////////////////////////////////////////////////////////////////////////////
//
// kmxlDisableControlChange
//
// Turns off Control chnage notifications on this control.
//
NTSTATUS
kmxlDisableControlChange(
IN PFILE_OBJECT pfo, // Handle of the topology driver instance
IN ULONG NodeId, //PMXLCONTROL pControl,
IN OUT PKSEVENTDATA pksed
)
{
NTSTATUS Status;
KSE_NODE KsNodeEvent;
ULONG BytesReturned;
PAGED_CODE();
// initialize event for basic support query
RtlZeroMemory(&KsNodeEvent,sizeof(KSE_NODE));
KsNodeEvent.Event.Set = KSEVENTSETID_AudioControlChange;
KsNodeEvent.Event.Id = KSEVENT_CONTROL_CHANGE;
KsNodeEvent.Event.Flags = KSEVENT_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_TOPOLOGY;
KsNodeEvent.NodeId = NodeId;
DPF(DL_TRACE|FA_SYSAUDIO,("IOCTL_KS_DISABLE_EVENT") );
Status = KsSynchronousIoControlDevice(
pfo, // The FILE_OBJECT for SysAudio
KernelMode, // Call originates in Kernel mode
IOCTL_KS_DISABLE_EVENT, // KS PROPERTY IOCTL
pksed, // Pointer to the buffer to store output
sizeof(KSEVENTDATA), // Size of the output buffer
NULL, // Pointer to the KSNODEPROPERTY struct
0, // Number or bytes input
&BytesReturned // Number of bytes returned from the call
);
if (!NT_SUCCESS(Status)) {
DPF(DL_WARNING|FA_HARDWAREEVENT,("KSEVENT_CONTROL_CHANGE Disable FAILED") );
RETURN( Status );
}
DPF(DL_TRACE|FA_HARDWAREEVENT,
("KSEVENT_CONTROL_CHANGE disabled on NodeId #%d",NodeId) );
return Status;
}
VOID
kmxlGrabNoteMutex(
)
{
//
// Turn off the APCDisable flag in the thread structure before going for our
// mutex. This will prevent us from getting suspeneded while holding this
// mutex.
//
KeEnterCriticalRegion();
KeWaitForMutexObject(&mtxNote, Executive, KernelMode, FALSE, NULL);
}
VOID
kmxlReleaseNoteMutex(
)
{
KeReleaseMutex(&mtxNote, FALSE);
KeLeaveCriticalRegion();
}
/////////////////////////////////////////////////////////////////////////////
//
// kmxlEnableControlChangeNotifications
//
// This routine gets called every time a new control is created. At that point
// we're going to query the node to see if it supports change notifications
// and enable them in this context.
//
NTSTATUS
kmxlEnableControlChangeNotifications(
IN PMIXEROBJECT pmxobj,
IN PMXLLINE pLine, // Line that owns control
IN PMXLCONTROL pControl // Control to Enable
)
{
PNOTENODE pnnode;
NTSTATUS Status;
LONG i;
PMIXERDEVICE pmxd;
PWDMACONTEXT pWdmaContext;
kmxlGrabNoteMutex();
DPFASSERT(IsValidMixerObject(pmxobj));
pmxd=pmxobj->pMixerDevice;
DPFASSERT(IsValidMixerDevice(pmxd));
pWdmaContext=pmxd->pWdmaContext;
PAGED_CODE();
//
// Never allow anything in the list if it's not valid!
//
DPFASSERT(IsValidControl(pControl));
//
// The first thing we do is look to see if a control of this ID is enabled in
// this context. If so, we'll get a PNOTENODE structure returned to us that
// contains that ID. All we should have to do is add our new pControl.
//
if( pnnode=kmxlFindIdInContextInNoteList(pWdmaContext,pmxd,pControl->Id) ) // Not pControl->Control.dwControlID
{
//
// yes there is. Add this pControl to this pnnode and we're done.
//
Status=kmxlAddControlToNoteList(pnnode,pControl);
} else {
//
// There is no Control with this ID in this Context. Let's try to
// Add one.
//
//
// This node is not in our list, we need to add it if and only if it
// supports change notifications
//
Status=kmxlQueryControlChange(pmxd->pfo,pControl->Id); //pLocfo
if( NT_SUCCESS(Status) )
{
//
// This control supports notifications, thus add info to our
// global list - if it's not already there...
//
if( (pnnode=kmxlNewNoteNode()) != NULL )
{
if( (pnnode->pcLink=kmxlNewControlLink(pControl)) != NULL )
{
pnnode->NodeId=pControl->Id;
//
// We have a new notification node to fill out.
//
pnnode->ControlID=pControl->Control.dwControlID;
pnnode->ControlType=pControl->Control.dwControlType;
pnnode->LineID=pLine->Line.dwLineID;
pnnode->pContext=pWdmaContext; //NOTENODE
pnnode->pmxd=pmxd;
DPF(DL_TRACE|FA_NOTE ,
("Init pnnode, NodeId=#%d: CtrlID=%X CtrlType=%X Context=%08X",
pnnode->NodeId, //pControl->Id
pnnode->ControlID, //pControl->Control.dwControlID,
pnnode->ControlType,
pnnode->pContext) ); //pControl->Control.dwControlType) );
//
// Now setup the DPC handling
//
KeInitializeDpc(&pnnode->NodeEventDPC,NodeEventDPCHandler,pnnode);
pnnode->NodeEventData.NotificationType=KSEVENTF_DPC;
pnnode->NodeEventData.Dpc.Dpc=&pnnode->NodeEventDPC;
//
// At this point, we've got a little window. We call and enable
// events on this control. From that time until the time we actually
// add it to the list, if an event fires, we will not find this node
// in the list - thus we will not process it.
//
Status=kmxlEnableControlChange(pnnode->pmxd->pfo,
pnnode->NodeId, //pControl,
&pnnode->NodeEventData);
//&NodeEvent[NumEventDPCs].NodeEventData);
if( NT_SUCCESS(Status) )
{
DPF(DL_TRACE|FA_HARDWAREEVENT,("Enabled Control #%d in context(%08X)!",pControl->Id,pWdmaContext) );
//
// Now let's add it to our global list
//
//
kmxlAddNoteNodeToList(pnnode);
DPFASSERT(IsValidNoteNode(pnnode));
} else {
DPF(DL_WARNING|FA_HARDWAREEVENT,("Failed to Enable Control #%d!",pControl->Id) );
DPFBTRAP();
//
// For some reason, this node failed to enable.
//
kmxlFreeControlLink(pnnode->pcLink);
kmxlFreeNoteNode(pnnode);
}
} else {
DPF(DL_ERROR|FA_NOTE,("kmxlNewControlLink failed") );
kmxlFreeNoteNode(pnnode);
}
} else {
DPF(DL_WARNING|FA_NOTE,("kmxlNewNoteNode failed") );
}
} else {
DPF(DL_MAX|FA_HARDWAREEVENT,("This control #%d doesn't support change notifications",pControl->Id) );
}
}
kmxlReleaseNoteMutex();
return Status;
}
/////////////////////////////////////////////////////////////////////////////
//
// kmxlDisableControlChangeNotifications
//
// This routine gets called every time a control is freed. We walk the list
// of enable controls and see if it's there. If so, we disable and clean up.
// if it's not in the list, it didn't support change notifications.
//
NTSTATUS
kmxlDisableControlChangeNotifications(
IN PMXLCONTROL pControl
)
{
NTSTATUS Status=STATUS_SUCCESS;
PNOTENODE pnnode;
PAGED_CODE();
kmxlGrabNoteMutex();
DPFASSERT(IsValidControl(pControl));
//
// Find this control in our list.
//
if(pnnode=kmxlFindControlInNoteList(pControl)) //pWdmaContext,
{
PCONTROLLINK pcLink;
#ifdef DEBUG
if( pControl->Id != pnnode->NodeId )
{
DPF(DL_ERROR|FA_NOTE,("Control NodeId Changed! CtrlId=%08X,pnnodeID=%08X",
pControl->Id,pnnode->NodeId) );
}
#endif
//
// This call removes pControl from this node. note that after this
// control is removed, pnnode->pcLink will be NULL if there are no
// more controls hanging on this Notification node. Thus, we need
// to disable it.
//
pcLink=kmxlRemoveControlFromNoteList(pnnode,pControl);
if( pnnode->pcLink == NULL )
{
//
// During cleanup, the mixer device structure will have already been
// cleaned up. This can be seen because the pmxd->Device entry will
// be UNUSED_DEVICE. Thus, we can't do anything on that mixerdevice.
//
if( ( pnnode->pmxd->Device != UNUSED_DEVICE ) &&
( pnnode->pmxd->pfo != NULL ) )
{
//
// There are no references to this node, we can free it. But, if
// this disable call fails, then the node was already distroyed.
//
Status=kmxlDisableControlChange(pnnode->pmxd->pfo,pnnode->NodeId,&pnnode->NodeEventData);
if( !NT_SUCCESS(Status) )
{
DPF(DL_WARNING|FA_NOTE,("Not able to disable! pnnode %08X",pnnode) );
}
} else {
DPF(DL_WARNING|FA_NOTE,("pmxd is invalid %08X",pnnode->pmxd) );
}
kmxlRemoveNoteNodeFromList(pnnode);
kmxlFreeNoteNode(pnnode);
}
DPF(DL_TRACE|FA_NOTE,("Removed PCONTROLLINK(%08X) for pControl(%08X)",pcLink,pcLink->pControl) );
kmxlFreeControlLink(pcLink);
} else {
//
// We get called on every control free. Thus, many will not be in our list.
//
DPF(DL_MAX|FA_NOTE,("Control=%d not in List!",pControl->Id) );
}
kmxlReleaseNoteMutex();
return Status;
}
/////////////////////////////////////////////////////////////////////////////
//
// kmxlRemoveContextFromNoteList
//
// This routine gets called when this context is going away. Thus, we need to
// remove it from our list.
//
VOID
kmxlRemoveContextFromNoteList(
IN PWDMACONTEXT pContext
)
{
NTSTATUS Status;
PNOTENODE pnnode;
PCONTROLLINK pcLink;
PAGED_CODE();
kmxlGrabNoteMutex();
//
// If we find this context is still alive in our list, someone leaked a control.
// Things should have been cleaned up when the controls went away! But, for
// some reasons they didn't.
//
// There could be multiple pContext nodes in list.
//
while( (pnnode=kmxlFindContextInNoteList(pContext)) != NULL )
{
DPFASSERT(IsValidNoteNode(pnnode));
kmxlRemoveNoteNodeFromList(pnnode);
//
// There can be multiple Controls on this Notification node.
//
while( (pnnode->pcLink != NULL) &&
( (pcLink=kmxlRemoveControlFromNoteList(pnnode,pnnode->pcLink->pControl)) != NULL) )
{
//
// To get here, pcLink is Valid. If it was the last pControl, then
// we want to turn off change notifications on it.
//
if( pnnode->pcLink == NULL )
{
//
// There are no references to this node, we can free it.
//
Status=kmxlDisableControlChange(pnnode->pmxd->pfo,pnnode->NodeId,&pnnode->NodeEventData);
DPFASSERT( Status == STATUS_SUCCESS );
DPFBTRAP();
}
kmxlFreeControlLink(pcLink);
DPFBTRAP();
}
kmxlFreeNoteNode(pnnode);
}
DPF(DL_TRACE|FA_NOTE,("pWdmaContext %08X going away",pContext) );
kmxlReleaseNoteMutex();
}
/////////////////////////////////////////////////////////////////////////////
//
// kmxlCleanupNotelist
//
// Driver is unloading, turn everything off and free the memory!
//
VOID
kmxlCleanupNoteList(
)
{
NTSTATUS Status;
PNOTENODE pnnode,pnnodeFree;
PCONTROLLINK pcLink;
PAGED_CODE();
kmxlGrabNoteMutex();
//
// If we find this context is still alive in our list, someone leaked a control.
// Things should have been cleaned up when the controls went away! But, for
// some reasons they didn't.
//
// There could be multiple pContext nodes in list.
//
pnnode=firstnotenode;
while( pnnode )
{
DPFASSERT(IsValidNoteNode(pnnode));
DPF(DL_ERROR|FA_NOTE,("pnnode(%08X) found in Notification List!",pnnode) );
kmxlRemoveNoteNodeFromList(pnnode);
//
// There can be multiple Controls on this Notification node.
//
while( (pnnode->pcLink != NULL) &&
( (pcLink=kmxlRemoveControlFromNoteList(pnnode,pnnode->pcLink->pControl)) != NULL) )
{
//
// To get here, pcLink is Valid. If it was the last pControl, then
// we want to turn off change notifications on it.
//
if( pnnode->pcLink == NULL )
{
//
// There are no references to this node, we can free it.
//
Status=kmxlDisableControlChange(pnnode->pmxd->pfo,pnnode->NodeId,&pnnode->NodeEventData);
DPFASSERT( Status == STATUS_SUCCESS );
DPFBTRAP();
}
kmxlFreeControlLink(pcLink);
DPFBTRAP();
}
pnnodeFree=pnnode;
pnnode=pnnode->pNext;
kmxlFreeNoteNode(pnnodeFree);
DPFBTRAP();
}
DPF(DL_TRACE|FA_NOTE,("Done with cleanup") );
kmxlReleaseNoteMutex();
}
/////////////////////////////////////////////////////////////////////////////
//
// kmxlPersistHWControlWorker
//
// When kmxlPersistHWControlWorker gets called, the pData value is a pointer
// to a globally allocated NOTENODE structure. Thus, we have all the context
// we need with regard to persisting the control. We just need to make sure
// that our node didn't go away between the time the evern was scheduled and
// when we got called.
//
VOID
kmxlPersistHWControlWorker(
PVOID pReference
)
{
NTSTATUS Status;
PMXLCONTROL pControl;
PVOID paDetails = NULL; // kmxlPersistSingleControl() allocates paDetails
// via kmxlGetCurrentControlValue()
PNOTENODE pnnode;
PHWLINK phwlink;
PLIST_ENTRY ple;
PAGED_CODE();
//
// We set HardwareCallbackSchedule to 0 here so that we can start adding new
// events for handling hardware notifications. Note: we do that here at the
// start of the routine so that there will not be a window where we have
// something in the list that we never get a event scheduled for. In other
// words, this routine handles empty lists.
//
HardwareCallbackScheduled=0;
//
// while we have events in our queue, get one and service it.
//
while((ple = ExInterlockedRemoveHeadList(&HardwareCallbackListHead,
&HardwareCallbackSpinLock)) != NULL)
{
phwlink = CONTAINING_RECORD(ple, HWLINK, Next);
DPFASSERT(phwlink->dwSig == HWLINK_SIGNATURE);
//
// Get our data for this event and then free the link that was allocated in
// the DPC handler.
//
pnnode=phwlink->pnnode;
AudioFreeMemory(sizeof(HWLINK),&phwlink);
//
// We are going to be working in this context for a while. Thus, we're going
// to enter our mtxNote mutex to make sure that our node doesn't go away
// while we're persisting the values!
//
kmxlGrabNoteMutex();
//
// Now our list can't chnage! Is this node still valid? If we don't find
// it in the list, it was removed before this event fired. Thus, there is
// nothing that we can do. --- Free mutex and leave.
//
Status=kmxlFindNodeInNoteList(pnnode);
if( NT_SUCCESS(Status) )
{
DPF( DL_TRACE|FA_HARDWAREEVENT ,
("Entering NodeId %d LineID %X ControlID %d ControlType = %X",
pnnode->NodeId, pnnode->LineID, pnnode->ControlID, pnnode->ControlType) );
//
// Yes. It's still valid. Persist the control.
//
pControl=kmxlFindControlTypeInList(pnnode,pnnode->ControlType);
if( pControl )
{
Status = kmxlPersistSingleControl(
pnnode->pmxd->pfo,
pnnode->pmxd,
pControl, // pControl here...
paDetails
);
}
if( !NT_SUCCESS(Status) )
{
//
// On shutdown, we might get an event that fires after things have
// been cleaned up.
//
if( Status != STATUS_TOO_LATE )
{
DPF(DL_WARNING|FA_NOTE, ("Failure from kmxlPersistSingleControl Status=%X",Status) );
}
}
else {
DPF(DL_TRACE|FA_HARDWAREEVENT ,("Done - success") );
}
} else {
DPF(DL_WARNING|FA_NOTE,("pnnode=%08X has been removed!",pnnode) );
}
//
// Persist this control!
//
kmxlReleaseNoteMutex();
}
DPF(DL_TRACE|FA_HARDWAREEVENT ,("exit") );
}
/////////////////////////////////////////////////////////////////////////////
//
// kmxlGetLineForControl
//
// For every line on this mixer device, look at every control to see if we
// can find this control. If found, return this line pointer.
//
NTSTATUS
kmxlEnableAllControls(
IN PMIXEROBJECT pmxobj
)
{
NTSTATUS Status=STATUS_SUCCESS;
PMIXERDEVICE pmxd;
PMXLLINE pLine;
PMXLCONTROL pControl;
PAGED_CODE();
//
// The first time through we will most likily not have a pfo in the MIXERDEVICE
// structure, thus fill it in!
//
DPFASSERT(pmxobj->dwSig == MIXEROBJECT_SIGNATURE );
DPFASSERT(pmxobj->pMixerDevice != NULL );
DPFASSERT(pmxobj->pMixerDevice->dwSig == MIXERDEVICE_SIGNATURE );
pmxd=pmxobj->pMixerDevice;
if( pmxd->pfo == NULL )
{
DPF(DL_WARNING|FA_NOTE,("fo is NULL, it should have been set!") );
//
// We need to assign a SysAudio FILE_OBJECT to this mixer device so that
// we can talk to it.
//
if( NULL==(pmxd->pfo=kmxlOpenSysAudio())) {
DPF(DL_WARNING|FA_NOTE,("OpenSysAudio failed") );
return STATUS_UNSUCCESSFUL;
}
Status = SetSysAudioProperty(
pmxd->pfo,
KSPROPERTY_SYSAUDIO_DEVICE_INSTANCE,
sizeof( pmxd->Device ),
&pmxd->Device
);
if( !NT_SUCCESS( Status ) ) {
kmxlCloseSysAudio( pmxd->pfo );
pmxd->pfo=NULL;
DPF(DL_WARNING|FA_NOTE,("SetSysAudioProperty failed %X",Status) );
return Status;
}
}
DPFASSERT(IsValidMixerObject(pmxobj));
for(pLine = kmxlFirstInList( pmxd->listLines );
pLine != NULL;
pLine = kmxlNextLine( pLine )
)
{
DPFASSERT(IsValidLine(pLine));
for(pControl = kmxlFirstInList( pLine->Controls );
pControl != NULL;
pControl = kmxlNextControl( pControl )
)
{
DPFASSERT(IsValidControl(pControl));
//
// Enable Notifications if it supports it here.
//
DPF(DL_TRACE|FA_NOTE,("pControl->Id=%d, pControl->Control.dwControlID=%d",
pControl->Id,pControl->Control.dwControlID) );
Status = kmxlEnableControlChangeNotifications(pmxobj,pLine,pControl);
}
}
return Status;
}
VOID
kmxlCloseMixerDevice(
IN OUT PMIXERDEVICE pmxd
)
{
if(pmxd->pfo)
{
kmxlCloseSysAudio( pmxd->pfo );
pmxd->pfo = NULL;
}
}
/////////////////////////////////////////////////////////////////////////////
//
// GetHardwareEventData
//
// Called by user mode driver to get the notification information.
//
VOID GetHardwareEventData(LPDEVICEINFO pDeviceInfo)
{
PAGED_CODE();
if (emptyindex!=loadindex) {
(pDeviceInfo->dwID)[0]=callbacks[emptyindex%CALLBACKARRAYSIZE].dwControlID;
pDeviceInfo->dwLineID=callbacks[emptyindex%CALLBACKARRAYSIZE].dwLineID;
pDeviceInfo->dwCallbackType=callbacks[emptyindex%CALLBACKARRAYSIZE].dwCallbackType;
pDeviceInfo->ControlCallbackCount=1;
emptyindex++;
}
pDeviceInfo->mmr=MMSYSERR_NOERROR;
}
///////////////////////////////////////////////////////////////////////
//
// kmxdInit
//
// Checks to see if the mixer lines have been built for the given
// index. If not, it calls kmxlBuildLines() to build up the lines.
//
// The topology information is kept around so that it can be dumped
// via a debugger command.
//
//
NTSTATUS
kmxlInit(
IN PFILE_OBJECT pfo, // Handle of the topology driver instance
IN PMIXERDEVICE pMixer
)
{
NTSTATUS Status = STATUS_SUCCESS;
HANDLE hKey;
ULONG ResultLength;
PAGED_CODE();
//
// Check to see if the lines have already been built for this device.
// If so, return success.
//
if( pMixer->listLines ) {
RETURN( STATUS_SUCCESS );
}
//
// Build the lines for this device.
//
Status = kmxlBuildLines(
pMixer,
pfo,
&pMixer->listLines,
&pMixer->cDestinations,
&pMixer->Topology
);
if( NT_SUCCESS( Status ) ) {
Status = kmxlOpenInterfaceKey( pfo, pMixer->Device, &hKey );
if( !NT_SUCCESS( Status ) ) {
pMixer->Mapping = MIXER_MAPPING_LOGRITHMIC;
Status = STATUS_SUCCESS;
goto exit;
}
Status = kmxlRegQueryValue( hKey,
L"CurveType",
&pMixer->Mapping,
sizeof( pMixer->Mapping ),
&ResultLength
);
if( !NT_SUCCESS( Status ) ) {
kmxlRegCloseKey( hKey );
pMixer->Mapping = MIXER_MAPPING_LOGRITHMIC;
Status = STATUS_SUCCESS;
goto exit;
}
kmxlRegCloseKey( hKey );
}
exit:
//
// Free up the topology allocated when the lines are built (RETAIL only).
//
#ifndef DEBUG
if(pMixer->Topology.Categories) {
ExFreePool(
( (( PKSMULTIPLE_ITEM )
pMixer->Topology.Categories )) - 1 );
pMixer->Topology.Categories = NULL;
}
if(pMixer->Topology.TopologyNodes) {
ExFreePool(
( (( PKSMULTIPLE_ITEM )
pMixer->Topology.TopologyNodes )) - 1 );
pMixer->Topology.TopologyNodes = NULL;
}
if(pMixer->Topology.TopologyConnections) {
ExFreePool(
( (( PKSMULTIPLE_ITEM )
pMixer->Topology.TopologyConnections )) - 1 );
pMixer->Topology.TopologyConnections = NULL;
}
#endif // !DEBUG
RETURN( Status );
}
////////////////////////////////////////////////////////////////////////
//
// kmxdDeInit
//
// Loops through each of the lines freeing the control structures and
// then the line structures.
//
//
NTSTATUS
kmxlDeInit(
PMIXERDEVICE pMixer
)
{
PMXLLINE pLine = NULL;
PMXLCONTROL pControl = NULL;
PAGED_CODE();
if( pMixer->Device != UNUSED_DEVICE ) {
while( pMixer->listLines ) {
pLine = kmxlRemoveFirstLine( pMixer->listLines );
while( pLine && pLine->Controls ) {
pControl = kmxlRemoveFirstControl( pLine->Controls );
kmxlFreeControl( pControl );
}
AudioFreeMemory( sizeof(MXLLINE),&pLine );
}
//
// Here we need to close sysaudio as used in this mixer device.
//
kmxlCloseMixerDevice(pMixer);
ASSERT( pMixer->listLines == NULL );
//
// Free up the topology (DEBUG only)
//
#ifdef DEBUG
if(pMixer->Topology.Categories) {
ExFreePool(( (( PKSMULTIPLE_ITEM )
pMixer->Topology.Categories )) - 1 );
pMixer->Topology.Categories = NULL;
}
if(pMixer->Topology.TopologyNodes) {
ExFreePool(( (( PKSMULTIPLE_ITEM )
pMixer->Topology.TopologyNodes )) - 1 );
pMixer->Topology.TopologyNodes = NULL;
}
if(pMixer->Topology.TopologyConnections) {
ExFreePool(( (( PKSMULTIPLE_ITEM )
pMixer->Topology.TopologyConnections )) - 1 );
pMixer->Topology.TopologyConnections = NULL;
}
#endif // !DEBUG
} // if
RETURN( STATUS_SUCCESS );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlBuildLines
//
// Builds up the line structures and count of destinations for the
// given instance.
//
//
NTSTATUS
kmxlBuildLines(
IN PMIXERDEVICE pMixer, // The mixer
IN PFILE_OBJECT pfoInstance, // The FILE_OBJECT of a filter instance
IN OUT LINELIST* plistLines, // Pointer to the list of all lines
IN OUT PULONG pcDestinations, // Pointer to the number of dests
IN OUT PKSTOPOLOGY pTopology // Pointer to a topology structure
)
{
NTSTATUS Status = STATUS_SUCCESS;
MIXEROBJECT mxobj;
LINELIST listSourceLines = NULL;
NODELIST listSources = NULL;
NODELIST listDests = NULL;
PMXLNODE pTemp = NULL;
ULONG i;
PAGED_CODE();
ASSERT( pfoInstance );
ASSERT( plistLines );
ASSERT( pcDestinations );
ASSERT( pTopology );
RtlZeroMemory( &mxobj, sizeof( MIXEROBJECT ) );
// Set up the MIXEROBJECT. Note that this structure is used only within
// the scope of this function, so it is okay to simply copy the
// DeviceInterface pointer from the MIXERDEV structure.
mxobj.pfo = pfoInstance;
mxobj.pTopology = pTopology;
mxobj.pMixerDevice = pMixer;
mxobj.DeviceInterface = pMixer->DeviceInterface;
#ifdef DEBUG
mxobj.dwSig = MIXEROBJECT_SIGNATURE;
#endif
//
// Read the topology from the device
//
DPF(DL_TRACE|FA_MIXER,("Querying Topology") );
Status = kmxlQueryTopology( mxobj.pfo, mxobj.pTopology );
if( !NT_SUCCESS( Status ) ) {
goto exit;
}
//
// Build up the node table. The node table is the mixer line's internal
// representation of the topology for easier processing.
//
DPF(DL_TRACE|FA_MIXER,("Building Node Table") );
mxobj.pNodeTable = kmxlBuildNodeTable( mxobj.pTopology );
if( !mxobj.pNodeTable ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
DPF(DL_WARNING|FA_MIXER,("kmxlBuildNodeTable failed") );
goto exit;
}
//
// Parse the topology and build the necessary data structures
// to walk the topology.
//
DPF(DL_TRACE|FA_MIXER,("Parsing Topology") );
Status = kmxlParseTopology(
&mxobj,
&listSources,
&listDests );
if( !NT_SUCCESS( Status ) ) {
DPF(DL_WARNING|FA_MIXER,("kmxlParseTopoloty failed Status=%X",Status) );
goto exit;
}
//
// Build up a list of destination lines.
//
DPF(DL_TRACE|FA_MIXER,("Building Destination lines") );
*plistLines = kmxlBuildDestinationLines(
&mxobj,
listDests
);
if( !(*plistLines) ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
DPF(DL_WARNING|FA_MIXER,("kmxlBuildDestinationLines failed") );
goto exit;
}
//
// Assign the line Ids and the Control Ids for the destinations. Also,
// fill in the number of destinations.
//
kmxlAssignLineAndControlIds( &mxobj, (*plistLines), DESTINATION_LIST );
*pcDestinations = kmxlListLength( (*plistLines) );
//
// Build up a list of source lines
//
DPF(DL_TRACE|FA_MIXER,("Building Source lines") );
listSourceLines = kmxlBuildSourceLines(
&mxobj,
listSources,
listDests
);
if( !listSourceLines ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
DPF(DL_WARNING|FA_MIXER,("kmxlBuildSourceLines failed") );
goto exit;
}
//
// Polish off the lines. First sort them by destination so that
// the source Ids will be assigned correctly.
//
DPF(DL_TRACE|FA_MIXER,("Sort By Destination") );
kmxlSortByDestination( &listSourceLines );
DPF(DL_TRACE|FA_MIXER,("Assign Line and Control Ids") );
kmxlAssignLineAndControlIds( &mxobj, listSourceLines, SOURCE_LIST );
//
// Now assign destinations to sources and construct the line Ids for
// the source lines.
//
DPF(DL_TRACE|FA_MIXER,("Assign Destinations to Sources") );
kmxlAssignDestinationsToSources( listSourceLines, (*plistLines) );
//
// Update the number of sources mapping to each of the destinations.
//
DPF(DL_TRACE|FA_MIXER,("Update Destination Connection Count") );
kmxlUpdateDestintationConnectionCount( listSourceLines, (*plistLines) );
//
// Assign the dwComponentIds for the source and destination lines.
//
DPF(DL_TRACE|FA_MIXER,("Assign Conponent IDs") );
kmxlAssignComponentIds( &mxobj, listSourceLines, (*plistLines) );
//
// Construct a single list of lines. Destinations will be first in
// increasing numerical order by line id, folowed by sources in
// increasing numerical order.
//
kmxlAppendListToEndOfList( (PSLIST*) plistLines, (PSLIST) listSourceLines );
//
// Eliminate any lines that are invalid.
//
DPF(DL_TRACE|FA_MIXER,("Eliminate Invalid Lines") );
kmxlEliminateInvalidLines( plistLines );
//
// Update the mux line IDs to match real line IDs
//
DPF(DL_TRACE|FA_MIXER,("Assign Mux IDs") );
kmxlAssignMuxIds( &mxobj, *plistLines );
//
// Here is where we want to Enable Change Notifications on all controls
// that support notifications.
//
DPF(DL_TRACE|FA_MIXER,("Enable All Controls") );
kmxlEnableAllControls(&mxobj);
exit:
//
// If we got here because of an error, clean up all the mixer lines
//
if( !NT_SUCCESS( Status ) ) {
PMXLLINE pLine;
PMXLCONTROL pControl;
while( (*plistLines) ) {
pLine = kmxlRemoveFirstLine( (*plistLines) );
while( pLine && pLine->Controls ) {
pControl = kmxlRemoveFirstControl( pLine->Controls );
kmxlFreeControl( pControl );
}
AudioFreeMemory( sizeof(MXLLINE),&pLine );
}
}
//
// Free up the mux control list. Note that we don't want to free
// the controls using kmxlFreeControl() because we need the special
// mux instance data to persist.
//
{
PMXLCONTROL pControl;
while( mxobj.listMuxControls ) {
pControl = kmxlRemoveFirstControl( mxobj.listMuxControls );
ASSERT( pControl->pChannelStepping == NULL);
AudioFreeMemory( sizeof(MXLCONTROL),&pControl );
}
}
//
// Free up the source and destination lists. Both types of these lists
// are allocated list nodes and allocated nodes. Both need to be freed.
// The Children and Parent lists, though, are only allocated list nodes.
// The actual nodes are contained in the node table and will be deallocated
// in one chunk in the next block of code.
//
while( listSources ) {
pTemp = kmxlRemoveFirstNode( listSources );
kmxlFreePeerList( pTemp->Children );
AudioFreeMemory( sizeof(MXLNODE),&pTemp );
}
while( listDests ) {
pTemp = kmxlRemoveFirstNode( listDests );
kmxlFreePeerList( pTemp->Parents );
AudioFreeMemory( sizeof(MXLNODE),&pTemp );
}
//
// Free up the peer lists for the children and parents inside the
// nodes of the node table. Finally, deallocate the array of nodes.
//
if( mxobj.pNodeTable ) {
for( i = 0; i < mxobj.pTopology->TopologyNodesCount; i++ ) {
kmxlFreePeerList( mxobj.pNodeTable[ i ].Children );
kmxlFreePeerList( mxobj.pNodeTable[ i ].Parents );
}
AudioFreeMemory_Unknown( &mxobj.pNodeTable );
}
RETURN( Status );
}
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// //
// M I X E R L I N E F U N C T I O N S //
// //
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
//
// kmxlBuildDestinationLines
//
// Loops through each of the destination nodes, allocates a line
// structure for it, and calls kmxlBuildDestinationControls to
// build the controls for that line.
//
//
LINELIST
kmxlBuildDestinationLines(
IN PMIXEROBJECT pmxobj,
IN NODELIST listDests // The list of destination nodes
)
{
LINELIST listDestLines = NULL;
PMXLNODE pDest = NULL;
PMXLLINE pLine = NULL;
PMXLCONTROL pControl = NULL;
ULONG MaxChannelsForLine;
ASSERT( pmxobj );
ASSERT( listDests );
PAGED_CODE();
//
// Loop over all the destination node allocating a line structure
// for each.
//
pDest = kmxlFirstInList( listDests );
while( pDest ) {
//
// Allocate a new line structure and add it to the list of
// destination lines.
//
pLine = kmxlAllocateLine( TAG_AudL_LINE );
if( !pLine ) {
goto exit;
}
kmxlAddToList( listDestLines, pLine );
//
// Fill in the details of the line structure. Some fields will
// be filled in later.
//
pLine->DestId = pDest->Id;
pLine->Type = pDest->NodeType;
pLine->Communication = pDest->Communication;
pLine->Line.cbStruct = sizeof( MIXERLINE );
pLine->Line.dwSource = (DWORD) -1;
pLine->Line.dwDestination = (DWORD) -1;
kmxlGetPinName( pmxobj->pfo, pDest->Id, pLine );
//
// HACK! The ACTIVE flag should only be set when the line is active
// but then no lines show up in SNDVOL32. It works if the flag is
// always set to ACTIVE for destinations. Also, the number of channels
// should be queried not hard coded. WDM Audio does not provide a
// way to easily query this.
//
pLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE;
pLine->Line.cChannels = 1; // should this default to 1 or 2?
//
// Build up a list of the controls on this destination
//
if( !NT_SUCCESS( kmxlBuildDestinationControls(
pmxobj,
pDest,
pLine
) ) )
{
DPF(DL_WARNING|FA_MIXER,("kmxlBuildDestinationControls failed") );
goto exit;
}
pDest = kmxlNextNode( pDest );
}
pLine = kmxlFirstInList( listDestLines );
while( pLine ) {
MaxChannelsForLine = 1;
pControl = kmxlFirstInList( pLine->Controls );
while( pControl ) {
ASSERT( IsValidControl( pControl ) );
if ( pControl->NumChannels > MaxChannelsForLine) {
MaxChannelsForLine = pControl->NumChannels;
}
pControl = kmxlNextControl( pControl );
}
if( pLine->Controls == NULL ) {
pLine->Line.cChannels = 1; // should this default to 1 or 2?
} else {
pLine->Line.cChannels = MaxChannelsForLine;
}
pLine = kmxlNextLine( pLine );
}
return( listDestLines );
exit:
//
// A memory allocation failed. Clean up the destination lines and
// return failure.
//
while( listDestLines ) {
pLine = kmxlRemoveFirstLine( listDestLines );
while( pLine && pLine->Controls ) {
pControl = kmxlRemoveFirstControl( pLine->Controls );
kmxlFreeControl( pControl );
}
AudioFreeMemory_Unknown( &pLine );
}
return( NULL );
}
///////////////////////////////////////////////////////////////////////
//
// BuildDestinationControls
//
// Starts at the destination node and translates all the parent nodes
// to mixer line controls. This process stops when the first SUM node
// is encountered, indicating the end of a destination line.
//
//
NTSTATUS
kmxlBuildDestinationControls(
IN PMIXEROBJECT pmxobj,
IN PMXLNODE pDest, // The destination to built controls for
IN PMXLLINE pLine // The line to add the controls to
)
{
PPEERNODE pTemp = NULL;
PMXLCONTROL pControl;
PAGED_CODE();
ASSERT( pmxobj );
ASSERT( pLine );
//
// Start at the immediate parent of the node passed.
//
pTemp = kmxlFirstParentNode( pDest );
while( pTemp ) {
if( IsEqualGUID( &pTemp->pNode->NodeType, &KSNODETYPE_SUM ) ||
( pTemp->pNode->Type == SOURCE ) ||
( pTemp->pNode->Type == DESTINATION ) ) {
//
// We've found a SUM node. Discontinue the loop... we've
// found all the controls.
//
break;
}
if( IsEqualGUID( &pTemp->pNode->NodeType, &KSNODETYPE_MUX ) ) {
if (kmxlTranslateNodeToControl( pmxobj, pTemp->pNode, &pControl )) {
kmxlAppendListToList( (PSLIST*) &pLine->Controls, (PSLIST) pControl );
}
break;
}
if( ( kmxlParentListLength( pTemp->pNode ) > 1 ) ) {
//
// Found a node with multiple parents that is not a SUM node.
// Can't handle that here so add any controls for this node
// and discontinue the loop.
//
if( kmxlTranslateNodeToControl( pmxobj, pTemp->pNode, &pControl ) ) {
kmxlAppendListToList( (PSLIST*) &pLine->Controls, (PSLIST) pControl );
}
break;
}
//
// By going up through the parents and inserting nodes at
// the front of the list, the list will contain the controls
// in the right order.
//
if( kmxlTranslateNodeToControl( pmxobj, pTemp->pNode, &pControl ) ) {
kmxlAppendListToList( (PSLIST*) &pLine->Controls, (PSLIST) pControl );
}
pTemp = kmxlFirstParentNode( pTemp->pNode );
}
DPF(DL_TRACE|FA_MIXER,(
"Found %d controls on destination %d:",
kmxlListLength( pLine->Controls ),
pDest->Id
) );
RETURN( STATUS_SUCCESS );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlBuildSourceLines
//
// Loops through each of the source nodes, allocating a new line
// structure, and calling kmxlBuildPath() to build the controls
// for the line (and possibly creating new lines if there are splits
// in the topology).
//
//
LINELIST
kmxlBuildSourceLines(
IN PMIXEROBJECT pmxobj,
IN NODELIST listSources, // The list of source nodes
IN NODELIST listDests // The list of dest. nodes
)
{
NTSTATUS Status;
LINELIST listSourceLines = NULL;
PMXLNODE pSource = NULL;
PMXLLINE pTemp = NULL;
PMXLCONTROL pControl;
ULONG MaxChannelsForLine;
ASSERT( pmxobj );
ASSERT( pmxobj->pfo );
ASSERT( pmxobj->pNodeTable );
ASSERT( listSources );
ASSERT( listDests );
PAGED_CODE();
pSource = kmxlFirstInList( listSources );
while( pSource ) {
//
// Allocate a new line structure and insert it into the list of
// source lines.
//
pTemp = kmxlAllocateLine( TAG_AudL_LINE );
if( !pTemp ) {
goto exit;
}
kmxlAddToEndOfList( listSourceLines, pTemp );
//
// Fill in the fields of the line structure. Some fields will need
// to be filled in later.
//
pTemp->SourceId = pSource->Id;
pTemp->Type = pSource->NodeType;
pTemp->Communication = pSource->Communication;
pTemp->Line.cbStruct = sizeof( MIXERLINE );
pTemp->Line.dwSource = (DWORD) -1;
pTemp->Line.dwDestination = (DWORD) -1;
pTemp->Line.fdwLine = MIXERLINE_LINEF_SOURCE |
MIXERLINE_LINEF_ACTIVE;
kmxlGetPinName( pmxobj->pfo, pSource->Id, pTemp );
// DPF(DL_TRACE|FA_MIXER,( "Building path for %s (%d).",
// PinCategoryToString( &pSource->NodeType ),
// pSource->Id
// ) );
//
// Build the controls for this line and identify the destination(s)
// it conntects to.
//
Status = kmxlBuildPath(
pmxobj,
pSource, // The source line to build controls for
pSource, // The node to start with
pTemp, // The line structure to add to
&listSourceLines, // The list of all source lines
listDests // THe list of all destinations
);
if( !NT_SUCCESS( Status ) ) {
DPF(DL_WARNING|FA_MIXER,("kmxlBuildPath failed Status=%X",Status) );
goto exit;
}
pSource = kmxlNextNode( pSource );
} // while( pSource )
pTemp = kmxlFirstInList( listSourceLines );
while( pTemp ) {
MaxChannelsForLine = 1;
pControl = kmxlFirstInList( pTemp->Controls );
while( pControl ) {
ASSERT( IsValidControl( pControl ) );
if ( pControl->NumChannels > MaxChannelsForLine) {
MaxChannelsForLine = pControl->NumChannels;
}
pControl = kmxlNextControl( pControl );
}
if( pTemp->Controls == NULL ) {
pTemp->Line.cChannels = 1; // should this default to 1 or 2?
} else {
pTemp->Line.cChannels = MaxChannelsForLine;
}
pTemp = kmxlNextLine( pTemp );
}
return( listSourceLines );
exit:
//
// Something went wrong. Clean up all memory allocated and return NULL
// to indicate the error.
//
while( listSourceLines ) {
pTemp = kmxlRemoveFirstLine( listSourceLines );
while( pTemp && pTemp->Controls ) {
pControl = kmxlRemoveFirstControl( pTemp->Controls );
kmxlFreeControl( pControl );
}
AudioFreeMemory_Unknown( &pTemp );
}
return( NULL );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlBuildPath
//
// Builds the controls for a source line. A source line ends when a
// SUM node, a destination node, a node contained in a destination line
// is encountered. When splits are encountered in the topology, new
// lines need to be created and the controls on those lines enumerated.
//
// kmxlBuildPath will recurse to find the controls on subnodes.
//
//
NTSTATUS
kmxlBuildPath(
IN PMIXEROBJECT pmxobj,
IN PMXLNODE pSource, // The source node for this path
IN PMXLNODE pNode, // The current node in the path
IN PMXLLINE pLine, // The current line
IN OUT LINELIST* plistLines, // The list of lines build so far
IN NODELIST listDests // The list of the destinations
)
{
NTSTATUS Status;
PMXLCONTROL pControl = NULL;
PMXLLINE pNewLine = NULL;
ULONG nControls;
PPEERNODE pChild = NULL;
ASSERT( pmxobj );
ASSERT( pSource );
ASSERT( pNode );
ASSERT( pLine );
ASSERT( plistLines );
PAGED_CODE();
DPF(DL_TRACE|FA_MIXER,( "Building path for %d(0x%x) (%s) NODE=%08x",
pNode->Id,pNode->Id,
NodeTypeToString( &pNode->NodeType ),
pNode ) );
//
// Check to see if this is the end of this line.
//
if( ( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_SUM ) ) ||
( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_MUX ) ) ||
( pNode->Type == DESTINATION ) ||
( kmxlIsDestinationNode( listDests, pNode ) ) )
{
//
// Find the destination node and update the line structure.
// If this IS the destination node, then set the ID in the line
// structure and return. There is no need to check the children,
// since there won't be any.
//
if( pNode->Type == DESTINATION ) {
pLine->DestId = pNode->Id;
RETURN( STATUS_SUCCESS );
}
//
// Find the destination node for the source. It is possible to
// have branches in the topology, so this may recurse.
//
pLine->DestId = kmxlFindDestinationForNode(
pmxobj,
pNode,
pNode,
pLine,
plistLines
);
RETURN( STATUS_SUCCESS );
}
//
// Retrieve and translate the node for the first child, appending any
// controls created onto the list of controls for this line.
//
pChild = kmxlFirstChildNode( pNode );
if( pChild == NULL ) {
RETURN( STATUS_SUCCESS );
}
//
// Save the number of controls here. If a split occurs beneath this
// node, we don't want to include children followed on the first
// child's path.
//
nControls = kmxlListLength( pLine->Controls );
if (kmxlTranslateNodeToControl(pmxobj, pChild->pNode, &pControl) ) {
if( pControl && IsEqualGUID( pControl->NodeType, &KSNODETYPE_MUX ) ) {
if( kmxlIsDestinationNode( listDests, pChild->pNode ) ) {
pControl->Parameters.bPlaceholder = TRUE;
}
}
kmxlAppendListToEndOfList( (PSLIST*) &pLine->Controls, (PSLIST) pControl );
}
//
// Recurse to build the controls for this child.
//
Status = kmxlBuildPath(
pmxobj,
pSource,
pChild->pNode,
pLine,
plistLines,
listDests
);
if( !NT_SUCCESS( Status ) ) {
RETURN( Status );
}
//
// For the rest of the children
//
// Create a new line based on pSource.
// Duplicate the list of controls in pLine.
// Recurse over the child node.
//
pChild = kmxlNextPeerNode( pChild );
while( pChild ) {
pNewLine = kmxlAllocateLine( TAG_AudL_LINE );
if( pNewLine == NULL ) {
RETURN( STATUS_INSUFFICIENT_RESOURCES );
}
//
// Insert this new node into the list of source lines
//
RtlCopyMemory( pNewLine, pLine, sizeof( MXLLINE ) );
pNewLine->List.Next = NULL;
pNewLine->Controls = NULL;
kmxlAddToEndOfList( *plistLines, pNewLine );
//
// Since this is a new line, the control structures also need to be
// duplicated.
//
Status = kmxlDuplicateLineControls( pNewLine, pLine, nControls );
if( !NT_SUCCESS( Status ) ) {
RETURN( Status );
}
//
// Just as for the first child, translate the node, append the
// controls to the list of controls for this list, and recurse
// to build the controls for its children.
//
if (kmxlTranslateNodeToControl(
pmxobj,
pChild->pNode,
&pControl ) ) {
kmxlAppendListToEndOfList( (PSLIST*) &pNewLine->Controls, (PSLIST) pControl );
}
Status = kmxlBuildPath(
pmxobj,
pSource,
pChild->pNode,
pNewLine,
plistLines,
listDests
);
if( !NT_SUCCESS( Status ) ) {
RETURN( Status );
}
pChild = kmxlNextPeerNode( pChild );
} // while( pChild )
RETURN( STATUS_SUCCESS );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlIsDestinationNode
//
// Searches all the list of controls on the given list of destinations
// to see if the node appears in any of those lists.
//
//
BOOL
kmxlIsDestinationNode(
IN NODELIST listDests, // The list of destinations
IN PMXLNODE pNode // The node to check
)
{
PMXLNODE pTemp;
PPEERNODE pParent;
PAGED_CODE();
if( pNode->Type == SOURCE ) {
return( FALSE );
}
if( pNode->Type == DESTINATION ) {
return( TRUE );
}
ASSERT(pNode->Type == NODE);
//
// Loop over each of the destinations
//
pTemp = kmxlFirstInList( listDests );
while( pTemp ) {
//
// Loop over the parent.
//
pParent = kmxlFirstParentNode( pTemp );
while( pParent ) {
if( ( pParent->pNode->Type == NODE ) &&
( pParent->pNode->Id == pNode->Id) ) {
return( TRUE );
}
if( ( IsEqualGUID( &pParent->pNode->NodeType, &KSNODETYPE_SUM ) ) ||
( IsEqualGUID( &pParent->pNode->NodeType, &KSNODETYPE_MUX ) ) ||
( pParent->pNode->Type == SOURCE ) )
{
break;
}
//
// Check for the node Ids matching.
//
pParent = kmxlFirstParentNode( pParent->pNode );
}
pTemp = kmxlNextNode( pTemp );
}
return( FALSE );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlDuplicateLine
//
// Duplicates a line and the associated controls.
//
//
NTSTATUS
kmxlDuplicateLine(
IN PMXLLINE* ppTargetLine, // Pointer to the new line
IN PMXLLINE pSourceLine // The line to duplicate
)
{
PAGED_CODE();
ASSERT( ppTargetLine );
ASSERT( pSourceLine );
DPF(DL_TRACE|FA_MIXER,( "Duplicated line with source=%d.",
pSourceLine->SourceId ) );
//
// Allocate a new line structure and copy the information from the
// source line.
//
*ppTargetLine = kmxlAllocateLine( TAG_AudL_LINE );
if( *ppTargetLine == NULL ) {
RETURN( STATUS_INSUFFICIENT_RESOURCES );
}
ASSERT( *ppTargetLine );
// DPF(DL_TRACE|FA_MIXER,( "Duplicated %s (%d).",
// PinCategoryToString( &pSourceLine->Type ),
// pSourceLine->SourceId
// ) );
RtlCopyMemory( *ppTargetLine, pSourceLine, sizeof( MXLLINE ) );
//
// Null out the controls and next pointer. This line does not have
// either of its own yet.
//
(*ppTargetLine)->List.Next = NULL;
(*ppTargetLine)->Controls = NULL;
//
// Duplicate all the controls for the source line.
//
return( kmxlDuplicateLineControls(
*ppTargetLine,
pSourceLine,
kmxlListLength( pSourceLine->Controls )
)
);
}
///////////////////////////////////////////////////////////////////////
//
// kmxlDuplicateLineControls
//
// Duplicates the controls for a line by allocating a new control
// structure for each and copying the information to the new node.
//
//
NTSTATUS
kmxlDuplicateLineControls(
IN PMXLLINE pTargetLine, // The line to put the controls into
IN PMXLLINE pSourceLine, // The line with the controls to dup
IN ULONG nCount // The number of controls to dup
)
{
PMXLCONTROL pControl,
pNewControl;
NTSTATUS Status;
PAGED_CODE();
ASSERT( pTargetLine->Controls == NULL );
if( nCount == 0 ) {
RETURN( STATUS_SUCCESS );
}
//
// Iterate over the list allocating and copying the controls
//
pControl = kmxlFirstInList( pSourceLine->Controls );
while( pControl ) {
ASSERT( IsValidControl( pControl ) );
//
// Allocate a new control structure.
//
pNewControl = kmxlAllocateControl( TAG_AudC_CONTROL );
if( pNewControl == NULL ) {
goto exit;
}
//
// Copy the entire MXLCONTROL structure and NULL out the
// List.Next field. This control will be part of a different
// list.
//
RtlCopyMemory( pNewControl, pControl, sizeof( MXLCONTROL ) );
pNewControl->List.Next = NULL;
pNewControl->pChannelStepping = NULL;
//
// Copy the channel steppings from the original control
//
ASSERT(pControl->NumChannels > 0);
Status = AudioAllocateMemory_Paged(pNewControl->NumChannels * sizeof( CHANNEL_STEPPING ),
TAG_AuDD_CHANNEL,
DEFAULT_MEMORY,
&pNewControl->pChannelStepping );
if( !NT_SUCCESS( Status ) ) {
pNewControl->NumChannels = 0;
goto exit;
}
RtlCopyMemory( pNewControl->pChannelStepping,
pControl->pChannelStepping,
pNewControl->NumChannels * sizeof( CHANNEL_STEPPING ) );
//
// We just made a copy of a MUX node. Mark the datastructures
// is has as a copy so it doesn't get freed from underneath
// somebody else.
//
if( IsEqualGUID( pNewControl->NodeType, &KSNODETYPE_MUX ) ) {
pNewControl->Parameters.bHasCopy = TRUE;
}
kmxlAddToList( pTargetLine->Controls, pNewControl );
//
// Decrement and check the number of controls copied. If we copied
// the requested number, stop.
//
--nCount;
if( nCount == 0 ) {
break;
}
pControl = kmxlNextControl( pControl );
}
RETURN( STATUS_SUCCESS );
exit:
//
// Failed to allocate the control structure. Free up all the
// controls already allocated and return an error.
//
while( pTargetLine->Controls ) {
pControl = kmxlRemoveFirstControl( pTargetLine->Controls );
kmxlFreeControl( pControl );
}
RETURN( STATUS_INSUFFICIENT_RESOURCES );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlFindDestinationForNode
//
// Finds a destination for the given node, duplicating lines if splits
// are found in the topology.
//
//
ULONG
kmxlFindDestinationForNode(
IN PMIXEROBJECT pmxobj,
IN PMXLNODE pNode, // The node to find dest for
IN PMXLNODE pParent, // The original parent
IN PMXLLINE pLine, // The current line it's on
IN OUT LINELIST* plistLines // The list of all lines
)
{
PPEERNODE pChild, pPeerChild;
PMXLLINE pNewLine;
PMXLNODE pShadow = pNode;
PAGED_CODE();
DPF(DL_TRACE|FA_MIXER,( "Finding destination for node %d(0x%x) (%s), parent %d(0x%x) (%s).",
pNode->Id,pNode->Id,
NodeTypeToString( &pNode->NodeType ),
pParent->Id,pParent->Id,
NodeTypeToString( &pNode->NodeType ) ) );
ASSERT( pmxobj ) ;
ASSERT( pNode );
ASSERT( pParent );
ASSERT( pLine );
ASSERT( plistLines );
if( pNode->Type == DESTINATION ) {
return( pNode->Id );
}
//
// Loop over the first children.
//
pChild = kmxlFirstChildNode( pNode );
while( pChild ) {
DPF(DL_TRACE|FA_MIXER,( "First child is %d(0x%x) (%s) NODE:%08x.",
pChild->pNode->Id,
pChild->pNode->Id,
NodeTypeToString( &pChild->pNode->NodeType ),
pChild->pNode ) );
if( pChild->pNode == pParent ) {
DPF(DL_TRACE|FA_MIXER,( "Child node is same as original parent!" ) );
return( INVALID_ID );
}
//
// Loop over the rest of the children.
//
pPeerChild = kmxlNextPeerNode( pChild );
while( pPeerChild ) {
if( pPeerChild->pNode == pParent ) {
DPF(DL_TRACE|FA_MIXER,( "Child node is same as original parent!" ) );
return( INVALID_ID );
}
DPF(DL_TRACE|FA_MIXER,( "Peer node of %d(0x%x) (%s) is %d(0x%x) (%s).",
pChild->pNode->Id,pChild->pNode->Id,
NodeTypeToString( &pChild->pNode->NodeType ),
pPeerChild->pNode->Id,pPeerChild->pNode->Id,
NodeTypeToString( &pPeerChild->pNode->NodeType ) ) );
//
// This line has more than 1 child. Duplicate this line
// and add it to the list of lines.
//
if( !NT_SUCCESS( kmxlDuplicateLine( &pNewLine, pLine ) ) ) {
DPF(DL_WARNING|FA_MIXER,("kmxlDuplicateLine failed") );
continue;
}
kmxlAddToEndOfList( *plistLines, pNewLine );
if( IsEqualGUID( &pPeerChild->pNode->NodeType, &KSNODETYPE_MUX ) ) {
//
// We've found a MUX after a SUM or another MUX node. Mark
// the current line as invalid and build a new, virtual
// line that feeds into the MUX.
//
pNewLine->DestId = INVALID_ID;
kmxlBuildVirtualMuxLine(
pmxobj,
pShadow,
pPeerChild->pNode,
plistLines
);
} else {
//
// Now to find the destination for this new line. Recurse
// on the node of this child.
//
pNewLine->DestId = kmxlFindDestinationForNode(
pmxobj,
pPeerChild->pNode,
pParent,
pNewLine,
plistLines
);
}
DPF(DL_TRACE|FA_MIXER,( "Found %x as dest for %d(0x%x) (%s).",
pNewLine->DestId, pPeerChild->pNode->Id,pPeerChild->pNode->Id,
NodeTypeToString( &pPeerChild->pNode->NodeType ),
pPeerChild->pNode ) );
pPeerChild = kmxlNextPeerNode( pPeerChild );
}
if( IsEqualGUID( &pChild->pNode->NodeType, &KSNODETYPE_MUX ) ) {
//
// We've found a MUX after a SUM or another MUX node. Mark
// the current line as invalid and build a new, virtual
// line that feeds into the MUX.
//
kmxlBuildVirtualMuxLine(
pmxobj,
pShadow,
pChild->pNode,
plistLines
);
return( INVALID_ID );
}
//
// Found the destination!
//
if( pChild->pNode->Type == DESTINATION ) {
DPF(DL_TRACE|FA_MIXER,( "Found %x as dest for %d.",
pChild->pNode->Id,
pNode->Id ) );
return( pChild->pNode->Id );
}
pShadow = pChild->pNode;
pChild = kmxlFirstChildNode( pChild->pNode );
}
DPF(DL_WARNING|FA_MIXER,("returning INVALID_ID") );
return( INVALID_ID );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlBuildVirtualMuxLine
//
//
NTSTATUS
kmxlBuildVirtualMuxLine(
IN PMIXEROBJECT pmxobj,
IN PMXLNODE pParent,
IN PMXLNODE pMux,
IN OUT LINELIST* plistLines
)
{
PMXLLINE pLine, pTemp;
PMXLNODE pNode;
PMXLCONTROL pControl;
MXLCONTROL Control;
PAGED_CODE();
//
// Allocate a new line to represent the virtual mux input line.
//
pLine = kmxlAllocateLine( TAG_AudL_LINE );
if( pLine == NULL ) {
RETURN( STATUS_INSUFFICIENT_RESOURCES );
}
DPF(DL_TRACE|FA_MIXER,("Virtual line %08x for Parent NODE:%08x",pLine,pParent) );
//
// Translate the mux control so that it will appear in this line.
//
if (kmxlTranslateNodeToControl(
pmxobj,
pMux,
&pControl
) ) {
pControl->Parameters.bPlaceholder = TRUE;
kmxlAppendListToList( (PSLIST*) &pLine->Controls, (PSLIST) pControl );
}
//
// Now start searching up from the parent.
//
pNode = pParent;
while( pNode ) {
//
// Translate the control.
//
if (kmxlTranslateNodeToControl(
pmxobj,
pNode,
&pControl
) ) {
kmxlAppendListToList( (PSLIST*) &pLine->Controls, (PSLIST) pControl );
}
//
// If we found a node with multiple parents, then this will be the
// "pin" for this line.
//
if( ( kmxlParentListLength( pNode ) > 1 ) ||
( pNode->Type == SOURCE ) ||
( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_MUX ) ) ||
( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_SUM ) ) ) {
//
// Check to see if this node has already been used in a virtual
// line.
//
pTemp = kmxlFirstInList( *plistLines );
while( pTemp ) {
if( pTemp->SourceId == ( 0x8000 + pNode->Id ) ) {
while( pLine->Controls ) {
pControl = kmxlRemoveFirstControl( pLine->Controls );
kmxlFreeControl( pControl );
}
AudioFreeMemory_Unknown( &pLine );
RETURN( STATUS_SUCCESS );
}
pTemp = kmxlNextLine( pTemp );
}
//
// Set up the pin. The name will be the name of the node.
//
pLine->SourceId = 0x8000 + pNode->Id;
Control.NodeType = &pNode->NodeType;
kmxlGetNodeName( pmxobj->pfo, pNode->Id, &Control );
RtlCopyMemory(
pLine->Line.szShortName,
Control.Control.szShortName,
min(
sizeof( pLine->Line.szShortName ),
sizeof( Control.Control.szShortName )
)
);
RtlCopyMemory(
pLine->Line.szName,
Control.Control.szName,
min(
sizeof( pLine->Line.szName ),
sizeof( Control.Control.szName )
)
);
break;
}
pNode = (kmxlFirstParentNode( pNode ))->pNode;
}
//
// By making this line type of "SUM" (which technically it is), it
// will guarantee that this line gets a target type of UNDEFINED.
//
pLine->Type = KSNODETYPE_SUM;
pLine->Communication = KSPIN_COMMUNICATION_NONE;
pLine->Line.cbStruct = sizeof( MIXERLINE );
pLine->Line.dwSource = (DWORD) -1;
pLine->Line.dwDestination = (DWORD) -1;
pLine->Line.fdwLine = MIXERLINE_LINEF_SOURCE |
MIXERLINE_LINEF_ACTIVE;
kmxlAddToEndOfList( plistLines, pLine );
pLine->DestId = kmxlFindDestinationForNode(
pmxobj, pMux, pMux, pLine, plistLines
);
RETURN( STATUS_SUCCESS );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlTranslateNodeToControl
//
//
// Translates a NodeType GUID into a mixer line control. The memory
// for the control(s) is allocated and as much information about the
// control is filled in.
//
// NOTES
// This function returns the number of controls added to the ppControl
// array.
//
// Returns the number of controls actually created.
//
//
ULONG
kmxlTranslateNodeToControl(
IN PMIXEROBJECT pmxobj,
IN PMXLNODE pNode, // The node to translate into a control
OUT PMXLCONTROL* ppControl // The control to fill in
)
{
PMXLCONTROL pControl;
NTSTATUS Status = STATUS_SUCCESS;
ASSERT( pmxobj );
ASSERT( pNode );
ASSERT( ppControl );
PAGED_CODE();
//
// Bug fix. The caller might not clear this. This needs to be NULL do
// the caller doesn't think controls were created when the function
// fails.
//
*ppControl = NULL;
//
// If the node is NULL, there's nothing to do.
//
if( pNode == NULL ) {
*ppControl = NULL;
return( 0 );
}
DPF(DL_TRACE|FA_MIXER,( "Translating %d(0x%x) ( %s ) NODE:%08x",
pNode->Id,pNode->Id,
NodeTypeToString( &pNode->NodeType ),
pNode ) );
///////////////////////////////////////////////////////////////////
if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_AGC ) ) {
///////////////////////////////////////////////////////////////////
//
// AGC is represented by an ONOFF control.
//
// AGC is a UNIFORM (mono) control.
//
///////////////////////////////////////////////////////////////////
//
// Check to see if the node properly supports AGC.
//
Status = kmxlSupportsControl( pmxobj->pfo, pNode->Id, KSPROPERTY_AUDIO_AGC );
if (!NT_SUCCESS(Status)) {
DPF(DL_TRACE|FA_MIXER,( "AGC node fails property!" ) );
goto exit;
}
//
// Allocate the new control structure.
//
*ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
if( *ppControl == NULL ) {
goto exit;
}
//
// Fill in as much information as possible.
//
(*ppControl)->NodeType = &KSNODETYPE_AGC;
(*ppControl)->Id = pNode->Id;
(*ppControl)->PropertyId = KSPROPERTY_AUDIO_AGC;
(*ppControl)->bScaled = FALSE;
(*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
(*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_ONOFF;
(*ppControl)->Control.cMultipleItems = 0;
(*ppControl)->Control.Bounds.dwMinimum = 0;
(*ppControl)->Control.Bounds.dwMaximum = 1;
(*ppControl)->Control.Metrics.cSteps = 0;
Status = kmxlGetControlChannels( pmxobj->pfo, *ppControl );
if (!NT_SUCCESS(Status))
{
kmxlFreeControl( *ppControl );
*ppControl = NULL;
goto exit;
} else {
kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
ASSERT( IsValidControl( *ppControl ) );
}
///////////////////////////////////////////////////////////////////
} else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_LOUDNESS ) ) {
///////////////////////////////////////////////////////////////////
//
// LOUNDNESS is represented by an ONOFF-type control.
//
// LOUDNESS is a UNIFORM (mono) control.
//
///////////////////////////////////////////////////////////////////
//
// Check to see if the node properly supports LOUDNESS.
//
Status = kmxlSupportsControl( pmxobj->pfo, pNode->Id, KSPROPERTY_AUDIO_LOUDNESS );
if (!NT_SUCCESS(Status)) {
DPF(DL_TRACE|FA_MIXER,( "Loudness node fails property!" ) );
goto exit;
}
//
// Allocate the new control structure
//
*ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
if( *ppControl == NULL ) {
goto exit;
}
//
// Fill in as much information as possible.
//
(*ppControl)->NodeType = &KSNODETYPE_LOUDNESS;
(*ppControl)->Id = pNode->Id;
(*ppControl)->PropertyId = KSPROPERTY_AUDIO_LOUDNESS;
(*ppControl)->bScaled = FALSE;
(*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
(*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_LOUDNESS;
(*ppControl)->Control.cMultipleItems = 0;
(*ppControl)->Control.Bounds.dwMinimum = 0;
(*ppControl)->Control.Bounds.dwMaximum = 1;
(*ppControl)->Control.Metrics.cSteps = 0;
Status = kmxlGetControlChannels( pmxobj->pfo, *ppControl );
if (!NT_SUCCESS(Status))
{
kmxlFreeControl( *ppControl );
*ppControl = NULL;
goto exit;
} else {
kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
ASSERT( IsValidControl( *ppControl ) );
}
///////////////////////////////////////////////////////////////////
} else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_MUTE ) ) {
///////////////////////////////////////////////////////////////////
//
// MUTE is represented by an ONOFF-type control.
//
// MUTE is a UNIFORM (mono) control.
//
///////////////////////////////////////////////////////////////////
//
// Check to see if the node properly supports MUTE.
//
Status = kmxlSupportsControl(
pmxobj->pfo,
pNode->Id,
KSPROPERTY_AUDIO_MUTE );
if (!NT_SUCCESS(Status)) {
DPF(DL_TRACE|FA_MIXER,( "Mute node fails property!" ) );
goto exit;
}
//
// Allocate the new control structure
//
*ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
if( *ppControl == NULL ) {
goto exit;
}
//
// Fill in as much information as possible.
//
(*ppControl)->NodeType = &KSNODETYPE_MUTE;
(*ppControl)->Id = pNode->Id;
(*ppControl)->PropertyId = KSPROPERTY_AUDIO_MUTE;
(*ppControl)->bScaled = FALSE;
(*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
(*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
(*ppControl)->Control.cMultipleItems = 0;
(*ppControl)->Control.Bounds.dwMinimum = 0;
(*ppControl)->Control.Bounds.dwMaximum = 1;
(*ppControl)->Control.Metrics.cSteps = 0;
Status = kmxlGetControlChannels( pmxobj->pfo, *ppControl );
if (!NT_SUCCESS(Status))
{
kmxlFreeControl( *ppControl );
*ppControl = NULL;
goto exit;
} else {
kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
ASSERT( IsValidControl( *ppControl ) );
}
///////////////////////////////////////////////////////////////////
} else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_TONE ) ) {
///////////////////////////////////////////////////////////////////
//
// A TONE node can represent up to 3 controls:
// Treble: A fader control
// Bass: A fader control
// Bass Boost: A OnOff control
//
// Both Treble and Bass are UNIFORM (mono) controls.
//
// To determine which control(s) the TONE node represents, a helper
// function is called to query the particular property. If the
// helper function succeeds, a control is created for that property.
//
///////////////////////////////////////////////////////////////////
Status = kmxlSupportsControl( pmxobj->pfo,
pNode->Id,
KSPROPERTY_AUDIO_BASS_BOOST );
if (NT_SUCCESS(Status)) {
//
// Bass boost control is supported. Allocate a new structure.
//
pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
if( pControl == NULL ) {
goto exit;
}
//
// Fill in as much information as possible.
//
pControl->NodeType = &KSNODETYPE_TONE;
pControl->Id = pNode->Id;
pControl->PropertyId = KSPROPERTY_AUDIO_BASS_BOOST;
pControl->bScaled = FALSE;
pControl->Control.cbStruct = sizeof( MIXERCONTROL );
pControl->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_ONOFF;
pControl->Control.cMultipleItems = 0;
pControl->Control.Bounds.dwMinimum = 0;
pControl->Control.Bounds.dwMaximum = 1;
pControl->Control.Metrics.cSteps = 0;
Status = kmxlGetControlChannels( pmxobj->pfo, pControl );
if (!NT_SUCCESS(Status))
{
kmxlFreeControl( pControl );
pControl = NULL;
goto exit;
}
kmxlGetNodeName( pmxobj->pfo, pNode->Id, pControl);
ASSERT( IsValidControl( pControl ) );
//
// Add this new control to the list.
//
kmxlAddToList( *ppControl, pControl );
pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
if( pControl ) {
RtlCopyMemory( pControl, *ppControl, sizeof( MXLCONTROL ) );
//
// Copy the channel steppings from the original control
//
//
// Sense we copied the control above, we might have gotten
// a pChannelStepping pointer in the copy. We'll NULL that out
// for the memory allocation.
//
pControl->pChannelStepping = NULL;
ASSERT(pControl->NumChannels > 0);
Status = AudioAllocateMemory_Paged(pControl->NumChannels * sizeof( CHANNEL_STEPPING ),
TAG_AuDC_CHANNEL,
DEFAULT_MEMORY,
&pControl->pChannelStepping );
if( !NT_SUCCESS( Status ) ) {
pControl->NumChannels = 0;
kmxlFreeControl( pControl );
pControl = NULL;
goto exit;
}
RtlCopyMemory( pControl->pChannelStepping,
(*ppControl)->pChannelStepping,
pControl->NumChannels * sizeof( CHANNEL_STEPPING ) );
pControl->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_BASS_BOOST;
kmxlAddToList( *ppControl, pControl );
ASSERT( IsValidControl( pControl ) );
}
}
Status = kmxlSupportsBassControl( pmxobj->pfo, pNode->Id );
if (NT_SUCCESS(Status)) {
//
// Bass control is supported. Allocate a new structure.
//
pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
if( pControl == NULL ) {
goto exit;
}
//
// Fill in as much information as possible.
//
pControl->NodeType = &KSNODETYPE_TONE;
pControl->Id = pNode->Id;
pControl->PropertyId = KSPROPERTY_AUDIO_BASS;
pControl->bScaled = TRUE;
pControl->Control.cbStruct = sizeof( MIXERCONTROL );
pControl->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_BASS;
pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
pControl->Control.cMultipleItems = 0;
pControl->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
pControl->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
pControl->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
Status = kmxlGetControlRange( pmxobj->pfo, pControl );
if (!NT_SUCCESS(Status))
{
kmxlFreeControl( pControl );
pControl = NULL;
goto exit;
} else {
kmxlGetNodeName( pmxobj->pfo, pNode->Id, pControl);
//
// Add this new control to the list.
//
ASSERT( IsValidControl( pControl ) );
kmxlAddToList( *ppControl, pControl );
}
}
Status = kmxlSupportsTrebleControl( pmxobj->pfo, pNode->Id );
if (NT_SUCCESS(Status)) {
//
// Treble is supported. Allocate a new control structure.
//
pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
if( pControl == NULL ) {
goto exit;
}
//
// Fill in as much information as possible.
//
pControl->NodeType = &KSNODETYPE_TONE;
pControl->Id = pNode->Id;
pControl->PropertyId = KSPROPERTY_AUDIO_TREBLE;
pControl->bScaled = TRUE;
pControl->Control.cbStruct = sizeof( MIXERCONTROL );
pControl->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_TREBLE;
pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
pControl->Control.cMultipleItems = 0;
pControl->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
pControl->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
pControl->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
Status = kmxlGetControlRange( pmxobj->pfo, pControl );
if (!NT_SUCCESS(Status))
{
kmxlFreeControl( pControl );
pControl = NULL;
goto exit;
} else {
kmxlGetNodeName( pmxobj->pfo, pNode->Id, pControl);
//
// Add this new control to the list.
//
ASSERT( IsValidControl( pControl ) );
kmxlAddToList( *ppControl, pControl );
}
}
///////////////////////////////////////////////////////////////////
} else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_VOLUME ) ) {
///////////////////////////////////////////////////////////////////
//
// A VOLUME is a fader-type control
//
// To determine if a node supports volume changes
//
///////////////////////////////////////////////////////////////////
//
// Check to see if the node properly supports volume
//
Status = kmxlSupportsControl(
pmxobj->pfo,
pNode->Id,
KSPROPERTY_AUDIO_VOLUMELEVEL
);
if (!NT_SUCCESS(Status)) {
DPF(DL_TRACE|FA_MIXER,( "Volume node fails property!" ) );
goto exit;
}
//
// Allocate the new control structure
//
*ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
if( *ppControl == NULL ) {
goto exit;
}
//
// Fill in as much information as possible.
//
(*ppControl)->NodeType = &KSNODETYPE_VOLUME;
(*ppControl)->Id = pNode->Id;
(*ppControl)->PropertyId = KSPROPERTY_AUDIO_VOLUMELEVEL;
(*ppControl)->bScaled = TRUE;
(*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
(*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
(*ppControl)->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
(*ppControl)->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
(*ppControl)->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
(*ppControl)->Control.cMultipleItems = 0;
Status = kmxlGetControlRange( pmxobj->pfo, (*ppControl) );
if (!NT_SUCCESS(Status))
{
kmxlFreeControl( *ppControl );
*ppControl = NULL;
goto exit;
}
kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
ASSERT( IsValidControl( *ppControl ) );
///////////////////////////////////////////////////////////////////
} else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_PEAKMETER ) ) {
///////////////////////////////////////////////////////////////////
//
// To determine if a node supports peak meter properties
//
///////////////////////////////////////////////////////////////////
//
// Check to see if the node properly supports peakmeter
//
Status = kmxlSupportsControl(
pmxobj->pfo,
pNode->Id,
KSPROPERTY_AUDIO_PEAKMETER
);
if (!NT_SUCCESS(Status)) {
DPF(DL_TRACE|FA_MIXER,( "Peakmeter node fails property!" ) );
goto exit;
}
//
// Allocate the new control structure
//
*ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
if( *ppControl == NULL ) {
goto exit;
}
//
// Fill in as much information as possible.
//
(*ppControl)->NodeType = &KSNODETYPE_PEAKMETER;
(*ppControl)->Id = pNode->Id;
(*ppControl)->PropertyId = KSPROPERTY_AUDIO_PEAKMETER;
(*ppControl)->bScaled = FALSE;
(*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
(*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_PEAKMETER;
(*ppControl)->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
(*ppControl)->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
(*ppControl)->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
(*ppControl)->Control.cMultipleItems = 0;
Status = kmxlGetControlRange( pmxobj->pfo, (*ppControl) );
if (!NT_SUCCESS(Status))
{
kmxlFreeControl( *ppControl );
*ppControl = NULL;
goto exit;
}
kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
ASSERT( IsValidControl( *ppControl ) );
///////////////////////////////////////////////////////////////////
} else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_MUX ) ) {
///////////////////////////////////////////////////////////////////
//
// A MUX is a single select type control.
//
///////////////////////////////////////////////////////////////////
{
ULONG Line;
//
// Do a quick check and see if the mux responds properly.
// If not, just get out of here quick.
//
if( !NT_SUCCESS( kmxlGetNodeProperty(
pmxobj->pfo,
&KSPROPSETID_Audio,
KSPROPERTY_AUDIO_MUX_SOURCE,
pNode->Id,
0,
NULL,
&Line,
sizeof( Line ) ) ) )
{
goto exit;
}
//
// Look to see if a control has already been generated for this
// node. If so, the control information can be used from it
// instead of creating a new one.
//
pControl = kmxlFirstInList( pmxobj->listMuxControls );
while( pControl ) {
ASSERT( IsValidControl( pControl ) );
if( pControl->Id == pNode->Id ) {
break;
}
pControl = kmxlNextControl( pControl );
}
//
// Allocate the new control structure
//
*ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
if( *ppControl == NULL ) {
goto exit;
}
if( pControl == NULL ) {
//
// This node has not been seen before. Fill in as much info as
// possible.
//
(*ppControl)->NodeType = &KSNODETYPE_MUX;
(*ppControl)->Id = pNode->Id;
(*ppControl)->PropertyId = KSPROPERTY_AUDIO_MUX_SOURCE;
(*ppControl)->bScaled = FALSE;
(*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
(*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX;
(*ppControl)->Control.cMultipleItems = kmxlGetNumMuxLines(
pmxobj->pTopology,
pNode->Id
);
(*ppControl)->Control.fdwControl = MIXERCONTROL_CONTROLF_MULTIPLE |
MIXERCONTROL_CONTROLF_UNIFORM;
(*ppControl)->Control.Bounds.dwMinimum = 0;
(*ppControl)->Control.Bounds.dwMaximum = (*ppControl)->Control.cMultipleItems - 1;
(*ppControl)->Control.Metrics.cSteps = (*ppControl)->Control.cMultipleItems;
kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
kmxlGetMuxLineNames( pmxobj, *ppControl );
pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
if( pControl == NULL ) {
kmxlFreeControl( *ppControl );
*ppControl = NULL;
goto exit;
}
//
// Make a copy of this control for the mux list
//
(*ppControl)->Control.dwControlID = pmxobj->dwControlId++;
RtlCopyMemory( pControl, *ppControl, sizeof( MXLCONTROL ) );
ASSERT( IsValidControl( pControl ) );
pControl->Parameters.bHasCopy = TRUE;
(*ppControl)->Parameters.bHasCopy = FALSE;
kmxlAddToList( pmxobj->listMuxControls, pControl );
} else {
RtlCopyMemory( *ppControl, pControl, sizeof( MXLCONTROL ) );
ASSERT( IsValidControl( *ppControl ) );
(*ppControl)->Parameters.bHasCopy = TRUE;
(*ppControl)->List.Next = NULL;
}
}
#ifdef STEREO_ENHANCE
///////////////////////////////////////////////////////////////////
} else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_STEREO_WIDE ) ) {
///////////////////////////////////////////////////////////////////
//
// Stereo Enhance is a boolean control.
//
///////////////////////////////////////////////////////////////////
//
// Check to see if the node properly supports stereo wide
//
Status = kmxlSupportsControl(
pfoInstance,
pNode->Id,
KSPROPERTY_AUDIO_WIDE_MODE
);
if (!NT_SUCCESS(Status)) {
DPF(DL_TRACE|FA_MIXER,( "Stereo Wide node fails property!" ) );
goto exit;
}
//
// Allocate the new control structure
//
*ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
if( *ppControl == NULL ) {
goto exit;
}
//
// Fill in as much information as possible.
//
(*ppControl)->NodeType = &KSNODETYPE_STEREO_ENHANCE;
(*ppControl)->Id = pNode->Id;
(*ppControl)->PropertyId = KSPROPERTY_AUDIO_WIDE_MODE;
(*ppControl)->bScaled = FALSE;
(*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
(*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_STEREOENH;
(*ppControl)->Control.cMultipleItems = 0;
(*ppControl)->Control.Bounds.dwMinimum = 0;
(*ppControl)->Control.Bounds.dwMaximum = 1;
(*ppControl)->Control.Metrics.cSteps = 0;
Status = kmxlGetControlChannels( pfoInstance, *ppControl );
if (!NT_SUCCESS(Status))
{
kmxlFreeControl( *ppControl );
*ppControl = NULL;
goto exit;
}
kmxlGetNodeName( pfoInstance, pNode->Id, (*ppControl));
#endif
///////////////////////////////////////////////////////////////////
} else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_STEREO_WIDE ) ) {
///////////////////////////////////////////////////////////////////
//
// Check to see if the node properly supports stereo wide
//
Status = kmxlSupportsControl(
pmxobj->pfo,
pNode->Id,
KSPROPERTY_AUDIO_WIDENESS
);
if (!NT_SUCCESS(Status)) {
DPF(DL_TRACE|FA_MIXER,( "Stereo wide node fails property!" ) );
goto exit;
}
//
// Allocate the new control structure
//
*ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
if( *ppControl == NULL ) {
goto exit;
}
//
// Fill in as much information as possible.
//
(*ppControl)->NodeType = &KSNODETYPE_STEREO_WIDE;
(*ppControl)->Id = pNode->Id;
(*ppControl)->PropertyId = KSPROPERTY_AUDIO_WIDENESS;
(*ppControl)->bScaled = FALSE;
(*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
(*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_FADER;
(*ppControl)->Control.cMultipleItems = 0;
(*ppControl)->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
(*ppControl)->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
(*ppControl)->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
Status = kmxlGetControlRange( pmxobj->pfo, (*ppControl) );
if (!NT_SUCCESS(Status))
{
kmxlFreeControl( *ppControl );
*ppControl = NULL;
goto exit;
}
kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
ASSERT( IsValidControl( *ppControl ) );
///////////////////////////////////////////////////////////////////
} else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_CHORUS ) ) {
///////////////////////////////////////////////////////////////////
//
// Check to see if the node properly supports chorus
//
Status = kmxlSupportsControl(
pmxobj->pfo,
pNode->Id,
KSPROPERTY_AUDIO_CHORUS_LEVEL
);
if (!NT_SUCCESS(Status)) {
DPF(DL_TRACE|FA_MIXER,( "Chorus node fails property!" ) );
goto exit;
}
//
// Allocate the new control structure
//
*ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
if( *ppControl == NULL ) {
goto exit;
}
//
// Fill in as much information as possible.
//
(*ppControl)->NodeType = &KSNODETYPE_CHORUS;
(*ppControl)->Id = pNode->Id;
(*ppControl)->PropertyId = KSPROPERTY_AUDIO_CHORUS_LEVEL;
(*ppControl)->bScaled = FALSE;
(*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
(*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_FADER;
(*ppControl)->Control.cMultipleItems = 0;
(*ppControl)->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
(*ppControl)->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
(*ppControl)->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
// (*ppControl)->Control.Metrics.cSteps = 0xFFFF;
Status = kmxlGetControlChannels( pmxobj->pfo, *ppControl ); // Should we also get the range?
if (!NT_SUCCESS(Status))
{
kmxlFreeControl( *ppControl );
*ppControl = NULL;
goto exit;
} else {
kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
ASSERT( IsValidControl( *ppControl ) );
}
///////////////////////////////////////////////////////////////////
} else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_REVERB ) ) {
///////////////////////////////////////////////////////////////////
//
// Check to see if the node properly supports reverb
//
Status = kmxlSupportsControl(
pmxobj->pfo,
pNode->Id,
KSPROPERTY_AUDIO_REVERB_LEVEL
);
if (!NT_SUCCESS(Status)) {
DPF(DL_TRACE|FA_MIXER,( "Reverb node fails property!" ) );
goto exit;
}
//
// Allocate the new control structure
//
*ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
if( *ppControl == NULL ) {
goto exit;
}
//
// Fill in as much information as possible.
//
(*ppControl)->NodeType = &KSNODETYPE_REVERB;
(*ppControl)->Id = pNode->Id;
(*ppControl)->PropertyId = KSPROPERTY_AUDIO_REVERB_LEVEL;
(*ppControl)->bScaled = FALSE;
(*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
(*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_FADER;
(*ppControl)->Control.cMultipleItems = 0;
(*ppControl)->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
(*ppControl)->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
(*ppControl)->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
// (*ppControl)->Control.Metrics.cSteps = 0xFFFF;
Status = kmxlGetControlChannels( pmxobj->pfo, *ppControl ); // Should we also get the range?
if (!NT_SUCCESS(Status))
{
kmxlFreeControl( *ppControl );
*ppControl = NULL;
goto exit;
} else {
kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
ASSERT( IsValidControl( *ppControl ) );
}
///////////////////////////////////////////////////////////////////
} else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_SUPERMIX ) ) {
///////////////////////////////////////////////////////////////////
//
// SuperMix nodes can be supported as MUTE controls if the MUTE
// property is supported.
//
///////////////////////////////////////////////////////////////////
PKSAUDIO_MIXCAP_TABLE pMixCaps;
PLONG pReferenceCount = NULL;
ULONG i,
Size;
BOOL bMutable;
BOOL bVolume = FALSE;
PKSAUDIO_MIXLEVEL pMixLevels = NULL;
#ifdef SUPERMIX_AS_VOL
ULONG Channels;
#endif
if( !NT_SUCCESS( kmxlGetSuperMixCaps( pmxobj->pfo, pNode->Id, &pMixCaps ) ) ) {
goto exit;
}
Status = AudioAllocateMemory_Paged(sizeof( LONG ),
TAG_AudS_SUPERMIX,
ZERO_FILL_MEMORY,
&pReferenceCount );
if( !NT_SUCCESS( Status ) ) {
AudioFreeMemory_Unknown( &pMixCaps );
*ppControl = NULL;
goto exit;
}
*pReferenceCount=0;
Size = pMixCaps->InputChannels * pMixCaps->OutputChannels;
Status = AudioAllocateMemory_Paged(Size * sizeof( KSAUDIO_MIXLEVEL ),
TAG_Audl_MIXLEVEL,
ZERO_FILL_MEMORY,
&pMixLevels );
if( !NT_SUCCESS( Status ) ) {
AudioFreeMemory_Unknown( &pMixCaps );
AudioFreeMemory( sizeof(LONG),&pReferenceCount );
*ppControl = NULL;
goto exit;
}
Status = kmxlGetNodeProperty(
pmxobj->pfo,
&KSPROPSETID_Audio,
KSPROPERTY_AUDIO_MIX_LEVEL_TABLE,
pNode->Id,
0,
NULL,
pMixLevels,
Size * sizeof( KSAUDIO_MIXLEVEL )
);
if( !NT_SUCCESS( Status ) ) {
AudioFreeMemory_Unknown( &pMixCaps );
AudioFreeMemory( sizeof(LONG),&pReferenceCount );
AudioFreeMemory_Unknown( &pMixLevels );
DPF(DL_WARNING|FA_MIXER,("kmxlGetNodeProperty failed Status=%X",Status) );
*ppControl = NULL;
goto exit;
}
bMutable = TRUE;
for( i = 0; i < Size; i++ ) {
//
// If the channel is mutable, then all is well for this entry.
//
if( pMixCaps->Capabilities[ i ].Mute ) {
continue;
}
//
// The the entry is not mutable but is fully attenuated,
// this will work too.
//
if( ( pMixCaps->Capabilities[ i ].Minimum == LONG_MIN ) &&
( pMixCaps->Capabilities[ i ].Maximum == LONG_MIN ) &&
( pMixCaps->Capabilities[ i ].Reset == LONG_MIN ) )
{
continue;
}
bMutable = FALSE;
break;
}
#ifdef SUPERMIX_AS_VOL
bVolume = TRUE;
Channels = 0;
for( i = 0; i < Size; i += pMixCaps->OutputChannels + 1 ) {
if( ( pMixCaps->Capabilities[ i ].Maximum -
pMixCaps->Capabilities[ i ].Minimum ) > 0 )
{
++Channels;
continue;
}
bVolume = FALSE;
break;
}
#endif
//
// This node cannot be used as a MUTE control.
//
if( !bMutable && !bVolume ) {
AudioFreeMemory_Unknown( &pMixCaps );
AudioFreeMemory( sizeof(LONG),&pReferenceCount );
AudioFreeMemory_Unknown( &pMixLevels );
*ppControl = NULL;
goto exit;
}
if( bMutable ) {
//
// The Supermix is verifiably usable as a MUTE. Fill in all the
// details.
//
pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
if( pControl != NULL ) {
pControl->NodeType = &KSNODETYPE_SUPERMIX;
pControl->Id = pNode->Id;
pControl->PropertyId = KSPROPERTY_AUDIO_MIX_LEVEL_TABLE;
pControl->bScaled = FALSE;
pControl->Control.cbStruct = sizeof( MIXERCONTROL );
pControl->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
pControl->Control.cMultipleItems = 0;
pControl->Control.Bounds.dwMinimum = 0;
pControl->Control.Bounds.dwMaximum = 1;
pControl->Control.Metrics.cSteps = 0;
InterlockedIncrement(pReferenceCount);
pControl->Parameters.pReferenceCount = pReferenceCount;
pControl->Parameters.Size = pMixCaps->InputChannels *
pMixCaps->OutputChannels;
pControl->Parameters.pMixCaps = pMixCaps;
pControl->Parameters.pMixLevels = pMixLevels;
Status = AudioAllocateMemory_Paged(sizeof( CHANNEL_STEPPING ),
TAG_AuDE_CHANNEL,
ZERO_FILL_MEMORY,
&pControl->pChannelStepping );
if( !NT_SUCCESS( Status ) ) {
AudioFreeMemory_Unknown( &pMixCaps );
AudioFreeMemory( sizeof(LONG),&pReferenceCount );
AudioFreeMemory_Unknown( &pMixLevels );
*ppControl = NULL;
goto exit;
}
pControl->NumChannels = 1;
pControl->pChannelStepping->MinValue = pMixCaps->Capabilities[ 0 ].Minimum;
pControl->pChannelStepping->MaxValue = pMixCaps->Capabilities[ 0 ].Maximum;
pControl->pChannelStepping->Steps = 32;
kmxlGetNodeName( pmxobj->pfo, pNode->Id, pControl);
kmxlAddToList( *ppControl, pControl );
ASSERT( IsValidControl( pControl ) );
}
}
#ifdef SUPERMIX_AS_VOL
if( bVolume ) {
pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
if( pControl != NULL ) {
pControl->NodeType = &KSNODETYPE_SUPERMIX;
pControl->Id = pNode->Id;
pControl->PropertyId = KSPROPERTY_AUDIO_MIX_LEVEL_TABLE;
pControl->bScaled = TRUE;
pControl->Control.cbStruct = sizeof( MIXERCONTROL );
pControl->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
pControl->Control.cMultipleItems = 0;
pControl->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
pControl->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
pControl->Control.Metrics.cSteps = 32;
InterlockedIncrement(pReferenceCount);
pControl->Parameters.pReferenceCount = pReferenceCount;
pControl->Parameters.Size = pMixCaps->InputChannels *
pMixCaps->OutputChannels;
pControl->Parameters.pMixCaps = pMixCaps;
pControl->Parameters.pMixLevels = pMixLevels;
if( Channels == 1 ) {
pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
} else {
pControl->Control.fdwControl = 0;
}
kmxlGetNodeName( pmxobj->pfo, pNode->Id, pControl );
kmxlAddToList( *ppControl, pControl );
ASSERT( IsValidControl( pControl ) );
}
}
#endif // SUPERMIX_AS_VOL
if( *ppControl == NULL ) {
AudioFreeMemory_Unknown( &pMixCaps );
AudioFreeMemory( sizeof(LONG),&pReferenceCount );
AudioFreeMemory_Unknown( &pMixLevels );
}
}
exit:
if( *ppControl ) {
DPF(DL_TRACE|FA_MIXER,( "Translated %d controls.", kmxlListLength( *ppControl ) ) );
return( kmxlListLength( *ppControl ) );
} else {
DPF(DL_TRACE|FA_MIXER,( "Translated no controls." ) );
return( 0 );
}
}
#define KsAudioPropertyToString( Property ) \
Property == KSPROPERTY_AUDIO_VOLUMELEVEL ? "Volume" : \
Property == KSPROPERTY_AUDIO_MUTE ? "Mute" : \
Property == KSPROPERTY_AUDIO_BASS ? "Bass" : \
Property == KSPROPERTY_AUDIO_TREBLE ? "Treble" : \
Property == KSPROPERTY_AUDIO_AGC ? "AGC" : \
Property == KSPROPERTY_AUDIO_LOUDNESS ? "Loudness" : \
Property == KSPROPERTY_AUDIO_PEAKMETER ? "Peakmeter" : \
"Unknown"
///////////////////////////////////////////////////////////////////////
//
// kmxlSupportsControl
//
// Queries for property on control to see if it is actually supported
//
//
NTSTATUS
kmxlSupportsControl(
IN PFILE_OBJECT pfoInstance, // The instance to check for
IN ULONG Node, // The node id to query
IN ULONG Property // The property to check for
)
{
NTSTATUS Status;
LONG Level;
ASSERT( pfoInstance );
PAGED_CODE();
//
// Check to see if the property works on the first channel.
//
Status = kmxlGetAudioNodeProperty(
pfoInstance,
Property,
Node,
0, // Channel 0 - first channel
NULL, 0,
&Level, sizeof( Level )
);
if( !NT_SUCCESS( Status ) ) {
DPF(DL_WARNING|FA_MIXER,( "SupportsControl for (%d,%X) failed on first channel with %x.",
Node, Property, Status ) );
}
RETURN( Status );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlSupportsMultiChannelControl
//
// Queries for property on the second channel of the control to see
// independent levels can be set. It is assumed that the first channel
// already succeeded in kmxlSupportsControl
//
//
NTSTATUS
kmxlSupportsMultiChannelControl(
IN PFILE_OBJECT pfoInstance, // The instance to check for
IN ULONG Node, // The node id to query
IN ULONG Property // The property to check for
)
{
NTSTATUS Status;
LONG Level;
ASSERT( pfoInstance );
PAGED_CODE();
//
// Just check the property on the second channel because we have already checked
// the first channel already.
//
Status = kmxlGetAudioNodeProperty(
pfoInstance,
Property,
Node,
1, // Second channel equals a channel value of 1
NULL, 0,
&Level, sizeof( Level )
);
RETURN( Status );
}
NTSTATUS
kmxlAssignLineAndControlIdsWorker(
IN PMIXEROBJECT pmxobj,
IN LINELIST listLines, // The list to assign ids for
IN ULONG ListType, // LIST_SOURCE or LIST_DESTINATION
IN OUT ULONG *pLineID,
IN GUID *pDestGuid
)
{
NTSTATUS Status = STATUS_SUCCESS;
PMXLLINE pLine = NULL;
PMXLCONTROL pControl = NULL;
ULONG LineID = 0;
ULONG Dest;
PAGED_CODE();
ASSERT ( ListType==SOURCE_LIST || ListType==DESTINATION_LIST );
if (pLineID!=NULL) {
LineID=*pLineID;
}
//
// Loop through each of the line structures
//
pLine = kmxlFirstInList( listLines );
if( pLine == NULL ) {
RETURN( Status );
}
Dest = pLine->DestId;
while( pLine ) {
//
// For destinations, set the dwDestination field and set
// the dwSource field for sources.
//
if( ListType == DESTINATION_LIST ) {
// Check if this line has already been assigned an ID.
// If so, then go to next line in list.
if (pLine->Line.dwDestination!=(DWORD)(-1)) {
pLine = kmxlNextLine( pLine );
continue;
}
// Now if we can only number lines of a particular GUID,
// then make sure this destination line type matches that guid.
if (pDestGuid!=NULL && !IsEqualGUID( pDestGuid, &pLine->Type )) {
pLine = kmxlNextLine( pLine );
continue;
}
//
// Assign the destination Id. Create the line Id by
// using -1 for the source in the highword and the
// destination in the loword.
//
pLine->Line.dwDestination = LineID++;
pLine->Line.dwLineID = MAKELONG(
pLine->Line.dwDestination,
-1
);
if (pLineID!=NULL) {
*pLineID=LineID;
}
} else if( ListType == SOURCE_LIST ) {
pLine->Line.dwSource = LineID++;
} else {
RETURN( STATUS_INVALID_PARAMETER );
}
//
// Set up the number of controls on this line.
//
pLine->Line.cControls = kmxlListLength( pLine->Controls );
//
// Loop through the controls, assigning them a control ID
// that is a pointer to the MXLCONTROL structure for that
// control.
//
pControl = kmxlFirstInList( pLine->Controls );
while( pControl ) {
if( pControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ) {
//
// MUX controls are already numbered by this point. Just skip
// it and go onto the next one.
//
pControl = kmxlNextControl( pControl );
continue;
}
pControl->Control.dwControlID = pmxobj->dwControlId++;
pControl = kmxlNextControl( pControl );
}
pLine = kmxlNextLine( pLine );
if( pLine == NULL ) {
continue;
}
if( ( ListType == SOURCE_LIST ) && ( pLine->DestId != Dest ) ) {
LineID = 0;
Dest = pLine->DestId;
}
}
RETURN( Status );
}
#define GUIDCOUNT 13
///////////////////////////////////////////////////////////////////////
//
// kmxlAssignLineAndControlIds
//
// Loops through the list of lines and assigns ids for those line.
// For destinations, the Id starts a 0 and is incremented each time.
// The line id is a long of -1 and the dest id. For sources, the
// line Ids will need to be specified elsewhere so only dwSource
// field is assigned.
//
// For controls, each control is given an Id of the address to the
// MXLCONTROL structure.
//
//
NTSTATUS
kmxlAssignLineAndControlIds(
IN PMIXEROBJECT pmxobj,
IN LINELIST listLines, // The list to assign ids for
IN ULONG ListType // LIST_SOURCE or LIST_DESTINATION
)
{
PAGED_CODE();
ASSERT ( ListType==SOURCE_LIST || ListType==DESTINATION_LIST );
if (SOURCE_LIST==ListType) {
return( kmxlAssignLineAndControlIdsWorker(pmxobj, listLines, ListType, NULL, NULL) );
}
else if (DESTINATION_LIST==ListType) {
// In order to help sndvol32 do the right thing as far as which
// lines displayed as the default playback and record lines, we
// number lines based on what their destinations are.
// We use guid the pLine->Type field to decide how to number lines.
// Lines are prioritized in the following way: speakers, then
// headphones, then telephones. Non prioritized guids are assigned
// last in whatever order they appear in the list.
ULONG LineID=0;
ULONG i;
GUID prioritizeddestinationguids[GUIDCOUNT]= {
STATIC_KSNODETYPE_ROOM_SPEAKER,
STATIC_KSNODETYPE_DESKTOP_SPEAKER,
STATIC_KSNODETYPE_SPEAKER,
STATIC_KSNODETYPE_COMMUNICATION_SPEAKER,
STATIC_KSNODETYPE_HEAD_MOUNTED_DISPLAY_AUDIO,
STATIC_KSNODETYPE_ANALOG_CONNECTOR,
STATIC_KSNODETYPE_SPDIF_INTERFACE,
STATIC_KSNODETYPE_HEADPHONES,
STATIC_KSNODETYPE_TELEPHONE,
STATIC_KSNODETYPE_PHONE_LINE,
STATIC_KSNODETYPE_DOWN_LINE_PHONE,
STATIC_PINNAME_CAPTURE,
STATIC_KSCATEGORY_AUDIO,
};
// Cycle through the list for each prioritized guid and number
// those lines that match that particular guid.
for (i=0; i<GUIDCOUNT; i++) {
kmxlAssignLineAndControlIdsWorker(pmxobj, listLines, ListType,
&LineID, &prioritizeddestinationguids[i]);
}
// Now, number anything left over with a number that depends solely on
// its random order in the list.
return( kmxlAssignLineAndControlIdsWorker(pmxobj, listLines, ListType, &LineID, NULL) );
}
else {
RETURN( STATUS_INVALID_PARAMETER );
}
}
///////////////////////////////////////////////////////////////////////
//
// kmxlAssignDestinationsToSources
//
// Loops through each source looking for a destination lines that
// have a matching destination id. Source line Ids are assigned
// by putting the source id in the hiword and the dest id in the
// loword.
//
//
NTSTATUS
kmxlAssignDestinationsToSources(
IN LINELIST listSourceLines, // The list of all source lines
IN LINELIST listDestLines // The list of all dest lines
)
{
PMXLLINE pSource = NULL,
pDest = NULL;
PAGED_CODE();
//
// For each source line, loop throught the destinations until a
// line is found matching the Id. The dwDestination field will
// be the zero-index Id of the destination.
//
pSource = kmxlFirstInList( listSourceLines );
while( pSource ) {
pDest = kmxlFirstInList( listDestLines );
while( pDest ) {
if( pSource->DestId == pDest->DestId ) {
//
// Heh, whatchya know?
//
pSource->Line.dwDestination = pDest->Line.dwDestination;
pSource->Line.dwLineID = MAKELONG(
(WORD) pSource->Line.dwDestination,
(WORD) pSource->Line.dwSource
);
break;
}
pDest = kmxlNextLine( pDest );
}
pSource = kmxlNextLine( pSource );
}
RETURN( STATUS_SUCCESS );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlUpdateDestinationConnectionCount
//
// For each of the destinations, loop through each of the sources
// and find those that connect to this destination. That count is
// then stored in the MIXERLINE.cConnections for the line.
//
//
NTSTATUS
kmxlUpdateDestintationConnectionCount(
IN LINELIST listSourceLines, // The list of source lines
IN LINELIST listDestLines // The list of destination lines
)
{
PMXLLINE pDest,
pSource;
ULONG Count;
PAGED_CODE();
//
// Loop through each destination finding all the sources that connect
// to it. The total number of sources connecting to a destination
// is sourced in the cConnections field of the MIXERLINE struct.
//
pDest = kmxlFirstInList( listDestLines );
while( pDest ) {
//
// Initialize the source ID. This will mark this as a valid
// destination.
//
pDest->SourceId = (ULONG) -1;
Count = 0;
//
// Loop through the sources looking for sources that connect to
// the current destination.
//
pSource = kmxlFirstInList( listSourceLines );
while( pSource ) {
//
// Found a match. Increment the count.
//
if( pSource->DestId == pDest->DestId ) {
++Count;
}
pSource = kmxlNextLine( pSource );
}
pDest->Line.cConnections = Count;
pDest = kmxlNextLine( pDest );
}
RETURN( STATUS_SUCCESS );
}
VOID
CleanupLine(
PMXLLINE pLine
)
{
PMXLCONTROL pControl;
while( pLine->Controls ) {
pControl = kmxlRemoveFirstControl( pLine->Controls );
kmxlFreeControl( pControl );
}
AudioFreeMemory( sizeof(MXLLINE),&pLine );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlEliminateInvalidLines
//
// Loops through the lines removing lines that are invalid. Refer
// to the function for IsValidLine() for details on what is an invalid
// line.
//
//
NTSTATUS
kmxlEliminateInvalidLines(
IN LINELIST* listLines // The list of lines
)
{
PMXLLINE pLine, pTemp, pShadow;
PAGED_CODE();
//
// Eliminate all invalid lines at the start of the list.
//
pLine = kmxlFirstInList( *listLines );
while( pLine ) {
//
// Found the first valid line. Break out of this loop.
//
if( Is_Valid_Line( pLine ) ) {
break;
}
//
// This is an invalid line. Remove it from the list, free up
// all its control structures, and free the line structure.
//
pTemp = kmxlRemoveFirstLine( pLine );
CleanupLine(pTemp);
}
//
// Assign listLines to point to the first valid line.
//
*listLines = pLine;
if( pLine == NULL ) {
RETURN( STATUS_SUCCESS );
}
//
// At this point, pLine is a valid line. Keeping a hold on the prev
// line, loop through the lines eliminating the invalid ones.
//
pShadow = pLine;
while( pShadow && kmxlNextLine( pShadow ) ) {
pLine = kmxlNextLine( pShadow );
if( pLine && !Is_Valid_Line( pLine ) ) {
//
// Remove the invalid line from the list
//
pShadow->List.Next = pLine->List.Next;
pLine->List.Next = NULL;
CleanupLine(pLine);
continue;
}
pShadow = kmxlNextLine( pShadow );
}
// All the invalid lines have been eliminated. Now eliminate bad
// duplicates.
pShadow = kmxlFirstInList( *listLines );
while( pShadow ) {
//
// Walk all the lines looking for a match.
//
pLine = kmxlNextLine( pShadow );
pTemp = NULL;
while( pLine ) {
if( ( pShadow->SourceId == pLine->SourceId ) &&
( pShadow->DestId == pLine->DestId ) )
{
DPF(DL_TRACE|FA_MIXER,( "Line %x is equal to line %x!",
pShadow->Line.dwLineID,
pLine->Line.dwLineID
) );
//
// Found a match.
//
if( pTemp == NULL )
{
//
// pShadow is our previous line. Remove this line from the
// list.
//
pShadow->List.Next = pLine->List.Next;
pLine->List.Next = NULL;
CleanupLine(pLine);
//
// Now adjust pLine to the next line and loop
//
pLine = kmxlNextLine( pShadow );
continue;
} else {
//
// pTemp is our previous line. Remove this line from the
// list.
//
pTemp->List.Next = pLine->List.Next;
pLine->List.Next = NULL;
CleanupLine(pLine);
//
// Now adjust pLine to the next line and loop
//
pLine = kmxlNextLine( pTemp );
continue;
}
}
pTemp = pLine; //temp is previous line
pLine = kmxlNextLine( pLine );
}
pShadow = kmxlNextLine( pShadow );
}
RETURN( STATUS_SUCCESS );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlAssignComponentIds
//
// Loops through all the destinations then the sources and determines
// their component type and target types.
//
//
VOID
kmxlAssignComponentIds(
IN PMIXEROBJECT pmxobj,
IN LINELIST listSourceLines,
IN LINELIST listDestLines
)
{
PMXLLINE pLine;
PAGED_CODE();
//
// Loop through the destinations...
//
pLine = kmxlFirstInList( listDestLines );
while( pLine ) {
pLine->Line.dwComponentType = kmxlDetermineDestinationType(
pmxobj,
pLine
);
pLine = kmxlNextLine( pLine );
}
//
// Loop through the sources...
//
pLine = kmxlFirstInList( listSourceLines );
while( pLine ) {
pLine->Line.dwComponentType = kmxlDetermineSourceType(
pmxobj,
pLine
);
pLine = kmxlNextLine( pLine );
}
}
///////////////////////////////////////////////////////////////////////
//
// kmxlUpdateMuxLines
//
// Updates the name, line ID, and componenttype of a line that has
// a mux control on it. The MixerControlDetails array is searched for
// an entry that has a matching source id and replaced with the info
// from this line.
//
//
VOID
kmxlUpdateMuxLines(
IN PMXLLINE pLine,
IN PMXLCONTROL pControl
)
{
ULONG i;
PAGED_CODE();
for( i = 0; i < pControl->Parameters.Count; i++ ) {
if( ( pLine->SourceId == pControl->Parameters.lpmcd_lt[ i ].dwParam1 ) &&
( pControl->Parameters.lpmcd_lt[ i ].dwParam2 == (DWORD) -1 ) )
{
wcscpy(
pControl->Parameters.lpmcd_lt[ i ].szName,
pLine->Line.szName
);
pControl->Parameters.lpmcd_lt[ i ].dwParam1 =
pLine->Line.dwLineID;
pControl->Parameters.lpmcd_lt[ i ].dwParam2 =
pLine->Line.dwComponentType;
}
}
}
///////////////////////////////////////////////////////////////////////
//
// kmxlAssignMuxIds
//
// Updates the source IDs stored in the MixerControlDetails array of
// the muxes and removes the muxes placed in lines as place holders.
//
//
NTSTATUS
kmxlAssignMuxIds(
IN PMIXEROBJECT pmxobj,
IN LINELIST listLines
)
{
PMXLLINE pLine;
PMXLCONTROL pControl;
CONTROLLIST listControls = NULL;
PAGED_CODE();
pLine = kmxlFirstInList( listLines );
while( pLine ) {
//
// Loop through the controls by removing them from the line's
// control list and building a new control list. This new
// control list will have the extra mux controls removed.
//
pControl = kmxlRemoveFirstControl( pLine->Controls );
while( pControl ) {
if( IsEqualGUID( pControl->NodeType, &KSNODETYPE_MUX ) ) {
kmxlUpdateMuxLines( pLine, pControl );
if( pControl->Parameters.bPlaceholder ) {
//
// This mux was here only to mark this line. Free
// up only the control memory and leave the parameters
// memeory alone.
//
ASSERT( pControl->pChannelStepping == NULL);
AudioFreeMemory_Unknown( &pControl );
--pLine->Line.cControls;
} else {
//
// This is a real mux control. Add it back into the
// list.
//
kmxlAddToEndOfList( listControls, pControl );
}
} else {
//
// Wasn't a mux. Put it onto the end of the new control
// list.
kmxlAddToEndOfList( listControls, pControl );
}
//
// Remove the next one!
//
pControl = kmxlRemoveFirstControl( pLine->Controls );
}
//
// Reassign the new control list back into this line.
//
pLine->Controls = listControls;
pLine = kmxlNextLine( pLine );
listControls = NULL;
}
RETURN( STATUS_SUCCESS );
}
///////////////////////////////////////////////////////////////////////
//
// TargetCommon
//
// Fills in the common fields of the target function.
//
//
VOID
TargetCommon(
IN PMIXEROBJECT pmxobj,
IN PMXLLINE pLine,
IN DWORD DeviceType
)
{
PWDMACONTEXT pWdmaContext;
PWAVEDEVICE paWaveOutDevs, paWaveInDevs;
PMIDIDEVICE paMidiOutDevs, paMidiInDevs;
ULONG i;
PAGED_CODE();
pWdmaContext = pmxobj->pMixerDevice->pWdmaContext;
paWaveOutDevs = pWdmaContext->WaveOutDevs;
paWaveInDevs = pWdmaContext->WaveInDevs;
paMidiOutDevs = pWdmaContext->MidiOutDevs;
paMidiInDevs = pWdmaContext->MidiInDevs;
for( i = 0; i < MAXNUMDEVS; i++ ) {
if( DeviceType == WaveOutDevice ) {
if( (paWaveOutDevs[i].Device != UNUSED_DEVICE) &&
!MyWcsicmp(pmxobj->DeviceInterface, paWaveOutDevs[ i ].DeviceInterface) ) {
WAVEOUTCAPS wc;
((PWAVEOUTCAPSA)(PVOID)&wc)->wMid=UNICODE_TAG;
wdmaudGetDevCaps( pWdmaContext, WaveOutDevice, i, (BYTE*) &wc, sizeof( WAVEOUTCAPS ) );
wcsncpy( pLine->Line.Target.szPname, wc.szPname, MAXPNAMELEN );
pLine->Line.Target.wMid = wc.wMid;
pLine->Line.Target.wPid = wc.wPid;
pLine->Line.Target.vDriverVersion = wc.vDriverVersion;
return;
}
}
if( DeviceType == WaveInDevice ) {
if( (paWaveInDevs[i].Device != UNUSED_DEVICE) &&
!MyWcsicmp(pmxobj->DeviceInterface, paWaveInDevs[ i ].DeviceInterface) ) {
WAVEINCAPS wc;
((PWAVEINCAPSA)(PVOID)&wc)->wMid=UNICODE_TAG;
wdmaudGetDevCaps( pWdmaContext, WaveInDevice, i, (BYTE*) &wc, sizeof( WAVEINCAPS ) );
wcsncpy( pLine->Line.Target.szPname, wc.szPname, MAXPNAMELEN );
pLine->Line.Target.wMid = wc.wMid;
pLine->Line.Target.wPid = wc.wPid;
pLine->Line.Target.vDriverVersion = wc.vDriverVersion;
return;
}
}
if( DeviceType == MidiOutDevice ) {
if( (paMidiOutDevs[i].Device != UNUSED_DEVICE) &&
!MyWcsicmp(pmxobj->DeviceInterface, paMidiOutDevs[ i ].DeviceInterface) ) {
MIDIOUTCAPS mc;
((PMIDIOUTCAPSA)(PVOID)&mc)->wMid=UNICODE_TAG;
wdmaudGetDevCaps( pWdmaContext, MidiOutDevice, i, (BYTE*) &mc, sizeof( MIDIOUTCAPS ) );
wcsncpy( pLine->Line.Target.szPname, mc.szPname, MAXPNAMELEN );
pLine->Line.Target.wMid = mc.wMid;
pLine->Line.Target.wPid = mc.wPid;
pLine->Line.Target.vDriverVersion = mc.vDriverVersion;
return;
}
}
if( DeviceType == MidiInDevice ) {
if( (paMidiInDevs[i].Device != UNUSED_DEVICE) &&
!MyWcsicmp(pmxobj->DeviceInterface, paMidiInDevs[ i ].DeviceInterface) ) {
MIDIINCAPS mc;
((PMIDIINCAPSA)(PVOID)&mc)->wMid=UNICODE_TAG;
wdmaudGetDevCaps( pWdmaContext, MidiInDevice, i, (BYTE*) &mc, sizeof( MIDIINCAPS ) );
wcsncpy( pLine->Line.Target.szPname, mc.szPname, MAXPNAMELEN) ;
pLine->Line.Target.wMid = mc.wMid;
pLine->Line.Target.wPid = mc.wPid;
pLine->Line.Target.vDriverVersion = mc.vDriverVersion;
return;
}
}
}
}
///////////////////////////////////////////////////////////////////////
//
// TargetTypeWaveOut
//
// Fills in the fields of aLine's target structure to be a waveout
// target.
//
//
VOID
TargetTypeWaveOut(
IN PMIXEROBJECT pmxobj,
IN PMXLLINE pLine
)
{
PAGED_CODE();
pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
TargetCommon( pmxobj, pLine, WaveOutDevice );
}
///////////////////////////////////////////////////////////////////////
//
// TargetTypeWaveIn
//
// Fills in the fields of aLine's target structure to be a wavein
// target.
//
//
#define TargetTypeWaveIn( pmxobj, pLine ) \
(pLine)->Line.Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN; \
(pLine)->Line.Target.wPid = MM_MSFT_WDMAUDIO_WAVEIN; \
TargetCommon( pmxobj, pLine, WaveInDevice )
///////////////////////////////////////////////////////////////////////
//
// TargetTypeMidiOut
//
// Fills in the fields of aLine's target structure to be a midi out
// target.
//
//
#define TargetTypeMidiOut( pmxobj, pLine ) \
(pLine)->Line.Target.dwType = MIXERLINE_TARGETTYPE_MIDIOUT; \
(pLine)->Line.Target.wPid = MM_MSFT_WDMAUDIO_MIDIOUT; \
TargetCommon( pmxobj, pLine, MidiOutDevice )
///////////////////////////////////////////////////////////////////////
//
// TargetTypeMidiIn
//
// Fills in the fields of aLine's target structure to be a midi in
// target.
//
//
#define TargetTypeMidiIn( pmxobj, pLine ) \
(aLine)->Line.Target.dwType = MIXERLINE_TARGETTYPE_MIDIOUT; \
(aLine)->Line.Target.wPid = MM_MSFT_WDMAUDIO_MIDIIN; \
TargetCommon( pmxobj, pLine, MidiInDevice )
///////////////////////////////////////////////////////////////////////
//
// TargetTypeAuxCD
//
// Fills in the fields of aLine's target structure to be a CD
// target.
//
//
#define TargetTypeAuxCD( pmxobj, pLine ) \
(pLine)->Line.Target.dwType = MIXERLINE_TARGETTYPE_AUX; \
TargetCommon( pmxobj, pLine, WaveOutDevice ); \
(pLine)->Line.Target.wPid = MM_MSFT_SB16_AUX_CD
///////////////////////////////////////////////////////////////////////
//
// TargetTypeAuxLine
//
// Fills in the fields of aLine's target structure to be a aux line
// target.
//
//
#define TargetTypeAuxLine( pmxobj, pLine ) \
(pLine)->Line.Target.dwType = MIXERLINE_TARGETTYPE_AUX; \
TargetCommon( pmxobj, pLine, WaveOutDevice );\
(pLine)->Line.Target.wPid = MM_MSFT_SB16_AUX_LINE
///////////////////////////////////////////////////////////////////////
//
// kmxlDetermineDestinationType
//
// Determines the destination and target types by using the Type
// GUID stored in the line structure.
//
//
ULONG
kmxlDetermineDestinationType(
IN PMIXEROBJECT pmxobj, // Instance data
IN PMXLLINE pLine // The line to determine type of
)
{
PAGED_CODE();
//
// Speaker type destinations
//
if( IsEqualGUID( &pLine->Type, &KSNODETYPE_SPEAKER ) ||
IsEqualGUID( &pLine->Type, &KSNODETYPE_DESKTOP_SPEAKER ) ||
IsEqualGUID( &pLine->Type, &KSNODETYPE_ROOM_SPEAKER ) ||
IsEqualGUID( &pLine->Type, &KSNODETYPE_COMMUNICATION_SPEAKER ) ) {
TargetTypeWaveOut( pmxobj, pLine );
return( MIXERLINE_COMPONENTTYPE_DST_SPEAKERS );
}
//
// WaveIn type destinations
//
if( IsEqualGUID( &pLine->Type, &KSCATEGORY_AUDIO )
|| IsEqualGUID( &pLine->Type, &PINNAME_CAPTURE )
) {
TargetTypeWaveIn( pmxobj, pLine );
return( MIXERLINE_COMPONENTTYPE_DST_WAVEIN );
}
//
// Headphone destination
//
if( IsEqualGUID( &pLine->Type, &KSNODETYPE_HEADPHONES ) ||
IsEqualGUID( &pLine->Type, &KSNODETYPE_HEAD_MOUNTED_DISPLAY_AUDIO ) ) {
TargetTypeWaveOut( pmxobj, pLine );
return( MIXERLINE_COMPONENTTYPE_DST_HEADPHONES );
}
//
// Telephone destination
//
if( IsEqualGUID( &pLine->Type, &KSNODETYPE_TELEPHONE ) ||
IsEqualGUID( &pLine->Type, &KSNODETYPE_PHONE_LINE ) ||
IsEqualGUID( &pLine->Type, &KSNODETYPE_DOWN_LINE_PHONE ) )
{
pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
return( MIXERLINE_COMPONENTTYPE_DST_TELEPHONE );
}
//
// Ambiguous destination type. Figure out the destination type by looking
// at the Communication.
//
if( IsEqualGUID( &pLine->Type, &KSNODETYPE_ANALOG_CONNECTOR ) ||
IsEqualGUID( &pLine->Type, &KSNODETYPE_SPDIF_INTERFACE ) ) {
if (pLine->Communication == KSPIN_COMMUNICATION_BRIDGE) {
TargetTypeWaveOut( pmxobj, pLine );
return( MIXERLINE_COMPONENTTYPE_DST_SPEAKERS );
} else {
TargetTypeWaveIn( pmxobj, pLine );
return( MIXERLINE_COMPONENTTYPE_DST_WAVEIN );
}
}
//
// Does not match the others. Default to Undefined destination.
//
pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
return( MIXERLINE_COMPONENTTYPE_DST_UNDEFINED );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlDetermineSourceType
//
// Determines the destination and target types by using the Type
// GUID stored in the line structure.
//
//
ULONG
kmxlDetermineSourceType(
IN PMIXEROBJECT pmxobj, // Instance data
IN PMXLLINE pLine // The line to determine type of
)
{
PAGED_CODE();
//
// All microphone type sources are a microphone source.
//
//
// We are only checking two microphone GUIDs here. We may
// want to consider the rest of the microphone types in
// ksmedia.h
//
if( IsEqualGUID( &pLine->Type, &KSNODETYPE_MICROPHONE )
|| IsEqualGUID( &pLine->Type, &KSNODETYPE_DESKTOP_MICROPHONE )
)
{
TargetTypeWaveIn( pmxobj, pLine );
return( MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE );
}
//
// Legacy audio connector and the speaker type sources represent a
// waveout source.
//
if( IsEqualGUID( &pLine->Type, &KSNODETYPE_LEGACY_AUDIO_CONNECTOR )
|| IsEqualGUID( &pLine->Type, &KSNODETYPE_SPEAKER )
|| IsEqualGUID( &pLine->Type, &KSCATEGORY_AUDIO )
)
{
TargetTypeWaveOut( pmxobj, pLine );
return( MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT );
}
//
// CD player is a compact disc source.
//
if( IsEqualGUID( &pLine->Type, &KSNODETYPE_CD_PLAYER ) ) {
TargetTypeAuxCD( pmxobj, pLine );
pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
return( MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC );
}
//
// Synthesizer is a sythesizer source.
//
if( IsEqualGUID( &pLine->Type, &KSNODETYPE_SYNTHESIZER ) ) {
TargetTypeMidiOut( pmxobj, pLine );
return( MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER );
}
if( IsEqualGUID( &pLine->Type, &KSNODETYPE_LINE_CONNECTOR ) ) {
TargetTypeAuxLine( pmxobj, pLine );
pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
return( MIXERLINE_COMPONENTTYPE_SRC_LINE );
}
if( IsEqualGUID( &pLine->Type, &KSNODETYPE_PHONE_LINE ) ||
IsEqualGUID( &pLine->Type, &KSNODETYPE_TELEPHONE ) ||
IsEqualGUID( &pLine->Type, &KSNODETYPE_DOWN_LINE_PHONE ) )
{
pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
return( MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE );
}
if( IsEqualGUID( &pLine->Type, &KSNODETYPE_ANALOG_CONNECTOR ) ) {
//
// Ambiguous src type. Figure out the destination type by looking
// at the Communication.
//
if (pLine->Communication == KSPIN_COMMUNICATION_BRIDGE) {
TargetTypeWaveIn( pmxobj, pLine );
}
else {
TargetTypeWaveOut( pmxobj, pLine );
}
return( MIXERLINE_COMPONENTTYPE_SRC_ANALOG );
}
//
// Digital in/out (SPDIF) source
//
if( IsEqualGUID( &pLine->Type, &KSNODETYPE_SPDIF_INTERFACE ) ) {
//
// Ambiguous src type. Figure out the destination type by looking
// at the Communication.
//
if (pLine->Communication == KSPIN_COMMUNICATION_BRIDGE) {
TargetTypeWaveIn( pmxobj, pLine );
}
else {
TargetTypeWaveOut( pmxobj, pLine );
}
return( MIXERLINE_COMPONENTTYPE_SRC_DIGITAL );
}
//
// All others are lumped under Undefined source.
//
pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
return( MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED );
}
///////////////////////////////////////////////////////////////////////
//
// PinCategoryToString
//
// Converts the Pin category GUIDs to a string.
#ifdef DEBUG
#pragma LOCKED_CODE
#endif
#define _EG_(x,y) if (IsEqualGUID( NodeType, &x)) { return y; }
const char*
PinCategoryToString
(
IN CONST GUID* NodeType // The GUID to translate
)
{
_EG_(KSNODETYPE_MICROPHONE,"Microphone");
_EG_(KSNODETYPE_DESKTOP_MICROPHONE,"Desktop Microphone");
_EG_(KSNODETYPE_SPEAKER,"Speaker");
_EG_(KSNODETYPE_HEADPHONES,"Headphones");
_EG_(KSNODETYPE_LEGACY_AUDIO_CONNECTOR,"Wave");
_EG_(KSNODETYPE_CD_PLAYER,"CD Player");
_EG_(KSNODETYPE_SYNTHESIZER,"Synthesizer");
_EG_(KSCATEGORY_AUDIO,"Wave");
_EG_(PINNAME_CAPTURE,"Wave In");
_EG_(KSNODETYPE_LINE_CONNECTOR,"Aux Line");
_EG_(KSNODETYPE_TELEPHONE,"Telephone");
_EG_(KSNODETYPE_PHONE_LINE,"Phone Line");
_EG_(KSNODETYPE_DOWN_LINE_PHONE,"Downline Phone");
_EG_(KSNODETYPE_ANALOG_CONNECTOR,"Analog connector");
//New debug names...
_EG_(KSAUDFNAME_MONO_OUT,"Mono Out");
_EG_(KSAUDFNAME_STEREO_MIX,"Stereo Mix");
_EG_(KSAUDFNAME_MONO_MIX,"Mono Mix");
_EG_(KSAUDFNAME_AUX,"Aux");
_EG_(KSAUDFNAME_VIDEO,"Video");
_EG_(KSAUDFNAME_LINE_IN,"Line In");
DPF(DL_WARNING|FA_MIXER,("Path Trap send me GUID - dt %08X _GUID",NodeType) );
return "Unknown Pin Category";
}
///////////////////////////////////////////////////////////////////////
//
// NodeTypeToString
//
// Converts a NodeType GUID to a string
//
//
const char*
NodeTypeToString
(
IN CONST GUID* NodeType // The GUID to translate
)
{
_EG_(KSNODETYPE_DAC,"DAC");
_EG_(KSNODETYPE_ADC,"ADC");
_EG_(KSNODETYPE_SRC,"SRC");
_EG_(KSNODETYPE_SUPERMIX,"SuperMIX");
_EG_(KSNODETYPE_SUM,"Sum");
_EG_(KSNODETYPE_MUTE,"Mute");
_EG_(KSNODETYPE_VOLUME,"Volume");
_EG_(KSNODETYPE_TONE,"Tone");
_EG_(KSNODETYPE_AGC,"AGC");
_EG_(KSNODETYPE_DELAY,"Delay");
_EG_(KSNODETYPE_LOUDNESS,"LOUDNESS");
_EG_(KSNODETYPE_3D_EFFECTS,"3D Effects");
_EG_(KSNODETYPE_DEV_SPECIFIC,"Dev Specific");
_EG_(KSNODETYPE_STEREO_WIDE,"Stereo Wide");
_EG_(KSNODETYPE_REVERB,"Reverb");
_EG_(KSNODETYPE_CHORUS,"Chorus");
_EG_(KSNODETYPE_ACOUSTIC_ECHO_CANCEL,"AEC");
_EG_(KSNODETYPE_EQUALIZER,"Equalizer");
_EG_(KSNODETYPE_MUX,"Mux");
_EG_(KSNODETYPE_DEMUX,"Demux");
_EG_(KSNODETYPE_STEREO_ENHANCE,"Stereo Enhance");
_EG_(KSNODETYPE_SYNTHESIZER,"Synthesizer");
_EG_(KSNODETYPE_PEAKMETER,"Peakmeter");
_EG_(KSNODETYPE_LINE_CONNECTOR,"Line Connector");
_EG_(KSNODETYPE_SPEAKER,"Speaker");
_EG_(KSNODETYPE_DESKTOP_SPEAKER,"");
_EG_(KSNODETYPE_ROOM_SPEAKER,"Room Speaker");
_EG_(KSNODETYPE_COMMUNICATION_SPEAKER,"Communication Speaker");
_EG_(KSNODETYPE_LOW_FREQUENCY_EFFECTS_SPEAKER,"? Whatever...");
_EG_(KSNODETYPE_HANDSET,"Handset");
_EG_(KSNODETYPE_HEADSET,"Headset");
_EG_(KSNODETYPE_SPEAKERPHONE_NO_ECHO_REDUCTION,"Speakerphone no echo reduction");
_EG_(KSNODETYPE_ECHO_SUPPRESSING_SPEAKERPHONE,"Echo Suppressing Speakerphone");
_EG_(KSNODETYPE_ECHO_CANCELING_SPEAKERPHONE,"Echo Canceling Speakerphone");
_EG_(KSNODETYPE_CD_PLAYER,"CD Player");
_EG_(KSNODETYPE_MICROPHONE,"Microphone");
_EG_(KSNODETYPE_DESKTOP_MICROPHONE,"Desktop Microphone");
_EG_(KSNODETYPE_PERSONAL_MICROPHONE,"Personal Microphone");
_EG_(KSNODETYPE_OMNI_DIRECTIONAL_MICROPHONE,"Omni Directional Microphone");
_EG_(KSNODETYPE_MICROPHONE_ARRAY,"Microphone Array");
_EG_(KSNODETYPE_PROCESSING_MICROPHONE_ARRAY,"Processing Microphone Array");
_EG_(KSNODETYPE_ANALOG_CONNECTOR,"Analog Connector");
_EG_(KSNODETYPE_PHONE_LINE,"Phone Line");
_EG_(KSNODETYPE_HEADPHONES,"Headphones");
_EG_(KSNODETYPE_HEAD_MOUNTED_DISPLAY_AUDIO,"Head Mounted Display Audio");
_EG_(KSNODETYPE_LEGACY_AUDIO_CONNECTOR,"Legacy Audio Connector");
// _EG_(KSNODETYPE_SURROUND_ENCODER,"Surround Encoder");
_EG_(KSNODETYPE_NOISE_SUPPRESS,"Noise Suppress");
_EG_(KSNODETYPE_DRM_DESCRAMBLE,"DRM Descramble");
_EG_(KSNODETYPE_SWMIDI,"SWMidi");
_EG_(KSNODETYPE_SWSYNTH,"SWSynth");
_EG_(KSNODETYPE_MULTITRACK_RECORDER,"Multitrack Recorder");
_EG_(KSNODETYPE_RADIO_TRANSMITTER,"Radio Transmitter");
_EG_(KSNODETYPE_TELEPHONE,"Telephone");
_EG_(KSAUDFNAME_MONO_OUT,"Mono Out");
_EG_(KSAUDFNAME_LINE_IN,"Line in");
_EG_(KSAUDFNAME_VIDEO,"Video");
_EG_(KSAUDFNAME_AUX,"Aux");
_EG_(KSAUDFNAME_MONO_MIX,"Mono Mix");
_EG_(KSAUDFNAME_STEREO_MIX,"Stereo Mix");
_EG_(KSCATEGORY_AUDIO,"Audio");
_EG_(PINNAME_VIDEO_CAPTURE,"Video Capture");
DPF(DL_WARNING|FA_MIXER,("Path Trap send me GUID - dt %08X _GUID",NodeType) );
return "Unknown NodeType";
}