
#define MAXWIDTH  640
#define MAXHEIGHT 480

#define NUMFIELDS 19
#define NUMWAVES 12

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>

#include <exec/types.h>
#include <exec/memory.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/cybergraphics.h>
#include <proto/icon.h>
#include <proto/keymap.h>
#include <devices/timer.h>
#include <proto/timer.h>
#include <exec/devices.h>

#include <workbench/startup.h>

#include <dos/dostags.h>
#include <graphics/gfxbase.h>
#include <cybergraphx/cybergraphics.h>

#ifdef __SASC
struct Library *PPCLibBase;
#include <PowerUP/PPCLib/tasks.h>
ULONG	PPCCallOS(struct Caos*);
ULONG	PPCSetTaskAttrs(void*, struct TagItem*);
#define	PPCSetTaskAttrs(TaskObject, Tags)	_PPCSetTaskAttrs(PPC_BASE_NAME, TaskObject, Tags)
static __inline ULONG _PPCSetTaskAttrs(void *PPCLibBase, void*TaskObject, struct TagItem*Tags) {
	struct Caos	MyCaos;
	MyCaos.M68kCacheMode	=	IF_CACHEFLUSHALL;
	MyCaos.PPCCacheMode	=	IF_CACHEFLUSHALL;
	MyCaos.a0		=(ULONG) TaskObject;
	MyCaos.a1		=(ULONG) Tags;
	MyCaos.caos_Un.Offset	=	(-192);
	MyCaos.a6		=(ULONG) PPCLibBase;	
	return((ULONG)PPCCallOS(&MyCaos));
}
#endif

#include "TrackInfo.h"

BOOL PluginInit(int argc, char **argv);
void PluginExit(void);
void PluginLoop(void);
void ShowRequester(char *Text, char *Button);
struct MsgPort *MyCreatePort(UBYTE *name, LONG pri);
void MyDeletePort(struct MsgPort *mp);

/***************************************************************************/
/* This is the global variables section. Don't change anything here unless */
/* you know what you're doing!                                             */
/***************************************************************************/

BYTE             PluginSignal, InfoSignal, ConfigSignal;
ULONG            PluginMask,   InfoMask,   ConfigMask;
BOOL             Accepted;
struct Process   *PluginTask;
struct MsgPort   *PluginMP;
struct MsgPort   *PluginRP;
struct TrackInfo *tinfo;

#ifdef __SASC
struct Library     *TimerBase;
#else
struct Device      *TimerBase;
#endif
struct MsgPort     *TimerMP;
struct timerequest *TimerIO = NULL;
struct EClockVal   ev1;
struct EClockVal   ev2;
BYTE               TimerError = -1;
ULONG              EFreq=0;
ULONG              TimerMask;

UWORD	TrackInfoPos = 1;

UWORD	*PluginRawL;
UWORD	*PluginRawR;
WORD	*PluginSamples;
UWORD	*SpecRawL;
UWORD	*SpecRawR;
WORD *SampleRaw;

struct PluginMessage {
	struct Message   msg;
	ULONG            PluginMask;
	struct Process   *PluginTask;
	UWORD            **SpecRawL;
	UWORD            **SpecRawR;
	UWORD            Accepted;
	UWORD            reserved0;
	ULONG            InfoMask;
	struct TrackInfo **tinfo;
	ULONG            ConfigMask;
	WORD             **SampleRaw;
};

BOOL OpenTimer(void);
void CloseTimer(void);
void StartTimer(void);

/***************************************************************************/
/* This is the main part. Again, don't change anything here if you haven't */
/* got a very good reason to do so!                                        */
/***************************************************************************/

