diff options
Diffstat (limited to 'src/sms/spectralApprox.c')
-rw-r--r-- | src/sms/spectralApprox.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/src/sms/spectralApprox.c b/src/sms/spectralApprox.c new file mode 100644 index 0000000..b1f65ef --- /dev/null +++ b/src/sms/spectralApprox.c @@ -0,0 +1,152 @@ +/* + * 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 spectralApprox.c + * \brief line segment approximation of a magnitude spectrum + */ +#include "sms.h" + +/*! \brief approximate a magnitude spectrum + * First downsampling using local maxima and then upsampling using linear + * interpolation. The output spectrum doesn't have to be the same size as + * the input one. + * + * \param pFSpec1 magnitude spectrum to approximate + * \param sizeSpec1 size of input spectrum + * \param sizeSpec1Used size of the spectrum to use + * \param pFSpec2 output envelope + * \param sizeSpec2 size of output envelope + * \param nCoefficients number of coefficients to use in approximation + * \return error code \see SMS_ERRORS + */ +int sms_spectralApprox(sfloat *pFSpec1, int sizeSpec1, int sizeSpec1Used, + sfloat *pFSpec2, int sizeSpec2, int nCoefficients, + sfloat *envelope) +{ + sfloat fHopSize, fCurrentLoc = 0, fLeft = 0, fRight = 0, fValue = 0, + fLastLocation, fSizeX, fSpec2Acum=0, fNextHop, fDeltaY; + int iFirstGood = 0, iLastSample = 0, i, j; + + /* when number of coefficients is smaller than 2 do not approximate */ + if(nCoefficients < 2) + { + for(i = 0; i < sizeSpec2; i++) + pFSpec2[i] = 1; + return SMS_OK; + } + + /* calculate the hop size */ + if(nCoefficients > sizeSpec1) + nCoefficients = sizeSpec1; + + fHopSize = (sfloat)sizeSpec1Used / nCoefficients; + + /* approximate by linear interpolation */ + if(fHopSize > 1) + { + iFirstGood = 0; + for(i = 0; i < nCoefficients; i++) + { + iLastSample = fLastLocation = fCurrentLoc + fHopSize; + iLastSample = MIN(sizeSpec1-1, iLastSample); + if(iLastSample < sizeSpec1-1) + { + fRight = pFSpec1[iLastSample] + + (pFSpec1[iLastSample+1] - pFSpec1[iLastSample]) * + (fLastLocation - iLastSample); + } + else + { + fRight = pFSpec1[iLastSample]; + } + + fValue = 0; + for(j = iFirstGood; j <= iLastSample; j++) + fValue = MAX (fValue, pFSpec1[j]); + fValue = MAX(fValue, MAX (fRight, fLeft)); + envelope[i] = fValue; + + fLeft = fRight; + fCurrentLoc = fLastLocation; + iFirstGood = (int)(1+ fCurrentLoc); + } + } + else if(fHopSize == 1) + { + for(i = 0; i < nCoefficients; i++) + envelope[i] = pFSpec1[i]; + } + else + { + sms_error("SpectralApprox: sizeSpec1 has too many nCoefficients"); /* \todo need to increase the frequency? */ + return -1; + } + + /* Creates Spec2 from Envelope */ + if(nCoefficients < sizeSpec2) + { + fSizeX = (sfloat) (sizeSpec2-1) / nCoefficients; + + /* the first step */ + fNextHop = fSizeX / 2; + fDeltaY = envelope[0] / fNextHop; + fSpec2Acum=pFSpec2[j=0]=0; + while(++j < fNextHop) + pFSpec2[j] = (fSpec2Acum += fDeltaY); + + /* middle values */ + for(i = 0; i <= nCoefficients-2; ++i) + { + fDeltaY = (envelope[i+1] - envelope[i]) / fSizeX; + /* first point of a segment */ + pFSpec2[j] = (fSpec2Acum = (envelope[i]+(fDeltaY*(j-fNextHop)))); + ++j; + /* remaining points */ + fNextHop += fSizeX; + while(j < fNextHop) + pFSpec2[j++] = (fSpec2Acum += fDeltaY); + } + + /* last step */ + fDeltaY = -envelope[i] * 2 / fSizeX; + /* first point of the last segment */ + pFSpec2[j] = (fSpec2Acum = (envelope[i]+(fDeltaY*(j-fNextHop)))); + ++j; + fNextHop += fSizeX / 2; + while(j < sizeSpec2-1) + pFSpec2[j++]=(fSpec2Acum += fDeltaY); + /* last should be exactly zero */ + pFSpec2[sizeSpec2-1] = .0; + } + else if(nCoefficients == sizeSpec2) + { + for(i = 0; i < nCoefficients; i++) + pFSpec2[i] = envelope[i]; + } + else + { + sms_error("SpectralApprox: sizeSpec2 has too many nCoefficients\n"); + return -1; + } + + return SMS_OK; +} + + |