/* SampleStorageDisplay.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 "SampleStorageDisplay.h"
#include "Memory.h"
#include "DataMunging.h"
#include "Scrap.h"
#include "Files.h"


#define MAGICSCRAPSTRING ("\xff\x00\x1f\xfe SampleStorageScrap")

typedef struct
	{
		char									MagicString[sizeof(MAGICSCRAPSTRING)];
		NumBitsType						NumberOfBits;
		NumChannelsType				NumberOfChannels;
		long									NumberOfBytes;
	} SampleClipboardRec;


struct SampleStorageDisplayRec
	{
		NumBitsType						NumberOfBits;
		NumChannelsType				MonoStereo;
		largefixedsigned*			Buffer;
		MyBoolean							DataHasChanged;

		MyBoolean							UndoFileIsValid;
		FileType*							TheUndoFile;
		FileSpec*							TheUndoFileLocation;
	};


/* create a zero-length sample storage object */
SampleStorageDisplayRec*	NewSampleStorageDisplayEmpty(NumBitsType NumBits,
														NumChannelsType NumChannels)
	{
		SampleStorageDisplayRec*	Storage;

		ERROR((NumBits != eSample16bit) && (NumBits != eSample8bit),
			PRERR(ForceAbort,"NewSampleStorageDisplayEmpty:  invalid value for NewNumBits"));
		ERROR((NumChannels != eSampleMono) && (NumChannels != eSampleStereo),
			PRERR(ForceAbort,"NewSampleStorageDisplayEmpty:  invalid number of channels"));
		Storage = (SampleStorageDisplayRec*)AllocPtrCanFail(
			sizeof(SampleStorageDisplayRec),"SampleStorageDisplayRec");
		if (Storage == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		Storage->Buffer = (largefixedsigned*)AllocPtrCanFail(0,"SampleDataArray");
		if (Storage->Buffer == NIL)
			{
			 FailurePoint2:
				ReleasePtr((char*)Storage);
				goto FailurePoint1;
			}
		Storage->NumberOfBits = NumBits;
		Storage->MonoStereo = NumChannels;
		Storage->DataHasChanged = False;
		Storage->UndoFileIsValid = False;
		return Storage;
	}


/* create a zero-length sample storage object */
SampleStorageDisplayRec*	NewSampleStorageDisplayData(NumBitsType NumBits,
														NumChannelsType NumChannels, largefixedsigned* DataBlock)
	{
		SampleStorageDisplayRec*	Storage;

		ERROR((NumBits != eSample16bit) && (NumBits != eSample8bit),
			PRERR(ForceAbort,"NewSampleStorageDisplayData:  invalid value for NewNumBits"));
		ERROR((NumChannels != eSampleMono) && (NumChannels != eSampleStereo),
			PRERR(ForceAbort,"NewSampleStorageDisplayData:  invalid number of channels"));
		CheckPtrExistence(DataBlock);
		Storage = (SampleStorageDisplayRec*)AllocPtrCanFail(
			sizeof(SampleStorageDisplayRec),"SampleStorageDisplayRec");
		if (Storage == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		Storage->Buffer = DataBlock;
		SetTag(DataBlock,"SampleDataArray");
		ERROR((NumChannels == eSampleStereo) && ((PtrSize((char*)DataBlock)
			% (2 * sizeof(largefixedsigned))) != 0),PRERR(ForceAbort,
			"NewSampleStorageDisplayData:  stereo sample not an even number of elements"));
		Storage->NumberOfBits = NumBits;
		Storage->MonoStereo = NumChannels;
		Storage->DataHasChanged = False;
		Storage->UndoFileIsValid = False;
		return Storage;
	}


/* dispose of a sample storage object */
void											DisposeSampleStorageDisplay(SampleStorageDisplayRec* Storage)
	{
		CheckPtrExistence(Storage);
		SampleStorageDisplayFlushUndo(Storage);
		ReleasePtr((char*)Storage->Buffer);
		ReleasePtr((char*)Storage);
	}


/* get the number of bits being represented in this sample */
NumBitsType								GetSampleStorageDisplayNumBits(
														SampleStorageDisplayRec* Storage)
	{
		CheckPtrExistence(Storage);
		return Storage->NumberOfBits;
	}


/* get the number of channels being represented in this sample */
NumChannelsType						GetSampleStorageDisplayNumChannels(
														SampleStorageDisplayRec* Storage)
	{
		CheckPtrExistence(Storage);
		return Storage->MonoStereo;
	}


/* get the number of frames stored in this object */
long											GetSampleStorageDisplayNumFrames(
														SampleStorageDisplayRec* Storage)
	{
		long										TheLengthThang;

		CheckPtrExistence(Storage);
		TheLengthThang = PtrSize((char*)Storage->Buffer);
		TheLengthThang = TheLengthThang / sizeof(largefixedsigned);
		if (Storage->MonoStereo == eSampleStereo)
			{
				TheLengthThang = TheLengthThang / 2;
			}
		return TheLengthThang;
	}


/* get a value from in the array */
largefixedsigned					GetSampleStorageDisplayValue(SampleStorageDisplayRec* Storage,
														long Index, ChannelType WhichChannel)
	{
		largefixedsigned				ReturnValue;

		CheckPtrExistence(Storage);
		ERROR((Index < 0) || (Index >= GetSampleStorageDisplayNumFrames(Storage)),
			PRERR(ForceAbort,"GetSampleStorageDisplayValue:  index out of range"));
		switch (Storage->MonoStereo)
			{
				default:
					EXECUTE(PRERR(ForceAbort,
						"GetSampleStorageDisplayValue:  bad internal number of channels"));
					break;
				case eSampleStereo:
					switch (WhichChannel)
						{
							default:
								EXECUTE(PRERR(ForceAbort,
								"GetSampleStorageDisplayValue:  bad channel selector"));
								break;
							case eLeftChannel:
								PRNGCHK(Storage->Buffer,&(Storage->Buffer[Index * 2]),
									sizeof(largefixedsigned));
								ReturnValue = Storage->Buffer[Index * 2];
								break;
							case eRightChannel:
								PRNGCHK(Storage->Buffer,&(Storage->Buffer[(Index * 2) + 1]),
									sizeof(largefixedsigned));
								ReturnValue = Storage->Buffer[(Index * 2) + 1];
								break;
						}
					break;
				case eSampleMono:
					switch (WhichChannel)
						{
							default:
								EXECUTE(PRERR(ForceAbort,
								"GetSampleStorageDisplayValue:  bad channel selector"));
								break;
							case eMonoChannel:
								PRNGCHK(Storage->Buffer,&(Storage->Buffer[Index]),
									sizeof(largefixedsigned));
								ReturnValue = Storage->Buffer[Index];
								break;
						}
					break;
			}
		return ReturnValue;
	}


/* find out if the array has been changed since last being saved */
MyBoolean									DoesSampleStorageDisplayNeedToBeSaved(
														SampleStorageDisplayRec* Storage)
	{
		CheckPtrExistence(Storage);
		return Storage->DataHasChanged;
	}


/* change the number of bits being used to store the data */
void											SetSampleStorageDisplayNumBits(
														SampleStorageDisplayRec* Storage, NumBitsType NewNumBits)
	{
		CheckPtrExistence(Storage);
		ERROR((NewNumBits != eSample16bit) && (NewNumBits != eSample8bit),
			PRERR(ForceAbort,"SetSampleStorageDisplayNumBits:  invalid value for NewNumBits"));
		Storage->NumberOfBits = NewNumBits;
		Storage->DataHasChanged = True;
	}


/* change the number of channels being stored in the array */
MyBoolean									SetSampleStorageDisplayNumChannels(
														SampleStorageDisplayRec* Storage,
														NumChannelsType NewNumChannels)
	{
		CheckPtrExistence(Storage);
		ERROR((NewNumChannels != eSampleMono) && (NewNumChannels != eSampleStereo),
			PRERR(ForceAbort,"SetSampleStorageDisplayNumChannels:  invalid number of channels"));
		if (Storage->MonoStereo != NewNumChannels)
			{
				largefixedsigned*				NewBuffer;
				long										Scan;
				long										NumSampleFrames;

				NumSampleFrames = GetSampleStorageDisplayNumFrames(Storage);
				if (NewNumChannels == eSampleStereo)
					{
						/* convert mono to stereo */
						NewBuffer = (largefixedsigned*)AllocPtrCanFail(sizeof(largefixedsigned)
							* NumSampleFrames * 2,"SampleStorageDisplay Data");
						if (NewBuffer == NIL)
							{
								return False;
							}
						for (Scan = 0; Scan < NumSampleFrames; Scan += 1)
							{
								PRNGCHK(NewBuffer,&(NewBuffer[2 * Scan + 0]),
									sizeof(NewBuffer[2 * Scan + 0]));
								PRNGCHK(Storage->Buffer,&(Storage->Buffer[Scan]),
									sizeof(Storage->Buffer[Scan]));
								NewBuffer[2 * Scan + 0] = Storage->Buffer[Scan];
								PRNGCHK(NewBuffer,&(NewBuffer[2 * Scan + 1]),
									sizeof(NewBuffer[2 * Scan + 1]));
								NewBuffer[2 * Scan + 1] = Storage->Buffer[Scan];
							}
					}
				 else
					{
						/* convert stereo to mono */
						NewBuffer = (largefixedsigned*)AllocPtrCanFail(sizeof(largefixedsigned)
							* NumSampleFrames,"SampleStorageDisplay Data");
						if (NewBuffer == NIL)
							{
								return False;
							}
						for (Scan = 0; Scan < NumSampleFrames; Scan += 1)
							{
								PRNGCHK(NewBuffer,&(NewBuffer[Scan]),sizeof(NewBuffer[Scan]));
								PRNGCHK(Storage->Buffer,&(Storage->Buffer[2 * Scan + 0]),
									sizeof(Storage->Buffer[2 * Scan + 0]));
								PRNGCHK(Storage->Buffer,&(Storage->Buffer[2 * Scan + 1]),
									sizeof(Storage->Buffer[2 * Scan + 1]));
								NewBuffer[Scan] = (Storage->Buffer[2 * Scan + 0]
									+ Storage->Buffer[2 * Scan + 1]) / 2;
							}
					}
				ReleasePtr((char*)Storage->Buffer);
				Storage->Buffer = NewBuffer;
				Storage->MonoStereo = NewNumChannels;
				Storage->DataHasChanged = True;
			}
		return True;
	}


/* change the value of one of the sample points */
void											SetSampleStorageDisplayValue(SampleStorageDisplayRec* Storage,
														long Index, ChannelType WhichChannel,
														largefixedsigned NewValue)
	{
		CheckPtrExistence(Storage);
		ERROR((Index < 0) || (Index >= GetSampleStorageDisplayNumFrames(Storage)),
			PRERR(ForceAbort,"SetSampleStorageDisplayValue:  index out of range"));
		Storage->DataHasChanged = True;
		switch (Storage->MonoStereo)
			{
				default:
					EXECUTE(PRERR(ForceAbort,
						"GetSampleStorageDisplayValue:  bad internal number of channels"));
					break;
				case eSampleStereo:
					switch (WhichChannel)
						{
							default:
								EXECUTE(PRERR(ForceAbort,
								"GetSampleStorageDisplayValue:  bad channel selector"));
								break;
							case eLeftChannel:
								PRNGCHK(Storage->Buffer,&(Storage->Buffer[Index * 2]),
									sizeof(largefixedsigned));
								Storage->Buffer[Index * 2] = NewValue;
								break;
							case eRightChannel:
								PRNGCHK(Storage->Buffer,&(Storage->Buffer[(Index * 2) + 1]),
									sizeof(largefixedsigned));
								Storage->Buffer[(Index * 2) + 1] = NewValue;
								break;
						}
					break;
				case eSampleMono:
					switch (WhichChannel)
						{
							default:
								EXECUTE(PRERR(ForceAbort,
								"GetSampleStorageDisplayValue:  bad channel selector"));
								break;
							case eMonoChannel:
								PRNGCHK(Storage->Buffer,&(Storage->Buffer[Index]),
									sizeof(largefixedsigned));
								Storage->Buffer[Index] = NewValue;
								break;
						}
					break;
			}
	}


/* indicate that the object has been saved */
void											SampleStorageDisplayHasBeenSaved(
														SampleStorageDisplayRec* Storage)
	{
		CheckPtrExistence(Storage);
		Storage->DataHasChanged = False;
	}


/* insert a bunch of zeros into the array */
MyBoolean									InsertSampleStorageDisplayArea(
														SampleStorageDisplayRec* Storage, long Index,
														long NumFramesToInsert)
	{
		long										Start;
		long										NumBytes;
		long										OldSize;
		char*										NewBuffer;
		long										Scan;

		CheckPtrExistence(Storage);
		ERROR((Index < 0) || (Index >/*NB*/ GetSampleStorageDisplayNumFrames(Storage)),
			PRERR(ForceAbort,"InsertSampleStorageDisplayArea:  index out of range"));
		ERROR(NumFramesToInsert < 0,PRERR(ForceAbort,
			"InsertSampleStorageDisplayArea:  number of frames is negative"));
		Storage->DataHasChanged = True;
		OldSize = PtrSize((char*)Storage->Buffer);
		Start = Index * sizeof(largefixedsigned);
		NumBytes = NumFramesToInsert * sizeof(largefixedsigned);
		if (Storage->MonoStereo == eSampleStereo)
			{
				NumBytes *= 2;
				Start *= 2;
			}
		NewBuffer = ResizePtr((char*)Storage->Buffer,OldSize + NumBytes);
		if (NewBuffer == NIL)
			{
				return False;
			}
		Storage->Buffer = (largefixedsigned*)NewBuffer;
		PRNGCHK(NewBuffer,&(NewBuffer[Start]),OldSize - Start);
		PRNGCHK(NewBuffer,&(NewBuffer[Start + NumBytes]),OldSize - Start);
		MoveData(&(NewBuffer[Start]),&(NewBuffer[Start + NumBytes]),OldSize - Start);
		for (Scan = Start; Scan < Start + NumBytes; Scan += 1)
			{
				NewBuffer[Scan] = 0;
			}
		return True;
	}


/* delete a range of values from the array */
MyBoolean									DeleteSampleStorageDisplayArea(
														SampleStorageDisplayRec* Storage, long Index,
														long NumFramesToDelete)
	{
		long										Start;
		long										NumBytes;
		long										OldSize;
		char*										NewBuffer;

		CheckPtrExistence(Storage);
		ERROR((Index < 0) || (Index + NumFramesToDelete
			> GetSampleStorageDisplayNumFrames(Storage)),
			PRERR(ForceAbort,"DeleteSampleStorageDisplayArea:  index out of range"));
		ERROR(NumFramesToDelete < 0,PRERR(ForceAbort,
			"DeleteSampleStorageDisplayArea:  number of frames is negative"));
		Storage->DataHasChanged = True;
		OldSize = PtrSize((char*)Storage->Buffer);
		Start = Index * sizeof(largefixedsigned);
		NumBytes = NumFramesToDelete * sizeof(largefixedsigned);
		if (Storage->MonoStereo == eSampleStereo)
			{
				NumBytes *= 2;
				Start *= 2;
			}
		NewBuffer = RemoveBlockFromBlockCopy((char*)Storage->Buffer,Start,NumBytes);
		if (NewBuffer == NIL)
			{
				return False;
			}
		ReleasePtr((char*)Storage->Buffer);
		Storage->Buffer = (largefixedsigned*)NewBuffer;
		return True;
	}


/* extract part of the sample & create another sample record containing it */
SampleStorageDisplayRec*	ExtractSampleStorageDisplaySection(
														SampleStorageDisplayRec* Storage, long Index,
														long NumFramesToExtract)
	{
		SampleStorageDisplayRec*	NewStorage;
		long											Start;
		long											NumBytes;
		char*											NewBuffer;

		CheckPtrExistence(Storage);
		ERROR(((Index < 0) || (Index + NumFramesToExtract
			> GetSampleStorageDisplayNumFrames(Storage))),
			PRERR(ForceAbort,"ExtractSampleStorageDisplaySection:  index out of range"));
		ERROR(NumFramesToExtract < 0,PRERR(ForceAbort,
			"ExtractSampleStorageDisplaySection:  number of frames is negative"));
		Start = Index * sizeof(largefixedsigned);
		NumBytes = NumFramesToExtract * sizeof(largefixedsigned);
		if (Storage->MonoStereo == eSampleStereo)
			{
				NumBytes *= 2;
				Start *= 2;
			}
		NewBuffer = AllocPtrCanFail(NumBytes,"SampleDataBuffer");
		if (NewBuffer != NIL)
			{
				PRNGCHK(Storage->Buffer,&(((char*)Storage->Buffer)[Start]),NumBytes);
				PRNGCHK(NewBuffer,&(NewBuffer[0]),NumBytes);
				CopyData(&(((char*)Storage->Buffer)[Start]),&(NewBuffer[0]),NumBytes);
				NewStorage = NewSampleStorageDisplayData(Storage->NumberOfBits,
					Storage->MonoStereo,(largefixedsigned*)NewBuffer);
				if (NewStorage == NIL)
					{
						ReleasePtr(NewBuffer);
						NewStorage = NIL;
					}
			}
		return NewStorage;
	}


/* insert one sample into another at a certain point */
MyBoolean									InsertSampleStorageDisplaySection(
														SampleStorageDisplayRec* Storage, long Index,
														SampleStorageDisplayRec* DataToInsert)
	{
		long										NumFramesToInsert;
		NumChannelsType					TheirNumChannels;
		NumChannelsType					OurNumChannels;

		CheckPtrExistence(Storage);
		ERROR((Index < 0) || (Index > GetSampleStorageDisplayNumFrames(Storage)),
			PRERR(ForceAbort,"InsertSampleStorageDisplaySection:  index is out of range"));
		Storage->DataHasChanged = True;
		NumFramesToInsert = GetSampleStorageDisplayNumFrames(DataToInsert);
		TheirNumChannels = GetSampleStorageDisplayNumChannels(DataToInsert);
		OurNumChannels = GetSampleStorageDisplayNumChannels(Storage);
		if (InsertSampleStorageDisplayArea(Storage,Index,NumFramesToInsert))
			{
				long						Scan;

				if (TheirNumChannels == eSampleStereo)
					{
						if (OurNumChannels == eSampleStereo)
							{
								/* them stereo and us stereo */
								for (Scan = 0; Scan < NumFramesToInsert; Scan += 1)
									{
										SetSampleStorageDisplayValue(Storage,Scan + Index,eLeftChannel,
											GetSampleStorageDisplayValue(DataToInsert,Scan,eLeftChannel));
										SetSampleStorageDisplayValue(Storage,Scan + Index,eRightChannel,
											GetSampleStorageDisplayValue(DataToInsert,Scan,eRightChannel));
									}
							}
						 else
							{
								/* them stereo and us mono; average their samples as we insert */
								for (Scan = 0; Scan < NumFramesToInsert; Scan += 1)
									{
										SetSampleStorageDisplayValue(Storage,Scan + Index,eMonoChannel,
											(GetSampleStorageDisplayValue(DataToInsert,Scan,eLeftChannel)
											+ GetSampleStorageDisplayValue(DataToInsert,Scan,eRightChannel))
											/ 2);
									}
							}
					}
				 else
					{
						if (OurNumChannels == eSampleStereo)
							{
								/* them mono and us stereo; duplicate their samples */
								for (Scan = 0; Scan < NumFramesToInsert; Scan += 1)
									{
										SetSampleStorageDisplayValue(Storage,Scan + Index,eLeftChannel,
											GetSampleStorageDisplayValue(DataToInsert,Scan,eMonoChannel));
										SetSampleStorageDisplayValue(Storage,Scan + Index,eRightChannel,
											GetSampleStorageDisplayValue(DataToInsert,Scan,eMonoChannel));
									}
							}
						 else
							{
								/* them mono and us mono */
								for (Scan = 0; Scan < NumFramesToInsert; Scan += 1)
									{
										SetSampleStorageDisplayValue(Storage,Scan + Index,eMonoChannel,
											GetSampleStorageDisplayValue(DataToInsert,Scan,eMonoChannel));
									}
							}
					}
				return True;
			}
		 else
			{
				return False;
			}
	}


/* dump the contents of the object and install a copy of the contents of another */
MyBoolean									SampleStorageDisplaySetContents(
														SampleStorageDisplayRec* Storage,
														SampleStorageDisplayRec* NewData)
	{
		char*										Copy;

		CheckPtrExistence(Storage);
		CheckPtrExistence(NewData);
		Copy = CopyPtr((char*)NewData->Buffer);
		if (Copy == NIL)
			{
				return False;
			}
		ReleasePtr((char*)Storage->Buffer);
		Storage->Buffer = (largefixedsigned*)Copy;
		Storage->NumberOfBits = NewData->NumberOfBits;
		Storage->MonoStereo = NewData->MonoStereo;
		Storage->DataHasChanged = True;
		return True;
	}


/* put the raw data for the sample onto the clipboard */
void											PutSampleStorageDisplayOnClipboard(
														SampleStorageDisplayRec* Storage)
	{
		char*										Buffer;

		CheckPtrExistence(Storage);
		Buffer = AllocPtrCanFail(sizeof(SampleClipboardRec)
			+ PtrSize((char*)Storage->Buffer),"SampleScrap");
		if (Buffer == NIL)
			{
				return;
			}
		CopyData(MAGICSCRAPSTRING,&(((SampleClipboardRec*)Buffer)->MagicString[0]),
			sizeof(MAGICSCRAPSTRING));
		((SampleClipboardRec*)Buffer)->NumberOfBits = Storage->NumberOfBits;
		((SampleClipboardRec*)Buffer)->NumberOfChannels = Storage->MonoStereo;
		((SampleClipboardRec*)Buffer)->NumberOfBytes = PtrSize((char*)Storage->Buffer);
		CopyData((char*)Storage->Buffer,(char*)Buffer + sizeof(SampleClipboardRec),
			PtrSize((char*)Storage->Buffer));
		SetScrapToThis(Buffer);
		ReleasePtr(Buffer);
	}


/* get a sample off the clipboard.  returns NIL if the scrap isn't a sample */
SampleStorageDisplayRec*	GetSampleStorageDisplayFromClipboard(void)
	{
		char*										Buffer;
		SampleStorageDisplayRec*	Storage;
		long										Scan;
		char*										BufferResized;
		NumBitsType							NumBits;
		NumChannelsType					NumChannels;

		Buffer = GetCopyOfScrap();
		if (Buffer == NIL)
			{
				return NIL;
			}
		if (PtrSize(Buffer) < sizeof(SampleClipboardRec))
			{
			 NotEnoughMemory:
			 NotOurScrap:
				ReleasePtr(Buffer);
				return NIL;
			}
		for (Scan = 0; Scan < sizeof(MAGICSCRAPSTRING); Scan += 1)
			{
				if (MAGICSCRAPSTRING[Scan] != ((SampleClipboardRec*)Buffer)->MagicString[Scan])
					{
						goto NotOurScrap;
					}
			}
		if (sizeof(SampleClipboardRec) + ((SampleClipboardRec*)Buffer)->NumberOfBytes
			!= PtrSize(Buffer))
			{
				goto NotOurScrap;
			}
		if ((((SampleClipboardRec*)Buffer)->NumberOfBits != eSample8bit)
			&& (((SampleClipboardRec*)Buffer)->NumberOfBits != eSample16bit))
			{
				goto NotOurScrap;
			}
		if ((((SampleClipboardRec*)Buffer)->NumberOfChannels != eSampleMono)
			&& (((SampleClipboardRec*)Buffer)->NumberOfChannels != eSampleStereo))
			{
				goto NotOurScrap;
			}
		NumBits = ((SampleClipboardRec*)Buffer)->NumberOfBits;
		NumChannels = ((SampleClipboardRec*)Buffer)->NumberOfChannels;
		MoveData(Buffer + sizeof(SampleClipboardRec),Buffer,
			PtrSize(Buffer) - sizeof(SampleClipboardRec));
		BufferResized = ResizePtr(Buffer,PtrSize(Buffer) - sizeof(SampleClipboardRec));
		if (BufferResized == NIL)
			{
				goto NotEnoughMemory;
			}
		Storage = NewSampleStorageDisplayData(NumBits,NumChannels,
			(largefixedsigned*)BufferResized);
		if (Storage == NIL)
			{
				ReleasePtr(BufferResized);
				return NIL;
			}
		return Storage;
	}


/* get a copy of a particular channel of data from the sample */
largefixedsigned*					SampleStorageDisplayGetChannelFixed(
														SampleStorageDisplayRec* Storage, ChannelType WhichChannel)
	{
		largefixedsigned*				Data;
		long										Limit;

		CheckPtrExistence(Storage);
		Limit = GetSampleStorageDisplayNumFrames(Storage);
		Data = (largefixedsigned*)AllocPtrCanFail(sizeof(largefixedsigned)
			* Limit,"SampleFixed");
		if (Data != NIL)
			{
				long								Scan;

				for (Scan = 0; Scan < Limit; Scan += 1)
					{
						Data[Scan] = GetSampleStorageDisplayValue(Storage,Scan,WhichChannel);
					}
			}
		return Data;
	}


/* put a new block of data into the sample object.  a copy of the block is made */
MyBoolean									SampleStorageDisplayPutMonoFixed(
														SampleStorageDisplayRec* Storage, largefixedsigned* Data)
	{
		long										Difference;
		long										Scan;
		long										Limit;

		CheckPtrExistence(Storage);
		CheckPtrExistence(Data);
		ERROR(PtrSize((char*)Data) % sizeof(largefixedsigned) != 0,PRERR(ForceAbort,
			"SampleStorageDisplayPutMonoFixed:  array alignment error"));
		Limit = PtrSize((char*)Data) / sizeof(largefixedsigned);
		Difference = Limit - GetSampleStorageDisplayNumFrames(Storage);
		if (Difference > 0)
			{
				/* new data is bigger, so we insert the difference */
				if (!InsertSampleStorageDisplayArea(Storage,
					GetSampleStorageDisplayNumFrames(Storage),Difference))
					{
						return False;
					}
			}
		else if (Difference < 0)
			{
				/* new data is smaller, so we delete the difference */
				Difference = - Difference;
				if (!DeleteSampleStorageDisplayArea(Storage,
					GetSampleStorageDisplayNumFrames(Storage) - Difference,Difference))
					{
						return False;
					}
			}
		/* else don't do anything */
		ERROR(GetSampleStorageDisplayNumFrames(Storage) != Limit,PRERR(ForceAbort,
			"SampleStorageDisplayPutMonoFixed:  storage size inconsistency"));
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				SetSampleStorageDisplayValue(Storage,Scan,eMonoChannel,Data[Scan]);
			}
		return True;
	}


/* put two new blocks of data into a stereo sample object.  copies are made */
MyBoolean									SampleStorageDisplayPutStereoFixed(
														SampleStorageDisplayRec* Storage, largefixedsigned* Left,
														largefixedsigned* Right)
	{
		long										Difference;
		long										Scan;
		long										Limit;

		CheckPtrExistence(Storage);
		ERROR(PtrSize((char*)Left) != PtrSize((char*)Right),PRERR(ForceAbort,
			"SampleStoragePutStereoFixed:  Left and Right sizes are not equal"));
		ERROR(PtrSize((char*)Left) % sizeof(largefixedsigned) != 0,PRERR(ForceAbort,
			"SampleStoragePutStereoFixed:  array alignment error"));
		Limit = PtrSize((char*)Left) / sizeof(largefixedsigned);
		Difference = Limit - GetSampleStorageDisplayNumFrames(Storage);
		if (Difference > 0)
			{
				/* new data is bigger, so we insert the difference */
				if (!InsertSampleStorageDisplayArea(Storage,
					GetSampleStorageDisplayNumFrames(Storage),Difference))
					{
						return False;
					}
			}
		else if (Difference < 0)
			{
				/* new data is smaller, so we delete the difference */
				Difference = - Difference;
				if (!DeleteSampleStorageDisplayArea(Storage,
					GetSampleStorageDisplayNumFrames(Storage) - Difference,Difference))
					{
						return False;
					}
			}
		/* else don't do anything */
		ERROR(GetSampleStorageDisplayNumFrames(Storage) != Limit,PRERR(ForceAbort,
			"SampleStoragePutStereoFixed:  storage size inconsistency"));
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				SetSampleStorageDisplayValue(Storage,Scan,eLeftChannel,Left[Scan]);
				SetSampleStorageDisplayValue(Storage,Scan,eRightChannel,Right[Scan]);
			}
		return True;
	}


