/* DelayLine.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 "DelayLine.h"
#include "Memory.h"
#include "DelayEffectSpec.h"
#include "FilterFirstOrderLowpass.h"


/* tap algorithm select */
typedef enum
	{
		eTapLeftToLeft EXECUTE(= -3241),
		eTapLeftToMono,
		eTapLeftToRight,
		eTapMonoToLeft,
		eTapMonoToMono,
		eTapMonoToRight,
		eTapRightToLeft,
		eTapRightToMono,
		eTapRightToRight,
		eTapLeftToLeftLPF,
		eTapLeftToMonoLPF,
		eTapLeftToRightLPF,
		eTapMonoToLeftLPF,
		eTapMonoToMonoLPF,
		eTapMonoToRightLPF,
		eTapRightToLeftLPF,
		eTapRightToMonoLPF,
		eTapRightToRightLPF
	} TapAlgorithm;


typedef struct DelayLineTapRec
	{
		/* link to next tap record */
		struct DelayLineTapRec*		Next;

		/* what are we tapping */
		DelayChannelType	SourceTap;
		float							SourceTime;
		float							SourceTimeAccent1Adjust;
		float							SourceTimeAccent2Adjust;
		float							SourceTimeAccent3Adjust;
		float							SourceTimeAccent4Adjust;
		DelayChannelType	TargetTap;
		float							TargetTime;
		float							TargetTimeAccent1Adjust;
		float							TargetTimeAccent2Adjust;
		float							TargetTimeAccent3Adjust;
		float							TargetTimeAccent4Adjust;
		float							ScaleFactor;
		float							ScaleFactorAccent1Adjust;
		float							ScaleFactorAccent2Adjust;
		float							ScaleFactorAccent3Adjust;
		float							ScaleFactorAccent4Adjust;
		float							Cutoff;
		float							CutoffAccent1Adjust;
		float							CutoffAccent2Adjust;
		float							CutoffAccent3Adjust;
		float							CutoffAccent4Adjust;

		/* control information */
		TapAlgorithm			TapProcessAlgorithm;

		/* state information */
		FirstOrderLowpassRec*	LowPassFilter;
		long							SourceOffset;
		long							TargetOffset;
		float							Scaling;
	} DelayLineTapRec;


struct DelayLineRec
	{
		/* maximum delay */
		float							MaxDelayTime;

		/* state information */
		long							DelayLineLength; /* must be power of 2 */
		long							DelayLineIndex;
		largefixedsigned*	LeftDelayLineArray; /* for mono, only this one is used */
		largefixedsigned*	RightDelayLineArray;
		long							FramesPerSecond;

		/* tap list */
		DelayLineTapRec*	Taps;

		/* free list link */
		DelayLineRec*			Next;
	};


static DelayLineRec*			DelayLineFreeList = NIL;
static DelayLineTapRec*		DelayLineTapFreeList = NIL;


