⛏️ index : haiku.git

// ****************************************************************************
//
//  	C3gDco.cpp
//
//		Implementation file for EchoGals generic driver 3G DSP
//		interface class.
//
// ----------------------------------------------------------------------------
//
// 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 "C3gDco.h"

#include "Echo3gDSP.c"
#include "3G_ASIC.c"

/****************************************************************************

	Construction and destruction

 ****************************************************************************/

//===========================================================================
//
// Constructor
//
//===========================================================================

C3gDco::C3gDco
(
	PDWORD		pdwRegBase,				// Virtual ptr to DSP registers
	PCOsSupport	pOsSupport
) : CDspCommObject( pdwRegBase, pOsSupport )
{
	m_pdwDspRegBase = pdwRegBase;		// Virtual addr DSP's register base  fixme put this in base class

	m_dwOriginalBoxType = NO3GBOX;
	m_dwCurrentBoxType = m_dwOriginalBoxType;
	SetChannelCounts();
	m_bBoxTypeSet = FALSE;
	
	m_fHasVmixer = FALSE;
	
	m_wNumMidiOut = 1;					// # MIDI out channels
	m_wNumMidiIn = 1;						// # MIDI in  channels

	m_pDspCommPage->dwSampleRate = SWAP( (DWORD) 48000 );
	m_pDspCommPage->dw3gFreqReg = SWAP( (DWORD) (E3G_MAGIC_NUMBER / 48000) - 2);

	m_bHasASIC = TRUE;

	m_pwDspCodeToLoad = pwEcho3gDSP;

	m_byDigitalMode = DIGITAL_MODE_SPDIF_RCA;
	
	m_bProfessionalSpdif = FALSE;
	m_bNonAudio = FALSE;
	
}	// C3gDco::C3gDco( DWORD dwPhysRegBase )



//===========================================================================
//
// Destructor
//
//===========================================================================

C3gDco::~C3gDco()
{
}	// C3gDco::~C3gDco()



//===========================================================================
//
// Supported digital modes depend on what kind of box you have
//
//===========================================================================

DWORD C3gDco::GetDigitalModes()
{
	DWORD dwModes;

	dwModes =	ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
					ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
					ECHOCAPS_HAS_DIGITAL_MODE_ADAT; 
	
	return dwModes;
}


/****************************************************************************

	Hardware setup and config

 ****************************************************************************/

//===========================================================================
//
// 3G has an ASIC in the external box
//
//===========================================================================

BOOL C3gDco::LoadASIC()
{
	DWORD	dwControlReg;

	if ( m_bASICLoaded == TRUE )
		return TRUE;
		
	ECHO_DEBUGPRINTF(("C3gDco::LoadASIC\n"));

	//
	// Give the DSP a few milliseconds to settle down
	//
	m_pOsSupport->OsSnooze( 2000 );

	//
	// Load the ASIC
	//
	if ( !CDspCommObject::LoadASIC( DSP_FNC_LOAD_3G_ASIC,
											  pb3g_asic,
											  sizeof(pb3g_asic) ) )
		return FALSE;

	//
	// Give the ASIC a whole second to set up
	//
	m_pOsSupport->OsSnooze( 1000000 );

	//
	// See if it worked
	//
	CheckAsicStatus();

	//
	// Set up the control register if the load succeeded -
	//
	// 48 kHz, internal clock, S/PDIF RCA mode
	//	
	if ( m_bASICLoaded )
	{
		dwControlReg = E3G_48KHZ;
		WriteControlReg( dwControlReg, E3G_FREQ_REG_DEFAULT, TRUE);	// TRUE == force write
	}

	ECHO_DEBUGPRINTF(("\t3G ASIC loader finished\n"));
		
	return m_bASICLoaded;
	
}	// BOOL C3gDco::LoadASIC()



//===========================================================================
//
//	Set the input clock
//
//===========================================================================

