winamp/Src/Plugins/Visualization/vis_avs/main.cpp

605 lines
14 KiB
C++
Raw Normal View History

2024-09-24 14:54:57 +02:00
/*
LICENSE
-------
Copyright 2005 Nullsoft, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Nullsoft nor the names of its contributors may be used to
endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <windows.h>
#include <math.h>
#include <process.h>
#include "draw.h"
#include "wnd.h"
#include "r_defs.h"
#include "render.h"
#include "vis.h"
#include "cfgwnd.h"
#include "resource.h"
#include "bpm.h"
#include "api.h"
#include "../winamp/wa_ipc.h"
#include "../Agave/Language/api_language.h"
#include <api/service/waServiceFactory.h>
#include <stdio.h>
#ifdef REAPLAY_PLUGIN
#include "../../jmde/reaper_plugin.h"
const char *(*get_ini_file)();
int (*vuGetVisData)(char *vdata, int size);
#endif
#define PLUGIN_VERSION "v2.93"
#include "avs_eelif.h"
extern void GetClientRect_adj(HWND hwnd, RECT *r);
static unsigned char g_logtab[256];
HINSTANCE g_hInstance;
/* char *verstr=
#ifndef LASER
"Advanced Visualization Studio"
#else
"AVS/Laser"
#endif
" v2.92"
;
*/
static unsigned int WINAPI RenderThread(LPVOID a);
static void config(struct winampVisModule *this_mod);
static int init(struct winampVisModule *this_mod);
static int render(struct winampVisModule *this_mod);
static void quit(struct winampVisModule *this_mod);
HANDLE g_hThread;
volatile int g_ThreadQuit;
static CRITICAL_SECTION g_cs;
static unsigned char g_visdata[2][2][576];
static int g_visdata_pstat;
/* wasabi based services for localisation support */
api_service *WASABI_API_SVC = 0;
api_language *WASABI_API_LNG = 0;
HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
static char module1[128];
static winampVisModule *getModule(int which);
static winampVisHeader hdr = { VIS_HDRVER, 0, getModule };
// use this to get our own HINSTACE since overriding DllMain(..) causes instant crashes (should see why)
static HINSTANCE GetMyInstance()
{
MEMORY_BASIC_INFORMATION mbi = {0};
if(VirtualQuery(GetMyInstance, &mbi, sizeof(mbi)))
return (HINSTANCE)mbi.AllocationBase;
return NULL;
}
extern "C" {
__declspec( dllexport ) winampVisHeader* winampVisGetHeader(HWND hwndParent)
{
if(!WASABI_API_LNG_HINST)
{
// loader so that we can get the localisation service api for use
WASABI_API_SVC = (api_service*)SendMessage(hwndParent, WM_WA_IPC, 0, IPC_GET_API_SERVICE);
if (WASABI_API_SVC == (api_service*)1) WASABI_API_SVC = NULL;
waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID);
if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
// need to have this initialised before we try to do anything with localisation features
WASABI_API_START_LANG(GetMyInstance(),VisAVSLangGUID);
}
#ifndef LASER
static char szDescription[256];
wsprintfA(szDescription,"%s " PLUGIN_VERSION,WASABI_API_LNGSTRING_BUF(IDS_AVS,module1,128));
hdr.description = szDescription;
#else
hdr.description = "AVS/Laser "PLUGIN_VERSION;
#endif
return &hdr;
}
}
static winampVisModule *getModule(int which)
{
static winampVisModule mod =
{
#ifdef LASER
"Advanced Visualization Studio/Laser",
#else
module1,
#endif
NULL, // hwndParent
NULL, // hDllInstance
0, // sRate
0, // nCh
1000/70, // latencyMS
1000/70,// delayMS
2, // spectrumNch
2, // waveformNch
{ 0, }, // spectrumData
{ 0, }, // waveformData
config,
init,
render,
quit
};
if (which==0) return &mod;
return 0;
}
void about(HWND hwndParent)
{
static int about_here = 0;
char aboutbuf[1024] = {0}, aboutTitle[48] = {0};
if (about_here)
{
SetActiveWindow(FindWindow("#32770",WASABI_API_LNGSTRING_BUF(IDS_ABOUT_AVS,aboutTitle,48)));
return;
}
about_here = 1;
wsprintf(aboutbuf,WASABI_API_LNGSTRING(IDS_ABOUT_STRING),hdr.description);
MessageBox(hwndParent,aboutbuf,WASABI_API_LNGSTRING_BUF(IDS_ABOUT_AVS,aboutTitle,48),0);
about_here = 0;
}
HWND GetDialogBoxParent(HWND winamp)
{
HWND parent = (HWND)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETDIALOGBOXPARENT);
if (!parent || parent == (HWND)1)
return winamp;
return parent;
/*
BOOL CALLBACK aboutProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
SetDlgItemText(hwndDlg,IDC_VERSTR,verstr);
return 1;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK: case IDCANCEL:
EndDialog(hwndDlg,0);
return 0;
}
return 0;
}
return 0;
*/
}
static void config(struct winampVisModule *this_mod)
{
if (!g_hwnd || !IsWindow(g_hwnd))
{
about(GetDialogBoxParent(this_mod->hwndParent));
// DialogBox(this_mod->hDllInstance,MAKEINTRESOURCE(IDD_DIALOG2),this_mod->hwndParent,aboutProc);
}
else
{
SendMessage(g_hwnd,WM_USER+33,0,0);
}
}
CRITICAL_SECTION g_render_cs;
static int g_is_beat;
char g_path[1024];
int beat_peak1,beat_peak2, beat_cnt,beat_peak1_peak;
void main_setRenderThreadPriority()
{
int prios[]={
GetThreadPriority(GetCurrentThread()),
THREAD_PRIORITY_IDLE,
THREAD_PRIORITY_LOWEST,
THREAD_PRIORITY_NORMAL,
THREAD_PRIORITY_HIGHEST,
};
SetThreadPriority(g_hThread,prios[cfg_render_prio]);
}
extern void previous_preset(HWND hwnd);
extern void next_preset(HWND hwnd);
extern void random_preset(HWND hwnd);
#if 0//syntax highlighting
HINSTANCE hRich;
#endif
static int init(struct winampVisModule *this_mod)
{
DWORD id;
FILETIME ft;
#if 0//syntax highlighting
if (!hRich) hRich=LoadLibrary("RICHED32.dll");
#endif
GetSystemTimeAsFileTime(&ft);
srand(ft.dwLowDateTime|ft.dwHighDateTime^GetCurrentThreadId());
g_hInstance=this_mod->hDllInstance;
GetModuleFileName(g_hInstance,g_path,MAX_PATH);
char *p=g_path+strlen(g_path);
while (p > g_path && *p != '\\') p--;
*p = 0;
#ifdef WA2_EMBED
if (SendMessage(this_mod->hwndParent,WM_USER,0,0) < 0x2900)
{
char title[16];
MessageBox(this_mod->hwndParent,WASABI_API_LNGSTRING(IDS_REQUIRES_2_9_PLUS),
WASABI_API_LNGSTRING_BUF(IDS_AVS_ERROR,title,16),MB_OK|MB_ICONSTOP);
return 1;
}
#endif
#ifndef NO_MMX
extern int is_mmx(void);
if (!is_mmx())
{
char title[16];
MessageBox(this_mod->hwndParent,WASABI_API_LNGSTRING(IDS_NO_MMX_SUPPORT),
WASABI_API_LNGSTRING_BUF(IDS_AVS_ERROR,title,16),MB_OK|MB_ICONSTOP);
return 1;
}
#endif
#ifdef LASER
strcat(g_path,"\\avs_laser");
#else
strcat(g_path,"\\avs");
#endif
CreateDirectory(g_path,NULL);
InitializeCriticalSection(&g_cs);
InitializeCriticalSection(&g_render_cs);
g_ThreadQuit=0;
g_visdata_pstat=1;
AVS_EEL_IF_init();
if (Wnd_Init(this_mod)) return 1;
{
int x;
for (x = 0; x < 256; x ++)
{
double a=log((double)x*60.0/255.0 + 1.0)/log(60.0);
int t=(int)(a*255.0);
if (t<0)t=0;
if (t>255)t=255;
g_logtab[x]=(unsigned char )t;
}
}
initBpm();
Render_Init(g_hInstance);
CfgWnd_Create(this_mod);
g_hThread=(HANDLE)_beginthreadex(NULL,0,RenderThread,0,0,(unsigned int *)&id);
main_setRenderThreadPriority();
SetForegroundWindow(g_hwnd);
SetFocus(g_hwnd);
return 0;
}
static int render(struct winampVisModule *this_mod)
{
int x,avs_beat=0,b;
if (g_ThreadQuit) return 1;
EnterCriticalSection(&g_cs);
if (g_ThreadQuit)
{
LeaveCriticalSection(&g_cs);
return 1;
}
if (g_visdata_pstat)
for (x = 0; x< 576*2; x ++)
g_visdata[0][0][x]=g_logtab[(unsigned char)this_mod->spectrumData[0][x]];
else
{
for (x = 0; x < 576*2; x ++)
{
int t=g_logtab[(unsigned char)this_mod->spectrumData[0][x]];
if (g_visdata[0][0][x] < t)
g_visdata[0][0][x] = t;
}
}
memcpy(&g_visdata[1][0][0],this_mod->waveformData,576*2);
{
int lt[2]={0,0};
int x;
int ch;
for (ch = 0; ch < 2; ch ++)
{
unsigned char *f=(unsigned char*)&this_mod->waveformData[ch][0];
for (x = 0; x < 576; x ++)
{
int r= *f++^128;
r-=128;
if (r<0)r=-r;
lt[ch]+=r;
}
}
lt[0]=max(lt[0],lt[1]);
beat_peak1=(beat_peak1*125+beat_peak2*3)/128;
beat_cnt++;
if (lt[0] >= (beat_peak1*34)/32 && lt[0] > (576*16))
{
if (beat_cnt>0)
{
beat_cnt=0;
avs_beat=1;
}
beat_peak1=(lt[0]+beat_peak1_peak)/2;
beat_peak1_peak=lt[0];
}
else if (lt[0] > beat_peak2)
{
beat_peak2=lt[0];
}
else beat_peak2=(beat_peak2*14)/16;
}
b=refineBeat(avs_beat);
if (b) g_is_beat=1;
g_visdata_pstat=0;
LeaveCriticalSection(&g_cs);
return 0;
}
static void quit(struct winampVisModule *this_mod)
{
#define DS(x)
//MessageBox(this_mod->hwndParent,x,"AVS Debug",MB_OK)
if (g_hThread)
{
DS("Waitin for thread to quit\n");
g_ThreadQuit=1;
if (WaitForSingleObject(g_hThread,10000) != WAIT_OBJECT_0)
{
DS("Terminated thread (BAD!)\n");
//MessageBox(NULL,"error waiting for thread to quit","a",MB_TASKMODAL);
TerminateThread(g_hThread,0);
}
DS("Thread done... calling ddraw_quit\n");
DDraw_Quit();
DS("Calling cfgwnd_destroy\n");
CfgWnd_Destroy();
DS("Calling render_quit\n");
Render_Quit(this_mod->hDllInstance);
DS("Calling wnd_quit\n");
Wnd_Quit();
DS("closing thread handle\n");
CloseHandle(g_hThread);
g_hThread=NULL;
DS("calling eel quit\n");
AVS_EEL_IF_quit();
DS("cleaning up critsections\n");
DeleteCriticalSection(&g_cs);
DeleteCriticalSection(&g_render_cs);
DS("smp_cleanupthreads\n");
C_RenderListClass::smp_cleanupthreads();
}
#undef DS
#if 0//syntax highlighting
if (hRich) FreeLibrary(hRich);
hRich=0;
#endif
}
#define FPS_NF 64
static unsigned int WINAPI RenderThread(LPVOID a)
{
int framedata[FPS_NF]={0,};
int framedata_pos=0;
int s=0;
char vis_data[2][2][576];
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
srand(ft.dwLowDateTime|ft.dwHighDateTime^GetCurrentThreadId());
while (!g_ThreadQuit)
{
int w,h,*fb=NULL, *fb2=NULL,beat=0;
#ifdef REAPLAY_PLUGIN
if(!IsWindowVisible(g_hwnd))
{
Sleep(1);
continue;
}
char visdata[576*2*2];
int ret = vuGetVisData(visdata, sizeof(visdata));
if (!ret)
{
memset(&vis_data[0][0][0],0,576*2*2);
beat=0;
}
else
{
int x;
unsigned char *v=(unsigned char *)visdata;
for (x = 0; x < 576*2; x ++)
vis_data[0][0][x]=g_logtab[*v++];
for (x = 0; x < 576*2; x ++)
((unsigned char *)vis_data[1][0])[x]=*v++;
v=(unsigned char *)visdata+1152;
{
int lt[2]={0,0};
int ch;
for (ch = 0; ch < 2; ch ++)
{
for (x = 0; x < 576; x ++)
{
int r=*v++^128;
r-=128;
if (r<0)r=-r;
lt[ch]+=r;
}
}
lt[0]=max(lt[0],lt[1]);
beat_peak1=(beat_peak1*125+beat_peak2*3)/128;
beat_cnt++;
if (lt[0] >= (beat_peak1*34)/32 && lt[0] > (576*16))
{
if (beat_cnt>0)
{
beat_cnt=0;
beat=1;
}
beat_peak1=(lt[0]+beat_peak1_peak)/2;
beat_peak1_peak=lt[0];
}
else if (lt[0] > beat_peak2)
{
beat_peak2=lt[0];
}
else beat_peak2=(beat_peak2*14)/16;
}
// EnterCriticalSection(&g_title_cs);
beat=refineBeat(beat);
// LeaveCriticalSection(&g_title_cs);
}
#else
EnterCriticalSection(&g_cs);
memcpy(&vis_data[0][0][0],&g_visdata[0][0][0],576*2*2);
g_visdata_pstat=1;
beat=g_is_beat;
g_is_beat=0;
LeaveCriticalSection(&g_cs);
#endif
if (!g_ThreadQuit)
{
if (IsWindow(g_hwnd)&&!g_in_destroy) DDraw_Enter(&w,&h,&fb,&fb2);
else break;
if (fb&&fb2)
{
extern int g_dlg_w, g_dlg_h, g_dlg_fps;
#ifdef LASER
g_laser_linelist->ClearLineList();
#endif
EnterCriticalSection(&g_render_cs);
int t=g_render_transition->render(vis_data,beat,s?fb2:fb,s?fb:fb2,w,h);
LeaveCriticalSection(&g_render_cs);
if (t&1) s^=1;
#ifdef LASER
s=0;
memset(fb,0,w*h*sizeof(int));
LineDrawList(g_laser_linelist,fb,w,h);
#endif
if (IsWindow(g_hwnd)) DDraw_Exit(s);
int lastt=framedata[framedata_pos];
int thist=GetTickCount();
framedata[framedata_pos]=thist;
g_dlg_w=w;
g_dlg_h=h;
if (lastt)
{
g_dlg_fps=MulDiv(sizeof(framedata)/sizeof(framedata[0]),10000,thist-lastt);
}
framedata_pos++;
if (framedata_pos >= sizeof(framedata)/sizeof(framedata[0])) framedata_pos=0;
}
int fs=DDraw_IsFullScreen();
int sv=(fs?(cfg_speed>>8):cfg_speed)&0xff;
Sleep(min(max(sv,1),100));
}
}
_endthreadex(0);
return 0;
}
#ifdef REAPLAY_PLUGIN
static winampVisModule dummyMod;
extern "C"
{
REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(REAPER_PLUGIN_HINSTANCE hInstance, reaper_plugin_info_t *rec)
{
g_hInstance=hInstance;
if (rec)
{
if (rec->caller_version != REAPER_PLUGIN_VERSION || !rec->GetFunc)
return 0;
*((void **)&get_ini_file) = rec->GetFunc("get_ini_file");
*((void **)&vuGetVisData) = rec->GetFunc("vuGetVisData");
if (!get_ini_file || !vuGetVisData)
return 0;
dummyMod.hwndParent=rec->hwnd_main;
dummyMod.hDllInstance=g_hInstance;
init(&dummyMod);
return 1;
}
else
{
quit(&dummyMod);
return 0;
}
}
};
#endif