/* SampleObject.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 "SampleObject.h"
#include "Memory.h"
#include "DataMunging.h"
#include "SampleList.h"
#include "SampleWindow.h"
#include "SampleStorageActual.h"
#include "BufferedFileInput.h"
#include "BufferedFileOutput.h"


struct SampleObjectRec
	{
		MyBoolean								DataModified;

		char*										Name;
		SampleStorageActualRec*	SampleData;
		char*										SampleFormula;

		long										Origin;
		long										LoopStart1;
		long										LoopStart2;
		long										LoopStart3;
		long										LoopEnd1;
		long										LoopEnd2;
		long										LoopEnd3;
		long										SamplingRate;
		double									NaturalFrequency;

		SampleWindowRec*				SampleWindow;

		struct CodeCenterRec*		CodeCenter;
		struct MainWindowRec*		MainWindow;
		SampleListRec*					SampleList;

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


/* allocate and create a new empty sample object, with reasonable defaults */
SampleObjectRec*			NewSampleObject(struct CodeCenterRec* CodeCenter,
												struct MainWindowRec* MainWindow,
												SampleListRec* SampleList)
	{
		SampleObjectRec*		SampObj;

		SampObj = (SampleObjectRec*)AllocPtrCanFail(sizeof(SampleObjectRec),
			"SampleObjectRec");
		if (SampObj == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		SampObj->SampleData = NewSampleStorageActual(eSample16bit,eSampleMono,0);
		if (SampObj->SampleData == NIL)
			{
			 FailurePoint2:
				ReleasePtr((char*)SampObj);
				goto FailurePoint1;
			}
		SampObj->Name = StringToBlockCopy("untitled");
		if (SampObj->Name == NIL)
			{
			 FailurePoint3:
				DisposeSampleStorageActual(SampObj->SampleData);
				goto FailurePoint2;
			}
		SetTag(SampObj->Name,"SampleName");
		SampObj->SampleFormula = StringToBlockCopy(
			"# This expression applies to the ENTIRE sample.\x0a"
			"# loopstart, loopend, origin, samplingrate, selectstart, selectend : integer\x0a"
			"# naturalfrequency : double; [leftdata, rightdata | data] : fixedarray\x0a");
		if (SampObj->SampleFormula == NIL)
			{
			 FailurePoint4:
				ReleasePtr(SampObj->Name);
				goto FailurePoint3;
			}
		/* initialize to reasonable values */
		SampObj->DataModified = False;
		SampObj->Origin = 0;
		SampObj->LoopStart1 = 0;
		SampObj->LoopStart2 = 0;
		SampObj->LoopStart3 = 0;
		SampObj->LoopEnd1 = 0;
		SampObj->LoopEnd2 = 0;
		SampObj->LoopEnd3 = 0;
		SampObj->SamplingRate = 44100;
		SampObj->NaturalFrequency = 261.625565300598635;
		SampObj->CodeCenter = CodeCenter;
		SampObj->MainWindow = MainWindow;
		SampObj->SampleList = SampleList;
		SampObj->SampleWindow = NIL;
		SampObj->SavedWindowXLoc = 0;
		SampObj->SavedWindowYLoc = 0;
		SampObj->SavedWindowWidth = 0;
		SampObj->SavedWindowHeight = 0;
		return SampObj;
	}


/* allocate and create a sample object with the specified attributes */
/* the RawData field must not be NIL.  It will be copied for the new sample */
SampleObjectRec*			NewSampleObjectInitialized(struct CodeCenterRec* CodeCenter,
												struct MainWindowRec* MainWindow,
												struct SampleListRec* SampleList, char* DataToCopy,
												NumBitsType NumBits, NumChannelsType NumChannels, long Origin,
												long LoopStart1, long LoopStart2, long LoopStart3,
												long LoopEnd1, long LoopEnd2, long LoopEnd3, long SamplingRate,
												double NaturalFrequency)
	{
		SampleObjectRec*		SampObj;
		long								NumSampleFrames;
		long								Scan;

		SampObj = (SampleObjectRec*)AllocPtrCanFail(sizeof(SampleObjectRec),
			"SampleObjectRec");
		if (SampObj == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		NumSampleFrames = PtrSize(DataToCopy);
		switch (NumBits)
			{
				default:
					EXECUTE(PRERR(ForceAbort,"NewSampleObjectInitialized:  bad num bits"));
					break;
				case eSample8bit:
					switch (NumChannels)
						{
							default:
								EXECUTE(PRERR(ForceAbort,"NewSampleObjectInitialized:  bad num channels"));
								break;
							case eSampleMono:
								NumSampleFrames = NumSampleFrames / 1;
								break;
							case eSampleStereo:
								NumSampleFrames = NumSampleFrames / 2;
								break;
						}
					break;
				case eSample16bit:
					switch (NumChannels)
						{
							default:
								EXECUTE(PRERR(ForceAbort,"NewSampleObjectInitialized:  bad num channels"));
								break;
							case eSampleMono:
								NumSampleFrames = NumSampleFrames / ((sizeof(short) / sizeof(char)));
								break;
							case eSampleStereo:
								NumSampleFrames = NumSampleFrames / (2 * (sizeof(short) / sizeof(char)));
								break;
						}
					break;
			}
		SampObj->SampleData = NewSampleStorageActual(NumBits,NumChannels,NumSampleFrames);
		if (SampObj->SampleData == NIL)
			{
			 FailurePoint2:
				ReleasePtr((char*)SampObj);
				goto FailurePoint1;
			}
		for (Scan = 0; Scan < NumSampleFrames; Scan += 1)
			{
				switch (NumBits)
					{
						default:
							EXECUTE(PRERR(ForceAbort,"NewSampleObjectInitialized:  bad num bits"));
							break;
						case eSample8bit:
							switch (NumChannels)
								{
									default:
										EXECUTE(PRERR(ForceAbort,"NewSampleObjectInitialized:  bad num channels"));
										break;
									case eSampleMono:
										PRNGCHK(DataToCopy,&(((char*)DataToCopy)[Scan]),sizeof(char));
										SetSampleStorageActualValue(SampObj->SampleData,Scan,eMonoChannel,
											double2largefixed(((double)(((char*)DataToCopy)[Scan]))
											/ MAX8BIT));
										break;
									case eSampleStereo:
										PRNGCHK(DataToCopy,&(((char*)DataToCopy)[2 * Scan]),
											sizeof(char));
										SetSampleStorageActualValue(SampObj->SampleData,Scan,eLeftChannel,
											double2largefixed(((double)(((char*)DataToCopy)[2 * Scan]))
											/ MAX8BIT));
										PRNGCHK(DataToCopy,&(((char*)DataToCopy)[(2 * Scan) + 1]),
											sizeof(char));
										SetSampleStorageActualValue(SampObj->SampleData,Scan,eRightChannel,
											double2largefixed(((double)(((char*)DataToCopy)[(2 * Scan) + 1]))
											/ MAX8BIT));
										break;
								}
							break;
						case eSample16bit:
							switch (NumChannels)
								{
									default:
										EXECUTE(PRERR(ForceAbort,"NewSampleObjectInitialized:  bad num channels"));
										break;
									case eSampleMono:
										PRNGCHK(DataToCopy,&(((short*)DataToCopy)[Scan]),sizeof(short));
										SetSampleStorageActualValue(SampObj->SampleData,Scan,eMonoChannel,
											double2largefixed(((double)(((short*)DataToCopy)[Scan]))
											/ MAX16BIT));
										break;
									case eSampleStereo:
										PRNGCHK(DataToCopy,&(((short*)DataToCopy)[2 * Scan]),
											sizeof(short));
										SetSampleStorageActualValue(SampObj->SampleData,Scan,eLeftChannel,
											double2largefixed(((double)(((short*)DataToCopy)[2 * Scan]))
											/ MAX16BIT));
										PRNGCHK(DataToCopy,&(((short*)DataToCopy)[(2 * Scan) + 1]),
											sizeof(short));
										SetSampleStorageActualValue(SampObj->SampleData,Scan,eRightChannel,
											double2largefixed(((double)(((short*)DataToCopy)[(2 * Scan) + 1]))
											/ MAX16BIT));
										break;
								}
							break;
					}
			}
		SampObj->Name = StringToBlockCopy("Algorithmic Sample Copy");
		if (SampObj->Name == NIL)
			{
			 FailurePoint3:
				DisposeSampleStorageActual(SampObj->SampleData);
				goto FailurePoint2;
			}
		SetTag(SampObj->Name,"SampleName");
		SampObj->SampleFormula = StringToBlockCopy(
			"# This expression applies to the ENTIRE sample.\x0a"
			"# loopstart, loopend, origin, samplingrate, selectstart, selectend : integer\x0a"
			"# naturalfrequency : double; [leftdata, rightdata | data] : fixedarray\x0a");
		if (SampObj->SampleFormula == NIL)
			{
			 FailurePoint4:
				ReleasePtr(SampObj->Name);
				goto FailurePoint3;
			}
		/* initialize to reasonable values */
		SampObj->DataModified = True;
		SampObj->CodeCenter = CodeCenter;
		SampObj->MainWindow = MainWindow;
		SampObj->SampleList = SampleList;
		SampObj->SampleWindow = NIL;
		SampleObjectPutOrigin(SampObj,Origin);
		SampleObjectPutLoopStart1(SampObj,LoopStart1);
		SampleObjectPutLoopStart2(SampObj,LoopStart2);
		SampleObjectPutLoopStart3(SampObj,LoopStart3);
		SampleObjectPutLoopEnd1(SampObj,LoopEnd1);
		SampleObjectPutLoopEnd2(SampObj,LoopEnd2);
		SampleObjectPutLoopEnd3(SampObj,LoopEnd3);
		SampleObjectPutSamplingRate(SampObj,SamplingRate);
		SampleObjectPutNaturalFrequency(SampObj,NaturalFrequency);
		SampObj->SavedWindowXLoc = 0;
		SampObj->SavedWindowYLoc = 0;
		SampObj->SavedWindowWidth = 0;
		SampObj->SavedWindowHeight = 0;
		return SampObj;
	}


/* dispose of a sample object */
void									DisposeSampleObject(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow != NIL)
			{
				DisposeSampleWindow(SampObj->SampleWindow);
				ERROR(SampObj->SampleWindow != NIL,PRERR(AllowResume,
					"DisposeSampleObject:  window thing not NIL after DisposeSampleWindow"));
			}
		DisposeSampleStorageActual(SampObj->SampleData);
		ReleasePtr(SampObj->Name);
		ReleasePtr(SampObj->SampleFormula);
		ReleasePtr((char*)SampObj);
	}


/* find out if sample object has been modified */
MyBoolean							HasSampleObjectBeenModified(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow != NIL)
			{
				return SampObj->DataModified
					|| HasSampleWindowBeenModified(SampObj->SampleWindow);
			}
		 else
			{
				return SampObj->DataModified;
			}
	}


