// configuration declarations
int config_x=50, config_y=50; // screen X position and Y position, repsectively
void config_read(struct winampVisModule *this_mod); // reads the configuration
void config_write(struct winampVisModule *this_mod); // writes the configuration
void config_getinifn(struct winampVisModule *this_mod, char *ini_file); // makes the .ini file filename
// returns a winampVisModule when requested. Used in hdr, below
winampVisModule *getModule(int which);
// "member" functions
void config(struct winampVisModule *this_mod); // configuration dialog
int init(struct winampVisModule *this_mod); // initialization for module
int render1(struct winampVisModule *this_mod); // rendering for module 1
int render2(struct winampVisModule *this_mod); // rendering for module 2
int render3(struct winampVisModule *this_mod); // rendering for module 3
void quit(struct winampVisModule *this_mod); // deinitialization for module
// Module header, includes version, description, and address of the module retriever function
winampVisHeader hdr = { VIS_HDRVER, "Nullsoft Test Visualization Library v1.0", getModule };
// this is the only exported symbol. returns our main header.
// if you are compiling C++, the extern "C" { is necessary, so we just #ifdef it
#ifdef __cplusplus
extern "C" {
#endif
__declspec( dllexport ) winampVisHeader *winampVisGetHeader()
{
return &hdr;
}
#ifdef __cplusplus
}
#endif
// getmodule routine from the main header. Returns NULL if an invalid module was requested,
// otherwise returns either mod1, mod2 or mod3 depending on 'which'.
winampVisModule *getModule(int which)
{
switch (which)
{
case 0: return &mod1;
case 1: return &mod2;
case 2: return &mod3;
default:return NULL;
}
}
// configuration. Passed this_mod, as a "this" parameter. Allows you to make one configuration
// function that shares code for all your modules (you don't HAVE to use it though, you can make
// config1(), config2(), etc...)
void config(struct winampVisModule *this_mod)
{
MessageBox(this_mod->hwndParent,"This module is Copyright (c) 1997-1998, Justin Frankel/Nullsoft\n"
"-- This is just a demonstration module, it really isn't\n"
" supposed to be enjoyable --","Configuration",MB_OK);
}
// initialization. Registers our window class, creates our window, etc. Again, this one works for
// both modules, but you could make init1() and init2()...
// returns 0 on success, 1 on failure.
int init(struct winampVisModule *this_mod)
{
int width = (this_mod == &mod3)?256:288; // width and height are the same for mod1 and mod2,
int height = (this_mod == &mod3)?32:256; // but mod3 is shaped differently
config_read(this_mod);
{ // Register our window class
WNDCLASS wc;
memset(&wc,0,sizeof(wc));
wc.lpfnWndProc = WndProc; // our window procedure
wc.hInstance = this_mod->hDllInstance; // hInstance of DLL
wc.lpszClassName = szAppName; // our window class name
hMainWnd = CreateWindowEx(
WS_EX_TOOLWINDOW|WS_EX_APPWINDOW, // these exstyles put a nice small frame,
// but also a button in the taskbar
szAppName, // our window class name
this_mod->description, // use description for a window title
WS_VISIBLE|WS_SYSMENU, // make the window visible with a close button
config_x,config_y, // screen position (read from config)
width,height, // width & height of window (need to adjust client area later)
this_mod->hwndParent, // parent window (winamp main window)
NULL, // no menu
this_mod->hDllInstance, // hInstance of DLL
0); // no window creation data
if (!hMainWnd)
{
MessageBox(this_mod->hwndParent,"Error creating window","blah",MB_OK);
return 1;
}
SetWindowLong(hMainWnd,GWL_USERDATA,(LONG)this_mod); // set our user data to a "this" pointer
{ // adjust size of window to make the client area exactly width x height
RECT r;
GetClientRect(hMainWnd,&r);
SetWindowPos(hMainWnd,0,0,0,width*2-r.right,height*2-r.bottom,SWP_NOMOVE|SWP_NOZORDER);
}
// show the window
ShowWindow(hMainWnd,SW_SHOWNORMAL);
return 0;
}
// render function for oscilliscope. Returns 0 if successful, 1 if visualization should end.
int render1(struct winampVisModule *this_mod)
{
int x, y;
// clear background
Rectangle(memDC,0,0,288,256);
// draw oscilliscope
for (y = 0; y < this_mod->nCh; y ++)
{
MoveToEx(memDC,0,(y*256)>>(this_mod->nCh-1),NULL);
for (x = 0; x < 288; x ++)
{
LineTo(memDC,x,(y*256 + this_mod->waveformData[y][x]^128)>>(this_mod->nCh-1));
}
}
{ // copy doublebuffer to window
HDC hdc = GetDC(hMainWnd);
BitBlt(hdc,0,0,288,256,memDC,0,0,SRCCOPY);
ReleaseDC(hMainWnd,hdc);
}
return 0;
}
// render function for analyser. Returns 0 if successful, 1 if visualization should end.
int render2(struct winampVisModule *this_mod)
{
int x, y;
// clear background
Rectangle(memDC,0,0,288,256);
// draw analyser
for (y = 0; y < this_mod->nCh; y ++)
{
for (x = 0; x < 288; x ++)
{
MoveToEx(memDC,x,(y*256+256)>>(this_mod->nCh-1),NULL);
LineTo(memDC,x,(y*256 + 256 - this_mod->spectrumData[y][x])>>(this_mod->nCh-1));
}
}
{ // copy doublebuffer to window
HDC hdc = GetDC(hMainWnd);
BitBlt(hdc,0,0,288,256,memDC,0,0,SRCCOPY);
ReleaseDC(hMainWnd,hdc);
}
return 0;
}
// render function for VU meter. Returns 0 if successful, 1 if visualization should end.
int render3(struct winampVisModule *this_mod)
{
int x, y;
// clear background
Rectangle(memDC,0,0,256,32);
// draw VU meter
for (y = 0; y < 2; y ++)
{
int last=this_mod->waveformData[y][0];
int total=0;
for (x = 1; x < 576; x ++)
{
total += abs(last - this_mod->waveformData[y][x]);
last = this_mod->waveformData[y][x];
}
total /= 288;
if (total > 127) total = 127;
if (y) Rectangle(memDC,128,0,128+total,32);
else Rectangle(memDC,128-total,0,128,32);
}
{ // copy doublebuffer to window
HDC hdc = GetDC(hMainWnd);
BitBlt(hdc,0,0,256,32,memDC,0,0,SRCCOPY);
ReleaseDC(hMainWnd,hdc);
}
return 0;
}
// cleanup (opposite of init()). Destroys the window, unregisters the window class
void quit(struct winampVisModule *this_mod)
{
config_write(this_mod); // write configuration
SelectObject(memDC,oldBM); // delete our doublebuffer
DeleteObject(memDC);
DeleteObject(memBM);
DestroyWindow(hMainWnd); // delete our window
UnregisterClass(szAppName,this_mod->hDllInstance); // unregister window class
}
// window procedure for our window
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE: return 0;
case WM_ERASEBKGND: return 0;
case WM_PAINT:
{ // update from doublebuffer
PAINTSTRUCT ps;
RECT r;
HDC hdc = BeginPaint(hwnd,&ps);
GetClientRect(hwnd,&r);
BitBlt(hdc,0,0,r.right,r.bottom,memDC,0,0,SRCCOPY);
EndPaint(hwnd,&ps);
}
return 0;
case WM_DESTROY: PostQuitMessage(0); return 0;
case WM_KEYDOWN: // pass keyboard messages to main winamp window (for processing)
case WM_KEYUP:
{ // get this_mod from our window's user data
winampVisModule *this_mod = (winampVisModule *) GetWindowLong(hwnd,GWL_USERDATA);
PostMessage(this_mod->hwndParent,message,wParam,lParam);
}
return 0;
case WM_MOVE:
{ // get config_x and config_y for configuration
RECT r;
GetWindowRect(hMainWnd,&r);
config_x = r.left;
config_y = r.top;
}
return 0;
}
return DefWindowProc(hwnd,message,wParam,lParam);
}
void config_getinifn(struct winampVisModule *this_mod, char *ini_file)
{ // makes a .ini file in the winamp directory named "plugin.ini"
char *p;
GetModuleFileName(this_mod->hDllInstance,ini_file,MAX_PATH);
p=ini_file+strlen(ini_file);
while (p >= ini_file && *p != '\\') p--;
if (++p >= ini_file) *p = 0;
strcat(ini_file,"plugin.ini");
}
// notes:
// any window that remains in foreground should optimally pass
// keystrokes to the parent (winamp's) window, so that the user
// can still control it. unless escape is hit, or some option
// key specific to the vis is hit. As for storing configuration,
// Configuration data should be stored in <dll directory>\plugin.ini
// Look at the example plugin for a framework.
// ints are 32 bits, and structure members are aligned on the default 8 byte boundaries
// tested with VC++ 4.2 and 5.0
typedef struct winampVisModule {
char *description; // description of module
HWND hwndParent; // parent window (filled in by calling app)
HINSTANCE hDllInstance; // instance handle to this DLL (filled in by calling app)
int sRate; // sample rate (filled in by calling app)
int nCh; // number of channels (filled in...)
int latencyMs; // latency from call of RenderFrame to actual drawing
// (calling app looks at this value when getting data)
int delayMs; // delay between calls in ms
// the data is filled in according to the respective Nch entry
int spectrumNch;
int waveformNch;
unsigned char spectrumData[2][576];
unsigned char waveformData[2][576];
void (*Config)(struct winampVisModule *this_mod); // configuration dialog
int (*Init)(struct winampVisModule *this_mod); // 0 on success, creates window, etc
int (*Render)(struct winampVisModule *this_mod); // returns 0 if successful, 1 if vis should end
void (*Quit)(struct winampVisModule *this_mod); // call when done
void *userData; // user data, optional
} winampVisModule;
typedef struct {
int version; // VID_HDRVER
char *description; // description of library
winampVisModule* (*getModule)(int);
} winampVisHeader;