#import <objc/Storage.h>
#import <appkit/nextstd.h>
#import <musickit/musickit.h>
#import <soundkit/Sound.h>
#import "../library/SmsObject.h"
#import <sms/sms.h>

typedef short          HWORD;

#import "SmsInstrument.h"
@implementation SmsInstrument /* See SmsInstrument.h for instance variables */

static int iFilePar = 0,iTimeOffsetPar = 0, iAmpDetPar = 0, iAmpStocPar = 0, 
	iTimeStretchPar = 0, iTimeStretchHybPar = 0, iFreqStretchPar = 0, 
	iAmpEnvPar = 0, iAmpDetEnvPar = 0, iAmpStocEnvPar = 0, iFreqEnvPar = 0, 
	iTimeStretchEnvPar = 0, iTimeStretchHybEnvPar = 0, iFreqStretchEnvPar = 0, 
	iMaxTrajPar = 0, iAmpPartialsEnvPar = 0, iAmpStocCoeffEnvPar = 0,
	iFreqPartialsEnvPar = 0, iFileHybPar = 0, iTimeOffsetHybPar = 0, 
	iDurHybPar = 0, iAmpDetHybPar = 0, iAmpStocHybPar = 0, iFreqHybPar = 0, 
	iAmpDetHybEnvPar = 0, iAmpStocHybEnvPar = 0, iFreqHybEnvPar = 0;

-init
{
	[super init];
	iCurOutSample = -1;
	idSmsObj = nil;
	fDur = 0;
	fTimeOffset = 0;
	fAmp = 1.0;
	fAmpDet = 1.0;
	fAmpStoc = 1.0;
	fFreq1 = 1.0;
	fFreq0 = 0.0;
	fTimeStretch = 1.0;
	fTimeStretchHyb = 0;
	fFreqStretch = 1.0;
	iMaxTraj = 200; 
	idAmpEnv = nil;   
	idAmpDetEnv = nil;
	idAmpStocEnv = nil;    
	idFreqEnv = nil;    
	idTimeStretchEnv = nil; 
	idTimeStretchHybEnv = nil;       
	idFreqStretchEnv = nil; 
	idAmpPartialsEnv = nil;
	idAmpStocCoeffEnv = nil;
	idFreqPartialsEnv = nil;
	iFirstRecord = 0;
	iLastRecord = 0; 
	idSmsObjHyb = nil;
	fTimeOffsetHyb = 0;
	fDurHyb = 0;
	fAmpDetHybFactor = 1;
	fAmpStocHybFactor = 1;
	fFreqHybFactor = 1;
	idAmpDetHybEnv = nil;
	idAmpStocHybEnv = nil;
	idFreqHybEnv = nil;
	newSmsRecord.pFFreqTraj = NULL;
	newSmsRecord.pFMagTraj = NULL;
	newSmsRecord.pFPhaTraj = NULL;
	newSmsRecord.pFMagTraj = NULL;
	newSmsRecord.nTraj = 0;
	newSmsRecord.nCoeff = 0;
	newSmsRecord.pFStocCoeff = NULL;
	newSmsRecord.pFStocGain = NULL;
	iModification = 0;
	iOldSample = 0;
	iNewSample = 0;
    
	iFilePar = [Note parName:"smsFile"];
	iTimeOffsetPar = [Note parName:"timeOffset"];
	iAmpDetPar = [Note parName:"ampDet"];    
	iAmpStocPar = [Note parName:"ampStoc"];
	iTimeStretchPar = [Note parName:"timeStretch"];
	iTimeStretchHybPar = [Note parName:"timeStretchHyb"];
	iFreqStretchPar = [Note parName:"freqStretch"];
	iMaxTrajPar = [Note parName:"maxTraj"];
	iAmpEnvPar = [Note parName:"ampEnv"];
	iAmpDetEnvPar = [Note parName:"ampDetEnv"];
	iAmpStocEnvPar = [Note parName:"ampStocEnv"];
	iFreqEnvPar = [Note parName:"freqEnv"];
	iTimeStretchEnvPar = [Note parName:"timeStretchEnv"];
	iTimeStretchHybEnvPar = [Note parName:"timeStretchHybEnv"];
	iFreqStretchEnvPar = [Note parName:"freqStretchEnv"];
	iAmpPartialsEnvPar = [Note parName:"ampPartialsEnv"];
	iAmpStocCoeffEnvPar = [Note parName:"ampStocCoeffEnv"];
	iFreqPartialsEnvPar = [Note parName:"freqPartialsEnv"];
	iFileHybPar = [Note parName:"smsFileHyb"];
	iTimeOffsetHybPar = [Note parName:"timeOffsetHyb"];
	iDurHybPar = [Note parName:"durHyb"];
	iAmpDetHybPar = [Note parName:"ampDetHybFactor"];
	iAmpStocHybPar = [Note parName:"ampStocHybFactor"];
	iFreqHybPar = [Note parName:"freqHybFactor"];
	iAmpDetHybEnvPar = [Note parName:"ampDetHybEnv"];
	iAmpStocHybEnvPar = [Note parName:"ampStocHybEnv"];
	iFreqHybEnvPar = [Note parName:"freqHybEnv"];

	FSamplingRate = fSamplingRate = 22050;
	FHalfSamplingRate = FSamplingRate / 2;
	/* prepare sinc and sine tables */
	if (pFSincTab == NULL)
		PrepSinc ();
	if (pFSTab == NULL)
		PrepSine (2046);

	[self addNoteReceiver:[[NoteReceiver alloc] init]]; 
	                 /* Need one NoteReceiver */ 
	return self;
}

