// **************************************************************************** // // CDspCommObject.cpp // // Implementation file for EchoGals generic driver 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" #ifdef DSP_56361 #include "LoaderDSP.c" #endif #define COMM_PAGE_PHYS_BYTES ((sizeof(DspCommPage)+PAGE_SIZE-1)/PAGE_SIZE)*PAGE_SIZE /**************************************************************************** Construction and destruction ****************************************************************************/ //=========================================================================== // // Overload new & delete so memory for this object is allocated // from non-paged memory. // //=========================================================================== PVOID CDspCommObject::operator new( size_t Size ) { PVOID pMemory; ECHOSTATUS Status; Status = OsAllocateNonPaged(Size,&pMemory); if ( (ECHOSTATUS_OK != Status) || (NULL == pMemory )) { ECHO_DEBUGPRINTF(("CDspCommObject::operator new - memory allocation failed\n")); pMemory = NULL; } else { memset( pMemory, 0, Size ); } return pMemory; } // PVOID CDspCommObject::operator new( size_t Size ) VOID CDspCommObject::operator delete( PVOID pVoid ) { if ( ECHOSTATUS_OK != OsFreeNonPaged( pVoid ) ) { ECHO_DEBUGPRINTF( ("CDspCommObject::operator delete memory free " "failed\n") ); } } // VOID CDspCommObject::operator delete( PVOID pVoid ) //=========================================================================== // // Constructor // //=========================================================================== CDspCommObject::CDspCommObject ( PDWORD pdwDspRegBase, // Virtual ptr to DSP registers PCOsSupport pOsSupport ) { INT32 i; ECHO_ASSERT(pOsSupport ); // // Init all the basic stuff // strcpy( m_szCardName, "??????" ); m_pOsSupport = pOsSupport; // Ptr to OS Support methods & data m_pdwDspRegBase = pdwDspRegBase; // Virtual addr DSP's register base m_bBadBoard = TRUE; // Set TRUE until DSP loaded m_pwDspCode = NULL; // Current DSP code not loaded m_byDigitalMode = DIGITAL_MODE_NONE; m_wInputClock = ECHO_CLOCK_INTERNAL; m_wOutputClock = ECHO_CLOCK_WORD; m_ullLastLoadAttemptTime = (ULONGLONG)(DWORD)(0L - DSP_LOAD_ATTEMPT_PERIOD); // force first load to go #ifdef MIDI_SUPPORT m_ullNextMidiWriteTime = 0; #endif // // Create the DSP comm page - this is the area of memory read and written by // the DSP via bus mastering // ECHOSTATUS Status; DWORD dwSegmentSize; PHYS_ADDR PhysAddr; Status = pOsSupport->AllocPhysPageBlock( COMM_PAGE_PHYS_BYTES, m_pDspCommPageBlock); if (ECHOSTATUS_OK != Status) { ECHO_DEBUGPRINTF( ("CDspCommObject::CDspCommObject DSP comm page " "memory allocation failed\n") ); return; } m_pDspCommPage = (PDspCommPage) pOsSupport-> GetPageBlockVirtAddress( m_pDspCommPageBlock ); pOsSupport->GetPageBlockPhysSegment(m_pDspCommPageBlock, 0, PhysAddr, dwSegmentSize); m_dwCommPagePhys = PhysAddr; // // Init the comm page // m_pDspCommPage->dwCommSize = SWAP( sizeof( DspCommPage ) ); // Size of DSP comm page m_pDspCommPage->dwHandshake = 0xffffffff; m_pDspCommPage->dwMidiOutFreeCount = SWAP( (DWORD) DSP_MIDI_OUT_FIFO_SIZE ); for ( i = 0; i < DSP_MAXAUDIOINPUTS; i++ ) m_pDspCommPage->InLineLevel[ i ] = 0x00; // Set line levels so we don't blast // any inputs on startup memset( m_pDspCommPage->byMonitors, GENERIC_TO_DSP(ECHOGAIN_MUTED), MONITOR_ARRAY_SIZE ); // Mute all monitors memset( m_pDspCommPage->byVmixerLevel, GENERIC_TO_DSP(ECHOGAIN_MUTED), VMIXER_ARRAY_SIZE ); // Mute all virtual mixer levels #ifdef DIGITAL_INPUT_AUTO_MUTE_SUPPORT m_fDigitalInAutoMute = TRUE; #endif } // CDspCommObject::CDspCommObject //=========================================================================== // // Destructor // //=========================================================================== CDspCommObject::~CDspCommObject() { // // Go to sleep // GoComatose(); // // Free the comm page // if ( NULL != m_pDspCommPageBlock ) { m_pOsSupport->FreePhysPageBlock( COMM_PAGE_PHYS_BYTES, m_pDspCommPageBlock); } ECHO_DEBUGPRINTF( ( "CDspCommObject::~CDspCommObject() is toast!\n" ) ); } // CDspCommObject::~CDspCommObject() /**************************************************************************** Firmware loading functions ****************************************************************************/ //=========================================================================== // // ASIC status check - some cards have one or two ASICs that need to be // loaded. Once that load is complete, this function is called to see if // the load was successful. // // If this load fails, it does not necessarily mean that the hardware is // defective - the external box may be disconnected or turned off. // //=========================================================================== BOOL CDspCommObject::CheckAsicStatus() { DWORD dwAsicStatus; DWORD dwReturn; // // Always succeed if this card doesn't have an ASIC // if ( !m_bHasASIC ) { m_bASICLoaded = TRUE; return TRUE; } // Send the vector command m_bASICLoaded = FALSE; SendVector( DSP_VC_TEST_ASIC ); // The DSP will return a value to indicate whether or not the // ASIC is currently loaded dwReturn = Read_DSP( &dwAsicStatus ); if ( ECHOSTATUS_OK != dwReturn ) { ECHO_DEBUGPRINTF(("CDspCommObject::CheckAsicStatus - failed on Read_DSP\n")); ECHO_DEBUGBREAK(); return FALSE; } #ifdef ECHO_DEBUG if ( (dwAsicStatus != ASIC_LOADED) && (dwAsicStatus != ASIC_NOT_LOADED) ) { ECHO_DEBUGBREAK(); } #endif if ( dwAsicStatus == ASIC_LOADED ) m_bASICLoaded = TRUE; return m_bASICLoaded; } // BOOL CDspCommObject::CheckAsicStatus() //=========================================================================== // // Load ASIC code - done after the DSP is loaded // //=========================================================================== BOOL CDspCommObject::LoadASIC ( DWORD dwCmd, PBYTE pCode, DWORD dwSize ) { DWORD i; #ifdef _WIN32 DWORD dwChecksum = 0; #endif ECHO_DEBUGPRINTF(("CDspCommObject::LoadASIC\n")); if ( !m_bHasASIC ) return TRUE; #ifdef _DEBUG ULONGLONG ullStartTime, ullCurTime; m_pOsSupport->OsGetSystemTime( &ullStartTime ); #endif // Send the "Here comes the ASIC" command if ( ECHOSTATUS_OK != Write_DSP( dwCmd ) ) return FALSE; // Write length of ASIC file in bytes if ( ECHOSTATUS_OK != Write_DSP( dwSize ) ) return FALSE; for ( i = 0; i < dwSize; i++ ) { #ifdef _WIN32 dwChecksum += pCode[i]; #endif if ( ECHOSTATUS_OK != Write_DSP( pCode[ i ] ) ) { ECHO_DEBUGPRINTF(("\tfailed on Write_DSP\n")); return FALSE; } } #ifdef _DEBUG m_pOsSupport->OsGetSystemTime( &ullCurTime ); ECHO_DEBUGPRINTF( ("CDspCommObject::LoadASIC took %ld usec.\n", (ULONG) ( ullCurTime - ullStartTime ) ) ); ECHO_DEBUGPRINTF(("ASIC load OK\n")); #endif #if defined(_WIN32) && (DBG) DbgPrint("--- ASIC checksum is 0x%lx\n",dwChecksum); #endif return TRUE; } // BOOL CDspCommObject::LoadASIC( DWORD dwCmd, PBYTE pCode, DWORD dwSize ) //=========================================================================== // // InstallResidentLoader // // Install the resident loader for 56361 DSPs; The resident loader // is on the EPROM on the board for 56301 DSP. // // The resident loader is a tiny little program that is used to load // the real DSP code. // //=========================================================================== #ifdef DSP_56361 ECHOSTATUS CDspCommObject::InstallResidentLoader() { DWORD dwAddress; DWORD dwIndex; INT32 iNum; INT32 i; DWORD dwReturn; PWORD pCode; ECHO_DEBUGPRINTF( ("CDspCommObject::InstallResidentLoader\n") ); // // 56361 cards only! // if (DEVICE_ID_56361 != m_pOsSupport->GetDeviceId() ) return ECHOSTATUS_OK; // // Look to see if the resident loader is present. If the resident loader // is already installed, host flag 5 will be on. // DWORD dwStatus; dwStatus = GetDspRegister( CHI32_STATUS_REG ); if ( 0 != (dwStatus & CHI32_STATUS_REG_HF5 ) ) { ECHO_DEBUGPRINTF(("\tResident loader already installed; status is 0x%lx\n", dwStatus)); return ECHOSTATUS_OK; } // // Set DSP format bits for 24 bit mode // SetDspRegister( CHI32_CONTROL_REG, GetDspRegister( CHI32_CONTROL_REG ) | 0x900 ); //--------------------------------------------------------------------------- // // Loader // // The DSP code is an array of 16 bit words. The array is divided up into // sections. The first word of each section is the size in words, followed // by the section type. // // Since DSP addresses and data are 24 bits wide, they each take up two // 16 bit words in the array. // // This is a lot like the other loader loop, but it's not a loop, // you don't write the memory type, and you don't write a zero at the end. // //--------------------------------------------------------------------------- pCode = pwLoaderDSP; // // Skip the header section; the first word in the array is the size of // the first section, so the first real section of code is pointed to // by pCode[0]. // dwIndex = pCode[ 0 ]; // // Skip the section size, LRS block type, and DSP memory type // dwIndex += 3; // // Get the number of DSP words to write // iNum = pCode[ dwIndex++ ]; // // Get the DSP address for this block; 24 bits, so build from two words // dwAddress = ( pCode[ dwIndex ] << 16 ) + pCode[ dwIndex + 1 ]; dwIndex += 2; // // Write the count to the DSP // dwReturn = Write_DSP( (DWORD) iNum ); if ( dwReturn != 0 ) { ECHO_DEBUGPRINTF( ("CDspCommObject::InstallResidentLoader: Failed to " "write word count!\n") ); return ECHOSTATUS_DSP_DEAD; } // Write the DSP address dwReturn = Write_DSP( dwAddress ); if ( dwReturn != 0 ) { ECHO_DEBUGPRINTF( ("CDspCommObject::InstallResidentLoader: Failed to " "write DSP address!\n") ); return ECHOSTATUS_DSP_DEAD; } // Write out this block of code to the DSP for ( i = 0; i < iNum; i++) // { DWORD dwData; dwData = ( pCode[ dwIndex ] << 16 ) + pCode[ dwIndex + 1 ]; dwReturn = Write_DSP( dwData ); if ( dwReturn != 0 ) { ECHO_DEBUGPRINTF( ("CDspCommObject::InstallResidentLoader: Failed to " "write DSP code\n") ); return ECHOSTATUS_DSP_DEAD; } dwIndex+=2; } // // Wait for flag 5 to come up // BOOL fSuccess; ULONGLONG ullCurTime,ullTimeout; m_pOsSupport->OsGetSystemTime( &ullCurTime ); ullTimeout = ullCurTime + 10000L; // 10m.s. fSuccess = FALSE; do { m_pOsSupport->OsSnooze(50); // Give the DSP some time; // no need to hog the CPU dwStatus = GetDspRegister( CHI32_STATUS_REG ); if (0 != (dwStatus & CHI32_STATUS_REG_HF5)) { fSuccess = TRUE; break; } m_pOsSupport->OsGetSystemTime( &ullCurTime ); } while (ullCurTime < ullTimeout); if (FALSE == fSuccess) { ECHO_DEBUGPRINTF(("\tResident loader failed to set HF5\n")); return ECHOSTATUS_DSP_DEAD; } ECHO_DEBUGPRINTF(("\tResident loader successfully installed\n")); return ECHOSTATUS_OK; } // ECHOSTATUS CDspCommObject::InstallResidentLoader() #endif // DSP_56361 //=========================================================================== // // LoadDSP // // This loads the DSP code. // //=========================================================================== ECHOSTATUS CDspCommObject::LoadDSP ( PWORD pCode // Ptr to DSP object code ) { DWORD dwAddress; DWORD dwIndex; INT32 iNum; INT32 i; DWORD dwReturn; ULONGLONG ullTimeout, ullCurTime; ECHOSTATUS Status; ECHO_DEBUGPRINTF(("CDspCommObject::LoadDSP\n")); if ( m_pwDspCode == pCode ) { ECHO_DEBUGPRINTF( ("\tDSP is already loaded!\n") ); return ECHOSTATUS_FIRMWARE_LOADED; } m_bBadBoard = TRUE; // Set TRUE until DSP loaded m_pwDspCode = NULL; // Current DSP code not loaded m_bASICLoaded = FALSE; // Loading the DSP code will reset the ASIC ECHO_DEBUGPRINTF(("CDspCommObject::LoadDSP Set m_bBadBoard to TRUE\n")); // // If this board requires a resident loader, install it. // #ifdef DSP_56361 InstallResidentLoader(); #endif // Send software reset command if ( ECHOSTATUS_OK != SendVector( DSP_VC_RESET ) ) { m_pOsSupport->EchoErrorMsg( "CDspCommObject::LoadDsp SendVector DSP_VC_RESET failed", "Critical Failure" ); return ECHOSTATUS_DSP_DEAD; } // Delay 10us m_pOsSupport->OsSnooze( 10L ); // Wait 10ms for HF3 to indicate that software reset is complete m_pOsSupport->OsGetSystemTime( &ullCurTime ); ullTimeout = ullCurTime + 10000L; // 10m.s. // wait for HF3 to be set wait_for_hf3: if ( GetDspRegister( CHI32_STATUS_REG ) & CHI32_STATUS_REG_HF3 ) goto set_dsp_format_bits; m_pOsSupport->OsGetSystemTime( &ullCurTime ); if ( ullCurTime > ullTimeout) { ECHO_DEBUGPRINTF( ("CDspCommObject::LoadDSP Timeout waiting for " "CHI32_STATUS_REG_HF3\n") ); m_pOsSupport->EchoErrorMsg( "CDspCommObject::LoadDSP SendVector DSP_VC_RESET failed", "Critical Failure" ); return ECHOSTATUS_DSP_TIMEOUT; } goto wait_for_hf3; // Set DSP format bits for 24 bit mode now that soft reset is done set_dsp_format_bits: SetDspRegister( CHI32_CONTROL_REG, GetDspRegister( CHI32_CONTROL_REG ) | (DWORD) 0x900 ); //--------------------------------------------------------------------------- // Main loader loop //--------------------------------------------------------------------------- dwIndex = pCode[ 0 ]; for (;;) { INT32 iBlockType; INT32 iMemType; // Total Block Size dwIndex++; // Block Type iBlockType = pCode[ dwIndex ]; if ( iBlockType == 4 ) // We're finished break; dwIndex++; // Memory Type P=0,X=1,Y=2 iMemType = pCode[ dwIndex ]; dwIndex++; // Block Code Size iNum = pCode[ dwIndex ]; dwIndex++; if ( iNum == 0 ) // We're finished break; // Start Address dwAddress = ( (DWORD) pCode[ dwIndex ] << 16 ) + pCode[ dwIndex + 1 ]; // ECHO_DEBUGPRINTF( ("\tdwAddress %lX\n", dwAddress) ); dwIndex += 2; dwReturn = Write_DSP( (DWORD)iNum ); if ( dwReturn != 0 ) { ECHO_DEBUGPRINTF(("LoadDSP - failed to write number of DSP words\n")); return ECHOSTATUS_DSP_DEAD; } dwReturn = Write_DSP( dwAddress ); if ( dwReturn != 0 ) { ECHO_DEBUGPRINTF(("LoadDSP - failed to write DSP address\n")); return ECHOSTATUS_DSP_DEAD; } dwReturn = Write_DSP( (DWORD)iMemType ); if ( dwReturn != 0 ) { ECHO_DEBUGPRINTF(("LoadDSP - failed to write DSP memory type\n")); return ECHOSTATUS_DSP_DEAD; } // Code for ( i = 0; i < iNum; i++ ) { DWORD dwData; dwData = ( (DWORD) pCode[ dwIndex ] << 16 ) + pCode[ dwIndex + 1 ]; dwReturn = Write_DSP( dwData ); if ( dwReturn != 0 ) { ECHO_DEBUGPRINTF(("LoadDSP - failed to write DSP data\n")); return ECHOSTATUS_DSP_DEAD; } dwIndex += 2; } // ECHO_DEBUGPRINTF( ("\tEnd Code Block\n") ); } dwReturn = Write_DSP( 0 ); // We're done!!! if ( dwReturn != 0 ) { ECHO_DEBUGPRINTF(("LoadDSP: Failed to write final zero\n")); return ECHOSTATUS_DSP_DEAD; } // Delay 10us m_pOsSupport->OsSnooze( 10L ); m_pOsSupport->OsGetSystemTime( &ullCurTime ); ullTimeout = ullCurTime + 500000L; // 1/2 sec. timeout while ( ullCurTime <= ullTimeout) { // // Wait for flag 4 - indicates that the DSP loaded OK // if ( GetDspRegister( CHI32_STATUS_REG ) & CHI32_STATUS_REG_HF4 ) { SetDspRegister( CHI32_CONTROL_REG, GetDspRegister( CHI32_CONTROL_REG ) & ~0x1b00 ); dwReturn = Write_DSP( DSP_FNC_SET_COMMPAGE_ADDR ); if ( dwReturn != 0 ) { ECHO_DEBUGPRINTF(("LoadDSP - Failed to write DSP_FNC_SET_COMMPAGE_ADDR\n")); return ECHOSTATUS_DSP_DEAD; } dwReturn = Write_DSP( m_dwCommPagePhys ); if ( dwReturn != 0 ) { ECHO_DEBUGPRINTF(("LoadDSP - Failed to write comm page address\n")); return ECHOSTATUS_DSP_DEAD; } // // Get the serial number via slave mode. // This is triggered by the SET_COMMPAGE_ADDR command. // We don't actually use the serial number but we have to get // it as part of the DSP init vodoo. // Status = ReadSn(); if ( ECHOSTATUS_OK != Status ) { ECHO_DEBUGPRINTF(("LoadDSP - Failed to read serial number\n")); return Status; } m_pwDspCode = pCode; // Show which DSP code loaded m_bBadBoard = FALSE; // DSP OK ECHO_DEBUGPRINTF(("CDspCommObject::LoadDSP Set m_bBadBoard to FALSE\n")); return ECHOSTATUS_OK; } m_pOsSupport->OsGetSystemTime( &ullCurTime ); } ECHO_DEBUGPRINTF( ("LoadDSP: DSP load timed out waiting for HF4\n") ); return ECHOSTATUS_DSP_TIMEOUT; } // DWORD CDspCommObject::LoadDSP //=========================================================================== // // LoadFirmware takes care of loading the DSP and any ASIC code. // //=========================================================================== ECHOSTATUS CDspCommObject::LoadFirmware() { ECHOSTATUS dwReturn; ULONGLONG ullRightNow; // Sanity check if ( NULL == m_pwDspCodeToLoad || NULL == m_pDspCommPage ) { ECHO_DEBUGBREAK(); return ECHOSTATUS_NO_MEM; } // // Even if the external box is off, an application may still try // to repeatedly open the driver, causing multiple load attempts and // making the machine spend lots of time in the kernel. If the ASIC is not // loaded, this code will gate the loading attempts so it doesn't happen // more than once per second. // m_pOsSupport->OsGetSystemTime(&ullRightNow); if ( (FALSE == m_bASICLoaded) && (DSP_LOAD_ATTEMPT_PERIOD > (ullRightNow - m_ullLastLoadAttemptTime)) ) return ECHOSTATUS_ASIC_NOT_LOADED; // // Update the timestamp // m_ullLastLoadAttemptTime = ullRightNow; // // See if the ASIC is present and working - only if the DSP is already loaded // if (NULL != m_pwDspCode) { dwReturn = CheckAsicStatus(); if (TRUE == dwReturn) return ECHOSTATUS_OK; // // ASIC check failed; force the DSP to reload // m_pwDspCode = NULL; } // // Try and load the DSP // dwReturn = LoadDSP( m_pwDspCodeToLoad ); if ( (ECHOSTATUS_OK != dwReturn) && (ECHOSTATUS_FIRMWARE_LOADED != dwReturn) ) { return dwReturn; } ECHO_DEBUGPRINTF(("DSP load OK\n")); // // Load the ASIC if the DSP load succeeded; LoadASIC will // always return TRUE for cards that don't have an ASIC. // dwReturn = LoadASIC(); if ( FALSE == dwReturn ) { dwReturn = ECHOSTATUS_ASIC_NOT_LOADED; } else { // // ASIC load was successful // RestoreDspSettings(); dwReturn = ECHOSTATUS_OK; } return dwReturn; } // BOOL CDspCommObject::LoadFirmware() //=========================================================================== // // This function is used to read back the serial number from the DSP; // this is triggered by the SET_COMMPAGE_ADDR command. // // Only some early Echogals products have serial numbers in the ROM; // the serial number is not used, but you still need to do this as // part of the DSP load process. // //=========================================================================== ECHOSTATUS CDspCommObject::ReadSn() { INT32 j; DWORD dwSn[ 6 ]; ECHOSTATUS Status; ECHO_DEBUGPRINTF( ("CDspCommObject::ReadSn\n") ); for ( j = 0; j < 5; j++ ) { Status = Read_DSP( &dwSn[ j ] ); if ( Status != 0 ) { ECHO_DEBUGPRINTF( ("\tFailed to read serial number word %ld\n", j) ); return ECHOSTATUS_DSP_DEAD; } } ECHO_DEBUGPRINTF( ("\tRead serial number %08lx %08lx %08lx %08lx %08lx\n", dwSn[0], dwSn[1], dwSn[2], dwSn[3], dwSn[4]) ); return ECHOSTATUS_OK; } // DWORD CDspCommObject::ReadSn //=========================================================================== // // This is called after LoadFirmware to restore old gains, meters on, // monitors, etc. // //=========================================================================== void CDspCommObject::RestoreDspSettings() { ECHO_DEBUGPRINTF(("RestoreDspSettings\n")); ECHO_DEBUGPRINTF(("\tControl reg is 0x%lx\n",SWAP(m_pDspCommPage->dwControlReg) )); if ( !CheckAsicStatus() ) return; m_pDspCommPage->dwHandshake = 0xffffffff; #ifdef MIDI_SUPPORT m_ullNextMidiWriteTime = 0; #endif SetSampleRate(); if ( 0 != m_wMeterOnCount ) { SendVector( DSP_VC_METERS_ON ); } SetInputClock( m_wInputClock ); SetOutputClock( m_wOutputClock ); if ( !WaitForHandshake() ) { return; } UpdateAudioOutLineLevel(); if ( !WaitForHandshake() ) return; UpdateAudioInLineLevel(); if ( HasVmixer() ) { if ( !WaitForHandshake() ) return; UpdateVmixerLevel(); } if ( !WaitForHandshake() ) return; ClearHandshake(); SendVector( DSP_VC_UPDATE_FLAGS ); ECHO_DEBUGPRINTF(("RestoreDspSettings done\n")); } // void CDspCommObject::RestoreDspSettings() /**************************************************************************** DSP utilities ****************************************************************************/ //=========================================================================== // // Write_DSP writes a 32-bit value to the DSP; this is used almost // exclusively for loading the DSP. // //=========================================================================== ECHOSTATUS CDspCommObject::Write_DSP ( DWORD dwData // 32 bit value to write to DSP data register ) { DWORD dwStatus; ULONGLONG ullCurTime, ullTimeout; // ECHO_DEBUGPRINTF(("Write_DSP\n")); m_pOsSupport->OsGetSystemTime( &ullCurTime ); ullTimeout = ullCurTime + 10000000L; // 10 sec. while ( ullTimeout >= ullCurTime ) { dwStatus = GetDspRegister( CHI32_STATUS_REG ); if ( ( dwStatus & CHI32_STATUS_HOST_WRITE_EMPTY ) != 0 ) { SetDspRegister( CHI32_DATA_REG, dwData ); // ECHO_DEBUGPRINTF(("Write DSP: 0x%x", dwData)); return ECHOSTATUS_OK; } m_pOsSupport->OsGetSystemTime( &ullCurTime ); } m_bBadBoard = TRUE; // Set TRUE until DSP re-loaded ECHO_DEBUGPRINTF(("CDspCommObject::Write_DSP Set m_bBadBoard to TRUE\n")); return ECHOSTATUS_DSP_TIMEOUT; } // ECHOSTATUS CDspCommObject::Write_DSP //=========================================================================== // // Read_DSP reads a 32-bit value from the DSP; this is used almost // exclusively for loading the DSP and checking the status of the ASIC. // //=========================================================================== ECHOSTATUS CDspCommObject::Read_DSP ( DWORD *pdwData // Ptr to 32 bit value read from DSP data register ) { DWORD dwStatus; ULONGLONG ullCurTime, ullTimeout; // ECHO_DEBUGPRINTF(("Read_DSP\n")); m_pOsSupport->OsGetSystemTime( &ullCurTime ); ullTimeout = ullCurTime + READ_DSP_TIMEOUT; while ( ullTimeout >= ullCurTime ) { dwStatus = GetDspRegister( CHI32_STATUS_REG ); if ( ( dwStatus & CHI32_STATUS_HOST_READ_FULL ) != 0 ) { *pdwData = GetDspRegister( CHI32_DATA_REG ); // ECHO_DEBUGPRINTF(("Read DSP: 0x%x\n", *pdwData)); return ECHOSTATUS_OK; } m_pOsSupport->OsGetSystemTime( &ullCurTime ); } m_bBadBoard = TRUE; // Set TRUE until DSP re-loaded ECHO_DEBUGPRINTF(("CDspCommObject::Read_DSP Set m_bBadBoard to TRUE\n")); return ECHOSTATUS_DSP_TIMEOUT; } // ECHOSTATUS CDspCommObject::Read_DSP //=========================================================================== // // Much of the interaction between the DSP and the driver is done via vector // commands; SendVector writes a vector command to the DSP. Typically, // this causes the DSP to read or write fields in the comm page. // // Returns ECHOSTATUS_OK if sent OK. // //=========================================================================== ECHOSTATUS CDspCommObject::SendVector ( DWORD dwCommand // 32 bit command to send to DSP vector register ) { ULONGLONG ullTimeout; ULONGLONG ullCurTime; // // Turn this on if you want to see debug prints for every vector command // #if 0 //#ifdef ECHO_DEBUG char * pszCmd; switch ( dwCommand ) { case DSP_VC_ACK_INT : pszCmd = "DSP_VC_ACK_INT"; break; case DSP_VC_SET_VMIXER_GAIN : pszCmd = "DSP_VC_SET_VMIXER_GAIN"; break; case DSP_VC_START_TRANSFER : pszCmd = "DSP_VC_START_TRANSFER"; break; case DSP_VC_METERS_ON : pszCmd = "DSP_VC_METERS_ON"; break; case DSP_VC_METERS_OFF : pszCmd = "DSP_VC_METERS_OFF"; break; case DSP_VC_UPDATE_OUTVOL : pszCmd = "DSP_VC_UPDATE_OUTVOL"; break; case DSP_VC_UPDATE_INGAIN : pszCmd = "DSP_VC_UPDATE_INGAIN"; break; case DSP_VC_ADD_AUDIO_BUFFER : pszCmd = "DSP_VC_ADD_AUDIO_BUFFER"; break; case DSP_VC_TEST_ASIC : pszCmd = "DSP_VC_TEST_ASIC"; break; case DSP_VC_UPDATE_CLOCKS : pszCmd = "DSP_VC_UPDATE_CLOCKS"; break; case DSP_VC_SET_LAYLA_SAMPLE_RATE : if ( GetCardType() == LAYLA ) pszCmd = "DSP_VC_SET_LAYLA_RATE"; else if ( GetCardType() == GINA || GetCardType() == DARLA ) pszCmd = "DSP_VC_SET_GD_AUDIO_STATE"; else pszCmd = "DSP_VC_WRITE_CONTROL_REG"; break; case DSP_VC_MIDI_WRITE : pszCmd = "DSP_VC_MIDI_WRITE"; break; case DSP_VC_STOP_TRANSFER : pszCmd = "DSP_VC_STOP_TRANSFER"; break; case DSP_VC_UPDATE_FLAGS : pszCmd = "DSP_VC_UPDATE_FLAGS"; break; case DSP_VC_RESET : pszCmd = "DSP_VC_RESET"; break; default : pszCmd = "?????"; break; } ECHO_DEBUGPRINTF( ("SendVector: %s dwCommand %s (0x%x)\n", GetCardName(), pszCmd, dwCommand) ); #endif m_pOsSupport->OsGetSystemTime( &ullCurTime ); ullTimeout = ullCurTime + 100000L; // 100m.s. // // Wait for the "vector busy" bit to be off // while ( ullCurTime <= ullTimeout) { DWORD dwReg; dwReg = GetDspRegister( CHI32_VECTOR_REG ); if ( 0 == (dwReg & CHI32_VECTOR_BUSY) ) { SetDspRegister( CHI32_VECTOR_REG, dwCommand ); return ECHOSTATUS_OK; } m_pOsSupport->OsGetSystemTime( &ullCurTime ); } ECHO_DEBUGPRINTF( ("\tPunked out on SendVector\n") ); ECHO_DEBUGBREAK(); return ECHOSTATUS_DSP_TIMEOUT; } // ECHOSTATUS CDspCommObject::SendVector //=========================================================================== // // Some vector commands involve the DSP reading or writing data to and // from the comm page; if you send one of these commands to the DSP, // it will complete the command and then write a non-zero value to // the dwHandshake field in the comm page. This function waits for the // handshake to show up. // //=========================================================================== BOOL CDspCommObject::WaitForHandshake() { ULONGLONG ullDelta; ULONGLONG ullStartTime,ullTime; // // Wait up to three milliseconds for the handshake from the DSP // m_pOsSupport->OsGetSystemTime( &ullStartTime ); do { // Look for the handshake value if ( 0 != GetHandshakeFlag() ) { return TRUE; } // Give the DSP time to access the comm page m_pOsSupport->OsSnooze( 2 ); m_pOsSupport->OsGetSystemTime(&ullTime); ullDelta = ullTime - ullStartTime; } while (ullDelta < (ULONGLONG) HANDSHAKE_TIMEOUT); ECHO_DEBUGPRINTF( ("CDspCommObject::WaitForHandshake: Timeout waiting " "for DSP\n") ); ECHO_DEBUGBREAK(); return FALSE; } // DWORD CDspCommObject::WaitForHandshake() /**************************************************************************** Transport methods ****************************************************************************/ //=========================================================================== // // StartTransport starts transport for a set of pipes // //=========================================================================== ECHOSTATUS CDspCommObject::StartTransport ( PCChannelMask pChannelMask // Pipes to start ) { ECHO_DEBUGPRINTF( ("StartTransport\n") ); // // Wait for the previous command to complete // if ( !WaitForHandshake() ) return ECHOSTATUS_DSP_DEAD; // // Write the appropriate fields in the comm page // m_pDspCommPage->cmdStart.Clear(); m_pDspCommPage->cmdStart = *pChannelMask; if ( !m_pDspCommPage->cmdStart.IsEmpty() ) { // // Clear the handshake and send the vector command // ClearHandshake(); SendVector( DSP_VC_START_TRANSFER ); // // Keep track of which pipes are transporting // m_cmActive += *pChannelMask; return ECHOSTATUS_OK; } // if this monkey is being started ECHO_DEBUGPRINTF( ("CDspCommObject::StartTransport: No pipes to start!\n") ); return ECHOSTATUS_INVALID_CHANNEL; } // ECHOSTATUS CDspCommObject::StartTransport //=========================================================================== // // StopTransport pauses transport for a set of pipes // //=========================================================================== ECHOSTATUS CDspCommObject::StopTransport ( PCChannelMask pChannelMask ) { ECHO_DEBUGPRINTF(("StopTransport\n")); // // Wait for the last command to finish // if ( !WaitForHandshake() ) return ECHOSTATUS_DSP_DEAD; // // Write to the comm page // m_pDspCommPage->cmdStop.Clear(); m_pDspCommPage->cmdStop = *pChannelMask; m_pDspCommPage->cmdReset.Clear(); if ( !m_pDspCommPage->cmdStop.IsEmpty() ) { // // Clear the handshake and send the vector command // ClearHandshake(); SendVector( DSP_VC_STOP_TRANSFER ); // // Keep track of which pipes are transporting // m_cmActive -= *pChannelMask; return ECHOSTATUS_OK; } // if this monkey is being started ECHO_DEBUGPRINTF( ("CDspCommObject::StopTransport: No pipes to stop!\n") ); return ECHOSTATUS_OK; } // ECHOSTATUS CDspCommObject::StopTransport //=========================================================================== // // ResetTransport resets transport for a set of pipes // //=========================================================================== ECHOSTATUS CDspCommObject::ResetTransport ( PCChannelMask pChannelMask ) { ECHO_DEBUGPRINTF(("ResetTransport\n")); // // Wait for the last command to finish // if ( !WaitForHandshake() ) return ECHOSTATUS_DSP_DEAD; // // Write to the comm page // m_pDspCommPage->cmdStop.Clear(); m_pDspCommPage->cmdReset.Clear(); m_pDspCommPage->cmdStop = *pChannelMask; m_pDspCommPage->cmdReset = *pChannelMask; if ( !m_pDspCommPage->cmdReset.IsEmpty() ) { // // Clear the handshake and send the vector command // ClearHandshake(); SendVector( DSP_VC_STOP_TRANSFER ); // // Keep track of which pipes are transporting // m_cmActive -= *pChannelMask; return ECHOSTATUS_OK; } // if this monkey is being started ECHO_DEBUGPRINTF( ("CDspCommObject::ResetTransport: No pipes to reset!\n") ); return ECHOSTATUS_OK; } // ECHOSTATUS CDspCommObject::ResetTransport //=========================================================================== // // This tells the DSP where to start reading the scatter-gather list // for a given pipe. // //=========================================================================== void CDspCommObject::SetAudioDuckListPhys ( WORD wPipeIndex, // Pipe index DWORD dwNewPhysAdr // Physical address asserted on the PCI bus ) { if (wPipeIndex < GetNumPipes() ) { m_pDspCommPage->DuckListPhys[ wPipeIndex ].PhysAddr = SWAP( dwNewPhysAdr ); } } // void CDspCommObject::SetAudioDuckListPhys //=========================================================================== // // Get a mask with active pipes // //=========================================================================== void CDspCommObject::GetActivePipes ( PCChannelMask pChannelMask ) { pChannelMask->Clear(); *pChannelMask += m_cmActive; } // void CDspCommObject::GetActivePipes() //=========================================================================== // // Set the audio format for a pipe // //=========================================================================== ECHOSTATUS CDspCommObject::SetAudioFormat ( WORD wPipeIndex, PECHOGALS_AUDIOFORMAT pFormat ) { WORD wDspFormat = DSP_AUDIOFORM_SS_16LE; ECHO_DEBUGPRINTF(("CDspCommObject::SetAudioFormat - pipe %d bps %d channels %d\n", wPipeIndex,pFormat->wBitsPerSample,pFormat->wDataInterleave)); // // Check the pipe number // if (wPipeIndex >= GetNumPipes() ) { ECHO_DEBUGPRINTF( ("CDspCommObject::SetAudioFormat: Invalid pipe" "%d\n", wPipeIndex) ); return ECHOSTATUS_INVALID_CHANNEL; } // // Look for super-interleave // if (pFormat->wDataInterleave > 2) { switch (pFormat->wBitsPerSample) { case 16 : wDspFormat = DSP_AUDIOFORM_SUPER_INTERLEAVE_16LE; break; case 24 : wDspFormat = DSP_AUDIOFORM_SUPER_INTERLEAVE_24LE; break; case 32 : wDspFormat = DSP_AUDIOFORM_SUPER_INTERLEAVE_32LE; break; } wDspFormat |= pFormat->wDataInterleave; } else { // // For big-endian data, only 32 bit mono->mono samples and 32 bit stereo->stereo // are supported // if (pFormat->byDataAreBigEndian) { switch ( pFormat->wDataInterleave ) { case 1 : wDspFormat = DSP_AUDIOFORM_MM_32BE; break; #ifdef STEREO_BIG_ENDIAN32_SUPPORT case 2 : wDspFormat = DSP_AUDIOFORM_SS_32BE; break; #endif } } else { // // Check for 32 bit little-endian mono->mono case // if ( (1 == pFormat->wDataInterleave) && (32 == pFormat->wBitsPerSample) && (0 == pFormat->byMonoToStereo) ) { wDspFormat = DSP_AUDIOFORM_MM_32LE; } else { // // Handle the other little-endian formats // switch (pFormat->wBitsPerSample) { case 8 : if (2 == pFormat->wDataInterleave) wDspFormat = DSP_AUDIOFORM_SS_8; else wDspFormat = DSP_AUDIOFORM_MS_8; break; default : case 16 : if (2 == pFormat->wDataInterleave) wDspFormat = DSP_AUDIOFORM_SS_16LE; else wDspFormat = DSP_AUDIOFORM_MS_16LE; break; case 24 : if (2 == pFormat->wDataInterleave) wDspFormat = DSP_AUDIOFORM_SS_24LE; else wDspFormat = DSP_AUDIOFORM_MS_24LE; break; case 32 : if (2 == pFormat->wDataInterleave) wDspFormat = DSP_AUDIOFORM_SS_32LE; else wDspFormat = DSP_AUDIOFORM_MS_32LE; break; } } // check other little-endian formats } // not big endian data } // not super-interleave m_pDspCommPage->wAudioFormat[wPipeIndex] = SWAP( wDspFormat ); return ECHOSTATUS_OK; } // ECHOSTATUS CDspCommObject::SetAudioFormat //=========================================================================== // // Get the audio format for a pipe // //=========================================================================== ECHOSTATUS CDspCommObject::GetAudioFormat ( WORD wPipeIndex, PECHOGALS_AUDIOFORMAT pFormat ) { if (wPipeIndex >= GetNumPipes() ) { ECHO_DEBUGPRINTF( ("CDspCommObject::GetAudioFormat: Invalid pipe %d\n", wPipeIndex) ); return ECHOSTATUS_INVALID_CHANNEL; } pFormat->byDataAreBigEndian = 0; // true for most of the formats pFormat->byMonoToStereo = 0; switch (SWAP(m_pDspCommPage->wAudioFormat[wPipeIndex])) { case DSP_AUDIOFORM_MS_8 : pFormat->wDataInterleave = 1; pFormat->wBitsPerSample = 8; pFormat->byMonoToStereo = 1; break; case DSP_AUDIOFORM_MS_16LE : pFormat->wDataInterleave = 1; pFormat->wBitsPerSample = 16; pFormat->byMonoToStereo = 1; break; case DSP_AUDIOFORM_SS_8 : pFormat->wDataInterleave = 2; pFormat->wBitsPerSample = 8; break; case DSP_AUDIOFORM_SS_16LE : pFormat->wDataInterleave = 2; pFormat->wBitsPerSample = 16; break; case DSP_AUDIOFORM_SS_32LE : pFormat->wDataInterleave = 2; pFormat->wBitsPerSample = 32; break; case DSP_AUDIOFORM_MS_32LE : pFormat->byMonoToStereo = 1; // fall through case DSP_AUDIOFORM_MM_32LE : pFormat->wDataInterleave = 1; pFormat->wBitsPerSample = 32; break; case DSP_AUDIOFORM_MM_32BE : pFormat->wDataInterleave = 1; pFormat->wBitsPerSample = 32; pFormat->byDataAreBigEndian = 1; break; case DSP_AUDIOFORM_SS_32BE : pFormat->wDataInterleave = 2; pFormat->wBitsPerSample = 32; pFormat->byDataAreBigEndian = 1; break; } return ECHOSTATUS_OK; } // void CDspCommObject::GetAudioFormat /**************************************************************************** Mixer methods ****************************************************************************/ //=========================================================================== // // SetPipeOutGain - set the gain for a single output pipe // //=========================================================================== ECHOSTATUS CDspCommObject::SetPipeOutGain ( WORD wPipeOut, WORD wBusOut, INT32 iGain, BOOL fImmediate ) { if ( wPipeOut < m_wNumPipesOut ) { // // Wait for the handshake // if ( !WaitForHandshake() ) return ECHOSTATUS_DSP_DEAD; // // Save the new value // iGain = GENERIC_TO_DSP(iGain); m_pDspCommPage->OutLineLevel[ wPipeOut ] = (BYTE) iGain; /* ECHO_DEBUGPRINTF( ("CDspCommObject::SetPipeOutGain: Out pipe %d " "= 0x%lx\n", wPipeOut, iGain) ); */ // // If fImmediate is true, then do the gain setting right now. // If you want to do a batch of gain settings all at once, it's // more efficient to call this several times and then only set // fImmediate for the last one; then the DSP picks up all of // them at once. // if (fImmediate) { return UpdateAudioOutLineLevel(); } return ECHOSTATUS_OK; } ECHO_DEBUGPRINTF( ("CDspCommObject::SetPipeOutGain: Invalid out pipe " "%d\n", wPipeOut) ); ECHO_DEBUGBREAK(); return ECHOSTATUS_INVALID_CHANNEL; } // SetPipeOutGain //=========================================================================== // // GetPipeOutGain returns the current gain for an output pipe. This isn't // really used as the mixer code in CEchoGals stores logical values for // these, but it's here for completeness. // //=========================================================================== ECHOSTATUS CDspCommObject::GetPipeOutGain ( WORD wPipeOut, WORD wBusOut, INT32 &iGain ) { if (wPipeOut < m_wNumPipesOut) { iGain = (INT32) (char) m_pDspCommPage->OutLineLevel[ wPipeOut ]; iGain = DSP_TO_GENERIC(8); return ECHOSTATUS_OK; } ECHO_DEBUGPRINTF( ("CDspCommObject::GetPipeOutGain: Invalid out pipe " "%d\n", wPipeOut) ); return ECHOSTATUS_INVALID_CHANNEL; } // GetPipeOutGain //=========================================================================== // // Set input bus gain - iGain is in units of 0.5 dB // //=========================================================================== ECHOSTATUS CDspCommObject::SetBusInGain( WORD wBusIn, INT32 iGain) { if (wBusIn > m_wNumBussesIn) return ECHOSTATUS_INVALID_CHANNEL; // // Wait for the handshake (OK even if ASIC is not loaded) // if ( !WaitForHandshake() ) return ECHOSTATUS_DSP_DEAD; // // Adjust the gain value // iGain += GL20_INPUT_GAIN_MAGIC_NUMBER; // // Put it in the comm page // m_pDspCommPage->InLineLevel[wBusIn] = (BYTE) iGain; return UpdateAudioInLineLevel(); } //=========================================================================== // // Get the input bus gain in units of 0.5 dB // //=========================================================================== ECHOSTATUS CDspCommObject::GetBusInGain( WORD wBusIn, INT32 &iGain) { if (wBusIn > m_wNumBussesIn) return ECHOSTATUS_INVALID_CHANNEL; iGain = m_pDspCommPage->InLineLevel[wBusIn]; iGain -= GL20_INPUT_GAIN_MAGIC_NUMBER; return ECHOSTATUS_OK; } //=========================================================================== // // Set the nominal level for an input or output bus // // bState TRUE -10 nominal level // bState FALSE +4 nominal level // //=========================================================================== ECHOSTATUS CDspCommObject::SetNominalLevel ( WORD wBus, BOOL bState ) { // // Check the pipe index // if (wBus < (m_wNumBussesOut + m_wNumBussesIn)) { // // Wait for the handshake (OK even if ASIC is not loaded) // if ( !WaitForHandshake() ) return ECHOSTATUS_DSP_DEAD; // // Set the nominal bit // if ( bState ) m_pDspCommPage->cmdNominalLevel.SetIndexInMask( wBus ); else m_pDspCommPage->cmdNominalLevel.ClearIndexInMask( wBus ); return UpdateAudioOutLineLevel(); } ECHO_DEBUGPRINTF( ("CDspCommObject::SetNominalOutLineLevel Invalid " "index %d\n", wBus ) ); return ECHOSTATUS_INVALID_CHANNEL; } // ECHOSTATUS CDspCommObject::SetNominalLevel //=========================================================================== // // Get the nominal level for an input or output bus // // bState TRUE -10 nominal level // bState FALSE +4 nominal level // //=========================================================================== ECHOSTATUS CDspCommObject::GetNominalLevel ( WORD wBus, PBYTE pbyState ) { if (wBus < (m_wNumBussesOut + m_wNumBussesIn)) { *pbyState = (BYTE) m_pDspCommPage->cmdNominalLevel.TestIndexInMask( wBus ); return ECHOSTATUS_OK; } ECHO_DEBUGPRINTF( ("CDspCommObject::GetNominalLevel Invalid " "index %d\n", wBus ) ); return ECHOSTATUS_INVALID_CHANNEL; } // ECHOSTATUS CDspCommObject::GetNominalLevel //=========================================================================== // // Set the monitor level from an input bus to an output bus. // //=========================================================================== ECHOSTATUS CDspCommObject::SetAudioMonitor ( WORD wBusOut, // output bus WORD wBusIn, // input bus INT32 iGain, BOOL fImmediate ) { /* ECHO_DEBUGPRINTF( ("CDspCommObject::SetAudioMonitor: " "Out %d in %d Gain %d (0x%x)\n", wBusOut, wBusIn, iGain, iGain) ); */ // // The monitor array is a one-dimensional array; // compute the offset into the array // WORD wOffset = ComputeAudioMonitorIndex( wBusOut, wBusIn ); // // Wait for the offset // if ( !WaitForHandshake() ) return ECHOSTATUS_DSP_DEAD; // // Write the gain value to the comm page // iGain = GENERIC_TO_DSP(iGain); m_pDspCommPage->byMonitors[ wOffset ] = (BYTE) (iGain); // // If fImmediate is set, do the command right now // if (fImmediate) { return UpdateAudioOutLineLevel(); } return ECHOSTATUS_OK; } // ECHOSTATUS CDspCommObject::SetAudioMonitor //=========================================================================== // // SetMetersOn turns the meters on or off. If meters are turned on, the // DSP will write the meter and clock detect values to the comm page // at about 30 Hz. // //=========================================================================== ECHOSTATUS CDspCommObject::SetMetersOn ( BOOL bOn ) { if ( bOn ) { if ( 0 == m_wMeterOnCount ) { SendVector( DSP_VC_METERS_ON ); } m_wMeterOnCount++; } else { INT32 iDevice; if ( m_wMeterOnCount == 0 ) return ECHOSTATUS_OK; if ( 0 == --m_wMeterOnCount ) { SendVector( DSP_VC_METERS_OFF ); for ( iDevice = 0; iDevice < DSP_MAXPIPES; iDevice++ ) { BYTE muted; muted = (BYTE) GENERIC_TO_DSP(ECHOGAIN_MUTED); m_pDspCommPage->VUMeter[ iDevice ] = muted; m_pDspCommPage->PeakMeter[ iDevice ] = muted; } } } return ECHOSTATUS_OK; } // ECHOSTATUS CDspCommObject::SetMetersOn //=========================================================================== // // Tell the DSP to read and update output, nominal & monitor levels // in comm page. // //=========================================================================== ECHOSTATUS CDspCommObject::UpdateAudioOutLineLevel() { //ECHO_DEBUGPRINTF( ( "CDspCommObject::UpdateAudioOutLineLevel:\n" ) ); if (FALSE == m_bASICLoaded) return ECHOSTATUS_ASIC_NOT_LOADED; ClearHandshake(); return( SendVector( DSP_VC_UPDATE_OUTVOL ) ); } // ECHOSTATUS CDspCommObject::UpdateAudioOutLineLevel() //=========================================================================== // // Tell the DSP to read and update input levels in comm page // //=========================================================================== ECHOSTATUS CDspCommObject::UpdateAudioInLineLevel() { //ECHO_DEBUGPRINTF( ( "CDspCommObject::UpdateAudioInLineLevel:\n" ) ); if (FALSE == m_bASICLoaded) return ECHOSTATUS_ASIC_NOT_LOADED; ClearHandshake(); return( SendVector( DSP_VC_UPDATE_INGAIN ) ); } // ECHOSTATUS CDspCommObject::UpdateAudioInLineLevel() //=========================================================================== // // Tell the DSP to read and update virtual mixer levels // in comm page. This method is overridden by cards that actually // support a vmixer. // //=========================================================================== ECHOSTATUS CDspCommObject::UpdateVmixerLevel() { ECHO_DEBUGPRINTF(("CDspCommObject::UpdateVmixerLevel\n")); return ECHOSTATUS_NOT_SUPPORTED; } // ECHOSTATUS CDspCommObject::UpdateVmixerLevel() //=========================================================================== // // Tell the DSP to change the input clock // //=========================================================================== ECHOSTATUS CDspCommObject::SetInputClock(WORD wClock) { // // Wait for the last command // if (!WaitForHandshake()) return ECHOSTATUS_DSP_DEAD; ECHO_DEBUGPRINTF( ("CDspCommObject::SetInputClock:\n") ); // // Write to the comm page // m_pDspCommPage->wInputClock = SWAP(wClock); // // Clear the handshake and send the command // ClearHandshake(); ECHOSTATUS Status = SendVector(DSP_VC_UPDATE_CLOCKS); return Status; } // ECHOSTATUS CDspCommObject::SetInputClock //=========================================================================== // // Tell the DSP to change the output clock - Layla20 only // //=========================================================================== ECHOSTATUS CDspCommObject::SetOutputClock(WORD wClock) { return ECHOSTATUS_CLOCK_NOT_SUPPORTED; } // ECHOSTATUS CDspCommObject::SetOutputClock //=========================================================================== // // Fill out an ECHOGALS_METERS struct using the current values in the // comm page. This method is overridden for vmixer cards. // //=========================================================================== ECHOSTATUS CDspCommObject::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; 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 #ifdef DIGITAL_INPUT_AUTO_MUTE_SUPPORT //=========================================================================== // // Digital input auto-mute - Gina24, Layla24, and Mona only // //=========================================================================== ECHOSTATUS CDspCommObject::GetDigitalInputAutoMute(BOOL &fAutoMute) { fAutoMute = m_fDigitalInAutoMute; ECHO_DEBUGPRINTF(("CDspCommObject::GetDigitalInputAutoMute %d\n",fAutoMute)); return ECHOSTATUS_OK; } ECHOSTATUS CDspCommObject::SetDigitalInputAutoMute(BOOL fAutoMute) { ECHO_DEBUGPRINTF(("CDspCommObject::SetDigitalInputAutoMute %d\n",fAutoMute)); // // Store the flag // m_fDigitalInAutoMute = fAutoMute; // // Re-set the input clock to the current value - indirectly causes the // auto-mute flag to be sent to the DSP // SetInputClock(m_wInputClock); return ECHOSTATUS_OK; } #endif // DIGITAL_INPUT_AUTO_MUTE_SUPPORT /**************************************************************************** Power management ****************************************************************************/ //=========================================================================== // // Tell the DSP to go into low-power mode // //=========================================================================== ECHOSTATUS CDspCommObject::GoComatose() { ECHO_DEBUGPRINTF(("CDspCommObject::GoComatose\n")); if (NULL != m_pwDspCode) { // // Make LoadFirmware do a complete reload // m_pwDspCode = NULL; // // Make sure that the sample rate get re-set on wakeup // (really only for Indigo and Mia) // m_pDspCommPage->dwControlReg = 0; // // Put the DSP to sleep // return SendVector(DSP_VC_GO_COMATOSE); } return ECHOSTATUS_OK; } // end of GoComatose #ifdef MIDI_SUPPORT /**************************************************************************** MIDI ****************************************************************************/ //=========================================================================== // // Send a buffer full of MIDI data to the DSP // //=========================================================================== ECHOSTATUS CDspCommObject::WriteMidi ( PBYTE pData, // Ptr to data buffer DWORD dwLength, // How many bytes to write PDWORD pdwActualCt // Return how many actually written ) { DWORD dwWriteCount,dwHandshake,dwStatus; BYTE *pOutBuffer; // // Return immediately if the handshake flag is clar // dwHandshake = GetHandshakeFlag(); if (0 == dwHandshake) { ECHO_DEBUGPRINTF(("CDCO::WriteMidi - can't write - handshake %ld\n",dwHandshake)); *pdwActualCt = 0; return ECHOSTATUS_BUSY; } // // Return immediately if HF4 is clear - HF4 indicates that it is safe // to write MIDI output data // dwStatus = GetDspRegister( CHI32_STATUS_REG ); if ( 0 == (dwStatus & CHI32_STATUS_REG_HF4 ) ) { ECHO_DEBUGPRINTF(("CDCO::WriteMidi - can't write - dwStatus 0x%lx\n",dwStatus)); *pdwActualCt = 0; return ECHOSTATUS_BUSY; } // // Copy data to the comm page; limit to the amount of space in the DSP output // FIFO and in the comm page // dwWriteCount = dwLength; if (dwWriteCount > (CP_MIDI_OUT_BUFFER_SIZE - 1)) { dwWriteCount = CP_MIDI_OUT_BUFFER_SIZE - 1; } ECHO_DEBUGPRINTF(("WriteMidi - dwWriteCount %ld\n",dwWriteCount)); *pdwActualCt = dwWriteCount; // Save the # of bytes written for the caller pOutBuffer = m_pDspCommPage->byMidiOutData; *pOutBuffer = (BYTE) dwWriteCount; pOutBuffer++; OsCopyMemory(pOutBuffer,pData,dwWriteCount); // // Send the command to the DSP // ClearHandshake(); m_pDspCommPage->dwMidiOutFreeCount = 0; SendVector( DSP_VC_MIDI_WRITE ); // // Save the current time - used to detect if MIDI out is currently busy // ULONGLONG ullTime; m_pOsSupport->OsGetSystemTime( &ullTime ); m_ullMidiOutTime = ullTime; return ECHOSTATUS_OK; } // ECHOSTATUS CDspCommObject::WriteMidi //=========================================================================== // // Called from the interrupt handler - get a MIDI input byte // //=========================================================================== ECHOSTATUS CDspCommObject::ReadMidi ( WORD wIndex, // Buffer index DWORD & dwData // Return data ) { if ( wIndex >= CP_MIDI_IN_BUFFER_SIZE ) return ECHOSTATUS_INVALID_INDEX; // // Get the data // dwData = SWAP( m_pDspCommPage->wMidiInData[ wIndex ] ); // // Timestamp for the MIDI input activity indicator // ULONGLONG ullTime; m_pOsSupport->OsGetSystemTime( &ullTime ); m_ullMidiInTime = ullTime; return ECHOSTATUS_OK; } // ECHOSTATUS CDspCommObject::ReadMidi ECHOSTATUS CDspCommObject::SetMidiOn( BOOL bOn ) { if ( bOn ) { if ( 0 == m_wMidiOnCount ) { if ( !WaitForHandshake() ) return ECHOSTATUS_DSP_DEAD; m_pDspCommPage->dwFlags |= SWAP( (DWORD) DSP_FLAG_MIDI_INPUT ); ClearHandshake(); SendVector( DSP_VC_UPDATE_FLAGS ); } m_wMidiOnCount++; } else { if ( m_wMidiOnCount == 0 ) return ECHOSTATUS_OK; if ( 0 == --m_wMidiOnCount ) { if ( !WaitForHandshake() ) return ECHOSTATUS_DSP_DEAD; m_pDspCommPage->dwFlags &= SWAP( (DWORD) ~DSP_FLAG_MIDI_INPUT ); ClearHandshake(); SendVector( DSP_VC_UPDATE_FLAGS ); } } return ECHOSTATUS_OK; } // ECHOSTATUS CDspCommObject::SetMidiOn //=========================================================================== // // Detect MIDI output activity // //=========================================================================== BOOL CDspCommObject::IsMidiOutActive() { ULONGLONG ullCurTime; m_pOsSupport->OsGetSystemTime( &ullCurTime ); return( ( ( ullCurTime - m_ullMidiOutTime ) > MIDI_ACTIVITY_TIMEOUT_USEC ) ? FALSE : TRUE ); } // BOOL CDspCommObject::IsMidiOutActive() #endif // MIDI_SUPPORT // **** CDspCommObject.cpp ****