CLAM-Development
1.1
|
00001 /* 00002 * Copyright (c) 2004 MUSIC TECHNOLOGY GROUP (MTG) 00003 * UNIVERSITAT POMPEU FABRA 00004 * 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software 00018 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00019 * 00020 */ 00021 00022 #include "Complex.hxx" 00023 #include "FundFreqDetect.hxx" 00024 #include "Fundamental.hxx" 00025 #include "SpectralPeakArray.hxx" 00026 #include <cmath> 00027 00028 #define INFINITE_MAGNITUD 1000000 00029 00030 namespace CLAM 00031 { 00032 00033 FundFreqDetect::FundFreqDetect() 00034 : mInput( "Input", this), 00035 mOutput( "Output", this ), 00036 mFundFreqValue( "Fund Freq Value", this ) 00037 { 00038 Configure(FundFreqDetectConfig()); 00039 } 00040 00041 FundFreqDetect::FundFreqDetect(const FundFreqDetectConfig &c ) 00042 : mInput( "Input", this ), 00043 mOutput( "Output", this ), 00044 mFundFreqValue( "Fund Freq Value", this ) 00045 { 00046 Configure(c); 00047 } 00048 00049 FundFreqDetect::~FundFreqDetect() {} 00050 00051 /* Configure the Processing Object according to the Config object */ 00052 bool FundFreqDetect::ConcreteConfigure(const ProcessingConfig& c) 00053 { 00054 CopyAsConcreteConfig(mConfig, c); 00055 00056 mReferenceFundFreq = mConfig.GetReferenceFundFreq(); 00057 mLowestFundFreq = mConfig.GetLowestFundFreq(); 00058 mHighestFundFreq = mConfig.GetHighestFundFreq(); 00059 mMaxCandMagDiff = mConfig.GetMaxCandMagDiff(); 00060 mMaxFundFreqError = mConfig.GetMaxFundFreqError(); 00061 mnInt = mConfig.GetNInt(); 00062 mPMp = mConfig.GetPMp(); 00063 mPMq = mConfig.GetPMq(); 00064 mPMr = mConfig.GetPMr(); 00065 mMPp = mConfig.GetMPp(); 00066 mMPq = mConfig.GetMPq(); 00067 mMPr = mConfig.GetMPr(); 00068 mPMnPeaks = mConfig.GetPMnPeaks(); 00069 mMPnPeaks = mConfig.GetMPnPeaks(); 00070 mPMCont = mConfig.GetPMCont(); 00071 mMPCont = mConfig.GetMPCont(); 00072 mnMaxCandidates= TData(mConfig.GetNMaxCandidates()); 00073 00074 return true; 00075 } 00076 00077 const ProcessingConfig &FundFreqDetect::GetConfig() const 00078 { 00079 mConfig.SetMaxCandMagDiff(mMaxCandMagDiff); 00080 mConfig.SetMaxFundFreqError(mMaxFundFreqError); 00081 mConfig.SetNInt(mnInt); 00082 mConfig.SetPMp(mPMp); 00083 mConfig.SetPMq(mPMq); 00084 mConfig.SetPMr(mPMr); 00085 mConfig.SetMPp(mMPp); 00086 mConfig.SetMPq(mMPq); 00087 mConfig.SetMPr(mMPr); 00088 mConfig.SetPMnPeaks(mPMnPeaks); 00089 mConfig.SetMPnPeaks(mMPnPeaks); 00090 mConfig.SetPMCont(mPMCont); 00091 mConfig.SetMPCont(mMPCont); 00092 mConfig.SetNMaxCandidates(TSize(mnMaxCandidates)); 00093 return mConfig; 00094 } 00095 00096 /* The supervised Do() function */ 00097 bool FundFreqDetect::Do(void) 00098 { 00099 mOutput.GetData().SetnMaxCandidates(1); 00100 00101 bool result = Do( mInput.GetData(), mOutput.GetData() ); 00102 mInput.Consume(); 00103 mOutput.Produce(); 00104 00105 return result; 00106 } 00107 00108 /* The unsupervised Do() function */ 00109 bool FundFreqDetect::Do(SpectralPeakArray& peaks,Fundamental& outFreq) 00110 { 00111 outFreq.Init(); 00112 00113 // Check Number of Candidates required 00114 CLAM_ASSERT (outFreq.GetnMaxCandidates() > 0, 00115 "FundFreqDet::Detection: negative number of candidates wanted"); 00116 00117 // See if the number of best candidates to be calculated is less than the maximum permitted 00118 CLAM_ASSERT (outFreq.GetnMaxCandidates() <= mnMaxCandidates, 00119 "FundFreqDet::Detection:Number of candidates wanted bigger " 00120 "than the maximum configured on the algorithm"); 00121 00122 // not enough peak information available for fundamental frequency detection"); 00123 if (peaks.GetnPeaks() <= 0) 00124 { 00125 mFundFreqValue.SendControl(0.0f); 00126 return false; 00127 } 00128 00129 // Calculate Maximun Magnitude Peak 00130 TIndex nMaxMagPeak = peaks.GetMaxMagPos(); 00131 TData maxMag = peaks.GetMag(nMaxMagPeak); 00132 00133 // 1.- SELECT PEAKS 00134 // Add an index to the PeakArray 00135 if(!peaks.HasIndexArray()) 00136 { 00137 peaks.AddIndexArray(); 00138 peaks.UpdateData(); 00139 peaks.SetnMaxPeaks(peaks.GetnMaxPeaks()); 00140 } 00141 00142 // Reset indices in the peak array 00143 peaks.ResetIndices(); 00144 00145 // Delete peaks below the lowest pitch 00146 DataArray& peakMagnitudes=peaks.GetMagBuffer(); 00147 DataArray& peakFrequencies=peaks.GetFreqBuffer(); 00148 DataArray& peakBinPosBuffer=peaks.GetBinPosBuffer(); 00149 00150 const TData spectralRes = peakFrequencies[nMaxMagPeak]/peakBinPosBuffer[nMaxMagPeak]; 00151 00152 TData lowestFundFreqBinPos = mLowestFundFreq/spectralRes; 00153 TIndex z=0; 00154 while((z<peaks.GetnPeaks()) && (peakBinPosBuffer[z]<lowestFundFreqBinPos)) 00155 { 00156 peaks.DeleteIndex(z); 00157 z++; 00158 } 00159 00160 // Before the maximum magnitude peak 00161 for(int i=z; i<nMaxMagPeak; i++) 00162 { 00163 if(peakMagnitudes[i] < maxMag - 30) 00164 peaks.DeleteIndex(i); 00165 } 00166 00167 // Delete peaks above 3000 00168 TData peaklimitBinPos = 3000.0/spectralRes; 00169 for (int i=peaks.GetnPeaks()-1; i > nMaxMagPeak; i--) 00170 { 00171 if (peakBinPosBuffer[i] <= peaklimitBinPos) break; 00172 peaks.DeleteIndex(i); 00173 } 00174 00175 // After the maximum magnitude peak 00176 TData x,y,a,b; 00177 a = - 10*spectralRes/TData(1000.0); 00178 b = maxMag - 50 - a*(TData)peakBinPosBuffer[nMaxMagPeak]; 00179 for(int i=nMaxMagPeak+1; i<z; i++) 00180 { 00181 y = peakMagnitudes[i]; 00182 x = peakBinPosBuffer[i]; 00183 if(y < (a*x+b)) 00184 { 00185 peaks.DeleteIndex(i); 00186 } 00187 } 00188 00189 const IndexArray & peakIndexes = peaks.GetIndexArray(); 00190 // If there no valid peaks for calculate a fundamental frequency 00191 if (peakIndexes.Size() <= 0) 00192 { 00193 mFundFreqValue.SendControl(0.0f); 00194 return false; 00195 } 00196 00197 // Find maximun magnitude peak from the selected ones 00198 nMaxMagPeak = peaks.GetMaxMagIndex(); // only indexed peaks 00199 maxMag = peakMagnitudes[peakIndexes[nMaxMagPeak]]; 00200 00201 // 2.- FIND mnMaxCandidates CANDIDATES 00202 00203 Fundamental tmpFreq; // this will be used throughout the algorithm to allocate new candidates 00204 tmpFreq.AddCandidatesFreq(); 00205 tmpFreq.AddCandidatesErr(); 00206 tmpFreq.UpdateData(); 00207 tmpFreq.SetnCandidates(0); 00208 tmpFreq.SetnMaxCandidates(int(mnMaxCandidates)); 00209 00210 DataArray & candidatesFrequency = tmpFreq.GetCandidatesFreq(); 00211 DataArray & candidatesError = tmpFreq.GetCandidatesErr(); 00212 00213 // 2.0.- Reference Fundamental Frequency 00214 if( IsGoodCandidate(mReferenceFundFreq) ) 00215 tmpFreq.AddElem(mReferenceFundFreq); 00216 00217 // 2.1.- Three maximum magnitude peaks and its integer ratios 00218 TIndex nMaxMagPeak2 = nMaxMagPeak; 00219 TIndex nMaxMagPeak3 = nMaxMagPeak; 00220 if(peakIndexes.Size() >= 2) 00221 { 00222 // find second max magnitude peak 00223 peakMagnitudes[peakIndexes[nMaxMagPeak]]=-2000; 00224 nMaxMagPeak2 = peaks.GetMaxMagIndex(); 00225 if(peakIndexes.Size() >= 3) 00226 { 00227 TData aux; 00228 // find third max magnitude peak 00229 aux = peakMagnitudes[peakIndexes[nMaxMagPeak2]]; 00230 peakMagnitudes[peakIndexes[nMaxMagPeak2]]=-2000; 00231 nMaxMagPeak3 = peaks.GetMaxMagIndex(); 00232 // add candidate 00233 if ( IsGoodCandidate(peakFrequencies[peakIndexes[nMaxMagPeak3]]) ) 00234 tmpFreq.AddElem(peakFrequencies[peakIndexes[nMaxMagPeak3]]); 00235 // restore second peak information 00236 peakMagnitudes[peakIndexes[nMaxMagPeak2]]=aux; 00237 } 00238 // add candidate 00239 if ( IsGoodCandidate(peakFrequencies[peakIndexes[nMaxMagPeak2]]) ) 00240 tmpFreq.AddElem(peakFrequencies[peakIndexes[nMaxMagPeak2]]); 00241 00242 // restore first peak information 00243 peakMagnitudes[peakIndexes[nMaxMagPeak]]=maxMag; 00244 } 00245 // Add peaks as candidates 00246 if ( IsGoodCandidate(peakFrequencies[peakIndexes[nMaxMagPeak]]) ) 00247 tmpFreq.AddElem(peakFrequencies[peakIndexes[nMaxMagPeak]]); 00248 00249 // 2.2.- Peaks below the maximum magnitude peak (except for the 3 max peaks) 00250 for (int i=0; i < nMaxMagPeak; i++ ) 00251 { 00252 // be careful not to exceed the maximun permitted 00253 if (candidatesFrequency.Size() >= mnMaxCandidates) break; 00254 if (i==nMaxMagPeak2) continue; 00255 if (i==nMaxMagPeak3) continue; 00256 if (peakMagnitudes[peakIndexes[i]] <= maxMag - mMaxCandMagDiff ) continue; 00257 if (! IsGoodCandidate(peakFrequencies[peakIndexes[i]]) ) continue; 00258 tmpFreq.AddElem(peakFrequencies[peakIndexes[i]]); 00259 } 00260 00261 // 2.3.- Frequency offset between peaks above the maximun magnitude peak and the maximun magnitude peak 00262 TData freq; 00263 for (int i = nMaxMagPeak+1; i<peakIndexes.Size(); i++) 00264 { 00265 // be careful not to exceed the maximun permitted 00266 if (candidatesFrequency.Size() >= mnMaxCandidates) break; 00267 freq = peakFrequencies[peakIndexes[i]] - peakFrequencies[peakIndexes[nMaxMagPeak]]; 00268 if (freq >= peakFrequencies[peakIndexes[nMaxMagPeak]]*1.1) continue; 00269 if (!IsGoodCandidate(freq)) continue; 00270 tmpFreq.AddElem(freq); 00271 } 00272 00273 // 2.4.- Frequency offset between peaks 00274 for (int i = 0; i<peakIndexes.Size(); i++ ) 00275 { 00276 if (i==nMaxMagPeak) continue; 00277 for (int j = i+1; j<peakIndexes.Size(); j++) 00278 { 00279 // be careful not to exceed the maximun permitted 00280 if (candidatesFrequency.Size() >= mnMaxCandidates) break; 00281 freq = peakFrequencies[peakIndexes[j]] - peakFrequencies[peakIndexes[i]]; 00282 if (freq < peakFrequencies[peakIndexes[nMaxMagPeak]]*1.1) 00283 if (IsGoodCandidate(freq)) 00284 tmpFreq.AddElem(freq); 00285 } 00286 } 00287 00288 // 2.5.- Frequencies related to peaks by integer ratios (before: except for the 3 maximun peaks. not now) 00289 for (int i=0; i<peakIndexes.Size(); i++ ) 00290 { 00291 for (int j=1; j <= mnInt; j++) 00292 { 00293 // be careful not to exceed the maximun permitted 00294 if (candidatesFrequency.Size() >= mnMaxCandidates) break; 00295 freq = peakFrequencies[peakIndexes[i]]/j; 00296 if (freq < peakFrequencies[peakIndexes[nMaxMagPeak]]*1.1) 00297 if (IsGoodCandidate(freq)) 00298 tmpFreq.AddElem(freq); 00299 } 00300 } 00301 00302 if(candidatesFrequency.Size() <= 0) 00303 { 00304 mFundFreqValue.SendControl(0.0f); 00305 return false; 00306 } 00307 00308 // 3.- CALCULATE ERRORS (TMW procedure) 00309 TData oneOverSeventyFive = 0.013333333333f; 00310 00311 const int nPeaks = peaks.GetIndexArray().Size(); 00312 00313 int i; 00314 00315 //These are all needed by the WeightCandidate and it is much better to compute just once 00316 DataArray magFactor(nPeaks); 00317 DataArray magFactor3(nPeaks); 00318 DataArray magFactor4(nPeaks); 00319 DataArray magFactor4r(nPeaks); 00320 DataArray magFactor4q(nPeaks); 00321 DataArray magFactor34q(nPeaks); 00322 00323 for (i=0; i<nPeaks; i++) 00324 { 00325 TData Mag = peakMagnitudes[peakIndexes[i]]; 00326 TData tmpMagFactor = TData(CLAM_max(0.0,maxMag - Mag + 20.0)); 00327 tmpMagFactor = TData(1.0) - tmpMagFactor*oneOverSeventyFive; 00328 if (tmpMagFactor < 0) 00329 tmpMagFactor = 0; 00330 magFactor[i] = tmpMagFactor; 00331 magFactor3[i] = tmpMagFactor*tmpMagFactor*tmpMagFactor; 00332 magFactor4[i] = magFactor3[i]*tmpMagFactor; 00333 magFactor4r[i] = magFactor4[i]*mMPr; 00334 magFactor4q[i] = magFactor4[i]*mMPq; 00335 magFactor34q[i] = magFactor3[i] + magFactor4q[i]; 00336 } 00337 00338 int maxNMP = CLAM_min(mMPnPeaks,nPeaks); 00339 // predicted to measured mismatch error 00340 int maxNPM = 10; 00341 //xamat: does not depend on variable data: compute out of the loop!! 00342 if (nPeaks > 4) 00343 maxNPM = CLAM_min(mPMnPeaks,nPeaks); 00344 00345 for (int i=0; i<candidatesFrequency.Size(); i++) 00346 { 00347 TData myFrequency = candidatesFrequency[i]; 00348 TData myError = WeightCandidate(myFrequency,peaks, magFactor, magFactor34q, magFactor4r, maxNMP, maxNPM); 00349 candidatesError[i] = myError; 00350 } 00351 00352 // 4.- CHOOSE THE BEST CANDIDATES: choose the FundFreq.NCandidates() candidates with the smallest error 00353 00354 tmpFreq.SortByError(); 00355 Fundamental tmpFreq2; 00356 tmpFreq2.SetnMaxCandidates(candidatesFrequency.Size()); 00357 DataArray & candidates2Frequency = tmpFreq2.GetCandidatesFreq(); 00358 DataArray & candidates2Error = tmpFreq2.GetCandidatesErr(); 00359 00360 for (int i=0;i<candidatesFrequency.Size();i++) 00361 { 00362 if (i<=0) 00363 { 00364 tmpFreq2.AddElem(candidatesFrequency[i],candidatesError[i]); 00365 continue; 00366 } 00367 bool addedNearOne=false; 00368 for(int j=0; j<candidates2Frequency.Size(); j++) 00369 { 00370 if (candidatesFrequency[i]<=0.95*candidates2Frequency[j]) continue; 00371 if (candidatesFrequency[i]>=1.1*candidates2Frequency[j]) continue; 00372 addedNearOne = true; 00373 if(candidates2Error[j] > candidatesError[i]) 00374 { 00375 candidates2Frequency[j] = candidatesFrequency[i]; 00376 candidates2Error[j] = candidatesError[i]; 00377 } 00378 } 00379 if(!addedNearOne) 00380 tmpFreq2.AddElem(candidatesFrequency[i],candidatesError[i]); 00381 } 00382 00383 // 5.- SEARCH AROUND FOR A RELATIVE MINIMUM 00384 TData nMinimum = std::min(3,candidates2Frequency.Size()); 00385 for(int i=0; i<nMinimum; i++) 00386 { 00387 TData & myFrequency = candidates2Frequency[i]; 00388 TData Low = myFrequency*TData(.9); 00389 TData High = myFrequency*TData(1.1); 00390 TData Incr = std::max(TData(1.0), myFrequency*TData(.008)); 00391 00392 TData FinalPitch = candidates2Frequency[i]; 00393 TData FinalError = candidates2Error[i]; 00394 for(TData lPitch = Low; lPitch <=High; lPitch += Incr) 00395 { 00396 TData lErr = WeightCandidate(lPitch,peaks, magFactor, magFactor34q, magFactor4r, maxNMP, maxNPM); 00397 if (lPitch > peakFrequencies[peakIndexes[nMaxMagPeak]]*1.1) 00398 lErr +=10; 00399 if (lErr < FinalError) 00400 { 00401 FinalPitch = lPitch; 00402 FinalError = lErr; 00403 } 00404 } 00405 candidates2Frequency[i] = FinalPitch; 00406 candidates2Error[i] = FinalError; 00407 } 00408 00409 // Ordering the minimum 00410 tmpFreq2.SortByError(); 00411 00412 TIndex nCandidates = std::min(outFreq.GetnMaxCandidates(),candidates2Frequency.Size()); 00413 for(int i=0; i<nCandidates; i++) 00414 if(candidates2Error[i] <= mMaxFundFreqError) 00415 outFreq.AddElem(candidates2Frequency[i], candidates2Error[i]); 00416 00417 if(outFreq.GetnCandidates() == 0) 00418 { 00419 mFundFreqValue.SendControl(0.0f); 00420 return false; 00421 } 00422 00423 // Added to get into account fundamental frequency for consecutive frames 00424 // Set Reference fundFreq to last FundFreq 00425 mReferenceFundFreq = outFreq.GetFreq(0); 00426 mFundFreqValue.SendControl( mReferenceFundFreq ); 00427 00428 return true; 00429 } 00430 00431 TData FundFreqDetect::WeightCandidate(TData freq, const SpectralPeakArray& peaks, const DataArray& magFactor, const DataArray& magFactor34q, 00432 const DataArray& magFactor4r, int maxNMP, int maxNPM) const 00433 { 00434 TData Tmp; 00435 const int nPeaks = peaks.GetIndexArray().Size(); 00436 const IndexArray & peakIndexes = peaks.GetIndexArray(); 00437 DataArray& peakFrequencies=peaks.GetFreqBuffer(); 00438 //DataArray& peakMagnitudes=peaks.GetMagBuffer(); 00439 00440 TData ErrorPM = 0; 00441 TData ErrorMP = 0; 00442 00443 TData HarmonicPm = freq; 00444 TSize nPM = maxNPM; 00445 TData lastFreq=peakFrequencies[peakIndexes[nPeaks-1]]; 00446 00447 // measured to predicted mismatch error 00448 TData HarmonicMp = freq; 00449 TSize nMP = nPeaks; 00450 00451 int Peak =0; 00452 int i; 00453 00454 bool finishedPM = false; 00455 bool finishedMP = false; 00456 00457 TData tenTimesFreq = freq*10.f; 00458 bool isFreqHigh = (freq>500.); 00459 TData oneOverFundFreq = 1./freq; 00460 00461 for (i=0; i<nPeaks; i++) 00462 { 00463 if(!finishedPM) 00464 { 00465 if(i<maxNPM) 00466 { 00467 if (HarmonicPm > lastFreq) 00468 { 00469 nPM = i+1; 00470 finishedPM = true; 00471 if (finishedMP) break; 00472 } 00473 else 00474 { 00475 Peak = GetClosestPeak(HarmonicPm,Peak, peakIndexes, peakFrequencies); 00476 //xamat: does not depend on variable data: compute out of the loop!! 00477 TData Freq = peakFrequencies[peakIndexes[Peak]]; 00478 //TData Mag = peakMagnitudes[peakIndexes[Peak]]; 00479 00480 TData FreqDistance = Abs(Freq - HarmonicPm); 00481 //xamat: note that default value for mPMp is 0.5 thus yielding a sqrt 00482 #ifdef CLAM_OPTIMIZE 00483 Tmp = FreqDistance / CLAM_sqrt(HarmonicPm); 00484 #else 00485 Tmp = FreqDistance * CLAM_pow(HarmonicPm, -mPMp); 00486 #endif 00487 ErrorPM += (Tmp +magFactor[Peak] * (mPMq * Tmp - mPMr)); 00488 HarmonicPm += freq; 00489 } 00490 } 00491 } 00492 00493 if(!finishedMP) 00494 { 00495 TData Freq = TData(peakFrequencies[peakIndexes[i]]); 00496 // For high frequency candidates, not get into account too-low peaks 00497 if ( (isFreqHigh) && (Freq < 100)) 00498 continue; 00499 00500 HarmonicMp = GetClosestHarmonic(Freq,freq, oneOverFundFreq); 00501 TData FreqDistance = Abs(Freq - HarmonicMp); 00502 //xamat: note that default value for mMPp is 0.5 thus yielding a sqrt 00503 #ifdef CLAM_OPTIMIZE 00504 Tmp = FreqDistance / CLAM_sqrt(Freq); 00505 #else 00506 Tmp = FreqDistance * CLAM_pow(Freq, -mMPp); 00507 #endif 00508 ErrorMP += magFactor34q[i] * Tmp - magFactor4r[i]; 00509 if (Freq > tenTimesFreq){ 00510 if (i > maxNMP) 00511 { 00512 nMP = i+1; 00513 finishedMP = true; 00514 if (finishedPM) break; 00515 } 00516 } 00517 } 00518 } 00519 00520 00521 // total error 00522 if (ErrorPM > 20) 00523 ErrorPM = 20 + (ErrorPM-20)*(ErrorPM-20); 00524 if (ErrorMP > 20) 00525 ErrorMP = 20 + (ErrorMP-20)*(ErrorMP-20); 00526 00527 return (mPMCont * ErrorPM/nPM + mMPCont * ErrorMP/nMP); 00528 } 00529 00530 /* Get the closest peak to a given frequency 00531 and returns the number of the closest peak 00532 there's another parameter, peak, that contains the last peak taken */ 00533 int FundFreqDetect::GetClosestPeak(TData freq, int firstPeak, const IndexArray& peakIndexes, const DataArray & peakFrequencies) const 00534 { 00535 const int size = peakIndexes.Size(); 00536 int bestpeak = firstPeak; 00537 TData distance = INFINITE_MAGNITUD; 00538 for (int peak=firstPeak; peak < size; peak++) 00539 { 00540 TData nextdistance = Abs(freq - peakFrequencies[peakIndexes[peak]]); 00541 if (nextdistance >= distance) 00542 return peak-1; 00543 bestpeak = peak; 00544 distance=nextdistance; 00545 } 00546 return bestpeak; 00547 } 00548 00549 /* Get Closest Harmonic */ 00550 TData FundFreqDetect::GetClosestHarmonic(TData peak, TData fundfreq, TData oneOverFundfreq) const 00551 { 00552 if(peak<fundfreq) 00553 return fundfreq; 00554 //xamat: this should be optimized! 00555 return floor(peak*oneOverFundfreq+.5)*fundfreq; 00556 } 00557 00558 bool FundFreqDetect::IsGoodCandidate(TData freq) const 00559 { 00560 return (freq >= mLowestFundFreq) && (freq <= mHighestFundFreq); 00561 } 00562 00563 } // namespace CLAM 00564