Marsyas
0.6.0-alpha
|
00001 #include "TranscriberExtract.h" 00002 namespace Marsyas 00003 { 00004 00005 //static MarSystemManager mng; // moved to private member 00006 00007 TranscriberExtract::TranscriberExtract() 00008 { 00009 } 00010 00011 TranscriberExtract::~TranscriberExtract() 00012 { 00013 } 00014 00015 mrs_real TranscriberExtract::addFileSource(MarSystem* net, const std::string infile) 00016 { 00017 if (infile == EMPTYSTRING) 00018 { 00019 MRSERR("Please specify a sound file."); 00020 return 0; 00021 } 00022 net->addMarSystem(mng.create("SoundFileSource", "src")); 00023 net->updControl("SoundFileSource/src/mrs_string/filename", infile); 00024 net->linkControl("mrs_bool/hasData", 00025 "SoundFileSource/src/mrs_bool/hasData"); 00026 return net->getctrl("SoundFileSource/src/mrs_real/osrate")->to<mrs_real>(); 00027 } 00028 00029 00030 MarSystem* 00031 TranscriberExtract::makePitchNet(const mrs_real srate, const mrs_real lowFreq, MarSystem* rvSink) 00032 { 00033 mrs_real highFreq = 5000.0; 00034 00035 MarSystem *net = mng.create("Series", "pitchNet"); 00036 net->addMarSystem(mng.create("ShiftInput", "sfi")); 00037 net->addMarSystem(mng.create("PitchPraat", "pitch")); 00038 if (rvSink != NULL) 00039 net->addMarSystem(rvSink); 00040 00041 // yes, this is the right way around (lowSamples<-highFreq) 00042 net->updControl("PitchPraat/pitch/mrs_natural/lowSamples", 00043 hertz2samples(highFreq, srate) ); 00044 net->updControl("PitchPraat/pitch/mrs_natural/highSamples", 00045 hertz2samples(lowFreq, srate) ); 00046 00047 // The window should be just long enough to contain three periods 00048 // (for pitch detection) of MinimumPitch. 00049 mrs_real windowSize = 3.0/lowFreq*srate; 00050 net->updControl("mrs_natural/inSamples", 512); 00051 net->updControl("ShiftInput/sfi/mrs_natural/winSize", 00052 powerOfTwo(windowSize)); 00053 00054 return net; 00055 } 00056 00057 MarSystem* TranscriberExtract::makeAmplitudeNet(MarSystem* rvSink) 00058 { 00059 MarSystem *net = mng.create("Series", "amplitudeNet"); 00060 net->addMarSystem(mng.create("ShiftInput", "sfiAmp")); 00061 net->addMarSystem(mng.create("Rms", "rms")); 00062 if (rvSink != NULL) 00063 net->addMarSystem(rvSink); 00064 00065 net->updControl("mrs_natural/inSamples", 512); 00066 net->updControl("ShiftInput/sfiAmp/mrs_natural/winSize", 512); 00067 00068 return net; 00069 } 00070 00071 void 00072 TranscriberExtract::getAllFromAudio(const std::string audioFilename, realvec& 00073 pitchList, realvec& ampList, 00074 realvec& boundaries) 00075 { 00076 MarSystem* pitchSink = mng.create("RealvecSink", "pitchSink"); 00077 MarSystem* ampSink = mng.create("RealvecSink", "ampSink"); 00078 00079 MarSystem* pnet = mng.create("Series", "pnet"); 00080 mrs_real srate = addFileSource(pnet, audioFilename); 00081 // TODO: double the number of observations? 00082 // pnet->updControl("SoundFileSource/src/mrs_natural/inSamples",256); 00083 // pnet->addMarSystem(mng.create("ShiftInput", "shift")); 00084 // pnet->updControl("ShiftInput/shift/mrs_natural/winSize",512); 00085 00086 MarSystem* fanout = mng.create("Fanout", "fanout"); 00087 fanout->addMarSystem(makePitchNet(srate, 100.0, pitchSink)); 00088 fanout->addMarSystem(makeAmplitudeNet(ampSink)); 00089 pnet->addMarSystem(fanout); 00090 00091 while ( pnet->getctrl("mrs_bool/hasData")->to<mrs_bool>() ) 00092 pnet->tick(); 00093 00094 pitchList = getPitchesFromRealvecSink(pitchSink, srate); 00095 ampList = getAmpsFromRealvecSink(ampSink); 00096 boundaries.create(2); 00097 boundaries(0) = 0; 00098 boundaries(1) = pitchList.getSize(); 00099 delete pnet; 00100 } 00101 00102 realvec 00103 TranscriberExtract::getPitchesFromAudio(const std::string audioFilename) 00104 { 00105 mrs_real normalize = getNormalizingGain(audioFilename); 00106 00107 MarSystem* pnet = mng.create("Series", "pnet"); 00108 mrs_real srate = addFileSource(pnet, audioFilename); 00109 pnet->addMarSystem(mng.create("Gain", "normalizing")); 00110 pnet->updControl("Gain/normalizing/mrs_real/gain",normalize); 00111 MarSystem* rvSink = mng.create("RealvecSink", "rvSink"); 00112 pnet->addMarSystem(makePitchNet(srate, 100.0, rvSink)); 00113 00114 while ( pnet->getctrl("mrs_bool/hasData")->to<mrs_bool>() ) 00115 pnet->tick(); 00116 00117 realvec pitchList = getPitchesFromRealvecSink(rvSink, srate); 00118 delete pnet; 00119 return pitchList; 00120 } 00121 00122 realvec 00123 TranscriberExtract::getAmpsFromAudio(const std::string audioFilename) 00124 { 00125 mrs_real normalize = getNormalizingGain(audioFilename); 00126 00127 MarSystem* pnet = mng.create("Series", "pnet"); 00128 addFileSource(pnet, audioFilename); 00129 00130 pnet->addMarSystem(mng.create("Gain", "normalizing")); 00131 pnet->updControl("Gain/normalizing/mrs_real/gain",normalize); 00132 MarSystem* rvSink = mng.create("RealvecSink", "rvSink"); 00133 pnet->addMarSystem(makeAmplitudeNet(rvSink)); 00134 00135 while ( pnet->getctrl("mrs_bool/hasData")->to<mrs_bool>() ) 00136 pnet->tick(); 00137 00138 realvec rmsList = getAmpsFromRealvecSink(rvSink); 00139 delete pnet; 00140 00141 // normalize RMS 00142 rmsList -= rmsList.minval(); 00143 mrs_real maxRms = rmsList.maxval(); 00144 if (maxRms != 0) 00145 rmsList /= maxRms; 00146 return rmsList; 00147 } 00148 00149 realvec 00150 TranscriberExtract::getPitchesFromRealvecSink(MarSystem* rvSink, 00151 const mrs_real srate) 00152 { 00153 realvec data = rvSink->getctrl("mrs_realvec/data")->to<mrs_realvec>(); 00154 rvSink->updControl("mrs_bool/done", true); 00155 00156 realvec pitchList(data.getSize()/2); 00157 mrs_real pitchOutput; 00158 for (mrs_natural i=0; i<pitchList.getSize(); ++i) 00159 { 00160 // on linux (but not OSX), we have pitchOutput of 0.5 if the pitch 00161 // detection can't decide on a pitch. 00162 pitchOutput = data(2*i+1); 00163 if (pitchOutput > 1) 00164 pitchList(i) = samples2hertz( pitchOutput, srate); 00165 else 00166 pitchList(i) = 0; 00167 } 00168 return pitchList; 00169 } 00170 00171 realvec 00172 TranscriberExtract::getAmpsFromRealvecSink(MarSystem* rvSink) 00173 { 00174 realvec data = rvSink->getctrl("mrs_realvec/data")->to<mrs_realvec>(); 00175 rvSink->updControl("mrs_bool/done", true); 00176 realvec ampList(data.getSize()); 00177 ampList = data; 00178 return ampList; 00179 } 00180 00181 void 00182 TranscriberExtract::toMidi(realvec& pitchList) 00183 { 00184 pitchList.apply( hertz2pitch ); 00185 } 00186 00187 mrs_real 00188 TranscriberExtract::getNormalizingGain(const std::string audioFilename) 00189 { 00190 mrs_real maxVal = 0.0; 00191 00192 MarSystem* pnet = mng.create("Series", "pnet"); 00193 addFileSource(pnet, audioFilename); 00194 // forces Marsyas to write to processedData 00195 pnet->addMarSystem(mng.create("Gain", "null")); 00196 00197 while ( pnet->getctrl("mrs_bool/hasData")->to<mrs_bool>() ) 00198 { 00199 pnet->tick(); 00200 const realvec& processedData = 00201 pnet->getctrl("SoundFileSource/src/mrs_realvec/processedData")->to<mrs_realvec>(); 00202 for (mrs_natural i=0; i< processedData.getSize(); ++i) 00203 { 00204 mrs_real val = fabs(processedData(i)); 00205 if (val > maxVal) 00206 maxVal = val; 00207 } 00208 } 00209 00210 delete pnet; 00211 return 1.0/maxVal; 00212 } 00213 00214 } // namespace Marsyas