/* flush any undo information */
void											SampleStorageDisplayFlushUndo(SampleStorageDisplayRec* Storage)
	{
		CheckPtrExistence(Storage);
		if (Storage->UndoFileIsValid)
			{
				CloseFile(Storage->TheUndoFile);
				DeleteFile(Storage->TheUndoFileLocation);
				DisposeFileSpec(Storage->TheUndoFileLocation);
				Storage->UndoFileIsValid = False;
			}
	}


/* save the object to a temporary file so that we can undo any changes. */
/* if it couldn't be done, then it returns False */
MyBoolean									SampleStorageDisplaySetupForUndo(SampleStorageDisplayRec* Storage)
	{
		long										BufferLength;

		CheckPtrExistence(Storage);
		SampleStorageDisplayFlushUndo(Storage);

		/* try to obtain a temporary file */
		Storage->TheUndoFileLocation = NewTempFileSpec(CODE4BYTES('?','?','?','?'),
			CODE4BYTES('?','?','?','?'));
		if (Storage->TheUndoFileLocation == NIL)
			{
				/* couldn't be done */
			 FailurePoint1:
				return False;
			}

		/* try to open it */
		if (!OpenFile(Storage->TheUndoFileLocation,&(Storage->TheUndoFile),eReadAndWrite))
			{
			 FailurePoint2:
				DeleteFile(Storage->TheUndoFileLocation);
				DisposeFileSpec(Storage->TheUndoFileLocation);
				goto FailurePoint2;
			}

		/* save data */
		if (0 != WriteToFile(Storage->TheUndoFile,(char*)&(Storage->NumberOfBits),
			sizeof(Storage->NumberOfBits)))
			{
			 FailurePoint3:
				CloseFile(Storage->TheUndoFile);
				goto FailurePoint2;
			}
		if (0 != WriteToFile(Storage->TheUndoFile,(char*)&(Storage->MonoStereo),
			sizeof(Storage->MonoStereo)))
			{
			 FailurePoint4:
				goto FailurePoint3;
			}
		BufferLength = PtrSize((char*)Storage->Buffer);
		if (0 != WriteToFile(Storage->TheUndoFile,(char*)&BufferLength,sizeof(long)))
			{
			 FailurePoint5:
				goto FailurePoint4;
			}
		if (0 != WriteToFile(Storage->TheUndoFile,(char*)Storage->Buffer,BufferLength))
			{
			 FailurePoint6:
				goto FailurePoint5;
			}

		/* wow, we made it through, so we get to set this little flag thingie here! */
		Storage->UndoFileIsValid = True;

		return True;
	}


