// **************************************************************************** // // CEchoGals_transport.cpp // // Audio transport methods for the CEchoGals driver class. // Set editor tabs to 3 for your viewing pleasure. // // ---------------------------------------------------------------------------- // // This file is part of Echo Digital Audio's generic driver library. // Copyright Echo Digital Audio Corporation (c) 1998 - 2005 // All rights reserved // www.echoaudio.com // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // **************************************************************************** #include "CEchoGals.h" /****************************************************************************** Functions for opening and closing pipes ******************************************************************************/ //=========================================================================== // // OpenAudio is used to reserve audio pipes for your exclusive use. The call // will fail if someone else has already opened the pipes. Calling OpenAudio // is the first step if you want to play or record. // // If the fCheckHardware flag is true, then the open will fail // if the DSP and ASIC firmware have not been loaded (usually means // your external box is turned off). // //=========================================================================== ECHOSTATUS CEchoGals::OpenAudio ( PECHOGALS_OPENAUDIOPARAMETERS pOpenParameters, // Info on pipe PWORD pwPipeIndex, // Pipe index ptr BOOL fCheckHardware, CDaffyDuck *pDuck ) { CChannelMask cmMask; WORD wPipeMax, wPipe, wPipeIndex, i, wWidth; ECHOSTATUS Status; ECHO_DEBUGPRINTF( ("CEchoGals::OpenAudio: %s %u " "PipeWidth %d " "Cyclic %u \n", ( pOpenParameters->Pipe.bIsInput ) ? "Input" : "Output", pOpenParameters->Pipe.nPipe, pOpenParameters->Pipe.wInterleave, pOpenParameters->bIsCyclic) ); *pwPipeIndex = (WORD) -1; // So it's never undefined // // Make sure the hardware is OK // if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) { ECHO_DEBUGPRINTF( ("\tECHOSTATUS_DSP_DEAD\n") ); return ECHOSTATUS_DSP_DEAD; } // // Make sure the DSP & ASIC are up and running // - only if fCheckHardware is true // if (fCheckHardware) { Status = GetDspCommObject()->LoadFirmware(); if ( ECHOSTATUS_OK != Status ) return Status; } // // Validate the pipe number // wPipe = pOpenParameters->Pipe.nPipe; wWidth = pOpenParameters->Pipe.wInterleave; if ( pOpenParameters->Pipe.bIsInput ) { wPipeIndex = wPipe + GetNumPipesOut(); wPipeMax = GetNumPipesIn(); } else { wPipeIndex = wPipe; wPipeMax = GetNumPipesOut(); } if ( ( wPipe + wWidth ) > wPipeMax ) { ECHO_DEBUGPRINTF( ("\tECHOSTATUS_INVALID_CHANNEL\n") ); return ECHOSTATUS_INVALID_CHANNEL; } // // If the width is more than two, make sure that this card // can handle super interleave // if ( (0 == (m_wFlags & ECHOGALS_ROFLAG_SUPER_INTERLEAVE_OK)) && (wWidth > 2) ) { ECHO_DEBUGPRINTF( ("\tECHOSTATUS_NO_SUPER_INTERLEAVE\n") ); return ECHOSTATUS_NO_SUPER_INTERLEAVE; } // // See if the specified pipes are already open // for ( i = 0; i < pOpenParameters->Pipe.wInterleave; i++ ) { cmMask.SetIndexInMask( wPipeIndex + i ); } if ( m_cmAudioOpen.Test( &cmMask ) ) { ECHO_DEBUGPRINTF( ("OpenAudio - ECHOSTATUS_CHANNEL_ALREADY_OPEN - m_cmAudioOpen 0x%x cmMask 0x%x\n", m_cmAudioOpen.GetMask(),cmMask.GetMask()) ); return ECHOSTATUS_CHANNEL_ALREADY_OPEN; } #ifdef AUTO_DUCK_ALLOCATE // // Make a daffy duck // if (NULL == pDuck) { pDuck = CDaffyDuck::MakeDaffyDuck(m_pOsSupport); if (NULL == pDuck) return ECHOSTATUS_NO_MEM; } SetDaffyDuck( wPipeIndex, pDuck ); #else // // Use the specified duck if one was passed in // if (NULL != pDuck) SetDaffyDuck( wPipeIndex, pDuck ); #endif // // Reset the 64-bit DMA position // ResetDmaPos(wPipeIndex); GetDspCommObject()->ResetPipePosition(wPipeIndex); // // Prep stuff // m_cmAudioOpen += cmMask; if ( pOpenParameters->bIsCyclic ) m_cmAudioCyclic += cmMask; m_Pipes[ wPipeIndex ] = pOpenParameters->Pipe; *pwPipeIndex = wPipeIndex; m_ProcessId[ wPipeIndex ] = pOpenParameters->ProcessId; Reset( wPipeIndex ); ECHO_DEBUGPRINTF( ("OpenAudio - ECHOSTATUS_OK - m_cmAudioOpen 0x%x\n",m_cmAudioOpen.GetMask()) ); return ECHOSTATUS_OK; } // ECHOSTATUS CEchoGals::OpenAudio //=========================================================================== // // CloseAudio is, naturally, the inverse of OpenAudio. // //=========================================================================== ECHOSTATUS CEchoGals::CloseAudio ( PECHOGALS_CLOSEAUDIOPARAMETERS pCloseParameters, BOOL fFreeDuck ) { CChannelMask cmMask; ECHOSTATUS Status; WORD i; WORD wPipeIndex; wPipeIndex = pCloseParameters->wPipeIndex; ECHO_DEBUGPRINTF( ("CEchoGals::CloseAudio: Pipe %u ", wPipeIndex) ); Status = VerifyAudioOpen( wPipeIndex ); if ( ECHOSTATUS_OK != Status ) return Status; for ( i = 0; i < m_Pipes[ wPipeIndex ].wInterleave; i++ ) { cmMask.SetIndexInMask( wPipeIndex + i ); } Reset( wPipeIndex ); // // Free the scatter-gather list // if (NULL != m_DaffyDucks[wPipeIndex]) { if (fFreeDuck) delete m_DaffyDucks[wPipeIndex]; m_DaffyDucks[wPipeIndex] = NULL; } m_cmAudioOpen -= cmMask; m_cmAudioCyclic -= cmMask; m_ProcessId[ wPipeIndex ] = NULL; m_Pipes[ wPipeIndex ].wInterleave = 0; ECHO_DEBUGPRINTF( ("CloseAudio - ECHOSTATUS_OK - m_cmAudioOpen 0x%x\n",m_cmAudioOpen.GetMask()) ); return ECHOSTATUS_OK; } // ECHOSTATUS CEchoGals::CloseAudio //=========================================================================== // // VerifyAudioOpen is a utility function; it tells you if // a pipe is open or not. // //=========================================================================== ECHOSTATUS CEchoGals::VerifyAudioOpen ( WORD wPipeIndex ) { CChannelMask cmMask; if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) { ECHO_DEBUGPRINTF( ("\tECHOSTATUS_DSP_DEAD\n") ); return ECHOSTATUS_DSP_DEAD; } cmMask.SetIndexInMask( wPipeIndex ); if ( !( m_cmAudioOpen.Test( &cmMask ) ) ) { ECHO_DEBUGPRINTF( ("VerifyAudioOpen - ECHOSTATUS_CHANNEL_NOT_OPEN - wPipeIndex %d - m_cmAudioOpen 0x%x - cmMask 0x%x\n", wPipeIndex,m_cmAudioOpen.GetMask(),cmMask.GetMask()) ); return ECHOSTATUS_CHANNEL_NOT_OPEN; } return ECHOSTATUS_OK; } // ECHOSTATUS CEchoGals::VerifyAudioOpen //=========================================================================== // // GetActivePipes tells you which pipes are currently active; that is, which // pipes are currently playing or recording. // //=========================================================================== ECHOSTATUS CEchoGals::GetActivePipes ( PCChannelMask pChannelMask ) { if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) return ECHOSTATUS_DSP_DEAD; GetDspCommObject()->GetActivePipes( pChannelMask ); return ECHOSTATUS_OK; } // void CEchoGals::GetActivePipes() //=========================================================================== // // Just like GetActivePipes, but this one tells you which pipes are currently // open. // //=========================================================================== ECHOSTATUS CEchoGals::GetOpenPipes ( PCChannelMask pChannelMask ) { if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) return ECHOSTATUS_DSP_DEAD; *pChannelMask = m_cmAudioOpen; return ECHOSTATUS_OK; } // void CEchoGals::GetOpenPipes() /****************************************************************************** Functions for setting audio formats and the sample rate ******************************************************************************/ //=========================================================================== // // Validate an audio format. // // For comments on audio formats, refer to the definition of // ECHOGALS_AUDIOFORMAT. // //=========================================================================== ECHOSTATUS CEchoGals::QueryAudioFormat ( WORD wPipeIndex, PECHOGALS_AUDIOFORMAT pAudioFormat ) { ECHOSTATUS Status = ECHOSTATUS_OK; ECHO_DEBUGPRINTF( ("CEchoGals::QueryAudioFormat:\n") ); // // If this pipe is open, make sure that this audio format // does not exceed the stored pipe width // WORD wInterleave = pAudioFormat->wDataInterleave; WORD wStoredPipeWidth = m_Pipes[ wPipeIndex ].wInterleave; if (0 != wStoredPipeWidth) { if (wInterleave > wStoredPipeWidth) { ECHO_DEBUGPRINTF(("CEchoGals::QueryAudioFormat - pipe was opened " "with a width of %d; interleave of %d invalid.\n", wStoredPipeWidth, pAudioFormat->wDataInterleave)); return ECHOSTATUS_BAD_FORMAT; } } // // Check for super interleave (i.e. interleave > 2) // if (wInterleave > 2) { // // Make sure the card is capable of super interleave // if (0 == (m_wFlags & ECHOGALS_ROFLAG_SUPER_INTERLEAVE_OK)) return ECHOSTATUS_NO_SUPER_INTERLEAVE; // // Interleave must be even & data must be little endian // if ( (0 != pAudioFormat->byDataAreBigEndian) || (0 != (wInterleave & 1) ) ) return ECHOSTATUS_BAD_FORMAT; // // 16, 24, or 32 bit samples are required // if ( (32 != pAudioFormat->wBitsPerSample) && (24 != pAudioFormat->wBitsPerSample) && (16 != pAudioFormat->wBitsPerSample) ) return ECHOSTATUS_BAD_FORMAT; // // Make sure that this interleave factor on this pipe // does not exceed the number of pipes for the card // WORD wMaxPipe; if (wPipeIndex >= GetNumPipesOut()) { wMaxPipe = GetNumPipesIn(); wPipeIndex = wPipeIndex - GetNumPipesOut(); } else { wMaxPipe = GetNumPipesOut(); } if ( (wPipeIndex + wInterleave) > wMaxPipe) return ECHOSTATUS_BAD_FORMAT; return ECHOSTATUS_OK; } // // Check the interleave // if ( (1 != pAudioFormat->wDataInterleave) && (2 != pAudioFormat->wDataInterleave) ) { ECHO_DEBUGPRINTF( ("CEchoGals::QueryAudioFormat - interleave %d not allowed\n", pAudioFormat->wDataInterleave)); return ECHOSTATUS_BAD_FORMAT; } // // If the big endian flag is set, the data must be mono or stereo interleave, // 32 bits wide, left justified data. Only the upper 24 bits are used. // if (pAudioFormat->byDataAreBigEndian) { // // Must have 32 bits per sample // if (pAudioFormat->wBitsPerSample != 32) { ECHO_DEBUGPRINTF(("CEchoGals::QueryAudioFormat - Only 32 bits per" " sample supported for big-endian data\n")); return ECHOSTATUS_BAD_FORMAT; } // // Mono or stereo only // switch (pAudioFormat->wDataInterleave) { #ifdef STEREO_BIG_ENDIAN32_SUPPORT case 1 : case 2 : break; #else case 1 : break; #endif default : ECHO_DEBUGPRINTF(("CEchoGals::QueryAudioFormat - Interleave of %d" " not allowed for big-endian data\n", pAudioFormat->wDataInterleave)); return ECHOSTATUS_BAD_FORMAT; } return ECHOSTATUS_OK; } // // Check bits per sample // switch ( pAudioFormat->wBitsPerSample ) { case 8 : case 16 : case 24 : case 32 : break; default : ECHO_DEBUGPRINTF( ("CEchoGals::QueryAudioFormat: No valid format " "specified, bits per sample %d\n", pAudioFormat->wBitsPerSample) ); Status = ECHOSTATUS_BAD_FORMAT; break; } return Status; } // ECHOSTATUS CEchoGals::QueryAudioFormat //=========================================================================== // // SetAudioFormat sets the format of the audio data in host memory // for this pipe. // //=========================================================================== ECHOSTATUS CEchoGals::SetAudioFormat ( WORD wPipeIndex, PECHOGALS_AUDIOFORMAT pAudioFormat ) { ECHOSTATUS Status; ECHO_DEBUGPRINTF( ("CEchoGals::SetAudioFormat: " "for pipe %d\n", wPipeIndex) ); // // Make sure this pipe is open // Status = VerifyAudioOpen( wPipeIndex ); if ( ECHOSTATUS_OK != Status ) return Status; // // Check the format // Status = QueryAudioFormat( wPipeIndex, pAudioFormat ); if ( ECHOSTATUS_OK != Status ) return Status; // // Set the format // Status = GetDspCommObject()->SetAudioFormat( wPipeIndex, pAudioFormat ); return Status; } // ECHOSTATUS CEchoGals::SetAudioFormat - single pipe //=========================================================================== // // This call lets you set the audio format for several pipes at once. // //=========================================================================== ECHOSTATUS CEchoGals::SetAudioFormat ( PCChannelMask pChannelMask, PECHOGALS_AUDIOFORMAT pAudioFormat ) { WORD wPipeIndex = 0xffff; ECHOSTATUS Status; if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) { ECHO_DEBUGPRINTF( ("\tECHOSTATUS_DSP_DEAD\n") ); return ECHOSTATUS_DSP_DEAD; } for ( ; ; ) { wPipeIndex = pChannelMask->GetIndexFromMask( ++wPipeIndex ); if ( (WORD) ECHO_INVALID_CHANNEL == wPipeIndex ) break; // We be done! // // See if this pipe is open // if ( !( m_cmAudioOpen.TestIndexInMask( wPipeIndex ) ) ) { ECHO_DEBUGPRINTF( ("CEchoGals::SetAudioFormat: " "for pipe %d failed, pipe not open\n", wPipeIndex) ); return ECHOSTATUS_CHANNEL_NOT_OPEN; } // // See if the format is OK // ECHO_DEBUGPRINTF( ("CEchoGals::SetAudioFormat: " "for pipe %d\n", wPipeIndex) ); Status = QueryAudioFormat( wPipeIndex, pAudioFormat ); if ( ECHOSTATUS_OK != Status ) return Status; // // Set the format for this pipe // Status = GetDspCommObject()->SetAudioFormat( wPipeIndex, pAudioFormat ); if ( ECHOSTATUS_OK != Status ) return Status; } return ECHOSTATUS_OK; } // ECHOSTATUS CEchoGals::SetAudioFormat - multiple pipes //=========================================================================== // // GetAudioFormat returns the current audio format for a pipe. // //=========================================================================== ECHOSTATUS CEchoGals::GetAudioFormat ( WORD wPipeIndex, PECHOGALS_AUDIOFORMAT pAudioFormat ) { ECHO_DEBUGPRINTF( ("CEchoGals::GetAudioFormat: " "for pipe %d\n", wPipeIndex) ); GetDspCommObject()->GetAudioFormat( wPipeIndex, pAudioFormat ); return ECHOSTATUS_OK; } // ECHOSTATUS CEchoGals::GetAudioFormat //=========================================================================== // // This function does exactly what you think it does. // // Note that if the card is not set to internal clock (that is, the hardware // is synced to word clock or some such), this call has no effect. // // Note that all of the inputs and outputs on a single card share the same // clock. // //=========================================================================== ECHOSTATUS CEchoGals::SetAudioSampleRate ( DWORD dwSampleRate ) { ECHOSTATUS Status; ECHO_DEBUGPRINTF( ("CEchoGals::SetAudioSampleRate: " "to %ld Hz\n", dwSampleRate) ); // // Check to see if the sample rate is locked // if ( 0 != (m_wFlags & ECHOGALS_FLAG_SAMPLE_RATE_LOCKED)) { return ECHOSTATUS_OK; } else { Status = QueryAudioSampleRate( dwSampleRate ); if ( ECHOSTATUS_OK != Status ) return Status; if ( dwSampleRate == GetDspCommObject()->SetSampleRate( dwSampleRate ) ) { m_dwSampleRate = dwSampleRate; return ECHOSTATUS_OK; } } return ECHOSTATUS_BAD_FORMAT; } // ECHOSTATUS CEchoGals::SetAudioSampleRate //=========================================================================== // // GetAudioSampleRate - retrieves the current sample rate for the hardware // //=========================================================================== ECHOSTATUS CEchoGals::GetAudioSampleRate ( PDWORD pdwSampleRate ) { ECHO_DEBUGPRINTF( ("CEchoGals::GetAudioSampleRate\n")); *pdwSampleRate = m_dwSampleRate; return ECHOSTATUS_OK; } // ECHOSTATUS CEchoGals::GetAudioSampleRate /****************************************************************************** Functions related to the scatter-gather list ******************************************************************************/ //=========================================================================== // // Use the given CDaffyDuck object as the scatter-gather list for this pipe // //=========================================================================== ECHOSTATUS CEchoGals::SetDaffyDuck(WORD wPipeIndex, CDaffyDuck *pDuck) { m_DaffyDucks[wPipeIndex] = pDuck; return ECHOSTATUS_OK; } // SetDaffyDuck //=========================================================================== // // This method returns a pointer to the daffy duck for a pipe; the caller // can then have direct access to the daffy duck object. // //=========================================================================== CDaffyDuck *CEchoGals::GetDaffyDuck(WORD wPipeIndex) { ECHO_DEBUGPRINTF(("CEchoGals::GetDaffyDuck for pipe index %d\n",wPipeIndex)); if (wPipeIndex >= GetNumPipes()) return NULL; return m_DaffyDucks[wPipeIndex]; } /****************************************************************************** Functions for starting and stopping transport ******************************************************************************/ //=========================================================================== // // Start transport for a single pipe // //=========================================================================== ECHOSTATUS CEchoGals::Start ( WORD wPipeIndex ) { CChannelMask cmMask; cmMask.SetIndexInMask( wPipeIndex ); return Start( &cmMask ); } // ECHOSTATUS CEchoGals::Start //=========================================================================== // // Start transport for a group of pipes // // This function includes logic to sync-start several pipes at once, // according to the process ID specified when the pipe was opened. This is // included to work around a limitation of the Windows wave API so that // programs could use multiple inputs and outputs and have them start at the // same time. // // If you don't want to use this feature, call CEchoGals::ClearFlags // with ECHOGALS_FLAG_SYNCH_WAVE and the pipes will start immediately. // //=========================================================================== ECHOSTATUS CEchoGals::Start ( PCChannelMask pChannelMask ) { WORD wPipe; DWORD dwPhysStartAddr; CChannelMask cmStart; PVOID ProcessId = NULL; CDspCommObject *pDCO; pDCO = GetDspCommObject(); if ( NULL == pDCO || pDCO->IsBoardBad() ) return ECHOSTATUS_DSP_DEAD; // // See if we are dealing with synchronized wave pipes. If the sync // flag is set, get the process ID for this pipe to compare with // other pipes. // if ( GetFlags() & ECHOGALS_FLAG_SYNCH_WAVE ) { wPipe = pChannelMask->GetIndexFromMask( 0 ); ProcessId = m_ProcessId[ wPipe ]; } //-------------------------------------------------------- // Process each pipe in the mask //-------------------------------------------------------- for (wPipe = 0; wPipe < GetNumPipes(); wPipe++) { PDWORD pdwDspCommPositions; // // Skip this pipe if it's not in the mask // if (!pChannelMask->TestIndexInMask(wPipe)) continue; // // This pipe must have a CDaffyDuck object // if (NULL == m_DaffyDucks[ wPipe ]) { ECHO_DEBUGPRINTF(("CDaffyDuck::Start - trying to start pipe index %d " "but there is no CDaffyDuck!\n",wPipe)); return ECHOSTATUS_CHANNEL_NOT_OPEN; } // // If this pipe was opened in cyclic mode, make sure that the corresponding // CDaffyDuck has been wrapped // if ( (0 != m_cmAudioCyclic.TestIndexInMask( wPipe ) ) && (FALSE == m_DaffyDucks[wPipe]->Wrapped()) ) { ECHO_DEBUGPRINTF(("CEchoGals::Start called for pipe index %d - " "pipe was opened in cyclic mode, but the duck " "has not been wrapped\n",wPipe)); return ECHOSTATUS_DUCK_NOT_WRAPPED; } // // Set the physical start address for the duck for this pipe // dwPhysStartAddr = m_DaffyDucks[wPipe]->GetPhysStartAddr(); pDCO->SetAudioDuckListPhys( wPipe, dwPhysStartAddr ); // // Do different things to this pipe depending on the // state // switch (m_byPipeState[wPipe]) { case PIPE_STATE_RESET : // // Clean up the DMA position stuff // pdwDspCommPositions = pDCO->GetAudioPositionPtr(); pdwDspCommPositions[ wPipe ] = 0; // // If this pipe isn't synced or is in a reset state, // start it up // if (NULL == ProcessId) { m_byPipeState[ wPipe ] = PIPE_STATE_STARTED; cmStart.SetIndexInMask( wPipe ); } else { // // This pipe is synced; upgrade to PENDING // m_byPipeState[ wPipe ] = PIPE_STATE_PENDING; } break; case PIPE_STATE_STOPPED : if (NULL == ProcessId) { // // Non-synced pipe; start 'er up! // m_byPipeState[ wPipe ] = PIPE_STATE_STARTED; cmStart.SetIndexInMask( wPipe ); } else { // // Synced pipe; if this pipe is in STOP mode, // upgrade it to PENDING status // m_byPipeState[ wPipe ] = PIPE_STATE_PENDING; } break; case PIPE_STATE_PENDING : case PIPE_STATE_STARTED : break; } } //----------------------------------------------------------------- // Start the pipes //----------------------------------------------------------------- // // Don't go if all the synced pipes are not yet pending // BOOL fAllReady = TRUE; for ( wPipe = 0; wPipe < GetNumPipes(); wPipe++ ) { if ( ( ProcessId == m_ProcessId[ wPipe ] ) && ( PIPE_STATE_STOPPED == m_byPipeState[wPipe])) { ECHO_DEBUGPRINTF(("CEchoGals::Start - can't start; pipe %d " "still set to state %d\n", wPipe, m_byPipeState[wPipe])); fAllReady = FALSE; } } // // All synced pipes are pending; time to go! // if (fAllReady) { for (wPipe = 0; wPipe < GetNumPipes(); wPipe++) { if ( (ProcessId == m_ProcessId[ wPipe ]) && (PIPE_STATE_PENDING == m_byPipeState[ wPipe ])) { m_byPipeState[wPipe] = PIPE_STATE_STARTED; cmStart.SetIndexInMask( wPipe ); ECHO_DEBUGPRINTF(("CEchoGals::Start - setting pipe %d to start\n", wPipe)); } } } if ( cmStart.IsEmpty() ) return ECHOSTATUS_OK; //----------------------------------------------------------------- // Time to go //----------------------------------------------------------------- return pDCO->StartTransport( &cmStart ); } // ECHOSTATUS CEchoGals::Start //=========================================================================== // // Stop a single pipe // //=========================================================================== ECHOSTATUS CEchoGals::Stop ( WORD wPipeIndex ) { CChannelMask cmMask; cmMask.SetIndexInMask( wPipeIndex ); return( Stop( &cmMask ) ); } // ECHOSTATUS CEchoGals::Stop //=========================================================================== // // Stop several pipes simultaneously // //=========================================================================== ECHOSTATUS CEchoGals::Stop ( PCChannelMask pChannelMask ) { INT32 i; ECHOSTATUS Status; if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) return ECHOSTATUS_DSP_DEAD; Status = GetDspCommObject()->StopTransport( pChannelMask ); if ( ECHOSTATUS_OK != Status ) return Status; for ( i = 0; i < GetNumPipes(); i++ ) { // // Skip channel if not in mask // if ( !pChannelMask->TestIndexInMask( (WORD) i ) ) continue; // // Don't bother if it's stopped already // if ( PIPE_STATE_STOPPED == m_byPipeState[ i ] ) continue; // // Muck with the DMA position // UpdateDmaPos( (WORD) i ); m_byPipeState[ i ] = PIPE_STATE_STOPPED; } return Status; } // ECHOSTATUS CEchoGals::Stop //=========================================================================== // // Reset transport for a single pipe // //=========================================================================== ECHOSTATUS CEchoGals::Reset ( WORD wPipeIndex ) { CChannelMask cmMask; cmMask.SetIndexInMask( wPipeIndex ); return Reset( &cmMask ); } // ECHOSTATUS CEchoGals::Reset //=========================================================================== // // Reset transport for a group of pipes simultaneously // //=========================================================================== ECHOSTATUS CEchoGals::Reset ( PCChannelMask pChannelMask ) { WORD i; ECHOSTATUS Status; if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) return ECHOSTATUS_DSP_DEAD; Status = GetDspCommObject()->ResetTransport( pChannelMask ); if ( ECHOSTATUS_OK != Status ) return Status; for ( i = 0; i < GetNumPipes(); i++ ) { // // Skip channel if not in mask // if ( !pChannelMask->TestIndexInMask( (WORD) i ) ) continue; if ( PIPE_STATE_RESET == m_byPipeState[ i ] ) continue; // // Muck with the DMA position // UpdateDmaPos( i ); m_dwLastDspPos[ i ] = 0; GetDspCommObject()->ResetPipePosition(i); m_byPipeState[ i ] = PIPE_STATE_RESET; } return Status; } // ECHOSTATUS CEchoGals::Reset /****************************************************************************** Functions for handling the current DMA position for pipes; the DMA position is the number of bytes transported by a pipe. ******************************************************************************/ //=========================================================================== // // The DSP sends back a 32 bit DMA counter for each pipe of the number of bytes // transported; this count is written by the DSP to the comm page without // the driver doing anything. // // The driver then maintains a 64 bit counter based off of the DSP's counter. // // Call UpdateDmaPos to cause the driver to update the internal 64 bit DMA // counter. // // The 64 bit DMA counter is in units of bytes, not samples. // //=========================================================================== void CEchoGals::UpdateDmaPos( WORD wPipeIndex ) { DWORD dwDspPos; DWORD dwDelta; // // Get the current DSP position and find out how much it // has moved since last time. This is necessary to avoid // the 32 bit counter wrapping around. // dwDspPos = GetDspCommObject()->GetAudioPosition( wPipeIndex ); dwDelta = dwDspPos - m_dwLastDspPos[ wPipeIndex ]; // // Adjust the 64 bit position // m_ullDmaPos[ wPipeIndex ] += dwDelta; m_dwLastDspPos[ wPipeIndex ] = dwDspPos; } // UpdateDmaPos //=========================================================================== // // ResetDmaPos resets the 64 bit DMA counter. // //=========================================================================== void CEchoGals::ResetDmaPos(WORD wPipe) { m_ullDmaPos[ wPipe ] = 0; m_dwLastDspPos[ wPipe ] = 0; // // There may still be mappings in the daffy duck; if so, // tell them to reset their DMA positions starting at zero // if (NULL != m_DaffyDucks[wPipe]) m_DaffyDucks[wPipe]->ResetStartPos(); } //=========================================================================== // // This is a very powerful feature; calling this function gives you a pointer // to the memory location where the DSP writes the 32 bit DMA position for // a pipe. The DSP is constantly updating this value as it moves data. // // Since the DSP is constantly updating it, you can dereference this pointer // from anywhere and read the DMA position without calling the generic driver. // This means that with some adroit mapping, you could read the DMA position // from user mode without calling the kernel. // // Remember, Peter - with great power comes great responsibility; you should // only read this pointer and never write to it or to anywhere around it. You // could easily lock up your computer. // // Note that the DSP writes the position in little endian format; if you are // on a big endian machine, you will need to byte swap the postion // before you use it. // //=========================================================================== ECHOSTATUS CEchoGals::GetAudioPositionPtr ( WORD wPipeIndex, PDWORD & pdwPosition ) { if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() ) return ECHOSTATUS_DSP_DEAD; if (wPipeIndex >= GetNumPipes()) { pdwPosition = NULL; return ECHOSTATUS_INVALID_CHANNEL; } PDWORD pdwDspCommPos = GetDspCommObject()->GetAudioPositionPtr(); pdwPosition = pdwDspCommPos + wPipeIndex; return ECHOSTATUS_OK; } // ECHOSTATUS CEchoGals::GetAudioPositionPtr