aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_midi/wa2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Input/in_midi/wa2.cpp')
-rw-r--r--Src/Plugins/Input/in_midi/wa2.cpp820
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