ECHOSTATUS C3gDco::SetInputClock(WORD wClock)
{
	DWORD dwControlReg,dwSampleRate;
	ECHOSTATUS Status;

	ECHO_DEBUGPRINTF( ("C3gDco::SetInputClock:\n") );
	
	//
	// Mask off the clock select bits
	//
	dwControlReg = GetControlRegister();
	dwControlReg &= E3G_CLOCK_CLEAR_MASK;
	
	//
	// New clock
	//
	switch (wClock)
	{
		case ECHO_CLOCK_INTERNAL :
			ECHO_DEBUGPRINTF(("\tsetting internal clock\n"));
			
			m_wInputClock = ECHO_CLOCK_INTERNAL;	// prevent recursion
			
			dwSampleRate = GetSampleRate();
			if ((dwSampleRate < 32000) || (dwSampleRate > 100000))
				dwSampleRate = 48000;
				
			SetSampleRate(dwSampleRate);
			return ECHOSTATUS_OK;
			

		case ECHO_CLOCK_WORD: 
			dwControlReg |= E3G_WORD_CLOCK;

			if ( E3G_CLOCK_DETECT_BIT_WORD96 & GetInputClockDetect() )
			{
				dwControlReg |= E3G_DOUBLE_SPEED_MODE;
			}
			else
			{
				dwControlReg &= ~E3G_DOUBLE_SPEED_MODE;
			}
			ECHO_DEBUGPRINTF( ( "\tSet 3G clock to WORD\n" ) );
			break;
			
		
		case ECHO_CLOCK_SPDIF :
			if ( DIGITAL_MODE_ADAT == GetDigitalMode() )
			{
				return ECHOSTATUS_CLOCK_NOT_AVAILABLE;
			}
			
			dwControlReg |= E3G_SPDIF_CLOCK;
			if ( E3G_CLOCK_DETECT_BIT_SPDIF96 & GetInputClockDetect() )
			{
				dwControlReg |= E3G_DOUBLE_SPEED_MODE;
			}
			else
			{
				dwControlReg &= ~E3G_DOUBLE_SPEED_MODE;
			}

			ECHO_DEBUGPRINTF( ( "\tSet 3G clock to SPDIF\n" ) );
			break;

	
		case ECHO_CLOCK_ADAT :
			if ( DIGITAL_MODE_ADAT != GetDigitalMode() )
			{
				return ECHOSTATUS_CLOCK_NOT_AVAILABLE;
			}
			
			dwControlReg |= E3G_ADAT_CLOCK;
			dwControlReg &= ~E3G_DOUBLE_SPEED_MODE;
			ECHO_DEBUGPRINTF( ( "\tSet 3G clock to ADAT\n" ) );
			break;
			

		default :
			ECHO_DEBUGPRINTF(("Input clock 0x%x not supported for 3G\n",wClock));
			ECHO_DEBUGBREAK();
				return ECHOSTATUS_CLOCK_NOT_SUPPORTED;
	}
	
	//
	// Winner! Try to write the hardware
	//
	Status = WriteControlReg( dwControlReg, Get3gFreqReg(), TRUE );
	if (ECHOSTATUS_OK == Status)
		m_wInputClock = wClock;
	
	return Status;

}	// ECHOSTATUS C3gDco::SetInputClock



//===========================================================================
//
// SetSampleRate
// 
// Set the audio sample rate for 3G
//
//===========================================================================

