Marsyas  0.6.0-alpha
/usr/src/RPM/BUILD/marsyas-0.6.0/src/marsyas/marsystems/AimSAI.cpp
Go to the documentation of this file.
00001 /*
00002 ** Copyright (C) 1998-2010 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 "AimSAI.h"
00020 #include "../common_source.h"
00021 #include "../ERBTools.h"
00022 #include <algorithm>
00023 
00024 using std::ostringstream;
00025 using namespace Marsyas;
00026 
00027 AimSAI::AimSAI(mrs_string name):MarSystem("AimSAI",name)
00028 {
00029   is_initialized = false;
00030   initialized_israte = 0;
00031   initialized_inobservations = 0;
00032   initialized_insamples = 0;
00033   initialized_frame_period_ms = 0.0;
00034   initialized_min_delay_ms = 0.0;
00035   initialized_max_delay_ms = 0.0;
00036   initialized_buffer_memory_decay = 0.0;
00037   initialized_max_concurrent_strobes = 0;
00038   initialized_strobe_weight_alpha = 0.0;
00039 
00040   is_reset = false;
00041   reseted_israte = 0;
00042   reseted_inobservations = 0;
00043   reseted_frame_period_ms = 0;
00044 
00045   addControls();
00046 
00047   // Set default values
00048   min_strobe_delay_idx_ = 0;
00049   max_strobe_delay_idx_ = 0;
00050   sai_decay_factor_ = 0.0;
00051   fire_counter_ = 0;
00052 }
00053 
00054 AimSAI::~AimSAI()
00055 {
00056 }
00057 
00058 MarSystem*
00059 AimSAI::clone() const
00060 {
00061   return new AimSAI(*this);
00062 }
00063 
00064 void
00065 AimSAI::addControls()
00066 {
00067   addControl("mrs_real/min_delay_ms_", 0.0 , ctrl_min_delay_ms_);
00068   addControl("mrs_real/max_delay_ms_", 11.63266, ctrl_max_delay_ms_);
00069   addControl("mrs_real/strobe_weight_alpha_", 0.5 , ctrl_strobe_weight_alpha_);
00070   addControl("mrs_real/buffer_memory_decay_;", 0.03 , ctrl_buffer_memory_decay_);
00071   addControl("mrs_real/frame_period_ms_ ", 11.63266 , ctrl_frame_period_ms_);
00072   addControl("mrs_natural/max_concurrent_strobes_;", 50 , ctrl_max_concurrent_strobes_);
00073 }
00074 
00075 void
00076 AimSAI::myUpdate(MarControlPtr sender)
00077 {
00078   int temp_frame_period_samples = (int)(1 + floor(ctrl_israte_->to<mrs_real>() * ctrl_frame_period_ms_->to<mrs_real>()
00079                                         / 1000.0));
00080   MRSDIAG("AimSAI.cpp - AimSAI:myUpdate");
00081 
00082   (void) sender;  //suppress warning of unused parameter(s)
00083   ctrl_onSamples_->setValue(temp_frame_period_samples, NOUPDATE);
00084   ctrl_osrate_->setValue(ctrl_israte_->to<mrs_real>(), NOUPDATE);
00085   ctrl_onObsNames_->setValue("AimSAI_" + ctrl_inObsNames_->to<mrs_string>() , NOUPDATE);
00086 
00087   // The actual number of signal channels in the input.  The output
00088   // from PZFC + HCL + Localmax is a realvec with the first third of
00089   // observations being the signal, and the second third being the
00090   // channels and the last third being the strobes.
00091   channel_count_ = ctrl_inObservations_->to<mrs_natural>() / 3;
00092   ctrl_onObservations_->setValue(channel_count_, NOUPDATE);
00093 
00094   //
00095   // Does the MarSystem need initialization?
00096   //
00097   if (initialized_israte != ctrl_israte_->to<mrs_real>() ||
00098       initialized_inobservations != ctrl_inObservations_->to<mrs_natural>() ||
00099       initialized_insamples != ctrl_inSamples_->to<mrs_natural>() ||
00100       initialized_frame_period_ms != ctrl_frame_period_ms_->to<mrs_real>() ||
00101       initialized_min_delay_ms != ctrl_min_delay_ms_->to<mrs_real>() ||
00102       initialized_max_delay_ms != ctrl_max_delay_ms_->to<mrs_real>() ||
00103       initialized_buffer_memory_decay != ctrl_buffer_memory_decay_->to<mrs_real>() ||
00104       initialized_max_concurrent_strobes != ctrl_max_concurrent_strobes_->to<mrs_natural>() ||
00105       initialized_strobe_weight_alpha != ctrl_strobe_weight_alpha_->to<mrs_real>()) {
00106     is_initialized = false;
00107   }
00108 
00109   if (!is_initialized) {
00110     InitializeInternal();
00111     is_initialized = true;
00112     initialized_israte = ctrl_israte_->to<mrs_real>();
00113     initialized_inobservations = ctrl_inObservations_->to<mrs_natural>();
00114     initialized_insamples = ctrl_inSamples_->to<mrs_natural>();
00115     initialized_frame_period_ms = ctrl_frame_period_ms_->to<mrs_real>();
00116     initialized_min_delay_ms = ctrl_min_delay_ms_->to<mrs_real>();
00117     initialized_max_delay_ms = ctrl_max_delay_ms_->to<mrs_real>();
00118     initialized_buffer_memory_decay = ctrl_buffer_memory_decay_->to<mrs_real>();
00119     initialized_max_concurrent_strobes = ctrl_max_concurrent_strobes_->to<mrs_natural>();
00120     initialized_strobe_weight_alpha = ctrl_strobe_weight_alpha_->to<mrs_real>();
00121   }
00122 
00123   //
00124   // Does the MarSystem need a reset?
00125   //
00126   if (reseted_israte != ctrl_israte_->to<mrs_real>() ||
00127       reseted_inobservations != ctrl_inObservations_->to<mrs_natural>() ||
00128       reseted_frame_period_ms != ctrl_frame_period_ms_->to<mrs_real>()) {
00129     is_reset = false;
00130   }
00131 
00132   if (!is_reset) {
00133     ResetInternal();
00134     is_reset = true;
00135     reseted_israte = ctrl_israte_->to<mrs_real>();
00136     reseted_inobservations = ctrl_inObservations_->to<mrs_natural>();
00137     reseted_frame_period_ms = ctrl_frame_period_ms_->to<mrs_real>();
00138   }
00139 
00140 }
00141 
00142 void
00143 AimSAI::InitializeInternal() {
00144   // The SAI output bank must be as long as the SAI's Maximum delay.
00145   // One sample is added to the SAI buffer length to account for the
00146   // zero-lag point
00147   // int sai_buffer_length = 1 + floor(ctrl_israte_->to<mrs_real>() * ctrl_max_delay_ms_->to<mrs_real>()
00148   //                                   / 1000.0);
00149   // channel_count_ = ctrl_inObservations_->to<mrs_natural>() / 2;
00150   // cout << "channel_count=" << channel_count_ << endl;
00151 
00152 
00153   // // Make an output SignalBank with the same number of channels and centre
00154   // // frequencies as the input, but with a different buffer length
00155   // if (!output_.Initialize(input.channel_count(),
00156   //                         sai_buffer_length,
00157   //                         input.sample_rate())) {
00158   //   LOG_ERROR("Failed to create output buffer in SAI module");
00159   //   return false;
00160   // }
00161   // for (int i = 0; i < inObservations; ++i) {
00162   //   output_.set_centre_frequency(i, input.centre_frequency(i));
00163   // }
00164 
00165   // // sai_temp_ will be initialized to zero
00166   // if (!sai_temp_.Initialize(output_)) {
00167   //   LOG_ERROR("Failed to create temporary buffer in SAI module");
00168   //   return false;
00169   // }
00170 
00171   centre_frequencies_.resize(channel_count_);
00172 
00173   int temp_frame_period_samples = (int)(1 + floor(ctrl_israte_->to<mrs_real>() * ctrl_frame_period_ms_->to<mrs_real>()
00174                                         / 1000.0));
00175   sai_temp_.create(channel_count_,temp_frame_period_samples);
00176 
00177   frame_period_samples_ = (int)floor(ctrl_israte_->to<mrs_real>() * ctrl_frame_period_ms_->to<mrs_real>()
00178                                      / 1000.0);
00179   min_strobe_delay_idx_ = (int)floor(ctrl_israte_->to<mrs_real>() * ctrl_min_delay_ms_->to<mrs_real>()
00180                                      / 1000.0);
00181   max_strobe_delay_idx_ = (int)floor(ctrl_israte_->to<mrs_real>() * ctrl_max_delay_ms_->to<mrs_real>()
00182                                      / 1000.0);
00183 
00184   // Make sure we don't go past the output buffer's upper bound
00185   if (max_strobe_delay_idx_ > ctrl_onSamples_->to<mrs_natural>()) {
00186     max_strobe_delay_idx_ = ctrl_onSamples_->to<mrs_natural>();
00187   }
00188 
00189   // Define decay factor from time since last sample (see ti2003)
00190   sai_decay_factor_ = pow(0.5, 1.0 / (double)(ctrl_buffer_memory_decay_->to<mrs_real>() * (double)ctrl_israte_->to<mrs_real>()));
00191 
00192   // Precompute strobe weights
00193   strobe_weights_.resize(ctrl_max_concurrent_strobes_->to<mrs_natural>());
00194   for (int n = 0; n < ctrl_max_concurrent_strobes_->to<mrs_natural>(); ++n) {
00195     strobe_weights_[n] = pow(1.0 / (n + 1), (double)ctrl_strobe_weight_alpha_->to<mrs_real>());
00196   }
00197 }
00198 
00199 void
00200 AimSAI::ResetInternal() {
00201   // Active Strobes
00202   active_strobes_.clear();
00203   active_strobes_.resize(channel_count_);
00204   fire_counter_ = frame_period_samples_ - 1;
00205 }
00206 
00207 
00208 // We are passed a realvec with the signals and then the strobes for
00209 // the signals, so we have twice the number of observations that we
00210 // would usually have.  Take the strobes part of the realvec and find
00211 // the strobes for each observation.
00212 void
00213 AimSAI::findStrobes(realvec& in) {
00214   mrs_natural _inSamples = ctrl_inSamples_->to<mrs_natural>();
00215 
00216   strobes_.clear();
00217   strobes_.resize(channel_count_);
00218   for (int o = 0; o < channel_count_; o++) {
00219     strobes_[o].clear();
00220     for (int t = 0; t < _inSamples; t++) {
00221       if (in(o + channel_count_ + channel_count_,t) == 1) {
00222         strobes_[o].push_back(t);
00223       }
00224     }
00225   }
00226 
00227   // for (int o = 0; o < channel_count_; o++) {
00228   //   // cout << "ch=" << o << " strobes=";
00229   //   for (unsigned int i=0; i < strobes_[o].size(); ++i) {
00230   //     cout << strobes_[o][i] << " ";
00231   //   }
00232   //   cout << endl;
00233   // }
00234 
00235   // exit(0);
00236 
00237 }
00238 
00239 // // sness - Because we can't pass the centre_frequencies from one
00240 // // MarSystem to the next, we need to recalculate them here.
00241 // void
00242 // AimSAI::CalculateCentreFrequencies() {
00243 //   // sness - Need to divide by two because there is one strobe channel
00244 //   // for every observation channel
00245 //   int num_channels = ctrl_inObservations_->to<mrs_natural>() / 2;
00246 //   double erb_min = ERBTools::Freq2ERB(ctrl_min_frequency_->to<mrs_real>());
00247 //   double erb_max = ERBTools::Freq2ERB(ctrl_max_frequency_->to<mrs_real>());
00248 //   double delta_erb = (erb_max - erb_min) / (num_channels - 1);
00249 
00250 //   centre_frequencies_.resize(num_channels);
00251 //   double erb_current = erb_min;
00252 
00253 //   for (int i = 0; i < num_channels; ++i) {
00254 //     centre_frequencies_[i] = ERBTools::ERB2Freq(erb_current);
00255 //     erb_current += delta_erb;
00256 //   }
00257 // }
00258 
00259 void
00260 AimSAI::myProcess(realvec& in, realvec& out)
00261 {
00262   // cout << "AimSAI::myProcess" << endl;
00263   size_t _max_concurrent_strobes = (size_t) std::max((mrs_natural) 0, ctrl_max_concurrent_strobes_->to<mrs_natural>());
00264   mrs_real _israte = ctrl_israte_->to<mrs_real>();
00265   // mrs_natural _inSamples = ctrl_inSamples_->to<mrs_natural>();
00266 
00267   // Grab the centre frequencies from the input
00268   for (int o = 0; o < channel_count_; ++o) {
00269     centre_frequencies_[o] = in(o + channel_count_);
00270     // cout << "centre_frequencies_[" << o << "]=" << centre_frequencies_[o] << endl;
00271   }
00272 
00273   findStrobes(in);
00274 
00275   // Reset the next strobe times
00276   next_strobes_.clear();
00277   next_strobes_.resize(channel_count_, 0);
00278 
00279   // Offset the times on the strobes from the previous buffer
00280   for (int o = 0; o < channel_count_; ++o) {
00281     active_strobes_[o].ShiftStrobes(ctrl_inSamples_->to<mrs_natural>());
00282   }
00283 
00284   // Loop over samples to make the SAI
00285   for (int t = 0; t < ctrl_inSamples_->to<mrs_natural>(); ++t)
00286   {
00287     double decay_factor = pow(sai_decay_factor_, fire_counter_);
00288 
00289     // Loop over channels
00290     for (int o = 0; o < channel_count_; ++o)
00291     {
00292       // cout << "o=" << o << endl;
00293       // cout << "\tch=" << o << endl;
00294       // Local convenience variables
00295       StrobeList &active_strobes = active_strobes_[o];
00296       unsigned int next_strobe_index = next_strobes_[o];
00297 
00298       // Update strobes
00299       // If we are up to or beyond the next strobe...
00300       if (next_strobe_index < strobes_[o].size()) {
00301         // cout << "(next_strobe_index < strobes_[o].size())" << endl;
00302         // cout << "\t\tnext_strobe_index" << next_strobe_index << endl;
00303         // cout << "\t\tinput.strobe(ch, next_strobe_index)=" << strobes_[o][next_strobe_index] << endl;
00304 
00305         if (t == strobes_[o][next_strobe_index]) {
00306           // cout << "(t == strobes_[o][next_strobe_index])" << endl;
00307           // A new strobe has arrived.
00308           // If there are too many strobes active, then get rid of the
00309           // earliest one
00310           if (active_strobes.strobe_count() >= _max_concurrent_strobes) {
00311             active_strobes.DeleteFirstStrobe();
00312           }
00313 
00314           // Add the active strobe to the list of current strobes and
00315           // calculate the strobe weight
00316           double weight = 1.0;
00317           if (active_strobes.strobe_count() > 0) {
00318             int last_strobe_time = active_strobes.Strobe(
00319                                      active_strobes.strobe_count() - 1).time;
00320 
00321             // If the strobe occured within 10 impulse-response
00322             // cycles of the previous strobe, then lower its weight
00323             // cout << "t=" << t << "\tlast_strobe_time=" << last_strobe_time << "_israte=" << _israte << " cf=" << centre_frequencies_[o] << endl;
00324             weight = (t - last_strobe_time) / _israte
00325                      * centre_frequencies_[o] / 10.0;
00326             if (weight > 1.0)
00327               weight = 1.0;
00328           }
00329           active_strobes.AddStrobe(t, weight);
00330           next_strobe_index++;
00331 
00332           // Update the strobe weights
00333           double total_strobe_weight = 0.0;
00334           for (size_t si = 0; si < active_strobes.strobe_count(); ++si) {
00335             // cout << "si=" << si << "\tw=" << active_strobes.Strobe(si).weight << "\tw2=" << strobe_weights_[active_strobes.strobe_count() - si - 1] << endl;
00336             total_strobe_weight += (active_strobes.Strobe(si).weight
00337                                     * strobe_weights_[active_strobes.strobe_count() - si - 1]);
00338           }
00339           for (size_t si = 0; si < active_strobes.strobe_count(); ++si) {
00340             // cout << "si=" << si << "\tw1=" << active_strobes.Strobe(si).weight << "\tw2=" << strobe_weights_[active_strobes.strobe_count() - si - 1] << " tot=" << total_strobe_weight << endl;
00341             active_strobes.SetWorkingWeight(si,
00342                                             (active_strobes.Strobe(si).weight
00343                                              * strobe_weights_[active_strobes.strobe_count() - si - 1])
00344                                             / total_strobe_weight);
00345           }
00346         }
00347       }
00348 
00349       // Remove inactive strobes
00350       while (active_strobes.strobe_count() > 0) {
00351         // Get the relative time of the first strobe, and see if it exceeds
00352         // the maximum allowed time.
00353         if ((t - active_strobes.Strobe(0).time) > max_strobe_delay_idx_) {
00354           active_strobes.DeleteFirstStrobe();
00355         } else {
00356           // cout << "break" << endl;
00357           break;
00358         }
00359       }
00360 
00361       // Update the SAI buffer with the weighted effect of all the active
00362       // strobes at the current sample
00363       for (size_t si = 0; si < active_strobes.strobe_count(); ++si) {
00364         // cout << "si=" << si << endl;
00365         // Add the effect of active strobe at correct place in the SAI buffer
00366         // Calculate 'delay', the time from the strobe event to now
00367         int delay = t - active_strobes.Strobe(si).time;
00368 
00369         // If the delay is greater than the (user-set)
00370         // minimum strobe delay, the strobe can be used
00371         if (delay >= min_strobe_delay_idx_ && delay < max_strobe_delay_idx_) {
00372           // The value at be added to the SAI
00373           double sig = in(o, t);
00374           // cout << "sig=" << sig << "\tww=" << active_strobes.Strobe(si).working_weight << "\tdecay=" << decay_factor << endl;
00375 
00376           // Weight the sample correctly
00377           sig *= active_strobes.Strobe(si).working_weight;
00378 
00379           // Adjust the weight acording to the number of samples until the
00380           // next output frame
00381           sig *= decay_factor;
00382 
00383           // Update the temporary SAI buffer
00384           // cout << "output1(" << o << "," << delay << ")=" << out(o, delay) + sig << endl;
00385           sai_temp_(o, delay) = sai_temp_(o, delay) + sig;
00386         }
00387       }
00388 
00389       next_strobes_[o] = next_strobe_index;
00390     }  // End loop over channels
00391 
00392     fire_counter_--;
00393 
00394     // cout << "fire_counter_=" << fire_counter_ << endl;
00395 
00396     // Check to see if we need to output an SAI frame on this sample
00397     if (fire_counter_ <= 0) {
00398       // cout << "(fire_counter_ <= 0)" << endl;
00399       // Decay the SAI by the correct amount and add the current output frame
00400       double decay = pow(sai_decay_factor_, frame_period_samples_);
00401 
00402       for (int o = 0; o < channel_count_; ++o) {
00403         // for (int t = 0; t < _inSamples; ++t ) {
00404         for (int t = 0; t < frame_period_samples_; ++t ) {
00405           // cout << "output2(" << o << "," << t << ")=" << sai_temp_(o,t) + out(o,t) * decay << endl;
00406           out(o, t) = sai_temp_(o,t) + out(o,t) * decay;
00407         }
00408       }
00409 
00410       // Zero the temporary signal
00411       for (int o = 0; o < sai_temp_.getRows(); ++o) {
00412         for (int t = 0; t < sai_temp_.getCols(); ++t) {
00413           sai_temp_(o, t) =  0.0;
00414         }
00415       }
00416       fire_counter_ = frame_period_samples_ - 1;
00417     }
00418   }  // End loop over samples
00419 
00420   // Copy over the strobes
00421   // for (int t = 0; t < ctrl_inSamples_->to<mrs_natural>(); ++t) {
00422   //   for (int o = channel_count_; o < channel_count_ * 2; ++o) {
00423   //     out(o,t) = in(o,t);
00424   //   }
00425   // }
00426 
00427 }