// ****************************************************************************
//
//		CEchoGals_mixer.cpp
//
//		Implementation file for the CEchoGals driver class (mixer functions).
//		Set editor tabs to 3 for your viewing pleasure.
//
// ----------------------------------------------------------------------------
//
// This file is part of Echo Digital Audio's generic driver library.
// Copyright Echo Digital Audio Corporation (c) 1998 - 2005
// All rights reserved
// www.echoaudio.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// ****************************************************************************
 
#include "CEchoGals.h"
 
#undef ECHO_DEBUGPRINTF
#define ECHO_DEBUGPRINTF(x)
 
/****************************************************************************
 
	CEchoGals mixer client management
 
 ****************************************************************************/
 
//===========================================================================
//
// Open the mixer - create a mixer client structure for this client and 
// return the cookie.  The cookie uniquely identifies this client to the 
// mixer driver. 
//
// Valid cookies are non-zero.  If you get a zero cookie, the open failed
// somehow.
//
// Clients can change mixer controls without calling OpenMixer first; it just
// means that they can't track control changes made by other clients.
//
//===========================================================================
 
ECHOSTATUS CEchoGals::OpenMixer(NUINT &Cookie)
{
	ECHO_MIXER_CLIENT *pTemp;
 
	if (m_fMixerDisabled)
		return ECHOSTATUS_MIXER_DISABLED;
 
	//
	// If the cookie is non-zero, then use the specified value
	//
	if (0 != Cookie)
	{
		pTemp = m_pMixerClients;
		while (pTemp != NULL)
		{
			if (Cookie == pTemp->Cookie)
			{
				ECHO_DEBUGPRINTF(("CEchoGals::OpenMixer - cookie 0x%lx already in use\n",
										Cookie));
				return ECHOSTATUS_BAD_COOKIE;
			}
			
			pTemp = pTemp->pNext;
		}
	}
	else
	{
		//
		// Make a new cookie
		//
		ULONGLONG ullTime;
 
		m_pOsSupport->OsGetSystemTime(&ullTime);
		Cookie = (NUINT) ullTime;
		if (0 == Cookie)
			Cookie = 1;
		
		//
		// Look through the existing mixer client list to ensure that this
		// cookie is unique.
		//
		pTemp = m_pMixerClients;
		while (pTemp != NULL)
		{
			if (Cookie == pTemp->Cookie)
			{
				//
				// Oops, someone is already using this cookie.  Increment 
				// the new cookie and try again.
				//
				Cookie++;
				pTemp = m_pMixerClients;			
			}
		
			pTemp = pTemp->pNext;
		}
		
	}
	
 
	//
	// Allocate the mixer client structure
	//
	ECHO_MIXER_CLIENT *pClient = NULL;
	ECHOSTATUS			Status;
	
	Status = OsAllocateNonPaged(sizeof(ECHO_MIXER_CLIENT),(void **) &pClient);
	if (NULL == pClient)
	{
		Cookie = 0;
		return Status;		
	}
 
	
	//
	// Store the new cookie and the new mixer client
	//
	pClient->Cookie = Cookie;
	pClient->pNext = m_pMixerClients;
	m_pMixerClients = pClient;
	
	return ECHOSTATUS_OK;
	
}	// OpenMixer
 
 
//===========================================================================
//
// Find a mixer client that matches a cookie
//
//===========================================================================
 
ECHO_MIXER_CLIENT *CEchoGals::GetMixerClient(NUINT Cookie)
{
	ECHO_MIXER_CLIENT *pTemp;
	
	pTemp = m_pMixerClients;
	while (NULL != pTemp)
	{
		if (Cookie == pTemp->Cookie)
			break;
			
		pTemp = pTemp->pNext;
	}
	
	return pTemp;
	
}	// GetMixerClient
 
 
//===========================================================================
//
// Close the mixer - free the mixer client structure
//
//===========================================================================
 
ECHOSTATUS CEchoGals::CloseMixer(NUINT Cookie)
{
	//
	//	Search the linked list and remove the client that matches the cookie
	// 
	ECHO_MIXER_CLIENT *pTemp;
	
	pTemp = m_pMixerClients;
	if (NULL == pTemp)
		return ECHOSTATUS_BAD_COOKIE;
		
	//
	// Head of the list?
	//		
	if (pTemp->Cookie == Cookie)
	{
		m_pMixerClients = pTemp->pNext;
		OsFreeNonPaged(pTemp);
		
		return ECHOSTATUS_OK;
	}
		
	//
	// Not the head of the list; search the list
	//
	while (NULL != pTemp->pNext)
	{
		if (Cookie == pTemp->pNext->Cookie)
		{
			ECHO_MIXER_CLIENT *pDeadClient;
			
			pDeadClient = pTemp->pNext;
			pTemp->pNext = pDeadClient->pNext;
			OsFreeNonPaged(pDeadClient);
			
			return ECHOSTATUS_OK;
		}
			
		pTemp = pTemp->pNext;
	}
 
	//
	// No soup for you!
	//
	return ECHOSTATUS_BAD_COOKIE;
 
} // CloseMixer	
 
 
//===========================================================================
//
// IsMixerOpen - returns true if at least one client has the mixer open
//
//===========================================================================
 
BOOL CEchoGals::IsMixerOpen()
{
	if (NULL == m_pMixerClients)
		return FALSE;
		
	return TRUE;
	
}	// IsMixerOpen
 
 
//===========================================================================
//
// This function is called when a mixer control changes; add the change
//	to the queue for each client.
//
// Here's what the wCh1 and wCh2 parameters represent, based on the wType
// parameter:
//
// wType				wCh1				wCh2
// -----				----				----
// ECHO_BUS_OUT	Output bus		Ignored
// ECHO_BUS_IN		Input bus		Ignored
// ECHO_PIPE_OUT	Output pipe		Output bus
// ECHO_MONITOR	Input bus		Output bus
//
// ECHO_PIPE_IN is not used right now.
//
//===========================================================================
 
ECHOSTATUS CEchoGals::MixerControlChanged
(
	WORD	wType,		// One of the ECHO_CHANNEL_TYPES
	WORD  wParameter, // One of the MXN_* values
	WORD 	wCh1,			// Depends on the wType value
	WORD  wCh2			// Also depends on wType value
)
{
	ECHO_MIXER_CLIENT *pClient = m_pMixerClients;
	PMIXER_NOTIFY		pNotify;
	
	if (m_fMixerDisabled)
		return ECHOSTATUS_MIXER_DISABLED;
	
	//
	// Go through all the clients and store this control change
	//
	while (NULL != pClient)
	{
		//
		// Search the circular buffer for this client and see if 
		// this control change is already stored
		//
		DWORD				dwIndex,dwCount;
		BOOL				fFound;
		
		dwCount = pClient->dwCount;
		dwIndex = pClient->dwTail;
		fFound = FALSE;
		while (dwCount > 0)
		{
			pNotify = pClient->Notifies + dwIndex;
			if (	(pNotify->wType == wType) &&
					(pNotify->wParameter == wParameter) &&
					(pNotify->u.wPipeOut == wCh1) && 	// can use any union member her
					(pNotify->wBusOut == wCh2))
			{
				//
				// Found this notify already in the circular buffer 
				//
				fFound = TRUE;
				break;
			}
			dwIndex++;
			dwIndex &= MAX_MIXER_NOTIFIES - 1;
			dwCount--;
		}
		
		//
		// If the notify was not found, add this notify to the circular buffer if 
		// there is enough room.
		//
		if ( 	(FALSE == fFound) &&
				(pClient->dwCount != MAX_MIXER_NOTIFIES))
		{
			pNotify = pClient->Notifies + pClient->dwHead;
			
			pNotify->wType 		= wType;
			pNotify->wParameter 	= wParameter;
			
			if (ECHO_BUS_OUT == wType)
			{
				pNotify->u.wPipeOut = ECHO_CHANNEL_UNUSED;
				pNotify->wBusOut = wCh1;
			}
			else
			{
				pNotify->u.wPipeOut	= wCh1;		// can use any union member here also
				pNotify->wBusOut	= wCh2;
			}
			
			pClient->dwCount += 1;
			pClient->dwHead = (pClient->dwHead + 1) & (MAX_MIXER_NOTIFIES - 1);
		}
		
		pClient = pClient->pNext;
	}
	
	return ECHOSTATUS_OK;
	
}	// MixerControlChanged
 
 
//===========================================================================
//
// This method is called when a client wants to know what controls have 
// changed.
//
//===========================================================================
 