DWORD C3gDco::SetSampleRate( DWORD dwNewSampleRate )
{
	DWORD dwControlReg,dwNewClock,dwBaseRate,dwFreqReg;
	
	ECHO_DEBUGPRINTF(("3G set sample rate to %ld\n",dwNewSampleRate));
	
	//
	// Only set the clock for internal mode.  If the clock is not set to
	// internal, try and re-set the input clock; this more transparently
	// handles switching between single and double-speed mode
	//
	if ( GetInputClock() != ECHO_CLOCK_INTERNAL )
	{
		ECHO_DEBUGPRINTF( ( "C3gDco::SetSampleRate: Cannot set sample rate - "
								  "clock not set to ECHO_CLOCK_INTERNAL\n" ) );
		
		//
		// Save the rate anyhow
		//
		m_pDspCommPage->dwSampleRate = SWAP( dwNewSampleRate );
		
		//
		// Set the input clock to the current value
		//
		SetInputClock( m_wInputClock );
		
		return dwNewSampleRate;
	}

	//
	// Get the control register & clear the appropriate bits
	// 
	dwControlReg = GetControlRegister();
	dwControlReg &= E3G_CLOCK_CLEAR_MASK;
	
	//
	// Set the sample rate
	//
	switch ( dwNewSampleRate )
	{
		case 96000 :
			dwNewClock = E3G_96KHZ;
			break;
		
		case 88200 :
			dwNewClock = E3G_88KHZ;
			break;
		
		case 48000 : 
			dwNewClock = E3G_48KHZ;
			break;
		
		case 44100 : 
			dwNewClock = E3G_44KHZ;
			break;
		
		case 32000 :
			dwNewClock = E3G_32KHZ;
			break;

		default :
			dwNewClock = E3G_CONTINUOUS_CLOCK;
			if (dwNewSampleRate > 50000)
				dwNewClock |= E3G_DOUBLE_SPEED_MODE;
			break;
	}

	dwControlReg |= dwNewClock;
	SetSpdifBits(&dwControlReg,dwNewSampleRate);
	
	ECHO_DEBUGPRINTF(("\tdwNewClock 0x%lx  dwControlReg 0x%lx\n",dwNewClock,dwControlReg));

	//
	// Set up the frequency reg
	//
	dwBaseRate = dwNewSampleRate;
	if (dwBaseRate > 50000)
		dwBaseRate /= 2;
	
	if (dwBaseRate < 32000)
		dwBaseRate = 32000;
	
	dwFreqReg = E3G_MAGIC_NUMBER / dwBaseRate - 2;
	if (dwFreqReg > E3G_FREQ_REG_MAX)
		dwFreqReg = E3G_FREQ_REG_MAX;
		
	//
	// Tell the DSP about it - DSP reads both control reg & freq reg
	//
	if ( ECHOSTATUS_OK == WriteControlReg( dwControlReg, dwFreqReg) )
	{
		m_pDspCommPage->dwSampleRate = SWAP( dwNewSampleRate );

		ECHO_DEBUGPRINTF( ("C3gDco::SetSampleRate: %ld  clock %lx\n", dwNewSampleRate, dwControlReg) );
	}
	else
	{
		ECHO_DEBUGPRINTF( ("C3gDco::SetSampleRate: could not set sample rate %ld\n", dwNewSampleRate) );

		dwNewSampleRate = SWAP( m_pDspCommPage->dwSampleRate );
	}

	return dwNewSampleRate;
	
} // DWORD C3gDco::SetSampleRate( DWORD dwNewSampleRate )



//===========================================================================
//
// SetDigitalMode
// 
//===========================================================================

ECHOSTATUS C3gDco::SetDigitalMode
(
	BYTE	byNewMode
)
{
	DWORD		dwControlReg;
	WORD		wInvalidClock;
	
	//
	// See if the current input clock doesn't match the new digital mode
	//
	switch (byNewMode)
	{
		case DIGITAL_MODE_SPDIF_RCA :
		case DIGITAL_MODE_SPDIF_OPTICAL :
			wInvalidClock = ECHO_CLOCK_ADAT;
			break;
			
		case DIGITAL_MODE_ADAT :
			wInvalidClock = ECHO_CLOCK_SPDIF;
			break;
			
		default :
			wInvalidClock = 0xffff;
			break;
	}

	if (wInvalidClock == GetInputClock())
	{
		SetInputClock( ECHO_CLOCK_INTERNAL );
		SetSampleRate( 48000 );
	}

	
	//
	// Clear the current digital mode
	//
	dwControlReg = GetControlRegister();
	dwControlReg &= E3G_DIGITAL_MODE_CLEAR_MASK;

	//
	// Tweak the control reg
	//
	switch ( byNewMode )
	{
		default :
			return ECHOSTATUS_DIGITAL_MODE_NOT_SUPPORTED;

		case DIGITAL_MODE_SPDIF_OPTICAL :
			dwControlReg |= E3G_SPDIF_OPTICAL_MODE;
			// fall through
			
		case DIGITAL_MODE_SPDIF_RCA :
			break;
			
		case DIGITAL_MODE_ADAT :
			dwControlReg |= E3G_ADAT_MODE;
			dwControlReg &= ~E3G_DOUBLE_SPEED_MODE;
			break;	
	}

	//
	// Write the control reg
	//
	WriteControlReg( dwControlReg, Get3gFreqReg(), TRUE );

	m_byDigitalMode = byNewMode;

	ECHO_DEBUGPRINTF( ("C3gDco::SetDigitalMode to %ld\n",
							(DWORD) m_byDigitalMode) );

	return ECHOSTATUS_OK;

}	// ECHOSTATUS C3gDco::SetDigitalMode



//===========================================================================
//
// WriteControlReg
//
//===========================================================================

