

/*****************************************************************************/
/*                                                                           */
/*                        AmigaAMP Wide Spectrometer Plugin                  */
/*                                                                           */
/*                     Written December 1998  by Thomas Wenzel               */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/* This is an example how to write your own AmigaAMP visualisation plugin    */
/* To make it as easy as possible all plugins are normal AmigaDOS            */
/* executables which can be launched and stopped at any time.                */
/*                                                                           */
/* This sourcecode and executable are free for non-commercial use only!      */
/*                                                                           */
/*****************************************************************************/

#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 <dos/dostags.h>
#include <graphics/gfxbase.h>

BOOL PluginInit(void);
void PluginExit(void);
void PluginLoop(void);
void ShowRequester(char *Text, char *Button);

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

BYTE           PluginSignal;
ULONG          PluginMask;
BOOL           Accepted;
struct Process *PluginTask;
struct MsgPort *PluginMP;
struct MsgPort *PluginRP;

UWORD	*PluginRawL;
UWORD	*PluginRawR;
UWORD	*SpecRawL;
UWORD	*SpecRawR;

struct PluginMessage {
	struct Message msg;
	ULONG          PluginMask;
	struct Process *PluginTask;
	UWORD          **SpecRawL;
	UWORD          **SpecRawR;
	BOOL           Accepted;
};

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

