// ****************************************************************************
//
//		CPipeOutCtrl.cpp
//
//		Class to control output pipes on cards with or without vmixers.
//
// ----------------------------------------------------------------------------
//
// 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"
#include "CPipeOutCtrl.h"
 
extern INT32 PanToDb( INT32 iPan );
 
 
//*****************************************************************************
//
// Destructor (this class uses the default constructor)
//
//*****************************************************************************
 
CPipeOutCtrl::~CPipeOutCtrl()
{
	Cleanup();	
}	
 
 
//*****************************************************************************
//
// Init
//
//*****************************************************************************
 
ECHOSTATUS CPipeOutCtrl::Init(CEchoGals *pEG)
{
	DWORD	dwBytes;
	WORD	wPipe,wStereoBus;
 
	m_Gains = NULL;
	m_Mutes = NULL;
	m_Pans = NULL;
	m_PanDbs = NULL;
	
	//
	// Cache stuff
	//
	m_pEG = pEG;
	m_wNumPipesOut = pEG->GetNumPipesOut();
	m_wNumBussesOut = pEG->GetNumBussesOut();
	m_fHasVmixer = pEG->HasVmixer();
	
	//
	// Allocate the arrays
	//
	if (m_fHasVmixer)
	{
		WORD wNumStereoBusses = m_wNumBussesOut >> 1;
		
		//
		// Allocate arrays for vmixer support
		//
		dwBytes = sizeof(INT8) * m_wNumPipesOut * wNumStereoBusses;
		OsAllocateNonPaged(dwBytes,(void **) &m_Gains);
		if (NULL == m_Gains)
		{
			return ECHOSTATUS_NO_MEM;
		}
 
		dwBytes = sizeof(BYTE) * m_wNumPipesOut * wNumStereoBusses;
		OsAllocateNonPaged(dwBytes,(void **) &m_Mutes);
		if (NULL == m_Mutes)
		{
			Cleanup();
			return ECHOSTATUS_NO_MEM;
		}
		
		dwBytes = sizeof(WORD) * m_wNumPipesOut * wNumStereoBusses;
		OsAllocateNonPaged(dwBytes,(void **) &m_Pans);
		if (NULL == m_Pans)
		{
			Cleanup();
			return ECHOSTATUS_NO_MEM;
		}
		
		dwBytes = sizeof(PAN_DB) * m_wNumPipesOut * wNumStereoBusses;
		OsAllocateNonPaged(dwBytes,(void **) &m_PanDbs);
		if (NULL == m_PanDbs)
		{
			Cleanup();
			return ECHOSTATUS_NO_MEM;
		}
		
		//
		// Initialize pans and mutes
		//
		for (wPipe = 0; wPipe < m_wNumPipesOut; wPipe++)
			for (wStereoBus = 0; wStereoBus < wNumStereoBusses; wStereoBus++)
			{
				WORD wIndex;
				
				wIndex = GetIndex(wPipe,wStereoBus << 1);
 
				//
				//	Pans
				// 
				if (0 == (wPipe & 1))
				{
					//
					// Even channel - pan hard left
					//
					m_Pans[wIndex] = 0;
					m_PanDbs[wIndex].iLeft = 0;
					m_PanDbs[wIndex].iRight = GENERIC_TO_DSP( ECHOGAIN_MUTED );
				}
				else
				{
					//
					// Odd channel - pan hard right
					//
					m_Pans[wIndex] = MAX_MIXER_PAN;
					m_PanDbs[wIndex].iLeft = GENERIC_TO_DSP( ECHOGAIN_MUTED );
					m_PanDbs[wIndex].iRight = 0;
				}
				
				//
				// Mutes
				//
				if ((wPipe >> 1) == wStereoBus)
				{
					m_Mutes[wIndex] = FALSE;
				}
				else
				{
					m_Mutes[wIndex] = TRUE;
				}
				
				//
				// Set the gain to the DSP; use fImmedate = FALSE here
				// to make this faster
				//
				SetGain(wPipe,wStereoBus << 1,ECHOGAIN_UPDATE,FALSE);
				
			}
			
			//
			// Set the gain one more time with the immediate flag set to
			// make sure the DSP gets the message
			//
			SetGain(0,0,ECHOGAIN_UPDATE,TRUE);
	}
	else
	{
		//
		// Allocate arrays for no vmixer support - don't need pans
		//
		dwBytes = sizeof(INT8) * m_wNumPipesOut;
		OsAllocateNonPaged(dwBytes,(void **) &m_Gains);
		if (NULL == m_Gains)
		{
			return ECHOSTATUS_NO_MEM;
		}
 
		dwBytes = sizeof(BYTE) * m_wNumPipesOut;
		OsAllocateNonPaged(dwBytes,(void **) &m_Mutes);
		if (NULL == m_Mutes)
		{
			OsFreeNonPaged(m_Gains);
			return ECHOSTATUS_NO_MEM;
		}
		
	}	  
	
	return ECHOSTATUS_OK;
	
}	// Init
 
 
//*****************************************************************************
//
// Cleanup - free allocated memory
//
//*****************************************************************************
 
