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 "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 }