This is harmDetection.c in view mode; [Download] [Up]
#include "../sms.h" /* get closest peak to a given harmonic of the possible fundamental * return the number of the closest peak or -1 if not found * * int iPeakCandidate peak number of possible fundamental * int nHarm number of harmonic * PEAK *pSpectralPeaks all the peaks * int *pICurrentPeak last peak taken */ static int GetClosestPeak (int iPeakCandidate, int nHarm, PEAK *pSpectralPeaks, int *pICurrentPeak, ANAL_PARAMS analParams) { int iBestPeak = *pICurrentPeak + 1, iNextPeak; float fBestPeakFreq = pSpectralPeaks[iBestPeak].fFreq, fHarmFreq = (1 + nHarm) * pSpectralPeaks[iPeakCandidate].fFreq / analParams.iRefHarmonic, fMinDistance = fabs(fHarmFreq - fBestPeakFreq), fMaxPeakDev = .5 * fHarmFreq / (nHarm + 1), fDistance; iNextPeak = iBestPeak + 1; fDistance = fabs(fHarmFreq - pSpectralPeaks[iNextPeak].fFreq); while (fDistance < fMinDistance) { iBestPeak = iNextPeak; fMinDistance = fDistance; iNextPeak++; fDistance = fabs (fHarmFreq - pSpectralPeaks[iNextPeak].fFreq); } /* make sure the chosen peak is good */ fBestPeakFreq = pSpectralPeaks[iBestPeak].fFreq; /* if best peak is not in the range */ if (fabs (fBestPeakFreq - fHarmFreq) > fMaxPeakDev) return (-1); *pICurrentPeak = iBestPeak; return (iBestPeak); } /* check if peak is larger enough to be considered a fundamental * without any further testing or too small to be considered * * return 1 if big peak -1 if too small , otherwise return 0 * float fRefHarmMag magnitude of possible fundamental * PEAK *pSpectralPeaks all the peaks * int nCand number of existing candidates */ static int ComparePeak (float fRefHarmMag, PEAK *pSpectralPeaks, int nCand, ANAL_PARAMS analParams) { int iPeak; float fMag = 0; /* if peak is very large take it as possible fundamental */ if (nCand == 0 && fRefHarmMag > 80.) return (1); /* compare the peak with the first N_FUND_HARM peaks */ /* if too small forget it */ for (iPeak = 0; iPeak < N_FUND_HARM; iPeak++) if (pSpectralPeaks[iPeak].fMag > 0 && fRefHarmMag - pSpectralPeaks[iPeak].fMag < -analParams.fRefHarmMagDiffFromMax) return (-1); /* if it is much bigger than rest take it */ for (iPeak = 0; iPeak < N_FUND_HARM; iPeak++) { fMag = pSpectralPeaks[iPeak].fMag; if (fMag <= 0 || ((fMag != fRefHarmMag) && (nCand > 0) && (fRefHarmMag - fMag < 30.0)) || ((nCand == 0) && (fRefHarmMag - fMag < 15.0))) return (0); } return (1); } /* check if the current peak is a harmonic of one of the candidates * return 1 if it is a harmonic, 0 if it is not * * float fFundFreq; frequency of peak to be tested * HARM_CANDIDATE *pCHarmonic; all candidates accepted * int nCand; location of las candidate */ int CheckIfHarmonic (float fFundFreq, HARM_CANDIDATE *pCHarmonic, int nCand) { int iPeak; /* go through all the candidates checking if they are fundamentals */ /* of the peak to be considered */ for (iPeak = 0; iPeak < nCand; iPeak++) if (fabs(floor((double)(fFundFreq / pCHarmonic[iPeak].fFreq) + .5) - (fFundFreq / pCHarmonic[iPeak].fFreq)) <= .1) return (1); return (0); } /* consider a peak as a possible candidate and give it a weight value, * return -1 if not good enough for a candidate, return 0 if reached * the top frequency boundary, return -2 if stop checking because it * found a really good one, return 1 if the peak is a good candidate * * int iPeak; iPeak number to be considered * PEAK *pSpectralPeaks; all the peaks * FUND_CANDIDATE *pCFundamental; all the candidates * int nCand; candidate number that is to be filled * ANAL_PARAMS analParams; analysis parameters * float fRefFundamental; previous fundamental */ int GoodCandidate (int iPeak, PEAK *pSpectralPeaks, HARM_CANDIDATE *pCHarmonic, int nCand, ANAL_PARAMS analParams, float fRefFundamental) { float fHarmFreq, fRefHarmFreq, fRefHarmMag, fTotalMag = 0, fTotalDev = 0, fTotalMaxMag = 0, fAvgMag = 0, fAvgDev = 0, fHarmRatio = 0; int iHarm = 0, iChosenPeak = 0, iPeakComp, iCurrentPeak, nGoodHarm = 0, i; fRefHarmFreq = fHarmFreq = pSpectralPeaks[iPeak].fFreq; fTotalDev = 0; fRefHarmMag = pSpectralPeaks[iPeak].fMag; fTotalMag = fRefHarmMag; /* check if magnitude is big enough */ if (((fRefFundamental > 0) && (fRefHarmMag < analParams.fMinRefHarmMag - 10)) || ((fRefFundamental <= 0) && (fRefHarmMag < analParams.fMinRefHarmMag))) return (-1); /* check that is not a harmonic of a previous candidate */ if (nCand > 0 && CheckIfHarmonic (fRefHarmFreq / analParams.iRefHarmonic, pCHarmonic, nCand)) return (-1); /* check if it is very big or very small */ iPeakComp = ComparePeak (fRefHarmMag, pSpectralPeaks, nCand, analParams); /* too small */ if (iPeakComp == -1) return (-1); /* very big */ else if (iPeakComp == 1) { pCHarmonic[nCand].fFreq = fRefHarmFreq; pCHarmonic[nCand].fMag = fRefHarmMag; pCHarmonic[nCand].fMagPerc = 1; pCHarmonic[nCand].fFreqDev = 0; pCHarmonic[nCand].fHarmRatio = 1; return (-2); } /* get a weight on the peak by comparing its harmonic series */ /* with the existing peaks */ if (analParams.iSoundType != TYPE_SINGLE_NOTE) { fHarmFreq = fRefHarmFreq; iCurrentPeak = iPeak; nGoodHarm = 0; for (iHarm = analParams.iRefHarmonic; iHarm < N_FUND_HARM; iHarm++) { fHarmFreq += fRefHarmFreq / analParams.iRefHarmonic; iChosenPeak = GetClosestPeak(iPeak, iHarm, pSpectralPeaks, &iCurrentPeak, analParams); if (iChosenPeak > 0) { fTotalDev += fabs(fHarmFreq - pSpectralPeaks[iChosenPeak].fFreq) / fHarmFreq; fTotalMag += pSpectralPeaks[iChosenPeak].fMag; nGoodHarm++; } } for (i = 0; i <= iCurrentPeak; i++) fTotalMaxMag += pSpectralPeaks[i].fMag; fAvgDev = fTotalDev / (iHarm + 1); fAvgMag = fTotalMag / fTotalMaxMag; fHarmRatio = (float) nGoodHarm / (N_FUND_HARM - 1); } if (analParams.iDebugMode == DEBUG_HARM_DET || analParams.iDebugMode == DEBUG_ALL) fprintf(stdout, "Harmonic Candidate: frq: %f mag: %f frqDev: %f magPrc: %f harmDev %f\n", fRefHarmFreq, fRefHarmMag, fAvgDev, fAvgMag, fHarmRatio); if (analParams.iSoundType != TYPE_SINGLE_NOTE) { if (fRefFundamental > 0) { if(fAvgDev > FREQ_DEV_THRES || fAvgMag < MAG_PERC_THRES - .1 || fHarmRatio < HARM_RATIO_THRES - .1) return (-1); } else { if (fAvgDev > FREQ_DEV_THRES || fAvgMag < MAG_PERC_THRES || fHarmRatio < HARM_RATIO_THRES) return (-1); } } pCHarmonic[nCand].fFreq = fRefHarmFreq; pCHarmonic[nCand].fMag = fRefHarmMag; pCHarmonic[nCand].fMagPerc = fAvgMag; pCHarmonic[nCand].fFreqDev = fAvgDev; pCHarmonic[nCand].fHarmRatio = fHarmRatio; return (1); } /* choose the best fundamental out of all the candidates * HARM_CANDIDATE *pCFundamental; array of candidates * ANAL_PARAMS analParams; analysis parameters * int nGoodPeaks; number of candiates * float fPrevFund; reference fundamental */ static int GetBestCandidate (HARM_CANDIDATE *pCHarmonic, ANAL_PARAMS analParams, int nGoodPeaks, float fPrevFund) { int iBestCandidate = 0, iPeak; float fBestFreq, fHarmFreq, fDev; /* if a fundamental existed in previous frame take the closest candidate */ if (fPrevFund > 0) for (iPeak = 1; iPeak < nGoodPeaks; iPeak++) { if (fabs (fPrevFund - pCHarmonic[iPeak].fFreq / analParams.iRefHarmonic) < fabs(fPrevFund - pCHarmonic[iBestCandidate].fFreq / analParams.iRefHarmonic)) iBestCandidate = iPeak; } else /* try to find the best candidate */ for (iPeak = 1; iPeak < nGoodPeaks; iPeak++) { fBestFreq = pCHarmonic[iBestCandidate].fFreq / analParams.iRefHarmonic; fHarmFreq = fBestFreq * floor (.5 + (pCHarmonic[iPeak].fFreq / analParams.iRefHarmonic) / fBestFreq); fDev = fabs (fHarmFreq - (pCHarmonic[iPeak].fFreq / analParams.iRefHarmonic)) / fHarmFreq; /* if candidate is far from harmonic from best candidate and */ /* bigger, take it */ if (fDev > .2 && pCHarmonic[iPeak].fMag > pCHarmonic[iBestCandidate].fMag) iBestCandidate = iPeak; /* if frequency deviation is much smaller, take it */ else if (pCHarmonic[iPeak].fFreqDev < .2 * pCHarmonic[iBestCandidate].fFreqDev) iBestCandidate = iPeak; /* if freq. deviation is smaller and bigger amplitude, take it */ else if (pCHarmonic[iPeak].fFreqDev < pCHarmonic[iBestCandidate].fFreqDev && pCHarmonic[iPeak].fMagPerc > pCHarmonic[iBestCandidate].fMagPerc && pCHarmonic[iPeak].fMag > pCHarmonic[iBestCandidate].fMag) iBestCandidate = iPeak; } return (iBestCandidate); } /* find a given harmonic peak from a set of spectral peaks, * put the frequency of the fundamental in the current frame * ANAL_FRAME *pFrame; current frame * float fRefFundamental; frequency of previous frame * ANAL_PARAMS analParams; analysis parameters */ void HarmDetection (ANAL_FRAME *pFrame, float fRefFundamental, ANAL_PARAMS analParams) { int iPeak = -1, nGoodPeaks = 0, iCandidate, iBestCandidate; float fLowestFreq, fHighestFreq, fPeakFreq; HARM_CANDIDATE pCHarmonic[N_HARM_PEAKS]; /* find all possible candidates to use as harmonic reference */ fLowestFreq = analParams.fLowestFundamental * analParams.iRefHarmonic; fHighestFreq = analParams.fHighestFundamental * analParams.iRefHarmonic; while (fPeakFreq < fHighestFreq) { iPeak++; fPeakFreq = pFrame->pSpectralPeaks[iPeak].fFreq; if (fPeakFreq > fHighestFreq) break; /* no more peaks */ if (pFrame->pSpectralPeaks[iPeak].fMag <= 0) break; /* peak too low */ if (fPeakFreq < fLowestFreq) continue; /* if previous fundamental look only around it */ if (fRefFundamental > 0 && fabs(fPeakFreq - (analParams.iRefHarmonic * fRefFundamental)) / fRefFundamental > .5) continue; iCandidate = GoodCandidate (iPeak, pFrame->pSpectralPeaks, pCHarmonic, nGoodPeaks, analParams, fRefFundamental); /* good candiate found */ if (iCandidate == 1) nGoodPeaks++; /* a perfect candiate found */ else if (iCandidate == -2) { nGoodPeaks++; break; } } /* if no candidate for fundamental, continue */ if (nGoodPeaks == 0) pFrame->fFundamental = -1; /* if only 1 candidate for fundamental take it */ else if (nGoodPeaks == 1) pFrame->fFundamental = pCHarmonic[0].fFreq / analParams.iRefHarmonic; /* if more than one candidate choose the best one */ else { iBestCandidate = GetBestCandidate (pCHarmonic, analParams, nGoodPeaks, fRefFundamental); pFrame->fFundamental = pCHarmonic[iBestCandidate].fFreq / analParams.iRefHarmonic; } }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.