ECHOSTATUS CEchoGals::GetControlChanges
(
	PMIXER_MULTI_NOTIFY	pNotifies,
	NUINT	MixerCookie
)
{
	//
	// Match the cookie
	//
	ECHO_MIXER_CLIENT *pClient = GetMixerClient(MixerCookie);
	
	if (NULL == pClient)
	{
		pNotifies->dwCount = 0;
		return ECHOSTATUS_BAD_COOKIE;		
	}
	
	//
	// Copy mixer notifies
	//
	PMIXER_NOTIFY	pDest,pSrc;
	DWORD				dwNumClientNotifies,dwNumReturned;
	
	dwNumClientNotifies = pNotifies->dwCount;
	pDest = pNotifies->Notifies;
	dwNumReturned = 0;
	while ( (dwNumClientNotifies > 0) && (pClient->dwCount > 0))
	{
		pSrc = pClient->Notifies + pClient->dwTail;
	
		OsCopyMemory(pDest,pSrc,sizeof(MIXER_NOTIFY));
		
		pDest++;
 
		pClient->dwTail = (pClient->dwTail + 1) & (MAX_MIXER_NOTIFIES - 1);
		pClient->dwCount -= 1;
 
		dwNumClientNotifies--;
 
		dwNumReturned++;
	}
	
	pNotifies->dwCount = dwNumReturned;
	
	return ECHOSTATUS_OK;
 
}	// GetControlChanges
 
 
 
 
/****************************************************************************
 
	CEchoGals mixer control
 
 ****************************************************************************/
 
//===========================================================================
//
// Process mixer function - service a single mixer function
//
//===========================================================================
 
ECHOSTATUS CEchoGals::ProcessMixerFunction
(
	PMIXER_FUNCTION	pMixerFunction,
	INT32 &					iRtnDataSz
)
{
	ECHOSTATUS			Status = ECHOSTATUS_OK;
	
	if (m_fMixerDisabled)
		return ECHOSTATUS_MIXER_DISABLED;
	
	switch ( pMixerFunction->iFunction )
	{
		case MXF_GET_CAPS :
			Status = GetCapabilities( &pMixerFunction->Data.Capabilities );
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_GET_CAPS  Status %ld\n", Status) );
			break;
 
		case MXF_GET_LEVEL :
			Status = GetAudioLineLevel( pMixerFunction);
			/*
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_GET_LEVEL  Status %ld\n", Status) );
			*/
			break;
 
		case MXF_SET_LEVEL :
			Status = SetAudioLineLevel( pMixerFunction);
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_SET_LEVEL  Status %ld\n", Status) );
			break;
 
		case MXF_GET_NOMINAL :
			Status = GetAudioNominal( pMixerFunction);
			/*
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_GET_NOMINAL  Status %ld\n", Status) );
			*/
			break;
 
		case MXF_SET_NOMINAL :
			Status = SetAudioNominal( pMixerFunction);
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_SET_NOMINAL  Status %ld\n", Status) );
			break;
 
		case MXF_GET_MONITOR :
			Status = GetAudioMonitor( pMixerFunction->Channel.wChannel,
											  pMixerFunction->Data.Monitor.wBusOut,
											  pMixerFunction->Data.Monitor.Data.iLevel );
			/*	  
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_GET_MONITOR  Status %ld\n", Status) );
			*/
			break;
 
		case MXF_SET_MONITOR :
			Status = SetAudioMonitor( pMixerFunction->Channel.wChannel,
											  pMixerFunction->Data.Monitor.wBusOut,
											  pMixerFunction->Data.Monitor.Data.iLevel );
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_SET_MONITOR  Status %ld\n", Status) );
			break;
 
		case MXF_GET_CLOCK_DETECT :
			Status = GetInputClockDetect( pMixerFunction->Data.dwClockDetectBits );
			break;
	
		case MXF_GET_INPUT_CLOCK :
			Status = GetInputClock( pMixerFunction->Data.wClock );
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_GET_INPUT_CLOCK  Status %ld\n", Status) );
			break;			
 
		case MXF_SET_INPUT_CLOCK :
			Status = SetInputClock( pMixerFunction->Data.wClock );
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_SET_INPUT_CLOCK  Status %ld\n", Status) );
			break;			
 
 
		case MXF_GET_OUTPUT_CLOCK :
			Status = GetOutputClock( pMixerFunction->Data.wClock );
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_GET_OUTPUT_CLOCK  Status %ld\n", Status) );
			break;			
 
		case MXF_SET_OUTPUT_CLOCK :
			Status = SetOutputClock( pMixerFunction->Data.wClock );
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_SET_OUTPUT_CLOCK  Status %ld\n", Status) );
			break;			
 
 
		case MXF_GET_METERS :
		
			if (NULL != GetDspCommObject())
			{
				Status = GetDspCommObject()->
								GetAudioMeters( &pMixerFunction->Data.Meters );				
			}
			else
			{
				Status = ECHOSTATUS_DSP_DEAD;
			}
 
			//ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
			// 						 "MXF_GET_METERS  Status %ld\n", Status) );
			break;
 
		case MXF_GET_METERS_ON :
			Status = GetMetersOn( pMixerFunction->Data.bMetersOn );
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_SET_METERS  Status %ld\n", Status) );
			break;
 
		case MXF_SET_METERS_ON :
			Status = SetMetersOn( pMixerFunction->Data.bMetersOn );
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_SET_METERS_ON  Status %ld\n", Status) );
			break;
 
		case MXF_GET_PROF_SPDIF :
			if ( ECHOSTATUS_DSP_DEAD == IsProfessionalSpdif() )
			{
				Status = ECHOSTATUS_DSP_DEAD;				
			}
			else
			{
				pMixerFunction->Data.bProfSpdif = IsProfessionalSpdif();
			}
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_GET_PROF_SPDIF  Pro S/PDIF: 0x%x  Status %ld\n", 
									 pMixerFunction->Data.bProfSpdif,
									 Status) );
			break;
 
		case MXF_SET_PROF_SPDIF :
			SetProfessionalSpdif( pMixerFunction->Data.bProfSpdif );
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_SET_PROF_SPDIF  Pro S/PDIF: 0x%x  Status %ld\n", 
									 pMixerFunction->Data.bProfSpdif,
									 Status) );
			break;
 
		case MXF_GET_MUTE :
			Status = GetAudioMute(pMixerFunction);
			/*
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_GET_MUTE  Status %ld\n", Status) );
			*/
			break;
 
		case MXF_SET_MUTE :
			Status = SetAudioMute(pMixerFunction);
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_SET_MUTE  Status %ld\n", Status) );
			break;
 
		case MXF_GET_MONITOR_MUTE :
			Status = 
				GetAudioMonitorMute( pMixerFunction->Channel.wChannel,
										   pMixerFunction->Data.Monitor.wBusOut,
										   pMixerFunction->Data.Monitor.Data.bMuteOn );
			/*
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_GET_MONITOR_MUTE  Status %ld\n", Status) );
			*/
			break;
 
		case MXF_SET_MONITOR_MUTE :
			Status = 
				SetAudioMonitorMute( pMixerFunction->Channel.wChannel,
										   pMixerFunction->Data.Monitor.wBusOut,
										   pMixerFunction->Data.Monitor.Data.bMuteOn );
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_SET_MONITOR_MUTE  Status %ld\n", Status) );
			break;
 
		case MXF_GET_MONITOR_PAN :
			Status = 
				GetAudioMonitorPan( pMixerFunction->Channel.wChannel,
										  pMixerFunction->Data.Monitor.wBusOut,
										  pMixerFunction->Data.Monitor.Data.iPan);
			/*
 
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_GET_MONITOR_PAN  Status %ld\n", Status) );
			*/									 
									 
			break;
		case MXF_SET_MONITOR_PAN :
			Status = 
				SetAudioMonitorPan( pMixerFunction->Channel.wChannel,
										  pMixerFunction->Data.Monitor.wBusOut,
										  pMixerFunction->Data.Monitor.Data.iPan );
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_SET_MONITOR_PAN  Status %ld\n", Status) );
			break;
 
		case MXF_GET_FLAGS :
			pMixerFunction->Data.wFlags = GetFlags();
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_GET_FLAGS 0x%x  Status %ld\n",
									 pMixerFunction->Data.wFlags,
									 Status) );
			break;
		case MXF_SET_FLAGS :
			SetFlags( pMixerFunction->Data.wFlags );
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_SET_FLAGS 0x%x  Status %ld\n",
									 pMixerFunction->Data.wFlags,
									 Status) );
			break;
		case MXF_CLEAR_FLAGS :
			ClearFlags( pMixerFunction->Data.wFlags );
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_CLEAR_FLAGS 0x%x  Status %ld\n",
									 pMixerFunction->Data.wFlags,
									 Status) );
			break;
 
		case MXF_GET_SAMPLERATE_LOCK :
			GetAudioLockedSampleRate( pMixerFunction->Data.dwLockedSampleRate );
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_GET_SAMPLERATE_LOCK 0x%lx  Status %ld\n",
									 pMixerFunction->Data.dwLockedSampleRate,
									 Status) );
			break;
 
		case MXF_SET_SAMPLERATE_LOCK :
			SetAudioLockedSampleRate( pMixerFunction->Data.dwLockedSampleRate );
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_SET_SAMPLERATE_LOCK 0x%lx  Status %ld\n",
									 pMixerFunction->Data.dwLockedSampleRate,
									 Status) );
			break;
 
		case MXF_GET_SAMPLERATE :
		
			GetAudioSampleRate( &pMixerFunction->Data.dwSampleRate );
 
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_GET_SAMPLERATE 0x%lx  Status %ld\n",
									 pMixerFunction->Data.dwSampleRate,
									 Status) );
			break;
 
