12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097 |
- #include "main.h"
- #include "genres.h"
- #include "decoder.h"
- #include "api__in_vorbis.h"
- #include "../Winamp/wa_ipc.h"
- #include "../nu/Singleton.h"
- #include "mkv_vorbis_decoder.h"
- #include <shlwapi.h>
- #include "../nu/AutoWide.h"
- #include "../nu/AutoChar.h"
- #include <strsafe.h>
- #include <api/service/waservicefactory.h>
- template <class api_T>
- void ServiceBuild(api_T *&api_t, GUID factoryGUID_t)
- {
- if (mod.service)
- {
- waServiceFactory *factory = mod.service->service_getServiceByGuid(factoryGUID_t);
- if (factory)
- api_t = reinterpret_cast<api_T *>( factory->getInterface() );
- }
- }
- template <class api_T>
- void ServiceRelease(api_T *api_t, GUID factoryGUID_t)
- {
- if (mod.service && api_t)
- {
- waServiceFactory *factory = mod.service->service_getServiceByGuid(factoryGUID_t);
- if (factory)
- factory->releaseInterface(api_t);
- }
- api_t = NULL;
- }
- VorbisFile * theFile = 0;
- extern CfgInt cfg_abr,cfg_httpseek2;
- OSVERSIONINFO os_ver = {0};
- static int pos_ms;
- static int seek_to=-1;
- static int length;
- static bool kill;
- StringW stat_disp;
- void show_stat(const wchar_t* txt)
- {
- if (txt)
- {
- stat_disp=txt;
- PostMessage(mod.hMainWindow,WM_USER,0,243);
- }
- else
- stat_disp=L"";
- }
- static int is_out_open;
- static int paused;
- static int volume=255;
- static int pan=0;
- StringW cur_file;
- CRITICAL_SECTION sync;
- HANDLE hThread=0;
- void Config(HWND);
- void About(HWND p);
- void do_cfg(int s);
- void GetFileInfo(const in_char *file, wchar_t *title, int *len);
- const char *INI_FILE=0;
- const wchar_t *INI_DIRECTORY=0;
- int (*warand)()=0;
- float (*warandf)()=0;
- api_application *WASABI_API_APP = 0;
- // wasabi based services for localisation support
- api_language *WASABI_API_LNG = 0;
- HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
- api_memmgr* WASABI_API_MEMMGR = 0;
- api_config *AGAVE_API_CONFIG=0;
- static MKVDecoderCreator mkvCreator;
- static SingletonServiceFactory<svc_mkvdecoder, MKVDecoderCreator> mkvFactory;
- void SetFileExtensions(void)
- {
- static char fileExtensionsString[1200] = {0}; // "OGG\0Ogg files (*.OGG)\0"
- char* end = 0;
- StringCchCopyExA(fileExtensionsString, 1200, "OGG;OGA", &end, 0, 0);
- StringCchCopyExA(end+1, 1200, WASABI_API_LNGSTRING(IDS_OGG_FILES), 0, 0, 0);
- mod.FileExtensions = fileExtensionsString;
- }
- int Init()
- {
- if (!IsWindow(mod.hMainWindow))
- return IN_INIT_FAILURE;
- mod.UsesOutputPlug|=8;
- warand = (int (*)())SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GET_RANDFUNC);
- warandf = (float (*)())SendMessage(mod.hMainWindow, WM_WA_IPC, 1, IPC_GET_RANDFUNC);
- ServiceBuild(WASABI_API_MEMMGR, memMgrApiServiceGuid);
- ServiceBuild(AGAVE_API_CONFIG, AgaveConfigGUID);
- // loader so that we can get the localisation service api for use
- ServiceBuild(WASABI_API_LNG, languageApiGUID);
- ServiceBuild(WASABI_API_APP, applicationApiServiceGuid);
- mkvFactory.Register(mod.service, &mkvCreator);
- // need to have this initialised before we try to do anything with localisation features
- WASABI_API_START_LANG(mod.hDllInstance,InVorbisLangGUID);
- static wchar_t szDescription[256];
- StringCchPrintfW(szDescription,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_VORBIS_DECODER),VER);
- mod.description = (char*)szDescription;
- SetFileExtensions();
- INI_FILE = (const char *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIFILE);
- INI_DIRECTORY = (const wchar_t *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIDIRECTORYW);
- os_ver.dwOSVersionInfoSize=sizeof(os_ver);
- GetVersionEx(&os_ver);
- InitializeCriticalSection(&sync);
- do_cfg(0);
- return IN_INIT_SUCCESS;
- }
- void Quit()
- {
- winampGetExtendedFileInfoW_Cleanup();
- DeleteCriticalSection(&sync);
- mkvFactory.Deregister(mod.service);
- ServiceRelease(WASABI_API_MEMMGR, memMgrApiServiceGuid);
- ServiceRelease(AGAVE_API_CONFIG, AgaveConfigGUID);
- ServiceRelease(WASABI_API_LNG, languageApiGUID);
- ServiceRelease(WASABI_API_APP, applicationApiServiceGuid);
- }
- int GetLength()
- {
- return length;
- }
- int IsOurFile(const in_char *fn)
- {
- if (PathIsURLW(fn))
- {
- const wchar_t *foo=wcsrchr(fn,L'.');
- return foo ? !_wcsicmp(foo,L".ogg") : 0;
- }
- else return 0;
- }
- static UINT kbps_disp;
- static void out_close()
- {
- if (is_out_open)
- {
- mod.outMod->Close();
- mod.SAVSADeInit();
- is_out_open=0;
- }
- }
- static bool need_full_setinfo;
- static int out_open(const Decoder &dec)
- {
- int max_l=mod.outMod->Open(dec.sr,dec.nch,dec.bps,-1,-1);
- if (max_l<0) return 0;
- mod.outMod->SetVolume(-666);
- mod.outMod->SetPan(pan);
- mod.SAVSAInit(max_l,dec.sr);
- mod.VSASetInfo(dec.sr,dec.nch);
- is_out_open=1;
- need_full_setinfo=1;
- return 1;
- }
- void Decoder::wa2_setinfo(UINT cur)
- {
- UINT disp=file->get_avg_bitrate();
- if (!cfg_abr)
- {
- disp=cur;
- }
- if ((disp && disp!=kbps_disp) || need_full_setinfo)
- {
- kbps_disp=disp;
- if (need_full_setinfo)
- {
- mod.SetInfo(disp,sr/1000,nch,1);
- need_full_setinfo=0;
- }
- else mod.SetInfo(disp,-1,-1,1);
- }
- }
- static bool need_movefile;
- static void process_movefile();
- void alloc_buffers(Decoder & dec,short ** visbuf,char ** sample_buf,int * s_size)
- {
- *s_size=576 * (dec.bps>>3) * dec.nch;
- if (*sample_buf) *sample_buf=(char*)realloc(*sample_buf,*s_size*2);
- else *sample_buf=(char*)malloc(*s_size*2);
- if (dec.bps>16)
- {
- int vs=576*2*dec.nch;
- if (*visbuf) *visbuf=(short*)realloc(*visbuf,vs);
- else *visbuf=(short*)malloc(vs);
- }
- else if (*visbuf) {free(*visbuf);*visbuf=0;}
- }
- static DWORD WINAPI PlayThread(Decoder &dec)
- {
- int pos_base=0;
- int samp_wr=0;
- int done=0;
- int upd=0;
- __int64 brate;
- int br_div,br_t;
- short* visbuf=0;
- char *sample_buf=0;
- int retries=0;
- int s_size=0;
- pos_ms=0;
- {
- int r;
- r=dec.play_init();
- if (r)
- {
- if (!kill) Sleep(50);
- if (!kill) Sleep(50);
- if (!kill) Sleep(50);
- if (!kill) Sleep(50);
- if (!kill)
- {
- if (r==2) PostMessage(mod.hMainWindow,WM_WA_MPEG_EOF,0,0);
- else PostMessage(mod.hMainWindow,WM_COMMAND,40047,0);
- }
- delete &dec;
- return 0;
- }
- theFile->do_prebuf();
- }
- brate=0;
- br_div=0;
- upd=0;
- alloc_buffers(dec,&visbuf,&sample_buf,&s_size);
- //int f_type=theFile->GetType();
- bool is_live=theFile->IsLive();
- while(!kill)
- {
- if (!theFile) break;//ugh
- if (seek_to!= -1)
- {
- UINT _st=seek_to;
- int r=1;
- seek_to=-1;
- if (theFile)
- {
- theFile->use_prebuf=0;
- int link=theFile->vf.current_link;
- r=dec.Seek((double)_st*0.001);
- if (link!=theFile->vf.current_link) PostMessage(mod.hMainWindow,WM_USER,0,243);
- }
- else r=1;
- if (!r)
- {
- pos_base=pos_ms=_st;
- mod.outMod->Flush(pos_ms);
- samp_wr=0;
- done=0;
- theFile->do_prebuf();
- }
- }
- if (need_movefile && paused)//HACK, prevent stupid lockup
- {
- process_movefile();
- if (!theFile) break;//#@!
- dec.file=theFile;
- dec.Flush();
- }
- if (done)
- {
- // mod.outMod->CanWrite();
- if (!mod.outMod->IsPlaying())
- {
- PostMessage(mod.hMainWindow,WM_WA_MPEG_EOF,0,0);
- break;
- }
- Sleep(10);
- }
- else if (mod.outMod->CanWrite() >= (s_size<<(mod.dsp_isactive()?1:0)))
- {
- int l=0;
- while(1)
- {
- if (!dec.need_reopen)
- {
- l+=dec.Read(s_size-l,sample_buf+l);
- if (l>=s_size) break;
- int link=theFile->vf.current_link;
- if (need_movefile)//safe not to flush here
- {
- process_movefile();
- if (!theFile) break;//#@!
- dec.file=theFile;
- }
- if (!dec.DoFrame()) break;
- if (kill) break;
- if (link!=theFile->vf.current_link)
- {
- PostMessage(mod.hMainWindow,WM_USER,0,243);
- }
- br_t=ov_bitrate_instant(&theFile->vf);
- if (br_t>0)
- {
- int i = dec.DataAvailable()/((dec.bps/8)*dec.nch);
- br_div+=i;
- brate+=(__int64)(br_t*i);
- }
- if (need_full_setinfo || (!((++upd)%200) && br_div))
- {
- if (!br_div) {br_div=1;brate=theFile->get_avg_bitrate();}
- dec.wa2_setinfo((int)((__int64)brate/(__int64)br_div/(__int64)1000));
- brate=0;
- br_div=0;
- }
- }
- if (dec.need_reopen)
- {//blargh, new PCM format
- if (l>0) break;//got samples to play, we'll deal with this later
- //l=0;
- while(!kill && mod.outMod->IsPlaying()) Sleep(1);
- if (kill) break;
- out_close();
- if (!out_open(dec))//boo
- {
- PostMessage(mod.hMainWindow,WM_COMMAND,40047,0);
- kill=1;
- break;
- }
- alloc_buffers(dec,&visbuf,&sample_buf,&s_size);
- dec.need_reopen=0;
- }
- }
- if (kill || !theFile) break;
- if (l<=0 && (!is_live || (--retries)<0))
- {
- mod.outMod->Write(sample_buf,0);
- done=1;
- }
- else if (l<=0)
- {
- int r;
- out_close();
- EnterCriticalSection(&sync);
- delete theFile;
- theFile=0;
- LeaveCriticalSection(&sync);
- if (sample_buf)
- {
- free(sample_buf);
- sample_buf=0;
- }
- r=dec.play_init();
- if (r)
- {
- mod.outMod->Write(sample_buf,0);
- done=1;
- }
- else
- {
- theFile->do_prebuf();
- }
- }
- else
- {
- if (l<s_size) memset(sample_buf+l,dec.bps==8 ? 0x80 : 0,s_size-l);
- char * vis=sample_buf;
- UINT vis_bps=dec.bps;
- if (dec.bps>16)
- {
- UINT n;
- UINT d=dec.bps>>3;
- char * foo=sample_buf+d-2;
- for(n=0;n<576*dec.nch;n++)
- {
- visbuf[n]=*(short*)foo;
- foo+=d;
- }
- vis=(char*)visbuf;
- vis_bps=16;
- }
- mod.SAAddPCMData(vis,dec.nch,vis_bps,pos_ms);
- mod.VSAAddPCMData(vis,dec.nch,vis_bps,pos_ms);
- if (mod.dsp_isactive())
- {
- l=(l<<3)/(dec.bps*dec.nch);
- l=mod.dsp_dosamples((short*)sample_buf,l,dec.bps,dec.nch,dec.sr);
- l*=(dec.nch*dec.bps)>>3;
- }
- if (kill) break;
- mod.outMod->Write((char*)sample_buf,l);
- samp_wr+=(8*l)/(dec.bps*dec.nch);
- pos_ms=pos_base+MulDiv(1000,samp_wr,dec.sr);
- }
- }
- else
- {
- theFile->Idle();
- }
- }
- // out_close();
- // gay gapless plugins puke, need to call this from stop
- // ok, hetero (out_wave v2.x / out_ds v1.4+) gapless plugins wouldn't puke anymore
- if (theFile)
- {
- VorbisFile * t=theFile;
- EnterCriticalSection(&sync);
- theFile=0;
- LeaveCriticalSection(&sync);
- delete t;
- }
- if (sample_buf)
- {
- free(sample_buf);
- sample_buf=0;
- }
- if (need_movefile) process_movefile();
- /* if (!kill)
- {
- CloseHandle(hThread);
- hThread=0;
- }*/
- if (visbuf) free(visbuf);
- delete &dec;
- return 0;
- }
- static StringW move_src,move_dst;
- static bool mf_ret;
- static void do_movefile()
- {
- mf_ret=1;
- winampGetExtendedFileInfoW_Cleanup();
- if (!DeleteFileW(move_dst)) mf_ret=0;
- else
- {
- if (!MoveFileW(move_src,move_dst))
- {
- if (!CopyFileW(move_src,move_dst,0)) mf_ret=0;
- DeleteFileW(move_src);
- }
- }
- }
- static void process_movefile()
- {
- if (theFile)
- {
- StringW f_path;
- f_path.AddString(theFile->url);
- double pos=theFile->GetPos();
- EnterCriticalSection(&sync);
- delete theFile;
- theFile=0;
- do_movefile();
- theFile=VorbisFile::Create(f_path,0);
- LeaveCriticalSection(&sync);
- if (theFile)
- {
- theFile->Seek(pos);
- }
- }
- else do_movefile();
- need_movefile=0;
- }
- bool sync_movefile(const wchar_t * src,const wchar_t * dst)//called from info_.cpp
- {
- move_src=src;
- move_dst=dst;
- need_movefile=1;
- if (!theFile) process_movefile();
- else
- {
- while(need_movefile && hThread) Sleep(1);
- if (need_movefile) process_movefile();//shouldnt really happen
- move_src=L"";
- move_dst=L"";
- PostMessage(mod.hMainWindow,WM_USER,0,243);
- }
- return mf_ret;
- }
- int Decoder::play_init()//still messy
- {
- if (play_inited) return 0;
- kbps_disp=0;
- VorbisFile * t=VorbisFile::Create(cur_file,0);
- if (!t)
- {
- #ifdef _DEBUG
- OutputDebugString(L"can't open file\n");
- #endif
- // if (scream) MessageBox(mod.hMainWindow,"error opening file",0,MB_ICONERROR);
- return 2;
- }
- Init(t);
- if (!out_open(*this))
- {
- #ifdef _DEBUG
- OutputDebugString(L"can't open output\n");
- #endif
- delete t;
- return 1;
- }
- EnterCriticalSection(&sync);
- theFile=t;
- LeaveCriticalSection(&sync);
- wa2_setinfo(theFile->get_avg_bitrate());
- {
- double v=theFile->Length();
- if (v==OV_EINVAL || v<=0) length=-1;
- else length=(int)(v*1000.0);
- }
- play_inited=1;
- return 0;
- }
- int Play(const in_char *fn)
- {
- seek_to=-1;
- kill=0;
- length=0;
- paused=0;
- show_stat(0);
- EnterCriticalSection(&sync);
- cur_file=fn;
- LeaveCriticalSection(&sync);
- Decoder * dec=new Decoder;
- if (!PathIsURLW(fn))
- {
- mod.is_seekable=1;
- #if 1
- int rv=dec->play_init();
- if (rv)
- {
- delete dec;
- if (rv==2) return -1;
- return 1;
- }
- #endif
- }
- else mod.is_seekable=cfg_httpseek2;
- {
- DWORD id;
- hThread=CreateThread(0,0,(LPTHREAD_START_ROUTINE)PlayThread,dec,CREATE_SUSPENDED,&id);
- }
- if (hThread)
- {
- SetThreadPriority(hThread, (int)AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
- ResumeThread(hThread);
- return 0;
- }
- else
- {
- out_close();
- delete dec;
- return 1;
- }
- }
- void Pause()
- {
- if (!paused)
- {
- mod.outMod->Pause(1);
- paused=1;
- }
- }
- void UnPause()
- {
- if (paused)
- {
- mod.outMod->Pause(0);
- paused=0;
- }
- }
- int IsPaused()
- {
- return paused;
- }
- void Stop()
- {
- if (hThread)
- {
- kill=1;
- EnterCriticalSection(&sync);
- if (theFile) theFile->stopping=1;
- LeaveCriticalSection(&sync);
- if (WaitForSingleObject(hThread,10000)!=WAIT_OBJECT_0)
- {
- TerminateThread(hThread,0);
- //MessageBox(mod.hMainWindow,"error asking thread to die",0,MB_ICONERROR);
- }
- CloseHandle(hThread);
- hThread=0;
- out_close();
- }
- show_stat(0);
- winampGetExtendedFileInfoW_Cleanup();
- }
- void EQSet(int on, char data[10], int preamp)
- {
- }
- int GetOutputTime()
- {
- return pos_ms+(mod.outMod->GetOutputTime()-mod.outMod->GetWrittenTime());
- }
- void SetOutputTime(int t)
- {
- seek_to=t;
- EnterCriticalSection(&sync);
- if (theFile) theFile->abort_prebuf=1;
- LeaveCriticalSection(&sync);
- }
- void SetVolume(int v)
- {
- mod.outMod->SetVolume(volume=v);
- }
- void SetPan(int p)
- {
- mod.outMod->SetPan(pan=p);
- }
- //int InfoBox(char *file, HWND parent); //old
- int RunInfoDlg(const in_char * url,HWND parent);
- In_Module mod=
- {
- IN_VER_RET,
- "nullsoft(in_vorbis.dll)",
- 0,0,
- 0,
- 1,
- 1,
- Config,
- About,
- Init,
- Quit,
- GetFileInfo,
- RunInfoDlg,
- 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 VorbisFile::Status(const wchar_t * zzz)
- {
- if (primary)
- show_stat(zzz);
- }
- bool VorbisFile::Aborting()
- {
- return stopping || (primary && kill);
- }
- Info::Info(const wchar_t *filename) : filename(filename), vc(0)
- {
- VorbisFile * vf = VorbisFile::Create(filename,true);
- if(!vf)
- return;
- numstreams = vf->vf.links;
- if(numstreams)
- {
- // now copy the comment section to our own memory...
- stream = vf->vf.current_link; // this is the stream we're editing...
- vc = (vorbis_comment*)calloc(sizeof(vorbis_comment),numstreams);
-
- for(int i=0; i<numstreams; i++)
- { // one comment section per stream
- vorbis_comment *c = ov_comment(&vf->vf,i);
- vc[i].comments = c->comments;
- vc[i].user_comments = (char **)malloc(sizeof(char*)*c->comments);
- vc[i].comment_lengths = (int *)malloc(sizeof(int)*c->comments);
- for(int j=0;j<vc[i].comments;j++)
- { // copy the comments over
- vc[i].user_comments[j] = _strdup(c->user_comments[j]);
- vc[i].comment_lengths[j] = c->comment_lengths[j];
- }
- vc[i].vendor=_strdup(c->vendor);
- }
- }
- delete vf;
- }
- Info::~Info()
- {
- if(vc) {
- for(int i=0; i < numstreams; i++)
- vorbis_comment_clear(&vc[i]);
- free(vc);
- }
- }
- bool Info::Save()
- {
- return !!modify_file(filename,vc,numstreams);
- }
- int Info::GetNumMetadataItems()
- {
- return vc[stream].comments;
- }
- void Info::EnumMetadata(int n, wchar_t *key, int keylen, wchar_t *val, int vallen)
- {
- if(keylen) key[0]=0;
- if(vallen) val[0]=0;
- if(!vc) return;
- if(!vc[stream].user_comments[n]) return;
- AutoWide comment(vc[stream].user_comments[n],CP_UTF8);
- const wchar_t* eq = wcschr((const wchar_t*)comment,L'=');
- if(eq)
- {
- if(keylen) lstrcpynW(key,comment,(int)min(eq - comment + 1,keylen));
- if(vallen) lstrcpynW(val,eq+1,vallen);
- }
- else
- {
- if(keylen) lstrcpynW(key,L"COMMENT",keylen);
- if(vallen) lstrcpynW(val,comment,vallen);
- }
- }
- void Info::RemoveMetadata(wchar_t * key)
- {
- wchar_t k[256] = {0};
- for(int i=0; i<GetNumMetadataItems(); i++)
- {
- EnumMetadata(i,k,256,0,0);
- if(_wcsicmp(k,key)==0)
- RemoveMetadata(i);
- }
- }
- void Info::RemoveMetadata(int n)
- {
- if(!vc) return;
- free(vc[stream].user_comments[n]);
-
- for(int i=n+1; i<vc[stream].comments; i++)
- {
- vc[stream].user_comments[i-1] = vc[stream].user_comments[i];
- if(vc[stream].comment_lengths)
- vc[stream].comment_lengths[i-1] = vc[stream].comment_lengths[i];
- }
- vc[stream].comments--;
- vc[stream].user_comments = (char**)realloc(vc[stream].user_comments,sizeof(vc[stream].user_comments[0]) * vc[stream].comments);
- if(vc[stream].comment_lengths)
- vc[stream].comment_lengths = (int*)realloc(vc[stream].comment_lengths,sizeof(vc[stream].comment_lengths[0]) * vc[stream].comments);
- }
- void Info::SetMetadata(wchar_t *key, wchar_t *val)
- {
- bool set=false;
- wchar_t k[256] = {0};
- for(int i=0; i<GetNumMetadataItems(); i++)
- {
- EnumMetadata(i,k,256,0,0);
- if(_wcsicmp(k,key)==0)
- {
- SetMetadata(i,key,val);
- set=true;
- }
- }
- if(!set)
- {
- int n = vc[stream].comments++;
- vc[stream].user_comments = (char**)realloc(vc[stream].user_comments,sizeof(vc[stream].user_comments[0]) * vc[stream].comments);
- if(vc[stream].comment_lengths)
- vc[stream].comment_lengths = (int*)realloc(vc[stream].comment_lengths,sizeof(vc[stream].comment_lengths[0]) * vc[stream].comments);
- vc[stream].user_comments[n] = NULL;
- SetMetadata(n,key,val);
- }
- }
- void Info::SetMetadata(int n, wchar_t *key, wchar_t *val)
- {
- AutoChar k(key,CP_UTF8);
- AutoChar v(val,CP_UTF8);
- int l = (int)(strlen(k)+strlen(v)+2);
- char * c = (char*)malloc(l);
- StringCchPrintfA(c,l,"%s=%s",(char*)k,(char*)v);
-
- if(vc[stream].user_comments[n])
- free(vc[stream].user_comments[n]);
- vc[stream].user_comments[n] = c;
- if(vc[stream].comment_lengths)
- vc[stream].comment_lengths[n] = l-1;
- }
- void Info::SetTag(int n,wchar_t *key) // changes the key name
- {
- wchar_t val[2048] = {0};
- EnumMetadata(n,NULL,0,val,2048);
- SetMetadata(n,key,val);
- }
- Info *setMetadata = 0;
- extern "C"
- {
- static wchar_t m_lastfn[2048];
- #define START_TAG_ALIAS(name, alias) if (KeywordMatch(data, name)) lookup=alias
- #define TAG_ALIAS(name, alias) else if (KeywordMatch(data, name)) lookup=alias
- __declspec( dllexport ) int winampSetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *val)
- {
- if (!setMetadata || setMetadata && wcscmp(fn,m_lastfn))
- {
- if (setMetadata)
- {
- delete setMetadata;
- setMetadata = 0;
- }
- setMetadata = new Info(fn);
- if(setMetadata->Error())
- {
- delete setMetadata;
- m_lastfn[0] = 0;
- return 0;
- }
- lstrcpynW(m_lastfn,fn, 2048);
- }
- wchar_t *lookup=0;
- START_TAG_ALIAS("artist", L"ARTIST");
- TAG_ALIAS("title", L"TITLE");
- TAG_ALIAS("album", L"ALBUM");
- TAG_ALIAS("genre", L"GENRE");
- TAG_ALIAS("comment", L"COMMENT");
- TAG_ALIAS("year", L"DATE");
- TAG_ALIAS("track", L"TRACKNUMBER");
- TAG_ALIAS("albumartist", L"ALBUM ARTIST");
- TAG_ALIAS("composer", L"COMPOSER");
- TAG_ALIAS("disc", L"DISCNUMBER");
- TAG_ALIAS("publisher", L"PUBLISHER");
- TAG_ALIAS("conductor", L"CONDUCTOR");
- TAG_ALIAS("tool", L"ENCODED-BY");
- TAG_ALIAS("replaygain_track_gain", L"REPLAYGAIN_TRACK_GAIN");
- TAG_ALIAS("replaygain_track_peak", L"REPLAYGAIN_TRACK_PEAK");
- TAG_ALIAS("replaygain_album_gain", L"REPLAYGAIN_ALBUM_GAIN");
- TAG_ALIAS("replaygain_album_peak", L"REPLAYGAIN_ALBUM_PEAK");
- TAG_ALIAS("GracenoteFileID", L"GRACENOTEFILEID");
- TAG_ALIAS("GracenoteExtData", L"GRACENOTEEXTDATA");
- TAG_ALIAS("bpm", L"BPM");
- TAG_ALIAS("remixing", L"REMIXING");
- TAG_ALIAS("subtitle", L"VERSION");
- TAG_ALIAS("isrc", L"ISRC");
- TAG_ALIAS("category", L"CATEGORY");
- TAG_ALIAS("rating", L"RATING");
- TAG_ALIAS("producer", L"PRODUCER");
- if (!lookup)
- return 0;
- #if 0
- if (val && *val)
- {
- if(KeywordMatch("rating",data))
- {
- wchar_t temp[128] = {0};
- StringCchPrintfW(temp, 128, L"%u", _wtoi(val)*20);
- val=temp;
- }
- }
- AutoChar utf8(val, CP_UTF8);
- for(int i=0;i<m_vc->comments;i++)
- {
- char *c=m_vc[m_curstream].user_comments[i];
- if(!c) continue;
- char *p=strchr(c,'=');
- if (p && *p)
- {
- if(strlen(data) == (p-c) && !_strnicmp(c,data,p-c))
- {
- //found!
- if (val && val[0])
- {
- int added_buf_len = strlen(utf8)+strlen(lookup)+2;
- m_vc[m_curstream].user_comments[i]=(char *)realloc(m_vc[m_curstream].user_comments[i],added_buf_len);
- StringCchPrintfA(m_vc[m_curstream].user_comments[i],added_buf_len,"%s=%s",lookup,(char *)utf8);
- m_vc[m_curstream].comment_lengths[i]=strlen(m_vc[m_curstream].user_comments[i]);
- }
- else
- {
- free(m_vc[m_curstream].user_comments[i]);
- m_vc[m_curstream].user_comments[i]=0;
- m_vc[m_curstream].comment_lengths[i]=0;
- }
- return 1;
- }
- }
- }
- //not found, so create new field
- if (val && val[0])
- {
- int k=m_vc[m_curstream].comments++;
- m_vc[m_curstream].user_comments=(char **)realloc(m_vc[m_curstream].user_comments,sizeof(char*)*m_vc[m_curstream].comments);
- m_vc[m_curstream].comment_lengths=(int *)realloc(m_vc[m_curstream].comment_lengths,sizeof(int)*m_vc[m_curstream].comments);
- int added_buf_len = strlen(utf8)+strlen(lookup)+2;
- m_vc[m_curstream].user_comments[k]=(char *)malloc(added_buf_len);
- StringCchPrintfA(m_vc[m_curstream].user_comments[k],added_buf_len,"%s=%s",lookup,(char *)utf8);
- m_vc[m_curstream].comment_lengths[k]=strlen(m_vc[m_curstream].user_comments[k]);
- }
- #endif
- if (val && *val)
- {
- if(KeywordMatch("rating",data))
- {
- wchar_t temp[128] = {0};
- StringCchPrintfW(temp, 128, L"%u", _wtoi(val)*20);
- setMetadata->SetMetadata(lookup, temp);
- }
- else
- {
- setMetadata->SetMetadata(lookup, val);
- }
- }
- else
- {
- setMetadata->RemoveMetadata(lookup);
- if(KeywordMatch("comment",data))
- {
- // need to remove this one also, or else it's gonna look like delete doesn't work
- // if the file was tagged using this alternate field
- setMetadata->RemoveMetadata(L"DESCRIPTION");
- }
- else if(KeywordMatch("year",data))
- {
- // need to remove this one also, or else it's gonna look like delete doesn't work
- // if the file was tagged using this alternate field
- setMetadata->RemoveMetadata(L"YEAR");
- }
- else if(KeywordMatch("track",data))
- {
- // need to remove this one also, or else it's gonna look like delete doesn't work
- // if the file was tagged using this alternate field
- setMetadata->RemoveMetadata(L"TRACK");
- }
- else if(KeywordMatch("albumartist",data))
- {
- // need to remove these two, also, or else it's gonna look like delete doesn't work
- // if the file was tagged using these alternate fields
- setMetadata->RemoveMetadata(L"ALBUMARTIST");
- setMetadata->RemoveMetadata(L"ENSEMBLE");
- }
- else if(KeywordMatch("publisher",data))
- {
- // need to remove this one also, or else it's gonna look like delete doesn't work
- // if the file was tagged using this alternate field
- setMetadata->RemoveMetadata(L"ORGANIZATION");
- }
- else if(KeywordMatch("category",data))
- {
- // need to remove these two also, or else it's gonna look like delete doesn't work
- // if the file was tagged using these alternate fields
- setMetadata->RemoveMetadata(L"CONTENTGROUP");
- setMetadata->RemoveMetadata(L"GROUPING");
- }
- }
- return 1;
- }
- __declspec( dllexport ) int winampWriteExtendedFileInfo()
- {
- if(!setMetadata) return 0;
- bool ret = setMetadata->Save();
- delete setMetadata;
- setMetadata = 0;
- // update last modified so we're not re-queried on our own updates
- UpdateFileTimeChanged(m_lastfn);
- return ret;
- }
- }
|