/* get a copy of the sample's name. */
char*									SampleObjectGetNameCopy(SampleObjectRec* SampObj)
	{
		char*								NameTemp;

		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow != NIL)
			{
				NameTemp = SampleWindowGetNameCopy(SampObj->SampleWindow);
			}
		 else
			{
				NameTemp = CopyPtr(SampObj->Name);
			}
		if (NameTemp != NIL)
			{
				SetTag(NameTemp,"SampleNameCopy");
			}
		return NameTemp;
	}


/* set the sample's name.  the object becomes the owner of the Name block so */
/* the caller should not release it. */
void									SampleObjectNewName(SampleObjectRec* SampObj, char* Name)
	{
		CheckPtrExistence(SampObj);
		CheckPtrExistence(Name);
		ReleasePtr(SampObj->Name);
		SetTag(Name,"SampleName");
		SampObj->Name = Name;
		SampObj->DataModified = True;
		if (SampObj->SampleWindow != NIL)
			{
				SampleWindowObjectNameChange(SampObj->SampleWindow,Name);
			}
		SampleListSampleNameChanged(SampObj->SampleList,SampObj);
	}


/* get a copy of the formula applied to the sample */
char*									SampleObjectGetFormulaCopy(SampleObjectRec* SampObj)
	{
		char*								TextCopy;

		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow != NIL)
			{
				TextCopy = SampleWindowGetFormulaCopy(SampObj->SampleWindow);
			}
		 else
			{
				TextCopy = CopyPtr(SampObj->SampleFormula);
			}
		if (TextCopy != NIL)
			{
				SetTag(TextCopy,"SampFormulaCopy");
			}
		return TextCopy;
	}