ECHOSTATUS C3gDco::WriteControlReg
( 
	DWORD dwControlReg,
	DWORD	dwFreqReg,
	BOOL 	fForceWrite
)
{
	ECHOSTATUS Status;

	ECHO_DEBUGPRINTF(("C3gDco::WriteControlReg 0x%lx 0x%lx\n",dwControlReg,dwFreqReg));
	
	//
	// New value OK?
	//
	Status = ValidateCtrlReg(dwControlReg);
	if (ECHOSTATUS_OK != Status)
		return Status;

	//
	// Ready to go?
	//
	if ( !m_bASICLoaded )
	{
		ECHO_DEBUGPRINTF(("C3gDco::WriteControlReg - ASIC not loaded\n"));
		return( ECHOSTATUS_ASIC_NOT_LOADED );
	}
		
	if ( !WaitForHandshake() )
	{
		ECHO_DEBUGPRINTF(("C3gDco::WriteControlReg - no handshake\n"));
		return ECHOSTATUS_DSP_DEAD;
	}
	
	//
	// Write the control register
	//
	if (	fForceWrite || 
			(dwControlReg != GetControlRegister()) ||
			(dwFreqReg != Get3gFreqReg())
		)
	{
		m_pDspCommPage->dw3gFreqReg = SWAP( dwFreqReg );
		SetControlRegister( dwControlReg );

		ECHO_DEBUGPRINTF( ("C3gDco::WriteControlReg: Setting 0x%lx, 0x%lx\n",
								 dwControlReg,dwFreqReg) );
								 
		ClearHandshake();							 
		return SendVector( DSP_VC_WRITE_CONTROL_REG );
	}
	else
	{
		ECHO_DEBUGPRINTF( ("C3gDco::WriteControlReg: not written, no change\n") );
	}
	
	return ECHOSTATUS_OK;
	
} // ECHOSTATUS C3gDco::WriteControlReg


//===========================================================================
//
// SetSpdifBits
//
//===========================================================================

void C3gDco::SetSpdifBits(DWORD *pdwCtrlReg,DWORD dwSampleRate)
{
	DWORD dwCtrlReg;
	
	dwCtrlReg = *pdwCtrlReg;
	
	//
	// Clean out the old status bits
	//
	dwCtrlReg &= E3G_SPDIF_FORMAT_CLEAR_MASK;

	//
	// Sample rate
	// 
	switch (dwSampleRate)
	{
		case 32000 :
			dwCtrlReg |= E3G_SPDIF_SAMPLE_RATE0 |
							 E3G_SPDIF_SAMPLE_RATE1;
			break;
			
		case 44100 :
			if (m_bProfessionalSpdif)
				dwCtrlReg |= E3G_SPDIF_SAMPLE_RATE0;
			break;
			
		case 48000 :
			dwCtrlReg |= E3G_SPDIF_SAMPLE_RATE1;
			break;
	}
	
	//
	// Professional mode?
	//
	if (m_bProfessionalSpdif)
		dwCtrlReg |= E3G_SPDIF_PRO_MODE;
		
	//
	// Non-audio data?
	//
	if (m_bNonAudio)
		dwCtrlReg |= E3G_SPDIF_NOT_AUDIO;
		
	//
	// Always stereo, 24 bit, copy permit
	//
	dwCtrlReg |= E3G_SPDIF_24_BIT | E3G_SPDIF_TWO_CHANNEL | E3G_SPDIF_COPY_PERMIT;

	*pdwCtrlReg = dwCtrlReg;

} // SetSpdifBits


//===========================================================================
//
// SetSpdifOutNonAudio
//
// Set the state of the non-audio status bit in the S/PDIF out status bits
// 
//===========================================================================

void C3gDco::SetSpdifOutNonAudio(BOOL bNonAudio)
{
	DWORD		dwControlReg;

	m_bNonAudio = bNonAudio;

	dwControlReg = GetControlRegister();
	SetSpdifBits( &dwControlReg, SWAP(	m_pDspCommPage->dwSampleRate ));
	WriteControlReg( dwControlReg, Get3gFreqReg() );	
}	


//===========================================================================
//
// Set the S/PDIF output format
// 
//===========================================================================

void C3gDco::SetProfessionalSpdif
(
	BOOL bNewStatus
)
{
	DWORD		dwControlReg;

	m_bProfessionalSpdif = bNewStatus;

	dwControlReg = GetControlRegister();
	SetSpdifBits( &dwControlReg, SWAP(	m_pDspCommPage->dwSampleRate ));
	WriteControlReg( dwControlReg, Get3gFreqReg() );

}	// void C3gDco::SetProfessionalSpdif( ... )


