|
|
//---------------------------------------------------------------------------
//
// 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; }
|