Marsyas  0.5.0-beta1
/Users/jleben/code/marsyas/src/marsyas/marsystems/BeatAgent.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 "BeatAgent.h"
00020 #include <string.h>
00021 #include "../common_source.h"
00022 
00023 using namespace std;
00024 using namespace Marsyas;
00025 
00026 #define NONE 0.0
00027 #define BEAT 1.0
00028 #define EVAL 2.0
00029 
00030 #define INNER 3.0
00031 #define OUTTER 4.0
00032 
00033 BeatAgent::BeatAgent(mrs_string name):MarSystem("BeatAgent", name)
00034 {
00035   addControls();
00036   beatCount_ = 0;
00037   score_ = 0;
00038   curBeatPointValue_ = 0;
00039   myIndex_ = -1;
00040   fraction_ = 0.0;
00041 }
00042 
00043 BeatAgent::BeatAgent(const BeatAgent& a) : MarSystem(a)
00044 {
00045   // For any MarControlPtr in a MarSystem
00046   // it is necessary to perform this getctrl
00047   // in the copy constructor in order for cloning to work
00048   ctrl_identity_ = getctrl("mrs_string/identity");
00049   ctrl_timming_ = getctrl("mrs_natural/timming");
00050   ctrl_agentControl_ = getctrl("mrs_realvec/agentControl");
00051   ctrl_scoreFunc_ = getctrl("mrs_string/scoreFunc");
00052   ctrl_lftOutterMargin_ = getctrl("mrs_real/lftOutterMargin");
00053   ctrl_rgtOutterMargin_ = getctrl("mrs_real/rgtOutterMargin");
00054   ctrl_innerMargin_ = getctrl("mrs_real/innerMargin");
00055   ctrl_maxPeriod_ = getctrl("mrs_natural/maxPeriod");
00056   ctrl_minPeriod_ = getctrl("mrs_natural/minPeriod");
00057 
00058   beatCount_ = a.beatCount_;
00059   lastBeatPoint_ = a.lastBeatPoint_;
00060   score_ = a.score_;
00061   curBeatPointValue_ = a.curBeatPointValue_;
00062   myIndex_ = a.myIndex_;
00063 }
00064 
00065 BeatAgent::~BeatAgent()
00066 {
00067 }
00068 
00069 MarSystem*
00070 BeatAgent::clone() const
00071 {
00072   return new BeatAgent(*this);
00073 }
00074 
00075 void
00076 BeatAgent::addControls()
00077 {
00078   //Add specific controls needed by this MarSystem.
00079   addctrl("mrs_string/identity", "AgentX", ctrl_identity_);
00080   addctrl("mrs_natural/timming", 0, ctrl_timming_);
00081   addctrl("mrs_realvec/agentControl", realvec(), ctrl_agentControl_);
00082   addctrl("mrs_string/scoreFunc", "regular", ctrl_scoreFunc_);
00083   setctrlState("mrs_string/scoreFunc", true);
00084   addctrl("mrs_real/lftOutterMargin", 0.2, ctrl_lftOutterMargin_);
00085   setctrlState("mrs_real/lftOutterMargin", true);
00086   addctrl("mrs_real/rgtOutterMargin", 0.4, ctrl_rgtOutterMargin_);
00087   setctrlState("mrs_real/rgtOutterMargin", true);
00088   addctrl("mrs_real/innerMargin", 3.0, ctrl_innerMargin_);
00089   setctrlState("mrs_real/innerMargin", true);
00090   addctrl("mrs_natural/maxPeriod", -1, ctrl_maxPeriod_);
00091   setctrlState("mrs_natural/maxPeriod", true);
00092   addctrl("mrs_natural/minPeriod", -1, ctrl_minPeriod_);
00093   setctrlState("mrs_natural/minPeriod", true);
00094 }
00095 
00096 void
00097 BeatAgent::myUpdate(MarControlPtr sender)
00098 {
00099   (void) sender;  //suppress warning of unused parameter(s)
00100   MRSDIAG("BeatAgent.cpp - BeatAgent:myUpdate");
00101 
00102   ctrl_onSamples_->setValue(6, NOUPDATE);
00103   ctrl_onObservations_->setValue(1, NOUPDATE);
00104   ctrl_osrate_->setValue(ctrl_israte_, NOUPDATE);
00105 
00106   //history_.create(1000,2);
00107   lastBeatPoint_ = inSamples_-1;
00108 
00109   myIndex_ = getChildIndex();
00110   if(myIndex_ == -1) {
00111     MRSWARN("Agent Index Not Found!");
00112   }
00113 
00114   scoreFunc_ = ctrl_scoreFunc_->to<mrs_string>();
00115 
00116   lftOutterMargin_ = ctrl_lftOutterMargin_->to<mrs_real>();
00117   rgtOutterMargin_ = ctrl_rgtOutterMargin_->to<mrs_real>();
00118   innerMargin_ = ctrl_innerMargin_->to<mrs_real>();
00119   maxPeriod_ = ctrl_maxPeriod_->to<mrs_natural>();
00120   minPeriod_ = ctrl_minPeriod_->to<mrs_natural>();
00121 }
00122 
00123 
00124 
00125 mrs_real
00126 BeatAgent::calcDScoreCorrSquare(realvec& in)
00127 {
00128   mrs_real dScore = 0.0;
00129 
00130   //outterLeft Tolerance:
00131   for(mrs_natural t = lastBeatPoint_ - outterWinLft_; t < lastBeatPoint_ - innerWin_; t++)
00132   {
00133     fraction_ = (mrs_real) abs(error_) / outterWinRgt_;
00134     //fraction_ = (mrs_real) abs(lastBeatPoint_-t) / (((outterWinRgt_+outterWinLft_)/2)+0.5);
00135     //dScore += -1 * pow((fraction_)* in(t),2) ;
00136     dScore += -1 * pow((fraction_),2) * in(t);
00137   }
00138 
00139   //innerTolerance:
00140   for(mrs_natural t = lastBeatPoint_ - innerWin_; t <= lastBeatPoint_ + innerWin_; t++)
00141   {
00142     fraction_ = (mrs_real) abs(error_) / (((outterWinRgt_+outterWinLft_)/2)+0.5);
00143     //fraction_ = (mrs_real) abs(lastBeatPoint_-t) / (((outterWinRgt_+outterWinLft_)/2)+0.5);
00144     //fraction_ = (mrs_real) abs(lastBeatPoint_-t) / outterWinRgt_;
00145     dScore += pow((1 - fraction_),2) * in(t);
00146     //dScore += pow((1 - fraction_) * in(t),2);
00147   }
00148 
00149   //outterRight Tolerance:
00150   for(mrs_natural t = (lastBeatPoint_ + innerWin_)+1; t <= lastBeatPoint_ + outterWinRgt_; t++)
00151   {
00152     fraction_ = (mrs_real) abs(error_) / outterWinLft_;
00153     //fraction_ = (mrs_real) abs(lastBeatPoint_-t) / outterWinRgt_;
00154     //dScore += -1 * pow((fraction_)* in(t),2) ;
00155     dScore += -1 * pow((fraction_),2) * in(t);
00156   }
00157 
00158   //if(strcmp(identity_.c_str(), "Agent0") == 0);
00159   //    cout << "MAX: " << max << "; ERRROR: " << error_ << "; dSCORE: " << dScore << endl;
00160 
00161   //dScore /= (outterWinLft_+outterWinRgt_); //normalized by the full window size
00162   //multiplied by sqrt(period) for disinflating the faster agents (with smaller periods) [!]
00163   //return dScore * sqrt((mrs_real)period_) / (outterWinLft_+outterWinRgt_);
00164   return dScore * (period_ / maxPeriod_);
00165 }
00166 
00167 mrs_real
00168 BeatAgent::calcDScoreCorr(realvec& in, mrs_natural maxInd)
00169 {
00170   (void) maxInd; // [!] what was this supposed to do?
00171   mrs_real dScore = 0.0;
00172 
00173   //outterLeft Tolerance:
00174   for(mrs_natural t = lastBeatPoint_ - outterWinLft_; t < lastBeatPoint_ - innerWin_; t++)
00175   {
00176     fraction_ = (mrs_real) abs(error_) / outterWinRgt_;
00177     //fraction_ = (mrs_real) abs(lastBeatPoint_-t) / outterWinRgt_;
00178     dScore += (-1 * fraction_) * in(t);//pow(in(t),2);
00179   }
00180 
00181   //innerTolerance:
00182   for(mrs_natural t = lastBeatPoint_ - innerWin_; t <= lastBeatPoint_ + innerWin_; t++)
00183   {
00184     fraction_ = (mrs_real) abs(error_) / outterWinRgt_;//(((outterWinRgt_+outterWinLft_)/2)+0.5);
00185     //fraction_ = (mrs_real) abs(lastBeatPoint_-t) / outterWinRgt_;//(((outterWinRgt_+outterWinLft_)/2)+0.5);
00186     dScore += (1 - fraction_) * in(t);//pow(in(t),2);
00187   }
00188 
00189   //outterRight Tolerance:
00190   for(mrs_natural t = (lastBeatPoint_ + innerWin_)+1; t <= lastBeatPoint_ + outterWinRgt_; t++)
00191   {
00192     fraction_ = (mrs_real) abs(error_) / outterWinRgt_;
00193     //fraction_ = (mrs_real) abs(lastBeatPoint_-t) / outterWinRgt_;
00194     dScore += (-1 * fraction_) * in(t);//pow(in(t),2);
00195   }
00196 
00197   //dScore /= (outterWinLft_+outterWinRgt_); //normalized by the full window size
00198   //multiplied by sqrt(period) for disinflating the faster agents (with smaller periods) [!]
00199   //return dScore * sqrt((mrs_real)period_) / (outterWinLft_+outterWinRgt_);
00200   return dScore * (period_ / maxPeriod_);
00201   //return dScore;
00202 }
00203 
00204 mrs_natural
00205 BeatAgent::getChildIndex()
00206 {
00207   //check for parent:
00208   MarSystem* parent = this->getParent();
00209   myIndex_ = -1;
00210   if(parent)
00211   {
00212     vector<MarSystem*> siblings = parent->getChildren();
00213     for(mrs_natural i = 0; i < (mrs_natural)siblings.size(); i++)
00214     {
00215       if(this == siblings[i])
00216       {
00217         myIndex_ = i;
00218         break;
00219       }
00220     }
00221   }
00222   return myIndex_;
00223 }
00224 
00225 void
00226 BeatAgent::fillOutput(realvec& out, mrs_real flag, mrs_real period, mrs_real curBeat,
00227                       mrs_real tolerance, mrs_real error, mrs_real score)
00228 {
00229   out(0) = flag;
00230   out(1) = period;
00231   out(2) = curBeat;
00232   out(3) = tolerance;
00233   out(4) = error;
00234   out(5) = score;
00235 }
00236 
00237 void
00238 BeatAgent::myProcess(realvec& in, realvec& out)
00239 {
00240   //Output Format: [Beat/Eval/None|Period|CurBeat|Inner/Outter|Error|Score] -> OnSamples = 6
00241   agentControl_ = ctrl_agentControl_->to<mrs_realvec>();
00242 
00243   //timeElapsed_ is constantly updated with the referee's next time frame
00244   timeElapsed_ = (mrs_natural) agentControl_(myIndex_, 3);
00245 
00246   //cout << "Agent: " << myIndex_ << "-" << timeElapsed_ << endl;
00247 
00248   //At first no beat info is considered - while no beat detected:
00249   fillOutput(out, NONE, 0.0, 0.0, 0.0, 0.0, 0.0);
00250 
00251   identity_ = ctrl_identity_->to<mrs_string>();
00252 
00253   isNewOrUpdated_ = (mrs_bool) (agentControl_(myIndex_, 0) == 1);
00254   period_ = (mrs_natural) agentControl_(myIndex_, 1);
00255   phase_ = (mrs_natural) agentControl_(myIndex_, 2);
00256   periodFraction_ = ((mrs_real)period_ / (mrs_real)maxPeriod_);
00257 
00258   outterWinLft_ = (mrs_natural) ceil(period_ * lftOutterMargin_); //(% of IBI)
00259   outterWinRgt_ = (mrs_natural) ceil(period_ * rgtOutterMargin_); //(% of IBI)
00260   innerWin_ = (mrs_natural) innerMargin_;
00261   //innerWin_ = (mrs_natural) min(4.0, ceil(period_ * innerMargin_));
00262 
00263   mrs_natural curBeatPoint = inSamples_-1; //curBeatPoint always point to the end point of the full flux window.
00264 
00265   mrs_real max = 0.0;
00266   mrs_natural max_i = 0;
00267 
00268   //If the agent was just created or its hypothesis updated:
00269   if(isNewOrUpdated_)
00270     curBeat_ = phase_;
00271   //If the agent already existed and is keeping its hypothesis:
00272   else
00273     curBeat_ = prevBeat_ + period_;
00274 
00275   //cout << "t:" << timeElapsed_ << "-" << identity_ << " -> BEAT: " << curBeat_ << "(" << beatCount_ << ")" << endl;
00276 
00277   //Considers beat hypothesis every phase + period
00278   if(timeElapsed_ == curBeat_)
00279   {
00280     //cout << "t:" << timeElapsed_ << "-" << identity_ << " -> BEAT (" << beatCount_ << ")" << endl;
00281 
00282     //Beat Info filling the remaining indexes of output with undef. value
00283     fillOutput(out, BEAT, -1.0, -1.0, -1.0, -1.0, -1.0);
00284 
00285     curBeatPointValue_ = in(curBeatPoint);
00286 
00287     //history_(beatCount_,0) = timeElapsed_;
00288     //history_(beatCount_,1) = curBeatPointValue_;
00289 
00290     //lastBeatPoint points to the beat time point to be evaluated
00291     //(corresponds to the end point of the flux window - the outter rgt tolerance)
00292     lastBeatPoint_ = curBeatPoint - outterWinRgt_;
00293 
00294     beatCount_++;
00295 
00296     return;
00297   }
00298 
00299   mrs_natural evalPoint = curBeat_ + outterWinRgt_;
00300   //Evaluates each beat at the end of its beat position + outterWindow tolerance:
00301   if(timeElapsed_ == evalPoint)
00302   {
00303     //point in flux window corresponding to the beat time point being evaluated
00304     max_i = lastBeatPoint_;
00305 
00306     for(mrs_natural t = lastBeatPoint_ - outterWinLft_; t <= lastBeatPoint_ + outterWinRgt_; t++)
00307     {
00308       //check over the full eval window ([lastBeatPoint_-outterWinLft_; lastBeatPoint_+outterWinRgt_]) for maximum flux
00309       if(max < in(t))
00310       {
00311         max = in(t);
00312         max_i = t;
00313       }
00314     }
00315 
00316     //error = difference between predicted beat time point and the point, in the eval window, with maximum flux value
00317     error_ = max_i - lastBeatPoint_;
00318 
00319     //cout << identity_ << " -> MAX -> pred: " << in(lastBeatPoint_) << " ; act: " << max << endl;
00320 
00321     //Classification of last beat based on above evaluation:
00322 
00323     //cout << identity_ << " -> Score: " << score_ << endl;
00324     if(strcmp(scoreFunc_.c_str(), "squareCorr") == 0)
00325       score_ = calcDScoreCorrSquare(in);
00326 
00327     else if(strcmp(scoreFunc_.c_str(), "correlation") == 0)
00328       score_ = calcDScoreCorr(in, max_i);
00329 
00330     //mrs_real phase;
00331     //1st Condition: is beat  inside innerWindow?
00332     if(max_i >= lastBeatPoint_ - innerWin_ && max_i <= lastBeatPoint_)
00333     {
00334       if(strcmp(scoreFunc_.c_str(), "regular") == 0)
00335       {
00336         fraction_ = (mrs_real) abs(error_) / outterWinRgt_;
00337         //score_ = (1 - fraction_);
00338         //score_ = (1 - fraction_) * max;
00339         score_ = (1 - fraction_) * max * periodFraction_;
00340 
00341         //multiplied by sqrt(period) for disinflating the faster agents (with smaller periods) [!]
00342         //score_*= sqrt((mrs_real)period_);
00343       }
00344 
00345       MRSDIAG("BeatAgent::myProcess() - Beat Inside innerWindow!");
00346       //cout << identity_ << "(" << timeElapsed_ <<") -> Beat (" << curBeat_ << ") Inside innerWindow! with error: ";
00347 
00348       //Evaluation info:
00349       //phase = actualBeatPoint = max_i
00350       fillOutput(out, EVAL, period_, curBeat_, INNER, error_, score_);
00351     }
00352     else if(max_i > lastBeatPoint_ && max_i <= lastBeatPoint_ + innerWin_)
00353     {
00354       if(strcmp(scoreFunc_.c_str(), "regular") == 0)
00355       {
00356         fraction_ = (mrs_real) abs(error_) / outterWinRgt_;
00357         //score_ = (1 - fraction_) * max;
00358         //score_ = (1 - fraction_);
00359         score_ = (1 - fraction_) * max * periodFraction_;
00360 
00361         //multiplied by sqrt(period) for disinflating the faster agents (with smaller periods) [!]
00362         //score_*= sqrt((mrs_real)period_);
00363       }
00364 
00365       MRSDIAG("BeatAgent::myProcess() - Beat Inside innerWindow!");
00366       //cout << identity_ << "(" << timeElapsed_ <<") -> Beat (" << curBeat_ << ") Inside innerWindow! with error: ";
00367 
00368       //Evaluation info:
00369       //phase = actualBeatPoint = max_i
00370       fillOutput(out, EVAL, period_, curBeat_, INNER, error_, score_);
00371     }
00372 
00373     //2nd Condition: is beat insdie outterWindow but outside innerWindow?
00374     else
00375     {
00376       if((max_i >= lastBeatPoint_ - outterWinLft_) && (max_i < lastBeatPoint_ - innerWin_))
00377       {
00378         if(strcmp(scoreFunc_.c_str(), "regular") == 0)
00379         {
00380           fraction_ = (mrs_real) abs(error_) / outterWinRgt_;
00381           //score_ = -1 * fraction_ * max;
00382           //score_ = (1 - fraction_) * max;
00383           score_ = -1 * fraction_ * max * periodFraction_;
00384 
00385           //multiplied by sqrt(period) for disinflating the faster agents (with smaller periods) [!]
00386           //score_*= sqrt((mrs_real)period_);
00387         }
00388       }
00389       if((max_i > lastBeatPoint_ + innerWin_) && (max_i <= lastBeatPoint_ + outterWinRgt_))
00390       {
00391         if(strcmp(scoreFunc_.c_str(), "regular") == 0)
00392         {
00393           fraction_ = (mrs_real) abs(error_) / outterWinRgt_;
00394           //score_ = -1 * fraction_ * max;
00395           //score_ = (1 - fraction_) * max;
00396           score_ = -1 * fraction_ * max * periodFraction_;
00397 
00398           //multiplied by sqrt(period) for disinflating the faster agents (with smaller periods) [!]
00399           //score_*= sqrt((mrs_real)period_);
00400         }
00401       }
00402 
00403       //Evaluation info:
00404       //phase = actualBeatPoint = max_i
00405       //cout << identity_ << "(" << timeElapsed_ <<") -> Beat (" << curBeat_ << ") Outside innerWindow! with error: ";
00406       MRSDIAG("BeatAgent::myProcess() - Beat Inside OutterWindow but outside innerWindow!");
00407 
00408       fillOutput(out, EVAL, period_, curBeat_, OUTTER, error_, score_);
00409     }
00410 
00411     //cout << "t:" << timeElapsed_ << "-" << identity_ << "(error:" << error_ << "): curBeat-" << curBeat_ << "; act-"
00412     //  << curBeat_+error_ << " -> flux: " << in(lastBeatPoint_) << "-" << lastBeatPoint_ << "("
00413     //  << max << "-" << max_i << ") dS: " << score_ << " NextBeat(ifNotChanged): " << curBeat_+period_ << endl;
00414 
00415     /*
00416       for(mrs_natural i = 0; i < beatCount_; i++)
00417       {
00418       cout << identity_ << " -> "History Phase(" << i << "): " << history_(i, 0) << endl;
00419       cout << identity_ << " ->  "History Value(" << i << "): " << history_(i, 1) << endl;
00420       }
00421     */
00422 
00423     //Updates previous Beat
00424     prevBeat_ = curBeat_;
00425 
00426     //Disbales new/updated flag:
00427     agentControl_(myIndex_, 0) = 0.0;
00428     updControl(ctrl_agentControl_, agentControl_);
00429   }
00430 
00431   //MATLAB_EVAL("plot(FluxTrack/max(FluxTrack))");
00432   //MATLAB_EVAL("hold on;");
00433   /*MATLAB_PUT(out, "BeatAgent");
00434     MATLAB_PUT(lastBeatPoint_ + 2, "t");
00435     MATLAB_PUT(lastBeatPoint_ - outterWinLft_ + 2, "t1");
00436     MATLAB_PUT(lastBeatPoint_ + outterWinRgt_ + 2, "t2");
00437     MATLAB_EVAL("BeatAgentTS = [BeatAgentTS, BeatAgent];");
00438     MATLAB_EVAL("stem(t1,1, 'g');");
00439     MATLAB_EVAL("stem(t,1, 'r');");
00440     MATLAB_EVAL("stem(t2,1, 'g');");
00441     MATLAB_EVAL("hold off;");
00442   */
00443 }