void CPipeOutCtrl::Cleanup()
{
	if (m_Gains)
		OsFreeNonPaged(m_Gains);
		
	if (m_Mutes)
		OsFreeNonPaged(m_Mutes);
		
	if (m_Pans)
		OsFreeNonPaged(m_Pans);
		
	if (m_PanDbs)
		OsFreeNonPaged(m_PanDbs);
	
}	// Cleanup
 
 
//*****************************************************************************
//
// Set and get gain
//
// For cards without vmixers, output bus gain is not handled by the DSP.
// Instead, the driver adjusts the output pipe volumes by the output bus gain
// and sends that value to the DSP.
//
// For cards with vmixers, the output bus gain is handled by the DSP, so
// the gain setting does not need to take into account the output bus gain
// stored by the driver.
//
//*****************************************************************************
 
ECHOSTATUS CPipeOutCtrl::SetGain
(
	WORD 	wPipeOut, 
	WORD 	wBusOut, 
	INT32 iGain,
	BOOL 	fImmediate
)
{
	INT32 iBusOutGain;
	ECHOSTATUS Status;
 
	if ( NULL == m_pEG)
		return ECHOSTATUS_DSP_DEAD;
		
	if (!m_fHasVmixer && (wPipeOut != wBusOut))
		return ECHOSTATUS_OK;
	
	if ((NULL == m_Gains) || (NULL == m_Mutes))
		return ECHOSTATUS_NO_MEM;
	
	if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut))
	{
		ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetGain - out of range pipe %d bus %d\n",
								wPipeOut,wBusOut));
		return ECHOSTATUS_INVALID_PARAM;		
	}
	
	WORD wIndex = GetIndex(wPipeOut,wBusOut);
 
	/*
	ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetGain pipe %d  bus %d  gain 0x%lx  index %d\n",
							wPipeOut,wBusOut,iGain,wIndex));
	*/
	
	if (ECHOGAIN_UPDATE == iGain)
	{
		iGain = DSP_TO_GENERIC( m_Gains[ wIndex ] );
	}
	else
	{
		if (iGain > ECHOGAIN_MAXOUT)
			iGain = ECHOGAIN_MAXOUT;
		else if (iGain < ECHOGAIN_MUTED)
			iGain = ECHOGAIN_MUTED;
	
		m_Gains[ wIndex ] = (INT8) GENERIC_TO_DSP( iGain );
		
		//
		// Store the notify
		//
		m_pEG->MixerControlChanged(ECHO_PIPE_OUT,MXN_LEVEL,wPipeOut,wBusOut);
	}
		
	if (m_fHasVmixer)
	{
		wBusOut &= 0xfffe;
		
		if (NULL == m_Pans)
			return ECHOSTATUS_NO_MEM;
	
		//
		// For vmixer cards, the DSP handles the output bus gain, 
		// so no need to account for it here.  Vmixer output pipes
		// do have to handle panning.
		//
		INT32 iLeft = iGain + DSP_TO_GENERIC( m_PanDbs[wIndex].iLeft );
		INT32 iRight = iGain + DSP_TO_GENERIC( m_PanDbs[wIndex].iRight );
		
		//
		// Add master gain values
		//
		iBusOutGain = m_pEG->m_BusOutLineLevels[wBusOut].GetGain();
		iLeft += iBusOutGain;
		iBusOutGain = m_pEG->m_BusOutLineLevels[wBusOut+1].GetGain();
		iRight += iBusOutGain;
		
		//
		// Muting and clamping
		//		
		if (m_Mutes[wIndex])
		{
			iLeft = ECHOGAIN_MUTED;
			iRight = ECHOGAIN_MUTED;
		}
		else
		{
			if (  (m_pEG->m_BusOutLineLevels[wBusOut].IsMuteOn()) ||
					(iLeft < ECHOGAIN_MUTED))
			{
				iLeft = ECHOGAIN_MUTED;
			}
			else if (iLeft > ECHOGAIN_MAXOUT)
			{
				iLeft = ECHOGAIN_MAXOUT;				
			}
 
			if (  (m_pEG->m_BusOutLineLevels[wBusOut + 1].IsMuteOn()) ||
					(iRight < ECHOGAIN_MUTED))
			{
				iRight = ECHOGAIN_MUTED;
			}
			else if (iRight > ECHOGAIN_MAXOUT)
			{
				iRight = ECHOGAIN_MAXOUT;				
			}
		}
		
		//
		// Set the left channel gain
		//
		Status = m_pEG->GetDspCommObject()->SetPipeOutGain(wPipeOut,
																			wBusOut,
																			iLeft,
																			FALSE);
		if (ECHOSTATUS_OK == Status)
		{
			//
			// And the right channel
			//
			Status = m_pEG->GetDspCommObject()->SetPipeOutGain(wPipeOut,
																				wBusOut + 1,
																				iRight,
																				fImmediate);
		}
		
	}
	else
	{
		//
		// Add this output pipe gain to the output bus gain
		// Since these gains are in decibels, it's OK to just add them
		//
		iBusOutGain = m_pEG->m_BusOutLineLevels[wBusOut].GetGain();
		iGain += iBusOutGain;
 
		//
		// Mute this output pipe if this output bus is muted
		//	
		if (m_Mutes[ wIndex ] ||
			 (m_pEG->m_BusOutLineLevels[wBusOut].IsMuteOn()) )
		{
			iGain = ECHOGAIN_MUTED;
		}
		else
		{
			//
			// Clamp the output pipe gain if necessary
			//
			if (iGain < ECHOGAIN_MUTED)
				iGain = ECHOGAIN_MUTED;
			else if (iGain > ECHOGAIN_MAXOUT)
				iGain = ECHOGAIN_MAXOUT;
			
		}
 
		//
		// Set the gain
		//
		Status = m_pEG->GetDspCommObject()->SetPipeOutGain(wPipeOut,
																			wBusOut,
																			iGain,
																			fImmediate);
 
	}
	
	return Status;
}	
 
 
ECHOSTATUS CPipeOutCtrl::GetGain(WORD wPipeOut, WORD wBusOut, INT32 &iGain)
{
	WORD	wIndex = GetIndex(wPipeOut,wBusOut);
	
	if (NULL == m_Gains)
		return ECHOSTATUS_NO_MEM;
 
	if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut))
	{
		ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetGain - out of range pipe %d bus %d\n",
								wPipeOut,wBusOut));
		return ECHOSTATUS_INVALID_PARAM;		
	}
 
	iGain = DSP_TO_GENERIC( m_Gains[wIndex] );
 
	/*
	ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetGain pipe %d  bus %d  gain 0x%lx  index %d\n",
							wPipeOut,wBusOut,iGain,wIndex));
	*/
					
	return ECHOSTATUS_OK;
}	
 
 
//*****************************************************************************
//
// Set and get mute
//
//*****************************************************************************
 
