diff options
Diffstat (limited to 'src/sms/fileIO.c')
-rw-r--r-- | src/sms/fileIO.c | 559 |
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]); +} + |