int main(int argc, char **argv) {
	struct PluginMessage *PluginMsg;
	struct PluginMessage *ReplyMsg;

	/* Allocate all user resources */
	if(PluginInit(argc, argv)) {
		/* Check if a plugin capable instance of AmigaAMP is running */
		if(PluginMP=FindPort("AmigaAMP plugin port"))	{
			/* Allocate some sigbits for receiving signals FROM AmigaAMP */
			PluginSignal = AllocSignal(-1);
			ConfigSignal = AllocSignal(-1);
			InfoSignal   = AllocSignal(-1);
			PluginTask   = (struct Process *)FindTask(NULL);

			if(PluginSignal != -1 && InfoSignal != -1 && PluginSignal != -1) {
				PluginMask = 1L << PluginSignal;
				ConfigMask = 1L << ConfigSignal;
				InfoMask   = 1L << InfoSignal;

				/* Allocate a message and reply port for sending messages TO AmigaAMP */
				#ifdef __SASC
				PluginMsg=PPCAllocVec(sizeof(struct PluginMessage), MEMF_PUBLIC|MEMF_CLEAR);
				PluginRP=(struct MsgPort*)MyCreatePort(0,0);
				#else
				PluginMsg=AllocVecPPC(sizeof(struct PluginMessage), MEMF_PUBLIC|MEMF_CLEAR, 0);
				PluginRP=(struct MsgPort*)CreatePort(0,0);
				#endif

				/* Tell AmigaAMP all the details it needs to know */
				PluginMsg->msg.mn_Node.ln_Type = NT_MESSAGE;
				PluginMsg->msg.mn_Length       = sizeof(struct PluginMessage);
				PluginMsg->msg.mn_ReplyPort    = PluginRP;
				PluginMsg->PluginMask          = PluginMask;
				PluginMsg->PluginTask          = PluginTask;
				PluginMsg->SpecRawL            = &SpecRawL;
				PluginMsg->SpecRawR            = &SpecRawR;
				PluginMsg->InfoMask            = InfoMask;
				PluginMsg->tinfo               = &tinfo;
				PluginMsg->ConfigMask          = ConfigMask;
				PluginMsg->SampleRaw           = &SampleRaw;
				PutMsg(PluginMP, (struct Message *)PluginMsg);
				/* Wait for a reply */
				WaitPort(PluginRP);
				/* Let's see if AmigaAMP accepted our registration attempt */
				if(ReplyMsg = (struct PluginMessage *)GetMsg(PluginRP)) Accepted=ReplyMsg->Accepted;
				else Accepted=FALSE;

				if(Accepted) {
					/* If it did, start the plugin loop */
					PluginLoop();

					/* Tell AmigaAMP that this plugin is going down */
					PluginMsg->PluginMask          = NULL;
					PluginMsg->PluginTask          = NULL;
					PluginMsg->SpecRawL            = NULL;
					PluginMsg->SpecRawR            = NULL;
					PluginMsg->ConfigMask          = NULL;
					PluginMsg->InfoMask            = NULL;
					PluginMsg->tinfo               = NULL;
					PluginMsg->SampleRaw           = NULL;
					PutMsg(PluginMP, (struct Message *)PluginMsg);
					/* Wait for confirmation before going on! */
					WaitPort(PluginRP);
					GetMsg(PluginRP);
					/* Now that AmigaAMP knows that we're gone, we can quit */
				}
				else {
					/* If AmigaAMP didn't accept us, tell the user about it */
					ShowRequester("Plugin rejected by AmigaAMP!\nPerhaps there's another one running.", "Abort");
				}

				/* Free all resources */
				#ifdef __SASC
					PPCFreeVec(PluginMsg);
					MyDeletePort(PluginRP);
				#else
					FreeVecPPC(PluginMsg);
					DeletePort(PluginRP);
				#endif
			}
			else {
				ShowRequester("Signal allocation failure!", "Abort");
			}
			if(PluginSignal != -1) FreeSignal(PluginSignal);
			if(ConfigSignal != -1) FreeSignal(ConfigSignal);
			if(InfoSignal   != -1) FreeSignal(InfoSignal);
		}
		else {
			ShowRequester("Could not find message port!\nAmigaAMP probably not running.", "Abort");
		}
	}
	else {
		ShowRequester("Plugin initialisation failed!", "Ok");
	}
	/* Free all user resources */
	PluginExit();
}

/*****************************************************************************/
/* Ok, now for the individual plugin sourcecode. Everything below should be  */
/* changed to your needs.                                                    */
/* Just like before, we start with the global variables section              */
/*****************************************************************************/

void InitWave(UBYTE WaveNum, ULONG BufNum);
void CalcWave(UBYTE WaveNum, ULONG BufNum);
void DrawWave(LONG *WaveX, LONG *WaveY, ULONG Width, ULONG Height, UBYTE WaveNum, ULONG BufNum);

void InitField(LONG *Zoom, ULONG Width, ULONG Height);
void PrepareConstants(UBYTE FieldNum);
void CalcLine(LONG *Zoom, ULONG Width, ULONG Height, UBYTE FieldNum);
void MakeCMap(void);
void Blur(UBYTE *PixelC, UBYTE *PixelD);
void ReColor(void);
void MakeTitle(void);
float rnd(float max);
float sgn(float val);

const char VersionString[]="\0$VER: Subspace 1.1 (10.11.00)";
const char Line1[]="Subspace";
const char Line2[]="by Thomas Wenzel";

#define PRECISION PRECISION_EXACT

struct IntuitionBase *IntuitionBase = NULL;
struct Library *IconBase     = NULL;
struct Library *KeymapBase   = NULL;
struct Library *AslBase      = NULL;
struct Library *GadToolsBase = NULL;
struct GfxBase *GfxBase      = NULL;
struct Library *CyberGfxBase = NULL;
struct Screen  *PluginScreen = NULL;
struct Window  *PluginWin    = NULL;
struct RastPort *rp;
struct ViewPort *vp;

struct EClockVal ev1;
struct EClockVal ev2;


ULONG ModeID;
ULONG WinMask;

UBYTE *PixelD;
UBYTE *PixelC;
ULONG *Colour;

UWORD *EmptyPointer;

char InfoLine[256];

LONG Zoom[MAXWIDTH*MAXHEIGHT];
LONG Zoom2[MAXWIDTH*MAXHEIGHT];
LONG WaveX[MAXWIDTH*2];
LONG WaveY[MAXWIDTH*2];
LONG WaveX2[MAXWIDTH*2];
LONG WaveY2[MAXWIDTH*2];

ULONG MidR[4], MidG[4], MidB[4];
ULONG DisplayFPS;
ULONG ShowWave;
ULONG LimitFPS;
ULONG ScreenSize = 100;
ULONG Width      = 320;
ULONG Height     = 240;
ULONG HalfHeight = 120;
ULONG Cutoff     = 1;
ULONG InfoCount;
ULONG FrameCountLow;
ULONG FrameCountHigh;
ULONG FrameCountWave;
ULONG FrameCountField;
ULONG TimeWave;
ULONG DestWaveNum;
ULONG DestWaveBuffer;
float WaveMorph;
ULONG MidRdst[3], MidGdst[3], MidBdst[3];
ULONG LineToCalc;
UBYTE FieldToCalc;
UBYTE WaveToCalc, WaveToCalc0, WaveToCalc1;
UBYTE PrevField;

