CLAM-Development
1.1
|
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