/* install a new formula into the sample.  the object becomes the owner of the */
/* formula, so the caller should not release it */
void									SampleObjectNewFormula(SampleObjectRec* SampObj, char* Formula)
	{
		CheckPtrExistence(SampObj);
		CheckPtrExistence(Formula);
		ReleasePtr(SampObj->SampleFormula);
		SetTag(Formula,"SampleFormula");
		SampObj->SampleFormula = Formula;
		SampObj->DataModified = True;
	}


/* get the number of bits in a channel for the sample */
NumBitsType						SampleObjectGetNumBits(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow != NIL)
			{
				return SampleWindowGetNumBits(SampObj->SampleWindow);
			}
		 else
			{
				return GetSampleStorageActualNumBits(SampObj->SampleData);
			}
	}


/* get the number of channels in the sample */
NumChannelsType				SampleObjectGetNumChannels(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow != NIL)
			{
				return SampleWindowGetNumChannels(SampObj->SampleWindow);
			}
		 else
			{
				return GetSampleStorageActualNumChannels(SampObj->SampleData);
			}
	}


long									SampleObjectGetOrigin(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow != NIL)
			{
				return SampleWindowGetOrigin(SampObj->SampleWindow);
			}
		 else
			{
				return SampObj->Origin;
			}
	}


long									SampleObjectGetLoopStart1(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		if ((SampObj->SampleWindow != NIL)
			&& (eSampleLoop1 == SampleWindowEditingWhichLoop(SampObj->SampleWindow)))
			{
				return SampleWindowGetLoopStart(SampObj->SampleWindow);
			}
		 else
			{
				return SampObj->LoopStart1;
			}
	}


long									SampleObjectGetLoopStart2(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		if ((SampObj->SampleWindow != NIL)
			&& (eSampleLoop2 == SampleWindowEditingWhichLoop(SampObj->SampleWindow)))
			{
				return SampleWindowGetLoopStart(SampObj->SampleWindow);
			}
		 else
			{
				return SampObj->LoopStart2;
			}
	}


long									SampleObjectGetLoopStart3(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		if ((SampObj->SampleWindow != NIL)
			&& (eSampleLoop3 == SampleWindowEditingWhichLoop(SampObj->SampleWindow)))
			{
				return SampleWindowGetLoopStart(SampObj->SampleWindow);
			}
		 else
			{
				return SampObj->LoopStart3;
			}
	}


long									SampleObjectGetLoopEnd1(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		if ((SampObj->SampleWindow != NIL)
			&& (eSampleLoop1 == SampleWindowEditingWhichLoop(SampObj->SampleWindow)))
			{
				return SampleWindowGetLoopEnd(SampObj->SampleWindow);
			}
		 else
			{
				return SampObj->LoopEnd1;
			}
	}


long									SampleObjectGetLoopEnd2(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		if ((SampObj->SampleWindow != NIL)
			&& (eSampleLoop2 == SampleWindowEditingWhichLoop(SampObj->SampleWindow)))
			{
				return SampleWindowGetLoopEnd(SampObj->SampleWindow);
			}
		 else
			{
				return SampObj->LoopEnd2;
			}
	}


long									SampleObjectGetLoopEnd3(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		if ((SampObj->SampleWindow != NIL)
			&& (eSampleLoop3 == SampleWindowEditingWhichLoop(SampObj->SampleWindow)))
			{
				return SampleWindowGetLoopEnd(SampObj->SampleWindow);
			}
		 else
			{
				return SampObj->LoopEnd3;
			}
	}


long									SampleObjectGetSamplingRate(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow != NIL)
			{
				return SampleWindowGetSamplingRate(SampObj->SampleWindow);
			}
		 else
			{
				return SampObj->SamplingRate;
			}
	}


double								SampleObjectGetNaturalFrequency(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow != NIL)
			{
				return SampleWindowGetNaturalFrequency(SampObj->SampleWindow);
			}
		 else
			{
				return SampObj->NaturalFrequency;
			}
	}


/* get the number of sample frames */
long									SampleObjectGetNumSampleFrames(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow != NIL)
			{
				return SampleWindowGetNumFrames(SampObj->SampleWindow);
			}
		 else
			{
				return GetSampleStorageActualNumFrames(SampObj->SampleData);
			}
	}


/* get a pointer to the raw data for the sample.  this pointer is the */
/* actual data, not a copy, so don't dispose of it.  if any operations are performed */
/* on the sample, this pointer may become invalid.  format of raw data: */
/*  - mono, 8-bit:  array of signed bytes */
/*  - stereo, 8-bit:  array of signed bytes, grouped in pairs.  the one lower in */
/*    memory is the left channel */
/*  - mono, 16-bit:  array of signed short integers (either 2 or 4 bytes) */
/*  - stereo, 16-bit:  array of signed short integers, grouped in pairs.  the one */
/*    lower in memory is the left channel */
char*									SampleObjectGetRawData(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow != NIL)
			{
				if (!SampleWindowForceUpdateSampleObjectData(SampObj->SampleWindow))
					{
						return NIL;
					}
			}
		return GetSampleStorageActualRawData(SampObj->SampleData);
	}


/* put new data into the sample.  the num channels, bits, and data has to be put */
/* in all at the same time so that no reformatting routines need to be supplied. */
/* the object becomes owner of the data block, so it must not be released by the */
/* caller.  this call should also be made BEFORE any of the calls to set the */
/* attributes, since the attribute's values will be constrained based on this data */
void									SampleObjectPutNewSample(SampleObjectRec* SampObj,
												SampleStorageActualRec* NewStorage)
	{
		long								NumFrames;

		CheckPtrExistence(SampObj);
		CheckPtrExistence(NewStorage);
		DisposeSampleStorageActual(SampObj->SampleData);
		SampObj->SampleData = NewStorage;
		NumFrames = GetSampleStorageActualNumFrames(NewStorage);
		if (SampObj->LoopStart1 > NumFrames)
			{
				SampObj->LoopStart1 = NumFrames;
			}
		if (SampObj->LoopStart2 > NumFrames)
			{
				SampObj->LoopStart2 = NumFrames;
			}
		if (SampObj->LoopStart3 > NumFrames)
			{
				SampObj->LoopStart3 = NumFrames;
			}
		if (SampObj->LoopEnd1 > NumFrames)
			{
				SampObj->LoopEnd1 = NumFrames;
			}
		if (SampObj->LoopEnd2 > NumFrames)
			{
				SampObj->LoopEnd2 = NumFrames;
			}
		if (SampObj->LoopEnd3 > NumFrames)
			{
				SampObj->LoopEnd3 = NumFrames;
			}
		SampObj->DataModified = True;
	}


