/* WaveTableObject.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 "WaveTableObject.h"
#include "Array.h"
#include "WaveTableList.h"
#include "Memory.h"
#include "DataMunging.h"
#include "FixedPoint.h"
#include "WaveTableWindow.h"
#include "WaveTableStorage.h"
#include "BufferedFileInput.h"
#include "BufferedFileOutput.h"
#include "BinaryCodedDecimal.h"


struct WaveTableObjectRec
	{
		MyBoolean								DataModified;

		char*										Name;
		WaveTableStorageRec*		WaveTableData;
		char*										WaveTableFormula;
		double									TestAttackDuration;
		double									TestDecayDuration;
		double									TestFrequency;
		long										TestSamplingRate;

		WaveTableWindowRec*			WaveTableWindow;

		struct CodeCenterRec*		CodeCenter;
		struct MainWindowRec*		MainWindow;
		WaveTableListRec*				WaveTableList;

		short										SavedWindowXLoc;
		short										SavedWindowYLoc;
		short										SavedWindowWidth;
		short										SavedWindowHeight;
	};


/* create a new wave table object with reasonable defaults. */
WaveTableObjectRec*		NewWaveTableObject(struct CodeCenterRec* CodeCenter,
												struct MainWindowRec* MainWindow,
												struct WaveTableListRec* WaveTableList)
	{
		WaveTableObjectRec*	WaveTableObj;

		WaveTableObj = (WaveTableObjectRec*)AllocPtrCanFail(sizeof(WaveTableObjectRec),
			"WaveTableObjectRec");
		if (WaveTableObj == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		WaveTableObj->DataModified = False;
		WaveTableObj->WaveTableWindow = NIL;
		WaveTableObj->CodeCenter = CodeCenter;
		WaveTableObj->MainWindow = MainWindow;
		WaveTableObj->WaveTableList = WaveTableList;
		WaveTableObj->TestAttackDuration = 1;
		WaveTableObj->TestDecayDuration = 2;
		WaveTableObj->TestFrequency = 261.625565300598635;
		WaveTableObj->TestSamplingRate = 44100;
		WaveTableObj->SavedWindowXLoc = 0;
		WaveTableObj->SavedWindowYLoc = 0;
		WaveTableObj->SavedWindowWidth = 0;
		WaveTableObj->SavedWindowHeight = 0;
		WaveTableObj->Name = StringToBlockCopy("untitled");
		if (WaveTableObj->Name == NIL)
			{
			 FailurePoint2:
				ReleasePtr((char*)WaveTableObj);
				goto FailurePoint1;
			}
		WaveTableObj->WaveTableFormula = StringToBlockCopy(
			"# data : fixedarray; frames : integer; tables : integer\x0a");
		if (WaveTableObj->WaveTableFormula == NIL)
			{
			 FailurePoint3:
				ReleasePtr(WaveTableObj->Name);
				goto FailurePoint2;
			}
		WaveTableObj->WaveTableData = NewWaveTableStorage(eSample16bit,256);
		if (WaveTableObj->WaveTableData == NIL)
			{
			 FailurePoint4:
				ReleasePtr(WaveTableObj->WaveTableFormula);
				goto FailurePoint3;
			}
		return WaveTableObj;
	}


/* create a new wave table based on the data being passed in. */
WaveTableObjectRec*		NewWaveTableObjectFromData(struct CodeCenterRec* CodeCenter,
												struct MainWindowRec* MainWindow,
												struct WaveTableListRec* WaveTableList, char* RawData,
												NumBitsType NumBits, long NumTables, long FramesPerTable)
	{
		long								Scan;
		WaveTableObjectRec*	WaveTableObj;

		WaveTableObj = (WaveTableObjectRec*)AllocPtrCanFail(sizeof(WaveTableObjectRec),
			"WaveTableObjectRec");
		if (WaveTableObj == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		WaveTableObj->DataModified = False;
		WaveTableObj->WaveTableWindow = NIL;
		WaveTableObj->CodeCenter = CodeCenter;
		WaveTableObj->MainWindow = MainWindow;
		WaveTableObj->WaveTableList = WaveTableList;
		WaveTableObj->TestAttackDuration = 1;
		WaveTableObj->TestDecayDuration = 2;
		WaveTableObj->TestFrequency = 261.625565300598635;
		WaveTableObj->TestSamplingRate = 44100;
		WaveTableObj->SavedWindowXLoc = 0;
		WaveTableObj->SavedWindowYLoc = 0;
		WaveTableObj->SavedWindowWidth = 0;
		WaveTableObj->SavedWindowHeight = 0;
		WaveTableObj->Name = StringToBlockCopy("AlgoWaveTable Copy");
		if (WaveTableObj->Name == NIL)
			{
			 FailurePoint2:
				ReleasePtr((char*)WaveTableObj);
				goto FailurePoint1;
			}
		WaveTableObj->WaveTableFormula = StringToBlockCopy(
			"# data : fixedarray; frames : integer; tables : integer\x0a");
		if (WaveTableObj->WaveTableFormula == NIL)
			{
			 FailurePoint3:
				ReleasePtr(WaveTableObj->Name);
				goto FailurePoint2;
			}
		WaveTableObj->WaveTableData = NewWaveTableStorage(NumBits,FramesPerTable);
		if (WaveTableObj->WaveTableData == NIL)
			{
			 FailurePoint4:
				ReleasePtr(WaveTableObj->WaveTableFormula);
				goto FailurePoint3;
			}
		for (Scan = 0; Scan < NumTables; Scan += 1)
			{
				if (!WaveTableStorageAppendEntry(WaveTableObj->WaveTableData))
					{
					 FailurePoint5:
						DisposeWaveTableStorage(WaveTableObj->WaveTableData);
						goto FailurePoint4;
					}
			}
		for (Scan = 0; Scan < NumTables; Scan += 1)
			{
				long						Index;

				switch (NumBits)
					{
						default:
							EXECUTE(PRERR(ForceAbort,"NewWaveTableObjectFromData:  bad NumBits"));
							break;
						case eSample8bit:
							for (Index = 0; Index < FramesPerTable; Index += 1)
								{
									WaveTableStorageSetFrame(WaveTableObj->WaveTableData,Scan,
										Index,double2largefixed(
										((double)(((signed char*)RawData)[Scan * FramesPerTable + Index]))
										/ MAX8BIT));
								}
							break;
						case eSample16bit:
							for (Index = 0; Index < FramesPerTable; Index += 1)
								{
									WaveTableStorageSetFrame(WaveTableObj->WaveTableData,Scan,
										Index,double2largefixed(
										((double)(((signed short*)RawData)[Scan * FramesPerTable + Index]))
										/ MAX16BIT));
								}
							break;
					}
			}
		return WaveTableObj;
	}


/* dispose of a wave table object */
void									DisposeWaveTableObject(WaveTableObjectRec* WaveTableObj)
	{
		CheckPtrExistence(WaveTableObj);
		if (WaveTableObj->WaveTableWindow != NIL)
			{
				DisposeWaveTableWindow(WaveTableObj->WaveTableWindow);
				ERROR(WaveTableObj->WaveTableWindow != NIL,PRERR(ForceAbort,
					"DisposeWaveTableObject:  disposed window but it isn't NIL"));
			}
		DisposeWaveTableStorage(WaveTableObj->WaveTableData);
		ReleasePtr(WaveTableObj->Name);
		ReleasePtr(WaveTableObj->WaveTableFormula);
		ReleasePtr((char*)WaveTableObj);
	}


/* find out if wave table object has been modified */
MyBoolean							HasWaveTableObjectBeenModified(WaveTableObjectRec* WaveTableObj)
	{
		CheckPtrExistence(WaveTableObj);
		if (WaveTableObj->WaveTableWindow != NIL)
			{
				return WaveTableObj->DataModified
					|| HasWaveTableWindowBeenModified(WaveTableObj->WaveTableWindow);
			}
		 else
			{
				return WaveTableObj->DataModified;
			}
	}


/* get a copy of the wave table's name */
char*									WaveTableObjectGetNameCopy(WaveTableObjectRec* WaveTableObj)
	{
		char*								NameTemp;

		CheckPtrExistence(WaveTableObj);
		if (WaveTableObj->WaveTableWindow != NIL)
			{
				NameTemp = WaveTableWindowGetNameCopy(WaveTableObj->WaveTableWindow);
			}
		 else
			{
				NameTemp = CopyPtr(WaveTableObj->Name);
			}
		if (NameTemp != NIL)
			{
				SetTag(NameTemp,"WaveTableNameCopy");
			}
		return NameTemp;
	}


/* set the wave table's name.  the object becomes the owner of the Name block, */
/* so the caller should not release it. */
void									WaveTableObjectNewName(WaveTableObjectRec* WaveTableObj,
												char* Name)
	{
		CheckPtrExistence(WaveTableObj);
		CheckPtrExistence(Name);
		ReleasePtr(WaveTableObj->Name);
		SetTag(Name,"WaveTableName");
		WaveTableObj->Name = Name;
		WaveTableObj->DataModified = True;
		WaveTableListWaveTableNameChanged(WaveTableObj->WaveTableList,WaveTableObj);
	}


/* get a copy of the formula applied to the wave table. */
char*									WaveTableObjectGetFormulaCopy(WaveTableObjectRec* WaveTableObj)
	{
		char*								TextCopy;

		CheckPtrExistence(WaveTableObj);
		if (WaveTableObj->WaveTableWindow != NIL)
			{
				TextCopy = WaveTableWindowGetFormulaCopy(WaveTableObj->WaveTableWindow);
			}
		 else
			{
				TextCopy = CopyPtr(WaveTableObj->WaveTableFormula);
			}
		if (TextCopy != NIL)
			{
				SetTag(TextCopy,"WaveTableFormulaCopy");
			}
		return TextCopy;
	}


/* install a new formula into the wave table.  the object becomes the owner of */
/* the formula, so the caller should not dispose it. */
void									WaveTableObjectNewFormula(WaveTableObjectRec* WaveTableObj,
												char* Formula)
	{
		CheckPtrExistence(WaveTableObj);
		CheckPtrExistence(Formula);
		ReleasePtr(WaveTableObj->WaveTableFormula);
		SetTag(Formula,"WaveTableFormula");
		WaveTableObj->WaveTableFormula = Formula;
		WaveTableObj->DataModified = True;
	}


/* find out whether its 8 or 16 bits. */
NumBitsType						WaveTableObjectGetNumBits(WaveTableObjectRec* WaveTableObj)
	{
		CheckPtrExistence(WaveTableObj);
		if (WaveTableObj->WaveTableWindow != NIL)
			{
				return WaveTableWindowGetNumBits(WaveTableObj->WaveTableWindow);
			}
		 else
			{
				return WaveTableStorageNumBits(WaveTableObj->WaveTableData);
			}
	}


/* find out how many tables there are in the wave table */
long									WaveTableObjectGetNumTables(WaveTableObjectRec* WaveTableObj)
	{
		CheckPtrExistence(WaveTableObj);
		if (WaveTableObj->WaveTableWindow != NIL)
			{
				return WaveTableWindowGetNumTables(WaveTableObj->WaveTableWindow);
			}
		 else
			{
				return WaveTableStorageNumTables(WaveTableObj->WaveTableData);
			}
	}


/* find out how many entries there are in a single table */
long									WaveTableObjectEntriesPerTable(WaveTableObjectRec* WaveTableObj)
	{
		CheckPtrExistence(WaveTableObj);
		if (WaveTableObj->WaveTableWindow != NIL)
			{
				return WaveTableWindowGetNumFramesPerTable(WaveTableObj->WaveTableWindow);
			}
		 else
			{
				return WaveTableStorageNumFramesPerTable(WaveTableObj->WaveTableData);
			}
	}


/* get the raw data for a particular wave table.  this returns the actual data, so */
/* don't dispose it.  if any operations are performed on the wave table, this */
/* pointer may become invalid.  the data is in the following format: */
/*  - 8-bit:  array of signed bytes */
/*  - 16-bit:  array of signed short integers */
char*									WaveTableObjectGetRawSlice(WaveTableObjectRec* WaveTableObj,
												long WaveTableIndex)
	{
		CheckPtrExistence(WaveTableObj);
		ERROR((WaveTableIndex < 0) || (WaveTableIndex
			>= WaveTableObjectGetNumTables(WaveTableObj)),PRERR(ForceAbort,
			"WaveTableObjectGetRawSlice:  index out of range"));
		if (WaveTableObj->WaveTableWindow != NIL)
			{
				if (!WaveTableWindowForceWaveTableUpdate(WaveTableObj->WaveTableWindow))
					{
						return NIL;
					}
			}
		return (char*)WaveTableStorageGetTable(WaveTableObj->WaveTableData,WaveTableIndex);
	}


/* put new data into the wave table. the object becomes the owner of all data passed in. */
void									WaveTableObjectPutNewData(WaveTableObjectRec* WaveTableObj,
												struct WaveTableStorageRec* NewWaveTable)
	{
		CheckPtrExistence(WaveTableObj);
		CheckPtrExistence(NewWaveTable);
		DisposeWaveTableStorage(WaveTableObj->WaveTableData);
		WaveTableObj->WaveTableData = NewWaveTable;
		WaveTableObj->DataModified = True;
	}


/* tell the wave table to open it's editor window */
MyBoolean							WaveTableObjectOpenWindow(WaveTableObjectRec* WaveTableObj)
	{
		CheckPtrExistence(WaveTableObj);
		if (WaveTableObj->WaveTableWindow == NIL)
			{
				WaveTableObj->WaveTableWindow = NewWaveTableWindow(WaveTableObj->MainWindow,
					WaveTableObj,WaveTableObj->CodeCenter,WaveTableObj->WaveTableList,
					WaveTableObj->WaveTableData,WaveTableObj->SavedWindowXLoc,
					WaveTableObj->SavedWindowYLoc,WaveTableObj->SavedWindowWidth,
					WaveTableObj->SavedWindowHeight);
			}
		 else
			{
				WaveTableWindowBringToTop(WaveTableObj->WaveTableWindow);
			}
		return (WaveTableObj->WaveTableWindow != NIL);
	}


/* this is called by the window when it is closing to notify the object. */
/* the object should not take any action. */
void									WaveTableObjectClosingWindowNotify(
												WaveTableObjectRec* WaveTableObj, short NewX, short NewY,
												short NewWidth, short NewHeight)
	{
		CheckPtrExistence(WaveTableObj);
		ERROR(WaveTableObj->WaveTableWindow == NIL,PRERR(ForceAbort,
			"WaveTableObjectClosingWindowNotify:  window not open"));
		WaveTableObj->WaveTableWindow = NIL;
		WaveTableObj->SavedWindowXLoc = NewX;
		WaveTableObj->SavedWindowYLoc = NewY;
		WaveTableObj->SavedWindowWidth = NewWidth;
		WaveTableObj->SavedWindowHeight = NewHeight;
	}


/* obtain a fixedpoint array of data.  NIL is returned if there isn't enough memory. */
largefixedsigned*			WaveTableObjectGetFixed(WaveTableObjectRec* WaveTableObj)
	{
		largefixedsigned*		Array;

		CheckPtrExistence(WaveTableObj);
		if (WaveTableObj->WaveTableWindow == NIL)
			{
				long								TableLimit;
				long								FrameLimit;
				long								TableScan;

				TableLimit = WaveTableObjectGetNumTables(WaveTableObj);
				FrameLimit = WaveTableObjectEntriesPerTable(WaveTableObj);
				Array = (largefixedsigned*)AllocPtrCanFail(TableLimit * FrameLimit
					* sizeof(largefixedsigned),"WaveTableFixedArrayCopy");
				if (Array != NIL)
					{
						for (TableScan = 0; TableScan < TableLimit; TableScan += 1)
							{
								long								FrameScan;

								for (FrameScan = 0; FrameScan < FrameLimit; FrameScan += 1)
									{
										PRNGCHK(Array,&(Array[TableScan * FrameLimit + FrameScan]),
											sizeof(Array[TableScan * FrameLimit + FrameScan]));
										Array[TableScan * FrameLimit + FrameScan]
											= WaveTableStorageGetFrame(WaveTableObj->WaveTableData,
											TableScan,FrameScan);
									}
							}
					}
			}
		 else
			{
				Array = WaveTableWindowGetWaveArray(WaveTableObj->WaveTableWindow);
			}
		return Array;
	}


/* the document's name has changed, so we need to update the windows */
void									WaveTableObjectGlobalNameChange(WaveTableObjectRec* WaveTableObj,
												char* NewFilename)
	{
		CheckPtrExistence(WaveTableObj);
		if (WaveTableObj->WaveTableWindow != NIL)
			{
				WaveTableWindowGlobalNameChange(WaveTableObj->WaveTableWindow,NewFilename);
			}
	}


/* get the test attack duration */
double								WaveTableObjectGetTestAttack(WaveTableObjectRec* WaveTableObj)
	{
		CheckPtrExistence(WaveTableObj);
		if (WaveTableObj->WaveTableWindow != NIL)
			{
				return WaveTableWindowGetAscendingDuration(WaveTableObj->WaveTableWindow);
			}
		 else
			{
				return WaveTableObj->TestAttackDuration;
			}
	}


/* put a new test attack duration */
void									SetWaveTableObjectTestAttack(WaveTableObjectRec* WaveTableObj,
												double NewTestAttack)
	{
		CheckPtrExistence(WaveTableObj);
		WaveTableObj->TestAttackDuration = NewTestAttack;
	}


/* get the test decay duration */
double								WaveTableObjectGetTestDecay(WaveTableObjectRec* WaveTableObj)
	{
		CheckPtrExistence(WaveTableObj);
		if (WaveTableObj->WaveTableWindow != NIL)
			{
				return WaveTableWindowGetDescendingDuration(WaveTableObj->WaveTableWindow);
			}
		 else
			{
				return WaveTableObj->TestDecayDuration;
			}
	}


/* put a new test decay duration */
void									SetWaveTableObjectTestDecay(WaveTableObjectRec* WaveTableObj,
												double NewTestDecay)
	{
		CheckPtrExistence(WaveTableObj);
		WaveTableObj->TestDecayDuration = NewTestDecay;
	}


/* get the test sampling rate */
long									WaveTableObjectGetTestSamplingRate(WaveTableObjectRec* WaveTableObj)
	{
		CheckPtrExistence(WaveTableObj);
		if (WaveTableObj->WaveTableWindow != NIL)
			{
				return WaveTableWindowGetTestSamplingRate(WaveTableObj->WaveTableWindow);
			}
		 else
			{
				return WaveTableObj->TestSamplingRate;
			}
	}


/* put a new test sampling rate */
void									SetWaveTableObjectTestSamplingRate(WaveTableObjectRec* WaveTableObj,
												long NewTestSamplingRate)
	{
		CheckPtrExistence(WaveTableObj);
		WaveTableObj->TestSamplingRate = NewTestSamplingRate;
	}


/* get the test pitch */
double								WaveTableObjectGetTestPitch(WaveTableObjectRec* WaveTableObj)
	{
		CheckPtrExistence(WaveTableObj);
		if (WaveTableObj->WaveTableWindow != NIL)
			{
				return WaveTableWindowGetPitch(WaveTableObj->WaveTableWindow);
			}
		 else
			{
				return WaveTableObj->TestFrequency;
			}
	}


/* put a new test pitch */
void									SetWaveTableObjectTestPitch(WaveTableObjectRec* WaveTableObj,
												double NewTestPitch)
	{
		CheckPtrExistence(WaveTableObj);
		WaveTableObj->TestFrequency = NewTestPitch;
	}


/* Wave Table Object Subblock Format: */
/*   1-byte format version number */
/*       should be 1 */
/*   2-byte little endian window X location (signed; origin at upper left corner) */
/*   2-byte little endian window Y location */
/*   2-byte little endian window width */
/*   2-byte little endian window height */
/*   4-byte little endian wave table name length descriptor */
/*   n-byte name string (line feed = 0x0a) */
/*   4-byte little endian wave table formula length descriptor */
/*   n-byte formula string (line feed = 0x0a) */
/*   4-byte little endian large integer encoded test attack duration. */
/*       large integer coded decimal is decimal * 1000000 with a */
/*       range of -1999.999999 to 1999.999999 */
/*   4-byte little endian large integer encoded test decay duration. */
/*   4-byte little endian test frequency fractional portion */
/*       unsigned; divide by 2^32 to get the actual fraction */
/*   4-byte little endian test frequency integer portion */
/*       total test frequency should be between 0.01 and 1e6 */
/*   4-byte little endian test sampling rate */
/*       should be between 100 and 65535 */
/*   4-byte little endian number of tables */
/*   4-byte little endian number of frames per table */
/*       must be an integral power or 2 between 2 and 65536 */
/*   1-byte number of bits specifier */
/*       must be 8 or 16 */
/*   n-byte sample data for the wave table */
/*       data is stored as follows:  each table is stored consecutively starting */
/*       with the table numbered 0.  in each table, each sample frame is stored */
/*       consecutively as a signed 2s complement value.  8-bit sample frames */
/*       use 1 byte each.  16-bit sample frames use 2 bytes and are stored little */
/*       endian. */


/* load an object from the file */
FileLoadingErrors			WaveTableObjectNewFromFile(WaveTableObjectRec** ObjectOut,
												struct BufferedInputRec* Input, struct CodeCenterRec* CodeCenter,
												struct MainWindowRec* MainWindow,
												struct WaveTableListRec* WaveTableList)
	{
		unsigned char				UnsignedChar;
		WaveTableObjectRec*	WaveTableObj;
		FileLoadingErrors		Error;
		signed short				SignedShort;
		unsigned long				UnsignedLong;
		signed long					SignedLong;
		signed long					NumberOfTables;
		signed long					NumberOfFrames;
		NumBitsType					NumberOfBits;
		long								Scan;

		CheckPtrExistence(Input);
		CheckPtrExistence(CodeCenter);
		CheckPtrExistence(MainWindow);
		CheckPtrExistence(WaveTableList);

		WaveTableObj = (WaveTableObjectRec*)AllocPtrCanFail(sizeof(WaveTableObjectRec),
			"WaveTableObjectRec");
		if (WaveTableObj == NIL)
			{
				Error = eFileLoadOutOfMemory;
			 FailurePoint1:
				return Error;
			}

		/*   1-byte format version number */
		/*       should be 1 */
		if (!ReadBufferedUnsignedChar(Input,&UnsignedChar))
			{
				Error = eFileLoadDiskError;
			 FailurePoint2:
				ReleasePtr((char*)WaveTableObj);
				goto FailurePoint1;
			}
		if (UnsignedChar != 1)
			{
				Error = eFileLoadBadFormat;
			 FailurePoint3:
				goto FailurePoint2;
			}

		/*   2-byte little endian window X location (signed; origin at upper left corner) */
		if (!ReadBufferedSignedShortLittleEndian(Input,&SignedShort))
			{
				Error = eFileLoadDiskError;
			 FailurePoint4:
				goto FailurePoint3;
			}
		WaveTableObj->SavedWindowXLoc = SignedShort;

		/*   2-byte little endian window Y location */
		if (!ReadBufferedSignedShortLittleEndian(Input,&SignedShort))
			{
				Error = eFileLoadDiskError;
			 FailurePoint5:
				goto FailurePoint4;
			}
		WaveTableObj->SavedWindowYLoc = SignedShort;

		/*   2-byte little endian window width */
		if (!ReadBufferedSignedShortLittleEndian(Input,&SignedShort))
			{
				Error = eFileLoadDiskError;
			 FailurePoint6:
				goto FailurePoint5;
			}
		WaveTableObj->SavedWindowWidth = SignedShort;

		/*   2-byte little endian window height */
		if (!ReadBufferedSignedShortLittleEndian(Input,&SignedShort))
			{
				Error = eFileLoadDiskError;
			 FailurePoint7:
				goto FailurePoint6;
			}
		WaveTableObj->SavedWindowHeight = SignedShort;

		/*   4-byte little endian wave table name length descriptor */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint8:
				goto FailurePoint7;
			}
		if (SignedLong < 0)
			{
				Error = eFileLoadBadFormat;
			 FailurePoint9:
				goto FailurePoint8;
			}

		/*   n-byte name string (line feed = 0x0a) */
		WaveTableObj->Name = AllocPtrCanFail(SignedLong,"WaveTableObjectRec:  name");
		if (WaveTableObj->Name == NIL)
			{
				Error = eFileLoadOutOfMemory;
			 FailurePoint10:
				goto FailurePoint9;
			}
		if (!ReadBufferedInput(Input,SignedLong,WaveTableObj->Name))
			{
				Error = eFileLoadDiskError;
			 FailurePoint11:
				ReleasePtr(WaveTableObj->Name);
				goto FailurePoint10;
			}

		/*   4-byte little endian wave table formula length descriptor */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint12:
				goto FailurePoint11;
			}
		if (SignedLong < 0)
			{
				Error = eFileLoadBadFormat;
			 FailurePoint13:
				goto FailurePoint12;
			}

		/*   n-byte formula string (line feed = 0x0a) */
		WaveTableObj->WaveTableFormula = AllocPtrCanFail(SignedLong,
			"WaveTableObjectRec:  formula");
		if (WaveTableObj->WaveTableFormula == NIL)
			{
				Error = eFileLoadOutOfMemory;
			 FailurePoint14:
				goto FailurePoint13;
			}
		if (!ReadBufferedInput(Input,SignedLong,WaveTableObj->WaveTableFormula))
			{
				Error = eFileLoadDiskError;
			 FailurePoint15:
				ReleasePtr(WaveTableObj->WaveTableFormula);
				goto FailurePoint14;
			}

		/*   4-byte little endian large integer encoded test attack duration. */
		/*       large integer coded decimal is decimal * 1000000 with a */
		/*       range of -1999.999999 to 1999.999999 */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint16:
				goto FailurePoint15;
			}
		WaveTableObj->TestAttackDuration = LargeBCD2Double(SignedLong);
		if (WaveTableObj->TestAttackDuration < 0)
			{
				WaveTableObj->TestAttackDuration = 0;
			}

		/*   4-byte little endian large integer encoded test decay duration. */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint17:
				goto FailurePoint16;
			}
		WaveTableObj->TestDecayDuration = LargeBCD2Double(SignedLong);
		if (WaveTableObj->TestDecayDuration < 0)
			{
				WaveTableObj->TestDecayDuration = 0;
			}

		/*   4-byte little endian test frequency fractional portion */
		/*       unsigned; divide by 2^32 to get the actual fraction */
		if (!ReadBufferedUnsignedLongLittleEndian(Input,&UnsignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint18:
				goto FailurePoint17;
			}
		WaveTableObj->TestFrequency = (double)UnsignedLong / 4294967296.0L;

		/*   4-byte little endian test frequency integer portion */
		/*       total test frequency should be between 0.01 and 1e6 */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint19:
				goto FailurePoint18;
			}
		WaveTableObj->TestFrequency += SignedLong;
		if (WaveTableObj->TestFrequency < MINNATURALFREQ)
			{
				WaveTableObj->TestFrequency = MINNATURALFREQ;
			}
		if (WaveTableObj->TestFrequency > MAXNATURALFREQ)
			{
				WaveTableObj->TestFrequency = MAXNATURALFREQ;
			}

		/*   4-byte little endian test sampling rate */
		/*       should be between 100 and 65535 */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint20:
				goto FailurePoint19;
			}
		if (SignedLong < MINSAMPLINGRATE)
			{
				SignedLong = MINSAMPLINGRATE;
			}
		if (SignedLong > MAXSAMPLINGRATE)
			{
				SignedLong = MAXSAMPLINGRATE;
			}
		WaveTableObj->TestSamplingRate = SignedLong;

		/*   4-byte little endian number of tables */
		if (!ReadBufferedSignedLongLittleEndian(Input,&NumberOfTables))
			{
				Error = eFileLoadDiskError;
			 FailurePoint21:
				goto FailurePoint20;
			}
		if (NumberOfTables < 0)
			{
				Error = eFileLoadBadFormat;
			 FailurePoint22:
				goto FailurePoint21;
			}

		/*   4-byte little endian number of frames per table */
		/*       must be an integral power or 2 between 2 and 65536 */
		if (!ReadBufferedSignedLongLittleEndian(Input,&NumberOfFrames))
			{
				Error = eFileLoadDiskError;
			 FailurePoint23:
				goto FailurePoint22;
			}
		if ((NumberOfFrames != 2) && (NumberOfFrames != 4) && (NumberOfFrames != 8)
			&& (NumberOfFrames != 16) && (NumberOfFrames != 32) && (NumberOfFrames != 64)
			&& (NumberOfFrames != 128) && (NumberOfFrames != 256) && (NumberOfFrames != 512)
			&& (NumberOfFrames != 1024) && (NumberOfFrames != 2048)
			&& (NumberOfFrames != 4096) && (NumberOfFrames != 8192)
			&& (NumberOfFrames != 16384) && (NumberOfFrames != 32768)
			&& (NumberOfFrames != 65536))
			{
				Error = eFileLoadBadFormat;
			 FailurePoint24:
				goto FailurePoint23;
			}

		/*   1-byte number of bits specifier */
		/*       must be 8 or 16 */
		if (!ReadBufferedUnsignedChar(Input,&UnsignedChar))
			{
				Error = eFileLoadDiskError;
			 FailurePoint25:
				goto FailurePoint24;
			}
		if (UnsignedChar == 8)
			{
				NumberOfBits = eSample8bit;
			}
		else if (UnsignedChar == 16)
			{
				NumberOfBits = eSample16bit;
			}
		else
			{
				Error = eFileLoadBadFormat;
			 FailurePoint26:
				goto FailurePoint25;
			}

		/*   n-byte sample data for the wave table */
		/*       data is stored as follows:  each table is stored consecutively starting */
		/*       with the table numbered 0.  in each table, each sample frame is stored */
		/*       consecutively as a signed 2s complement value.  8-bit sample frames */
		/*       use 1 byte each.  16-bit sample frames use 2 bytes and are stored little */
		/*       endian. */
		WaveTableObj->WaveTableData = NewWaveTableStorage(NumberOfBits,NumberOfFrames);
		if (WaveTableObj->WaveTableData == NIL)
			{
				Error = eFileLoadOutOfMemory;
			 FailurePoint27:
				goto FailurePoint26;
			}
		for (Scan = 0; Scan < NumberOfTables; Scan += 1)
			{
				if (!WaveTableStorageAppendEntry(WaveTableObj->WaveTableData))
					{
						Error = eFileLoadOutOfMemory;
					 FailurePoint28:
						DisposeWaveTableStorage(WaveTableObj->WaveTableData);
						goto FailurePoint27;
					}
			}
		switch (NumberOfBits)
			{
				default:
					EXECUTE(PRERR(ForceAbort,"WaveTableObjectNewFromFile:  bad number of bits"));
					break;
				case eSample8bit:
					for (Scan = 0; Scan < NumberOfTables; Scan += 1)
						{
							long							Index;

							for (Index = 0; Index < NumberOfFrames; Index += 1)
								{
									signed char				SamplePoint;

									if (!ReadBufferedSignedChar(Input,&SamplePoint))
										{
											Error = eFileLoadDiskError;
											goto FailurePoint28;
										}
									WaveTableStorageSetFrame(WaveTableObj->WaveTableData,Scan,Index,
										double2largefixed((double)SamplePoint / MAX8BIT));
								}
						}
					break;
				case eSample16bit:
					for (Scan = 0; Scan < NumberOfTables; Scan += 1)
						{
							long							Index;

							for (Index = 0; Index < NumberOfFrames; Index += 1)
								{
									signed short			SamplePoint;

									if (!ReadBufferedSignedShortLittleEndian(Input,&SamplePoint))
										{
											Error = eFileLoadDiskError;
											goto FailurePoint28;
										}
									WaveTableStorageSetFrame(WaveTableObj->WaveTableData,Scan,Index,
										double2largefixed((double)SamplePoint / MAX16BIT));
								}
						}
					break;
			}

		/* fill in the other fields */
		WaveTableObj->DataModified = False;
		WaveTableObj->WaveTableWindow = NIL;
		WaveTableObj->CodeCenter = CodeCenter;
		WaveTableObj->MainWindow = MainWindow;
		WaveTableObj->WaveTableList = WaveTableList;

		*ObjectOut = WaveTableObj;
		return eFileLoadNoError;
	}