-setSamplingRate:(double)fSRate stream:(NXStream *)aStream
    /* Invoked once before performance from smssynth.m. */
{
    if ([self inPerformance])
      return nil;
    FSamplingRate = fSamplingRate = fSRate;
    FHalfSamplingRate = FSamplingRate / 2;
    pStream = aStream;
    return self;
}

-firstNote:aNote 
    /* This is invoked when first note is received during performance */
{
    SNDAlloc(&pOutSoundStruct,
	     0 /* data size (we'll set this later) */,
	     SND_FORMAT_LINEAR_16,
	     (int)fSamplingRate,
	     1,
	     0);
    /* Write header */
    NXWrite(pStream,(char *)pOutSoundStruct, sizeof(*pOutSoundStruct));
    return self;
}

-_mixToOutput:(short *)pData length:(int)iLength sample:(int)iFirstSample
{
    int iLastSample = iFirstSample + iLength;
    int i, iLastCommonSample = 0, iLoc = 0, nSamps = 0;
		
    if (iFirstSample > (iCurOutSample + 1))
    {
      short *pBuffer;
      nSamps = iFirstSample - iCurOutSample;
      pBuffer = (short *) calloc (nSamps, sizeof(short));
      NXSeek(pStream, ((iCurOutSample + 1) * sizeof(short)) +
             sizeof(*pOutSoundStruct), NX_FROMSTART);
      NXWrite(pStream,(char *)pBuffer, nSamps * sizeof(short));
      iCurOutSample += nSamps;
      free (pBuffer);
    }
    else if (iCurOutSample >= iFirstSample)
    {
      short *pBuffer;
      NXSeek(pStream, (iFirstSample * sizeof (short)) +
             sizeof(*pOutSoundStruct), NX_FROMSTART);
      iLastCommonSample = MIN (iLastSample, iCurOutSample);
      nSamps = MIN (iLength , 1 + iLastCommonSample - iFirstSample);
      pBuffer = (short *) calloc (nSamps, sizeof(short));
      NXRead (pStream, (char *)pBuffer, sizeof(short) * nSamps);
      for (i = 0; i < nSamps; i++)
        pBuffer[i] += pData[i];
      NXSeek(pStream, (iFirstSample * sizeof (short)) +
		     sizeof(*pOutSoundStruct), NX_FROMSTART);
      NXWrite (pStream, (char *)pBuffer, sizeof(short) * nSamps);    
      iLoc = nSamps - 1;
      free (pBuffer);
    }
    
    if (iLastSample > (iCurOutSample + 1))
    {
      nSamps = iLastSample - (iCurOutSample + 1);
      NXSeek(pStream, ((iCurOutSample + 1) * sizeof (short)) +
             sizeof(*pOutSoundStruct), NX_FROMSTART);
      NXWrite(pStream,(char *)(pData+iLoc), nSamps * sizeof(short));
      iCurOutSample += nSamps;
    }
    NXFlush(pStream);
    return self;
}

-_generateArray:(float *)pFArray size:(int)sizeArray 
                         envelope:(id)idEnvelope
{
	double xfirst, xlast, y, s, fValT = 1, xval, fVal = 0;
	float fXinc;
	int lastPoint = [idEnvelope pointCount] - 1, i;

	if (idEnvelope)
	{
		[idEnvelope getNth:0 x:&xfirst y:&y smoothing:&s];
		[idEnvelope getNth:lastPoint x:&xlast y:&y smoothing:&s];
   
		fXinc = (float) ((xlast - xfirst) / sizeArray);
		xval = xfirst;
		for (i = 0; i < sizeArray; i++)
		{
			pFArray[i] = [idEnvelope lookupYForX:xval];
			xval += fXinc;  
		}
	}
	else
		for (i = 0; i < sizeArray; i++)
			pFArray[i] = 0;

  return self;
}

