/* WaveTableWindow.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 "WaveTableWindow.h"
#include "MainWindowStuff.h"
#include "CodeCenter.h"
#include "WaveTableList.h"
#include "TextEdit.h"
#include "IconButton.h"
#include "Scroll.h"
#include "SimpleButton.h"
#include "SampleView.h"
#include "WindowDispatcher.h"
#include "WaveTableObject.h"
#include "Memory.h"
#include "Numbers.h"
#include "Array.h"
#include "DataMunging.h"
#include "EditImages.h"
#include "GrowIcon.h"
#include "Main.h"
#include "StringDialog.h"
#include "FindDialog.h"
#include "GlobalWindowMenuList.h"
#include "WaveTableStorage.h"
#include "WaveTableStorageDisplay.h"
#include "SoundOutput.h"
#include "Alert.h"
#include "NumberDialog.h"
#include "WaveTableSizeDialog.h"
#include "CompilerRoot.h"
#include "PcodeObject.h"
#include "PcodeStack.h"
#include "PcodeSystem.h"
#include "FunctionCode.h"


#define PAGEINCREMENT (1)


#define EIGHTBITSHIFTFACTOR (18)
#define SIXTEENBITSHIFTFACTOR (10)


#define WINDOWXSIZE (420)
#define WINDOWYSIZE (300)

#define NAMEEDITX (80)
#define NAMEEDITY (1)
#define NAMEEDITWIDTH (80)
#define NAMEEDITHEIGHT (19)

#define NAMEX (3)
#define NAMEY (NAMEEDITY + 3)

#define FRAMEEDITX (NAMEEDITX)
#define FRAMEEDITY (NAMEEDITY + NAMEEDITHEIGHT + 3)
#define FRAMEEDITWIDTH (NAMEEDITWIDTH)
#define FRAMEEDITHEIGHT (NAMEEDITHEIGHT)

#define FRAMEX (NAMEX)
#define FRAMEY (FRAMEEDITY + 3)

#define TABLESEDITX (FRAMEEDITX)
#define TABLESEDITY (FRAMEEDITY + FRAMEEDITHEIGHT + 3)
#define TABLESEDITWIDTH (FRAMEEDITWIDTH)
#define TABLESEDITHEIGHT (FRAMEEDITHEIGHT)

#define TABLESX (FRAMEX)
#define TABLESY (TABLESEDITY + 3)

#define BITS8BUTTONX (10)
#define BITS8BUTTONY (TABLESEDITY + TABLESEDITHEIGHT + 5)
#define BITS8BUTTONWIDTH (32)
#define BITS8BUTTONHEIGHT (32)

#define BITS16BUTTONX (BITS8BUTTONX + BITS8BUTTONWIDTH + 1)
#define BITS16BUTTONY (BITS8BUTTONY)
#define BITS16BUTTONWIDTH (BITS8BUTTONWIDTH)
#define BITS16BUTTONHEIGHT (BITS8BUTTONHEIGHT)

#define TESTATTACKDURATIONX (TABLESEDITX)
#define TESTATTACKDURATIONY (BITS16BUTTONY + BITS16BUTTONHEIGHT + 5)
#define TESTATTACKDURATIONWIDTH (TABLESEDITWIDTH)
#define TESTATTACKDURATIONHEIGHT (TABLESEDITHEIGHT)

#define TESTATTACKX (TABLESX)
#define TESTATTACKY (TESTATTACKDURATIONY + 3)

#define TESTDECAYDURATIONX (TESTATTACKDURATIONX)
#define TESTDECAYDURATIONY (TESTATTACKDURATIONY + TESTATTACKDURATIONHEIGHT + 3)
#define TESTDECAYDURATIONWIDTH (TESTATTACKDURATIONWIDTH)
#define TESTDECAYDURATIONHEIGHT (TESTATTACKDURATIONHEIGHT)

#define TESTDECAYX (TESTATTACKX)
#define TESTDECAYY (TESTDECAYDURATIONY + 3)

#define TESTFREQUENCYX (TESTDECAYDURATIONX)
#define TESTFREQUENCYY (TESTDECAYDURATIONY + TESTDECAYDURATIONHEIGHT + 3)
#define TESTFREQUENCYWIDTH (TESTDECAYDURATIONWIDTH)
#define TESTFREQUENCYHEIGHT (TESTDECAYDURATIONHEIGHT)

#define TESTFREQX (TESTDECAYX)
#define TESTFREQY (TESTFREQUENCYY + 3)

#define TESTSAMPLINGRATEX (TESTFREQUENCYX)
#define TESTSAMPLINGRATEY (TESTFREQUENCYY + TESTFREQUENCYHEIGHT + 3)
#define TESTSAMPLINGRATEWIDTH (TESTFREQUENCYWIDTH)
#define TESTSAMPLINGRATEHEIGHT (TESTFREQUENCYHEIGHT)

#define TESTSAMPLX (TESTFREQX)
#define TESTSAMPLY (TESTSAMPLINGRATEY + 3)

#define TESTBUTTONX (10)
#define TESTBUTTONY (TESTSAMPLINGRATEY + TESTSAMPLINGRATEHEIGHT + 5)
#define TESTBUTTONWIDTH (80)
#define TESTBUTTONHEIGHT (21)

#define EXPRESSIONEDITX (-1)
#define EXPRESSIONEDITWIDTH(WindowWidth) ((WindowWidth) + 2)
#define EXPRESSIONEDITHEIGHT (80)
#define EXPRESSIONEDITY(WindowHeight) ((WindowHeight) - EXPRESSIONEDITHEIGHT + 1)

#define WAVEFORMVIEWX (NAMEEDITX + NAMEEDITWIDTH + 10)
#define WAVEFORMVIEWY (-1)
#define WAVEFORMVIEWWIDTH(WinWidth) ((WinWidth) - WAVEFORMVIEWX + 1)
#define WAVEFORMVIEWHEIGHT(WinHeight) (EXPRESSIONEDITY(WinHeight) - WAVEFORMVIEWY - 16)

#define TABLESCROLLY(WinHeight) (WAVEFORMVIEWY + WAVEFORMVIEWHEIGHT(WinHeight) - 1)


struct WaveTableWindowRec
	{
		MainWindowRec*			MainWindow;
		CodeCenterRec*			CodeCenter;
		WaveTableListRec*		WaveTableList;
		WaveTableObjectRec*	WaveTableObject;

		WaveTableStorDispRec*	WaveTableData;
		MyBoolean						WaveDataModified;
		WaveTableStorDispRec*	UndoBackupWaveTable;

		WinType*						ScreenID;
		TextEditRec*				NameEdit;
		TextEditRec*				FunctionEdit;
		TextEditRec*				NumFramesEdit;
		TextEditRec*				NumTablesEdit;
		TextEditRec*				TestAttackDuration;
		TextEditRec*				TestDecayDuration;
		TextEditRec*				TestFrequency;
		TextEditRec*				TestSamplingRate;
		TextEditRec*				ActiveTextEdit;
		SimpleButtonRec*		TestButton;
		IconButtonRec*			Bits8Button;
		IconButtonRec*			Bits16Button;
		ScrollRec*					TableScroll;
		long								CurrentlyVisibleTable;
		GenericWindowRec*		MyGenericWindow; /* how the window event dispatcher knows us */
		MenuItemType*				MyMenuItem;
	};


