aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Library/ml_rg/replaygain.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Library/ml_rg/replaygain.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/Plugins/Library/ml_rg/replaygain.cpp')
-rw-r--r--Src/Plugins/Library/ml_rg/replaygain.cpp370
1 files changed, 370 insertions, 0 deletions
diff --git a/Src/Plugins/Library/ml_rg/replaygain.cpp b/Src/Plugins/Library/ml_rg/replaygain.cpp
new file mode 100644
index 00000000..beda7f8b
--- /dev/null
+++ b/Src/Plugins/Library/ml_rg/replaygain.cpp
@@ -0,0 +1,370 @@
+#include "main.h"
+#include <math.h>
+#include "../ReplayGainAnalysis/gain_analysis.h"
+#include "api__ml_rg.h"
+#include <shlwapi.h>
+#include <strsafe.h>
+#include <locale.h>
+
+#pragma intrinsic(fabs)
+
+static inline float fastmax(float x, float a)
+{
+ x -= a;
+ x += (float)fabs(x);
+ x *= 0.5f;
+ x += a;
+ return (x);
+}
+
+static HMODULE rgLib = 0;
+typedef int(*INITGAINANALYSIS)(void *context, long samplefreq);
+static INITGAINANALYSIS InitGainAnalysis = 0;
+typedef int(*ANALYZESAMPLES)(void *context, const float * left_samples, const float * right_samples, size_t num_samples, int num_channels);
+static ANALYZESAMPLES AnalyzeSamples = 0;
+typedef int(*RESETSAMPLEFREQUENCY)(void *context, long samplefreq);
+static RESETSAMPLEFREQUENCY ResetSampleFrequency = 0;
+typedef float(*GETTITLEGAIN)(void *context);
+static GETTITLEGAIN GetTitleGain = 0;
+typedef float(*GETALBUMGAIN)(void *context);
+static GETALBUMGAIN GetAlbumGain = 0;
+typedef void *(* CREATERGCONTEXT)();
+static CREATERGCONTEXT CreateRGContext = 0;
+typedef void(*FREERGCONTEXT)(void *context);
+static FREERGCONTEXT FreeRGContext = 0;
+
+void LoadRG()
+{
+ if (rgLib)
+ return ;
+
+ wchar_t path[MAX_PATH] = {0};
+ PathCombineW(path, (wchar_t*)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETSHAREDDLLDIRECTORYW), L"ReplayGainAnalysis.dll");
+ rgLib = LoadLibraryW(path);
+
+ if (rgLib)
+ {
+ InitGainAnalysis = (INITGAINANALYSIS)GetProcAddress(rgLib, "WAInitGainAnalysis");
+ AnalyzeSamples = (ANALYZESAMPLES)GetProcAddress(rgLib, "WAAnalyzeSamples");
+ GetTitleGain = (GETTITLEGAIN)GetProcAddress(rgLib, "WAGetTitleGain");
+ ResetSampleFrequency = (RESETSAMPLEFREQUENCY)GetProcAddress(rgLib, "WAResetSampleFrequency");
+ GetAlbumGain = (GETALBUMGAIN)GetProcAddress(rgLib, "WAGetAlbumGain");
+ CreateRGContext = (CREATERGCONTEXT)GetProcAddress(rgLib, "WACreateRGContext");
+ FreeRGContext = (FREERGCONTEXT)GetProcAddress(rgLib, "WAFreeRGContext");
+ }
+}
+
+void *CreateRG()
+{
+ LoadRG();
+
+ return CreateRGContext();
+}
+
+void DestroyRG(void *context)
+{
+ FreeRGContext(context);
+}
+
+#define CHUNKSIZE 16384
+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)
+{
+ float data[2*CHUNKSIZE] = {0};
+ float right[CHUNKSIZE] = {0};
+ float peak = 0;
+
+ if (parameters->channels > 2)
+ {
+ char titleStr[32] = {0};
+ MessageBoxA(GetDialogBoxParent(),
+ WASABI_API_LNGSTRING(IDS_TOO_MANY_CHANNELS),
+ WASABI_API_LNGSTRING_BUF(IDS_REPLAYGAIN,titleStr,32),
+ MB_OK);
+ decodeFile->CloseAudio(decoder);
+ return ;
+ }
+ ResetSampleFrequency(context, parameters->sampleRate);
+ if (callback) callback->InformSize((parameters->sizeBytes == -1) ? 0 : parameters->sizeBytes);
+ while (1)
+ {
+ if (*killSwitch)
+ {
+ decodeFile->CloseAudio(decoder);
+ return ;
+ }
+ int error=0;
+ size_t bytesRead = decoder->ReadAudio((void *)data, sizeof(data), killSwitch, &error);
+ if (*killSwitch)
+ {
+ decodeFile->CloseAudio(decoder);
+ return ;
+ }
+ else if (error)
+ {
+ break;
+ }
+ if (callback) callback->Progress(bytesRead);
+
+ size_t samples = bytesRead / sizeof(*data);
+
+ if (!samples)
+ break;
+
+ for (size_t i = 0;i != samples;i++)
+ {
+ peak = fastmax(peak, (float)fabs(data[i]));
+ data[i] *= 32768.0f;
+ }
+ albumPeak = fastmax(peak, albumPeak);
+
+ if (parameters->channels == 1)
+ AnalyzeSamples(context, data, NULL, samples, 1);
+ else
+ {
+ size_t samples2 = samples / 2;
+ for (size_t i = 0;i != samples2;i++)
+ {
+ data[i] = data[i * 2];
+ right[i] = data[i * 2 + 1];
+ }
+ AnalyzeSamples(context, data, right, samples2, 2);
+ }
+ }
+ decodeFile->CloseAudio(decoder);
+ float gain = GetTitleGain(context);
+ if (gain != GAIN_NOT_ENOUGH_SAMPLES)
+ {
+ _locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
+ _snwprintf_l(track_gain, 64, L"%-+.2f dB", C_locale, gain);
+ _snwprintf_l(track_peak, 64, L"%-.9f", C_locale, peak);
+ }
+}
+
+static void FillFloat(float *left, float *right, void *samples, size_t bps, size_t numSamples, size_t numChannels, float &peak, float &albumPeak, float gain)
+{
+ switch (bps)
+ {
+ case 8:
+ {
+ unsigned __int8 *samples8 = (unsigned __int8 *)samples;
+ size_t t = 0;
+ for (size_t x = 0; x != numSamples; x ++)
+ {
+ left[x] = (float)(samples8[t++] - 128) * 256.0f * gain;
+
+ if (numChannels == 2)
+ {
+ right[x] = (float)(samples8[t++] - 128) * 256.0f* gain;
+ }
+ else
+ right[x] = left[x];
+ peak = fastmax(peak, (float)fabs(left[x]));
+ peak = fastmax(peak, (float)fabs(right[x]));
+ albumPeak=fastmax(albumPeak, peak);
+ }
+ }
+ break;
+ case 16:
+ {
+ short *samples16 = (short *)samples;
+ size_t t = 0;
+ if (numChannels == 1)
+ {
+ for (size_t x = 0; x != numSamples; x ++)
+ {
+ left[x] = (float)samples16[t++] * gain;
+ right[x] = left[x];
+ peak = fastmax(peak, (float)fabs(left[x]));
+ albumPeak=fastmax(albumPeak, peak);
+
+ }
+ }
+ else if (numChannels == 2)
+ {
+ for (size_t x = 0; x != numSamples; x ++)
+ {
+ left[x] = (float)samples16[t++] * gain ;
+ right[x] = (float)samples16[t++] * gain;
+
+ peak = fastmax(peak, (float)fabs(left[x]));
+ peak = fastmax(peak, (float)fabs(right[x]));
+ albumPeak=fastmax(albumPeak, peak);
+ }
+ }
+ }
+ break;
+ case 24:
+ {
+ unsigned __int8 *samples8 = (unsigned __int8 *)samples;
+ for (size_t x = 0; x != numSamples; x ++)
+ {
+ long temp = (((long)samples8[0]) << 8);
+ temp = temp | (((long)samples8[1]) << 16);
+ temp = temp | (((long)samples8[2]) << 24);
+ left[x] = (float)temp* gain / 65536.0f;
+ samples8 += 3;
+ if (numChannels == 2)
+ {
+ temp = (((long)samples8[0]) << 8);
+ temp = temp | (((long)samples8[1]) << 16);
+ temp = temp | (((long)samples8[2]) << 24);
+ right[x] = (float)temp* gain / 65536.0f;
+ samples8 += 3;
+ }
+ else
+ right[x] = left[x];
+ peak = fastmax(peak, (float)fabs(left[x]));
+ peak = fastmax(peak, (float)fabs(right[x]));
+ albumPeak=fastmax(albumPeak, peak);
+
+ }
+ }
+ break;
+ }
+
+}
+#undef CHUNKSIZE
+#define CHUNKSIZE 4096
+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)
+{
+ char data[4*2*CHUNKSIZE] = {0};
+ float left[CHUNKSIZE] = {0};
+ float right[CHUNKSIZE] = {0};
+ float peak = 0;
+ if (parameters->channels > 2)
+ {
+ char titleStr[32];
+ MessageBoxA(GetDialogBoxParent(),
+ WASABI_API_LNGSTRING(IDS_TOO_MANY_CHANNELS),
+ WASABI_API_LNGSTRING_BUF(IDS_REPLAYGAIN,titleStr,32),
+ MB_OK);
+ decodeFile->CloseAudio(decoder);
+ return ;
+ }
+
+ int padded_bits = (parameters->bitsPerSample + 7) & (~7);
+ albumPeak *= 32768.0f;
+ ResetSampleFrequency(context, parameters->sampleRate);
+ if (callback) callback->InformSize((parameters->sizeBytes == -1) ? 0 : parameters->sizeBytes);
+ while (1)
+ {
+ if (*killSwitch)
+ {
+ decodeFile->CloseAudio(decoder);
+ return ;
+ }
+ int error=0;
+ size_t bytesRead = decoder->ReadAudio((void *)data, 4096 * parameters->channels * (padded_bits / 8), killSwitch, &error);
+ if (*killSwitch)
+ {
+ decodeFile->CloseAudio(decoder);
+ return ;
+ }
+ else if (error)
+ {
+ break;
+ }
+
+ if (callback) callback->Progress(bytesRead);
+
+ size_t samples = bytesRead / (padded_bits / 8);
+
+ if (!samples)
+ break;
+
+ FillFloat(left, right, data, padded_bits, samples / parameters->channels, parameters->channels, peak, albumPeak, (float)pow(2., (double)(padded_bits - parameters->bitsPerSample)));
+
+ size_t samples2 = samples / 2;
+ AnalyzeSamples(context, left, right, samples2, 2);
+ }
+ decodeFile->CloseAudio(decoder);
+ float gain = GetTitleGain(context);
+ if (gain != GAIN_NOT_ENOUGH_SAMPLES)
+ {
+ StringCchPrintfW(track_gain, 64, L"%-+.2f dB", gain);
+ StringCchPrintfW(track_peak, 64, L"%-.9f", peak / 32768.0f);
+ }
+
+ albumPeak /= 32768.0f;
+}
+
+void CalculateRG(void *context, const wchar_t *filename, wchar_t track_gain[64], wchar_t track_peak[64], ProgressCallback *callback, int *killSwitch, float &albumPeak)
+{
+ LoadRG();
+ if (!rgLib)
+ {
+ char titleStr[32] = {0};
+ MessageBoxA(GetDialogBoxParent(),
+ WASABI_API_LNGSTRING(IDS_NOT_ABLE_TO_OPEN_RG_DLL),
+ WASABI_API_LNGSTRING_BUF(IDS_REPLAYGAIN,titleStr,32),
+ MB_OK);
+ return ;
+ }
+
+ wchar_t dummy[64] = {0};
+ if (!GetFileInfo(filename, L"replaygain_track_gain", dummy, 64)) // check if the plugin even supports replaygain
+ return ;
+
+ /*
+ TODO: want to do something like this, but have to do it on the main thread (ugh)
+ if (!_wcsnicmp(dummy, "-24601", 6))
+ {
+ SetFileInfo(itr->filename, L"replaygain_track_gain", L"");
+ SetFileInfo(itr->filename, L"replaygain_track_peak", L"");
+ SetFileInfo(itr->filename, L"replaygain_album_gain", L"");
+ SetFileInfo(itr->filename, L"replaygain_album_peak", L"");
+ WriteFileInfo();
+ }
+ */
+
+ AudioParameters parameters;
+ parameters.flags = AUDIOPARAMETERS_FLOAT | AUDIOPARAMETERS_MAXCHANNELS | AUDIOPARAMETERS_MAXSAMPLERATE;
+ parameters.channels = 2;
+ parameters.sampleRate = 192000;
+
+ ifc_audiostream *decoder = decodeFile->OpenAudioBackground(filename, &parameters);
+ if (decoder)
+ CalculateRG_float(context, decoder, &parameters, track_gain, track_peak, callback, killSwitch, albumPeak);
+ else
+ {
+ // try PCM
+ memset(&parameters, 0, sizeof(AudioParameters));
+ parameters.flags = AUDIOPARAMETERS_MAXCHANNELS | AUDIOPARAMETERS_MAXSAMPLERATE;
+ parameters.channels = 2;
+ parameters.sampleRate = 192000;
+
+ ifc_audiostream *decoder = decodeFile->OpenAudioBackground(filename, &parameters);
+ if (decoder)
+ CalculateRG_pcm(context, decoder, &parameters, track_gain, track_peak, callback, killSwitch, albumPeak);
+ }
+
+}
+
+void CalculateAlbumRG(void *context, wchar_t album_gain[64], wchar_t album_peak[64], float &albumPeak)
+{
+ float gain = GetAlbumGain(context);
+ if (gain != GAIN_NOT_ENOUGH_SAMPLES)
+ {
+ /*StringCchPrintfW(album_gain, 64, L"%-+.2f dB", gain);
+ StringCchPrintfW(album_peak, 64, L"%-.9f", albumPeak);*/
+ _locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
+ _snwprintf_l(album_gain, 64, L"%-+.2f dB", C_locale, gain);
+ _snwprintf_l(album_peak, 64, L"%-.9f", C_locale, albumPeak);
+ }
+}
+
+void StartRG(void *context)
+{
+ LoadRG();
+ if (!rgLib)
+ {
+ char titleStr[32] = {0};
+ MessageBoxA(GetDialogBoxParent(),
+ WASABI_API_LNGSTRING(IDS_NOT_ABLE_TO_OPEN_RG_DLL),
+ WASABI_API_LNGSTRING_BUF(IDS_REPLAYGAIN,titleStr,32),
+ MB_OK);
+ return ;
+ }
+
+ InitGainAnalysis(context, 44100); // since this is most common. We'll reset it before doing a real calculation anyway
+}