summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sms/analysis.c2
-rw-r--r--sms/harmDetection.c232
-rw-r--r--sms/peakContinuation.c44
-rw-r--r--sms/sms.c15
-rw-r--r--tests/sms.py525
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;
}
diff --git a/sms/sms.c b/sms/sms.c
index 78ccb6b..3a02b4b 100644
--- a/sms/sms.c
+++ b/sms/sms.c
@@ -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)