#ifdef MIDI_SUPPORT			
 
		case MXF_GET_MIDI_IN_ACTIVITY :
			pMixerFunction->Data.bMidiActive = m_MidiIn.IsActive();
 
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_GET_MIDI_IN_ACTIVITY %s "
									 "Status %ld\n",
									 ( pMixerFunction->Data.bMidiActive ) 
										? "ACTIVE" : "INACTIVE",
									 Status) );
			break;
 
 
		case MXF_GET_MIDI_OUT_ACTIVITY :
			pMixerFunction->Data.bMidiActive =
				GetDspCommObject()->IsMidiOutActive();
 
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_GET_MIDI_OUT_ACTIVITY %s "
									 "Status %ld\n",
									 ( pMixerFunction->Data.bMidiActive ) 
										? "ACTIVE" : "INACTIVE",
									 Status) );
			break;
 
#endif // MIDI_SUPPORT
 
 
		case MXF_GET_DIGITAL_MODE :
			Status = ECHOSTATUS_OK;
			pMixerFunction->Data.iDigMode = GetDigitalMode();
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_GET_DIGITAL_MODE %s "
									 "Status %ld\n",
									 ( DIGITAL_MODE_SPDIF_RCA == 
											pMixerFunction->Data.iDigMode )
										? "S/PDIF RCA" 
										: ( DIGITAL_MODE_SPDIF_OPTICAL == 
											pMixerFunction->Data.iDigMode )
												? "S/PDIF Optical" : "ADAT",
									 Status) );
			break;
 
 
		case MXF_SET_DIGITAL_MODE :
			Status = SetDigitalMode( (BYTE) pMixerFunction->Data.iDigMode );
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_SET_DIGITAL_MODE %s "
									 "Status %ld\n",
									 ( DIGITAL_MODE_SPDIF_RCA == 
											pMixerFunction->Data.iDigMode )
										? "S/PDIF RCA" 
										: ( DIGITAL_MODE_SPDIF_OPTICAL == 
											pMixerFunction->Data.iDigMode )
												? "S/PDIF Optical" : "ADAT",
									 Status) );
			break;
 
 
		case MXF_GET_PAN :
			Status = GetAudioPan( pMixerFunction);
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_GET_PAN  Status %ld\n", Status) );
			break;
 
		case MXF_SET_PAN :
			Status = SetAudioPan( pMixerFunction);
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_SET_PAN  Status %ld\n", Status) );
			break;
			
#ifdef DIGITAL_INPUT_AUTO_MUTE_SUPPORT
 
		case MXF_GET_DIG_IN_AUTO_MUTE	:
			Status =  GetDigitalInAutoMute( pMixerFunction );
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_GET_DIG_IN_AUTO_MUTE  Status %ld\n", Status) );
			break;
			
 
		case MXF_SET_DIG_IN_AUTO_MUTE	:
			Status = SetDigitalInAutoMute( pMixerFunction );
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "MXF_SET_DIG_IN_AUTO_MUTE  Status %ld\n", Status) );
			break;
 
#endif // DIGITAL_INPUT_AUTO_MUTE_SUPPORT
 
		case MXF_GET_AUDIO_LATENCY :
			GetAudioLatency( &(pMixerFunction->Data.Latency) );
			break;
			
#ifdef PHANTOM_POWER_CONTROL
		
		case MXF_GET_PHANTOM_POWER :
			GetPhantomPower( &(pMixerFunction->Data.fPhantomPower) );
			break;
			
		case MXF_SET_PHANTOM_POWER :
			SetPhantomPower( pMixerFunction->Data.fPhantomPower );
			break;
			
#endif
 
		default :
			iRtnDataSz = 0;
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
									 "Function %ld not supported\n",
									 pMixerFunction->iFunction) );
			return ECHOSTATUS_NOT_SUPPORTED;
	}
 
	pMixerFunction->RtnStatus = Status;
	iRtnDataSz = sizeof( MIXER_FUNCTION );
 
	return Status;
 
}	// ECHOSTATUS CEchoGals::ProcessMixerFunction
 
 
 
 
//===========================================================================
//
// Process multiple mixer functions
//
//===========================================================================
 