/* flush cached delay line records */
void							FlushCachedDelayLineStuff(void)
	{
		while (DelayLineFreeList != NIL)
			{
				DelayLineRec*		Temp;

				Temp = DelayLineFreeList;
				DelayLineFreeList = DelayLineFreeList->Next;
				ReleasePtr((char*)Temp);
			}
		while (DelayLineTapFreeList != NIL)
			{
				DelayLineTapRec*	Temp;

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


/* create a new delay line processor */
DelayLineRec*			NewDelayLineProcessor(struct DelayEffectRec* Template,
										long FramesPerSecond)
	{
		DelayLineRec*		Delay;
		long						Temp;
		long						Scan;
		long						Limit;
		DelayLineTapRec*	Appender;

		CheckPtrExistence(Template);
		if (DelayLineFreeList != NIL)
			{
				Delay = DelayLineFreeList;
				DelayLineFreeList = DelayLineFreeList->Next;
			}
		 else
			{
				Delay = (DelayLineRec*)AllocPtrCanFail(sizeof(DelayLineRec),"DelayLineRec");
				if (Delay == NIL)
					{
					 FailurePoint1:
						return NIL;
					}
			}

		Delay->MaxDelayTime = GetDelayMaxTime(Template);
		Delay->Taps = NIL;

		/* set line length */
		Temp = FramesPerSecond * Delay->MaxDelayTime + 1;
		Temp = Temp & 0x3fffffff;
		Delay->DelayLineLength = 1UL;
		while (Temp != 0)
			{
				Temp = Temp >> 1;
				Delay->DelayLineLength = Delay->DelayLineLength << 1;
			}
		Delay->FramesPerSecond = FramesPerSecond;
		Delay->DelayLineIndex = 0;

		/* build lines */
		Delay->LeftDelayLineArray = (largefixedsigned*)AllocPtrCanFail(
			sizeof(largefixedsigned) * Delay->DelayLineLength,"DelayLineArray");
		if (Delay->LeftDelayLineArray == NIL)
			{
			 FailurePoint2:
				ReleasePtr((char*)Delay);
				goto FailurePoint1;
			}
		Delay->RightDelayLineArray = (largefixedsigned*)AllocPtrCanFail(
			sizeof(largefixedsigned) * Delay->DelayLineLength,"DelayLineArray");
		if (Delay->RightDelayLineArray == NIL)
			{
			 FailurePoint3:
				ReleasePtr((char*)Delay->LeftDelayLineArray);
				goto FailurePoint2;
			}
		for (Temp = 0; Temp < Delay->DelayLineLength; Temp += 1)
			{
				Delay->LeftDelayLineArray[Temp] = 0;
				Delay->RightDelayLineArray[Temp] = 0;
			}

		/* build tap list */
		Appender = NIL;
		Limit = GetDelayEffectSpecNumTaps(Template);
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				DelayLineTapRec*		Tap;

				/* allocate delay line */
				if (DelayLineTapFreeList != NIL)
					{
						Tap = DelayLineTapFreeList;
						DelayLineTapFreeList = DelayLineTapFreeList->Next;
					}
				 else
					{
						Tap = (DelayLineTapRec*)AllocPtrCanFail(sizeof(DelayLineTapRec),"DelayLineTapRec");
						if (Tap == NIL)
							{
							 FailurePoint4:
								while (Delay->Taps != NIL)
									{
										Tap = Delay->Taps;
										Delay->Taps = Delay->Taps->Next;
										if (Tap->LowPassFilter != NIL)
											{
												DisposeFirstOrderLowpass(Tap->LowPassFilter);
											}
										ReleasePtr((char*)Tap);
									}
								ReleasePtr((char*)Delay->RightDelayLineArray);
								goto FailurePoint3;
							}
					}
				/* append to list */
				Tap->Next = NIL;
				if (Appender == NIL)
					{
						Delay->Taps = Tap;
					}
				 else
					{
						Appender->Next = Tap;
					}
				Appender = Tap;
				/* fill in template fields */
				Tap->SourceTap = GetDelayTapSource(Template,Scan);
				Tap->SourceTime = GetDelayTapSourceTime(Template,Scan);
				Tap->SourceTimeAccent1Adjust = GetDelayTapSourceTimeAccent1(Template,Scan);
				Tap->SourceTimeAccent2Adjust = GetDelayTapSourceTimeAccent2(Template,Scan);
				Tap->SourceTimeAccent3Adjust = GetDelayTapSourceTimeAccent3(Template,Scan);
				Tap->SourceTimeAccent4Adjust = GetDelayTapSourceTimeAccent4(Template,Scan);
				Tap->TargetTap = GetDelayTapTarget(Template,Scan);
				Tap->TargetTime = GetDelayTapTargetTime(Template,Scan);
				Tap->TargetTimeAccent1Adjust = GetDelayTapTargetTimeAccent1(Template,Scan);
				Tap->TargetTimeAccent2Adjust = GetDelayTapTargetTimeAccent2(Template,Scan);
				Tap->TargetTimeAccent3Adjust = GetDelayTapTargetTimeAccent3(Template,Scan);
				Tap->TargetTimeAccent4Adjust = GetDelayTapTargetTimeAccent4(Template,Scan);
				Tap->ScaleFactor = GetDelayTapScale(Template,Scan);
				Tap->ScaleFactorAccent1Adjust = GetDelayTapScaleAccent1(Template,Scan);
				Tap->ScaleFactorAccent2Adjust = GetDelayTapScaleAccent2(Template,Scan);
				Tap->ScaleFactorAccent3Adjust = GetDelayTapScaleAccent3(Template,Scan);
				Tap->ScaleFactorAccent4Adjust = GetDelayTapScaleAccent4(Template,Scan);
				Tap->Cutoff = GetDelayTapFilterCutoff(Template,Scan);
				Tap->CutoffAccent1Adjust = GetDelayTapFilterCutoffAccent1(Template,Scan);
				Tap->CutoffAccent2Adjust = GetDelayTapFilterCutoffAccent2(Template,Scan);
				Tap->CutoffAccent3Adjust = GetDelayTapFilterCutoffAccent3(Template,Scan);
				Tap->CutoffAccent4Adjust = GetDelayTapFilterCutoffAccent4(Template,Scan);
				/* create lowpass filter if necessary */
				if (GetDelayTapFilterEnable(Template,Scan))
					{
						Tap->LowPassFilter = NewFirstOrderLowpass();
						if (Tap->LowPassFilter == NIL)
							{
							 FailurePoint5:
								ReleasePtr((char*)Tap);
								goto FailurePoint4;
							}
					}
				 else
					{
						Tap->LowPassFilter = NIL;
					}
				/* determine which algorithm to use */
				switch (Tap->SourceTap)
					{
						default:
							EXECUTE(PRERR(ForceAbort,"NewDelayLineProcessor:  bad tap source"));
							break;
						case eTapLeftChannel:
							switch (Tap->TargetTap)
								{
									default:
										EXECUTE(PRERR(ForceAbort,"NewDelayLineProcessor:  bad tap target"));
										break;
									case eTapLeftChannel:
										if (!GetDelayTapFilterEnable(Template,Scan))
											{
												Tap->TapProcessAlgorithm = eTapLeftToLeft;
											}
										 else
											{
												Tap->TapProcessAlgorithm = eTapLeftToLeftLPF;
											}
										break;
									case eTapRightChannel:
										if (!GetDelayTapFilterEnable(Template,Scan))
											{
												Tap->TapProcessAlgorithm = eTapLeftToRight;
											}
										 else
											{
												Tap->TapProcessAlgorithm = eTapLeftToRightLPF;
											}
										break;
									case eTapMonoChannel:
										if (!GetDelayTapFilterEnable(Template,Scan))
											{
												Tap->TapProcessAlgorithm = eTapLeftToMono;
											}
										 else
											{
												Tap->TapProcessAlgorithm = eTapLeftToMonoLPF;
											}
										break;
								}
							break;
						case eTapRightChannel:
							switch (Tap->TargetTap)
								{
									default:
										EXECUTE(PRERR(ForceAbort,"NewDelayLineProcessor:  bad tap target"));
										break;
									case eTapLeftChannel:
										if (!GetDelayTapFilterEnable(Template,Scan))
											{
												Tap->TapProcessAlgorithm = eTapRightToLeft;
											}
										 else
											{
												Tap->TapProcessAlgorithm = eTapRightToLeftLPF;
											}
										break;
									case eTapRightChannel:
										if (!GetDelayTapFilterEnable(Template,Scan))
											{
												Tap->TapProcessAlgorithm = eTapRightToRight;
											}
										 else
											{
												Tap->TapProcessAlgorithm = eTapRightToRightLPF;
											}
										break;
									case eTapMonoChannel:
										if (!GetDelayTapFilterEnable(Template,Scan))
											{
												Tap->TapProcessAlgorithm = eTapRightToMono;
											}
										 else
											{
												Tap->TapProcessAlgorithm = eTapRightToMonoLPF;
											}
										break;
								}
							break;
						case eTapMonoChannel:
							switch (Tap->TargetTap)
								{
									default:
										EXECUTE(PRERR(ForceAbort,"NewDelayLineProcessor:  bad tap target"));
										break;
									case eTapLeftChannel:
										if (!GetDelayTapFilterEnable(Template,Scan))
											{
												Tap->TapProcessAlgorithm = eTapMonoToLeft;
											}
										 else
											{
												Tap->TapProcessAlgorithm = eTapMonoToLeftLPF;
											}
										break;
									case eTapRightChannel:
										if (!GetDelayTapFilterEnable(Template,Scan))
											{
												Tap->TapProcessAlgorithm = eTapMonoToRight;
											}
										 else
											{
												Tap->TapProcessAlgorithm = eTapMonoToRightLPF;
											}
										break;
									case eTapMonoChannel:
										if (!GetDelayTapFilterEnable(Template,Scan))
											{
												Tap->TapProcessAlgorithm = eTapMonoToMono;
											}
										 else
											{
												Tap->TapProcessAlgorithm = eTapMonoToMonoLPF;
											}
										break;
								}
							break;
					}
			}

		return Delay;
	}


/* dispose of the delay line processor */
void							DisposeDelayLineProcessor(DelayLineRec* Delay)
	{
		CheckPtrExistence(Delay);
		ReleasePtr((char*)Delay->LeftDelayLineArray);
		ReleasePtr((char*)Delay->RightDelayLineArray);
		while (Delay->Taps != NIL)
			{
				DelayLineTapRec*		Temp;

				Temp = Delay->Taps;
				Delay->Taps = Delay->Taps->Next;
				if (Temp->LowPassFilter != NIL)
					{
						DisposeFirstOrderLowpass(Temp->LowPassFilter);
					}
				Temp->Next = DelayLineTapFreeList;
				DelayLineTapFreeList = Temp;
			}
		Delay->Next = DelayLineFreeList;
		DelayLineFreeList = Delay;
	}


/* update delay line state with accent information */
void							UpdateDelayLineState(DelayLineRec* Delay, float Accent1, float Accent2,
										float Accent3, float Accent4)
	{
		DelayLineTapRec*	Scan;

		CheckPtrExistence(Delay);
		/* update tap states */
		Scan = Delay->Taps;
		while (Scan != NIL)
			{
				float							Time;

				/* determine source offset */
				Time = Scan->SourceTime + Accent1 * Scan->SourceTimeAccent1Adjust
					+ Accent2 * Scan->SourceTimeAccent2Adjust + Accent3
					* Scan->SourceTimeAccent3Adjust + Accent4 * Scan->SourceTimeAccent4Adjust;
				if (Time < 0)
					{
						Time = 0;
					}
				if (Time > Delay->MaxDelayTime)
					{
						Time = Delay->MaxDelayTime;
					}
				Scan->SourceOffset = Time * Delay->FramesPerSecond;
				/* determine target offset */
				Time = Scan->TargetTime + Accent1 * Scan->TargetTimeAccent1Adjust
					+ Accent2 * Scan->TargetTimeAccent2Adjust + Accent3
					* Scan->TargetTimeAccent3Adjust + Accent4 * Scan->TargetTimeAccent4Adjust;
				if (Time < 0)
					{
						Time = 0;
					}
				if (Time > Delay->MaxDelayTime)
					{
						Time = Delay->MaxDelayTime;
					}
				Scan->TargetOffset = Time * Delay->FramesPerSecond;
				/* determine scale factor */
				Scan->Scaling = Scan->ScaleFactor + Accent1 * Scan->ScaleFactorAccent1Adjust
					+ Accent2 * Scan->ScaleFactorAccent2Adjust + Accent3
					* Scan->ScaleFactorAccent3Adjust + Accent4 * Scan->ScaleFactorAccent4Adjust;
				/* update filter, if it exists */
				if (Scan->LowPassFilter != NIL)
					{
						SetFirstOrderLowpassCoefficients(Scan->LowPassFilter,
							Scan->Cutoff + Accent1 * Scan->CutoffAccent1Adjust
							+ Accent2 * Scan->CutoffAccent2Adjust
							+ Accent3 * Scan->CutoffAccent3Adjust
							+ Accent4 * Scan->CutoffAccent4Adjust,Delay->FramesPerSecond);
					}
				/* do the next one */
				Scan = Scan->Next;
			}
	}


/* apply delay processing to some stuff to stereo data */
void							ApplyDelayLineStereo(largefixedsigned* Data, long NumFrames,
										DelayLineRec* Delay)
	{
		long						Scan;
		long						VectorMask;
		long						VectorIndex;
		largefixedsigned*	LeftDelayLineArray;
		largefixedsigned*	RightDelayLineArray;

		CheckPtrExistence(Data);
		CheckPtrExistence(Delay);
		VectorMask = Delay->DelayLineLength - 1;
		VectorIndex = Delay->DelayLineIndex;
		LeftDelayLineArray = Delay->LeftDelayLineArray;
		RightDelayLineArray = Delay->RightDelayLineArray;
		for (Scan = 0; Scan < NumFrames; Scan += 1)
			{
				DelayLineTapRec*	Tap;

				/* initialize current point with dry data */
				LeftDelayLineArray[VectorIndex] = Data[Scan * 2 + 0];
				RightDelayLineArray[VectorIndex] = Data[Scan * 2 + 1];
				/* iterate over taps */
				Tap = Delay->Taps;
				while (Tap != NIL)
					{
						long							SourceIndex;
						long							TargetIndex;
						largefixedsigned	Temp;

						/* precompute some stuff */
						SourceIndex = VectorMask & (VectorIndex +  Tap->SourceOffset);
						TargetIndex = VectorMask & (VectorIndex +  Tap->TargetOffset);
						PRNGCHK(LeftDelayLineArray,&(LeftDelayLineArray[SourceIndex]),
							sizeof(LeftDelayLineArray[SourceIndex]));
						PRNGCHK(LeftDelayLineArray,&(LeftDelayLineArray[TargetIndex]),
							sizeof(LeftDelayLineArray[TargetIndex]));
						PRNGCHK(RightDelayLineArray,&(RightDelayLineArray[SourceIndex]),
							sizeof(RightDelayLineArray[SourceIndex]));
						PRNGCHK(RightDelayLineArray,&(RightDelayLineArray[TargetIndex]),
							sizeof(RightDelayLineArray[TargetIndex]));
						/* apply transformation */
						switch (Tap->TapProcessAlgorithm)
							{
								default:
									EXECUTE(PRERR(ForceAbort,
										"ApplyDelayLineStereo:  unknown tap processing algorithm"));
									break;

								case eTapLeftToLeft:
									LeftDelayLineArray[TargetIndex] += (largefixedsigned)(
										LeftDelayLineArray[SourceIndex] * Tap->Scaling);
									break;
								case eTapLeftToMono:
									Temp = LeftDelayLineArray[SourceIndex] * Tap->Scaling;
									LeftDelayLineArray[TargetIndex] += Temp;
									RightDelayLineArray[TargetIndex] += Temp;
									break;
								case eTapLeftToRight:
									RightDelayLineArray[TargetIndex] += (largefixedsigned)(
										LeftDelayLineArray[SourceIndex] * Tap->Scaling);
									break;
								case eTapMonoToLeft:
									LeftDelayLineArray[TargetIndex] += (largefixedsigned)(
										((LeftDelayLineArray[SourceIndex]
										+ RightDelayLineArray[SourceIndex]) >> 1) * Tap->Scaling);
									break;
								case eTapMonoToMono:
									Temp = ((LeftDelayLineArray[SourceIndex]
										+ RightDelayLineArray[SourceIndex]) >> 1) * Tap->Scaling;
									LeftDelayLineArray[TargetIndex] += Temp;
									RightDelayLineArray[TargetIndex] += Temp;
									break;
								case eTapMonoToRight:
									RightDelayLineArray[TargetIndex] += (largefixedsigned)(
										((LeftDelayLineArray[SourceIndex]
										+ RightDelayLineArray[SourceIndex]) >> 1) * Tap->Scaling);
									break;
								case eTapRightToLeft:
									LeftDelayLineArray[TargetIndex] += (largefixedsigned)(
										RightDelayLineArray[SourceIndex] * Tap->Scaling);
									break;
								case eTapRightToMono:
									Temp = RightDelayLineArray[SourceIndex] * Tap->Scaling;
									LeftDelayLineArray[TargetIndex] += Temp;
									RightDelayLineArray[TargetIndex] += Temp;
									break;
								case eTapRightToRight:
									RightDelayLineArray[TargetIndex] += (largefixedsigned)(
										RightDelayLineArray[SourceIndex] * Tap->Scaling);
									break;

								case eTapLeftToLeftLPF:
									LeftDelayLineArray[TargetIndex] += (largefixedsigned)
										ApplyFirstOrderLowpass(Tap->LowPassFilter,
										LeftDelayLineArray[SourceIndex] * Tap->Scaling);
									break;
								case eTapLeftToMonoLPF:
									Temp = ApplyFirstOrderLowpass(Tap->LowPassFilter,
										LeftDelayLineArray[SourceIndex] * Tap->Scaling);
									LeftDelayLineArray[TargetIndex] += Temp;
									RightDelayLineArray[TargetIndex] += Temp;
									break;
								case eTapLeftToRightLPF:
									RightDelayLineArray[TargetIndex] += (largefixedsigned)
										ApplyFirstOrderLowpass(Tap->LowPassFilter,
										LeftDelayLineArray[SourceIndex] * Tap->Scaling);
									break;
								case eTapMonoToLeftLPF:
									LeftDelayLineArray[TargetIndex] += (largefixedsigned)
										ApplyFirstOrderLowpass(Tap->LowPassFilter,
										((LeftDelayLineArray[SourceIndex]
										+ RightDelayLineArray[SourceIndex]) >> 1) * Tap->Scaling);
									break;
								case eTapMonoToMonoLPF:
									Temp = ApplyFirstOrderLowpass(Tap->LowPassFilter,((LeftDelayLineArray[
										SourceIndex] + RightDelayLineArray[SourceIndex]) >> 1)
										* Tap->Scaling);
									LeftDelayLineArray[TargetIndex] += Temp;
									RightDelayLineArray[TargetIndex] += Temp;
									break;
								case eTapMonoToRightLPF:
									RightDelayLineArray[TargetIndex] += (largefixedsigned)
										ApplyFirstOrderLowpass(Tap->LowPassFilter,((LeftDelayLineArray[
										SourceIndex] + RightDelayLineArray[SourceIndex]) >> 1)
										* Tap->Scaling);
									break;
								case eTapRightToLeftLPF:
									LeftDelayLineArray[TargetIndex] += (largefixedsigned)
										ApplyFirstOrderLowpass(Tap->LowPassFilter,
										RightDelayLineArray[SourceIndex] * Tap->Scaling);
									break;
								case eTapRightToMonoLPF:
									Temp = ApplyFirstOrderLowpass(Tap->LowPassFilter,
										RightDelayLineArray[SourceIndex] * Tap->Scaling);
									LeftDelayLineArray[TargetIndex] += Temp;
									RightDelayLineArray[TargetIndex] += Temp;
									break;
								case eTapRightToRightLPF:
									RightDelayLineArray[TargetIndex] += (largefixedsigned)
										ApplyFirstOrderLowpass(Tap->LowPassFilter,
										RightDelayLineArray[SourceIndex] * Tap->Scaling);
									break;
							}
						/* do the next one */
						Tap = Tap->Next;
					}
				/* perform data update */
				PRNGCHK(Data,&(Data[Scan * 2 + 0]),sizeof(Data[Scan * 2 + 0]));
				PRNGCHK(Data,&(Data[Scan * 2 + 1]),sizeof(Data[Scan * 2 + 1]));
				PRNGCHK(LeftDelayLineArray,&(LeftDelayLineArray[VectorIndex]),
					sizeof(LeftDelayLineArray[VectorIndex]));
				PRNGCHK(RightDelayLineArray,&(RightDelayLineArray[VectorIndex]),
					sizeof(RightDelayLineArray[VectorIndex]));
				Data[Scan * 2 + 0] = LeftDelayLineArray[VectorIndex];
				Data[Scan * 2 + 1] = RightDelayLineArray[VectorIndex];
				/* increment index */
				VectorIndex = (VectorIndex - 1) & VectorMask;
			}
		Delay->DelayLineIndex = VectorIndex;
	}


/* apply delay processing to some stuff to mono data */
void							ApplyDelayLineMono(largefixedsigned* Data, long NumFrames,
										DelayLineRec* Delay)
	{
		long						Scan;
		long						VectorMask;
		long						VectorIndex;
		largefixedsigned*	LeftDelayLineArray;

		CheckPtrExistence(Data);
		CheckPtrExistence(Delay);
		VectorMask = Delay->DelayLineLength - 1;
		VectorIndex = Delay->DelayLineIndex;
		LeftDelayLineArray = Delay->LeftDelayLineArray;
		for (Scan = 0; Scan < NumFrames; Scan += 1)
			{
				DelayLineTapRec*	Tap;

				/* clear current output point */
				LeftDelayLineArray[VectorIndex] = Data[Scan];
				/* iterate over taps */
				Tap = Delay->Taps;
				while (Tap != NIL)
					{
						long							SourceIndex;
						long							TargetIndex;
						largefixedsigned	Temp;

						/* precompute some stuff */
						SourceIndex = VectorMask & (VectorIndex +  Tap->SourceOffset);
						TargetIndex = VectorMask & (VectorIndex +  Tap->TargetOffset);
						PRNGCHK(LeftDelayLineArray,&(LeftDelayLineArray[SourceIndex]),
							sizeof(LeftDelayLineArray[SourceIndex]));
						PRNGCHK(LeftDelayLineArray,&(LeftDelayLineArray[TargetIndex]),
							sizeof(LeftDelayLineArray[TargetIndex]));
						/* apply transformation */
						switch (Tap->TapProcessAlgorithm)
							{
								default:
									EXECUTE(PRERR(ForceAbort,
										"ApplyDelayLineMono:  unknown tap processing algorithm"));
									break;

								case eTapLeftToLeft:
								case eTapLeftToMono:
								case eTapLeftToRight:
								case eTapMonoToLeft:
								case eTapMonoToMono:
								case eTapMonoToRight:
								case eTapRightToLeft:
								case eTapRightToMono:
								case eTapRightToRight:
									LeftDelayLineArray[TargetIndex] += (largefixedsigned)(
										LeftDelayLineArray[SourceIndex] * Tap->Scaling);
									break;

								case eTapLeftToLeftLPF:
								case eTapLeftToMonoLPF:
								case eTapLeftToRightLPF:
								case eTapMonoToLeftLPF:
								case eTapMonoToMonoLPF:
								case eTapMonoToRightLPF:
								case eTapRightToLeftLPF:
								case eTapRightToMonoLPF:
								case eTapRightToRightLPF:
									LeftDelayLineArray[TargetIndex] += (largefixedsigned)
										ApplyFirstOrderLowpass(Tap->LowPassFilter,
										LeftDelayLineArray[SourceIndex] * Tap->Scaling);
									break;
							}
						/* do the next one */
						Tap = Tap->Next;
					}
				/* perform data update */
				PRNGCHK(Data,&(Data[Scan]),sizeof(Data[Scan]));
				PRNGCHK(LeftDelayLineArray,&(LeftDelayLineArray[VectorIndex]),
					sizeof(LeftDelayLineArray[VectorIndex]));
				Data[Scan] = LeftDelayLineArray[VectorIndex];
				/* increment index */
				VectorIndex = (VectorIndex - 1) & VectorMask;
			}
		Delay->DelayLineIndex = VectorIndex;
	}
