taptaudio_paimpl.h

Go to the documentation of this file.
00001 /* $Id: taptaudio_paimpl.h 191 2005-08-05 00:55:59Z tapted $ $URL: svn+ssh://pc-g33-9.it.usyd.edu.au/var/svn/pub/taptaudio/trunk/src/taptaudio_paimpl.h $ */
00002 #ifndef TAPTAUDIO_PAIMPL_DOT_AITCH
00003 #define TAPTAUDIO_PAIMPL_DOT_AITCH
00004 
00005 #include "taptaudio.h"
00006 
00007 /**\file taptaudio_paimpl.h
00008  * Header for portaudio implementation of taptaudio
00009  * \author Trent Apted <tapted@it.usyd.edu.au>
00010  * $Revision: 191 $
00011  * $Date: 2005-08-05 10:55:59 +1000 (Fri, 05 Aug 2005) $
00012  */
00013 
00014 #ifdef HAVE_CONFIG_H
00015 #include "config.h"
00016 #endif
00017 
00018 #include <map>
00019 #include <SDL_thread.h>
00020 #include <limits>
00021 #include <math.h>
00022 
00023 #include "sample.h"
00024 
00025 /**\def PAV19
00026  * If this is defined, we will try to use the version 1.9 of portaudio.
00027  * \note the API is very different -- we will look for the header at
00028  * portaudio_v19.h, wherever that may be..
00029  */
00030 
00031 #ifndef PAV19
00032 #include "portaudio.h"
00033 #else
00034 #include "portaudio_v19.h"
00035 #endif
00036 
00037 /** The portaudio implementation of the audio system */
00038 class AudioSystemImpl {
00039 private:
00040     AudioSystemImpl(const AudioSystemImpl&) {}
00041     AudioSystemImpl& operator=(const AudioSystemImpl&) {return *this;}
00042 public:
00043     typedef std::map<std::string, ASSample*> REGISTRY; ///< Type for WAV file registry
00044     static SDL_mutex *as_mutex;                        ///< Audio System mutex
00045     static SDL_mutex *registry_mutex;                  ///< Registry mutex
00046 protected:
00047     REGISTRY registry;             ///< The registy of loaded WAV files
00048 #ifndef PAV19
00049     PortAudioStream *stream;       ///< The portaudio strem handle
00050     typedef PaTimestamp Timestamp; ///< The type used for time stamps in this version of portaudio
00051 #else
00052     PaStream *stream;
00053     typedef PaTime Timestamp;
00054 #endif
00055     bool stopping;        ///< Are we trying to stop the audio system
00056     bool fakeMonoMic;     ///< Are we "faking" a Mono input stream by averaging stereo channels
00057     unsigned inChannels;  ///< Number of input channels
00058     unsigned outChannels; ///< Number of output channels
00059     double sampRate;      ///< The sample rate of the audio stream
00060     AUDIO_FORMAT fmt;     ///< The audio format
00061 
00062     unsigned iframe_size; ///< The size (in bytes) of an input frame
00063     unsigned oframe_size; ///< The size (in bytes) of an output frame
00064 
00065     Recording *cur_rec;           ///< The current recording (e.g. we are recording if this is non-null)
00066     Recording *record_after_play; ///< The recording we will start as soon as no samples are playing
00067 
00068     ASSample *cur_play;           ///< For non-tracked implementations, the currently playing sample
00069     unsigned long play_offset;    ///< The current offset into \a cur_play
00070 
00071     /** Perform the "record" portion of the callback */
00072     virtual void doRecord(const void *inputBuffer,
00073                           unsigned long framesPerBuffer);
00074 
00075     /** Perform the "play" portion of the callback */
00076     virtual void doPlay(void *outputBuffer,
00077                         unsigned long framesPerBuffer,
00078                         Timestamp outTime);
00079 
00080 public:
00081     /** Virtual destructor, free the portaudio handles and clear the registry (?) */
00082     virtual ~AudioSystemImpl();
00083 
00084     /** Create an AudioSystemImpl */
00085     AudioSystemImpl(unsigned inputChannels = 1,
00086                     unsigned outputChannels = 2,
00087                     double sampleRate = 44100,
00088                     AUDIO_FORMAT format = AF_Int16,
00089                     int deviceIDin = 0,
00090                     int deviceIDout = 0);
00091 
00092     /** Portaudio callback
00093      * \note occurs at interrupt level so we CAN NOT ALLOCATE MEMORY
00094      */
00095     virtual int callback(const void *inputBuffer,
00096                          void *outputBuffer,
00097                          unsigned long framesPerBuffer,
00098                          Timestamp outTime);
00099 
00100     /**
00101      * Get a holder (e.g. a track reference) in which to load a sample.
00102      */
00103     virtual ASSample** getSampleHolder(bool loop = false, float vol = 1.0f, unsigned* trackno = 0);
00104 
00105     /** Load a "known" sample, or a file */
00106     virtual ASSample* loadSample(const std::string &name);
00107     ASSample* loadRawSample(const std::string &name, void* data, unsigned long size);
00108     bool mixSample(ASSample *samp, bool record_after, double record_size, bool loop = false, float vol = 1.0f, unsigned* trackno = 0);
00109     bool loopSample(ASSample *samp, float vol = 1.0f, unsigned* trackno = 0);
00110     bool freeSample(ASSample *samp);
00111     virtual unsigned stopSample(ASSample *samp, int trackno = -1, bool lock = true);
00112     virtual unsigned setVolume(ASSample */*samp*/, float /*vol*/ = 1.0f, int /*trackno*/ = -1) {return 0;}
00113     bool startRec(double max_seconds);
00114     bool isRecording() { return cur_rec; }
00115     ASSample* stopRec(const std::string &name, bool save = false, bool save_in_thread = false);
00116     void stop() {stopping = true;}
00117     void waitstop();
00118 
00119     static void listDevices();
00120 };
00121 
00122 /**
00123  * An implementation of AudioSystemImpl that mixes any number of tracks
00124  * with appropriate clipping.
00125  */
00126 class AudioSystemMixer : public AudioSystemImpl {
00127 protected:
00128     const unsigned tracks; /* todo: this should be a struct */
00129     struct Mix {
00130         ASSample *sam;
00131         unsigned off;
00132         float vol;
00133         bool looping;
00134     };
00135     Mix *track;
00136 
00137     virtual void doPlay(void *outputBuffer,
00138                         unsigned long framesPerBuffer,
00139                         Timestamp outTime) = 0;
00140 
00141 public:
00142     AudioSystemMixer(unsigned inputChannels = 1,
00143                      unsigned outputChannels = 2,
00144                      double sampleRate = 44100,
00145                      AUDIO_FORMAT format = AF_Int16,
00146                      unsigned ttracks = 32,
00147                      int deviceIDin = 0,
00148                      int deviceIDout = 0)
00149         :
00150     AudioSystemImpl(inputChannels, outputChannels, sampleRate, format, deviceIDin, deviceIDout), tracks(ttracks) {
00151         track = new Mix[tracks];
00152         for (unsigned i = 0; i < tracks; ++i)
00153             track[i].sam = 0;
00154     }
00155 
00156     virtual ~AudioSystemMixer() {
00157         delete[] track;
00158     }
00159 
00160     virtual ASSample** getSampleHolder(bool loop = false, float vol = 1.0f, unsigned* trackno = 0);
00161     virtual unsigned stopSample(ASSample *samp, int trackno = -1, bool lock = true);
00162     virtual unsigned setVolume(ASSample *samp, float vol = 1.0f, int trackno = -1);
00163 };
00164 
00165 /**
00166  * Templatized version of AudioSystemMixer, that knows how each audio format
00167  * can be mixed
00168  */
00169 template <class T>
00170 class AudioSystemMixerT : public AudioSystemMixer {
00171     virtual void doPlay(void *outputBuffer,
00172                         unsigned long framesPerBuffer,
00173                         Timestamp outTime);
00174 
00175     /** Add \a val to \a accum, clipping if it overflows */
00176     void clipadd(T& accum, const T& val);
00177 public:
00178     AudioSystemMixerT(unsigned inputChannels = 1,
00179                       unsigned outputChannels = 2,
00180                       double sampleRate = 44100,
00181                       AUDIO_FORMAT format = AF_Int16,
00182                       unsigned ttracks = 32,
00183                       int deviceIDin = 0,
00184                       int deviceIDout = 0)
00185         :
00186     AudioSystemMixer(inputChannels, outputChannels, sampleRate, format, ttracks, deviceIDin, deviceIDout) {}
00187     virtual ASSample* loadSample(const std::string &name);
00188 };
00189 
00190 template <class T>
00191 inline void AudioSystemMixerT<T>::clipadd(T& accum, const T& val) {
00192     //if (std::numeric_limits<T>::is_integer) {
00193     const T tmp = accum + val;
00194     accum = (((accum & val & ~tmp) | (~accum & ~val & tmp)) < 0) ?
00195         (accum < 0 ?                  /* under/overflow/clipping */
00196          std::numeric_limits<T>::min()  :
00197          std::numeric_limits<T>::max()) :
00198         tmp;
00199 }
00200 
00201 template <>
00202 inline void AudioSystemMixerT<float>::clipadd(float& accum, const float& val) {
00203     accum += val;
00204 }
00205 
00206 template <>
00207 inline void AudioSystemMixerT<double>::clipadd(double& accum, const double& val) {
00208     accum += val;
00209 }
00210 
00211 template <class T>
00212 void AudioSystemMixerT<T>::doPlay(void *outputBuffer,
00213                                   unsigned long framesPerBuffer,
00214                                   Timestamp /*outTime*/) { /*FOLD00*/
00215     if (!outputBuffer)
00216         return;
00217 
00218     T *opb = static_cast<T*>(outputBuffer);
00219     unsigned nmixed = 0;
00220     memset(outputBuffer, 0, oframe_size * framesPerBuffer);
00221 
00222     for (unsigned i = 0; i < tracks; ++i) {
00223         if (track[i].sam) {
00224             Mix &m = track[i];
00225             /* we assume for now that format is the same (checked in loadSample) */
00226             const T* ipb = static_cast<const T*>(m.sam->getBytes());
00227             /* how many frames are there in the entire sample? */
00228             unsigned long nframes = m.sam->numBytes() / m.sam->getChannels() / sizeof(T);
00229             /* what frame are we at? */
00230             unsigned &offset = m.off;
00231             /* are we looping */
00232             bool &loop = m.looping;
00233             bool v = m.vol != 1.0f;
00234 
00235             if (loop || offset < nframes) {
00236                 unsigned opb_offset = 0;
00237                 unsigned long n;
00238                 do {
00239                     if (loop && offset >= nframes)
00240                         offset = 0;
00241                     /* how many frames are left in the sample stream */
00242                     n = nframes-offset;
00243                     if (n > framesPerBuffer - opb_offset)
00244                         n = framesPerBuffer - opb_offset;
00245 
00246                     unsigned ich = m.sam->getChannels();
00247                     ipb += offset*ich;
00248 
00249                     if  (ich == 1) {
00250                         /* mix mono -> N channels */
00251                         for (unsigned f = opb_offset; f < n; ++f) {
00252                             for (unsigned c = 0; c < outChannels; ++c) {
00253                                 clipadd(opb[outChannels*f + c],
00254                                         v ? static_cast<T>(roundf(ipb[f] * m.vol))
00255                                         : ipb[f]);
00256                             }
00257                         }
00258                         m.sam->mixed(n, track + i);
00259                     } else if (ich == outChannels) {
00260                         for (unsigned f = opb_offset; f < n*ich; ++f) {
00261                             clipadd(opb[f],
00262                                     v ? static_cast<T>(roundf(ipb[f] * m.vol))
00263                                     : ipb[f]);
00264                         }
00265                         m.sam->mixed(n, track + i);
00266                     } else {
00267                         /* not supported -- skip */
00268                     }
00269                     offset += n;
00270                 } while (loop && (opb_offset += n) < framesPerBuffer);
00271                 ++nmixed;
00272             }
00273             if (!loop && offset >= nframes) {
00274                 --m.sam->refs;
00275                 m.sam = 0;
00276             }
00277         }
00278     }
00279     if (nmixed == 0 && record_after_play) {
00280         cur_rec = record_after_play;
00281         record_after_play = 0;
00282     }
00283 } /*FOLD00*/
00284 
00285 
00286 template <class T>
00287 ASSample* AudioSystemMixerT<T>::loadSample(const std::string &name) { /*FOLD00*/
00288     ASSample *ret = 0;
00289     //note we only need to take out a registry mutex for this
00290     SDL_mutexP(AudioSystemImpl::registry_mutex);
00291 
00292     REGISTRY::iterator it = registry.find(name);
00293     if (it == registry.end()) {
00294         try {
00295             ret = new FileSampleT<T>(name, outChannels == 2);
00296 
00297             /* sample rates still should match, but no huge dramas if they don't --
00298              * we will just play it slower/faster.
00299              */
00300             if (sampRate != ret->getSamRate()) {
00301                 DODEBUG(WARNING, ("[Aw] Warning: the sample rate of %s (%fHz) differs from the current AudioSystem (%fHz)\n",
00302                                   name.c_str(), ret->getSamRate(), sampRate));
00303             }
00304 
00305             registry.insert(REGISTRY::value_type(name, ret));
00306         } catch (const char *err) {
00307             DODEBUG(ERROR, ("[Ae] Exception caught at loadSample: %s",
00308                             err));
00309         } catch (std::bad_alloc &) {
00310             DODEBUG(ERROR, ("[Ae] Not enough memory to load %s\n",
00311                             name.c_str()));
00312         }
00313     } else {
00314         ret = it->second;
00315     }
00316     SDL_mutexV(AudioSystemImpl::registry_mutex);
00317     return ret;
00318 } /*FOLD00*/
00319 
00320 
00321 #endif

Generated on Fri Aug 5 19:43:10 2005 for TaptAudio by  doxygen 1.4.3