/* OscBankPlayer.c */
/*****************************************************************************/
/*                                                                           */
/*    Out Of Phase:  Digital Music Synthesis on General Purpose Computers    */
/*    Copyright (C) 1994  Thomas R. Lawrence                                 */
/*                                                                           */
/*    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., 675 Mass Ave, Cambridge, MA 02139, USA.              */
/*                                                                           */
/*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
/*                                                                           */
/*****************************************************************************/

#include "MiscInfo.h"
#include "Audit.h"
#include "Debug.h"
#include "Definitions.h"

#define ShowMeFrozenNoteRec
#define ShowMe_NoteObjectRec
#include "OscBankPlayer.h"
#include "LFOGenerator.h"
#include "IncrementalParameterUpdator.h"
#include "LFOSpecifier.h"
#include "LFOListSpecifier.h"
#include "Memory.h"
#include "Array.h"
#include "OscillatorListSpecifier.h"
#include "OscillatorSpecifier.h"
#include "InstrumentStructure.h"
#include "SampleOscControl.h"
#include "WaveTableOscControl.h"
#include "DeterminedNoteStructure.h"
#include "NoteObject.h"
#include "FloatingPoint.h"
#include "Frequency.h"
#include "ErrorDaemon.h"


typedef struct
	{
		/* this is the reference to the template object */
		void*										TemplateReference;

		/* perform one envelope update cycle, and set a new frequency for a state */
		/* object.  used for portamento and modulation of frequency (vibrato) */
		void										(*UpdateEnvelopes)(void* State, float NewFrequencyHertz);
		/* dispose of the state record */
		void										(*DisposeState)(void* State);
		/* dispose of the information template */
		void										(*DisposeTemplate)(void* Template);
		/* create a new state object. */
		void*										(*NewState)(void* Template,
															float FreqForMultisampling, AccentParam* Accents,
															float Loudness, float HurryUp,
															long* PreOriginTimeOut, float StereoPosition,
															float InitialFrequency, float PitchDisplacementDepthLimit,
															float PitchDisplacementRateLimit,
															long PitchDisplacementStartPoint);
		/* fix up pre-origin time for the state object */
		void										(*FixUpStatePreOrigin)(void* State, long ActualPreOrigin);
		/* send a key-up signal to one of the oscillators */
		void										(*KeyUpSustain1)(void* State);
		void										(*KeyUpSustain2)(void* State);
		void										(*KeyUpSustain3)(void* State);
		/* restart a oscillator.  this is used for tie continuations */
		void										(*RestartState)(void* State, float NewFreqMultisampling,
															AccentParam* NewAccents, float NewLoudness,
															float NewHurryUp, MyBoolean RetriggerEnvelopes,
															float NewStereoPosition,
															float NewInitialFrequency,
															float PitchDisplacementDepthLimit,
															float PitchDisplacementRateLimit,
															long PitchDisplacementStartPoint);
		/* generate a sequence of samples (called for each envelope clock) */
		void										(*GenSamples)(void* State, long SampleCount,
															largefixedsigned* RawBuffer,
															largefixedsigned* PrivateWorkspace);
		/* find out if the oscillator has finished yet */
		MyBoolean								(*IsItFinished)(void* State);
	} OscBankVectorRec;


struct OscBankTemplateRec
	{
		/* general parameters */
		MyBoolean								StereoOutput;
		float										OverallVolumeScalingFactor;
		long										SamplingRate;
		float										EnvelopeUpdateRate;
		MyBoolean								TimeInterpolation;
		MyBoolean								WaveInterpolation;

		/* parameter updator for track (so we can set up individual notes properly) */
		IncrParamUpdateRec*			ParamUpdator;

		/* template for the pitch displacement LFO */
		LFOListSpecRec*					PitchLFOTemplate;

		/* instrument overall loudness */
		float										InstrOverallLoudness;

		/* list of template records describing each oscillator */
		OscBankVectorRec*				TemplateArray;
		/* this is the number of oscillators in the array */
		long										NumOscillatorsInBank;
	};


