winamp/Src/Plugins/Library/ml_rg/ml_rg.cpp
2024-09-24 14:54:57 +02:00

409 lines
12 KiB
C++

#include "Main.h"
#include "../nu/AutoWideFn.h"
#include "api__ml_rg.h"
//#include <api/service/waservicefactory.h>
#include "RGFactory.h"
#include "../playlist/ifc_playlistloadercallback.h"
#include <strsafe.h>
// wasabi based services for localisation support
api_language *WASABI_API_LNG = 0;
HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
int uninstalling = 0;
RGFactory rgFactory;
static DWORD ml_rg_config_ipc;
static BOOL ml_rg_open_prefs;
template <class api_T>
void ServiceBuild(api_T *&api_t, GUID factoryGUID_t)
{
if (plugin.service)
{
waServiceFactory *factory = plugin.service->service_getServiceByGuid(factoryGUID_t);
if (factory)
api_t = reinterpret_cast<api_T *>( factory->getInterface() );
}
}
template <class api_T>
void ServiceRelease(api_T *api_t, GUID factoryGUID_t)
{
if (plugin.service && api_t)
{
waServiceFactory *factory = plugin.service->service_getServiceByGuid(factoryGUID_t);
if (factory)
factory->releaseInterface(api_t);
}
api_t = NULL;
}
static int Init();
static void Quit();
static INT_PTR PluginMessageProc(int message_type, INT_PTR param1, INT_PTR param2, INT_PTR param3);
#define PLUG_VER L"1.29"
extern "C" winampMediaLibraryPlugin plugin =
{
MLHDR_VER,
"nullsoft(ml_rg.dll)",
Init,
Quit,
PluginMessageProc,
0,
0,
0,
};
api_decodefile *AGAVE_API_DECODE = 0;
api_application *WASABI_API_APP = 0;
api_playlistmanager *AGAVE_API_PLAYLISTMANAGER = 0;
api_stats *AGAVE_API_STATS = 0;
char *iniFile = 0;
int Init()
{
waServiceFactory *sf = 0;
ServiceBuild( AGAVE_API_PLAYLISTMANAGER, api_playlistmanagerGUID );
ServiceBuild( AGAVE_API_DECODE, decodeFileGUID );
ServiceBuild( WASABI_API_APP, applicationApiServiceGuid );
ServiceBuild( AGAVE_API_STATS, AnonymousStatsGUID );
plugin.service->service_register( &rgFactory );
// loader so that we can get the localisation service api for use
ServiceBuild( WASABI_API_LNG, languageApiGUID );
// need to have this initialised before we try to do anything with localisation features
WASABI_API_START_LANG( plugin.hDllInstance, MlReplayGainLangGUID );
static wchar_t szDescription[ 256 ];
StringCchPrintfW( szDescription, ARRAYSIZE( szDescription ), WASABI_API_LNGSTRINGW( IDS_NULLSOFT_REPLAY_GAIN_ANALYZER ), PLUG_VER );
plugin.description = (char *)szDescription;
ml_rg_config_ipc = (DWORD)SendMessageA( plugin.hwndWinampParent, WM_WA_IPC, ( WPARAM ) & "ml_rg_config", IPC_REGISTER_WINAMP_IPCMESSAGE );
iniFile = (char *)SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETINIFILE );
config_ask = GetPrivateProfileIntA( "ml_rg", "config_ask", config_ask, iniFile );
config_ask_each_album = GetPrivateProfileIntA( "ml_rg", "config_ask_each_album", config_ask_each_album, iniFile );
return ML_INIT_SUCCESS;
}
void Quit()
{
ServiceRelease(decodeFile, decodeFileGUID);
ServiceRelease(WASABI_API_APP, applicationApiServiceGuid);
ServiceRelease(WASABI_API_LNG, languageApiGUID);
ServiceRelease(AGAVE_API_PLAYLISTMANAGER, api_playlistmanagerGUID);
ServiceRelease(AGAVE_API_STATS, AnonymousStatsGUID);
plugin.service->service_deregister(&rgFactory);
}
void WorkQueue::Add(const wchar_t *filename)
{
wchar_t album[512] = L"";
// if the user wants to ignore tracks already scanned, check and skip appropriately
if (config_ignore_gained_album && GetFileInfo(filename, L"replaygain_album_gain", album, 256) && album[0] != 0)
return;
GetFileInfo(filename, L"album", album, 256);
if (album[0])
{
RGWorkFile workFile;
lstrcpynW(workFile.filename, filename, MAX_PATH);
albums[album].push_back(workFile);
}
else
{
RGWorkFile workFile;
lstrcpynW(workFile.filename, filename, MAX_PATH);
unclassified.push_back(workFile);
}
totalFiles++;
}
void WriteAlbum(WorkQueue::RGWorkQueue &workQueue)
{
for (WorkQueue::RGWorkQueue::iterator itr = workQueue.begin();itr != workQueue.end();itr++)
{
if (itr->track_gain[0])
SetFileInfo(itr->filename, L"replaygain_track_gain", itr->track_gain);
if (itr->track_peak[0])
SetFileInfo(itr->filename, L"replaygain_track_peak", itr->track_peak);
if (itr->album_gain[0])
SetFileInfo(itr->filename, L"replaygain_album_gain", itr->album_gain);
if (itr->album_peak[0])
SetFileInfo(itr->filename, L"replaygain_album_peak", itr->album_peak);
WriteFileInfo();
if (AGAVE_API_STATS)
AGAVE_API_STATS->IncrementStat(api_stats::REPLAYGAIN_COUNT);
TagUpdated(itr->filename);
}
}
void WriteTracks(WorkQueue::RGWorkQueue &workQueue)
{
for (WorkQueue::RGWorkQueue::iterator itr = workQueue.begin();itr != workQueue.end();itr++)
{
if (itr->track_gain[0])
SetFileInfo(itr->filename, L"replaygain_track_gain", itr->track_gain);
if (itr->track_peak[0])
SetFileInfo(itr->filename, L"replaygain_track_peak", itr->track_peak);
WriteFileInfo();
if (AGAVE_API_STATS)
AGAVE_API_STATS->IncrementStat(api_stats::REPLAYGAIN_COUNT);
TagUpdated(itr->filename);
}
}
void CopyAlbumData(WorkQueue::RGWorkQueue &workQueue, const wchar_t *album_gain, const wchar_t *album_peak)
{
for (WorkQueue::RGWorkQueue::iterator itr = workQueue.begin();itr != workQueue.end();itr++)
{
if (itr->track_gain && itr->track_gain[0]) // if there's no track gain, it's because there was an error!
{
if (album_gain && album_gain[0])
lstrcpynW(itr->album_gain, album_gain, 64);
if (album_peak && album_peak[0])
lstrcpynW(itr->album_peak, album_peak, 64);
}
}
}
void WorkQueue::Calculate(ProgressCallback *callback, int *killSwitch)
{
void *context = CreateRG();
StartRG(context);
float albumPeak = 0;
for (RGWorkQueue::iterator itr = unclassified.begin();itr != unclassified.end();itr++)
{
if (*killSwitch) {DestroyRG(context); return ;}
CalculateRG(context, itr->filename, itr->track_gain, itr->track_peak, callback, killSwitch, albumPeak);
callback->FileFinished();
}
if (*killSwitch) {DestroyRG(context); return ;}
callback->AlbumFinished(&unclassified);
for (AlbumMap::iterator mapItr = albums.begin();mapItr != albums.end();mapItr++)
{
albumPeak = 0;
StartRG(context);
for (RGWorkQueue::iterator itr = mapItr->second.begin();itr != mapItr->second.end();itr++)
{
if (*killSwitch) {DestroyRG(context); return ;}
CalculateRG(context, itr->filename, itr->track_gain, itr->track_peak, callback, killSwitch, albumPeak);
callback->FileFinished();
}
wchar_t album_gain[64] = L"", album_peak[64] = L"";
CalculateAlbumRG(context, album_gain, album_peak, albumPeak);
CopyAlbumData(mapItr->second, album_gain, album_peak);
if (*killSwitch) {DestroyRG(context); return ;}
callback->AlbumFinished(&(mapItr->second));
}
DestroyRG(context);
}
class WorkQueuePlaylistLoader : public ifc_playlistloadercallback
{
public:
WorkQueuePlaylistLoader(WorkQueue *_queue);
void OnFile(const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info);
protected:
WorkQueue *queue;
RECVS_DISPATCH;
};
WorkQueuePlaylistLoader::WorkQueuePlaylistLoader(WorkQueue *_queue)
{
queue = _queue;
}
void WorkQueuePlaylistLoader::OnFile(const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info)
{
queue->Add(filename);
}
#define CBCLASS WorkQueuePlaylistLoader
START_DISPATCH;
VCB(IFC_PLAYLISTLOADERCALLBACK_ONFILE, OnFile)
END_DISPATCH;
#undef CBCLASS
INT_PTR PluginMessageProc(int message_type, INT_PTR param1, INT_PTR param2, INT_PTR param3)
{
if (message_type == ML_MSG_ONSENDTOBUILD)
{
if (param1 == ML_TYPE_ITEMRECORDLISTW || param1 == ML_TYPE_ITEMRECORDLIST ||
param1 == ML_TYPE_FILENAMES || param1 == ML_TYPE_STREAMNAMES ||
param1 == ML_TYPE_FILENAMESW || param1 == ML_TYPE_STREAMNAMESW ||
(AGAVE_API_PLAYLISTMANAGER && (param1 == ML_TYPE_PLAYLIST || param1 == ML_TYPE_PLAYLISTS)))
{
wchar_t description[512] = {0};
WASABI_API_LNGSTRINGW_BUF(IDS_CALCULATE_REPLAY_GAIN, description, 512);
mlAddToSendToStructW s = {0};
s.context = param2;
s.desc = description;
s.user32 = (INT_PTR)PluginMessageProc;
SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&s, ML_IPC_ADDTOSENDTOW);
}
}
else if (message_type == ML_MSG_ONSENDTOSELECT)
{
if (param3 != (INT_PTR)PluginMessageProc) return 0;
INT_PTR type = param1;
INT_PTR data = param2;
if (data)
{
WorkQueue workQueue;
if (type == ML_TYPE_ITEMRECORDLIST)
{
itemRecordList *p = (itemRecordList*)data;
for (int x = 0; x < p->Size; x ++)
{
workQueue.Add(AutoWideFn(p->Items[x].filename));
}
DoProgress(workQueue);
return 1;
}
else if (type == ML_TYPE_ITEMRECORDLISTW)
{
itemRecordListW *p = (itemRecordListW*)data;
for (int x = 0; x < p->Size; x ++)
{
workQueue.Add(p->Items[x].filename);
}
DoProgress(workQueue);
return 1;
}
else if (type == ML_TYPE_FILENAMES || type == ML_TYPE_STREAMNAMES)
{
char *p = (char*)data;
while (p && *p)
{
workQueue.Add(AutoWideFn(p));
p += strlen(p) + 1;
}
DoProgress(workQueue);
return 1;
}
else if (type == ML_TYPE_FILENAMESW || type == ML_TYPE_STREAMNAMESW)
{
wchar_t *p = (wchar_t*)data;
while (p && *p)
{
workQueue.Add(p);
p += wcslen(p) + 1;
}
DoProgress(workQueue);
return 1;
}
else if (type == ML_TYPE_PLAYLIST)
{
mlPlaylist *playlist = (mlPlaylist *)param2;
WorkQueuePlaylistLoader loader(&workQueue);
AGAVE_API_PLAYLISTMANAGER->Load(playlist->filename, &loader);
DoProgress(workQueue);
return 1;
}
else if (type == ML_TYPE_PLAYLISTS)
{
mlPlaylist **playlists = (mlPlaylist **)param2;
WorkQueuePlaylistLoader loader(&workQueue);
while (playlists && *playlists)
{
mlPlaylist *playlist = *playlists;
AGAVE_API_PLAYLISTMANAGER->Load(playlist->filename, &loader);
playlists++;
}
DoProgress(workQueue);
return 1;
}
}
}
else if (message_type == ML_MSG_CONFIG)
{
ml_rg_open_prefs = TRUE;
SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 42, IPC_OPENPREFSTOPAGE);
ml_rg_open_prefs = FALSE;
return 1;
}
// this will be sent if we've opened the playback->replay gain prefs page
// so that we can enable the embedded controls and return the RGConfig proc
else if (message_type == ml_rg_config_ipc)
{
// sanity check by winamp.exe to make sure that we're valid
if(!param1)
{
return 1;
}
// return the config dialog proceedure
else if(param1 == 1)
{
return (INT_PTR)RGConfig;
}
// queried when the playback prefs page is opened to see if we [ml_rg] caused it
else if(param1 == 2)
{
if(ml_rg_open_prefs)
{
return TRUE;
}
}
}
return 0;
}
extern "C" {
__declspec(dllexport) winampMediaLibraryPlugin *winampGetMediaLibraryPlugin()
{
return &plugin;
}
__declspec( dllexport ) int winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param) {
uninstalling = 1;
// prompt to remove our settings with default as no (just incase)
if(MessageBoxW(hwndDlg,WASABI_API_LNGSTRINGW(IDS_DO_YOU_ALSO_WANT_TO_REMOVE_SETTINGS),
(wchar_t*)plugin.description,MB_YESNO|MB_DEFBUTTON2) == IDYES)
{
WritePrivateProfileStringA("ml_rg", 0, 0, iniFile);
}
// also attempt to remove the ReplayGainAnalysis.dll so everything is kept cleaner
wchar_t path[MAX_PATH] = {0};
PathCombineW(path, (wchar_t*)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETSHAREDDLLDIRECTORYW), L"ReplayGainAnalysis.dll");
// if we get a handle then try to lower the handle count so we can delete
HINSTANCE rgLib = GetModuleHandleW(path);
if(rgLib) {
FreeLibrary(rgLib);
}
DeleteFileW(path);
// allow an on-the-fly removal (since we've got to be with a compatible client build)
return ML_PLUGIN_UNINSTALL_NOW;
}
};