/*

This software module was originally developed by 

Luke Dahl, Brian Link (E-mu Systems)


in the course of development of the MPEG-4 Audio (ISO/IEC 14496-3) standard. 
This software module is an implementation of a part of one or more 
MPEG-4 Audio (ISO/IEC 14496-3) tools as specified by the MPEG-4 Audio 
(ISO/IEC 14496-3). ISO/IEC gives users of the MPEG-4 Audio (ISO/IEC 14496-3) 
free license to this software module or modifications thereof for use 
in hardware or software products claiming conformance to the MPEG-4 Audio 
(ISO/IEC 14496-3). Those intending to use this software module in hardware 
or software products are advised that its use may infringe existing patents. 
The original developer of this software module and his/her company, the 
subsequent editors and their companies, and ISO/IEC have no liability for 
use of this software module or modifications thereof in an implementation. 
Copyright is not released for non MPEG-4 Audio (ISO/IEC 14496-3) conforming 
products. E-mu Systems retains full right to use the code for his/her own 
purpose, assign or donate the code to a third party and to inhibit third 
parties from using the code for non MPEG-4 Audio (ISO/IEC 14496-3) 
conforming products. This copyright notice must be included in all copies 
or derivative works. 

Copyright (C) 1997 E-mu Systems, Inc.

*/
/*****************************************************************************
;
;	sf_param_trans.c
;
;	Parameter Transformation functions and tables.
;
;	History:
;		created	9/97	Luke Dahl, Brian Link
;
*****************************************************************************/



#include <math.h>
#include "sf_wave_def.h"
#include "sf_wave_equ.h"
#include "sf_param_trans.h"

#define PITCHOCTOFFSET 10
#define NUMOCT 16
#define MAXOCTAVESDOWN (-PITCHOCTOFFSET)
#define MAXOCTAVESUP   (NUMOCT - PITCHOCTOFFSET - 1)

/* Globals */
double PitchOctLookup[NUMOCT] = 
{
	0.0009765625, 0.001953125, 0.00390625, 0.0078125, 0.015625, 0.03125, 0.0625, 0.125, 0.25, 0.5, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0 
};

double PitchSemiLookup[13] =
{
	1.0,
	1.05946,
	1.12246,
	1.18921,
	1.25992,
	1.33484,
	1.41421,
	1.49831,
	1.58740,
	1.68179,
	1.78180,
	1.88775,
	2.0
};


/* Functions: */
/*	Parameter DiffCents is (DesiredCents - SampleCents) */
double CalcPhaseInc(int DiffCents)
{
	int Octaves, Semi;
	double PhaseInc;
	double Fraction, SemiFrac;
	
	Octaves = (int) (DiffCents / 1200);
	if (DiffCents >= 0)
	{
		if (Octaves >= MAXOCTAVESUP)		
		{
			PhaseInc = PitchOctLookup[MAXOCTAVESUP + PITCHOCTOFFSET]; 
			return (PhaseInc);
		}
	}
	else
	{
		if (DiffCents % 1200 < 0)
		{
			Octaves -= 1;
			DiffCents += -Octaves * 1200;
		}
		if (Octaves < MAXOCTAVESDOWN) 
		{
			PhaseInc = PitchOctLookup[0];		
			return (PhaseInc);
		}
	}

	Semi = (int)((DiffCents % 1200) / 100);
	SemiFrac = ((DiffCents % 1200) / 100.0) - Semi;

/*	Linearly interpolate between semitones */

	assert(Semi >= 0 && Semi <= 12);
	assert(Octaves + PITCHOCTOFFSET >= 0 && Octaves + PITCHOCTOFFSET <= NUMOCT);

	Fraction = (1.0 - SemiFrac) * PitchSemiLookup[Semi] + 
			SemiFrac * PitchSemiLookup[Semi + 1];
	PhaseInc = PitchOctLookup[Octaves + PITCHOCTOFFSET] * Fraction;
	return (PhaseInc);
}