typedef struct OscStateRec
	{
		/* this is a reference to the state object for this oscillator */
		void*										StateReference;

		/* copy of the routine vectors */
		OscBankVectorRec				Template;

		/* next oscillator in the list */
		struct OscStateRec*			Next;
	} OscStateRec;


struct OscStateBankRec
	{
		/* what are we derived from */
		OscBankTemplateRec*			BankTemplate;

		/* list of oscillators that this oscillator bank is comprised of */
		OscStateRec*						OscillatorList;

		/* this calculates the differential values for periodic pitch displacements */
		LFOGenRec*							PitchLFO;

		/* if this object ties to a note, then this is the note to tie to.  this is */
		/* used for finding existing oscillators for tie continuations. */
		struct NoteObjectRec*		TieToNote;

		/* portamento control parameters */
		long										PortamentoCounter; /* 0 = done */
		long										TotalPortamentoTicks;
		float										InitialFrequency;
		float										FinalFrequency;
		float										CurrentFrequency;
		/* True = portamento linear to Hertz; False = portamento linear to half-steps */
		MyBoolean								PortamentoHertz;

		/* various counters (in terms of envelope ticks) */
		/* negative = expired */
		long										Release1Countdown;
		long										Release2Countdown;
		long										Release3Countdown;
		long										PitchLFOStartCountdown;

		/* next oscillator bank in the list */
		struct OscStateBankRec*	Next;
	};


static OscStateRec*							StateFreeList = NIL;
static OscStateBankRec*					StateBankFreeList = NIL;


/* flush all cached oscillator state bank records */
void									FlushCachedOscStateBankRecords(void)
	{
		while (StateBankFreeList != NIL)
			{
				OscStateBankRec*		Temp;

				Temp = StateBankFreeList;
				StateBankFreeList = StateBankFreeList->Next;
				ReleasePtr((char*)Temp);
			}

		while (StateFreeList != NIL)
			{
				OscStateRec*				Temp;

				Temp = StateFreeList;
				StateFreeList = StateFreeList->Next;
				ReleasePtr((char*)Temp);
			}
	}


#if DEBUG
static void					ValidateOscState(OscStateRec* State)
	{
		OscStateRec*			Scan;

		CheckPtrExistence(State);
		Scan = StateFreeList;
		while (Scan != NIL)
			{
				if (Scan == State)
					{
						PRERR(ForceAbort,"ValidateOscState:  state object is on free list");
					}
				Scan = Scan->Next;
			}
	}
#else
#define ValidateOscState(x) ((void)0)
#endif


#if DEBUG
static void					ValidateOscStateBank(OscStateBankRec* StateBank)
	{
		OscStateBankRec*	Scan;

		CheckPtrExistence(StateBank);
		Scan = StateBankFreeList;
		while (Scan != NIL)
			{
				if (Scan == StateBank)
					{
						PRERR(ForceAbort,"ValidateOscStateBank:  state bank object is on free list");
					}
				Scan = Scan->Next;
			}
	}
#else
#define ValidateOscStateBank(x) ((void)0)
#endif


