/* DelayEffectSpec.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 "DelayEffectSpec.h"
#include "Memory.h"
#include "Array.h"
#include "LFOListSpecifier.h"
#include "Envelope.h"


struct DelayTapRec
	{
		/* tap control parameters */
		DelayChannelType	SourceTap;
		DelayChannelType	TargetTap;

		/* these controls only apply to the track effect, not the oscillator effect */
		float							SourceTime;
		float							SourceTimeAccent1Adjust;
		float							SourceTimeAccent2Adjust;
		float							SourceTimeAccent3Adjust;
		float							SourceTimeAccent4Adjust;
		float							TargetTime;
		float							TargetTimeAccent1Adjust;
		float							TargetTimeAccent2Adjust;
		float							TargetTimeAccent3Adjust;
		float							TargetTimeAccent4Adjust;
		float							ScaleFactor;
		float							ScaleFactorAccent1Adjust;
		float							ScaleFactorAccent2Adjust;
		float							ScaleFactorAccent3Adjust;
		float							ScaleFactorAccent4Adjust;
		MyBoolean					FilterEnable;
		float							FilterCutoff;
		float							FilterCutoffAccent1Adjust;
		float							FilterCutoffAccent2Adjust;
		float							FilterCutoffAccent3Adjust;
		float							FilterCutoffAccent4Adjust;

		/* these controls only apply to the oscillator effect, not the track effect */
		EnvelopeRec*			SourceTimeEnvelope;
		EnvelopeRec*			TargetTimeEnvelope;
		EnvelopeRec*			ScaleFactorEnvelope;
		EnvelopeRec*			CutoffEnvelope;
		LFOListSpecRec*		SourceTimeLFO;
		LFOListSpecRec*		TargetTimeLFO;
		LFOListSpecRec*		ScaleFactorLFO;
		LFOListSpecRec*		CutoffLFO;
	};


/* structure for whole delay line */
struct DelayEffectRec
	{
		/* maximum delay */
		float							MaxDelayTime;

		/* tap list */
		ArrayRec*					List;
	};


/* create a new delay line specification */
DelayEffectRec*			NewDelayLineSpec(void)
	{
		DelayEffectRec*		DelaySpec;

		DelaySpec = (DelayEffectRec*)AllocPtrCanFail(sizeof(DelayEffectRec),"DelayEffectRec");
		if (DelaySpec == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		DelaySpec->MaxDelayTime = 0;
		DelaySpec->List = NewArray();
		if (DelaySpec->List == NIL)
			{
			 FailurePoint2:
				ReleasePtr((char*)DelaySpec);
				goto FailurePoint1;
			}
		return DelaySpec;
	}


/* dispose of a delay line specification */
void								DisposeDelayLineSpec(DelayEffectRec* DelaySpec)
	{
		long							Limit;
		long							Scan;

		CheckPtrExistence(DelaySpec);
		Limit = ArrayGetLength(DelaySpec->List);
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				DelayTapRec*			Tap;

				Tap = (DelayTapRec*)ArrayGetElement(DelaySpec->List,Scan);
				DisposeEnvelope(Tap->SourceTimeEnvelope);
				DisposeEnvelope(Tap->TargetTimeEnvelope);
				DisposeEnvelope(Tap->ScaleFactorEnvelope);
				DisposeEnvelope(Tap->CutoffEnvelope);
				DisposeLFOListSpecifier(Tap->SourceTimeLFO);
				DisposeLFOListSpecifier(Tap->TargetTimeLFO);
				DisposeLFOListSpecifier(Tap->ScaleFactorLFO);
				DisposeLFOListSpecifier(Tap->CutoffLFO);
				ReleasePtr((char*)Tap);
			}
		DisposeArray(DelaySpec->List);
		ReleasePtr((char*)DelaySpec);
	}


/* set max delay time */
void								SetDelayMaxTime(DelayEffectRec* DelaySpec, float MaxTime)
	{
		CheckPtrExistence(DelaySpec);
		DelaySpec->MaxDelayTime = MaxTime;
	}


/* get max delay time */
float								GetDelayMaxTime(DelayEffectRec* DelaySpec)
	{
		CheckPtrExistence(DelaySpec);
		return DelaySpec->MaxDelayTime;
	}


