diff options
Diffstat (limited to 'Src/Plugins/Library/ml_rg')
-rw-r--r-- | Src/Plugins/Library/ml_rg/Process.cpp | 53 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_rg/Process.h | 25 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_rg/Progress.cpp | 261 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_rg/RGFactory.cpp | 67 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_rg/RGFactory.h | 24 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_rg/Results.cpp | 172 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_rg/api__ml_rg.h | 24 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_rg/config.cpp | 52 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_rg/main.h | 146 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_rg/metadata.cpp | 28 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_rg/ml_rg.cpp | 409 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_rg/ml_rg.rc | 152 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_rg/ml_rg.sln | 57 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_rg/ml_rg.vcxproj | 310 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_rg/ml_rg.vcxproj.filters | 68 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_rg/obj_replaygain.h | 62 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_rg/replaygain.cpp | 370 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_rg/resource.h | 40 | ||||
-rw-r--r-- | Src/Plugins/Library/ml_rg/version.rc2 | 39 |
19 files changed, 2359 insertions, 0 deletions
diff --git a/Src/Plugins/Library/ml_rg/Process.cpp b/Src/Plugins/Library/ml_rg/Process.cpp new file mode 100644 index 00000000..21cc97e7 --- /dev/null +++ b/Src/Plugins/Library/ml_rg/Process.cpp @@ -0,0 +1,53 @@ +#include "main.h" +#include "Process.h" + +int ProcessReplayGain::Open(int _mode) +{ + mode=_mode; + if (mode != RG_INDIVIDUAL_TRACKS + && mode != RG_ALBUM) + return RG_MODE_NOT_SUPPORTED; + context=CreateRG(); + if (!context) + return RG_FAILURE; + + StartRG(context); + return RG_SUCCESS; +} + +int ProcessReplayGain::ProcessTrack(const wchar_t *filename) +{ + int killSwitch=0; + RGWorkFile workFile(filename); + + CalculateRG(context, workFile.filename, workFile.track_gain, workFile.track_peak, 0, &killSwitch, albumPeak); + queue.push_back(workFile); + + return RG_SUCCESS; +} + +int ProcessReplayGain::Write() +{ + if (mode == RG_ALBUM) + { + wchar_t album_gain[64]=L"", album_peak[64]=L""; + CalculateAlbumRG(context, album_gain, album_peak, albumPeak); + CopyAlbumData(queue, album_gain, album_peak); + } + WriteAlbum(queue); + + return RG_SUCCESS; +} + +void ProcessReplayGain::Close() +{ + DestroyRG(context); +} + +#define CBCLASS ProcessReplayGain +START_DISPATCH; +CB(OBJ_REPLAYGAIN_OPEN, Open) +CB(OBJ_REPLAYGAIN_PROCESSTRACK, ProcessTrack) +CB(OBJ_REPLAYGAIN_WRITE, Write) +VCB(OBJ_REPLAYGAIN_CLOSE,Close) +END_DISPATCH; diff --git a/Src/Plugins/Library/ml_rg/Process.h b/Src/Plugins/Library/ml_rg/Process.h new file mode 100644 index 00000000..e3f1d195 --- /dev/null +++ b/Src/Plugins/Library/ml_rg/Process.h @@ -0,0 +1,25 @@ +#ifndef NULLSOFT_ML_RG_PROCESS_H +#define NULLSOFT_ML_RG_PROCESS_H + +#include "obj_replaygain.h" + +//this class is meant for use as a service + +class ProcessReplayGain : public obj_replaygain +{ +public: + ProcessReplayGain() : context(0), albumPeak(0), mode(RG_INDIVIDUAL_TRACKS) {} + int Open(int mode); + int ProcessTrack(const wchar_t *filename); + int Write(); + void Close(); + +protected: + RECVS_DISPATCH; + void *context; + int mode; + float albumPeak; + WorkQueue::RGWorkQueue queue; +}; + +#endif
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_rg/Progress.cpp b/Src/Plugins/Library/ml_rg/Progress.cpp new file mode 100644 index 00000000..18c26031 --- /dev/null +++ b/Src/Plugins/Library/ml_rg/Progress.cpp @@ -0,0 +1,261 @@ +#include "main.h" +#include "resource.h" +#include"api__ml_rg.h" +#include <strsafe.h> + +struct Progress +{ + Progress() + { + processedFiles = 0; + currentBytes = 0; + totalBytes = 0; + activeHWND = 0; + threadHandle = 0; + openDialogs = 0; + done = false; + killSwitch = 0; + } + + size_t processedFiles; + uint64_t currentBytes; + uint32_t totalBytes; + WorkQueue activeQueue; + HWND activeHWND; + HANDLE threadHandle; + size_t openDialogs; + bool done; + int killSwitch; +}; + +DWORD WINAPI ThreadProc(void *param) +{ + Progress *progress = (Progress *)param; + ProgressCallback callback(progress->activeHWND); + progress->activeQueue.Calculate(&callback, &progress->killSwitch); + PostMessage(progress->activeHWND, WM_USER + 2, 0, 0); + + return 0; +} + +void getViewport(RECT *r, HWND wnd, int full, RECT *sr) +{ + POINT *p = NULL; + + if (p || sr || wnd) + { + HMONITOR hm = NULL; + + if (sr) + hm = MonitorFromRect(sr, MONITOR_DEFAULTTONEAREST); + else if (wnd) + hm = MonitorFromWindow(wnd, MONITOR_DEFAULTTONEAREST); + else if (p) + hm = MonitorFromPoint(*p, MONITOR_DEFAULTTONEAREST); + + if (hm) + { + MONITORINFOEXW mi; + memset(&mi, 0, sizeof(mi)); + mi.cbSize = sizeof(mi); + + if (GetMonitorInfoW(hm, &mi)) + { + if (!full) + *r = mi.rcWork; + else + *r = mi.rcMonitor; + + return ; + } + } + } + if (full) + { // this might be borked =) + r->top = r->left = 0; + r->right = GetSystemMetrics(SM_CXSCREEN); + r->bottom = GetSystemMetrics(SM_CYSCREEN); + } + else + { + SystemParametersInfoW(SPI_GETWORKAREA, 0, r, 0); + } +} + +BOOL windowOffScreen(HWND hwnd, POINT pt) +{ + RECT r = {0}, wnd = {0}, sr = {0}; + GetWindowRect(hwnd, &wnd); + sr.left = pt.x; + sr.top = pt.y; + sr.right = sr.left + (wnd.right - wnd.left); + sr.bottom = sr.top + (wnd.bottom - wnd.top); + getViewport(&r, hwnd, 0, &sr); + return !PtInRect(&r, pt); +} + +void SaveWindowPos(HWND hwnd) +{ + RECT rect = {0}; + GetWindowRect(hwnd, &rect); + char buf[16] = {0}; + StringCchPrintfA(buf, 16, "%d", rect.left); + WritePrivateProfileStringA("ml_rg", "prog_x", buf, iniFile); + StringCchPrintfA(buf, 16, "%d", rect.top); + WritePrivateProfileStringA("ml_rg", "prog_y", buf, iniFile); +} + +INT_PTR WINAPI ReplayGainProgressProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + Progress *progress = (Progress *)lParam; + progress->killSwitch = 0; + progress->done = false; + progress->openDialogs = 0; + progress->processedFiles = 0; + progress->activeHWND = hwndDlg; + + wchar_t dummy[64] = {0}; + StringCchPrintfW(dummy, 64, WASABI_API_LNGSTRINGW(IDS_1_OF_X_FILES), progress->activeQueue.totalFiles); + SetDlgItemTextW(hwndDlg, IDC_PROGRESS_FILES, dummy); + + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)progress); + DWORD threadId; + progress->threadHandle = CreateThread(NULL, 0, ThreadProc, (void *)progress, CREATE_SUSPENDED, &threadId); + SetThreadPriority(progress->threadHandle, THREAD_PRIORITY_IDLE); + ResumeThread(progress->threadHandle); + + POINT pt = {(LONG)GetPrivateProfileIntA("ml_rg", "prog_x", -1, iniFile), + (LONG)GetPrivateProfileIntA("ml_rg", "prog_y", -1, iniFile)}; + if (!windowOffScreen(hwndDlg, pt)) + SetWindowPos(hwndDlg, HWND_TOP, pt.x, pt.y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOSENDCHANGING); + else + ShowWindow(hwndDlg, SW_SHOW); + } + break; + case WM_DESTROY: + { + SaveWindowPos(hwndDlg); + Progress *progress = (Progress *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + CloseHandle(progress->threadHandle); + progress->activeHWND = 0; + delete progress; + } + break; + case WM_USER: // file finished + { + Progress *progress = (Progress *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + progress->processedFiles++; + + if (progress->processedFiles + 1 > progress->activeQueue.totalFiles) + SetDlgItemTextW(hwndDlg, IDC_PROGRESS_FILES, WASABI_API_LNGSTRINGW(IDS_FINISHED)); + else + { + wchar_t dummy[64] = {0}; + StringCchPrintfW(dummy, 64, + WASABI_API_LNGSTRINGW(IDS_X_OF_X_FILES), + progress->processedFiles + 1, progress->activeQueue.totalFiles); + SetDlgItemTextW(hwndDlg, IDC_PROGRESS_FILES, dummy); + } + } + break; + case WM_USER + 1: // album done + { + Progress *progress = (Progress *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + WorkQueue::RGWorkQueue *queue = (WorkQueue::RGWorkQueue *)lParam; + SaveWindowPos(hwndDlg); + if (config_ask && config_ask_each_album) + { + progress->openDialogs++; + DoResults(*queue); + progress->openDialogs--; + if (!progress->openDialogs && progress->done) + DestroyWindow(hwndDlg); + } + else if (config_ask == 0) + { + WriteAlbum(*queue); + } + } + break; + case WM_USER + 2: // all tracks done + { + Progress *progress = (Progress *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + ShowWindow(hwndDlg, SW_HIDE); + SaveWindowPos(hwndDlg); + if (config_ask && config_ask_each_album == 0) + { + DoResults(progress->activeQueue); + } + progress->killSwitch = 1; + WaitForSingleObject(progress->threadHandle, INFINITE); + progress->done = true; + if (!progress->openDialogs) + DestroyWindow(hwndDlg); + } + break; + case WM_USER + 3: // total bytes of current file + { + Progress *progress = (Progress *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + progress->currentBytes = 0; + progress->totalBytes = (uint32_t)lParam; + if (progress->totalBytes == 0) + { + SetDlgItemTextW(hwndDlg, IDC_FILE_PROGRESS, WASABI_API_LNGSTRINGW(IDS_PROCESSING)); + } + else + { + wchar_t dummy[64] = {0}; + StringCchPrintfW(dummy, 64, L"%u%%", (progress->currentBytes * 100) / progress->totalBytes); + SetDlgItemTextW(hwndDlg, IDC_FILE_PROGRESS, dummy); + } + } + break; + case WM_USER + 4: // more bytes read + { + Progress *progress = (Progress *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + progress->currentBytes += lParam; + if (progress->totalBytes == 0) + { + SetDlgItemTextW(hwndDlg, IDC_FILE_PROGRESS, WASABI_API_LNGSTRINGW(IDS_PROCESSING)); + } + else + { + wchar_t dummy[64] = {0}; + StringCchPrintfW(dummy, 64, L"%u%%", (progress->currentBytes * 100) / progress->totalBytes); + SetDlgItemTextW(hwndDlg, IDC_FILE_PROGRESS, dummy); + } + } + break; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDCANCEL: + { + Progress *progress = (Progress *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + progress->killSwitch = 1; + break; + } + } + break; + } + return 0; +} + +void DoProgress(WorkQueue &workQueue) +{ + Progress *progress = new Progress; + progress->activeQueue = workQueue; // this is a huge slow copy, but I don't care at the moment + WASABI_API_CREATEDIALOGPARAMW(IDD_PROGRESS, GetDialogBoxParent(), ReplayGainProgressProc, (LPARAM)progress); +} + +HWND GetDialogBoxParent() +{ + HWND parent = (HWND)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETDIALOGBOXPARENT); + if (!parent || parent == (HWND)1) + return plugin.hwndWinampParent; + return parent; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_rg/RGFactory.cpp b/Src/Plugins/Library/ml_rg/RGFactory.cpp new file mode 100644 index 00000000..a70daea2 --- /dev/null +++ b/Src/Plugins/Library/ml_rg/RGFactory.cpp @@ -0,0 +1,67 @@ +#include "main.h" +#include "RGFactory.h" +#include "Process.h" +static const char serviceName[] = "Replay Gain Processor"; + +FOURCC RGFactory::GetServiceType() +{ + return WaSvc::OBJECT; +} + +const char *RGFactory::GetServiceName() +{ + return serviceName; +} + +GUID RGFactory::GetGUID() +{ + return RGGUID; +} + +void *RGFactory::GetInterface(int global_lock) +{ + obj_replaygain *ifc=new ProcessReplayGain; +// if (global_lock) +// plugin.service->service_lock(this, (void *)ifc); + return ifc; +} + +int RGFactory::SupportNonLockingInterface() +{ + return 1; +} + +int RGFactory::ReleaseInterface(void *ifc) +{ + //plugin.service->service_unlock(ifc); + obj_replaygain *api_ = static_cast<obj_replaygain *>(ifc); + ProcessReplayGain *svc_ = static_cast<ProcessReplayGain *>(api_); + delete svc_; + return 1; +} + +const char *RGFactory::GetTestString() +{ + return 0; +} + +int RGFactory::ServiceNotify(int msg, int param1, int param2) +{ + return 1; +} + +#ifdef CBCLASS +#undef CBCLASS +#endif + +#define CBCLASS RGFactory +START_DISPATCH; +CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType) +CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName) +CB(WASERVICEFACTORY_GETGUID, GetGUID) +CB(WASERVICEFACTORY_GETINTERFACE, GetInterface) +CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface) +CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface) +CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString) +CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify) +END_DISPATCH; diff --git a/Src/Plugins/Library/ml_rg/RGFactory.h b/Src/Plugins/Library/ml_rg/RGFactory.h new file mode 100644 index 00000000..5deea3aa --- /dev/null +++ b/Src/Plugins/Library/ml_rg/RGFactory.h @@ -0,0 +1,24 @@ +#ifndef NULLSOFT_RG_FACTORY_H +#define NULLSOFT_RG_FACTORY_H + +#include <api/service/waservicefactory.h> +#include <api/service/services.h> + +class RGFactory : public waServiceFactory +{ +public: + FOURCC GetServiceType(); + const char *GetServiceName(); + GUID GetGUID(); + void *GetInterface(int global_lock); + int SupportNonLockingInterface(); + int ReleaseInterface(void *ifc); + const char *GetTestString(); + int ServiceNotify(int msg, int param1, int param2); + +protected: + RECVS_DISPATCH; +}; + + +#endif
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_rg/Results.cpp b/Src/Plugins/Library/ml_rg/Results.cpp new file mode 100644 index 00000000..592bd47b --- /dev/null +++ b/Src/Plugins/Library/ml_rg/Results.cpp @@ -0,0 +1,172 @@ +#include "main.h" +#include "resource.h" +#include "../nu/listview.h" +#include "api__ml_rg.h" +#include <shlwapi.h> +#include <strsafe.h> + +// this isn't nice but it localises the values for display as they're saved in "C" locale +enum { GAIN_MODE=0, PEAK_MODE }; +wchar_t* LocaliseNumericText(wchar_t str[64], int mode){ +static wchar_t tmp[64]; +double value; + tmp[0]=0; + value = _wtof_l(str,WASABI_API_LNG->Get_C_NumericLocale()); + StringCchPrintfW(tmp,64,(mode==GAIN_MODE?L"%-+.2f dB":L"%-.9f"),value); + return tmp; +} + +static void AddQueueToListView(W_ListView *listView, WorkQueue::RGWorkQueue *queue) +{ + int i=listView->GetCount(); + for(WorkQueue::RGWorkQueue::iterator itr = queue->begin(); itr!= queue->end(); itr++) + { + listView->InsertItem(i, PathFindFileNameW(itr->filename), (int)&*itr); + listView->SetItemText(i, 1, LocaliseNumericText(itr->track_gain,GAIN_MODE)); + listView->SetItemText(i, 2, LocaliseNumericText(itr->track_peak,PEAK_MODE)); + listView->SetItemText(i, 3, LocaliseNumericText(itr->album_gain,GAIN_MODE)); + listView->SetItemText(i, 4, LocaliseNumericText(itr->album_peak,PEAK_MODE)); + i++; + } +} + +INT_PTR WINAPI ReplayGainDialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch(msg) + { + case WM_INITDIALOG: + { + W_ListView listView(GetDlgItem(hwndDlg, IDC_RGLIST)); + listView.setwnd(GetDlgItem(hwndDlg, IDC_RGLIST)); + + listView.AddCol(WASABI_API_LNGSTRINGW(IDS_COL_FILENAME), 200); + listView.AddCol(WASABI_API_LNGSTRINGW(IDS_COL_TRACK_GAIN), 65); + listView.AddCol(WASABI_API_LNGSTRINGW(IDS_COL_TRACK_PEAK), 80); + listView.AddCol(WASABI_API_LNGSTRINGW(IDS_COL_ALBUM_GAIN), 65); + listView.AddCol(WASABI_API_LNGSTRINGW(IDS_COL_ALBUM_PEAK), 80); + + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); + + WorkQueue::RGWorkQueue *queue = (WorkQueue::RGWorkQueue *)lParam; + + AddQueueToListView(&listView, queue); + + POINT pt = {(LONG)GetPrivateProfileIntA("ml_rg", "res_x", -1, iniFile), + (LONG)GetPrivateProfileIntA("ml_rg", "res_y", -1, iniFile)}; + if (!windowOffScreen(hwndDlg, pt)) + SetWindowPos(hwndDlg, HWND_TOP, pt.x, pt.y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOSENDCHANGING); + } + break; + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDOK: + { + WorkQueue::RGWorkQueue *queue = (WorkQueue::RGWorkQueue *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + WriteAlbum(*queue); + } + case IDCANCEL: + { + RECT rect = {0}; + GetWindowRect(hwndDlg, &rect); + char buf[16] = {0}; + StringCchPrintfA(buf, 16, "%d", rect.left); + WritePrivateProfileStringA("ml_rg", "res_x", buf, iniFile); + StringCchPrintfA(buf, 16, "%d", rect.top); + WritePrivateProfileStringA("ml_rg", "res_y", buf, iniFile); + EndDialog(hwndDlg, 0); + } + break; + case IDC_SAVETRACK: + { + WorkQueue::RGWorkQueue *queue = (WorkQueue::RGWorkQueue *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + WriteTracks(*queue); + EndDialog(hwndDlg, 0); + } + break; + } + } + return 0; +} + +INT_PTR WINAPI ReplayGainDialogProcAll(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch(msg) + { + case WM_INITDIALOG: + { + W_ListView listView(GetDlgItem(hwndDlg, IDC_RGLIST)); + listView.setwnd(GetDlgItem(hwndDlg, IDC_RGLIST)); + + listView.AddCol(WASABI_API_LNGSTRINGW(IDS_COL_FILENAME), 200); + listView.AddCol(WASABI_API_LNGSTRINGW(IDS_COL_TRACK_GAIN), 65); + listView.AddCol(WASABI_API_LNGSTRINGW(IDS_COL_TRACK_PEAK), 80); + listView.AddCol(WASABI_API_LNGSTRINGW(IDS_COL_ALBUM_GAIN), 65); + listView.AddCol(WASABI_API_LNGSTRINGW(IDS_COL_ALBUM_PEAK), 80); + + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); + + WorkQueue *queue = (WorkQueue *)lParam; + + AddQueueToListView(&listView, &queue->unclassified); + for (WorkQueue::AlbumMap::iterator mapItr=queue->albums.begin();mapItr!=queue->albums.end();mapItr++) + { + AddQueueToListView(&listView, &mapItr->second); + } + + POINT pt = {(LONG)GetPrivateProfileIntA("ml_rg", "res_x", -1, iniFile), + (LONG)GetPrivateProfileIntA("ml_rg", "res_y", -1, iniFile)}; + if (!windowOffScreen(hwndDlg, pt)) + SetWindowPos(hwndDlg, HWND_TOP, pt.x, pt.y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOSENDCHANGING); + } + break; + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDOK: + { + WorkQueue *queue = (WorkQueue *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + WriteAlbum(queue->unclassified); + for (WorkQueue::AlbumMap::iterator mapItr=queue->albums.begin();mapItr!=queue->albums.end();mapItr++) + { + WriteAlbum(mapItr->second); + } + } + case IDCANCEL: + { + RECT rect = {0}; + GetWindowRect(hwndDlg, &rect); + char buf[16] = {0}; + StringCchPrintfA(buf, 16, "%d", rect.left); + WritePrivateProfileStringA("ml_rg", "res_x", buf, iniFile); + StringCchPrintfA(buf, 16, "%d", rect.top); + WritePrivateProfileStringA("ml_rg", "res_y", buf, iniFile); + EndDialog(hwndDlg, 0); + } + break; + case IDC_SAVETRACK: + { + WorkQueue *queue = (WorkQueue *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + WriteTracks(queue->unclassified); + for (WorkQueue::AlbumMap::iterator mapItr=queue->albums.begin();mapItr!=queue->albums.end();mapItr++) + { + WriteTracks(mapItr->second); + } + EndDialog(hwndDlg, 0); + } + break; + } + } + return 0; +} + +void DoResults(WorkQueue::RGWorkQueue &queue) +{ + if (!queue.empty()) + WASABI_API_DIALOGBOXPARAM(IDD_RESULTS, GetDialogBoxParent(), ReplayGainDialogProc, (LPARAM)&queue); +} + +void DoResults(WorkQueue &queue) +{ + WASABI_API_DIALOGBOXPARAM(IDD_RESULTS, GetDialogBoxParent(), ReplayGainDialogProcAll, (LPARAM)&queue); +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_rg/api__ml_rg.h b/Src/Plugins/Library/ml_rg/api__ml_rg.h new file mode 100644 index 00000000..f46f1047 --- /dev/null +++ b/Src/Plugins/Library/ml_rg/api__ml_rg.h @@ -0,0 +1,24 @@ +#ifndef NULLSOFT_ML_RG_API_H +#define NULLSOFT_ML_RG_API_H + +#include "../Winamp/api_decodefile.h" +extern api_decodefile *decodeFile; +#define AGAVE_API_DECODE decodeFile + +#include "api/application/api_application.h" +extern api_application *applicationApi; +#define WASABI_API_APP applicationApi + +#include "../playlist/api_playlistmanager.h" +extern api_playlistmanager *playlistManager; +#define AGAVE_API_PLAYLISTMANAGER playlistManager + +#include "../Agave/Language/api_language.h" + +#include "../Winamp/api_stats.h" +extern api_stats *statsApi; +#define AGAVE_API_STATS statsApi + +#include <api/service/waservicefactory.h> + +#endif
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_rg/config.cpp b/Src/Plugins/Library/ml_rg/config.cpp new file mode 100644 index 00000000..3c5ea665 --- /dev/null +++ b/Src/Plugins/Library/ml_rg/config.cpp @@ -0,0 +1,52 @@ +#include "main.h" +#include "resource.h" + +int config_ask=1; +int config_ask_each_album=1; +int config_ignore_gained_album=0; + +void DoButtons(HWND hwndDlg) +{ + config_ask = IsDlgButtonChecked(hwndDlg, IDC_ASK); + config_ask_each_album = IsDlgButtonChecked(hwndDlg, IDC_ALBUM); + EnableWindow(GetDlgItem(hwndDlg, IDC_ALBUM), config_ask); + EnableWindow(GetDlgItem(hwndDlg, IDC_ALL), config_ask); +} + +INT_PTR WINAPI RGConfig(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch(msg) + { + case WM_INITDIALOG: + CheckDlgButton(hwndDlg, IDC_ASK, config_ask); + if (config_ask_each_album) + CheckDlgButton(hwndDlg, IDC_ALBUM, BST_CHECKED); + else + CheckDlgButton(hwndDlg, IDC_ALL, BST_CHECKED); + DoButtons(hwndDlg); + break; + case WM_DESTROY: + { + config_ask = IsDlgButtonChecked(hwndDlg, IDC_ASK); + config_ask_each_album = IsDlgButtonChecked(hwndDlg, IDC_ALBUM); + WritePrivateProfileStringA("ml_rg", "config_ask", config_ask ? "1" : "0", iniFile); + WritePrivateProfileStringA("ml_rg", "config_ask_each_album", config_ask_each_album ? "1" : "0", iniFile); + break; + } + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDCANCEL: + case IDOK: + EndDialog(hwndDlg, 0); + break; + case IDC_ASK: + case IDC_ALBUM: + case IDC_ALL: + DoButtons(hwndDlg); + break; + } + break; + } + return 0; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_rg/main.h b/Src/Plugins/Library/ml_rg/main.h new file mode 100644 index 00000000..461b4ae7 --- /dev/null +++ b/Src/Plugins/Library/ml_rg/main.h @@ -0,0 +1,146 @@ +#ifndef NULLSOFT_ML_RG_MAIN_H +#define NULLSOFT_ML_RG_MAIN_H + +#include <windows.h> +#include "../../General/gen_ml/ml.h" +#include <windowsx.h> +#include "../winamp/wa_ipc.h" +#include "../../General/gen_ml/ml.h" +#include "resource.h" +#include <string> +#include <vector> +#include <map> + +extern winampMediaLibraryPlugin plugin; +extern char *iniFile; + +LRESULT SetFileInfo(const wchar_t *filename, const wchar_t *metadata, const wchar_t *data); +int GetFileInfo(const wchar_t *filename, const wchar_t *metadata, wchar_t *dest, int len); +void WriteFileInfo(); +void TagUpdated(const wchar_t *filename); + +struct RGWorkFile +{ + RGWorkFile(const wchar_t *_filename=0) + { + if (_filename) + lstrcpynW(filename, _filename, MAX_PATH); + else + filename[0]=0; + track_gain[0]=0; + track_peak[0]=0; + album_gain[0]=0; + album_peak[0]=0; + } + + wchar_t filename[MAX_PATH]; + wchar_t track_gain[64]; + wchar_t track_peak[64]; + wchar_t album_gain[64]; + wchar_t album_peak[64]; +}; + +class ProgressCallback; + +class WorkQueue +{ +public: + WorkQueue() : totalFiles(0){} + void Add(const wchar_t *filename); + void Calculate(ProgressCallback *callback, int *killSwitch); + typedef std::vector<RGWorkFile> RGWorkQueue; + typedef std::map<std::wstring, RGWorkQueue> AlbumMap; + AlbumMap albums; + RGWorkQueue unclassified; + size_t totalFiles; +}; + +constexpr auto TIME_SPAN_MS = 10; + +class ProgressCallback +{ +public: + ProgressCallback(HWND _c) + : callback(_c), + totalReceived(0) + { + ticks = GetTickCount64(); + } + + void InformSize(size_t bytes) + { + if (!PostMessage(callback, WM_USER + 3, 0, bytes)) + { + // LOG the error + DWORD e = GetLastError(); + } + } + /// <summary> + /// This function may fire an "ERROR_NOT_ENOUGH_QUOTA" 1816 (0x718) error when the limit is hit! + /// Put some throttle here, post message every 10 ms, not each time we receive a progress. + /// </summary> + /// <param name="bytes"></param> + void Progress(size_t bytes) + { + totalReceived += bytes; + ULONGLONG currentTicks = GetTickCount64(); + if (currentTicks - ticks >= TIME_SPAN_MS) + { + ticks = currentTicks; + if (!PostMessage(callback, WM_USER + 4, 0, totalReceived)) + { + // LOG the error + DWORD e = GetLastError(); + } + + totalReceived = 0; + } + } + void FileFinished() + { + // notify remaining bytes + if (totalReceived) + { + PostMessage(callback, WM_USER + 4, 0, totalReceived); + totalReceived = 0; + } + + if(!PostMessage(callback, WM_USER, 0, 0)) + { + // LOG the error + DWORD e = GetLastError(); + } + } + void AlbumFinished(WorkQueue::RGWorkQueue *album) + { + if(!PostMessage(callback, WM_USER + 1, 0, (LPARAM)album)) + { + // LOG the error + DWORD e = GetLastError(); + } + } + + HWND callback; + ULONGLONG ticks; + size_t totalReceived; +}; + +void CopyAlbumData(WorkQueue::RGWorkQueue &workQueue, const wchar_t *album_gain, const wchar_t *album_peak); +void WriteAlbum(WorkQueue::RGWorkQueue &workQueue); +void WriteTracks(WorkQueue::RGWorkQueue &workQueue); +void DoResults(WorkQueue::RGWorkQueue &queue); +void DoResults(WorkQueue &queue); +void DoProgress(WorkQueue &workQueue); + +void DestroyRG(void *context); +void *CreateRG(); +void CalculateAlbumRG(void *context, wchar_t album_gain[64], wchar_t album_peak[64], float &albumPeak); +void StartRG(void *context); +void CalculateRG(void *context, const wchar_t *filename, wchar_t track_gain[64], wchar_t track_peak[64], ProgressCallback *callback, int *killSwitch, float &albumPeak); + +HWND GetDialogBoxParent(); +BOOL windowOffScreen(HWND hwnd, POINT pt); + +extern int config_ask, config_ask_each_album, config_ignore_gained_album; +INT_PTR WINAPI RGConfig(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); +#endif
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_rg/metadata.cpp b/Src/Plugins/Library/ml_rg/metadata.cpp new file mode 100644 index 00000000..63d4107f --- /dev/null +++ b/Src/Plugins/Library/ml_rg/metadata.cpp @@ -0,0 +1,28 @@ +#include "main.h" + +LRESULT SetFileInfo(const wchar_t *filename, const wchar_t *metadata, const wchar_t *data) +{ + extendedFileInfoStructW efis = { + filename, + metadata, + data ? data : L"", + data ? (size_t)lstrlenW(data) : 0, + }; + return SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&efis, IPC_SET_EXTENDED_FILE_INFOW); +} + +void WriteFileInfo() +{ + SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_WRITE_EXTENDED_FILE_INFO); +} + +int GetFileInfo(const wchar_t *filename, const wchar_t *metadata, wchar_t *dest, int len) +{ + extendedFileInfoStructW efis = { filename, metadata, dest, (size_t)len, }; + return (int)(INT_PTR)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&efis, IPC_GET_EXTENDED_FILE_INFOW_HOOKABLE); //will return 1 if wa2 supports this IPC call +} + +void TagUpdated(const wchar_t *filename) +{ + SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)filename, IPC_FILE_TAG_MAY_HAVE_UPDATEDW); +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_rg/ml_rg.cpp b/Src/Plugins/Library/ml_rg/ml_rg.cpp new file mode 100644 index 00000000..6d6dcb34 --- /dev/null +++ b/Src/Plugins/Library/ml_rg/ml_rg.cpp @@ -0,0 +1,409 @@ +#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; + } +};
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_rg/ml_rg.rc b/Src/Plugins/Library/ml_rg/ml_rg.rc new file mode 100644 index 00000000..fe1ce9c8 --- /dev/null +++ b/Src/Plugins/Library/ml_rg/ml_rg.rc @@ -0,0 +1,152 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_RESULTS DIALOGEX 0, 0, 356, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT | WS_EX_APPWINDOW +CAPTION "Replay Gain Results" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_RGLIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,4,7,348,141 + DEFPUSHBUTTON "Save as Album",IDOK,116,153,76,13 + PUSHBUTTON "Save Track data",IDC_SAVETRACK,196,153,76,13 + PUSHBUTTON "Cancel",IDCANCEL,276,153,76,13 +END + +IDD_PROGRESS DIALOGEX 0, 0, 186, 63 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_APPWINDOW +CAPTION "Calculating Replay Gain" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "File Progress:",IDC_STATIC,7,7,44,8 + LTEXT "Calculating...",IDC_FILE_PROGRESS,63,7,116,8 + LTEXT "Total Progress:",IDC_STATIC,7,23,50,8 + LTEXT "Calculating...",IDC_PROGRESS_FILES,63,23,116,8 + PUSHBUTTON "Abort",IDCANCEL,129,41,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_RESULTS, DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 352 + TOPMARGIN, 7 + BOTTOMMARGIN, 166 + END + + IDD_PROGRESS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 56 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_NULLSOFT_REPLAY_GAIN_ANALYZER "Nullsoft Replay Gain Analyzer v%s" + 65535 "{5F633543-148D-48cc-B683-DA82F592CF28}" +END + +STRINGTABLE +BEGIN + IDS_CALCULATE_REPLAY_GAIN "Calculate Replay Gain" + IDS_1_OF_X_FILES "1 of %u files" + IDS_FINISHED "Finished." + IDS_X_OF_X_FILES "%u of %u files" + IDS_PROCESSING "Processing..." + IDS_TOO_MANY_CHANNELS "Too many channels, can't deal with this yet" + IDS_REPLAYGAIN "ReplayGain" + IDS_NOT_ABLE_TO_OPEN_RG_DLL "Not able to open ReplayGainAnalysis.dll" + IDS_COL_FILENAME "Filename" + IDS_COL_TRACK_GAIN "Track Gain" + IDS_COL_TRACK_PEAK "Track Peak" + IDS_COL_ALBUM_GAIN "Album Gain" + IDS_COL_ALBUM_PEAK "Album Peak" + IDS_DO_YOU_ALSO_WANT_TO_REMOVE_SETTINGS + "Do you also want to remove the saved settings for this plug-in?" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/Library/ml_rg/ml_rg.sln b/Src/Plugins/Library/ml_rg/ml_rg.sln new file mode 100644 index 00000000..5beb288b --- /dev/null +++ b/Src/Plugins/Library/ml_rg/ml_rg.sln @@ -0,0 +1,57 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29424.173 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ml_rg", "ml_rg.vcxproj", "{F42B3AC8-E719-4AA6-B9EC-1F9BFC799861}" + ProjectSection(ProjectDependencies) = postProject + {8155E1C4-B6F8-4A1D-96A3-E4FF0FE9192D} = {8155E1C4-B6F8-4A1D-96A3-E4FF0FE9192D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReplayGainAnalysis", "..\ReplayGainAnalysis\ReplayGainAnalysis.vcxproj", "{8155E1C4-B6F8-4A1D-96A3-E4FF0FE9192D}" + ProjectSection(ProjectDependencies) = postProject + {DABE6307-F8DD-416D-9DAC-673E2DECB73F} = {DABE6307-F8DD-416D-9DAC-673E2DECB73F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nsutil", "..\nsutil\nsutil.vcxproj", "{DABE6307-F8DD-416D-9DAC-673E2DECB73F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F42B3AC8-E719-4AA6-B9EC-1F9BFC799861}.Debug|Win32.ActiveCfg = Debug|Win32 + {F42B3AC8-E719-4AA6-B9EC-1F9BFC799861}.Debug|Win32.Build.0 = Debug|Win32 + {F42B3AC8-E719-4AA6-B9EC-1F9BFC799861}.Debug|x64.ActiveCfg = Debug|x64 + {F42B3AC8-E719-4AA6-B9EC-1F9BFC799861}.Debug|x64.Build.0 = Debug|x64 + {F42B3AC8-E719-4AA6-B9EC-1F9BFC799861}.Release|Win32.ActiveCfg = Release|Win32 + {F42B3AC8-E719-4AA6-B9EC-1F9BFC799861}.Release|Win32.Build.0 = Release|Win32 + {F42B3AC8-E719-4AA6-B9EC-1F9BFC799861}.Release|x64.ActiveCfg = Release|x64 + {F42B3AC8-E719-4AA6-B9EC-1F9BFC799861}.Release|x64.Build.0 = Release|x64 + {8155E1C4-B6F8-4A1D-96A3-E4FF0FE9192D}.Debug|Win32.ActiveCfg = Debug|Win32 + {8155E1C4-B6F8-4A1D-96A3-E4FF0FE9192D}.Debug|Win32.Build.0 = Debug|Win32 + {8155E1C4-B6F8-4A1D-96A3-E4FF0FE9192D}.Debug|x64.ActiveCfg = Debug|x64 + {8155E1C4-B6F8-4A1D-96A3-E4FF0FE9192D}.Debug|x64.Build.0 = Debug|x64 + {8155E1C4-B6F8-4A1D-96A3-E4FF0FE9192D}.Release|Win32.ActiveCfg = Release|Win32 + {8155E1C4-B6F8-4A1D-96A3-E4FF0FE9192D}.Release|Win32.Build.0 = Release|Win32 + {8155E1C4-B6F8-4A1D-96A3-E4FF0FE9192D}.Release|x64.ActiveCfg = Release|x64 + {8155E1C4-B6F8-4A1D-96A3-E4FF0FE9192D}.Release|x64.Build.0 = Release|x64 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|Win32.ActiveCfg = Debug|Win32 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|Win32.Build.0 = Debug|Win32 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|x64.ActiveCfg = Debug|x64 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|x64.Build.0 = Debug|x64 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|Win32.ActiveCfg = Release|Win32 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|Win32.Build.0 = Release|Win32 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|x64.ActiveCfg = Release|x64 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FC646532-2050-40A5-A2AB-F699F1C071C4} + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/Library/ml_rg/ml_rg.vcxproj b/Src/Plugins/Library/ml_rg/ml_rg.vcxproj new file mode 100644 index 00000000..309e9194 --- /dev/null +++ b/Src/Plugins/Library/ml_rg/ml_rg.vcxproj @@ -0,0 +1,310 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{F42B3AC8-E719-4AA6-B9EC-1F9BFC799861}</ProjectGuid> + <RootNamespace>ml_rg</RootNamespace> + <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Label="Vcpkg"> + <VcpkgEnableManifest>false</VcpkgEnableManifest> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_DEBUG;_WINDOWS;_USRDLL;ML_RG_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <BufferSecurityCheck>true</BufferSecurityCheck> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\</Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;_DEBUG;_WINDOWS;_USRDLL;ML_RG_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <DisableSpecificWarnings>4311;4302;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <BufferSecurityCheck>true</BufferSecurityCheck> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\</Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion> + <IntrinsicFunctions>true</IntrinsicFunctions> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;NDEBUG;_WINDOWS;_USRDLL;ML_RG_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <BufferSecurityCheck>true</BufferSecurityCheck> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +</Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion> + <IntrinsicFunctions>true</IntrinsicFunctions> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;NDEBUG;_WINDOWS;_USRDLL;ML_RG_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <BufferSecurityCheck>true</BufferSecurityCheck> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <DisableSpecificWarnings>4311;4302;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +</Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="api__ml_rg.h" /> + <ClInclude Include="main.h" /> + <ClInclude Include="obj_replaygain.h" /> + <ClInclude Include="Process.h" /> + <ClInclude Include="resource.h" /> + <ClInclude Include="RGFactory.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\..\..\nu\listview.cpp" /> + <ClCompile Include="config.cpp" /> + <ClCompile Include="metadata.cpp" /> + <ClCompile Include="ml_rg.cpp" /> + <ClCompile Include="Process.cpp" /> + <ClCompile Include="Progress.cpp" /> + <ClCompile Include="replaygain.cpp" /> + <ClCompile Include="Results.cpp" /> + <ClCompile Include="RGFactory.cpp" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="ml_rg.rc" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj"> + <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_rg/ml_rg.vcxproj.filters b/Src/Plugins/Library/ml_rg/ml_rg.vcxproj.filters new file mode 100644 index 00000000..c0ec5bcf --- /dev/null +++ b/Src/Plugins/Library/ml_rg/ml_rg.vcxproj.filters @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ClCompile Include="config.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="metadata.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Process.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Progress.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="replaygain.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Results.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="RGFactory.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ml_rg.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\..\nu\listview.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="api__ml_rg.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="main.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="obj_replaygain.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Process.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="resource.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="RGFactory.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <Filter Include="Header Files"> + <UniqueIdentifier>{20ba2df7-10e3-42e6-896b-4c0a795e542f}</UniqueIdentifier> + </Filter> + <Filter Include="Ressource Files"> + <UniqueIdentifier>{018c7bf8-fe6c-4705-aa4d-c641ffd71994}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files"> + <UniqueIdentifier>{916716df-cad8-4fa2-9a6f-2203f1fbade7}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="ml_rg.rc"> + <Filter>Ressource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_rg/obj_replaygain.h b/Src/Plugins/Library/ml_rg/obj_replaygain.h new file mode 100644 index 00000000..a594c246 --- /dev/null +++ b/Src/Plugins/Library/ml_rg/obj_replaygain.h @@ -0,0 +1,62 @@ +#ifndef NULLSOFT_ML_RG_OBJ_REPLAYGAIN_H +#define NULLSOFT_ML_RG_OBJ_REPLAYGAIN_H + +#include <bfc/dispatch.h> + +enum +{ + RG_SUCCESS = 0, + RG_FAILURE = 1, + RG_MODE_NOT_SUPPORTED=2, + + RG_INDIVIDUAL_TRACKS = 0, // use this mode to calculate each track sent individually + RG_ALBUM = 1, // use this mode to treat all tracks sent as belonging to the same album + RG_AUTO = 2, // retrieve tags from the files to determine album info +}; + +class obj_replaygain : public Dispatchable +{ +protected: + obj_replaygain() {} + ~obj_replaygain() {} +public: + int Open(int mode); + int ProcessTrack(const wchar_t *filename); + int Write(); + void Close(); + + DISPATCH_CODES + { + OBJ_REPLAYGAIN_OPEN = 10, + OBJ_REPLAYGAIN_PROCESSTRACK = 20, + OBJ_REPLAYGAIN_WRITE = 30, + OBJ_REPLAYGAIN_CLOSE = 40, + }; +}; + +inline int obj_replaygain::Open(int mode) +{ + return _call(OBJ_REPLAYGAIN_OPEN, (int)RG_FAILURE, mode); +} + +inline int obj_replaygain::ProcessTrack(const wchar_t *filename) +{ + return _call(OBJ_REPLAYGAIN_PROCESSTRACK, (int)RG_FAILURE, filename); +} + +inline int obj_replaygain::Write() +{ + return _call(OBJ_REPLAYGAIN_WRITE, (int)RG_FAILURE); +} + +inline void obj_replaygain::Close() +{ + _voidcall(OBJ_REPLAYGAIN_CLOSE); +} + +// {3A398A1B-D316-4094-993E-27EAEA553D19} +static const GUID RGGUID = +{ 0x3a398a1b, 0xd316, 0x4094, { 0x99, 0x3e, 0x27, 0xea, 0xea, 0x55, 0x3d, 0x19 } }; + + +#endif 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 +} diff --git a/Src/Plugins/Library/ml_rg/resource.h b/Src/Plugins/Library/ml_rg/resource.h new file mode 100644 index 00000000..88f4f058 --- /dev/null +++ b/Src/Plugins/Library/ml_rg/resource.h @@ -0,0 +1,40 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ml_rg.rc +// +#define IDS_CALCULATE_REPLAY_GAIN 1 +#define IDS_1_OF_X_FILES 2 +#define IDS_FINISHED 3 +#define IDS_X_OF_X_FILES 4 +#define IDS_PROCESSING 5 +#define IDS_TOO_MANY_CHANNELS 6 +#define IDS_REPLAYGAIN 7 +#define IDS_NOT_ABLE_TO_OPEN_RG_DLL 8 +#define IDS_COL_FILENAME 9 +#define IDS_COL_TRACK_GAIN 10 +#define IDS_COL_TRACK_PEAK 11 +#define IDS_COL_ALBUM_GAIN 12 +#define IDS_COL_ALBUM_PEAK 13 +#define IDS_STRING14 14 +#define IDS_DO_YOU_ALSO_WANT_TO_REMOVE_SETTINGS 14 +#define IDD_RESULTS 101 +#define IDD_PROGRESS 102 +#define IDC_RGLIST 1001 +#define IDC_SAVETRACK 1002 +#define IDC_PROGRESS_FILES 1003 +#define IDC_FILE_PROGRESS 1004 +#define IDC_ASK 1005 +#define IDC_ALBUM 1006 +#define IDC_ALL 1007 +#define IDS_NULLSOFT_REPLAY_GAIN_ANALYZER 65534 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 107 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1008 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/Library/ml_rg/version.rc2 b/Src/Plugins/Library/ml_rg/version.rc2 new file mode 100644 index 00000000..c016ebfe --- /dev/null +++ b/Src/Plugins/Library/ml_rg/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../../../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,29,0,0 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp Media Library Plug-in" + VALUE "FileVersion", "1,29,0,0" + VALUE "InternalName", "Nullsoft Replay Gain Analyzer" + VALUE "LegalCopyright", "Copyright © 2006-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "ml_rg.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END |