Marsyas  0.6.0-alpha
/usr/src/RPM/BUILD/marsyas-0.6.0/src/marsyas/Transcriber.cpp
Go to the documentation of this file.
00001 #include "Transcriber.h"
00002 #include <iostream>
00003 
00004 namespace Marsyas
00005 {
00006 
00007 //static const mrs_natural MIN_NOTE_FRAMES = 9; // note length
00008 #define MIN_NOTE_FRAMES 9
00009 // sixteenth notes at 120 BPM would be 10.75 frames apart
00010 // if each frame is 512 samples, or 11.6 ms.
00011 
00012 
00013 
00014 Transcriber::Transcriber()
00015 {
00016 }
00017 
00018 Transcriber::~Transcriber()
00019 {
00020 }
00021 
00022 // general functions
00023 mrs_real
00024 Transcriber::findMedianWithoutZeros(const mrs_natural start,
00025                                     const mrs_natural length,
00026                                     const realvec& array)
00027 {
00028   if ( length<=0 )
00029     return 0;
00030   realvec noZeros;
00031   noZeros.create(length);
00032   mrs_natural j=0;
00033   // don't include 0s
00034   for (mrs_natural i=0; i<length; ++i)
00035   {
00036     if ( array(start+i) > 0 )
00037     {
00038       noZeros(j)=array(start+i);
00039       j++;
00040     }
00041   }
00042   noZeros.stretch(j-1);
00043   if (j-1 <= 0)
00044     return 0;
00045   return noZeros.median();
00046 }
00047 
00048 realvec
00049 Transcriber::findPeaks(const realvec& list, const mrs_real cutoff)
00050 {
00051   realvec peaks(1);
00052   mrs_natural valIndex = 0;
00053 
00054   mrs_real localMax;
00055   mrs_natural minSpace = MIN_NOTE_FRAMES;
00056   mrs_natural prevValIndex = 0;
00057   mrs_real prevValValue = 1.0;
00058   for (mrs_natural i=minSpace; i<list.getSize()-minSpace; ++i)
00059   {
00060     if ( (list(i) > list(i-1)) &&
00061          (list(i) > list(i+1)) &&
00062          (list(i) > cutoff) )
00063     {
00064       localMax = list(i);
00065       if ((mrs_natural)i < prevValIndex+minSpace)
00066       {
00067         if (localMax > prevValValue)
00068         {
00069           // replace previous valley with this one
00070           peaks(valIndex-1) = i;
00071           prevValIndex = i;
00072           prevValValue = localMax;
00073         }
00074       }
00075       else
00076       {
00077         // new valley found
00078         peaks.stretchWrite(valIndex, i);
00079         valIndex++;
00080         prevValIndex = i;
00081         prevValValue = localMax;
00082       }
00083     }
00084   }
00085   peaks.stretch(valIndex);
00086   return peaks;
00087 }
00088 
00089 realvec
00090 Transcriber::findValleys(const realvec& list)
00091 {
00092   realvec valleys(1);
00093   mrs_natural valIndex = 0;
00094 
00095   mrs_real localMin;
00096   mrs_natural minSpace = MIN_NOTE_FRAMES;
00097   mrs_natural prevValIndex = 0;
00098   mrs_real prevValValue = 1.0;
00099   for (mrs_natural i=minSpace; i<list.getSize()-minSpace; ++i)
00100   {
00101     if ( (list(i) < list(i-1)) &&
00102          (list(i) < list(i+1)))
00103     {
00104       localMin = list(i);
00105       if ((mrs_natural)i < prevValIndex+minSpace)
00106       {
00107         if (localMin < prevValValue)
00108         {
00109           // replace previous valley with this one
00110           valleys(valIndex-1) = i;
00111           prevValIndex = i;
00112           prevValValue = localMin;
00113         }
00114       }
00115       else
00116       {
00117         // new valley found
00118         valleys.stretchWrite(valIndex, i);
00119         valIndex++;
00120         prevValIndex = i;
00121         prevValValue = localMin;
00122       }
00123     }
00124   }
00125   valleys.stretch(valIndex);
00126   return valleys;
00127 }
00128 
00129 mrs_real
00130 Transcriber::findNextPeakValue(const realvec& list, const mrs_natural
00131                                start)
00132 {
00133   mrs_natural i = start;
00134   mrs_bool isPeak = false;
00135   mrs_real minValue = 0.1;
00136   do
00137   {
00138     ++i;
00139     if (i == list.getSize())
00140       return 0.0;
00141     if ( (list(i) > list(i-1)) &&
00142          (list(i) > list(i+1)) &&
00143          ( list(i) > minValue) )
00144     {
00145       isPeak = true;
00146     }
00147   }
00148   while ( isPeak == false );
00149   return list(i);
00150 }
00151 
00152 
00153 
00154 // pitch stuff
00155 
00156 void
00157 Transcriber::pitchSegment(const realvec& pitchList, realvec&
00158                           boundaries, const mrs_natural width)
00159 {
00160   if (boundaries.getSize() == 0)
00161   {
00162     boundaries.create(2);
00163     boundaries(0) = 0.0;
00164     boundaries(1) = pitchList.getSize();
00165   }
00166   realvec region, *newBoundaries, regionBounds;
00167   mrs_natural start, length;
00168   newBoundaries = new realvec;
00169   for (mrs_natural i=0; i<boundaries.getSize()-1; ++i)
00170   {
00171     start = (mrs_natural) boundaries(i);
00172     length = (mrs_natural) (boundaries(i+1) - boundaries(i));
00173     region = pitchList.getSubVector(start, length);
00174     regionBounds = findPitchBoundaries(region, width);
00175     regionBounds += start;
00176     newBoundaries->appendRealvec(regionBounds);
00177   }
00178   boundaries.appendRealvec(*newBoundaries);
00179   boundaries.sort();
00180 }
00181 
00182 realvec
00183 Transcriber::findPitchBoundaries(const realvec& pitchList, const
00184                                  mrs_natural width)
00185 {
00186   //mrs_natural minSpace = MIN_NOTE_FRAMES;
00187   mrs_natural minSpace = width;
00188   mrs_real noteBoundary = 0.5;
00189 
00190   realvec boundaries(1);
00191   mrs_natural onsetIndex=0;
00192 
00193   mrs_real median;
00194   mrs_real prevNote=0.0;
00195   mrs_natural prevSamp=0;
00196   for (mrs_natural i=minSpace; i<pitchList.getSize()-minSpace; ++i)
00197   {
00198     median = findMedianWithoutZeros(i-minSpace, 2*minSpace, pitchList);
00199     if ( fabs(median-prevNote) > noteBoundary )
00200     {
00201       if ((mrs_natural)i > prevSamp+minSpace)
00202       {
00203         prevNote = median;
00204         prevSamp = i;
00205         boundaries.stretchWrite( onsetIndex, i);
00206         onsetIndex++;
00207       }
00208       else
00209       {
00210         prevNote = median;
00211       }
00212     }
00213   }
00214   boundaries.stretch(onsetIndex);
00215   return boundaries;
00216 }
00217 
00218 
00219 
00220 // amplitude stuff
00221 void
00222 Transcriber::ampSegment(const realvec& ampList, realvec&
00223                         boundaries, const mrs_real cutoff)
00224 {
00225   if (boundaries.getSize() == 0)
00226   {
00227     boundaries.create(2);
00228     boundaries(0) = 0.0;
00229     boundaries(1) = ampList.getSize()-1;
00230   }
00231   realvec region, *newBoundaries, regionBounds;
00232   mrs_natural start, length;
00233   newBoundaries = new realvec;
00234   for (mrs_natural i=0; i<boundaries.getSize()-1; ++i)
00235   {
00236     start = (mrs_natural) boundaries(i);
00237     length = (mrs_natural) (boundaries(i+1) - boundaries(i));
00238     region = ampList.getSubVector(start, length);
00239     //regionBounds = findValleys(region);
00240     regionBounds = findPeaks(region, cutoff);
00241 //      filterAmpBoundaries(region, regionBounds);
00242     regionBounds += start;
00243     newBoundaries->appendRealvec(regionBounds);
00244   }
00245   boundaries.appendRealvec(*newBoundaries);
00246   boundaries.sort();
00247 }
00248 
00249 void
00250 Transcriber::filterAmpBoundaries(realvec& regionAmps, realvec &regionBounds)
00251 {
00252   if (regionBounds.getSize() < 2)
00253     return;
00254 
00255   // create empty list of regionBounds to keep
00256   mrs_natural numSamples = regionBounds.getSize();
00257   realvec newBounds(numSamples);
00258   mrs_natural newIndex=0;
00259 
00260   mrs_real regionMinVal = 0.1;
00261   /*
00262     // ignore quiet parts
00263     if ( regionAmps.mean() < regionMinVal )
00264     {
00265         newBounds.stretch(0);
00266         regionBounds = newBounds;
00267         return;
00268     }
00269   */
00270 
00271   // normalize amps in pitch region
00272   regionAmps /= regionAmps.maxval();
00273 
00274   mrs_natural start, length;
00275   mrs_real valleyMinVal = 0.20;
00276   mrs_real valley;
00277   realvec region;
00278   for (mrs_natural i=0; i<regionBounds.getSize(); ++i)
00279   {
00280     start = (mrs_natural) regionBounds(i);
00281     if (i < regionBounds.getSize()-1 )
00282       length = (mrs_natural) (regionBounds(i+1) - regionBounds(i));
00283     else
00284       length = regionAmps.getSize() - i;
00285     region = regionAmps.getSubVector(start, length);
00286 
00287     valley = regionAmps(start);
00288     if ( (valley < valleyMinVal) &&
00289 //      if ( (valley < peakRatio*findNextPeakValue(&region, 0)) &&
00290          (region.mean() > regionMinVal) )
00291     {
00292 //          cout<<"at frame "<<start<<" keep valley: "<<valley<<endl;
00293       newBounds(newIndex) = start;
00294       newIndex++;
00295     }
00296     else
00297     {
00298 //          cout<<"Removed "<<start<<endl;
00299 //          cout<<valley<<"\t"<<region.mean()<<endl;
00300 //          cout<<region;
00301     }
00302   }
00303   newBounds.stretch(newIndex);
00304 //  cout<<"in: "<<regionBounds.getSize();
00305 //  cout<<"\t out: "<<newBounds.getSize()<<endl;
00306   regionBounds = newBounds;
00307 }
00308 
00309 
00310 
00311 // note stuff
00312 void
00313 Transcriber::getRelativeDurations(const realvec& boundaries, realvec
00314                                   &durations)
00315 {
00316   mrs_natural numNotes = boundaries.getSize()-1;
00317   durations.create(numNotes);
00318 
00319   mrs_natural i;
00320   mrs_natural min = 99999; // infinity
00321   // calculate durations in samples
00322   // and find smallest
00323   for (i=0; i<numNotes; ++i)
00324   {
00325     durations(i) = boundaries(i+1) - boundaries(i);
00326 //      cout<<"duration: "<<(*durations)(i)<<endl;
00327     // we don't care about silent durations
00328     if (durations(i) < min)
00329       min = (mrs_natural) durations(i);
00330   }
00331 //  cout<<"min: "<<min<<endl;
00332   // find relative durations
00333   // yes, we want to truncate the division.
00334   for (i=0; i<numNotes; ++i)
00335   {
00336     durations(i) = (mrs_natural) ( durations(i) / (min) );
00337   }
00338 }
00339 
00340 void
00341 Transcriber::discardBeginEndSilences(const realvec& pitchList, const realvec&
00342                                      ampList, realvec& boundaries)
00343 {
00344   // could be useful for an improved function.
00345   (void) ampList;
00346 
00347   mrs_real notePitch;
00348   mrs_natural i,j;
00349   mrs_natural start,length;
00350   // Remove beginning silences.
00351   i=0;
00352   start = (mrs_natural) boundaries(i);
00353   length = (mrs_natural) ( boundaries(i+1)-boundaries(i) );
00354   notePitch = findMedianWithoutZeros(start, length, pitchList);
00355   while ( (notePitch == 0) && (i < boundaries.getSize()-1) )
00356   {
00357     for (j=i; j<boundaries.getSize()-1; j++)
00358       boundaries(j) = boundaries(j+1);
00359     boundaries.stretch(j);
00360     ++i;
00361     start = (mrs_natural) boundaries(i);
00362     length = (mrs_natural) ( boundaries(i+1)-boundaries(i) );
00363     notePitch = findMedianWithoutZeros(start, length, pitchList);
00364   }
00365   // Remove ending silences.
00366   i=boundaries.getSize()-2;
00367   start = (mrs_natural) boundaries(i);
00368   length = (mrs_natural) ( boundaries(i+1)-boundaries(i) );
00369   notePitch = findMedianWithoutZeros(start, length, pitchList);
00370   while ( (notePitch == 0) && (i < boundaries.getSize()-1) )
00371   {
00372     boundaries.stretch(i+1);
00373     i--;
00374     start = (mrs_natural) boundaries(i);
00375     length = (mrs_natural) ( boundaries(i+1)-boundaries(i) );
00376     notePitch = findMedianWithoutZeros(start, length, pitchList);
00377   }
00378 }
00379 
00380 void
00381 Transcriber::discardEndingTotalSilenceAmpsOnly(realvec& ampList)
00382 {
00383   mrs_natural i = ampList.getSize()-1;
00384   // Remove ending silences.
00385   while ( (i>0) && (ampList(i) == 0.0))
00386   {
00387     i--;
00388   }
00389   ampList.stretch(i);
00390 }
00391 
00392 void
00393 Transcriber::discardBeginEndSilencesAmpsOnly(const realvec& ampList,
00394     realvec& boundaries)
00395 {
00396   mrs_real sampleAmp;
00397   mrs_natural i,j;
00398   i=0;
00399   // Remove beginning silences.
00400   mrs_natural start = (mrs_natural) boundaries(i);
00401   //mrs_natural length = (mrs_natural) ( boundaries(i+1)-boundaries(i) );
00402   sampleAmp = ampList(start);
00403   while ( (sampleAmp < 0.1) && (i < boundaries.getSize()-1) )
00404   {
00405     for (j=i; j<boundaries.getSize()-1; j++)
00406       boundaries(j) = boundaries(j+1);
00407     boundaries.stretch(j);
00408     ++i;
00409     start = (mrs_natural) boundaries(i);
00410     //length = (mrs_natural) ( boundaries(i+1)-boundaries(i) );
00411     sampleAmp = ampList(start);
00412   }
00413   // Remove ending silences.
00414   i=boundaries.getSize()-2;
00415   start = (mrs_natural) boundaries(i);
00416   //length = (mrs_natural) ( boundaries(i+1)-boundaries(i) );
00417   sampleAmp = ampList(start);
00418   while ( (sampleAmp < 0.1) && (i < boundaries.getSize()-1) )
00419   {
00420     boundaries.stretch(i+1);
00421     i--;
00422     start = (mrs_natural) boundaries(i);
00423     //length = (mrs_natural) ( boundaries(i+1)-boundaries(i) );
00424     sampleAmp = ampList(start);
00425   }
00426 }
00427 
00428 
00429 // this will not include relative durations
00430 realvec
00431 Transcriber::getNotes(const realvec& pitchList, const realvec& ampList,
00432                       const realvec& boundaries)
00433 {
00434   (void) ampList;
00435   mrs_natural numNotes = boundaries.getSize()-1;
00436   realvec notes(numNotes, 2);
00437 
00438   mrs_natural start, length;
00439   mrs_real notePitch;
00440 
00441   mrs_natural boundIndex = 0;
00442   notePitch = findMedianWithoutZeros(0, (mrs_natural) boundaries(1),
00443                                      pitchList);
00444   if (notePitch == 0)
00445     boundIndex++;
00446   mrs_natural firstFrame = (mrs_natural) boundaries(boundIndex);
00447   for (mrs_natural i=0; i<numNotes; ++i)
00448   {
00449     notes(i,1) = boundaries(boundIndex) - firstFrame;
00450 
00451     // get approximate pitch
00452     start = (mrs_natural) boundaries(boundIndex);
00453     length = (mrs_natural) (boundaries(boundIndex+1) -
00454                             boundaries(boundIndex));
00455     notePitch = findMedianWithoutZeros(start, length, pitchList);
00456 
00457     notes(i,0) = notePitch;
00458     boundIndex++;
00459   }
00460   notes.stretch(boundIndex-2,2);
00461   return notes;
00462 }
00463 
00464 } //namespace
00465