CLAM-Development
1.1
|
00001 /* 00002 * Copyright (c) 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 #include "PACLAMCallbacks.hxx" 00023 #include "CLAM_windows.h" 00024 #undef GetClassName 00025 #include <iostream> 00026 using std::cout; 00027 using std::endl; 00028 00029 #include "AudioIO.hxx" 00030 #include "AudioDevice.hxx" 00031 #include "AudioDeviceList.hxx" 00032 #include <portaudio.h> 00033 #include "PortAudioUtils.hxx" 00034 #include "PAAudioInputStream.hxx" 00035 #include "PAAudioOutputStream.hxx" 00036 #include "PAAudioFullDuplexStream.hxx" 00037 00038 #include "DoubleBuffer.hxx" 00039 00040 namespace CLAM 00041 { 00042 00043 class PAAudioDevice : public AudioDevice 00044 { 00045 enum IOModalities 00046 { 00047 eFullDuplex=0, 00048 eHalfDuplexIn, 00049 eHalfDuplexOut, 00050 eNoneYet 00051 }; 00052 00053 public: 00054 00055 PAAudioDevice( const std::string& str, PaDeviceIndex devID ); 00056 ~PAAudioDevice( ); 00057 00058 void Start() throw( ErrPortAudio ); 00059 void Stop() throw ( ErrPortAudio ); 00060 void Read( Audio& samples, const int channelID ); 00061 void Write( const Audio& samples, const int channelID ); 00062 00063 virtual void GetInfo( AudioDevice::TInfo& ); 00064 00065 protected: 00066 00067 void SetupMonoOutputStream() throw( ErrPortAudio ); 00068 void SetupStereoOutputStream() throw( ErrPortAudio ); 00069 void SetupMultiOutputStream() throw( ErrPortAudio ); 00070 void SetupMonoInputStream() throw( ErrPortAudio ); 00071 void SetupStereoInputStream() throw( ErrPortAudio ); 00072 void SetupMultiInputStream() throw( ErrPortAudio ); 00073 void SetupStereoFullDuplexStream() throw( ErrPortAudio ); 00074 00075 private: 00076 00077 unsigned mNChannelsWritten; 00078 bool mChannelsWritten[256]; 00079 00080 unsigned mNChannelsRead; 00081 bool mChannelsRead[256]; 00082 00083 unsigned mWriteBuffSize; 00084 unsigned mReadBuffSize; 00085 00086 PaDeviceIndex mDevID; 00087 00088 DoubleBuffer mInputIntBuffer; 00089 DoubleBuffer mOutputIntBuffer; 00090 00091 PAAudioInputStream* mInStream; 00092 PAAudioOutputStream* mOutStream; 00093 PAAudioFullDuplexStream* mFullDuplexStream; 00094 00095 IOModalities mIOModel ; 00096 00097 bool mStarted; 00098 }; 00099 00100 00101 class PAAudioDeviceList : public AudioDeviceList 00102 { 00103 private: 00104 static PAAudioDeviceList mDevices; 00105 00106 PAAudioDeviceList(); 00107 00108 std::vector< PaDeviceIndex > mDevIDs; 00109 00110 protected: 00111 00112 void EnumerateAvailableDevices(); 00113 00114 public: 00115 00116 virtual ~PAAudioDeviceList(); 00117 00118 inline std::string DefaultDevice() 00119 { 00120 return mAvailableDevices[0]; 00121 } 00122 00123 AudioDevice* Create( const std::string& name, const std::string& device ); 00124 00125 }; 00126 00127 PAAudioDevice::PAAudioDevice( const std::string& name, PaDeviceIndex devID ) 00128 : AudioDevice(name), mDevID( devID ), 00129 mInStream( NULL ), mOutStream( NULL ), mIOModel( eNoneYet ),mStarted(false) 00130 { 00131 00132 // MRJ: Read/Written Channel Masks initialization 00133 int i; 00134 00135 mNChannelsWritten = 0; 00136 for (i=0;i<256;i++) 00137 { 00138 mChannelsWritten[i] = false; 00139 } 00140 00141 mNChannelsRead = 0; 00142 for (i=0;i<256;i++) 00143 { 00144 mChannelsRead[i] = false; 00145 } 00146 00147 } 00148 00149 PAAudioDevice::~PAAudioDevice( ) 00150 { 00151 mInputIntBuffer.DeAllocate(); 00152 mOutputIntBuffer.DeAllocate(); 00153 00154 if ( mInStream ) 00155 delete mInStream; 00156 00157 if ( mOutStream ) 00158 delete mOutStream; 00159 00160 if ( mFullDuplexStream ) 00161 delete mFullDuplexStream; 00162 } 00163 00164 void PAAudioDevice::Start() throw( Err ) 00165 { 00166 if ( mStarted && (((mInputs.size() | mOutputs.size()) != mNChannels) || 00167 (mInputIntBuffer.GetSize() != mInputs.size()*Latency()) || 00168 (mOutputIntBuffer.GetSize() != mOutputs.size()*Latency()))) // evil workaround start 00169 { 00170 switch ( mIOModel ) 00171 { 00172 case eFullDuplex: 00173 mFullDuplexStream->Stop(); 00174 delete mFullDuplexStream; 00175 break; 00176 case eHalfDuplexIn: 00177 mInStream->Stop(); 00178 delete mInStream; 00179 break; 00180 case eHalfDuplexOut: 00181 mOutStream->Stop(); 00182 delete mOutStream; 00183 break; 00184 case eNoneYet: 00185 CLAM_ASSERT( false, "You cannot stop what hasn't been started" ); 00186 break; 00187 } 00188 mStarted = false; 00189 } // evil workaround end 00190 00191 if ( mStarted ) 00192 return; 00193 00194 unsigned outChannels = mOutputs.size(); 00195 unsigned inChannels = mInputs.size(); 00196 00197 try 00198 { 00199 // Check for Full Duplex I/O 00200 if ( (outChannels > 0) && (inChannels > 0) && (outChannels==inChannels) ) 00201 { 00202 // both outChannels and inChannels have the same value 00203 // and are above zero 00204 mIOModel = eFullDuplex; 00205 mNChannels = outChannels; 00206 switch ( outChannels ) 00207 { 00208 case 1: 00209 break; 00210 case 2: 00211 SetupStereoFullDuplexStream(); 00212 break; 00213 default: 00214 break; 00215 }; 00216 00217 mFullDuplexStream->Start(); 00218 } 00219 // Check for Half Duplex Ouput 00220 else if ( (inChannels == 0) && (outChannels > 0)) 00221 { 00222 mIOModel = eHalfDuplexOut; 00223 mNChannels = outChannels; 00224 // depending on outChannels value 00225 switch ( outChannels ) 00226 { 00227 case 1: 00228 SetupMonoOutputStream(); 00229 break; 00230 case 2: 00231 SetupStereoOutputStream(); 00232 break; 00233 default: 00234 SetupMultiOutputStream(); 00235 break; 00236 }; 00237 00238 mOutStream->Start(); 00239 } 00240 // Check for Half Duplex Input 00241 else if ( (outChannels == 0) && (inChannels > 0)) 00242 { 00243 mIOModel = eHalfDuplexIn; 00244 // depending on the value of inChannels 00245 mNChannels = inChannels; 00246 switch( inChannels ) 00247 { 00248 case 1: 00249 SetupMonoInputStream(); 00250 break; 00251 case 2: 00252 SetupStereoInputStream(); 00253 break; 00254 default: 00255 SetupMultiInputStream(); 00256 break; 00257 }; 00258 mInStream->Start(); 00259 } 00260 else 00261 { 00262 // Error. None of the supported I/O modalities. 00263 CLAM_ASSERT( false, "Check the number of AudioIn's and AudioOut's in your system.\n" 00264 "I have detected a input output mismatch( either you have none input and some outputs or\n" 00265 " some input or none output or just the same number of inputs and outputs)"); 00266 } 00267 mStarted = true; 00268 } 00269 catch( Err& e ) 00270 { 00271 ErrPortAudio new_e( "Error Starting PortAudio Device:\n" ); 00272 new_e.Embed( e ); 00273 throw ( new_e ); 00274 } 00275 } 00276 00277 void PAAudioDevice::SetupMonoOutputStream() throw ( ErrPortAudio ) 00278 { 00279 PAAudioStreamConfig cfg; 00280 cfg.SetSampleRate( SampleRate() ); 00281 cfg.SetChannelNumber( 1 ); 00282 cfg.SetCallback( monoOutCallback ); 00283 cfg.SetDeviceID( mDevID ); 00284 mOutputIntBuffer.DeAllocate(); 00285 mOutputIntBuffer.Allocate( Latency() ); 00286 cfg.SetOutputDblBuffer( &mOutputIntBuffer); 00287 00288 mOutStream = new PAAudioOutputStream( cfg ); 00289 00290 } 00291 00292 void PAAudioDevice::SetupStereoOutputStream() throw ( ErrPortAudio ) 00293 { 00294 PAAudioStreamConfig cfg; 00295 cfg.SetSampleRate( SampleRate() ); 00296 cfg.SetChannelNumber( 2 ); 00297 cfg.SetCallback( stereoOutCallback ); 00298 cfg.SetDeviceID( mDevID ); 00299 mOutputIntBuffer.DeAllocate(); 00300 mOutputIntBuffer.Allocate( 2 * Latency() ); 00301 cfg.SetOutputDblBuffer( &mOutputIntBuffer); 00302 00303 mOutStream = new PAAudioOutputStream( cfg ); 00304 00305 } 00306 00307 void PAAudioDevice::SetupMultiOutputStream() throw( ErrPortAudio ) 00308 { 00309 PAAudioStreamConfig cfg; 00310 cfg.SetSampleRate( SampleRate() ); 00311 cfg.SetChannelNumber( mNChannels ); 00312 cfg.SetCallback( multiOutCallback ); 00313 cfg.SetDeviceID( mDevID ); 00314 mOutputIntBuffer.DeAllocate(); 00315 mOutputIntBuffer.Allocate( mNChannels * Latency() ); 00316 cfg.SetOutputDblBuffer( &mOutputIntBuffer); 00317 00318 mOutStream = new PAAudioOutputStream( cfg ); 00319 00320 } 00321 00322 void PAAudioDevice::SetupMonoInputStream() throw ( ErrPortAudio ) 00323 { 00324 PAAudioStreamConfig cfg; 00325 cfg.SetSampleRate( SampleRate() ); 00326 cfg.SetChannelNumber( 1 ); 00327 cfg.SetCallback( monoInCallback ); 00328 cfg.SetDeviceID( mDevID ); 00329 mInputIntBuffer.DeAllocate(); 00330 mInputIntBuffer.Allocate( Latency() ); 00331 cfg.SetInputDblBuffer( &mInputIntBuffer); 00332 00333 mInStream = new PAAudioInputStream( cfg ); 00334 00335 } 00336 00337 void PAAudioDevice::SetupStereoInputStream() throw ( ErrPortAudio ) 00338 { 00339 PAAudioStreamConfig cfg; 00340 cfg.SetSampleRate( SampleRate() ); 00341 cfg.SetChannelNumber( 2 ); 00342 cfg.SetCallback( stereoInCallback ); 00343 cfg.SetDeviceID( mDevID ); 00344 mInputIntBuffer.DeAllocate(); 00345 mInputIntBuffer.Allocate( 2*Latency() ); 00346 cfg.SetInputDblBuffer( &mInputIntBuffer); 00347 00348 mInStream = new PAAudioInputStream( cfg ); 00349 00350 } 00351 00352 void PAAudioDevice::SetupMultiInputStream() throw ( ErrPortAudio ) 00353 { 00354 PAAudioStreamConfig cfg; 00355 cfg.SetSampleRate( SampleRate() ); 00356 cfg.SetChannelNumber( mInputs.size() ); 00357 cfg.SetCallback( multiInCallback ); 00358 cfg.SetDeviceID( mDevID ); 00359 mInputIntBuffer.DeAllocate(); 00360 mInputIntBuffer.Allocate( mInputs.size()*Latency() ); 00361 cfg.SetInputDblBuffer( &mInputIntBuffer); 00362 00363 mInStream = new PAAudioInputStream( cfg ); 00364 00365 } 00366 00367 void PAAudioDevice::SetupStereoFullDuplexStream() throw ( ErrPortAudio ) 00368 { 00369 PAAudioStreamConfig cfg; 00370 cfg.SetSampleRate( SampleRate() ); 00371 cfg.SetChannelNumber( mNChannels ); 00372 cfg.SetCallback( stereoFDCallback ); 00373 cfg.SetDeviceID( mDevID ); 00374 mInputIntBuffer.DeAllocate(); 00375 mOutputIntBuffer.DeAllocate(); 00376 mInputIntBuffer.Allocate( 2*Latency() ); 00377 mOutputIntBuffer.Allocate( 2*Latency() ); 00378 cfg.SetInputDblBuffer( &mInputIntBuffer); 00379 cfg.SetOutputDblBuffer( &mOutputIntBuffer ); 00380 00381 mFullDuplexStream = new PAAudioFullDuplexStream( cfg ); 00382 00383 } 00384 00385 00386 void PAAudioDevice::Stop() throw( ErrPortAudio ) 00387 { 00388 switch ( mIOModel ) 00389 { 00390 case eFullDuplex: 00391 mFullDuplexStream->Stop(); 00392 break; 00393 case eHalfDuplexIn: 00394 mInStream->Stop(); 00395 break; 00396 case eHalfDuplexOut: 00397 mOutStream->Stop(); 00398 break; 00399 case eNoneYet: 00400 CLAM_ASSERT( false, "You cannot stop what hasn't been started" ); 00401 break; 00402 } 00403 mStarted = false; 00404 } 00405 00406 void PAAudioDevice::GetInfo( AudioDevice::TInfo& nfo ) 00407 { 00408 AudioDevice::GetInfo( nfo ); 00409 } 00410 00411 void PAAudioDevice::Read( Audio& samples, const int channelID ) 00412 { 00413 TData* dst = samples.GetBuffer().GetPtr(); 00414 short* src = NULL; 00415 unsigned samples_to_read = (mInputIntBuffer.GetSize()/mInputs.size()); 00416 00417 // Some error checking - proof of concepts 00418 CLAM_ASSERT( !mChannelsRead[channelID], "Tried to read twice from the same channel in single time frame!" ); 00419 CLAM_ASSERT( mInStream || mFullDuplexStream, "No Input stream over the device has been created yet!" ); 00420 CLAM_ASSERT( samples_to_read == samples.GetSize(), "Inconsistent Audio size" ); 00421 00422 static TData inv_2_15 = 1 / TData(32767.); 00423 00424 WaitForSingleObject( mInputIntBuffer.mBackBufferReady, INFINITE ); 00425 00426 src = mInputIntBuffer.mBackBuffer + channelID; 00427 00428 while ( samples_to_read-- ) 00429 { 00430 *dst++ = TData(*src)*inv_2_15; 00431 src+=mNChannels; 00432 } 00433 mChannelsRead[channelID] = true; 00434 mNChannelsRead++; 00435 00436 if (mNChannelsRead==mInputs.size()) 00437 { 00438 mNChannelsRead = 0; 00439 // for (int i=0;i<mInputs.size();i++) 00440 // mChannelsRead[i] = false; 00441 std::fill(mChannelsRead, mChannelsRead + mInputs.size(), false); 00442 ResetEvent( mInputIntBuffer.mBackBufferReady ); 00443 } 00444 00445 00446 } 00447 00448 void PAAudioDevice::Write( const Audio& samples, const int channelID ) 00449 { 00450 unsigned samples_to_write = (mOutputIntBuffer.GetSize()/mNChannels); 00451 00452 CLAM_ASSERT( !mChannelsWritten[channelID], "Tried to read twice from the same channel in single time frame!" ); 00453 CLAM_ASSERT( mOutStream || mFullDuplexStream, "No Input stream over the device has been created yet!" ); 00454 CLAM_ASSERT( samples_to_write == samples.GetSize(), "Inconsistent Audio size" ); 00455 00456 short* dst = mOutputIntBuffer.mFrontBuffer + channelID; 00457 TData* src = samples.GetBuffer().GetPtr(); 00458 00459 while ( samples_to_write-- ) 00460 { 00461 *dst = (short) ( 32767. * (*src++)); 00462 dst+=mNChannels; 00463 } 00464 mChannelsWritten[channelID] = true; 00465 mNChannelsWritten++; 00466 00467 if (mNChannelsWritten==mOutputs.size()) 00468 { 00469 00470 00471 mNChannelsWritten = 0; 00472 // for (int i=0;i<mOutputs.size();i++) 00473 // mChannelsWritten[i] = false; 00474 std::fill(mChannelsWritten, mChannelsWritten + mOutputs.size(), false); 00475 00476 mOutputIntBuffer.SwapBuffers(); 00477 } 00478 00479 00480 } 00481 00482 PAAudioDeviceList::PAAudioDeviceList() 00483 : AudioDeviceList( std::string("portaudio") ) 00484 { 00485 Pa_Initialize(); 00486 00487 EnumerateAvailableDevices(); 00488 00489 AddMe(); 00490 } 00491 00492 PAAudioDeviceList::~PAAudioDeviceList() 00493 { 00494 Pa_Terminate(); 00495 } 00496 00497 void PAAudioDeviceList::EnumerateAvailableDevices() 00498 { 00499 int numDevs = Pa_GetDeviceCount(); 00500 int k = 0; 00501 const PaDeviceInfo* devnfo = NULL; 00502 00503 for ( k = 0; k < numDevs; k++ ) 00504 { 00505 devnfo = Pa_GetDeviceInfo( k ); 00506 mAvailableDevices.push_back( devnfo->name ); 00507 mDevIDs.push_back( k ); 00508 } 00509 00510 } 00511 00512 AudioDevice* PAAudioDeviceList::Create( const std::string& name, const std::string& device ) 00513 { 00514 int i = 0; 00515 00516 for ( i=0; i < mAvailableDevices.size(); i++ ) 00517 { 00518 if ( device == mAvailableDevices[i] ) 00519 return new PAAudioDevice( name, mDevIDs[i] ); 00520 } 00521 00522 return 0; 00523 } 00524 00525 PAAudioDeviceList PAAudioDeviceList::mDevices; 00526 } 00527