/****************************************************************************/
/* This function will be called once when the plugin is started. You should */
/* allocate all needed resources here. Return TRUE if all went well.        */
/****************************************************************************/

BOOL PluginInit(int argc, char **argv) {
	long i,xs,ys,xd,yd,ysp,ydp;
	float xf,yf;
	UBYTE **ttypes;
	char *str;
	struct DiskObject *IconObj;
	struct WBStartup  *WBStart;
	#ifdef __SASC
	void *ThisTask;
	#else
	struct TaskPPC *ThisTask;
	#endif
	WORD Length;

	struct TagItem ModeIDtags[] = {
		CYBRBIDTG_Depth, 8, CYBRBIDTG_NominalWidth, 320, CYBRBIDTG_NominalHeight, 240, TAG_DONE
	};

	AslBase=OpenLibrary("asl.library", 0);
	if(!AslBase) {
		ShowRequester("Can't open asl.library!", "Abort");
		return(FALSE);
	}

	IconBase=OpenLibrary("icon.library", 0);
	if(!IconBase) {
		ShowRequester("Can't open icon.library!", "Abort");
		return(FALSE);
	}

	KeymapBase=OpenLibrary("keymap.library", 0);
	if(!KeymapBase) {
		ShowRequester("Can't open keymap.library!", "Abort");
		return(FALSE);
	}

	IntuitionBase=(struct IntuitionBase*)OpenLibrary("intuition.library", 0);
	if(!IntuitionBase) {
		ShowRequester("Can't open intuition.library!", "Abort");
		return(FALSE);
	}

	GadToolsBase=OpenLibrary("gadtools.library", 0);
	if(!GadToolsBase) {
		ShowRequester("Can't open gadtools.library!", "Abort");
		return(FALSE);
	}

	GfxBase=(struct GfxBase*)OpenLibrary("graphics.library", 0);
	if(!GfxBase) {
		ShowRequester("Can't open graphics.library!", "Abort");
		return(FALSE);
	}

	if(GfxBase->LibNode.lib_Version < 39) {
		ShowRequester("This plugin requires AmigaOS 3.0 or greater!", "Abort");
		return(FALSE);
	}

	CyberGfxBase=OpenLibrary("cybergraphics.library", 40);
	if(!CyberGfxBase) {
		ShowRequester("This plugin requires CyberGraphX v3 or higher!", "Abort");
		return(FALSE);
	}

	#ifdef __SASC
		/* Increase the priority of the 68k mirror task */
		SetTaskPri(FindTask(NULL), 15);
		/* Increase our own priority (if it only would work...) */
		PPCSetTaskAttr(PPCTASKTAG_PRIORITY, 15);
	#else
		/* Increase our own priority */
		ThisTask = FindTaskPPC(NULL);
		SetNiceValue(ThisTask, -15);
	#endif

	WBStart=(struct WBStartup *)argv;

// if(argc==0) printf("IconName: %s\n", WBStart->sm_ArgList[0].wa_Name);

	if(argc==0) IconObj=GetDiskObject(WBStart->sm_ArgList[0].wa_Name);
	else        IconObj=GetDiskObject(argv[0]);

	LimitFPS = 999;

	if(IconObj) {
		if(str=FindToolType(IconObj->do_ToolTypes,"MAXFPS")) {
			LimitFPS = atol(str);
		}
		if(str=FindToolType(IconObj->do_ToolTypes,"RESOLUTION")) {
			if(stricmp(str,"high") == 0) {
				Width      = 640;
				Height     = 480;
				ScreenSize = 66;
			}
			else {
				Width      = 320;
				Height     = 240;
				ScreenSize = 100;
			}
		}
		if(str=FindToolType(IconObj->do_ToolTypes,"SCREENSIZE")) {
			ScreenSize = atol(str);
		}
		FreeDiskObject(IconObj);
	}

	if(ttypes=(UBYTE**)ArgArrayInit(argc, argv)) {
		str=(char*)ArgString(ttypes, "MAXFPS", "0");
		LimitFPS = atol(str);

		str=(char*)ArgString(ttypes, "RESOLUTION", "low");
		if(stricmp(str,"high") == 0) {
			Width      = 640;
			Height     = 480;
			ScreenSize = 66;
			ShowWave   = 1;
		}
		else {
			Width      = 320;
			Height     = 240;
			ScreenSize = 100;
			ShowWave   = 0;
		}

		if(str=(char*)ArgString(ttypes, "SCREENSIZE", NULL)) {
			ScreenSize = atol(str);
		}

		ArgArrayDone();
	}

	if(LimitFPS == 0)  LimitFPS = 999;
	if(LimitFPS > 999) LimitFPS = 999;

	if(ScreenSize <  25) ScreenSize =  25;
	if(ScreenSize > 100) ScreenSize = 100;

	Cutoff = Height * (100-ScreenSize) / 200;
	if(Cutoff <   1) Cutoff =   1;
	if(Cutoff > 200) Cutoff = 200;

	ModeIDtags[1].ti_Data = Width;
	ModeIDtags[2].ti_Data = Height;

	HalfHeight = Height/2;

	#ifdef __SASC
		PixelD       = PPCAllocVec(Width*Height, MEMF_PUBLIC|MEMF_CLEAR);
		PixelC       = PPCAllocVec(Width*Height, MEMF_PUBLIC|MEMF_CLEAR);
		Colour       = PPCAllocVec(770*4,        MEMF_PUBLIC|MEMF_CLEAR);
		EmptyPointer = PPCAllocVec(512,          MEMF_CHIP|MEMF_CLEAR);
	#else
		PixelD       = AllocVecPPC(Width*Height, MEMF_PUBLIC|MEMF_CLEAR, 0);
		PixelC       = AllocVecPPC(Width*Height, MEMF_PUBLIC|MEMF_CLEAR, 0);
		Colour       = AllocVecPPC(770*4,        MEMF_PUBLIC|MEMF_CLEAR, 0);
		EmptyPointer = AllocVecPPC(512,          MEMF_CHIP|MEMF_CLEAR,   0);
	#endif

	OpenTimer();

	if(PixelD==NULL || PixelC==NULL) {
		ShowRequester("Out of memory for graphics buffers!", "Abort");
		return(FALSE);
	}

	srand(time(NULL));

	MidR[0] = 255;
	MidG[0] = 255;
	MidB[0] = 127;

	MidR[1] = 170;
	MidG[1] = 170;
	MidB[1] = 64;

	MidR[2] = 85;
	MidG[2] = 85;
	MidB[2] = 32;

	MidR[3] = 0;
	MidG[3] = 0;
	MidB[3] = 0;

	MidRdst[0] = (UBYTE)(rand()>>25) + 127;
	MidGdst[0] = (UBYTE)(rand()>>25) + 127;
	MidBdst[0] = (UBYTE)(rand()>>25) + 127;

	MidRdst[1] = (UBYTE)(rand()>>25) + 96;
	MidGdst[1] = (UBYTE)(rand()>>25) + 96;
	MidBdst[1] = (UBYTE)(rand()>>25) + 96;

	MidRdst[2] = (UBYTE)(rand()>>25) + 64;
	MidGdst[2] = (UBYTE)(rand()>>25) + 64;
	MidBdst[2] = (UBYTE)(rand()>>25) + 64;

	DisplayFPS      = 0;
	InfoCount       = 0;
	FrameCountLow   = 0;
	FrameCountHigh  = 0;
	FrameCountWave  = 0;
	FrameCountField = 0;
	TimeWave        = 0;
	DestWaveNum     = 1;
	DestWaveBuffer  = 1;
	WaveMorph       = 0.0;
	LineToCalc      = 0;
	WaveToCalc      = 0;
	WaveToCalc0     = 0;

	FieldToCalc     = (UBYTE)(rand()>>24) % NUMFIELDS;
	WaveToCalc1     = (UBYTE)(rand()>>24) % NUMWAVES;
//	FieldToCalc   = NUMFIELDS-1;
//	WaveToCalc1   = NUMWAVES-1;


	PrepareConstants(FieldToCalc);

	MakeCMap();

	ModeID=BestCModeIDTagList(ModeIDtags);
	if(ModeID == INVALID_ID) {
		ShowRequester("Could not find suitable screen mode!", "Abort");
		return(FALSE);
	}

	PluginScreen=OpenScreenTags(NULL, SA_DisplayID, ModeID, SA_Depth, 8, SA_Width, Width, SA_Height, Height, SA_SharePens, TRUE, SA_ShowTitle, FALSE, SA_Quiet, TRUE, TAG_DONE);
	if(PluginScreen == NULL) {
		ShowRequester("Could not open screen!", "Abort");
		return(FALSE);
	}
	rp = &PluginScreen->RastPort;
	vp = &PluginScreen->ViewPort;

	LoadRGB32(vp, Colour);

	PluginWin=OpenWindowTags(NULL, WA_CustomScreen, (ULONG)PluginScreen,
	                               WA_Left,         0,
	                               WA_Top,          0,
	                               WA_Width,        Width,
	                               WA_Height,       Height,
	                               WA_SizeGadget,   FALSE,
	                               WA_DragBar,      FALSE,
	                               WA_DepthGadget,  FALSE,
	                               WA_CloseGadget,  FALSE,
	                               WA_Backdrop,     TRUE,
	                               WA_Borderless,   TRUE,
	                               WA_Activate,     TRUE,
	                               WA_AutoAdjust,   TRUE,  // Just to be sure :-)
	                               WA_IDCMP,        IDCMP_RAWKEY | IDCMP_ACTIVEWINDOW | IDCMP_INACTIVEWINDOW,
	                               TAG_DONE);
	if(PluginWin == NULL) {
		ShowRequester("Could not open window!", "Abort");
		return(FALSE);
	}

	SetPointer(PluginWin, EmptyPointer, 1, 1, 0, 0);
	SetAPen(rp, 1);
	RectFill(rp, 0, 0, (Width-1), (Height-1));
	SetAPen(rp, 255);

	WinMask = 1L << PluginWin->UserPort->mp_SigBit;

	InitField(Zoom, Width, Height);
	MakeTitle();

	return(TRUE);
}