-_changeDetAmp:(double)fLocation weights:(float *)pFWeights
{
	double xfirst, xlast, y, s, fAmpDetT = 1, fAmpT = 1, fScalar, xval;
    int i, lastPoint = [idAmpDetEnv pointCount] - 1;
    
	if (idAmpDetEnv)
	{
		[idAmpDetEnv getNth:0 x:&xfirst y:&y smoothing:&s];
		[idAmpDetEnv getNth:lastPoint x:&xlast y:&y smoothing:&s];
		xval = xfirst + fLocation * (xlast - xfirst);
		fAmpDetT = [idAmpDetEnv lookupYForX:xval];
	}
    
	lastPoint = [idAmpEnv pointCount] - 1;
	if (idAmpEnv)
	{
		[idAmpEnv getNth:0 x:&xfirst y:&y smoothing:&s];
		[idAmpEnv getNth:lastPoint x:&xlast y:&y smoothing:&s];
		xval = xfirst + fLocation * (xlast - xfirst);
		fAmpT = [idAmpEnv lookupYForX:xval];
	}

	fScalar = fAmpDetT * fAmpT * fAmp * fAmpDet;
    
	if (!pFWeights)
	{
		if (fScalar <= 0) 
			for (i = 0; i < iMaxTraj; i++)  
				newSmsRecord.pFMagTraj[i] = 0;
		else
			for (i = 0; i < iMaxTraj; i++)
				newSmsRecord.pFMagTraj[i] = 
					TO_DB (TO_MAG (newSmsRecord.pFMagTraj[i]) * fScalar);
	}
	else
		for (i = 0; i < iMaxTraj; i++)
		{
			if (pFWeights[i] == 0)
				newSmsRecord.pFMagTraj[i] = 
					TO_DB (TO_MAG (newSmsRecord.pFMagTraj[i]) * fAmpT * fAmp);
			else
				newSmsRecord.pFMagTraj[i] = 
					TO_DB (TO_MAG (newSmsRecord.pFMagTraj[i]) * fScalar * 
				           pFWeights[i]);
		}

    return self;
}	      

-_changeStocAmp:(double)fLocation weights:(float *)pFWeights
{
	double xfirst, xlast, y, s, fAmpStocT = 1, fAmpT = 1, fScalar, xval;
	int i, lastPoint = [idAmpStocEnv pointCount] - 1;

	if (!newSmsRecord.pFStocGain)
		return self;

	if (idAmpStocEnv)
	{
		[idAmpStocEnv getNth:0 x:&xfirst y:&y smoothing:&s];
		[idAmpStocEnv getNth:lastPoint x:&xlast y:&y smoothing:&s];
		xval = xfirst + fLocation * (xlast - xfirst);
		fAmpStocT = [idAmpStocEnv lookupYForX:xval];
	}
    
	lastPoint = [idAmpEnv pointCount] - 1;
	if (idAmpEnv)
	{
		[idAmpEnv getNth:0 x:&xfirst y:&y smoothing:&s];
		[idAmpEnv getNth:lastPoint x:&xlast y:&y smoothing:&s];
		xval = xfirst + fLocation * (xlast - xfirst);
		fAmpT = [idAmpEnv lookupYForX:xval];
	}

	fScalar = fAmpStocT * fAmpT * fAmp * fAmpStoc;
	if (!pFWeights)
	{
		if (fScalar == 0) 
			*(newSmsRecord.pFStocGain) = 0;
		else if (fScalar > 0)
			*(newSmsRecord.pFStocGain) = 
				TO_DB (TO_MAG (*(newSmsRecord.pFStocGain)) * fScalar);
	}
	else
	{  
		for (i = 0; i < newSmsRecord.nCoeff; i++)
			if (pFWeights[i] > 0)
				newSmsRecord.pFStocCoeff[i] *= (pFWeights[i] * fScalar);
	}
	return self;
}	      
    
-_changeFreq:(double)fLocation weights:(float *)pFWeights
{
	double xfirst, xlast, y, s, fFreqT = 1, xval, fVal, fTmp;
	int i, lastPoint = [idFreqEnv pointCount] - 1;

	if (idFreqEnv)
	{
		[idFreqEnv getNth:0 x:&xfirst y:&y smoothing:&s];
		[idFreqEnv getNth:lastPoint x:&xlast y:&y smoothing:&s];
		xval = xfirst + fLocation * (xlast - xfirst);
		fFreqT = [idFreqEnv lookupYForX:xval];
	}
  
    fVal = fFreq0 + fFreqT * (fFreq1 - fFreq0);
    
    if (fVal > 50)
       fprintf (stderr, "transposing by %f, are you sure!\n", fVal);
       
	if (fVal > 0)
	{
		iModification = 1;
		if (pFWeights)
			for (i = 0; i < iMaxTraj; i++)
			{
				fTmp = (fVal * pFWeights[i]);
				if (fTmp > 0) newSmsRecord.pFFreqTraj[i] *= fTmp;
			}
		else
    		for (i = 0; i < iMaxTraj; i++)
					newSmsRecord.pFFreqTraj[i] *= fVal;
    } 
    return self;
}	      

