1
0

out_wave.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. #include "out_wave.h"
  2. #include "api.h"
  3. #include "resource.h"
  4. #include "waveout.h"
  5. #include "../winamp/wa_ipc.h"
  6. #include "../nu/AutoWide.h"
  7. #ifdef HAVE_SSRC
  8. #include "ssrc\ssrc.h"
  9. static Resampler_base * pSSRC;
  10. #endif
  11. static bool gapless_stop;
  12. static WaveOut * pWO;
  13. static __int64 total_written;
  14. static int pos_delta;
  15. static UINT canwrite_hack;
  16. // wasabi based services for localisation support
  17. api_service *WASABI_API_SVC = 0;
  18. api_language *WASABI_API_LNG = 0;
  19. api_application *WASABI_API_APP = 0;
  20. HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
  21. void _init();
  22. //cfg_prebuf now in ms !
  23. UINT cfg_dev = 0, cfg_buf_ms = 2000, cfg_prebuf = 200, cfg_trackhack = 200;
  24. bool cfg_volume = 1, cfg_altvol = 0, cfg_resetvol = 0;
  25. static int fmt_sr, fmt_bps, fmt_nch;
  26. static int volume = 255, pan = 0;
  27. #ifdef HAVE_SSRC
  28. UINT cfg_dither = 1, cfg_resample_freq = 48000, cfg_resample_bps = 1;
  29. bool cfg_fast = 1;
  30. UINT cfg_pdf = 1;
  31. UINT bps_tab[3] = {8, 16, 24};
  32. static bool finished, use_finish;
  33. static UINT resample_freq;
  34. static UINT resample_bps;
  35. static void do_ssrc_create()
  36. {
  37. pSSRC = SSRC_create(fmt_sr, resample_freq, fmt_bps, resample_bps, fmt_nch, cfg_dither, cfg_pdf, cfg_fast, 0);
  38. finished = 0;
  39. use_finish = cfg_trackhack == 0 ? 1 : 0;
  40. }
  41. #endif
  42. void do_cfg(bool s);
  43. static CRITICAL_SECTION sync; //various funky time consuming stuff going on, better protect ourselves with a critical section here, resampler doesnt have its own one
  44. #define SYNC_IN EnterCriticalSection(&sync);
  45. #define SYNC_OUT LeaveCriticalSection(&sync);
  46. void Config(HWND);
  47. int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message)
  48. {
  49. MSGBOXPARAMSW msgbx = {sizeof(MSGBOXPARAMSW),0};
  50. msgbx.lpszText = message;
  51. msgbx.lpszCaption = title;
  52. msgbx.lpszIcon = MAKEINTRESOURCEW(102);
  53. msgbx.hInstance = GetModuleHandle(0);
  54. msgbx.dwStyle = MB_USERICON;
  55. msgbx.hwndOwner = parent;
  56. return MessageBoxIndirectW(&msgbx);
  57. }
  58. void About(HWND hwndParent)
  59. {
  60. wchar_t message[1024] = {0}, text[1024] = {0};
  61. WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_WAVEOUT_OLD,text,1024);
  62. wsprintfW(message, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),
  63. mod.description, __DATE__);
  64. DoAboutMessageBox(hwndParent,text,message);
  65. }
  66. static void Init()
  67. {
  68. if (!IsWindow(mod.hMainWindow))
  69. return;
  70. // loader so that we can get the localisation service api for use
  71. WASABI_API_SVC = (api_service*)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GET_API_SERVICE);
  72. if (WASABI_API_SVC == (api_service*)1) WASABI_API_SVC = NULL;
  73. if (!WASABI_API_SVC || WASABI_API_SVC == (api_service *)1)
  74. return;
  75. waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID);
  76. if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
  77. sf = WASABI_API_SVC->service_getServiceByGuid(applicationApiServiceGuid);
  78. if (sf) WASABI_API_APP = reinterpret_cast<api_application*>(sf->getInterface());
  79. // need to have this initialised before we try to do anything with localisation features
  80. WASABI_API_START_LANG(mod.hDllInstance,OutWaveLangGUID);
  81. static wchar_t szDescription[256];
  82. swprintf(szDescription,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_WAVEOUT),OUT_WAVE_VER);
  83. mod.description = (char*)szDescription;
  84. }
  85. static int inited;
  86. static void Quit()
  87. {
  88. if (inited)
  89. {
  90. if (pWO)
  91. {
  92. delete pWO;
  93. pWO = 0;
  94. }
  95. #ifdef HAVE_SSRC
  96. if (pSSRC)
  97. {
  98. delete pSSRC;
  99. pSSRC = 0;
  100. }
  101. #endif
  102. do_cfg(1);
  103. inited = 0;
  104. }
  105. }
  106. static void reset_stuff()
  107. {
  108. canwrite_hack = 0;
  109. pos_delta = 0;
  110. total_written = 0;
  111. gapless_stop = 0;
  112. }
  113. void _init()
  114. {
  115. if (!inited)
  116. {
  117. inited = 1;
  118. do_cfg(0);
  119. if (cfg_dev > waveOutGetNumDevs()) cfg_dev = 0;
  120. }
  121. }
  122. static void _write(char * data, int size);
  123. int Open(int sr, int nch, int bps, int bufferlenms, int prebufferms)
  124. {
  125. _init();
  126. SYNC_IN;
  127. if (pWO) //"gapless" track change (or someone forgot to close output)
  128. {
  129. pWO->SetCloseOnStop(0); //turn off self-destruct on out-of-PCM-data
  130. if (!pWO->IsClosed()) //has it run out of PCM data or not ? if yes, we can only delete and create new one
  131. {
  132. if (sr != fmt_sr || nch != fmt_nch || bps != fmt_bps) //tough shit, new pcm format
  133. { //wait-then-close, dont cut previous track
  134. #ifdef HAVE_SSRC
  135. if (!pSSRC && !finished)
  136. {
  137. use_finish = 1;
  138. _write(0, 0);
  139. }
  140. #endif
  141. while (pWO->GetLatency() > 0) Sleep(1);
  142. }
  143. else
  144. { //successful gapless track change. yay.
  145. reset_stuff();
  146. #ifdef HAVE_SSRC
  147. if (pSSRC)
  148. {
  149. if (finished)
  150. {
  151. delete pSSRC;
  152. do_ssrc_create();
  153. }
  154. else use_finish = cfg_trackhack == 0 ? 1 : 0;
  155. }
  156. #endif
  157. int r = pWO->GetMaxLatency();
  158. SYNC_OUT;
  159. return r;
  160. }
  161. }
  162. #ifdef HAVE_SSRC
  163. if (pSSRC)
  164. {
  165. delete pSSRC;
  166. pSSRC = 0;
  167. }
  168. #endif
  169. delete pWO;
  170. }
  171. WaveOutConfig * cfg = new WaveOutConfig; //avoid crazy crt references with creating cfg on stack, keep TINY_DLL config happy
  172. cfg->SetBuffer(cfg_buf_ms, cfg_prebuf);
  173. cfg->SetDevice(cfg_dev);
  174. cfg->SetVolumeSetup(cfg_volume, cfg_altvol, cfg_resetvol);
  175. fmt_sr = sr;
  176. fmt_nch = nch;
  177. fmt_bps = bps;
  178. #ifdef HAVE_SSRC
  179. resample_freq = cfg_resample_freq;
  180. if (resample_freq < 6000) resample_freq = 6000;
  181. else if (resample_freq > 192000) resample_freq = 192000;
  182. resample_bps = bps_tab[cfg_resample_bps];
  183. if (fmt_sr == (int)resample_freq && fmt_bps == (int)resample_bps)
  184. {
  185. cfg->SetPCM(fmt_sr, fmt_nch, fmt_bps);
  186. }
  187. else
  188. {
  189. do_ssrc_create();
  190. }
  191. if (!pSSRC) cfg->SetPCM(sr, nch, bps);
  192. else cfg->SetPCM(resample_freq, nch, resample_bps);
  193. #else//!HAVE_SSRC
  194. cfg->SetPCM(sr, nch, bps);
  195. #endif
  196. pWO = WaveOut::Create(cfg);
  197. if (!pWO)
  198. {
  199. const WCHAR *error = cfg->GetError();
  200. if (error)
  201. {
  202. WCHAR err[128] = {0}, temp[128] = {0};
  203. swprintf(err,128,WASABI_API_LNGSTRINGW(IDS_ERROR),WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_WAVEOUT_OLD,temp,128));
  204. MessageBoxW(mod.hMainWindow, error, err, MB_ICONERROR);
  205. }
  206. }
  207. else
  208. {
  209. reset_stuff();
  210. }
  211. delete cfg;
  212. if (pWO)
  213. {
  214. int r = pWO->GetMaxLatency();
  215. SYNC_OUT;
  216. return r;
  217. }
  218. else
  219. {
  220. #ifdef HAVE_SSRC
  221. if (pSSRC)
  222. {
  223. delete pSSRC;
  224. pSSRC = 0;
  225. }
  226. #endif
  227. SYNC_OUT;
  228. return -1;
  229. }
  230. }
  231. #ifdef HAVE_SSRC
  232. static UINT ssrc_extra_latency;
  233. static void _write(char * data, int size)
  234. {
  235. if (pWO)
  236. {
  237. if (pSSRC)
  238. {
  239. if (!finished)
  240. {
  241. UINT nsiz;
  242. if (data > 0) pSSRC->Write(data, (UINT)size);
  243. else if (use_finish)
  244. {
  245. finished = 1;
  246. pSSRC->Finish();
  247. }
  248. data = (char*)pSSRC->GetBuffer(&nsiz);
  249. UINT nsiz1 = nsiz;
  250. while (nsiz) //ugly
  251. {
  252. int wr = pWO->WriteData(data, nsiz);
  253. if (wr > 0)
  254. {
  255. data += wr;
  256. nsiz -= wr;
  257. if (!nsiz) break;
  258. }
  259. ssrc_extra_latency = MulDiv(nsiz, 1000, resample_freq * fmt_nch * (resample_bps >> 3));
  260. SYNC_OUT;
  261. Sleep(1); //shouldnt happen anymore since canwrite works correctly
  262. SYNC_IN;
  263. }
  264. pSSRC->Read(nsiz1);
  265. ssrc_extra_latency = 0;
  266. }
  267. }
  268. else
  269. {
  270. pWO->WriteData(data, size);
  271. }
  272. total_written += size / ((fmt_bps >> 3) * fmt_nch);
  273. }
  274. }
  275. #endif
  276. int Write(char *data, int size)
  277. {
  278. SYNC_IN;
  279. gapless_stop = 0;
  280. canwrite_hack = 0;
  281. // decrypt, if necessary
  282. #ifdef HAVE_SSRC
  283. _write(data, size);
  284. #else
  285. if (pWO)
  286. {
  287. pWO->WriteData(data, size);
  288. total_written += size / ((fmt_bps >> 3) * fmt_nch);
  289. }
  290. #endif
  291. SYNC_OUT;
  292. return 0;
  293. }
  294. void Close()
  295. {
  296. SYNC_IN;
  297. if (pWO)
  298. {
  299. if (gapless_stop) //end-of-song stop, dont close yet, use gapless hacks
  300. {
  301. pWO->SetCloseOnStop(1); //will self-destruct when out of PCM data to play, has no more than 200ms in buffer
  302. }
  303. else //regular stop (user action)
  304. {
  305. delete pWO;
  306. pWO = 0;
  307. #ifdef HAVE_SSRC
  308. if (pSSRC)
  309. {
  310. delete pSSRC;
  311. pSSRC = 0;
  312. }
  313. #endif
  314. }
  315. }
  316. SYNC_OUT;
  317. }
  318. int CanWrite()
  319. {
  320. int r;
  321. SYNC_IN;
  322. if (pWO)
  323. {
  324. #ifdef HAVE_SSRC
  325. if (pSSRC)
  326. {
  327. r = MulDiv(pWO->CanWrite() - (resample_bps >> 3) * fmt_nch, fmt_bps * fmt_sr, resample_freq * resample_bps) - pSSRC->GetDataInInbuf();
  328. if (r < 0) r = 0;
  329. }
  330. else
  331. #endif
  332. r = pWO->CanWrite();
  333. if (++canwrite_hack > 2) pWO->ForcePlay(); //avoid constant-small-canwrite-while-still-prebuffering snafu
  334. }
  335. else r = 0;
  336. SYNC_OUT;
  337. return r;
  338. }
  339. int IsPlaying()
  340. { //this is called only when decoding is done unless some input plugin dev is really nuts about making useless calls
  341. SYNC_IN;
  342. if (pWO)
  343. {
  344. #ifdef HAVE_SSRC
  345. _write(0, 0);
  346. #endif
  347. pWO->ForcePlay(); //evil short files: make sure that output has started
  348. if ((UINT)pWO->GetLatency() > cfg_trackhack) //cfg_trackhack used to be 200ms constant
  349. { //just for the case some input plugin dev is actually nuts about making useless calls or user presses stop/prev/next when decoding is finished, we don't activate gapless_stop here
  350. gapless_stop = 0;
  351. SYNC_OUT;
  352. return 1;
  353. }
  354. else
  355. { //ok so looks like we're really near the end-of-track, time to do gapless track switch mumbo-jumbo
  356. gapless_stop = 1;
  357. SYNC_OUT;
  358. return 0; //hack: make the input plugin think that we're done with current track
  359. }
  360. }
  361. else
  362. {
  363. SYNC_OUT;
  364. return 0;
  365. }
  366. }
  367. int Pause(int new_state)
  368. {
  369. int rv;
  370. SYNC_IN;
  371. if (pWO)
  372. {
  373. rv = pWO->IsPaused();
  374. pWO->Pause(new_state);
  375. }
  376. else rv = 0;
  377. SYNC_OUT;
  378. return rv;
  379. }
  380. void Flush(int pos)
  381. {
  382. SYNC_IN;
  383. if (pWO) pWO->Flush();
  384. #ifdef HAVE_SSRC
  385. if (pSSRC)
  386. {
  387. delete pSSRC;
  388. do_ssrc_create();
  389. }
  390. #endif
  391. reset_stuff();
  392. pos_delta = pos;
  393. SYNC_OUT;
  394. }
  395. void SetVolume(int v)
  396. {
  397. SYNC_IN;
  398. if (v != -666)
  399. {
  400. volume = v;
  401. }
  402. if (pWO) pWO->SetVolume(volume);
  403. SYNC_OUT;
  404. }
  405. void SetPan(int p)
  406. {
  407. SYNC_IN;
  408. pan = p;
  409. if (pWO) pWO->SetPan(pan);
  410. SYNC_OUT;
  411. }
  412. int get_written_time() //this ignores high 32bits of total_written
  413. {
  414. return MulDiv((int)total_written, 1000, fmt_sr);
  415. }
  416. int GetWrittenTime()
  417. {
  418. int r;
  419. SYNC_IN;
  420. r = pWO ? pos_delta + get_written_time() : 0;
  421. SYNC_OUT;
  422. return r;
  423. }
  424. int GetOutputTime()
  425. {
  426. int r;
  427. SYNC_IN;
  428. r = pWO ? (pos_delta + get_written_time()) - pWO->GetLatency() : 0;
  429. #ifdef HAVE_SSRC
  430. if (pSSRC)
  431. r -= ssrc_extra_latency ? ssrc_extra_latency : pSSRC->GetLatency();
  432. #endif
  433. SYNC_OUT;
  434. return r;
  435. }
  436. static int Validate(int dummy1, int dummy2, short key, char dummy4);
  437. static int NewWrite(int len, char *buf);
  438. Out_Module mod =
  439. {
  440. OUT_VER_U,
  441. #ifdef HAVE_SSRC
  442. NAME" SSRC",
  443. #else
  444. 0,
  445. #endif
  446. 1471482036, //could put different one for SSRC config but i'm too lazy and this shit doesnt seem used anymore anyway
  447. 0, 0,
  448. Config,
  449. About,
  450. Init,
  451. Quit,
  452. Open,
  453. Close,
  454. Write,
  455. CanWrite,
  456. IsPlaying,
  457. Pause,
  458. SetVolume,
  459. SetPan,
  460. Flush,
  461. GetOutputTime,
  462. GetWrittenTime,
  463. };
  464. HMODULE thisMod=0;
  465. Out_Module *(*waveGetter)(HINSTANCE) = 0;
  466. HMODULE inWMDLL = 0;
  467. BOOL APIENTRY DllMain(HANDLE hMod, DWORD r, void*)
  468. {
  469. if (r == DLL_PROCESS_ATTACH)
  470. {
  471. thisMod=(HMODULE)hMod;
  472. DisableThreadLibraryCalls((HMODULE)hMod);
  473. InitializeCriticalSection(&sync);
  474. }
  475. else if (r == DLL_PROCESS_DETACH)
  476. {
  477. DeleteCriticalSection(&sync);
  478. if (inWMDLL)
  479. {
  480. FreeLibrary(inWMDLL); // potentially unsafe, we'll see ...
  481. inWMDLL = 0;
  482. }
  483. }
  484. return TRUE;
  485. }
  486. extern "C"
  487. {
  488. __declspec( dllexport ) Out_Module * winampGetOutModule()
  489. {
  490. inWMDLL = GetModuleHandleW(L"in_wm.dll");
  491. if (inWMDLL)
  492. {
  493. waveGetter = (Out_Module * (*)(HINSTANCE))GetProcAddress(inWMDLL, "GetWave");
  494. if (waveGetter)
  495. return waveGetter(thisMod);
  496. }
  497. return &mod;
  498. }
  499. }
  500. bool get_waveout_state(char * z)
  501. {
  502. if (pWO) return pWO->PrintState(z);
  503. else return 0;
  504. }