/* create a new wave table window. */
WaveTableWindowRec*	NewWaveTableWindow(struct MainWindowRec* MainWindow,
											struct WaveTableObjectRec* WaveTableObject,
											struct CodeCenterRec* CodeCenter,
											struct WaveTableListRec* WaveTableList,
											struct WaveTableStorageRec* WaveData,
											OrdType WinX, OrdType WinY, OrdType WinWidth, OrdType WinHeight)
	{
		WaveTableWindowRec*		Window;
		char*									StringTemp;
		long									Scan;
		long									Limit;

		CheckPtrExistence(MainWindow);
		CheckPtrExistence(WaveTableObject);
		CheckPtrExistence(CodeCenter);
		CheckPtrExistence(WaveTableList);
		CheckPtrExistence(WaveData);

		/* deal with window placement */
		if ((WinWidth < 100) || (WinHeight < 100) || ((eOptionKey & CheckModifiers()) != 0))
			{
				WinX = 1 + WindowOtherEdgeWidths(eDocumentWindow);
				WinY = 1 + WindowTitleBarHeight(eDocumentWindow);
				WinWidth = WINDOWXSIZE;
				WinHeight = WINDOWYSIZE;
			}
		MakeWindowFitOnScreen(&WinX,&WinY,&WinWidth,&WinHeight);

		Window = (WaveTableWindowRec*)AllocPtrCanFail(sizeof(WaveTableWindowRec),
			"WaveTableWindowRec");
		if (Window == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		Window->MainWindow = MainWindow;
		Window->WaveTableObject = WaveTableObject;
		Window->CodeCenter = CodeCenter;
		Window->WaveTableList = WaveTableList;

		Window->ScreenID = MakeNewWindow(eDocumentWindow,eWindowClosable,
			eWindowZoomable,eWindowResizable,WinX,WinY,WinWidth,WinHeight,
			(void (*)(void*))&WaveTableWindowUpdator,Window);
		if (Window->ScreenID == 0)
			{
			 FailurePoint2:
				ReleasePtr((char*)Window);
				goto FailurePoint1;
			}

		Window->NameEdit = NewTextEdit(Window->ScreenID,eTENoScrollBars,
			GetScreenFont(),9,NAMEEDITX,NAMEEDITY,NAMEEDITWIDTH,NAMEEDITHEIGHT);
		if (Window->NameEdit == NIL)
			{
			 FailurePoint3:
				KillWindow(Window->ScreenID);
				goto FailurePoint2;
			}
		StringTemp = WaveTableObjectGetNameCopy(WaveTableObject);
		if (StringTemp == NIL)
			{
			 FailurePoint4:
				DisposeTextEdit(Window->NameEdit);
				goto FailurePoint3;
			}
		TextEditNewRawData(Window->NameEdit,StringTemp,"\x0a");
		ReleasePtr(StringTemp);
		TextEditHasBeenSaved(Window->NameEdit);

		Window->FunctionEdit = NewTextEdit(Window->ScreenID,
			(TEScrollType)(eTEVScrollBar | eTEHScrollBar),GetMonospacedFont(),9,
			EXPRESSIONEDITX,EXPRESSIONEDITY(WinHeight),EXPRESSIONEDITWIDTH(WinWidth),
			EXPRESSIONEDITHEIGHT);
		if (Window->FunctionEdit == NIL)
			{
			 FailurePoint5:
				goto FailurePoint4;
			}
		StringTemp = WaveTableObjectGetFormulaCopy(WaveTableObject);
		SetTextEditTabSize(Window->FunctionEdit,MainWindowGetTabSize(MainWindow));
		if (StringTemp == NIL)
			{
			 FailurePoint6:
				DisposeTextEdit(Window->FunctionEdit);
				goto FailurePoint5;
			}
		TextEditNewRawData(Window->FunctionEdit,StringTemp,"\x0a");
		ReleasePtr(StringTemp);
		TextEditHasBeenSaved(Window->FunctionEdit);
		SetTextEditAutoIndent(Window->FunctionEdit,True);

		Window->NumFramesEdit = NewTextEdit(Window->ScreenID,eTENoScrollBars,
			GetScreenFont(),9,FRAMEEDITX,FRAMEEDITY,FRAMEEDITWIDTH,FRAMEEDITHEIGHT);
		if (Window->NumFramesEdit == NIL)
			{
			 FailurePoint7:
				goto FailurePoint6;
			}
		StringTemp = IntegerToString(WaveTableObjectEntriesPerTable(WaveTableObject));
		if (StringTemp == NIL)
			{
			 FailurePoint8:
				DisposeTextEdit(Window->NumFramesEdit);
				goto FailurePoint7;
			}
		TextEditNewRawData(Window->NumFramesEdit,StringTemp,"\x0a");
		ReleasePtr(StringTemp);
		TextEditHasBeenSaved(Window->NumFramesEdit);

		Window->NumTablesEdit = NewTextEdit(Window->ScreenID,eTENoScrollBars,
			GetScreenFont(),9,TABLESEDITX,TABLESEDITY,TABLESEDITWIDTH,TABLESEDITHEIGHT);
		if (Window->NumTablesEdit == NIL)
			{
			 FailurePoint9:
				goto FailurePoint8;
			}
		StringTemp = IntegerToString(WaveTableObjectGetNumTables(WaveTableObject));
		if (StringTemp == NIL)
			{
			 FailurePoint10:
				DisposeTextEdit(Window->NumTablesEdit);
				goto FailurePoint9;
			}
		TextEditNewRawData(Window->NumTablesEdit,StringTemp,"\x0a");
		ReleasePtr(StringTemp);
		TextEditHasBeenSaved(Window->NumTablesEdit);

		Window->TestAttackDuration = NewTextEdit(Window->ScreenID,eTENoScrollBars,
			GetScreenFont(),9,TESTATTACKDURATIONX,TESTATTACKDURATIONY,TESTATTACKDURATIONWIDTH,
			TESTATTACKDURATIONHEIGHT);
		if (Window->TestAttackDuration == NIL)
			{
			 FailurePoint11:
				goto FailurePoint10;
			}
		StringTemp = LongDoubleToString(WaveTableObjectGetTestAttack(WaveTableObject),
			6,1e-4,1e6);
		if (StringTemp == NIL)
			{
			 FailurePoint12:
				DisposeTextEdit(Window->TestAttackDuration);
				goto FailurePoint11;
			}
		TextEditNewRawData(Window->TestAttackDuration,StringTemp,"\x0a");
		ReleasePtr(StringTemp);

		Window->TestDecayDuration = NewTextEdit(Window->ScreenID,eTENoScrollBars,
			GetScreenFont(),9,TESTDECAYDURATIONX,TESTDECAYDURATIONY,TESTDECAYDURATIONWIDTH,
			TESTDECAYDURATIONHEIGHT);
		if (Window->TestDecayDuration == NIL)
			{
			 FailurePoint13:
				goto FailurePoint12;
			}
		StringTemp = LongDoubleToString(WaveTableObjectGetTestDecay(WaveTableObject),
			6,1e-4,1e6);
		if (StringTemp == NIL)
			{
			 FailurePoint14:
				DisposeTextEdit(Window->TestDecayDuration);
				goto FailurePoint13;
			}
		TextEditNewRawData(Window->TestDecayDuration,StringTemp,"\x0a");
		ReleasePtr(StringTemp);

		Window->Bits8Button = NewIconButtonPreparedBitmaps(Window->ScreenID,BITS8BUTTONX,
			BITS8BUTTONY,BITS8BUTTONWIDTH,BITS8BUTTONHEIGHT,Bits8Unselected,Bits8MouseDown,
			Bits8Selected,Bits8Selected,eIconRadioMode);
		if (Window->Bits8Button == NIL)
			{
			 FailurePoint15:
				goto FailurePoint14;
			}

		Window->Bits16Button = NewIconButtonPreparedBitmaps(Window->ScreenID,BITS16BUTTONX,
			BITS16BUTTONY,BITS16BUTTONWIDTH,BITS16BUTTONHEIGHT,Bits16Unselected,
			Bits16MouseDown,Bits16Selected,Bits16Selected,eIconRadioMode);
		if (Window->Bits16Button == NIL)
			{
			 FailurePoint16:
				DisposeIconButton(Window->Bits8Button);
				goto FailurePoint15;
			}

		switch (WaveTableObjectGetNumBits(WaveTableObject))
			{
				default:
					EXECUTE(PRERR(ForceAbort,"NewWaveTableWindow:  bad number of bits"));
					break;
				case eSample8bit:
					SetIconButtonState(Window->Bits8Button,True);
					break;
				case eSample16bit:
					SetIconButtonState(Window->Bits16Button,True);
					break;
			}

		Window->TestButton = NewSimpleButton(Window->ScreenID,"Test",
			TESTBUTTONX,TESTBUTTONY,TESTBUTTONWIDTH,TESTBUTTONHEIGHT);
		if (Window->TestButton == NIL)
			{
			 FailurePoint17:
				DisposeIconButton(Window->Bits16Button);
				goto FailurePoint16;
			}

		Window->TableScroll = NewScrollBar(Window->ScreenID,eHScrollBar,
			WAVEFORMVIEWX,TABLESCROLLY(WinHeight),WAVEFORMVIEWWIDTH(WinWidth));
		if (Window->TableScroll == NIL)
			{
			 FailurePoint18:
				DisposeSimpleButton(Window->TestButton);
				goto FailurePoint17;
			}
		Window->CurrentlyVisibleTable = 0;

		Window->WaveTableData = NewWaveTableStorDisp(WaveTableStorageNumBits(WaveData),
			WaveTableStorageNumFramesPerTable(WaveData));
		if (Window->WaveTableData == NIL)
			{
			 FailurePoint19:
				DisposeScrollBar(Window->TableScroll);
				goto FailurePoint18;
			}
		Limit = WaveTableStorageNumTables(WaveData);
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				long							Index;
				long							IndexLimit;

				if (!WaveTableStorDispAppendEntry(Window->WaveTableData))
					{
					 FailurePoint20:
						DisposeWaveTableStorDisp(Window->WaveTableData);
						goto FailurePoint19;
					}
				IndexLimit = WaveTableStorageNumFramesPerTable(WaveData);
				for (Index = 0; Index < IndexLimit; Index += 1)
					{
						WaveTableStorDispSetFrame(Window->WaveTableData,Scan,Index,
							WaveTableStorageGetFrame(WaveData,Scan,Index));
					}
			}

		Window->MyGenericWindow = CheckInNewWindow(Window->ScreenID,Window,
			(void (*)(void*,MyBoolean,OrdType,OrdType,ModifierFlags))&WaveTableWindowDoIdle,
			(void (*)(void*))&WaveTableWindowBecomeActive,
			(void (*)(void*))&WaveTableWindowBecomeInactive,
			(void (*)(void*))&WaveTableWindowJustResized,
			(void (*)(OrdType,OrdType,ModifierFlags,void*))&WaveTableWindowDoMouseDown,
			(void (*)(unsigned char,ModifierFlags,void*))&WaveTableWindowDoKeyDown,
			(void (*)(void*))&WaveTableWindowClose,
			(void (*)(void*))&WaveTableWindowMenuSetup,
			(void (*)(void*,MenuItemType*))&WaveTableWindowDoMenuCommand);
		if (Window->MyGenericWindow == NIL)
			{
			 FailurePoint21:
				goto FailurePoint20;
			}

		Window->TestFrequency = NewTextEdit(Window->ScreenID,eTENoScrollBars,
			GetScreenFont(),9,TESTFREQUENCYX,TESTFREQUENCYY,TESTFREQUENCYWIDTH,
			TESTFREQUENCYHEIGHT);
		if (Window->TestFrequency == NIL)
			{
			 FailurePoint22:
				CheckOutDyingWindow(Window->MyGenericWindow);
				goto FailurePoint21;
			}
		StringTemp = LongDoubleToString(WaveTableObjectGetTestPitch(WaveTableObject),
			13,1e-4,1e6);
		if (StringTemp == NIL)
			{
			 FailurePoint23:
				DisposeTextEdit(Window->TestFrequency);
				goto FailurePoint22;
			}
		TextEditNewRawData(Window->TestFrequency,StringTemp,"\x0a");
		ReleasePtr(StringTemp);

		Window->TestSamplingRate = NewTextEdit(Window->ScreenID,eTENoScrollBars,
			GetScreenFont(),9,TESTSAMPLINGRATEX,TESTSAMPLINGRATEY,TESTSAMPLINGRATEWIDTH,
			TESTSAMPLINGRATEHEIGHT);
		if (Window->TestSamplingRate == NIL)
			{
			 FailurePoint24:
				goto FailurePoint23;
			}
		StringTemp = IntegerToString(WaveTableObjectGetTestSamplingRate(WaveTableObject));
		if (StringTemp == NIL)
			{
			 FailurePoint25:
				DisposeTextEdit(Window->TestSamplingRate);
				goto FailurePoint24;
			}
		TextEditNewRawData(Window->TestSamplingRate,StringTemp,"\x0a");
		ReleasePtr(StringTemp);

		Window->MyMenuItem = MakeNewMenuItem(mmWindowMenu,"x",0);
		if (Window->MyMenuItem == NIL)
			{
			 FailurePoint26:
				goto FailurePoint25;
			}
		if (!RegisterWindowMenuItem(Window->MyMenuItem,(void (*)(void*))&ActivateThisWindow,
			Window->ScreenID))
			{
			 FailurePoint27:
				KillMenuItem(Window->MyMenuItem);
				goto FailurePoint26;
			}

		Window->WaveDataModified = False;
		Window->ActiveTextEdit = Window->FunctionEdit;
		Window->UndoBackupWaveTable = NIL;
		WaveTableWindowResetTitlebar(Window);
		WaveTableWindowUpdateScrollBar(Window);

		return Window;
	}


/* write back modified data and dispose of the wave table window */
void								DisposeWaveTableWindow(WaveTableWindowRec* Window)
	{
		CheckPtrExistence(Window);

		/* save data */
		if (!WaveTableWindowWritebackModifiedData(Window))
			{
				/* failed -- now what? */
			}

		WaveTableObjectClosingWindowNotify(Window->WaveTableObject,
			GetWindowXStart(Window->ScreenID),GetWindowYStart(Window->ScreenID),
			GetWindowWidth(Window->ScreenID),GetWindowHeight(Window->ScreenID));
		DeregisterWindowMenuItem(Window->MyMenuItem);
		KillMenuItem(Window->MyMenuItem);
		CheckOutDyingWindow(Window->MyGenericWindow);
		DisposeWaveTableStorDisp(Window->WaveTableData);
		if (Window->UndoBackupWaveTable != NIL)
			{
				DisposeWaveTableStorDisp(Window->UndoBackupWaveTable);
			}
		DisposeScrollBar(Window->TableScroll);
		DisposeIconButton(Window->Bits16Button);
		DisposeIconButton(Window->Bits8Button);
		DisposeTextEdit(Window->NameEdit);
		DisposeTextEdit(Window->FunctionEdit);
		DisposeTextEdit(Window->NumFramesEdit);
		DisposeTextEdit(Window->NumTablesEdit);
		DisposeTextEdit(Window->TestAttackDuration);
		DisposeTextEdit(Window->TestDecayDuration);
		DisposeTextEdit(Window->TestFrequency);
		DisposeTextEdit(Window->TestSamplingRate);
		DisposeSimpleButton(Window->TestButton);
		KillWindow(Window->ScreenID);
		ReleasePtr((char*)Window);
	}


