Marsyas  0.6.0-alpha
/usr/src/RPM/BUILD/marsyas-0.6.0/src/marsyas/marsystems/AimSSI.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 "AimSSI.h"
00020 #include "../common_source.h"
00021 #include "../ERBTools.h"
00022 
00023 using std::ostringstream;
00024 using namespace Marsyas;
00025 
00026 AimSSI::AimSSI(mrs_string name):MarSystem("AimSSI",name)
00027 {
00028   is_centre_frequencies_calculated = false;
00029   addControls();
00030 }
00031 
00032 
00033 AimSSI::~AimSSI()
00034 {
00035 }
00036 
00037 bool
00038 AimSSI::InitializeInternal() {
00039   return true;
00040 }
00041 
00042 void
00043 AimSSI::ResetInternal() {
00044 }
00045 
00046 // Calculates log2 of number.
00047 double
00048 AimSSI::Log2(double n) {
00049   // log(n)/log(2) is log2.
00050   return log( n ) / log( 2.0f );
00051 }
00052 
00053 
00054 MarSystem*
00055 AimSSI::clone() const
00056 {
00057   return new AimSSI(*this);
00058 }
00059 
00060 void
00061 AimSSI::addControls()
00062 {
00063   addControl("mrs_bool/do_pitch_cutoff", false , ctrl_do_pitch_cutoff_);
00064   addControl("mrs_bool/weight_by_cutoff", false , ctrl_weight_by_cutoff_);
00065   addControl("mrs_bool/weight_by_scaling", false , ctrl_weight_by_scaling_);
00066   addControl("mrs_bool/log_cycles_axis", true , ctrl_log_cycles_axis_);
00067   addControl("mrs_real/pitch_search_start_ms", 2.0 , ctrl_pitch_search_start_ms_);
00068   addControl("mrs_real/ssi_width_cycles", 10.0 , ctrl_ssi_width_cycles_);
00069   addControl("mrs_real/pivot_cf", 1000.0 , ctrl_pivot_cf_);
00070 
00071   // From AimGammatone
00072   addControl("mrs_real/min_frequency", 86.0 , ctrl_min_frequency_);
00073   addControl("mrs_real/max_frequency", 16000.0 , ctrl_max_frequency_);
00074 
00075 }
00076 
00077 void
00078 AimSSI::myUpdate(MarControlPtr sender)
00079 {
00080 
00081   (void) sender;  //suppress warning of unused parameter(s)
00082   MRSDIAG("AimSSI.cpp - AimSSI:myUpdate");
00083   ctrl_onSamples_->setValue(ctrl_inSamples_, NOUPDATE);
00084   ctrl_onObservations_->setValue(ctrl_inObservations_, NOUPDATE);
00085   ctrl_osrate_->setValue(ctrl_israte_, NOUPDATE);
00086   ctrl_onObsNames_->setValue("AimSSI_" + ctrl_inObsNames_->to<mrs_string>() , NOUPDATE);
00087 
00088   // sness - Used to be in InitializeInternal in AIM-C.  Would have
00089   // been really wasteful to keep track of all these variables that
00090   // need to be initialized.
00091 
00092   // sness - Hacking this for now
00093   ssi_width_samples_ = 512;
00094   // ssi_width_samples_ =  ctrl_israte_->to<mrs_real>() * ctrl_ssi_width_cycles_->to<mrs_real>() / ctrl_pivot_cf_->to<mrs_real>();
00095 
00096   if (ssi_width_samples_ > ctrl_inSamples_->to<mrs_natural>()) {
00097     ssi_width_samples_ = ctrl_inSamples_->to<mrs_natural>();
00098     double cycles = ssi_width_samples_ * ctrl_pivot_cf_->to<mrs_real>() / ctrl_israte_->to<mrs_real>();
00099     MRSWARN("Requested SSI width is too long for the input buffer");
00100     // MRSWARN("Requested SSI width of " + ctrl_ssi_width_cycles_->to<mrs_real>() + " cycles is too long for the " +
00101     //         "input buffer length of " + ctrl_inObservations_->to<mrs_natural>() + " samples. The SSI will be " +
00102     //             "truncated at " + ssi_width_samples_ + " samples wide. This corresponds to a width " +
00103     //             "of " + cycles + " cycles.")
00104     ctrl_ssi_width_cycles_ = cycles;
00105   }
00106 
00107   if (!is_centre_frequencies_calculated) {
00108     CalculateCentreFrequencies();
00109     is_centre_frequencies_calculated = true;
00110   }
00111 
00112 }
00113 
00114 // sness - Because we can't pass the centre_frequencies from one
00115 // MarSystem to the next, we need to recalculate them here.
00116 void
00117 AimSSI::CalculateCentreFrequencies() {
00118   int num_channels = ctrl_inObservations_->to<mrs_natural>();
00119   double erb_max = ERBTools::Freq2ERB(ctrl_max_frequency_->to<mrs_real>());
00120   double erb_min = ERBTools::Freq2ERB(ctrl_min_frequency_->to<mrs_real>());
00121   double delta_erb = (erb_max - erb_min) / (num_channels - 1);
00122 
00123   centre_frequencies_.resize(num_channels);
00124   double erb_current = erb_min;
00125 
00126   for (int i = 0; i < num_channels; ++i) {
00127     centre_frequencies_[i] = ERBTools::ERB2Freq(erb_current);
00128     erb_current += delta_erb;
00129   }
00130 }
00131 
00132 int
00133 AimSSI::ExtractPitchIndex(realvec& in) const {
00134   // Generate temporal profile of the SAI
00135   std::vector<double> sai_temporal_profile(ctrl_inSamples_->to<mrs_natural>(), 0.0);
00136   for (int i = 0; i < ctrl_inSamples_->to<mrs_natural>(); ++i) {
00137     double val = 0.0;
00138     for (int ch = 0; ch < ctrl_inObservations_->to<mrs_natural>(); ++ch) {
00139       val += in(ch, i);
00140     }
00141     sai_temporal_profile[i] = val;
00142   }
00143 
00144   // Find pitch value
00145   int start_sample = (int)floor(ctrl_pitch_search_start_ms_->to<mrs_real>() * ctrl_israte_->to<mrs_real>() / 1000.0);
00146   int max_idx = 0;
00147   double max_val = 0.0;
00148   for (int i = start_sample; i < ctrl_inSamples_->to<mrs_natural>(); ++i) {
00149     if (sai_temporal_profile[i] > max_val) {
00150       max_idx = i;
00151       max_val = sai_temporal_profile[i];
00152     }
00153   }
00154   return max_idx;
00155 }
00156 
00157 
00158 void
00159 AimSSI::myProcess(realvec& in, realvec& out)
00160 {
00161   int pitch_index = ctrl_inSamples_->to<mrs_natural>() - 1;
00162   if (ctrl_do_pitch_cutoff_->to<mrs_bool>()) {
00163     pitch_index = ExtractPitchIndex(in);
00164   }
00165 
00166   for (mrs_natural o = 0; o < ctrl_inObservations_->to<mrs_natural>(); o++) {
00167     double centre_frequency = centre_frequencies_[o];
00168     // Copy the buffer from input to output, addressing by h-value
00169     for (mrs_natural t = 0; t < ssi_width_samples_; t++) {
00170       double h;
00171       double cycle_samples = ctrl_israte_->to<mrs_real>() / centre_frequency;
00172       if (ctrl_log_cycles_axis_->to<mrs_bool>()) {
00173         double gamma_min = -1.0;
00174         double gamma_max = Log2(ctrl_ssi_width_cycles_->to<mrs_real>());
00175         double gamma = gamma_min + (gamma_max - gamma_min)
00176                        * static_cast<double>(t)
00177                        / static_cast<double>(ssi_width_samples_);
00178         h = pow(2.0, gamma);
00179       } else {
00180         h = static_cast<double>(t) * ctrl_ssi_width_cycles_->to<mrs_real>()
00181             / static_cast<double>(ssi_width_samples_);
00182       }
00183 
00184       // The index into the input array is a doubleing-point number, which is
00185       // split into a whole part and a fractional part. The whole part and
00186       // fractional part are found, and are used to linearly interpolate
00187       // between input samples to yield an output sample.
00188       double whole_part;
00189       double frac_part = modf(h * cycle_samples, &whole_part);
00190       int sample = (int)floor(whole_part);
00191 
00192       double weight = 1.0;
00193 
00194       int cutoff_index = ctrl_inSamples_->to<mrs_natural>() - 1;
00195       if (ctrl_do_pitch_cutoff_->to<mrs_bool>()) {
00196         if (pitch_index < cutoff_index) {
00197           if (ctrl_weight_by_cutoff_->to<mrs_bool>()) {
00198             weight *= static_cast<double>(ctrl_inSamples_->to<mrs_natural>())
00199                       / static_cast<double>(pitch_index);
00200           }
00201           cutoff_index = pitch_index;
00202         }
00203       }
00204 
00205       if (ctrl_weight_by_scaling_->to<mrs_bool>()) {
00206         if (centre_frequency > ctrl_pivot_cf_->to<mrs_real>()) {
00207           weight *= (centre_frequency / ctrl_pivot_cf_->to<mrs_real>());
00208         }
00209       }
00210 
00211       double val;
00212       if (sample < cutoff_index) {
00213         double curr_sample = in(o, sample);
00214         double next_sample = in(o, sample + 1);
00215         val = weight * (curr_sample
00216                         + frac_part * (next_sample - curr_sample));
00217       } else {
00218         val = 0.0;
00219       }
00220       out(o, t) = val;
00221     }
00222   }
00223 
00224 }