taptaudio_paimpl.cpp

Go to the documentation of this file.
00001 /* $Id: taptaudio_paimpl.cpp 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.cpp $ */
00002 
00003 /**\file taptaudio_paimpl.cpp
00004  * Implementation of portaudio implementation of taptaudio
00005  * \author Trent Apted <tapted@it.usyd.edu.au>
00006  * $Revision: 191 $
00007  * $Date: 2005-08-05 10:55:59 +1000 (Fri, 05 Aug 2005) $
00008  */
00009 
00010 #include "taptaudio_paimpl.h"
00011 
00012 SDL_mutex *AudioSystemImpl::as_mutex = SDL_CreateMutex();
00013 SDL_mutex *AudioSystemImpl::registry_mutex = SDL_CreateMutex();
00014 
00015 namespace {
00016     SDL_sem *stop_sem = SDL_CreateSemaphore(0);
00017 
00018 #ifndef PAV19
00019     int my_callback(void *inputBuffer,
00020                     void *outputBuffer,
00021                     unsigned long framesPerBuffer,
00022                     PaTimestamp outTime,
00023                     void *userData) {
00024         return static_cast<AudioSystemImpl*>(userData)->
00025             callback(inputBuffer,
00026                      outputBuffer,
00027                      framesPerBuffer,
00028                      outTime);
00029     }
00030 #else
00031     int my_callback(const void *input,
00032                     void *output,
00033                     unsigned long frameCount,
00034                     const PaStreamCallbackTimeInfo* timeInfo,
00035                     PaStreamCallbackFlags /*statusFlags*/,
00036                     void *userData ) {
00037         return static_cast<AudioSystemImpl*>(userData)->
00038             callback(input,
00039                      output,
00040                      frameCount,
00041                      timeInfo->outputBufferDacTime);
00042     }
00043 #endif
00044 
00045     struct SaveInfo {
00046         Recording *r;
00047         std::string f;
00048         SaveInfo(Recording *rr, const std::string &file) : r(rr), f(file) {}
00049     };
00050 
00051     int my_saver(void *data) {
00052         SaveInfo * si= static_cast<SaveInfo*>(data);
00053         int ret = si->r->save(si->f);
00054         delete si;
00055         return ret;
00056     }
00057 }
00058 
00059 /****************************************************/
00060 /* AudioSystemImpl class implementation starts here */
00061 /****************************************************/
00062 
00063 void AudioSystemImpl::waitstop() {
00064     SDL_SemWait(stop_sem);
00065 }
00066 
00067 AudioSystemImpl::~AudioSystemImpl() {
00068     DODEBUG(INFO, ("[Ai] Not locking to close AudioSystem..."));
00069     //SDL_mutexP(as_mutex);
00070     //SDL_mutexP(registry_mutex);
00071 
00072     PaError err;
00073     DODEBUG(NOTICE, ("[An] Stopping stream..."));
00074     /* this function may have a bug in it... */
00075     err = Pa_StopStream(stream);
00076     DODEBUG(NOTICE, ("[An] Closing stream..."));
00077     err = Pa_CloseStream(stream);
00078     DODEBUG(NOTICE, ("[An] Clearing registry..."));
00079     for (REGISTRY::iterator it = registry.begin(); it != registry.end(); ++it)
00080         delete it->second;
00081 
00082     //SDL_mutexV(as_mutex);
00083     //SDL_mutexV(registry_mutex);
00084     DODEBUG(NOTICE, ("[An] Done."));
00085 }
00086 
00087 AudioSystemImpl::AudioSystemImpl(unsigned inputChannels,
00088                                  unsigned outputChannels,
00089                                  double sampleRate,
00090                                  AUDIO_FORMAT format,
00091                                  int deviceIDin,
00092                                  int deviceIDout)
00093 :
00094 stream(0),
00095 stopping(false),
00096 inChannels(inputChannels),
00097 outChannels(outputChannels),
00098 sampRate(sampleRate == 0.0 ? 44100 : sampleRate),
00099 fmt(format == AF_Default ? AF_Int16 : format),
00100 cur_rec(0),
00101 cur_play(0),
00102 record_after_play(0)
00103 {
00104     int sz = 0;
00105     switch (fmt) {
00106     case AF_Float32:
00107     case AF_Int32: sz++;
00108     case AF_PackedInt24 :
00109     case AF_Int24: sz++;
00110     case AF_Int16: sz++;
00111     case AF_Int8:
00112     case AF_UInt8: sz++;
00113     default: break;
00114     }
00115     iframe_size = inChannels * sz;
00116     oframe_size = outChannels * sz;
00117 
00118     PaError err;
00119     if ((err = Pa_Initialize()) != paNoError)
00120         throw err;
00121 
00122     fakeMonoMic = inChannels == 1 && outChannels == 2;
00123 
00124 #ifndef PAV19
00125     if (deviceIDin == 0 && deviceIDout == 0) {
00126         if ((err = Pa_OpenDefaultStream(&stream,    /* stream */
00127                                         fakeMonoMic ? 2 : inChannels,
00128                                         outChannels,
00129                                         fmt,         /* outputSampleFormat */
00130                                         sampRate,    /* sampleRate */
00131                                         1024,        /* frames per buffer -- about 23ms at 44,100 */
00132                                         0,           /* number of buffers -- use the default minimum */
00133                                         my_callback,
00134                                         this)) != paNoError)
00135             throw err;
00136     } else {
00137         if ((err = Pa_OpenStream(&stream,    /* stream */
00138                                  deviceIDin, /* inputDeviceID */
00139                                  fakeMonoMic ? 2 : inChannels,
00140                                  fmt,        /* inputSampleFormat */
00141                                  0,          /* inputDriverInfo */
00142                                  deviceIDout, /* outputDeviceID */
00143                                  outChannels,
00144                                  fmt,         /* outputSampleFormat */
00145                                  0,           /* outputDriverInfo */
00146                                  sampRate,    /* sampleRate */
00147                                  1024,        /* frames per buffer -- about 23ms at 44,100 */
00148                                  0,           /* number of buffers -- use the default minimum */
00149                                  0,           /* PortAudio Stream Flags */
00150                                  my_callback,
00151                                  this)) != paNoError)
00152             throw err;
00153     }
00154 #else
00155     if (deviceIDin == 0 && deviceIDout == 0) {
00156         if ((err = Pa_OpenDefaultStream(&stream,
00157                                         fakeMonoMic ? 2 : inChannels,
00158                                         outChannels,
00159                                         fmt,
00160                                         sampRate,
00161                                         1024,
00162                                         my_callback,
00163                                         this)) != paNoError)
00164             throw err;
00165     } else {
00166         PaStreamParameters in, out;
00167         in.device = out.device = 0;
00168         in.channelCount = fakeMonoMic ? 2 : inChannels;
00169         out.channelCount = outChannels;
00170         in.sampleFormat = out.sampleFormat = fmt;
00171         in.suggestedLatency = out.suggestedLatency = 0.010; //10ms
00172         in.hostApiSpecificStreamInfo = out.hostApiSpecificStreamInfo = 0;
00173 
00174         if ((err = Pa_OpenStream(&stream,
00175                                  &in, &out,
00176                                  sampRate,
00177                                  1024,  /* frames per buffer */
00178                                  0,     /* stream flags */
00179                                  my_callback,
00180                                  this)) != paNoError)
00181             throw err;
00182     }
00183 #endif
00184 
00185     if ((err = Pa_StartStream(stream)) != paNoError)
00186         throw err;
00187 
00188     /* we are now running... */
00189     DODEBUG(NOTICE, ("[An] AudioSystem mixing at %fHz for %d input and %d output channels on device %d/%d...",
00190                      sampRate, inChannels, outChannels, deviceIDin, deviceIDout));
00191     DODEBUG(INFO, ("[Ai] fakeMonoMic = %s, iframe_size = %u, oframe_size = %u, sz = %d.",
00192                    fakeMonoMic ? "true" : "false", iframe_size, oframe_size, sz));
00193 }
00194 
00195 int AudioSystemImpl::callback(const void *inputBuffer,
00196                               void *outputBuffer,
00197                               unsigned long framesPerBuffer,
00198                               Timestamp outTime)
00199 {
00200     if (stopping) {
00201         SDL_SemPost(stop_sem);
00202         return 1;
00203     }
00204     SDL_mutexP(as_mutex);
00205     if (cur_rec)
00206         doRecord(inputBuffer, framesPerBuffer);
00207     doPlay(outputBuffer, framesPerBuffer, outTime);
00208     SDL_mutexV(as_mutex);
00209     return 0;
00210 }
00211 
00212 void AudioSystemImpl::doRecord(const void *inputBuffer,
00213                                unsigned long framesPerBuffer)
00214 {
00215     if (inputBuffer) {
00216         //cur_rec->append(inputBuffer, iframe_size * framesPerBuffer);
00217         if (fakeMonoMic)
00218             cur_rec->skipfill(inputBuffer, iframe_size * framesPerBuffer, iframe_size/2);
00219         else
00220             cur_rec->fill(inputBuffer, iframe_size * framesPerBuffer);
00221     } else { //fill with silence
00222         cur_rec->fillSilence(iframe_size * framesPerBuffer);
00223     }
00224 }
00225 
00226 void AudioSystemImpl::doPlay(void *outputBuffer,
00227                              unsigned long framesPerBuffer,
00228                              Timestamp /*outTime*/) {
00229     if (outputBuffer) {
00230         memset(outputBuffer, 0, oframe_size * framesPerBuffer);
00231         if (cur_play) {
00232 //            putc('b', stderr);
00233             unsigned long nb = cur_play->numBytes();
00234             if (play_offset < nb) {
00235                 unsigned long sz = framesPerBuffer * oframe_size;
00236                 if (sz > nb - play_offset)
00237                     sz = nb - play_offset;
00238 
00239                 if  (cur_play->getChannels() == 1 && outChannels == 2) {
00240                     sz /= 2;
00241                     PCMSample::mono2stereo(static_cast<Uint8*>(outputBuffer),
00242                                            static_cast<const Uint8*>(cur_play->getBytes()) + play_offset,
00243                                            sz,
00244                                            oframe_size/2);
00245                 } else {
00246                     memcpy(outputBuffer,
00247                            static_cast<const Uint8*>(cur_play->getBytes()) + play_offset,
00248                            sz);
00249                 }
00250                 play_offset += sz;
00251             }
00252             if (play_offset >= nb - 1) {
00253                 //play_offset = 0;
00254                 --cur_play->refs;
00255                 cur_play = 0;
00256                 if (record_after_play) {
00257                     cur_rec = record_after_play;
00258                     record_after_play = 0;
00259                 }
00260             }
00261         }
00262     }
00263 }
00264 
00265 
00266 ASSample* AudioSystemImpl::loadSample(const std::string &name) {
00267     ASSample *ret = 0;
00268     //note we only need to take out a registry mutex for this
00269     SDL_mutexP(registry_mutex);
00270 
00271     REGISTRY::iterator it = registry.find(name);
00272     if (it == registry.end()) {
00273         try {
00274             ret = new FileSample(name/*, outChannels == 2*/);
00275 
00276             if   (fmt == ret->getFormat() &&
00277                   outChannels >= ret->getChannels() &&
00278                   sampRate == ret->getSamRate()) {
00279 
00280                 registry.insert(REGISTRY::value_type(name, ret));
00281 
00282             } else {
00283 
00284                 DODEBUG(WARNING, ("[Aw] The format of %s is not compatible with the current AudioSystem",
00285                                   name.c_str()));
00286                 DODEBUG(WARNING, ("[Aw] (file = [%d]fmt %d ch @ %fHz != [%d]fmt %d ch @ %fHz = AudioSystem)",
00287                                   static_cast<int>(ret->getFormat()), ret->getChannels(), ret->getSamRate(),
00288                                   static_cast<int>(fmt), outChannels, sampRate));
00289 
00290                 delete ret;
00291 
00292             }
00293         } catch (const char *err) {
00294             DODEBUG(ERROR, ("[Ae] Exception caught trying to load %s: %s",
00295                             name.c_str(), err));
00296         } catch (std::bad_alloc &) {
00297             DODEBUG(ERROR, ("[Ae] Not enough memory to load %s",
00298                             name.c_str()));
00299         }
00300     } else {
00301         ret = it->second;
00302     }
00303     SDL_mutexV(registry_mutex);
00304     return ret;
00305 }
00306 
00307 bool AudioSystemImpl::freeSample(ASSample* samp) {
00308     /* \todo -- use some RAII, this looks crap */
00309     bool ret = false;
00310     SDL_mutexP(as_mutex);
00311     if (samp->refs) {
00312         SDL_mutexV(as_mutex);
00313         DODEBUG(WARNING, ("[Aw] Couldn't free sample -- maybe it is still playing"));
00314         return false;
00315     }
00316     SDL_mutexP(registry_mutex);
00317 
00318     REGISTRY::iterator it = registry.begin();
00319     for (; it != registry.end() && it->second != samp; ++it)
00320         ;
00321 
00322     if (it == registry.end()) {
00323         DODEBUG(ERROR, ("[Ae] Couldn't find sample in registry -- not freeing"));
00324     } else {
00325         registry.erase(it);
00326         delete samp;
00327         DODEBUG(INFO, ("[Ai] Sample removed from registry and deleted"));
00328         ret = true;
00329     }
00330     SDL_mutexV(registry_mutex);
00331     SDL_mutexV(as_mutex);
00332     return ret;
00333 }
00334 
00335 ASSample* AudioSystemImpl::loadRawSample(const std::string &name, void* data, unsigned long size) {
00336     ASSample *ret = 0;
00337     //note we only need to take out a registry mutex for this
00338     SDL_mutexP(registry_mutex);
00339 
00340     REGISTRY::iterator it = registry.find(name);
00341     if (it != registry.end()) {
00342         registry.erase(it);
00343     }
00344     ret = new PCMSample(data, outChannels, size, sampRate, fmt);
00345     registry.insert(REGISTRY::value_type(name, ret));
00346     SDL_mutexV(registry_mutex);
00347     return ret;
00348 }
00349 
00350 
00351 ASSample** AudioSystemImpl::getSampleHolder(bool loop, float /*vol*/, unsigned* trackno) {
00352     if (trackno)
00353         *trackno = 0;
00354     if (!loop && !cur_play) {
00355         play_offset = 0;
00356         return &cur_play;
00357     }
00358     return 0;
00359 }
00360 
00361 unsigned AudioSystemImpl::stopSample(ASSample *samp, int /*trackno*/, bool lock) {
00362     if (cur_play == samp) {
00363         if (lock) SDL_mutexP(as_mutex);
00364         --cur_play->refs;
00365         cur_play = 0;
00366         if (lock) SDL_mutexV(as_mutex);
00367         return 1;
00368     }
00369     return 0;
00370 }
00371 
00372 ASSample** AudioSystemMixer::getSampleHolder(bool loop, float vol, unsigned* trackno) {
00373     for (unsigned i = 0; i < tracks; ++i) {
00374         if (!track[i].sam) {
00375             track[i].off = 0;
00376             track[i].vol = vol;
00377             track[i].looping = loop;
00378             if (trackno)
00379                 *trackno = i;
00380             return &(track[i].sam);
00381         }
00382     }
00383     return 0;
00384 }
00385 
00386 unsigned AudioSystemMixer::stopSample(ASSample *samp, int trackno, bool lock) {
00387     unsigned ctr = 0;
00388     unsigned i = (trackno < 0) ? 0 : trackno;
00389     const unsigned max = (trackno < 0) ? tracks : trackno+1;
00390     if (lock) SDL_mutexP(as_mutex);
00391     for (; i < max; ++i) {
00392         if (track[i].sam == samp) {
00393             --track[i].sam->refs;
00394             track[i].sam = 0;
00395             ctr++;
00396         }
00397     }
00398     if (lock) SDL_mutexV(as_mutex);
00399     return ctr;
00400 }
00401 
00402 unsigned AudioSystemMixer::setVolume(ASSample *samp, float vol, int trackno) {
00403     unsigned ctr = 0;
00404     unsigned i = (trackno < 0) ? 0 : trackno;
00405     const unsigned max = (trackno < 0) ? tracks : trackno+1;
00406     SDL_mutexP(as_mutex);
00407     for (; i < max; ++i) {
00408         if (track[i].sam == samp) {
00409             track[i].vol = vol;
00410             ctr++;
00411         }
00412     }
00413     SDL_mutexV(as_mutex);
00414     return ctr;
00415 }
00416 
00417 bool AudioSystemImpl::mixSample(ASSample *samp, bool record_after, double record_size, bool loop, float vol, unsigned* trackno) {
00418     bool success = false;
00419     SDL_mutexP(as_mutex);
00420     ASSample **holder = getSampleHolder(loop, vol, trackno);
00421     if (holder) {
00422         *holder = samp;
00423         ++samp->refs;
00424         success = true;
00425         if (record_after && !cur_rec && !record_after_play)
00426             record_after_play = new Recording(static_cast<unsigned long>(record_size * sampRate * iframe_size),
00427                                               inChannels,
00428                                               fmt,
00429                                               sampRate);
00430         DODEBUG(FUNCTION, ("[Af] %sixing %lu bytes, %d channels @ %fHz",
00431                            (*holder)->getChannels() == outChannels ? "M" : "Upm",
00432                            (*holder)->numBytes(), (*holder)->getChannels(), (*holder)->getSamRate()));
00433     }
00434     SDL_mutexV(as_mutex);
00435     return success;
00436 }
00437 
00438 bool AudioSystemImpl::loopSample(ASSample *samp, float vol, unsigned* trackno) {
00439     return mixSample(samp, false, 0.0, true, vol, trackno);
00440 }
00441 
00442 bool AudioSystemImpl::startRec(double max_seconds) {
00443     DODEBUG(FUNCTION, ("[Af] Recording..."));
00444     SDL_mutexP(as_mutex);
00445     bool success = false;
00446     if (!cur_rec && !record_after_play) {
00447         try {
00448             //play_offset = 0;
00449             cur_rec = new Recording(static_cast<unsigned long>(max_seconds * sampRate * iframe_size),
00450                                     inChannels,
00451                                     fmt,
00452                                     sampRate);
00453             success = true;
00454         } catch (std::bad_alloc&) {
00455         }
00456     }
00457     SDL_mutexV(as_mutex);
00458     return success;
00459 }
00460 
00461 ASSample* AudioSystemImpl::stopRec(const std::string &name, bool save, bool save_in_thread) {
00462     DODEBUG(FUNCTION, ("[Af] Stopping... "));
00463     Recording *ret = 0;
00464     SDL_mutexP(as_mutex);
00465     if (cur_rec) {
00466         cur_rec->trim();
00467         DODEBUG(INFO, ("[Ai] Size is %lu bytes\n",
00468                        cur_rec->numBytes()));
00469         ret = cur_rec;
00470         cur_rec = 0;
00471 
00472         { //register
00473             SDL_mutexP(registry_mutex);
00474             REGISTRY::iterator it = registry.find(name);
00475             if (it != registry.end()) {
00476                 delete it->second;
00477                 it->second = ret;
00478             } else {
00479                 registry.insert(REGISTRY::value_type(name, ret));
00480             }
00481             SDL_mutexV(registry_mutex);
00482         }
00483 
00484         if (save) {
00485             if (save_in_thread) {
00486                 (void)SDL_CreateThread(my_saver, new SaveInfo(ret, name));
00487             } else {
00488                 ret->save(name);
00489             }
00490         }
00491     }
00492     SDL_mutexV(as_mutex);
00493     return ret;
00494 }
00495 
00496 void AudioSystemImpl::listDevices() {
00497     (void)Pa_Initialize();
00498 #ifndef PAV19
00499     int numdevices = Pa_CountDevices();
00500     int defaultin = Pa_GetDefaultInputDeviceID();
00501     int defaultout = Pa_GetDefaultOutputDeviceID();
00502 
00503     fprintf(stderr, "Counted %d device%s. Device #0 for default (?).\n", numdevices, numdevices == 1 ? "" : "s");
00504     for (int i = 0; i < numdevices; ++i) {
00505         const PaDeviceInfo* d = Pa_GetDeviceInfo(i);
00506         fprintf(stderr,
00507                 "Device #%d%s%s\n"
00508                 "\tstructVersion   : %d\n"
00509                 "\tname            : %s\n"
00510                 "\tmaxInputChannels: %d\n"
00511                 "\tmaxOutputChans  : %d\n"
00512                 "\tsampleFormats   : %lx\n"
00513                 "\tsampleRates     : ",
00514                 i,
00515                 (i == defaultin  ? " (Default Input)"  : ""),
00516                 (i == defaultout ? " (Default Output)" : ""),
00517                 d->structVersion, d->name,
00518                 d->maxInputChannels, d->maxOutputChannels,
00519                 d->nativeSampleFormats);
00520         if (d->numSampleRates < 0) {
00521             fprintf(stderr,
00522                     "%.0f -- %.0f\n",
00523                     d->sampleRates[0], d->sampleRates[1]);
00524         } else {
00525             if (d->numSampleRates > 0)
00526                 fprintf(stderr, "%.0f", d->sampleRates[0]);
00527             for (int j = 1; j < d->numSampleRates; ++j) {
00528                 fprintf(stderr, ", %.0f", d->sampleRates[j]);
00529             }
00530             fprintf(stderr, "\n");
00531         }
00532     }
00533 #else
00534 
00535 #endif
00536 }

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