/* SampleSelector.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 "SampleSelector.h"
#include "Memory.h"
#include "SampleList.h"
#include "WaveTableList.h"
#include "AlgoSampList.h"
#include "AlgoWaveTableList.h"


/* in the list, elements are cascaded as follows: */
/*  <lowestbound>  <boundary2>  <boundary3>  <highestbound> */
/*          <sampname1>   <sampname2>  <sampname2> */
struct SampleSelectorRec
	{
		/* number of RANGES (i.e. unique sample intervals) */
		long								NumRanges;

		/* this is a list of the boundaries of the ranges, so there is one extra element */
		SampRangeHzType*		BoundaryList; /* NumRanges + 1 elements */

		/* this is an array of sample names, one sample for each interval */
		char**							SampleNameList; /* NumRanges elements */

		/* this is an array of sampled object references.  it is not immediately valid */
		void**							ReferencedObjectList; /* NumRanges elements */

		/* this is the type of object that is referenced */
		SampleSourceTypes*	ReferencedObjectTypeList; /* NumRanges elements */
	};


/* create a new sample selector record */
SampleSelectorRec*		NewSampleSelectorList(double LowestBound)
	{
		SampleSelectorRec*	SampList;

		SampList = (SampleSelectorRec*)AllocPtrCanFail(sizeof(SampleSelectorRec),
			"SampleSelectorRec");
		if (SampList == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		SampList->BoundaryList = (SampRangeHzType*)AllocPtrCanFail(
			1 * sizeof(SampRangeHzType),"SampRangeHzType");
		if (SampList->BoundaryList == NIL)
			{
			 FailurePoint2:
				ReleasePtr((char*)SampList);
				goto FailurePoint1;
			}
		SampList->BoundaryList[0] = LowestBound;
		SampList->SampleNameList = (char**)AllocPtrCanFail(0,"SampleNameList");
		if (SampList->SampleNameList == NIL)
			{
			 FailurePoint3:
				ReleasePtr((char*)SampList->BoundaryList);
				goto FailurePoint2;
			}
		SampList->NumRanges = 0;
		SampList->ReferencedObjectList = NIL;
		SampList->ReferencedObjectTypeList = NIL;
		return SampList;
	}


/* dispose of a sample selector list */
void									DisposeSampleSelectorList(SampleSelectorRec* SampList)
	{
		long								Scan;

		CheckPtrExistence(SampList);
		for (Scan = 0; Scan < SampList->NumRanges; Scan += 1)
			{
				ReleasePtr(SampList->SampleNameList[Scan]);
			}
		if (SampList->ReferencedObjectList != NIL)
			{
				ReleasePtr((char*)SampList->ReferencedObjectList);
			}
		if (SampList->ReferencedObjectTypeList != NIL)
			{
				ReleasePtr((char*)SampList->ReferencedObjectTypeList);
			}
		ReleasePtr((char*)SampList->SampleNameList);
		ReleasePtr((char*)SampList->BoundaryList);
		ReleasePtr((char*)SampList);
	}


/* add a new sample thing, with it's upper bound.  the object becomes the */
/* owner of the name string */
MyBoolean							AppendSampleSelector(SampleSelectorRec* SampList,
												double UpperBound, char* Name)
	{
		SampRangeHzType*		NewBoundaryList;
		char**							NewNameList;

		CheckPtrExistence(SampList);
		CheckPtrExistence(Name);
		NewBoundaryList = (SampRangeHzType*)ResizePtr((char*)SampList->BoundaryList,
			sizeof(SampRangeHzType) * (1 + 1 + SampList->NumRanges));
		if (NewBoundaryList == NIL)
			{
				return False;
			}
		NewNameList = (char**)ResizePtr((char*)SampList->SampleNameList,
			sizeof(char*) * (1 + SampList->NumRanges));
		/* if this fails, we don't do any cleanup because it doesn't matter if the */
		/* size of the array is a bit long -- the size is really determined by NumRanges */
		if (NewNameList == NIL)
			{
				return False;
			}
		SampList->BoundaryList = NewBoundaryList;
		SampList->SampleNameList = NewNameList;
		SampList->BoundaryList[SampList->NumRanges + 1] = UpperBound;
		SampList->SampleNameList[SampList->NumRanges] = Name;
		SampList->NumRanges += 1;
		return True;
	}


/* get the number of sample ranges specified in the list */
long									GetSampleSelectorListLength(SampleSelectorRec* SampList)
	{
		CheckPtrExistence(SampList);
		return SampList->NumRanges;
	}


/* return the low bound frequency of a particular list entry */
SampRangeHzType				GetSampleListEntryLowFreqBound(SampleSelectorRec* SampList,
												long Index)
	{
		CheckPtrExistence(SampList);
		ERROR((Index < 0) || (Index >= SampList->NumRanges),PRERR(ForceAbort,
			"GetSampleListEntryLowFreqBound:  index is out of range"));
		PRNGCHK(SampList->BoundaryList,&(SampList->BoundaryList[Index]),
			sizeof(SampList->BoundaryList[Index]));
		return SampList->BoundaryList[Index];
	}


/* return the high bound frequency of a particular list entry */
SampRangeHzType				GetSampleListEntryHighFreqBound(SampleSelectorRec* SampList,
												long Index)
	{
		CheckPtrExistence(SampList);
		ERROR((Index < 0) || (Index >= SampList->NumRanges),PRERR(ForceAbort,
			"GetSampleListEntryHighFreqBound:  index is out of range"));
		PRNGCHK(SampList->BoundaryList,&(SampList->BoundaryList[Index + 1]),
			sizeof(SampList->BoundaryList[Index + 1]));
		return SampList->BoundaryList[Index + 1];
	}


/* get the actual ptr to the sample name */
char*									GetSampleListEntryName(SampleSelectorRec* SampList, long Index)
	{
		CheckPtrExistence(SampList);
		ERROR((Index < 0) || (Index >= SampList->NumRanges),PRERR(ForceAbort,
			"GetSampleListEntryName:  index is out of range"));
		PRNGCHK(SampList->SampleNameList,&(SampList->SampleNameList[Index]),
			sizeof(SampList->SampleNameList[Index]));
		return SampList->SampleNameList[Index];
	}


/* resolve named references to samples.  returns False if it can't */
MyBoolean							ResolveSamplesInSampleList(SampleSelectorRec* SampList,
												struct SampleListRec* SampleList,
												struct AlgoSampListRec* AlgoSampList)
	{
		long								Scan;

		CheckPtrExistence(SampList);
		CheckPtrExistence(SampleList);
		CheckPtrExistence(AlgoSampList);
		ERROR(SampList->ReferencedObjectList != NIL,PRERR(ForceAbort,
			"ResolveSamplesInSampleList:  ReferencedObjectList is not NIL"));
		ERROR(SampList->ReferencedObjectTypeList != NIL,PRERR(ForceAbort,
			"ResolveSamplesInSampleList:  ReferencedObjectTypeList is not NIL"));

		SampList->ReferencedObjectList = (void**)AllocPtrCanFail(SampList->NumRanges
			* sizeof(void*),"SampleSelectorRec:  ReferencedObjectList");
		if (SampList->ReferencedObjectList == NIL)
			{
			 FailurePoint1:
				return False;
			}
		SampList->ReferencedObjectTypeList = (SampleSourceTypes*)AllocPtrCanFail(
			SampList->NumRanges * sizeof(SampleSourceTypes),
			"SampleSelectorRec:  ReferencedObjectTypeList");
		if (SampList->ReferencedObjectTypeList == NIL)
			{
			 FailurePoint2:
				ReleasePtr((char*)SampList->ReferencedObjectList);
				SampList->ReferencedObjectList = NIL;
				goto FailurePoint1;
			}

		for (Scan = 0; Scan < SampList->NumRanges; Scan += 1)
			{
				PRNGCHK(SampList->SampleNameList,&(SampList->SampleNameList[Scan]),
					sizeof(SampList->SampleNameList[Scan]));
				SampList->ReferencedObjectList[Scan] = SampleListLookupNamedSample(
					SampleList,SampList->SampleNameList[Scan]);
				if (SampList->ReferencedObjectList[Scan] != NIL)
					{
						SampList->ReferencedObjectTypeList[Scan] = eDataSample;
					}
				 else
					{
						SampList->ReferencedObjectList[Scan] = AlgoSampListLookupNamedAlgoSamp(
							AlgoSampList,SampList->SampleNameList[Scan]);
						if (SampList->ReferencedObjectList[Scan] != NIL)
							{
								SampList->ReferencedObjectTypeList[Scan] = eAlgoSample;
							}
						 else
							{
								return False;
							}
					}
			}

		return True;
	}


/* resolve named references to wave tables.  returns False if it can't */
MyBoolean							ResolveWaveTablesInSampleList(SampleSelectorRec* SampList,
												struct WaveTableListRec* WaveTableList,
												struct AlgoWaveTableListRec* AlgoWaveTableList)
	{
		long								Scan;

		CheckPtrExistence(SampList);
		CheckPtrExistence(WaveTableList);
		CheckPtrExistence(AlgoWaveTableList);
		ERROR(SampList->ReferencedObjectList != NIL,PRERR(ForceAbort,
			"ResolveWaveTablesInSampleList:  ReferencedObjectList is not NIL"));
		ERROR(SampList->ReferencedObjectTypeList != NIL,PRERR(ForceAbort,
			"ResolveWaveTablesInSampleList:  ReferencedObjectTypeList is not NIL"));

		SampList->ReferencedObjectList = (void**)AllocPtrCanFail(SampList->NumRanges
			* sizeof(void*),"SampleSelectorRec:  ReferencedObjectList");
		if (SampList->ReferencedObjectList == NIL)
			{
			 FailurePoint1:
				return False;
			}
		SampList->ReferencedObjectTypeList = (SampleSourceTypes*)AllocPtrCanFail(
			SampList->NumRanges * sizeof(SampleSourceTypes),
			"SampleSelectorRec:  ReferencedObjectTypeList");
		if (SampList->ReferencedObjectTypeList == NIL)
			{
			 FailurePoint2:
				ReleasePtr((char*)SampList->ReferencedObjectList);
				SampList->ReferencedObjectList = NIL;
				goto FailurePoint1;
			}

		for (Scan = 0; Scan < SampList->NumRanges; Scan += 1)
			{
				PRNGCHK(SampList->SampleNameList,&(SampList->SampleNameList[Scan]),
					sizeof(SampList->SampleNameList[Scan]));
				SampList->ReferencedObjectList[Scan] = WaveTableListLookupNamedWaveTable(
					WaveTableList,SampList->SampleNameList[Scan]);
				if (SampList->ReferencedObjectList[Scan] != NIL)
					{
						SampList->ReferencedObjectTypeList[Scan] = eDataWaveTable;
					}
				 else
					{
						SampList->ReferencedObjectList[Scan]
							= AlgoWaveTableListLookupNamedAlgoWaveTable(AlgoWaveTableList,
							SampList->SampleNameList[Scan]);
						if (SampList->ReferencedObjectList[Scan] != NIL)
							{
								SampList->ReferencedObjectTypeList[Scan] = eAlgoWaveTable;
							}
						 else
							{
								return False;
							}
					}
			}

		return True;
	}


/* get the wave type of an entry */
SampleSourceTypes			GetSampleListWaveType(SampleSelectorRec* SampList,
												long Index)
	{
		CheckPtrExistence(SampList);
		ERROR(SampList->ReferencedObjectTypeList == NIL,PRERR(ForceAbort,
			"GetSampleListWaveType:  type list is NIL (refs haven't been resolved yet)"));
		CheckPtrExistence(SampList->ReferencedObjectTypeList);
		ERROR((Index < 0) || (Index >= SampList->NumRanges),PRERR(ForceAbort,
			"GetSampleListWaveType:  index out of range"));
		PRNGCHK(SampList->ReferencedObjectTypeList,
			&(SampList->ReferencedObjectTypeList[Index]),
			sizeof(SampList->ReferencedObjectTypeList[Index]));
		return SampList->ReferencedObjectTypeList[Index];
	}


/* get a reference to the object */
void*									GetSampleListWaveReference(SampleSelectorRec* SampList,
												long Index)
	{
		CheckPtrExistence(SampList);
		ERROR(SampList->ReferencedObjectList == NIL,PRERR(ForceAbort,
			"GetSampleListWaveReference:  type list is NIL (refs haven't been resolved yet)"));
		CheckPtrExistence(SampList->ReferencedObjectList);
		ERROR((Index < 0) || (Index >= SampList->NumRanges),PRERR(ForceAbort,
			"GetSampleListWaveReference:  index out of range"));
		PRNGCHK(SampList->ReferencedObjectList,&(SampList->ReferencedObjectList[Index]),
			sizeof(SampList->ReferencedObjectList[Index]));
		return SampList->ReferencedObjectList[Index];
	}