/* return True if the data has been modified since the last time the file was saved */
MyBoolean						HasWaveTableWindowBeenModified(WaveTableWindowRec* Window)
	{
		CheckPtrExistence(Window);
		return Window->WaveDataModified
			|| TextEditDoesItNeedToBeSaved(Window->NameEdit)
			|| TextEditDoesItNeedToBeSaved(Window->FunctionEdit)
			|| TextEditDoesItNeedToBeSaved(Window->TestAttackDuration)
			|| TextEditDoesItNeedToBeSaved(Window->TestDecayDuration)
			|| TextEditDoesItNeedToBeSaved(Window->TestFrequency)
			|| TextEditDoesItNeedToBeSaved(Window->TestSamplingRate);
	}


/* bring the window to the top and give it the focus */
void								WaveTableWindowBringToTop(WaveTableWindowRec* Window)
	{
		CheckPtrExistence(Window);
		ActivateThisWindow(Window->ScreenID);
	}


void								WaveTableWindowDoIdle(WaveTableWindowRec* Window,
											MyBoolean CheckCursorFlag, OrdType XLoc, OrdType YLoc,
											ModifierFlags Modifiers)
	{
		CheckPtrExistence(Window);
		if (Window->ActiveTextEdit != NIL)
			{
				TextEditUpdateCursor(Window->ActiveTextEdit);
			}
		if (CheckCursorFlag)
			{
				if (TextEditIBeamTest(Window->NameEdit,XLoc,YLoc)
					|| TextEditIBeamTest(Window->FunctionEdit,XLoc,YLoc)
					|| TextEditIBeamTest(Window->NumFramesEdit,XLoc,YLoc)
					|| TextEditIBeamTest(Window->NumTablesEdit,XLoc,YLoc)
					|| TextEditIBeamTest(Window->TestAttackDuration,XLoc,YLoc)
					|| TextEditIBeamTest(Window->TestDecayDuration,XLoc,YLoc)
					|| TextEditIBeamTest(Window->TestFrequency,XLoc,YLoc)
					|| TextEditIBeamTest(Window->TestSamplingRate,XLoc,YLoc))
					{
						SetIBeamCursor();
					}
				else if ((XLoc >= WAVEFORMVIEWX) && (YLoc >= WAVEFORMVIEWY)
					&& (XLoc < WAVEFORMVIEWX + WAVEFORMVIEWWIDTH(GetWindowWidth(Window->ScreenID)))
					&& (YLoc < WAVEFORMVIEWY
					+ WAVEFORMVIEWHEIGHT(GetWindowHeight(Window->ScreenID)) - 15))
					{
						SetSampleInsertionCursor();
					}
				else
					{
						SetArrowCursor();
					}
			}
	}


void								WaveTableWindowBecomeActive(WaveTableWindowRec* Window)
	{
		OrdType						XSize;
		OrdType						YSize;

		CheckPtrExistence(Window);
		if (Window->ActiveTextEdit != NIL)
			{
				EnableTextEditSelection(Window->ActiveTextEdit);
			}
		EnableScrollBar(Window->TableScroll);
		XSize = GetWindowWidth(Window->ScreenID);
		YSize = GetWindowHeight(Window->ScreenID);
		SetClipRect(Window->ScreenID,XSize - 15,YSize - 15,XSize,YSize);
		DrawBitmap(Window->ScreenID,XSize-15,YSize-15,GetGrowIcon(True/*enablegrowicon*/));
	}


void								WaveTableWindowBecomeInactive(WaveTableWindowRec* Window)
	{
		OrdType						XSize;
		OrdType						YSize;

		CheckPtrExistence(Window);
		if (Window->ActiveTextEdit != NIL)
			{
				DisableTextEditSelection(Window->ActiveTextEdit);
			}
		DisableScrollBar(Window->TableScroll);
		XSize = GetWindowWidth(Window->ScreenID);
		YSize = GetWindowHeight(Window->ScreenID);
		SetClipRect(Window->ScreenID,XSize - 15,YSize - 15,XSize,YSize);
		DrawBitmap(Window->ScreenID,XSize-15,YSize-15,GetGrowIcon(False/*disablegrowicon*/));
	}


void								WaveTableWindowJustResized(WaveTableWindowRec* Window)
	{
		OrdType						XSize;
		OrdType						YSize;

		CheckPtrExistence(Window);
		XSize = GetWindowWidth(Window->ScreenID);
		YSize = GetWindowHeight(Window->ScreenID);
		SetClipRect(Window->ScreenID,0,0,XSize,YSize);
		DrawBoxErase(Window->ScreenID,0,0,XSize,YSize);

		SetTextEditPosition(Window->FunctionEdit,EXPRESSIONEDITX,EXPRESSIONEDITY(YSize),
			EXPRESSIONEDITWIDTH(XSize),EXPRESSIONEDITHEIGHT);
		SetScrollLocation(Window->TableScroll,WAVEFORMVIEWX,TABLESCROLLY(YSize),
			WAVEFORMVIEWWIDTH(XSize));
	}


static void					WaveTableWindowScrollHook(long Parameter, ScrollType How,
											WaveTableWindowRec* Window)
	{
		CheckPtrExistence(Window);
		switch (How)
			{
				case eScrollToPosition:
					Window->CurrentlyVisibleTable = Parameter;
					WaveTableWindowUpdateScrollBar(Window);
					WaveTableWindowRedrawTable(Window);
					break;
				case eScrollPageMinus:
					Window->CurrentlyVisibleTable -= PAGEINCREMENT;
					if (Window->CurrentlyVisibleTable < 0)
						{
							Window->CurrentlyVisibleTable = 0;
						}
					WaveTableWindowUpdateScrollBar(Window);
					WaveTableWindowRedrawTable(Window);
					break;
				case eScrollPagePlus:
					Window->CurrentlyVisibleTable += PAGEINCREMENT;
					if (Window->CurrentlyVisibleTable > WaveTableWindowGetNumTables(Window) - 1)
						{
							Window->CurrentlyVisibleTable = WaveTableWindowGetNumTables(Window) - 1;
						}
					if (Window->CurrentlyVisibleTable < 0)
						{
							/* just in case there are 0 tables -- this makes index become -1 */
							Window->CurrentlyVisibleTable = 0;
						}
					WaveTableWindowUpdateScrollBar(Window);
					WaveTableWindowRedrawTable(Window);
					break;
				case eScrollLineMinus:
					Window->CurrentlyVisibleTable -= 1;
					if (Window->CurrentlyVisibleTable < 0)
						{
							Window->CurrentlyVisibleTable = 0;
						}
					WaveTableWindowUpdateScrollBar(Window);
					WaveTableWindowRedrawTable(Window);
					break;
				case eScrollLinePlus:
					Window->CurrentlyVisibleTable += 1;
					if (Window->CurrentlyVisibleTable > WaveTableWindowGetNumTables(Window) - 1)
						{
							Window->CurrentlyVisibleTable = WaveTableWindowGetNumTables(Window) - 1;
						}
					if (Window->CurrentlyVisibleTable < 0)
						{
							/* just in case there are 0 tables -- this makes index become -1 */
							Window->CurrentlyVisibleTable = 0;
						}
					WaveTableWindowUpdateScrollBar(Window);
					WaveTableWindowRedrawTable(Window);
					break;
				default:
					EXECUTE(PRERR(AllowResume,"WaveTableWindowScrollHook:  Unknown scroll opcode"));
					break;
			}
	}


