root/src/add-ons/kernel/drivers/audio/echo/generic/C3gDco.cpp
// ****************************************************************************
//
//      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 ****