windows-nt/Source/XPSP1/NT/drivers/wdm/audio/legacy/wdmaud.sys/kmxlutil.c
2020-09-26 16:20:57 +08:00

1143 lines
28 KiB
C

//---------------------------------------------------------------------------
//
// Module: kmxlutil.c
//
// Description:
// Utility routines used by the kernel mixer line driver (KMXL).
//
//
//@@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"
#undef SUPER_DEBUG
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// //
// U T I L I T Y F U N C T I O N S //
// //
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
//
// kmxlOpenSysAudio
//
// Opens the topology driver and dereferences the handle to get the
// file object.
//
//
PFILE_OBJECT
kmxlOpenSysAudio(
)
{
PFILE_OBJECT pfo = NULL;
HANDLE hDevice = NULL;
ULONG ulDefault;
NTSTATUS Status;
PAGED_CODE();
//
// Open the topology driver.
//
Status = OpenSysAudio(&hDevice, &pfo);
if( !NT_SUCCESS( Status ) ) {
DPF(DL_WARNING|FA_SYSAUDIO,("OpenSysAudio failed Status=%X",Status) );
return( NULL );
}
//
// The handle is no longer necessary so close it.
//
NtClose( hDevice );
ulDefault = KSPROPERTY_SYSAUDIO_MIXER_DEFAULT;
Status = SetSysAudioProperty(
pfo,
KSPROPERTY_SYSAUDIO_DEVICE_DEFAULT,
sizeof(ulDefault),
&ulDefault);
if( !NT_SUCCESS( Status ) ) {
DPF(DL_WARNING|FA_SYSAUDIO,("SetSysAudioProperty failed Status=%X",Status) );
return( NULL );
}
return( pfo );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlCloseSysAudio
//
// Close the topology device by dereferencing the file object.
//
//
VOID
kmxlCloseSysAudio(
IN PFILE_OBJECT pfo // Pointer to the file object to close
)
{
PAGED_CODE();
ObDereferenceObject( pfo );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlFindDestination
//
// In the list of destinations, it finds the destination matching
// the given id.
//
//
PMXLNODE
kmxlFindDestination(
IN NODELIST listDests, // The list of destinations to search
IN ULONG Id // The node Id to look for in the list
)
{
PMXLNODE pTemp = kmxlFirstInList( listDests );
PAGED_CODE();
while( pTemp ) {
if( pTemp->Id == Id ) {
return( pTemp );
}
pTemp = kmxlNextNode( pTemp );
}
return( NULL );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlAppendListToList
//
// Finds the end of the source list and makes the next element point
// to the head of target list.
//
//
VOID
kmxlAppendListToList(
IN OUT PSLIST* plistTarget, // The list to append to
IN PSLIST listSource // the list to append
)
{
PSLIST pTemp;
PAGED_CODE();
if( *plistTarget == NULL ) {
*plistTarget = listSource;
return;
}
//
// If source is NULL, there's no need to append.
//
if( listSource == NULL ) {
return;
}
//
// First find the end of the source list. At this point,
// listSource has at least 1 element.
//
pTemp = listSource;
while( pTemp->Next ) {
pTemp = pTemp->Next;
}
//
// Attach the target list onto the end.
//
pTemp->Next = *plistTarget;
*plistTarget = listSource;
}
///////////////////////////////////////////////////////////////////////
//
// kmxlAppendListToEndOfList
//
// Finds the end of the target list and points the next to the source
// list.
//
//
VOID
kmxlAppendListToEndOfList(
IN OUT PSLIST* plistTarget, // The list to append to
IN PSLIST listSource // the list to append
)
{
PSLIST pTemp;
PAGED_CODE();
if( *plistTarget == NULL ) {
*plistTarget = listSource;
return;
}
//
// Find the end of the target list. Target list must contain
// at least one element at this point.
//
pTemp = *plistTarget;
while( pTemp->Next ) {
pTemp = pTemp->Next;
}
pTemp->Next = listSource;
}
////////////////////////////////////////////////////////////////////////
//
// kmxlListCount
//
// Loops through the Next fields to count the elements.
//
//
ULONG
kmxlListCount(
IN PSLIST pList // The list to count the elements of
)
{
ULONG Count = 0;
PSLIST pTemp = pList;
PAGED_CODE();
while( pTemp ) {
++Count;
pTemp = pTemp->Next;
}
return( Count );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlInList
//
// Loops through the given list looking for pNewNode.
//
//
BOOL
kmxlInList(
IN PEERLIST list, // The list to search
IN PMXLNODE pNewNode // The new to search for
)
{
PEERNODE* pTemp = kmxlFirstInList( list );
PAGED_CODE();
// Zing through the list checking to see if there is a node with
// the same Id and Type. These two checks are suffient to ensure
// uniquness. Ids are unique among all sources and destinations,
// and Ids, or node numbers, are unique among all nodes. Note
// that a source (or destination) node and a node can have the same
// Id.
while( pTemp ) {
if( ( pTemp->pNode->Id == pNewNode->Id ) &&
( pTemp->pNode->Type == pNewNode->Type ) )
return( TRUE );
pTemp = kmxlNextPeerNode( pTemp );
}
// No match in the entire list, the new node is not already in the
// list.
return( FALSE );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlInChildList
//
// Calls kmxlInList on the child list of the node.
//
//
BOOL
kmxlInChildList(
IN NODELIST list, // The list to search the parent list
IN PMXLNODE pNewNode // The node to search for
)
{
ASSERT( list ) ;
ASSERT( pNewNode );
PAGED_CODE();
return( kmxlInList( list->Children, pNewNode ) );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlInParentList
//
// Calls kmxlInList on the parent list of the node.
//
//
BOOL
kmxlInParentList(
IN NODELIST list, // The list to search the parent list
IN PMXLNODE pNewNode // The node to search for
)
{
ASSERT( list );
ASSERT( pNewNode );
PAGED_CODE();
return( kmxlInList( list->Parents, pNewNode ) );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlFreePeerList
//
//
// NOTES
// This only frees the peer nodes in a peer list. The nodes pointed
// to be the pNode member must be clean up in some other manner.
//
//
VOID
kmxlFreePeerList(
IN PEERLIST list // The PeerList to free
)
{
PEERNODE* pPeerNode = kmxlRemoveFirstPeerNode( list );
PAGED_CODE();
while( pPeerNode ) {
AudioFreeMemory( sizeof(PEERNODE),&pPeerNode );
pPeerNode = kmxlRemoveFirstPeerNode( list );
}
}
///////////////////////////////////////////////////////////////////////
//
// kmxlAllocateMixerControl
//
// Calls AudioAllocateMemory() to allocate and zero fill the MXLCONTROL.
//
//
MXLCONTROL*
kmxlAllocateControl(
IN ULONG ultag
)
{
MXLCONTROL* p = NULL;
PAGED_CODE();
if( NT_SUCCESS( AudioAllocateMemory_Paged(sizeof( MXLCONTROL ),
ultag,
ZERO_FILL_MEMORY,
&p) ) )
{
#ifdef DEBUG
p->Tag=CONTROL_TAG;
#endif
return( p );
} else {
return( NULL );
}
}
///////////////////////////////////////////////////////////////////////
//
// kmxlFreeControl
//
// Frees the memory associated with a control. It also checkes the
// special cases for some controls that have special memory associated
// with them. And, if the control supports change notifications, it gets
// turned off here.
//
//
VOID
kmxlFreeControl(
IN PMXLCONTROL pControl
)
{
NTSTATUS Status;
PAGED_CODE();
DPFASSERT( IsValidControl( pControl ) );
//
// Need to disable change notifications on this node if it supported them!
//
kmxlDisableControlChangeNotifications(pControl);
if( pControl->NodeType ) {
if( IsEqualGUID( pControl->NodeType, &KSNODETYPE_MUX ) &&
!pControl->Parameters.bHasCopy ) {
AudioFreeMemory_Unknown( &pControl->Parameters.lpmcd_lt );
AudioFreeMemory_Unknown( &pControl->Parameters.pPins );
}
if( IsEqualGUID( pControl->NodeType, &KSNODETYPE_SUPERMIX ) ) {
if (InterlockedDecrement(pControl->Parameters.pReferenceCount)==0) {
AudioFreeMemory_Unknown( &pControl->Parameters.pMixCaps );
AudioFreeMemory_Unknown( &pControl->Parameters.pMixLevels );
AudioFreeMemory( sizeof(LONG),&pControl->Parameters.pReferenceCount );
}
}
}
// Check that we're not in the case where Numchannels == 0 And we have a valid
// pControl->pChannelStepping. If this were true, we'd end up leaking
// pChannelStepping.
ASSERT( !(pControl->pChannelStepping && pControl->NumChannels == 0) );
if ( pControl->pChannelStepping && pControl->NumChannels > 0 ) {
RtlZeroMemory( pControl->pChannelStepping, pControl->NumChannels * sizeof( CHANNEL_STEPPING ) );
AudioFreeMemory_Unknown( &pControl->pChannelStepping );
}
//
// Why do we zero the memory on this free?
//
RtlZeroMemory( pControl, sizeof( MXLCONTROL ) );
AudioFreeMemory( sizeof( MXLCONTROL ),&pControl );
}
///////////////////////////////////////////////////////////////////////
//
// kmxlAllocateLine
//
// Calls AudioAllocateMemory() to allocate and zero fill the MXLLINE.
//
//
//
// Workitem: Tag all these structures in debug!
//
MXLLINE*
kmxlAllocateLine(
IN ULONG ultag
)
{
MXLLINE* p = NULL;
PAGED_CODE();
if( NT_SUCCESS( AudioAllocateMemory_Paged( sizeof( MXLLINE ),
ultag,
ZERO_FILL_MEMORY,
&p ) ) )
{
p->SourceId = INVALID_ID;
p->DestId = INVALID_ID;
return( p );
} else {
return( NULL );
}
}
///////////////////////////////////////////////////////////////////////
//
// kmxlAllocateNode
//
// Calls AudioAllocateMemory() to allocate and zero fill the MXLNODE.
//
//
MXLNODE*
kmxlAllocateNode(
IN ULONG ultag
)
{
MXLNODE* p = NULL;
PAGED_CODE();
if( NT_SUCCESS( AudioAllocateMemory_Paged( sizeof( MXLNODE ),
ultag,
ZERO_FILL_MEMORY,
&p ) ) )
{
return( p );
} else {
return( NULL );
}
}
///////////////////////////////////////////////////////////////////////
//
// kmxlAllocatePeerNode
//
// Calls AudioAllocateMemory() to allocate and zero fill the PEERNODE.
//
//
PEERNODE*
kmxlAllocatePeerNode(
IN PMXLNODE pNode OPTIONAL, // The node to associate with the peer
IN ULONG ultag
)
{
PEERNODE* p = NULL;
PAGED_CODE();
if( NT_SUCCESS( AudioAllocateMemory_Paged( sizeof( PEERNODE ),
ultag,
ZERO_FILL_MEMORY,
&p ) ) )
{
p->pNode = pNode;
return( p );
} else {
return( NULL );
}
}
///////////////////////////////////////////////////////////////////////
//
// kmxlAddToEndOfList
//
// Finds the end of the list and sets the next field to the new element.
//
//
VOID
kmxlAddElemToEndOfList(
IN OUT PSLIST* list, // The list to add to the end of
IN PSLIST elem // The element or list to add
)
{
PSLIST pTemp;
PAGED_CODE();
ASSERT( list );
ASSERT( elem->Next == NULL );
//
// If the list doesn't have anything in it, the element becomes the
// list.
//
if( *list == NULL ) {
*list = elem;
return;
}
//
// Find the end of the list.
//
pTemp = *list;
while( pTemp->Next ) {
pTemp = pTemp->Next;
}
//
// And attach the element to it.
//
pTemp->Next = elem;
}
#define LINEAR_RANGE 0xFFFF // 64k
#define DFLINEAR_RANGE ( 96.0 * 65535.0 )
#define NEG_INF_DB 0x80000000 // -32767 * 64k dB
///////////////////////////////////////////////////////////////////////
//
// kmxlVolLogToLinear
//
// Converts from the hardware range (dB) to the liner mixer line range (0-64k).
//
//
DWORD
kmxlVolLogToLinear(
IN PMXLCONTROL pControl,
IN LONG Value,
IN MIXERMAPPING Mapping,
IN ULONG Channel
)
{
KFLOATING_SAVE FloatSave;
double LinearRange;
double dfValue;
double dfResult;
double dfRatio;
DWORD Result;
PCHANNEL_STEPPING pChannelStepping;
PAGED_CODE();
if( Value == NEG_INF_DB ) {
return( 0 );
}
ASSERT( Channel < pControl->NumChannels );
// Get the proper range for the specified channel
pChannelStepping = &pControl->pChannelStepping[Channel];
if( NT_SUCCESS( KeSaveFloatingPointState( &FloatSave ) ) ) {
LinearRange = (double) LINEAR_RANGE;
dfValue = (double) Value;
switch( Mapping ) {
////////////////////////////////////////////////////////////
case MIXER_MAPPING_LOGRITHMIC:
////////////////////////////////////////////////////////////
dfRatio = ( (double) pChannelStepping->MaxValue -
(double) pChannelStepping->MinValue ) / DFLINEAR_RANGE;
if( dfRatio < 1.0 ) {
dfRatio = 1.0;
}
dfValue = ( dfValue - pChannelStepping->MaxValue ) / LinearRange;
dfResult = LinearRange * pow( 10.0, dfValue / ( 20.0 * dfRatio ) );
if( dfResult >= LINEAR_RANGE ) {
Result = LINEAR_RANGE;
} else if ( dfResult < 0.0 ) {
Result = 0;
} else {
Result = (DWORD) ( dfResult + 0.5 );
}
break;
////////////////////////////////////////////////////////////
case MIXER_MAPPING_LINEAR:
////////////////////////////////////////////////////////////
dfResult = ( LinearRange * ( dfValue - pChannelStepping->MinValue ) ) /
( pChannelStepping->MaxValue - pChannelStepping->MinValue );
Result = (DWORD) ( dfResult + 0.5 );
break;
////////////////////////////////////////////////////////////
default:
////////////////////////////////////////////////////////////
ASSERT( 0 );
Result = 0;
}
KeRestoreFloatingPointState( &FloatSave );
DPF(DL_TRACE|FA_MIXER,
( "kmxlVolLogToLinear( %x [%d] ) =%d= %x [%d]",
Value,
Value,
Mapping,
(WORD) Result,
(WORD) Result
) );
return( Result );
} else {
return( (DWORD) ( LINEAR_RANGE *
( (LONGLONG) Value - (LONGLONG) pChannelStepping->MinValue ) /
( (LONGLONG) pChannelStepping->MaxValue -
(LONGLONG) pChannelStepping->MinValue ) ) );
}
#ifdef LEGACY_SCALE
WORD Result;
Result = VolLogToLinear( (WORD) ( Value / ( -1 * LINEAR_RANGE ) ) );
#ifdef API_TRACE
TRACE( "WDMAUD: kmxlVolLogToLinear( %x [%d] ) = %x [%d]\n",
Value,
Value,
(WORD) Result,
(WORD) Result
);
#endif
return( Result );
#endif // LEGACY_SCALE
#ifdef LONG_CALC_SCALE
LONGLONG ControlRange = (LONGLONG) pChannelStepping->MaxValue -
(LONGLONG) pChannelStepping->MinValue;
LONGLONG MinValue = (LONGLONG) pChannelStepping->MinValue;
LONGLONG Result;
ASSERT( ControlRange );
Result = LINEAR_RANGE * ( (LONGLONG) Value - MinValue ) / ControlRange;
#ifdef API_TRACE
TRACE( "WDMAUD: kmxlVolLogToLinear( %x [%d] ) = %x [%d]\n",
Value,
Value,
(WORD) Result,
(WORD) Result
);
#endif
return( (WORD) Result );
#endif // LONG_CALC_SCALE
}
///////////////////////////////////////////////////////////////////////
//
// kmxlVolLinearToLog
//
// Converts from the mixer line range (0-64k) to the hardware range (dB).
//
//
LONG
kmxlVolLinearToLog(
IN PMXLCONTROL pControl,
IN DWORD Value,
IN MIXERMAPPING Mapping,
IN ULONG Channel
)
{
KFLOATING_SAVE FloatSave;
double LinearRange;
double dfValue;
double dfResult;
double dfRatio;
LONG Result;
PCHANNEL_STEPPING pChannelStepping;
PAGED_CODE();
if( Value == 0 ) {
return( NEG_INF_DB );
}
ASSERT( Channel < pControl->NumChannels );
// Get the proper range for the specified channel
pChannelStepping = &pControl->pChannelStepping[Channel];
if( NT_SUCCESS( KeSaveFloatingPointState( &FloatSave ) ) ) {
LinearRange = (double) LINEAR_RANGE;
dfValue = (double) Value;
switch( Mapping ) {
////////////////////////////////////////////////////////////
case MIXER_MAPPING_LOGRITHMIC:
////////////////////////////////////////////////////////////
dfRatio = ( (double) pChannelStepping->MaxValue -
(double) pChannelStepping->MinValue ) / DFLINEAR_RANGE;
if( dfRatio < 1.0 ) {
dfRatio = 1.0;
}
dfResult = LinearRange * dfRatio * 20.0 * log10( dfValue / LinearRange );
if( dfResult < 0.0 ) {
Result = (LONG) ( dfResult - 0.5 ) + pChannelStepping->MaxValue;
} else {
Result = (LONG) ( dfResult + 0.5 ) + pChannelStepping->MaxValue;
}
break;
////////////////////////////////////////////////////////////
case MIXER_MAPPING_LINEAR:
////////////////////////////////////////////////////////////
dfResult = ( dfValue * ( pChannelStepping->MaxValue - pChannelStepping->MinValue ) ) /
LinearRange + pChannelStepping->MinValue;
if( dfResult < 0.0 ) {
Result = (LONG) ( dfResult - 0.5 );
} else {
Result = (LONG) ( dfResult + 0.5 );
}
break;
////////////////////////////////////////////////////////////
default:
////////////////////////////////////////////////////////////
ASSERT( 0 );
Result = NEG_INF_DB;
}
KeRestoreFloatingPointState( &FloatSave );
DPF(DL_TRACE|FA_MIXER,
( "kmxlVolLinearToLog( %x [%d]) =%d= %x [%d]",
Value,
Value,
Mapping,
(LONG) Result,
(LONG) Result
) );
return( Result );
} else {
return( (LONG)
( (LONGLONG) Value *
(LONGLONG) ( pChannelStepping->MaxValue - pChannelStepping->MinValue )
/ ( LONGLONG ) LINEAR_RANGE + (LONGLONG) pChannelStepping->MinValue )
);
}
#ifdef LEGACY_SCALE
LONG Result;
if( Value == 0 ) {
Result = NEG_INF_DB;
} else {
Result = (LONG) VolLinearToLog( Value ) * -1 * (LONG) LINEAR_RANGE + pChannelStepping->MaxValue;
}
#ifdef API_TRACE
TRACE( "WDMAUD: kmxlVolLinearToLog( %x [%d]) = %x [%d]\n",
Value,
Value,
(LONG) Result,
(LONG) Result
);
#endif
return( Result );
#endif // LEGACY_SCALE
#ifdef LONG_CALC_SCALE
LONGLONG ControlRange = (LONGLONG) pChannelStepping->MaxValue -
(LONGLONG) pChannelStepping->MinValue;
LONGLONG MinValue = pChannelStepping->MinValue;
LONGLONG Result;
ASSERT( ControlRange );
Result = (LONGLONG) Value * ControlRange / LINEAR_RANGE + MinValue;
#ifdef API_TRACE
TRACE( "WDMAUD: kmxlVolLinearToLog( %x [%d]) = %x [%d]\n",
Value,
Value,
(LONG) Result,
(LONG) Result
);
#endif
return( (LONG) Result );
#endif // LONG_CALC_SCALE
}
///////////////////////////////////////////////////////////////////////
//
// kmxlSortByDestination
//
// Performs a sort by destination in numerical increasing order.
//
//
NTSTATUS
kmxlSortByDestination(
IN LINELIST* list // The pointer to the list to sort
)
{
PMXLLINE pTemp1,
pTemp2;
MXLLINE Temp;
ULONG Count = kmxlListLength( *list );
PAGED_CODE();
//
// If there are only 0 or 1 elements, there's no reason to even try to
// sort.
//
if( Count < 2 ) {
return( STATUS_SUCCESS );
}
//
// Pretty standard BubbleSort.
//
while( --Count ) {
//
// Loop over each element in the list.
//
pTemp1 = kmxlFirstInList( *list );
while( pTemp1 ) {
//
// Loop over the remaining elements.
//
pTemp2 = kmxlNextLine( pTemp1 );
while( pTemp2 ) {
//
// The destination is strictly bigger. Swap 'em.
//
if( pTemp1->DestId > pTemp2->DestId ) {
SwapEm( pTemp1, pTemp2, &Temp, sizeof( MXLLINE ) );
break;
}
//
// The destinations are the same, but the source is
// bigger. Swap 'em.
//
if( pTemp1->DestId == pTemp2->DestId ) {
if( pTemp1->SourceId > pTemp2->SourceId ) {
SwapEm( pTemp1, pTemp2, &Temp, sizeof( MXLLINE ) );
break;
}
}
pTemp2 = kmxlNextLine( pTemp2 );
}
pTemp1 = kmxlNextLine( pTemp1 );
}
}
return( STATUS_SUCCESS );
}
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// //
// M I X E R L I N E W R A P P E R S //
// //
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
#pragma warning( disable : 4273 )
///////////////////////////////////////////////////////////////////////
//
// kmxlAllocDeviceInfo
//
// Note: when allocating DeviceInfo structure, we know that the structure's
// definition includes one character for the DeviceInterface, so we only need
// to allocate additional length for the string but not its NULL terminator
//
///////////////////////////////////////////////////////////////////////
NTSTATUS kmxlAllocDeviceInfo(
LPDEVICEINFO *ppDeviceInfo,
PCWSTR DeviceInterface,
DWORD dwFlags,
ULONG ultag
)
{
NTSTATUS Status;
PAGED_CODE();
Status = AudioAllocateMemory_Paged(sizeof(**ppDeviceInfo)+(wcslen(DeviceInterface)*sizeof(WCHAR)),
ultag,
ZERO_FILL_MEMORY,
ppDeviceInfo);
if (NT_SUCCESS(Status))
{
wcscpy((*ppDeviceInfo)->wstrDeviceInterface, DeviceInterface);
(*ppDeviceInfo)->DeviceType = MixerDevice;
(*ppDeviceInfo)->dwFormat = UNICODE_TAG;
(*ppDeviceInfo)->dwFlags = dwFlags;
} else {
*ppDeviceInfo = NULL;
}
return Status;
}
///////////////////////////////////////////////////////////////////////
//
// mixerGetControlDetails
//
//
MMRESULT
WINAPI
kmxlGetControlDetails(
PWDMACONTEXT pWdmaContext,
PCWSTR DeviceInterface,
LPMIXERCONTROLDETAILS pmxcd,
DWORD fdwDetails
)
{
LPDEVICEINFO DeviceInfo = NULL;
NTSTATUS Status;
MMRESULT mmr;
PAGED_CODE();
if (!NT_SUCCESS(kmxlAllocDeviceInfo(&DeviceInfo, DeviceInterface, fdwDetails,TAG_AudD_DEVICEINFO))) {
return MMSYSERR_NOMEM;
}
Status = kmxlGetControlDetailsHandler( pWdmaContext, DeviceInfo, pmxcd, pmxcd->paDetails );
mmr = DeviceInfo->mmr;
AudioFreeMemory_Unknown( &DeviceInfo);
return mmr;
}
///////////////////////////////////////////////////////////////////////
//
// mixerGetLineControls
//
//
MMRESULT
WINAPI
kmxlGetLineControls(
PWDMACONTEXT pWdmaContext,
PCWSTR DeviceInterface,
LPMIXERLINECONTROLS pmxlc,
DWORD fdwControls
)
{
LPDEVICEINFO DeviceInfo = NULL;
NTSTATUS Status;
MMRESULT mmr;
PAGED_CODE();
if (!NT_SUCCESS(kmxlAllocDeviceInfo(&DeviceInfo, DeviceInterface, fdwControls,TAG_AudD_DEVICEINFO))) {
return MMSYSERR_NOMEM;
}
Status = kmxlGetLineControlsHandler( pWdmaContext, DeviceInfo, pmxlc, pmxlc->pamxctrl );
mmr = DeviceInfo->mmr;
AudioFreeMemory_Unknown(&DeviceInfo);
return mmr;
}
///////////////////////////////////////////////////////////////////////
//
// mixerGetLineInfo
//
//
MMRESULT
WINAPI
kmxlGetLineInfo(
PWDMACONTEXT pWdmaContext,
PCWSTR DeviceInterface,
LPMIXERLINE pmxl,
DWORD fdwInfo
)
{
LPDEVICEINFO DeviceInfo = NULL;
NTSTATUS Status;
MMRESULT mmr;
PAGED_CODE();
if (!NT_SUCCESS(kmxlAllocDeviceInfo(&DeviceInfo, DeviceInterface, fdwInfo, TAG_AudD_DEVICEINFO))) {
return MMSYSERR_NOMEM;
}
Status = kmxlGetLineInfoHandler( pWdmaContext, DeviceInfo, pmxl );
mmr = DeviceInfo->mmr;
AudioFreeMemory_Unknown(&DeviceInfo);
return mmr;
}
///////////////////////////////////////////////////////////////////////
//
// mixerSetControlDetails
//
//
MMRESULT
WINAPI
kmxlSetControlDetails(
PWDMACONTEXT pWdmaContext,
PCWSTR DeviceInterface,
LPMIXERCONTROLDETAILS pmxcd,
DWORD fdwDetails
)
{
LPDEVICEINFO DeviceInfo = NULL;
NTSTATUS Status;
MMRESULT mmr;
PAGED_CODE();
if (!NT_SUCCESS(kmxlAllocDeviceInfo(&DeviceInfo, DeviceInterface, fdwDetails, TAG_AudD_DEVICEINFO))) {
return MMSYSERR_NOMEM;
}
Status =
kmxlSetControlDetailsHandler( pWdmaContext,
DeviceInfo,
pmxcd,
pmxcd->paDetails,
MIXER_FLAG_PERSIST
);
mmr = DeviceInfo->mmr;
AudioFreeMemory_Unknown(&DeviceInfo);
return mmr;
}