/* write the object out to the file. */
FileLoadingErrors			WaveTableObjectWriteDataOut(WaveTableObjectRec* WaveTableObj,
												struct BufferedOutputRec* Output)
	{
		char*								StringTemp;
		double							FrequencyTemp;
		long								NumberOfTables;
		long								NumberOfFrames;
		NumBitsType					NumberOfBits;
		long								Scan;

		CheckPtrExistence(WaveTableObj);
		CheckPtrExistence(Output);

		if (WaveTableObj->WaveTableWindow != NIL)
			{
				if (!WaveTableWindowForceWaveTableUpdate(WaveTableObj->WaveTableWindow))
					{
						return eFileLoadOutOfMemory;
					}
			}

		/*   1-byte format version number */
		/*       should be 1 */
		if (!WriteBufferedUnsignedChar(Output,1))
			{
				return eFileLoadDiskError;
			}

		/*   2-byte little endian window X location (signed; origin at upper left corner) */
		/* note that if the window is open when the file is saved, then the most */
		/* recent coordinates of the window will not be saved */
		if (!WriteBufferedSignedShortLittleEndian(Output,WaveTableObj->SavedWindowXLoc))
			{
				return eFileLoadDiskError;
			}

		/*   2-byte little endian window Y location */
		if (!WriteBufferedSignedShortLittleEndian(Output,WaveTableObj->SavedWindowYLoc))
			{
				return eFileLoadDiskError;
			}

		/*   2-byte little endian window width */
		if (!WriteBufferedSignedShortLittleEndian(Output,WaveTableObj->SavedWindowWidth))
			{
				return eFileLoadDiskError;
			}

		/*   2-byte little endian window height */
		if (!WriteBufferedSignedShortLittleEndian(Output,WaveTableObj->SavedWindowHeight))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian wave table name length descriptor */
		StringTemp = WaveTableObjectGetNameCopy(WaveTableObj);
		if (StringTemp == NIL)
			{
				return eFileLoadOutOfMemory;
			}
		if (!WriteBufferedSignedLongLittleEndian(Output,PtrSize(StringTemp)))
			{
				ReleasePtr(StringTemp);
				return eFileLoadDiskError;
			}

		/*   n-byte name string (line feed = 0x0a) */
		if (!WriteBufferedOutput(Output,PtrSize(StringTemp),StringTemp))
			{
				ReleasePtr(StringTemp);
				return eFileLoadDiskError;
			}
		ReleasePtr(StringTemp);

		/*   4-byte little endian wave table formula length descriptor */
		StringTemp = WaveTableObjectGetFormulaCopy(WaveTableObj);
		if (StringTemp == NIL)
			{
				return eFileLoadOutOfMemory;
			}
		if (!WriteBufferedSignedLongLittleEndian(Output,PtrSize(StringTemp)))
			{
				ReleasePtr(StringTemp);
				return eFileLoadDiskError;
			}

		/*   n-byte formula string (line feed = 0x0a) */
		if (!WriteBufferedOutput(Output,PtrSize(StringTemp),StringTemp))
			{
				ReleasePtr(StringTemp);
				return eFileLoadDiskError;
			}
		ReleasePtr(StringTemp);

		/*   4-byte little endian large integer encoded test attack duration. */
		/*       large integer coded decimal is decimal * 1000000 with a */
		/*       range of -1999.999999 to 1999.999999 */
		if (!WriteBufferedSignedLongLittleEndian(Output,
			Double2LargeBCD(WaveTableObjectGetTestAttack(WaveTableObj))))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian large integer encoded test decay duration. */
		if (!WriteBufferedSignedLongLittleEndian(Output,
			Double2LargeBCD(WaveTableObjectGetTestDecay(WaveTableObj))))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian test frequency fractional portion */
		/*       unsigned; divide by 2^32 to get the actual fraction */
		FrequencyTemp = WaveTableObjectGetTestPitch(WaveTableObj);
		if (!WriteBufferedUnsignedLongLittleEndian(Output,
			(unsigned long)((FrequencyTemp - (long)FrequencyTemp) * 4294967296.0L)))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian test frequency integer portion */
		/*       total test frequency should be between 0.01 and 1e6 */
		if (!WriteBufferedSignedLongLittleEndian(Output,(long)FrequencyTemp))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian test sampling rate */
		/*       should be between 100 and 65535 */
		if (!WriteBufferedSignedLongLittleEndian(Output,
			WaveTableObjectGetTestSamplingRate(WaveTableObj)))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian number of tables */
		NumberOfTables = WaveTableObjectGetNumTables(WaveTableObj);
		if (!WriteBufferedSignedLongLittleEndian(Output,NumberOfTables))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian number of frames per table */
		/*       must be an integral power or 2 between 2 and 65536 */
		NumberOfFrames = WaveTableObjectEntriesPerTable(WaveTableObj);
		if (!WriteBufferedSignedLongLittleEndian(Output,NumberOfFrames))
			{
				return eFileLoadDiskError;
			}

		/*   1-byte number of bits specifier */
		/*       must be 8 or 16 */
		NumberOfBits = WaveTableObjectGetNumBits(WaveTableObj);
		switch (NumberOfBits)
			{
				default:
					EXECUTE(PRERR(ForceAbort,"WaveTableObjectWriteDataOut:  bad number of bits"));
					break;
				case eSample8bit:
					if (!WriteBufferedUnsignedChar(Output,8))
						{
							return eFileLoadDiskError;
						}
					break;
				case eSample16bit:
					if (!WriteBufferedUnsignedChar(Output,16))
						{
							return eFileLoadDiskError;
						}
					break;
			}

		/*   n-byte sample data for the wave table */
		/*       data is stored as follows:  each table is stored consecutively starting */
		/*       with the table numbered 0.  in each table, each sample frame is stored */
		/*       consecutively as a signed 2s complement value.  8-bit sample frames */
		/*       use 1 byte each.  16-bit sample frames use 2 bytes and are stored little */
		/*       endian. */
		switch (NumberOfBits)
			{
				default:
					EXECUTE(PRERR(ForceAbort,"WaveTableObjectWriteDataOut:  bad number of bits"));
					break;
				case eSample8bit:
					for (Scan = 0; Scan < NumberOfTables; Scan += 1)
						{
							long							Index;
							signed char*			Slice;

							Slice = (signed char*)WaveTableObjectGetRawSlice(WaveTableObj,Scan);
							for (Index = 0; Index < NumberOfFrames; Index += 1)
								{
									PRNGCHK(Slice,&(Slice[Index]),sizeof(Slice[Index]));
									if (!WriteBufferedSignedChar(Output,Slice[Index]))
										{
											return eFileLoadDiskError;
										}
								}
						}
					break;
				case eSample16bit:
					for (Scan = 0; Scan < NumberOfTables; Scan += 1)
						{
							long							Index;
							signed short*			Slice;

							Slice = (signed short*)WaveTableObjectGetRawSlice(WaveTableObj,Scan);
							for (Index = 0; Index < NumberOfFrames; Index += 1)
								{
									PRNGCHK(Slice,&(Slice[Index]),sizeof(Slice[Index]));
									if (!WriteBufferedSignedShortLittleEndian(Output,Slice[Index]))
										{
											return eFileLoadDiskError;
										}
								}
						}
					break;
			}

		return eFileLoadNoError;
	}


/* mark wave table object as saved */
void									WaveTableObjectMarkAsSaved(WaveTableObjectRec* WaveTableObj)
	{
		CheckPtrExistence(WaveTableObj);
		if (WaveTableObj->WaveTableWindow != NIL)
			{
				WaveTableWindowWritebackModifiedData(WaveTableObj->WaveTableWindow);
			}
		WaveTableObj->DataModified = False;
	}