/* construct an oscillator bank template record.  various parameters are passed in */
/* which are needed for synthesis.  ParameterUpdator is the parameter information */
/* record for the whole track of which this is a part. */
OscBankTemplateRec*		NewOscBankTemplate(struct InstrumentRec* InstrumentDefinition,
												MyBoolean StereoFlag, LargeBCDType OverallVolumeScalingReciprocal,
												long SamplingRate, float EnvelopeRate, MyBoolean TimeInterp,
												MyBoolean WaveInterp, struct IncrParamUpdateRec* ParameterUpdator,
												ErrorDaemonRec* ErrorDaemon, struct MainWindowRec* MainWindow)
	{
		OscBankTemplateRec*	Template;
		OscillatorListRec*	OscillatorListObject;
		long								Scan;

		CheckPtrExistence(InstrumentDefinition);
		CheckPtrExistence(ParameterUpdator);
		CheckPtrExistence(ErrorDaemon);

		Template = (OscBankTemplateRec*)AllocPtrCanFail(sizeof(OscBankTemplateRec),
			"OscBankTemplateRec");
		if (Template == NIL)
			{
			 FailurePoint1:
				return NIL;
			}

		/* the oscillator bank template contains all of the information needed for */
		/* constructing oscillators as notes are to be executed. */
		/* number of oscillators in a bank. */
		OscillatorListObject = GetInstrumentOscillatorList(InstrumentDefinition);

		/* get LFO information */
		Template->PitchLFOTemplate = GetInstrumentFrequencyLFOList(InstrumentDefinition);

		/* vector containing templates for all of the oscillators */
		Template->TemplateArray = (OscBankVectorRec*)AllocPtrCanFail(
			sizeof(OscBankVectorRec) * GetOscillatorListLength(OscillatorListObject),
			"OscBankVectorRec");
		if (Template->TemplateArray == NIL)
			{
			 FailurePoint4:
				ReleasePtr((char*)Template);
				goto FailurePoint1;
			}

		/* build entry for each oscillator */
		Template->NumOscillatorsInBank = GetOscillatorListLength(OscillatorListObject);
		for (Scan = 0; Scan < Template->NumOscillatorsInBank; Scan += 1)
			{
				OscillatorRec*		Osc = GetOscillatorFromList(OscillatorListObject,Scan);

				CheckPtrExistence(Osc);
				switch (OscillatorGetWhatKindItIs(Osc))
					{
						default:
							EXECUTE(PRERR(ForceAbort,"NewOscBankTemplate:  bad oscillator type"));
							break;
						case eOscillatorSampled:
							PRNGCHK(Template->TemplateArray,&(Template->TemplateArray[Scan]),
								sizeof(Template->TemplateArray[Scan]));
							Template->TemplateArray[Scan].TemplateReference
								= NewSampleTemplate(Osc,EnvelopeRate,SamplingRate,StereoFlag,
								TimeInterp,WaveInterp,ErrorDaemon,MainWindow,
								LargeBCD2Single(OverallVolumeScalingReciprocal));
							if (Template->TemplateArray[Scan].TemplateReference == NIL)
								{
								 FailurePoint5:
									for (Scan = 0; Scan < Template->NumOscillatorsInBank; Scan += 1)
										{
											(*Template->TemplateArray[Scan]
												.DisposeTemplate)(Template->TemplateArray[
												Scan].TemplateReference);
										}
									goto FailurePoint4;
								}
							Template->TemplateArray[Scan].UpdateEnvelopes
								= (void (*)(void*,float))&UpdateSampleEnvelopes;
							Template->TemplateArray[Scan].DisposeState
								= (void (*)(void*))&DisposeSampleState;
							Template->TemplateArray[Scan].DisposeTemplate
								= (void (*)(void*))&DisposeSampleTemplate;
							Template->TemplateArray[Scan].NewState
								= (void* (*)(void*,float,AccentParam*,
								float,float,long*,float,float,float,float,long))&NewSampleState;
							Template->TemplateArray[Scan].FixUpStatePreOrigin
								= (void (*)(void*,long))&FixUpSampleStatePreOrigin;
							Template->TemplateArray[Scan].KeyUpSustain1
								= (void (*)(void*))&SampleKeyUpSustain1;
							Template->TemplateArray[Scan].KeyUpSustain2
								= (void (*)(void*))&SampleKeyUpSustain2;
							Template->TemplateArray[Scan].KeyUpSustain3
								= (void (*)(void*))&SampleKeyUpSustain3;
							Template->TemplateArray[Scan].RestartState
								= (void (*)(void*,float,AccentParam*,float,float,
								MyBoolean,float,float,float,float,long))&RestartSampleState;
							Template->TemplateArray[Scan].GenSamples
								= (void (*)(void*,long,largefixedsigned*,
								largefixedsigned*))&SampleGenSamples;
							Template->TemplateArray[Scan].IsItFinished
								= (MyBoolean (*)(void*))&SampleIsItFinished;
							break;
						case eOscillatorWaveTable:
							PRNGCHK(Template->TemplateArray,&(Template->TemplateArray[Scan]),
								sizeof(Template->TemplateArray[Scan]));
							Template->TemplateArray[Scan].TemplateReference
								= NewWaveTableTemplate(Osc,EnvelopeRate,SamplingRate,StereoFlag,
								TimeInterp,WaveInterp,ErrorDaemon,MainWindow,
								LargeBCD2Single(OverallVolumeScalingReciprocal));
							if (Template->TemplateArray[Scan].TemplateReference == NIL)
								{
									goto FailurePoint5;
								}
							Template->TemplateArray[Scan].UpdateEnvelopes
								= (void (*)(void*,float))&UpdateWaveTableEnvelopes;
							Template->TemplateArray[Scan].DisposeState
								= (void (*)(void*))&DisposeWaveTableState;
							Template->TemplateArray[Scan].DisposeTemplate
								= (void (*)(void*))&DisposeWaveTableTemplate;
							Template->TemplateArray[Scan].NewState
								= (void* (*)(void*,float,AccentParam*,
								float,float,long*,float,float,float,float,long))&NewWaveTableState;
							Template->TemplateArray[Scan].FixUpStatePreOrigin
								= (void (*)(void*,long))&FixUpWaveTableStatePreOrigin;
							Template->TemplateArray[Scan].KeyUpSustain1
								= (void (*)(void*))&WaveTableKeyUpSustain1;
							Template->TemplateArray[Scan].KeyUpSustain2
								= (void (*)(void*))&WaveTableKeyUpSustain2;
							Template->TemplateArray[Scan].KeyUpSustain3
								= (void (*)(void*))&WaveTableKeyUpSustain3;
							Template->TemplateArray[Scan].RestartState
								= (void (*)(void*,float,AccentParam*,float,float,
								MyBoolean,float,float,float,float,long))&RestartWaveTableState;
							Template->TemplateArray[Scan].GenSamples
								= (void (*)(void*,long,largefixedsigned*,
								largefixedsigned*))&WaveTableGenSamples;
							Template->TemplateArray[Scan].IsItFinished
								= (MyBoolean (*)(void*))&WaveTableIsItFinished;
							break;
					}
			}

		/* playback control parameters */
		Template->StereoOutput = StereoFlag;
		Template->OverallVolumeScalingFactor = 1.0 / LargeBCD2Single(OverallVolumeScalingReciprocal);
		Template->SamplingRate = SamplingRate;
		Template->EnvelopeUpdateRate = EnvelopeRate;
		Template->TimeInterpolation = TimeInterp;
		Template->WaveInterpolation = WaveInterp;

		Template->ParamUpdator = ParameterUpdator;

		Template->InstrOverallLoudness = GetInstrumentOverallLoudness(InstrumentDefinition);

		return Template;
	}


