371 lines
10 KiB
C++
371 lines
10 KiB
C++
#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
|
|
}
|