ftp.nice.ch/pub/next/unix/audio/sms.N.bs.tar.gz#/sms/library/peakDetection.c

This is peakDetection.c in view mode; [Download] [Up]

#include "../sms.h"

/* function used for the parabolic interpolation of the spectral peaks
 * it performs the interpolation in a log scale                       
 * stores the location in pFDiffFromMax and returns the peak height 
 *
 * float fMaxVal;        value of max bin 
 * float fLeftBinVal;    value for left bin
 * float fRightBinVal;   value for right bin
 * float *pFDiffFromMax; location of the tip as the difference from the
 *                         top bin 
 */
static float PeakInterpolation (float fMaxVal, float fLeftBinVal, 
                                float fRightBinVal, float *pFDiffFromMax)
{
	/* get the location of the tip of the parabola */
	*pFDiffFromMax = (.5 * (fLeftBinVal - fRightBinVal) /
		(fLeftBinVal - (2*fMaxVal) + fRightBinVal));
	/* return the value at the tip */
	return(fMaxVal - (.25 * (fLeftBinVal - fRightBinVal) * 
	                  *pFDiffFromMax));
}

/* function to detect the next local maximum in the spectrum
 * store the value in pFMaxVal and returns the bin 
 * location of the maximum 
 *                                      
 * float *pFMagSpectrum;   magnitude spectrum 
 * int iHighBinBound;      highest bin to search
 * int *pICurrentLoc;      current bin location
 * float *pFMaxVal;        value of the maximum found
 */
static int FindNextMax (float *pFMagSpectrum, int iHighBinBound, 
                        int *pICurrentLoc, float *pFMaxVal, 
                        ANAL_PARAMS analParams)
{
	int iCurrentBin = *pICurrentLoc;
	float fPrevVal = pFMagSpectrum[iCurrentBin - 1],
		fCurrentVal = pFMagSpectrum[iCurrentBin],
		fNextVal = (iCurrentBin >= iHighBinBound) 
			? 0 : pFMagSpectrum[iCurrentBin + 1];
  
	/* try to find a local maximum */
	while (iCurrentBin <= iHighBinBound)
	{
		if (fCurrentVal > analParams.fMinPeakMag &&
		   fCurrentVal >= fPrevVal &&
		   fCurrentVal >= fNextVal)
			break;
		iCurrentBin++;
		fPrevVal = fCurrentVal;
		fCurrentVal = fNextVal;
		fNextVal = pFMagSpectrum [1+iCurrentBin];
	}
	/* save the current location, value of maximum and return */
	/*    location of max */
	*pICurrentLoc = iCurrentBin + 1;
	*pFMaxVal = fCurrentVal;
	return(iCurrentBin);
}

/* function to detect the next spectral peak, returns 1 if found, 0 if not  
 *                           
 * float *pFMagSpectrum;    magnitude spectrum 
 * int iHighBinBound;       highest bin to search
 * int *pICurrentLoc;       current bin location
 * float *pFPeakMag;        magnitude value of peak
 * float *pFPeakLoc;        location of peak
 */
static int FindNextPeak (float *pFMagSpectrum, int iHighBinBound, 
                         int *pICurrentLoc, float *pFPeakMag, float *pFPeakLoc,
                         ANAL_PARAMS analParams)
{
	int iMaxBin = 0;		/* location of the local maximum */
	float fMaxVal = 0;
  
	/* keep trying to find a good peak while inside the freq range */
	while ((iMaxBin = FindNextMax(pFMagSpectrum, iHighBinBound, 
	       pICurrentLoc, &fMaxVal, analParams)) 
	       <= iHighBinBound)
	{
		/* get the neighboring samples */
		float fDiffFromMax = 0;
		float fLeftBinVal = pFMagSpectrum[iMaxBin - 1];
		float fRightBinVal = pFMagSpectrum[iMaxBin + 1];
		if (fLeftBinVal <= 0 || fRightBinVal <= 0)
			continue;
		/* interpolate the spectral samples to obtain
		   a more accurate magnitude and freq */
		*pFPeakMag = PeakInterpolation (fMaxVal, fLeftBinVal,
		                                fRightBinVal, &fDiffFromMax);
		*pFPeakLoc = iMaxBin + fDiffFromMax;
		return (1);
	}
	/* if it does not find a peak return 0 */
	return (0);
}

/* get the corresponding phase value for a given peak
 * performs linear interpolation for a more accurate phase
 *    returns the phase value                             
 * float *pAPhaSpectrum;     phase spectrum
 * float fPeakLoc;           location of peak
 */
static float GetPhaseVal (float *pAPhaSpectrum, float fPeakLoc)
{
	int bin = (int) fPeakLoc;
	float fFraction = fPeakLoc - bin,
		fLeftPha = pAPhaSpectrum[bin],
		fRightPha = pAPhaSpectrum[bin+1];
  
	/* check for phase wrapping */
	if (fLeftPha - fRightPha > 1.5 * PI)
		fRightPha += TWO_PI;
	else if (fRightPha - fLeftPha > 1.5 * PI)
		fLeftPha += TWO_PI;
  
	/* return interpolated phase */
	return (fLeftPha + fFraction * (fRightPha - fLeftPha));
}


/* function to find the prominent spectral peaks on a dB spectrum
 * returns the number of peaks found
 *
 * float *pFMagSpectrum;     pointer to power spectrum
 * float *pAPhaSpectrum;     pointer to phase spectrum
 * int sizeMag;              size of magnitude spectrum
 * PEAK *pSpectralPeaks;	 pointer to array of peaks
 * float fSamplingRate;      sampling rate of sound
 */
int PeakDetection (float *pFMagSpectrum, float *pAPhaSpectrum, int sizeMag, 
                   int sizeWindow, PEAK *pSpectralPeaks, 
                   ANAL_PARAMS analParams)
{
	int iPeak = 0;		/* index for spectral search */
	int sizeFft = sizeMag * 2;
	int iCurrentLoc = MAX (2, sizeFft * analParams.fLowestFundamental / 
	                          analParams.fSamplingRate); 
	int iHighestBin  = MIN (sizeMag-1, 
	                        sizeFft * analParams.fHighestFreq / 
	                        analParams.fSamplingRate);
  	float fPeakMag = 0;		/* magnitude of peak */
	float fPeakLoc = 0;		/* location of peak */

	/* clear peak structure */
	memset (pSpectralPeaks, 0, MAX_NUM_PEAKS * sizeof(PEAK));
  
	/* find peaks */
	iPeak = 0;
	while ((iPeak < MAX_NUM_PEAKS) &&
	       (FindNextPeak(pFMagSpectrum, iHighestBin,
	                     &iCurrentLoc, &fPeakMag, &fPeakLoc, analParams)
	        == 1))
	{
		/* store peak values */
		pSpectralPeaks[iPeak].fFreq = analParams.fSamplingRate * fPeakLoc / 
			sizeFft;
		pSpectralPeaks[iPeak].fMag = fPeakMag;
		pSpectralPeaks[iPeak].fPhase = GetPhaseVal(pAPhaSpectrum, fPeakLoc);
		iPeak++;
	}
	/* return the number of peaks found */
	return (iPeak);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.