/* dispose of the template */
void									DisposeOscBankTemplate(OscBankTemplateRec* Template)
	{
		long								Scan;

		CheckPtrExistence(Template);

		for (Scan = 0; Scan < Template->NumOscillatorsInBank; Scan += 1)
			{
				PRNGCHK(Template->TemplateArray,&(Template->TemplateArray[Scan]),
					sizeof(Template->TemplateArray[Scan]));
				(*Template->TemplateArray[Scan].DisposeTemplate)(Template->
					TemplateArray[Scan].TemplateReference);
			}
		ReleasePtr((char*)Template->TemplateArray);

		ReleasePtr((char*)Template);
	}


/* construct a new oscillator bank state object based on the note.  the note is */
/* assumed to start "now" in terms of the parameters in the ParameterUpdator.  */
/* the ScanningGapWidth is the number of envelope clock ticks in the current scanning */
/* gap.  this is used to determine how far later than "now" in terms of the back */
/* edge of the scanning gap (different from above) the osc bank should start playing. */
/* *WhenToStartPlayingOut returns the number of envelope ticks after the back edge */
/* of the scanning gap that the note should be started. */
/*     <already played>       |    <scanning gap>     |    <not yet analyzed> */
/*   time ---->    time ---->    time ---->    time ---->    time ---->   time ----> */
/*                            ^A                      ^B     */
/* point A is the back edge of the scanning gap.  as this edge moves forward in time, */
/*   oscillator bank state objects are removed from the queue and playback is commenced */
/*   for them. */
/* point B is the front edge of the scanning gap.  as this edge moves forward in time, */
/*   notes are extracted from the track and state bank objects are created for them. */
/*   ParameterUpdator always reflects parameters at this point in time. */
OscStateBankRec*			NewOscBankState(OscBankTemplateRec* Template,
												long* WhenToStartPlayingOut, struct NoteObjectRec* Note,
												float EnvelopeTicksPerDurationTick)
	{
		OscStateBankRec*		State;
		long								Scan;
		FrozenNoteRec*			FrozenNote;
		long								StartPointAdjust;
		long								MaxOscillatorPreOriginTime;
		OscStateRec*				OneState;
		long								ThisPreOriginTime;

		CheckPtrExistence(Template);
		CheckPtrExistence(Note);

		if (StateBankFreeList != NIL)
			{
				State = StateBankFreeList;
				StateBankFreeList = StateBankFreeList->Next;
			}
		 else
			{
				State = (OscStateBankRec*)AllocPtrCanFail(sizeof(OscStateBankRec),"OscStateBankRec");
				if (State == NIL)
					{
					 FailurePoint1:
						return NIL;
					}
			}
		EXECUTE(State->Next = (OscStateBankRec*)0x81818181;)

		State->BankTemplate = Template;

		/* freeze the parameters */
		FrozenNote = FixNoteParameters(Template->ParamUpdator,Note,&StartPointAdjust,
			Template->OverallVolumeScalingFactor,EnvelopeTicksPerDurationTick);
		if (FrozenNote == NIL)
			{
			 FailurePoint2:
				State->Next = StateBankFreeList;
				StateBankFreeList = State;
				goto FailurePoint1;
			}

		/* list of oscillators that this oscillator bank is comprised of */
		State->OscillatorList = NIL;
		MaxOscillatorPreOriginTime = 0;
		for (Scan = 0; Scan < Template->NumOscillatorsInBank; Scan += 1)
			{
				/* allocate the new record */
				if (StateFreeList != NIL)
					{
						OneState = StateFreeList;
						StateFreeList = StateFreeList->Next;
					}
				 else
					{
						OneState = (OscStateRec*)AllocPtrCanFail(sizeof(OscStateRec),"OscStateRec");
						if (OneState == NIL)
							{
							 FailurePoint3:
								while (State->OscillatorList != NIL)
									{
										/* delink object */
										OneState = State->OscillatorList;
										State->OscillatorList = State->OscillatorList->Next;
										/* dispose members */
										(*OneState->Template.DisposeState)(OneState->StateReference);
										/* stick on free list */
										OneState->Next = StateFreeList;
										StateFreeList = OneState;
									}
								goto FailurePoint2;
							}
					}

				/* copy over the function vectors */
				PRNGCHK(Template->TemplateArray,&(Template->TemplateArray[Scan]),
					sizeof(Template->TemplateArray[Scan]));
				OneState->Template = Template->TemplateArray[Scan];

				/* create the oscillator */
				OneState->StateReference = (OneState->Template.NewState)(
					OneState->Template.TemplateReference,FrozenNote->MultisampleFrequency,
					&(FrozenNote->Accents),FrozenNote->LoudnessAdjust * Template->InstrOverallLoudness,
					FrozenNote->HurryUpFactor,&ThisPreOriginTime,FrozenNote->StereoPosition,
					FrozenNote->NominalFrequency,FrozenNote->PitchDisplacementDepthLimit,
					FrozenNote->PitchDisplacementRateLimit,FrozenNote->PitchDisplacementStartPoint);
				if (OneState->StateReference == NIL)
					{
					 FailurePoint3a:
						OneState->Next = StateFreeList;
						StateFreeList = OneState;
						goto FailurePoint3;
					}

				if (ThisPreOriginTime > MaxOscillatorPreOriginTime)
					{
						MaxOscillatorPreOriginTime = ThisPreOriginTime;
					}

				/* link it in */
				OneState->Next = State->OscillatorList;
				State->OscillatorList = OneState;
			}

		/* this calculates the differential values for periodic pitch displacements */
		State->PitchLFO = NewLFOGenerator(Template->PitchLFOTemplate,&ThisPreOriginTime,
			&(FrozenNote->Accents),FrozenNote->NominalFrequency,FrozenNote->HurryUpFactor,
			Template->EnvelopeUpdateRate,FrozenNote->PitchDisplacementDepthLimit,
			FrozenNote->PitchDisplacementRateLimit,FrozenNote->MultisampleFrequency);
		if (State->PitchLFO == NIL)
			{
			 FailurePoint4:
				goto FailurePoint3;
			}
		if (ThisPreOriginTime > MaxOscillatorPreOriginTime)
			{
				MaxOscillatorPreOriginTime = ThisPreOriginTime;
			}

		/* if this object ties to a note, then this is the note to tie to.  this is */
		/* used for finding existing oscillators for tie continuations. */
		State->TieToNote = Note->a.Note.Tie;

		/* portamento control parameters */
		State->PortamentoCounter = 0;
		State->CurrentFrequency = FrozenNote->NominalFrequency;

		/* fix up pre-origin times */
		OneState = State->OscillatorList;
		while (OneState != NIL)
			{
				(*OneState->Template.FixUpStatePreOrigin)(OneState->StateReference,
					MaxOscillatorPreOriginTime);
				OneState = OneState->Next;
			}
		LFOGeneratorFixEnvelopeOrigins(State->PitchLFO,MaxOscillatorPreOriginTime);

		/* various counters (in terms of envelope ticks) */
		if (State->TieToNote == NIL)
			{
				State->Release1Countdown = FrozenNote->ReleasePoint1
					+ MaxOscillatorPreOriginTime;
				State->Release2Countdown = FrozenNote->ReleasePoint2
					+ MaxOscillatorPreOriginTime;
				State->Release3Countdown = FrozenNote->ReleasePoint3
					+ MaxOscillatorPreOriginTime;
			}
		 else
			{
				/* for ties, only honor releases from start */
				if (FrozenNote->Release1FromStart)
					{
						State->Release1Countdown = FrozenNote->ReleasePoint1
							+ MaxOscillatorPreOriginTime;
					}
				 else
					{
						State->Release1Countdown = -1;
					}
				if (FrozenNote->Release2FromStart)
					{
						State->Release2Countdown = FrozenNote->ReleasePoint2
							+ MaxOscillatorPreOriginTime;
					}
				 else
					{
						State->Release2Countdown = -1;
					}
				if (FrozenNote->Release3FromStart)
					{
						State->Release3Countdown = FrozenNote->ReleasePoint3
							+ MaxOscillatorPreOriginTime;
					}
				 else
					{
						State->Release3Countdown = -1;
					}
			}
		State->PitchLFOStartCountdown = FrozenNote->PitchDisplacementStartPoint
			/*+ MaxOscillatorPreOriginTime*/;
		/* pre origin relationship must be preserved for pitch LFO trigger */

		/* clean up */
		DisposeFrozenNote(FrozenNote);

		*WhenToStartPlayingOut = StartPointAdjust - MaxOscillatorPreOriginTime;

		return State;
	}


