summaryrefslogtreecommitdiff
path: root/src/sms/fileIO.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sms/fileIO.c')
-rw-r--r--src/sms/fileIO.c559
1 files changed, 559 insertions, 0 deletions
diff --git a/src/sms/fileIO.c b/src/sms/fileIO.c
new file mode 100644
index 0000000..e870c5a
--- /dev/null
+++ b/src/sms/fileIO.c
@@ -0,0 +1,559 @@
+/*
+ * 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 fileIO.c
+ * \brief SMS file input and output
+ */
+
+#include "sms.h"
+
+/*! \brief file identification constant
+ *
+ * constant number that is first within SMS_Header, in order to correctly
+ * identify an SMS file when read.
+ */
+#define SMS_MAGIC 767
+
+static char pChTextString[1000]; /*!< string to store analysis parameters in sms header */
+
+/*! \brief initialize the header structure of an SMS file
+ *
+ * \param pSmsHeader header for SMS file
+ */
+void sms_initHeader(SMS_Header *pSmsHeader)
+{
+ pSmsHeader->iSmsMagic = SMS_MAGIC;
+ pSmsHeader->iHeadBSize = sizeof(SMS_Header);
+ pSmsHeader->nFrames = 0;
+ pSmsHeader->iFrameBSize = 0;
+ pSmsHeader->iFormat = SMS_FORMAT_H;
+ pSmsHeader->iFrameRate = 0;
+ pSmsHeader->iStochasticType = SMS_STOC_APPROX;
+ pSmsHeader->nTracks = 0;
+ pSmsHeader->nStochasticCoeff = 0;
+ pSmsHeader->nEnvCoeff = 0;
+ pSmsHeader->iMaxFreq = 0;
+ pSmsHeader->fResidualPerc = 0;
+}
+
+/*! \brief fill an SMS header with necessary information for storage
+ *
+ * copies parameters from SMS_AnalParams, along with other values
+ * so an SMS file can be stored and correctly synthesized at a later
+ * time. This is somewhat of a convenience function.
+ *
+ * sms_initAnal() should be done first to properly set everything.
+ *
+ * \param pSmsHeader header for SMS file (to be stored)
+ * \param pAnalParams structure of analysis parameters
+ * \param pProgramString pointer to a string containing the name of the program that made the analysis data
+ */
+void sms_fillHeader(SMS_Header *pSmsHeader, SMS_AnalParams *pAnalParams, char *pProgramString)
+{
+ sms_initHeader(pSmsHeader);
+ pSmsHeader->nFrames = pAnalParams->nFrames;
+ pSmsHeader->iFormat = pAnalParams->iFormat;
+ pSmsHeader->iFrameRate = pAnalParams->iFrameRate;
+ pSmsHeader->iStochasticType = pAnalParams->iStochasticType;
+ pSmsHeader->nTracks = pAnalParams->nTracks;
+ pSmsHeader->iSamplingRate = pAnalParams->iSamplingRate;
+ if(pAnalParams->iStochasticType == SMS_STOC_NONE)
+ pSmsHeader->nStochasticCoeff = 0;
+ else
+ pSmsHeader->nStochasticCoeff = pAnalParams->nStochasticCoeff;
+ pSmsHeader->iEnvType = pAnalParams->specEnvParams.iType;
+ pSmsHeader->nEnvCoeff = pAnalParams->specEnvParams.nCoeff;
+ pSmsHeader->iMaxFreq = (int)pAnalParams->fHighestFreq;
+ pSmsHeader->iFrameBSize = sms_frameSizeB(pSmsHeader);
+}
+
+/*! \brief write SMS header to file
+ *
+ * \param pChFileName file name for SMS file
+ * \param pSmsHeader header for SMS file
+ * \param ppSmsFile (double pointer to) file to be created
+ * \return error code \see SMS_WRERR in SMS_ERRORS
+ */
+int sms_writeHeader(char *pChFileName, SMS_Header *pSmsHeader, FILE **ppSmsFile)
+{
+ int iVariableSize = 0;
+
+ if(pSmsHeader->iSmsMagic != SMS_MAGIC)
+ {
+ sms_error("not an SMS file");
+ return -1;
+ }
+ if((*ppSmsFile = fopen (pChFileName, "w+")) == NULL)
+ {
+ sms_error("cannot open file for writing");
+ return -1;
+ }
+
+ pSmsHeader->iHeadBSize = sizeof(SMS_Header);
+
+ /* write header */
+ if(fwrite((void *)pSmsHeader, (size_t)1, (size_t)sizeof(SMS_Header),
+ *ppSmsFile) < (size_t)sizeof(SMS_Header))
+ {
+ sms_error("cannot write output file");
+ return(-1);
+ }
+ return 0;
+}
+
+/*! \brief rewrite SMS header and close file
+ *
+ * \param pSmsFile pointer to SMS file
+ * \param pSmsHeader pointer to header for SMS file
+ * \return error code \see SMS_WRERR in SMS_ERRORS
+ */
+int sms_writeFile(FILE *pSmsFile, SMS_Header *pSmsHeader)
+{
+ int iVariableSize;
+ rewind(pSmsFile);
+
+ pSmsHeader->iHeadBSize = sizeof(SMS_Header);
+
+ /* write header */
+ if(fwrite((void *)pSmsHeader, (size_t)1, (size_t)sizeof(SMS_Header),
+ pSmsFile) < (size_t)sizeof(SMS_Header))
+ {
+ sms_error("cannot write output file (header)");
+ return -1;
+ }
+
+ fclose(pSmsFile);
+ return 0;
+}
+
+/*! \brief write SMS frame
+ *
+ * \param pSmsFile pointer to SMS file
+ * \param pSmsHeader pointer to SMS header
+ * \param pSmsFrame pointer to SMS data frame
+ * \return 0 on success, -1 on failure
+ */
+int sms_writeFrame(FILE *pSmsFile, SMS_Header *pSmsHeader, SMS_Data *pSmsFrame)
+{
+ if (fwrite((void *)pSmsFrame->pSmsData, 1, pSmsHeader->iFrameBSize,
+ pSmsFile) < (unsigned int) pSmsHeader->iFrameBSize)
+ {
+ sms_error("cannot write frame to output file");
+ return -1;
+ }
+ return 0;
+}
+
+/*! \brief get the size in bytes of the frame in a SMS file
+ *
+ * \param pSmsHeader pointer to SMS header
+ * \return the size in bytes of the frame
+ */
+int sms_frameSizeB(SMS_Header *pSmsHeader)
+{
+ int iSize, nDet;
+
+ if(pSmsHeader->iFormat == SMS_FORMAT_H ||
+ pSmsHeader->iFormat == SMS_FORMAT_IH)
+ nDet = 2;/* freq, mag */
+ else
+ nDet = 3; /* freq, mag, phase */
+
+ iSize = sizeof (sfloat) * (nDet * pSmsHeader->nTracks);
+
+ if(pSmsHeader->iStochasticType == SMS_STOC_APPROX)
+ { /* stocCoeff + 1 (gain) */
+ iSize += sizeof(sfloat) * (pSmsHeader->nStochasticCoeff + 1);
+ }
+ else if(pSmsHeader->iStochasticType == SMS_STOC_IFFT)
+ {
+ /* sizeFFT*2 + 1 (gain) */
+ iSize += sizeof(sfloat) * (pSmsHeader->nStochasticCoeff * 2 + 1);
+ }
+ iSize += sizeof(sfloat) * pSmsHeader->nEnvCoeff;
+ return iSize;
+}
+
+/*! \brief function to read SMS header
+ *
+ * \param pChFileName file name for SMS file
+ * \param ppSmsHeader (double pointer to) SMS header
+ * \param ppSmsFile (double pointer to) inputfile
+ * \return error code \see SMS_ERRORS
+ */
+int sms_getHeader(char *pChFileName, SMS_Header **ppSmsHeader, FILE **ppSmsFile)
+{
+ int iHeadBSize, iFrameBSize, nFrames;
+ int iMagicNumber;
+
+ /* open file for reading */
+ if((*ppSmsFile = fopen (pChFileName, "r")) == NULL)
+ {
+ sms_error("could not open SMS header");
+ return -1;
+ }
+ /* read magic number */
+ if(fread((void *) &iMagicNumber, (size_t) sizeof(int), (size_t)1,
+ *ppSmsFile) < (size_t)1)
+ {
+ sms_error("could not read SMS header");
+ return -1;
+ }
+
+ if(iMagicNumber != SMS_MAGIC)
+ {
+ sms_error("not an SMS file");
+ return -1;
+ }
+
+ /* read size of of header */
+ if(fread((void *) &iHeadBSize, (size_t) sizeof(int), (size_t)1,
+ *ppSmsFile) < (size_t)1)
+ {
+ sms_error("could not read SMS header (iHeadBSize)");
+ return -1;
+ }
+
+ if(iHeadBSize <= 0)
+ {
+ sms_error("bad SMS header size");
+ return -1;
+ }
+
+ /* read number of data Frames */
+ if(fread((void *) &nFrames, (size_t) sizeof(int), (size_t)1,
+ *ppSmsFile) < (size_t)1)
+ {
+ sms_error("could not read SMS number of frames");
+ return -1;
+ }
+
+ if(nFrames <= 0)
+ {
+ sms_error("number of frames <= 0");
+ return -1;
+ }
+
+ /* read size of data Frames */
+ if(fread((void *) &iFrameBSize, (size_t) sizeof(int), (size_t)1,
+ *ppSmsFile) < (size_t)1)
+ {
+ sms_error("could not read size of SMS data");
+ return -1;
+ }
+
+ if(iFrameBSize <= 0)
+ {
+ sms_error("size bytes of frames <= 0");
+ return -1;
+ }
+
+ /* allocate memory for header */
+ if(((*ppSmsHeader) = (SMS_Header *)malloc (iHeadBSize)) == NULL)
+ {
+ sms_error("cannot allocate memory for header");
+ return -1;
+ }
+
+ /* read header */
+ rewind(*ppSmsFile);
+ if(fread ((void *) (*ppSmsHeader), 1, iHeadBSize, *ppSmsFile) < (unsigned int) iHeadBSize)
+ {
+ sms_error("cannot read header of SMS file");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*! \brief read an SMS data frame
+ *
+ * \param pSmsFile pointer to SMS file
+ * \param pSmsHeader pointer to SMS header
+ * \param iFrame frame number
+ * \param pSmsFrame pointer to SMS frame
+ * \return 0 on sucess, -1 on error
+ */
+int sms_getFrame(FILE *pSmsFile, SMS_Header *pSmsHeader, int iFrame, SMS_Data *pSmsFrame)
+{
+ if(fseek(pSmsFile, pSmsHeader->iHeadBSize + iFrame *
+ pSmsHeader->iFrameBSize, SEEK_SET) < 0)
+ {
+ sms_error("cannot seek to the SMS frame");
+ return -1;
+ }
+ if((pSmsHeader->iFrameBSize =
+ fread((void *)pSmsFrame->pSmsData, (size_t)1,
+ (size_t)pSmsHeader->iFrameBSize, pSmsFile))
+ != pSmsHeader->iFrameBSize)
+ {
+ sms_error("cannot read SMS frame");
+ return -1;
+ }
+ return 0;
+}
+
+/*! \brief allocate memory for a frame of SMS data
+ *
+ * \param pSmsFrame pointer to a frame of SMS data
+ * \param nTracks number of sinusoidal tracks in frame
+ * \param nStochCoeff number of stochastic coefficients in frame
+ * \param iPhase whether phase information is in the frame
+ * \param stochType stochastic resynthesis type
+ * \param nStochCoeff number of envelope coefficients in frame
+ * \param nEnvCoeff number of envelope coefficients in frame
+ * \return 0 on success, -1 on error
+ */
+int sms_allocFrame(SMS_Data *pSmsFrame, int nTracks, int nStochCoeff, int iPhase,
+ int stochType, int nEnvCoeff)
+{
+ sfloat *dataPos; /* a marker to locate specific data witin smsData */
+
+ /* calculate size of frame */
+ int sizeData = sizeof(sfloat); /* nSamples */
+ /* frequencies and magnitudes */
+ sizeData += 2 * nTracks * sizeof(sfloat);
+ /* phases */
+ if(iPhase > 0)
+ sizeData += nTracks * sizeof(sfloat);
+ /* stochastic coefficients */
+ if(stochType == SMS_STOC_APPROX)
+ sizeData += (nStochCoeff + 1) * sizeof(sfloat);
+ else if(stochType == SMS_STOC_IFFT)
+ sizeData += ((2*nStochCoeff) + 1) * sizeof(sfloat);
+ /* spectral envelope */
+ sizeData += nEnvCoeff * sizeof(sfloat); /* add in number of envelope coefficients (cep or fbins) if any */
+
+ /* allocate memory for data */
+ pSmsFrame->pSmsData = (sfloat *)malloc(sizeData);
+ if(pSmsFrame->pSmsData == NULL)
+ {
+ sms_error("cannot allocate memory for SMS frame data");
+ return -1;
+ }
+ memset(pSmsFrame->pSmsData, 0, sizeData);
+
+ /* set the variables in the structure */
+ /* \todo why not set these in init functions, then allocate with them?? */
+ pSmsFrame->sizeData = sizeData;
+ pSmsFrame->nTracks = nTracks;
+ pSmsFrame->nCoeff = nStochCoeff;
+ pSmsFrame->nEnvCoeff = nEnvCoeff;
+
+ /* set pointers to data types within smsData array */
+ pSmsFrame->pFSinFreq = pSmsFrame->pSmsData;
+ dataPos = (sfloat *)(pSmsFrame->pFSinFreq + nTracks);
+
+ pSmsFrame->pFSinAmp = dataPos;
+ dataPos = (sfloat *)(pSmsFrame->pFSinAmp + nTracks);
+
+ if(iPhase > 0)
+ {
+ pSmsFrame->pFSinPha = dataPos;
+ dataPos = (sfloat *)(pSmsFrame->pFSinPha + nTracks);
+ }
+ else
+ pSmsFrame->pFSinPha = NULL;
+
+ if(stochType == SMS_STOC_APPROX)
+ {
+ pSmsFrame->pFStocCoeff = dataPos;
+ dataPos = (sfloat *)(pSmsFrame->pFStocCoeff + nStochCoeff);
+
+ pSmsFrame->pFStocGain = dataPos;
+ dataPos = (sfloat *)(pSmsFrame->pFStocGain + 1);
+ }
+ else if(stochType == SMS_STOC_IFFT)
+ {
+ pSmsFrame->pFStocCoeff = dataPos;
+ dataPos = (sfloat *)(pSmsFrame->pFStocCoeff + nStochCoeff);
+ pSmsFrame->pResPhase = dataPos;
+ dataPos = (sfloat *)(pSmsFrame->pResPhase + nStochCoeff);
+ pSmsFrame->pFStocGain = dataPos;
+ dataPos = (sfloat *)(pSmsFrame->pFStocGain + 1);
+ }
+ else
+ {
+ pSmsFrame->pFStocCoeff = NULL;
+ pSmsFrame->pResPhase = NULL;
+ pSmsFrame->pFStocGain = NULL;
+ }
+
+ if(nEnvCoeff > 0)
+ pSmsFrame->pSpecEnv = dataPos;
+ else
+ pSmsFrame->pSpecEnv = NULL;
+
+ return 0;
+}
+
+/*! \brief function to allocate an SMS data frame using an SMS_Header
+ *
+ * this one is used when you have only read the header, such as after
+ * opening a file.
+ *
+ * \param pSmsHeader pointer to SMS header
+ * \param pSmsFrame pointer to SMS frame
+ * \return 0 on success, -1 on error
+ */
+int sms_allocFrameH(SMS_Header *pSmsHeader, SMS_Data *pSmsFrame)
+{
+ int iPhase = (pSmsHeader->iFormat == SMS_FORMAT_HP ||
+ pSmsHeader->iFormat == SMS_FORMAT_IHP) ? 1 : 0;
+ return sms_allocFrame(pSmsFrame, pSmsHeader->nTracks,
+ pSmsHeader->nStochasticCoeff, iPhase,
+ pSmsHeader->iStochasticType,
+ pSmsHeader->nEnvCoeff);
+}
+
+/*! \brief free the SMS data structure
+ *
+ * \param pSmsFrame pointer to frame of SMS data
+ */
+void sms_freeFrame(SMS_Data *pSmsFrame)
+{
+ if(!pSmsFrame)
+ return;
+
+ if(pSmsFrame->pSmsData)
+ free(pSmsFrame->pSmsData);
+
+ pSmsFrame->nTracks = 0;
+ pSmsFrame->nCoeff = 0;
+ pSmsFrame->sizeData = 0;
+ pSmsFrame->pFSinFreq = NULL;
+ pSmsFrame->pFSinAmp = NULL;
+ pSmsFrame->pFStocCoeff = NULL;
+ pSmsFrame->pResPhase = NULL;
+ pSmsFrame->pFStocGain = NULL;
+}
+
+/*! \brief clear the SMS data structure
+ *
+ * \param pSmsFrame pointer to frame of SMS data
+ */
+void sms_clearFrame(SMS_Data *pSmsFrame)
+{
+ memset(pSmsFrame->pSmsData, 0, pSmsFrame->sizeData);
+}
+
+/*! \brief copy a frame of SMS_Data
+ *
+ * \param pCopySmsData copy of frame
+ * \param pOriginalSmsData original frame
+ *
+ */
+void sms_copyFrame(SMS_Data *pCopySmsData, SMS_Data *pOriginalSmsData)
+{
+ /* if the two frames are the same size just copy data */
+ if(pCopySmsData->sizeData == pOriginalSmsData->sizeData &&
+ pCopySmsData->nTracks == pOriginalSmsData->nTracks)
+ {
+ memcpy((char *)pCopySmsData->pSmsData,
+ (char *)pOriginalSmsData->pSmsData,
+ pCopySmsData->sizeData);
+ }
+ /* if frames is different size copy the smallest */
+ else
+ {
+ int nTracks = MIN(pCopySmsData->nTracks, pOriginalSmsData->nTracks);
+ int nCoeff = MIN(pCopySmsData->nCoeff, pOriginalSmsData->nCoeff);
+
+ pCopySmsData->nTracks = nTracks;
+ pCopySmsData->nCoeff = nCoeff;
+ memcpy((char *)pCopySmsData->pFSinFreq,
+ (char *)pOriginalSmsData->pFSinFreq,
+ sizeof(sfloat) * nTracks);
+ memcpy((char *)pCopySmsData->pFSinAmp,
+ (char *)pOriginalSmsData->pFSinAmp,
+ sizeof(sfloat) * nTracks);
+ if(pOriginalSmsData->pFSinPha != NULL &&
+ pCopySmsData->pFSinPha != NULL)
+ memcpy((char *)pCopySmsData->pFSinPha,
+ (char *)pOriginalSmsData->pFSinPha,
+ sizeof(sfloat) * nTracks);
+ if(pOriginalSmsData->pFStocCoeff != NULL &&
+ pCopySmsData->pFStocCoeff != NULL)
+ {
+ if(pOriginalSmsData->pResPhase != NULL &&
+ pCopySmsData->pResPhase != NULL)
+ memcpy((char *)pCopySmsData->pResPhase,
+ (char *)pOriginalSmsData->pResPhase,
+ sizeof(sfloat) * nCoeff);
+ }
+ if(pOriginalSmsData->pFStocGain != NULL &&
+ pCopySmsData->pFStocGain != NULL)
+ memcpy((char *)pCopySmsData->pFStocGain,
+ (char *)pOriginalSmsData->pFStocGain,
+ sizeof(sfloat));
+ }
+}
+
+/*! \brief function to interpolate two SMS frames
+ *
+ * this assumes that the two frames are of the same size
+ *
+ * \param pSmsFrame1 sms frame 1
+ * \param pSmsFrame2 sms frame 2
+ * \param pSmsFrameOut sms output frame
+ * \param fInterpFactor interpolation factor
+ */
+void sms_interpolateFrames(SMS_Data *pSmsFrame1, SMS_Data *pSmsFrame2,
+ SMS_Data *pSmsFrameOut, sfloat fInterpFactor)
+{
+ int i;
+ sfloat fFreq1, fFreq2;
+
+ /* interpolate the deterministic part */
+ for(i = 0; i < pSmsFrame1->nTracks; i++)
+ {
+ fFreq1 = pSmsFrame1->pFSinFreq[i];
+ fFreq2 = pSmsFrame2->pFSinFreq[i];
+ if(fFreq1 == 0)
+ fFreq1 = fFreq2;
+ if(fFreq2 == 0)
+ fFreq2 = fFreq1;
+ pSmsFrameOut->pFSinFreq[i] = fFreq1 + fInterpFactor * (fFreq2 - fFreq1);
+ pSmsFrameOut->pFSinAmp[i] =
+ pSmsFrame1->pFSinAmp[i] + fInterpFactor *
+ (pSmsFrame2->pFSinAmp[i] - pSmsFrame1->pFSinAmp[i]);
+ }
+
+ /* interpolate the stochastic part. The pointer is non-null when the frame contains
+ stochastic coefficients */
+ if(pSmsFrameOut->pFStocGain)
+ {
+ *(pSmsFrameOut->pFStocGain) =
+ *(pSmsFrame1->pFStocGain) + fInterpFactor *
+ (*(pSmsFrame2->pFStocGain) - *(pSmsFrame1->pFStocGain));
+ }
+ /*! \todo how to interpolate residual phase spectrum */
+ for(i = 0; i < pSmsFrame1->nCoeff; i++)
+ pSmsFrameOut->pFStocCoeff[i] =
+ pSmsFrame1->pFStocCoeff[i] + fInterpFactor *
+ (pSmsFrame2->pFStocCoeff[i] - pSmsFrame1->pFStocCoeff[i]);
+
+ /* DO NEXT: interpolate spec env here if fbins */
+ for(i = 0; i < pSmsFrame1->nEnvCoeff; i++)
+ pSmsFrameOut->pSpecEnv[i] =
+ pSmsFrame1->pSpecEnv[i] + fInterpFactor *
+ (pSmsFrame2->pSpecEnv[i] - pSmsFrame1->pSpecEnv[i]);
+}
+