diff options
-rw-r--r-- | sms/analysis.c | 2 | ||||
-rw-r--r-- | sms/harmDetection.c | 232 | ||||
-rw-r--r-- | sms/peakContinuation.c | 44 | ||||
-rw-r--r-- | sms/sms.c | 15 | ||||
-rw-r--r-- | tests/sms.py | 525 |
5 files changed, 577 insertions, 241 deletions
diff --git a/sms/analysis.c b/sms/analysis.c index f4da530..a435a5a 100644 --- a/sms/analysis.c +++ b/sms/analysis.c @@ -87,7 +87,7 @@ void sms_analyzeFrame(int iCurrentFrame, SMS_AnalParams *pAnalParams, sfloat fRe /* find a reference harmonic */ if(pCurrentFrame->nPeaks > 0 && (pAnalParams->iFormat == SMS_FORMAT_H || pAnalParams->iFormat == SMS_FORMAT_HP)) - pCurrentFrame->fFundamental = sms_harmDetection(pAnalParams->nTracks, pCurrentFrame->pSpectralPeaks, + pCurrentFrame->fFundamental = sms_harmDetection(pAnalParams->maxPeaks, pCurrentFrame->pSpectralPeaks, fRefFundamental, pAnalParams->iRefHarmonic, pAnalParams->fLowestFundamental, pAnalParams->fHighestFundamental, pAnalParams->iSoundType, pAnalParams->fMinRefHarmMag, diff --git a/sms/harmDetection.c b/sms/harmDetection.c index 1612fc8..a2be13b 100644 --- a/sms/harmDetection.c +++ b/sms/harmDetection.c @@ -24,14 +24,11 @@ #include "sms.h" -#define N_FUND_HARM 6 /*!< number of harmonics to use for fundamental - detection */ -#define N_HARM_PEAKS 4 /*!< number of peaks to check as possible ref - harmonics */ -#define FREQ_DEV_THRES .07 /*!< threshold for deviation from perfect - harmonics */ -#define MAG_PERC_THRES .6 /*!< threshold for magnitude of harmonics - with respect to the total magnitude */ +#define N_FUND_HARM 6 /*!< number of harmonics to use for fundamental detection */ +#define N_HARM_PEAKS 4 /*!< number of peaks to check as possible ref harmonics */ +#define FREQ_DEV_THRES .07 /*!< threshold for deviation from perfect harmonics */ +#define MAG_PERC_THRES .6 /*!< threshold for magnitude of harmonics + with respect to the total magnitude */ #define HARM_RATIO_THRES .8 /*!< threshold for percentage of harmonics found */ /*! \brief get closest peak to a given harmonic of the possible fundamental @@ -43,33 +40,39 @@ * \param iRefHarmonic reference harmonic number * \return the number of the closest peak or -1 if not found */ -static int GetClosestPeak (int iPeakCandidate, int nHarm, SMS_Peak *pSpectralPeaks, - int *pICurrentPeak, int iRefHarmonic) +static int GetClosestPeak(int iPeakCandidate, int nHarm, SMS_Peak *pSpectralPeaks, + int *pICurrentPeak, int iRefHarmonic, int maxPeaks) { - int iBestPeak = *pICurrentPeak + 1, iNextPeak; + int iBestPeak = *pICurrentPeak + 1; + int iNextPeak = iBestPeak + 1; + + if((iBestPeak >= maxPeaks) || (iNextPeak >= maxPeaks)) + return -1; + sfloat fBestPeakFreq = pSpectralPeaks[iBestPeak].fFreq, - fHarmFreq = (1 + nHarm) * pSpectralPeaks[iPeakCandidate].fFreq / iRefHarmonic, - fMinDistance = fabs(fHarmFreq - fBestPeakFreq), - fMaxPeakDev = .5 * fHarmFreq / (nHarm + 1), fDistance; + fHarmFreq = (1 + nHarm) * pSpectralPeaks[iPeakCandidate].fFreq / iRefHarmonic, + fMinDistance = fabs(fHarmFreq - fBestPeakFreq), + fMaxPeakDev = .5 * fHarmFreq / (nHarm + 1), + fDistance = 0.0; - iNextPeak = iBestPeak + 1; fDistance = fabs(fHarmFreq - pSpectralPeaks[iNextPeak].fFreq); - while (fDistance < fMinDistance) + while((fDistance < fMinDistance) && (iNextPeak < maxPeaks - 1)) { iBestPeak = iNextPeak; fMinDistance = fDistance; iNextPeak++; - fDistance = fabs (fHarmFreq - pSpectralPeaks[iNextPeak].fFreq); + 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); + if(fabs(fBestPeakFreq - fHarmFreq) > fMaxPeakDev) + return -1; *pICurrentPeak = iBestPeak; - return (iBestPeak); + return iBestPeak; } /*! \brief checks if peak is substantial @@ -84,35 +87,35 @@ static int GetClosestPeak (int iPeakCandidate, int nHarm, SMS_Peak *pSpectralPea * \param fRefHarmMagDiffFromMax value to judge the peak based on the difference of its magnitude compared to the reference * \return 1 if big peak, -1 if too small , otherwise return 0 */ -static int ComparePeak (sfloat fRefHarmMag, SMS_Peak *pSpectralPeaks, int nCand, - sfloat fRefHarmMagDiffFromMax) +static int ComparePeak(sfloat fRefHarmMag, SMS_Peak *pSpectralPeaks, int nCand, + sfloat fRefHarmMagDiffFromMax, int maxPeaks) { int iPeak; sfloat fMag = 0; /* if peak is very large take it as possible fundamental */ - if (nCand == 0 && - fRefHarmMag > 80.) - return (1); + 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 < - fRefHarmMagDiffFromMax) - return (-1); + for(iPeak = 0; (iPeak < N_FUND_HARM) && (iPeak < maxPeaks); iPeak++) + { + if(pSpectralPeaks[iPeak].fMag > 0 && + fRefHarmMag - pSpectralPeaks[iPeak].fMag < - fRefHarmMagDiffFromMax) + return -1; + } /* if it is much bigger than rest take it */ - for (iPeak = 0; iPeak < N_FUND_HARM; iPeak++) + for(iPeak = 0; (iPeak < N_FUND_HARM) && (iPeak < maxPeaks); iPeak++) { fMag = pSpectralPeaks[iPeak].fMag; - if (fMag <= 0 || - ((fMag != fRefHarmMag) && - (nCand > 0) && (fRefHarmMag - fMag < 30.0)) || + if(fMag <= 0 || + ((fMag != fRefHarmMag) && (nCand > 0) && (fRefHarmMag - fMag < 30.0)) || ((nCand == 0) && (fRefHarmMag - fMag < 15.0))) - return (0); + return 0; } - return (1); + return 1; } @@ -123,19 +126,19 @@ static int ComparePeak (sfloat fRefHarmMag, SMS_Peak *pSpectralPeaks, int nCand, * \param nCand location of las candidate * \return 1 if it is a harmonic, 0 if it is not */ -int CheckIfHarmonic (sfloat fFundFreq, SMS_HarmCandidate *pCHarmonic, int nCand) +static int CheckIfHarmonic(sfloat fFundFreq, SMS_HarmCandidate *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); + /* 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; } @@ -152,13 +155,25 @@ int CheckIfHarmonic (sfloat fFundFreq, SMS_HarmCandidate *pCHarmonic, int nCand) * found a really good one, return 1 if the peak is a good candidate */ -static int GoodCandidate (int iPeak, SMS_Peak *pSpectralPeaks, SMS_HarmCandidate *pCHarmonic, - int nCand, int soundType, sfloat fRefFundamental, - sfloat minRefHarmMag, sfloat refHarmMagDiffFromMax, sfloat refHarmonic) +static int GoodCandidate(int iPeak, int maxPeaks, SMS_Peak *pSpectralPeaks, + SMS_HarmCandidate *pCHarmonic, int nCand, int soundType, sfloat fRefFundamental, + sfloat minRefHarmMag, sfloat refHarmMagDiffFromMax, sfloat refHarmonic) { - sfloat 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; + sfloat fHarmFreq = 0.0, + fRefHarmFreq = 0.0, + fRefHarmMag = 0.0, + fTotalMag = 0.0, + fTotalDev = 0.0, + fTotalMaxMag = 0.0, + fAvgMag = 0.0, + fAvgDev = 0.0, + fHarmRatio = 0.0; + int iHarm = 0, + iChosenPeak = 0, + iPeakComp = 0, + iCurrentPeak = 0, + nGoodHarm = 0, + i = 0; fRefHarmFreq = fHarmFreq = pSpectralPeaks[iPeak].fFreq; @@ -167,79 +182,73 @@ static int GoodCandidate (int iPeak, SMS_Peak *pSpectralPeaks, SMS_HarmCandidate fTotalMag = fRefHarmMag; /* check if magnitude is big enough */ - /*! \bug sfloat comparison to 0 */ - if (((fRefFundamental > 0) && - (fRefHarmMag < minRefHarmMag - 10)) || - ((fRefFundamental <= 0) && - (fRefHarmMag < minRefHarmMag))) - return (-1); + /*! \bug sfloat comparison to 0 */ + if(((fRefFundamental > 0) && (fRefHarmMag < minRefHarmMag - 10)) || + ((fRefFundamental <= 0) && (fRefHarmMag < minRefHarmMag))) + return -1; /* check that it is not a harmonic of a previous candidate */ - if (nCand > 0 && - CheckIfHarmonic (fRefHarmFreq / refHarmonic, pCHarmonic, - nCand)) - return (-1); + if(nCand > 0 && + CheckIfHarmonic(fRefHarmFreq / refHarmonic, pCHarmonic, nCand)) + return -1; /* check if it is very big or very small */ - iPeakComp = ComparePeak (fRefHarmMag, pSpectralPeaks, nCand, refHarmMagDiffFromMax); + iPeakComp = ComparePeak(fRefHarmMag, pSpectralPeaks, nCand, refHarmMagDiffFromMax, maxPeaks); + /* too small */ - if (iPeakComp == -1) - return (-1); + if(iPeakComp == -1) + return -1; /* very big */ - else if (iPeakComp == 1) + 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); + return -2; } /* get a weight on the peak by comparing its harmonic series */ /* with the existing peaks */ - if (soundType != SMS_SOUND_TYPE_NOTE) + if(soundType != SMS_SOUND_TYPE_NOTE) { fHarmFreq = fRefHarmFreq; iCurrentPeak = iPeak; nGoodHarm = 0; - for (iHarm = refHarmonic; iHarm < N_FUND_HARM; iHarm++) + for(iHarm = refHarmonic; (iHarm < N_FUND_HARM) && (iHarm < maxPeaks); iHarm++) { fHarmFreq += fRefHarmFreq / refHarmonic; iChosenPeak = GetClosestPeak(iPeak, iHarm, pSpectralPeaks, - &iCurrentPeak, refHarmonic); - if (iChosenPeak > 0) + &iCurrentPeak, refHarmonic, + maxPeaks); + if(iChosenPeak > 0) { - fTotalDev += - fabs(fHarmFreq - pSpectralPeaks[iChosenPeak].fFreq) / - fHarmFreq; + fTotalDev += fabs(fHarmFreq - pSpectralPeaks[iChosenPeak].fFreq) / + fHarmFreq; fTotalMag += pSpectralPeaks[iChosenPeak].fMag; nGoodHarm++; } } - for (i = 0; i <= iCurrentPeak; i++) + for(i = 0; i <= iCurrentPeak; i++) fTotalMaxMag += pSpectralPeaks[i].fMag; fAvgDev = fTotalDev / (iHarm + 1); fAvgMag = fTotalMag / fTotalMaxMag; - fHarmRatio = (sfloat) nGoodHarm / (N_FUND_HARM - 1); - - } + fHarmRatio = (sfloat)nGoodHarm / (N_FUND_HARM - 1); - if (soundType != SMS_SOUND_TYPE_NOTE) - { - if (fRefFundamental > 0) + if(fRefFundamental > 0) { if(fAvgDev > FREQ_DEV_THRES || fAvgMag < MAG_PERC_THRES - .1 || fHarmRatio < HARM_RATIO_THRES - .1) - return (-1); + return -1; } else { - if (fAvgDev > FREQ_DEV_THRES || fAvgMag < MAG_PERC_THRES || - fHarmRatio < HARM_RATIO_THRES) - return (-1); + if(fAvgDev > FREQ_DEV_THRES || fAvgMag < MAG_PERC_THRES || + fHarmRatio < HARM_RATIO_THRES) + return -1; } } @@ -249,7 +258,7 @@ static int GoodCandidate (int iPeak, SMS_Peak *pSpectralPeaks, SMS_HarmCandidate pCHarmonic[nCand].fFreqDev = fAvgDev; pCHarmonic[nCand].fHarmRatio = fHarmRatio; - return (1); + return 1; } /*! \brief choose the best fundamental out of all the candidates @@ -260,52 +269,49 @@ static int GoodCandidate (int iPeak, SMS_Peak *pSpectralPeaks, SMS_HarmCandidate * \param fPrevFund reference fundamental * \return the integer number of the best candidate */ -static int GetBestCandidate (SMS_HarmCandidate *pCHarmonic, - int iRefHarmonic, int nGoodPeaks, sfloat fPrevFund) +static int GetBestCandidate(SMS_HarmCandidate *pCHarmonic, + int iRefHarmonic, int nGoodPeaks, sfloat fPrevFund) { int iBestCandidate = 0, iPeak; sfloat fBestFreq, fHarmFreq, fDev; /* if a fundamental existed in previous frame take the closest candidate */ - if (fPrevFund > 0) - for (iPeak = 1; iPeak < nGoodPeaks; iPeak++) + if(fPrevFund > 0) + { + for(iPeak = 1; iPeak < nGoodPeaks; iPeak++) { - if (fabs (fPrevFund - pCHarmonic[iPeak].fFreq / iRefHarmonic) < - fabs(fPrevFund - pCHarmonic[iBestCandidate].fFreq / iRefHarmonic)) + if(fabs(fPrevFund - pCHarmonic[iPeak].fFreq / iRefHarmonic) < + fabs(fPrevFund - pCHarmonic[iBestCandidate].fFreq / iRefHarmonic)) iBestCandidate = iPeak; } + } else + { /* try to find the best candidate */ - for (iPeak = 1; iPeak < nGoodPeaks; iPeak++) + for(iPeak = 1; iPeak < nGoodPeaks; iPeak++) { fBestFreq = pCHarmonic[iBestCandidate].fFreq / iRefHarmonic; - fHarmFreq = fBestFreq * - floor (.5 + - (pCHarmonic[iPeak].fFreq / iRefHarmonic) / - fBestFreq); - fDev = fabs (fHarmFreq - (pCHarmonic[iPeak].fFreq / - iRefHarmonic)) / fHarmFreq; + fHarmFreq = fBestFreq * floor(.5 + + (pCHarmonic[iPeak].fFreq / iRefHarmonic) / + fBestFreq); + fDev = fabs(fHarmFreq - (pCHarmonic[iPeak].fFreq / iRefHarmonic)) / fHarmFreq; - /* if candidate is far from harmonic from best candidate and */ - /* bigger, take it */ - if (fDev > .2 && - pCHarmonic[iPeak].fMag > - pCHarmonic[iBestCandidate].fMag) + /* 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) + 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) + else if(pCHarmonic[iPeak].fFreqDev < pCHarmonic[iBestCandidate].fFreqDev && + pCHarmonic[iPeak].fMagPerc > pCHarmonic[iBestCandidate].fMagPerc && + pCHarmonic[iPeak].fMag > pCHarmonic[iBestCandidate].fMag) iBestCandidate = iPeak; } - return (iBestCandidate); + } + return iBestCandidate; } /*! \brief main harmonic detection function @@ -353,7 +359,7 @@ sfloat sms_harmDetection(int numPeaks, SMS_Peak* spectralPeaks, sfloat refFundam fabs(peakFreq - (refHarmonic * refFundamental)) / refFundamental > .5) continue; - iCandidate = GoodCandidate(iPeak, spectralPeaks, pCHarmonic, + iCandidate = GoodCandidate(iPeak, numPeaks, spectralPeaks, pCHarmonic, nGoodPeaks, soundType, refFundamental, minRefHarmMag, refHarmMagDiffFromMax, refHarmonic); diff --git a/sms/peakContinuation.c b/sms/peakContinuation.c index 5e6426b..b61d14e 100644 --- a/sms/peakContinuation.c +++ b/sms/peakContinuation.c @@ -24,13 +24,14 @@ #include "sms.h" -/*! diferent status of guide */ +/*! guide states */ #define GUIDE_BEG -2 #define GUIDE_DEAD -1 #define GUIDE_ACTIVE 0 -#define MAX_CONT_CANDIDATES 5 /*!< maximum number of peak continuation - candidates */ +/*!< 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 @@ -40,20 +41,20 @@ * \param fFreqDev maximum deviation from guide * \return peak number or -1 if nothing is good */ -int GetNextClosestPeak(sfloat fGuideFreq, sfloat *pFFreqDistance, - SMS_Peak *pSpectralPeaks, SMS_AnalParams *pAnalParams, - sfloat fFreqDev) +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(pSpectralPeaks[iInitialPeak].fFreq <= 0) - iInitialPeak = 0; - 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)) { @@ -67,18 +68,15 @@ int GetNextClosestPeak(sfloat fGuideFreq, sfloat *pFFreqDistance, else { while(floor(fLowDistance) >= floor(*pFFreqDistance) && - iInitialPeak < pAnalParams->maxPeaks) + iInitialPeak < (pAnalParams->maxPeaks-1)) { iInitialPeak++; - /* TODO: is this really the correct behaviour? Will ignore - * iInitialPeak values of maxPeaks */ - if((fFreq = pSpectralPeaks[iInitialPeak].fFreq) == 0 || - (iInitialPeak == pAnalParams->maxPeaks)) - return -1; - + if((fFreq = pSpectralPeaks[iInitialPeak].fFreq) == 0) + return -1; fLowDistance = fGuideFreq - fFreq; } - iInitialPeak--; + if(iInitialPeak > 0) + iInitialPeak--; fLowDistance = fGuideFreq - pSpectralPeaks[iInitialPeak].fFreq; } @@ -92,7 +90,7 @@ int GetNextClosestPeak(sfloat fGuideFreq, sfloat *pFFreqDistance, iHighPeak = iInitialPeak; fHighDistance = fGuideFreq - pSpectralPeaks[iHighPeak].fFreq; while(floor(fHighDistance) >= floor(-*pFFreqDistance) && - iHighPeak < pAnalParams->maxPeaks) + iHighPeak < (pAnalParams->maxPeaks - 1)) { iHighPeak++; if((fFreq = pSpectralPeaks[iHighPeak].fFreq) == 0) @@ -107,21 +105,21 @@ int GetNextClosestPeak(sfloat fGuideFreq, sfloat *pFFreqDistance, iHighPeak = -1; /* chose between the two extrema */ - if (iHighPeak >= 0 && iLowPeak >= 0) + if(iHighPeak >= 0 && iLowPeak >= 0) { - if (fabs(fHighDistance) > fLowDistance) + if(fabs(fHighDistance) > fLowDistance) iChosenPeak = iLowPeak; else iChosenPeak = iHighPeak; } - else if (iHighPeak < 0 && iLowPeak >= 0) + else if(iHighPeak < 0 && iLowPeak >= 0) iChosenPeak = iLowPeak; - else if (iHighPeak >= 0 && iLowPeak < 0) + else if(iHighPeak >= 0 && iLowPeak < 0) iChosenPeak = iHighPeak; else return -1; - *pFFreqDistance = fabs (fGuideFreq - pSpectralPeaks[iChosenPeak].fFreq); + *pFFreqDistance = fabs(fGuideFreq - pSpectralPeaks[iChosenPeak].fFreq); return iChosenPeak; } @@ -344,14 +344,21 @@ int sms_initAnalysis(SMS_AnalParams *pAnalParams) sms_error("Could not allocate memory for guides"); return -1; } + /* initial guide values */ - if(pAnalParams->iFormat == SMS_FORMAT_H || - pAnalParams->iFormat == SMS_FORMAT_HP) + for (i = 0; i < pAnalParams->nGuides; i++) { - for (i = 0; i < pAnalParams->nGuides; i++) + if(pAnalParams->iFormat == SMS_FORMAT_H || pAnalParams->iFormat == SMS_FORMAT_HP) { pAnalParams->guides[i].fFreq = pAnalParams->fDefaultFundamental * (i + 1); } + else + { + pAnalParams->guides[i].fFreq = 0.0; + } + pAnalParams->guides[i].fMag = 0.0; + pAnalParams->guides[i].iPeakChosen = -1; + pAnalParams->guides[i].iStatus = 0; } /* stochastic analysis */ @@ -671,7 +678,7 @@ int sms_initFrame(int iCurrentFrame, SMS_AnalParams *pAnalParams, int sizeWindow pAnalParams->ppFrames[iCurrentFrame-1]->iFrameSample + pAnalParams->sizeHop; /* check for end of sound */ - if ((pAnalParams->ppFrames[iCurrentFrame]->iFrameSample + (sizeWindow+1)/2) >= pAnalParams->iSizeSound + if((pAnalParams->ppFrames[iCurrentFrame]->iFrameSample + (sizeWindow+1)/2) >= pAnalParams->iSizeSound && pAnalParams->iSizeSound > 0) { pAnalParams->ppFrames[iCurrentFrame]->iFrameNum = -1; diff --git a/tests/sms.py b/tests/sms.py index 5c8c2c0..d30686b 100644 --- a/tests/sms.py +++ b/tests/sms.py @@ -19,6 +19,7 @@ from simpl import simplsms import pysms import numpy as np from scipy.io.wavfile import read +from nose.tools import assert_almost_equals import unittest class TestSimplSMS(unittest.TestCase): @@ -125,6 +126,7 @@ class TestSimplSMS(unittest.TestCase): pd = simpl.SMSPeakDetection() pd.hop_size = self.hop_size + pd.max_peaks = self.max_peaks current_frame = 0 sample_offset = 0 @@ -135,6 +137,316 @@ class TestSimplSMS(unittest.TestCase): sample_offset += pd.frame_size current_frame += 1 + def test_sms_analyze(self): + """test_sms_analyzebt43lztar + Make sure that the simplsms.sms_analyze function does the same thing + as the sms_analyze function from libsms.""" + audio, sampling_rate = self.get_audio() + + pysms.sms_init() + snd_header = pysms.SMS_SndHeader() + # Try to open the input file to fill snd_header + if(pysms.sms_openSF(self.input_file, snd_header)): + raise NameError("error opening sound file: " + pysms.sms_errorString()) + analysis_params = self.pysms_analysis_params(sampling_rate) + analysis_params.iMaxDelayFrames = self.num_frames + 1 + analysis_params.analDelay = 0 + analysis_params.minGoodFrames = 1 + if pysms.sms_initAnalysis(analysis_params, snd_header) != 0: + raise Exception("Error allocating memory for analysis_params") + analysis_params.nFrames = self.num_frames + analysis_params.iSizeSound = self.num_samples + analysis_params.peakParams.iMaxPeaks = self.max_peaks + sms_header = pysms.SMS_Header() + pysms.sms_fillHeader(sms_header, analysis_params, "pysms") + + sample_offset = 0 + size_new_data = 0 + current_frame = 0 + sms_partials = [] + live_partials = [None for i in range(self.max_peaks)] + do_analysis = True + + while do_analysis and (current_frame < self.num_frames): + sample_offset += size_new_data + size_new_data = analysis_params.sizeNextRead + # convert frame to floats for libsms + frame = audio[sample_offset:sample_offset + size_new_data] + frame = np.array(frame, dtype=np.float32) + analysis_data = pysms.SMS_Data() + pysms.sms_allocFrameH(sms_header, analysis_data) + status = pysms.sms_analyze(frame, analysis_data, analysis_params) + + if status == 1: + num_partials = analysis_data.nTracks + sms_freqs = np.zeros(num_partials, dtype=np.float32) + sms_amps = np.zeros(num_partials, dtype=np.float32) + sms_phases = np.zeros(num_partials, dtype=np.float32) + analysis_data.getSinFreq(sms_freqs) + analysis_data.getSinAmp(sms_amps) + analysis_data.getSinPhase(sms_phases) + # make partial objects + for i in range(num_partials): + # for each partial, if the mag is > 0, this partial is alive + if sms_amps[i] > 0: + # create a peak object + p = simpl.Peak() + p.amplitude = sms_amps[i] + p.frequency = sms_freqs[i] + p.phase = sms_phases[i] + # add this peak to the appropriate partial + if not live_partials[i]: + live_partials[i] = simpl.Partial() + live_partials[i].starting_frame = current_frame + sms_partials.append(live_partials[i]) + live_partials[i].add_peak(p) + # if the mag is 0 and this partial was alive, kill it + else: + if live_partials[i]: + live_partials[i] = None + elif status == -1: + do_analysis = False + pysms.sms_freeFrame(analysis_data) + current_frame += 1 + + pysms.sms_freeAnalysis(analysis_params) + pysms.sms_closeSF() + pysms.sms_free() + + audio, sampling_rate = self.get_audio() + simplsms.sms_init() + simpl_analysis_params = self.simplsms_analysis_params(sampling_rate) + simpl_analysis_params.iMaxDelayFrames = self.num_frames + 1 + if simplsms.sms_initAnalysis(simpl_analysis_params) != 0: + raise Exception("Error allocating memory for analysis_params") + simpl_analysis_params.nFrames = self.num_frames + simpl_analysis_params.iSizeSound = self.num_samples + simpl_sms_header = simplsms.SMS_Header() + simplsms.sms_fillHeader(simpl_sms_header, simpl_analysis_params, "simplsms") + + sample_offset = 0 + size_new_data = 0 + current_frame = 0 + simplsms_partials = [] + live_partials = [None for i in range(self.max_peaks)] + do_analysis = True + + while do_analysis and (current_frame < self.num_frames): + sample_offset += size_new_data + size_new_data = simpl_analysis_params.sizeNextRead + frame = audio[sample_offset:sample_offset + size_new_data] + analysis_data = simplsms.SMS_Data() + simplsms.sms_allocFrameH(simpl_sms_header, analysis_data) + status = simplsms.sms_analyze(frame, analysis_data, simpl_analysis_params) + + if status == 1: + num_partials = analysis_data.nTracks + freqs = simpl.zeros(num_partials) + amps = simpl.zeros(num_partials) + phases = simpl.zeros(num_partials) + analysis_data.getSinFreq(freqs) + analysis_data.getSinAmp(amps) + analysis_data.getSinPhase(phases) + # make partial objects + for i in range(num_partials): + # for each partial, if the mag is > 0, this partial is alive + if amps[i] > 0: + # create a peak object + p = simpl.Peak() + p.amplitude = amps[i] + p.frequency = freqs[i] + p.phase = phases[i] + # add this peak to the appropriate partial + if not live_partials[i]: + live_partials[i] = simpl.Partial() + live_partials[i].starting_frame = current_frame + simplsms_partials.append(live_partials[i]) + live_partials[i].add_peak(p) + # if the mag is 0 and this partial was alive, kill it + else: + if live_partials[i]: + live_partials[i] = None + elif status == -1: + do_analysis = False + simplsms.sms_freeFrame(analysis_data) + current_frame += 1 + + simplsms.sms_freeAnalysis(simpl_analysis_params) + simplsms.sms_free() + + # make sure both have the same number of partials + self.assertEquals(len(sms_partials), len(simplsms_partials)) + + # make sure each partial is the same + for i in range(len(sms_partials)): + self.assertEquals(sms_partials[i].get_length(), simplsms_partials[i].get_length()) + for peak_number in range(sms_partials[i].get_length()): + self.assertAlmostEquals(sms_partials[i].peaks[peak_number].amplitude, + simplsms_partials[i].peaks[peak_number].amplitude, + places = self.FLOAT_PRECISION) + self.assertAlmostEquals(sms_partials[i].peaks[peak_number].frequency, + simplsms_partials[i].peaks[peak_number].frequency, + places = self.FLOAT_PRECISION) + self.assertAlmostEquals(sms_partials[i].peaks[peak_number].phase, + simplsms_partials[i].peaks[peak_number].phase, + places = self.FLOAT_PRECISION) + + def test_multi_sms_peak_detection(self): + """test_multi_sms_peak_detection + Test that running the same peak detection process twice in a row + produces the same results each time. This makes sure that results + are independent, and also helps to highlight any memory errors.""" + audio, sampling_rate = self.get_audio() + simplsms.sms_init() + analysis_params = self.simplsms_analysis_params(sampling_rate) + analysis_params.iMaxDelayFrames = self.num_frames + 1 + if simplsms.sms_initAnalysis(analysis_params) != 0: + raise Exception("Error allocating memory for analysis_params") + analysis_params.nFrames = self.num_frames + sms_header = simplsms.SMS_Header() + simplsms.sms_fillHeader(sms_header, analysis_params, "simplsms") + + sample_offset = 0 + size_new_data = 0 + current_frame = 0 + peaks1 = [] + + while current_frame < self.num_frames: + sample_offset += size_new_data + size_new_data = analysis_params.sizeNextRead + frame = audio[sample_offset:sample_offset + size_new_data] + analysis_data = simplsms.SMS_Data() + simplsms.sms_allocFrameH(sms_header, analysis_data) + status = simplsms.sms_analyze(frame, analysis_data, analysis_params) + # as the no. of frames of delay is > num_frames, sms_analyze should + # never get around to performing partial tracking, and so the return + # value should be 0 + self.assertEquals(status, 0) + num_peaks = analysis_data.nTracks + frame_peaks = [] + simplsms_freqs = simpl.zeros(num_peaks) + simplsms_amps = simpl.zeros(num_peaks) + simplsms_phases = simpl.zeros(num_peaks) + analysis_data.getSinFreq(simplsms_freqs) + analysis_data.getSinAmp(simplsms_amps) + analysis_data.getSinPhase(simplsms_phases) + for i in range(num_peaks): + if simplsms_amps[i]: + p = simpl.Peak() + # convert amplitude back to linear + p.amplitude = 10**(simplsms_amps[i]/20.0) + p.frequency = simplsms_freqs[i] + p.phase = simplsms_phases[i] + frame_peaks.append(p) + peaks1.append(frame_peaks) + pysms.sms_freeFrame(analysis_data) + current_frame += 1 + + simplsms.sms_freeAnalysis(analysis_params) + simplsms.sms_free() + + # Second run + audio, sampling_rate = self.get_audio() + simplsms.sms_init() + analysis_params = self.simplsms_analysis_params(sampling_rate) + analysis_params.iMaxDelayFrames = self.num_frames + 1 + if simplsms.sms_initAnalysis(analysis_params) != 0: + raise Exception("Error allocating memory for analysis_params") + analysis_params.nFrames = self.num_frames + sms_header = simplsms.SMS_Header() + simplsms.sms_fillHeader(sms_header, analysis_params, "simplsms") + + sample_offset = 0 + size_new_data = 0 + current_frame = 0 + peaks2 = [] + + while current_frame < self.num_frames: + sample_offset += size_new_data + size_new_data = analysis_params.sizeNextRead + frame = audio[sample_offset:sample_offset + size_new_data] + analysis_data = simplsms.SMS_Data() + simplsms.sms_allocFrameH(sms_header, analysis_data) + status = simplsms.sms_analyze(frame, analysis_data, analysis_params) + # as the no. of frames of delay is > num_frames, sms_analyze should + # never get around to performing partial tracking, and so the return + # value should be 0 + self.assertEquals(status, 0) + num_peaks = analysis_data.nTracks + frame_peaks = [] + simplsms_freqs = simpl.zeros(num_peaks) + simplsms_amps = simpl.zeros(num_peaks) + simplsms_phases = simpl.zeros(num_peaks) + analysis_data.getSinFreq(simplsms_freqs) + analysis_data.getSinAmp(simplsms_amps) + analysis_data.getSinPhase(simplsms_phases) + for i in range(num_peaks): + if simplsms_amps[i]: + p = simpl.Peak() + # convert amplitude back to linear + p.amplitude = 10**(simplsms_amps[i]/20.0) + p.frequency = simplsms_freqs[i] + p.phase = simplsms_phases[i] + frame_peaks.append(p) + peaks2.append(frame_peaks) + pysms.sms_freeFrame(analysis_data) + current_frame += 1 + + simplsms.sms_freeAnalysis(analysis_params) + simplsms.sms_free() + + # make sure we have the same number of frames in each run + assert len(peaks1) == len(peaks2) + for f in range(len(peaks1)): + # in each frame, make sure that we have the same number of peaks + assert len(peaks1[f]) == len(peaks2[f]) + # make sure that each peak has the same value + for p in range(len(peaks1[f])): + assert_almost_equals(peaks1[f][p].frequency, + peaks2[f][p].frequency, + self.FLOAT_PRECISION) + assert_almost_equals(peaks1[f][p].amplitude, + peaks2[f][p].amplitude, + self.FLOAT_PRECISION) + assert_almost_equals(peaks1[f][p].phase, + peaks2[f][p].phase, + self.FLOAT_PRECISION) + + def test_multi_simpl_peak_detection(self): + """test_multi_simpl_peak_detection + Test that running the simpl peak detection process twice in a row + produces the same results each time. This makes sure that results + are independent, and also helps to highlight any memory errors.""" + audio, sampling_rate = self.get_audio() + pd = simpl.SMSPeakDetection() + pd.max_peaks = self.max_peaks + pd.hop_size = self.hop_size + peaks1 = pd.find_peaks(audio)[0:self.num_frames] + del pd + # second run + audio, sampling_rate = self.get_audio() + pd = simpl.SMSPeakDetection() + pd.max_peaks = self.max_peaks + pd.hop_size = self.hop_size + peaks2 = pd.find_peaks(audio)[0:self.num_frames] + + # make sure we have the same number of frames in each run + assert len(peaks1) == len(peaks2) + for f in range(len(peaks1)): + # in each frame, make sure that we have the same number of peaks + assert len(peaks1[f]) == len(peaks2[f]) + # make sure that each peak has the same value + for p in range(len(peaks1[f])): + assert_almost_equals(peaks1[f][p].frequency, + peaks2[f][p].frequency, + self.FLOAT_PRECISION) + assert_almost_equals(peaks1[f][p].amplitude, + peaks2[f][p].amplitude, + self.FLOAT_PRECISION) + assert_almost_equals(peaks1[f][p].phase, + peaks2[f][p].phase, + self.FLOAT_PRECISION) + def test_peak_detection(self): """test_peak_detection Compare simplsms Peaks with SMS peaks. Exact peak @@ -232,34 +544,30 @@ class TestSimplSMS(unittest.TestCase): self.assertAlmostEquals(sms_peak.phase, simpl_peak.phase, places=self.FLOAT_PRECISION) - def test_sms_analyze(self): - """test_sms_analyzebt43lztar - Make sure that the simplsms.sms_analyze function does the same thing - as the sms_analyze function from libsms.""" + def test_multi_pysms_analyze(self): + """test_multi_pysms_analyze + Test that running the pysms sms_analyze function twice in a row + produces the same results each time. This makes sure that results + are independent, and also helps to highlight any memory errors.""" audio, sampling_rate = self.get_audio() - pysms.sms_init() snd_header = pysms.SMS_SndHeader() # Try to open the input file to fill snd_header if(pysms.sms_openSF(self.input_file, snd_header)): raise NameError("error opening sound file: " + pysms.sms_errorString()) analysis_params = self.pysms_analysis_params(sampling_rate) - analysis_params.iMaxDelayFrames = self.num_frames + 1 - analysis_params.analDelay = 0 - analysis_params.minGoodFrames = 1 if pysms.sms_initAnalysis(analysis_params, snd_header) != 0: raise Exception("Error allocating memory for analysis_params") - analysis_params.nFrames = self.num_frames analysis_params.iSizeSound = self.num_samples - analysis_params.peakParams.iMaxPeaks = self.max_peaks sms_header = pysms.SMS_Header() pysms.sms_fillHeader(sms_header, analysis_params, "pysms") sample_offset = 0 size_new_data = 0 current_frame = 0 - sms_partials = [] - live_partials = [None for i in range(self.max_peaks)] + freqs1 = [] + amps1 = [] + phases1 = [] do_analysis = True while do_analysis and (current_frame < self.num_frames): @@ -271,34 +579,17 @@ class TestSimplSMS(unittest.TestCase): analysis_data = pysms.SMS_Data() pysms.sms_allocFrameH(sms_header, analysis_data) status = pysms.sms_analyze(frame, analysis_data, analysis_params) - if status == 1: num_partials = analysis_data.nTracks - sms_freqs = np.zeros(num_partials, dtype=np.float32) - sms_amps = np.zeros(num_partials, dtype=np.float32) - sms_phases = np.zeros(num_partials, dtype=np.float32) - analysis_data.getSinFreq(sms_freqs) - analysis_data.getSinAmp(sms_amps) - analysis_data.getSinPhase(sms_phases) - # make partial objects - for i in range(num_partials): - # for each partial, if the mag is > 0, this partial is alive - if sms_amps[i] > 0: - # create a peak object - p = simpl.Peak() - p.amplitude = sms_amps[i] - p.frequency = sms_freqs[i] - p.phase = sms_phases[i] - # add this peak to the appropriate partial - if not live_partials[i]: - live_partials[i] = simpl.Partial() - live_partials[i].starting_frame = current_frame - sms_partials.append(live_partials[i]) - live_partials[i].add_peak(p) - # if the mag is 0 and this partial was alive, kill it - else: - if live_partials[i]: - live_partials[i] = None + freqs = np.zeros(num_partials, dtype=np.float32) + amps = np.zeros(num_partials, dtype=np.float32) + phases = np.zeros(num_partials, dtype=np.float32) + analysis_data.getSinFreq(freqs) + analysis_data.getSinAmp(amps) + analysis_data.getSinPhase(phases) + amps1.append(amps) + freqs1.append(freqs) + phases1.append(phases) elif status == -1: do_analysis = False pysms.sms_freeFrame(analysis_data) @@ -308,89 +599,120 @@ class TestSimplSMS(unittest.TestCase): pysms.sms_closeSF() pysms.sms_free() + # second run audio, sampling_rate = self.get_audio() - simplsms.sms_init() - simpl_analysis_params = self.simplsms_analysis_params(sampling_rate) - simpl_analysis_params.iMaxDelayFrames = self.num_frames + 1 - if simplsms.sms_initAnalysis(simpl_analysis_params) != 0: + pysms.sms_init() + snd_header = pysms.SMS_SndHeader() + # Try to open the input file to fill snd_header + if(pysms.sms_openSF(self.input_file, snd_header)): + raise NameError("error opening sound file: " + pysms.sms_errorString()) + analysis_params = self.pysms_analysis_params(sampling_rate) + if pysms.sms_initAnalysis(analysis_params, snd_header) != 0: raise Exception("Error allocating memory for analysis_params") - simpl_analysis_params.nFrames = self.num_frames - simpl_analysis_params.iSizeSound = self.num_samples - simpl_sms_header = simplsms.SMS_Header() - simplsms.sms_fillHeader(simpl_sms_header, simpl_analysis_params, "simplsms") + analysis_params.iSizeSound = self.num_samples + sms_header = pysms.SMS_Header() + pysms.sms_fillHeader(sms_header, analysis_params, "pysms") sample_offset = 0 size_new_data = 0 current_frame = 0 - simplsms_partials = [] - live_partials = [None for i in range(self.max_peaks)] + freqs2 = [] + amps2 = [] + phases2 = [] do_analysis = True while do_analysis and (current_frame < self.num_frames): sample_offset += size_new_data - size_new_data = simpl_analysis_params.sizeNextRead + size_new_data = analysis_params.sizeNextRead + # convert frame to floats for libsms frame = audio[sample_offset:sample_offset + size_new_data] - analysis_data = simplsms.SMS_Data() - simplsms.sms_allocFrameH(simpl_sms_header, analysis_data) - status = simplsms.sms_analyze(frame, analysis_data, simpl_analysis_params) - + frame = np.array(frame, dtype=np.float32) + analysis_data = pysms.SMS_Data() + pysms.sms_allocFrameH(sms_header, analysis_data) + status = pysms.sms_analyze(frame, analysis_data, analysis_params) if status == 1: num_partials = analysis_data.nTracks - freqs = simpl.zeros(num_partials) - amps = simpl.zeros(num_partials) - phases = simpl.zeros(num_partials) + freqs = np.zeros(num_partials, dtype=np.float32) + amps = np.zeros(num_partials, dtype=np.float32) + phases = np.zeros(num_partials, dtype=np.float32) analysis_data.getSinFreq(freqs) analysis_data.getSinAmp(amps) analysis_data.getSinPhase(phases) - # make partial objects - for i in range(num_partials): - # for each partial, if the mag is > 0, this partial is alive - if amps[i] > 0: - # create a peak object - p = simpl.Peak() - p.amplitude = amps[i] - p.frequency = freqs[i] - p.phase = phases[i] - # add this peak to the appropriate partial - if not live_partials[i]: - live_partials[i] = simpl.Partial() - live_partials[i].starting_frame = current_frame - simplsms_partials.append(live_partials[i]) - live_partials[i].add_peak(p) - # if the mag is 0 and this partial was alive, kill it - else: - if live_partials[i]: - live_partials[i] = None + amps2.append(amps) + freqs2.append(freqs) + phases2.append(phases) elif status == -1: do_analysis = False - simplsms.sms_freeFrame(analysis_data) + pysms.sms_freeFrame(analysis_data) current_frame += 1 - simplsms.sms_freeAnalysis(simpl_analysis_params) - simplsms.sms_free() - - # make sure both have the same number of partials - self.assertEquals(len(sms_partials), len(simplsms_partials)) + pysms.sms_freeAnalysis(analysis_params) + pysms.sms_closeSF() + pysms.sms_free() - # make sure each partial is the same - for i in range(len(sms_partials)): - self.assertEquals(sms_partials[i].get_length(), simplsms_partials[i].get_length()) - for peak_number in range(sms_partials[i].get_length()): - self.assertAlmostEquals(sms_partials[i].peaks[peak_number].amplitude, - simplsms_partials[i].peaks[peak_number].amplitude, - places = self.FLOAT_PRECISION) - self.assertAlmostEquals(sms_partials[i].peaks[peak_number].frequency, - simplsms_partials[i].peaks[peak_number].frequency, - places = self.FLOAT_PRECISION) - self.assertAlmostEquals(sms_partials[i].peaks[peak_number].phase, - simplsms_partials[i].peaks[peak_number].phase, - places = self.FLOAT_PRECISION) + # make sure we have the same number of results in each run + assert len(freqs1) == len(freqs2) + assert len(amps1) == len(amps2) + assert len(phases1) == len(phases2) + + for r in range(len(freqs1)): + # in each result, make sure that we have the same number amps, freqs and phases + assert len(freqs1[r]) == len(freqs2[r]) + assert len(amps1[r]) == len(amps2[r]) + assert len(phases1[r]) == len(phases2[r]) + # make sure that each partial has the same value + for p in range(len(freqs1[r])): + assert_almost_equals(freqs1[r][p], freqs2[r][p], self.FLOAT_PRECISION) + assert_almost_equals(amps1[r][p], amps2[r][p], self.FLOAT_PRECISION) + assert_almost_equals(phases1[r][p], phases2[r][p], self.FLOAT_PRECISION) + + def test_multi_simpl_partial_tracking(self): + """test_multi_simpl_partial_tracking + Test that running the simpl peak detection process twice in a row + produces the same results each time. This makes sure that results + are independent, and also helps to highlight any memory errors.""" + audio, sampling_rate = self.get_audio() + pd = simpl.SMSPeakDetection() + pd.max_peaks = self.max_peaks + pd.hop_size = self.hop_size + peaks = pd.find_peaks(audio)[0:self.num_frames] + pt = simpl.SMSPartialTracking() + pt.max_partials = self.max_peaks + partials1 = pt.find_partials(peaks) + del pd + del pt + # second run + audio, sampling_rate = self.get_audio() + pd = simpl.SMSPeakDetection() + pd.max_peaks = self.max_peaks + pd.hop_size = self.hop_size + peaks = pd.find_peaks(audio)[0:self.num_frames] + pt = simpl.SMSPartialTracking() + pt.max_partials = self.max_peaks + partials2 = pt.find_partials(peaks) + + # make sure we have the same number of partials in each run + print len(partials1), len(partials2) + assert len(partials1) == len(partials2) + for p in range(len(partials1)): + # make sure each partial is the same length + assert partials1[p].get_length() == partials2[p].get_length() + # make sure that the peaks in each partial have the same values + for i in range(partials1[p].get_length()): + assert_almost_equals(partials1[p].peaks[i].frequency, + partials2[p].peaks[i].frequency, + self.FLOAT_PRECISION) + assert_almost_equals(partials1[p].peaks[i].amplitude, + partials2[p].peaks[i].amplitude, + self.FLOAT_PRECISION) + assert_almost_equals(partials1[p].peaks[i].phase, + partials2[p].peaks[i].phase, + self.FLOAT_PRECISION) def test_partial_tracking(self): """test_partial_tracking Compare pysms Partials with SMS partials.""" audio, sampling_rate = self.get_audio() - pysms.sms_init() snd_header = pysms.SMS_SndHeader() # Try to open the input file to fill snd_header @@ -464,10 +786,10 @@ class TestSimplSMS(unittest.TestCase): pt.max_partials = self.max_peaks partials = pt.find_partials(peaks) - import debug - debug.print_partials(sms_partials) - print - debug.print_partials(partials) + #import debug + #debug.print_partials(sms_partials) + #print + #debug.print_partials(partials) #raise Exception("ok") # make sure both have the same number of partials @@ -779,7 +1101,10 @@ if __name__ == "__main__": # useful for debugging, particularly with GDB import nose argv = [__file__, - #__file__ + ":TestSimplSMS.test_partial_tracking", + #__file__ + ":TestSimplSMS.test_multi_sms_peak_detection", + #__file__ + ":TestSimplSMS.test_multi_simpl_peak_detection", + #__file__ + ":TestSimplSMS.test_multi_pysms_analyze", + #__file__ + ":TestSimplSMS.test_multi_simpl_partial_tracking", __file__ + ":TestSimplSMS.test_partial_tracking"] nose.run(argv=argv) |