void									SampleObjectPutOrigin(SampleObjectRec* SampObj, long Origin)
	{
		CheckPtrExistence(SampObj);
		SampObj->Origin = Origin;
		SampObj->DataModified = True;
	}


void									SampleObjectPutLoopStart1(SampleObjectRec* SampObj, long LoopStart)
	{
		long								NumFrames;

		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow != NIL)
			{
				NumFrames = SampleWindowGetNumFrames(SampObj->SampleWindow);
			}
		 else
			{
				NumFrames = GetSampleStorageActualNumFrames(SampObj->SampleData);
			}
		if (LoopStart < 0)
			{
				LoopStart = 0;
			}
		if (LoopStart > NumFrames)
			{
				LoopStart = NumFrames;
			}
		SampObj->LoopStart1 = LoopStart;
		if (LoopStart > SampObj->LoopEnd1)
			{
				SampObj->LoopEnd1 = LoopStart;
			}
		SampObj->DataModified = True;
	}


void									SampleObjectPutLoopStart2(SampleObjectRec* SampObj, long LoopStart)
	{
		long								NumFrames;

		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow != NIL)
			{
				NumFrames = SampleWindowGetNumFrames(SampObj->SampleWindow);
			}
		 else
			{
				NumFrames = GetSampleStorageActualNumFrames(SampObj->SampleData);
			}
		if (LoopStart < 0)
			{
				LoopStart = 0;
			}
		if (LoopStart > NumFrames)
			{
				LoopStart = NumFrames;
			}
		SampObj->LoopStart2 = LoopStart;
		if (LoopStart > SampObj->LoopEnd2)
			{
				SampObj->LoopEnd2 = LoopStart;
			}
		SampObj->DataModified = True;
	}


void									SampleObjectPutLoopStart3(SampleObjectRec* SampObj, long LoopStart)
	{
		long								NumFrames;

		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow != NIL)
			{
				NumFrames = SampleWindowGetNumFrames(SampObj->SampleWindow);
			}
		 else
			{
				NumFrames = GetSampleStorageActualNumFrames(SampObj->SampleData);
			}
		if (LoopStart < 0)
			{
				LoopStart = 0;
			}
		if (LoopStart > NumFrames)
			{
				LoopStart = NumFrames;
			}
		SampObj->LoopStart3 = LoopStart;
		if (LoopStart > SampObj->LoopEnd3)
			{
				SampObj->LoopEnd3 = LoopStart;
			}
		SampObj->DataModified = True;
	}


void									SampleObjectPutLoopEnd1(SampleObjectRec* SampObj, long LoopEnd)
	{
		long								NumFrames;

		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow != NIL)
			{
				NumFrames = SampleWindowGetNumFrames(SampObj->SampleWindow);
			}
		 else
			{
				NumFrames = GetSampleStorageActualNumFrames(SampObj->SampleData);
			}
		if (LoopEnd < 0)
			{
				LoopEnd = 0;
			}
		if (LoopEnd > NumFrames)
			{
				LoopEnd = NumFrames;
			}
		if (LoopEnd < SampObj->LoopStart1)
			{
				LoopEnd = SampObj->LoopStart1;
			}
		SampObj->LoopEnd1 = LoopEnd;
		SampObj->DataModified = True;
	}


void									SampleObjectPutLoopEnd2(SampleObjectRec* SampObj, long LoopEnd)
	{
		long								NumFrames;

		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow != NIL)
			{
				NumFrames = SampleWindowGetNumFrames(SampObj->SampleWindow);
			}
		 else
			{
				NumFrames = GetSampleStorageActualNumFrames(SampObj->SampleData);
			}
		if (LoopEnd < 0)
			{
				LoopEnd = 0;
			}
		if (LoopEnd > NumFrames)
			{
				LoopEnd = NumFrames;
			}
		if (LoopEnd < SampObj->LoopStart2)
			{
				LoopEnd = SampObj->LoopStart2;
			}
		SampObj->LoopEnd2 = LoopEnd;
		SampObj->DataModified = True;
	}


void									SampleObjectPutLoopEnd3(SampleObjectRec* SampObj, long LoopEnd)
	{
		long								NumFrames;

		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow != NIL)
			{
				NumFrames = SampleWindowGetNumFrames(SampObj->SampleWindow);
			}
		 else
			{
				NumFrames = GetSampleStorageActualNumFrames(SampObj->SampleData);
			}
		if (LoopEnd < 0)
			{
				LoopEnd = 0;
			}
		if (LoopEnd > NumFrames)
			{
				LoopEnd = NumFrames;
			}
		if (LoopEnd < SampObj->LoopStart3)
			{
				LoopEnd = SampObj->LoopStart3;
			}
		SampObj->LoopEnd3 = LoopEnd;
		SampObj->DataModified = True;
	}


void									SampleObjectPutSamplingRate(SampleObjectRec* SampObj,
												long SamplingRate)
	{
		CheckPtrExistence(SampObj);
		if (SamplingRate < MINSAMPLINGRATE)
			{
				SamplingRate = MINSAMPLINGRATE;
			}
		if (SamplingRate > MAXSAMPLINGRATE)
			{
				SamplingRate = MAXSAMPLINGRATE;
			}
		SampObj->SamplingRate = SamplingRate;
		SampObj->DataModified = True;
	}


void									SampleObjectPutNaturalFrequency(SampleObjectRec* SampObj,
												double NaturalFrequency)
	{
		CheckPtrExistence(SampObj);
		if (NaturalFrequency < MINNATURALFREQ)
			{
				NaturalFrequency = MINNATURALFREQ;
			}
		if (NaturalFrequency > MAXNATURALFREQ)
			{
				NaturalFrequency = MAXNATURALFREQ;
			}
		SampObj->NaturalFrequency = NaturalFrequency;
		SampObj->DataModified = True;
	}


/* call which makes object open its editor window */
MyBoolean							SampleObjectOpenWindow(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow == NIL)
			{
				SampObj->SampleWindow = NewSampleWindow(SampObj->MainWindow,
					SampObj,SampObj->CodeCenter,SampObj->SampleData,SampObj->SampleList,
					SampObj->SavedWindowXLoc,SampObj->SavedWindowYLoc,SampObj->SavedWindowWidth,
					SampObj->SavedWindowHeight);
			}
		 else
			{
				SampleWindowBringToTop(SampObj->SampleWindow);
			}
		return (SampObj->SampleWindow != NIL);
	}