#define SOUNDBUFFERLENGTHBYTES (16384)
#define NUMSOUNDBUFFERS (4)
static void					WaveTablePlayIt(WaveTableWindowRec* Window)
	{
		long										NumTables;
		long										Scan;
		SoundOutputNumBits			SoundChannelBits;
		largefixedsigned**			ReferenceArray;
		unsigned long						WaveformIndex;
		unsigned long						WaveformIncrementor;
		unsigned long						WaveformMask;
		long										NumberOfIterationsAttack;
		long										NumberOfIterationsDecay;
		long										PlaybackSamplingRate;
		void*										OutputBuffer;
		long										OutputIndex;

		/* get the number of tables */
		NumTables = WaveTableStorDispNumTables(Window->WaveTableData);

		/* fill in the buffer */
		ReferenceArray = (largefixedsigned**)AllocPtrCanFail(sizeof(largefixedsigned*)
			* NumTables,"WaveTablePlayIt:  ReferenceArray");
		if (ReferenceArray == NIL)
			{
				AlertHalt("There is not enough memory available to play the sample.",NIL);
			 FailurePoint1:
				return;
			}
		for (Scan = 0; Scan < WaveTableStorDispNumTables(Window->WaveTableData); Scan += 1)
			{
				ReferenceArray[Scan] = WaveTableStorDispGetTable(Window->WaveTableData,Scan);
			}

		/* how many bits should be used for playback */
		switch (WaveTableStorDispNumBits(Window->WaveTableData))
			{
				default:
					EXECUTE(PRERR(ForceAbort,"WaveTablePlayIt:  bad number of bits"));
					break;
				case eSample16bit:
					SoundChannelBits = e16bit;
					break;
				case eSample8bit:
					SoundChannelBits = e8bit;
					break;
			}

		/* playback at what sampling rate */
		PlaybackSamplingRate = WaveTableWindowGetTestSamplingRate(Window);
		if (PlaybackSamplingRate < MINSAMPLINGRATE)
			{
				PlaybackSamplingRate = MINSAMPLINGRATE;
			}
		if (PlaybackSamplingRate > MAXSAMPLINGRATE)
			{
				PlaybackSamplingRate = MAXSAMPLINGRATE;
			}

		/* this is the initial index into the wave table */
		WaveformIndex = 0;

		/* this is the 16.16 bit fixed point number used to increment the index */
		/* into the wave table */
		WaveformIncrementor = WaveTableWindowGetNumFramesPerTable(Window)
			* WaveTableWindowGetPitch(Window) / PlaybackSamplingRate * 65536;

		/* this is used to mask off all garbage bits from the index when */
		/* looking values up in the wave table */
		WaveformMask = WaveTableWindowGetNumFramesPerTable(Window) - 1;

		/* the number of times each wave slice has to be used */
		NumberOfIterationsAttack = WaveTableWindowGetAscendingDuration(Window)
			/ (double)NumTables * PlaybackSamplingRate;
		NumberOfIterationsDecay = WaveTableWindowGetDescendingDuration(Window)
			/ (double)NumTables * PlaybackSamplingRate;

		if (!OpenSoundChannel(PlaybackSamplingRate,eMono,SoundChannelBits,
			SOUNDBUFFERLENGTHBYTES,NUMSOUNDBUFFERS,NUMSOUNDBUFFERS))
			{
				AlertHalt("Unable top open the sound device.",NIL);
			 FailurePoint2:
				ReleasePtr((char*)ReferenceArray);
				goto FailurePoint1;
			}
		OutputIndex = 0;
		do
			{
				OutputBuffer = CheckOutSoundBuffer();
			} while (OutputBuffer == NIL);

		if (SoundChannelBits == e16bit)
			{
				for (Scan = 0; Scan < NumTables; Scan += 1)
					{
						largefixedsigned*			Buffer = ReferenceArray[Scan];
						long									Index;

						for (Index = 0; Index < NumberOfIterationsAttack; Index += 1)
							{
								unsigned long					SampleReference;
								signed short					Left;
								signed short					Right;
								signed long						Fraction;

								SampleReference = (WaveformIndex >> 16) & WaveformMask;
								Left = Buffer[SampleReference] >> SIXTEENBITSHIFTFACTOR;
								Right = Buffer[SampleReference + 1] >> SIXTEENBITSHIFTFACTOR;
								Fraction = (WaveformIndex & 0xffff) / 2;
								WaveformIndex += WaveformIncrementor;
								((short*)OutputBuffer)[OutputIndex] = (Left * (0x8000 - Fraction)
									+ Right * Fraction) >> 15;
								OutputIndex += 1;
								if (OutputIndex >= SOUNDBUFFERLENGTHBYTES / sizeof(short))
									{
										SubmitBuffer((char*)OutputBuffer,SOUNDBUFFERLENGTHBYTES
											/ sizeof(short),NIL,NIL);
										if (RelinquishCPUJudiciouslyCheckCancel())
											{
												goto EscapePoint;
											}
										do
											{
												OutputBuffer = CheckOutSoundBuffer();
											} while (OutputBuffer == NIL);
										OutputIndex = 0;
									}
							}
					}
			}
		 else
			{
				for (Scan = 0; Scan < NumTables; Scan += 1)
					{
						largefixedsigned*			Buffer = ReferenceArray[Scan];
						long									Index;

						for (Index = 0; Index < NumberOfIterationsAttack; Index += 1)
							{
								unsigned long					SampleReference;
								signed short					Left;
								signed short					Right;
								signed long						Fraction;

								SampleReference = (WaveformIndex >> 16) & WaveformMask;
								Left = Buffer[SampleReference] >> EIGHTBITSHIFTFACTOR;
								Right = Buffer[SampleReference + 1] >> EIGHTBITSHIFTFACTOR;
								Fraction = (WaveformIndex & 0xffff) / 2;
								WaveformIndex += WaveformIncrementor;
								((char*)OutputBuffer)[OutputIndex] = (Left * (0x8000 - Fraction)
									+ Right * Fraction) >> 15;
								OutputIndex += 1;
								if (OutputIndex >= SOUNDBUFFERLENGTHBYTES / sizeof(char))
									{
										SubmitBuffer((char*)OutputBuffer,SOUNDBUFFERLENGTHBYTES
											/ sizeof(char),NIL,NIL);
										if (RelinquishCPUJudiciouslyCheckCancel())
											{
												goto EscapePoint;
											}
										do
											{
												OutputBuffer = CheckOutSoundBuffer();
											} while (OutputBuffer == NIL);
										OutputIndex = 0;
									}
							}
					}
			}

		if (SoundChannelBits == e16bit)
			{
				for (Scan = NumTables - 1; Scan >= 0; Scan -= 1)
					{
						largefixedsigned*			Buffer = ReferenceArray[Scan];
						long									Index;

						for (Index = 0; Index < NumberOfIterationsDecay; Index += 1)
							{
								unsigned long					SampleReference;
								signed short					Left;
								signed short					Right;
								signed long						Fraction;

								SampleReference = (WaveformIndex >> 16) & WaveformMask;
								Left = Buffer[SampleReference] >> SIXTEENBITSHIFTFACTOR;
								Right = Buffer[SampleReference + 1] >> SIXTEENBITSHIFTFACTOR;
								Fraction = (WaveformIndex & 0xffff) / 2;
								WaveformIndex += WaveformIncrementor;
								((short*)OutputBuffer)[OutputIndex] = (Left * (0x8000 - Fraction)
									+ Right * Fraction) >> 15;
								OutputIndex += 1;
								if (OutputIndex >= SOUNDBUFFERLENGTHBYTES / sizeof(short))
									{
										SubmitBuffer((char*)OutputBuffer,SOUNDBUFFERLENGTHBYTES
											/ sizeof(short),NIL,NIL);
										if (RelinquishCPUJudiciouslyCheckCancel())
											{
												goto EscapePoint;
											}
										do
											{
												OutputBuffer = CheckOutSoundBuffer();
											} while (OutputBuffer == NIL);
										OutputIndex = 0;
									}
							}
					}
				SubmitBuffer((char*)OutputBuffer,OutputIndex / sizeof(short),NIL,NIL);
			}
		 else
			{
				for (Scan = NumTables - 1; Scan >= 0; Scan -= 1)
					{
						largefixedsigned*			Buffer = ReferenceArray[Scan];
						long									Index;

						for (Index = 0; Index < NumberOfIterationsDecay; Index += 1)
							{
								unsigned long					SampleReference;
								signed short					Left;
								signed short					Right;
								signed long						Fraction;

								SampleReference = (WaveformIndex >> 16) & WaveformMask;
								Left = Buffer[SampleReference] >> EIGHTBITSHIFTFACTOR;
								Right = Buffer[SampleReference + 1] >> EIGHTBITSHIFTFACTOR;
								Fraction = (WaveformIndex & 0xffff) / 2;
								WaveformIndex += WaveformIncrementor;
								((char*)OutputBuffer)[OutputIndex] = (Left * (0x8000 - Fraction)
									+ Right * Fraction) >> 15;
								OutputIndex += 1;
								if (OutputIndex >= SOUNDBUFFERLENGTHBYTES / sizeof(char))
									{
										SubmitBuffer((char*)OutputBuffer,SOUNDBUFFERLENGTHBYTES
											/ sizeof(char),NIL,NIL);
										if (RelinquishCPUJudiciouslyCheckCancel())
											{
												goto EscapePoint;
											}
										do
											{
												OutputBuffer = CheckOutSoundBuffer();
											} while (OutputBuffer == NIL);
										OutputIndex = 0;
									}
							}
					}
				SubmitBuffer((char*)OutputBuffer,OutputIndex / sizeof(char),NIL,NIL);
			}

	 EscapePoint:
		CloseSoundChannel(NIL,NIL);
		ReleasePtr((char*)ReferenceArray);
	}


void								WaveTableWindowDoMouseDown(OrdType XLoc, OrdType YLoc,
											ModifierFlags Modifiers, WaveTableWindowRec* Window)
	{
		CheckPtrExistence(Window);
		if ((XLoc >= GetWindowWidth(Window->ScreenID) - 15)
			&& (XLoc <= GetWindowWidth(Window->ScreenID))
			&& (YLoc >= GetWindowHeight(Window->ScreenID) - 15)
			&& (YLoc <= GetWindowHeight(Window->ScreenID)))
			{
				UserGrowWindow(Window->ScreenID,XLoc,YLoc);
				WaveTableWindowJustResized(Window);
			}
		else if (TextEditHitTest(Window->NameEdit,XLoc,YLoc))
			{
				if (Window->ActiveTextEdit != Window->NameEdit)
					{
						if (Window->ActiveTextEdit != NIL)
							{
								DisableTextEditSelection(Window->ActiveTextEdit);
							}
						Window->ActiveTextEdit = Window->NameEdit;
						EnableTextEditSelection(Window->NameEdit);
					}
				TextEditDoMouseDown(Window->NameEdit,XLoc,YLoc,Modifiers);
			}
		else if (TextEditHitTest(Window->FunctionEdit,XLoc,YLoc))
			{
				if (Window->ActiveTextEdit != Window->FunctionEdit)
					{
						if (Window->ActiveTextEdit != NIL)
							{
								DisableTextEditSelection(Window->ActiveTextEdit);
							}
						Window->ActiveTextEdit = Window->FunctionEdit;
						EnableTextEditSelection(Window->FunctionEdit);
					}
				TextEditDoMouseDown(Window->FunctionEdit,XLoc,YLoc,Modifiers);
			}
		else if (TextEditHitTest(Window->TestAttackDuration,XLoc,YLoc))
			{
				if (Window->ActiveTextEdit != Window->TestAttackDuration)
					{
						if (Window->ActiveTextEdit != NIL)
							{
								DisableTextEditSelection(Window->ActiveTextEdit);
							}
						Window->ActiveTextEdit = Window->TestAttackDuration;
						EnableTextEditSelection(Window->TestAttackDuration);
					}
				TextEditDoMouseDown(Window->TestAttackDuration,XLoc,YLoc,Modifiers);
			}
		else if (TextEditHitTest(Window->TestDecayDuration,XLoc,YLoc))
			{
				if (Window->ActiveTextEdit != Window->TestDecayDuration)
					{
						if (Window->ActiveTextEdit != NIL)
							{
								DisableTextEditSelection(Window->ActiveTextEdit);
							}
						Window->ActiveTextEdit = Window->TestDecayDuration;
						EnableTextEditSelection(Window->TestDecayDuration);
					}
				TextEditDoMouseDown(Window->TestDecayDuration,XLoc,YLoc,Modifiers);
			}
		else if (TextEditHitTest(Window->TestFrequency,XLoc,YLoc))
			{
				if (Window->ActiveTextEdit != Window->TestFrequency)
					{
						if (Window->ActiveTextEdit != NIL)
							{
								DisableTextEditSelection(Window->ActiveTextEdit);
							}
						Window->ActiveTextEdit = Window->TestFrequency;
						EnableTextEditSelection(Window->TestFrequency);
					}
				TextEditDoMouseDown(Window->TestFrequency,XLoc,YLoc,Modifiers);
			}
		else if (TextEditHitTest(Window->TestSamplingRate,XLoc,YLoc))
			{
				if (Window->ActiveTextEdit != Window->TestSamplingRate)
					{
						if (Window->ActiveTextEdit != NIL)
							{
								DisableTextEditSelection(Window->ActiveTextEdit);
							}
						Window->ActiveTextEdit = Window->TestSamplingRate;
						EnableTextEditSelection(Window->TestSamplingRate);
					}
				TextEditDoMouseDown(Window->TestSamplingRate,XLoc,YLoc,Modifiers);
			}
		else if (TextEditHitTest(Window->NumFramesEdit,XLoc,YLoc))
			{
				WaveTableWindowSetNewNumFrames(Window);
			}
		else if (TextEditHitTest(Window->NumTablesEdit,XLoc,YLoc))
			{
				WaveTableWindowSetNewNumTables(Window);
			}
		else if (IconButtonHitTest(Window->Bits8Button,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->Bits8Button,XLoc,YLoc,NIL,NIL))
					{
						SetWaveTableStorDispNumBits(Window->WaveTableData,eSample8bit);
						SetIconButtonState(Window->Bits16Button,False);
						Window->WaveDataModified = True;
					}
			}
		else if (IconButtonHitTest(Window->Bits16Button,XLoc,YLoc))
			{
				if (IconButtonMouseDown(Window->Bits16Button,XLoc,YLoc,NIL,NIL))
					{
						SetWaveTableStorDispNumBits(Window->WaveTableData,eSample16bit);
						SetIconButtonState(Window->Bits8Button,False);
						Window->WaveDataModified = True;
					}
			}
		else if (ScrollHitTest(Window->TableScroll,XLoc,YLoc))
			{
				ScrollHitProc(Window->TableScroll,CheckModifiers(),XLoc,YLoc,Window,
					(void (*)(long,ScrollType,void*))&WaveTableWindowScrollHook);
			}
		else if (SimpleButtonHitTest(Window->TestButton,XLoc,YLoc))
			{
				if (SimpleButtonMouseDown(Window->TestButton,XLoc,YLoc,NIL,NIL))
					{
						WaveTablePlayIt(Window);
					}
			}
		else
			{
				if (Window->ActiveTextEdit != NIL)
					{
						DisableTextEditSelection(Window->ActiveTextEdit);
						Window->ActiveTextEdit = NIL;
					}
			}
	}


