/* TrackObject.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 "TrackObject.h"
#include "TrackWindow.h"
#include "TrackList.h"
#include "Memory.h"
#include "DataMunging.h"
#include "Array.h"
#include "FrameObject.h"
#include "Screen.h"
#include "TrackView.h"
#include "NoteObject.h"
#include "Menus.h"
#include "BufferedFileInput.h"
#include "BufferedFileOutput.h"
#include "BinaryCodedDecimal.h"
#include "LoadSaveNoteVectors.h"


struct TrackObjectRec
	{
		MyBoolean								DataModified;

		char*										Name;

		/* defaults for per-note parameters */
		double									DefaultEarlyLateAdjust;
		double									DefaultReleasePoint1;
		unsigned long						DefaultReleasePoint1ModeFlag;
		double									DefaultReleasePoint2;
		unsigned long						DefaultReleasePoint2ModeFlag;
		double									DefaultOverallLoudness;
		double									DefaultStereoPositioning;
		double									DefaultSurroundPositioning;
		double									DefaultAccent1;
		double									DefaultAccent2;
		double									DefaultAccent3;
		double									DefaultAccent4;
		double									DefaultPitchDisplacementDepthAdjust;
		unsigned long						DefaultPitchDisplacementDepthAdjustModeFlag;
		double									DefaultPitchDisplacementRateAdjust;
		double									DefaultPitchDisplacementStartPoint;
		unsigned long						DefaultPitchDisplacementStartPointModeFlag;
		double									DefaultHurryUpFactor;
		double									DefaultDetune;
		unsigned long						DefaultDetuneModeFlag;
		double									DefaultDuration;
		unsigned long						DefaultDurationModeFlag;
		char*										PostProcessingFormula;
		MyBoolean								PostProcessingEnable;

		MyBoolean								IncludeThisTrackInFinalPlayback;

		char*										InstrumentName;
		ArrayRec*								FrameArray;
		ArrayRec*								DependentViews;
		ArrayRec*								BackgroundObjects;

		MenuItemType*						TrackMenuItem;
		TrackWindowRec*					TrackWindow;

		struct CodeCenterRec*		CodeCenter;
		struct MainWindowRec*		MainWindow;
		TrackListRec*						TrackList;

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


/* create a new empty track object */
TrackObjectRec*				NewTrackObject(struct CodeCenterRec* CodeCenter,
												struct MainWindowRec* MainWindow, struct TrackListRec* TrackList)
	{
		TrackObjectRec*			TrackObj;
		char*								NullTerminated;

		TrackObj = (TrackObjectRec*)AllocPtrCanFail(sizeof(TrackObjectRec),"TrackObjectRec");
		if (TrackObj == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		TrackObj->Name = StringToBlockCopy("untitled");
		if (TrackObj->Name == NIL)
			{
			 FailurePoint2:
				ReleasePtr((char*)TrackObj);
				goto FailurePoint1;
			}
		TrackObj->InstrumentName = AllocPtrCanFail(0,"TrackObjInstrumentName");
		if (TrackObj->InstrumentName == NIL)
			{
			 FailurePoint3:
				ReleasePtr(TrackObj->Name);
				goto FailurePoint2;
			}
		TrackObj->FrameArray = NewArray();
		if (TrackObj->FrameArray == NIL)
			{
			 FailurePoint4:
				ReleasePtr(TrackObj->InstrumentName);
				goto FailurePoint3;
			}
		TrackObj->DependentViews = NewArray();
		if (TrackObj->DependentViews == NIL)
			{
			 FailurePoint5:
				DisposeArray(TrackObj->FrameArray);
				goto FailurePoint4;
			}
		TrackObj->BackgroundObjects = NewArray();
		if (TrackObj->BackgroundObjects == NIL)
			{
			 FailurePoint6:
				DisposeArray(TrackObj->DependentViews);
				goto FailurePoint5;
			}
		NullTerminated = BlockToStringCopy(TrackObj->Name);
		if (NullTerminated == NIL)
			{
			 FailurePoint7:
				DisposeArray(TrackObj->BackgroundObjects);
				goto FailurePoint6;
			}
		TrackObj->TrackMenuItem = MakeNewMenuItem(
			TrackListGetTrackMenu(TrackList),NullTerminated,0);
		ReleasePtr(NullTerminated);
		if (TrackObj->TrackMenuItem == NIL)
			{
			 FailurePoint8:
				goto FailurePoint7;
			}
		TrackObj->PostProcessingFormula = StringToBlockCopy("#channel postprocessing\x0a");
		if (TrackObj->PostProcessingFormula == NIL)
			{
			 FailurePoint9:
				KillMenuItem(TrackObj->TrackMenuItem);
				goto FailurePoint8;
			}
		SetTag(TrackObj->PostProcessingFormula,"PostProcessingFormula");

		TrackObj->DataModified = False;
		TrackObj->TrackWindow = NIL;
		TrackObj->CodeCenter = CodeCenter;
		TrackObj->MainWindow = MainWindow;
		TrackObj->TrackList = TrackList;
		TrackObj->IncludeThisTrackInFinalPlayback = True;

		TrackObj->DefaultEarlyLateAdjust = 0;
		TrackObj->DefaultReleasePoint1 = 0;
		TrackObj->DefaultReleasePoint1ModeFlag = eRelease1FromStart;
		TrackObj->DefaultReleasePoint2 = 0;
		TrackObj->DefaultReleasePoint2ModeFlag = eRelease2FromStart;
		TrackObj->DefaultOverallLoudness = 1;
		TrackObj->DefaultStereoPositioning = 0;
		TrackObj->DefaultSurroundPositioning = 0;
		TrackObj->DefaultAccent1 = 0;
		TrackObj->DefaultAccent2 = 0;
		TrackObj->DefaultAccent3 = 0;
		TrackObj->DefaultAccent4 = 0;
		TrackObj->DefaultPitchDisplacementDepthAdjust = 1;
		TrackObj->DefaultPitchDisplacementRateAdjust = 1;
		TrackObj->DefaultPitchDisplacementStartPoint = 0;
		TrackObj->DefaultPitchDisplacementStartPointModeFlag = ePitchDisplacementStartFromStart;
		TrackObj->DefaultHurryUpFactor = 1;
		TrackObj->DefaultDetune = 0;
		TrackObj->DefaultDetuneModeFlag = eDetuningModeHalfSteps;
		TrackObj->DefaultDuration = 0;
		TrackObj->DefaultDurationModeFlag = eDurationAdjustAdditive;
		TrackObj->PostProcessingEnable = False;

		TrackObj->SavedWindowXLoc = 0;
		TrackObj->SavedWindowYLoc = 0;
		TrackObj->SavedWindowWidth = 0;
		TrackObj->SavedWindowHeight = 0;
		return TrackObj;
	}


/* dispose of track object and all the crud it contains */
void									DisposeTrackObject(TrackObjectRec* TrackObj)
	{
		long								Limit;
		long								Scan;

		CheckPtrExistence(TrackObj);
		if (TrackObj->TrackWindow != NIL)
			{
				DisposeTrackWindow(TrackObj->TrackWindow);
				ERROR(TrackObj->TrackWindow != NIL,PRERR(ForceAbort,
					"DisposeTrackObject:  disposed track window, but window ptr isn't NIL"));
			}
		/* notify dependent views that this object is disappearing */
		Limit = ArrayGetLength(TrackObj->DependentViews);
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				TrackViewRec*				ViewObject;

				ViewObject = (TrackViewRec*)ArrayGetElement(TrackObj->DependentViews,Scan);
				TrackViewObjectTrackDying(ViewObject,TrackObj);
			}
		DisposeArray(TrackObj->DependentViews);
		/* dispose list of background things. have the list dispose all ones */
		/* depending on us. */
		DisposeArray(TrackObj->BackgroundObjects);
		TrackListDelinkBackgroundInstances(TrackObj->TrackList,TrackObj);
		/* dispose notes */
		Limit = ArrayGetLength(TrackObj->FrameArray);
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				FrameObjectRec*			Record;

				Record = (FrameObjectRec*)ArrayGetElement(TrackObj->FrameArray,Scan);
				DisposeFrameAndContents(Record);
			}
		DisposeArray(TrackObj->FrameArray);
		ReleasePtr(TrackObj->InstrumentName);
		ReleasePtr(TrackObj->Name);
		KillMenuItem(TrackObj->TrackMenuItem);
		ReleasePtr(TrackObj->PostProcessingFormula);
		ReleasePtr((char*)TrackObj);
	}


