/* 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 #include #include #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 #include #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(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