/* WaveTableStorage.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 "WaveTableStorage.h"
#include "Array.h"
#include "Memory.h"
#include "DataMunging.h"


struct WaveTableStorageRec
	{
		ArrayRec*				ListOfTables; /* of char*s or short*s */
		NumBitsType			NumBits;
		long						NumFrames;
	};


/* create a new wave table storage object */
WaveTableStorageRec*		NewWaveTableStorage(NumBitsType NumBits, long NumFrames)
	{
		WaveTableStorageRec*	Storage;

		ERROR((NumBits != eSample8bit) && (NumBits != eSample16bit),
			PRERR(ForceAbort,"NewWaveTableStorage:  bad number of bits"));
		ERROR((NumFrames != 2) && (NumFrames != 4) && (NumFrames != 8) && (NumFrames != 16)
			&& (NumFrames != 32) && (NumFrames != 64) && (NumFrames != 128)
			&& (NumFrames != 256) && (NumFrames != 512) && (NumFrames != 1024)
			&& (NumFrames != 2048) && (NumFrames != 4096) && (NumFrames != 8192)
			&& (NumFrames != 16384) && (NumFrames != 32768) && (NumFrames != 65536),
			PRERR(ForceAbort,"NewWaveTableStorage:  bad number of frames"));
		Storage = (WaveTableStorageRec*)AllocPtrCanFail(sizeof(WaveTableStorageRec),
			"WaveTableStorageRec");
		if (Storage == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		Storage->ListOfTables = NewArray();
		if (Storage->ListOfTables == NIL)
			{
				ReleasePtr((char*)Storage);
				goto FailurePoint1;
			}
		Storage->NumBits = NumBits;
		Storage->NumFrames = NumFrames;
		return Storage;
	}


/* dispose of the wave table storage object */
void										DisposeWaveTableStorage(WaveTableStorageRec* Storage)
	{
		long									Scan;
		long									Limit;

		CheckPtrExistence(Storage);
		Limit = ArrayGetLength(Storage->ListOfTables);
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				ReleasePtr((char*)ArrayGetElement(Storage->ListOfTables,Scan));
			}
		DisposeArray(Storage->ListOfTables);
		ReleasePtr((char*)Storage);
	}


/* get the number of frames per table */
long										WaveTableStorageNumFramesPerTable(WaveTableStorageRec* Storage)
	{
		CheckPtrExistence(Storage);
		return Storage->NumFrames;
	}


/* get the number of tables */
long										WaveTableStorageNumTables(WaveTableStorageRec* Storage)
	{
		CheckPtrExistence(Storage);
		return ArrayGetLength(Storage->ListOfTables);
	}


/* get the number of bits for the wave table */
NumBitsType							WaveTableStorageNumBits(WaveTableStorageRec* Storage)
	{
		CheckPtrExistence(Storage);
		return Storage->NumBits;
	}


/* get a reference to a table.  this is NOT copied, and there is no type information. */
/* it is formatted as an array of characters for 8 bit data or array of shorts for */
/* 16 bit data.  it contains (WaveTableStorageNumFramesPerTable + 1) elements for */
/* each table.  The last element is a repeat of the first, and is provided for */
/* making anti-aliasing more efficient. */
void*										WaveTableStorageGetTable(WaveTableStorageRec* Storage,
													long Index)
	{
		CheckPtrExistence(Storage);
		ERROR((Index < 0) || (Index >= ArrayGetLength(Storage->ListOfTables)),
			PRERR(ForceAbort,"WaveTableStorageGetTable:  index out of range"));
		return ArrayGetElement(Storage->ListOfTables,Index);
	}


/* append a new (zeroed out) table to the end of the array */
MyBoolean								WaveTableStorageAppendEntry(WaveTableStorageRec* Storage)
	{
		char*									Buffer;
		long									Limit;
		long									Scan;

		CheckPtrExistence(Storage);
		switch (Storage->NumBits)
			{
				default:
					EXECUTE(PRERR(ForceAbort,
						"WaveTableStorageAppendEntry:  internal invalid num bits"));
					break;
				case eSample8bit:
					Buffer = AllocPtrCanFail(Storage->NumFrames + 1,
						"WaveTableStorageAppendEntry");
					break;
				case eSample16bit:
					Buffer = AllocPtrCanFail(sizeof(short) * (Storage->NumFrames + 1),
						"WaveTableStorageAppendEntry");
					break;
			}
		if (Buffer == NIL)
			{
			 FailurePoint1:
				return False;
			}
		if (!ArrayAppendElement(Storage->ListOfTables,Buffer))
			{
			 FailurePoint2:
				ReleasePtr(Buffer);
				goto FailurePoint1;
			}
		Limit = PtrSize(Buffer);
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				Buffer[Scan] = 0;
			}
		return True;
	}


