winamp/Src/Plugins/Input/in_midi/wa2.cpp
2024-09-24 14:54:57 +02:00

820 lines
15 KiB
C++

#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);
}