-_stretchPartials:(double)fLocation
{
	double xfirst, xlast, y, s, fFreqStretchT = 1, xval, fVal, fIncr;
	int i, lastPoint = [idFreqStretchEnv pointCount] - 1;
    
	if (idFreqStretchEnv)
	{
		[idFreqStretchEnv getNth:0 x:&xfirst y:&y smoothing:&s];
		[idFreqStretchEnv getNth:lastPoint x:&xlast y:&y smoothing:&s];
		xval = xfirst + fLocation * (xlast - xfirst);
		fFreqStretchT = [idFreqStretchEnv lookupYForX:xval];
	}
  
	fFreqStretchT *= fFreqStretch;
    
	fVal = 1.0;
	fIncr = (fFreqStretchT - 1.0) / iMaxTraj;
    
	/* assume that the new record has been filled */
	if (fIncr != 0)
	{
		iModification = 1;
		for (i = 0; i < iMaxTraj; i++)
		{
			newSmsRecord.pFFreqTraj[i] *= fVal;
			fVal += fIncr;
		}
	}
	return self;
}	      

- _hybridizeDetAmp:(SMS_DATA *)pSmsRecordHyb location:(double)fLocation
{
	double xfirst, xlast, y, s, fAmpDetHybT = 1, fVal, xval;
	int i, lastPoint = 0;
        
	/* hybridize deterministic amplitude */
	lastPoint = [idAmpDetHybEnv pointCount] - 1;
	if (idAmpDetHybEnv)
	{
		[idAmpDetHybEnv getNth:0 x:&xfirst y:&y smoothing:&s];
		[idAmpDetHybEnv getNth:lastPoint x:&xlast y:&y smoothing:&s];
		xval = xfirst + fLocation * (xlast - xfirst);
		fAmpDetHybT = [idAmpDetHybEnv lookupYForX:xval];
	}
	fVal = fAmpDetHybT * fAmpDetHybFactor;

	if (fVal < 0)
	{
		float fMag = newSmsRecord.pFMagTraj[0];
		float fNewVal = fabs ((double) fVal);

		for (i = 0; i < iMaxTraj; i++)
			newSmsRecord.pFMagTraj[i] = 
				(pSmsRecordHyb->pFMagTraj[i] - pSmsRecordHyb->pFMagTraj[0]) +
				(fNewVal * fMag);
	}
	if (fVal > 0 && fVal < 1)
		for (i = 0; i < iMaxTraj; i++)
			newSmsRecord.pFMagTraj[i] = 
				(pSmsRecordHyb->pFMagTraj[i] - newSmsRecord.pFMagTraj[i]) *
					fVal + newSmsRecord.pFMagTraj[i];
	else if (fVal >= 1)
		for (i = 0; i < iMaxTraj; i++)
			newSmsRecord.pFMagTraj[i] = pSmsRecordHyb->pFMagTraj[i];

	return self;
}

- _hybridizeDetFreq:(SMS_DATA *)pSmsRecordHyb location:(double)fLocation
{
	double xfirst, xlast, y, s, fFreqHybT = 1, fVal, xval;
	int i, lastPoint = 0;

	/* hybridize deterministic frequency */
	lastPoint = [idFreqHybEnv pointCount] - 1;
	if (idFreqHybEnv)
	{
		[idFreqHybEnv getNth:0 x:&xfirst y:&y smoothing:&s];
		[idFreqHybEnv getNth:lastPoint x:&xlast y:&y smoothing:&s];
		xval = xfirst + fLocation * (xlast - xfirst);
		fFreqHybT = [idFreqHybEnv lookupYForX:xval];
	}
	fVal = fFreqHybT * fFreqHybFactor;

	if (fVal < 0)
	{
		float fFund = newSmsRecord.pFFreqTraj[0];
		float fNewVal = fabs ((double) fVal);

		for (i = 0; i < iMaxTraj; i++)
			newSmsRecord.pFFreqTraj[i] = 
				(pSmsRecordHyb->pFFreqTraj[i] / pSmsRecordHyb->pFFreqTraj[0]) *
				fNewVal * fFund;
	}
	else if (fVal > 0 && fVal < 1)
		for (i = 0; i < iMaxTraj; i++)
			newSmsRecord.pFFreqTraj[i] = 
				(pSmsRecordHyb->pFFreqTraj[i] - newSmsRecord.pFFreqTraj[i]) *
				fVal + newSmsRecord.pFFreqTraj[i];
	else if (fVal >= 1)
		for (i = 0; i < iMaxTraj; i++)
			newSmsRecord.pFFreqTraj[i] = pSmsRecordHyb->pFFreqTraj[i];

	return self;
}