/* find out if we can undo something */
MyBoolean									SampleStorageDisplayUndoAvailable(SampleStorageDisplayRec* Storage)
	{
		CheckPtrExistence(Storage);
		return Storage->UndoFileIsValid;
	}


/* try to undo something.  returns True if the undo data was successfully recovered. */
/* the current state is swapped with the recovered state */
MyBoolean									SampleStorageDisplayUndo(SampleStorageDisplayRec* Storage)
	{
		MyBoolean								OldUndoIsValid;
		FileType*								OldUndoFile;
		FileSpec*								OldUndoFileLocation;

		NumBitsType							LocalNumberOfBits;
		NumChannelsType					LocalMonoStereo;
		long										NumberOfBytes;
		largefixedsigned*				LocalBuffer;


		CheckPtrExistence(Storage);
		ERROR(!Storage->UndoFileIsValid,PRERR(ForceAbort,
			"SampleStorageDisplayUndo:  there is no saved undo information"));

		/* save old undo information so we can recover stuff */
		OldUndoIsValid = Storage->UndoFileIsValid;
		OldUndoFile = Storage->TheUndoFile;
		OldUndoFileLocation = Storage->TheUndoFileLocation;

		/* save current state for undoing */
		Storage->UndoFileIsValid = False; /* DON'T discard previous information */
		if (!SampleStorageDisplaySetupForUndo(Storage))
			{
				/* failed -- restore state & return */
			 FailurePoint1:
				Storage->UndoFileIsValid = OldUndoIsValid;
				Storage->TheUndoFile = OldUndoFile;
				Storage->TheUndoFileLocation = OldUndoFileLocation;
				return False;
			}

		/* now try to build new information for us */
		if (!SetFilePosition(OldUndoFile,0))
			{
			 FailurePoint2:
				goto FailurePoint1;
			}

		/* read in cute informations */
		if (0 != ReadFromFile(OldUndoFile,(char*)&LocalNumberOfBits,sizeof(LocalNumberOfBits)))
			{
			 FailurePoint3:
				goto FailurePoint2;
			}
		if (0 != ReadFromFile(OldUndoFile,(char*)&LocalMonoStereo,sizeof(LocalMonoStereo)))
			{
			 FailurePoint4:
				goto FailurePoint3;
			}
		if (0 != ReadFromFile(OldUndoFile,(char*)&NumberOfBytes,sizeof(long)))
			{
			 FailurePoint5:
				goto FailurePoint4;
			}
		LocalBuffer = (largefixedsigned*)AllocPtrCanFail(NumberOfBytes,"SampleDataArray");
		if (LocalBuffer == NIL)
			{
			 FailurePoint6:
				goto FailurePoint5;
			}
		if (0 != ReadFromFile(OldUndoFile,(char*)LocalBuffer,NumberOfBytes))
			{
			 FailurePoint7:
				ReleasePtr((char*)LocalBuffer);
				goto FailurePoint6;
			}

		/* now we've successfully read all of the data in.  */
		/* we can replace current data with read in data & discard local undo stuff */

		/* dispose old undo information */
		CloseFile(OldUndoFile);
		DeleteFile(OldUndoFileLocation);
		DisposeFileSpec(OldUndoFileLocation);

		/* dump current information */
		Storage->NumberOfBits = LocalNumberOfBits;
		Storage->MonoStereo = LocalMonoStereo;
		ReleasePtr((char*)Storage->Buffer);
		Storage->Buffer = LocalBuffer;

		Storage->DataHasChanged = True;

		return True;
	}


/* get the actual raw data in the channel */
largefixedsigned*					SampleStorageDisplayGetActualData(SampleStorageDisplayRec* Storage)
	{
		CheckPtrExistence(Storage);
		return Storage->Buffer;
	}