/* this is called by the window when it is closing to notify the object. */
/* the object should not take any action. */
void									SampleObjectClosingWindowNotify(SampleObjectRec* SampObj,
												short NewXSize, short NewYSize, short NewWidth, short NewHeight)
	{
		CheckPtrExistence(SampObj);
		ERROR(SampObj->SampleWindow == NIL,PRERR(ForceAbort,
			"SampleObjectClosingWindowNotify:  window not open"));
		SampObj->SampleWindow = NIL;
		SampObj->SavedWindowXLoc = NewXSize;
		SampObj->SavedWindowYLoc = NewYSize;
		SampObj->SavedWindowWidth = NewWidth;
		SampObj->SavedWindowHeight = NewHeight;
	}


largefixedsigned*			SampleObjectGetLeftFixed(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		ERROR(eSampleStereo != SampleObjectGetNumChannels(SampObj),PRERR(ForceAbort,
			"SampleObjectGetLeftFixed called on mono sample"));
		if (SampObj->SampleWindow != NIL)
			{
				return SampleWindowGetFixedArrayLeft(SampObj->SampleWindow);
			}
		 else
			{
				return SampleStorageActualGetChannelFixed(SampObj->SampleData,eLeftChannel);
			}
	}


largefixedsigned*			SampleObjectGetRightFixed(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		ERROR(eSampleStereo != SampleObjectGetNumChannels(SampObj),PRERR(ForceAbort,
			"SampleObjectGetRightFixed called on mono sample"));
		if (SampObj->SampleWindow != NIL)
			{
				return SampleWindowGetFixedArrayRight(SampObj->SampleWindow);
			}
		 else
			{
				return SampleStorageActualGetChannelFixed(SampObj->SampleData,eRightChannel);
			}
	}


largefixedsigned*			SampleObjectGetMonoFixed(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		ERROR(eSampleMono != SampleObjectGetNumChannels(SampObj),PRERR(ForceAbort,
			"SampleObjectGetMonoFixed called on mono sample"));
		if (SampObj->SampleWindow != NIL)
			{
				return SampleWindowGetFixedArrayMono(SampObj->SampleWindow);
			}
		 else
			{
				return SampleStorageActualGetChannelFixed(SampObj->SampleData,eMonoChannel);
			}
	}


/* the document's name has changed, so the window needs to be updated */
void									SampleObjectGlobalNameChange(SampleObjectRec* SampObj,
												char* NewFilename)
	{
		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow != NIL)
			{
				SampleWindowGlobalNameChange(SampObj->SampleWindow,NewFilename);
			}
	}


/* Sample Object Subblock Structure: */
/*   1-byte sample version number */
/*       should be 1 */
/*   2-byte little endian window X position (signed, origin at top-left corner) */
/*   2-byte little endian window Y position */
/*   2-byte little endian window width */
/*   2-byte little endian window height */
/*   4-byte little endian sample name length descriptor (positive 2's complement) */
/*   n-byte sample name text (line feed = 0x0a) */
/*   4-byte little endian sample formula length descriptor (positive 2's complement) */
/*   n-byte sample formula text (line feed = 0x0a) */
/*   4-byte little endian sample frame index of sample's origin */
/*   4-byte little endian sample frame index of loop 1 start */
/*       must be a valid index, i.e. >= 0 and < num sample frames */
/*   4-byte little endian sample frame index of loop 1 end */
/*       must be a valid index, i.e. >= 0 and < num sample frames */
/*       also, loop end must not be less than loop start */
/*   4-byte little endian sample frame index of loop 2 start */
/*       must be a valid index, i.e. >= 0 and < num sample frames */
/*   4-byte little endian sample frame index of loop 2 end */
/*       must be a valid index, i.e. >= 0 and < num sample frames */
/*       also, loop end must not be less than loop start */
/*   4-byte little endian sample frame index of loop 3 start */
/*       must be a valid index, i.e. >= 0 and < num sample frames */
/*   4-byte little endian sample frame index of loop 3 end */
/*       must be a valid index, i.e. >= 0 and < num sample frames */
/*       also, loop end must not be less than loop start */
/*   4-byte little endian sampling rate value */
/*       should be between 100 and 65535 */
/*   4-byte little endian natural frequency fractional portion */
/*       unsigned; divide by 2^32 to get the actual fraction */
/*   4-byte little endian natural frequency integer portion */
/*       total natural frequency should be between 0.01 and 1e6 */
/*   4-byte total number of sample frames */
/*   1-byte mono/stereo flag */
/*       1 = mono */
/*       2 = stereo */
/*   1-byte number of bits per sample point */
/*       should be 8 or 16 */
/*   n-bytes of data for sample frames */
/*       stereo samples have the left channel sample point preceding the right */
/*       channel sample point. */
/*       sample points that require more than 1 byte are stored little endian */
/*       all sample data is stored in signed 2's complement form */