- _hybridizeStoc:(SMS_DATA *)pSmsRecordHyb location:(double)fLocation
{
	double xfirst, xlast, y, s, fAmpStocHybT = 1, fVal, xval;
	int i, lastPoint = 0;

	/* hybridize stochastic amplitude */
	lastPoint = [idAmpStocHybEnv pointCount] - 1;
	if (idAmpStocHybEnv)
	{
		[idAmpStocHybEnv getNth:0 x:&xfirst y:&y smoothing:&s];
		[idAmpStocHybEnv getNth:lastPoint x:&xlast y:&y smoothing:&s];
		xval = xfirst + fLocation * (xlast - xfirst);
		fAmpStocHybT = [idAmpStocHybEnv lookupYForX:xval];
	}
	fVal = fAmpStocHybT * fAmpStocHybFactor;
    
	if (newSmsRecord.pFStocGain && pSmsRecordHyb->pFStocGain)
	{
		if (fVal > 0 && fVal < 1)
			*(newSmsRecord.pFStocGain) = 
				(*(pSmsRecordHyb->pFStocGain) - *(newSmsRecord.pFStocGain)) *
				fVal + *(newSmsRecord.pFStocGain);
		else if (fVal >= 1)
			*(newSmsRecord.pFStocGain) = *(pSmsRecordHyb->pFStocGain);

		/* hybridize stochastic coefficients */
		if ([idSmsObj getStochasticType] == STOC_FILTER_TYPE)
		{
			if (fVal > .5 && (pSmsRecordHyb->nCoeff == newSmsRecord.nCoeff))
				for (i = 0; i < newSmsRecord.nCoeff; i++)
					newSmsRecord.pFStocCoeff[i] = 
						pSmsRecordHyb->pFStocCoeff[i];
		}
		else if ([idSmsObj getStochasticType] == STOC_MAG_ENV_TYPE)
			InterpolateArrays (newSmsRecord.pFStocCoeff, newSmsRecord.nCoeff,
				               pSmsRecordHyb->pFStocCoeff, 
			                   pSmsRecordHyb->nCoeff,
			                   newSmsRecord.pFStocCoeff, newSmsRecord.nCoeff, 
			                   fVal);
	}

	return self;
}

- _hybridize:(SMS_DATA *)pSmsRecordHyb location:(double)fLocation
{  
	if (idFreqHybEnv || fFreqHybFactor != 0)
		[self _hybridizeDetFreq:pSmsRecordHyb location:fLocation];
      
	if (idAmpDetHybEnv || fAmpDetHybFactor != 0)
		[self _hybridizeDetAmp:pSmsRecordHyb location:fLocation];

	if (idAmpStocHybEnv || fAmpStocHybFactor != 0)
		[self _hybridizeStoc:pSmsRecordHyb location:fLocation];

	return self;
}	     

-(double)_changeTime:(double)fLocation
{			   
	double xfirst, xlast, y, s, fTimeStretchT = 1, xval, fVal;
	int lastPoint = [idTimeStretchEnv pointCount] - 1;
    
	if (idTimeStretchEnv)
	{
		[idTimeStretchEnv getNth:0 x:&xfirst y:&y smoothing:&s];
		[idTimeStretchEnv getNth:lastPoint x:&xlast y:&y smoothing:&s];
		xval = xfirst + fLocation * (xlast - xfirst);
		fTimeStretchT = [idTimeStretchEnv lookupYForX:xval];
	}
  
	fVal = 1 / (fTimeStretch * fTimeStretchT);
	return fVal;
}

-(double)_changeHybTime:(double)fLocation
{			   
	double xfirst, xlast, y, s, fTimeStretchT = 1, xval, fVal;
	int lastPoint = [idTimeStretchHybEnv pointCount] - 1;
    
	if (idTimeStretchHybEnv)
	{
		[idTimeStretchHybEnv getNth:0 x:&xfirst y:&y smoothing:&s];
		[idTimeStretchHybEnv getNth:lastPoint x:&xlast y:&y smoothing:&s];
		xval = xfirst + fLocation * (xlast - xfirst);
		fTimeStretchT = [idTimeStretchHybEnv lookupYForX:xval];
	}
  
	fVal = 1 / (fTimeStretchHyb * fTimeStretchT);
	return fVal;
}



/* This is invoked when performance is over. */
-afterPerformance 
{
	if (!pOutSoundStruct)  /* Did we never received any notes? */
		return self;
	pOutSoundStruct->dataSize = iCurOutSample * sizeof(short);
	NXSeek(pStream, 0, NX_FROMSTART);
	NXWrite(pStream,(char *)pOutSoundStruct, sizeof(*pOutSoundStruct));
	NXFlush(pStream);
	pOutSoundStruct->dataSize = 0;
	SNDFree(pOutSoundStruct);
	pOutSoundStruct = NULL;
	NXSeek(pStream,0,NX_FROMEND);
	NXFlush(pStream);
	return self;
}

