summaryrefslogtreecommitdiff
path: root/sms/sms.c
diff options
context:
space:
mode:
authorJohn Glover <glover.john@gmail.com>2010-10-18 17:32:05 +0100
committerJohn Glover <glover.john@gmail.com>2010-10-18 17:32:05 +0100
commit30755b92afeae5a5a32860b4f4297180f6d3398d (patch)
treec9332a65adc6a27016678fccee6ce979d87fed07 /sms/sms.c
parent58a7c36c5a219d8306f276db157097ac30782079 (diff)
downloadsimpl-30755b92afeae5a5a32860b4f4297180f6d3398d.tar.gz
simpl-30755b92afeae5a5a32860b4f4297180f6d3398d.tar.bz2
simpl-30755b92afeae5a5a32860b4f4297180f6d3398d.zip
Moved project over to Git
Diffstat (limited to 'sms/sms.c')
-rw-r--r--sms/sms.c831
1 files changed, 831 insertions, 0 deletions
diff --git a/sms/sms.c b/sms/sms.c
new file mode 100644
index 0000000..c8a6803
--- /dev/null
+++ b/sms/sms.c
@@ -0,0 +1,831 @@
+/*
+ * 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 sms.c
+ * \brief initialization, free, and debug functions
+ */
+
+#include "sms.h"
+#include "SFMT.h" /*!< mersenne twister random number genorator */
+
+char *pChDebugFile = "debug.txt"; /*!< debug text file */
+FILE *pDebug; /*!< pointer to debug file */
+
+static char error_message[256];
+static int error_status = 0;
+static sfloat mag_thresh = .00001; /*!< magnitude threshold for db conversion (-100db)*/
+static sfloat inv_mag_thresh = 100000.; /*!< inv(.00001) */
+static int initIsDone = 0; /* \todo is this variable necessary? */
+
+#define SIZE_TABLES 4096
+
+#define HALF_MAX 1073741823.5 /*!< half the max of a 32-bit word */
+#define INV_HALF_MAX (1.0 / HALF_MAX)
+#define TWENTY_OVER_LOG10 (20. / LOG10)
+
+/*! \brief initialize global data
+ *
+ * Currently, just generating the sine and sinc tables.
+ * This is necessary before both analysis and synthesis.
+ *
+ * If using the Mersenne Twister algorithm for random number
+ * generation, initialize (seed) it.
+ *
+ * \return error code \see SMS_MALLOC or SMS_OK in SMS_ERRORS
+ */
+int sms_init( void )
+{
+ int iError;
+ if (!initIsDone)
+ {
+ initIsDone = 1;
+ if(sms_prepSine (SIZE_TABLES))
+ {
+ sms_error("cannot allocate memory for sine table");
+ return (-1);
+ }
+ if(sms_prepSinc (SIZE_TABLES))
+ {
+ sms_error("cannot allocate memory for sinc table");
+ return (-1);
+ }
+ }
+
+#ifdef MERSENNE_TWISTER
+ init_gen_rand(1234);
+#endif
+
+ return (0);
+}
+
+/*! \brief free global data
+ *
+ * deallocates memory allocated to global arrays (windows and tables)
+ */
+void sms_free( void )
+{
+ initIsDone = 0;
+ sms_clearSine();
+ sms_clearSinc();
+}
+
+/*! \brief give default values to an SMS_AnalParams struct
+ *
+ * This will initialize an SMS_AnalParams with values that work
+ * for common analyses. It is useful to start with and then
+ * adjust the parameters manually to fit a particular sound
+ *
+ * Certain things are hard coded in here that will have to
+ * be updated later (i.e. samplerate), so it is best to call this
+ * function first, then fill whatever parameters need to be
+ * adjusted.
+ *
+ * \param pAnalParams pointer to analysis data structure
+ */
+void sms_initAnalParams(SMS_AnalParams *pAnalParams)
+{
+ pAnalParams->iDebugMode = 0;
+ pAnalParams->iFormat = SMS_FORMAT_H;
+ pAnalParams->iSoundType = SMS_SOUND_TYPE_MELODY;
+ pAnalParams->iStochasticType =SMS_STOC_APPROX;
+ pAnalParams->iFrameRate = 300;
+ pAnalParams->nStochasticCoeff = 128;
+ pAnalParams->fLowestFundamental = 50;
+ pAnalParams->fHighestFundamental = 1000;
+ pAnalParams->fDefaultFundamental = 100;
+ pAnalParams->fPeakContToGuide = .4;
+ pAnalParams->fFundContToGuide = .5;
+ pAnalParams->fFreqDeviation = .45;
+ pAnalParams->iSamplingRate = 44100; /* should be set to the real samplingrate with sms_initAnalysis */
+ pAnalParams->iDefaultSizeWindow = 1001;
+ pAnalParams->windowSize = 0;
+ pAnalParams->sizeHop = 110;
+ pAnalParams->fSizeWindow = 3.5;
+ pAnalParams->nTracks = 60;
+ pAnalParams->nGuides = 100;
+ pAnalParams->iCleanTracks = 1;
+ pAnalParams->fMinRefHarmMag = 30;
+ pAnalParams->fRefHarmMagDiffFromMax = 30;
+ pAnalParams->iRefHarmonic = 1;
+ pAnalParams->iMinTrackLength = 40; /*!< depends on iFrameRate normally */
+ pAnalParams->iMaxSleepingTime = 40; /*!< depends on iFrameRate normally */
+ pAnalParams->fHighestFreq = 12000.;
+ pAnalParams->fMinPeakMag = 0.;
+ pAnalParams->iAnalysisDirection = SMS_DIR_FWD;
+ pAnalParams->iWindowType = SMS_WIN_BH_70;
+ pAnalParams->iSizeSound = 0; /*!< no sound yet */
+ pAnalParams->nFrames = 0; /*!< no frames yet */
+ pAnalParams->minGoodFrames = 3;
+ pAnalParams->maxDeviation = 0.01;
+ pAnalParams->analDelay = 100;
+ pAnalParams->iMaxDelayFrames = MAX(pAnalParams->iMinTrackLength, pAnalParams->iMaxSleepingTime) + 2 +
+ (pAnalParams->minGoodFrames + pAnalParams->analDelay);
+ pAnalParams->fResidualAccumPerc = 0.;
+ pAnalParams->preEmphasisLastValue = 0.;
+ pAnalParams->resetGuides = 1;
+ pAnalParams->resetGuideStates = 1;
+ /* spectral envelope params */
+ pAnalParams->specEnvParams.iType = SMS_ENV_NONE; /* turn off enveloping */
+ pAnalParams->specEnvParams.iOrder = 25; /* ... but set default params anyway */
+ pAnalParams->specEnvParams.fLambda = 0.00001;
+ pAnalParams->specEnvParams.iMaxFreq = 0;
+ pAnalParams->specEnvParams.nCoeff = 0;
+ pAnalParams->specEnvParams.iAnchor = 0; /* not yet implemented */
+}
+
+/*! \brief initialize analysis data structure's arrays
+ *
+ * based on the SMS_AnalParams current settings, this function will
+ * initialize the sound, synth, and fft arrays. It is necessary before analysis.
+ * there can be multple SMS_AnalParams at the same time
+ *
+ * \param pAnalParams pointer to analysis paramaters
+ * \param pSoundHeader pointer to sound header
+ * \return 0 on success, -1 on error
+ */
+int sms_initAnalysis(SMS_AnalParams *pAnalParams)
+{
+ int i;
+ SMS_SndBuffer *pSynthBuf = &pAnalParams->synthBuffer;
+ SMS_SndBuffer *pSoundBuf = &pAnalParams->soundBuffer;
+
+ /* define the hopsize for each record */
+ pAnalParams->sizeHop = (int)(pAnalParams->iSamplingRate /
+ (sfloat) pAnalParams->iFrameRate);
+
+ /* define the number of frames and number of samples */
+// pAnalParams->nFrames = pSoundHeader->nSamples / (sfloat) pAnalParams->sizeHop;
+// pAnalParams->iSizeSound = pSoundHeader->nSamples;
+
+ /* set the default size window to an odd length */
+ pAnalParams->iDefaultSizeWindow =
+ (int)((pAnalParams->iSamplingRate / pAnalParams->fDefaultFundamental) *
+ pAnalParams->fSizeWindow / 2) * 2 + 1;
+
+ int sizeBuffer = (pAnalParams->iMaxDelayFrames * pAnalParams->sizeHop) + SMS_MAX_WINDOW;
+
+ /* if storing residual phases, restrict number of stochastic coefficients to the size of the spectrum (sizeHop = 1/2 sizeFft)*/
+ if(pAnalParams->iStochasticType == SMS_STOC_IFFT)
+ pAnalParams->nStochasticCoeff = sms_power2(pAnalParams->sizeHop);
+
+ /* do the same if spectral envelope is to be stored in frequency bins */
+ if(pAnalParams->specEnvParams.iType == SMS_ENV_FBINS)
+ pAnalParams->specEnvParams.nCoeff = sms_power2(pAnalParams->specEnvParams.iOrder * 2);
+ else if(pAnalParams->specEnvParams.iType == SMS_ENV_CEP)
+ pAnalParams->specEnvParams.nCoeff = pAnalParams->specEnvParams.iOrder+1;
+ /* if specEnvParams.iMaxFreq is still 0, set it to the same as fHighestFreq (normally what you want)*/
+ if(pAnalParams->specEnvParams.iMaxFreq == 0)
+ pAnalParams->specEnvParams.iMaxFreq = pAnalParams->fHighestFreq;
+
+ /*\todo this probably doesn't need env coefficients - they aren't getting used */
+ sms_allocFrame (&pAnalParams->prevFrame, pAnalParams->nGuides,
+ pAnalParams->nStochasticCoeff, 1, pAnalParams->iStochasticType, 0);
+
+ pAnalParams->sizeNextRead = (pAnalParams->iDefaultSizeWindow + 1) * 0.5; /* \todo REMOVE THIS from other files first */
+
+ /* sound buffer */
+ if ((pSoundBuf->pFBuffer = (sfloat *) calloc(sizeBuffer, sizeof(float))) == NULL)
+ {
+ sms_error("could not allocate memory");
+ return(-1);
+ }
+ pSoundBuf->iMarker = -sizeBuffer;
+ pSoundBuf->iFirstGood = sizeBuffer;
+ pSoundBuf->sizeBuffer = sizeBuffer;
+
+ /* check default fundamental */
+ if (pAnalParams->fDefaultFundamental < pAnalParams->fLowestFundamental)
+ {
+ pAnalParams->fDefaultFundamental = pAnalParams->fLowestFundamental;
+ }
+ if (pAnalParams->fDefaultFundamental > pAnalParams->fHighestFundamental)
+ {
+ pAnalParams->fDefaultFundamental = pAnalParams->fHighestFundamental;
+ }
+
+ /* initialize peak detection/continuation parameters */
+ pAnalParams->peakParams.fLowestFreq = pAnalParams->fLowestFundamental;
+ pAnalParams->peakParams.fHighestFreq = pAnalParams->fHighestFreq;
+ pAnalParams->peakParams.fMinPeakMag = pAnalParams->fMinPeakMag;
+ pAnalParams->peakParams.iSamplingRate = pAnalParams->iSamplingRate;
+ pAnalParams->peakParams.iMaxPeaks = SMS_MAX_NPEAKS;
+ pAnalParams->peakParams.fHighestFundamental = pAnalParams->fHighestFundamental;
+ pAnalParams->peakParams.iRefHarmonic = pAnalParams->iRefHarmonic;
+ pAnalParams->peakParams.fMinRefHarmMag = pAnalParams->fMinRefHarmMag;
+ pAnalParams->peakParams.fRefHarmMagDiffFromMax = pAnalParams->fRefHarmMagDiffFromMax;
+ pAnalParams->peakParams.iSoundType = pAnalParams->iSoundType;
+
+ /* deterministic synthesis buffer */
+ pSynthBuf->sizeBuffer = pAnalParams->sizeHop << 1;
+ if((pSynthBuf->pFBuffer = (sfloat *)calloc(pSynthBuf->sizeBuffer, sizeof(float))) == NULL)
+ {
+ sms_error("could not allocate memory");
+ return(-1);
+ }
+ pSynthBuf->iMarker = -sizeBuffer;
+ pSynthBuf->iMarker = pSynthBuf->sizeBuffer;
+
+ /* buffer of analysis frames */
+ if ((pAnalParams->pFrames = (SMS_AnalFrame *) calloc(pAnalParams->iMaxDelayFrames, sizeof(SMS_AnalFrame))) == NULL)
+ {
+ sms_error("could not allocate memory for delay frames");
+ return(-1);
+ }
+ if ((pAnalParams->ppFrames =
+ (SMS_AnalFrame **) calloc(pAnalParams->iMaxDelayFrames, sizeof(SMS_AnalFrame *))) == NULL)
+ {
+ sms_error("could not allocate memory for pointers to delay frames");
+ return(-1);
+ }
+
+ /* initialize the frame pointers and allocate memory */
+ for (i = 0; i < pAnalParams->iMaxDelayFrames; i++)
+ {
+ pAnalParams->pFrames[i].iStatus = SMS_FRAME_EMPTY;
+ if (((pAnalParams->pFrames[i]).pSpectralPeaks =
+ (SMS_Peak *)calloc (pAnalParams->peakParams.iMaxPeaks, sizeof(SMS_Peak))) == NULL)
+ {
+ sms_error("could not allocate memory for spectral peaks");
+ return(-1);
+ }
+ (pAnalParams->pFrames[i].deterministic).nTracks = pAnalParams->nGuides;
+ if (((pAnalParams->pFrames[i].deterministic).pFSinFreq =
+ (sfloat *)calloc (pAnalParams->nGuides, sizeof(float))) == NULL)
+ {
+ sms_error("could not allocate memory");
+ return(-1);
+ }
+ if (((pAnalParams->pFrames[i].deterministic).pFSinAmp =
+ (sfloat *)calloc (pAnalParams->nGuides, sizeof(float))) == NULL)
+ {
+ sms_error("could not allocate memory");
+ return(-1);
+ }
+ if (((pAnalParams->pFrames[i].deterministic).pFSinPha =
+ (sfloat *) calloc (pAnalParams->nGuides, sizeof(float))) == NULL)
+ {
+ sms_error("could not allocate memory");
+ return(-1);
+ }
+ pAnalParams->ppFrames[i] = &pAnalParams->pFrames[i];
+ }
+
+ return (0);
+}
+
+void sms_changeHopSize(int hopSize, SMS_AnalParams *pAnalParams)
+{
+ pAnalParams->sizeHop = hopSize;
+ pAnalParams->iFrameRate = pAnalParams->iSamplingRate / hopSize;
+ int sizeBuffer = (pAnalParams->iMaxDelayFrames * pAnalParams->sizeHop) + SMS_MAX_WINDOW;
+ SMS_SndBuffer *pSynthBuf = &pAnalParams->synthBuffer;
+ SMS_SndBuffer *pSoundBuf = &pAnalParams->soundBuffer;
+
+ /* if storing residual phases, restrict number of stochastic coefficients to the size of the spectrum (sizeHop = 1/2 sizeFft)*/
+ if(pAnalParams->iStochasticType == SMS_STOC_IFFT)
+ pAnalParams->nStochasticCoeff = sms_power2(pAnalParams->sizeHop);
+
+ /* sound buffer */
+ if ((pSoundBuf->pFBuffer = (sfloat *) calloc(sizeBuffer, sizeof(float))) == NULL)
+ {
+ sms_error("could not allocate memory");
+ return(-1);
+ }
+ pSoundBuf->iMarker = -sizeBuffer;
+ pSoundBuf->iFirstGood = sizeBuffer;
+ pSoundBuf->sizeBuffer = sizeBuffer;
+
+ /* deterministic synthesis buffer */
+ pSynthBuf->sizeBuffer = pAnalParams->sizeHop << 1;
+ if((pSynthBuf->pFBuffer = (sfloat *)calloc(pSynthBuf->sizeBuffer, sizeof(float))) == NULL)
+ {
+ sms_error("could not allocate memory");
+ return(-1);
+ }
+ pSynthBuf->iMarker = -sizeBuffer;
+ pSynthBuf->iMarker = pSynthBuf->sizeBuffer;
+}
+
+/*! \brief give default values to an SMS_SynthParams struct
+ *
+ * This will initialize an SMS_SynthParams with values that work
+ * for common analyses. It is useful to start with and then
+ * adjust the parameters manually to fit a particular sound
+ *
+ * \param synthParams pointer to synthesis parameters data structure
+ */
+void sms_initSynthParams(SMS_SynthParams *synthParams)
+{
+ synthParams->iSamplingRate = 44100;
+ synthParams->iOriginalSRate = 44100;
+ synthParams->iSynthesisType = SMS_STYPE_ALL;
+ synthParams->iDetSynthType = SMS_DET_IFFT;
+ synthParams->sizeHop = SMS_MIN_SIZE_FRAME;
+ synthParams->origSizeHop = SMS_MIN_SIZE_FRAME;
+ synthParams->nTracks = 60;
+ synthParams->iStochasticType = SMS_STOC_APPROX;
+ synthParams->nStochasticCoeff = 128;
+ synthParams->deemphasisLastValue = 0;
+}
+
+/*! \brief initialize synthesis data structure's arrays
+ *
+ * Initialize the synthesis and fft arrays. It is necessary before synthesis.
+ * there can be multple SMS_SynthParams at the same time
+ * This function also sets some initial values that will create a sane synthesis
+ * environment.
+ *
+ * This function requires an SMS_Header because it may be called to synthesize
+ * a stored .sms file, which contains a header with necessary information.
+ *
+ * \param pSmsHeader pointer to SMS_Header
+ * \param pSynthParams pointer to synthesis paramaters
+ * \return 0 on success, -1 on error
+ */
+int sms_initSynth(SMS_SynthParams *pSynthParams)
+{
+ int sizeHop, sizeFft, err;
+ /* set synthesis parameters from arguments and header */
+// pSynthParams->iOriginalSRate = pSmsHeader->iSamplingRate;
+// pSynthParams->origSizeHop = pSynthParams->iOriginalSRate / pSmsHeader->iFrameRate;
+// pSynthParams->iStochasticType = pSmsHeader->iStochasticType;
+// if(pSynthParams->iSamplingRate <= 0)
+// pSynthParams->iSamplingRate = pSynthParams->iOriginalSRate;
+
+ /* make sure sizeHop is something to the power of 2 */
+ sizeHop = sms_power2(pSynthParams->sizeHop);
+ if(sizeHop != pSynthParams->sizeHop)
+ {
+ sms_error("sizeHop was not a power of two.");
+ err = -1;
+ pSynthParams->sizeHop = sizeHop;
+ }
+ sizeFft = sizeHop * 2;
+
+ pSynthParams->pFStocWindow =(sfloat *) calloc(sizeFft, sizeof(float));
+ sms_getWindow( sizeFft, pSynthParams->pFStocWindow, SMS_WIN_HANNING );
+ pSynthParams->pFDetWindow = (sfloat *) calloc(sizeFft, sizeof(float));
+ sms_getWindow( sizeFft, pSynthParams->pFDetWindow, SMS_WIN_IFFT );
+
+ /* allocate memory for analysis data - size of original hopsize */
+ /* previous frame to interpolate from */
+ /* \todo why is stoch coeff + 1? */
+ sms_allocFrame(&pSynthParams->prevFrame, pSynthParams->nTracks,
+ pSynthParams->nStochasticCoeff + 1, 1,
+ pSynthParams->iStochasticType, 0);
+
+ pSynthParams->pSynthBuff = (sfloat *) calloc(sizeFft, sizeof(float));
+ pSynthParams->pMagBuff = (sfloat *) calloc(sizeHop, sizeof(float));
+ pSynthParams->pPhaseBuff = (sfloat *) calloc(sizeHop, sizeof(float));
+ pSynthParams->pSpectra = (sfloat *) calloc(sizeFft, sizeof(float));
+
+ /* set/check modification parameters */
+// pSynthParams->modParams.maxFreq = pSmsHeader->iMaxFreq;
+
+ return SMS_OK;
+}
+
+int sms_changeSynthHop( SMS_SynthParams *pSynthParams, int sizeHop)
+{
+ int sizeFft = sizeHop * 2;
+
+ pSynthParams->pSynthBuff = (sfloat *) realloc(pSynthParams->pSynthBuff, sizeFft * sizeof(float));
+ pSynthParams->pSpectra = (sfloat *) realloc(pSynthParams->pSpectra, sizeFft * sizeof(float));
+ pSynthParams->pMagBuff = (sfloat *) realloc(pSynthParams->pMagBuff, sizeHop * sizeof(float));
+ pSynthParams->pPhaseBuff = (sfloat *) realloc(pSynthParams->pPhaseBuff, sizeHop * sizeof(float));
+ pSynthParams->pFStocWindow =
+ (sfloat *) realloc(pSynthParams->pFStocWindow, sizeFft * sizeof(float));
+ sms_getWindow( sizeFft, pSynthParams->pFStocWindow, SMS_WIN_HANNING );
+ pSynthParams->pFDetWindow =
+ (sfloat *) realloc(pSynthParams->pFDetWindow, sizeFft * sizeof(float));
+ sms_getWindow( sizeFft, pSynthParams->pFDetWindow, SMS_WIN_IFFT );
+
+ pSynthParams->sizeHop = sizeHop;
+
+ return(SMS_OK);
+}
+
+/*! \brief free analysis data
+ *
+ * frees all the memory allocated to an SMS_AnalParams by
+ * sms_initAnalysis
+ *
+ * \param pAnalParams pointer to analysis data structure
+ */
+void sms_freeAnalysis( SMS_AnalParams *pAnalParams )
+{
+ int i;
+ for (i = 0; i < pAnalParams->iMaxDelayFrames; i++)
+ {
+ free((pAnalParams->pFrames[i]).pSpectralPeaks);
+ free((pAnalParams->pFrames[i].deterministic).pFSinFreq);
+ free((pAnalParams->pFrames[i].deterministic).pFSinAmp);
+ free((pAnalParams->pFrames[i].deterministic).pFSinPha);
+ }
+
+ sms_freeFrame(&pAnalParams->prevFrame);
+// free(pAnalParams->soundBuffer.pFBuffer);
+ free(pAnalParams->synthBuffer.pFBuffer);
+ free(pAnalParams->pFrames);
+ free(pAnalParams->ppFrames);
+// free(pAnalParams->pFSpectrumWindow);
+
+}
+
+/*! \brief free analysis data
+ *
+ * frees all the memory allocated to an SMS_SynthParams by
+ * sms_initSynthesis
+ *
+ * \todo is there a way to make sure the plan has been made
+ * already? as it is, it crashes if this is called without one
+ * \param pSynthParams pointer to synthesis data structure
+ */
+void sms_freeSynth( SMS_SynthParams *pSynthParams )
+{
+ free(pSynthParams->pFStocWindow);
+ free(pSynthParams->pFDetWindow);
+ free (pSynthParams->pSynthBuff);
+ free (pSynthParams->pSpectra);
+ free (pSynthParams->pMagBuff);
+ free (pSynthParams->pPhaseBuff);
+ sms_freeFrame(&pSynthParams->prevFrame);
+
+}
+
+/*! \brief set window size for next frame
+ *
+ * adjusts the next window size to fit the currently detected fundamental
+ * frequency, or resets to a default window size if unstable.
+ *
+ * \param iCurrentFrame number of current frame
+ * \param pAnalParams analysis parameters
+ * \return the size of the next window in samples
+ */
+int sms_sizeNextWindow (int iCurrentFrame, SMS_AnalParams *pAnalParams)
+{
+ sfloat fFund = pAnalParams->ppFrames[iCurrentFrame]->fFundamental,
+ fPrevFund = pAnalParams->ppFrames[iCurrentFrame-1]->fFundamental;
+ int sizeWindow;
+
+ /* if the previous fundamental was stable use it to set the window size */
+ if (fPrevFund > 0 &&
+ fabs(fPrevFund - fFund) / fFund <= .2)
+ sizeWindow = (int) ((pAnalParams->iSamplingRate / fFund) *
+ pAnalParams->fSizeWindow * .5) * 2 + 1;
+ /* otherwise use the default size window */
+ else
+ sizeWindow = pAnalParams->iDefaultSizeWindow;
+
+ if (sizeWindow > SMS_MAX_WINDOW)
+ {
+ fprintf (stderr, "sms_sizeNextWindow error: sizeWindow (%d) too big, set to %d\n", sizeWindow,
+ SMS_MAX_WINDOW);
+ sizeWindow = SMS_MAX_WINDOW;
+ }
+
+ return (sizeWindow);
+}
+
+/*! \brief initialize the current frame
+ *
+ * initializes arrays to zero and sets the correct sample position.
+ * Special care is taken at the end the sample source (if there is
+ * not enough samples for an entire frame.
+ *
+ * \param iCurrentFrame frame number of current frame in buffer
+ * \param pAnalParams analysis parameters
+ * \param sizeWindow size of analysis window
+ * \return -1 on error \todo make this return void
+ */
+int sms_initFrame(int iCurrentFrame, SMS_AnalParams *pAnalParams, int sizeWindow)
+{
+ /* clear deterministic data */
+ memset ((sfloat *) pAnalParams->ppFrames[iCurrentFrame]->deterministic.pFSinFreq, 0,
+ sizeof(sfloat) * pAnalParams->nGuides);
+ memset ((sfloat *) pAnalParams->ppFrames[iCurrentFrame]->deterministic.pFSinAmp, 0,
+ sizeof(sfloat) * pAnalParams->nGuides);
+ memset ((sfloat *) pAnalParams->ppFrames[iCurrentFrame]->deterministic.pFSinPha, 0,
+ sizeof(sfloat) * pAnalParams->nGuides);
+ /* clear peaks */
+ memset ((void *) pAnalParams->ppFrames[iCurrentFrame]->pSpectralPeaks, 0,
+ sizeof (SMS_Peak) * SMS_MAX_NPEAKS);
+
+ pAnalParams->ppFrames[iCurrentFrame]->nPeaks = 0;
+ pAnalParams->ppFrames[iCurrentFrame]->fFundamental = 0;
+
+ pAnalParams->ppFrames[iCurrentFrame]->iFrameNum =
+ pAnalParams->ppFrames[iCurrentFrame - 1]->iFrameNum + 1;
+ pAnalParams->ppFrames[iCurrentFrame]->iFrameSize = sizeWindow;
+
+ //printf("sizeHop (sms_initFrame #: %d): %d \n", iCurrentFrame, pAnalParams->sizeHop);
+ /* if first frame set center of data around 0 */
+ if(pAnalParams->ppFrames[iCurrentFrame]->iFrameNum == 1)
+ pAnalParams->ppFrames[iCurrentFrame]->iFrameSample = 0;
+ /* increment center of data by sizeHop */
+ else
+ pAnalParams->ppFrames[iCurrentFrame]->iFrameSample =
+ pAnalParams->ppFrames[iCurrentFrame-1]->iFrameSample + pAnalParams->sizeHop;
+
+ /* check for error */
+// if (pAnalParams->soundBuffer.iMarker >
+// pAnalParams->ppFrames[iCurrentFrame]->iFrameSample - (sizeWindow+1)/2)
+// {
+// sms_error("sms_initFrame: runoff on the sound buffer.");
+// //printf("BLAG: sms_initFrame: runoff on the sound buffer.");
+// return(-1);
+// }
+
+// /* check for end of sound */
+ if ((pAnalParams->ppFrames[iCurrentFrame]->iFrameSample + (sizeWindow+1)/2) >= pAnalParams->iSizeSound
+ && pAnalParams->iSizeSound > 0)
+ {
+ pAnalParams->ppFrames[iCurrentFrame]->iFrameNum = -1;
+ pAnalParams->ppFrames[iCurrentFrame]->iFrameSize = 0;
+ pAnalParams->ppFrames[iCurrentFrame]->iStatus = SMS_FRAME_END;
+ }
+ else
+ {
+ /* good status, ready to start computing */
+ pAnalParams->ppFrames[iCurrentFrame]->iStatus = SMS_FRAME_READY;
+ }
+ return SMS_OK;
+// pAnalParams->ppFrames[iCurrentFrame]->iStatus = SMS_FRAME_READY;
+}
+
+/*! \brief get deviation from average fundamental
+ *\
+ * \param pAnalParams pointer to analysis params
+ * \param iCurrentFrame number of current frame
+ * \return deviation value or -1 if really off
+ */
+sfloat sms_fundDeviation(SMS_AnalParams *pAnalParams, int iCurrentFrame)
+{
+ sfloat fFund, fSum = 0, fAverage, fDeviation = 0;
+ int i;
+
+ /* get the sum of the past few fundamentals */
+ for (i = 0; i < pAnalParams->minGoodFrames; i++)
+ {
+ fFund = pAnalParams->ppFrames[iCurrentFrame-i]->fFundamental;
+ if(fFund <= 0)
+ return(-1);
+ else
+ fSum += fFund;
+ }
+
+ /* find the average */
+ fAverage = fSum / pAnalParams->minGoodFrames;
+
+ /* get the deviation from the average */
+ for (i = 0; i < pAnalParams->minGoodFrames; i++)
+ fDeviation += fabs(pAnalParams->ppFrames[iCurrentFrame-i]->fFundamental - fAverage);
+
+ /* return the deviation from the average */
+ return (fDeviation / (pAnalParams->minGoodFrames * fAverage));
+}
+
+
+/*! \brief function to create the debug file
+ *
+ * \param pAnalParams pointer to analysis params
+ * \return error value \see SMS_ERRORS
+*/
+int sms_createDebugFile (SMS_AnalParams *pAnalParams)
+{
+ if ((pDebug = fopen(pChDebugFile, "w+")) == NULL)
+ {
+ fprintf(stderr, "Cannot open debugfile: %s\n", pChDebugFile);
+ return(SMS_WRERR);
+ }
+ else return(SMS_OK);
+}
+
+/*! \brief function to write to the debug file
+ *
+ * writes three arrays of equal size to a debug text
+ * file ("./debug.txt"). There are three arrays for the
+ * frequency, magnitude, phase sets.
+ *
+ * \param pFBuffer1 pointer to array 1
+ * \param pFBuffer2 pointer to array 2
+ * \param pFBuffer3 pointer to array 3
+ * \param sizeBuffer the size of the buffers
+ */
+void sms_writeDebugData (sfloat *pFBuffer1, sfloat *pFBuffer2,
+ sfloat *pFBuffer3, int sizeBuffer)
+{
+ int i;
+ static int counter = 0;
+
+ for (i = 0; i < sizeBuffer; i++)
+ fprintf (pDebug, "%d %d %d %d\n", counter++, (int)pFBuffer1[i],
+ (int)pFBuffer2[i], (int)pFBuffer3[i]);
+
+}
+
+/*! \brief function to write the residual sound file to disk
+ *
+ * writes the "debug.txt" file to disk and closes the file.
+ */
+void sms_writeDebugFile ()
+{
+ fclose (pDebug);
+}
+
+/*! \brief convert from magnitude to decibel
+ *
+ * \param x magnitude (0:1)
+ * \return decibel (0: -100)
+ */
+sfloat sms_magToDB( sfloat x)
+{
+ if(x < mag_thresh)
+ return(0.);
+ else
+ //return(20. * log10(x * inv_mag_thresh));
+ return(TWENTY_OVER_LOG10 * log(x * inv_mag_thresh));
+}
+
+/*! \brief convert from decibel to magnitude
+ *
+ * \param x decibel (0-100)
+ * \return magnitude (0-1)
+ */
+sfloat sms_dBToMag( sfloat x)
+{
+ if(x < 0.00001)
+ return (0.);
+ else
+ return(mag_thresh * pow(10., x*0.05));
+}
+
+/*! \brief convert an array from magnitude to decibel
+ *
+ * Depends on a linear threshold that indicates the bottom end
+ * of the dB scale (magnutdes at this value will convert to zero).
+ * \see sms_setMagThresh
+ *
+ * \param sizeArray size of array
+ * \param pArray pointer to array
+ */
+void sms_arrayMagToDB( int sizeArray, sfloat *pArray)
+{
+ int i;
+ for( i = 0; i < sizeArray; i++)
+ pArray[i] = sms_magToDB(pArray[i]);
+}
+
+/*! \brief convert and array from decibel (0-100) to magnitude (0-1)
+ *
+ * depends on the magnitude threshold
+ * \see sms_setMagThresh
+ *
+ * \param sizeArray size of array
+ * \param pArray pointer to array
+ */
+void sms_arrayDBToMag( int sizeArray, sfloat *pArray)
+{
+ int i;
+ for( i = 0; i < sizeArray; i++)
+ pArray[i] = sms_dBToMag(pArray[i]);
+}
+/*! \brief set the linear magnitude threshold
+ *
+ * magnitudes below this will go to zero when converted to db.
+ * it is limited to 0.00001 (-100db)
+ *
+ * \param x threshold value
+ */
+void sms_setMagThresh( sfloat x)
+{
+ /* limit threshold to -100db */
+ if(x < 0.00001)
+ mag_thresh = 0.00001;
+ else
+ mag_thresh = x;
+ inv_mag_thresh = 1. / mag_thresh;
+}
+
+/*! \brief get a string containing information about the error code
+ *
+ * \param pErrorMessage pointer to error message string
+ */
+void sms_error(char *pErrorMessage) {
+ strncpy(error_message, pErrorMessage, 256);
+ error_status = -1;
+}
+
+/*! \brief check if an error has been reported
+ *
+ * \return -1 if there is an error, 0 if ok
+ */
+int sms_errorCheck()
+{
+ return(error_status);
+}
+
+/*! \brief get a string containing information about the last error
+ *
+ * \return pointer to a char string, or NULL if no error
+ */
+char* sms_errorString()
+{
+ if (error_status)
+ {
+ error_status = 0;
+ return error_message;
+ }
+ else return NULL;
+}
+
+/*! \brief random number genorator
+ *
+ * \return random number between -1 and 1
+ */
+sfloat sms_random()
+{
+#ifdef MERSENNE_TWISTER
+ return(genrand_real1());
+#else
+ return((sfloat)(random() * 2 * INV_HALF_MAX));
+#endif
+}
+
+/*! \brief Root Mean Squared of an array
+ *
+ * \return RMS energy
+ */
+sfloat sms_rms(int sizeArray, sfloat *pArray)
+{
+ int i;
+ sfloat mean_squared = 0.;
+ for( i = 0; i < sizeArray; i++)
+ mean_squared += pArray[i] * pArray[i];
+
+ return(sqrtf(mean_squared / sizeArray));
+}
+
+/*! \brief make sure a number is a power of 2
+ *
+ * \return a power of two integer >= input value
+ */
+int sms_power2( int n)
+{
+ int p = -1;
+ int N = n;
+ while(n)
+ {
+ n >>= 1;
+ p++;
+ }
+
+ if(1<<p == N) /* n was a power of 2 */
+ {
+ return(N);
+ }
+ else /* make the new value larger than n */
+ {
+ p++;
+ return(1<<p);
+ }
+}
+
+/*! \brief compute a value for scaling frequency based on the well-tempered scale
+ *
+ * \param x linear frequency value
+ * \return (1.059...)^x, where 1.059 is the 12th root of 2 precomputed
+ */
+sfloat sms_scalarTempered( sfloat x)
+{
+ return(powf(1.0594630943592953, x));
+}
+
+/*! \brief scale an array of linear frequencies to the well-tempered scale
+ *
+ * \param sizeArray size of the array
+ * \param pArray pointer to array of frequencies
+ */
+void sms_arrayScalarTempered( int sizeArray, sfloat *pArray)
+{
+ int i;
+ for( i = 0; i < sizeArray; i++)
+ pArray[i] = sms_scalarTempered(pArray[i]);
+}