/* find out if the object has been changed */
MyBoolean							HasTrackObjectBeenModified(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		/* unlike the other objects, the track object does NOT check the window */
		/* to see if it has been changed because all data is always stored in */
		/* the object itself for tracks */
		return TrackObj->DataModified;
	}


/* get a copy of the object's name */
char*									TrackObjectGetNameCopy(TrackObjectRec* TrackObj)
	{
		char*								StringTemp;

		CheckPtrExistence(TrackObj);
		StringTemp = CopyPtr(TrackObj->Name);
		if (StringTemp != NIL)
			{
				SetTag(StringTemp,"TrackObjectGetNameCopy");
			}
		return StringTemp;
	}


/* put a new name.  the object becomes the owner of the name block, so the */
/* caller should not release it */
void									TrackObjectPutName(TrackObjectRec* TrackObj, char* Name)
	{
		char*								NullTerminated;

		CheckPtrExistence(TrackObj);
		CheckPtrExistence(Name);
		ReleasePtr(TrackObj->Name);
		TrackObj->Name = Name;
		SetTag(Name,"TrackObjectPutName name");
		TrackObj->DataModified = True; /* no need to call TrackObjectAltered */
		NullTerminated = BlockToStringCopy(Name);
		if (NullTerminated != NIL)
			{
				if (StrLen(NullTerminated) > 0)
					{
						/* it's bad to set menu item names to the empty string! */
						ChangeItemName(TrackObj->TrackMenuItem,NullTerminated);
					}
				ReleasePtr(NullTerminated);
			}
		TrackListTrackNameChanged(TrackObj->TrackList,TrackObj);
	}


/* get a copy of the name of the instrument that the track will be played with */
char*									TrackObjectGetInstrName(TrackObjectRec* TrackObj)
	{
		char*								StringTemp;

		CheckPtrExistence(TrackObj);
		StringTemp = CopyPtr(TrackObj->InstrumentName);
		if (StringTemp != NIL)
			{
				SetTag(StringTemp,"TrackObjectGetInstrName name");
			}
		return StringTemp;
	}


/* change the name of the instrument that the track will be played with.  the */
/* track object will become the owner of the block of memory. */
void									TrackObjectPutNewInstrName(TrackObjectRec* TrackObj, char* Name)
	{
		CheckPtrExistence(TrackObj);
		CheckPtrExistence(Name);
		ReleasePtr(TrackObj->InstrumentName);
		TrackObj->InstrumentName = Name;
		SetTag(Name,"TrackObjectPutNewInstrName name");
		TrackObj->DataModified = True; /* no need to call TrackObjectAltered */
	}


/* get a copy of the postprocessing formula */
char*									TrackObjectGetPostProcessing(TrackObjectRec* TrackObj)
	{
		char*								StringTemp;

		CheckPtrExistence(TrackObj);
		StringTemp = CopyPtr(TrackObj->PostProcessingFormula);
		if (StringTemp != NIL)
			{
				SetTag(StringTemp,"PostProcessingFormula");
			}
		return StringTemp;
	}


/* change the postprocessing formula.  the object becomes owner of the memory block. */
void									TrackObjectPutNewPostProcessing(TrackObjectRec* TrackObj,
												char* PostProcExpr)
	{
		CheckPtrExistence(TrackObj);
		CheckPtrExistence(PostProcExpr);
		ReleasePtr(TrackObj->PostProcessingFormula);
		TrackObj->PostProcessingFormula = PostProcExpr;
		SetTag(PostProcExpr,"PostProcessingFormula");
		TrackObj->DataModified = True; /* no need to call TrackObjectAltered */
	}


/* get number of frames in track */
long									TrackObjectGetNumFrames(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return ArrayGetLength(TrackObj->FrameArray);
	}


/* get the frame for a given track index */
FrameObjectRec*				TrackObjectGetFrame(TrackObjectRec* TrackObj, long Index)
	{
		CheckPtrExistence(TrackObj);
		ERROR((Index < 0) || (Index >= TrackObjectGetNumFrames(TrackObj)),PRERR(ForceAbort,
			"TrackObjectGetFrame:  index out of range"));
		return (FrameObjectRec*)ArrayGetElement(TrackObj->FrameArray,Index);
	}


/* delete a range of frames from the track. */
void									TrackObjectDeleteFrameRun(TrackObjectRec* TrackObj,
												long Index, long Count)
	{
		long								SourceFrameScan;
		long								SourceFrameLimit;

		CheckPtrExistence(TrackObj);
		/* this thing is going to have to be redrawn */
		TrackObjectAltered(TrackObj,Index);

		/* do the deletion */
		for (SourceFrameScan = Index; SourceFrameScan < Index + Count; SourceFrameScan += 1)
			{
				FrameObjectRec*			GallowsFrame;

				GallowsFrame = (FrameObjectRec*)ArrayGetElement(TrackObj->FrameArray,Index);
				ArrayDeleteElement(TrackObj->FrameArray,Index);
				DisposeFrameAndContents(GallowsFrame);
			}

		/* now we have to break any ties that are no longer valid (because their */
		/* targets were just deleted) */
		SourceFrameLimit = ArrayGetLength(TrackObj->FrameArray);
		for (SourceFrameScan = 0; SourceFrameScan < SourceFrameLimit; SourceFrameScan += 1)
			{
				FrameObjectRec*			SourceFrame;

				SourceFrame = (FrameObjectRec*)ArrayGetElement(TrackObj->FrameArray,
					SourceFrameScan);
				/* it's either got some notes or one command */
				if (!IsThisACommandFrame(SourceFrame))
					{
						long								SourceNoteLimit;
						long								SourceNoteScan;

						SourceNoteLimit = NumNotesInFrame(SourceFrame);
						for (SourceNoteScan = 0; SourceNoteScan < SourceNoteLimit;
							SourceNoteScan += 1)
							{
								NoteObjectRec*			SourceNote;
								NoteObjectRec*			TieTarget;

								SourceNote = GetNoteFromFrame(SourceFrame,SourceNoteScan);
								TieTarget = GetNoteTieTarget(SourceNote);
								if (TieTarget != NIL)
									{
										long								TargetFrameLimit;
										long								TargetFrameScan;

										TargetFrameLimit = ArrayGetLength(TrackObj->FrameArray);
										for (TargetFrameScan = 0; TargetFrameScan < TargetFrameLimit;
											TargetFrameScan += 1)
											{
												FrameObjectRec*			TargetFrame;

												TargetFrame = (FrameObjectRec*)ArrayGetElement(
													TrackObj->FrameArray,TargetFrameScan);
												if (!IsThisACommandFrame(TargetFrame))
													{
														long								TargetNoteLimit;
														long								TargetNoteScan;

														TargetNoteLimit = NumNotesInFrame(TargetFrame);
														for (TargetNoteScan = 0; TargetNoteScan < TargetNoteLimit;
															TargetNoteScan += 1)
															{
																if (GetNoteFromFrame(TargetFrame,
																	TargetNoteScan) == TieTarget)
																	{
																		/* tie is valid, so no need to zap it */
																		goto TieValidPoint1;
																	}
															}
													}
											} /* end inner frame list scan */
										/* the target wasn't found, so we clobber it */
										PutNoteTieTarget(SourceNote,NIL);
									 TieValidPoint1:
										;
									}
							} /* end frame scan */
					}
			} /* end frame list scan */
	}