/* read a sample object from the file */
FileLoadingErrors			SampleObjectNewFromFile(SampleObjectRec** ObjectOut,
												struct BufferedInputRec* Input, struct CodeCenterRec* CodeCenter,
												struct MainWindowRec* MainWindow,
												struct SampleListRec* SampleList)
	{
		unsigned char				UnsignedChar;
		signed short				SignedShort;
		signed long					SignedLong;
		unsigned long				UnsignedLong;
		SampleObjectRec*		SampleObject;
		FileLoadingErrors		Error;
		NumBitsType					NumBits;
		NumChannelsType			NumChannels;
		long								NumSampleFrames;
		long								Scan;

		CheckPtrExistence(Input);
		CheckPtrExistence(CodeCenter);
		CheckPtrExistence(MainWindow);
		CheckPtrExistence(SampleList);

		/* create a sample object */
		SampleObject = (SampleObjectRec*)AllocPtrCanFail(sizeof(SampleObjectRec),
			"SampleObjectRec");
		if (SampleObject == NIL)
			{
				Error = eFileLoadOutOfMemory;
			 FailurePoint1:
				return Error;
			}

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

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

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

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

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

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

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

		/*   4-byte little endian sample formula length descriptor (positive 2's complement) */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint12:
				ReleasePtr(SampleObject->Name);
				goto FailurePoint11;
			}
		if (SignedLong < 0)
			{
				Error = eFileLoadBadFormat;
			 FailurePoint13:
				goto FailurePoint12;
			}

		/*   n-byte sample formula text (line feed = 0x0a) */
		SampleObject->SampleFormula = AllocPtrCanFail(SignedLong,"SampleObjectRec: formula");
		if (SampleObject->SampleFormula == NIL)
			{
				Error = eFileLoadOutOfMemory;
			 FailurePoint14:
				goto FailurePoint13;
			}
		if (!ReadBufferedInput(Input,SignedLong,SampleObject->SampleFormula))
			{
				Error = eFileLoadDiskError;
			 FailurePoint15:
				ReleasePtr(SampleObject->SampleFormula);
				goto FailurePoint14;
			}

		/*   4-byte little endian sample frame index of sample's origin */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint16:
				goto FailurePoint15;
			}
		SampleObject->Origin = SignedLong;

		/*   4-byte little endian sample frame index of loop 1 start */
		/*       must be a valid index, i.e. >= 0 and < num sample frames */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint17:
				goto FailurePoint16;
			}
		SampleObject->LoopStart1 = SignedLong;

		/*   4-byte little endian sample frame index of loop 1 end */
		/*       must be a valid index, i.e. >= 0 and < num sample frames */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint18:
				goto FailurePoint17;
			}
		SampleObject->LoopEnd1 = SignedLong;

		/*   4-byte little endian sample frame index of loop 2 start */
		/*       must be a valid index, i.e. >= 0 and < num sample frames */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint19:
				goto FailurePoint18;
			}
		SampleObject->LoopStart2 = SignedLong;

		/*   4-byte little endian sample frame index of loop 2 end */
		/*       must be a valid index, i.e. >= 0 and < num sample frames */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint20:
				goto FailurePoint19;
			}
		SampleObject->LoopEnd2 = SignedLong;

		/*   4-byte little endian sample frame index of loop 3 start */
		/*       must be a valid index, i.e. >= 0 and < num sample frames */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint21:
				goto FailurePoint20;
			}
		SampleObject->LoopStart3 = SignedLong;

		/*   4-byte little endian sample frame index of loop 3 end */
		/*       must be a valid index, i.e. >= 0 and < num sample frames */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint22:
				goto FailurePoint21;
			}
		SampleObject->LoopEnd3 = SignedLong;

		/*   4-byte little endian sampling rate value */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint23:
				goto FailurePoint22;
			}
		if (SignedLong < MINSAMPLINGRATE)
			{
				SignedLong = MINSAMPLINGRATE;
			}
		if (SignedLong > MAXSAMPLINGRATE)
			{
				SignedLong = MAXSAMPLINGRATE;
			}
		SampleObject->SamplingRate = SignedLong;

		/*   4-byte little endian natural frequency fractional portion */
		/*       unsigned; divide by 2^32 to get the actual fraction */
		if (!ReadBufferedUnsignedLongLittleEndian(Input,&UnsignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint24:
				goto FailurePoint23;
			}
		SampleObject->NaturalFrequency = UnsignedLong / 4294967296.0L;

		/*   4-byte little endian natural frequency integer portion */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint25:
				goto FailurePoint24;
			}
		SampleObject->NaturalFrequency += SignedLong;
		if (SampleObject->NaturalFrequency < MINNATURALFREQ)
			{
				SampleObject->NaturalFrequency = MINNATURALFREQ;
			}
		if (SampleObject->NaturalFrequency > MAXNATURALFREQ)
			{
				SampleObject->NaturalFrequency = MAXNATURALFREQ;
			}

		/*   4-byte total number of sample frames */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint26:
				goto FailurePoint25;
			}
		if (SignedLong < 0)
			{
				Error = eFileLoadBadFormat;
			 FailurePoint27:
				goto FailurePoint26;
			}
		NumSampleFrames = SignedLong;
		/* now, check the loop start and end points to make sure they are within range */
		if ((SampleObject->LoopStart1 < 0) || (SampleObject->LoopStart1 > SignedLong)
			|| (SampleObject->LoopStart1 > SampleObject->LoopEnd1)
			|| (SampleObject->LoopStart2 < 0) || (SampleObject->LoopStart2 > SignedLong)
			|| (SampleObject->LoopStart2 > SampleObject->LoopEnd2)
			|| (SampleObject->LoopStart3 < 0) || (SampleObject->LoopStart3 > SignedLong)
			|| (SampleObject->LoopStart3 > SampleObject->LoopEnd3))
			{
				Error = eFileLoadBadFormat;
			 FailurePoint28:
				goto FailurePoint27;
			}

		/*   1-byte mono/stereo flag */
		/*       1 = mono */
		/*       2 = stereo */
		if (!ReadBufferedUnsignedChar(Input,&UnsignedChar))
			{
				Error = eFileLoadDiskError;
			 FailurePoint29:
				goto FailurePoint28;
			}
		if (UnsignedChar == 2)
			{
				NumChannels = eSampleStereo;
			}
		else if (UnsignedChar == 1)
			{
				NumChannels = eSampleMono;
			}
		else
			{
				Error = eFileLoadBadFormat;
			 FailurePoint30:
				goto FailurePoint29;
			}

		/*   1-byte number of bits per sample point */
		/*       should be 8 or 16 */
		if (!ReadBufferedUnsignedChar(Input,&UnsignedChar))
			{
				Error = eFileLoadDiskError;
			 FailurePoint31:
				goto FailurePoint30;
			}
		if (UnsignedChar == 8)
			{
				NumBits = eSample8bit;
			}
		else if (UnsignedChar == 16)
			{
				NumBits = eSample16bit;
			}
		else
			{
				Error = eFileLoadBadFormat;
			 FailurePoint32:
				goto FailurePoint31;
			}

		/*   n-bytes of data for sample frames */
		/*       stereo samples have the left channel sample point preceding the right */
		/*       channel sample point. */
		/*       sample points that require more than 1 byte are stored little endian */
		/*       all sample data is stored in signed 2's complement form */
		SampleObject->SampleData = NewSampleStorageActual(NumBits,NumChannels,
			NumSampleFrames);
		if (SampleObject->SampleData == NIL)
			{
				Error = eFileLoadOutOfMemory;
			 FailurePoint33:
				goto FailurePoint32;
			}
		switch (NumBits)
			{
				default:
					EXECUTE(PRERR(ForceAbort,"SampleObjectNewFromFile:  bad NumBits value"));
					break;
				case eSample8bit:
					switch (NumChannels)
						{
							default:
								EXECUTE(PRERR(ForceAbort,"SampleObjectNewFromFile:  bad NumChannels value"));
								break;
							case eSampleMono:
								for (Scan = 0; Scan < NumSampleFrames; Scan += 1)
									{
										signed char					SamplePoint;

										if (!ReadBufferedSignedChar(Input,&SamplePoint))
											{
											 ErrorWhileReadingSampleData:
												Error = eFileLoadDiskError;
												DisposeSampleStorageActual(SampleObject->SampleData);
												goto FailurePoint33;
											}
										SetSampleStorageActualValue(SampleObject->SampleData,Scan,
											eMonoChannel,double2largefixed((double)SamplePoint / MAX8BIT));
									}
								break;
							case eSampleStereo:
								for (Scan = 0; Scan < NumSampleFrames; Scan += 1)
									{
										signed char					SamplePoint;

										if (!ReadBufferedSignedChar(Input,&SamplePoint))
											{
												goto ErrorWhileReadingSampleData;
											}
										SetSampleStorageActualValue(SampleObject->SampleData,Scan,
											eLeftChannel,double2largefixed((double)SamplePoint / MAX8BIT));

										if (!ReadBufferedSignedChar(Input,&SamplePoint))
											{
												goto ErrorWhileReadingSampleData;
											}
										SetSampleStorageActualValue(SampleObject->SampleData,Scan,
											eRightChannel,double2largefixed((double)SamplePoint / MAX8BIT));
									}
								break;
						}
					break;
				case eSample16bit:
					switch (NumChannels)
						{
							default:
								EXECUTE(PRERR(ForceAbort,"SampleObjectNewFromFile:  bad NumChannels value"));
								break;
							case eSampleMono:
								for (Scan = 0; Scan < NumSampleFrames; Scan += 1)
									{
										signed short				SamplePoint;

										if (!ReadBufferedSignedShortLittleEndian(Input,&SamplePoint))
											{
												goto ErrorWhileReadingSampleData;
											}
										SetSampleStorageActualValue(SampleObject->SampleData,Scan,
											eMonoChannel,double2largefixed((double)SamplePoint / MAX16BIT));
									}
								break;
							case eSampleStereo:
								for (Scan = 0; Scan < NumSampleFrames; Scan += 1)
									{
										signed short				SamplePoint;

										if (!ReadBufferedSignedShortLittleEndian(Input,&SamplePoint))
											{
												goto ErrorWhileReadingSampleData;
											}
										SetSampleStorageActualValue(SampleObject->SampleData,Scan,
											eLeftChannel,double2largefixed((double)SamplePoint / MAX16BIT));

										if (!ReadBufferedSignedShortLittleEndian(Input,&SamplePoint))
											{
												goto ErrorWhileReadingSampleData;
											}
										SetSampleStorageActualValue(SampleObject->SampleData,Scan,
											eRightChannel,double2largefixed((double)SamplePoint / MAX16BIT));
									}
								break;
						}
					break;
			}

		/* fill in other fields */
		SampleObject->DataModified = False;
		SampleObject->SampleWindow = NIL;
		SampleObject->CodeCenter = CodeCenter;
		SampleObject->MainWindow = MainWindow;
		SampleObject->SampleList = SampleList;

		*ObjectOut = SampleObject;
		return eFileLoadNoError;
	}


