/* OscEffectGenerator.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"

#include "OscEffectGenerator.h"
#include "EffectSpecList.h"
#include "OscDelayLine.h"
#include "OscNLProc.h"
#include "OscFilterArray.h"
#include "Analyzer.h"
#include "Memory.h"


typedef struct OneEffectRec
	{
		struct OneEffectRec*		Next;
		EffectTypes							Type;
		union
			{
				OscDelayLineRec*				OscDelayEffect;
				OscNLProcRec*						OscNLProcEffect;
				OscFilterArrayRec*			OscFilterEffect;
				AnalyzerRec*						AnalyzerEffect;
			} u;
	} OneEffectRec;


struct OscEffectGenRec
	{
		/* list of effects records */
		OneEffectRec*						List;

		/* mono/stereo flag */
		MyBoolean								Stereo;

		/* trash link */
		OscEffectGenRec*				Next;
	};


static OneEffectRec*						OneEffectFreeList = NIL;
static OscEffectGenRec*					OscEffectGenFreeList = NIL;


/* dispose of cached effect generator structures */
void									FlushOscEffectGeneratorInfo(void)
	{
		while (OneEffectFreeList != NIL)
			{
				OneEffectRec*			Temp;

				Temp = OneEffectFreeList;
				OneEffectFreeList = OneEffectFreeList->Next;
				ReleasePtr((char*)Temp);
			}
		while (OscEffectGenFreeList != NIL)
			{
				OscEffectGenRec*	Temp;

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


/* create a new oscillator effect generator */
OscEffectGenRec*			NewOscEffectGenerator(struct EffectSpecListRec* SpecList,
												float EnvelopeTicksPerSecond, long SamplingRate,
												MyBoolean DoingStereo, float InverseVolume,
												struct MainWindowRec* MainWindow, AccentParam* Accents,
												float HurryUp, float InitialFrequency,
												float FreqForMultisampling, long* PreOriginTimeOut)
	{
		OscEffectGenRec*		Generator;
		OneEffectRec*				Appender;
		long								List;
		long								Scan;
		long								MaxPreOrigin;
		long								OnePreOrigin;

		CheckPtrExistence(SpecList);
		Generator = (OscEffectGenRec*)AllocPtrCanFail(sizeof(OscEffectGenRec),
			"OscEffectGenRec");
		if (Generator == NIL)
			{
			 FailurePoint1:
				return NIL;
			}

		MaxPreOrigin = 0;

		Generator->List = NIL;
		Generator->Stereo = DoingStereo;

		/* build list of thingers */
		Appender = NIL;
		List = GetEffectSpecListLength(SpecList);
		for (Scan = 0; Scan < List; Scan += 1)
			{
				OneEffectRec*				Effect;

				/* allocate record */
				Effect = (OneEffectRec*)AllocPtrCanFail(sizeof(OneEffectRec),"OneEffectRec");
				if (Effect == NIL)
					{
					 FailurePoint2:
						while (Generator->List != NIL)
							{
								Effect = Generator->List;
								Generator->List = Generator->List->Next;
								switch (Effect->Type)
									{
										default:
											EXECUTE(PRERR(ForceAbort,"NewTrackEffectGenerator:  bad effect type"));
											break;
										case eDelayEffect:
											DisposeOscDelayLineProcessor(Effect->u.OscDelayEffect);
											break;
										case eNLProcEffect:
											DisposeOscNLProcProcessor(Effect->u.OscNLProcEffect);
											break;
										case eFilterEffect:
											DisposeOscFilterArrayProcessor(Effect->u.OscFilterEffect);
											break;
										case eAnalyzerEffect:
											DisposeAnalyzer(Effect->u.AnalyzerEffect);
											break;
									}
								ReleasePtr((char*)Effect);
							}
						ReleasePtr((char*)Generator);
						goto FailurePoint1;
					}
				/* fill in fields */
				Effect->Type = GetEffectSpecListElementType(SpecList,Scan);
				switch (Effect->Type)
					{
						default:
							EXECUTE(PRERR(ForceAbort,"NewTrackEffectGenerator:  bad effect type"));
							break;
						case eDelayEffect:
							Effect->u.OscDelayEffect = NewOscDelayLineProcessor(
								GetDelayEffectFromEffectSpecList(SpecList,Scan),EnvelopeTicksPerSecond,
								SamplingRate,MainWindow,Accents,
								HurryUp,InitialFrequency,FreqForMultisampling,&OnePreOrigin);
							if (Effect->u.OscDelayEffect == NIL)
								{
								 FailurePoint2b:
									ReleasePtr((char*)Effect);
									goto FailurePoint2;
								}
							if (OnePreOrigin > MaxPreOrigin)
								{
									MaxPreOrigin = OnePreOrigin;
								}
							break;
						case eNLProcEffect:
							Effect->u.OscNLProcEffect = NewOscNLProcProcessor(
								GetNLProcEffectFromEffectSpecList(SpecList,Scan),EnvelopeTicksPerSecond,
								SamplingRate,MainWindow,Accents,HurryUp,
								InitialFrequency,FreqForMultisampling,&OnePreOrigin,DoingStereo,InverseVolume);
							if (Effect->u.OscNLProcEffect == NIL)
								{
									goto FailurePoint2b;
								}
							if (OnePreOrigin > MaxPreOrigin)
								{
									MaxPreOrigin = OnePreOrigin;
								}
							break;
						case eFilterEffect:
							Effect->u.OscFilterEffect = NewOscFilterArrayProcessor(
								GetFilterEffectFromEffectSpecList(SpecList,Scan),EnvelopeTicksPerSecond,
								SamplingRate,MainWindow,Accents,HurryUp,
								InitialFrequency,FreqForMultisampling,&OnePreOrigin,DoingStereo);
							if (Effect->u.OscFilterEffect == NIL)
								{
									goto FailurePoint2b;
								}
							if (OnePreOrigin > MaxPreOrigin)
								{
									MaxPreOrigin = OnePreOrigin;
								}
							break;
						case eAnalyzerEffect:
							Effect->u.AnalyzerEffect = NewAnalyzer(MainWindow,
								GetAnalyzerEffectFromEffectSpecList(SpecList,Scan),DoingStereo,
								InverseVolume);
							if (Effect->u.AnalyzerEffect == NIL)
								{
									goto FailurePoint2b;
								}
							break;
					}
				/* link */
				Effect->Next = NIL;
				if (Appender == NIL)
					{
						Generator->List = Effect;
					}
				 else
					{
						Appender->Next = Effect;
					}
				Appender = Effect;
			}
		return Generator;
	}


/* fix up pre-origin time for the oscillator effect generator */
void									FixUpOscEffectGeneratorPreOrigin(OscEffectGenRec* Generator,
												long ActualPreOrigin)
	{
		OneEffectRec*				Scan;

		CheckPtrExistence(Generator);
		Scan = Generator->List;
		while (Scan != NIL)
			{
				switch (Scan->Type)
					{
						default:
							EXECUTE(PRERR(ForceAbort,"FixUpOscEffectGeneratorPreOrigin:  bad effect type"));
							break;
						case eDelayEffect:
							OscDelayLineProcessorFixEnvelopeOrigins(Scan->u.OscDelayEffect,
								ActualPreOrigin);
							break;
						case eNLProcEffect:
							OscOscNLProcProcessorFixEnvelopeOrigins(Scan->u.OscNLProcEffect,
								ActualPreOrigin);
							break;
						case eFilterEffect:
							FixUpOscFilterArrayProcessorOrigins(Scan->u.OscFilterEffect,
								ActualPreOrigin);
							break;
						case eAnalyzerEffect:
							break;
					}
				Scan = Scan->Next;
			}
	}


/* dispose of an oscillator effect generator */
void									DisposeOscEffectGenerator(OscEffectGenRec* Generator)
	{
		CheckPtrExistence(Generator);
		while (Generator->List != NIL)
			{
				OneEffectRec*				Temp;

				Temp = Generator->List;
				Generator->List = Generator->List->Next;
				switch (Temp->Type)
					{
						default:
							EXECUTE(PRERR(ForceAbort,"DisposeOscEffectGenerator:  bad effect type"));
							break;
						case eDelayEffect:
							DisposeOscDelayLineProcessor(Temp->u.OscDelayEffect);
							break;
						case eNLProcEffect:
							DisposeOscNLProcProcessor(Temp->u.OscNLProcEffect);
							break;
						case eFilterEffect:
							DisposeOscFilterArrayProcessor(Temp->u.OscFilterEffect);
							break;
						case eAnalyzerEffect:
							DisposeAnalyzer(Temp->u.AnalyzerEffect);
							break;
					}
				Temp->Next = OneEffectFreeList;
				OneEffectFreeList = Temp;
			}
		Generator->Next = OscEffectGenFreeList;
		OscEffectGenFreeList = Generator;
	}


/* update envelopes for effects */
void									OscEffectGeneratorUpdateEnvelopes(OscEffectGenRec* Generator,
												float OscillatorFrequency)
	{
		OneEffectRec*				Scan;

		CheckPtrExistence(Generator);
		Scan = Generator->List;
		while (Scan != NIL)
			{
				switch (Scan->Type)
					{
						default:
							EXECUTE(PRERR(ForceAbort,"OscEffectGeneratorUpdateEnvelopes:  bad effect type"));
							break;
						case eDelayEffect:
							OscDelayLineProcessorUpdateEnvelopes(Scan->u.OscDelayEffect,
								OscillatorFrequency);
							break;
						case eNLProcEffect:
							OscNLProcUpdateEnvelopes(Scan->u.OscNLProcEffect,OscillatorFrequency);
							break;
						case eFilterEffect:
							OscFilterArrayProcessorUpdateEnvelopes(Scan->u.OscFilterEffect,
								OscillatorFrequency);
							break;
						case eAnalyzerEffect:
							break;
					}
				Scan = Scan->Next;
			}
	}


/* generate effect cycle.  this is called once per envelope tick to apply */
/* effects to data generated during this envelope clock cycle. */
void									ApplyOscEffectGenerator(OscEffectGenRec* Generator,
												largefixedsigned* Data, long NumFrames)
	{
		OneEffectRec*				Scan;

		CheckPtrExistence(Generator);
		Scan = Generator->List;
		while (Scan != NIL)
			{
				switch (Scan->Type)
					{
						default:
							EXECUTE(PRERR(ForceAbort,"ApplyOscEffectGenerator:  bad effect type"));
							break;
						case eDelayEffect:
							if (Generator->Stereo)
								{
									ApplyOscDelayLineStereo(Data,NumFrames,Scan->u.OscDelayEffect);
								}
							 else
								{
									ApplyOscDelayLineMono(Data,NumFrames,Scan->u.OscDelayEffect);
								}
							break;
						case eNLProcEffect:
							ApplyOscNLProc(Data,NumFrames,Scan->u.OscNLProcEffect);
							break;
						case eFilterEffect:
							ApplyOscFilterArray(Data,NumFrames,Scan->u.OscFilterEffect);
							break;
						case eAnalyzerEffect:
							ApplyAnalyzer(Data,NumFrames,Scan->u.AnalyzerEffect);
							break;
					}
				Scan = Scan->Next;
			}
	}


void									OscEffectKeyUpSustain1(OscEffectGenRec* Generator)
	{
		OneEffectRec*				Scan;

		CheckPtrExistence(Generator);
		Scan = Generator->List;
		while (Scan != NIL)
			{
				switch (Scan->Type)
					{
						default:
							EXECUTE(PRERR(ForceAbort,"OscEffectKeyUpSustain1:  bad effect type"));
							break;
						case eDelayEffect:
							OscDelayKeyUpSustain1(Scan->u.OscDelayEffect);
							break;
						case eNLProcEffect:
							OscNLProcKeyUpSustain1(Scan->u.OscNLProcEffect);
							break;
						case eFilterEffect:
							OscFilterArrayKeyUpSustain1(Scan->u.OscFilterEffect);
							break;
						case eAnalyzerEffect:
							break;
					}
				Scan = Scan->Next;
			}
	}


void									OscEffectKeyUpSustain2(OscEffectGenRec* Generator)
	{
		OneEffectRec*				Scan;

		CheckPtrExistence(Generator);
		Scan = Generator->List;
		while (Scan != NIL)
			{
				switch (Scan->Type)
					{
						default:
							EXECUTE(PRERR(ForceAbort,"OscEffectKeyUpSustain2:  bad effect type"));
							break;
						case eDelayEffect:
							OscDelayKeyUpSustain2(Scan->u.OscDelayEffect);
							break;
						case eNLProcEffect:
							OscNLProcKeyUpSustain2(Scan->u.OscNLProcEffect);
							break;
						case eFilterEffect:
							OscFilterArrayKeyUpSustain2(Scan->u.OscFilterEffect);
							break;
						case eAnalyzerEffect:
							break;
					}
				Scan = Scan->Next;
			}
	}


void									OscEffectKeyUpSustain3(OscEffectGenRec* Generator)
	{
		OneEffectRec*				Scan;

		CheckPtrExistence(Generator);
		Scan = Generator->List;
		while (Scan != NIL)
			{
				switch (Scan->Type)
					{
						default:
							EXECUTE(PRERR(ForceAbort,"OscEffectKeyUpSustain3:  bad effect type"));
							break;
						case eDelayEffect:
							OscDelayKeyUpSustain3(Scan->u.OscDelayEffect);
							break;
						case eNLProcEffect:
							OscNLProcKeyUpSustain3(Scan->u.OscNLProcEffect);
							break;
						case eFilterEffect:
							OscFilterArrayKeyUpSustain3(Scan->u.OscFilterEffect);
							break;
						case eAnalyzerEffect:
							break;
					}
				Scan = Scan->Next;
			}
	}


/* retrigger effect envelopes from the origin point */
void									OscEffectGeneratorRetriggerFromOrigin(OscEffectGenRec* Generator,
												AccentParam* Accents, float NewInitialFrequency,
												float HurryUp, MyBoolean ActuallyRetrigger)
	{
		OneEffectRec*				Scan;

		CheckPtrExistence(Generator);
		Scan = Generator->List;
		while (Scan != NIL)
			{
				switch (Scan->Type)
					{
						default:
							EXECUTE(PRERR(ForceAbort,"OscEffectKeyUpSustain3:  bad effect type"));
							break;
						case eDelayEffect:
							OscDelayRetriggerEnvelopes(Scan->u.OscDelayEffect,Accents,
								HurryUp,NewInitialFrequency,ActuallyRetrigger);
							break;
						case eNLProcEffect:
							OscNLProcRetriggerEnvelopes(Scan->u.OscNLProcEffect,Accents,
								HurryUp,NewInitialFrequency,ActuallyRetrigger);
							break;
						case eFilterEffect:
							OscFilterArrayRetriggerEnvelopes(Scan->u.OscFilterEffect,Accents,
								HurryUp,NewInitialFrequency,ActuallyRetrigger);
							break;
						case eAnalyzerEffect:
							break;
					}
				Scan = Scan->Next;
			}
	}