//===========================================================================
//
// ASIC status check
//
// 3G ASIC status check returns different values depending on what kind of box
// is hooked up
//
//===========================================================================

BOOL C3gDco::CheckAsicStatus()
{
	DWORD	dwBoxStatus,dwBoxType;
	
	if ( !WaitForHandshake() )
	{
		ECHO_DEBUGPRINTF(("CheckAsicStatus - no handshake!\n"));
		return FALSE;
	}
		
	//
	// Send the vector command
	//
	m_pDspCommPage->dwExtBoxStatus = SWAP( (DWORD) E3G_ASIC_NOT_LOADED);
	m_bASICLoaded = FALSE;
	ClearHandshake();
	SendVector( DSP_VC_TEST_ASIC );	

	//
	// Wait for return from DSP
	//
	if ( !WaitForHandshake() )
	{
		ECHO_DEBUGPRINTF(("CheckAsicStatus - no handshake after VC\n"));
		m_pwDspCode = NULL;
		m_ullLastLoadAttemptTime = 0;	// so LoadFirmware will try again right away
		return FALSE;
	}
		
	//
	// What box type was set?
	//
	dwBoxStatus = SWAP(m_pDspCommPage->dwExtBoxStatus);
	if (E3G_ASIC_NOT_LOADED == dwBoxStatus)
	{
		ECHO_DEBUGPRINTF(("CheckAsicStatus - ASIC not loaded\n"));
		dwBoxType = NO3GBOX;
	}
	else
	{
		dwBoxType = dwBoxStatus & E3G_BOX_TYPE_MASK;
		m_bASICLoaded = TRUE;
		ECHO_DEBUGPRINTF(("CheckAsicStatus - read box type %x\n",dwBoxType));
	}
		
	m_dwCurrentBoxType = dwBoxType;

	//
	// Has the box type already been set?
	//
	if (m_bBoxTypeSet)
	{
		//
		// Did the ASIC load?
		// Was the box type correct?
		//
		if (	(NO3GBOX == dwBoxType) ||
				(dwBoxType != m_dwOriginalBoxType) )
		{
			ECHO_DEBUGPRINTF(("CheckAsicStatus - box type mismatch - original %x, got %x\n",m_dwOriginalBoxType,dwBoxType));
			return FALSE;
		}
			
		ECHO_DEBUGPRINTF(("CheckAsicStatus - ASIC ok\n"));
		m_bASICLoaded = TRUE;
		return TRUE;
	}
	
	//
	// First ASIC load - determine the box type and set up for that kind of box
	//		
	m_dwOriginalBoxType = dwBoxType;
	m_bBoxTypeSet = TRUE;

	SetChannelCounts();
	
	//
	// Set the bad board flag if no external box
	//
	if (NO3GBOX == dwBoxType)
	{
		ECHO_DEBUGPRINTF(("CheckAsicStatus - no external box\n"));
		m_bBadBoard = TRUE;
	}

	return m_bASICLoaded;

}	// BOOL C3gDco::CheckAsicStatus()


//===========================================================================
//
// SetPhantomPower
//
//===========================================================================

void C3gDco::SetPhantomPower(BOOL fPhantom)
{
	DWORD		dwControlReg;

	dwControlReg = GetControlRegister();
	if (fPhantom)
	{
		dwControlReg |= E3G_PHANTOM_POWER;
	}
	else
	{
		dwControlReg &= ~E3G_PHANTOM_POWER;
	}
	
	WriteControlReg( dwControlReg, Get3gFreqReg() );
}


//===========================================================================
//
// Set channel counts for the current box type
//
//===========================================================================

void C3gDco::SetChannelCounts()
{
	char *pszName;
	WORD ch,i;
	
	switch (m_dwOriginalBoxType)
	{
		case GINA3G :
			m_wNumPipesOut = 14;
			m_wNumPipesIn = 10;
			m_wFirstDigitalBusOut = 6;
			m_wFirstDigitalBusIn = 2;
			
			pszName = "Gina3G";
			break;
			
		
		case NO3GBOX :
		case LAYLA3G :
		default :
			m_wNumPipesOut = 16;
			m_wNumPipesIn = 16;
			m_wFirstDigitalBusOut = 8;
			m_wFirstDigitalBusIn = 8;
			
			pszName = "Layla3G";
			break;
	}

	m_wNumBussesOut = m_wNumPipesOut;
	m_wNumBussesIn = m_wNumPipesIn;
	strcpy( m_szCardName, pszName);
	
	//
	// Build a channel mask for ADAT inputs & outputs 3-8
	// OK to use bus # here since this hardware has no virtual outputs
	//
	m_Adat38Mask.Clear(); 
	ch = m_wFirstDigitalBusOut + 2;
	for (i = 0; i < 6; i++)
	{
		m_Adat38Mask.SetIndexInMask(ch);
		ch++;
	}
	
	ch += m_wFirstDigitalBusIn + 2;
	for (i = 0; i < 6; i++)
	{
		m_Adat38Mask.SetIndexInMask(ch);
		ch++;
	}
}