ECHOSTATUS CEchoGals::ProcessMixerMultiFunction
(
	PMIXER_MULTI_FUNCTION	pMixerFunctions,	// Request from mixer
	INT32 &							iRtnDataSz			// # bytes returned (if any)
)
{
	ECHOSTATUS			Status = ECHOSTATUS_NOT_SUPPORTED;
	PMIXER_FUNCTION	pMixerFunction;
	INT32					iRtn, nCard, i;
	
	if (m_fMixerDisabled)
		return ECHOSTATUS_MIXER_DISABLED;
 
	iRtnDataSz = sizeof( MIXER_MULTI_FUNCTION ) - sizeof( MIXER_FUNCTION );
	pMixerFunction = &pMixerFunctions->MixerFunction[ 0 ];
	nCard = pMixerFunction->Channel.wCardId;
	for ( i = 0; i < pMixerFunctions->iCount; i++ )
	{
		pMixerFunction = &pMixerFunctions->MixerFunction[ i ];
		if ( nCard != pMixerFunction->Channel.wCardId )
		{
			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerMultiFunction: "
									 "All functions MUST be for the same card "
									 "exp %ld act %d!\n",
									 nCard,
									 pMixerFunction->Channel.wCardId) );
			return ECHOSTATUS_NOT_SUPPORTED;
		}
 
		Status = ProcessMixerFunction(pMixerFunction,iRtn);
		iRtnDataSz += iRtn;
	}
 
	return Status;
 
}	// ECHOSTATUS CEchoGals::ProcessMixerMultiFunction
 
 
 
 
//===========================================================================
//
// Typically, if you are writing a console, you will be polling the driver
// to get the current peak and VU meters, clock detect bits, and 
// control changes.  GetPolledStuff will fill out an ECHO_POLLED_STUFF
// structure with all of those things.
//
//===========================================================================
 
ECHOSTATUS CEchoGals::GetPolledStuff
(
	ECHO_POLLED_STUFF *pPolledStuff,
	NUINT MixerCookie
)
{
	ECHO_MIXER_CLIENT *pClient;
	CDspCommObject *pDCO = GetDspCommObject();
	
	if (m_fMixerDisabled)
		return ECHOSTATUS_MIXER_DISABLED;
	
	if (NULL == pDCO)
		return ECHOSTATUS_DSP_DEAD;
		
	//
	// Fill out the non-client-specific portions of the struct
	//
	pDCO->GetAudioMeters(&(pPolledStuff->Meters));
	GetInputClockDetect(pPolledStuff->dwClockDetectBits);
	
	//
	// If there is a matching client, fill out the number
	// of notifies
	//
	pClient = GetMixerClient(MixerCookie);
	if (NULL == pClient)
		pPolledStuff->dwNumPendingNotifies = 0;
	else	
		pPolledStuff->dwNumPendingNotifies = pClient->dwCount;
	
	return ECHOSTATUS_OK;
	
}	// GetPolledStuff
 
 
//===========================================================================
//
//	Get the pan setting for an output pipe (virtual outputs only)
//
//===========================================================================
 
ECHOSTATUS CEchoGals::GetAudioPan
(
	PMIXER_FUNCTION	pMF
)
{
	WORD 			wPipe;
	WORD 			wBus;
	ECHOSTATUS 	Status;
	
	if ( 	(pMF->Channel.dwType != ECHO_PIPE_OUT) ||
			( !HasVmixer() ) )
		return ECHOSTATUS_INVALID_CHANNEL;
		
	wPipe = pMF->Channel.wChannel;
	wBus = pMF->Data.PipeOut.wBusOut;
	Status = m_PipeOutCtrl.GetPan(wPipe,
											wBus,
											pMF->Data.PipeOut.Data.iPan);
 
	return Status;
 
}	// ECHOSTATUS CEchoGals::GetAudioPan
 
 
//===========================================================================
//
//	Set the pan for an output pipe (virtual outputs only)
//
//===========================================================================
 
ECHOSTATUS CEchoGals::SetAudioPan
(
	PMIXER_FUNCTION	pMF
)
{
	WORD 			wPipe;
	WORD 			wBus;
	ECHOSTATUS 	Status;
	
	if ( 	(pMF->Channel.dwType != ECHO_PIPE_OUT) ||
			( !HasVmixer() ) )
		return ECHOSTATUS_INVALID_CHANNEL;
		
	wPipe = pMF->Channel.wChannel;
	wBus = pMF->Data.PipeOut.wBusOut;
	Status = m_PipeOutCtrl.SetPan(wPipe,
											wBus,
											pMF->Data.PipeOut.Data.iPan);
 
	return Status;
	
}	// ECHOSTATUS CEchoGals::SetAudioPan
 
 
 
 
/****************************************************************************
 
	CEchoGals clock control
	
	The input clock is the sync source - is the audio for this card running
	off of the internal clock, synced to word clock, etc.
	
	Output clock is only supported on Layla20 - Layla20 can transmit either
	word or super clock.
 
 ****************************************************************************/
 
//===========================================================================
//
// Get input and output clocks - just return the stored value
//
//===========================================================================
 
ECHOSTATUS CEchoGals::GetInputClock(WORD &wClock)
{
	if ( NULL == GetDspCommObject() )
		return ECHOSTATUS_DSP_DEAD;
 
	wClock = GetDspCommObject()->GetInputClock();
	
	return ECHOSTATUS_OK;
}	
 
 
ECHOSTATUS CEchoGals::GetOutputClock(WORD &wClock)
{
	if ( NULL == GetDspCommObject() )
		return ECHOSTATUS_DSP_DEAD;
 
	wClock = GetDspCommObject()->GetOutputClock();
	
	return ECHOSTATUS_OK;
}	
 
 
//===========================================================================
//
// Set input and output clocks - pass it down to the comm page and
// store the control change.
//
//===========================================================================
 
ECHOSTATUS CEchoGals::SetInputClock(WORD wClock)
{
	ECHOSTATUS Status;
 
	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
		return ECHOSTATUS_DSP_DEAD;
 
	ECHO_DEBUGPRINTF( ("CEchoGals::SetInputClock: ") );
	
	Status = GetDspCommObject()->SetInputClock( wClock );
 
	if (ECHOSTATUS_OK == Status)
	{
		MixerControlChanged( ECHO_NO_CHANNEL_TYPE,
									MXN_INPUT_CLOCK);
	}
	return Status;
 
}	// SetInputClock
 
 
ECHOSTATUS CEchoGals::SetOutputClock(WORD wClock)
{
	ECHOSTATUS Status;
 
	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
		return ECHOSTATUS_DSP_DEAD;
 
	ECHO_DEBUGPRINTF( ("CEchoGals::SetOutputClock: ") );
	
	Status = GetDspCommObject()->SetOutputClock( wClock );
 
	if (ECHOSTATUS_OK == Status)
	{
		MixerControlChanged( ECHO_NO_CHANNEL_TYPE,
									MXN_OUTPUT_CLOCK);
	}
	return Status;
 
}	
 
 
//===========================================================================
//
// Get the currently detected clock bits - default method.  Overridden by 
// derived card classes.
//
//===========================================================================
 
ECHOSTATUS CEchoGals::GetInputClockDetect(DWORD &dwClockDetectBits)
{
	dwClockDetectBits = ECHO_CLOCK_INTERNAL;
 
	return ECHOSTATUS_OK;
}	
 
 
//===========================================================================
//
// Set the locked sample rate
//
//===========================================================================
 
ECHOSTATUS CEchoGals::SetAudioLockedSampleRate
(
	DWORD		dwSampleRate
)
{
	ECHOSTATUS	Status;
							 
	Status = QueryAudioSampleRate( dwSampleRate );
	if ( ECHOSTATUS_OK != Status )
		return Status;
 
	if (0 != (ECHOGALS_FLAG_SAMPLE_RATE_LOCKED & GetFlags()))
	{
		GetDspCommObject()->SetSampleRate( dwSampleRate );
		m_dwSampleRate = dwSampleRate;
	}
 
	m_dwLockedSampleRate = dwSampleRate;
 
	return ECHOSTATUS_OK;
 
}	// ECHOSTATUS CEchoGals::SetAudioLockedSampleRate
 
 
//===========================================================================
//
// Get the locked sample rate
//
//===========================================================================
 
