replaygain.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. #include "main.h"
  2. #include <math.h>
  3. #include "../ReplayGainAnalysis/gain_analysis.h"
  4. #include "api__ml_rg.h"
  5. #include <shlwapi.h>
  6. #include <strsafe.h>
  7. #include <locale.h>
  8. #pragma intrinsic(fabs)
  9. static inline float fastmax(float x, float a)
  10. {
  11. x -= a;
  12. x += (float)fabs(x);
  13. x *= 0.5f;
  14. x += a;
  15. return (x);
  16. }
  17. static HMODULE rgLib = 0;
  18. typedef int(*INITGAINANALYSIS)(void *context, long samplefreq);
  19. static INITGAINANALYSIS InitGainAnalysis = 0;
  20. typedef int(*ANALYZESAMPLES)(void *context, const float * left_samples, const float * right_samples, size_t num_samples, int num_channels);
  21. static ANALYZESAMPLES AnalyzeSamples = 0;
  22. typedef int(*RESETSAMPLEFREQUENCY)(void *context, long samplefreq);
  23. static RESETSAMPLEFREQUENCY ResetSampleFrequency = 0;
  24. typedef float(*GETTITLEGAIN)(void *context);
  25. static GETTITLEGAIN GetTitleGain = 0;
  26. typedef float(*GETALBUMGAIN)(void *context);
  27. static GETALBUMGAIN GetAlbumGain = 0;
  28. typedef void *(* CREATERGCONTEXT)();
  29. static CREATERGCONTEXT CreateRGContext = 0;
  30. typedef void(*FREERGCONTEXT)(void *context);
  31. static FREERGCONTEXT FreeRGContext = 0;
  32. void LoadRG()
  33. {
  34. if (rgLib)
  35. return ;
  36. wchar_t path[MAX_PATH] = {0};
  37. PathCombineW(path, (wchar_t*)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETSHAREDDLLDIRECTORYW), L"ReplayGainAnalysis.dll");
  38. rgLib = LoadLibraryW(path);
  39. if (rgLib)
  40. {
  41. InitGainAnalysis = (INITGAINANALYSIS)GetProcAddress(rgLib, "WAInitGainAnalysis");
  42. AnalyzeSamples = (ANALYZESAMPLES)GetProcAddress(rgLib, "WAAnalyzeSamples");
  43. GetTitleGain = (GETTITLEGAIN)GetProcAddress(rgLib, "WAGetTitleGain");
  44. ResetSampleFrequency = (RESETSAMPLEFREQUENCY)GetProcAddress(rgLib, "WAResetSampleFrequency");
  45. GetAlbumGain = (GETALBUMGAIN)GetProcAddress(rgLib, "WAGetAlbumGain");
  46. CreateRGContext = (CREATERGCONTEXT)GetProcAddress(rgLib, "WACreateRGContext");
  47. FreeRGContext = (FREERGCONTEXT)GetProcAddress(rgLib, "WAFreeRGContext");
  48. }
  49. }
  50. void *CreateRG()
  51. {
  52. LoadRG();
  53. return CreateRGContext();
  54. }
  55. void DestroyRG(void *context)
  56. {
  57. FreeRGContext(context);
  58. }
  59. #define CHUNKSIZE 16384
  60. static void CalculateRG_float(void *context, ifc_audiostream *decoder, AudioParameters *parameters, wchar_t track_gain[64], wchar_t track_peak[64], ProgressCallback *callback, int *killSwitch, float &albumPeak)
  61. {
  62. float data[2*CHUNKSIZE] = {0};
  63. float right[CHUNKSIZE] = {0};
  64. float peak = 0;
  65. if (parameters->channels > 2)
  66. {
  67. char titleStr[32] = {0};
  68. MessageBoxA(GetDialogBoxParent(),
  69. WASABI_API_LNGSTRING(IDS_TOO_MANY_CHANNELS),
  70. WASABI_API_LNGSTRING_BUF(IDS_REPLAYGAIN,titleStr,32),
  71. MB_OK);
  72. decodeFile->CloseAudio(decoder);
  73. return ;
  74. }
  75. ResetSampleFrequency(context, parameters->sampleRate);
  76. if (callback) callback->InformSize((parameters->sizeBytes == -1) ? 0 : parameters->sizeBytes);
  77. while (1)
  78. {
  79. if (*killSwitch)
  80. {
  81. decodeFile->CloseAudio(decoder);
  82. return ;
  83. }
  84. int error=0;
  85. size_t bytesRead = decoder->ReadAudio((void *)data, sizeof(data), killSwitch, &error);
  86. if (*killSwitch)
  87. {
  88. decodeFile->CloseAudio(decoder);
  89. return ;
  90. }
  91. else if (error)
  92. {
  93. break;
  94. }
  95. if (callback) callback->Progress(bytesRead);
  96. size_t samples = bytesRead / sizeof(*data);
  97. if (!samples)
  98. break;
  99. for (size_t i = 0;i != samples;i++)
  100. {
  101. peak = fastmax(peak, (float)fabs(data[i]));
  102. data[i] *= 32768.0f;
  103. }
  104. albumPeak = fastmax(peak, albumPeak);
  105. if (parameters->channels == 1)
  106. AnalyzeSamples(context, data, NULL, samples, 1);
  107. else
  108. {
  109. size_t samples2 = samples / 2;
  110. for (size_t i = 0;i != samples2;i++)
  111. {
  112. data[i] = data[i * 2];
  113. right[i] = data[i * 2 + 1];
  114. }
  115. AnalyzeSamples(context, data, right, samples2, 2);
  116. }
  117. }
  118. decodeFile->CloseAudio(decoder);
  119. float gain = GetTitleGain(context);
  120. if (gain != GAIN_NOT_ENOUGH_SAMPLES)
  121. {
  122. _locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
  123. _snwprintf_l(track_gain, 64, L"%-+.2f dB", C_locale, gain);
  124. _snwprintf_l(track_peak, 64, L"%-.9f", C_locale, peak);
  125. }
  126. }
  127. static void FillFloat(float *left, float *right, void *samples, size_t bps, size_t numSamples, size_t numChannels, float &peak, float &albumPeak, float gain)
  128. {
  129. switch (bps)
  130. {
  131. case 8:
  132. {
  133. unsigned __int8 *samples8 = (unsigned __int8 *)samples;
  134. size_t t = 0;
  135. for (size_t x = 0; x != numSamples; x ++)
  136. {
  137. left[x] = (float)(samples8[t++] - 128) * 256.0f * gain;
  138. if (numChannels == 2)
  139. {
  140. right[x] = (float)(samples8[t++] - 128) * 256.0f* gain;
  141. }
  142. else
  143. right[x] = left[x];
  144. peak = fastmax(peak, (float)fabs(left[x]));
  145. peak = fastmax(peak, (float)fabs(right[x]));
  146. albumPeak=fastmax(albumPeak, peak);
  147. }
  148. }
  149. break;
  150. case 16:
  151. {
  152. short *samples16 = (short *)samples;
  153. size_t t = 0;
  154. if (numChannels == 1)
  155. {
  156. for (size_t x = 0; x != numSamples; x ++)
  157. {
  158. left[x] = (float)samples16[t++] * gain;
  159. right[x] = left[x];
  160. peak = fastmax(peak, (float)fabs(left[x]));
  161. albumPeak=fastmax(albumPeak, peak);
  162. }
  163. }
  164. else if (numChannels == 2)
  165. {
  166. for (size_t x = 0; x != numSamples; x ++)
  167. {
  168. left[x] = (float)samples16[t++] * gain ;
  169. right[x] = (float)samples16[t++] * gain;
  170. peak = fastmax(peak, (float)fabs(left[x]));
  171. peak = fastmax(peak, (float)fabs(right[x]));
  172. albumPeak=fastmax(albumPeak, peak);
  173. }
  174. }
  175. }
  176. break;
  177. case 24:
  178. {
  179. unsigned __int8 *samples8 = (unsigned __int8 *)samples;
  180. for (size_t x = 0; x != numSamples; x ++)
  181. {
  182. long temp = (((long)samples8[0]) << 8);
  183. temp = temp | (((long)samples8[1]) << 16);
  184. temp = temp | (((long)samples8[2]) << 24);
  185. left[x] = (float)temp* gain / 65536.0f;
  186. samples8 += 3;
  187. if (numChannels == 2)
  188. {
  189. temp = (((long)samples8[0]) << 8);
  190. temp = temp | (((long)samples8[1]) << 16);
  191. temp = temp | (((long)samples8[2]) << 24);
  192. right[x] = (float)temp* gain / 65536.0f;
  193. samples8 += 3;
  194. }
  195. else
  196. right[x] = left[x];
  197. peak = fastmax(peak, (float)fabs(left[x]));
  198. peak = fastmax(peak, (float)fabs(right[x]));
  199. albumPeak=fastmax(albumPeak, peak);
  200. }
  201. }
  202. break;
  203. }
  204. }
  205. #undef CHUNKSIZE
  206. #define CHUNKSIZE 4096
  207. static void CalculateRG_pcm(void *context, ifc_audiostream *decoder, AudioParameters *parameters, wchar_t track_gain[64], wchar_t track_peak[64], ProgressCallback *callback, int *killSwitch, float &albumPeak)
  208. {
  209. char data[4*2*CHUNKSIZE] = {0};
  210. float left[CHUNKSIZE] = {0};
  211. float right[CHUNKSIZE] = {0};
  212. float peak = 0;
  213. if (parameters->channels > 2)
  214. {
  215. char titleStr[32];
  216. MessageBoxA(GetDialogBoxParent(),
  217. WASABI_API_LNGSTRING(IDS_TOO_MANY_CHANNELS),
  218. WASABI_API_LNGSTRING_BUF(IDS_REPLAYGAIN,titleStr,32),
  219. MB_OK);
  220. decodeFile->CloseAudio(decoder);
  221. return ;
  222. }
  223. int padded_bits = (parameters->bitsPerSample + 7) & (~7);
  224. albumPeak *= 32768.0f;
  225. ResetSampleFrequency(context, parameters->sampleRate);
  226. if (callback) callback->InformSize((parameters->sizeBytes == -1) ? 0 : parameters->sizeBytes);
  227. while (1)
  228. {
  229. if (*killSwitch)
  230. {
  231. decodeFile->CloseAudio(decoder);
  232. return ;
  233. }
  234. int error=0;
  235. size_t bytesRead = decoder->ReadAudio((void *)data, 4096 * parameters->channels * (padded_bits / 8), killSwitch, &error);
  236. if (*killSwitch)
  237. {
  238. decodeFile->CloseAudio(decoder);
  239. return ;
  240. }
  241. else if (error)
  242. {
  243. break;
  244. }
  245. if (callback) callback->Progress(bytesRead);
  246. size_t samples = bytesRead / (padded_bits / 8);
  247. if (!samples)
  248. break;
  249. FillFloat(left, right, data, padded_bits, samples / parameters->channels, parameters->channels, peak, albumPeak, (float)pow(2., (double)(padded_bits - parameters->bitsPerSample)));
  250. size_t samples2 = samples / 2;
  251. AnalyzeSamples(context, left, right, samples2, 2);
  252. }
  253. decodeFile->CloseAudio(decoder);
  254. float gain = GetTitleGain(context);
  255. if (gain != GAIN_NOT_ENOUGH_SAMPLES)
  256. {
  257. StringCchPrintfW(track_gain, 64, L"%-+.2f dB", gain);
  258. StringCchPrintfW(track_peak, 64, L"%-.9f", peak / 32768.0f);
  259. }
  260. albumPeak /= 32768.0f;
  261. }
  262. void CalculateRG(void *context, const wchar_t *filename, wchar_t track_gain[64], wchar_t track_peak[64], ProgressCallback *callback, int *killSwitch, float &albumPeak)
  263. {
  264. LoadRG();
  265. if (!rgLib)
  266. {
  267. char titleStr[32] = {0};
  268. MessageBoxA(GetDialogBoxParent(),
  269. WASABI_API_LNGSTRING(IDS_NOT_ABLE_TO_OPEN_RG_DLL),
  270. WASABI_API_LNGSTRING_BUF(IDS_REPLAYGAIN,titleStr,32),
  271. MB_OK);
  272. return ;
  273. }
  274. wchar_t dummy[64] = {0};
  275. if (!GetFileInfo(filename, L"replaygain_track_gain", dummy, 64)) // check if the plugin even supports replaygain
  276. return ;
  277. /*
  278. TODO: want to do something like this, but have to do it on the main thread (ugh)
  279. if (!_wcsnicmp(dummy, "-24601", 6))
  280. {
  281. SetFileInfo(itr->filename, L"replaygain_track_gain", L"");
  282. SetFileInfo(itr->filename, L"replaygain_track_peak", L"");
  283. SetFileInfo(itr->filename, L"replaygain_album_gain", L"");
  284. SetFileInfo(itr->filename, L"replaygain_album_peak", L"");
  285. WriteFileInfo();
  286. }
  287. */
  288. AudioParameters parameters;
  289. parameters.flags = AUDIOPARAMETERS_FLOAT | AUDIOPARAMETERS_MAXCHANNELS | AUDIOPARAMETERS_MAXSAMPLERATE;
  290. parameters.channels = 2;
  291. parameters.sampleRate = 192000;
  292. ifc_audiostream *decoder = decodeFile->OpenAudioBackground(filename, &parameters);
  293. if (decoder)
  294. CalculateRG_float(context, decoder, &parameters, track_gain, track_peak, callback, killSwitch, albumPeak);
  295. else
  296. {
  297. // try PCM
  298. memset(&parameters, 0, sizeof(AudioParameters));
  299. parameters.flags = AUDIOPARAMETERS_MAXCHANNELS | AUDIOPARAMETERS_MAXSAMPLERATE;
  300. parameters.channels = 2;
  301. parameters.sampleRate = 192000;
  302. ifc_audiostream *decoder = decodeFile->OpenAudioBackground(filename, &parameters);
  303. if (decoder)
  304. CalculateRG_pcm(context, decoder, &parameters, track_gain, track_peak, callback, killSwitch, albumPeak);
  305. }
  306. }
  307. void CalculateAlbumRG(void *context, wchar_t album_gain[64], wchar_t album_peak[64], float &albumPeak)
  308. {
  309. float gain = GetAlbumGain(context);
  310. if (gain != GAIN_NOT_ENOUGH_SAMPLES)
  311. {
  312. /*StringCchPrintfW(album_gain, 64, L"%-+.2f dB", gain);
  313. StringCchPrintfW(album_peak, 64, L"%-.9f", albumPeak);*/
  314. _locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
  315. _snwprintf_l(album_gain, 64, L"%-+.2f dB", C_locale, gain);
  316. _snwprintf_l(album_peak, 64, L"%-.9f", C_locale, albumPeak);
  317. }
  318. }
  319. void StartRG(void *context)
  320. {
  321. LoadRG();
  322. if (!rgLib)
  323. {
  324. char titleStr[32] = {0};
  325. MessageBoxA(GetDialogBoxParent(),
  326. WASABI_API_LNGSTRING(IDS_NOT_ABLE_TO_OPEN_RG_DLL),
  327. WASABI_API_LNGSTRING_BUF(IDS_REPLAYGAIN,titleStr,32),
  328. MB_OK);
  329. return ;
  330. }
  331. InitGainAnalysis(context, 44100); // since this is most common. We'll reset it before doing a real calculation anyway
  332. }