wa2.cpp 23 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097
  1. #include "main.h"
  2. #include "genres.h"
  3. #include "decoder.h"
  4. #include "api__in_vorbis.h"
  5. #include "../Winamp/wa_ipc.h"
  6. #include "../nu/Singleton.h"
  7. #include "mkv_vorbis_decoder.h"
  8. #include <shlwapi.h>
  9. #include "../nu/AutoWide.h"
  10. #include "../nu/AutoChar.h"
  11. #include <strsafe.h>
  12. #include <api/service/waservicefactory.h>
  13. template <class api_T>
  14. void ServiceBuild(api_T *&api_t, GUID factoryGUID_t)
  15. {
  16. if (mod.service)
  17. {
  18. waServiceFactory *factory = mod.service->service_getServiceByGuid(factoryGUID_t);
  19. if (factory)
  20. api_t = reinterpret_cast<api_T *>( factory->getInterface() );
  21. }
  22. }
  23. template <class api_T>
  24. void ServiceRelease(api_T *api_t, GUID factoryGUID_t)
  25. {
  26. if (mod.service && api_t)
  27. {
  28. waServiceFactory *factory = mod.service->service_getServiceByGuid(factoryGUID_t);
  29. if (factory)
  30. factory->releaseInterface(api_t);
  31. }
  32. api_t = NULL;
  33. }
  34. VorbisFile * theFile = 0;
  35. extern CfgInt cfg_abr,cfg_httpseek2;
  36. OSVERSIONINFO os_ver = {0};
  37. static int pos_ms;
  38. static int seek_to=-1;
  39. static int length;
  40. static bool kill;
  41. StringW stat_disp;
  42. void show_stat(const wchar_t* txt)
  43. {
  44. if (txt)
  45. {
  46. stat_disp=txt;
  47. PostMessage(mod.hMainWindow,WM_USER,0,243);
  48. }
  49. else
  50. stat_disp=L"";
  51. }
  52. static int is_out_open;
  53. static int paused;
  54. static int volume=255;
  55. static int pan=0;
  56. StringW cur_file;
  57. CRITICAL_SECTION sync;
  58. HANDLE hThread=0;
  59. void Config(HWND);
  60. void About(HWND p);
  61. void do_cfg(int s);
  62. void GetFileInfo(const in_char *file, wchar_t *title, int *len);
  63. const char *INI_FILE=0;
  64. const wchar_t *INI_DIRECTORY=0;
  65. int (*warand)()=0;
  66. float (*warandf)()=0;
  67. api_application *WASABI_API_APP = 0;
  68. // wasabi based services for localisation support
  69. api_language *WASABI_API_LNG = 0;
  70. HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
  71. api_memmgr* WASABI_API_MEMMGR = 0;
  72. api_config *AGAVE_API_CONFIG=0;
  73. static MKVDecoderCreator mkvCreator;
  74. static SingletonServiceFactory<svc_mkvdecoder, MKVDecoderCreator> mkvFactory;
  75. void SetFileExtensions(void)
  76. {
  77. static char fileExtensionsString[1200] = {0}; // "OGG\0Ogg files (*.OGG)\0"
  78. char* end = 0;
  79. StringCchCopyExA(fileExtensionsString, 1200, "OGG;OGA", &end, 0, 0);
  80. StringCchCopyExA(end+1, 1200, WASABI_API_LNGSTRING(IDS_OGG_FILES), 0, 0, 0);
  81. mod.FileExtensions = fileExtensionsString;
  82. }
  83. int Init()
  84. {
  85. if (!IsWindow(mod.hMainWindow))
  86. return IN_INIT_FAILURE;
  87. mod.UsesOutputPlug|=8;
  88. warand = (int (*)())SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GET_RANDFUNC);
  89. warandf = (float (*)())SendMessage(mod.hMainWindow, WM_WA_IPC, 1, IPC_GET_RANDFUNC);
  90. ServiceBuild(WASABI_API_MEMMGR, memMgrApiServiceGuid);
  91. ServiceBuild(AGAVE_API_CONFIG, AgaveConfigGUID);
  92. // loader so that we can get the localisation service api for use
  93. ServiceBuild(WASABI_API_LNG, languageApiGUID);
  94. ServiceBuild(WASABI_API_APP, applicationApiServiceGuid);
  95. mkvFactory.Register(mod.service, &mkvCreator);
  96. // need to have this initialised before we try to do anything with localisation features
  97. WASABI_API_START_LANG(mod.hDllInstance,InVorbisLangGUID);
  98. static wchar_t szDescription[256];
  99. StringCchPrintfW(szDescription,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_VORBIS_DECODER),VER);
  100. mod.description = (char*)szDescription;
  101. SetFileExtensions();
  102. INI_FILE = (const char *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIFILE);
  103. INI_DIRECTORY = (const wchar_t *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIDIRECTORYW);
  104. os_ver.dwOSVersionInfoSize=sizeof(os_ver);
  105. GetVersionEx(&os_ver);
  106. InitializeCriticalSection(&sync);
  107. do_cfg(0);
  108. return IN_INIT_SUCCESS;
  109. }
  110. void Quit()
  111. {
  112. winampGetExtendedFileInfoW_Cleanup();
  113. DeleteCriticalSection(&sync);
  114. mkvFactory.Deregister(mod.service);
  115. ServiceRelease(WASABI_API_MEMMGR, memMgrApiServiceGuid);
  116. ServiceRelease(AGAVE_API_CONFIG, AgaveConfigGUID);
  117. ServiceRelease(WASABI_API_LNG, languageApiGUID);
  118. ServiceRelease(WASABI_API_APP, applicationApiServiceGuid);
  119. }
  120. int GetLength()
  121. {
  122. return length;
  123. }
  124. int IsOurFile(const in_char *fn)
  125. {
  126. if (PathIsURLW(fn))
  127. {
  128. const wchar_t *foo=wcsrchr(fn,L'.');
  129. return foo ? !_wcsicmp(foo,L".ogg") : 0;
  130. }
  131. else return 0;
  132. }
  133. static UINT kbps_disp;
  134. static void out_close()
  135. {
  136. if (is_out_open)
  137. {
  138. mod.outMod->Close();
  139. mod.SAVSADeInit();
  140. is_out_open=0;
  141. }
  142. }
  143. static bool need_full_setinfo;
  144. static int out_open(const Decoder &dec)
  145. {
  146. int max_l=mod.outMod->Open(dec.sr,dec.nch,dec.bps,-1,-1);
  147. if (max_l<0) return 0;
  148. mod.outMod->SetVolume(-666);
  149. mod.outMod->SetPan(pan);
  150. mod.SAVSAInit(max_l,dec.sr);
  151. mod.VSASetInfo(dec.sr,dec.nch);
  152. is_out_open=1;
  153. need_full_setinfo=1;
  154. return 1;
  155. }
  156. void Decoder::wa2_setinfo(UINT cur)
  157. {
  158. UINT disp=file->get_avg_bitrate();
  159. if (!cfg_abr)
  160. {
  161. disp=cur;
  162. }
  163. if ((disp && disp!=kbps_disp) || need_full_setinfo)
  164. {
  165. kbps_disp=disp;
  166. if (need_full_setinfo)
  167. {
  168. mod.SetInfo(disp,sr/1000,nch,1);
  169. need_full_setinfo=0;
  170. }
  171. else mod.SetInfo(disp,-1,-1,1);
  172. }
  173. }
  174. static bool need_movefile;
  175. static void process_movefile();
  176. void alloc_buffers(Decoder & dec,short ** visbuf,char ** sample_buf,int * s_size)
  177. {
  178. *s_size=576 * (dec.bps>>3) * dec.nch;
  179. if (*sample_buf) *sample_buf=(char*)realloc(*sample_buf,*s_size*2);
  180. else *sample_buf=(char*)malloc(*s_size*2);
  181. if (dec.bps>16)
  182. {
  183. int vs=576*2*dec.nch;
  184. if (*visbuf) *visbuf=(short*)realloc(*visbuf,vs);
  185. else *visbuf=(short*)malloc(vs);
  186. }
  187. else if (*visbuf) {free(*visbuf);*visbuf=0;}
  188. }
  189. static DWORD WINAPI PlayThread(Decoder &dec)
  190. {
  191. int pos_base=0;
  192. int samp_wr=0;
  193. int done=0;
  194. int upd=0;
  195. __int64 brate;
  196. int br_div,br_t;
  197. short* visbuf=0;
  198. char *sample_buf=0;
  199. int retries=0;
  200. int s_size=0;
  201. pos_ms=0;
  202. {
  203. int r;
  204. r=dec.play_init();
  205. if (r)
  206. {
  207. if (!kill) Sleep(50);
  208. if (!kill) Sleep(50);
  209. if (!kill) Sleep(50);
  210. if (!kill) Sleep(50);
  211. if (!kill)
  212. {
  213. if (r==2) PostMessage(mod.hMainWindow,WM_WA_MPEG_EOF,0,0);
  214. else PostMessage(mod.hMainWindow,WM_COMMAND,40047,0);
  215. }
  216. delete &dec;
  217. return 0;
  218. }
  219. theFile->do_prebuf();
  220. }
  221. brate=0;
  222. br_div=0;
  223. upd=0;
  224. alloc_buffers(dec,&visbuf,&sample_buf,&s_size);
  225. //int f_type=theFile->GetType();
  226. bool is_live=theFile->IsLive();
  227. while(!kill)
  228. {
  229. if (!theFile) break;//ugh
  230. if (seek_to!= -1)
  231. {
  232. UINT _st=seek_to;
  233. int r=1;
  234. seek_to=-1;
  235. if (theFile)
  236. {
  237. theFile->use_prebuf=0;
  238. int link=theFile->vf.current_link;
  239. r=dec.Seek((double)_st*0.001);
  240. if (link!=theFile->vf.current_link) PostMessage(mod.hMainWindow,WM_USER,0,243);
  241. }
  242. else r=1;
  243. if (!r)
  244. {
  245. pos_base=pos_ms=_st;
  246. mod.outMod->Flush(pos_ms);
  247. samp_wr=0;
  248. done=0;
  249. theFile->do_prebuf();
  250. }
  251. }
  252. if (need_movefile && paused)//HACK, prevent stupid lockup
  253. {
  254. process_movefile();
  255. if (!theFile) break;//#@!
  256. dec.file=theFile;
  257. dec.Flush();
  258. }
  259. if (done)
  260. {
  261. // mod.outMod->CanWrite();
  262. if (!mod.outMod->IsPlaying())
  263. {
  264. PostMessage(mod.hMainWindow,WM_WA_MPEG_EOF,0,0);
  265. break;
  266. }
  267. Sleep(10);
  268. }
  269. else if (mod.outMod->CanWrite() >= (s_size<<(mod.dsp_isactive()?1:0)))
  270. {
  271. int l=0;
  272. while(1)
  273. {
  274. if (!dec.need_reopen)
  275. {
  276. l+=dec.Read(s_size-l,sample_buf+l);
  277. if (l>=s_size) break;
  278. int link=theFile->vf.current_link;
  279. if (need_movefile)//safe not to flush here
  280. {
  281. process_movefile();
  282. if (!theFile) break;//#@!
  283. dec.file=theFile;
  284. }
  285. if (!dec.DoFrame()) break;
  286. if (kill) break;
  287. if (link!=theFile->vf.current_link)
  288. {
  289. PostMessage(mod.hMainWindow,WM_USER,0,243);
  290. }
  291. br_t=ov_bitrate_instant(&theFile->vf);
  292. if (br_t>0)
  293. {
  294. int i = dec.DataAvailable()/((dec.bps/8)*dec.nch);
  295. br_div+=i;
  296. brate+=(__int64)(br_t*i);
  297. }
  298. if (need_full_setinfo || (!((++upd)%200) && br_div))
  299. {
  300. if (!br_div) {br_div=1;brate=theFile->get_avg_bitrate();}
  301. dec.wa2_setinfo((int)((__int64)brate/(__int64)br_div/(__int64)1000));
  302. brate=0;
  303. br_div=0;
  304. }
  305. }
  306. if (dec.need_reopen)
  307. {//blargh, new PCM format
  308. if (l>0) break;//got samples to play, we'll deal with this later
  309. //l=0;
  310. while(!kill && mod.outMod->IsPlaying()) Sleep(1);
  311. if (kill) break;
  312. out_close();
  313. if (!out_open(dec))//boo
  314. {
  315. PostMessage(mod.hMainWindow,WM_COMMAND,40047,0);
  316. kill=1;
  317. break;
  318. }
  319. alloc_buffers(dec,&visbuf,&sample_buf,&s_size);
  320. dec.need_reopen=0;
  321. }
  322. }
  323. if (kill || !theFile) break;
  324. if (l<=0 && (!is_live || (--retries)<0))
  325. {
  326. mod.outMod->Write(sample_buf,0);
  327. done=1;
  328. }
  329. else if (l<=0)
  330. {
  331. int r;
  332. out_close();
  333. EnterCriticalSection(&sync);
  334. delete theFile;
  335. theFile=0;
  336. LeaveCriticalSection(&sync);
  337. if (sample_buf)
  338. {
  339. free(sample_buf);
  340. sample_buf=0;
  341. }
  342. r=dec.play_init();
  343. if (r)
  344. {
  345. mod.outMod->Write(sample_buf,0);
  346. done=1;
  347. }
  348. else
  349. {
  350. theFile->do_prebuf();
  351. }
  352. }
  353. else
  354. {
  355. if (l<s_size) memset(sample_buf+l,dec.bps==8 ? 0x80 : 0,s_size-l);
  356. char * vis=sample_buf;
  357. UINT vis_bps=dec.bps;
  358. if (dec.bps>16)
  359. {
  360. UINT n;
  361. UINT d=dec.bps>>3;
  362. char * foo=sample_buf+d-2;
  363. for(n=0;n<576*dec.nch;n++)
  364. {
  365. visbuf[n]=*(short*)foo;
  366. foo+=d;
  367. }
  368. vis=(char*)visbuf;
  369. vis_bps=16;
  370. }
  371. mod.SAAddPCMData(vis,dec.nch,vis_bps,pos_ms);
  372. mod.VSAAddPCMData(vis,dec.nch,vis_bps,pos_ms);
  373. if (mod.dsp_isactive())
  374. {
  375. l=(l<<3)/(dec.bps*dec.nch);
  376. l=mod.dsp_dosamples((short*)sample_buf,l,dec.bps,dec.nch,dec.sr);
  377. l*=(dec.nch*dec.bps)>>3;
  378. }
  379. if (kill) break;
  380. mod.outMod->Write((char*)sample_buf,l);
  381. samp_wr+=(8*l)/(dec.bps*dec.nch);
  382. pos_ms=pos_base+MulDiv(1000,samp_wr,dec.sr);
  383. }
  384. }
  385. else
  386. {
  387. theFile->Idle();
  388. }
  389. }
  390. // out_close();
  391. // gay gapless plugins puke, need to call this from stop
  392. // ok, hetero (out_wave v2.x / out_ds v1.4+) gapless plugins wouldn't puke anymore
  393. if (theFile)
  394. {
  395. VorbisFile * t=theFile;
  396. EnterCriticalSection(&sync);
  397. theFile=0;
  398. LeaveCriticalSection(&sync);
  399. delete t;
  400. }
  401. if (sample_buf)
  402. {
  403. free(sample_buf);
  404. sample_buf=0;
  405. }
  406. if (need_movefile) process_movefile();
  407. /* if (!kill)
  408. {
  409. CloseHandle(hThread);
  410. hThread=0;
  411. }*/
  412. if (visbuf) free(visbuf);
  413. delete &dec;
  414. return 0;
  415. }
  416. static StringW move_src,move_dst;
  417. static bool mf_ret;
  418. static void do_movefile()
  419. {
  420. mf_ret=1;
  421. winampGetExtendedFileInfoW_Cleanup();
  422. if (!DeleteFileW(move_dst)) mf_ret=0;
  423. else
  424. {
  425. if (!MoveFileW(move_src,move_dst))
  426. {
  427. if (!CopyFileW(move_src,move_dst,0)) mf_ret=0;
  428. DeleteFileW(move_src);
  429. }
  430. }
  431. }
  432. static void process_movefile()
  433. {
  434. if (theFile)
  435. {
  436. StringW f_path;
  437. f_path.AddString(theFile->url);
  438. double pos=theFile->GetPos();
  439. EnterCriticalSection(&sync);
  440. delete theFile;
  441. theFile=0;
  442. do_movefile();
  443. theFile=VorbisFile::Create(f_path,0);
  444. LeaveCriticalSection(&sync);
  445. if (theFile)
  446. {
  447. theFile->Seek(pos);
  448. }
  449. }
  450. else do_movefile();
  451. need_movefile=0;
  452. }
  453. bool sync_movefile(const wchar_t * src,const wchar_t * dst)//called from info_.cpp
  454. {
  455. move_src=src;
  456. move_dst=dst;
  457. need_movefile=1;
  458. if (!theFile) process_movefile();
  459. else
  460. {
  461. while(need_movefile && hThread) Sleep(1);
  462. if (need_movefile) process_movefile();//shouldnt really happen
  463. move_src=L"";
  464. move_dst=L"";
  465. PostMessage(mod.hMainWindow,WM_USER,0,243);
  466. }
  467. return mf_ret;
  468. }
  469. int Decoder::play_init()//still messy
  470. {
  471. if (play_inited) return 0;
  472. kbps_disp=0;
  473. VorbisFile * t=VorbisFile::Create(cur_file,0);
  474. if (!t)
  475. {
  476. #ifdef _DEBUG
  477. OutputDebugString(L"can't open file\n");
  478. #endif
  479. // if (scream) MessageBox(mod.hMainWindow,"error opening file",0,MB_ICONERROR);
  480. return 2;
  481. }
  482. Init(t);
  483. if (!out_open(*this))
  484. {
  485. #ifdef _DEBUG
  486. OutputDebugString(L"can't open output\n");
  487. #endif
  488. delete t;
  489. return 1;
  490. }
  491. EnterCriticalSection(&sync);
  492. theFile=t;
  493. LeaveCriticalSection(&sync);
  494. wa2_setinfo(theFile->get_avg_bitrate());
  495. {
  496. double v=theFile->Length();
  497. if (v==OV_EINVAL || v<=0) length=-1;
  498. else length=(int)(v*1000.0);
  499. }
  500. play_inited=1;
  501. return 0;
  502. }
  503. int Play(const in_char *fn)
  504. {
  505. seek_to=-1;
  506. kill=0;
  507. length=0;
  508. paused=0;
  509. show_stat(0);
  510. EnterCriticalSection(&sync);
  511. cur_file=fn;
  512. LeaveCriticalSection(&sync);
  513. Decoder * dec=new Decoder;
  514. if (!PathIsURLW(fn))
  515. {
  516. mod.is_seekable=1;
  517. #if 1
  518. int rv=dec->play_init();
  519. if (rv)
  520. {
  521. delete dec;
  522. if (rv==2) return -1;
  523. return 1;
  524. }
  525. #endif
  526. }
  527. else mod.is_seekable=cfg_httpseek2;
  528. {
  529. DWORD id;
  530. hThread=CreateThread(0,0,(LPTHREAD_START_ROUTINE)PlayThread,dec,CREATE_SUSPENDED,&id);
  531. }
  532. if (hThread)
  533. {
  534. SetThreadPriority(hThread, (int)AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
  535. ResumeThread(hThread);
  536. return 0;
  537. }
  538. else
  539. {
  540. out_close();
  541. delete dec;
  542. return 1;
  543. }
  544. }
  545. void Pause()
  546. {
  547. if (!paused)
  548. {
  549. mod.outMod->Pause(1);
  550. paused=1;
  551. }
  552. }
  553. void UnPause()
  554. {
  555. if (paused)
  556. {
  557. mod.outMod->Pause(0);
  558. paused=0;
  559. }
  560. }
  561. int IsPaused()
  562. {
  563. return paused;
  564. }
  565. void Stop()
  566. {
  567. if (hThread)
  568. {
  569. kill=1;
  570. EnterCriticalSection(&sync);
  571. if (theFile) theFile->stopping=1;
  572. LeaveCriticalSection(&sync);
  573. if (WaitForSingleObject(hThread,10000)!=WAIT_OBJECT_0)
  574. {
  575. TerminateThread(hThread,0);
  576. //MessageBox(mod.hMainWindow,"error asking thread to die",0,MB_ICONERROR);
  577. }
  578. CloseHandle(hThread);
  579. hThread=0;
  580. out_close();
  581. }
  582. show_stat(0);
  583. winampGetExtendedFileInfoW_Cleanup();
  584. }
  585. void EQSet(int on, char data[10], int preamp)
  586. {
  587. }
  588. int GetOutputTime()
  589. {
  590. return pos_ms+(mod.outMod->GetOutputTime()-mod.outMod->GetWrittenTime());
  591. }
  592. void SetOutputTime(int t)
  593. {
  594. seek_to=t;
  595. EnterCriticalSection(&sync);
  596. if (theFile) theFile->abort_prebuf=1;
  597. LeaveCriticalSection(&sync);
  598. }
  599. void SetVolume(int v)
  600. {
  601. mod.outMod->SetVolume(volume=v);
  602. }
  603. void SetPan(int p)
  604. {
  605. mod.outMod->SetPan(pan=p);
  606. }
  607. //int InfoBox(char *file, HWND parent); //old
  608. int RunInfoDlg(const in_char * url,HWND parent);
  609. In_Module mod=
  610. {
  611. IN_VER_RET,
  612. "nullsoft(in_vorbis.dll)",
  613. 0,0,
  614. 0,
  615. 1,
  616. 1,
  617. Config,
  618. About,
  619. Init,
  620. Quit,
  621. GetFileInfo,
  622. RunInfoDlg,
  623. IsOurFile,
  624. Play,
  625. Pause,
  626. UnPause,
  627. IsPaused,
  628. Stop,
  629. GetLength,
  630. GetOutputTime,
  631. SetOutputTime,
  632. SetVolume,
  633. SetPan,
  634. 0,0,0,0,0,0,0,0,0,0,0,
  635. EQSet,
  636. 0,
  637. 0,
  638. };
  639. extern "C" {
  640. __declspec( dllexport ) In_Module * winampGetInModule2()
  641. {
  642. return &mod;
  643. }
  644. }
  645. void VorbisFile::Status(const wchar_t * zzz)
  646. {
  647. if (primary)
  648. show_stat(zzz);
  649. }
  650. bool VorbisFile::Aborting()
  651. {
  652. return stopping || (primary && kill);
  653. }
  654. Info::Info(const wchar_t *filename) : filename(filename), vc(0)
  655. {
  656. VorbisFile * vf = VorbisFile::Create(filename,true);
  657. if(!vf)
  658. return;
  659. numstreams = vf->vf.links;
  660. if(numstreams)
  661. {
  662. // now copy the comment section to our own memory...
  663. stream = vf->vf.current_link; // this is the stream we're editing...
  664. vc = (vorbis_comment*)calloc(sizeof(vorbis_comment),numstreams);
  665. for(int i=0; i<numstreams; i++)
  666. { // one comment section per stream
  667. vorbis_comment *c = ov_comment(&vf->vf,i);
  668. vc[i].comments = c->comments;
  669. vc[i].user_comments = (char **)malloc(sizeof(char*)*c->comments);
  670. vc[i].comment_lengths = (int *)malloc(sizeof(int)*c->comments);
  671. for(int j=0;j<vc[i].comments;j++)
  672. { // copy the comments over
  673. vc[i].user_comments[j] = _strdup(c->user_comments[j]);
  674. vc[i].comment_lengths[j] = c->comment_lengths[j];
  675. }
  676. vc[i].vendor=_strdup(c->vendor);
  677. }
  678. }
  679. delete vf;
  680. }
  681. Info::~Info()
  682. {
  683. if(vc) {
  684. for(int i=0; i < numstreams; i++)
  685. vorbis_comment_clear(&vc[i]);
  686. free(vc);
  687. }
  688. }
  689. bool Info::Save()
  690. {
  691. return !!modify_file(filename,vc,numstreams);
  692. }
  693. int Info::GetNumMetadataItems()
  694. {
  695. return vc[stream].comments;
  696. }
  697. void Info::EnumMetadata(int n, wchar_t *key, int keylen, wchar_t *val, int vallen)
  698. {
  699. if(keylen) key[0]=0;
  700. if(vallen) val[0]=0;
  701. if(!vc) return;
  702. if(!vc[stream].user_comments[n]) return;
  703. AutoWide comment(vc[stream].user_comments[n],CP_UTF8);
  704. const wchar_t* eq = wcschr((const wchar_t*)comment,L'=');
  705. if(eq)
  706. {
  707. if(keylen) lstrcpynW(key,comment,(int)min(eq - comment + 1,keylen));
  708. if(vallen) lstrcpynW(val,eq+1,vallen);
  709. }
  710. else
  711. {
  712. if(keylen) lstrcpynW(key,L"COMMENT",keylen);
  713. if(vallen) lstrcpynW(val,comment,vallen);
  714. }
  715. }
  716. void Info::RemoveMetadata(wchar_t * key)
  717. {
  718. wchar_t k[256] = {0};
  719. for(int i=0; i<GetNumMetadataItems(); i++)
  720. {
  721. EnumMetadata(i,k,256,0,0);
  722. if(_wcsicmp(k,key)==0)
  723. RemoveMetadata(i);
  724. }
  725. }
  726. void Info::RemoveMetadata(int n)
  727. {
  728. if(!vc) return;
  729. free(vc[stream].user_comments[n]);
  730. for(int i=n+1; i<vc[stream].comments; i++)
  731. {
  732. vc[stream].user_comments[i-1] = vc[stream].user_comments[i];
  733. if(vc[stream].comment_lengths)
  734. vc[stream].comment_lengths[i-1] = vc[stream].comment_lengths[i];
  735. }
  736. vc[stream].comments--;
  737. vc[stream].user_comments = (char**)realloc(vc[stream].user_comments,sizeof(vc[stream].user_comments[0]) * vc[stream].comments);
  738. if(vc[stream].comment_lengths)
  739. vc[stream].comment_lengths = (int*)realloc(vc[stream].comment_lengths,sizeof(vc[stream].comment_lengths[0]) * vc[stream].comments);
  740. }
  741. void Info::SetMetadata(wchar_t *key, wchar_t *val)
  742. {
  743. bool set=false;
  744. wchar_t k[256] = {0};
  745. for(int i=0; i<GetNumMetadataItems(); i++)
  746. {
  747. EnumMetadata(i,k,256,0,0);
  748. if(_wcsicmp(k,key)==0)
  749. {
  750. SetMetadata(i,key,val);
  751. set=true;
  752. }
  753. }
  754. if(!set)
  755. {
  756. int n = vc[stream].comments++;
  757. vc[stream].user_comments = (char**)realloc(vc[stream].user_comments,sizeof(vc[stream].user_comments[0]) * vc[stream].comments);
  758. if(vc[stream].comment_lengths)
  759. vc[stream].comment_lengths = (int*)realloc(vc[stream].comment_lengths,sizeof(vc[stream].comment_lengths[0]) * vc[stream].comments);
  760. vc[stream].user_comments[n] = NULL;
  761. SetMetadata(n,key,val);
  762. }
  763. }
  764. void Info::SetMetadata(int n, wchar_t *key, wchar_t *val)
  765. {
  766. AutoChar k(key,CP_UTF8);
  767. AutoChar v(val,CP_UTF8);
  768. int l = (int)(strlen(k)+strlen(v)+2);
  769. char * c = (char*)malloc(l);
  770. StringCchPrintfA(c,l,"%s=%s",(char*)k,(char*)v);
  771. if(vc[stream].user_comments[n])
  772. free(vc[stream].user_comments[n]);
  773. vc[stream].user_comments[n] = c;
  774. if(vc[stream].comment_lengths)
  775. vc[stream].comment_lengths[n] = l-1;
  776. }
  777. void Info::SetTag(int n,wchar_t *key) // changes the key name
  778. {
  779. wchar_t val[2048] = {0};
  780. EnumMetadata(n,NULL,0,val,2048);
  781. SetMetadata(n,key,val);
  782. }
  783. Info *setMetadata = 0;
  784. extern "C"
  785. {
  786. static wchar_t m_lastfn[2048];
  787. #define START_TAG_ALIAS(name, alias) if (KeywordMatch(data, name)) lookup=alias
  788. #define TAG_ALIAS(name, alias) else if (KeywordMatch(data, name)) lookup=alias
  789. __declspec( dllexport ) int winampSetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *val)
  790. {
  791. if (!setMetadata || setMetadata && wcscmp(fn,m_lastfn))
  792. {
  793. if (setMetadata)
  794. {
  795. delete setMetadata;
  796. setMetadata = 0;
  797. }
  798. setMetadata = new Info(fn);
  799. if(setMetadata->Error())
  800. {
  801. delete setMetadata;
  802. m_lastfn[0] = 0;
  803. return 0;
  804. }
  805. lstrcpynW(m_lastfn,fn, 2048);
  806. }
  807. wchar_t *lookup=0;
  808. START_TAG_ALIAS("artist", L"ARTIST");
  809. TAG_ALIAS("title", L"TITLE");
  810. TAG_ALIAS("album", L"ALBUM");
  811. TAG_ALIAS("genre", L"GENRE");
  812. TAG_ALIAS("comment", L"COMMENT");
  813. TAG_ALIAS("year", L"DATE");
  814. TAG_ALIAS("track", L"TRACKNUMBER");
  815. TAG_ALIAS("albumartist", L"ALBUM ARTIST");
  816. TAG_ALIAS("composer", L"COMPOSER");
  817. TAG_ALIAS("disc", L"DISCNUMBER");
  818. TAG_ALIAS("publisher", L"PUBLISHER");
  819. TAG_ALIAS("conductor", L"CONDUCTOR");
  820. TAG_ALIAS("tool", L"ENCODED-BY");
  821. TAG_ALIAS("replaygain_track_gain", L"REPLAYGAIN_TRACK_GAIN");
  822. TAG_ALIAS("replaygain_track_peak", L"REPLAYGAIN_TRACK_PEAK");
  823. TAG_ALIAS("replaygain_album_gain", L"REPLAYGAIN_ALBUM_GAIN");
  824. TAG_ALIAS("replaygain_album_peak", L"REPLAYGAIN_ALBUM_PEAK");
  825. TAG_ALIAS("GracenoteFileID", L"GRACENOTEFILEID");
  826. TAG_ALIAS("GracenoteExtData", L"GRACENOTEEXTDATA");
  827. TAG_ALIAS("bpm", L"BPM");
  828. TAG_ALIAS("remixing", L"REMIXING");
  829. TAG_ALIAS("subtitle", L"VERSION");
  830. TAG_ALIAS("isrc", L"ISRC");
  831. TAG_ALIAS("category", L"CATEGORY");
  832. TAG_ALIAS("rating", L"RATING");
  833. TAG_ALIAS("producer", L"PRODUCER");
  834. if (!lookup)
  835. return 0;
  836. #if 0
  837. if (val && *val)
  838. {
  839. if(KeywordMatch("rating",data))
  840. {
  841. wchar_t temp[128] = {0};
  842. StringCchPrintfW(temp, 128, L"%u", _wtoi(val)*20);
  843. val=temp;
  844. }
  845. }
  846. AutoChar utf8(val, CP_UTF8);
  847. for(int i=0;i<m_vc->comments;i++)
  848. {
  849. char *c=m_vc[m_curstream].user_comments[i];
  850. if(!c) continue;
  851. char *p=strchr(c,'=');
  852. if (p && *p)
  853. {
  854. if(strlen(data) == (p-c) && !_strnicmp(c,data,p-c))
  855. {
  856. //found!
  857. if (val && val[0])
  858. {
  859. int added_buf_len = strlen(utf8)+strlen(lookup)+2;
  860. m_vc[m_curstream].user_comments[i]=(char *)realloc(m_vc[m_curstream].user_comments[i],added_buf_len);
  861. StringCchPrintfA(m_vc[m_curstream].user_comments[i],added_buf_len,"%s=%s",lookup,(char *)utf8);
  862. m_vc[m_curstream].comment_lengths[i]=strlen(m_vc[m_curstream].user_comments[i]);
  863. }
  864. else
  865. {
  866. free(m_vc[m_curstream].user_comments[i]);
  867. m_vc[m_curstream].user_comments[i]=0;
  868. m_vc[m_curstream].comment_lengths[i]=0;
  869. }
  870. return 1;
  871. }
  872. }
  873. }
  874. //not found, so create new field
  875. if (val && val[0])
  876. {
  877. int k=m_vc[m_curstream].comments++;
  878. m_vc[m_curstream].user_comments=(char **)realloc(m_vc[m_curstream].user_comments,sizeof(char*)*m_vc[m_curstream].comments);
  879. m_vc[m_curstream].comment_lengths=(int *)realloc(m_vc[m_curstream].comment_lengths,sizeof(int)*m_vc[m_curstream].comments);
  880. int added_buf_len = strlen(utf8)+strlen(lookup)+2;
  881. m_vc[m_curstream].user_comments[k]=(char *)malloc(added_buf_len);
  882. StringCchPrintfA(m_vc[m_curstream].user_comments[k],added_buf_len,"%s=%s",lookup,(char *)utf8);
  883. m_vc[m_curstream].comment_lengths[k]=strlen(m_vc[m_curstream].user_comments[k]);
  884. }
  885. #endif
  886. if (val && *val)
  887. {
  888. if(KeywordMatch("rating",data))
  889. {
  890. wchar_t temp[128] = {0};
  891. StringCchPrintfW(temp, 128, L"%u", _wtoi(val)*20);
  892. setMetadata->SetMetadata(lookup, temp);
  893. }
  894. else
  895. {
  896. setMetadata->SetMetadata(lookup, val);
  897. }
  898. }
  899. else
  900. {
  901. setMetadata->RemoveMetadata(lookup);
  902. if(KeywordMatch("comment",data))
  903. {
  904. // need to remove this one also, or else it's gonna look like delete doesn't work
  905. // if the file was tagged using this alternate field
  906. setMetadata->RemoveMetadata(L"DESCRIPTION");
  907. }
  908. else if(KeywordMatch("year",data))
  909. {
  910. // need to remove this one also, or else it's gonna look like delete doesn't work
  911. // if the file was tagged using this alternate field
  912. setMetadata->RemoveMetadata(L"YEAR");
  913. }
  914. else if(KeywordMatch("track",data))
  915. {
  916. // need to remove this one also, or else it's gonna look like delete doesn't work
  917. // if the file was tagged using this alternate field
  918. setMetadata->RemoveMetadata(L"TRACK");
  919. }
  920. else if(KeywordMatch("albumartist",data))
  921. {
  922. // need to remove these two, also, or else it's gonna look like delete doesn't work
  923. // if the file was tagged using these alternate fields
  924. setMetadata->RemoveMetadata(L"ALBUMARTIST");
  925. setMetadata->RemoveMetadata(L"ENSEMBLE");
  926. }
  927. else if(KeywordMatch("publisher",data))
  928. {
  929. // need to remove this one also, or else it's gonna look like delete doesn't work
  930. // if the file was tagged using this alternate field
  931. setMetadata->RemoveMetadata(L"ORGANIZATION");
  932. }
  933. else if(KeywordMatch("category",data))
  934. {
  935. // need to remove these two also, or else it's gonna look like delete doesn't work
  936. // if the file was tagged using these alternate fields
  937. setMetadata->RemoveMetadata(L"CONTENTGROUP");
  938. setMetadata->RemoveMetadata(L"GROUPING");
  939. }
  940. }
  941. return 1;
  942. }
  943. __declspec( dllexport ) int winampWriteExtendedFileInfo()
  944. {
  945. if(!setMetadata) return 0;
  946. bool ret = setMetadata->Save();
  947. delete setMetadata;
  948. setMetadata = 0;
  949. // update last modified so we're not re-queried on our own updates
  950. UpdateFileTimeChanged(m_lastfn);
  951. return ret;
  952. }
  953. }