//===========================================================================
//
// Return the 3G box type
//
//===========================================================================

void C3gDco::Get3gBoxType(DWORD *pOriginalBoxType,DWORD *pCurrentBoxType)
{
	if (NULL != pOriginalBoxType)
		*pOriginalBoxType = m_dwOriginalBoxType;
		
	if (NULL != pCurrentBoxType)
	{
		CheckAsicStatus();
		
		*pCurrentBoxType = m_dwCurrentBoxType;
	}

} // Get3gBoxType



//===========================================================================
//
// Fill out an ECHOGALS_METERS struct using the current values in the 
// comm page.  This method is overridden for vmixer cards.
//
//===========================================================================

ECHOSTATUS C3gDco::GetAudioMeters
(
	PECHOGALS_METERS	pMeters
)
{
	pMeters->iNumPipesOut = 0;
	pMeters->iNumPipesIn = 0;

	//
	//	Output 
	// 
	DWORD dwCh = 0;
	WORD 	i;

	pMeters->iNumBussesOut = (INT32) m_wNumBussesOut;
	for (i = 0; i < m_wNumBussesOut; i++)
	{
		pMeters->iBusOutVU[i] = 
			DSP_TO_GENERIC( ((INT32) (INT8) m_pDspCommPage->VUMeter[ dwCh ]) );

		pMeters->iBusOutPeak[i] = 
			DSP_TO_GENERIC( ((INT32) (INT8) m_pDspCommPage->PeakMeter[ dwCh ]) );
		
		dwCh++;
	}

	pMeters->iNumBussesIn = (INT32) m_wNumBussesIn;	
	dwCh = E3G_MAX_OUTPUTS;
	for (i = 0; i < m_wNumBussesIn; i++)
	{
		pMeters->iBusInVU[i] = 
			DSP_TO_GENERIC( ((INT32) (INT8) m_pDspCommPage->VUMeter[ dwCh ]) );
		pMeters->iBusInPeak[i] = 
			DSP_TO_GENERIC( ((INT32) (INT8) m_pDspCommPage->PeakMeter[ dwCh ]) );
		
		dwCh++;
	}
	
	return ECHOSTATUS_OK;
	
} // GetAudioMeters



//===========================================================================
//
// Utility function; returns TRUE if double speed mode is set
//
//===========================================================================

BOOL C3gDco::DoubleSpeedMode(DWORD *pdwNewCtrlReg)
{
	DWORD dwControlReg;
	
	if (NULL == pdwNewCtrlReg)
		dwControlReg = GetControlRegister();
	else
		dwControlReg = *pdwNewCtrlReg;
	
	if (0 != (dwControlReg & E3G_DOUBLE_SPEED_MODE))
		return TRUE;
		
	return FALSE;
}


//===========================================================================
//
// Utility function; validates a new control register value.  Prevents
// speed change while transport is running
//
//===========================================================================

ECHOSTATUS C3gDco::ValidateCtrlReg(DWORD dwNewControlReg)
{
	BOOL fCurrDoubleSpeed,fNewDoubleSpeed;
	
	//
	// Return OK if transport is off
	//
	if (m_cmActive.IsEmpty())
		return ECHOSTATUS_OK;

	//
	// Get the new and current state of things
	//
	fNewDoubleSpeed = DoubleSpeedMode(&dwNewControlReg);
	fCurrDoubleSpeed = DoubleSpeedMode(NULL);	

	//
	// OK to change?
	//
	if (fCurrDoubleSpeed != fNewDoubleSpeed)
	{
		ECHO_DEBUGPRINTF(("Can't switch to speeds with transport active\n"));
		return ECHOSTATUS_INVALID_CHANNEL;
	}

	return ECHOSTATUS_OK;
}

// **** C3gDco.cpp ****