Marsyas  0.6.0-alpha
/usr/src/RPM/BUILD/marsyas-0.6.0/src/marsyas/TranscriberExtract.cpp
Go to the documentation of this file.
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