ECHOSTATUS CPipeOutCtrl::SetMute
(
	WORD wPipeOut, 
	WORD wBusOut, 
	BOOL bMute,
	BOOL fImmediate
)
{
	if (!m_fHasVmixer && (wPipeOut != wBusOut))
		return ECHOSTATUS_OK;
 
	if (NULL == m_Mutes)
		return ECHOSTATUS_NO_MEM;
 
	if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut))
	{
		ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetMute - out of range pipe %d bus %d\n",
								wPipeOut,wBusOut));
		return ECHOSTATUS_INVALID_PARAM;		
	}
 
	WORD wIndex = GetIndex(wPipeOut,wBusOut);
	
	/*
	ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetMute wPipeOut %d  wBusOut %d  bMute %ld\n",
							wPipeOut,wBusOut,bMute));
	*/
	
	//
	// Store the mute
	//
 	m_Mutes[ wIndex ] = (BYTE) bMute;
 
	//
	// Store the notify
	//
	m_pEG->MixerControlChanged(ECHO_PIPE_OUT,MXN_MUTE,wPipeOut,wBusOut);
 
	//
	// Call the SetGain function to do all the heavy lifting
	// Use the ECHOGAIN_UPDATE value to tell the function to 
	// recalculate the gain setting using the currently stored value.
	//
	return SetGain(wPipeOut,wBusOut,ECHOGAIN_UPDATE,fImmediate);
}	
 
 
ECHOSTATUS CPipeOutCtrl::GetMute(WORD wPipeOut, WORD wBusOut, BOOL &bMute)
{
	WORD	wIndex = GetIndex(wPipeOut,wBusOut);
	
	if (NULL == m_Mutes)
		return ECHOSTATUS_NO_MEM;
 
	if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut))
	{
		ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetMute - out of range pipe %d bus %d\n",
								wPipeOut,wBusOut));
		return ECHOSTATUS_INVALID_PARAM;		
	}
 
	bMute = (BOOL) m_Mutes[ wIndex ];
 
	/*
	ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetMute wPipeOut %d  wBusOut %d  bMute %ld\n",
							wPipeOut,wBusOut,bMute));
	*/
 
	return ECHOSTATUS_OK;
}	
 
 
//*****************************************************************************
//
// Set and get pan (vmixer only)
//
//*****************************************************************************
 
