BOX is a quick and dirty demonstration of basic APE structure and the utilization of AVS features. It takes advantage of AVS's built-in beat detection and easy access to the frame buffer to flash a rectangle onscreen on every beat. We also learn how to set up a configuration screen template, load and save configuration settings, and use the default color selector. There is very little fat, so you can use this as a template for future APEs. You can view the main file below.
#include <windows.h>
#include "resource.h"
#include "avs_ape.h"
// this will be the directory and APE name displayed in
// the AVS Editor
#define MOD_NAME "Tutorials / BOX v1.0"
// this is how WVS will recognize this APE internally
#define UNIQUEIDSTRING "Nullsoft Tut0: BOX"
class C_THISCLASS : public C_RBASE
{
protected:
public:
C_THISCLASS();
virtual ~C_THISCLASS();
virtual int render(char visdata[2][2][576], int isBeat,
int *framebuffer, int *fbout, int w, int h);
virtual HWND conf(HINSTANCE hInstance, HWND hwndParent);
virtual char *get_desc();
virtual void load_config(unsigned char *data, int len);
virtual int save_config(unsigned char *data);
int enabled; // toggles plug-in on and off
// (a good idea for any APE)
int color; // color of rectangle
};
// global configuration dialog pointer
static C_THISCLASS *g_ConfigThis;
// global DLL instance pointer (not needed in this
// example, but is useful)
static HINSTANCE g_hDllInstance;
// this is where we deal with the configuration screen
static BOOL CALLBACK g_DlgProc(HWND hwndDlg, UINT uMsg,
WPARAM wParam,LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
if (g_ConfigThis->enabled)
{
CheckDlgButton(hwndDlg,IDC_CHECK1,BST_CHECKED);
}
return 1;
case WM_DRAWITEM:
DRAWITEMSTRUCT *di;
di=(DRAWITEMSTRUCT *)lParam;
if (di->CtlID == IDC_DEFCOL)
{
int w;
int color;
w=di->rcItem.right-di->rcItem.left;
color=g_ConfigThis->color;
color = ( (color>>16)&0xff) |
(color&0xff00) |
((color<<16)&0xff0000);
// paint nifty color button
HBRUSH hBrush,hOldBrush;
LOGBRUSH lb={BS_SOLID,color,0};
hBrush = CreateBrushIndirect(&lb);
hOldBrush=(HBRUSH)SelectObject(di->hDC,hBrush);
Rectangle(di->hDC,di->rcItem.left,di->rcItem.top,
di->rcItem.right,di->rcItem.bottom);
SelectObject(di->hDC,hOldBrush);
DeleteObject(hBrush);
}
return 0;
case WM_COMMAND:
// see if enable checkbox is checked
if (LOWORD(wParam) == IDC_CHECK1)
{
g_ConfigThis->enabled=
(IsDlgButtonChecked(hwndDlg,IDC_CHECK1)?1:0);
}
// is colorbox is selected?
if (LOWORD(wParam) == IDC_DEFCOL)
{
static COLORREF custcolors[16];
int *a;
CHOOSECOLOR cs;
a=&g_ConfigThis->color;
cs.lStructSize = sizeof(cs);
cs.hwndOwner = hwndDlg;
cs.hInstance = 0;
cs.rgbResult = ((*a>>16)&0xff)|
(*a&0xff00)|
((*a<<16)&0xff0000);
cs.lpCustColors = custcolors;
cs.Flags = CC_RGBINIT|CC_FULLOPEN;
// go to windows color selection screen
if (ChooseColor(&cs))
{
*a = ((cs.rgbResult>>16)&0xff)|
(cs.rgbResult&0xff00)|
((cs.rgbResult<<16)&0xff0000);
}
InvalidateRect(GetDlgItem(hwndDlg,IDC_DEFCOL),
NULL,TRUE);
}
return 0;
}
return 0;
}
// set up default configuration
C_THISCLASS::C_THISCLASS()
{
//set initial color
color=RGB(255,0,0);
enabled=1;
}
// virtual destructor
C_THISCLASS::~C_THISCLASS()
{
}
/* RENDER FUNCTION:
render should return 0 if it only used framebuffer,
or 1 if the new output data is in fbout.
this is used when you want to do something that you'd otherwise
need to make a copy of the framebuffer.
w and h are the width and height of the screen, in pixels.
isBeat is 1 if a beat has been detected.
visdata is in the format of [spectrum:0,wave:1][channel][band].
*/
int C_THISCLASS::render(char visdata[2][2][576], int isBeat,
int *framebuffer, int *fbout, int w, int h)
{
int halfw;
int halfh;
// is this effect on?
if (!enabled)
{
return 0;
}
// did we just hit a beat?
if(isBeat)
{
// draw our magic box
halfw=w/2;
halfh=h/2;
framebuffer+=(((halfh/2)*w)+ (halfw/2));
for(int j=0;j<halfh;j++)
{
for(int i=0;i<halfw;i++)
{
framebuffer[i]=color;
}
framebuffer+=w;
}
}
return 0;
}
HWND C_THISCLASS::conf(HINSTANCE hInstance, HWND hwndParent)
// return NULL if no config dialog possible
{
g_ConfigThis = this;
return CreateDialog(hInstance,MAKEINTRESOURCE(IDD_CONFIG),
hwndParent,g_DlgProc);
}
char *C_THISCLASS::get_desc(void)
{
return MOD_NAME;
}
// load_/save_config are called when saving and loading
// presets (.avs files)
#define GET_INT() (data[pos]|(data[pos+1]<<8)|\
(data[pos+2]<<16)|(data[pos+3]<<24))
// read configuration of max length "len" from data.
void C_THISCLASS::load_config(unsigned char *data, int len)
{
int pos=0;
// always ensure there is data to be loaded
if (len-pos >= 4)
{
// load activation toggle
enabled=GET_INT();
pos+=4;
}
if (len-pos >= 4)
{
// load the box color
color=GET_INT();
pos+=4;
}
}
// write configuration to data, return length.
// config data should not exceed 64k.
#define PUT_INT(y) data[pos]=(y)&255; data[pos+1]=(y>>8)&255;\
data[pos+2]=(y>>16)&255; data[pos+3]=(y>>24)&255
int C_THISCLASS::save_config(unsigned char *data)
{
int pos=0;
PUT_INT(enabled);
pos+=4;
PUT_INT(color);
pos+=4;
return pos;
}
/*
export stuff
creates a new effect object if desc is NULL, otherwise
fills in desc with description
*/
C_RBASE *R_RetrFunc(char *desc)
{
if (desc)
{
strcpy(desc,MOD_NAME);
return NULL;
}
return (C_RBASE *) new C_THISCLASS();
}
// allows AVS to retrieve this APE module
extern "C"
{
__declspec (dllexport) int _AVS_APE_RetrFunc(HINSTANCE\
hDllInstance, char **info, int *create)
// return 0 on failure
{
g_hDllInstance=hDllInstance;
*info=UNIQUEIDSTRING;
*create=(int)(void*)R_RetrFunc;
return 1;
}
};