/* get a list of frames and copy them out of the track */
struct ArrayRec*			TrackObjectCopyFrameRun(TrackObjectRec* TrackObj,
												long Index, long Count)
	{
		ArrayRec*						ReturnList;
		long								ReturnFrameScan;
		long								ReturnFrameLimit;

		CheckPtrExistence(TrackObj);

		/* create the list to hold all of the new things */
		ReturnList = NewArrayReserveSpace(Count);
		if (ReturnList == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		ReturnFrameScan = Index;
		while (ReturnFrameScan < Index + Count)
			{
				FrameObjectRec*			OriginalFrame;
				FrameObjectRec*			DuplicateFrame;

				OriginalFrame = (FrameObjectRec*)ArrayGetElement(TrackObj->FrameArray,
					ReturnFrameScan);
				DuplicateFrame = DeepDuplicateFrame(OriginalFrame);
				if (DuplicateFrame == NIL)
					{
					 FailurePoint2:
						ReturnFrameLimit = ArrayGetLength(ReturnList);
						for (ReturnFrameScan = 0; ReturnFrameScan < ReturnFrameLimit;
							ReturnFrameScan += 1)
							{
								DisposeFrameAndContents((FrameObjectRec*)ArrayGetElement(ReturnList,
									ReturnFrameScan));
							}
						DisposeArray(ReturnList);
						goto FailurePoint1;
					}
				if (!ArrayAppendElement(ReturnList,DuplicateFrame))
					{
					 FailurePoint2a:
						DisposeFrameAndContents(DuplicateFrame);
						goto FailurePoint2;
					}
				ReturnFrameScan += 1;
			}

		/* now we have to patch up ties.  for each tie in the new array, find the */
		/* note in the old array.  if it corresponds to a note we copied to the new */
		/* array, then fix it up, otherwise set it to NIL. */
		ReturnFrameLimit = ArrayGetLength(ReturnList);
		for (ReturnFrameScan = 0; ReturnFrameScan < ReturnFrameLimit; ReturnFrameScan += 1)
			{
				FrameObjectRec*			Frame;

				Frame = (FrameObjectRec*)ArrayGetElement(ReturnList,ReturnFrameScan);
				/* it's either got some notes or one command */
				if (!IsThisACommandFrame(Frame))
					{
						long								ReturnNoteLimit;
						long								ReturnNoteScan;

						ReturnNoteLimit = NumNotesInFrame(Frame);
						for (ReturnNoteScan = 0; ReturnNoteScan < ReturnNoteLimit;
							ReturnNoteScan += 1)
							{
								NoteObjectRec*			Note;
								NoteObjectRec*			OurNoteTieTarget;

								Note = GetNoteFromFrame(Frame,ReturnNoteScan);
								OurNoteTieTarget = GetNoteTieTarget(Note);
								if (OurNoteTieTarget != NIL)
									{
										long								OriginalFrameLimit;
										long								OriginalFrameScan;

										OriginalFrameLimit = ArrayGetLength(TrackObj->FrameArray);
										for (OriginalFrameScan = 0; OriginalFrameScan < OriginalFrameLimit;
											OriginalFrameScan += 1)
											{
												FrameObjectRec*			OrigFrame;

												OrigFrame = (FrameObjectRec*)ArrayGetElement(
													TrackObj->FrameArray,OriginalFrameScan);
												if (!IsThisACommandFrame(OrigFrame))
													{
														long								OriginalNoteLimit;
														long								OriginalNoteScan;

														OriginalNoteLimit = NumNotesInFrame(OrigFrame);
														for (OriginalNoteScan = 0; OriginalNoteScan
															< OriginalNoteLimit; OriginalNoteScan += 1)
															{
																NoteObjectRec*			Possibility;

																Possibility = GetNoteFromFrame(OrigFrame,
																	OriginalNoteScan);
																if (Possibility == OurNoteTieTarget)
																	{
																		/* found the tie target */
																		if ((OriginalFrameScan >= Index)
																			&& (OriginalFrameScan < Index + Count))
																			{
																				/* it's a valid tie, so fix it up */
																				PutNoteTieTarget(Note,GetNoteFromFrame(
																					(FrameObjectRec*)ArrayGetElement(ReturnList,
																					OriginalFrameScan - Index),OriginalNoteScan));
																			}
																		 else
																			{
																				/* it isn't valid, so kill it */
																				PutNoteTieTarget(Note,NIL);
																			}
																		goto TieValidPoint1;
																	}
															}
													}
											} /* end inner frame list scan */
										EXECUTE(PRERR(ForceAbort,
											"TrackObjectCopyFrameRun:  tie target not found"));
									 TieValidPoint1:
										;
									}
							} /* end frame scan */
					}
			} /* end frame list scan */

		return ReturnList;
	}


/* find any notes that are referencing the specified note via a tie and nullify the tie. */
void									TrackObjectNullifyTies(TrackObjectRec* TrackObj,
												struct NoteObjectRec* NoteThatIsDying)
	{
		long								FrameScan;
		long								FrameLimit;

		CheckPtrExistence(TrackObj);
		CheckPtrExistence(NoteThatIsDying);
		FrameLimit = ArrayGetLength(TrackObj->FrameArray);
		for (FrameScan = 0; FrameScan < FrameLimit; FrameScan += 1)
			{
				FrameObjectRec*			Frame;
				long								NoteScan;
				long								NoteLimit;

				Frame = (FrameObjectRec*)ArrayGetElement(TrackObj->FrameArray,FrameScan);
				CheckPtrExistence(Frame);
				NoteLimit = NumNotesInFrame(Frame);
				for (NoteScan = 0; NoteScan < NoteLimit; NoteScan += 1)
					{
						NoteObjectRec*			Note;

						Note = GetNoteFromFrame(Frame,NoteScan);
						if (!IsItACommand(Note))
							{
								if (GetNoteTieTarget(Note) == NoteThatIsDying)
									{
										PutNoteTieTarget(Note,NIL);
									}
							}
					}
			}
		TrackObj->DataModified = True;
	}


/* insert a frame at the specified position */
MyBoolean							TrackObjectInsertFrame(TrackObjectRec* TrackObj, long Index,
												struct FrameObjectRec* NewFrame)
	{
		CheckPtrExistence(TrackObj);
		CheckPtrExistence(NewFrame);
		ERROR((Index < 0) || (Index > TrackObjectGetNumFrames(TrackObj)),PRERR(ForceAbort,
			"TrackObjectInsertFrame:  index out of range"));
		if (ArrayInsertElement(TrackObj->FrameArray,NewFrame,Index))
			{
				TrackObjectAltered(TrackObj,Index);
				return True;
			}
		 else
			{
				return False;
			}
	}


/* show the window for this object.  returns True if successful */
MyBoolean							TrackObjectOpenWindow(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		if (TrackObj->TrackWindow != NIL)
			{
				TrackWindowBringToTop(TrackObj->TrackWindow);
			}
		 else
			{
				TrackObj->TrackWindow = NewTrackWindow(TrackObj,TrackObj->MainWindow,
					TrackObj->TrackList,TrackObj->SavedWindowXLoc,TrackObj->SavedWindowYLoc,
					TrackObj->SavedWindowWidth,TrackObj->SavedWindowHeight);
			}
		return (TrackObj->TrackWindow != NIL);
	}


/* notify the object that it's window is closing.  the object should take no action */
void									TrackObjectClosingWindowNotify(TrackObjectRec* TrackObj,
												short NewX, short NewY, short NewWidth, short NewHeight)
	{
		CheckPtrExistence(TrackObj);
		ERROR(TrackObj->TrackWindow == NIL,PRERR(ForceAbort,
			"TrackObjectClosingWindowNotify:  no window is open"));
		TrackObj->TrackWindow = NIL;
		TrackObj->SavedWindowXLoc = NewX;
		TrackObj->SavedWindowYLoc = NewY;
		TrackObj->SavedWindowWidth = NewWidth;
		TrackObj->SavedWindowHeight = NewHeight;
	}


/* indicate that the track has been altered starting at a certain position */
/* this sends a message to all track views that have been registered. */
void									TrackObjectAltered(TrackObjectRec* TrackObj, long Index)
	{
		long								Scan;
		long								Limit;

		CheckPtrExistence(TrackObj);
		ERROR((Index < 0) || (Index > TrackObjectGetNumFrames(TrackObj)),PRERR(ForceAbort,
			"TrackObjectAltered:  index out of range"));
		TrackObj->DataModified = True;
		Limit = ArrayGetLength(TrackObj->DependentViews);
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				TrackViewRec*				View;

				View = (TrackViewRec*)ArrayGetElement(TrackObj->DependentViews,Scan);
				TrackViewTrackObjectModified(View,TrackObj,Index);
			}
	}


/* add a track view object to the list of things that want to be notified when */
/* data in this track is altered */
MyBoolean							TrackObjectAddDependentView(TrackObjectRec* TrackObj,
												struct TrackViewRec* TheView)
	{
		CheckPtrExistence(TrackObj);
		CheckPtrExistence(TheView);
		ERROR(ArrayFindElement(TrackObj->DependentViews,TheView) >= 0,PRERR(ForceAbort,
			"TrackObjectAddDependentView:  view already on list"));
		return ArrayAppendElement(TrackObj->DependentViews,TheView);
	}


/* remove a track view object that no longer wants to be notified upon changes */
void									TrackObjectRemoveDependentView(TrackObjectRec* TrackObj,
												struct TrackViewRec* TheView)
	{
		CheckPtrExistence(TrackObj);
		ERROR(ArrayFindElement(TrackObj->DependentViews,TheView) < 0,PRERR(ForceAbort,
			"TrackObjectRemoveDependentView:  view isn't in list"));
		ArrayDeleteElement(TrackObj->DependentViews,
			ArrayFindElement(TrackObj->DependentViews,TheView));
	}