ECHOSTATUS CEchoGals::GetAudioLockedSampleRate
(
	DWORD	   &dwSampleRate
)
{
	dwSampleRate = m_dwLockedSampleRate;
		
	return ECHOSTATUS_OK;
	
}	// ECHOSTATUS CEchoGals::GetAudioLockedSampleRate
 
 
 
#ifdef DIGITAL_INPUT_AUTO_MUTE_SUPPORT
 
//===========================================================================
//
// Get the digital input auto mute flag from the comm page
//
//===========================================================================
 
ECHOSTATUS CEchoGals::GetDigitalInAutoMute(PMIXER_FUNCTION pMixerFunction)
{
	BOOL fAutoMute;
	
	if (0 == (m_wFlags & ECHOGALS_ROFLAG_DIGITAL_IN_AUTOMUTE))
	{
		pMixerFunction->Data.fDigitalInAutoMute = FALSE;
		return ECHOSTATUS_NOT_SUPPORTED;
	}
 
	GetDspCommObject()->GetDigitalInputAutoMute(	fAutoMute );
	pMixerFunction->Data.fDigitalInAutoMute = fAutoMute;
	
	return ECHOSTATUS_OK;		
	
} // GetDigitalInAutoMute
 
 
//===========================================================================
//
// Set the digital input auto mute flag
//
//===========================================================================
 
ECHOSTATUS CEchoGals::SetDigitalInAutoMute(PMIXER_FUNCTION pMixerFunction)
{
	BOOL fAutoMute;
	
	if (0 == (m_wFlags & ECHOGALS_ROFLAG_DIGITAL_IN_AUTOMUTE))
		return ECHOSTATUS_NOT_SUPPORTED;
	
	fAutoMute = pMixerFunction->Data.fDigitalInAutoMute;
	GetDspCommObject()->SetDigitalInputAutoMute( fAutoMute );	
	
	return ECHOSTATUS_OK;		
	
} // SetDigitalInAutoMute
 
#endif // DIGITAL_INPUT_AUTO_MUTE_SUPPORT
 
 
//===========================================================================
//
// Get the gain for an output bus, input bus, or output pipe.
//
// Gain levels are in units of dB * 256.
//
//===========================================================================
 
ECHOSTATUS CEchoGals::GetAudioLineLevel
(
	PMIXER_FUNCTION	pMF
)
{
	WORD 			wPipe;
	WORD 			wBus;
	ECHOSTATUS 	Status;
 
	switch (pMF->Channel.dwType)
	{
		case ECHO_BUS_OUT :
			
			wBus = pMF->Channel.wChannel;
			
			if (wBus < GetNumBussesOut())
			{
				pMF->Data.iLevel = m_BusOutLineLevels[wBus].GetGain();				
				Status = ECHOSTATUS_OK;
			}
			else
			{
				Status = ECHOSTATUS_INVALID_CHANNEL;
			}
 
			break;
 
		case ECHO_BUS_IN :
 
			wBus = pMF->Channel.wChannel;
			if (wBus < GetNumBussesIn())
			{
				pMF->Data.iLevel = m_BusInLineLevels[wBus].GetGain();				
				Status = ECHOSTATUS_OK;
			}
			else
			{
				Status = ECHOSTATUS_INVALID_CHANNEL;
			}
			break;
		
		case ECHO_PIPE_OUT :
		
			wPipe = pMF->Channel.wChannel;
			wBus = pMF->Data.PipeOut.wBusOut;
 			Status = m_PipeOutCtrl.GetGain(	wPipe,
														wBus,
														pMF->Data.PipeOut.Data.iLevel);
			break;
 
		default:
			Status = ECHOSTATUS_INVALID_PARAM;
			break;
	}
 
	return Status;
 
}	// ECHOSTATUS CEchoGals::GetAudioLineLevel
 
 
//===========================================================================
//
// Utility function to check that a setting is within the correct range.
//
//===========================================================================
 
ECHOSTATUS CheckSetting(INT32 iValue,INT32 iMin,INT32 iMax)
{
	if ( (iValue > iMax) || (iValue < iMin))
		return ECHOSTATUS_INVALID_PARAM;
	
	return ECHOSTATUS_OK;	
		
}	// CheckSetting
 
 
//===========================================================================
//
// Set the gain for an output bus, input bus, or output pipe.
//
// Gain levels are in units of dB * 256.
//
//===========================================================================
 
ECHOSTATUS CEchoGals::SetAudioLineLevel
(
	PMIXER_FUNCTION	pMF
)
{
	WORD 			wPipe;
	WORD 			wBus;
	ECHOSTATUS 	Status;
	INT32 			iLevel;
 
	switch (pMF->Channel.dwType)
	{
		case ECHO_BUS_OUT :
 
			wBus = pMF->Channel.wChannel;
			iLevel = pMF->Data.iLevel;
			
			Status = CheckSetting(iLevel,ECHOGAIN_MINOUT,ECHOGAIN_MAXOUT);
			if (ECHOSTATUS_OK != Status)
				break;
			
			Status = m_BusOutLineLevels[wBus].SetGain(iLevel);
			break;
			
		case ECHO_BUS_IN :
		
			wBus = pMF->Channel.wChannel;
			iLevel = pMF->Data.iLevel;
 
			Status = CheckSetting(iLevel,ECHOGAIN_MININP,ECHOGAIN_MAXINP);
			if (ECHOSTATUS_OK != Status)
				break;
 
			Status = m_BusInLineLevels[wBus].SetGain(iLevel);
			break;
			
		case ECHO_PIPE_OUT :
 
			wPipe = pMF->Channel.wChannel;
			wBus = pMF->Data.PipeOut.wBusOut;
			iLevel = pMF->Data.PipeOut.Data.iLevel;
 
			Status = CheckSetting(iLevel,ECHOGAIN_MINOUT,ECHOGAIN_MAXOUT);
			if (ECHOSTATUS_OK != Status)
				break;
 
 			Status = m_PipeOutCtrl.SetGain(	wPipe,
														wBus,
														iLevel);
			break;
			
		default:
			Status = ECHOSTATUS_INVALID_PARAM;
			break;
	}
 
	return Status;
	
}	// ECHOSTATUS CEchoGals::SetAudioLineLevel
 
 
//===========================================================================
//
// Get the nominal level for an output or input bus.  The nominal level is
// also referred to as the +4/-10 switch.
//
//===========================================================================
 
ECHOSTATUS CEchoGals::GetAudioNominal
(
	PMIXER_FUNCTION	pMF
)
{
	BYTE					byNominal;
	ECHOSTATUS			Status;
	CDspCommObject *	pDspCommObj = GetDspCommObject();
	WORD					wCh;
 
	if ( NULL == pDspCommObj || pDspCommObj->IsBoardBad() )
		return ECHOSTATUS_DSP_DEAD;
		
	switch (pMF->Channel.dwType)
	{
		case ECHO_BUS_OUT :
			wCh = pMF->Channel.wChannel;
			break;
			
		case ECHO_BUS_IN :
			wCh = pMF->Channel.wChannel + GetNumBussesOut();
			break;
		
		default :
			return ECHOSTATUS_INVALID_CHANNEL;
	}
		
	Status = pDspCommObj->GetNominalLevel( wCh, &byNominal );
 
	if ( ECHOSTATUS_OK != Status )
		return Status;
 
	pMF->Data.iNominal = ( byNominal ) ? -10 : 4;
 
	return ECHOSTATUS_OK;
}	// ECHOSTATUS CEchoGals::GetAudioNominal
 
 
//===========================================================================
//
// Set the nominal level for an output or input bus.  The nominal level is
// also referred to as the +4/-10 switch.
//
//===========================================================================
 