/******************************************************************************/
/* This function will be called when the plugin is shut down. You should free */
/* all previously allocated resources here.                                   */
/******************************************************************************/

void PluginExit(void) {
	long i;

	if(PluginWin)    CloseWindow(PluginWin);
	if(PluginScreen) CloseScreen(PluginScreen);
	#ifdef __SASC
		if(PixelD)       PPCFreeVec(PixelD);
		if(PixelC)       PPCFreeVec(PixelC);
		if(Colour)       PPCFreeVec(Colour);
		if(EmptyPointer) PPCFreeVec(EmptyPointer);
	#else
		if(PixelD)       FreeVecPPC(PixelD);
		if(PixelC)       FreeVecPPC(PixelC);
		if(Colour)       FreeVecPPC(Colour);
		if(EmptyPointer) FreeVecPPC(EmptyPointer);
	#endif

	CloseTimer();

	if(CyberGfxBase)  CloseLibrary(CyberGfxBase);
	if(GfxBase)       CloseLibrary(GfxBase);
	if(AslBase)       CloseLibrary(AslBase);
	if(IconBase)      CloseLibrary(IconBase);
	if(KeymapBase)    CloseLibrary(KeymapBase);
	if(GadToolsBase)  CloseLibrary(GadToolsBase);
	if(IntuitionBase) CloseLibrary(IntuitionBase);
}

