diff options
Diffstat (limited to 'Src/Plugins/Input/in_midi/wa2.cpp')
-rw-r--r-- | Src/Plugins/Input/in_midi/wa2.cpp | 820 |
1 files changed, 820 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_midi/wa2.cpp b/Src/Plugins/Input/in_midi/wa2.cpp new file mode 100644 index 00000000..71e39ff2 --- /dev/null +++ b/Src/Plugins/Input/in_midi/wa2.cpp @@ -0,0 +1,820 @@ +#include "main.h" +#include "resource.h" +#include <shlwapi.h> +#include <api/service/waServiceFactory.h> +#include "../winamp/wa_ipc.h" +#include "../Agave/language/api_language.h" +#include "CompressionUtility.h" +#include "minizip/unzip.h" + +static bool paused; +static int volume=255; +static int pan=0; +static string cur_file; +static HANDLE thread; +static HINSTANCE hRFdll; +static reader_source * pRF; + +// wasabi based services for localisation support +api_language *WASABI_API_LNG = 0; +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; + +#define IPC_GETHTTPGETTER 240 + +typedef int (*t_getf)(HWND hwnd, char *url, char *file, char *dlgtitle); + +static WReader * get_reader(const char * fn);//wa2 hack + + +static int reader_process_file(WReader * r,const char * fn,void * &out_data, size_t &out_size) +{ + void * data=0; + int size=0; + char ks=0; + if (r->Open((char*)fn,&ks)) + return 0; + + size = r->GetLength(); + + if (size==-1 || size<0x20) + return 0; + + data=malloc(size);//scan funcs assume that theres at least 256 data + + if (!data) + return 0; + + if (r->Read((char*)data,size,&ks)!=size) + { + free(data); + return 0; + } + + void* pvOut; + size_t nSizeOut = 0; + // GZIP + if (((*(DWORD*)data) & 0xFFFFFF) == 0x088b1f) + { + if (CompressionUtility::DecompressGZip(data, size, &pvOut, nSizeOut) >= 0) + { + out_data = pvOut; + out_size = nSizeOut; + return 1; + } + else + { + return 0; + } + } + // PKZIP + else if (*(DWORD*)data == 0x04034B50) + { + if (CompressionUtility::DecompressPKZip(fn, &pvOut, nSizeOut) >= 0) + { + out_data = pvOut; + out_size = nSizeOut; + return 1; + } + else + { + return 0; + } + } + + out_size = size; + out_data = data; + return 1; +} + +MIDI_file * wa2_open_file(const char * url) +{ + WReader * r=get_reader(url); + if (!r) return 0; + void * data=0; + size_t size=0; + MIDI_file* mf=0; + if (reader_process_file(r,url,data,size)) + { + mf=MIDI_file::Create(url,data,size); + free(data); + } + delete r; + return mf; +} + +static void build_fmtstring(); + +static cfg_int cfg_mod_output("dev_output",1); + +static void wa2_onCfgUpdate() +{ + MIDI_device * dev = MIDI_driver::find_device(cfg_driver,cfg_device); + if (!dev) + { + dev = MIDI_driver::find_device_default(); + } + + //hack for wa2input.wac in wa3 + mod.UsesOutputPlug=(dev->has_output() || (cfg_smp && cfg_sampout)) ? 1 : 0x8001; + cfg_mod_output=mod.UsesOutputPlug; + build_fmtstring(); +} + +static char fmts_string[1024]; + +#define NSEEK(a) {while(!(cfg_ext_mask&(1<<a)) && a<n_exts) a++;} +static void build_fmtstring() +{ + UINT n_exts = MIDI_core::FileTypes_GetNum(); + if (!cfg_ext_mask) + { + fmts_string[1]=fmts_string[0]=0; + return; + } + UINT n=0; + NSEEK(n); + const char* d=MIDI_core::FileTypes_GetDescription(n); + char* o=fmts_string; + while(1) + { + UINT f=n; + while(n<n_exts && d==MIDI_core::FileTypes_GetDescription(n)) + { + const char * e=MIDI_core::FileTypes_GetExtension(n); + while(e && *e) *(o++)=*(e++); + n++; + NSEEK(n); + *(o++)=';'; + } + o[-1]=0; + while(d && *d) *(o++)=*(d++); + *(o++)=' '; + *(o++)='('; + while(f<n) + { + const char * e=MIDI_core::FileTypes_GetExtension(f); + while(e && *e) *(o++)=*(e++); + f++; + NSEEK(f); + *(o++)=','; + } + o[-1]=')'; + *(o++)=0; + if (n>=n_exts) break; + d=MIDI_core::FileTypes_GetDescription(n); + } + if (cfg_extra_exts.get_string().length()>0) + { + d=cfg_extra_exts; + while(d && *d) *(o++)=*(d++); + *(o++)=0; + d=WASABI_API_LNGSTRING(STRING_FILES_OTHER); + while(d && *d) *(o++)=*(d++); + d=cfg_extra_exts; + while(d && *d) + { + if (*d==';') *o=','; + else *o=*d; + o++; + d++; + } + *(o++)=')'; + *(o++)=0; + } + *(o++)=0; +} +#undef NSEEK + + +static void Config(HWND p) +{ + if (MIDI_core::Config(p)) + { + MIDI_core::WriteConfig(); + wa2_onCfgUpdate(); + } +} + +void About(HWND); + +class CMemReader : public WReader +{ +public: + BYTE* mem; + UINT sz; + UINT pos; + int Open(char *url, char *killswitch); + int Read(char *buffer, int length, char* killswitch) {if (!mem) return 0;if (length+pos>sz) length=sz-pos;memcpy(buffer,mem+pos,length);pos+=length;return length;} + int GetLength(void) {return sz;} + int CanSeek(void) {return 1;}; + int Seek(int position, char*killswitch) {pos=position;return 0;}; + + CMemReader() {mem=0;sz=0;pos=0;} + ~CMemReader() {if (mem) free(mem);} + +}; + +static int Download(char* url,UINT* f_size,BYTE** m_buf) +{ + typedef int (*t_getf)(HWND hwnd, char *url, char *file, char *dlgtitle); + + t_getf getf; + + int t=SendMessage(mod.hMainWindow,WM_USER,0,IPC_GETHTTPGETTER); + if (!t || t==1) + { +#ifndef WINAMPX + MessageBoxA(mod.hMainWindow,WASABI_API_LNGSTRING(STRING_URL_ERROR),ERROR,MB_ICONERROR); +#endif + return 0; + } + else + { + int rv=0; + char tmp[MAX_PATH] = {0}; + get_temp_file(tmp); + HANDLE f; + DWORD br = 0,s = 0; + void* b; + getf=(t_getf)t; + if (getf(mod.hMainWindow,url,tmp,WASABI_API_LNGSTRING(STRING_RETRIEVING_FILE))) goto fail; + f=CreateFileA(tmp,GENERIC_READ,0,0,OPEN_EXISTING,0,0); + if (f==INVALID_HANDLE_VALUE) goto fail; + br=0; + s=GetFileSize(f,0); + if (!s) goto fail; + b=malloc(s); + if (!b) goto fail; + ReadFile(f,b,s,&br,0); + rv=1; + *f_size=br; + *m_buf=(BYTE*)b; +fail: + CloseHandle(f); + DeleteFileA(tmp); + return rv; + } +} + + +int CMemReader::Open(char* url,char*) +{ + sz=pos=0; + if (mem) {free(mem);mem=0;} + HANDLE f=CreateFileA(url,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,0,OPEN_EXISTING,0,0); + if (f!=INVALID_HANDLE_VALUE) + { + sz=GetFileSize(f,0); + mem=(BYTE*)malloc(sz); + if (!mem) {CloseHandle(f);return 1;} + ReadFile(f,mem,sz,(DWORD*)&sz,0); + CloseHandle(f); + return 0; + } + return !Download(url,&sz,&mem); +} + +class CFileReader : public WReader +{ + public: + HANDLE f; + CFileReader() {f=0;}; + ~CFileReader() {if (f) CloseHandle(f);} + int Open(char *url, char*killswitch) {f=CreateFileA(url,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,0,OPEN_EXISTING,0,0);return f==INVALID_HANDLE_VALUE;} + int Read(char *buffer, int length, char*killswitch) {DWORD br=0;ReadFile(f,buffer,length,&br,0);return br;} + int GetLength(void) {return GetFileSize(f,0);} + int CanSeek(void) {return 1;}; + int Seek(int position, char*killswitch) {SetFilePointer(f,position,0,FILE_BEGIN);return 0;} + +}; + +static WReader *get_reader(const char* url) +{ + if (!_strnicmp(url,"file://",7)) url+=7; + WReader* ret=0; + if (pRF && pRF->ismine((char*)url)) ret=pRF->create(); + if (ret) + { + ret->m_player=0; + return ret; + } + + if (_strnicmp(url,"http://",7)==0 || _strnicmp(url,"ftp://",6)==0 || _strnicmp(url,"https://",8)==0) return new CMemReader; + return new CFileReader(); +} + +int Init() +{ + if (!IsWindow(mod.hMainWindow)) + return IN_INIT_FAILURE; + + // loader so that we can get the localisation service api for use + waServiceFactory *sf = mod.service->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(mod.hDllInstance,InMidiLangGUID); + + static wchar_t szDescription[256]; + swprintf(szDescription,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_MIDI_PLAYER),VER); + mod.description = (char*)szDescription; + + MIDI_core::GlobalInit(); + mod.UsesOutputPlug=cfg_mod_output; + build_fmtstring(); + return IN_INIT_SUCCESS; +} + +void Quit() +{ + MIDI_core::GlobalQuit(); +} + +void GetFileInfo(const char *url, char *title, int *len) +{ + if (!url || !*url) + { + url=cur_file; + } + if (len) *len=0; + if (title) *title=0; + + char ks=0; + + bool file_local=0; + MIDI_file * file=0; + if (MIDI_core::getFile() && !_stricmp(url,MIDI_core::getFile()->path)) + { + file = MIDI_core::getFile()->AddRef(); + } + + if (!file) + { + file = wa2_open_file(url); + if (!file) { + return; + } + file_local=1; + } + + if (len) + *len=file->GetLength(); + if (title) + file->GetTitle(title,256); + + file->Free(); +} + +BOOL CALLBACK InfoProc(HWND,UINT,WPARAM,LPARAM); + +int show_rmi_info(HWND w,MIDI_file* mf); + +int infoDlg(const char *fn, HWND hwnd) +{ + int rv=1; + MIDI_file *mf=wa2_open_file(fn); + + if (!mf) return INFOBOX_UNCHANGED; + + if (cfg_rmi_def) rv=show_rmi_info(hwnd,mf); + else + { + rv = WASABI_API_DIALOGBOXPARAM(IDD_INFO,hwnd,InfoProc,(LPARAM)mf); + } + if (!rv && !_stricmp(mf->path,cur_file)) + { + PostMessage(mod.hMainWindow,WM_USER,0,243); + } + mf->Free(); + return rv; +} + +int InfoBox(const char *file, HWND parent) +{ + if (!file) file=cur_file; + return infoDlg(file,parent); +} + +static char kill; +static int pos_ms; +static int seek_to; +static bool out_open; + +DWORD WINAPI PlayThread(void*) +{ +#ifdef USE_LOG + log_write("PlayThread"); +#endif + short * visbuf; + + char *sample_buf; + int sr,bps,nch; + pos_ms=0; + int pos_base=0; + int samp_wr=0; + int max_l=0; + MIDI_core::GetPCM(&sr,&nch,&bps); + int s_size=576 * (bps/8) * nch; + if (bps>16) + { + visbuf=(short*)malloc(576*2*nch); + } + else visbuf=0; + + sample_buf = (char*)malloc(576 * 2 * (bps/8) * nch); + + bool done=0; + while(!(kill&1)) + { +#ifdef USE_LOG + log_write("main loop"); +#endif + if (paused) { +#ifdef USE_LOG + log_write("paused"); +#endif + Sleep(10); + continue; + } + if (seek_to!=-1) + { +#ifdef USE_LOG + log_write("seeking"); +#endif + if (MIDI_core::SetPosition(seek_to)) + { + pos_ms=seek_to; + if (out_open) + { + mod.outMod->Flush(pos_ms); + } + pos_base=pos_ms; + samp_wr=0; + done=0; + } + kill&=~2; + seek_to=-1; + } + if (done) + { +#ifdef USE_LOG + log_write("done"); +#endif + if (!mod.outMod->IsPlaying()) + { + PostMessage(mod.hMainWindow,WM_WA_MPEG_EOF,0,0); + break; + } + Sleep(10);continue; + } +#ifdef USE_LOG + log_write("calling GetSamples"); +#endif + int l=MIDI_core::GetSamples(sample_buf,s_size,&kill); + if (kill&1) { +#ifdef USE_LOG + log_write("kill&1"); +#endif + break; + } + if (kill&2) { +#ifdef USE_LOG + log_write("kill&2"); +#endif + continue; + } + if (l<=0 && !paused) + { +#ifdef USE_LOG + log_write("done(?)"); +#endif + done=1; + if (out_open) + { + mod.outMod->Write(sample_buf,0); + continue; + } + else + { + PostMessage(mod.hMainWindow,WM_WA_MPEG_EOF,0,0); + break; + } + } + if (mod.dsp_isactive()) + { +#ifdef USE_LOG + log_write("DSP"); +#endif + l=(8*l)/(bps*nch); + l=mod.dsp_dosamples((short*)sample_buf,l,bps,nch,sr); + l*=(nch*bps)/8; + } + if (out_open) + { +#ifdef USE_LOG + log_write("sending to output"); +#endif + if (kill&1) break; + while(mod.outMod->CanWrite()<l && !kill) Sleep(2); + if (kill&1) break; + + if (!kill) mod.outMod->Write((char*)sample_buf,l); + } + { + char * vis=sample_buf; + UINT vis_bps=bps; + if (bps>16) + { + int n; + UINT d=bps>>3; + char * foo=sample_buf+d-2; + for(n=0;n<576*nch;n++) + { + visbuf[n]=*(short*)foo; + foo+=d; + } + vis=(char*)visbuf; + vis_bps=16; + } +#ifdef USE_LOG + log_write("doing vis"); +#endif + mod.SAAddPCMData(vis,nch,vis_bps,pos_ms); + mod.VSAAddPCMData(vis,nch,vis_bps,pos_ms); + + } + samp_wr+=(8*l)/(bps*nch); + pos_ms=pos_base+MulDiv(1000,samp_wr,sr); + } + + free(sample_buf); + if (visbuf) free(visbuf); + return 0; +} + +int initDefaultDeviceShit() +{ + //CT> find default device if no device set + MIDI_device * dev = MIDI_driver::find_device(cfg_driver,cfg_device); + if(dev) return 1; + + //reinit to default + MIDI_driver *driver=MIDI_driver::driver_enumerate(0); + if(!driver) return 0; + MIDI_device *device=driver->device_enumerate(0); + if(!device) return 0; + cfg_driver=driver->get_guid(); + cfg_device=device->get_guid(); + return 1; +} + +int Play(const char *fn) +{ + if(!initDefaultDeviceShit()) return 0; + + paused=0; + seek_to=-1; + kill=0; + + if (!MIDI_core::Init()) return 0; + + if (!MIDI_core::UsesOutput()) + { + MIDI_core::SetVolume(volume); + MIDI_core::SetPan(pan); + } + else + { + MIDI_core::SetVolume(255); + MIDI_core::SetPan(0); + } + + MIDI_file * file = wa2_open_file(fn); + if (!file) return -1; + + int rv=MIDI_core::OpenFile(file); + + file->Free(); + + if (rv==0) + { + MIDI_core::Close(); + return 1; + } + cur_file=fn; + int sr,nch,bps; + MIDI_core::GetPCM(&sr,&nch,&bps); + + { + MIDI_file * mf=MIDI_core::getFile(); + UINT nc=0; + if (mf) nc=mf->info.channels; + mod.SetInfo(nc*10000,sr/1000,2,1); + } + + if (MIDI_core::HavePCM()) + { + int max_l=0; + MIDI_core::GetPCM(&sr,&nch,&bps); + if (MIDI_core::UsesOutput()) + { +#ifdef USE_LOG + log_write("output init"); +#endif + max_l=mod.outMod->Open(sr,nch,bps,-1,-1); + if (max_l<0) + { + MIDI_core::Close(); + return 1; + } + out_open=1; + mod.outMod->SetVolume(volume); + mod.outMod->SetPan(pan); + } + mod.SAVSAInit(max_l,sr); + mod.VSASetInfo(sr,nch); +#ifdef USE_LOG + log_write("Creating thread"); +#endif + DWORD id; + thread=CreateThread(0,0,PlayThread,0,CREATE_SUSPENDED,&id); +#ifndef _DEBUG + SetThreadPriority(thread,THREAD_PRIORITY_TIME_CRITICAL); +#endif + ResumeThread(thread); + + } + else + { +#ifdef USE_LOG + log_write("threadless mode"); +#endif + thread=0; + } + + return 0; +} + +void Pause() +{ + if (MIDI_core::HavePlayer() && !paused) + { + MIDI_core::Pause(paused=1); + if (MIDI_core::UsesOutput()) mod.outMod->Pause(1); + } +} + +void UnPause() +{ + if (MIDI_core::HavePlayer() && paused) + { + MIDI_core::Pause(paused=0); + if (MIDI_core::UsesOutput()) + { + mod.outMod->Flush(0); + mod.outMod->Pause(0); + } + } +} + +int IsPaused() +{ + return paused; +} + +void Stop() +{ + if (thread) + { + kill|=1; + WaitForSingleObject(thread,INFINITE); + CloseHandle(thread); + thread=0; + mod.SAVSADeInit(); + + if (out_open) + { + out_open=0; + mod.outMod->Close(); + } + } + + MIDI_core::Close(); +} + +void EQSet(int on, char data[10], int preamp) +{ +} + +int GetLength() +{ + return MIDI_core::GetLength(); +} + +int GetOutputTime() +{ + if (seek_to!=-1) return seek_to; + + if (thread && MIDI_core::UsesOutput()) return pos_ms+mod.outMod->GetOutputTime()-mod.outMod->GetWrittenTime(); + else return MIDI_core::GetPosition(); +} + +void SetOutputTime(int t) +{ + if (thread) + { + seek_to=t; + kill|=2; + } + else MIDI_core::SetPosition(t); +} + +void SetVolume(int v) +{ + volume=v; + if (MIDI_core::UsesOutput()) mod.outMod->SetVolume(v); + else MIDI_core::SetVolume(v); +} + +void SetPan(int p) +{ + pan=p; + if (MIDI_core::UsesOutput()) mod.outMod->SetPan(p); + else MIDI_core::SetPan(p); +} + +In_Module mod= +{ + IN_VER_RET, + "nullsoft(in_midi.dll)", + 0,0, + + fmts_string, + + 1, + 1, + + Config, + About, + + Init, + Quit, + + GetFileInfo, + InfoBox, + + MIDI_core::IsOurFile, + Play, + Pause, + UnPause, + IsPaused, + Stop, + + GetLength, + GetOutputTime, + SetOutputTime, + + SetVolume, + SetPan, + + 0,0,0,0,0,0,0,0,0,0,0, + EQSet, + + 0, + 0, +}; + +extern "C" +{ + __declspec( dllexport ) In_Module * winampGetInModule2() + { + return &mod; + } +} + +void MIDI_callback::NotifyEOF() {PostMessage(mod.hMainWindow,WM_WA_MPEG_EOF,0,0);} +HWND MIDI_callback::GetMainWindow() {return mod.hMainWindow;} +HINSTANCE MIDI_callback::GetInstance() {return mod.hDllInstance;} + +void MIDI_callback::Error(const char * tx) +{ +#ifndef WINAMPX + MessageBoxA(mod.hMainWindow,tx,0,MB_ICONERROR); +#endif +} + +BOOL APIENTRY DllMain(HANDLE hMod,DWORD r,void*) +{ + if (r==DLL_PROCESS_ATTACH) + { + DisableThreadLibraryCalls((HMODULE)hMod); + } + return 1; + +} + +void MIDI_callback::Idle(int ms) +{ + int start = timeGetTime(); + do Sleep(1); while( (int)timeGetTime() - start < ms); +}
\ No newline at end of file |