Marsyas  0.6.0-alpha
/usr/src/RPM/BUILD/marsyas-0.6.0/src/marsyas/marsystems/Spectrum2ACMChroma.cpp
Go to the documentation of this file.
00001 #include "Spectrum2ACMChroma.h"
00002 #include <marsyas/system/MarSystemManager.h>
00003 #include "Series.h"
00004 
00005 #include "MedianFilter.h"
00006 #include "PeakInObservation.h"
00007 #include "Negative.h"
00008 #include "Signum.h"
00009 #include "F0Analysis.h"
00010 #include "Pitch2Chroma.h"
00011 
00012 using namespace std;
00013 using namespace Marsyas;
00014 
00015 // Initialize 'mrs_real' constants outside class definition
00016 // const mrs_real Spectrum2ACMChroma::RaiseFactor_ = 2.;
00017 // const mrs_real Spectrum2ACMChroma::Hysteresis_ = sqrt(2.);
00018 // const mrs_real Spectrum2ACMChroma::Tolerance_ = 0.03;
00019 // const mrs_real Spectrum2ACMChroma::Attenuation_ = 0.75;
00020 // const mrs_real Spectrum2ACMChroma::PowerOfAmplitude_ = 0.5;
00021 // const mrs_real Spectrum2ACMChroma::Diapason_ = 440.;
00022 
00023 Spectrum2ACMChroma::Spectrum2ACMChroma(mrs_string inName)
00024   :MarSystem("Spectrum2ACMChroma",inName)
00025 {
00026   addControls();
00027 
00028   MarSystem* theNewSystem;
00029   MarSystemManager theManager;
00030 
00031   // Add new MarSystems to manager
00032   MarSystem* theDummy;
00033   theDummy = new MedianFilter("Anything");
00034   theManager.registerPrototype("MedianFilter",theDummy);
00035   theDummy = new PeakInObservation("Anything");
00036   theManager.registerPrototype("PeakInObservation",theDummy);
00037   theDummy = new Negative("Anything");
00038   theManager.registerPrototype("Negative",theDummy);
00039   theDummy = new Signum("Anything");
00040   theManager.registerPrototype("Signum",theDummy);
00041   theDummy = new F0Analysis("Anything");
00042   theManager.registerPrototype("F0Analysis",theDummy);
00043   theDummy = new Pitch2Chroma("Anything");
00044   theManager.registerPrototype("Pitch2Chroma",theDummy);
00045 
00046   // ------------------------ DEFINE CHROMA EXTRACTOR ------------------
00047   Spectrum2ACMChromaNet_ = theManager.create("Series","SER1");
00048 
00049   // --- 8.1 Compute peaks minus background in spectrum
00050   MarSystem* theFan2 = theManager.create("FanOutIn","FAN2");
00051 
00052   // ------ 8.1.1 Compute background spectrum
00053   MarSystem* theSeries3 = theManager.create("Series","SER3");
00054 
00055   // --------- 8.1.1a Median filter
00056   theNewSystem = theManager.create("MedianFilter","MedianFilter");
00057   theSeries3->addMarSystem(theNewSystem);
00058 
00059   // --------- 8.1.1b Amplify background spectrum
00060   theNewSystem = theManager.create("Gain","Gain");
00061   theSeries3->addMarSystem(theNewSystem);
00062 
00063   // --------- 8.1.1c Reverse sign of background spectrum (for subtraction)
00064   theNewSystem = theManager.create("Negative","Negative");
00065   theSeries3->addMarSystem(theNewSystem);
00066 
00067   // ------ 8.1.2 Compute peaks in spectrum
00068   theNewSystem = theManager.create("PeakInObservation","FindPeaks");
00069 
00070   // --- Determine the positions of the salient peaks
00071   //     Therefore, subtract peaks and background via sum in fanOutIn
00072   //     (Default combinator: +)
00073   theFan2->addMarSystem(theNewSystem);
00074   theFan2->addMarSystem(theSeries3);
00075 
00076   // --- 8.2 Derive booleans from salient peaks
00077   MarSystem* theSeries2 = theManager.create("Series","SER2");
00078   theSeries2->addMarSystem(theFan2);
00079 
00080   theNewSystem = theManager.create("Signum","Sign");
00081   theSeries2->addMarSystem(theNewSystem);
00082 
00083   // --- 8.3 Compute salient peaks
00084   //         Therefore, multiply booleans with spectrum via multiply in fanOutIn
00085   MarSystem* theFan1 = theManager.create("FanOutIn","FAN1");
00086   theFan1->addMarSystem(theSeries2);
00087 
00088   theNewSystem = theManager.create("Gain","Gain");  // No function
00089   theFan1->addMarSystem(theNewSystem);
00090 
00091   Spectrum2ACMChromaNet_->addMarSystem(theFan1);
00092 
00093   // 9. Perform pitch analysis
00094   theNewSystem = theManager.create("F0Analysis","F0Analysis");
00095   Spectrum2ACMChromaNet_->addMarSystem(theNewSystem);
00096 
00097   // 10. Map pitches to chroma
00098   theNewSystem = theManager.create("Pitch2Chroma","Pitch2Chroma");
00099   Spectrum2ACMChromaNet_->addMarSystem(theNewSystem);
00100 
00101   // ------------------------ DEFINE FIXED PARAMETERS ------------------
00102   mrs_string theControlString = "FanOutIn/FAN1/Series/SER2/FanOutIn/FAN2/"
00103                                 "Series/SER3/Gain/Gain/mrs_real/gain";
00104   Spectrum2ACMChromaNet_->updControl(theControlString,2.);
00105 
00106   theControlString = "FanOutIn/FAN1/Series/SER2/FanOutIn/FAN2/"
00107                      "PeakInObservation/FindPeaks/mrs_real/HystFactor";
00108   Spectrum2ACMChromaNet_->updControl(theControlString,sqrt(2.));
00109 
00110   theControlString = "FanOutIn/FAN1/Gain/Gain/mrs_real/gain";
00111   Spectrum2ACMChromaNet_->updControl(theControlString,1.);
00112 
00113   theControlString = "FanOutIn/FAN1/mrs_string/combinator";
00114   Spectrum2ACMChromaNet_->updControl(theControlString,"*");
00115 }
00116 
00117 Spectrum2ACMChroma::Spectrum2ACMChroma(const Spectrum2ACMChroma& inToCopy)
00118   :MarSystem(inToCopy)
00119 {
00120   Spectrum2ACMChromaNet_ = inToCopy.Spectrum2ACMChromaNet_->clone();
00121 
00122   // Copy MarControllers
00123   ctrl_NrOfHarmonics_ = getControl("mrs_natural/NrOfHarmonics");
00124   ctrl_F0Weight_ = getControl("mrs_real/F0Weight");
00125   ctrl_LowestF0_ = getControl("mrs_real/LowestF0");
00126 
00127   // Copy member variables
00128   NrOfHarmonics_ = inToCopy.NrOfHarmonics_;
00129   F0Weight_ = inToCopy.F0Weight_;
00130   LowestF0_ = inToCopy.LowestF0_;
00131 }
00132 
00133 Spectrum2ACMChroma::~Spectrum2ACMChroma()
00134 {
00135   delete Spectrum2ACMChromaNet_;
00136 }
00137 
00138 MarSystem* Spectrum2ACMChroma::clone() const
00139 {
00140   // Difference between COPY and CLONE?
00141   return new Spectrum2ACMChroma(*this);
00142 }
00143 
00144 void Spectrum2ACMChroma::addControls()
00145 {
00146   // Why in special addControls() function?
00147 
00148   // Add MarControllers with default values
00149   addctrl("mrs_natural/NrOfHarmonics",5,ctrl_NrOfHarmonics_);
00150   addctrl("mrs_real/F0Weight",0.5,ctrl_F0Weight_);
00151   addctrl("mrs_real/LowestF0",100.,ctrl_LowestF0_);
00152   addctrl("mrs_real/ChordEvidence",0.);
00153 
00154   // Call myUpdate when controller changes
00155   ctrl_NrOfHarmonics_->setState(true);
00156   ctrl_F0Weight_->setState(true);
00157   ctrl_LowestF0_->setState(true);
00158 
00159   // Set member variables to default values
00160   NrOfHarmonics_ = 5;
00161   F0Weight_ = 0.5;
00162   LowestF0_ = 100.;
00163 }
00164 
00165 void Spectrum2ACMChroma::myUpdate(MarControlPtr sender)
00166 {
00167   (void) sender;  //suppress warning of unused parameter(s)
00168   // output variables may change during course of program
00169   // therefore, declare them in myUpdate (not in constructor)
00170   // UPDATE is necessary! (why?)
00171 
00172   // Copy THIS input specs to input specs of Spectrum2ACMChromaNet
00173   updControl(Spectrum2ACMChromaNet_->ctrl_inSamples_, ctrl_inSamples_);
00174   updControl(Spectrum2ACMChromaNet_->ctrl_inObservations_, ctrl_inObservations_);
00175   updControl(Spectrum2ACMChromaNet_->ctrl_israte_, ctrl_israte_);
00176 
00177   // Copy output specs of Spectrum2ACMChromaNet to THIS output specs
00178   updControl(ctrl_onSamples_, Spectrum2ACMChromaNet_->ctrl_onSamples_);
00179   updControl(ctrl_onObservations_, Spectrum2ACMChromaNet_->ctrl_onObservations_);
00180   updControl(ctrl_osrate_, Spectrum2ACMChromaNet_->ctrl_osrate_);
00181 
00182   // Update member variables
00183   NrOfHarmonics_ = ctrl_NrOfHarmonics_->to<mrs_natural>();
00184   F0Weight_ = ctrl_F0Weight_->to<mrs_real>();
00185   LowestF0_ = ctrl_LowestF0_->to<mrs_real>();
00186 
00187   // Define MarSystem parameters that depend on bin width
00188   mrs_real theFrameSize = 0.08; 
00189 
00190   // israte_ = FFTSize/Fs = width of bins in input spectrum
00191   mrs_natural theWindowSize =
00192     (mrs_natural)floor((mrs_real)80/israte_+0.5);
00193   mrs_string theControlString = "FanOutIn/FAN1/Series/SER2/FanOutIn/FAN2/"
00194                                 "Series/SER3/MedianFilter/MedianFilter/mrs_natural/WindowSize";
00195   Spectrum2ACMChromaNet_->updControl(theControlString,theWindowSize);
00196 
00197   mrs_natural theHystLength =
00198     (mrs_natural)floor(2./(theFrameSize*israte_)+0.5);
00199   theControlString = "FanOutIn/FAN1/Series/SER2/FanOutIn/FAN2/"
00200                      "PeakInObservation/FindPeaks/mrs_natural/HystLength";
00201   Spectrum2ACMChromaNet_->updControl(theControlString,theHystLength);
00202 
00203   // All notes are referred to the diapason:
00204   // 1. search for the pitch of the lowest note >= the lowest pitch
00205   //    The lowest pitch is defined by the user
00206   // 2. search for the pitch of the highest note <= the highest pitch
00207   //    The highest pitch is defined by the algorithm and equals Fs/4
00208 
00209   // 1.1 compute the note index of the diapason referred to the min. pitch
00210   mrs_natural theDiapInd = (mrs_natural)(1 + floor(12. * log(440./100.)/log(2.)));
00211   // 1.2 compute the pitch of the lowest note given the note index of the diapason
00212   mrs_real theLowestNote = 440. * pow(2., (mrs_real)(1-theDiapInd)/12.);
00213   // 2 compute the note index of the pitch of the highest note = the number of notes
00214   // !! Use Nyquist frequency of input signal instead of 1000Hz!!
00215   mrs_natural theMaxInd = (mrs_natural)(theDiapInd + floor(12.*log(1000./(440.))/log(2.)));
00216 
00217   theControlString = "Pitch2Chroma/Pitch2Chroma/mrs_real/LowestPitch";
00218   Spectrum2ACMChromaNet_->updControl(theControlString,theLowestNote);
00219 
00220   theControlString = "Pitch2Chroma/Pitch2Chroma/mrs_natural/NrOfNotes";
00221   Spectrum2ACMChromaNet_->updControl(theControlString,theMaxInd);
00222 }
00223 
00224 void Spectrum2ACMChroma::myProcess(realvec& inSpectrum, realvec& outChroma)
00225 {
00226   Spectrum2ACMChromaNet_->process(inSpectrum,outChroma);
00227 
00228   // Update output variable
00229   mrs_string theControlString = "F0Analysis/F0Analysis/mrs_real/ChordEvidence";
00230   MarControlPtr theControlPtr = Spectrum2ACMChromaNet_->getctrl(theControlString);
00231   updControl("mrs_real/ChordEvidence",theControlPtr->to<mrs_real>());
00232 }