Marsyas
0.6.0-alpha
|
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 ®ionBounds) 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(®ion, 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