/* add a track view that wants to be seen in the background of this one. */
/* this doesn't actually do too much, since we only keep the list, we don't */
/* do any of the stuff required to actually show it in the background. */
MyBoolean							TrackObjectAddBackgroundObj(TrackObjectRec* TrackObj,
												TrackObjectRec* OtherTrackObj)
	{
		CheckPtrExistence(TrackObj);
		CheckPtrExistence(OtherTrackObj);
		ERROR(OtherTrackObj == TrackObj,PRERR(ForceAbort,
			"TrackObjectAddBackgroundObj:  adding self to list"));
		ERROR(ArrayFindElement(TrackObj->BackgroundObjects,OtherTrackObj) >= 0,
			PRERR(ForceAbort,"TrackObjectAddBackgroundObj:  view already on list"));
		return ArrayAppendElement(TrackObj->BackgroundObjects,OtherTrackObj);
	}


/* remove a track view that no longer wants to be seen in the background of this one */
/* the list should call this on everyone when one is deleted so that there aren't */
/* any dangling dependencies left around.  it is not an error to delete something */
/* that isn't in the list */
/* this doesn't actually do too much, since we only keep the list, we don't */
/* do any of the stuff required to actually show it in the background. */
void									TrackObjectRemoveBackgroundObj(TrackObjectRec* TrackObj,
												TrackObjectRec* OtherTrackObj)
	{
		long								Position;

		CheckPtrExistence(TrackObj);
		CheckPtrExistence(OtherTrackObj);
		Position = ArrayFindElement(TrackObj->BackgroundObjects,OtherTrackObj);
		if (Position >= 0)
			{
				ArrayDeleteElement(TrackObj->BackgroundObjects,Position);
			}
	}


/* get background object list.  (Actual thing). List of TrackObjectRec*'s */
struct ArrayRec*			TrackObjectGetBackgroundList(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->BackgroundObjects;
	}


/* get the menu item associated with this track */
struct MenuItemType*	TrackObjectGetMenuItem(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->TrackMenuItem;
	}


/* the document's name changed, so we need to update the window */
void									TrackObjectGlobalNameChange(TrackObjectRec* TrackObj,
												char* NewFilename)
	{
		CheckPtrExistence(TrackObj);
		if (TrackObj->TrackWindow != NIL)
			{
				TrackWindowGlobalNameChange(TrackObj->TrackWindow,NewFilename);
			}
	}


/* get the default early/late hit adjustment factor */
double								TrackObjectGetEarlyLateAdjust(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultEarlyLateAdjust;
	}


/* get the default first release point */
double								TrackObjectGetReleasePoint1(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultReleasePoint1;
	}


/* get the default first release point's from start or end flag (this is the same */
/* as the mask used in each note) */
unsigned long					TrackObjectGetReleasePoint1StartEndFlag(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultReleasePoint1ModeFlag;
	}


/* get the default second release point */
double								TrackObjectGetReleasePoint2(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultReleasePoint2;
	}


/* get the default second release point's from start or end flag */
unsigned long					TrackObjectGetReleasePoint2StartEndFlag(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultReleasePoint2ModeFlag;
	}


/* get the default overall loudness adjustment factor */
double								TrackObjectGetOverallLoudness(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultOverallLoudness;
	}


/* get the default stereo positioning */
double								TrackObjectGetStereoPositioning(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultStereoPositioning;
	}


/* get the default surround positioning */
double								TrackObjectGetSurroundPositioning(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultSurroundPositioning;
	}


/* get the default first accent adjust */
double								TrackObjectGetAccent1(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultAccent1;
	}


/* get the default second accent adjust */
double								TrackObjectGetAccent2(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultAccent2;
	}


/* get the default third accent adjust */
double								TrackObjectGetAccent3(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultAccent3;
	}


/* get the default fourth accent adjust */
double								TrackObjectGetAccent4(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultAccent4;
	}


/* get the default pitch displacement depth adjust */
double								TrackObjectGetPitchDisplacementDepthAdjust(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultPitchDisplacementDepthAdjust;
	}


/* get the default pitch displacement rate adjust */
double								TrackObjectGetPitchDisplacementRateAdjust(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultPitchDisplacementRateAdjust;
	}


/* get the default pitch displacement start point */
double								TrackObjectGetPitchDisplacementStartPoint(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultPitchDisplacementStartPoint;
	}


/* get the default pitch displacement start point control flag */
unsigned long					TrackObjectGetPitchDisplacementFromStartOrEnd(
												TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultPitchDisplacementStartPointModeFlag;
	}


/* get the default hurry-up factor */
double								TrackObjectGetHurryUp(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultHurryUpFactor;
	}


/* get the default detuning */
double								TrackObjectGetDetune(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultDetune;
	}


/* get the detuning control flag */
unsigned long					TrackObjectGetDetuneControlFlag(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultDetuneModeFlag;
	}


/* get the default duration adjustment */
double								TrackObjectGetDurationAdjust(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultDuration;
	}


/* get the default duration adjust mode flag */
unsigned long					TrackObjectGetDurationModeFlag(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->DefaultDurationModeFlag;
	}


/* change the default early/late hit adjustment factor */
void									PutTrackObjectEarlyLateAdjust(TrackObjectRec* TrackObj,
												double NewEarlyLateAdjust)
	{
		CheckPtrExistence(TrackObj);
		TrackObj->DefaultEarlyLateAdjust = NewEarlyLateAdjust;
		TrackObj->DataModified = True;
	}


/* change the default first release point */
void									PutTrackObjectReleasePoint1(TrackObjectRec* TrackObj,
												double NewReleasePoint1)
	{
		CheckPtrExistence(TrackObj);
		TrackObj->DefaultReleasePoint1 = NewReleasePoint1;
		TrackObj->DataModified = True;
	}


/* change the default first release point's from start or end flag */
void									PutTrackObjectReleasePoint1StartEndFlag(TrackObjectRec* TrackObj,
												unsigned long NewReleasePoint1Flag)
	{
		CheckPtrExistence(TrackObj);
		ERROR((NewReleasePoint1Flag != eRelease1FromStart)
			&& (NewReleasePoint1Flag != eRelease1FromEnd),PRERR(AllowResume,
			"PutTrackObjectReleasePoint1StartEndFlag:  bad value"));
		TrackObj->DefaultReleasePoint1ModeFlag = NewReleasePoint1Flag;
		TrackObj->DataModified = True;
	}


/* change the default second release point */
void									PutTrackObjectReleasePoint2(TrackObjectRec* TrackObj,
												double NewReleasePoint2)
	{
		CheckPtrExistence(TrackObj);
		TrackObj->DefaultReleasePoint2 = NewReleasePoint2;
		TrackObj->DataModified = True;
	}


/* change the default second release point's from start or end flag */
void									PutTrackObjectReleasePoint2StartEndFlag(TrackObjectRec* TrackObj,
												unsigned long NewReleasePoint2Flag)
	{
		CheckPtrExistence(TrackObj);
		ERROR((NewReleasePoint2Flag != eRelease2FromStart)
			&& (NewReleasePoint2Flag != eRelease2FromEnd),PRERR(AllowResume,
			"PutTrackObjectReleasePoint2StartEndFlag:  bad value"));
		TrackObj->DefaultReleasePoint2ModeFlag = NewReleasePoint2Flag;
		TrackObj->DataModified = True;
	}


/* change the default overall loudness adjustment factor */
void									PutTrackObjectOverallLoudness(TrackObjectRec* TrackObj,
												double NewOverallLoudness)
	{
		CheckPtrExistence(TrackObj);
		TrackObj->DefaultOverallLoudness = NewOverallLoudness;
		TrackObj->DataModified = True;
	}


/* change the default stereo positioning value */
void									PutTrackObjectStereoPositioning(TrackObjectRec* TrackObj,
												double NewStereoPositioning)
	{
		CheckPtrExistence(TrackObj);
		TrackObj->DefaultStereoPositioning = NewStereoPositioning;
		TrackObj->DataModified = True;
	}


/* change the default surround positioning value */
void									PutTrackObjectSurroundPositioning(TrackObjectRec* TrackObj,
												double NewSurroundPositioning)
	{
		CheckPtrExistence(TrackObj);
		TrackObj->DefaultSurroundPositioning = NewSurroundPositioning;
		TrackObj->DataModified = True;
	}


