diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Library/ml_rg/replaygain.cpp | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-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.cpp | 370 |
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, ¶meters); + if (decoder) + CalculateRG_float(context, decoder, ¶meters, track_gain, track_peak, callback, killSwitch, albumPeak); + else + { + // try PCM + memset(¶meters, 0, sizeof(AudioParameters)); + parameters.flags = AUDIOPARAMETERS_MAXCHANNELS | AUDIOPARAMETERS_MAXSAMPLERATE; + parameters.channels = 2; + parameters.sampleRate = 192000; + + ifc_audiostream *decoder = decodeFile->OpenAudioBackground(filename, ¶meters); + if (decoder) + CalculateRG_pcm(context, decoder, ¶meters, 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 +} |