/* FilterResonantLowpass.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 "FilterResonantLowpass.h"
#include "Memory.h"
#include "FilterButterworthLowpass.h"
#include "FilterButterworthBandpass.h"


typedef struct LowpassConsCell
	{
		/* link */
		struct LowpassConsCell*		Next;

		/* filter element */
		ButterworthLowpassRec*		Lowpasser;
	} LowpassConsCell;


typedef struct BandpassConsCell
	{
		/* link */
		struct BandpassConsCell*	Next;

		/* filter element */
		ButterworthBandpassRec*		Bandpasser;
	} BandpassConsCell;


struct ResonantLowpassRec
	{
		/* link */
		ResonantLowpassRec*				Next;

		/* lowpass chain */
		LowpassConsCell*					LowpassList;

		/* bandpass chain */
		BandpassConsCell*					BandpassList;

		/* gain */
		float											BandpassGain;
	};


static ResonantLowpassRec*			FreeList = NIL;
static LowpassConsCell*					LowpassFreeList = NIL;
static BandpassConsCell*				BandpassFreeList = NIL;


/* flush free list */
void											FlushCachedResonantLowpassStuff(void)
	{
		while (FreeList != NIL)
			{
				ResonantLowpassRec*		Temp;

				Temp = FreeList;
				FreeList = FreeList->Next;
				ReleasePtr((char*)Temp);
			}
		while (LowpassFreeList != NIL)
			{
				LowpassConsCell*			Temp;

				Temp = LowpassFreeList;
				LowpassFreeList = LowpassFreeList->Next;
				ReleasePtr((char*)Temp);
			}
		while (BandpassFreeList != NIL)
			{
				BandpassConsCell*			Temp;

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


/* create a new filter record */
ResonantLowpassRec*				NewResonantLowpass(long LowpassOrder, long BandpassOrder)
	{
		ResonantLowpassRec*			Filter;
		long										Count;

		ERROR((LowpassOrder < 0) || (LowpassOrder % 2 != 0),
			PRERR(AllowResume,"NewResonantLowpass:  LowpassOrder not non-negative even"));
		ERROR((BandpassOrder < 0) || (BandpassOrder % 2 != 0),
			PRERR(AllowResume,"NewResonantLowpass:  BandpassOrder not non-negative even"));
		if (FreeList != NIL)
			{
				Filter = FreeList;
				FreeList = FreeList->Next;
			}
		 else
			{
				Filter = (ResonantLowpassRec*)AllocPtrCanFail(sizeof(ResonantLowpassRec),
					"ResonantLowpassRec");
				if (Filter == NIL)
					{
					 FailurePoint1:
						return NIL;
					}
			}

		Filter->LowpassList = NIL;
		for (Count = 0; Count < LowpassOrder; Count += 2)
			{
				LowpassConsCell*		Cell;

				if (LowpassFreeList != NIL)
					{
						Cell = LowpassFreeList;
						LowpassFreeList = LowpassFreeList->Next;
					}
				 else
					{
						Cell = (LowpassConsCell*)AllocPtrCanFail(sizeof(LowpassConsCell),
							"LowpassConsCell");
						if (Cell == NIL)
							{
							 FailurePoint2:
								while (Filter->LowpassList != NIL)
									{
										LowpassConsCell*		Temp;

										Temp = Filter->LowpassList;
										if (Temp->Lowpasser != NIL)
											{
												DisposeButterworthLowpass(Temp->Lowpasser);
											}
										Filter->LowpassList = Filter->LowpassList->Next;
										Temp->Next = LowpassFreeList;
										LowpassFreeList = Temp;
									}
								Filter->Next = FreeList;
								FreeList = Filter;
								goto FailurePoint1;
							}
					}
				Cell->Next = Filter->LowpassList;
				Filter->LowpassList = Cell;
				Cell->Lowpasser = NewButterworthLowpass();
				if (Cell->Lowpasser == NIL)
					{
					 FailurePoint2a:
						goto FailurePoint2;
					}
			}

		Filter->BandpassList = NIL;
		for (Count = 0; Count < BandpassOrder; Count += 2)
			{
				BandpassConsCell*		Cell;

				if (BandpassFreeList != NIL)
					{
						Cell = BandpassFreeList;
						BandpassFreeList = BandpassFreeList->Next;
					}
				 else
					{
						Cell = (BandpassConsCell*)AllocPtrCanFail(sizeof(BandpassConsCell),
							"BandpassConsCell");
						if (Cell == NIL)
							{
							 FailurePoint3:
								while (Filter->BandpassList != NIL)
									{
										BandpassConsCell*		Temp;

										Temp = Filter->BandpassList;
										if (Temp->Bandpasser != NIL)
											{
												DisposeButterworthBandpass(Temp->Bandpasser);
											}
										Filter->BandpassList = Filter->BandpassList->Next;
										Temp->Next = BandpassFreeList;
										BandpassFreeList = Temp;
									}
								goto FailurePoint2;
							}
					}
				Cell->Next = Filter->BandpassList;
				Filter->BandpassList = Cell;
				Cell->Bandpasser = NewButterworthBandpass();
				if (Cell->Bandpasser == NIL)
					{
					 FailurePoint3a:
						goto FailurePoint3;
					}
			}

		return Filter;
	}


/* dispose filter record */
void											DisposeResonantLowpass(ResonantLowpassRec* Filter)
	{
		CheckPtrExistence(Filter);
		while (Filter->LowpassList != NIL)
			{
				LowpassConsCell*		Temp;

				Temp = Filter->LowpassList;
				DisposeButterworthLowpass(Temp->Lowpasser);
				Filter->LowpassList = Filter->LowpassList->Next;
				Temp->Next = LowpassFreeList;
				LowpassFreeList = Temp;
			}
		while (Filter->BandpassList != NIL)
			{
				BandpassConsCell*		Temp;

				Temp = Filter->BandpassList;
				DisposeButterworthBandpass(Temp->Bandpasser);
				Filter->BandpassList = Filter->BandpassList->Next;
				Temp->Next = BandpassFreeList;
				BandpassFreeList = Temp;
			}
		Filter->Next = FreeList;
		FreeList = Filter;
	}


/* adjust filter coefficients */
void											SetResonantLowpassCoefficients(ResonantLowpassRec* Filter,
														float Cutoff, float Bandwidth, float Gain, long SamplingRate)
	{
		LowpassConsCell*				LPScan;
		BandpassConsCell*				BPScan;

		CheckPtrExistence(Filter);
		LPScan = Filter->LowpassList;
		while (LPScan != NIL)
			{
				SetButterworthLowpassCoefficients(LPScan->Lowpasser,Cutoff,SamplingRate);
				LPScan = LPScan->Next;
			}
		BPScan = Filter->BandpassList;
		while (BPScan != NIL)
			{
				SetButterworthBandpassCoefficients(BPScan->Bandpasser,Cutoff,Bandwidth,SamplingRate);
				BPScan = BPScan->Next;
			}
		Filter->BandpassGain = Gain;
	}


/* apply filter to a sample value */
float											ApplyResonantLowpass(ResonantLowpassRec* Filter, float Xin)
	{
		float										LPFac;
		float										BPFac;
		LowpassConsCell*				LPScan;
		BandpassConsCell*				BPScan;

		CheckPtrExistence(Filter);

		LPFac = Xin;
		LPScan = Filter->LowpassList;
		while (LPScan != NIL)
			{
				LPFac = ApplyButterworthLowpass(LPScan->Lowpasser,LPFac);
				LPScan = LPScan->Next;
			}

		BPFac = Xin;
		BPScan = Filter->BandpassList;
		while (BPScan != NIL)
			{
				BPFac = ApplyButterworthBandpass(BPScan->Bandpasser,BPFac);
				BPScan = BPScan->Next;
			}

		return BPFac * Filter->BandpassGain + LPFac;
	}
