main.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. /*
  2. ** enc_vorbis: main.cpp - Ogg Vorbis encoder plug-in
  3. **
  4. ** Copyright (C) 2001-2012 Nullsoft, Inc.
  5. **
  6. ** This software is provided 'as-is', without any express or implied warranty.
  7. ** In no event will the authors be held liable for any damages arising from the use of this software.
  8. **
  9. ** Permission is granted to anyone to use this software for any purpose, including commercial
  10. ** applications, and to alter it and redistribute it freely, subject to the following restrictions:
  11. ** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the
  12. ** original software. If you use this software in a product, an acknowledgment in the product
  13. ** documentation would be appreciated but is not required.
  14. ** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as
  15. ** being the original software.
  16. ** 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #define ENC_VERSION "v1.58"
  19. #include <windows.h>
  20. #include <commctrl.h>
  21. #include <uxtheme.h>
  22. #include <strsafe.h>
  23. #include "resource.h"
  24. #include "../nsv/enc_if.h"
  25. #include <vorbis/vorbisenc.h>
  26. // wasabi based services for localisation support
  27. #include <api/service/waServiceFactory.h>
  28. #include "../Agave/Language/api_language.h"
  29. #include <api/application/api_application.h>
  30. #include "../winamp/wa_ipc.h"
  31. HWND winampwnd = 0;
  32. int isthemethere = 0;
  33. api_service *WASABI_API_SVC = 0;
  34. api_application *WASABI_API_APP = 0;
  35. api_language *WASABI_API_LNG = 0;
  36. HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
  37. BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
  38. {
  39. return TRUE;
  40. }
  41. typedef struct
  42. {
  43. bool cfg_abr_use_max,cfg_abr_use_min;
  44. UINT cfg_mode;
  45. float cfg_vbrquality;
  46. UINT cfg_abr_nominal;
  47. UINT cfg_abr_max;
  48. UINT cfg_abr_min;
  49. } configtype;
  50. typedef struct
  51. {
  52. configtype cfg;
  53. char *configfile;
  54. }
  55. configwndrec;
  56. void readconfig(char *configfile, configtype *cfg)
  57. {
  58. cfg->cfg_abr_use_max=0;
  59. cfg->cfg_abr_use_min=0;
  60. cfg->cfg_mode=0; //VBR
  61. cfg->cfg_vbrquality=0.4f;
  62. cfg->cfg_abr_nominal=160;
  63. cfg->cfg_abr_max=352;
  64. cfg->cfg_abr_min=32;
  65. if (configfile) GetPrivateProfileStructA("audio_ogg","conf",cfg,sizeof(configtype),configfile);
  66. cfg->cfg_mode=0; // VBR, fuckers.
  67. }
  68. void writeconfig(char *configfile, configtype *cfg)
  69. {
  70. if (configfile) WritePrivateProfileStructA("audio_ogg","conf",cfg,sizeof(configtype),configfile);
  71. }
  72. static HINSTANCE GetMyInstance()
  73. {
  74. MEMORY_BASIC_INFORMATION mbi = {0};
  75. if(VirtualQuery(GetMyInstance, &mbi, sizeof(mbi)))
  76. return (HINSTANCE)mbi.AllocationBase;
  77. return NULL;
  78. }
  79. void GetLocalisationApiService(void)
  80. {
  81. if(!WASABI_API_LNG)
  82. {
  83. // loader so that we can get the localisation service api for use
  84. if(!WASABI_API_SVC)
  85. {
  86. WASABI_API_SVC = (api_service*)SendMessage(winampwnd, WM_WA_IPC, 0, IPC_GET_API_SERVICE);
  87. if (WASABI_API_SVC == (api_service*)1)
  88. {
  89. WASABI_API_SVC = NULL;
  90. return;
  91. }
  92. }
  93. if(!WASABI_API_APP)
  94. {
  95. waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(applicationApiServiceGuid);
  96. if (sf) WASABI_API_APP = reinterpret_cast<api_application*>(sf->getInterface());
  97. }
  98. if(!WASABI_API_LNG)
  99. {
  100. waServiceFactory *sf;
  101. sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID);
  102. if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
  103. }
  104. // need to have this initialised before we try to do anything with localisation features
  105. WASABI_API_START_LANG(GetMyInstance(),EncVorbisLangGUID);
  106. }
  107. }
  108. class AudioCoderOgg : public AudioCoder
  109. {
  110. public:
  111. AudioCoderOgg(int nch, int srate, int bps, configtype *cfg);
  112. int Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail);
  113. ~AudioCoderOgg()
  114. {
  115. ogg_stream_clear(&os);
  116. vorbis_block_clear(&vb);
  117. vorbis_dsp_clear(&vd);
  118. vorbis_comment_clear(&vc);
  119. vorbis_info_clear(&vi);
  120. }
  121. int GetLastError() { return m_err; };
  122. private:
  123. int m_err;
  124. int m_bps, m_nch;
  125. ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */
  126. ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */
  127. ogg_packet op; /* one raw packet of data for decode */
  128. vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */
  129. vorbis_comment vc; /* struct that stores all the user comments */
  130. vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
  131. vorbis_block vb; /* local working space for packet->PCM decode */
  132. int m_inbuf;
  133. };
  134. int AudioCoderOgg::Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail)
  135. {
  136. if (m_err) return -1;
  137. *in_used = 0; //only necessary for flawed impl that dont reset this to zero each time (BAD rOn)
  138. int wrote=0;
  139. char *dest = (char*)out;
  140. if (!in_avail && !framepos)
  141. {
  142. vorbis_analysis_wrote(&vd,0);
  143. }
  144. for (;;)
  145. {
  146. /* vorbis does some data preanalysis, then divvies up blocks for
  147. more involved (potentially parallel) processing. Get a single
  148. block for encoding now */
  149. while (m_inbuf || vorbis_analysis_blockout(&vd,&vb)==1)
  150. {
  151. /* analysis */
  152. if (!m_inbuf)
  153. {
  154. vorbis_analysis(&vb,&op);
  155. vorbis_bitrate_addblock(&vb);
  156. }
  157. while (m_inbuf || vorbis_bitrate_flushpacket(&vd, &op))
  158. {
  159. /* weld the packet into the bitstream */
  160. if (!m_inbuf) ogg_stream_packetin(&os,&op);
  161. /* write out pages (if any) */
  162. while (m_inbuf || ogg_stream_pageout(&os,&og))
  163. {
  164. int l=og.header_len+og.body_len;
  165. if(out_avail<l)
  166. {
  167. m_inbuf=1;
  168. return wrote;
  169. }
  170. memcpy(dest,og.header,og.header_len);
  171. memcpy(dest+og.header_len,og.body,og.body_len);
  172. dest+=l;
  173. wrote+=l;
  174. out_avail-=l;
  175. m_inbuf=0;
  176. if (ogg_page_eos(&og)) break;
  177. }
  178. }
  179. }
  180. // if we used all our samples, or had output, flush it
  181. if (*in_used >= in_avail || wrote) return wrote;
  182. // bring in more pcm samples
  183. if (in_avail > *in_used)
  184. {
  185. UINT i;
  186. int c;
  187. int bytes=in_avail-*in_used;
  188. void *buf=(char*)in + *in_used;
  189. if (bytes > 1024) bytes=1024;
  190. *in_used+=bytes;
  191. UINT nsam=bytes/((m_bps>>3)*m_nch);
  192. float **buffer=vorbis_analysis_buffer(&vd,nsam);
  193. switch(m_bps)
  194. {
  195. case 8:
  196. {
  197. BYTE* rbuf=(BYTE*)buf;
  198. for(i=0;i<nsam;i++)
  199. {
  200. for(c=0;c<m_nch;c++)
  201. {
  202. buffer[c][i]=((float)(UINT)*rbuf)/128.f-1.f;
  203. rbuf++;
  204. }
  205. }
  206. }
  207. break;
  208. case 16:
  209. {
  210. short* rbuf=(short*)buf;
  211. for(i=0;i<nsam;i++)
  212. {
  213. for(c=0;c<m_nch;c++)
  214. {
  215. buffer[c][i]=(float)*rbuf/32768.f;
  216. rbuf++;
  217. }
  218. }
  219. }
  220. break;
  221. case 24:
  222. {
  223. char* rbuf=(char*)buf;
  224. for(i=0;i<nsam;i++)
  225. {
  226. for(c=0;c<m_nch;c++)
  227. {
  228. BYTE* b=(BYTE*)rbuf;
  229. long val = b[0] | (b[1]<<8) | (b[2]<<16);
  230. if (val&0x800000) val|=0xFF000000;
  231. buffer[c][i]=(float)((double)val/(double)0x800000);
  232. rbuf+=3;
  233. }
  234. }
  235. }
  236. break;
  237. case 32:
  238. {
  239. long* rbuf=(long*)buf;
  240. for(i=0;i<nsam;i++)
  241. {
  242. for(c=0;c<m_nch;c++)
  243. {
  244. buffer[c][i]=(float)((double)rbuf[i*m_nch+c]/(double)0x80000000);
  245. }
  246. }
  247. }
  248. break;
  249. }
  250. /* tell the library how much we actually submitted */
  251. vorbis_analysis_wrote(&vd,nsam);
  252. }
  253. }
  254. }
  255. AudioCoderOgg::AudioCoderOgg(int nch, int srate, int bps, configtype *cfg)
  256. {
  257. m_err=0;
  258. m_bps=bps;
  259. m_nch=nch;
  260. m_inbuf=0;
  261. int poo;
  262. vorbis_info_init(&vi);
  263. if (cfg->cfg_mode==0) poo=vorbis_encode_init_vbr(&vi,nch,srate,cfg->cfg_vbrquality);
  264. else
  265. {
  266. UINT nominal,min,max;
  267. if (cfg->cfg_mode==1)
  268. {//abr
  269. nominal=cfg->cfg_abr_nominal * 1000;
  270. min=cfg->cfg_abr_use_min ? cfg->cfg_abr_min * 1000: -1;
  271. max=cfg->cfg_abr_use_max ? cfg->cfg_abr_max * 1000: -1;
  272. }
  273. else//cbr
  274. {
  275. nominal=min=max=cfg->cfg_abr_nominal*1000;
  276. }
  277. poo=vorbis_encode_init(&vi,nch,srate,max,nominal,min);
  278. }
  279. if (poo)
  280. {
  281. vorbis_info_clear(&vi);
  282. m_err++;
  283. return;
  284. }
  285. vorbis_comment_init(&vc);
  286. vorbis_comment_add(&vc, "ENCODEDBY=Winamp");
  287. /* set up the analysis state and auxiliary encoding storage */
  288. vorbis_analysis_init(&vd,&vi);
  289. vorbis_block_init(&vd,&vb);
  290. /* set up our packet->stream encoder */
  291. /* pick a random serial number; that way we can more likely build
  292. chained streams just by concatenation */
  293. ogg_stream_init(&os,GetTickCount()^(GetTickCount()<<16));//fixme : rand
  294. /* Vorbis streams begin with three headers; the initial header (with
  295. most of the codec setup parameters) which is mandated by the Ogg
  296. bitstream spec. The second header holds any comment fields. The
  297. third header holds the bitstream codebook. We merely need to
  298. make the headers, then pass them to libvorbis one at a time;
  299. libvorbis handles the additional Ogg bitstream constraints */
  300. {
  301. ogg_packet header;
  302. ogg_packet header_comm;
  303. ogg_packet header_code;
  304. vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
  305. ogg_stream_packetin(&os,&header); /* automatically placed in its own page */
  306. ogg_stream_packetin(&os,&header_comm);
  307. ogg_stream_packetin(&os,&header_code);
  308. }
  309. }
  310. extern "C"
  311. {
  312. unsigned int __declspec(dllexport) GetAudioTypes3(int idx, char *desc)
  313. {
  314. if (idx==0)
  315. {
  316. GetLocalisationApiService();
  317. StringCchPrintfA(desc, 1024, "%s %s (aoTuV b6.03)", WASABI_API_LNGSTRING(IDS_ENC_VORBIS_DESC), ENC_VERSION);
  318. return mmioFOURCC('O','G','G',' ');
  319. }
  320. return 0;
  321. }
  322. AudioCoder __declspec(dllexport) *CreateAudio3(int nch, int srate, int bps, unsigned int srct, unsigned int *outt, char *configfile)
  323. {
  324. if (srct == mmioFOURCC('P','C','M',' ') && *outt == mmioFOURCC('O','G','G',' '))
  325. {
  326. configtype cfg;
  327. readconfig(configfile,&cfg);
  328. AudioCoderOgg *t = new AudioCoderOgg(nch,srate,bps,&cfg);
  329. if (t->GetLastError())
  330. {
  331. delete t;
  332. return NULL;
  333. }
  334. return t;
  335. }
  336. return NULL;
  337. }
  338. void __declspec(dllexport) FinishAudio3(const char *filename, AudioCoder *coder)
  339. {
  340. }
  341. #define doshow(x,y) ShowWindow(x,(y)?SW_SHOWNA:SW_HIDE)
  342. static HCURSOR link_hand_cursor;
  343. LRESULT link_handlecursor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  344. {
  345. LRESULT ret = CallWindowProcW((WNDPROC)GetPropW(hwndDlg, L"link_proc"), hwndDlg, uMsg, wParam, lParam);
  346. // override the normal cursor behaviour so we have a hand to show it is a link
  347. if(uMsg == WM_SETCURSOR)
  348. {
  349. if((HWND)wParam == hwndDlg)
  350. {
  351. if(!link_hand_cursor)
  352. {
  353. link_hand_cursor = LoadCursor(NULL, IDC_HAND);
  354. }
  355. SetCursor(link_hand_cursor);
  356. return TRUE;
  357. }
  358. }
  359. return ret;
  360. }
  361. void link_handledraw(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  362. {
  363. if (uMsg == WM_DRAWITEM)
  364. {
  365. DRAWITEMSTRUCT *di = (DRAWITEMSTRUCT *)lParam;
  366. if (di->CtlType == ODT_BUTTON)
  367. {
  368. wchar_t wt[123] = {0};
  369. int y;
  370. RECT r;
  371. HPEN hPen, hOldPen;
  372. GetDlgItemTextW(hwndDlg, (int)wParam, wt, sizeof(wt)/sizeof(wt[0]));
  373. // due to the fun of theming and owner drawing we have to get the background colour
  374. if(isthemethere){
  375. HTHEME hTheme = OpenThemeData(hwndDlg, L"Tab");
  376. if (hTheme) {
  377. DrawThemeParentBackground(di->hwndItem, di->hDC, &di->rcItem);
  378. CloseThemeData(hTheme);
  379. }
  380. }
  381. // draw text
  382. SetTextColor(di->hDC, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220));
  383. r = di->rcItem;
  384. r.left += 2;
  385. DrawTextW(di->hDC, wt, -1, &r, DT_VCENTER | DT_SINGLELINE);
  386. memset(&r, 0, sizeof(r));
  387. DrawTextW(di->hDC, wt, -1, &r, DT_SINGLELINE | DT_CALCRECT);
  388. // draw underline
  389. y = di->rcItem.bottom - ((di->rcItem.bottom - di->rcItem.top) - (r.bottom - r.top)) / 2 - 1;
  390. hPen = CreatePen(PS_SOLID, 0, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220));
  391. hOldPen = (HPEN) SelectObject(di->hDC, hPen);
  392. MoveToEx(di->hDC, di->rcItem.left + 2, y, NULL);
  393. LineTo(di->hDC, di->rcItem.right + 2 - ((di->rcItem.right - di->rcItem.left) - (r.right - r.left)), y);
  394. SelectObject(di->hDC, hOldPen);
  395. DeleteObject(hPen);
  396. }
  397. }
  398. }
  399. void link_startsubclass(HWND hwndDlg, UINT id)
  400. {
  401. HWND ctrl = GetDlgItem(hwndDlg, id);
  402. if(!GetPropW(ctrl, L"link_proc"))
  403. {
  404. SetPropW(ctrl, L"link_proc",
  405. (HANDLE)SetWindowLongPtrW(ctrl, GWLP_WNDPROC, (LONG_PTR)link_handlecursor));
  406. }
  407. }
  408. BOOL CALLBACK DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
  409. {
  410. if (uMsg == WM_USER+667)
  411. {
  412. int p=(int)SendDlgItemMessage(hwndDlg,IDC_VBRQUALITY,TBM_GETPOS,0,0)-10;
  413. char tmp[512] = {0};
  414. double dispVal = ((double)p)/10;
  415. vorbis_info vi = {0};
  416. int br = 128;
  417. vorbis_info_init(&vi);
  418. if(vorbis_encode_init_vbr(&vi, 2, 44100, (float) (p / 100.0)))
  419. br=128; // Mode setup failed: go with a default.
  420. else
  421. {
  422. br = vi.bitrate_nominal / 1000;
  423. vorbis_info_clear(&vi);
  424. }
  425. StringCchPrintfA(tmp, 512, WASABI_API_LNGSTRING(IDS_QUALITY_FACTOR_F_KBPS), dispVal, br);
  426. SetDlgItemTextA(hwndDlg,IDC_VBRVAL,tmp);
  427. link_startsubclass(hwndDlg, IDC_URL1);
  428. link_startsubclass(hwndDlg, IDC_URL2);
  429. }
  430. else if (uMsg == WM_INITDIALOG)
  431. {
  432. #if defined (_WIN64)
  433. SetWindowLong(hwndDlg,GWLP_USERDATA,(LONG)lParam);
  434. #else
  435. SetWindowLong(hwndDlg, GWL_USERDATA, lParam);
  436. #endif
  437. if (lParam)
  438. {
  439. configwndrec *wc=(configwndrec*)lParam;
  440. SendDlgItemMessage(hwndDlg,IDC_VBRQUALITY,TBM_SETRANGE,0,MAKELONG(0,110));
  441. SendDlgItemMessage(hwndDlg,IDC_VBRQUALITY,TBM_SETTICFREQ,5,0);
  442. SendDlgItemMessage(hwndDlg,IDC_VBRQUALITY,TBM_SETPAGESIZE,0,10);
  443. SendDlgItemMessage(hwndDlg,IDC_VBRQUALITY,TBM_SETPOS,TRUE,(int)(wc->cfg.cfg_vbrquality*100.0f + (wc->cfg.cfg_vbrquality < 0.0 ? - 0.5f : 0.5f)) + 10);
  444. SendMessage(hwndDlg,WM_USER+667,0,0);
  445. }
  446. }
  447. else if (uMsg == WM_HSCROLL)
  448. {
  449. HWND swnd = (HWND) lParam;
  450. if (swnd == GetDlgItem(hwndDlg, IDC_VBRQUALITY))
  451. {
  452. int p=(int)SendDlgItemMessage(hwndDlg,IDC_VBRQUALITY,TBM_GETPOS,0,0)-10;
  453. #if defined (_WIN64)
  454. configwndrec* wc = (configwndrec*)GetWindowLong(hwndDlg, GWLP_USERDATA);
  455. #else
  456. configwndrec* wc = (configwndrec*)GetWindowLong(hwndDlg, GWL_USERDATA);
  457. #endif
  458. if (wc)
  459. {
  460. wc->cfg.cfg_vbrquality=(float)p/100.0f;
  461. }
  462. SendMessage(hwndDlg,WM_USER+667,0,0);
  463. }
  464. }
  465. else if (uMsg == WM_COMMAND)
  466. {
  467. if(LOWORD(wParam) == IDC_URL1)
  468. {
  469. SendMessage(winampwnd, WM_WA_IPC, (WPARAM)"http://xiph.org/vorbis/", IPC_OPEN_URL);
  470. }
  471. else if(LOWORD(wParam) == IDC_URL2)
  472. {
  473. SendMessage(winampwnd, WM_WA_IPC, (WPARAM)"https://ao-yumi.github.io/aotuv_web/index.html", IPC_OPEN_URL);
  474. }
  475. }
  476. else if (uMsg == WM_DESTROY)
  477. {
  478. #if defined (_WIN64)
  479. configwndrec* wc = (configwndrec*)SetWindowLong(hwndDlg, GWLP_USERDATA, 0);
  480. #else
  481. configwndrec* wc = (configwndrec*)SetWindowLong(hwndDlg, GWL_USERDATA, 0);
  482. #endif
  483. if (wc)
  484. {
  485. wc->cfg.cfg_mode=0;
  486. writeconfig(wc->configfile,&wc->cfg);
  487. free(wc->configfile);
  488. free(wc);
  489. }
  490. }
  491. const int controls[] =
  492. {
  493. IDC_VBRQUALITY,
  494. };
  495. if (FALSE != WASABI_API_APP->DirectMouseWheel_ProcessDialogMessage(hwndDlg, uMsg, wParam, lParam, controls, ARRAYSIZE(controls)))
  496. {
  497. return TRUE;
  498. }
  499. link_handledraw(hwndDlg, uMsg, wParam, lParam);
  500. return 0;
  501. }
  502. HWND __declspec(dllexport) ConfigAudio3(HWND hwndParent, HINSTANCE hinst, unsigned int outt, char *configfile)
  503. {
  504. if (outt == mmioFOURCC('O','G','G',' '))
  505. {
  506. configwndrec *wr=(configwndrec*)malloc(sizeof(configwndrec));
  507. if (configfile) wr->configfile=_strdup(configfile);
  508. else wr->configfile=0;
  509. readconfig(configfile,&wr->cfg);
  510. GetLocalisationApiService();
  511. return WASABI_API_CREATEDIALOGPARAMW(IDD_CONFIG,hwndParent,DlgProc,(LPARAM)wr);
  512. }
  513. return NULL;
  514. }
  515. void __declspec(dllexport) SetWinampHWND(HWND hwnd)
  516. {
  517. winampwnd = hwnd;
  518. isthemethere = !SendMessage(hwnd,WM_WA_IPC,IPC_ISWINTHEMEPRESENT,IPC_USE_UXTHEME_FUNC);
  519. }
  520. };