-_readParams:aNote
{
	char *pChFileT = [aNote parAsStringNoCopy:iFilePar];
	char *pChFileHybT = "";

	/* get input file */ 
	if (pChFileT && strlen(pChFileT))
		pChFile = pChFileT;
	idSmsObj = (SmsObject *)[[SmsObject alloc] initFromFile:pChFile]; 
	if (!idSmsObj) 
	{
		fprintf(stderr,"Can't find file %s.\n",pChFile);
		return self;
	}

	/* get other input file */
	if ([aNote isParPresent:iFileHybPar])
	{
		pChFileHybT = [aNote parAsStringNoCopy:iFileHybPar];
		if (pChFileHybT && strlen(pChFileHybT))
			pChFileHyb = pChFileHybT;
		idSmsObjHyb = (SmsObject *)[[SmsObject alloc] initFromFile:pChFileHyb]; 
		if (!idSmsObjHyb) 
		{
			fprintf(stderr,"Can't find file %s.\n",pChFileHyb);
			return self;
		}
	}
	/* get amplitude parameters */
	if ([aNote isParPresent:MK_amp])
		fAmp = [aNote parAsDouble:MK_amp];
	if ([aNote isParPresent:MK_velocity])
		fAmp *= MKMidiToAmpAttenuation([aNote parAsInt:MK_velocity]);
	if ([aNote isParPresent:iAmpDetPar]) 
		fAmpDet = [aNote parAsDouble:iAmpDetPar];
	if ([aNote isParPresent:iAmpStocPar]) 
		fAmpStoc = [aNote parAsDouble:iAmpStocPar];
	if ([aNote isParPresent:iAmpEnvPar]) 
		idAmpEnv = (Envelope *)[aNote parAsEnvelope:iAmpEnvPar];
	if ([aNote isParPresent:iAmpDetEnvPar]) 
		idAmpDetEnv = (Envelope *)[aNote parAsEnvelope:iAmpDetEnvPar];
	if ([aNote isParPresent:iAmpPartialsEnvPar]) 
		idAmpPartialsEnv = 
			(Envelope *)[aNote parAsEnvelope:iAmpPartialsEnvPar];
	if ([aNote isParPresent:iAmpStocEnvPar]) 
		idAmpStocEnv = (Envelope *)[aNote parAsEnvelope:iAmpStocEnvPar];
	if ([aNote isParPresent:iAmpStocCoeffEnvPar]) 
		idAmpStocCoeffEnv = 
			(Envelope *)[aNote parAsEnvelope:iAmpStocCoeffEnvPar];
		    
	/* get frequency parameters */
	if ([aNote isParPresent:MK_freq])
		fFreq1 = [aNote parAsDouble:MK_freq];
	if ([aNote isParPresent:MK_freq1])
		fFreq1 = [aNote parAsDouble:MK_freq1];
	if ([aNote isParPresent:MK_freq0])
		fFreq0 = [aNote parAsDouble:MK_freq0];
	if ([aNote isParPresent:MK_freqEnv]) 
		idFreqEnv = (Envelope *)[aNote parAsEnvelope:MK_freqEnv];
	if ([aNote isParPresent:iFreqPartialsEnvPar]) 
		idFreqPartialsEnv = 
			(Envelope *)[aNote parAsEnvelope:iFreqPartialsEnvPar];
	    
	/* get time offset */		    
	if ([aNote isParPresent:iTimeOffsetPar]) 
		fTimeOffset = [aNote parAsDouble:iTimeOffsetPar];
	iFirstRecord = MIN ([idSmsObj getFrameRate] * fTimeOffset,
		                  [idSmsObj getNRecords] - 1);
	/* get duration */
	fDur = [aNote dur];
	if (!MKIsNoDVal(fDur) && fDur != 0) 
		iLastRecord = MIN ([idSmsObj getFrameRate] * fDur + iFirstRecord,
	     	               [idSmsObj getNRecords] - 1);
	if (iLastRecord <= iFirstRecord || fDur <= 0) 
		fprintf(stderr,"Warning: no samples to generate for this file.\n");

	/* get time stretch parameters */
	if ([aNote isParPresent:iTimeStretchPar]) 
		fTimeStretch = [aNote parAsDouble:iTimeStretchPar];
	if ([aNote isParPresent:iTimeStretchEnvPar]) 
		idTimeStretchEnv = 
			(Envelope *)[aNote parAsEnvelope:iTimeStretchEnvPar];

	/* get time stretch parameters */
	if ([aNote isParPresent:iTimeStretchHybPar]) 
		fTimeStretchHyb = [aNote parAsDouble:iTimeStretchHybPar];
	if ([aNote isParPresent:iTimeStretchHybEnvPar]) 
		idTimeStretchHybEnv = 
			(Envelope *)[aNote parAsEnvelope:iTimeStretchHybEnvPar];
      
	/* get frequency stretch parameters */
	if ([aNote isParPresent:iFreqStretchPar]) 
		fFreqStretch = [aNote parAsDouble:iFreqStretchPar];
	if ([aNote isParPresent:iFreqStretchEnvPar]) 
		idFreqStretchEnv = 
			(Envelope *)[aNote parAsEnvelope:iFreqStretchEnvPar];

	if ([aNote isParPresent:iMaxTrajPar])
		iMaxTraj = [aNote parAsInt:iMaxTrajPar];

	/* get parameters for hybridizing process */
	if ([aNote isParPresent:iTimeOffsetHybPar]) 
		fTimeOffsetHyb = [aNote parAsDouble:iTimeOffsetHybPar];
	if ([aNote isParPresent:iDurHybPar]) 
		fDurHyb = [aNote parAsDouble:iDurHybPar];
	if ([aNote isParPresent:iAmpDetHybPar]) 
		fAmpDetHybFactor = [aNote parAsDouble:iAmpDetHybPar];
	if ([aNote isParPresent:iAmpDetHybEnvPar]) 
		idAmpDetHybEnv = (Envelope *)[aNote parAsEnvelope:iAmpDetHybEnvPar];
	if ([aNote isParPresent:iAmpStocHybPar]) 
		fAmpStocHybFactor = [aNote parAsDouble:iAmpStocHybPar];
	if ([aNote isParPresent:iAmpStocHybEnvPar]) 
		idAmpStocHybEnv = (Envelope *)[aNote parAsEnvelope:iAmpStocHybEnvPar];
	if ([aNote isParPresent:iFreqHybPar]) 
		fFreqHybFactor = [aNote parAsDouble:iFreqHybPar];
	if ([aNote isParPresent:iFreqHybEnvPar]) 
		idFreqHybEnv = (Envelope *)[aNote parAsEnvelope:iFreqHybEnvPar];

	return self;
}