/* change the default first accent adjust */
void									PutTrackObjectAccent1(TrackObjectRec* TrackObj,
												double NewAccent1)
	{
		CheckPtrExistence(TrackObj);
		TrackObj->DefaultAccent1 = NewAccent1;
		TrackObj->DataModified = True;
	}


/* change the default second accent adjust */
void									PutTrackObjectAccent2(TrackObjectRec* TrackObj,
												double NewAccent2)
	{
		CheckPtrExistence(TrackObj);
		TrackObj->DefaultAccent2 = NewAccent2;
		TrackObj->DataModified = True;
	}


/* change the default third accent adjust */
void									PutTrackObjectAccent3(TrackObjectRec* TrackObj,
												double NewAccent3)
	{
		CheckPtrExistence(TrackObj);
		TrackObj->DefaultAccent3 = NewAccent3;
		TrackObj->DataModified = True;
	}


/* change the default fourth accent adjust */
void									PutTrackObjectAccent4(TrackObjectRec* TrackObj,
												double NewAccent4)
	{
		CheckPtrExistence(TrackObj);
		TrackObj->DefaultAccent4 = NewAccent4;
		TrackObj->DataModified = True;
	}


/* change the default pitch displacement depth adjust */
void									PutTrackObjectPitchDisplacementDepthAdjust(TrackObjectRec* TrackObj,
												double NewPitchDisplacementDepthAdjust)
	{
		CheckPtrExistence(TrackObj);
		TrackObj->DefaultPitchDisplacementDepthAdjust = NewPitchDisplacementDepthAdjust;
		TrackObj->DataModified = True;
	}


/* change the default pitch displacement rate adjust */
void									PutTrackObjectPitchDisplacementRateAdjust(TrackObjectRec* TrackObj,
												double NewPitchDisplacementRate)
	{
		CheckPtrExistence(TrackObj);
		TrackObj->DefaultPitchDisplacementRateAdjust = NewPitchDisplacementRate;
		TrackObj->DataModified = True;
	}


/* change the default pitch displacement start point */
void									PutTrackObjectPitchDisplacementStartPoint(TrackObjectRec* TrackObj,
												double NewPitchDisplacementStartPoint)
	{
		CheckPtrExistence(TrackObj);
		TrackObj->DefaultPitchDisplacementStartPoint = NewPitchDisplacementStartPoint;
		TrackObj->DataModified = True;
	}


/* change the default pitch displacement start point control flag */
void									PutTrackObjectPitchDisplacementFromStartOrEnd(TrackObjectRec*
												TrackObj, unsigned long NewPitchDisplacementStartPointControl)
	{
		CheckPtrExistence(TrackObj);
		ERROR((NewPitchDisplacementStartPointControl != ePitchDisplacementStartFromStart)
			&& (NewPitchDisplacementStartPointControl != ePitchDisplacementStartFromEnd),
			PRERR(AllowResume,"PutTrackObjectPitchDisplacementFromStartOrEnd:  bad value"));
		TrackObj->DefaultPitchDisplacementStartPointModeFlag
			= NewPitchDisplacementStartPointControl;
		TrackObj->DataModified = True;
	}


/* change the default hurry-up factor */
void									PutTrackObjectHurryUp(TrackObjectRec* TrackObj, double NewHurryUp)
	{
		CheckPtrExistence(TrackObj);
		TrackObj->DefaultHurryUpFactor = NewHurryUp;
		TrackObj->DataModified = True;
	}


/* change the default detuning */
void									PutTrackObjectDetune(TrackObjectRec* TrackObj, double NewDetune)
	{
		CheckPtrExistence(TrackObj);
		TrackObj->DefaultDetune = NewDetune;
		TrackObj->DataModified = True;
	}


/* change the detuning control flag */
void									PutTrackObjectDetuneControlFlag(TrackObjectRec* TrackObj,
												unsigned long NewDetuneControlFlag)
	{
		CheckPtrExistence(TrackObj);
		ERROR((NewDetuneControlFlag != eDetuningModeHalfSteps)
			&& (NewDetuneControlFlag != eDetuningModeHertz),PRERR(AllowResume,
			"PutTrackObjectDetuneControlFlag:  bad value"));
		TrackObj->DefaultDetuneModeFlag = NewDetuneControlFlag;
		TrackObj->DataModified = True;
	}


/* change the default duration adjustment */
void									PutTrackObjectDurationAdjust(TrackObjectRec* TrackObj,
												double NewDurationAdjust)
	{
		CheckPtrExistence(TrackObj);
		TrackObj->DefaultDuration = NewDurationAdjust;
		TrackObj->DataModified = True;
	}


/* change the default duration adjust mode flag */
void									PutTrackObjectDurationModeFlag(TrackObjectRec* TrackObj,
												unsigned long NewDurationModeFlag)
	{
		CheckPtrExistence(TrackObj);
		ERROR((NewDurationModeFlag != eDurationAdjustAdditive)
			&& (NewDurationModeFlag != eDurationAdjustMultiplicative),PRERR(AllowResume,
			"PutTrackObjectDurationModeFlag:  bad value"));
		TrackObj->DefaultDurationModeFlag = NewDurationModeFlag;
		TrackObj->DataModified = True;
	}


/* find out if this track should be included when we play the score */
MyBoolean							TrackObjectShouldItBePlayed(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		return TrackObj->IncludeThisTrackInFinalPlayback;
	}


/* chage status of whether track should be played */
void									ChangeTrackObjectShouldBePlayed(TrackObjectRec* TrackObj,
												MyBoolean ShouldWePlayIt)
	{
		CheckPtrExistence(TrackObj);
		TrackObj->IncludeThisTrackInFinalPlayback = ShouldWePlayIt;
		/* don't set DataModified */
	}


/* Track Object Subblock Format: */
/*   1-byte format version number */
/*       should be 1 or 2 */
/*   2-byte little endian window X position (signed; from top-left corner of screen) */
/*   2-byte little endian window Y position */
/*   2-byte little endian window width */
/*   2-byte little endian window height */
/*   4-byte little endian track name length descriptor */
/*   n-byte track name string (line feed = 0x0a) */
/*   4-byte little endian large integer coded decimal default early/late adjust */
/*       large integer coded decimal is decimal * 1000000 with a */
/*       range of -1999.999999 to 1999.999999 */
/*   4-byte little endian large integer coded decimal default release point 1 */
/*   1-byte default release point 1 mode flag */
/*       0 = release from start */
/*       1 = release from end */
/*   4-byte little endian large integer coded decimal default release point 2 */
/*   1-byte default release point 2 mode flag */
/*       0 = release from start */
/*       1 = release from end */
/*   4-byte little endian large integer coded decimal default overall loudness */
/*   4-byte little endian large integer coded decimal default stereo positioning */
/*   4-byte little endian large integer coded decimal default surround positioning */
/*   4-byte little endian large integer coded decimal default accent 1 */
/*   4-byte little endian large integer coded decimal default accent 2 */
/*   4-byte little endian large integer coded decimal default accent 3 */
/*   4-byte little endian large integer coded decimal default accent 4 */
/*   4-byte little endian large integer coded decimal default pitch disp depth adjust */
/*   1-byte default pitch displacement depth adjust mode flag */
/*       0 = half steps */
/*       1 = hertz */
/*       this field has been eliminated in the version 2 file format! */
/*   4-byte little endian large integer coded decimal default pitch disp rate adjust */
/*   4-byte little endian large integer coded decimal default pitch disp start point */
/*   1-byte default pitch displacement start point mode flag */
/*       0 = pitch displacement point from start */
/*       1 = pitch displacement point from end */
/*   4-byte little endian large integer coded decimal default hurry-up factor */
/*   4-byte little endian large integer coded decimal default detuning */
/*   1-byte default detuning mode flag */
/*       0 = half steps */
/*       1 = hertz */
/*   4-byte little endian large integer coded decimal default duration */
/*   1-byte default duration mode flag */
/*       0 = duration adjust is multiplicative */
/*       1 = duration adjust is additive */
/*   1-byte flag for playback inclusion */
/*       0 = don't play track in final playback */
/*       1 = do play track in final playback */
/*   4-byte little endian instrument name string length descriptor */
/*   n-byte instrument name string (line feed = 0x0a) */
/*   1-byte flag for channel post processing enabling */
/*       0 = don't do channel postprocessing */
/*       1 = do channel postprocessing */
/*   4-byte little endian postprocessing expression length descriptor */
/*   n-bytes of postprocessing stuff (line feed = 0x0a) */
/*   n-bytes of data for note vector */