/* this is used for resetting a note for a tie */
/* the FrozenNote object is NOT disposed */
MyBoolean							ResetOscBankState(OscStateBankRec* State,
												struct FrozenNoteRec* FrozenNote,
												float EnvelopeTicksPerDurationTick)
	{
		OscStateRec*				OneState;
		MyBoolean						RetriggerEnvelopes;

		CheckPtrExistence(State);
		ValidateOscStateBank(State);
		CheckPtrExistence(FrozenNote);

		RetriggerEnvelopes = ((FrozenNote->OriginalNote->Flags
			& eRetriggerEnvelopesOnTieFlag) != 0);

		/* go through the oscillators and retrigger them */
		OneState = State->OscillatorList;
		while (OneState != NIL)
			{
				(*OneState->Template.RestartState)(OneState->StateReference,
					FrozenNote->MultisampleFrequency,&(FrozenNote->Accents),
					FrozenNote->LoudnessAdjust * State->BankTemplate->InstrOverallLoudness,
					FrozenNote->HurryUpFactor,RetriggerEnvelopes,FrozenNote->StereoPosition,
					FrozenNote->NominalFrequency,FrozenNote->PitchDisplacementDepthLimit,
					FrozenNote->PitchDisplacementRateLimit,FrozenNote->PitchDisplacementStartPoint);
				OneState = OneState->Next;
			}

		LFOGeneratorRetriggerFromOrigin(State->PitchLFO,&(FrozenNote->Accents),
			FrozenNote->NominalFrequency,FrozenNote->HurryUpFactor,
			FrozenNote->PitchDisplacementDepthLimit,
			FrozenNote->PitchDisplacementRateLimit,RetriggerEnvelopes);

		/* if this object ties to a note, then this is the note to tie to.  this is */
		/* used for finding existing oscillators for tie continuations. */
		State->TieToNote = FrozenNote->OriginalNote->a.Note.Tie;

		/* portamento control parameters */
		if (FrozenNote->PortamentoDuration > 0)
			{
				State->PortamentoCounter = FrozenNote->PortamentoDuration;
				State->TotalPortamentoTicks = FrozenNote->PortamentoDuration;
				State->InitialFrequency = State->CurrentFrequency; /* save current pitch */
				State->FinalFrequency = FrozenNote->NominalFrequency;
				State->PortamentoHertz = ((FrozenNote->OriginalNote->Flags
					& ePortamentoHertzNotHalfsteps) != 0);
			}
		 else
			{
				State->PortamentoCounter = 0;
				State->CurrentFrequency = FrozenNote->NominalFrequency;
			}

		/* various counters (in terms of envelope ticks) */
		if (State->TieToNote == NIL)
			{
				State->Release1Countdown = FrozenNote->ReleasePoint1;
				State->Release2Countdown = FrozenNote->ReleasePoint2;
				State->Release3Countdown = FrozenNote->ReleasePoint3;
			}
		 else
			{
				/* for ties, only honor releases from start */
				if (FrozenNote->Release1FromStart)
					{
						State->Release1Countdown = FrozenNote->ReleasePoint1;
					}
				 else
					{
						State->Release1Countdown = -1;
					}
				if (FrozenNote->Release2FromStart)
					{
						State->Release2Countdown = FrozenNote->ReleasePoint2;
					}
				 else
					{
						State->Release2Countdown = -1;
					}
				if (FrozenNote->Release3FromStart)
					{
						State->Release3Countdown = FrozenNote->ReleasePoint3;
					}
				 else
					{
						State->Release3Countdown = -1;
					}
			}
		if (RetriggerEnvelopes)
			{
				State->PitchLFOStartCountdown = FrozenNote->PitchDisplacementStartPoint;
			}
		 else
			{
				State->PitchLFOStartCountdown = -1;
			}

		return True;
	}