void								WaveTableWindowDoKeyDown(unsigned char KeyCode,
											ModifierFlags Modifiers, WaveTableWindowRec* Window)
	{
		CheckPtrExistence(Window);
		if (Window->ActiveTextEdit != NIL)
			{
				TextEditDoKeyPressed(Window->ActiveTextEdit,KeyCode,Modifiers);
			}
	}


void								WaveTableWindowClose(WaveTableWindowRec* Window)
	{
		CheckPtrExistence(Window);
		DisposeWaveTableWindow(Window);
	}


void								WaveTableWindowUpdator(WaveTableWindowRec* Window)
	{
		OrdType						Height;
		OrdType						Width;

		CheckPtrExistence(Window);
		Width = GetWindowWidth(Window->ScreenID);
		Height = GetWindowHeight(Window->ScreenID);

		TextEditFullRedraw(Window->NameEdit);
		TextEditFullRedraw(Window->FunctionEdit);
		TextEditFullRedraw(Window->NumFramesEdit);
		TextEditFullRedraw(Window->NumTablesEdit);
		TextEditFullRedraw(Window->TestAttackDuration);
		TextEditFullRedraw(Window->TestDecayDuration);
		TextEditFullRedraw(Window->TestFrequency);
		TextEditFullRedraw(Window->TestSamplingRate);
		RedrawSimpleButton(Window->TestButton);
		RedrawIconButton(Window->Bits8Button);
		RedrawIconButton(Window->Bits16Button);
		RedrawScrollBar(Window->TableScroll);
		WaveTableWindowRedrawTable(Window);

		SetClipRect(Window->ScreenID,0,0,Width,Height);
		DrawTextLine(Window->ScreenID,GetScreenFont(),9,"Name:",5,NAMEX,NAMEY,ePlain);
		DrawTextLine(Window->ScreenID,GetScreenFont(),9,"Frames:",7,FRAMEX,FRAMEY,ePlain);
		DrawTextLine(Window->ScreenID,GetScreenFont(),9,"Tables:",7,TABLESX,TABLESY,ePlain);
		DrawTextLine(Window->ScreenID,GetScreenFont(),9,"Test Attack:",12,
			TESTATTACKX,TESTATTACKY,ePlain);
		DrawTextLine(Window->ScreenID,GetScreenFont(),9,"Test Decay:",11,
			TESTDECAYX,TESTDECAYY,ePlain);
		DrawTextLine(Window->ScreenID,GetScreenFont(),9,"Test Freq:",10,
			TESTFREQX,TESTFREQY,ePlain);
		DrawTextLine(Window->ScreenID,GetScreenFont(),9,"Test Smpl Rate:",15,
			TESTSAMPLX,TESTSAMPLY,ePlain);

		SetClipRect(Window->ScreenID,Width - 15,Height - 15,Width,Height);
		DrawBitmap(Window->ScreenID,Width - 15,Height - 15,
			GetGrowIcon(Window->MyGenericWindow == GetCurrentWindowID()));
	}


void								WaveTableWindowMenuSetup(WaveTableWindowRec* Window)
	{
		CheckPtrExistence(Window);
		MainWindowEnableGlobalMenus(Window->MainWindow);
		EnableMenuItem(mPaste);
		ChangeItemName(mPaste,"Paste Text");
		if (Window->ActiveTextEdit != NIL)
			{
				if (TextEditIsThereValidSelection(Window->ActiveTextEdit))
					{
						EnableMenuItem(mCut);
						ChangeItemName(mCut,"Cut Text");
						EnableMenuItem(mCopy);
						ChangeItemName(mCopy,"Copy Text");
						EnableMenuItem(mClear);
						ChangeItemName(mClear,"Clear Text");
					}
				EnableMenuItem(mSelectAll);
				ChangeItemName(mSelectAll,"Select All Text");
				if (TextEditCanWeUndo(Window->ActiveTextEdit))
					{
						EnableMenuItem(mUndo);
						ChangeItemName(mUndo,"Undo Text Change");
					}
			}
		 else
			{
				if (Window->UndoBackupWaveTable != NIL)
					{
						EnableMenuItem(mUndo);
						ChangeItemName(mUndo,"Undo Wave Table Edit");
					}
			}
		EnableMenuItem(mCloseFile);
		ChangeItemName(mCloseFile,"Close Wave Table Editor");
		EnableMenuItem(mEvaluateCalc);
		EnableMenuItem(mShiftLeft);
		EnableMenuItem(mShiftRight);
		EnableMenuItem(mBalanceParens);
		ChangeItemName(mDeleteObject,"Delete Wave Table");
		EnableMenuItem(mDeleteObject);
		EnableMenuItem(mFind);
		if (PtrSize(GlobalSearchString) != 0)
			{
				EnableMenuItem(mFindAgain);
				if ((Window->ActiveTextEdit != NIL)
					&& TextEditIsThereValidSelection(Window->ActiveTextEdit))
					{
						EnableMenuItem(mReplace);
						EnableMenuItem(mReplaceAndFindAgain);
					}
			}
		if ((Window->ActiveTextEdit != NIL)
			&& TextEditIsThereValidSelection(Window->ActiveTextEdit))
			{
				EnableMenuItem(mEnterSelection);
			}
		SetItemCheckmark(Window->MyMenuItem);
	}


void								WaveTableWindowDoMenuCommand(WaveTableWindowRec* Window,
											MenuItemType* MenuItem)
	{
		CheckPtrExistence(Window);
		if (MainWindowDoGlobalMenuItem(Window->MainWindow,MenuItem))
			{
			}
		else if (MenuItem == mPaste)
			{
				if (Window->ActiveTextEdit != NIL)
					{
						TextEditDoMenuPaste(Window->ActiveTextEdit);
					}
			}
		else if (MenuItem == mCut)
			{
				if (Window->ActiveTextEdit != NIL)
					{
						TextEditDoMenuCut(Window->ActiveTextEdit);
					}
			}
		else if (MenuItem == mCopy)
			{
				if (Window->ActiveTextEdit != NIL)
					{
						TextEditDoMenuCopy(Window->ActiveTextEdit);
					}
			}
		else if (MenuItem == mClear)
			{
				if (Window->ActiveTextEdit != NIL)
					{
						TextEditDoMenuClear(Window->ActiveTextEdit);
					}
			}
		else if (MenuItem == mSelectAll)
			{
				if (Window->ActiveTextEdit != NIL)
					{
						TextEditDoMenuSelectAll(Window->ActiveTextEdit);
					}
			}
		else if (MenuItem == mUndo)
			{
				if (Window->ActiveTextEdit != NIL)
					{
						TextEditDoMenuUndo(Window->ActiveTextEdit);
					}
				 else
					{
						WaveTableStorDispRec*		Temp;

						Temp = Window->UndoBackupWaveTable;
						Window->UndoBackupWaveTable = Window->WaveTableData;
						Window->WaveTableData = Temp;
						Window->WaveDataModified = True;
						WaveTableWindowUpdateAllParameters(Window);
					}
			}
		else if (MenuItem == mCloseFile)
			{
				WaveTableWindowClose(Window);
			}
		else if (MenuItem == mEvaluateCalc)
			{
				WaveTableWindowEvaluateFunction(Window);
			}
		else if (MenuItem == mShiftLeft)
			{
				TextEditShiftSelectionLeftOneTab(Window->ActiveTextEdit);
			}
		else if (MenuItem == mShiftRight)
			{
				TextEditShiftSelectionRightOneTab(Window->ActiveTextEdit);
			}
		else if (MenuItem == mBalanceParens)
			{
				TextEditBalanceParens(Window->ActiveTextEdit);
			}
		else if (MenuItem == mDeleteObject)
			{
				WaveTableListDeleteWaveTable(Window->WaveTableList,Window->WaveTableObject);
			}
		else if (MenuItem == mFind)
			{
				if (Window->ActiveTextEdit != Window->FunctionEdit)
					{
						if (Window->ActiveTextEdit != NIL)
							{
								DisableTextEditSelection(Window->ActiveTextEdit);
							}
						Window->ActiveTextEdit = Window->FunctionEdit;
						EnableTextEditSelection(Window->ActiveTextEdit);
					}
				switch (DoFindDialog(&GlobalSearchString,&GlobalReplaceString,
					mCut,mPaste,mCopy,mUndo,mSelectAll,mClear))
					{
						default:
							EXECUTE(PRERR(ForceAbort,
								"WaveTableWindowDoMenuCommand:  bad value from DoFindDialog"));
							break;
						case eFindCancel:
						case eDontFind:
							break;
						case eFindFromStart:
							SetTextEditInsertionPoint(Window->ActiveTextEdit,0,0);
							TextEditFindAgain(Window->ActiveTextEdit,GlobalSearchString);
							TextEditShowSelection(Window->ActiveTextEdit);
							break;
						case eFindAgain:
							TextEditFindAgain(Window->ActiveTextEdit,GlobalSearchString);
							TextEditShowSelection(Window->ActiveTextEdit);
							break;
					}
			}
		else if (MenuItem == mFindAgain)
			{
				if (Window->ActiveTextEdit != Window->FunctionEdit)
					{
						if (Window->ActiveTextEdit != NIL)
							{
								DisableTextEditSelection(Window->ActiveTextEdit);
							}
						Window->ActiveTextEdit = Window->FunctionEdit;
						EnableTextEditSelection(Window->ActiveTextEdit);
					}
				TextEditFindAgain(Window->ActiveTextEdit,GlobalSearchString);
				TextEditShowSelection(Window->ActiveTextEdit);
			}
		else if (MenuItem == mReplace)
			{
				if (Window->ActiveTextEdit != Window->FunctionEdit)
					{
						if (Window->ActiveTextEdit != NIL)
							{
								DisableTextEditSelection(Window->ActiveTextEdit);
							}
						Window->ActiveTextEdit = Window->FunctionEdit;
						EnableTextEditSelection(Window->ActiveTextEdit);
					}
				if (TextEditIsThereValidSelection(Window->ActiveTextEdit))
					{
						TextEditInsertRawDataWithUndo(Window->ActiveTextEdit,GlobalReplaceString,
							SYSTEMLINEFEED);
					}
			}
		else if (MenuItem == mReplaceAndFindAgain)
			{
				if (Window->ActiveTextEdit != Window->FunctionEdit)
					{
						if (Window->ActiveTextEdit != NIL)
							{
								DisableTextEditSelection(Window->ActiveTextEdit);
							}
						Window->ActiveTextEdit = Window->FunctionEdit;
						EnableTextEditSelection(Window->ActiveTextEdit);
					}
				if (TextEditIsThereValidSelection(Window->ActiveTextEdit))
					{
						TextEditInsertRawDataWithUndo(Window->ActiveTextEdit,GlobalReplaceString,
							SYSTEMLINEFEED);
						TextEditFindAgain(Window->ActiveTextEdit,GlobalSearchString);
						TextEditShowSelection(Window->ActiveTextEdit);
					}
			}
		else if (MenuItem == mEnterSelection)
			{
				if (Window->ActiveTextEdit != NIL)
					{
						char*						NewString;

						NewString = TextEditGetSelection(Window->ActiveTextEdit);
						if (NewString != NIL)
							{
								ReleasePtr(GlobalSearchString);
								GlobalSearchString = NewString;
							}
					}
			}
		else
			{
				EXECUTE(PRERR(AllowResume,"WaveTableWindowDoMenuCommand:  unknown menu command"));
			}
	}


