Marsyas  0.6.0-alpha
/usr/src/RPM/BUILD/marsyas-0.6.0/src/marsyas/marsystems/AimLocalMax.cpp
Go to the documentation of this file.
00001 /*
00002 ** Copyright (C) 1998-2006 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 "AimLocalMax.h"
00020 #include "../common_source.h"
00021 
00022 using std::ostringstream;
00023 using namespace Marsyas;
00024 
00025 AimLocalMax::AimLocalMax(mrs_string name):MarSystem("AimLocalMax",name)
00026 {
00027   is_initialized = false;
00028   initialized_israte = 0.0;
00029 
00030   is_reset = false;
00031   reset_inobservations = -1;
00032 
00033   addControls();
00034 }
00035 
00036 AimLocalMax::AimLocalMax(const AimLocalMax& a) : MarSystem(a)
00037 {
00038   // For any MarControlPtr in a MarSystem
00039   // it is necessary to perform this getctrl
00040   // in the copy constructor in order for cloning to work
00041   ctrl_decay_time_ms_ = getctrl("mrs_real/decay_time_ms");
00042   ctrl_timeout_ms_ = getctrl("mrs_real/timeout_ms");
00043 
00044   is_initialized = false;
00045   initialized_israte = 0.0;
00046 
00047   is_reset = false;
00048   reset_inobservations = -1;
00049 
00050 }
00051 
00052 
00053 AimLocalMax::~AimLocalMax()
00054 {
00055 }
00056 
00057 MarSystem*
00058 AimLocalMax::clone() const
00059 {
00060   return new AimLocalMax(*this);
00061 }
00062 
00063 void
00064 AimLocalMax::addControls()
00065 {
00066   addControl("mrs_real/decay_time_ms", 20.0 , ctrl_decay_time_ms_);
00067   addControl("mrs_real/timeout_ms", 3.0 , ctrl_timeout_ms_);
00068 }
00069 
00070 void
00071 AimLocalMax::myUpdate(MarControlPtr sender)
00072 {
00073   (void)sender;
00074 
00075   MRSDIAG("AimLocalMax.cpp - AimLocalMax:myUpdate");
00076   ctrl_onSamples_->setValue(ctrl_inSamples_, NOUPDATE);
00077 
00078   // The output from PZFC (and HCL) is a realvec with the first half
00079   // of observations being the signal, and the second half being the
00080   // channels.  So, the output of AimLocalMax is a realvec with 3
00081   // times the number of channels, with the first block of
00082   // observations being the signal (unchanged) the second block being
00083   // the centre frequencies (unchanged) and the third block being the
00084   // newly calculated strobe points.
00085   channel_count_ = ctrl_inObservations_->to<mrs_natural>() / 2;
00086   ctrl_onObservations_->setValue(channel_count_ * 3);
00087 
00088   ctrl_osrate_->setValue(ctrl_israte_, NOUPDATE);
00089   ctrl_onObsNames_->setValue("AimLocalMax_" + ctrl_inObsNames_->to<mrs_string>() , NOUPDATE);
00090 
00091   //
00092   // Does the MarSystem need initialization?
00093   //
00094   if (initialized_israte != ctrl_israte_->to<mrs_real>()) {
00095     is_initialized = false;
00096   }
00097 
00098   if (!is_initialized) {
00099     InitializeInternal();
00100     is_initialized = true;
00101     initialized_israte = ctrl_israte_->to<mrs_real>();
00102   }
00103 
00104   //
00105   // Does the MarSystem need a reset?
00106   //
00107   if (reset_inobservations != ctrl_inObservations_->to<mrs_natural>()) {
00108     is_reset = false;
00109   }
00110 
00111   if (!is_reset) {
00112     ResetInternal();
00113     is_reset = true;
00114     reset_inobservations = ctrl_inObservations_->to<mrs_natural>();
00115   }
00116 
00117 }
00118 
00119 
00120 bool
00121 AimLocalMax::InitializeInternal() {
00122   strobe_timeout_samples_ = (int) floor(ctrl_timeout_ms_->to<mrs_real>() * ctrl_israte_->to<mrs_real>() / 1000.0);
00123   strobe_decay_samples_ = (int) floor(ctrl_decay_time_ms_->to<mrs_real>() * ctrl_israte_->to<mrs_real>() / 1000.0);
00124   return true;
00125 
00126 }
00127 
00128 void
00129 AimLocalMax::ResetInternal() {
00130   threshold_.clear();
00131   threshold_.resize(channel_count_, 0.0);
00132 
00133   decay_constant_.clear();
00134   decay_constant_.resize(channel_count_, 1.0);
00135 
00136   prev_sample_.clear();
00137   prev_sample_.resize(channel_count_, 10000.0);
00138   curr_sample_.clear();
00139   curr_sample_.resize(channel_count_, 5000.0);
00140   next_sample_.clear();
00141   next_sample_.resize(channel_count_, 0.0);
00142 }
00143 
00144 
00145 
00146 void
00147 AimLocalMax::myProcess(realvec& in, realvec& out)
00148 {
00149   mrs_natural o,t;
00150 
00151   // sness - Need this because we don't have a SignalBuffer class like AIM-C has, so we
00152   // have to keep track of the strobes ourselves.
00153   strobe_count_.clear();
00154   strobe_count_.resize(channel_count_, 0.0);
00155   last_strobe_.clear();
00156   last_strobe_.resize(channel_count_, 0); // vector of ints
00157 
00158   // sness - Hmm, the original code isn't doing this, but I don't see
00159   // how this could possibly work if you don't reset these samples at
00160   // the beginning of a buffer.  Otherwise, you get carryover from the
00161   // last step that causes the line after the condition
00162   // "strobe_count_[o] > 0" to coredump.  Requires further
00163   // investigation.
00164   prev_sample_.clear();
00165   prev_sample_.resize(channel_count_, 10000.0);
00166   curr_sample_.clear();
00167   curr_sample_.resize(channel_count_, 5000.0);
00168   next_sample_.clear();
00169   next_sample_.resize(channel_count_, 0.0);
00170 
00171   // Skip over the signals and centre frequencies from PZFC (and HCL)
00172   mrs_natural skip_channels = channel_count_ + channel_count_;
00173 
00174   for (t = 0; t < ctrl_inSamples_->to<mrs_natural>(); t++) {
00175     for (o = 0; o < channel_count_; o++) {
00176       // Initialize the strobe
00177       out(o + skip_channels,t) = 0.0;
00178 
00179       // curr_sample is the sample at time (i - 1)
00180       prev_sample_[o] = curr_sample_[o];
00181       curr_sample_[o] = next_sample_[o];
00182       next_sample_[o] = in(o, t);
00183 
00184       // If the current sample is above threshold, the threshold is raised to
00185       // the level of the current sample, and decays from there.
00186       if (curr_sample_[o] >= threshold_[o]) {
00187         threshold_[o] = curr_sample_[o];
00188         decay_constant_[o] = threshold_[o] / strobe_decay_samples_;
00189         // If the current sample is also a peak, then it is a potential strobe
00190         // point.
00191         if (prev_sample_[o] < curr_sample_[o]
00192             && next_sample_[o] < curr_sample_[o]) {
00193           // If there are no strobes so far in this channel, then the sample
00194           // is definitely a strobe (this means that the timeout is not
00195           // respected across frame boundaries. This is a minor bug, but I
00196           // don't believe that it's serious enough to warrant updating the
00197           // samples since last strobe all the time.)
00198           if (strobe_count_[o] > 0) {
00199             // If there are previous strobes, then calculate the time since
00200             // the last one. If it's long enough, then this is a strobe point,
00201             // if not, then just move on.
00202             int samples_since_last = (t - 1) - last_strobe_[o];
00203             if (samples_since_last > strobe_timeout_samples_) {
00204               out(o + skip_channels, t-1) = 1.0;
00205               strobe_count_[o]++;
00206               last_strobe_[o] = t;
00207             }
00208           } else {
00209             out(o + skip_channels, t-1) = 1.0;
00210             strobe_count_[o]++;
00211             last_strobe_[o] = t;
00212           }
00213         }
00214       }
00215 
00216       // Update the threshold, decaying as necessary
00217       if (threshold_[o] > decay_constant_[o])
00218         threshold_[o] -= decay_constant_[o];
00219       else
00220         threshold_[o] = 0.0;
00221     }
00222   }
00223 
00224   // Now that we've added strobes as a second set of observations
00225   // after the first, copy the the data from the input to the output.
00226   for (t = 0; t < ctrl_inSamples_->to<mrs_natural>(); t++) {
00227     for (o = 0; o < skip_channels; o++) {
00228       out(o,t) = in(o,t);
00229     }
00230   }
00231 }