/* This is invoked when a new Note is received during performance */
-realizeNote:aNote fromNoteReceiver:aNoteReceiver
{
	MKNoteType type;

	if (!aNote)
		return self;
	type = [aNote noteType]; 
	if(type == MK_noteDur ||  type == MK_noteUpdate)
		[self _readParams:aNote];
	else
		return self;
    	
	if (type == MK_noteUpdate)
		fprintf (stderr, "SmsInstrument cannot handle note updates\n");

	/* if a real note, synthesize */
	if (type == MK_noteDur)
	{  
		SMS_DATA smsRecord1, smsRecord2, smsRecordHyb1, smsRecordHyb2,
			smsRecordTmp;
	 	SYNTH_PARAMS synthParams;
		short *pBuffer;
		int iFirstSample = 0, iRecords, iLeftRecord,
		    iLeftRecordHyb, iFirstRecordHyb, iLastRecordHyb,
			iSizeFrame, iNCoeff;
		float *pFAmpArray = NULL, *pFFreqArray = NULL, *pFAmpStocArray = NULL;
		double fLocation = 0, fLocationHyb = 0, fRecordLoc, fInterpFactor, 
			fRecordLocHyb, fInterpFactorHyb, fSynthesisRateHyb, fCurrentLoc, 
			fCurrentLocHyb;

		if (!idSmsObj)
			return self;
		
		/* Give user feedback */
		fprintf(stderr,"%f ",MKGetTime()); 
		fflush(stderr);
	
		iNCoeff = [idSmsObj getNStochasticCoeff];
		if (idSmsObjHyb)
		{
			if (iMaxTraj >= 0)
				iMaxTraj = MIN ([idSmsObj getNTrajectories],
				                MIN ([idSmsObjHyb getNTrajectories], 
				                     iMaxTraj));
			else
				iMaxTraj = MIN ([idSmsObj getNTrajectories], 
				                [idSmsObjHyb getNTrajectories]);
		}
		else
		{
			if (iMaxTraj >= 0)
				iMaxTraj = MIN ([idSmsObj getNTrajectories], iMaxTraj);
			else
				iMaxTraj = [idSmsObj getNTrajectories]; 
		}

		if (idAmpPartialsEnv)
		{
			if ((pFAmpArray = (float *) calloc(iMaxTraj, sizeof(float))) == 
			     NULL)
				return nil;
			[self _generateArray:pFAmpArray size:iMaxTraj 
				envelope:idAmpPartialsEnv];
		}
		if (idAmpStocCoeffEnv && 
        ([idSmsObj getStochasticType] == STOC_MAG_ENV_TYPE))
		{
			if ((pFAmpStocArray = (float *) calloc(iNCoeff, sizeof(float))) == 
			     NULL)
				return nil;
			[self _generateArray:pFAmpStocArray size:iNCoeff 
				envelope:idAmpStocCoeffEnv];
		}
		if (idFreqPartialsEnv)
		{
			if ((pFFreqArray = (float *) calloc(iMaxTraj, sizeof(float))) == 
			    NULL)
				return nil;
			[self _generateArray:pFFreqArray size:iMaxTraj 
			                     envelope:idFreqPartialsEnv];
		}			

		synthParams.iStochasticType = [idSmsObj getStochasticType];
		synthParams.fOriginalSRate = [idSmsObj getOriginalSRate];
		synthParams.fSamplingRate = fSamplingRate;

		synthParams.sizeHop = iSizeFrame = 128;
		if ((pBuffer = (short *) calloc (iSizeFrame, sizeof (short))) == NULL)
			return nil;
		
		if ((synthParams.pFStocWindow = 
			(float *) calloc(iSizeFrame * 2, sizeof(float))) == NULL)
			return nil;
		Hanning (iSizeFrame * 2, synthParams.pFStocWindow);
		if ((synthParams.pFDetWindow = 
			(float *) calloc(iSizeFrame * 2, sizeof(float))) == NULL)
			return nil;
		IFFTwindow (iSizeFrame * 2, synthParams.pFDetWindow);

		allocateSmsRecord (&newSmsRecord, iMaxTraj, iNCoeff, 0);
		/* allocate the stochastic data with one more coefficient */
		allocateSmsRecord (&(synthParams.previousFrame), iMaxTraj, 1 + iNCoeff, 
		                   1);
		allocateSmsRecord (&smsRecordTmp, iMaxTraj,
	                     [idSmsObjHyb getNStochasticCoeff], 0);

		iRecords = fDur * synthParams.fSamplingRate / synthParams.sizeHop;
		
		if (idSmsObjHyb && fDurHyb > 0)
		{
			iFirstRecordHyb = [idSmsObjHyb getFrameRate] * fTimeOffsetHyb;					
			iLastRecordHyb = 
				[idSmsObjHyb getFrameRate] * fDurHyb + iFirstRecordHyb;
			if (iLastRecordHyb <= iFirstRecordHyb || fDurHyb <= 0) 
				fprintf(stderr,"Warning: no samples to hybridize.\n");
			fSynthesisRateHyb = iRecords / fDurHyb;
		}
       
		/* loop through the input file */
		iFirstSample = MKGetTime() * fSamplingRate;
		fCurrentLoc = fCurrentLocHyb = 0;
		while (fCurrentLoc < fDur)
		{
			fRecordLoc = [idSmsObj getFrameRate] * fCurrentLoc;
			iLeftRecord = iFirstRecord + fRecordLoc;
			fLocation = fCurrentLoc / fDur;
			fInterpFactor = fRecordLoc - (int) fRecordLoc;
	   
			if (iLeftRecord <= iLastRecord)
				[idSmsObj setSmsRecord:iLeftRecord record:&smsRecord1];
		
			if ((iLeftRecord + 1) <= iLastRecord)
			 [idSmsObj setSmsRecord:(1+ iLeftRecord) record:&smsRecord2];
	
			InterpolateSmsRecords (&smsRecord1, &smsRecord2, &newSmsRecord, 
				                     fInterpFactor);
   
			if (idSmsObjHyb && fDurHyb > 0)
			{
				fLocationHyb = fCurrentLocHyb / fDurHyb;
				fRecordLocHyb = [idSmsObjHyb getFrameRate] * fCurrentLocHyb;
				iLeftRecordHyb = iFirstRecordHyb + fRecordLocHyb;
				fInterpFactorHyb = fRecordLocHyb - (int) fRecordLocHyb;

				if (iLeftRecordHyb <= iLastRecordHyb)
					[idSmsObjHyb setSmsRecord:iLeftRecordHyb 
						record:&smsRecordHyb1];
		
				if ((iLeftRecordHyb + 1) <= iLastRecordHyb)
					[idSmsObjHyb setSmsRecord:(1+ iLeftRecordHyb) 
						record:&smsRecordHyb2];

				InterpolateSmsRecords (&smsRecordHyb1, &smsRecordHyb2, 
				                       &smsRecordTmp, fInterpFactorHyb);

				[self _hybridize:&smsRecordTmp location:fLocation];
			}
	  
			[self _changeDetAmp:fLocation weights:pFAmpArray];
			[self _changeStocAmp:fLocation weights:pFAmpStocArray];
			[self _changeFreq:fLocation weights:pFFreqArray];
			[self _stretchPartials:fLocation];
	  	
	 		SmsSynthesis(&newSmsRecord, pBuffer, &synthParams);
			[self _mixToOutput:pBuffer length:iSizeFrame sample:iFirstSample];
			iFirstSample += iSizeFrame;
			fCurrentLoc += 
				(synthParams.sizeHop / synthParams.fSamplingRate) * 
			    [self _changeTime:fLocation];
			if (idSmsObjHyb)
				if (fTimeStretchHyb)
					fCurrentLocHyb += 
						(1 / fSynthesisRateHyb) * 
						[self _changeHybTime:fLocationHyb];
				else
					fCurrentLocHyb += 
						(1 / fSynthesisRateHyb) * 
						[self _changeTime:fLocation];
		}
		freeSmsRecord (&newSmsRecord);
		freeSmsRecord (&smsRecordTmp);
		freeSmsRecord (&(synthParams.previousFrame));
		free (synthParams.pFStocWindow);
		free (synthParams.pFDetWindow);
		if (idSmsObjHyb) [idSmsObjHyb free];
		if (pFAmpArray) free (pFAmpArray);
		if (pFFreqArray) free (pFFreqArray);
		if (pFAmpStocArray) free (pFAmpStocArray);
		[idSmsObj free];
		free (pBuffer);
	}
	return self;
}

@end