/* get a copy of the wave table's name */
char*								WaveTableWindowGetNameCopy(WaveTableWindowRec* Window)
	{
		char*							TextCopy;

		CheckPtrExistence(Window);
		TextCopy = TextEditGetRawData(Window->NameEdit,"\x0a");
		if (TextCopy != NIL)
			{
				SetTag(TextCopy,"WaveTableWindowNameCopy");
			}
		return TextCopy;
	}


/* get a copy of the wave table's formula */
char*								WaveTableWindowGetFormulaCopy(WaveTableWindowRec* Window)
	{
		char*							TextCopy;

		CheckPtrExistence(Window);
		TextCopy = TextEditGetRawData(Window->FunctionEdit,"\x0a");
		if (TextCopy != NIL)
			{
				SetTag(TextCopy,"WaveTableWindowFormulaCopy");
			}
		return TextCopy;
	}


/* get the number of bits used for the wave table */
NumBitsType					WaveTableWindowGetNumBits(WaveTableWindowRec* Window)
	{
		CheckPtrExistence(Window);
		if (GetIconButtonState(Window->Bits16Button))
			{
				return eSample16bit;
			}
		 else
			{
				return eSample8bit;
			}
	}


/* get the number of periods in the wave table */
long								WaveTableWindowGetNumTables(WaveTableWindowRec* Window)
	{
		CheckPtrExistence(Window);
		return WaveTableStorDispNumTables(Window->WaveTableData);
	}


/* get the number of frames in each period of the wave table */
long								WaveTableWindowGetNumFramesPerTable(WaveTableWindowRec* Window)
	{
		CheckPtrExistence(Window);
		return WaveTableStorDispNumFramesPerTable(Window->WaveTableData);
	}


/* get the pitch at which the wave table should be tested */
double							WaveTableWindowGetPitch(WaveTableWindowRec* Window)
	{
		char*							Text;
		double						ReturnValue;

		CheckPtrExistence(Window);
		Text = TextEditGetRawData(Window->TestFrequency,SYSTEMLINEFEED);
		if (Text != NIL)
			{
				ReturnValue = StringToLongDouble(Text,PtrSize(Text));
				ReleasePtr(Text);
			}
		 else
			{
				ReturnValue = 261.625565300598635;
			}
		return ReturnValue;
	}


/* get the duration (seconds) for the increasing table index phase of the test */
double							WaveTableWindowGetAscendingDuration(WaveTableWindowRec* Window)
	{
		char*							Text;
		double						ReturnValue;

		CheckPtrExistence(Window);
		Text = TextEditGetRawData(Window->TestAttackDuration,SYSTEMLINEFEED);
		if (Text != NIL)
			{
				ReturnValue = StringToLongDouble(Text,PtrSize(Text));
				ReleasePtr(Text);
			}
		 else
			{
				ReturnValue = 1;
			}
		return ReturnValue;
	}


/* get the duration (seconds) for the decreasing table index phase of the test */
double							WaveTableWindowGetDescendingDuration(WaveTableWindowRec* Window)
	{
		char*							Text;
		double						ReturnValue;

		CheckPtrExistence(Window);
		Text = TextEditGetRawData(Window->TestDecayDuration,SYSTEMLINEFEED);
		if (Text != NIL)
			{
				ReturnValue = StringToLongDouble(Text,PtrSize(Text));
				ReleasePtr(Text);
			}
		 else
			{
				ReturnValue = 2;
			}
		return ReturnValue;
	}


/* get the sampling rate to use for the test */
long								WaveTableWindowGetTestSamplingRate(WaveTableWindowRec* Window)
	{
		char*							Text;
		double						ReturnValue;

		CheckPtrExistence(Window);
		Text = TextEditGetRawData(Window->TestSamplingRate,SYSTEMLINEFEED);
		if (Text != NIL)
			{
				ReturnValue = StringToInteger(Text,PtrSize(Text));
				ReleasePtr(Text);
			}
		 else
			{
				ReturnValue = 44100;
			}
		return ReturnValue;
	}


/* reset the scroll bar indices and redraw it */
void								WaveTableWindowUpdateScrollBar(WaveTableWindowRec* Window)
	{
		CheckPtrExistence(Window);
		SetMaxScrollIndex(Window->TableScroll,WaveTableWindowGetNumTables(Window));
		SetScrollIndex(Window->TableScroll,Window->CurrentlyVisibleTable);
	}


/* redraw the wave table waveform box */
void								WaveTableWindowRedrawTable(WaveTableWindowRec* Window)
	{
		OrdType						WindowWidth;
		OrdType						WindowHeight;
		long							Scan;
		long							Limit;
		OrdType						PreviousYValue;
		OrdType						PreviousXValue;
		MyBoolean					PreviousYValueIsValid;

		CheckPtrExistence(Window);
		WindowWidth = GetWindowWidth(Window->ScreenID);
		WindowHeight = GetWindowHeight(Window->ScreenID);
		SetClipRect(Window->ScreenID,WAVEFORMVIEWX,WAVEFORMVIEWY,
			WAVEFORMVIEWWIDTH(WindowWidth),WAVEFORMVIEWHEIGHT(WindowHeight));
		DrawBoxFrame(Window->ScreenID,eBlack,WAVEFORMVIEWX,WAVEFORMVIEWY,
			WAVEFORMVIEWWIDTH(WindowWidth),WAVEFORMVIEWHEIGHT(WindowHeight));
		if (WaveTableWindowGetNumTables(Window) == 0)
			{
				DrawBoxPaint(Window->ScreenID,eLightGrey,WAVEFORMVIEWX + 1,WAVEFORMVIEWY + 1,
					WAVEFORMVIEWWIDTH(WindowWidth) - 2,WAVEFORMVIEWHEIGHT(WindowHeight) - 2);
				return;
			}
		DrawBoxErase(Window->ScreenID,WAVEFORMVIEWX + 1,WAVEFORMVIEWY + 1,
			WAVEFORMVIEWWIDTH(WindowWidth) - 2,WAVEFORMVIEWHEIGHT(WindowHeight) - 2);
		ERROR((Window->CurrentlyVisibleTable < 0) || (Window->CurrentlyVisibleTable
			>= WaveTableWindowGetNumTables(Window)),PRERR(ForceAbort,
			"WaveTableWindowRedrawTable:  current table index is out of range"));
		Limit = WaveTableWindowGetNumFramesPerTable(Window);
		PreviousYValueIsValid = False;
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				double					Value;
				OrdType					ThisYValue;
				OrdType					ThisXValue;

				Value = largefixed2double(WaveTableStorDispGetFrame(Window->WaveTableData,
					Window->CurrentlyVisibleTable,Scan));
				ThisYValue = (1 - ((Value + 1) / 2)) * (WAVEFORMVIEWHEIGHT(WindowHeight) - 3)
					+ WAVEFORMVIEWY + 1;
				ThisXValue = (Scan * (WAVEFORMVIEWWIDTH(WindowWidth) - 2))
					/ (Limit - 1) + WAVEFORMVIEWX + 1;
				if (!PreviousYValueIsValid)
					{
						PreviousYValueIsValid = True;
						PreviousYValue = ThisYValue;
						PreviousXValue = ThisXValue;
					}
				DrawLine(Window->ScreenID,eBlack,PreviousXValue,PreviousYValue,
					ThisXValue - PreviousXValue,ThisYValue - PreviousYValue);
				PreviousYValue = ThisYValue;
				PreviousXValue = ThisXValue;
			}
	}


/* the name of the file has changed, so change the title bar.  the string is a */
/* non-null-terminated, and the caller is responsible for disposing of it */
void								WaveTableWindowGlobalNameChange(WaveTableWindowRec* Window,
											char* NewFilename)
	{
		char*							LocalNameCopy;

		CheckPtrExistence(Window);
		CheckPtrExistence(NewFilename);
		LocalNameCopy = WaveTableWindowGetNameCopy(Window);
		if (LocalNameCopy != NIL)
			{
				char*							SeparatorString;

				SeparatorString = StringToBlockCopy(":  ");
				if (SeparatorString != NIL)
					{
						char*							LeftHalfOfString;

						LeftHalfOfString = ConcatBlockCopy(NewFilename,SeparatorString);
						if (LeftHalfOfString != NIL)
							{
								char*							TotalString;

								TotalString = ConcatBlockCopy(LeftHalfOfString,LocalNameCopy);
								if (TotalString != NIL)
									{
										char*							NullTerminatedString;

										NullTerminatedString = BlockToStringCopy(TotalString);
										if (NullTerminatedString != NIL)
											{
												SetWindowName(Window->ScreenID,NullTerminatedString);
												ChangeItemName(Window->MyMenuItem,NullTerminatedString);
												ReleasePtr(NullTerminatedString);
											}
										ReleasePtr(TotalString);
									}
								ReleasePtr(LeftHalfOfString);
							}
						ReleasePtr(SeparatorString);
					}
				ReleasePtr(LocalNameCopy);
			}
	}


/* reset the title bar name even if the document name hasn't changed */
void								WaveTableWindowResetTitlebar(WaveTableWindowRec* Window)
	{
		char*							DocumentName;

		CheckPtrExistence(Window);
		DocumentName = GetCopyOfDocumentName(Window->MainWindow);
		if (DocumentName != NIL)
			{
				WaveTableWindowGlobalNameChange(Window,DocumentName);
				ReleasePtr(DocumentName);
			}
	}


/* set the number of periods to that stored in the edit box, and interpolate */
/* the periods. */
void								WaveTableWindowSetNewNumTables(WaveTableWindowRec* Window)
	{
		long							OriginalNumTables;
		long							NewNumTables;

		CheckPtrExistence(Window);
		OriginalNumTables = WaveTableWindowGetNumTables(Window);
		NewNumTables = DoNumberDialog("Enter new number of tables:",OriginalNumTables,
			mCut,mPaste,mCopy,mUndo,mSelectAll,mClear);
		if (NewNumTables != OriginalNumTables)
			{
				WaveTableStorDispRec*	NewTable;
				long									TableScan;

				/* free up undo-backup info */
				if (Window->UndoBackupWaveTable != NIL)
					{
						DisposeWaveTableStorDisp(Window->UndoBackupWaveTable);
						Window->UndoBackupWaveTable = NIL;
					}

				/* build new table */
				NewTable = NewWaveTableStorDisp(WaveTableStorDispNumBits(Window->WaveTableData),
					WaveTableStorDispNumFramesPerTable(Window->WaveTableData));
				if (NewTable == NIL)
					{
					 MemoryFailurePoint1:
						AlertHalt("There is not enough memory available to resize the wave table.",NIL);
						return;
					}
				for (TableScan = 0; TableScan < NewNumTables; TableScan += 1)
					{
						if (!WaveTableStorDispAppendEntry(NewTable))
							{
							 MemoryFailurePoint2:
								DisposeWaveTableStorDisp(NewTable);
								goto MemoryFailurePoint1;
							}
					}

				if ((NewNumTables != 0) && (OriginalNumTables != 0))
					{
						long						NumFrames;

						NumFrames = WaveTableStorDispNumFramesPerTable(NewTable);
						/* store the neat things into the table.  we use linear interpolation */
						/* between adjacent tables to create new tables. */
						for (TableScan = 0; TableScan < NewNumTables; TableScan += 1)
							{
								double						LeftWeight;
								double						RightWeight;
								long							LeftIndex;
								long							RightIndex;
								double						PrecisePositioning;
								long							FrameScan;

								if (NewNumTables > 1)
									{
										PrecisePositioning = (double)TableScan / (double)(NewNumTables - 1)
											* (double)(OriginalNumTables - 1);
									}
								 else
									{
										PrecisePositioning = (double)(OriginalNumTables - 1) / 2;
									}
								LeftIndex = (long)PrecisePositioning;
								RightIndex = LeftIndex + 1;
								RightWeight = PrecisePositioning - LeftIndex;
								LeftWeight = 1 - RightWeight;
								for (FrameScan = 0; FrameScan < NumFrames; FrameScan += 1)
									{
										double						LeftValue;
										double						RightValue;
										double						ResultantComposite;

										LeftValue = largefixed2double(WaveTableStorDispGetFrame(
											Window->WaveTableData,LeftIndex,FrameScan));
										if (RightIndex < OriginalNumTables)
											{
												RightValue = largefixed2double(WaveTableStorDispGetFrame(
													Window->WaveTableData,RightIndex,FrameScan));
											}
										 else
											{
												RightValue = 0;
												ERROR(RightWeight != 0,PRERR(ForceAbort,
													"WaveTableWindowSetNewNumTables:  on last table, but right "
													"weight is non-zero."));
											}
										ResultantComposite = LeftValue * LeftWeight
											+ RightValue * RightWeight;
										WaveTableStorDispSetFrame(NewTable,TableScan,FrameScan,
											double2largefixed(ResultantComposite));
									}
							}
					}

				/* save it */
				Window->UndoBackupWaveTable = Window->WaveTableData;
				Window->WaveTableData = NewTable;
				WaveTableWindowUpdateAllParameters(Window);
				Window->WaveDataModified = True;
			}
	}