/* put a value into a frame in a table */
void										WaveTableStorageSetFrame(WaveTableStorageRec* Storage,
													long TableIndex, long FrameIndex, largefixedsigned Value)
	{
		long									NormalizedValue;
		char*									Reference;

		CheckPtrExistence(Storage);
		ERROR((TableIndex < 0) || (TableIndex >= ArrayGetLength(Storage->ListOfTables)),
			PRERR(ForceAbort,"WaveTableStorageSetFrame:  table index out of range"));
		Reference = (char*)ArrayGetElement(Storage->ListOfTables,TableIndex);
		ERROR((FrameIndex < 0) || (FrameIndex >= Storage->NumFrames),
			PRERR(ForceAbort,"WaveTableStorageSetFrame:  frame index out of range"));
		switch (Storage->NumBits)
			{
				default:
					EXECUTE(PRERR(ForceAbort,
						"WaveTableStorageSetFrame:  internal invalid num bits"));
					break;
				case eSample8bit:
					NormalizedValue = roundtonearest(largefixed2double(Value) * MAX8BIT);
					if (NormalizedValue < MIN8BIT)
						{
							NormalizedValue = MIN8BIT;
						}
					else if (NormalizedValue > MAX8BIT)
						{
							NormalizedValue = MAX8BIT;
						}
					PRNGCHK(Reference,&(((char*)Reference)[FrameIndex]),sizeof(char));
					((char*)Reference)[FrameIndex] = NormalizedValue;
					if (FrameIndex == 0)
						{
							PRNGCHK(Reference,&(((char*)Reference)[Storage->NumFrames]),
								sizeof(char));
							((char*)Reference)[Storage->NumFrames] = NormalizedValue;
						}
					break;
				case eSample16bit:
					NormalizedValue = roundtonearest(largefixed2double(Value) * MAX16BIT);
					if (NormalizedValue < MIN16BIT)
						{
							NormalizedValue = MIN16BIT;
						}
					else if (NormalizedValue > MAX16BIT)
						{
							NormalizedValue = MAX16BIT;
						}
					PRNGCHK(Reference,&(((short*)Reference)[FrameIndex]),sizeof(short));
					((short*)Reference)[FrameIndex] = NormalizedValue;
					if (FrameIndex == 0)
						{
							PRNGCHK(Reference,&(((short*)Reference)[Storage->NumFrames]),
								sizeof(short));
							((short*)Reference)[Storage->NumFrames] = NormalizedValue;
						}
					break;
			}
	}


/* get a value from a frame in a table */
largefixedsigned				WaveTableStorageGetFrame(WaveTableStorageRec* Storage,
													long TableIndex, long FrameIndex)
	{
		double								ReturnValue;
		char*									Reference;

		CheckPtrExistence(Storage);
		ERROR((TableIndex < 0) || (TableIndex >= ArrayGetLength(Storage->ListOfTables)),
			PRERR(ForceAbort,"WaveTableStorageGetFrame:  table index out of range"));
		Reference = (char*)ArrayGetElement(Storage->ListOfTables,TableIndex);
		ERROR((FrameIndex < 0) || (FrameIndex >= Storage->NumFrames),
			PRERR(ForceAbort,"WaveTableStorageGetFrame:  frame index out of range"));
		switch (Storage->NumBits)
			{
				default:
					EXECUTE(PRERR(ForceAbort,
						"WaveTableStorageGetFrame:  internal invalid num bits"));
					break;
				case eSample8bit:
					PRNGCHK(Reference,&(((char*)Reference)[FrameIndex]),sizeof(char));
					ReturnValue = ((double)((signed char*)Reference)[FrameIndex]) / MAX8BIT;
					break;
				case eSample16bit:
					PRNGCHK(Reference,&(((short*)Reference)[FrameIndex]),sizeof(short));
					ReturnValue = ((double)((signed short*)Reference)[FrameIndex]) / MAX16BIT;
					break;
			}
		return double2largefixed(ReturnValue);
	}


/* make a duplicate of the wave table */
WaveTableStorageRec*		WaveTableDuplicate(WaveTableStorageRec* Original)
	{
		WaveTableStorageRec*	Copy;
		long									Limit;
		long									Scan;

		CheckPtrExistence(Original);

		Copy = (WaveTableStorageRec*)AllocPtrCanFail(sizeof(WaveTableStorageRec),
			"WaveTableStorageRec");
		if (Copy == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		Copy->NumBits = Original->NumBits;
		Copy->NumFrames = Original->NumFrames;

		Copy->ListOfTables = NewArray();
		if (Copy->ListOfTables == NIL)
			{
			 FailurePoint2:
				ReleasePtr((char*)Copy);
				goto FailurePoint1;
			}

		Limit = ArrayGetLength(Original->ListOfTables);
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				char*									SliceCopy;

				SliceCopy = CopyPtr((char*)ArrayGetElement(Original->ListOfTables,Scan));
				if (SliceCopy == NIL)
					{
						/* bummer */
					 FailurePoint3:
						Limit = ArrayGetLength(Copy->ListOfTables);
						for (Scan = 0; Scan < Limit; Scan += 1)
							{
								ReleasePtr((char*)ArrayGetElement(Copy->ListOfTables,Scan));
							}
						DisposeArray(Copy->ListOfTables);
						goto FailurePoint2;
					}
				SetTag(SliceCopy,"WaveTableStorageAppendEntry");
				if (!ArrayAppendElement(Copy->ListOfTables,SliceCopy))
					{
					 FailurePoint3a:
						ReleasePtr((char*)SliceCopy);
						goto FailurePoint3;
					}
			}

		return Copy;
	}
