00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
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
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
00109 bool FundFreqDetect::Do(SpectralPeakArray& peaks,Fundamental& outFreq)
00110 {
00111 outFreq.Init();
00112
00113
00114 CLAM_ASSERT (outFreq.GetnMaxCandidates() > 0,
00115 "FundFreqDet::Detection: negative number of candidates wanted");
00116
00117
00118 CLAM_ASSERT (outFreq.GetnMaxCandidates() <= mnMaxCandidates,
00119 "FundFreqDet::Detection:Number of candidates wanted bigger "
00120 "than the maximum configured on the algorithm");
00121
00122
00123 if (peaks.GetnPeaks() <= 0)
00124 {
00125 mFundFreqValue.SendControl(0.0f);
00126 return false;
00127 }
00128
00129
00130 TIndex nMaxMagPeak = peaks.GetMaxMagPos();
00131 TData maxMag = peaks.GetMag(nMaxMagPeak);
00132
00133
00134
00135 if(!peaks.HasIndexArray())
00136 {
00137 peaks.AddIndexArray();
00138 peaks.UpdateData();
00139 peaks.SetnMaxPeaks(peaks.GetnMaxPeaks());
00140 }
00141
00142
00143 peaks.ResetIndices();
00144
00145
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
00161 for(int i=z; i<nMaxMagPeak; i++)
00162 {
00163 if(peakMagnitudes[i] < maxMag - 30)
00164 peaks.DeleteIndex(i);
00165 }
00166
00167
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
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
00191 if (peakIndexes.Size() <= 0)
00192 {
00193 mFundFreqValue.SendControl(0.0f);
00194 return false;
00195 }
00196
00197
00198 nMaxMagPeak = peaks.GetMaxMagIndex();
00199 maxMag = peakMagnitudes[peakIndexes[nMaxMagPeak]];
00200
00201
00202
00203 Fundamental tmpFreq;
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
00214 if( IsGoodCandidate(mReferenceFundFreq) )
00215 tmpFreq.AddElem(mReferenceFundFreq);
00216
00217
00218 TIndex nMaxMagPeak2 = nMaxMagPeak;
00219 TIndex nMaxMagPeak3 = nMaxMagPeak;
00220 if(peakIndexes.Size() >= 2)
00221 {
00222
00223 peakMagnitudes[peakIndexes[nMaxMagPeak]]=-2000;
00224 nMaxMagPeak2 = peaks.GetMaxMagIndex();
00225 if(peakIndexes.Size() >= 3)
00226 {
00227 TData aux;
00228
00229 aux = peakMagnitudes[peakIndexes[nMaxMagPeak2]];
00230 peakMagnitudes[peakIndexes[nMaxMagPeak2]]=-2000;
00231 nMaxMagPeak3 = peaks.GetMaxMagIndex();
00232
00233 if ( IsGoodCandidate(peakFrequencies[peakIndexes[nMaxMagPeak3]]) )
00234 tmpFreq.AddElem(peakFrequencies[peakIndexes[nMaxMagPeak3]]);
00235
00236 peakMagnitudes[peakIndexes[nMaxMagPeak2]]=aux;
00237 }
00238
00239 if ( IsGoodCandidate(peakFrequencies[peakIndexes[nMaxMagPeak2]]) )
00240 tmpFreq.AddElem(peakFrequencies[peakIndexes[nMaxMagPeak2]]);
00241
00242
00243 peakMagnitudes[peakIndexes[nMaxMagPeak]]=maxMag;
00244 }
00245
00246 if ( IsGoodCandidate(peakFrequencies[peakIndexes[nMaxMagPeak]]) )
00247 tmpFreq.AddElem(peakFrequencies[peakIndexes[nMaxMagPeak]]);
00248
00249
00250 for (int i=0; i < nMaxMagPeak; i++ )
00251 {
00252
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
00262 TData freq;
00263 for (int i = nMaxMagPeak+1; i<peakIndexes.Size(); i++)
00264 {
00265
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
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
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
00289 for (int i=0; i<peakIndexes.Size(); i++ )
00290 {
00291 for (int j=1; j <= mnInt; j++)
00292 {
00293
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
00309 TData oneOverSeventyFive = 0.013333333333f;
00310
00311 const int nPeaks = peaks.GetIndexArray().Size();
00312
00313 int i;
00314
00315
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
00340 int maxNPM = 10;
00341
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
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
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
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
00424
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
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
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
00477 TData Freq = peakFrequencies[peakIndexes[Peak]];
00478
00479
00480 TData FreqDistance = Abs(Freq - HarmonicPm);
00481
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
00497 if ( (isFreqHigh) && (Freq < 100))
00498 continue;
00499
00500 HarmonicMp = GetClosestHarmonic(Freq,freq, oneOverFundFreq);
00501 TData FreqDistance = Abs(Freq - HarmonicMp);
00502
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
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
00531
00532
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
00550 TData FundFreqDetect::GetClosestHarmonic(TData peak, TData fundfreq, TData oneOverFundfreq) const
00551 {
00552 if(peak<fundfreq)
00553 return fundfreq;
00554
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 }
00564