/* set the number of periods edit box to be the same as the data */
void								WaveTableWindowUpdateTableCountEdit(WaveTableWindowRec* Window)
	{
		char*							StringTemp;

		CheckPtrExistence(Window);
		StringTemp = IntegerToString(WaveTableObjectGetNumTables(Window->WaveTableObject));
		if (StringTemp == NIL)
			{
				return;
			}
		TextEditNewRawData(Window->NumTablesEdit,StringTemp,"\x0a");
		ReleasePtr(StringTemp);
		TextEditHasBeenSaved(Window->NumTablesEdit);
	}


/* set the number of frames edit box to be the same as the data */
void								WaveTableWindowUpdateFrameCountEdit(WaveTableWindowRec* Window)
	{
		char*							StringTemp;

		CheckPtrExistence(Window);
		StringTemp = IntegerToString(
			WaveTableStorDispNumFramesPerTable(Window->WaveTableData));
		if (StringTemp == NIL)
			{
				return;
			}
		TextEditNewRawData(Window->NumFramesEdit,StringTemp,"\x0a");
		ReleasePtr(StringTemp);
		TextEditHasBeenSaved(Window->NumFramesEdit);
	}


/* set all edit boxes to be the same as the underlying data */
void								WaveTableWindowUpdateAllParameters(WaveTableWindowRec* Window)
	{
		if (Window->CurrentlyVisibleTable
			> WaveTableObjectGetNumTables(Window->WaveTableObject) - 1)
			{
				Window->CurrentlyVisibleTable
					= WaveTableObjectGetNumTables(Window->WaveTableObject) - 1;
			}
		if (Window->CurrentlyVisibleTable < 0)
			{
				Window->CurrentlyVisibleTable = 0;
			}
		WaveTableWindowUpdateTableCountEdit(Window);
		WaveTableWindowUpdateFrameCountEdit(Window);
		WaveTableWindowUpdateScrollBar(Window);
		WaveTableWindowRedrawTable(Window);
	}


/* give a dialog box asking for a new number of frames, and then interpolate all */
/* of the wave periods. */
void								WaveTableWindowSetNewNumFrames(WaveTableWindowRec* Window)
	{
		long							OriginalNumFrames;
		long							NewNumFrames;
		WaveTableStorDispRec*	NewTable;
		long							NumberOfTables;
		long							TableScan;

		CheckPtrExistence(Window);
		OriginalNumFrames = WaveTableStorDispNumFramesPerTable(Window->WaveTableData);
		NewNumFrames = AskForNewWaveTableSize(OriginalNumFrames);

		if (NewNumFrames == OriginalNumFrames)
			{
				return;
			}

		/* dump the undo backup information */
		if (Window->UndoBackupWaveTable != NIL)
			{
				DisposeWaveTableStorDisp(Window->UndoBackupWaveTable);
				Window->UndoBackupWaveTable = NIL;
			}

		/* create a new table */
		NewTable = NewWaveTableStorDisp(WaveTableStorDispNumBits(Window->WaveTableData),
			NewNumFrames);
		if (NewTable == NIL)
			{
			 MemoryFailurePoint1:
				AlertHalt("There is not enough memory available to resample the wave table.",NIL);
				return;
			}
		NumberOfTables = WaveTableStorDispNumTables(Window->WaveTableData);
		for (TableScan = 0; TableScan < NumberOfTables; TableScan += 1)
			{
				if (!WaveTableStorDispAppendEntry(NewTable))
					{
					 MemoryFailurePoint2:
						DisposeWaveTableStorDisp(NewTable);
						goto MemoryFailurePoint1;
					}
			}

		/* this one handles interpolating between entries to expand the table */
		if (NewNumFrames > OriginalNumFrames)
			{
				long							ExpansionFactor;

				ExpansionFactor = NewNumFrames / OriginalNumFrames;
				for (TableScan = 0; TableScan < NumberOfTables; TableScan += 1)
					{
						long							NewFrameScan;

						for (NewFrameScan = 0; NewFrameScan < NewNumFrames; NewFrameScan += 1)
							{
								double						LeftWeight;
								double						RightWeight;
								long							LeftIndex;
								long							RightIndex;
								double						PrecisePositioning;
								double						LeftValue;
								double						RightValue;
								double						ResultantComposite;

								PrecisePositioning = (double)NewFrameScan / (double)NewNumFrames
									* (double)OriginalNumFrames;
								LeftIndex = (long)PrecisePositioning;
								RightIndex = LeftIndex + 1;
								RightWeight = PrecisePositioning - LeftIndex;
								LeftWeight = 1 - RightWeight;
								LeftValue = largefixed2double(WaveTableStorDispGetFrame(
									Window->WaveTableData,TableScan,LeftIndex));
								RightValue = largefixed2double(WaveTableStorDispGetFrame(
									Window->WaveTableData,TableScan,RightIndex % OriginalNumFrames));
								ResultantComposite = LeftValue * LeftWeight
									+ RightValue * RightWeight;
								WaveTableStorDispSetFrame(NewTable,TableScan,NewFrameScan,
									double2largefixed(ResultantComposite));
							}
					}
			}

		/* this one handles averaging the frames for table compression */
		else
			{
				long							FoldingFactor;

				FoldingFactor = OriginalNumFrames / NewNumFrames;
				for (TableScan = 0; TableScan < NumberOfTables; TableScan += 1)
					{
						long							NewFrameScan;

						for (NewFrameScan = 0; NewFrameScan < NewNumFrames; NewFrameScan += 1)
							{
								double						Accumulator;
								long							SumScan;
								double						Average;

								Accumulator = 0;
								for (SumScan = 0; SumScan < FoldingFactor; SumScan += 1)
									{
										Accumulator += largefixed2double(WaveTableStorDispGetFrame(
											Window->WaveTableData,TableScan,
											NewFrameScan * FoldingFactor + SumScan));
									}
								Average = Accumulator / FoldingFactor;
								WaveTableStorDispSetFrame(NewTable,TableScan,NewFrameScan,
									double2largefixed(Average));
							}
					}
			}

		Window->UndoBackupWaveTable = Window->WaveTableData;
		Window->WaveTableData = NewTable;
		WaveTableWindowUpdateAllParameters(Window);
		Window->WaveDataModified = True;
	}


/* argument list for wave table function thang */
static FunctionParamRec		ArgumentList[] =
	{
		{"frames",eInteger},
		{"tables",eInteger},
		{"data",eArrayOfFixed}
	};
#define ARGLISTLENGTH (sizeof(ArgumentList) / sizeof(ArgumentList[0]))