/*******************************************************************************/
/* This is the main Plugin Loop. It will receive a signal matching PluginMask  */
/* each time new spectral data is ready. The data is stored in two arrays,     */
/* UWORD Spec[512] in structures DataL and DataR. The scale is logarithmic,    */
/* 0 means below -96dB, 65535 means 0dB.                                       */
/* No matter how long it takes until your plugin actually processes the data,  */
/* the memory referenced by the array pointers always remains valid!           */
/*                                                                             */
/* Your plugin loop MUST quit when it receives SIGBREAKF_CTRL_C. If you've     */
/* opened a window you should react to the close gadget as well. For full      */
/* screen plugins I strongly recommend checking the ESC key.                   */
/*******************************************************************************/

void PluginLoop(void) {
	struct TextExtent te;
	ULONG *PixelL;
	UBYTE *ActPixel;
	ULONG Signals;
	LONG i,j,k;
	LONG l,r;
	LONG x,y;
	UBYTE v;
	UWORD w;
	UBYTE ColorMode;

	ULONG	hi,lo,itime,eclock;
	char FPStext[64];

	UBYTE Vanilla;
	struct InputEvent ie;
	WORD	RawLen;

	Wait(SIGBREAKF_CTRL_C | PluginMask);

	/* Make a backup of the data pointers */
	PluginRawL    = SpecRawL;
	PluginRawR    = SpecRawR;
	PluginSamples = SampleRaw;

	WritePixelArray(PixelC, 0, 0, Width, rp, 0, 0, Width, Height, RECTFMT_LUT8);
	Delay(100);

	StartTimer();

	InitWave(WaveToCalc0, 0);
	InitWave(WaveToCalc1, 1);

	for(;;) {
		/* Wait until there's something to do */

		Signals=Wait(SIGBREAKF_CTRL_C | PluginMask | ConfigMask | InfoMask | WinMask | TimerMask);
		if(Signals & TimerMask) {
			WaitIO((struct IORequest *) TimerIO);
		}

		/* Break received -> quit at once! */
		if(Signals & SIGBREAKF_CTRL_C) break;

		/* Window close gadget hit -> quit as well! */
		if(Signals & WinMask) {
			struct IntuiMessage *imsg;
			BOOL done=0;

			while(imsg=(struct IntuiMessage *)GetMsg(PluginWin->UserPort)) {
				if(imsg->Class == IDCMP_RAWKEY) {
					ie.ie_Class        = IECLASS_RAWKEY;
					ie.ie_SubClass     = 0;
					ie.ie_Code         = imsg->Code;
					ie.ie_Qualifier    = 0;
					ie.ie_EventAddress = imsg->IAddress;
					RawLen = MapRawKey(&ie, &Vanilla, 1, 0);

					switch(imsg->Code) {
						case 0x45: done=1; break;
					}

					if(RawLen==1) {
						switch(Vanilla) {
							case 'f': DisplayFPS = 1-DisplayFPS; break;
							case 'w': ShowWave   = 1-ShowWave;   break;
							case 'i': InfoCount  = 0;            break;
//						case '+': Cutoff--;                  break;
//						case '-': Cutoff++;                  break;
						}
						if(Cutoff < 1) Cutoff =   1;
						if(Cutoff > HalfHeight-10) Cutoff = HalfHeight-10;
						for(i=0; i<Width*Cutoff; i++) {
							PixelC[i]=PixelD[i]=0;
						}
						for(i=Width*(Height-Cutoff); i<Width*Height; i++) {
							PixelC[i]=PixelD[i]=0;
						}
					}
				}
				ReplyMsg(imsg);
			}
			if(done) break;
		}

		if(Signals & TimerMask) {
			StartTimer();

      Blur(PixelC, PixelD);

			FrameCountLow++;
			if(FrameCountLow > 4) {
				FrameCountLow = 0;

				eclock=ReadEClock(&ev2);
				hi = ev2.ev_hi - ev1.ev_hi;
				lo = ev2.ev_lo - ev1.ev_lo;
				itime = hi*eclock+lo;

				if(itime==0) itime=1;

				ev1.ev_hi = ev2.ev_hi;
				ev1.ev_lo = ev2.ev_lo;
				ReColor();
			}

			FrameCountHigh++;
			if(FrameCountHigh > 600) {
				FrameCountHigh = 0;
				ColorMode = (UBYTE)(rand()>>24) % 10;
//			printf("ColorMode: %d\n", ColorMode);
				switch(ColorMode) {
					case 0:
					case 1:
						MidRdst[0] = (UBYTE)(rand()>>25) + 127;
						MidGdst[0] = (UBYTE)(rand()>>25) + 127;
						MidBdst[0] = (UBYTE)(rand()>>25) + 127;

						MidRdst[1] = (UBYTE)(rand()>>24) + 0;
						MidGdst[1] = (UBYTE)(rand()>>24) + 0;
						MidBdst[1] = (UBYTE)(rand()>>24) + 0;

						MidRdst[2] = (UBYTE)(rand()>>24) + 0;
						MidGdst[2] = (UBYTE)(rand()>>24) + 0;
						MidBdst[2] = (UBYTE)(rand()>>24) + 0;
					break;

					case 2:
						MidRdst[0] = (UBYTE)(rand()>>24) + 0;
						MidGdst[0] = (UBYTE)(rand()>>24) + 0;
						MidBdst[0] = (UBYTE)(rand()>>24) + 0;

						MidRdst[1] = (UBYTE)(rand()>>24) + 0;
						MidGdst[1] = (UBYTE)(rand()>>24) + 0;
						MidBdst[1] = (UBYTE)(rand()>>24) + 0;

						MidRdst[2] = (UBYTE)(rand()>>24) + 0;
						MidGdst[2] = (UBYTE)(rand()>>24) + 0;
						MidBdst[2] = (UBYTE)(rand()>>24) + 0;
					break;

					case 3:
						MidRdst[0] = (UBYTE)(rand()>>25) + 0;
						MidGdst[0] = (UBYTE)(rand()>>25) + 0;
						MidBdst[0] = (UBYTE)(rand()>>25) + 0;

						MidRdst[1] = (UBYTE)(rand()>>24) + 0;
						MidGdst[1] = (UBYTE)(rand()>>24) + 0;
						MidBdst[1] = (UBYTE)(rand()>>24) + 0;

						MidRdst[2] = (UBYTE)(rand()>>24) + 0;
						MidGdst[2] = (UBYTE)(rand()>>24) + 0;
						MidBdst[2] = (UBYTE)(rand()>>24) + 0;
					break;

					default:
						MidRdst[0] = (UBYTE)(rand()>>25) + 127;
						MidGdst[0] = (UBYTE)(rand()>>25) + 127;
						MidBdst[0] = (UBYTE)(rand()>>25) + 127;

						MidRdst[1] = (UBYTE)(rand()>>25) + 64;
						MidGdst[1] = (UBYTE)(rand()>>25) + 64;
						MidBdst[1] = (UBYTE)(rand()>>25) + 64;

						MidRdst[2] = (UBYTE)(rand()>>25) + 32;
						MidGdst[2] = (UBYTE)(rand()>>25) + 32;
						MidBdst[2] = (UBYTE)(rand()>>25) + 32;
					break;
				}
			}

			CalcLine(Zoom2, Width, Height, FieldToCalc);
			LineToCalc++;
			if(LineToCalc > Height-1) LineToCalc = Height-1;
			FrameCountField++;
			
			if(FrameCountField > 500) {
				FrameCountField=0;
				LineToCalc = 0;
				PrevField = FieldToCalc;
				do {
					FieldToCalc=(UBYTE)(rand()>>24) % NUMFIELDS;
				} while (FieldToCalc == PrevField);

				for(j=0; j<Width*Height; j++) {
					Zoom[j]=Zoom2[j];
				}
				PrepareConstants(FieldToCalc);
			}

			for(i=Width*Cutoff; i<Width*(Height-Cutoff); i++) {
				PixelC[i]=PixelD[Zoom[i]];
			}

			/* Make a backup of the data pointers */
			PluginRawL    = SpecRawL;
			PluginRawR    = SpecRawR;
			PluginSamples = SampleRaw;

			InfoCount++;
			if(InfoCount < 75) MakeTitle();
			else InfoCount=75;

			TimeWave++;
			FrameCountWave++;
			CalcWave(WaveToCalc0, 0);
			DrawWave(WaveX,  WaveY,  Width, Height, WaveToCalc0, 0);
			CalcWave(WaveToCalc1, 1);
			DrawWave(WaveX2, WaveY2, Width, Height, WaveToCalc1, 1);

			if(FrameCountWave > 200) {
				if(DestWaveBuffer == 1) WaveMorph += 0.005;
				else                    WaveMorph -= 0.005;
			}
			if (WaveMorph > 1.0) WaveMorph = 1.0;
			if (WaveMorph < 0.0) WaveMorph = 0.0;

			if(FrameCountWave > 400) {
				FrameCountWave = 0;
				DestWaveBuffer = 1 - DestWaveBuffer;
				do {
					WaveToCalc = (UBYTE)(rand()>>24) % NUMWAVES;
				} while (WaveToCalc == WaveToCalc0 || WaveToCalc == WaveToCalc1);
				if(DestWaveBuffer == 1) {
					WaveToCalc1 = WaveToCalc;
					InitWave(WaveToCalc1, 1);
				}
				else {
					WaveToCalc0 = WaveToCalc;
					InitWave(WaveToCalc0, 0);
				}
			}

			for(i=0; i<Width*2; i++) {
				float xf,yf;
				ULONG x,y;
				xf = (float)WaveX[i]*(1.0-WaveMorph) + (float)WaveX2[i]*(WaveMorph);
				yf = (float)WaveY[i]*(1.0-WaveMorph) + (float)WaveY2[i]*(WaveMorph);
				x  = (ULONG)xf + Width/2;
				y  = (ULONG)yf + Height/2;
				if(x < Width-1) {
					if(y < Height-1) {
						if(x > 0) {
							if(y > 0) {
								ActPixel = PixelC + y * Width + x;
								*ActPixel=255;
							}
						}
					}
				}
			}

			SetAPen(rp, 0);
			if(ShowWave) WritePixelArray(PixelC, 1, Cutoff+1, Width, rp, 0, Cutoff+1, Width-2, Height-2*Cutoff-2, RECTFMT_LUT8);
			else         WritePixelArray(PixelD, 1, Cutoff+1, Width, rp, 0, Cutoff+1, Width-2, Height-2*Cutoff-2, RECTFMT_LUT8);
			RectFill(rp, 0, 0, Width, Cutoff);
			RectFill(rp, 0, Height-Cutoff-1, Width, Height-1);

			if(DisplayFPS) {
				Move(rp, 0, 20);
				SetABPenDrMd(rp, 255, 0, JAM2);
				sprintf(FPStext, "%3ld fps       ", 5*eclock/itime);
				Text(rp, FPStext, 7);
			}
		}
	}
}


