/* * Copyright (c) 2009 John Glover, National University of Ireland, Maynooth * * 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 modify.c * \brief modify sms data */ #include "sms.h" /*! \brief initialize a modifications structure based on an SMS_Header * * \param params pointer to parameter structure * \param header pointer to sms header */ void sms_initModify(SMS_Header *header, SMS_ModifyParams *params) { static int sizeEnvArray = 0; params->maxFreq = header->iMaxFreq; params->sizeSinEnv = header->nEnvCoeff; if(sizeEnvArray < params->sizeSinEnv) { if(sizeEnvArray != 0) free(params->sinEnv); if ((params->sinEnv = (sfloat *) malloc(params->sizeSinEnv * sizeof(sfloat))) == NULL) { sms_error("could not allocate memory for envelope array"); return; } sizeEnvArray = params->sizeSinEnv; } params->ready = 1; } /*! \brief initialize modification parameters * * \todo call this from sms_initSynth()? some other mod params are updated there * * \param params pointer to parameters structure */ void sms_initModifyParams(SMS_ModifyParams *params) { params->ready = 0; params->doResGain = 0; params->resGain = 1.; params->doTranspose = 0; params->transpose = 0; params->doSinEnv = 0; params->sinEnvInterp = 0.; params->sizeSinEnv = 0; params->doResEnv = 0; params->resEnvInterp = 0.; params->sizeResEnv = 0; } /*! \brief free memory allocated during initialization * * \param params pointer to parameter structure */ void sms_freeModify(SMS_ModifyParams *params) { } /*! \brief linear interpolation between 2 spectral envelopes. * * The values in env2 are overwritten by the new interpolated envelope values. */ void sms_interpEnvelopes(int sizeEnv, sfloat *env1, sfloat *env2, sfloat interpFactor) { if(sizeEnv <= 0) { return; } int i; sfloat amp1, amp2; for(i = 0; i < sizeEnv; i++) { amp1 = env1[i]; amp2 = env2[i]; if(amp1 <= 0) amp1 = amp2; if(amp2 <= 0) amp2 = amp1; env2[i] = amp1 + (interpFactor * (amp2 - amp1)); } } /*! \brief apply the spectral envelope of 1 sound to another * * Changes the amplitude of spectral peaks in a target sound (pFreqs, pMags) to match those * in the envelope (pCepEnvFreqs, pCepEnvMags) of another, up to a maximum frequency of maxFreq. */ void sms_applyEnvelope(int numPeaks, sfloat *pFreqs, sfloat *pMags, int sizeEnv, sfloat *pEnvMags, int maxFreq) { if(sizeEnv <= 0 || maxFreq <= 0) { return; } int i, envPos; sfloat frac, binSize = (sfloat)maxFreq / (sfloat)sizeEnv; for(i = 0; i < numPeaks; i++) { /* convert peak freqs into bin positions for quicker envelope lookup */ /* \todo try to remove so many pFreq lookups and get rid of divide */ pFreqs[i] /= binSize; /* if current peak is within envelope range, set its mag to the envelope mag */ if(pFreqs[i] < (sizeEnv-1) && pFreqs[i] > 0) { envPos = (int)pFreqs[i]; frac = pFreqs[i] - envPos; if(envPos < sizeEnv - 1) { pMags[i] = ((1.0 - frac) * pEnvMags[envPos]) + (frac * pEnvMags[envPos+1]); } else { pMags[i] = pEnvMags[sizeEnv-1]; } } else { pMags[i] = 0; } /* convert back to frequency values */ pFreqs[i] *= binSize; } } /*! \brief scale the residual gain factor * * \param frame pointer to sms data * \param gain factor to scale the residual */ void sms_resGain(SMS_Data *frame, sfloat gain) { int i; for( i = 0; i < frame->nCoeff; i++) frame->pFStocCoeff[i] *= gain; } /*! \brief basic transposition * Multiply the frequencies of the deterministic component by a constant */ void sms_transpose(SMS_Data *frame, sfloat transpositionFactor) { int i; for(i = 0; i < frame->nTracks; i++) { frame->pFSinFreq[i] *= sms_scalarTempered(transpositionFactor); } } /*! \brief transposition maintaining spectral envelope * * Multiply the frequencies of the deterministic component by a constant, then change * their amplitudes so that the original spectral envelope is maintained */ void sms_transposeKeepEnv(SMS_Data *frame, sfloat transpositionFactor, int maxFreq) { sms_transpose(frame, transpositionFactor); sms_applyEnvelope(frame->nTracks, frame->pFSinFreq, frame->pFSinAmp, frame->nEnvCoeff, frame->pSpecEnv, maxFreq); } /*! \brief modify a frame (SMS_Data object) * * Performs a modification on a SMS_Data object. The type of modification and any additional * parameters are specified in the given SMS_ModifyParams structure. */ void sms_modify(SMS_Data *frame, SMS_ModifyParams *params) { if(params->doResGain) sms_resGain(frame, params->resGain); if(params->doTranspose) sms_transpose(frame, params->transpose); if(params->doSinEnv) { if(params->sinEnvInterp < .00001) /* maintain original */ sms_applyEnvelope(frame->nTracks, frame->pFSinFreq, frame->pFSinAmp, frame->nEnvCoeff, frame->pSpecEnv, params->maxFreq); else { if(params->sinEnvInterp > .00001 && params->sinEnvInterp < .99999) sms_interpEnvelopes(params->sizeSinEnv, frame->pSpecEnv, params->sinEnv, params->sinEnvInterp); sms_applyEnvelope(frame->nTracks, frame->pFSinFreq, frame->pFSinAmp, params->sizeSinEnv, params->sinEnv, params->maxFreq); } } }