Marsyas
0.6.0-alpha
|
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 }