void Blur(UBYTE *PixelC, UBYTE *PixelD) {
  UBYTE *ActPixel;
  UBYTE *DstPixel;
  UBYTE *AbovePixel, *BelowPixel;
  UBYTE Left, Act, Right;
  WORD w;
  ULONG i;

	PixelC += Width*Cutoff;
	PixelD += Width*Cutoff;

  ActPixel   = PixelC+Width+1;
  AbovePixel = ActPixel-Width;
  BelowPixel = ActPixel+Width;
  DstPixel   = PixelD+Width+1;

  Left  = 0;
  Act   = 0;
  Right = 0;

	for(i=Width*(Cutoff+1); i<(Width*(Height-Cutoff-2)-2); i++) {
    Right = *(ActPixel+1);

    w = (Right + Act + Left + *AbovePixel + *BelowPixel) * 3 / 16;
		if(w<0) w=0;
    *DstPixel++ = w;

    ActPixel++;
    AbovePixel++;
    BelowPixel++;

    Left = Act;
    Act = Right;
  }
}

void ShowRequester(char *Text, char *Button) {
	struct EasyStruct Req;
	Req.es_StructSize   = sizeof(struct EasyStruct);
	Req.es_Flags        = 0;
  Req.es_Title        = "AmigaAMP Plugin";
  Req.es_TextFormat   = (UBYTE*)Text;
  Req.es_GadgetFormat = (UBYTE*)Button;
	EasyRequestArgs(NULL, &Req, NULL, NULL);
}

