mirror of
https://github.com/WinampDesktop/winamp.git
synced 2024-12-19 17:25:54 +01:00
294 lines
6.4 KiB
C++
294 lines
6.4 KiB
C++
|
|
||
|
/**
|
||
|
Example Winamp AVS plug-in
|
||
|
Copyright (c) 2000, Nullsoft Inc.
|
||
|
|
||
|
Hello, welcome to the first Advanced Visualization
|
||
|
Studio tutorial!
|
||
|
The hope is that together, we can learn to utilize
|
||
|
AVS's powerful features: Namely direct access to the
|
||
|
frame buffer, EZ beat detection, and the ability to
|
||
|
stack plug-ins written by other developers for an
|
||
|
infinite array of possible effects.
|
||
|
|
||
|
I hereby present:
|
||
|
Tutorial 0: BOX-
|
||
|
Simplicity at its finest. Displays a rectangle on
|
||
|
screen on every beat. Oh, and you can change its color
|
||
|
too... Check avstut00.avs for a demonstration of a
|
||
|
spinning rectangle's power!
|
||
|
|
||
|
good luck and have fun!
|
||
|
**/
|
||
|
|
||
|
#include <windows.h>
|
||
|
#include "resource.h"
|
||
|
#include "avs_ape.h"
|
||
|
|
||
|
|
||
|
#define MOD_NAME "Tutorials / BOX v1.0"
|
||
|
#define UNIQUEIDSTRING "Nullsoft Tut0: BOX"
|
||
|
|
||
|
// extended APE api support
|
||
|
APEinfo *g_extinfo;
|
||
|
extern "C"
|
||
|
{
|
||
|
void __declspec(dllexport) _AVS_APE_SetExtInfo(HINSTANCE hDllInstance, APEinfo *ptr)
|
||
|
{
|
||
|
g_extinfo = ptr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
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
|
||
|
int color; // color of rectangle
|
||
|
};
|
||
|
|
||
|
// global configuration dialog pointer
|
||
|
static C_THISCLASS *g_ConfigThis;
|
||
|
// global DLL instance pointer (not needed in this example, but could be 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))
|
||
|
void C_THISCLASS::load_config(unsigned char *data, int len) // read configuration of max length "len" from data.
|
||
|
{
|
||
|
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
|
||
|
C_RBASE *R_RetrFunc(char *desc) // creates a new effect object if desc is NULL, otherwise fills in desc with description
|
||
|
{
|
||
|
if (desc)
|
||
|
{
|
||
|
strcpy(desc,MOD_NAME);
|
||
|
return NULL;
|
||
|
}
|
||
|
return (C_RBASE *) new C_THISCLASS();
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
Final Thoughts:
|
||
|
Alright! Hopefully you guys can take the next step
|
||
|
and display more than just a colored rectangle ;) The
|
||
|
exciting thing is, each time you write an AVS plug-in,
|
||
|
you exponentially increase AVS's potential, unlocking
|
||
|
the possibility of an effect you never expected. Good
|
||
|
luck, I hope this has helped!
|
||
|
|
||
|
See you next time!
|
||
|
**/
|