/* write the data in a sample object out to the file */
FileLoadingErrors			SampleObjectWriteOutData(SampleObjectRec* SampObj,
												struct BufferedOutputRec* Output)
	{
		char*								StringTemp;
		double							NaturalFreqTemp;
		NumBitsType					NumBits;
		NumChannelsType			NumChannels;
		long								NumSampleFrames;
		long								Scan;
		char*								RawDataPointer;

		CheckPtrExistence(SampObj);
		CheckPtrExistence(Output);

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

		/*   2-byte little endian window X position (signed, origin at top-left corner) */
		/* the way we're doing this, if the window is open when we save, the most */
		/* recent coordinates will not be saved. */
		if (!WriteBufferedSignedShortLittleEndian(Output,SampObj->SavedWindowXLoc))
			{
				return eFileLoadDiskError;
			}

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

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

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

		/*   4-byte little endian sample name length descriptor (positive 2's complement) */
		StringTemp = SampleObjectGetNameCopy(SampObj);
		if (StringTemp == NIL)
			{
				return eFileLoadOutOfMemory;
			}
		if (!WriteBufferedSignedLongLittleEndian(Output,PtrSize(StringTemp)))
			{
				ReleasePtr(StringTemp);
				return eFileLoadDiskError;
			}

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

		/*   4-byte little endian sample formula length descriptor (positive 2's complement) */
		StringTemp = SampleObjectGetFormulaCopy(SampObj);
		if (StringTemp == NIL)
			{
				return eFileLoadOutOfMemory;
			}
		if (!WriteBufferedSignedLongLittleEndian(Output,PtrSize(StringTemp)))
			{
				ReleasePtr(StringTemp);
				return eFileLoadDiskError;
			}

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

		/*   4-byte little endian sample frame index of sample's origin */
		if (!WriteBufferedSignedLongLittleEndian(Output,SampleObjectGetOrigin(SampObj)))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian sample frame index of loop 1 start */
		/*       must be a valid index, i.e. >= 0 and < num sample frames */
		if (!WriteBufferedSignedLongLittleEndian(Output,SampleObjectGetLoopStart1(SampObj)))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian sample frame index of loop 1 end */
		/*       must be a valid index, i.e. >= 0 and < num sample frames */
		if (!WriteBufferedSignedLongLittleEndian(Output,SampleObjectGetLoopEnd1(SampObj)))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian sample frame index of loop 2 start */
		/*       must be a valid index, i.e. >= 0 and < num sample frames */
		if (!WriteBufferedSignedLongLittleEndian(Output,SampleObjectGetLoopStart2(SampObj)))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian sample frame index of loop 2 end */
		/*       must be a valid index, i.e. >= 0 and < num sample frames */
		if (!WriteBufferedSignedLongLittleEndian(Output,SampleObjectGetLoopEnd2(SampObj)))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian sample frame index of loop 3 start */
		/*       must be a valid index, i.e. >= 0 and < num sample frames */
		if (!WriteBufferedSignedLongLittleEndian(Output,SampleObjectGetLoopStart3(SampObj)))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian sample frame index of loop 3 end */
		/*       must be a valid index, i.e. >= 0 and < num sample frames */
		if (!WriteBufferedSignedLongLittleEndian(Output,SampleObjectGetLoopEnd3(SampObj)))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian sampling rate value */
		if (!WriteBufferedSignedLongLittleEndian(Output,SampleObjectGetSamplingRate(SampObj)))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian natural frequency fractional portion */
		/*       unsigned; divide by 2^32 to get the actual fraction */
		NaturalFreqTemp = SampleObjectGetNaturalFrequency(SampObj) + (1.0L / 8589934592.0L);
		if (!WriteBufferedUnsignedLongLittleEndian(Output,
			(unsigned long)((NaturalFreqTemp - (long)NaturalFreqTemp) * 4294967296.0L)))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian natural frequency integer portion */
		if (!WriteBufferedSignedLongLittleEndian(Output,(long)NaturalFreqTemp))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte total number of sample frames */
		NumSampleFrames = SampleObjectGetNumSampleFrames(SampObj);
		if (!WriteBufferedSignedLongLittleEndian(Output,NumSampleFrames))
			{
				return eFileLoadDiskError;
			}

		/*   1-byte mono/stereo flag */
		/*       1 = mono */
		/*       2 = stereo */
		NumChannels = SampleObjectGetNumChannels(SampObj);
		switch (NumChannels)
			{
				default:
					EXECUTE(PRERR(ForceAbort,"SampleObjectWriteOutData:  bad NumChannels"));
					break;
				case eSampleStereo:
					if (!WriteBufferedUnsignedChar(Output,2))
						{
							return eFileLoadDiskError;
						}
					break;
				case eSampleMono:
					if (!WriteBufferedUnsignedChar(Output,1))
						{
							return eFileLoadDiskError;
						}
					break;
			}

		/*   1-byte number of bits per sample point */
		/*       should be 8 or 16 */
		NumBits = SampleObjectGetNumBits(SampObj);
		switch (NumBits)
			{
				default:
					EXECUTE(PRERR(ForceAbort,"SampleObjectWriteOutData:  bad NumBits"));
					break;
				case eSample8bit:
					if (!WriteBufferedUnsignedChar(Output,8))
						{
							return eFileLoadDiskError;
						}
					break;
				case eSample16bit:
					if (!WriteBufferedUnsignedChar(Output,16))
						{
							return eFileLoadDiskError;
						}
					break;
			}

		/*   n-bytes of data for sample frames */
		/*       stereo samples have the left channel sample point preceding the right */
		/*       channel sample point. */
		/*       sample points that require more than 1 byte are stored little endian */
		/*       all sample data is stored in signed 2's complement form */
		RawDataPointer = SampleObjectGetRawData(SampObj);
		if (RawDataPointer == NIL)
			{
				return eFileLoadOutOfMemory;
			}
		switch (NumChannels)
			{
				default:
					EXECUTE(PRERR(ForceAbort,"SampleObjectWriteOutData:  bad NumChannels"));
					break;
				case eSampleMono:
					switch (NumBits)
						{
							default:
								EXECUTE(PRERR(ForceAbort,"SampleObjectWriteOutData:  bad NumBits"));
								break;
							case eSample8bit:
								for (Scan = 0; Scan < NumSampleFrames; Scan += 1)
									{
										PRNGCHK(RawDataPointer,&(((signed char*)RawDataPointer)[Scan]),
											sizeof(((signed char*)RawDataPointer)[Scan]));
										if (!WriteBufferedSignedChar(Output,
											((signed char*)RawDataPointer)[Scan]))
											{
												return eFileLoadDiskError;
											}
									}
								break;
							case eSample16bit:
								for (Scan = 0; Scan < NumSampleFrames; Scan += 1)
									{
										PRNGCHK(RawDataPointer,&(((signed short*)RawDataPointer)[Scan]),
											sizeof(((signed short*)RawDataPointer)[Scan]));
										if (!WriteBufferedSignedShortLittleEndian(Output,
											((signed short*)RawDataPointer)[Scan]))
											{
												return eFileLoadDiskError;
											}
									}
								break;
						}
					break;
				case eSampleStereo:
					switch (NumBits)
						{
							default:
								EXECUTE(PRERR(ForceAbort,"SampleObjectWriteOutData:  bad NumBits"));
								break;
							case eSample8bit:
								for (Scan = 0; Scan < NumSampleFrames; Scan += 1)
									{
										PRNGCHK(RawDataPointer,
											&(((signed char*)RawDataPointer)[2 * Scan + 0]),
											sizeof(((signed char*)RawDataPointer)[2 * Scan + 0]));
										if (!WriteBufferedSignedChar(Output,
											((signed char*)RawDataPointer)[2 * Scan + 0]))
											{
												return eFileLoadDiskError;
											}
										PRNGCHK(RawDataPointer,
											&(((signed char*)RawDataPointer)[2 * Scan + 1]),
											sizeof(((signed char*)RawDataPointer)[2 * Scan + 1]));
										if (!WriteBufferedSignedChar(Output,
											((signed char*)RawDataPointer)[2 * Scan + 1]))
											{
												return eFileLoadDiskError;
											}
									}
								break;
							case eSample16bit:
								for (Scan = 0; Scan < NumSampleFrames; Scan += 1)
									{
										PRNGCHK(RawDataPointer,
											&(((signed short*)RawDataPointer)[2 * Scan + 0]),
											sizeof(((signed short*)RawDataPointer)[2 * Scan + 0]));
										if (!WriteBufferedSignedShortLittleEndian(Output,
											((signed short*)RawDataPointer)[2 * Scan + 0]))
											{
												return eFileLoadDiskError;
											}
										PRNGCHK(RawDataPointer,
											&(((signed short*)RawDataPointer)[2 * Scan + 1]),
											sizeof(((signed short*)RawDataPointer)[2 * Scan + 1]));
										if (!WriteBufferedSignedShortLittleEndian(Output,
											((signed short*)RawDataPointer)[2 * Scan + 1]))
											{
												return eFileLoadDiskError;
											}
									}
								break;
						}
					break;
			}

		return eFileLoadNoError;
	}


/* mark sample object as not modified */
void									SampleObjectMarkAsSaved(SampleObjectRec* SampObj)
	{
		CheckPtrExistence(SampObj);
		if (SampObj->SampleWindow != NIL)
			{
				SampleWindowWritebackModifiedData(SampObj->SampleWindow);
			}
		SampObj->DataModified = False;
	}