/* evaluate the wave formula and update the wave data */
void								WaveTableWindowEvaluateFunction(WaveTableWindowRec* Window)
	{
		char*								Blob;
		PcodeRec*						FuncCode;
		CompileErrors				Error;
		long								LineNumber;
		ParamStackRec*			ParamList;
		EvalErrors					OtherError;
		OpcodeRec*					ErrorOpcode;
		long								OffendingInstruction;
		DataTypes						ReturnType;
		long								TotalFramesPerTable;
		long								TotalNumTables;

		CheckPtrExistence(Window);

		/* bring the world up to date */
		if (!MainWindowMakeUpToDateFunctions(Window->MainWindow))
			{
				return;
			}

		/* prepare the text blob to be evaluated */
		Blob = TextEditGetRawData(Window->FunctionEdit,"\x0a");
		if (Blob == NIL)
			{
			 FailurePoint1:
				AlertHalt("There is not enough memory available to compile the expression.",NIL);
				return;
			}

		if (Window->UndoBackupWaveTable != NIL)
			{
				DisposeWaveTableStorDisp(Window->UndoBackupWaveTable);
				Window->UndoBackupWaveTable = NIL;
			}

		/* perform compilation */
		Error = CompileSpecialFunction(ArgumentList,ARGLISTLENGTH,&LineNumber,
			&ReturnType,Blob,&FuncCode);
		ReleasePtr(Blob);
		if (Error != eCompileNoError)
			{
				SetTextEditSelection(Window->FunctionEdit,LineNumber - 1,0,LineNumber,0);
				TextEditShowSelection(Window->FunctionEdit);
				AlertHalt("A compile error occurred:  _",GetCompileErrorString(Error));
				return;
			}

		/* try to evaluate the code */
		ParamList = NewParamStack();
		if (ParamList == NIL)
			{
			 SecondFailurePoint1:
				DisposePcode(FuncCode);
				AlertHalt("There is not enough memory available to evaluate the expression.",NIL);
				return;
			}
		TotalFramesPerTable = WaveTableWindowGetNumFramesPerTable(Window);
		TotalNumTables = WaveTableWindowGetNumTables(Window);
		/* add a space for the return value */
		if (!AddIntegerToStack(ParamList,0))
			{
			 SecondFailurePoint2:
				DisposeParamStack(ParamList);
				goto SecondFailurePoint1;
			}
		/* frames */
		if (!AddIntegerToStack(ParamList,TotalFramesPerTable))
			{
				goto SecondFailurePoint2;
			}
		/* tables */
		if (!AddIntegerToStack(ParamList,TotalNumTables))
			{
				goto SecondFailurePoint2;
			}
		/* data */
		{
			largefixedsigned*		TheDataBlock;
			long								TableScan;
			long								FrameScan;

			TheDataBlock = (largefixedsigned*)AllocPtrCanFail(sizeof(largefixedsigned)
				* TotalFramesPerTable * TotalNumTables,"WaveTableWindowEvaluateFunction: array");
			if (TheDataBlock == NIL)
				{
					goto SecondFailurePoint2;
				}
			for (TableScan = 0; TableScan < TotalNumTables; TableScan += 1)
				{
					for (FrameScan = 0; FrameScan < TotalFramesPerTable; FrameScan += 1)
						{
							PRNGCHK(TheDataBlock,&(TheDataBlock[TableScan * TotalFramesPerTable
								+ FrameScan]),sizeof(TheDataBlock[TableScan * TotalFramesPerTable
								+ FrameScan]));
							TheDataBlock[TableScan * TotalFramesPerTable + FrameScan]
								= WaveTableStorDispGetFrame(Window->WaveTableData,TableScan,FrameScan);
						}
				}
			if (!AddArrayToStack(ParamList,TheDataBlock))
				{
					ReleasePtr((char*)TheDataBlock);
					goto SecondFailurePoint2;
				}
		}

		/* executing the actual code */
		OtherError = EvaluatePcode(ParamList,FuncCode,
			Window->CodeCenter,&ErrorOpcode,&OffendingInstruction,Window->MainWindow,
			(SampleErrors (*)(void*,char*,largefixedsigned**))&MainWindowGetSampleLeftCopy,
			(SampleErrors (*)(void*,char*,largefixedsigned**))&MainWindowGetSampleRightCopy,
			(SampleErrors (*)(void*,char*,largefixedsigned**))&MainWindowGetSampleMonoCopy,
			(SampleErrors (*)(void*,char*,long*))&MainWindowGetWaveTableFrameCount,
			(SampleErrors (*)(void*,char*,long*))&MainWindowGetWaveTableTableCount,
			(SampleErrors (*)(void*,char*,largefixedsigned**))&MainWindowGetWaveTableArray,
			Window->MainWindow,
			(struct InteractionWindowRec* (*)(void*))&MainWindowGetInteractionWindow);
		if (OtherError != eEvalNoError)
			{
				char*					FuncNameString;
				FuncCodeRec*	ErrorFunction;
				MyBoolean			SuccessFlag;

				/* present error message */
				SuccessFlag = False;
				ErrorFunction = GetFunctionFromOpcode(Window->CodeCenter,ErrorOpcode);
				if (ErrorFunction == NIL)
					{
						FuncNameString = StringToBlockCopy("<anonymous>");
					}
				 else
					{
						FuncNameString = CopyPtr(GetFunctionName(ErrorFunction));
					}
				if (FuncNameString != NIL)
					{
						char*					Key;

						Key = StringToBlockCopy("_");
						if (Key != NIL)
							{
								char*					BaseMessage;

								BaseMessage = StringFromRaw("Error in function _, instruction _:  _");
								if (BaseMessage != NIL)
									{
										char*					FixedMessage1;

										FixedMessage1 = ReplaceBlockCopy(BaseMessage,Key,FuncNameString);
										if (FixedMessage1 != NIL)
											{
												char*					NumberStr;

												NumberStr = IntegerToString(OffendingInstruction);
												if (NumberStr != NIL)
													{
														char*					FixedMessage2;

														FixedMessage2 = ReplaceBlockCopy(FixedMessage1,Key,NumberStr);
														if (FixedMessage2 != NIL)
															{
																AlertHalt(FixedMessage2,GetPcodeErrorMessage(OtherError));
																SuccessFlag = True;
																ReleasePtr(FixedMessage2);
															}
														ReleasePtr(NumberStr);
													}
												ReleasePtr(FixedMessage1);
											}
										ReleasePtr(BaseMessage);
									}
								ReleasePtr(Key);
							}
						ReleasePtr(FuncNameString);
					}
				if (!SuccessFlag)
					{
						AlertHalt("There is not enough memory available to show "
							"the compile error message.",NIL);
					}
				DisposeParamStack(ParamList);
				DisposePcode(FuncCode);
				return;
			}

		/* get the data out */
		{
			largefixedsigned*			DataBlock;
			WaveTableStorDispRec*	NewWaveTable;
			long									TableScan;
			long									FrameScan;

			DataBlock = (largefixedsigned*)GetStackArray(ParamList,3);
			CheckPtrExistence((char*)DataBlock);
			NewWaveTable = NewWaveTableStorDisp(WaveTableStorDispNumBits(
				Window->WaveTableData),TotalFramesPerTable);
			if (NewWaveTable == NIL)
				{
				 FinishedSideNoMemory1:
					AlertHalt("There is not enough memory available to build the wave.",NIL);
					DisposeParamStack(ParamList);
					DisposePcode(FuncCode);
					return;
				}
			for (TableScan = 0; TableScan < TotalNumTables; TableScan += 1)
				{
					if (!WaveTableStorDispAppendEntry(NewWaveTable))
						{
						 FinishedSideNoMemory2:
							DisposeWaveTableStorDisp(NewWaveTable);
							goto FinishedSideNoMemory1;
						}
				}
			for (TableScan = 0; TableScan < TotalNumTables; TableScan += 1)
				{
					for (FrameScan = 0; FrameScan < TotalFramesPerTable; FrameScan += 1)
						{
							PRNGCHK(DataBlock,&(DataBlock[TableScan * TotalFramesPerTable + FrameScan]),
								sizeof(DataBlock[TableScan * TotalFramesPerTable + FrameScan]));
							WaveTableStorDispSetFrame(NewWaveTable,TableScan,FrameScan,
								DataBlock[TableScan * TotalFramesPerTable + FrameScan]);
						}
				}
			Window->UndoBackupWaveTable = Window->WaveTableData;
			Window->WaveTableData = NewWaveTable;
			Window->WaveDataModified = True;
		}
		DisposeParamStack(ParamList); /* disposes our array for us */
		DisposePcode(FuncCode);
		WaveTableWindowUpdateAllParameters(Window);
	}


/* get a copy of the wave data array */
largefixedsigned*		WaveTableWindowGetWaveArray(WaveTableWindowRec* Window)
	{
		largefixedsigned*	Array;
		long							TableLimit;
		long							FrameLimit;
		long							TableScan;

		CheckPtrExistence(Window);
		TableLimit = WaveTableStorDispNumTables(Window->WaveTableData);
		FrameLimit = WaveTableStorDispNumFramesPerTable(Window->WaveTableData);
		Array = (largefixedsigned*)AllocPtrCanFail(TableLimit * FrameLimit
			* sizeof(largefixedsigned),"WaveTableWindowGetWaveArray");
		if (Array != NIL)
			{
				for (TableScan = 0; TableScan < TableLimit; TableScan += 1)
					{
						long							FrameScan;

						for (FrameScan = 0; FrameScan < FrameLimit; FrameScan += 1)
							{
								PRNGCHK(Array,&(Array[TableScan * FrameLimit + FrameScan]),
									sizeof(Array[TableScan * FrameLimit + FrameScan]));
								Array[TableScan * FrameLimit + FrameScan]
									= WaveTableStorDispGetFrame(Window->WaveTableData,
									TableScan,FrameScan);
							}
					}
			}
		return Array;
	}


/* force the wave data to be written back to the object.  this does not write back */
/* any other data. */
MyBoolean						WaveTableWindowForceWaveTableUpdate(WaveTableWindowRec* Window)
	{
		WaveTableStorageRec*		NewWaveTable;

		CheckPtrExistence(Window);

		NewWaveTable = NewWaveTableStorage(WaveTableStorDispNumBits(Window->WaveTableData),
			WaveTableStorDispNumFramesPerTable(Window->WaveTableData));
		if (NewWaveTable != NIL)
			{
				long										Limit;
				long										Scan;

				Limit = WaveTableStorDispNumTables(Window->WaveTableData);
				for (Scan = 0; Scan < Limit; Scan += 1)
					{
						long							Index;
						long							IndexLimit;

						if (!WaveTableStorageAppendEntry(NewWaveTable))
							{
								DisposeWaveTableStorage(NewWaveTable);
								return False;
							}
						IndexLimit = WaveTableStorDispNumFramesPerTable(Window->WaveTableData);
						for (Index = 0; Index < IndexLimit; Index += 1)
							{
								WaveTableStorageSetFrame(NewWaveTable,Scan,Index,
									WaveTableStorDispGetFrame(Window->WaveTableData,Scan,Index));
							}
					}
				WaveTableObjectPutNewData(Window->WaveTableObject,NewWaveTable);
				return True;
			}
		 else
			{
				return False;
			}
	}


/* force all data to be written back to the object.  this includes calling */
/* WaveTableWindowForceWaveTableUpdate() */
MyBoolean						WaveTableWindowWritebackModifiedData(WaveTableWindowRec* Window)
	{
		MyBoolean					SuccessFlag = True;

		CheckPtrExistence(Window);

		if (TextEditDoesItNeedToBeSaved(Window->NameEdit))
			{
				char*						String;

				String = WaveTableWindowGetNameCopy(Window);
				if (String != NIL)
					{
						WaveTableObjectNewName(Window->WaveTableObject,String);
						TextEditHasBeenSaved(Window->NameEdit);
					}
				 else
					{
						SuccessFlag = False;
					}
			}

		if (TextEditDoesItNeedToBeSaved(Window->FunctionEdit))
			{
				char*						String;

				String = WaveTableWindowGetFormulaCopy(Window);
				if (String != NIL)
					{
						WaveTableObjectNewFormula(Window->WaveTableObject,String);
						TextEditHasBeenSaved(Window->FunctionEdit);
					}
				 else
					{
						SuccessFlag = False;
					}
			}

		if (TextEditDoesItNeedToBeSaved(Window->TestAttackDuration))
			{
				char*						String;

				String = TextEditGetRawData(Window->TestAttackDuration,SYSTEMLINEFEED);
				if (String != NIL)
					{
						SetWaveTableObjectTestAttack(Window->WaveTableObject,
							StringToLongDouble(String,PtrSize(String)));
						ReleasePtr(String);
						TextEditHasBeenSaved(Window->TestAttackDuration);
					}
				 else
					{
						SuccessFlag = False;
					}
			}

		if (TextEditDoesItNeedToBeSaved(Window->TestDecayDuration))
			{
				char*						String;

				String = TextEditGetRawData(Window->TestDecayDuration,SYSTEMLINEFEED);
				if (String != NIL)
					{
						SetWaveTableObjectTestDecay(Window->WaveTableObject,
							StringToLongDouble(String,PtrSize(String)));
						ReleasePtr(String);
						TextEditHasBeenSaved(Window->TestDecayDuration);
					}
				 else
					{
						SuccessFlag = False;
					}
			}

		if (TextEditDoesItNeedToBeSaved(Window->TestFrequency))
			{
				char*						String;

				String = TextEditGetRawData(Window->TestFrequency,SYSTEMLINEFEED);
				if (String != NIL)
					{
						SetWaveTableObjectTestPitch(Window->WaveTableObject,
							StringToLongDouble(String,PtrSize(String)));
						ReleasePtr(String);
						TextEditHasBeenSaved(Window->TestFrequency);
					}
				 else
					{
						SuccessFlag = False;
					}
			}

		if (TextEditDoesItNeedToBeSaved(Window->TestSamplingRate))
			{
				char*						String;

				String = TextEditGetRawData(Window->TestSamplingRate,SYSTEMLINEFEED);
				if (String != NIL)
					{
						SetWaveTableObjectTestSamplingRate(Window->WaveTableObject,
							StringToInteger(String,PtrSize(String)));
						ReleasePtr(String);
						TextEditHasBeenSaved(Window->TestSamplingRate);
					}
				 else
					{
						SuccessFlag = False;
					}
			}

		if (Window->WaveDataModified)
			{
				if (WaveTableWindowForceWaveTableUpdate(Window))
					{
						Window->WaveDataModified = False;
					}
				 else
					{
						SuccessFlag = False;
					}
			}

		return SuccessFlag;
	}