/* read track information from the file and create a new track object */
FileLoadingErrors			TrackObjectNewFromFile(TrackObjectRec** ObjectOut,
												struct BufferedInputRec* Input, struct CodeCenterRec* CodeCenter,
												struct MainWindowRec* MainWindow, struct TrackListRec* TrackList)
	{
		TrackObjectRec*			TrackObj;
		FileLoadingErrors		Error;
		unsigned char				UnsignedChar;
		signed short				SignedShort;
		signed long					SignedLong;
		char*								NullTerminated;
		short								FormatVersionNumber;

		CheckPtrExistence(Input);
		CheckPtrExistence(CodeCenter);
		CheckPtrExistence(MainWindow);
		CheckPtrExistence(TrackList);

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

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

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

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

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

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

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

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

		/*   4-byte little endian large integer coded decimal default early/late adjust */
		/*       large integer coded decimal is decimal * 1000000 with a */
		/*       range of -1999.999999 to 1999.999999 */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint12:
				goto FailurePoint11;
			}
		TrackObj->DefaultEarlyLateAdjust = LargeBCD2Double(SignedLong);

		/*   4-byte little endian large integer coded decimal default release point 1 */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint13:
				goto FailurePoint12;
			}
		TrackObj->DefaultReleasePoint1 = LargeBCD2Double(SignedLong);

		/*   1-byte default release point 1 mode flag */
		/*       0 = release from start */
		/*       1 = release from end */
		if (!ReadBufferedUnsignedChar(Input,&UnsignedChar))
			{
				Error = eFileLoadDiskError;
			 FailurePoint14:
				goto FailurePoint13;
			}
		if (UnsignedChar == 0)
			{
				TrackObj->DefaultReleasePoint1ModeFlag = eRelease1FromStart;
			}
		else if (UnsignedChar == 1)
			{
				TrackObj->DefaultReleasePoint1ModeFlag = eRelease1FromEnd;
			}
		else
			{
				Error = eFileLoadBadFormat;
			 FailurePoint15:
				goto FailurePoint14;
			}

		/*   4-byte little endian large integer coded decimal default release point 2 */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint16:
				goto FailurePoint15;
			}
		TrackObj->DefaultReleasePoint2 = LargeBCD2Double(SignedLong);

		/*   1-byte default release point 2 mode flag */
		/*       0 = release from start */
		/*       1 = release from end */
		if (!ReadBufferedUnsignedChar(Input,&UnsignedChar))
			{
				Error = eFileLoadDiskError;
			 FailurePoint17:
				goto FailurePoint16;
			}
		if (UnsignedChar == 0)
			{
				TrackObj->DefaultReleasePoint2ModeFlag = eRelease2FromStart;
			}
		else if (UnsignedChar == 1)
			{
				TrackObj->DefaultReleasePoint2ModeFlag = eRelease2FromEnd;
			}
		else
			{
				Error = eFileLoadBadFormat;
			 FailurePoint18:
				goto FailurePoint17;
			}

		/*   4-byte little endian large integer coded decimal default overall loudness */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint19:
				goto FailurePoint18;
			}
		TrackObj->DefaultOverallLoudness = LargeBCD2Double(SignedLong);

		/*   4-byte little endian large integer coded decimal default stereo positioning */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint20:
				goto FailurePoint19;
			}
		TrackObj->DefaultStereoPositioning = LargeBCD2Double(SignedLong);

		/*   4-byte little endian large integer coded decimal default surround positioning */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint20oops:
				goto FailurePoint20;
			}
		TrackObj->DefaultSurroundPositioning = LargeBCD2Double(SignedLong);

		/*   4-byte little endian large integer coded decimal default accent 1 */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint21:
				goto FailurePoint20oops;
			}
		TrackObj->DefaultAccent1 = LargeBCD2Double(SignedLong);

		/*   4-byte little endian large integer coded decimal default accent 2 */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint22:
				goto FailurePoint21;
			}
		TrackObj->DefaultAccent2 = LargeBCD2Double(SignedLong);

		/*   4-byte little endian large integer coded decimal default accent 3 */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint23:
				goto FailurePoint22;
			}
		TrackObj->DefaultAccent3 = LargeBCD2Double(SignedLong);

		/*   4-byte little endian large integer coded decimal default accent 4 */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint24:
				goto FailurePoint23;
			}
		TrackObj->DefaultAccent4 = LargeBCD2Double(SignedLong);

		/*   4-byte little endian large integer coded decimal default pitch disp depth adjust */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint25:
				goto FailurePoint24;
			}
		TrackObj->DefaultPitchDisplacementDepthAdjust = LargeBCD2Double(SignedLong);

		/*   1-byte default pitch displacement depth adjust mode flag */
		/*       0 = half steps */
		/*       1 = hertz */
		/*       this field has been eliminated in the version 2 file format! */
		if (FormatVersionNumber == 1)
			{
				if (!ReadBufferedUnsignedChar(Input,&UnsignedChar))
					{
						Error = eFileLoadDiskError;
					 FailurePoint26:
						goto FailurePoint25;
					}
			}

		/*   4-byte little endian large integer coded decimal default pitch disp rate adjust */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint28:
				goto FailurePoint26;
			}
		TrackObj->DefaultPitchDisplacementRateAdjust = LargeBCD2Double(SignedLong);

		/*   4-byte little endian large integer coded decimal default pitch disp start point */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint29:
				goto FailurePoint28;
			}
		TrackObj->DefaultPitchDisplacementStartPoint = LargeBCD2Double(SignedLong);

		/*   1-byte default pitch displacement start point mode flag */
		/*       0 = pitch displacement point from start */
		/*       1 = pitch displacement point from end */
		if (!ReadBufferedUnsignedChar(Input,&UnsignedChar))
			{
				Error = eFileLoadDiskError;
			 FailurePoint30:
				goto FailurePoint29;
			}
		if (UnsignedChar == 0)
			{
				TrackObj->DefaultPitchDisplacementStartPointModeFlag
					= ePitchDisplacementStartFromStart;
			}
		else if (UnsignedChar == 1)
			{
				TrackObj->DefaultPitchDisplacementStartPointModeFlag
					= ePitchDisplacementStartFromEnd;
			}
		else
			{
				Error = eFileLoadBadFormat;
			 FailurePoint31:
				goto FailurePoint30;
			}

		/*   4-byte little endian large integer coded decimal default hurry-up factor */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint32:
				goto FailurePoint31;
			}
		TrackObj->DefaultHurryUpFactor = LargeBCD2Double(SignedLong);

		/*   4-byte little endian large integer coded decimal default detuning */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint33:
				goto FailurePoint32;
			}
		TrackObj->DefaultDetune = LargeBCD2Double(SignedLong);

		/*   1-byte default detuning mode flag */
		/*       0 = half steps */
		/*       1 = hertz */
		if (!ReadBufferedUnsignedChar(Input,&UnsignedChar))
			{
				Error = eFileLoadDiskError;
			 FailurePoint34:
				goto FailurePoint33;
			}
		if (UnsignedChar == 0)
			{
				TrackObj->DefaultDetuneModeFlag = eDetuningModeHalfSteps;
			}
		else if (UnsignedChar == 1)
			{
				TrackObj->DefaultDetuneModeFlag = eDetuningModeHertz;
			}
		else
			{
				Error = eFileLoadBadFormat;
			 FailurePoint35:
				goto FailurePoint34;
			}

		/*   4-byte little endian large integer coded decimal default duration */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint36:
				goto FailurePoint35;
			}
		TrackObj->DefaultDuration = LargeBCD2Double(SignedLong);

		/*   1-byte default duration mode flag */
		/*       0 = duration adjust is multiplicative */
		/*       1 = duration adjust is additive */
		if (!ReadBufferedUnsignedChar(Input,&UnsignedChar))
			{
				Error = eFileLoadDiskError;
			 FailurePoint37:
				goto FailurePoint36;
			}
		if (UnsignedChar == 0)
			{
				TrackObj->DefaultDurationModeFlag = eDurationAdjustMultiplicative;
			}
		else if (UnsignedChar == 1)
			{
				TrackObj->DefaultDurationModeFlag = eDurationAdjustAdditive;
			}
		else
			{
				Error = eFileLoadBadFormat;
			 FailurePoint38:
				goto FailurePoint37;
			}

		/*   1-byte flag for playback inclusion */
		/*       0 = don't play track in final playback */
		/*       1 = do play track in final playback */
		if (!ReadBufferedUnsignedChar(Input,&UnsignedChar))
			{
				Error = eFileLoadDiskError;
			 FailurePoint39:
				goto FailurePoint38;
			}
		if (UnsignedChar == 0)
			{
				TrackObj->IncludeThisTrackInFinalPlayback = False;
			}
		else if (UnsignedChar == 1)
			{
				TrackObj->IncludeThisTrackInFinalPlayback = True;
			}
		else
			{
				Error = eFileLoadBadFormat;
			 FailurePoint40:
				goto FailurePoint39;
			}

		/*   4-byte little endian instrument name string length descriptor */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint41:
				goto FailurePoint40;
			}
		if (SignedLong < 0)
			{
				Error = eFileLoadBadFormat;
			 FailurePoint42:
				goto FailurePoint41;
			}

		/*   n-byte instrument name string (line feed = 0x0a) */
		TrackObj->InstrumentName = AllocPtrCanFail(SignedLong,"TrackObjectRec:  instr name");
		if (TrackObj->InstrumentName == NIL)
			{
				Error = eFileLoadOutOfMemory;
			 FailurePoint43:
				goto FailurePoint42;
			}
		if (!ReadBufferedInput(Input,SignedLong,TrackObj->InstrumentName))
			{
				Error = eFileLoadDiskError;
			 FailurePoint44:
				ReleasePtr(TrackObj->InstrumentName);
				goto FailurePoint43;
			}

		/*   1-byte flag for channel post processing enabling */
		/*       0 = don't do channel postprocessing */
		/*       1 = do channel postprocessing */
		if (!ReadBufferedUnsignedChar(Input,&UnsignedChar))
			{
				Error = eFileLoadDiskError;
			 FailurePoint44a:
				goto FailurePoint44;
			}
		if (UnsignedChar == 0)
			{
				TrackObj->PostProcessingEnable = False;
			}
		else if (UnsignedChar == 1)
			{
				TrackObj->PostProcessingEnable = True;
			}
		else
			{
				Error = eFileLoadBadFormat;
			 FailurePoint44b:
				goto FailurePoint44a;
			}

		/*   4-byte little endian postprocessing expression length descriptor */
		if (!ReadBufferedSignedLongLittleEndian(Input,&SignedLong))
			{
				Error = eFileLoadDiskError;
			 FailurePoint45:
				goto FailurePoint44b;
			}
		if (SignedLong < 0)
			{
				Error = eFileLoadBadFormat;
			 FailurePoint46:
				goto FailurePoint45;
			}

		/*   n-bytes of postprocessing stuff (line feed = 0x0a) */
		TrackObj->PostProcessingFormula = AllocPtrCanFail(SignedLong,"PostProcessingFormula");
		if (TrackObj->PostProcessingFormula == NIL)
			{
				Error = eFileLoadOutOfMemory;
			 FailurePoint47:
				goto FailurePoint46;
			}
		if (!ReadBufferedInput(Input,SignedLong,TrackObj->PostProcessingFormula))
			{
				Error = eFileLoadDiskError;
			 FailurePoint48:
				ReleasePtr(TrackObj->PostProcessingFormula);
				goto FailurePoint47;
			}

		/*   n-bytes of data for note array */
		EXECUTE(TrackObj->FrameArray = (ArrayRec*)0x81818181;)
		Error = ReadNoteVector(&(TrackObj->FrameArray),Input);
		if (Error != eFileLoadNoError)
			{
			 FailurePoint49:
				goto FailurePoint48;
			}
		CheckPtrExistence(TrackObj->FrameArray);

		/* fill in the other fields */
		TrackObj->DataModified = False;
		TrackObj->DependentViews = NewArray();
		if (TrackObj->DependentViews == NIL)
			{
				long						Scan;
				long						Limit;

				Error = eFileLoadOutOfMemory;
			 FailurePoint50:
				Limit = ArrayGetLength(TrackObj->FrameArray);
				for (Scan = 0; Scan < Limit; Scan += 1)
					{
						DisposeFrameAndContents((FrameObjectRec*)ArrayGetElement(
							TrackObj->FrameArray,Scan));
					}
				goto FailurePoint49;
			}
		TrackObj->BackgroundObjects = NewArray();
		if (TrackObj->BackgroundObjects == NIL)
			{
				Error = eFileLoadOutOfMemory;
			 FailurePoint51:
				DisposeArray(TrackObj->DependentViews);
				goto FailurePoint50;
			}
		NullTerminated = BlockToStringCopy(TrackObj->Name);
		if (NullTerminated == NIL)
			{
				Error = eFileLoadOutOfMemory;
			 FailurePoint52:
				goto FailurePoint51;
			}
		TrackObj->TrackMenuItem = MakeNewMenuItem(TrackListGetTrackMenu(TrackList),
			NullTerminated,0);
		if (TrackObj->TrackMenuItem == NIL)
			{
				Error = eFileLoadOutOfMemory;
			 FailurePoint52a:
				ReleasePtr(NullTerminated);
				goto FailurePoint52;
			}
		ReleasePtr(NullTerminated);
		TrackObj->TrackWindow = NIL;
		TrackObj->CodeCenter = CodeCenter;
		TrackObj->MainWindow = MainWindow;
		TrackObj->TrackList = TrackList;

		*ObjectOut = TrackObj;
		return eFileLoadNoError;
	}


