PAAudioDevice.cxx

Go to the documentation of this file.
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 

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