/* get rid of a state bank */
void									DisposeOscStateBank(OscStateBankRec* State)
	{
		OscStateRec*				OneStateScan;

		CheckPtrExistence(State);
		ValidateOscStateBank(State);

		OneStateScan = State->OscillatorList;
		while (OneStateScan != NIL)
			{
				OscStateRec*				Temp;

				(*OneStateScan->Template.DisposeState)(OneStateScan->StateReference);
				Temp = OneStateScan;
				OneStateScan = OneStateScan->Next;
				Temp->Next = StateFreeList;
				StateFreeList = Temp;
			}

		DisposeLFOGenerator(State->PitchLFO);

		State->Next = StateBankFreeList;
		StateBankFreeList = State;
	}


/* get the reference to the note that this bank ties to.  NIL if it doesn't */
struct NoteObjectRec*	GetOscStateTieTarget(OscStateBankRec* State)
	{
		CheckPtrExistence(State);
		ValidateOscStateBank(State);

		return State->TieToNote;
	}


/* perform one envelope clock cycle on a state bank.  this returns True if the */
/* state bank is done and should be retired.  (it will return false if it is a */
/* tie source.) */
MyBoolean							UpdateOscStateBank(OscStateBankRec* State, long NumFrames,
												largefixedsigned* OutputData, largefixedsigned* PrivateWorkspace)
	{
		OscStateRec*				OneStateScan;
		MyBoolean						OscillatorsRunning;
		float								Frequency;

		CheckPtrExistence(State);
		ValidateOscStateBank(State);

		if (State->Release1Countdown >= 0)
			{
				if (State->Release1Countdown == 0)
					{
						OneStateScan = State->OscillatorList;
						while (OneStateScan != NIL)
							{
								(*OneStateScan->Template.KeyUpSustain1)(OneStateScan->StateReference);
								OneStateScan = OneStateScan->Next;
							}
						LFOGeneratorKeyUpSustain1(State->PitchLFO);
					}
				State->Release1Countdown -= 1;
			}

		if (State->Release2Countdown >= 0)
			{
				if (State->Release2Countdown == 0)
					{
						OneStateScan = State->OscillatorList;
						while (OneStateScan != NIL)
							{
								(*OneStateScan->Template.KeyUpSustain2)(OneStateScan->StateReference);
								OneStateScan = OneStateScan->Next;
							}
						LFOGeneratorKeyUpSustain2(State->PitchLFO);
					}
				State->Release2Countdown -= 1;
			}

		if (State->Release3Countdown >= 0)
			{
				if (State->Release3Countdown == 0)
					{
						OneStateScan = State->OscillatorList;
						while (OneStateScan != NIL)
							{
								(*OneStateScan->Template.KeyUpSustain3)(OneStateScan->StateReference);
								OneStateScan = OneStateScan->Next;
							}
						LFOGeneratorKeyUpSustain3(State->PitchLFO);
					}
				State->Release3Countdown -= 1;
			}

		/* perform portamento */
		if (State->PortamentoCounter > 0)
			{
				/* decrement is done before interpolation so that the final frequency */
				/* will actually be reached. */
				State->PortamentoCounter -= 1;
				if (State->PortamentoHertz)
					{
						/* this transition is linear, so it's easy to compute */
						/* L+F(R-L) */
						State->CurrentFrequency = State->InitialFrequency
							+ ((float)(State->TotalPortamentoTicks
							- State->PortamentoCounter) / State->TotalPortamentoTicks)
							* (State->FinalFrequency - State->InitialFrequency);
					}
				 else
					{
						/* this transition is log-linear, so it's a bit messier */
						State->CurrentFrequency = State->InitialFrequency * (float)FEXP(
							((float)(State->TotalPortamentoTicks - State->PortamentoCounter)
							/ State->TotalPortamentoTicks)
							* (((float)FLN(State->FinalFrequency) / (float)LOG2)
							- ((float)FLN(State->InitialFrequency) / (float)LOG2)) * LOG2);
					}
			}

		/* update the pitch LFO modulation & figure out what the current pitch is */
		if (State->PitchLFOStartCountdown > 0)
			{
				State->PitchLFOStartCountdown -= 1;
				Frequency = State->CurrentFrequency;
			}
		 else
			{
				/* do some pitch stuff */
				Frequency = FastFixed2Float(LFOGenUpdateCycle(State->PitchLFO,
					Double2FastFixed(State->CurrentFrequency),State->CurrentFrequency));
			}

		/* perform a cycle of resampling */
		OscillatorsRunning = False;
		OneStateScan = State->OscillatorList;
		while (OneStateScan != NIL)
			{
				(*OneStateScan->Template.UpdateEnvelopes)(OneStateScan->StateReference,
					Frequency);
				(*OneStateScan->Template.GenSamples)(OneStateScan->StateReference,NumFrames,
					OutputData,PrivateWorkspace);
				OscillatorsRunning = OscillatorsRunning || !(*OneStateScan->Template
					.IsItFinished)(OneStateScan->StateReference);
				OneStateScan = OneStateScan->Next;
			}

		return !OscillatorsRunning;
	}