BOOL OpenTimer(void) {
	struct EClockVal ev = {0,0};

	if(TimerMP=(struct MsgPort*)CreatePort(0,0)) {
		if(TimerIO=(struct timerequest *)CreateIORequest(TimerMP, sizeof(struct timerequest))) {
			TimerError=OpenDevice("timer.device", UNIT_ECLOCK, (struct IORequest *)TimerIO, NULL);
			if(TimerError==0) {

				TimerBase = (struct Library*) &TimerIO->tr_node.io_Device->dd_Library;
				EFreq=ReadEClock(&ev);

				TimerMask = 1L << TimerMP->mp_SigBit;

				return(TRUE);
			}
		}
	}
	return(FALSE);
}

void CloseTimer(void) {
	if(!TimerError)   CloseDevice((struct IORequest *)TimerIO);
	if(TimerIO)       DeleteIORequest(TimerIO);
	if(TimerMP)       DeletePort(TimerMP);
}

void StartTimer(void) {
	TimerIO->tr_node.io_Command = TR_ADDREQUEST;
	TimerIO->tr_node.io_Flags   = 0;
	TimerIO->tr_time.tv_secs    = 0;
	TimerIO->tr_time.tv_micro   = EFreq/LimitFPS;
	SendIO((struct IORequest *)TimerIO);
}

struct MsgPort *MyCreatePort(UBYTE *name, LONG pri) {
	int sigBit;
	struct MsgPort *mp;

	if((sigBit = AllocSignal(-1L)) == -1)	return(NULL);

	mp = (struct MsgPort*) AllocVec(sizeof(struct MsgPort), MEMF_PUBLIC|MEMF_CLEAR);

	if(!mp) {
		FreeSignal(sigBit);
		return(NULL);
	}

	mp->mp_Node.ln_Name = name;
	mp->mp_Node.ln_Pri  = pri;
	mp->mp_Node.ln_Type = NT_MSGPORT;
	mp->mp_Flags        = PA_SIGNAL;
	mp->mp_SigBit       = sigBit;
	mp->mp_SigTask      = FindTask(NULL);
	if(name) AddPort(mp);
	#ifdef __SASC
		else NewList(&(mp->mp_MsgList));
	#else
		else NewListPPC(&(mp->mp_MsgList));
	#endif

	return(mp);
}

void MyDeletePort(struct MsgPort *mp) {
	if(mp->mp_Node.ln_Name) RemPort(mp);
	mp->mp_SigTask         = (struct Task *) -1;
	mp->mp_MsgList.lh_Head = (struct Node *) -1;
	FreeSignal(mp->mp_SigBit);
	FreeVec(mp); mp=NULL;
}

