DXFullDuplex.cxx

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2001-2004 MUSIC TECHNOLOGY GROUP (MTG)
00003  *                         UNIVERSITAT POMPEU FABRA
00004  *
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  */
00021 
00022 #define STRICT
00023 #include "CLAM_windows.h"
00024 #undef GetClassName
00025 #include <basetsd.h>
00026 #include <mmsystem.h>
00027 #include <mmreg.h>
00028 #include <dxerr8.h>
00029 #include <dinput.h>
00030 #include <cstdlib>
00031 #include <iostream>
00032 #include <algorithm>
00033 using std::copy;
00034 using std::cout;
00035 using std::endl;
00036 #include "DXFullDuplex.hxx"
00037 
00038 using namespace CLAM;
00039 
00040 HWND DXFullDuplex::shMainWnd = 0; // We initialize the handle
00041 
00042 DXFullDuplex::DXFullDuplex( TUInt32 irate, TByte ichannels, TSize latency, LPGUID pGUID )
00043         : mSampleRate ( irate ), mChannels ( ichannels ), mLatency ( latency ), mGUID( pGUID ),
00044           mDS( NULL ), mDSCapture( NULL ), mDSBPrimary( NULL ), mDSBOutput( NULL ), mDSBCapture( NULL ),
00045           mOutputBufferSize( 0 ), mCaptureBufferSize( 0 ), mNextCaptureOffset( 0 ), mNextOutputOffset( 0 ),
00046           mWritingToPrimary( false )
00047 {
00048         HRESULT hr;
00049 
00050         hr = InitDSoundDevices();
00051         
00052 
00053         CheckResult( "At DXFullDuplex constructor - InitDSoundDevices", hr );
00054 
00055         hr = CreateDXBuffers();
00056 
00057         CheckResult( "At DXFullDuplex constructor - CreateDXBuffers", hr );
00058 
00059 }
00060 
00061 DXFullDuplex::~DXFullDuplex()
00062 {
00063 
00064         // Releasing the buffers
00065         SafeRelease( mDSBPrimary );
00066         SafeRelease( mDSBOutput );
00067         SafeRelease( mDSBCapture );
00068         
00069         // Releasing the devices
00070         SafeRelease( mDSCapture );
00071         SafeRelease( mDS );
00072 }
00073 
00074 HRESULT DXFullDuplex::InitDSoundDevices( void )
00075 {
00076         HRESULT hr;
00077 
00078 
00079         // Obtaining the DirectSound interface
00080         hr = DirectSoundCreate( mGUID, &mDS, NULL );
00081         CheckResult( "Failed while obtaining a DirectSound interface", hr );
00082 
00083         hr = mDS->SetCooperativeLevel(  GetForegroundWindow(), DSSCL_WRITEPRIMARY );
00084 
00085         if ( hr == DS_OK)
00086         {
00087                 mWritingToPrimary = true;
00088 //              CLAM_WARNING( true, "Able to get the primary buffer for writing" );
00089         }
00090         else
00091         {
00092                 hr = mDS->SetCooperativeLevel( shMainWnd, DSSCL_PRIORITY );
00093                 CheckResult( "Failed while setting up the cooperative level", hr );
00094                 mWritingToPrimary = false;
00095 //              CLAM_WARNING( true, "Unable to get the primary buffer for writing" );
00096 
00097 
00098         }
00099 
00100         mDS->Compact(); // We compact on board memory
00101 
00102         // Obtaining the Capture Device interface
00103         
00104         hr = DirectSoundCaptureCreate( mGUID, &mDSCapture, NULL );
00105         CheckResult( "Failed while obtaining a DirectSoundCapture interface", hr );
00106 
00107         return S_OK;
00108 }
00109 
00110 HRESULT DXFullDuplex::CreateDXBuffers( void )
00111 {
00112         HRESULT       hr;
00113         DSBUFFERDESC  buff_descriptor;
00114         DSCBUFFERDESC capt_buff_descriptor;
00115         DSBCAPS       buff_caps;        // Buffer Capabilities Descriptor
00116         
00117 
00118         // Let's build the primary buffer
00119         // We obtain the desired format
00120         SetWaveFormat( &mOutputFormat );
00121         
00122         memset(&buff_descriptor, 0, sizeof(DSBUFFERDESC));
00123         buff_descriptor.dwSize = sizeof(DSBUFFERDESC); 
00124         buff_descriptor.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_LOCHARDWARE ; 
00125         // Buffer size is determined by sound hardware. 
00126         buff_descriptor.dwBufferBytes = 0; 
00127         buff_descriptor.lpwfxFormat = NULL; // Must be NULL for primary buffers
00128 
00129         hr = mDS->CreateSoundBuffer( &buff_descriptor, &mDSBPrimary, NULL );
00130         CheckResult( "Creating the Primary buffer", hr );
00131 
00132         hr = mDSBPrimary->SetFormat( &mOutputFormat );
00133         CheckResult( "Setting up the Primary buffer format", hr );
00134         
00135         
00136         if ( mWritingToPrimary )
00137         {
00138                 memset(&buff_caps, 0, sizeof(DSBUFFERDESC));
00139                 buff_caps.dwSize = sizeof(DSBCAPS); 
00140 
00141 //              CLAM_WARNING( true, "PrimaryBuffer configuration start" );
00142                 // We need to find out which was the primary buffer size
00143                 hr = mDSBPrimary->GetCaps( &buff_caps );
00144                 CheckResult( "Getting the buffer capabilities", hr );
00145 
00146                 mOutputBufferSize = mCaptureBufferSize = buff_caps.dwBufferBytes;
00147 
00148                 mDSBOutput = mDSBPrimary; // We will try using the primary buffer
00149 //              CLAM_WARNING( true, "PrimaryBuffer configuration end" );
00150 
00151         }
00152         else
00153         {
00154                 // Let's see which is the largest contigous audio hardware
00155                 // memory block
00156                 DSCAPS  dsound_caps;
00157 
00158                 memset( &dsound_caps, 0, sizeof(DSCAPS) );
00159                 dsound_caps.dwSize = sizeof( DSCAPS );
00160 
00161                 hr = mDS->GetCaps( &dsound_caps );
00162 
00163                 mOutputBufferSize = mCaptureBufferSize = 32768;
00164 
00165                 // We must create a secondary buffer
00166                 
00167                 memset( &buff_descriptor, 0, sizeof( DSBUFFERDESC ) );
00168                 buff_descriptor.dwSize = sizeof( DSBUFFERDESC );
00169                 buff_descriptor.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_LOCHARDWARE;
00170                 buff_descriptor.dwBufferBytes = mOutputBufferSize;
00171                 buff_descriptor.guid3DAlgorithm = GUID_NULL;
00172                 buff_descriptor.lpwfxFormat = &mOutputFormat;
00173 
00174                 hr = mDS->CreateSoundBuffer( &buff_descriptor, &mDSBOutput, NULL );
00175                 CheckResult( "Creating the Secondary buffer", hr );
00176         
00177         }
00178 
00179         // Now it is the moment for creating the capture buffer;
00180 
00181         SetWaveFormat( &mCaptureWaveFormat );
00182 
00183         memset( &capt_buff_descriptor, 0, sizeof( DSCBUFFERDESC ) );
00184         capt_buff_descriptor.dwSize = sizeof( DSCBUFFERDESC );
00185         capt_buff_descriptor.dwBufferBytes = mCaptureBufferSize; // 1sec
00186         capt_buff_descriptor.lpwfxFormat = &mCaptureWaveFormat;
00187 
00188         hr = mDSCapture->CreateCaptureBuffer( &capt_buff_descriptor, &mDSBCapture, NULL );
00189         CheckResult( "Creating the Capture (Input) buffer", hr );
00190 
00191         return S_OK;
00192 }
00193 
00194 void DXFullDuplex::CheckResult ( const char* msg, HRESULT hr )
00195 {
00196         if (FAILED(hr))
00197         {
00198                 throw ErrDXFullDuplex( msg, hr);
00199         }
00200 }
00201 
00202 HRESULT DXFullDuplex::Read( short* buf, TSize size)
00203 {
00204         HRESULT hr;
00205         VOID*        pDSLockedBuffer1 = NULL;
00206         DWORD        dwDSLockedBufferSize1;
00207         VOID*        pDSLockedBuffer2 = NULL;
00208         DWORD        dwDSLockedBufferSize2;
00209 
00210         DWORD blocksize = size*mChannels*2; /* size is in samples, blocksize in bytes */
00211         DWORD tmpPos = (mNextCaptureOffset - mLatency + mCaptureBufferSize)%mCaptureBufferSize;
00212         /* MDB: directx voodoo: Poll positions the nextcaptureoffset at mLatency before the capture
00213            current position. but we need to go even more back in time (mLatency), if not we get
00214            xruns...
00215         */
00216 
00217 
00218         hr = mDSBCapture->Lock( tmpPos, blocksize, 
00219                                                    &pDSLockedBuffer1, 
00220                                                    &dwDSLockedBufferSize1, 
00221                                                    &pDSLockedBuffer2, 
00222                                                    &dwDSLockedBufferSize2, 
00223                                                    0L );
00224 
00225         CheckResult("DirectSoundBuffer::Lock", hr );
00226 
00227         TSize midpoint = dwDSLockedBufferSize1 >> 1;
00228         TSize endpoint = dwDSLockedBufferSize2 >> 1;
00229 
00230         buf = copy( (short*)pDSLockedBuffer1, (short*)pDSLockedBuffer1 + midpoint, buf); 
00231         
00232         copy( (short*)pDSLockedBuffer2, (short*)pDSLockedBuffer2+endpoint, buf );
00233 
00234         mDSBCapture->Unlock(
00235                 pDSLockedBuffer1, dwDSLockedBufferSize1, 
00236                 pDSLockedBuffer2, dwDSLockedBufferSize2
00237         );
00238 
00239         mNextCaptureOffset += blocksize;
00240         mNextCaptureOffset %= mCaptureBufferSize;
00241 
00242         return S_OK;
00243 }
00244 
00245 HRESULT DXFullDuplex::Write(short* buf,TSize size)
00246 {
00247         HRESULT hr;
00248         VOID*        pDSLockedBuffer1 = NULL;
00249         DWORD        dwDSLockedBufferSize1;
00250         VOID*        pDSLockedBuffer2 = NULL;
00251         DWORD        dwDSLockedBufferSize2;
00252 
00253         DWORD blocksize = size*mChannels*2; /* size is in samples, blocksize in bytes */
00254         bool were_restored;
00255 
00256         hr = RestoreBuffer( mDSBOutput, were_restored );
00257         CheckResult("Restoring Output Buffer",hr);
00258 
00259         hr = mDSBOutput->Lock( mNextOutputOffset, blocksize, 
00260                                                    &pDSLockedBuffer1, 
00261                                                    &dwDSLockedBufferSize1, 
00262                                                    &pDSLockedBuffer2, 
00263                                                    &dwDSLockedBufferSize2, 
00264                                                    0L );
00265 
00266         CheckResult("DirectSoundBuffer::Lock", hr );
00267 
00268         TSize midpoint = dwDSLockedBufferSize1 >> 1;
00269         TSize endpoint = dwDSLockedBufferSize2 >> 1;
00270 
00271         copy( buf, buf + midpoint, (short*)pDSLockedBuffer1 ); 
00272         
00273         copy( buf+midpoint, buf+midpoint+endpoint, (short*)pDSLockedBuffer2 );
00274 
00275 
00276         // Unlock the play buffer
00277         mDSBOutput->Unlock(
00278                 pDSLockedBuffer1, dwDSLockedBufferSize1, 
00279                 pDSLockedBuffer2, dwDSLockedBufferSize2
00280         );
00281 
00282         mNextOutputOffset += blocksize;
00283         mNextOutputOffset %= mOutputBufferSize;
00284 
00285         return S_OK;
00286 }
00287 
00288 // Output buffers format
00289 void DXFullDuplex::SetWaveFormat( WAVEFORMATEX* pwfx )
00290 {
00291         memset( pwfx, 0, sizeof( WAVEFORMATEX ) );
00292 
00293         pwfx->wFormatTag = WAVE_FORMAT_PCM;
00294         pwfx->nChannels = mChannels;
00295         pwfx->nSamplesPerSec = mSampleRate;
00296         pwfx->wBitsPerSample = 16;
00297         pwfx->nBlockAlign = pwfx->nChannels * ( pwfx->wBitsPerSample / 8 );
00298         pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
00299 }
00300 
00301 HRESULT DXFullDuplex::Poll( void )
00302 {
00303 /* We poll on the input cursor: we check if we have latency data available.
00304 */
00305         DWORD dist;
00306         
00307         do
00308         {
00309                 
00310                 DWORD pos;
00311                 mDSBCapture->GetCurrentPosition(&pos,0);
00312                 dist = pos >= mNextCaptureOffset ? pos - mNextCaptureOffset : pos + mCaptureBufferSize- mNextCaptureOffset;
00313 
00314 /* MDB: This might be an alternative solution using realtime priority settings for the
00315         threads ( see BaseAudioApplication::SAudioThread method )
00316 */              
00317                 if ( dist < mLatency )
00318                 {
00319                         Sleep( 1 );
00320                 }
00321                 
00322         } while (dist<mLatency);
00323 
00324         return S_OK;            
00325 }
00326 
00327 HRESULT DXFullDuplex::Start( void )
00328 {
00329         HRESULT hr;
00330         
00331         mNextCaptureOffset = 0;
00332         mNextOutputOffset = mLatency*3; 
00333         // MDB: More DirectX voodoo. Why do we need to position 
00334         // the output cursor mLatency*3 forward? With smaller values, we get xruns....
00335 
00336         // Restore lost buffers
00337         bool were_restored;
00338 
00339         hr = RestoreBuffer( mDSBOutput, were_restored );
00340         CheckResult("Restoring Output Buffer",hr);
00341 
00342         mDSBOutput->SetCurrentPosition(0);
00343         mDSBCapture->GetCurrentPosition(&mNextCaptureOffset,0);
00344 
00345         hr = mDSBCapture->Start( DSCBSTART_LOOPING );
00346         CheckResult("Start Capture",hr);
00347 
00348         hr = mDSBOutput->Play( 0, 0, DSBPLAY_LOOPING );
00349         CheckResult("Start Output",hr);
00350 
00351         return S_OK;
00352 }
00353 
00354 HRESULT DXFullDuplex::RestoreBuffer( LPDIRECTSOUNDBUFFER pDSBuffer, bool& pbRestored )
00355 {
00356         HRESULT hr;
00357 
00358         pbRestored = false;
00359 
00360         if( !pDSBuffer )
00361                 return S_FALSE;
00362 
00363         DWORD dwStatus;
00364         hr = pDSBuffer->GetStatus( &dwStatus );
00365         CheckResult("At DXFullDuplex::RestoreBuffer GetStatus", hr );
00366 
00367         if( dwStatus & DSBSTATUS_BUFFERLOST )
00368         {
00369         // Since the app could have just been activated, then
00370         // DirectSound may not be giving us control yet, so 
00371         // the restoring the buffer may fail.  
00372         // If it does, sleep until DirectSound gives us control.
00373                 hr = pDSBuffer->Restore();
00374                 while ( hr != DS_OK )
00375                         {
00376                                 Sleep( 10 );
00377                                 hr = pDSBuffer->Restore();
00378                         }
00379 
00380                 
00381                 pbRestored = true;
00382 
00383                 return S_OK;
00384         }
00385         else
00386         {
00387                 return S_FALSE;
00388         }
00389 }
00390 

Generated on Tue Aug 12 22:33:42 2008 for CLAM by  doxygen 1.5.5