in_wv.cpp 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062
  1. /*
  2. ** .WV input plug-in for WavPack
  3. ** Copyright (c) 2000 - 2006, Conifer Software, All Rights Reserved
  4. */
  5. #include <windows.h>
  6. #include <fcntl.h>
  7. #include <stdio.h>
  8. #include <mmreg.h>
  9. #include <msacm.h>
  10. #include <math.h>
  11. #include <sys/stat.h>
  12. #include <io.h>
  13. #include <strsafe.h>
  14. #include "in2.h"
  15. #include "wavpack.h"
  16. #include "resource.h"
  17. #include "wasabi/winamp/wa_ipc.h"
  18. #include "wasabi/wasabi.h"
  19. #include "wasabi/nu/autochar.h"
  20. #include "wasabi/nu/autowide.h"
  21. #define fileno _fileno
  22. static float calculate_gain(WavpackContext *wpc, bool allowDefault=true);
  23. #define PLUGIN_VERSION "2.8.1"
  24. //#define DEBUG_CONSOLE
  25. //#define ANSI_METADATA
  26. #define UNICODE_METADATA
  27. //#define OLD_INFO_DIALOG
  28. // post this to the main window at end of file (after playback as stopped)
  29. #define WM_WA_MPEG_EOF WM_USER+2
  30. #define MAX_NCH 8
  31. static struct wpcnxt {
  32. WavpackContext *wpc; // WavPack context provided by library
  33. float play_gain; // playback gain (for replaygain support)
  34. //int soft_clipping; // soft clipping active for playback
  35. int output_bits; // 16, 24, or 32 bits / sample
  36. long sample_buffer[576*MAX_NCH*2]; // sample buffer
  37. float error [MAX_NCH]; // error term for noise shaping
  38. char lastfn[MAX_PATH]; // filename stored for comparisons only
  39. wchar_t w_lastfn[MAX_PATH]; // w_filename stored for comparisons only
  40. FILE *wv_id, *wvc_id; // file pointer when we use reader callbacks
  41. } curr, edit, info;
  42. int decode_pos_ms; // current decoding position, in milliseconds
  43. int paused; // are we paused?
  44. int seek_needed; // if != -1, it is the point that the decode thread should seek to, in ms.
  45. #define ALLOW_WVC 0x1
  46. #define REPLAYGAIN_TRACK 0x2
  47. #define REPLAYGAIN_ALBUM 0x4
  48. #define SOFTEN_CLIPPING 0x8
  49. #define PREVENT_CLIPPING 0x10
  50. #define ALWAYS_16BIT 0x20 // new flags added for version 2.5
  51. #define ALLOW_MULTICHANNEL 0x40
  52. #define REPLAYGAIN_24BIT 0x80
  53. int config_bits = ALLOW_WVC | ALLOW_MULTICHANNEL; // all configuration goes here
  54. int killDecodeThread=0; // the kill switch for the decode thread
  55. HANDLE thread_handle=INVALID_HANDLE_VALUE; // the handle to the decode thread
  56. DWORD WINAPI __stdcall DecodeThread(void *b); // the decode thread procedure
  57. static api_service* WASABI_API_SVC;
  58. static api_language* WASABI_API_LNG;
  59. static api_config *AGAVE_API_CONFIG;
  60. static api_memmgr *WASABI_API_MEMMGR;
  61. HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
  62. static char file_extensions[128] = {"WV\0WavPack Files (*.WV)\0"};
  63. // function definitions for the In_Module stucture
  64. void about (HWND hwndParent);
  65. void init();
  66. void quit();
  67. void getfileinfo(const char *filename, char *title, int *length_in_ms);
  68. int infoDlg(const char *fn, HWND hwnd);
  69. int isourfile(const char *fn);
  70. int play(const char *fn);
  71. void pause();
  72. void unpause();
  73. int ispaused();
  74. void stop();
  75. int getlength();
  76. int getoutputtime();
  77. void setoutputtime(int time_in_ms);
  78. void setvolume(int volume);
  79. void setpan(int pan);
  80. void eq_set(int on, char data [10], int preamp);
  81. In_Module mod = // the output module
  82. {
  83. IN_VER,
  84. "WavPack Decoder v"PLUGIN_VERSION,
  85. 0, // hMainWindow
  86. 0, // hDllInstance
  87. file_extensions,
  88. 1, // is_seekable
  89. 1, // uses output
  90. about,
  91. about,
  92. init,
  93. quit,
  94. getfileinfo,
  95. infoDlg,
  96. isourfile,
  97. play,
  98. pause,
  99. unpause,
  100. ispaused,
  101. stop,
  102. getlength,
  103. getoutputtime,
  104. setoutputtime,
  105. setvolume,
  106. setpan,
  107. 0,0,0,0,0,0,0,0,0, // vis stuff
  108. 0,0, // dsp
  109. eq_set,
  110. NULL, // setinfo
  111. 0 // out_mod
  112. };
  113. static BOOL CALLBACK WavPackDlgProc(HWND, UINT, WPARAM, LPARAM);
  114. extern long dump_alloc (void);
  115. int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message)
  116. {
  117. MSGBOXPARAMSW msgbx = {sizeof(MSGBOXPARAMSW),0};
  118. msgbx.lpszText = message;
  119. msgbx.lpszCaption = title;
  120. msgbx.lpszIcon = MAKEINTRESOURCEW(102);
  121. msgbx.hInstance = GetModuleHandle(0);
  122. msgbx.dwStyle = MB_USERICON;
  123. msgbx.hwndOwner = parent;
  124. return MessageBoxIndirectW(&msgbx);
  125. }
  126. void about(HWND hwndParent)
  127. {
  128. wchar_t about_string[512];
  129. #ifdef DEBUG_ALLOC
  130. sprintf (about_string, "alloc_count = %d", dump_alloc ());
  131. #else
  132. StringCchPrintfW(about_string, 512, WASABI_API_LNGSTRINGW(IDS_ABOUT_MESSAGE), PLUGIN_VERSION, "1998-2010", __DATE__);
  133. #endif
  134. DoAboutMessageBox(hwndParent, WASABI_API_LNGSTRINGW(IDS_ABOUT), about_string);
  135. }
  136. void init() /* any one-time initialization goes here (configuration reading, etc) */
  137. {
  138. if (mod.hMainWindow)
  139. {
  140. // load all of the required wasabi services from the winamp client
  141. WASABI_API_SVC = (api_service *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GET_API_SERVICE);
  142. if (WASABI_API_SVC == (api_service *)1)
  143. WASABI_API_SVC=0;
  144. WASABI_API_SVC->service_register(&albumArtFactory);
  145. }
  146. ServiceBuild(AGAVE_API_CONFIG, AgaveConfigGUID);
  147. ServiceBuild(WASABI_API_LNG, languageApiGUID);
  148. ServiceBuild(WASABI_API_MEMMGR, memMgrApiServiceGuid);
  149. // need to have this initialised before we try to do anything with localisation features
  150. WASABI_API_START_LANG(mod.hDllInstance,InWvLangGuid);
  151. static char szDescription[256];
  152. StringCchPrintfA(szDescription,256,WASABI_API_LNGSTRING(IDS_DESCRIPTION),PLUGIN_VERSION);
  153. mod.description = szDescription;
  154. // set the file extension to the localised version
  155. char tmp [64], *tmp_ptr = tmp, *fex_ptr = file_extensions;
  156. WASABI_API_LNGSTRING_BUF(IDS_FILETYPE, tmp, sizeof (tmp));
  157. *fex_ptr++ = 'W';
  158. *fex_ptr++ = 'V';
  159. *fex_ptr++ = 0;
  160. while (*tmp_ptr)
  161. *fex_ptr++ = *tmp_ptr++;
  162. *fex_ptr++ = 0;
  163. *fex_ptr++ = 0;
  164. }
  165. #ifdef DEBUG_CONSOLE
  166. HANDLE debug_console=INVALID_HANDLE_VALUE; // debug console
  167. void debug_write (char *str)
  168. {
  169. static int cant_debug;
  170. if (cant_debug)
  171. return;
  172. if (debug_console == INVALID_HANDLE_VALUE) {
  173. AllocConsole ();
  174. #if 1
  175. debug_console = GetStdHandle (STD_OUTPUT_HANDLE);
  176. #else
  177. debug_console = CreateConsoleScreenBuffer (GENERIC_WRITE, FILE_SHARE_WRITE,
  178. NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
  179. #endif
  180. if (debug_console == INVALID_HANDLE_VALUE) {
  181. MessageBox(NULL, "Can't get a console handle", "WavPack",MB_OK);
  182. cant_debug = 1;
  183. return;
  184. }
  185. else if (!SetConsoleActiveScreenBuffer (debug_console)) {
  186. MessageBox(NULL, "Can't activate console buffer", "WavPack",MB_OK);
  187. cant_debug = 1;
  188. return;
  189. }
  190. }
  191. WriteConsole (debug_console, str, strlen (str), NULL, NULL);
  192. }
  193. #endif
  194. void quit() /* one-time deinit, such as memory freeing */
  195. {
  196. #ifdef DEBUG_CONSOLE
  197. if (debug_console != INVALID_HANDLE_VALUE) {
  198. FreeConsole ();
  199. if (debug_console != GetStdHandle (STD_OUTPUT_HANDLE))
  200. CloseHandle (debug_console);
  201. debug_console = INVALID_HANDLE_VALUE;
  202. }
  203. #endif
  204. ServiceRelease(AGAVE_API_CONFIG, AgaveConfigGUID);
  205. ServiceRelease(WASABI_API_LNG, languageApiGUID);
  206. ServiceRelease(WASABI_API_MEMMGR, memMgrApiServiceGuid);
  207. WASABI_API_SVC->service_deregister(&albumArtFactory);
  208. }
  209. // used for detecting URL streams.. unused here. strncmp(fn,"http://",7) to detect HTTP streams, etc
  210. int isourfile(const char *fn)
  211. {
  212. return 0;
  213. }
  214. int play(const char *fn)
  215. {
  216. int num_chans, sample_rate;
  217. char error[128];
  218. int maxlatency;
  219. int thread_id;
  220. int open_flags;
  221. #ifdef DEBUG_CONSOLE
  222. sprintf (error, "play (%s)\n", fn);
  223. debug_write (error);
  224. #endif
  225. open_flags = OPEN_TAGS | OPEN_NORMALIZE;
  226. if (config_bits & ALLOW_WVC)
  227. open_flags |= OPEN_WVC;
  228. if (!(config_bits & ALLOW_MULTICHANNEL))
  229. open_flags |= OPEN_2CH_MAX;
  230. curr.wpc = WavpackOpenFileInput(fn, error, open_flags, 0);
  231. if (!curr.wpc) // error opening file, just return error
  232. return -1;
  233. num_chans = WavpackGetReducedChannels(curr.wpc);
  234. sample_rate = WavpackGetSampleRate(curr.wpc);
  235. curr.output_bits = WavpackGetBitsPerSample(curr.wpc) > 16 ? 24 : 16;
  236. if (config_bits & ALWAYS_16BIT)
  237. curr.output_bits = 16;
  238. else if ((config_bits & (REPLAYGAIN_TRACK | REPLAYGAIN_ALBUM)) &&
  239. (config_bits & REPLAYGAIN_24BIT))
  240. curr.output_bits = 24;
  241. if (num_chans > MAX_NCH) // don't allow too many channels!
  242. {
  243. WavpackCloseFile(curr.wpc);
  244. return -1;
  245. }
  246. curr.play_gain = calculate_gain(curr.wpc);
  247. lstrcpyn(curr.lastfn, fn, MAX_PATH);
  248. paused = 0;
  249. decode_pos_ms = 0;
  250. seek_needed = -1;
  251. maxlatency = mod.outMod->Open(sample_rate, num_chans, curr.output_bits, -1, -1);
  252. if (maxlatency < 0) // error opening device
  253. {
  254. curr.wpc = WavpackCloseFile(curr.wpc);
  255. return -1;
  256. }
  257. // dividing by 1000 for the first parameter of setinfo makes it
  258. // display 'H'... for hundred.. i.e. 14H Kbps.
  259. mod.SetInfo(0, (sample_rate + 500) / 1000, num_chans, 1);
  260. // initialize vis stuff
  261. mod.SAVSAInit(maxlatency, sample_rate);
  262. mod.VSASetInfo(sample_rate, num_chans);
  263. mod.outMod->SetVolume(-666); // set the output plug-ins default volume
  264. killDecodeThread=0;
  265. thread_handle = (HANDLE)CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) DecodeThread, (void *) &killDecodeThread, 0, (LPDWORD)&thread_id);
  266. if (SetThreadPriority(thread_handle, THREAD_PRIORITY_HIGHEST) == 0) {
  267. curr.wpc = WavpackCloseFile(curr.wpc);
  268. return -1;
  269. }
  270. return 0;
  271. }
  272. void pause()
  273. {
  274. #ifdef DEBUG_CONSOLE
  275. debug_write ("pause ()\n");
  276. #endif
  277. paused = 1;
  278. mod.outMod->Pause(1);
  279. }
  280. void unpause()
  281. {
  282. #ifdef DEBUG_CONSOLE
  283. debug_write ("unpause ()\n");
  284. #endif
  285. paused = 0;
  286. mod.outMod->Pause(0);
  287. }
  288. int ispaused()
  289. {
  290. return paused;
  291. }
  292. void stop()
  293. {
  294. #ifdef DEBUG_CONSOLE
  295. debug_write ("stop ()\n");
  296. #endif
  297. if (thread_handle != INVALID_HANDLE_VALUE)
  298. {
  299. killDecodeThread = 1;
  300. if (WaitForSingleObject(thread_handle, INFINITE) == WAIT_TIMEOUT)
  301. {
  302. MessageBox(mod.hMainWindow,"error asking thread to die!\n", "error killing decode thread", 0);
  303. TerminateThread(thread_handle,0);
  304. }
  305. CloseHandle(thread_handle);
  306. thread_handle = INVALID_HANDLE_VALUE;
  307. }
  308. if (curr.wpc)
  309. curr.wpc = WavpackCloseFile(curr.wpc);
  310. mod.outMod->Close();
  311. mod.SAVSADeInit();
  312. }
  313. int getlength()
  314. {
  315. return (int)(WavpackGetNumSamples (curr.wpc) * 1000.0 / WavpackGetSampleRate (curr.wpc));
  316. }
  317. int getoutputtime()
  318. {
  319. if (seek_needed == -1)
  320. return decode_pos_ms + (mod.outMod->GetOutputTime () - mod.outMod->GetWrittenTime ());
  321. else
  322. return seek_needed;
  323. }
  324. void setoutputtime (int time_in_ms)
  325. {
  326. #ifdef DEBUG_CONSOLE
  327. char str [40];
  328. sprintf (str, "setoutputtime (%d)\n", time_in_ms);
  329. debug_write (str);
  330. #endif
  331. seek_needed = time_in_ms;
  332. }
  333. void setvolume (int volume)
  334. {
  335. mod.outMod->SetVolume(volume);
  336. }
  337. void setpan (int pan)
  338. {
  339. mod.outMod->SetPan(pan);
  340. }
  341. static void generate_format_string(WavpackContext *wpc, wchar_t *string, int maxlen, int wide);
  342. static void AnsiToUTF8(char *string, int len);
  343. static int UTF8ToWideChar(const char *pUTF8, wchar_t *pWide);
  344. static void UTF8ToAnsi(char *string, int len);
  345. int infoDlg(const char *fn, HWND hwnd)
  346. {
  347. #ifdef OLD_INFO_DIALOG
  348. char string[2048];
  349. wchar_t w_string[2048];
  350. WavpackContext *wpc;
  351. int open_flags;
  352. open_flags = OPEN_TAGS | OPEN_NORMALIZE;
  353. if (config_bits & ALLOW_WVC)
  354. open_flags |= OPEN_WVC;
  355. if (!(config_bits & ALLOW_MULTICHANNEL))
  356. open_flags |= OPEN_2CH_MAX;
  357. wpc = WavpackOpenFileInput(fn, string, open_flags, 0);
  358. if (wpc)
  359. {
  360. int mode = WavpackGetMode(wpc);
  361. //generate_format_string(wpc, string, sizeof (string), 1);
  362. wchar_t *temp = (wchar_t *)malloc(sizeof(string) * sizeof(wchar_t));
  363. generate_format_string(wpc, temp, sizeof(string), 0);
  364. lstrcpyn(string, AutoChar(temp, CP_UTF8), sizeof(string));
  365. free(temp);
  366. if (WavpackGetMode(wpc) & MODE_VALID_TAG)
  367. {
  368. char value [128];
  369. if (config_bits & (REPLAYGAIN_TRACK | REPLAYGAIN_ALBUM))
  370. {
  371. int local_clipping = 0;
  372. float local_gain;
  373. local_gain = calculate_gain(wpc);
  374. if (local_gain != 1.0)
  375. StringCchPrintf(string + strlen (string), 2048, "Gain: %+.2f dB %s\n",
  376. log10 (local_gain) * 20.0, local_clipping ? "(w/soft clipping)" : "");
  377. }
  378. if (WavpackGetTagItem(wpc, "title", value, sizeof (value)))
  379. {
  380. if (!(mode & MODE_APETAG))
  381. AnsiToUTF8(value, sizeof (value));
  382. StringCchPrintf(string + strlen (string), 2048, "\nTitle: %s", value);
  383. }
  384. if (WavpackGetTagItem(wpc, "artist", value, sizeof (value)))
  385. {
  386. if (!(mode & MODE_APETAG))
  387. AnsiToUTF8(value, sizeof (value));
  388. StringCchPrintf(string + strlen (string), 2048, "\nArtist: %s", value);
  389. }
  390. if (WavpackGetTagItem (wpc, "album", value, sizeof (value)))
  391. {
  392. if (!(mode & MODE_APETAG))
  393. AnsiToUTF8(value, sizeof (value));
  394. StringCchPrintf (string + strlen (string), 2048, "\nAlbum: %s", value);
  395. }
  396. if (WavpackGetTagItem (wpc, "genre", value, sizeof (value)))
  397. {
  398. if (!(mode & MODE_APETAG))
  399. AnsiToUTF8(value, sizeof (value));
  400. StringCchPrintf(string + strlen (string), 2048, "\nGenre: %s", value);
  401. }
  402. if (WavpackGetTagItem (wpc, "comment", value, sizeof (value)))
  403. {
  404. if (!(mode & MODE_APETAG))
  405. AnsiToUTF8(value, sizeof (value));
  406. StringCchPrintf(string + strlen (string), 2048, "\nComment: %s", value);
  407. }
  408. if (WavpackGetTagItem(wpc, "year", value, sizeof (value)))
  409. StringCchPrintf(string + strlen (string), 2048, "\nYear: %s", value);
  410. if (WavpackGetTagItem(wpc, "track", value, sizeof (value)))
  411. StringCchPrintf(string + strlen (string), 2048, "\nTrack: %s", value);
  412. StringCchCat(string, 2048, "\n");
  413. }
  414. UTF8ToWideChar(string, w_string);
  415. MessageBoxW(hwnd, w_string, L"WavPack File Info Box", MB_OK);
  416. wpc = WavpackCloseFile(wpc);
  417. }
  418. else
  419. MessageBox(hwnd, string, "WavPack Decoder", MB_OK);
  420. return 0;
  421. #else
  422. return 1;
  423. #endif
  424. }
  425. void getfileinfo(const char *filename, char *title, int *length_in_ms)
  426. {
  427. if (!filename || !*filename) // currently playing file
  428. {
  429. if (length_in_ms)
  430. *length_in_ms = getlength ();
  431. if (title)
  432. {
  433. if (WavpackGetTagItem(curr.wpc, "title", NULL, 0))
  434. {
  435. char art [128], ttl [128];
  436. WavpackGetTagItem(curr.wpc, "title", ttl, sizeof (ttl));
  437. if (WavpackGetMode(curr.wpc) & MODE_APETAG)
  438. UTF8ToAnsi(ttl, sizeof (ttl));
  439. if (WavpackGetTagItem(curr.wpc, "artist", art, sizeof (art)))
  440. {
  441. if (WavpackGetMode(curr.wpc) & MODE_APETAG)
  442. UTF8ToAnsi(art, sizeof (art));
  443. StringCchPrintf(title, GETFILEINFO_TITLE_LENGTH, "%s - %s", art, ttl);
  444. }
  445. else
  446. lstrcpyn(title, ttl, GETFILEINFO_TITLE_LENGTH);
  447. }
  448. else
  449. {
  450. char *p = curr.lastfn + strlen (curr.lastfn);
  451. while (*p != '\\' && p >= curr.lastfn)
  452. p--;
  453. lstrcpyn(title, ++p, GETFILEINFO_TITLE_LENGTH);
  454. }
  455. }
  456. }
  457. else // some other file
  458. {
  459. WavpackContext *wpc;
  460. char error [128];
  461. int open_flags;
  462. if (length_in_ms)
  463. *length_in_ms = -1000;
  464. if (title)
  465. *title = 0;
  466. open_flags = OPEN_TAGS | OPEN_NORMALIZE;
  467. if (config_bits & ALLOW_WVC)
  468. open_flags |= OPEN_WVC;
  469. if (!(config_bits & ALLOW_MULTICHANNEL))
  470. open_flags |= OPEN_2CH_MAX;
  471. wpc = WavpackOpenFileInput(filename, error, open_flags, 0);
  472. if (wpc)
  473. {
  474. if (length_in_ms)
  475. *length_in_ms = (int)(WavpackGetNumSamples(wpc) * 1000.0 / WavpackGetSampleRate(wpc));
  476. if (title && WavpackGetTagItem(wpc, "title", NULL, 0))
  477. {
  478. char art [128], ttl [128];
  479. WavpackGetTagItem(wpc, "title", ttl, sizeof (ttl));
  480. if (WavpackGetMode(wpc) & MODE_APETAG)
  481. UTF8ToAnsi(ttl, sizeof (ttl));
  482. if (WavpackGetTagItem(wpc, "artist", art, sizeof (art)))
  483. {
  484. if (WavpackGetMode(wpc) & MODE_APETAG)
  485. UTF8ToAnsi(art, sizeof (art));
  486. StringCchPrintf(title, GETFILEINFO_TITLE_LENGTH, "%s - %s", art, ttl);
  487. }
  488. else
  489. lstrcpyn(title, ttl, GETFILEINFO_TITLE_LENGTH);
  490. }
  491. wpc = WavpackCloseFile(wpc);
  492. }
  493. if (title && !*title)
  494. {
  495. char *p = (char*)filename + strlen (filename);
  496. while (*p != '\\' && p >= filename) p--;
  497. lstrcpyn(title, ++p, GETFILEINFO_TITLE_LENGTH);
  498. }
  499. }
  500. }
  501. void eq_set(int on, char data [10], int preamp)
  502. {
  503. // most plug-ins can't even do an EQ anyhow.. I'm working on writing
  504. // a generic PCM EQ, but it looks like it'll be a little too CPU
  505. // consuming to be useful :)
  506. }
  507. static int read_samples(struct wpcnxt *cnxt, int num_samples);
  508. DWORD WINAPI __stdcall DecodeThread(void *b)
  509. {
  510. int num_chans, sample_rate;
  511. int done = 0;
  512. memset(curr.error, 0, sizeof (curr.error));
  513. num_chans = WavpackGetReducedChannels(curr.wpc);
  514. sample_rate = WavpackGetSampleRate(curr.wpc);
  515. while (!*((int *)b) )
  516. {
  517. if (seek_needed != -1)
  518. {
  519. int seek_position = seek_needed;
  520. int bc = 0;
  521. seek_needed = -1;
  522. if (seek_position > getlength() - 1000 && getlength() > 1000)
  523. seek_position = getlength() - 1000; // don't seek to last second
  524. mod.outMod->Flush(decode_pos_ms = seek_position);
  525. if (WavpackSeekSample(curr.wpc, (int)(sample_rate / 1000.0 * seek_position))) {
  526. decode_pos_ms = (int)(WavpackGetSampleIndex(curr.wpc) * 1000.0 / sample_rate);
  527. mod.outMod->Flush(decode_pos_ms);
  528. continue;
  529. }
  530. else
  531. done = 1;
  532. }
  533. if (done) {
  534. mod.outMod->CanWrite();
  535. if (!mod.outMod->IsPlaying()) {
  536. PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
  537. return 0;
  538. }
  539. Sleep(10);
  540. }
  541. else if (mod.outMod->CanWrite() >= ((576 * num_chans * (curr.output_bits / 8)) << (mod.dsp_isactive () ? 1 : 0)))
  542. {
  543. int tsamples = read_samples (&curr, 576) * num_chans;
  544. int tbytes = tsamples * (curr.output_bits/8);
  545. if (tsamples)
  546. {
  547. mod.SAAddPCMData((char *) curr.sample_buffer, num_chans, curr.output_bits, decode_pos_ms);
  548. mod.VSAAddPCMData((char *) curr.sample_buffer, num_chans, curr.output_bits, decode_pos_ms);
  549. decode_pos_ms = (int)(WavpackGetSampleIndex(curr.wpc) * 1000.0 / sample_rate);
  550. if (mod.dsp_isactive())
  551. tbytes = mod.dsp_dosamples ((short *) curr.sample_buffer,
  552. tsamples / num_chans, curr.output_bits, num_chans, sample_rate) * (num_chans * (curr.output_bits/8));
  553. mod.outMod->Write ((char *) curr.sample_buffer, tbytes);
  554. }
  555. else
  556. done = 1;
  557. }
  558. else
  559. {
  560. mod.SetInfo((int) ((WavpackGetInstantBitrate (curr.wpc) + 500.0) / 1000.0), -1, -1, 1);
  561. Sleep(20);
  562. }
  563. }
  564. return 0;
  565. }
  566. /********* These functions provide the "transcoding" mode of winamp. *********/
  567. extern "C" __declspec (dllexport) intptr_t winampGetExtendedRead_open (const char *fn, int *size, int *bps, int *nch, int *srate)
  568. {
  569. struct wpcnxt *cnxt = (struct wpcnxt *)malloc(sizeof (struct wpcnxt));
  570. int num_chans, sample_rate, open_flags;
  571. char error[128];
  572. #ifdef DEBUG_CONSOLE
  573. sprintf (error, "Read_open (%s)\n", fn);
  574. debug_write (error);
  575. #endif
  576. if (!cnxt)
  577. return 0;
  578. memset(cnxt, 0, sizeof (struct wpcnxt));
  579. open_flags = OPEN_NORMALIZE | OPEN_WVC;
  580. if (!(config_bits & ALLOW_MULTICHANNEL) || *nch == 2)
  581. open_flags |= OPEN_2CH_MAX;
  582. if (config_bits & (REPLAYGAIN_TRACK | REPLAYGAIN_ALBUM))
  583. open_flags |= OPEN_TAGS;
  584. cnxt->wpc = WavpackOpenFileInput(fn, error, open_flags, 0);
  585. if (!cnxt->wpc) // error opening file, just return error
  586. {
  587. free (cnxt);
  588. return 0;
  589. }
  590. num_chans = WavpackGetReducedChannels(cnxt->wpc);
  591. sample_rate = WavpackGetSampleRate(cnxt->wpc);
  592. if (num_chans > MAX_NCH)
  593. {
  594. WavpackCloseFile(cnxt->wpc);
  595. free (cnxt);
  596. return 0;
  597. }
  598. if (*bps != 16 && *bps != 24 && *bps != 32)
  599. {
  600. cnxt->output_bits = WavpackGetBitsPerSample(cnxt->wpc) > 16 ? 24 : 16;
  601. if (config_bits & ALWAYS_16BIT)
  602. cnxt->output_bits = 16;
  603. else if ((config_bits & (REPLAYGAIN_TRACK | REPLAYGAIN_ALBUM)) &&
  604. (config_bits & REPLAYGAIN_24BIT))
  605. cnxt->output_bits = 24;
  606. }
  607. else
  608. cnxt->output_bits = *bps;
  609. if (num_chans > MAX_NCH) // don't allow too many channels!
  610. {
  611. WavpackCloseFile(cnxt->wpc);
  612. free (cnxt);
  613. return 0;
  614. }
  615. *nch = num_chans;
  616. *srate = sample_rate;
  617. *bps = cnxt->output_bits;
  618. *size = WavpackGetNumSamples(cnxt->wpc) * (*bps / 8) * (*nch);
  619. cnxt->play_gain = calculate_gain(cnxt->wpc);
  620. #ifdef DEBUG_CONSOLE
  621. sprintf (error, "Read_open success! nch=%d, srate=%d, bps=%d, size=%d\n",
  622. *nch, *srate, *bps, *size);
  623. debug_write (error);
  624. #endif
  625. return (intptr_t) cnxt;
  626. }
  627. extern "C" __declspec (dllexport) intptr_t winampGetExtendedRead_getData (intptr_t handle, char *dest, int len, int *killswitch)
  628. {
  629. struct wpcnxt *cnxt = (struct wpcnxt *)handle;
  630. int num_chans = WavpackGetReducedChannels(cnxt->wpc);
  631. int bytes_per_sample = num_chans * cnxt->output_bits / 8;
  632. int used = 0;
  633. #ifdef DEBUG_CONSOLE
  634. char error [128];
  635. #endif
  636. while (used < len && !*killswitch)
  637. {
  638. int nsamples = (len - used) / bytes_per_sample, tsamples;
  639. if (!nsamples)
  640. break;
  641. else if (nsamples > 576)
  642. nsamples = 576;
  643. tsamples = read_samples(cnxt, nsamples) * num_chans;
  644. if (tsamples)
  645. {
  646. int tbytes = tsamples * (cnxt->output_bits/8);
  647. memcpy (dest + used, cnxt->sample_buffer, tbytes);
  648. used += tbytes;
  649. }
  650. else
  651. break;
  652. }
  653. #ifdef DEBUG_CONSOLE
  654. sprintf (error, "Read_getData (%d), actualy read %d\n", len, used);
  655. debug_write (error);
  656. #endif
  657. return used;
  658. }
  659. extern "C" __declspec (dllexport) int winampGetExtendedRead_setTime (intptr_t handle, int millisecs)
  660. {
  661. struct wpcnxt *cnxt = (struct wpcnxt *) handle;
  662. int sample_rate = WavpackGetSampleRate(cnxt->wpc);
  663. return WavpackSeekSample(cnxt->wpc, (int)(sample_rate / 1000.0 * millisecs));
  664. }
  665. extern "C" __declspec (dllexport) void winampGetExtendedRead_close (intptr_t handle)
  666. {
  667. struct wpcnxt *cnxt = (struct wpcnxt *) handle;
  668. #ifdef DEBUG_CONSOLE
  669. char error [128];
  670. sprintf (error, "Read_close ()\n");
  671. debug_write (error);
  672. #endif
  673. WavpackCloseFile(cnxt->wpc);
  674. free (cnxt);
  675. }
  676. /* This is a generic function to read WavPack samples and convert them to a
  677. * form usable by winamp. It includes conversion of any WavPack format
  678. * (including ieee float) to 16, 24, or 32-bit integers (with noise shaping
  679. * for the 16-bit case) and replay gain implementation (with optional soft
  680. * clipping). It is used by both the regular "play" code and the newer
  681. * transcoding functions.
  682. *
  683. * The num_samples parameter is the number of "composite" samples to
  684. * convert and is limited currently to 576 samples for legacy reasons. The
  685. * return value is the number of samples actually converted and will be
  686. * equal to the number requested unless an error occurs or the end-of-file
  687. * is encountered. The converted samples are stored (interleaved) at
  688. * cnxt->sample_buffer[].
  689. */
  690. static int read_samples (struct wpcnxt *cnxt, int num_samples)
  691. {
  692. int num_chans = WavpackGetReducedChannels(cnxt->wpc), samples, tsamples;
  693. samples = WavpackUnpackSamples(cnxt->wpc, (int32_t*) cnxt->sample_buffer, num_samples);
  694. tsamples = samples * num_chans;
  695. if (tsamples)
  696. {
  697. if (!(WavpackGetMode(cnxt->wpc) & MODE_FLOAT))
  698. {
  699. float scaler = (float) (1.0 / ((unsigned long) 1 << (WavpackGetBytesPerSample(cnxt->wpc) * 8 - 1)));
  700. float *fptr = (float *) cnxt->sample_buffer;
  701. long *lptr = cnxt->sample_buffer;
  702. int cnt = tsamples;
  703. while (cnt--)
  704. *fptr++ = *lptr++ * scaler;
  705. }
  706. if (cnxt->play_gain != 1.0)
  707. {
  708. float *fptr = (float *) cnxt->sample_buffer;
  709. int cnt = tsamples;
  710. double outval;
  711. while (cnt--)
  712. {
  713. outval = *fptr * cnxt->play_gain;
  714. /*if (cnxt->soft_clipping)
  715. {
  716. if (outval > 0.75)
  717. outval = 1.0 - (0.0625 / (outval - 0.5));
  718. else if (outval < -0.75)
  719. outval = -1.0 - (0.0625 / (outval + 0.5));
  720. }*/
  721. *fptr++ = (float) outval;
  722. }
  723. }
  724. if (cnxt->output_bits == 16)
  725. {
  726. float *fptr = (float *) cnxt->sample_buffer;
  727. short *sptr = (short *) cnxt->sample_buffer;
  728. int cnt = samples, ch;
  729. while (cnt--)
  730. for (ch = 0; ch < num_chans; ++ch)
  731. {
  732. int dst;
  733. *fptr -= cnxt->error [ch];
  734. if (*fptr >= 1.0)
  735. dst = 32767;
  736. else if (*fptr <= -1.0)
  737. dst = -32768;
  738. else
  739. dst = (int) floor (*fptr * 32768.0);
  740. cnxt->error [ch] = (float)(dst / 32768.0 - *fptr++);
  741. *sptr++ = dst;
  742. }
  743. }
  744. else if (cnxt->output_bits == 24)
  745. {
  746. unsigned char *cptr = (unsigned char *) cnxt->sample_buffer;
  747. float *fptr = (float *) cnxt->sample_buffer;
  748. int cnt = tsamples;
  749. long outval;
  750. while (cnt--) {
  751. if (*fptr >= 1.0)
  752. outval = 8388607;
  753. else if (*fptr <= -1.0)
  754. outval = -8388608;
  755. else
  756. outval = (int) floor (*fptr * 8388608.0);
  757. *cptr++ = (unsigned char) outval;
  758. *cptr++ = (unsigned char) (outval >> 8);
  759. *cptr++ = (unsigned char) (outval >> 16);
  760. fptr++;
  761. }
  762. }
  763. else if (cnxt->output_bits == 32)
  764. {
  765. float *fptr = (float *) cnxt->sample_buffer;
  766. long *sptr = (long *) cnxt->sample_buffer;
  767. int cnt = tsamples;
  768. while (cnt--)
  769. {
  770. if (*fptr >= 1.0)
  771. *sptr++ = 8388607 << 8;
  772. else if (*fptr <= -1.0)
  773. *sptr++ = -8388608 << 8;
  774. else
  775. *sptr++ = ((int) floor (*fptr * 8388608.0)) << 8;
  776. fptr++;
  777. }
  778. }
  779. }
  780. return samples;
  781. }
  782. extern "C" __declspec (dllexport) In_Module * winampGetInModule2()
  783. {
  784. return &mod;
  785. }
  786. // This code provides an interface between the reader callback mechanism that
  787. // WavPack uses internally and the standard fstream C library.
  788. static int32_t read_bytes(void *id, void *data, int32_t bcount)
  789. {
  790. FILE *file = id ? *(FILE**)id : NULL;
  791. if (file)
  792. return (int32_t) fread(data, 1, bcount, file);
  793. else
  794. return 0;
  795. }
  796. static uint32_t get_pos(void *id)
  797. {
  798. FILE *file = id ? *(FILE**)id : NULL;
  799. if (file)
  800. return ftell(file);
  801. else
  802. return -1;
  803. }
  804. static int set_pos_abs(void *id, uint32_t pos)
  805. {
  806. FILE *file = id ? *(FILE**)id : NULL;
  807. if (file)
  808. return fseek(file, pos, SEEK_SET);
  809. else
  810. return 0;
  811. }
  812. static int set_pos_rel(void *id, int32_t delta, int mode)
  813. {
  814. FILE *file = id ? *(FILE**)id : NULL;
  815. if (file)
  816. return fseek(file, delta, mode);
  817. else
  818. return -1;
  819. }
  820. static int push_back_byte(void *id, int c)
  821. {
  822. FILE *file = id ? *(FILE**)id : NULL;
  823. if (file)
  824. return ungetc(c, file);
  825. else
  826. return EOF;
  827. }
  828. static uint32_t get_length(void *id)
  829. {
  830. FILE *file = id ? *(FILE**)id : NULL;
  831. struct stat statbuf;
  832. if (!file || fstat (fileno (file), &statbuf) || !(statbuf.st_mode & S_IFREG))
  833. return 0;
  834. else
  835. return statbuf.st_size;
  836. }
  837. static int can_seek(void *id)
  838. {
  839. FILE *file = id ? *(FILE**)id : NULL;
  840. struct stat statbuf;
  841. return file && !fstat (fileno (file), &statbuf) && (statbuf.st_mode & S_IFREG);
  842. }
  843. static int32_t write_bytes(void *id, void *data, int32_t bcount)
  844. {
  845. FILE *file = id ? *(FILE**)id : NULL;
  846. if (file)
  847. return (int32_t) fwrite (data, 1, bcount, file);
  848. else
  849. return 0;
  850. }
  851. static WavpackStreamReader freader = {
  852. read_bytes, get_pos, set_pos_abs,
  853. set_pos_rel, push_back_byte,
  854. get_length, can_seek, write_bytes
  855. };
  856. /* These functions provide UNICODE support for the winamp media library */
  857. static int metadata_we_can_write(const char *metadata);
  858. static void close_context(struct wpcnxt *cxt)
  859. {
  860. if (cxt->wpc)
  861. WavpackCloseFile(cxt->wpc);
  862. if (cxt->wv_id)
  863. fclose(cxt->wv_id);
  864. if (cxt->wvc_id)
  865. fclose(cxt->wvc_id);
  866. memset(cxt, 0, sizeof (*cxt));
  867. }
  868. #ifdef ANSI_METADATA
  869. extern "C" __declspec (dllexport) int winampGetExtendedFileInfo(char *filename, char *metadata, char *ret, int retlen)
  870. {
  871. int open_flags = OPEN_TAGS;
  872. char error[128];
  873. int retval = 0;
  874. #ifdef DEBUG_CONSOLE
  875. sprintf (error, "winampGetExtendedFileInfo (%s)\n", metadata);
  876. debug_write (error);
  877. #endif
  878. if (!_stricmp(metadata, "type"))
  879. {
  880. ret[0] = '0';
  881. ret[1] = 0;
  882. return 1;
  883. }
  884. else if (!_stricmp(metadata, "family"))
  885. {
  886. int len;
  887. const char *p;
  888. if (!filename || !filename[0]) return 0;
  889. len = lstrlen(filename);
  890. if (len < 3 || '.' != filename[len - 3]) return 0;
  891. p = &filename[len - 2];
  892. if (!_stricmp(p, "wv")) { WASABI_API_LNGSTRING_BUF(IDS_FAMILY_STRING, ret, retlen); return 1; }
  893. return 0;
  894. }
  895. if (!filename || !*filename)
  896. return retval;
  897. if (!_stricmp(metadata, "length")) { /* even if no file, return a 1 and write "0" */
  898. StringCchPrintf(ret, retlen, "%d", 0);
  899. retval = 1;
  900. }
  901. if (!info.wpc || strcmp(filename, info.lastfn) || !_stricmp(metadata, "formatinformation"))
  902. {
  903. close_context(&info);
  904. if (!(info.wv_id = fopen(filename, "rb")))
  905. return retval;
  906. if (config_bits & ALLOW_WVC)
  907. {
  908. int length = strlen(filename) + 10;
  909. char *wvc_name = (char *)malloc(length);
  910. if (wvc_name)
  911. {
  912. lstrcpyn(wvc_name, filename, length);
  913. StringCchCat(wvc_name, length, "c");
  914. info.wvc_id = fopen(wvc_name, "rb");
  915. free(wvc_name);
  916. }
  917. }
  918. info.wpc = WavpackOpenFileInputEx(&freader, &info.wv_id, info.wvc_id ? &info.wvc_id : NULL, error, open_flags, 0);
  919. if (!info.wpc)
  920. {
  921. close_context(&info);
  922. return retval;
  923. }
  924. lstrcpyn(info.lastfn, filename, MAX_PATH);
  925. info.w_lastfn [0] = 0;
  926. }
  927. if (!_stricmp(metadata, "formatinformation"))
  928. {
  929. wchar_t *temp = (wchar_t *)malloc(retlen * sizeof(wchar_t));
  930. generate_format_string(info.wpc, temp, retlen, 0);
  931. lstrcpyn(ret, AutoChar(temp), retlen);
  932. free(temp);
  933. retval = 1;
  934. }
  935. else if (!_stricmp(metadata, "length"))
  936. {
  937. StringCchPrintf(ret, retlen, "%d", (int)(WavpackGetNumSamples(info.wpc) * 1000.0 / WavpackGetSampleRate(info.wpc)));
  938. retval = 1;
  939. }
  940. else if (!_stricmp(metadata, "lossless"))
  941. {
  942. StringCchPrintf (ret, retlen, "%d", (WavpackGetMode(info.wpc) & MODE_LOSSLESS) ? 1 : 0);
  943. retval = 1;
  944. }
  945. else if (!_stricmp(metadata, "numsamples"))
  946. {
  947. StringCchPrintf(ret, retlen, "%d", WavpackGetNumSamples(info.wpc));
  948. retval = 1;
  949. }
  950. else if (!_stricmp(metadata, "mime"))
  951. {
  952. lstrcpyn(ret, L"audio/x-wavpack", retlen);
  953. retval = 1;
  954. }
  955. else if (!_stricmp(metadata, "gain"))
  956. {
  957. StringCchPrintf(ret, retlen, "%-+.2f dB", calculate_gain(info.wpc, false));
  958. retval = 1;
  959. }
  960. else if (WavpackGetTagItem(info.wpc, metadata, ret, retlen))
  961. {
  962. if (!_stricmp(metadata, "rating"))
  963. {
  964. int rating = atoi(ret);
  965. // appears to be generally 0-5 or 0-100
  966. if (rating > 10) {
  967. rating /= 20;
  968. }
  969. // or maybe we're dealing with a 1-10 range
  970. else if (rating > 5) {
  971. rating /= 2;
  972. }
  973. // otherwise it is hopefully in the 0-5 range
  974. StringCchPrintf(ret, retlen, "%u", rating);
  975. }
  976. else
  977. {
  978. if (WavpackGetMode(info.wpc) & MODE_APETAG)
  979. {
  980. UTF8ToAnsi(ret, retlen);
  981. }
  982. }
  983. retval = 1;
  984. }
  985. else if (metadata_we_can_write(metadata))
  986. {
  987. if (retlen)
  988. *ret = 0;
  989. retval = 1;
  990. }
  991. // This is a little ugly, but since the WavPack library has read the tags off the
  992. // files, we can close the files (but not the WavPack context) now so that we don't
  993. // leave handles open. We may access the file again for the "formatinformation"
  994. // field, so we reopen the file if we get that one.
  995. if (info.wv_id)
  996. {
  997. fclose(info.wv_id);
  998. info.wv_id = NULL;
  999. }
  1000. if (info.wvc_id)
  1001. {
  1002. fclose(info.wvc_id);
  1003. info.wvc_id = NULL;
  1004. }
  1005. return retval;
  1006. }
  1007. #endif
  1008. #ifdef UNICODE_METADATA
  1009. extern "C" __declspec (dllexport) int winampGetExtendedFileInfoW (wchar_t *filename, char *metadata, wchar_t *ret, int retlen)
  1010. {
  1011. char error[128], res[256];
  1012. int open_flags = OPEN_TAGS;
  1013. int retval = 0;
  1014. #ifdef DEBUG_CONSOLE
  1015. sprintf (error, "winampGetExtendedFileInfoW (%s)\n", metadata);
  1016. debug_write (error);
  1017. #endif
  1018. if (!_stricmp(metadata, "type"))
  1019. {
  1020. ret[0] = '0';
  1021. ret[1] = 0;
  1022. return 1;
  1023. }
  1024. else if (!_stricmp(metadata, "family"))
  1025. {
  1026. int len;
  1027. const wchar_t *p;
  1028. if (!filename || !filename[0]) return 0;
  1029. len = lstrlenW(filename);
  1030. if (len < 3 || L'.' != filename[len - 3]) return 0;
  1031. p = &filename[len - 2];
  1032. if (!_wcsicmp(p, L"wv")) { WASABI_API_LNGSTRINGW_BUF(IDS_FAMILY_STRING, ret, retlen); return 1; }
  1033. return 0;
  1034. }
  1035. if (!filename || !*filename)
  1036. return retval;
  1037. if (!_stricmp(metadata, "length")) /* even if no file, return a 1 and write "0" */
  1038. {
  1039. StringCchPrintfW(ret, retlen, L"%d", 0);
  1040. retval = 1;
  1041. }
  1042. if (!info.wpc || wcscmp(filename, info.w_lastfn) || !_stricmp(metadata, "formatinformation"))
  1043. {
  1044. close_context(&info);
  1045. if (!(info.wv_id = _wfopen(filename, L"rb")))
  1046. return retval;
  1047. if (config_bits & ALLOW_WVC) {
  1048. int length = wcslen(filename) + 10;
  1049. wchar_t *wvc_name = (wchar_t *)malloc(length * sizeof(wchar_t));
  1050. if (wvc_name) {
  1051. lstrcpynW(wvc_name, filename, length);
  1052. StringCchCatW(wvc_name, length, L"c");
  1053. info.wvc_id = _wfopen(wvc_name, L"rb");
  1054. free(wvc_name);
  1055. }
  1056. }
  1057. info.wpc = WavpackOpenFileInputEx(&freader, &info.wv_id, info.wvc_id ? &info.wvc_id : NULL, error, open_flags, 0);
  1058. if (!info.wpc)
  1059. {
  1060. close_context(&info);
  1061. return retval;
  1062. }
  1063. lstrcpynW(info.w_lastfn, filename, MAX_PATH);
  1064. info.lastfn[0] = 0;
  1065. }
  1066. if (!_stricmp(metadata, "formatinformation"))
  1067. {
  1068. generate_format_string(info.wpc, ret, retlen, 0);
  1069. retval = 1;
  1070. }
  1071. else if (!_stricmp (metadata, "length"))
  1072. {
  1073. StringCchPrintfW(ret, retlen, L"%d", (int)(WavpackGetNumSamples(info.wpc) * 1000.0 / WavpackGetSampleRate(info.wpc)));
  1074. retval = 1;
  1075. }
  1076. else if (!_stricmp(metadata, "lossless"))
  1077. {
  1078. StringCchPrintfW(ret, retlen, L"%d", (WavpackGetMode(info.wpc) & MODE_LOSSLESS) ? 1 : 0);
  1079. retval = 1;
  1080. }
  1081. else if (!_stricmp(metadata, "gain"))
  1082. {
  1083. StringCchPrintfW(ret, retlen, L"%-+.2f dB", calculate_gain(info.wpc, false));
  1084. retval = 1;
  1085. }
  1086. else if (!_stricmp(metadata, "numsamples"))
  1087. {
  1088. StringCchPrintfW(ret, retlen, L"%d", WavpackGetNumSamples(info.wpc));
  1089. retval = 1;
  1090. }
  1091. else if (!_stricmp(metadata, "mime"))
  1092. {
  1093. lstrcpynW(ret, L"audio/x-wavpack", retlen);
  1094. retval = 1;
  1095. }
  1096. else if (WavpackGetTagItem(info.wpc, metadata, res, sizeof (res)))
  1097. {
  1098. if (!_stricmp(metadata, "rating"))
  1099. {
  1100. int rating = atoi(res);
  1101. // appears to be generally 0-5 or 0-100
  1102. if (rating > 10) {
  1103. rating /= 20;
  1104. }
  1105. // or maybe we're dealing with a 1-10 range
  1106. else if (rating > 5) {
  1107. rating /= 2;
  1108. }
  1109. // otherwise it is hopefully in the 0-5 range
  1110. StringCchPrintfW(ret, retlen, L"%u", rating);
  1111. }
  1112. else
  1113. {
  1114. if (!(WavpackGetMode(info.wpc) & MODE_APETAG))
  1115. lstrcpynW(ret, AutoWide(res), retlen);
  1116. else
  1117. lstrcpynW(ret, AutoWide(res, CP_UTF8), retlen);
  1118. }
  1119. retval = 1;
  1120. }
  1121. else if (metadata_we_can_write (metadata))
  1122. {
  1123. if (retlen)
  1124. *ret = 0;
  1125. retval = 1;
  1126. }
  1127. // This is a little ugly, but since the WavPack library has read the tags off the
  1128. // files, we can close the files (but not the WavPack context) now so that we don't
  1129. // leave handles open. We may access the file again for the "formatinformation"
  1130. // field, so we reopen the file if we get that one.
  1131. if (info.wv_id)
  1132. {
  1133. fclose (info.wv_id);
  1134. info.wv_id = NULL;
  1135. }
  1136. if (info.wvc_id)
  1137. {
  1138. fclose (info.wvc_id);
  1139. info.wvc_id = NULL;
  1140. }
  1141. return retval;
  1142. }
  1143. #endif
  1144. #ifdef ANSI_METADATA
  1145. extern "C" int __declspec (dllexport) winampSetExtendedFileInfo(const char *filename, const char *metadata, char *val)
  1146. {
  1147. char error[128];
  1148. #ifdef DEBUG_CONSOLE
  1149. sprintf (error, "winampSetExtendedFileInfo (%s=%s)\n", metadata, val);
  1150. debug_write (error);
  1151. #endif
  1152. if (!filename || !*filename || !metadata_we_can_write(metadata))
  1153. return 0;
  1154. if (!edit.wpc || strcmp(filename, edit.lastfn))
  1155. {
  1156. if (edit.wpc)
  1157. WavpackCloseFile(edit.wpc);
  1158. edit.wpc = WavpackOpenFileInput(filename, error, OPEN_TAGS | OPEN_EDIT_TAGS, 0);
  1159. if (!edit.wpc)
  1160. return 0;
  1161. lstrcpyn(edit.lastfn, filename, MAX_PATH);
  1162. edit.w_lastfn [0] = 0;
  1163. }
  1164. if (strlen(val))
  1165. return WavpackAppendTagItem(edit.wpc, metadata, val, strlen (val));
  1166. else
  1167. return WavpackDeleteTagItem(edit.wpc, metadata);
  1168. }
  1169. #endif
  1170. #ifdef UNICODE_METADATA
  1171. extern "C" int __declspec (dllexport) winampSetExtendedFileInfoW(const wchar_t *filename, const char *metadata, wchar_t *val)
  1172. {
  1173. char error[128], utf8_val[256];
  1174. lstrcpyn(utf8_val,AutoChar(val, CP_UTF8),sizeof(utf8_val) - 1);
  1175. #ifdef DEBUG_CONSOLE
  1176. sprintf (error, "winampSetExtendedFileInfoW (%s=%s)\n", metadata, utf8_val);
  1177. debug_write (error);
  1178. #endif
  1179. if (!filename || !*filename || !metadata_we_can_write(metadata))
  1180. return 0;
  1181. if (!edit.wpc || wcscmp(filename, edit.w_lastfn))
  1182. {
  1183. if (edit.wpc)
  1184. {
  1185. WavpackCloseFile(edit.wpc);
  1186. edit.wpc = NULL;
  1187. }
  1188. if (edit.wv_id)
  1189. fclose(edit.wv_id);
  1190. if (!(edit.wv_id = _wfopen(filename, L"r+b")))
  1191. return 0;
  1192. edit.wpc = WavpackOpenFileInputEx(&freader, &edit.wv_id, NULL, error, OPEN_TAGS | OPEN_EDIT_TAGS, 0);
  1193. if (!edit.wpc)
  1194. {
  1195. fclose(edit.wv_id);
  1196. return 0;
  1197. }
  1198. lstrcpynW(edit.w_lastfn, filename, MAX_PATH);
  1199. edit.lastfn [0] = 0;
  1200. }
  1201. if (strlen(utf8_val))
  1202. return WavpackAppendTagItem(edit.wpc, metadata, utf8_val, strlen (utf8_val));
  1203. else
  1204. return WavpackDeleteTagItem(edit.wpc, metadata);
  1205. }
  1206. #endif
  1207. extern "C" int __declspec (dllexport) winampWriteExtendedFileInfo(void)
  1208. {
  1209. #ifdef DEBUG_CONSOLE
  1210. debug_write ("winampWriteExtendedFileInfo ()\n");
  1211. #endif
  1212. if (edit.wpc)
  1213. {
  1214. WavpackWriteTag(edit.wpc);
  1215. WavpackCloseFile(edit.wpc);
  1216. edit.wpc = NULL;
  1217. }
  1218. if (edit.wv_id)
  1219. {
  1220. fclose(edit.wv_id);
  1221. edit.wv_id = NULL;
  1222. }
  1223. close_context(&info); // make sure we re-read info on any open files
  1224. return 1;
  1225. }
  1226. // return 1 if you want winamp to show it's own file info dialogue, 0 if you want to show your own (via In_Module.InfoBox)
  1227. // if returning 1, remember to implement winampGetExtendedFileInfo("formatinformation")!
  1228. extern "C" __declspec(dllexport) int winampUseUnifiedFileInfoDlg(const wchar_t * fn)
  1229. {
  1230. return 1;
  1231. }
  1232. static const char *writable_metadata [] = {
  1233. "track", "genre", "year", "comment", "artist",
  1234. "album", "title", "albumartist", "composer",
  1235. "publisher", "disc", "tool", "encoder", "bpm",
  1236. "category", "rating",
  1237. "replaygain_track_gain", "replaygain_track_peak",
  1238. "replaygain_album_gain", "replaygain_album_peak"
  1239. };
  1240. #define NUM_KNOWN_METADATA (sizeof (writable_metadata) / sizeof (writable_metadata [0]))
  1241. static int metadata_we_can_write(const char *metadata)
  1242. {
  1243. if (!metadata || !*metadata)
  1244. return 0;
  1245. for (int i = 0; i < NUM_KNOWN_METADATA; ++i)
  1246. if (!_stricmp(metadata, writable_metadata[i]))
  1247. return 1;
  1248. return 0;
  1249. }
  1250. static void generate_format_string(WavpackContext *wpc, wchar_t *string, int maxlen, int wide)
  1251. {
  1252. int mode = WavpackGetMode(wpc);
  1253. unsigned char md5_sum[16];
  1254. wchar_t modes[256];
  1255. wchar_t fmt[256];
  1256. WASABI_API_LNGSTRINGW_BUF(IDS_ENCODER_VERSION, fmt, sizeof(fmt));
  1257. StringCchPrintfW(string, maxlen, fmt, WavpackGetVersion(wpc));
  1258. while (*string && string++ && maxlen--);
  1259. WASABI_API_LNGSTRINGW_BUF(IDS_SOURCE, fmt, sizeof (fmt));
  1260. StringCchPrintfW(string, maxlen, fmt, WavpackGetBitsPerSample(wpc),
  1261. WASABI_API_LNGSTRINGW((WavpackGetMode(wpc) & MODE_FLOAT) ? IDS_FLOATS : IDS_INTS),
  1262. WavpackGetSampleRate(wpc));
  1263. while (*string && string++ && maxlen--);
  1264. if (WavpackGetNumChannels(wpc) > 2)
  1265. {
  1266. WASABI_API_LNGSTRINGW_BUF(IDS_MULTICHANNEL, fmt, sizeof (fmt));
  1267. StringCchPrintfW(string, maxlen, fmt, WavpackGetNumChannels(wpc));
  1268. while (*string && string++ && maxlen--);
  1269. }
  1270. else if (WavpackGetNumChannels(wpc) == 2)
  1271. {
  1272. WASABI_API_LNGSTRINGW_BUF(IDS_STEREO, fmt, sizeof (fmt));
  1273. StringCchPrintfW(string, maxlen, fmt);
  1274. while (*string && string++ && maxlen--);
  1275. }
  1276. else
  1277. {
  1278. WASABI_API_LNGSTRINGW_BUF(IDS_MONO, fmt, sizeof (fmt));
  1279. StringCchPrintfW(string, maxlen, fmt);
  1280. while (*string && string++ && maxlen--);
  1281. }
  1282. modes [0] = 0;
  1283. if (WavpackGetMode(wpc) & MODE_HYBRID)
  1284. {
  1285. StringCchCatW(modes, 256, WASABI_API_LNGSTRINGW(IDS_HYBRID));
  1286. StringCchCatW(modes, 256, L" ");
  1287. }
  1288. StringCchCatW(modes, 256, WASABI_API_LNGSTRINGW((WavpackGetMode(wpc) & MODE_LOSSLESS) ? IDS_LOSSLESS : IDS_LOSSY));
  1289. if (WavpackGetMode(wpc) & MODE_FAST)
  1290. StringCchCatW(modes, 256, WASABI_API_LNGSTRINGW(IDS_FAST));
  1291. else if (WavpackGetMode(wpc) & MODE_VERY_HIGH)
  1292. StringCchCatW(modes, 256, WASABI_API_LNGSTRINGW(IDS_VHIGH));
  1293. else if (WavpackGetMode(wpc) & MODE_HIGH)
  1294. StringCchCatW(modes, 256, WASABI_API_LNGSTRINGW(IDS_HIGH));
  1295. if (WavpackGetMode(wpc) & MODE_EXTRA)
  1296. StringCchCatW(modes, 256, WASABI_API_LNGSTRINGW(IDS_EXTRA));
  1297. StringCchPrintfW(string, maxlen, L"%s:%s %s\n",
  1298. WASABI_API_LNGSTRINGW(IDS_MODES),
  1299. (wide || lstrlenW(modes) < 24) ? L"" : L"\n", modes);
  1300. while (*string && string++ && maxlen--);
  1301. if (WavpackGetRatio(wpc) != 0.0)
  1302. {
  1303. wchar_t str_kbps[32];
  1304. StringCchPrintfW(string, maxlen, L"%s: %d %s \n",
  1305. WASABI_API_LNGSTRINGW(IDS_BITRATE),
  1306. (int)((WavpackGetAverageBitrate(wpc, TRUE) + 500.0) / 1000.0),
  1307. WASABI_API_LNGSTRINGW_BUF(IDS_KBPS, str_kbps, sizeof(str_kbps)));
  1308. while (*string && string++ && maxlen--);
  1309. StringCchPrintfW(string, maxlen, L"%s: %.2f : 1 \n",
  1310. WASABI_API_LNGSTRINGW(IDS_RATIO), 1.0 / WavpackGetRatio(wpc));
  1311. while (*string && string++ && maxlen--);
  1312. }
  1313. if (WavpackGetMD5Sum(wpc, md5_sum))
  1314. {
  1315. wchar_t md5s1 [17], md5s2 [17];
  1316. int i;
  1317. for (i = 0; i < 8; ++i)
  1318. {
  1319. StringCchPrintfW(md5s1 + i * 2, sizeof(md5s1), L"%02x", md5_sum [i]);
  1320. StringCchPrintfW(md5s2 + i * 2, sizeof(md5s2), L"%02x", md5_sum [i+8]);
  1321. }
  1322. StringCchPrintfW(string, maxlen, (wide ? L"%s: %s%s\n" : L"%s:\n %s\n %s\n"),
  1323. WASABI_API_LNGSTRINGW(IDS_MD5), md5s1, md5s2);
  1324. }
  1325. }
  1326. ///////////////////// native "C" functions required for AlbumArt support ///////////////////////////
  1327. #ifdef DEBUG_CONSOLE
  1328. static char temp_buff [256];
  1329. static char *wide2char (const wchar_t *src)
  1330. {
  1331. char *dst = temp_buff;
  1332. while (*src)
  1333. *dst++ = (char) *src++;
  1334. *dst = 0;
  1335. return temp_buff;
  1336. }
  1337. #endif
  1338. int WavPack_HandlesExtension(const wchar_t *extension)
  1339. {
  1340. return !_wcsicmp(extension, L"wv");
  1341. }
  1342. int WavPack_GetAlbumArt(const wchar_t *filename, const wchar_t *type, void **bits, size_t *len, wchar_t **mime_type)
  1343. {
  1344. char *buffer;
  1345. char error[128];
  1346. int tag_size, i;
  1347. int retval = 1;
  1348. #ifdef DEBUG_CONSOLE
  1349. sprintf (error, "WavPack_GetAlbumArt (%s)\n", wide2char (type));
  1350. debug_write (error);
  1351. #endif
  1352. if (!filename || !*filename || _wcsicmp(type, L"cover"))
  1353. return retval;
  1354. if (!info.wpc || wcscmp(filename, info.w_lastfn))
  1355. {
  1356. close_context(&info);
  1357. if (!(info.wv_id = _wfopen(filename, L"rb")))
  1358. return retval;
  1359. if (config_bits & ALLOW_WVC)
  1360. {
  1361. int length = wcslen(filename) + 10;
  1362. wchar_t *wvc_name = (wchar_t *)malloc(length * sizeof(wchar_t));
  1363. if (wvc_name)
  1364. {
  1365. lstrcpynW(wvc_name, filename, length);
  1366. StringCchCatW(wvc_name, length, L"c");
  1367. info.wvc_id = _wfopen(wvc_name, L"rb");
  1368. free(wvc_name);
  1369. }
  1370. }
  1371. info.wpc = WavpackOpenFileInputEx(&freader, &info.wv_id, info.wvc_id ? &info.wvc_id : NULL, error, OPEN_TAGS, 0);
  1372. if (!info.wpc)
  1373. {
  1374. close_context(&info);
  1375. return retval;
  1376. }
  1377. lstrcpynW(info.w_lastfn, filename, MAX_PATH);
  1378. info.lastfn[0] = 0;
  1379. }
  1380. tag_size = WavpackGetBinaryTagItem(info.wpc, "Cover Art (Front)", NULL, 0);
  1381. if (!tag_size)
  1382. return retval;
  1383. buffer = (char*)WASABI_API_MEMMGR->sysMalloc(tag_size);
  1384. WavpackGetBinaryTagItem(info.wpc, "Cover Art (Front)", buffer, tag_size);
  1385. for (i = 0; i < tag_size - 1; ++i)
  1386. if (!buffer[i] && strrchr(buffer, '.')) {
  1387. char *ext = strrchr(buffer, '.') + 1;
  1388. wchar_t *wcptr;
  1389. wcptr = *mime_type = (wchar_t*)WASABI_API_MEMMGR->sysMalloc(strlen(ext) * 2 + 2);
  1390. while (*ext)
  1391. *wcptr++ = *ext++;
  1392. *wcptr = 0;
  1393. *bits = buffer;
  1394. *len = tag_size - i - 1;
  1395. memmove(buffer, buffer + i + 1, *len);
  1396. retval = 0;
  1397. #ifdef DEBUG_CONSOLE
  1398. sprintf (error, "WavPack_GetAlbumArt (\"%s\", %d) success!\n", wide2char (*mime_type), *len);
  1399. debug_write (error);
  1400. #endif
  1401. }
  1402. if (retval)
  1403. WASABI_API_MEMMGR->sysFree(buffer);
  1404. // This is a little ugly, but since the WavPack library has read the tags off the
  1405. // files, we can close the files (but not the WavPack context) now so that we don't
  1406. // leave handles open. We may access the file again for the "formatinformation"
  1407. // field, so we reopen the file if we get that one.
  1408. if (info.wv_id)
  1409. {
  1410. fclose(info.wv_id);
  1411. info.wv_id = NULL;
  1412. }
  1413. if (info.wvc_id)
  1414. {
  1415. fclose(info.wvc_id);
  1416. info.wvc_id = NULL;
  1417. }
  1418. return retval;
  1419. }
  1420. int WavPack_SetAlbumArt(const wchar_t *filename, const wchar_t *type, void *bits, size_t len, const wchar_t *mime_type)
  1421. {
  1422. #if 1
  1423. return 2; // return 2 to indicate "read-only" cover art for now
  1424. #else
  1425. char error [128], name [50], *cp;
  1426. int tag_size, retval = 0;
  1427. unsigned char *buffer;
  1428. #ifdef DEBUG_CONSOLE
  1429. sprintf (error, "WavPack_SetAlbumArt (%s)\n", wide2char (mime_type));
  1430. debug_write (error);
  1431. #endif
  1432. if (!filename || !*filename || _wcsicmp (type, L"cover") || wcslen (mime_type) > 16)
  1433. return 1;
  1434. strcpy (name, "Cover Art (Front)");
  1435. cp = name + strlen (name);
  1436. *cp++ = '.';
  1437. while (*mime_type)
  1438. *cp++ = (char) *mime_type++;
  1439. *cp = 0;
  1440. tag_size = strlen (name) + 1 + len;
  1441. buffer = malloc (tag_size);
  1442. strcpy (buffer, name);
  1443. memcpy (buffer + strlen (buffer) + 1, bits, len);
  1444. if (!edit.wpc || wcscmp (filename, edit.w_lastfn)) {
  1445. if (edit.wpc) {
  1446. WavpackCloseFile (edit.wpc);
  1447. edit.wpc = NULL;
  1448. }
  1449. if (edit.wv_id)
  1450. fclose (edit.wv_id);
  1451. if (!(edit.wv_id = _wfopen (filename, L"r+b"))) {
  1452. free (buffer);
  1453. return 1;
  1454. }
  1455. edit.wpc = WavpackOpenFileInputEx (&freader, &edit.wv_id, NULL, error, OPEN_TAGS | OPEN_EDIT_TAGS, 0);
  1456. if (!edit.wpc) {
  1457. fclose (edit.wv_id);
  1458. free (buffer);
  1459. return 1;
  1460. }
  1461. wcscpy (edit.w_lastfn, filename);
  1462. edit.lastfn [0] = 0;
  1463. }
  1464. retval = WavpackAppendTagItem (edit.wpc, "Cover Art (Front)", buffer, tag_size);
  1465. free (buffer);
  1466. if (retval) {
  1467. winampWriteExtendedFileInfo ();
  1468. return 0;
  1469. }
  1470. else {
  1471. close_context (&edit);
  1472. return 1;
  1473. }
  1474. #endif
  1475. }
  1476. int WavPack_DeleteAlbumArt(const wchar_t *filename, const wchar_t *type)
  1477. {
  1478. #if 1
  1479. return 2; // return 2 to indicate "read-only" cover art for now
  1480. #else
  1481. char error [128];
  1482. #ifdef DEBUG_CONSOLE
  1483. sprintf (error, "WavPack_DeleteAlbumArt ()\n");
  1484. debug_write (error);
  1485. #endif
  1486. if (!filename || !*filename || _wcsicmp (type, L"cover"))
  1487. return 0;
  1488. if (!edit.wpc || wcscmp (filename, edit.w_lastfn)) {
  1489. if (edit.wpc) {
  1490. WavpackCloseFile (edit.wpc);
  1491. edit.wpc = NULL;
  1492. }
  1493. if (edit.wv_id)
  1494. fclose (edit.wv_id);
  1495. if (!(edit.wv_id = _wfopen (filename, L"r+b")))
  1496. return 1;
  1497. edit.wpc = WavpackOpenFileInputEx (&freader, &edit.wv_id, NULL, error, OPEN_TAGS | OPEN_EDIT_TAGS, 0);
  1498. if (!edit.wpc) {
  1499. fclose (edit.wv_id);
  1500. return 1;
  1501. }
  1502. wcscpy (edit.w_lastfn, filename);
  1503. edit.lastfn [0] = 0;
  1504. }
  1505. if (WavpackDeleteTagItem (edit.wpc, "Cover Art (Front)")) {
  1506. winampWriteExtendedFileInfo ();
  1507. return 0;
  1508. }
  1509. else {
  1510. close_context (&edit);
  1511. return 1;
  1512. }
  1513. #endif
  1514. }
  1515. //////////////////////////////////////////////////////////////////////////////
  1516. // This function uses the ReplayGain mode selected by the user and the info //
  1517. // stored in the specified tag to determine the gain value used to play the //
  1518. // file and whether "soft clipping" is required. Note that the gain is in //
  1519. // voltage scaling (not dB), so a value of 1.0 (not 0.0) is unity gain. //
  1520. //////////////////////////////////////////////////////////////////////////////
  1521. static float calculate_gain(WavpackContext *wpc, bool allowDefault)
  1522. {
  1523. if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false))
  1524. {
  1525. float dB = 0, peak = 1.0f;
  1526. char gain[128] = "", peakVal[128] = "";
  1527. _locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
  1528. switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_source", 0))
  1529. {
  1530. case 0: // track
  1531. if ((!WavpackGetTagItem(wpc, "replaygain_track_gain", gain, sizeof(gain)) || !gain[0])
  1532. && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
  1533. WavpackGetTagItem(wpc, "replaygain_album_gain", gain, sizeof(gain));
  1534. if ((!WavpackGetTagItem(wpc, "replaygain_track_peak", peakVal, sizeof(peakVal)) || !peakVal[0])
  1535. && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
  1536. WavpackGetTagItem(wpc, "replaygain_album_peak", peakVal, sizeof(peakVal));
  1537. break;
  1538. case 1:
  1539. if ((!WavpackGetTagItem(wpc, "replaygain_album_gain", gain, sizeof(gain)) || !gain[0])
  1540. && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
  1541. WavpackGetTagItem(wpc, "replaygain_track_gain", gain, sizeof(gain));
  1542. if ((!WavpackGetTagItem(wpc, "replaygain_album_peak", peakVal, sizeof(peakVal)) || !peakVal[0])
  1543. && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
  1544. WavpackGetTagItem(wpc, "replaygain_track_peak", peakVal, sizeof(peakVal));
  1545. break;
  1546. }
  1547. if (gain[0])
  1548. {
  1549. if (gain[0] == L'+')
  1550. dB = (float)_atof_l(&gain[1],C_locale);
  1551. else
  1552. dB = (float)_atof_l(gain,C_locale);
  1553. }
  1554. else if (allowDefault)
  1555. {
  1556. dB = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0);
  1557. return powf(10.0f, dB / 20.0f);
  1558. }
  1559. if (peakVal[0])
  1560. {
  1561. peak = (float)_atof_l(peakVal,C_locale);
  1562. }
  1563. switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_mode", 1))
  1564. {
  1565. case 0: // apply gain
  1566. return powf(10.0f, dB / 20.0f);
  1567. case 1: // apply gain, but don't clip
  1568. return min(powf(10.0f, dB / 20.0f), 1.0f / peak);
  1569. case 2: // normalize
  1570. return 1.0f / peak;
  1571. case 3: // prevent clipping
  1572. if (peak > 1.0f)
  1573. return 1.0f / peak;
  1574. else
  1575. return 1.0f;
  1576. }
  1577. }
  1578. return 1.0f; // no gain
  1579. }
  1580. // Convert a Ansi string into its Unicode UTF-8 format equivalent. The
  1581. // conversion is done in-place so the maximum length of the string buffer must
  1582. // be specified because the string may become longer or shorter. If the
  1583. // resulting string will not fit in the specified buffer size then it is
  1584. // truncated.
  1585. #ifdef OLD_INFO_DIALOG
  1586. static void AnsiToUTF8(char *string, int len)
  1587. {
  1588. int max_chars = (int) strlen(string);
  1589. wchar_t *temp = (wchar_t *) malloc((max_chars + 1) * 2);
  1590. MultiByteToWideChar(CP_ACP, 0, string, -1, temp, max_chars + 1);
  1591. lstrcpyn(string, AutoChar(temp, CP_UTF8), len);
  1592. free(temp);
  1593. }
  1594. #endif
  1595. // Convert Unicode UTF-8 string to wide format. UTF-8 string must be NULL
  1596. // terminated. Resulting wide string must be able to fit in provided space
  1597. // and will also be NULL terminated. The number of characters converted will
  1598. // be returned (not counting terminator).
  1599. static int UTF8ToWideChar(const char *pUTF8, wchar_t *pWide)
  1600. {
  1601. int trail_bytes = 0;
  1602. int chrcnt = 0;
  1603. while (*pUTF8)
  1604. {
  1605. if (*pUTF8 & 0x80)
  1606. {
  1607. if (*pUTF8 & 0x40)
  1608. {
  1609. if (trail_bytes)
  1610. {
  1611. trail_bytes = 0;
  1612. chrcnt++;
  1613. }
  1614. else
  1615. {
  1616. char temp = *pUTF8;
  1617. while (temp & 0x80)
  1618. {
  1619. trail_bytes++;
  1620. temp <<= 1;
  1621. }
  1622. pWide [chrcnt] = temp >> trail_bytes--;
  1623. }
  1624. }
  1625. else if (trail_bytes)
  1626. {
  1627. pWide [chrcnt] = (pWide [chrcnt] << 6) | (*pUTF8 & 0x3f);
  1628. if (!--trail_bytes)
  1629. chrcnt++;
  1630. }
  1631. }
  1632. else
  1633. pWide [chrcnt++] = *pUTF8;
  1634. pUTF8++;
  1635. }
  1636. pWide [chrcnt] = 0;
  1637. return chrcnt;
  1638. }
  1639. // Convert a Unicode UTF-8 format string into its Ansi equivalent. The
  1640. // conversion is done in-place so the maximum length of the string buffer must
  1641. // be specified because the string may become longer or shorter. If the
  1642. // resulting string will not fit in the specified buffer size then it is
  1643. // truncated.
  1644. static void UTF8ToAnsi(char *string, int len)
  1645. {
  1646. int max_chars = (int)strlen(string);
  1647. wchar_t *temp = (wchar_t *)malloc((max_chars + 1) * 2);
  1648. int act_chars = UTF8ToWideChar(string, temp);
  1649. while (act_chars)
  1650. {
  1651. memset (string, 0, len);
  1652. if (WideCharToMultiByte(CP_ACP, 0, temp, act_chars, string, len - 1, NULL, NULL))
  1653. break;
  1654. else
  1655. act_chars--;
  1656. }
  1657. if (!act_chars)
  1658. *string = 0;
  1659. free (temp);
  1660. }
  1661. extern "C" __declspec(dllexport) int winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param)
  1662. {
  1663. // as we're not hooking anything and have no settings we can support an on-the-fly uninstall action
  1664. return IN_PLUGIN_UNINSTALL_NOW;
  1665. }