/* * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) * UNIVERSITAT POMPEU FABRA * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /*! \file peakContinuation.c * \brief peak continuation algorithm and functions */ #include "sms.h" /*! guide states */ #define GUIDE_BEG -2 #define GUIDE_DEAD -1 #define GUIDE_ACTIVE 0 /*!< maximum number of peak continuation candidates */ #define MAX_CONT_CANDIDATES 5 /*! \brief function to get the next closest peak from a guide * * \param fGuideFreq guide's frequency * \param pFFreqDistance distance of last best peak from guide * \param pSpectralPeaks array of peaks * \param pAnalParams analysis parameters * \param fFreqDev maximum deviation from guide * \return peak number or -1 if nothing is good */ static int GetNextClosestPeak(sfloat fGuideFreq, sfloat *pFFreqDistance, SMS_Peak *pSpectralPeaks, SMS_AnalParams *pAnalParams, sfloat fFreqDev) { int iInitialPeak = SMS_MAX_NPEAKS * fGuideFreq / (pAnalParams->iSamplingRate * .5); int iLowPeak, iHighPeak, iChosenPeak = -1; sfloat fLowDistance, fHighDistance, fFreq; if(iInitialPeak >= pAnalParams->maxPeaks) iInitialPeak = 0; else if(pSpectralPeaks[iInitialPeak].fFreq <= 0) iInitialPeak = 0; /* find a low peak to start */ fLowDistance = fGuideFreq - pSpectralPeaks[iInitialPeak].fFreq; if(floor(fLowDistance) < floor(*pFFreqDistance)) { while(floor(fLowDistance) <= floor(*pFFreqDistance) && iInitialPeak > 0) { iInitialPeak--; fLowDistance = fGuideFreq - pSpectralPeaks[iInitialPeak].fFreq; } } else { while(floor(fLowDistance) >= floor(*pFFreqDistance) && iInitialPeak < (pAnalParams->maxPeaks-1)) { iInitialPeak++; if((fFreq = pSpectralPeaks[iInitialPeak].fFreq) == 0) return -1; fLowDistance = fGuideFreq - fFreq; } if(iInitialPeak > 0) iInitialPeak--; fLowDistance = fGuideFreq - pSpectralPeaks[iInitialPeak].fFreq; } if(floor(fLowDistance) <= floor(*pFFreqDistance) || fLowDistance > fFreqDev) iLowPeak = -1; else iLowPeak = iInitialPeak; /* find a high peak to finish */ iHighPeak = iInitialPeak; fHighDistance = fGuideFreq - pSpectralPeaks[iHighPeak].fFreq; while(floor(fHighDistance) >= floor(-*pFFreqDistance) && iHighPeak < (pAnalParams->maxPeaks - 1)) { iHighPeak++; if((fFreq = pSpectralPeaks[iHighPeak].fFreq) == 0) { iHighPeak = -1; break; } fHighDistance = fGuideFreq - fFreq; } if(fHighDistance > 0 || fabs(fHighDistance) > fFreqDev || floor(fabs(fHighDistance)) <= floor(*pFFreqDistance)) iHighPeak = -1; /* chose between the two extrema */ if(iHighPeak >= 0 && iLowPeak >= 0) { if(fabs(fHighDistance) > fLowDistance) iChosenPeak = iLowPeak; else iChosenPeak = iHighPeak; } else if(iHighPeak < 0 && iLowPeak >= 0) iChosenPeak = iLowPeak; else if(iHighPeak >= 0 && iLowPeak < 0) iChosenPeak = iHighPeak; else return -1; *pFFreqDistance = fabs(fGuideFreq - pSpectralPeaks[iChosenPeak].fFreq); return iChosenPeak; } /*! \brief choose the best candidate out of all * * \param pCandidate pointer to all the continuation candidates * \param nCandidates number of candidates * \param fFreqDev maximum frequency deviation allowed * \return the peak number of the best candidate */ static int ChooseBestCand(SMS_ContCandidate *pCandidate, int nCandidates, sfloat fFreqDev) { int i, iHighestCand, iClosestCand, iBestCand = 0; sfloat fMaxMag, fClosestFreq; /* intial guess */ iClosestCand = 0; fClosestFreq = pCandidate[iClosestCand].fFreqDev; iHighestCand = 0; fMaxMag = pCandidate[iHighestCand].fMagDev; /* get the best candidate */ for (i = 1; i < nCandidates; i++) { /* look for the one with highest magnitude */ if (pCandidate[i].fMagDev > fMaxMag) { fMaxMag = pCandidate[i].fMagDev; iHighestCand = i; } /* look for the closest one to the guide */ if (pCandidate[i].fFreqDev < fClosestFreq) { fClosestFreq = pCandidate[i].fFreqDev; iClosestCand = i; } } iBestCand = iHighestCand; /* reconcile the two results */ if(iBestCand != iClosestCand && fabs(pCandidate[iHighestCand].fFreqDev - fClosestFreq) > fFreqDev / 2) iBestCand = iClosestCand; return pCandidate[iBestCand].iPeak; } /*! \brief check for one guide that has choosen iBestPeak * * \param iBestPeak choosen peak for a guide * \param pGuides array of guides * \param nGuides total number of guides * \return number of guide that chose the peak, or -1 if none */ static int CheckForConflict(int iBestPeak, SMS_Guide *pGuides, int nGuides) { int iGuide; for (iGuide = 0; iGuide < nGuides; iGuide++) if (pGuides[iGuide].iPeakChosen == iBestPeak) return iGuide; return -1; } /*! \brief chose the best of the two guides for the conflicting peak * * \param iConflictingGuide conflicting guide number * \param iGuide guide number * \param pGuides array of guides * \param pSpectralPeaks array of peaks * \return number of guide */ static int BestGuide(int iConflictingGuide, int iGuide, SMS_Guide *pGuides, SMS_Peak *pSpectralPeaks) { int iConflictingPeak = pGuides[iConflictingGuide].iPeakChosen; sfloat fGuideDistance = fabs(pSpectralPeaks[iConflictingPeak].fFreq - pGuides[iGuide].fFreq); sfloat fConfGuideDistance = fabs(pSpectralPeaks[iConflictingPeak].fFreq - pGuides[iConflictingGuide].fFreq); if(fGuideDistance > fConfGuideDistance) return iConflictingGuide; else return iGuide; } /*! \brief function to find the best continuation peak for a given guide * \param pGuides guide attributes * \param iGuide number of guide * \param pSpectralPeaks peak values at the current frame * \param pAnalParams analysis parameters * \param fFreqDev frequency deviation allowed * \return the peak number */ int GetBestPeak(SMS_Guide *pGuides, int iGuide, SMS_Peak *pSpectralPeaks, SMS_AnalParams *pAnalParams, sfloat fFreqDev) { int iCand = 0, iPeak, iBestPeak, iConflictingGuide, iWinnerGuide; sfloat fGuideFreq = pGuides[iGuide].fFreq, fGuideMag = pGuides[iGuide].fMag, fMagDistance = 0; sfloat fFreqDistance = -1; SMS_ContCandidate pCandidate[MAX_CONT_CANDIDATES]; /* find all possible candidates */ while (iCand < MAX_CONT_CANDIDATES) { /* find the next best peak */ if((iPeak = GetNextClosestPeak(fGuideFreq, &fFreqDistance, pSpectralPeaks, pAnalParams, fFreqDev)) < 0) break; /* if the peak's magnitude is not too small accept it as */ /* possible candidate */ if ((fMagDistance = pSpectralPeaks[iPeak].fMag - fGuideMag) > -20.0) { pCandidate[iCand].fFreqDev = fabs(fFreqDistance); pCandidate[iCand].fMagDev = fMagDistance; pCandidate[iCand].iPeak = iPeak; if(pAnalParams->iDebugMode == SMS_DBG_PEAK_CONT || pAnalParams->iDebugMode == SMS_DBG_ALL) fprintf(stdout, "candidate %d: freq %f mag %f\n", iCand, pSpectralPeaks[iPeak].fFreq, pSpectralPeaks[iPeak].fMag); iCand++; } } /* get best candidate */ if(iCand < 1) return 0; else if (iCand == 1) iBestPeak = pCandidate[0].iPeak; else iBestPeak = ChooseBestCand (pCandidate, iCand, pAnalParams->fFreqDeviation); if(pAnalParams->iDebugMode == SMS_DBG_PEAK_CONT || pAnalParams->iDebugMode == SMS_DBG_ALL) fprintf (stdout, "BestCandidate: freq %f\n", pSpectralPeaks[iBestPeak].fFreq); /* if peak taken by another guide resolve conflict */ if ((iConflictingGuide = CheckForConflict (iBestPeak, pGuides, pAnalParams->nGuides)) >= 0) { iWinnerGuide = BestGuide (iConflictingGuide, iGuide, pGuides, pSpectralPeaks); if(pAnalParams->iDebugMode == SMS_DBG_PEAK_CONT || pAnalParams->iDebugMode == SMS_DBG_ALL) fprintf (stdout, "Conflict: guide: %d (%f), and guide: %d (%f). best: %d\n", iGuide, pGuides[iGuide].fFreq, iConflictingGuide, pGuides[iConflictingGuide].fFreq, iWinnerGuide); if (iGuide == iWinnerGuide) { pGuides[iGuide].iPeakChosen = iBestPeak; pGuides[iConflictingGuide].iPeakChosen = -1; } } else pGuides[iGuide].iPeakChosen = iBestPeak; return iBestPeak; } /*! \brief function to get the next maximum (magnitude) peak * \param pSpectralPeaks array of peaks * \param pFCurrentMax last peak maximum * \return the number of the maximum peak */ static int GetNextMax(SMS_Peak *pSpectralPeaks, SMS_AnalParams *pAnalParams, sfloat *pFCurrentMax) { sfloat fPeakMag; sfloat fMaxMag = 0.; int iPeak, iMaxPeak = -1; for (iPeak = 0; iPeak < pAnalParams->maxPeaks; iPeak++) { fPeakMag = pSpectralPeaks[iPeak].fMag; if (fPeakMag == 0) break; if (fPeakMag > fMaxMag && fPeakMag < *pFCurrentMax) { iMaxPeak = iPeak; fMaxMag = fPeakMag; } } *pFCurrentMax = fMaxMag; return (iMaxPeak); } /*! \brief function to get a good starting peak for a track * * \param iGuide current guide * \param pGuides array of guides * \param nGuides total number of guides * \param pSpectralPeaks array of peaks * \param pFCurrentMax current peak maximum * \return \todo should this return something? */ static int GetStartingPeak(int iGuide, SMS_Guide *pGuides, int nGuides, SMS_Peak *pSpectralPeaks, SMS_AnalParams *pAnalParams, sfloat *pFCurrentMax) { int iPeak = -1; short peakNotFound = 1; while (peakNotFound == 1 && *pFCurrentMax > 0) { /* \todo I don't think this ever returns -1, but check */ if ((iPeak = GetNextMax(pSpectralPeaks, pAnalParams, pFCurrentMax)) < 0) return (-1); if (CheckForConflict (iPeak, pGuides, nGuides) < 0) { pGuides[iGuide].iPeakChosen = iPeak; pGuides[iGuide].iStatus = GUIDE_BEG; pGuides[iGuide].fFreq = pSpectralPeaks[iPeak].fFreq; peakNotFound = 0; } } return (1); } /*! \brief function to advance the guides through the next frame * * the output is the frequency, magnitude, and phase tracks * * \param iFrame current frame number * \param pAnalParams analysis parameters * \return error code \see SMS_ERRORS */ int sms_peakContinuation(int iFrame, SMS_AnalParams *pAnalParams) { int iGuide, iCurrentPeak = -1, iGoodPeak = -1; sfloat fFund = pAnalParams->ppFrames[iFrame]->fFundamental, fFreqDev = fFund * pAnalParams->fFreqDeviation, fCurrentMax = 1000; /* update guides with fundamental contribution */ if(fFund > 0 && (pAnalParams->iFormat == SMS_FORMAT_H || pAnalParams->iFormat == SMS_FORMAT_HP)) for(iGuide = 0; iGuide < pAnalParams->nGuides; iGuide++) pAnalParams->guides[iGuide].fFreq = (1 - pAnalParams->fFundContToGuide) * pAnalParams->guides[iGuide].fFreq + pAnalParams->fFundContToGuide * fFund * (iGuide + 1); if(pAnalParams->iDebugMode == SMS_DBG_PEAK_CONT || pAnalParams->iDebugMode == SMS_DBG_ALL) fprintf(stdout, "Frame %d Peak Continuation: \n", pAnalParams->ppFrames[iFrame]->iFrameNum); /* continue all guides */ for(iGuide = 0; iGuide < pAnalParams->nGuides; iGuide++) { sfloat fPreviousFreq = pAnalParams->ppFrames[iFrame-1]->deterministic.pFSinFreq[iGuide]; /* get the guide value by upgrading the previous guide */ if(fPreviousFreq > 0) pAnalParams->guides[iGuide].fFreq = (1 - pAnalParams->fPeakContToGuide) * pAnalParams->guides[iGuide].fFreq + pAnalParams->fPeakContToGuide * fPreviousFreq; if(pAnalParams->iDebugMode == SMS_DBG_PEAK_CONT || pAnalParams->iDebugMode == SMS_DBG_ALL) fprintf(stdout, "Guide %d: freq %f, mag %f\n", iGuide, pAnalParams->guides[iGuide].fFreq, pAnalParams->guides[iGuide].fMag); if(pAnalParams->guides[iGuide].fFreq <= 0.0 || pAnalParams->guides[iGuide].fFreq > pAnalParams->fHighestFreq) { pAnalParams->guides[iGuide].iStatus = GUIDE_DEAD; pAnalParams->guides[iGuide].fFreq = 0; continue; } pAnalParams->guides[iGuide].iPeakChosen = -1; if(pAnalParams->iFormat == SMS_FORMAT_IH || pAnalParams->iFormat == SMS_FORMAT_IHP) fFreqDev = pAnalParams->guides[iGuide].fFreq * pAnalParams->fFreqDeviation; /* get the best peak for the guide */ iGoodPeak = GetBestPeak(pAnalParams->guides, iGuide, pAnalParams->ppFrames[iFrame]->pSpectralPeaks, pAnalParams, fFreqDev); } /* try to find good peaks for the GUIDE_DEAD guides */ if(pAnalParams->iFormat == SMS_FORMAT_IH || pAnalParams->iFormat == SMS_FORMAT_IHP) for(iGuide = 0; iGuide < pAnalParams->nGuides; iGuide++) { if(pAnalParams->guides[iGuide].iStatus != GUIDE_DEAD) continue; if(GetStartingPeak(iGuide, pAnalParams->guides, pAnalParams->nGuides, pAnalParams->ppFrames[iFrame]->pSpectralPeaks, pAnalParams, &fCurrentMax) == -1) break; } /* save all the continuation values, * assume output tracks are already clear */ for(iGuide = 0; iGuide < pAnalParams->nGuides; iGuide++) { if(pAnalParams->guides[iGuide].iStatus == GUIDE_DEAD) continue; if(pAnalParams->iFormat == SMS_FORMAT_IH || pAnalParams->iFormat == SMS_FORMAT_IHP) { if(pAnalParams->guides[iGuide].iStatus > 0 && pAnalParams->guides[iGuide].iPeakChosen == -1) { if(pAnalParams->guides[iGuide].iStatus++ > pAnalParams->iMaxSleepingTime) { pAnalParams->guides[iGuide].iStatus = GUIDE_DEAD; pAnalParams->guides[iGuide].fFreq = 0; pAnalParams->guides[iGuide].fMag = 0; pAnalParams->guides[iGuide].iPeakChosen = -1; } else pAnalParams->guides[iGuide].iStatus++; continue; } if(pAnalParams->guides[iGuide].iStatus == GUIDE_ACTIVE && pAnalParams->guides[iGuide].iPeakChosen == -1) { pAnalParams->guides[iGuide].iStatus = 1; continue; } } /* if good continuation peak found, save it */ if((iCurrentPeak = pAnalParams->guides[iGuide].iPeakChosen) >= 0) { pAnalParams->ppFrames[iFrame]->deterministic.pFSinFreq[iGuide] = pAnalParams->ppFrames[iFrame]->pSpectralPeaks[iCurrentPeak].fFreq; pAnalParams->ppFrames[iFrame]->deterministic.pFSinAmp[iGuide] = pAnalParams->ppFrames[iFrame]->pSpectralPeaks[iCurrentPeak].fMag; pAnalParams->ppFrames[iFrame]->deterministic.pFSinPha[iGuide] = pAnalParams->ppFrames[iFrame]->pSpectralPeaks[iCurrentPeak].fPhase; pAnalParams->guides[iGuide].iStatus = GUIDE_ACTIVE; pAnalParams->guides[iGuide].iPeakChosen = -1; } } return SMS_OK; }