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