/* create a new tap record */
DelayTapRec*				NewDelayTap(void)
	{
		DelayTapRec*			Tap;

		Tap = (DelayTapRec*)AllocPtrCanFail(sizeof(DelayTapRec),"DelayTapRec");
		if (Tap == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		Tap->SourceTimeEnvelope = NewEnvelope();
		if (Tap->SourceTimeEnvelope == NIL)
			{
			 FailurePoint2:
				ReleasePtr((char*)Tap);
				goto FailurePoint1;
			}
		Tap->TargetTimeEnvelope = NewEnvelope();
		if (Tap->TargetTimeEnvelope == NIL)
			{
			 FailurePoint3:
				DisposeEnvelope(Tap->SourceTimeEnvelope);
				goto FailurePoint2;
			}
		Tap->ScaleFactorEnvelope = NewEnvelope();
		if (Tap->ScaleFactorEnvelope == NIL)
			{
			 FailurePoint4:
				DisposeEnvelope(Tap->TargetTimeEnvelope);
				goto FailurePoint3;
			}
		Tap->SourceTimeLFO = NewLFOListSpecifier();
		if (Tap->SourceTimeLFO == NIL)
			{
			 FailurePoint5:
				DisposeEnvelope(Tap->ScaleFactorEnvelope);
				goto FailurePoint4;
			}
		Tap->TargetTimeLFO = NewLFOListSpecifier();
		if (Tap->TargetTimeLFO == NIL)
			{
			 FailurePoint6:
				DisposeLFOListSpecifier(Tap->SourceTimeLFO);
				goto FailurePoint5;
			}
		Tap->ScaleFactorLFO = NewLFOListSpecifier();
		if (Tap->ScaleFactorLFO == NIL)
			{
			 FailurePoint7:
				DisposeLFOListSpecifier(Tap->TargetTimeLFO);
				goto FailurePoint6;
			}
		Tap->CutoffEnvelope = NewEnvelope();
		if (Tap->CutoffEnvelope == NIL)
			{
			 FailurePoint8:
				DisposeLFOListSpecifier(Tap->ScaleFactorLFO);
				goto FailurePoint7;
			}
		Tap->CutoffLFO = NewLFOListSpecifier();
		if (Tap->CutoffLFO == NIL)
			{
			 FailurePoint9:
				DisposeEnvelope(Tap->CutoffEnvelope);
				goto FailurePoint8;
			}
		Tap->SourceTap = eTapMonoChannel;
		Tap->SourceTime = 0;
		Tap->SourceTimeAccent1Adjust = 0;
		Tap->SourceTimeAccent2Adjust = 0;
		Tap->SourceTimeAccent3Adjust = 0;
		Tap->SourceTimeAccent4Adjust = 0;
		Tap->TargetTap = eTapMonoChannel;
		Tap->TargetTime = 0;
		Tap->TargetTimeAccent1Adjust = 0;
		Tap->TargetTimeAccent2Adjust = 0;
		Tap->TargetTimeAccent3Adjust = 0;
		Tap->TargetTimeAccent4Adjust = 0;
		Tap->ScaleFactor = 0;
		Tap->ScaleFactorAccent1Adjust = 0;
		Tap->ScaleFactorAccent2Adjust = 0;
		Tap->ScaleFactorAccent3Adjust = 0;
		Tap->ScaleFactorAccent4Adjust = 0;
		Tap->FilterEnable = False;
		Tap->FilterCutoff = 0;
		Tap->FilterCutoffAccent1Adjust = 0;
		Tap->FilterCutoffAccent2Adjust = 0;
		Tap->FilterCutoffAccent3Adjust = 0;
		Tap->FilterCutoffAccent4Adjust = 0;
		return Tap;
	}


/* dispose delay tap */
void								DisposeDelayTap(DelayTapRec* Tap)
	{
		CheckPtrExistence(Tap);
		DisposeEnvelope(Tap->SourceTimeEnvelope);
		DisposeEnvelope(Tap->TargetTimeEnvelope);
		DisposeEnvelope(Tap->ScaleFactorEnvelope);
		DisposeLFOListSpecifier(Tap->SourceTimeLFO);
		DisposeLFOListSpecifier(Tap->TargetTimeLFO);
		DisposeLFOListSpecifier(Tap->ScaleFactorLFO);
		ReleasePtr((char*)Tap);
	}


void								SetDelayTapSource(DelayTapRec* Tap, DelayChannelType Source)
	{
		CheckPtrExistence(Tap);
		Tap->SourceTap = Source;
	}


void								SetDelayTapSourceTime(DelayTapRec* Tap, float Time)
	{
		CheckPtrExistence(Tap);
		Tap->SourceTime = Time;
	}


void								SetDelayTapSourceTimeAccent1(DelayTapRec* Tap, float Accent1)
	{
		CheckPtrExistence(Tap);
		Tap->SourceTimeAccent1Adjust = Accent1;
	}


void								SetDelayTapSourceTimeAccent2(DelayTapRec* Tap, float Accent2)
	{
		CheckPtrExistence(Tap);
		Tap->SourceTimeAccent2Adjust = Accent2;
	}


void								SetDelayTapSourceTimeAccent3(DelayTapRec* Tap, float Accent3)
	{
		CheckPtrExistence(Tap);
		Tap->SourceTimeAccent3Adjust = Accent3;
	}


void								SetDelayTapSourceTimeAccent4(DelayTapRec* Tap, float Accent4)
	{
		CheckPtrExistence(Tap);
		Tap->SourceTimeAccent4Adjust = Accent4;
	}


void								SetDelayTapTarget(DelayTapRec* Tap, DelayChannelType Target)
	{
		CheckPtrExistence(Tap);
		Tap->TargetTap = Target;
	}


void								SetDelayTapTargetTime(DelayTapRec* Tap, float Time)
	{
		CheckPtrExistence(Tap);
		Tap->TargetTime = Time;
	}


void								SetDelayTapTargetTimeAccent1(DelayTapRec* Tap, float Accent1)
	{
		CheckPtrExistence(Tap);
		Tap->TargetTimeAccent1Adjust = Accent1;
	}


void								SetDelayTapTargetTimeAccent2(DelayTapRec* Tap, float Accent2)
	{
		CheckPtrExistence(Tap);
		Tap->TargetTimeAccent2Adjust = Accent2;
	}


void								SetDelayTapTargetTimeAccent3(DelayTapRec* Tap, float Accent3)
	{
		CheckPtrExistence(Tap);
		Tap->TargetTimeAccent3Adjust = Accent3;
	}


void								SetDelayTapTargetTimeAccent4(DelayTapRec* Tap, float Accent4)
	{
		CheckPtrExistence(Tap);
		Tap->TargetTimeAccent4Adjust = Accent4;
	}


void								SetDelayTapScale(DelayTapRec* Tap, float Scale)
	{
		CheckPtrExistence(Tap);
		Tap->ScaleFactor = Scale;
	}


void								SetDelayTapScaleAccent1(DelayTapRec* Tap, float Accent1)
	{
		CheckPtrExistence(Tap);
		Tap->ScaleFactorAccent1Adjust = Accent1;
	}


void								SetDelayTapScaleAccent2(DelayTapRec* Tap, float Accent2)
	{
		CheckPtrExistence(Tap);
		Tap->ScaleFactorAccent2Adjust = Accent2;
	}


void								SetDelayTapScaleAccent3(DelayTapRec* Tap, float Accent3)
	{
		CheckPtrExistence(Tap);
		Tap->ScaleFactorAccent3Adjust = Accent3;
	}


void								SetDelayTapScaleAccent4(DelayTapRec* Tap, float Accent4)
	{
		CheckPtrExistence(Tap);
		Tap->ScaleFactorAccent4Adjust = Accent4;
	}


void								SetDelayTapFilterEnable(DelayTapRec* Tap, MyBoolean EnableFilter)
	{
		CheckPtrExistence(Tap);
		Tap->FilterEnable = EnableFilter;
	}


void								SetDelayTapFilterCutoff(DelayTapRec* Tap, float Cutoff)
	{
		CheckPtrExistence(Tap);
		Tap->FilterCutoff = Cutoff;
	}


void								SetDelayTapFilterCutoffAccent1(DelayTapRec* Tap, float Accent1)
	{
		CheckPtrExistence(Tap);
		Tap->FilterCutoffAccent1Adjust = Accent1;
	}


void								SetDelayTapFilterCutoffAccent2(DelayTapRec* Tap, float Accent2)
	{
		CheckPtrExistence(Tap);
		Tap->FilterCutoffAccent2Adjust = Accent2;
	}


void								SetDelayTapFilterCutoffAccent3(DelayTapRec* Tap, float Accent3)
	{
		CheckPtrExistence(Tap);
		Tap->FilterCutoffAccent3Adjust = Accent3;
	}


void								SetDelayTapFilterCutoffAccent4(DelayTapRec* Tap, float Accent4)
	{
		CheckPtrExistence(Tap);
		Tap->FilterCutoffAccent4Adjust = Accent4;
	}


/* append tap to list in delay line */
MyBoolean						AppendTapToDelayEffect(DelayEffectRec* DelaySpec, DelayTapRec* Tap)
	{
		CheckPtrExistence(DelaySpec);
		CheckPtrExistence(Tap);
		return ArrayAppendElement(DelaySpec->List,Tap);
	}


/* get the number of taps in the delay line */
long								GetDelayEffectSpecNumTaps(DelayEffectRec* DelaySpec)
	{
		CheckPtrExistence(DelaySpec);
		return ArrayGetLength(DelaySpec->List);
	}


/* get a specified tap from the delay line */
DelayTapRec*				GetTapFromDelayEffectSpec(DelayEffectRec* DelaySpec, long Index)
	{
		DelayTapRec*			Tap;

		CheckPtrExistence(DelaySpec);
		Tap = (DelayTapRec*)ArrayGetElement(DelaySpec->List,Index);
		CheckPtrExistence(Tap);
		return Tap;
	}


DelayChannelType		GetDelayTapSource(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->SourceTap;
	}


float								GetDelayTapSourceTime(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->SourceTime;
	}


float								GetDelayTapSourceTimeAccent1(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->SourceTimeAccent1Adjust;
	}


float								GetDelayTapSourceTimeAccent2(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->SourceTimeAccent2Adjust;
	}


float								GetDelayTapSourceTimeAccent3(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->SourceTimeAccent3Adjust;
	}


float								GetDelayTapSourceTimeAccent4(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->SourceTimeAccent4Adjust;
	}


DelayChannelType		GetDelayTapTarget(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->TargetTap;
	}


float								GetDelayTapTargetTime(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->TargetTime;
	}


float								GetDelayTapTargetTimeAccent1(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->TargetTimeAccent1Adjust;
	}


float								GetDelayTapTargetTimeAccent2(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->TargetTimeAccent2Adjust;
	}


float								GetDelayTapTargetTimeAccent3(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->TargetTimeAccent3Adjust;
	}


float								GetDelayTapTargetTimeAccent4(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->TargetTimeAccent4Adjust;
	}


float								GetDelayTapScale(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->ScaleFactor;
	}


float								GetDelayTapScaleAccent1(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->ScaleFactorAccent1Adjust;
	}


float								GetDelayTapScaleAccent2(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->ScaleFactorAccent2Adjust;
	}


float								GetDelayTapScaleAccent3(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->ScaleFactorAccent3Adjust;
	}


float								GetDelayTapScaleAccent4(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->ScaleFactorAccent4Adjust;
	}


MyBoolean						GetDelayTapFilterEnable(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->FilterEnable;
	}


float								GetDelayTapFilterCutoff(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->FilterCutoff;
	}


float								GetDelayTapFilterCutoffAccent1(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->FilterCutoffAccent1Adjust;
	}


float								GetDelayTapFilterCutoffAccent2(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->FilterCutoffAccent2Adjust;
	}


float								GetDelayTapFilterCutoffAccent3(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->FilterCutoffAccent3Adjust;
	}


float								GetDelayTapFilterCutoffAccent4(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->FilterCutoffAccent4Adjust;
	}


struct EnvelopeRec*	GetDelayTapSourceEnvelope(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->SourceTimeEnvelope;
	}


struct EnvelopeRec*	GetDelayTapTargetEnvelope(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->TargetTimeEnvelope;
	}


struct EnvelopeRec*	GetDelayTapScaleEnvelope(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->ScaleFactorEnvelope;
	}


struct EnvelopeRec*	GetDelayTapCutoffEnvelope(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->CutoffEnvelope;
	}


struct LFOListSpecRec*	GetDelayTapSourceLFO(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->SourceTimeLFO;
	}


struct LFOListSpecRec*	GetDelayTapTargetLFO(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->TargetTimeLFO;
	}


struct LFOListSpecRec*	GetDelayTapScaleLFO(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->ScaleFactorLFO;
	}


struct LFOListSpecRec*	GetDelayTapCutoffLFO(DelayEffectRec* DelaySpec, long Index)
	{
		CheckPtrExistence(DelaySpec);
		return GetTapFromDelayEffectSpec(DelaySpec,Index)->CutoffLFO;
	}
