/* * 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 */ void sms_fillHeader(SMS_Header *pSmsHeader, SMS_AnalParams *pAnalParams) { 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]); }