void InitField(LONG *Zoom, ULONG Width, ULONG Height) {
	LONG i,xs,ys,xd,yd;

	float scale,greater;
	float xsf,ysf,xdf,ydf;
	float rsf,tsf,rdf,tdf;
	float wf,hf;

	wf=(float)Width;
	hf=(float)Height;

	greater=1.0;
	if(wf > greater) greater = wf;
	if(hf > greater) greater = hf;

	scale = 1.0 / (greater/2.0);

	for(yd=0; yd<(Height); yd++) {
		for(xd=0; xd<(Width); xd++) {
			xdf = (xd - wf/2.0) * scale;
			ydf = (yd - hf/2.0) * scale;

			xsf = xdf - xdf * 0.075;
			ysf = ydf - ydf * 0.075;

			xs = xsf/scale + wf/2.0;
			ys = ysf/scale + hf/2.0;

			if(xs < Width/2)  xs++;
			if(ys < Height/2) ys++;

			if(xs < 0)      xs = 0;
			if(xs > Width)  xs = Width;
			if(ys < 0)      ys = 0;
			if(ys > Height) ys = Height;

			Zoom[Width * yd + xd] = Width * ys + xs;
		}
	}
}


void MakeCMap(void) {
	int j,i,c;
	float r,g,b;
	float rs,gs,bs;

	r=255; g=255; b=255;

	c=255;

	Colour[0]=256L<<16+0;

	for(j=0; j<4; j++) {
		rs = (r - (float)MidR[j]) / 64.0;
		gs = (g - (float)MidG[j]) / 64.0;
		bs = (b - (float)MidB[j]) / 64.0;
		for(i=0; i<64; i++) {
			Colour[1+3*c+0] = (ULONG)r << 24;
			Colour[1+3*c+1] = (ULONG)g << 24;
			Colour[1+3*c+2] = (ULONG)b << 24;
			r -= rs;
			g -= gs;
			b -= bs;
			c--;
		}
	}

}

void MakeTitle(void) {
	long x,y,i,p,s,v,xs,ys;
	UWORD Blt1[27] = {
		0x7008,0x0000,0x0000,0x8808,0x0000,0x0000,0x822B,0x1963,
		0x8E38,0x722C,0xA590,0x5144,0x0A28,0x9913,0xD07C,0x8A28,
		0x8514,0x5040,0x8A68,0xA594,0xD144,0x71AF,0x1963,0x4E38,
		0x0000,0x0100,0x0000
	}; // W=46, H=9
	UWORD Blt2[54] = {
		0x8001,0xF400,0x0000,0x0101,0x0000,0x0080,0x8000,0x4400,
		0x0000,0x0111,0x0000,0x0080,0xB220,0x458E,0x7638,0xC111,
		0x3967,0x9C80,0xCA20,0x4651,0x4905,0x2092,0x4590,0xA280,
		0x8940,0x4451,0x493C,0xC0AA,0x7D11,0x3E80,0x8940,0x4451,
		0x4944,0x20AA,0x4112,0x2080,0x8880,0x4451,0x494D,0x2044,
		0x4514,0x2280,0xF080,0x444E,0x4934,0xC044,0x3917,0x9C80,
		0x0100,0x0000,0x0000,0x0000,0x0000,0x0000
	}; // W=89, H=9

	xs = (Width-46)/2;
	ys = Height/2-30-9;
	s=0;
	for(y=0; y<9; y++) {
		x=0;
		for(i=0; i<3; i++) {
			for(p=0; p<16; p++) {
				x++;
				v = Blt1[s] & (1<<(15-p));
				if(v) PixelC[(ys+y)*Width+xs+x] = 255;
			}
			s++;
		}
	}
	xs = (Width-89)/2;
	ys = Height/2+30;
	s=0;
	for(y=0; y<9; y++) {
		x=0;
		for(i=0; i<6; i++) {
			for(p=0; p<16; p++) {
				x++;
				v = Blt2[s] & (1<<(15-p));
				if(v) PixelC[(ys+y)*Width+xs+x] = 255;
			}
			s++;
		}
	}
}


void ReColor(void) {
	int i;

	for(i=0; i<3; i++) {
		if(MidR[i] > MidRdst[i]) MidR[i] --;
		if(MidG[i] > MidGdst[i]) MidG[i] --;
		if(MidB[i] > MidBdst[i]) MidB[i] --;
		if(MidR[i] < MidRdst[i]) MidR[i] ++;
		if(MidG[i] < MidGdst[i]) MidG[i] ++;
		if(MidB[i] < MidBdst[i]) MidB[i] ++;
	}

	MakeCMap();
	LoadRGB32(&PluginScreen->ViewPort, Colour);
}

float rnd(float max) {
	ULONG r;
	ULONG m;

	m = (ULONG)(max * 10000.0);

	r=(rand()>>8) % m;

	return (float)r/10000.0;
}

float sgn(float val) {
	if(val > 0.0) return  1.0;
	if(val < 0.0) return -1.0;
	return 0.0;
}

float trnc(float value) {
	return ceil(value);
}

#ifdef __SASC
void SetPriority(void) {
	struct TagItem MyTags[2];
	if(PPCLibBase=OpenLibrary("ppc.library",0)) {
		MyTags[0].ti_Tag  = PPCTASKTAG_PRIORITY;
		MyTags[0].ti_Data = 15;
		MyTags[1].ti_Tag	 = TAG_END;
		CloseLibrary(PPCLibBase);
	}
}
#endif