/* write the track information to the file. */
FileLoadingErrors			TrackObjectWriteDataOut(TrackObjectRec* TrackObj,
												struct BufferedOutputRec* Output)
	{
		char*								StringTemp;
		FileLoadingErrors		Error;

		CheckPtrExistence(TrackObj);
		CheckPtrExistence(Output);

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

		/*   2-byte little endian window X position (signed; from top-left corner of screen) */
		/* if the window is open when the file is saved, then the most recent location */
		/* of the window will not be saved. */
		if (!WriteBufferedSignedShortLittleEndian(Output,TrackObj->SavedWindowXLoc))
			{
				return eFileLoadDiskError;
			}

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

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

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

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

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

		/*   4-byte little endian large integer coded decimal default early/late adjust */
		/*       large integer coded decimal is decimal * 1000000 with a */
		/*       range of -1999.999999 to 1999.999999 */
		if (!WriteBufferedSignedLongLittleEndian(Output,
			Double2LargeBCD(TrackObjectGetEarlyLateAdjust(TrackObj))))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian large integer coded decimal default release point 1 */
		if (!WriteBufferedSignedLongLittleEndian(Output,
			Double2LargeBCD(TrackObjectGetReleasePoint1(TrackObj))))
			{
				return eFileLoadDiskError;
			}

		/*   1-byte default release point 1 mode flag */
		/*       0 = release from start */
		/*       1 = release from end */
		switch (TrackObjectGetReleasePoint1StartEndFlag(TrackObj))
			{
				default:
					EXECUTE(PRERR(ForceAbort,"TrackObjectWriteDataOut:  bad value "
						"from TrackObjectGetReleasePoint1StartEndFlag"));
					break;
				case eRelease1FromStart:
					if (!WriteBufferedUnsignedChar(Output,0))
						{
							return eFileLoadDiskError;
						}
					break;
				case eRelease1FromEnd:
					if (!WriteBufferedUnsignedChar(Output,1))
						{
							return eFileLoadDiskError;
						}
					break;
			}

		/*   4-byte little endian large integer coded decimal default release point 2 */
		if (!WriteBufferedSignedLongLittleEndian(Output,
			Double2LargeBCD(TrackObjectGetReleasePoint2(TrackObj))))
			{
				return eFileLoadDiskError;
			}

		/*   1-byte default release point 2 mode flag */
		/*       0 = release from start */
		/*       1 = release from end */
		switch (TrackObjectGetReleasePoint2StartEndFlag(TrackObj))
			{
				default:
					EXECUTE(PRERR(ForceAbort,"TrackObjectWriteDataOut:  bad value "
						"from TrackObjectGetReleasePoint2StartEndFlag"));
					break;
				case eRelease2FromStart:
					if (!WriteBufferedUnsignedChar(Output,0))
						{
							return eFileLoadDiskError;
						}
					break;
				case eRelease2FromEnd:
					if (!WriteBufferedUnsignedChar(Output,1))
						{
							return eFileLoadDiskError;
						}
					break;
			}

		/*   4-byte little endian large integer coded decimal default overall loudness */
		if (!WriteBufferedSignedLongLittleEndian(Output,
			Double2LargeBCD(TrackObjectGetOverallLoudness(TrackObj))))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian large integer coded decimal default stereo positioning */
		if (!WriteBufferedSignedLongLittleEndian(Output,
			Double2LargeBCD(TrackObjectGetStereoPositioning(TrackObj))))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian large integer coded decimal default surround positioning */
		if (!WriteBufferedSignedLongLittleEndian(Output,
			Double2LargeBCD(TrackObjectGetSurroundPositioning(TrackObj))))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian large integer coded decimal default accent 1 */
		if (!WriteBufferedSignedLongLittleEndian(Output,
			Double2LargeBCD(TrackObjectGetAccent1(TrackObj))))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian large integer coded decimal default accent 2 */
		if (!WriteBufferedSignedLongLittleEndian(Output,
			Double2LargeBCD(TrackObjectGetAccent2(TrackObj))))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian large integer coded decimal default accent 3 */
		if (!WriteBufferedSignedLongLittleEndian(Output,
			Double2LargeBCD(TrackObjectGetAccent3(TrackObj))))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian large integer coded decimal default accent 4 */
		if (!WriteBufferedSignedLongLittleEndian(Output,
			Double2LargeBCD(TrackObjectGetAccent4(TrackObj))))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian large integer coded decimal default pitch disp depth adjust */
		if (!WriteBufferedSignedLongLittleEndian(Output,
			Double2LargeBCD(TrackObjectGetPitchDisplacementDepthAdjust(TrackObj))))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian large integer coded decimal default pitch disp rate adjust */
		if (!WriteBufferedSignedLongLittleEndian(Output,
			Double2LargeBCD(TrackObjectGetPitchDisplacementRateAdjust(TrackObj))))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian large integer coded decimal default pitch disp start point */
		if (!WriteBufferedSignedLongLittleEndian(Output,
			Double2LargeBCD(TrackObjectGetPitchDisplacementStartPoint(TrackObj))))
			{
				return eFileLoadDiskError;
			}

		/*   1-byte default pitch displacement start point mode flag */
		/*       0 = pitch displacement point from start */
		/*       1 = pitch displacement point from end */
		switch (TrackObjectGetPitchDisplacementFromStartOrEnd(TrackObj))
			{
				default:
					EXECUTE(PRERR(ForceAbort,"TrackObjectWriteDataOut:  bad value "
						"from TrackObjectGetPitchDisplacementFromStartOrEnd"));
					break;
				case ePitchDisplacementStartFromStart:
					if (!WriteBufferedUnsignedChar(Output,0))
						{
							return eFileLoadDiskError;
						}
					break;
				case ePitchDisplacementStartFromEnd:
					if (!WriteBufferedUnsignedChar(Output,1))
						{
							return eFileLoadDiskError;
						}
					break;
			}

		/*   4-byte little endian large integer coded decimal default hurry-up factor */
		if (!WriteBufferedSignedLongLittleEndian(Output,
			Double2LargeBCD(TrackObjectGetHurryUp(TrackObj))))
			{
				return eFileLoadDiskError;
			}

		/*   4-byte little endian large integer coded decimal default detuning */
		if (!WriteBufferedSignedLongLittleEndian(Output,
			Double2LargeBCD(TrackObjectGetDetune(TrackObj))))
			{
				return eFileLoadDiskError;
			}

		/*   1-byte default detuning mode flag */
		/*       0 = half steps */
		/*       1 = hertz */
		switch (TrackObjectGetDetuneControlFlag(TrackObj))
			{
				default:
					EXECUTE(PRERR(ForceAbort,"TrackObjectWriteDataOut:  bad value "
						"from TrackObjectGetDetuneControlFlag"));
					break;
				case eDetuningModeHalfSteps:
					if (!WriteBufferedUnsignedChar(Output,0))
						{
							return eFileLoadDiskError;
						}
					break;
				case eDetuningModeHertz:
					if (!WriteBufferedUnsignedChar(Output,1))
						{
							return eFileLoadDiskError;
						}
					break;
			}

		/*   4-byte little endian large integer coded decimal default duration */
		if (!WriteBufferedSignedLongLittleEndian(Output,
			Double2LargeBCD(TrackObjectGetDurationAdjust(TrackObj))))
			{
				return eFileLoadDiskError;
			}

		/*   1-byte default duration mode flag */
		/*       0 = duration adjust is multiplicative */
		/*       1 = duration adjust is additive */
		switch (TrackObjectGetDurationModeFlag(TrackObj))
			{
				default:
					EXECUTE(PRERR(ForceAbort,"TrackObjectWriteDataOut:  bad value "
						"from TrackObjectGetDurationModeFlag"));
					break;
				case eDurationAdjustMultiplicative:
					if (!WriteBufferedUnsignedChar(Output,0))
						{
							return eFileLoadDiskError;
						}
					break;
				case eDurationAdjustAdditive:
					if (!WriteBufferedUnsignedChar(Output,1))
						{
							return eFileLoadDiskError;
						}
					break;
			}

		/*   1-byte flag for playback inclusion */
		/*       0 = don't play track in final playback */
		/*       1 = do play track in final playback */
		if (TrackObjectShouldItBePlayed(TrackObj))
			{
				if (!WriteBufferedUnsignedChar(Output,1))
					{
						return eFileLoadDiskError;
					}
			}
		 else
			{
				if (!WriteBufferedUnsignedChar(Output,0))
					{
						return eFileLoadDiskError;
					}
			}

		/*   4-byte little endian instrument name string length descriptor */
		StringTemp = TrackObjectGetInstrName(TrackObj);
		if (StringTemp == NIL)
			{
				return eFileLoadOutOfMemory;
			}
		if (!WriteBufferedSignedLongLittleEndian(Output,PtrSize(StringTemp)))
			{
				ReleasePtr(StringTemp);
				return eFileLoadDiskError;
			}

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

		/*   1-byte flag for channel post processing enabling */
		/*       0 = don't do channel postprocessing */
		/*       1 = do channel postprocessing */
		if (TrackObj->PostProcessingEnable)
			{
				if (!WriteBufferedUnsignedChar(Output,1))
					{
						return eFileLoadDiskError;
					}
			}
		 else
			{
				if (!WriteBufferedUnsignedChar(Output,0))
					{
						return eFileLoadDiskError;
					}
			}

		/*   4-byte little endian postprocessing expression length descriptor */
		StringTemp = TrackObjectGetPostProcessing(TrackObj);
		if (StringTemp == NIL)
			{
				return eFileLoadOutOfMemory;
			}
		if (!WriteBufferedSignedLongLittleEndian(Output,PtrSize(StringTemp)))
			{
				ReleasePtr(StringTemp);
				return eFileLoadDiskError;
			}

		/*   n-bytes of postprocessing stuff (line feed = 0x0a) */
		if (!WriteBufferedOutput(Output,PtrSize(StringTemp),StringTemp))
			{
				ReleasePtr(StringTemp);
				return eFileLoadDiskError;
			}
		ReleasePtr(StringTemp);

		/*   n-bytes of data for note array */
		Error = WriteNoteVector(TrackObj->FrameArray,Output);
		if (Error != eFileLoadNoError)
			{
				return Error;
			}

		return eFileLoadNoError;
	}