ECHOSTATUS CEchoGals::SetAudioNominal
(
	PMIXER_FUNCTION	pMF
)
{
	ECHOSTATUS	Status;
	WORD			wCh;
	INT32			iNominal;
 
	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
		return ECHOSTATUS_DSP_DEAD;
	
	switch (pMF->Channel.dwType)
	{
		case ECHO_BUS_OUT :
			wCh = pMF->Channel.wChannel;
			break;
			
		case ECHO_BUS_IN :
			wCh = pMF->Channel.wChannel + GetNumBussesOut();
			break;
		
		default :
			return ECHOSTATUS_INVALID_CHANNEL;
	}
 
	iNominal = pMF->Data.iNominal;
	
	if ((iNominal!= -10) && (iNominal != 4))
		return ECHOSTATUS_INVALID_PARAM;
	
	Status =
		GetDspCommObject()->SetNominalLevel( wCh,
														( iNominal == -10 ) );
 
	if ( ECHOSTATUS_OK != Status )
		return Status;
 
	Status = MixerControlChanged( (WORD) pMF->Channel.dwType,
											MXN_NOMINAL,
											pMF->Channel.wChannel);
	return Status;
 
}	// ECHOSTATUS CEchoGals::SetAudioNominal
 
 
//===========================================================================
//
// Set the mute for an output bus, input bus, or output pipe.
//
//===========================================================================
 
ECHOSTATUS CEchoGals::SetAudioMute
(
		PMIXER_FUNCTION	pMF
)
{
	WORD 			wPipe;
	WORD 			wBus;
	ECHOSTATUS 	Status;
	BOOL			bMute;
	
	switch (pMF->Channel.dwType)
	{
		case ECHO_BUS_OUT :
 
			wBus = pMF->Channel.wChannel;
			bMute = pMF->Data.bMuteOn;
			Status = m_BusOutLineLevels[wBus].SetMute(bMute);
			break;
			
		case ECHO_BUS_IN :
	
			wBus = pMF->Channel.wChannel;
			bMute = pMF->Data.bMuteOn;
			Status = m_BusInLineLevels[wBus].SetMute(bMute);
			break;
			
		case ECHO_PIPE_OUT :
 
			wPipe = pMF->Channel.wChannel;
			wBus = pMF->Data.PipeOut.wBusOut;
			bMute = pMF->Data.PipeOut.Data.bMuteOn;
 			Status = m_PipeOutCtrl.SetMute(	wPipe,
														wBus,
														bMute);
			break;
			
		default:
			Status = ECHOSTATUS_INVALID_PARAM;
			break;
	}
 
	return Status;
}	// ECHOSTATUS CEchoGals::SetAudioMute
 
 
//===========================================================================
//
// Get the mute for an output bus, input bus, or output pipe.
//
//===========================================================================
 
ECHOSTATUS CEchoGals::GetAudioMute
(
		PMIXER_FUNCTION	pMF
)
{
	WORD 			wPipe;
	WORD 			wBus;
	ECHOSTATUS 	Status;
 
	switch (pMF->Channel.dwType)
	{
		case ECHO_BUS_OUT :
			
			wBus = pMF->Channel.wChannel;
			
			if (wBus < GetNumBussesOut())
			{
				pMF->Data.bMuteOn = m_BusOutLineLevels[wBus].IsMuteOn();
				Status = ECHOSTATUS_OK;
			}
			else
			{
				Status = ECHOSTATUS_INVALID_CHANNEL;
			}
 
			break;
 
		case ECHO_BUS_IN :
 
			wBus = pMF->Channel.wChannel;
			
			if (wBus < GetNumBussesIn())
			{
				pMF->Data.bMuteOn = m_BusInLineLevels[wBus].IsMuteOn();
				Status = ECHOSTATUS_OK;
			}
			else
			{
				Status = ECHOSTATUS_INVALID_CHANNEL;
			}
			break;
		
		case ECHO_PIPE_OUT :
		
			wPipe = pMF->Channel.wChannel;
			wBus = pMF->Data.PipeOut.wBusOut;
 			Status = m_PipeOutCtrl.GetMute(	wPipe,
														wBus,
														pMF->Data.PipeOut.Data.bMuteOn);
			break;
 
		default:
			Status = ECHOSTATUS_INVALID_PARAM;
			break;
	}
 
	return Status;
 
}	// ECHOSTATUS CEchoGals::GetAudioMute
 
 
//===========================================================================
//
// Get the monitor gain for a single input bus mixed to a single output bus.
//
//===========================================================================
 
ECHOSTATUS CEchoGals::GetAudioMonitor
(
	WORD	wBusIn,
	WORD	wBusOut,
	INT32 &	iGain
)
{
	if ( wBusIn  >= GetNumBussesIn() ||
		  wBusOut >= GetNumBussesOut() )
	{
		return ECHOSTATUS_INVALID_INDEX;
	}
	
	//
	// Get the monitor value
	//
	m_MonitorCtrl.GetGain(wBusIn,wBusOut,iGain);
	
	return ECHOSTATUS_OK;
 
}	// ECHOSTATUS CEchoGals::GetAudioMonitor
 
 
//===========================================================================
//
// Set the monitor gain for a single input bus mixed to a single output bus.
//
//===========================================================================
 
ECHOSTATUS CEchoGals::SetAudioMonitor
(
	WORD	wBusIn,
	WORD	wBusOut,
	INT32	iGain
)
{
	ECHOSTATUS	Status;
 
	if ( wBusIn  >= GetNumBussesIn() ||
		  wBusOut >= GetNumBussesOut() )
	{
		return ECHOSTATUS_INVALID_INDEX;
	}
	
	Status = CheckSetting(iGain,ECHOGAIN_MINOUT,ECHOGAIN_MAXOUT);
	if (ECHOSTATUS_OK == Status)
	{
		//
		// Set the monitor gain
		// 
		m_MonitorCtrl.SetGain(wBusIn,wBusOut,iGain);
	}
 
	return Status;
	
}	// ECHOSTATUS CEchoGals::SetAudioMonitor
 
 
//===========================================================================
//
//	Helper functions for doing log conversions on pan values
//
// The parameter iNum is a fixed point 32 bit number in 16.16 format;
// that is, 16 bits of integer and 16 bits of decimal
// To convert a float number to fixed point, simply multiply by 2^16 and round
//
// Valid range for iNum is from +1.0 (0x10000) to 0.
//
//===========================================================================
 
#define FIXED_BASE		16							// 16 bits of fraction
#define FIXED_ONE_HALF	((INT32) 0x00008000)	// 0.5 in 16.16 format
#define COEFF_A2			((INT32) 0xffffa9ac)	// -.3372223
#define COEFF_A1			((INT32) 0x0000ff8a)	//  .9981958
#define COEFF_A0			((INT32) 0xffff5661)	// -.6626105
 
#define DB_CONVERT		0x60546		// 6.02... in 16.16
 