void main(void) {
	struct PluginMessage *PluginMsg;
	struct PluginMessage *ReplyMsg;

	/* Allocate all user resources */
	if(PluginInit()) {
		/* Check if a plugin capable instance of AmigaAMP is running */
		if(PluginMP=FindPort("AmigaAMP plugin port"))	{
			/* Allocate a sigbit for receiving signals FROM AmigaAMP */
			PluginSignal=AllocSignal(-1);
			if(PluginSignal != -1) {
				PluginMask = 1L << PluginSignal;
				PluginTask = (struct Process *)FindTask(NULL);

				/* Allocate a message and reply port for sending messages TO AmigaAMP */
				PluginMsg=AllocVec(sizeof(struct PluginMessage), MEMF_PUBLIC|MEMF_CLEAR);
				PluginRP=CreatePort(0,0);

				/* 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;
				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;
					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 */
				FreeVec(PluginMsg);
				DeletePort(PluginRP);
				FreeSignal(PluginSignal);
			}
			else {
				ShowRequester("Signal allocation failure!", "Abort");
			}
		}
		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              */
/*****************************************************************************/

#define STEP 4

struct Screen *PluginScreen = NULL;
struct Window *PluginWin    = NULL;
struct RastPort *rp;

ULONG WinMask;

UBYTE Pixel[132][512];
UBYTE Colour[128];

UBYTE PeakL[256];
UBYTE PeakR[256];
UBYTE MaxL[256];
UBYTE MaxR[256];

UWORD offx, offy;

/****************************************************************************/
/* 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(void) {
	long i;

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

	if(PluginWin=OpenWindowTags(NULL,
		WA_Left,         20,
		WA_Top,          20,
		WA_InnerWidth,   512,
		WA_InnerHeight,  132,
		WA_Title,        "AmigaAMP Wide Spectrometer Plugin",
		WA_DragBar,      TRUE,
		WA_DepthGadget,  TRUE,
		WA_CloseGadget,  TRUE,
		WA_IDCMP,        IDCMP_CLOSEWINDOW,
	TAG_END)) {
		WinMask = 1L << PluginWin->UserPort->mp_SigBit;
		rp=PluginWin->RPort;
		offx = PluginWin->BorderLeft;
		offy = PluginWin->BorderTop;

		if(PluginScreen=LockPubScreen(NULL)) {
			for(i=0; i<64; i++) {
				Colour[i]    = ObtainBestPen(PluginScreen->ViewPort.ColorMap, 0xFFFFFFFF, i * 0x4000000, 0, NULL);
				Colour[i+64] = ObtainBestPen(PluginScreen->ViewPort.ColorMap, 0xFFFFFFFF, 0xFFFFFFFF, i * 0x4000000, NULL);
			}

			for(i=0; i<256; i++) {
				PeakL[i]=0;
				PeakR[i]=0;
				MaxL[i]=0;
				MaxR[i]=0;
			}
			return(TRUE);
		}
	}
	return(FALSE);
}

/******************************************************************************/
/* 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(PluginScreen) for(i=0; i<128; i++) {
		ReleasePen(PluginScreen->ViewPort.ColorMap, Colour[i]);
	}
	if(PluginWin) CloseWindow(PluginWin);
	if(PluginScreen) UnlockPubScreen(NULL, PluginScreen);
}

/*******************************************************************************/
/* 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 SpecRawL[512] and UWORD SpecRawR[512]. The scale is logarithmic, i.e. */
/* 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) {
	ULONG Signals;
	LONG i,j,k,l;

	for(;;) {

		/* Wait until there's something to do */
		Signals=Wait(SIGBREAKF_CTRL_C | PluginMask | WinMask);

		/* 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_CLOSEWINDOW) done=1;
			}
			if(done) break;
		}

		/* New data has arrived! */
		if(Signals & PluginMask) {

			/* Initialize the pixel array */
			for(i=0; i<512; i++) {
				for(j=0; j<132; j++) {
					Pixel[j][i]=1;
				}
			}

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

			/* Process the spectral data of the left channel */
			for(i=1; i<256; i+=STEP) {

				/* Get the value of each spectral line to be drawn  */
				/* The value ranges from 0 to 65535. Our window has */
				/* only room for 128 pixels so we need to scale it  */
				/* down.                                            */
				l=PluginRawL[512-i*2]>>9;

				/* Set peak- and peak-hold-values */
				if(l>PeakL[i]) PeakL[i]=l;
				if(l>MaxL[i])  MaxL[i]=l;

				/* Draw the colourful line */
				for(j=0; j<MaxL[i]; j++) {
					Pixel[130-j][i]=Colour[j];
				}
				/* Draw the peak pixel */
				Pixel[130-PeakL[i]][i]=2;

				/* Calculate the falloff of both peak and peak-hold */
				if(PeakL[i] > 96) PeakL[i]--;
				if(PeakL[i] > 64) PeakL[i]--;
				if(PeakL[i] > 32) PeakL[i]--;
				if(PeakL[i] >  1) PeakL[i]--;
				if(MaxL[i]  > 96) MaxL[i]-=4;
				if(MaxL[i]  > 64) MaxL[i]-=4;
				if(MaxL[i]  > 32) MaxL[i]-=4;
				if(MaxL[i]  >  4) MaxL[i]-=4;
				if(MaxL[i]  >  1) MaxL[i]-=1;
			}

			/* Process the spectral data of the right channel */
			for(i=1; i<256; i+=STEP) {
				l=PluginRawR[i*2]>>9;
				if(l>PeakR[i]) PeakR[i]=l;
				if(l>MaxR[i])  MaxR[i]=l;
				for(j=0; j<MaxR[i]; j++) {
					Pixel[130-j][256+i]=Colour[j];
				}
				Pixel[130-PeakR[i]][256+i]=2;

				if(PeakR[i] > 96) PeakR[i]--;
				if(PeakR[i] > 64) PeakR[i]--;
				if(PeakR[i] > 32) PeakR[i]--;
				if(PeakR[i] >  1) PeakR[i]--;
				if(MaxR[i]  > 96) MaxR[i]-=4;
				if(MaxR[i]  > 64) MaxR[i]-=4;
				if(MaxR[i]  > 32) MaxR[i]-=4;
				if(MaxR[i]  >  4) MaxR[i]-=4;
				if(MaxR[i]  >  1) MaxR[i]-=1;
			}

			/* Draw the image (this is why we need OS3.1) */
			WriteChunkyPixels(rp, offx, offy, offx+512, offy+131, &Pixel[0][0], 512);
		}
	}
}

void ShowRequester(char *Text, char *Button) {
	struct EasyStruct Req;

  Req.es_Title        = "AmigaAMP Plugin";
  Req.es_TextFormat   = (UBYTE*)Text;
  Req.es_GadgetFormat = (UBYTE*)Button;
	EasyRequestArgs(NULL, &Req, NULL, NULL);
}
