Marsyas  0.6.0-alpha
/usr/src/RPM/BUILD/marsyas-0.6.0/src/marsyas/marsystems/AudioSource.cpp
Go to the documentation of this file.
00001 /*
00002 ** Copyright (C) 1998-2013 George Tzanetakis <gtzan@cs.uvic.ca>
00003 **
00004 ** This program is free software; you can redistribute it and/or modify
00005 ** it under the terms of the GNU General Public License as published by
00006 ** the Free Software Foundation; either version 2 of the License, or
00007 ** (at your option) any later version.
00008 **
00009 ** This program is distributed in the hope that it will be useful,
00010 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 ** GNU General Public License for more details.
00013 **
00014 ** You should have received a copy of the GNU General Public License
00015 ** along with this program; if not, write to the Free Software
00016 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017 */
00018 
00019 #include "../common_source.h"
00020 #include "AudioSource.h"
00021 
00022 #include <algorithm>
00023 #include <cassert>
00024 
00025 using namespace std;
00026 using namespace Marsyas;
00027 
00028 AudioSource::AudioSource(string name):MarSystem("AudioSource", name)
00029 {
00030   audio_ = NULL;
00031 
00032   isInitialized_ = false;
00033   stopped_ = true;
00034 
00035   addControls();
00036 }
00037 
00038 AudioSource::~AudioSource()
00039 {
00040   delete audio_;
00041 
00042 }
00043 
00044 
00045 MarSystem*
00046 AudioSource::clone() const
00047 {
00048   return new AudioSource(*this);
00049 }
00050 
00051 void
00052 AudioSource::addControls()
00053 {
00054   addctrl("mrs_natural/nChannels", 1);
00055 
00056 
00057 #ifdef MARSYAS_MACOSX
00058   addctrl("mrs_natural/bufferSize", 1024);
00059 #else
00060   addctrl("mrs_natural/bufferSize", 256);
00061 #endif
00062 
00063 
00064   addctrl("mrs_bool/initAudio", false);
00065   setctrlState("mrs_bool/initAudio", true);
00066 
00067   addctrl("mrs_bool/hasData", true);
00068   addctrl("mrs_real/gain", 1.0);
00069 
00070   addControl("mrs_bool/realtime", false);
00071   setControlState("mrs_bool/realtime", true);
00072 }
00073 
00074 void
00075 AudioSource::myUpdate(MarControlPtr sender)
00076 {
00077   (void) sender;  //suppress warning of unused parameter(s)
00078   MRSDIAG("AudioSource::myUpdate");
00079 
00080   //set output controls
00081   setctrl("mrs_natural/onSamples", getctrl("mrs_natural/inSamples"));
00082   setctrl("mrs_real/osrate", getctrl("mrs_real/israte"));
00083   setctrl("mrs_natural/onObservations", getctrl("mrs_natural/nChannels"));
00084 
00085   mrs_natural source_block_size = getctrl("mrs_natural/bufferSize")->to<mrs_natural>();
00086   mrs_natural dest_block_size = getctrl("mrs_natural/inSamples")->to<mrs_natural>();
00087   mrs_natural sample_rate = (mrs_natural) getctrl("mrs_real/israte")->to<mrs_real>();
00088   mrs_natural channel_count = getctrl("mrs_natural/nChannels")->to<mrs_natural>();
00089   bool realtime = getControl("mrs_bool/realtime")->to<mrs_bool>();
00090 
00091   if (getctrl("mrs_bool/initAudio")->to<mrs_bool>())
00092   {
00093     stop();
00094 
00095     initRtAudio(sample_rate, &source_block_size, channel_count, realtime);
00096 
00097     const bool do_resize_buffer = true;
00098     reformatBuffer(source_block_size, dest_block_size, channel_count, realtime, do_resize_buffer);
00099 
00100     shared.sample_rate = sample_rate;
00101     shared.channel_count = channel_count;
00102     shared.overrun = false;
00103 
00104     isInitialized_ = true;
00105 
00106     //update bufferSize control which may have been changed
00107     //by RtAudio (see RtAudio documentation)
00108     setctrl("mrs_natural/bufferSize", (mrs_natural) source_block_size);
00109 
00110     setctrl("mrs_bool/initAudio", false);
00111   }
00112   else if (isInitialized_)
00113   {
00114     const bool do_not_resize_buffer = false;
00115 
00116     if (source_block_size != old_source_block_size_
00117         || sample_rate != shared.sample_rate
00118         || (realtime != (shared.watermark == 0))
00119         || !reformatBuffer(source_block_size,
00120                            dest_block_size,
00121                            channel_count,
00122                            realtime,
00123                            do_not_resize_buffer) )
00124     {
00125       MRSERR("AudioSource: Reinitialization required!");
00126       // stop processing until re-initialized;
00127       stop();
00128       isInitialized_ = false;
00129     }
00130   }
00131 
00132   old_source_block_size_ = source_block_size;
00133   old_dest_block_size_ = dest_block_size;
00134 }
00135 
00136 
00137 void
00138 AudioSource::initRtAudio(
00139   mrs_natural sample_rate,
00140   mrs_natural *block_size,
00141   mrs_natural channel_count,
00142   bool realtime
00143 )
00144 {
00145   //marsyas represents audio data as float numbers
00146   if (audio_ == NULL)
00147   {
00148     audio_ = new RtAudio();
00149   }
00150   else if (audio_->isStreamOpen())
00151   {
00152     audio_->closeStream();
00153   }
00154 
00155   RtAudio::StreamParameters source_params;
00156   source_params.deviceId = audio_->getDefaultInputDevice();
00157   source_params.nChannels = channel_count;
00158   source_params.firstChannel = 0;
00159 
00160   RtAudio::StreamOptions options;
00161   options.streamName = "Marsyas";
00162   options.numberOfBuffers = 0; // Use default.
00163   options.flags = RTAUDIO_SCHEDULE_REALTIME;
00164   options.priority = 70;
00165   if (realtime)
00166     options.flags |= RTAUDIO_MINIMIZE_LATENCY; // Use smallest possible 'numberOfBuffers'.
00167 
00168   RtAudioFormat source_format = (sizeof(mrs_real) == 8) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32;
00169 
00170   // Suppress useless warnings when both an AudioSource and
00171   // an AudioSink are being opened using ALSA:
00172   audio_->showWarnings(false);
00173 
00174   try
00175   {
00176     unsigned int uint_block_size = *block_size;
00177     audio_->openStream(NULL, &source_params, source_format, sample_rate,
00178                        &uint_block_size, &recordCallback, (void *)&shared, &options);
00179     *block_size = uint_block_size;
00180   }
00181   catch (RtError& e)
00182   {
00183     MRSERR("AudioSource: RtAudio error:");
00184     e.printMessage();
00185     exit(0);
00186   }
00187 
00188   audio_->showWarnings(true);
00189 }
00190 
00191 void
00192 AudioSource::start()
00193 {
00194   if ( stopped_ && isInitialized_ )
00195   {
00196     clearBuffer();
00197     audio_->startStream();
00198     stopped_ = false;
00199   }
00200 }
00201 
00202 void
00203 AudioSource::stop()
00204 {
00205   if ( !stopped_ )
00206   {
00207     audio_->stopStream();
00208     stopped_ = true;
00209   }
00210 }
00211 
00212 void
00213 AudioSource::localActivate(bool state)
00214 {
00215   if(state)
00216   {
00217     start();
00218   }
00219   else
00220   {
00221     stop();
00222   }
00223 }
00224 
00225 void AudioSource::clearBuffer()
00226 {
00227   assert(stopped_);
00228   shared.buffer.clear();
00229   shared.overrun = false;
00230 }
00231 
00232 bool AudioSource::reformatBuffer(mrs_natural source_block_size,
00233                                  mrs_natural dest_block_size,
00234                                  mrs_natural channel_count,
00235                                  bool realtime, bool resize)
00236 {
00237   mrs_natural new_capacity = source_block_size + dest_block_size + 1;
00238   if (!realtime)
00239     new_capacity = std::max( new_capacity * 4, (mrs_natural) 2000 );
00240 
00241   if (resize)
00242   {
00243     assert(stopped_);
00244     mrs_natural size = new_capacity * 2;
00245     if (size != shared.buffer.samples() || channel_count != shared.buffer.observations())
00246     {
00247       bool do_clear_buffer = true;
00248       shared.buffer.resize(channel_count, size, new_capacity, do_clear_buffer);
00249     }
00250     else
00251     {
00252       shared.buffer.set_capacity(new_capacity);
00253     }
00254     shared.watermark = realtime ? 0 : new_capacity / 2;
00255   }
00256   else
00257   {
00258     if (channel_count != shared.buffer.observations()
00259         || new_capacity > shared.buffer.samples())
00260     {
00261       MRSERR("AudioSource: Can not set requested buffer capacity or channel count without"
00262              " resizing the buffer!");
00263       return false;
00264     }
00265 
00266     //cout << "Changing capacity: " << new_capacity << "/" << shared.buffer.samples() << endl;
00267     mrs_natural new_watermark = realtime ? 0 : new_capacity / 2;;
00268     if (new_capacity > shared.buffer.capacity())
00269     {
00270       // First increase capacity, then watermark.
00271       shared.buffer.set_capacity(new_capacity);
00272       shared.watermark = new_watermark;
00273     }
00274     else
00275     {
00276       // First decrease watermark, then capacity.
00277       shared.watermark = new_watermark;
00278       shared.buffer.set_capacity(new_capacity);
00279     }
00280   }
00281 
00282   return true;
00283 }
00284 
00285 int
00286 AudioSource::recordCallback(void *outputBuffer, void *inputBuffer,
00287                             unsigned int nFrames,
00288                             double streamTime, unsigned int status,
00289                             void *userData)
00290 {
00291   (void) outputBuffer;
00292   (void) streamTime;
00293   (void) status;
00294 
00295   mrs_real* data = (mrs_real*)inputBuffer;
00296   InputData &shared = *((InputData*) userData);
00297   unsigned int nChannels = shared.channel_count;
00298 
00299   if (shared.overrun)
00300     shared.overrun = shared.buffer.write_capacity() <= shared.watermark;
00301 
00302   if (!shared.overrun)
00303   {
00304     // Limit scope of realvec_queue_producer!
00305     realvec_queue_producer producer( shared.buffer, nFrames );
00306 
00307     if (producer.capacity() == nFrames)
00308     {
00309       for (unsigned int t=0; t < nFrames; t++)
00310       {
00311         for (unsigned int ch=0; ch < nChannels; ch++) {
00312           producer(ch, t) = data[nChannels*t+ch];
00313         }
00314       }
00315     }
00316     else
00317     {
00318       shared.overrun = true;
00319       MRSWARN("AudioSource: buffer overrun!");
00320     }
00321   }
00322 
00323   shared.mutex.lock();
00324   shared.condition.notify_all();
00325   shared.mutex.unlock();
00326 
00327   return 0;
00328 }
00329 
00330 void
00331 AudioSource::myProcess(realvec& in, realvec& out)
00332 {
00333   (void) in;
00334 
00335   //check if RtAudio is initialized
00336   if (!isInitialized_)
00337   {
00338     return;
00339   }
00340 
00341   //check MUTE
00342   if(ctrl_mute_->isTrue())
00343   {
00344     return;
00345   }
00346 
00347   //assure that RtAudio thread is running
00348   //(this may be needed by if an explicit call to start()
00349   //is not done before ticking or calling process() )
00350   if ( stopped_ )
00351   {
00352     start();
00353   }
00354 
00355   realvec_queue_consumer consumer(shared.buffer, onSamples_);
00356 
00357   if ((mrs_natural) consumer.capacity() < onSamples_)
00358   {
00359     std::unique_lock<std::mutex> locker(shared.mutex);
00360 
00361     shared.condition.wait (
00362       locker,
00363       [&consumer, this]()
00364     {
00365       bool ok = consumer.reserve(onSamples_);
00366       if (shared.watermark > 0)
00367         ok = ok && shared.buffer.read_capacity() >= shared.watermark;
00368       return ok;
00369     }
00370     );
00371 
00372     locker.unlock();
00373   }
00374 
00375   MRSASSERT((mrs_natural) consumer.capacity() == onSamples_);
00376 
00377   for (mrs_natural t=0; t < onSamples_; t++)
00378   {
00379     for (mrs_natural o=0; o < onObservations_; o++)
00380     {
00381       out(o,t) = consumer(o,t);
00382     }
00383   }
00384 }
00385