ECHOSTATUS CPipeOutCtrl::SetPan(WORD wPipeOut, WORD wBusOut, INT32 iPan)
{
	if (!m_fHasVmixer)
		return ECHOSTATUS_OK;
		
	if (NULL == m_Pans)
		return ECHOSTATUS_NO_MEM;
	
	if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut))
	{
		ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetPan - out of range pipe %d bus %d\n",
								wPipeOut,wBusOut));
		return ECHOSTATUS_INVALID_PARAM;		
	}
		
 
	WORD	wIndex = GetIndex(wPipeOut,wBusOut);		
 
	//
	// Clamp it and stash it
	//		
	if (iPan < 0)
		iPan = 0;
	else if (iPan > MAX_MIXER_PAN)
		iPan = MAX_MIXER_PAN;
		
	m_Pans[wIndex] = (WORD) iPan;
 
	//
	// Store the notify
	//
	m_pEG->MixerControlChanged(ECHO_PIPE_OUT,MXN_PAN,wPipeOut,wBusOut);
 
	//
	//	Convert this pan setting into left and right dB values
	// 		
	m_PanDbs[wIndex].iLeft = (INT8) GENERIC_TO_DSP( PanToDb(MAX_MIXER_PAN - iPan) );
	m_PanDbs[wIndex].iRight = (INT8) GENERIC_TO_DSP( PanToDb(iPan) );
 
	//
	// Again, SetGain does all the hard work
	//	
	return SetGain(wPipeOut,wBusOut,ECHOGAIN_UPDATE);
}	
 
 
ECHOSTATUS CPipeOutCtrl::GetPan(WORD wPipeOut, WORD wBusOut, INT32 &iPan)
{
	WORD	wIndex = GetIndex(wPipeOut,wBusOut);
	
	if (NULL == m_Pans)
		return ECHOSTATUS_NO_MEM;
 
	if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut))
	{
		ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetPan - out of range pipe %d bus %d\n",
								wPipeOut,wBusOut));
		return ECHOSTATUS_INVALID_PARAM;		
	}
 
	iPan = m_Pans[ wIndex ];
 
	return ECHOSTATUS_OK;
}	

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 '-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 '-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 '-128' is negative.