/* Convert from raw SF units to native units for this synhesizer */
void sf_xlat(sfData *rawsp, sfStuff *sfBank, struct SynthParams *nativesp, 
		int srate, int krate)
{
	short origKey;			/* must be 16 bits */
	short correction;		/* must be 16 bits */
	
#ifdef DEBUG_XLAT
	printf("dwStart %d\n", rawsp->dwStart);
	printf("dwEnd %d\n", rawsp->dwEnd);
	printf("dwStartloop %d\n", rawsp->dwStartloop);
	printf("dwEndloop %d\n", rawsp->dwEndloop);
	printf("dwSampleRate %d\n", rawsp->dwSampleRate);
	printf("shOrigKeyAndCorr 0x%x\n", rawsp->shOrigKeyAndCorr);
	printf("shSampleModes %d\n", rawsp->shSampleModes); /*SampleType */
	printf("shSampleLink %d\n", rawsp->shSampleLink);
#endif

	/************************************************/
	/*** convert sample addresses for oscillator  ***/
	/************************************************/
	nativesp->Start = sfBank->samples + rawsp->dwStart + 
		32768 * rawsp->shStartAddrsCoarseOffset;
	nativesp->End = sfBank->samples + rawsp->dwEnd +
		32768 * rawsp->shEndAddrsCoarseOffset;
	nativesp->Startloop = sfBank->samples + rawsp->dwStartloop +
		32768 * rawsp->shStartloopAddrsCoarseOffset;
	nativesp->Endloop = sfBank->samples + rawsp->dwEndloop +
		32768 * rawsp->shEndloopAddrsCoarseOffset;

	if (rawsp->shSampleModes == 0)
		nativesp->SampleMode = LOOP_NONE;
	else if (rawsp->shSampleModes == 3)
		nativesp->SampleMode = LOOP_HOLD;
	else
		nativesp->SampleMode = LOOP_CONT;

	
#ifdef DEBUG_XLAT
	printf("\t (start address of samples %d)\n", sfBank->samples);
	printf("\t Start %d\n", nativesp->Start);
	printf("\t End %d\n", nativesp->End);
	printf("\t Startloop %d\n", nativesp->Startloop);
	printf("\t Endloop %d\n", nativesp->Endloop);
	
	if(nativesp->SampleMode == LOOP_CONT)
		printf("\t SampleMode = LOOP_CONT\n");
	if(nativesp->SampleMode == LOOP_NONE)
		printf("\t SampleMode = LOOP_NONE\n");
	if(nativesp->SampleMode == LOOP_HOLD)
		printf("\t SampleMode = LOOP_HOLD\n");


	/* set in start voice */
	printf("\t curSamp %d\n", nativesp->curSamp);
	printf("\t curFract %f\n", nativesp->curFract);
	
	printf("shCoarseTune %d\n", rawsp->shCoarseTune);
	printf("shFineTune %d\n", rawsp->shFineTune);
	printf("shScaleTuning %d\n", rawsp->shScaleTuning);
	printf("shModLfoToPitch %d\n", rawsp->shModLfoToPitch);
	printf("shVibLfoToPitch %d\n", rawsp->shVibLfoToPitch);
	printf("shModEnvToPitch %d\n", rawsp->shModEnvToPitch);
#endif

	/************************************************/
	/*** convert to desired sample pitch in cents ***/
	/************************************************/

	/* want top byte as signed; gives MIDI keynum */
	origKey = (rawsp->shOrigKeyAndCorr & 0xff00) >> 8;
	if (origKey < 0 || origKey > 127)	/* constrain to legal range */
		origKey = 60;

	/* want bottom byte as signed; give correction in cents */
	correction = (short)(rawsp->shOrigKeyAndCorr << 8) >> 8;
	/* may be overridden */
	if (rawsp->shOverridingRootKey >= 0 && rawsp->shOverridingRootKey <= 127)
	{
		origKey = rawsp->shOverridingRootKey;
	}

	/* This assume a sample rate of 44100. */
	nativesp->InitPitch =
		(rawsp->shKeynum - origKey) * rawsp->shScaleTuning +
		+ rawsp->shCoarseTune * 100. + rawsp->shFineTune + correction;

	nativesp->SFLfo1ToPitchVal = (double)rawsp->shModLfoToPitch;
	nativesp->SFLfo2ToPitchVal = (double)rawsp->shVibLfoToPitch;
	nativesp->Lfo1ToPitch = (double)rawsp->shModLfoToPitch;
	nativesp->Lfo2ToPitch = (double)rawsp->shVibLfoToPitch;
	nativesp->PitchEnvToPitch = (double)rawsp->shModEnvToPitch;

#ifdef DEBUG_XLAT
	printf("\t (origKey %d\tcorrection %d)\n", origKey, correction);

	printf("\t InitPitch %d cents\n", nativesp->InitPitch);
	printf("\t PhaseInc %f\n", nativesp->PhaseInc);		/* set in env_gen */
	printf("\t PitchEnvToPitch %f cents\n", nativesp->PitchEnvToPitch);
	printf("\t Lfo1ToPitch %f cents\n", nativesp->Lfo1ToPitch);
	printf("\t Lfo2ToPitch %f cents\n", nativesp->Lfo2ToPitch);
	printf("\t SFLfo1ToPitchVal %d cents\n", nativesp->SFLfo1ToPitchVal);
	printf("\t SFLfo2ToPitchVal %d cents\n", nativesp->SFLfo2ToPitchVal);

	printf("shInitialFilterFc %d\n", rawsp->shInitialFilterFc);
	printf("shInitialFilterQ %d\n", rawsp->shInitialFilterQ);
	printf("shModLfoToFilterFc %d\n", rawsp->shModLfoToFilterFc);
	printf("shModEnvToFilterFc %d\n", rawsp->shModEnvToFilterFc);
#endif

	/************************************************/
	/*** copy over filter parms					  ***/
	/************************************************/
	
	/* FIX ME - probably needs conversion when filter is done */
	nativesp->FilterFc = (double) rawsp->shInitialFilterFc -
					2400. * (127. - rawsp->shVelocity) / 128.;
	nativesp->InitFilterFc = nativesp->FilterFc;
	nativesp->FilterQ = (double) rawsp->shInitialFilterQ;
	nativesp->Lfo1ToFilterFc = (double) rawsp->shModLfoToFilterFc;
	nativesp->PitchEnvToFilterFc = (double) rawsp->shModEnvToFilterFc;

#ifdef DEBUG_XLAT
	printf("\t FilterFc %f cents\n", nativesp->FilterFc);
	printf("\t InitFilterFc %f cents\n", nativesp->InitFilterFc);
	printf("\t FilterQ %f cB\n", nativesp->FilterQ);
	printf("\t Lfo1ToFilterFc %f cents\n", nativesp->Lfo1ToFilterFc);
	printf("\t PitchEnvToFilterFc %f cents\n", nativesp->PitchEnvToFilterFc);

	printf("shInstVol %d\n", rawsp->shInstVol);
	printf("shModLfoToVolume %d\n", rawsp->shModLfoToVolume);
#endif

	/************************************************/
	/*** copy attenuation values				  ***/
	/************************************************/

	nativesp->SFVolValue = rawsp->shInstVol;
 	/* Attenuation is scaled by velocity according to 20*log10((vel / 127.0)^2) in dB */
	/* We keep attenuation in cB */
	nativesp->Attenuation = (rawsp->shInstVol * 0.4) - 400.0 * log10(rawsp->shVelocity / 127.0);  
	nativesp->Tremolo = rawsp->shModLfoToVolume;

#ifdef DEBUG_XLAT
	printf("\t SFVolValue %d\n", nativesp->SFVolValue);
	printf("\t Attenuation %f\n", nativesp->Attenuation);	
	printf("\t Velocity %d\n", rawsp->shVelocity);
	printf("\t Vol %f\n", nativesp->Vol);
	printf("\t VolInc %f\n", nativesp->VolInc);
	printf("\t Tremolo %f\n", nativesp->Tremolo);

	printf("shChorusEffectsSend %d\n", rawsp->shChorusEffectsSend);
	printf("shReverbEffectsSend %d\n", rawsp->shReverbEffectsSend);
	printf("shPanEffectsSend %d\n", rawsp->shPanEffectsSend);
#endif

	/************************************************/
	/*** convert bus sends						  ***/
	/************************************************/

	nativesp->SFPanValue = rawsp->shPanEffectsSend;    /* kept for Cont Ctrl */
	nativesp->SFRvbSendVal = rawsp->shReverbEffectsSend;
	nativesp->SFChsSendVal = rawsp->shChorusEffectsSend;

	if (rawsp->shPanEffectsSend < -500)
	{
		nativesp->LSend = 1.0;
		nativesp->RSend = 0;
	}
	else if (rawsp->shPanEffectsSend > 500)
	{
		nativesp->LSend = 0;
		nativesp->RSend = 1.0;
	}
	else
	{
		nativesp->LSend = -0.001 * (rawsp->shPanEffectsSend - 500.);
		nativesp->RSend = 0.001 * (rawsp->shPanEffectsSend + 500.);
	}

	if (rawsp->shReverbEffectsSend < 0)
		nativesp->RvbSend = 0;
	else if (rawsp->shReverbEffectsSend > 1000)
		nativesp->RvbSend = 1.0;
	else
		nativesp->RvbSend = 0.001 * rawsp->shReverbEffectsSend;

	if (rawsp->shChorusEffectsSend < 0)
		nativesp->ChsSend = 0;
	else if (rawsp->shChorusEffectsSend > 1000)
		nativesp->ChsSend = 1.0;
	else
		nativesp->ChsSend = 0.001 * rawsp->shChorusEffectsSend;

#ifdef DEBUG_XLAT
	printf("\t SFPanValue %d\n", nativesp->SFPanValue);
	printf("\t LSend %f\n", nativesp->LSend);
	printf("\t RSend %f\n", nativesp->RSend);
	printf("\t SFRvbSendVal %d\n", nativesp->SFRvbSendVal);
	printf("\t SFChsSendVal %d\n", nativesp->SFChsSendVal);
	printf("\t RvbSend %f\n", nativesp->RvbSend);
	printf("\t ChsSend %f\n", nativesp->ChsSend);

	printf("shDelayModLfo %d\n", rawsp->shDelayModLfo);
	printf("shFreqModLfo %d\n", rawsp->shFreqModLfo);
#endif

	/************************************************/
	/*** convert Lfo1 (Mod) values				  ***/
	/************************************************/

	/* (seconds of delay) * calls/second = (# calls to achieve delay) */
	nativesp->Lfo1.delay =
		(int)(TimeCentsToSeconds(rawsp->shDelayModLfo) * krate);
	/* (magnitude of saw) * periods/sec / calls/sec = amp change per call */
	nativesp->Lfo1.freq =
		2. * CentsToHertz(rawsp->shFreqModLfo) / krate;

#ifdef DEBUG_XLAT
	printf("\t Lfo1.delay %d\n", nativesp->Lfo1.delay);
	printf("\t Lfo1.freq %f\n", nativesp->Lfo1.freq);

	printf("shDelayVibLfo %d\n", rawsp->shDelayVibLfo);
	printf("shFreqVibLfo %d\n", rawsp->shFreqVibLfo);
#endif

	/************************************************/
	/*** convert Lfo2 (Vib) values				  ***/
	/************************************************/

	/* (seconds of delay) * calls/second = (# calls to achieve delay) */
	nativesp->Lfo2.delay =
		(int)(TimeCentsToSeconds(rawsp->shDelayVibLfo) * krate);
	/* (magnitude of saw) * periods/sec / calls/sec = amp change per call */
	nativesp->Lfo2.freq =
		2. * CentsToHertz(rawsp->shFreqVibLfo) / krate;

#ifdef DEBUG_XLAT
	printf("\t Lfo2.delay %d\n", nativesp->Lfo2.delay);
	printf("\t Lfo2.freq %f\n", nativesp->Lfo2.freq);

	printf("shDelayModEnv %d\n", rawsp->shDelayModEnv);
	printf("shAttackModEnv %d\n", rawsp->shAttackModEnv);
	printf("shHoldModEnv %d\n", rawsp->shHoldModEnv);
	printf("shDecayModEnv %d\n", rawsp->shDecayModEnv);
	printf("shSustainModEnv %d\n", rawsp->shSustainModEnv);
	printf("shReleaseModEnv %d\n", rawsp->shReleaseModEnv);
	printf("shAutoHoldModEnv %d\n", rawsp->shAutoHoldModEnv);
	printf("shAutoDecayModEnv %d\n", rawsp->shAutoDecayModEnv);
#endif

	/************************************************/
	/*** convert Pitch Envelope (Mod) values	  ***/
	/************************************************/

	if (rawsp->shDelayModEnv == -32768)
		nativesp->PitchEnv.delay = 0;
	else
	/* (seconds of delay) * calls/second = (# calls to achieve delay) */
		nativesp->PitchEnv.delay = 
			(int)(TimeCentsToSeconds(rawsp->shDelayModEnv) * krate);

	if (rawsp->shAttackModEnv == -32768)
		nativesp->PitchEnv.attack = 1.0;		/* reach peak immediately */
	else
	/* (magnitude of 1.0) / ((attack time) * calls/second) = increment per call */
		nativesp->PitchEnv.attack =
			1. / (TimeCentsToSeconds(rawsp->shAttackModEnv) * krate);

	if (rawsp->shHoldModEnv == -32768)
		nativesp->PitchEnv.hold = 0;
	else
	/* (seconds of hold) * calls/second = (# calls to achieve hold) */
		nativesp->PitchEnv.hold = 
			(int)(TimeCentsToSeconds(rawsp->shHoldModEnv) * krate);

	nativesp->PitchEnv.decay =
		pow(10., (-100./20) / (TimeCentsToSeconds(rawsp->shDecayModEnv) * krate));

	if (rawsp->shSustainModEnv < 0)
		nativesp->PitchEnv.sustain = 1.0;
	else if (rawsp->shSustainModEnv > 1000)
		nativesp->PitchEnv.sustain = 0;
	else
		nativesp->PitchEnv.sustain = 0.001 * (1000. - rawsp->shSustainModEnv);

	nativesp->PitchEnv.release =
		pow(10., (-100./20) / (TimeCentsToSeconds(rawsp->shReleaseModEnv) * krate));

#ifdef DEBUG_XLAT
	printf("\t PitchEnv.delay %d\n", nativesp->PitchEnv.delay);
	printf("\t PitchEnv.attack %f\n", nativesp->PitchEnv.attack);
	printf("\t PitchEnv.hold %d\n", nativesp->PitchEnv.hold);
	printf("\t PitchEnv.decay %f\n", nativesp->PitchEnv.decay);
	printf("\t PitchEnv.sustain %f\n", nativesp->PitchEnv.sustain);
	printf("\t PitchEnv.release %f\n", nativesp->PitchEnv.release);

	printf("shDelayVolEnv %d\n", rawsp->shDelayVolEnv);
	printf("shAttackVolEnv %d\n", rawsp->shAttackVolEnv);
	printf("shHoldVolEnv %d\n", rawsp->shHoldVolEnv);
	printf("shDecayVolEnv %d\n", rawsp->shDecayVolEnv);
	printf("shSustainVolEnv %d\n", rawsp->shSustainVolEnv);
	printf("shReleaseVolEnv %d\n", rawsp->shReleaseVolEnv);
	printf("shAutoHoldVolEnv %d\n", rawsp->shAutoHoldVolEnv);
	printf("shAutoDecayVolEnv %d\n", rawsp->shAutoDecayVolEnv);
#endif

	/************************************************/
	/*** convert Volume Envelope (Vol) values	  ***/
	/************************************************/

	if (rawsp->shDelayVolEnv == -32768)
		nativesp->VolEnv.delay = 0;
	else
	/* (seconds of delay) * calls/second = (# calls to achieve delay) */
		nativesp->VolEnv.delay = 
			(int)(TimeCentsToSeconds(rawsp->shDelayVolEnv) * krate);

	if (rawsp->shAttackVolEnv == -32768)
		nativesp->VolEnv.attack = 1.0;		/* reach peak immediately */
	else
	/* (magnitude of 1.0) / ((attack time) * calls/second) = increment per call */
		nativesp->VolEnv.attack =
			1. / (TimeCentsToSeconds(rawsp->shAttackVolEnv) * krate);

	if (rawsp->shHoldVolEnv == -32768)
		nativesp->VolEnv.hold = 0;
	else
	/* (seconds of hold) * calls/second = (# calls to achieve hold) */
		nativesp->VolEnv.hold = 
			(int)(TimeCentsToSeconds(rawsp->shHoldVolEnv) * krate);

	nativesp->VolEnv.decay =
		pow(10., (-100./20) / (TimeCentsToSeconds(rawsp->shDecayVolEnv) * krate));

	if (rawsp->shSustainVolEnv < 0)
		nativesp->VolEnv.sustain = 1.0;
	else if (rawsp->shSustainVolEnv > 1000)
		nativesp->VolEnv.sustain = 0;
	else
		nativesp->VolEnv.sustain = pow(10., -0.1 * rawsp->shSustainVolEnv / 20.);

	nativesp->VolEnv.release =
		pow(10., (-100./20) / (TimeCentsToSeconds(rawsp->shReleaseVolEnv) * krate));

#ifdef DEBUG_XLAT
	printf("\t VolEnv.delay %d\n", nativesp->VolEnv.delay);
	printf("\t VolEnv.attack %f\n", nativesp->VolEnv.attack);
	printf("\t VolEnv.hold %d\n", nativesp->VolEnv.hold);
	printf("\t VolEnv.decay %f\n", nativesp->VolEnv.decay);
	printf("\t VolEnv.sustain %f\n", nativesp->VolEnv.sustain);
	printf("\t VolEnv.release %f\n", nativesp->VolEnv.release);

	printf("\t EnvMessage %d\n", nativesp->EnvMessage);
	printf("\t InstantAttack %d\n", nativesp->InstantAttack);

	printf("shKeyExclusiveClass %d\n", rawsp->shKeyExclusiveClass);

	printf("shKeynum %d\n", rawsp->shKeynum);
	printf("shVelocity %d\n", rawsp->shVelocity);

	printf("shStartAddrsCoarseOffset %d\n", rawsp->shStartAddrsCoarseOffset);
	printf("shEndAddrsCoarseOffset %d\n", rawsp->shEndAddrsCoarseOffset);
	printf("shStartloopAddrsCoarseOffset %d\n", rawsp->shStartloopAddrsCoarseOffset);
	printf("shEndloopAddrsCoarseOffset %d\n", rawsp->shEndloopAddrsCoarseOffset);
	printf("shOverridingRootKey %d\n", rawsp->shOverridingRootKey);

	printf("shNOP %d\n", rawsp->shNOP);
	printf("shEndOper %d\n", rawsp->shEndOper);

	printf("\t Chan %d\n", nativesp->Chan);
	printf("\t Note %d\n", nativesp->Note);
	printf("\t InSustain %d\n", nativesp->InSustain);
	printf("\t Pending %d\n", nativesp->Pending);
#endif

}

double TimeCentsToSeconds(double tc)
{
	return (exp(tc * log(2) / 1200.));	/* log(x)/log(2) give log base 2 of x */
}

double CentsToHertz(double c)
{
	return (8.176 * exp(c * log(2) / 1200.));
}