/* mark track object as saved */
void									TrackObjectMarkAsSaved(TrackObjectRec* TrackObj)
	{
		CheckPtrExistence(TrackObj);
		/* there is no state data in the window */
		TrackObj->DataModified = False;
	}


/* make the track object write out the note array to the file */
MyBoolean							TrackObjectWriteNotesOutToFile(TrackObjectRec* TrackObj,
												struct BufferedOutputRec* Output)
	{
		CheckPtrExistence(TrackObj);
		CheckPtrExistence(Output);
		return (eFileLoadNoError == WriteNoteVector(TrackObj->FrameArray,Output));
	}


/* load notes from the file & replace track's notes with them */
MyBoolean							TrackObjectRecoverNotesFromFile(TrackObjectRec* TrackObj,
												struct BufferedInputRec* Input)
	{
		ArrayRec*						NewNoteVector EXECUTE(= (ArrayRec*)0x81818181);
		long								Limit;
		long								Scan;

		CheckPtrExistence(TrackObj);
		CheckPtrExistence(Input);
		/* get the new vector */
		if (eFileLoadNoError != ReadNoteVector(&NewNoteVector,Input))
			{
				return False;
			}
		CheckPtrExistence(NewNoteVector);
		/* delete the old one */
		Limit = ArrayGetLength(TrackObj->FrameArray);
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				DisposeFrameAndContents((FrameObjectRec*)ArrayGetElement(
					TrackObj->FrameArray,Scan));
			}
		DisposeArray(TrackObj->FrameArray);
		/* install the new one */
		TrackObj->FrameArray = NewNoteVector;
		TrackObjectAltered(TrackObj,0);
		return True;
	}
