mirror of
https://github.com/WinampDesktop/winamp.git
synced 2024-12-23 22:34:49 +01:00
605 lines
14 KiB
C++
605 lines
14 KiB
C++
/*
|
|
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
|