Home   Class/Enum List   File List   Compound Members  

RtAudio.h

Go to the documentation of this file.
00001 /************************************************************************/
00039 /************************************************************************/
00040 
00045 // RtAudio: Version 4.0.8
00046 
00047 #ifndef __RTAUDIO_H
00048 #define __RTAUDIO_H
00049 
00050 #include <string>
00051 #include <vector>
00052 #include "RtError.h"
00053 
00072 typedef unsigned long RtAudioFormat;
00073 static const RtAudioFormat RTAUDIO_SINT8 = 0x1;    // 8-bit signed integer.
00074 static const RtAudioFormat RTAUDIO_SINT16 = 0x2;   // 16-bit signed integer.
00075 static const RtAudioFormat RTAUDIO_SINT24 = 0x4;   // Lower 3 bytes of 32-bit signed integer.
00076 static const RtAudioFormat RTAUDIO_SINT32 = 0x8;   // 32-bit signed integer.
00077 static const RtAudioFormat RTAUDIO_FLOAT32 = 0x10; // Normalized between plus/minus 1.0.
00078 static const RtAudioFormat RTAUDIO_FLOAT64 = 0x20; // Normalized between plus/minus 1.0.
00079 
00122 typedef unsigned int RtAudioStreamFlags;
00123 static const RtAudioStreamFlags RTAUDIO_NONINTERLEAVED = 0x1;    // Use non-interleaved buffers (default = interleaved).
00124 static const RtAudioStreamFlags RTAUDIO_MINIMIZE_LATENCY = 0x2;  // Attempt to set stream parameters for lowest possible latency.
00125 static const RtAudioStreamFlags RTAUDIO_HOG_DEVICE = 0x4;        // Attempt grab device and prevent use by others.
00126 static const RtAudioStreamFlags RTAUDIO_SCHEDULE_REALTIME = 0x8; // Try to select realtime scheduling for callback thread.
00127 static const RtAudioStreamFlags RTAUDIO_ALSA_USE_DEFAULT = 0x10; // Use the "default" PCM device (ALSA only).
00128 
00140 typedef unsigned int RtAudioStreamStatus;
00141 static const RtAudioStreamStatus RTAUDIO_INPUT_OVERFLOW = 0x1;    // Input data was discarded because of an overflow condition at the driver.
00142 static const RtAudioStreamStatus RTAUDIO_OUTPUT_UNDERFLOW = 0x2;  // The output buffer ran low, likely causing a gap in the output sound.
00143 
00145 
00183 typedef int (*RtAudioCallback)( void *outputBuffer, void *inputBuffer,
00184                                 unsigned int nFrames,
00185                                 double streamTime,
00186                                 RtAudioStreamStatus status,
00187                                 void *userData );
00188 
00189 
00190 // **************************************************************** //
00191 //
00192 // RtAudio class declaration.
00193 //
00194 // RtAudio is a "controller" used to select an available audio i/o
00195 // interface.  It presents a common API for the user to call but all
00196 // functionality is implemented by the class RtApi and its
00197 // subclasses.  RtAudio creates an instance of an RtApi subclass
00198 // based on the user's API choice.  If no choice is made, RtAudio
00199 // attempts to make a "logical" API selection.
00200 //
00201 // **************************************************************** //
00202 
00203 class RtApi;
00204 
00205 class RtAudio
00206 {
00207  public:
00208 
00210   enum Api {
00211     UNSPECIFIED,    
00212     LINUX_ALSA,     
00213     LINUX_OSS,      
00214     UNIX_JACK,      
00215     MACOSX_CORE,    
00216     WINDOWS_ASIO,   
00217     WINDOWS_DS,     
00218     RTAUDIO_DUMMY   
00219   };
00220 
00222   struct DeviceInfo {
00223     bool probed;                  
00224     std::string name;             
00225     unsigned int outputChannels;  
00226     unsigned int inputChannels;   
00227     unsigned int duplexChannels;  
00228     bool isDefaultOutput;         
00229     bool isDefaultInput;          
00230     std::vector<unsigned int> sampleRates; 
00231     RtAudioFormat nativeFormats;  
00233     // Default constructor.
00234     DeviceInfo()
00235       :probed(false), outputChannels(0), inputChannels(0), duplexChannels(0),
00236        isDefaultOutput(false), isDefaultInput(false), nativeFormats(0) {}
00237   };
00238 
00240   struct StreamParameters {
00241     unsigned int deviceId;     
00242     unsigned int nChannels;    
00243     unsigned int firstChannel; 
00245     // Default constructor.
00246     StreamParameters()
00247       : deviceId(0), nChannels(0), firstChannel(0) {}
00248   };
00249 
00251 
00307   struct StreamOptions {
00308     RtAudioStreamFlags flags;      
00309     unsigned int numberOfBuffers;  
00310     std::string streamName;        
00311     int priority;                  
00313     // Default constructor.
00314     StreamOptions()
00315     : flags(0), numberOfBuffers(0), priority(0) {}
00316   };
00317 
00319 
00324   static void getCompiledApi( std::vector<RtAudio::Api> &apis ) throw();
00325 
00327 
00335   RtAudio( RtAudio::Api api=UNSPECIFIED ) throw();
00336 
00338 
00342   ~RtAudio() throw();
00343 
00345   RtAudio::Api getCurrentApi( void ) throw();
00346 
00348 
00353   unsigned int getDeviceCount( void ) throw();
00354 
00356 
00366   RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
00367 
00369 
00376   unsigned int getDefaultOutputDevice( void ) throw();
00377 
00379 
00386   unsigned int getDefaultInputDevice( void ) throw();
00387 
00389 
00426   void openStream( RtAudio::StreamParameters *outputParameters,
00427                    RtAudio::StreamParameters *inputParameters,
00428                    RtAudioFormat format, unsigned int sampleRate,
00429                    unsigned int *bufferFrames, RtAudioCallback callback,
00430                    void *userData = NULL, RtAudio::StreamOptions *options = NULL );
00431 
00433 
00437   void closeStream( void ) throw();
00438 
00440 
00446   void startStream( void );
00447 
00449 
00455   void stopStream( void );
00456 
00458 
00464   void abortStream( void );
00465 
00467   bool isStreamOpen( void ) const throw();
00468 
00470   bool isStreamRunning( void ) const throw();
00471 
00473 
00476   double getStreamTime( void );
00477 
00479 
00487   long getStreamLatency( void );
00488 
00490 
00495   unsigned int getStreamSampleRate( void );
00496 
00498   void showWarnings( bool value = true ) throw();
00499 
00500  protected:
00501 
00502   void openRtApi( RtAudio::Api api );
00503   RtApi *rtapi_;
00504 };
00505 
00506 // Operating system dependent thread functionality.
00507 #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__)
00508   #include <windows.h>
00509   #include <process.h>
00510 
00511   typedef unsigned long ThreadHandle;
00512   typedef CRITICAL_SECTION StreamMutex;
00513 
00514 #elif defined(__LINUX_ALSA__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)
00515   // Using pthread library for various flavors of unix.
00516   #include <pthread.h>
00517 
00518   typedef pthread_t ThreadHandle;
00519   typedef pthread_mutex_t StreamMutex;
00520 
00521 #else // Setup for "dummy" behavior
00522 
00523   #define __RTAUDIO_DUMMY__
00524   typedef int ThreadHandle;
00525   typedef int StreamMutex;
00526 
00527 #endif
00528 
00529 // This global structure type is used to pass callback information
00530 // between the private RtAudio stream structure and global callback
00531 // handling functions.
00532 struct CallbackInfo {
00533   void *object;    // Used as a "this" pointer.
00534   ThreadHandle thread;
00535   void *callback;
00536   void *userData;
00537   void *apiInfo;   // void pointer for API specific callback information
00538   bool isRunning;
00539 
00540   // Default constructor.
00541   CallbackInfo()
00542     :object(0), callback(0), userData(0), apiInfo(0), isRunning(false) {}
00543 };
00544 
00545 // **************************************************************** //
00546 //
00547 // RtApi class declaration.
00548 //
00549 // Subclasses of RtApi contain all API- and OS-specific code necessary
00550 // to fully implement the RtAudio API.
00551 //
00552 // Note that RtApi is an abstract base class and cannot be
00553 // explicitly instantiated.  The class RtAudio will create an
00554 // instance of an RtApi subclass (RtApiOss, RtApiAlsa,
00555 // RtApiJack, RtApiCore, RtApiAl, RtApiDs, or RtApiAsio).
00556 //
00557 // **************************************************************** //
00558 
00559 #if defined( HAVE_GETTIMEOFDAY )
00560   #include <sys/time.h>
00561 #endif
00562 
00563 #include <sstream>
00564 
00565 class RtApi
00566 {
00567 public:
00568 
00569   RtApi();
00570   virtual ~RtApi();
00571   virtual RtAudio::Api getCurrentApi( void ) = 0;
00572   virtual unsigned int getDeviceCount( void ) = 0;
00573   virtual RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) = 0;
00574   virtual unsigned int getDefaultInputDevice( void );
00575   virtual unsigned int getDefaultOutputDevice( void );
00576   void openStream( RtAudio::StreamParameters *outputParameters,
00577                    RtAudio::StreamParameters *inputParameters,
00578                    RtAudioFormat format, unsigned int sampleRate,
00579                    unsigned int *bufferFrames, RtAudioCallback callback,
00580                    void *userData, RtAudio::StreamOptions *options );
00581   virtual void closeStream( void );
00582   virtual void startStream( void ) = 0;
00583   virtual void stopStream( void ) = 0;
00584   virtual void abortStream( void ) = 0;
00585   long getStreamLatency( void );
00586   unsigned int getStreamSampleRate( void );
00587   virtual double getStreamTime( void );
00588   bool isStreamOpen( void ) const { return stream_.state != STREAM_CLOSED; };
00589   bool isStreamRunning( void ) const { return stream_.state == STREAM_RUNNING; };
00590   void showWarnings( bool value ) { showWarnings_ = value; };
00591 
00592 
00593 protected:
00594 
00595   static const unsigned int MAX_SAMPLE_RATES;
00596   static const unsigned int SAMPLE_RATES[];
00597 
00598   enum { FAILURE, SUCCESS };
00599 
00600   enum StreamState {
00601     STREAM_STOPPED,
00602     STREAM_RUNNING,
00603     STREAM_CLOSED = -50
00604   };
00605 
00606   enum StreamMode {
00607     OUTPUT,
00608     INPUT,
00609     DUPLEX,
00610     UNINITIALIZED = -75
00611   };
00612 
00613   // A protected structure used for buffer conversion.
00614   struct ConvertInfo {
00615     int channels;
00616     int inJump, outJump;
00617     RtAudioFormat inFormat, outFormat;
00618     std::vector<int> inOffset;
00619     std::vector<int> outOffset;
00620   };
00621 
00622   // A protected structure for audio streams.
00623   struct RtApiStream {
00624     unsigned int device[2];    // Playback and record, respectively.
00625     void *apiHandle;           // void pointer for API specific stream handle information
00626     StreamMode mode;           // OUTPUT, INPUT, or DUPLEX.
00627     StreamState state;         // STOPPED, RUNNING, or CLOSED
00628     char *userBuffer[2];       // Playback and record, respectively.
00629     char *deviceBuffer;
00630     bool doConvertBuffer[2];   // Playback and record, respectively.
00631     bool userInterleaved;
00632     bool deviceInterleaved[2]; // Playback and record, respectively.
00633     bool doByteSwap[2];        // Playback and record, respectively.
00634     unsigned int sampleRate;
00635     unsigned int bufferSize;
00636     unsigned int nBuffers;
00637     unsigned int nUserChannels[2];    // Playback and record, respectively.
00638     unsigned int nDeviceChannels[2];  // Playback and record channels, respectively.
00639     unsigned int channelOffset[2];    // Playback and record, respectively.
00640     unsigned long latency[2];         // Playback and record, respectively.
00641     RtAudioFormat userFormat;
00642     RtAudioFormat deviceFormat[2];    // Playback and record, respectively.
00643     StreamMutex mutex;
00644     CallbackInfo callbackInfo;
00645     ConvertInfo convertInfo[2];
00646     double streamTime;         // Number of elapsed seconds since the stream started.
00647 
00648 #if defined(HAVE_GETTIMEOFDAY)
00649     struct timeval lastTickTimestamp;
00650 #endif
00651 
00652     RtApiStream()
00653       :apiHandle(0), deviceBuffer(0) { device[0] = 11111; device[1] = 11111; }
00654   };
00655 
00656   typedef signed short Int16;
00657   typedef signed int Int32;
00658   typedef float Float32;
00659   typedef double Float64;
00660 
00661   std::ostringstream errorStream_;
00662   std::string errorText_;
00663   bool showWarnings_;
00664   RtApiStream stream_;
00665 
00673   virtual bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 
00674                                 unsigned int firstChannel, unsigned int sampleRate,
00675                                 RtAudioFormat format, unsigned int *bufferSize,
00676                                 RtAudio::StreamOptions *options );
00677 
00679   void tickStreamTime( void );
00680 
00682   void clearStreamInfo();
00683 
00688   void verifyStream( void );
00689 
00691   void error( RtError::Type type );
00692 
00697   void convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info );
00698 
00700   void byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format );
00701 
00703   unsigned int formatBytes( RtAudioFormat format );
00704 
00706   void setConvertInfo( StreamMode mode, unsigned int firstChannel );
00707 };
00708 
00709 // **************************************************************** //
00710 //
00711 // Inline RtAudio definitions.
00712 //
00713 // **************************************************************** //
00714 
00715 inline RtAudio::Api RtAudio :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); }
00716 inline unsigned int RtAudio :: getDeviceCount( void ) throw() { return rtapi_->getDeviceCount(); }
00717 inline RtAudio::DeviceInfo RtAudio :: getDeviceInfo( unsigned int device ) { return rtapi_->getDeviceInfo( device ); }
00718 inline unsigned int RtAudio :: getDefaultInputDevice( void ) throw() { return rtapi_->getDefaultInputDevice(); }
00719 inline unsigned int RtAudio :: getDefaultOutputDevice( void ) throw() { return rtapi_->getDefaultOutputDevice(); }
00720 inline void RtAudio :: closeStream( void ) throw() { return rtapi_->closeStream(); }
00721 inline void RtAudio :: startStream( void ) { return rtapi_->startStream(); }
00722 inline void RtAudio :: stopStream( void )  { return rtapi_->stopStream(); }
00723 inline void RtAudio :: abortStream( void ) { return rtapi_->abortStream(); }
00724 inline bool RtAudio :: isStreamOpen( void ) const throw() { return rtapi_->isStreamOpen(); }
00725 inline bool RtAudio :: isStreamRunning( void ) const throw() { return rtapi_->isStreamRunning(); }
00726 inline long RtAudio :: getStreamLatency( void ) { return rtapi_->getStreamLatency(); }
00727 inline unsigned int RtAudio :: getStreamSampleRate( void ) { return rtapi_->getStreamSampleRate(); };
00728 inline double RtAudio :: getStreamTime( void ) { return rtapi_->getStreamTime(); }
00729 inline void RtAudio :: showWarnings( bool value ) throw() { rtapi_->showWarnings( value ); }
00730 
00731 // RtApi Subclass prototypes.
00732 
00733 #if defined(__MACOSX_CORE__)
00734 
00735 #include <CoreAudio/AudioHardware.h>
00736 
00737 class RtApiCore: public RtApi
00738 {
00739 public:
00740 
00741   RtApiCore();
00742   ~RtApiCore();
00743   RtAudio::Api getCurrentApi( void ) { return RtAudio::MACOSX_CORE; };
00744   unsigned int getDeviceCount( void );
00745   RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
00746   unsigned int getDefaultOutputDevice( void );
00747   unsigned int getDefaultInputDevice( void );
00748   void closeStream( void );
00749   void startStream( void );
00750   void stopStream( void );
00751   void abortStream( void );
00752   long getStreamLatency( void );
00753 
00754   // This function is intended for internal use only.  It must be
00755   // public because it is called by the internal callback handler,
00756   // which is not a member of RtAudio.  External use of this function
00757   // will most likely produce highly undesireable results!
00758   bool callbackEvent( AudioDeviceID deviceId,
00759                       const AudioBufferList *inBufferList,
00760                       const AudioBufferList *outBufferList );
00761 
00762   private:
00763 
00764   bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 
00765                         unsigned int firstChannel, unsigned int sampleRate,
00766                         RtAudioFormat format, unsigned int *bufferSize,
00767                         RtAudio::StreamOptions *options );
00768   static const char* getErrorCode( OSStatus code );
00769 };
00770 
00771 #endif
00772 
00773 #if defined(__UNIX_JACK__)
00774 
00775 class RtApiJack: public RtApi
00776 {
00777 public:
00778 
00779   RtApiJack();
00780   ~RtApiJack();
00781   RtAudio::Api getCurrentApi( void ) { return RtAudio::UNIX_JACK; };
00782   unsigned int getDeviceCount( void );
00783   RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
00784   void closeStream( void );
00785   void startStream( void );
00786   void stopStream( void );
00787   void abortStream( void );
00788   long getStreamLatency( void );
00789 
00790   // This function is intended for internal use only.  It must be
00791   // public because it is called by the internal callback handler,
00792   // which is not a member of RtAudio.  External use of this function
00793   // will most likely produce highly undesireable results!
00794   bool callbackEvent( unsigned long nframes );
00795 
00796   private:
00797 
00798   bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 
00799                         unsigned int firstChannel, unsigned int sampleRate,
00800                         RtAudioFormat format, unsigned int *bufferSize,
00801                         RtAudio::StreamOptions *options );
00802 };
00803 
00804 #endif
00805 
00806 #if defined(__WINDOWS_ASIO__)
00807 
00808 class RtApiAsio: public RtApi
00809 {
00810 public:
00811 
00812   RtApiAsio();
00813   ~RtApiAsio();
00814   RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_ASIO; };
00815   unsigned int getDeviceCount( void );
00816   RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
00817   void closeStream( void );
00818   void startStream( void );
00819   void stopStream( void );
00820   void abortStream( void );
00821   long getStreamLatency( void );
00822 
00823   // This function is intended for internal use only.  It must be
00824   // public because it is called by the internal callback handler,
00825   // which is not a member of RtAudio.  External use of this function
00826   // will most likely produce highly undesireable results!
00827   bool callbackEvent( long bufferIndex );
00828 
00829   private:
00830 
00831   std::vector<RtAudio::DeviceInfo> devices_;
00832   void saveDeviceInfo( void );
00833   bool coInitialized_;
00834   bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 
00835                         unsigned int firstChannel, unsigned int sampleRate,
00836                         RtAudioFormat format, unsigned int *bufferSize,
00837                         RtAudio::StreamOptions *options );
00838 };
00839 
00840 #endif
00841 
00842 #if defined(__WINDOWS_DS__)
00843 
00844 class RtApiDs: public RtApi
00845 {
00846 public:
00847 
00848   RtApiDs();
00849   ~RtApiDs();
00850   RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_DS; };
00851   unsigned int getDeviceCount( void );
00852   unsigned int getDefaultOutputDevice( void );
00853   unsigned int getDefaultInputDevice( void );
00854   RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
00855   void closeStream( void );
00856   void startStream( void );
00857   void stopStream( void );
00858   void abortStream( void );
00859   long getStreamLatency( void );
00860 
00861   // This function is intended for internal use only.  It must be
00862   // public because it is called by the internal callback handler,
00863   // which is not a member of RtAudio.  External use of this function
00864   // will most likely produce highly undesireable results!
00865   void callbackEvent( void );
00866 
00867   private:
00868 
00869   bool coInitialized_;
00870   bool buffersRolling;
00871   long duplexPrerollBytes;
00872   bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 
00873                         unsigned int firstChannel, unsigned int sampleRate,
00874                         RtAudioFormat format, unsigned int *bufferSize,
00875                         RtAudio::StreamOptions *options );
00876 };
00877 
00878 #endif
00879 
00880 #if defined(__LINUX_ALSA__)
00881 
00882 class RtApiAlsa: public RtApi
00883 {
00884 public:
00885 
00886   RtApiAlsa();
00887   ~RtApiAlsa();
00888   RtAudio::Api getCurrentApi() { return RtAudio::LINUX_ALSA; };
00889   unsigned int getDeviceCount( void );
00890   RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
00891   void closeStream( void );
00892   void startStream( void );
00893   void stopStream( void );
00894   void abortStream( void );
00895 
00896   // This function is intended for internal use only.  It must be
00897   // public because it is called by the internal callback handler,
00898   // which is not a member of RtAudio.  External use of this function
00899   // will most likely produce highly undesireable results!
00900   void callbackEvent( void );
00901 
00902   private:
00903 
00904   std::vector<RtAudio::DeviceInfo> devices_;
00905   void saveDeviceInfo( void );
00906   bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 
00907                         unsigned int firstChannel, unsigned int sampleRate,
00908                         RtAudioFormat format, unsigned int *bufferSize,
00909                         RtAudio::StreamOptions *options );
00910 };
00911 
00912 #endif
00913 
00914 #if defined(__LINUX_OSS__)
00915 
00916 class RtApiOss: public RtApi
00917 {
00918 public:
00919 
00920   RtApiOss();
00921   ~RtApiOss();
00922   RtAudio::Api getCurrentApi() { return RtAudio::LINUX_OSS; };
00923   unsigned int getDeviceCount( void );
00924   RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
00925   void closeStream( void );
00926   void startStream( void );
00927   void stopStream( void );
00928   void abortStream( void );
00929 
00930   // This function is intended for internal use only.  It must be
00931   // public because it is called by the internal callback handler,
00932   // which is not a member of RtAudio.  External use of this function
00933   // will most likely produce highly undesireable results!
00934   void callbackEvent( void );
00935 
00936   private:
00937 
00938   bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 
00939                         unsigned int firstChannel, unsigned int sampleRate,
00940                         RtAudioFormat format, unsigned int *bufferSize,
00941                         RtAudio::StreamOptions *options );
00942 };
00943 
00944 #endif
00945 
00946 #if defined(__RTAUDIO_DUMMY__)
00947 
00948 class RtApiDummy: public RtApi
00949 {
00950 public:
00951 
00952   RtApiDummy() { errorText_ = "RtApiDummy: This class provides no functionality."; error( RtError::WARNING ); };
00953   RtAudio::Api getCurrentApi( void ) { return RtAudio::RTAUDIO_DUMMY; };
00954   unsigned int getDeviceCount( void ) { return 0; };
00955   RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) { RtAudio::DeviceInfo info; return info; };
00956   void closeStream( void ) {};
00957   void startStream( void ) {};
00958   void stopStream( void ) {};
00959   void abortStream( void ) {};
00960 
00961   private:
00962 
00963   bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 
00964                         unsigned int firstChannel, unsigned int sampleRate,
00965                         RtAudioFormat format, unsigned int *bufferSize,
00966                         RtAudio::StreamOptions *options ) { return false; };
00967 };
00968 
00969 #endif
00970 
00971 #endif
00972 
00973 // Indentation settings for Vim and Emacs
00974 //
00975 // Local Variables:
00976 // c-basic-offset: 2
00977 // indent-tabs-mode: nil
00978 // End:
00979 //
00980 // vim: et sts=2 sw=2

©2001-2010 Gary P. Scavone, McGill University. All Rights Reserved.
Maintained by Gary P. Scavone.