// Note use of double precision here to prevent overflow
static INT32 FixedMult( INT32 iNum1, INT32 iNum2 )
{
	LONGLONG llNum;
 
	llNum = (LONGLONG) iNum1 * (LONGLONG) iNum2;
 
	return (INT32) (llNum >> FIXED_BASE);
}	// INT32 FixedMult( INT32 iNum1, INT32 iNum2 )
 
 
static INT32 log2( INT32 iNum )
{
	INT32 iNumShifts;
	INT32 iTemp;
 
	// log2 is undefined for zero, so return -infinity (or close enough)
	if ( 0 == iNum )
		return ECHOGAIN_MUTED;
 
	// Step 1 - Normalize and save the number of shifts
	// Keep shifting iNum up until iNum > 0.5
	iNumShifts = 0;
	while ( iNum < FIXED_ONE_HALF )
	{
		iNumShifts++;
		iNum <<= 1;
	}
 
	// Step 2 - Calculate LOG2 by polynomial approximation.  8 bit accuracy.
	//
	// LOG2(x) = 4.0* (-.3372223 x*x + .9981958 x - .6626105)
	//                           a2             a1            a0
	// where  0.5 <= x < 1.0
	//
 
	// Compute polynomial sum
	iTemp = FixedMult( iNum, iNum );				// iTemp now has iNum squared
	iTemp = FixedMult( iTemp, COEFF_A2 );
	iTemp += FixedMult( iNum, COEFF_A1 );
	iTemp += COEFF_A0;
 
	// Multiply by four
	iTemp <<= 2;
 
	// Account for the normalize shifts
	iTemp -= ( iNumShifts << FIXED_BASE );
 
	return( iTemp );
}	// INT32 log2( INT32 iNum )
 
 
//
//	Convert pan value to Db X 256
//	Pan value is 0 - MAX_MIXER_PAN
//
INT32 PanToDb( INT32 iPan )
{
	if ( iPan >= ( MAX_MIXER_PAN - 1 ) )
		return( 0 );
	if ( iPan <= 1 )
		return( ECHOGAIN_MUTED );
	//
	//	Convert pan to 16.16
	//
	iPan = ( iPan << 16 ) / MAX_MIXER_PAN;
	//
	//	Take the log
	//
	iPan = log2( iPan );
	//
	// To convert to decibels*256, just multiply by the conversion factor
	//
	iPan = FixedMult( iPan << 8, DB_CONVERT );
	//
	// To round, add one half and then mask off the fractional bits
	//
	iPan = ( iPan + FIXED_ONE_HALF ) >> FIXED_BASE;
	return( iPan );
}	// INT32 PanToDb( INT32 iPan )
 
 
//===========================================================================
//
// Set the monitor pan
//
//	For this to work effectively, both the input and output channels must
//	both either be odd or even.  Thus even channel numbers are for the
//	left channel and odd channel numbers are for the right channel.
//	Pan values will be computed for both channels.
//
// iPan ranges from 0 (hard left) to MAX_MIXER_PAN (hard right)
//
//===========================================================================
 
ECHOSTATUS CEchoGals::SetAudioMonitorPan
(
	WORD	wBusIn,
	WORD	wBusOut,
	INT32	iPan						// New pan
)
{
	ECHOSTATUS Status;
 
	if ( wBusIn  >= GetNumBussesIn() ||
		  wBusOut >= GetNumBussesOut() )
	{
		return ECHOSTATUS_INVALID_INDEX;
	}
	
	Status = CheckSetting(iPan,0,MAX_MIXER_PAN);
	if (ECHOSTATUS_OK == Status)
	{
		//
		// Set the pan
		// 
		m_MonitorCtrl.SetPan(wBusIn,wBusOut,iPan);
	}
 
	return Status;
		
}	// ECHOSTATUS CEchoGals::SetAudioMonitorPan
 
 
//===========================================================================
//
// Get the monitor pan
//
//===========================================================================
 
ECHOSTATUS CEchoGals::GetAudioMonitorPan
(
	WORD	wBusIn,
	WORD	wBusOut,
	INT32 &	iPan						// Returns current pan (0 - MAX_MIXER_PAN)
)
{
	if ( wBusIn  >= GetNumBussesIn() ||
		  wBusOut >= GetNumBussesOut() )
	{
		return ECHOSTATUS_INVALID_INDEX;
	}
	
	//
	// Get the pan
	// 
	m_MonitorCtrl.GetPan(wBusIn,wBusOut,iPan);
	
	return ECHOSTATUS_OK;
 
}	// ECHOSTATUS CEchoGals::GetAudioMonitorPan
 
 
//===========================================================================
//
// Set the monitor mute
//
//===========================================================================
 
ECHOSTATUS CEchoGals::SetAudioMonitorMute
(
	WORD	wBusIn,
	WORD	wBusOut,
	BOOL	bMute						// New state
)
{
	if ( wBusIn  >= GetNumBussesIn() ||
		  wBusOut >= GetNumBussesOut() )
	{
		return ECHOSTATUS_INVALID_INDEX;
	}
	
	//
	// Set the mute
	// 
	m_MonitorCtrl.SetMute(wBusIn,wBusOut,bMute);
		
	return ECHOSTATUS_OK;
	
}	// ECHOSTATUS CEchoGals::SetAudioMonitorMute
 
 
//===========================================================================
//
// Get the monitor mute
//
//===========================================================================
 
ECHOSTATUS CEchoGals::GetAudioMonitorMute
(
	WORD	wBusIn,
	WORD	wBusOut,
	BOOL 	&bMute	  				// Returns current state
)
{
	if ( wBusIn  >= GetNumBussesIn() ||
		  wBusOut >= GetNumBussesOut() )
	{
		return ECHOSTATUS_INVALID_INDEX;
	}
	
	//
	// Get the mute
	// 
	m_MonitorCtrl.GetMute(wBusIn,wBusOut,bMute);
	
	return ECHOSTATUS_OK;
	
}	// ECHOSTATUS CEchoGals::GetAudioMonitorMute
 
 
//===========================================================================
//
// Set the S/PDIF output format to professional or consumer
//
//===========================================================================
 
void CEchoGals::SetProfessionalSpdif( BOOL bNewStatus )
{
	ECHO_DEBUGPRINTF(("CEchoGals::SetProfessionalSpdif %d\n",bNewStatus));
	
	if ( NULL != GetDspCommObject() )
	{
		GetDspCommObject()->SetProfessionalSpdif( bNewStatus );
		MixerControlChanged( ECHO_NO_CHANNEL_TYPE,
									MXN_SPDIF );
	}
}	// void CEchoGals::SetProfessionalSpdif( BOOL bNewStatus )
 
 
//===========================================================================
//
// Set driver flags
//
//	See ECHOGALS_FLAG_??? definitions in EchoGalsXface.h
//
//===========================================================================
 
ECHOSTATUS CEchoGals::SetFlags
(
	WORD	wFlags
)
{
	//
	// Mask off the read-only flags so they don't change
	//
	wFlags &= ECHOGALS_FLAG_WRITABLE_MASK;
 
	//
	// Set the flags & mark the flags as changed
	//
	m_wFlags |= wFlags;
 
	MixerControlChanged(	ECHO_NO_CHANNEL_TYPE,
								MXN_FLAGS );
 
	return ECHOSTATUS_OK;
 
}	// ECHOSTATUS CEchoGals::SetFlags
 
 
//===========================================================================
//
// Clear driver flags
//
//	See ECHOGALS_FLAG_??? definitions in EchoGalsXface.h
//
//===========================================================================
 
ECHOSTATUS CEchoGals::ClearFlags
(
	WORD	wFlags
)
{
	//
	// Mask off the read-only flags so they don't change
	//
	wFlags &= ECHOGALS_FLAG_WRITABLE_MASK;
 
	//
	// Clear the flags & mark the flags as changed
	//
	m_wFlags &= ~wFlags;
 
	MixerControlChanged(	ECHO_NO_CHANNEL_TYPE,
								MXN_FLAGS );
 
	return ECHOSTATUS_OK;
	
}	// ECHOSTATUS CEchoGals::ClearFlags
 
 
//===========================================================================
//
// Set the digital mode - currently for Gina24, Layla24, and Mona
//
//===========================================================================
 
ECHOSTATUS CEchoGals::SetDigitalMode
(
	BYTE byDigitalMode
)
{
	ECHOSTATUS	Status;
	BYTE			byPreviousDigitalMode;
 
	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
		return ECHOSTATUS_DSP_DEAD;
 
	if ( 0 == GetDspCommObject()->GetDigitalModes() )
		return ECHOSTATUS_DIGITAL_MODE_NOT_SUPPORTED;
 
	if ( TRUE == GetDspCommObject()->IsTransportActive() ) 
	{
		ECHO_DEBUGPRINTF( ( 	"CEchoGals::SetDigitalMode()  Cannot set the digital "
									"mode while transport is running\n"));
		return ECHOSTATUS_BUSY;
	}
	byPreviousDigitalMode = GetDspCommObject()->GetDigitalMode();
	Status = GetDspCommObject()->SetDigitalMode( byDigitalMode );
	MixerControlChanged( ECHO_NO_CHANNEL_TYPE,
								MXN_DIGITAL_MODE );
	MixerControlChanged( ECHO_NO_CHANNEL_TYPE, 
								MXN_INPUT_CLOCK );
 
	//
	//	If we successfully changed the digital mode from or to ADAT, then 
	//	make sure all output, input and monitor levels are updated by the
	//	DSP comm object.
	//
	if ( ECHOSTATUS_OK == Status &&
		  byPreviousDigitalMode != byDigitalMode &&
		  ( DIGITAL_MODE_ADAT == byPreviousDigitalMode ||
		    DIGITAL_MODE_ADAT == byDigitalMode ) )
	{
		WORD	i, j,wBus,wPipe;
 
		for ( i = 0; i < GetNumBussesIn(); i++ )
		{
			for ( j = 0; j < GetNumBussesOut(); j += 2 )
			{
				m_MonitorCtrl.SetGain(i,j,ECHOGAIN_UPDATE,FALSE);
			}
		}
 
		for ( wBus = 0; wBus < GetNumBussesOut(); wBus++)
		{
			for ( wPipe = 0; wPipe < GetNumPipesOut(); wPipe++)
			{
				m_PipeOutCtrl.SetGain(wPipe,wBus,ECHOGAIN_UPDATE,FALSE);
			}
		}
 
		for ( i = 0; i < GetNumBussesOut(); i++ )
		{
			m_BusOutLineLevels[ i ].SetGain(ECHOGAIN_UPDATE,FALSE);
		}
 
		for ( i = 0; i < GetNumBussesIn(); i++ )
		{
			m_BusInLineLevels[ i ].SetGain( ECHOGAIN_UPDATE );
		}
		
		//
		// Now set them all at once, since all the
		// fImmediate parameters were set to FALSE.  Do the output
		// bus _and_ the output pipe in case this is a vmixer card.
		//
		m_BusOutLineLevels[0].SetGain(ECHOGAIN_UPDATE,TRUE);
		m_PipeOutCtrl.SetGain(0,0,ECHOGAIN_UPDATE,TRUE);
	}
	
	//
	// If the card has just been put in ADAT mode, it is possible
	// that the locked sample rate is greater than 48KHz, which is not allowed
	// in ADAT mode.  If this happens, change the locked rate to 48.
	//
	if ( 	(DIGITAL_MODE_ADAT == byDigitalMode) &&
			(m_wFlags & ECHOGALS_FLAG_SAMPLE_RATE_LOCKED) &&
			(m_dwLockedSampleRate > 48000) )
	{
		m_dwLockedSampleRate = 48000;
	}
	
	return Status;
	
}	// ECHOSTATUS CEchoGals::SetDigitalMode( ... )
 
 
/*
 
The output bus gain controls aren't actually implemented in the hardware;
insted they are virtual controls created by the generic code.
 
The signal sent to an output bus is a mix of the monitors and output pipes
routed to that bus; the output bus gain is therefore implemented by tweaking
each appropriate monitor and output pipe gain.
 
*/
 
 
//===========================================================================
//
// Adjust all the monitor levels for a particular output bus
//
// For efficiency, fImmediate is set to FALSE in the call
// to SetGain; all the monitor and pipe out gains are
// sent to the DSP at once by AdjustPipesOutForBusOut.
//
//===========================================================================
 
ECHOSTATUS CEchoGals::AdjustMonitorsForBusOut(WORD wBusOut)
{
	WORD wBusIn;
	
	//
	// Poke the monitors
	//
	for (wBusIn = 0; wBusIn < GetNumBussesIn(); wBusIn++)
	{
		m_MonitorCtrl.SetGain(wBusIn,wBusOut,ECHOGAIN_UPDATE,FALSE);
	}
	
	return ECHOSTATUS_OK;
	
}	// AdjustMonitorsForBusOut
 
	
//===========================================================================
//
// Adjust all the pipe out levels for a particular output bus
//
// For efficiency, fImmediate is set to FALSE in the call
// to SetGain; all the monitor and pipe out gains are
// sent to the DSP at once by AdjustPipesOutForBusOut.
//
//===========================================================================
 
ECHOSTATUS CEchoGals::AdjustPipesOutForBusOut(WORD wBusOut,INT32 iBusOutGain)
{
	ECHO_DEBUGPRINTF(("CEchoGals::AdjustPipesOutForBusOut wBusOut %d  iBusOutGain %ld\n",
							wBusOut,
							iBusOutGain));
					
	//
	// Round down to the nearest even bus
	//
	wBusOut &= 0xfffe;							
							
	m_PipeOutCtrl.SetGain(wBusOut,wBusOut,ECHOGAIN_UPDATE,FALSE);
	wBusOut++;
	m_PipeOutCtrl.SetGain(wBusOut,wBusOut,ECHOGAIN_UPDATE,TRUE);	
	
	return ECHOSTATUS_OK;
	
}	// AdjustPipesOutForBusOut
 
 
 
//===========================================================================
//
// GetAudioLatency - returns the latency for a single pipe
//
//===========================================================================
 
void CEchoGals::GetAudioLatency(ECHO_AUDIO_LATENCY *pLatency)
{
	DWORD dwLatency;
 
	if (FALSE == pLatency->wIsInput)
	{
		if (pLatency->wPipe >= GetFirstDigitalBusOut())
			dwLatency = m_wDigitalOutputLatency;
		else
			dwLatency = m_wAnalogOutputLatency;	
	}
	else
	{
		if (pLatency->wPipe >= GetFirstDigitalBusIn())
			dwLatency = m_wDigitalInputLatency;
		else
			dwLatency = m_wAnalogInputLatency;	
	}
	
	pLatency->dwLatency = dwLatency;
 
}	// GetAudioLatency

V610 Undefined behavior. Check the shift operator '<<'. The left operand '-128' is negative.

V610 Undefined behavior. Check the shift operator '<<'. The left operand '-128' is negative.

V610 Undefined behavior. Check the shift operator '<<'. The left operand '-128' is negative.

V610 Undefined behavior. Check the shift operator '<<'. The left operand '-128' is negative.

V610 Undefined behavior. Check the shift operator '<<'. The left operand '-25' is negative.

V562 It's odd to compare a bool type value with a value of 18: 0x12 == IsProfessionalSpdif().

V610 Undefined behavior. Check the shift operator '<<'. The left operand '-128' is negative.